@timurproko/a1 0.2.7-dev.1275 → 0.2.7-dev.1276

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 (60) hide show
  1. package/dist/app/session-shell/apps-dialog.d.ts +1 -1
  2. package/dist/app/session-shell/apps-dialog.js +2 -1
  3. package/dist/app/session-shell/dialog-coordinator.d.ts +4 -5
  4. package/dist/app/session-shell/dialog-coordinator.js +41 -12
  5. package/dist/app/session-shell/input-route.d.ts +23 -0
  6. package/dist/app/session-shell/input-route.js +59 -0
  7. package/dist/app/session-shell/session-selectors.d.ts +3 -3
  8. package/dist/app/session-shell/session-selectors.js +7 -2
  9. package/dist/app/session-shell/session-shell-root.js +6 -2
  10. package/dist/app/session-shell/session-shell.js +4 -2
  11. package/dist/app/session-shell/viewport-pointer.js +5 -1
  12. package/dist/app/session-shell/workflow-router.d.ts +1 -1
  13. package/dist/composition/settings-route-host.js +1 -0
  14. package/dist/contracts/owned-ui/shell-components.d.ts +10 -0
  15. package/dist/contracts/presentation/component.d.ts +7 -0
  16. package/dist/contracts/presentation/index.d.ts +2 -0
  17. package/dist/contracts/presentation/index.js +4 -0
  18. package/dist/contracts/presentation/progress-palette.generated.d.ts +24 -0
  19. package/dist/contracts/presentation/progress-palette.generated.js +1 -134
  20. package/dist/features/owned-ui/project-trust-dialog.d.ts +1 -1
  21. package/dist/features/owned-ui/project-trust-dialog.js +5 -5
  22. package/dist/features/owned-ui/project-trust-prompt.js +2 -2
  23. package/dist/features/owned-ui/reference-screen-app.d.ts +2 -0
  24. package/dist/features/owned-ui/reference-screen-app.js +4 -1
  25. package/dist/features/owned-ui/settings-app.js +6 -6
  26. package/dist/features/owned-ui/settings-model-dialog.js +3 -3
  27. package/dist/integrations/pi/components/deferred-selector.d.ts +6 -0
  28. package/dist/integrations/pi/components/deferred-selector.js +40 -0
  29. package/dist/integrations/pi/components/login-dialog-loader.d.ts +3 -0
  30. package/dist/integrations/pi/components/login-dialog-loader.js +57 -0
  31. package/dist/integrations/pi/components/login-links.d.ts +6 -0
  32. package/dist/integrations/pi/components/login-links.js +31 -0
  33. package/dist/integrations/pi/components/login-text.d.ts +7 -0
  34. package/dist/integrations/pi/components/login-text.js +17 -0
  35. package/dist/integrations/pi/components/modal-frame.d.ts +5 -0
  36. package/dist/integrations/pi/components/modal-frame.js +30 -2
  37. package/dist/integrations/pi/components/shell-selectors-dialogs.d.ts +11 -8
  38. package/dist/integrations/pi/components/shell-selectors-dialogs.js +36 -35
  39. package/dist/integrations/pi/components/shell-session-info-reference.d.ts +1 -1
  40. package/dist/integrations/pi/components/shell-session-info-reference.js +49 -46
  41. package/dist/integrations/pi/components/theme.d.ts +2 -0
  42. package/dist/integrations/pi/components/theme.js +4 -0
  43. package/dist/integrations/pi/components/upstream/components/auth-url.d.ts +16 -0
  44. package/dist/integrations/pi/components/upstream/components/auth-url.js +49 -0
  45. package/dist/integrations/pi/components/upstream/components/extension-selector.d.ts +1 -0
  46. package/dist/integrations/pi/components/upstream/components/extension-selector.js +21 -10
  47. package/dist/integrations/pi/components/upstream/components/login-dialog.d.ts +3 -0
  48. package/dist/integrations/pi/components/upstream/components/login-dialog.js +216 -0
  49. package/dist/integrations/pi/components/upstream/components/oauth-selector.d.ts +19 -0
  50. package/dist/integrations/pi/components/upstream/components/oauth-selector.js +123 -0
  51. package/dist/integrations/pi/components/upstream/components/session-footer.js +10 -7
  52. package/dist/integrations/pi/components/upstream/components/trust-selector.d.ts +2 -0
  53. package/dist/integrations/pi/components/upstream/components/trust-selector.js +29 -12
  54. package/dist/integrations/pi/session-presenters/components.d.ts +1 -1
  55. package/dist/integrations/pi/session-presenters/components.js +8 -8
  56. package/dist/integrations/pi/session-presenters/presenters.js +2 -2
  57. package/dist/ui/components/list-block.d.ts +2 -2
  58. package/dist/ui/components/list-block.js +4 -3
  59. package/dist/ui/settings/early-accent.d.ts +24 -0
  60. package/package.json +3 -3
@@ -3,13 +3,14 @@ import { EarendilAnnouncementComponent } from "./upstream/components/earendil-an
3
3
  import { ExtensionSelectorComponent } from "./upstream/components/extension-selector.js";
4
4
  import { Box, Container, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
5
5
  import { ScopedModelsSelectorComponent } from "./upstream/components/scoped-models-selector.js";
6
- import { addPiModalSelectFooter, adoptPiModalFrame, adoptPiModalHeader, adoptPiModalSearchInput } from "./modal-frame.js";
6
+ import { addPiModalSelectFooter, adoptPiModalFrame, adoptPiModalHeader, adoptPiModalSearchInput, renderPiModalSelectedPrimary, standardInternalChoicePort } from "./modal-frame.js";
7
+ import { deferredLoginDialog } from "./login-dialog-loader.js";
8
+ import { deferredSelector } from "./deferred-selector.js";
7
9
  import { createDeferredModelsDialog } from "./models-dialog-loader.js";
8
- import { TrustSelectorComponent } from "./upstream/components/trust-selector.js";
9
- import { canonicalizePiSettingsHint, DIALOG_CLOSE_SHORTCUT_HINT, PINNED_PI_LAYOUT, getPiSelectListTheme, paintPiBorder, piTheme, renderPiModalShortcutHints, } from "./theme.js";
10
+ import { canonicalizePiSettingsHint, DIALOG_CLOSE_SHORTCUT_HINT, PINNED_PI_LAYOUT, getPiSelectListTheme, paintPiBorder, piTheme, renderPiModalShortcutHints, internalDialogTitle, } from "./theme.js";
10
11
  import { componentFromPort, componentPort, createTuiFacade, ensureTheme, isRecord, } from "./shell-shared-facade.js";
11
- export { createPiShellSkillsSelector } from "./skills-dialog.js";
12
- export function createPiShellSelector(options) {
12
+ export { createPiShellSkillsSelector } from "./deferred-selector.js";
13
+ export function createPiShellSelector(options, standard = false) {
13
14
  ensureTheme();
14
15
  const items = options.options.map(toSelectItem);
15
16
  const list = new SelectList(items, options.maxVisible ?? Math.min(PINNED_PI_LAYOUT.selectorMaxVisible, Math.max(1, items.length)), getPiSelectListTheme());
@@ -17,11 +18,11 @@ export function createPiShellSelector(options) {
17
18
  list.onSelect = item => options.onSelect?.(item.value);
18
19
  if (options.onCancel !== undefined)
19
20
  list.onCancel = options.onCancel;
20
- if (!options.title && options.onCancel === undefined)
21
+ if (!options.title && options.onCancel === undefined && !standard)
21
22
  return componentPort(list);
22
23
  const container = new Container();
23
24
  if (options.title)
24
- container.addChild(new Text(piTheme().fg("accent", piTheme().bold(options.title)), PINNED_PI_LAYOUT.contentPaddingX, 0));
25
+ container.addChild(new Text(piTheme().fg("accent", piTheme().bold(standard ? internalDialogTitle(options.title) : options.title)), PINNED_PI_LAYOUT.contentPaddingX, 0));
25
26
  container.addChild(list);
26
27
  if (options.onCancel !== undefined) {
27
28
  container.addChild(new Spacer(1));
@@ -31,7 +32,8 @@ export function createPiShellSelector(options) {
31
32
  DIALOG_CLOSE_SHORTCUT_HINT,
32
33
  ]), 0, 0));
33
34
  }
34
- return componentPort(container, data => list.handleInput(data));
35
+ const port = componentPort(container, data => list.handleInput(data));
36
+ return standard ? { ...port, render: width => port.render(width).map(row => renderPiModalSelectedPrimary(` ${row}`, width + 1, options.options.map(option => option.label)).slice(1)) } : port;
35
37
  }
36
38
  export function createPiShellSettingsSelector(options) {
37
39
  ensureTheme();
@@ -98,7 +100,8 @@ export function createPiShellModelSelector(options) {
98
100
  const selector = new ModelSelectorComponent(createTuiFacade(options.runtime), options.currentModel, options.modelRuntime, options.scopedModels, options.onSelect, options.onCancel, options.initialSearchInput, options.onSelectAsDefault, options.defaultModel);
99
101
  adoptPiModalSearchInput(selector, selector.getSearchInput());
100
102
  adoptPiModalFrame(selector, { topIndex: 0, bottomIndex: selector.children.length - 1 });
101
- return componentPort(selector);
103
+ const port = componentPort(selector);
104
+ return options.standardStyle ? standardInternalChoicePort(port, true) : port;
102
105
  }
103
106
  function toPiScopedModel(model) {
104
107
  const candidate = { provider: model.provider, id: model.id, name: model.name };
@@ -137,7 +140,8 @@ export function createPiShellModelsDialog(options) {
137
140
  }
138
141
  export function createPiShellTrustSelector(options) {
139
142
  ensureTheme();
140
- return componentPort(new TrustSelectorComponent(options));
143
+ return deferredSelector(() => import("./upstream/components/trust-selector.js")
144
+ .then(({ TrustSelectorComponent }) => componentPort(new TrustSelectorComponent(options))), options.onCancel);
141
145
  }
142
146
  export async function createPiShellSessionSelector(options) {
143
147
  ensureTheme();
@@ -153,21 +157,26 @@ export async function createPiShellTreeSelector(options) {
153
157
  selector.onCopy = options.onCopy;
154
158
  return componentPort(selector);
155
159
  }
156
- export function createPiShellUserMessageSelector(messages, onSelect, onCancel, initialSelectedId) {
160
+ export function createPiShellUserMessageSelector(messages, onSelect, onCancel, initialSelectedId, standard = false) {
157
161
  ensureTheme();
158
162
  const selector = new UserMessageSelectorComponent(messages.map(message => ({ id: message.id, text: message.label })), onSelect, onCancel, initialSelectedId);
159
163
  const list = selector.getMessageList();
164
+ if (standard)
165
+ selector.children[1] = new Text(piTheme().fg("accent", piTheme().bold("Fork from Message")), 1, 0);
160
166
  addPiModalSelectFooter(selector);
161
167
  adoptPiModalFrame(selector, { topIndex: 4, bottomIndex: selector.children.length - 1 });
162
- return componentPort(selector, data => list.handleInput(data));
168
+ const port = componentPort(selector, data => list.handleInput(data));
169
+ return standard ? standardInternalChoicePort(port) : port;
163
170
  }
164
- export function createPiShellLoginDialog(runtime, providerId, onComplete, providerName, title) {
171
+ export function createPiShellLoginDialog(runtime, providerId, onComplete, providerName, title, standard = false) {
165
172
  ensureTheme();
173
+ if (standard)
174
+ return deferredLoginDialog(runtime, providerId, onComplete, providerName, title);
166
175
  const dialog = new LoginDialogComponent(createTuiFacade(runtime), providerId, onComplete, providerName, title);
167
176
  const preInsetTitle = dialog.children[1];
168
177
  const content = dialog.children[2];
169
178
  dialog.children.splice(dialog.children.length - 1, 0, new Spacer(1), new Text(renderPiModalShortcutHints([DIALOG_CLOSE_SHORTCUT_HINT]), 0, 0));
170
- const removeDependencyHint = () => { content.children.pop(); };
179
+ const stripHint = (result) => { content.children.pop(); return result; };
171
180
  const header = adoptPiModalHeader(dialog, 0, 1);
172
181
  adoptPiModalFrame(dialog, {
173
182
  topIndex: 0,
@@ -179,23 +188,12 @@ export function createPiShellLoginDialog(runtime, providerId, onComplete, provid
179
188
  ...componentPort(dialog),
180
189
  showAuth: (url, instructions) => dialog.showAuth(url, instructions),
181
190
  showDeviceCode: info => dialog.showDeviceCode(info),
182
- showManualInput(message) {
183
- const result = dialog.showManualInput(message);
184
- removeDependencyHint();
185
- return result;
186
- },
187
- showPrompt(message, placeholder) {
188
- const result = dialog.showPrompt(message, placeholder);
189
- removeDependencyHint();
190
- return result;
191
- },
191
+ showManualInput: message => stripHint(dialog.showManualInput(message)),
192
+ showPrompt: (message, placeholder) => stripHint(dialog.showPrompt(message, placeholder)),
192
193
  showDetails: lines => dialog.showDetails([...lines]),
193
- // Rationale: The persistent canonical footer replaces the dependency's optional close hint.
194
+ // Rationale: one footer replaces optional hints.
194
195
  showInfo: (message, links = []) => dialog.showInfo(message, [...links], false),
195
- showWaiting(message) {
196
- dialog.showWaiting(message);
197
- removeDependencyHint();
198
- },
196
+ showWaiting: message => stripHint(dialog.showWaiting(message)),
199
197
  showProgress: message => dialog.showProgress(message),
200
198
  };
201
199
  }
@@ -218,8 +216,11 @@ export function createPiShellReloadBox() {
218
216
  adoptPiModalFrame(container, { topIndex: 0, bottomIndex: container.children.length - 1 });
219
217
  return componentPort(container);
220
218
  }
221
- export function createPiShellAuthProviderSelector(mode, providers, onSelect, onCancel, initialSearchInput) {
219
+ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onCancel, initialSearchInput, standardStyle = false) {
222
220
  ensureTheme();
221
+ if (standardStyle)
222
+ return deferredSelector(() => import("./upstream/components/oauth-selector.js")
223
+ .then(({ AuthProviderSelector }) => componentPort(new AuthProviderSelector(mode, providers, onSelect, onCancel, initialSearchInput))), onCancel);
223
224
  const selector = new OAuthSelectorComponent(mode, providers.map(provider => ({
224
225
  id: provider.providerId,
225
226
  name: provider.label,
@@ -230,7 +231,7 @@ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onC
230
231
  if (selected)
231
232
  onSelect(selected.id);
232
233
  }, onCancel, initialSearchInput);
233
- // Provenance: OAuth selector slot is top rule, spacer, title, spacer, then its search Input.
234
+ // Provenance: public search slot 4.
234
235
  adoptPiModalSearchInput(selector, selector.children[4]);
235
236
  addPiModalSelectFooter(selector);
236
237
  const preInsetContent = [selector.children[2], selector.children[6]];
@@ -243,9 +244,9 @@ export function createPiShellAuthProviderSelector(mode, providers, onSelect, onC
243
244
  });
244
245
  return componentPort(selector);
245
246
  }
246
- export function createPiShellExtensionSelector(title, options, onSelect, onCancel) {
247
+ export function createPiShellExtensionSelector(title, options, onSelect, onCancel, standardSelection = false) {
247
248
  ensureTheme();
248
- return componentPort(new ExtensionSelectorComponent(title, [...options], onSelect, onCancel));
249
+ return componentPort(new ExtensionSelectorComponent(standardSelection ? internalDialogTitle(title) : title, [...options], onSelect, onCancel, { standardSelection }));
249
250
  }
250
251
  export function createPiShellThemeSelector(currentTheme, onSelect, onCancel, onPreview) {
251
252
  ensureTheme();
@@ -263,7 +264,7 @@ export function createPiShellShowImagesSelector(currentValue, onSelect, onCancel
263
264
  adoptPiModalFrame(selector, { topIndex: 0, bottomIndex: selector.children.length - 1 });
264
265
  return componentPort(selector, data => list.handleInput(data));
265
266
  }
266
- export function createPiShellDialog(dialog, handlers = {}) {
267
+ export function createPiShellDialog(dialog, handlers = {}, standard = false) {
267
268
  ensureTheme();
268
269
  const selector = createPiShellSelector({
269
270
  title: dialog.title,
@@ -271,7 +272,7 @@ export function createPiShellDialog(dialog, handlers = {}) {
271
272
  maxVisible: 8,
272
273
  ...(handlers.onSelect === undefined ? {} : { onSelect: handlers.onSelect }),
273
274
  ...(handlers.onCancel === undefined ? {} : { onCancel: handlers.onCancel }),
274
- });
275
+ }, standard);
275
276
  const box = new Box(1, 1);
276
277
  box.addChild(componentFromPort(selector));
277
278
  return componentPort(box, data => selector.handleInput?.(data));
@@ -6,6 +6,6 @@ interface PiShellSessionInfoReferenceDocument {
6
6
  readonly rows: readonly string[];
7
7
  }[];
8
8
  }
9
- /** Renders the pinned report values while leaving group names semantic for the owned screen. */
9
+ /** Renders complete report data in semantic rows sharing a left-aligned value column. */
10
10
  export declare function renderPiShellSessionInfoReferenceDocument(presentation: PiShellSessionInfoPresentation, width: number): PiShellSessionInfoReferenceDocument;
11
11
  export {};
@@ -14,7 +14,7 @@ function splitSessionFile(sessionFile, firstWidth, continuationWidth) {
14
14
  rowWidth = continuationWidth;
15
15
  continue;
16
16
  }
17
- // Compatibility: a one-column frame cannot fit a wide grapheme; preserve it for outer truncation.
17
+ // Compatibility: preserve a wide grapheme even when the outer frame cannot fit it.
18
18
  if (!row)
19
19
  row = sliceByColumn(remaining, 0, rowWidth);
20
20
  rows.push(row);
@@ -23,72 +23,75 @@ function splitSessionFile(sessionFile, firstWidth, continuationWidth) {
23
23
  }
24
24
  return rows.length > 0 ? rows : [""];
25
25
  }
26
- /** Keeps the file value on its labelled row before using full continuation rows. */
27
- function sessionFileRows(sessionFile, width) {
28
- // Compatibility: match Text's reduced-padding rule so these explicit breaks survive its renderer.
29
- const paddingX = Math.min(1, Math.max(0, Math.floor((width - 1) / 2)));
30
- const contentWidth = Math.max(1, width - paddingX * 2);
31
- const label = piTheme().fg("dim", "File:");
32
- const firstValueWidth = Math.max(0, contentWidth - visibleWidth("File: "));
33
- const [first = "", ...continuation] = splitSessionFile(sessionFile, firstValueWidth, contentWidth);
34
- return firstValueWidth > 0
35
- ? [`${label} ${first}`, ...continuation]
36
- : [label, ...continuation];
37
- }
38
- /** Renders the pinned report values while leaving group names semantic for the owned screen. */
26
+ /** Renders complete report data in semantic rows sharing a left-aligned value column. */
39
27
  export function renderPiShellSessionInfoReferenceDocument(presentation, width) {
40
28
  ensureTheme();
41
29
  const { stats, sessionName, cacheWaste, usageBreakdown, cacheWarming } = presentation;
42
- const render = (rows) => new Text(rows.join("\n"), 1, 0).render(width);
43
- const preamble = [
44
- ...(sessionName ? [`${piTheme().fg("dim", "Name:")} ${sessionName}`] : []),
45
- ...sessionFileRows(stats.sessionFile ?? "In-memory", width),
46
- `${piTheme().fg("dim", "ID:")} ${stats.sessionId}`,
30
+ const identity = [
31
+ ...(sessionName ? [{ label: "Name:", value: sessionName }] : []),
32
+ { label: "File:", value: stats.sessionFile ?? "In-memory" },
33
+ { label: "ID:", value: stats.sessionId },
47
34
  ];
48
35
  const messages = [
49
- `${piTheme().fg("dim", "Total:")} ${stats.totalMessages}`,
50
- `${piTheme().fg("dim", "User:")} ${stats.userMessages}`,
51
- `${piTheme().fg("dim", "Assistant:")} ${stats.assistantMessages}`,
52
- `${piTheme().fg("dim", "Tools:")} ${stats.toolCalls} calls, ${stats.toolResults} results`,
36
+ { label: "Total:", value: String(stats.totalMessages) },
37
+ { label: "User:", value: String(stats.userMessages) },
38
+ { label: "Assistant:", value: String(stats.assistantMessages) },
39
+ { label: "Tools:", value: `${stats.toolCalls} calls, ${stats.toolResults} results` },
53
40
  ];
54
41
  const { input, cacheRead, cacheWrite } = stats.tokens;
55
42
  const promptTokens = input + cacheRead + cacheWrite;
56
- const tokens = [`${piTheme().fg("dim", "Input:")} ${promptTokens.toLocaleString()}`];
43
+ const tokens = [{ label: "Input:", value: promptTokens.toLocaleString() }];
57
44
  if (promptTokens > 0 && (cacheRead > 0 || cacheWrite > 0)) {
58
- tokens.push(` ${piTheme().fg("dim", "Cached:")} ${cacheRead.toLocaleString()} ${piTheme().fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`)}`);
45
+ tokens.push({ label: "Cached:", indent: 2, value: `${cacheRead.toLocaleString()} ${piTheme().fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`)}` });
59
46
  const written = cacheWrite > 0 ? ` ${piTheme().fg("dim", `(${cacheWrite.toLocaleString()} written to cache)`)}` : "";
60
- tokens.push(` ${piTheme().fg("dim", "Uncached:")} ${(input + cacheWrite).toLocaleString()}${written}`);
47
+ tokens.push({ label: "Uncached:", indent: 2, value: `${(input + cacheWrite).toLocaleString()}${written}` });
61
48
  }
62
- tokens.push(`${piTheme().fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}`);
63
- tokens.push(`${piTheme().fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}`);
49
+ tokens.push({ label: "Output:", value: stats.tokens.output.toLocaleString() });
50
+ tokens.push({ label: "Total:", value: stats.tokens.total.toLocaleString() });
64
51
  const warmingStatus = cacheWarming.status;
65
52
  const warming = [
66
- `${piTheme().fg("dim", "Mode:")} ${cacheWarming.mode}`,
67
- `${piTheme().fg("dim", "Status:")} ${warmingStatus ? formatCacheWarmingStatus(warmingStatus) : "Inactive (cache warming unavailable)"}`,
53
+ { label: "Mode:", value: cacheWarming.mode },
54
+ { label: "Status:", value: warmingStatus ? formatCacheWarmingStatus(warmingStatus) : "Inactive (cache warming unavailable)" },
68
55
  ];
69
- const warmingDecision = warmingStatus?.decision;
70
- if (warmingDecision?.economicsAvailable) {
71
- warming.push(`${piTheme().fg("dim", "Cache miss penalty:")} $${warmingDecision.missCost.toFixed(3)}`);
72
- warming.push(`${piTheme().fg("dim", "Refresh cost:")} $${warmingDecision.warmCost.toFixed(3)}`);
56
+ const decision = warmingStatus?.decision;
57
+ if (decision?.economicsAvailable) {
58
+ warming.push({ label: "Cache miss penalty:", value: `$${decision.missCost.toFixed(3)}` });
59
+ warming.push({ label: "Refresh cost:", value: `$${decision.warmCost.toFixed(3)}` });
73
60
  }
74
- const sections = [
75
- { title: "Messages", rows: render(messages) },
76
- { title: "Tokens", rows: render(tokens) },
77
- { title: "Cache Warming", rows: render(warming) },
78
- ];
61
+ const groups = [{ title: "Messages", rows: messages }, { title: "Tokens", rows: tokens }, { title: "Cache Warming", rows: warming }];
79
62
  if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
80
- const cost = [`${piTheme().fg("dim", "Total:")} $${stats.cost.toFixed(3)}`];
63
+ const cost = [{ label: "Total:", value: `$${stats.cost.toFixed(3)}` }];
81
64
  if (usageBreakdown.length > 1)
82
65
  for (const entry of usageBreakdown) {
83
- cost.push(` ${piTheme().fg("dim", `${entry.key}:`)} $${entry.cost.toFixed(3)} ${piTheme().fg("dim", `(${formatSessionTokens(entry.tokens)} tokens)`)}`);
66
+ cost.push({ label: `${entry.key}:`, indent: 2, value: `$${entry.cost.toFixed(3)} ${piTheme().fg("dim", `(${formatSessionTokens(entry.tokens)} tokens)`)}` });
84
67
  }
85
68
  if (cacheWaste.missedTokens > 0) {
86
69
  const detail = `${cacheWaste.missedTokens.toLocaleString()} tokens, ${cacheWaste.missCount === 1 ? "1 miss" : `${cacheWaste.missCount} misses`}`;
87
- cost.push(cacheWaste.missedCost >= 0.0001
88
- ? `${piTheme().fg("dim", "Cache Re-billed:")} $${cacheWaste.missedCost.toFixed(3)} ${piTheme().fg("dim", `(${detail})`)}`
89
- : `${piTheme().fg("dim", "Cache Re-billed:")} ${detail}`);
70
+ cost.push({ label: "Cache Re-billed:", value: cacheWaste.missedCost >= 0.0001
71
+ ? `$${cacheWaste.missedCost.toFixed(3)} ${piTheme().fg("dim", `(${detail})`)}` : detail });
72
+ }
73
+ groups.push({ title: "Cost", rows: cost });
74
+ }
75
+ // Compatibility: match Text's reduced-padding rule so explicit path breaks survive rendering.
76
+ const padding = Math.min(1, Math.max(0, Math.floor((width - 1) / 2)));
77
+ const contentWidth = Math.max(1, width - padding * 2);
78
+ const allRows = [...identity, ...groups.flatMap(group => group.rows)];
79
+ const column = Math.min(Math.max(...allRows.map(row => (row.indent ?? 0) + visibleWidth(row.label))) + 1, contentWidth - 1);
80
+ const prefix = (row) => `${" ".repeat(row.indent ?? 0)}${piTheme().fg("dim", row.label)}${" ".repeat(Math.max(1, column - (row.indent ?? 0) - visibleWidth(row.label)))}`;
81
+ const render = (rows) => new Text(rows.map(row => `${prefix(row)}${row.value}`).join("\n"), 1, 0).render(width);
82
+ const preamble = [];
83
+ for (const row of identity) {
84
+ if (row.label !== "File:") {
85
+ preamble.push(`${prefix(row)}${row.value}`);
86
+ continue;
90
87
  }
91
- sections.push({ title: "Cost", rows: render(cost) });
88
+ const label = prefix(row);
89
+ const firstWidth = Math.max(0, contentWidth - visibleWidth(label));
90
+ const [first = "", ...continuation] = splitSessionFile(row.value, firstWidth, contentWidth);
91
+ preamble.push(firstWidth > 0 ? `${label}${first}` : piTheme().fg("dim", row.label), ...continuation);
92
92
  }
93
- return { preamble: render(preamble), sections };
93
+ return {
94
+ preamble: new Text(preamble.join("\n"), 1, 0).render(width),
95
+ sections: groups.map(group => ({ title: group.title, rows: render(group.rows) })),
96
+ };
94
97
  }
@@ -20,6 +20,8 @@ export declare function getPiMarkdownTheme(): MarkdownTheme;
20
20
  export declare function getPiHotkeysMarkdownTheme(): MarkdownTheme;
21
21
  /** Select-list roles resolved through A1's active theme rather than Pi's package-global base theme. */
22
22
  export declare function getPiSelectListTheme(): SelectListTheme;
23
+ /** Display casing for internal headings, preserving provider spelling and acronyms. */
24
+ export declare function internalDialogTitle(title: string): string;
23
25
  /** Paint one selected bare-A1 list item with the standard blue selection surface. */
24
26
  export declare function renderPiListSelection(content: string): string;
25
27
  /** One standard bare-A1 modal row, clipped before its item-bounded selection is painted. */
@@ -53,6 +53,10 @@ export function getPiSelectListTheme() {
53
53
  noMatch: text => piTheme().fg("muted", text),
54
54
  };
55
55
  }
56
+ /** Display casing for internal headings, preserving provider spelling and acronyms. */
57
+ export function internalDialogTitle(title) {
58
+ return title.replace(/:\s*$/u, "").replace(/\b[a-z][a-z]*/gu, (word, offset) => offset > 0 && /^(a|an|and|for|in|of|on|or|the|to|with)$/u.test(word) ? word : word[0].toUpperCase() + word.slice(1));
59
+ }
56
60
  /** Paint one selected bare-A1 list item with the standard blue selection surface. */
57
61
  export function renderPiListSelection(content) {
58
62
  return piTheme().bg("selectedBg", content);
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
3
+ * packages/coding-agent/src/modes/interactive/components/auth-url.ts.
4
+ * Modifications: Adapt sign-in URL/copy feedback to live semantic roles, shared zero-inset login
5
+ * content and canonical shortcut hints. Preserve safe hyperlinks, copy results and lifetime guards;
6
+ * the comparison profile keeps the public component.
7
+ * Deviations: shared-login-template.
8
+ */
9
+ import { Container } from "@earendil-works/pi-tui";
10
+ /** A live-themed browser URL whose copy feedback stays local to the same login state. */
11
+ export declare class AuthUrlComponent extends Container {
12
+ #private;
13
+ constructor(url: string, requestRender: () => void);
14
+ copy(): Promise<void>;
15
+ dispose(): void;
16
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
3
+ * packages/coding-agent/src/modes/interactive/components/auth-url.ts.
4
+ * Modifications: Adapt sign-in URL/copy feedback to live semantic roles, shared zero-inset login
5
+ * content and canonical shortcut hints. Preserve safe hyperlinks, copy results and lifetime guards;
6
+ * the comparison profile keeps the public component.
7
+ * Deviations: shared-login-template.
8
+ */
9
+ import { Container, getKeybindings } from "@earendil-works/pi-tui";
10
+ import { copyToClipboard } from "../../../startup-public.js";
11
+ import { loginLink, loginWebUrl } from "../../login-links.js";
12
+ import { loginText } from "../../login-text.js";
13
+ import { renderPiModalShortcutHints } from "../../theme.js";
14
+ /** A live-themed browser URL whose copy feedback stays local to the same login state. */
15
+ export class AuthUrlComponent extends Container {
16
+ #url;
17
+ #render;
18
+ #feedback;
19
+ #failure = "";
20
+ #closed = false;
21
+ constructor(url, requestRender) {
22
+ super();
23
+ this.#url = loginWebUrl(url) ?? url;
24
+ this.#render = requestRender;
25
+ this.addChild(loginText(() => loginLink(this.#url), "accent"));
26
+ this.addChild({
27
+ render: width => {
28
+ if (this.#feedback !== undefined)
29
+ return loginText(this.#feedback === "copied" ? "Copied URL to clipboard" : this.#failure, this.#feedback === "copied" ? "success" : "error").render(width);
30
+ const click = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
31
+ return loginText(() => `${loginLink(this.#url, click)} ${renderPiModalShortcutHints([{ key: getKeybindings().getKeys("app.message.copy").join("/"), action: "copy" }])}`).render(width);
32
+ },
33
+ invalidate() { },
34
+ });
35
+ }
36
+ async copy() {
37
+ try {
38
+ await copyToClipboard(this.#url);
39
+ this.#feedback = "copied";
40
+ }
41
+ catch (error) {
42
+ this.#feedback = "failed";
43
+ this.#failure = error instanceof Error ? error.message : String(error);
44
+ }
45
+ if (!this.#closed)
46
+ this.#render();
47
+ }
48
+ dispose() { this.#closed = true; }
49
+ }
@@ -4,6 +4,7 @@ interface ExtensionSelectorOptions {
4
4
  readonly timeout?: number;
5
5
  readonly description?: string;
6
6
  readonly onToggleToolsExpanded?: () => void;
7
+ readonly standardSelection?: boolean;
7
8
  }
8
9
  /** Bare-A1 extension selector using the common modal shortcut row. */
9
10
  export declare class ExtensionSelectorComponent extends Container {
@@ -1,20 +1,21 @@
1
1
  /**
2
2
  * Provenance: @earendil-works/pi-coding-agent 1.5.1 (MIT), commit dd30101cfc5b0609411dd80b6f93adbc14db14cf,
3
3
  * packages/coding-agent/src/modes/interactive/components/extension-selector.ts.
4
- * Modifications: Mechanical port: remap public imports, use ECMAScript private fields, and route the
5
- * bare-A1 instruction row and chrome through the shared semantic shortcut and compact padded
6
- * modal-frame components while preserving options, timeout, navigation, selection, cancellation, and
7
- * disposal behavior.
4
+ * Modifications: Mechanical port with public imports, private fields, shared shortcut rows and compact
5
+ * framing; internal choices opt into normal labels over selected backgrounds and circular navigation
6
+ * while preserving extension/comparison styling, timeout, cancellation, and disposal.
8
7
  * Deviations: owned-modal-shortcut-hints.
9
8
  */
9
+ import { wrapChoiceIndex } from "../../../../../contracts/presentation/index.js";
10
10
  import { DynamicBorder } from "../../../startup-public.js";
11
11
  import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
12
12
  import { addPiModalHeader, adoptPiModalFrame } from "../../modal-frame.js";
13
- import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, piTheme, renderPiModalShortcutHints } from "../../theme.js";
13
+ import { DIALOG_CLOSE_SHORTCUT_HINT, paintPiBorder, piTheme, renderPiModalShortcutHints, renderPiModalListRow } from "../../theme.js";
14
14
  import { CountdownTimer } from "./countdown-timer.js";
15
15
  /** Bare-A1 extension selector using the common modal shortcut row. */
16
16
  export class ExtensionSelectorComponent extends Container {
17
17
  #options;
18
+ #standardSelection;
18
19
  #selectedIndex = 0;
19
20
  #listContainer = new Container();
20
21
  #onSelect;
@@ -26,6 +27,7 @@ export class ExtensionSelectorComponent extends Container {
26
27
  constructor(title, options, onSelect, onCancel, opts) {
27
28
  super();
28
29
  this.#options = options;
30
+ this.#standardSelection = opts?.standardSelection === true;
29
31
  this.#onSelect = onSelect;
30
32
  this.#onCancel = onCancel;
31
33
  this.#baseTitle = title;
@@ -65,9 +67,18 @@ export class ExtensionSelectorComponent extends Container {
65
67
  const endIndex = Math.min(startIndex + maxVisible, this.#options.length);
66
68
  for (let index = startIndex; index < endIndex; index += 1) {
67
69
  const option = this.#options[index];
68
- this.#listContainer.addChild(new Text(index === this.#selectedIndex
69
- ? theme.fg("accent", "→ ") + theme.fg("accent", option)
70
- : ` ${theme.fg("text", option)}`, 0, 0));
70
+ const selected = index === this.#selectedIndex;
71
+ const content = selected
72
+ ? theme.fg("accent", "→ ") + theme.fg(this.#standardSelection ? "text" : "accent", option)
73
+ : ` ${theme.fg("text", option)}`;
74
+ this.#listContainer.addChild(this.#standardSelection ? {
75
+ render: width => {
76
+ const current = piTheme();
77
+ const arrow = selected ? current.fg("accent", "→ ") : " ";
78
+ return [renderPiModalListRow(`${arrow}${current.fg("text", option)}`, width, selected)];
79
+ },
80
+ invalidate: () => { },
81
+ } : new Text(content, 0, 0));
71
82
  }
72
83
  if (startIndex > 0 || endIndex < this.#options.length) {
73
84
  this.#listContainer.addChild(new Text(theme.fg("muted", ` (${this.#selectedIndex + 1}/${this.#options.length})`), 0, 0));
@@ -78,11 +89,11 @@ export class ExtensionSelectorComponent extends Container {
78
89
  if (keys.matches(keyData, "app.tools.expand"))
79
90
  this.#onToggleToolsExpanded?.();
80
91
  else if (keys.matches(keyData, "tui.select.up") || keyData === "k") {
81
- this.#selectedIndex = Math.max(0, this.#selectedIndex - 1);
92
+ this.#selectedIndex = this.#standardSelection ? wrapChoiceIndex(this.#selectedIndex, -1, this.#options.length) : Math.max(0, this.#selectedIndex - 1);
82
93
  this.#updateList();
83
94
  }
84
95
  else if (keys.matches(keyData, "tui.select.down") || keyData === "j") {
85
- this.#selectedIndex = Math.min(this.#options.length - 1, this.#selectedIndex + 1);
96
+ this.#selectedIndex = this.#standardSelection ? wrapChoiceIndex(this.#selectedIndex, 1, this.#options.length) : Math.min(this.#options.length - 1, this.#selectedIndex + 1);
86
97
  this.#updateList();
87
98
  }
88
99
  else if (keys.matches(keyData, "tui.select.confirm") || keyData === "\n") {
@@ -0,0 +1,3 @@
1
+ import type { OwnedUiShellLoginDialogPort, OwnedUiShellRuntime } from "../../../../../contracts/owned-ui/index.js";
2
+ /** One owned login port for every provider, keeping all state methods on the shared template. */
3
+ export declare function createThemedLoginDialog(runtime: OwnedUiShellRuntime, providerId: string, complete: (success: boolean, message?: string) => void, providerName?: string, title?: string): OwnedUiShellLoginDialogPort;