@h-rig/cli 0.0.6-alpha.83 → 0.0.6-alpha.84
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/dist/bin/rig.js +22085 -14618
- package/dist/src/app/board.js +155 -28
- package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
- package/dist/src/app-opentui/adapters/doctor.js +1 -25
- package/dist/src/app-opentui/adapters/family.d.ts +62 -0
- package/dist/src/app-opentui/adapters/family.js +14305 -0
- package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
- package/dist/src/app-opentui/adapters/fleet.js +6 -40
- package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
- package/dist/src/app-opentui/adapters/inbox.js +54 -59
- package/dist/src/app-opentui/adapters/init.d.ts +0 -2
- package/dist/src/app-opentui/adapters/init.js +0 -26
- package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
- package/dist/src/app-opentui/adapters/inspect.js +1024 -0
- package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
- package/dist/src/app-opentui/adapters/pi-attach.js +348 -93
- package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
- package/dist/src/app-opentui/adapters/pi.js +363 -0
- package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
- package/dist/src/app-opentui/adapters/plugin.js +544 -0
- package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
- package/dist/src/app-opentui/adapters/repo.js +186 -0
- package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
- package/dist/src/app-opentui/adapters/run-detail.js +97 -44
- package/dist/src/app-opentui/adapters/server.d.ts +10 -2
- package/dist/src/app-opentui/adapters/server.js +96 -27
- package/dist/src/app-opentui/adapters/tasks.d.ts +0 -2
- package/dist/src/app-opentui/adapters/tasks.js +437 -105
- package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
- package/dist/src/app-opentui/adapters/workspace.js +333 -0
- package/dist/src/app-opentui/autocomplete.d.ts +20 -0
- package/dist/src/app-opentui/autocomplete.js +576 -0
- package/dist/src/app-opentui/bootstrap.js +24296 -16919
- package/dist/src/app-opentui/command-palette.d.ts +3 -0
- package/dist/src/app-opentui/command-palette.js +1010 -0
- package/dist/src/app-opentui/drone.js +8 -6
- package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
- package/dist/src/app-opentui/fleet-stats.js +114 -0
- package/dist/src/app-opentui/index.js +3438 -1724
- package/dist/src/app-opentui/intent.js +71 -48
- package/dist/src/app-opentui/keymap.d.ts +6 -5
- package/dist/src/app-opentui/keymap.js +1064 -23
- package/dist/src/app-opentui/layout.d.ts +7 -0
- package/dist/src/app-opentui/layout.js +13 -6
- package/dist/src/app-opentui/list-search.d.ts +24 -0
- package/dist/src/app-opentui/list-search.js +88 -1
- package/dist/src/app-opentui/pi-host-child.js +33 -1
- package/dist/src/app-opentui/pi-pty-host.d.ts +5 -0
- package/dist/src/app-opentui/pi-pty-host.js +21 -3
- package/dist/src/app-opentui/react/App.d.ts +9 -0
- package/dist/src/app-opentui/react/App.js +5144 -0
- package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
- package/dist/src/app-opentui/react/Backdrop.js +1834 -0
- package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
- package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
- package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
- package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
- package/dist/src/app-opentui/react/context.d.ts +17 -0
- package/dist/src/app-opentui/react/context.js +37 -0
- package/dist/src/app-opentui/react/launch.d.ts +2 -0
- package/dist/src/app-opentui/react/launch.js +5743 -0
- package/dist/src/app-opentui/react/nav.d.ts +18 -0
- package/dist/src/app-opentui/react/nav.js +54 -0
- package/dist/src/app-opentui/react/scroll.d.ts +12 -0
- package/dist/src/app-opentui/react/scroll.js +21 -0
- package/dist/src/app-opentui/react/syntax.d.ts +2 -0
- package/dist/src/app-opentui/react/syntax.js +64 -0
- package/dist/src/app-opentui/registry.js +19923 -5151
- package/dist/src/app-opentui/render/constants.d.ts +31 -0
- package/dist/src/app-opentui/render/constants.js +66 -0
- package/dist/src/app-opentui/render/graphics.d.ts +2 -1
- package/dist/src/app-opentui/render/graphics.js +106 -39
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +108 -20
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +7 -0
- package/dist/src/app-opentui/render/image-visual-layer.js +109 -20
- package/dist/src/app-opentui/render/native-host.d.ts +37 -0
- package/dist/src/app-opentui/render/native-host.js +179 -0
- package/dist/src/app-opentui/render/panels.js +18 -11
- package/dist/src/app-opentui/render/preloader.js +8 -6
- package/dist/src/app-opentui/render/scene.d.ts +50 -1
- package/dist/src/app-opentui/render/scene.js +183 -6
- package/dist/src/app-opentui/render/text.d.ts +7 -1
- package/dist/src/app-opentui/render/text.js +10 -7
- package/dist/src/app-opentui/render/type-bar.js +69 -30
- package/dist/src/app-opentui/runtime.d.ts +44 -1
- package/dist/src/app-opentui/runtime.js +3231 -1363
- package/dist/src/app-opentui/scenes/command.js +20 -6
- package/dist/src/app-opentui/scenes/doctor.js +176 -11
- package/dist/src/app-opentui/scenes/error.js +20 -10
- package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
- package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
- package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
- package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
- package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
- package/dist/src/app-opentui/scenes/family.d.ts +3 -0
- package/dist/src/app-opentui/scenes/family.js +2144 -0
- package/dist/src/app-opentui/scenes/fleet.js +521 -31
- package/dist/src/app-opentui/scenes/handoff.js +204 -12
- package/dist/src/app-opentui/scenes/help.js +609 -40
- package/dist/src/app-opentui/scenes/inbox.js +278 -17
- package/dist/src/app-opentui/scenes/init.js +84 -39
- package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
- package/dist/src/app-opentui/scenes/inspect.js +793 -0
- package/dist/src/app-opentui/scenes/main.js +218 -39
- package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
- package/dist/src/app-opentui/scenes/pi.js +508 -0
- package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
- package/dist/src/app-opentui/scenes/plugin.js +486 -0
- package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
- package/dist/src/app-opentui/scenes/repo.js +424 -0
- package/dist/src/app-opentui/scenes/run-detail.js +333 -20
- package/dist/src/app-opentui/scenes/server.js +181 -15
- package/dist/src/app-opentui/scenes/tasks.js +486 -31
- package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
- package/dist/src/app-opentui/scenes/workspace.js +426 -0
- package/dist/src/app-opentui/selectable.d.ts +19 -0
- package/dist/src/app-opentui/selectable.js +79 -0
- package/dist/src/app-opentui/state.js +83 -30
- package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
- package/dist/src/app-opentui/surface-catalog.js +540 -0
- package/dist/src/app-opentui/theme.d.ts +28 -6
- package/dist/src/app-opentui/theme.js +61 -8
- package/dist/src/app-opentui/types.d.ts +121 -4
- package/dist/src/commands/_authority-runs.d.ts +1 -1
- package/dist/src/commands/_authority-runs.js +2 -12
- package/dist/src/commands/_help-catalog.js +95 -15
- package/dist/src/commands/_operator-view.js +34 -2
- package/dist/src/commands/_pi-frontend.d.ts +2 -0
- package/dist/src/commands/_pi-frontend.js +34 -0
- package/dist/src/commands/_server-events.d.ts +26 -0
- package/dist/src/commands/_server-events.js +310 -0
- package/dist/src/commands/agent.js +2 -12
- package/dist/src/commands/run.js +34 -2
- package/dist/src/commands/server.js +2 -12
- package/dist/src/commands/stats.js +95 -15
- package/dist/src/commands/task-run-driver.js +2 -12
- package/dist/src/commands/task.js +131 -29
- package/dist/src/commands.js +192 -31
- package/dist/src/index.js +192 -31
- package/package.json +11 -9
|
@@ -11,15 +11,17 @@ var RIG_UI = {
|
|
|
11
11
|
bg: "#070809",
|
|
12
12
|
bg2: "#0b0c0e",
|
|
13
13
|
panel: "#101115",
|
|
14
|
-
panel2: "#
|
|
15
|
-
glass: "#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
panel2: "#14161b",
|
|
15
|
+
glass: "#1e2230",
|
|
16
|
+
border: "#2a2e3a",
|
|
17
|
+
ink: "#f4f5f8",
|
|
18
|
+
ink2: "#c7c9d3",
|
|
19
|
+
ink3: "#9aa0ae",
|
|
20
|
+
ink4: "#787b86",
|
|
20
21
|
lime: "#ccff4d",
|
|
21
22
|
limeDim: "#a9d63f",
|
|
22
23
|
cyan: "#56d8ff",
|
|
24
|
+
cyanDim: "#3f9fbd",
|
|
23
25
|
red: "#ff5d5d",
|
|
24
26
|
yellow: "#ffd24d",
|
|
25
27
|
magenta: "#ff79b0"
|
|
@@ -38,11 +40,11 @@ var styles = {
|
|
|
38
40
|
};
|
|
39
41
|
function statusColor(status) {
|
|
40
42
|
switch (status) {
|
|
41
|
-
case "running":
|
|
42
43
|
case "success":
|
|
43
44
|
case "completed":
|
|
44
45
|
case "merged":
|
|
45
46
|
return RIG_UI.lime;
|
|
47
|
+
case "running":
|
|
46
48
|
case "preparing":
|
|
47
49
|
case "created":
|
|
48
50
|
case "validating":
|
|
@@ -62,11 +64,182 @@ function statusColor(status) {
|
|
|
62
64
|
return RIG_UI.ink3;
|
|
63
65
|
}
|
|
64
66
|
}
|
|
67
|
+
function statusGlyph(status) {
|
|
68
|
+
switch (status) {
|
|
69
|
+
case "success":
|
|
70
|
+
case "completed":
|
|
71
|
+
case "merged":
|
|
72
|
+
return "\u2713";
|
|
73
|
+
case "running":
|
|
74
|
+
return "\u25CF";
|
|
75
|
+
case "preparing":
|
|
76
|
+
case "created":
|
|
77
|
+
case "validating":
|
|
78
|
+
case "reviewing":
|
|
79
|
+
case "closing-out":
|
|
80
|
+
case "stopping":
|
|
81
|
+
case "loading":
|
|
82
|
+
case "action":
|
|
83
|
+
return "\u25D0";
|
|
84
|
+
case "needs-attention":
|
|
85
|
+
case "needs_attention":
|
|
86
|
+
return "\u26A0";
|
|
87
|
+
case "failed":
|
|
88
|
+
case "error":
|
|
89
|
+
return "\u2717";
|
|
90
|
+
default:
|
|
91
|
+
return "\xB7";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function noColorActive() {
|
|
95
|
+
const noColor = process.env.NO_COLOR;
|
|
96
|
+
if (noColor !== undefined && noColor !== "") {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return process.env.RIG_NO_COLOR !== undefined;
|
|
100
|
+
}
|
|
101
|
+
function statusLabel(status) {
|
|
102
|
+
return {
|
|
103
|
+
glyph: statusGlyph(status),
|
|
104
|
+
color: noColorActive() ? RIG_UI.ink : statusColor(status)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// packages/cli/src/app-opentui/drone.ts
|
|
109
|
+
import { RGBA, StyledText, TextAttributes as TextAttributes2 } from "@opentui/core";
|
|
110
|
+
var MINI_DRONE = [
|
|
111
|
+
"(!!!) (!!!)",
|
|
112
|
+
" \\%==%/ ",
|
|
113
|
+
" %%?%% ",
|
|
114
|
+
" /%==%\\ ",
|
|
115
|
+
"(!!!) (!!!)"
|
|
116
|
+
];
|
|
117
|
+
var LEAD_MARK = [
|
|
118
|
+
" .-=-. ",
|
|
119
|
+
"=%%?%%=",
|
|
120
|
+
" '-=-' "
|
|
121
|
+
];
|
|
122
|
+
var BLADE_FRAMES = ["---", "\\\\\\", "|||", "///"];
|
|
123
|
+
var EYE_FRAMES = ["o", "@", "\u2022", "."];
|
|
124
|
+
var COLOR = {
|
|
125
|
+
body: RGBA.fromHex(RIG_UI.lime),
|
|
126
|
+
mini: RGBA.fromHex(RIG_UI.limeDim),
|
|
127
|
+
rotor: RGBA.fromHex(RIG_UI.cyan),
|
|
128
|
+
path: RGBA.fromHex(RIG_UI.cyan),
|
|
129
|
+
eye: RGBA.fromHex(RIG_UI.ink),
|
|
130
|
+
dim: RGBA.fromHex(RIG_UI.ink4),
|
|
131
|
+
ink: RGBA.fromHex(RIG_UI.ink2)
|
|
132
|
+
};
|
|
133
|
+
function bladeForTick(tick, phase = 0) {
|
|
134
|
+
return BLADE_FRAMES[(Math.floor(tick / 3) + phase) % BLADE_FRAMES.length];
|
|
135
|
+
}
|
|
136
|
+
function eyeForTick(tick, phase = 0) {
|
|
137
|
+
const pulse = Math.sin(tick * 0.09 + phase);
|
|
138
|
+
return pulse > 0.55 ? EYE_FRAMES[1] : pulse > 0.1 ? EYE_FRAMES[0] : pulse > -0.45 ? EYE_FRAMES[2] : EYE_FRAMES[3];
|
|
139
|
+
}
|
|
140
|
+
function chunk(text, fg, bold = false, dim = false) {
|
|
141
|
+
let attributes = TextAttributes2.NONE;
|
|
142
|
+
if (bold)
|
|
143
|
+
attributes |= TextAttributes2.BOLD;
|
|
144
|
+
if (dim)
|
|
145
|
+
attributes |= TextAttributes2.DIM;
|
|
146
|
+
return { __isChunk: true, text, fg, ...attributes !== TextAttributes2.NONE ? { attributes } : {} };
|
|
147
|
+
}
|
|
148
|
+
function styledLine(text, colorFor) {
|
|
149
|
+
const chunks = [];
|
|
150
|
+
let run = "";
|
|
151
|
+
let runColor = null;
|
|
152
|
+
const flush = () => {
|
|
153
|
+
if (!run || !runColor)
|
|
154
|
+
return;
|
|
155
|
+
chunks.push(chunk(run, COLOR[runColor], runColor === "body" || runColor === "eye", runColor === "dim"));
|
|
156
|
+
run = "";
|
|
157
|
+
};
|
|
158
|
+
for (const char of text) {
|
|
159
|
+
const next = colorFor(char);
|
|
160
|
+
if (next !== runColor) {
|
|
161
|
+
flush();
|
|
162
|
+
runColor = next;
|
|
163
|
+
}
|
|
164
|
+
run += char;
|
|
165
|
+
}
|
|
166
|
+
flush();
|
|
167
|
+
return new StyledText(chunks.length > 0 ? chunks : [chunk("", COLOR.ink)]);
|
|
168
|
+
}
|
|
169
|
+
function droneColor(char) {
|
|
170
|
+
if (char === "?" || char === "o" || char === "@" || char === "\u2022")
|
|
171
|
+
return "eye";
|
|
172
|
+
if (char === "%" || char === "=")
|
|
173
|
+
return "mini";
|
|
174
|
+
if (char === "$")
|
|
175
|
+
return "body";
|
|
176
|
+
if (char === "(" || char === ")" || char === "/" || char === "\\" || char === "|" || char === "-")
|
|
177
|
+
return "rotor";
|
|
178
|
+
if (char === "\xB7" || char === "\u2022" || char === "'" || char === ".")
|
|
179
|
+
return "path";
|
|
180
|
+
if (char.trim() === "")
|
|
181
|
+
return "dim";
|
|
182
|
+
return "ink";
|
|
183
|
+
}
|
|
184
|
+
function motionColor(char) {
|
|
185
|
+
if (char === "\u25CF" || char === "\u25C6" || char === "\u25C9")
|
|
186
|
+
return "body";
|
|
187
|
+
if (char === "\u2022" || char === "\xB7" || char === "\u2500" || char === "\u2502" || char === "\u2571" || char === "\u2572" || char === "\u256D" || char === "\u256E" || char === "\u2570" || char === "\u256F")
|
|
188
|
+
return "path";
|
|
189
|
+
if (char === "\u258C" || char === "\u2590" || char === "\u2581" || char === "\u2594")
|
|
190
|
+
return "mini";
|
|
191
|
+
return droneColor(char);
|
|
192
|
+
}
|
|
193
|
+
function materializeDrone(lines, tick, phase = 0) {
|
|
194
|
+
const blade = bladeForTick(tick, phase);
|
|
195
|
+
const eye = eyeForTick(tick, phase);
|
|
196
|
+
return lines.map((line) => line.replaceAll("!!!", blade).replaceAll("?", eye));
|
|
197
|
+
}
|
|
198
|
+
function miniDroneLines(tick, align = "left", phase = 0) {
|
|
199
|
+
return materializeDrone(MINI_DRONE, tick, phase).map((text) => ({
|
|
200
|
+
text,
|
|
201
|
+
styledText: styledLine(text, droneColor),
|
|
202
|
+
fg: RIG_UI.limeDim,
|
|
203
|
+
align
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
function brandFleetLines(tick, align = "center") {
|
|
207
|
+
const left = materializeDrone(MINI_DRONE, tick, 0);
|
|
208
|
+
const right = materializeDrone(MINI_DRONE, tick, 2);
|
|
209
|
+
const lead = materializeDrone(LEAD_MARK, tick, 1);
|
|
210
|
+
const packet = ["\xB7", "\u2022", "\u25CF", "\u2022"][Math.floor(tick / 2) % 4];
|
|
211
|
+
const bridge = [
|
|
212
|
+
` ${left[0]} ${lead[0]} ${right[0]}`,
|
|
213
|
+
` ${left[1]} \xB7\u2500\u2500${packet}\u2500\u2500\xB7 ${lead[1]} \xB7\u2500\u2500${packet}\u2500\u2500\xB7 ${right[1]}`,
|
|
214
|
+
` ${left[2]} \xB7' '${lead[2]}' '\xB7 ${right[2]}`,
|
|
215
|
+
` ${left[3]} \u2572 signal bus \u2571 ${right[3]}`,
|
|
216
|
+
` ${left[4]} \xB7\u2500\u2500\u2500${packet}\u2500\u2500\u2500\u2500\u2500\u2500${packet}\u2500\u2500\u2500\xB7 ${right[4]}`
|
|
217
|
+
];
|
|
218
|
+
return bridge.map((text) => ({
|
|
219
|
+
text,
|
|
220
|
+
styledText: styledLine(text, motionColor),
|
|
221
|
+
fg: RIG_UI.limeDim,
|
|
222
|
+
align
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
function actionDrone(tick, phase = 0) {
|
|
226
|
+
const blade = bladeForTick(tick, phase);
|
|
227
|
+
const eye = eyeForTick(tick, phase);
|
|
228
|
+
return `(${blade})\u2572${eye}\u2571(${blade})`;
|
|
229
|
+
}
|
|
230
|
+
function actionDroneLine(tick, text, fg = RIG_UI.ink2) {
|
|
231
|
+
const prefix = actionDrone(tick);
|
|
232
|
+
const content = `${prefix} ${text}`;
|
|
233
|
+
return { text: content, styledText: styledLine(content, motionColor), fg, align: "center" };
|
|
234
|
+
}
|
|
65
235
|
|
|
66
236
|
// packages/cli/src/app-opentui/render/scene.ts
|
|
67
237
|
function line(text, options = {}) {
|
|
68
238
|
return { text, ...options };
|
|
69
239
|
}
|
|
240
|
+
function blank() {
|
|
241
|
+
return { text: "" };
|
|
242
|
+
}
|
|
70
243
|
function deckRow(input) {
|
|
71
244
|
const prefix = input.active ? "\u25B8" : " ";
|
|
72
245
|
const label = input.label.toUpperCase().padEnd(10);
|
|
@@ -77,10 +250,39 @@ function deckRow(input) {
|
|
|
77
250
|
activateOnClick: input.activateOnClick
|
|
78
251
|
});
|
|
79
252
|
}
|
|
253
|
+
function loadingRows(label, tick = 0, compact = false) {
|
|
254
|
+
const mascot = compact ? miniDroneLines(tick, "center") : brandFleetLines(tick, "center");
|
|
255
|
+
return [
|
|
256
|
+
blank(),
|
|
257
|
+
...mascot,
|
|
258
|
+
blank(),
|
|
259
|
+
{ ...actionDroneLine(tick, `loading ${label}\u2026`, RIG_UI.cyan), align: "center" }
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
function errorBanner(message, hint) {
|
|
263
|
+
return [
|
|
264
|
+
line(`\u2717 failed to load \u2014 ${message}`, { fg: RIG_UI.red, bold: true }),
|
|
265
|
+
...hint ? [line(` ${hint}`, { fg: RIG_UI.ink3 })] : [],
|
|
266
|
+
line(" select refresh/retry below", { fg: RIG_UI.ink4 }),
|
|
267
|
+
line("", { fg: RIG_UI.ink3 })
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
function lastRefreshError(state) {
|
|
271
|
+
const value = state.data.lastRefreshError;
|
|
272
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
273
|
+
}
|
|
80
274
|
function makeSceneFrame(input) {
|
|
81
275
|
return input;
|
|
82
276
|
}
|
|
83
277
|
|
|
278
|
+
// packages/cli/src/app-opentui/selectable.ts
|
|
279
|
+
function selectableDeckRow(deck, item) {
|
|
280
|
+
return { ...deckRow({ ...deck, activateOnClick: true }), selectable: item };
|
|
281
|
+
}
|
|
282
|
+
function withSelectable(line2, item) {
|
|
283
|
+
return { ...line2, selectable: item, activateOnClick: true };
|
|
284
|
+
}
|
|
285
|
+
|
|
84
286
|
// packages/cli/src/app-opentui/list-search.ts
|
|
85
287
|
function isListSearchScene(scene) {
|
|
86
288
|
return scene === "fleet" || scene === "tasks";
|
|
@@ -113,8 +315,86 @@ function filterTasksForSearch(tasks, query) {
|
|
|
113
315
|
function searchSummary(kind, query, visible, total) {
|
|
114
316
|
return query.trim() ? `search ${JSON.stringify(query)} \xB7 ${visible}/${total} ${kind}` : `${total} ${kind}`;
|
|
115
317
|
}
|
|
318
|
+
function taskSortSpec(value) {
|
|
319
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
320
|
+
return null;
|
|
321
|
+
const record = value;
|
|
322
|
+
const key = record.key;
|
|
323
|
+
const dir = record.dir;
|
|
324
|
+
if ((key === "status" || key === "id" || key === "priority" || key === "assignee" || key === "title") && (dir === "asc" || dir === "desc")) {
|
|
325
|
+
return { key, dir };
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
function cycleTaskSort(current, key) {
|
|
330
|
+
if (!current || current.key !== key)
|
|
331
|
+
return { key, dir: "asc" };
|
|
332
|
+
if (current.dir === "asc")
|
|
333
|
+
return { key, dir: "desc" };
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
function applyDir(rows, compare, dir) {
|
|
337
|
+
const sorted = [...rows].sort(compare);
|
|
338
|
+
return dir === "desc" ? sorted.reverse() : sorted;
|
|
339
|
+
}
|
|
340
|
+
function sortTasks(tasks, spec) {
|
|
341
|
+
if (!spec)
|
|
342
|
+
return [...tasks];
|
|
343
|
+
const { key, dir } = spec;
|
|
344
|
+
const compare = (a, b) => {
|
|
345
|
+
if (key === "status")
|
|
346
|
+
return a.status.localeCompare(b.status) || a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
347
|
+
if (key === "priority") {
|
|
348
|
+
const left = a.priority ?? Number.NEGATIVE_INFINITY;
|
|
349
|
+
const right = b.priority ?? Number.NEGATIVE_INFINITY;
|
|
350
|
+
return right - left || a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
351
|
+
}
|
|
352
|
+
if (key === "assignee")
|
|
353
|
+
return (a.assignees[0] ?? "").localeCompare(b.assignees[0] ?? "") || a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
354
|
+
if (key === "title")
|
|
355
|
+
return a.title.localeCompare(b.title, undefined, { sensitivity: "base" }) || a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
356
|
+
return a.id.localeCompare(b.id, undefined, { numeric: true });
|
|
357
|
+
};
|
|
358
|
+
return applyDir(tasks, compare, dir);
|
|
359
|
+
}
|
|
116
360
|
|
|
117
361
|
// packages/cli/src/app-opentui/scenes/tasks.ts
|
|
362
|
+
var TASK_RECOVERY_ITEMS = [
|
|
363
|
+
{ id: "refresh", label: "refresh", intent: { scene: "tasks", argv: ["tasks"], action: { kind: "refresh", label: "Refresh tasks" } }, message: "refresh task source" },
|
|
364
|
+
{ id: "next", label: "next", intent: { scene: "command", argv: ["task", "run", "--next"], action: { kind: "command-run", label: "Run next task" } }, message: "dispatch next runnable task" },
|
|
365
|
+
{ id: "repair", label: "repair", intent: { scene: "command", argv: ["init", "--repair"], action: { kind: "command-run", label: "Repair task source" } }, message: "repair project/task source" },
|
|
366
|
+
{ id: "server", label: "server", intent: { scene: "server", argv: ["server", "status"], action: { kind: "refresh", label: "Open server controls" } }, message: "server controls" },
|
|
367
|
+
{ id: "doctor", label: "doctor", intent: { scene: "doctor", argv: ["doctor"], action: { kind: "doctor-run", label: "Run doctor" } }, message: "diagnose task source" }
|
|
368
|
+
];
|
|
369
|
+
var COL = {
|
|
370
|
+
glyph: 1,
|
|
371
|
+
id: 11,
|
|
372
|
+
status: 12,
|
|
373
|
+
priority: 5,
|
|
374
|
+
assignee: 12
|
|
375
|
+
};
|
|
376
|
+
var GUTTER = " ";
|
|
377
|
+
var TASK_COLUMNS = [
|
|
378
|
+
{ key: "status", label: "status", hint: "by task state" },
|
|
379
|
+
{ key: "id", label: "task-id", hint: "by id" },
|
|
380
|
+
{ key: "priority", label: "priority", hint: "by priority" },
|
|
381
|
+
{ key: "assignee", label: "assignee", hint: "by assignee" },
|
|
382
|
+
{ key: "title", label: "title", hint: "by title" }
|
|
383
|
+
];
|
|
384
|
+
function taskSelectableItem(task) {
|
|
385
|
+
const taskId = task.id?.trim();
|
|
386
|
+
if (!taskId)
|
|
387
|
+
return null;
|
|
388
|
+
const title = (task.title || "task").trim() || "task";
|
|
389
|
+
const activeRunId = task.activeRun?.runId?.trim();
|
|
390
|
+
return {
|
|
391
|
+
id: taskId,
|
|
392
|
+
label: title,
|
|
393
|
+
data: { taskDetailId: taskId, selectedTaskId: taskId, ...activeRunId ? { selectedRunId: activeRunId } : {} },
|
|
394
|
+
intent: { scene: "tasks", argv: ["tasks", taskId], action: { kind: "refresh", label: `Open ${title}`, payload: { taskDetailId: taskId } } },
|
|
395
|
+
message: `open ${title} detail`
|
|
396
|
+
};
|
|
397
|
+
}
|
|
118
398
|
function taskView(state) {
|
|
119
399
|
const tasks = state.data.tasks;
|
|
120
400
|
return tasks && typeof tasks === "object" && !Array.isArray(tasks) ? tasks : null;
|
|
@@ -123,6 +403,32 @@ function taskRecords(state) {
|
|
|
123
403
|
const records = taskView(state)?.records;
|
|
124
404
|
return Array.isArray(records) ? records : [];
|
|
125
405
|
}
|
|
406
|
+
function dispatchingFor(state) {
|
|
407
|
+
const value = state.data.dispatchingRun;
|
|
408
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
409
|
+
return null;
|
|
410
|
+
const record = value;
|
|
411
|
+
if (typeof record.taskId !== "string" || !record.taskId.trim())
|
|
412
|
+
return null;
|
|
413
|
+
return { taskId: record.taskId, status: typeof record.status === "string" ? record.status : "dispatching" };
|
|
414
|
+
}
|
|
415
|
+
function updatedAgoLabel(state) {
|
|
416
|
+
const refreshedAt = taskView(state)?.refreshedAt;
|
|
417
|
+
if (typeof refreshedAt !== "string")
|
|
418
|
+
return;
|
|
419
|
+
const parsed = Date.parse(refreshedAt);
|
|
420
|
+
if (!Number.isFinite(parsed))
|
|
421
|
+
return;
|
|
422
|
+
const seconds = Math.max(0, Math.round((Date.now() - parsed) / 1000));
|
|
423
|
+
if (seconds < 1)
|
|
424
|
+
return "updated just now";
|
|
425
|
+
if (seconds < 60)
|
|
426
|
+
return `updated ${seconds}s ago`;
|
|
427
|
+
const minutes = Math.round(seconds / 60);
|
|
428
|
+
if (minutes < 60)
|
|
429
|
+
return `updated ${minutes}m ago`;
|
|
430
|
+
return `updated ${Math.round(minutes / 60)}h ago`;
|
|
431
|
+
}
|
|
126
432
|
function panelWidth(layout) {
|
|
127
433
|
return layout?.centerWidth ?? 100;
|
|
128
434
|
}
|
|
@@ -134,9 +440,19 @@ function clip(value, width) {
|
|
|
134
440
|
return "";
|
|
135
441
|
return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
136
442
|
}
|
|
443
|
+
function cell(value, width) {
|
|
444
|
+
return clip(value, width).padEnd(width);
|
|
445
|
+
}
|
|
137
446
|
function formatAssignee(value) {
|
|
138
447
|
return value.startsWith("@") ? value : `@${value}`;
|
|
139
448
|
}
|
|
449
|
+
function assigneeCell(task) {
|
|
450
|
+
const first = task.assignees[0];
|
|
451
|
+
return first ? formatAssignee(first) : "\u2014";
|
|
452
|
+
}
|
|
453
|
+
function priorityCell(task) {
|
|
454
|
+
return task.priority === undefined ? "\u2014" : `P${String(task.priority).slice(0, 3)}`;
|
|
455
|
+
}
|
|
140
456
|
function taskViewLabel(view) {
|
|
141
457
|
const filters = view?.filters && typeof view.filters === "object" && !Array.isArray(view.filters) ? view.filters : {};
|
|
142
458
|
const filter = typeof filters.preset === "string" && filters.preset !== "custom" ? filters.preset : [
|
|
@@ -152,31 +468,75 @@ function taskViewLabel(view) {
|
|
|
152
468
|
const count = visible !== undefined && total !== undefined ? `${visible}/${total}` : "syncing";
|
|
153
469
|
return `filter ${filter} \xB7 sort ${sort} \xB7 ${count}`;
|
|
154
470
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
471
|
+
function sortArrow(spec, key) {
|
|
472
|
+
if (!spec || spec.key !== key)
|
|
473
|
+
return "";
|
|
474
|
+
return spec.dir === "asc" ? "\u25B2" : "\u25BC";
|
|
475
|
+
}
|
|
476
|
+
function headerRow(spec) {
|
|
477
|
+
const text = [
|
|
478
|
+
" ",
|
|
479
|
+
cell(`task-id${sortArrow(spec, "id")}`, COL.id),
|
|
480
|
+
cell(`status${sortArrow(spec, "status")}`, COL.status),
|
|
481
|
+
cell(`prio${sortArrow(spec, "priority")}`, COL.priority),
|
|
482
|
+
cell(`assignee${sortArrow(spec, "assignee")}`, COL.assignee),
|
|
483
|
+
`title${sortArrow(spec, "title")}`
|
|
484
|
+
].join(GUTTER);
|
|
485
|
+
return line(text, { fg: RIG_UI.ink, bold: true, bg: RIG_UI.glass });
|
|
486
|
+
}
|
|
487
|
+
function sortToggleRow(column, current) {
|
|
488
|
+
const arrow = sortArrow(current, column.key);
|
|
489
|
+
const active = current?.key === column.key;
|
|
490
|
+
const next = cycleTaskSort(current, column.key);
|
|
491
|
+
const marker = active ? "\u25B8" : " ";
|
|
492
|
+
const label = `${column.label}${arrow ? ` ${arrow}` : ""}`.padEnd(12);
|
|
493
|
+
const row = line(`${marker} ${label} ${column.hint}`, {
|
|
494
|
+
fg: active ? RIG_UI.lime : RIG_UI.ink2,
|
|
495
|
+
bold: active
|
|
496
|
+
});
|
|
497
|
+
return withSelectable(row, {
|
|
498
|
+
id: `task-sort-${column.key}`,
|
|
499
|
+
label: `sort by ${column.label}`,
|
|
500
|
+
data: { taskSort: next },
|
|
501
|
+
message: `sort tasks ${next ? `${column.label} ${next.dir}` : "default"}`
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
function sortStrip(spec) {
|
|
505
|
+
return [
|
|
506
|
+
line("SORT", { fg: RIG_UI.ink3, bold: true }),
|
|
507
|
+
...TASK_COLUMNS.map((column) => sortToggleRow(column, spec)),
|
|
508
|
+
line("", { fg: RIG_UI.ink3 })
|
|
509
|
+
];
|
|
510
|
+
}
|
|
511
|
+
function taskRow(width, task, active, optimistic) {
|
|
512
|
+
const effectiveStatus = optimistic ?? task.status;
|
|
513
|
+
const { glyph } = statusLabel(effectiveStatus);
|
|
159
514
|
const marker = active ? "\u258C" : " ";
|
|
160
|
-
const
|
|
515
|
+
const status = effectiveStatus.replace(/[_-]/g, " ");
|
|
516
|
+
const fixed = `${marker} ${glyph}${GUTTER}${cell(task.id, COL.id)}${GUTTER}${cell(status, COL.status)}${GUTTER}${cell(priorityCell(task), COL.priority)}${GUTTER}${cell(assigneeCell(task), COL.assignee)}${GUTTER}`;
|
|
161
517
|
const activeRun = task.activeRun;
|
|
162
518
|
const activeSuffix = activeRun ? ` \xB7 attach ${activeRun.runId.slice(0, 8)}${activeRun.status ? ` ${activeRun.status}` : ""}` : "";
|
|
163
|
-
const titleWidth = Math.max(
|
|
164
|
-
|
|
165
|
-
fg: active ? RIG_UI.ink :
|
|
519
|
+
const titleWidth = Math.max(8, width - fixed.length - 2);
|
|
520
|
+
const row = line(`${fixed}${clip(`${task.title}${activeSuffix}`, titleWidth)}`, {
|
|
521
|
+
fg: active ? RIG_UI.ink : RIG_UI.ink2,
|
|
166
522
|
bold: active,
|
|
167
|
-
|
|
168
|
-
activateOnClick: true
|
|
523
|
+
...active ? { bg: RIG_UI.glass } : {}
|
|
169
524
|
});
|
|
525
|
+
const item = taskSelectableItem(task);
|
|
526
|
+
return item ? withSelectable(row, item) : row;
|
|
170
527
|
}
|
|
528
|
+
var RECOVERY_DECKS = [
|
|
529
|
+
{ label: "refresh", detail: "reload task source" },
|
|
530
|
+
{ label: "next", detail: "dispatch next runnable task" },
|
|
531
|
+
{ label: "repair", detail: "repair project/task source link" },
|
|
532
|
+
{ label: "server", detail: "open server controls" },
|
|
533
|
+
{ label: "doctor", detail: "diagnose task source" }
|
|
534
|
+
];
|
|
171
535
|
function recoveryRows(selected, reason) {
|
|
172
536
|
return [
|
|
173
537
|
...reason ? [line(reason, { fg: RIG_UI.yellow }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
174
538
|
line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
|
|
175
|
-
|
|
176
|
-
deckRow({ label: "next", detail: "dispatch next runnable task", index: 1, active: selected === 1, activateOnClick: true }),
|
|
177
|
-
deckRow({ label: "repair", detail: "repair project/task source link", index: 2, active: selected === 2, activateOnClick: true }),
|
|
178
|
-
deckRow({ label: "server", detail: "open server controls", index: 3, active: selected === 3, activateOnClick: true }),
|
|
179
|
-
deckRow({ label: "doctor", detail: "diagnose task source", index: 4, active: selected === 4, activateOnClick: true })
|
|
539
|
+
...RECOVERY_DECKS.map((deck, index) => selectableDeckRow({ ...deck, index, active: selected === index }, TASK_RECOVERY_ITEMS[index]))
|
|
180
540
|
];
|
|
181
541
|
}
|
|
182
542
|
function emptyRows(query, total, selected) {
|
|
@@ -194,27 +554,119 @@ function degradedRows(state, selected) {
|
|
|
194
554
|
return [];
|
|
195
555
|
return recoveryRows(selected, state.error.message);
|
|
196
556
|
}
|
|
557
|
+
function rawString(raw, key) {
|
|
558
|
+
const value = raw?.[key];
|
|
559
|
+
return typeof value === "string" ? value : "";
|
|
560
|
+
}
|
|
561
|
+
function taskBody(state, task) {
|
|
562
|
+
const preview = state.data.taskPreviewRaw;
|
|
563
|
+
if (preview && typeof preview === "object" && !Array.isArray(preview)) {
|
|
564
|
+
const fromPreview = rawString(preview, "body");
|
|
565
|
+
if (fromPreview.trim())
|
|
566
|
+
return fromPreview;
|
|
567
|
+
}
|
|
568
|
+
return rawString(task?.raw, "body");
|
|
569
|
+
}
|
|
570
|
+
function renderTaskDetail(state, taskId, layout) {
|
|
571
|
+
const task = taskRecords(state).find((entry) => entry.id === taskId);
|
|
572
|
+
const title = (task?.title || taskId).trim();
|
|
573
|
+
const status = task?.status ?? "unknown";
|
|
574
|
+
const { glyph, color } = statusLabel(status);
|
|
575
|
+
const body = taskBody(state, task);
|
|
576
|
+
const activeRunId = task?.activeRun?.runId?.trim();
|
|
577
|
+
const width = panelWidth(layout);
|
|
578
|
+
const selected = Math.max(0, state.selection.index);
|
|
579
|
+
const headerLines = [
|
|
580
|
+
line(`${glyph} ${title}`, { fg: color, bold: true }),
|
|
581
|
+
line(`task ${taskId} \xB7 ${status}`, { fg: RIG_UI.ink3 }),
|
|
582
|
+
...task && task.labels.length > 0 ? [line(`labels: ${task.labels.join(", ")}`, { fg: RIG_UI.ink4 })] : [],
|
|
583
|
+
...task && task.assignees.length > 0 ? [line(`assignees: ${task.assignees.join(", ")}`, { fg: RIG_UI.ink4 })] : [],
|
|
584
|
+
line("", { fg: RIG_UI.ink3 }),
|
|
585
|
+
line("ACTIONS", { fg: RIG_UI.ink3, bold: true })
|
|
586
|
+
];
|
|
587
|
+
let index = 0;
|
|
588
|
+
const actionRows = [];
|
|
589
|
+
actionRows.push(selectableDeckRow({ label: "back", detail: "return to the task list", index, active: selected === index }, { id: "task-detail-back", label: "back", data: { taskDetailId: undefined }, message: "back to task list" }));
|
|
590
|
+
index += 1;
|
|
591
|
+
actionRows.push(selectableDeckRow({ label: "dispatch", detail: "submit a Pi run for this task", index, active: selected === index }, { id: `task-detail-run:${taskId}`, label: `dispatch ${taskId}`, intent: { scene: "tasks", argv: ["run", taskId], action: { kind: "task-run-id", payload: { task: taskId }, label: `Dispatching ${taskId}` } }, message: `dispatch task ${taskId}` }));
|
|
592
|
+
index += 1;
|
|
593
|
+
if (activeRunId) {
|
|
594
|
+
actionRows.push(selectableDeckRow({ label: "attach", detail: `attach the Pi console to run ${activeRunId.slice(0, 8)}`, index, active: selected === index }, { id: `task-detail-attach:${taskId}`, label: `attach ${activeRunId.slice(0, 8)}`, intent: { scene: "handoff", argv: ["attach", activeRunId], action: { kind: "run-attach", payload: { runId: activeRunId }, label: `Attach Pi ${activeRunId.slice(0, 8)}` } }, message: `attach Pi to ${activeRunId.slice(0, 8)}` }));
|
|
595
|
+
index += 1;
|
|
596
|
+
}
|
|
597
|
+
const bodyHeading = [line("", { fg: RIG_UI.ink3 }), line("BODY", { fg: RIG_UI.ink3, bold: true })];
|
|
598
|
+
const bodyLines = body.trim() ? body.split(`
|
|
599
|
+
`).slice(0, 400).map((text) => line(text, { fg: RIG_UI.ink2 })) : [line("(no description)", { fg: RIG_UI.ink4 })];
|
|
600
|
+
const panelLines = [...headerLines, ...actionRows, ...bodyHeading, ...bodyLines];
|
|
601
|
+
const paddingX = layout?.compact ? 2 : 3;
|
|
602
|
+
const chromeTopInset = 3 + 1;
|
|
603
|
+
const headerCount = headerLines.length + actionRows.length + bodyHeading.length;
|
|
604
|
+
const native = body.trim() ? {
|
|
605
|
+
kind: "markdown",
|
|
606
|
+
content: body,
|
|
607
|
+
top: (layout?.centerTop ?? 1) + chromeTopInset + headerCount,
|
|
608
|
+
left: (layout?.centerLeft ?? 2) + paddingX,
|
|
609
|
+
width: Math.max(20, width - paddingX * 2),
|
|
610
|
+
height: Math.max(6, panelHeight(layout, 0) - chromeTopInset - headerCount - 1)
|
|
611
|
+
} : undefined;
|
|
612
|
+
return makeSceneFrame({
|
|
613
|
+
scene: "tasks",
|
|
614
|
+
title: "Task",
|
|
615
|
+
lines: [],
|
|
616
|
+
...native ? { native } : {},
|
|
617
|
+
panels: [{
|
|
618
|
+
id: "task-detail",
|
|
619
|
+
top: 0,
|
|
620
|
+
width,
|
|
621
|
+
height: panelHeight(layout, 0),
|
|
622
|
+
lines: panelLines,
|
|
623
|
+
backgroundColor: RIG_UI.panel,
|
|
624
|
+
backgroundAlpha: 184,
|
|
625
|
+
opacity: 1,
|
|
626
|
+
border: false,
|
|
627
|
+
stickyScroll: true,
|
|
628
|
+
stickyStart: "top",
|
|
629
|
+
chrome: "ad-terminal",
|
|
630
|
+
headerText: `task ${taskId}`,
|
|
631
|
+
headerHeight: 3,
|
|
632
|
+
paddingX,
|
|
633
|
+
paddingY: 1
|
|
634
|
+
}],
|
|
635
|
+
footer: { run: activeRunId ? `${activeRunId.slice(0, 8)} active` : undefined, message: "back returns to the list \xB7 dispatch runs the task" },
|
|
636
|
+
live: state.status === "action"
|
|
637
|
+
});
|
|
638
|
+
}
|
|
197
639
|
function renderTasksScene(state, layout) {
|
|
640
|
+
const detailId = typeof state.data.taskDetailId === "string" ? state.data.taskDetailId.trim() : "";
|
|
641
|
+
if (detailId)
|
|
642
|
+
return renderTaskDetail(state, detailId, layout);
|
|
198
643
|
const width = panelWidth(layout);
|
|
199
644
|
const view = taskView(state);
|
|
200
645
|
const allTasks = taskRecords(state);
|
|
201
646
|
const query = listSearchQuery(state);
|
|
202
|
-
const
|
|
647
|
+
const spec = taskSortSpec(state.data.taskSort);
|
|
648
|
+
const tasks = sortTasks(filterTasksForSearch(allTasks, query), spec);
|
|
203
649
|
const selected = typeof state.data.selectedTaskId === "string" && tasks.some((task) => task.id === state.data.selectedTaskId) ? state.data.selectedTaskId : tasks[0]?.id;
|
|
204
650
|
const selectedIndex = Math.max(0, tasks.findIndex((task) => task.id === selected));
|
|
205
651
|
const recoveryIndex = Math.max(0, Math.min(4, state.selection.index));
|
|
206
|
-
const
|
|
207
|
-
const dispatchLine =
|
|
652
|
+
const dispatching = dispatchingFor(state);
|
|
653
|
+
const dispatchLine = dispatching ? ` \xB7 ${state.data.dispatchingRun?.runId ?? "new"} ${dispatching.status}` : "";
|
|
654
|
+
const updatedAgo = updatedAgoLabel(state);
|
|
208
655
|
const panelTop = 0;
|
|
209
656
|
const contentWidth = Math.max(12, width - (layout?.compact ? 8 : 10));
|
|
657
|
+
const loading = state.status === "loading" && allTasks.length === 0;
|
|
658
|
+
const refreshError = lastRefreshError(state);
|
|
659
|
+
const banner = refreshError && !state.error ? errorBanner(refreshError) : [];
|
|
210
660
|
const panelLines = [
|
|
211
|
-
|
|
212
|
-
line("", { fg: RIG_UI.ink3 }),
|
|
661
|
+
...banner,
|
|
213
662
|
...tasks.length > 0 ? [
|
|
214
663
|
...degradedRows(state, recoveryIndex),
|
|
215
664
|
...query.trim() ? [line(searchSummary("tasks", query, tasks.length, allTasks.length), { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
216
|
-
...
|
|
217
|
-
|
|
665
|
+
...sortStrip(spec),
|
|
666
|
+
headerRow(spec),
|
|
667
|
+
line("", { fg: RIG_UI.ink3 }),
|
|
668
|
+
...tasks.map((task, index) => taskRow(contentWidth, task, index === selectedIndex, dispatching && dispatching.taskId === task.id ? dispatching.status : undefined))
|
|
669
|
+
] : state.error ? degradedRows(state, recoveryIndex) : loading ? loadingRows("tasks", state.tick) : [
|
|
218
670
|
...query.trim() ? [line(searchSummary("tasks", query, tasks.length, allTasks.length), { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 })] : [],
|
|
219
671
|
...emptyRows(query, allTasks.length, recoveryIndex)
|
|
220
672
|
]
|
|
@@ -224,7 +676,7 @@ function renderTasksScene(state, layout) {
|
|
|
224
676
|
title: "Tasks",
|
|
225
677
|
lines: [
|
|
226
678
|
line("", { fg: RIG_UI.ink4 }),
|
|
227
|
-
line(query.trim() ? ` tasks --search ${JSON.stringify(query)}` : ` tasks \xB7 ${taskViewLabel(view)}${dispatchLine}`, { fg: RIG_UI.ink3 })
|
|
679
|
+
line(`${query.trim() ? ` tasks --search ${JSON.stringify(query)}` : ` tasks \xB7 ${taskViewLabel(view)}${dispatchLine}`}${updatedAgo ? ` \xB7 ${updatedAgo}` : ""}`, { fg: RIG_UI.ink3 })
|
|
228
680
|
],
|
|
229
681
|
panels: [{
|
|
230
682
|
id: "tasks-list",
|
|
@@ -242,7 +694,10 @@ function renderTasksScene(state, layout) {
|
|
|
242
694
|
paddingX: layout?.compact ? 2 : 3,
|
|
243
695
|
paddingY: 1
|
|
244
696
|
}],
|
|
245
|
-
footer: {
|
|
697
|
+
footer: {
|
|
698
|
+
message: query.trim() ? searchSummary("tasks", query, tasks.length, allTasks.length) : taskViewLabel(view),
|
|
699
|
+
...updatedAgo ? { updated: updatedAgo } : {}
|
|
700
|
+
},
|
|
246
701
|
typeBarPlaceholder: "search tasks\u2026",
|
|
247
702
|
live: true
|
|
248
703
|
});
|