@timurproko/a1 0.2.7-dev.810 → 0.2.7-dev.831
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/bin/automatic-update.js +7 -0
- package/bin/ui.js +1 -0
- package/dist/app/session-shell/resident-tab-coordinator.d.ts +6 -9
- package/dist/app/session-shell/resident-tab-coordinator.js +9 -52
- package/dist/app/session-shell/session-shell-root.d.ts +9 -3
- package/dist/app/session-shell/session-shell.js +70 -20
- package/dist/app/session-shell/session-viewport-controller.js +6 -4
- package/dist/app/session-shell/transcript-document.js +1 -1
- package/dist/composition/owned-ui.d.ts +3 -11
- package/dist/composition/owned-ui.js +93 -43
- package/dist/composition/resident-tab-bridge.d.ts +10 -1
- package/dist/composition/resident-tab-bridge.js +21 -0
- package/dist/composition/settings-route-host.d.ts +12 -2
- package/dist/composition/settings-route-host.js +27 -9
- package/dist/composition/settings-search-input.d.ts +9 -0
- package/dist/composition/settings-search-input.js +58 -0
- package/dist/contracts/owned-ui/session-backend.d.ts +2 -0
- package/dist/contracts/owned-ui/session-workflows.d.ts +7 -3
- package/dist/contracts/presentation/index.d.ts +11 -0
- package/dist/contracts/presentation/index.js +12 -0
- package/dist/features/launch/development-launch.js +6 -0
- package/dist/features/launch/profile-paths.js +4 -0
- package/dist/features/owned-ui/plus-menu-dialog.d.ts +22 -0
- package/dist/features/owned-ui/plus-menu-dialog.js +76 -0
- package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
- package/dist/features/owned-ui/reference-screen-app.js +12 -2
- package/dist/features/owned-ui/settings-app.d.ts +10 -3
- package/dist/features/owned-ui/settings-app.js +98 -45
- package/dist/features/owned-ui/settings-search-input.d.ts +29 -0
- package/dist/features/owned-ui/settings-search-input.js +1 -0
- package/dist/features/prompt-history/index.d.ts +1 -1
- package/dist/features/prompt-history/paths.d.ts +2 -1
- package/dist/features/prompt-history/paths.js +10 -2
- package/dist/features/prompt-history/service.d.ts +5 -0
- package/dist/features/prompt-history/service.js +1 -1
- package/dist/features/resident-tab/shortcut-declarations.d.ts +1 -1
- package/dist/features/resident-tab/shortcut-declarations.js +2 -0
- package/dist/foundation/launch-context/index.d.ts +2 -0
- package/dist/foundation/launch-context/index.js +1 -0
- package/dist/foundation/release/automatic-update.d.ts +37 -0
- package/dist/foundation/release/automatic-update.js +265 -0
- package/dist/foundation/release/bootstrap.d.ts +1 -9
- package/dist/foundation/release/bootstrap.js +7 -3
- package/dist/foundation/release/index.d.ts +1 -1
- package/dist/foundation/release/index.js +1 -1
- package/dist/foundation/release/latest-release.d.ts +3 -2
- package/dist/foundation/release/latest-release.js +1 -1
- package/dist/foundation/release/update-execution-lease.d.ts +36 -0
- package/dist/foundation/release/update-execution-lease.js +145 -0
- package/dist/foundation/release/update.d.ts +8 -6
- package/dist/foundation/release/update.js +232 -167
- package/dist/foundation/resident-tabs/bridge-client.d.ts +9 -3
- package/dist/foundation/resident-tabs/bridge-client.js +14 -7
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- package/dist/foundation/resident-tabs/settings.d.ts +0 -1
- package/dist/foundation/resident-tabs/settings.js +0 -2
- package/dist/integrations/pi/components/editor-interaction.d.ts +2 -0
- package/dist/integrations/pi/components/modal-frame.d.ts +17 -1
- package/dist/integrations/pi/components/modal-frame.js +48 -9
- package/dist/integrations/pi/components/models-dialog.js +4 -52
- package/dist/integrations/pi/components/owned-editor-ux.d.ts +3 -0
- package/dist/integrations/pi/components/owned-editor-ux.js +75 -4
- package/dist/integrations/pi/components/shell-editor-autocomplete.js +57 -37
- package/dist/integrations/pi/components/shell-footer-status.js +19 -3
- package/dist/integrations/pi/components/shell-presenters-transcript.js +4 -1
- package/dist/integrations/pi/components/shell-selectors-dialogs.js +4 -1
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -2
- package/dist/integrations/pi/components/skills-dialog.js +2 -2
- package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +7 -5
- package/dist/integrations/pi/components/upstream/components/owned-editor.js +15 -8
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +3 -3
- package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +2 -2
- package/dist/integrations/pi/components/upstream/components/session-footer.js +26 -23
- package/dist/integrations/pi/components/upstream/components/session-selector.js +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +7 -6
- package/dist/integrations/pi/components/upstream/components/thinking-selector.js +9 -8
- package/dist/integrations/pi/components/upstream/components/tool-execution.js +6 -4
- package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +8 -8
- package/dist/integrations/pi/components/upstream/components/tree-selector.js +10 -10
- package/dist/integrations/pi/components/upstream/history/editor-core.js +2 -0
- package/dist/integrations/pi/engine/adapter.js +7 -2
- package/dist/integrations/pi/engine/ports.d.ts +1 -1
- package/dist/integrations/pi/engine/ports.js +4 -0
- package/dist/integrations/pi/engine/workflow-runner.js +9 -2
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/darwin-arm64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-arm64/terminal-host.node +0 -0
- package/dist/native/darwin-x64/manifest.json +1 -1
- package/dist/native/darwin-x64/terminal-host.manifest.json +3 -3
- package/dist/native/darwin-x64/terminal-host.node +0 -0
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/linux-x64/terminal-host.manifest.json +3 -3
- package/dist/native/linux-x64/terminal-host.node +0 -0
- package/dist/native/win32-x64/manifest.json +2 -2
- package/dist/native/win32-x64/process-guardian.node +0 -0
- package/dist/native/win32-x64/terminal-host.manifest.json +3 -3
- package/dist/native/win32-x64/terminal-host.node +0 -0
- package/dist/product-identity.d.ts +1 -1
- package/dist/product-identity.js +1 -1
- package/dist/product-identity.json +1 -0
- package/dist/ui/apps/host.d.ts +2 -0
- package/dist/ui/apps/host.js +7 -1
- package/dist/ui/components/dialog-panel.js +35 -30
- package/dist/ui/components/line-input.d.ts +3 -0
- package/dist/ui/components/line-input.js +3 -1
- package/dist/ui/components/prompt-input.d.ts +5 -4
- package/dist/ui/components/prompt-input.js +8 -6
- package/dist/ui/components/submitted-prompt.d.ts +2 -2
- package/dist/ui/components/submitted-prompt.js +3 -3
- package/dist/ui/components/surface.js +4 -2
- package/dist/ui/components/transcript-viewport.d.ts +1 -1
- package/dist/ui/components/transcript-viewport.js +141 -24
- package/dist/ui/settings/declarations.d.ts +65 -15
- package/dist/ui/settings/declarations.js +45 -13
- package/dist/ui/settings/manager.js +7 -3
- package/dist/ui/settings/migrations.js +22 -0
- package/dist/ui/settings/sections.d.ts +4 -0
- package/dist/ui/settings/sections.js +9 -3
- package/docs/architecture/resident-tabs.md +39 -11
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/features/prompt-history.md +41 -20
- package/package.json +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { LineInput, displayWidth, handleLineInputKey, renderInputRow, truncateToWidth, } from "../../ui/components/index.js";
|
|
2
|
+
/** The A1 setting holding the tab kinds the strip's `+` offers. */
|
|
3
|
+
export const PLUS_MENU_SETTING_ID = "tabBarPlusMenu";
|
|
4
|
+
const TITLE = "Plus Menu";
|
|
5
|
+
const DESCRIPTION = "Choose what the + button in the tab bar can add.";
|
|
6
|
+
const HINT = "Type search · ↑↓ navigate · Space toggle · Esc close";
|
|
7
|
+
/**
|
|
8
|
+
* The plus-menu dialog: every tab kind with `●` when `+` offers it and `○` when not, filtered by typed
|
|
9
|
+
* text. It owns only its filter and the row in hand; the enabled record and its saving stay with the
|
|
10
|
+
* Settings screen that opened it.
|
|
11
|
+
*/
|
|
12
|
+
export class PlusMenuDialog {
|
|
13
|
+
#kinds;
|
|
14
|
+
#filter = new LineInput("");
|
|
15
|
+
#index = 0;
|
|
16
|
+
constructor(kinds) {
|
|
17
|
+
this.#kinds = kinds;
|
|
18
|
+
}
|
|
19
|
+
/** The kinds the filter keeps, in kind order. */
|
|
20
|
+
shown() {
|
|
21
|
+
const needle = this.#filter.value.trim().toLowerCase();
|
|
22
|
+
return this.#kinds.filter(kind => needle.length === 0 || kind.label.toLowerCase().includes(needle));
|
|
23
|
+
}
|
|
24
|
+
/** Handles one key by its resolved name; `toggle` flips a kind. Returns whether the dialog closed. */
|
|
25
|
+
handle(name, data, toggle) {
|
|
26
|
+
const shown = this.shown();
|
|
27
|
+
if (name === "escape")
|
|
28
|
+
return "close";
|
|
29
|
+
if (name === "up" || name === "down") {
|
|
30
|
+
this.#index = Math.min(Math.max(0, shown.length - 1), Math.max(0, this.#index + (name === "down" ? 1 : -1)));
|
|
31
|
+
return "handled";
|
|
32
|
+
}
|
|
33
|
+
if (name === "space") {
|
|
34
|
+
const kind = shown[this.#index];
|
|
35
|
+
if (kind !== undefined)
|
|
36
|
+
toggle(kind.key);
|
|
37
|
+
return "handled";
|
|
38
|
+
}
|
|
39
|
+
handleLineInputKey(this.#filter, data);
|
|
40
|
+
this.#index = 0;
|
|
41
|
+
return "handled";
|
|
42
|
+
}
|
|
43
|
+
lines(enabled, width, theme) {
|
|
44
|
+
const rule = theme.fg("border", "─".repeat(Math.max(0, width)));
|
|
45
|
+
const inset = width > 0 ? " " : "";
|
|
46
|
+
const contentWidth = Math.max(0, width - inset.length);
|
|
47
|
+
const row = (painted, raw) => {
|
|
48
|
+
const clipped = truncateToWidth(painted, contentWidth);
|
|
49
|
+
return `${inset}${clipped}${" ".repeat(Math.max(0, contentWidth - displayWidth(raw)))}`;
|
|
50
|
+
};
|
|
51
|
+
const shown = this.shown();
|
|
52
|
+
const index = Math.min(this.#index, Math.max(0, shown.length - 1));
|
|
53
|
+
const rows = shown.length === 0
|
|
54
|
+
? [row(theme.fg("dim", "No tabs match"), "No tabs match")]
|
|
55
|
+
: shown.map((kind, position) => {
|
|
56
|
+
const selected = position === index;
|
|
57
|
+
const cursor = selected ? "→ " : " ";
|
|
58
|
+
const marker = enabled[kind.key] === true ? "●" : "○";
|
|
59
|
+
const raw = `${cursor}${marker} ${kind.label}`;
|
|
60
|
+
const content = `${selected ? theme.fg("accent", cursor) : cursor}${theme.fg("text", marker)} ${theme.fg("text", kind.label)}`;
|
|
61
|
+
return row(selected ? theme.highlight(content) : content, raw);
|
|
62
|
+
});
|
|
63
|
+
return [
|
|
64
|
+
rule,
|
|
65
|
+
row(theme.bold(theme.fg("accent", TITLE)), TITLE),
|
|
66
|
+
row(theme.fg("dim", DESCRIPTION), DESCRIPTION),
|
|
67
|
+
"",
|
|
68
|
+
...renderInputRow(this.#filter, width, { ruled: false, promptGlyph: "> ", theme }).lines,
|
|
69
|
+
"",
|
|
70
|
+
...rows,
|
|
71
|
+
"",
|
|
72
|
+
row(theme.fg("dim", HINT), HINT),
|
|
73
|
+
rule,
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -24,6 +24,8 @@ export interface ReferenceScreenOptions {
|
|
|
24
24
|
readonly document: ReferenceDocumentProvider;
|
|
25
25
|
/** The reader's scroll settings, read on every frame so a live change is honored. */
|
|
26
26
|
readonly scrollSettings: () => OwnedUiViewportSettings;
|
|
27
|
+
/** The screen is a resident tab, such as the Changelog tab: it offers no screen-level close. */
|
|
28
|
+
readonly pinned?: boolean;
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
31
|
* Presents one read-only document full screen between two accent rules: the bold
|
|
@@ -48,6 +48,7 @@ export class ReferenceScreenApp {
|
|
|
48
48
|
#title;
|
|
49
49
|
#document;
|
|
50
50
|
#scrollSettings;
|
|
51
|
+
#pinned;
|
|
51
52
|
#scroll = 0;
|
|
52
53
|
#interruptArmed = false;
|
|
53
54
|
#failure = null;
|
|
@@ -67,6 +68,7 @@ export class ReferenceScreenApp {
|
|
|
67
68
|
this.#title = options.title;
|
|
68
69
|
this.#document = options.document;
|
|
69
70
|
this.#scrollSettings = options.scrollSettings;
|
|
71
|
+
this.#pinned = options.pinned === true;
|
|
70
72
|
}
|
|
71
73
|
onActivate(host) {
|
|
72
74
|
const ready = this.#document.ready;
|
|
@@ -139,7 +141,7 @@ export class ReferenceScreenApp {
|
|
|
139
141
|
const withRail = withScrollbarRail(body.slice(0, bodyHeight), geometry, contentWidth, theme, { presentation });
|
|
140
142
|
const hint = this.#interruptArmed
|
|
141
143
|
? theme.fg("dim", " press ctrl+c again to exit a1")
|
|
142
|
-
: renderShortcutHintsWithClose(
|
|
144
|
+
: renderShortcutHintsWithClose(this.#hintEntries(), theme, rect.width, 1);
|
|
143
145
|
// Compatibility: the v2 reference screen frames its document between two border-coloured rules.
|
|
144
146
|
const rule = theme.fg("border", "─".repeat(rect.width));
|
|
145
147
|
// Invariant: dialog chrome shares the title's one-cell inset; document content keeps its own layout.
|
|
@@ -153,7 +155,8 @@ export class ReferenceScreenApp {
|
|
|
153
155
|
return { consumed: false };
|
|
154
156
|
switch (action) {
|
|
155
157
|
case "close":
|
|
156
|
-
|
|
158
|
+
if (!this.#pinned)
|
|
159
|
+
host.close();
|
|
157
160
|
return { consumed: true };
|
|
158
161
|
case "up":
|
|
159
162
|
this.#scrollBy(-1);
|
|
@@ -216,6 +219,13 @@ export class ReferenceScreenApp {
|
|
|
216
219
|
}
|
|
217
220
|
return { owned: true, changed: true };
|
|
218
221
|
}
|
|
222
|
+
// Invariant: a pinned screen is its tab and `Esc` does not close it, so it shows no close hint.
|
|
223
|
+
#hintEntries() {
|
|
224
|
+
const entries = REFERENCE_SCREEN_SHORTCUTS.hintEntries(SCOPE);
|
|
225
|
+
if (!this.#pinned)
|
|
226
|
+
return entries;
|
|
227
|
+
return entries.filter(entry => entry.key !== DIALOG_CLOSE_SHORTCUT_HINT.key || entry.action !== DIALOG_CLOSE_SHORTCUT_HINT.action);
|
|
228
|
+
}
|
|
219
229
|
// Invariant: the title is the first document row, as in v2, so it scrolls with the document.
|
|
220
230
|
#content(width, theme) {
|
|
221
231
|
// Compatibility: one leading space, so the title lines up with the padded Markdown rows as in v2.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { AppHostServices, UiApp } from "../../ui/apps/index.js";
|
|
2
2
|
import { ShortcutRegistry, type PaneInputResult, type PaneMouseEvent, type PaneRect } from "../../ui/components/index.js";
|
|
3
3
|
import type { OwnedSettingsManager } from "../../ui/settings/index.js";
|
|
4
|
+
import type { SettingsSearchInputFactory } from "./settings-search-input.js";
|
|
4
5
|
export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
|
|
5
|
-
type Action = "move-up" | "move-down" | "block-up" | "block-down" | "first" | "last" | "page-up" | "page-down" | "previous-value" | "next-value" | "activate" | "
|
|
6
|
+
type Action = "move-up" | "move-down" | "block-up" | "block-down" | "first" | "last" | "page-up" | "page-down" | "previous-value" | "next-value" | "activate" | "edit-filter" | "close" | "undo" | "part-previous" | "part-next" | "part-change";
|
|
6
7
|
export declare const SETTINGS_SHORTCUTS: ShortcutRegistry<Action>;
|
|
7
8
|
/**
|
|
8
9
|
* The settings screen. Rows, sticky headers, scrolling, and the keymap come from
|
|
@@ -12,9 +13,15 @@ export declare const SETTINGS_SHORTCUTS: ShortcutRegistry<Action>;
|
|
|
12
13
|
export declare class SettingsApp implements UiApp {
|
|
13
14
|
#private;
|
|
14
15
|
readonly id = "settings";
|
|
15
|
-
/**
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* `pinned` presents the screen as a resident Settings tab, which offers no screen-level close;
|
|
18
|
+
* `configurePlusMenu` opens the tab bar's Plus Menu dialog in place of this screen's own.
|
|
19
|
+
*/
|
|
20
|
+
constructor(session: OwnedSettingsManager, options: {
|
|
17
21
|
readonly pinned?: boolean;
|
|
22
|
+
readonly searchInputFactory: SettingsSearchInputFactory;
|
|
23
|
+
readonly copyText?: (text: string) => void;
|
|
24
|
+
readonly configurePlusMenu?: (() => boolean) | null;
|
|
18
25
|
});
|
|
19
26
|
onActivate(host: AppHostServices): void;
|
|
20
27
|
onClose(_host: AppHostServices): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DIALOG_BACK_SHORTCUT_HINT, DIALOG_CLOSE_SHORTCUT_HINT } from "../../contracts/presentation/index.js";
|
|
2
|
-
import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, numericValues, RAIL_COLUMNS, renderDialogPanel, renderSteppedDialogPanel, renderEmptyState, renderGroupHeader,
|
|
2
|
+
import { GLOBAL_SCOPE, LineInput, PLAIN_THEME, ShortcutRegistry, assertNoShortcutConflicts, blockJumpTarget, numericValues, RAIL_COLUMNS, renderDialogPanel, renderSteppedDialogPanel, renderEmptyState, renderGroupHeader, renderListRow, renderNote, renderShortcutHintsWithClose, menuRowAt, regionAt, renderValueMenu, withScrollbarRail, stepperEnds, steppedValue, truncateToWidth, valueColumnFor, valueMenuFrame, blockRowSpan, displayWidth, formatLocalTime, handleLineInputKey, humanizeLabel, humanizeTitle, indexOfKey, layoutList, moveSelection, rowKey, scrollForSelection, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, ScrollbarRails, scrollbarWheelRows, selectableIndexes, } from "../../ui/components/index.js";
|
|
3
|
+
import { PLUS_MENU_SETTING_ID, PlusMenuDialog } from "./plus-menu-dialog.js";
|
|
3
4
|
import { SETTINGS_APP_ID } from "./settings-route.js";
|
|
4
5
|
export { SETTINGS_APP_ID, SETTINGS_ROUTE } from "./settings-route.js";
|
|
5
6
|
const SCOPE = SETTINGS_APP_ID;
|
|
@@ -19,11 +20,10 @@ const RAIL_KEY = "settings";
|
|
|
19
20
|
// Compatibility: the transcript rail stays lit this long after a scroll, and repaints just after.
|
|
20
21
|
const SCROLL_LINGER_MS = 900;
|
|
21
22
|
const SCROLL_LINGER_REPAINT_MS = 925;
|
|
22
|
-
const SEARCH_PLACEHOLDER = "search settings";
|
|
23
23
|
/** What a structured value offers instead of printing itself. */
|
|
24
24
|
const CONFIGURE = "configure";
|
|
25
25
|
export const SETTINGS_SHORTCUTS = new ShortcutRegistry();
|
|
26
|
-
SETTINGS_SHORTCUTS.declare({ key: "
|
|
26
|
+
SETTINGS_SHORTCUTS.declare({ key: "edit", scope: SCOPE, description: "Search settings", section: "Change", hint: { keys: "Type", does: "search" } }, "edit-filter");
|
|
27
27
|
SETTINGS_SHORTCUTS.declare({ key: "up", scope: SCOPE, description: "Previous setting", section: "Navigate", hint: { keys: "↑↓", does: "navigate" } }, "move-up");
|
|
28
28
|
SETTINGS_SHORTCUTS.declare({ key: "down", scope: SCOPE, description: "Next setting", section: "Navigate", hint: { keys: "↑↓", does: "navigate" } }, "move-down");
|
|
29
29
|
SETTINGS_SHORTCUTS.declare({ key: "shift+up", scope: SCOPE, description: "Previous section", section: "Navigate", hint: { keys: "Shift+↑↓", does: "jump" } }, "block-up");
|
|
@@ -78,7 +78,6 @@ const KEYS = {
|
|
|
78
78
|
"\r": "enter",
|
|
79
79
|
"\n": "enter",
|
|
80
80
|
" ": "space",
|
|
81
|
-
"/": "/",
|
|
82
81
|
"\u001a": "ctrl+z",
|
|
83
82
|
};
|
|
84
83
|
/**
|
|
@@ -94,8 +93,7 @@ export class SettingsApp {
|
|
|
94
93
|
// Invariant: keyboard navigation requests visibility once; pointer scrolling then stays free.
|
|
95
94
|
#selectionNeedsReveal = true;
|
|
96
95
|
#reveal;
|
|
97
|
-
#
|
|
98
|
-
#scrollBeforeFilter = null;
|
|
96
|
+
#search;
|
|
99
97
|
#menu = null;
|
|
100
98
|
#structured = null;
|
|
101
99
|
// Invariant: pending values remain visible until the source reflects them.
|
|
@@ -120,11 +118,20 @@ export class SettingsApp {
|
|
|
120
118
|
#activeUntil = 0;
|
|
121
119
|
#activityTimer;
|
|
122
120
|
#renderedScroll;
|
|
123
|
-
#
|
|
124
|
-
/**
|
|
125
|
-
|
|
121
|
+
#options;
|
|
122
|
+
/**
|
|
123
|
+
* `pinned` presents the screen as a resident Settings tab, which offers no screen-level close;
|
|
124
|
+
* `configurePlusMenu` opens the tab bar's Plus Menu dialog in place of this screen's own.
|
|
125
|
+
*/
|
|
126
|
+
constructor(session, options) {
|
|
126
127
|
this.#session = session;
|
|
127
|
-
this.#
|
|
128
|
+
this.#options = { pinned: options.pinned === true, configurePlusMenu: options.configurePlusMenu ?? null };
|
|
129
|
+
this.#search = {
|
|
130
|
+
input: null,
|
|
131
|
+
scrollBefore: null,
|
|
132
|
+
factory: options.searchInputFactory,
|
|
133
|
+
copyText: options.copyText ?? (() => { }),
|
|
134
|
+
};
|
|
128
135
|
}
|
|
129
136
|
onActivate(host) {
|
|
130
137
|
this.#closed = false;
|
|
@@ -134,6 +141,8 @@ export class SettingsApp {
|
|
|
134
141
|
this.#closed = true;
|
|
135
142
|
this.#undoHistory.length = 0;
|
|
136
143
|
this.#undoRequests = 0;
|
|
144
|
+
this.#search.input?.setFocused(false);
|
|
145
|
+
this.#search.input = null;
|
|
137
146
|
this.#clearActivityTimer();
|
|
138
147
|
this.#rails.clear();
|
|
139
148
|
}
|
|
@@ -144,7 +153,7 @@ export class SettingsApp {
|
|
|
144
153
|
const footer = this.#footerLines(rect.width, theme);
|
|
145
154
|
// Invariant: the shared search input and structured panel each begin with their own standard rule.
|
|
146
155
|
// That rule replaces the ordinary content/footer divider instead of stacking beneath it.
|
|
147
|
-
const dividerRows = this.#
|
|
156
|
+
const dividerRows = this.#search.input === null && this.#structured === null ? SETTINGS_FOOTER_DIVIDER_ROWS : 0;
|
|
148
157
|
const topRows = Math.min(SETTINGS_TOP_RULE_ROWS, rect.height);
|
|
149
158
|
const contentHeight = Math.max(0, rect.height - topRows - dividerRows - footer.length);
|
|
150
159
|
let titleRows = this.#scroll <= 0 ? Math.min(SETTINGS_TITLE_ROWS, contentHeight) : 0;
|
|
@@ -205,7 +214,7 @@ export class SettingsApp {
|
|
|
205
214
|
const body = [];
|
|
206
215
|
this.#frameRows = [];
|
|
207
216
|
if (rows.length === 0) {
|
|
208
|
-
body.push(...renderEmptyState("No settings found
|
|
217
|
+
body.push(...renderEmptyState("No settings found", "👀", bodyHeight, contentWidth, theme));
|
|
209
218
|
}
|
|
210
219
|
else {
|
|
211
220
|
if (layout.topPadding > 0)
|
|
@@ -240,27 +249,32 @@ export class SettingsApp {
|
|
|
240
249
|
return frame.slice(0, rect.height).concat(Array(Math.max(0, rect.height - frame.length)).fill(""));
|
|
241
250
|
}
|
|
242
251
|
onInput(data, host) {
|
|
252
|
+
if (data === "\u0003" && this.#search.input === null)
|
|
253
|
+
return { consumed: false };
|
|
243
254
|
if (this.#structured !== null)
|
|
244
255
|
return this.#structuredKey(data);
|
|
245
256
|
if (this.#menu !== null)
|
|
246
257
|
return this.#menuKey(data);
|
|
247
|
-
if (this.#
|
|
258
|
+
if (this.#search.input !== null)
|
|
248
259
|
return this.#filterKey(data);
|
|
249
|
-
const key = KEYS[data]
|
|
250
|
-
const action = SETTINGS_SHORTCUTS.resolve(key, SCOPE);
|
|
251
|
-
if (action === null)
|
|
252
|
-
|
|
260
|
+
const key = KEYS[data];
|
|
261
|
+
const action = key === undefined ? null : SETTINGS_SHORTCUTS.resolve(key, SCOPE);
|
|
262
|
+
if (action === null) {
|
|
263
|
+
if (!this.#search.factory.isTextInput(data))
|
|
264
|
+
return { consumed: false };
|
|
265
|
+
openSettingsSearch(this.#search, host, data, this.#scroll);
|
|
266
|
+
this.#scroll = 0;
|
|
267
|
+
return { consumed: true };
|
|
268
|
+
}
|
|
253
269
|
const rows = this.#rows();
|
|
254
270
|
const selected = indexOfKey(rows, this.#selectedKey);
|
|
255
271
|
switch (action) {
|
|
256
272
|
case "close":
|
|
257
|
-
if (!this.#pinned)
|
|
273
|
+
if (!this.#options.pinned)
|
|
258
274
|
host.close();
|
|
259
275
|
return { consumed: true };
|
|
260
|
-
case "
|
|
261
|
-
|
|
262
|
-
this.#filter = new LineInput("");
|
|
263
|
-
return { consumed: true };
|
|
276
|
+
case "edit-filter":
|
|
277
|
+
return { consumed: false };
|
|
264
278
|
case "activate": {
|
|
265
279
|
const row = rows[selected];
|
|
266
280
|
if (row?.kind === "element" && row.value.structured)
|
|
@@ -310,6 +324,9 @@ export class SettingsApp {
|
|
|
310
324
|
}
|
|
311
325
|
}
|
|
312
326
|
onMouse(event, _host) {
|
|
327
|
+
const searchPointer = handleSettingsSearchPointer(this.#search, event, this.#panelTopForFrame);
|
|
328
|
+
if (searchPointer !== null)
|
|
329
|
+
return searchPointer;
|
|
313
330
|
const menu = this.#menu;
|
|
314
331
|
const frame = this.#menuFrame;
|
|
315
332
|
if (menu !== null && frame !== null) {
|
|
@@ -349,13 +366,13 @@ export class SettingsApp {
|
|
|
349
366
|
// Invariant: the whole list pane owns wheel scrolling, including blank space beside
|
|
350
367
|
// short labels. A fixed structured dialog keeps that content interaction available.
|
|
351
368
|
const screenRow = event.row - 1;
|
|
352
|
-
const wheelBottom = this.#
|
|
369
|
+
const wheelBottom = this.#search.input === null
|
|
353
370
|
? this.#bodyTopForFrame + this.#bodyHeightForFrame
|
|
354
371
|
: this.#panelTopForFrame + SETTINGS_SEARCH_INPUT_ROWS + SETTINGS_STATUS_ROWS;
|
|
355
372
|
if (screenRow >= 0 && screenRow < wheelBottom) {
|
|
356
373
|
const distance = scrollbarWheelRows(this.#scrollbarSpeed());
|
|
357
|
-
if (this.#
|
|
358
|
-
this.#
|
|
374
|
+
if (this.#search.input !== null)
|
|
375
|
+
this.#search.scrollBefore = null;
|
|
359
376
|
this.#scroll = Math.max(0, this.#scroll + (event.kind === "wheel-down" ? distance : -distance));
|
|
360
377
|
return { consumed: true };
|
|
361
378
|
}
|
|
@@ -491,6 +508,9 @@ export class SettingsApp {
|
|
|
491
508
|
// flag still has a row, showing the default the source would apply.
|
|
492
509
|
if (entry.flags.length === 0)
|
|
493
510
|
return;
|
|
511
|
+
// Rationale: beside a tab bar, its own Plus Menu dialog configures `+`, so both ways look the same.
|
|
512
|
+
if (entry.backend === "a1" && entry.id === PLUS_MENU_SETTING_ID && this.#options.configurePlusMenu?.() === true)
|
|
513
|
+
return;
|
|
494
514
|
const stored = typeof entry.rawValue === "object" && entry.rawValue !== null && !Array.isArray(entry.rawValue)
|
|
495
515
|
? entry.rawValue
|
|
496
516
|
: {};
|
|
@@ -511,6 +531,7 @@ export class SettingsApp {
|
|
|
511
531
|
entry,
|
|
512
532
|
flags: entry.flags.map(flag => flag.key),
|
|
513
533
|
modelDialog,
|
|
534
|
+
plusMenu: entry.backend === "a1" && entry.id === PLUS_MENU_SETTING_ID ? new PlusMenuDialog(entry.flags) : null,
|
|
514
535
|
filter: modelDialog ? new LineInput("") : null,
|
|
515
536
|
step: "model",
|
|
516
537
|
modelKey: null,
|
|
@@ -525,6 +546,12 @@ export class SettingsApp {
|
|
|
525
546
|
return { consumed: false };
|
|
526
547
|
if (open.modelDialog)
|
|
527
548
|
return this.#modelDialogKey(open, data);
|
|
549
|
+
if (open.plusMenu !== null) {
|
|
550
|
+
const outcome = open.plusMenu.handle(KEYS[data] ?? data, data, key => this.#toggleFlag(open.flags.indexOf(key)));
|
|
551
|
+
if (outcome === "close")
|
|
552
|
+
this.#structured = null;
|
|
553
|
+
return { consumed: true };
|
|
554
|
+
}
|
|
528
555
|
switch (SETTINGS_SHORTCUTS.resolve(KEYS[data] ?? data, DIALOG_SCOPE)) {
|
|
529
556
|
case "close":
|
|
530
557
|
this.#structured = null;
|
|
@@ -679,18 +706,18 @@ export class SettingsApp {
|
|
|
679
706
|
return { consumed: true };
|
|
680
707
|
}
|
|
681
708
|
#filterKey(data) {
|
|
682
|
-
const input = this.#
|
|
709
|
+
const input = this.#search.input;
|
|
683
710
|
if (input === null)
|
|
684
711
|
return { consumed: false };
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
this.#undo();
|
|
712
|
+
if (data === "\u001b" || (data === "\u0003" && !input.hasSelection())) {
|
|
713
|
+
this.#scroll = closeSettingsSearch(this.#search) ?? this.#scroll;
|
|
688
714
|
return { consumed: true };
|
|
689
715
|
}
|
|
716
|
+
const key = KEYS[data];
|
|
690
717
|
// Rationale: the boundary chords jump through the results; plain Home and End stay with
|
|
691
|
-
// the search cursor, which the shared
|
|
718
|
+
// the search cursor, which the shared interactive input moves below.
|
|
692
719
|
if (key === "ctrl+home" || key === "ctrl+end") {
|
|
693
|
-
this.#
|
|
720
|
+
this.#search.scrollBefore = null;
|
|
694
721
|
const rows = this.#rows();
|
|
695
722
|
const selectable = selectableIndexes(rows);
|
|
696
723
|
const target = key === "ctrl+end" ? selectable.at(-1) : selectable[0];
|
|
@@ -702,7 +729,7 @@ export class SettingsApp {
|
|
|
702
729
|
return { consumed: true };
|
|
703
730
|
}
|
|
704
731
|
if (key === "up" || key === "down" || key === "shift+up" || key === "shift+down") {
|
|
705
|
-
this.#
|
|
732
|
+
this.#search.scrollBefore = null;
|
|
706
733
|
const rows = this.#rows();
|
|
707
734
|
// Rationale: nothing found means nothing to move through; the key is still swallowed
|
|
708
735
|
// rather than typed into the search.
|
|
@@ -719,15 +746,10 @@ export class SettingsApp {
|
|
|
719
746
|
this.#select(rows, moveSelection(rows, selected, forward ? 1 : -1));
|
|
720
747
|
return { consumed: true };
|
|
721
748
|
}
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
this.#scroll = this.#scrollBeforeFilter;
|
|
727
|
-
this.#scrollBeforeFilter = null;
|
|
728
|
-
}
|
|
729
|
-
else {
|
|
730
|
-
this.#scrollBeforeFilter = null;
|
|
749
|
+
const before = input.getText();
|
|
750
|
+
input.handleInput(data);
|
|
751
|
+
if (input.getText() !== before) {
|
|
752
|
+
this.#search.scrollBefore = null;
|
|
731
753
|
this.#scroll = 0;
|
|
732
754
|
}
|
|
733
755
|
return { consumed: true };
|
|
@@ -862,7 +884,7 @@ export class SettingsApp {
|
|
|
862
884
|
this.#selectionNeedsReveal = true;
|
|
863
885
|
}
|
|
864
886
|
#rows() {
|
|
865
|
-
const needle = this.#
|
|
887
|
+
const needle = this.#search.input?.getText().trim().toLowerCase() ?? "";
|
|
866
888
|
const matches = (entry) => needle.length === 0
|
|
867
889
|
|| entry.id.toLowerCase().includes(needle)
|
|
868
890
|
|| labelOf(entry).toLowerCase().includes(needle);
|
|
@@ -964,6 +986,8 @@ export class SettingsApp {
|
|
|
964
986
|
#dialogLines(open, width, theme) {
|
|
965
987
|
if (open.modelDialog)
|
|
966
988
|
return this.#modelDialogLines(open, width, theme);
|
|
989
|
+
if (open.plusMenu !== null)
|
|
990
|
+
return open.plusMenu.lines(open.record, width, theme);
|
|
967
991
|
const rows = open.flags.map(key => {
|
|
968
992
|
const declared = open.entry.flags.find(flag => flag.key === key);
|
|
969
993
|
return {
|
|
@@ -1020,15 +1044,44 @@ export class SettingsApp {
|
|
|
1020
1044
|
return this.#dialogLines(open, width, theme);
|
|
1021
1045
|
// Rationale: standing guidance is more useful than transient feedback; failures still
|
|
1022
1046
|
// roll back without displacing the Settings interaction contract.
|
|
1023
|
-
const entries = SETTINGS_SHORTCUTS.hintEntries(SCOPE).filter(entry => !this.#pinned
|
|
1047
|
+
const entries = SETTINGS_SHORTCUTS.hintEntries(SCOPE).filter(entry => !this.#options.pinned
|
|
1024
1048
|
|| entry.key !== DIALOG_CLOSE_SHORTCUT_HINT.key || entry.action !== DIALOG_CLOSE_SHORTCUT_HINT.action);
|
|
1025
1049
|
const status = renderShortcutHintsWithClose(entries, theme, width, width > 0 ? 1 : 0);
|
|
1026
|
-
const input = this.#
|
|
1050
|
+
const input = this.#search.input;
|
|
1027
1051
|
if (input === null)
|
|
1028
1052
|
return [status];
|
|
1029
|
-
return [...
|
|
1053
|
+
return [...input.render(width), status];
|
|
1030
1054
|
}
|
|
1031
1055
|
}
|
|
1056
|
+
function openSettingsSearch(state, host, initial, scroll) {
|
|
1057
|
+
state.scrollBefore = scroll;
|
|
1058
|
+
state.input = state.factory.create({
|
|
1059
|
+
theme: host.theme ?? PLAIN_THEME,
|
|
1060
|
+
getRows: () => host.getSize().height,
|
|
1061
|
+
requestRender: () => host.requestRender(),
|
|
1062
|
+
copyText: state.copyText,
|
|
1063
|
+
});
|
|
1064
|
+
state.input.setFocused(true);
|
|
1065
|
+
state.input.handleInput(initial);
|
|
1066
|
+
}
|
|
1067
|
+
function closeSettingsSearch(state) {
|
|
1068
|
+
state.input?.setFocused(false);
|
|
1069
|
+
state.input = null;
|
|
1070
|
+
const scroll = state.scrollBefore;
|
|
1071
|
+
state.scrollBefore = null;
|
|
1072
|
+
return scroll;
|
|
1073
|
+
}
|
|
1074
|
+
function handleSettingsSearchPointer(state, event, panelTop) {
|
|
1075
|
+
const input = state.input;
|
|
1076
|
+
if (input === null || event.kind === "wheel-up" || event.kind === "wheel-down")
|
|
1077
|
+
return null;
|
|
1078
|
+
const row = event.row - panelTop;
|
|
1079
|
+
const overInput = row >= 1 && row <= SETTINGS_SEARCH_INPUT_ROWS;
|
|
1080
|
+
if (!overInput && !input.ownsPointer())
|
|
1081
|
+
return null;
|
|
1082
|
+
const handled = input.handlePointer({ kind: event.kind, button: event.button, column: event.column, row });
|
|
1083
|
+
return { consumed: handled || overInput, render: handled };
|
|
1084
|
+
}
|
|
1032
1085
|
function modelLabelParts(label) {
|
|
1033
1086
|
const match = /^(.*?)(\s+\[[^\]]+\])$/u.exec(label);
|
|
1034
1087
|
return match === null ? { label } : { label: match[1] ?? label, labelSuffix: match[2] ?? "" };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { UiTheme } from "../../ui/components/index.js";
|
|
2
|
+
export interface SettingsSearchPointerEvent {
|
|
3
|
+
readonly kind: "press" | "motion" | "release";
|
|
4
|
+
readonly button: number;
|
|
5
|
+
readonly column: number;
|
|
6
|
+
/** One-based row relative to the search input's top rule. */
|
|
7
|
+
readonly row: number;
|
|
8
|
+
}
|
|
9
|
+
/** The shared interactive input as consumed by the Settings surface. */
|
|
10
|
+
export interface SettingsSearchInput {
|
|
11
|
+
render(width: number): readonly string[];
|
|
12
|
+
handleInput(data: string): void;
|
|
13
|
+
handlePointer(event: SettingsSearchPointerEvent): boolean;
|
|
14
|
+
getText(): string;
|
|
15
|
+
hasSelection(): boolean;
|
|
16
|
+
ownsPointer(): boolean;
|
|
17
|
+
setFocused(focused: boolean): void;
|
|
18
|
+
}
|
|
19
|
+
export interface SettingsSearchInputOptions {
|
|
20
|
+
readonly theme: UiTheme;
|
|
21
|
+
readonly getRows: () => number;
|
|
22
|
+
readonly requestRender: () => void;
|
|
23
|
+
readonly copyText: (text: string) => void;
|
|
24
|
+
}
|
|
25
|
+
/** Composition supplies the same configurable editor component used by the agent prompt. */
|
|
26
|
+
export interface SettingsSearchInputFactory {
|
|
27
|
+
create(options: SettingsSearchInputOptions): SettingsSearchInput;
|
|
28
|
+
isTextInput(data: string): boolean;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { PromptHistoryService } from "./service.js";
|
|
2
|
-
export { resolvePromptHistoryPath } from "./paths.js";
|
|
2
|
+
export { resolvePromptHistoryPath, type PromptHistoryScope } from "./paths.js";
|
|
3
3
|
export { PromptImageSidecar, collectImageChipIdentifiers } from "./image-sidecar.js";
|
|
4
4
|
export type { PromptImageSidecarRecord, PromptImageSidecarAttachment } from "./image-sidecar.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type PromptHistoryScope = "unique" | "shared";
|
|
2
|
+
export declare function resolvePromptHistoryPath(dataDir: string, profileRoot: string, platform?: NodeJS.Platform, scope?: PromptHistoryScope, sessionId?: string): {
|
|
2
3
|
path: string;
|
|
3
4
|
profileId: string;
|
|
4
5
|
imagesDir: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { posix, win32 } from "node:path";
|
|
3
3
|
import { PRODUCT_IDENTITY } from "../../product-identity.js";
|
|
4
|
-
export function resolvePromptHistoryPath(dataDir, profileRoot, platform = process.platform) {
|
|
4
|
+
export function resolvePromptHistoryPath(dataDir, profileRoot, platform = process.platform, scope = "shared", sessionId) {
|
|
5
5
|
const paths = platform === "win32" ? win32 : posix;
|
|
6
6
|
if (!paths.isAbsolute(dataDir) || !paths.isAbsolute(profileRoot) || dataDir.includes("\0") || profileRoot.includes("\0"))
|
|
7
7
|
throw new TypeError("History requires absolute roots");
|
|
@@ -11,7 +11,15 @@ export function resolvePromptHistoryPath(dataDir, profileRoot, platform = proces
|
|
|
11
11
|
if (platform === "win32")
|
|
12
12
|
normalized = normalized.replaceAll("\\", "/").toLowerCase();
|
|
13
13
|
const digest = createHash("sha256").update(JSON.stringify([1, PRODUCT_IDENTITY.commandName, normalized])).digest("hex");
|
|
14
|
-
|
|
14
|
+
if (scope !== "unique" && scope !== "shared")
|
|
15
|
+
throw new TypeError("Invalid history scope");
|
|
16
|
+
if (scope === "unique" && (typeof sessionId !== "string" || sessionId.length === 0 || sessionId.length > 256 || sessionId.includes("\0"))) {
|
|
17
|
+
throw new TypeError("Unique history requires a valid session ID");
|
|
18
|
+
}
|
|
19
|
+
// Compatibility: preserve the former Shared identity; a separate domain prevents
|
|
20
|
+
// collisions with per-session databases and image sidecars.
|
|
21
|
+
const profileId = scope === "shared" ? `${PRODUCT_IDENTITY.filesystem.temporaryPrefix}${digest}`
|
|
22
|
+
: `${PRODUCT_IDENTITY.filesystem.temporaryPrefix}${createHash("sha256").update(JSON.stringify([2, PRODUCT_IDENTITY.commandName, normalized, sessionId])).digest("hex")}`;
|
|
15
23
|
const historyDir = paths.join(dataDir, "history");
|
|
16
24
|
return { path: paths.join(historyDir, `${profileId}.sqlite3`), profileId, imagesDir: paths.join(historyDir, `${profileId}-images`) };
|
|
17
25
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Worker } from "node:worker_threads";
|
|
2
2
|
import { type PromptHistoryFailure, type PromptHistoryPort, type PromptHistoryResult, type PromptHistorySnapshot, type PromptHistorySubmission } from "../../contracts/owned-ui/index.js";
|
|
3
|
+
import { type PromptHistoryScope } from "./paths.js";
|
|
3
4
|
export type HistoryRecoveryState = "starting" | "ready" | "recovering" | "blocked-storage" | "closing" | "closed";
|
|
4
5
|
export interface HistoryClock {
|
|
5
6
|
now(): number;
|
|
@@ -13,6 +14,8 @@ export declare class PromptHistoryService implements PromptHistoryPort {
|
|
|
13
14
|
dataDir: string;
|
|
14
15
|
profileRoot: string;
|
|
15
16
|
limit: number;
|
|
17
|
+
scope?: PromptHistoryScope;
|
|
18
|
+
sessionId?: string;
|
|
16
19
|
createWorker?: () => Worker;
|
|
17
20
|
clock?: HistoryClock;
|
|
18
21
|
random?: () => number;
|
|
@@ -21,6 +24,8 @@ export declare class PromptHistoryService implements PromptHistoryPort {
|
|
|
21
24
|
dataDir: string;
|
|
22
25
|
profileRoot: string;
|
|
23
26
|
limit: number;
|
|
27
|
+
scope?: PromptHistoryScope;
|
|
28
|
+
sessionId?: string;
|
|
24
29
|
createWorker?: () => Worker;
|
|
25
30
|
clock?: HistoryClock;
|
|
26
31
|
random?: () => number;
|
|
@@ -129,7 +129,7 @@ export class PromptHistoryService {
|
|
|
129
129
|
const { Worker } = await import("node:worker_threads");
|
|
130
130
|
if (this.#isClosed() || generation !== this.#generation)
|
|
131
131
|
return;
|
|
132
|
-
const location = resolvePromptHistoryPath(this.options.dataDir, this.options.profileRoot);
|
|
132
|
+
const location = resolvePromptHistoryPath(this.options.dataDir, this.options.profileRoot, process.platform, this.options.scope, this.options.sessionId);
|
|
133
133
|
const source = import.meta.url.endsWith(".ts");
|
|
134
134
|
const entry = new URL(source ? "./worker.ts" : "./worker.js", import.meta.url);
|
|
135
135
|
const options = { workerData: { ...location, limit: this.options.limit }, stdout: true, stderr: true };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ResidentTabAction = "tabs.new" | "tabs.close" | "tabs.rename" | "tabs.settings" | "tabs.retry" | "tabs.fresh" | "tabs.next" | "tabs.previous" | "tabs.moveRight" | "tabs.moveLeft" | `tabs.jump${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10}`;
|
|
1
|
+
export type ResidentTabAction = "tabs.new" | "tabs.close" | "tabs.rename" | "tabs.settings" | "tabs.changelog" | "tabs.commands" | "tabs.retry" | "tabs.fresh" | "tabs.next" | "tabs.previous" | "tabs.moveRight" | "tabs.moveLeft" | `tabs.jump${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10}`;
|
|
2
2
|
export interface ResidentTabShortcut {
|
|
3
3
|
readonly id: ResidentTabAction;
|
|
4
4
|
readonly defaultKey: string | null;
|
|
@@ -14,6 +14,8 @@ export const RESIDENT_TAB_SHORTCUTS = Object.freeze([
|
|
|
14
14
|
{ id: "tabs.close", defaultKey: "alt+w", scope: "global", description: "Close A1 tab" },
|
|
15
15
|
{ id: "tabs.rename", defaultKey: "f2", scope: "global", description: "Rename A1 tab" },
|
|
16
16
|
{ id: "tabs.settings", defaultKey: "alt+,", scope: "global", description: "Open Settings tab" },
|
|
17
|
+
{ id: "tabs.changelog", defaultKey: null, scope: "global", description: "Open Changelog tab" },
|
|
18
|
+
{ id: "tabs.commands", defaultKey: "alt+/", scope: "global", description: "Open command palette" },
|
|
17
19
|
...jumps,
|
|
18
20
|
{ id: "tabs.next", defaultKey: null, scope: "global", description: "View next A1 tab" },
|
|
19
21
|
{ id: "tabs.previous", defaultKey: null, scope: "global", description: "View previous A1 tab" },
|
|
@@ -7,6 +7,7 @@ export declare const PRIVATE_ENVIRONMENT: Readonly<{
|
|
|
7
7
|
releaseLayers: "LAUNCH_CONTEXT_RELEASE_LAYERS";
|
|
8
8
|
launchProfile: "LAUNCH_CONTEXT_PROFILE";
|
|
9
9
|
launchMode: "LAUNCH_CONTEXT_MODE";
|
|
10
|
+
updateVersion: "LAUNCH_CONTEXT_UPDATE_VERSION";
|
|
10
11
|
immutableWarmup: "LAUNCH_CONTEXT_WARMUP";
|
|
11
12
|
residentRootExecutable: "LAUNCH_CONTEXT_RESIDENT_ROOT_EXECUTABLE";
|
|
12
13
|
residentRootArguments: "LAUNCH_CONTEXT_RESIDENT_ROOT_ARGUMENTS";
|
|
@@ -35,6 +36,7 @@ export interface LaunchContext {
|
|
|
35
36
|
readonly releaseLayers?: string;
|
|
36
37
|
readonly launchProfile?: "a1" | "pi";
|
|
37
38
|
readonly launchMode?: "tabs" | "solo";
|
|
39
|
+
readonly updateVersion?: string;
|
|
38
40
|
readonly immutableWarmup?: "1";
|
|
39
41
|
readonly residentRootExecutable?: string;
|
|
40
42
|
readonly residentRootArguments?: string;
|
|
@@ -7,6 +7,7 @@ export const PRIVATE_ENVIRONMENT = Object.freeze({
|
|
|
7
7
|
releaseLayers: "LAUNCH_CONTEXT_RELEASE_LAYERS",
|
|
8
8
|
launchProfile: "LAUNCH_CONTEXT_PROFILE",
|
|
9
9
|
launchMode: "LAUNCH_CONTEXT_MODE",
|
|
10
|
+
updateVersion: "LAUNCH_CONTEXT_UPDATE_VERSION",
|
|
10
11
|
immutableWarmup: "LAUNCH_CONTEXT_WARMUP",
|
|
11
12
|
residentRootExecutable: "LAUNCH_CONTEXT_RESIDENT_ROOT_EXECUTABLE",
|
|
12
13
|
residentRootArguments: "LAUNCH_CONTEXT_RESIDENT_ROOT_ARGUMENTS",
|