@timurproko/a1 0.1.8-dev.290 → 0.1.8-dev.322
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 +21 -0
- package/bin/guardian.js +4 -3
- package/bin/ui.js +4 -2
- package/bin/update-recovery.js +3 -1
- package/dist/composition/owned-ui.js +13 -1
- package/dist/contracts/owned-ui/index.d.ts +1 -0
- package/dist/contracts/owned-ui/index.js +1 -0
- package/dist/contracts/owned-ui/prompt-history.d.ts +37 -0
- package/dist/contracts/owned-ui/prompt-history.js +23 -0
- package/dist/features/launch/profile-paths.d.ts +2 -0
- package/dist/features/launch/profile-paths.js +11 -0
- package/dist/features/prompt-history/index.d.ts +2 -0
- package/dist/features/prompt-history/index.js +2 -0
- package/dist/features/prompt-history/paths.d.ts +4 -0
- package/dist/features/prompt-history/paths.js +16 -0
- package/dist/features/prompt-history/service.d.ts +24 -0
- package/dist/features/prompt-history/service.js +260 -0
- package/dist/features/prompt-history/store.d.ts +15 -0
- package/dist/features/prompt-history/store.js +166 -0
- package/dist/features/prompt-history/worker.d.ts +9 -0
- package/dist/features/prompt-history/worker.js +42 -0
- package/dist/foundation/launch-context/index.d.ts +29 -0
- package/dist/foundation/launch-context/index.js +83 -0
- package/dist/foundation/launch-guardian/main.js +4 -3
- package/dist/foundation/release/bootstrap.d.ts +1 -1
- package/dist/foundation/release/bootstrap.js +18 -12
- package/dist/foundation/release/cohort-state.d.ts +1 -0
- package/dist/foundation/release/cohort-state.js +4 -0
- package/dist/foundation/release/release-gc.js +2 -2
- package/dist/foundation/release/release-store.js +10 -3
- package/dist/foundation/release/release.d.ts +3 -1
- package/dist/foundation/release/release.js +7 -3
- package/dist/foundation/release/restart-certification.d.ts +1 -0
- package/dist/foundation/release/restart-certification.js +5 -0
- package/dist/foundation/release/update-recovery.d.ts +2 -0
- package/dist/foundation/release/update-recovery.js +4 -0
- package/dist/foundation/release/update.js +5 -0
- package/dist/foundation/release/warmup.d.ts +1 -1
- package/dist/foundation/release/warmup.js +5 -4
- package/dist/foundation/startup/startup-runtime.js +13 -11
- package/dist/foundation/supervision/main.js +8 -7
- package/dist/integrations/pi/components/editor-interaction.d.ts +38 -0
- package/dist/integrations/pi/components/editor-interaction.js +1 -0
- package/dist/integrations/pi/components/history-editor-loader.d.ts +5 -0
- package/dist/integrations/pi/components/history-editor-loader.js +5 -0
- package/dist/integrations/pi/components/index.d.ts +1 -0
- package/dist/integrations/pi/components/index.js +1 -0
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +2 -1
- package/dist/integrations/pi/components/owned-editor-ux.js +95 -22
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +12 -1
- package/dist/integrations/pi/components/shell-presenters-info.d.ts +9 -2
- package/dist/integrations/pi/components/shell-presenters-info.js +35 -4
- package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +3 -2
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +8 -5
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +15 -2
- package/dist/integrations/pi/components/upstream/assets/earendil-image.json +12 -0
- package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +25 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +13 -9
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +137 -133
- package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +280 -0
- package/dist/integrations/pi/components/upstream/history/editor-core.js +2062 -0
- package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +32 -0
- package/dist/integrations/pi/components/upstream/history/kill-ring.js +48 -0
- package/dist/integrations/pi/components/upstream/history/printable-key.d.ts +1 -0
- package/dist/integrations/pi/components/upstream/history/printable-key.js +40 -0
- package/dist/integrations/pi/components/upstream/history/text-helpers.d.ts +14 -0
- package/dist/integrations/pi/components/upstream/history/text-helpers.js +28 -0
- package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +22 -0
- package/dist/integrations/pi/components/upstream/history/undo-stack.js +36 -0
- package/dist/integrations/pi/components/upstream/history/word-navigation.d.ts +24 -0
- package/dist/integrations/pi/components/upstream/history/word-navigation.js +100 -0
- package/dist/integrations/pi/engine/adapter.d.ts +13 -1
- package/dist/integrations/pi/engine/adapter.js +86 -25
- package/dist/integrations/pi/engine/changelog.d.ts +2 -0
- package/dist/integrations/pi/engine/changelog.js +63 -0
- package/dist/integrations/pi/engine/resources/changelog.json +12 -0
- package/dist/integrations/pi/engine/workflow-controllers.d.ts +1 -1
- package/dist/integrations/pi/engine/workflows.d.ts +1 -1
- package/dist/integrations/pi/session-ui/prompt-chips.d.ts +4 -1
- package/dist/integrations/pi/session-ui/prompt-chips.js +40 -13
- package/dist/integrations/pi/session-ui/prompt-history-controller.d.ts +30 -0
- package/dist/integrations/pi/session-ui/prompt-history-controller.js +147 -0
- package/dist/integrations/pi/session-ui/session-shell-root.d.ts +33 -0
- package/dist/integrations/pi/session-ui/session-shell-root.js +68 -74
- package/dist/integrations/pi/session-ui/session-shell.d.ts +1 -1
- package/dist/integrations/pi/session-ui/session-shell.js +98 -12
- package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +5 -0
- package/dist/integrations/pi/session-ui/session-viewport-controller.js +21 -15
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +7 -0
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +7 -6
- 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 +2 -2
- package/dist/product-identity.js +3 -36
- package/dist/product-identity.json +1 -22
- package/dist/ui/components/transcript-viewport.d.ts +8 -0
- package/dist/ui/components/transcript-viewport.js +23 -2
- package/dist/ui/components/visible-hyperlinks.d.ts +7 -0
- package/dist/ui/components/visible-hyperlinks.js +1 -0
- package/dist/ui/settings/declarations.js +19 -0
- package/dist/ui/settings/sections.js +11 -1
- package/dist/ui/settings/session.js +13 -4
- package/docs/architecture/boundaries.md +2 -0
- package/docs/architecture/history-editor-provenance.md +96 -0
- package/docs/architecture/internal-naming.md +89 -0
- package/docs/architecture/project-structure.md +1 -0
- package/docs/architecture/resource-and-data-policy.md +27 -1
- package/docs/architecture/toolchain.md +15 -1
- package/docs/features/prompt-history.md +149 -0
- package/package.json +8 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PROMPT_GLYPH, backgroundSgrSpan, caretCell, composeSubmittedPromptRows, displayWidth, faint, formatSubmittedPromptTime, heldNativeHyperlinkStyle, hyperlinkSgrSpan, nativeHyperlinkStyle, overlaySpan, progressStatusText, submittedPromptLayout, stripAnsi, } from "../../../ui/components/index.js";
|
|
2
2
|
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, renderPiShellPackageUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, } from "../components/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
4
|
import { PiTuiRuntimeAdapter, classifyPiTuiInput, } from "../tui-runtime/index.js";
|
|
5
5
|
import { PromptChipStore } from "./prompt-chips.js";
|
|
6
6
|
import { SessionViewportController } from "./session-viewport-controller.js";
|
|
@@ -45,7 +45,8 @@ export class OwnedUiSessionShellRoot {
|
|
|
45
45
|
#inputSurfaceCoordination = "editor";
|
|
46
46
|
#dockInputReuseEnabled;
|
|
47
47
|
#dockInputCandidate = false;
|
|
48
|
-
|
|
48
|
+
// Invariant: receipt-time eligibility is evidence, never authorization for composition-time reuse.
|
|
49
|
+
#dockInputRevision;
|
|
49
50
|
#visibleViewportSnapshot;
|
|
50
51
|
#fullViewportCompositions = 0;
|
|
51
52
|
#dockOnlyViewportCompositions = 0;
|
|
@@ -94,8 +95,9 @@ export class OwnedUiSessionShellRoot {
|
|
|
94
95
|
return "";
|
|
95
96
|
}
|
|
96
97
|
},
|
|
97
|
-
...(this.#customViewport ? { beginClipboardPaste: () => this.#promptChips.beginPaste(this.editor.getText(), signal => handlers.readClipboardContent?.(signal) ?? Promise.resolve(null), error => handlers.onPasteRejected?.(error)) } : {}),
|
|
98
|
+
...(this.#customViewport ? { beginClipboardPaste: () => this.#promptChips.beginPaste(this.editor.getText(), signal => handlers.readClipboardContent?.(signal) ?? Promise.resolve(null), error => handlers.onPasteRejected?.(error), () => handlers.requestRender()) } : {}),
|
|
98
99
|
editorAtomicRanges: line => this.#promptChips.atomicRanges(line),
|
|
100
|
+
editorHiddenRanges: line => this.#promptChips.hiddenRanges(line),
|
|
99
101
|
decorateEditorRow: (row, width) => {
|
|
100
102
|
const plain = stripAnsi(row);
|
|
101
103
|
const ranges = this.#promptChips.hyperlinkRanges(plain);
|
|
@@ -204,6 +206,9 @@ export class OwnedUiSessionShellRoot {
|
|
|
204
206
|
component.setImagePresentation(showImages, imageWidthCells);
|
|
205
207
|
this.#invalidateTranscriptPresentation();
|
|
206
208
|
}
|
|
209
|
+
prepareHistoryText(text) {
|
|
210
|
+
return this.#promptChips.prepareHistoryText(text);
|
|
211
|
+
}
|
|
207
212
|
preparePromptSubmission(text) {
|
|
208
213
|
return this.#promptChips.prepareSubmission(text);
|
|
209
214
|
}
|
|
@@ -286,13 +291,12 @@ export class OwnedUiSessionShellRoot {
|
|
|
286
291
|
const transientSignature = transientRowsSignature(steeringRows, statusRows);
|
|
287
292
|
const snapshot = this.#visibleViewportSnapshot;
|
|
288
293
|
const dockInputCandidate = this.#dockInputCandidate;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const scrollRows = [...document.rows, ...steeringRows, ...statusRows];
|
|
294
|
+
// Concurrency: input may change after a keyboard receipt but before its paint.
|
|
295
|
+
// Capture only the live inputs this composition will actually represent.
|
|
296
|
+
const viewportRevision = this.#viewportController.presentationRevision;
|
|
297
|
+
const selectionRevision = this.#viewportController.selectionRevision;
|
|
298
|
+
const pointerPosition = this.#viewportController.pointerPosition;
|
|
299
|
+
const inputSurface = this.#inputSurface;
|
|
296
300
|
const dockRows = dock.rows;
|
|
297
301
|
const selectableDocumentRowCount = document.rows.length;
|
|
298
302
|
const dockStartRow = height - dockRows.length + 1;
|
|
@@ -308,28 +312,27 @@ export class OwnedUiSessionShellRoot {
|
|
|
308
312
|
&& snapshot !== undefined
|
|
309
313
|
&& snapshot.width === width
|
|
310
314
|
&& snapshot.height === height
|
|
311
|
-
&& snapshot.documentRows ===
|
|
312
|
-
&& snapshot.transientSignature ===
|
|
313
|
-
&& snapshot.promptAnchors ===
|
|
315
|
+
&& snapshot.documentRows === document.rows
|
|
316
|
+
&& snapshot.transientSignature === transientSignature
|
|
317
|
+
&& snapshot.promptAnchors === document.promptAnchors
|
|
314
318
|
&& snapshot.dockLength === dockRows.length
|
|
315
|
-
&& snapshot.inputSurface ===
|
|
316
|
-
&& snapshot.viewportRevision ===
|
|
317
|
-
&& snapshot.selectionRevision ===
|
|
319
|
+
&& snapshot.inputSurface === inputSurface
|
|
320
|
+
&& snapshot.viewportRevision === viewportRevision
|
|
321
|
+
&& snapshot.selectionRevision === selectionRevision
|
|
318
322
|
? this.#viewportController.composeDockOnly(dockRows, width, height)
|
|
319
323
|
: null;
|
|
320
|
-
// Performance: a spinner tick or queue update can race the input-triggered
|
|
321
|
-
// render. Recompute the complete transient signature before reusing rows.
|
|
322
|
-
if (frame !== null && dockInputTransientSignature !== transientSignature)
|
|
323
|
-
frame = null;
|
|
324
324
|
if (frame === null) {
|
|
325
325
|
frame = this.#viewportController.compose({
|
|
326
|
-
|
|
326
|
+
// Performance: do not copy the complete document on unchanged dock-only input.
|
|
327
|
+
// Steering and Working retain their one non-selectable viewport-tail ownership.
|
|
328
|
+
documentRows: [...document.rows, ...steeringRows, ...statusRows],
|
|
327
329
|
...(this.#viewportController.transcriptPointerSelecting
|
|
328
330
|
? { paintDocumentRow: heldNativeHyperlinkStyle }
|
|
329
331
|
: {}),
|
|
330
332
|
// Invariant: selection and copying stop at the real document tail; Steering,
|
|
331
333
|
// fitting alignment, and live Working remain transient presentation chrome.
|
|
332
334
|
selectableDocumentRowCount,
|
|
335
|
+
...(document.liveTailStartRow === undefined ? {} : { liveTailStartRow: document.liveTailStartRow }),
|
|
333
336
|
bottomAlignedTailRowCount: statusRows.length,
|
|
334
337
|
dockRows,
|
|
335
338
|
promptAnchors: document.promptAnchors,
|
|
@@ -347,13 +350,16 @@ export class OwnedUiSessionShellRoot {
|
|
|
347
350
|
this.#visibleViewportSnapshot = {
|
|
348
351
|
width,
|
|
349
352
|
height,
|
|
350
|
-
documentRows:
|
|
353
|
+
documentRows: document.rows,
|
|
351
354
|
transientSignature,
|
|
352
355
|
promptAnchors: document.promptAnchors,
|
|
353
356
|
dockLength: dockRows.length,
|
|
354
|
-
inputSurface
|
|
355
|
-
|
|
356
|
-
|
|
357
|
+
inputSurface,
|
|
358
|
+
// Invariant: a callback during composition cannot bless older rows with its
|
|
359
|
+
// newer revision. Its ordinary pending render will publish the newer state.
|
|
360
|
+
viewportRevision,
|
|
361
|
+
selectionRevision: frame.descriptor.selectionRevision,
|
|
362
|
+
pointerPosition,
|
|
357
363
|
};
|
|
358
364
|
this.#onViewportFrame?.(frame);
|
|
359
365
|
return frame.rows;
|
|
@@ -361,6 +367,25 @@ export class OwnedUiSessionShellRoot {
|
|
|
361
367
|
viewportFrameDescriptor() {
|
|
362
368
|
return this.#viewportController.frame?.descriptor ?? null;
|
|
363
369
|
}
|
|
370
|
+
/** Read-only, payload-free evidence; observing a pending frame must never render or repair it. */
|
|
371
|
+
viewportPresentationEvidence() {
|
|
372
|
+
const frame = this.#viewportController.frame;
|
|
373
|
+
const snapshot = this.#visibleViewportSnapshot;
|
|
374
|
+
return {
|
|
375
|
+
candidate: this.#dockInputCandidate,
|
|
376
|
+
candidateRevision: this.#dockInputCandidate ? this.#dockInputRevision ?? null : null,
|
|
377
|
+
currentRevision: this.#viewportController.presentationRevision,
|
|
378
|
+
composedRevision: snapshot?.viewportRevision ?? null,
|
|
379
|
+
currentPointer: this.#viewportController.pointerPosition ?? null,
|
|
380
|
+
composedPointer: snapshot?.pointerPosition ?? null,
|
|
381
|
+
frameId: frame?.descriptor.frameId ?? null,
|
|
382
|
+
bottom: frame?.hits.bottom ?? null,
|
|
383
|
+
followingEnd: frame?.followingEnd ?? true,
|
|
384
|
+
scrollTop: frame?.scrollTop ?? 0,
|
|
385
|
+
maxScroll: frame?.maxScroll ?? 0,
|
|
386
|
+
cause: frame?.descriptor.cause ?? null,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
364
389
|
/** Visible non-selectable Steering, alignment, and Working rows, for rendering evidence. */
|
|
365
390
|
viewportTransientTailRowCount() {
|
|
366
391
|
return this.#viewportController.frame?.hits.transientTail.length ?? 0;
|
|
@@ -482,6 +507,10 @@ export class OwnedUiSessionShellRoot {
|
|
|
482
507
|
...resourceRows,
|
|
483
508
|
];
|
|
484
509
|
const promptAnchors = [];
|
|
510
|
+
// Rationale: a block that is still streaming re-renders every row it owns, so the frame
|
|
511
|
+
// reports where its rows begin. Damage-aware painting uses that to tell a live block's
|
|
512
|
+
// own reflow apart from a repaint that reaches settled rows.
|
|
513
|
+
let liveTailStartRow;
|
|
485
514
|
for (let index = 0; index < this.#transcriptOrder.length; index += 1) {
|
|
486
515
|
const id = this.#transcriptOrder[index];
|
|
487
516
|
const block = this.#blocksById.get(id);
|
|
@@ -504,6 +533,9 @@ export class OwnedUiSessionShellRoot {
|
|
|
504
533
|
rows.push("");
|
|
505
534
|
}
|
|
506
535
|
const firstRow = rows.length;
|
|
536
|
+
if (liveTailStartRow === undefined && block !== undefined && block.status !== "finalized") {
|
|
537
|
+
liveTailStartRow = firstRow;
|
|
538
|
+
}
|
|
507
539
|
rows.push(...blockRows);
|
|
508
540
|
if (block?.kind === "user" && blockRows[0] !== undefined) {
|
|
509
541
|
promptAnchors.push({
|
|
@@ -535,7 +567,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
535
567
|
text: diagnostic.message,
|
|
536
568
|
payload: {},
|
|
537
569
|
}, width, this.#cwd)));
|
|
538
|
-
const layout = { rows: Object.freeze(rows), promptAnchors: Object.freeze(promptAnchors) };
|
|
570
|
+
const layout = { rows: Object.freeze(rows), promptAnchors: Object.freeze(promptAnchors), liveTailStartRow };
|
|
539
571
|
if (this.#extensionHeader === null) {
|
|
540
572
|
this.#documentLayouts.set(width, layout);
|
|
541
573
|
// Performance: the custom viewport commonly probes full width and reserved-rail width.
|
|
@@ -603,7 +635,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
603
635
|
}
|
|
604
636
|
const id = this.#appendAnchoredWorkflowComponent(width => [
|
|
605
637
|
"",
|
|
606
|
-
...renderPiShellStatusText(this.#workflowStatusMessages.get(id) ?? message, width
|
|
638
|
+
...renderPiShellStatusText(this.#workflowStatusMessages.get(id) ?? message, width),
|
|
607
639
|
]);
|
|
608
640
|
this.#workflowStatusMessages.set(id, message);
|
|
609
641
|
this.#lastWorkflowStatusId = id;
|
|
@@ -614,10 +646,8 @@ export class OwnedUiSessionShellRoot {
|
|
|
614
646
|
return;
|
|
615
647
|
}
|
|
616
648
|
this.#lastWorkflowStatusId = undefined;
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
const text = prefix === undefined ? message.message : `${prefix}: ${message.message}`;
|
|
620
|
-
this.#appendAnchoredWorkflowComponent(() => ["", ` ${piTheme().fg(color, text)}`]);
|
|
649
|
+
const presentation = { kind: message.kind, message: message.message };
|
|
650
|
+
this.#appendAnchoredWorkflowComponent(width => renderPiShellCommandMessage(presentation, width, this.#outputPad));
|
|
621
651
|
}
|
|
622
652
|
appendWorkflowResult(result) {
|
|
623
653
|
if (result.messages !== undefined) {
|
|
@@ -639,7 +669,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
639
669
|
}
|
|
640
670
|
if (result.command === "hotkeys" && result.outcome === "completed") {
|
|
641
671
|
this.#lastWorkflowStatusId = undefined;
|
|
642
|
-
const hotkeys = createPiShellHotkeys();
|
|
672
|
+
const hotkeys = createPiShellHotkeys(this.editor.keybindingConfig(), bindings => this.#extensionRenderers.getShortcuts?.(bindings) ?? [], this.#customViewport ? "a1" : "pi");
|
|
643
673
|
this.#appendAnchoredWorkflowComponent(width => hotkeys.render(width), () => hotkeys.dispose?.());
|
|
644
674
|
return;
|
|
645
675
|
}
|
|
@@ -658,26 +688,10 @@ export class OwnedUiSessionShellRoot {
|
|
|
658
688
|
}
|
|
659
689
|
if (result.outcome === "completed" && (result.command === "quit" || result.command === "compact"))
|
|
660
690
|
return;
|
|
661
|
-
if (result.command === "new"
|
|
662
|
-
this.appendWorkflowMessage({ kind: "accent", message: result.message });
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
if (result.command === "name" && result.outcome === "completed") {
|
|
691
|
+
if (result.outcome === "completed" && (result.command === "new" || result.command === "name" || result.command === "debug")) {
|
|
666
692
|
this.#lastWorkflowStatusId = undefined;
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
"",
|
|
670
|
-
` ${piTheme().fg("dim", result.message)}`,
|
|
671
|
-
]);
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
if (result.command === "debug" && result.outcome === "completed") {
|
|
675
|
-
this.#lastWorkflowStatusId = undefined;
|
|
676
|
-
this.#appendAnchoredWorkflowComponent(() => [
|
|
677
|
-
"",
|
|
678
|
-
` ${piTheme().fg("accent", result.message)}`,
|
|
679
|
-
...(result.detail ? [` ${piTheme().fg("muted", result.detail)}`] : []),
|
|
680
|
-
]);
|
|
693
|
+
const presentation = { kind: result.command, message: result.message, ...(result.detail === undefined ? {} : { detail: result.detail }) };
|
|
694
|
+
this.#appendAnchoredWorkflowComponent(width => renderPiShellCommandMessage(presentation, width, this.#outputPad));
|
|
681
695
|
return;
|
|
682
696
|
}
|
|
683
697
|
if (result.command === "arminsayshi" && result.outcome === "completed") {
|
|
@@ -782,9 +796,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
782
796
|
this.appendWorkflowStatus(message);
|
|
783
797
|
return;
|
|
784
798
|
}
|
|
785
|
-
this
|
|
786
|
-
const prefix = type === "warning" ? "Warning" : "Error";
|
|
787
|
-
this.#appendAnchoredWorkflowComponent(() => ["", ` ${piTheme().fg(type, `${prefix}: ${message}`)}`]);
|
|
799
|
+
this.appendWorkflowMessage({ kind: type, message });
|
|
788
800
|
}
|
|
789
801
|
extensionFooterData() {
|
|
790
802
|
return {
|
|
@@ -826,8 +838,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
826
838
|
handleInput(data) {
|
|
827
839
|
this.#dockInputCandidate = this.#customViewport && this.#dockInputReuseEnabled
|
|
828
840
|
&& classifyPiTuiInput(data, this.#inputSurfaceCoordination) === "safe";
|
|
829
|
-
|
|
830
|
-
this.#captureDockInputSnapshot();
|
|
841
|
+
this.#dockInputRevision = this.#dockInputCandidate ? this.#viewportController.presentationRevision : undefined;
|
|
831
842
|
try {
|
|
832
843
|
this.#inputSurface.handleInput?.(data);
|
|
833
844
|
}
|
|
@@ -836,23 +847,6 @@ export class OwnedUiSessionShellRoot {
|
|
|
836
847
|
throw error;
|
|
837
848
|
}
|
|
838
849
|
}
|
|
839
|
-
#captureDockInputSnapshot() {
|
|
840
|
-
const width = Math.max(1, this.#componentRuntime.getColumns());
|
|
841
|
-
const steeringRows = this.#renderQueued(width);
|
|
842
|
-
const statusRows = this.#renderStatus(width);
|
|
843
|
-
const transientSignature = transientRowsSignature(steeringRows, statusRows);
|
|
844
|
-
const snapshot = this.#visibleViewportSnapshot;
|
|
845
|
-
this.#dockInputSnapshot = snapshot === undefined
|
|
846
|
-
? undefined
|
|
847
|
-
: {
|
|
848
|
-
documentRows: snapshot.documentRows,
|
|
849
|
-
transientSignature,
|
|
850
|
-
promptAnchors: snapshot.promptAnchors,
|
|
851
|
-
inputSurface: this.#inputSurface,
|
|
852
|
-
viewportRevision: this.#viewportController.presentationRevision,
|
|
853
|
-
selectionRevision: this.#viewportController.selectionRevision,
|
|
854
|
-
};
|
|
855
|
-
}
|
|
856
850
|
invalidate() {
|
|
857
851
|
this.header.invalidate();
|
|
858
852
|
this.resources.invalidate();
|
|
@@ -881,7 +875,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
881
875
|
this.#documentLayouts.clear();
|
|
882
876
|
this.#visibleViewportSnapshot = undefined;
|
|
883
877
|
this.#dockInputCandidate = false;
|
|
884
|
-
this.#
|
|
878
|
+
this.#dockInputRevision = undefined;
|
|
885
879
|
if (this.#inputSurface !== this.editor)
|
|
886
880
|
this.#inputSurface.dispose?.();
|
|
887
881
|
this.#extensionHeader?.dispose?.();
|
|
@@ -37,7 +37,7 @@ export declare class OwnedUiSessionShell {
|
|
|
37
37
|
showLoginMethodSelector(providerReference: string): void;
|
|
38
38
|
showTreeSelector(initialSelectedId?: string): void;
|
|
39
39
|
showLoginAuthTypeSelector(): void;
|
|
40
|
-
showLoginProviderSelector(authType
|
|
40
|
+
showLoginProviderSelector(authType?: "oauth" | "api_key", initialSearchInput?: string): void;
|
|
41
41
|
showSessionSelector(): void;
|
|
42
42
|
showSettingsSelector(): void;
|
|
43
43
|
shutdown(): Promise<AdapterCommandResult>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PromptHistoryController } from "./prompt-history-controller.js";
|
|
1
2
|
import { PRODUCT_TEXT } from "../../../product-identity.js";
|
|
2
3
|
import { boundedCleanup } from "../../../foundation/terminal-cleanup/index.js";
|
|
3
4
|
import { assertOwnedUiCommand, assertPromptImages, ImageAttachmentError } from "../../../contracts/owned-ui/index.js";
|
|
@@ -21,6 +22,7 @@ export class OwnedUiSessionShell {
|
|
|
21
22
|
#unsubscribe;
|
|
22
23
|
#unsubscribePromptSuggestions;
|
|
23
24
|
#promptSuggestions;
|
|
25
|
+
#promptHistory = null;
|
|
24
26
|
#extensionBridge;
|
|
25
27
|
#stopped;
|
|
26
28
|
#resolveStopped;
|
|
@@ -78,6 +80,8 @@ export class OwnedUiSessionShell {
|
|
|
78
80
|
replacementSurfaceActive: !this.root.usesDefaultInputSurface(),
|
|
79
81
|
}),
|
|
80
82
|
enableDockInputReuse: options.inputPresentation?.viewportReuse !== false,
|
|
83
|
+
persistentHistory: this.#customViewport && options.promptHistory !== undefined,
|
|
84
|
+
...(options.promptHistory === undefined ? {} : { historyEditor: options.promptHistory.editor }),
|
|
81
85
|
onSubmit: text => { void this.submit(text).catch(() => this.#reportSubmissionError()); },
|
|
82
86
|
onPasteRejected: error => this.#reportSubmissionError(error),
|
|
83
87
|
onInterrupt: () => { void this.interrupt(); },
|
|
@@ -98,6 +102,7 @@ export class OwnedUiSessionShell {
|
|
|
98
102
|
onInputSurfaceChanged: () => {
|
|
99
103
|
this.root.clearViewportPointerState();
|
|
100
104
|
promptSuggestionController?.invalidate();
|
|
105
|
+
this.#promptHistory?.synchronize();
|
|
101
106
|
},
|
|
102
107
|
onCopyText: text => {
|
|
103
108
|
runtime?.writeControl(`\u001b]52;c;${Buffer.from(text, "utf8").toString("base64")}\u0007`);
|
|
@@ -134,6 +139,7 @@ export class OwnedUiSessionShell {
|
|
|
134
139
|
}, this.backend.agentDir, {
|
|
135
140
|
getMessageRenderer: customType => this.backend.pinnedMessageRenderer(customType),
|
|
136
141
|
getToolDefinition: toolName => this.backend.pinnedToolDefinition(toolName),
|
|
142
|
+
getShortcuts: bindings => this.backend.pinnedShortcutDescriptions(bindings),
|
|
137
143
|
}, options.sessionLayout, {
|
|
138
144
|
resolve: assetId => this.backend.resolveTranscriptImage(assetId),
|
|
139
145
|
});
|
|
@@ -232,6 +238,17 @@ export class OwnedUiSessionShell {
|
|
|
232
238
|
? this.runtime.addPreInputListener(data => {
|
|
233
239
|
if (options.inputPresentation?.coordination === false)
|
|
234
240
|
this.#streamPresentation.noteImmediatePresentation();
|
|
241
|
+
// Compatibility: Pi's fullscreen renderer also intercepts plain Home/End.
|
|
242
|
+
// Deliver them to the focused owned input before that outer scroll handler;
|
|
243
|
+
// overlays retain Pi's normal dispatch, and comparison profiles never enter here.
|
|
244
|
+
if (!this.runtime.hasOverlay() && (this.root.editor.matchesTerminalKey(data, "home")
|
|
245
|
+
|| this.root.editor.matchesTerminalKey(data, "end"))) {
|
|
246
|
+
if (this.root.usesDefaultInputSurface())
|
|
247
|
+
this.root.handleViewportPreInput(data, true);
|
|
248
|
+
this.root.handleInput(data);
|
|
249
|
+
this.runtime.requestRender();
|
|
250
|
+
return { consume: true };
|
|
251
|
+
}
|
|
235
252
|
// Invariant: an overlay or editor-replacement screen owns its entire pointer
|
|
236
253
|
// surface. Letting the transcript pre-router inspect those reports
|
|
237
254
|
// steals settings value menus and numeric +/- controls before the
|
|
@@ -309,6 +326,17 @@ export class OwnedUiSessionShell {
|
|
|
309
326
|
this.root.setImagePresentation(this.#showImages, this.#imageWidthCells);
|
|
310
327
|
} },
|
|
311
328
|
});
|
|
329
|
+
if (this.#customViewport && options.promptHistory !== undefined) {
|
|
330
|
+
this.#promptHistory = new PromptHistoryController({
|
|
331
|
+
editor: this.root.editor,
|
|
332
|
+
store: options.promptHistory.store,
|
|
333
|
+
limit: options.promptHistory.limit,
|
|
334
|
+
fallback: this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []),
|
|
335
|
+
active: () => this.root.usesDefaultInputSurface(),
|
|
336
|
+
render: () => this.runtime.requestRender(),
|
|
337
|
+
failure: message => this.root.addExtensionNotification(message, "warning"),
|
|
338
|
+
});
|
|
339
|
+
}
|
|
312
340
|
this.#extensionBridge = createPiExtensionUiBridge({
|
|
313
341
|
runtime: {
|
|
314
342
|
getColumns: () => this.runtime.viewport().columns,
|
|
@@ -419,6 +447,7 @@ export class OwnedUiSessionShell {
|
|
|
419
447
|
return;
|
|
420
448
|
this.#started = true;
|
|
421
449
|
this.runtime.start();
|
|
450
|
+
this.#promptHistory?.start();
|
|
422
451
|
this.#syncTerminalProgress(this.view());
|
|
423
452
|
if (this.#customViewport)
|
|
424
453
|
this.#setPointerReporting(true);
|
|
@@ -472,6 +501,16 @@ export class OwnedUiSessionShell {
|
|
|
472
501
|
for (const item of this.#waitingImages.values())
|
|
473
502
|
item.controller.abort();
|
|
474
503
|
}
|
|
504
|
+
#rememberInput(text, kind) {
|
|
505
|
+
this.root.editor.addToHistory(text);
|
|
506
|
+
if (this.#promptHistory !== null) {
|
|
507
|
+
const reusable = this.root.prepareHistoryText(text);
|
|
508
|
+
if (reusable.length === 0)
|
|
509
|
+
this.#promptHistory.rememberRecovery(text);
|
|
510
|
+
else
|
|
511
|
+
this.#promptHistory.capture(reusable, kind, this.#cwd, this.backend.sessionId);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
475
514
|
async #submit(text) {
|
|
476
515
|
this.#promptSuggestions?.invalidate();
|
|
477
516
|
const displayInput = text.trim();
|
|
@@ -486,7 +525,7 @@ export class OwnedUiSessionShell {
|
|
|
486
525
|
const excludeFromContext = input.startsWith("!!");
|
|
487
526
|
const command = input.slice(excludeFromContext ? 2 : 1).trim();
|
|
488
527
|
if (command) {
|
|
489
|
-
this
|
|
528
|
+
this.#rememberInput(displayInput, "bash");
|
|
490
529
|
try {
|
|
491
530
|
const result = await this.backend.executeBashWorkflow(command, excludeFromContext);
|
|
492
531
|
const workflow = {
|
|
@@ -508,7 +547,7 @@ export class OwnedUiSessionShell {
|
|
|
508
547
|
}
|
|
509
548
|
}
|
|
510
549
|
if (this.view().status.workingMessage?.startsWith("Compacting") === true) {
|
|
511
|
-
this
|
|
550
|
+
this.#rememberInput(displayInput, "steer");
|
|
512
551
|
this.#compactionQueue.push({
|
|
513
552
|
text: input,
|
|
514
553
|
draft: displayInput,
|
|
@@ -520,7 +559,7 @@ export class OwnedUiSessionShell {
|
|
|
520
559
|
return { outcome: "completed", diagnostic: null };
|
|
521
560
|
}
|
|
522
561
|
const type = this.view().lifecycle === "busy" ? "steer" : "prompt";
|
|
523
|
-
this
|
|
562
|
+
this.#rememberInput(displayInput, type);
|
|
524
563
|
this.root.resumeViewportFollowing();
|
|
525
564
|
return this.#execute({
|
|
526
565
|
type,
|
|
@@ -633,7 +672,7 @@ export class OwnedUiSessionShell {
|
|
|
633
672
|
const prepared = this.root.preparePromptSubmission(displayInput);
|
|
634
673
|
assertPromptImages(prepared.images);
|
|
635
674
|
const text = prepared.text.trim();
|
|
636
|
-
this
|
|
675
|
+
this.#rememberInput(displayInput, "follow-up");
|
|
637
676
|
if (this.root.editor.getText() === draft)
|
|
638
677
|
this.root.editor.setText("");
|
|
639
678
|
this.root.resumeViewportFollowing();
|
|
@@ -766,8 +805,7 @@ export class OwnedUiSessionShell {
|
|
|
766
805
|
showLoginMethodSelector(providerReference) {
|
|
767
806
|
const method = this.backend.pinnedLoginMethodOptions(providerReference);
|
|
768
807
|
if (method.options.length === 0) {
|
|
769
|
-
this.
|
|
770
|
-
this.runtime.requestRender();
|
|
808
|
+
this.showLoginProviderSelector(undefined, providerReference);
|
|
771
809
|
return;
|
|
772
810
|
}
|
|
773
811
|
if (method.options.length === 1) {
|
|
@@ -809,6 +847,20 @@ export class OwnedUiSessionShell {
|
|
|
809
847
|
initialFilterMode: context.filterMode,
|
|
810
848
|
...(initialSelectedId === undefined ? {} : { initialSelectedId }),
|
|
811
849
|
onLabelChange: context.appendLabelChange,
|
|
850
|
+
onCopy: text => {
|
|
851
|
+
if (!text) {
|
|
852
|
+
this.root.appendWorkflowResult({ command: "tree", outcome: "failed", message: "Selected entry has no text to copy" });
|
|
853
|
+
this.runtime.requestRender();
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
void this.backend.copyWorkflowText(text).then(() => {
|
|
857
|
+
this.root.appendWorkflowStatus("Copied selected message to clipboard");
|
|
858
|
+
this.runtime.requestRender();
|
|
859
|
+
}).catch(error => {
|
|
860
|
+
this.root.appendWorkflowResult({ command: "tree", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
|
|
861
|
+
this.runtime.requestRender();
|
|
862
|
+
});
|
|
863
|
+
},
|
|
812
864
|
onCancel: close,
|
|
813
865
|
onSelect: entryId => {
|
|
814
866
|
close();
|
|
@@ -836,10 +888,10 @@ export class OwnedUiSessionShell {
|
|
|
836
888
|
this.root.setInputSurface(component);
|
|
837
889
|
this.runtime.requestRender();
|
|
838
890
|
}
|
|
839
|
-
showLoginProviderSelector(authType) {
|
|
891
|
+
showLoginProviderSelector(authType, initialSearchInput) {
|
|
840
892
|
const options = this.backend.pinnedLoginOptions(authType);
|
|
841
893
|
if (options.length === 0) {
|
|
842
|
-
this.root.appendWorkflowStatus(authType === "oauth" ? "No subscription providers available." : "No API key providers available.");
|
|
894
|
+
this.root.appendWorkflowStatus(authType === "oauth" ? "No subscription providers available." : authType === "api_key" ? "No API key providers available." : "No login providers available.");
|
|
843
895
|
this.runtime.requestRender();
|
|
844
896
|
return;
|
|
845
897
|
}
|
|
@@ -852,8 +904,9 @@ export class OwnedUiSessionShell {
|
|
|
852
904
|
void this.runWorkflow({ command: "login", argument: "", selection: id });
|
|
853
905
|
}, () => {
|
|
854
906
|
close();
|
|
855
|
-
|
|
856
|
-
|
|
907
|
+
if (authType !== undefined)
|
|
908
|
+
this.showLoginAuthTypeSelector();
|
|
909
|
+
}, initialSearchInput);
|
|
857
910
|
this.root.setInputSurface(component);
|
|
858
911
|
this.runtime.requestRender();
|
|
859
912
|
}
|
|
@@ -934,6 +987,24 @@ export class OwnedUiSessionShell {
|
|
|
934
987
|
return this.runWorkflow({ command: "quit", argument: "" });
|
|
935
988
|
}
|
|
936
989
|
async runWorkflow(request) {
|
|
990
|
+
if (request.command === "login" && request.selection !== undefined) {
|
|
991
|
+
const setup = this.backend.pinnedAmbientAuthentication(request.selection);
|
|
992
|
+
if (setup) {
|
|
993
|
+
const close = () => {
|
|
994
|
+
this.root.setInputSurface(null);
|
|
995
|
+
this.runtime.requestRender();
|
|
996
|
+
};
|
|
997
|
+
const dialog = createPiShellLoginDialog({
|
|
998
|
+
getColumns: () => this.runtime.viewport().columns,
|
|
999
|
+
getRows: () => this.runtime.viewport().rows,
|
|
1000
|
+
requestRender: () => this.runtime.requestRender(),
|
|
1001
|
+
}, setup.providerId, close, setup.providerName, setup.title);
|
|
1002
|
+
dialog.showInfo(setup.message, [], true);
|
|
1003
|
+
this.root.setInputSurface(dialog);
|
|
1004
|
+
this.runtime.requestRender();
|
|
1005
|
+
return { outcome: "completed", diagnostic: null };
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
937
1008
|
if (request.command === "scoped-models" && request.selection === undefined && request.confirmed === undefined) {
|
|
938
1009
|
this.showScopedModelsSelector();
|
|
939
1010
|
return { outcome: "completed", diagnostic: null };
|
|
@@ -974,8 +1045,13 @@ export class OwnedUiSessionShell {
|
|
|
974
1045
|
return { outcome: "completed", diagnostic: null };
|
|
975
1046
|
}
|
|
976
1047
|
if (request.command === "reload") {
|
|
1048
|
+
const blocked = this.backend.reloadBlockedResult();
|
|
1049
|
+
if (blocked) {
|
|
1050
|
+
this.root.appendWorkflowResult(blocked);
|
|
1051
|
+
this.runtime.requestRender();
|
|
1052
|
+
return workflowAdapterResult(blocked);
|
|
1053
|
+
}
|
|
977
1054
|
this.root.resetExtensionUi();
|
|
978
|
-
this.root.resetWorkflowPresentation();
|
|
979
1055
|
}
|
|
980
1056
|
const shareSurface = request.command === "share"
|
|
981
1057
|
? createPiShellOperationLoader({
|
|
@@ -1026,6 +1102,8 @@ export class OwnedUiSessionShell {
|
|
|
1026
1102
|
return { outcome: "failed", diagnostic: `Owned controller missing for ${request.command}` };
|
|
1027
1103
|
}
|
|
1028
1104
|
if (request.command === "reload" && result.outcome === "completed") {
|
|
1105
|
+
this.root.resetWorkflowPresentation();
|
|
1106
|
+
this.root.editor.reloadKeybindings();
|
|
1029
1107
|
this.root.editor.setAutocompleteCommands(this.backend.workflowAutocompleteCommands());
|
|
1030
1108
|
}
|
|
1031
1109
|
this.root.appendWorkflowResult(result);
|
|
@@ -1157,6 +1235,8 @@ export class OwnedUiSessionShell {
|
|
|
1157
1235
|
failures.push(error);
|
|
1158
1236
|
} };
|
|
1159
1237
|
let pasteCleanup = Promise.resolve();
|
|
1238
|
+
let historyCleanup = Promise.resolve(true);
|
|
1239
|
+
attempt(() => { historyCleanup = this.#promptHistory?.close() ?? Promise.resolve(true); });
|
|
1160
1240
|
attempt(() => { pasteCleanup = this.root.disposePendingPastes(); });
|
|
1161
1241
|
attempt(() => this.root.clearViewportPointerState());
|
|
1162
1242
|
attempt(() => this.#setPointerReporting(false, true));
|
|
@@ -1182,6 +1262,9 @@ export class OwnedUiSessionShell {
|
|
|
1182
1262
|
attempt(() => this.#extensionBridge.dispose());
|
|
1183
1263
|
// Invariant: terminal restoration precedes any potentially stalled backend teardown.
|
|
1184
1264
|
await this.runtime.dispose().catch(error => failures.push(error));
|
|
1265
|
+
const historySaved = await historyCleanup.catch(() => false);
|
|
1266
|
+
if (!historySaved)
|
|
1267
|
+
this.runtime.writeAfterStop("Prompt history could not finish saving before exit.\n");
|
|
1185
1268
|
await boundedCleanup(() => pasteCleanup).catch(error => failures.push(error));
|
|
1186
1269
|
await boundedCleanup(() => this.backend.unbindExtensionUi()).catch(error => failures.push(error));
|
|
1187
1270
|
if (failures.length > 0)
|
|
@@ -1219,6 +1302,7 @@ export class OwnedUiSessionShell {
|
|
|
1219
1302
|
this.root.resetPendingPastes();
|
|
1220
1303
|
this.#promptSuggestions?.invalidate();
|
|
1221
1304
|
this.#sessionGeneration = this.backend.sessionGeneration;
|
|
1305
|
+
this.#promptHistory?.reset(view.transcript.flatMap(block => block.kind === "user" ? [block.text] : []));
|
|
1222
1306
|
this.#activeLoginDialog = undefined;
|
|
1223
1307
|
this.#extensionBridge.reset();
|
|
1224
1308
|
// Invariant: a replaced session takes its transient viewport and owned-route state with it.
|
|
@@ -1377,7 +1461,7 @@ export class OwnedUiSessionShell {
|
|
|
1377
1461
|
if (isWorkflowRoute(name))
|
|
1378
1462
|
return this.runWorkflow({ command: name, argument });
|
|
1379
1463
|
// Compatibility: unknown slash input, prompt templates, skills, and extension commands remain Pi prompt input.
|
|
1380
|
-
this
|
|
1464
|
+
this.#rememberInput(text, "slash");
|
|
1381
1465
|
this.root.resumeViewportFollowing();
|
|
1382
1466
|
return this.#execute({
|
|
1383
1467
|
type: this.view().lifecycle === "busy" ? "steer" : "prompt",
|
|
@@ -1523,6 +1607,7 @@ export class OwnedUiSessionShell {
|
|
|
1523
1607
|
catch {
|
|
1524
1608
|
// Security: dispatch might already have reached the provider. Never retry automatically.
|
|
1525
1609
|
this.root.editor.addToHistory(draft);
|
|
1610
|
+
this.#promptHistory?.rememberRecovery(draft);
|
|
1526
1611
|
this.#reportSubmissionError(undefined, "Submission failed; delivery is uncertain. Check the conversation before retrying. Press Up to recover the draft.");
|
|
1527
1612
|
return { outcome: "failed", diagnostic: "submission delivery is uncertain" };
|
|
1528
1613
|
}
|
|
@@ -1540,6 +1625,7 @@ export class OwnedUiSessionShell {
|
|
|
1540
1625
|
}
|
|
1541
1626
|
#recoverSubmission(draft, revision, error) {
|
|
1542
1627
|
this.root.editor.addToHistory(draft);
|
|
1628
|
+
this.#promptHistory?.rememberRecovery(draft);
|
|
1543
1629
|
// Concurrency: never overwrite input typed (even typed and cleared) after this submission.
|
|
1544
1630
|
if (revision === this.#editorRevision && this.root.editor.getText().length === 0)
|
|
1545
1631
|
this.root.editor.setText(draft);
|
|
@@ -28,6 +28,11 @@ export declare class SessionViewportController {
|
|
|
28
28
|
get hasSelection(): boolean;
|
|
29
29
|
get selectionRevision(): number;
|
|
30
30
|
get presentationRevision(): number;
|
|
31
|
+
/** Latest reported coordinates; exposed without rendering for frame provenance and evidence. */
|
|
32
|
+
get pointerPosition(): {
|
|
33
|
+
readonly column: number;
|
|
34
|
+
readonly row: number;
|
|
35
|
+
} | undefined;
|
|
31
36
|
get frame(): TranscriptViewportFrame | null;
|
|
32
37
|
setEditorPointerFrame(frame: {
|
|
33
38
|
readonly rowStart: number;
|
|
@@ -60,6 +60,10 @@ export class SessionViewportController {
|
|
|
60
60
|
get presentationRevision() {
|
|
61
61
|
return this.#presentationRevision;
|
|
62
62
|
}
|
|
63
|
+
/** Latest reported coordinates; exposed without rendering for frame provenance and evidence. */
|
|
64
|
+
get pointerPosition() {
|
|
65
|
+
return this.#pointerPosition;
|
|
66
|
+
}
|
|
63
67
|
get frame() {
|
|
64
68
|
return this.#viewport.frame;
|
|
65
69
|
}
|
|
@@ -159,6 +163,23 @@ export class SessionViewportController {
|
|
|
159
163
|
this.#requestHyperlinkCleanup();
|
|
160
164
|
this.#requestRender(true);
|
|
161
165
|
}
|
|
166
|
+
// Invariant: content shortcuts never reach the prompt, even before the first frame
|
|
167
|
+
// or when scrolling is a no-op. Plain Home/End remain editor line navigation.
|
|
168
|
+
if (allowWheel && this.#editor.matchesTerminalKey(data, "ctrl+home")) {
|
|
169
|
+
if (this.#viewport.scrollTo(0, now)) {
|
|
170
|
+
this.#scheduleActivityExpiry();
|
|
171
|
+
this.#requestRender();
|
|
172
|
+
}
|
|
173
|
+
return { data: "", consumed: true };
|
|
174
|
+
}
|
|
175
|
+
if (allowWheel && this.#editor.matchesTerminalKey(data, "ctrl+end")) {
|
|
176
|
+
if (!this.#viewport.followingEnd) {
|
|
177
|
+
this.#viewport.scrollToEnd(now);
|
|
178
|
+
this.#scheduleActivityExpiry();
|
|
179
|
+
this.#requestRender();
|
|
180
|
+
}
|
|
181
|
+
return { data: "", consumed: true };
|
|
182
|
+
}
|
|
162
183
|
if (this.#viewport.frame === null) {
|
|
163
184
|
// Invariant: a not-yet-painted A1 screen is not Pi's selection surface.
|
|
164
185
|
return routeMouseInput(data, event => {
|
|
@@ -183,21 +204,6 @@ export class SessionViewportController {
|
|
|
183
204
|
}
|
|
184
205
|
}
|
|
185
206
|
}
|
|
186
|
-
if (allowWheel && this.#editor.matchesTerminalKey(data, "home")) {
|
|
187
|
-
if (this.#viewport.scrollTo(0, now)) {
|
|
188
|
-
this.#scheduleActivityExpiry();
|
|
189
|
-
this.#requestRender();
|
|
190
|
-
}
|
|
191
|
-
return { data: "", consumed: true };
|
|
192
|
-
}
|
|
193
|
-
if (allowWheel && this.#editor.matchesTerminalKey(data, "end")) {
|
|
194
|
-
if (!this.#viewport.followingEnd) {
|
|
195
|
-
this.#viewport.scrollToEnd(now);
|
|
196
|
-
this.#scheduleActivityExpiry();
|
|
197
|
-
this.#requestRender();
|
|
198
|
-
}
|
|
199
|
-
return { data: "", consumed: true };
|
|
200
|
-
}
|
|
201
207
|
if (allowWheel && (SHIFT_UP_INPUTS.has(data) || SHIFT_DOWN_INPUTS.has(data))) {
|
|
202
208
|
const scrolled = SHIFT_UP_INPUTS.has(data)
|
|
203
209
|
? this.#viewport.scrollToPreviousPrompt(now)
|