praxis-agent 0.43.0 → 0.44.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 +48 -47
- package/dist/application/native-session-projection.d.ts +4 -0
- package/dist/application/native-session-projection.js +135 -0
- package/dist/application/session-service.d.ts +0 -1
- package/dist/application/session-service.js +9 -170
- package/dist/application/top-level-agent-manager.d.ts +7 -0
- package/dist/application/top-level-agent-manager.js +21 -3
- package/dist/cli/controls.js +4 -0
- package/dist/cli/interactive.js +112 -156
- package/dist/cli/protocol.d.ts +5 -0
- package/dist/cli/protocol.js +45 -0
- package/dist/cli/provider-auth-command.d.ts +32 -0
- package/dist/cli/provider-auth-command.js +249 -0
- package/dist/cli/tui/ansi-frame-renderer.d.ts +2 -0
- package/dist/cli/tui/ansi-frame-renderer.js +109 -21
- package/dist/cli/tui/ansi-surface.d.ts +6 -22
- package/dist/cli/tui/ansi-surface.js +6 -126
- package/dist/cli/tui/ansi-theme.d.ts +3 -2
- package/dist/cli/tui/ansi-theme.js +22 -1
- package/dist/cli/tui/quiet-choice-rows.d.ts +26 -0
- package/dist/cli/tui/quiet-choice-rows.js +302 -0
- package/dist/cli/tui/quiet-frame-adapter.d.ts +18 -0
- package/dist/cli/tui/quiet-frame-adapter.js +90 -0
- package/dist/cli/tui/quiet-frame.d.ts +43 -0
- package/dist/cli/tui/quiet-frame.js +346 -0
- package/dist/cli/tui/quiet-operational-rows.d.ts +17 -0
- package/dist/cli/tui/quiet-operational-rows.js +413 -0
- package/dist/cli/tui/quiet-screen-projector.d.ts +25 -0
- package/dist/cli/tui/quiet-screen-projector.js +110 -0
- package/dist/cli/tui/quiet-settings-rows.d.ts +17 -0
- package/dist/cli/tui/quiet-settings-rows.js +490 -0
- package/dist/cli/tui/status-line.d.ts +15 -9
- package/dist/cli/tui/status-line.js +15 -6
- package/dist/cli/tui/theme.js +20 -20
- package/dist/cli/tui/transcript-viewport.d.ts +5 -0
- package/dist/cli/tui/transcript-viewport.js +213 -78
- package/dist/cli/tui/tui-row-ir.js +4 -4
- package/dist/cli/workspace-trust-prompt.js +2 -2
- package/dist/cli-runtime.d.ts +34 -1
- package/dist/cli-runtime.js +587 -184
- package/dist/maintenance/doctor.js +100 -22
- package/dist/persistence/in-memory-transcript-store.js +1 -7
- package/dist/persistence/native-transcript-store.d.ts +0 -9
- package/dist/persistence/native-transcript-store.js +4 -38
- package/dist/persistence/provider-credential-vault.d.ts +72 -0
- package/dist/persistence/provider-credential-vault.js +498 -0
- package/dist/providers/codex-oauth.d.ts +104 -0
- package/dist/providers/codex-oauth.js +770 -0
- package/dist/providers/codex-subscription.d.ts +33 -0
- package/dist/providers/codex-subscription.js +849 -0
- package/dist/providers/provider-auth.d.ts +71 -0
- package/dist/providers/provider-auth.js +149 -0
- package/dist/providers/provider-registry.d.ts +52 -0
- package/dist/providers/provider-registry.js +187 -0
- package/dist/providers/provider-settings.d.ts +38 -0
- package/dist/providers/provider-settings.js +334 -0
- package/dist/security/workspace-trust.d.ts +4 -3
- package/dist/security/workspace-trust.js +35 -1
- package/package.json +8 -4
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type QuietFrameDensity, type QuietFrameRow } from './quiet-frame.js';
|
|
2
|
+
import type { TuiCommandPaletteModel } from './command-palette-model.js';
|
|
3
|
+
import type { TuiDecisionSurfaceModel } from './decision-surface-model.js';
|
|
4
|
+
import type { TuiElicitationSurfaceModel } from './mcp-elicitation-surface-model.js';
|
|
5
|
+
import type { TuiMentionPickerModel } from './mention-picker-model.js';
|
|
6
|
+
import type { TuiPermissionSurfaceModel } from './permission-surface-model.js';
|
|
7
|
+
import type { TuiSessionPickerModel } from './session-picker-model.js';
|
|
8
|
+
export type QuietPrioritySurface = {
|
|
9
|
+
readonly kind: 'editor-wait';
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: 'permission';
|
|
12
|
+
readonly surface: TuiPermissionSurfaceModel;
|
|
13
|
+
} | TuiDecisionSurfaceModel | {
|
|
14
|
+
readonly kind: 'elicitation';
|
|
15
|
+
readonly surface: TuiElicitationSurfaceModel;
|
|
16
|
+
};
|
|
17
|
+
export type QuietOverlaySurface = TuiCommandPaletteModel | TuiMentionPickerModel | {
|
|
18
|
+
readonly kind: 'exit-confirmation';
|
|
19
|
+
};
|
|
20
|
+
export type QuietChoiceSurface = QuietPrioritySurface | QuietOverlaySurface | TuiSessionPickerModel | TuiPermissionSurfaceModel;
|
|
21
|
+
export interface QuietChoiceProjectionOptions {
|
|
22
|
+
readonly density: QuietFrameDensity;
|
|
23
|
+
readonly screenReader?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function projectQuietChoiceRows(surface: QuietChoiceSurface, options: QuietChoiceProjectionOptions): readonly QuietFrameRow[];
|
|
26
|
+
//# sourceMappingURL=quiet-choice-rows.d.ts.map
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { createQuietFrameRow, } from './quiet-frame.js';
|
|
2
|
+
const excerpt = (value, max = 160) => typeof value === 'string'
|
|
3
|
+
? value.replace(/\s+/gu, ' ').trim().slice(0, max)
|
|
4
|
+
: '';
|
|
5
|
+
const detailAllowed = (density) => density === 'full' || density === 'standard';
|
|
6
|
+
const row = (key, text, role = 'body', sr) => createQuietFrameRow(key, text, 'focus', role, sr);
|
|
7
|
+
const footer = (screenReader, text = '↑/↓ select Enter confirm Esc cancel') => row('quiet:choice:footer', text, 'muted', screenReader
|
|
8
|
+
? 'Use up and down arrows to select. Press Enter to confirm. Press Escape to cancel.'
|
|
9
|
+
: undefined);
|
|
10
|
+
const explicitFooter = (key, visual, accessible) => row(key, visual, 'muted', accessible);
|
|
11
|
+
const marker = (selected, label, screenReader) => screenReader
|
|
12
|
+
? selected
|
|
13
|
+
? `Selected: ${label}`
|
|
14
|
+
: `Option: ${label}`
|
|
15
|
+
: `${selected ? '❯ ' : ' '}${label}`;
|
|
16
|
+
const excerptOption = (label, description, density) => detailAllowed(density) && description
|
|
17
|
+
? `${label} — ${excerpt(description)}`
|
|
18
|
+
: label;
|
|
19
|
+
const unsupported = (value) => {
|
|
20
|
+
throw new Error(`Unsupported quiet choice surface: ${String(value)}`);
|
|
21
|
+
};
|
|
22
|
+
function optionsRows(options, prefix, density, sr) {
|
|
23
|
+
return options.map((item, index) => row(`${prefix}:${item.id ?? item.index ?? index}`, marker(item.selected, excerptOption(item.label, item.description, density), sr), item.selected ? 'selection' : 'body', sr
|
|
24
|
+
? item.selected
|
|
25
|
+
? `Selected: ${item.label}`
|
|
26
|
+
: `Option: ${item.label}`
|
|
27
|
+
: undefined));
|
|
28
|
+
}
|
|
29
|
+
function permission(surface, density, sr) {
|
|
30
|
+
const out = [];
|
|
31
|
+
switch (surface.kind) {
|
|
32
|
+
case 'tool-request': {
|
|
33
|
+
out.push(row('quiet:permission:heading', excerpt(surface.tool.question || surface.heading), 'heading'));
|
|
34
|
+
if (detailAllowed(density)) {
|
|
35
|
+
for (const [key, value] of [
|
|
36
|
+
['subtitle', surface.tool.subtitle],
|
|
37
|
+
['explanation', surface.tool.explanation],
|
|
38
|
+
])
|
|
39
|
+
if (value)
|
|
40
|
+
out.push(row(`quiet:permission:${key}`, excerpt(value), 'muted'));
|
|
41
|
+
surface.tool.detail
|
|
42
|
+
.slice(0, 20)
|
|
43
|
+
.forEach((item, i) => out.push(row(`quiet:permission:detail:${i}`, `${item.prefix ?? ' '} ${excerpt(item.text)}`, item.prefix === '-'
|
|
44
|
+
? 'diffRemoved'
|
|
45
|
+
: item.prefix === '+'
|
|
46
|
+
? 'diffAdded'
|
|
47
|
+
: 'body')));
|
|
48
|
+
}
|
|
49
|
+
out.push(...optionsRows(surface.options.map((item, index) => ({
|
|
50
|
+
...item,
|
|
51
|
+
label: permissionLabel({
|
|
52
|
+
...item,
|
|
53
|
+
index: index + 1,
|
|
54
|
+
selected: index === surface.selectedIndex,
|
|
55
|
+
}),
|
|
56
|
+
selected: index === surface.selectedIndex,
|
|
57
|
+
})), 'quiet:permission:option', density, sr));
|
|
58
|
+
if (surface.feedbackMode)
|
|
59
|
+
out.push(row('quiet:permission:feedback', `> ${excerpt(surface.feedback) || 'Enter feedback…'}`, 'input'));
|
|
60
|
+
return [...out, footer(sr)];
|
|
61
|
+
}
|
|
62
|
+
case 'recovery-request':
|
|
63
|
+
out.push(row('quiet:recovery:heading', surface.heading, 'heading'), row('quiet:recovery:question', surface.question), row('quiet:recovery:display', excerpt(surface.display), 'muted'));
|
|
64
|
+
if (surface.feedbackMode)
|
|
65
|
+
out.push(row('quiet:recovery:feedback', `> ${excerpt(surface.feedback) || surface.feedbackPlaceholder}`, 'input'));
|
|
66
|
+
return [
|
|
67
|
+
...out,
|
|
68
|
+
...optionsRows(surface.options, 'quiet:recovery:option', density, sr),
|
|
69
|
+
footer(sr),
|
|
70
|
+
];
|
|
71
|
+
case 'permission-dashboard':
|
|
72
|
+
out.push(row('quiet:dashboard:heading', surface.heading, 'heading'), row('quiet:dashboard:tab', `Tab: ${surface.tabs.find((x) => x.current)?.label ?? 'Permissions'}`));
|
|
73
|
+
if (detailAllowed(density))
|
|
74
|
+
out.push(row('quiet:dashboard:description', excerpt(surface.description)));
|
|
75
|
+
if (surface.query)
|
|
76
|
+
out.push(row('quiet:dashboard:search', `Search: ${excerpt(surface.query)}`, 'input'));
|
|
77
|
+
if (surface.originalWorkspace)
|
|
78
|
+
out.push(row('quiet:dashboard:workspace:original', `Original workspace: ${surface.originalWorkspace.label}`, 'muted'));
|
|
79
|
+
out.push(...surface.rows
|
|
80
|
+
.slice(0, 20)
|
|
81
|
+
.map((item, i) => row(`quiet:dashboard:${item.id ?? item.index ?? i}`, marker(item.selected, `${item.label}${item.status === 'retrying' ? ' (retrying)' : item.status === 'denied' ? ' (denied)' : ''}`, sr), item.selected
|
|
82
|
+
? 'selection'
|
|
83
|
+
: item.status === 'denied'
|
|
84
|
+
? 'error'
|
|
85
|
+
: item.status === 'retrying'
|
|
86
|
+
? 'warning'
|
|
87
|
+
: 'body')));
|
|
88
|
+
if (surface.rows.length === 0 && surface.emptyState)
|
|
89
|
+
out.push(row('quiet:dashboard:empty', surface.emptyState, 'muted'));
|
|
90
|
+
return [
|
|
91
|
+
...out,
|
|
92
|
+
explicitFooter('quiet:dashboard:footer', surface.rows.length > 0
|
|
93
|
+
? '↑/↓ select Enter open Esc close'
|
|
94
|
+
: 'Esc close', surface.rows.length > 0
|
|
95
|
+
? 'Use left and right arrows to switch tabs. Use up and down arrows to select. Press Enter to open. Press Escape to close.'
|
|
96
|
+
: 'Use left and right arrows to switch tabs. Press Escape to close.'),
|
|
97
|
+
];
|
|
98
|
+
case 'permission-rule-input':
|
|
99
|
+
case 'workspace-directory-input':
|
|
100
|
+
out.push(row(`quiet:${surface.kind}:heading`, surface.heading, 'heading'), row(`quiet:${surface.kind}:description`, excerpt(surface.description)), row(`quiet:${surface.kind}:value`, `> ${excerpt(surface.value) || surface.placeholder}`, 'input'));
|
|
101
|
+
return [
|
|
102
|
+
...out,
|
|
103
|
+
explicitFooter(`quiet:${surface.kind}:footer`, 'Enter confirm Esc cancel', 'Press Enter to confirm. Press Escape to cancel.'),
|
|
104
|
+
];
|
|
105
|
+
case 'permission-scope':
|
|
106
|
+
out.push(row('quiet:scope:heading', surface.heading, 'heading'), row('quiet:scope:description', excerpt(surface.description)));
|
|
107
|
+
return [
|
|
108
|
+
...out,
|
|
109
|
+
...optionsRows(surface.options, 'quiet:scope:option', density, sr),
|
|
110
|
+
footer(sr),
|
|
111
|
+
];
|
|
112
|
+
case 'permission-delete':
|
|
113
|
+
out.push(row('quiet:delete:heading', surface.heading, 'heading'), row('quiet:delete:rule', `${surface.rule} · ${surface.scope}`), row('quiet:delete:question', surface.question, 'warning'));
|
|
114
|
+
if (surface.description)
|
|
115
|
+
out.push(row('quiet:delete:description', excerpt(surface.description), 'warning'));
|
|
116
|
+
return [
|
|
117
|
+
...out,
|
|
118
|
+
...optionsRows(surface.options, 'quiet:delete:option', density, sr).map((x, i) => i === 0
|
|
119
|
+
? {
|
|
120
|
+
...x,
|
|
121
|
+
segments: x.segments.map((s) => ({
|
|
122
|
+
...s,
|
|
123
|
+
role: 'error',
|
|
124
|
+
})),
|
|
125
|
+
}
|
|
126
|
+
: x),
|
|
127
|
+
footer(sr),
|
|
128
|
+
];
|
|
129
|
+
case 'workspace-directory-delete':
|
|
130
|
+
out.push(row('quiet:workspace-delete:heading', surface.heading, 'heading'), row('quiet:workspace-delete:path', surface.path), row('quiet:workspace-delete:description', excerpt(surface.description), 'warning'));
|
|
131
|
+
return [
|
|
132
|
+
...out,
|
|
133
|
+
...optionsRows(surface.options, 'quiet:workspace-delete:option', density, sr),
|
|
134
|
+
footer(sr),
|
|
135
|
+
];
|
|
136
|
+
default:
|
|
137
|
+
return unsupported(surface);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function permissionLabel(item) {
|
|
141
|
+
if (item.action === 'allow-once')
|
|
142
|
+
return 'Allow once';
|
|
143
|
+
if (item.action === 'deny')
|
|
144
|
+
return 'Deny';
|
|
145
|
+
const label = item.label.trim();
|
|
146
|
+
if (/^yes,\s*/iu.test(label)) {
|
|
147
|
+
const consequence = label.replace(/^yes,\s*/iu, '');
|
|
148
|
+
return /^allow\b/iu.test(consequence)
|
|
149
|
+
? `Allow${consequence.slice('allow'.length)}`
|
|
150
|
+
: `Allow ${consequence}`;
|
|
151
|
+
}
|
|
152
|
+
if (/^no,\s*/iu.test(label))
|
|
153
|
+
return `Deny ${label.replace(/^no,\s*/iu, '')}`;
|
|
154
|
+
if (/^yes$/iu.test(label))
|
|
155
|
+
return 'Allow for this session';
|
|
156
|
+
if (/^no$/iu.test(label))
|
|
157
|
+
return 'Deny';
|
|
158
|
+
return label;
|
|
159
|
+
}
|
|
160
|
+
function decision(surface, density, sr) {
|
|
161
|
+
const out = [
|
|
162
|
+
row(`quiet:decision:${surface.kind}:heading`, surface.heading, 'heading'),
|
|
163
|
+
];
|
|
164
|
+
if (surface.kind === 'plan-approval') {
|
|
165
|
+
if (detailAllowed(density)) {
|
|
166
|
+
out.push(row('quiet:plan:explanation', surface.explanation));
|
|
167
|
+
if (surface.plan)
|
|
168
|
+
out.push(row('quiet:plan:content', excerpt(surface.plan, 160), 'muted'));
|
|
169
|
+
out.push(row('quiet:plan:path', `Plan: ${excerpt(surface.planPath)}`, 'muted'));
|
|
170
|
+
}
|
|
171
|
+
out.push(...optionsRows(surface.options, 'quiet:plan:option', density, sr));
|
|
172
|
+
if (surface.feedbackMode)
|
|
173
|
+
out.push(row('quiet:plan:feedback', `> ${excerpt(surface.feedback) || surface.feedbackPlaceholder}`, 'input'));
|
|
174
|
+
return [
|
|
175
|
+
...out,
|
|
176
|
+
explicitFooter('quiet:plan:footer', '↑/↓ select Enter confirm Esc cancel', 'Use up and down arrows to select. Press Enter to confirm. Press Escape to cancel.'),
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
if (surface.progress)
|
|
180
|
+
out.push(row('quiet:question:progress', surface.progress, 'muted'));
|
|
181
|
+
if (surface.question)
|
|
182
|
+
out.push(row('quiet:question:text', surface.question));
|
|
183
|
+
out.push(...optionsRows(surface.options, 'quiet:question:option', density, sr));
|
|
184
|
+
out.push(row('quiet:question:answer', `Current answer: ${excerpt(surface.answer) || '(empty)'}`, 'input'));
|
|
185
|
+
if (surface.guidance)
|
|
186
|
+
out.push(row('quiet:question:guidance', surface.guidance, 'muted'));
|
|
187
|
+
if (surface.emptyState)
|
|
188
|
+
out.push(row('quiet:question:empty', surface.emptyState, 'muted'));
|
|
189
|
+
out.push(explicitFooter('quiet:question:footer', 'Enter answer Esc cancel', 'Press Enter to submit the answer. Press Escape to cancel.'));
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
function picker(surface, density, sr) {
|
|
193
|
+
const name = surface.kind === 'session-picker'
|
|
194
|
+
? 'Sessions'
|
|
195
|
+
: surface.kind === 'command-palette'
|
|
196
|
+
? 'Commands'
|
|
197
|
+
: 'Mentions';
|
|
198
|
+
const out = [
|
|
199
|
+
row(`quiet:${surface.kind}:heading`, `${name}${surface.query ? ` · ${excerpt(surface.query)}` : ''}`, 'heading'),
|
|
200
|
+
];
|
|
201
|
+
const { start, end } = surface.visibleRange;
|
|
202
|
+
surface.rows.slice(start, end).forEach((item) => {
|
|
203
|
+
const label = 'path' in item
|
|
204
|
+
? item.path
|
|
205
|
+
: 'invocation' in item
|
|
206
|
+
? detailAllowed(density)
|
|
207
|
+
? `${item.invocation} — ${item.description}`
|
|
208
|
+
: item.invocation
|
|
209
|
+
: 'name' in item
|
|
210
|
+
? detailAllowed(density)
|
|
211
|
+
? `${item.name} — ${item.description}`
|
|
212
|
+
: item.name
|
|
213
|
+
: detailAllowed(density) && item.detail
|
|
214
|
+
? `${item.label} — ${item.detail}`
|
|
215
|
+
: item.label;
|
|
216
|
+
out.push(row(`quiet:${surface.kind}:${item.id}`, marker(item.selected, excerptOption(label, undefined, density), sr), item.selected ? 'selection' : 'body'));
|
|
217
|
+
});
|
|
218
|
+
if (surface.rows.length === 0)
|
|
219
|
+
out.push(row(`quiet:${surface.kind}:empty`, 'No matching choices.', 'muted'));
|
|
220
|
+
return [
|
|
221
|
+
...out,
|
|
222
|
+
explicitFooter(`quiet:${surface.kind}:footer`, '↑/↓ select Enter confirm Esc cancel', 'Use up and down arrows to select. Press Enter to confirm. Press Escape to cancel.'),
|
|
223
|
+
];
|
|
224
|
+
}
|
|
225
|
+
function elicitation(surface, density, sr) {
|
|
226
|
+
const out = [
|
|
227
|
+
row(`quiet:elicitation:${surface.kind}:server`, `Server: ${excerpt(surface.serverName)}`, 'heading'),
|
|
228
|
+
row(`quiet:elicitation:${surface.kind}:message`, excerpt(surface.message)),
|
|
229
|
+
];
|
|
230
|
+
if (surface.kind === 'elicitation-url') {
|
|
231
|
+
out.push(row('quiet:elicitation:url', `URL: ${excerpt(surface.url)}`), row('quiet:elicitation:waiting', surface.waiting
|
|
232
|
+
? 'Waiting for confirmation…'
|
|
233
|
+
: marker(surface.selection === 0, surface.actionLabel, sr), surface.waiting
|
|
234
|
+
? 'warning'
|
|
235
|
+
: surface.selection === 0
|
|
236
|
+
? 'selection'
|
|
237
|
+
: 'body'), row('quiet:elicitation:cancel', marker(surface.selection === 1, 'Cancel', sr), surface.selection === 1 ? 'selection' : 'body'));
|
|
238
|
+
return [...out, footer(sr)];
|
|
239
|
+
}
|
|
240
|
+
const start = Math.max(0, Math.min(surface.state.focusIndex - 2, surface.state.fields.length - Math.max(1, surface.maxVisibleFields)));
|
|
241
|
+
surface.state.fields
|
|
242
|
+
.slice(start, start + Math.max(1, surface.maxVisibleFields))
|
|
243
|
+
.forEach((field) => {
|
|
244
|
+
const stored = surface.state.values[field.name];
|
|
245
|
+
const value = surface.state.fields.indexOf(field) === surface.state.focusIndex &&
|
|
246
|
+
['text', 'number', 'integer'].includes(field.kind) &&
|
|
247
|
+
surface.state.expandedField !== field.name
|
|
248
|
+
? surface.input
|
|
249
|
+
: stored;
|
|
250
|
+
const text = `${field.title}${field.required ? ' (required)' : ''}: ${value === undefined || value === '' ? '(empty)' : String(value)}${detailAllowed(density) && field.description ? ` — ${excerpt(field.description)}` : ''}${surface.state.errors[field.name] ? ` — ${excerpt(surface.state.errors[field.name])}` : ''}`;
|
|
251
|
+
out.push(row(`quiet:elicitation:field:${field.name}`, marker(surface.state.fields.indexOf(field) === surface.state.focusIndex, text, sr), surface.state.fields.indexOf(field) === surface.state.focusIndex
|
|
252
|
+
? 'selection'
|
|
253
|
+
: surface.state.errors[field.name]
|
|
254
|
+
? 'error'
|
|
255
|
+
: 'body'));
|
|
256
|
+
if (surface.state.expandedField === field.name) {
|
|
257
|
+
const optionStart = Math.max(0, Math.min(surface.state.optionIndex - 10, field.options.length - 20));
|
|
258
|
+
field.options
|
|
259
|
+
.slice(optionStart, optionStart + 20)
|
|
260
|
+
.forEach((option, offset) => {
|
|
261
|
+
const optionIndex = optionStart + offset;
|
|
262
|
+
const focused = surface.state.optionIndex === optionIndex;
|
|
263
|
+
const checked = field.kind === 'multi-enum' &&
|
|
264
|
+
Array.isArray(stored) &&
|
|
265
|
+
stored.includes(option.value);
|
|
266
|
+
const label = field.kind === 'multi-enum'
|
|
267
|
+
? `${checked ? '[x]' : '[ ]'} ${option.label}`
|
|
268
|
+
: option.label;
|
|
269
|
+
out.push(row(`quiet:elicitation:field:${field.name}:option:${option.value}`, marker(focused, label, sr), focused ? 'selection' : checked ? 'success' : 'body'));
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return [
|
|
274
|
+
...out,
|
|
275
|
+
explicitFooter('quiet:elicitation:footer', '↑/↓ field Enter edit/select Esc cancel', 'Use up and down arrows to change fields. Press Enter to edit or select. Press Escape to cancel.'),
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
export function projectQuietChoiceRows(surface, options) {
|
|
279
|
+
const sr = options.screenReader === true;
|
|
280
|
+
if (surface.kind === 'permission')
|
|
281
|
+
return permission(surface.surface, options.density, sr);
|
|
282
|
+
if (surface.kind === 'elicitation')
|
|
283
|
+
return elicitation(surface.surface, options.density, sr);
|
|
284
|
+
if (surface.kind === 'editor-wait')
|
|
285
|
+
return [
|
|
286
|
+
row('quiet:editor-wait', 'Editor is open. Waiting for changes…', 'warning'),
|
|
287
|
+
explicitFooter('quiet:editor-wait:footer', 'Esc cancel', 'Press Escape to cancel.'),
|
|
288
|
+
];
|
|
289
|
+
if (surface.kind === 'exit-confirmation')
|
|
290
|
+
return [
|
|
291
|
+
row('quiet:exit:heading', 'Exit Praxis?', 'heading'),
|
|
292
|
+
explicitFooter('quiet:exit:footer', 'Enter confirm Esc cancel', 'Press Enter to confirm. Press Escape to cancel.'),
|
|
293
|
+
];
|
|
294
|
+
if (surface.kind === 'plan-approval' || surface.kind === 'question')
|
|
295
|
+
return decision(surface, options.density, sr);
|
|
296
|
+
if (surface.kind === 'session-picker' ||
|
|
297
|
+
surface.kind === 'command-palette' ||
|
|
298
|
+
surface.kind === 'mention-picker')
|
|
299
|
+
return picker(surface, options.density, sr);
|
|
300
|
+
return permission(surface, options.density, sr);
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=quiet-choice-rows.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { QuietFrame } from './quiet-frame.js';
|
|
2
|
+
import { type TuiTextRole } from './theme.js';
|
|
3
|
+
export interface QuietInkLineSegment {
|
|
4
|
+
readonly text: string;
|
|
5
|
+
readonly role: TuiTextRole;
|
|
6
|
+
readonly cursor?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface QuietInkFrameLine {
|
|
9
|
+
readonly key: string;
|
|
10
|
+
readonly segments: readonly QuietInkLineSegment[];
|
|
11
|
+
}
|
|
12
|
+
export declare function projectQuietInkFrameLines(frame: QuietFrame, screenReader?: boolean): readonly QuietInkFrameLine[];
|
|
13
|
+
export interface QuietInkFrameProps {
|
|
14
|
+
readonly frame: QuietFrame;
|
|
15
|
+
readonly screenReader?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function QuietInkFrame({ frame, screenReader, }: QuietInkFrameProps): import("react").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=quiet-frame-adapter.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { useTuiTheme } from './theme.js';
|
|
4
|
+
import { terminalGraphemeWidth, terminalGraphemes, terminalTextWidth, } from './transcript-viewport.js';
|
|
5
|
+
const ROLE_MAP = {
|
|
6
|
+
body: 'body',
|
|
7
|
+
heading: 'heading',
|
|
8
|
+
muted: 'muted',
|
|
9
|
+
accent: 'focusMarker',
|
|
10
|
+
success: 'success',
|
|
11
|
+
warning: 'warning',
|
|
12
|
+
error: 'error',
|
|
13
|
+
tool: 'info',
|
|
14
|
+
selection: 'focusMarker',
|
|
15
|
+
input: 'inputMarker',
|
|
16
|
+
diffAdded: 'diffAdded',
|
|
17
|
+
diffRemoved: 'diffRemoved',
|
|
18
|
+
};
|
|
19
|
+
function plainText(row) {
|
|
20
|
+
return row.segments.map((segment) => segment.text).join('') || ' ';
|
|
21
|
+
}
|
|
22
|
+
export function projectQuietInkFrameLines(frame, screenReader = false) {
|
|
23
|
+
return frame.lines.map((row) => {
|
|
24
|
+
if (screenReader)
|
|
25
|
+
return {
|
|
26
|
+
key: row.key,
|
|
27
|
+
segments: [
|
|
28
|
+
{
|
|
29
|
+
text: row.accessibleText ?? plainText(row),
|
|
30
|
+
role: 'body',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
const cursor = frame.cursor?.rowKey === row.key ? frame.cursor.column : undefined;
|
|
35
|
+
if (cursor === undefined)
|
|
36
|
+
return {
|
|
37
|
+
key: row.key,
|
|
38
|
+
segments: row.segments.map((segment) => ({
|
|
39
|
+
...segment,
|
|
40
|
+
role: ROLE_MAP[segment.role],
|
|
41
|
+
})),
|
|
42
|
+
};
|
|
43
|
+
const output = [];
|
|
44
|
+
let position = 0;
|
|
45
|
+
let inserted = false;
|
|
46
|
+
for (const segment of row.segments) {
|
|
47
|
+
const chars = terminalGraphemes(segment.text);
|
|
48
|
+
const mappedRole = ROLE_MAP[segment.role];
|
|
49
|
+
const segmentWidth = terminalTextWidth(segment.text);
|
|
50
|
+
if (!inserted && cursor >= position && cursor < position + segmentWidth) {
|
|
51
|
+
let offset = 0;
|
|
52
|
+
let cell = position;
|
|
53
|
+
for (const [index, grapheme] of chars.entries()) {
|
|
54
|
+
const width = terminalGraphemeWidth(grapheme);
|
|
55
|
+
if (cursor < cell + width) {
|
|
56
|
+
offset = index;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
cell += width;
|
|
60
|
+
}
|
|
61
|
+
const before = chars.slice(0, offset).join('');
|
|
62
|
+
const at = chars[offset] ?? ' ';
|
|
63
|
+
const after = chars
|
|
64
|
+
.slice(offset + (offset < chars.length ? 1 : 0))
|
|
65
|
+
.join('');
|
|
66
|
+
if (before)
|
|
67
|
+
output.push({ text: before, role: mappedRole });
|
|
68
|
+
output.push({ text: at, role: mappedRole, cursor: true });
|
|
69
|
+
if (after)
|
|
70
|
+
output.push({ text: after, role: mappedRole });
|
|
71
|
+
inserted = true;
|
|
72
|
+
}
|
|
73
|
+
else
|
|
74
|
+
output.push({ text: segment.text, role: mappedRole });
|
|
75
|
+
position += segmentWidth;
|
|
76
|
+
}
|
|
77
|
+
if (!inserted)
|
|
78
|
+
output.push({ text: ' ', role: 'body', cursor: true });
|
|
79
|
+
return { key: row.key, segments: output };
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
export function QuietInkFrame({ frame, screenReader = false, }) {
|
|
83
|
+
const theme = useTuiTheme();
|
|
84
|
+
const lines = projectQuietInkFrameLines(frame, screenReader);
|
|
85
|
+
return (_jsx(_Fragment, { children: lines.map((line) => (_jsx(Text, { children: line.segments.map((segment, index) => {
|
|
86
|
+
const style = theme.text[segment.role];
|
|
87
|
+
return (_jsx(Text, { ...style, ...(segment.cursor || style.inverse ? { inverse: true } : {}), children: segment.text }, `${line.key}:${index}`));
|
|
88
|
+
}) }, line.key))) }));
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=quiet-frame-adapter.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { TuiScreenModel } from './tui-screen-model.js';
|
|
2
|
+
import type { TuiRow, TuiTextRole } from './tui-row-ir.js';
|
|
3
|
+
export type QuietFrameDensity = 'full' | 'standard' | 'compact' | 'narrow' | 'minimal';
|
|
4
|
+
export type QuietFrameRegion = 'identity' | 'transcript' | 'active' | 'focus' | 'composer' | 'status';
|
|
5
|
+
export interface QuietFrameRow extends TuiRow {
|
|
6
|
+
readonly region: QuietFrameRegion;
|
|
7
|
+
readonly accessibleText?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface QuietFrameCursor {
|
|
10
|
+
readonly rowKey: string;
|
|
11
|
+
readonly column: number;
|
|
12
|
+
}
|
|
13
|
+
export interface QuietFrame {
|
|
14
|
+
readonly columns: number;
|
|
15
|
+
readonly rows: number;
|
|
16
|
+
readonly density: QuietFrameDensity;
|
|
17
|
+
readonly lines: readonly QuietFrameRow[];
|
|
18
|
+
readonly cursor?: QuietFrameCursor;
|
|
19
|
+
}
|
|
20
|
+
export interface QuietFrameInput {
|
|
21
|
+
readonly screen: TuiScreenModel;
|
|
22
|
+
readonly width: number;
|
|
23
|
+
readonly rows: number | undefined;
|
|
24
|
+
readonly composerText: string;
|
|
25
|
+
readonly composerCursor: number;
|
|
26
|
+
readonly shellMode: boolean;
|
|
27
|
+
readonly busy: boolean;
|
|
28
|
+
readonly status: string;
|
|
29
|
+
readonly display?: {
|
|
30
|
+
readonly cwd?: string;
|
|
31
|
+
readonly model?: string;
|
|
32
|
+
readonly effort?: string;
|
|
33
|
+
readonly permissionMode?: string;
|
|
34
|
+
readonly version?: string;
|
|
35
|
+
};
|
|
36
|
+
readonly focusRows: readonly (TuiRow & {
|
|
37
|
+
readonly accessibleText?: string;
|
|
38
|
+
})[];
|
|
39
|
+
}
|
|
40
|
+
export declare function resolveQuietFrameDensity(width: number): QuietFrameDensity;
|
|
41
|
+
export declare function createQuietFrameRow(key: string, text: string, region: QuietFrameRegion, role?: TuiTextRole, accessibleText?: string): QuietFrameRow;
|
|
42
|
+
export declare function projectQuietFrame(input: QuietFrameInput): QuietFrame;
|
|
43
|
+
//# sourceMappingURL=quiet-frame.d.ts.map
|