@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,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Height Estimation Utilities (T002)
|
|
3
|
+
*
|
|
4
|
+
* Functions for estimating rendered line heights of text and messages.
|
|
5
|
+
* Used by virtualization and adaptive rendering systems.
|
|
6
|
+
*
|
|
7
|
+
* CRITICAL: Height estimates MUST be UPPER BOUNDS (never under-estimate).
|
|
8
|
+
* Under-estimation causes terminal overflow and UI "sinking" bugs.
|
|
9
|
+
*
|
|
10
|
+
* @module tui/utils/heightEstimator
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import stringWidth from "string-width";
|
|
14
|
+
import type { Message } from "../context/MessagesContext.js";
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Constants - Upper Bound Safety Margins
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Safety margin added to all height estimates to prevent overflow.
|
|
22
|
+
* Accounts for borders, padding, and rendering variations.
|
|
23
|
+
*/
|
|
24
|
+
export const HEIGHT_SAFETY_MARGIN = 2;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Upper bound lines per tool call.
|
|
28
|
+
* Tool calls render as inline status indicators (Gemini-style):
|
|
29
|
+
* - Status icon + tool name + optional error = typically 1-2 lines
|
|
30
|
+
* Previous value of 10 was too conservative for inline rendering.
|
|
31
|
+
*/
|
|
32
|
+
export const TOOL_CALL_UPPER_BOUND = 3;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Upper bound lines per tool call when a diff preview is shown.
|
|
36
|
+
* Accounts for status line + margin + MaxSizedBox diff preview (12 lines).
|
|
37
|
+
*/
|
|
38
|
+
export const TOOL_CALL_DIFF_UPPER_BOUND = 14;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Upper bound for thinking block header and chrome.
|
|
42
|
+
* Includes collapsible header, borders, and internal padding.
|
|
43
|
+
*/
|
|
44
|
+
export const THINKING_HEADER_UPPER_BOUND = 4;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Default estimated item height for virtualization.
|
|
48
|
+
* Used when no specific estimate is available.
|
|
49
|
+
* Increased from 4 to prevent overflow from under-estimation.
|
|
50
|
+
*/
|
|
51
|
+
export const DEFAULT_ESTIMATED_ITEM_HEIGHT = 12;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Minimum message height (header + minimal content + margin).
|
|
55
|
+
*/
|
|
56
|
+
export const MIN_MESSAGE_HEIGHT = 4;
|
|
57
|
+
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// Types
|
|
60
|
+
// =============================================================================
|
|
61
|
+
|
|
62
|
+
interface DiffMetadata {
|
|
63
|
+
readonly diff: string;
|
|
64
|
+
readonly additions: number;
|
|
65
|
+
readonly deletions: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
type ToolCallInfo = NonNullable<Message["toolCalls"]>[number];
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Options for message height estimation.
|
|
72
|
+
*/
|
|
73
|
+
export interface HeightEstimatorOptions {
|
|
74
|
+
/** Terminal width for line wrapping calculation */
|
|
75
|
+
readonly width: number;
|
|
76
|
+
/** Whether to include tool calls in height calculation (default: true) */
|
|
77
|
+
readonly includeToolCalls?: boolean;
|
|
78
|
+
/** Padding to subtract from width for content area (default: 4) */
|
|
79
|
+
readonly contentPadding?: number;
|
|
80
|
+
/** Padding to subtract for thinking blocks (default: 6) */
|
|
81
|
+
readonly thinkingPadding?: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// =============================================================================
|
|
85
|
+
// Functions
|
|
86
|
+
// =============================================================================
|
|
87
|
+
|
|
88
|
+
function isDiffMetadata(value: unknown): value is DiffMetadata {
|
|
89
|
+
if (!value || typeof value !== "object") {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const obj = value as Record<string, unknown>;
|
|
93
|
+
return (
|
|
94
|
+
typeof obj.diff === "string" &&
|
|
95
|
+
typeof obj.additions === "number" &&
|
|
96
|
+
typeof obj.deletions === "number"
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getDiffMetadata(result: unknown): DiffMetadata | null {
|
|
101
|
+
if (isDiffMetadata(result)) {
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
if (!result || typeof result !== "object") {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const diffMeta = (result as Record<string, unknown>).diffMeta;
|
|
108
|
+
return isDiffMetadata(diffMeta) ? diffMeta : null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function getToolCallUpperBound(toolCall: ToolCallInfo): number {
|
|
112
|
+
const diffMeta = getDiffMetadata(toolCall.result);
|
|
113
|
+
const hasDiffContent =
|
|
114
|
+
toolCall.status === "completed" &&
|
|
115
|
+
diffMeta !== null &&
|
|
116
|
+
diffMeta.diff.trim() !== "" &&
|
|
117
|
+
diffMeta.diff !== "(no changes)";
|
|
118
|
+
return hasDiffContent ? TOOL_CALL_DIFF_UPPER_BOUND : TOOL_CALL_UPPER_BOUND;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Estimate the number of lines a text will occupy when wrapped.
|
|
123
|
+
* Uses string-width for accurate CJK/Emoji/ANSI handling.
|
|
124
|
+
*
|
|
125
|
+
* @param text - The text to measure
|
|
126
|
+
* @param width - The available terminal width
|
|
127
|
+
* @returns Estimated number of lines after wrapping
|
|
128
|
+
*/
|
|
129
|
+
export function estimateWrappedLineCount(text: string, width: number): number {
|
|
130
|
+
const safeWidth = Math.max(1, width);
|
|
131
|
+
if (!text) {
|
|
132
|
+
return 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let total = 0;
|
|
136
|
+
const lines = text.split("\n");
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
// Use string-width for accurate display width calculation
|
|
139
|
+
// This correctly handles CJK characters (2 cells), Emoji, and ANSI escape codes
|
|
140
|
+
const lineWidth = stringWidth(line);
|
|
141
|
+
if (lineWidth === 0) {
|
|
142
|
+
total += 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
total += Math.max(1, Math.ceil(lineWidth / safeWidth));
|
|
146
|
+
}
|
|
147
|
+
return total;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Estimate a message height in lines for virtualized scrolling and layout.
|
|
152
|
+
* Uses UPPER BOUND estimation to prevent terminal overflow.
|
|
153
|
+
*
|
|
154
|
+
* @param message - The message to measure
|
|
155
|
+
* @param options - Height estimation options
|
|
156
|
+
* @returns Estimated height in lines (conservative upper bound)
|
|
157
|
+
*/
|
|
158
|
+
export function estimateMessageHeight(message: Message, options: HeightEstimatorOptions): number {
|
|
159
|
+
const { width, includeToolCalls = true, contentPadding = 4, thinkingPadding = 6 } = options;
|
|
160
|
+
|
|
161
|
+
// Use 2 lines for header (role + timestamp) to account for wrapping
|
|
162
|
+
const headerLines = 2;
|
|
163
|
+
// Margin between messages
|
|
164
|
+
const marginLines = 2;
|
|
165
|
+
const contentWidth = Math.max(10, width - contentPadding);
|
|
166
|
+
const thinkingWidth = Math.max(10, width - thinkingPadding);
|
|
167
|
+
const toolCalls = message.toolCalls ?? [];
|
|
168
|
+
|
|
169
|
+
// Tool group messages have special handling
|
|
170
|
+
if (message.role === "tool_group") {
|
|
171
|
+
const toolLines = toolCalls.reduce((sum, call) => sum + getToolCallUpperBound(call), 0);
|
|
172
|
+
// Each tool in a group can expand to multiple lines
|
|
173
|
+
return Math.max(MIN_MESSAGE_HEIGHT, toolLines) + marginLines;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const content = message.content || (message.isStreaming ? "" : "(empty)");
|
|
177
|
+
let lines = headerLines;
|
|
178
|
+
|
|
179
|
+
lines += estimateWrappedLineCount(content, contentWidth);
|
|
180
|
+
|
|
181
|
+
// Add thinking block lines if present (with upper bound for chrome)
|
|
182
|
+
if (message.thinking && message.thinking.length > 0) {
|
|
183
|
+
lines += THINKING_HEADER_UPPER_BOUND; // Collapsible header + borders
|
|
184
|
+
lines += estimateWrappedLineCount(message.thinking, thinkingWidth);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Add tool call lines if enabled and present (UPPER BOUND per tool)
|
|
188
|
+
if (includeToolCalls && toolCalls.length > 0) {
|
|
189
|
+
lines += 1; // separator before tool calls (margin already counted)
|
|
190
|
+
lines += toolCalls.reduce((sum, call) => sum + getToolCallUpperBound(call), 0);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// FIX: Simplified height calculation to avoid double-counting margins
|
|
194
|
+
// Previous version added marginLines (2) + HEIGHT_SAFETY_MARGIN (2) = 4 extra lines
|
|
195
|
+
// which caused over-estimation and blank spaces in virtualized list
|
|
196
|
+
// Now we only add a single safety buffer
|
|
197
|
+
return Math.max(MIN_MESSAGE_HEIGHT, lines + HEIGHT_SAFETY_MARGIN);
|
|
198
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utility functions for the Vellum CLI terminal UI.
|
|
5
|
+
*
|
|
6
|
+
* @module tui/utils
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
disableBracketedPaste,
|
|
11
|
+
enableBracketedPaste,
|
|
12
|
+
extractPasteContent,
|
|
13
|
+
hasPasteEnd,
|
|
14
|
+
hasPasteStart,
|
|
15
|
+
PASTE_END,
|
|
16
|
+
PASTE_START,
|
|
17
|
+
} from "./bracketedPaste.js";
|
|
18
|
+
|
|
19
|
+
export { CursorManager, type CursorManagerImpl } from "./cursor-manager.js";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
clearTerminalCapabilitiesCache,
|
|
23
|
+
createHyperlink,
|
|
24
|
+
type DetectTerminalOptions,
|
|
25
|
+
degradeColor,
|
|
26
|
+
detectTerminal,
|
|
27
|
+
getSymbol,
|
|
28
|
+
getTerminalCapabilities,
|
|
29
|
+
getTerminalName,
|
|
30
|
+
type TerminalCapabilities,
|
|
31
|
+
type TerminalType,
|
|
32
|
+
} from "./detectTerminal.js";
|
|
33
|
+
|
|
34
|
+
export { findLastSafeSplitPoint } from "./findLastSafeSplitPoint.js";
|
|
35
|
+
// Height estimation utilities (T002)
|
|
36
|
+
export {
|
|
37
|
+
// Constants for upper-bound estimation
|
|
38
|
+
DEFAULT_ESTIMATED_ITEM_HEIGHT,
|
|
39
|
+
// Functions
|
|
40
|
+
estimateMessageHeight,
|
|
41
|
+
estimateWrappedLineCount,
|
|
42
|
+
HEIGHT_SAFETY_MARGIN,
|
|
43
|
+
type HeightEstimatorOptions,
|
|
44
|
+
MIN_MESSAGE_HEIGHT,
|
|
45
|
+
THINKING_HEADER_UPPER_BOUND,
|
|
46
|
+
TOOL_CALL_UPPER_BOUND,
|
|
47
|
+
} from "./heightEstimator.js";
|
|
48
|
+
// Narrow width detection
|
|
49
|
+
export { getNarrowBreakpoint, isNarrowWidth } from "./isNarrowWidth.js";
|
|
50
|
+
// Stdout guard for debugging (T002 Hardening)
|
|
51
|
+
export {
|
|
52
|
+
disableStdoutGuard,
|
|
53
|
+
enableStdoutGuard,
|
|
54
|
+
isStdoutGuardActive,
|
|
55
|
+
} from "./stdoutGuard.js";
|
|
56
|
+
export { isSyncUpdateSupported, syncUpdate } from "./synchronized-update.js";
|
|
57
|
+
// Text width utilities (CJK/emoji aware)
|
|
58
|
+
export {
|
|
59
|
+
countLines,
|
|
60
|
+
getVisualWidth,
|
|
61
|
+
padToWidth,
|
|
62
|
+
splitLines,
|
|
63
|
+
type TextAlign,
|
|
64
|
+
truncateToWidth,
|
|
65
|
+
wrapToWidth,
|
|
66
|
+
} from "./text-width.js";
|
|
67
|
+
// Text sanitization utilities (Phase 1 - World-Class TUI)
|
|
68
|
+
export {
|
|
69
|
+
type SanitizeOptions,
|
|
70
|
+
sanitize,
|
|
71
|
+
sanitizeAnsi,
|
|
72
|
+
sanitizeText,
|
|
73
|
+
} from "./textSanitizer.js";
|
|
74
|
+
// Text utilities (T002 Hardening)
|
|
75
|
+
export { hardWrap, truncateToDisplayWidth } from "./textUtils.js";
|
|
76
|
+
// Terminal UI sizing utilities
|
|
77
|
+
export {
|
|
78
|
+
clamp,
|
|
79
|
+
DEFAULT_TERMINAL_HEIGHT,
|
|
80
|
+
DEFAULT_TERMINAL_WIDTH,
|
|
81
|
+
getContentPadding,
|
|
82
|
+
getMaxContentWidth,
|
|
83
|
+
getTerminalHeight,
|
|
84
|
+
getTerminalSize,
|
|
85
|
+
getTerminalWidth,
|
|
86
|
+
lerp,
|
|
87
|
+
NARROW_CONTENT_RATIO,
|
|
88
|
+
NARROW_WIDTH_BREAKPOINT,
|
|
89
|
+
WIDE_CONTENT_RATIO,
|
|
90
|
+
WIDE_WIDTH_BREAKPOINT,
|
|
91
|
+
} from "./ui-sizing.js";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow Width Detection Utility
|
|
3
|
+
*
|
|
4
|
+
* Simple utility for detecting narrow terminal widths.
|
|
5
|
+
* Used for responsive layout decisions throughout the TUI.
|
|
6
|
+
*
|
|
7
|
+
* @module tui/utils/isNarrowWidth
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getTerminalWidth, NARROW_WIDTH_BREAKPOINT } from "./ui-sizing.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get the narrow width breakpoint constant.
|
|
14
|
+
*
|
|
15
|
+
* @returns The narrow width breakpoint (80 columns)
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const breakpoint = getNarrowBreakpoint();
|
|
20
|
+
* console.log(`Narrow mode below ${breakpoint} columns`);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function getNarrowBreakpoint(): number {
|
|
24
|
+
return NARROW_WIDTH_BREAKPOINT;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if the terminal is in narrow width mode.
|
|
29
|
+
*
|
|
30
|
+
* Narrow mode is defined as terminal width <= 80 columns.
|
|
31
|
+
* In narrow mode, UI elements should be more compact and
|
|
32
|
+
* non-essential elements may be hidden.
|
|
33
|
+
*
|
|
34
|
+
* @param width - Terminal width to check (defaults to current terminal)
|
|
35
|
+
* @returns True if terminal is narrow (<= 80 columns)
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* if (isNarrowWidth()) {
|
|
40
|
+
* // Render compact layout
|
|
41
|
+
* } else {
|
|
42
|
+
* // Render full layout with sidebar
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* // Or with explicit width
|
|
46
|
+
* const narrow = isNarrowWidth(60); // true
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function isNarrowWidth(width?: number): boolean {
|
|
50
|
+
const terminalWidth = width ?? getTerminalWidth();
|
|
51
|
+
return terminalWidth <= NARROW_WIDTH_BREAKPOINT;
|
|
52
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stdout Guard Utilities (T002 Hardening)
|
|
3
|
+
*
|
|
4
|
+
* Development-time utilities for detecting unexpected stdout writes
|
|
5
|
+
* that bypass Ink's rendering and could cause terminal overflow.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This is for DEBUGGING ONLY. It must NOT block writes
|
|
8
|
+
* or break Ink's stdout handling.
|
|
9
|
+
*
|
|
10
|
+
* @module tui/utils/stdoutGuard
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Store the original stdout.write
|
|
14
|
+
let originalStdoutWrite: typeof process.stdout.write | null = null;
|
|
15
|
+
let guardActive = false;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Enable stdout guard for debugging non-Ink writes.
|
|
19
|
+
*
|
|
20
|
+
* When enabled (via VELLUM_DEBUG_STDOUT env var), this will log
|
|
21
|
+
* any writes to stdout that don't appear to come from Ink.
|
|
22
|
+
*
|
|
23
|
+
* SAFETY: All writes are allowed through - this only logs warnings.
|
|
24
|
+
*/
|
|
25
|
+
export function enableStdoutGuard(): void {
|
|
26
|
+
// Only enable in debug mode
|
|
27
|
+
if (!process.env.VELLUM_DEBUG_STDOUT) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (guardActive) {
|
|
32
|
+
return; // Already active
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
36
|
+
guardActive = true;
|
|
37
|
+
|
|
38
|
+
const guardedWrite = function (
|
|
39
|
+
this: NodeJS.WriteStream,
|
|
40
|
+
chunk: string | Uint8Array,
|
|
41
|
+
encodingOrCallback?: BufferEncoding | ((err?: Error | null) => void),
|
|
42
|
+
callback?: (err?: Error | null) => void
|
|
43
|
+
): boolean {
|
|
44
|
+
// Try to detect if this is an Ink write by checking the stack
|
|
45
|
+
const stack = new Error().stack ?? "";
|
|
46
|
+
const isInkWrite =
|
|
47
|
+
stack.includes("ink") ||
|
|
48
|
+
stack.includes("render") ||
|
|
49
|
+
stack.includes("reconciler") ||
|
|
50
|
+
stack.includes("yoga");
|
|
51
|
+
|
|
52
|
+
if (!isInkWrite) {
|
|
53
|
+
const chunkStr = typeof chunk === "string" ? chunk : chunk.toString();
|
|
54
|
+
const preview = chunkStr.substring(0, 80).replace(/\n/g, "\\n");
|
|
55
|
+
// Log to stderr to avoid recursion
|
|
56
|
+
process.stderr.write(`[STDOUT GUARD] Non-Ink write detected: "${preview}"\n`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ALWAYS allow the write through - never block
|
|
60
|
+
if (originalStdoutWrite) {
|
|
61
|
+
if (typeof encodingOrCallback === "function") {
|
|
62
|
+
return originalStdoutWrite(chunk, encodingOrCallback);
|
|
63
|
+
}
|
|
64
|
+
return originalStdoutWrite(chunk, encodingOrCallback, callback);
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
process.stdout.write = guardedWrite as typeof process.stdout.write;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Disable the stdout guard and restore original stdout.write.
|
|
74
|
+
*/
|
|
75
|
+
export function disableStdoutGuard(): void {
|
|
76
|
+
if (!guardActive || !originalStdoutWrite) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
process.stdout.write = originalStdoutWrite;
|
|
81
|
+
originalStdoutWrite = null;
|
|
82
|
+
guardActive = false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if the stdout guard is currently active.
|
|
87
|
+
*/
|
|
88
|
+
export function isStdoutGuardActive(): boolean {
|
|
89
|
+
return guardActive;
|
|
90
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synchronized Update - DEC 2026 protocol
|
|
3
|
+
*
|
|
4
|
+
* Prevents terminal flickering by batching output.
|
|
5
|
+
* Supported in VS Code Terminal 1.85+, iTerm2, Kitty
|
|
6
|
+
*
|
|
7
|
+
* @module tui/utils/synchronized-update
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getActiveStdout } from "../buffered-stdout.js";
|
|
11
|
+
|
|
12
|
+
const BEGIN_SYNC = "\x1b[?2026h";
|
|
13
|
+
const END_SYNC = "\x1b[?2026l";
|
|
14
|
+
|
|
15
|
+
let syncDepth = 0;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Execute a function with synchronized terminal updates.
|
|
19
|
+
* All output within the callback will be batched and rendered atomically.
|
|
20
|
+
*
|
|
21
|
+
* @param fn - The function to execute within synchronized updates
|
|
22
|
+
* @returns The return value of the function
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* syncUpdate(() => {
|
|
27
|
+
* process.stdout.write('Line 1\n');
|
|
28
|
+
* process.stdout.write('Line 2\n');
|
|
29
|
+
* });
|
|
30
|
+
* // Both lines rendered atomically without flicker
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function syncUpdate<T>(fn: () => T): T {
|
|
34
|
+
if (syncDepth === 0) {
|
|
35
|
+
getActiveStdout().write(BEGIN_SYNC);
|
|
36
|
+
}
|
|
37
|
+
syncDepth++;
|
|
38
|
+
try {
|
|
39
|
+
return fn();
|
|
40
|
+
} finally {
|
|
41
|
+
syncDepth--;
|
|
42
|
+
if (syncDepth === 0) {
|
|
43
|
+
getActiveStdout().write(END_SYNC);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if synchronized updates are supported by the current terminal.
|
|
50
|
+
*
|
|
51
|
+
* @returns true if DEC 2026 protocol is supported
|
|
52
|
+
*/
|
|
53
|
+
export function isSyncUpdateSupported(): boolean {
|
|
54
|
+
// VS Code 1.85+, iTerm2, Kitty support DEC 2026
|
|
55
|
+
const term = process.env.TERM_PROGRAM?.toLowerCase() ?? "";
|
|
56
|
+
const termVersion = process.env.TERM_PROGRAM_VERSION ?? "";
|
|
57
|
+
|
|
58
|
+
if (term === "vscode") {
|
|
59
|
+
// VS Code 1.85.0 added sync update support
|
|
60
|
+
const [major = 0] = termVersion.split(".").map(Number);
|
|
61
|
+
return major >= 1; // Most recent VS Code versions support it
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (term === "iterm.app" || term === "kitty") {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check for explicit support via COLORTERM
|
|
69
|
+
return process.env.COLORTERM === "truecolor";
|
|
70
|
+
}
|