@alumbwe/anvil 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/bin/anvil +2 -0
- package/bin/tree-sitter.wasm +0 -0
- package/package.json +67 -0
- package/src/__tests__/README.md +256 -0
- package/src/__tests__/bash-mode.test.ts +442 -0
- package/src/__tests__/cli-args.test.ts +181 -0
- package/src/__tests__/cli-harness.test.ts +52 -0
- package/src/__tests__/e2e/first-time-login.test.ts +173 -0
- package/src/__tests__/e2e/logout-relogin-flow.test.ts +110 -0
- package/src/__tests__/e2e/returning-user-auth.test.ts +113 -0
- package/src/__tests__/e2e-cli.test.ts +183 -0
- package/src/__tests__/helpers/launcher-disconnect-fixture.cjs +176 -0
- package/src/__tests__/helpers/mock-api-client.ts +64 -0
- package/src/__tests__/helpers/renderer-cleanup-fixture.tsx +89 -0
- package/src/__tests__/helpers/terminal-watchdog-fixture.ts +75 -0
- package/src/__tests__/home-directory-detection.test.ts +213 -0
- package/src/__tests__/integration/api-integration.test.ts +288 -0
- package/src/__tests__/integration/credentials-storage.test.ts +486 -0
- package/src/__tests__/integration/local-agents.test.ts +1187 -0
- package/src/__tests__/integration/login-flow-instrumentation.test.ts +134 -0
- package/src/__tests__/integration/login-polling-working.test.ts +285 -0
- package/src/__tests__/integration/queue-editing.test.tsx +261 -0
- package/src/__tests__/integration/usage-refresh-on-completion.test.ts +168 -0
- package/src/__tests__/integration-tmux.test.ts +188 -0
- package/src/__tests__/launcher-avx2-fallback.test.ts +630 -0
- package/src/__tests__/launcher-disconnect.test.ts +104 -0
- package/src/__tests__/mocks/hover-toggle-controller.ts +71 -0
- package/src/__tests__/open-tui-native-bundle.test.ts +378 -0
- package/src/__tests__/path-completion.test.ts +243 -0
- package/src/__tests__/project-files-chat-id.test.ts +34 -0
- package/src/__tests__/release/proxy-http-get.test.ts +667 -0
- package/src/__tests__/release/wrapper-safety.test.ts +412 -0
- package/src/__tests__/renderer-cleanup.test.ts +174 -0
- package/src/__tests__/rerender-perf.integration.test.ts +283 -0
- package/src/__tests__/terminal-reset-sequences.test.ts +36 -0
- package/src/__tests__/terminal-watchdog.test.ts +388 -0
- package/src/__tests__/test-utils.ts +271 -0
- package/src/__tests__/tmux-poc.ts +150 -0
- package/src/__tests__/unit/agent-mode-toggle.test.ts +137 -0
- package/src/__tests__/unit/copy-button.test.ts +157 -0
- package/src/__tests__/unit/create-run-config.test.ts +66 -0
- package/src/__tests__/unit/publish-confirmation.test.ts +70 -0
- package/src/__tests__/unit/segmented-control.test.ts +152 -0
- package/src/__tests__/utils/env.test.ts +169 -0
- package/src/__tests__/utils/project-picker.test.ts +39 -0
- package/src/agents/bundled-agents.generated.d.ts +14 -0
- package/src/agents/bundled-agents.generated.ts +3692 -0
- package/src/app.tsx +399 -0
- package/src/chat.tsx +1698 -0
- package/src/cli-args.ts +134 -0
- package/src/commands/__tests__/bash-command.test.ts +445 -0
- package/src/commands/__tests__/command-args.test.ts +349 -0
- package/src/commands/__tests__/copy-conversation-clipboard.test.ts +108 -0
- package/src/commands/__tests__/copy-conversation.test.ts +226 -0
- package/src/commands/__tests__/diagnostics-command.test.ts +35 -0
- package/src/commands/__tests__/freebuff-command-aliases.test.ts +56 -0
- package/src/commands/__tests__/image.test.ts +99 -0
- package/src/commands/__tests__/init.test.ts +456 -0
- package/src/commands/__tests__/new-command-rotates-chat.test.ts +91 -0
- package/src/commands/__tests__/process-diagnostics.test.ts +54 -0
- package/src/commands/__tests__/prompt-builders.test.ts +28 -0
- package/src/commands/__tests__/router-input.test.ts +298 -0
- package/src/commands/ads.ts +44 -0
- package/src/commands/command-registry.ts +726 -0
- package/src/commands/copy-conversation.ts +392 -0
- package/src/commands/help.ts +13 -0
- package/src/commands/image.ts +20 -0
- package/src/commands/init.ts +121 -0
- package/src/commands/process-diagnostics.ts +120 -0
- package/src/commands/prompt-builders.ts +100 -0
- package/src/commands/publish.ts +203 -0
- package/src/commands/router-utils.ts +80 -0
- package/src/commands/router.ts +487 -0
- package/src/commands/usage.ts +26 -0
- package/src/components/__tests__/ad-banner.test.tsx +83 -0
- package/src/components/__tests__/chat-input-bar.test.tsx +130 -0
- package/src/components/__tests__/freebuff-landing-heading-row.test.tsx +91 -0
- package/src/components/__tests__/freebuff-model-selector.test.tsx +519 -0
- package/src/components/__tests__/freebuff-offer-invariants.test.ts +54 -0
- package/src/components/__tests__/freebuff-takeover-prompt.test.tsx +212 -0
- package/src/components/__tests__/grid-layout.integration.test.tsx +304 -0
- package/src/components/__tests__/grid-layout.test.tsx +1051 -0
- package/src/components/__tests__/message-block.completion.test.tsx +104 -0
- package/src/components/__tests__/message-block.streaming.test.tsx +78 -0
- package/src/components/__tests__/message-with-agents.test.tsx +579 -0
- package/src/components/__tests__/multiline-input.test.tsx +1132 -0
- package/src/components/__tests__/queue-panel.test.tsx +398 -0
- package/src/components/__tests__/selectable-list.test.ts +232 -0
- package/src/components/__tests__/status-bar.test.tsx +44 -0
- package/src/components/__tests__/status-indicator.test.tsx +204 -0
- package/src/components/__tests__/terminal-thinking-orb.test.tsx +111 -0
- package/src/components/__tests__/user-error-banner.test.tsx +102 -0
- package/src/components/ad-banner.tsx +323 -0
- package/src/components/agent-checklist.tsx +350 -0
- package/src/components/agent-mode-toggle.tsx +241 -0
- package/src/components/anvil-mark.tsx +52 -0
- package/src/components/ask-user/__tests__/multiple-choice-form.test.ts +432 -0
- package/src/components/ask-user/__tests__/validation.test.ts +213 -0
- package/src/components/ask-user/components/accordion-question.tsx +153 -0
- package/src/components/ask-user/components/custom-answer-input.tsx +66 -0
- package/src/components/ask-user/components/options-list.tsx +133 -0
- package/src/components/ask-user/components/question-header.tsx +80 -0
- package/src/components/ask-user/components/question-option.tsx +89 -0
- package/src/components/ask-user/constants.ts +38 -0
- package/src/components/ask-user/index.tsx +631 -0
- package/src/components/ask-user/utils/validation.ts +85 -0
- package/src/components/attachment-card.tsx +65 -0
- package/src/components/blocks/agent-block-grid.tsx +58 -0
- package/src/components/blocks/agent-branch-item.tsx +311 -0
- package/src/components/blocks/agent-branch-wrapper.tsx +493 -0
- package/src/components/blocks/agent-list-branch.tsx +77 -0
- package/src/components/blocks/ask-user-branch.tsx +81 -0
- package/src/components/blocks/block-helpers.ts +11 -0
- package/src/components/blocks/blocks-renderer.tsx +289 -0
- package/src/components/blocks/content-with-markdown.tsx +33 -0
- package/src/components/blocks/image-block.tsx +128 -0
- package/src/components/blocks/implementor-row.tsx +429 -0
- package/src/components/blocks/single-block.tsx +201 -0
- package/src/components/blocks/thinking-block.tsx +67 -0
- package/src/components/blocks/tool-block-group.tsx +57 -0
- package/src/components/blocks/tool-branch.tsx +206 -0
- package/src/components/blocks/user-content-copy.tsx +157 -0
- package/src/components/bottom-banner.tsx +136 -0
- package/src/components/build-mode-buttons.tsx +110 -0
- package/src/components/button.tsx +66 -0
- package/src/components/chat-header.tsx +77 -0
- package/src/components/chat-history-screen.tsx +413 -0
- package/src/components/chat-input-bar.tsx +523 -0
- package/src/components/clickable-title-box.tsx +66 -0
- package/src/components/clickable.tsx +119 -0
- package/src/components/collapse-button.tsx +34 -0
- package/src/components/copy-button.tsx +203 -0
- package/src/components/elapsed-timer.tsx +55 -0
- package/src/components/error-boundary.tsx +55 -0
- package/src/components/feedback-container.tsx +181 -0
- package/src/components/feedback-icon-button.tsx +89 -0
- package/src/components/feedback-input-mode.tsx +344 -0
- package/src/components/file-attachment-card.tsx +98 -0
- package/src/components/freebuff-active-session-summary.tsx +63 -0
- package/src/components/freebuff-landing-screen.tsx +865 -0
- package/src/components/freebuff-model-selector.tsx +1087 -0
- package/src/components/freebuff-referral-banner.tsx +604 -0
- package/src/components/freebuff-superseded-screen.tsx +62 -0
- package/src/components/grid-layout.tsx +80 -0
- package/src/components/help-banner.tsx +127 -0
- package/src/components/highlighted-text.tsx +52 -0
- package/src/components/image-card.tsx +167 -0
- package/src/components/image-thumbnail.tsx +114 -0
- package/src/components/input-cursor.tsx +77 -0
- package/src/components/input-mode-banner.tsx +57 -0
- package/src/components/load-previous-button.tsx +48 -0
- package/src/components/login-modal.tsx +475 -0
- package/src/components/message-block.tsx +346 -0
- package/src/components/message-footer.tsx +258 -0
- package/src/components/message-with-agents.tsx +506 -0
- package/src/components/mode-divider.tsx +43 -0
- package/src/components/multiline-input.tsx +1241 -0
- package/src/components/out-of-credits-banner.tsx +160 -0
- package/src/components/pending-attachments-banner.tsx +116 -0
- package/src/components/pending-bash-message.tsx +59 -0
- package/src/components/progress-bar.tsx +81 -0
- package/src/components/project-picker-screen.tsx +485 -0
- package/src/components/publish-confirmation.tsx +493 -0
- package/src/components/publish-container.tsx +687 -0
- package/src/components/queue-panel.tsx +310 -0
- package/src/components/raised-pill.tsx +95 -0
- package/src/components/renderers/plan-box.tsx +59 -0
- package/src/components/review-screen.tsx +112 -0
- package/src/components/scroll-to-bottom-button.tsx +34 -0
- package/src/components/segmented-control.tsx +187 -0
- package/src/components/selectable-list.tsx +248 -0
- package/src/components/selected-chips.tsx +80 -0
- package/src/components/separator.tsx +40 -0
- package/src/components/session-ended-banner.tsx +226 -0
- package/src/components/shimmer-text.tsx +248 -0
- package/src/components/status-bar.tsx +313 -0
- package/src/components/subscription-limit-banner.tsx +206 -0
- package/src/components/suggested-prompts.tsx +184 -0
- package/src/components/suggestion-menu.tsx +215 -0
- package/src/components/terminal-command-display.tsx +151 -0
- package/src/components/terminal-link.tsx +97 -0
- package/src/components/terminal-thinking-orb.tsx +95 -0
- package/src/components/text-attachment-card.tsx +77 -0
- package/src/components/thinking.tsx +109 -0
- package/src/components/tools/__tests__/apply-patch.test.tsx +84 -0
- package/src/components/tools/__tests__/code-search.test.tsx +45 -0
- package/src/components/tools/__tests__/gravity-index.test.ts +76 -0
- package/src/components/tools/__tests__/render-ui.test.tsx +92 -0
- package/src/components/tools/__tests__/run-terminal-command.test.ts +233 -0
- package/src/components/tools/apply-patch.tsx +95 -0
- package/src/components/tools/code-search.tsx +45 -0
- package/src/components/tools/composio.tsx +152 -0
- package/src/components/tools/diff-viewer.tsx +76 -0
- package/src/components/tools/glob.tsx +61 -0
- package/src/components/tools/gravity-index.tsx +77 -0
- package/src/components/tools/list-directory.tsx +61 -0
- package/src/components/tools/read-docs.tsx +37 -0
- package/src/components/tools/read-files.tsx +91 -0
- package/src/components/tools/read-subtree.tsx +44 -0
- package/src/components/tools/read-url.tsx +33 -0
- package/src/components/tools/registry.ts +126 -0
- package/src/components/tools/render-ui.tsx +122 -0
- package/src/components/tools/run-terminal-command.tsx +79 -0
- package/src/components/tools/skill.tsx +29 -0
- package/src/components/tools/str-replace.tsx +77 -0
- package/src/components/tools/suggest-followups.tsx +369 -0
- package/src/components/tools/task-completed.tsx +16 -0
- package/src/components/tools/tool-call-item.tsx +273 -0
- package/src/components/tools/types.ts +55 -0
- package/src/components/tools/web-search.tsx +33 -0
- package/src/components/tools/write-file.tsx +16 -0
- package/src/components/tools/write-todos.tsx +95 -0
- package/src/components/top-banner.tsx +178 -0
- package/src/components/usage-banner.tsx +244 -0
- package/src/components/user-error-banner.tsx +56 -0
- package/src/components/validation-error-popover.tsx +192 -0
- package/src/contexts/__tests__/chat-runtime-context.test.tsx +149 -0
- package/src/contexts/chat-runtime-context.tsx +240 -0
- package/src/data/slash-commands.test.ts +24 -0
- package/src/data/slash-commands.ts +249 -0
- package/src/entry.ts +12 -0
- package/src/hooks/__tests__/holds-live-freebuff-slot.test.ts +49 -0
- package/src/hooks/__tests__/session-fetch-signal.test.ts +63 -0
- package/src/hooks/__tests__/use-activity-query.test.ts +1000 -0
- package/src/hooks/__tests__/use-ask-user-bridge.test.ts +176 -0
- package/src/hooks/__tests__/use-auth-query.test.ts +21 -0
- package/src/hooks/__tests__/use-chat-input-remount.test.tsx +66 -0
- package/src/hooks/__tests__/use-clipboard.test.tsx +73 -0
- package/src/hooks/__tests__/use-connection-status.test.ts +112 -0
- package/src/hooks/__tests__/use-directory-browser.test.ts +203 -0
- package/src/hooks/__tests__/use-gravity-ad.test.ts +73 -0
- package/src/hooks/__tests__/use-grid-layout.test.ts +349 -0
- package/src/hooks/__tests__/use-input-history.test.ts +699 -0
- package/src/hooks/__tests__/use-message-queue-editing.test.tsx +209 -0
- package/src/hooks/__tests__/use-message-queue.test.tsx +71 -0
- package/src/hooks/__tests__/use-path-tab-completion.test.ts +315 -0
- package/src/hooks/__tests__/use-queue-controls.test.ts +68 -0
- package/src/hooks/__tests__/use-queue-ui.test.tsx +83 -0
- package/src/hooks/__tests__/use-searchable-list.test.ts +359 -0
- package/src/hooks/__tests__/use-send-message-timer.test.ts +146 -0
- package/src/hooks/__tests__/use-suggestion-engine-mention.test.ts +361 -0
- package/src/hooks/__tests__/use-suggestion-engine.test.ts +485 -0
- package/src/hooks/__tests__/use-terminal-focus.test.ts +66 -0
- package/src/hooks/__tests__/use-terminal-layout.test.ts +675 -0
- package/src/hooks/__tests__/use-timeout.test.ts +330 -0
- package/src/hooks/__tests__/use-usage-query.test.ts +502 -0
- package/src/hooks/__tests__/use-user-details-query.test.ts +195 -0
- package/src/hooks/helpers/__tests__/send-message.test.ts +1918 -0
- package/src/hooks/helpers/send-message.ts +654 -0
- package/src/hooks/stream-state.ts +83 -0
- package/src/hooks/use-activity-query.ts +668 -0
- package/src/hooks/use-agent-validation.ts +74 -0
- package/src/hooks/use-ask-user-bridge.ts +92 -0
- package/src/hooks/use-auth-query.ts +253 -0
- package/src/hooks/use-auth-state.ts +152 -0
- package/src/hooks/use-chat-input.ts +105 -0
- package/src/hooks/use-chat-keyboard.ts +321 -0
- package/src/hooks/use-chat-messages.ts +254 -0
- package/src/hooks/use-chat-state.ts +165 -0
- package/src/hooks/use-chat-streaming.ts +180 -0
- package/src/hooks/use-chat-ui.ts +131 -0
- package/src/hooks/use-clipboard.ts +151 -0
- package/src/hooks/use-connection-status.ts +142 -0
- package/src/hooks/use-directory-browser.ts +88 -0
- package/src/hooks/use-elapsed-time.ts +116 -0
- package/src/hooks/use-event.ts +34 -0
- package/src/hooks/use-exit-handler.ts +66 -0
- package/src/hooks/use-fetch-login-url.ts +65 -0
- package/src/hooks/use-fingerprint.ts +61 -0
- package/src/hooks/use-freebuff-ctrl-c-exit.ts +23 -0
- package/src/hooks/use-freebuff-session-progress.ts +34 -0
- package/src/hooks/use-freebuff-session.ts +806 -0
- package/src/hooks/use-freebuff-streak-query.ts +61 -0
- package/src/hooks/use-gravity-ad.ts +656 -0
- package/src/hooks/use-grid-layout.ts +73 -0
- package/src/hooks/use-input-history.ts +173 -0
- package/src/hooks/use-login-keyboard-handlers.ts +75 -0
- package/src/hooks/use-login-polling.ts +114 -0
- package/src/hooks/use-logo.tsx +161 -0
- package/src/hooks/use-message-queue.ts +387 -0
- package/src/hooks/use-now.ts +20 -0
- package/src/hooks/use-path-tab-completion.ts +88 -0
- package/src/hooks/use-publish-mutation.ts +60 -0
- package/src/hooks/use-queue-controls.ts +68 -0
- package/src/hooks/use-queue-ui.ts +74 -0
- package/src/hooks/use-scroll-management.ts +184 -0
- package/src/hooks/use-searchable-list.ts +98 -0
- package/src/hooks/use-send-message.ts +773 -0
- package/src/hooks/use-sheen-animation.tsx +88 -0
- package/src/hooks/use-subscription-query.ts +71 -0
- package/src/hooks/use-suggestion-engine.ts +783 -0
- package/src/hooks/use-terminal-breakpoints.ts +77 -0
- package/src/hooks/use-terminal-dimensions.ts +46 -0
- package/src/hooks/use-terminal-focus.ts +29 -0
- package/src/hooks/use-terminal-layout.ts +189 -0
- package/src/hooks/use-theme.tsx +140 -0
- package/src/hooks/use-timeout.ts +85 -0
- package/src/hooks/use-update-preference.ts +66 -0
- package/src/hooks/use-usage-monitor.ts +55 -0
- package/src/hooks/use-usage-query.ts +124 -0
- package/src/hooks/use-user-details-query.ts +93 -0
- package/src/hooks/use-why-did-you-update.ts +177 -0
- package/src/index.tsx +441 -0
- package/src/init/__tests__/init-direnv.test.ts +526 -0
- package/src/init/init-app.ts +34 -0
- package/src/init/init-direnv.ts +133 -0
- package/src/login/constants.ts +60 -0
- package/src/login/login-flow.ts +204 -0
- package/src/login/plain-login.ts +108 -0
- package/src/login/utils.ts +183 -0
- package/src/native/ripgrep.ts +80 -0
- package/src/polyfills/bun-strip-ansi.ts +12 -0
- package/src/pre-init/tree-sitter-wasm.ts +96 -0
- package/src/project-files.ts +102 -0
- package/src/smoke/terminal-command-broker.ts +439 -0
- package/src/state/__tests__/chat-store-focus.test.ts +22 -0
- package/src/state/__tests__/feedback-store.test.ts +257 -0
- package/src/state/chat-history-store.ts +30 -0
- package/src/state/chat-store.ts +544 -0
- package/src/state/feedback-store.ts +160 -0
- package/src/state/freebuff-model-store.ts +42 -0
- package/src/state/freebuff-session-store.ts +53 -0
- package/src/state/login-store.ts +150 -0
- package/src/state/message-block-store.ts +149 -0
- package/src/state/publish-store.ts +146 -0
- package/src/state/queue-panel-store.ts +26 -0
- package/src/state/review-store.ts +24 -0
- package/src/testing/env.ts +46 -0
- package/src/types/chat-state.ts +15 -0
- package/src/types/chat.ts +241 -0
- package/src/types/contracts/send-message.ts +12 -0
- package/src/types/env.ts +98 -0
- package/src/types/freebuff-session.ts +17 -0
- package/src/types/function-params.ts +28 -0
- package/src/types/react19-compat.d.ts +19 -0
- package/src/types/store.ts +111 -0
- package/src/types/theme-system.ts +144 -0
- package/src/types/utils.ts +3 -0
- package/src/utils/__tests__/active-run.test.ts +140 -0
- package/src/utils/__tests__/activity-tracker.test.ts +440 -0
- package/src/utils/__tests__/agent-display.test.ts +139 -0
- package/src/utils/__tests__/analytics-client.test.ts +262 -0
- package/src/utils/__tests__/anonymous-id.test.ts +74 -0
- package/src/utils/__tests__/anvil-api.test.ts +578 -0
- package/src/utils/__tests__/arrays.test.ts +264 -0
- package/src/utils/__tests__/bash-context-processor.test.ts +38 -0
- package/src/utils/__tests__/block-processor.test.ts +810 -0
- package/src/utils/__tests__/chat-history.test.ts +203 -0
- package/src/utils/__tests__/chat-input-key-intercept.test.ts +40 -0
- package/src/utils/__tests__/chat-meta.test.ts +130 -0
- package/src/utils/__tests__/clipboard-linux.test.ts +359 -0
- package/src/utils/__tests__/clipboard.test.ts +910 -0
- package/src/utils/__tests__/code-search-summary.test.ts +84 -0
- package/src/utils/__tests__/collapse-helpers.test.ts +1109 -0
- package/src/utils/__tests__/error-handling.test.ts +658 -0
- package/src/utils/__tests__/exit-cleanly.test.ts +110 -0
- package/src/utils/__tests__/feedback-helpers.test.ts +444 -0
- package/src/utils/__tests__/feedback-submission.test.ts +26 -0
- package/src/utils/__tests__/fetch-usage.test.ts +355 -0
- package/src/utils/__tests__/fingerprint.test.ts +144 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-child.ts +7 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-entry.ts +3 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-owner.ts +33 -0
- package/src/utils/__tests__/format-elapsed-time.test.ts +103 -0
- package/src/utils/__tests__/format-timeout.test.ts +87 -0
- package/src/utils/__tests__/freebuff-instance-owner.test.ts +69 -0
- package/src/utils/__tests__/freebuff-model-navigation.test.ts +102 -0
- package/src/utils/__tests__/freebuff-premium-reset.test.ts +79 -0
- package/src/utils/__tests__/freebuff-referral-cache.test.ts +116 -0
- package/src/utils/__tests__/freebuff-session-api.test.ts +215 -0
- package/src/utils/__tests__/freebuff-session-display.test.ts +23 -0
- package/src/utils/__tests__/freebuff-streak-line.test.ts +217 -0
- package/src/utils/__tests__/image-dimensions.test.ts +249 -0
- package/src/utils/__tests__/image-processor.test.ts +209 -0
- package/src/utils/__tests__/implementor-helpers.test.ts +1458 -0
- package/src/utils/__tests__/keyboard-actions.test.ts +660 -0
- package/src/utils/__tests__/layout-helpers.test.ts +36 -0
- package/src/utils/__tests__/lazy-response-ads.test.ts +167 -0
- package/src/utils/__tests__/log-shipper.test.ts +53 -0
- package/src/utils/__tests__/markdown-renderer.test.tsx +421 -0
- package/src/utils/__tests__/message-block-helpers.test.ts +1263 -0
- package/src/utils/__tests__/message-updater.test.ts +645 -0
- package/src/utils/__tests__/osc-timeout-scenarios.test.ts +172 -0
- package/src/utils/__tests__/pending-attachments.test.ts +277 -0
- package/src/utils/__tests__/polling-backoff.test.ts +51 -0
- package/src/utils/__tests__/queue-panel-actions.test.ts +148 -0
- package/src/utils/__tests__/run-state-storage.test.ts +919 -0
- package/src/utils/__tests__/safe-json.test.ts +77 -0
- package/src/utils/__tests__/sdk-event-handlers.test.ts +685 -0
- package/src/utils/__tests__/send-message-helpers.test.ts +1898 -0
- package/src/utils/__tests__/send-message-timer.test.ts +64 -0
- package/src/utils/__tests__/settings.test.ts +99 -0
- package/src/utils/__tests__/strings.test.ts +192 -0
- package/src/utils/__tests__/terminal-color-detection.test.ts +425 -0
- package/src/utils/__tests__/terminal-command-broker.test.ts +528 -0
- package/src/utils/__tests__/terminal-enter-detection.test.ts +77 -0
- package/src/utils/__tests__/terminal-io.test.ts +137 -0
- package/src/utils/__tests__/terminal-protocol-controller.test.ts +119 -0
- package/src/utils/__tests__/text-layout.test.ts +82 -0
- package/src/utils/__tests__/theme-platform-detection.test.ts +125 -0
- package/src/utils/__tests__/think-tag-parser.test.ts +159 -0
- package/src/utils/__tests__/trace-writer.test.ts +223 -0
- package/src/utils/__tests__/trim-chat-logs.test.ts +78 -0
- package/src/utils/__tests__/usage-banner-state.test.ts +298 -0
- package/src/utils/__tests__/validation-error-formatting.test.ts +126 -0
- package/src/utils/__tests__/windows-terminal-health.test.ts +101 -0
- package/src/utils/__tests__/write-file-atomic.test.ts +127 -0
- package/src/utils/active-run.ts +131 -0
- package/src/utils/activity-tracker.ts +66 -0
- package/src/utils/agent-display.ts +87 -0
- package/src/utils/agent-helpers.ts +27 -0
- package/src/utils/agent-id-utils.ts +14 -0
- package/src/utils/ai-message-id.ts +12 -0
- package/src/utils/analytics.ts +361 -0
- package/src/utils/anonymous-id.ts +87 -0
- package/src/utils/anvil-api.ts +612 -0
- package/src/utils/anvil-client.ts +217 -0
- package/src/utils/arrays.ts +117 -0
- package/src/utils/auth.ts +258 -0
- package/src/utils/bash-context-processor.ts +47 -0
- package/src/utils/bash-messages.ts +108 -0
- package/src/utils/block-margins.ts +35 -0
- package/src/utils/block-operations.ts +523 -0
- package/src/utils/block-processor.ts +213 -0
- package/src/utils/chat-history.ts +253 -0
- package/src/utils/chat-input-key-intercept.ts +52 -0
- package/src/utils/chat-meta.ts +90 -0
- package/src/utils/chat-scroll-accel.ts +151 -0
- package/src/utils/clipboard-image.ts +588 -0
- package/src/utils/clipboard.ts +397 -0
- package/src/utils/code-search-summary.ts +70 -0
- package/src/utils/collapse-helpers.ts +256 -0
- package/src/utils/config-dir.ts +24 -0
- package/src/utils/constants.ts +190 -0
- package/src/utils/create-event-handler-state.ts +73 -0
- package/src/utils/create-run-config.ts +121 -0
- package/src/utils/detect-shell.ts +116 -0
- package/src/utils/directory-browser.ts +72 -0
- package/src/utils/engagement.ts +55 -0
- package/src/utils/env.ts +92 -0
- package/src/utils/error-handling.ts +255 -0
- package/src/utils/error-messages.ts +62 -0
- package/src/utils/exit-cleanly.ts +86 -0
- package/src/utils/feedback-helpers.ts +103 -0
- package/src/utils/feedback-submission.ts +22 -0
- package/src/utils/fetch-usage.ts +76 -0
- package/src/utils/fingerprint.ts +245 -0
- package/src/utils/format-elapsed-time.ts +33 -0
- package/src/utils/format-session-units.ts +6 -0
- package/src/utils/format-timeout.ts +28 -0
- package/src/utils/format-validation-errors-for-message.ts +84 -0
- package/src/utils/freebuff-agent-selection.ts +33 -0
- package/src/utils/freebuff-instance-owner.ts +66 -0
- package/src/utils/freebuff-model-navigation.ts +50 -0
- package/src/utils/freebuff-premium-reset.ts +55 -0
- package/src/utils/freebuff-referral-cache.ts +51 -0
- package/src/utils/freebuff-session-api.ts +243 -0
- package/src/utils/freebuff-session-display.ts +21 -0
- package/src/utils/freebuff-streak-line.ts +93 -0
- package/src/utils/git.ts +17 -0
- package/src/utils/helpers.ts +47 -0
- package/src/utils/image-display.ts +67 -0
- package/src/utils/image-handler.ts +325 -0
- package/src/utils/image-processor.ts +133 -0
- package/src/utils/image-thumbnail.ts +78 -0
- package/src/utils/implementor-helpers.ts +787 -0
- package/src/utils/input-modes.ts +174 -0
- package/src/utils/keyboard-actions.ts +392 -0
- package/src/utils/keypad-keys.ts +47 -0
- package/src/utils/layout-helpers.ts +33 -0
- package/src/utils/lazy-response-ads.ts +1 -0
- package/src/utils/local-agent-registry.ts +474 -0
- package/src/utils/log-shipper.ts +126 -0
- package/src/utils/logger.ts +327 -0
- package/src/utils/markdown-renderer.tsx +1090 -0
- package/src/utils/math.ts +3 -0
- package/src/utils/message-block-helpers.ts +684 -0
- package/src/utils/message-history.ts +143 -0
- package/src/utils/message-tree-utils.ts +67 -0
- package/src/utils/message-updater.ts +279 -0
- package/src/utils/open-file.ts +146 -0
- package/src/utils/open-url.ts +63 -0
- package/src/utils/path-completion.ts +111 -0
- package/src/utils/path-helpers.ts +38 -0
- package/src/utils/pending-attachments.ts +360 -0
- package/src/utils/polling-backoff.ts +59 -0
- package/src/utils/project-picker.ts +12 -0
- package/src/utils/queue-panel-actions.ts +70 -0
- package/src/utils/recent-projects.ts +156 -0
- package/src/utils/renderer-cleanup.ts +233 -0
- package/src/utils/response-ad-positions.ts +1 -0
- package/src/utils/run-state-storage.ts +603 -0
- package/src/utils/safe-json.ts +153 -0
- package/src/utils/sdk-event-handlers.ts +571 -0
- package/src/utils/send-message-helpers.ts +210 -0
- package/src/utils/send-message-timer.ts +110 -0
- package/src/utils/settings.ts +226 -0
- package/src/utils/skill-registry.ts +94 -0
- package/src/utils/spawn-agent-matcher.ts +55 -0
- package/src/utils/status-indicator-state.ts +107 -0
- package/src/utils/stream-chunk-processor.ts +57 -0
- package/src/utils/strings.ts +271 -0
- package/src/utils/subscription.ts +31 -0
- package/src/utils/syntax-highlighter.tsx +19 -0
- package/src/utils/terminal-color-detection.ts +474 -0
- package/src/utils/terminal-command-broker.ts +467 -0
- package/src/utils/terminal-enter-detection.ts +63 -0
- package/src/utils/terminal-images.ts +229 -0
- package/src/utils/terminal-io.ts +85 -0
- package/src/utils/terminal-protocol-controller.ts +173 -0
- package/src/utils/terminal-reset-sequences.ts +33 -0
- package/src/utils/terminal-title.ts +84 -0
- package/src/utils/terminal-watchdog.ts +366 -0
- package/src/utils/text-layout.ts +149 -0
- package/src/utils/theme-config.ts +141 -0
- package/src/utils/theme-system.ts +1206 -0
- package/src/utils/think-tag-parser.ts +95 -0
- package/src/utils/time-format.ts +21 -0
- package/src/utils/trace-writer.ts +162 -0
- package/src/utils/ui-constants.ts +69 -0
- package/src/utils/update-checker.ts +110 -0
- package/src/utils/usage-banner-state.ts +146 -0
- package/src/utils/validation-error-formatting.ts +86 -0
- package/src/utils/validation-error-helpers.ts +19 -0
- package/src/utils/windows-terminal-health.ts +124 -0
- package/src/utils/word-wrap-utils.ts +53 -0
- package/src/utils/write-file-atomic.ts +54 -0
- package/src/utils/yield-to-event-loop.ts +9 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { statSync } from 'fs'
|
|
2
|
+
|
|
3
|
+
import { useKeyboard } from '@opentui/react'
|
|
4
|
+
import { useCallback, useRef } from 'react'
|
|
5
|
+
|
|
6
|
+
import { getProjectRoot } from '../project-files'
|
|
7
|
+
import { reportActivity } from '../utils/activity-tracker'
|
|
8
|
+
import { hasClipboardImage, readClipboardText, readClipboardFilePath, getImageFilePathFromText } from '../utils/clipboard-image'
|
|
9
|
+
import { isImageFile } from '../utils/image-handler'
|
|
10
|
+
import {
|
|
11
|
+
resolveChatKeyboardAction,
|
|
12
|
+
type ChatKeyboardState,
|
|
13
|
+
type ChatKeyboardAction,
|
|
14
|
+
} from '../utils/keyboard-actions'
|
|
15
|
+
import { markReturnKeySeenForKey } from '../utils/terminal-enter-detection'
|
|
16
|
+
|
|
17
|
+
import type { KeyEvent } from '@opentui/core'
|
|
18
|
+
|
|
19
|
+
// Throttle interval for keyboard activity reporting (ms)
|
|
20
|
+
const KEYBOARD_ACTIVITY_THROTTLE_MS = 1000
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Handlers for chat keyboard actions.
|
|
24
|
+
* Each handler corresponds to a ChatKeyboardAction type.
|
|
25
|
+
*/
|
|
26
|
+
export type ChatKeyboardHandlers = {
|
|
27
|
+
// Mode handlers
|
|
28
|
+
onExitInputMode: () => void
|
|
29
|
+
onExitFeedbackMode: () => void
|
|
30
|
+
onClearFeedbackInput: () => void
|
|
31
|
+
|
|
32
|
+
// Input handlers
|
|
33
|
+
onClearInput: () => void
|
|
34
|
+
onBackspaceExitMode: () => void
|
|
35
|
+
|
|
36
|
+
// Stream handlers
|
|
37
|
+
onInterruptStream: () => void
|
|
38
|
+
|
|
39
|
+
// Slash menu handlers
|
|
40
|
+
onSlashMenuDown: () => void
|
|
41
|
+
onSlashMenuUp: () => void
|
|
42
|
+
onSlashMenuSelect: () => Promise<void> | void
|
|
43
|
+
onSlashMenuComplete: () => void
|
|
44
|
+
|
|
45
|
+
// Mention menu handlers
|
|
46
|
+
onMentionMenuDown: () => void
|
|
47
|
+
onMentionMenuUp: () => void
|
|
48
|
+
onMentionMenuTab: () => void
|
|
49
|
+
onMentionMenuShiftTab: () => void
|
|
50
|
+
onMentionMenuSelect: () => void
|
|
51
|
+
onMentionMenuComplete: () => void
|
|
52
|
+
|
|
53
|
+
// File menu handler
|
|
54
|
+
onOpenFileMenuWithTab: () => boolean // Returns true if menu was opened
|
|
55
|
+
|
|
56
|
+
// History handlers
|
|
57
|
+
onHistoryUp: () => void
|
|
58
|
+
onHistoryDown: () => void
|
|
59
|
+
|
|
60
|
+
// Agent handlers
|
|
61
|
+
onToggleAgentMode: () => void
|
|
62
|
+
onUnfocusAgent: () => void
|
|
63
|
+
|
|
64
|
+
// Queue handlers
|
|
65
|
+
onClearQueue: () => void
|
|
66
|
+
onOpenQueuePanel: () => void
|
|
67
|
+
|
|
68
|
+
// Exit handlers
|
|
69
|
+
onExitAppWarning: () => void
|
|
70
|
+
onExitApp: () => void
|
|
71
|
+
|
|
72
|
+
// Bash history handlers
|
|
73
|
+
onBashHistoryUp: () => void
|
|
74
|
+
onBashHistoryDown: () => void
|
|
75
|
+
|
|
76
|
+
// Clipboard handlers
|
|
77
|
+
onPasteImage: () => void
|
|
78
|
+
onPasteImagePath: (imagePath: string) => void
|
|
79
|
+
onPasteFilePath: (filePath: string, isDirectory: boolean) => void
|
|
80
|
+
onPasteText: (text: string) => void
|
|
81
|
+
|
|
82
|
+
// Scroll handlers
|
|
83
|
+
onScrollUp: () => void
|
|
84
|
+
onScrollDown: () => void
|
|
85
|
+
|
|
86
|
+
// Toggle all handler
|
|
87
|
+
onToggleAll: () => void
|
|
88
|
+
|
|
89
|
+
// Out of credits handler
|
|
90
|
+
onOpenBuyCredits: () => void
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Options for the useChatKeyboard hook.
|
|
95
|
+
*/
|
|
96
|
+
export type UseChatKeyboardOptions = {
|
|
97
|
+
/** Current keyboard state extracted from stores */
|
|
98
|
+
state: ChatKeyboardState
|
|
99
|
+
/** Handlers for keyboard actions */
|
|
100
|
+
handlers: ChatKeyboardHandlers
|
|
101
|
+
/** Whether keyboard handling is disabled (e.g., during ask-user) */
|
|
102
|
+
disabled?: boolean
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Dispatches a keyboard action to the appropriate handler.
|
|
107
|
+
*/
|
|
108
|
+
function assertNever(action: never): never {
|
|
109
|
+
throw new Error(`Unhandled chat keyboard action: ${String(action)}`)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function dispatchAction(
|
|
113
|
+
action: ChatKeyboardAction,
|
|
114
|
+
handlers: ChatKeyboardHandlers,
|
|
115
|
+
): boolean {
|
|
116
|
+
switch (action.type) {
|
|
117
|
+
case 'exit-input-mode':
|
|
118
|
+
handlers.onExitInputMode()
|
|
119
|
+
return true
|
|
120
|
+
case 'exit-feedback-mode':
|
|
121
|
+
handlers.onExitFeedbackMode()
|
|
122
|
+
return true
|
|
123
|
+
case 'clear-feedback-input':
|
|
124
|
+
handlers.onClearFeedbackInput()
|
|
125
|
+
return true
|
|
126
|
+
case 'clear-input':
|
|
127
|
+
handlers.onClearInput()
|
|
128
|
+
return true
|
|
129
|
+
case 'backspace-exit-mode':
|
|
130
|
+
handlers.onBackspaceExitMode()
|
|
131
|
+
return true
|
|
132
|
+
case 'interrupt-stream':
|
|
133
|
+
handlers.onInterruptStream()
|
|
134
|
+
return true
|
|
135
|
+
case 'slash-menu-down':
|
|
136
|
+
handlers.onSlashMenuDown()
|
|
137
|
+
return true
|
|
138
|
+
case 'slash-menu-up':
|
|
139
|
+
handlers.onSlashMenuUp()
|
|
140
|
+
return true
|
|
141
|
+
case 'slash-menu-select':
|
|
142
|
+
handlers.onSlashMenuSelect()
|
|
143
|
+
return true
|
|
144
|
+
case 'slash-menu-complete':
|
|
145
|
+
handlers.onSlashMenuComplete()
|
|
146
|
+
return true
|
|
147
|
+
case 'mention-menu-down':
|
|
148
|
+
handlers.onMentionMenuDown()
|
|
149
|
+
return true
|
|
150
|
+
case 'mention-menu-up':
|
|
151
|
+
handlers.onMentionMenuUp()
|
|
152
|
+
return true
|
|
153
|
+
case 'mention-menu-tab':
|
|
154
|
+
handlers.onMentionMenuTab()
|
|
155
|
+
return true
|
|
156
|
+
case 'mention-menu-shift-tab':
|
|
157
|
+
handlers.onMentionMenuShiftTab()
|
|
158
|
+
return true
|
|
159
|
+
case 'mention-menu-select':
|
|
160
|
+
handlers.onMentionMenuSelect()
|
|
161
|
+
return true
|
|
162
|
+
case 'mention-menu-complete':
|
|
163
|
+
handlers.onMentionMenuComplete()
|
|
164
|
+
return true
|
|
165
|
+
case 'open-file-menu-with-tab': {
|
|
166
|
+
const opened = handlers.onOpenFileMenuWithTab()
|
|
167
|
+
if (!opened) {
|
|
168
|
+
handlers.onToggleAgentMode()
|
|
169
|
+
}
|
|
170
|
+
return true
|
|
171
|
+
}
|
|
172
|
+
case 'history-up':
|
|
173
|
+
handlers.onHistoryUp()
|
|
174
|
+
return true
|
|
175
|
+
case 'history-down':
|
|
176
|
+
handlers.onHistoryDown()
|
|
177
|
+
return true
|
|
178
|
+
case 'toggle-agent-mode':
|
|
179
|
+
handlers.onToggleAgentMode()
|
|
180
|
+
return true
|
|
181
|
+
case 'unfocus-agent':
|
|
182
|
+
handlers.onUnfocusAgent()
|
|
183
|
+
return true
|
|
184
|
+
case 'clear-queue':
|
|
185
|
+
handlers.onClearQueue()
|
|
186
|
+
return true
|
|
187
|
+
case 'open-queue-panel':
|
|
188
|
+
handlers.onOpenQueuePanel()
|
|
189
|
+
return true
|
|
190
|
+
case 'exit-app-warning':
|
|
191
|
+
handlers.onExitAppWarning()
|
|
192
|
+
return true
|
|
193
|
+
case 'exit-app':
|
|
194
|
+
handlers.onExitApp()
|
|
195
|
+
return true
|
|
196
|
+
case 'bash-history-up':
|
|
197
|
+
handlers.onBashHistoryUp()
|
|
198
|
+
return true
|
|
199
|
+
case 'bash-history-down':
|
|
200
|
+
handlers.onBashHistoryDown()
|
|
201
|
+
return true
|
|
202
|
+
case 'paste': {
|
|
203
|
+
const cwd = getProjectRoot() ?? process.cwd()
|
|
204
|
+
|
|
205
|
+
// First, check if clipboard contains a copied file (e.g., from Finder)
|
|
206
|
+
// This is different from text - it's when you Cmd+C a file in Finder
|
|
207
|
+
const copiedFilePath = readClipboardFilePath()
|
|
208
|
+
if (copiedFilePath) {
|
|
209
|
+
if (isImageFile(copiedFilePath)) {
|
|
210
|
+
handlers.onPasteImagePath(copiedFilePath)
|
|
211
|
+
return true
|
|
212
|
+
}
|
|
213
|
+
// Non-image file or directory
|
|
214
|
+
try {
|
|
215
|
+
const fileStats = statSync(copiedFilePath)
|
|
216
|
+
handlers.onPasteFilePath(copiedFilePath, fileStats.isDirectory())
|
|
217
|
+
return true
|
|
218
|
+
} catch {
|
|
219
|
+
// Fall through to other paste handlers
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Next, read clipboard text to check if it's a file path
|
|
224
|
+
// This handles the case where a file is dragged/dropped - we want to use
|
|
225
|
+
// the file path, not any stale image data that might be in the clipboard
|
|
226
|
+
const rawText = readClipboardText()
|
|
227
|
+
const text = rawText ? Bun.stripANSI(rawText) : null
|
|
228
|
+
if (text) {
|
|
229
|
+
// Check if the text is a path to an image file
|
|
230
|
+
const imagePath = getImageFilePathFromText(text, cwd)
|
|
231
|
+
if (imagePath) {
|
|
232
|
+
handlers.onPasteImagePath(imagePath)
|
|
233
|
+
return true
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Check for actual image data in clipboard (screenshots, copied images)
|
|
238
|
+
// This comes AFTER the file path check so dragged files take priority
|
|
239
|
+
if (hasClipboardImage()) {
|
|
240
|
+
handlers.onPasteImage()
|
|
241
|
+
return true
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Regular text paste
|
|
245
|
+
if (text) {
|
|
246
|
+
handlers.onPasteText(text)
|
|
247
|
+
return true
|
|
248
|
+
}
|
|
249
|
+
return true
|
|
250
|
+
}
|
|
251
|
+
case 'scroll-up':
|
|
252
|
+
handlers.onScrollUp()
|
|
253
|
+
return true
|
|
254
|
+
case 'scroll-down':
|
|
255
|
+
handlers.onScrollDown()
|
|
256
|
+
return true
|
|
257
|
+
case 'toggle-all':
|
|
258
|
+
handlers.onToggleAll()
|
|
259
|
+
return true
|
|
260
|
+
case 'open-buy-credits':
|
|
261
|
+
handlers.onOpenBuyCredits()
|
|
262
|
+
return true
|
|
263
|
+
case 'none':
|
|
264
|
+
return false
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return assertNever(action)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Hook for handling keyboard input in chat text input contexts.
|
|
272
|
+
* Integrates priority-based action resolution with handlers.
|
|
273
|
+
*
|
|
274
|
+
* This hook handles:
|
|
275
|
+
* - Mode switching (bash, etc.)
|
|
276
|
+
* - Stream interruption
|
|
277
|
+
* - Suggestion menu navigation (slash and mention menus)
|
|
278
|
+
* - History navigation
|
|
279
|
+
* - Agent mode toggle
|
|
280
|
+
* - Exit handling
|
|
281
|
+
*
|
|
282
|
+
* For feedback mode, the hook respects the feedbackMode state and routes
|
|
283
|
+
* escape/ctrl-c appropriately.
|
|
284
|
+
*/
|
|
285
|
+
export function useChatKeyboard({
|
|
286
|
+
state,
|
|
287
|
+
handlers,
|
|
288
|
+
disabled = false,
|
|
289
|
+
}: UseChatKeyboardOptions): void {
|
|
290
|
+
const lastKeyboardActivityRef = useRef<number>(0)
|
|
291
|
+
|
|
292
|
+
useKeyboard(
|
|
293
|
+
useCallback(
|
|
294
|
+
(key: KeyEvent) => {
|
|
295
|
+
if (disabled) return
|
|
296
|
+
|
|
297
|
+
// Report keyboard activity for activity-aware features (throttled)
|
|
298
|
+
const now = Date.now()
|
|
299
|
+
if (now - lastKeyboardActivityRef.current > KEYBOARD_ACTIVITY_THROTTLE_MS) {
|
|
300
|
+
lastKeyboardActivityRef.current = now
|
|
301
|
+
reportActivity()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
markReturnKeySeenForKey(key)
|
|
305
|
+
|
|
306
|
+
const action = resolveChatKeyboardAction(key, state)
|
|
307
|
+
const handled = dispatchAction(action, handlers)
|
|
308
|
+
|
|
309
|
+
// Prevent default for handled actions
|
|
310
|
+
if (
|
|
311
|
+
handled &&
|
|
312
|
+
'preventDefault' in key &&
|
|
313
|
+
typeof key.preventDefault === 'function'
|
|
314
|
+
) {
|
|
315
|
+
key.preventDefault()
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
[state, handlers, disabled],
|
|
319
|
+
),
|
|
320
|
+
)
|
|
321
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracted chat messages hook.
|
|
3
|
+
* Handles message tree building, pagination, and collapse state management.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
7
|
+
|
|
8
|
+
import { setAllBlocksCollapsedState, hasAnyExpandedBlocks } from '../utils/collapse-helpers'
|
|
9
|
+
import { buildMessageTree } from '../utils/message-tree-utils'
|
|
10
|
+
|
|
11
|
+
import type { ChatMessage, ContentBlock } from '../types/chat'
|
|
12
|
+
|
|
13
|
+
/** Batch size for message pagination */
|
|
14
|
+
const MESSAGE_BATCH_SIZE = 15
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Options for useChatMessages hook.
|
|
18
|
+
*/
|
|
19
|
+
export interface UseChatMessagesOptions {
|
|
20
|
+
/** Current messages array from store */
|
|
21
|
+
messages: ChatMessage[]
|
|
22
|
+
/** Setter for messages */
|
|
23
|
+
setMessages: (
|
|
24
|
+
value: ChatMessage[] | ((prev: ChatMessage[]) => ChatMessage[]),
|
|
25
|
+
) => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Return type for useChatMessages hook.
|
|
30
|
+
*/
|
|
31
|
+
export interface UseChatMessagesReturn {
|
|
32
|
+
/** Map of parent ID to child messages */
|
|
33
|
+
messageTree: Map<string, ChatMessage[]>
|
|
34
|
+
/** Messages without a parent (root level) */
|
|
35
|
+
topLevelMessages: ChatMessage[]
|
|
36
|
+
/** Paginated visible messages from top level */
|
|
37
|
+
visibleTopLevelMessages: ChatMessage[]
|
|
38
|
+
/** Count of hidden messages due to pagination */
|
|
39
|
+
hiddenMessageCount: number
|
|
40
|
+
/** Handler to toggle collapsed state of a block */
|
|
41
|
+
handleCollapseToggle: (id: string) => void
|
|
42
|
+
/** Returns true if user is currently collapsing (to prevent auto-scroll) */
|
|
43
|
+
isUserCollapsing: () => boolean
|
|
44
|
+
/** Handler to load more previous messages */
|
|
45
|
+
handleLoadPreviousMessages: () => void
|
|
46
|
+
/** Handler to toggle all collapsed/expanded state in all AI responses */
|
|
47
|
+
handleToggleAll: () => void
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Custom hook that encapsulates message handling logic.
|
|
52
|
+
* Extracts message tree building, pagination, and collapse management.
|
|
53
|
+
*
|
|
54
|
+
* @param options - Messages array and setter from store
|
|
55
|
+
* @returns Message tree, pagination state, and handlers
|
|
56
|
+
*/
|
|
57
|
+
export function useChatMessages({
|
|
58
|
+
messages,
|
|
59
|
+
setMessages,
|
|
60
|
+
}: UseChatMessagesOptions): UseChatMessagesReturn {
|
|
61
|
+
// Message pagination state
|
|
62
|
+
const [visibleMessageCount, setVisibleMessageCount] =
|
|
63
|
+
useState(MESSAGE_BATCH_SIZE)
|
|
64
|
+
|
|
65
|
+
// Reset visible message count when messages are cleared or conversation changes
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (messages.length <= MESSAGE_BATCH_SIZE) {
|
|
68
|
+
setVisibleMessageCount(MESSAGE_BATCH_SIZE)
|
|
69
|
+
}
|
|
70
|
+
}, [messages.length])
|
|
71
|
+
|
|
72
|
+
// Ref to track user-initiated collapse (prevents auto-scroll during collapse)
|
|
73
|
+
const isUserCollapsingRef = useRef<boolean>(false)
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns true if user is currently collapsing.
|
|
77
|
+
* Used by scroll management to prevent auto-scroll during collapse.
|
|
78
|
+
*/
|
|
79
|
+
const isUserCollapsing = useCallback(() => {
|
|
80
|
+
return isUserCollapsingRef.current
|
|
81
|
+
}, [])
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Toggles the collapsed state of a block or agent message.
|
|
85
|
+
* Handles both top-level agent messages and nested content blocks.
|
|
86
|
+
*/
|
|
87
|
+
const handleCollapseToggle = useCallback(
|
|
88
|
+
(id: string) => {
|
|
89
|
+
// Set flag to prevent auto-scroll during user-initiated collapse
|
|
90
|
+
isUserCollapsingRef.current = true
|
|
91
|
+
|
|
92
|
+
// Find and toggle the block's isCollapsed property
|
|
93
|
+
setMessages((prevMessages) => {
|
|
94
|
+
return prevMessages.map((message) => {
|
|
95
|
+
// Handle agent variant messages
|
|
96
|
+
if (message.variant === 'agent' && message.id === id) {
|
|
97
|
+
const wasCollapsed = message.metadata?.isCollapsed ?? false
|
|
98
|
+
return {
|
|
99
|
+
...message,
|
|
100
|
+
metadata: {
|
|
101
|
+
...message.metadata,
|
|
102
|
+
isCollapsed: !wasCollapsed,
|
|
103
|
+
userOpened: wasCollapsed, // Mark as user-opened if expanding
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Handle blocks within messages
|
|
109
|
+
if (!message.blocks) return message
|
|
110
|
+
|
|
111
|
+
const updateBlocksRecursively = (
|
|
112
|
+
blocks: ContentBlock[],
|
|
113
|
+
): ContentBlock[] => {
|
|
114
|
+
let foundTarget = false
|
|
115
|
+
const result = blocks.map((block) => {
|
|
116
|
+
// Handle thinking blocks - just match by thinkingId
|
|
117
|
+
if (block.type === 'text' && block.thinkingId === id) {
|
|
118
|
+
foundTarget = true
|
|
119
|
+
const isExpanded = block.thinkingCollapseState === 'expanded'
|
|
120
|
+
return {
|
|
121
|
+
...block,
|
|
122
|
+
thinkingCollapseState: isExpanded ? 'preview' as const : 'expanded' as const,
|
|
123
|
+
userOpened: !isExpanded, // Mark as user-opened if expanding
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Handle agent blocks
|
|
128
|
+
if (block.type === 'agent' && block.agentId === id) {
|
|
129
|
+
foundTarget = true
|
|
130
|
+
const wasCollapsed = block.isCollapsed ?? false
|
|
131
|
+
return {
|
|
132
|
+
...block,
|
|
133
|
+
isCollapsed: !wasCollapsed,
|
|
134
|
+
userOpened: wasCollapsed, // Mark as user-opened if expanding
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Handle tool blocks
|
|
139
|
+
if (block.type === 'tool' && block.toolCallId === id) {
|
|
140
|
+
foundTarget = true
|
|
141
|
+
const wasCollapsed = block.isCollapsed ?? false
|
|
142
|
+
return {
|
|
143
|
+
...block,
|
|
144
|
+
isCollapsed: !wasCollapsed,
|
|
145
|
+
userOpened: wasCollapsed, // Mark as user-opened if expanding
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Handle agent-list blocks
|
|
150
|
+
if (block.type === 'agent-list' && block.id === id) {
|
|
151
|
+
foundTarget = true
|
|
152
|
+
const wasCollapsed = block.isCollapsed ?? false
|
|
153
|
+
return {
|
|
154
|
+
...block,
|
|
155
|
+
isCollapsed: !wasCollapsed,
|
|
156
|
+
userOpened: wasCollapsed, // Mark as user-opened if expanding
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Recursively update nested blocks inside agent blocks
|
|
161
|
+
if (block.type === 'agent' && block.blocks) {
|
|
162
|
+
const updatedBlocks = updateBlocksRecursively(block.blocks)
|
|
163
|
+
// Only create new block if nested blocks actually changed
|
|
164
|
+
if (updatedBlocks !== block.blocks) {
|
|
165
|
+
foundTarget = true
|
|
166
|
+
return {
|
|
167
|
+
...block,
|
|
168
|
+
blocks: updatedBlocks,
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return block
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// Return original array reference if nothing changed
|
|
177
|
+
return foundTarget ? result : blocks
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
...message,
|
|
182
|
+
blocks: updateBlocksRecursively(message.blocks),
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// Reset flag after state update completes.
|
|
188
|
+
// Uses setTimeout(0) to defer until after React's batched state updates
|
|
189
|
+
// have been applied, ensuring the flag stays true during the render cycle.
|
|
190
|
+
setTimeout(() => {
|
|
191
|
+
isUserCollapsingRef.current = false
|
|
192
|
+
}, 0)
|
|
193
|
+
},
|
|
194
|
+
[setMessages],
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Loads more previous messages by increasing the visible count.
|
|
199
|
+
*/
|
|
200
|
+
const handleLoadPreviousMessages = useCallback(() => {
|
|
201
|
+
setVisibleMessageCount((prev) => prev + MESSAGE_BATCH_SIZE)
|
|
202
|
+
}, [])
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Toggles all collapsible blocks in all AI responses.
|
|
206
|
+
* Primary action is to collapse all. Only expands if everything is already collapsed.
|
|
207
|
+
*/
|
|
208
|
+
const handleToggleAll = useCallback(() => {
|
|
209
|
+
isUserCollapsingRef.current = true
|
|
210
|
+
|
|
211
|
+
setMessages((prevMessages) => {
|
|
212
|
+
// Primary action: collapse all open blocks
|
|
213
|
+
// Only expand if everything is already collapsed
|
|
214
|
+
const allCollapsed = !hasAnyExpandedBlocks(prevMessages)
|
|
215
|
+
const shouldCollapse = !allCollapsed
|
|
216
|
+
return setAllBlocksCollapsedState(prevMessages, shouldCollapse)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
// Reset flag after state update completes.
|
|
220
|
+
// Uses setTimeout(0) to defer until after React's batched state updates
|
|
221
|
+
// have been applied, ensuring the flag stays true during the render cycle.
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
isUserCollapsingRef.current = false
|
|
224
|
+
}, 0)
|
|
225
|
+
}, [setMessages])
|
|
226
|
+
|
|
227
|
+
// Build message tree from flat messages array
|
|
228
|
+
const { tree: messageTree, topLevelMessages } = useMemo(
|
|
229
|
+
() => buildMessageTree(messages),
|
|
230
|
+
[messages],
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
// Compute visible messages slice (from the end)
|
|
234
|
+
const visibleTopLevelMessages = useMemo(() => {
|
|
235
|
+
if (topLevelMessages.length <= visibleMessageCount) {
|
|
236
|
+
return topLevelMessages
|
|
237
|
+
}
|
|
238
|
+
return topLevelMessages.slice(-visibleMessageCount)
|
|
239
|
+
}, [topLevelMessages, visibleMessageCount])
|
|
240
|
+
|
|
241
|
+
const hiddenMessageCount =
|
|
242
|
+
topLevelMessages.length - visibleTopLevelMessages.length
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
messageTree,
|
|
246
|
+
topLevelMessages,
|
|
247
|
+
visibleTopLevelMessages,
|
|
248
|
+
hiddenMessageCount,
|
|
249
|
+
handleCollapseToggle,
|
|
250
|
+
isUserCollapsing,
|
|
251
|
+
handleLoadPreviousMessages,
|
|
252
|
+
handleToggleAll,
|
|
253
|
+
}
|
|
254
|
+
}
|