praxis-agent 0.34.0 → 0.36.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 (78) hide show
  1. package/README.md +9 -1
  2. package/dist/application/background-agent-manager.d.ts +16 -6
  3. package/dist/application/background-agent-manager.js +139 -97
  4. package/dist/application/context-engine.d.ts +56 -0
  5. package/dist/application/context-engine.js +129 -0
  6. package/dist/application/session-service.d.ts +8 -5
  7. package/dist/application/session-service.js +1824 -1728
  8. package/dist/application/subagent-service.js +385 -193
  9. package/dist/application/top-level-agent-manager.d.ts +1 -0
  10. package/dist/application/top-level-agent-manager.js +1025 -403
  11. package/dist/application/transcript-projection.d.ts +33 -0
  12. package/dist/application/transcript-projection.js +110 -0
  13. package/dist/application/turn-lifecycle.d.ts +40 -0
  14. package/dist/application/turn-lifecycle.js +36 -0
  15. package/dist/application/turn-memory-coordinator.d.ts +43 -0
  16. package/dist/application/turn-memory-coordinator.js +98 -0
  17. package/dist/cli/agents-dashboard.js +10 -5
  18. package/dist/cli/interactive.js +279 -209
  19. package/dist/cli/protocol.js +5 -2
  20. package/dist/cli/tui/claude-style.d.ts +5 -48
  21. package/dist/cli/tui/claude-style.js +167 -197
  22. package/dist/cli/tui/task-panel.js +10 -1
  23. package/dist/cli/tui/theme.d.ts +46 -3
  24. package/dist/cli/tui/theme.js +414 -56
  25. package/dist/cli/tui/tool-permission.js +9 -5
  26. package/dist/cli/tui/transcript-presentation.d.ts +89 -0
  27. package/dist/cli/tui/transcript-presentation.js +169 -0
  28. package/dist/cli/tui/transcript-viewport.d.ts +5 -34
  29. package/dist/cli/tui/transcript-viewport.js +639 -158
  30. package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
  31. package/dist/cli/tui/tui-interaction-router.js +127 -0
  32. package/dist/cli/tui/tui-view-model.d.ts +6 -2
  33. package/dist/cli/tui/tui-view-model.js +23 -6
  34. package/dist/cli-runtime.d.ts +2 -2
  35. package/dist/compatibility/claude/context.d.ts +2 -2
  36. package/dist/compatibility/claude/context.js +2 -14
  37. package/dist/compatibility/claude/data-plane-adapter.d.ts +9 -0
  38. package/dist/compatibility/claude/data-plane-adapter.js +44 -0
  39. package/dist/compatibility/claude/paths.js +2 -2
  40. package/dist/compatibility/claude/shared-resources.d.ts +7 -38
  41. package/dist/compatibility/claude/transcript-codec.d.ts +21 -0
  42. package/dist/compatibility/claude/transcript-codec.js +512 -0
  43. package/dist/core/agent-orchestration.d.ts +31 -0
  44. package/dist/core/agent-orchestration.js +235 -0
  45. package/dist/core/context.d.ts +25 -7
  46. package/dist/core/context.js +46 -0
  47. package/dist/core/prompt-composer.d.ts +8 -16
  48. package/dist/core/prompt-composer.js +65 -25
  49. package/dist/core/resources.d.ts +39 -0
  50. package/dist/core/resources.js +2 -0
  51. package/dist/core/runtime.d.ts +6 -1
  52. package/dist/core/runtime.js +15 -1
  53. package/dist/core/session.d.ts +2 -0
  54. package/dist/core/session.js +5 -0
  55. package/dist/core/transcript-codec.d.ts +70 -0
  56. package/dist/core/transcript-codec.js +60 -0
  57. package/dist/core/transcript-event.d.ts +33 -0
  58. package/dist/core/transcript-event.js +198 -0
  59. package/dist/persistence/claude-job-store.d.ts +58 -0
  60. package/dist/persistence/claude-job-store.js +483 -1
  61. package/dist/persistence/data-plane-adapter.d.ts +32 -0
  62. package/dist/persistence/data-plane-adapter.js +2 -0
  63. package/dist/persistence/data-plane.d.ts +4 -20
  64. package/dist/persistence/data-plane.js +10 -47
  65. package/dist/persistence/native-data-plane-adapter.d.ts +9 -0
  66. package/dist/persistence/native-data-plane-adapter.js +39 -0
  67. package/dist/persistence/native-resources.d.ts +4 -4
  68. package/dist/persistence/native-transcript-codec.d.ts +13 -0
  69. package/dist/persistence/native-transcript-codec.js +102 -0
  70. package/dist/persistence/native-transcript-reader.d.ts +53 -0
  71. package/dist/persistence/native-transcript-reader.js +283 -0
  72. package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
  73. package/dist/persistence/subagent-lifecycle-store.js +373 -146
  74. package/dist/platform/exclusive-file-lease.d.ts +3 -0
  75. package/dist/platform/exclusive-file-lease.js +3 -0
  76. package/dist/tools/claude-user-message.d.ts +0 -1
  77. package/dist/tools/claude-user-message.js +0 -1
  78. package/package.json +6 -2
@@ -5,7 +5,7 @@ import { composerEditorSegments } from './composer-editor.js';
5
5
  import { composerLayoutForWidth } from './composer-layout.js';
6
6
  import { visiblePatchLines } from './git-diff.js';
7
7
  import { TUI_HOOK_MENU } from './hook-settings.js';
8
- import { tuiPalette, tuiSyntaxStyle, useTuiPalette, } from './theme.js';
8
+ import { resolveTuiTheme, tuiSyntaxStyle, useTuiTheme, } from './theme.js';
9
9
  const SPINNER = ['✳', '✢', '✣', '✤', '✥'];
10
10
  export function useTerminalWidth(override) {
11
11
  const { stdout } = useStdout();
@@ -106,8 +106,13 @@ function selectionPrefix(selected, screenReader) {
106
106
  return screenReader ? 'Selected: ' : ' ❯ ';
107
107
  return screenReader ? '' : ' ';
108
108
  }
109
+ function dashboardSelectionPrefix(selected, screenReader, noColor) {
110
+ if (selected)
111
+ return screenReader || noColor ? 'Selected: ' : '❯ ';
112
+ return screenReader || noColor ? '' : ' ';
113
+ }
109
114
  export function WelcomePanel({ display, width, showTips, dataPlane = 'claude', }) {
110
- const palette = useTuiPalette();
115
+ const theme = useTuiTheme();
111
116
  if (!showTips)
112
117
  return null;
113
118
  const panelWidth = Math.min(100, Math.max(32, width));
@@ -124,7 +129,7 @@ export function WelcomePanel({ display, width, showTips, dataPlane = 'claude', }
124
129
  const fill = Math.max(1, panelWidth - title.length - 6);
125
130
  const leftWidth = Math.max(12, Math.floor((panelWidth - 4) / 2));
126
131
  const rightWidth = Math.max(12, panelWidth - leftWidth - 4);
127
- return (_jsxs(Box, { flexDirection: "column", width: panelWidth, children: [_jsxs(Text, { color: palette.muted, children: ['╭───', _jsx(Text, { color: palette.brand, bold: true, children: brand }), ` Code v${display.version} `, _jsx(Text, { dimColor: true, children: '─'.repeat(fill) }), '╮'] }), _jsxs(Box, { borderStyle: "round", borderColor: palette.muted, borderTop: false, flexDirection: wide ? 'row' : 'column', width: panelWidth, paddingX: 1, children: [_jsxs(Box, { alignItems: wide ? 'center' : undefined, flexDirection: "column", width: wide ? leftWidth : '100%', children: [_jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: ["Welcome to ", brand] }), _jsx(Text, { children: " " }), _jsx(Text, { color: palette.brand, bold: true, children: "\u2590\u259B\u2588\u2588\u2588\u259C\u258C" }), _jsx(Text, { color: palette.brand, children: "\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u259B\u2598" }), _jsx(Text, { color: palette.brand, children: " \u2598\u2598 \u259D\u259D" }), _jsx(Text, { children: " " }), _jsxs(Text, { wrap: "truncate-end", children: [model, effort] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: cwd })] }), _jsxs(Box, { flexDirection: "column", width: wide ? rightWidth : '100%', marginTop: wide ? 0 : 1, children: [_jsx(Text, { bold: true, children: "Get started" }), _jsxs(Text, { wrap: "truncate-end", children: ["/init to create ", dataPlane === 'native' ? 'PRAXIS.md' : 'CLAUDE.md'] }), _jsx(Text, { wrap: "truncate-end", children: "/config to open settings" }), _jsx(Text, { dimColor: true, children: " " }), _jsx(Text, { bold: true, children: dataPlane === 'native'
132
+ return (_jsxs(Box, { flexDirection: "column", width: panelWidth, children: [_jsxs(Text, { ...theme.text.muted, children: ['╭───', _jsx(Text, { ...theme.text.productIdentity, bold: true, children: brand }), ` Code v${display.version} `, _jsx(Text, { dimColor: true, children: '─'.repeat(fill) }), '╮'] }), _jsxs(Box, { borderStyle: "round", ...theme.surface.neutralBorder, borderTop: false, flexDirection: wide ? 'row' : 'column', width: panelWidth, paddingX: 1, children: [_jsxs(Box, { alignItems: wide ? 'center' : undefined, flexDirection: "column", width: wide ? leftWidth : '100%', children: [_jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: ["Welcome to ", brand] }), _jsx(Text, { children: " " }), _jsx(Text, { ...theme.text.productIdentity, bold: true, children: "\u2590\u259B\u2588\u2588\u2588\u259C\u258C" }), _jsx(Text, { ...theme.text.productIdentity, children: "\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u259B\u2598" }), _jsx(Text, { ...theme.text.productIdentity, children: " \u2598\u2598 \u259D\u259D" }), _jsx(Text, { children: " " }), _jsxs(Text, { wrap: "truncate-end", children: [model, effort] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: cwd })] }), _jsxs(Box, { flexDirection: "column", width: wide ? rightWidth : '100%', marginTop: wide ? 0 : 1, children: [_jsx(Text, { bold: true, children: "Get started" }), _jsxs(Text, { wrap: "truncate-end", children: ["/init to create ", dataPlane === 'native' ? 'PRAXIS.md' : 'CLAUDE.md'] }), _jsx(Text, { wrap: "truncate-end", children: "/config to open settings" }), _jsx(Text, { dimColor: true, children: " " }), _jsx(Text, { bold: true, children: dataPlane === 'native'
128
133
  ? 'Stored by Praxis'
129
134
  : 'Shared with Claude Code' }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: "Sessions \u00B7 memory \u00B7 skills" })] })] })] }));
130
135
  }
@@ -132,12 +137,12 @@ export function WelcomePanel({ display, width, showTips, dataPlane = 'claude', }
132
137
  // conversation content. Keeps the same product/model/cwd facts as WelcomePanel
133
138
  // in a small fixed footprint, truncating every line to the supplied width.
134
139
  export function SessionIdentity({ display, width, }) {
135
- const palette = useTuiPalette();
140
+ const theme = useTuiTheme();
136
141
  const identityWidth = Math.max(1, Math.floor(width));
137
142
  const model = display.model ?? 'provider default';
138
143
  const effort = display.effort ? ` · ${display.effort} effort` : '';
139
144
  const cwd = compactPath(display.cwd);
140
- return (_jsxs(Box, { flexDirection: "column", width: identityWidth, children: [_jsx(Text, { wrap: "truncate-end", children: _jsx(Text, { color: palette.brand, bold: true, children: `Praxis Code v${display.version}` }) }), _jsxs(Text, { wrap: "truncate-end", children: [model, effort] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: cwd })] }));
145
+ return (_jsxs(Box, { flexDirection: "column", width: identityWidth, children: [_jsx(Text, { wrap: "truncate-end", children: _jsx(Text, { ...theme.text.productIdentity, bold: true, children: `Praxis Code v${display.version}` }) }), _jsxs(Text, { wrap: "truncate-end", children: [model, effort] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: cwd })] }));
141
146
  }
142
147
  const INLINE_SEGMENT_CACHE_MAX = 4096;
143
148
  const inlineSegmentCache = new Map();
@@ -168,14 +173,14 @@ function cachedInlineSegments(text) {
168
173
  }
169
174
  return segments;
170
175
  }
171
- function inlineTextElement(text, palette) {
176
+ function inlineTextElement(text, theme) {
172
177
  return (_jsx(Text, { children: cachedInlineSegments(text).map((segment, index) => {
173
178
  if (segment.kind === 'code')
174
- return (_jsx(Text, { color: palette.info, children: segment.text }, index));
179
+ return (_jsx(Text, { ...theme.text.info, children: segment.text }, index));
175
180
  if (segment.kind === 'bold')
176
181
  return (_jsx(Text, { bold: true, children: segment.text }, index));
177
182
  if (segment.kind === 'link')
178
- return (_jsx(Text, { color: palette.link, underline: true, children: segment.text }, index));
183
+ return (_jsx(Text, { ...theme.text.link, underline: true, children: segment.text }, index));
179
184
  return segment.text;
180
185
  }) }));
181
186
  }
@@ -211,35 +216,40 @@ function cachedMarkdownLineShape(line) {
211
216
  return shape;
212
217
  }
213
218
  const ELEMENT_CACHE_KEY_SEPARATOR = String.fromCharCode(0);
214
- function markdownLineStyleSignature(palette) {
215
- return [palette.accent, palette.brand, palette.info, palette.link].join(ELEMENT_CACHE_KEY_SEPARATOR);
219
+ function markdownLineStyleSignature(theme) {
220
+ return [
221
+ theme.text.focusMarker.color,
222
+ theme.text.productIdentity.color,
223
+ theme.text.info.color,
224
+ theme.text.link.color,
225
+ ].join(ELEMENT_CACHE_KEY_SEPARATOR);
216
226
  }
217
227
  const MARKDOWN_LINE_ELEMENT_CACHE_MAX = 4096;
218
228
  const markdownLineElementCache = new Map();
219
- function buildMarkdownLine(line, palette) {
229
+ function buildMarkdownLine(line, theme) {
220
230
  const shape = cachedMarkdownLineShape(line);
221
231
  if (shape.role === 'h3')
222
- return _jsx(Text, { bold: true, children: inlineTextElement(shape.content, palette) });
232
+ return _jsx(Text, { bold: true, children: inlineTextElement(shape.content, theme) });
223
233
  if (shape.role === 'h2')
224
- return (_jsx(Text, { bold: true, color: palette.accent, children: inlineTextElement(shape.content, palette) }));
234
+ return (_jsx(Text, { bold: true, ...theme.text.focusMarker, children: inlineTextElement(shape.content, theme) }));
225
235
  if (shape.role === 'h1')
226
- return (_jsx(Text, { bold: true, color: palette.brand, children: inlineTextElement(shape.content, palette) }));
236
+ return (_jsx(Text, { bold: true, ...theme.text.productIdentity, children: inlineTextElement(shape.content, theme) }));
227
237
  if (shape.role === 'bullet')
228
- return (_jsxs(Text, { children: [' • ', inlineTextElement(shape.content, palette)] }));
238
+ return (_jsxs(Text, { children: [' • ', inlineTextElement(shape.content, theme)] }));
229
239
  if (shape.role === 'ordered')
230
- return (_jsxs(Text, { children: [' ', inlineTextElement(shape.content, palette)] }));
240
+ return (_jsxs(Text, { children: [' ', inlineTextElement(shape.content, theme)] }));
231
241
  if (shape.role === 'quote')
232
- return (_jsxs(Text, { dimColor: true, children: ['│ ', inlineTextElement(shape.content, palette)] }));
242
+ return (_jsxs(Text, { dimColor: true, children: ['│ ', inlineTextElement(shape.content, theme)] }));
233
243
  if (shape.role === 'plain')
234
- return inlineTextElement(shape.content, palette);
244
+ return inlineTextElement(shape.content, theme);
235
245
  return _jsx(Text, { children: " " });
236
246
  }
237
- function cachedMarkdownLineElement(line, palette) {
238
- const key = [line, markdownLineStyleSignature(palette)].join(ELEMENT_CACHE_KEY_SEPARATOR);
247
+ function cachedMarkdownLineElement(line, theme) {
248
+ const key = [line, markdownLineStyleSignature(theme)].join(ELEMENT_CACHE_KEY_SEPARATOR);
239
249
  const cached = markdownLineElementCache.get(key);
240
250
  if (cached)
241
251
  return cached;
242
- const element = buildMarkdownLine(line, palette);
252
+ const element = buildMarkdownLine(line, theme);
243
253
  markdownLineElementCache.set(key, element);
244
254
  if (markdownLineElementCache.size > MARKDOWN_LINE_ELEMENT_CACHE_MAX) {
245
255
  const oldest = markdownLineElementCache.keys().next().value;
@@ -271,6 +281,7 @@ const CODE_KEYWORDS = new Set([
271
281
  const CODE_TOKEN_PATTERN = /("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\b(?:async|await|class|const|else|export|false|from|function|if|import|let|new|null|return|true|undefined|var)\b|\b[A-Za-z_$][\w$]*(?=\s*\())/gu;
272
282
  const SYNTAX_TOKEN_CACHE_MAX = 2048;
273
283
  const syntaxTokenCache = new Map();
284
+ const syntaxStyleSignatureCache = new WeakMap();
274
285
  function cachedSyntaxTokens(text) {
275
286
  const cached = syntaxTokenCache.get(text);
276
287
  if (cached)
@@ -293,37 +304,42 @@ function cachedSyntaxTokens(text) {
293
304
  }
294
305
  return tokens;
295
306
  }
296
- function syntaxStyleSignature(palette) {
297
- const syntax = palette.syntax;
298
- return [
299
- palette.syntaxHighlightingDisabled ? '1' : '0',
300
- syntax.text,
301
- syntax.keyword,
302
- syntax.identifier,
303
- syntax.string,
304
- syntax.removedBackground ?? '',
305
- syntax.addedBackground ?? '',
307
+ function syntaxStyleSignature(theme) {
308
+ const cached = syntaxStyleSignatureCache.get(theme);
309
+ if (cached)
310
+ return cached;
311
+ const signature = [
312
+ theme.syntaxHighlightingDisabled ? '1' : '0',
313
+ JSON.stringify(theme.syntax('text')),
314
+ JSON.stringify(theme.syntax('keyword')),
315
+ JSON.stringify(theme.syntax('identifier')),
316
+ JSON.stringify(theme.syntax('string')),
317
+ JSON.stringify(theme.syntax('text', 'removed')),
318
+ JSON.stringify(theme.syntax('text', 'added')),
319
+ JSON.stringify(theme.syntax('addedHighlight')),
306
320
  ].join(ELEMENT_CACHE_KEY_SEPARATOR);
321
+ syntaxStyleSignatureCache.set(theme, signature);
322
+ return signature;
307
323
  }
308
324
  const SYNTAX_LINE_ELEMENT_CACHE_MAX = 4096;
309
325
  const syntaxLineElementCache = new Map();
310
- function buildSyntaxCodeLine(text, prefix, change, palette) {
311
- if (palette.syntaxHighlightingDisabled) {
326
+ function buildSyntaxCodeLine(text, prefix, change, theme) {
327
+ if (theme.syntaxHighlightingDisabled) {
312
328
  return (_jsxs(Text, { children: [prefix, text || ' '] }));
313
329
  }
314
- const lineStyle = tuiSyntaxStyle(palette, 'text', change);
330
+ const lineStyle = tuiSyntaxStyle(theme, 'text', change);
315
331
  return (_jsxs(Text, { children: [prefix, _jsx(Text, { ...lineStyle, children: cachedSyntaxTokens(text).map(({ kind, token }, index) => {
316
332
  if (kind === 'text')
317
333
  return token;
318
- return (_jsx(Text, { ...tuiSyntaxStyle(palette, kind), children: token }, index));
334
+ return (_jsx(Text, { ...tuiSyntaxStyle(theme, kind), children: token }, index));
319
335
  }) })] }));
320
336
  }
321
- function cachedSyntaxCodeLine(text, prefix, change, palette) {
322
- const key = [text, prefix, change ?? '', syntaxStyleSignature(palette)].join(ELEMENT_CACHE_KEY_SEPARATOR);
337
+ function cachedSyntaxCodeLine(text, prefix, change, theme) {
338
+ const key = [text, prefix, change ?? '', syntaxStyleSignature(theme)].join(ELEMENT_CACHE_KEY_SEPARATOR);
323
339
  const cached = syntaxLineElementCache.get(key);
324
340
  if (cached)
325
341
  return cached;
326
- const element = buildSyntaxCodeLine(text, prefix, change, palette);
342
+ const element = buildSyntaxCodeLine(text, prefix, change, theme);
327
343
  syntaxLineElementCache.set(key, element);
328
344
  if (syntaxLineElementCache.size > SYNTAX_LINE_ELEMENT_CACHE_MAX) {
329
345
  const oldest = syntaxLineElementCache.keys().next().value;
@@ -333,14 +349,12 @@ function cachedSyntaxCodeLine(text, prefix, change, palette) {
333
349
  return element;
334
350
  }
335
351
  function SyntaxCodeLine({ text, prefix = '', change, }) {
336
- const palette = useTuiPalette();
337
- return cachedSyntaxCodeLine(text, prefix, change, palette);
352
+ const theme = useTuiTheme();
353
+ return cachedSyntaxCodeLine(text, prefix, change, theme);
338
354
  }
339
355
  function ToolResultText({ text, prefix = '', }) {
340
- const palette = useTuiPalette();
341
- return (_jsx(Box, { flexDirection: "column", children: text.split('\n').map((line, index) => line.startsWith('+') && !line.startsWith('+++') ? (_jsx(SyntaxCodeLine, { prefix: index === 0 ? prefix : prefix ? ' ' : '', text: line, change: "added" }, index)) : line.startsWith('-') && !line.startsWith('---') ? (_jsx(SyntaxCodeLine, { prefix: index === 0 ? prefix : prefix ? ' ' : '', text: line, change: "removed" }, index)) : (_jsxs(Text, { ...(line.startsWith('@@')
342
- ? { color: palette.info }
343
- : { dimColor: true }), children: [index === 0 ? prefix : prefix ? ' ' : '', line || ' '] }, index))) }));
356
+ const theme = useTuiTheme();
357
+ return (_jsx(Box, { flexDirection: "column", children: text.split('\n').map((line, index) => line.startsWith('+') && !line.startsWith('+++') ? (_jsx(SyntaxCodeLine, { prefix: index === 0 ? prefix : prefix ? ' ' : '', text: line, change: "added" }, index)) : line.startsWith('-') && !line.startsWith('---') ? (_jsx(SyntaxCodeLine, { prefix: index === 0 ? prefix : prefix ? ' ' : '', text: line, change: "removed" }, index)) : (_jsxs(Text, { ...(line.startsWith('@@') ? theme.text.info : { dimColor: true }), children: [index === 0 ? prefix : prefix ? ' ' : '', line || ' '] }, index))) }));
344
358
  }
345
359
  function inputString(call, key) {
346
360
  const value = call.input[key];
@@ -362,7 +376,7 @@ function toolHeading(call) {
362
376
  return call.name;
363
377
  }
364
378
  function ToolTranscriptEntry({ call, detail, result, detailed, }) {
365
- const palette = useTuiPalette();
379
+ const theme = useTuiTheme();
366
380
  const displayResult = call.name === 'Read' &&
367
381
  result?.text.startsWith('Wasted call — file unchanged since your last Read')
368
382
  ? 'Unchanged since last read'
@@ -380,15 +394,24 @@ function ToolTranscriptEntry({ call, detail, result, detailed, }) {
380
394
  : displayResult;
381
395
  const oldLines = contentLines(inputString(call, 'old_string'));
382
396
  const newLines = contentLines(inputString(call, 'new_string'));
383
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: palette.accent, children: "\u23FA" }), ' ', _jsx(Text, { bold: true, children: toolHeading(call) })] }), !['Bash', 'Read', 'Edit'].includes(call.name) && detail ? (_jsxs(Text, { dimColor: true, children: [" ", detail] })) : null, result ? (_jsx(Box, { marginLeft: 2, flexDirection: "column", children: result.isError ? (_jsxs(Text, { color: palette.error, children: ["\u23BF Error: ", errorText] })) : call.name === 'Edit' ? (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: ["\u23BF Added ", newLines.length, " line", newLines.length === 1 ? '' : 's', ", removed ", oldLines.length, " line", oldLines.length === 1 ? '' : 's'] }), detailed &&
397
+ const markerStyle = result
398
+ ? result.isError
399
+ ? theme.text.error
400
+ : theme.text.success
401
+ : theme.text.active;
402
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { ...markerStyle, children: "\u23FA" }), ' ', _jsx(Text, { ...theme.text.heading, bold: true, children: toolHeading(call) })] }), !['Bash', 'Read', 'Edit'].includes(call.name) && detail ? (_jsxs(Text, { dimColor: true, children: [" ", detail] })) : null, result ? (_jsx(Box, { marginLeft: 2, flexDirection: "column", children: result.isError ? (_jsxs(Text, { ...theme.text.error, children: ["\u23BF Error: ", errorText] })) : call.name === 'Edit' ? (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: ["\u23BF Added ", newLines.length, " line", newLines.length === 1 ? '' : 's', ", removed ", oldLines.length, " line", oldLines.length === 1 ? '' : 's'] }), detailed &&
384
403
  oldLines.map((line, index) => (_jsx(SyntaxCodeLine, { prefix: ` ${index + 1} -`, text: line, change: "removed" }, `old-${index}`))), detailed &&
385
404
  newLines.map((line, index) => (_jsx(SyntaxCodeLine, { prefix: ` ${index + 1} +`, text: line, change: "added" }, `new-${index}`)))] })) : resultIsDiff ? (_jsx(ToolResultText, { text: visible.join('\n'), prefix: "\u23BF " })) : (_jsxs(_Fragment, { children: [visible.map((line, index) => (_jsxs(Text, { dimColor: true, children: [index === 0 ? '⎿ ' : ' ', line || ' '] }, index))), hidden > 0 ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2026 +", hidden, " lines (ctrl+o to expand)"] })) : null] })) })) : null] }));
386
405
  }
387
406
  function ThinkingBlock({ text, active, expanded, screenReader, }) {
388
- const palette = useTuiPalette();
407
+ const theme = useTuiTheme();
389
408
  const summary = text.replace(/\s+/gu, ' ').trim();
390
409
  const showFull = screenReader || active || expanded;
391
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { ...(!screenReader ? { color: palette.accent } : {}), dimColor: !screenReader, italic: !screenReader, children: [screenReader ? 'Thinking:' : '✻ ', active ? 'Thinking…' : 'Thought for a moment', !showFull && summary ? ` · ${summary.slice(0, 160)}` : ''] }), showFull && text ? (_jsx(Box, { marginLeft: screenReader ? 0 : 2, flexDirection: "column", children: _jsx(MarkdownText, { text: text }) })) : !screenReader && summary.length > 160 ? (_jsx(Text, { dimColor: true, children: " ctrl+o to expand thinking" })) : null] }));
410
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { ...(active
411
+ ? theme.text.active
412
+ : !screenReader
413
+ ? theme.text.focusMarker
414
+ : {}), dimColor: !screenReader, italic: !screenReader, children: [screenReader ? 'Thinking:' : '✻ ', active ? 'Thinking…' : 'Thought for a moment', !showFull && summary ? ` · ${summary.slice(0, 160)}` : ''] }), showFull && text ? (_jsx(Box, { marginLeft: screenReader ? 0 : 2, flexDirection: "column", children: _jsx(MarkdownText, { text: text }) })) : !screenReader && summary.length > 160 ? (_jsx(Text, { dimColor: true, children: " ctrl+o to expand thinking" })) : null] }));
392
415
  }
393
416
  function compactTokens(tokens) {
394
417
  if (tokens >= 1_000_000)
@@ -401,16 +424,17 @@ function percent(tokens, total) {
401
424
  return `${Math.round((tokens / Math.max(1, total)) * 100 * 10) / 10}%`;
402
425
  }
403
426
  function ContextUsageBlock({ usedTokens, contextWindowTokens, model, skills, memoryFiles, screenReader, }) {
427
+ const theme = useTuiTheme();
404
428
  const totalTokens = Math.max(1, contextWindowTokens);
405
429
  const compactBuffer = Math.round(totalTokens * 0.165);
406
430
  const usable = Math.max(1, totalTokens - compactBuffer);
407
431
  if (screenReader) {
408
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Context Usage" }), _jsxs(Text, { children: [model ?? 'provider default', " \u00B7 ", usedTokens.toLocaleString(), "/", totalTokens.toLocaleString(), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsxs(Text, { children: ["Autocompact buffer: ", compactBuffer.toLocaleString(), " tokens"] }), _jsxs(Text, { children: ["Skills: ", skills.map(({ name }) => name).join(', ') || 'none'] })] }));
432
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { ...theme.text.heading, children: "Context Usage" }), _jsxs(Text, { children: [model ?? 'provider default', " \u00B7 ", usedTokens.toLocaleString(), "/", totalTokens.toLocaleString(), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsxs(Text, { children: ["Autocompact buffer: ", compactBuffer.toLocaleString(), " tokens"] }), _jsxs(Text, { children: ["Skills: ", skills.map(({ name }) => name).join(', ') || 'none'] })] }));
409
433
  }
410
434
  const usedCells = Math.min(25, Math.round((usedTokens / totalTokens) * 25));
411
435
  const bufferCells = Math.min(25 - usedCells, Math.round((compactBuffer / totalTokens) * 25));
412
436
  const cells = Array.from({ length: 25 }, (_, index) => index < usedCells ? '⛁' : index >= 25 - bufferCells ? '⛝' : '⛶');
413
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { bold: true, children: "Context Usage" }), _jsxs(Box, { children: [_jsx(Box, { flexDirection: "column", marginRight: 2, children: Array.from({ length: 5 }, (_, row) => (_jsx(Text, { children: cells.slice(row * 5, row * 5 + 5).join(' ') }, row))) }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [model ?? 'provider default', " \u00B7 ", compactTokens(usedTokens), "/", compactTokens(totalTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, italic: true, children: "Estimated usage by category" }), _jsxs(Text, { children: ["\u26C1 Messages and other context: ", compactTokens(usedTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsxs(Text, { children: ["\u26F6 Free space: ", compactTokens(Math.max(0, usable - usedTokens)), " (", percent(Math.max(0, usable - usedTokens), totalTokens), ")"] }), _jsxs(Text, { children: ["\u26DD Autocompact buffer: ", compactTokens(compactBuffer), " tokens (", percent(compactBuffer, totalTokens), ")"] })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Memory files \u00B7 /memory" }), memoryFiles.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u2514 No memory files" })) : (memoryFiles.map((file, index) => (_jsxs(Text, { dimColor: true, children: [index === memoryFiles.length - 1 ? '└' : '├', " ", file.path, ":", ' ', file.tokens, " tokens"] }, file.path)))), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Skills \u00B7 /skills" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Loaded" }), skills.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u2514 No skills loaded" })) : (skills.map((skill, index) => (_jsxs(Text, { dimColor: true, children: [index === skills.length - 1 ? '└' : '├', " ", skill.name, ": ~", skill.tokens, " tokens"] }, skill.name))))] }));
437
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsx(Text, { ...theme.text.heading, bold: true, children: "Context Usage" }), _jsxs(Box, { children: [_jsx(Box, { flexDirection: "column", marginRight: 2, children: Array.from({ length: 5 }, (_, row) => (_jsx(Text, { children: cells.slice(row * 5, row * 5 + 5).join(' ') }, row))) }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [model ?? 'provider default', " \u00B7 ", compactTokens(usedTokens), "/", compactTokens(totalTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, italic: true, children: "Estimated usage by category" }), _jsxs(Text, { children: ["\u26C1 Messages and other context: ", compactTokens(usedTokens), " tokens (", percent(usedTokens, totalTokens), ")"] }), _jsxs(Text, { children: ["\u26F6 Free space: ", compactTokens(Math.max(0, usable - usedTokens)), " (", percent(Math.max(0, usable - usedTokens), totalTokens), ")"] }), _jsxs(Text, { children: ["\u26DD Autocompact buffer: ", compactTokens(compactBuffer), " tokens (", percent(compactBuffer, totalTokens), ")"] })] })] }), _jsx(Text, { children: " " }), _jsx(Text, { ...theme.text.heading, bold: true, children: "Memory files \u00B7 /memory" }), memoryFiles.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u2514 No memory files" })) : (memoryFiles.map((file, index) => (_jsxs(Text, { dimColor: true, children: [index === memoryFiles.length - 1 ? '└' : '├', " ", file.path, ":", ' ', file.tokens, " tokens"] }, file.path)))), _jsx(Text, { children: " " }), _jsx(Text, { ...theme.text.heading, bold: true, children: "Skills \u00B7 /skills" }), _jsx(Text, { children: " " }), _jsx(Text, { children: "Loaded" }), skills.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u2514 No skills loaded" })) : (skills.map((skill, index) => (_jsxs(Text, { dimColor: true, children: [index === skills.length - 1 ? '└' : '├', " ", skill.name, ": ~", skill.tokens, " tokens"] }, skill.name))))] }));
414
438
  }
415
439
  const MARKDOWN_TEXT_CACHE_MAX = 2048;
416
440
  const markdownTextCache = new Map();
@@ -439,33 +463,30 @@ function cachedMarkdownText(text) {
439
463
  }
440
464
  return parsed;
441
465
  }
442
- function markdownTextStyleSignature(palette) {
443
- return [
444
- syntaxStyleSignature(palette),
445
- markdownLineStyleSignature(palette),
446
- ].join(ELEMENT_CACHE_KEY_SEPARATOR);
466
+ function markdownTextStyleSignature(theme) {
467
+ return [syntaxStyleSignature(theme), markdownLineStyleSignature(theme)].join(ELEMENT_CACHE_KEY_SEPARATOR);
447
468
  }
448
469
  const MARKDOWN_TEXT_ELEMENT_CACHE_MAX = 4096;
449
470
  const markdownTextElementCache = new Map();
450
- function buildMarkdownText(text, palette) {
471
+ function buildMarkdownText(text, theme) {
451
472
  return (_jsx(Box, { flexDirection: "column", children: cachedMarkdownText(text).map((line, index) => {
452
473
  if (line.type === 'fence-open')
453
474
  return (_jsx(Text, { dimColor: true, children: `╭─ ${line.label}` }, index));
454
475
  if (line.type === 'fence-close')
455
476
  return (_jsx(Text, { dimColor: true, children: '╰─' }, index));
456
477
  if (line.type === 'code')
457
- return cloneElement(cachedSyntaxCodeLine(line.text, '│ ', undefined, palette), { key: index });
458
- return cloneElement(cachedMarkdownLineElement(line.line, palette), {
478
+ return cloneElement(cachedSyntaxCodeLine(line.text, '│ ', undefined, theme), { key: index });
479
+ return cloneElement(cachedMarkdownLineElement(line.line, theme), {
459
480
  key: index,
460
481
  });
461
482
  }) }));
462
483
  }
463
- function cachedMarkdownTextElement(text, palette) {
464
- const key = [text, markdownTextStyleSignature(palette)].join(ELEMENT_CACHE_KEY_SEPARATOR);
484
+ function cachedMarkdownTextElement(text, theme) {
485
+ const key = [text, markdownTextStyleSignature(theme)].join(ELEMENT_CACHE_KEY_SEPARATOR);
465
486
  const cached = markdownTextElementCache.get(key);
466
487
  if (cached)
467
488
  return cached;
468
- const element = buildMarkdownText(text, palette);
489
+ const element = buildMarkdownText(text, theme);
469
490
  markdownTextElementCache.set(key, element);
470
491
  if (markdownTextElementCache.size > MARKDOWN_TEXT_ELEMENT_CACHE_MAX) {
471
492
  const oldest = markdownTextElementCache.keys().next().value;
@@ -475,8 +496,8 @@ function cachedMarkdownTextElement(text, palette) {
475
496
  return element;
476
497
  }
477
498
  export function MarkdownText({ text }) {
478
- const palette = useTuiPalette();
479
- return cachedMarkdownTextElement(text, palette);
499
+ const theme = useTuiTheme();
500
+ return cachedMarkdownTextElement(text, theme);
480
501
  }
481
502
  // Bounded streaming-text presentation. While a turn is in progress the active
482
503
  // assistant text grows on every frame; re-parsing the entire Markdown document
@@ -516,94 +537,42 @@ function ActiveStreamText({ text }) {
516
537
  const { stableText, pendingText, truncated } = activeStreamWindow(text);
517
538
  return (_jsxs(Box, { flexDirection: "column", children: [truncated ? _jsx(Text, { dimColor: true, children: "\u2026 earlier streaming content \u2026" }) : null, stableText ? _jsx(MarkdownText, { text: stableText }) : null, pendingText ? _jsx(Text, { children: pendingText }) : null] }));
518
539
  }
519
- export function Transcript({ items, activeText, activeThinking = '', activeStreamVisible = true, thinkingExpanded = false, detailedTranscript = false, screenReader, }) {
520
- const palette = useTuiPalette();
540
+ export function Transcript({ entries, activeText, activeThinking = '', activeStreamVisible = true, thinkingExpanded = false, detailedTranscript = false, screenReader, }) {
541
+ const theme = useTuiTheme();
521
542
  const detailed = thinkingExpanded || detailedTranscript;
522
- const results = new Map(items
523
- .filter((item) => item.kind === 'tool-result')
524
- .map((item) => [item.callId, item]));
525
- const pairedResults = new Set(items
526
- .filter((item) => item.kind === 'tool' && results.has(item.call.id))
527
- .map((item) => item.call.id));
528
- const shellResults = new Map(items
529
- .filter((item) => item.kind === 'shell-result')
530
- .map((item) => [item.callId, item]));
531
- const pairedShellResults = new Set(items
532
- .filter((item) => item.kind === 'shell' && shellResults.has(item.callId))
533
- .map((item) => item.callId));
534
- const readGroupStarts = new Map();
535
- const groupedReadTools = new Set();
536
- if (!detailed && !screenReader) {
537
- for (let index = 0; index < items.length; index += 1) {
538
- const first = items[index];
539
- if (first?.kind !== 'tool' || first.call.name !== 'Read')
540
- continue;
541
- let cursor = index;
542
- const memberIndexes = [];
543
- const memberCallIds = new Set();
544
- while (cursor < items.length) {
545
- const candidate = items[cursor];
546
- if (candidate?.kind === 'tool' && candidate.call.name === 'Read') {
547
- const result = results.get(candidate.call.id);
548
- if (!result || result.isError)
549
- break;
550
- memberIndexes.push(cursor);
551
- memberCallIds.add(candidate.call.id);
552
- cursor += 1;
553
- continue;
554
- }
555
- if (candidate?.kind === 'tool-result' &&
556
- memberCallIds.has(candidate.callId)) {
557
- cursor += 1;
558
- continue;
559
- }
560
- break;
561
- }
562
- if (memberIndexes.length === 0)
563
- continue;
564
- readGroupStarts.set(index, memberIndexes.length);
565
- for (const memberIndex of memberIndexes.slice(1)) {
566
- groupedReadTools.add(memberIndex);
567
- }
568
- index = cursor - 1;
569
- }
570
- }
571
- return (_jsxs(Box, { flexDirection: "column", children: [items.map((item, index) => {
572
- const readCount = readGroupStarts.get(index);
573
- if (readCount !== undefined) {
574
- return (_jsxs(Text, { children: [' ', "Read ", readCount, " file", readCount === 1 ? '' : 's', ' ', _jsx(Text, { dimColor: true, children: "(ctrl+o to expand)" })] }, index));
543
+ return (_jsxs(Box, { flexDirection: "column", children: [entries.map((entry, index) => {
544
+ if (entry.kind === 'read-summary') {
545
+ return (_jsxs(Text, { children: [' ', "Read ", entry.count, " file", entry.count === 1 ? '' : 's', ' ', _jsx(Text, { dimColor: true, children: "(ctrl+o to expand)" })] }, entry.key));
575
546
  }
576
- if (groupedReadTools.has(index))
577
- return null;
547
+ const item = entry.item;
578
548
  if (item.kind === 'user') {
579
- return (_jsxs(Box, { marginTop: index === 0 ? 0 : 1, children: [_jsx(Text, { color: palette.brand, bold: true, children: screenReader ? 'You: ' : '❯ ' }), _jsx(Text, { bold: true, children: item.text })] }, index));
549
+ return (_jsxs(Box, { marginTop: index === 0 ? 0 : 1, children: [_jsx(Text, { ...theme.text.productIdentity, bold: true, children: screenReader ? 'You: ' : '❯ ' }), _jsx(Text, { bold: true, children: item.text })] }, entry.key));
580
550
  }
581
551
  if (item.kind === 'assistant') {
582
- return (_jsxs(Box, { marginTop: 1, children: [screenReader ? _jsx(Text, { children: "Praxis:" }) : null, !screenReader ? _jsx(Text, { color: palette.accent, children: "\u23FA " }) : null, _jsx(MarkdownText, { text: item.text })] }, index));
552
+ return (_jsxs(Box, { marginTop: 1, children: [screenReader ? (_jsx(Text, { ...theme.text.heading, children: "Praxis:" })) : null, !screenReader ? (_jsx(Text, { ...theme.text.focusMarker, children: "\u23FA " })) : null, _jsx(MarkdownText, { text: item.text })] }, entry.key));
583
553
  }
584
554
  if (item.kind === 'thinking') {
585
- return (_jsx(ThinkingBlock, { text: item.text, active: false, expanded: detailed, screenReader: screenReader }, index));
555
+ return (_jsx(ThinkingBlock, { text: item.text, active: false, expanded: detailed, screenReader: screenReader }, entry.key));
586
556
  }
587
557
  if (item.kind === 'compact') {
588
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: palette.accent, italic: true, children: screenReader
558
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { ...theme.text.focusMarker, italic: true, children: screenReader
589
559
  ? 'Conversation compacted'
590
- : '✻ Conversation compacted (ctrl+o for history)' }), detailed ? (_jsx(Box, { marginLeft: screenReader ? 0 : 2, children: _jsx(MarkdownText, { text: item.summary }) })) : null] }, index));
560
+ : '✻ Conversation compacted (ctrl+o for history)' }), detailed ? (_jsx(Box, { marginLeft: screenReader ? 0 : 2, children: _jsx(MarkdownText, { text: item.summary }) })) : null] }, entry.key));
591
561
  }
592
562
  if (item.kind === 'context') {
593
- return (_jsx(ContextUsageBlock, { ...item, screenReader: screenReader }, index));
563
+ return (_jsx(ContextUsageBlock, { ...item, screenReader: screenReader }, entry.key));
594
564
  }
595
565
  if (item.kind === 'tool') {
596
- const result = results.get(item.call.id);
597
- return (_jsx(ToolTranscriptEntry, { call: item.call, detail: item.detail, ...(result ? { result } : {}), detailed: detailed || screenReader }, index));
566
+ return (_jsx(ToolTranscriptEntry, { call: item.call, detail: item.detail, ...(entry.kind === 'tool' && entry.result
567
+ ? { result: entry.result }
568
+ : {}), detailed: detailed || screenReader }, entry.key));
598
569
  }
599
570
  if (item.kind === 'tool-result') {
600
- if (pairedResults.has(item.callId))
601
- return null;
602
571
  const text = item.text.length > 500 ? `${item.text.slice(0, 497)}...` : item.text;
603
- return (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { color: item.isError ? palette.error : palette.muted, children: item.isError ? '└ Error' : '└ Result' }), item.isError ? (_jsx(Text, { color: palette.error, children: text })) : (_jsx(ToolResultText, { text: text }))] }, index));
572
+ return (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { ...(item.isError ? theme.text.error : theme.text.muted), children: item.isError ? '└ Error' : '└ Result' }), item.isError ? (_jsx(Text, { ...theme.text.error, children: text })) : (_jsx(ToolResultText, { text: text }))] }, entry.key));
604
573
  }
605
574
  if (item.kind === 'shell') {
606
- const result = shellResults.get(item.callId);
575
+ const result = entry.kind === 'shell' ? entry.result : undefined;
607
576
  const output = result
608
577
  ? [result.stdout, result.stderr]
609
578
  .filter(Boolean)
@@ -616,22 +585,20 @@ export function Transcript({ items, activeText, activeThinking = '', activeStrea
616
585
  const lines = contentLines(output);
617
586
  const visible = detailed || screenReader ? lines : lines.slice(0, 3);
618
587
  const hidden = lines.length - visible.length;
619
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { children: _jsxs(Text, { bold: true, children: [screenReader ? 'Shell command: ' : '! ', item.command] }) }), result ? (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [lines.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u23BF " })) : (visible.map((line, lineIndex) => (_jsxs(Text, { ...(result.isError ? { color: palette.error } : {}), dimColor: !result.isError, children: [lineIndex === 0 ? '⎿ ' : ' ', line || ' '] }, lineIndex)))), hidden > 0 ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2026 +", hidden, " lines (ctrl+o to expand)"] })) : null] })) : null] }, index));
588
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { children: _jsxs(Text, { bold: true, children: [screenReader ? 'Shell command: ' : '! ', item.command] }) }), result ? (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [lines.length === 0 ? (_jsx(Text, { dimColor: true, children: "\u23BF " })) : (visible.map((line, lineIndex) => (_jsxs(Text, { ...(result.isError ? theme.text.error : {}), dimColor: !result.isError, children: [lineIndex === 0 ? '⎿ ' : ' ', line || ' '] }, lineIndex)))), hidden > 0 ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2026 +", hidden, " lines (ctrl+o to expand)"] })) : null] })) : null] }, entry.key));
620
589
  }
621
590
  if (item.kind === 'shell-result') {
622
- if (pairedShellResults.has(item.callId))
623
- return null;
624
591
  const output = [item.stdout, item.stderr].filter(Boolean).join('\n');
625
- return (_jsxs(Text, { ...(item.isError ? { color: palette.error } : {}), children: ["\u23BF ", output] }, index));
592
+ return (_jsxs(Text, { ...(item.isError ? theme.text.error : {}), children: ["\u23BF ", output] }, entry.key));
626
593
  }
627
594
  if (item.kind === 'local-result') {
628
- return (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["\u23BF ", item.text] }) }, index));
595
+ return (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["\u23BF ", item.text] }) }, entry.key));
629
596
  }
630
- return (_jsxs(Text, { ...(item.kind === 'warning' ? { color: palette.error } : {}), dimColor: item.kind === 'notice', children: [item.kind === 'warning' ? '⚠ ' : '· ', item.text] }, index));
631
- }), activeStreamVisible && activeThinking ? (_jsx(ThinkingBlock, { text: activeThinking, active: true, expanded: detailed, screenReader: screenReader })) : activeStreamVisible && activeText ? (_jsxs(Box, { marginTop: 1, children: [screenReader ? (_jsx(Text, { children: "Praxis: " })) : (_jsx(Text, { color: palette.accent, children: "\u2733 " })), screenReader ? (_jsx(MarkdownText, { text: activeText })) : (_jsx(ActiveStreamText, { text: activeText }))] })) : null] }));
597
+ return (_jsxs(Text, { ...(item.kind === 'warning' ? theme.text.error : {}), dimColor: item.kind === 'notice', children: [item.kind === 'warning' ? '⚠ ' : '· ', item.text] }, entry.key));
598
+ }), activeStreamVisible && activeThinking ? (_jsx(ThinkingBlock, { text: activeThinking, active: true, expanded: detailed, screenReader: screenReader })) : activeStreamVisible && activeText ? (_jsxs(Box, { marginTop: 1, children: [screenReader ? (_jsx(Text, { ...theme.text.active, children: "Praxis: " })) : (_jsx(Text, { ...theme.text.active, children: "\u2733 " })), screenReader ? (_jsx(MarkdownText, { text: activeText })) : (_jsx(ActiveStreamText, { text: activeText }))] })) : null] }));
632
599
  }
633
600
  export function DiffDashboard({ snapshots, sourceIndex, selectedIndex, viewingFile, scrollOffset, width, screenReader, }) {
634
- const palette = useTuiPalette();
601
+ const theme = useTuiTheme();
635
602
  const source = snapshots[sourceIndex];
636
603
  const snapshot = source?.snapshot ?? { files: [], additions: 0, deletions: 0 };
637
604
  const selected = snapshot.files[selectedIndex];
@@ -639,11 +606,12 @@ export function DiffDashboard({ snapshots, sourceIndex, selectedIndex, viewingFi
639
606
  const line = '─'.repeat(panelWidth);
640
607
  if (viewingFile && selected) {
641
608
  const lines = visiblePatchLines(selected.patch).slice(scrollOffset, scrollOffset + 18);
642
- return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsx(Text, { bold: true, children: " Uncommitted changes (git diff HEAD)" }), _jsxs(Text, { children: [' ', snapshots.map((item, index) => (_jsxs(Text, { inverse: !screenReader && index === sourceIndex, children: [screenReader && index === sourceIndex ? 'Current source: ' : ' ', item.label, ' '] }, item.label)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: [" ", selected.path] }), !screenReader ? (_jsxs(Text, { dimColor: true, children: [" ", '─'.repeat(Math.max(1, panelWidth - 4))] })) : null, lines.map((patchLine, index) => patchLine.startsWith('+') && !patchLine.startsWith('+++') ? (_jsx(SyntaxCodeLine, { prefix: " ", text: patchLine, change: "added" }, `${scrollOffset}-${index}`)) : patchLine.startsWith('-') && !patchLine.startsWith('---') ? (_jsx(SyntaxCodeLine, { prefix: " ", text: patchLine, change: "removed" }, `${scrollOffset}-${index}`)) : (_jsxs(Text, { dimColor: true, children: [' ', patchLine] }, `${scrollOffset}-${index}`))), _jsx(Text, { dimColor: true, children: " \u2191/\u2193 to scroll \u00B7 Esc to back" })] }));
609
+ return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', "Uncommitted changes (git diff HEAD)"] }), _jsxs(Text, { children: [' ', snapshots.map((item, index) => (_jsxs(Text, { ...(index === sourceIndex ? theme.text.selectedTab : {}), children: [screenReader && index === sourceIndex ? 'Current source: ' : ' ', item.label, ' '] }, item.label)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', selected.path] }), !screenReader ? (_jsxs(Text, { dimColor: true, children: [" ", '─'.repeat(Math.max(1, panelWidth - 4))] })) : null, lines.map((patchLine, index) => patchLine.startsWith('+') && !patchLine.startsWith('+++') ? (_jsx(SyntaxCodeLine, { prefix: " ", text: patchLine, change: "added" }, `${scrollOffset}-${index}`)) : patchLine.startsWith('-') && !patchLine.startsWith('---') ? (_jsx(SyntaxCodeLine, { prefix: " ", text: patchLine, change: "removed" }, `${scrollOffset}-${index}`)) : (_jsxs(Text, { dimColor: true, children: [' ', patchLine] }, `${scrollOffset}-${index}`))), _jsx(Text, { dimColor: true, children: " \u2191/\u2193 to scroll \u00B7 Esc to back" })] }));
643
610
  }
644
- return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsx(Text, { bold: true, children: " Uncommitted changes (git diff HEAD)" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', snapshots.map((item, index) => (_jsxs(Text, { inverse: !screenReader && index === sourceIndex, children: [screenReader && index === sourceIndex ? 'Current source: ' : ' ', item.label, ' '] }, item.label)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', snapshot.files.length, " file", snapshot.files.length === 1 ? '' : 's', ' ', "changed ", _jsxs(Text, { color: palette.success, children: ["+", snapshot.additions] }), ' ', _jsxs(Text, { color: palette.error, children: ["-", snapshot.deletions] })] }), _jsx(Text, { children: " " }), snapshot.files.length === 0 ? (_jsx(Text, { dimColor: true, children: " No uncommitted changes." })) : (snapshot.files.map((file, index) => (_jsxs(Text, { inverse: !screenReader && index === selectedIndex, children: [selectionPrefix(index === selectedIndex, screenReader), file.path, ' ', _jsxs(Text, { ...(!screenReader ? { color: palette.success } : {}), children: ["+", file.additions] }), ' ', _jsxs(Text, { ...(!screenReader ? { color: palette.error } : {}), children: ["-", file.deletions] })] }, file.path)))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192 to switch source \u00B7 \u2191/\u2193 to select \u00B7 Enter to view \u00B7 Esc to close" })] }));
611
+ return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', "Uncommitted changes (git diff HEAD)"] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', snapshots.map((item, index) => (_jsxs(Text, { ...(index === sourceIndex ? theme.text.selectedTab : {}), children: [screenReader && index === sourceIndex ? 'Current source: ' : ' ', item.label, ' '] }, item.label)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', snapshot.files.length, " file", snapshot.files.length === 1 ? '' : 's', ' ', "changed ", _jsxs(Text, { ...theme.text.diffAdded, children: ["+", snapshot.additions] }), ' ', _jsxs(Text, { ...theme.text.diffRemoved, children: ["-", snapshot.deletions] })] }), _jsx(Text, { children: " " }), snapshot.files.length === 0 ? (_jsx(Text, { dimColor: true, children: " No uncommitted changes." })) : (snapshot.files.map((file, index) => (_jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), children: [selectionPrefix(index === selectedIndex, screenReader), file.path, ' ', _jsxs(Text, { ...(!screenReader ? theme.text.diffAdded : {}), children: ["+", file.additions] }), ' ', _jsxs(Text, { ...(!screenReader ? theme.text.diffRemoved : {}), children: ["-", file.deletions] })] }, file.path)))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192 to switch source \u00B7 \u2191/\u2193 to select \u00B7 Enter to view \u00B7 Esc to close" })] }));
645
612
  }
646
613
  export function PermissionDashboard({ tabIndex, selectedIndex, query, rules, recentDenied, retryingDeniedId, workspaceDirectories, width, screenReader, }) {
614
+ const theme = useTuiTheme();
647
615
  const tabs = ['Recently denied', 'Allow', 'Ask', 'Deny', 'Workspace'];
648
616
  const behavior = ['allow', 'ask', 'deny'][tabIndex - 1];
649
617
  const normalizedQuery = query.trim().toLowerCase();
@@ -667,7 +635,9 @@ export function PermissionDashboard({ tabIndex, selectedIndex, query, rules, rec
667
635
  : tabIndex === 3
668
636
  ? 'Praxis Code will reject requests to use denied tools.'
669
637
  : 'Praxis Code can read files in the workspace, and make edits when auto-accept edits is on.';
670
- return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, _jsx(Text, { bold: true, children: " Permissions" }), _jsxs(Text, { children: [' ', tabs.map((tab, index) => (_jsxs(Text, { inverse: !screenReader && index === tabIndex, children: [screenReader && index === tabIndex ? 'Current tab: ' : ' ', tab, ' '] }, tab)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", description] }), tabIndex >= 1 && tabIndex <= 3 ? (_jsx(Box, { borderStyle: screenReader ? undefined : 'round', paddingX: 1, marginY: 1, children: _jsxs(Text, { ...(query ? {} : { dimColor: true }), children: [screenReader ? 'Search: ' : '⌕ ', query || 'Search…'] }) })) : (_jsx(Text, { children: " " })), tabIndex >= 1 && tabIndex <= 3 ? (['Add a new rule…', ...rows].map((row, index) => (_jsxs(Text, { inverse: !screenReader && index === selectedIndex, children: [selectionPrefix(index === selectedIndex, screenReader), index + 1, ". ", row] }, `${index}-${row}`)))) : tabIndex === 0 ? (recentDenied.map((action, index) => (_jsxs(Text, { inverse: !screenReader && index === selectedIndex, children: [selectionPrefix(index === selectedIndex, screenReader), index + 1, ". ", action.id === retryingDeniedId ? '✔' : '✘', ' ', action.display, action.id === retryingDeniedId ? ' (retry)' : '', action.reason ? ` ${action.reason}` : ''] }, action.id)))) : tabIndex === 4 ? (_jsxs(_Fragment, { children: [originalWorkspace ? (_jsxs(Text, { children: [' - ', originalWorkspace.path, " (Original working directory)"] })) : null, additionalWorkspaces.map((directory, index) => (_jsxs(Text, { inverse: !screenReader && selectedIndex === index, children: [selectionPrefix(selectedIndex === index, screenReader), index + 1, ". ", directory.path] }, directory.path))), _jsxs(Text, { inverse: !screenReader && selectedIndex === additionalWorkspaces.length, children: [selectionPrefix(selectedIndex === additionalWorkspaces.length, screenReader), additionalWorkspaces.length + 1, ". Add directory\u2026"] })] })) : rows.length > 0 ? (rows.map((row, index) => (_jsxs(Text, { inverse: !screenReader && index === selectedIndex, children: [selectionPrefix(index === selectedIndex, screenReader), index + 1, ". ", row] }, `${index}-${row}`)))) : null, _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: tabIndex === 0 && recentDenied.length > 0
638
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, _jsxs(Text, { ...theme.text.navigation, bold: true, children: [' ', "Permissions"] }), _jsxs(Text, { children: [' ', tabs.map((tab, index) => (_jsxs(Text, { ...(index === tabIndex ? theme.text.selectedTab : {}), children: [screenReader && index === tabIndex ? 'Current tab: ' : ' ', tab, ' '] }, tab)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", description] }), tabIndex >= 1 && tabIndex <= 3 ? (_jsx(Box, { borderStyle: screenReader ? undefined : 'round', paddingX: 1, marginY: 1, children: _jsxs(Text, { ...(query ? {} : { dimColor: true }), children: [screenReader ? 'Search: ' : '⌕ ', query || 'Search…'] }) })) : (_jsx(Text, { children: " " })), tabIndex >= 1 && tabIndex <= 3 ? (['Add a new rule…', ...rows].map((row, index) => (_jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), children: [selectionPrefix(index === selectedIndex, screenReader), index + 1, ". ", row] }, `${index}-${row}`)))) : tabIndex === 0 ? (recentDenied.map((action, index) => (_jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), children: [selectionPrefix(index === selectedIndex, screenReader), index + 1, ". ", action.id === retryingDeniedId ? '✔' : '✘', ' ', action.display, action.id === retryingDeniedId ? ' (retry)' : '', action.reason ? ` ${action.reason}` : ''] }, action.id)))) : tabIndex === 4 ? (_jsxs(_Fragment, { children: [originalWorkspace ? (_jsxs(Text, { children: [' - ', originalWorkspace.path, " (Original working directory)"] })) : null, additionalWorkspaces.map((directory, index) => (_jsxs(Text, { ...(selectedIndex === index ? theme.text.selectedRow : {}), children: [selectionPrefix(selectedIndex === index, screenReader), index + 1, ". ", directory.path] }, directory.path))), _jsxs(Text, { ...(selectedIndex === additionalWorkspaces.length
639
+ ? theme.text.selectedRow
640
+ : {}), children: [selectionPrefix(selectedIndex === additionalWorkspaces.length, screenReader), additionalWorkspaces.length + 1, ". Add directory\u2026"] })] })) : rows.length > 0 ? (rows.map((row, index) => (_jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), children: [selectionPrefix(index === selectedIndex, screenReader), index + 1, ". ", row] }, `${index}-${row}`)))) : null, _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: tabIndex === 0 && recentDenied.length > 0
671
641
  ? 'Enter to approve · r to retry · ↑/↓ to navigate · Esc to cancel'
672
642
  : selectedIndex >= 0
673
643
  ? '↑/↓ navigate · Enter to select · ←/→ to switch · Esc to cancel'
@@ -683,6 +653,7 @@ const THEME_OPTIONS = [
683
653
  { theme: 'light-ansi', label: 'Light mode (ANSI colors only)' },
684
654
  ];
685
655
  export function ThemePicker({ currentTheme, selectedIndex, syntaxHighlightingDisabled, customThemes = [], allowCustomThemes = true, width, screenReader, }) {
656
+ const theme = useTuiTheme();
686
657
  const options = [
687
658
  ...THEME_OPTIONS.map((option) => ({ ...option, customTheme: undefined })),
688
659
  ...customThemes.map((theme) => ({
@@ -702,43 +673,45 @@ export function ThemePicker({ currentTheme, selectedIndex, syntaxHighlightingDis
702
673
  ? 'dark'
703
674
  : (selected?.theme ??
704
675
  (currentTheme.startsWith('custom:') ? 'dark' : currentTheme));
705
- const preview = tuiPalette(previewTheme, syntaxHighlightingDisabled, process.env, selectedCustomTheme);
706
- const syntax = preview.syntax;
707
- const syntaxColor = (color) => syntaxHighlightingDisabled ? {} : { color };
708
- return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? _jsx(Text, { children: '▔'.repeat(Math.min(100, width)) }) : null, _jsx(Text, { bold: true, children: " Theme" }), _jsx(Text, { children: " " }), _jsx(Text, { children: " Choose the text style that looks best with your terminal" }), _jsx(Text, { children: " " }), options.map((option, index) => screenReader ? (_jsxs(Text, { children: [index + 1, ". ", option.label, option.theme === currentTheme ? ' (current)' : '', index === selectedIndex ? ' (focused)' : ''] }, option.theme)) : (_jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? ' ❯ ' : ' ', index + 1, ". ", option.label, option.theme === currentTheme ? ' ✔' : ''] }, option.theme))), _jsx(Text, { children: " " }), screenReader ? (_jsxs(Text, { children: ["Selected: ", options[selectedIndex]?.label] })) : null, !screenReader ? (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: [' ', '╌'.repeat(Math.max(1, Math.min(96, width - 3)))] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 1 " }), _jsx(Text, { ...syntaxColor(syntax.keyword), children: "function" }), _jsx(Text, { ...syntaxColor(syntax.text), children: " " }), _jsx(Text, { ...syntaxColor(syntax.identifier), children: "greet" }), _jsxs(Text, { ...syntaxColor(syntax.text), children: ["() ", '{'] })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 2 - " }), _jsx(Text, { ...syntaxColor(syntax.text), ...(syntaxHighlightingDisabled || !syntax.removedBackground
709
- ? {}
710
- : { backgroundColor: syntax.removedBackground }), children: ' console.log("Hello, World!"); ' })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 2 + " }), _jsxs(Text, { ...syntaxColor(syntax.text), ...(syntaxHighlightingDisabled || !syntax.addedBackground
711
- ? {}
712
- : { backgroundColor: syntax.addedBackground }), children: [' console.', _jsx(Text, { ...syntaxColor(syntax.identifier), children: "log" }), '(', _jsx(Text, { ...syntaxColor(syntax.string), children: "\"Hello, " }), _jsx(Text, { ...(syntaxHighlightingDisabled || !syntax.addedHighlight
713
- ? {}
714
- : { backgroundColor: syntax.addedHighlight }), children: "Claude" }), _jsx(Text, { ...syntaxColor(syntax.string), children: "!\"" }), '); '] })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 3 " }), _jsx(Text, { ...syntaxColor(syntax.text), children: '}' })] }), _jsxs(Text, { dimColor: true, children: [' ', '╌'.repeat(Math.max(1, Math.min(96, width - 3)))] })] })) : null, _jsxs(Text, { dimColor: true, children: [' ', selected?.theme === '__new__'
676
+ const preview = resolveTuiTheme({
677
+ theme: previewTheme,
678
+ syntaxHighlightingDisabled,
679
+ ...(selectedCustomTheme === undefined
680
+ ? {}
681
+ : { customTheme: selectedCustomTheme }),
682
+ }, { environment: process.env, screenReader });
683
+ const syntaxColor = (token, change) => preview.syntax(token, change);
684
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? _jsx(Text, { children: '▔'.repeat(Math.min(100, width)) }) : null, _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', "Theme"] }), _jsx(Text, { children: " " }), _jsx(Text, { children: " Choose the text style that looks best with your terminal" }), _jsx(Text, { children: " " }), options.map((option, index) => screenReader ? (_jsxs(Text, { children: [index + 1, ". ", option.label, option.theme === currentTheme ? ' (current)' : '', index === selectedIndex ? ' (focused)' : ''] }, option.theme)) : (_jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), children: [index === selectedIndex ? ' ❯ ' : ' ', index + 1, ". ", option.label, option.theme === currentTheme ? ' ✔' : ''] }, option.theme))), _jsx(Text, { children: " " }), screenReader ? (_jsxs(Text, { children: ["Selected: ", options[selectedIndex]?.label] })) : null, !screenReader ? (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: [' ', '╌'.repeat(Math.max(1, Math.min(96, width - 3)))] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 1 " }), _jsx(Text, { ...syntaxColor('keyword'), children: "function" }), _jsx(Text, { ...syntaxColor('text'), children: " " }), _jsx(Text, { ...syntaxColor('identifier'), children: "greet" }), _jsx(Text, { ...syntaxColor('text'), children: '() {' })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 2 - " }), _jsx(Text, { ...syntaxColor('text', 'removed'), children: ' console.log("Hello, World!"); ' })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 2 + " }), _jsxs(Text, { ...syntaxColor('text', 'added'), children: [' console.', _jsx(Text, { ...syntaxColor('identifier'), children: "log" }), '(', _jsx(Text, { ...syntaxColor('string'), children: "\"Hello, " }), _jsx(Text, { ...syntaxColor('addedHighlight'), children: "Claude" }), _jsx(Text, { ...syntaxColor('string'), children: "!\"" }), '); '] })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: " 3 " }), _jsx(Text, { ...syntaxColor('text'), children: '}' })] }), _jsxs(Text, { dimColor: true, children: [' ', '╌'.repeat(Math.max(1, Math.min(96, width - 3)))] })] })) : null, _jsxs(Text, { dimColor: true, children: [' ', selected?.theme === '__new__'
715
685
  ? 'Enter to create a custom theme'
716
686
  : syntaxHighlightingDisabled
717
687
  ? 'Syntax highlighting disabled (ctrl+t to enable)'
718
688
  : `Syntax theme: ${preview.syntaxTheme} (ctrl+t to disable)`] }), _jsx(Text, { dimColor: true, children: " Enter to select \u00B7 Esc to cancel" })] }));
719
689
  }
720
690
  export function CustomThemeEditor({ theme, token, value, tokens = [], selectedIndex = 0, query = '', width, screenReader, }) {
721
- return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [_jsx(Text, { bold: true, children: theme.name }), token ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { children: ["\u2588\u2588 ", token] }), _jsxs(Text, { dimColor: true, children: ["preset: ", value || '(not customized)'] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Value: ", value] }), _jsx(Text, { dimColor: true, children: "Accepts rgb(r,g,b), #rrggbb, ansi256(n), or ansi:name" }), _jsx(Text, { dimColor: true, children: "Enter to save \u00B7 Esc to cancel" })] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: ["\u2315 ", query || 'Filter color tokens…'] }), tokens
691
+ const semanticTheme = useTuiTheme();
692
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [_jsx(Text, { ...semanticTheme.text.heading, bold: true, children: theme.name }), token ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { children: ["\u2588\u2588 ", token] }), _jsxs(Text, { dimColor: true, children: ["preset: ", value || '(not customized)'] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Value: ", value] }), _jsx(Text, { dimColor: true, children: "Accepts rgb(r,g,b), #rrggbb, ansi256(n), or ansi:name" }), _jsx(Text, { dimColor: true, children: "Enter to save \u00B7 Esc to cancel" })] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { dimColor: true, children: ["\u2315 ", query || 'Filter color tokens…'] }), tokens
722
693
  .slice(selectedIndex, selectedIndex + 8)
723
- .map((entry, index) => (_jsxs(Text, { inverse: index === 0, children: [index === 0 ? '❯ ' : ' ', "\u2588\u2588 ", entry, theme.overrides[entry] === undefined ? '' : ' (custom)'] }, entry))), _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to nav \u00B7 Enter to edit \u00B7 Tab to reset \u00B7 Esc to done" })] })), screenReader ? _jsx(Text, { children: "Editing custom theme token" }) : null] }));
694
+ .map((entry, index) => (_jsxs(Text, { ...(index === 0 ? semanticTheme.text.selectedRow : {}), children: [dashboardSelectionPrefix(index === 0, screenReader, semanticTheme.noColor), "\u2588\u2588 ", entry, theme.overrides[entry] === undefined ? '' : ' (custom)'] }, entry))), _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to nav \u00B7 Enter to edit \u00B7 Tab to reset \u00B7 Esc to done" })] })), screenReader ? _jsx(Text, { children: "Editing custom theme token" }) : null] }));
724
695
  }
725
696
  export function ListDashboard({ title, rows, emptyText, selectedIndex, width, screenReader, }) {
726
- return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, _jsxs(Text, { bold: true, children: [" ", title] }), _jsx(Text, { children: " " }), rows.length === 0 ? (_jsxs(Text, { dimColor: true, children: [" ", emptyText] })) : (rows.map((row, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', row.label] }), row.description ? _jsxs(Text, { dimColor: true, children: [" ", row.description] }) : null] }, `${index}-${row.label}`)))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to select \u00B7 Esc to close" })] }));
697
+ const theme = useTuiTheme();
698
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', title] }), _jsx(Text, { children: " " }), rows.length === 0 ? (_jsxs(Text, { dimColor: true, children: [" ", emptyText] })) : (rows.map((row, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), children: [dashboardSelectionPrefix(index === selectedIndex, screenReader, theme.noColor), row.label] }), row.description ? _jsxs(Text, { dimColor: true, children: [" ", row.description] }) : null] }, `${index}-${row.label}`)))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191/\u2193 to select \u00B7 Esc to close" })] }));
727
699
  }
728
700
  export function MemoryDashboard({ autoMemoryEnabled, entries, selectedIndex, openedIndex, loading = false, width, screenReader, dataPlane = 'claude', }) {
729
- const palette = useTuiPalette();
701
+ const theme = useTuiTheme();
730
702
  const panelWidth = Math.min(100, width);
731
- return (_jsxs(Box, { flexDirection: "column", width: panelWidth, children: [!screenReader ? (_jsx(Text, { color: palette.accent, children: '─'.repeat(panelWidth) })) : null, _jsx(Text, { bold: true, children: " Memory" }), _jsx(Text, { children: " " }), loading ? (_jsx(Text, { dimColor: true, children: " \u2736 Loading memory files\u2026" })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [" Auto-memory: ", autoMemoryEnabled ? 'on' : 'off'] }), _jsx(Text, { children: " " }), entries.map((entry, index) => (_jsxs(Box, { children: [_jsx(Box, { width: Math.max(24, Math.min(52, panelWidth - 28)), children: _jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", entry.label, openedIndex === index ? ' ✔' : ''] }) }), entry.annotation ? (_jsx(Text, { dimColor: true, children: entry.annotation })) : null] }, `${entry.kind}-${entry.path}`)))] })), _jsx(Text, { children: " " }), dataPlane === 'claude' ? (_jsxs(Text, { dimColor: true, children: [' ', "Learn more: https://code.claude.com/docs/en/memory"] })) : null, _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, italic: true, children: "Enter to confirm \u00B7 Esc to cancel" })] }));
703
+ return (_jsxs(Box, { flexDirection: "column", width: panelWidth, children: [!screenReader ? (_jsx(Text, { ...theme.text.focusMarker, children: '─'.repeat(panelWidth) })) : null, _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', "Memory"] }), _jsx(Text, { children: " " }), loading ? (_jsx(Text, { dimColor: true, children: " \u2736 Loading memory files\u2026" })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [" Auto-memory: ", autoMemoryEnabled ? 'on' : 'off'] }), _jsx(Text, { children: " " }), entries.map((entry, index) => (_jsxs(Box, { children: [_jsx(Box, { width: Math.max(24, Math.min(52, panelWidth - 28)), children: _jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), children: [dashboardSelectionPrefix(index === selectedIndex, screenReader, theme.noColor), index + 1, ". ", entry.label, openedIndex === index ? ' ✔' : ''] }) }), entry.annotation ? (_jsx(Text, { dimColor: true, children: entry.annotation })) : null] }, `${entry.kind}-${entry.path}`)))] })), _jsx(Text, { children: " " }), dataPlane === 'claude' ? (_jsxs(Text, { dimColor: true, children: [' ', "Learn more: https://code.claude.com/docs/en/memory"] })) : null, _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, italic: true, children: "Enter to confirm \u00B7 Esc to cancel" })] }));
732
704
  }
733
705
  function selectedWindow(length, selectedIndex, size) {
734
706
  return Math.max(0, Math.min(selectedIndex - Math.floor(size / 2), Math.max(0, length - size)));
735
707
  }
736
708
  export function HookDashboard({ configuration, depth, eventIndex, matcherIndex, hookIndex, width, screenReader, }) {
709
+ const theme = useTuiTheme();
737
710
  const event = configuration.events[eventIndex];
738
711
  const matcher = event?.matchers[matcherIndex];
739
712
  const hook = matcher?.hooks[hookIndex];
740
713
  if (depth === 'detail') {
741
- return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(12, Math.min(100, width))) })) : null, _jsx(Text, { bold: true, children: " Hook details" }), _jsxs(Text, { children: [" Event: ", event?.name ?? 'Hooks'] }), _jsxs(Text, { children: [" Matcher: ", matcher?.matcher ?? '(all)'] }), _jsxs(Text, { children: [" Type: ", hook?.type ?? 'command'] }), _jsxs(Text, { children: [" Source: ", hook?.scopeLabel ?? 'Settings'] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", hook?.label ?? ''] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Esc to go back" })] }));
714
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(12, Math.min(100, width))) })) : null, _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', "Hook details"] }), _jsxs(Text, { children: [" Event: ", event?.name ?? 'Hooks'] }), _jsxs(Text, { children: [" Matcher: ", matcher?.matcher ?? '(all)'] }), _jsxs(Text, { children: [" Type: ", hook?.type ?? 'command'] }), _jsxs(Text, { children: [" Source: ", hook?.scopeLabel ?? 'Settings'] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [" ", hook?.label ?? ''] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Esc to go back" })] }));
742
715
  }
743
716
  const rows = depth === 'events'
744
717
  ? configuration.events
@@ -754,7 +727,7 @@ export function HookDashboard({ configuration, depth, eventIndex, matcherIndex,
754
727
  const start = selectedWindow(rows.length, selectedIndex, maxVisible);
755
728
  const visible = rows.slice(start, start + maxVisible);
756
729
  const line = '─'.repeat(Math.max(12, Math.min(100, width)));
757
- return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsxs(Text, { bold: true, children: [' ', depth === 'events'
730
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', depth === 'events'
758
731
  ? TUI_HOOK_MENU.title
759
732
  : depth === 'matchers'
760
733
  ? `${event?.name ?? 'Hooks'} - Matchers`
@@ -765,7 +738,7 @@ export function HookDashboard({ configuration, depth, eventIndex, matcherIndex,
765
738
  const selected = index === selectedIndex;
766
739
  if (depth === 'events') {
767
740
  const item = row;
768
- return (_jsxs(Box, { children: [_jsx(Box, { width: 30, children: _jsxs(Text, { inverse: !screenReader && selected, children: [screenReader && selected
741
+ return (_jsxs(Box, { children: [_jsx(Box, { width: 30, children: _jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), children: [screenReader && selected
769
742
  ? 'Selected: '
770
743
  : selected
771
744
  ? '❯ '
@@ -775,14 +748,14 @@ export function HookDashboard({ configuration, depth, eventIndex, matcherIndex,
775
748
  }
776
749
  if (depth === 'matchers') {
777
750
  const item = row;
778
- return (_jsxs(Text, { inverse: !screenReader && selected, children: [screenReader && selected
751
+ return (_jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), children: [screenReader && selected
779
752
  ? 'Selected: '
780
753
  : selected
781
754
  ? '❯ '
782
755
  : ' ', index + 1, ". [", item.scope, "] ", item.matcher, " ", item.hooks.length, ' ', item.hooks.length === 1 ? 'hook' : 'hooks'] }, `${item.scope}-${item.matcher}-${index}`));
783
756
  }
784
757
  const item = row;
785
- return (_jsxs(Box, { children: [_jsx(Box, { flexGrow: 1, children: _jsxs(Text, { inverse: !screenReader && selected, wrap: "truncate-end", children: [screenReader && selected
758
+ return (_jsxs(Box, { children: [_jsx(Box, { flexGrow: 1, children: _jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), wrap: "truncate-end", children: [screenReader && selected
786
759
  ? 'Selected: '
787
760
  : selected
788
761
  ? '❯ '
@@ -792,7 +765,7 @@ export function HookDashboard({ configuration, depth, eventIndex, matcherIndex,
792
765
  : 'Enter to confirm · Esc to cancel' })] }));
793
766
  }
794
767
  export function BtwPanel({ entries, selectedIndex, scrollOffset, copied, width, screenReader, }) {
795
- const palette = useTuiPalette();
768
+ const theme = useTuiTheme();
796
769
  const selected = entries[selectedIndex];
797
770
  const lines = selected?.answer.split('\n') ?? [];
798
771
  const visibleLines = lines.slice(scrollOffset, scrollOffset + 16);
@@ -816,22 +789,22 @@ export function BtwPanel({ entries, selectedIndex, scrollOffset, copied, width,
816
789
  ].join(' · ');
817
790
  return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, entries.map((entry, index) => (_jsxs(Text, { dimColor: index !== selectedIndex, children: [' ', "/btw ", entry.question] }, entry.id))), _jsx(Text, { children: " " }), selected?.status === 'answering' ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [" ", ' ', "\u00B7 Answering\u2026"] }), selected.answer
818
791
  ? visibleLines.map((line, index) => (_jsxs(Text, { children: [' ', ' ', line || ' '] }, `${scrollOffset + index}-${line}`)))
819
- : null] })) : selected?.status === 'forking' ? (_jsxs(Text, { children: [" ", ' ', "\u00B7 Forking\u2026"] })) : selected?.status === 'error' ? (_jsxs(Text, { color: palette.error, children: [' ', ' ', selected.error ?? 'Side question failed'] })) : (visibleLines.map((line, index) => (_jsxs(Text, { children: [' ', ' ', line || ' '] }, `${scrollOffset + index}-${line}`)))), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: [" ", footer] })] }));
792
+ : null] })) : selected?.status === 'forking' ? (_jsxs(Text, { children: [" ", ' ', "\u00B7 Forking\u2026"] })) : selected?.status === 'error' ? (_jsxs(Text, { ...theme.text.error, children: [' ', ' ', selected.error ?? 'Side question failed'] })) : (visibleLines.map((line, index) => (_jsxs(Text, { children: [' ', ' ', line || ' '] }, `${scrollOffset + index}-${line}`)))), _jsx(Text, { children: " " }), _jsxs(Text, { dimColor: true, children: [" ", footer] })] }));
820
793
  }
821
794
  export function SessionPicker({ sessions, selectedIndex, screenReader, query = '', }) {
822
- const palette = useTuiPalette();
795
+ const theme = useTuiTheme();
823
796
  const maxVisible = 8;
824
797
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), sessions.length - maxVisible));
825
798
  const visible = sessions.slice(start, start + maxVisible);
826
- return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: '─'.repeat(80) }) : null, _jsx(Text, { bold: true, children: " Resume session" }), _jsx(Box, { borderStyle: screenReader ? undefined : 'round', borderColor: palette.muted, paddingX: screenReader ? 0 : 1, marginY: 1, children: _jsxs(Text, { ...(query ? {} : { dimColor: true }), children: ["\u2315 ", query || 'Search…'] }) }), start > 0 ? _jsxs(Text, { dimColor: true, children: [" \u2191 ", start, " earlier"] }) : null, visible.length === 0 ? _jsx(Text, { dimColor: true, children: "No sessions found." }) : null, visible.map((session, visibleIndex) => {
799
+ return (_jsxs(Box, { flexDirection: "column", children: [!screenReader ? _jsx(Text, { dimColor: true, children: '─'.repeat(80) }) : null, _jsxs(Text, { ...theme.text.heading, bold: true, children: [' ', "Resume session"] }), _jsx(Box, { borderStyle: screenReader ? undefined : 'round', ...theme.surface.neutralBorder, paddingX: screenReader ? 0 : 1, marginY: 1, children: _jsxs(Text, { ...(query ? {} : { dimColor: true }), children: ["\u2315 ", query || 'Search…'] }) }), start > 0 ? _jsxs(Text, { dimColor: true, children: [" \u2191 ", start, " earlier"] }) : null, visible.length === 0 ? _jsx(Text, { dimColor: true, children: "No sessions found." }) : null, visible.map((session, visibleIndex) => {
827
800
  const index = start + visibleIndex;
828
- return (_jsxs(Box, { children: [_jsxs(Text, { ...(index === selectedIndex ? { color: palette.brand } : {}), bold: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', session
801
+ return (_jsxs(Box, { children: [_jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), bold: index === selectedIndex, children: [dashboardSelectionPrefix(index === selectedIndex, screenReader, theme.noColor), session
829
802
  ? (session.name ?? session.lastPrompt ?? 'Untitled')
830
803
  : 'Start a new session'] }), session ? (_jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", session.sessionId, " \u00B7 ", session.status] })) : null] }, session?.sessionId ?? 'new'));
831
804
  }), start + visible.length < sessions.length ? (_jsxs(Text, { dimColor: true, children: [" \u2193 ", sessions.length - start - visible.length, " more"] })) : null, !screenReader ? (_jsx(Text, { dimColor: true, children: "\u2191/\u2193 to navigate \u00B7 Enter to select \u00B7 Type to search \u00B7 Esc to cancel" })) : null] }));
832
805
  }
833
806
  export function CommandPalette({ commands, selectedIndex, width, screenReader, }) {
834
- const palette = useTuiPalette();
807
+ const theme = useTuiTheme();
835
808
  const paletteWidth = Math.max(1, Math.min(100, width));
836
809
  const nameWidth = Math.min(30, paletteWidth);
837
810
  const descriptionWidth = Math.max(0, paletteWidth - nameWidth);
@@ -844,19 +817,21 @@ export function CommandPalette({ commands, selectedIndex, width, screenReader, }
844
817
  return (_jsx(Box, { flexDirection: "column", width: paletteWidth, children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No matching commands." })) : (visible.map((command, visibleIndex) => {
845
818
  const index = start + visibleIndex;
846
819
  const selected = index === selectedIndex;
847
- return (_jsxs(Box, { flexDirection: "row", width: paletteWidth, children: [_jsx(Box, { width: nameWidth, flexShrink: 0, children: _jsxs(Text, { wrap: "truncate-end", ...(selected ? { color: palette.brand, bold: true } : {}), children: ["/", command.name] }) }), _jsx(Box, { width: descriptionWidth, flexShrink: 1, children: _jsx(Text, { dimColor: true, wrap: "truncate-end", children: command.description }) })] }, command.name));
820
+ return (_jsxs(Box, { flexDirection: "row", width: paletteWidth, children: [_jsx(Box, { width: nameWidth, flexShrink: 0, children: _jsxs(Text, { wrap: "truncate-end", ...(selected ? theme.text.selectedRow : {}), children: ["/", command.name] }) }), _jsx(Box, { width: descriptionWidth, flexShrink: 1, children: _jsx(Text, { dimColor: true, wrap: "truncate-end", children: command.description }) })] }, command.name));
848
821
  })) }));
849
822
  }
850
823
  export function FilePicker({ entries, selectedIndex, width, screenReader, }) {
824
+ const theme = useTuiTheme();
851
825
  const maxVisible = 12;
852
826
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
853
827
  const visible = entries.slice(start, start + maxVisible);
854
828
  return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No matching files." })) : (visible.map((entry, visibleIndex) => {
855
829
  const index = start + visibleIndex;
856
- return (_jsxs(Text, { inverse: !screenReader && index === selectedIndex, children: [screenReader && index === selectedIndex ? 'Selected: ' : '+ ', entry.path] }, entry.path));
830
+ return (_jsxs(Text, { ...(index === selectedIndex ? theme.text.selectedRow : {}), children: [screenReader && index === selectedIndex ? 'Selected: ' : '+ ', entry.path] }, entry.path));
857
831
  })) }));
858
832
  }
859
833
  export function MentionPicker({ entries, selectedIndex, width, screenReader, }) {
834
+ const theme = useTuiTheme();
860
835
  const maxVisible = 12;
861
836
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
862
837
  const visible = entries.slice(start, start + maxVisible);
@@ -864,9 +839,9 @@ export function MentionPicker({ entries, selectedIndex, width, screenReader, })
864
839
  const index = start + visibleIndex;
865
840
  const selected = index === selectedIndex;
866
841
  if (entry.kind === 'agent') {
867
- return (_jsxs(Text, { inverse: !screenReader && selected, ...(screenReader ? {} : { wrap: 'truncate-end' }), children: [screenReader && selected ? 'Selected agent: ' : '* ', entry.name, screenReader ? '' : ' (agent)', entry.description ? ` – ${entry.description}` : ''] }, `agent:${entry.name}`));
842
+ return (_jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), ...(screenReader ? {} : { wrap: 'truncate-end' }), children: [screenReader && selected ? 'Selected agent: ' : '* ', entry.name, screenReader ? '' : ' (agent)', entry.description ? ` – ${entry.description}` : ''] }, `agent:${entry.name}`));
868
843
  }
869
- return (_jsxs(Text, { inverse: !screenReader && selected, ...(screenReader ? {} : { wrap: 'truncate-end' }), children: [screenReader && selected ? 'Selected: ' : '+ ', entry.path] }, `file:${entry.path}`));
844
+ return (_jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), ...(screenReader ? {} : { wrap: 'truncate-end' }), children: [screenReader && selected ? 'Selected: ' : '+ ', entry.path] }, `file:${entry.path}`));
870
845
  })) }));
871
846
  }
872
847
  const SHORTCUT_ROWS = [
@@ -896,38 +871,34 @@ export function ShortcutHelp({ width }) {
896
871
  return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: SHORTCUT_ROWS.map((row, rowIndex) => wide ? (_jsx(Box, { flexDirection: "row", children: row.map((cell, cellIndex) => (_jsx(Box, { width: cellIndex === 2 ? undefined : '33%', children: _jsx(Text, { dimColor: true, children: cell }) }, cellIndex))) }, rowIndex)) : (row.filter(Boolean).map((cell, cellIndex) => (_jsx(Text, { dimColor: true, children: cell }, `${rowIndex}-${cellIndex}`))))) }));
897
872
  }
898
873
  export function HelpMenu({ tabIndex, selectedIndex, builtinCommands, customCommands, width, screenReader, }) {
899
- const palette = useTuiPalette();
874
+ const theme = useTuiTheme();
900
875
  const tabs = ['General', 'Commands', 'Custom commands'];
901
876
  const commands = tabIndex === 1 ? builtinCommands : customCommands;
902
877
  const maxVisible = 10;
903
878
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, commands.length - maxVisible)));
904
879
  const visible = commands.slice(start, start + maxVisible);
905
880
  const line = '─'.repeat(Math.max(12, Math.min(100, width)));
906
- return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsxs(Box, { children: [_jsx(Text, { bold: true, children: " Help " }), tabs.map((tab, index) => (_jsxs(Text, { ...(index === tabIndex
907
- ? { color: palette.brand, bold: true }
908
- : {}), children: [' ', tab, ' '] }, tab)))] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: tabIndex === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "Praxis understands your codebase, makes edits with your permission, and executes commands from your terminal." }), _jsx(Text, { bold: true, children: "Shortcuts" }), _jsx(ShortcutHelp, { width: width })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: tabIndex === 1
881
+ return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? _jsx(Text, { dimColor: true, children: line }) : null, _jsxs(Box, { children: [_jsxs(Text, { ...theme.text.navigation, bold: true, children: [' ', "Help", ' '] }), tabs.map((tab, index) => (_jsxs(Text, { ...(index === tabIndex ? theme.text.selectedTab : {}), children: [' ', tab, ' '] }, tab)))] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: tabIndex === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { children: "Praxis understands your codebase, makes edits with your permission, and executes commands from your terminal." }), _jsx(Text, { ...theme.text.heading, bold: true, children: "Shortcuts" }), _jsx(ShortcutHelp, { width: width })] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: tabIndex === 1
909
882
  ? 'Browse default commands'
910
883
  : 'Browse shared commands and skills' }), visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No commands found." })) : (visible.map((command, visibleIndex) => {
911
884
  const index = start + visibleIndex;
912
885
  return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { ...(index === selectedIndex
913
- ? { color: palette.brand, bold: true }
886
+ ? theme.text.selectedRow
914
887
  : {}), children: [index === selectedIndex ? '↓ ' : ' ', "/", command.name] }), _jsxs(Text, { dimColor: true, children: [" ", command.description] })] }, command.name));
915
888
  }))] })) }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192 to switch \u00B7 \u2191/\u2193 to navigate \u00B7 Esc to cancel" })] }));
916
889
  }
917
890
  export function SelectionMenu({ title, description, options, selectedIndex, footer, width, screenReader, }) {
918
- const palette = useTuiPalette();
891
+ const theme = useTuiTheme();
919
892
  const maxVisible = 7;
920
893
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, options.length - maxVisible)));
921
894
  const visible = options.slice(start, start + maxVisible);
922
895
  const current = options.find((option) => option.selected);
923
896
  const maxLabelWidth = options.reduce((max, option, index) => Math.max(max, `${index + 1}. ${option.label}`.length), 0);
924
- return (_jsxs(Box, { borderStyle: screenReader ? undefined : 'round', borderColor: palette.muted, flexDirection: "column", marginTop: 1, paddingX: screenReader ? 0 : 1, width: screenReader ? undefined : Math.min(80, width), children: [_jsx(Text, { bold: true, children: title }), description ? _jsx(Text, { dimColor: true, children: description }) : null, screenReader && current ? _jsxs(Text, { children: ["Current: ", current.label] }) : null, visible.map((option, visibleIndex) => {
897
+ return (_jsxs(Box, { borderStyle: screenReader ? undefined : 'round', ...theme.surface.neutralBorder, flexDirection: "column", marginTop: 1, paddingX: screenReader ? 0 : 1, width: screenReader ? undefined : Math.min(80, width), children: [_jsx(Text, { ...theme.text.heading, bold: true, children: title }), description ? _jsx(Text, { dimColor: true, children: description }) : null, screenReader && current ? _jsxs(Text, { children: ["Current: ", current.label] }) : null, visible.map((option, visibleIndex) => {
925
898
  const index = start + visibleIndex;
926
899
  const selected = index === selectedIndex;
927
900
  const rowLabel = `${index + 1}. ${option.label}`;
928
- return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsxs(Text, { ...(!screenReader && selected
929
- ? { color: palette.brand, bold: true }
930
- : {}), children: [selectionPrefix(selected, screenReader), rowLabel, !screenReader && option.selected ? ' ✔' : '', option.description ? (_jsxs(Text, { dimColor: true, children: [' '.repeat(Math.max(2, maxLabelWidth -
901
+ return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsxs(Text, { ...(!screenReader && selected ? theme.text.selectedRow : {}), children: [selectionPrefix(selected, screenReader), rowLabel, !screenReader && option.selected ? ' ✔' : '', option.description ? (_jsxs(Text, { dimColor: true, children: [' '.repeat(Math.max(2, maxLabelWidth -
931
902
  rowLabel.length -
932
903
  (option.selected && !screenReader ? 2 : 0))), option.description] })) : null] }) }, `${index}-${option.label}`));
933
904
  }), start > 0 || start + visible.length < options.length ? (_jsxs(Text, { dimColor: true, children: [start > 0 ? `↑ ${start} earlier` : '', start > 0 && start + visible.length < options.length ? ' · ' : '', start + visible.length < options.length
@@ -935,27 +906,26 @@ export function SelectionMenu({ title, description, options, selectedIndex, foot
935
906
  : ''] })) : null, _jsx(Text, { dimColor: true, children: footer })] }));
936
907
  }
937
908
  export function ModelMenu({ options, effort, selectedIndex, width, screenReader, }) {
938
- const palette = useTuiPalette();
909
+ const theme = useTuiTheme();
939
910
  const current = options.find((option) => option.selected);
940
911
  const maxLabelWidth = options.reduce((max, option, index) => Math.max(max, `${index + 1}. ${option.label}`.length), 0);
941
- return (_jsxs(Box, { borderStyle: screenReader ? undefined : 'round', borderColor: palette.muted, flexDirection: "column", marginTop: 1, paddingX: screenReader ? 0 : 1, width: screenReader ? undefined : Math.min(80, width), children: [_jsx(Text, { bold: true, children: "Select model" }), _jsx(Text, { dimColor: true, children: 'Switch between models. Your pick applies to this and future Praxis Code sessions. For other model names, specify with --model.' }), screenReader && current ? _jsxs(Text, { children: ["Current: ", current.label] }) : null, options.map((option, index) => {
912
+ return (_jsxs(Box, { borderStyle: screenReader ? undefined : 'round', ...theme.surface.neutralBorder, flexDirection: "column", marginTop: 1, paddingX: screenReader ? 0 : 1, width: screenReader ? undefined : Math.min(80, width), children: [_jsx(Text, { ...theme.text.heading, bold: true, children: "Select model" }), _jsx(Text, { dimColor: true, children: 'Switch between models. Your pick applies to this and future Praxis Code sessions. For other model names, specify with --model.' }), screenReader && current ? _jsxs(Text, { children: ["Current: ", current.label] }) : null, options.map((option, index) => {
942
913
  const selected = index === selectedIndex;
943
914
  const rowLabel = `${index + 1}. ${option.label}`;
944
- return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsxs(Text, { ...(!screenReader && selected
945
- ? { color: palette.brand, bold: true }
946
- : {}), children: [selectionPrefix(selected, screenReader), rowLabel, !screenReader && option.selected ? ' ✔' : '', option.description ? (_jsxs(Text, { dimColor: true, children: [' '.repeat(Math.max(2, maxLabelWidth -
915
+ return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsxs(Text, { ...(!screenReader && selected ? theme.text.selectedRow : {}), children: [selectionPrefix(selected, screenReader), rowLabel, !screenReader && option.selected ? ' ✔' : '', option.description ? (_jsxs(Text, { dimColor: true, children: [' '.repeat(Math.max(2, maxLabelWidth -
947
916
  rowLabel.length -
948
917
  (option.selected && !screenReader ? 2 : 0))), option.description] })) : null] }) }, `${index}-${option.label}`));
949
- }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: palette.accent, children: "\u25CF " }), effort.charAt(0).toUpperCase() + effort.slice(1), " effort (default)", _jsx(Text, { dimColor: true, children: " \u2190/\u2192 to adjust" })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter to select \u00B7 Esc to cancel" }) })] }));
918
+ }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { ...theme.text.focusMarker, children: "\u25CF " }), effort.charAt(0).toUpperCase() + effort.slice(1), " effort (default)", _jsx(Text, { dimColor: true, children: " \u2190/\u2192 to adjust" })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter to select \u00B7 Esc to cancel" }) })] }));
950
919
  }
951
920
  function ComposerInput({ input, cursor }) {
952
- const palette = useTuiPalette();
921
+ const theme = useTuiTheme();
953
922
  const { before, current, after } = composerEditorSegments({
954
923
  text: input,
955
924
  cursor,
956
925
  });
957
926
  const cursorText = current === '\n' ? '↵' : (current ?? ' ');
958
- return (_jsxs(Text, { children: [before, _jsx(Text, { color: palette.selectionText, backgroundColor: palette.accent, children: cursorText }), after] }));
927
+ const noColorCursorText = current === undefined ? ` ${'\u0332'}` : `${cursorText}\u0332`;
928
+ return (_jsxs(Text, { children: [before, _jsx(Text, { ...theme.text.inputCursor, children: theme.noColor ? noColorCursorText : cursorText }), after] }));
959
929
  }
960
930
  export function ExternalEditorWait({ screenReader, }) {
961
931
  if (screenReader)
@@ -963,7 +933,7 @@ export function ExternalEditorWait({ screenReader, }) {
963
933
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Text, { children: "Save and close editor to continue..." }), _jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" })] }));
964
934
  }
965
935
  export function Composer({ input, cursor, busy, clipboardBusy = false, status, display, usage, costUsd, width, screenReader, hasThinking = false, thinkingExpanded = false, shortcutsVisible = false, shellMode = false, footerMessage, reduceMotion = false, progressBar = true, turnDuration, editorMode = 'normal', prStatus, sessionColor, commandArgumentHint, }) {
966
- const palette = useTuiPalette();
936
+ const theme = useTuiTheme();
967
937
  const [spinnerIndex, setSpinnerIndex] = useState(0);
968
938
  useEffect(() => {
969
939
  if (!busy || screenReader || reduceMotion)
@@ -981,7 +951,7 @@ export function Composer({ input, cursor, busy, clipboardBusy = false, status, d
981
951
  : `Prompt: ${input}` }));
982
952
  const { lineWidth, footerWidth, showEditorHint } = composerLayoutForWidth(width);
983
953
  const line = '─'.repeat(lineWidth);
984
- const separatorColor = sessionColor === undefined ? undefined : palette.sessionColors[sessionColor];
954
+ const separatorColor = sessionColor === undefined ? undefined : theme.session(sessionColor);
985
955
  const footerMode = `⏵⏵ ${permissionLabel(display.permissionMode)}`;
986
956
  const footerCompactMode = `⏵⏵ ${compactPermissionLabel(display.permissionMode)}`;
987
957
  const footerLeft = composerFooterLeft(footerWidth, busy, hasThinking, thinkingExpanded, footerMode, footerCompactMode);
@@ -994,14 +964,14 @@ export function Composer({ input, cursor, busy, clipboardBusy = false, status, d
994
964
  ? ` · ${usage.cacheReadInputTokens} cached`
995
965
  : '', costUsd === undefined ? '' : ` · $${costUsd.toFixed(6)}`] })) : null, _jsx(Text, { ...(separatorColor === undefined
996
966
  ? { dimColor: true }
997
- : { color: separatorColor }), children: line }), clipboardBusy ? (_jsx(Text, { children: "Pasting\u2026" })) : busy ? (_jsxs(Text, { children: [progressBar ? (_jsx(Text, { color: palette.accent, children: reduceMotion ? '•' : SPINNER[spinnerIndex] })) : null, ' ', status, "\u2026 ", _jsx(Text, { dimColor: true, children: "\u00B7 esc to interrupt" })] })) : (_jsxs(Text, { children: [_jsx(Text, { ...(shellMode ? {} : { color: palette.brand }), bold: true, children: shellMode ? '! ' : '❯ ' }), input ? (_jsxs(Text, { children: [_jsx(ComposerInput, { cursor: cursor ?? Array.from(input).length, input: input }), commandArgumentHint ? (_jsxs(Text, { dimColor: true, children: [input.endsWith(' ') ? '' : ' ', commandArgumentHint] })) : null] })) : (_jsx(Text, { dimColor: true, children: shellMode
967
+ : { color: separatorColor }), children: line }), clipboardBusy ? (_jsx(Text, { children: "Pasting\u2026" })) : busy ? (_jsxs(Text, { children: [progressBar ? (_jsx(Text, { ...theme.text.focusMarker, children: reduceMotion ? '•' : SPINNER[spinnerIndex] })) : null, ' ', _jsxs(Text, { ...theme.text.active, children: [status, "\u2026"] }), ' ', _jsx(Text, { dimColor: true, children: "\u00B7 esc to interrupt" })] })) : (_jsxs(Text, { children: [_jsx(Text, { ...(shellMode ? theme.text.shellMode : theme.text.inputMarker), children: shellMode ? '! ' : '❯ ' }), input ? (_jsxs(Text, { children: [_jsx(ComposerInput, { cursor: cursor ?? Array.from(input).length, input: input }), commandArgumentHint ? (_jsxs(Text, { dimColor: true, children: [input.endsWith(' ') ? '' : ' ', commandArgumentHint] })) : null] })) : (_jsx(Text, { dimColor: true, children: shellMode
998
968
  ? 'Enter a shell command'
999
969
  : 'Try "review this project"' }))] })), _jsx(Text, { ...(separatorColor === undefined
1000
970
  ? { dimColor: true }
1001
- : { color: separatorColor }), children: line }), shortcutsVisible ? (_jsx(ShortcutHelp, { width: width })) : (_jsx(Box, { width: footerWidth, children: _jsx(Text, { wrap: "truncate", children: shellMode ? (_jsx(Text, { dimColor: true, children: "! for bash mode" })) : footerMessage ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerMessageLeft, " \u00B7 "] }), footerMessage.isError ? (_jsx(Text, { color: palette.error, children: footerMessage.text })) : (_jsx(Text, { dimColor: true, children: footerMessage.text }))] })) : prStatus ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerLeft, " \u00B7 "] }), _jsx(Text, { dimColor: true, children: prStatus })] })) : turnDuration ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerLeft, " \u00B7 "] }), _jsxs(Text, { dimColor: true, children: ["Cooked for ", turnDuration] })] })) : display.effort ? (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: footerLeft }), _jsx(Text, { dimColor: true, children: " \u00B7 " }), _jsxs(Text, { color: palette.accent, children: ["\u25CF ", display.effort] }), showEditorHint ? (_jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", editorMode === 'vim' ? 'vim' : '/effort'] })) : null] })) : (_jsx(Text, { dimColor: true, children: footerLeft })) }) }))] }));
971
+ : { color: separatorColor }), children: line }), shortcutsVisible ? (_jsx(ShortcutHelp, { width: width })) : (_jsx(Box, { width: footerWidth, children: _jsx(Text, { wrap: "truncate", children: shellMode ? (_jsx(Text, { dimColor: true, children: "! for bash mode" })) : footerMessage ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerMessageLeft, " \u00B7 "] }), footerMessage.isError ? (_jsx(Text, { ...theme.text.error, children: footerMessage.text })) : (_jsx(Text, { dimColor: true, children: footerMessage.text }))] })) : prStatus ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerLeft, " \u00B7 "] }), _jsx(Text, { dimColor: true, children: prStatus })] })) : turnDuration ? (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [footerLeft, " \u00B7 "] }), _jsxs(Text, { dimColor: true, children: ["Cooked for ", turnDuration] })] })) : display.effort ? (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: footerLeft }), _jsx(Text, { dimColor: true, children: " \u00B7 " }), _jsxs(Text, { ...theme.text.focusMarker, children: ["\u25CF ", display.effort] }), showEditorHint ? (_jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", editorMode === 'vim' ? 'vim' : '/effort'] })) : null] })) : (_jsx(Text, { dimColor: true, children: footerLeft })) }) }))] }));
1002
972
  }
1003
973
  export function DialogFrame({ title, children, screenReader, }) {
1004
- const palette = useTuiPalette();
1005
- return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round', borderColor: palette.warning, paddingX: screenReader ? 0 : 1, marginTop: 1, children: [_jsx(Text, { color: palette.warning, bold: true, children: title }), children] }));
974
+ const theme = useTuiTheme();
975
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round', ...theme.surface.decision, paddingX: screenReader ? 0 : 1, marginTop: 1, children: [_jsx(Text, { ...theme.text.heading, ...theme.text.warning, bold: true, children: title }), children] }));
1006
976
  }
1007
977
  //# sourceMappingURL=claude-style.js.map