praxis-agent 0.2.0 → 0.3.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 +18 -4
- package/dist/application/session-service.d.ts +34 -0
- package/dist/application/session-service.d.ts.map +1 -1
- package/dist/application/session-service.js +646 -38
- package/dist/application/session-service.js.map +1 -1
- package/dist/cli/interactive.d.ts +68 -8
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +2617 -206
- package/dist/cli/interactive.js.map +1 -1
- package/dist/cli/protocol.d.ts +1 -0
- package/dist/cli/protocol.d.ts.map +1 -1
- package/dist/cli/protocol.js.map +1 -1
- package/dist/cli/tui/claude-style.d.ts +142 -3
- package/dist/cli/tui/claude-style.d.ts.map +1 -1
- package/dist/cli/tui/claude-style.js +377 -16
- package/dist/cli/tui/claude-style.js.map +1 -1
- package/dist/cli/tui/clipboard.d.ts +24 -0
- package/dist/cli/tui/clipboard.d.ts.map +1 -0
- package/dist/cli/tui/clipboard.js +153 -0
- package/dist/cli/tui/clipboard.js.map +1 -0
- package/dist/cli/tui/composer-editor.d.ts +19 -0
- package/dist/cli/tui/composer-editor.d.ts.map +1 -0
- package/dist/cli/tui/composer-editor.js +99 -0
- package/dist/cli/tui/composer-editor.js.map +1 -0
- package/dist/cli/tui/composer-images.d.ts +7 -0
- package/dist/cli/tui/composer-images.d.ts.map +1 -0
- package/dist/cli/tui/composer-images.js +48 -0
- package/dist/cli/tui/composer-images.js.map +1 -0
- package/dist/cli/tui/conversation-export.d.ts +6 -0
- package/dist/cli/tui/conversation-export.d.ts.map +1 -0
- package/dist/cli/tui/conversation-export.js +76 -0
- package/dist/cli/tui/conversation-export.js.map +1 -0
- package/dist/cli/tui/external-editor.d.ts +21 -0
- package/dist/cli/tui/external-editor.d.ts.map +1 -0
- package/dist/cli/tui/external-editor.js +144 -0
- package/dist/cli/tui/external-editor.js.map +1 -0
- package/dist/cli/tui/file-picker.d.ts +33 -0
- package/dist/cli/tui/file-picker.d.ts.map +1 -0
- package/dist/cli/tui/file-picker.js +117 -0
- package/dist/cli/tui/file-picker.js.map +1 -0
- package/dist/cli/tui/git-diff.d.ts +14 -0
- package/dist/cli/tui/git-diff.d.ts.map +1 -0
- package/dist/cli/tui/git-diff.js +55 -0
- package/dist/cli/tui/git-diff.js.map +1 -0
- package/dist/cli/tui/keybindings.d.ts +14 -0
- package/dist/cli/tui/keybindings.d.ts.map +1 -0
- package/dist/cli/tui/keybindings.js +430 -0
- package/dist/cli/tui/keybindings.js.map +1 -0
- package/dist/cli/tui/permission-settings.d.ts +18 -0
- package/dist/cli/tui/permission-settings.d.ts.map +1 -0
- package/dist/cli/tui/permission-settings.js +109 -0
- package/dist/cli/tui/permission-settings.js.map +1 -0
- package/dist/cli/tui/slash-commands.d.ts +11 -0
- package/dist/cli/tui/slash-commands.d.ts.map +1 -0
- package/dist/cli/tui/slash-commands.js +183 -0
- package/dist/cli/tui/slash-commands.js.map +1 -0
- package/dist/cli/tui/terminal-suspend.d.ts +5 -0
- package/dist/cli/tui/terminal-suspend.d.ts.map +1 -0
- package/dist/cli/tui/terminal-suspend.js +9 -0
- package/dist/cli/tui/terminal-suspend.js.map +1 -0
- package/dist/cli/tui/workspace-directories.d.ts +3 -0
- package/dist/cli/tui/workspace-directories.d.ts.map +1 -0
- package/dist/cli/tui/workspace-directories.js +55 -0
- package/dist/cli/tui/workspace-directories.js.map +1 -0
- package/dist/cli.d.ts +23 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +45 -5
- package/dist/cli.js.map +1 -1
- package/dist/compatibility/claude/compaction.d.ts +8 -0
- package/dist/compatibility/claude/compaction.d.ts.map +1 -1
- package/dist/compatibility/claude/compaction.js +45 -8
- package/dist/compatibility/claude/compaction.js.map +1 -1
- package/dist/compatibility/claude/fork.d.ts.map +1 -1
- package/dist/compatibility/claude/fork.js +24 -6
- package/dist/compatibility/claude/fork.js.map +1 -1
- package/dist/compatibility/claude/history.d.ts.map +1 -1
- package/dist/compatibility/claude/history.js +41 -1
- package/dist/compatibility/claude/history.js.map +1 -1
- package/dist/compatibility/claude/projection.d.ts +29 -1
- package/dist/compatibility/claude/projection.d.ts.map +1 -1
- package/dist/compatibility/claude/projection.js +132 -2
- package/dist/compatibility/claude/projection.js.map +1 -1
- package/dist/compatibility/claude/schema.d.ts.map +1 -1
- package/dist/compatibility/claude/schema.js +34 -7
- package/dist/compatibility/claude/schema.js.map +1 -1
- package/dist/compatibility/claude/translation.d.ts +7 -0
- package/dist/compatibility/claude/translation.d.ts.map +1 -1
- package/dist/compatibility/claude/translation.js +20 -0
- package/dist/compatibility/claude/translation.js.map +1 -1
- package/dist/core/runtime.d.ts +19 -0
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +30 -11
- package/dist/core/runtime.js.map +1 -1
- package/dist/extensions/claude-extensions.d.ts +12 -0
- package/dist/extensions/claude-extensions.d.ts.map +1 -1
- package/dist/extensions/claude-extensions.js +49 -0
- package/dist/extensions/claude-extensions.js.map +1 -1
- package/dist/hooks/claude-hook-tools.js +2 -2
- package/dist/hooks/claude-hook-tools.js.map +1 -1
- package/dist/persistence/claude-transcript-store.d.ts.map +1 -1
- package/dist/persistence/claude-transcript-store.js +4 -3
- package/dist/persistence/claude-transcript-store.js.map +1 -1
- package/dist/tools/claude-interactive-tools.d.ts +1 -0
- package/dist/tools/claude-interactive-tools.d.ts.map +1 -1
- package/dist/tools/claude-interactive-tools.js +12 -0
- package/dist/tools/claude-interactive-tools.js.map +1 -1
- package/dist/tools/local-tools.d.ts.map +1 -1
- package/dist/tools/local-tools.js +11 -0
- package/dist/tools/local-tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Box, Text, useStdout } from 'ink';
|
|
4
|
+
import { composerEditorSegments } from './composer-editor.js';
|
|
5
|
+
import { visiblePatchLines } from './git-diff.js';
|
|
4
6
|
const BRAND = '#D97757';
|
|
5
7
|
const ACCENT = '#B8A1FF';
|
|
6
8
|
const SPINNER = ['✳', '✢', '✣', '✤', '✥'];
|
|
@@ -43,9 +45,9 @@ function permissionLabel(mode) {
|
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
export function WelcomePanel({ display, width, }) {
|
|
46
|
-
const panelWidth = Math.min(
|
|
48
|
+
const panelWidth = Math.min(100, Math.max(32, width));
|
|
47
49
|
const wide = panelWidth >= 68;
|
|
48
|
-
return (_jsxs(Box, { borderStyle: "round", borderColor: "gray", flexDirection: "column", width: panelWidth, paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: BRAND, bold: true, children: "Praxis" }), _jsxs(Text, { children: [" Code v", display.version, " "] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(1, panelWidth - 24)) })] }), _jsxs(Box, { flexDirection: wide ? 'row' : 'column', marginTop: 1, children: [_jsxs(Box, { flexDirection: "column", width: wide ? '
|
|
50
|
+
return (_jsxs(Box, { borderStyle: "round", borderColor: "gray", flexDirection: "column", width: panelWidth, paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: BRAND, bold: true, children: "Praxis" }), _jsxs(Text, { children: [" Code v", display.version, " "] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(1, panelWidth - 24)) })] }), _jsxs(Box, { flexDirection: wide ? 'row' : 'column', marginTop: 1, children: [_jsxs(Box, { alignItems: wide ? 'center' : undefined, flexDirection: "column", width: wide ? '50%' : '100%', children: [_jsx(Text, { bold: true, children: "Welcome back!" }), _jsx(Text, { color: BRAND, bold: true, children: "\u2590\u259B\u2588\u2588\u2588\u259C\u258C" }), _jsx(Text, { color: BRAND, children: "\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u259B\u2598" }), _jsx(Text, { color: BRAND, children: " \u2598\u2598 \u259D\u259D" }), _jsxs(Text, { children: [display.model ?? 'provider default', display.effort ? (_jsxs(Text, { dimColor: true, children: [" \u00B7 ", display.effort, " effort"] })) : null] }), _jsx(Text, { dimColor: true, children: compactPath(display.cwd) })] }), _jsxs(Box, { flexDirection: "column", width: wide ? '50%' : '100%', marginTop: wide ? 0 : 1, children: [_jsx(Text, { bold: true, children: "Tips for getting started" }), _jsx(Text, { children: "Run /help for commands" }), _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" }), _jsx(Text, { bold: true, children: "Shared with Claude Code" }), _jsx(Text, { children: "Sessions, memory, skills" })] })] })] }));
|
|
49
51
|
}
|
|
50
52
|
function InlineText({ text }) {
|
|
51
53
|
const tokens = text.split(/(`[^`]+`|\*\*[^*]+\*\*|\[[^\]]+\]\([^)]+\))/u);
|
|
@@ -84,6 +86,62 @@ function ToolResultText({ text }) {
|
|
|
84
86
|
? { color: 'cyan' }
|
|
85
87
|
: { dimColor: true }), children: line || ' ' }, index))) }));
|
|
86
88
|
}
|
|
89
|
+
function inputString(call, key) {
|
|
90
|
+
const value = call.input[key];
|
|
91
|
+
return typeof value === 'string' ? value : '';
|
|
92
|
+
}
|
|
93
|
+
function contentLines(text) {
|
|
94
|
+
const lines = text.split('\n');
|
|
95
|
+
if (lines.at(-1) === '')
|
|
96
|
+
lines.pop();
|
|
97
|
+
return lines;
|
|
98
|
+
}
|
|
99
|
+
function toolHeading(call) {
|
|
100
|
+
if (call.name === 'Edit')
|
|
101
|
+
return `Update(${inputString(call, 'file_path')})`;
|
|
102
|
+
if (call.name === 'Read')
|
|
103
|
+
return `Read(${inputString(call, 'file_path')})`;
|
|
104
|
+
if (call.name === 'Bash')
|
|
105
|
+
return `Bash(${inputString(call, 'command')})`;
|
|
106
|
+
return call.name;
|
|
107
|
+
}
|
|
108
|
+
function ToolTranscriptEntry({ call, detail, result, detailed, }) {
|
|
109
|
+
const displayResult = call.name === 'Read' &&
|
|
110
|
+
result?.text.startsWith('Wasted call — file unchanged since your last Read')
|
|
111
|
+
? 'Unchanged since last read'
|
|
112
|
+
: (result?.text ?? '');
|
|
113
|
+
const resultLines = contentLines(displayResult);
|
|
114
|
+
if (call.name === 'Read' && result && !result.isError && !detailed) {
|
|
115
|
+
return (_jsxs(Text, { children: [' ', "Read 1 file ", _jsx(Text, { dimColor: true, children: "(ctrl+o to expand)" })] }));
|
|
116
|
+
}
|
|
117
|
+
const visible = detailed ? resultLines : resultLines.slice(0, 3);
|
|
118
|
+
const hidden = resultLines.length - visible.length;
|
|
119
|
+
const errorText = displayResult.length > 500
|
|
120
|
+
? `${displayResult.slice(0, 497)}...`
|
|
121
|
+
: displayResult;
|
|
122
|
+
const oldLines = contentLines(inputString(call, 'old_string'));
|
|
123
|
+
const newLines = contentLines(inputString(call, 'new_string'));
|
|
124
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: 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: "red", 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'] }), oldLines.map((line, index) => (_jsxs(Text, { color: "red", children: [' ', index + 1, " -", line] }, `old-${index}`))), newLines.map((line, index) => (_jsxs(Text, { color: "green", children: [' ', index + 1, " +", line] }, `new-${index}`)))] })) : (_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] }));
|
|
125
|
+
}
|
|
126
|
+
function ThinkingBlock({ text, active, expanded, screenReader, }) {
|
|
127
|
+
const summary = text.replace(/\s+/gu, ' ').trim();
|
|
128
|
+
const showFull = screenReader || active || expanded;
|
|
129
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { ...(!screenReader ? { color: 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] }));
|
|
130
|
+
}
|
|
131
|
+
function ContextUsageBlock({ usedTokens, contextWindowTokens, skills, screenReader, }) {
|
|
132
|
+
const totalTokens = Math.max(1, contextWindowTokens);
|
|
133
|
+
const compactBuffer = Math.round(totalTokens * 0.165);
|
|
134
|
+
const usable = Math.max(1, totalTokens - compactBuffer);
|
|
135
|
+
if (screenReader) {
|
|
136
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Context Usage" }), _jsxs(Text, { children: [usedTokens.toLocaleString(), " of ", totalTokens.toLocaleString(), " tokens"] }), _jsxs(Text, { children: ["Autocompact buffer: ", compactBuffer.toLocaleString(), " tokens"] }), _jsxs(Text, { children: ["Skills: ", skills.map(({ name }) => name).join(', ') || 'none'] })] }));
|
|
137
|
+
}
|
|
138
|
+
const usedCells = Math.min(100, Math.ceil((usedTokens / totalTokens) * 100));
|
|
139
|
+
const bufferCells = Math.min(100 - usedCells, Math.ceil((compactBuffer / totalTokens) * 100));
|
|
140
|
+
const cells = Array.from({ length: 100 }, (_, index) => index < usedCells ? '⛁' : index >= 100 - bufferCells ? '⛝' : '⛶');
|
|
141
|
+
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: 10 }, (_, row) => (_jsx(Text, { children: cells.slice(row * 10, row * 10 + 10).join(' ') }, row))) }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [usedTokens.toLocaleString(), "/", totalTokens.toLocaleString(), " tokens"] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Estimated usage by category" }), _jsxs(Text, { children: ["\u26C1 Skills:", ' ', skills
|
|
142
|
+
.reduce((total, skill) => total + skill.tokens, 0)
|
|
143
|
+
.toLocaleString(), ' ', "tokens"] }), _jsxs(Text, { children: ["\u26F6 Free space: ", Math.max(0, usable - usedTokens).toLocaleString()] }), _jsxs(Text, { children: ["\u26DD Autocompact buffer: ", compactBuffer.toLocaleString(), " tokens"] })] })] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Auto-compact window: ", totalTokens.toLocaleString(), " tokens"] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Skills \u00B7 /skills" }), 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))))] }));
|
|
144
|
+
}
|
|
87
145
|
export function MarkdownText({ text }) {
|
|
88
146
|
const lines = text.split('\n');
|
|
89
147
|
let code = false;
|
|
@@ -95,39 +153,328 @@ export function MarkdownText({ text }) {
|
|
|
95
153
|
return code ? (_jsxs(Text, { color: "cyan", children: ["\u2502 ", line] }, index)) : (_jsx(MarkdownLine, { line: line }, index));
|
|
96
154
|
}) }));
|
|
97
155
|
}
|
|
98
|
-
export function Transcript({ items, activeText, activeThinking = '', screenReader, }) {
|
|
156
|
+
export function Transcript({ items, activeText, activeThinking = '', thinkingExpanded = false, detailedTranscript = false, screenReader, }) {
|
|
157
|
+
const detailed = thinkingExpanded || detailedTranscript;
|
|
158
|
+
const results = new Map(items
|
|
159
|
+
.filter((item) => item.kind === 'tool-result')
|
|
160
|
+
.map((item) => [item.callId, item]));
|
|
161
|
+
const pairedResults = new Set(items
|
|
162
|
+
.filter((item) => item.kind === 'tool' && results.has(item.call.id))
|
|
163
|
+
.map((item) => item.call.id));
|
|
164
|
+
const shellResults = new Map(items
|
|
165
|
+
.filter((item) => item.kind === 'shell-result')
|
|
166
|
+
.map((item) => [item.callId, item]));
|
|
167
|
+
const pairedShellResults = new Set(items
|
|
168
|
+
.filter((item) => item.kind === 'shell' && shellResults.has(item.callId))
|
|
169
|
+
.map((item) => item.callId));
|
|
170
|
+
const readGroupStarts = new Map();
|
|
171
|
+
const groupedReadTools = new Set();
|
|
172
|
+
if (!detailed && !screenReader) {
|
|
173
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
174
|
+
const first = items[index];
|
|
175
|
+
if (first?.kind !== 'tool' || first.call.name !== 'Read')
|
|
176
|
+
continue;
|
|
177
|
+
let cursor = index;
|
|
178
|
+
const memberIndexes = [];
|
|
179
|
+
const memberCallIds = new Set();
|
|
180
|
+
while (cursor < items.length) {
|
|
181
|
+
const candidate = items[cursor];
|
|
182
|
+
if (candidate?.kind === 'tool' && candidate.call.name === 'Read') {
|
|
183
|
+
const result = results.get(candidate.call.id);
|
|
184
|
+
if (!result || result.isError)
|
|
185
|
+
break;
|
|
186
|
+
memberIndexes.push(cursor);
|
|
187
|
+
memberCallIds.add(candidate.call.id);
|
|
188
|
+
cursor += 1;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (candidate?.kind === 'tool-result' &&
|
|
192
|
+
memberCallIds.has(candidate.callId)) {
|
|
193
|
+
cursor += 1;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
if (memberIndexes.length === 0)
|
|
199
|
+
continue;
|
|
200
|
+
readGroupStarts.set(index, memberIndexes.length);
|
|
201
|
+
for (const memberIndex of memberIndexes.slice(1)) {
|
|
202
|
+
groupedReadTools.add(memberIndex);
|
|
203
|
+
}
|
|
204
|
+
index = cursor - 1;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
99
207
|
return (_jsxs(Box, { flexDirection: "column", children: [items.map((item, index) => {
|
|
208
|
+
const readCount = readGroupStarts.get(index);
|
|
209
|
+
if (readCount !== undefined) {
|
|
210
|
+
return (_jsxs(Text, { children: [' ', "Read ", readCount, " file", readCount === 1 ? '' : 's', ' ', _jsx(Text, { dimColor: true, children: "(ctrl+o to expand)" })] }, index));
|
|
211
|
+
}
|
|
212
|
+
if (groupedReadTools.has(index))
|
|
213
|
+
return null;
|
|
100
214
|
if (item.kind === 'user') {
|
|
101
215
|
return (_jsxs(Box, { marginTop: index === 0 ? 0 : 1, children: [_jsx(Text, { color: BRAND, bold: true, children: screenReader ? 'You: ' : '❯ ' }), _jsx(Text, { bold: true, children: item.text })] }, index));
|
|
102
216
|
}
|
|
103
217
|
if (item.kind === 'assistant') {
|
|
104
|
-
return (_jsxs(Box, {
|
|
218
|
+
return (_jsxs(Box, { marginTop: 1, children: [screenReader ? _jsx(Text, { children: "Praxis:" }) : null, !screenReader ? _jsx(Text, { color: ACCENT, children: "\u23FA " }) : null, _jsx(MarkdownText, { text: item.text })] }, index));
|
|
105
219
|
}
|
|
106
220
|
if (item.kind === 'thinking') {
|
|
107
|
-
return (
|
|
221
|
+
return (_jsx(ThinkingBlock, { text: item.text, active: false, expanded: detailed, screenReader: screenReader }, index));
|
|
222
|
+
}
|
|
223
|
+
if (item.kind === 'compact') {
|
|
224
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: ACCENT, italic: true, children: screenReader
|
|
225
|
+
? 'Conversation compacted'
|
|
226
|
+
: '✻ Conversation compacted (ctrl+o for history)' }), detailed ? (_jsx(Box, { marginLeft: screenReader ? 0 : 2, children: _jsx(MarkdownText, { text: item.summary }) })) : null] }, index));
|
|
227
|
+
}
|
|
228
|
+
if (item.kind === 'context') {
|
|
229
|
+
return (_jsx(ContextUsageBlock, { ...item, screenReader: screenReader }, index));
|
|
108
230
|
}
|
|
109
231
|
if (item.kind === 'tool') {
|
|
110
|
-
|
|
232
|
+
const result = results.get(item.call.id);
|
|
233
|
+
return (_jsx(ToolTranscriptEntry, { call: item.call, detail: item.detail, ...(result ? { result } : {}), detailed: detailed || screenReader }, index));
|
|
111
234
|
}
|
|
112
235
|
if (item.kind === 'tool-result') {
|
|
236
|
+
if (pairedResults.has(item.callId))
|
|
237
|
+
return null;
|
|
113
238
|
const text = item.text.length > 500 ? `${item.text.slice(0, 497)}...` : item.text;
|
|
114
239
|
return (_jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { color: item.isError ? 'red' : 'gray', children: item.isError ? '└ Error' : '└ Result' }), item.isError ? (_jsx(Text, { color: "red", children: text })) : (_jsx(ToolResultText, { text: text }))] }, index));
|
|
115
240
|
}
|
|
241
|
+
if (item.kind === 'shell') {
|
|
242
|
+
const result = shellResults.get(item.callId);
|
|
243
|
+
const output = result
|
|
244
|
+
? [result.stdout, result.stderr]
|
|
245
|
+
.filter(Boolean)
|
|
246
|
+
.join(result.stdout &&
|
|
247
|
+
result.stderr &&
|
|
248
|
+
!result.stdout.endsWith('\n')
|
|
249
|
+
? '\n'
|
|
250
|
+
: '')
|
|
251
|
+
: '';
|
|
252
|
+
const lines = contentLines(output);
|
|
253
|
+
const visible = detailed || screenReader ? lines : lines.slice(0, 3);
|
|
254
|
+
const hidden = lines.length - visible.length;
|
|
255
|
+
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: 'red' } : {}), dimColor: !result.isError, children: [lineIndex === 0 ? '⎿ ' : ' ', line || ' '] }, lineIndex)))), hidden > 0 ? (_jsxs(Text, { dimColor: true, children: [' ', "\u2026 +", hidden, " lines (ctrl+o to expand)"] })) : null] })) : null] }, index));
|
|
256
|
+
}
|
|
257
|
+
if (item.kind === 'shell-result') {
|
|
258
|
+
if (pairedShellResults.has(item.callId))
|
|
259
|
+
return null;
|
|
260
|
+
const output = [item.stdout, item.stderr].filter(Boolean).join('\n');
|
|
261
|
+
return (_jsxs(Text, { ...(item.isError ? { color: 'red' } : {}), children: ["\u23BF ", output] }, index));
|
|
262
|
+
}
|
|
263
|
+
if (item.kind === 'local-result') {
|
|
264
|
+
return (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["\u23BF ", item.text] }) }, index));
|
|
265
|
+
}
|
|
116
266
|
return (_jsxs(Text, { ...(item.kind === 'warning' ? { color: 'red' } : {}), dimColor: item.kind === 'notice', children: [item.kind === 'warning' ? '⚠ ' : '· ', item.text] }, index));
|
|
117
|
-
}), activeThinking ? (
|
|
267
|
+
}), activeThinking ? (_jsx(ThinkingBlock, { text: activeThinking, active: true, expanded: detailed, screenReader: screenReader })) : activeText ? (_jsxs(Box, { marginTop: 1, children: [screenReader ? (_jsx(Text, { children: "Praxis: " })) : (_jsx(Text, { color: ACCENT, children: "\u2733 " })), _jsx(MarkdownText, { text: activeText })] })) : null] }));
|
|
268
|
+
}
|
|
269
|
+
export function DiffDashboard({ snapshots, sourceIndex, selectedIndex, viewingFile, scrollOffset, width, screenReader, }) {
|
|
270
|
+
const source = snapshots[sourceIndex];
|
|
271
|
+
const snapshot = source?.snapshot ?? { files: [], additions: 0, deletions: 0 };
|
|
272
|
+
const selected = snapshot.files[selectedIndex];
|
|
273
|
+
const panelWidth = Math.max(32, Math.min(100, width));
|
|
274
|
+
const line = '─'.repeat(panelWidth);
|
|
275
|
+
if (viewingFile && selected) {
|
|
276
|
+
const lines = visiblePatchLines(selected.patch).slice(scrollOffset, scrollOffset + 18);
|
|
277
|
+
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: index === sourceIndex, children: [' ', item.label, ' '] }, item.label)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { bold: true, children: [" ", selected.path] }), _jsxs(Text, { dimColor: true, children: [" ", '─'.repeat(Math.max(1, panelWidth - 4))] }), lines.map((patchLine, index) => (_jsxs(Text, { ...(patchLine.startsWith('+')
|
|
278
|
+
? { color: 'green' }
|
|
279
|
+
: patchLine.startsWith('-')
|
|
280
|
+
? { color: 'red' }
|
|
281
|
+
: { dimColor: true }), children: [' ', patchLine] }, `${scrollOffset}-${index}`))), _jsx(Text, { dimColor: true, children: " \u2191/\u2193 to scroll \u00B7 Esc to back" })] }));
|
|
282
|
+
}
|
|
283
|
+
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: index === sourceIndex, children: [' ', item.label, ' '] }, item.label)))] }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [' ', snapshot.files.length, " file", snapshot.files.length === 1 ? '' : 's', ' ', "changed ", _jsxs(Text, { color: "green", children: ["+", snapshot.additions] }), ' ', _jsxs(Text, { color: "red", 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: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', file.path, " ", _jsxs(Text, { color: "green", children: ["+", file.additions] }), ' ', _jsxs(Text, { color: "red", 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" })] }));
|
|
284
|
+
}
|
|
285
|
+
export function PermissionDashboard({ tabIndex, selectedIndex, query, rules, recentDenied, workspaceDirectories, width, screenReader, }) {
|
|
286
|
+
const tabs = ['Recently denied', 'Allow', 'Ask', 'Deny', 'Workspace'];
|
|
287
|
+
const behavior = ['allow', 'ask', 'deny'][tabIndex - 1];
|
|
288
|
+
const normalizedQuery = query.trim().toLowerCase();
|
|
289
|
+
const matchingRules = behavior
|
|
290
|
+
? rules.filter((rule) => rule.behavior === behavior &&
|
|
291
|
+
(!normalizedQuery ||
|
|
292
|
+
rule.rule.toLowerCase().includes(normalizedQuery) ||
|
|
293
|
+
rule.scope.includes(normalizedQuery)))
|
|
294
|
+
: [];
|
|
295
|
+
const rows = tabIndex === 0 ? recentDenied : matchingRules.map((rule) => rule.rule);
|
|
296
|
+
const originalWorkspace = workspaceDirectories.find((directory) => directory.original);
|
|
297
|
+
const additionalWorkspaces = workspaceDirectories.filter((directory) => !directory.original);
|
|
298
|
+
const description = tabIndex === 0
|
|
299
|
+
? recentDenied.length === 0
|
|
300
|
+
? 'No recent denials. Commands denied by the auto mode classifier will appear here.'
|
|
301
|
+
: 'Commands denied by the auto mode classifier appear here.'
|
|
302
|
+
: tabIndex === 1
|
|
303
|
+
? "Praxis Code won't ask before using allowed tools."
|
|
304
|
+
: tabIndex === 2
|
|
305
|
+
? 'Praxis Code will always ask for confirmation before using these tools.'
|
|
306
|
+
: tabIndex === 3
|
|
307
|
+
? 'Praxis Code will reject requests to use denied tools.'
|
|
308
|
+
: 'Praxis Code can read files in the workspace, and make edits when auto-accept edits is on.';
|
|
309
|
+
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: index === tabIndex, children: [' ', 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: ["\u2315 ", query || 'Search…'] }) })) : (_jsx(Text, { children: " " })), tabIndex >= 1 && tabIndex <= 3 ? (['Add a new rule…', ...rows].map((row, index) => (_jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", row] }, `${index}-${row}`)))) : tabIndex === 4 ? (_jsxs(_Fragment, { children: [originalWorkspace ? (_jsxs(Text, { children: [' - ', originalWorkspace.path, " (Original working directory)"] })) : null, additionalWorkspaces.map((directory, index) => (_jsxs(Text, { inverse: selectedIndex === index, children: [selectedIndex === index ? '❯ ' : ' ', index + 1, ". ", directory.path] }, directory.path))), _jsxs(Text, { inverse: selectedIndex === additionalWorkspaces.length, children: [selectedIndex === additionalWorkspaces.length ? '❯ ' : ' ', additionalWorkspaces.length + 1, ". Add directory\u2026"] })] })) : rows.length > 0 ? (rows.map((row, index) => (_jsxs(Text, { inverse: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', index + 1, ". ", row] }, `${index}-${row}`)))) : null, _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: selectedIndex >= 0
|
|
310
|
+
? '↑/↓ navigate · Enter to select · ←/→ to switch · Esc to cancel'
|
|
311
|
+
: '←/→ to switch · ↓ to select · Esc to cancel' })] }));
|
|
118
312
|
}
|
|
119
|
-
export function
|
|
313
|
+
export function StatusDashboard({ tabIndex, version, sessionId, display, usage, costUsd, turnCount, toolCount, commandCount, detailedTranscript, width, screenReader, }) {
|
|
314
|
+
const tabs = ['Settings', 'Status', 'Config', 'Usage', 'Stats'];
|
|
315
|
+
const rows = tabIndex === 0
|
|
316
|
+
? [
|
|
317
|
+
['Thinking mode', 'provider controlled'],
|
|
318
|
+
['Verbose output', detailedTranscript ? 'true' : 'false'],
|
|
319
|
+
['Default permission mode', permissionLabel(display.permissionMode)],
|
|
320
|
+
['Context compaction', 'automatic'],
|
|
321
|
+
['Shared Claude data', 'enabled'],
|
|
322
|
+
]
|
|
323
|
+
: tabIndex === 1
|
|
324
|
+
? [
|
|
325
|
+
['Version', version],
|
|
326
|
+
['Session ID', sessionId ?? 'new session'],
|
|
327
|
+
['cwd', display.cwd],
|
|
328
|
+
['Model', display.model ?? 'provider default'],
|
|
329
|
+
['Permission mode', permissionLabel(display.permissionMode)],
|
|
330
|
+
]
|
|
331
|
+
: tabIndex === 2
|
|
332
|
+
? [
|
|
333
|
+
['Model', display.model ?? 'provider default'],
|
|
334
|
+
['Effort', display.effort ?? 'high'],
|
|
335
|
+
[
|
|
336
|
+
'Context window',
|
|
337
|
+
String(display.contextWindowTokens ?? 'provider default'),
|
|
338
|
+
],
|
|
339
|
+
['Available commands', String(commandCount)],
|
|
340
|
+
]
|
|
341
|
+
: tabIndex === 3
|
|
342
|
+
? [
|
|
343
|
+
['Input tokens', String(usage?.inputTokens ?? 0)],
|
|
344
|
+
['Output tokens', String(usage?.outputTokens ?? 0)],
|
|
345
|
+
[
|
|
346
|
+
'Session cost',
|
|
347
|
+
costUsd === undefined
|
|
348
|
+
? 'unavailable'
|
|
349
|
+
: `$${costUsd.toFixed(4)}`,
|
|
350
|
+
],
|
|
351
|
+
]
|
|
352
|
+
: [
|
|
353
|
+
['Turns', String(turnCount)],
|
|
354
|
+
['Tool calls', String(toolCount)],
|
|
355
|
+
[
|
|
356
|
+
'Context used',
|
|
357
|
+
String((usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)),
|
|
358
|
+
],
|
|
359
|
+
];
|
|
360
|
+
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, { children: [' ', tabs.map((tab, index) => (_jsxs(Text, { inverse: index === tabIndex, children: [' ', tab, ' '] }, tab)))] }), _jsx(Text, { children: " " }), rows.map(([label, value]) => (_jsxs(Box, { children: [_jsx(Box, { width: 28, children: _jsxs(Text, { children: [label, ":"] }) }), _jsx(Text, { children: value })] }, label))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192/tab to switch \u00B7 Esc to close" })] }));
|
|
361
|
+
}
|
|
362
|
+
export function ListDashboard({ title, rows, emptyText, selectedIndex, width, screenReader, }) {
|
|
363
|
+
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" })] }));
|
|
364
|
+
}
|
|
365
|
+
export function SessionPicker({ sessions, selectedIndex, screenReader, query = '', }) {
|
|
120
366
|
const maxVisible = 8;
|
|
121
367
|
const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), sessions.length - maxVisible));
|
|
122
368
|
const visible = sessions.slice(start, start + maxVisible);
|
|
123
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round', borderColor: "gray", paddingX: screenReader ? 0 : 1, children:
|
|
369
|
+
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: "gray", 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) => {
|
|
124
370
|
const index = start + visibleIndex;
|
|
125
371
|
return (_jsxs(Box, { children: [_jsxs(Text, { ...(index === selectedIndex ? { color: BRAND } : {}), bold: index === selectedIndex, children: [index === selectedIndex ? '❯ ' : ' ', session
|
|
126
372
|
? (session.name ?? session.lastPrompt ?? 'Untitled')
|
|
127
373
|
: 'Start a new session'] }), session ? (_jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", session.sessionId, " \u00B7 ", session.status] })) : null] }, session?.sessionId ?? 'new'));
|
|
128
|
-
}), start + visible.length < sessions.length ? (_jsxs(Text, { dimColor: true, children: [" \u2193 ", sessions.length - start - visible.length, " more"] })) : null] }));
|
|
374
|
+
}), 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] }));
|
|
375
|
+
}
|
|
376
|
+
export function CommandPalette({ commands, selectedIndex, width, screenReader, }) {
|
|
377
|
+
const maxVisible = 12;
|
|
378
|
+
const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, commands.length - maxVisible)));
|
|
379
|
+
const visible = commands.slice(start, start + maxVisible);
|
|
380
|
+
if (screenReader) {
|
|
381
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Commands" }), visible.length === 0 ? (_jsx(Text, { children: "No matching commands." })) : (visible.map((command) => (_jsxs(Text, { children: ["/", command.name, ": ", command.description] }, command.name))))] }));
|
|
382
|
+
}
|
|
383
|
+
return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No matching commands." })) : (visible.map((command, visibleIndex) => {
|
|
384
|
+
const index = start + visibleIndex;
|
|
385
|
+
const selected = index === selectedIndex;
|
|
386
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 30, children: _jsxs(Text, { ...(selected ? { color: BRAND, bold: true } : {}), children: ["/", command.name] }) }), _jsx(Box, { flexGrow: 1, children: _jsx(Text, { dimColor: true, children: command.description }) })] }, command.name));
|
|
387
|
+
})) }));
|
|
388
|
+
}
|
|
389
|
+
export function FilePicker({ entries, selectedIndex, width, screenReader, }) {
|
|
390
|
+
const maxVisible = 12;
|
|
391
|
+
const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
|
|
392
|
+
const visible = entries.slice(start, start + maxVisible);
|
|
393
|
+
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) => {
|
|
394
|
+
const index = start + visibleIndex;
|
|
395
|
+
return (_jsxs(Text, { inverse: !screenReader && index === selectedIndex, children: [screenReader && index === selectedIndex ? 'Selected: ' : '+ ', entry.path] }, entry.path));
|
|
396
|
+
})) }));
|
|
397
|
+
}
|
|
398
|
+
export function MentionPicker({ entries, selectedIndex, width, screenReader, }) {
|
|
399
|
+
const maxVisible = 12;
|
|
400
|
+
const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, entries.length - maxVisible)));
|
|
401
|
+
const visible = entries.slice(start, start + maxVisible);
|
|
402
|
+
return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No matching files or agents." })) : (visible.map((entry, visibleIndex) => {
|
|
403
|
+
const index = start + visibleIndex;
|
|
404
|
+
const selected = index === selectedIndex;
|
|
405
|
+
if (entry.kind === 'agent') {
|
|
406
|
+
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}`));
|
|
407
|
+
}
|
|
408
|
+
return (_jsxs(Text, { inverse: !screenReader && selected, ...(screenReader ? {} : { wrap: 'truncate-end' }), children: [screenReader && selected ? 'Selected: ' : '+ ', entry.path] }, `file:${entry.path}`));
|
|
409
|
+
})) }));
|
|
410
|
+
}
|
|
411
|
+
const SHORTCUT_ROWS = [
|
|
412
|
+
[
|
|
413
|
+
'! for shell mode',
|
|
414
|
+
'double tap esc to clear input',
|
|
415
|
+
'ctrl + shift + _ to undo',
|
|
416
|
+
],
|
|
417
|
+
['/ for commands', 'shift + tab to cycle permissions', 'ctrl + z to suspend'],
|
|
418
|
+
[
|
|
419
|
+
'@ for file paths',
|
|
420
|
+
'ctrl + o for verbose output',
|
|
421
|
+
'ctrl + v to paste images',
|
|
422
|
+
],
|
|
423
|
+
[
|
|
424
|
+
'/btw for side question',
|
|
425
|
+
'ctrl + t to toggle tasks',
|
|
426
|
+
'opt + p to switch model',
|
|
427
|
+
],
|
|
428
|
+
['', 'backslash (\\) + return (⏎) for newline', 'ctrl + s to stash prompt'],
|
|
429
|
+
['', '', 'ctrl + g to edit in $EDITOR'],
|
|
430
|
+
['', '', '/keybindings to customize'],
|
|
431
|
+
];
|
|
432
|
+
export function ShortcutHelp({ width }) {
|
|
433
|
+
const wide = width >= 78;
|
|
434
|
+
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}`))))) }));
|
|
435
|
+
}
|
|
436
|
+
export function HelpMenu({ tabIndex, selectedIndex, builtinCommands, customCommands, width, screenReader, }) {
|
|
437
|
+
const tabs = ['General', 'Commands', 'Custom commands'];
|
|
438
|
+
const commands = tabIndex === 1 ? builtinCommands : customCommands;
|
|
439
|
+
const maxVisible = 10;
|
|
440
|
+
const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, commands.length - maxVisible)));
|
|
441
|
+
const visible = commands.slice(start, start + maxVisible);
|
|
442
|
+
const line = '─'.repeat(Math.max(12, Math.min(100, width)));
|
|
443
|
+
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 ? { color: BRAND, bold: true } : {}), 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
|
|
444
|
+
? 'Browse default commands'
|
|
445
|
+
: 'Browse shared commands and skills' }), visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No commands found." })) : (visible.map((command, visibleIndex) => {
|
|
446
|
+
const index = start + visibleIndex;
|
|
447
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { ...(index === selectedIndex
|
|
448
|
+
? { color: BRAND, bold: true }
|
|
449
|
+
: {}), children: [index === selectedIndex ? '↓ ' : ' ', "/", command.name] }), _jsxs(Text, { dimColor: true, children: [" ", command.description] })] }, command.name));
|
|
450
|
+
}))] })) }), _jsx(Text, { dimColor: true, children: "\u2190/\u2192 to switch \u00B7 \u2191/\u2193 to navigate \u00B7 Esc to cancel" })] }));
|
|
451
|
+
}
|
|
452
|
+
export function SelectionMenu({ title, description, options, selectedIndex, footer, width, screenReader, }) {
|
|
453
|
+
const maxVisible = 7;
|
|
454
|
+
const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, options.length - maxVisible)));
|
|
455
|
+
const visible = options.slice(start, start + maxVisible);
|
|
456
|
+
return (_jsxs(Box, { borderStyle: screenReader ? undefined : 'round', borderColor: "gray", 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, visible.map((option, visibleIndex) => {
|
|
457
|
+
const index = start + visibleIndex;
|
|
458
|
+
const selected = index === selectedIndex;
|
|
459
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { ...(selected ? { color: BRAND, bold: true } : {}), children: [selected ? '❯ ' : ' ', index + 1, ". ", option.label, option.selected ? ' ✔' : ''] }), option.description ? (_jsxs(Text, { dimColor: true, children: [" ", option.description] })) : null] }, `${index}-${option.label}`));
|
|
460
|
+
}), 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
|
|
461
|
+
? `↓ ${options.length - start - visible.length} more`
|
|
462
|
+
: ''] })) : null, _jsx(Text, { dimColor: true, children: footer })] }));
|
|
463
|
+
}
|
|
464
|
+
function ComposerInput({ input, cursor }) {
|
|
465
|
+
const { before, current, after } = composerEditorSegments({
|
|
466
|
+
text: input,
|
|
467
|
+
cursor,
|
|
468
|
+
});
|
|
469
|
+
const cursorText = current === '\n' ? '↵' : (current ?? ' ');
|
|
470
|
+
return (_jsxs(Text, { children: [before, _jsx(Text, { color: "black", backgroundColor: ACCENT, children: cursorText }), after] }));
|
|
471
|
+
}
|
|
472
|
+
export function ExternalEditorWait({ screenReader, }) {
|
|
473
|
+
if (screenReader)
|
|
474
|
+
return _jsx(Text, { children: "External editor open. Save and close it to continue." });
|
|
475
|
+
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" })] }));
|
|
129
476
|
}
|
|
130
|
-
export function Composer({ input, busy, status, display, usage, width, screenReader, }) {
|
|
477
|
+
export function Composer({ input, cursor, busy, clipboardBusy = false, status, display, usage, costUsd, width, screenReader, hasThinking = false, thinkingExpanded = false, shortcutsVisible = false, shellMode = false, footerMessage, }) {
|
|
131
478
|
const [spinnerIndex, setSpinnerIndex] = useState(0);
|
|
132
479
|
useEffect(() => {
|
|
133
480
|
if (!busy || screenReader)
|
|
@@ -136,11 +483,25 @@ export function Composer({ input, busy, status, display, usage, width, screenRea
|
|
|
136
483
|
return () => clearInterval(timer);
|
|
137
484
|
}, [busy, screenReader]);
|
|
138
485
|
if (screenReader)
|
|
139
|
-
return _jsx(Text, { children:
|
|
140
|
-
|
|
141
|
-
|
|
486
|
+
return (_jsx(Text, { children: clipboardBusy
|
|
487
|
+
? 'Pasting…'
|
|
488
|
+
: busy
|
|
489
|
+
? `Status: ${status}`
|
|
490
|
+
: shellMode
|
|
491
|
+
? `Shell command: ${input}`
|
|
492
|
+
: `Prompt: ${input}` }));
|
|
493
|
+
const line = '─'.repeat(Math.max(12, Math.min(100, width)));
|
|
494
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [usage ? (_jsxs(Text, { dimColor: true, children: ["Context \u00B7 ", usage.inputTokens + usage.outputTokens, " tokens", display.contextWindowTokens
|
|
495
|
+
? ` / ${display.contextWindowTokens} (${Math.min(100, Math.round(((usage.inputTokens + usage.outputTokens) /
|
|
496
|
+
display.contextWindowTokens) *
|
|
497
|
+
100))}%)`
|
|
498
|
+
: '', usage.cacheReadInputTokens
|
|
142
499
|
? ` · ${usage.cacheReadInputTokens} cached`
|
|
143
|
-
: ''] })) : null, _jsx(Text, { dimColor: true, children: line }), busy ? (_jsxs(Text, { children: [_jsx(Text, { color: ACCENT, children: SPINNER[spinnerIndex] }), " ", status, "\u2026", ' ', _jsx(Text, { dimColor: true, children: "\u00B7 esc to interrupt" })] })) : (_jsxs(Text, { children: [
|
|
500
|
+
: '', costUsd === undefined ? '' : ` · $${costUsd.toFixed(6)}`] })) : null, _jsx(Text, { dimColor: true, children: line }), clipboardBusy ? (_jsx(Text, { children: "Pasting\u2026" })) : busy ? (_jsxs(Text, { children: [_jsx(Text, { color: ACCENT, children: SPINNER[spinnerIndex] }), " ", status, "\u2026", ' ', _jsx(Text, { dimColor: true, children: "\u00B7 esc to interrupt" })] })) : (_jsxs(Text, { children: [_jsx(Text, { ...(shellMode ? {} : { color: BRAND }), bold: true, children: shellMode ? '! ' : '❯ ' }), input ? (_jsx(ComposerInput, { cursor: cursor ?? Array.from(input).length, input: input })) : (_jsx(Text, { dimColor: true, children: shellMode
|
|
501
|
+
? 'Enter a shell command'
|
|
502
|
+
: 'Try "review this project"' }))] })), _jsx(Text, { dimColor: true, children: line }), shortcutsVisible ? (_jsx(ShortcutHelp, { width: width })) : (_jsxs(Box, { width: Math.min(100, width), children: [_jsx(Text, { dimColor: true, children: shellMode ? ('! for shell mode') : (_jsxs(_Fragment, { children: ["\u23F5\u23F5 ", permissionLabel(display.permissionMode), " \u00B7", ' ', busy ? 'esc to interrupt' : '? for shortcuts', " \u00B7 \u2190 for agents", hasThinking
|
|
503
|
+
? ` · ctrl+o ${thinkingExpanded ? 'collapse' : 'expand'}`
|
|
504
|
+
: ''] })) }), _jsx(Box, { flexGrow: 1 }), footerMessage ? (footerMessage.isError ? (_jsx(Text, { color: "red", children: footerMessage.text })) : (_jsx(Text, { dimColor: true, children: footerMessage.text }))) : display.effort ? (_jsxs(Text, { children: [_jsxs(Text, { color: ACCENT, children: ["\u25CF ", display.effort] }), _jsx(Text, { dimColor: true, children: " \u00B7 /effort" })] })) : null] }))] }));
|
|
144
505
|
}
|
|
145
506
|
export function DialogFrame({ title, children, screenReader, }) {
|
|
146
507
|
return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round', borderColor: "yellow", paddingX: screenReader ? 0 : 1, marginTop: 1, children: [_jsx(Text, { color: "yellow", bold: true, children: title }), children] }));
|