@h-rig/omp-extension-plugin 0.0.6-alpha.186
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.
- package/README.md +1 -0
- package/dist/src/cockpit/board-views.d.ts +100 -0
- package/dist/src/cockpit/board-views.js +298 -0
- package/dist/src/cockpit/cockpit-product.d.ts +12 -0
- package/dist/src/cockpit/cockpit-product.js +55 -0
- package/dist/src/cockpit/drone-preloader.d.ts +11 -0
- package/dist/src/cockpit/drone-preloader.js +220 -0
- package/dist/src/cockpit/extension.d.ts +85 -0
- package/dist/src/cockpit/extension.js +2222 -0
- package/dist/src/cockpit/plugin.d.ts +14 -0
- package/dist/src/cockpit/plugin.js +2277 -0
- package/dist/src/cockpit/screen-catalog.d.ts +55 -0
- package/dist/src/cockpit/screen-catalog.js +65 -0
- package/dist/src/plugin.d.ts +4 -0
- package/dist/src/plugin.js +2596 -0
- package/dist/src/product-entrypoint/contributions.d.ts +3 -0
- package/dist/src/product-entrypoint/contributions.js +215 -0
- package/dist/src/product-entrypoint/metadata.d.ts +17 -0
- package/dist/src/product-entrypoint/metadata.js +29 -0
- package/dist/src/product-entrypoint/plugin.d.ts +4 -0
- package/dist/src/product-entrypoint/plugin.js +300 -0
- package/dist/src/product-entrypoint/product-entrypoint.d.ts +14 -0
- package/dist/src/product-entrypoint/product-entrypoint.js +136 -0
- package/dist/src/product-entrypoint/product-help.d.ts +6 -0
- package/dist/src/product-entrypoint/product-help.js +57 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @h-rig/omp-extension-plugin
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { HelpCatalog, RigBoardRunRow, RigBoardTaskRow } from "@rig/contracts";
|
|
2
|
+
import { type Component } from "@oh-my-pi/pi-tui";
|
|
3
|
+
export type BoardRun = RigBoardRunRow;
|
|
4
|
+
export type BoardTask = RigBoardTaskRow;
|
|
5
|
+
export type RunSortMode = "active" | "recent" | "status";
|
|
6
|
+
export type BoardSettingItem = {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
currentValue?: string;
|
|
10
|
+
values?: readonly string[];
|
|
11
|
+
description?: string;
|
|
12
|
+
heading?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function hairline(width: number): string;
|
|
15
|
+
/** Live runs list: selection bar, status dots, scroll window, search filter,
|
|
16
|
+
* sort modes. Selection tracks the run, not the row index. */
|
|
17
|
+
/** Board-styled menu/detail rows, used for every non-live family surface. */
|
|
18
|
+
export declare class BoardSettingsList implements Component {
|
|
19
|
+
items: BoardSettingItem[];
|
|
20
|
+
selected: number;
|
|
21
|
+
title: string;
|
|
22
|
+
emptyMessage: string;
|
|
23
|
+
setItems(items: readonly BoardSettingItem[]): void;
|
|
24
|
+
setTitle(title: string): void;
|
|
25
|
+
private selectableAt;
|
|
26
|
+
private firstSelectable;
|
|
27
|
+
moveSelection(delta: number): void;
|
|
28
|
+
selectedItem(): BoardSettingItem | null;
|
|
29
|
+
invalidate(): void;
|
|
30
|
+
render(width: number): string[];
|
|
31
|
+
private renderLines;
|
|
32
|
+
}
|
|
33
|
+
/** Full-width hairline rule, matching the published shell. */
|
|
34
|
+
export declare class Rule implements Component {
|
|
35
|
+
invalidate(): void;
|
|
36
|
+
render(width: number): string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* In-app help built from the shared command catalog (the exact published help
|
|
40
|
+
* data, owned by @rig/omp-extension-plugin), with width-aware two-column layout.
|
|
41
|
+
*/
|
|
42
|
+
export declare class HelpView implements Component {
|
|
43
|
+
#private;
|
|
44
|
+
private readonly catalog;
|
|
45
|
+
private readonly group?;
|
|
46
|
+
offset: number;
|
|
47
|
+
constructor(catalog: HelpCatalog, group?: string | undefined);
|
|
48
|
+
scroll(delta: number): void;
|
|
49
|
+
invalidate(): void;
|
|
50
|
+
render(width: number): string[];
|
|
51
|
+
}
|
|
52
|
+
export type DetailTone = "muted" | "strong" | "link" | "warn";
|
|
53
|
+
export type DetailFact = {
|
|
54
|
+
label: string;
|
|
55
|
+
value: string;
|
|
56
|
+
tone?: DetailTone;
|
|
57
|
+
/** When set, the value renders with the status color + glyph. */
|
|
58
|
+
status?: string;
|
|
59
|
+
};
|
|
60
|
+
export type DetailAction = {
|
|
61
|
+
id: string;
|
|
62
|
+
label: string;
|
|
63
|
+
glyph?: string;
|
|
64
|
+
value?: string;
|
|
65
|
+
hint?: string;
|
|
66
|
+
/** Default true; disabled actions render greyed and are not selectable. */
|
|
67
|
+
enabled?: boolean;
|
|
68
|
+
danger?: boolean;
|
|
69
|
+
};
|
|
70
|
+
export type DetailStage = {
|
|
71
|
+
label: string;
|
|
72
|
+
status: string;
|
|
73
|
+
};
|
|
74
|
+
export type DetailModel = {
|
|
75
|
+
screen: "task-detail" | "run-detail";
|
|
76
|
+
icon: string;
|
|
77
|
+
ref: string;
|
|
78
|
+
title: string;
|
|
79
|
+
status?: string;
|
|
80
|
+
body?: string;
|
|
81
|
+
bodyLabel?: string;
|
|
82
|
+
facts: DetailFact[];
|
|
83
|
+
stages?: DetailStage[];
|
|
84
|
+
actions: DetailAction[];
|
|
85
|
+
};
|
|
86
|
+
export declare class DetailComposer implements Component {
|
|
87
|
+
model: DetailModel;
|
|
88
|
+
selected: number;
|
|
89
|
+
tick: number;
|
|
90
|
+
private bodyScroll;
|
|
91
|
+
setModel(model: DetailModel): void;
|
|
92
|
+
private enabledActions;
|
|
93
|
+
private clamp;
|
|
94
|
+
moveSelection(delta: number): void;
|
|
95
|
+
selectedAction(): DetailAction | null;
|
|
96
|
+
scrollBody(delta: number): void;
|
|
97
|
+
resetScroll(): void;
|
|
98
|
+
invalidate(): void;
|
|
99
|
+
render(width: number): string[];
|
|
100
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/omp-extension-plugin/src/cockpit/board-views.ts
|
|
3
|
+
import { truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
4
|
+
import {
|
|
5
|
+
accent,
|
|
6
|
+
accentDim,
|
|
7
|
+
bold,
|
|
8
|
+
cyan,
|
|
9
|
+
ink,
|
|
10
|
+
ink2,
|
|
11
|
+
ink3,
|
|
12
|
+
ink4,
|
|
13
|
+
red,
|
|
14
|
+
DRONE_WIDTH,
|
|
15
|
+
renderDroneFrame,
|
|
16
|
+
renderMicroDroneFrame,
|
|
17
|
+
RIG_SPINNER_FRAMES,
|
|
18
|
+
screenGlyph,
|
|
19
|
+
sectionDivider,
|
|
20
|
+
statusColor,
|
|
21
|
+
statusGlyph,
|
|
22
|
+
statusPill,
|
|
23
|
+
wrapText,
|
|
24
|
+
yellow
|
|
25
|
+
} from "@rig/std-shared/board-theme";
|
|
26
|
+
function hairline(width) {
|
|
27
|
+
return ink4("\u2500".repeat(Math.max(0, width)));
|
|
28
|
+
}
|
|
29
|
+
class BoardSettingsList {
|
|
30
|
+
items = [];
|
|
31
|
+
selected = 0;
|
|
32
|
+
title = "";
|
|
33
|
+
emptyMessage = "nothing to show";
|
|
34
|
+
setItems(items) {
|
|
35
|
+
this.items = [...items];
|
|
36
|
+
if (!this.selectableAt(this.selected))
|
|
37
|
+
this.selected = this.firstSelectable();
|
|
38
|
+
}
|
|
39
|
+
setTitle(title) {
|
|
40
|
+
this.title = title;
|
|
41
|
+
}
|
|
42
|
+
selectableAt(index) {
|
|
43
|
+
const item = this.items[index];
|
|
44
|
+
return !!item && item.heading !== true;
|
|
45
|
+
}
|
|
46
|
+
firstSelectable() {
|
|
47
|
+
const i = this.items.findIndex((_, idx) => this.selectableAt(idx));
|
|
48
|
+
return i >= 0 ? i : 0;
|
|
49
|
+
}
|
|
50
|
+
moveSelection(delta) {
|
|
51
|
+
if (this.items.length === 0)
|
|
52
|
+
return;
|
|
53
|
+
const step = delta < 0 ? -1 : 1;
|
|
54
|
+
for (let i = this.selected + step;i >= 0 && i < this.items.length; i += step) {
|
|
55
|
+
if (this.selectableAt(i)) {
|
|
56
|
+
this.selected = i;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
selectedItem() {
|
|
62
|
+
return this.selectableAt(this.selected) ? this.items[this.selected] : null;
|
|
63
|
+
}
|
|
64
|
+
invalidate() {}
|
|
65
|
+
render(width) {
|
|
66
|
+
return this.renderLines(width).map((line) => truncateToWidth(line, Math.max(10, width - 1)));
|
|
67
|
+
}
|
|
68
|
+
renderLines(width) {
|
|
69
|
+
if (this.items.length === 0) {
|
|
70
|
+
return ["", ` ${ink2(this.emptyMessage)}`, ""];
|
|
71
|
+
}
|
|
72
|
+
const maxVisible = Math.max(8, Math.min(16, (process.stdout.rows ?? 30) - 10));
|
|
73
|
+
const start = Math.max(0, Math.min(this.selected - Math.floor(maxVisible / 2), this.items.length - maxVisible));
|
|
74
|
+
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) => {
|
|
78
|
+
const absolute = start + index;
|
|
79
|
+
const isSelected = absolute === this.selected;
|
|
80
|
+
const marker = isSelected ? accent("\u258C") : " ";
|
|
81
|
+
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";
|
|
84
|
+
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}` : ""}`;
|
|
89
|
+
if (isSelected && item.description) {
|
|
90
|
+
return [row, ` ${ink4(truncateToWidth(item.description, Math.max(12, width - 5)))}`];
|
|
91
|
+
}
|
|
92
|
+
return [row];
|
|
93
|
+
});
|
|
94
|
+
const meta = [];
|
|
95
|
+
if (this.items.length > maxVisible)
|
|
96
|
+
meta.push(`${this.selected + 1}/${this.items.length}`);
|
|
97
|
+
if (this.title)
|
|
98
|
+
meta.push(this.title);
|
|
99
|
+
if (meta.length > 0)
|
|
100
|
+
lines.push(ink4(` ${meta.join(" \xB7 ")}`));
|
|
101
|
+
return ["", ...lines, ""];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
class Rule {
|
|
106
|
+
invalidate() {}
|
|
107
|
+
render(width) {
|
|
108
|
+
return [hairline(width)];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class HelpView {
|
|
113
|
+
catalog;
|
|
114
|
+
group;
|
|
115
|
+
offset = 0;
|
|
116
|
+
#built = null;
|
|
117
|
+
constructor(catalog, group) {
|
|
118
|
+
this.catalog = catalog;
|
|
119
|
+
this.group = group;
|
|
120
|
+
}
|
|
121
|
+
#build(width) {
|
|
122
|
+
if (this.#built)
|
|
123
|
+
return this.#built;
|
|
124
|
+
const { sections, groups } = this.catalog;
|
|
125
|
+
const lines = [];
|
|
126
|
+
const groupAnchors = new Map;
|
|
127
|
+
const columnWidth = Math.min(38, Math.max(24, Math.floor(width * 0.4)));
|
|
128
|
+
const row = (command, description) => {
|
|
129
|
+
const left = command.length >= columnWidth ? `${command} ` : command.padEnd(columnWidth);
|
|
130
|
+
return ` ${bold(ink(left))} ${ink2(truncateToWidth(description, Math.max(16, width - columnWidth - 4)))}`;
|
|
131
|
+
};
|
|
132
|
+
for (const section of sections) {
|
|
133
|
+
lines.push(`${accent("\u25C7")} ${bold(ink(section.title))} ${ink3(`\u2014 ${section.subtitle}`)}`);
|
|
134
|
+
for (const command of section.commands)
|
|
135
|
+
lines.push(row(command.command, command.description));
|
|
136
|
+
lines.push("");
|
|
137
|
+
}
|
|
138
|
+
lines.push(`${accent("\u25C7")} ${bold(ink("command groups"))} ${ink3("\u2014 every `rig <group>` surface")}`);
|
|
139
|
+
lines.push("");
|
|
140
|
+
for (const group of groups) {
|
|
141
|
+
groupAnchors.set(group.name, lines.length);
|
|
142
|
+
lines.push(`${accentDim("\u258D")}${bold(ink(`rig ${group.name}`))} ${ink3(`\u2014 ${group.summary}`)}`);
|
|
143
|
+
for (const usage of group.usage)
|
|
144
|
+
lines.push(` ${cyan(usage)}`);
|
|
145
|
+
for (const command of group.commands)
|
|
146
|
+
lines.push(row(command.command, command.description));
|
|
147
|
+
if (group.examples?.length) {
|
|
148
|
+
for (const example of group.examples)
|
|
149
|
+
lines.push(` ${ink4("$")} ${ink2(example)}`);
|
|
150
|
+
}
|
|
151
|
+
if (group.next?.length) {
|
|
152
|
+
for (const next of group.next)
|
|
153
|
+
lines.push(` ${accent("\u203A")} ${ink3(next)}`);
|
|
154
|
+
}
|
|
155
|
+
lines.push("");
|
|
156
|
+
}
|
|
157
|
+
this.#built = { lines, groupAnchors };
|
|
158
|
+
if (this.group) {
|
|
159
|
+
const anchor = groupAnchors.get(this.group);
|
|
160
|
+
if (anchor !== undefined)
|
|
161
|
+
this.offset = anchor;
|
|
162
|
+
}
|
|
163
|
+
return this.#built;
|
|
164
|
+
}
|
|
165
|
+
scroll(delta) {
|
|
166
|
+
if (!this.#built)
|
|
167
|
+
return;
|
|
168
|
+
const max = Math.max(0, this.#built.lines.length - 10);
|
|
169
|
+
this.offset = Math.max(0, Math.min(max, this.offset + delta));
|
|
170
|
+
}
|
|
171
|
+
invalidate() {
|
|
172
|
+
this.#built = null;
|
|
173
|
+
}
|
|
174
|
+
render(width) {
|
|
175
|
+
const { lines } = this.#build(width);
|
|
176
|
+
const viewport = Math.max(10, (process.stdout.rows ?? 30) - 8);
|
|
177
|
+
const slice = lines.slice(this.offset, this.offset + viewport);
|
|
178
|
+
const more = this.offset + viewport < lines.length;
|
|
179
|
+
return [
|
|
180
|
+
"",
|
|
181
|
+
...slice.map((line) => truncateToWidth(` ${line}`, Math.max(10, width - 1))),
|
|
182
|
+
more ? ink4(` \u2026 ${lines.length - this.offset - viewport} more (\u2193)`) : ""
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
var EMPTY_DETAIL = { screen: "task-detail", icon: "", ref: "", title: "", facts: [], actions: [] };
|
|
187
|
+
|
|
188
|
+
class DetailComposer {
|
|
189
|
+
model = EMPTY_DETAIL;
|
|
190
|
+
selected = 0;
|
|
191
|
+
tick = 0;
|
|
192
|
+
bodyScroll = 0;
|
|
193
|
+
setModel(model) {
|
|
194
|
+
const prevId = this.enabledActions()[this.selected]?.id;
|
|
195
|
+
this.model = model;
|
|
196
|
+
const actions = this.enabledActions();
|
|
197
|
+
const idx = prevId ? actions.findIndex((action) => action.id === prevId) : -1;
|
|
198
|
+
this.selected = idx >= 0 ? idx : 0;
|
|
199
|
+
this.clamp();
|
|
200
|
+
}
|
|
201
|
+
enabledActions() {
|
|
202
|
+
return this.model.actions.filter((action) => action.enabled !== false);
|
|
203
|
+
}
|
|
204
|
+
clamp() {
|
|
205
|
+
const count = this.enabledActions().length;
|
|
206
|
+
this.selected = count === 0 ? 0 : Math.max(0, Math.min(this.selected, count - 1));
|
|
207
|
+
}
|
|
208
|
+
moveSelection(delta) {
|
|
209
|
+
const count = this.enabledActions().length;
|
|
210
|
+
if (count === 0)
|
|
211
|
+
return;
|
|
212
|
+
this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));
|
|
213
|
+
}
|
|
214
|
+
selectedAction() {
|
|
215
|
+
return this.enabledActions()[this.selected] ?? null;
|
|
216
|
+
}
|
|
217
|
+
scrollBody(delta) {
|
|
218
|
+
this.bodyScroll = Math.max(0, this.bodyScroll + delta);
|
|
219
|
+
}
|
|
220
|
+
resetScroll() {
|
|
221
|
+
this.bodyScroll = 0;
|
|
222
|
+
}
|
|
223
|
+
invalidate() {}
|
|
224
|
+
render(width) {
|
|
225
|
+
const model = this.model;
|
|
226
|
+
const inner = Math.max(12, width - 2);
|
|
227
|
+
const budget = Math.max(8, (process.stdout.rows ?? 40) - 5);
|
|
228
|
+
const top = [""];
|
|
229
|
+
top.push(` ${accent(model.icon)} ${accent(model.ref)}${model.status ? ` ${statusPill(model.status)}` : ""}`);
|
|
230
|
+
for (const line of wrapText(model.title, inner - 1))
|
|
231
|
+
top.push(` ${ink(line)}`);
|
|
232
|
+
top.push("");
|
|
233
|
+
const bottom = [];
|
|
234
|
+
if (model.facts.length > 0) {
|
|
235
|
+
bottom.push(sectionDivider("details", width));
|
|
236
|
+
const labelWidth = Math.min(16, Math.max(...model.facts.map((fact) => fact.label.length)) + 1);
|
|
237
|
+
for (const fact of model.facts) {
|
|
238
|
+
const label = ink3(fact.label.toUpperCase().padEnd(labelWidth));
|
|
239
|
+
const value = fact.status ? statusColor(fact.status)(`${statusGlyph(fact.status)} ${fact.value}`) : fact.tone === "link" ? cyan(truncateToWidth(fact.value, inner - labelWidth - 3)) : fact.tone === "warn" ? yellow(fact.value) : ink2(truncateToWidth(fact.value, inner - labelWidth - 3));
|
|
240
|
+
bottom.push(` ${label} ${value}`);
|
|
241
|
+
}
|
|
242
|
+
bottom.push("");
|
|
243
|
+
}
|
|
244
|
+
if (model.stages && model.stages.length > 0) {
|
|
245
|
+
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));
|
|
248
|
+
bottom.push("");
|
|
249
|
+
}
|
|
250
|
+
if (model.actions.length > 0) {
|
|
251
|
+
bottom.push(sectionDivider("actions", width));
|
|
252
|
+
const selId = this.selectedAction()?.id;
|
|
253
|
+
for (const action of model.actions) {
|
|
254
|
+
const on = action.enabled !== false;
|
|
255
|
+
const isSel = on && action.id === selId;
|
|
256
|
+
const rail = isSel ? accent("\u258C") : " ";
|
|
257
|
+
const labelText = `${action.glyph ? `${action.glyph} ` : ""}${action.label}`;
|
|
258
|
+
const label = !on ? ink4(labelText) : action.danger ? red(labelText) : isSel ? accent(labelText) : ink2(labelText);
|
|
259
|
+
const tail = !on ? ink4(action.value ?? "unavailable") : isSel ? ink3(action.hint ?? action.value ?? "") : ink3(action.value ?? "");
|
|
260
|
+
bottom.push(`${rail} ${label}${tail ? ` ${tail}` : ""}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const body = [];
|
|
264
|
+
if (model.body && model.body.trim()) {
|
|
265
|
+
const bodyCap = budget - top.length - bottom.length;
|
|
266
|
+
if (bodyCap >= 3) {
|
|
267
|
+
const bodyLines = wrapText(model.body, inner - 3);
|
|
268
|
+
const contentCap = bodyCap - 2;
|
|
269
|
+
const needScroll = bodyLines.length > contentCap;
|
|
270
|
+
const shownCount = needScroll ? Math.max(1, contentCap - 1) : bodyLines.length;
|
|
271
|
+
const maxScroll = Math.max(0, bodyLines.length - shownCount);
|
|
272
|
+
const scroll = Math.min(this.bodyScroll, maxScroll);
|
|
273
|
+
body.push(sectionDivider(model.bodyLabel ?? "body", width));
|
|
274
|
+
for (const line of bodyLines.slice(scroll, scroll + shownCount))
|
|
275
|
+
body.push(` ${ink4("\u2502")} ${ink2(line)}`);
|
|
276
|
+
if (needScroll) {
|
|
277
|
+
const up = scroll > 0 ? "\u2191 " : "";
|
|
278
|
+
const down = scroll < maxScroll ? `\u2193 ${maxScroll - scroll} more` : "";
|
|
279
|
+
body.push(` ${ink4("\u2502")} ${accentDim(`${up}${down}`.trim())}`);
|
|
280
|
+
}
|
|
281
|
+
body.push("");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const out = [...top, ...body, ...bottom];
|
|
285
|
+
if (out.length > budget) {
|
|
286
|
+
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)));
|
|
288
|
+
}
|
|
289
|
+
return out.map((line) => truncateToWidth(line, Math.max(10, width - 1)));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
export {
|
|
293
|
+
hairline,
|
|
294
|
+
Rule,
|
|
295
|
+
HelpView,
|
|
296
|
+
DetailComposer,
|
|
297
|
+
BoardSettingsList
|
|
298
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CockpitCapabilities } from "@rig/contracts";
|
|
2
|
+
import { type PluginHost } from "@rig/core";
|
|
3
|
+
export type { CockpitCapabilities, DispatchOutcomeCapability, RigTaskBadgeProjection, TaskProjectionCapability, } from "@rig/contracts";
|
|
4
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
5
|
+
export declare const defaultCockpitCapabilities: CockpitCapabilities;
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the cockpit capability bag from the live plugin host. Task projection
|
|
8
|
+
* is required when a host exists: cockpit must consume TASK_PROJECTION from the
|
|
9
|
+
* task-io owner instead of silently falling back to local semantics. Dispatch
|
|
10
|
+
* outcome wording remains optional and inert when absent.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveCockpitCapabilities(host?: PluginHost): Promise<CockpitCapabilities>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/omp-extension-plugin/src/cockpit/cockpit-product.ts
|
|
3
|
+
import {
|
|
4
|
+
DISPATCH_OUTCOME_CAPABILITY_ID,
|
|
5
|
+
TASK_PROJECTION_CAPABILITY_ID
|
|
6
|
+
} from "@rig/contracts";
|
|
7
|
+
import { defineServiceCapability } from "@rig/core";
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
var unavailableTaskProjection = {
|
|
12
|
+
normalizeStatus: () => "unknown",
|
|
13
|
+
normalizePriority: () => null,
|
|
14
|
+
project: (task) => ({
|
|
15
|
+
id: task.id,
|
|
16
|
+
title: "",
|
|
17
|
+
status: "unknown",
|
|
18
|
+
priority: null,
|
|
19
|
+
metadata: { projection: "unavailable" }
|
|
20
|
+
}),
|
|
21
|
+
assigneeText: () => "",
|
|
22
|
+
title: () => "",
|
|
23
|
+
id: () => null,
|
|
24
|
+
status: () => "unknown",
|
|
25
|
+
description: () => "",
|
|
26
|
+
listRows: () => [],
|
|
27
|
+
boardTasks: () => [],
|
|
28
|
+
stats: () => null
|
|
29
|
+
};
|
|
30
|
+
var unavailableDispatchOutcome = {
|
|
31
|
+
humanize: () => null
|
|
32
|
+
};
|
|
33
|
+
var defaultCockpitCapabilities = {
|
|
34
|
+
taskProjection: unavailableTaskProjection,
|
|
35
|
+
dispatchOutcome: unavailableDispatchOutcome
|
|
36
|
+
};
|
|
37
|
+
var TaskProjectionCapability = defineServiceCapability(TASK_PROJECTION_CAPABILITY_ID);
|
|
38
|
+
var DispatchOutcomeCapability = defineServiceCapability(DISPATCH_OUTCOME_CAPABILITY_ID);
|
|
39
|
+
async function resolveCockpitCapabilities(host) {
|
|
40
|
+
if (!host)
|
|
41
|
+
return defaultCockpitCapabilities;
|
|
42
|
+
const [taskProjection, dispatchOutcome] = await Promise.all([
|
|
43
|
+
TaskProjectionCapability.requireService(host),
|
|
44
|
+
DispatchOutcomeCapability.resolveService(host)
|
|
45
|
+
]);
|
|
46
|
+
return {
|
|
47
|
+
taskProjection,
|
|
48
|
+
dispatchOutcome: dispatchOutcome ?? defaultCockpitCapabilities.dispatchOutcome
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
resolveCockpitCapabilities,
|
|
53
|
+
isRecord,
|
|
54
|
+
defaultCockpitCapabilities
|
|
55
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ExtensionUiComponent } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
+
import type { TUI } from "@oh-my-pi/pi-tui";
|
|
3
|
+
export declare class DronePreloaderComponent implements ExtensionUiComponent {
|
|
4
|
+
#private;
|
|
5
|
+
private readonly tui;
|
|
6
|
+
constructor(tui: TUI);
|
|
7
|
+
render(width: number): readonly string[];
|
|
8
|
+
handleInput(_data: string): void;
|
|
9
|
+
invalidate(): void;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/omp-extension-plugin/src/cockpit/drone-preloader.ts
|
|
3
|
+
var ART = [
|
|
4
|
+
" .-=-. .-=-. ",
|
|
5
|
+
" ( !!! ) ( !!! ) ",
|
|
6
|
+
" '-=-'._ _.'-=-' ",
|
|
7
|
+
" '._ _.' ",
|
|
8
|
+
" '=$$$$$$$=.' ",
|
|
9
|
+
" =$$$$$$$$$$$= ",
|
|
10
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
11
|
+
" $$$@@ @@$$$ ",
|
|
12
|
+
" $$@ ? @$$$ ",
|
|
13
|
+
" $$$@ '-' @$$$ ",
|
|
14
|
+
" $$$@@ @@$$$ ",
|
|
15
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
16
|
+
" =$$$$$$$$$$$= ",
|
|
17
|
+
" '=$$$$$$$=.' ",
|
|
18
|
+
" _.' '._ ",
|
|
19
|
+
" .-=-.' '.-=-. ",
|
|
20
|
+
" ( !!! ) ( !!! ) ",
|
|
21
|
+
" '-=-' '-=-' "
|
|
22
|
+
];
|
|
23
|
+
var MINI = [
|
|
24
|
+
"(!!!) (!!!)",
|
|
25
|
+
" \\%==%/ ",
|
|
26
|
+
" %%?%% ",
|
|
27
|
+
" /%==%\\ ",
|
|
28
|
+
"(!!!) (!!!)"
|
|
29
|
+
];
|
|
30
|
+
var BL = ["---", "\\\\\\", "|||", "///"];
|
|
31
|
+
var GW = 52;
|
|
32
|
+
var GH = 27;
|
|
33
|
+
var FLEET = [
|
|
34
|
+
{ s: MINI, x: 0, y: 2, amp: 1, sp: 0.05, ph: 0, dx: 1, dsp: 0.02 },
|
|
35
|
+
{ s: MINI, x: 39, y: 2, amp: 1, sp: 0.044, ph: 2.1, dx: 1, dsp: 0.017 },
|
|
36
|
+
{ s: MINI, x: 1, y: 20, amp: 1, sp: 0.048, ph: 4.2, dx: 1, dsp: 0.023 },
|
|
37
|
+
{ s: MINI, x: 38, y: 20, amp: 1, sp: 0.041, ph: 1.3, dx: 1, dsp: 0.015 },
|
|
38
|
+
{ s: ART, x: 10, y: 5, amp: 2, sp: 0.04, ph: 0, dx: 0, dsp: 0.011 }
|
|
39
|
+
];
|
|
40
|
+
var fg = (r, g, b) => (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
41
|
+
var COL = {
|
|
42
|
+
d: fg(204, 255, 77),
|
|
43
|
+
m: fg(169, 214, 63),
|
|
44
|
+
c: fg(86, 216, 255),
|
|
45
|
+
r: fg(95, 107, 58),
|
|
46
|
+
rb: fg(125, 158, 52),
|
|
47
|
+
eye: fg(234, 255, 176)
|
|
48
|
+
};
|
|
49
|
+
function classOf(ch) {
|
|
50
|
+
if (ch === "$" || ch === "@")
|
|
51
|
+
return "d";
|
|
52
|
+
if (ch === "=" || ch === "%")
|
|
53
|
+
return "m";
|
|
54
|
+
if (ch === "\\" || ch === "/")
|
|
55
|
+
return "r";
|
|
56
|
+
if (ch === "(" || ch === ")" || ch === "-" || ch === "|" || ch === "'" || ch === ".")
|
|
57
|
+
return "c";
|
|
58
|
+
return "b";
|
|
59
|
+
}
|
|
60
|
+
var GAP = "";
|
|
61
|
+
var SPACED_WIDTH = GW + GAP.length * (GW - 1);
|
|
62
|
+
var bolden = (f) => (t) => `\x1B[1m${f(t)}\x1B[22m`;
|
|
63
|
+
function colorFor(key) {
|
|
64
|
+
switch (key) {
|
|
65
|
+
case "d":
|
|
66
|
+
return bolden(COL.d);
|
|
67
|
+
case "m":
|
|
68
|
+
return COL.m;
|
|
69
|
+
case "c":
|
|
70
|
+
return COL.c;
|
|
71
|
+
case "r":
|
|
72
|
+
return COL.r;
|
|
73
|
+
case "rb":
|
|
74
|
+
return bolden(COL.rb);
|
|
75
|
+
case "eye":
|
|
76
|
+
return bolden(COL.eye);
|
|
77
|
+
default:
|
|
78
|
+
return (text) => text;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function composeGrid(t) {
|
|
82
|
+
const phase = Math.floor(t / 4);
|
|
83
|
+
const blade = BL[phase % 4];
|
|
84
|
+
const p = Math.sin(t * 0.07);
|
|
85
|
+
const core = p > 0.45 ? "@" : p > -0.1 ? "o" : ".";
|
|
86
|
+
const grid = Array.from({ length: GH }, () => Array.from({ length: GW }, () => " "));
|
|
87
|
+
for (const d of FLEET) {
|
|
88
|
+
const oy = d.y + Math.round(d.amp * Math.sin(t * d.sp + d.ph));
|
|
89
|
+
const ox = d.x + Math.round(d.dx * Math.sin(t * d.dsp + d.ph));
|
|
90
|
+
for (let i = 0;i < d.s.length; i++) {
|
|
91
|
+
const ln = d.s[i];
|
|
92
|
+
const ry = oy + i;
|
|
93
|
+
if (ry < 0 || ry >= GH)
|
|
94
|
+
continue;
|
|
95
|
+
for (let j = 0;j < ln.length; j++) {
|
|
96
|
+
const ch = ln[j];
|
|
97
|
+
if (ch === " ")
|
|
98
|
+
continue;
|
|
99
|
+
const rx = ox + j;
|
|
100
|
+
if (rx < 0 || rx >= GW)
|
|
101
|
+
continue;
|
|
102
|
+
grid[ry][rx] = ch;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const eyes = [];
|
|
107
|
+
const rows = grid.map((row, r) => {
|
|
108
|
+
const line = row.join("");
|
|
109
|
+
const cells = new Array(GW);
|
|
110
|
+
let k = 0;
|
|
111
|
+
while (k < GW) {
|
|
112
|
+
if (line.substr(k, 3) === "!!!") {
|
|
113
|
+
cells[k] = { ch: blade[0], key: "rb" };
|
|
114
|
+
cells[k + 1] = { ch: blade[1], key: "rb" };
|
|
115
|
+
cells[k + 2] = { ch: blade[2], key: "rb" };
|
|
116
|
+
k += 3;
|
|
117
|
+
} else if (line[k] === "?") {
|
|
118
|
+
eyes.push([r, k]);
|
|
119
|
+
cells[k] = { ch: core, key: "eye" };
|
|
120
|
+
k += 1;
|
|
121
|
+
} else {
|
|
122
|
+
cells[k] = { ch: line[k], key: classOf(line[k]) };
|
|
123
|
+
k += 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return cells;
|
|
127
|
+
});
|
|
128
|
+
return { rows, eyes };
|
|
129
|
+
}
|
|
130
|
+
var GLOW_RGB = [204, 255, 77];
|
|
131
|
+
var GLOW_RADIUS = 2;
|
|
132
|
+
var GLOW_SIGMA2 = 2 * 1.05 * 1.05;
|
|
133
|
+
var GLOW_MAX_BG = 0.15;
|
|
134
|
+
var GLOW_LEVELS = 10;
|
|
135
|
+
function buildGlow(eyes) {
|
|
136
|
+
const glow = Array.from({ length: GH }, () => new Array(GW).fill(0));
|
|
137
|
+
for (const [er, ec] of eyes) {
|
|
138
|
+
for (let r = Math.max(0, er - GLOW_RADIUS);r <= Math.min(GH - 1, er + GLOW_RADIUS); r++) {
|
|
139
|
+
for (let c = Math.max(0, ec - GLOW_RADIUS);c <= Math.min(GW - 1, ec + GLOW_RADIUS); c++) {
|
|
140
|
+
const d2 = (r - er) * (r - er) + (c - ec) * (c - ec);
|
|
141
|
+
if (d2 > GLOW_RADIUS * GLOW_RADIUS)
|
|
142
|
+
continue;
|
|
143
|
+
const v = Math.exp(-d2 / GLOW_SIGMA2);
|
|
144
|
+
if (v > glow[r][c])
|
|
145
|
+
glow[r][c] = v;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return glow;
|
|
150
|
+
}
|
|
151
|
+
function bgLevel(intensity) {
|
|
152
|
+
return Math.round(Math.min(1, Math.max(0, intensity)) * GLOW_LEVELS);
|
|
153
|
+
}
|
|
154
|
+
function bgOpen(level) {
|
|
155
|
+
const f = level / GLOW_LEVELS * GLOW_MAX_BG;
|
|
156
|
+
return `\x1B[48;2;${Math.round(GLOW_RGB[0] * f)};${Math.round(GLOW_RGB[1] * f)};${Math.round(GLOW_RGB[2] * f)}m`;
|
|
157
|
+
}
|
|
158
|
+
var BG_CLOSE = "\x1B[49m";
|
|
159
|
+
function renderRow(cells, glowRow) {
|
|
160
|
+
const outs = [];
|
|
161
|
+
for (let c = 0;c < GW; c++) {
|
|
162
|
+
outs.push({ ch: cells[c].ch, key: cells[c].key, bg: bgLevel(glowRow[c]) });
|
|
163
|
+
if (GAP && c < GW - 1)
|
|
164
|
+
outs.push({ ch: GAP, key: "b", bg: bgLevel(Math.max(glowRow[c], glowRow[c + 1])) });
|
|
165
|
+
}
|
|
166
|
+
let out = "";
|
|
167
|
+
let i = 0;
|
|
168
|
+
while (i < outs.length) {
|
|
169
|
+
const { key, bg } = outs[i];
|
|
170
|
+
let chunk = "";
|
|
171
|
+
while (i < outs.length && outs[i].key === key && outs[i].bg === bg) {
|
|
172
|
+
chunk += outs[i].ch;
|
|
173
|
+
i += 1;
|
|
174
|
+
}
|
|
175
|
+
out += bg > 0 ? bgOpen(bg) + colorFor(key)(chunk) + BG_CLOSE : colorFor(key)(chunk);
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
function composeFleet(t) {
|
|
180
|
+
const { rows, eyes } = composeGrid(t);
|
|
181
|
+
const glow = buildGlow(eyes);
|
|
182
|
+
return rows.map((cells, r) => renderRow(cells, glow[r]));
|
|
183
|
+
}
|
|
184
|
+
var ANIM_HZ = 120;
|
|
185
|
+
var RENDER_MS = 1000 / 60;
|
|
186
|
+
|
|
187
|
+
class DronePreloaderComponent {
|
|
188
|
+
tui;
|
|
189
|
+
#start = Date.now();
|
|
190
|
+
#timer;
|
|
191
|
+
constructor(tui) {
|
|
192
|
+
this.tui = tui;
|
|
193
|
+
this.#timer = setInterval(() => this.tui.requestRender(), Math.round(RENDER_MS));
|
|
194
|
+
}
|
|
195
|
+
render(width) {
|
|
196
|
+
const t = Math.round((Date.now() - this.#start) * ANIM_HZ / 1000);
|
|
197
|
+
const sprite = composeFleet(t);
|
|
198
|
+
const left = Math.max(0, Math.floor((width - SPACED_WIDTH) / 2));
|
|
199
|
+
const indent = " ".repeat(left);
|
|
200
|
+
const rows = typeof process.stdout.rows === "number" && process.stdout.rows > 0 ? process.stdout.rows : 0;
|
|
201
|
+
const topPad = rows > sprite.length ? Math.max(0, Math.floor((rows - sprite.length) / 2)) : 0;
|
|
202
|
+
const lines = [];
|
|
203
|
+
for (let i = 0;i < topPad; i++)
|
|
204
|
+
lines.push("");
|
|
205
|
+
for (const row of sprite)
|
|
206
|
+
lines.push(indent + row);
|
|
207
|
+
return lines;
|
|
208
|
+
}
|
|
209
|
+
handleInput(_data) {}
|
|
210
|
+
invalidate() {}
|
|
211
|
+
dispose() {
|
|
212
|
+
if (this.#timer) {
|
|
213
|
+
clearInterval(this.#timer);
|
|
214
|
+
this.#timer = undefined;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
export {
|
|
219
|
+
DronePreloaderComponent
|
|
220
|
+
};
|