@butlerw/vellum 0.1.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/LICENSE +21 -0
- package/README.md +411 -0
- package/__fixtures__/responses/code-generation.json +42 -0
- package/__fixtures__/responses/error-response.json +20 -0
- package/__fixtures__/responses/hello-world.json +32 -0
- package/dist/auth-6MCXESOH.js +26 -0
- package/dist/chunk-SECXJGWA.js +597 -0
- package/dist/index.js +34023 -0
- package/package.json +67 -0
- package/src/__tests__/commands.e2e.test.ts +728 -0
- package/src/__tests__/credentials.test.ts +713 -0
- package/src/__tests__/mode-e2e.test.ts +391 -0
- package/src/__tests__/tui-integration.test.tsx +1271 -0
- package/src/agents/__tests__/task-persistence.test.ts +235 -0
- package/src/agents/commands/delegate.ts +240 -0
- package/src/agents/commands/index.ts +10 -0
- package/src/agents/commands/resume.ts +335 -0
- package/src/agents/index.ts +29 -0
- package/src/agents/task-persistence.ts +272 -0
- package/src/agents/task-resumption.ts +242 -0
- package/src/app.tsx +4737 -0
- package/src/commands/__tests__/.gitkeep +1 -0
- package/src/commands/__tests__/agents.test.ts +606 -0
- package/src/commands/__tests__/auth.test.ts +626 -0
- package/src/commands/__tests__/autocomplete.test.ts +683 -0
- package/src/commands/__tests__/batch.test.ts +287 -0
- package/src/commands/__tests__/chain-pipe-parser.test.ts +654 -0
- package/src/commands/__tests__/completion.test.ts +238 -0
- package/src/commands/__tests__/core.test.ts +363 -0
- package/src/commands/__tests__/executor.test.ts +496 -0
- package/src/commands/__tests__/exit-codes.test.ts +220 -0
- package/src/commands/__tests__/init.test.ts +243 -0
- package/src/commands/__tests__/language.test.ts +353 -0
- package/src/commands/__tests__/mode-cli.test.ts +667 -0
- package/src/commands/__tests__/model.test.ts +277 -0
- package/src/commands/__tests__/parser.test.ts +493 -0
- package/src/commands/__tests__/performance.bench.ts +380 -0
- package/src/commands/__tests__/registry.test.ts +534 -0
- package/src/commands/__tests__/resume.test.ts +449 -0
- package/src/commands/__tests__/security.test.ts +845 -0
- package/src/commands/__tests__/stream-json.test.ts +372 -0
- package/src/commands/__tests__/user-commands.test.ts +597 -0
- package/src/commands/adapters.ts +267 -0
- package/src/commands/agent.ts +395 -0
- package/src/commands/agents/generate.ts +506 -0
- package/src/commands/agents/index.ts +272 -0
- package/src/commands/agents/show.ts +271 -0
- package/src/commands/agents/validate.ts +387 -0
- package/src/commands/auth.ts +883 -0
- package/src/commands/autocomplete.ts +480 -0
- package/src/commands/batch/command.ts +388 -0
- package/src/commands/batch/executor.ts +361 -0
- package/src/commands/batch/index.ts +12 -0
- package/src/commands/commit.ts +235 -0
- package/src/commands/completion/index.ts +371 -0
- package/src/commands/condense.ts +191 -0
- package/src/commands/config.ts +344 -0
- package/src/commands/context-provider.ts +173 -0
- package/src/commands/copy.ts +329 -0
- package/src/commands/core/clear.ts +38 -0
- package/src/commands/core/exit.ts +43 -0
- package/src/commands/core/help.ts +354 -0
- package/src/commands/core/index.ts +15 -0
- package/src/commands/cost.ts +179 -0
- package/src/commands/credentials.tsx +618 -0
- package/src/commands/custom-agents/__tests__/custom-agents.test.ts +709 -0
- package/src/commands/custom-agents/create.ts +377 -0
- package/src/commands/custom-agents/export.ts +135 -0
- package/src/commands/custom-agents/import.ts +199 -0
- package/src/commands/custom-agents/index.ts +372 -0
- package/src/commands/custom-agents/info.ts +318 -0
- package/src/commands/custom-agents/list.ts +267 -0
- package/src/commands/custom-agents/validate.ts +388 -0
- package/src/commands/diff-mode.ts +241 -0
- package/src/commands/env.ts +53 -0
- package/src/commands/executor.ts +579 -0
- package/src/commands/exit-codes.ts +202 -0
- package/src/commands/index.ts +701 -0
- package/src/commands/init/index.ts +15 -0
- package/src/commands/init/prompts.ts +366 -0
- package/src/commands/init/templates/commands-readme.md +80 -0
- package/src/commands/init/templates/example-command.md +79 -0
- package/src/commands/init/templates/example-skill.md +168 -0
- package/src/commands/init/templates/example-workflow.md +101 -0
- package/src/commands/init/templates/prompts-readme.md +52 -0
- package/src/commands/init/templates/rules-readme.md +63 -0
- package/src/commands/init/templates/skills-readme.md +83 -0
- package/src/commands/init/templates/workflows-readme.md +94 -0
- package/src/commands/init.ts +391 -0
- package/src/commands/install.ts +90 -0
- package/src/commands/language.ts +191 -0
- package/src/commands/loaders/.gitkeep +1 -0
- package/src/commands/lsp.ts +199 -0
- package/src/commands/markdown-commands.ts +253 -0
- package/src/commands/mcp.ts +588 -0
- package/src/commands/memory/export.ts +341 -0
- package/src/commands/memory/index.ts +148 -0
- package/src/commands/memory/list.ts +261 -0
- package/src/commands/memory/search.ts +346 -0
- package/src/commands/memory/utils.ts +15 -0
- package/src/commands/metrics.ts +75 -0
- package/src/commands/migrate/index.ts +16 -0
- package/src/commands/migrate/prompts.ts +477 -0
- package/src/commands/mode.ts +331 -0
- package/src/commands/model.ts +298 -0
- package/src/commands/onboard.ts +205 -0
- package/src/commands/open.ts +169 -0
- package/src/commands/output/stream-json.ts +373 -0
- package/src/commands/parser/chain-parser.ts +370 -0
- package/src/commands/parser/index.ts +29 -0
- package/src/commands/parser/pipe-parser.ts +480 -0
- package/src/commands/parser.ts +588 -0
- package/src/commands/persistence.ts +355 -0
- package/src/commands/progress.ts +18 -0
- package/src/commands/prompt/index.ts +17 -0
- package/src/commands/prompt/validate.ts +621 -0
- package/src/commands/prompt-priority.ts +401 -0
- package/src/commands/registry.ts +374 -0
- package/src/commands/sandbox/index.ts +131 -0
- package/src/commands/security/index.ts +21 -0
- package/src/commands/security/input-sanitizer.ts +168 -0
- package/src/commands/security/permission-checker.ts +456 -0
- package/src/commands/security/sensitive-data.ts +350 -0
- package/src/commands/session/delete.ts +38 -0
- package/src/commands/session/export.ts +39 -0
- package/src/commands/session/index.ts +26 -0
- package/src/commands/session/list.ts +26 -0
- package/src/commands/session/resume.ts +562 -0
- package/src/commands/session/search.ts +434 -0
- package/src/commands/session/show.ts +26 -0
- package/src/commands/settings.ts +368 -0
- package/src/commands/setup.ts +23 -0
- package/src/commands/shell/index.ts +16 -0
- package/src/commands/shell/setup.ts +422 -0
- package/src/commands/shell-init.ts +50 -0
- package/src/commands/shell-integration/index.ts +194 -0
- package/src/commands/skill.ts +1220 -0
- package/src/commands/spec.ts +558 -0
- package/src/commands/status.ts +246 -0
- package/src/commands/theme.ts +211 -0
- package/src/commands/think.ts +551 -0
- package/src/commands/trust.ts +211 -0
- package/src/commands/tutorial.ts +522 -0
- package/src/commands/types.ts +512 -0
- package/src/commands/update.ts +274 -0
- package/src/commands/usage.ts +213 -0
- package/src/commands/user-commands.ts +630 -0
- package/src/commands/utils.ts +142 -0
- package/src/commands/vim.ts +152 -0
- package/src/commands/workflow.ts +257 -0
- package/src/components/header.tsx +25 -0
- package/src/components/input.tsx +25 -0
- package/src/components/message-list.tsx +32 -0
- package/src/components/status-bar.tsx +23 -0
- package/src/index.tsx +614 -0
- package/src/onboarding/__tests__/tutorial.test.ts +740 -0
- package/src/onboarding/index.ts +69 -0
- package/src/onboarding/tips/index.ts +9 -0
- package/src/onboarding/tips/tip-engine.ts +459 -0
- package/src/onboarding/tutorial/index.ts +88 -0
- package/src/onboarding/tutorial/lessons/basics.ts +151 -0
- package/src/onboarding/tutorial/lessons/index.ts +151 -0
- package/src/onboarding/tutorial/lessons/modes.ts +230 -0
- package/src/onboarding/tutorial/lessons/tools.ts +172 -0
- package/src/onboarding/tutorial/progress-tracker.ts +350 -0
- package/src/onboarding/tutorial/storage.ts +249 -0
- package/src/onboarding/tutorial/tutorial-system.ts +462 -0
- package/src/onboarding/tutorial/types.ts +310 -0
- package/src/orchestrator-singleton.ts +129 -0
- package/src/shutdown.ts +33 -0
- package/src/test/e2e/assertions.ts +267 -0
- package/src/test/e2e/fixtures.ts +204 -0
- package/src/test/e2e/harness.ts +575 -0
- package/src/test/e2e/index.ts +57 -0
- package/src/test/e2e/types.ts +228 -0
- package/src/test/fixtures/__tests__/fake-response-loader.test.ts +314 -0
- package/src/test/fixtures/fake-response-loader.ts +314 -0
- package/src/test/fixtures/index.ts +20 -0
- package/src/tui/__tests__/mcp-panel.test.tsx +82 -0
- package/src/tui/__tests__/mcp-wiring.test.tsx +78 -0
- package/src/tui/__tests__/mode-components.test.tsx +395 -0
- package/src/tui/__tests__/permission-ask-flow.test.tsx +138 -0
- package/src/tui/__tests__/sidebar-panel-data.test.tsx +148 -0
- package/src/tui/__tests__/tools-panel-hotkeys.test.tsx +41 -0
- package/src/tui/adapters/agent-adapter.ts +1008 -0
- package/src/tui/adapters/index.ts +48 -0
- package/src/tui/adapters/message-adapter.ts +315 -0
- package/src/tui/adapters/persistence-bridge.ts +331 -0
- package/src/tui/adapters/session-adapter.ts +419 -0
- package/src/tui/buffered-stdout.ts +223 -0
- package/src/tui/components/AgentProgress.tsx +424 -0
- package/src/tui/components/Banner/AsciiArt.ts +160 -0
- package/src/tui/components/Banner/Banner.tsx +355 -0
- package/src/tui/components/Banner/ShimmerContext.tsx +131 -0
- package/src/tui/components/Banner/ShimmerText.tsx +193 -0
- package/src/tui/components/Banner/TypeWriterGradient.tsx +321 -0
- package/src/tui/components/Banner/index.ts +61 -0
- package/src/tui/components/Banner/useShimmer.ts +241 -0
- package/src/tui/components/ChatView.tsx +11 -0
- package/src/tui/components/Checkpoint/CheckpointDiffView.tsx +371 -0
- package/src/tui/components/Checkpoint/SnapshotCheckpointPanel.tsx +440 -0
- package/src/tui/components/Checkpoint/index.ts +19 -0
- package/src/tui/components/CostDisplay.tsx +226 -0
- package/src/tui/components/InitErrorBanner.tsx +122 -0
- package/src/tui/components/Input/Autocomplete.tsx +603 -0
- package/src/tui/components/Input/EnhancedCommandInput.tsx +471 -0
- package/src/tui/components/Input/HighlightedText.tsx +236 -0
- package/src/tui/components/Input/MentionAutocomplete.tsx +375 -0
- package/src/tui/components/Input/TextInput.tsx +1002 -0
- package/src/tui/components/Input/__tests__/Autocomplete.test.tsx +374 -0
- package/src/tui/components/Input/__tests__/TextInput.test.tsx +241 -0
- package/src/tui/components/Input/__tests__/highlight.test.ts +219 -0
- package/src/tui/components/Input/__tests__/slash-command-utils.test.ts +104 -0
- package/src/tui/components/Input/highlight.ts +362 -0
- package/src/tui/components/Input/index.ts +36 -0
- package/src/tui/components/Input/slash-command-utils.ts +135 -0
- package/src/tui/components/Layout.tsx +432 -0
- package/src/tui/components/McpPanel.tsx +137 -0
- package/src/tui/components/MemoryPanel.tsx +448 -0
- package/src/tui/components/Messages/CodeBlock.tsx +527 -0
- package/src/tui/components/Messages/DiffView.tsx +679 -0
- package/src/tui/components/Messages/ImageReference.tsx +89 -0
- package/src/tui/components/Messages/MarkdownBlock.tsx +228 -0
- package/src/tui/components/Messages/MarkdownRenderer.tsx +498 -0
- package/src/tui/components/Messages/MessageBubble.tsx +270 -0
- package/src/tui/components/Messages/MessageList.tsx +1719 -0
- package/src/tui/components/Messages/StreamingText.tsx +216 -0
- package/src/tui/components/Messages/ThinkingBlock.tsx +408 -0
- package/src/tui/components/Messages/ToolResultPreview.tsx +243 -0
- package/src/tui/components/Messages/__tests__/CodeBlock.test.tsx +296 -0
- package/src/tui/components/Messages/__tests__/DiffView.test.tsx +239 -0
- package/src/tui/components/Messages/__tests__/MarkdownRenderer.test.tsx +303 -0
- package/src/tui/components/Messages/__tests__/MessageBubble.test.tsx +268 -0
- package/src/tui/components/Messages/__tests__/MessageList.test.tsx +324 -0
- package/src/tui/components/Messages/__tests__/StreamingText.test.tsx +215 -0
- package/src/tui/components/Messages/index.ts +25 -0
- package/src/tui/components/ModeIndicator.tsx +177 -0
- package/src/tui/components/ModeSelector.tsx +216 -0
- package/src/tui/components/ModelSelector.tsx +339 -0
- package/src/tui/components/OnboardingWizard.tsx +670 -0
- package/src/tui/components/PhaseProgressIndicator.tsx +270 -0
- package/src/tui/components/RateLimitIndicator.tsx +82 -0
- package/src/tui/components/ScreenReaderLayout.tsx +295 -0
- package/src/tui/components/SettingsPanel.tsx +643 -0
- package/src/tui/components/Sidebar/SystemStatusPanel.tsx +284 -0
- package/src/tui/components/Sidebar/index.ts +9 -0
- package/src/tui/components/Status/ModelStatusBar.tsx +270 -0
- package/src/tui/components/Status/index.ts +12 -0
- package/src/tui/components/StatusBar/AgentModeIndicator.tsx +257 -0
- package/src/tui/components/StatusBar/ContextProgress.tsx +167 -0
- package/src/tui/components/StatusBar/FileChangesIndicator.tsx +62 -0
- package/src/tui/components/StatusBar/GitIndicator.tsx +89 -0
- package/src/tui/components/StatusBar/HeaderBar.tsx +126 -0
- package/src/tui/components/StatusBar/ModelIndicator.tsx +157 -0
- package/src/tui/components/StatusBar/PersistenceStatusIndicator.tsx +210 -0
- package/src/tui/components/StatusBar/ResilienceIndicator.tsx +106 -0
- package/src/tui/components/StatusBar/SandboxIndicator.tsx +167 -0
- package/src/tui/components/StatusBar/StatusBar.tsx +368 -0
- package/src/tui/components/StatusBar/ThinkingModeIndicator.tsx +170 -0
- package/src/tui/components/StatusBar/TokenBreakdown.tsx +246 -0
- package/src/tui/components/StatusBar/TokenCounter.tsx +135 -0
- package/src/tui/components/StatusBar/TrustModeIndicator.tsx +130 -0
- package/src/tui/components/StatusBar/WorkspaceIndicator.tsx +86 -0
- package/src/tui/components/StatusBar/__tests__/AgentModeIndicator.test.tsx +193 -0
- package/src/tui/components/StatusBar/__tests__/StatusBar.test.tsx +729 -0
- package/src/tui/components/StatusBar/index.ts +60 -0
- package/src/tui/components/TipBanner.tsx +115 -0
- package/src/tui/components/TodoItem.tsx +208 -0
- package/src/tui/components/TodoPanel.tsx +455 -0
- package/src/tui/components/Tools/ApprovalQueue.tsx +407 -0
- package/src/tui/components/Tools/OptionSelector.tsx +160 -0
- package/src/tui/components/Tools/PermissionDialog.tsx +286 -0
- package/src/tui/components/Tools/ToolParams.tsx +483 -0
- package/src/tui/components/Tools/ToolsPanel.tsx +178 -0
- package/src/tui/components/Tools/__tests__/PermissionDialog.test.tsx +510 -0
- package/src/tui/components/Tools/__tests__/ToolParams.test.tsx +432 -0
- package/src/tui/components/Tools/index.ts +21 -0
- package/src/tui/components/TrustPrompt.tsx +279 -0
- package/src/tui/components/UpdateBanner.tsx +166 -0
- package/src/tui/components/VimModeIndicator.tsx +112 -0
- package/src/tui/components/backtrack/BacktrackControls.tsx +402 -0
- package/src/tui/components/backtrack/index.ts +13 -0
- package/src/tui/components/common/AutoApprovalStatus.tsx +251 -0
- package/src/tui/components/common/CostWarning.tsx +294 -0
- package/src/tui/components/common/DynamicShortcutHints.tsx +209 -0
- package/src/tui/components/common/EnhancedLoadingIndicator.tsx +305 -0
- package/src/tui/components/common/ErrorBoundary.tsx +140 -0
- package/src/tui/components/common/GradientText.tsx +224 -0
- package/src/tui/components/common/HotkeyHelpModal.tsx +193 -0
- package/src/tui/components/common/HotkeyHints.tsx +70 -0
- package/src/tui/components/common/MaxSizedBox.tsx +354 -0
- package/src/tui/components/common/NewMessagesBadge.tsx +65 -0
- package/src/tui/components/common/ProtectedFileLegend.tsx +89 -0
- package/src/tui/components/common/ScrollIndicator.tsx +160 -0
- package/src/tui/components/common/Spinner.tsx +342 -0
- package/src/tui/components/common/StreamingIndicator.tsx +316 -0
- package/src/tui/components/common/VirtualizedList/VirtualizedList.tsx +428 -0
- package/src/tui/components/common/VirtualizedList/hooks/index.ts +19 -0
- package/src/tui/components/common/VirtualizedList/hooks/useBatchedScroll.ts +64 -0
- package/src/tui/components/common/VirtualizedList/hooks/useScrollAnchor.ts +290 -0
- package/src/tui/components/common/VirtualizedList/hooks/useVirtualization.ts +340 -0
- package/src/tui/components/common/VirtualizedList/index.ts +30 -0
- package/src/tui/components/common/VirtualizedList/types.ts +107 -0
- package/src/tui/components/common/__tests__/NewMessagesBadge.test.tsx +74 -0
- package/src/tui/components/common/__tests__/ScrollIndicator.test.tsx +193 -0
- package/src/tui/components/common/index.ts +110 -0
- package/src/tui/components/index.ts +79 -0
- package/src/tui/components/session/CheckpointPanel.tsx +323 -0
- package/src/tui/components/session/RollbackDialog.tsx +169 -0
- package/src/tui/components/session/SessionItem.tsx +136 -0
- package/src/tui/components/session/SessionListPanel.tsx +252 -0
- package/src/tui/components/session/SessionPicker.tsx +449 -0
- package/src/tui/components/session/SessionPreview.tsx +240 -0
- package/src/tui/components/session/__tests__/session.test.tsx +408 -0
- package/src/tui/components/session/index.ts +28 -0
- package/src/tui/components/session/types.ts +116 -0
- package/src/tui/components/theme/__tests__/tokens.test.ts +471 -0
- package/src/tui/components/theme/index.ts +227 -0
- package/src/tui/components/theme/tokens.ts +484 -0
- package/src/tui/config/defaults.ts +134 -0
- package/src/tui/config/index.ts +17 -0
- package/src/tui/context/AnimationContext.tsx +284 -0
- package/src/tui/context/AppContext.tsx +349 -0
- package/src/tui/context/BracketedPasteContext.tsx +372 -0
- package/src/tui/context/LspContext.tsx +192 -0
- package/src/tui/context/McpContext.tsx +325 -0
- package/src/tui/context/MessagesContext.tsx +870 -0
- package/src/tui/context/OverflowContext.tsx +213 -0
- package/src/tui/context/RateLimitContext.tsx +108 -0
- package/src/tui/context/ResilienceContext.tsx +275 -0
- package/src/tui/context/RootProvider.tsx +136 -0
- package/src/tui/context/ScrollContext.tsx +331 -0
- package/src/tui/context/ToolsContext.tsx +702 -0
- package/src/tui/context/__tests__/BracketedPasteContext.test.tsx +416 -0
- package/src/tui/context/index.ts +140 -0
- package/src/tui/enterprise-integration.ts +282 -0
- package/src/tui/hooks/__tests__/useBacktrack.test.tsx +138 -0
- package/src/tui/hooks/__tests__/useBracketedPaste.test.tsx +222 -0
- package/src/tui/hooks/__tests__/useCopyMode.test.tsx +336 -0
- package/src/tui/hooks/__tests__/useHotkeys.ctrl-input.test.tsx +96 -0
- package/src/tui/hooks/__tests__/useHotkeys.test.tsx +454 -0
- package/src/tui/hooks/__tests__/useInputHistory.test.tsx +660 -0
- package/src/tui/hooks/__tests__/useLineBuffer.test.ts +295 -0
- package/src/tui/hooks/__tests__/useModeController.test.ts +137 -0
- package/src/tui/hooks/__tests__/useModeShortcuts.test.tsx +142 -0
- package/src/tui/hooks/__tests__/useScrollController.test.ts +464 -0
- package/src/tui/hooks/__tests__/useVim.test.tsx +531 -0
- package/src/tui/hooks/index.ts +252 -0
- package/src/tui/hooks/useAgentLoop.ts +712 -0
- package/src/tui/hooks/useAlternateBuffer.ts +398 -0
- package/src/tui/hooks/useAnimatedScrollbar.ts +241 -0
- package/src/tui/hooks/useBacktrack.ts +443 -0
- package/src/tui/hooks/useBracketedPaste.ts +104 -0
- package/src/tui/hooks/useCollapsible.ts +240 -0
- package/src/tui/hooks/useCopyMode.ts +382 -0
- package/src/tui/hooks/useCostSummary.ts +75 -0
- package/src/tui/hooks/useDesktopNotification.ts +414 -0
- package/src/tui/hooks/useDiffMode.ts +44 -0
- package/src/tui/hooks/useFileChangeStats.ts +110 -0
- package/src/tui/hooks/useFileSuggestions.ts +284 -0
- package/src/tui/hooks/useFlickerDetector.ts +250 -0
- package/src/tui/hooks/useGitStatus.ts +200 -0
- package/src/tui/hooks/useHotkeys.ts +579 -0
- package/src/tui/hooks/useImagePaste.ts +114 -0
- package/src/tui/hooks/useInputHighlight.ts +145 -0
- package/src/tui/hooks/useInputHistory.ts +246 -0
- package/src/tui/hooks/useKeyboardScroll.ts +209 -0
- package/src/tui/hooks/useLineBuffer.ts +356 -0
- package/src/tui/hooks/useMentionAutocomplete.ts +235 -0
- package/src/tui/hooks/useModeController.ts +167 -0
- package/src/tui/hooks/useModeShortcuts.ts +196 -0
- package/src/tui/hooks/usePermissionHandler.ts +146 -0
- package/src/tui/hooks/usePersistence.ts +480 -0
- package/src/tui/hooks/usePersistenceShortcuts.ts +225 -0
- package/src/tui/hooks/usePlaceholderRotation.ts +143 -0
- package/src/tui/hooks/useProviderStatus.ts +270 -0
- package/src/tui/hooks/useRateLimitStatus.ts +90 -0
- package/src/tui/hooks/useScreenReader.ts +315 -0
- package/src/tui/hooks/useScrollController.ts +450 -0
- package/src/tui/hooks/useScrollEventBatcher.ts +185 -0
- package/src/tui/hooks/useSidebarPanelData.ts +115 -0
- package/src/tui/hooks/useSmoothScroll.ts +202 -0
- package/src/tui/hooks/useSnapshots.ts +300 -0
- package/src/tui/hooks/useStateAndRef.ts +50 -0
- package/src/tui/hooks/useTerminalSize.ts +206 -0
- package/src/tui/hooks/useToolApprovalController.ts +91 -0
- package/src/tui/hooks/useVim.ts +334 -0
- package/src/tui/hooks/useWorkspace.ts +56 -0
- package/src/tui/i18n/__tests__/init.test.ts +278 -0
- package/src/tui/i18n/__tests__/language-config.test.ts +199 -0
- package/src/tui/i18n/__tests__/locale-detection.test.ts +250 -0
- package/src/tui/i18n/__tests__/settings-integration.test.ts +262 -0
- package/src/tui/i18n/index.ts +72 -0
- package/src/tui/i18n/init.ts +131 -0
- package/src/tui/i18n/language-config.ts +106 -0
- package/src/tui/i18n/locale-detection.ts +173 -0
- package/src/tui/i18n/settings-integration.ts +557 -0
- package/src/tui/i18n/tui-namespace.ts +538 -0
- package/src/tui/i18n/types.ts +312 -0
- package/src/tui/index.ts +43 -0
- package/src/tui/lsp-integration.ts +409 -0
- package/src/tui/metrics-integration.ts +366 -0
- package/src/tui/plugins.ts +383 -0
- package/src/tui/resilience.ts +342 -0
- package/src/tui/sandbox-integration.ts +317 -0
- package/src/tui/services/clipboard.ts +348 -0
- package/src/tui/services/fuzzy-search.ts +441 -0
- package/src/tui/services/index.ts +72 -0
- package/src/tui/services/markdown-renderer.ts +565 -0
- package/src/tui/services/open-external.ts +247 -0
- package/src/tui/services/syntax-highlighter.ts +483 -0
- package/src/tui/slash-commands.ts +12 -0
- package/src/tui/theme/index.ts +15 -0
- package/src/tui/theme/provider.tsx +206 -0
- package/src/tui/tip-integration.ts +300 -0
- package/src/tui/types/__tests__/ink-extended.test.ts +121 -0
- package/src/tui/types/ink-extended.ts +87 -0
- package/src/tui/utils/__tests__/bracketedPaste.test.ts +231 -0
- package/src/tui/utils/__tests__/heightEstimator.test.ts +157 -0
- package/src/tui/utils/__tests__/text-width.test.ts +158 -0
- package/src/tui/utils/__tests__/textSanitizer.test.ts +266 -0
- package/src/tui/utils/__tests__/ui-sizing.test.ts +169 -0
- package/src/tui/utils/bracketedPaste.ts +107 -0
- package/src/tui/utils/cursor-manager.ts +131 -0
- package/src/tui/utils/detectTerminal.ts +596 -0
- package/src/tui/utils/findLastSafeSplitPoint.ts +92 -0
- package/src/tui/utils/heightEstimator.ts +198 -0
- package/src/tui/utils/index.ts +91 -0
- package/src/tui/utils/isNarrowWidth.ts +52 -0
- package/src/tui/utils/stdoutGuard.ts +90 -0
- package/src/tui/utils/synchronized-update.ts +70 -0
- package/src/tui/utils/text-width.ts +225 -0
- package/src/tui/utils/textSanitizer.ts +225 -0
- package/src/tui/utils/textUtils.ts +114 -0
- package/src/tui/utils/ui-sizing.ts +192 -0
- package/src/tui-blessed/app.ts +160 -0
- package/src/tui-blessed/index.ts +2 -0
- package/src/tui-blessed/neo-blessed.d.ts +6 -0
- package/src/tui-blessed/test.ts +21 -0
- package/src/tui-blessed/types.ts +14 -0
- package/src/utils/icons.ts +130 -0
- package/src/utils/index.ts +33 -0
- package/src/utils/resume-hint.ts +86 -0
- package/src/version.ts +1 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CostWarning Component (Phase 35+)
|
|
3
|
+
*
|
|
4
|
+
* Displays cost limit warnings and status in the TUI.
|
|
5
|
+
* Uses ASCII symbols instead of emoji for terminal compatibility.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/common/CostWarning
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Box, Text } from "ink";
|
|
11
|
+
import type React from "react";
|
|
12
|
+
import { memo } from "react";
|
|
13
|
+
import { useTheme } from "../../theme/index.js";
|
|
14
|
+
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Types
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Warning severity level.
|
|
21
|
+
*/
|
|
22
|
+
export type CostWarningSeverity = "info" | "warning" | "error";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Props for the CostWarning component.
|
|
26
|
+
*/
|
|
27
|
+
export interface CostWarningProps {
|
|
28
|
+
/** Current cost used in USD */
|
|
29
|
+
readonly costUsed: number;
|
|
30
|
+
/** Cost limit in USD (undefined if no limit) */
|
|
31
|
+
readonly costLimit?: number;
|
|
32
|
+
/** Number of requests made */
|
|
33
|
+
readonly requestsUsed: number;
|
|
34
|
+
/** Request limit (undefined if no limit) */
|
|
35
|
+
readonly requestLimit?: number;
|
|
36
|
+
/** Percentage of limit used (0-100) */
|
|
37
|
+
readonly percentUsed: number;
|
|
38
|
+
/** Whether limit has been reached */
|
|
39
|
+
readonly limitReached?: boolean;
|
|
40
|
+
/** Whether awaiting user approval */
|
|
41
|
+
readonly awaitingApproval?: boolean;
|
|
42
|
+
/** Severity level for styling */
|
|
43
|
+
readonly severity?: CostWarningSeverity;
|
|
44
|
+
/** Whether to show in compact mode */
|
|
45
|
+
readonly compact?: boolean;
|
|
46
|
+
/** Callback when user approves continuation */
|
|
47
|
+
readonly onApprove?: () => void;
|
|
48
|
+
/** Callback when user denies continuation */
|
|
49
|
+
readonly onDeny?: () => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// Constants - ASCII Symbols (NO EMOJI)
|
|
54
|
+
// =============================================================================
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* ASCII symbols for different severity levels.
|
|
58
|
+
*/
|
|
59
|
+
const SEVERITY_SYMBOLS: Record<CostWarningSeverity, string> = {
|
|
60
|
+
info: "[i]",
|
|
61
|
+
warning: "[!]",
|
|
62
|
+
error: "[X]",
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* ASCII symbol for cost indicator.
|
|
67
|
+
*/
|
|
68
|
+
const COST_SYMBOL = "[$]";
|
|
69
|
+
|
|
70
|
+
// =============================================================================
|
|
71
|
+
// Helper Functions
|
|
72
|
+
// =============================================================================
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Format cost as USD string.
|
|
76
|
+
*/
|
|
77
|
+
function formatCostUSD(cost: number): string {
|
|
78
|
+
return `$${cost.toFixed(2)}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Determine severity based on percentage used.
|
|
83
|
+
*/
|
|
84
|
+
function getSeverityFromPercent(percentUsed: number, limitReached: boolean): CostWarningSeverity {
|
|
85
|
+
if (limitReached) return "error";
|
|
86
|
+
if (percentUsed >= 80) return "warning";
|
|
87
|
+
return "info";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// =============================================================================
|
|
91
|
+
// Component
|
|
92
|
+
// =============================================================================
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* CostWarning - Displays cost limit warnings and status.
|
|
96
|
+
*
|
|
97
|
+
* Features:
|
|
98
|
+
* - Shows current cost vs limit
|
|
99
|
+
* - Shows request count vs limit
|
|
100
|
+
* - Displays percentage used with progress indicator
|
|
101
|
+
* - Different severity levels (info, warning, error)
|
|
102
|
+
* - Compact and full display modes
|
|
103
|
+
* - ASCII-only symbols for terminal compatibility
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```tsx
|
|
107
|
+
* // Warning at 80%
|
|
108
|
+
* <CostWarning
|
|
109
|
+
* costUsed={4.00}
|
|
110
|
+
* costLimit={5.00}
|
|
111
|
+
* requestsUsed={80}
|
|
112
|
+
* requestLimit={100}
|
|
113
|
+
* percentUsed={80}
|
|
114
|
+
* severity="warning"
|
|
115
|
+
* />
|
|
116
|
+
*
|
|
117
|
+
* // Limit reached
|
|
118
|
+
* <CostWarning
|
|
119
|
+
* costUsed={5.20}
|
|
120
|
+
* costLimit={5.00}
|
|
121
|
+
* requestsUsed={100}
|
|
122
|
+
* percentUsed={100}
|
|
123
|
+
* limitReached
|
|
124
|
+
* awaitingApproval
|
|
125
|
+
* />
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export const CostWarning: React.FC<CostWarningProps> = memo(function CostWarning({
|
|
129
|
+
costUsed,
|
|
130
|
+
costLimit,
|
|
131
|
+
requestsUsed,
|
|
132
|
+
requestLimit,
|
|
133
|
+
percentUsed,
|
|
134
|
+
limitReached = false,
|
|
135
|
+
awaitingApproval = false,
|
|
136
|
+
severity: severityProp,
|
|
137
|
+
compact = false,
|
|
138
|
+
}) {
|
|
139
|
+
const { theme } = useTheme();
|
|
140
|
+
|
|
141
|
+
// Determine severity from props or calculate from percentage
|
|
142
|
+
const severity = severityProp ?? getSeverityFromPercent(percentUsed, limitReached);
|
|
143
|
+
|
|
144
|
+
// Get colors based on severity
|
|
145
|
+
const getColor = () => {
|
|
146
|
+
switch (severity) {
|
|
147
|
+
case "error":
|
|
148
|
+
return theme.colors.error;
|
|
149
|
+
case "warning":
|
|
150
|
+
return theme.colors.warning;
|
|
151
|
+
default:
|
|
152
|
+
return theme.colors.info;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const symbol = SEVERITY_SYMBOLS[severity];
|
|
157
|
+
const color = getColor();
|
|
158
|
+
|
|
159
|
+
// Compact display: single line
|
|
160
|
+
if (compact) {
|
|
161
|
+
const limitDisplay = costLimit
|
|
162
|
+
? `${formatCostUSD(costUsed)} / ${formatCostUSD(costLimit)}`
|
|
163
|
+
: formatCostUSD(costUsed);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<Box>
|
|
167
|
+
<Text color={color}>
|
|
168
|
+
{COST_SYMBOL} {limitDisplay} ({percentUsed.toFixed(0)}%)
|
|
169
|
+
</Text>
|
|
170
|
+
</Box>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Full display
|
|
175
|
+
return (
|
|
176
|
+
<Box flexDirection="column" marginY={1}>
|
|
177
|
+
{/* Header line */}
|
|
178
|
+
<Box>
|
|
179
|
+
<Text color={color} bold>
|
|
180
|
+
{symbol}{" "}
|
|
181
|
+
{limitReached
|
|
182
|
+
? "LIMIT REACHED: Cost limit exceeded"
|
|
183
|
+
: `WARNING: Cost limit ${percentUsed.toFixed(0)}% reached`}
|
|
184
|
+
</Text>
|
|
185
|
+
</Box>
|
|
186
|
+
|
|
187
|
+
{/* Cost details */}
|
|
188
|
+
<Box marginLeft={4}>
|
|
189
|
+
<Text>
|
|
190
|
+
{COST_SYMBOL} Cost:{" "}
|
|
191
|
+
<Text color={color}>
|
|
192
|
+
{formatCostUSD(costUsed)}
|
|
193
|
+
{costLimit && ` / ${formatCostUSD(costLimit)}`}
|
|
194
|
+
</Text>
|
|
195
|
+
</Text>
|
|
196
|
+
</Box>
|
|
197
|
+
|
|
198
|
+
{/* Request details (if limit set) */}
|
|
199
|
+
{requestLimit && (
|
|
200
|
+
<Box marginLeft={4}>
|
|
201
|
+
<Text>
|
|
202
|
+
[#] Requests:{" "}
|
|
203
|
+
<Text color={color}>
|
|
204
|
+
{requestsUsed} / {requestLimit}
|
|
205
|
+
</Text>
|
|
206
|
+
</Text>
|
|
207
|
+
</Box>
|
|
208
|
+
)}
|
|
209
|
+
|
|
210
|
+
{/* Progress bar */}
|
|
211
|
+
<Box marginLeft={4} marginTop={1}>
|
|
212
|
+
<ProgressBar percent={percentUsed} color={color} width={30} />
|
|
213
|
+
</Box>
|
|
214
|
+
|
|
215
|
+
{/* Approval prompt */}
|
|
216
|
+
{awaitingApproval && (
|
|
217
|
+
<Box marginTop={1} marginLeft={4}>
|
|
218
|
+
<Text color={theme.colors.warning}>Press [y] to continue or [n] to stop</Text>
|
|
219
|
+
</Box>
|
|
220
|
+
)}
|
|
221
|
+
</Box>
|
|
222
|
+
);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// =============================================================================
|
|
226
|
+
// Progress Bar Sub-component
|
|
227
|
+
// =============================================================================
|
|
228
|
+
|
|
229
|
+
interface ProgressBarProps {
|
|
230
|
+
readonly percent: number;
|
|
231
|
+
readonly color: string;
|
|
232
|
+
readonly width?: number;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Simple ASCII progress bar.
|
|
237
|
+
*/
|
|
238
|
+
const ProgressBar: React.FC<ProgressBarProps> = memo(function ProgressBar({
|
|
239
|
+
percent,
|
|
240
|
+
color,
|
|
241
|
+
width = 20,
|
|
242
|
+
}) {
|
|
243
|
+
const filled = Math.round((percent / 100) * width);
|
|
244
|
+
const empty = width - filled;
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<Box>
|
|
248
|
+
<Text color={color}>[</Text>
|
|
249
|
+
<Text color={color}>{"=".repeat(Math.max(0, filled))}</Text>
|
|
250
|
+
<Text dimColor>{"-".repeat(Math.max(0, empty))}</Text>
|
|
251
|
+
<Text color={color}>]</Text>
|
|
252
|
+
<Text dimColor> {percent.toFixed(0)}%</Text>
|
|
253
|
+
</Box>
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// =============================================================================
|
|
258
|
+
// Compact Cost Display
|
|
259
|
+
// =============================================================================
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Props for CompactCostDisplay.
|
|
263
|
+
*/
|
|
264
|
+
export interface CompactCostDisplayProps {
|
|
265
|
+
/** Current cost used in USD */
|
|
266
|
+
readonly cost: number;
|
|
267
|
+
/** Cost limit (optional) */
|
|
268
|
+
readonly limit?: number;
|
|
269
|
+
/** Show warning color if approaching limit */
|
|
270
|
+
readonly showWarning?: boolean;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Compact single-line cost display for status bar.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```tsx
|
|
278
|
+
* <CompactCostDisplay cost={2.50} limit={5.00} />
|
|
279
|
+
* // Output: [$] $2.50 / $5.00
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export const CompactCostDisplay: React.FC<CompactCostDisplayProps> = memo(
|
|
283
|
+
function CompactCostDisplay({ cost, limit, showWarning = false }) {
|
|
284
|
+
const { theme } = useTheme();
|
|
285
|
+
const color = showWarning ? theme.colors.warning : theme.colors.muted;
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
<Text color={color}>
|
|
289
|
+
{COST_SYMBOL} {formatCostUSD(cost)}
|
|
290
|
+
{limit && ` / ${formatCostUSD(limit)}`}
|
|
291
|
+
</Text>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
);
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DynamicShortcutHints Component
|
|
3
|
+
*
|
|
4
|
+
* Displays context-aware keyboard shortcuts based on current UI state.
|
|
5
|
+
* Shows different hints for:
|
|
6
|
+
* - Input mode (typing)
|
|
7
|
+
* - Agent running (processing)
|
|
8
|
+
* - Idle state
|
|
9
|
+
* - Autocomplete active
|
|
10
|
+
*
|
|
11
|
+
* @module tui/components/common/DynamicShortcutHints
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Box, Text } from "ink";
|
|
15
|
+
import type React from "react";
|
|
16
|
+
import { memo, useMemo } from "react";
|
|
17
|
+
import { useTheme } from "../../theme/index.js";
|
|
18
|
+
import { type HotkeyHint, HotkeyHints } from "./HotkeyHints.js";
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Types
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Current UI state for determining which shortcuts to show.
|
|
26
|
+
*/
|
|
27
|
+
export type UIState =
|
|
28
|
+
| "idle" // Waiting for input
|
|
29
|
+
| "input" // User is typing
|
|
30
|
+
| "running" // Agent is processing
|
|
31
|
+
| "autocomplete" // Autocomplete menu is open
|
|
32
|
+
| "confirmation" // Waiting for user confirmation
|
|
33
|
+
| "multiline"; // Multiline input mode
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Props for DynamicShortcutHints component.
|
|
37
|
+
*/
|
|
38
|
+
export interface DynamicShortcutHintsProps {
|
|
39
|
+
/** Current UI state */
|
|
40
|
+
readonly state: UIState;
|
|
41
|
+
/** Whether sidebar is visible */
|
|
42
|
+
readonly sidebarVisible?: boolean;
|
|
43
|
+
/** Whether vim mode is active */
|
|
44
|
+
readonly vimMode?: boolean;
|
|
45
|
+
/** Additional custom hints to append */
|
|
46
|
+
readonly customHints?: readonly HotkeyHint[];
|
|
47
|
+
/** Whether to show compact version (fewer hints) */
|
|
48
|
+
readonly compact?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Constants
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Shortcut hints for different UI states.
|
|
57
|
+
*/
|
|
58
|
+
const STATE_HINTS: Record<UIState, readonly HotkeyHint[]> = {
|
|
59
|
+
idle: [
|
|
60
|
+
{ keys: "Enter", label: "Send" },
|
|
61
|
+
{ keys: "↑/↓", label: "History" },
|
|
62
|
+
{ keys: "/", label: "Commands" },
|
|
63
|
+
{ keys: "@", label: "Mention" },
|
|
64
|
+
{ keys: "!", label: "Shell" },
|
|
65
|
+
],
|
|
66
|
+
input: [
|
|
67
|
+
{ keys: "Enter", label: "Send" },
|
|
68
|
+
{ keys: "Ctrl+C", label: "Clear" },
|
|
69
|
+
{ keys: "Tab", label: "Complete" },
|
|
70
|
+
{ keys: "↑/↓", label: "History" },
|
|
71
|
+
],
|
|
72
|
+
running: [
|
|
73
|
+
{ keys: "Esc", label: "Stop" },
|
|
74
|
+
{ keys: "PgUp/Dn", label: "Scroll" },
|
|
75
|
+
{ keys: "Ctrl+C", label: "Cancel" },
|
|
76
|
+
],
|
|
77
|
+
autocomplete: [
|
|
78
|
+
{ keys: "↑/↓", label: "Navigate" },
|
|
79
|
+
{ keys: "Tab/Enter", label: "Select" },
|
|
80
|
+
{ keys: "Esc", label: "Close" },
|
|
81
|
+
],
|
|
82
|
+
confirmation: [
|
|
83
|
+
{ keys: "y/Enter", label: "Confirm" },
|
|
84
|
+
{ keys: "n/Esc", label: "Cancel" },
|
|
85
|
+
{ keys: "a", label: "Always" },
|
|
86
|
+
],
|
|
87
|
+
multiline: [
|
|
88
|
+
{ keys: "Ctrl+Enter", label: "Send" },
|
|
89
|
+
{ keys: "Enter", label: "Newline" },
|
|
90
|
+
{ keys: "Esc", label: "Exit" },
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Compact hints (fewer items for narrow terminals).
|
|
96
|
+
*/
|
|
97
|
+
const COMPACT_HINTS: Record<UIState, readonly HotkeyHint[]> = {
|
|
98
|
+
idle: [
|
|
99
|
+
{ keys: "Enter", label: "Send" },
|
|
100
|
+
{ keys: "/", label: "Cmds" },
|
|
101
|
+
{ keys: "!", label: "Shell" },
|
|
102
|
+
],
|
|
103
|
+
input: [
|
|
104
|
+
{ keys: "Enter", label: "Send" },
|
|
105
|
+
{ keys: "Tab", label: "Complete" },
|
|
106
|
+
],
|
|
107
|
+
running: [
|
|
108
|
+
{ keys: "Esc", label: "Stop" },
|
|
109
|
+
{ keys: "Ctrl+C", label: "Cancel" },
|
|
110
|
+
],
|
|
111
|
+
autocomplete: [
|
|
112
|
+
{ keys: "↑/↓", label: "Nav" },
|
|
113
|
+
{ keys: "Enter", label: "Select" },
|
|
114
|
+
],
|
|
115
|
+
confirmation: [
|
|
116
|
+
{ keys: "y", label: "Yes" },
|
|
117
|
+
{ keys: "n", label: "No" },
|
|
118
|
+
],
|
|
119
|
+
multiline: [
|
|
120
|
+
{ keys: "Ctrl+Enter", label: "Send" },
|
|
121
|
+
{ keys: "Esc", label: "Exit" },
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Component
|
|
127
|
+
// =============================================================================
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get hints for the current state.
|
|
131
|
+
*/
|
|
132
|
+
function getHintsForState(
|
|
133
|
+
state: UIState,
|
|
134
|
+
compact: boolean,
|
|
135
|
+
sidebarVisible: boolean,
|
|
136
|
+
vimMode: boolean,
|
|
137
|
+
customHints?: readonly HotkeyHint[]
|
|
138
|
+
): HotkeyHint[] {
|
|
139
|
+
const baseHints = compact ? COMPACT_HINTS[state] : STATE_HINTS[state];
|
|
140
|
+
const hints: HotkeyHint[] = [...baseHints];
|
|
141
|
+
|
|
142
|
+
// Add sidebar toggle hint if not in compact mode
|
|
143
|
+
if (!compact && state !== "running" && state !== "autocomplete") {
|
|
144
|
+
hints.push({ keys: "Alt+K", label: sidebarVisible ? "Hide sidebar" : "Show sidebar" });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Add vim mode indicator if active
|
|
148
|
+
if (vimMode && !compact) {
|
|
149
|
+
hints.push({ keys: "Esc", label: "Vim normal" });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Append custom hints
|
|
153
|
+
if (customHints) {
|
|
154
|
+
hints.push(...customHints);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return hints;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* DynamicShortcutHints displays context-aware keyboard shortcuts.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```tsx
|
|
165
|
+
* <DynamicShortcutHints
|
|
166
|
+
* state={isRunning ? "running" : hasInput ? "input" : "idle"}
|
|
167
|
+
* sidebarVisible={showSidebar}
|
|
168
|
+
* />
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
function DynamicShortcutHintsImpl({
|
|
172
|
+
state,
|
|
173
|
+
sidebarVisible = false,
|
|
174
|
+
vimMode = false,
|
|
175
|
+
customHints,
|
|
176
|
+
compact = false,
|
|
177
|
+
}: DynamicShortcutHintsProps): React.JSX.Element {
|
|
178
|
+
const { theme } = useTheme();
|
|
179
|
+
|
|
180
|
+
const hints = useMemo(
|
|
181
|
+
() => getHintsForState(state, compact, sidebarVisible, vimMode, customHints),
|
|
182
|
+
[state, compact, sidebarVisible, vimMode, customHints]
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Box flexDirection="row" alignItems="center">
|
|
187
|
+
<Text color={theme.semantic.text.muted} dimColor>
|
|
188
|
+
<HotkeyHints hints={hints} />
|
|
189
|
+
</Text>
|
|
190
|
+
</Box>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Compare props for memo optimization.
|
|
196
|
+
*/
|
|
197
|
+
function arePropsEqual(prev: DynamicShortcutHintsProps, next: DynamicShortcutHintsProps): boolean {
|
|
198
|
+
return (
|
|
199
|
+
prev.state === next.state &&
|
|
200
|
+
prev.sidebarVisible === next.sidebarVisible &&
|
|
201
|
+
prev.vimMode === next.vimMode &&
|
|
202
|
+
prev.compact === next.compact &&
|
|
203
|
+
prev.customHints === next.customHints
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const DynamicShortcutHints = memo(DynamicShortcutHintsImpl, arePropsEqual);
|
|
208
|
+
|
|
209
|
+
export default DynamicShortcutHints;
|