@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.
Files changed (446) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +411 -0
  3. package/__fixtures__/responses/code-generation.json +42 -0
  4. package/__fixtures__/responses/error-response.json +20 -0
  5. package/__fixtures__/responses/hello-world.json +32 -0
  6. package/dist/auth-6MCXESOH.js +26 -0
  7. package/dist/chunk-SECXJGWA.js +597 -0
  8. package/dist/index.js +34023 -0
  9. package/package.json +67 -0
  10. package/src/__tests__/commands.e2e.test.ts +728 -0
  11. package/src/__tests__/credentials.test.ts +713 -0
  12. package/src/__tests__/mode-e2e.test.ts +391 -0
  13. package/src/__tests__/tui-integration.test.tsx +1271 -0
  14. package/src/agents/__tests__/task-persistence.test.ts +235 -0
  15. package/src/agents/commands/delegate.ts +240 -0
  16. package/src/agents/commands/index.ts +10 -0
  17. package/src/agents/commands/resume.ts +335 -0
  18. package/src/agents/index.ts +29 -0
  19. package/src/agents/task-persistence.ts +272 -0
  20. package/src/agents/task-resumption.ts +242 -0
  21. package/src/app.tsx +4737 -0
  22. package/src/commands/__tests__/.gitkeep +1 -0
  23. package/src/commands/__tests__/agents.test.ts +606 -0
  24. package/src/commands/__tests__/auth.test.ts +626 -0
  25. package/src/commands/__tests__/autocomplete.test.ts +683 -0
  26. package/src/commands/__tests__/batch.test.ts +287 -0
  27. package/src/commands/__tests__/chain-pipe-parser.test.ts +654 -0
  28. package/src/commands/__tests__/completion.test.ts +238 -0
  29. package/src/commands/__tests__/core.test.ts +363 -0
  30. package/src/commands/__tests__/executor.test.ts +496 -0
  31. package/src/commands/__tests__/exit-codes.test.ts +220 -0
  32. package/src/commands/__tests__/init.test.ts +243 -0
  33. package/src/commands/__tests__/language.test.ts +353 -0
  34. package/src/commands/__tests__/mode-cli.test.ts +667 -0
  35. package/src/commands/__tests__/model.test.ts +277 -0
  36. package/src/commands/__tests__/parser.test.ts +493 -0
  37. package/src/commands/__tests__/performance.bench.ts +380 -0
  38. package/src/commands/__tests__/registry.test.ts +534 -0
  39. package/src/commands/__tests__/resume.test.ts +449 -0
  40. package/src/commands/__tests__/security.test.ts +845 -0
  41. package/src/commands/__tests__/stream-json.test.ts +372 -0
  42. package/src/commands/__tests__/user-commands.test.ts +597 -0
  43. package/src/commands/adapters.ts +267 -0
  44. package/src/commands/agent.ts +395 -0
  45. package/src/commands/agents/generate.ts +506 -0
  46. package/src/commands/agents/index.ts +272 -0
  47. package/src/commands/agents/show.ts +271 -0
  48. package/src/commands/agents/validate.ts +387 -0
  49. package/src/commands/auth.ts +883 -0
  50. package/src/commands/autocomplete.ts +480 -0
  51. package/src/commands/batch/command.ts +388 -0
  52. package/src/commands/batch/executor.ts +361 -0
  53. package/src/commands/batch/index.ts +12 -0
  54. package/src/commands/commit.ts +235 -0
  55. package/src/commands/completion/index.ts +371 -0
  56. package/src/commands/condense.ts +191 -0
  57. package/src/commands/config.ts +344 -0
  58. package/src/commands/context-provider.ts +173 -0
  59. package/src/commands/copy.ts +329 -0
  60. package/src/commands/core/clear.ts +38 -0
  61. package/src/commands/core/exit.ts +43 -0
  62. package/src/commands/core/help.ts +354 -0
  63. package/src/commands/core/index.ts +15 -0
  64. package/src/commands/cost.ts +179 -0
  65. package/src/commands/credentials.tsx +618 -0
  66. package/src/commands/custom-agents/__tests__/custom-agents.test.ts +709 -0
  67. package/src/commands/custom-agents/create.ts +377 -0
  68. package/src/commands/custom-agents/export.ts +135 -0
  69. package/src/commands/custom-agents/import.ts +199 -0
  70. package/src/commands/custom-agents/index.ts +372 -0
  71. package/src/commands/custom-agents/info.ts +318 -0
  72. package/src/commands/custom-agents/list.ts +267 -0
  73. package/src/commands/custom-agents/validate.ts +388 -0
  74. package/src/commands/diff-mode.ts +241 -0
  75. package/src/commands/env.ts +53 -0
  76. package/src/commands/executor.ts +579 -0
  77. package/src/commands/exit-codes.ts +202 -0
  78. package/src/commands/index.ts +701 -0
  79. package/src/commands/init/index.ts +15 -0
  80. package/src/commands/init/prompts.ts +366 -0
  81. package/src/commands/init/templates/commands-readme.md +80 -0
  82. package/src/commands/init/templates/example-command.md +79 -0
  83. package/src/commands/init/templates/example-skill.md +168 -0
  84. package/src/commands/init/templates/example-workflow.md +101 -0
  85. package/src/commands/init/templates/prompts-readme.md +52 -0
  86. package/src/commands/init/templates/rules-readme.md +63 -0
  87. package/src/commands/init/templates/skills-readme.md +83 -0
  88. package/src/commands/init/templates/workflows-readme.md +94 -0
  89. package/src/commands/init.ts +391 -0
  90. package/src/commands/install.ts +90 -0
  91. package/src/commands/language.ts +191 -0
  92. package/src/commands/loaders/.gitkeep +1 -0
  93. package/src/commands/lsp.ts +199 -0
  94. package/src/commands/markdown-commands.ts +253 -0
  95. package/src/commands/mcp.ts +588 -0
  96. package/src/commands/memory/export.ts +341 -0
  97. package/src/commands/memory/index.ts +148 -0
  98. package/src/commands/memory/list.ts +261 -0
  99. package/src/commands/memory/search.ts +346 -0
  100. package/src/commands/memory/utils.ts +15 -0
  101. package/src/commands/metrics.ts +75 -0
  102. package/src/commands/migrate/index.ts +16 -0
  103. package/src/commands/migrate/prompts.ts +477 -0
  104. package/src/commands/mode.ts +331 -0
  105. package/src/commands/model.ts +298 -0
  106. package/src/commands/onboard.ts +205 -0
  107. package/src/commands/open.ts +169 -0
  108. package/src/commands/output/stream-json.ts +373 -0
  109. package/src/commands/parser/chain-parser.ts +370 -0
  110. package/src/commands/parser/index.ts +29 -0
  111. package/src/commands/parser/pipe-parser.ts +480 -0
  112. package/src/commands/parser.ts +588 -0
  113. package/src/commands/persistence.ts +355 -0
  114. package/src/commands/progress.ts +18 -0
  115. package/src/commands/prompt/index.ts +17 -0
  116. package/src/commands/prompt/validate.ts +621 -0
  117. package/src/commands/prompt-priority.ts +401 -0
  118. package/src/commands/registry.ts +374 -0
  119. package/src/commands/sandbox/index.ts +131 -0
  120. package/src/commands/security/index.ts +21 -0
  121. package/src/commands/security/input-sanitizer.ts +168 -0
  122. package/src/commands/security/permission-checker.ts +456 -0
  123. package/src/commands/security/sensitive-data.ts +350 -0
  124. package/src/commands/session/delete.ts +38 -0
  125. package/src/commands/session/export.ts +39 -0
  126. package/src/commands/session/index.ts +26 -0
  127. package/src/commands/session/list.ts +26 -0
  128. package/src/commands/session/resume.ts +562 -0
  129. package/src/commands/session/search.ts +434 -0
  130. package/src/commands/session/show.ts +26 -0
  131. package/src/commands/settings.ts +368 -0
  132. package/src/commands/setup.ts +23 -0
  133. package/src/commands/shell/index.ts +16 -0
  134. package/src/commands/shell/setup.ts +422 -0
  135. package/src/commands/shell-init.ts +50 -0
  136. package/src/commands/shell-integration/index.ts +194 -0
  137. package/src/commands/skill.ts +1220 -0
  138. package/src/commands/spec.ts +558 -0
  139. package/src/commands/status.ts +246 -0
  140. package/src/commands/theme.ts +211 -0
  141. package/src/commands/think.ts +551 -0
  142. package/src/commands/trust.ts +211 -0
  143. package/src/commands/tutorial.ts +522 -0
  144. package/src/commands/types.ts +512 -0
  145. package/src/commands/update.ts +274 -0
  146. package/src/commands/usage.ts +213 -0
  147. package/src/commands/user-commands.ts +630 -0
  148. package/src/commands/utils.ts +142 -0
  149. package/src/commands/vim.ts +152 -0
  150. package/src/commands/workflow.ts +257 -0
  151. package/src/components/header.tsx +25 -0
  152. package/src/components/input.tsx +25 -0
  153. package/src/components/message-list.tsx +32 -0
  154. package/src/components/status-bar.tsx +23 -0
  155. package/src/index.tsx +614 -0
  156. package/src/onboarding/__tests__/tutorial.test.ts +740 -0
  157. package/src/onboarding/index.ts +69 -0
  158. package/src/onboarding/tips/index.ts +9 -0
  159. package/src/onboarding/tips/tip-engine.ts +459 -0
  160. package/src/onboarding/tutorial/index.ts +88 -0
  161. package/src/onboarding/tutorial/lessons/basics.ts +151 -0
  162. package/src/onboarding/tutorial/lessons/index.ts +151 -0
  163. package/src/onboarding/tutorial/lessons/modes.ts +230 -0
  164. package/src/onboarding/tutorial/lessons/tools.ts +172 -0
  165. package/src/onboarding/tutorial/progress-tracker.ts +350 -0
  166. package/src/onboarding/tutorial/storage.ts +249 -0
  167. package/src/onboarding/tutorial/tutorial-system.ts +462 -0
  168. package/src/onboarding/tutorial/types.ts +310 -0
  169. package/src/orchestrator-singleton.ts +129 -0
  170. package/src/shutdown.ts +33 -0
  171. package/src/test/e2e/assertions.ts +267 -0
  172. package/src/test/e2e/fixtures.ts +204 -0
  173. package/src/test/e2e/harness.ts +575 -0
  174. package/src/test/e2e/index.ts +57 -0
  175. package/src/test/e2e/types.ts +228 -0
  176. package/src/test/fixtures/__tests__/fake-response-loader.test.ts +314 -0
  177. package/src/test/fixtures/fake-response-loader.ts +314 -0
  178. package/src/test/fixtures/index.ts +20 -0
  179. package/src/tui/__tests__/mcp-panel.test.tsx +82 -0
  180. package/src/tui/__tests__/mcp-wiring.test.tsx +78 -0
  181. package/src/tui/__tests__/mode-components.test.tsx +395 -0
  182. package/src/tui/__tests__/permission-ask-flow.test.tsx +138 -0
  183. package/src/tui/__tests__/sidebar-panel-data.test.tsx +148 -0
  184. package/src/tui/__tests__/tools-panel-hotkeys.test.tsx +41 -0
  185. package/src/tui/adapters/agent-adapter.ts +1008 -0
  186. package/src/tui/adapters/index.ts +48 -0
  187. package/src/tui/adapters/message-adapter.ts +315 -0
  188. package/src/tui/adapters/persistence-bridge.ts +331 -0
  189. package/src/tui/adapters/session-adapter.ts +419 -0
  190. package/src/tui/buffered-stdout.ts +223 -0
  191. package/src/tui/components/AgentProgress.tsx +424 -0
  192. package/src/tui/components/Banner/AsciiArt.ts +160 -0
  193. package/src/tui/components/Banner/Banner.tsx +355 -0
  194. package/src/tui/components/Banner/ShimmerContext.tsx +131 -0
  195. package/src/tui/components/Banner/ShimmerText.tsx +193 -0
  196. package/src/tui/components/Banner/TypeWriterGradient.tsx +321 -0
  197. package/src/tui/components/Banner/index.ts +61 -0
  198. package/src/tui/components/Banner/useShimmer.ts +241 -0
  199. package/src/tui/components/ChatView.tsx +11 -0
  200. package/src/tui/components/Checkpoint/CheckpointDiffView.tsx +371 -0
  201. package/src/tui/components/Checkpoint/SnapshotCheckpointPanel.tsx +440 -0
  202. package/src/tui/components/Checkpoint/index.ts +19 -0
  203. package/src/tui/components/CostDisplay.tsx +226 -0
  204. package/src/tui/components/InitErrorBanner.tsx +122 -0
  205. package/src/tui/components/Input/Autocomplete.tsx +603 -0
  206. package/src/tui/components/Input/EnhancedCommandInput.tsx +471 -0
  207. package/src/tui/components/Input/HighlightedText.tsx +236 -0
  208. package/src/tui/components/Input/MentionAutocomplete.tsx +375 -0
  209. package/src/tui/components/Input/TextInput.tsx +1002 -0
  210. package/src/tui/components/Input/__tests__/Autocomplete.test.tsx +374 -0
  211. package/src/tui/components/Input/__tests__/TextInput.test.tsx +241 -0
  212. package/src/tui/components/Input/__tests__/highlight.test.ts +219 -0
  213. package/src/tui/components/Input/__tests__/slash-command-utils.test.ts +104 -0
  214. package/src/tui/components/Input/highlight.ts +362 -0
  215. package/src/tui/components/Input/index.ts +36 -0
  216. package/src/tui/components/Input/slash-command-utils.ts +135 -0
  217. package/src/tui/components/Layout.tsx +432 -0
  218. package/src/tui/components/McpPanel.tsx +137 -0
  219. package/src/tui/components/MemoryPanel.tsx +448 -0
  220. package/src/tui/components/Messages/CodeBlock.tsx +527 -0
  221. package/src/tui/components/Messages/DiffView.tsx +679 -0
  222. package/src/tui/components/Messages/ImageReference.tsx +89 -0
  223. package/src/tui/components/Messages/MarkdownBlock.tsx +228 -0
  224. package/src/tui/components/Messages/MarkdownRenderer.tsx +498 -0
  225. package/src/tui/components/Messages/MessageBubble.tsx +270 -0
  226. package/src/tui/components/Messages/MessageList.tsx +1719 -0
  227. package/src/tui/components/Messages/StreamingText.tsx +216 -0
  228. package/src/tui/components/Messages/ThinkingBlock.tsx +408 -0
  229. package/src/tui/components/Messages/ToolResultPreview.tsx +243 -0
  230. package/src/tui/components/Messages/__tests__/CodeBlock.test.tsx +296 -0
  231. package/src/tui/components/Messages/__tests__/DiffView.test.tsx +239 -0
  232. package/src/tui/components/Messages/__tests__/MarkdownRenderer.test.tsx +303 -0
  233. package/src/tui/components/Messages/__tests__/MessageBubble.test.tsx +268 -0
  234. package/src/tui/components/Messages/__tests__/MessageList.test.tsx +324 -0
  235. package/src/tui/components/Messages/__tests__/StreamingText.test.tsx +215 -0
  236. package/src/tui/components/Messages/index.ts +25 -0
  237. package/src/tui/components/ModeIndicator.tsx +177 -0
  238. package/src/tui/components/ModeSelector.tsx +216 -0
  239. package/src/tui/components/ModelSelector.tsx +339 -0
  240. package/src/tui/components/OnboardingWizard.tsx +670 -0
  241. package/src/tui/components/PhaseProgressIndicator.tsx +270 -0
  242. package/src/tui/components/RateLimitIndicator.tsx +82 -0
  243. package/src/tui/components/ScreenReaderLayout.tsx +295 -0
  244. package/src/tui/components/SettingsPanel.tsx +643 -0
  245. package/src/tui/components/Sidebar/SystemStatusPanel.tsx +284 -0
  246. package/src/tui/components/Sidebar/index.ts +9 -0
  247. package/src/tui/components/Status/ModelStatusBar.tsx +270 -0
  248. package/src/tui/components/Status/index.ts +12 -0
  249. package/src/tui/components/StatusBar/AgentModeIndicator.tsx +257 -0
  250. package/src/tui/components/StatusBar/ContextProgress.tsx +167 -0
  251. package/src/tui/components/StatusBar/FileChangesIndicator.tsx +62 -0
  252. package/src/tui/components/StatusBar/GitIndicator.tsx +89 -0
  253. package/src/tui/components/StatusBar/HeaderBar.tsx +126 -0
  254. package/src/tui/components/StatusBar/ModelIndicator.tsx +157 -0
  255. package/src/tui/components/StatusBar/PersistenceStatusIndicator.tsx +210 -0
  256. package/src/tui/components/StatusBar/ResilienceIndicator.tsx +106 -0
  257. package/src/tui/components/StatusBar/SandboxIndicator.tsx +167 -0
  258. package/src/tui/components/StatusBar/StatusBar.tsx +368 -0
  259. package/src/tui/components/StatusBar/ThinkingModeIndicator.tsx +170 -0
  260. package/src/tui/components/StatusBar/TokenBreakdown.tsx +246 -0
  261. package/src/tui/components/StatusBar/TokenCounter.tsx +135 -0
  262. package/src/tui/components/StatusBar/TrustModeIndicator.tsx +130 -0
  263. package/src/tui/components/StatusBar/WorkspaceIndicator.tsx +86 -0
  264. package/src/tui/components/StatusBar/__tests__/AgentModeIndicator.test.tsx +193 -0
  265. package/src/tui/components/StatusBar/__tests__/StatusBar.test.tsx +729 -0
  266. package/src/tui/components/StatusBar/index.ts +60 -0
  267. package/src/tui/components/TipBanner.tsx +115 -0
  268. package/src/tui/components/TodoItem.tsx +208 -0
  269. package/src/tui/components/TodoPanel.tsx +455 -0
  270. package/src/tui/components/Tools/ApprovalQueue.tsx +407 -0
  271. package/src/tui/components/Tools/OptionSelector.tsx +160 -0
  272. package/src/tui/components/Tools/PermissionDialog.tsx +286 -0
  273. package/src/tui/components/Tools/ToolParams.tsx +483 -0
  274. package/src/tui/components/Tools/ToolsPanel.tsx +178 -0
  275. package/src/tui/components/Tools/__tests__/PermissionDialog.test.tsx +510 -0
  276. package/src/tui/components/Tools/__tests__/ToolParams.test.tsx +432 -0
  277. package/src/tui/components/Tools/index.ts +21 -0
  278. package/src/tui/components/TrustPrompt.tsx +279 -0
  279. package/src/tui/components/UpdateBanner.tsx +166 -0
  280. package/src/tui/components/VimModeIndicator.tsx +112 -0
  281. package/src/tui/components/backtrack/BacktrackControls.tsx +402 -0
  282. package/src/tui/components/backtrack/index.ts +13 -0
  283. package/src/tui/components/common/AutoApprovalStatus.tsx +251 -0
  284. package/src/tui/components/common/CostWarning.tsx +294 -0
  285. package/src/tui/components/common/DynamicShortcutHints.tsx +209 -0
  286. package/src/tui/components/common/EnhancedLoadingIndicator.tsx +305 -0
  287. package/src/tui/components/common/ErrorBoundary.tsx +140 -0
  288. package/src/tui/components/common/GradientText.tsx +224 -0
  289. package/src/tui/components/common/HotkeyHelpModal.tsx +193 -0
  290. package/src/tui/components/common/HotkeyHints.tsx +70 -0
  291. package/src/tui/components/common/MaxSizedBox.tsx +354 -0
  292. package/src/tui/components/common/NewMessagesBadge.tsx +65 -0
  293. package/src/tui/components/common/ProtectedFileLegend.tsx +89 -0
  294. package/src/tui/components/common/ScrollIndicator.tsx +160 -0
  295. package/src/tui/components/common/Spinner.tsx +342 -0
  296. package/src/tui/components/common/StreamingIndicator.tsx +316 -0
  297. package/src/tui/components/common/VirtualizedList/VirtualizedList.tsx +428 -0
  298. package/src/tui/components/common/VirtualizedList/hooks/index.ts +19 -0
  299. package/src/tui/components/common/VirtualizedList/hooks/useBatchedScroll.ts +64 -0
  300. package/src/tui/components/common/VirtualizedList/hooks/useScrollAnchor.ts +290 -0
  301. package/src/tui/components/common/VirtualizedList/hooks/useVirtualization.ts +340 -0
  302. package/src/tui/components/common/VirtualizedList/index.ts +30 -0
  303. package/src/tui/components/common/VirtualizedList/types.ts +107 -0
  304. package/src/tui/components/common/__tests__/NewMessagesBadge.test.tsx +74 -0
  305. package/src/tui/components/common/__tests__/ScrollIndicator.test.tsx +193 -0
  306. package/src/tui/components/common/index.ts +110 -0
  307. package/src/tui/components/index.ts +79 -0
  308. package/src/tui/components/session/CheckpointPanel.tsx +323 -0
  309. package/src/tui/components/session/RollbackDialog.tsx +169 -0
  310. package/src/tui/components/session/SessionItem.tsx +136 -0
  311. package/src/tui/components/session/SessionListPanel.tsx +252 -0
  312. package/src/tui/components/session/SessionPicker.tsx +449 -0
  313. package/src/tui/components/session/SessionPreview.tsx +240 -0
  314. package/src/tui/components/session/__tests__/session.test.tsx +408 -0
  315. package/src/tui/components/session/index.ts +28 -0
  316. package/src/tui/components/session/types.ts +116 -0
  317. package/src/tui/components/theme/__tests__/tokens.test.ts +471 -0
  318. package/src/tui/components/theme/index.ts +227 -0
  319. package/src/tui/components/theme/tokens.ts +484 -0
  320. package/src/tui/config/defaults.ts +134 -0
  321. package/src/tui/config/index.ts +17 -0
  322. package/src/tui/context/AnimationContext.tsx +284 -0
  323. package/src/tui/context/AppContext.tsx +349 -0
  324. package/src/tui/context/BracketedPasteContext.tsx +372 -0
  325. package/src/tui/context/LspContext.tsx +192 -0
  326. package/src/tui/context/McpContext.tsx +325 -0
  327. package/src/tui/context/MessagesContext.tsx +870 -0
  328. package/src/tui/context/OverflowContext.tsx +213 -0
  329. package/src/tui/context/RateLimitContext.tsx +108 -0
  330. package/src/tui/context/ResilienceContext.tsx +275 -0
  331. package/src/tui/context/RootProvider.tsx +136 -0
  332. package/src/tui/context/ScrollContext.tsx +331 -0
  333. package/src/tui/context/ToolsContext.tsx +702 -0
  334. package/src/tui/context/__tests__/BracketedPasteContext.test.tsx +416 -0
  335. package/src/tui/context/index.ts +140 -0
  336. package/src/tui/enterprise-integration.ts +282 -0
  337. package/src/tui/hooks/__tests__/useBacktrack.test.tsx +138 -0
  338. package/src/tui/hooks/__tests__/useBracketedPaste.test.tsx +222 -0
  339. package/src/tui/hooks/__tests__/useCopyMode.test.tsx +336 -0
  340. package/src/tui/hooks/__tests__/useHotkeys.ctrl-input.test.tsx +96 -0
  341. package/src/tui/hooks/__tests__/useHotkeys.test.tsx +454 -0
  342. package/src/tui/hooks/__tests__/useInputHistory.test.tsx +660 -0
  343. package/src/tui/hooks/__tests__/useLineBuffer.test.ts +295 -0
  344. package/src/tui/hooks/__tests__/useModeController.test.ts +137 -0
  345. package/src/tui/hooks/__tests__/useModeShortcuts.test.tsx +142 -0
  346. package/src/tui/hooks/__tests__/useScrollController.test.ts +464 -0
  347. package/src/tui/hooks/__tests__/useVim.test.tsx +531 -0
  348. package/src/tui/hooks/index.ts +252 -0
  349. package/src/tui/hooks/useAgentLoop.ts +712 -0
  350. package/src/tui/hooks/useAlternateBuffer.ts +398 -0
  351. package/src/tui/hooks/useAnimatedScrollbar.ts +241 -0
  352. package/src/tui/hooks/useBacktrack.ts +443 -0
  353. package/src/tui/hooks/useBracketedPaste.ts +104 -0
  354. package/src/tui/hooks/useCollapsible.ts +240 -0
  355. package/src/tui/hooks/useCopyMode.ts +382 -0
  356. package/src/tui/hooks/useCostSummary.ts +75 -0
  357. package/src/tui/hooks/useDesktopNotification.ts +414 -0
  358. package/src/tui/hooks/useDiffMode.ts +44 -0
  359. package/src/tui/hooks/useFileChangeStats.ts +110 -0
  360. package/src/tui/hooks/useFileSuggestions.ts +284 -0
  361. package/src/tui/hooks/useFlickerDetector.ts +250 -0
  362. package/src/tui/hooks/useGitStatus.ts +200 -0
  363. package/src/tui/hooks/useHotkeys.ts +579 -0
  364. package/src/tui/hooks/useImagePaste.ts +114 -0
  365. package/src/tui/hooks/useInputHighlight.ts +145 -0
  366. package/src/tui/hooks/useInputHistory.ts +246 -0
  367. package/src/tui/hooks/useKeyboardScroll.ts +209 -0
  368. package/src/tui/hooks/useLineBuffer.ts +356 -0
  369. package/src/tui/hooks/useMentionAutocomplete.ts +235 -0
  370. package/src/tui/hooks/useModeController.ts +167 -0
  371. package/src/tui/hooks/useModeShortcuts.ts +196 -0
  372. package/src/tui/hooks/usePermissionHandler.ts +146 -0
  373. package/src/tui/hooks/usePersistence.ts +480 -0
  374. package/src/tui/hooks/usePersistenceShortcuts.ts +225 -0
  375. package/src/tui/hooks/usePlaceholderRotation.ts +143 -0
  376. package/src/tui/hooks/useProviderStatus.ts +270 -0
  377. package/src/tui/hooks/useRateLimitStatus.ts +90 -0
  378. package/src/tui/hooks/useScreenReader.ts +315 -0
  379. package/src/tui/hooks/useScrollController.ts +450 -0
  380. package/src/tui/hooks/useScrollEventBatcher.ts +185 -0
  381. package/src/tui/hooks/useSidebarPanelData.ts +115 -0
  382. package/src/tui/hooks/useSmoothScroll.ts +202 -0
  383. package/src/tui/hooks/useSnapshots.ts +300 -0
  384. package/src/tui/hooks/useStateAndRef.ts +50 -0
  385. package/src/tui/hooks/useTerminalSize.ts +206 -0
  386. package/src/tui/hooks/useToolApprovalController.ts +91 -0
  387. package/src/tui/hooks/useVim.ts +334 -0
  388. package/src/tui/hooks/useWorkspace.ts +56 -0
  389. package/src/tui/i18n/__tests__/init.test.ts +278 -0
  390. package/src/tui/i18n/__tests__/language-config.test.ts +199 -0
  391. package/src/tui/i18n/__tests__/locale-detection.test.ts +250 -0
  392. package/src/tui/i18n/__tests__/settings-integration.test.ts +262 -0
  393. package/src/tui/i18n/index.ts +72 -0
  394. package/src/tui/i18n/init.ts +131 -0
  395. package/src/tui/i18n/language-config.ts +106 -0
  396. package/src/tui/i18n/locale-detection.ts +173 -0
  397. package/src/tui/i18n/settings-integration.ts +557 -0
  398. package/src/tui/i18n/tui-namespace.ts +538 -0
  399. package/src/tui/i18n/types.ts +312 -0
  400. package/src/tui/index.ts +43 -0
  401. package/src/tui/lsp-integration.ts +409 -0
  402. package/src/tui/metrics-integration.ts +366 -0
  403. package/src/tui/plugins.ts +383 -0
  404. package/src/tui/resilience.ts +342 -0
  405. package/src/tui/sandbox-integration.ts +317 -0
  406. package/src/tui/services/clipboard.ts +348 -0
  407. package/src/tui/services/fuzzy-search.ts +441 -0
  408. package/src/tui/services/index.ts +72 -0
  409. package/src/tui/services/markdown-renderer.ts +565 -0
  410. package/src/tui/services/open-external.ts +247 -0
  411. package/src/tui/services/syntax-highlighter.ts +483 -0
  412. package/src/tui/slash-commands.ts +12 -0
  413. package/src/tui/theme/index.ts +15 -0
  414. package/src/tui/theme/provider.tsx +206 -0
  415. package/src/tui/tip-integration.ts +300 -0
  416. package/src/tui/types/__tests__/ink-extended.test.ts +121 -0
  417. package/src/tui/types/ink-extended.ts +87 -0
  418. package/src/tui/utils/__tests__/bracketedPaste.test.ts +231 -0
  419. package/src/tui/utils/__tests__/heightEstimator.test.ts +157 -0
  420. package/src/tui/utils/__tests__/text-width.test.ts +158 -0
  421. package/src/tui/utils/__tests__/textSanitizer.test.ts +266 -0
  422. package/src/tui/utils/__tests__/ui-sizing.test.ts +169 -0
  423. package/src/tui/utils/bracketedPaste.ts +107 -0
  424. package/src/tui/utils/cursor-manager.ts +131 -0
  425. package/src/tui/utils/detectTerminal.ts +596 -0
  426. package/src/tui/utils/findLastSafeSplitPoint.ts +92 -0
  427. package/src/tui/utils/heightEstimator.ts +198 -0
  428. package/src/tui/utils/index.ts +91 -0
  429. package/src/tui/utils/isNarrowWidth.ts +52 -0
  430. package/src/tui/utils/stdoutGuard.ts +90 -0
  431. package/src/tui/utils/synchronized-update.ts +70 -0
  432. package/src/tui/utils/text-width.ts +225 -0
  433. package/src/tui/utils/textSanitizer.ts +225 -0
  434. package/src/tui/utils/textUtils.ts +114 -0
  435. package/src/tui/utils/ui-sizing.ts +192 -0
  436. package/src/tui-blessed/app.ts +160 -0
  437. package/src/tui-blessed/index.ts +2 -0
  438. package/src/tui-blessed/neo-blessed.d.ts +6 -0
  439. package/src/tui-blessed/test.ts +21 -0
  440. package/src/tui-blessed/types.ts +14 -0
  441. package/src/utils/icons.ts +130 -0
  442. package/src/utils/index.ts +33 -0
  443. package/src/utils/resume-hint.ts +86 -0
  444. package/src/version.ts +1 -0
  445. package/tsconfig.json +8 -0
  446. 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 };