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.
- package/README.md +5 -1
- package/dist/application/background-agent-manager.d.ts +16 -6
- package/dist/application/background-agent-manager.js +139 -97
- package/dist/application/context-engine.d.ts +56 -0
- package/dist/application/context-engine.js +129 -0
- package/dist/application/session-service.d.ts +8 -4
- package/dist/application/session-service.js +528 -434
- package/dist/application/subagent-service.js +358 -177
- package/dist/application/top-level-agent-manager.d.ts +1 -0
- package/dist/application/top-level-agent-manager.js +1025 -403
- package/dist/application/transcript-projection.d.ts +33 -0
- package/dist/application/transcript-projection.js +110 -0
- package/dist/application/turn-lifecycle.d.ts +2 -0
- package/dist/application/turn-memory-coordinator.d.ts +43 -0
- package/dist/application/turn-memory-coordinator.js +98 -0
- package/dist/cli/agents-dashboard.js +10 -5
- package/dist/cli/interactive.js +264 -204
- package/dist/cli/tui/claude-style.js +154 -130
- package/dist/cli/tui/task-panel.js +10 -1
- package/dist/cli/tui/theme.d.ts +46 -3
- package/dist/cli/tui/theme.js +414 -56
- package/dist/cli/tui/tool-permission.js +9 -5
- package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
- package/dist/cli/tui/tui-interaction-router.js +127 -0
- package/dist/cli-runtime.d.ts +2 -2
- package/dist/core/agent-orchestration.d.ts +31 -0
- package/dist/core/agent-orchestration.js +235 -0
- package/dist/core/runtime.d.ts +5 -0
- package/dist/core/runtime.js +15 -1
- package/dist/persistence/claude-job-store.d.ts +58 -0
- package/dist/persistence/claude-job-store.js +483 -1
- package/dist/persistence/native-transcript-reader.d.ts +53 -0
- package/dist/persistence/native-transcript-reader.js +283 -0
- package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
- package/dist/persistence/subagent-lifecycle-store.js +373 -146
- package/dist/platform/exclusive-file-lease.d.ts +3 -0
- package/dist/platform/exclusive-file-lease.js +3 -0
- 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 {
|
|
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
|
|
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, {
|
|
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
|
|
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, {
|
|
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,
|
|
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, {
|
|
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, {
|
|
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(
|
|
215
|
-
return [
|
|
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,
|
|
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,
|
|
232
|
+
return _jsx(Text, { bold: true, children: inlineTextElement(shape.content, theme) });
|
|
223
233
|
if (shape.role === 'h2')
|
|
224
|
-
return (_jsx(Text, { bold: true,
|
|
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,
|
|
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,
|
|
238
|
+
return (_jsxs(Text, { children: [' • ', inlineTextElement(shape.content, theme)] }));
|
|
229
239
|
if (shape.role === 'ordered')
|
|
230
|
-
return (_jsxs(Text, { children: [' ', inlineTextElement(shape.content,
|
|
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,
|
|
242
|
+
return (_jsxs(Text, { dimColor: true, children: ['│ ', inlineTextElement(shape.content, theme)] }));
|
|
233
243
|
if (shape.role === 'plain')
|
|
234
|
-
return inlineTextElement(shape.content,
|
|
244
|
+
return inlineTextElement(shape.content, theme);
|
|
235
245
|
return _jsx(Text, { children: " " });
|
|
236
246
|
}
|
|
237
|
-
function cachedMarkdownLineElement(line,
|
|
238
|
-
const key = [line, markdownLineStyleSignature(
|
|
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,
|
|
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(
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
syntax
|
|
303
|
-
syntax
|
|
304
|
-
syntax
|
|
305
|
-
syntax
|
|
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,
|
|
311
|
-
if (
|
|
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(
|
|
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(
|
|
334
|
+
return (_jsx(Text, { ...tuiSyntaxStyle(theme, kind), children: token }, index));
|
|
319
335
|
}) })] }));
|
|
320
336
|
}
|
|
321
|
-
function cachedSyntaxCodeLine(text, prefix, change,
|
|
322
|
-
const key = [text, prefix, change ?? '', syntaxStyleSignature(
|
|
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,
|
|
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
|
|
337
|
-
return cachedSyntaxCodeLine(text, prefix, change,
|
|
352
|
+
const theme = useTuiTheme();
|
|
353
|
+
return cachedSyntaxCodeLine(text, prefix, change, theme);
|
|
338
354
|
}
|
|
339
355
|
function ToolResultText({ text, prefix = '', }) {
|
|
340
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
|
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, { ...(
|
|
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(
|
|
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,
|
|
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,
|
|
458
|
-
return cloneElement(cachedMarkdownLineElement(line.line,
|
|
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,
|
|
464
|
-
const key = [text, markdownTextStyleSignature(
|
|
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,
|
|
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
|
|
479
|
-
return cachedMarkdownTextElement(text,
|
|
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
|
|
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, {
|
|
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, {
|
|
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, {
|
|
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, {
|
|
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 ?
|
|
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 ?
|
|
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' ?
|
|
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, {
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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
|
-
|
|
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, {
|
|
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
|
-
|
|
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
|
|
701
|
+
const theme = useTuiTheme();
|
|
676
702
|
const panelWidth = Math.min(100, width);
|
|
677
|
-
return (_jsxs(Box, { flexDirection: "column", width: panelWidth, children: [!screenReader ? (_jsx(Text, {
|
|
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,
|
|
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, {
|
|
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, {
|
|
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, {
|
|
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
|
|
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, {
|
|
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
|
|
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,
|
|
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 ?
|
|
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
|
|
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 ?
|
|
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, {
|
|
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, {
|
|
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, {
|
|
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
|
|
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: [
|
|
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
|
-
?
|
|
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
|
|
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',
|
|
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
|
|
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',
|
|
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, {
|
|
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
|
|
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
|
-
|
|
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
|
|
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 :
|
|
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, {
|
|
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, {
|
|
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
|
|
951
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round',
|
|
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
|