@timurproko/a1 0.1.8-dev.332 → 0.1.8-dev.368
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 +40 -11
- package/dist/composition/owned-ui.d.ts +2 -0
- package/dist/composition/owned-ui.js +20 -3
- package/dist/contracts/owned-ui/index.d.ts +2 -0
- package/dist/contracts/owned-ui/index.js +2 -0
- package/dist/contracts/owned-ui/model.d.ts +38 -3
- package/dist/contracts/owned-ui/prompt-history.d.ts +18 -0
- package/dist/contracts/owned-ui/prompt-suggestions.d.ts +1 -1
- package/dist/contracts/owned-ui/prompt-suggestions.js +4 -1
- package/dist/contracts/owned-ui/suggestion-diagnostics.d.ts +22 -0
- package/dist/contracts/owned-ui/suggestion-diagnostics.js +9 -0
- package/dist/contracts/owned-ui/transcript-lifecycle.d.ts +5 -0
- package/dist/contracts/owned-ui/transcript-lifecycle.js +33 -0
- package/dist/contracts/owned-ui/validation.js +45 -2
- package/dist/contracts/presentation/index.d.ts +8 -0
- package/dist/features/owned-ui/settings-app.js +1 -1
- package/dist/features/prompt-history/image-sidecar.d.ts +32 -0
- package/dist/features/prompt-history/image-sidecar.js +130 -0
- package/dist/features/prompt-history/index.d.ts +2 -0
- package/dist/features/prompt-history/index.js +1 -0
- package/dist/features/prompt-history/paths.d.ts +1 -0
- package/dist/features/prompt-history/paths.js +2 -1
- package/dist/features/prompt-history/store.d.ts +1 -1
- package/dist/features/prompt-history/store.js +46 -2
- package/dist/features/prompt-history/worker.js +1 -1
- package/dist/features/prompt-suggestions/diagnostics.d.ts +18 -0
- package/dist/features/prompt-suggestions/diagnostics.js +82 -0
- package/dist/features/prompt-suggestions/index.d.ts +1 -0
- package/dist/features/prompt-suggestions/index.js +1 -0
- package/dist/foundation/release/bootstrap.d.ts +2 -0
- package/dist/foundation/release/bootstrap.js +54 -18
- package/dist/foundation/release/index.d.ts +1 -0
- package/dist/foundation/release/index.js +1 -0
- package/dist/foundation/release/update-launch.d.ts +6 -0
- package/dist/foundation/release/update-launch.js +17 -0
- package/dist/foundation/release/update.d.ts +2 -0
- package/dist/foundation/release/update.js +20 -21
- package/dist/foundation/supervision/main.js +5 -2
- package/dist/foundation/supervision/server.js +40 -13
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +9 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +11 -8
- package/dist/integrations/pi/components/prompt-input-port.d.ts +18 -0
- package/dist/integrations/pi/components/prompt-input-port.js +1 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +66 -11
- package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
- package/dist/integrations/pi/components/shell-footer-status.js +9 -5
- package/dist/integrations/pi/components/shell-presenters-info.js +7 -2
- package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +6 -2
- package/dist/integrations/pi/components/shell-presenters-transcript.js +81 -49
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +17 -4
- package/dist/integrations/pi/components/shell-shared-facade.js +2 -1
- package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +3 -1
- package/dist/integrations/pi/components/submitted-prompt-adapter.js +20 -4
- package/dist/integrations/pi/components/tool-result-adapter.d.ts +6 -0
- package/dist/integrations/pi/components/tool-result-adapter.js +27 -0
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -0
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +5 -0
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +41 -33
- package/dist/integrations/pi/components/upstream/components/session-footer.d.ts +2 -1
- package/dist/integrations/pi/components/upstream/components/session-footer.js +12 -7
- package/dist/integrations/pi/components/upstream/history/editor-core.js +1 -1
- package/dist/integrations/pi/engine/adapter.d.ts +2 -1
- package/dist/integrations/pi/engine/adapter.js +181 -84
- package/dist/integrations/pi/engine/tool-rendering.d.ts +13 -0
- package/dist/integrations/pi/engine/tool-rendering.js +122 -0
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +30 -0
- package/dist/integrations/pi/session-ui/prompt-chips.js +128 -1
- package/dist/integrations/pi/session-ui/prompt-history-controller.d.ts +8 -0
- package/dist/integrations/pi/session-ui/prompt-history-controller.js +30 -2
- package/dist/integrations/pi/session-ui/prompt-suggestion-controller.d.ts +11 -3
- package/dist/integrations/pi/session-ui/prompt-suggestion-controller.js +126 -51
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +24 -2
- package/dist/integrations/pi/session-ui/session-shell-root.js +108 -38
- package/dist/integrations/pi/session-ui/session-shell.js +51 -31
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +7 -1
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +106 -16
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +35 -11
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +4 -1
- package/dist/integrations/pi/tui-runtime/index.d.ts +1 -0
- package/dist/integrations/pi/tui-runtime/index.js +1 -0
- package/dist/integrations/pi/tui-runtime/mouse-report-input.d.ts +13 -0
- package/dist/integrations/pi/tui-runtime/mouse-report-input.js +59 -0
- package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +32 -0
- package/dist/integrations/pi/tui-runtime/overlay-geometry.js +159 -0
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +1 -0
- package/dist/ui/components/index.d.ts +1 -0
- package/dist/ui/components/index.js +1 -0
- package/dist/ui/components/line-input.d.ts +2 -3
- package/dist/ui/components/line-input.js +4 -20
- package/dist/ui/components/mouse.d.ts +1 -1
- package/dist/ui/components/mouse.js +4 -3
- package/dist/ui/components/prompt-input.d.ts +32 -0
- package/dist/ui/components/prompt-input.js +54 -0
- package/docs/architecture/boundaries.md +1 -0
- package/docs/architecture/history-editor-provenance.md +5 -1
- package/docs/architecture/project-structure.md +1 -0
- package/docs/architecture/prompt-suggestions.md +68 -0
- package/docs/architecture/toolchain.md +1 -1
- package/docs/architecture/ui-reference-provenance.md +23 -0
- package/docs/ci-release-runbook.md +45 -7
- package/docs/features/modal-content-interaction.md +37 -0
- package/package.json +1 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { AssistantMessageComponent, BashExecutionComponent, CompactionSummaryMessageComponent, CustomMessageComponent, DynamicBorder, getMarkdownTheme, parseSkillBlock, ToolExecutionComponent, UserMessageComponent, } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { SkillInvocationMessageComponent } from "./upstream/components/skill-invocation-message.js";
|
|
3
|
+
import { piToolArguments as toolArguments, updatePiToolResult as updateToolResult } from "./tool-result-adapter.js";
|
|
3
4
|
import { createMermaidMarkdownTransformer } from "./upstream/components/mermaid.js";
|
|
4
5
|
import { Container, Image, Spacer, Text, } from "#pi-tui";
|
|
5
6
|
import { PRODUCT_TEXT } from "../../../product-identity.js";
|
|
6
|
-
import { createPiSubmittedPromptComponent, } from "./submitted-prompt-adapter.js";
|
|
7
|
+
import { createPiSubmittedPromptComponent, isPiPromptStyleCompaction, } from "./submitted-prompt-adapter.js";
|
|
8
|
+
export { isPiPromptStyleCompaction } from "./submitted-prompt-adapter.js";
|
|
7
9
|
import { PINNED_PI_LAYOUT, piTheme, } from "./theme.js";
|
|
8
10
|
import { componentPort, createTuiFacade, ensureTheme, isRecord, } from "./shell-shared-facade.js";
|
|
9
|
-
export function createPiShellTranscriptComponent(initial, cwd, extensions, submittedPrompt, initialOutputPad = PINNED_PI_LAYOUT.outputPad, initialHideThinkingBlock = false, initialMermaidRenderingMode = "off", initialShowImages = true, initialImageWidthCells = 80, imageAssets) {
|
|
11
|
+
export function createPiShellTranscriptComponent(initial, cwd, extensions, submittedPrompt, initialOutputPad = PINNED_PI_LAYOUT.outputPad, initialHideThinkingBlock = false, initialMermaidRenderingMode = "off", initialShowImages = true, initialImageWidthCells = 80, imageAssets, presentation) {
|
|
10
12
|
ensureTheme();
|
|
11
13
|
let block = initial;
|
|
12
14
|
let expanded = false;
|
|
@@ -15,27 +17,65 @@ export function createPiShellTranscriptComponent(initial, cwd, extensions, submi
|
|
|
15
17
|
let mermaidRenderingMode = initialMermaidRenderingMode;
|
|
16
18
|
let showImages = initialShowImages;
|
|
17
19
|
let imageWidthCells = initialImageWidthCells;
|
|
18
|
-
|
|
20
|
+
let disposed = false;
|
|
21
|
+
let mount = 0;
|
|
22
|
+
let updating = false;
|
|
23
|
+
let dirty = false;
|
|
24
|
+
let presentationRevision = 0;
|
|
25
|
+
const mutate = (action) => {
|
|
26
|
+
const previous = updating;
|
|
27
|
+
updating = true;
|
|
28
|
+
try {
|
|
29
|
+
return action();
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
updating = previous;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const rebuild = () => {
|
|
36
|
+
const token = ++mount;
|
|
37
|
+
const requestRender = () => {
|
|
38
|
+
if (disposed || updating || token !== mount || dirty)
|
|
39
|
+
return;
|
|
40
|
+
dirty = true;
|
|
41
|
+
presentationRevision++;
|
|
42
|
+
presentation?.changed();
|
|
43
|
+
};
|
|
44
|
+
return mutate(() => withTranscriptImages(transcriptComponent(block, cwd, expanded, extensions, submittedPrompt, outputPad, hideThinkingBlock, mermaidRenderingMode, showImages, imageWidthCells, imageAssets, createTuiFacade({ getColumns: presentation?.getColumns ?? (() => 80),
|
|
45
|
+
getRows: presentation?.getRows ?? (() => 24), requestRender })), block, imageAssets, showImages, imageWidthCells));
|
|
46
|
+
};
|
|
19
47
|
let component = rebuild();
|
|
20
48
|
return {
|
|
21
49
|
get id() { return block.id; },
|
|
22
50
|
get revision() { return block.revision; },
|
|
23
|
-
|
|
24
|
-
|
|
51
|
+
get presentationRevision() { return presentationRevision; },
|
|
52
|
+
render: width => {
|
|
53
|
+
dirty = false;
|
|
54
|
+
const rows = mutate(() => renderWithOutputPad(component, block.kind, width, outputPad));
|
|
55
|
+
const warning = block.toolRendering?.unavailable;
|
|
56
|
+
return warning === undefined ? rows : [...rows, ...new Text(piTheme().fg("warning", warning), outputPad, 0).render(width)];
|
|
57
|
+
},
|
|
58
|
+
invalidate: () => mutate(() => component.invalidate()),
|
|
59
|
+
dispose: () => { disposed = true; mount++; },
|
|
25
60
|
update(next) {
|
|
26
61
|
if (next.id !== block.id)
|
|
27
62
|
throw new TypeError("Pi transcript component identity cannot change");
|
|
28
63
|
const previous = block;
|
|
29
64
|
block = next;
|
|
30
|
-
if (!updateTranscriptComponent(component, previous, next, expanded))
|
|
65
|
+
if (!mutate(() => updateTranscriptComponent(component, previous, next, expanded, imageAssets)))
|
|
31
66
|
component = rebuild();
|
|
32
67
|
},
|
|
33
68
|
setExpanded(next) {
|
|
34
69
|
if (expanded === next)
|
|
35
70
|
return;
|
|
36
71
|
expanded = next;
|
|
72
|
+
if (submittedPrompt !== undefined && isPiPromptStyleCompaction(block))
|
|
73
|
+
return;
|
|
37
74
|
if ("setExpanded" in component && typeof component.setExpanded === "function")
|
|
38
|
-
|
|
75
|
+
mutate(() => {
|
|
76
|
+
if ("setExpanded" in component && typeof component.setExpanded === "function")
|
|
77
|
+
component.setExpanded(expanded);
|
|
78
|
+
});
|
|
39
79
|
else
|
|
40
80
|
component = rebuild();
|
|
41
81
|
},
|
|
@@ -61,8 +101,8 @@ export function createPiShellTranscriptComponent(initial, cwd, extensions, submi
|
|
|
61
101
|
showImages = show;
|
|
62
102
|
imageWidthCells = width;
|
|
63
103
|
if (component instanceof ToolExecutionComponent) {
|
|
64
|
-
component
|
|
65
|
-
|
|
104
|
+
const tool = component;
|
|
105
|
+
mutate(() => { tool.setShowImages(show); tool.setImageWidthCells(width); });
|
|
66
106
|
}
|
|
67
107
|
else
|
|
68
108
|
component = rebuild();
|
|
@@ -70,6 +110,9 @@ export function createPiShellTranscriptComponent(initial, cwd, extensions, submi
|
|
|
70
110
|
};
|
|
71
111
|
}
|
|
72
112
|
function withTranscriptImages(component, block, assets, showImages, imageWidthCells) {
|
|
113
|
+
// Compatibility: Pi owns tool images and renderer state, including references introduced after the call header.
|
|
114
|
+
if (component instanceof ToolExecutionComponent)
|
|
115
|
+
return component;
|
|
73
116
|
const references = block.imageReferences ?? [];
|
|
74
117
|
if (references.length === 0)
|
|
75
118
|
return component;
|
|
@@ -135,7 +178,7 @@ export function renderPiShellPackageUpdateNotice(packages, width) {
|
|
|
135
178
|
container.addChild(new DynamicBorder(text => theme.fg("warning", text)));
|
|
136
179
|
return container.render(width);
|
|
137
180
|
}
|
|
138
|
-
function transcriptComponent(block, cwd, expanded, extensions, submittedPrompt, outputPad, hideThinkingBlock, mermaidRenderingMode, showImages, imageWidthCells) {
|
|
181
|
+
function transcriptComponent(block, cwd, expanded, extensions, submittedPrompt, outputPad, hideThinkingBlock, mermaidRenderingMode, showImages, imageWidthCells, imageAssets, tui) {
|
|
139
182
|
switch (block.kind) {
|
|
140
183
|
case "user": {
|
|
141
184
|
const skill = parseSkillBlock(block.text);
|
|
@@ -156,11 +199,15 @@ function transcriptComponent(block, cwd, expanded, extensions, submittedPrompt,
|
|
|
156
199
|
return assistantComponent(block, outputPad, hideThinkingBlock, mermaidRenderingMode);
|
|
157
200
|
case "tool-call":
|
|
158
201
|
case "tool-result": {
|
|
159
|
-
const component = toolComponent(block, cwd, extensions, showImages, imageWidthCells);
|
|
202
|
+
const component = toolComponent(block, cwd, extensions, showImages, imageWidthCells, imageAssets, tui);
|
|
160
203
|
component.setExpanded(expanded);
|
|
161
204
|
return component;
|
|
162
205
|
}
|
|
163
206
|
case "compaction": {
|
|
207
|
+
if (submittedPrompt !== undefined && isPiPromptStyleCompaction(block)) {
|
|
208
|
+
const tokens = numericPayload(block, "tokensBefore").toLocaleString("en-US");
|
|
209
|
+
return createPiSubmittedPromptComponent(block, submittedPrompt, `Compacted from ${tokens} tokens`);
|
|
210
|
+
}
|
|
164
211
|
const component = new CompactionSummaryMessageComponent({
|
|
165
212
|
role: "compactionSummary",
|
|
166
213
|
summary: block.text,
|
|
@@ -179,10 +226,10 @@ function transcriptComponent(block, cwd, expanded, extensions, submittedPrompt,
|
|
|
179
226
|
case "custom":
|
|
180
227
|
return customMessageComponent(block, expanded, extensions, outputPad);
|
|
181
228
|
case "bash":
|
|
182
|
-
return bashExecutionComponent(block, cwd, expanded);
|
|
229
|
+
return bashExecutionComponent(block, cwd, expanded, tui);
|
|
183
230
|
}
|
|
184
231
|
}
|
|
185
|
-
function updateTranscriptComponent(component, previous, next, expanded) {
|
|
232
|
+
function updateTranscriptComponent(component, previous, next, expanded, imageAssets) {
|
|
186
233
|
if (component instanceof AssistantMessageComponent && next.kind === previous.kind
|
|
187
234
|
&& (next.kind === "assistant" || next.kind === "thinking")) {
|
|
188
235
|
component.updateContent(validatedAssistantMessage(next), next.status === "live");
|
|
@@ -191,22 +238,10 @@ function updateTranscriptComponent(component, previous, next, expanded) {
|
|
|
191
238
|
if (component instanceof ToolExecutionComponent
|
|
192
239
|
&& (next.kind === "tool-call" || next.kind === "tool-result")
|
|
193
240
|
&& (previous.kind === "tool-call" || previous.kind === "tool-result")) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (next.status === "finalized" || payload.argsComplete === true)
|
|
199
|
-
component.setArgsComplete();
|
|
200
|
-
if (payload.partialResult === true) {
|
|
201
|
-
component.updateResult({ content: [{ type: "text", text: next.text }], isError: false }, true);
|
|
202
|
-
}
|
|
203
|
-
else if (next.kind === "tool-result") {
|
|
204
|
-
component.updateResult({
|
|
205
|
-
content: [{ type: "text", text: next.text }],
|
|
206
|
-
isError: payload.isError === true,
|
|
207
|
-
}, next.status === "live");
|
|
208
|
-
}
|
|
209
|
-
component.setExpanded(expanded);
|
|
241
|
+
if (toolArguments(previous) !== toolArguments(next))
|
|
242
|
+
component.updateArgs(toolArguments(next));
|
|
243
|
+
applyToolState(component, next, previous);
|
|
244
|
+
updateToolResult(component, next, imageAssets);
|
|
210
245
|
return true;
|
|
211
246
|
}
|
|
212
247
|
if (component instanceof BashExecutionComponent && next.kind === "bash" && previous.kind === "bash") {
|
|
@@ -296,26 +331,26 @@ function validatedMessageRenderer(value) {
|
|
|
296
331
|
function isPiMessageRenderer(value) {
|
|
297
332
|
return typeof value === "function";
|
|
298
333
|
}
|
|
299
|
-
function toolComponent(block, cwd, extensions, showImages, imageWidthCells) {
|
|
334
|
+
function toolComponent(block, cwd, extensions, showImages, imageWidthCells, imageAssets, tui) {
|
|
300
335
|
const payload = blockPayload(block);
|
|
301
336
|
const toolCallId = stringPayload(payload, "toolCallId") ?? block.id;
|
|
302
337
|
const toolName = stringPayload(payload, "toolName") ?? block.title ?? "tool";
|
|
303
|
-
const argumentsPayload = toolArguments(
|
|
304
|
-
const component = new ToolExecutionComponent(toolName, toolCallId, argumentsPayload, { showImages, imageWidthCells }, validatedToolDefinition(extensions?.getToolDefinition(toolName)), createTuiFacade({ getColumns: () => 80, getRows: () => 24, requestRender() { }
|
|
305
|
-
|
|
338
|
+
const argumentsPayload = toolArguments(block);
|
|
339
|
+
const component = new ToolExecutionComponent(toolName, toolCallId, argumentsPayload, { showImages, imageWidthCells }, validatedToolDefinition(extensions?.getToolDefinition(toolName)), tui ?? createTuiFacade({ getColumns: () => 80, getRows: () => 24, requestRender() { } }), cwd);
|
|
340
|
+
applyToolState(component, block);
|
|
341
|
+
updateToolResult(component, block, imageAssets);
|
|
342
|
+
return component;
|
|
343
|
+
}
|
|
344
|
+
/** The public renderer's argument-complete and execution-started flags are independent. */
|
|
345
|
+
function applyToolState(component, block, previous) {
|
|
346
|
+
const started = (value) => value.toolState?.execution === "running"
|
|
347
|
+
|| value.toolState === undefined && value.status === "live";
|
|
348
|
+
const complete = (value) => value.toolState?.argsComplete
|
|
349
|
+
?? (value.status === "finalized" || blockPayload(value).argsComplete === true);
|
|
350
|
+
if (started(block) && (previous === undefined || !started(previous)))
|
|
306
351
|
component.markExecutionStarted();
|
|
307
|
-
if (block
|
|
352
|
+
if (complete(block) && (previous === undefined || !complete(previous)))
|
|
308
353
|
component.setArgsComplete();
|
|
309
|
-
if (payload.partialResult === true) {
|
|
310
|
-
component.updateResult({ content: [{ type: "text", text: block.text }], isError: false }, true);
|
|
311
|
-
}
|
|
312
|
-
else if (block.kind === "tool-result") {
|
|
313
|
-
component.updateResult({
|
|
314
|
-
content: [{ type: "text", text: block.text }],
|
|
315
|
-
isError: payload.isError === true,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
return component;
|
|
319
354
|
}
|
|
320
355
|
function customMessageComponent(block, expanded, extensions, outputPad) {
|
|
321
356
|
const payload = blockPayload(block);
|
|
@@ -332,9 +367,9 @@ function customMessageComponent(block, expanded, extensions, outputPad) {
|
|
|
332
367
|
component.setExpanded(expanded);
|
|
333
368
|
return component;
|
|
334
369
|
}
|
|
335
|
-
function bashExecutionComponent(block, cwd, expanded) {
|
|
370
|
+
function bashExecutionComponent(block, cwd, expanded, tui) {
|
|
336
371
|
const payload = blockPayload(block);
|
|
337
|
-
const component = new BashExecutionComponent(stringPayload(payload, "command") ?? block.title ?? "", createTuiFacade({ getColumns: () => 80, getRows: () => 24, requestRender() { } }), payload.excludeFromContext === true);
|
|
372
|
+
const component = new BashExecutionComponent(stringPayload(payload, "command") ?? block.title ?? "", tui ?? createTuiFacade({ getColumns: () => 80, getRows: () => 24, requestRender() { } }), payload.excludeFromContext === true);
|
|
338
373
|
if (block.text)
|
|
339
374
|
component.appendOutput(block.text);
|
|
340
375
|
if (block.status === "finalized")
|
|
@@ -358,9 +393,6 @@ function completeBashComponent(component, block) {
|
|
|
358
393
|
maxBytes: 50 * 1024,
|
|
359
394
|
} : undefined, stringPayload(payload, "fullOutputPath"));
|
|
360
395
|
}
|
|
361
|
-
function toolArguments(payload) {
|
|
362
|
-
return isRecord(payload.arguments) && "json" in payload.arguments ? payload.arguments.json : payload.arguments ?? {};
|
|
363
|
-
}
|
|
364
396
|
function transcriptText(block) {
|
|
365
397
|
if (block.kind === "retry")
|
|
366
398
|
return piTheme().fg("warning", `Retry: ${block.text}`);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { visibleWidth, type AutocompleteProvider, type Component, type KeybindingsConfig, type TUI } from "#pi-tui";
|
|
2
|
-
import type { OwnedUiImageAttachment, OwnedUiSessionViewModel, OwnedUiThinkingLevel, OwnedUiTranscriptBlock } from "../../../contracts/owned-ui/index.js";
|
|
2
|
+
import type { OwnedUiImageAttachment, OwnedUiSessionViewModel, OwnedUiThinkingLevel, OwnedUiTranscriptBlock, SuggestionDecision } from "../../../contracts/owned-ui/index.js";
|
|
3
3
|
import type { HistoryEditorConstructor } from "./history-editor-loader.js";
|
|
4
|
+
import type { PiShellPromptInputPresentation } from "./prompt-input-port.js";
|
|
4
5
|
import type { EditorRecallPort } from "./editor-interaction.js";
|
|
5
6
|
import type { PresentationComponentPort } from "../../../contracts/presentation/index.js";
|
|
6
7
|
/** Terminal-cell width authority used by Pi-rendered component rows. */
|
|
7
8
|
export declare const piShellVisibleWidth: typeof visibleWidth;
|
|
9
|
+
export declare const piShellTruncateToWidth: (text: string, width: number) => string;
|
|
8
10
|
export interface PiShellComponentPort extends PresentationComponentPort {
|
|
9
11
|
}
|
|
10
12
|
export type PiShellClipboardContent = {
|
|
@@ -23,10 +25,18 @@ export interface PiShellEditorPointerEvent {
|
|
|
23
25
|
readonly kind: "press" | "motion" | "release";
|
|
24
26
|
readonly button: number;
|
|
25
27
|
readonly column: number;
|
|
26
|
-
/** One-based row relative to the top of the editor
|
|
28
|
+
/** One-based row relative to the top border of the editor body, excluding its menu. */
|
|
27
29
|
readonly row: number;
|
|
28
30
|
}
|
|
31
|
+
export interface PiShellEditorBodyGeometry {
|
|
32
|
+
/** Zero-based body offset in the most recently rendered component rows. */
|
|
33
|
+
readonly rowOffset: number;
|
|
34
|
+
/** Border-inclusive body height, excluding autocomplete. */
|
|
35
|
+
readonly rowCount: number;
|
|
36
|
+
}
|
|
29
37
|
export interface PiShellEditorPort extends PiShellComponentPort {
|
|
38
|
+
/** Present only for the owned above-prompt autocomplete presentation. */
|
|
39
|
+
bodyGeometry?(): PiShellEditorBodyGeometry;
|
|
30
40
|
readonly recall?: EditorRecallPort;
|
|
31
41
|
readonly historyReplacement?: typeof import("../../../contracts/owned-ui/index.js").PROMPT_HISTORY_EDITOR_REPLACEMENT;
|
|
32
42
|
/** Restores this editor's profile after another Pi component changed the global manager. */
|
|
@@ -34,7 +44,7 @@ export interface PiShellEditorPort extends PiShellComponentPort {
|
|
|
34
44
|
keybindingConfig(): KeybindingsConfig;
|
|
35
45
|
reloadKeybindings(): void;
|
|
36
46
|
/** Uses Pi's terminal decoder rather than assuming one terminal escape spelling. */
|
|
37
|
-
matchesTerminalKey(data: string, key: "home" | "end" | "ctrl+home" | "ctrl+end" | "ctrl+v"): boolean;
|
|
47
|
+
matchesTerminalKey(data: string, key: "home" | "end" | "ctrl+home" | "ctrl+end" | "alt+home" | "ctrl+v"): boolean;
|
|
38
48
|
getText(): string;
|
|
39
49
|
setText(text: string): void;
|
|
40
50
|
insertText(text: string): void;
|
|
@@ -49,6 +59,7 @@ export interface PiShellEditorPort extends PiShellComponentPort {
|
|
|
49
59
|
setThinkingLevel(level: OwnedUiThinkingLevel): void;
|
|
50
60
|
setPromptSuggestion(text: string | null): void;
|
|
51
61
|
canPresentPromptSuggestion(): boolean;
|
|
62
|
+
promptSuggestionBlockReason?(): SuggestionDecision;
|
|
52
63
|
hasSelection(): boolean;
|
|
53
64
|
ownsPointer(): boolean;
|
|
54
65
|
handlePointer(event: PiShellEditorPointerEvent): boolean;
|
|
@@ -104,6 +115,7 @@ export interface PiShellExtensionRendererResolver {
|
|
|
104
115
|
export interface PiShellTranscriptComponentPort extends PiShellComponentPort {
|
|
105
116
|
readonly id: string;
|
|
106
117
|
readonly revision: number;
|
|
118
|
+
readonly presentationRevision?: number;
|
|
107
119
|
update(block: OwnedUiTranscriptBlock): void;
|
|
108
120
|
setExpanded(expanded: boolean): void;
|
|
109
121
|
setOutputPad(padding: 0 | 1): void;
|
|
@@ -116,6 +128,7 @@ export interface PiShellStartupNotice {
|
|
|
116
128
|
readonly message: string;
|
|
117
129
|
}
|
|
118
130
|
export interface PiShellHeaderOptions {
|
|
131
|
+
readonly getKeybindings?: () => KeybindingsConfig;
|
|
119
132
|
readonly quiet?: boolean;
|
|
120
133
|
readonly expanded?: boolean;
|
|
121
134
|
readonly notices?: readonly PiShellStartupNotice[];
|
|
@@ -163,7 +176,7 @@ export interface PiShellEditorOptions {
|
|
|
163
176
|
readonly agentDir?: string;
|
|
164
177
|
readonly autocompleteCommands?: readonly PiShellAutocompleteCommand[];
|
|
165
178
|
readonly promptPresentation?: {
|
|
166
|
-
readonly
|
|
179
|
+
readonly input: PiShellPromptInputPresentation;
|
|
167
180
|
readonly styleSuggestion: (text: string) => string;
|
|
168
181
|
readonly styleSuggestionCaret: (text: string) => string;
|
|
169
182
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { setKeybindings, visibleWidth, } from "#pi-tui";
|
|
1
|
+
import { setKeybindings, visibleWidth, truncateToWidth, } from "#pi-tui";
|
|
2
2
|
import { KeybindingsManager, } from "./upstream/adjacent/core/keybindings.js";
|
|
3
3
|
import { ensurePiTheme, } from "./theme.js";
|
|
4
4
|
/** Terminal-cell width authority used by Pi-rendered component rows. */
|
|
5
5
|
export const piShellVisibleWidth = visibleWidth;
|
|
6
|
+
export const piShellTruncateToWidth = (text, width) => truncateToWidth(text, width, "");
|
|
6
7
|
// Provenance: pinned from packages/coding-agent/src/core/slash-commands.ts at 914cf14.
|
|
7
8
|
export function createTuiFacade(options) {
|
|
8
9
|
const children = [];
|
|
@@ -9,5 +9,7 @@ export interface PiShellSubmittedPromptComposer {
|
|
|
9
9
|
readonly timestamp?: (text: string) => string;
|
|
10
10
|
}) => readonly string[];
|
|
11
11
|
}
|
|
12
|
+
/** Branch summaries share the block kind but do not participate in prompt navigation. */
|
|
13
|
+
export declare function isPiPromptStyleCompaction(block: OwnedUiTranscriptBlock): boolean;
|
|
12
14
|
/** Pi owns Markdown and theme adaptation; the injected composer owns neutral row geometry. */
|
|
13
|
-
export declare function createPiSubmittedPromptComponent(block: OwnedUiTranscriptBlock, composer: PiShellSubmittedPromptComposer): Component;
|
|
15
|
+
export declare function createPiSubmittedPromptComponent(block: OwnedUiTranscriptBlock, composer: PiShellSubmittedPromptComposer, heading?: string): Component;
|
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { Box, Markdown } from "#pi-tui";
|
|
2
|
+
import { Box, Markdown, Text } from "#pi-tui";
|
|
3
3
|
import { piTheme } from "./theme.js";
|
|
4
|
+
/** Branch summaries share the block kind but do not participate in prompt navigation. */
|
|
5
|
+
export function isPiPromptStyleCompaction(block) {
|
|
6
|
+
return block.kind === "compaction" && block.status === "finalized"
|
|
7
|
+
&& typeof block.payload === "object" && block.payload !== null
|
|
8
|
+
&& block.payload.role === "compactionSummary";
|
|
9
|
+
}
|
|
4
10
|
/** Pi owns Markdown and theme adaptation; the injected composer owns neutral row geometry. */
|
|
5
|
-
export function createPiSubmittedPromptComponent(block, composer) {
|
|
11
|
+
export function createPiSubmittedPromptComponent(block, composer, heading) {
|
|
6
12
|
const timestamp = numericTimestamp(block);
|
|
13
|
+
// Invariant: presentation-only chrome stays outside the summary's Markdown parser and stored text.
|
|
14
|
+
const header = heading === undefined ? undefined : new Text(heading, 0, 0);
|
|
7
15
|
const markdown = new Markdown(block.text, 0, 0, getMarkdownTheme(), { color: content => piTheme().fg("userMessageText", content) }, { preserveOrderedListMarkers: true, preserveBackslashEscapes: true });
|
|
8
16
|
const content = {
|
|
9
17
|
render(width) {
|
|
10
18
|
const layout = composer.layout(width, timestamp);
|
|
11
|
-
|
|
19
|
+
const bodyRows = markdown.render(layout.contentWidth);
|
|
20
|
+
const rows = header === undefined ? bodyRows : [
|
|
21
|
+
...header.render(layout.contentWidth).map(row => piTheme().fg("userMessageText", row)),
|
|
22
|
+
...(bodyRows.length === 0 ? [] : ["", ...bodyRows]),
|
|
23
|
+
];
|
|
24
|
+
return [...composer.compose(rows, width, timestamp, {
|
|
12
25
|
prefix: text => piTheme().fg("muted", text),
|
|
13
26
|
timestamp: text => piTheme().fg("dim", text),
|
|
14
27
|
})];
|
|
15
28
|
},
|
|
16
|
-
invalidate
|
|
29
|
+
invalidate() {
|
|
30
|
+
header?.invalidate();
|
|
31
|
+
markdown.invalidate();
|
|
32
|
+
},
|
|
17
33
|
};
|
|
18
34
|
const box = new Box(0, 0, line => piTheme().bg("userMessageBg", line));
|
|
19
35
|
box.addChild(content);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ToolExecutionComponent } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { OwnedUiTranscriptBlock } from "../../../contracts/owned-ui/index.js";
|
|
3
|
+
import { type PiShellImageAssetResolver } from "./shell-shared-facade.js";
|
|
4
|
+
/** Reconstruct public Pi inputs once at the component boundary, never from diagnostic summaries. */
|
|
5
|
+
export declare function updatePiToolResult(component: ToolExecutionComponent, block: OwnedUiTranscriptBlock, assets?: PiShellImageAssetResolver): void;
|
|
6
|
+
export declare function piToolArguments(block: OwnedUiTranscriptBlock): unknown;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { isRecord } from "./shell-shared-facade.js";
|
|
2
|
+
/** Reconstruct public Pi inputs once at the component boundary, never from diagnostic summaries. */
|
|
3
|
+
export function updatePiToolResult(component, block, assets) {
|
|
4
|
+
const payload = isRecord(block.payload) ? block.payload : {};
|
|
5
|
+
if (block.kind !== "tool-result" && payload.partialResult !== true)
|
|
6
|
+
return;
|
|
7
|
+
const image = (index) => {
|
|
8
|
+
const reference = block.imageReferences?.[index];
|
|
9
|
+
const asset = reference === undefined ? null : assets?.resolve(reference.assetId);
|
|
10
|
+
return asset == null
|
|
11
|
+
? { type: "text", text: `[Image unavailable: ${reference?.mimeType ?? "unknown"}]` }
|
|
12
|
+
: { type: "image", data: asset.data, mimeType: asset.mimeType };
|
|
13
|
+
};
|
|
14
|
+
const result = block.toolRendering?.result;
|
|
15
|
+
const content = result === undefined
|
|
16
|
+
? [{ type: "text", text: block.text }, ...(block.imageReferences ?? []).map((_, index) => image(index))]
|
|
17
|
+
: result.content.map(part => part.type === "text"
|
|
18
|
+
? { type: "text", text: block.text.slice(part.start, part.end) } : image(part.imageIndex));
|
|
19
|
+
component.updateResult({ content, details: result?.details, isError: payload.isError === true }, block.status === "live");
|
|
20
|
+
}
|
|
21
|
+
export function piToolArguments(block) {
|
|
22
|
+
if (block.toolRendering !== undefined)
|
|
23
|
+
return block.toolRendering.arguments;
|
|
24
|
+
// Compatibility: existing owned blocks and component consumers may lack rendering metadata.
|
|
25
|
+
const payload = isRecord(block.payload) ? block.payload : {};
|
|
26
|
+
return isRecord(payload.arguments) && "json" in payload.arguments ? payload.arguments.json : payload.arguments ?? {};
|
|
27
|
+
}
|
|
@@ -421,6 +421,7 @@ export declare class KeybindingsManager extends TuiKeybindingsManager {
|
|
|
421
421
|
private configPath;
|
|
422
422
|
constructor(userBindings?: KeybindingsConfig, configPath?: string, definitions?: KeybindingDefinitions);
|
|
423
423
|
static create(agentDir?: string): KeybindingsManager;
|
|
424
|
+
static fromOwnedBindings(bindings?: KeybindingsConfig): KeybindingsManager;
|
|
424
425
|
static createForOwnedInput(agentDir?: string): KeybindingsManager;
|
|
425
426
|
private static createWithDefinitions;
|
|
426
427
|
reload(): void;
|
|
@@ -150,6 +150,8 @@ export const KEYBINDINGS = {
|
|
|
150
150
|
/** Pi actions with bare-A1 aliases; the comparison profile keeps KEYBINDINGS unchanged. */
|
|
151
151
|
const OWNED_INPUT_KEYBINDINGS = {
|
|
152
152
|
...KEYBINDINGS,
|
|
153
|
+
"app.thinking.cycle": { ...KEYBINDINGS["app.thinking.cycle"], defaultKeys: "ctrl+l" },
|
|
154
|
+
"app.model.select": { ...KEYBINDINGS["app.model.select"], defaultKeys: [] },
|
|
153
155
|
"tui.editor.deleteWordBackward": {
|
|
154
156
|
...KEYBINDINGS["tui.editor.deleteWordBackward"],
|
|
155
157
|
defaultKeys: [...KEYBINDINGS["tui.editor.deleteWordBackward"].defaultKeys, "ctrl+backspace"],
|
|
@@ -304,6 +306,9 @@ export class KeybindingsManager extends TuiKeybindingsManager {
|
|
|
304
306
|
static create(agentDir = getAgentDir()) {
|
|
305
307
|
return KeybindingsManager.createWithDefinitions(agentDir, KEYBINDINGS);
|
|
306
308
|
}
|
|
309
|
+
static fromOwnedBindings(bindings = {}) {
|
|
310
|
+
return new KeybindingsManager(bindings, undefined, OWNED_INPUT_KEYBINDINGS);
|
|
311
|
+
}
|
|
307
312
|
static createForOwnedInput(agentDir = getAgentDir()) {
|
|
308
313
|
return KeybindingsManager.createWithDefinitions(agentDir, OWNED_INPUT_KEYBINDINGS);
|
|
309
314
|
}
|
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
* Adapted from @earendil-works/pi-coding-agent 0.84.2
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/custom-editor.ts (MIT).
|
|
4
4
|
* Modifications: A1-owned class name, synchronized keybinding contract, and a semantic
|
|
5
|
-
* bare-A1
|
|
5
|
+
* injected bare-A1 input-frame/contextual-suggestion presentation with explicit body geometry.
|
|
6
6
|
*/
|
|
7
7
|
import { type EditorOptions, type EditorTheme, type TUI } from "#pi-tui";
|
|
8
8
|
import type { AppKeybinding, KeybindingsManager } from "../adjacent/core/keybindings.js";
|
|
9
9
|
import type { EditorSurface } from "../../editor-interaction.js";
|
|
10
|
+
import type { PiShellPromptInputPresentation } from "../../prompt-input-port.js";
|
|
10
11
|
export interface OwnedEditorOptions extends EditorOptions {
|
|
11
12
|
readonly persistentHistory?: boolean;
|
|
12
13
|
readonly styleHistoryLabel?: (text: string) => string;
|
|
13
|
-
readonly
|
|
14
|
+
readonly inputPresentation?: PiShellPromptInputPresentation;
|
|
14
15
|
readonly styleSuggestion?: (text: string) => string;
|
|
15
16
|
readonly styleSuggestionCaret?: (text: string) => string;
|
|
16
17
|
readonly terminalRows?: () => number;
|
|
18
|
+
/** Reuses the editor's established atomic-aware visual layout when available. */
|
|
19
|
+
readonly getVisualLineCount?: (width: number) => number | undefined;
|
|
17
20
|
}
|
|
18
21
|
export interface ShellEditorInstance extends EditorSurface {
|
|
19
22
|
readonly actionHandlers: Map<AppKeybinding, () => void>;
|
|
@@ -22,6 +25,8 @@ export interface ShellEditorInstance extends EditorSurface {
|
|
|
22
25
|
onPasteImage?: () => void;
|
|
23
26
|
onExtensionShortcut?: (data: string) => boolean;
|
|
24
27
|
onPromptSuggestionAccepted?: (text: string) => void;
|
|
28
|
+
/** Border-inclusive body height from the most recent render, excluding autocomplete. */
|
|
29
|
+
getRenderedBodyRowCount(): number;
|
|
25
30
|
setPromptSuggestion(text: string | null): void;
|
|
26
31
|
canPresentPromptSuggestion(): boolean;
|
|
27
32
|
onAction(action: AppKeybinding, handler: () => void): void;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Adapted from @earendil-works/pi-coding-agent 0.84.2
|
|
3
3
|
* packages/coding-agent/src/modes/interactive/components/custom-editor.ts (MIT).
|
|
4
4
|
* Modifications: A1-owned class name, synchronized keybinding contract, and a semantic
|
|
5
|
-
* bare-A1
|
|
5
|
+
* injected bare-A1 input-frame/contextual-suggestion presentation with explicit body geometry.
|
|
6
6
|
*/
|
|
7
|
-
import { CURSOR_MARKER, Editor,
|
|
7
|
+
import { CURSOR_MARKER, Editor, visibleWidth, wrapTextWithAnsi, } from "#pi-tui";
|
|
8
8
|
const PROMPT_GRAPHEMES = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
9
9
|
export function createOwnedEditorClass(Base) {
|
|
10
10
|
return class extends Base {
|
|
@@ -16,23 +16,27 @@ export function createOwnedEditorClass(Base) {
|
|
|
16
16
|
onExtensionShortcut;
|
|
17
17
|
onPromptSuggestionAccepted;
|
|
18
18
|
#promptSuggestion = null;
|
|
19
|
-
#
|
|
19
|
+
#inputPresentation;
|
|
20
20
|
#styleSuggestion;
|
|
21
21
|
#styleSuggestionCaret;
|
|
22
22
|
#terminalRows;
|
|
23
|
+
#getVisualLineCount;
|
|
24
|
+
#renderedBodyRowCount = 0;
|
|
23
25
|
constructor(tui, theme, keybindings, options = {}) {
|
|
24
26
|
super(tui, theme, options);
|
|
25
27
|
this.keybindings = keybindings;
|
|
26
|
-
this.#
|
|
28
|
+
this.#inputPresentation = options.inputPresentation;
|
|
27
29
|
this.#styleSuggestion = options.styleSuggestion ?? (text => text);
|
|
28
30
|
this.#styleSuggestionCaret = options.styleSuggestionCaret ?? (text => `\u001b[7m${text}\u001b[27m`);
|
|
29
31
|
this.#terminalRows = options.terminalRows ?? (() => 24);
|
|
32
|
+
this.#getVisualLineCount = options.getVisualLineCount;
|
|
30
33
|
}
|
|
34
|
+
getRenderedBodyRowCount() { return this.#renderedBodyRowCount; }
|
|
31
35
|
setPromptSuggestion(text) {
|
|
32
36
|
this.#promptSuggestion = text;
|
|
33
37
|
}
|
|
34
38
|
canPresentPromptSuggestion() {
|
|
35
|
-
return this.#
|
|
39
|
+
return this.#inputPresentation !== undefined
|
|
36
40
|
&& this.focused
|
|
37
41
|
&& !this.disableSubmit
|
|
38
42
|
&& this.getText().length === 0
|
|
@@ -44,10 +48,15 @@ export function createOwnedEditorClass(Base) {
|
|
|
44
48
|
super.setText(text);
|
|
45
49
|
}
|
|
46
50
|
render(width) {
|
|
47
|
-
if (this.#
|
|
48
|
-
|
|
51
|
+
if (this.#inputPresentation === undefined) {
|
|
52
|
+
const rows = super.render(width);
|
|
53
|
+
this.#renderedBodyRowCount = this.#getVisualLineCount === undefined ? rows.length : this.#measureBodyRows(width);
|
|
54
|
+
return rows;
|
|
55
|
+
}
|
|
49
56
|
if (this.#promptSuggestion !== null && this.canPresentPromptSuggestion()) {
|
|
50
|
-
|
|
57
|
+
const rows = this.#renderSuggestion(width);
|
|
58
|
+
this.#renderedBodyRowCount = rows.length;
|
|
59
|
+
return rows;
|
|
51
60
|
}
|
|
52
61
|
return this.#renderPrefixedEditor(width);
|
|
53
62
|
}
|
|
@@ -105,16 +114,11 @@ export function createOwnedEditorClass(Base) {
|
|
|
105
114
|
super.handleInput(data);
|
|
106
115
|
}
|
|
107
116
|
#renderSuggestion(width) {
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
const maxPadding = Math.max(0, Math.floor((innerWidth - 1) / 2));
|
|
111
|
-
const paddingX = Math.min(this.getPaddingX(), maxPadding);
|
|
112
|
-
const contentWidth = Math.max(1, innerWidth - paddingX * 2);
|
|
113
|
-
const layoutWidth = Math.max(1, contentWidth - (paddingX ? 0 : 1));
|
|
117
|
+
const presentation = this.#inputPresentation;
|
|
118
|
+
const { paddingX, contentWidth, layoutWidth } = presentation.geometry(width, this.getPaddingX());
|
|
114
119
|
const chunks = wrapTextWithAnsi(this.#promptSuggestion ?? "", layoutWidth).map(text => ({ text }));
|
|
115
120
|
const maxVisible = Math.max(5, Math.floor(this.#terminalRows() * 0.3));
|
|
116
121
|
const visible = chunks.slice(0, maxVisible);
|
|
117
|
-
const horizontal = this.borderColor("─".repeat(Math.max(0, width)));
|
|
118
122
|
const leftPadding = " ".repeat(paddingX);
|
|
119
123
|
const rightPadding = leftPadding;
|
|
120
124
|
const rows = visible.map((chunk, index) => {
|
|
@@ -130,27 +134,31 @@ export function createOwnedEditorClass(Base) {
|
|
|
130
134
|
}
|
|
131
135
|
const plainWidth = visibleWidth(chunk.text);
|
|
132
136
|
const padding = " ".repeat(Math.max(0, contentWidth - plainWidth));
|
|
133
|
-
|
|
134
|
-
return truncateToWidth(`${prefix}${leftPadding}${content}${padding}${rightPadding}`, width);
|
|
137
|
+
return `${leftPadding}${content}${padding}${rightPadding}`;
|
|
135
138
|
});
|
|
136
|
-
return
|
|
139
|
+
return presentation.render(width, () => ({ rows }));
|
|
137
140
|
}
|
|
138
141
|
#renderPrefixedEditor(width) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
142
|
+
return this.#inputPresentation.render(width, innerWidth => {
|
|
143
|
+
const rows = super.render(innerWidth);
|
|
144
|
+
this.#renderedBodyRowCount = this.#measureBodyRows(innerWidth);
|
|
145
|
+
const bottomBorder = this.#renderedBodyRowCount - 1;
|
|
146
|
+
return {
|
|
147
|
+
rows: rows.slice(1, bottomBorder),
|
|
148
|
+
topRule: rows[0],
|
|
149
|
+
bottomRule: rows[bottomBorder],
|
|
150
|
+
after: rows.slice(bottomBorder + 1),
|
|
151
|
+
};
|
|
152
|
+
}, true, this.getPaddingX());
|
|
153
|
+
}
|
|
154
|
+
#measureBodyRows(width) {
|
|
155
|
+
const padding = Math.min(this.getPaddingX(), Math.max(0, Math.floor((width - 1) / 2)));
|
|
156
|
+
const contentWidth = Math.max(1, width - padding * 2);
|
|
157
|
+
const layoutWidth = Math.max(1, contentWidth - (padding ? 0 : 1));
|
|
158
|
+
// Invariant: split by editor layout, never by styled border or completion text.
|
|
159
|
+
const lineCount = this.#getVisualLineCount?.(layoutWidth)
|
|
160
|
+
?? this.getLines().reduce((count, line) => count + wrapTextWithAnsi(line, layoutWidth).length, 0);
|
|
161
|
+
return Math.min(Math.max(1, lineCount), Math.max(5, Math.floor(this.#terminalRows() * 0.3))) + 2;
|
|
154
162
|
}
|
|
155
163
|
};
|
|
156
164
|
}
|
|
@@ -3,7 +3,8 @@ import type { OwnedUiSessionViewModel } from "../../../../../contracts/owned-ui/
|
|
|
3
3
|
export declare class SessionFooter implements Component {
|
|
4
4
|
private readonly getView;
|
|
5
5
|
private readonly cwd;
|
|
6
|
-
|
|
6
|
+
private readonly profile;
|
|
7
|
+
constructor(getView: () => OwnedUiSessionViewModel, cwd: string, profile?: "pi" | "a1");
|
|
7
8
|
invalidate(): void;
|
|
8
9
|
dispose(): void;
|
|
9
10
|
render(width: number): string[];
|