@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,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Components
|
|
3
|
+
*
|
|
4
|
+
* Components for user input handling in the Vellum TUI.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
Autocomplete,
|
|
9
|
+
type AutocompleteOption,
|
|
10
|
+
type AutocompleteProps,
|
|
11
|
+
} from "./Autocomplete.js";
|
|
12
|
+
export {
|
|
13
|
+
EnhancedCommandInput,
|
|
14
|
+
type EnhancedCommandInputProps,
|
|
15
|
+
} from "./EnhancedCommandInput.js";
|
|
16
|
+
export { HighlightedText, type HighlightedTextProps } from "./HighlightedText.js";
|
|
17
|
+
// Input highlighting utilities
|
|
18
|
+
export {
|
|
19
|
+
applyHighlightStyle,
|
|
20
|
+
findSegmentAtCursor,
|
|
21
|
+
getHighlightStyleDescription,
|
|
22
|
+
type HighlightResult,
|
|
23
|
+
type HighlightSegment,
|
|
24
|
+
type HighlightType,
|
|
25
|
+
highlightInput,
|
|
26
|
+
parseHighlights,
|
|
27
|
+
splitSegmentAtCursor,
|
|
28
|
+
} from "./highlight.js";
|
|
29
|
+
export {
|
|
30
|
+
type FileSuggestion,
|
|
31
|
+
MentionAutocomplete,
|
|
32
|
+
type MentionAutocompleteMode,
|
|
33
|
+
type MentionAutocompleteProps,
|
|
34
|
+
} from "./MentionAutocomplete.js";
|
|
35
|
+
export { parseSlashCommand, type SlashCommand } from "./slash-command-utils.js";
|
|
36
|
+
export { TextInput, type TextInputProps } from "./TextInput.js";
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash Command Parsing Utilities
|
|
3
|
+
*
|
|
4
|
+
* Parsing functions for slash commands. Extracted from CommandInput
|
|
5
|
+
* for reuse across input components.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/Input/slash-command-utils
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Types
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Represents a parsed slash command.
|
|
16
|
+
*/
|
|
17
|
+
export interface SlashCommand {
|
|
18
|
+
/** Command name without the leading slash */
|
|
19
|
+
readonly name: string;
|
|
20
|
+
/** Parsed arguments (handles quoted strings) */
|
|
21
|
+
readonly args: readonly string[];
|
|
22
|
+
/** Original raw input string */
|
|
23
|
+
readonly raw: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// Parsing Functions
|
|
28
|
+
// =============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parse arguments from a command string, handling quoted strings.
|
|
32
|
+
*
|
|
33
|
+
* Supports:
|
|
34
|
+
* - Space-separated arguments
|
|
35
|
+
* - Double-quoted strings: "arg with spaces"
|
|
36
|
+
* - Single-quoted strings: 'arg with spaces'
|
|
37
|
+
* - Escaped quotes within strings: "say \"hello\""
|
|
38
|
+
*
|
|
39
|
+
* @param argsString - The argument portion of the command
|
|
40
|
+
* @returns Array of parsed arguments
|
|
41
|
+
*/
|
|
42
|
+
function parseArguments(argsString: string): string[] {
|
|
43
|
+
const args: string[] = [];
|
|
44
|
+
let current = "";
|
|
45
|
+
let inQuotes = false;
|
|
46
|
+
let quoteChar = "";
|
|
47
|
+
let escaped = false;
|
|
48
|
+
|
|
49
|
+
for (let i = 0; i < argsString.length; i++) {
|
|
50
|
+
const char = argsString[i];
|
|
51
|
+
|
|
52
|
+
if (escaped) {
|
|
53
|
+
current += char;
|
|
54
|
+
escaped = false;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (char === "\\") {
|
|
59
|
+
escaped = true;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if ((char === '"' || char === "'") && !inQuotes) {
|
|
64
|
+
inQuotes = true;
|
|
65
|
+
quoteChar = char;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (char === quoteChar && inQuotes) {
|
|
70
|
+
inQuotes = false;
|
|
71
|
+
quoteChar = "";
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (char === " " && !inQuotes) {
|
|
76
|
+
if (current.length > 0) {
|
|
77
|
+
args.push(current);
|
|
78
|
+
current = "";
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
current += char;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add final argument if exists
|
|
87
|
+
if (current.length > 0) {
|
|
88
|
+
args.push(current);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return args;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Parse a slash command string into a SlashCommand object.
|
|
96
|
+
*
|
|
97
|
+
* @param input - The raw input string starting with /
|
|
98
|
+
* @returns Parsed SlashCommand object
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* parseSlashCommand('/help')
|
|
102
|
+
* // => { name: 'help', args: [], raw: '/help' }
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* parseSlashCommand('/search "hello world" --limit 10')
|
|
106
|
+
* // => { name: 'search', args: ['hello world', '--limit', '10'], raw: '...' }
|
|
107
|
+
*/
|
|
108
|
+
export function parseSlashCommand(input: string): SlashCommand {
|
|
109
|
+
const trimmed = input.trim();
|
|
110
|
+
|
|
111
|
+
// Remove leading slash
|
|
112
|
+
const withoutSlash = trimmed.slice(1);
|
|
113
|
+
|
|
114
|
+
// Find the command name (everything until first space)
|
|
115
|
+
const spaceIndex = withoutSlash.indexOf(" ");
|
|
116
|
+
|
|
117
|
+
if (spaceIndex === -1) {
|
|
118
|
+
// Command with no arguments
|
|
119
|
+
return {
|
|
120
|
+
name: withoutSlash,
|
|
121
|
+
args: [],
|
|
122
|
+
raw: trimmed,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const name = withoutSlash.slice(0, spaceIndex);
|
|
127
|
+
const argsString = withoutSlash.slice(spaceIndex + 1).trim();
|
|
128
|
+
const args = parseArguments(argsString);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
name,
|
|
132
|
+
args,
|
|
133
|
+
raw: trimmed,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout Component (T033)
|
|
3
|
+
*
|
|
4
|
+
* Main application layout with configurable regions:
|
|
5
|
+
* - Header (top)
|
|
6
|
+
* - Sidebar (optional, right)
|
|
7
|
+
* - Content (main area)
|
|
8
|
+
* - Footer (bottom)
|
|
9
|
+
*
|
|
10
|
+
* Supports compact mode for narrow terminals (< 80 columns).
|
|
11
|
+
*
|
|
12
|
+
* @module tui/components/Layout
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { Box, Text } from "ink";
|
|
16
|
+
import type React from "react";
|
|
17
|
+
import { useMemo } from "react";
|
|
18
|
+
import { useTerminalDimensions } from "../hooks/useTerminalSize.js";
|
|
19
|
+
import { getAlternateBufferEnabled } from "../i18n/settings-integration.js";
|
|
20
|
+
import { useTheme } from "../theme/index.js";
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Constants
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
/** Compact mode threshold in columns (lowered from 80 for better sidebar visibility) */
|
|
27
|
+
const COMPACT_THRESHOLD = 60;
|
|
28
|
+
|
|
29
|
+
/** Default sidebar width percentage - reduced from 25% to give more space to messages */
|
|
30
|
+
const SIDEBAR_WIDTH_PERCENT = 20;
|
|
31
|
+
|
|
32
|
+
/** Minimum sidebar width in columns - allow narrower for compact mode */
|
|
33
|
+
const SIDEBAR_MIN_WIDTH = 16;
|
|
34
|
+
|
|
35
|
+
/** Maximum sidebar width in columns - allow wider on large terminals */
|
|
36
|
+
const SIDEBAR_MAX_WIDTH = 60;
|
|
37
|
+
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// Types
|
|
40
|
+
// =============================================================================
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Props for the Layout component.
|
|
44
|
+
*/
|
|
45
|
+
export interface LayoutProps {
|
|
46
|
+
/** Header region content (top) */
|
|
47
|
+
readonly header?: React.ReactNode;
|
|
48
|
+
/** Footer region content (bottom) */
|
|
49
|
+
readonly footer?: React.ReactNode;
|
|
50
|
+
/** Sidebar region content (right) */
|
|
51
|
+
readonly sidebar?: React.ReactNode;
|
|
52
|
+
/** Main content area */
|
|
53
|
+
readonly children: React.ReactNode;
|
|
54
|
+
/** Whether to show the sidebar (default: true if sidebar provided) */
|
|
55
|
+
readonly showSidebar?: boolean;
|
|
56
|
+
/** Force compact mode or auto-detect based on terminal width */
|
|
57
|
+
readonly compactMode?: boolean;
|
|
58
|
+
/** Workspace name to show in header separator */
|
|
59
|
+
readonly workspace?: string;
|
|
60
|
+
/** Git branch to show in header separator */
|
|
61
|
+
readonly branch?: string;
|
|
62
|
+
/** Number of changed files to show in header separator */
|
|
63
|
+
readonly changedFiles?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// =============================================================================
|
|
67
|
+
// Hooks
|
|
68
|
+
// =============================================================================
|
|
69
|
+
|
|
70
|
+
// Terminal dimensions hook moved to hooks/useTerminalSize.ts
|
|
71
|
+
// Uses useTerminalDimensions from that module for resize handling
|
|
72
|
+
|
|
73
|
+
// =============================================================================
|
|
74
|
+
// Sub-Components
|
|
75
|
+
// =============================================================================
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Horizontal line separator with optional embedded info.
|
|
79
|
+
*/
|
|
80
|
+
interface SeparatorProps {
|
|
81
|
+
readonly color: string;
|
|
82
|
+
readonly style?: "single" | "double";
|
|
83
|
+
/** Workspace name to embed in the separator */
|
|
84
|
+
readonly workspace?: string;
|
|
85
|
+
/** Git branch to embed in the separator */
|
|
86
|
+
readonly branch?: string;
|
|
87
|
+
/** Number of changed files */
|
|
88
|
+
readonly changedFiles?: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Minimum line chars on each side of embedded info */
|
|
92
|
+
const MIN_LINE_PADDING = 4;
|
|
93
|
+
|
|
94
|
+
function Separator({
|
|
95
|
+
color,
|
|
96
|
+
style = "single",
|
|
97
|
+
workspace,
|
|
98
|
+
branch,
|
|
99
|
+
changedFiles,
|
|
100
|
+
}: SeparatorProps): React.JSX.Element {
|
|
101
|
+
const { width: columns } = useTerminalDimensions();
|
|
102
|
+
const char = style === "double" ? "═" : "─";
|
|
103
|
+
|
|
104
|
+
// If no embedded info, render simple line
|
|
105
|
+
if (!workspace && !branch) {
|
|
106
|
+
const line = char.repeat(columns);
|
|
107
|
+
return (
|
|
108
|
+
<Box width="100%">
|
|
109
|
+
<Text color={color} wrap="truncate-end">
|
|
110
|
+
{line}
|
|
111
|
+
</Text>
|
|
112
|
+
</Box>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Build embedded info string: "[ workspace | branch *N ]"
|
|
117
|
+
const parts: string[] = [];
|
|
118
|
+
if (workspace) {
|
|
119
|
+
parts.push(workspace);
|
|
120
|
+
}
|
|
121
|
+
if (branch) {
|
|
122
|
+
let branchPart = branch;
|
|
123
|
+
if (changedFiles && changedFiles > 0) {
|
|
124
|
+
branchPart += ` *${changedFiles}`;
|
|
125
|
+
}
|
|
126
|
+
parts.push(branchPart);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const infoText = parts.length > 0 ? `[ ${parts.join(" | ")} ]` : "";
|
|
130
|
+
const infoLength = infoText.length;
|
|
131
|
+
|
|
132
|
+
// Calculate available space for line chars
|
|
133
|
+
const availableSpace = columns - infoLength;
|
|
134
|
+
|
|
135
|
+
// If not enough space, fall back to simple line
|
|
136
|
+
if (availableSpace < MIN_LINE_PADDING * 2) {
|
|
137
|
+
const line = char.repeat(columns);
|
|
138
|
+
return (
|
|
139
|
+
<Box width="100%">
|
|
140
|
+
<Text color={color} wrap="truncate-end">
|
|
141
|
+
{line}
|
|
142
|
+
</Text>
|
|
143
|
+
</Box>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Distribute line chars: more on right side for visual balance
|
|
148
|
+
const leftPadding = MIN_LINE_PADDING;
|
|
149
|
+
const rightPadding = availableSpace - leftPadding;
|
|
150
|
+
|
|
151
|
+
const leftLine = char.repeat(leftPadding);
|
|
152
|
+
const rightLine = char.repeat(Math.max(0, rightPadding));
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<Box width="100%">
|
|
156
|
+
<Text color={color} wrap="truncate-end">
|
|
157
|
+
{leftLine}
|
|
158
|
+
{infoText}
|
|
159
|
+
{rightLine}
|
|
160
|
+
</Text>
|
|
161
|
+
</Box>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Header region of the layout.
|
|
167
|
+
* Borderless design with simple line separator below.
|
|
168
|
+
*/
|
|
169
|
+
interface HeaderRegionProps {
|
|
170
|
+
readonly children: React.ReactNode;
|
|
171
|
+
readonly borderColor: string;
|
|
172
|
+
/** Workspace name to show in separator */
|
|
173
|
+
readonly workspace?: string;
|
|
174
|
+
/** Git branch to show in separator */
|
|
175
|
+
readonly branch?: string;
|
|
176
|
+
/** Number of changed files */
|
|
177
|
+
readonly changedFiles?: number;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function HeaderRegion({
|
|
181
|
+
children,
|
|
182
|
+
borderColor,
|
|
183
|
+
workspace,
|
|
184
|
+
branch,
|
|
185
|
+
changedFiles,
|
|
186
|
+
}: HeaderRegionProps): React.JSX.Element {
|
|
187
|
+
return (
|
|
188
|
+
<Box flexDirection="column" width="100%">
|
|
189
|
+
<Box paddingX={1}>{children}</Box>
|
|
190
|
+
<Separator
|
|
191
|
+
color={borderColor}
|
|
192
|
+
workspace={workspace}
|
|
193
|
+
branch={branch}
|
|
194
|
+
changedFiles={changedFiles}
|
|
195
|
+
/>
|
|
196
|
+
</Box>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Footer region of the layout.
|
|
202
|
+
* Uses single-line separator above (double caused visual confusion with progress bars).
|
|
203
|
+
* Footer has flexShrink={0} to prevent compression.
|
|
204
|
+
*/
|
|
205
|
+
interface FooterRegionProps {
|
|
206
|
+
readonly children: React.ReactNode;
|
|
207
|
+
readonly borderColor: string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function FooterRegion({ children, borderColor }: FooterRegionProps): React.JSX.Element {
|
|
211
|
+
return (
|
|
212
|
+
<Box flexDirection="column" width="100%" flexShrink={0}>
|
|
213
|
+
<Separator color={borderColor} style="single" />
|
|
214
|
+
<Box paddingX={1}>{children}</Box>
|
|
215
|
+
</Box>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Sidebar region of the layout.
|
|
221
|
+
*/
|
|
222
|
+
interface SidebarRegionProps {
|
|
223
|
+
readonly children: React.ReactNode;
|
|
224
|
+
readonly width: number;
|
|
225
|
+
readonly borderColor: string;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function SidebarRegion({ children, width, borderColor }: SidebarRegionProps): React.JSX.Element {
|
|
229
|
+
return (
|
|
230
|
+
<Box
|
|
231
|
+
flexDirection="column"
|
|
232
|
+
flexBasis={width}
|
|
233
|
+
flexShrink={1}
|
|
234
|
+
flexGrow={0}
|
|
235
|
+
width={width}
|
|
236
|
+
minHeight={0}
|
|
237
|
+
borderStyle="single"
|
|
238
|
+
borderColor={borderColor}
|
|
239
|
+
borderLeft={true}
|
|
240
|
+
borderRight={false}
|
|
241
|
+
borderTop={false}
|
|
242
|
+
borderBottom={false}
|
|
243
|
+
paddingX={1}
|
|
244
|
+
overflow="hidden"
|
|
245
|
+
>
|
|
246
|
+
{children}
|
|
247
|
+
</Box>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Content region of the layout.
|
|
253
|
+
*/
|
|
254
|
+
interface ContentRegionProps {
|
|
255
|
+
readonly children: React.ReactNode;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function ContentRegion({ children }: ContentRegionProps): React.JSX.Element {
|
|
259
|
+
return (
|
|
260
|
+
<Box
|
|
261
|
+
flexDirection="column"
|
|
262
|
+
flexGrow={1}
|
|
263
|
+
flexShrink={1}
|
|
264
|
+
minHeight={0}
|
|
265
|
+
paddingX={1}
|
|
266
|
+
overflow="hidden"
|
|
267
|
+
>
|
|
268
|
+
{children}
|
|
269
|
+
</Box>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// =============================================================================
|
|
274
|
+
// Main Component
|
|
275
|
+
// =============================================================================
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Layout provides the main application structure with configurable regions.
|
|
279
|
+
*
|
|
280
|
+
* The layout structure:
|
|
281
|
+
* ```
|
|
282
|
+
* ┌─────────────────────────────┐
|
|
283
|
+
* │ Header │
|
|
284
|
+
* ├──────────────────┬──────────┤
|
|
285
|
+
* │ Content │ Sidebar │
|
|
286
|
+
* │ │ (opt) │
|
|
287
|
+
* ├──────────────────┴──────────┤
|
|
288
|
+
* │ Footer │
|
|
289
|
+
* └─────────────────────────────┘
|
|
290
|
+
* ```
|
|
291
|
+
*
|
|
292
|
+
* Features:
|
|
293
|
+
* - Three main regions: header, content, footer
|
|
294
|
+
* - Optional sidebar (right side, collapsible)
|
|
295
|
+
* - Auto-detect compact mode for narrow terminals
|
|
296
|
+
* - Theme-aware border colors
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```tsx
|
|
300
|
+
* // Basic layout with header and footer
|
|
301
|
+
* <Layout
|
|
302
|
+
* header={<Text>My App</Text>}
|
|
303
|
+
* footer={<StatusBar />}
|
|
304
|
+
* >
|
|
305
|
+
* <MainContent />
|
|
306
|
+
* </Layout>
|
|
307
|
+
*
|
|
308
|
+
* // Layout with sidebar
|
|
309
|
+
* <Layout
|
|
310
|
+
* header={<Header />}
|
|
311
|
+
* sidebar={<Navigation />}
|
|
312
|
+
* footer={<StatusBar />}
|
|
313
|
+
* showSidebar={true}
|
|
314
|
+
* >
|
|
315
|
+
* <MainContent />
|
|
316
|
+
* </Layout>
|
|
317
|
+
*
|
|
318
|
+
* // Force compact mode
|
|
319
|
+
* <Layout compactMode={true}>
|
|
320
|
+
* <Content />
|
|
321
|
+
* </Layout>
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
export function Layout({
|
|
325
|
+
header,
|
|
326
|
+
footer,
|
|
327
|
+
sidebar,
|
|
328
|
+
children,
|
|
329
|
+
showSidebar,
|
|
330
|
+
compactMode,
|
|
331
|
+
workspace,
|
|
332
|
+
branch,
|
|
333
|
+
changedFiles,
|
|
334
|
+
}: LayoutProps): React.JSX.Element {
|
|
335
|
+
const { theme } = useTheme();
|
|
336
|
+
const { width: columns, height: rows } = useTerminalDimensions();
|
|
337
|
+
|
|
338
|
+
// Determine if we should be in compact mode
|
|
339
|
+
const isCompact = useMemo(() => {
|
|
340
|
+
if (compactMode !== undefined) {
|
|
341
|
+
return compactMode;
|
|
342
|
+
}
|
|
343
|
+
return columns < COMPACT_THRESHOLD;
|
|
344
|
+
}, [compactMode, columns]);
|
|
345
|
+
|
|
346
|
+
// Determine if sidebar should be visible
|
|
347
|
+
const sidebarVisible = useMemo(() => {
|
|
348
|
+
// If explicitly set, use that value
|
|
349
|
+
if (showSidebar !== undefined) {
|
|
350
|
+
return showSidebar && !isCompact;
|
|
351
|
+
}
|
|
352
|
+
// Auto-show if sidebar content is provided and not in compact mode
|
|
353
|
+
return !!sidebar && !isCompact;
|
|
354
|
+
}, [showSidebar, sidebar, isCompact]);
|
|
355
|
+
|
|
356
|
+
// Calculate sidebar width based on terminal width - adaptive tiered approach
|
|
357
|
+
const sidebarWidth = useMemo(() => {
|
|
358
|
+
if (!sidebarVisible) {
|
|
359
|
+
return 0;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Adaptive calculation based on terminal size
|
|
363
|
+
const baseWidth = Math.floor(columns * (SIDEBAR_WIDTH_PERCENT / 100));
|
|
364
|
+
|
|
365
|
+
// Tiered approach: small terminals get smaller sidebar, large terminals get proportional
|
|
366
|
+
if (columns < 80) {
|
|
367
|
+
// Compact: minimum viable sidebar
|
|
368
|
+
return Math.max(baseWidth, SIDEBAR_MIN_WIDTH);
|
|
369
|
+
} else if (columns < 120) {
|
|
370
|
+
// Normal: standard percentage, cap at 35
|
|
371
|
+
return Math.min(baseWidth, 35);
|
|
372
|
+
} else {
|
|
373
|
+
// Wide: allow up to max, but keep percentage-based
|
|
374
|
+
return Math.min(baseWidth, SIDEBAR_MAX_WIDTH);
|
|
375
|
+
}
|
|
376
|
+
}, [sidebarVisible, columns]);
|
|
377
|
+
|
|
378
|
+
// Get border colors from theme - use focus color for header/footer, default for sidebar
|
|
379
|
+
const headerBorderColor = theme.colors.primary;
|
|
380
|
+
const footerBorderColor = theme.semantic.border.focus;
|
|
381
|
+
const sidebarBorderColor = theme.semantic.border.default;
|
|
382
|
+
|
|
383
|
+
// Constrain height for interactive TUI rendering to prevent scrollback duplication.
|
|
384
|
+
// Allow static output mode to grow naturally (e.g. debug snapshots/logs).
|
|
385
|
+
const isAlternateBuffer = getAlternateBufferEnabled();
|
|
386
|
+
const isStaticOutputMode = process.env.VELLUM_STATIC_OUTPUT === "1";
|
|
387
|
+
const shouldConstrainHeight =
|
|
388
|
+
!isStaticOutputMode && (isAlternateBuffer || (process.stdout.isTTY ?? false));
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<Box
|
|
392
|
+
flexDirection="column"
|
|
393
|
+
width="100%"
|
|
394
|
+
height={shouldConstrainHeight ? rows : undefined}
|
|
395
|
+
minHeight={shouldConstrainHeight ? undefined : rows}
|
|
396
|
+
>
|
|
397
|
+
{/* Header Region */}
|
|
398
|
+
{header && (
|
|
399
|
+
<HeaderRegion
|
|
400
|
+
borderColor={headerBorderColor}
|
|
401
|
+
workspace={workspace}
|
|
402
|
+
branch={branch}
|
|
403
|
+
changedFiles={changedFiles}
|
|
404
|
+
>
|
|
405
|
+
{header}
|
|
406
|
+
</HeaderRegion>
|
|
407
|
+
)}
|
|
408
|
+
|
|
409
|
+
{/* Middle Section: Content + Sidebar (sidebar on right) */}
|
|
410
|
+
<Box flexDirection="row" flexGrow={1} flexShrink={1} minHeight={0}>
|
|
411
|
+
{/* Content Region */}
|
|
412
|
+
<ContentRegion>{children}</ContentRegion>
|
|
413
|
+
|
|
414
|
+
{/* Sidebar Region (optional, on right) */}
|
|
415
|
+
{sidebarVisible && sidebar && (
|
|
416
|
+
<SidebarRegion width={sidebarWidth} borderColor={sidebarBorderColor}>
|
|
417
|
+
{sidebar}
|
|
418
|
+
</SidebarRegion>
|
|
419
|
+
)}
|
|
420
|
+
</Box>
|
|
421
|
+
|
|
422
|
+
{/* Footer Region */}
|
|
423
|
+
{footer && <FooterRegion borderColor={footerBorderColor}>{footer}</FooterRegion>}
|
|
424
|
+
</Box>
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Re-export hook for components that need terminal size.
|
|
430
|
+
* @deprecated Import directly from hooks/useTerminalSize.js instead
|
|
431
|
+
*/
|
|
432
|
+
export { useTerminalDimensions as useTerminalSize };
|