fmt(js): `npm run fix` on merge (#79521)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
hermes-seaeye[bot] 2026-08-05 16:51:56 +00:00 committed by GitHub
parent 69bedb7bed
commit 25c7827ec9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 55 additions and 23 deletions

View File

@ -179,10 +179,9 @@ describe('PreviewPane console state', () => {
)
})
await waitFor(
() => expect(rendered.container.querySelector('iframe')).not.toBeNull(),
{ container: rendered.container }
)
await waitFor(() => expect(rendered.container.querySelector('iframe')).not.toBeNull(), {
container: rendered.container
})
expect(rendered.container.querySelector('iframe')?.getAttribute('src')).toBe('blob:pdf-preview-1')
expect(readFileDataUrl).toHaveBeenCalledWith('/tmp/spec.pdf')
const blob = createObjectURL.mock.calls[0]?.[0]
@ -328,10 +327,9 @@ describe('PreviewPane console state', () => {
$connection.set({ baseUrl: 'http://macmini', mode: 'remote', profile: 'macmini' } as never)
})
await waitFor(
() => expect(rendered.container.querySelector('iframe')).not.toBeNull(),
{ container: rendered.container }
)
await waitFor(() => expect(rendered.container.querySelector('iframe')).not.toBeNull(), {
container: rendered.container
})
expect(api).toHaveBeenCalledWith({
path: `/api/fs/read-data-url?path=${encodeURIComponent(filePath)}`,
profile: 'macmini'

View File

@ -663,6 +663,7 @@ export function useSlashCommand(deps: SlashCommandDeps) {
client_capture: true,
surface: 'gui'
})
applyWakeStatus(current)
return current

View File

@ -10,10 +10,7 @@
const TARGET_RATE = 16_000
const DEFAULT_FRAME = 1280 // 80 ms @ 16 kHz — matches tools/wake_word.py
export type WakeFeedRequester = (
method: string,
params?: Record<string, unknown>
) => Promise<unknown>
export type WakeFeedRequester = (method: string, params?: Record<string, unknown>) => Promise<unknown>
export interface ClientWakeCaptureOptions {
/** Samples per frame at 16 kHz (from wake.start response). */
@ -31,33 +28,41 @@ function downsampleTo16k(input: Float32Array, inputRate: number): Float32Array {
if (inputRate === TARGET_RATE) {
return input
}
if (inputRate <= 0) {
return new Float32Array(0)
}
const ratio = inputRate / TARGET_RATE
const outLen = Math.max(1, Math.floor(input.length / ratio))
const out = new Float32Array(outLen)
for (let i = 0; i < outLen; i++) {
const start = Math.floor(i * ratio)
const end = Math.min(input.length, Math.floor((i + 1) * ratio))
let sum = 0
let count = 0
for (let j = start; j < end; j++) {
sum += input[j] ?? 0
count++
}
out[i] = count > 0 ? sum / count : 0
}
return out
}
function floatToInt16LE(input: Float32Array): ArrayBuffer {
const buf = new ArrayBuffer(input.length * 2)
const view = new DataView(buf)
for (let i = 0; i < input.length; i++) {
const s = Math.max(-1, Math.min(1, input[i] ?? 0))
view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true)
}
return buf
}
@ -65,9 +70,11 @@ function bytesToBase64(buf: ArrayBuffer): string {
const bytes = new Uint8Array(buf)
let binary = ''
const chunk = 0x8000
for (let i = 0; i < bytes.length; i += chunk) {
binary += String.fromCharCode(...bytes.subarray(i, i + chunk))
}
return btoa(binary)
}
@ -75,15 +82,15 @@ function bytesToBase64(buf: ArrayBuffer): string {
* Start streaming the default microphone to `wake.feed`.
* Returns a handle whose `stop()` ends tracks + audio graph.
*/
export async function startClientWakeCapture(
options: ClientWakeCaptureOptions
): Promise<ClientWakeCaptureHandle> {
export async function startClientWakeCapture(options: ClientWakeCaptureOptions): Promise<ClientWakeCaptureHandle> {
const frameLength = Math.max(160, Math.trunc(options.frameLength || DEFAULT_FRAME))
const audioWindow = window as Window & { webkitAudioContext?: typeof AudioContext }
const AudioContextCtor = window.AudioContext || audioWindow.webkitAudioContext
if (!AudioContextCtor) {
throw new Error('AudioContext unavailable for client wake capture')
}
if (!navigator.mediaDevices?.getUserMedia) {
throw new Error('getUserMedia unavailable for client wake capture')
}
@ -123,13 +130,17 @@ export async function startClientWakeCapture(
if (draining) {
return
}
draining = true
try {
while (!stopped && queue.length > 0) {
const batch = queue.splice(0, MAX_FRAMES_PER_FEED)
if (batch.length === 0) {
break
}
try {
const merged = new Float32Array(batch.length * frameLength)
batch.forEach((frame, i) => merged.set(frame, i * frameLength))
@ -145,6 +156,7 @@ export async function startClientWakeCapture(
}
} finally {
draining = false
if (!stopped && queue.length > 0) {
void drainQueue()
}
@ -155,10 +167,13 @@ export async function startClientWakeCapture(
if (stopped) {
return
}
queue.push(frame)
while (queue.length > MAX_QUEUED_FRAMES) {
queue.shift()
}
void drainQueue()
}
@ -166,6 +181,7 @@ export async function startClientWakeCapture(
if (stopped) {
return
}
const input = event.inputBuffer.getChannelData(0)
const at16k = downsampleTo16k(input, context.sampleRate)
// Append to pending and emit full frames
@ -173,11 +189,13 @@ export async function startClientWakeCapture(
merged.set(pending, 0)
merged.set(at16k, pending.length)
let offset = 0
while (offset + frameLength <= merged.length) {
const frame = merged.subarray(offset, offset + frameLength)
offset += frameLength
enqueueFrame(new Float32Array(frame))
}
pending = merged.subarray(offset)
}
@ -197,8 +215,10 @@ export async function startClientWakeCapture(
if (stopped) {
return
}
stopped = true
queue.length = 0
try {
processor.disconnect()
source.disconnect()
@ -206,6 +226,7 @@ export async function startClientWakeCapture(
} catch {
// ignore
}
void context.close().catch(() => undefined)
stream.getTracks().forEach(t => t.stop())
}

View File

@ -117,6 +117,7 @@ function isPdfFileTarget(target: PreviewTarget): boolean {
* obsolete raw-binary path after Desktop itself has been upgraded. */
export function decodePreviewTabs(raw: string): PreviewTab[] {
const parsed = JSON.parse(raw) as unknown
const tabs = (Array.isArray(parsed) ? parsed.filter(isPreviewTab) : []).map(tab =>
isPdfFileTarget(tab.target) && tab.target.previewKind === 'binary'
? { ...tab, target: { ...tab.target, previewKind: 'pdf' as const } }

View File

@ -772,6 +772,7 @@ describe('applyBackendUpdate recovery', () => {
it('restores the fixed action deadline after reconnecting', async () => {
updateHermesSpy.mockResolvedValue({ action_id: 'a'.repeat(32), ok: true, name: 'hermes-update', pid: 1 })
const running = {
exit_code: null,
lines: ['still running'],
@ -783,6 +784,7 @@ describe('applyBackendUpdate recovery', () => {
for (let attempt = 0; attempt < 119; attempt += 1) {
getActionStatusSpy.mockResolvedValueOnce(running)
}
getActionStatusSpy.mockRejectedValueOnce(new Error('ECONNRESET')).mockResolvedValue(running)
const promise = applyBackendUpdate()

View File

@ -569,9 +569,11 @@ async function runBackendUpdate(): Promise<DesktopUpdateApplyResult> {
try {
const previousStatus = $backendUpdateStatus.get()
const requestedTargetSha = previousStatus?.commits?.at(0)?.sha
const previousVersion = previousStatus?.targetSha?.startsWith('backend:')
? previousStatus.targetSha.slice('backend:'.length)
: undefined
const started = await updateHermes()
if (!started.ok) {

View File

@ -1,9 +1,6 @@
import { atom } from 'nanostores'
import {
type ClientWakeCaptureHandle,
startClientWakeCapture
} from '@/lib/wake-client-capture'
import { type ClientWakeCaptureHandle, startClientWakeCapture } from '@/lib/wake-client-capture'
import { $gateway } from '@/store/gateway'
// "Hey Hermes" wake-word listener state for the composer toggle. The gateway is
@ -48,13 +45,17 @@ export function stopClientCapture(): void {
async function maybeStartClientCapture(result: WakeStartResponse | null | undefined): Promise<void> {
stopClientCapture()
if (!result?.started) {
return
}
const mode = (result.capture || '').toLowerCase()
if (mode !== 'client' && mode !== 'remote' && mode !== 'external') {
return
}
try {
clientCapture = await startClientWakeCapture({
frameLength: result.frame_length,
@ -65,12 +66,10 @@ async function maybeStartClientCapture(result: WakeStartResponse | null | undefi
$wakeWord.set({
...current,
listening: false,
notice:
error instanceof Error
? error.message
: 'Failed to open the client microphone for wake word',
notice: error instanceof Error ? error.message : 'Failed to open the client microphone for wake word',
pending: false
})
// Best-effort: release server lease if client mic failed.
try {
await gatewayRequester('wake.stop', {})
@ -255,12 +254,14 @@ export async function armWakeWord(request: WakeRequester = gatewayRequester): Pr
client_capture: true,
surface: 'gui'
})
applyWakeStatus(status)
if (!status?.available || status.listening) {
// Armed already (e.g. another surface/restart) — reattach feeder if client.
if (status?.listening) {
const mode = (status.capture || '').toLowerCase()
if (mode === 'client' || mode === 'remote' || mode === 'external') {
void maybeStartClientCapture({
started: true,
@ -269,6 +270,7 @@ export async function armWakeWord(request: WakeRequester = gatewayRequester): Pr
})
}
}
return
}
@ -276,6 +278,7 @@ export async function armWakeWord(request: WakeRequester = gatewayRequester): Pr
surface: 'gui',
client_capture: true
})
applyWakeStartResult(result)
} catch {
// Older backends / transient failures — keep whatever we last knew.
@ -350,6 +353,7 @@ export async function resumeWakeAfterVoice(request: WakeRequester = gatewayReque
client_capture: true,
surface: 'gui'
})
applyWakeStatus(status)
// Config says off (or the feature can't run) — off is the correct rest
@ -362,6 +366,7 @@ export async function resumeWakeAfterVoice(request: WakeRequester = gatewayReque
// Server lease is still armed (e.g. wake.resume after voice).
// Client PCM was stopped on wake.detected — reattach if needed.
const mode = (status.capture || '').toLowerCase()
if (mode === 'client' || mode === 'remote' || mode === 'external') {
void maybeStartClientCapture({
started: true,
@ -369,6 +374,7 @@ export async function resumeWakeAfterVoice(request: WakeRequester = gatewayReque
frame_length: status.frame_length ?? 1280
})
}
return
}
@ -376,6 +382,7 @@ export async function resumeWakeAfterVoice(request: WakeRequester = gatewayReque
surface: 'gui',
client_capture: true
})
applyWakeStartResult(started)
if (started?.started) {