@timurproko/a1 0.2.5-dev.683 → 0.2.5-dev.704
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-suggestion-controller.d.ts +2 -0
- package/dist/app/session-shell/prompt-suggestion-controller.js +10 -0
- package/dist/app/session-shell/session-shell-root.js +15 -2
- package/dist/app/session-shell/session-shell.js +80 -23
- package/dist/app/session-shell/share-operation.d.ts +3 -0
- package/dist/app/session-shell/share-operation.js +11 -0
- package/dist/composition/owned-ui.js +2 -0
- package/dist/composition/settings-route-host.js +1 -1
- package/dist/contracts/presentation/index.d.ts +11 -1
- package/dist/contracts/presentation/index.js +6 -2
- package/dist/features/owned-ui/project-trust-dialog.js +7 -5
- package/dist/features/owned-ui/reference-screen-app.d.ts +2 -2
- package/dist/features/owned-ui/reference-screen-app.js +10 -6
- package/dist/features/owned-ui/settings-app.d.ts +1 -1
- package/dist/features/owned-ui/settings-app.js +342 -66
- package/dist/integrations/pi/components/index.d.ts +3 -2
- package/dist/integrations/pi/components/index.js +2 -1
- package/dist/integrations/pi/components/modal-frame.d.ts +11 -1
- package/dist/integrations/pi/components/modal-frame.js +29 -1
- package/dist/integrations/pi/components/models-dialog.js +19 -19
- package/dist/integrations/pi/components/share-operation-dialog.d.ts +6 -0
- package/dist/integrations/pi/components/share-operation-dialog.js +40 -0
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +55 -8
- package/dist/integrations/pi/components/shell-extension-ui.d.ts +3 -0
- package/dist/integrations/pi/components/shell-extension-ui.js +8 -4
- package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +0 -4
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +56 -34
- package/dist/integrations/pi/components/shell-session-info-reference.js +35 -2
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +3 -1
- package/dist/integrations/pi/components/shell-shared-facade.js +13 -1
- package/dist/integrations/pi/components/skills-dialog.js +21 -8
- package/dist/integrations/pi/components/theme.d.ts +8 -1
- package/dist/integrations/pi/components/theme.js +28 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +10 -2
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +20 -6
- package/dist/integrations/pi/components/upstream/components/extension-editor.js +2 -2
- package/dist/integrations/pi/components/upstream/components/extension-input.js +4 -5
- package/dist/integrations/pi/components/upstream/components/extension-selector.js +2 -3
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +4 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +48 -12
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -2
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +4 -15
- package/dist/integrations/pi/components/upstream/components/session-selector.d.ts +2 -0
- package/dist/integrations/pi/components/upstream/components/session-selector.js +137 -110
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +9 -5
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +65 -26
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +31 -21
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +255 -214
- package/dist/integrations/pi/components/upstream/components/trust-selector.js +13 -15
- package/dist/integrations/pi/engine/adapter.js +21 -2
- package/dist/integrations/pi/engine/workflow-runner.js +2 -1
- 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/ui/apps/host.d.ts +4 -3
- package/dist/ui/apps/host.js +9 -4
- package/dist/ui/components/dialog-panel.d.ts +15 -0
- package/dist/ui/components/dialog-panel.js +55 -9
- package/dist/ui/components/index.d.ts +3 -3
- package/dist/ui/components/index.js +2 -2
- package/dist/ui/components/line-input.d.ts +2 -0
- package/dist/ui/components/line-input.js +2 -2
- package/dist/ui/components/list-view.d.ts +3 -3
- package/dist/ui/components/list-view.js +8 -9
- package/dist/ui/components/prompt-input.d.ts +2 -2
- package/dist/ui/components/prompt-input.js +5 -5
- package/dist/ui/components/shortcut-hints.d.ts +10 -3
- package/dist/ui/components/shortcut-hints.js +42 -6
- package/dist/ui/components/shortcuts.d.ts +1 -1
- package/dist/ui/components/shortcuts.js +4 -0
- package/dist/ui/components/theme.d.ts +1 -1
- package/dist/ui/components/value-menu.js +3 -1
- package/dist/ui/settings/manager.d.ts +1 -0
- package/dist/ui/settings/manager.js +9 -3
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { adaptPiAssistantMessage, adaptPiToolExecution, adaptPiUserMessage } from "./components.js";
|
|
2
2
|
export { loadHistoryEditor } from "./history-editor-loader.js";
|
|
3
3
|
export { PiComponentConformanceError, runPiComponentConformance } from "./conformance.js";
|
|
4
|
-
export {
|
|
4
|
+
export { createPiShellOperationLoader, createPiShellShareOperationDialog, } from "./share-operation-dialog.js";
|
|
5
|
+
export { OWNED_BUILTIN_SLASH_COMMANDS, PINNED_PI_BUILTIN_SLASH_COMMANDS, componentFromPort, componentPort, createPiExtensionUiBridge, createPiQueuedInputStatus, createPiShellArmin, createPiShellAuthProviderSelector, createPiShellChangelog, createPiShellCollapsedChangelog, createPiShellDialog, createPiShellEarendilAnnouncement, createPiShellEditor, createPiShellExtensionSelector, createPiShellFooter, createPiShellHeader, createPiShellHotkeys, createPiShellLoadedResources, createPiShellLoginDialog, createPiShellModelSelector, createPiShellModelsDialog, createPiShellReloadBox, createPiShellScopedModelsSelector, createPiShellSelector, createPiShellSessionInfo, createPiShellSessionSelector, createPiShellSettingsSelector, createPiShellShowImagesSelector, createPiShellSkillsSelector, createPiShellStatus, createPiShellThemeSelector, createPiShellTranscriptComponent, createPiShellTreeSelector, createPiShellTrustSelector, createPiShellUserMessageSelector, createTuiFacade, ensureTheme, formatSessionTokens, isAutocompleteProvider, isPiPromptStyleCompaction, isRecord, paintPiSubmittedPromptTimestamp, piShellHyperlink, piShellTruncateToWidth, piShellVisibleWidth, renderPiShellChangelogLines, renderPiShellCommandMessage, renderPiShellHotkeysLines, renderPiShellPackageUpdateNotice, renderPiShellReleaseUpdateBanner, renderPiShellReleaseUpdateNotice, renderPiShellStartupDiagnostic, renderPiShellStatusText, renderPiShellTranscriptBlock, validatedAssistantMessage, } from "./shell-components.js";
|
|
5
6
|
export { renderPiShellHotkeySections } from "./shell-hotkey-sections.js";
|
|
6
7
|
export { PINNED_PI_LAYOUT, applyConfiguredPiTheme, applyPiTheme, applyPiThemeInstance, currentPiThemeName, detectPiTerminalBackgroundFromEnv, detectPiTerminalBackgroundTheme, detectPiTerminalThemeForAuto, ensurePiTheme, getAvailablePiThemes, loadPiTheme, onPiThemeChange, parsePiAutoThemeSetting, piTheme, resolvePiThemeSetting, stopPiThemeWatcher, } from "./theme.js";
|
|
7
8
|
export { OwnedPiThemeController } from "./upstream/theme/theme-controller.js";
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import { Container, type Component } from "@earendil-works/pi-tui";
|
|
1
|
+
import { Container, type Component, type TUI } from "@earendil-works/pi-tui";
|
|
2
2
|
export declare const PI_MODAL_CONTENT_PADDING_X = 1;
|
|
3
|
+
/** Adds the standard navigation/select/close footer before a dependency component's bottom rule. */
|
|
4
|
+
export declare function addPiModalSelectFooter(container: Container): void;
|
|
5
|
+
/** Owned cancellable operation frame with the canonical dialog close guidance. */
|
|
6
|
+
export declare class PiCancellableOperationFrame extends Container {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(tui: TUI, message: string);
|
|
9
|
+
get signal(): AbortSignal;
|
|
10
|
+
handleInput(data: string): void;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
3
13
|
/** The semantic top rule/title pair used while a modal assembles its frame. */
|
|
4
14
|
export declare class PiModalHeader implements Component {
|
|
5
15
|
readonly rule: Component;
|
|
@@ -1,5 +1,33 @@
|
|
|
1
|
-
import { Container, Spacer } from "@earendil-works/pi-tui";
|
|
1
|
+
import { CancellableLoader, Container, Spacer, Text } from "@earendil-works/pi-tui";
|
|
2
|
+
import { DynamicBorder } from "../startup-public.js";
|
|
3
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalShortcutHints } from "./theme.js";
|
|
2
4
|
export const PI_MODAL_CONTENT_PADDING_X = 1;
|
|
5
|
+
/** Adds the standard navigation/select/close footer before a dependency component's bottom rule. */
|
|
6
|
+
export function addPiModalSelectFooter(container) {
|
|
7
|
+
container.children.splice(container.children.length - 1, 0, new Text(renderPiModalShortcutHints([
|
|
8
|
+
{ key: "↑↓", action: "navigate" }, { key: "enter", action: "select" }, DIALOG_CLOSE_SHORTCUT_HINT,
|
|
9
|
+
]), 0, 0));
|
|
10
|
+
}
|
|
11
|
+
/** Owned cancellable operation frame with the canonical dialog close guidance. */
|
|
12
|
+
export class PiCancellableOperationFrame extends Container {
|
|
13
|
+
#loader;
|
|
14
|
+
constructor(tui, message) {
|
|
15
|
+
super();
|
|
16
|
+
const theme = piTheme();
|
|
17
|
+
const border = (text) => theme.fg("border", text);
|
|
18
|
+
this.#loader = new CancellableLoader(tui, text => theme.fg("accent", text), text => theme.fg("muted", text), message);
|
|
19
|
+
this.addChild(new DynamicBorder(border));
|
|
20
|
+
this.addChild(this.#loader);
|
|
21
|
+
this.addChild(new Spacer(1));
|
|
22
|
+
this.addChild(new Text(renderPiModalShortcutHints([DIALOG_CLOSE_SHORTCUT_HINT]), 0, 0));
|
|
23
|
+
this.addChild(new Spacer(1));
|
|
24
|
+
this.addChild(new DynamicBorder(border));
|
|
25
|
+
adoptPiModalFrame(this, { topIndex: 0, bottomIndex: this.children.length - 1 });
|
|
26
|
+
}
|
|
27
|
+
get signal() { return this.#loader.signal; }
|
|
28
|
+
handleInput(data) { this.#loader.handleInput(data); }
|
|
29
|
+
dispose() { this.#loader.dispose(); }
|
|
30
|
+
}
|
|
3
31
|
/** The semantic top rule/title pair used while a modal assembles its frame. */
|
|
4
32
|
export class PiModalHeader {
|
|
5
33
|
rule;
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { Input,
|
|
1
|
+
import { Input, getKeybindings, matchesKey, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
|
|
2
2
|
import { DynamicBorder } from "../startup-public.js";
|
|
3
3
|
import { PiModalFrame } from "./modal-frame.js";
|
|
4
|
-
import { piTheme, renderPiModalShortcutHints } from "./theme.js";
|
|
4
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalListRow, renderPiModalShortcutHints, } from "./theme.js";
|
|
5
|
+
function renderHintsWithClose(entries, width) {
|
|
6
|
+
const body = renderPiModalShortcutHints(entries.slice(0, -1));
|
|
7
|
+
const close = renderPiModalShortcutHints([DIALOG_CLOSE_SHORTCUT_HINT]);
|
|
8
|
+
const closeWidth = visibleWidth(close);
|
|
9
|
+
if (width <= closeWidth)
|
|
10
|
+
return truncateToWidth(close, width, "");
|
|
11
|
+
const clippedBody = truncateToWidth(body, Math.max(0, width - closeWidth - 2), "…");
|
|
12
|
+
return visibleWidth(clippedBody) === 0 ? close : `${clippedBody} ${close}`;
|
|
13
|
+
}
|
|
5
14
|
const MAX_VISIBLE_ROWS = 10;
|
|
6
15
|
const MODELS_TITLE = "Models";
|
|
7
16
|
const REFRESHING_TITLE_MIN_DURATION_MS = 1_000;
|
|
@@ -166,7 +175,7 @@ export class ModelsDialogComponent {
|
|
|
166
175
|
this.#selectedIndex = this.#selectedIndex === rows.length - 1 ? 0 : this.#selectedIndex + 1;
|
|
167
176
|
return;
|
|
168
177
|
}
|
|
169
|
-
if (kb.matches(data, "tui.input.tab")) {
|
|
178
|
+
if (matchesKey(data, "shift+tab") || kb.matches(data, "tui.input.tab")) {
|
|
170
179
|
this.#filter = this.#filter === "all" ? "scoped" : "all";
|
|
171
180
|
this.#restoreSelection(preferredId);
|
|
172
181
|
return;
|
|
@@ -221,15 +230,6 @@ export class ModelsDialogComponent {
|
|
|
221
230
|
this.#setScope(next, selected.fullId);
|
|
222
231
|
return;
|
|
223
232
|
}
|
|
224
|
-
if (matchesKey(data, Key.ctrl("c"))) {
|
|
225
|
-
if (this.#input.getValue().length > 0) {
|
|
226
|
-
this.#input.setValue("");
|
|
227
|
-
this.#selectedIndex = 0;
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
this.#callbacks.onCancel();
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
233
|
if (kb.matches(data, "tui.select.cancel")) {
|
|
234
234
|
this.#callbacks.onCancel();
|
|
235
235
|
return;
|
|
@@ -303,13 +303,13 @@ export class ModelsDialogComponent {
|
|
|
303
303
|
const row = rows[index];
|
|
304
304
|
const selected = index === this.#selectedIndex;
|
|
305
305
|
const scoped = this.#scopeIds.includes(row.fullId);
|
|
306
|
-
// Invariant: arrow, scope marker, model id, [provider], then the active checkmark
|
|
306
|
+
// Invariant: arrow, scope marker, model id, [provider], then the active checkmark.
|
|
307
307
|
const prefix = selected ? theme.fg("accent", "→ ") : " ";
|
|
308
|
-
const marker = scoped ? theme.fg("
|
|
309
|
-
const label = selected ? theme.fg("
|
|
308
|
+
const marker = scoped ? theme.fg("accent", "●") : theme.fg("dim", "○");
|
|
309
|
+
const label = selected ? theme.fg("text", row.model.id) : row.model.id;
|
|
310
310
|
const provider = theme.fg("muted", `[${row.model.provider}]`);
|
|
311
311
|
const active = row.fullId === this.#activeModelId ? ` ${theme.fg("success", "✓")}` : "";
|
|
312
|
-
push(`${prefix}${marker} ${label} ${provider}${active}
|
|
312
|
+
push(renderPiModalListRow(`${prefix}${marker} ${label} ${provider}${active}`, width, selected));
|
|
313
313
|
}
|
|
314
314
|
if (startIndex > 0 || endIndex < rows.length)
|
|
315
315
|
push(theme.fg("muted", ` (${this.#selectedIndex + 1}/${rows.length})`));
|
|
@@ -322,7 +322,7 @@ export class ModelsDialogComponent {
|
|
|
322
322
|
push();
|
|
323
323
|
if (this.#refreshStatus !== undefined)
|
|
324
324
|
push(theme.fg(this.#refreshStatus.kind, ` ${this.#refreshStatus.message}`));
|
|
325
|
-
push(
|
|
325
|
+
push(renderHintsWithClose(this.#hints(), width));
|
|
326
326
|
return lines;
|
|
327
327
|
}
|
|
328
328
|
#hints() {
|
|
@@ -330,13 +330,13 @@ export class ModelsDialogComponent {
|
|
|
330
330
|
const confirm = keyLabel("tui.select.confirm");
|
|
331
331
|
const save = keyLabel("app.models.save");
|
|
332
332
|
return [
|
|
333
|
-
{
|
|
333
|
+
{ key: "Type", action: "search" },
|
|
334
334
|
{ key: "↑↓", action: "navigate" },
|
|
335
335
|
...(tab.length === 0 ? [] : [{ key: tab, action: "filter" }]),
|
|
336
336
|
...(confirm.length === 0 ? [] : [{ key: confirm, action: "switch" }]),
|
|
337
337
|
{ key: "space", action: "scope" },
|
|
338
338
|
...(save.length === 0 ? [] : [{ key: save, action: "save" }]),
|
|
339
|
-
|
|
339
|
+
DIALOG_CLOSE_SHORTCUT_HINT,
|
|
340
340
|
];
|
|
341
341
|
}
|
|
342
342
|
#emptyText() {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type PiShellComponentPort, type PiShellEditorOptions } from "./shell-shared-facade.js";
|
|
2
|
+
export interface PiShellOperationLoaderPort extends PiShellComponentPort {
|
|
3
|
+
readonly signal: AbortSignal;
|
|
4
|
+
}
|
|
5
|
+
export declare function createPiShellShareOperationDialog(runtime: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">, message: string): PiShellOperationLoaderPort;
|
|
6
|
+
export declare function createPiShellOperationLoader(runtime: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">, message: string): PiShellOperationLoaderPort;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CancellableLoader, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
2
|
+
import { BorderedLoader, DynamicBorder } from "../startup-public.js";
|
|
3
|
+
import { adoptPiModalFrame, PiModalFrame } from "./modal-frame.js";
|
|
4
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalShortcutHints } from "./theme.js";
|
|
5
|
+
import { componentPort, createTuiFacade, ensureTheme, } from "./shell-shared-facade.js";
|
|
6
|
+
/** Bare A1's titled share progress surface; the pinned comparison keeps its bordered loader. */
|
|
7
|
+
class ShareOperationDialog {
|
|
8
|
+
#loader;
|
|
9
|
+
#frame;
|
|
10
|
+
constructor(runtime, message) {
|
|
11
|
+
const theme = piTheme();
|
|
12
|
+
this.#loader = new CancellableLoader(createTuiFacade(runtime), text => theme.fg("accent", text), text => theme.fg("muted", text), message);
|
|
13
|
+
this.#frame = new PiModalFrame(new DynamicBorder(text => theme.fg("border", text)), [
|
|
14
|
+
new Text(theme.fg("accent", theme.bold("Share")), 0, 0),
|
|
15
|
+
this.#loader,
|
|
16
|
+
new Spacer(1),
|
|
17
|
+
new Text(renderPiModalShortcutHints([DIALOG_CLOSE_SHORTCUT_HINT]), 0, 0),
|
|
18
|
+
], new DynamicBorder(text => theme.fg("border", text)));
|
|
19
|
+
}
|
|
20
|
+
get signal() { return this.#loader.signal; }
|
|
21
|
+
invalidate() { this.#frame.invalidate(); }
|
|
22
|
+
render(width) { return this.#frame.render(width); }
|
|
23
|
+
handleInput(data) { this.#loader.handleInput(data); }
|
|
24
|
+
dispose() { this.#loader.dispose(); }
|
|
25
|
+
}
|
|
26
|
+
export function createPiShellShareOperationDialog(runtime, message) {
|
|
27
|
+
ensureTheme();
|
|
28
|
+
const dialog = new ShareOperationDialog(runtime, message);
|
|
29
|
+
return { ...componentPort(dialog), signal: dialog.signal };
|
|
30
|
+
}
|
|
31
|
+
export function createPiShellOperationLoader(runtime, message) {
|
|
32
|
+
ensureTheme();
|
|
33
|
+
const loader = new BorderedLoader(createTuiFacade(runtime), piTheme(), message, { cancellable: true });
|
|
34
|
+
adoptPiModalFrame(loader, {
|
|
35
|
+
topIndex: 0,
|
|
36
|
+
bottomIndex: loader.children.length - 1,
|
|
37
|
+
preInsetContent: loader.children[3] === undefined ? [] : [loader.children[3]],
|
|
38
|
+
});
|
|
39
|
+
return { ...componentPort(loader), signal: loader.signal };
|
|
40
|
+
}
|
|
@@ -4,11 +4,21 @@ import { PROMPT_HISTORY_EDITOR_REPLACEMENT } from "../../../contracts/owned-ui/i
|
|
|
4
4
|
import { KeybindingsManager } from "./upstream/adjacent/core/keybindings.js";
|
|
5
5
|
import { OwnedEditor } from "./upstream/components/owned-editor.js";
|
|
6
6
|
import { OwnedEditorUxInterception, createPromptSelectionInterceptor, editorVisualLineCount, } from "./owned-editor-ux.js";
|
|
7
|
-
import { PINNED_PI_LAYOUT, piTheme, } from "./theme.js";
|
|
7
|
+
import { PINNED_PI_LAYOUT, piTheme, renderPiListSelection, } from "./theme.js";
|
|
8
8
|
import { createTuiFacade, ensureTheme, isAutocompleteProvider, } from "./shell-shared-facade.js";
|
|
9
9
|
import { SKILLS_COMMAND_NAME, collapseSkillCommands, createSkillsTunnelProvider, } from "./skills-command.js";
|
|
10
|
-
/** A selected autocomplete row:
|
|
10
|
+
/** A selected autocomplete row: its arrow/primary column, then the aligned description. */
|
|
11
11
|
const SELECTED_DESCRIBED_ROW = /^(→ .*?\S)(\s{2,}.*)$/u;
|
|
12
|
+
function renderOwnedAutocompleteSelection(text) {
|
|
13
|
+
const theme = piTheme();
|
|
14
|
+
const row = SELECTED_DESCRIBED_ROW.exec(text);
|
|
15
|
+
const primary = row?.[1] ?? text;
|
|
16
|
+
const hasArrow = primary.startsWith("→ ");
|
|
17
|
+
const arrow = hasArrow ? theme.fg("accent", "→ ") : "";
|
|
18
|
+
const label = theme.fg("text", hasArrow ? primary.slice(2) : primary);
|
|
19
|
+
const description = row === null ? "" : theme.fg("muted", row[2]);
|
|
20
|
+
return renderPiListSelection(arrow + label + description);
|
|
21
|
+
}
|
|
12
22
|
const PINNED_THINKING_SLASH_COMMAND = { name: "thinking", description: "Set thinking level", argumentHint: "<level>" };
|
|
13
23
|
const OWNED_THINKING_SLASH_COMMAND = { name: "thinking", description: "Set thinking level" };
|
|
14
24
|
const OWNED_LOGIN_SLASH_COMMAND = { name: "login", description: "Configure provider authentication" };
|
|
@@ -74,10 +84,9 @@ export function createPiShellEditor(options) {
|
|
|
74
84
|
return selectListTheme.scrollInfo(text);
|
|
75
85
|
},
|
|
76
86
|
selectedText: text => {
|
|
77
|
-
// Rationale:
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
return row === null ? selectListTheme.selectedText(text) : selectListTheme.selectedText(row[1]) + selectListTheme.description(row[2]);
|
|
87
|
+
// Rationale: bare A1 keeps the arrow accented, the actionable label readable, and descriptions muted
|
|
88
|
+
// on the same item-bounded blue surface used by its standard modal selectors.
|
|
89
|
+
return renderOwnedAutocompleteSelection(text);
|
|
81
90
|
},
|
|
82
91
|
},
|
|
83
92
|
}, keybindings, {
|
|
@@ -152,8 +161,12 @@ export function createPiShellEditor(options) {
|
|
|
152
161
|
const resources = commands
|
|
153
162
|
.filter(command => command.name !== collapsed.collapsedCommand?.name && command.source !== "builtin" && !builtInNames.has(command.name))
|
|
154
163
|
.map(command => autocompleteCommand(command));
|
|
155
|
-
const
|
|
156
|
-
|
|
164
|
+
const catalog = [...builtIns, ...resources];
|
|
165
|
+
const combined = new CombinedAutocompleteProvider(catalog, options.cwd ?? process.cwd());
|
|
166
|
+
const tunnelAware = tunnelSkills.length === 0 ? combined : createSkillsTunnelProvider(combined, tunnelSkills);
|
|
167
|
+
autocompleteProvider = options.keybindingProfile === "a1"
|
|
168
|
+
? createDelimiterReadyCommandProvider(tunnelAware, catalog)
|
|
169
|
+
: tunnelAware;
|
|
157
170
|
editor.setAutocompleteProvider(autocompleteProvider);
|
|
158
171
|
};
|
|
159
172
|
setAutocompleteCommands(options.autocompleteCommands ?? []);
|
|
@@ -336,6 +349,40 @@ export function createPiShellEditor(options) {
|
|
|
336
349
|
cancelPendingPastes: () => editorUx?.cancelPendingPastes(),
|
|
337
350
|
};
|
|
338
351
|
}
|
|
352
|
+
/** Bare A1 leaves an applied catalog command ready for `:`, a user-entered space, or submission. */
|
|
353
|
+
function createDelimiterReadyCommandProvider(base, commands) {
|
|
354
|
+
const commandNames = new Set(commands.map(command => command.name));
|
|
355
|
+
return {
|
|
356
|
+
...(base.triggerCharacters === undefined ? {} : { triggerCharacters: base.triggerCharacters }),
|
|
357
|
+
getSuggestions: (lines, cursorLine, cursorCol, options) => base.getSuggestions(lines, cursorLine, cursorCol, options),
|
|
358
|
+
applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
|
|
359
|
+
const result = base.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
|
|
360
|
+
const currentLine = lines[cursorLine] ?? "";
|
|
361
|
+
const textBeforeCursor = currentLine.slice(0, cursorCol);
|
|
362
|
+
const beforePrefix = currentLine.slice(0, cursorCol - prefix.length);
|
|
363
|
+
const completedLine = result.lines[result.cursorLine] ?? "";
|
|
364
|
+
const spacerIndex = result.cursorCol - 1;
|
|
365
|
+
const expectedCommand = `${beforePrefix}/${item.value}`;
|
|
366
|
+
const isCatalogCommand = commandNames.has(item.value)
|
|
367
|
+
&& prefix.startsWith("/")
|
|
368
|
+
&& !prefix.slice(1).includes("/")
|
|
369
|
+
&& !prefix.includes(" ")
|
|
370
|
+
&& textBeforeCursor.trimStart() === prefix;
|
|
371
|
+
if (!isCatalogCommand
|
|
372
|
+
|| result.cursorLine !== cursorLine
|
|
373
|
+
|| spacerIndex < 0
|
|
374
|
+
|| completedLine[spacerIndex] !== " "
|
|
375
|
+
|| completedLine.slice(0, spacerIndex) !== expectedCommand)
|
|
376
|
+
return result;
|
|
377
|
+
const nextLines = [...result.lines];
|
|
378
|
+
nextLines[result.cursorLine] = completedLine.slice(0, spacerIndex) + completedLine.slice(spacerIndex + 1);
|
|
379
|
+
return { ...result, lines: nextLines, cursorCol: spacerIndex };
|
|
380
|
+
},
|
|
381
|
+
...(base.shouldTriggerFileCompletion === undefined ? {} : {
|
|
382
|
+
shouldTriggerFileCompletion: (lines, cursorLine, cursorCol) => base.shouldTriggerFileCompletion(lines, cursorLine, cursorCol),
|
|
383
|
+
}),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
339
386
|
function autocompleteCommand(command, addition) {
|
|
340
387
|
const argumentOptions = addition?.argumentOptions ?? command.argumentOptions;
|
|
341
388
|
return {
|
|
@@ -28,6 +28,9 @@ export interface PiExtensionUiBridgeHost {
|
|
|
28
28
|
}
|
|
29
29
|
export interface PiExtensionUiBridge {
|
|
30
30
|
readonly context: ExtensionUIContext;
|
|
31
|
+
input(title: string, placeholder: string, options?: {
|
|
32
|
+
readonly retainSurfaceOnSettle?: boolean;
|
|
33
|
+
}): Promise<string | undefined>;
|
|
31
34
|
reset(): void;
|
|
32
35
|
dispose(): void;
|
|
33
36
|
}
|
|
@@ -5,7 +5,7 @@ import { ExtensionSelectorComponent } from "./upstream/components/extension-sele
|
|
|
5
5
|
import { setKeybindings, Text, } from "@earendil-works/pi-tui";
|
|
6
6
|
import { KeybindingsManager, } from "./upstream/adjacent/core/keybindings.js";
|
|
7
7
|
import { PINNED_PI_LAYOUT, applyPiTheme, applyPiThemeInstance, getAvailablePiThemes, loadPiTheme, piTheme, } from "./theme.js";
|
|
8
|
-
import { componentPort, createTuiFacade, ensureTheme, isRecord, } from "./shell-shared-facade.js";
|
|
8
|
+
import { componentPort, createTuiFacade, ensureTheme, isRecord, withPiDialogCancel, } from "./shell-shared-facade.js";
|
|
9
9
|
/** Public ExtensionUIContext ported from pinned InteractiveMode without constructing it. */
|
|
10
10
|
export function createPiExtensionUiBridge(host) {
|
|
11
11
|
ensureTheme();
|
|
@@ -45,7 +45,7 @@ export function createPiExtensionUiBridge(host) {
|
|
|
45
45
|
disposers.delete(dispose);
|
|
46
46
|
};
|
|
47
47
|
};
|
|
48
|
-
const showInput = (create, options) => new Promise(resolve => {
|
|
48
|
+
const showInput = (create, options, retainSurfaceOnSettle = false) => new Promise(resolve => {
|
|
49
49
|
let settled = false;
|
|
50
50
|
let surface;
|
|
51
51
|
let untrack = () => { };
|
|
@@ -56,7 +56,10 @@ export function createPiExtensionUiBridge(host) {
|
|
|
56
56
|
if (activeCancel === cancel)
|
|
57
57
|
activeCancel = undefined;
|
|
58
58
|
untrack();
|
|
59
|
-
|
|
59
|
+
if (retainSurfaceOnSettle && activeSurface === surface)
|
|
60
|
+
activeSurface = undefined;
|
|
61
|
+
else
|
|
62
|
+
closeSurface(surface);
|
|
60
63
|
resolve(value);
|
|
61
64
|
};
|
|
62
65
|
const cancel = () => finish(undefined);
|
|
@@ -173,7 +176,7 @@ export function createPiExtensionUiBridge(host) {
|
|
|
173
176
|
component.dispose();
|
|
174
177
|
return;
|
|
175
178
|
}
|
|
176
|
-
surface = componentPort(component);
|
|
179
|
+
surface = withPiDialogCancel(componentPort(component), cancelCustom);
|
|
177
180
|
activeCancel = cancelCustom;
|
|
178
181
|
if (options?.overlay) {
|
|
179
182
|
const overlayOptions = typeof options.overlayOptions === "function" ? options.overlayOptions() : options.overlayOptions;
|
|
@@ -247,6 +250,7 @@ export function createPiExtensionUiBridge(host) {
|
|
|
247
250
|
};
|
|
248
251
|
return {
|
|
249
252
|
context,
|
|
253
|
+
input: (title, placeholder, options) => showInput((resolve, cancel) => componentPort(new ExtensionInputComponent(title, placeholder, resolve, cancel, { tui })), undefined, options?.retainSurfaceOnSettle === true),
|
|
250
254
|
reset,
|
|
251
255
|
dispose() {
|
|
252
256
|
reset();
|
|
@@ -110,10 +110,6 @@ export interface PiShellLoginDialogPort extends PiShellComponentPort {
|
|
|
110
110
|
export declare function createPiShellLoginDialog(runtime: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">, providerId: string, onComplete: (success: boolean, message?: string) => void, providerName?: string, title?: string): PiShellLoginDialogPort;
|
|
111
111
|
export declare function createPiShellArmin(runtime: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">): PiShellComponentPort;
|
|
112
112
|
export declare function createPiShellEarendilAnnouncement(): PiShellComponentPort;
|
|
113
|
-
export interface PiShellOperationLoaderPort extends PiShellComponentPort {
|
|
114
|
-
readonly signal: AbortSignal;
|
|
115
|
-
}
|
|
116
|
-
export declare function createPiShellOperationLoader(runtime: Pick<PiShellEditorOptions, "getColumns" | "getRows" | "requestRender">, message: string): PiShellOperationLoaderPort;
|
|
117
113
|
export declare function createPiShellReloadBox(): PiShellComponentPort;
|
|
118
114
|
export interface PiShellAuthProviderOption extends PiShellSelectorOption {
|
|
119
115
|
readonly providerId: string;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { ArminComponent,
|
|
2
|
-
import { EarendilAnnouncementComponent
|
|
1
|
+
import { ArminComponent, DynamicBorder, getSelectListTheme, LoginDialogComponent, ModelSelectorComponent, OAuthSelectorComponent, SettingsSelectorComponent, ShowImagesSelectorComponent, ThemeSelectorComponent, UserMessageSelectorComponent, } from "../startup-public.js";
|
|
2
|
+
import { EarendilAnnouncementComponent } from "./upstream/components/earendil-announcement.js";
|
|
3
|
+
import { ExtensionSelectorComponent } from "./upstream/components/extension-selector.js";
|
|
3
4
|
import { Box, Container, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
4
|
-
import { ScopedModelsSelectorComponent
|
|
5
|
-
import { adoptPiModalFrame, adoptPiModalHeader } from "./modal-frame.js";
|
|
6
|
-
import { ModelsDialogComponent
|
|
7
|
-
import { TrustSelectorComponent
|
|
8
|
-
import { PINNED_PI_LAYOUT, piTheme, } from "./theme.js";
|
|
5
|
+
import { ScopedModelsSelectorComponent } from "./upstream/components/scoped-models-selector.js";
|
|
6
|
+
import { addPiModalSelectFooter, adoptPiModalFrame, adoptPiModalHeader } from "./modal-frame.js";
|
|
7
|
+
import { ModelsDialogComponent } from "./models-dialog.js";
|
|
8
|
+
import { TrustSelectorComponent } from "./upstream/components/trust-selector.js";
|
|
9
|
+
import { canonicalizePiSettingsHint, DIALOG_CLOSE_SHORTCUT_HINT, PINNED_PI_LAYOUT, piTheme, renderPiModalShortcutHints, } from "./theme.js";
|
|
9
10
|
import { componentFromPort, componentPort, createTuiFacade, ensureTheme, isRecord, } from "./shell-shared-facade.js";
|
|
10
11
|
export { createPiShellSkillsSelector } from "./skills-dialog.js";
|
|
11
12
|
export function createPiShellSelector(options) {
|
|
@@ -16,11 +17,20 @@ export function createPiShellSelector(options) {
|
|
|
16
17
|
list.onSelect = item => options.onSelect?.(item.value);
|
|
17
18
|
if (options.onCancel !== undefined)
|
|
18
19
|
list.onCancel = options.onCancel;
|
|
19
|
-
if (!options.title)
|
|
20
|
+
if (!options.title && options.onCancel === undefined)
|
|
20
21
|
return componentPort(list);
|
|
21
22
|
const container = new Container();
|
|
22
|
-
|
|
23
|
+
if (options.title)
|
|
24
|
+
container.addChild(new Text(piTheme().fg("accent", piTheme().bold(options.title)), PINNED_PI_LAYOUT.contentPaddingX, 0));
|
|
23
25
|
container.addChild(list);
|
|
26
|
+
if (options.onCancel !== undefined) {
|
|
27
|
+
container.addChild(new Spacer(1));
|
|
28
|
+
container.addChild(new Text(renderPiModalShortcutHints([
|
|
29
|
+
{ key: "↑↓", action: "navigate" },
|
|
30
|
+
{ key: "enter", action: "select" },
|
|
31
|
+
DIALOG_CLOSE_SHORTCUT_HINT,
|
|
32
|
+
]), 0, 0));
|
|
33
|
+
}
|
|
24
34
|
return componentPort(container, data => list.handleInput(data));
|
|
25
35
|
}
|
|
26
36
|
export function createPiShellSettingsSelector(options) {
|
|
@@ -68,7 +78,19 @@ export function createPiShellSettingsSelector(options) {
|
|
|
68
78
|
};
|
|
69
79
|
const selector = new SettingsSelectorComponent(options.config, callbacks);
|
|
70
80
|
const settingsList = selector.getSettingsList();
|
|
71
|
-
|
|
81
|
+
const port = componentPort(selector, data => settingsList.handleInput(data));
|
|
82
|
+
return {
|
|
83
|
+
...port,
|
|
84
|
+
// Rationale: Pi's nested settings components own their hint rows and do not expose hint entries.
|
|
85
|
+
// Rebuild only the footer row so the canonical close entry survives narrow clipping.
|
|
86
|
+
render(width) {
|
|
87
|
+
const rows = [...port.render(width)];
|
|
88
|
+
const hintIndex = rows.length - 2;
|
|
89
|
+
if (rows[hintIndex] !== undefined)
|
|
90
|
+
rows[hintIndex] = canonicalizePiSettingsHint(rows[hintIndex], width);
|
|
91
|
+
return rows;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
72
94
|
}
|
|
73
95
|
export function createPiShellModelSelector(options) {
|
|
74
96
|
ensureTheme();
|
|
@@ -139,6 +161,7 @@ export function createPiShellUserMessageSelector(messages, onSelect, onCancel, i
|
|
|
139
161
|
ensureTheme();
|
|
140
162
|
const selector = new UserMessageSelectorComponent(messages.map(message => ({ id: message.id, text: message.label })), onSelect, onCancel, initialSelectedId);
|
|
141
163
|
const list = selector.getMessageList();
|
|
164
|
+
addPiModalSelectFooter(selector);
|
|
142
165
|
adoptPiModalFrame(selector, { topIndex: 4, bottomIndex: selector.children.length - 1 });
|
|
143
166
|
return componentPort(selector, data => list.handleInput(data));
|
|
144
167
|
}
|
|
@@ -146,6 +169,9 @@ export function createPiShellLoginDialog(runtime, providerId, onComplete, provid
|
|
|
146
169
|
ensureTheme();
|
|
147
170
|
const dialog = new LoginDialogComponent(createTuiFacade(runtime), providerId, onComplete, providerName, title);
|
|
148
171
|
const preInsetTitle = dialog.children[1];
|
|
172
|
+
const content = dialog.children[2];
|
|
173
|
+
dialog.children.splice(dialog.children.length - 1, 0, new Spacer(1), new Text(renderPiModalShortcutHints([DIALOG_CLOSE_SHORTCUT_HINT]), 0, 0));
|
|
174
|
+
const removeDependencyHint = () => { content.children.pop(); };
|
|
149
175
|
const header = adoptPiModalHeader(dialog, 0, 1);
|
|
150
176
|
adoptPiModalFrame(dialog, {
|
|
151
177
|
topIndex: 0,
|
|
@@ -157,11 +183,23 @@ export function createPiShellLoginDialog(runtime, providerId, onComplete, provid
|
|
|
157
183
|
...componentPort(dialog),
|
|
158
184
|
showAuth: (url, instructions) => dialog.showAuth(url, instructions),
|
|
159
185
|
showDeviceCode: info => dialog.showDeviceCode(info),
|
|
160
|
-
showManualInput
|
|
161
|
-
|
|
186
|
+
showManualInput(message) {
|
|
187
|
+
const result = dialog.showManualInput(message);
|
|
188
|
+
removeDependencyHint();
|
|
189
|
+
return result;
|
|
190
|
+
},
|
|
191
|
+
showPrompt(message, placeholder) {
|
|
192
|
+
const result = dialog.showPrompt(message, placeholder);
|
|
193
|
+
removeDependencyHint();
|
|
194
|
+
return result;
|
|
195
|
+
},
|
|
162
196
|
showDetails: lines => dialog.showDetails([...lines]),
|
|
163
|
-
|
|
164
|
-
|
|
197
|
+
// Rationale: The persistent canonical footer replaces the dependency's optional close hint.
|
|
198
|
+
showInfo: (message, links = []) => dialog.showInfo(message, [...links], false),
|
|
199
|
+
showWaiting(message) {
|
|
200
|
+
dialog.showWaiting(message);
|
|
201
|
+
removeDependencyHint();
|
|
202
|
+
},
|
|
165
203
|
showProgress: message => dialog.showProgress(message),
|
|
166
204
|
};
|
|
167
205
|
}
|
|
@@ -173,16 +211,6 @@ export function createPiShellEarendilAnnouncement() {
|
|
|
173
211
|
ensureTheme();
|
|
174
212
|
return componentPort(new EarendilAnnouncementComponent());
|
|
175
213
|
}
|
|
176
|
-
export function createPiShellOperationLoader(runtime, message) {
|
|
177
|
-
ensureTheme();
|
|
178
|
-
const loader = new BorderedLoader(createTuiFacade(runtime), piTheme(), message, { cancellable: true });
|
|
179
|
-
adoptPiModalFrame(loader, {
|
|
180
|
-
topIndex: 0,
|
|
181
|
-
bottomIndex: loader.children.length - 1,
|
|
182
|
-
preInsetContent: loader.children[3] === undefined ? [] : [loader.children[3]],
|
|
183
|
-
});
|
|
184
|
-
return { ...componentPort(loader), signal: loader.signal };
|
|
185
|
-
}
|
|
186
214
|
export function createPiShellReloadBox() {
|
|
187
215
|
ensureTheme();
|
|
188
216
|
const container = new Container();
|
|
@@ -207,6 +235,7 @@ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onC
|
|
|
207
235
|
if (selected)
|
|
208
236
|
onSelect(selected.id);
|
|
209
237
|
}, onCancel, initialSearchInput);
|
|
238
|
+
addPiModalSelectFooter(selector);
|
|
210
239
|
const preInsetContent = [selector.children[2], selector.children[6]];
|
|
211
240
|
const header = adoptPiModalHeader(selector, 0, 2);
|
|
212
241
|
adoptPiModalFrame(selector, {
|
|
@@ -219,21 +248,13 @@ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onC
|
|
|
219
248
|
}
|
|
220
249
|
export function createPiShellExtensionSelector(title, options, onSelect, onCancel) {
|
|
221
250
|
ensureTheme();
|
|
222
|
-
|
|
223
|
-
const preInsetContent = [selector.children[2], selector.children[4], selector.children[6]];
|
|
224
|
-
const header = adoptPiModalHeader(selector, 0, 2);
|
|
225
|
-
adoptPiModalFrame(selector, {
|
|
226
|
-
topIndex: 0,
|
|
227
|
-
bottomIndex: selector.children.length - 1,
|
|
228
|
-
header,
|
|
229
|
-
preInsetContent,
|
|
230
|
-
});
|
|
231
|
-
return componentPort(selector);
|
|
251
|
+
return componentPort(new ExtensionSelectorComponent(title, [...options], onSelect, onCancel));
|
|
232
252
|
}
|
|
233
253
|
export function createPiShellThemeSelector(currentTheme, onSelect, onCancel, onPreview) {
|
|
234
254
|
ensureTheme();
|
|
235
255
|
const selector = new ThemeSelectorComponent(currentTheme, onSelect, onCancel, onPreview);
|
|
236
256
|
const list = selector.getSelectList();
|
|
257
|
+
addPiModalSelectFooter(selector);
|
|
237
258
|
adoptPiModalFrame(selector, { topIndex: 0, bottomIndex: selector.children.length - 1 });
|
|
238
259
|
return componentPort(selector, data => list.handleInput(data));
|
|
239
260
|
}
|
|
@@ -241,6 +262,7 @@ export function createPiShellShowImagesSelector(currentValue, onSelect, onCancel
|
|
|
241
262
|
ensureTheme();
|
|
242
263
|
const selector = new ShowImagesSelectorComponent(currentValue, onSelect, onCancel);
|
|
243
264
|
const list = selector.getSelectList();
|
|
265
|
+
addPiModalSelectFooter(selector);
|
|
244
266
|
adoptPiModalFrame(selector, { topIndex: 0, bottomIndex: selector.children.length - 1 });
|
|
245
267
|
return componentPort(selector, data => list.handleInput(data));
|
|
246
268
|
}
|
|
@@ -1,7 +1,40 @@
|
|
|
1
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
1
|
+
import { sliceByColumn, Text, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
2
|
import { formatCacheWarmingStatus } from "./shell-presenters-info.js";
|
|
3
3
|
import { ensureTheme, formatSessionTokens } from "./shell-shared-facade.js";
|
|
4
4
|
import { piTheme } from "./theme.js";
|
|
5
|
+
/** Splits a plain path by visible columns, preserving every source grapheme in order. */
|
|
6
|
+
function splitSessionFile(sessionFile, firstWidth, continuationWidth) {
|
|
7
|
+
const rows = [];
|
|
8
|
+
let remaining = sessionFile;
|
|
9
|
+
let rowWidth = firstWidth;
|
|
10
|
+
while (remaining) {
|
|
11
|
+
let row = sliceByColumn(remaining, 0, rowWidth, true);
|
|
12
|
+
if (!row && rowWidth !== continuationWidth) {
|
|
13
|
+
rows.push("");
|
|
14
|
+
rowWidth = continuationWidth;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
// Compatibility: a one-column frame cannot fit a wide grapheme; preserve it for outer truncation.
|
|
18
|
+
if (!row)
|
|
19
|
+
row = sliceByColumn(remaining, 0, rowWidth);
|
|
20
|
+
rows.push(row);
|
|
21
|
+
remaining = remaining.slice(row.length);
|
|
22
|
+
rowWidth = continuationWidth;
|
|
23
|
+
}
|
|
24
|
+
return rows.length > 0 ? rows : [""];
|
|
25
|
+
}
|
|
26
|
+
/** Keeps the file value on its labelled row before using full continuation rows. */
|
|
27
|
+
function sessionFileRows(sessionFile, width) {
|
|
28
|
+
// Compatibility: match Text's reduced-padding rule so these explicit breaks survive its renderer.
|
|
29
|
+
const paddingX = Math.min(1, Math.max(0, Math.floor((width - 1) / 2)));
|
|
30
|
+
const contentWidth = Math.max(1, width - paddingX * 2);
|
|
31
|
+
const label = piTheme().fg("dim", "File:");
|
|
32
|
+
const firstValueWidth = Math.max(0, contentWidth - visibleWidth("File: "));
|
|
33
|
+
const [first = "", ...continuation] = splitSessionFile(sessionFile, firstValueWidth, contentWidth);
|
|
34
|
+
return firstValueWidth > 0
|
|
35
|
+
? [`${label} ${first}`, ...continuation]
|
|
36
|
+
: [label, ...continuation];
|
|
37
|
+
}
|
|
5
38
|
/** Renders the pinned report values while leaving group names semantic for the owned screen. */
|
|
6
39
|
export function renderPiShellSessionInfoReferenceDocument(presentation, width) {
|
|
7
40
|
ensureTheme();
|
|
@@ -9,7 +42,7 @@ export function renderPiShellSessionInfoReferenceDocument(presentation, width) {
|
|
|
9
42
|
const render = (rows) => new Text(rows.join("\n"), 1, 0).render(width);
|
|
10
43
|
const preamble = [
|
|
11
44
|
...(sessionName ? [`${piTheme().fg("dim", "Name:")} ${sessionName}`] : []),
|
|
12
|
-
|
|
45
|
+
...sessionFileRows(stats.sessionFile ?? "In-memory", width),
|
|
13
46
|
`${piTheme().fg("dim", "ID:")} ${stats.sessionId}`,
|
|
14
47
|
];
|
|
15
48
|
const messages = [
|
|
@@ -46,7 +46,7 @@ export interface PiShellEditorPort extends PiShellComponentPort {
|
|
|
46
46
|
keybindingConfig(): KeybindingsConfig;
|
|
47
47
|
reloadKeybindings(): void;
|
|
48
48
|
/** Uses Pi's terminal decoder rather than assuming one terminal escape spelling. */
|
|
49
|
-
matchesTerminalKey(data: string, key: "home" | "end" | "ctrl+home" | "ctrl+end" | "alt+home" | "ctrl+v"): boolean;
|
|
49
|
+
matchesTerminalKey(data: string, key: "home" | "end" | "pageUp" | "pageDown" | "ctrl+home" | "ctrl+end" | "alt+home" | "ctrl+v"): boolean;
|
|
50
50
|
getText(): string;
|
|
51
51
|
setText(text: string): void;
|
|
52
52
|
insertText(text: string): void;
|
|
@@ -227,6 +227,8 @@ export declare function createTuiFacade(options: Pick<PiShellEditorOptions, "get
|
|
|
227
227
|
readonly onSubmit?: (text: string) => void;
|
|
228
228
|
}): TUI;
|
|
229
229
|
export declare function componentPort(component: Component, handleInput?: (data: string) => void): PiShellComponentPort;
|
|
230
|
+
/** Gives an extension-hosted dialog the shell's implicit close alias without rewriting its component. */
|
|
231
|
+
export declare function withPiDialogCancel(port: PiShellComponentPort, onCancel: () => void): PiShellComponentPort;
|
|
230
232
|
export declare function componentFromPort(port: PiShellComponentPort): Component;
|
|
231
233
|
export declare function formatSessionTokens(count: number): string;
|
|
232
234
|
export declare function isAutocompleteProvider(value: unknown): value is AutocompleteProvider;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hyperlink, setKeybindings, visibleWidth, truncateToWidth, } from "@earendil-works/pi-tui";
|
|
1
|
+
import { hyperlink, matchesKey, setKeybindings, visibleWidth, truncateToWidth, } from "@earendil-works/pi-tui";
|
|
2
2
|
import { KeybindingsManager, } from "./upstream/adjacent/core/keybindings.js";
|
|
3
3
|
import { ensurePiTheme, } from "./theme.js";
|
|
4
4
|
/** Terminal-cell width authority used by Pi-rendered component rows. */
|
|
@@ -61,6 +61,18 @@ export function componentPort(component, handleInput) {
|
|
|
61
61
|
...(typeof disposable.dispose === "function" ? { dispose: () => disposable.dispose?.() } : {}),
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
|
+
/** Gives an extension-hosted dialog the shell's implicit close alias without rewriting its component. */
|
|
65
|
+
export function withPiDialogCancel(port, onCancel) {
|
|
66
|
+
return {
|
|
67
|
+
...port,
|
|
68
|
+
handleInput: data => {
|
|
69
|
+
if (matchesKey(data, "ctrl+c"))
|
|
70
|
+
onCancel();
|
|
71
|
+
else
|
|
72
|
+
port.handleInput?.(data);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
64
76
|
export function componentFromPort(port) {
|
|
65
77
|
return {
|
|
66
78
|
render: width => [...port.render(width)],
|