@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,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { acceptsTranscriptUpdate } from "../../../contracts/owned-ui/index.js";
|
|
2
|
+
import { backgroundSgrSpan, caretCell, composeSubmittedPromptRows, PromptInput, promptArrow, displayWidth, faint, formatSubmittedPromptTime, heldNativeHyperlinkStyle, hyperlinkSgrSpan, nativeHyperlinkStyle, overlaySpan, progressStatusText, submittedPromptLayout, stripAnsi, } from "../../../ui/components/index.js";
|
|
2
3
|
import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES, } from "../engine/index.js";
|
|
3
|
-
import { createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellStatus, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, onPiThemeChange, piShellVisibleWidth, piTheme, renderPiShellCommandMessage, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../components/index.js";
|
|
4
|
+
import { createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDaxnuts, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellOperationLoader, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellStatus, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, onPiThemeChange, isPiPromptStyleCompaction, piShellVisibleWidth, piShellTruncateToWidth, piTheme, renderPiShellCommandMessage, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../components/index.js";
|
|
4
5
|
import { PiTuiRuntimeAdapter, classifyPiTuiInput, } from "../tui-runtime/index.js";
|
|
5
6
|
import { PromptChipStore } from "./prompt-chips.js";
|
|
6
7
|
import { SessionViewportController } from "./session-viewport-controller.js";
|
|
@@ -66,7 +67,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
66
67
|
this.#submittedPromptComposer = this.#customViewport
|
|
67
68
|
? {
|
|
68
69
|
layout: submittedPromptLayout,
|
|
69
|
-
compose: (rows, width, source, style) => composeSubmittedPromptRows(rows, width, source, style)
|
|
70
|
+
compose: (rows, width, source, style) => composeSubmittedPromptRows(rows, width, source, { ...style, prefix: text => promptArrow(text, piTheme()) })
|
|
70
71
|
.map(row => nativeHyperlinkStyle(row, nativeTranscriptLinkColor)),
|
|
71
72
|
}
|
|
72
73
|
: undefined;
|
|
@@ -77,10 +78,13 @@ export class OwnedUiSessionShellRoot {
|
|
|
77
78
|
this.#onViewportFrame = handlers.onViewportFrame;
|
|
78
79
|
this.#onInputSurfaceChanged = handlers.onInputSurfaceChanged;
|
|
79
80
|
this.#dockInputReuseEnabled = handlers.enableDockInputReuse ?? true;
|
|
80
|
-
this.header = createPiShellHeader(
|
|
81
|
+
this.header = createPiShellHeader({
|
|
82
|
+
...startup,
|
|
83
|
+
...(this.#customViewport ? { getKeybindings: () => this.editor.keybindingConfig() } : {}),
|
|
84
|
+
});
|
|
81
85
|
this.resources = createPiShellLoadedResources(startup.resources ?? [], startup.expanded ?? false);
|
|
82
86
|
this.#status = createPiShellStatus(view, progressStatusText, handlers);
|
|
83
|
-
this.#footer = createPiShellFooter(this.#viewWithExtensionStatuses(view), cwd);
|
|
87
|
+
this.#footer = createPiShellFooter(this.#viewWithExtensionStatuses(view), cwd, this.#customViewport ? "a1" : "pi");
|
|
84
88
|
this.#queued = createPiQueuedInputStatus(view.editor.queuedSubmissions, this.#customViewport ? "custom-viewport" : "pinned");
|
|
85
89
|
this.editor = createPiShellEditor({
|
|
86
90
|
...handlers,
|
|
@@ -122,7 +126,10 @@ export class OwnedUiSessionShellRoot {
|
|
|
122
126
|
onToolsExpand: () => this.#setToolsExpanded(!this.#toolsExpanded),
|
|
123
127
|
...(this.#customViewport ? {
|
|
124
128
|
promptPresentation: {
|
|
125
|
-
|
|
129
|
+
input: new PromptInput({ fg: (token, text) => piTheme().fg(token, text) }, {
|
|
130
|
+
measure: piShellVisibleWidth,
|
|
131
|
+
truncate: piShellTruncateToWidth,
|
|
132
|
+
}),
|
|
126
133
|
styleSuggestion: faint,
|
|
127
134
|
styleSuggestionCaret: caretCell,
|
|
128
135
|
},
|
|
@@ -209,6 +216,12 @@ export class OwnedUiSessionShellRoot {
|
|
|
209
216
|
prepareHistoryText(text) {
|
|
210
217
|
return this.#promptChips.prepareHistoryText(text);
|
|
211
218
|
}
|
|
219
|
+
rehydrateHistoryText(text, resolveImage) {
|
|
220
|
+
return this.#promptChips.rehydrateHistoryText(text, resolveImage);
|
|
221
|
+
}
|
|
222
|
+
imageChipAttachmentsFromEditor(text) {
|
|
223
|
+
return this.#promptChips.imageChipAttachments(text);
|
|
224
|
+
}
|
|
212
225
|
preparePromptSubmission(text) {
|
|
213
226
|
return this.#promptChips.prepareSubmission(text);
|
|
214
227
|
}
|
|
@@ -224,17 +237,26 @@ export class OwnedUiSessionShellRoot {
|
|
|
224
237
|
this.resetPendingPastes();
|
|
225
238
|
return this.#promptChips.dispose();
|
|
226
239
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
240
|
+
promptSuggestionPrepareBlockReason() {
|
|
241
|
+
if (!this.usesDefaultInputSurface())
|
|
242
|
+
return "replacement-input";
|
|
243
|
+
if (this.#view.dialog !== null || this.#view.overlay !== null)
|
|
244
|
+
return "modal";
|
|
245
|
+
if (this.editor.getText().length > 0)
|
|
246
|
+
return "draft";
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
canPreparePromptSuggestion() { return this.promptSuggestionPrepareBlockReason() === null; }
|
|
250
|
+
promptSuggestionPresentationBlockReason() {
|
|
251
|
+
const reason = this.promptSuggestionPrepareBlockReason();
|
|
252
|
+
if (reason !== null)
|
|
253
|
+
return reason;
|
|
254
|
+
if (this.#view.lifecycle !== "ready")
|
|
255
|
+
return "not-ready";
|
|
256
|
+
return this.editor.promptSuggestionBlockReason?.()
|
|
257
|
+
?? (this.editor.canPresentPromptSuggestion() ? null : "presentation-unavailable");
|
|
258
|
+
}
|
|
259
|
+
canPresentPromptSuggestion() { return this.promptSuggestionPresentationBlockReason() === null; }
|
|
238
260
|
setPromptSuggestion(text) {
|
|
239
261
|
this.editor.setPromptSuggestion(text);
|
|
240
262
|
}
|
|
@@ -250,7 +272,8 @@ export class OwnedUiSessionShellRoot {
|
|
|
250
272
|
this.#syncTranscript(view.transcript);
|
|
251
273
|
this.editor.setSubmitEnabled(view.lifecycle !== "stopping" && view.lifecycle !== "stopped" && view.lifecycle !== "failed");
|
|
252
274
|
this.editor.setThinkingLevel(view.thinkingLevel);
|
|
253
|
-
|
|
275
|
+
// Performance: semantic updates already invalidate affected transcript blocks. Chrome is a separate authority.
|
|
276
|
+
this.#invalidateChrome();
|
|
254
277
|
}
|
|
255
278
|
/**
|
|
256
279
|
* Applies one block: its component is created or updated in place and the order grows
|
|
@@ -260,13 +283,12 @@ export class OwnedUiSessionShellRoot {
|
|
|
260
283
|
applyTranscriptBlock(block) {
|
|
261
284
|
const current = this.#blocksById.get(block.id);
|
|
262
285
|
// Invariant: a full-view/final presentation may preempt queued partials. Never revive an older revision.
|
|
263
|
-
if (current !== undefined && (current
|
|
286
|
+
if (current !== undefined && !acceptsTranscriptUpdate(current, block))
|
|
264
287
|
return;
|
|
265
288
|
this.#blocksById.set(block.id, block);
|
|
266
289
|
const component = this.#transcript.get(block.id);
|
|
267
290
|
if (component === undefined) {
|
|
268
|
-
const created =
|
|
269
|
-
created.setExpanded(this.#toolsExpanded);
|
|
291
|
+
const created = this.#mountTranscript(block);
|
|
270
292
|
this.#transcript.set(block.id, created);
|
|
271
293
|
this.#transcriptOrder.push(block.id);
|
|
272
294
|
}
|
|
@@ -278,6 +300,20 @@ export class OwnedUiSessionShellRoot {
|
|
|
278
300
|
this.#renderedRows.delete(block.id);
|
|
279
301
|
this.#documentLayouts.clear();
|
|
280
302
|
}
|
|
303
|
+
#mountTranscript(block) {
|
|
304
|
+
const created = createPiShellTranscriptComponent(block, this.#cwd, this.#extensionRenderers, this.#submittedPromptComposer, this.#outputPad, !this.#thinkingVisible, this.#mermaidRenderingMode, this.#showImages, this.#imageWidthCells, this.#imageAssets, { ...this.#componentRuntime, changed: () => {
|
|
305
|
+
if (this.#transcript.get(block.id) !== created)
|
|
306
|
+
return;
|
|
307
|
+
this.#renderedRows.delete(block.id);
|
|
308
|
+
this.#documentLayouts.clear();
|
|
309
|
+
this.#visibleViewportSnapshot = undefined;
|
|
310
|
+
this.#dockInputCandidate = false;
|
|
311
|
+
this.#dockInputRevision = undefined;
|
|
312
|
+
this.#componentRuntime.requestRender();
|
|
313
|
+
} });
|
|
314
|
+
created.setExpanded(this.#toolsExpanded);
|
|
315
|
+
return created;
|
|
316
|
+
}
|
|
281
317
|
render(width) {
|
|
282
318
|
if (!this.#customViewport) {
|
|
283
319
|
this.#viewportController.setEditorPointerFrame(undefined);
|
|
@@ -297,20 +333,27 @@ export class OwnedUiSessionShellRoot {
|
|
|
297
333
|
const dockInputCandidate = this.#dockInputCandidate;
|
|
298
334
|
// Concurrency: input may change after a keyboard receipt but before its paint.
|
|
299
335
|
// Capture only the live inputs this composition will actually represent.
|
|
300
|
-
const viewportRevision = this.#viewportController.presentationRevision;
|
|
301
|
-
const selectionRevision = this.#viewportController.selectionRevision;
|
|
302
|
-
const pointerPosition = this.#viewportController.pointerPosition;
|
|
303
336
|
const inputSurface = this.#inputSurface;
|
|
304
337
|
const dockRows = dock.rows;
|
|
305
338
|
const selectableDocumentRowCount = document.rows.length;
|
|
306
339
|
const dockStartRow = height - dockRows.length + 1;
|
|
307
340
|
const editorOffset = dock.editorOffset;
|
|
341
|
+
this.#viewportController.setInputSurfaceFrame(this.usesDefaultInputSurface() ? undefined : {
|
|
342
|
+
component: this.#inputSurface,
|
|
343
|
+
columnStart: 1,
|
|
344
|
+
columnEnd: width,
|
|
345
|
+
rowStart: Math.max(1, dockStartRow + editorOffset),
|
|
346
|
+
rowEnd: Math.min(height, dockStartRow + editorOffset + dock.inputRows - 1),
|
|
347
|
+
});
|
|
308
348
|
this.#viewportController.setEditorPointerFrame(this.usesDefaultInputSurface()
|
|
309
349
|
? {
|
|
310
350
|
rowStart: dockStartRow + editorOffset,
|
|
311
351
|
rowEnd: dockStartRow + editorOffset + dock.inputRows - 1,
|
|
312
352
|
}
|
|
313
353
|
: undefined);
|
|
354
|
+
const viewportRevision = this.#viewportController.presentationRevision;
|
|
355
|
+
const selectionRevision = this.#viewportController.selectionRevision;
|
|
356
|
+
const pointerPosition = this.#viewportController.pointerPosition;
|
|
314
357
|
this.#dockInputCandidate = false;
|
|
315
358
|
let frame = dockInputCandidate
|
|
316
359
|
&& snapshot !== undefined
|
|
@@ -407,14 +450,31 @@ export class OwnedUiSessionShellRoot {
|
|
|
407
450
|
noteCompletedAssistantMessage() {
|
|
408
451
|
this.#viewportController.noteCompletedAssistantMessage();
|
|
409
452
|
}
|
|
453
|
+
/** A new session binding owns new mounts even when it reuses semantic invocation ids. */
|
|
454
|
+
resetTranscript() {
|
|
455
|
+
for (const component of this.#transcript.values())
|
|
456
|
+
component.dispose?.();
|
|
457
|
+
this.#transcript.clear();
|
|
458
|
+
this.#blocksById.clear();
|
|
459
|
+
this.#transcriptOrder = [];
|
|
460
|
+
this.#renderedRows.clear();
|
|
461
|
+
this.#documentLayouts.clear();
|
|
462
|
+
}
|
|
410
463
|
resetViewport() {
|
|
411
464
|
this.#viewportController.reset();
|
|
412
465
|
}
|
|
413
466
|
clearViewportPointerState() {
|
|
414
467
|
this.#viewportController.clearPointerState();
|
|
415
468
|
}
|
|
416
|
-
|
|
417
|
-
|
|
469
|
+
setViewportOverlaySurfaces(surfaces) {
|
|
470
|
+
this.#viewportController.setOverlaySurfaces(surfaces);
|
|
471
|
+
}
|
|
472
|
+
viewportInputGeometryReady(width, height) {
|
|
473
|
+
const frame = this.#viewportController.frame;
|
|
474
|
+
return this.#viewportController.inputGeometryReady && frame?.descriptor.width === width && frame.descriptor.height === height;
|
|
475
|
+
}
|
|
476
|
+
handleViewportPreInput(data, allowWheel = true, now = Date.now(), editorActive = this.usesDefaultInputSurface()) {
|
|
477
|
+
return this.#viewportController.handlePreInput(data, allowWheel, now, editorActive);
|
|
418
478
|
}
|
|
419
479
|
#renderDock(width) {
|
|
420
480
|
return this.#renderDockLayout(width).rows;
|
|
@@ -425,13 +485,15 @@ export class OwnedUiSessionShellRoot {
|
|
|
425
485
|
const transientRows = [...queued, ...statusRows];
|
|
426
486
|
const aboveWidgets = this.#renderWidgets("aboveEditor", width);
|
|
427
487
|
const input = this.#inputSurface.render(width);
|
|
488
|
+
// Invariant: pointer rows describe the body, not the autocomplete block now preceding it.
|
|
489
|
+
const body = this.usesDefaultInputSurface() ? this.editor.bodyGeometry?.() : undefined;
|
|
428
490
|
const belowWidgets = this.#renderWidgets("belowEditor", width);
|
|
429
491
|
const footer = this.#renderFooter(width);
|
|
430
492
|
const rowsWithoutTransient = [...aboveWidgets, ...input, ...belowWidgets, ...footer];
|
|
431
493
|
return {
|
|
432
494
|
rows: [...transientRows, ...rowsWithoutTransient],
|
|
433
|
-
editorOffset: transientRows.length + aboveWidgets.length,
|
|
434
|
-
inputRows: input.length,
|
|
495
|
+
editorOffset: transientRows.length + aboveWidgets.length + (body?.rowOffset ?? 0),
|
|
496
|
+
inputRows: body?.rowCount ?? input.length,
|
|
435
497
|
};
|
|
436
498
|
}
|
|
437
499
|
#renderQueued(width) {
|
|
@@ -520,14 +582,16 @@ export class OwnedUiSessionShellRoot {
|
|
|
520
582
|
const block = this.#blocksById.get(id);
|
|
521
583
|
if (!this.#thinkingVisible && block?.kind === "thinking")
|
|
522
584
|
continue;
|
|
585
|
+
const promptLike = block !== undefined
|
|
586
|
+
&& (block.kind === "user" || this.#customViewport && isPiPromptStyleCompaction(block));
|
|
523
587
|
const blockWidth = this.#customViewport
|
|
524
|
-
&&
|
|
588
|
+
&& promptLike
|
|
525
589
|
&& this.#viewportController.config.scrollbarAppearance !== "hidden"
|
|
526
590
|
&& width > 1
|
|
527
591
|
? width - 1
|
|
528
592
|
: width;
|
|
529
593
|
const blockRows = this.#blockRows(id, block, blockWidth);
|
|
530
|
-
if (
|
|
594
|
+
if (promptLike) {
|
|
531
595
|
// Compatibility: the first natural prompt gets one breathing row at the document top.
|
|
532
596
|
// Once scrolling advances, the prompt itself reaches row zero and then
|
|
533
597
|
// becomes sticky there, so the spacer is never pinned with it.
|
|
@@ -541,7 +605,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
541
605
|
liveTailStartRow = firstRow;
|
|
542
606
|
}
|
|
543
607
|
rows.push(...blockRows);
|
|
544
|
-
if (
|
|
608
|
+
if (promptLike && blockRows[0] !== undefined) {
|
|
545
609
|
promptAnchors.push({
|
|
546
610
|
id: block.id,
|
|
547
611
|
firstRow,
|
|
@@ -596,14 +660,15 @@ export class OwnedUiSessionShellRoot {
|
|
|
596
660
|
return render();
|
|
597
661
|
let byWidth = this.#renderedRows.get(id);
|
|
598
662
|
const cached = byWidth?.get(width);
|
|
599
|
-
|
|
663
|
+
const presentationRevision = component.presentationRevision ?? 0;
|
|
664
|
+
if (cached?.revision === block.revision && cached.presentationRevision === presentationRevision)
|
|
600
665
|
return cached.rows;
|
|
601
666
|
const rows = render();
|
|
602
667
|
if (byWidth === undefined) {
|
|
603
668
|
byWidth = new Map();
|
|
604
669
|
this.#renderedRows.set(id, byWidth);
|
|
605
670
|
}
|
|
606
|
-
byWidth.set(width, { revision: block.revision, rows });
|
|
671
|
+
byWidth.set(width, { revision: block.revision, presentationRevision, rows });
|
|
607
672
|
// Invariant: bare A1 probes full width before reserving the overflowing rail column;
|
|
608
673
|
// retain both widths without turning resize history into an unbounded cache.
|
|
609
674
|
while (byWidth.size > 2)
|
|
@@ -619,7 +684,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
619
684
|
const block = this.#blocksById.get(id);
|
|
620
685
|
if (block === undefined || (!this.#thinkingVisible && block.kind === "thinking"))
|
|
621
686
|
continue;
|
|
622
|
-
if (rows.length > 0 && block.kind === "user")
|
|
687
|
+
if (rows.length > 0 && (block.kind === "user" || this.#customViewport && isPiPromptStyleCompaction(block)))
|
|
623
688
|
rows.push("");
|
|
624
689
|
rows.push(...this.#blockRows(id, block, width).map(sanitizeExitTranscriptRow));
|
|
625
690
|
}
|
|
@@ -830,6 +895,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
830
895
|
return;
|
|
831
896
|
}
|
|
832
897
|
this.#dockInputCandidate = false;
|
|
898
|
+
this.#viewportController.invalidateInputSurface();
|
|
833
899
|
this.#inputSurface.setFocused?.(false);
|
|
834
900
|
if (disposePrevious && this.#inputSurface !== this.editor)
|
|
835
901
|
this.#inputSurface.dispose?.();
|
|
@@ -852,10 +918,15 @@ export class OwnedUiSessionShellRoot {
|
|
|
852
918
|
}
|
|
853
919
|
}
|
|
854
920
|
invalidate() {
|
|
855
|
-
this.
|
|
856
|
-
this.
|
|
921
|
+
this.#renderedRows.clear();
|
|
922
|
+
this.#documentLayouts.clear();
|
|
857
923
|
for (const component of this.#transcript.values())
|
|
858
924
|
component.invalidate();
|
|
925
|
+
this.#invalidateChrome();
|
|
926
|
+
}
|
|
927
|
+
#invalidateChrome() {
|
|
928
|
+
this.header.invalidate();
|
|
929
|
+
this.resources.invalidate();
|
|
859
930
|
this.editor.invalidate();
|
|
860
931
|
if (this.#inputSurface !== this.editor)
|
|
861
932
|
this.#inputSurface.invalidate();
|
|
@@ -919,8 +990,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
919
990
|
for (const block of blocks) {
|
|
920
991
|
const component = this.#transcript.get(block.id);
|
|
921
992
|
if (component === undefined) {
|
|
922
|
-
const created =
|
|
923
|
-
created.setExpanded(this.#toolsExpanded);
|
|
993
|
+
const created = this.#mountTranscript(block);
|
|
924
994
|
this.#transcript.set(block.id, created);
|
|
925
995
|
}
|
|
926
996
|
else if (component.revision !== block.revision) {
|
|
@@ -23,6 +23,7 @@ export class OwnedUiSessionShell {
|
|
|
23
23
|
#unsubscribePromptSuggestions;
|
|
24
24
|
#promptSuggestions;
|
|
25
25
|
#promptHistory = null;
|
|
26
|
+
#promptHistoryImageSidecar;
|
|
26
27
|
#extensionBridge;
|
|
27
28
|
#stopped;
|
|
28
29
|
#resolveStopped;
|
|
@@ -102,7 +103,6 @@ export class OwnedUiSessionShell {
|
|
|
102
103
|
onEditorChange: () => { this.#editorRevision++; promptSuggestionController?.invalidate(); },
|
|
103
104
|
onPromptSuggestionAccepted: () => promptSuggestionController?.accept(),
|
|
104
105
|
onInputSurfaceChanged: () => {
|
|
105
|
-
this.root.clearViewportPointerState();
|
|
106
106
|
promptSuggestionController?.invalidate();
|
|
107
107
|
this.#promptHistory?.synchronize();
|
|
108
108
|
},
|
|
@@ -154,6 +154,7 @@ export class OwnedUiSessionShell {
|
|
|
154
154
|
root: this.root,
|
|
155
155
|
mode: tuiMode,
|
|
156
156
|
...(this.#customViewport ? {
|
|
157
|
+
onOverlayGeometry: surfaces => this.root.setViewportOverlaySurfaces(surfaces),
|
|
157
158
|
decorateTerminal: (terminal) => {
|
|
158
159
|
damageTerminal = new DamageAwareTerminalAdapter(terminal, {
|
|
159
160
|
regionalScroll: process.env.TERM !== "dumb",
|
|
@@ -194,8 +195,10 @@ export class OwnedUiSessionShell {
|
|
|
194
195
|
promptSuggestionController = promptSuggestionOptions === undefined ? null : new ContextualPromptSuggestionController({
|
|
195
196
|
generator: promptSuggestionOptions.generator,
|
|
196
197
|
enabled: promptSuggestionOptions.enabled(),
|
|
198
|
+
...(promptSuggestionOptions.diagnostics === undefined ? {} : { diagnostics: promptSuggestionOptions.diagnostics }),
|
|
197
199
|
surface: {
|
|
198
|
-
canPresent: identity => this.#
|
|
200
|
+
canPresent: identity => this.#promptSuggestionPresentationBlockReason(identity) === null,
|
|
201
|
+
presentationBlockReason: identity => this.#promptSuggestionPresentationBlockReason(identity),
|
|
199
202
|
present: text => {
|
|
200
203
|
if (!this.root.canPresentPromptSuggestion())
|
|
201
204
|
return false;
|
|
@@ -251,16 +254,13 @@ export class OwnedUiSessionShell {
|
|
|
251
254
|
this.runtime.requestRender();
|
|
252
255
|
return { consume: true };
|
|
253
256
|
}
|
|
254
|
-
// Invariant:
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (data.includes("\u001b[<"))
|
|
260
|
-
this.root.clearViewportPointerState();
|
|
261
|
-
return undefined;
|
|
257
|
+
// Invariant: geometry must belong to the painted frame, including newly opened/nested
|
|
258
|
+
// surfaces. Steady pointer input does not trigger a synchronous composition.
|
|
259
|
+
const viewport = this.runtime.viewport();
|
|
260
|
+
if (data.includes("\u001b[<") && !this.root.viewportInputGeometryReady(viewport.columns, viewport.rows)) {
|
|
261
|
+
this.runtime.renderNow();
|
|
262
262
|
}
|
|
263
|
-
const routed = this.root.handleViewportPreInput(data, true);
|
|
263
|
+
const routed = this.root.handleViewportPreInput(data, true, Date.now(), this.root.usesDefaultInputSurface() && !this.runtime.hasFocusedOverlay());
|
|
264
264
|
if (routed.copyText !== undefined) {
|
|
265
265
|
this.runtime.writeControl(`\u001b]52;c;${Buffer.from(routed.copyText, "utf8").toString("base64")}\u0007`);
|
|
266
266
|
}
|
|
@@ -329,6 +329,8 @@ export class OwnedUiSessionShell {
|
|
|
329
329
|
} },
|
|
330
330
|
});
|
|
331
331
|
if (this.#customViewport && options.promptHistory !== undefined) {
|
|
332
|
+
this.#promptHistoryImageSidecar = options.promptHistory.imageSidecar;
|
|
333
|
+
const sidecar = this.#promptHistoryImageSidecar;
|
|
332
334
|
this.#promptHistory = new PromptHistoryController({
|
|
333
335
|
editor: this.root.editor,
|
|
334
336
|
store: options.promptHistory.store,
|
|
@@ -336,6 +338,7 @@ export class OwnedUiSessionShell {
|
|
|
336
338
|
fallback: this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []),
|
|
337
339
|
active: () => this.root.usesDefaultInputSurface(),
|
|
338
340
|
render: () => this.runtime.requestRender(),
|
|
341
|
+
rehydrate: value => this.root.rehydrateHistoryText(value, id => sidecar?.readAttachment(id) ?? null),
|
|
339
342
|
});
|
|
340
343
|
}
|
|
341
344
|
this.#extensionBridge = createPiExtensionUiBridge({
|
|
@@ -385,19 +388,21 @@ export class OwnedUiSessionShell {
|
|
|
385
388
|
}
|
|
386
389
|
if (event.type === "assistant-message-completed") {
|
|
387
390
|
this.root.noteCompletedAssistantMessage();
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
391
|
+
const identity = {
|
|
392
|
+
sessionId: event.sessionId,
|
|
393
|
+
sessionGeneration: event.sessionGeneration,
|
|
394
|
+
runSequence: event.runSequence,
|
|
395
|
+
responseSequence: event.responseSequence,
|
|
396
|
+
model: event.model,
|
|
397
|
+
};
|
|
398
|
+
if (event.model === null)
|
|
399
|
+
this.#promptSuggestions?.skip(identity, "no-model");
|
|
400
|
+
else
|
|
401
|
+
this.#promptSuggestions?.consider({ ...identity, model: event.model }, !event.successful ? "failed-response"
|
|
402
|
+
: event.toolContinuation || event.stopReason === "toolUse" ? "tool-continuation"
|
|
403
|
+
: event.stopReason !== "stop" ? "incomplete-response"
|
|
404
|
+
: event.assistantMessageCount < 2 ? "early-conversation"
|
|
405
|
+
: this.root.promptSuggestionPrepareBlockReason());
|
|
401
406
|
}
|
|
402
407
|
const semanticOnly = event.type === "agent-run-started" || event.type === "assistant-message-completed";
|
|
403
408
|
const view = event.type === "transcript-block" && this.#sessionGeneration === this.backend.sessionGeneration
|
|
@@ -432,13 +437,14 @@ export class OwnedUiSessionShell {
|
|
|
432
437
|
view() {
|
|
433
438
|
return this.backend.view();
|
|
434
439
|
}
|
|
435
|
-
#
|
|
440
|
+
#promptSuggestionPresentationBlockReason(identity) {
|
|
436
441
|
const view = this.view();
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
+
if (this.#disposed)
|
|
443
|
+
return "disposed";
|
|
444
|
+
if (identity.sessionId !== view.sessionId || identity.sessionGeneration !== this.backend.sessionGeneration
|
|
445
|
+
|| modelKey(view) !== `${identity.model.providerId}/${identity.model.modelId}`)
|
|
446
|
+
return "stale-identity";
|
|
447
|
+
return this.root.promptSuggestionPresentationBlockReason();
|
|
442
448
|
}
|
|
443
449
|
damagePresentationDecision() {
|
|
444
450
|
return this.#damageTerminal?.lastDecision ?? null;
|
|
@@ -508,8 +514,21 @@ export class OwnedUiSessionShell {
|
|
|
508
514
|
const reusable = this.root.prepareHistoryText(text);
|
|
509
515
|
if (reusable.length === 0)
|
|
510
516
|
this.#promptHistory.rememberRecovery(text);
|
|
511
|
-
else
|
|
517
|
+
else {
|
|
518
|
+
this.#persistImageSidecars(text);
|
|
512
519
|
this.#promptHistory.capture(reusable, kind, this.#cwd, this.backend.sessionId);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// Rationale: write the image chip payloads referenced by an editor draft to the sidecar
|
|
524
|
+
// directory before their history row commits. Missing sidecar dependency = silent skip; the
|
|
525
|
+
// recall path treats an absent sidecar the same as a corrupt one.
|
|
526
|
+
#persistImageSidecars(text) {
|
|
527
|
+
const sidecar = this.#promptHistoryImageSidecar;
|
|
528
|
+
if (sidecar === undefined)
|
|
529
|
+
return;
|
|
530
|
+
for (const record of this.root.imageChipAttachmentsFromEditor(text)) {
|
|
531
|
+
sidecar.write(record.id, { tag: record.tag, data: record.image.data, mimeType: record.image.mimeType, savedAt: new Date().toISOString() });
|
|
513
532
|
}
|
|
514
533
|
}
|
|
515
534
|
async #submit(text) {
|
|
@@ -1304,6 +1323,7 @@ export class OwnedUiSessionShell {
|
|
|
1304
1323
|
// Invariant: delivery recovery invalidates callbacks, not same-session local recall or its draft.
|
|
1305
1324
|
if (this.#sessionBindingGeneration !== this.backend.sessionBindingGeneration) {
|
|
1306
1325
|
this.#sessionBindingGeneration = this.backend.sessionBindingGeneration;
|
|
1326
|
+
this.root.resetTranscript();
|
|
1307
1327
|
this.#promptHistory?.reset(view.transcript.flatMap(block => block.kind === "user" ? [block.text] : []));
|
|
1308
1328
|
}
|
|
1309
1329
|
this.#activeLoginDialog = undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type PiTuiPointerSurface } from "../tui-runtime/index.js";
|
|
1
2
|
import type { OwnedUiViewportSettings } from "../../../contracts/owned-ui/index.js";
|
|
2
3
|
import { type TranscriptViewportFrame, type TranscriptViewportFrameInput } from "../../../ui/components/index.js";
|
|
3
4
|
import type { PiShellEditorPort } from "../components/index.js";
|
|
@@ -38,6 +39,11 @@ export declare class SessionViewportController {
|
|
|
38
39
|
readonly rowStart: number;
|
|
39
40
|
readonly rowEnd: number;
|
|
40
41
|
} | undefined): void;
|
|
42
|
+
/** Replacement input changed before its new bounds have been painted. */
|
|
43
|
+
invalidateInputSurface(): void;
|
|
44
|
+
setInputSurfaceFrame(surface: PiTuiPointerSurface | undefined): void;
|
|
45
|
+
setOverlaySurfaces(surfaces: readonly PiTuiPointerSurface[] | null): void;
|
|
46
|
+
get inputGeometryReady(): boolean;
|
|
41
47
|
compose(input: Omit<TranscriptViewportFrameInput, "pointerPosition">): TranscriptViewportFrame;
|
|
42
48
|
composeDockOnly(dockRows: readonly string[], width: number, height: number): TranscriptViewportFrame | null;
|
|
43
49
|
/** Returns true when document wrapping may have changed. */
|
|
@@ -46,5 +52,5 @@ export declare class SessionViewportController {
|
|
|
46
52
|
noteCompletedAssistantMessage(): void;
|
|
47
53
|
reset(): void;
|
|
48
54
|
clearPointerState(): void;
|
|
49
|
-
handlePreInput(data: string, allowWheel?: boolean, now?: number): SessionViewportInputResult;
|
|
55
|
+
handlePreInput(data: string, allowWheel?: boolean, now?: number, editorActive?: boolean): SessionViewportInputResult;
|
|
50
56
|
}
|