diff --git a/packages/client/ui-command/tests/popup-view.spec.tsx b/packages/client/ui-command/tests/popup-view.spec.tsx index 9afd432d37..bda650e190 100644 --- a/packages/client/ui-command/tests/popup-view.spec.tsx +++ b/packages/client/ui-command/tests/popup-view.spec.tsx @@ -45,7 +45,7 @@ async function mountOpen(overrides: Partial> = {}, consumeResu } function rowLabels(): string[] { - return screen.getAllByRole('option').map(o => o.querySelector('span')!.textContent!) + return screen.getAllByRole('option').map(o => o.querySelector('span')!.textContent) } describe('PopupSelectView', () => { diff --git a/packages/client/ui-conversation/src/client/chat/ChatView.tsx b/packages/client/ui-conversation/src/client/chat/ChatView.tsx index 3b2c1b9ef0..2aae37d99d 100644 --- a/packages/client/ui-conversation/src/client/chat/ChatView.tsx +++ b/packages/client/ui-conversation/src/client/chat/ChatView.tsx @@ -103,7 +103,7 @@ const CallRow = memo(function CallRow({ renderSlot, callId, toolName, block, seq })} {subCalls !== undefined && subCalls.length > 0 && (
- {subCalls.map((node) => ( + {subCalls.map(node => ( - {results.map((node) => ( + {results.map(node => ( void }) { - const partial = useSession((s) => s.partial) + const partial = useSession(s => s.partial) useLayoutEffect(() => { onGrow() }) @@ -164,15 +164,15 @@ function StreamingTail({ useSession, onGrow }: { /** The chat view slot entry: pure component over the composed props (tool rows render through the declared keyed hole's renderSlot share). */ export function ChatView({ useSession, useStore, renderSlot, openDetails, loadOlder }: ChatViewSlotProps) { - const nodes = useSession((s) => s.nodes) - const runningCalls = useSession((s) => s.runningCalls) - const codeDispatches = useSession((s) => s.codeDispatches) - const pending = useSession((s) => s.pending) - const openState = useSession((s) => s.openState) - const openErrorMessage = useSession((s) => s.openError === null ? null : `${s.openError.message}(${s.openError.code})`) - const hasMore = useSession((s) => s.hasMore) - const loadingOlder = useSession((s) => s.loadingOlder) - const selectedCallId = useStore((s) => s.selection?.callId) + const nodes = useSession(s => s.nodes) + const runningCalls = useSession(s => s.runningCalls) + const codeDispatches = useSession(s => s.codeDispatches) + const pending = useSession(s => s.pending) + const openState = useSession(s => s.openState) + const openErrorMessage = useSession(s => s.openError === null ? null : `${s.openError.message}(${s.openError.code})`) + const hasMore = useSession(s => s.hasMore) + const loadingOlder = useSession(s => s.loadingOlder) + const selectedCallId = useStore(s => s.selection?.callId) const items = useMemo(() => deriveChatFlow(nodes), [nodes]) @@ -254,8 +254,8 @@ export function ChatView({ useSession, useStore, renderSlot, openDetails, loadOl const renderItem = (item: ChatFlowItem): ReactNode => { if (item.kind === 'tool-group') { const inGroup = selectedCallId !== undefined - && item.results.some((r) => r.callId === selectedCallId - || codeDispatches.get(r.callId)?.some((sub) => sub.callId === selectedCallId) === true) + && item.results.some(r => r.callId === selectedCallId + || codeDispatches.get(r.callId)?.some(sub => sub.callId === selectedCallId) === true) return (
- {openState === 'loading' &&
载入历史…
} - {openState === 'error' &&
历史加载失败:{openErrorMessage}
} - {hasMore && ( -
- -
- )} - {items.map(renderItem)} - - {runningCalls.length > 0 && ( -
- {runningCalls.map((call) => ( - - ))} -
- )} - {pending.map((item) => )} + {openState === 'loading' &&
载入历史…
} + {openState === 'error' &&
历史加载失败:{openErrorMessage}
} + {hasMore && ( +
+ +
+ )} + {items.map(renderItem)} + + {runningCalls.length > 0 && ( +
+ {runningCalls.map(call => ( + + ))} +
+ )} + {pending.map(item => )}
diff --git a/packages/client/ui-conversation/src/client/chat/StatsLine.tsx b/packages/client/ui-conversation/src/client/chat/StatsLine.tsx index 50dead9529..45b783f19b 100644 --- a/packages/client/ui-conversation/src/client/chat/StatsLine.tsx +++ b/packages/client/ui-conversation/src/client/chat/StatsLine.tsx @@ -53,7 +53,7 @@ export function deriveStats(nodes: ConversationSnapshot['nodes']): UsageTotals { export interface StatsLineProps { useSession: SnapshotSelectorHook } export const StatsLine = memo(function StatsLine({ useSession }: StatsLineProps) { - const nodes = useSession((s) => s.nodes) + const nodes = useSession(s => s.nodes) const stats = useMemo(() => deriveStats(nodes), [nodes]) if (stats.steps === 0) return null const parts: string[] = [] diff --git a/packages/client/ui-conversation/src/client/chat/ToolRow.tsx b/packages/client/ui-conversation/src/client/chat/ToolRow.tsx index 113241eb5d..a6930c7752 100644 --- a/packages/client/ui-conversation/src/client/chat/ToolRow.tsx +++ b/packages/client/ui-conversation/src/client/chat/ToolRow.tsx @@ -52,7 +52,7 @@ export function ToolRow({ const open = expanded && expandable const rowExpands = expandable && expandOnRowClick const toggleExpand = () => { - setExpanded((v) => !v) + setExpanded(v => !v) } const toggleFromLeading = (event: MouseEvent) => { event.stopPropagation() diff --git a/packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx b/packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx index 780e74be4c..d6e7492836 100644 --- a/packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx +++ b/packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx @@ -78,12 +78,12 @@ export function ConversationRoot({ const inputBar = sessionId === undefined ? : renderSlot('conversation.composer.bar', { - variant: hero ? 'hero' : 'composer', - ...(hero ? { placeholder: 'Describe what you want to build' } : {}), - overlay: renderSlot('conversation.input.overlay', {}), - leftItems: zone === undefined ? null : renderSlot('conversation.input.left', zone), - rightItems: zone === undefined ? null : renderSlot('conversation.input.right', zone), - }) + variant: hero ? 'hero' : 'composer', + ...(hero ? { placeholder: 'Describe what you want to build' } : {}), + overlay: renderSlot('conversation.input.overlay', {}), + leftItems: zone === undefined ? null : renderSlot('conversation.input.left', zone), + rightItems: zone === undefined ? null : renderSlot('conversation.input.right', zone), + }) const composerBar = (
diff --git a/packages/client/ui-conversation/src/client/skeleton/DetailsPanel.tsx b/packages/client/ui-conversation/src/client/skeleton/DetailsPanel.tsx index 3d3c84a646..650a95f833 100644 --- a/packages/client/ui-conversation/src/client/skeleton/DetailsPanel.tsx +++ b/packages/client/ui-conversation/src/client/skeleton/DetailsPanel.tsx @@ -86,27 +86,27 @@ export function DetailsPanel({ useSession, useStore, closeDetails }: DetailsPane : material === null ?
该调用不在当前窗口内
: ( - <> - {material.argsRaw !== null && ( -
-
Input
- -
- )} + <> + {material.argsRaw !== null && (
-
Output
- {/* materialFor invariant: result===null ⇔ running (a settled - material always carries its result node). */} - {material.result === null - ?
运行中…
- : ( -
-                            {renderResult(material.result)}
-                          
- )} +
Input
+
- - )} + )} +
+
Output
+ {/* materialFor invariant: result===null ⇔ running (a settled + material always carries its result node). */} + {material.result === null + ?
运行中…
+ : ( +
+                        {renderResult(material.result)}
+                      
+ )} +
+ + )}
) diff --git a/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx b/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx index 5f9a3c02d5..98aad0bb00 100644 --- a/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx +++ b/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx @@ -330,8 +330,8 @@ export function InputBar({ onChange={onChange} onKeyDown={onKeyDown} onSelect={onSelect} - onCopy={e => { onCopyOrCut(e, false) }} - onCut={e => { onCopyOrCut(e, true) }} + onCopy={(e) => { onCopyOrCut(e, false) }} + onCut={(e) => { onCopyOrCut(e, true) }} onPaste={onPaste} onCompositionStart={onCompositionStart} onCompositionEnd={onCompositionEnd} diff --git a/packages/client/ui-conversation/tests/apply-inject.spec.tsx b/packages/client/ui-conversation/tests/apply-inject.spec.tsx index da255415ce..558fdbb9c8 100644 --- a/packages/client/ui-conversation/tests/apply-inject.spec.tsx +++ b/packages/client/ui-conversation/tests/apply-inject.spec.tsx @@ -39,7 +39,7 @@ const SCOPE_TAG: symbol = (() => { return Reflect.get(target, prop, receiver) }, }) - void scopeOf(spy as Context) + void scopeOf(spy) const symbol = recorded.find((p): p is symbol => typeof p === 'symbol') if (symbol === undefined) throw new Error('scopeOf probe recorded no symbol read') return symbol @@ -73,7 +73,7 @@ async function bench() { const mint = (id: SessionId): Context => { let scoped = scopes.get(id) if (scoped === undefined) { - scoped = ctx.plugin(() => {}).ctx.extend({ [SCOPE_TAG]: id }) as Context + scoped = ctx.plugin(() => {}).ctx.extend({ [SCOPE_TAG]: id }) scopes.set(id, scoped) } return scoped @@ -234,11 +234,11 @@ describe('conversation slot inject surface', () => { const injectFn = entry.inject as unknown as (sessionId: SessionId) => ComposerBarInjected // Unknown session: sessions.scope answers nothing. ;(b.sessionsFake.scope as unknown) = () => undefined - expect(() => injectFn(ROOT).stop()).toThrow(/resolved no scope/) + expect(() => { injectFn(ROOT).stop() }).toThrow(/resolved no scope/) // A scope minted outside the service tree: no conversation service on it. const foreign = new Context() ;(b.sessionsFake.scope as unknown) = () => foreign.plugin(() => {}).ctx.extend({}) - expect(() => injectFn(ROOT).stop()).toThrow(/unavailable through the session scope/) + expect(() => { injectFn(ROOT).stop() }).toThrow(/unavailable through the session scope/) }) it('openDetails (chat view face) writes the selection through the store actions and opens the panel', async () => { diff --git a/packages/client/ui-conversation/tests/chat-apply.spec.tsx b/packages/client/ui-conversation/tests/chat-apply.spec.tsx index d0f37e0229..36a25c5b34 100644 --- a/packages/client/ui-conversation/tests/chat-apply.spec.tsx +++ b/packages/client/ui-conversation/tests/chat-apply.spec.tsx @@ -31,7 +31,7 @@ async function bench() { }, current: undefined, phase: 'ready', - } as SessionListState) + }) const sessionsFake = { list: listStore, binding: vi.fn(), @@ -83,7 +83,7 @@ describe('apply wiring', () => { const b = await bench() await b.fiber.await() const entries = b.slots.entries('conversation.view') - expect(entries.map((e) => e.options.id)).toEqual(['chat']) + expect(entries.map(e => e.options.id)).toEqual(['chat']) expect(entries[0]?.options.label).toBe('Chat') expect(entries[0]?.options.order).toBe(0) // Declaring is claiming: the chat entry's registration put the hole on @@ -117,7 +117,7 @@ describe('apply wiring', () => { // Both registrant plugins' inject: ['slots', 'conversation'] resolved — the // service being present implies the chat entry declared the hole first. const entries = b.slots.entries('conversation.chat.toolview') - expect(entries.map((e) => e.options.key)).toEqual(['bash', 'todo_write']) + expect(entries.map(e => e.options.key)).toEqual(['bash', 'todo_write']) }) it('plugin fiber disposal collects every registration (unload cascade, ring and hole included)', async () => { diff --git a/packages/client/ui-conversation/tests/chat-code-subcalls.spec.tsx b/packages/client/ui-conversation/tests/chat-code-subcalls.spec.tsx index 972cb930d2..b253562921 100644 --- a/packages/client/ui-conversation/tests/chat-code-subcalls.spec.tsx +++ b/packages/client/ui-conversation/tests/chat-code-subcalls.spec.tsx @@ -59,7 +59,7 @@ function snapshotWith( pending: [], queue: [], todos: [], running: runningCalls.length > 0, composerPhase: 'active', removed: false, openState: 'open', openError: null, hasMore: false, loadingOlder: false, promptError: null, blank: false, lastAgentError: null, - } as ConversationSnapshot + } } /** Test-owned AppFrame role: declares and renders the resident conversation area. */ diff --git a/packages/client/ui-conversation/tests/chat-stats-bash-sample.spec.tsx b/packages/client/ui-conversation/tests/chat-stats-bash-sample.spec.tsx index 2ae1d88eca..3edbde1c21 100644 --- a/packages/client/ui-conversation/tests/chat-stats-bash-sample.spec.tsx +++ b/packages/client/ui-conversation/tests/chat-stats-bash-sample.spec.tsx @@ -100,9 +100,9 @@ describe('StatsLine', () => { render() const before = renders // Chunk frames swap partial only; nodes keeps its reference (object-layer contract). - act(() => set({ partial: { turn: 1, step: 2, blocks: [{ kind: 'text', text: 'a' }] } })) - act(() => set({ partial: { turn: 1, step: 2, blocks: [{ kind: 'text', text: 'ab' }] } })) - act(() => set({ running: true })) + act(() => { set({ partial: { turn: 1, step: 2, blocks: [{ kind: 'text', text: 'a' }] } }) }) + act(() => { set({ partial: { turn: 1, step: 2, blocks: [{ kind: 'text', text: 'ab' }] } }) }) + act(() => { set({ running: true }) }) expect(renders).toBe(before) }) }) @@ -128,7 +128,7 @@ describe('bash sample row', () => { }, current: undefined, phase: 'ready', - } as SessionListState) + }) } const rowProps = (sessionId: SessionId, over?: { diff --git a/packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx b/packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx index 5495750d23..645684d699 100644 --- a/packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx +++ b/packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx @@ -42,7 +42,7 @@ function snapshotWith(nodes: ToolResultNode[]): ConversationSnapshot { sessionId: SID, nodes, foldDegraded: false, partial: null, runningCalls: [], codeDispatches: new Map(), pending: [], queue: [], todos: [], running: false, composerPhase: 'active', removed: false, openState: 'open', openError: null, hasMore: false, loadingOlder: false, promptError: null, blank: false, lastAgentError: null, - } as ConversationSnapshot + } } /** Test-owned AppFrame role: declares and renders the resident conversation area. */ @@ -252,7 +252,7 @@ describe('registrant load-order seam', () => { children: { 'conversation': { kind: 'single', scope: 'session-maybe' }, 'details': { kind: 'single', scope: 'session' }, - }, + }, }, AppRoot) // Third-party posture, mounted BEFORE ui-conversation: real fiber inject diff --git a/packages/client/ui-conversation/tests/chat-view.spec.tsx b/packages/client/ui-conversation/tests/chat-view.spec.tsx index 8d50fe47db..93445fe4ae 100644 --- a/packages/client/ui-conversation/tests/chat-view.spec.tsx +++ b/packages/client/ui-conversation/tests/chat-view.spec.tsx @@ -104,8 +104,8 @@ function makeHarness(init?: Partial) { useSession: bindSnapshotSelector(source), useSessions: emptySessions(), useWorkspaces: emptyWorkspaces(), - useInput: (() => { throw new Error('unused') }) as never, - inputActions: { setDraft: () => {}, submit: () => {} } as never, + useInput: (() => { throw new Error('unused') }), + inputActions: { setDraft: () => {}, submit: () => {} }, useStore: bindSnapshotSelector(chat), actions: chat.actions, renderSlot, @@ -124,9 +124,9 @@ describe('chat-flow derivation', () => { assistant(5, 'found'), toolResult(6, 'c'), ] const items = deriveChatFlow(nodes) - expect(items.map((i) => i.kind)).toEqual(['node', 'node', 'tool-group', 'node', 'tool-group']) + expect(items.map(i => i.kind)).toEqual(['node', 'node', 'tool-group', 'node', 'tool-group']) const group = items[2]! - expect(group.kind === 'tool-group' && group.results.map((r) => r.callId)).toEqual(['a', 'b']) + expect(group.kind === 'tool-group' && group.results.map(r => r.callId)).toEqual(['a', 'b']) expect(flowKeys(items)).toBe('n1|n2|g3|n5|g6') expect(flowKeys(deriveChatFlow([...nodes, toolResult(7, 'd')]))).toBe('n1|n2|g3|n5|g6') }) @@ -155,7 +155,7 @@ describe('ChatView', () => { fireEvent.scroll(scroller) fireEvent.click(view.getByText('加载更早')) Object.defineProperty(scroller, 'scrollHeight', { value: 1300, writable: true }) - act(() => h.set({ nodes: [assistant(2, 'older'), user(9, 'late')] })) + act(() => { h.set({ nodes: [assistant(2, 'older'), user(9, 'late')] }) }) expect(scroller.scrollTop).toBe(550) // 50 + (1300 - 800) }) @@ -240,10 +240,10 @@ describe('ChatView', () => { // Count renderSlot invocations: the memo boundary holds when CallRow does // not re-render, so the row's renderSlot call count freezes during chunks. let rowRenders = 0 - h.props.renderSlot = (((_key: string, _owner: object) => { + h.props.renderSlot = ((_key: string, _owner: object) => { rowRenders += 1 return
- }) as unknown as ChatViewSlotProps['renderSlot']) + }) const view = render() expect(view.getByTestId('counting-row')).toBeTruthy() const afterMount = rowRenders @@ -270,7 +270,7 @@ describe('ChatView', () => { fireEvent.click(view.getByText('run a')) expect(h.openDetails).toHaveBeenCalledWith({ turnSeq: 3, callId: 'a', toolName: 'bash' }) expect(view.container.querySelector('[data-selected]')).toBeNull() - act(() => h.setSelection({ turnSeq: 3, callId: 'a', toolName: 'bash' })) + act(() => { h.setSelection({ turnSeq: 3, callId: 'a', toolName: 'bash' }) }) expect(view.container.querySelector('[data-selected]')).not.toBeNull() }) @@ -284,10 +284,10 @@ describe('ChatView', () => { it('dispatches each tool row through the keyed slot with the tool name as entryKey', () => { const h = makeHarness({ nodes: [toolResult(3, 'a')] }) const calls: { key: string; entryKey?: string }[] = [] - h.props.renderSlot = (((key: string, _owner: object, opts?: { entryKey?: string; fallback?: React.ReactNode }) => { + h.props.renderSlot = ((key: string, _owner: object, opts?: { entryKey?: string; fallback?: React.ReactNode }) => { calls.push({ key, ...(opts?.entryKey !== undefined ? { entryKey: opts.entryKey } : {}) }) return opts?.fallback ?? null - }) as unknown as ChatViewSlotProps['renderSlot']) + }) render() // Keyed dispatch: slot name is the declared hole, entryKey the wire tool // name, and the fallback (GenericToolCard) renders on an empty ledger. @@ -306,10 +306,10 @@ describe('ChatView', () => { // Arm the paging anchor, then deliver an older page (head seq decreases). fireEvent.click(view.getByText('加载更早')) Object.defineProperty(scroller, 'scrollHeight', { value: 1600, writable: true }) - act(() => h.set({ nodes: [user(1, 'old'), assistant(2, 'b'), user(5, 'later'), assistant(6, 'a')] })) + act(() => { h.set({ nodes: [user(1, 'old'), assistant(2, 'b'), user(5, 'later'), assistant(6, 'a')] }) }) expect(scroller.scrollTop).toBe(600) // 0 + (1600 - 1000) // A new trailing user bubble (own words) force-scrolls to the bottom. - act(() => h.set({ nodes: [user(1, 'old'), assistant(2, 'b'), user(5, 'later'), assistant(6, 'a'), user(9, 'mine')] })) + act(() => { h.set({ nodes: [user(1, 'old'), assistant(2, 'b'), user(5, 'later'), assistant(6, 'a'), user(9, 'mine')] }) }) expect(scroller.scrollTop).toBe(1600) }) @@ -324,7 +324,7 @@ describe('ChatView', () => { const backButton = view.getByLabelText('回到底部') expect(backButton).toBeTruthy() // Streaming growth must NOT drag a scrolled-away reader down. - act(() => h.set({ partial: { turn: 1, step: 1, blocks: [{ kind: 'text', text: 'grow' }] } })) + act(() => { h.set({ partial: { turn: 1, step: 1, blocks: [{ kind: 'text', text: 'grow' }] } }) }) expect(scroller.scrollTop).toBe(100) fireEvent.click(backButton) expect(scroller.scrollTop).toBe(1000) @@ -337,7 +337,7 @@ describe('ChatView', () => { const view = render() fireEvent.click(view.getByText('加载更早')) expect(h.loadOlder).toHaveBeenCalledTimes(1) - act(() => h.set({ loadingOlder: true })) + act(() => { h.set({ loadingOlder: true }) }) expect(view.getByText('加载中…')).toBeTruthy() }) diff --git a/packages/client/ui-conversation/tests/coverage-tails.spec.tsx b/packages/client/ui-conversation/tests/coverage-tails.spec.tsx index 18ac9a2891..94747731eb 100644 --- a/packages/client/ui-conversation/tests/coverage-tails.spec.tsx +++ b/packages/client/ui-conversation/tests/coverage-tails.spec.tsx @@ -83,7 +83,7 @@ describe('tails', () => { byId: { [sid]: { id: sid, title: 'r', displayTitle: 'r', running: false, blank: false, updatedAt: 0 } }, current: undefined, phase: 'ready', - } as SessionListState) + }) const props = (block: RunningToolCall | ToolResultNode) => ({ callId: 'c1', toolName: 'bash', block, openDetails: vi.fn(), sessionId: sid, useSessions: bindSnapshotSelector(list), diff --git a/packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx b/packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx index f3d82eed99..8ee049f899 100644 --- a/packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx +++ b/packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx @@ -21,7 +21,7 @@ function snapshotBase(): ConversationSnapshot { sessionId: SID, nodes: [], foldDegraded: false, partial: null, runningCalls: [], codeDispatches: new Map(), pending: [], queue: [], todos: [], running: false, composerPhase: 'active', removed: false, openState: 'open', openError: null, hasMore: false, loadingOlder: false, promptError: null, blank: false, lastAgentError: null, - } as ConversationSnapshot + } } describe('render branch tails', () => { @@ -73,11 +73,11 @@ describe('render branch tails', () => { const view = render( snap, subscribe: () => () => {} }) as unknown as UseSession} + useSession={bindSnapshotSelector({ getSnapshot: () => snap, subscribe: () => () => {} })} useSessions={bindSnapshotSelector(emptyList)} useWorkspaces={bindSnapshotSelector(emptyWorkspaces)} - useInput={(() => { throw new Error('unused') }) as never} - inputActions={{ setDraft: () => {}, submit: () => {} } as never} + useInput={(() => { throw new Error('unused') })} + inputActions={{ setDraft: () => {}, submit: () => {} }} useStore={bindSnapshotSelector(chat)} actions={chat.actions} closeDetails={vi.fn()} @@ -108,11 +108,11 @@ describe('render branch tails', () => { const view = render( snap, subscribe: () => () => {} }) as unknown as UseSession} + useSession={bindSnapshotSelector({ getSnapshot: () => snap, subscribe: () => () => {} })} useSessions={bindSnapshotSelector(emptyList)} useWorkspaces={bindSnapshotSelector(emptyWorkspaces)} - useInput={(() => { throw new Error('unused') }) as never} - inputActions={{ setDraft: () => {}, submit: () => {} } as never} + useInput={(() => { throw new Error('unused') })} + inputActions={{ setDraft: () => {}, submit: () => {} }} useStore={bindSnapshotSelector(chat)} actions={chat.actions} closeDetails={vi.fn()} diff --git a/packages/client/ui-conversation/tests/input-bar.spec.tsx b/packages/client/ui-conversation/tests/input-bar.spec.tsx index 06a8d266a0..c50a35110a 100644 --- a/packages/client/ui-conversation/tests/input-bar.spec.tsx +++ b/packages/client/ui-conversation/tests/input-bar.spec.tsx @@ -79,11 +79,11 @@ function bench(over?: BenchOptions) { useSession: bindSnapshotSelector(session), useSessions: bindSnapshotSelector(createSnapshotStore({ ids: [], byId: {}, current: undefined, phase: 'ready', - })) as InputBarProps['useSessions'], + })), useWorkspaces: bindSnapshotSelector(createSnapshotStore({ items: [], state: 'idle', phase: 'ready', error: null, baselinesReady: true, recentWorkspaceId: undefined, - })) as InputBarProps['useWorkspaces'], + })), useInput: bindSnapshotSelector(shell.state), inputActions: shell.actions, keyboard: shell, @@ -212,7 +212,7 @@ describe('running and lock semantics (queue cut 1)', () => { const { textarea, wiring } = bench() fireEvent.change(textarea, { target: { value: 'typed' } }) expect(wiring.state.getSnapshot().draft).toBe('typed') - expect((textarea as HTMLTextAreaElement).value).toBe('typed') + expect((textarea).value).toBe('typed') }) it('disabled state shows the unavailable placeholder; custom placeholder wins', () => { @@ -364,10 +364,10 @@ describe('placeholder chrome and control seats', () => { expect(view.getByTestId('plan-entry')).toBeTruthy() expect(view.getByTestId('model-entry')).toBeTruthy() // The bar hands its chrome disable state to the filling entry. - expect(slotCalls.every(c => (c.owner as { locked: boolean }).locked === true)).toBe(true) + expect(slotCalls.every(c => (c.owner as { locked: boolean }).locked)).toBe(true) cleanup() const live = bench({ running: true }) - expect(live.slotCalls.every(c => (c.owner as { locked: boolean }).locked === false)).toBe(true) + expect(live.slotCalls.every(c => !(c.owner as { locked: boolean }).locked)).toBe(true) }) it('disabled locks the Access placeholder and attach control (running does not)', () => { diff --git a/packages/client/ui-conversation/tests/input-matrix.spec.tsx b/packages/client/ui-conversation/tests/input-matrix.spec.tsx index caa9b85ad9..284ef6c76a 100644 --- a/packages/client/ui-conversation/tests/input-matrix.spec.tsx +++ b/packages/client/ui-conversation/tests/input-matrix.spec.tsx @@ -34,11 +34,11 @@ function mountBar(shell: SessionInputShell, over?: { running?: boolean; disabled useSession: bindSnapshotSelector(session), useSessions: bindSnapshotSelector(createSnapshotStore({ ids: [], byId: {}, current: undefined, phase: 'ready', - })) as InputBarProps['useSessions'], + })), useWorkspaces: bindSnapshotSelector(createSnapshotStore({ items: [], state: 'idle', phase: 'ready', error: null, baselinesReady: true, recentWorkspaceId: undefined, - })) as InputBarProps['useWorkspaces'], + })), useInput: bindSnapshotSelector(shell.state), inputActions: shell.actions, keyboard: shell, @@ -88,7 +88,7 @@ describe('matrix row: claimed', () => { expect(shell.snapshot.claim).toEqual({ token: '/goal ', hint: '目标' }) expect(view.container.querySelector('[data-decoration="token"]')?.textContent).toBe('/goal ') expect(view.container.querySelector('[data-decoration="hint"]')?.textContent).toBe('目标') - expect((textarea as HTMLTextAreaElement).readOnly).toBe(false) + expect((textarea).readOnly).toBe(false) // Free editing beyond the token: hint drops, claim holds. fireEvent.change(textarea, { target: { value: '/goal 发布版本' } }) expect(shell.snapshot.phase).toBe('claimed') @@ -104,7 +104,7 @@ describe('matrix row: claimed', () => { expect(sink).not.toHaveBeenCalled() await vi.waitFor(() => { expect(submit).toHaveBeenCalledWith('发布', SCTX) }) // Commit: draft cleared, notice surfaced, back to plain. - await vi.waitFor(() => { expect((textarea as HTMLTextAreaElement).value).toBe('') }) + await vi.waitFor(() => { expect((textarea).value).toBe('') }) expect(view.getByText('完成')).toBeTruthy() }) @@ -126,7 +126,7 @@ describe('matrix row: submitting', () => { fireEvent.keyDown(textarea, { key: 'Enter' }) expect(shell.snapshot.phase).toBe('submitting') expect(shell.snapshot.claim).toBeDefined() - expect((textarea as HTMLTextAreaElement).readOnly).toBe(true) + expect((textarea).readOnly).toBe(true) expect(view.container.querySelector('[data-input-pending]')).not.toBeNull() // Enter is dead inside the lock (submit dispatch is microtask-deferred). await vi.waitFor(() => { expect(submit).toHaveBeenCalledTimes(1) }) @@ -145,7 +145,7 @@ describe('matrix row: submitting', () => { await vi.waitFor(() => { expect(submit).toHaveBeenCalled() }) act(() => { rejectSubmit(new Error('执行失败')) }) await vi.waitFor(() => { expect(first.shell.snapshot.phase).toBe('claimed') }) - expect((first.textarea as HTMLTextAreaElement).value).toBe('/goal ') + expect((first.textarea).value).toBe('/goal ') expect(first.view.getByText('执行失败')).toBeTruthy() cleanup() // Drift: typing during flight wins; no restore, plain, notice only. @@ -157,7 +157,7 @@ describe('matrix row: submitting', () => { act(() => { second.shell.setDraft('用户飞行中打的新稿') }) act(() => { rejectSubmit(new Error('晚到失败')) }) await vi.waitFor(() => { expect(second.shell.snapshot.phase).toBe('plain') }) - expect((second.textarea as HTMLTextAreaElement).value).toBe('用户飞行中打的新稿') + expect((second.textarea).value).toBe('用户飞行中打的新稿') expect(second.view.getByText('晚到失败')).toBeTruthy() }) }) @@ -165,14 +165,14 @@ describe('matrix row: submitting', () => { describe('matrix row: locked (session disabled)', () => { it('disables the textarea and chrome; the machine currency is untouched', () => { const { view, textarea, shell } = bench({ disabled: true }) - expect((textarea as HTMLTextAreaElement).disabled).toBe(true) + expect((textarea).disabled).toBe(true) expect((view.getByLabelText('Add attachment') as HTMLButtonElement).disabled).toBe(true) expect(shell.snapshot.phase).toBe('plain') }) it('running does NOT lock (queue cut 1): typing and enter-queue stay live', () => { const { textarea, sink } = bench({ running: true }) - expect((textarea as HTMLTextAreaElement).disabled).toBe(false) + expect((textarea).disabled).toBe(false) fireEvent.change(textarea, { target: { value: '排队' } }) fireEvent.keyDown(textarea, { key: 'Enter' }) expect(sink).toHaveBeenCalledWith('排队', 'queue') diff --git a/packages/client/ui-conversation/tests/input-scenarios.spec.tsx b/packages/client/ui-conversation/tests/input-scenarios.spec.tsx index ca71a2f0f8..11b272c906 100644 --- a/packages/client/ui-conversation/tests/input-scenarios.spec.tsx +++ b/packages/client/ui-conversation/tests/input-scenarios.spec.tsx @@ -100,7 +100,7 @@ async function scopedBench(register?: (slash: SlashService) => void) { await ctx.plugin(SlashService).await() const slash = ctx.get('slash') as SlashService register?.(slash) - const actx = sessions.scope(sessionId)! as ClientContext + const actx = sessions.scope(sessionId)! const controller = slash.sessionOf(actx) const sink = vi.fn() const shell = new SessionInputShell({ actx, slash: () => controller, defaultSink: sink }) @@ -121,11 +121,11 @@ async function scopedBench(register?: (slash: SlashService) => void) { useSession: bindSnapshotSelector(sessionStore), useSessions: bindSnapshotSelector(createSnapshotStore({ ids: [], byId: {}, current: undefined, phase: 'ready', - })) as InputBarProps['useSessions'], + })), useWorkspaces: bindSnapshotSelector(createSnapshotStore({ items: [], state: 'idle', phase: 'ready', error: null, baselinesReady: true, recentWorkspaceId: undefined, - })) as InputBarProps['useWorkspaces'], + })), useInput: bindSnapshotSelector(shell.state), inputActions: shell.actions, keyboard: shell, @@ -134,7 +134,7 @@ async function scopedBench(register?: (slash: SlashService) => void) { variant: 'composer', } const view = render() - const textarea = view.container.querySelector('textarea')! as HTMLTextAreaElement + const textarea = view.container.querySelector('textarea')! const type = (text: string): void => { fireEvent.change(textarea, { target: { value: text } }) } @@ -145,7 +145,7 @@ async function bench(executeImpl?: (line: string) => Promise) { const execute = vi.fn(executeImpl ?? ((line: string) => Promise.resolve({ kind: 'success' as const, text: `已执行 ${line}` }))) const { source, executed } = commandSource(COMMANDS, execute) - const base = await scopedBench((slash) => { slash.registerSource(source as never) }) + const base = await scopedBench((slash) => { slash.registerSource(source) }) return { ...base, execute, executed } } diff --git a/packages/client/ui-layout/src/client/AppFrame.tsx b/packages/client/ui-layout/src/client/AppFrame.tsx index 2df7920032..5f4984f579 100644 --- a/packages/client/ui-layout/src/client/AppFrame.tsx +++ b/packages/client/ui-layout/src/client/AppFrame.tsx @@ -86,7 +86,7 @@ export function AppFrame({ actions, renderSlot, }: AppFrameProps) { - const panels = useStore((s) => s) + const panels = useStore(s => s) const frameRef = useRef(null) const [viewport, setViewport] = useState(() => window.innerWidth) diff --git a/packages/client/ui-layout/tests/app-frame.spec.tsx b/packages/client/ui-layout/tests/app-frame.spec.tsx index 7f86ee7823..4d5f6de30d 100644 --- a/packages/client/ui-layout/tests/app-frame.spec.tsx +++ b/packages/client/ui-layout/tests/app-frame.spec.tsx @@ -39,7 +39,7 @@ let fireResize: (() => void) | null = null class ResizeObserverStub { #cb: ResizeObserverCallback constructor(cb: ResizeObserverCallback) { this.#cb = cb } - observe(): void { fireResize = () => { this.#cb([], this as unknown as ResizeObserver) } } + observe(): void { fireResize = () => { this.#cb([], this) } } unobserve(): void {} disconnect(): void { fireResize = null } } @@ -48,7 +48,7 @@ let frameWidth = 1920 /** Test-local selector hook over a framework-neutral store instance. */ function hookOf(inst: { subscribe: (fn: () => void) => () => void; getSnapshot: () => T }) { - return (sel: (s: T) => S): S => sel(useSyncExternalStore(inst.subscribe, inst.getSnapshot)) + return function useSelector(sel: (s: T) => S): S { return sel(useSyncExternalStore(inst.subscribe, inst.getSnapshot)) } } function mountFrame() { @@ -118,7 +118,7 @@ beforeEach(() => { vi.stubGlobal('cancelAnimationFrame', (h: number) => { clearTimeout(h) }) window.innerWidth = frameWidth Element.prototype.getBoundingClientRect = function () { - return { width: frameWidth, height: 1080, top: 0, left: 0, right: frameWidth, bottom: 1080, x: 0, y: 0, toJSON: () => ({}) } as DOMRect + return { width: frameWidth, height: 1080, top: 0, left: 0, right: frameWidth, bottom: 1080, x: 0, y: 0, toJSON: () => ({}) } } // jsdom lacks pointer capture: emulate per-element so hasPointerCapture gates pass. const captured = new WeakSet() @@ -143,12 +143,12 @@ describe('AppFrame', () => { const { slotCalls, getByTestId } = mountFrame() expect(getByTestId('center-content')).toBeTruthy() expect(getByTestId('details-content')).toBeTruthy() - const keys = slotCalls.map((c) => c.key) + const keys = slotCalls.map(c => c.key) expect(keys).toContain('conversation') expect(keys).toContain('details') expect(keys).not.toContain('conversation.empty') - expect(slotCalls.find((c) => c.key === 'conversation')!.props).toEqual({}) - expect(slotCalls.find((c) => c.key === 'details')!.props).toEqual({}) + expect(slotCalls.find(c => c.key === 'conversation')!.props).toEqual({}) + expect(slotCalls.find(c => c.key === 'details')!.props).toEqual({}) }) it('keeps the conversation slot mounted while no session is current', () => { @@ -157,7 +157,7 @@ describe('AppFrame', () => { sessionMode.current = false const { slotCalls, getByTestId } = mountFrame() expect(getByTestId('center-content')).toBeTruthy() - expect(slotCalls.map((c) => c.key)).toContain('conversation') + expect(slotCalls.map(c => c.key)).toContain('conversation') }) it('renders both column occupants before baselines settle (no loading gate)', () => { @@ -165,13 +165,13 @@ describe('AppFrame', () => { // pending rendering — both occupants mount from first paint. baselinesReady.current = false const { slotCalls } = mountFrame() - expect(slotCalls.map((c) => c.key)).toContain('conversation') - expect(slotCalls.map((c) => c.key)).toContain('details') + expect(slotCalls.map(c => c.key)).toContain('conversation') + expect(slotCalls.map(c => c.key)).toContain('details') }) it('sidebar slot receives live concession output as owner props', () => { const { slotCalls } = mountFrame() - expect(slotCalls.find((c) => c.key === 'sidebar')!.props).toEqual({ collapsed: false, width: 280 }) + expect(slotCalls.find(c => c.key === 'sidebar')!.props).toEqual({ collapsed: false, width: 280 }) }) it('sidebar drag widens through rAF-batched pointer moves', () => { @@ -211,7 +211,7 @@ describe('AppFrame', () => { expect(tracks(frame)).toEqual([SIDEBAR_COLLAPSED, 360]) expect(getByTestId('sidebar-content')).toBeTruthy() expect(frame.hasAttribute('data-sidebar-collapsed')).toBe(true) - const lastSidebarCall = slotCalls.filter((c) => c.key === 'sidebar').at(-1)! + const lastSidebarCall = slotCalls.filter(c => c.key === 'sidebar').at(-1)! expect(lastSidebarCall.props).toEqual({ collapsed: true, width: SIDEBAR_COLLAPSED }) }) diff --git a/packages/client/ui-primitives/src/Menu.tsx b/packages/client/ui-primitives/src/Menu.tsx index 9abb6a3bb2..6ed7f6c0c2 100644 --- a/packages/client/ui-primitives/src/Menu.tsx +++ b/packages/client/ui-primitives/src/Menu.tsx @@ -158,63 +158,63 @@ export function Menu({ open, anchor, items, selectedId, onSelect, onClose, align // (open/toggle) after onSelect. onClick={(e) => { e.stopPropagation() }} > - {items.map(entry => { - if (isSeparator(entry)) { - return
- } - if (isLabel(entry)) { - return
{entry.text}
- } - const hasSub = entry.submenu !== undefined && entry.submenu.length > 0 - const subOpen = hasSub && openSubmenuId === entry.id - return ( -
{ setOpenSubmenuId(hasSub ? entry.id : null) }} - onMouseLeave={() => { setOpenSubmenuId(null) }} - > - - {subOpen && entry.submenu !== undefined && ( -
- {entry.submenu.map(sub => ( - - ))} -
- )} + {items.map((entry) => { + if (isSeparator(entry)) { + return
+ } + if (isLabel(entry)) { + return
{entry.text}
+ } + const hasSub = entry.submenu !== undefined && entry.submenu.length > 0 + const subOpen = hasSub && openSubmenuId === entry.id + return ( +
{ setOpenSubmenuId(hasSub ? entry.id : null) }} + onMouseLeave={() => { setOpenSubmenuId(null) }} + > + + {subOpen && entry.submenu !== undefined && ( +
+ {entry.submenu.map(sub => ( + + ))}
- ) - })} + )} +
+ ) + })}
) diff --git a/packages/client/ui-primitives/src/icons/index.tsx b/packages/client/ui-primitives/src/icons/index.tsx index 4c2083bae1..46bf94c865 100644 --- a/packages/client/ui-primitives/src/icons/index.tsx +++ b/packages/client/ui-primitives/src/icons/index.tsx @@ -544,14 +544,14 @@ export const IconApiOutline14 = ({ size = 14, className }: IconProps) => ( - + ) /** ic_ds_personalization_outline_16 (figma extract) */ export const IconPersonalizationOutline16 = ({ size = 16, className }: IconProps) => ( - + ) /** ic_ds_project_add_outline_16 (figma extract) */ @@ -559,7 +559,7 @@ export const IconProjectAddOutline16 = ({ size = 16, className }: IconProps) => - + ) /** folder_open_16 (figma extract): outline at full ink + 20%-opacity inner fill riding the same currentColor. */ @@ -567,14 +567,14 @@ export const IconFolderOpen16 = ({ size = 16, className }: IconProps) => ( - + ) /** folder_close_16 (figma extract) */ export const IconFolderClose16 = ({ size = 16, className }: IconProps) => ( - + ) /** tree_corner_8x10 (figma extract; session-tree "L" connector, stroke geometry pre-expanded) */ diff --git a/packages/client/ui-primitives/src/markdown/CodeBlock.tsx b/packages/client/ui-primitives/src/markdown/CodeBlock.tsx index 151af94e1c..6e7a5c73ab 100644 --- a/packages/client/ui-primitives/src/markdown/CodeBlock.tsx +++ b/packages/client/ui-primitives/src/markdown/CodeBlock.tsx @@ -64,20 +64,20 @@ export function CodeBlock({ code, lang, className }: CodeBlockProps) { void writeClipboard(text).then((ok) => { if (!ok) return setCopied(true) - window.setTimeout(() => setCopied(false), 1000) + window.setTimeout(() => { setCopied(false) }, 1000) }) }, [copied, trimmed]) const body = html === undefined ? ( -
{trimmed}
- ) +
{trimmed}
+ ) : ( - // eslint-disable-next-line react/no-danger -- shiki's output is a static - // span tree it generated from `code` (no user HTML passes through), the - // sanctioned innerHTML consumption path per shiki's own docs. -
- ) + // eslint-disable-next-line react/no-danger -- shiki's output is a static + // span tree it generated from `code` (no user HTML passes through), the + // sanctioned innerHTML consumption path per shiki's own docs. +
+ ) return (
diff --git a/packages/client/ui-primitives/src/markdown/JsonBlock.tsx b/packages/client/ui-primitives/src/markdown/JsonBlock.tsx index c916c8303d..4469b4a161 100644 --- a/packages/client/ui-primitives/src/markdown/JsonBlock.tsx +++ b/packages/client/ui-primitives/src/markdown/JsonBlock.tsx @@ -23,7 +23,7 @@ export function JsonBlock({ label, payload, defaultOpen = false }: { }, [open, payload]) return (
- {open &&
{body}
} diff --git a/packages/client/ui-primitives/src/markdown/MarkdownText.tsx b/packages/client/ui-primitives/src/markdown/MarkdownText.tsx index 775978a275..28bd9b5374 100644 --- a/packages/client/ui-primitives/src/markdown/MarkdownText.tsx +++ b/packages/client/ui-primitives/src/markdown/MarkdownText.tsx @@ -27,25 +27,25 @@ const safeUrl: UrlTransform = url => sanitizeUrl(url) /** Build the component table; while `streaming`, fences render the plain arm (see CodeBlock). */ function buildComponents(streaming: boolean): Components { return { - a: ({ href = '', children }) => { - const safeHref = sanitizeUrl(href) - if (safeHref === '') return <>{children} - const external = ['http:', 'https:'].includes(new URL(safeHref).protocol) - return ( - - {children} - - ) - }, - img: ({ alt = '' }) => {alt}, - table: ({ children }) => ( -
- {children}
-
- ), + a: ({ href = '', children }) => { + const safeHref = sanitizeUrl(href) + if (safeHref === '') return <>{children} + const external = ['http:', 'https:'].includes(new URL(safeHref).protocol) + return ( + + {children} + + ) + }, + img: ({ alt = '' }) => {alt}, + table: ({ children }) => ( +
+ {children}
+
+ ), // Fenced blocks route through the shared CodeBlock (shiki for registered // grammars, identical-geometry plain fallback for unknown/absent // languages); inline code keeps the default path (the :not(pre) diff --git a/packages/client/ui-primitives/tests/hover-card.spec.tsx b/packages/client/ui-primitives/tests/hover-card.spec.tsx index c7a95f49fb..ce599c0258 100644 --- a/packages/client/ui-primitives/tests/hover-card.spec.tsx +++ b/packages/client/ui-primitives/tests/hover-card.spec.tsx @@ -13,7 +13,7 @@ function stubAnchorRect(anchor: HTMLElement, rect: { top: number; right: number wrapper.getBoundingClientRect = () => ({ top: rect.top, right: rect.right, left: rect.right - 100, bottom: rect.top + 34, width: 100, height: 34, x: rect.right - 100, y: rect.top, toJSON: () => ({}), - } as DOMRect) + }) } function mount(props: { openDelayMs?: number; disabled?: boolean } = {}) { diff --git a/packages/client/ui-primitives/tests/icons.spec.tsx b/packages/client/ui-primitives/tests/icons.spec.tsx index 281124b8d5..86cf99e49b 100644 --- a/packages/client/ui-primitives/tests/icons.spec.tsx +++ b/packages/client/ui-primitives/tests/icons.spec.tsx @@ -18,7 +18,7 @@ describe('ic_ds_ icon set', () => { expect(iconNames.length).toBe(55) }) - it.each(iconNames)('%s renders an svg with currentColor fills and no hardcoded palette', name => { + it.each(iconNames)('%s renders an svg with currentColor fills and no hardcoded palette', (name) => { const Icon = icons[name]! const { container } = render() const svg = container.querySelector('svg') diff --git a/packages/client/ui-primitives/tests/markdown.spec.tsx b/packages/client/ui-primitives/tests/markdown.spec.tsx index 07df7cebdc..b7f665c78a 100644 --- a/packages/client/ui-primitives/tests/markdown.spec.tsx +++ b/packages/client/ui-primitives/tests/markdown.spec.tsx @@ -153,7 +153,7 @@ describe('JsonBlock', () => { it('truncates beyond the size cap with a suffix note', () => { const big = 'x'.repeat(30_000) const { container } = render() - const body = container.querySelector('pre')!.textContent! + const body = container.querySelector('pre')!.textContent expect(body.length).toBeLessThan(30_000) expect(body).toContain('截断') }) diff --git a/packages/client/ui-primitives/tests/state-dot.spec.tsx b/packages/client/ui-primitives/tests/state-dot.spec.tsx index 0d2cf52ef2..a3759174ff 100644 --- a/packages/client/ui-primitives/tests/state-dot.spec.tsx +++ b/packages/client/ui-primitives/tests/state-dot.spec.tsx @@ -7,7 +7,7 @@ import type { StateDotState } from '@deepseek-ai/dsh-client-ui-primitives' afterEach(cleanup) describe('StateDot', () => { - it.each(['done', 'warning', 'ongoing', 'error'] as const)('renders state %s as data-state', state => { + it.each(['done', 'warning', 'ongoing', 'error'] as const)('renders state %s as data-state', (state) => { const { container } = render() const dot = container.firstElementChild as HTMLElement expect(dot.dataset['state']).toBe(state) diff --git a/packages/client/ui-question/src/client/QuestionComposer.tsx b/packages/client/ui-question/src/client/QuestionComposer.tsx index 3571263f61..e11c943bc9 100644 --- a/packages/client/ui-question/src/client/QuestionComposer.tsx +++ b/packages/client/ui-question/src/client/QuestionComposer.tsx @@ -145,10 +145,10 @@ function QuestionFlow({ pending }: { pending: PendingQuestion }) { const skipQuestion = (): void => { const nextDrafts = drafts.map((item, itemIndex) => itemIndex === index ? { - selected: [], custom: '', - customOpen: (question.options?.length ?? 0) === 0, - skipped: true, - } + selected: [], custom: '', + customOpen: (question.options?.length ?? 0) === 0, + skipped: true, + } : item) setDrafts(nextDrafts) setError(null) diff --git a/packages/client/ui-question/tests/question-composer.spec.tsx b/packages/client/ui-question/tests/question-composer.spec.tsx index 2bc9289cef..4494e87ed1 100644 --- a/packages/client/ui-question/tests/question-composer.spec.tsx +++ b/packages/client/ui-question/tests/question-composer.spec.tsx @@ -50,7 +50,7 @@ const QUESTIONS = [ /** Carrier fixture: a real PendingWait over a scripted respond carrier. */ function wait(rpcId = 'question-1', respond = vi.fn(() => Promise.resolve({ accepted: true }))) { const carrier = new PendingWait( - 'question', RpcId(rpcId), SID, { questions: QUESTIONS } as PendingWait<'question'>['payload'], respond) + 'question', RpcId(rpcId), SID, { questions: QUESTIONS }, respond) return { carrier, respond } } @@ -99,7 +99,7 @@ describe('QuestionComposer', () => { { id: 'detail', selected: [], custom: '要能独立排查线上问题' }, { id: 'signals', selected: ['系统设计', '代码质量'] }, ])) - expect((screen.getByRole('button', { name: '正在提交…' }) as HTMLButtonElement).disabled).toBe(true) + expect((screen.getByRole('button', { name: '正在提交…' })).disabled).toBe(true) }) it('skips individual questions without discarding earlier answers', () => { @@ -173,7 +173,7 @@ describe('QuestionComposer', () => { // Receipt rejection surfaces through the domain face's thrown message. fireEvent.click(screen.getByRole('button', { name: '放弃整组问题' })) expect(await screen.findByText('question cancellation rejected: bad-response')).toBeTruthy() - expect((screen.getByRole('button', { name: '跳过本题' }) as HTMLButtonElement).disabled).toBe(false) + expect((screen.getByRole('button', { name: '跳过本题' })).disabled).toBe(false) fireEvent.click(screen.getByRole('button', { name: '放弃整组问题' })) expect(await screen.findByText('第二次取消失败')).toBeTruthy() @@ -199,7 +199,7 @@ describe('QuestionComposer', () => { fireEvent.click(screen.getByRole('checkbox', { name: '系统设计' })) fireEvent.click(screen.getByRole('button', { name: '提交' })) expect(await screen.findByText('网络中断')).toBeTruthy() - expect((screen.getByRole('button', { name: '提交' }) as HTMLButtonElement).disabled).toBe(false) + expect((screen.getByRole('button', { name: '提交' })).disabled).toBe(false) fireEvent.click(screen.getByRole('button', { name: '提交' })) expect(await screen.findByText('字符串错误')).toBeTruthy() diff --git a/packages/client/ui-settings-general/tests/components.spec.tsx b/packages/client/ui-settings-general/tests/components.spec.tsx index 2a041c6cf4..33368b6f12 100644 --- a/packages/client/ui-settings-general/tests/components.spec.tsx +++ b/packages/client/ui-settings-general/tests/components.spec.tsx @@ -49,7 +49,7 @@ describe('GeneralSection', () => { mount() expect(screen.getByText('Permission')).toBeTruthy() expect(screen.getByText('Choose default permission mode')).toBeTruthy() - const selector = screen.getByRole('button', { name: /Read only/ }) as HTMLButtonElement + const selector = screen.getByRole('button', { name: /Read only/ }) expect(selector.disabled).toBe(true) }) diff --git a/packages/client/ui-settings/src/client/SettingsRoot.tsx b/packages/client/ui-settings/src/client/SettingsRoot.tsx index 04fa39a03c..0d12135311 100644 --- a/packages/client/ui-settings/src/client/SettingsRoot.tsx +++ b/packages/client/ui-settings/src/client/SettingsRoot.tsx @@ -34,7 +34,7 @@ function SettingsPanel({ rows, renderSlot, onClose }: PanelProps) { // Local selection; entries can unmount underneath it, so the render-time // projection falls back to the first row when the id is gone. const [activeId, setActiveId] = useState(undefined) - const active = rows.find((r) => r.id === activeId)?.id ?? rows[0]?.id + const active = rows.find(r => r.id === activeId)?.id ?? rows[0]?.id const titleId = useId() useEffect(() => { @@ -56,7 +56,7 @@ function SettingsPanel({ rows, renderSlot, onClose }: PanelProps) {