pi-ui-extend 0.1.13 → 0.1.17

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 (111) hide show
  1. package/README.md +1 -1
  2. package/dist/app/app.d.ts +7 -0
  3. package/dist/app/app.js +102 -17
  4. package/dist/app/commands/command-controller.js +2 -0
  5. package/dist/app/commands/command-host.d.ts +5 -0
  6. package/dist/app/commands/command-model-actions.d.ts +2 -0
  7. package/dist/app/commands/command-model-actions.js +40 -4
  8. package/dist/app/commands/command-navigation-actions.d.ts +9 -0
  9. package/dist/app/commands/command-navigation-actions.js +62 -0
  10. package/dist/app/commands/command-registry.d.ts +2 -0
  11. package/dist/app/commands/command-registry.js +16 -0
  12. package/dist/app/constants.d.ts +0 -1
  13. package/dist/app/constants.js +0 -1
  14. package/dist/app/extensions/extension-ui-controller.d.ts +16 -5
  15. package/dist/app/extensions/extension-ui-controller.js +99 -61
  16. package/dist/app/icons.d.ts +1 -0
  17. package/dist/app/icons.js +2 -0
  18. package/dist/app/input/input-action-controller.d.ts +2 -0
  19. package/dist/app/input/input-action-controller.js +8 -1
  20. package/dist/app/logger.d.ts +25 -0
  21. package/dist/app/logger.js +90 -0
  22. package/dist/app/model/model-usage-status.js +30 -15
  23. package/dist/app/popup/menu-items-controller.d.ts +4 -0
  24. package/dist/app/popup/menu-items-controller.js +68 -6
  25. package/dist/app/popup/popup-action-controller.d.ts +2 -1
  26. package/dist/app/popup/popup-action-controller.js +7 -4
  27. package/dist/app/popup/popup-menu-controller.d.ts +36 -23
  28. package/dist/app/popup/popup-menu-controller.js +97 -326
  29. package/dist/app/rendering/conversation-entry-renderer.js +3 -3
  30. package/dist/app/rendering/conversation-viewport.d.ts +10 -2
  31. package/dist/app/rendering/conversation-viewport.js +157 -16
  32. package/dist/app/rendering/editor-panels.js +22 -9
  33. package/dist/app/rendering/popup-menu-renderer.d.ts +62 -0
  34. package/dist/app/rendering/popup-menu-renderer.js +405 -0
  35. package/dist/app/rendering/render-controller.js +30 -28
  36. package/dist/app/rendering/render-text.js +5 -2
  37. package/dist/app/rendering/status-line-renderer.d.ts +8 -1
  38. package/dist/app/rendering/status-line-renderer.js +217 -117
  39. package/dist/app/rendering/toast-controller.d.ts +12 -3
  40. package/dist/app/rendering/toast-controller.js +70 -12
  41. package/dist/app/runtime.d.ts +2 -1
  42. package/dist/app/runtime.js +20 -10
  43. package/dist/app/screen/mouse-controller.d.ts +2 -2
  44. package/dist/app/screen/mouse-controller.js +27 -48
  45. package/dist/app/screen/screen-styler.d.ts +1 -1
  46. package/dist/app/screen/screen-styler.js +9 -7
  47. package/dist/app/screen/scroll-controller.d.ts +12 -9
  48. package/dist/app/screen/scroll-controller.js +56 -45
  49. package/dist/app/screen/status-controller.js +2 -1
  50. package/dist/app/session/lazy-session-manager.d.ts +11 -0
  51. package/dist/app/session/lazy-session-manager.js +539 -0
  52. package/dist/app/session/pix-system-message.d.ts +16 -0
  53. package/dist/app/session/pix-system-message.js +64 -0
  54. package/dist/app/session/request-history.d.ts +4 -0
  55. package/dist/app/session/request-history.js +11 -0
  56. package/dist/app/session/session-event-controller.d.ts +11 -0
  57. package/dist/app/session/session-event-controller.js +58 -2
  58. package/dist/app/session/session-history.d.ts +18 -0
  59. package/dist/app/session/session-history.js +72 -3
  60. package/dist/app/session/session-lifecycle-controller.d.ts +6 -2
  61. package/dist/app/session/session-lifecycle-controller.js +7 -2
  62. package/dist/app/session/session-search.js +10 -0
  63. package/dist/app/session/tabs-controller.d.ts +17 -5
  64. package/dist/app/session/tabs-controller.js +308 -29
  65. package/dist/app/todo/todo-model.d.ts +4 -2
  66. package/dist/app/todo/todo-model.js +23 -13
  67. package/dist/app/types.d.ts +17 -6
  68. package/dist/app/workspace/workspace-actions-controller.d.ts +2 -0
  69. package/dist/app/workspace/workspace-actions-controller.js +12 -0
  70. package/dist/config.d.ts +6 -1
  71. package/dist/config.js +82 -25
  72. package/dist/default-pix-config.js +4 -0
  73. package/dist/fuzzy.d.ts +2 -0
  74. package/dist/fuzzy.js +27 -7
  75. package/dist/input-editor.d.ts +9 -0
  76. package/dist/input-editor.js +52 -0
  77. package/dist/schemas/pi-tools-suite-schema.d.ts +1 -0
  78. package/dist/schemas/pi-tools-suite-schema.js +1 -0
  79. package/dist/schemas/pix-schema.d.ts +3 -1
  80. package/dist/schemas/pix-schema.js +6 -4
  81. package/dist/terminal-width.d.ts +2 -0
  82. package/dist/terminal-width.js +64 -3
  83. package/dist/theme.js +6 -6
  84. package/dist/ui.d.ts +8 -0
  85. package/external/pi-tools-suite/README.md +3 -2
  86. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +52 -8
  87. package/external/pi-tools-suite/src/antigravity-auth/commands.ts +3 -41
  88. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +0 -2
  89. package/external/pi-tools-suite/src/antigravity-auth/index.ts +11 -18
  90. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +129 -61
  91. package/external/pi-tools-suite/src/antigravity-auth/status.ts +82 -3
  92. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +20 -7
  93. package/external/pi-tools-suite/src/antigravity-auth/types.ts +21 -0
  94. package/external/pi-tools-suite/src/config.ts +8 -0
  95. package/external/pi-tools-suite/src/dcp/index.ts +16 -1
  96. package/external/pi-tools-suite/src/dcp/state.ts +35 -0
  97. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +3 -0
  98. package/external/pi-tools-suite/src/todo/index.ts +123 -14
  99. package/external/pi-tools-suite/src/todo/state/persistence.ts +0 -1
  100. package/external/pi-tools-suite/src/todo/state/state-reducer.ts +26 -43
  101. package/external/pi-tools-suite/src/todo/todo.ts +12 -23
  102. package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +34 -16
  103. package/external/pi-tools-suite/src/todo/tool/types.ts +7 -28
  104. package/external/pi-tools-suite/src/todo/view/format.ts +2 -3
  105. package/external/pi-tools-suite/src/tool-descriptions.ts +6 -4
  106. package/external/pi-tools-suite/src/usage/index.ts +5 -2
  107. package/external/pi-tools-suite/src/usage/lib/google.ts +53 -40
  108. package/external/pi-tools-suite/src/usage/lib/types.ts +12 -2
  109. package/package.json +1 -1
  110. package/schemas/pi-tools-suite.json +4 -0
  111. package/schemas/pix.json +11 -2
@@ -1,5 +1,7 @@
1
+ import { resolveToolRule } from "../../config.js";
2
+ import { stringDisplayWidth } from "../../terminal-width.js";
1
3
  import { renderConversationEntry as renderConversationEntryLines } from "./conversation-entry-renderer.js";
2
- import { shortHash } from "./render-text.js";
4
+ import { horizontalPaddingLayout, shortHash } from "./render-text.js";
3
5
  export class ConversationViewport {
4
6
  host;
5
7
  blockCachesByWidth = new Map();
@@ -23,12 +25,24 @@ export class ConversationViewport {
23
25
  slice(width, start, count) {
24
26
  if (count <= 0)
25
27
  return [];
28
+ for (let attempt = 0; attempt < 4; attempt += 1) {
29
+ const layout = this.layoutForWidth(width);
30
+ const visible = this.sliceMeasured(layout, width, start, count);
31
+ if (!visible.changed)
32
+ return visible.lines;
33
+ }
26
34
  const layout = this.layoutForWidth(width);
35
+ return this.sliceMeasured(layout, width, start, count, { allowLayoutChanges: false }).lines;
36
+ }
37
+ sliceMeasured(layout, width, start, count, options = {}) {
38
+ const allowLayoutChanges = options.allowLayoutChanges !== false;
27
39
  const visible = [];
28
40
  const end = start + count;
29
41
  let entryIndex = this.entryIndexForOffset(layout.offsets, start);
30
42
  for (; entryIndex < layout.entries.length; entryIndex += 1) {
31
43
  const entry = layout.entries[entryIndex];
44
+ if (allowLayoutChanges && this.ensureEntryMeasured(layout, width, entryIndex))
45
+ return { lines: [], changed: true };
32
46
  const block = this.blockForEntry(entry, width);
33
47
  const blockLineCount = layout.lineCounts[entryIndex] ?? 0;
34
48
  if (blockLineCount === 0)
@@ -45,7 +59,7 @@ export class ConversationViewport {
45
59
  if (visible.length >= count)
46
60
  break;
47
61
  }
48
- return visible;
62
+ return { lines: visible, changed: false };
49
63
  }
50
64
  entries() {
51
65
  const queued = this.queuedEntries();
@@ -80,6 +94,8 @@ export class ConversationViewport {
80
94
  }
81
95
  entryBlockPositions(width) {
82
96
  const layout = this.layoutForWidth(width);
97
+ for (let index = 0; index < layout.entries.length; index += 1)
98
+ this.ensureEntryMeasured(layout, width, index);
83
99
  return layout.entries.map((entry, index) => ({
84
100
  entry,
85
101
  offset: layout.offsets[index] ?? 0,
@@ -87,6 +103,21 @@ export class ConversationViewport {
87
103
  block: this.blockForEntry(entry, width),
88
104
  }));
89
105
  }
106
+ measuredLineCountForEntries(width, entryIds) {
107
+ if (entryIds.length === 0)
108
+ return 0;
109
+ const layout = this.layoutForWidth(width);
110
+ const indexes = [...new Set(entryIds
111
+ .map((entryId) => layout.positions.get(entryId))
112
+ .filter((index) => index !== undefined))]
113
+ .sort((left, right) => left - right);
114
+ let lineCount = 0;
115
+ for (const index of indexes) {
116
+ this.ensureEntryMeasured(layout, width, index);
117
+ lineCount += layout.lineCounts[index] ?? 0;
118
+ }
119
+ return lineCount;
120
+ }
90
121
  queuedEntries() {
91
122
  const session = this.host.session;
92
123
  const entries = [];
@@ -130,7 +161,10 @@ export class ConversationViewport {
130
161
  const allThinkingExpanded = Boolean(this.host.allThinkingExpanded);
131
162
  let layout = this.layoutCachesByWidth.get(width);
132
163
  if (!layout || this.layoutStructureChanged(layout, entries, queuedSignature, superCompactTools, allThinkingExpanded)) {
133
- layout = this.buildLayout(entries, width, queuedSignature, superCompactTools, allThinkingExpanded);
164
+ const previousLayout = layout && layout.queuedSignature === queuedSignature && layout.superCompactTools === superCompactTools && layout.allThinkingExpanded === allThinkingExpanded
165
+ ? layout
166
+ : undefined;
167
+ layout = this.buildLayout(entries, width, queuedSignature, superCompactTools, allThinkingExpanded, previousLayout);
134
168
  this.layoutCachesByWidth.set(width, layout);
135
169
  }
136
170
  else {
@@ -141,22 +175,40 @@ export class ConversationViewport {
141
175
  }
142
176
  return layout;
143
177
  }
144
- buildLayout(entries, width, queuedSignature, superCompactTools, allThinkingExpanded) {
178
+ buildLayout(entries, width, queuedSignature, superCompactTools, allThinkingExpanded, previousLayout) {
145
179
  const entryIds = [];
146
180
  const lineCounts = [];
181
+ const measuredLineCounts = [];
147
182
  const offsets = [];
148
183
  const positions = new Map();
149
184
  let totalLineCount = 0;
185
+ const estimatedBlockLineCounts = entries.map((entry) => this.estimatedBlockLineCountForEntry(entry, width));
150
186
  for (const [index, entry] of entries.entries()) {
151
187
  entryIds.push(entry.id);
152
188
  positions.set(entry.id, index);
153
189
  offsets.push(totalLineCount);
154
- const lineCount = this.lineCountForEntry(entry, entries, index, width);
190
+ const previousLineCount = this.previousMeasuredLineCount(previousLayout, entries, index, entry);
191
+ const lineCount = previousLineCount ?? this.lineCountWithGap(entry, estimatedBlockLineCounts[index] ?? 0, this.nextEstimatedVisibleEntry(entries, estimatedBlockLineCounts, index));
155
192
  lineCounts.push(lineCount);
193
+ measuredLineCounts.push(previousLineCount !== undefined);
156
194
  totalLineCount += lineCount;
157
195
  }
158
196
  offsets.push(totalLineCount);
159
- return { entries, entryIds, lineCounts, offsets, positions, dirtyEntryIds: new Set(), totalLineCount, queuedSignature, superCompactTools, allThinkingExpanded };
197
+ return { entries, entryIds, lineCounts, measuredLineCounts, offsets, positions, dirtyEntryIds: new Set(), totalLineCount, queuedSignature, superCompactTools, allThinkingExpanded };
198
+ }
199
+ previousMeasuredLineCount(previousLayout, entries, index, entry) {
200
+ const previousIndex = previousLayout?.positions.get(entry.id);
201
+ if (previousLayout === undefined || previousIndex === undefined)
202
+ return undefined;
203
+ if (previousLayout.measuredLineCounts[previousIndex] !== true)
204
+ return undefined;
205
+ if (previousLayout.dirtyEntryIds.has(entry.id))
206
+ return undefined;
207
+ const previousNextEntryId = previousLayout.entryIds[previousIndex + 1];
208
+ const nextEntryId = entries[index + 1]?.id;
209
+ if (previousNextEntryId !== nextEntryId)
210
+ return undefined;
211
+ return previousLayout.lineCounts[previousIndex];
160
212
  }
161
213
  layoutStructureChanged(layout, entries, queuedSignature, superCompactTools, allThinkingExpanded) {
162
214
  if (layout.entries.length !== entries.length || layout.queuedSignature !== queuedSignature || layout.superCompactTools !== superCompactTools || layout.allThinkingExpanded !== allThinkingExpanded)
@@ -168,12 +220,17 @@ export class ConversationViewport {
168
220
  refreshDirtyLayoutEntries(layout, width) {
169
221
  if (layout.dirtyEntryIds.size === 0)
170
222
  return;
223
+ const indexes = new Set();
171
224
  for (const entryId of layout.dirtyEntryIds) {
172
225
  const position = layout.positions.get(entryId);
173
226
  if (position === undefined)
174
227
  continue;
175
- this.refreshLayoutEntry(layout, width, position);
228
+ indexes.add(position);
229
+ if (position > 0)
230
+ indexes.add(position - 1);
176
231
  }
232
+ for (const position of [...indexes].sort((left, right) => left - right))
233
+ this.refreshLayoutEntry(layout, width, position, true);
177
234
  layout.dirtyEntryIds.clear();
178
235
  }
179
236
  blockCacheForWidth(width) {
@@ -187,30 +244,80 @@ export class ConversationViewport {
187
244
  refreshDynamicLayoutEntries(layout, width) {
188
245
  for (let index = 0; index < layout.entries.length; index += 1) {
189
246
  if (this.host.isDynamicConversationBlock(layout.entries[index]))
190
- this.refreshLayoutEntry(layout, width, index);
247
+ this.refreshLayoutEntry(layout, width, index, true);
191
248
  }
192
249
  }
193
- refreshLayoutEntry(layout, width, index) {
250
+ ensureEntryMeasured(layout, width, index) {
194
251
  const entry = layout.entries[index];
195
252
  if (!entry)
196
- return;
253
+ return false;
254
+ if (layout.measuredLineCounts[index] === true && !this.host.isDynamicConversationBlock(entry))
255
+ return false;
256
+ return this.refreshLayoutEntry(layout, width, index, true);
257
+ }
258
+ refreshLayoutEntry(layout, width, index, measure) {
259
+ const entry = layout.entries[index];
260
+ if (!entry)
261
+ return false;
197
262
  const previousLineCount = layout.lineCounts[index] ?? 0;
198
- const nextLineCount = this.lineCountForEntry(entry, layout.entries, index, width);
263
+ const nextLineCount = measure
264
+ ? this.measuredLineCountForEntry(entry, layout.entries, index, width)
265
+ : this.estimatedLineCountForEntry(entry, layout.entries, index, width);
266
+ layout.measuredLineCounts[index] = measure;
199
267
  if (previousLineCount === nextLineCount)
200
- return;
268
+ return false;
201
269
  const delta = nextLineCount - previousLineCount;
202
270
  layout.lineCounts[index] = nextLineCount;
203
271
  layout.totalLineCount += delta;
204
272
  for (let offsetIndex = index + 1; offsetIndex < layout.offsets.length; offsetIndex += 1) {
205
273
  layout.offsets[offsetIndex] = (layout.offsets[offsetIndex] ?? 0) + delta;
206
274
  }
275
+ return true;
207
276
  }
208
- lineCountForEntry(entry, entries, index, width) {
277
+ measuredLineCountForEntry(entry, entries, index, width) {
209
278
  const block = this.blockForEntry(entry, width);
210
- if (block.lineCount === 0)
279
+ return this.lineCountWithGap(entry, block.lineCount, this.nextVisibleEntry(entries, index, width));
280
+ }
281
+ estimatedLineCountForEntry(entry, entries, index, width) {
282
+ const blockLineCount = this.estimatedBlockLineCountForEntry(entry, width);
283
+ const blockLineCounts = entries.map((candidate) => this.estimatedBlockLineCountForEntry(candidate, width));
284
+ return this.lineCountWithGap(entry, blockLineCount, this.nextEstimatedVisibleEntry(entries, blockLineCounts, index));
285
+ }
286
+ lineCountWithGap(entry, blockLineCount, nextEntry) {
287
+ if (blockLineCount === 0)
211
288
  return 0;
212
- const nextEntry = this.nextVisibleEntry(entries, index, width);
213
- return block.lineCount + (this.gapAfterEntry(entry, nextEntry) ? 1 : 0);
289
+ return blockLineCount + (this.gapAfterEntry(entry, nextEntry) ? 1 : 0);
290
+ }
291
+ estimatedBlockLineCountForEntry(entry, width) {
292
+ if (width <= 0)
293
+ return 0;
294
+ switch (entry.kind) {
295
+ case "assistant":
296
+ return estimateWrappedLineCount(entry.text, width);
297
+ case "system":
298
+ case "error":
299
+ case "custom":
300
+ case "session-aborted":
301
+ return estimateWrappedLineCount(entry.text, width);
302
+ case "user": {
303
+ const { contentWidth } = horizontalPaddingLayout(width);
304
+ return 2 + estimateWrappedLineCount(entry.text, contentWidth);
305
+ }
306
+ case "queued": {
307
+ const { contentWidth } = horizontalPaddingLayout(width);
308
+ return estimateWrappedLineCount(entry.text, contentWidth);
309
+ }
310
+ case "thinking": {
311
+ const expanded = entry.expanded || this.host.allThinkingExpanded === true;
312
+ return expanded ? 1 + estimateWrappedLineCount(entry.text, Math.max(1, width - 2)) : 1;
313
+ }
314
+ case "shell":
315
+ return estimateToolLikeLineCount("shell", entry.expanded, `${entry.output}\n${entry.status}`, width, this.host.pixConfig, this.host.superCompactTools === true, true);
316
+ case "tool":
317
+ return estimateToolLikeLineCount(entry.toolName, entry.expanded, entry.output, width, this.host.pixConfig, this.host.superCompactTools === true, false);
318
+ default:
319
+ return 1;
320
+ }
214
321
  }
215
322
  nextVisibleEntry(entries, index, width) {
216
323
  for (let nextIndex = index + 1; nextIndex < entries.length; nextIndex += 1) {
@@ -222,6 +329,16 @@ export class ConversationViewport {
222
329
  }
223
330
  return undefined;
224
331
  }
332
+ nextEstimatedVisibleEntry(entries, lineCounts, index) {
333
+ for (let nextIndex = index + 1; nextIndex < entries.length; nextIndex += 1) {
334
+ const nextEntry = entries[nextIndex];
335
+ if (!nextEntry)
336
+ continue;
337
+ if ((lineCounts[nextIndex] ?? 0) > 0)
338
+ return nextEntry;
339
+ }
340
+ return undefined;
341
+ }
225
342
  gapAfterEntry(entry, nextEntry) {
226
343
  if (!this.host.superCompactTools)
227
344
  return true;
@@ -249,3 +366,27 @@ export class ConversationViewport {
249
366
  return result;
250
367
  }
251
368
  }
369
+ function estimateWrappedLineCount(text, width) {
370
+ const safeWidth = Math.max(1, width);
371
+ if (!text)
372
+ return 0;
373
+ let count = 0;
374
+ for (const line of text.split("\n")) {
375
+ const displayWidth = stringDisplayWidth(line);
376
+ count += Math.max(1, Math.ceil(displayWidth / safeWidth));
377
+ }
378
+ return count;
379
+ }
380
+ function estimateToolLikeLineCount(toolName, expanded, output, width, pixConfig, superCompactTools, includeStatusLine) {
381
+ const rule = resolveToolRule(toolName, pixConfig.toolRenderer);
382
+ if (rule.hidden)
383
+ return 0;
384
+ if (expanded)
385
+ return 1 + estimateWrappedLineCount(output, Math.max(1, width - 2));
386
+ if (rule.compactHidden || (rule.defaultExpanded === true && !superCompactTools))
387
+ return 1;
388
+ const bodyLineCount = estimateWrappedLineCount(output, Math.max(1, width - 2));
389
+ const previewLineCount = Math.min(rule.previewLines, bodyLineCount);
390
+ const extraStatusLine = includeStatusLine && output.trimEnd().length === 0 ? 1 : 0;
391
+ return superCompactTools ? 1 : 1 + Math.max(extraStatusLine, previewLineCount);
392
+ }
@@ -1,6 +1,7 @@
1
1
  import { stringDisplayWidth } from "../../terminal-width.js";
2
2
  import { SUBAGENTS_WIDGET_MAX_ROWS } from "../constants.js";
3
3
  import { ellipsizeDisplay, padOrTrimPlain, wrapLine } from "./render-text.js";
4
+ import { thinkingLevelThemeColor } from "./status-line-renderer.js";
4
5
  import { activeSubagentStates, formatElapsedSince, formatSubagentsPanelStats, subagentModelThinkingLabel, subagentRunName, subagentStatusIcon, taskPreviewMap, } from "../subagents/subagents-model.js";
5
6
  import { formatTodoPanelStats, formatTodoTaskLine, hasOpenTodoTasks, shiftSegmentsToSlice, todoTaskLineSegments, visibleTodoTaskRows, visibleTodoTasks, } from "../todo/todo-model.js";
6
7
  export function renderTodoPanel(details, expanded, width, colors) {
@@ -18,35 +19,47 @@ export function renderTodoPanel(details, expanded, width, colors) {
18
19
  const headerText = `todos ${expanded ? "▾" : "▸"}${stats ? ` ${stats}` : ""}`;
19
20
  const todoPanelColor = colors.warning;
20
21
  const todoMetaColor = colors.muted;
22
+ const todoThinkingColor = (level) => thinkingLevelThemeColor(level, colors);
23
+ const todoStatusThemeColor = (status) => {
24
+ switch (status) {
25
+ case "pending": return colors.muted;
26
+ case "in_progress": return colors.warning;
27
+ case "deferred": return colors.muted;
28
+ case "completed": return colors.success;
29
+ case "deleted": return colors.error;
30
+ }
31
+ };
21
32
  if (!expanded) {
22
33
  const prefix = `${headerText} — current: `;
23
34
  const current = activeTask ? formatTodoTaskLine(activeTask) : "no active todo";
24
35
  const collapsedText = `${prefix}${current}`;
25
- const segments = activeTask
26
- ? todoTaskLineSegments(activeTask, todoMetaColor).map((segment) => ({
36
+ const segments = [
37
+ { start: 0, end: headerText.length, foreground: todoPanelColor },
38
+ { start: headerText.length, end: prefix.length, foreground: todoMetaColor },
39
+ ];
40
+ if (activeTask) {
41
+ const activeSegments = todoTaskLineSegments(activeTask, todoMetaColor, { thinkingColor: todoThinkingColor, statusColor: todoStatusThemeColor }).map((segment) => ({
27
42
  ...segment,
28
43
  start: segment.start + prefix.length,
29
44
  end: segment.end + prefix.length,
30
- }))
31
- : undefined;
45
+ }));
46
+ segments.push(...activeSegments);
47
+ }
32
48
  const line = {
33
49
  text: padOrTrimPlain(ellipsizeDisplay(collapsedText, contentWidth), width),
34
- colorOverride: todoPanelColor,
50
+ segments,
35
51
  target,
36
52
  };
37
- if (segments)
38
- line.segments = segments;
39
53
  return [line];
40
54
  }
41
55
  const lines = [];
42
56
  for (const { task, depth } of visibleTodoTaskRows(details)) {
43
57
  const text = formatTodoTaskLine(task, { depth });
44
- const segments = todoTaskLineSegments(task, todoMetaColor, { depth });
58
+ const segments = todoTaskLineSegments(task, todoMetaColor, { depth, thinkingColor: todoThinkingColor, statusColor: todoStatusThemeColor });
45
59
  let start = 0;
46
60
  for (const wrapped of wrapLine(text, contentWidth)) {
47
61
  lines.push({
48
62
  text: padOrTrimPlain(wrapped, width),
49
- colorOverride: todoPanelColor,
50
63
  segments: shiftSegmentsToSlice(segments, start, wrapped.length),
51
64
  target,
52
65
  });
@@ -0,0 +1,62 @@
1
+ import { type Theme } from "../../theme.js";
2
+ import { type ModelColorsConfig } from "../../config.js";
3
+ import type { PopupMenu } from "../../ui.js";
4
+ import type { ScreenStyler } from "../screen/screen-styler.js";
5
+ import type { Entry, ModelMenuValue, PixMenuItem, PixMenuOptions, QueueMessageMenuValue, RenderedLine, ResumeMenuValue, SlashCommand, ThinkingMenuValue, UserMessageJumpMenuValue, UserMessageMenuValue } from "../types.js";
6
+ import type { AgentSession } from "@earendil-works/pi-coding-agent";
7
+ export type PopupMenuRendererHost = {
8
+ readonly theme: Theme;
9
+ readonly screenStyler: ScreenStyler;
10
+ readonly entries: readonly Entry[];
11
+ readonly session: AgentSession | undefined;
12
+ readonly modelColors?: ModelColorsConfig;
13
+ readonly resumeLoading: boolean;
14
+ readonly resumeSessionCount: number;
15
+ readonly userMessageJumpLoading: boolean;
16
+ };
17
+ export declare class PopupMenuRenderer {
18
+ private readonly host;
19
+ constructor(host: PopupMenuRendererHost);
20
+ popupMenuWidth(columns: number): number;
21
+ popupMenuMargin(columns: number): number;
22
+ effectivePopupMenuWidth(columns: number): number;
23
+ styleOverlayLine(row: number, line: RenderedLine, width: number, activeMenu: PopupMenu<unknown>): string;
24
+ overlayPlainText(line: RenderedLine, width: number): string;
25
+ renderInlineUserMessageMenu(options: {
26
+ userContentWidth: number;
27
+ userContentLeft: number;
28
+ userLine: (text: string, entryId?: string, syntaxHighlight?: RenderedLine["syntaxHighlight"]) => RenderedLine;
29
+ }, menu: PopupMenu<UserMessageMenuValue>): RenderedLine[];
30
+ renderSlashCommandMenu(width: number, menu: PopupMenu<SlashCommand>): RenderedLine[];
31
+ renderModelMenu(width: number, menu: PopupMenu<ModelMenuValue>): RenderedLine[];
32
+ renderThinkingMenu(width: number, menu: PopupMenu<ThinkingMenuValue>): RenderedLine[];
33
+ renderResumeMenu(width: number, menu: PopupMenu<ResumeMenuValue>, state: {
34
+ directQuery: string;
35
+ allSessionsLoaded: boolean;
36
+ loadedSessionCount: number;
37
+ }): RenderedLine[];
38
+ renderUserMessageJumpMenu(width: number, menu: PopupMenu<UserMessageJumpMenuValue>, directQuery: string): RenderedLine[];
39
+ renderQueueMessageMenu(width: number, menu: PopupMenu<QueueMessageMenuValue>): RenderedLine[];
40
+ renderSdkMenu(width: number, menu: PopupMenu<PixMenuItem<unknown>>, request: {
41
+ options: PixMenuOptions;
42
+ } | undefined, directQuery: string): RenderedLine[];
43
+ private hasPopupActionItems;
44
+ private labelDescriptionText;
45
+ private userMessageActionForeground;
46
+ private selectableItemVariant;
47
+ private thinkingMenuItemSegments;
48
+ private modelMenuItemSegments;
49
+ private modelMenuItemColor;
50
+ private availableThinkingLevels;
51
+ private queueMessageItemVariant;
52
+ private sdkItemVariant;
53
+ private sdkMenuItemSegments;
54
+ private itemHighlightSegments;
55
+ private highlightSegments;
56
+ private descriptionHighlightSegments;
57
+ private resumeMenuItemSegments;
58
+ private popupMenuHeader;
59
+ private popupLineForeground;
60
+ private popupLineBackground;
61
+ }
62
+ export declare function formatPopupMenuHeader(title: string, width: number): string;