@timurproko/a1 0.1.8-dev.508 → 0.1.8-dev.510

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.
Files changed (75) hide show
  1. package/README.md +44 -1
  2. package/bin/activate.js +8 -0
  3. package/bin/sync-pi-tui-proxy.js +7 -0
  4. package/dist/app/session-shell/index.d.ts +1 -1
  5. package/dist/app/session-shell/prompt-suggestion-controller.d.ts +2 -0
  6. package/dist/app/session-shell/prompt-suggestion-controller.js +12 -0
  7. package/dist/app/session-shell/session-shell-root.d.ts +10 -0
  8. package/dist/app/session-shell/session-shell-root.js +12 -2
  9. package/dist/app/session-shell/session-shell.d.ts +8 -1
  10. package/dist/app/session-shell/session-shell.js +217 -18
  11. package/dist/composition/index.d.ts +1 -1
  12. package/dist/composition/owned-ui.js +21 -3
  13. package/dist/composition/settings-route-host.d.ts +17 -4
  14. package/dist/composition/settings-route-host.js +47 -16
  15. package/dist/contracts/owned-ui/index.d.ts +1 -1
  16. package/dist/contracts/owned-ui/model.d.ts +1 -5
  17. package/dist/features/owned-ui/index.d.ts +3 -0
  18. package/dist/features/owned-ui/index.js +2 -0
  19. package/dist/features/owned-ui/reference-routes.d.ts +6 -0
  20. package/dist/features/owned-ui/reference-routes.js +6 -0
  21. package/dist/features/owned-ui/reference-screen-app.d.ts +37 -0
  22. package/dist/features/owned-ui/reference-screen-app.js +232 -0
  23. package/dist/features/owned-ui/settings-app.js +2 -7
  24. package/dist/foundation/release/index.d.ts +2 -0
  25. package/dist/foundation/release/index.js +1 -0
  26. package/dist/foundation/release/update-activation.d.ts +72 -0
  27. package/dist/foundation/release/update-activation.js +204 -0
  28. package/dist/foundation/release/update.d.ts +5 -10
  29. package/dist/foundation/release/update.js +39 -38
  30. package/dist/integrations/pi/components/index.d.ts +4 -2
  31. package/dist/integrations/pi/components/index.js +2 -1
  32. package/dist/integrations/pi/components/models-dialog.d.ts +55 -0
  33. package/dist/integrations/pi/components/models-dialog.js +310 -0
  34. package/dist/integrations/pi/components/shell-editor-autocomplete.d.ts +10 -0
  35. package/dist/integrations/pi/components/shell-editor-autocomplete.js +29 -5
  36. package/dist/integrations/pi/components/shell-footer-status.js +2 -1
  37. package/dist/integrations/pi/components/shell-presenters-info.d.ts +10 -0
  38. package/dist/integrations/pi/components/shell-presenters-info.js +31 -11
  39. package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +11 -1
  40. package/dist/integrations/pi/components/shell-selectors-dialogs.js +13 -0
  41. package/dist/integrations/pi/components/shell-shared-facade.d.ts +8 -0
  42. package/dist/integrations/pi/components/skills-command.d.ts +47 -0
  43. package/dist/integrations/pi/components/skills-command.js +118 -0
  44. package/dist/integrations/pi/components/skills-dialog.d.ts +9 -0
  45. package/dist/integrations/pi/components/skills-dialog.js +112 -0
  46. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +8 -2
  47. package/dist/integrations/pi/components/upstream/components/owned-editor.js +42 -7
  48. package/dist/integrations/pi/engine/adapter.d.ts +5 -1
  49. package/dist/integrations/pi/engine/adapter.js +13 -1
  50. package/dist/integrations/pi/engine/index.d.ts +2 -2
  51. package/dist/integrations/pi/engine/index.js +1 -1
  52. package/dist/integrations/pi/engine/resource-catalog.d.ts +2 -1
  53. package/dist/integrations/pi/engine/resource-catalog.js +19 -9
  54. package/dist/integrations/pi/engine/workflow-contexts.d.ts +8 -1
  55. package/dist/integrations/pi/engine/workflow-contexts.js +52 -8
  56. package/dist/integrations/pi/engine/workflow-runner.js +98 -74
  57. package/dist/integrations/pi/engine/workflows.d.ts +26 -2
  58. package/dist/integrations/pi/engine/workflows.js +8 -0
  59. package/dist/native/darwin-arm64/manifest.json +1 -1
  60. package/dist/native/linux-x64/manifest.json +1 -1
  61. package/dist/native/win32-x64/manifest.json +2 -2
  62. package/dist/native/win32-x64/process-guardian.exe +0 -0
  63. package/dist/runtime-payload-inventory.json +1 -0
  64. package/dist/ui/apps/contracts.d.ts +9 -1
  65. package/dist/ui/apps/index.d.ts +1 -1
  66. package/dist/ui/settings/declarations.d.ts +15 -29
  67. package/dist/ui/settings/declarations.js +14 -25
  68. package/dist/ui/settings/index.d.ts +1 -1
  69. package/dist/ui/settings/index.js +1 -1
  70. package/dist/ui/settings/migrations.js +18 -0
  71. package/docs/architecture/internal-naming.md +2 -0
  72. package/docs/architecture/prompt-suggestions.md +1 -1
  73. package/docs/architecture/ui-reference-provenance.md +1 -0
  74. package/docs/features/modal-content-interaction.md +3 -2
  75. package/package.json +4 -1
@@ -12,6 +12,8 @@ import { OwnedUiSessionShell } from "../app/session-shell/session-shell.js";
12
12
  import { OwnedSettingsManager } from "../ui/settings/manager.js";
13
13
  import { createPiTerminalBridge } from "../integrations/pi/tui-runtime/presentation-adapter.js";
14
14
  import { createOwnedRouteHost } from "./settings-route-host.js";
15
+ import { renderPiShellChangelogLines, renderPiShellHotkeysLines } from "../integrations/pi/components/shell-presenters-info.js";
16
+ import { readPinnedCommandChangelog } from "../integrations/pi/engine/changelog.js";
15
17
  export async function composeOwnedUiApplication(options = {}) {
16
18
  return (await composeOwnedUi(options)).application;
17
19
  }
@@ -40,7 +42,19 @@ export async function composeOwnedUi(options = {}) {
40
42
  // dark, regardless of terminal detection or a previously stored Pi theme. The
41
43
  // comparison profile keeps Pi's configured theme behavior and settings surface.
42
44
  applyConfiguredPiTheme(ownedSurfaces ? "dark" : adapter.configuredTheme());
43
- const routeHost = settings === null || !ownedSurfaces ? null : createOwnedRouteHost(settings);
45
+ // Rationale: the reference screens read the shell the composition is about to construct; a route
46
+ // cannot open before the shell exists, so the closure is settled by the time it runs.
47
+ const references = {
48
+ changelog: async (input) => {
49
+ const markdown = input?.document ?? await readPinnedCommandChangelog();
50
+ return width => renderPiShellChangelogLines(markdown, width);
51
+ },
52
+ hotkeys: async () => {
53
+ const presentation = shell.hotkeysPresentation();
54
+ return width => renderPiShellHotkeysLines(presentation, width);
55
+ },
56
+ };
57
+ const routeHost = settings === null || !ownedSurfaces ? null : createOwnedRouteHost(settings, references);
44
58
  const viewportSettings = settings === null || !ownedSurfaces ? null : {
45
59
  snapshot: () => viewportSettingsSnapshot(settings),
46
60
  onChange: listener => settings.onChange(() => listener(viewportSettingsSnapshot(settings))),
@@ -54,6 +68,11 @@ export async function composeOwnedUi(options = {}) {
54
68
  enabled: () => settings.value("promptSuggestions"),
55
69
  onChange: (listener) => settings.onChange(() => listener(settings.value("promptSuggestions"))),
56
70
  };
71
+ const skills = settings === null || !ownedSurfaces ? null : {
72
+ presentation: () => settings.value("skillsPresentation"),
73
+ // Rationale: one listener covers both the A1 presentation choice and the engine skill-command toggle.
74
+ onChange: (listener) => settings.onChange(() => listener()),
75
+ };
57
76
  const historyLimit = settings?.value("promptHistoryMaxItems");
58
77
  const historyProfileLocation = settings === null || !ownedSurfaces || !settings.value("promptHistoryEnabled")
59
78
  ? null
@@ -92,6 +111,7 @@ export async function composeOwnedUi(options = {}) {
92
111
  paste: event => clipboardDiagnostics.paste(event),
93
112
  } }),
94
113
  ...(promptSuggestions === null ? {} : { suggestions: promptSuggestions }),
114
+ ...(skills === null ? {} : { skills }),
95
115
  ...(promptHistory === null ? {} : { history: {
96
116
  ...promptHistory,
97
117
  editor: await import("../integrations/pi/components/history-editor-loader.js").then(module => module.loadHistoryEditor()),
@@ -123,8 +143,6 @@ export async function composeOwnedUi(options = {}) {
123
143
  function quitOutroSettingsSnapshot(settings) {
124
144
  return {
125
145
  enabled: settings.value("quitAnimation"),
126
- effect: settings.value("quitEffect"),
127
- durationMs: settings.value("quitEffectDurationMs"),
128
146
  };
129
147
  }
130
148
  function viewportSettingsSnapshot(settings) {
@@ -1,7 +1,20 @@
1
1
  import type { OwnedSettingsManager } from "../ui/settings/manager.js";
2
- import type { UiRouteHost } from "../ui/apps/contracts.js";
2
+ import type { UiRouteHost, UiRouteInput } from "../ui/apps/contracts.js";
3
+ /** Styled document rows for one content width. */
4
+ export type OwnedReferenceRows = (width: number) => readonly string[];
3
5
  /**
4
- * Declares the A1-owned settings route without evaluating its presentation graph
5
- * during startup. Opening the route retains input while the optional module loads.
6
+ * The documents the reference routes present, supplied by the composition so the
7
+ * route host never reads the changelog or the shell's keybindings itself.
6
8
  */
7
- export declare function createOwnedRouteHost(settings: OwnedSettingsManager): UiRouteHost;
9
+ export interface OwnedReferenceProviders {
10
+ /** The complete pinned changelog, or the supplied document when the route input carries one. */
11
+ changelog(input?: UiRouteInput): Promise<OwnedReferenceRows>;
12
+ /** The shell's keyboard shortcuts as they stand when the screen opens. */
13
+ hotkeys(): Promise<OwnedReferenceRows>;
14
+ }
15
+ /**
16
+ * Declares the A1-owned settings route, and the changelog and hotkeys reference
17
+ * routes when their documents are supplied, without evaluating their presentation
18
+ * graphs during startup. Opening a route retains input while the optional module loads.
19
+ */
20
+ export declare function createOwnedRouteHost(settings: OwnedSettingsManager, references?: OwnedReferenceProviders): UiRouteHost;
@@ -1,24 +1,38 @@
1
+ import { CHANGELOG_APP_ID, CHANGELOG_ROUTE, CHANGELOG_TITLE, HOTKEYS_APP_ID, HOTKEYS_ROUTE, HOTKEYS_TITLE, } from "../features/owned-ui/reference-routes.js";
1
2
  import { SETTINGS_APP_ID, SETTINGS_ROUTE } from "../features/owned-ui/settings-route.js";
2
3
  import { piTheme } from "../integrations/pi/components/upstream/theme/theme.js";
3
4
  import { faint } from "../ui/components/text.js";
4
5
  /**
5
- * Declares the A1-owned settings route without evaluating its presentation graph
6
- * during startup. Opening the route retains input while the optional module loads.
6
+ * Declares the A1-owned settings route, and the changelog and hotkeys reference
7
+ * routes when their documents are supplied, without evaluating their presentation
8
+ * graphs during startup. Opening a route retains input while the optional module loads.
7
9
  */
8
- export function createOwnedRouteHost(settings) {
10
+ export function createOwnedRouteHost(settings, references) {
11
+ const routes = new Set([SETTINGS_ROUTE, ...(references === undefined ? [] : [CHANGELOG_ROUTE, HOTKEYS_ROUTE])]);
9
12
  return {
10
- claims: route => route === SETTINGS_ROUTE,
11
- open: route => route === SETTINGS_ROUTE ? deferredSettingsSurface(settings) : null,
13
+ claims: route => routes.has(route),
14
+ open: (route, input) => {
15
+ if (!routes.has(route))
16
+ return null;
17
+ if (route === SETTINGS_ROUTE) {
18
+ return deferredSurface(SETTINGS_APP_ID, "settings", () => loadSettingsSurface(settings));
19
+ }
20
+ const changelog = route === CHANGELOG_ROUTE;
21
+ const id = changelog ? CHANGELOG_APP_ID : HOTKEYS_APP_ID;
22
+ const title = changelog ? CHANGELOG_TITLE : HOTKEYS_TITLE;
23
+ const document = changelog ? references.changelog(input) : references.hotkeys();
24
+ return deferredSurface(id, title, () => loadReferenceSurface(settings, id, route, title, document));
25
+ },
12
26
  };
13
27
  }
14
- function deferredSettingsSurface(settings) {
28
+ function deferredSurface(id, label, load) {
15
29
  let delegate = null;
16
30
  let closed = false;
17
31
  let failure = null;
18
32
  let onRender = () => undefined;
19
33
  let onExit = () => undefined;
20
34
  const pending = [];
21
- void loadSettingsSurface(settings).then(surface => {
35
+ void load().then(surface => {
22
36
  if (closed) {
23
37
  surface.close();
24
38
  return;
@@ -30,7 +44,7 @@ function deferredSettingsSurface(settings) {
30
44
  action(surface);
31
45
  onRender();
32
46
  }).catch(error => {
33
- failure = `Could not load settings: ${error instanceof Error ? error.message : String(error)}`;
47
+ failure = `Could not load ${label}: ${error instanceof Error ? error.message : String(error)}`;
34
48
  onRender();
35
49
  });
36
50
  const defer = (action) => {
@@ -40,10 +54,9 @@ function deferredSettingsSurface(settings) {
40
54
  pending.push(action);
41
55
  };
42
56
  return {
43
- id: SETTINGS_APP_ID,
57
+ id,
44
58
  render: (width, height) => delegate?.render(width, height)
45
- ?? [...(height > 0 ? [failure ?? "Loading settings…"] : []), ...Array(Math.max(0, height - 1)).fill("")]
46
- .map(line => line.slice(0, Math.max(0, width))),
59
+ ?? Array.from({ length: Math.max(0, height) }, (_, row) => row === 0 && failure !== null ? failure.slice(0, Math.max(0, width)) : ""),
47
60
  handleInput: data => { defer(surface => surface.handleInput(data)); return true; },
48
61
  handleMouse: event => { defer(surface => surface.handleMouse(event)); return true; },
49
62
  isClosed: () => closed || delegate?.isClosed() === true,
@@ -53,13 +66,31 @@ function deferredSettingsSurface(settings) {
53
66
  };
54
67
  }
55
68
  async function loadSettingsSurface(settings) {
56
- const [{ SettingsApp }, { UiAppHost }, { UiAppRegistry }] = await Promise.all([
57
- import("../features/owned-ui/settings-app.js"),
69
+ const { SettingsApp } = await import("../features/owned-ui/settings-app.js");
70
+ return hostApp(SETTINGS_APP_ID, SETTINGS_ROUTE, () => new SettingsApp(settings));
71
+ }
72
+ async function loadReferenceSurface(settings, id, route, title, document) {
73
+ // Rationale: the app module and the document load together, so a document that fails
74
+ // to render is reported by the placeholder rather than by a screen that never fills.
75
+ const [{ ReferenceScreenApp }, rows] = await Promise.all([import("../features/owned-ui/reference-screen-app.js"), document]);
76
+ return hostApp(id, route, () => new ReferenceScreenApp({
77
+ id,
78
+ title,
79
+ document: { rows },
80
+ scrollSettings: () => ({
81
+ scrollbarAppearance: settings.value("scrollbarAppearance"),
82
+ scrollbarStyle: settings.value("scrollbarStyle"),
83
+ scrollbarSpeed: settings.value("scrollbarSpeed"),
84
+ }),
85
+ }));
86
+ }
87
+ async function hostApp(id, route, create) {
88
+ const [{ UiAppHost }, { UiAppRegistry }] = await Promise.all([
58
89
  import("../ui/apps/host.js"),
59
90
  import("../ui/apps/registry.js"),
60
91
  ]);
61
92
  const registry = new UiAppRegistry();
62
- registry.register({ id: SETTINGS_APP_ID, route: SETTINGS_ROUTE, create: () => new SettingsApp(settings) });
93
+ registry.register({ id, route, create });
63
94
  let size = { width: 80, height: 24 };
64
95
  let frame = [];
65
96
  let closed = false;
@@ -81,9 +112,9 @@ async function loadSettingsSurface(settings) {
81
112
  },
82
113
  },
83
114
  });
84
- host.open(SETTINGS_APP_ID);
115
+ host.open(id);
85
116
  return {
86
- id: SETTINGS_APP_ID,
117
+ id,
87
118
  render: (width, height) => {
88
119
  size = { width, height };
89
120
  host.render();
@@ -1,7 +1,7 @@
1
1
  export { OWNED_UI_EXTENSION_CONTRACT_VERSION, OWNED_UI_EXTENSION_RENDER_CALLBACKS, OWNED_UI_EXTENSION_UI_CALLBACKS, OWNED_UI_EXTENSION_UI_PROPERTIES, assertOwnedUiExtensionUiPort, } from "./extension-ui.js";
2
2
  export type { OwnedUiExtensionAutocompleteItem, OwnedUiExtensionAutocompleteProvider, OwnedUiExtensionAutocompleteProviderFactory, OwnedUiExtensionAutocompleteSuggestions, OwnedUiExtensionComponent, OwnedUiExtensionComponentFactory, OwnedUiExtensionComponentFactoryContext, OwnedUiExtensionCustomFactory, OwnedUiExtensionCustomOptions, OwnedUiExtensionDialogOptions, OwnedUiExtensionEditorComponent, OwnedUiExtensionEditorFactory, OwnedUiExtensionEditorThemePort, OwnedUiExtensionEntryRenderInput, OwnedUiExtensionFooterDataPort, OwnedUiExtensionFooterFactory, OwnedUiExtensionInputListener, OwnedUiExtensionKeybindingsPort, OwnedUiExtensionMessageRenderInput, OwnedUiExtensionNotificationSeverity, OwnedUiExtensionOverlayAnchor, OwnedUiExtensionOverlayHandle, OwnedUiExtensionOverlayMargin, OwnedUiExtensionOverlayOptions, OwnedUiExtensionRenderCallback, OwnedUiExtensionRendererPort, OwnedUiExtensionRuntimePort, OwnedUiExtensionSelectListThemePort, OwnedUiExtensionSizeValue, OwnedUiExtensionTerminalPort, OwnedUiExtensionThemeBackground, OwnedUiExtensionThemeColor, OwnedUiExtensionThemeInfo, OwnedUiExtensionThemePort, OwnedUiExtensionToolRenderContext, OwnedUiExtensionUiCallback, OwnedUiExtensionUiPort, OwnedUiExtensionUiProperty, OwnedUiExtensionWidgetOptions, OwnedUiExtensionWidgetPlacement, OwnedUiExtensionWorkingIndicatorOptions, } from "./extension-ui.js";
3
3
  export { OWNED_UI_CONTRACT_VERSION } from "./model.js";
4
- export type { OwnedUiBlockStatus, OwnedUiCommand, OwnedUiCommandOutcome, OwnedUiCorrelationId, OwnedUiCustomization, OwnedUiDiagnostics, OwnedUiDialog, OwnedUiEditorState, OwnedUiEntityId, OwnedUiEvent, OwnedUiFooterView, OwnedUiImageAttachment, OwnedUiLifecycle, OwnedUiModelInfo, OwnedUiOverlay, OwnedUiPromptSuggestionGeneratorPort, OwnedUiPromptSuggestionIdentity, OwnedUiPromptSuggestionReasoning, OwnedUiPromptSuggestionRequest, OwnedUiPromptSuggestionResult, OwnedUiPromptSuggestionState, OwnedUiQuitEffect, OwnedUiQuitOutroSettings, OwnedUiQuitOutroSettingsPort, OwnedUiSessionId, OwnedUiSessionViewModel, OwnedUiSeverity, OwnedUiSlotId, OwnedUiSnapshot, OwnedUiStatusView, OwnedUiTerminalSurface, OwnedUiThinkingLevel, OwnedUiToolRendering, OwnedUiToolResultPart, OwnedUiToolState, OwnedUiTranscriptBlock, OwnedUiTranscriptBlockKind, OwnedUiTranscriptImageReference, OwnedUiUsageView, OwnedUiViewportSettings, OwnedUiViewportSettingsPort, } from "./model.js";
4
+ export type { OwnedUiBlockStatus, OwnedUiCommand, OwnedUiCommandOutcome, OwnedUiCorrelationId, OwnedUiCustomization, OwnedUiDiagnostics, OwnedUiDialog, OwnedUiEditorState, OwnedUiEntityId, OwnedUiEvent, OwnedUiFooterView, OwnedUiImageAttachment, OwnedUiLifecycle, OwnedUiModelInfo, OwnedUiOverlay, OwnedUiPromptSuggestionGeneratorPort, OwnedUiPromptSuggestionIdentity, OwnedUiPromptSuggestionReasoning, OwnedUiPromptSuggestionRequest, OwnedUiPromptSuggestionResult, OwnedUiPromptSuggestionState, OwnedUiQuitOutroSettings, OwnedUiQuitOutroSettingsPort, OwnedUiSessionId, OwnedUiSessionViewModel, OwnedUiSeverity, OwnedUiSlotId, OwnedUiSnapshot, OwnedUiStatusView, OwnedUiTerminalSurface, OwnedUiThinkingLevel, OwnedUiToolRendering, OwnedUiToolResultPart, OwnedUiToolState, OwnedUiTranscriptBlock, OwnedUiTranscriptBlockKind, OwnedUiTranscriptImageReference, OwnedUiUsageView, OwnedUiViewportSettings, OwnedUiViewportSettingsPort, } from "./model.js";
5
5
  export { acceptsTranscriptUpdate, transcriptToolState } from "./transcript-lifecycle.js";
6
6
  export { ImageAttachmentError, MAX_CLIPBOARD_TEXT_BYTES, MAX_IMAGE_DATA_BYTES, MAX_PROMPT_IMAGES, assertImageEncodedSize, assertPromptImages, } from "./image-attachments.js";
7
7
  export { CONTEXTUAL_PROMPT_SUGGESTION_INSTRUCTION, normalizePromptSuggestionCandidate } from "./prompt-suggestions.js";
@@ -64,14 +64,10 @@ export interface OwnedUiViewportSettingsPort {
64
64
  snapshot(): OwnedUiViewportSettings;
65
65
  onChange(listener: (settings: OwnedUiViewportSettings) => void): () => void;
66
66
  }
67
- export type OwnedUiQuitEffect = "fall" | "dissolve" | "starburst" | "waves";
68
67
  /** Profile-local quit outro choice, read at the moment bare A1 quits. */
69
68
  export interface OwnedUiQuitOutroSettings {
70
- /** False leaves the terminal immediately; the effect and duration are then ignored. */
69
+ /** False leaves the terminal immediately; true plays the shell's fixed effect. */
71
70
  readonly enabled: boolean;
72
- readonly effect: OwnedUiQuitEffect;
73
- /** Requested playback length; the player clamps it to its supported range. */
74
- readonly durationMs: number;
75
71
  }
76
72
  export interface OwnedUiQuitOutroSettingsPort {
77
73
  snapshot(): OwnedUiQuitOutroSettings;
@@ -4,3 +4,6 @@ export { createConsoleProjectTrustPrompt } from "./project-trust-prompt.js";
4
4
  export type { ConsoleProjectTrustPromptOptions, OwnedProjectTrustPrompt, OwnedProjectTrustPromptRequest } from "./project-trust-prompt.js";
5
5
  export { createConsoleSessionForkPrompt } from "./session-fork-prompt.js";
6
6
  export { SETTINGS_APP_ID, SETTINGS_ROUTE, SETTINGS_SHORTCUTS, SettingsApp } from "./settings-app.js";
7
+ export { REFERENCE_SCREEN_SHORTCUTS, ReferenceScreenApp } from "./reference-screen-app.js";
8
+ export type { ReferenceDocumentProvider, ReferenceScreenOptions } from "./reference-screen-app.js";
9
+ export { CHANGELOG_APP_ID, CHANGELOG_ROUTE, CHANGELOG_TITLE, HOTKEYS_APP_ID, HOTKEYS_ROUTE, HOTKEYS_TITLE, } from "./reference-routes.js";
@@ -2,3 +2,5 @@ export { runOwnedUi, terminateOwnedUiProcess } from "./run.js";
2
2
  export { createConsoleProjectTrustPrompt } from "./project-trust-prompt.js";
3
3
  export { createConsoleSessionForkPrompt } from "./session-fork-prompt.js";
4
4
  export { SETTINGS_APP_ID, SETTINGS_ROUTE, SETTINGS_SHORTCUTS, SettingsApp } from "./settings-app.js";
5
+ export { REFERENCE_SCREEN_SHORTCUTS, ReferenceScreenApp } from "./reference-screen-app.js";
6
+ export { CHANGELOG_APP_ID, CHANGELOG_ROUTE, CHANGELOG_TITLE, HOTKEYS_APP_ID, HOTKEYS_ROUTE, HOTKEYS_TITLE, } from "./reference-routes.js";
@@ -0,0 +1,6 @@
1
+ export declare const CHANGELOG_APP_ID = "changelog";
2
+ export declare const CHANGELOG_ROUTE = "changelog";
3
+ export declare const CHANGELOG_TITLE = "What's New";
4
+ export declare const HOTKEYS_APP_ID = "hotkeys";
5
+ export declare const HOTKEYS_ROUTE = "hotkeys";
6
+ export declare const HOTKEYS_TITLE = "Keyboard Shortcuts";
@@ -0,0 +1,6 @@
1
+ export const CHANGELOG_APP_ID = "changelog";
2
+ export const CHANGELOG_ROUTE = "changelog";
3
+ export const CHANGELOG_TITLE = "What's New";
4
+ export const HOTKEYS_APP_ID = "hotkeys";
5
+ export const HOTKEYS_ROUTE = "hotkeys";
6
+ export const HOTKEYS_TITLE = "Keyboard Shortcuts";
@@ -0,0 +1,37 @@
1
+ import type { OwnedUiViewportSettings } from "../../contracts/owned-ui/index.js";
2
+ import type { AppHostServices, UiApp } from "../../ui/apps/index.js";
3
+ import { ShortcutRegistry, type PaneInputResult, type PaneMouseEvent, type PaneRect } from "../../ui/components/index.js";
4
+ type Action = "up" | "down" | "page-up" | "page-down" | "first" | "last" | "close";
5
+ export declare const REFERENCE_SCREEN_SHORTCUTS: ShortcutRegistry<Action>;
6
+ /** Rows of a document already styled and wrapped for one content width. */
7
+ export interface ReferenceDocumentProvider {
8
+ /** The rows for this width, or null while the document is not available yet. */
9
+ rows(width: number): readonly string[] | null;
10
+ /** Settles once `rows` answers; absent when the document is available at once. */
11
+ readonly ready?: Promise<void>;
12
+ }
13
+ export interface ReferenceScreenOptions {
14
+ readonly id: string;
15
+ readonly title: string;
16
+ readonly document: ReferenceDocumentProvider;
17
+ /** The reader's scroll settings, read on every frame so a live change is honored. */
18
+ readonly scrollSettings: () => OwnedUiViewportSettings;
19
+ }
20
+ /**
21
+ * Presents one read-only document full screen between two accent rules: the bold
22
+ * title leads the scrolled rows, the shared scrollbar rail runs beside them, and
23
+ * the hint line closes the frame. Rows come from a provider per content width and
24
+ * are cached until the width changes; the screen closes on Escape and leaves the
25
+ * interrupt chord to its host.
26
+ */
27
+ export declare class ReferenceScreenApp implements UiApp {
28
+ #private;
29
+ readonly id: string;
30
+ constructor(options: ReferenceScreenOptions);
31
+ onActivate(host: AppHostServices): void;
32
+ onClose(_host: AppHostServices): void;
33
+ render(rect: PaneRect, host: AppHostServices): readonly string[];
34
+ onInput(data: string, host: AppHostServices): PaneInputResult;
35
+ onMouse(event: PaneMouseEvent, _host: AppHostServices): PaneInputResult;
36
+ }
37
+ export {};
@@ -0,0 +1,232 @@
1
+ import { GLOBAL_SCOPE, PLAIN_THEME, RAIL_COLUMNS, ShortcutRegistry, ScrollbarRails, assertNoShortcutConflicts, displayWidth, padToWidth, scrollForTrackPage, scrollbarGeometry, scrollbarPresentation, scrollbarWheelRows, truncateToWidth, withScrollbarRail, } from "../../ui/components/index.js";
2
+ const SCOPE = "reference-screen";
3
+ /** Rows above the document: the top rule. */
4
+ const HEADER_ROWS = 1;
5
+ /** Rows below the document: the bottom rule and the hint line. */
6
+ const FOOTER_ROWS = 2;
7
+ // Compatibility: the transcript rail stays lit this long after a scroll, and repaints just after.
8
+ const SCROLL_LINGER_MS = 900;
9
+ const SCROLL_LINGER_REPAINT_MS = 925;
10
+ const LOADING_NOTICE = "Loading…";
11
+ export const REFERENCE_SCREEN_SHORTCUTS = new ShortcutRegistry();
12
+ // Compatibility: the footer reads as v2's `esc/ctrl+c close • ↑↓ scroll`, without ctrl+c, which is
13
+ // the A1 interrupt chord here rather than a close key.
14
+ REFERENCE_SCREEN_SHORTCUTS.declare({ key: "escape", scope: GLOBAL_SCOPE, description: "Close", section: "Screen", hint: { keys: "esc", does: "close" } }, "close");
15
+ REFERENCE_SCREEN_SHORTCUTS.declare({ key: "up", scope: SCOPE, description: "Scroll up", section: "Navigate", hint: { keys: "↑↓", does: "scroll" } }, "up");
16
+ REFERENCE_SCREEN_SHORTCUTS.declare({ key: "down", scope: SCOPE, description: "Scroll down", section: "Navigate", hint: { keys: "↑↓", does: "scroll" } }, "down");
17
+ REFERENCE_SCREEN_SHORTCUTS.declare({ key: "pageUp", scope: SCOPE, description: "Up a page", section: "Navigate" }, "page-up");
18
+ REFERENCE_SCREEN_SHORTCUTS.declare({ key: "pageDown", scope: SCOPE, description: "Down a page", section: "Navigate" }, "page-down");
19
+ REFERENCE_SCREEN_SHORTCUTS.declare({ key: "home", scope: SCOPE, description: "First row", section: "Navigate" }, "first");
20
+ REFERENCE_SCREEN_SHORTCUTS.declare({ key: "end", scope: SCOPE, description: "Last row", section: "Navigate" }, "last");
21
+ /** The separator v2's footer puts between its hints. */
22
+ const HINT_SEPARATOR = " • ";
23
+ assertNoShortcutConflicts(REFERENCE_SCREEN_SHORTCUTS.assemble());
24
+ const KEYS = {
25
+ "\u001b[A": "up",
26
+ "\u001b[B": "down",
27
+ "\u001b[5~": "pageUp",
28
+ "\u001b[6~": "pageDown",
29
+ // Protocol: xterm, VT220, and rxvt forms of the same two chords.
30
+ "\u001b[H": "home",
31
+ "\u001b[1~": "home",
32
+ "\u001b[7~": "home",
33
+ "\u001b[F": "end",
34
+ "\u001b[4~": "end",
35
+ "\u001b[8~": "end",
36
+ "\u001b": "escape",
37
+ };
38
+ /**
39
+ * Presents one read-only document full screen between two accent rules: the bold
40
+ * title leads the scrolled rows, the shared scrollbar rail runs beside them, and
41
+ * the hint line closes the frame. Rows come from a provider per content width and
42
+ * are cached until the width changes; the screen closes on Escape and leaves the
43
+ * interrupt chord to its host.
44
+ */
45
+ export class ReferenceScreenApp {
46
+ id;
47
+ #title;
48
+ #document;
49
+ #scrollSettings;
50
+ #scroll = 0;
51
+ #interruptArmed = false;
52
+ #failure = null;
53
+ // Performance: the provider wraps Markdown; wheel and drag frames must not repeat that work.
54
+ #cached = null;
55
+ // Invariant: rail hover and drag live in the shared keyed state, as the transcript's do.
56
+ #rails = new ScrollbarRails();
57
+ // Rationale: the rail as drawn in the last frame, or null when nothing can be pointed at.
58
+ #railFrame = null;
59
+ #bodyHeight = 0;
60
+ // Invariant: a scroll lights the rail until this time; the timer repaints once it has passed.
61
+ #activeUntil = 0;
62
+ #activityTimer;
63
+ #renderedScroll;
64
+ constructor(options) {
65
+ this.id = options.id;
66
+ this.#title = options.title;
67
+ this.#document = options.document;
68
+ this.#scrollSettings = options.scrollSettings;
69
+ }
70
+ onActivate(host) {
71
+ const ready = this.#document.ready;
72
+ if (ready === undefined)
73
+ return;
74
+ void ready.then(() => {
75
+ this.#cached = null;
76
+ host.requestRender();
77
+ }, (error) => {
78
+ this.#failure = `Could not load ${this.#title}: ${error instanceof Error ? error.message : String(error)}`;
79
+ host.requestRender();
80
+ });
81
+ }
82
+ onClose(_host) {
83
+ this.#clearActivityTimer();
84
+ this.#rails.clear();
85
+ }
86
+ render(rect, host) {
87
+ const theme = host.theme ?? PLAIN_THEME;
88
+ this.#interruptArmed = host.interruptArmed;
89
+ const settings = this.#scrollSettings();
90
+ const reservesRail = settings.scrollbarAppearance !== "hidden";
91
+ const contentWidth = reservesRail ? Math.max(0, rect.width - RAIL_COLUMNS) : rect.width;
92
+ const bodyHeight = Math.max(0, rect.height - HEADER_ROWS - FOOTER_ROWS);
93
+ this.#bodyHeight = bodyHeight;
94
+ const rows = this.#rows(contentWidth, theme);
95
+ const maxScroll = Math.max(0, rows.length - bodyHeight);
96
+ this.#scroll = Math.min(Math.max(0, this.#scroll), maxScroll);
97
+ const now = Date.now();
98
+ // Rationale: every way of scrolling ends in this frame, so a moved document is noticed here
99
+ // once rather than at each wheel, drag, and key branch.
100
+ if (this.#renderedScroll !== undefined && this.#renderedScroll !== this.#scroll)
101
+ this.#noteScrollActivity(host, now);
102
+ this.#renderedScroll = this.#scroll;
103
+ const geometry = scrollbarGeometry({ contentLength: rows.length, viewportHeight: bodyHeight, scroll: this.#scroll, trackHeight: bodyHeight });
104
+ const presentation = scrollbarPresentation({
105
+ geometry,
106
+ appearance: settings.scrollbarAppearance,
107
+ style: settings.scrollbarStyle,
108
+ hovered: this.#rails.isHovered(this.id),
109
+ dragging: this.#rails.isDragging(this.id),
110
+ activeUntil: this.#activeUntil,
111
+ now,
112
+ });
113
+ this.#railFrame = reservesRail && geometry !== null
114
+ ? { rail: { key: this.id, column: rect.width, rowStart: HEADER_ROWS, trackHeight: geometry.trackHeight }, geometry, page: bodyHeight }
115
+ : null;
116
+ const body = [];
117
+ for (let index = this.#scroll; index < this.#scroll + bodyHeight; index++) {
118
+ const row = rows[index] ?? "";
119
+ body.push(displayWidth(row) > contentWidth ? truncateToWidth(row, contentWidth) : row);
120
+ }
121
+ const withRail = withScrollbarRail(body, geometry, contentWidth, theme, { presentation });
122
+ const hint = this.#interruptArmed ? "press ctrl+c again to exit a1" : REFERENCE_SCREEN_SHORTCUTS.hint(SCOPE, HINT_SEPARATOR);
123
+ // Compatibility: the v2 reference screen frames its document between two border-coloured rules.
124
+ const rule = theme.fg("border", "─".repeat(rect.width));
125
+ // Compatibility: v2 sets its footer against the left edge, unlike the settings status line.
126
+ const frame = [rule, ...withRail, rule, padToWidth(theme.fg("dim", hint), rect.width)];
127
+ // Invariant: a rectangle too small for the chrome still gets exactly its rows, top first.
128
+ return frame.slice(0, rect.height).concat(Array(Math.max(0, rect.height - frame.length)).fill(""));
129
+ }
130
+ onInput(data, host) {
131
+ const action = REFERENCE_SCREEN_SHORTCUTS.resolve(KEYS[data] ?? data, SCOPE);
132
+ if (action === null)
133
+ return { consumed: false };
134
+ switch (action) {
135
+ case "close":
136
+ host.close();
137
+ return { consumed: true };
138
+ case "up":
139
+ this.#scrollBy(-1);
140
+ return { consumed: true };
141
+ case "down":
142
+ this.#scrollBy(1);
143
+ return { consumed: true };
144
+ case "page-up":
145
+ this.#scrollBy(-Math.max(1, this.#bodyHeight));
146
+ return { consumed: true };
147
+ case "page-down":
148
+ this.#scrollBy(Math.max(1, this.#bodyHeight));
149
+ return { consumed: true };
150
+ case "first":
151
+ this.#scroll = 0;
152
+ return { consumed: true };
153
+ case "last":
154
+ this.#scroll = Number.MAX_SAFE_INTEGER;
155
+ return { consumed: true };
156
+ }
157
+ }
158
+ onMouse(event, _host) {
159
+ if (event.kind === "wheel-up" || event.kind === "wheel-down") {
160
+ // Invariant: the whole screen owns wheel scrolling; it must not depend on the pointer's row.
161
+ const distance = scrollbarWheelRows(this.#scrollSettings().scrollbarSpeed);
162
+ this.#scrollBy(event.kind === "wheel-down" ? distance : -distance);
163
+ return { consumed: true };
164
+ }
165
+ const rail = this.#railPointer(event);
166
+ if (rail.owned)
167
+ return { consumed: true };
168
+ return { consumed: event.kind !== "motion", render: rail.changed };
169
+ }
170
+ // Rationale: the rail takes its share of a pointer report first: hover, a thumb drag, or a
171
+ // track page. Owned means the document must not see the report; changed means the rail looks different.
172
+ #railPointer(event) {
173
+ const frame = this.#railFrame;
174
+ const wasHovered = this.#rails.isHovered(this.id);
175
+ if (frame === null) {
176
+ this.#rails.clear();
177
+ return { owned: false, changed: wasHovered };
178
+ }
179
+ const pointer = { column: event.column, row: event.row - 1 };
180
+ if (this.#rails.isDragging(this.id)) {
181
+ // Invariant: a drag keeps the pointer wherever it goes until the button comes up.
182
+ if (event.kind === "release")
183
+ this.#rails.endDrag();
184
+ else {
185
+ const target = this.#rails.dragTo(frame.rail, frame.geometry, pointer);
186
+ if (target !== null)
187
+ this.#scroll = target;
188
+ }
189
+ return { owned: true, changed: true };
190
+ }
191
+ const over = this.#rails.notePointer([frame.rail], pointer) !== null;
192
+ if (!over)
193
+ return { owned: false, changed: wasHovered };
194
+ if (event.kind === "press" && !this.#rails.beginDrag(frame.rail, frame.geometry, pointer)) {
195
+ this.#scroll = scrollForTrackPage(frame.geometry, pointer.row - frame.rail.rowStart, this.#scroll, frame.page);
196
+ }
197
+ return { owned: true, changed: true };
198
+ }
199
+ // Invariant: the title is the first document row, as in v2, so it scrolls with the document.
200
+ #rows(width, theme) {
201
+ // Compatibility: one leading space, so the title lines up with the padded Markdown rows as in v2.
202
+ const title = ` ${theme.bold(theme.fg("accent", this.#title))}`;
203
+ if (this.#failure !== null)
204
+ return [title, "", this.#failure];
205
+ const cached = this.#cached;
206
+ if (cached !== null && cached.width === width)
207
+ return [title, ...cached.rows];
208
+ const rows = this.#document.rows(width);
209
+ if (rows === null)
210
+ return [title, "", LOADING_NOTICE];
211
+ this.#cached = { width, rows };
212
+ return [title, ...rows];
213
+ }
214
+ #scrollBy(distance) {
215
+ // Invariant: the upper clamp happens at render, where the body height and row count are known.
216
+ this.#scroll = Math.max(0, this.#scroll + distance);
217
+ }
218
+ #noteScrollActivity(host, now) {
219
+ this.#activeUntil = Math.max(this.#activeUntil, now + SCROLL_LINGER_MS);
220
+ this.#clearActivityTimer();
221
+ this.#activityTimer = setTimeout(() => {
222
+ this.#activityTimer = undefined;
223
+ host.requestRender();
224
+ }, SCROLL_LINGER_REPAINT_MS);
225
+ this.#activityTimer.unref?.();
226
+ }
227
+ #clearActivityTimer() {
228
+ if (this.#activityTimer !== undefined)
229
+ clearTimeout(this.#activityTimer);
230
+ this.#activityTimer = undefined;
231
+ }
232
+ }
@@ -74,7 +74,6 @@ export class SettingsApp {
74
74
  #filter = null;
75
75
  #menu = null;
76
76
  #structured = null;
77
- #loading = true;
78
77
  #interruptArmed = false;
79
78
  // Invariant: pending values remain visible until the source reflects them.
80
79
  #pending = new Map();
@@ -98,10 +97,7 @@ export class SettingsApp {
98
97
  this.#session = session;
99
98
  }
100
99
  onActivate(host) {
101
- void this.#session.load().then(() => {
102
- this.#loading = false;
103
- host.requestRender();
104
- });
100
+ void this.#session.load().then(() => host.requestRender());
105
101
  }
106
102
  onClose(_host) {
107
103
  this.#clearActivityTimer();
@@ -161,8 +157,7 @@ export class SettingsApp {
161
157
  const body = [];
162
158
  this.#frameRows = [];
163
159
  if (rows.length === 0) {
164
- const message = this.#loading ? "Loading settings…" : "No settings found.";
165
- body.push(...renderEmptyState(message, "👀", bodyHeight, contentWidth, theme));
160
+ body.push(...renderEmptyState("No settings found.", "👀", bodyHeight, contentWidth, theme));
166
161
  }
167
162
  else {
168
163
  if (layout.topPadding > 0)
@@ -22,6 +22,8 @@ export { STABLE_RELEASE_SCHEMA, createStableReleaseEvidence, verifyStableRegistr
22
22
  export type { StableRegistryState, StableRegistryVerificationOptions, StableReleaseEvidence, StableReleaseEvidenceInput } from "./stable-release.js";
23
23
  export { PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
24
24
  export type { ProcessRequest, ProcessResult, SelfUpdateOptions, UpdateActivationPhase, UpdateChannel, UpdateFileSystem, UpdateLifecycleCoordinator, UpdateMaterializationProgress, UpdateMeasuredPhase, UpdateOutput, UpdateOwnershipAction, UpdatePackageReplacementInput, UpdatePerformanceEvidence, UpdatePhaseTimingEvent, UpdateProcessRunner, UpdateTransactionJournal, } from "./update.js";
25
+ export { UPDATE_ACTIVATION_CONTRACT, UPDATE_ACTIVATION_ENTRY, UPDATE_ACTIVATION_MANIFEST_FIELD, activateInstalledRelease, delegateActivation, readActivationContracts, runActivationEntry, } from "./update-activation.js";
26
+ export type { UpdateActivationCallbacks, UpdateActivationEvent, UpdateActivationRequest } from "./update-activation.js";
25
27
  export { selectSupervisorLaunchReleaseId, selectUpdateLaunchRelease } from "./update-launch.js";
26
28
  export { UPDATE_RECOVERY_SCHEMA, cleanupUpdateRecoveryCapsules, inspectUpdateLauncherSet, prepareUpdateRecoveryCapsule, readUpdateRecoveryCapsule, removeUpdateRecoveryCapsule, runProtectedPackageReplacement, updateLauncherPaths, } from "./update-recovery.js";
27
29
  export type { ProtectedPackageReplacementOptions, ProtectedPackageReplacementResult, UpdateRecoveryCapsule, UpdateRecoveryResult } from "./update-recovery.js";
@@ -10,6 +10,7 @@ export { RELEASE_MANIFEST_FILENAME, assertImmutableExecutionRoot, consumeMateria
10
10
  export { createRestartSeal, readRestartCertifiedRelease, releaseCertificationDocument, restartSealDigest } from "./restart-certification.js";
11
11
  export { STABLE_RELEASE_SCHEMA, createStableReleaseEvidence, verifyStableRegistry } from "./stable-release.js";
12
12
  export { PRODUCT_PACKAGE, assertUpdatePerformanceBudget, createNpmProcessRunner, createUpdateLifecycleCoordinator, planUpdateOwnership, renderUpdateProgressBar, runSelfUpdate, } from "./update.js";
13
+ export { UPDATE_ACTIVATION_CONTRACT, UPDATE_ACTIVATION_ENTRY, UPDATE_ACTIVATION_MANIFEST_FIELD, activateInstalledRelease, delegateActivation, readActivationContracts, runActivationEntry, } from "./update-activation.js";
13
14
  export { selectSupervisorLaunchReleaseId, selectUpdateLaunchRelease } from "./update-launch.js";
14
15
  export { UPDATE_RECOVERY_SCHEMA, cleanupUpdateRecoveryCapsules, inspectUpdateLauncherSet, prepareUpdateRecoveryCapsule, readUpdateRecoveryCapsule, removeUpdateRecoveryCapsule, runProtectedPackageReplacement, updateLauncherPaths, } from "./update-recovery.js";
15
16
  export { UPDATE_JOURNAL_SCHEMA, UpdateTransactionStore } from "./update-transaction.js";