Skip to content

Chat: extract useChatSend and the action-decision handlers; ChatPanel within the size limits #847

Open

Description

@smart-ssh-planner

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: respond and acceptWithRule (approve, reject, edit-then-approve, accept with a quick rule).
  • ChatPanel composes useChatTranscript, useChatComposer, useVoiceComposer, useChatSend, useActionDecisions, useStickToBottom and renders the item list and Composer.
  • After this issue ChatPanel.tsx and every file under src/components/chat/ are below 800 lines; each function component is below 400 lines; each component has at most 12 useState.

Acceptance Criteria

  • useChatSend and useActionDecisions exist 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; respond sends the decision once and ignores a second click while pending.
  • ChatPanel.tsx < 800 lines; every file in src/components/chat/ < 800 lines; no function component > 400 lines; no component with more than 12 useState (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 build and npm run test:e2e pass.

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 develop when 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 through FileBrowserPanel/ChatPanel/ServerForm as 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 build in apps/smart-ssh-community/frontend, plus npm run test:e2e if a browser test covers the component.

  • Do not add React.memo, useMemo around item rendering, or list virtualisation in this series. DocumentCard reads the document-action registry with listDocumentActions() 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

Activity

  1. added this to the 0.8.0 milestone on Oct 26, 2026
  2. 21 remaining items

  3. marked this as blocking 15 issues on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    frontendFrontend (React/TypeScript)

    Type

    Fields

    Priority

    P2 - Normal

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions