Repository navigation
Chat: extract useChatSend and the action-decision handlers; ChatPanel within the size limits #847
Description
Goal
Sending, retrying, continuing and stopping a chat turn, and answering a proposed action, live in one hook each; ChatPanel becomes a small composition of transcript, composer and item views.
Context
apps/smart-ssh-community/frontend/src/components/ChatPanel.tsx has 3,058 lines. The component function ChatPanel spans :401–1737 (JSX from ~:1444) with 44 useState and 17 useEffect. One effect (:662–998) subscribes to about 26 chat events (onChatAiRequestStarted, onChatTextDelta, onChatActionProposed, onRiskAssessmentUpdated, … onChatDocumentGenerated, wrappers in src/events.ts) and reduces them with many setters per event (setRetrying, setAiWait, setItems, …). About 1,320 further lines hold 15 sub-components and helpers: RiskBadge (:1737), RiskBadges (:1758), RiskHintFootnote (:1816), errorCardText/ErrorCardExtras (:1829–1895), ChatItemView (:1896–2292, exported), RunningCommandIndicator (:2293), ConfirmActionForm (:2336), reasonList/CoverageList (:2430–2510), QuickRuleButton (:2511–2720, exported), BinaryFileChangeHint (:2721), ActionResultView (:2734), TakeIntoNoteButton (:2803), ScrollablePre (:2855), DocumentCard (:2863), AssistantMessageView (:2951), attachmentLine (:3047); exported helpers ChatItem type (:119) and splitInjectionReason (:323). Tests: ChatPanel.test.tsx and 13 ChatPanel*.test.tsx files (about 130 tests). Line numbers are from develop before the colour-token migration and may shift slightly.
Remaining logic in ChatPanel after the previous steps: respond (:1000), handleStopAutoContinuation (:1028), acceptWithRule (:1058), handleExport (:1111), handleContinueTruncated (:1128), handleRetryTurn (:1139), handleQuestionAnswer (:1180), handleSubmit (:1192), handleNewChat (:1373), the AI wait/retry countdown display and scrolling (useStickToBottom, :523–529). Tests: ChatPanel.test.tsx, ChatPanelAiError.test.tsx, ChatPanelAiWait.test.tsx, ChatPanelNewChat.test.tsx, ChatPanelQuestion.test.tsx, ChatPanelWaitingInput.test.tsx, ChatPanelAutoExecRules.test.tsx, ChatPanelExplanation.test.tsx.
End state of the series: no file over 800 lines, no function component over 400 lines, at most 12 useState per component; sub-components and hooks under src/components/chat/.
Requirements
src/components/chat/useChatSend.ts: submit, retry, continue truncated, answer question, stop auto-continuation, new chat; exposes busy/wait state needed by the composer.src/components/chat/useActionDecisions.ts:respondandacceptWithRule(approve, reject, edit-then-approve, accept with a quick rule).ChatPanelcomposesuseChatTranscript,useChatComposer,useVoiceComposer,useChatSend,useActionDecisions,useStickToBottomand renders the item list andComposer.- After this issue
ChatPanel.tsxand every file undersrc/components/chat/are below 800 lines; each function component is below 400 lines; each component has at most 12useState.
Acceptance Criteria
-
useChatSendanduseActionDecisionsexist with hook tests: submit calls the existing send API (mocked) with draft and attachments and clears the draft; retry and continue call the matching API;respondsends the decision once and ignores a second click while pending. -
ChatPanel.tsx< 800 lines; every file insrc/components/chat/< 800 lines; no function component > 400 lines; no component with more than 12useState(report the numbers in the PR). - The test count of
ChatPanel*.test.tsx(plus the new hook/reducer tests) is not lower than at the start of the series; all pass. -
npm run lint,npx vitest run,npm run buildandnpm run test:e2epass.
Technical Notes
-
Pure refactoring: no change to behaviour, visible text, i18n keys, CSS classes,
data-testids, ARIA attributes, keyboard handling, invoked Tauri commands or listened events. Behaviour changes, if you find a bug, go into a separate issue (mention it in the PR). -
Colour classes in these files are being migrated to theme tokens in a separate issue that must be merged first; keep exactly the classes that are on
developwhen you start. -
Exports that other files or tests import from the original file keep working: either update the importers or re-export from the original path. The extension API (
src/extensions/*,src/api.ts,src/components/ui) does not change. -
Tests: count
it(/test(in the listed test files before and after; the number must not decrease. Tests may move next to an extracted unit, but their assertions stay the same. Tests drive the user-visible behaviour throughFileBrowserPanel/ChatPanel/ServerFormas today. -
New code comments in English; existing German comments may move unchanged.
-
No filter, risk, redaction or credential logic is touched (frontend only).
-
Gate:
npm run lint,npx vitest run,npm run buildinapps/smart-ssh-community/frontend, plusnpm run test:e2eif a browser test covers the component. -
Do not add
React.memo,useMemoaround item rendering, or list virtualisation in this series.DocumentCardreads the document-action registry withlistDocumentActions()during render (:2913); actions registered later only appear because the chat re-renders. Memoising the item views is only safe once that registry is reactive (subscribe +useSyncExternalStore, like the composer registry at :411–412), which is a separate change. -
The confirmation dialog for proposed commands is part of the safety model (a command runs only after the backend received an explicit decision). Keep exactly the same API calls and their arguments; do not add any client-side shortcut that sends an approval.
Spec impact: none
Related Work
- Previous: Chat: toggling the risk second opinion re-subscribes all chat events and can drop streamed text #829, Chat: move ChatItemView, the action card parts, QuickRuleButton, DocumentCard and AssistantMessageView into components/chat/ #832, Chat: useChatTranscript hook with a pure reducer for chat events #838, Chat: extract useChatComposer, useVoiceComposer and the Composer component #842
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
21 remaining items