@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,9 +1,9 @@
|
|
|
1
1
|
import { DynamicBorder } from "../startup-public.js";
|
|
2
2
|
import { Container, getKeybindings, Input, Spacer, Text } from "@earendil-works/pi-tui";
|
|
3
3
|
import { addPiModalHeader, adoptPiModalFrame } from "./modal-frame.js";
|
|
4
|
-
import { PINNED_PI_LAYOUT, piTheme, renderPiModalShortcutHints } from "./theme.js";
|
|
4
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, PINNED_PI_LAYOUT, piTheme, renderPiModalListRow, renderPiModalShortcutHints, } from "./theme.js";
|
|
5
5
|
import { componentPort, ensureTheme, piShellTruncateToWidth, piShellVisibleWidth } from "./shell-shared-facade.js";
|
|
6
|
-
import {
|
|
6
|
+
import { skillMatchesQuery } from "./skills-command.js";
|
|
7
7
|
/** A single line clipped at the viewport width: no wrap, no ellipsis, padded to the full width. */
|
|
8
8
|
class ClippedLine {
|
|
9
9
|
#text;
|
|
@@ -14,10 +14,21 @@ class ClippedLine {
|
|
|
14
14
|
return [line + " ".repeat(Math.max(0, width - piShellVisibleWidth(line)))];
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
class SkillRow {
|
|
18
|
+
#content;
|
|
19
|
+
#selected;
|
|
20
|
+
constructor(content, selected) {
|
|
21
|
+
this.#content = content;
|
|
22
|
+
this.#selected = selected;
|
|
23
|
+
}
|
|
24
|
+
invalidate() { }
|
|
25
|
+
render(width) {
|
|
26
|
+
return [renderPiModalListRow(this.#content, width, this.#selected)];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
17
29
|
/**
|
|
18
|
-
* The A1-owned searchable Skills dialog
|
|
19
|
-
*
|
|
20
|
-
* content follows the v2 skills extension: an accent bold title, `skill:<name>` rows, the selected
|
|
30
|
+
* The A1-owned searchable Skills dialog uses the model selector's public pi-tui composition; its
|
|
31
|
+
* content follows the v2 skills extension: an accent bold title, bare skill-name rows, the selected
|
|
21
32
|
* skill's description cut to one line under the rows, the pinned scroll counter on overflow,
|
|
22
33
|
* and the two empty states. Enter applies the selected skill with no arguments; the query is never appended.
|
|
23
34
|
*/
|
|
@@ -45,9 +56,10 @@ class SkillsSelectorComponent extends Container {
|
|
|
45
56
|
this.addChild(new Spacer(1));
|
|
46
57
|
const bindings = getKeybindings();
|
|
47
58
|
this.addChild(new Text(renderPiModalShortcutHints([
|
|
59
|
+
{ key: "Type", action: "search" },
|
|
48
60
|
{ key: "↑↓", action: "navigate" },
|
|
49
61
|
{ key: bindings.getKeys("tui.select.confirm").join("/"), action: "select" },
|
|
50
|
-
|
|
62
|
+
DIALOG_CLOSE_SHORTCUT_HINT,
|
|
51
63
|
]), 0, 0));
|
|
52
64
|
this.addChild(new DynamicBorder());
|
|
53
65
|
adoptPiModalFrame(this, { topIndex: 0, bottomIndex: this.children.length - 1, header });
|
|
@@ -108,8 +120,9 @@ class SkillsSelectorComponent extends Container {
|
|
|
108
120
|
for (let index = start; index < end; index++) {
|
|
109
121
|
const skill = this.#filtered[index];
|
|
110
122
|
const selected = index === this.#selectedIndex;
|
|
111
|
-
const label =
|
|
112
|
-
|
|
123
|
+
const label = skill.name;
|
|
124
|
+
const content = `${selected ? theme.fg("accent", "→ ") : " "}${selected ? theme.fg("text", label) : label}`;
|
|
125
|
+
this.#listContainer.addChild(new SkillRow(content, selected));
|
|
113
126
|
}
|
|
114
127
|
if (start > 0 || end < this.#filtered.length) {
|
|
115
128
|
this.#listContainer.addChild(new Text(theme.fg("muted", ` (${this.#selectedIndex + 1}/${this.#filtered.length})`), 0, 0));
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import { type SemanticShortcutHint } from "../../../contracts/presentation/index.js";
|
|
1
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, displayShortcutKeyLabel, type SemanticShortcutHint } from "../../../contracts/presentation/index.js";
|
|
2
2
|
export * from "./upstream/theme/theme.js";
|
|
3
3
|
export type PiModalShortcutHint = SemanticShortcutHint;
|
|
4
|
+
export { DIALOG_CLOSE_SHORTCUT_HINT, displayShortcutKeyLabel };
|
|
5
|
+
/** Paint one selected bare-A1 list item with the standard blue selection surface. */
|
|
6
|
+
export declare function renderPiListSelection(content: string): string;
|
|
7
|
+
/** One standard bare-A1 modal row, clipped before its item-bounded selection is painted. */
|
|
8
|
+
export declare function renderPiModalListRow(content: string, width: number, selected: boolean): string;
|
|
4
9
|
/** The common bare-A1 modal footer: quiet display-cased keys, muted names, and whitespace-only gaps. */
|
|
5
10
|
export declare function renderPiModalShortcutHints(entries: readonly PiModalShortcutHint[], indent?: number): string;
|
|
11
|
+
/** Adapts Pi's closed settings footer while reserving the canonical close suffix. */
|
|
12
|
+
export declare function canonicalizePiSettingsHint(row: string, width: number): string;
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import { renderSemanticShortcutHints, } from "../../../contracts/presentation/index.js";
|
|
1
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, displayShortcutKeyLabel, renderSemanticShortcutHints, } from "../../../contracts/presentation/index.js";
|
|
2
|
+
import { stripTerminalSequences, truncateToWidth } from "@earendil-works/pi-tui";
|
|
2
3
|
import { piTheme } from "./upstream/theme/theme.js";
|
|
3
4
|
export * from "./upstream/theme/theme.js";
|
|
5
|
+
export { DIALOG_CLOSE_SHORTCUT_HINT, displayShortcutKeyLabel };
|
|
6
|
+
/** Paint one selected bare-A1 list item with the standard blue selection surface. */
|
|
7
|
+
export function renderPiListSelection(content) {
|
|
8
|
+
return piTheme().bg("selectedBg", content);
|
|
9
|
+
}
|
|
10
|
+
/** One standard bare-A1 modal row, clipped before its item-bounded selection is painted. */
|
|
11
|
+
export function renderPiModalListRow(content, width, selected) {
|
|
12
|
+
const clipped = truncateToWidth(content, Math.max(0, width), "");
|
|
13
|
+
return selected ? renderPiListSelection(clipped) : clipped;
|
|
14
|
+
}
|
|
4
15
|
/** The common bare-A1 modal footer: quiet display-cased keys, muted names, and whitespace-only gaps. */
|
|
5
16
|
export function renderPiModalShortcutHints(entries, indent = 0) {
|
|
6
17
|
const theme = piTheme();
|
|
@@ -9,3 +20,19 @@ export function renderPiModalShortcutHints(entries, indent = 0) {
|
|
|
9
20
|
action: text => theme.fg("muted", text),
|
|
10
21
|
}, indent);
|
|
11
22
|
}
|
|
23
|
+
/** Adapts Pi's closed settings footer while reserving the canonical close suffix. */
|
|
24
|
+
export function canonicalizePiSettingsHint(row, width) {
|
|
25
|
+
const plain = stripTerminalSequences(row);
|
|
26
|
+
const body = plain.includes("Type to search") ? " Type to search · Enter/Space to change"
|
|
27
|
+
: plain.includes("Enter/Space to change") ? " Enter/Space to change"
|
|
28
|
+
: plain.includes("Type to filter") ? " Type to filter · Enter to select"
|
|
29
|
+
: plain.includes("Enter to select") && plain.includes("Esc") ? " Enter to select"
|
|
30
|
+
: /^\s*(?:Type to|Enter(?:\/Space)? to)/u.test(plain) ? plain.replace(/\.{3}$/u, "") : null;
|
|
31
|
+
if (body === null)
|
|
32
|
+
return row;
|
|
33
|
+
const close = `${displayShortcutKeyLabel(DIALOG_CLOSE_SHORTCUT_HINT.key)} ${DIALOG_CLOSE_SHORTCUT_HINT.action}`;
|
|
34
|
+
if (width <= close.length)
|
|
35
|
+
return piTheme().fg("dim", truncateToWidth(close, width, ""));
|
|
36
|
+
const bodyWidth = width - close.length - 3;
|
|
37
|
+
return piTheme().fg("dim", bodyWidth <= 0 ? close : `${truncateToWidth(body, bodyWidth, "…")} · ${close}`);
|
|
38
|
+
}
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* packages/coding-agent/src/core/keybindings.ts.
|
|
4
4
|
* Modifications: Mechanical source port with Node import prefixes, public package-root agent-directory
|
|
5
5
|
* resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling, unbound model
|
|
6
|
-
* selection,
|
|
6
|
+
* selection, Tab tree-filter cycling, tree-local navigation aliases, cross-platform Alt+Up
|
|
7
|
+
* queued-message restoration, and an implicit Ctrl+C selection-cancel alias omitted from shortcut
|
|
8
|
+
* labels.
|
|
7
9
|
* Deviations: keybindings-public-config-boundary, owned-level-cycle-shortcut,
|
|
8
|
-
* owned-input-keybinding-aliases.
|
|
10
|
+
* owned-input-keybinding-aliases, owned-session-tree-dialog, owned-dialog-ctrl-c-cancel.
|
|
9
11
|
*/
|
|
10
12
|
import { type Keybinding, type KeybindingDefinitions, type KeybindingsConfig, type KeyId, KeybindingsManager as TuiKeybindingsManager } from "@earendil-works/pi-tui";
|
|
11
13
|
export interface AppKeybindings {
|
|
@@ -58,6 +60,10 @@ export interface AppKeybindings {
|
|
|
58
60
|
"owned.editor.redo": true;
|
|
59
61
|
"owned.editor.extendLeft": true;
|
|
60
62
|
"owned.editor.extendRight": true;
|
|
63
|
+
"owned.tree.collapse": true;
|
|
64
|
+
"owned.tree.expand": true;
|
|
65
|
+
"owned.tree.first": true;
|
|
66
|
+
"owned.tree.last": true;
|
|
61
67
|
}
|
|
62
68
|
export type AppKeybinding = keyof AppKeybindings;
|
|
63
69
|
export declare function useWindowsKeybindings(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): boolean;
|
|
@@ -437,6 +443,8 @@ export declare class KeybindingsManager extends TuiKeybindingsManager {
|
|
|
437
443
|
static create(agentDir?: string): KeybindingsManager;
|
|
438
444
|
static fromOwnedBindings(bindings?: KeybindingsConfig): KeybindingsManager;
|
|
439
445
|
static createForOwnedInput(agentDir?: string): KeybindingsManager;
|
|
446
|
+
matches(data: string, keybinding: Keybinding): boolean;
|
|
447
|
+
getKeys(keybinding: Keybinding): KeyId[];
|
|
440
448
|
private static createWithDefinitions;
|
|
441
449
|
reload(): void;
|
|
442
450
|
getEffectiveConfig(): KeybindingsConfig;
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
* packages/coding-agent/src/core/keybindings.ts.
|
|
4
4
|
* Modifications: Mechanical source port with Node import prefixes, public package-root agent-directory
|
|
5
5
|
* resolution, and an opt-in bare-A1 input profile including Ctrl+L level cycling, unbound model
|
|
6
|
-
* selection,
|
|
6
|
+
* selection, Tab tree-filter cycling, tree-local navigation aliases, cross-platform Alt+Up
|
|
7
|
+
* queued-message restoration, and an implicit Ctrl+C selection-cancel alias omitted from shortcut
|
|
8
|
+
* labels.
|
|
7
9
|
* Deviations: keybindings-public-config-boundary, owned-level-cycle-shortcut,
|
|
8
|
-
* owned-input-keybinding-aliases.
|
|
10
|
+
* owned-input-keybinding-aliases, owned-session-tree-dialog, owned-dialog-ctrl-c-cancel.
|
|
9
11
|
*/
|
|
10
|
-
import { TUI_KEYBINDINGS, KeybindingsManager as TuiKeybindingsManager, } from "@earendil-works/pi-tui";
|
|
12
|
+
import { matchesKey, TUI_KEYBINDINGS, KeybindingsManager as TuiKeybindingsManager, } from "@earendil-works/pi-tui";
|
|
11
13
|
import { existsSync, readFileSync } from "node:fs";
|
|
12
14
|
import { join } from "node:path";
|
|
13
15
|
import { getAgentDir } from "../../../../startup-public.js";
|
|
@@ -185,6 +187,7 @@ const OWNED_INPUT_KEYBINDINGS = {
|
|
|
185
187
|
"app.thinking.cycle": { ...KEYBINDINGS["app.thinking.cycle"], defaultKeys: "ctrl+l" },
|
|
186
188
|
"app.model.select": { ...KEYBINDINGS["app.model.select"], defaultKeys: [] },
|
|
187
189
|
"app.message.dequeue": { ...KEYBINDINGS["app.message.dequeue"], defaultKeys: "alt+up" },
|
|
190
|
+
"app.tree.filter.cycleForward": { ...KEYBINDINGS["app.tree.filter.cycleForward"], defaultKeys: "tab" },
|
|
188
191
|
"tui.editor.deleteWordBackward": {
|
|
189
192
|
...KEYBINDINGS["tui.editor.deleteWordBackward"],
|
|
190
193
|
defaultKeys: [...KEYBINDINGS["tui.editor.deleteWordBackward"].defaultKeys, "ctrl+backspace"],
|
|
@@ -199,15 +202,17 @@ const OWNED_INPUT_KEYBINDINGS = {
|
|
|
199
202
|
},
|
|
200
203
|
// Ctrl+Z edits the prompt in bare A1 instead of suspending the process.
|
|
201
204
|
"app.suspend": { ...KEYBINDINGS["app.suspend"], defaultKeys: [] },
|
|
202
|
-
// Owned prompt-selection actions
|
|
203
|
-
// actions. Keeping them in the keybinding manager makes the UX declarative,
|
|
204
|
-
// configurable, and independent of terminal escape-sequence spellings.
|
|
205
|
+
// Owned prompt-selection actions remain declarative and take priority.
|
|
205
206
|
"owned.editor.selectAll": { defaultKeys: "ctrl+a", description: "Select all prompt text" },
|
|
206
207
|
"owned.editor.cut": { defaultKeys: "ctrl+x", description: "Cut selected prompt text" },
|
|
207
208
|
"owned.editor.paste": { defaultKeys: "ctrl+v", description: "Paste clipboard text" },
|
|
208
209
|
"owned.editor.redo": { defaultKeys: "ctrl+y", description: "Redo prompt edit" },
|
|
209
210
|
"owned.editor.extendLeft": { defaultKeys: "shift+left", description: "Extend prompt selection left" },
|
|
210
211
|
"owned.editor.extendRight": { defaultKeys: "shift+right", description: "Extend prompt selection right" },
|
|
212
|
+
"owned.tree.collapse": { defaultKeys: "left", description: "Collapse selected tree branch" },
|
|
213
|
+
"owned.tree.expand": { defaultKeys: "right", description: "Expand selected tree branch" },
|
|
214
|
+
"owned.tree.first": { defaultKeys: "home", description: "Select first tree entry" },
|
|
215
|
+
"owned.tree.last": { defaultKeys: "end", description: "Select last tree entry" },
|
|
211
216
|
};
|
|
212
217
|
const KEYBINDING_NAME_MIGRATIONS = {
|
|
213
218
|
cursorUp: "tui.editor.cursorUp",
|
|
@@ -349,6 +354,15 @@ export class KeybindingsManager extends TuiKeybindingsManager {
|
|
|
349
354
|
static createForOwnedInput(agentDir = getAgentDir()) {
|
|
350
355
|
return KeybindingsManager.createWithDefinitions(agentDir, OWNED_INPUT_KEYBINDINGS);
|
|
351
356
|
}
|
|
357
|
+
// Protocol: Ctrl+C is a conventional dialog escape hatch, not advertised keybinding chrome.
|
|
358
|
+
matches(data, keybinding) {
|
|
359
|
+
return keybinding === "tui.select.cancel" && matchesKey(data, "ctrl+c")
|
|
360
|
+
|| super.matches(data, keybinding);
|
|
361
|
+
}
|
|
362
|
+
getKeys(keybinding) {
|
|
363
|
+
const keys = super.getKeys(keybinding);
|
|
364
|
+
return keybinding === "tui.select.cancel" ? keys.filter(key => key !== "ctrl+c") : keys;
|
|
365
|
+
}
|
|
352
366
|
static createWithDefinitions(agentDir, definitions) {
|
|
353
367
|
const configPath = join(agentDir, "keybindings.json");
|
|
354
368
|
const userBindings = KeybindingsManager.loadFromFile(configPath);
|
|
@@ -11,7 +11,7 @@ import { Container, Editor, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
|
11
11
|
import { DynamicBorder, getSelectListTheme } from "../../../startup-public.js";
|
|
12
12
|
import { editInExternalEditor } from "../external-editor.js";
|
|
13
13
|
import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
|
|
14
|
-
import { piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
14
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
15
15
|
export class ExtensionEditorComponent extends Container {
|
|
16
16
|
#editor;
|
|
17
17
|
#tui;
|
|
@@ -54,8 +54,8 @@ export class ExtensionEditorComponent extends Container {
|
|
|
54
54
|
const hint = renderPiModalShortcutHints([
|
|
55
55
|
{ key: this.#keybindings.getKeys("tui.select.confirm").join("/"), action: "submit" },
|
|
56
56
|
{ key: this.#keybindings.getKeys("tui.input.newLine").join("/"), action: "newline" },
|
|
57
|
-
{ key: this.#keybindings.getKeys("tui.select.cancel").join("/"), action: "cancel" },
|
|
58
57
|
{ key: this.#keybindings.getKeys("app.editor.external").join("/"), action: "external editor" },
|
|
58
|
+
DIALOG_CLOSE_SHORTCUT_HINT,
|
|
59
59
|
]);
|
|
60
60
|
this.addChild(new Text(hint, 0, 0));
|
|
61
61
|
this.addChild(new Spacer(1));
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { DynamicBorder } from "../../../startup-public.js";
|
|
11
11
|
import { Container, getKeybindings, Input, Spacer, Text } from "@earendil-works/pi-tui";
|
|
12
12
|
import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
|
|
13
|
-
import { piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
13
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
14
14
|
import { CountdownTimer } from "./countdown-timer.js";
|
|
15
15
|
/** Bare-A1 extension text input using the common modal shortcut row. */
|
|
16
16
|
export class ExtensionInputComponent extends Container {
|
|
@@ -32,7 +32,7 @@ export class ExtensionInputComponent extends Container {
|
|
|
32
32
|
this.#onCancel = onCancel;
|
|
33
33
|
this.#baseTitle = title;
|
|
34
34
|
const theme = piTheme();
|
|
35
|
-
this.#titleText = new Text(theme.fg("accent", title), 0, 0);
|
|
35
|
+
this.#titleText = new Text(theme.fg("accent", theme.bold(title)), 0, 0);
|
|
36
36
|
const header = addPiModalHeader(this, new DynamicBorder(), this.#titleText);
|
|
37
37
|
if (opts?.description) {
|
|
38
38
|
this.addChild(new Spacer(1));
|
|
@@ -42,7 +42,7 @@ export class ExtensionInputComponent extends Container {
|
|
|
42
42
|
this.#countdown = opts?.timeout !== undefined && opts.timeout > 0 && opts.tui !== undefined
|
|
43
43
|
? new CountdownTimer(opts.timeout, opts.tui, seconds => {
|
|
44
44
|
const current = piTheme();
|
|
45
|
-
this.#titleText.setText(current.fg("accent", `${this.#baseTitle} (${seconds}s)`));
|
|
45
|
+
this.#titleText.setText(current.fg("accent", current.bold(`${this.#baseTitle} (${seconds}s)`)));
|
|
46
46
|
}, () => this.#onCancel())
|
|
47
47
|
: undefined;
|
|
48
48
|
if (opts?.initialValue !== undefined)
|
|
@@ -52,9 +52,8 @@ export class ExtensionInputComponent extends Container {
|
|
|
52
52
|
const keys = getKeybindings();
|
|
53
53
|
this.addChild(new Text(renderPiModalShortcutHints([
|
|
54
54
|
{ key: keys.getKeys("tui.select.confirm").join("/"), action: "submit" },
|
|
55
|
-
|
|
55
|
+
DIALOG_CLOSE_SHORTCUT_HINT,
|
|
56
56
|
]), 0, 0));
|
|
57
|
-
this.addChild(new Spacer(1));
|
|
58
57
|
this.addChild(new DynamicBorder());
|
|
59
58
|
adoptPiModalFrame(this, { topIndex: 0, bottomIndex: this.children.length - 1, header });
|
|
60
59
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { DynamicBorder } from "../../../startup-public.js";
|
|
11
11
|
import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
|
|
12
12
|
import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
|
|
13
|
-
import { piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
13
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
14
14
|
import { CountdownTimer } from "./countdown-timer.js";
|
|
15
15
|
/** Bare-A1 extension selector using the common modal shortcut row. */
|
|
16
16
|
export class ExtensionSelectorComponent extends Container {
|
|
@@ -50,9 +50,8 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
50
50
|
this.addChild(new Text(renderPiModalShortcutHints([
|
|
51
51
|
{ key: "↑↓", action: "navigate" },
|
|
52
52
|
{ key: keys.getKeys("tui.select.confirm").join("/"), action: "select" },
|
|
53
|
-
|
|
53
|
+
DIALOG_CLOSE_SHORTCUT_HINT,
|
|
54
54
|
]), 0, 0));
|
|
55
|
-
this.addChild(new Spacer(1));
|
|
56
55
|
this.addChild(new DynamicBorder());
|
|
57
56
|
adoptPiModalFrame(this, { topIndex: 0, bottomIndex: this.children.length - 1, header });
|
|
58
57
|
this.#updateList();
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* private upstream keybinding constructor dependency; bare A1 injects the shared Settings/agent input
|
|
6
6
|
* frame, transient contextual-suggestion branch, and explicit body geometry for selection and
|
|
7
7
|
* above-prompt autocomplete. Bare A1 also clears a sole top-level slash-command search on Escape. Bare
|
|
8
|
-
* A1 also completes a selected tunnel command with `:` and reopens the menu on its
|
|
8
|
+
* A1 also completes a selected or exact completed tunnel command with `:` and reopens the menu on its
|
|
9
|
+
* tunnel rows, and synchronously retains then refreshes an exact top-level command menu after Tab
|
|
10
|
+
* applies its row.
|
|
9
11
|
* Deviations: owned-shared-input-frame, above-prompt-autocomplete-placement,
|
|
10
|
-
* clear-command-search-on-escape, command-tunnel-colon-completion.
|
|
12
|
+
* clear-command-search-on-escape, command-tunnel-colon-completion, keep-command-menu-open-after-tab.
|
|
11
13
|
*/
|
|
12
14
|
import { type EditorOptions, type EditorTheme, type TUI } from "@earendil-works/pi-tui";
|
|
13
15
|
import type { AppKeybinding, KeybindingsManager } from "../adjacent/core/keybindings.js";
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* private upstream keybinding constructor dependency; bare A1 injects the shared Settings/agent input
|
|
6
6
|
* frame, transient contextual-suggestion branch, and explicit body geometry for selection and
|
|
7
7
|
* above-prompt autocomplete. Bare A1 also clears a sole top-level slash-command search on Escape. Bare
|
|
8
|
-
* A1 also completes a selected tunnel command with `:` and reopens the menu on its
|
|
8
|
+
* A1 also completes a selected or exact completed tunnel command with `:` and reopens the menu on its
|
|
9
|
+
* tunnel rows, and synchronously retains then refreshes an exact top-level command menu after Tab
|
|
10
|
+
* applies its row.
|
|
9
11
|
* Deviations: owned-shared-input-frame, above-prompt-autocomplete-placement,
|
|
10
|
-
* clear-command-search-on-escape, command-tunnel-colon-completion.
|
|
12
|
+
* clear-command-search-on-escape, command-tunnel-colon-completion, keep-command-menu-open-after-tab.
|
|
11
13
|
*/
|
|
12
14
|
import { CURSOR_MARKER, Editor, visibleWidth, wrapTextWithAnsi, } from "@earendil-works/pi-tui";
|
|
13
15
|
const PROMPT_GRAPHEMES = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
@@ -78,7 +80,10 @@ export function createOwnedEditorClass(Base) {
|
|
|
78
80
|
handleInput(data) {
|
|
79
81
|
if (this.onExtensionShortcut?.(data))
|
|
80
82
|
return;
|
|
81
|
-
|
|
83
|
+
const tabbedCommand = this.keybindings.matches(data, "tui.input.tab") && this.isTopLevelCommandSearch()
|
|
84
|
+
? retainedAutocomplete(this)
|
|
85
|
+
: undefined;
|
|
86
|
+
if (data === ":" && this.#completeCommandTunnel())
|
|
82
87
|
return;
|
|
83
88
|
if (this.#promptSuggestion !== null
|
|
84
89
|
&& !this.isShowingAutocomplete()
|
|
@@ -133,19 +138,41 @@ export function createOwnedEditorClass(Base) {
|
|
|
133
138
|
}
|
|
134
139
|
}
|
|
135
140
|
super.handleInput(data);
|
|
141
|
+
if (tabbedCommand !== undefined)
|
|
142
|
+
this.#retainCompletedCommandMenu(tabbedCommand);
|
|
143
|
+
}
|
|
144
|
+
/** Keep the current menu visible while refreshing it for the exact completed command. */
|
|
145
|
+
#retainCompletedCommandMenu(completion) {
|
|
146
|
+
const text = this.getText();
|
|
147
|
+
const cursor = this.getCursor();
|
|
148
|
+
if (text !== `/${completion.value}` || cursor.line !== 0 || cursor.col !== text.length)
|
|
149
|
+
return;
|
|
150
|
+
Reflect.set(this, "autocompleteList", completion.list);
|
|
151
|
+
Reflect.set(this, "autocompleteState", completion.state);
|
|
152
|
+
Reflect.set(this, "autocompletePrefix", text);
|
|
153
|
+
triggerAutocomplete(this);
|
|
136
154
|
}
|
|
137
155
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
156
|
+
* Complete either a selected tunnel row or an exact command left by Tab to `/<command>:` and
|
|
157
|
+
* reopen the menu on the tunnel rows. Public setText records the undo snapshot; every other colon
|
|
158
|
+
* stays ordinary text.
|
|
141
159
|
*/
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
#completeCommandTunnel() {
|
|
161
|
+
const tunnels = this.#commandTunnels();
|
|
162
|
+
let command;
|
|
163
|
+
if (this.isTopLevelCommandSearch()) {
|
|
164
|
+
const selected = selectedAutocompleteValue(this);
|
|
165
|
+
if (selected !== undefined && tunnels.includes(selected))
|
|
166
|
+
command = selected;
|
|
167
|
+
}
|
|
168
|
+
else if (!this.isShowingAutocomplete()) {
|
|
169
|
+
const text = this.getText();
|
|
170
|
+
const cursor = this.getCursor();
|
|
171
|
+
command = tunnels.find(candidate => text === `/${candidate}` && cursor.line === 0 && cursor.col === text.length);
|
|
172
|
+
}
|
|
173
|
+
if (command === undefined)
|
|
147
174
|
return false;
|
|
148
|
-
this.setText(`/${
|
|
175
|
+
this.setText(`/${command}:`);
|
|
149
176
|
triggerAutocomplete(this);
|
|
150
177
|
return true;
|
|
151
178
|
}
|
|
@@ -200,6 +227,15 @@ export function createOwnedEditorClass(Base) {
|
|
|
200
227
|
}
|
|
201
228
|
export class OwnedEditor extends createOwnedEditorClass(Editor) {
|
|
202
229
|
}
|
|
230
|
+
/** Capture the active selected command list so exact Tab application can retain it synchronously. */
|
|
231
|
+
function retainedAutocomplete(editor) {
|
|
232
|
+
const value = selectedAutocompleteValue(editor);
|
|
233
|
+
const list = Reflect.get(editor, "autocompleteList");
|
|
234
|
+
const state = Reflect.get(editor, "autocompleteState");
|
|
235
|
+
if (value === undefined || typeof list !== "object" || list === null || (state !== "regular" && state !== "force"))
|
|
236
|
+
return undefined;
|
|
237
|
+
return { value, list, state };
|
|
238
|
+
}
|
|
203
239
|
/** The value of the row the open autocomplete list highlights, read from either editor's list. */
|
|
204
240
|
function selectedAutocompleteValue(editor) {
|
|
205
241
|
const list = Reflect.get(editor, "autocompleteList");
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* search, bulk/provider/reorder actions, dirty state, Ctrl+S persistence, refresh status,
|
|
6
6
|
* cancellation, and focus while remapping theme and public helper imports; local key labels preserve
|
|
7
7
|
* pinned platform formatting before layout without changing binding identities, and bare A1 uses the
|
|
8
|
-
* shared semantic modal shortcut row and compact padded modal frame
|
|
9
|
-
*
|
|
8
|
+
* shared semantic modal shortcut row and compact padded modal frame while immediately closing on the
|
|
9
|
+
* implicit Ctrl+C selection-cancel alias without advertising it.
|
|
10
|
+
* Deviations: owned-modal-shortcut-hints, owned-dialog-ctrl-c-cancel.
|
|
10
11
|
*/
|
|
11
12
|
interface ScopedModel {
|
|
12
13
|
readonly provider: string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Container, fuzzyFilter, getKeybindings, Input,
|
|
1
|
+
import { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
2
2
|
import { DynamicBorder } from "../../../startup-public.js";
|
|
3
3
|
import { getModelSearchText } from "../model-search.js";
|
|
4
4
|
import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
|
|
5
|
-
import { piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
5
|
+
import { DIALOG_CLOSE_SHORTCUT_HINT, piTheme, renderPiModalShortcutHints } from "../../theme.js";
|
|
6
6
|
// Rationale: upstream's capitalized hint formatter is private to the package; the same mapping lives here.
|
|
7
7
|
function keyDisplayText(keybinding) {
|
|
8
8
|
return getKeybindings().getKeys(keybinding)
|
|
@@ -179,6 +179,7 @@ export class ScopedModelsSelectorComponent extends Container {
|
|
|
179
179
|
{ key: reorderKeys, action: "reorder" },
|
|
180
180
|
{ key: keyDisplayText("app.models.save"), action: "save" },
|
|
181
181
|
{ action: countText },
|
|
182
|
+
DIALOG_CLOSE_SHORTCUT_HINT,
|
|
182
183
|
]);
|
|
183
184
|
return this.isDirty ? `${hint} ${theme.fg("warning", "(unsaved)")}` : hint;
|
|
184
185
|
}
|
|
@@ -321,19 +322,7 @@ export class ScopedModelsSelectorComponent extends Container {
|
|
|
321
322
|
this.footerText.setText(this.getFooterText());
|
|
322
323
|
return;
|
|
323
324
|
}
|
|
324
|
-
|
|
325
|
-
if (matchesKey(data, Key.ctrl("c"))) {
|
|
326
|
-
if (this.searchInput.getValue()) {
|
|
327
|
-
this.searchInput.setValue("");
|
|
328
|
-
this.refresh();
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
this.callbacks.onCancel();
|
|
332
|
-
}
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
// Escape - cancel
|
|
336
|
-
if (matchesKey(data, Key.escape)) {
|
|
325
|
+
if (kb.matches(data, "tui.select.cancel")) {
|
|
337
326
|
this.callbacks.onCancel();
|
|
338
327
|
return;
|
|
339
328
|
}
|
|
@@ -45,6 +45,7 @@ declare class SessionList implements Component, Focusable {
|
|
|
45
45
|
private setConfirmingDeletePath;
|
|
46
46
|
private startDeleteConfirmationForSelectedSession;
|
|
47
47
|
private isCurrentSessionPath;
|
|
48
|
+
private sessionPathText;
|
|
48
49
|
invalidate(): void;
|
|
49
50
|
render(width: number): string[];
|
|
50
51
|
private buildTreePrefix;
|
|
@@ -59,6 +60,7 @@ export declare class SessionSelectorComponent extends Container implements Focus
|
|
|
59
60
|
private canRename;
|
|
60
61
|
private sessionList;
|
|
61
62
|
private header;
|
|
63
|
+
private footer;
|
|
62
64
|
private keybindings;
|
|
63
65
|
private scope;
|
|
64
66
|
private sortMode;
|