praxis-agent 0.35.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 (38) hide show
  1. package/README.md +5 -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 -4
  7. package/dist/application/session-service.js +528 -434
  8. package/dist/application/subagent-service.js +358 -177
  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 +2 -0
  14. package/dist/application/turn-memory-coordinator.d.ts +43 -0
  15. package/dist/application/turn-memory-coordinator.js +98 -0
  16. package/dist/cli/agents-dashboard.js +10 -5
  17. package/dist/cli/interactive.js +264 -204
  18. package/dist/cli/tui/claude-style.js +154 -130
  19. package/dist/cli/tui/task-panel.js +10 -1
  20. package/dist/cli/tui/theme.d.ts +46 -3
  21. package/dist/cli/tui/theme.js +414 -56
  22. package/dist/cli/tui/tool-permission.js +9 -5
  23. package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
  24. package/dist/cli/tui/tui-interaction-router.js +127 -0
  25. package/dist/cli-runtime.d.ts +2 -2
  26. package/dist/core/agent-orchestration.d.ts +31 -0
  27. package/dist/core/agent-orchestration.js +235 -0
  28. package/dist/core/runtime.d.ts +5 -0
  29. package/dist/core/runtime.js +15 -1
  30. package/dist/persistence/claude-job-store.d.ts +58 -0
  31. package/dist/persistence/claude-job-store.js +483 -1
  32. package/dist/persistence/native-transcript-reader.d.ts +53 -0
  33. package/dist/persistence/native-transcript-reader.js +283 -0
  34. package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
  35. package/dist/persistence/subagent-lifecycle-store.js +373 -146
  36. package/dist/platform/exclusive-file-lease.d.ts +3 -0
  37. package/dist/platform/exclusive-file-lease.js +3 -0
  38. package/package.json +1 -1
@@ -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
@@ -517,7 +538,7 @@ function ActiveStreamText({ 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
540
  export function Transcript({ entries, activeText, activeThinking = '', activeStreamVisible = true, thinkingExpanded = false, detailedTranscript = false, screenReader, }) {
520
- const palette = useTuiPalette();
541
+ const theme = useTuiTheme();
521
542
  const detailed = thinkingExpanded || detailedTranscript;
522
543
  return (_jsxs(Box, { flexDirection: "column", children: [entries.map((entry, index) => {
523
544
  if (entry.kind === 'read-summary') {
@@ -525,16 +546,16 @@ export function Transcript({ entries, activeText, activeThinking = '', activeStr
525
546
  }
526
547
  const item = entry.item;
527
548
  if (item.kind === 'user') {
528
- 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 })] }, entry.key));
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));
529
550
  }
530
551
  if (item.kind === 'assistant') {
531
- 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 })] }, entry.key));
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));
532
553
  }
533
554
  if (item.kind === 'thinking') {
534
555
  return (_jsx(ThinkingBlock, { text: item.text, active: false, expanded: detailed, screenReader: screenReader }, entry.key));
535
556
  }
536
557
  if (item.kind === 'compact') {
537
- 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
538
559
  ? 'Conversation compacted'
539
560
  : '✻ Conversation compacted (ctrl+o for history)' }), detailed ? (_jsx(Box, { marginLeft: screenReader ? 0 : 2, children: _jsx(MarkdownText, { text: item.summary }) })) : null] }, entry.key));
540
561
  }
@@ -548,7 +569,7 @@ export function Transcript({ entries, activeText, activeThinking = '', activeStr
548
569
  }
549
570
  if (item.kind === 'tool-result') {
550
571
  const text = item.text.length > 500 ? `${item.text.slice(0, 497)}...` : item.text;
551
- 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 }))] }, entry.key));
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));
552
573
  }
553
574
  if (item.kind === 'shell') {
554
575
  const result = entry.kind === 'shell' ? entry.result : undefined;
@@ -564,20 +585,20 @@ export function Transcript({ entries, activeText, activeThinking = '', activeStr
564
585
  const lines = contentLines(output);
565
586
  const visible = detailed || screenReader ? lines : lines.slice(0, 3);
566
587
  const hidden = lines.length - visible.length;
567
- 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] }, entry.key));
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));
568
589
  }
569
590
  if (item.kind === 'shell-result') {
570
591
  const output = [item.stdout, item.stderr].filter(Boolean).join('\n');
571
- return (_jsxs(Text, { ...(item.isError ? { color: palette.error } : {}), children: ["\u23BF ", output] }, entry.key));
592
+ return (_jsxs(Text, { ...(item.isError ? theme.text.error : {}), children: ["\u23BF ", output] }, entry.key));
572
593
  }
573
594
  if (item.kind === 'local-result') {
574
595
  return (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["\u23BF ", item.text] }) }, entry.key));
575
596
  }
576
- return (_jsxs(Text, { ...(item.kind === 'warning' ? { color: palette.error } : {}), dimColor: item.kind === 'notice', children: [item.kind === 'warning' ? '⚠ ' : '· ', item.text] }, entry.key));
577
- }), 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] }));
578
599
  }
579
600
  export function DiffDashboard({ snapshots, sourceIndex, selectedIndex, viewingFile, scrollOffset, width, screenReader, }) {
580
- const palette = useTuiPalette();
601
+ const theme = useTuiTheme();
581
602
  const source = snapshots[sourceIndex];
582
603
  const snapshot = source?.snapshot ?? { files: [], additions: 0, deletions: 0 };
583
604
  const selected = snapshot.files[selectedIndex];
@@ -585,11 +606,12 @@ export function DiffDashboard({ snapshots, sourceIndex, selectedIndex, viewingFi
585
606
  const line = '─'.repeat(panelWidth);
586
607
  if (viewingFile && selected) {
587
608
  const lines = visiblePatchLines(selected.patch).slice(scrollOffset, scrollOffset + 18);
588
- 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" })] }));
589
610
  }
590
- 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" })] }));
591
612
  }
592
613
  export function PermissionDashboard({ tabIndex, selectedIndex, query, rules, recentDenied, retryingDeniedId, workspaceDirectories, width, screenReader, }) {
614
+ const theme = useTuiTheme();
593
615
  const tabs = ['Recently denied', 'Allow', 'Ask', 'Deny', 'Workspace'];
594
616
  const behavior = ['allow', 'ask', 'deny'][tabIndex - 1];
595
617
  const normalizedQuery = query.trim().toLowerCase();
@@ -613,7 +635,9 @@ export function PermissionDashboard({ tabIndex, selectedIndex, query, rules, rec
613
635
  : tabIndex === 3
614
636
  ? 'Praxis Code will reject requests to use denied tools.'
615
637
  : 'Praxis Code can read files in the workspace, and make edits when auto-accept edits is on.';
616
- 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
617
641
  ? 'Enter to approve · r to retry · ↑/↓ to navigate · Esc to cancel'
618
642
  : selectedIndex >= 0
619
643
  ? '↑/↓ navigate · Enter to select · ←/→ to switch · Esc to cancel'
@@ -629,6 +653,7 @@ const THEME_OPTIONS = [
629
653
  { theme: 'light-ansi', label: 'Light mode (ANSI colors only)' },
630
654
  ];
631
655
  export function ThemePicker({ currentTheme, selectedIndex, syntaxHighlightingDisabled, customThemes = [], allowCustomThemes = true, width, screenReader, }) {
656
+ const theme = useTuiTheme();
632
657
  const options = [
633
658
  ...THEME_OPTIONS.map((option) => ({ ...option, customTheme: undefined })),
634
659
  ...customThemes.map((theme) => ({
@@ -648,43 +673,45 @@ export function ThemePicker({ currentTheme, selectedIndex, syntaxHighlightingDis
648
673
  ? 'dark'
649
674
  : (selected?.theme ??
650
675
  (currentTheme.startsWith('custom:') ? 'dark' : currentTheme));
651
- const preview = tuiPalette(previewTheme, syntaxHighlightingDisabled, process.env, selectedCustomTheme);
652
- const syntax = preview.syntax;
653
- const syntaxColor = (color) => syntaxHighlightingDisabled ? {} : { color };
654
- 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
655
- ? {}
656
- : { 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
657
- ? {}
658
- : { backgroundColor: syntax.addedBackground }), children: [' console.', _jsx(Text, { ...syntaxColor(syntax.identifier), children: "log" }), '(', _jsx(Text, { ...syntaxColor(syntax.string), children: "\"Hello, " }), _jsx(Text, { ...(syntaxHighlightingDisabled || !syntax.addedHighlight
659
- ? {}
660
- : { 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__'
661
685
  ? 'Enter to create a custom theme'
662
686
  : syntaxHighlightingDisabled
663
687
  ? 'Syntax highlighting disabled (ctrl+t to enable)'
664
688
  : `Syntax theme: ${preview.syntaxTheme} (ctrl+t to disable)`] }), _jsx(Text, { dimColor: true, children: " Enter to select \u00B7 Esc to cancel" })] }));
665
689
  }
666
690
  export function CustomThemeEditor({ theme, token, value, tokens = [], selectedIndex = 0, query = '', width, screenReader, }) {
667
- 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
668
693
  .slice(selectedIndex, selectedIndex + 8)
669
- .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] }));
670
695
  }
671
696
  export function ListDashboard({ title, rows, emptyText, selectedIndex, width, screenReader, }) {
672
- 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" })] }));
673
699
  }
674
700
  export function MemoryDashboard({ autoMemoryEnabled, entries, selectedIndex, openedIndex, loading = false, width, screenReader, dataPlane = 'claude', }) {
675
- const palette = useTuiPalette();
701
+ const theme = useTuiTheme();
676
702
  const panelWidth = Math.min(100, width);
677
- 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" })] }));
678
704
  }
679
705
  function selectedWindow(length, selectedIndex, size) {
680
706
  return Math.max(0, Math.min(selectedIndex - Math.floor(size / 2), Math.max(0, length - size)));
681
707
  }
682
708
  export function HookDashboard({ configuration, depth, eventIndex, matcherIndex, hookIndex, width, screenReader, }) {
709
+ const theme = useTuiTheme();
683
710
  const event = configuration.events[eventIndex];
684
711
  const matcher = event?.matchers[matcherIndex];
685
712
  const hook = matcher?.hooks[hookIndex];
686
713
  if (depth === 'detail') {
687
- 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" })] }));
688
715
  }
689
716
  const rows = depth === 'events'
690
717
  ? configuration.events
@@ -700,7 +727,7 @@ export function HookDashboard({ configuration, depth, eventIndex, matcherIndex,
700
727
  const start = selectedWindow(rows.length, selectedIndex, maxVisible);
701
728
  const visible = rows.slice(start, start + maxVisible);
702
729
  const line = '─'.repeat(Math.max(12, Math.min(100, width)));
703
- 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'
704
731
  ? TUI_HOOK_MENU.title
705
732
  : depth === 'matchers'
706
733
  ? `${event?.name ?? 'Hooks'} - Matchers`
@@ -711,7 +738,7 @@ export function HookDashboard({ configuration, depth, eventIndex, matcherIndex,
711
738
  const selected = index === selectedIndex;
712
739
  if (depth === 'events') {
713
740
  const item = row;
714
- 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
715
742
  ? 'Selected: '
716
743
  : selected
717
744
  ? '❯ '
@@ -721,14 +748,14 @@ export function HookDashboard({ configuration, depth, eventIndex, matcherIndex,
721
748
  }
722
749
  if (depth === 'matchers') {
723
750
  const item = row;
724
- return (_jsxs(Text, { inverse: !screenReader && selected, children: [screenReader && selected
751
+ return (_jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), children: [screenReader && selected
725
752
  ? 'Selected: '
726
753
  : selected
727
754
  ? '❯ '
728
755
  : ' ', index + 1, ". [", item.scope, "] ", item.matcher, " ", item.hooks.length, ' ', item.hooks.length === 1 ? 'hook' : 'hooks'] }, `${item.scope}-${item.matcher}-${index}`));
729
756
  }
730
757
  const item = row;
731
- 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
732
759
  ? 'Selected: '
733
760
  : selected
734
761
  ? '❯ '
@@ -738,7 +765,7 @@ export function HookDashboard({ configuration, depth, eventIndex, matcherIndex,
738
765
  : 'Enter to confirm · Esc to cancel' })] }));
739
766
  }
740
767
  export function BtwPanel({ entries, selectedIndex, scrollOffset, copied, width, screenReader, }) {
741
- const palette = useTuiPalette();
768
+ const theme = useTuiTheme();
742
769
  const selected = entries[selectedIndex];
743
770
  const lines = selected?.answer.split('\n') ?? [];
744
771
  const visibleLines = lines.slice(scrollOffset, scrollOffset + 16);
@@ -762,22 +789,22 @@ export function BtwPanel({ entries, selectedIndex, scrollOffset, copied, width,
762
789
  ].join(' · ');
763
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
764
791
  ? visibleLines.map((line, index) => (_jsxs(Text, { children: [' ', ' ', line || ' '] }, `${scrollOffset + index}-${line}`)))
765
- : 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] })] }));
766
793
  }
767
794
  export function SessionPicker({ sessions, selectedIndex, screenReader, query = '', }) {
768
- const palette = useTuiPalette();
795
+ const theme = useTuiTheme();
769
796
  const maxVisible = 8;
770
797
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), sessions.length - maxVisible));
771
798
  const visible = sessions.slice(start, start + maxVisible);
772
- 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) => {
773
800
  const index = start + visibleIndex;
774
- 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
775
802
  ? (session.name ?? session.lastPrompt ?? 'Untitled')
776
803
  : 'Start a new session'] }), session ? (_jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", session.sessionId, " \u00B7 ", session.status] })) : null] }, session?.sessionId ?? 'new'));
777
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] }));
778
805
  }
779
806
  export function CommandPalette({ commands, selectedIndex, width, screenReader, }) {
780
- const palette = useTuiPalette();
807
+ const theme = useTuiTheme();
781
808
  const paletteWidth = Math.max(1, Math.min(100, width));
782
809
  const nameWidth = Math.min(30, paletteWidth);
783
810
  const descriptionWidth = Math.max(0, paletteWidth - nameWidth);
@@ -790,19 +817,21 @@ export function CommandPalette({ commands, selectedIndex, width, screenReader, }
790
817
  return (_jsx(Box, { flexDirection: "column", width: paletteWidth, children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No matching commands." })) : (visible.map((command, visibleIndex) => {
791
818
  const index = start + visibleIndex;
792
819
  const selected = index === selectedIndex;
793
- 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));
794
821
  })) }));
795
822
  }
796
823
  export function FilePicker({ entries, selectedIndex, width, screenReader, }) {
824
+ const theme = useTuiTheme();
797
825
  const maxVisible = 12;
798
826
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
799
827
  const visible = entries.slice(start, start + maxVisible);
800
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) => {
801
829
  const index = start + visibleIndex;
802
- 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));
803
831
  })) }));
804
832
  }
805
833
  export function MentionPicker({ entries, selectedIndex, width, screenReader, }) {
834
+ const theme = useTuiTheme();
806
835
  const maxVisible = 12;
807
836
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
808
837
  const visible = entries.slice(start, start + maxVisible);
@@ -810,9 +839,9 @@ export function MentionPicker({ entries, selectedIndex, width, screenReader, })
810
839
  const index = start + visibleIndex;
811
840
  const selected = index === selectedIndex;
812
841
  if (entry.kind === 'agent') {
813
- 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}`));
814
843
  }
815
- 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}`));
816
845
  })) }));
817
846
  }
818
847
  const SHORTCUT_ROWS = [
@@ -842,38 +871,34 @@ export function ShortcutHelp({ width }) {
842
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}`))))) }));
843
872
  }
844
873
  export function HelpMenu({ tabIndex, selectedIndex, builtinCommands, customCommands, width, screenReader, }) {
845
- const palette = useTuiPalette();
874
+ const theme = useTuiTheme();
846
875
  const tabs = ['General', 'Commands', 'Custom commands'];
847
876
  const commands = tabIndex === 1 ? builtinCommands : customCommands;
848
877
  const maxVisible = 10;
849
878
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, commands.length - maxVisible)));
850
879
  const visible = commands.slice(start, start + maxVisible);
851
880
  const line = '─'.repeat(Math.max(12, Math.min(100, width)));
852
- 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
853
- ? { color: palette.brand, bold: true }
854
- : {}), 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
855
882
  ? 'Browse default commands'
856
883
  : 'Browse shared commands and skills' }), visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No commands found." })) : (visible.map((command, visibleIndex) => {
857
884
  const index = start + visibleIndex;
858
885
  return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { ...(index === selectedIndex
859
- ? { color: palette.brand, bold: true }
886
+ ? theme.text.selectedRow
860
887
  : {}), children: [index === selectedIndex ? '↓ ' : ' ', "/", command.name] }), _jsxs(Text, { dimColor: true, children: [" ", command.description] })] }, command.name));
861
888
  }))] })) }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192 to switch \u00B7 \u2191/\u2193 to navigate \u00B7 Esc to cancel" })] }));
862
889
  }
863
890
  export function SelectionMenu({ title, description, options, selectedIndex, footer, width, screenReader, }) {
864
- const palette = useTuiPalette();
891
+ const theme = useTuiTheme();
865
892
  const maxVisible = 7;
866
893
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, options.length - maxVisible)));
867
894
  const visible = options.slice(start, start + maxVisible);
868
895
  const current = options.find((option) => option.selected);
869
896
  const maxLabelWidth = options.reduce((max, option, index) => Math.max(max, `${index + 1}. ${option.label}`.length), 0);
870
- 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) => {
871
898
  const index = start + visibleIndex;
872
899
  const selected = index === selectedIndex;
873
900
  const rowLabel = `${index + 1}. ${option.label}`;
874
- return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsxs(Text, { ...(!screenReader && selected
875
- ? { color: palette.brand, bold: true }
876
- : {}), 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 -
877
902
  rowLabel.length -
878
903
  (option.selected && !screenReader ? 2 : 0))), option.description] })) : null] }) }, `${index}-${option.label}`));
879
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
@@ -881,27 +906,26 @@ export function SelectionMenu({ title, description, options, selectedIndex, foot
881
906
  : ''] })) : null, _jsx(Text, { dimColor: true, children: footer })] }));
882
907
  }
883
908
  export function ModelMenu({ options, effort, selectedIndex, width, screenReader, }) {
884
- const palette = useTuiPalette();
909
+ const theme = useTuiTheme();
885
910
  const current = options.find((option) => option.selected);
886
911
  const maxLabelWidth = options.reduce((max, option, index) => Math.max(max, `${index + 1}. ${option.label}`.length), 0);
887
- 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) => {
888
913
  const selected = index === selectedIndex;
889
914
  const rowLabel = `${index + 1}. ${option.label}`;
890
- return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsxs(Text, { ...(!screenReader && selected
891
- ? { color: palette.brand, bold: true }
892
- : {}), 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 -
893
916
  rowLabel.length -
894
917
  (option.selected && !screenReader ? 2 : 0))), option.description] })) : null] }) }, `${index}-${option.label}`));
895
- }), _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" }) })] }));
896
919
  }
897
920
  function ComposerInput({ input, cursor }) {
898
- const palette = useTuiPalette();
921
+ const theme = useTuiTheme();
899
922
  const { before, current, after } = composerEditorSegments({
900
923
  text: input,
901
924
  cursor,
902
925
  });
903
926
  const cursorText = current === '\n' ? '↵' : (current ?? ' ');
904
- 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] }));
905
929
  }
906
930
  export function ExternalEditorWait({ screenReader, }) {
907
931
  if (screenReader)
@@ -909,7 +933,7 @@ export function ExternalEditorWait({ screenReader, }) {
909
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" })] }));
910
934
  }
911
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, }) {
912
- const palette = useTuiPalette();
936
+ const theme = useTuiTheme();
913
937
  const [spinnerIndex, setSpinnerIndex] = useState(0);
914
938
  useEffect(() => {
915
939
  if (!busy || screenReader || reduceMotion)
@@ -927,7 +951,7 @@ export function Composer({ input, cursor, busy, clipboardBusy = false, status, d
927
951
  : `Prompt: ${input}` }));
928
952
  const { lineWidth, footerWidth, showEditorHint } = composerLayoutForWidth(width);
929
953
  const line = '─'.repeat(lineWidth);
930
- const separatorColor = sessionColor === undefined ? undefined : palette.sessionColors[sessionColor];
954
+ const separatorColor = sessionColor === undefined ? undefined : theme.session(sessionColor);
931
955
  const footerMode = `⏵⏵ ${permissionLabel(display.permissionMode)}`;
932
956
  const footerCompactMode = `⏵⏵ ${compactPermissionLabel(display.permissionMode)}`;
933
957
  const footerLeft = composerFooterLeft(footerWidth, busy, hasThinking, thinkingExpanded, footerMode, footerCompactMode);
@@ -940,14 +964,14 @@ export function Composer({ input, cursor, busy, clipboardBusy = false, status, d
940
964
  ? ` · ${usage.cacheReadInputTokens} cached`
941
965
  : '', costUsd === undefined ? '' : ` · $${costUsd.toFixed(6)}`] })) : null, _jsx(Text, { ...(separatorColor === undefined
942
966
  ? { dimColor: true }
943
- : { 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
944
968
  ? 'Enter a shell command'
945
969
  : 'Try "review this project"' }))] })), _jsx(Text, { ...(separatorColor === undefined
946
970
  ? { dimColor: true }
947
- : { 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 })) }) }))] }));
948
972
  }
949
973
  export function DialogFrame({ title, children, screenReader, }) {
950
- const palette = useTuiPalette();
951
- 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] }));
952
976
  }
953
977
  //# sourceMappingURL=claude-style.js.map