@timurproko/a1 0.2.7-dev.1255 → 0.2.7-dev.1264
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/dist/app/session-shell/prompt-submission.d.ts +1 -1
- package/dist/app/session-shell/prompt-submission.js +9 -9
- package/dist/app/session-shell/resource-labels.d.ts +2 -0
- package/dist/app/session-shell/resource-labels.js +158 -0
- package/dist/app/session-shell/session-prompt-history.d.ts +29 -0
- package/dist/app/session-shell/session-prompt-history.js +80 -0
- package/dist/app/session-shell/session-selectors.d.ts +46 -0
- package/dist/app/session-shell/session-selectors.js +269 -0
- package/dist/app/session-shell/session-shell-root.d.ts +6 -28
- package/dist/app/session-shell/session-shell-root.js +26 -220
- package/dist/app/session-shell/session-shell.d.ts +3 -23
- package/dist/app/session-shell/session-shell.js +54 -365
- package/dist/app/session-shell/session-viewport-controller.d.ts +2 -10
- package/dist/app/session-shell/session-viewport-controller.js +40 -318
- package/dist/app/session-shell/settings-bindings.d.ts +1 -1
- package/dist/app/session-shell/settings-bindings.js +2 -2
- package/dist/app/session-shell/viewport-pointer.d.ts +83 -0
- package/dist/app/session-shell/viewport-pointer.js +361 -0
- package/dist/composition/owned-ui.js +63 -59
- package/dist/contracts/owned-ui/index.d.ts +1 -1
- package/dist/contracts/owned-ui/index.js +1 -1
- package/dist/contracts/owned-ui/validation.d.ts +1 -11
- package/dist/contracts/owned-ui/validation.js +8 -104
- package/dist/contracts/presentation/index.d.ts +1 -1
- package/dist/contracts/presentation/terminal.d.ts +0 -6
- package/dist/features/owned-ui/settings-app.js +13 -208
- package/dist/features/owned-ui/settings-model-dialog.d.ts +41 -0
- package/dist/features/owned-ui/settings-model-dialog.js +194 -0
- package/dist/foundation/release/automatic-update.js +1 -8
- package/dist/foundation/release/dependency-layer.js +3 -3
- package/dist/foundation/release/latest-release.js +1 -1
- package/dist/foundation/release/managed-path.d.ts +10 -0
- package/dist/foundation/release/managed-path.js +26 -0
- package/dist/foundation/release/release-gc.js +6 -8
- package/dist/foundation/release/restart-certification.js +3 -4
- package/dist/foundation/release/update-activation.js +1 -1
- package/dist/foundation/release/update-execution-lease.js +1 -7
- package/dist/foundation/release/update-installer.d.ts +45 -0
- package/dist/foundation/release/update-installer.js +57 -0
- package/dist/foundation/release/update-recovery.js +2 -9
- package/dist/foundation/release/update.d.ts +4 -23
- package/dist/foundation/release/update.js +160 -179
- package/dist/integrations/pi/components/bracketed-paste.d.ts +33 -0
- package/dist/integrations/pi/components/bracketed-paste.js +123 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +46 -140
- package/dist/integrations/pi/engine/adapter.d.ts +1 -8
- package/dist/integrations/pi/engine/adapter.js +6 -49
- package/dist/integrations/pi/engine/host.js +1 -1
- package/dist/integrations/pi/engine/repository-metadata.d.ts +49 -0
- package/dist/integrations/pi/engine/repository-metadata.js +189 -0
- package/dist/integrations/pi/engine/session-runtime.d.ts +6 -12
- package/dist/integrations/pi/engine/session-runtime.js +21 -178
- package/dist/integrations/pi/engine/view-state.d.ts +21 -2
- package/dist/integrations/pi/engine/view-state.js +129 -1
- package/dist/integrations/pi/engine/workflow-runner.d.ts +0 -1
- package/dist/integrations/pi/engine/workflow-runner.js +254 -365
- package/dist/integrations/pi/engine/workflow-share.d.ts +9 -0
- package/dist/integrations/pi/engine/workflow-share.js +168 -0
- package/dist/integrations/pi/engine/workflow-support.d.ts +0 -14
- package/dist/integrations/pi/engine/workflow-support.js +0 -47
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +9 -19
- package/dist/integrations/pi/tui-runtime/adapter.js +35 -144
- package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +4 -4
- package/dist/integrations/pi/tui-runtime/index.d.ts +0 -1
- package/dist/integrations/pi/tui-runtime/overlay-geometry.d.ts +2 -2
- package/dist/integrations/pi/tui-runtime/presentation-adapter.d.ts +2 -2
- package/dist/ui/components/transcript-viewport-frame.d.ts +120 -0
- package/dist/ui/components/transcript-viewport-frame.js +314 -0
- package/dist/ui/components/transcript-viewport-types.d.ts +147 -0
- package/dist/ui/components/transcript-viewport.d.ts +2 -147
- package/dist/ui/components/transcript-viewport.js +87 -315
- package/package.json +3 -3
- package/dist/integrations/pi/engine/diagnostics-state.d.ts +0 -9
- package/dist/integrations/pi/engine/diagnostics-state.js +0 -34
- package/dist/integrations/pi/engine/usage-view.d.ts +0 -4
- package/dist/integrations/pi/engine/usage-view.js +0 -56
- package/dist/integrations/pi/tui-runtime/contracts.d.ts +0 -22
- /package/dist/{integrations/pi/tui-runtime/contracts.js → ui/components/transcript-viewport-types.js} +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { ShellClipboard } from "./shell-clipboard.js";
|
|
2
|
-
import {
|
|
2
|
+
import { SessionPromptHistory } from "./session-prompt-history.js";
|
|
3
|
+
import { shellResourceEntries } from "./resource-labels.js";
|
|
3
4
|
import { SessionDialogCoordinator } from "./dialog-coordinator.js";
|
|
4
5
|
import { LoginInteraction } from "./login-interaction.js";
|
|
5
6
|
import { PromptSubmission } from "./prompt-submission.js";
|
|
6
7
|
import { SessionConcerns } from "./session-concern.js";
|
|
7
8
|
import { composeExitText, SessionStopLatch } from "./session-lifecycle.js";
|
|
8
9
|
import { openWorkflowSelector } from "./workflow-router.js";
|
|
10
|
+
import { SessionSelectors } from "./session-selectors.js";
|
|
9
11
|
import { bindSessionSettings, bindShellSettings } from "./settings-bindings.js";
|
|
10
12
|
import { promptSuggestionSurface, suggestionPresentationBlockReason, } from "./suggestion-surface.js";
|
|
11
13
|
import { CLEAR_EXIT_INTERVAL_MS, boundedCleanup } from "../../foundation/terminal-cleanup/terminal-reset.js";
|
|
@@ -14,7 +16,7 @@ import { copyAcknowledgementText } from "../../ui/components/selection-copy.js";
|
|
|
14
16
|
import { ShellPromptSuggestions } from "./shell-prompt-suggestions.js";
|
|
15
17
|
import { OwnedUiTerminalHost, componentRuntime, sessionTerminalHostOptions, } from "./shell-host.js";
|
|
16
18
|
import { STREAM_PRESENTATION_INTERVAL_MS, StreamPresentationCoalescer, } from "./stream-presentation-coalescer.js";
|
|
17
|
-
import { OwnedUiSessionShellRoot,
|
|
19
|
+
import { OwnedUiSessionShellRoot, } from "./session-shell-root.js";
|
|
18
20
|
// Invariant: a disposed shell leaves the engine a host that holds no reference back to it.
|
|
19
21
|
const DETACHED_WORKFLOW_INTERACTION = { prompt: async () => null, notify() { } };
|
|
20
22
|
export { OwnedUiSessionShellRoot, } from "./session-shell-root.js";
|
|
@@ -27,21 +29,19 @@ export { formatSessionResumeCommand, quoteCommandArgument } from "./session-life
|
|
|
27
29
|
export class OwnedUiSessionPresenter {
|
|
28
30
|
backend;
|
|
29
31
|
#presenters;
|
|
30
|
-
|
|
32
|
+
selectors;
|
|
31
33
|
root;
|
|
32
34
|
#host;
|
|
33
35
|
#terminal;
|
|
34
36
|
#submission;
|
|
35
37
|
#login;
|
|
36
38
|
#concerns;
|
|
37
|
-
#listeners = new Set();
|
|
38
39
|
#unsubscribe;
|
|
39
40
|
#promptSuggestions;
|
|
40
41
|
#promptImages;
|
|
41
42
|
#skills;
|
|
42
43
|
#installedCommandSignature = "";
|
|
43
|
-
#
|
|
44
|
-
#historyBinding;
|
|
44
|
+
#history = null;
|
|
45
45
|
#extensionBridge;
|
|
46
46
|
#stop = new SessionStopLatch();
|
|
47
47
|
#dialogs;
|
|
@@ -85,8 +85,6 @@ export class OwnedUiSessionPresenter {
|
|
|
85
85
|
this.#inputCoordination = inputPresentation?.coordination !== false;
|
|
86
86
|
this.#host = host;
|
|
87
87
|
this.#terminal = host.connect(this);
|
|
88
|
-
// Invariant: the host's optional members follow the layout, so it is built only once the layout is known.
|
|
89
|
-
this.#dialogHost = this.#createDialogHost();
|
|
90
88
|
// Invariant: owned prompt limits and collapsed skills are bare-A1 replacements; comparison profiles keep pinned behavior.
|
|
91
89
|
this.#promptImages = this.#customViewport ? options.promptImages ?? null : null;
|
|
92
90
|
this.#skills = this.#customViewport ? options.skills ?? null : null;
|
|
@@ -124,7 +122,7 @@ export class OwnedUiSessionPresenter {
|
|
|
124
122
|
onInterrupt: () => { void this.interrupt(); },
|
|
125
123
|
onClear: () => { void this.clearOrExit(); },
|
|
126
124
|
onExit: () => { void this.shutdown(); },
|
|
127
|
-
onModelSelect: () => this.#customViewport ? this.showModelsDialog() : this.showModelSelector(),
|
|
125
|
+
onModelSelect: () => this.#customViewport ? this.selectors.showModelsDialog() : this.selectors.showModelSelector(),
|
|
128
126
|
onModelCycle: direction => { void this.cycleModel(direction); },
|
|
129
127
|
onThinkingCycle: () => { void this.cycleThinkingLevel(); },
|
|
130
128
|
onThinkingToggle: () => {
|
|
@@ -150,7 +148,7 @@ export class OwnedUiSessionPresenter {
|
|
|
150
148
|
},
|
|
151
149
|
onInputSurfaceChanged: () => {
|
|
152
150
|
this.#promptSuggestions?.invalidate();
|
|
153
|
-
this.#
|
|
151
|
+
this.#history?.synchronize();
|
|
154
152
|
},
|
|
155
153
|
onCopyText: (text, cut) => {
|
|
156
154
|
const delivery = this.#clipboard.copyText(text);
|
|
@@ -183,6 +181,7 @@ export class OwnedUiSessionPresenter {
|
|
|
183
181
|
...(startupRoute === undefined ? {} : { startupRoute }),
|
|
184
182
|
});
|
|
185
183
|
constructed.push(() => this.#dialogs.dispose());
|
|
184
|
+
this.selectors = this.#createSelectors(options.presenters);
|
|
186
185
|
const initialPiSettings = this.backend.settings.snapshot();
|
|
187
186
|
this.#programStatus = options.presenters.components.chrome.programStatusReporter(status => this.#terminal.setProgramStatus(status), () => this.view().status.footer?.sessionName ?? undefined);
|
|
188
187
|
this.#login = new LoginInteraction({
|
|
@@ -224,17 +223,8 @@ export class OwnedUiSessionPresenter {
|
|
|
224
223
|
}, { customViewport: this.#customViewport });
|
|
225
224
|
constructed.push(this.#unbindSessionSettings);
|
|
226
225
|
if (this.#customViewport && promptHistory !== undefined) {
|
|
227
|
-
this.#
|
|
228
|
-
this.#
|
|
229
|
-
editor: this.root.editor,
|
|
230
|
-
store: promptHistory.store,
|
|
231
|
-
limit: promptHistory.limit,
|
|
232
|
-
fallback: this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []),
|
|
233
|
-
active: () => this.root.usesDefaultInputSurface(),
|
|
234
|
-
render: () => this.#terminal.requestRender(),
|
|
235
|
-
rehydrate: value => this.root.rehydrateHistoryText(value, id => this.#historyBinding?.imageSidecar?.readAttachment(id) ?? null),
|
|
236
|
-
});
|
|
237
|
-
constructed.push(() => { void this.#promptHistory?.close().catch(() => false); });
|
|
226
|
+
this.#history = this.#createHistory(promptHistory);
|
|
227
|
+
constructed.push(() => { void this.#history?.close().catch(() => false); });
|
|
238
228
|
}
|
|
239
229
|
this.#extensionBridge = this.#createExtensionBridge();
|
|
240
230
|
constructed.push(() => this.#extensionBridge.dispose());
|
|
@@ -291,7 +281,7 @@ export class OwnedUiSessionPresenter {
|
|
|
291
281
|
return this.root.inputCoordinationSurface();
|
|
292
282
|
}
|
|
293
283
|
setOverlaySurfaces(surfaces) {
|
|
294
|
-
this.root.
|
|
284
|
+
this.root.viewport.setOverlaySurfaces(surfaces);
|
|
295
285
|
}
|
|
296
286
|
noteImmediatePresentation() {
|
|
297
287
|
this.#streamPresentation.noteImmediatePresentation();
|
|
@@ -354,7 +344,7 @@ export class OwnedUiSessionPresenter {
|
|
|
354
344
|
// resynchronizes the view, which is cheap next to re-reading the transcript.
|
|
355
345
|
if (event.type === "agent-run-started") {
|
|
356
346
|
this.#promptSuggestions?.invalidate();
|
|
357
|
-
this.root.
|
|
347
|
+
this.root.viewport.resumeFollowing();
|
|
358
348
|
}
|
|
359
349
|
if (event.type === "assistant-message-completed" && !event.successful) {
|
|
360
350
|
this.#programStatus.setErrorMessage(this.view().status.diagnostics.at(-1));
|
|
@@ -363,7 +353,7 @@ export class OwnedUiSessionPresenter {
|
|
|
363
353
|
this.#programStatus.handleEvent(event);
|
|
364
354
|
}
|
|
365
355
|
if (event.type === "assistant-message-completed") {
|
|
366
|
-
this.root.noteCompletedAssistantMessage();
|
|
356
|
+
this.root.viewport.noteCompletedAssistantMessage();
|
|
367
357
|
this.#promptSuggestions?.route(event, () => this.root.promptSuggestionPrepareBlockReason());
|
|
368
358
|
}
|
|
369
359
|
const semanticOnly = event.type === "agent-run-started" || event.type === "assistant-message-completed";
|
|
@@ -395,7 +385,7 @@ export class OwnedUiSessionPresenter {
|
|
|
395
385
|
return;
|
|
396
386
|
this.#started = true;
|
|
397
387
|
this.#terminal.start();
|
|
398
|
-
this.#
|
|
388
|
+
this.#history?.start();
|
|
399
389
|
this.#syncTerminalProgress(this.view());
|
|
400
390
|
void this.backend.extensions.bindExtensionUi(this.#extensionBridge.context, () => { void this.shutdown(); });
|
|
401
391
|
this.#syncView();
|
|
@@ -405,15 +395,10 @@ export class OwnedUiSessionPresenter {
|
|
|
405
395
|
return;
|
|
406
396
|
this.#dialogs.presentStartupRoute();
|
|
407
397
|
void this.#presenters.components.dialogs.lazySelectors().then(selectors => selectors.prepare()).catch(() => { });
|
|
408
|
-
this.root.
|
|
398
|
+
this.root.promptChips.warm();
|
|
409
399
|
this.#clipboard.warm();
|
|
410
400
|
});
|
|
411
401
|
}
|
|
412
|
-
onView(listener) {
|
|
413
|
-
this.#listeners.add(listener);
|
|
414
|
-
listener(this.view());
|
|
415
|
-
return () => this.#listeners.delete(listener);
|
|
416
|
-
}
|
|
417
402
|
waitUntilStopped() {
|
|
418
403
|
return this.#stop.stopped;
|
|
419
404
|
}
|
|
@@ -450,9 +435,9 @@ export class OwnedUiSessionPresenter {
|
|
|
450
435
|
if (now - this.#lastEscapeTime < 500) {
|
|
451
436
|
this.#lastEscapeTime = 0;
|
|
452
437
|
if (action === "tree")
|
|
453
|
-
await this.showTreeSelector();
|
|
438
|
+
await this.selectors.showTreeSelector();
|
|
454
439
|
else
|
|
455
|
-
this.showForkSelector();
|
|
440
|
+
this.selectors.showForkSelector();
|
|
456
441
|
return { outcome: "completed", diagnostic: null };
|
|
457
442
|
}
|
|
458
443
|
this.#lastEscapeTime = now;
|
|
@@ -461,36 +446,6 @@ export class OwnedUiSessionPresenter {
|
|
|
461
446
|
async abort() {
|
|
462
447
|
return this.#submission.execute(this.#simple("abort"));
|
|
463
448
|
}
|
|
464
|
-
async retry() {
|
|
465
|
-
this.#promptSuggestions?.invalidate();
|
|
466
|
-
return this.#submission.execute(this.#simple("retry"));
|
|
467
|
-
}
|
|
468
|
-
async compact() {
|
|
469
|
-
this.#promptSuggestions?.invalidate();
|
|
470
|
-
return this.#submission.execute(this.#simple("compact"));
|
|
471
|
-
}
|
|
472
|
-
async newSession() {
|
|
473
|
-
this.#promptSuggestions?.invalidate();
|
|
474
|
-
return this.#submission.execute(this.#simple("new-session"));
|
|
475
|
-
}
|
|
476
|
-
async resumeSession(sessionPath) {
|
|
477
|
-
this.#promptSuggestions?.invalidate();
|
|
478
|
-
return this.#submission.execute({
|
|
479
|
-
type: "resume-session",
|
|
480
|
-
correlationId: this.#correlation("resume"),
|
|
481
|
-
sessionId: this.backend.identity.sessionId,
|
|
482
|
-
sessionPath,
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
async setModel(providerId, modelId) {
|
|
486
|
-
this.#promptSuggestions?.invalidate();
|
|
487
|
-
return this.#submission.execute({
|
|
488
|
-
type: "set-model",
|
|
489
|
-
correlationId: this.#correlation("model"),
|
|
490
|
-
sessionId: this.backend.identity.sessionId,
|
|
491
|
-
model: { providerId, modelId, displayName: modelId },
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
449
|
async setThinkingLevel(thinkingLevel) {
|
|
495
450
|
return this.#submission.execute({
|
|
496
451
|
type: "set-thinking-level",
|
|
@@ -519,185 +474,6 @@ export class OwnedUiSessionPresenter {
|
|
|
519
474
|
showSelector(title, options, onSelect, onCancel) {
|
|
520
475
|
return this.#dialogs.showSelector(title, options, onSelect, onCancel);
|
|
521
476
|
}
|
|
522
|
-
// Rationale: presenters see the shell only as a dialog host; selectors open in the input dock and run choices as workflows.
|
|
523
|
-
#createDialogHost() {
|
|
524
|
-
const isDisposed = () => this.#disposed;
|
|
525
|
-
return {
|
|
526
|
-
get disposed() { return isDisposed(); },
|
|
527
|
-
setInputSurface: surface => this.root.setInputSurface(surface),
|
|
528
|
-
requestRender: () => this.#terminal.requestRender(),
|
|
529
|
-
viewport: () => this.#terminal.viewport(),
|
|
530
|
-
appendWorkflowStatus: text => this.root.workflow.appendStatus(text),
|
|
531
|
-
appendWorkflowResult: result => this.root.workflow.appendResult(result),
|
|
532
|
-
runWorkflow: request => this.runWorkflow(request),
|
|
533
|
-
// Invariant: the custom bare-A1 surface holds frames while optional selectors load and is permanently
|
|
534
|
-
// fullscreen; the pinned comparison profiles render immediately and keep Pi's TUI mode switch.
|
|
535
|
-
...(this.#customViewport
|
|
536
|
-
? { beginPresentationHold: () => this.#terminal.beginPresentationHold() }
|
|
537
|
-
: { switchTuiMode: mode => this.#terminal.switchMode(mode) }),
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
async showThinkingSelector() {
|
|
541
|
-
const snapshot = this.backend.settings.snapshot();
|
|
542
|
-
const close = () => {
|
|
543
|
-
this.root.setFooterLevel(true);
|
|
544
|
-
this.root.setInputSurface(null);
|
|
545
|
-
this.#terminal.requestRender();
|
|
546
|
-
};
|
|
547
|
-
const select = (level) => {
|
|
548
|
-
close();
|
|
549
|
-
void this.runWorkflow({ command: "thinking", argument: "", selection: level });
|
|
550
|
-
};
|
|
551
|
-
const releasePresentation = this.#customViewport ? this.#terminal.beginPresentationHold() : undefined;
|
|
552
|
-
try {
|
|
553
|
-
const selectors = await this.#presenters.components.dialogs.lazySelectors();
|
|
554
|
-
const component = await selectors.createThinking({
|
|
555
|
-
currentLevel: snapshot.thinkingLevel,
|
|
556
|
-
availableLevels: snapshot.availableThinkingLevels,
|
|
557
|
-
onSelect: select,
|
|
558
|
-
onCancel: close,
|
|
559
|
-
onSelectAsDefault: level => this.backend.settings.setDefaultThinkingLevel(level),
|
|
560
|
-
defaultLevel: snapshot.defaultThinkingLevel,
|
|
561
|
-
...(this.backend.settings.productMode === "bare" ? {
|
|
562
|
-
presentation: {
|
|
563
|
-
profile: "bare",
|
|
564
|
-
cycleBinding: this.root.editor.keybindingConfig()["app.thinking.cycle"] ?? [],
|
|
565
|
-
},
|
|
566
|
-
} : {}),
|
|
567
|
-
});
|
|
568
|
-
if (this.#disposed)
|
|
569
|
-
return;
|
|
570
|
-
this.root.setFooterLevel(false);
|
|
571
|
-
this.root.setInputSurface(component);
|
|
572
|
-
this.#terminal.requestRender();
|
|
573
|
-
}
|
|
574
|
-
finally {
|
|
575
|
-
releasePresentation?.();
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
/** The Skills dialog: a replacement input like the model selector, applying the chosen skill through the prompt path. */
|
|
579
|
-
showSkillsSelector(skills = this.#skillSummaries()) {
|
|
580
|
-
const close = this.#dialogs.closeInputSurface;
|
|
581
|
-
const component = this.#presenters.components.dialogs.skillsSelector({
|
|
582
|
-
skills,
|
|
583
|
-
onSelect: name => {
|
|
584
|
-
close();
|
|
585
|
-
void this.#submission.sendSlashPrompt(this.#presenters.components.skills.prompt(name), this.#presenters.components.skills.prompt(name)).catch(() => this.#submission.reportError());
|
|
586
|
-
},
|
|
587
|
-
onCancel: close,
|
|
588
|
-
});
|
|
589
|
-
this.root.setInputSurface(component);
|
|
590
|
-
this.#terminal.requestRender();
|
|
591
|
-
}
|
|
592
|
-
showModelSelector(initialSearchInput) {
|
|
593
|
-
this.#presenters.openModelSelector(this.#dialogHost, initialSearchInput);
|
|
594
|
-
}
|
|
595
|
-
showForkSelector() {
|
|
596
|
-
const options = this.backend.catalog.pinnedForkOptions();
|
|
597
|
-
if (options.length === 0) {
|
|
598
|
-
this.root.workflow.appendResult({ command: "fork", outcome: "completed", message: "No messages to fork from", messageKind: "status" });
|
|
599
|
-
this.#terminal.requestRender();
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
const close = this.#dialogs.closeInputSurface;
|
|
603
|
-
const component = this.#presenters.components.dialogs.userMessageSelector(options, selection => {
|
|
604
|
-
close();
|
|
605
|
-
void this.runWorkflow({ command: "fork", argument: "", selection });
|
|
606
|
-
}, close);
|
|
607
|
-
this.root.setInputSurface(component);
|
|
608
|
-
this.#terminal.requestRender();
|
|
609
|
-
}
|
|
610
|
-
async showLogoutSelector() {
|
|
611
|
-
let options;
|
|
612
|
-
try {
|
|
613
|
-
options = await this.backend.catalog.pinnedLogoutOptions();
|
|
614
|
-
}
|
|
615
|
-
catch (error) {
|
|
616
|
-
this.root.workflow.appendResult({
|
|
617
|
-
command: "logout",
|
|
618
|
-
outcome: "failed",
|
|
619
|
-
message: `Could not read stored credentials: ${error instanceof Error ? error.message : String(error)}`,
|
|
620
|
-
});
|
|
621
|
-
this.#terminal.requestRender();
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
if (options.length === 0) {
|
|
625
|
-
this.root.workflow.appendStatus("No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.");
|
|
626
|
-
this.#terminal.requestRender();
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
const close = this.#dialogs.closeInputSurface;
|
|
630
|
-
const component = this.#presenters.components.dialogs.authProviderSelector("logout", options, selection => {
|
|
631
|
-
close();
|
|
632
|
-
void this.runWorkflow({ command: "logout", argument: "", selection });
|
|
633
|
-
}, close);
|
|
634
|
-
this.root.setInputSurface(component);
|
|
635
|
-
this.#terminal.requestRender();
|
|
636
|
-
}
|
|
637
|
-
showLoginMethodSelector(providerReference) {
|
|
638
|
-
const method = this.backend.catalog.pinnedLoginMethodOptions(providerReference);
|
|
639
|
-
if (method.options.length === 0) {
|
|
640
|
-
this.showLoginProviderSelector(undefined, providerReference);
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
if (method.options.length === 1) {
|
|
644
|
-
const selection = method.options[0]?.id;
|
|
645
|
-
if (selection)
|
|
646
|
-
void this.runWorkflow({ command: "login", argument: "", selection });
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
const close = this.#dialogs.closeInputSurface;
|
|
650
|
-
const labels = method.options.map(option => option.label);
|
|
651
|
-
const component = this.#presenters.components.dialogs.extensionSelector(method.title, labels, label => {
|
|
652
|
-
const selection = method.options.find(option => option.label === label)?.id;
|
|
653
|
-
if (!selection)
|
|
654
|
-
return;
|
|
655
|
-
close();
|
|
656
|
-
void this.runWorkflow({ command: "login", argument: "", selection });
|
|
657
|
-
}, close);
|
|
658
|
-
this.root.setInputSurface(component);
|
|
659
|
-
this.#terminal.requestRender();
|
|
660
|
-
}
|
|
661
|
-
showTreeSelector(initialSelectedId) {
|
|
662
|
-
return this.#presenters.openTreeSelector(this.#dialogHost, (entryId, skipSummaryPrompt) => {
|
|
663
|
-
void this.#completeTreeSelection(entryId, skipSummaryPrompt);
|
|
664
|
-
}, initialSelectedId);
|
|
665
|
-
}
|
|
666
|
-
showLoginAuthTypeSelector() {
|
|
667
|
-
const close = this.#dialogs.closeInputSurface;
|
|
668
|
-
const labels = ["Sign in with an account", "Sign in with an API key"];
|
|
669
|
-
const component = this.#presenters.components.dialogs.extensionSelector("Select authentication method:", labels, label => {
|
|
670
|
-
close();
|
|
671
|
-
this.showLoginProviderSelector(label === labels[0] ? "oauth" : "api_key");
|
|
672
|
-
}, close);
|
|
673
|
-
this.root.setInputSurface(component);
|
|
674
|
-
this.#terminal.requestRender();
|
|
675
|
-
}
|
|
676
|
-
showLoginProviderSelector(authType, initialSearchInput) {
|
|
677
|
-
const options = this.backend.catalog.pinnedLoginOptions(authType);
|
|
678
|
-
if (options.length === 0) {
|
|
679
|
-
this.root.workflow.appendStatus(authType === "oauth" ? "No account providers available." : authType === "api_key" ? "No API key providers available." : "No login providers available.");
|
|
680
|
-
this.#terminal.requestRender();
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
const close = this.#dialogs.closeInputSurface;
|
|
684
|
-
const component = this.#presenters.components.dialogs.authProviderSelector("login", options, id => {
|
|
685
|
-
close();
|
|
686
|
-
void this.runWorkflow({ command: "login", argument: "", selection: id });
|
|
687
|
-
}, () => {
|
|
688
|
-
close();
|
|
689
|
-
if (authType !== undefined)
|
|
690
|
-
this.showLoginAuthTypeSelector();
|
|
691
|
-
}, initialSearchInput);
|
|
692
|
-
this.root.setInputSurface(component);
|
|
693
|
-
this.#terminal.requestRender();
|
|
694
|
-
}
|
|
695
|
-
showSessionSelector() {
|
|
696
|
-
return this.#presenters.openSessionSelector(this.#dialogHost, () => { void this.shutdown(); });
|
|
697
|
-
}
|
|
698
|
-
showSettingsSelector() {
|
|
699
|
-
this.#presenters.openSettingsSelector(this.#dialogHost);
|
|
700
|
-
}
|
|
701
477
|
shutdown() {
|
|
702
478
|
this.#shutdownPromise ??= this.#shutdown().finally(() => {
|
|
703
479
|
// Invariant: a failed quit is retryable; ordinary shutdown has disposed the shell already.
|
|
@@ -757,7 +533,7 @@ export class OwnedUiSessionPresenter {
|
|
|
757
533
|
return { outcome: "completed", diagnostic: null };
|
|
758
534
|
}
|
|
759
535
|
}
|
|
760
|
-
const selected = openWorkflowSelector(request, this);
|
|
536
|
+
const selected = openWorkflowSelector(request, this.selectors);
|
|
761
537
|
if (selected !== undefined)
|
|
762
538
|
return selected;
|
|
763
539
|
if (request.command === "reload") {
|
|
@@ -807,7 +583,7 @@ export class OwnedUiSessionPresenter {
|
|
|
807
583
|
for (const message of result.messages)
|
|
808
584
|
this.root.workflow.appendMessage(message);
|
|
809
585
|
}
|
|
810
|
-
this.showModelSelector((result.detail ?? request.argument.trim()) || undefined);
|
|
586
|
+
this.selectors.showModelSelector((result.detail ?? request.argument.trim()) || undefined);
|
|
811
587
|
return { outcome: "completed", diagnostic: null };
|
|
812
588
|
}
|
|
813
589
|
if (result.outcome === "requires-confirmation" && request.command === "resume") {
|
|
@@ -836,7 +612,7 @@ export class OwnedUiSessionPresenter {
|
|
|
836
612
|
if (request.command === "tree" && result.outcome === "completed") {
|
|
837
613
|
if (result.detail && !this.root.editor.getText().trim())
|
|
838
614
|
this.root.editor.setText(result.detail);
|
|
839
|
-
this.root.
|
|
615
|
+
this.root.viewport.resumeFollowing();
|
|
840
616
|
}
|
|
841
617
|
if (shareDialog !== undefined && this.#customViewport && result.outcome === "completed" && result.detail !== undefined) {
|
|
842
618
|
this.root.workflow.appendStatus(shareDialog.renderShellShareResult(result.message, result.detail, this.#presenters.theme.current(), url => this.#presenters.components.text.hyperlink(url)));
|
|
@@ -855,40 +631,11 @@ export class OwnedUiSessionPresenter {
|
|
|
855
631
|
const sleep = this.#reloadPresentation?.sleep ?? ((ms) => new Promise(resolve => setTimeout(resolve, ms)));
|
|
856
632
|
await sleep(remaining);
|
|
857
633
|
}
|
|
858
|
-
showTrustSelector() {
|
|
859
|
-
const context = this.backend.catalog.pinnedProjectTrustContext();
|
|
860
|
-
const close = this.#dialogs.closeInputSurface;
|
|
861
|
-
const component = this.#presenters.components.dialogs.trustSelector({
|
|
862
|
-
...context,
|
|
863
|
-
onSelect: selection => {
|
|
864
|
-
try {
|
|
865
|
-
this.backend.catalog.persistProjectTrust(selection.updates);
|
|
866
|
-
close();
|
|
867
|
-
this.root.workflow.appendStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
|
|
868
|
-
}
|
|
869
|
-
catch (error) {
|
|
870
|
-
close();
|
|
871
|
-
this.root.workflow.appendResult({ command: "trust", outcome: "failed", message: error instanceof Error ? error.message : String(error) });
|
|
872
|
-
}
|
|
873
|
-
this.#terminal.requestRender();
|
|
874
|
-
},
|
|
875
|
-
onCancel: close,
|
|
876
|
-
});
|
|
877
|
-
this.root.setInputSurface(component);
|
|
878
|
-
this.#terminal.requestRender();
|
|
879
|
-
}
|
|
880
|
-
showScopedModelsSelector() {
|
|
881
|
-
this.#presenters.openScopedModelsSelector(this.#dialogHost);
|
|
882
|
-
}
|
|
883
634
|
// Invariant: bare A1 routes `models`; the comparison profile keeps the pinned `model`/`scoped-models` pair. Hidden routes are shared.
|
|
884
635
|
#isWorkflowRoute(value) {
|
|
885
636
|
return this.#presenters.components.workflows.commandNames(this.backend.settings.productMode).includes(value)
|
|
886
637
|
|| OWNED_UI_HIDDEN_COMMAND_NAMES.includes(value);
|
|
887
638
|
}
|
|
888
|
-
/** The bare-A1 unified Models dialog: switch on Enter, scope on Space, persist on Ctrl+S, all through the engine. */
|
|
889
|
-
showModelsDialog(initialQuery) {
|
|
890
|
-
this.#presenters.openModelsDialog(this.#dialogHost, initialQuery);
|
|
891
|
-
}
|
|
892
639
|
// Invariant: one teardown per presenter, starting on a microtask so never inside the event listener that
|
|
893
640
|
// requested it. Callers during the teardown share its outcome; a later call finds it done. Closing the
|
|
894
641
|
// terminal host's last presenter ends the terminal.
|
|
@@ -923,9 +670,9 @@ export class OwnedUiSessionPresenter {
|
|
|
923
670
|
this.#submission.cancelWaiting();
|
|
924
671
|
let pasteCleanup = Promise.resolve();
|
|
925
672
|
let historyCleanup = Promise.resolve(true);
|
|
926
|
-
attempt(() => { historyCleanup = this.#
|
|
673
|
+
attempt(() => { historyCleanup = this.#history?.close() ?? Promise.resolve(true); });
|
|
927
674
|
attempt(() => { pasteCleanup = this.root.disposePendingPastes(); });
|
|
928
|
-
attempt(() => this.root.
|
|
675
|
+
attempt(() => this.root.viewport.clearPointerState());
|
|
929
676
|
attempt(() => this.#streamPresentation.dispose());
|
|
930
677
|
attempt(() => this.#promptSuggestions?.dispose());
|
|
931
678
|
attempt(() => this.#unbindShellSettings());
|
|
@@ -975,8 +722,6 @@ export class OwnedUiSessionPresenter {
|
|
|
975
722
|
this.#streamPresentation.presentImmediate();
|
|
976
723
|
}
|
|
977
724
|
const view = this.view();
|
|
978
|
-
for (const listener of this.#listeners)
|
|
979
|
-
listener(view);
|
|
980
725
|
return view;
|
|
981
726
|
}
|
|
982
727
|
// Rationale: extensions reach the shell only through this bridge; every surface they draw lands in the root.
|
|
@@ -1005,14 +750,40 @@ export class OwnedUiSessionPresenter {
|
|
|
1005
750
|
setToolsExpanded: expanded => this.root.setToolsExpanded(expanded),
|
|
1006
751
|
});
|
|
1007
752
|
}
|
|
753
|
+
#createSelectors(presenters) {
|
|
754
|
+
return new SessionSelectors({
|
|
755
|
+
root: this.root,
|
|
756
|
+
terminal: this.#terminal,
|
|
757
|
+
dialogs: this.#dialogs,
|
|
758
|
+
presenters,
|
|
759
|
+
backend: this.backend,
|
|
760
|
+
customViewport: this.#customViewport,
|
|
761
|
+
disposed: () => this.#disposed,
|
|
762
|
+
runWorkflow: request => this.runWorkflow(request),
|
|
763
|
+
sendSlashPrompt: (text, typed) => this.#submission.sendSlashPrompt(text, typed),
|
|
764
|
+
reportError: () => this.#submission.reportError(),
|
|
765
|
+
input: (title, placeholder, inputOptions) => this.#extensionBridge.input(title, placeholder, inputOptions),
|
|
766
|
+
shutdown: () => { void this.shutdown(); },
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
#createHistory(promptHistory) {
|
|
770
|
+
return new SessionPromptHistory(promptHistory, {
|
|
771
|
+
editor: this.root.editor,
|
|
772
|
+
usesDefaultInputSurface: () => this.root.usesDefaultInputSurface(),
|
|
773
|
+
rehydrate: (text, resolveImage) => this.root.promptChips.rehydrateHistoryText(text, resolveImage),
|
|
774
|
+
requestRender: () => this.#terminal.requestRender(),
|
|
775
|
+
transcript: () => this.view().transcript,
|
|
776
|
+
currentHistorySessionId: () => this.backend.identity.currentHistorySessionId(),
|
|
777
|
+
});
|
|
778
|
+
}
|
|
1008
779
|
#createSubmission(cwd) {
|
|
1009
780
|
return new PromptSubmission({
|
|
1010
781
|
backend: this.backend,
|
|
1011
782
|
root: this.root,
|
|
1012
783
|
terminal: this.#terminal,
|
|
1013
784
|
cwd, statusText: this.#presenters.components.info.statusText,
|
|
1014
|
-
promptHistory: () => this.#
|
|
1015
|
-
imageSidecar: () => this.#
|
|
785
|
+
promptHistory: () => this.#history?.controller ?? null,
|
|
786
|
+
imageSidecar: () => this.#history?.imageSidecar,
|
|
1016
787
|
disposed: () => this.#disposed,
|
|
1017
788
|
correlation: prefix => this.#correlation(prefix),
|
|
1018
789
|
lifecycle: () => this.view().lifecycle,
|
|
@@ -1033,7 +804,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1033
804
|
this.#login,
|
|
1034
805
|
{ onSessionReplaced: () => this.#extensionBridge.reset() },
|
|
1035
806
|
// Invariant: a replaced session takes its transient viewport and owned-route state with it.
|
|
1036
|
-
{ onSessionReplaced: () => this.root.
|
|
807
|
+
{ onSessionReplaced: () => this.root.viewport.reset() },
|
|
1037
808
|
this.#dialogs,
|
|
1038
809
|
{ onSessionReplaced: () => this.root.setInputSurface(null) },
|
|
1039
810
|
{ onSessionReplaced: () => this.root.resetExtensionUi() },
|
|
@@ -1049,48 +820,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1049
820
|
this.#sessionBindingGeneration = this.backend.identity.sessionBindingGeneration;
|
|
1050
821
|
this.root.transcript.reset();
|
|
1051
822
|
}
|
|
1052
|
-
|
|
1053
|
-
if (binding?.options.forSession !== undefined || binding?.options.forScope !== undefined) {
|
|
1054
|
-
const sessionId = this.backend.identity.currentHistorySessionId();
|
|
1055
|
-
const selectedScope = scope ?? binding.scope;
|
|
1056
|
-
if (selectedScope !== binding.scope || sessionId !== binding.sessionId) {
|
|
1057
|
-
if (!replaced)
|
|
1058
|
-
this.root.editor.recall?.restoreDraft?.();
|
|
1059
|
-
const previous = this.#promptHistory;
|
|
1060
|
-
this.#promptHistory = null;
|
|
1061
|
-
binding.imageSidecar = undefined;
|
|
1062
|
-
binding.scope = selectedScope;
|
|
1063
|
-
binding.sessionId = sessionId ?? undefined;
|
|
1064
|
-
void previous?.close().catch(() => false);
|
|
1065
|
-
const destination = binding.options.forScope !== undefined
|
|
1066
|
-
? binding.options.forScope(selectedScope, sessionId)
|
|
1067
|
-
: sessionId === null ? null : binding.options.forSession(sessionId);
|
|
1068
|
-
if (destination !== null) {
|
|
1069
|
-
binding.imageSidecar = destination.imageSidecar;
|
|
1070
|
-
const controller = new PromptHistoryController({
|
|
1071
|
-
editor: this.root.editor,
|
|
1072
|
-
store: destination.store,
|
|
1073
|
-
limit: binding.options.limit,
|
|
1074
|
-
fallback: replaced ? this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []) : [],
|
|
1075
|
-
active: () => this.root.usesDefaultInputSurface(),
|
|
1076
|
-
render: () => this.#terminal.requestRender(),
|
|
1077
|
-
rehydrate: value => this.root.rehydrateHistoryText(value, id => this.#historyBinding?.imageSidecar?.readAttachment(id) ?? null),
|
|
1078
|
-
});
|
|
1079
|
-
this.#promptHistory = controller;
|
|
1080
|
-
if (this.#started)
|
|
1081
|
-
controller.start();
|
|
1082
|
-
}
|
|
1083
|
-
else {
|
|
1084
|
-
// Security: missing identity must not reopen Shared or retain another session's recall.
|
|
1085
|
-
const fallback = replaced ? this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []).reverse() : [];
|
|
1086
|
-
this.root.editor.recall?.reset();
|
|
1087
|
-
this.root.editor.recall?.replace(fallback);
|
|
1088
|
-
}
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
if (replaced)
|
|
1093
|
-
this.#promptHistory?.reset(this.view().transcript.flatMap(block => block.kind === "user" ? [block.text] : []));
|
|
823
|
+
this.#history?.rebind(replaced, scope);
|
|
1094
824
|
}
|
|
1095
825
|
#syncTerminalProgress(view) {
|
|
1096
826
|
if (!this.#terminal.running)
|
|
@@ -1117,8 +847,6 @@ export class OwnedUiSessionPresenter {
|
|
|
1117
847
|
this.#dialogs.syncBackend(view.dialog);
|
|
1118
848
|
this.#presentStartupTrustWarning(view);
|
|
1119
849
|
this.#terminal.requestRender();
|
|
1120
|
-
for (const listener of this.#listeners)
|
|
1121
|
-
listener(view);
|
|
1122
850
|
return view;
|
|
1123
851
|
}
|
|
1124
852
|
// Rationale: a cancelled or failed trust choice explains the restricted session where the
|
|
@@ -1188,7 +916,7 @@ export class OwnedUiSessionPresenter {
|
|
|
1188
916
|
const skills = this.#skillSummaries();
|
|
1189
917
|
const trimmed = argument.trim();
|
|
1190
918
|
if (trimmed.length === 0) {
|
|
1191
|
-
this.showSkillsSelector(skills);
|
|
919
|
+
this.selectors.showSkillsSelector(skills);
|
|
1192
920
|
return { outcome: "completed", diagnostic: null };
|
|
1193
921
|
}
|
|
1194
922
|
const separator = trimmed.search(/\s/u);
|
|
@@ -1202,45 +930,6 @@ export class OwnedUiSessionPresenter {
|
|
|
1202
930
|
}
|
|
1203
931
|
return this.#submission.sendSlashPrompt(this.#presenters.components.skills.prompt(skill.name, separator < 0 ? "" : trimmed.slice(separator + 1)), text);
|
|
1204
932
|
}
|
|
1205
|
-
#treeSummary() {
|
|
1206
|
-
return new Promise(resolve => {
|
|
1207
|
-
this.root.setInputSurface(this.#presenters.components.dialogs.extensionSelector("Summarize Branch?", ["No summary", "Summarize", "Summarize with custom prompt"], resolve, () => resolve(undefined)), true, "opaque");
|
|
1208
|
-
this.#terminal.requestRender();
|
|
1209
|
-
});
|
|
1210
|
-
}
|
|
1211
|
-
async #completeTreeSelection(entryId, skipSummaryPrompt) {
|
|
1212
|
-
let summarize = false;
|
|
1213
|
-
let customInstructions;
|
|
1214
|
-
if (!skipSummaryPrompt) {
|
|
1215
|
-
while (true) {
|
|
1216
|
-
const choice = await this.#treeSummary();
|
|
1217
|
-
if (choice === undefined) {
|
|
1218
|
-
await this.showTreeSelector(entryId);
|
|
1219
|
-
return;
|
|
1220
|
-
}
|
|
1221
|
-
summarize = choice !== "No summary";
|
|
1222
|
-
if (choice === "Summarize with custom prompt") {
|
|
1223
|
-
customInstructions = await this.#extensionBridge.input("Custom Summarization Instructions", "", { retainSurfaceOnSettle: true });
|
|
1224
|
-
if (customInstructions === undefined)
|
|
1225
|
-
continue;
|
|
1226
|
-
}
|
|
1227
|
-
break;
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
this.root.setInputSurface(null);
|
|
1231
|
-
this.#terminal.requestRender();
|
|
1232
|
-
const result = await this.runWorkflow({
|
|
1233
|
-
command: "tree",
|
|
1234
|
-
argument: "",
|
|
1235
|
-
selection: entryId,
|
|
1236
|
-
treeSummary: {
|
|
1237
|
-
summarize,
|
|
1238
|
-
...(customInstructions === undefined ? {} : { customInstructions }),
|
|
1239
|
-
},
|
|
1240
|
-
});
|
|
1241
|
-
if (result.diagnostic === "Branch summarization cancelled")
|
|
1242
|
-
await this.showTreeSelector(entryId);
|
|
1243
|
-
}
|
|
1244
933
|
#simple(type) {
|
|
1245
934
|
return { type, correlationId: this.#correlation(type), sessionId: this.backend.identity.sessionId };
|
|
1246
935
|
}
|
|
@@ -4,6 +4,7 @@ import type { OwnedUiViewportSettings } from "../../contracts/owned-ui/index.js"
|
|
|
4
4
|
import { type TranscriptViewportFrame, type TranscriptViewportFrameInput } from "../../ui/components/transcript-viewport.js";
|
|
5
5
|
import type { SelectionCopySnapshot } from "../../ui/components/selection-copy.js";
|
|
6
6
|
import type { PasteEvent } from "./paste-protocol.js";
|
|
7
|
+
import { type TailControl, type TailControlRegion } from "./viewport-pointer.js";
|
|
7
8
|
interface SessionViewportControllerOptions {
|
|
8
9
|
readonly enabled: boolean;
|
|
9
10
|
readonly editor: OwnedUiShellEditorPort;
|
|
@@ -15,13 +16,6 @@ interface SessionViewportControllerOptions {
|
|
|
15
16
|
readonly pasteDiagnostics?: (event: PasteEvent) => void;
|
|
16
17
|
readonly nextPasteDiagnosticRequest?: () => number;
|
|
17
18
|
}
|
|
18
|
-
/** One-based, inclusive terminal cells of a clickable viewport-tail control. */
|
|
19
|
-
export interface TailControlRegion {
|
|
20
|
-
readonly rowStart: number;
|
|
21
|
-
readonly rowEnd: number;
|
|
22
|
-
readonly columnStart: number;
|
|
23
|
-
readonly columnEnd: number;
|
|
24
|
-
}
|
|
25
19
|
export interface SessionViewportInputResult {
|
|
26
20
|
readonly data: string;
|
|
27
21
|
readonly consumed: boolean;
|
|
@@ -54,9 +48,7 @@ export declare class SessionViewportController {
|
|
|
54
48
|
readonly rowEnd: number;
|
|
55
49
|
} | undefined): void;
|
|
56
50
|
/** The visible close control of a viewport-tail notice, or undefined when none is on screen. */
|
|
57
|
-
setTailControl(control:
|
|
58
|
-
readonly activate: () => void;
|
|
59
|
-
}) | undefined): void;
|
|
51
|
+
setTailControl(control: TailControl | undefined): void;
|
|
60
52
|
/** Whether the pointer currently rests on the tail control; drives its hover paint. */
|
|
61
53
|
tailControlHovered(target: TailControlRegion | undefined): boolean;
|
|
62
54
|
/** Replacement input changed before its new bounds have been painted. */
|