diff --git a/apps/desktop/electron/find-in-page.test.ts b/apps/desktop/electron/find-in-page.test.ts index 7a5bd124578..e902bb42966 100644 --- a/apps/desktop/electron/find-in-page.test.ts +++ b/apps/desktop/electron/find-in-page.test.ts @@ -9,12 +9,7 @@ import { EventEmitter } from 'node:events' import { describe, test } from 'vitest' -import { - formatFoundInPage, - installFoundInPageForwarder, - performFind, - stopFind -} from './find-in-page' +import { formatFoundInPage, installFoundInPageForwarder, performFind, stopFind } from './find-in-page' // Minimal webContents stub. The Electron.WebContents type is huge, so we // model just the slice the helpers touch (`isDestroyed`, `findInPage`, @@ -90,10 +85,10 @@ describe('formatFoundInPage', () => { }) test('null / undefined inputs still produce a well-formed payload', () => { - assert.deepEqual( - formatFoundInPage(null as unknown as { activeMatchOrdinal?: number; matches?: number }), - { activeMatchOrdinal: 0, count: 0 } - ) + assert.deepEqual(formatFoundInPage(null as unknown as { activeMatchOrdinal?: number; matches?: number }), { + activeMatchOrdinal: 0, + count: 0 + }) assert.deepEqual(formatFoundInPage(undefined), { activeMatchOrdinal: 0, count: 0 }) }) }) @@ -102,33 +97,25 @@ describe('performFind', () => { test('forwards the query and options to webContents.findInPage', () => { const wc = makeFakeWebContents() performFind(asWC(wc), 'hello', { forward: true, findNext: false }) - assert.deepEqual(wc.calls.find, [ - { query: 'hello', options: { forward: true, findNext: false } } - ]) + assert.deepEqual(wc.calls.find, [{ query: 'hello', options: { forward: true, findNext: false } }]) }) test('defaults forward to true when omitted', () => { const wc = makeFakeWebContents() performFind(asWC(wc), 'x', { findNext: true }) - assert.deepEqual(wc.calls.find, [ - { query: 'x', options: { forward: true, findNext: true } } - ]) + assert.deepEqual(wc.calls.find, [{ query: 'x', options: { forward: true, findNext: true } }]) }) test('defaults findNext to false when omitted', () => { const wc = makeFakeWebContents() performFind(asWC(wc), 'x', { forward: false }) - assert.deepEqual(wc.calls.find, [ - { query: 'x', options: { forward: false, findNext: false } } - ]) + assert.deepEqual(wc.calls.find, [{ query: 'x', options: { forward: false, findNext: false } }]) }) test('treats null / non-object options as "all defaults"', () => { const wc = makeFakeWebContents() performFind(asWC(wc), 'x', null) - assert.deepEqual(wc.calls.find, [ - { query: 'x', options: { forward: true, findNext: false } } - ]) + assert.deepEqual(wc.calls.find, [{ query: 'x', options: { forward: true, findNext: false } }]) }) test('coerces a non-string query to string (defensive against bad renderer payloads)', () => { @@ -178,18 +165,14 @@ describe('installFoundInPageForwarder', () => { // Drive the fake's emit directly — this exercises the same code path // as Electron's actual `webContents.emit('found-in-page', …)`. wc.emit('found-in-page', {}, { activeMatchOrdinal: 2, matches: 5 }) - assert.deepEqual(wc.calls.send, [ - { channel: 'hermes:found-in-page', payload: { activeMatchOrdinal: 2, count: 5 } } - ]) + assert.deepEqual(wc.calls.send, [{ channel: 'hermes:found-in-page', payload: { activeMatchOrdinal: 2, count: 5 } }]) }) test('handles missing fields without throwing', () => { const wc = makeFakeWebContents() installFoundInPageForwarder(asWC(wc)) wc.emit('found-in-page', {}, {}) - assert.deepEqual(wc.calls.send, [ - { channel: 'hermes:found-in-page', payload: { activeMatchOrdinal: 0, count: 0 } } - ]) + assert.deepEqual(wc.calls.send, [{ channel: 'hermes:found-in-page', payload: { activeMatchOrdinal: 0, count: 0 } }]) }) test('skips send when webContents is destroyed at fire time', () => { diff --git a/apps/desktop/electron/find-in-page.ts b/apps/desktop/electron/find-in-page.ts index d08e3da4e34..509fc5dccd7 100644 --- a/apps/desktop/electron/find-in-page.ts +++ b/apps/desktop/electron/find-in-page.ts @@ -41,10 +41,7 @@ export interface FoundInPagePayload { * keeping the projection explicit makes the wire shape auditable and keeps * tests independent of the runtime type. */ -export function formatFoundInPage(result: { - activeMatchOrdinal?: number - matches?: number -}): FoundInPagePayload { +export function formatFoundInPage(result: { activeMatchOrdinal?: number; matches?: number }): FoundInPagePayload { return { activeMatchOrdinal: Number(result?.activeMatchOrdinal ?? 0), count: Number(result?.matches ?? 0) @@ -102,9 +99,7 @@ export function stopFind( * highlight matches in THAT window, and the match counter must reflect * THAT window's DOM, not the primary's. */ -export function installFoundInPageForwarder( - webContents: Electron.WebContents | null | undefined -): () => void { +export function installFoundInPageForwarder(webContents: Electron.WebContents | null | undefined): () => void { if (!webContents || webContents.isDestroyed()) { return () => {} } diff --git a/apps/desktop/electron/main.ts b/apps/desktop/electron/main.ts index e19ccf3db3a..5f6ba957ff8 100644 --- a/apps/desktop/electron/main.ts +++ b/apps/desktop/electron/main.ts @@ -10188,6 +10188,7 @@ ipcMain.handle('hermes:quick-entry:settings:get', async () => { ipcMain.handle('hermes:quick-entry:settings:set', async (_event, patch) => { const current = readQuickEntrySettings() + const next = sanitizeQuickEntrySettings({ enabled: patch?.enabled === undefined ? current.enabled : patch.enabled === true, shortcut: typeof patch?.shortcut === 'string' && patch.shortcut.trim() ? patch.shortcut : current.shortcut diff --git a/apps/desktop/src/app/chat/composer/hooks/use-composer-undo.ts b/apps/desktop/src/app/chat/composer/hooks/use-composer-undo.ts index d55f75dcd2b..72f891f6dac 100644 --- a/apps/desktop/src/app/chat/composer/hooks/use-composer-undo.ts +++ b/apps/desktop/src/app/chat/composer/hooks/use-composer-undo.ts @@ -1,11 +1,6 @@ import { type RefObject, useCallback, useEffect, useMemo } from 'react' -import { - caretOffsetInEditor, - composerPlainText, - placeCaretAtOffset, - renderComposerContents -} from '../rich-editor' +import { caretOffsetInEditor, composerPlainText, placeCaretAtOffset, renderComposerContents } from '../rich-editor' import { type ComposerSnapshot, createComposerUndoHistory } from '../undo-history' interface UseComposerUndoArgs { diff --git a/apps/desktop/src/app/chat/composer/undo-history.test.ts b/apps/desktop/src/app/chat/composer/undo-history.test.ts index e59935c73cc..5ce477a8020 100644 --- a/apps/desktop/src/app/chat/composer/undo-history.test.ts +++ b/apps/desktop/src/app/chat/composer/undo-history.test.ts @@ -193,7 +193,11 @@ describe('caret offsets in composerPlainText coordinates', () => { }) it('counts a chip as its whole @kind:value text', () => { - editor.append(document.createTextNode('see '), refChipElement('file', '`src/a.ts`'), document.createTextNode(' now')) + editor.append( + document.createTextNode('see '), + refChipElement('file', '`src/a.ts`'), + document.createTextNode(' now') + ) const chipText = '@file:`src/a.ts`' // Caret at the very end = everything before it. diff --git a/apps/desktop/src/app/right-sidebar/terminal/persistent.test.tsx b/apps/desktop/src/app/right-sidebar/terminal/persistent.test.tsx index cc6b15c1507..c0f718e546c 100644 --- a/apps/desktop/src/app/right-sidebar/terminal/persistent.test.tsx +++ b/apps/desktop/src/app/right-sidebar/terminal/persistent.test.tsx @@ -81,12 +81,14 @@ function rect(top: number, left: number, width: number, height: number): DOMRect function installRaf() { let nextId = 1 const frames = new Map() + const request = vi.fn((callback: FrameRequestCallback) => { const id = nextId++ frames.set(id, callback) return id }) + const cancel = vi.fn((id: number) => { frames.delete(id) }) diff --git a/apps/desktop/src/app/session/hooks/use-session-list-actions.test.tsx b/apps/desktop/src/app/session/hooks/use-session-list-actions.test.tsx index 7ca89984ddf..8b32a8b85ee 100644 --- a/apps/desktop/src/app/session/hooks/use-session-list-actions.test.tsx +++ b/apps/desktop/src/app/session/hooks/use-session-list-actions.test.tsx @@ -102,9 +102,7 @@ describe('refreshSessions identity + loading hygiene', () => { expect(first.map(s => s.id)).toEqual(['a', 'b']) // Second refresh returns fresh (but equal) row objects, as the API does. - listSidebarSessions.mockResolvedValue( - sidebar({ sessions: [row('a'), row('b')] }) - ) + listSidebarSessions.mockResolvedValue(sidebar({ sessions: [row('a'), row('b')] })) await act(async () => { await result.current.refreshSessions() @@ -123,9 +121,7 @@ describe('refreshSessions identity + loading hygiene', () => { const first = $sessions.get() - listSidebarSessions.mockResolvedValue( - sidebar({ sessions: [row('a', { last_active: 2000, title: 'Renamed' })] }) - ) + listSidebarSessions.mockResolvedValue(sidebar({ sessions: [row('a', { last_active: 2000, title: 'Renamed' })] })) await act(async () => { await result.current.refreshSessions() @@ -197,9 +193,7 @@ describe('refreshSessions batches slices into one request', () => { const cron = [row('c1', { source: 'cron', title: 'nightly' })] const messaging = [row('m1', { source: 'telegram', title: 'tg chat' })] - listSidebarSessions.mockResolvedValue( - sidebar({ sessions: recents }, cron, messaging) - ) + listSidebarSessions.mockResolvedValue(sidebar({ sessions: recents }, cron, messaging)) const { result } = renderHook(() => useSessionListActions({ profileScope: 'default' })) diff --git a/apps/desktop/src/components/assistant-ui/thread/index.tsx b/apps/desktop/src/components/assistant-ui/thread/index.tsx index a5263e13710..946860795e3 100644 --- a/apps/desktop/src/components/assistant-ui/thread/index.tsx +++ b/apps/desktop/src/components/assistant-ui/thread/index.tsx @@ -104,7 +104,16 @@ export const Thread: FC<{ /> ) }), - [cwd, gateway, hasBranchInNewChat, hasCancel, hasDismissError, hasRestoreToMessage, requestRestoreConfirm, sessionId] + [ + cwd, + gateway, + hasBranchInNewChat, + hasCancel, + hasDismissError, + hasRestoreToMessage, + requestRestoreConfirm, + sessionId + ] ) const emptyPlaceholder = intro ? ( diff --git a/apps/desktop/src/components/find-bar.tsx b/apps/desktop/src/components/find-bar.tsx index c7e0dd74dad..6e99e581ae5 100644 --- a/apps/desktop/src/components/find-bar.tsx +++ b/apps/desktop/src/components/find-bar.tsx @@ -176,10 +176,7 @@ export function FindBar() { /> {matchLabel && ( - + {matchLabel} )} diff --git a/apps/desktop/src/components/pet/pet-sprite.test.tsx b/apps/desktop/src/components/pet/pet-sprite.test.tsx index 6da30fdd5f7..f197ec49d80 100644 --- a/apps/desktop/src/components/pet/pet-sprite.test.tsx +++ b/apps/desktop/src/components/pet/pet-sprite.test.tsx @@ -84,12 +84,14 @@ function installWindowStateBridge() { function installRaf() { let nextId = 1 const frames = new Map() + const request = vi.fn((callback: FrameRequestCallback) => { const id = nextId++ frames.set(id, callback) return id }) + const cancel = vi.fn((id: number) => { frames.delete(id) }) diff --git a/apps/desktop/src/components/pet/use-pet-roam.ts b/apps/desktop/src/components/pet/use-pet-roam.ts index e4d1e9a801b..2850f473771 100644 --- a/apps/desktop/src/components/pet/use-pet-roam.ts +++ b/apps/desktop/src/components/pet/use-pet-roam.ts @@ -173,10 +173,13 @@ export function usePetRoam({ const delay = Math.max(0, pauseUntil - now) if (delay > 0) { - pauseTimer = window.setTimeout(() => { - pauseTimer = 0 - step(performance.now()) - }, Math.min(delay, PAUSE_POLL_MS)) + pauseTimer = window.setTimeout( + () => { + pauseTimer = 0 + step(performance.now()) + }, + Math.min(delay, PAUSE_POLL_MS) + ) return } diff --git a/apps/desktop/src/global.d.ts b/apps/desktop/src/global.d.ts index debae77f457..1e6225bb209 100644 --- a/apps/desktop/src/global.d.ts +++ b/apps/desktop/src/global.d.ts @@ -271,14 +271,9 @@ declare global { // searches that window (not the primary). `onFoundInPage` returns the // unsubscribe fn; the renderer wires it via `initFindInPageListener` // in store/find-in-page.ts and tears it down when the FindBar unmounts. - findInPage: ( - query: string, - options?: { forward?: boolean; findNext?: boolean } - ) => Promise<{ count: number }> + findInPage: (query: string, options?: { forward?: boolean; findNext?: boolean }) => Promise<{ count: number }> stopFindInPage: () => Promise - onFoundInPage: ( - callback: (result: { activeMatchOrdinal: number; count: number }) => void - ) => () => void + onFoundInPage: (callback: (result: { activeMatchOrdinal: number; count: number }) => void) => () => void } } } diff --git a/apps/desktop/src/main.tsx b/apps/desktop/src/main.tsx index 3d1c164b6de..9aec2131073 100644 --- a/apps/desktop/src/main.tsx +++ b/apps/desktop/src/main.tsx @@ -53,7 +53,7 @@ if (winParam === 'overlay') { renders in a single sash drag). Radix's provider holds only refs and stable callbacks, so hoisting is what it's for. */} - {/* useTransitions={false}: react-router v7's HashRouter wraps every + {/* useTransitions={false}: react-router v7's HashRouter wraps every route state update in React.startTransition() by default. In React 19's concurrent renderer, transitions are non-urgent — React can yield mid-render and resume later. When the app is under load @@ -62,9 +62,9 @@ if (winParam === 'overlay') { the route change commit. The session sidebar highlight + main pane both freeze for seconds despite the main thread being free. Disabling transitions makes navigate() commit at default priority. */} - - - + + + diff --git a/ui-tui/src/app/slash/commands/core.ts b/ui-tui/src/app/slash/commands/core.ts index 52f235f4222..b69d2e89b44 100644 --- a/ui-tui/src/app/slash/commands/core.ts +++ b/ui-tui/src/app/slash/commands/core.ts @@ -564,9 +564,7 @@ export const coreCommands: SlashCommand[] = [ // `/focus status` reports without writing, matching the CLI surface. if (mode === 'status' || mode === 'show' || mode === '?') { return ctx.transcript.sys( - current - ? 'focus view on — only your prompt and the final response' - : 'focus view off' + current ? 'focus view on — only your prompt and the final response' : 'focus view off' ) } @@ -584,9 +582,7 @@ export const coreCommands: SlashCommand[] = [ queueMicrotask(() => ctx.transcript.sys( - next - ? 'focus view enabled — just your prompt and the final response' - : 'focus view disabled' + next ? 'focus view enabled — just your prompt and the final response' : 'focus view disabled' ) ) }