@h-rig/omp-extension-plugin 0.0.6-alpha.191 → 0.0.6-alpha.192

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.
@@ -16,6 +16,7 @@ export declare function hairline(width: number): string;
16
16
  * sort modes. Selection tracks the run, not the row index. */
17
17
  /** Board-styled menu/detail rows, used for every non-live family surface. */
18
18
  export declare class BoardSettingsList implements Component {
19
+ #private;
19
20
  items: BoardSettingItem[];
20
21
  selected: number;
21
22
  title: string;
@@ -26,6 +27,10 @@ export declare class BoardSettingsList implements Component {
26
27
  private firstSelectable;
27
28
  moveSelection(delta: number): void;
28
29
  selectedItem(): BoardSettingItem | null;
30
+ /** Item index under a rendered line, when that line is a selectable row. */
31
+ hitTest(line: number): number | undefined;
32
+ /** Select the item at `index` (mouse click); true when the selection moved. */
33
+ selectIndex(index: number): boolean;
29
34
  invalidate(): void;
30
35
  render(width: number): string[];
31
36
  private renderLines;
@@ -84,6 +89,7 @@ export type DetailModel = {
84
89
  actions: DetailAction[];
85
90
  };
86
91
  export declare class DetailComposer implements Component {
92
+ #private;
87
93
  model: DetailModel;
88
94
  selected: number;
89
95
  tick: number;
@@ -93,6 +99,10 @@ export declare class DetailComposer implements Component {
93
99
  private clamp;
94
100
  moveSelection(delta: number): void;
95
101
  selectedAction(): DetailAction | null;
102
+ /** Enabled-action index under a rendered line (mouse hit-testing). */
103
+ hitTestAction(line: number): number | undefined;
104
+ /** Select the enabled action at `index` (mouse click); true when it moved. */
105
+ selectAction(index: number): boolean;
96
106
  scrollBody(delta: number): void;
97
107
  resetScroll(): void;
98
108
  invalidate(): void;
@@ -4,6 +4,7 @@ import { truncateToWidth } from "@oh-my-pi/pi-tui";
4
4
  import {
5
5
  accent,
6
6
  accentDim,
7
+ bgPanel,
7
8
  bold,
8
9
  cyan,
9
10
  ink,
@@ -11,10 +12,6 @@ import {
11
12
  ink3,
12
13
  ink4,
13
14
  red,
14
- DRONE_WIDTH,
15
- renderDroneFrame,
16
- renderMicroDroneFrame,
17
- RIG_SPINNER_FRAMES,
18
15
  screenGlyph,
19
16
  sectionDivider,
20
17
  statusColor,
@@ -26,11 +23,13 @@ import {
26
23
  function hairline(width) {
27
24
  return ink4("\u2500".repeat(Math.max(0, width)));
28
25
  }
26
+
29
27
  class BoardSettingsList {
30
28
  items = [];
31
29
  selected = 0;
32
30
  title = "";
33
31
  emptyMessage = "nothing to show";
32
+ #rowToIndex = new Map;
34
33
  setItems(items) {
35
34
  this.items = [...items];
36
35
  if (!this.selectableAt(this.selected))
@@ -61,44 +60,77 @@ class BoardSettingsList {
61
60
  selectedItem() {
62
61
  return this.selectableAt(this.selected) ? this.items[this.selected] : null;
63
62
  }
63
+ hitTest(line) {
64
+ const index = this.#rowToIndex.get(line);
65
+ return index !== undefined && this.selectableAt(index) ? index : undefined;
66
+ }
67
+ selectIndex(index) {
68
+ if (!this.selectableAt(index))
69
+ return false;
70
+ const moved = this.selected !== index;
71
+ this.selected = index;
72
+ return moved;
73
+ }
64
74
  invalidate() {}
65
75
  render(width) {
66
76
  return this.renderLines(width).map((line) => truncateToWidth(line, Math.max(10, width - 1)));
67
77
  }
68
78
  renderLines(width) {
79
+ this.#rowToIndex.clear();
69
80
  if (this.items.length === 0) {
70
81
  return ["", ` ${ink2(this.emptyMessage)}`, ""];
71
82
  }
72
- const maxVisible = Math.max(8, Math.min(16, (process.stdout.rows ?? 30) - 10));
83
+ const maxVisible = Math.max(8, Math.min(18, (process.stdout.rows ?? 30) - 10));
73
84
  const start = Math.max(0, Math.min(this.selected - Math.floor(maxVisible / 2), this.items.length - maxVisible));
74
85
  const window = this.items.slice(start, start + maxVisible);
75
- const valueWidth = Math.min(24, Math.max(10, Math.floor(width * 0.24)));
76
- const labelWidth = Math.min(24, Math.max(10, Math.floor(width * 0.26)));
77
- const lines = window.flatMap((item, index) => {
86
+ const labelWidth = Math.min(28, Math.max(10, ...window.map((item) => {
87
+ const navGlyph = item.id.startsWith("to:") ? 2 : 0;
88
+ return item.label.length + navGlyph;
89
+ })));
90
+ const actionWidth = Math.max(0, ...window.map((item) => item.values?.[0] && item.values[0] !== item.currentValue ? item.values[0].length + 2 : 0));
91
+ const valueWidth = Math.max(12, width - labelWidth - actionWidth - 8);
92
+ const lines = [""];
93
+ for (let index = 0;index < window.length; index += 1) {
94
+ const item = window[index];
78
95
  const absolute = start + index;
79
96
  const isSelected = absolute === this.selected;
80
- const marker = isSelected ? accent("\u258C") : " ";
97
+ if (item.id === "title") {
98
+ lines.push(` ${accent("\u258D")}${bold(ink(item.label))}${item.currentValue ? ` ${ink3(item.currentValue)}` : ""}`);
99
+ if (item.description)
100
+ lines.push(` ${ink4(truncateToWidth(item.description, Math.max(12, width - 4)))}`);
101
+ lines.push("");
102
+ continue;
103
+ }
104
+ if (item.heading && !item.currentValue) {
105
+ lines.push(sectionDivider(item.label.toLowerCase(), width));
106
+ continue;
107
+ }
81
108
  const navGlyph = item.id.startsWith("to:") ? `${screenGlyph(item.id.slice(3))} ` : "";
82
- const labelText = truncateToWidth(`${navGlyph}${item.label}`, labelWidth).padEnd(labelWidth);
83
- const isTitle = item.id === "title";
109
+ const labelPlain = truncateToWidth(`${navGlyph}${item.label}`, labelWidth).padEnd(labelWidth);
84
110
  const isStatus = item.id.endsWith(":status");
85
- const label = isTitle ? accent(labelText) : isSelected ? ink(labelText) : item.heading ? ink3(labelText) : ink2(labelText);
86
- const value = !item.currentValue ? ink4("".padEnd(valueWidth)) : isStatus ? statusColor(item.currentValue)(truncateToWidth(`${statusGlyph(item.currentValue)} ${item.currentValue}`, valueWidth).padEnd(valueWidth)) : item.heading ? ink2(truncateToWidth(item.currentValue, valueWidth).padEnd(valueWidth)) : ink3(truncateToWidth(item.currentValue, valueWidth).padEnd(valueWidth));
87
- const action = item.values?.[0] && item.values[0] !== item.currentValue ? isSelected ? accent(item.values[0]) : accentDim(item.values[0]) : "";
88
- const row = `${marker} ${label} ${value}${action ? ` ${action}` : ""}`;
111
+ const valuePlain = truncateToWidth(item.currentValue ?? "", valueWidth).padEnd(valueWidth);
112
+ const label = isSelected ? ink(labelPlain) : item.heading ? ink3(labelPlain) : ink2(labelPlain);
113
+ const value = !item.currentValue ? ink4(valuePlain) : isStatus ? statusColor(item.currentValue)(truncateToWidth(`${statusGlyph(item.currentValue)} ${item.currentValue}`, valueWidth).padEnd(valueWidth)) : item.heading ? ink2(valuePlain) : ink3(valuePlain);
114
+ const actionText = item.values?.[0] && item.values[0] !== item.currentValue ? item.values[0] : "";
115
+ const action = actionText ? isSelected ? accent(`${actionText} \u203A`) : accentDim(actionText) : "";
116
+ const rail = isSelected ? accent("\u258C") : " ";
117
+ const body = `${rail} ${label} ${value}${action ? ` ${action}` : ""}`;
118
+ if (!item.heading)
119
+ this.#rowToIndex.set(lines.length, absolute);
120
+ lines.push(isSelected ? bgPanel(body) : body);
89
121
  if (isSelected && item.description) {
90
- return [row, ` ${ink4(truncateToWidth(item.description, Math.max(12, width - 5)))}`];
122
+ if (!item.heading)
123
+ this.#rowToIndex.set(lines.length, absolute);
124
+ lines.push(` ${ink4("\u21B3")} ${ink4(truncateToWidth(item.description, Math.max(12, width - 6)))}`);
91
125
  }
92
- return [row];
93
- });
126
+ }
94
127
  const meta = [];
95
128
  if (this.items.length > maxVisible)
96
- meta.push(`${this.selected + 1}/${this.items.length}`);
97
- if (this.title)
98
- meta.push(this.title);
129
+ meta.push(`${this.selected + 1} of ${this.items.length}`);
99
130
  if (meta.length > 0)
100
- lines.push(ink4(` ${meta.join(" \xB7 ")}`));
101
- return ["", ...lines, ""];
131
+ lines.push("", ink4(` ${meta.join(" \xB7 ")}`));
132
+ lines.push("");
133
+ return lines;
102
134
  }
103
135
  }
104
136
 
@@ -190,6 +222,7 @@ class DetailComposer {
190
222
  selected = 0;
191
223
  tick = 0;
192
224
  bodyScroll = 0;
225
+ #lineToAction = new Map;
193
226
  setModel(model) {
194
227
  const prevId = this.enabledActions()[this.selected]?.id;
195
228
  this.model = model;
@@ -214,6 +247,17 @@ class DetailComposer {
214
247
  selectedAction() {
215
248
  return this.enabledActions()[this.selected] ?? null;
216
249
  }
250
+ hitTestAction(line) {
251
+ return this.#lineToAction.get(line);
252
+ }
253
+ selectAction(index) {
254
+ const count = this.enabledActions().length;
255
+ if (index < 0 || index >= count)
256
+ return false;
257
+ const moved = this.selected !== index;
258
+ this.selected = index;
259
+ return moved;
260
+ }
217
261
  scrollBody(delta) {
218
262
  this.bodyScroll = Math.max(0, this.bodyScroll + delta);
219
263
  }
@@ -243,21 +287,45 @@ class DetailComposer {
243
287
  }
244
288
  if (model.stages && model.stages.length > 0) {
245
289
  bottom.push(sectionDivider("pipeline", width));
246
- const parts = model.stages.map((stage) => `${statusColor(stage.status)(statusGlyph(stage.status))} ${ink3(stage.label)}`);
247
- bottom.push(truncateToWidth(` ${parts.join(ink4(" \u2192 "))}`, inner));
290
+ let linePlain = " ";
291
+ let linePainted = " ";
292
+ for (let i = 0;i < model.stages.length; i += 1) {
293
+ const stage = model.stages[i];
294
+ const sepPlain = linePlain.trim().length > 0 ? " \u2192 " : " ";
295
+ const cellPlain = `${sepPlain}? ${stage.label}`;
296
+ if (linePlain.length + cellPlain.length > inner - 2 && linePlain.trim().length > 0) {
297
+ bottom.push(linePainted);
298
+ linePlain = " ";
299
+ linePainted = " ";
300
+ }
301
+ const sep = linePlain.trim().length > 0 ? ink4(" \u2192 ") : " ";
302
+ linePlain += cellPlain;
303
+ linePainted += `${sep}${statusColor(stage.status)(statusGlyph(stage.status))} ${ink3(stage.label)}`;
304
+ }
305
+ if (linePainted.trim().length > 0)
306
+ bottom.push(linePainted);
248
307
  bottom.push("");
249
308
  }
309
+ const actionLines = [];
250
310
  if (model.actions.length > 0) {
251
311
  bottom.push(sectionDivider("actions", width));
252
312
  const selId = this.selectedAction()?.id;
313
+ let enabledIndex = -1;
253
314
  for (const action of model.actions) {
254
315
  const on = action.enabled !== false;
316
+ if (on)
317
+ enabledIndex += 1;
255
318
  const isSel = on && action.id === selId;
256
319
  const rail = isSel ? accent("\u258C") : " ";
257
320
  const labelText = `${action.glyph ? `${action.glyph} ` : ""}${action.label}`;
258
321
  const label = !on ? ink4(labelText) : action.danger ? red(labelText) : isSel ? accent(labelText) : ink2(labelText);
259
322
  const tail = !on ? ink4(action.value ?? "unavailable") : isSel ? ink3(action.hint ?? action.value ?? "") : ink3(action.value ?? "");
260
- bottom.push(`${rail} ${label}${tail ? ` ${tail}` : ""}`);
323
+ const row = `${rail} ${label}${tail ? ` ${tail}` : ""}`;
324
+ if (on)
325
+ actionLines.push(enabledIndex);
326
+ else
327
+ actionLines.push(-1);
328
+ bottom.push(isSel ? bgPanel(row) : row);
261
329
  }
262
330
  }
263
331
  const body = [];
@@ -282,10 +350,25 @@ class DetailComposer {
282
350
  }
283
351
  }
284
352
  const out = [...top, ...body, ...bottom];
353
+ const mapActions = (linesOut) => {
354
+ this.#lineToAction.clear();
355
+ if (actionLines.length === 0)
356
+ return;
357
+ const actionCount = model.actions.length;
358
+ const firstActionLine = linesOut.length - actionCount;
359
+ for (let i = 0;i < actionCount; i += 1) {
360
+ const enabledIdx = actionLines[i];
361
+ if (enabledIdx >= 0)
362
+ this.#lineToAction.set(firstActionLine + i, enabledIdx);
363
+ }
364
+ };
285
365
  if (out.length > budget) {
286
366
  const keepTail = Math.min(bottom.length, budget - top.length);
287
- return [...top, ...out.slice(out.length - keepTail)].map((line) => truncateToWidth(line, Math.max(10, width - 1)));
367
+ const clipped = [...top, ...out.slice(out.length - keepTail)];
368
+ mapActions(clipped);
369
+ return clipped.map((line) => truncateToWidth(line, Math.max(10, width - 1)));
288
370
  }
371
+ mapActions(out);
289
372
  return out.map((line) => truncateToWidth(line, Math.max(10, width - 1)));
290
373
  }
291
374
  }
@@ -68,7 +68,7 @@ declare function runListActionHints(run: RunRecord, source?: RunClassificationSo
68
68
  }[];
69
69
  declare function taskDetailModel(state: RigFlowState, server: RigServerState | null, projection: TaskProjectionCapability): DetailModel;
70
70
  declare function runDetailModel(run: RunRecord, source?: RunClassificationSource): DetailModel;
71
- declare function itemsFor(screen: RigScreen, runs: readonly RunRecord[], actionRowsEnabled: boolean, _selectedRunId: string | null, state?: RigFlowState, catalog?: readonly ScreenDescriptor[], projection?: TaskProjectionCapability): SettingItem[];
71
+ declare function itemsFor(screen: RigScreen, runs: readonly RunRecord[], actionRowsEnabled: boolean, _selectedRunId: string | null, state?: RigFlowState, catalog?: readonly ScreenDescriptor[], _projection?: TaskProjectionCapability): SettingItem[];
72
72
  export declare const __rigExtensionTest: {
73
73
  itemsFor: typeof itemsFor;
74
74
  taskDetailModel: typeof taskDetailModel;