@timurproko/a1 0.2.7-dev.820 → 0.2.7-dev.834
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/session-shell-root.d.ts +8 -1
- package/dist/app/session-shell/session-shell.js +68 -18
- package/dist/app/session-shell/shell-host.js +6 -3
- 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 -42
- 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 +8 -2
- package/dist/foundation/resident-tabs/bridge-client.js +13 -6
- package/dist/foundation/resident-tabs/progress-observation-schema.d.ts +34 -0
- package/dist/foundation/resident-tabs/progress-observation-schema.js +1 -0
- package/dist/foundation/resident-tabs/progress-observation.d.ts +3 -6
- package/dist/foundation/resident-tabs/progress-observation.js +42 -12
- package/dist/foundation/resident-tabs/routes.d.ts +5 -0
- package/dist/foundation/resident-tabs/routes.js +5 -0
- 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 +3 -2
- package/dist/ui/components/prompt-input.js +6 -4
- package/dist/ui/components/spans.js +2 -2
- package/dist/ui/components/surface.js +4 -2
- package/dist/ui/components/text.d.ts +3 -6
- package/dist/ui/components/text.js +6 -8
- package/dist/ui/settings/declarations.d.ts +54 -8
- package/dist/ui/settings/declarations.js +36 -7
- package/dist/ui/settings/manager.js +7 -3
- package/dist/ui/settings/migrations.js +15 -0
- package/dist/ui/settings/sections.d.ts +4 -0
- package/dist/ui/settings/sections.js +9 -3
- package/docs/architecture/resident-tabs.md +36 -8
- package/docs/architecture/tool-image-presentation.md +2 -1
- package/docs/features/prompt-history.md +41 -20
- package/docs/validation.md +25 -5
- package/package.json +1 -1
|
@@ -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",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type AvailableRelease, type ReleaseChannel } from "./latest-release.js";
|
|
2
|
+
export declare const AUTOMATIC_UPDATE_STATE_SCHEMA = 1;
|
|
3
|
+
export declare const AUTOMATIC_UPDATE_RETRY_MS: number;
|
|
4
|
+
export interface AutomaticUpdateState {
|
|
5
|
+
readonly schema: typeof AUTOMATIC_UPDATE_STATE_SCHEMA;
|
|
6
|
+
readonly channel: ReleaseChannel;
|
|
7
|
+
readonly targetVersion: string;
|
|
8
|
+
readonly status: "running" | "installed" | "failed";
|
|
9
|
+
readonly attemptedAt: number;
|
|
10
|
+
readonly updatedAt: number;
|
|
11
|
+
readonly installedVersion: string | null;
|
|
12
|
+
readonly failure: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare class AutomaticUpdateStateStore {
|
|
15
|
+
readonly path: string;
|
|
16
|
+
constructor(dataDir: string);
|
|
17
|
+
read(): Promise<AutomaticUpdateState | null>;
|
|
18
|
+
write(value: AutomaticUpdateState): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface AutomaticUpdateRequestOptions {
|
|
21
|
+
readonly release: AvailableRelease;
|
|
22
|
+
readonly runningVersion: string;
|
|
23
|
+
readonly environment?: NodeJS.ProcessEnv;
|
|
24
|
+
readonly signal?: AbortSignal;
|
|
25
|
+
readonly now?: () => number;
|
|
26
|
+
readonly spawnWorker?: (entry: string, arguments_: readonly string[], environment: NodeJS.ProcessEnv) => Promise<void>;
|
|
27
|
+
readonly sleep?: (durationMs: number) => Promise<void>;
|
|
28
|
+
/** Focused-test seams; production resolves both from the certified launch cohort. */
|
|
29
|
+
readonly worker?: {
|
|
30
|
+
readonly entry: string;
|
|
31
|
+
readonly packageRoot: string;
|
|
32
|
+
};
|
|
33
|
+
readonly activeVersion?: () => Promise<string | null>;
|
|
34
|
+
}
|
|
35
|
+
/** Start or observe one silent update and resolve with the active newer version once restart is useful. */
|
|
36
|
+
export declare function requestAutomaticUpdate(options: AutomaticUpdateRequestOptions): Promise<string | null>;
|
|
37
|
+
export declare function runAutomaticUpdateWorker(argv: readonly string[], environment?: NodeJS.ProcessEnv): Promise<number>;
|