@nghyane/arcane 0.1.12 → 0.1.14

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 (333) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/package.json +21 -70
  3. package/scripts/format-prompts.ts +1 -3
  4. package/src/cli/args.ts +2 -7
  5. package/src/cli/config-cli.ts +1 -1
  6. package/src/cli/plugin-cli.ts +1 -1
  7. package/src/cli/setup-cli.ts +1 -1
  8. package/src/cli/update-cli.ts +1 -1
  9. package/src/cli/web-search-cli.ts +1 -1
  10. package/src/cli.ts +0 -1
  11. package/src/commands/config.ts +1 -1
  12. package/src/commands/grep.ts +1 -1
  13. package/src/commands/jupyter.ts +1 -1
  14. package/src/commands/plugin.ts +1 -1
  15. package/src/commands/setup.ts +1 -1
  16. package/src/commands/shell.ts +1 -1
  17. package/src/commands/ssh.ts +1 -1
  18. package/src/commands/stats.ts +1 -1
  19. package/src/commands/update.ts +1 -1
  20. package/src/config/model-registry.ts +3 -4
  21. package/src/config/model-resolver.ts +36 -9
  22. package/src/config/prompt-templates.ts +1 -9
  23. package/src/config/settings-schema.ts +32 -88
  24. package/src/config/settings.ts +3 -4
  25. package/src/debug/index.ts +1 -1
  26. package/src/debug/log-formatting.ts +1 -1
  27. package/src/debug/log-viewer.ts +2 -2
  28. package/src/discovery/helpers.ts +13 -3
  29. package/src/exa/company.ts +2 -7
  30. package/src/exa/index.ts +1 -35
  31. package/src/exa/linkedin.ts +2 -7
  32. package/src/exa/mcp-client.ts +21 -11
  33. package/src/exa/render.ts +30 -190
  34. package/src/exa/researcher.ts +2 -12
  35. package/src/exa/search.ts +5 -25
  36. package/src/exa/types.ts +3 -3
  37. package/src/exec/bash-executor.ts +2 -1
  38. package/src/exec/non-interactive-env.ts +43 -0
  39. package/src/export/html/index.ts +1 -1
  40. package/src/extensibility/custom-tools/loader.ts +1 -1
  41. package/src/extensibility/custom-tools/types.ts +5 -1
  42. package/src/extensibility/custom-tools/wrapper.ts +1 -1
  43. package/src/extensibility/extensions/runner.ts +1 -1
  44. package/src/extensibility/extensions/types.ts +1 -1
  45. package/src/extensibility/extensions/wrapper.ts +7 -15
  46. package/src/extensibility/hooks/runner.ts +1 -1
  47. package/src/extensibility/hooks/types.ts +1 -1
  48. package/src/extensibility/plugins/doctor.ts +1 -1
  49. package/src/index.ts +13 -13
  50. package/src/lsp/index.ts +77 -24
  51. package/src/lsp/render.ts +34 -583
  52. package/src/lsp/types.ts +3 -3
  53. package/src/lsp/utils.ts +1 -1
  54. package/src/main.ts +1 -1
  55. package/src/mcp/tool-bridge.ts +1 -24
  56. package/src/modes/components/assistant-message.ts +7 -7
  57. package/src/modes/components/bash-execution.ts +48 -113
  58. package/src/modes/components/bordered-loader.ts +1 -1
  59. package/src/modes/components/branch-summary-message.ts +13 -10
  60. package/src/modes/components/compaction-summary-message.ts +14 -13
  61. package/src/modes/components/context-group.ts +106 -0
  62. package/src/modes/components/custom-message.ts +4 -5
  63. package/src/modes/components/diff.ts +2 -2
  64. package/src/modes/components/dynamic-border.ts +1 -1
  65. package/src/modes/components/extensions/extension-dashboard.ts +2 -2
  66. package/src/modes/components/extensions/extension-list.ts +1 -1
  67. package/src/modes/components/extensions/inspector-panel.ts +8 -3
  68. package/src/modes/components/footer.ts +2 -2
  69. package/src/modes/components/history-search.ts +1 -1
  70. package/src/modes/components/hook-editor.ts +1 -1
  71. package/src/modes/components/hook-input.ts +1 -1
  72. package/src/modes/components/hook-message.ts +4 -5
  73. package/src/modes/components/hook-selector.ts +1 -1
  74. package/src/modes/components/index.ts +0 -2
  75. package/src/modes/components/keybinding-hints.ts +1 -1
  76. package/src/modes/components/login-dialog.ts +1 -1
  77. package/src/modes/components/mcp-add-wizard.ts +1 -1
  78. package/src/modes/components/model-selector.ts +1 -1
  79. package/src/modes/components/oauth-selector.ts +1 -1
  80. package/src/modes/components/plugin-settings.ts +1 -1
  81. package/src/modes/components/python-execution.ts +49 -92
  82. package/src/modes/components/queue-mode-selector.ts +1 -1
  83. package/src/modes/components/session-selector.ts +1 -1
  84. package/src/modes/components/settings-defs.ts +5 -10
  85. package/src/modes/components/settings-selector.ts +1 -1
  86. package/src/modes/components/show-images-selector.ts +1 -1
  87. package/src/modes/components/skill-message.ts +4 -4
  88. package/src/modes/components/status-line/segments.ts +2 -2
  89. package/src/modes/components/status-line/separators.ts +1 -1
  90. package/src/modes/components/status-line-segment-editor.ts +1 -1
  91. package/src/modes/components/status-line.ts +1 -1
  92. package/src/modes/components/theme-selector.ts +1 -1
  93. package/src/modes/components/thinking-selector.ts +1 -1
  94. package/src/modes/components/todo-display.ts +2 -4
  95. package/src/modes/components/todo-reminder.ts +4 -4
  96. package/src/modes/components/tool-execution.ts +118 -440
  97. package/src/modes/components/tool-image-display.ts +107 -0
  98. package/src/modes/components/tree-selector.ts +2 -2
  99. package/src/modes/components/ttsr-notification.ts +4 -17
  100. package/src/modes/components/user-message-selector.ts +1 -1
  101. package/src/modes/components/user-message.ts +9 -10
  102. package/src/modes/components/welcome.ts +1 -1
  103. package/src/modes/controllers/command-controller.ts +1 -1
  104. package/src/modes/controllers/event-controller.ts +58 -187
  105. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  106. package/src/modes/controllers/input-controller.ts +3 -1
  107. package/src/modes/controllers/mcp-command-controller.ts +1 -1
  108. package/src/modes/controllers/selector-controller.ts +3 -26
  109. package/src/modes/controllers/ssh-command-controller.ts +1 -1
  110. package/src/modes/interactive-mode.ts +3 -7
  111. package/src/modes/print-mode.ts +5 -5
  112. package/src/modes/rpc/rpc-mode.ts +1 -1
  113. package/src/modes/types.ts +1 -2
  114. package/src/modes/utils/ui-helpers.ts +34 -32
  115. package/src/patch/edit-tool.ts +742 -0
  116. package/src/patch/index.ts +32 -898
  117. package/src/patch/schemas.ts +208 -0
  118. package/src/patch/shared.ts +83 -151
  119. package/src/prompts/agents/explore.md +22 -37
  120. package/src/prompts/agents/frontmatter.md +1 -1
  121. package/src/prompts/agents/init.md +2 -2
  122. package/src/prompts/agents/librarian.md +30 -21
  123. package/src/prompts/agents/oracle.md +9 -2
  124. package/src/prompts/agents/reviewer.md +15 -49
  125. package/src/prompts/agents/task.md +17 -9
  126. package/src/prompts/compaction/branch-summary-context.md +1 -1
  127. package/src/prompts/compaction/branch-summary-preamble.md +1 -1
  128. package/src/prompts/compaction/branch-summary.md +4 -1
  129. package/src/prompts/compaction/compaction-short-summary.md +1 -1
  130. package/src/prompts/compaction/compaction-summary-context.md +1 -1
  131. package/src/prompts/compaction/compaction-summary.md +4 -1
  132. package/src/prompts/compaction/compaction-turn-prefix.md +1 -1
  133. package/src/prompts/compaction/compaction-update-summary.md +1 -1
  134. package/src/prompts/memories/consolidation.md +1 -1
  135. package/src/prompts/memories/read_path.md +1 -1
  136. package/src/prompts/memories/stage_one_input.md +1 -1
  137. package/src/prompts/memories/stage_one_system.md +1 -1
  138. package/src/prompts/review-request.md +1 -1
  139. package/src/prompts/system/agent-creation-architect.md +1 -1
  140. package/src/prompts/system/agent-creation-user.md +1 -1
  141. package/src/prompts/system/custom-system-prompt.md +1 -1
  142. package/src/prompts/system/file-operations.md +1 -1
  143. package/src/prompts/system/subagent-system-prompt.md +2 -2
  144. package/src/prompts/system/summarization-system.md +1 -1
  145. package/src/prompts/system/system-prompt.md +163 -178
  146. package/src/prompts/system/title-system.md +1 -1
  147. package/src/prompts/system/ttsr-interrupt.md +1 -1
  148. package/src/prompts/system/verification-reminder.md +6 -0
  149. package/src/prompts/system/web-search.md +1 -1
  150. package/src/sdk.ts +0 -9
  151. package/src/session/agent-session.ts +244 -1459
  152. package/src/session/auth-storage.ts +5 -0
  153. package/src/session/model-controller.ts +406 -0
  154. package/src/session/retry-utils.ts +71 -0
  155. package/src/session/session-manager.ts +22 -186
  156. package/src/session/session-types.ts +312 -0
  157. package/src/session/stats.ts +387 -0
  158. package/src/session/streaming-edit.ts +258 -0
  159. package/src/session/ttsr.ts +213 -0
  160. package/src/slash-commands/builtin-registry.ts +0 -8
  161. package/src/ssh/connection-manager.ts +1 -0
  162. package/src/stt/recorder.ts +2 -2
  163. package/src/system-prompt.ts +1 -14
  164. package/src/task/agents.ts +7 -33
  165. package/src/task/executor.ts +50 -438
  166. package/src/task/index.ts +104 -71
  167. package/src/task/progress-tracker.ts +390 -0
  168. package/src/task/render.ts +371 -187
  169. package/src/task/subprocess-tool-registry.ts +1 -1
  170. package/src/task/types.ts +14 -47
  171. package/src/tools/ask.ts +31 -42
  172. package/src/tools/bash-interactive.ts +4 -47
  173. package/src/tools/bash-interceptor.ts +2 -2
  174. package/src/tools/bash-normalize.ts +1 -1
  175. package/src/tools/bash-skill-urls.ts +2 -2
  176. package/src/tools/bash.ts +87 -136
  177. package/src/tools/browser.ts +54 -84
  178. package/src/tools/create-tools.ts +186 -0
  179. package/src/tools/default-renderer.ts +104 -0
  180. package/src/tools/explore.ts +11 -10
  181. package/src/tools/fetch.ts +24 -114
  182. package/src/tools/find.ts +48 -132
  183. package/src/tools/gemini-image.ts +5 -15
  184. package/src/tools/github.ts +450 -0
  185. package/src/tools/grep.ts +43 -179
  186. package/src/tools/index.ts +35 -198
  187. package/src/tools/json-tree.ts +3 -3
  188. package/src/tools/librarian.ts +18 -18
  189. package/src/tools/list-limit.ts +2 -2
  190. package/src/tools/notebook.ts +35 -87
  191. package/src/tools/oracle.ts +25 -25
  192. package/src/tools/output-meta.ts +89 -4
  193. package/src/tools/output-utils.ts +2 -2
  194. package/src/tools/python.ts +86 -637
  195. package/src/tools/read.ts +36 -119
  196. package/src/tools/reviewer-tool.ts +19 -21
  197. package/src/tools/search-code.ts +128 -0
  198. package/src/tools/ssh.ts +67 -126
  199. package/src/tools/subagent-tool.ts +197 -123
  200. package/src/tools/todo-write.ts +15 -31
  201. package/src/tools/tool-errors.ts +0 -30
  202. package/src/tools/undo-edit.ts +30 -67
  203. package/src/tools/write.ts +78 -127
  204. package/src/tui/code-cell.ts +4 -4
  205. package/src/tui/file-list.ts +2 -2
  206. package/src/tui/output-block.ts +1 -1
  207. package/src/tui/status-line.ts +1 -1
  208. package/src/tui/tree-list.ts +2 -2
  209. package/src/tui/types.ts +1 -1
  210. package/src/tui/utils.ts +1 -1
  211. package/src/{tools → ui}/render-utils.ts +87 -126
  212. package/src/utils/external-editor.ts +4 -4
  213. package/src/utils/file-mentions.ts +1 -1
  214. package/src/utils/index.ts +30 -0
  215. package/src/utils/tools-manager.ts +9 -19
  216. package/src/web/github-client.ts +290 -0
  217. package/src/web/scrapers/github.ts +11 -62
  218. package/src/web/search/auth.ts +1 -3
  219. package/src/web/search/index.ts +85 -49
  220. package/src/web/search/provider.ts +11 -16
  221. package/src/web/search/providers/grep.ts +160 -0
  222. package/src/web/search/render.ts +48 -235
  223. package/src/web/search/types.ts +1 -1
  224. package/src/commands/commit.ts +0 -36
  225. package/src/commit/agentic/agent.ts +0 -311
  226. package/src/commit/agentic/fallback.ts +0 -96
  227. package/src/commit/agentic/index.ts +0 -359
  228. package/src/commit/agentic/prompts/analyze-file.md +0 -22
  229. package/src/commit/agentic/prompts/session-user.md +0 -25
  230. package/src/commit/agentic/prompts/split-confirm.md +0 -1
  231. package/src/commit/agentic/prompts/system.md +0 -38
  232. package/src/commit/agentic/state.ts +0 -69
  233. package/src/commit/agentic/tools/analyze-file.ts +0 -118
  234. package/src/commit/agentic/tools/git-file-diff.ts +0 -194
  235. package/src/commit/agentic/tools/git-hunk.ts +0 -50
  236. package/src/commit/agentic/tools/git-overview.ts +0 -84
  237. package/src/commit/agentic/tools/index.ts +0 -56
  238. package/src/commit/agentic/tools/propose-changelog.ts +0 -128
  239. package/src/commit/agentic/tools/propose-commit.ts +0 -154
  240. package/src/commit/agentic/tools/recent-commits.ts +0 -81
  241. package/src/commit/agentic/tools/split-commit.ts +0 -280
  242. package/src/commit/agentic/topo-sort.ts +0 -44
  243. package/src/commit/agentic/trivial.ts +0 -51
  244. package/src/commit/agentic/validation.ts +0 -200
  245. package/src/commit/analysis/conventional.ts +0 -165
  246. package/src/commit/analysis/index.ts +0 -4
  247. package/src/commit/analysis/scope.ts +0 -242
  248. package/src/commit/analysis/summary.ts +0 -112
  249. package/src/commit/analysis/validation.ts +0 -66
  250. package/src/commit/changelog/detect.ts +0 -37
  251. package/src/commit/changelog/generate.ts +0 -110
  252. package/src/commit/changelog/index.ts +0 -234
  253. package/src/commit/changelog/parse.ts +0 -44
  254. package/src/commit/cli.ts +0 -93
  255. package/src/commit/git/diff.ts +0 -148
  256. package/src/commit/git/errors.ts +0 -9
  257. package/src/commit/git/index.ts +0 -211
  258. package/src/commit/git/operations.ts +0 -54
  259. package/src/commit/index.ts +0 -5
  260. package/src/commit/map-reduce/index.ts +0 -64
  261. package/src/commit/map-reduce/map-phase.ts +0 -178
  262. package/src/commit/map-reduce/reduce-phase.ts +0 -145
  263. package/src/commit/map-reduce/utils.ts +0 -9
  264. package/src/commit/message.ts +0 -11
  265. package/src/commit/model-selection.ts +0 -69
  266. package/src/commit/pipeline.ts +0 -243
  267. package/src/commit/prompts/analysis-system.md +0 -148
  268. package/src/commit/prompts/analysis-user.md +0 -38
  269. package/src/commit/prompts/changelog-system.md +0 -50
  270. package/src/commit/prompts/changelog-user.md +0 -18
  271. package/src/commit/prompts/file-observer-system.md +0 -24
  272. package/src/commit/prompts/file-observer-user.md +0 -8
  273. package/src/commit/prompts/reduce-system.md +0 -50
  274. package/src/commit/prompts/reduce-user.md +0 -17
  275. package/src/commit/prompts/summary-retry.md +0 -3
  276. package/src/commit/prompts/summary-system.md +0 -38
  277. package/src/commit/prompts/summary-user.md +0 -13
  278. package/src/commit/prompts/types-description.md +0 -2
  279. package/src/commit/types.ts +0 -109
  280. package/src/commit/utils/exclusions.ts +0 -42
  281. package/src/mcp/render.ts +0 -123
  282. package/src/modes/components/agent-dashboard.ts +0 -1130
  283. package/src/modes/components/codemode-group.ts +0 -369
  284. package/src/modes/components/read-tool-group.ts +0 -119
  285. package/src/modes/components/visual-truncate.ts +0 -63
  286. package/src/prompts/system/subagent-user-prompt.md +0 -8
  287. package/src/prompts/tools/ask.md +0 -44
  288. package/src/prompts/tools/bash.md +0 -24
  289. package/src/prompts/tools/browser.md +0 -33
  290. package/src/prompts/tools/calculator.md +0 -12
  291. package/src/prompts/tools/explore.md +0 -29
  292. package/src/prompts/tools/fetch.md +0 -16
  293. package/src/prompts/tools/find.md +0 -18
  294. package/src/prompts/tools/gemini-image.md +0 -23
  295. package/src/prompts/tools/grep.md +0 -28
  296. package/src/prompts/tools/hashline.md +0 -232
  297. package/src/prompts/tools/librarian.md +0 -24
  298. package/src/prompts/tools/lsp.md +0 -28
  299. package/src/prompts/tools/oracle.md +0 -26
  300. package/src/prompts/tools/patch.md +0 -74
  301. package/src/prompts/tools/python.md +0 -66
  302. package/src/prompts/tools/read.md +0 -36
  303. package/src/prompts/tools/replace.md +0 -38
  304. package/src/prompts/tools/reviewer.md +0 -41
  305. package/src/prompts/tools/ssh.md +0 -51
  306. package/src/prompts/tools/task-summary.md +0 -28
  307. package/src/prompts/tools/task.md +0 -146
  308. package/src/prompts/tools/todo-write.md +0 -65
  309. package/src/prompts/tools/undo-edit.md +0 -7
  310. package/src/prompts/tools/web-search.md +0 -19
  311. package/src/prompts/tools/write.md +0 -18
  312. package/src/task/batch.ts +0 -102
  313. package/src/task/discovery.ts +0 -126
  314. package/src/task/parallel.ts +0 -84
  315. package/src/task/template.ts +0 -32
  316. package/src/tools/calculator.ts +0 -537
  317. package/src/tools/jtd-to-typescript.ts +0 -198
  318. package/src/tools/renderers.ts +0 -60
  319. package/src/tools/tool-result.ts +0 -86
  320. /package/src/{modes/theme → theme}/dark.json +0 -0
  321. /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
  322. /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
  323. /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
  324. /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
  325. /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  326. /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
  327. /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
  328. /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
  329. /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
  330. /package/src/{modes/theme → theme}/light.json +0 -0
  331. /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
  332. /package/src/{modes/theme → theme}/theme-schema.json +0 -0
  333. /package/src/{modes/theme → theme}/theme.ts +0 -0
package/src/main.ts CHANGED
@@ -27,11 +27,11 @@ import { initializeWithSettings } from "./discovery";
27
27
  import { exportFromFile } from "./export/html";
28
28
  import type { ExtensionUIContext } from "./extensibility/extensions/types";
29
29
  import { InteractiveMode, runPrintMode, runRpcMode } from "./modes";
30
- import { initTheme, stopThemeWatcher } from "./modes/theme/theme";
31
30
  import { type CreateAgentSessionOptions, createAgentSession, discoverAuthStorage } from "./sdk";
32
31
  import type { AgentSession } from "./session/agent-session";
33
32
  import { type SessionInfo, SessionManager } from "./session/session-manager";
34
33
  import { resolvePromptInput } from "./system-prompt";
34
+ import { initTheme, stopThemeWatcher } from "./theme/theme";
35
35
  import { getChangelogPath, getNewEntries, parseChangelog } from "./utils/changelog";
36
36
  import { printTimings, time } from "./utils/timings";
37
37
  import { VERSION } from "./version";
@@ -6,16 +6,9 @@
6
6
  import type { AgentToolUpdateCallback } from "@nghyane/arcane-agent";
7
7
  import type { TSchema } from "@sinclair/typebox";
8
8
  import type { SourceMeta } from "../capability/types";
9
- import type {
10
- CustomTool,
11
- CustomToolContext,
12
- CustomToolResult,
13
- RenderResultOptions,
14
- } from "../extensibility/custom-tools/types";
15
- import type { Theme } from "../modes/theme/theme";
9
+ import type { CustomTool, CustomToolContext, CustomToolResult } from "../extensibility/custom-tools/types";
16
10
  import { ToolAbortError, throwIfAborted } from "../tools/tool-errors";
17
11
  import { callTool } from "./client";
18
- import { renderMCPCall, renderMCPResult } from "./render";
19
12
  import type { MCPContent, MCPServerConnection, MCPToolDefinition } from "./types";
20
13
 
21
14
  function withAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {
@@ -198,14 +191,6 @@ export class MCPTool implements CustomTool<TSchema, MCPToolDetails> {
198
191
  this.mcpServerName = connection.name;
199
192
  }
200
193
 
201
- renderCall(args: unknown, _options: RenderResultOptions, theme: Theme) {
202
- return renderMCPCall((args ?? {}) as Record<string, unknown>, theme, this.label);
203
- }
204
-
205
- renderResult(result: CustomToolResult<MCPToolDetails>, options: RenderResultOptions, theme: Theme, args?: unknown) {
206
- return renderMCPResult(result, options, theme, (args ?? {}) as Record<string, unknown>);
207
- }
208
-
209
194
  async execute(
210
195
  _toolCallId: string,
211
196
  params: unknown,
@@ -304,14 +289,6 @@ export class DeferredMCPTool implements CustomTool<TSchema, MCPToolDetails> {
304
289
  this.#fallbackProviderName = source?.providerName;
305
290
  }
306
291
 
307
- renderCall(args: unknown, _options: RenderResultOptions, theme: Theme) {
308
- return renderMCPCall((args ?? {}) as Record<string, unknown>, theme, this.label);
309
- }
310
-
311
- renderResult(result: CustomToolResult<MCPToolDetails>, options: RenderResultOptions, theme: Theme, args?: unknown) {
312
- return renderMCPResult(result, options, theme, (args ?? {}) as Record<string, unknown>);
313
- }
314
-
315
292
  async execute(
316
293
  _toolCallId: string,
317
294
  params: unknown,
@@ -1,8 +1,8 @@
1
1
  import type { AssistantMessage } from "@nghyane/arcane-ai";
2
2
  import { Container, Markdown, Spacer, TERMINAL, Text } from "@nghyane/arcane-tui";
3
3
  import { logger } from "@nghyane/arcane-utils";
4
- import { hasPendingMermaid, prerenderMermaid } from "../../modes/theme/mermaid-cache";
5
- import { getMarkdownTheme, theme } from "../../modes/theme/theme";
4
+ import { hasPendingMermaid, prerenderMermaid } from "../../theme/mermaid-cache";
5
+ import { getMarkdownTheme, theme } from "../../theme/theme";
6
6
 
7
7
  /**
8
8
  * Component that renders a complete assistant message
@@ -90,7 +90,7 @@ export class AssistantMessageComponent extends Container {
90
90
  if (content.type === "text" && content.text.trim()) {
91
91
  // Assistant text messages with no background - trim the text
92
92
  // Set paddingY=0 to avoid extra spacing before tool executions
93
- this.#contentContainer.addChild(new Markdown(content.text.trim(), 1, 0, getMarkdownTheme()));
93
+ this.#contentContainer.addChild(new Markdown(content.text.trim(), 2, 0, getMarkdownTheme()));
94
94
  } else if (content.type === "thinking" && content.thinking.trim()) {
95
95
  // Add spacing only when another visible assistant content block follows.
96
96
  // This avoids a superfluous blank line before separately-rendered tool execution blocks.
@@ -100,14 +100,14 @@ export class AssistantMessageComponent extends Container {
100
100
 
101
101
  if (this.hideThinkingBlock) {
102
102
  // Show static "Thinking..." label when hidden
103
- this.#contentContainer.addChild(new Text(theme.italic(theme.fg("thinkingText", "Thinking...")), 1, 0));
103
+ this.#contentContainer.addChild(new Text(theme.italic(theme.fg("thinkingText", "Thinking...")), 2, 0));
104
104
  if (hasVisibleContentAfter) {
105
105
  this.#contentContainer.addChild(new Spacer(1));
106
106
  }
107
107
  } else {
108
108
  // Thinking traces in thinkingText color, italic
109
109
  this.#contentContainer.addChild(
110
- new Markdown(content.thinking.trim(), 1, 0, getMarkdownTheme(), {
110
+ new Markdown(content.thinking.trim(), 2, 0, getMarkdownTheme(), {
111
111
  color: (text: string) => theme.fg("thinkingText", text),
112
112
  italic: true,
113
113
  }),
@@ -133,11 +133,11 @@ export class AssistantMessageComponent extends Container {
133
133
  } else {
134
134
  this.#contentContainer.addChild(new Spacer(1));
135
135
  }
136
- this.#contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
136
+ this.#contentContainer.addChild(new Text(theme.fg("error", abortMessage), 2, 0));
137
137
  } else if (message.stopReason === "error") {
138
138
  const errorMsg = message.errorMessage || "Unknown error";
139
139
  this.#contentContainer.addChild(new Spacer(1));
140
- this.#contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
140
+ this.#contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 2, 0));
141
141
  }
142
142
  }
143
143
  }
@@ -1,17 +1,16 @@
1
1
  /**
2
2
  * Component for displaying bash command execution with streaming output.
3
+ * Tree-style output: tail 4 lines on success, all on error.
3
4
  */
4
5
 
5
6
  import { sanitizeText } from "@nghyane/arcane-natives";
6
7
  import { Container, Loader, Spacer, Text, type TUI } from "@nghyane/arcane-tui";
7
- import { getSymbolTheme, theme } from "../../modes/theme/theme";
8
- import { formatBytes } from "../../session/streaming-output";
8
+ import { getSymbolTheme, theme } from "../../theme/theme";
9
9
  import type { TruncationMeta } from "../../tools/output-meta";
10
- import { DynamicBorder } from "./dynamic-border";
11
- import { truncateToVisualLines } from "./visual-truncate";
10
+ import { renderStatusLine } from "../../tui/status-line";
11
+ import { formatCount, replaceTabs } from "../../ui/render-utils";
12
12
 
13
- // Preview line limit when not expanded (matches tool execution behavior)
14
- const PREVIEW_LINES = 20;
13
+ const TAIL_LINES = 4;
15
14
  const MAX_DISPLAY_LINE_CHARS = 4000;
16
15
 
17
16
  export class BashExecutionComponent extends Container {
@@ -21,50 +20,37 @@ export class BashExecutionComponent extends Container {
21
20
  #loader: Loader;
22
21
  #truncation?: TruncationMeta;
23
22
  #expanded = false;
24
- #contentContainer: Container;
23
+ #headerText: Text;
24
+ #bodyText: Text;
25
25
 
26
26
  constructor(
27
27
  private readonly command: string,
28
28
  ui: TUI,
29
- excludeFromContext = false,
29
+ _excludeFromContext = false,
30
30
  ) {
31
31
  super();
32
-
33
- // Use dim border for excluded-from-context commands (!! prefix)
34
- const colorKey = excludeFromContext ? "dim" : "bashMode";
35
- const borderColor = (str: string) => theme.fg(colorKey, str);
36
-
37
- // Add spacer
38
32
  this.addChild(new Spacer(1));
39
33
 
40
- // Top border
41
- this.addChild(new DynamicBorder(borderColor));
42
-
43
- // Content container (holds dynamic content between borders)
44
- this.#contentContainer = new Container();
45
- this.addChild(this.#contentContainer);
34
+ this.#headerText = new Text(
35
+ renderStatusLine({ icon: "running", title: "Bash", description: `$ ${command}` }, theme),
36
+ 1,
37
+ 0,
38
+ );
39
+ this.addChild(this.#headerText);
46
40
 
47
- // Command header
48
- const header = new Text(theme.fg(colorKey, theme.bold(`$ ${command}`)), 1, 0);
49
- this.#contentContainer.addChild(header);
41
+ this.#bodyText = new Text("", 1, 0);
42
+ this.addChild(this.#bodyText);
50
43
 
51
- // Loader
52
44
  this.#loader = new Loader(
53
45
  ui,
54
- spinner => theme.fg(colorKey, spinner),
46
+ spinner => theme.fg("accent", spinner),
55
47
  text => theme.fg("muted", text),
56
- `Running (esc to cancel)`,
48
+ "Running\u2026 (esc to cancel)",
57
49
  getSymbolTheme().spinnerFrames,
58
50
  );
59
- this.#contentContainer.addChild(this.#loader);
60
-
61
- // Bottom border
62
- this.addChild(new DynamicBorder(borderColor));
51
+ this.addChild(this.#loader);
63
52
  }
64
53
 
65
- /**
66
- * Set whether the output is expanded (shows full output) or collapsed (preview only).
67
- */
68
54
  setExpanded(expanded: boolean): void {
69
55
  this.#expanded = expanded;
70
56
  this.#updateDisplay();
@@ -77,11 +63,8 @@ export class BashExecutionComponent extends Container {
77
63
 
78
64
  appendOutput(chunk: string): void {
79
65
  const clean = sanitizeText(chunk);
80
-
81
- // Append to output lines
82
66
  const newLines = clean.split("\n").map(line => this.#clampDisplayLine(line));
83
67
  if (this.#outputLines.length > 0 && newLines.length > 0) {
84
- // Append first chunk to last line (incomplete line continuation)
85
68
  this.#outputLines[this.#outputLines.length - 1] = this.#clampDisplayLine(
86
69
  `${this.#outputLines[this.#outputLines.length - 1]}${newLines[0]}`,
87
70
  );
@@ -89,7 +72,6 @@ export class BashExecutionComponent extends Container {
89
72
  } else {
90
73
  this.#outputLines.push(...newLines);
91
74
  }
92
-
93
75
  this.#updateDisplay();
94
76
  }
95
77
 
@@ -108,93 +90,52 @@ export class BashExecutionComponent extends Container {
108
90
  if (options?.output !== undefined) {
109
91
  this.#setOutput(options.output);
110
92
  }
111
-
112
- // Stop loader
113
93
  this.#loader.stop();
114
-
115
94
  this.#updateDisplay();
116
95
  }
117
96
 
118
97
  #updateDisplay(): void {
119
- const availableLines = this.#outputLines;
120
-
121
- // Apply preview truncation based on expanded state
122
- const previewLogicalLines = availableLines.slice(-PREVIEW_LINES);
123
- const hiddenLineCount = availableLines.length - previewLogicalLines.length;
124
-
125
- // Rebuild content container
126
- this.#contentContainer.clear();
127
-
128
- // Command header
129
- const header = new Text(theme.fg("bashMode", theme.bold(`$ ${this.command}`)), 1, 0);
130
- this.#contentContainer.addChild(header);
131
-
132
- // Output
133
- if (availableLines.length > 0) {
134
- if (this.#expanded) {
135
- // Show all lines
136
- const displayText = availableLines.map(line => theme.fg("muted", line)).join("\n");
137
- this.#contentContainer.addChild(new Text(`\n${displayText}`, 1, 0));
138
- } else {
139
- // Use shared visual truncation utility, recomputed per render width
140
- const styledOutput = previewLogicalLines.map(line => theme.fg("muted", line)).join("\n");
141
- const previewText = `\n${styledOutput}`;
142
- this.#contentContainer.addChild({
143
- render: (width: number) => {
144
- const { visualLines } = truncateToVisualLines(previewText, PREVIEW_LINES, width, 1);
145
- return visualLines;
146
- },
147
- invalidate: () => {},
148
- });
149
- }
98
+ const isError = this.#status === "error";
99
+ const isDone = this.#status !== "running";
100
+ const lines = this.#outputLines.filter(l => l.trim());
101
+ const total = lines.length;
102
+
103
+ // Update header
104
+ if (isDone) {
105
+ const icon = isError ? "error" : this.#status === "cancelled" ? "warning" : "success";
106
+ const meta: string[] = [];
107
+ if (isError && this.#exitCode !== undefined) meta.push(`exit ${this.#exitCode}`);
108
+ if (total > 0) meta.push(formatCount("line", total));
109
+ this.#headerText.setText(
110
+ renderStatusLine({ icon, title: "Bash", description: `$ ${this.command}`, meta }, theme),
111
+ );
150
112
  }
151
113
 
152
- // Loader or status
153
- if (this.#status === "running") {
154
- this.#contentContainer.addChild(this.#loader);
155
- } else {
156
- const statusParts: string[] = [];
114
+ // Build tree-style body
115
+ const bodyLines: string[] = [];
116
+ if (total > 0) {
117
+ const showAll = isError || this.#expanded;
118
+ const displayLines = showAll ? lines : lines.slice(-TAIL_LINES);
119
+ const skipped = total - displayLines.length;
157
120
 
158
- // Show how many lines are hidden (collapsed preview)
159
- if (hiddenLineCount > 0) {
160
- statusParts.push(theme.fg("dim", `… ${hiddenLineCount} more lines (ctrl+o to expand)`));
121
+ if (skipped > 0) {
122
+ bodyLines.push(theme.fg("dim", `\u2026 (${skipped} earlier lines)`));
161
123
  }
162
-
163
- if (this.#status === "cancelled") {
164
- statusParts.push(theme.fg("warning", "(cancelled)"));
165
- } else if (this.#status === "error") {
166
- statusParts.push(theme.fg("error", `(exit ${this.#exitCode})`));
124
+ for (let i = 0; i < displayLines.length; i++) {
125
+ bodyLines.push(theme.fg("toolOutput", replaceTabs(displayLines[i])));
167
126
  }
168
-
169
127
  if (this.#truncation) {
170
- const warnings: string[] = [];
171
- if (this.#truncation.artifactId) {
172
- warnings.push(`Full output: artifact://${this.#truncation.artifactId}`);
173
- }
174
- if (this.#truncation.truncatedBy === "lines") {
175
- warnings.push(
176
- `Truncated: showing ${this.#truncation.outputLines} of ${this.#truncation.totalLines} lines`,
177
- );
178
- } else {
179
- warnings.push(
180
- `Truncated: ${this.#truncation.outputLines} lines shown (${formatBytes(this.#truncation.outputBytes)} limit)`,
181
- );
182
- }
183
- statusParts.push(theme.fg("warning", warnings.join(". ")));
184
- }
185
-
186
- if (statusParts.length > 0) {
187
- this.#contentContainer.addChild(new Text(`\n${statusParts.join("\n")}`, 1, 0));
128
+ bodyLines.push(theme.fg("warning", "output truncated"));
188
129
  }
189
130
  }
131
+
132
+ this.#bodyText.setText(bodyLines.length > 0 ? bodyLines.join("\n") : "");
190
133
  }
191
134
 
192
135
  #clampDisplayLine(line: string): string {
193
- if (line.length <= MAX_DISPLAY_LINE_CHARS) {
194
- return line;
195
- }
136
+ if (line.length <= MAX_DISPLAY_LINE_CHARS) return line;
196
137
  const omitted = line.length - MAX_DISPLAY_LINE_CHARS;
197
- return `${line.slice(0, MAX_DISPLAY_LINE_CHARS)} [${omitted} chars omitted]`;
138
+ return `${line.slice(0, MAX_DISPLAY_LINE_CHARS)}\u2026 [${omitted} chars omitted]`;
198
139
  }
199
140
 
200
141
  #setOutput(output: string): void {
@@ -202,16 +143,10 @@ export class BashExecutionComponent extends Container {
202
143
  this.#outputLines = clean ? clean.split("\n").map(line => this.#clampDisplayLine(line)) : [];
203
144
  }
204
145
 
205
- /**
206
- * Get the raw output for creating BashExecutionMessage.
207
- */
208
146
  getOutput(): string {
209
147
  return this.#outputLines.join("\n");
210
148
  }
211
149
 
212
- /**
213
- * Get the command that was executed.
214
- */
215
150
  getCommand(): string {
216
151
  return this.command;
217
152
  }
@@ -1,5 +1,5 @@
1
1
  import { CancellableLoader, Container, Spacer, Text, type TUI } from "@nghyane/arcane-tui";
2
- import type { Theme } from "../../modes/theme/theme";
2
+ import type { Theme } from "../../theme/theme";
3
3
  import { DynamicBorder } from "./dynamic-border";
4
4
 
5
5
  /** Loader wrapped with borders for hook UI */
@@ -1,16 +1,19 @@
1
- import { Box, Markdown, Spacer, Text } from "@nghyane/arcane-tui";
2
- import { getMarkdownTheme, theme } from "../../modes/theme/theme";
1
+ import { Container, LeftBorderBox, Markdown, Spacer, Text } from "@nghyane/arcane-tui";
3
2
  import type { BranchSummaryMessage } from "../../session/messages";
3
+ import { getMarkdownTheme, theme } from "../../theme/theme";
4
4
 
5
5
  /**
6
6
  * Component that renders a branch summary message with collapsed/expanded state.
7
- * Uses same background color as hook messages for visual consistency.
8
7
  */
9
- export class BranchSummaryMessageComponent extends Box {
8
+ export class BranchSummaryMessageComponent extends Container {
9
+ #box: LeftBorderBox;
10
10
  #expanded = false;
11
11
 
12
12
  constructor(private readonly message: BranchSummaryMessage) {
13
- super(1, 1, t => theme.bg("customMessageBg", t));
13
+ super();
14
+ this.addChild(new Spacer(1));
15
+ this.#box = new LeftBorderBox(1, 1, s => theme.fg("dim", s));
16
+ this.addChild(this.#box);
14
17
  this.#updateDisplay();
15
18
  }
16
19
 
@@ -25,21 +28,21 @@ export class BranchSummaryMessageComponent extends Box {
25
28
  }
26
29
 
27
30
  #updateDisplay(): void {
28
- this.clear();
31
+ this.#box.clear();
29
32
 
30
33
  const label = theme.fg("customMessageLabel", theme.bold("[branch]"));
31
- this.addChild(new Text(label, 0, 0));
32
- this.addChild(new Spacer(1));
34
+ this.#box.addChild(new Text(label, 0, 0));
35
+ this.#box.addChild(new Spacer(1));
33
36
 
34
37
  if (this.#expanded) {
35
38
  const header = "**Branch Summary**\n\n";
36
- this.addChild(
39
+ this.#box.addChild(
37
40
  new Markdown(header + this.message.summary, 0, 0, getMarkdownTheme(), {
38
41
  color: (text: string) => theme.fg("customMessageText", text),
39
42
  }),
40
43
  );
41
44
  } else {
42
- this.addChild(new Text(theme.fg("customMessageText", "Branch summary (ctrl+o to expand)"), 0, 0));
45
+ this.#box.addChild(new Text(theme.fg("customMessageText", "Branch summary"), 0, 0));
43
46
  }
44
47
  }
45
48
  }
@@ -1,16 +1,19 @@
1
- import { Box, Markdown, Spacer, Text } from "@nghyane/arcane-tui";
2
- import { getMarkdownTheme, theme } from "../../modes/theme/theme";
1
+ import { Container, LeftBorderBox, Markdown, Spacer, Text } from "@nghyane/arcane-tui";
3
2
  import type { CompactionSummaryMessage } from "../../session/messages";
3
+ import { getMarkdownTheme, theme } from "../../theme/theme";
4
4
 
5
5
  /**
6
6
  * Component that renders a compaction message with collapsed/expanded state.
7
- * Uses same background color as hook messages for visual consistency.
8
7
  */
9
- export class CompactionSummaryMessageComponent extends Box {
8
+ export class CompactionSummaryMessageComponent extends Container {
9
+ #box: LeftBorderBox;
10
10
  #expanded = false;
11
11
 
12
12
  constructor(private readonly message: CompactionSummaryMessage) {
13
- super(1, 1, t => theme.bg("customMessageBg", t));
13
+ super();
14
+ this.addChild(new Spacer(1));
15
+ this.#box = new LeftBorderBox(1, 1, s => theme.fg("dim", s));
16
+ this.addChild(this.#box);
14
17
  this.#updateDisplay();
15
18
  }
16
19
 
@@ -25,26 +28,24 @@ export class CompactionSummaryMessageComponent extends Box {
25
28
  }
26
29
 
27
30
  #updateDisplay(): void {
28
- this.clear();
31
+ this.#box.clear();
29
32
 
30
33
  const tokenStr = this.message.tokensBefore.toLocaleString();
31
34
  const label = theme.fg("customMessageLabel", theme.bold("[compaction]"));
32
- this.addChild(new Text(label, 0, 0));
33
- this.addChild(new Spacer(1));
35
+ this.#box.addChild(new Text(label, 0, 0));
36
+ this.#box.addChild(new Spacer(1));
34
37
 
35
38
  if (this.#expanded) {
36
39
  const header = `**Compacted from ${tokenStr} tokens**\n\n`;
37
- this.addChild(
40
+ this.#box.addChild(
38
41
  new Markdown(header + this.message.summary, 0, 0, getMarkdownTheme(), {
39
42
  color: (text: string) => theme.fg("customMessageText", text),
40
43
  }),
41
44
  );
42
45
  } else {
43
- this.addChild(
44
- new Text(theme.fg("customMessageText", `Compacted from ${tokenStr} tokens (ctrl+o to expand)`), 0, 0),
45
- );
46
+ this.#box.addChild(new Text(theme.fg("customMessageText", `Compacted from ${tokenStr} tokens`), 0, 0));
46
47
  if (this.message.shortSummary) {
47
- this.addChild(new Text(theme.fg("customMessageText", this.message.shortSummary), 0, 1));
48
+ this.#box.addChild(new Text(theme.fg("customMessageText", this.message.shortSummary), 0, 1));
48
49
  }
49
50
  }
50
51
  }
@@ -0,0 +1,106 @@
1
+ import type { Component } from "@nghyane/arcane-tui";
2
+ import { Spacer } from "@nghyane/arcane-tui";
3
+ import { theme } from "../../theme/theme";
4
+ import { formatCount, formatStatusIcon } from "../../ui/render-utils";
5
+ import type { ToolExecutionComponent } from "./tool-execution";
6
+
7
+ const CONTEXT_TOOL_LABELS: Record<string, string> = {
8
+ read: "read",
9
+ grep: "grep",
10
+ find: "find",
11
+ fetch: "fetch",
12
+ search_code: "search",
13
+ lsp: "lsp",
14
+ notebook: "notebook",
15
+ };
16
+
17
+ /**
18
+ * Groups consecutive context-gathering tools (read, grep, find, etc.) into
19
+ * a single collapsible summary line to reduce visual noise.
20
+ *
21
+ * Collapsed: "Gathered context 3 reads, 2 greps, 1 find"
22
+ * Expanded: summary + individual tool components
23
+ */
24
+ export class ContextGroupComponent implements Component {
25
+ #entries: Array<{ name: string; component: ToolExecutionComponent }> = [];
26
+ #expanded = false;
27
+ #pendingCount = 0;
28
+ #spacer: Spacer;
29
+
30
+ constructor() {
31
+ this.#spacer = new Spacer(1);
32
+ }
33
+
34
+ addTool(name: string, component: ToolExecutionComponent): void {
35
+ this.#entries.push({ name, component });
36
+ this.#pendingCount++;
37
+ component.setExpanded(this.#expanded);
38
+ }
39
+
40
+ markDone(): void {
41
+ this.#pendingCount = Math.max(0, this.#pendingCount - 1);
42
+ }
43
+
44
+ setExpanded(expanded: boolean): void {
45
+ if (this.#expanded === expanded) return;
46
+ this.#expanded = expanded;
47
+ for (const entry of this.#entries) {
48
+ entry.component.setExpanded(expanded);
49
+ }
50
+ }
51
+
52
+ setMarginTop(lines: number): void {
53
+ this.#spacer.setLines(lines);
54
+ }
55
+
56
+ get size(): number {
57
+ return this.#entries.length;
58
+ }
59
+
60
+ invalidate(): void {
61
+ for (const entry of this.#entries) {
62
+ entry.component.invalidate?.();
63
+ }
64
+ }
65
+
66
+ render(width: number): string[] {
67
+ const lines: string[] = [];
68
+
69
+ // Margin
70
+ lines.push(...this.#spacer.render(width));
71
+
72
+ // Summary line
73
+ const allDone = this.#pendingCount <= 0;
74
+ const icon = allDone ? formatStatusIcon("success", theme) : formatStatusIcon("running", theme);
75
+ const label = allDone ? "Gathered context" : "Gathering context…";
76
+
77
+ // Count by tool type
78
+ const counts = new Map<string, number>();
79
+ for (const entry of this.#entries) {
80
+ const displayName = CONTEXT_TOOL_LABELS[entry.name] ?? entry.name;
81
+ counts.set(displayName, (counts.get(displayName) ?? 0) + 1);
82
+ }
83
+ const parts: string[] = [];
84
+ for (const [name, count] of counts) {
85
+ parts.push(formatCount(name, count));
86
+ }
87
+ const summary = parts.join(", ");
88
+
89
+ const summaryLine = ` ${icon} ${theme.fg("muted", label)} ${theme.fg("dim", summary)}`;
90
+ lines.push(summaryLine);
91
+
92
+ // Always show individual tool entries, indented under summary
93
+ const indent = " ";
94
+ const innerWidth = width - indent.length;
95
+ for (let i = 0; i < this.#entries.length; i++) {
96
+ const entryLines = this.#entries[i].component.render(innerWidth);
97
+ for (const line of entryLines) {
98
+ if (line.trim()) {
99
+ lines.push(indent + line);
100
+ }
101
+ }
102
+ }
103
+
104
+ return lines;
105
+ }
106
+ }
@@ -1,16 +1,16 @@
1
1
  import type { TextContent } from "@nghyane/arcane-ai";
2
2
  import type { Component } from "@nghyane/arcane-tui";
3
- import { Box, Container, Markdown, Spacer, Text } from "@nghyane/arcane-tui";
3
+ import { Container, LeftBorderBox, Markdown, Spacer, Text } from "@nghyane/arcane-tui";
4
4
  import type { MessageRenderer } from "../../extensibility/extensions/types";
5
- import { getMarkdownTheme, theme } from "../../modes/theme/theme";
6
5
  import type { CustomMessage } from "../../session/messages";
6
+ import { getMarkdownTheme, theme } from "../../theme/theme";
7
7
 
8
8
  /**
9
9
  * Component that renders a custom message entry from extensions.
10
10
  * Uses distinct styling to differentiate from user messages.
11
11
  */
12
12
  export class CustomMessageComponent extends Container {
13
- #box: Box;
13
+ #box: LeftBorderBox;
14
14
  #customComponent?: Component;
15
15
  #expanded = false;
16
16
 
@@ -22,8 +22,7 @@ export class CustomMessageComponent extends Container {
22
22
 
23
23
  this.addChild(new Spacer(1));
24
24
 
25
- // Create box with custom background (used for default rendering)
26
- this.#box = new Box(1, 1, t => theme.bg("customMessageBg", t));
25
+ this.#box = new LeftBorderBox(1, 1, s => theme.fg("dim", s));
27
26
 
28
27
  this.#rebuild();
29
28
  }
@@ -1,6 +1,6 @@
1
1
  import * as Diff from "diff";
2
- import { theme } from "../../modes/theme/theme";
3
- import { replaceTabs } from "../../tools/render-utils";
2
+ import { theme } from "../../theme/theme";
3
+ import { replaceTabs } from "../../ui/render-utils";
4
4
 
5
5
  /** SGR dim on / normal intensity — additive, preserves fg/bg colors. */
6
6
  const DIM = "\x1b[2m";
@@ -1,5 +1,5 @@
1
1
  import type { Component } from "@nghyane/arcane-tui";
2
- import { theme } from "../../modes/theme/theme";
2
+ import { theme } from "../../theme/theme";
3
3
 
4
4
  /**
5
5
  * Dynamic border component that adjusts to viewport width.
@@ -23,7 +23,7 @@ import {
23
23
  } from "@nghyane/arcane-tui";
24
24
  import { Settings } from "../../../config/settings";
25
25
  import { DynamicBorder } from "../../../modes/components/dynamic-border";
26
- import { theme } from "../../../modes/theme/theme";
26
+ import { theme } from "../../../theme/theme";
27
27
  import { ExtensionList } from "./extension-list";
28
28
  import { InspectorPanel } from "./inspector-panel";
29
29
  import { applyFilter, createInitialState, filterByProvider, refreshState, toggleProvider } from "./state-manager";
@@ -298,7 +298,7 @@ class TwoColumnBody implements Component {
298
298
 
299
299
  render(width: number): string[] {
300
300
  const leftWidth = Math.floor(width * 0.5);
301
- const rightWidth = width - leftWidth - 3;
301
+ const rightWidth = Math.max(0, width - leftWidth - 3);
302
302
 
303
303
  const leftLines = this.leftPane.render(leftWidth);
304
304
  const rightLines = this.rightPane.render(rightWidth);
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { type Component, matchesKey, padding, truncateToWidth, visibleWidth } from "@nghyane/arcane-tui";
9
9
  import { isProviderEnabled } from "../../../discovery";
10
- import { theme } from "../../../modes/theme/theme";
10
+ import { theme } from "../../../theme/theme";
11
11
  import { applyFilter } from "./state-manager";
12
12
  import type { Extension, ExtensionKind, ExtensionState } from "./types";
13
13