@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,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CheckpointPanel Component
|
|
3
|
+
*
|
|
4
|
+
* Modal panel for managing session checkpoints.
|
|
5
|
+
* Allows viewing, creating, and rolling back to checkpoints.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/session/CheckpointPanel
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { SessionCheckpoint } from "@vellum/core";
|
|
11
|
+
import { Box, Text, useInput } from "ink";
|
|
12
|
+
import type React from "react";
|
|
13
|
+
import { useCallback, useState } from "react";
|
|
14
|
+
import { useTUITranslation } from "../../i18n/index.js";
|
|
15
|
+
import { useTheme } from "../../theme/index.js";
|
|
16
|
+
import { truncateToDisplayWidth } from "../../utils/index.js";
|
|
17
|
+
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Types
|
|
20
|
+
// =============================================================================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Props for the CheckpointPanel component
|
|
24
|
+
*/
|
|
25
|
+
export interface CheckpointPanelProps {
|
|
26
|
+
/** List of checkpoints to display */
|
|
27
|
+
readonly checkpoints: readonly SessionCheckpoint[];
|
|
28
|
+
/** Current message count (for calculating messages to lose) */
|
|
29
|
+
readonly currentMessageCount: number;
|
|
30
|
+
/** Whether the panel is open */
|
|
31
|
+
readonly isOpen: boolean;
|
|
32
|
+
/** Callback when a checkpoint is selected for rollback */
|
|
33
|
+
readonly onRollback: (checkpointId: string) => void;
|
|
34
|
+
/** Callback when a new checkpoint is requested */
|
|
35
|
+
readonly onCreateCheckpoint: (description?: string) => void;
|
|
36
|
+
/** Callback when a checkpoint is deleted */
|
|
37
|
+
readonly onDeleteCheckpoint: (checkpointId: string) => void;
|
|
38
|
+
/** Callback to close the panel */
|
|
39
|
+
readonly onClose: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// Helper Functions
|
|
44
|
+
// =============================================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Format a date for display.
|
|
48
|
+
*/
|
|
49
|
+
function formatDate(date: Date): string {
|
|
50
|
+
const now = new Date();
|
|
51
|
+
const isToday =
|
|
52
|
+
date.getDate() === now.getDate() &&
|
|
53
|
+
date.getMonth() === now.getMonth() &&
|
|
54
|
+
date.getFullYear() === now.getFullYear();
|
|
55
|
+
|
|
56
|
+
if (isToday) {
|
|
57
|
+
return date.toLocaleTimeString(undefined, {
|
|
58
|
+
hour: "2-digit",
|
|
59
|
+
minute: "2-digit",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return date.toLocaleDateString(undefined, {
|
|
64
|
+
month: "short",
|
|
65
|
+
day: "numeric",
|
|
66
|
+
hour: "2-digit",
|
|
67
|
+
minute: "2-digit",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Truncate text with ellipsis.
|
|
73
|
+
* Uses string-width for accurate CJK/Emoji handling.
|
|
74
|
+
*/
|
|
75
|
+
function truncate(text: string, maxLength: number): string {
|
|
76
|
+
return truncateToDisplayWidth(text, maxLength);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// CheckpointItem Sub-Component
|
|
81
|
+
// =============================================================================
|
|
82
|
+
|
|
83
|
+
interface CheckpointItemProps {
|
|
84
|
+
readonly checkpoint: SessionCheckpoint;
|
|
85
|
+
readonly isSelected: boolean;
|
|
86
|
+
readonly messagesToLose: number;
|
|
87
|
+
readonly primaryColor: string;
|
|
88
|
+
readonly warningColor: string;
|
|
89
|
+
readonly textColor: string;
|
|
90
|
+
readonly mutedColor: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function CheckpointItem({
|
|
94
|
+
checkpoint,
|
|
95
|
+
isSelected,
|
|
96
|
+
messagesToLose,
|
|
97
|
+
primaryColor,
|
|
98
|
+
warningColor,
|
|
99
|
+
textColor,
|
|
100
|
+
mutedColor,
|
|
101
|
+
}: CheckpointItemProps): React.JSX.Element {
|
|
102
|
+
const indicator = isSelected ? "▶" : " ";
|
|
103
|
+
const description = checkpoint.description ?? "(no description)";
|
|
104
|
+
const idShort = checkpoint.id.slice(0, 8);
|
|
105
|
+
const dateStr = formatDate(new Date(checkpoint.createdAt));
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Box flexDirection="row" paddingX={1}>
|
|
109
|
+
<Text color={isSelected ? primaryColor : mutedColor}>{indicator} </Text>
|
|
110
|
+
<Box flexDirection="row" justifyContent="space-between" flexGrow={1}>
|
|
111
|
+
<Box flexDirection="row" gap={1}>
|
|
112
|
+
<Text color={isSelected ? primaryColor : mutedColor} dimColor={!isSelected}>
|
|
113
|
+
[{idShort}]
|
|
114
|
+
</Text>
|
|
115
|
+
<Text color={isSelected ? primaryColor : textColor} bold={isSelected}>
|
|
116
|
+
{truncate(description, 30)}
|
|
117
|
+
</Text>
|
|
118
|
+
</Box>
|
|
119
|
+
<Box flexDirection="row" gap={1}>
|
|
120
|
+
<Text color={mutedColor}>{dateStr}</Text>
|
|
121
|
+
<Text color={mutedColor}>|</Text>
|
|
122
|
+
<Text color={mutedColor}>msg #{checkpoint.messageIndex}</Text>
|
|
123
|
+
{messagesToLose > 0 && <Text color={warningColor}>(-{messagesToLose})</Text>}
|
|
124
|
+
</Box>
|
|
125
|
+
</Box>
|
|
126
|
+
</Box>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// =============================================================================
|
|
131
|
+
// Main Component
|
|
132
|
+
// =============================================================================
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* CheckpointPanel displays and manages session checkpoints.
|
|
136
|
+
*
|
|
137
|
+
* Keybindings:
|
|
138
|
+
* - j/k or ↑/↓: Navigate checkpoints
|
|
139
|
+
* - Enter: Rollback to selected checkpoint
|
|
140
|
+
* - n: Create new checkpoint
|
|
141
|
+
* - d: Delete selected checkpoint
|
|
142
|
+
* - Escape/q: Close panel
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```tsx
|
|
146
|
+
* <CheckpointPanel
|
|
147
|
+
* checkpoints={checkpoints}
|
|
148
|
+
* currentMessageCount={messages.length}
|
|
149
|
+
* isOpen={showPanel}
|
|
150
|
+
* onRollback={(id) => handleRollback(id)}
|
|
151
|
+
* onCreateCheckpoint={(desc) => handleCreate(desc)}
|
|
152
|
+
* onDeleteCheckpoint={(id) => handleDelete(id)}
|
|
153
|
+
* onClose={() => setShowPanel(false)}
|
|
154
|
+
* />
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export function CheckpointPanel({
|
|
158
|
+
checkpoints,
|
|
159
|
+
currentMessageCount,
|
|
160
|
+
isOpen,
|
|
161
|
+
onRollback,
|
|
162
|
+
onCreateCheckpoint,
|
|
163
|
+
onDeleteCheckpoint,
|
|
164
|
+
onClose,
|
|
165
|
+
}: CheckpointPanelProps): React.JSX.Element | null {
|
|
166
|
+
const { theme } = useTheme();
|
|
167
|
+
const { t } = useTUITranslation();
|
|
168
|
+
|
|
169
|
+
// Selection state
|
|
170
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
171
|
+
|
|
172
|
+
// Get colors from theme
|
|
173
|
+
const primaryColor = theme.brand.primary;
|
|
174
|
+
const warningColor = theme.colors.warning;
|
|
175
|
+
const textColor = theme.semantic.text.primary;
|
|
176
|
+
const mutedColor = theme.semantic.text.muted;
|
|
177
|
+
const borderColor = theme.semantic.border.default;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Calculate messages that would be lost for a checkpoint.
|
|
181
|
+
*/
|
|
182
|
+
const getMessagesToLose = useCallback(
|
|
183
|
+
(checkpoint: SessionCheckpoint): number => {
|
|
184
|
+
return currentMessageCount - checkpoint.messageIndex;
|
|
185
|
+
},
|
|
186
|
+
[currentMessageCount]
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Handle navigation keys (j/k, arrows)
|
|
191
|
+
*/
|
|
192
|
+
const handleNavigation = useCallback(
|
|
193
|
+
(input: string, key: { downArrow: boolean; upArrow: boolean }): boolean => {
|
|
194
|
+
if (input === "j" || key.downArrow) {
|
|
195
|
+
setSelectedIndex((prev) => Math.min(prev + 1, checkpoints.length - 1));
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
if (input === "k" || key.upArrow) {
|
|
199
|
+
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
},
|
|
204
|
+
[checkpoints.length]
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Handle action keys (Enter, n, d)
|
|
209
|
+
*/
|
|
210
|
+
const handleAction = useCallback(
|
|
211
|
+
(input: string, key: { return: boolean }): boolean => {
|
|
212
|
+
if (key.return && checkpoints.length > 0) {
|
|
213
|
+
const checkpoint = checkpoints[selectedIndex];
|
|
214
|
+
if (checkpoint) {
|
|
215
|
+
onRollback(checkpoint.id);
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
if (input === "n") {
|
|
220
|
+
onCreateCheckpoint();
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
if (input === "d" && checkpoints.length > 0) {
|
|
224
|
+
const checkpoint = checkpoints[selectedIndex];
|
|
225
|
+
if (checkpoint) {
|
|
226
|
+
onDeleteCheckpoint(checkpoint.id);
|
|
227
|
+
if (selectedIndex >= checkpoints.length - 1) {
|
|
228
|
+
setSelectedIndex(Math.max(0, checkpoints.length - 2));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
},
|
|
235
|
+
[checkpoints, selectedIndex, onRollback, onCreateCheckpoint, onDeleteCheckpoint]
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Handle keyboard input.
|
|
240
|
+
*/
|
|
241
|
+
useInput(
|
|
242
|
+
(input, key) => {
|
|
243
|
+
if (handleNavigation(input, key)) return;
|
|
244
|
+
if (handleAction(input, key)) return;
|
|
245
|
+
if (key.escape || input === "q") {
|
|
246
|
+
onClose();
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
{ isActive: isOpen }
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
if (!isOpen) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const isEmpty = checkpoints.length === 0;
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<Box
|
|
260
|
+
flexDirection="column"
|
|
261
|
+
borderStyle="double"
|
|
262
|
+
borderColor={borderColor}
|
|
263
|
+
paddingX={1}
|
|
264
|
+
paddingY={0}
|
|
265
|
+
>
|
|
266
|
+
{/* Header */}
|
|
267
|
+
<Box flexDirection="row" justifyContent="space-between" marginBottom={1}>
|
|
268
|
+
<Text bold color={primaryColor}>
|
|
269
|
+
📌 {t("persistence.checkpoint.title")}
|
|
270
|
+
</Text>
|
|
271
|
+
<Text dimColor>j/k navigate • Enter rollback • n new • d delete • Esc close</Text>
|
|
272
|
+
</Box>
|
|
273
|
+
|
|
274
|
+
{/* Checkpoint List */}
|
|
275
|
+
{isEmpty ? (
|
|
276
|
+
<Box paddingX={1} paddingY={1}>
|
|
277
|
+
<Text color={mutedColor}>{t("persistence.checkpoint.empty")}</Text>
|
|
278
|
+
</Box>
|
|
279
|
+
) : (
|
|
280
|
+
<Box flexDirection="column">
|
|
281
|
+
{checkpoints.map((checkpoint, index) => (
|
|
282
|
+
<CheckpointItem
|
|
283
|
+
key={checkpoint.id}
|
|
284
|
+
checkpoint={checkpoint}
|
|
285
|
+
isSelected={index === selectedIndex}
|
|
286
|
+
messagesToLose={getMessagesToLose(checkpoint)}
|
|
287
|
+
primaryColor={primaryColor}
|
|
288
|
+
warningColor={warningColor}
|
|
289
|
+
textColor={textColor}
|
|
290
|
+
mutedColor={mutedColor}
|
|
291
|
+
/>
|
|
292
|
+
))}
|
|
293
|
+
</Box>
|
|
294
|
+
)}
|
|
295
|
+
|
|
296
|
+
{/* Footer - Warning for selected checkpoint */}
|
|
297
|
+
{!isEmpty &&
|
|
298
|
+
(() => {
|
|
299
|
+
const selectedCheckpoint = checkpoints[selectedIndex];
|
|
300
|
+
if (!selectedCheckpoint) return null;
|
|
301
|
+
const messagesToLose = getMessagesToLose(selectedCheckpoint);
|
|
302
|
+
return (
|
|
303
|
+
<Box marginTop={1} paddingX={1}>
|
|
304
|
+
{messagesToLose > 0 ? (
|
|
305
|
+
<Text color={warningColor}>
|
|
306
|
+
⚠ Rollback will remove {messagesToLose} message
|
|
307
|
+
{messagesToLose === 1 ? "" : "s"}
|
|
308
|
+
</Text>
|
|
309
|
+
) : (
|
|
310
|
+
<Text color={mutedColor}>Already at this checkpoint</Text>
|
|
311
|
+
)}
|
|
312
|
+
</Box>
|
|
313
|
+
);
|
|
314
|
+
})()}
|
|
315
|
+
</Box>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// =============================================================================
|
|
320
|
+
// Exports
|
|
321
|
+
// =============================================================================
|
|
322
|
+
|
|
323
|
+
export default CheckpointPanel;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RollbackDialog Component
|
|
3
|
+
*
|
|
4
|
+
* Modal confirmation dialog for session rollback operations.
|
|
5
|
+
* Displays a warning about data loss and requires user confirmation.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/session/RollbackDialog
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { SessionCheckpoint } from "@vellum/core";
|
|
11
|
+
import { Box, Text, useInput } from "ink";
|
|
12
|
+
import type React from "react";
|
|
13
|
+
import { useTUITranslation } from "../../i18n/index.js";
|
|
14
|
+
import { useTheme } from "../../theme/index.js";
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Types
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Props for the RollbackDialog component
|
|
22
|
+
*/
|
|
23
|
+
export interface RollbackDialogProps {
|
|
24
|
+
/** Checkpoint to rollback to */
|
|
25
|
+
readonly checkpoint: SessionCheckpoint | null;
|
|
26
|
+
/** Number of messages that will be lost */
|
|
27
|
+
readonly messagesToLose: number;
|
|
28
|
+
/** Whether the dialog is open */
|
|
29
|
+
readonly isOpen: boolean;
|
|
30
|
+
/** Callback when user confirms rollback */
|
|
31
|
+
readonly onConfirm: () => void;
|
|
32
|
+
/** Callback when user cancels */
|
|
33
|
+
readonly onCancel: () => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// =============================================================================
|
|
37
|
+
// Main Component
|
|
38
|
+
// =============================================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* RollbackDialog displays a confirmation prompt for rollback operations.
|
|
42
|
+
*
|
|
43
|
+
* Features:
|
|
44
|
+
* - Shows checkpoint details
|
|
45
|
+
* - Displays warning about message loss
|
|
46
|
+
* - y/n confirmation
|
|
47
|
+
* - Red warning styling
|
|
48
|
+
*
|
|
49
|
+
* Keybindings:
|
|
50
|
+
* - y/Y/Enter: Confirm rollback
|
|
51
|
+
* - n/N/Escape: Cancel
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* <RollbackDialog
|
|
56
|
+
* checkpoint={selectedCheckpoint}
|
|
57
|
+
* messagesToLose={5}
|
|
58
|
+
* isOpen={showDialog}
|
|
59
|
+
* onConfirm={() => {
|
|
60
|
+
* handleRollback();
|
|
61
|
+
* setShowDialog(false);
|
|
62
|
+
* }}
|
|
63
|
+
* onCancel={() => setShowDialog(false)}
|
|
64
|
+
* />
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export function RollbackDialog({
|
|
68
|
+
checkpoint,
|
|
69
|
+
messagesToLose,
|
|
70
|
+
isOpen,
|
|
71
|
+
onConfirm,
|
|
72
|
+
onCancel,
|
|
73
|
+
}: RollbackDialogProps): React.JSX.Element | null {
|
|
74
|
+
const { theme } = useTheme();
|
|
75
|
+
const { t } = useTUITranslation();
|
|
76
|
+
|
|
77
|
+
// Get colors from theme
|
|
78
|
+
const errorColor = theme.colors.error;
|
|
79
|
+
const warningColor = theme.colors.warning;
|
|
80
|
+
const successColor = theme.colors.success;
|
|
81
|
+
const textColor = theme.semantic.text.primary;
|
|
82
|
+
const mutedColor = theme.semantic.text.muted;
|
|
83
|
+
const borderColor = errorColor; // Use error color for border to emphasize danger
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Handle keyboard input
|
|
87
|
+
*/
|
|
88
|
+
useInput(
|
|
89
|
+
(input, key) => {
|
|
90
|
+
// Confirm: y, Y, or Enter
|
|
91
|
+
if (input === "y" || input === "Y" || key.return) {
|
|
92
|
+
onConfirm();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Cancel: n, N, or Escape
|
|
97
|
+
if (input === "n" || input === "N" || key.escape) {
|
|
98
|
+
onCancel();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{ isActive: isOpen }
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
if (!isOpen || !checkpoint) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const checkpointDescription = checkpoint.description ?? "(no description)";
|
|
110
|
+
const checkpointIdShort = checkpoint.id.slice(0, 8);
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<Box
|
|
114
|
+
flexDirection="column"
|
|
115
|
+
borderStyle="double"
|
|
116
|
+
borderColor={borderColor}
|
|
117
|
+
paddingX={2}
|
|
118
|
+
paddingY={1}
|
|
119
|
+
>
|
|
120
|
+
{/* Title */}
|
|
121
|
+
<Box marginBottom={1}>
|
|
122
|
+
<Text bold color={errorColor}>
|
|
123
|
+
⚠️ {t("persistence.rollback.confirm")}
|
|
124
|
+
</Text>
|
|
125
|
+
</Box>
|
|
126
|
+
|
|
127
|
+
{/* Checkpoint Info */}
|
|
128
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
129
|
+
<Text color={textColor}>
|
|
130
|
+
Checkpoint: <Text color={mutedColor}>[{checkpointIdShort}]</Text>{" "}
|
|
131
|
+
<Text bold>{checkpointDescription}</Text>
|
|
132
|
+
</Text>
|
|
133
|
+
<Text color={mutedColor}>Message index: {checkpoint.messageIndex}</Text>
|
|
134
|
+
</Box>
|
|
135
|
+
|
|
136
|
+
{/* Warning Message */}
|
|
137
|
+
<Box marginBottom={1} paddingX={1}>
|
|
138
|
+
<Text color={warningColor} bold>
|
|
139
|
+
{t("persistence.rollback.warning", { count: messagesToLose })}
|
|
140
|
+
</Text>
|
|
141
|
+
</Box>
|
|
142
|
+
|
|
143
|
+
{/* Emphasis on irreversibility */}
|
|
144
|
+
<Box marginBottom={1}>
|
|
145
|
+
<Text color={errorColor} dimColor>
|
|
146
|
+
This action cannot be undone.
|
|
147
|
+
</Text>
|
|
148
|
+
</Box>
|
|
149
|
+
|
|
150
|
+
{/* Action Buttons */}
|
|
151
|
+
<Box flexDirection="row" gap={2}>
|
|
152
|
+
<Box>
|
|
153
|
+
<Text color={successColor}>[y]</Text>
|
|
154
|
+
<Text color={textColor}> Confirm</Text>
|
|
155
|
+
</Box>
|
|
156
|
+
<Box>
|
|
157
|
+
<Text color={errorColor}>[n]</Text>
|
|
158
|
+
<Text color={textColor}> Cancel</Text>
|
|
159
|
+
</Box>
|
|
160
|
+
</Box>
|
|
161
|
+
</Box>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// =============================================================================
|
|
166
|
+
// Exports
|
|
167
|
+
// =============================================================================
|
|
168
|
+
|
|
169
|
+
export default RollbackDialog;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionItem Component (T056)
|
|
3
|
+
*
|
|
4
|
+
* Displays a single session in the session list with title,
|
|
5
|
+
* last message preview, and timestamp.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/components/session/SessionItem
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Box, Text } from "ink";
|
|
11
|
+
import type React from "react";
|
|
12
|
+
import { useTheme } from "../../theme/index.js";
|
|
13
|
+
import { truncateToDisplayWidth } from "../../utils/index.js";
|
|
14
|
+
import type { SessionItemProps } from "./types.js";
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Helper Functions
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Format a timestamp for display.
|
|
22
|
+
* Shows time if today, otherwise shows date.
|
|
23
|
+
*/
|
|
24
|
+
function formatTimestamp(date: Date): string {
|
|
25
|
+
const now = new Date();
|
|
26
|
+
const isToday =
|
|
27
|
+
date.getDate() === now.getDate() &&
|
|
28
|
+
date.getMonth() === now.getMonth() &&
|
|
29
|
+
date.getFullYear() === now.getFullYear();
|
|
30
|
+
|
|
31
|
+
if (isToday) {
|
|
32
|
+
return date.toLocaleTimeString(undefined, {
|
|
33
|
+
hour: "2-digit",
|
|
34
|
+
minute: "2-digit",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return date.toLocaleDateString(undefined, {
|
|
39
|
+
month: "short",
|
|
40
|
+
day: "numeric",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Truncate text to a maximum display width with ellipsis.
|
|
46
|
+
* Uses string-width for accurate CJK/Emoji handling.
|
|
47
|
+
*/
|
|
48
|
+
function truncateText(text: string, maxLength: number): string {
|
|
49
|
+
return truncateToDisplayWidth(text, maxLength);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// Main Component
|
|
54
|
+
// =============================================================================
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* SessionItem displays a single session entry.
|
|
58
|
+
*
|
|
59
|
+
* Features:
|
|
60
|
+
* - Shows session title with truncation
|
|
61
|
+
* - Displays last message preview (muted)
|
|
62
|
+
* - Shows relative timestamp
|
|
63
|
+
* - Visual indicators for selected/active states
|
|
64
|
+
* - Message count badge
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```tsx
|
|
68
|
+
* <SessionItem
|
|
69
|
+
* session={{
|
|
70
|
+
* id: "sess-1",
|
|
71
|
+
* title: "Debug React app",
|
|
72
|
+
* lastMessage: "I'll help you fix that...",
|
|
73
|
+
* timestamp: new Date(),
|
|
74
|
+
* messageCount: 12
|
|
75
|
+
* }}
|
|
76
|
+
* isSelected={true}
|
|
77
|
+
* isActive={false}
|
|
78
|
+
* onSelect={(id) => handleSelect(id)}
|
|
79
|
+
* />
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export function SessionItem({
|
|
83
|
+
session,
|
|
84
|
+
isSelected = false,
|
|
85
|
+
isActive = false,
|
|
86
|
+
}: SessionItemProps): React.JSX.Element {
|
|
87
|
+
const { theme } = useTheme();
|
|
88
|
+
|
|
89
|
+
const textColor = theme.semantic.text.primary;
|
|
90
|
+
const mutedColor = theme.semantic.text.muted;
|
|
91
|
+
const primaryColor = theme.colors.primary;
|
|
92
|
+
const successColor = theme.colors.success;
|
|
93
|
+
|
|
94
|
+
// Determine background indicator
|
|
95
|
+
const indicator = isSelected ? "▶" : isActive ? "●" : " ";
|
|
96
|
+
const indicatorColor = isSelected ? primaryColor : isActive ? successColor : mutedColor;
|
|
97
|
+
|
|
98
|
+
// Truncate title and last message for display
|
|
99
|
+
const displayTitle = truncateText(session.title, 40);
|
|
100
|
+
const displayMessage = session.lastMessage ? truncateText(session.lastMessage, 50) : "";
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<Box
|
|
104
|
+
flexDirection="column"
|
|
105
|
+
paddingX={1}
|
|
106
|
+
paddingY={0}
|
|
107
|
+
borderStyle={isSelected ? "single" : undefined}
|
|
108
|
+
borderColor={isSelected ? primaryColor : undefined}
|
|
109
|
+
>
|
|
110
|
+
{/* Title row with indicator and timestamp */}
|
|
111
|
+
<Box flexDirection="row" justifyContent="space-between">
|
|
112
|
+
<Box flexDirection="row" gap={1}>
|
|
113
|
+
<Text color={indicatorColor}>{indicator}</Text>
|
|
114
|
+
<Text color={isSelected ? primaryColor : textColor} bold={isSelected || isActive}>
|
|
115
|
+
{displayTitle}
|
|
116
|
+
</Text>
|
|
117
|
+
</Box>
|
|
118
|
+
<Box flexDirection="row" gap={1}>
|
|
119
|
+
<Text dimColor>({session.messageCount})</Text>
|
|
120
|
+
<Text color={mutedColor}>{formatTimestamp(session.timestamp)}</Text>
|
|
121
|
+
</Box>
|
|
122
|
+
</Box>
|
|
123
|
+
|
|
124
|
+
{/* Last message preview */}
|
|
125
|
+
{displayMessage && (
|
|
126
|
+
<Box marginLeft={2}>
|
|
127
|
+
<Text color={mutedColor} wrap="truncate">
|
|
128
|
+
{displayMessage}
|
|
129
|
+
</Text>
|
|
130
|
+
</Box>
|
|
131
|
+
)}
|
|
132
|
+
</Box>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default SessionItem;
|