@h-rig/rig-extension 0.0.6-alpha.91
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/board-theme.js +150 -0
- package/dist/src/board-views.js +475 -0
- package/dist/src/extension.js +3743 -0
- package/dist/src/workflow/identity.js +429 -0
- package/dist/src/workflow/projections.js +389 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @h-rig/rig-extension
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/rig-extension/src/board-theme.ts
|
|
3
|
+
var RIG_PALETTE = {
|
|
4
|
+
ink: "#f2f3f6",
|
|
5
|
+
ink2: "#aeb0ba",
|
|
6
|
+
ink3: "#6c6e79",
|
|
7
|
+
ink4: "#44464f",
|
|
8
|
+
accent: "#ccff4d",
|
|
9
|
+
accentDim: "#a9d63f",
|
|
10
|
+
cyan: "#56d8ff",
|
|
11
|
+
red: "#ff5d5d",
|
|
12
|
+
yellow: "#ffd24d"
|
|
13
|
+
};
|
|
14
|
+
function hexToRgb(hex) {
|
|
15
|
+
const value = hex.replace("#", "");
|
|
16
|
+
return [
|
|
17
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
18
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
19
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
function fg(hex) {
|
|
23
|
+
const [r, g, b] = hexToRgb(hex);
|
|
24
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
25
|
+
}
|
|
26
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
27
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
28
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
29
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
30
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
31
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
32
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
33
|
+
var red = fg(RIG_PALETTE.red);
|
|
34
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
35
|
+
function bold(text) {
|
|
36
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
37
|
+
}
|
|
38
|
+
function statusColor(status) {
|
|
39
|
+
switch (status) {
|
|
40
|
+
case "running":
|
|
41
|
+
return accent;
|
|
42
|
+
case "preparing":
|
|
43
|
+
case "created":
|
|
44
|
+
case "validating":
|
|
45
|
+
case "reviewing":
|
|
46
|
+
case "closing-out":
|
|
47
|
+
return cyan;
|
|
48
|
+
case "needs-attention":
|
|
49
|
+
case "needs_attention":
|
|
50
|
+
return yellow;
|
|
51
|
+
case "failed":
|
|
52
|
+
return red;
|
|
53
|
+
default:
|
|
54
|
+
return ink3;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var RIG_SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
58
|
+
var DRONE_ART = [
|
|
59
|
+
" .-=-. .-=-. ",
|
|
60
|
+
" ( !!! ) ( !!! ) ",
|
|
61
|
+
" '-=-'._ _.'-=-' ",
|
|
62
|
+
" '._ _.' ",
|
|
63
|
+
" '=$$$$$$$=.' ",
|
|
64
|
+
" =$$$$$$$$$$$= ",
|
|
65
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
66
|
+
" $$$@@ @@$$$ ",
|
|
67
|
+
" $$@ ? @$$$ ",
|
|
68
|
+
" $$$@ '-' @$$$ ",
|
|
69
|
+
" $$$@@ @@$$$ ",
|
|
70
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
71
|
+
" =$$$$$$$$$$$= ",
|
|
72
|
+
" '=$$$$$$$=.' ",
|
|
73
|
+
" _.' '._ ",
|
|
74
|
+
" .-=-.' '.-=-. ",
|
|
75
|
+
" ( !!! ) ( !!! ) ",
|
|
76
|
+
" '-=-' '-=-' "
|
|
77
|
+
];
|
|
78
|
+
var BLADE_FRAMES = ["---", "\\\\\\", "|||", "///"];
|
|
79
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
80
|
+
function droneCharColor(char) {
|
|
81
|
+
if (char === "$" || char === "@")
|
|
82
|
+
return accent;
|
|
83
|
+
if (char === "=" || char === "%")
|
|
84
|
+
return accentDim;
|
|
85
|
+
if (char === "\\" || char === "/")
|
|
86
|
+
return ink3;
|
|
87
|
+
return ink4;
|
|
88
|
+
}
|
|
89
|
+
function renderDroneFrame(tick) {
|
|
90
|
+
const blade = BLADE_FRAMES[Math.floor(tick / 4) % BLADE_FRAMES.length];
|
|
91
|
+
const pulse = Math.sin(tick * 0.07);
|
|
92
|
+
const eye = pulse > 0.45 ? EYE_FRAMES[0] : pulse > -0.1 ? EYE_FRAMES[1] : EYE_FRAMES[2];
|
|
93
|
+
return DRONE_ART.map((line) => {
|
|
94
|
+
let out = "";
|
|
95
|
+
let index = 0;
|
|
96
|
+
while (index < line.length) {
|
|
97
|
+
if (line.startsWith("!!!", index)) {
|
|
98
|
+
out += cyan(blade);
|
|
99
|
+
index += 3;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const char = line[index];
|
|
103
|
+
if (char === "?") {
|
|
104
|
+
out += bold(cyan(eye));
|
|
105
|
+
} else if (char !== " ") {
|
|
106
|
+
out += droneCharColor(char)(char);
|
|
107
|
+
} else {
|
|
108
|
+
out += " ";
|
|
109
|
+
}
|
|
110
|
+
index += 1;
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
116
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
117
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
118
|
+
function microDroneFrame(tick) {
|
|
119
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
120
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
121
|
+
return `(${blade})${eye}(${blade})`;
|
|
122
|
+
}
|
|
123
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
124
|
+
function renderMicroDroneFrame(tick) {
|
|
125
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
126
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
127
|
+
return `${ink4("(")}${cyan(blade)}${ink4(")")}${bold(accent(eye))}${ink4("(")}${cyan(blade)}${ink4(")")}`;
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
yellow,
|
|
131
|
+
statusColor,
|
|
132
|
+
renderMicroDroneFrame,
|
|
133
|
+
renderDroneFrame,
|
|
134
|
+
red,
|
|
135
|
+
microDroneFrame,
|
|
136
|
+
ink4,
|
|
137
|
+
ink3,
|
|
138
|
+
ink2,
|
|
139
|
+
ink,
|
|
140
|
+
fg,
|
|
141
|
+
cyan,
|
|
142
|
+
bold,
|
|
143
|
+
accentDim,
|
|
144
|
+
accent,
|
|
145
|
+
RIG_SPINNER_FRAMES,
|
|
146
|
+
RIG_PALETTE,
|
|
147
|
+
MICRO_DRONE_FRAMES,
|
|
148
|
+
DRONE_WIDTH,
|
|
149
|
+
DRONE_HEIGHT
|
|
150
|
+
};
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/rig-extension/src/board-views.ts
|
|
3
|
+
import { helpCatalog } from "@rig/contracts";
|
|
4
|
+
import { truncateToWidth } from "@oh-my-pi/pi-tui";
|
|
5
|
+
|
|
6
|
+
// packages/rig-extension/src/board-theme.ts
|
|
7
|
+
var RIG_PALETTE = {
|
|
8
|
+
ink: "#f2f3f6",
|
|
9
|
+
ink2: "#aeb0ba",
|
|
10
|
+
ink3: "#6c6e79",
|
|
11
|
+
ink4: "#44464f",
|
|
12
|
+
accent: "#ccff4d",
|
|
13
|
+
accentDim: "#a9d63f",
|
|
14
|
+
cyan: "#56d8ff",
|
|
15
|
+
red: "#ff5d5d",
|
|
16
|
+
yellow: "#ffd24d"
|
|
17
|
+
};
|
|
18
|
+
function hexToRgb(hex) {
|
|
19
|
+
const value = hex.replace("#", "");
|
|
20
|
+
return [
|
|
21
|
+
Number.parseInt(value.slice(0, 2), 16),
|
|
22
|
+
Number.parseInt(value.slice(2, 4), 16),
|
|
23
|
+
Number.parseInt(value.slice(4, 6), 16)
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
function fg(hex) {
|
|
27
|
+
const [r, g, b] = hexToRgb(hex);
|
|
28
|
+
return (text) => `\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`;
|
|
29
|
+
}
|
|
30
|
+
var ink = fg(RIG_PALETTE.ink);
|
|
31
|
+
var ink2 = fg(RIG_PALETTE.ink2);
|
|
32
|
+
var ink3 = fg(RIG_PALETTE.ink3);
|
|
33
|
+
var ink4 = fg(RIG_PALETTE.ink4);
|
|
34
|
+
var accent = fg(RIG_PALETTE.accent);
|
|
35
|
+
var accentDim = fg(RIG_PALETTE.accentDim);
|
|
36
|
+
var cyan = fg(RIG_PALETTE.cyan);
|
|
37
|
+
var red = fg(RIG_PALETTE.red);
|
|
38
|
+
var yellow = fg(RIG_PALETTE.yellow);
|
|
39
|
+
function bold(text) {
|
|
40
|
+
return `\x1B[1m${text}\x1B[22m`;
|
|
41
|
+
}
|
|
42
|
+
function statusColor(status) {
|
|
43
|
+
switch (status) {
|
|
44
|
+
case "running":
|
|
45
|
+
return accent;
|
|
46
|
+
case "preparing":
|
|
47
|
+
case "created":
|
|
48
|
+
case "validating":
|
|
49
|
+
case "reviewing":
|
|
50
|
+
case "closing-out":
|
|
51
|
+
return cyan;
|
|
52
|
+
case "needs-attention":
|
|
53
|
+
case "needs_attention":
|
|
54
|
+
return yellow;
|
|
55
|
+
case "failed":
|
|
56
|
+
return red;
|
|
57
|
+
default:
|
|
58
|
+
return ink3;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
var RIG_SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
62
|
+
var DRONE_ART = [
|
|
63
|
+
" .-=-. .-=-. ",
|
|
64
|
+
" ( !!! ) ( !!! ) ",
|
|
65
|
+
" '-=-'._ _.'-=-' ",
|
|
66
|
+
" '._ _.' ",
|
|
67
|
+
" '=$$$$$$$=.' ",
|
|
68
|
+
" =$$$$$$$$$$$= ",
|
|
69
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
70
|
+
" $$$@@ @@$$$ ",
|
|
71
|
+
" $$@ ? @$$$ ",
|
|
72
|
+
" $$$@ '-' @$$$ ",
|
|
73
|
+
" $$$@@ @@$$$ ",
|
|
74
|
+
" $$$@@@@@@@@@@$$$ ",
|
|
75
|
+
" =$$$$$$$$$$$= ",
|
|
76
|
+
" '=$$$$$$$=.' ",
|
|
77
|
+
" _.' '._ ",
|
|
78
|
+
" .-=-.' '.-=-. ",
|
|
79
|
+
" ( !!! ) ( !!! ) ",
|
|
80
|
+
" '-=-' '-=-' "
|
|
81
|
+
];
|
|
82
|
+
var BLADE_FRAMES = ["---", "\\\\\\", "|||", "///"];
|
|
83
|
+
var EYE_FRAMES = ["@", "o", "."];
|
|
84
|
+
function droneCharColor(char) {
|
|
85
|
+
if (char === "$" || char === "@")
|
|
86
|
+
return accent;
|
|
87
|
+
if (char === "=" || char === "%")
|
|
88
|
+
return accentDim;
|
|
89
|
+
if (char === "\\" || char === "/")
|
|
90
|
+
return ink3;
|
|
91
|
+
return ink4;
|
|
92
|
+
}
|
|
93
|
+
function renderDroneFrame(tick) {
|
|
94
|
+
const blade = BLADE_FRAMES[Math.floor(tick / 4) % BLADE_FRAMES.length];
|
|
95
|
+
const pulse = Math.sin(tick * 0.07);
|
|
96
|
+
const eye = pulse > 0.45 ? EYE_FRAMES[0] : pulse > -0.1 ? EYE_FRAMES[1] : EYE_FRAMES[2];
|
|
97
|
+
return DRONE_ART.map((line) => {
|
|
98
|
+
let out = "";
|
|
99
|
+
let index = 0;
|
|
100
|
+
while (index < line.length) {
|
|
101
|
+
if (line.startsWith("!!!", index)) {
|
|
102
|
+
out += cyan(blade);
|
|
103
|
+
index += 3;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const char = line[index];
|
|
107
|
+
if (char === "?") {
|
|
108
|
+
out += bold(cyan(eye));
|
|
109
|
+
} else if (char !== " ") {
|
|
110
|
+
out += droneCharColor(char)(char);
|
|
111
|
+
} else {
|
|
112
|
+
out += " ";
|
|
113
|
+
}
|
|
114
|
+
index += 1;
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
var DRONE_WIDTH = DRONE_ART[0].length;
|
|
120
|
+
var DRONE_HEIGHT = DRONE_ART.length;
|
|
121
|
+
var MICRO_BLADES = ["---", "\\\\\\", "|||", "///"];
|
|
122
|
+
function microDroneFrame(tick) {
|
|
123
|
+
const blade = MICRO_BLADES[tick % MICRO_BLADES.length];
|
|
124
|
+
const eye = EYE_FRAMES[Math.floor(tick / 2) % EYE_FRAMES.length];
|
|
125
|
+
return `(${blade})${eye}(${blade})`;
|
|
126
|
+
}
|
|
127
|
+
var MICRO_DRONE_FRAMES = Array.from({ length: 12 }, (_, index) => microDroneFrame(index));
|
|
128
|
+
|
|
129
|
+
// packages/rig-extension/src/board-views.ts
|
|
130
|
+
function hairline(width) {
|
|
131
|
+
return ink4("\u2500".repeat(Math.max(0, width)));
|
|
132
|
+
}
|
|
133
|
+
function statusRank(status) {
|
|
134
|
+
switch (status) {
|
|
135
|
+
case "needs-attention":
|
|
136
|
+
case "needs_attention":
|
|
137
|
+
return 0;
|
|
138
|
+
case "running":
|
|
139
|
+
return 1;
|
|
140
|
+
case "pending":
|
|
141
|
+
case "preparing":
|
|
142
|
+
case "adopted":
|
|
143
|
+
return 2;
|
|
144
|
+
case "completed":
|
|
145
|
+
case "merged":
|
|
146
|
+
return 3;
|
|
147
|
+
case "failed":
|
|
148
|
+
return 4;
|
|
149
|
+
default:
|
|
150
|
+
return 5;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class RunsList {
|
|
155
|
+
runs = [];
|
|
156
|
+
selected = 0;
|
|
157
|
+
loading = true;
|
|
158
|
+
noProject = false;
|
|
159
|
+
tick = 0;
|
|
160
|
+
filter = "";
|
|
161
|
+
sortMode = "active";
|
|
162
|
+
visibleRuns() {
|
|
163
|
+
const query = this.filter.trim().toLowerCase();
|
|
164
|
+
const filtered = query ? this.runs.filter((run) => run.runId.toLowerCase().includes(query) || run.status.toLowerCase().includes(query) || run.title.toLowerCase().includes(query)) : [...this.runs];
|
|
165
|
+
if (this.sortMode === "active") {
|
|
166
|
+
return filtered.map((run, index) => ({ run, index })).sort((a, b) => statusRank(a.run.status) - statusRank(b.run.status) || a.index - b.index).map((entry) => entry.run);
|
|
167
|
+
}
|
|
168
|
+
if (this.sortMode === "status") {
|
|
169
|
+
return filtered.map((run, index) => ({ run, index })).sort((a, b) => a.run.status.localeCompare(b.run.status) || a.index - b.index).map((entry) => entry.run);
|
|
170
|
+
}
|
|
171
|
+
return filtered;
|
|
172
|
+
}
|
|
173
|
+
moveSelection(delta) {
|
|
174
|
+
const visible = this.visibleRuns();
|
|
175
|
+
if (visible.length === 0)
|
|
176
|
+
return;
|
|
177
|
+
this.selected = Math.max(0, Math.min(visible.length - 1, this.selected + delta));
|
|
178
|
+
}
|
|
179
|
+
selectedRun() {
|
|
180
|
+
return this.visibleRuns()[this.selected] ?? null;
|
|
181
|
+
}
|
|
182
|
+
cycleSort() {
|
|
183
|
+
this.sortMode = this.sortMode === "active" ? "recent" : this.sortMode === "recent" ? "status" : "active";
|
|
184
|
+
this.clampSelection();
|
|
185
|
+
return this.sortMode;
|
|
186
|
+
}
|
|
187
|
+
setFilter(filter) {
|
|
188
|
+
this.filter = filter;
|
|
189
|
+
this.clampSelection();
|
|
190
|
+
}
|
|
191
|
+
clampSelection() {
|
|
192
|
+
this.selected = Math.max(0, Math.min(this.selected, Math.max(0, this.visibleRuns().length - 1)));
|
|
193
|
+
}
|
|
194
|
+
setRuns(runs) {
|
|
195
|
+
const previous = this.selectedRun()?.runId;
|
|
196
|
+
this.runs = runs;
|
|
197
|
+
if (previous) {
|
|
198
|
+
const index = this.visibleRuns().findIndex((run) => run.runId === previous);
|
|
199
|
+
if (index >= 0)
|
|
200
|
+
this.selected = index;
|
|
201
|
+
}
|
|
202
|
+
this.clampSelection();
|
|
203
|
+
this.loading = false;
|
|
204
|
+
}
|
|
205
|
+
invalidate() {}
|
|
206
|
+
render(width) {
|
|
207
|
+
return this.renderLines(width).map((line) => truncateToWidth(line, Math.max(10, width - 1)));
|
|
208
|
+
}
|
|
209
|
+
renderLines(width) {
|
|
210
|
+
if (this.loading) {
|
|
211
|
+
const frame = renderDroneFrame(this.tick);
|
|
212
|
+
const spinner = RIG_SPINNER_FRAMES[this.tick % RIG_SPINNER_FRAMES.length];
|
|
213
|
+
const pad = Math.max(0, Math.floor((width - 34) / 2));
|
|
214
|
+
return [
|
|
215
|
+
"",
|
|
216
|
+
...frame.map((line) => " ".repeat(pad) + line),
|
|
217
|
+
"",
|
|
218
|
+
" ".repeat(Math.max(0, Math.floor((width - 24) / 2))) + accent(spinner) + ink3(" contacting the fleet\u2026"),
|
|
219
|
+
""
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
if (this.noProject) {
|
|
223
|
+
const frame = renderDroneFrame(this.tick);
|
|
224
|
+
const pad = Math.max(0, Math.floor((width - 34) / 2));
|
|
225
|
+
const center = (text, visible2) => " ".repeat(Math.max(0, Math.floor((width - visible2) / 2))) + text;
|
|
226
|
+
return [
|
|
227
|
+
"",
|
|
228
|
+
...frame.map((line) => " ".repeat(pad) + line),
|
|
229
|
+
"",
|
|
230
|
+
center(ink2("no rig project in this directory"), 32),
|
|
231
|
+
"",
|
|
232
|
+
center(`${accent("rig init")}${ink2(" set this repo up: config, GitHub auth, task source, server, Pi")}`, 80),
|
|
233
|
+
center(`${accent("rig init --yes")}${ink2(" same, non-interactive, sensible defaults")}`, 60),
|
|
234
|
+
center(`${accent("rig doctor")}${ink2(" already initialized somewhere else? check the wiring")}`, 70),
|
|
235
|
+
"",
|
|
236
|
+
center(ink3("after init: rig task run --next puts a drone on your next task"), 62),
|
|
237
|
+
""
|
|
238
|
+
];
|
|
239
|
+
}
|
|
240
|
+
const visible = this.visibleRuns();
|
|
241
|
+
if (this.runs.length === 0) {
|
|
242
|
+
const frame = renderDroneFrame(this.tick);
|
|
243
|
+
const pad = Math.max(0, Math.floor((width - 34) / 2));
|
|
244
|
+
return [
|
|
245
|
+
"",
|
|
246
|
+
...frame.map((line) => " ".repeat(pad) + line),
|
|
247
|
+
"",
|
|
248
|
+
" ".repeat(Math.max(0, Math.floor((width - 44) / 2))) + ink2("no runs yet \u2014 ") + accent("n") + ink2(" picks a task and launches one"),
|
|
249
|
+
""
|
|
250
|
+
];
|
|
251
|
+
}
|
|
252
|
+
if (visible.length === 0) {
|
|
253
|
+
return ["", ` ${ink2("no runs match")} ${accent(`/${this.filter}`)} ${ink3("\u2014 esc clears the filter")}`, ""];
|
|
254
|
+
}
|
|
255
|
+
const maxVisible = 16;
|
|
256
|
+
const start = Math.max(0, Math.min(this.selected - Math.floor(maxVisible / 2), visible.length - maxVisible));
|
|
257
|
+
const window = visible.slice(start, start + maxVisible);
|
|
258
|
+
const lines = window.map((run, index) => {
|
|
259
|
+
const absolute = start + index;
|
|
260
|
+
const isSelected = absolute === this.selected;
|
|
261
|
+
const dot = statusColor(run.status)(isSelected ? "\u25CF" : "\xB7");
|
|
262
|
+
const id = run.runId.slice(0, 8);
|
|
263
|
+
const status = statusColor(run.status)(run.status.padEnd(16));
|
|
264
|
+
const title = truncateToWidth(run.title, Math.max(8, width - 36));
|
|
265
|
+
const row = ` ${dot} ${isSelected ? bold(ink(id)) : ink3(id)} ${status} ${isSelected ? ink(title) : ink2(title)}`;
|
|
266
|
+
return isSelected ? accent("\u258C") + row : " " + row;
|
|
267
|
+
});
|
|
268
|
+
const meta = [];
|
|
269
|
+
if (visible.length > maxVisible)
|
|
270
|
+
meta.push(`${this.selected + 1}/${visible.length}`);
|
|
271
|
+
if (this.filter)
|
|
272
|
+
meta.push(`filter: ${this.filter}`);
|
|
273
|
+
meta.push(`sort: ${this.sortMode}`);
|
|
274
|
+
lines.push(ink4(` ${meta.join(" \xB7 ")}`));
|
|
275
|
+
return ["", ...lines, ""];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
class TasksView {
|
|
280
|
+
tasks = [];
|
|
281
|
+
selected = 0;
|
|
282
|
+
loading = true;
|
|
283
|
+
error = null;
|
|
284
|
+
tick = 0;
|
|
285
|
+
setTasks(tasks) {
|
|
286
|
+
this.tasks = tasks;
|
|
287
|
+
this.selected = Math.min(this.selected, Math.max(0, tasks.length - 1));
|
|
288
|
+
this.loading = false;
|
|
289
|
+
this.error = null;
|
|
290
|
+
}
|
|
291
|
+
moveSelection(delta) {
|
|
292
|
+
if (this.tasks.length === 0)
|
|
293
|
+
return;
|
|
294
|
+
this.selected = Math.max(0, Math.min(this.tasks.length - 1, this.selected + delta));
|
|
295
|
+
}
|
|
296
|
+
selectedTask() {
|
|
297
|
+
return this.tasks[this.selected] ?? null;
|
|
298
|
+
}
|
|
299
|
+
invalidate() {}
|
|
300
|
+
render(width) {
|
|
301
|
+
return this.renderLines(width).map((line) => truncateToWidth(line, Math.max(10, width - 1)));
|
|
302
|
+
}
|
|
303
|
+
renderLines(width) {
|
|
304
|
+
if (this.loading) {
|
|
305
|
+
const spinner = RIG_SPINNER_FRAMES[this.tick % RIG_SPINNER_FRAMES.length];
|
|
306
|
+
return ["", ` ${accent(spinner)} ${ink3("reading tasks from the server\u2026")}`, ""];
|
|
307
|
+
}
|
|
308
|
+
if (this.error) {
|
|
309
|
+
return ["", ` ${red("tasks unavailable:")} ${ink2(this.error)}`, ""];
|
|
310
|
+
}
|
|
311
|
+
if (this.tasks.length === 0) {
|
|
312
|
+
return ["", ` ${ink2("no ready tasks \u2014 the task source is drained")}`, ""];
|
|
313
|
+
}
|
|
314
|
+
const maxVisible = 16;
|
|
315
|
+
const start = Math.max(0, Math.min(this.selected - Math.floor(maxVisible / 2), this.tasks.length - maxVisible));
|
|
316
|
+
const window = this.tasks.slice(start, start + maxVisible);
|
|
317
|
+
const lines = window.map((task, index) => {
|
|
318
|
+
const absolute = start + index;
|
|
319
|
+
const isSelected = absolute === this.selected;
|
|
320
|
+
const id = task.id.padEnd(8).slice(0, 12);
|
|
321
|
+
const status = statusColor(task.status)(task.status.padEnd(12));
|
|
322
|
+
const title = truncateToWidth(task.title, Math.max(8, width - 32));
|
|
323
|
+
const row = ` ${isSelected ? bold(ink(id)) : ink3(id)} ${status} ${isSelected ? ink(title) : ink2(title)}`;
|
|
324
|
+
return isSelected ? accent("\u258C") + row : " " + row;
|
|
325
|
+
});
|
|
326
|
+
if (this.tasks.length > maxVisible)
|
|
327
|
+
lines.push(ink4(` ${this.selected + 1}/${this.tasks.length}`));
|
|
328
|
+
return ["", ...lines, ""];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
class InboxView {
|
|
333
|
+
items = [];
|
|
334
|
+
selected = 0;
|
|
335
|
+
loading = true;
|
|
336
|
+
error = null;
|
|
337
|
+
tick = 0;
|
|
338
|
+
setItems(items) {
|
|
339
|
+
this.items = items;
|
|
340
|
+
this.selected = Math.min(this.selected, Math.max(0, items.length - 1));
|
|
341
|
+
this.loading = false;
|
|
342
|
+
this.error = null;
|
|
343
|
+
}
|
|
344
|
+
moveSelection(delta) {
|
|
345
|
+
if (this.items.length === 0)
|
|
346
|
+
return;
|
|
347
|
+
this.selected = Math.max(0, Math.min(this.items.length - 1, this.selected + delta));
|
|
348
|
+
}
|
|
349
|
+
selectedItem() {
|
|
350
|
+
return this.items[this.selected] ?? null;
|
|
351
|
+
}
|
|
352
|
+
invalidate() {}
|
|
353
|
+
render(width) {
|
|
354
|
+
return this.renderLines(width).map((line) => truncateToWidth(line, Math.max(10, width - 1)));
|
|
355
|
+
}
|
|
356
|
+
renderLines(width) {
|
|
357
|
+
if (this.loading) {
|
|
358
|
+
const spinner = RIG_SPINNER_FRAMES[this.tick % RIG_SPINNER_FRAMES.length];
|
|
359
|
+
return ["", ` ${accent(spinner)} ${ink3("reading the inbox\u2026")}`, ""];
|
|
360
|
+
}
|
|
361
|
+
if (this.error) {
|
|
362
|
+
return ["", ` ${red("inbox unavailable:")} ${ink2(this.error)}`, ""];
|
|
363
|
+
}
|
|
364
|
+
if (this.items.length === 0) {
|
|
365
|
+
return ["", ` ${ink2("inbox clear \u2014 no drone is waiting on you")}`, ""];
|
|
366
|
+
}
|
|
367
|
+
const maxVisible = 14;
|
|
368
|
+
const start = Math.max(0, Math.min(this.selected - Math.floor(maxVisible / 2), this.items.length - maxVisible));
|
|
369
|
+
const window = this.items.slice(start, start + maxVisible);
|
|
370
|
+
const lines = window.flatMap((item, index) => {
|
|
371
|
+
const absolute = start + index;
|
|
372
|
+
const isSelected = absolute === this.selected;
|
|
373
|
+
const kind = item.kind === "approval" ? accentDim("approval") : cyan("input ");
|
|
374
|
+
const run = item.runId.slice(0, 8);
|
|
375
|
+
const summary = truncateToWidth(item.summary, Math.max(8, width - 30));
|
|
376
|
+
const row = ` ${kind} ${isSelected ? bold(ink(run)) : ink3(run)} ${isSelected ? ink(summary) : ink2(summary)}`;
|
|
377
|
+
const main = isSelected ? accent("\u258C") + row : " " + row;
|
|
378
|
+
if (isSelected && item.kind === "input" && item.options.length > 0) {
|
|
379
|
+
return [main, ink4(` options: ${item.options.join(" \xB7 ")}`)];
|
|
380
|
+
}
|
|
381
|
+
return [main];
|
|
382
|
+
});
|
|
383
|
+
if (this.items.length > maxVisible)
|
|
384
|
+
lines.push(ink4(` ${this.selected + 1}/${this.items.length}`));
|
|
385
|
+
return ["", ...lines, ""];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
class Rule {
|
|
390
|
+
invalidate() {}
|
|
391
|
+
render(width) {
|
|
392
|
+
return [hairline(width)];
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
class HelpView {
|
|
397
|
+
group;
|
|
398
|
+
offset = 0;
|
|
399
|
+
#built = null;
|
|
400
|
+
constructor(group) {
|
|
401
|
+
this.group = group;
|
|
402
|
+
}
|
|
403
|
+
#build(width) {
|
|
404
|
+
if (this.#built)
|
|
405
|
+
return this.#built;
|
|
406
|
+
const { sections, groups } = helpCatalog();
|
|
407
|
+
const lines = [];
|
|
408
|
+
const groupAnchors = new Map;
|
|
409
|
+
const columnWidth = Math.min(38, Math.max(24, Math.floor(width * 0.4)));
|
|
410
|
+
const row = (command, description) => {
|
|
411
|
+
const left = command.length >= columnWidth ? `${command} ` : command.padEnd(columnWidth);
|
|
412
|
+
return ` ${bold(ink(left))} ${ink2(truncateToWidth(description, Math.max(16, width - columnWidth - 4)))}`;
|
|
413
|
+
};
|
|
414
|
+
for (const section of sections) {
|
|
415
|
+
lines.push(`${accent("\u25C7")} ${bold(ink(section.title))} ${ink3(`\u2014 ${section.subtitle}`)}`);
|
|
416
|
+
for (const command of section.commands)
|
|
417
|
+
lines.push(row(command.command, command.description));
|
|
418
|
+
lines.push("");
|
|
419
|
+
}
|
|
420
|
+
lines.push(`${accent("\u25C7")} ${bold(ink("command groups"))} ${ink3("\u2014 every `rig <group>` surface")}`);
|
|
421
|
+
lines.push("");
|
|
422
|
+
for (const group of groups) {
|
|
423
|
+
groupAnchors.set(group.name, lines.length);
|
|
424
|
+
lines.push(`${accentDim("\u258D")}${bold(ink(`rig ${group.name}`))} ${ink3(`\u2014 ${group.summary}`)}`);
|
|
425
|
+
for (const usage of group.usage)
|
|
426
|
+
lines.push(` ${cyan(usage)}`);
|
|
427
|
+
for (const command of group.commands)
|
|
428
|
+
lines.push(row(command.command, command.description));
|
|
429
|
+
if (group.examples?.length) {
|
|
430
|
+
for (const example of group.examples)
|
|
431
|
+
lines.push(` ${ink4("$")} ${ink2(example)}`);
|
|
432
|
+
}
|
|
433
|
+
if (group.next?.length) {
|
|
434
|
+
for (const next of group.next)
|
|
435
|
+
lines.push(` ${accent("\u203A")} ${ink3(next)}`);
|
|
436
|
+
}
|
|
437
|
+
lines.push("");
|
|
438
|
+
}
|
|
439
|
+
this.#built = { lines, groupAnchors };
|
|
440
|
+
if (this.group) {
|
|
441
|
+
const anchor = groupAnchors.get(this.group);
|
|
442
|
+
if (anchor !== undefined)
|
|
443
|
+
this.offset = anchor;
|
|
444
|
+
}
|
|
445
|
+
return this.#built;
|
|
446
|
+
}
|
|
447
|
+
scroll(delta) {
|
|
448
|
+
if (!this.#built)
|
|
449
|
+
return;
|
|
450
|
+
const max = Math.max(0, this.#built.lines.length - 10);
|
|
451
|
+
this.offset = Math.max(0, Math.min(max, this.offset + delta));
|
|
452
|
+
}
|
|
453
|
+
invalidate() {
|
|
454
|
+
this.#built = null;
|
|
455
|
+
}
|
|
456
|
+
render(width) {
|
|
457
|
+
const { lines } = this.#build(width);
|
|
458
|
+
const viewport = Math.max(10, (process.stdout.rows ?? 30) - 8);
|
|
459
|
+
const slice = lines.slice(this.offset, this.offset + viewport);
|
|
460
|
+
const more = this.offset + viewport < lines.length;
|
|
461
|
+
return [
|
|
462
|
+
"",
|
|
463
|
+
...slice.map((line) => truncateToWidth(` ${line}`, Math.max(10, width - 1))),
|
|
464
|
+
more ? ink4(` \u2026 ${lines.length - this.offset - viewport} more (\u2193)`) : ""
|
|
465
|
+
];
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
export {
|
|
469
|
+
hairline,
|
|
470
|
+
TasksView,
|
|
471
|
+
RunsList,
|
|
472
|
+
Rule,
|
|
473
|
+
InboxView,
|
|
474
|
+
HelpView
|
|
475
|
+
};
|