@timurproko/a1 0.1.8-dev.563 → 0.1.8-dev.573

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 (71) hide show
  1. package/README.md +13 -11
  2. package/dist/app/session-shell/prompt-chips.js +20 -28
  3. package/dist/app/session-shell/session-shell-root.d.ts +2 -0
  4. package/dist/app/session-shell/session-shell-root.js +60 -17
  5. package/dist/app/session-shell/session-shell.js +35 -1
  6. package/dist/app/session-shell/session-viewport-controller.d.ts +1 -0
  7. package/dist/app/session-shell/session-viewport-controller.js +52 -30
  8. package/dist/cli/dispatch.d.ts +3 -1
  9. package/dist/cli/dispatch.js +56 -56
  10. package/dist/cli/package-messages.d.ts +4 -3
  11. package/dist/cli/package-messages.js +23 -11
  12. package/dist/contracts/owned-ui/index.d.ts +2 -0
  13. package/dist/contracts/owned-ui/index.js +1 -0
  14. package/dist/contracts/owned-ui/prompt-chips.d.ts +15 -0
  15. package/dist/contracts/owned-ui/prompt-chips.js +59 -0
  16. package/dist/contracts/owned-ui/validation.js +1 -1
  17. package/dist/integrations/pi/components/index.d.ts +1 -0
  18. package/dist/integrations/pi/components/index.js +1 -0
  19. package/dist/integrations/pi/components/modal-frame.d.ts +41 -0
  20. package/dist/integrations/pi/components/modal-frame.js +110 -0
  21. package/dist/integrations/pi/components/models-dialog.js +19 -5
  22. package/dist/integrations/pi/components/shell-footer-status.d.ts +4 -3
  23. package/dist/integrations/pi/components/shell-footer-status.js +53 -15
  24. package/dist/integrations/pi/components/shell-selectors-dialogs.js +38 -2
  25. package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -1
  26. package/dist/integrations/pi/components/skills-dialog.js +3 -3
  27. package/dist/integrations/pi/components/submitted-prompt-adapter.d.ts +1 -1
  28. package/dist/integrations/pi/components/submitted-prompt-adapter.js +5 -2
  29. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
  30. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +3 -2
  31. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +2 -1
  32. package/dist/integrations/pi/components/upstream/components/extension-editor.js +7 -6
  33. package/dist/integrations/pi/components/upstream/components/extension-input.js +9 -8
  34. package/dist/integrations/pi/components/upstream/components/extension-selector.js +10 -9
  35. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  36. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +3 -3
  37. package/dist/integrations/pi/components/upstream/components/session-selector.js +13 -6
  38. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +7 -6
  39. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +15 -12
  40. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +1 -1
  41. package/dist/integrations/pi/components/upstream/components/tree-selector.js +13 -6
  42. package/dist/integrations/pi/components/upstream/components/trust-selector.js +9 -9
  43. package/dist/integrations/pi/engine/compaction-progress.d.ts +3 -7
  44. package/dist/integrations/pi/engine/compaction-progress.js +11 -10
  45. package/dist/integrations/pi/engine/repository-pr.d.ts +3 -3
  46. package/dist/integrations/pi/engine/repository-pr.js +6 -6
  47. package/dist/integrations/pi/engine/session-runtime.js +5 -4
  48. package/dist/integrations/pi/engine/settings-bridge.js +1 -1
  49. package/dist/integrations/pi/engine/settings-port.js +1 -1
  50. package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -0
  51. package/dist/integrations/pi/tui-runtime/adapter.js +11 -2
  52. package/dist/integrations/pi/tui-runtime/contracts.d.ts +2 -0
  53. package/dist/integrations/pi/tui-runtime/mouse-report-input.d.ts +6 -0
  54. package/dist/integrations/pi/tui-runtime/mouse-report-input.js +34 -0
  55. package/dist/native/darwin-arm64/manifest.json +1 -1
  56. package/dist/native/linux-x64/manifest.json +1 -1
  57. package/dist/native/win32-x64/manifest.json +3 -3
  58. package/dist/native/win32-x64/process-guardian.exe +0 -0
  59. package/dist/ui/components/dialog-panel.js +10 -6
  60. package/dist/ui/components/index.d.ts +2 -1
  61. package/dist/ui/components/index.js +1 -1
  62. package/dist/ui/components/mouse.d.ts +2 -2
  63. package/dist/ui/components/mouse.js +2 -2
  64. package/dist/ui/components/progress-status.d.ts +9 -2
  65. package/dist/ui/components/progress-status.js +24 -3
  66. package/dist/ui/components/transcript-viewport.d.ts +9 -3
  67. package/dist/ui/components/transcript-viewport.js +116 -136
  68. package/docs/architecture/ui-reference-provenance.md +8 -5
  69. package/docs/openspec-archive-automation.md +2 -2
  70. package/docs/validation.md +1 -1
  71. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { interactiveLaunchIntent, parseSessionSelection } from "../features/launch/index.js";
2
- import { packageCommandHelp, renderPackageSyntax } from "./package-messages.js";
2
+ import { packageCommandHelp, renderPackageSyntax, } from "./package-messages.js";
3
3
  import { PRODUCT_TEXT } from "../product-identity.js";
4
4
  export function cliUsage(capabilities) {
5
5
  return PRODUCT_TEXT.usage([
@@ -7,22 +7,19 @@ export function cliUsage(capabilities) {
7
7
  "--session <path|id>",
8
8
  "--session-dir <dir> --session <path|id>",
9
9
  ...(capabilities.developmentComparison ? ["pi"] : []),
10
- "--help",
11
- "-h",
12
- "--version",
13
- "-v",
10
+ "help",
11
+ "version",
14
12
  "update",
15
13
  "update --develop [preview-or-version]",
16
14
  "update --models",
15
+ "update --extensions",
16
+ "update <source>",
17
+ "install <source>",
18
+ "remove <source>",
19
+ "uninstall <source>",
20
+ "list",
17
21
  "session link-worktree <path>",
18
22
  "session unlink-worktree",
19
- "pi install <source>",
20
- "pi remove <source>",
21
- "pi uninstall <source>",
22
- "pi list",
23
- "pi update --extensions",
24
- "pi update --models",
25
- "pi update <source>",
26
23
  ]);
27
24
  }
28
25
  export function cliHelp(capabilities) {
@@ -33,29 +30,26 @@ export function cliHelp(capabilities) {
33
30
  ` ${command} --session <path|id>`,
34
31
  ` ${command} --session-dir <dir> --session <path|id>`,
35
32
  ...(capabilities.developmentComparison ? [` ${command} pi`] : []),
36
- ` ${command} --help`,
37
- ` ${command} -h`,
38
- ` ${command} --version`,
39
- ` ${command} -v`,
33
+ ` ${command} help`,
34
+ ` ${command} version`,
40
35
  "",
41
36
  "Update:",
42
37
  ` ${command} update`,
43
38
  ` ${command} update --develop [preview-or-version]`,
44
39
  ` ${command} update --models`,
40
+ ` ${command} update --extensions`,
41
+ ` ${command} update <source>`,
42
+ "",
43
+ "Packages:",
44
+ ` ${command} install <source>`,
45
+ ` ${command} remove <source>`,
46
+ ` ${command} uninstall <source>`,
47
+ ` ${command} list`,
45
48
  "",
46
49
  "Session context:",
47
50
  ` ${command} session link-worktree <path>`,
48
51
  ` ${command} session unlink-worktree`,
49
52
  "",
50
- "Pi-compatible packages:",
51
- ` ${command} pi install <source>`,
52
- ` ${command} pi remove <source>`,
53
- ` ${command} pi uninstall <source>`,
54
- ` ${command} pi list`,
55
- ` ${command} pi update --extensions`,
56
- ` ${command} pi update --models`,
57
- ` ${command} pi update <source>`,
58
- "",
59
53
  ].join("\n");
60
54
  }
61
55
  const PROFILE_WORDS = new Set(["pi"]);
@@ -68,11 +62,11 @@ export async function dispatchCli(arguments_, handlers, output, capabilities) {
68
62
  return 0;
69
63
  }
70
64
  if (command.kind === "package-help") {
71
- output.stdout(packageCommandHelp(command.verb));
65
+ output.stdout(packageCommandHelp(command.verb, command.namespace));
72
66
  return 0;
73
67
  }
74
68
  if (command.kind === "package-error") {
75
- output.stderr(renderPackageSyntax(command.diagnostic));
69
+ output.stderr(renderPackageSyntax(command.diagnostic, command.namespace));
76
70
  return 2;
77
71
  }
78
72
  if (command.kind === "error") {
@@ -102,13 +96,16 @@ export function parseCliCommand(arguments_, capabilities) {
102
96
  return { kind: "error", message: PRODUCT_TEXT.diagnostic(`could not parse session launch: ${error instanceof Error ? error.message : String(error)}`) };
103
97
  }
104
98
  }
105
- if (command === "--help" || command === "-h")
99
+ if (command === "help" || command === "--help" || command === "-h")
106
100
  return withoutArguments(rest, { kind: "help" });
107
- if (command === "--version" || command === "-v")
101
+ if (command === "version" || command === "--version" || command === "-v")
108
102
  return withoutArguments(rest, { kind: "version" });
103
+ if (command === "install" || command === "remove" || command === "uninstall" || command === "list") {
104
+ return parsePackageCommand(arguments_, "direct");
105
+ }
109
106
  if (command === "pi") {
110
107
  if (rest.length > 0)
111
- return parsePiPackageCommand(rest);
108
+ return parsePackageCommand(rest, "pi");
112
109
  return capabilities.developmentComparison ? { kind: "launch", profileId: "pi" } : { kind: "noop" };
113
110
  }
114
111
  if (command === "update")
@@ -143,6 +140,9 @@ function parseSessionContext(rest) {
143
140
  * operation that cannot be combined with either update channel.
144
141
  */
145
142
  function parseUpdate(rest) {
143
+ if (rest.some(value => value === "--help" || value === "-h")) {
144
+ return { kind: "package-help", verb: "update", namespace: "direct" };
145
+ }
146
146
  if (rest.length === 0)
147
147
  return { kind: "update", channel: "stable" };
148
148
  const [selector, ...values] = rest;
@@ -167,10 +167,9 @@ function parseUpdate(rest) {
167
167
  }
168
168
  if (selector === "pi")
169
169
  return pinnedPiUpdateError();
170
- if (selector !== undefined && selector.startsWith("-"))
170
+ if (selector === "--self" || selector === "--all")
171
171
  return unknownOption(selector, "update");
172
- // Rationale: positional update targets are reserved for a future A1-native plugin model.
173
- return { kind: "noop" };
172
+ return parsePackageUpdate(rest, "direct");
174
173
  }
175
174
  function isDevelopmentTarget(target) {
176
175
  return /^[1-9]\d*$/.test(target) || /^\d+\.\d+\.\d+-dev\.[1-9]\d*$/.test(target);
@@ -178,48 +177,49 @@ function isDevelopmentTarget(target) {
178
177
  function updateGrammarError(detail) {
179
178
  return { kind: "error", message: PRODUCT_TEXT.diagnostic(`could not parse update: ${detail}`) };
180
179
  }
181
- function parsePiPackageCommand(arguments_) {
180
+ function parsePackageCommand(arguments_, namespace) {
182
181
  const [verb, ...rest] = arguments_;
183
182
  if ((verb === "install" || verb === "remove" || verb === "uninstall" || verb === "list" || verb === "update")
184
183
  && rest.some(value => value === "--help" || value === "-h")) {
185
- return { kind: "package-help", verb: verb === "uninstall" ? "remove" : verb };
184
+ return { kind: "package-help", verb: verb === "uninstall" ? "remove" : verb, namespace };
186
185
  }
187
186
  if (verb === "install" || verb === "remove" || verb === "uninstall") {
188
- return parseSourceCommand(verb === "install" ? "install" : "remove", rest);
187
+ return parseSourceCommand(verb === "install" ? "install" : "remove", rest, namespace);
189
188
  }
190
189
  if (verb === "list") {
191
190
  const flag = rest.find(argument => argument.startsWith("-"));
192
191
  if (flag !== undefined)
193
- return packageOptionError(flag, "list");
192
+ return packageOptionError(flag, "list", namespace);
194
193
  if (rest[0] !== undefined)
195
- return packageSyntax({ verb: "list", kind: "unexpected-argument", value: rest[0] });
194
+ return packageSyntax({ verb: "list", kind: "unexpected-argument", value: rest[0] }, namespace);
196
195
  return { kind: "packages", request: { verb: "list", source: null } };
197
196
  }
198
197
  if (verb === "update")
199
- return parsePiPackageUpdate(rest);
198
+ return parsePackageUpdate(rest, namespace);
200
199
  return { kind: "noop" };
201
200
  }
202
- function parsePiPackageUpdate(rest) {
201
+ function parsePackageUpdate(rest, namespace) {
203
202
  if (rest.length === 0)
204
203
  return pinnedPiUpdateError();
205
204
  if (rest.length > 1) {
206
205
  const unknown = rest.find(value => value.startsWith("-") && !["--extensions", "--models", "--self", "--all"].includes(value));
207
206
  if (unknown !== undefined)
208
- return packageOptionError(unknown, "update");
207
+ return packageOptionError(unknown, "update", namespace);
209
208
  if (rest.includes("--self") || rest.includes("--all") || rest.includes("self") || rest.includes("pi"))
210
209
  return pinnedPiUpdateError();
211
210
  if (rest.every(value => value === rest[0]) && rest[0]?.startsWith("-")) {
212
- return { kind: "error", message: PRODUCT_TEXT.diagnostic("pi update accepts one target.") };
211
+ const command = namespace === "direct" ? "update" : "pi update";
212
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic(`${command} accepts one target.`) };
213
213
  }
214
214
  const sources = rest.filter(value => !value.startsWith("-"));
215
215
  if (sources[1] !== undefined)
216
- return packageSyntax({ verb: "update", kind: "unexpected-argument", value: sources[1] });
216
+ return packageSyntax({ verb: "update", kind: "unexpected-argument", value: sources[1] }, namespace);
217
217
  const message = rest.includes("--models")
218
218
  ? rest.includes("--extensions")
219
219
  ? "--models cannot be combined with --self, --extensions, --all, or --extension"
220
220
  : "--models cannot be combined with a positional source"
221
221
  : "positional update targets cannot be combined with --self, --extensions, or --all";
222
- return packageSyntax({ verb: "update", kind: "conflict", message });
222
+ return packageSyntax({ verb: "update", kind: "conflict", message }, namespace);
223
223
  }
224
224
  const [target] = rest;
225
225
  if (target === "--extensions")
@@ -229,7 +229,7 @@ function parsePiPackageUpdate(rest) {
229
229
  if (target === "--self" || target === "--all" || target === "self" || target === "pi")
230
230
  return pinnedPiUpdateError();
231
231
  if (target === undefined || target.startsWith("-"))
232
- return packageOptionError(target ?? "", "update");
232
+ return packageOptionError(target ?? "", "update", namespace);
233
233
  return { kind: "packages", request: { verb: "update", source: target } };
234
234
  }
235
235
  function pinnedPiUpdateError() {
@@ -239,35 +239,35 @@ function pinnedPiUpdateError() {
239
239
  PRODUCT_TEXT.diagnostic("pins its certified Pi runtime and cannot update it independently."),
240
240
  "Use:",
241
241
  ` ${PRODUCT_TEXT.commandName} update Update a1`,
242
- ` ${PRODUCT_TEXT.commandName} pi update --extensions Update Pi-compatible packages`,
243
- ` ${PRODUCT_TEXT.commandName} pi update --models Refresh model catalogs`,
242
+ ` ${PRODUCT_TEXT.commandName} update --extensions Update Pi-compatible packages`,
243
+ ` ${PRODUCT_TEXT.commandName} update --models Refresh model catalogs`,
244
244
  ].join("\n"),
245
245
  };
246
246
  }
247
- function parseSourceCommand(verb, rest) {
247
+ function parseSourceCommand(verb, rest, namespace) {
248
248
  const flag = rest.find(argument => argument.startsWith("-"));
249
249
  if (flag !== undefined)
250
- return packageOptionError(flag, verb);
250
+ return packageOptionError(flag, verb, namespace);
251
251
  const [source, extra] = rest;
252
252
  if (extra !== undefined)
253
- return packageSyntax({ verb, kind: "unexpected-argument", value: extra });
253
+ return packageSyntax({ verb, kind: "unexpected-argument", value: extra }, namespace);
254
254
  if (source === undefined)
255
- return packageSyntax({ verb, kind: "missing-source" });
255
+ return packageSyntax({ verb, kind: "missing-source" }, namespace);
256
256
  if (PROFILE_WORDS.has(source))
257
257
  return profileRejection(verb);
258
258
  return { kind: "packages", request: { verb, source } };
259
259
  }
260
- function packageSyntax(diagnostic) {
261
- return { kind: "package-error", diagnostic };
260
+ function packageSyntax(diagnostic, namespace) {
261
+ return { kind: "package-error", diagnostic, namespace };
262
262
  }
263
- function packageOptionError(option, verb) {
263
+ function packageOptionError(option, verb, namespace) {
264
264
  if (option.startsWith("--profile") || option === "-l" || option === "--local")
265
265
  return profileRejection(verb);
266
266
  // Compatibility: these recognized Pi flags remain outside A1's supported subset.
267
267
  if (["--extension", "--approve", "--no-approve", "-a", "-na", "--force"].includes(option)) {
268
- return unknownOption(option, `pi ${verb}`);
268
+ return unknownOption(option, namespace === "direct" ? verb : `pi ${verb}`);
269
269
  }
270
- return packageSyntax({ verb, kind: "unknown-option", value: option });
270
+ return packageSyntax({ verb, kind: "unknown-option", value: option }, namespace);
271
271
  }
272
272
  function withoutArguments(rest, command) {
273
273
  if (rest.length === 0)
@@ -1,6 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  /** Supported subset of pinned Pi's package-manager CLI presentation. */
3
3
  export type PackageCliVerb = "install" | "remove" | "list" | "update";
4
+ export type PackageCommandNamespace = "direct" | "pi";
4
5
  export type PackageSyntaxDiagnostic = {
5
6
  readonly verb: PackageCliVerb;
6
7
  readonly kind: "missing-source";
@@ -18,9 +19,9 @@ export type PackageSyntaxDiagnostic = {
18
19
  readonly message: string;
19
20
  };
20
21
  type MessageStyle = Pick<typeof chalk, "red" | "dim" | "bold">;
21
- export declare function packageCommandUsage(verb: PackageCliVerb): string;
22
+ export declare function packageCommandUsage(verb: PackageCliVerb, namespace?: PackageCommandNamespace): string;
22
23
  /** Diagnostic facts stay unstyled until the terminal boundary decides color support. */
23
- export declare function renderPackageSyntax(diagnostic: PackageSyntaxDiagnostic, style?: MessageStyle): string;
24
+ export declare function renderPackageSyntax(diagnostic: PackageSyntaxDiagnostic, namespace?: PackageCommandNamespace, style?: MessageStyle): string;
24
25
  /** Pi's help layout, with unsupported options and examples deliberately absent. */
25
- export declare function packageCommandHelp(verb: PackageCliVerb, style?: MessageStyle): string;
26
+ export declare function packageCommandHelp(verb: PackageCliVerb, namespace?: PackageCommandNamespace, style?: MessageStyle): string;
26
27
  export {};
@@ -2,21 +2,26 @@
2
2
  // at 914cf1472e715297caa30db4b9535d534a9eb718 (MIT); grammar is restricted to A1's supported subset.
3
3
  import chalk from "chalk";
4
4
  import { PRODUCT_TEXT } from "../product-identity.js";
5
- export function packageCommandUsage(verb) {
6
- const command = `${PRODUCT_TEXT.commandName} pi ${verb}`;
5
+ export function packageCommandUsage(verb, namespace = "pi") {
6
+ const command = namespace === "direct"
7
+ ? `${PRODUCT_TEXT.commandName} ${verb}`
8
+ : `${PRODUCT_TEXT.commandName} pi ${verb}`;
7
9
  if (verb === "install" || verb === "remove")
8
10
  return `${command} <source>`;
11
+ if (verb === "update" && namespace === "direct") {
12
+ return `${command} [--develop [preview-or-version]|--models|--extensions|source]`;
13
+ }
9
14
  if (verb === "update")
10
15
  return `${command} [source|--extensions|--models]`;
11
16
  return command;
12
17
  }
13
18
  /** Diagnostic facts stay unstyled until the terminal boundary decides color support. */
14
- export function renderPackageSyntax(diagnostic, style = chalk) {
15
- const usage = packageCommandUsage(diagnostic.verb);
19
+ export function renderPackageSyntax(diagnostic, namespace = "pi", style = chalk) {
20
+ const usage = packageCommandUsage(diagnostic.verb, namespace);
16
21
  let message;
17
22
  if (diagnostic.kind === "unknown-option") {
18
23
  return `${style.red(`Unknown option ${diagnostic.value} for "${diagnostic.verb}".`)}\n`
19
- + `${style.dim(`Use "${PRODUCT_TEXT.commandName} --help" or "${usage}".`)}\n`;
24
+ + `${style.dim(`Use "${PRODUCT_TEXT.commandName} help" or "${usage}".`)}\n`;
20
25
  }
21
26
  if (diagnostic.kind === "missing-source")
22
27
  message = `Missing ${diagnostic.verb} source.`;
@@ -27,9 +32,16 @@ export function renderPackageSyntax(diagnostic, style = chalk) {
27
32
  return `${style.red(message)}\n${style.dim(`Usage: ${usage}`)}\n`;
28
33
  }
29
34
  /** Pi's help layout, with unsupported options and examples deliberately absent. */
30
- export function packageCommandHelp(verb, style = chalk) {
31
- const command = `${PRODUCT_TEXT.commandName} pi`;
32
- const header = `${style.bold("Usage:")}\n ${packageCommandUsage(verb)}\n\n`;
35
+ export function packageCommandHelp(verb, namespace = "pi", style = chalk) {
36
+ const command = namespace === "direct" ? PRODUCT_TEXT.commandName : `${PRODUCT_TEXT.commandName} pi`;
37
+ const header = verb === "update" && namespace === "direct"
38
+ ? `${style.bold("Usage:")}\n`
39
+ + ` ${command} update\n`
40
+ + ` ${command} update --develop [preview-or-version]\n`
41
+ + ` ${command} update --models\n`
42
+ + ` ${command} update --extensions\n`
43
+ + ` ${command} update <source>\n\n`
44
+ : `${style.bold("Usage:")}\n ${packageCommandUsage(verb, namespace)}\n\n`;
33
45
  switch (verb) {
34
46
  case "install":
35
47
  return header + `Install a package and add it to settings.
@@ -55,14 +67,14 @@ Examples:
55
67
  case "list":
56
68
  return header + "List installed packages from user settings.\n\n";
57
69
  case "update":
58
- return header + `Update installed packages or model catalogs.
70
+ return header + `${namespace === "direct" ? "Update A1, installed packages, or model catalogs." : "Update installed packages or model catalogs."}
59
71
 
60
72
  Options:
61
- --extensions Update installed packages only
73
+ ${namespace === "direct" ? " --develop [target] Update to the development channel or one preview\n" : ""} --extensions Update installed packages only
62
74
  --models Refresh model catalogs only
63
75
 
64
76
  Short forms:
65
- ${command} update --extensions Update installed packages only
77
+ ${namespace === "direct" ? ` ${command} update Update A1 to the stable release\n ${command} update --develop Update A1 to the development preview\n` : ""} ${command} update --extensions Update installed packages only
66
78
  ${command} update --models Refresh model catalogs only
67
79
  ${command} update <source> Update one package
68
80
 
@@ -5,6 +5,8 @@ export type { OwnedUiBlockStatus, OwnedUiCommand, OwnedUiCommandOutcome, OwnedUi
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";
8
+ export { canonicalPromptChipMatches, protectPromptChipWrapping, replaceCanonicalPromptChips } from "./prompt-chips.js";
9
+ export type { PromptChipTextMatch, PromptChipWrapProtection } from "./prompt-chips.js";
8
10
  export { SUGGESTION_DECISION_REASONS, SUGGESTION_DIAGNOSTIC_EVENTS } from "./suggestion-diagnostics.js";
9
11
  export type { SuggestionDecision, SuggestionDecisionReason, SuggestionDiagnosticEvent, SuggestionDiagnosticObserver, SuggestionDiagnosticRecord, } from "./suggestion-diagnostics.js";
10
12
  export { PROMPT_HISTORY_EDITOR_REPLACEMENT, PROMPT_HISTORY_MAX_ENTRY_BYTES, PROMPT_HISTORY_MAX_PENDING, PROMPT_HISTORY_MAX_TEXT_BYTES, assertPromptHistorySubmission, } from "./prompt-history.js";
@@ -3,6 +3,7 @@ export { OWNED_UI_CONTRACT_VERSION } from "./model.js";
3
3
  export { acceptsTranscriptUpdate, transcriptToolState } from "./transcript-lifecycle.js";
4
4
  export { ImageAttachmentError, MAX_CLIPBOARD_TEXT_BYTES, MAX_IMAGE_DATA_BYTES, MAX_PROMPT_IMAGES, assertImageEncodedSize, assertPromptImages, } from "./image-attachments.js";
5
5
  export { CONTEXTUAL_PROMPT_SUGGESTION_INSTRUCTION, normalizePromptSuggestionCandidate } from "./prompt-suggestions.js";
6
+ export { canonicalPromptChipMatches, protectPromptChipWrapping, replaceCanonicalPromptChips } from "./prompt-chips.js";
6
7
  export { SUGGESTION_DECISION_REASONS, SUGGESTION_DIAGNOSTIC_EVENTS } from "./suggestion-diagnostics.js";
7
8
  export { PROMPT_HISTORY_EDITOR_REPLACEMENT, PROMPT_HISTORY_MAX_ENTRY_BYTES, PROMPT_HISTORY_MAX_PENDING, PROMPT_HISTORY_MAX_TEXT_BYTES, assertPromptHistorySubmission, } from "./prompt-history.js";
8
9
  export { assertOwnedUiCommand, assertOwnedUiCustomization, assertOwnedUiDiagnostics, assertOwnedUiEditorState, assertOwnedUiEvent, assertOwnedUiPromptSuggestionIdentity, assertOwnedUiPromptSuggestionRequest, assertOwnedUiPromptSuggestionResult, assertOwnedUiPromptSuggestionState, assertOwnedUiSessionViewModel, assertOwnedUiSnapshot, assertOwnedUiStatusView, assertOwnedUiTerminalSurface, assertOwnedUiTranscriptBlock, } from "./validation.js";
@@ -0,0 +1,15 @@
1
+ export interface PromptChipTextMatch {
2
+ readonly text: string;
3
+ readonly start: number;
4
+ readonly end: number;
5
+ }
6
+ export interface PromptChipWrapProtection {
7
+ readonly text: string;
8
+ restore(rendered: string): string;
9
+ }
10
+ /** Finds canonical prompt-chip labels without requiring their process-local backing values. */
11
+ export declare function canonicalPromptChipMatches(text: string): readonly PromptChipTextMatch[];
12
+ /** Replaces only canonical prompt-chip labels while preserving every intervening source byte. */
13
+ export declare function replaceCanonicalPromptChips(text: string, replacement: (match: PromptChipTextMatch) => string): string;
14
+ /** Protects fitting chip labels for Markdown wrapping and restores their exact visible source text. */
15
+ export declare function protectPromptChipWrapping(text: string): PromptChipWrapProtection;
@@ -0,0 +1,59 @@
1
+ const PROMPT_CHIP_PATTERN = /\[(?:paste #\d+ (?:\+\d+ lines|\d+ chars)|📷 [^\]]+|📁 [^\]]+|📄 [^\]]+|🖼 {1,2}[^\]]+|🔗 [^\]]+)\]/gu;
2
+ const ZERO_WIDTH_MARKERS = ["\u2060", "\u200b", "\u200c", "\u200d", "\ufeff"];
3
+ /** Finds canonical prompt-chip labels without requiring their process-local backing values. */
4
+ export function canonicalPromptChipMatches(text) {
5
+ return [...text.matchAll(PROMPT_CHIP_PATTERN)].map(match => ({
6
+ text: match[0], start: match.index, end: match.index + match[0].length,
7
+ }));
8
+ }
9
+ /** Replaces only canonical prompt-chip labels while preserving every intervening source byte. */
10
+ export function replaceCanonicalPromptChips(text, replacement) {
11
+ return text.replace(PROMPT_CHIP_PATTERN, (value, offset) => replacement({
12
+ text: value, start: offset, end: offset + value.length,
13
+ }));
14
+ }
15
+ /** Protects fitting chip labels for Markdown wrapping and restores their exact visible source text. */
16
+ export function protectPromptChipWrapping(text) {
17
+ const matches = canonicalPromptChipMatches(text);
18
+ if (matches.length === 0)
19
+ return { text, restore: identity };
20
+ const spaceMarker = unusedPrivateMarker(text);
21
+ const boundaryMarker = unusedZeroWidthMarker(text);
22
+ let protectedText = "", cursor = 0;
23
+ for (const match of matches) {
24
+ protectedText += text.slice(cursor, match.start);
25
+ // Rationale: adjacent chips need a temporary break opportunity or Markdown treats the complete run as one word.
26
+ if (match.start === cursor && cursor > 0)
27
+ protectedText += `${boundaryMarker} `;
28
+ protectedText += match.text.replaceAll(" ", spaceMarker);
29
+ cursor = match.end;
30
+ }
31
+ protectedText += text.slice(cursor);
32
+ return {
33
+ text: protectedText,
34
+ restore: rendered => rendered
35
+ .replaceAll(`${boundaryMarker} `, "")
36
+ .replaceAll(boundaryMarker, "")
37
+ .replaceAll(spaceMarker, " "),
38
+ };
39
+ }
40
+ function unusedPrivateMarker(text) {
41
+ for (let codePoint = 0xe000; codePoint <= 0xf8ff; codePoint++) {
42
+ const marker = String.fromCodePoint(codePoint);
43
+ if (!text.includes(marker))
44
+ return marker;
45
+ }
46
+ throw new Error("Prompt chip text exhausts the private presentation marker range");
47
+ }
48
+ function unusedZeroWidthMarker(text) {
49
+ for (const marker of ZERO_WIDTH_MARKERS)
50
+ if (!text.includes(marker))
51
+ return marker;
52
+ for (const left of ZERO_WIDTH_MARKERS) {
53
+ for (const right of ZERO_WIDTH_MARKERS)
54
+ if (!text.includes(left + right))
55
+ return left + right;
56
+ }
57
+ throw new Error("Prompt chip text exhausts the zero-width presentation marker range");
58
+ }
59
+ function identity(value) { return value; }
@@ -353,7 +353,7 @@ export function assertOwnedUiStatusView(status) {
353
353
  assertBoundedText(status.title, "owned-UI status title", MAX_LABEL_LENGTH);
354
354
  assertOptionalText(status.workingMessage, "owned-UI working message", MAX_MESSAGE_LENGTH);
355
355
  if (status.workingProgress !== undefined && status.workingProgress !== null) {
356
- assertIntegerInRange(status.workingProgress, 0, 99, "owned-UI working progress");
356
+ assertIntegerInRange(status.workingProgress, 0, 100, "owned-UI working progress");
357
357
  }
358
358
  assertCollection(status.diagnostics, "owned-UI status diagnostics", MAX_STATUS_DIAGNOSTICS);
359
359
  for (const diagnostic of status.diagnostics) {
@@ -10,6 +10,7 @@ export { PINNED_PI_LAYOUT, applyConfiguredPiTheme, applyPiTheme, applyPiThemeIns
10
10
  export type { PiColorMode, PiTerminalTheme, PiTerminalThemeDetection, PiTerminalThemeDetector, PiThemeBackground, PiThemeResult } from "./theme.js";
11
11
  export { OwnedPiThemeController } from "./upstream/theme/theme-controller.js";
12
12
  export type { PiThemeRuntimePort, PiThemeSettingsPort } from "./upstream/theme/theme-controller.js";
13
+ export { PI_MODAL_CONTENT_PADDING_X, PiModalFrame, PiModalHeader, addPiModalHeader, adoptPiModalFrame, adoptPiModalHeader, } from "./modal-frame.js";
13
14
  export { ModelsDialogComponent } from "./models-dialog.js";
14
15
  export type { ModelsDialogCallbacks, ModelsDialogConfig, ModelsDialogFilter, ModelsDialogModel } from "./models-dialog.js";
15
16
  export { CountdownTimer } from "./upstream/components/countdown-timer.js";
@@ -5,6 +5,7 @@ export { OWNED_BUILTIN_SLASH_COMMANDS, PINNED_PI_BUILTIN_SLASH_COMMANDS, compone
5
5
  export { renderPiShellHotkeySections } from "./shell-hotkey-sections.js";
6
6
  export { PINNED_PI_LAYOUT, applyConfiguredPiTheme, applyPiTheme, applyPiThemeInstance, currentPiThemeName, detectPiTerminalBackgroundFromEnv, detectPiTerminalBackgroundTheme, detectPiTerminalThemeForAuto, ensurePiTheme, getAvailablePiThemes, loadPiTheme, onPiThemeChange, parsePiAutoThemeSetting, piTheme, resolvePiThemeSetting, stopPiThemeWatcher, } from "./theme.js";
7
7
  export { OwnedPiThemeController } from "./upstream/theme/theme-controller.js";
8
+ export { PI_MODAL_CONTENT_PADDING_X, PiModalFrame, PiModalHeader, addPiModalHeader, adoptPiModalFrame, adoptPiModalHeader, } from "./modal-frame.js";
8
9
  export { ModelsDialogComponent } from "./models-dialog.js";
9
10
  export { CountdownTimer } from "./upstream/components/countdown-timer.js";
10
11
  export { ExtensionEditorComponent } from "./upstream/components/extension-editor.js";
@@ -0,0 +1,41 @@
1
+ import { Container, type Component } from "@earendil-works/pi-tui";
2
+ export declare const PI_MODAL_CONTENT_PADDING_X = 1;
3
+ /** The semantic top rule/title pair used while a modal assembles its frame. */
4
+ export declare class PiModalHeader implements Component {
5
+ readonly rule: Component;
6
+ readonly title: Component;
7
+ constructor(rule: Component, title: Component);
8
+ invalidate(): void;
9
+ render(width: number): string[];
10
+ }
11
+ /**
12
+ * Complete framed-modal chrome. Rules remain frame-wide; every semantic content component renders
13
+ * in the same one-cell-smaller rectangle and receives the same left inset.
14
+ */
15
+ export declare class PiModalFrame implements Component {
16
+ #private;
17
+ constructor(topRule: Component, content: readonly Component[], bottomRule: Component, fullWidthContent?: readonly Component[], preInsetContent?: readonly Component[]);
18
+ invalidate(): void;
19
+ render(width: number): string[];
20
+ }
21
+ /** Add compact titled-modal chrome while a component assembles its semantic body. */
22
+ export declare function addPiModalHeader(container: Container, rule: Component, title: Component): PiModalHeader;
23
+ export interface PiModalFrameAdoption {
24
+ readonly topIndex: number;
25
+ readonly bottomIndex: number;
26
+ readonly header?: PiModalHeader;
27
+ readonly fullWidthContent?: readonly Component[];
28
+ /** Public pinned children that already own the same one-cell outer inset. */
29
+ readonly preInsetContent?: readonly Component[];
30
+ }
31
+ /**
32
+ * Replace explicitly identified structural modal children with the shared padded frame. Callers
33
+ * identify semantic rules/components; rendered text is never inspected or rewritten.
34
+ */
35
+ export declare function adoptPiModalFrame(container: Container, adoption: PiModalFrameAdoption): PiModalFrame;
36
+ /**
37
+ * Adopt a public component's semantic rule/title children at the bare-A1 boundary. Only an optional
38
+ * structural Spacer between those explicitly identified children is accepted; rendered text is
39
+ * never inspected or rewritten. The pinned comparison profile does not call this adapter.
40
+ */
41
+ export declare function adoptPiModalHeader(container: Container, ruleIndex: number, titleIndex: number): PiModalHeader;
@@ -0,0 +1,110 @@
1
+ import { Container, Spacer } from "@earendil-works/pi-tui";
2
+ export const PI_MODAL_CONTENT_PADDING_X = 1;
3
+ /** The semantic top rule/title pair used while a modal assembles its frame. */
4
+ export class PiModalHeader {
5
+ rule;
6
+ title;
7
+ constructor(rule, title) {
8
+ this.rule = rule;
9
+ this.title = title;
10
+ }
11
+ invalidate() {
12
+ this.rule.invalidate();
13
+ this.title.invalidate();
14
+ }
15
+ render(width) {
16
+ return [...this.rule.render(width), ...this.title.render(width)];
17
+ }
18
+ }
19
+ /**
20
+ * Complete framed-modal chrome. Rules remain frame-wide; every semantic content component renders
21
+ * in the same one-cell-smaller rectangle and receives the same left inset.
22
+ */
23
+ export class PiModalFrame {
24
+ #topRule;
25
+ #content;
26
+ #bottomRule;
27
+ #fullWidthContent;
28
+ #preInsetContent;
29
+ constructor(topRule, content, bottomRule, fullWidthContent = [], preInsetContent = []) {
30
+ this.#topRule = topRule;
31
+ this.#content = content;
32
+ this.#bottomRule = bottomRule;
33
+ this.#fullWidthContent = new Set(fullWidthContent);
34
+ this.#preInsetContent = new Set(preInsetContent);
35
+ }
36
+ invalidate() {
37
+ this.#topRule.invalidate();
38
+ for (const component of this.#content)
39
+ component.invalidate();
40
+ this.#bottomRule.invalidate();
41
+ }
42
+ render(width) {
43
+ const padding = Math.min(PI_MODAL_CONTENT_PADDING_X, Math.max(0, width));
44
+ const contentWidth = Math.max(0, width - padding);
45
+ const rows = [...this.#topRule.render(width)];
46
+ for (const component of this.#content) {
47
+ if (this.#fullWidthContent.has(component) || this.#preInsetContent.has(component)) {
48
+ rows.push(...component.render(width));
49
+ }
50
+ else if (component instanceof Spacer) {
51
+ rows.push(...component.render(contentWidth));
52
+ }
53
+ else {
54
+ rows.push(...component.render(contentWidth).map(row => `${" ".repeat(padding)}${row}`));
55
+ }
56
+ }
57
+ rows.push(...this.#bottomRule.render(width));
58
+ return rows;
59
+ }
60
+ }
61
+ /** Add compact titled-modal chrome while a component assembles its semantic body. */
62
+ export function addPiModalHeader(container, rule, title) {
63
+ const header = new PiModalHeader(rule, title);
64
+ container.addChild(header);
65
+ return header;
66
+ }
67
+ /**
68
+ * Replace explicitly identified structural modal children with the shared padded frame. Callers
69
+ * identify semantic rules/components; rendered text is never inspected or rewritten.
70
+ */
71
+ export function adoptPiModalFrame(container, adoption) {
72
+ const { topIndex, bottomIndex, header, fullWidthContent = [], preInsetContent = [] } = adoption;
73
+ if (topIndex < 0 || bottomIndex <= topIndex || bottomIndex >= container.children.length) {
74
+ throw new TypeError("invalid modal frame child positions");
75
+ }
76
+ const top = container.children[topIndex];
77
+ const bottom = container.children[bottomIndex];
78
+ const contentStart = topIndex + 1;
79
+ let topRule = top;
80
+ const content = [];
81
+ if (header !== undefined) {
82
+ if (top !== header)
83
+ throw new TypeError("modal header is not at the declared top position");
84
+ topRule = header.rule;
85
+ content.push(header.title);
86
+ }
87
+ content.push(...container.children.slice(contentStart, bottomIndex));
88
+ const frame = new PiModalFrame(topRule, content, bottom, fullWidthContent, preInsetContent);
89
+ container.children.splice(topIndex, bottomIndex - topIndex + 1, frame);
90
+ return frame;
91
+ }
92
+ /**
93
+ * Adopt a public component's semantic rule/title children at the bare-A1 boundary. Only an optional
94
+ * structural Spacer between those explicitly identified children is accepted; rendered text is
95
+ * never inspected or rewritten. The pinned comparison profile does not call this adapter.
96
+ */
97
+ export function adoptPiModalHeader(container, ruleIndex, titleIndex) {
98
+ if (ruleIndex < 0 || titleIndex <= ruleIndex || titleIndex >= container.children.length) {
99
+ throw new TypeError("invalid modal rule/title child positions");
100
+ }
101
+ const between = container.children.slice(ruleIndex + 1, titleIndex);
102
+ if (between.length > 1 || between.some(child => !(child instanceof Spacer))) {
103
+ throw new TypeError("modal rule and title are not separated by an optional single Spacer");
104
+ }
105
+ const rule = container.children[ruleIndex];
106
+ const title = container.children[titleIndex];
107
+ const header = new PiModalHeader(rule, title);
108
+ container.children.splice(ruleIndex, titleIndex - ruleIndex + 1, header);
109
+ return header;
110
+ }