@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,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProtectedFileLegend Component
|
|
3
|
+
*
|
|
4
|
+
* Legend indicator for protected files in directory listings.
|
|
5
|
+
* Shows [#] = Protected file explanation when protected files are present.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/common/ProtectedFileLegend
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Box, Text } from "ink";
|
|
11
|
+
import type React from "react";
|
|
12
|
+
import { memo } from "react";
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Constants
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
/** Protected file indicator matching core/permission/protected-files.ts */
|
|
19
|
+
export const PROTECTED_INDICATOR = "[#]" as const;
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Types
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Props for ProtectedFileLegend component.
|
|
27
|
+
*/
|
|
28
|
+
export interface ProtectedFileLegendProps {
|
|
29
|
+
/** Whether to show the legend (typically when hasProtectedFiles is true) */
|
|
30
|
+
readonly show?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// Component
|
|
35
|
+
// =============================================================================
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Legend explaining the protected file indicator.
|
|
39
|
+
*
|
|
40
|
+
* Displays a dim, single-line hint at the bottom of file listings
|
|
41
|
+
* when protected files are present.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* <ProtectedFileLegend show={hasProtectedFiles} />
|
|
46
|
+
* // Renders: [#] = Protected file (always requires approval)
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
function ProtectedFileLegendImpl({ show = false }: ProtectedFileLegendProps): React.JSX.Element {
|
|
50
|
+
if (!show) {
|
|
51
|
+
return <Box />;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Box marginTop={1}>
|
|
56
|
+
<Text dimColor>
|
|
57
|
+
<Text bold>{PROTECTED_INDICATOR}</Text>
|
|
58
|
+
<Text> = Protected file (always requires approval)</Text>
|
|
59
|
+
</Text>
|
|
60
|
+
</Box>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Memoized ProtectedFileLegend component.
|
|
66
|
+
* Only re-renders when show prop changes.
|
|
67
|
+
*/
|
|
68
|
+
export const ProtectedFileLegend = memo(ProtectedFileLegendImpl);
|
|
69
|
+
|
|
70
|
+
// =============================================================================
|
|
71
|
+
// Formatting Utilities
|
|
72
|
+
// =============================================================================
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Format a file entry name with protection indicator.
|
|
76
|
+
*
|
|
77
|
+
* @param name - File name
|
|
78
|
+
* @param isProtected - Whether the file is protected
|
|
79
|
+
* @returns Formatted name with [#] prefix if protected
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* formatProtectedFileName('.env', true); // '[#] .env'
|
|
84
|
+
* formatProtectedFileName('app.ts', false); // 'app.ts'
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export function formatProtectedFileName(name: string, isProtected?: boolean): string {
|
|
88
|
+
return isProtected ? `${PROTECTED_INDICATOR} ${name}` : name;
|
|
89
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ScrollIndicator Component
|
|
3
|
+
*
|
|
4
|
+
* Minimal ASCII scrollbar for terminal UIs.
|
|
5
|
+
* Displays a vertical track with a proportionally-sized thumb.
|
|
6
|
+
* Supports animated colors via useAnimatedScrollbar hook.
|
|
7
|
+
*
|
|
8
|
+
* @module tui/components/common/ScrollIndicator
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Box, Text } from "ink";
|
|
12
|
+
import type React from "react";
|
|
13
|
+
import { useMemo } from "react";
|
|
14
|
+
import { useTheme } from "../../theme/index.js";
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Types
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Props for ScrollIndicator component.
|
|
22
|
+
*/
|
|
23
|
+
export interface ScrollIndicatorProps {
|
|
24
|
+
/** Total content height in lines */
|
|
25
|
+
readonly totalHeight: number;
|
|
26
|
+
/** Current offset from bottom */
|
|
27
|
+
readonly offsetFromBottom: number;
|
|
28
|
+
/** Viewport height */
|
|
29
|
+
readonly viewportHeight: number;
|
|
30
|
+
/** Whether to show (default: auto based on content > viewport) */
|
|
31
|
+
readonly show?: boolean;
|
|
32
|
+
/** Animated thumb color (from useAnimatedScrollbar) */
|
|
33
|
+
readonly thumbColor?: string;
|
|
34
|
+
/** Animated track color (from useAnimatedScrollbar) */
|
|
35
|
+
readonly trackColor?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// Constants
|
|
40
|
+
// =============================================================================
|
|
41
|
+
|
|
42
|
+
/** Scrollbar characters */
|
|
43
|
+
const CHARS = {
|
|
44
|
+
thumb: "█",
|
|
45
|
+
track: "│",
|
|
46
|
+
} as const;
|
|
47
|
+
|
|
48
|
+
/** Minimum thumb size in lines */
|
|
49
|
+
const MIN_THUMB_SIZE = 1;
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Component
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Vertical scrollbar indicator for terminal UIs.
|
|
57
|
+
*
|
|
58
|
+
* Renders a minimal ASCII scrollbar. Thumb size is proportional
|
|
59
|
+
* to viewport/content ratio. Returns null when not scrollable.
|
|
60
|
+
*
|
|
61
|
+
* Supports animated colors via optional thumbColor/trackColor props
|
|
62
|
+
* from useAnimatedScrollbar hook.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* // Basic usage
|
|
67
|
+
* <Box flexDirection="row">
|
|
68
|
+
* <Box flexGrow={1}>{content}</Box>
|
|
69
|
+
* <ScrollIndicator
|
|
70
|
+
* totalHeight={100}
|
|
71
|
+
* offsetFromBottom={25}
|
|
72
|
+
* viewportHeight={20}
|
|
73
|
+
* />
|
|
74
|
+
* </Box>
|
|
75
|
+
*
|
|
76
|
+
* // With animated colors
|
|
77
|
+
* const { scrollbarColor, trackColor } = useAnimatedScrollbar(isFocused, scrollBy);
|
|
78
|
+
* <ScrollIndicator
|
|
79
|
+
* totalHeight={100}
|
|
80
|
+
* offsetFromBottom={25}
|
|
81
|
+
* viewportHeight={20}
|
|
82
|
+
* thumbColor={scrollbarColor}
|
|
83
|
+
* trackColor={trackColor}
|
|
84
|
+
* />
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export function ScrollIndicator({
|
|
88
|
+
totalHeight,
|
|
89
|
+
offsetFromBottom,
|
|
90
|
+
viewportHeight,
|
|
91
|
+
show,
|
|
92
|
+
thumbColor: animatedThumbColor,
|
|
93
|
+
trackColor: animatedTrackColor,
|
|
94
|
+
}: ScrollIndicatorProps): React.ReactElement | null {
|
|
95
|
+
const { theme } = useTheme();
|
|
96
|
+
|
|
97
|
+
// Determine if scrollable
|
|
98
|
+
const isScrollable = totalHeight > viewportHeight;
|
|
99
|
+
|
|
100
|
+
// Calculate visibility
|
|
101
|
+
const shouldShow = show ?? isScrollable;
|
|
102
|
+
|
|
103
|
+
// Calculate scrollbar metrics
|
|
104
|
+
const metrics = useMemo(() => {
|
|
105
|
+
if (!isScrollable || viewportHeight <= 0 || totalHeight <= 0) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Thumb size proportional to viewport/content ratio
|
|
110
|
+
const ratio = viewportHeight / totalHeight;
|
|
111
|
+
const thumbSize = Math.max(MIN_THUMB_SIZE, Math.round(viewportHeight * ratio));
|
|
112
|
+
|
|
113
|
+
// Track height (use actual viewport height)
|
|
114
|
+
const trackHeight = viewportHeight;
|
|
115
|
+
|
|
116
|
+
// Calculate thumb position
|
|
117
|
+
// offsetFromBottom=0 means at bottom, thumb at bottom
|
|
118
|
+
// offsetFromBottom=max means at top, thumb at top
|
|
119
|
+
const maxOffset = totalHeight - viewportHeight;
|
|
120
|
+
const scrollProgress = maxOffset > 0 ? 1 - offsetFromBottom / maxOffset : 1;
|
|
121
|
+
|
|
122
|
+
// Thumb position: 0 = top, (trackHeight - thumbSize) = bottom
|
|
123
|
+
const maxThumbPos = trackHeight - thumbSize;
|
|
124
|
+
const thumbPosition = Math.round(scrollProgress * maxThumbPos);
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
thumbSize,
|
|
128
|
+
thumbPosition,
|
|
129
|
+
trackHeight,
|
|
130
|
+
};
|
|
131
|
+
}, [totalHeight, offsetFromBottom, viewportHeight, isScrollable]);
|
|
132
|
+
|
|
133
|
+
// Don't render if not showing or no metrics
|
|
134
|
+
if (!shouldShow || !metrics) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { thumbSize, thumbPosition, trackHeight } = metrics;
|
|
139
|
+
|
|
140
|
+
// Use animated colors if provided, otherwise fall back to theme
|
|
141
|
+
const trackColor = animatedTrackColor ?? theme.semantic.border.muted;
|
|
142
|
+
const thumbColor = animatedThumbColor ?? theme.semantic.text.muted;
|
|
143
|
+
|
|
144
|
+
// Build scrollbar lines
|
|
145
|
+
const lines: React.ReactNode[] = [];
|
|
146
|
+
for (let i = 0; i < trackHeight; i++) {
|
|
147
|
+
const isThumb = i >= thumbPosition && i < thumbPosition + thumbSize;
|
|
148
|
+
lines.push(
|
|
149
|
+
<Text key={i} color={isThumb ? thumbColor : trackColor}>
|
|
150
|
+
{isThumb ? CHARS.thumb : CHARS.track}
|
|
151
|
+
</Text>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<Box flexDirection="column" width={1}>
|
|
157
|
+
{lines}
|
|
158
|
+
</Box>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Component (Chain 21)
|
|
3
|
+
*
|
|
4
|
+
* Reusable animated spinner for loading states.
|
|
5
|
+
* Extracted from ThinkingBlock for general use.
|
|
6
|
+
*
|
|
7
|
+
* Uses global AnimationContext for centralized timing to prevent
|
|
8
|
+
* flickering from multiple independent timers.
|
|
9
|
+
*
|
|
10
|
+
* Supports both custom animation frames and ink-spinner types.
|
|
11
|
+
* Includes scene-based animations for contextual loading states.
|
|
12
|
+
*
|
|
13
|
+
* @module tui/components/common/Spinner
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Text } from "ink";
|
|
17
|
+
import InkSpinner from "ink-spinner";
|
|
18
|
+
import type React from "react";
|
|
19
|
+
import { useAnimationFrame } from "../../context/AnimationContext.js";
|
|
20
|
+
import { useTheme } from "../../theme/index.js";
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Types
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Scene-based spinner types for contextual loading states.
|
|
28
|
+
* Each scene has a unique animation that conveys the operation type.
|
|
29
|
+
*/
|
|
30
|
+
export type SpinnerScene =
|
|
31
|
+
| "default" // Default braille spinner
|
|
32
|
+
| "thinking" // AI thinking: 🤔 💭 💡 ✨
|
|
33
|
+
| "writing" // File writing: ✎. ✎.. ✎...
|
|
34
|
+
| "searching" // Search operation: 🔍 🔎
|
|
35
|
+
| "loading" // Generic loading: ◐ ◓ ◑ ◒
|
|
36
|
+
| "streaming"; // Stream output: ▰▰▰▱▱▱
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Available ink-spinner type names.
|
|
40
|
+
* These are the built-in spinner types from cli-spinners.
|
|
41
|
+
*/
|
|
42
|
+
export type SpinnerType =
|
|
43
|
+
| "dots"
|
|
44
|
+
| "dots2"
|
|
45
|
+
| "dots3"
|
|
46
|
+
| "dots4"
|
|
47
|
+
| "dots5"
|
|
48
|
+
| "dots6"
|
|
49
|
+
| "dots7"
|
|
50
|
+
| "dots8"
|
|
51
|
+
| "dots9"
|
|
52
|
+
| "dots10"
|
|
53
|
+
| "dots11"
|
|
54
|
+
| "dots12"
|
|
55
|
+
| "line"
|
|
56
|
+
| "line2"
|
|
57
|
+
| "pipe"
|
|
58
|
+
| "simpleDots"
|
|
59
|
+
| "simpleDotsScrolling"
|
|
60
|
+
| "star"
|
|
61
|
+
| "star2"
|
|
62
|
+
| "flip"
|
|
63
|
+
| "hamburger"
|
|
64
|
+
| "growVertical"
|
|
65
|
+
| "growHorizontal"
|
|
66
|
+
| "balloon"
|
|
67
|
+
| "balloon2"
|
|
68
|
+
| "noise"
|
|
69
|
+
| "bounce"
|
|
70
|
+
| "boxBounce"
|
|
71
|
+
| "boxBounce2"
|
|
72
|
+
| "triangle"
|
|
73
|
+
| "arc"
|
|
74
|
+
| "circle"
|
|
75
|
+
| "squareCorners"
|
|
76
|
+
| "circleQuarters"
|
|
77
|
+
| "circleHalves"
|
|
78
|
+
| "squish"
|
|
79
|
+
| "toggle"
|
|
80
|
+
| "toggle2"
|
|
81
|
+
| "toggle3"
|
|
82
|
+
| "toggle4"
|
|
83
|
+
| "toggle5"
|
|
84
|
+
| "toggle6"
|
|
85
|
+
| "toggle7"
|
|
86
|
+
| "toggle8"
|
|
87
|
+
| "toggle9"
|
|
88
|
+
| "toggle10"
|
|
89
|
+
| "toggle11"
|
|
90
|
+
| "toggle12"
|
|
91
|
+
| "toggle13"
|
|
92
|
+
| "arrow"
|
|
93
|
+
| "arrow2"
|
|
94
|
+
| "arrow3"
|
|
95
|
+
| "bouncingBar"
|
|
96
|
+
| "bouncingBall"
|
|
97
|
+
| "smiley"
|
|
98
|
+
| "monkey"
|
|
99
|
+
| "hearts"
|
|
100
|
+
| "clock"
|
|
101
|
+
| "earth"
|
|
102
|
+
| "moon"
|
|
103
|
+
| "runner"
|
|
104
|
+
| "pong"
|
|
105
|
+
| "shark"
|
|
106
|
+
| "dqpb"
|
|
107
|
+
| "weather"
|
|
108
|
+
| "christmas"
|
|
109
|
+
| "grenade"
|
|
110
|
+
| "point"
|
|
111
|
+
| "layer"
|
|
112
|
+
| "betaWave";
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Props for the Spinner component.
|
|
116
|
+
*/
|
|
117
|
+
export interface SpinnerProps {
|
|
118
|
+
/** Color of the spinner (default: from theme or "cyan") */
|
|
119
|
+
readonly color?: string;
|
|
120
|
+
/**
|
|
121
|
+
* Scene-based spinner animation (default: 'default').
|
|
122
|
+
* Provides contextual animations for different operation types.
|
|
123
|
+
* Takes precedence over `frames` when specified (unless 'default').
|
|
124
|
+
*/
|
|
125
|
+
readonly scene?: SpinnerScene;
|
|
126
|
+
/**
|
|
127
|
+
* Label to display after the spinner.
|
|
128
|
+
* Useful for providing context about the current operation.
|
|
129
|
+
*/
|
|
130
|
+
readonly label?: string;
|
|
131
|
+
/**
|
|
132
|
+
* Animation frames (default: braille spinner)
|
|
133
|
+
* Ignored when `type` or non-default `scene` is specified.
|
|
134
|
+
*/
|
|
135
|
+
readonly frames?: readonly string[];
|
|
136
|
+
/**
|
|
137
|
+
* Animation interval in milliseconds
|
|
138
|
+
* @deprecated No longer used - interval is controlled globally by AnimationContext
|
|
139
|
+
*/
|
|
140
|
+
readonly interval?: number;
|
|
141
|
+
/**
|
|
142
|
+
* Use ink-spinner with specified type.
|
|
143
|
+
* When set, this takes precedence over `frames` and `scene`.
|
|
144
|
+
* @example "dots", "line", "arc", "bounce"
|
|
145
|
+
*/
|
|
146
|
+
readonly type?: SpinnerType;
|
|
147
|
+
/**
|
|
148
|
+
* Whether to use ink-spinner (default: false for backward compatibility).
|
|
149
|
+
* Set to true to use ink-spinner even without specifying a type.
|
|
150
|
+
*/
|
|
151
|
+
readonly useInkSpinner?: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Props for the LoadingIndicator component.
|
|
156
|
+
*/
|
|
157
|
+
export interface LoadingIndicatorProps {
|
|
158
|
+
/** Message to display alongside the spinner */
|
|
159
|
+
readonly message?: string;
|
|
160
|
+
/** Whether to show the spinner (default: true) */
|
|
161
|
+
readonly showSpinner?: boolean;
|
|
162
|
+
/** Color for the spinner */
|
|
163
|
+
readonly spinnerColor?: string;
|
|
164
|
+
/** Spinner animation frames */
|
|
165
|
+
readonly frames?: readonly string[];
|
|
166
|
+
/** Whether the indicator is dimmed */
|
|
167
|
+
readonly dimmed?: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// =============================================================================
|
|
171
|
+
// Constants
|
|
172
|
+
// =============================================================================
|
|
173
|
+
|
|
174
|
+
/** Default spinner animation frames (braille pattern) */
|
|
175
|
+
export const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Scene-based animation frames for contextual loading states.
|
|
179
|
+
* Each scene provides a unique visual representation of the operation.
|
|
180
|
+
*/
|
|
181
|
+
export const SCENE_FRAMES = {
|
|
182
|
+
/** Default braille spinner */
|
|
183
|
+
default: ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"],
|
|
184
|
+
/** AI thinking animation */
|
|
185
|
+
thinking: ["🤔", "💭", "💡", "✨"],
|
|
186
|
+
/** File writing animation */
|
|
187
|
+
writing: ["✎", "✎.", "✎..", "✎..."],
|
|
188
|
+
/** Search operation animation */
|
|
189
|
+
searching: ["🔍", "🔎"],
|
|
190
|
+
/** Generic loading animation */
|
|
191
|
+
loading: ["◐", "◓", "◑", "◒"],
|
|
192
|
+
/** Streaming data animation */
|
|
193
|
+
streaming: ["▰▱▱▱", "▰▰▱▱", "▰▰▰▱", "▰▰▰▰", "▱▰▰▰", "▱▱▰▰", "▱▱▱▰"],
|
|
194
|
+
} as const;
|
|
195
|
+
|
|
196
|
+
/** Alternative spinner styles */
|
|
197
|
+
export const SPINNER_STYLES = {
|
|
198
|
+
/** Braille dots (default) */
|
|
199
|
+
braille: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
200
|
+
/** Classic dots */
|
|
201
|
+
dots: ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"],
|
|
202
|
+
/** Simple line */
|
|
203
|
+
line: ["-", "\\", "|", "/"],
|
|
204
|
+
/** Growing dots */
|
|
205
|
+
growDots: [".", "..", "...", "...."],
|
|
206
|
+
/** Bouncing ball */
|
|
207
|
+
bounce: ["⠁", "⠂", "⠄", "⠂"],
|
|
208
|
+
/** Arc spinner */
|
|
209
|
+
arc: ["◜", "◠", "◝", "◞", "◡", "◟"],
|
|
210
|
+
/** Box spinner */
|
|
211
|
+
box: ["▖", "▘", "▝", "▗"],
|
|
212
|
+
} as const;
|
|
213
|
+
|
|
214
|
+
/** Default animation interval in milliseconds
|
|
215
|
+
* @deprecated Interval is now controlled globally by AnimationContext (120ms default, 150ms for VS Code)
|
|
216
|
+
*/
|
|
217
|
+
const DEFAULT_INTERVAL_MS = 120;
|
|
218
|
+
|
|
219
|
+
// =============================================================================
|
|
220
|
+
// Spinner Component
|
|
221
|
+
// =============================================================================
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Spinner - Animated loading indicator.
|
|
225
|
+
*
|
|
226
|
+
* Features:
|
|
227
|
+
* - Configurable animation frames
|
|
228
|
+
* - Scene-based animations for contextual loading states
|
|
229
|
+
* - Adjustable speed
|
|
230
|
+
* - Custom colors
|
|
231
|
+
* - Multiple built-in styles
|
|
232
|
+
* - Support for ink-spinner types
|
|
233
|
+
* - Optional label display
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```tsx
|
|
237
|
+
* // Basic usage (custom frames)
|
|
238
|
+
* <Spinner />
|
|
239
|
+
*
|
|
240
|
+
* // Scene-based animation
|
|
241
|
+
* <Spinner scene="thinking" label="Analyzing..." />
|
|
242
|
+
*
|
|
243
|
+
* // Writing scene
|
|
244
|
+
* <Spinner scene="writing" label="Saving file..." />
|
|
245
|
+
*
|
|
246
|
+
* // Custom color
|
|
247
|
+
* <Spinner color="yellow" />
|
|
248
|
+
*
|
|
249
|
+
* // Different style
|
|
250
|
+
* <Spinner frames={SPINNER_STYLES.dots} />
|
|
251
|
+
*
|
|
252
|
+
* // Using ink-spinner type
|
|
253
|
+
* <Spinner type="dots" />
|
|
254
|
+
*
|
|
255
|
+
* // Using ink-spinner with explicit flag
|
|
256
|
+
* <Spinner useInkSpinner type="arc" color="green" />
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
export function Spinner({
|
|
260
|
+
color,
|
|
261
|
+
scene = "default",
|
|
262
|
+
label,
|
|
263
|
+
frames = SPINNER_FRAMES,
|
|
264
|
+
// interval prop kept for backward compatibility but no longer used
|
|
265
|
+
interval: _interval = DEFAULT_INTERVAL_MS,
|
|
266
|
+
type,
|
|
267
|
+
useInkSpinner = false,
|
|
268
|
+
}: SpinnerProps): React.JSX.Element {
|
|
269
|
+
const { theme } = useTheme();
|
|
270
|
+
|
|
271
|
+
// Determine which frames to use based on scene
|
|
272
|
+
const effectiveFrames = scene !== "default" ? SCENE_FRAMES[scene] : frames;
|
|
273
|
+
|
|
274
|
+
// Use global animation context for custom frames
|
|
275
|
+
const frameIndex = useAnimationFrame(effectiveFrames);
|
|
276
|
+
|
|
277
|
+
const spinnerColor = color ?? theme.semantic.text.primary;
|
|
278
|
+
|
|
279
|
+
// Use ink-spinner if type is specified or useInkSpinner is true
|
|
280
|
+
if (type || useInkSpinner) {
|
|
281
|
+
return (
|
|
282
|
+
<Text color={spinnerColor}>
|
|
283
|
+
<InkSpinner type={type ?? "dots"} />
|
|
284
|
+
{label && <Text> {label}</Text>}
|
|
285
|
+
</Text>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Default: use custom frames with AnimationContext
|
|
290
|
+
return (
|
|
291
|
+
<Text color={spinnerColor}>
|
|
292
|
+
{effectiveFrames[frameIndex]}
|
|
293
|
+
{label && <Text> {label}</Text>}
|
|
294
|
+
</Text>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// =============================================================================
|
|
299
|
+
// LoadingIndicator Component
|
|
300
|
+
// =============================================================================
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* LoadingIndicator - Spinner with optional message.
|
|
304
|
+
*
|
|
305
|
+
* Combines a spinner with a loading message for common loading states.
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```tsx
|
|
309
|
+
* // Basic loading
|
|
310
|
+
* <LoadingIndicator message="Loading..." />
|
|
311
|
+
*
|
|
312
|
+
* // Without spinner (just text)
|
|
313
|
+
* <LoadingIndicator message="Please wait" showSpinner={false} />
|
|
314
|
+
*
|
|
315
|
+
* // Dimmed style
|
|
316
|
+
* <LoadingIndicator message="Processing..." dimmed />
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export function LoadingIndicator({
|
|
320
|
+
message = "Loading...",
|
|
321
|
+
showSpinner = true,
|
|
322
|
+
spinnerColor,
|
|
323
|
+
frames,
|
|
324
|
+
dimmed = false,
|
|
325
|
+
}: LoadingIndicatorProps): React.JSX.Element {
|
|
326
|
+
return (
|
|
327
|
+
<Text dimColor={dimmed}>
|
|
328
|
+
{showSpinner && (
|
|
329
|
+
<>
|
|
330
|
+
<Spinner color={spinnerColor} frames={frames} />{" "}
|
|
331
|
+
</>
|
|
332
|
+
)}
|
|
333
|
+
{message}
|
|
334
|
+
</Text>
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// =============================================================================
|
|
339
|
+
// Exports
|
|
340
|
+
// =============================================================================
|
|
341
|
+
|
|
342
|
+
export default Spinner;
|