@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,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBar Component (T034)
|
|
3
|
+
*
|
|
4
|
+
* Container component that renders all status indicators in a horizontal row.
|
|
5
|
+
* Unified Footer layout: modes | model | context+cost
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/StatusBar/StatusBar
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { CodingMode, SandboxPolicy } from "@vellum/core";
|
|
11
|
+
import { Box, Text } from "ink";
|
|
12
|
+
import { useMemo } from "react";
|
|
13
|
+
import { useTUITranslation } from "../../i18n/index.js";
|
|
14
|
+
import { useTheme } from "../../theme/index.js";
|
|
15
|
+
import { GradientText } from "../common/GradientText.js";
|
|
16
|
+
import type { AgentLevel } from "./AgentModeIndicator.js";
|
|
17
|
+
import { ContextProgress, type ContextProgressProps } from "./ContextProgress.js";
|
|
18
|
+
import { ModelIndicator } from "./ModelIndicator.js";
|
|
19
|
+
import type { PersistenceStatusIndicatorProps } from "./PersistenceStatusIndicator.js";
|
|
20
|
+
import { ResilienceStatusSegment } from "./ResilienceIndicator.js";
|
|
21
|
+
import { SandboxIndicator } from "./SandboxIndicator.js";
|
|
22
|
+
import { ThinkingModeIndicator, type ThinkingModeIndicatorProps } from "./ThinkingModeIndicator.js";
|
|
23
|
+
import { TokenBreakdown, type TokenStats } from "./TokenBreakdown.js";
|
|
24
|
+
import { TrustModeIndicator, type TrustModeIndicatorProps } from "./TrustModeIndicator.js";
|
|
25
|
+
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// Types
|
|
28
|
+
// =============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extended token usage with granular breakdown.
|
|
32
|
+
* Extends ContextProgressProps with detailed token stats.
|
|
33
|
+
*/
|
|
34
|
+
export interface ExtendedTokenProps extends ContextProgressProps {
|
|
35
|
+
/** Detailed breakdown of token usage */
|
|
36
|
+
readonly breakdown?: TokenStats;
|
|
37
|
+
/** Current turn's token usage (for per-turn display) */
|
|
38
|
+
readonly turnUsage?: TokenStats;
|
|
39
|
+
/** Whether to show granular breakdown (default: false) */
|
|
40
|
+
readonly showBreakdown?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Props for the StatusBar component.
|
|
45
|
+
*/
|
|
46
|
+
export interface StatusBarProps {
|
|
47
|
+
/** Current coding mode */
|
|
48
|
+
readonly mode?: CodingMode;
|
|
49
|
+
/** Agent name for display (e.g., 'orchestrator', 'coder') */
|
|
50
|
+
readonly agentName?: string;
|
|
51
|
+
/** Agent level for spec mode (0=orchestrator, 1=sub-orchestrator, 2=worker) */
|
|
52
|
+
readonly agentLevel?: AgentLevel;
|
|
53
|
+
/** Model name (e.g., 'claude-sonnet-4') */
|
|
54
|
+
readonly modelName?: string;
|
|
55
|
+
/** Token usage information (displayed with visual progress bar) */
|
|
56
|
+
readonly tokens?: ExtendedTokenProps;
|
|
57
|
+
/** Trust mode setting */
|
|
58
|
+
readonly trustMode?: TrustModeIndicatorProps["mode"];
|
|
59
|
+
/** Sandbox policy for file system access boundaries */
|
|
60
|
+
readonly sandboxPolicy?: SandboxPolicy;
|
|
61
|
+
/** Thinking mode status */
|
|
62
|
+
readonly thinking?: ThinkingModeIndicatorProps;
|
|
63
|
+
/** Current cost in dollars */
|
|
64
|
+
readonly cost?: number;
|
|
65
|
+
/** Whether to show a border (default: false for unified footer) */
|
|
66
|
+
readonly showBorder?: boolean;
|
|
67
|
+
/** Whether to show all modes or only active (default: false) */
|
|
68
|
+
readonly showAllModes?: boolean;
|
|
69
|
+
/** Persistence status for session save indicator */
|
|
70
|
+
readonly persistence?: Pick<
|
|
71
|
+
PersistenceStatusIndicatorProps,
|
|
72
|
+
"status" | "unsavedCount" | "lastSavedAt"
|
|
73
|
+
>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// =============================================================================
|
|
77
|
+
// Constants
|
|
78
|
+
// =============================================================================
|
|
79
|
+
|
|
80
|
+
/** Separator between status items (compact, no extra spaces) */
|
|
81
|
+
const SEPARATOR = "│";
|
|
82
|
+
|
|
83
|
+
/** Mode display configuration */
|
|
84
|
+
const MODES_CONFIG: Array<{ mode: CodingMode; icon: string; label: string }> = [
|
|
85
|
+
{ mode: "vibe", icon: "◐", label: "vibe" },
|
|
86
|
+
{ mode: "plan", icon: "◑", label: "plan" },
|
|
87
|
+
{ mode: "spec", icon: "◒", label: "spec" },
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
/** Agent name to abbreviation mapping for status bar display */
|
|
91
|
+
const AGENT_ABBREVIATIONS: Record<string, string> = {
|
|
92
|
+
// Core Agents
|
|
93
|
+
"vibe-agent": "Vibe",
|
|
94
|
+
"plan-agent": "Plan",
|
|
95
|
+
"spec-orchestrator": "Spec",
|
|
96
|
+
|
|
97
|
+
// Spec Workflow Workers
|
|
98
|
+
researcher: "Rsrch",
|
|
99
|
+
requirements: "Reqs",
|
|
100
|
+
design: "Dsgn",
|
|
101
|
+
tasks: "Tasks",
|
|
102
|
+
validator: "Valid",
|
|
103
|
+
|
|
104
|
+
// Builtin Workers
|
|
105
|
+
coder: "Code",
|
|
106
|
+
qa: "QA",
|
|
107
|
+
writer: "Write",
|
|
108
|
+
analyst: "Anlst",
|
|
109
|
+
architect: "Arch",
|
|
110
|
+
devops: "DevOp",
|
|
111
|
+
security: "Secur",
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// Main Component
|
|
116
|
+
// =============================================================================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Format cost as currency string.
|
|
120
|
+
*/
|
|
121
|
+
function formatCost(cost: number): string {
|
|
122
|
+
if (cost < 0.01) {
|
|
123
|
+
return `$${cost.toFixed(4)}`;
|
|
124
|
+
}
|
|
125
|
+
if (cost < 1) {
|
|
126
|
+
return `$${cost.toFixed(2)}`;
|
|
127
|
+
}
|
|
128
|
+
return `$${cost.toFixed(2)}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* StatusBar displays all status indicators in a horizontal layout.
|
|
133
|
+
*
|
|
134
|
+
* Unified Footer Layout:
|
|
135
|
+
* ```
|
|
136
|
+
* ◐ vibe ◇ planning ◈ spec L1 │ claude-sonnet │ ▓▓▓░░ 45% │ $0.02
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* Features:
|
|
140
|
+
* - All modes shown with active one highlighted
|
|
141
|
+
* - Agent level indicator for spec mode (L0/L1/L2)
|
|
142
|
+
* - Model name only (no provider prefix)
|
|
143
|
+
* - Context progress with visual bar
|
|
144
|
+
* - Cost display
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```tsx
|
|
148
|
+
* <StatusBar
|
|
149
|
+
* mode="vibe"
|
|
150
|
+
* modelName="claude-sonnet-4"
|
|
151
|
+
* tokens={{ current: 5000, max: 100000 }}
|
|
152
|
+
* cost={0.02}
|
|
153
|
+
* />
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: StatusBar displays many dynamic UI elements
|
|
157
|
+
export function StatusBar({
|
|
158
|
+
mode = "vibe",
|
|
159
|
+
agentName,
|
|
160
|
+
agentLevel,
|
|
161
|
+
modelName,
|
|
162
|
+
tokens,
|
|
163
|
+
trustMode,
|
|
164
|
+
sandboxPolicy,
|
|
165
|
+
thinking,
|
|
166
|
+
cost,
|
|
167
|
+
showBorder = false,
|
|
168
|
+
showAllModes = false,
|
|
169
|
+
// NOTE: persistence prop kept for API compatibility but no longer rendered in footer
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
171
|
+
persistence: _persistence,
|
|
172
|
+
}: StatusBarProps): React.JSX.Element {
|
|
173
|
+
const { theme } = useTheme();
|
|
174
|
+
const { t } = useTUITranslation();
|
|
175
|
+
|
|
176
|
+
// Log deprecation warning for agentLevel prop (T017)
|
|
177
|
+
if (agentLevel !== undefined && process.env.NODE_ENV !== "production") {
|
|
178
|
+
console.warn(
|
|
179
|
+
"DEPRECATION WARNING: The 'agentLevel' prop is deprecated in StatusBar. " +
|
|
180
|
+
"Agent level is now derived from agent state in spec mode workflows."
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Use primary/accent color for status bar border
|
|
185
|
+
const borderColor = theme.colors.primary;
|
|
186
|
+
|
|
187
|
+
// Get agent abbreviation for display (fallback: first 5 chars)
|
|
188
|
+
const agentAbbrev = agentName
|
|
189
|
+
? (AGENT_ABBREVIATIONS[agentName] ?? agentName.slice(0, 5))
|
|
190
|
+
: undefined;
|
|
191
|
+
|
|
192
|
+
// Render mode selector (all modes shown, active highlighted)
|
|
193
|
+
const visibleModes = showAllModes
|
|
194
|
+
? MODES_CONFIG
|
|
195
|
+
: MODES_CONFIG.filter((modeConfig) => modeConfig.mode === mode);
|
|
196
|
+
|
|
197
|
+
// Mode-specific gradient colors
|
|
198
|
+
const modeGradients: Record<CodingMode, readonly string[]> = useMemo(
|
|
199
|
+
() => ({
|
|
200
|
+
vibe: [theme.colors.success, "#34d399", "#6ee7b7"], // Green gradient
|
|
201
|
+
plan: [theme.colors.info, "#60a5fa", "#93c5fd"], // Blue gradient
|
|
202
|
+
spec: [theme.colors.primary, "#8b5cf6", "#a78bfa"], // Purple gradient
|
|
203
|
+
}),
|
|
204
|
+
[theme.colors]
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const modeSection = (
|
|
208
|
+
<Box key="modes" flexDirection="row" alignItems="center">
|
|
209
|
+
{visibleModes.map((modeConfig, index) => {
|
|
210
|
+
const isActive = modeConfig.mode === mode;
|
|
211
|
+
const modeText = `${modeConfig.icon} ${modeConfig.label}`;
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<Text key={modeConfig.mode}>
|
|
215
|
+
{index > 0 && " "}
|
|
216
|
+
{isActive ? (
|
|
217
|
+
<GradientText text={modeText} colors={modeGradients[modeConfig.mode]} bold />
|
|
218
|
+
) : (
|
|
219
|
+
<Text color={theme.semantic.text.muted} dimColor>
|
|
220
|
+
{modeText}
|
|
221
|
+
</Text>
|
|
222
|
+
)}
|
|
223
|
+
</Text>
|
|
224
|
+
);
|
|
225
|
+
})}
|
|
226
|
+
{/* Agent level indicator: │Orch·L0 */}
|
|
227
|
+
{agentAbbrev !== undefined && agentLevel !== undefined && (
|
|
228
|
+
<Text>
|
|
229
|
+
<Text color={theme.semantic.border.muted}>│</Text>
|
|
230
|
+
<GradientText
|
|
231
|
+
text={`${agentAbbrev}·L${agentLevel}`}
|
|
232
|
+
colors={[theme.brand.primary, theme.brand.secondary]}
|
|
233
|
+
bold
|
|
234
|
+
/>
|
|
235
|
+
</Text>
|
|
236
|
+
)}
|
|
237
|
+
</Box>
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Collect right-side indicators
|
|
241
|
+
const rightIndicators: React.ReactNode[] = [];
|
|
242
|
+
|
|
243
|
+
// Resilience status (rate limiting, retry) - shown first when active
|
|
244
|
+
const resilienceSegment = <ResilienceStatusSegment key="resilience" />;
|
|
245
|
+
// Note: ResilienceStatusSegment returns null when inactive, so we can always include it
|
|
246
|
+
rightIndicators.push(resilienceSegment);
|
|
247
|
+
|
|
248
|
+
// Model indicator (compact, name only)
|
|
249
|
+
if (modelName) {
|
|
250
|
+
rightIndicators.push(<ModelIndicator key="model" model={modelName} compact />);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Sandbox policy indicator (shows file access boundaries)
|
|
254
|
+
if (sandboxPolicy) {
|
|
255
|
+
rightIndicators.push(<SandboxIndicator key="sandbox" policy={sandboxPolicy} />);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Context progress and/or token breakdown
|
|
259
|
+
if (tokens) {
|
|
260
|
+
// Show granular breakdown if requested and data available
|
|
261
|
+
if (tokens.showBreakdown && tokens.breakdown) {
|
|
262
|
+
rightIndicators.push(
|
|
263
|
+
<TokenBreakdown
|
|
264
|
+
key="token-breakdown"
|
|
265
|
+
turn={tokens.turnUsage}
|
|
266
|
+
total={tokens.breakdown}
|
|
267
|
+
compact={true}
|
|
268
|
+
showTurn={!!tokens.turnUsage}
|
|
269
|
+
/>
|
|
270
|
+
);
|
|
271
|
+
} else {
|
|
272
|
+
// Default: show progress bar
|
|
273
|
+
rightIndicators.push(
|
|
274
|
+
<ContextProgress
|
|
275
|
+
key="tokens"
|
|
276
|
+
current={tokens.current}
|
|
277
|
+
max={tokens.max}
|
|
278
|
+
showLabel={false}
|
|
279
|
+
barWidth={tokens.barWidth ?? 5}
|
|
280
|
+
/>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Trust mode (if provided)
|
|
286
|
+
if (trustMode) {
|
|
287
|
+
rightIndicators.push(<TrustModeIndicator key="trust" mode={trustMode} />);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Thinking mode (if provided)
|
|
291
|
+
if (thinking) {
|
|
292
|
+
rightIndicators.push(
|
|
293
|
+
<ThinkingModeIndicator
|
|
294
|
+
key="thinking"
|
|
295
|
+
active={thinking.active}
|
|
296
|
+
budget={thinking.budget}
|
|
297
|
+
used={thinking.used}
|
|
298
|
+
/>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Cost display
|
|
303
|
+
if (cost !== undefined && cost > 0) {
|
|
304
|
+
rightIndicators.push(
|
|
305
|
+
<Box key="cost">
|
|
306
|
+
<Text color={theme.colors.success}>{formatCost(cost)}</Text>
|
|
307
|
+
</Box>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// NOTE: Persistence status indicator moved to Sidebar's SystemStatusPanel (T034 UI cleanup)
|
|
312
|
+
// The persistence prop is kept for API compatibility but no longer rendered in footer.
|
|
313
|
+
|
|
314
|
+
// Sidebar toggle hint (show only if there's room - not shown when many indicators present)
|
|
315
|
+
// Skip when there are 4+ indicators to avoid terminal overflow
|
|
316
|
+
if (rightIndicators.length < 4) {
|
|
317
|
+
rightIndicators.push(
|
|
318
|
+
<Box key="sidebar-hint">
|
|
319
|
+
<Text color={theme.semantic.text.muted} dimColor>
|
|
320
|
+
Alt+K sidebar
|
|
321
|
+
</Text>
|
|
322
|
+
</Box>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Render right indicators with separators (compact layout)
|
|
327
|
+
const renderedRightItems: React.ReactNode[] = [];
|
|
328
|
+
for (let i = 0; i < rightIndicators.length; i++) {
|
|
329
|
+
if (i > 0) {
|
|
330
|
+
renderedRightItems.push(
|
|
331
|
+
<Text key={`sep-${i}`} color={theme.semantic.border.muted}>
|
|
332
|
+
{SEPARATOR}
|
|
333
|
+
</Text>
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
renderedRightItems.push(rightIndicators[i]);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Empty state
|
|
340
|
+
if (!modelName && !tokens && !cost) {
|
|
341
|
+
return (
|
|
342
|
+
<Box flexDirection="row" justifyContent="space-between" width="100%">
|
|
343
|
+
{modeSection}
|
|
344
|
+
<Text color={theme.semantic.text.muted}>{t("status.noInfo")}</Text>
|
|
345
|
+
</Box>
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<Box
|
|
351
|
+
flexDirection="row"
|
|
352
|
+
justifyContent="space-between"
|
|
353
|
+
width="100%"
|
|
354
|
+
paddingX={showBorder ? 1 : 0}
|
|
355
|
+
borderStyle={showBorder ? "round" : undefined}
|
|
356
|
+
borderColor={showBorder ? borderColor : undefined}
|
|
357
|
+
>
|
|
358
|
+
{/* Left: Mode selector */}
|
|
359
|
+
{modeSection}
|
|
360
|
+
|
|
361
|
+
{/* Right: Model, Context, Cost - with separator from mode section */}
|
|
362
|
+
<Box flexDirection="row" alignItems="center">
|
|
363
|
+
<Text color={theme.semantic.border.muted}>{SEPARATOR}</Text>
|
|
364
|
+
{renderedRightItems}
|
|
365
|
+
</Box>
|
|
366
|
+
</Box>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThinkingModeIndicator Component (T038)
|
|
3
|
+
*
|
|
4
|
+
* Displays the thinking mode status with optional budget tracking.
|
|
5
|
+
* Shows whether extended thinking is active and usage if budget is set.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/StatusBar/ThinkingModeIndicator
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Box, Text } from "ink";
|
|
11
|
+
import { useMemo } from "react";
|
|
12
|
+
import { useTheme } from "../../theme/index.js";
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Types
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Props for the ThinkingModeIndicator component.
|
|
20
|
+
*/
|
|
21
|
+
export interface ThinkingModeIndicatorProps {
|
|
22
|
+
/** Whether thinking mode is active */
|
|
23
|
+
readonly active: boolean;
|
|
24
|
+
/** Optional thinking budget (max tokens for thinking) */
|
|
25
|
+
readonly budget?: number;
|
|
26
|
+
/** Optional tokens used for thinking */
|
|
27
|
+
readonly used?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// Constants
|
|
32
|
+
// =============================================================================
|
|
33
|
+
|
|
34
|
+
/** Icon for active thinking mode */
|
|
35
|
+
const THINKING_ACTIVE_ICON = "◆";
|
|
36
|
+
|
|
37
|
+
/** Icon for inactive thinking mode */
|
|
38
|
+
const THINKING_INACTIVE_ICON = "◇";
|
|
39
|
+
|
|
40
|
+
/** Warning threshold for thinking budget (80%) */
|
|
41
|
+
const BUDGET_WARNING_THRESHOLD = 80;
|
|
42
|
+
|
|
43
|
+
/** Error threshold for thinking budget (95%) */
|
|
44
|
+
const BUDGET_ERROR_THRESHOLD = 95;
|
|
45
|
+
|
|
46
|
+
// =============================================================================
|
|
47
|
+
// Helper Functions
|
|
48
|
+
// =============================================================================
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Formats token count for compact display.
|
|
52
|
+
*/
|
|
53
|
+
function formatTokens(count: number): string {
|
|
54
|
+
if (count < 1000) {
|
|
55
|
+
return count.toString();
|
|
56
|
+
}
|
|
57
|
+
if (count < 1000000) {
|
|
58
|
+
const k = count / 1000;
|
|
59
|
+
return k >= 10 ? `${Math.round(k)}K` : `${k.toFixed(1)}K`;
|
|
60
|
+
}
|
|
61
|
+
const m = count / 1000000;
|
|
62
|
+
return m >= 10 ? `${Math.round(m)}M` : `${m.toFixed(1)}M`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Calculates percentage of budget used.
|
|
67
|
+
*/
|
|
68
|
+
function calculateUsagePercentage(used: number, budget: number): number {
|
|
69
|
+
if (budget <= 0) return 0;
|
|
70
|
+
return Math.min(Math.round((used / budget) * 100), 100);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// =============================================================================
|
|
74
|
+
// Main Component
|
|
75
|
+
// =============================================================================
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* ThinkingModeIndicator displays thinking mode status and budget usage.
|
|
79
|
+
*
|
|
80
|
+
* Features:
|
|
81
|
+
* - Active/inactive state with icon
|
|
82
|
+
* - Optional budget display
|
|
83
|
+
* - Usage tracking with color-coded warnings
|
|
84
|
+
* - Compact format for status bar
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```tsx
|
|
88
|
+
* // Active without budget
|
|
89
|
+
* <ThinkingModeIndicator active={true} />
|
|
90
|
+
*
|
|
91
|
+
* // Active with budget tracking
|
|
92
|
+
* <ThinkingModeIndicator
|
|
93
|
+
* active={true}
|
|
94
|
+
* budget={10000}
|
|
95
|
+
* used={5000}
|
|
96
|
+
* />
|
|
97
|
+
*
|
|
98
|
+
* // Inactive
|
|
99
|
+
* <ThinkingModeIndicator active={false} />
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function ThinkingModeIndicator({
|
|
103
|
+
active,
|
|
104
|
+
budget,
|
|
105
|
+
used,
|
|
106
|
+
}: ThinkingModeIndicatorProps): React.JSX.Element {
|
|
107
|
+
const { theme } = useTheme();
|
|
108
|
+
|
|
109
|
+
const icon = active ? THINKING_ACTIVE_ICON : THINKING_INACTIVE_ICON;
|
|
110
|
+
|
|
111
|
+
// Determine icon color
|
|
112
|
+
const iconColor = useMemo(() => {
|
|
113
|
+
if (!active) {
|
|
114
|
+
return theme.semantic.text.muted;
|
|
115
|
+
}
|
|
116
|
+
return theme.colors.primary;
|
|
117
|
+
}, [active, theme.colors.primary, theme.semantic.text.muted]);
|
|
118
|
+
|
|
119
|
+
// Calculate usage if budget is provided
|
|
120
|
+
const usageInfo = useMemo(() => {
|
|
121
|
+
if (budget === undefined || !active) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const currentUsed = used ?? 0;
|
|
126
|
+
const percentage = calculateUsagePercentage(currentUsed, budget);
|
|
127
|
+
|
|
128
|
+
// Determine color based on usage
|
|
129
|
+
let color: string;
|
|
130
|
+
if (percentage >= BUDGET_ERROR_THRESHOLD) {
|
|
131
|
+
color = theme.colors.error;
|
|
132
|
+
} else if (percentage >= BUDGET_WARNING_THRESHOLD) {
|
|
133
|
+
color = theme.colors.warning;
|
|
134
|
+
} else {
|
|
135
|
+
color = theme.semantic.text.secondary;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
used: formatTokens(currentUsed),
|
|
140
|
+
budget: formatTokens(budget),
|
|
141
|
+
percentage,
|
|
142
|
+
color,
|
|
143
|
+
};
|
|
144
|
+
}, [
|
|
145
|
+
budget,
|
|
146
|
+
used,
|
|
147
|
+
active,
|
|
148
|
+
theme.colors.error,
|
|
149
|
+
theme.colors.warning,
|
|
150
|
+
theme.semantic.text.secondary,
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
const label = active ? "Think" : "Think";
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<Box>
|
|
157
|
+
<Text color={iconColor}>{icon}</Text>
|
|
158
|
+
<Text color={theme.semantic.text.muted}> </Text>
|
|
159
|
+
<Text color={active ? theme.semantic.text.primary : theme.semantic.text.muted}>{label}</Text>
|
|
160
|
+
{usageInfo && (
|
|
161
|
+
<>
|
|
162
|
+
<Text color={theme.semantic.text.muted}> </Text>
|
|
163
|
+
<Text color={usageInfo.color}>{usageInfo.used}</Text>
|
|
164
|
+
<Text color={theme.semantic.text.muted}>/</Text>
|
|
165
|
+
<Text color={theme.semantic.text.secondary}>{usageInfo.budget}</Text>
|
|
166
|
+
</>
|
|
167
|
+
)}
|
|
168
|
+
</Box>
|
|
169
|
+
);
|
|
170
|
+
}
|