@lumea-labs/orchestrator 0.1.0
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 +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +93 -0
- package/dist/lib/format.d.ts +3 -0
- package/dist/lib/format.js +9 -0
- package/dist/orchestrator-document.d.ts +37 -0
- package/dist/orchestrator-document.js +122 -0
- package/dist/plan-detail.d.ts +102 -0
- package/dist/plan-detail.js +385 -0
- package/dist/plan-graph.d.ts +39 -0
- package/dist/plan-graph.js +597 -0
- package/dist/plan-node-detail.d.ts +29 -0
- package/dist/plan-node-detail.js +346 -0
- package/dist/plan-task-detail.d.ts +76 -0
- package/dist/plan-task-detail.js +450 -0
- package/dist/plan-types.d.ts +85 -0
- package/dist/plan-types.js +51 -0
- package/dist/run-kanban-filter-menu.d.ts +24 -0
- package/dist/run-kanban-filter-menu.js +152 -0
- package/dist/run-kanban.d.ts +61 -0
- package/dist/run-kanban.js +234 -0
- package/dist/swarm-agent-badge.d.ts +15 -0
- package/dist/swarm-agent-badge.js +39 -0
- package/dist/swarm-run-activity.d.ts +39 -0
- package/dist/swarm-run-activity.js +289 -0
- package/dist/swarm-run-card.d.ts +22 -0
- package/dist/swarm-run-card.js +91 -0
- package/dist/swarm-run-detail.d.ts +45 -0
- package/dist/swarm-run-detail.js +559 -0
- package/dist/swarm-run-list.d.ts +22 -0
- package/dist/swarm-run-list.js +75 -0
- package/dist/swarm-run-row.d.ts +22 -0
- package/dist/swarm-run-row.js +125 -0
- package/dist/swarm-skeletons.d.ts +28 -0
- package/dist/swarm-skeletons.js +78 -0
- package/dist/swarm-status-bar.d.ts +15 -0
- package/dist/swarm-status-bar.js +79 -0
- package/dist/swarm-status-pill.d.ts +12 -0
- package/dist/swarm-status-pill.js +86 -0
- package/dist/swarm-timeline.d.ts +21 -0
- package/dist/swarm-timeline.js +414 -0
- package/dist/task-workspace-sidebar.d.ts +71 -0
- package/dist/task-workspace-sidebar.js +352 -0
- package/dist/types.d.ts +285 -0
- package/dist/types.js +44 -0
- package/package.json +41 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import {
|
|
4
|
+
defaultSwarmLabels
|
|
5
|
+
} from "./types";
|
|
6
|
+
const STATUS_FILL = {
|
|
7
|
+
scheduled: "bg-[#7B3FE4]/40",
|
|
8
|
+
pending: "bg-p-warm",
|
|
9
|
+
running: "bg-p-accent",
|
|
10
|
+
review: "bg-[#2B44FF]",
|
|
11
|
+
done: "bg-p-green",
|
|
12
|
+
failed: "bg-[#E63946]",
|
|
13
|
+
cancelled: "bg-p-ink-3/40"
|
|
14
|
+
};
|
|
15
|
+
const STATUS_DOT = {
|
|
16
|
+
scheduled: "bg-[#7B3FE4]",
|
|
17
|
+
pending: "bg-p-ink-3",
|
|
18
|
+
running: "bg-p-accent",
|
|
19
|
+
review: "bg-[#2B44FF]",
|
|
20
|
+
done: "bg-p-green",
|
|
21
|
+
failed: "bg-[#E63946]",
|
|
22
|
+
cancelled: "bg-p-ink-3/50"
|
|
23
|
+
};
|
|
24
|
+
function SwarmTimeline({
|
|
25
|
+
runs,
|
|
26
|
+
now,
|
|
27
|
+
renderAvatar,
|
|
28
|
+
onOpen,
|
|
29
|
+
rowHeight = 32,
|
|
30
|
+
labelWidth = 264,
|
|
31
|
+
labels,
|
|
32
|
+
className
|
|
33
|
+
}) {
|
|
34
|
+
const L = { ...defaultSwarmLabels, ...labels };
|
|
35
|
+
const clock = now ?? Date.now();
|
|
36
|
+
const { rows, span, originMs, endMs, ticks, deps, nowPct, counts } = useMemo(() => {
|
|
37
|
+
const startedRuns = runs.filter((r) => r.startedAt);
|
|
38
|
+
if (startedRuns.length === 0) {
|
|
39
|
+
return {
|
|
40
|
+
rows: [],
|
|
41
|
+
span: 0,
|
|
42
|
+
originMs: 0,
|
|
43
|
+
endMs: 0,
|
|
44
|
+
ticks: [],
|
|
45
|
+
deps: [],
|
|
46
|
+
nowPct: 0,
|
|
47
|
+
counts: { running: 0, done: 0, failed: 0 }
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const startedMsList = startedRuns.map((r) => new Date(r.startedAt).getTime());
|
|
51
|
+
const origin = Math.min(...startedMsList);
|
|
52
|
+
const lastEnd = Math.max(
|
|
53
|
+
clock,
|
|
54
|
+
...startedRuns.map(
|
|
55
|
+
(r) => r.finishedAt ? new Date(r.finishedAt).getTime() : clock
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
const padded = origin + (lastEnd - origin) * 1.06;
|
|
59
|
+
const span2 = Math.max(1, padded - origin);
|
|
60
|
+
const sorted = [...startedRuns].sort((a, b) => {
|
|
61
|
+
const ai = new Date(a.startedAt).getTime();
|
|
62
|
+
const bi = new Date(b.startedAt).getTime();
|
|
63
|
+
return ai - bi;
|
|
64
|
+
});
|
|
65
|
+
const idIndex = /* @__PURE__ */ new Map();
|
|
66
|
+
const rows2 = sorted.map((run, index) => {
|
|
67
|
+
const startedMs = new Date(run.startedAt).getTime();
|
|
68
|
+
const endedMs = run.finishedAt ? new Date(run.finishedAt).getTime() : clock;
|
|
69
|
+
idIndex.set(run.id, index);
|
|
70
|
+
return {
|
|
71
|
+
run,
|
|
72
|
+
startedMs,
|
|
73
|
+
endedMs,
|
|
74
|
+
leftPct: (startedMs - origin) / span2 * 100,
|
|
75
|
+
widthPct: Math.max(0.4, (endedMs - startedMs) / span2 * 100),
|
|
76
|
+
index
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
const deps2 = [];
|
|
80
|
+
for (const row of rows2) {
|
|
81
|
+
const parentId = row.run.parentRunId;
|
|
82
|
+
if (!parentId) continue;
|
|
83
|
+
const parentIndex = idIndex.get(parentId);
|
|
84
|
+
if (parentIndex === void 0) continue;
|
|
85
|
+
const parent = rows2[parentIndex];
|
|
86
|
+
deps2.push({
|
|
87
|
+
fromIndex: parent.index,
|
|
88
|
+
toIndex: row.index,
|
|
89
|
+
fromPct: parent.leftPct + parent.widthPct,
|
|
90
|
+
toPct: row.leftPct
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const spanSec = span2 / 1e3;
|
|
94
|
+
const stepSec = pickStep(spanSec);
|
|
95
|
+
const ticks2 = [];
|
|
96
|
+
for (let s = 0; s <= spanSec; s += stepSec) {
|
|
97
|
+
ticks2.push({
|
|
98
|
+
pct: s / spanSec * 100,
|
|
99
|
+
label: formatTick(s)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const nowPct2 = (clock - origin) / span2 * 100;
|
|
103
|
+
const counts2 = startedRuns.reduce(
|
|
104
|
+
(acc, r) => {
|
|
105
|
+
if (r.status === "running") acc.running += 1;
|
|
106
|
+
else if (r.status === "done") acc.done += 1;
|
|
107
|
+
else if (r.status === "failed") acc.failed += 1;
|
|
108
|
+
return acc;
|
|
109
|
+
},
|
|
110
|
+
{ running: 0, done: 0, failed: 0 }
|
|
111
|
+
);
|
|
112
|
+
return {
|
|
113
|
+
rows: rows2,
|
|
114
|
+
span: span2,
|
|
115
|
+
originMs: origin,
|
|
116
|
+
endMs: padded,
|
|
117
|
+
ticks: ticks2,
|
|
118
|
+
deps: deps2,
|
|
119
|
+
nowPct: nowPct2,
|
|
120
|
+
counts: counts2
|
|
121
|
+
};
|
|
122
|
+
}, [runs, clock]);
|
|
123
|
+
if (rows.length === 0) {
|
|
124
|
+
return /* @__PURE__ */ React.createElement("div", { className: ["px-3 py-10 text-center", className || ""].join(" ") }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[11px] uppercase tracking-[0.16em] text-p-ink-3" }, L.noRuns));
|
|
125
|
+
}
|
|
126
|
+
const totalLanesHeight = rows.length * rowHeight;
|
|
127
|
+
const headerH = 56;
|
|
128
|
+
const axisH = 26;
|
|
129
|
+
return /* @__PURE__ */ React.createElement(
|
|
130
|
+
"div",
|
|
131
|
+
{
|
|
132
|
+
className: [
|
|
133
|
+
"overflow-hidden rounded-2xl border border-p-line bg-p-surface",
|
|
134
|
+
className || ""
|
|
135
|
+
].join(" ")
|
|
136
|
+
},
|
|
137
|
+
/* @__PURE__ */ React.createElement(
|
|
138
|
+
"div",
|
|
139
|
+
{
|
|
140
|
+
className: "flex items-center justify-between gap-6 border-b border-p-line px-5",
|
|
141
|
+
style: { height: headerH }
|
|
142
|
+
},
|
|
143
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex items-baseline gap-3" }, /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.24em] text-p-ink-3" }, "mission control"), /* @__PURE__ */ React.createElement("span", { className: "font-display text-[20px] font-bold leading-none tracking-[-0.02em] text-p-ink tabular-nums" }, String(rows.length).padStart(2, "0")), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] uppercase tracking-[0.2em] text-p-ink-3" }, "runs \xB7 ", L.duration(span / 1e3))),
|
|
144
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 font-mono text-[10px] uppercase tracking-[0.16em] text-p-ink-3" }, /* @__PURE__ */ React.createElement(Tally, { tone: "accent", value: counts.running, label: "run", pulse: true }), /* @__PURE__ */ React.createElement(Tally, { tone: "green", value: counts.done, label: "done" }), /* @__PURE__ */ React.createElement(Tally, { tone: "red", value: counts.failed, label: "fail" }))
|
|
145
|
+
),
|
|
146
|
+
/* @__PURE__ */ React.createElement(
|
|
147
|
+
"div",
|
|
148
|
+
{
|
|
149
|
+
className: "grid",
|
|
150
|
+
style: { gridTemplateColumns: `${labelWidth}px minmax(0,1fr)` }
|
|
151
|
+
},
|
|
152
|
+
/* @__PURE__ */ React.createElement(
|
|
153
|
+
"div",
|
|
154
|
+
{
|
|
155
|
+
className: "border-b border-r border-p-line bg-p-bg/60",
|
|
156
|
+
style: { height: axisH }
|
|
157
|
+
},
|
|
158
|
+
/* @__PURE__ */ React.createElement("span", { className: "ml-5 inline-flex h-full items-center font-mono text-[9.5px] uppercase tracking-[0.2em] text-p-ink-3" }, "agent \xB7 goal")
|
|
159
|
+
),
|
|
160
|
+
/* @__PURE__ */ React.createElement(
|
|
161
|
+
"div",
|
|
162
|
+
{
|
|
163
|
+
className: "relative border-b border-p-line bg-p-bg/60",
|
|
164
|
+
style: { height: axisH }
|
|
165
|
+
},
|
|
166
|
+
ticks.map((t, i) => /* @__PURE__ */ React.createElement(
|
|
167
|
+
"span",
|
|
168
|
+
{
|
|
169
|
+
key: i,
|
|
170
|
+
className: "absolute top-1/2 -translate-x-1/2 -translate-y-1/2 font-mono text-[9.5px] uppercase tracking-[0.2em] text-p-ink-3 tabular-nums",
|
|
171
|
+
style: { left: `${t.pct}%` }
|
|
172
|
+
},
|
|
173
|
+
t.label
|
|
174
|
+
)),
|
|
175
|
+
/* @__PURE__ */ React.createElement("span", { className: "absolute right-3 top-1/2 -translate-y-1/2 font-mono text-[9.5px] uppercase tracking-[0.16em] text-p-ink-3 tabular-nums" }, formatClock(endMs))
|
|
176
|
+
),
|
|
177
|
+
/* @__PURE__ */ React.createElement(
|
|
178
|
+
"div",
|
|
179
|
+
{
|
|
180
|
+
className: "flex flex-col border-r border-p-line",
|
|
181
|
+
style: { height: totalLanesHeight }
|
|
182
|
+
},
|
|
183
|
+
rows.map(({ run, index }) => /* @__PURE__ */ React.createElement(
|
|
184
|
+
"div",
|
|
185
|
+
{
|
|
186
|
+
key: run.id,
|
|
187
|
+
role: onOpen ? "button" : void 0,
|
|
188
|
+
tabIndex: onOpen ? 0 : void 0,
|
|
189
|
+
onClick: onOpen ? () => onOpen(run.id) : void 0,
|
|
190
|
+
className: [
|
|
191
|
+
"group flex shrink-0 items-center gap-2.5 px-5",
|
|
192
|
+
onOpen ? "cursor-pointer transition-colors hover:bg-p-warm/50" : "",
|
|
193
|
+
index !== 0 ? "border-t border-p-line/60" : ""
|
|
194
|
+
].join(" "),
|
|
195
|
+
style: { height: rowHeight }
|
|
196
|
+
},
|
|
197
|
+
/* @__PURE__ */ React.createElement(
|
|
198
|
+
"span",
|
|
199
|
+
{
|
|
200
|
+
"aria-hidden": true,
|
|
201
|
+
className: [
|
|
202
|
+
"size-1.5 shrink-0 rounded-full",
|
|
203
|
+
STATUS_DOT[run.status],
|
|
204
|
+
run.status === "running" ? "animate-pulse" : ""
|
|
205
|
+
].join(" ")
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
renderAvatar ? renderAvatar(run.agent) : /* @__PURE__ */ React.createElement(
|
|
209
|
+
"span",
|
|
210
|
+
{
|
|
211
|
+
"aria-hidden": true,
|
|
212
|
+
className: "grid size-[18px] shrink-0 place-items-center rounded font-display text-[10px] font-bold text-white",
|
|
213
|
+
style: { background: run.agent.color || "#999" }
|
|
214
|
+
},
|
|
215
|
+
run.agent.glyph || (run.agent.displayName || run.agent.name).charAt(0).toUpperCase()
|
|
216
|
+
),
|
|
217
|
+
/* @__PURE__ */ React.createElement("span", { className: "w-[72px] shrink-0 truncate font-mono text-[10.5px] font-bold uppercase tracking-[0.14em] text-p-ink" }, run.agent.displayName || run.agent.name),
|
|
218
|
+
/* @__PURE__ */ React.createElement("span", { className: "min-w-0 flex-1 truncate font-display italic text-[12.5px] text-p-ink-2" }, run.title)
|
|
219
|
+
))
|
|
220
|
+
),
|
|
221
|
+
/* @__PURE__ */ React.createElement("div", { className: "relative", style: { height: totalLanesHeight } }, ticks.map((t, i) => /* @__PURE__ */ React.createElement(
|
|
222
|
+
"span",
|
|
223
|
+
{
|
|
224
|
+
key: i,
|
|
225
|
+
"aria-hidden": true,
|
|
226
|
+
className: "pointer-events-none absolute inset-y-0 w-px bg-p-line/60",
|
|
227
|
+
style: { left: `${t.pct}%` }
|
|
228
|
+
}
|
|
229
|
+
)), rows.map(
|
|
230
|
+
({ run, index }) => index === 0 ? null : /* @__PURE__ */ React.createElement(
|
|
231
|
+
"span",
|
|
232
|
+
{
|
|
233
|
+
key: `sep-${run.id}`,
|
|
234
|
+
"aria-hidden": true,
|
|
235
|
+
className: "pointer-events-none absolute inset-x-0 h-px bg-p-line/60",
|
|
236
|
+
style: { top: index * rowHeight }
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
), nowPct > 0 && nowPct <= 100 ? /* @__PURE__ */ React.createElement(
|
|
240
|
+
"span",
|
|
241
|
+
{
|
|
242
|
+
"aria-hidden": true,
|
|
243
|
+
className: "pointer-events-none absolute inset-y-0 w-[2px] -translate-x-1/2 bg-p-accent/15",
|
|
244
|
+
style: { left: `${nowPct}%` }
|
|
245
|
+
}
|
|
246
|
+
) : null, /* @__PURE__ */ React.createElement(
|
|
247
|
+
"svg",
|
|
248
|
+
{
|
|
249
|
+
"aria-hidden": true,
|
|
250
|
+
className: "pointer-events-none absolute inset-0 h-full w-full overflow-visible"
|
|
251
|
+
},
|
|
252
|
+
/* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement(
|
|
253
|
+
"marker",
|
|
254
|
+
{
|
|
255
|
+
id: "swarm-dep-arrow",
|
|
256
|
+
viewBox: "0 0 6 6",
|
|
257
|
+
refX: "5",
|
|
258
|
+
refY: "3",
|
|
259
|
+
markerWidth: "6",
|
|
260
|
+
markerHeight: "6",
|
|
261
|
+
orient: "auto-start-reverse"
|
|
262
|
+
},
|
|
263
|
+
/* @__PURE__ */ React.createElement("path", { d: "M 0 0 L 6 3 L 0 6 z", className: "fill-p-ink-3" })
|
|
264
|
+
)),
|
|
265
|
+
deps.map((d, i) => /* @__PURE__ */ React.createElement(DependencyPath, { key: i, dep: d, rowHeight }))
|
|
266
|
+
), rows.map(({ run, leftPct, widthPct, index }) => {
|
|
267
|
+
const isLive = run.status === "running" || run.status === "pending";
|
|
268
|
+
const fillPct = run.progress !== void 0 ? Math.max(0, Math.min(1, run.progress)) * 100 : isLive ? 0 : 100;
|
|
269
|
+
const top = index * rowHeight + (rowHeight - 14) / 2;
|
|
270
|
+
return /* @__PURE__ */ React.createElement(
|
|
271
|
+
"button",
|
|
272
|
+
{
|
|
273
|
+
key: `bar-${run.id}`,
|
|
274
|
+
type: "button",
|
|
275
|
+
onClick: onOpen ? () => onOpen(run.id) : void 0,
|
|
276
|
+
className: [
|
|
277
|
+
"absolute overflow-hidden rounded-[3px] transition-[box-shadow,transform]",
|
|
278
|
+
onOpen ? "cursor-pointer hover:scale-y-110" : "cursor-default",
|
|
279
|
+
run.status === "running" ? "shadow-[0_0_18px_-4px_rgba(226,115,61,0.5)]" : ""
|
|
280
|
+
].join(" "),
|
|
281
|
+
style: {
|
|
282
|
+
left: `${leftPct}%`,
|
|
283
|
+
width: `${widthPct}%`,
|
|
284
|
+
top,
|
|
285
|
+
height: 14,
|
|
286
|
+
minWidth: 4
|
|
287
|
+
},
|
|
288
|
+
title: `${run.agent.displayName || run.agent.name} \xB7 ${run.title}`
|
|
289
|
+
},
|
|
290
|
+
isLive ? /* @__PURE__ */ React.createElement(
|
|
291
|
+
"span",
|
|
292
|
+
{
|
|
293
|
+
"aria-hidden": true,
|
|
294
|
+
className: "absolute inset-0 bg-p-warm"
|
|
295
|
+
}
|
|
296
|
+
) : /* @__PURE__ */ React.createElement(
|
|
297
|
+
"span",
|
|
298
|
+
{
|
|
299
|
+
"aria-hidden": true,
|
|
300
|
+
className: ["absolute inset-0", STATUS_FILL[run.status]].join(" ")
|
|
301
|
+
}
|
|
302
|
+
),
|
|
303
|
+
isLive ? /* @__PURE__ */ React.createElement(
|
|
304
|
+
"span",
|
|
305
|
+
{
|
|
306
|
+
"aria-hidden": true,
|
|
307
|
+
className: "absolute inset-y-0 left-0 bg-p-accent transition-[width] duration-500 ease-out",
|
|
308
|
+
style: { width: `${fillPct}%` }
|
|
309
|
+
}
|
|
310
|
+
) : null
|
|
311
|
+
);
|
|
312
|
+
}), nowPct > 0 && nowPct <= 100 ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
313
|
+
"span",
|
|
314
|
+
{
|
|
315
|
+
"aria-hidden": true,
|
|
316
|
+
className: "pointer-events-none absolute inset-y-0 w-px bg-p-accent",
|
|
317
|
+
style: { left: `${nowPct}%` }
|
|
318
|
+
}
|
|
319
|
+
), /* @__PURE__ */ React.createElement(
|
|
320
|
+
"span",
|
|
321
|
+
{
|
|
322
|
+
"aria-hidden": true,
|
|
323
|
+
className: "pointer-events-none absolute -top-3 -translate-x-1/2 rounded-full bg-p-accent px-1.5 py-[1px] font-mono text-[8.5px] font-bold uppercase tracking-[0.22em] text-white shadow-[0_2px_8px_rgba(226,115,61,0.45)]",
|
|
324
|
+
style: { left: `${nowPct}%` }
|
|
325
|
+
},
|
|
326
|
+
"now"
|
|
327
|
+
)) : null)
|
|
328
|
+
),
|
|
329
|
+
/* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between border-t border-p-line px-5 py-2 font-mono text-[9.5px] uppercase tracking-[0.18em] text-p-ink-3 tabular-nums" }, /* @__PURE__ */ React.createElement("span", null, "start \xB7 ", formatClock(originMs)), /* @__PURE__ */ React.createElement(Legend, null), /* @__PURE__ */ React.createElement("span", null, "end \xB7 ", formatClock(endMs)))
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
function Tally({
|
|
333
|
+
tone,
|
|
334
|
+
value,
|
|
335
|
+
label,
|
|
336
|
+
pulse
|
|
337
|
+
}) {
|
|
338
|
+
const fg = tone === "accent" ? "text-p-accent" : tone === "green" ? "text-p-green" : "text-[#E63946]";
|
|
339
|
+
const dot = tone === "accent" ? "bg-p-accent" : tone === "green" ? "bg-p-green" : "bg-[#E63946]";
|
|
340
|
+
return /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1.5" }, /* @__PURE__ */ React.createElement("span", { className: "relative inline-flex size-2" }, pulse && value > 0 ? /* @__PURE__ */ React.createElement(
|
|
341
|
+
"span",
|
|
342
|
+
{
|
|
343
|
+
"aria-hidden": true,
|
|
344
|
+
className: ["absolute inset-0 rounded-full opacity-60 animate-ping", dot].join(" ")
|
|
345
|
+
}
|
|
346
|
+
) : null, /* @__PURE__ */ React.createElement(
|
|
347
|
+
"span",
|
|
348
|
+
{
|
|
349
|
+
"aria-hidden": true,
|
|
350
|
+
className: ["relative inline-block size-2 rounded-full", dot].join(" ")
|
|
351
|
+
}
|
|
352
|
+
)), /* @__PURE__ */ React.createElement("span", { className: ["font-display text-[14px] font-bold tabular-nums leading-none", fg].join(" ") }, value), /* @__PURE__ */ React.createElement("span", null, label));
|
|
353
|
+
}
|
|
354
|
+
function Legend() {
|
|
355
|
+
const items = [
|
|
356
|
+
{ key: "running", cls: "bg-p-accent", label: "running" },
|
|
357
|
+
{ key: "done", cls: "bg-p-green", label: "done" },
|
|
358
|
+
{ key: "failed", cls: "bg-[#E63946]", label: "failed" },
|
|
359
|
+
{ key: "cancelled", cls: "bg-p-ink-3/40", label: "cancelled" }
|
|
360
|
+
];
|
|
361
|
+
return /* @__PURE__ */ React.createElement("span", { className: "hidden items-center gap-3 md:inline-flex" }, items.map((it) => /* @__PURE__ */ React.createElement("span", { key: it.key, className: "inline-flex items-center gap-1.5" }, /* @__PURE__ */ React.createElement("span", { "aria-hidden": true, className: ["h-1.5 w-3 rounded-sm", it.cls].join(" ") }), /* @__PURE__ */ React.createElement("span", null, it.label))));
|
|
362
|
+
}
|
|
363
|
+
function DependencyPath({
|
|
364
|
+
dep,
|
|
365
|
+
rowHeight
|
|
366
|
+
}) {
|
|
367
|
+
const fromY = dep.fromIndex * rowHeight + rowHeight / 2;
|
|
368
|
+
const toY = dep.toIndex * rowHeight + rowHeight / 2;
|
|
369
|
+
const horizontalGap = Math.max(2, Math.abs(dep.toPct - dep.fromPct) / 3);
|
|
370
|
+
const c1 = `${dep.fromPct + horizontalGap}% ${fromY}`;
|
|
371
|
+
const c2 = `${dep.toPct - horizontalGap}% ${toY}`;
|
|
372
|
+
const d = `M ${dep.fromPct}% ${fromY} C ${c1}, ${c2}, ${dep.toPct}% ${toY}`;
|
|
373
|
+
return /* @__PURE__ */ React.createElement("g", null, /* @__PURE__ */ React.createElement(
|
|
374
|
+
"path",
|
|
375
|
+
{
|
|
376
|
+
d,
|
|
377
|
+
fill: "none",
|
|
378
|
+
strokeWidth: "1.25",
|
|
379
|
+
className: "stroke-p-ink-3/70",
|
|
380
|
+
markerEnd: "url(#swarm-dep-arrow)"
|
|
381
|
+
}
|
|
382
|
+
), /* @__PURE__ */ React.createElement(
|
|
383
|
+
"circle",
|
|
384
|
+
{
|
|
385
|
+
cx: `${dep.fromPct}%`,
|
|
386
|
+
cy: fromY,
|
|
387
|
+
r: "1.6",
|
|
388
|
+
className: "fill-p-ink-3"
|
|
389
|
+
}
|
|
390
|
+
));
|
|
391
|
+
}
|
|
392
|
+
function pickStep(spanSec) {
|
|
393
|
+
if (spanSec <= 30) return 5;
|
|
394
|
+
if (spanSec <= 120) return 15;
|
|
395
|
+
if (spanSec <= 600) return 60;
|
|
396
|
+
if (spanSec <= 3600) return 300;
|
|
397
|
+
return 900;
|
|
398
|
+
}
|
|
399
|
+
function formatTick(seconds) {
|
|
400
|
+
if (seconds === 0) return "0";
|
|
401
|
+
if (seconds < 60) return `${Math.round(seconds)}s`;
|
|
402
|
+
if (seconds < 3600) return `${Math.round(seconds / 60)}m`;
|
|
403
|
+
return `${(seconds / 3600).toFixed(1)}h`;
|
|
404
|
+
}
|
|
405
|
+
function formatClock(ms) {
|
|
406
|
+
return new Date(ms).toLocaleTimeString(void 0, {
|
|
407
|
+
hour: "2-digit",
|
|
408
|
+
minute: "2-digit",
|
|
409
|
+
second: "2-digit"
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
export {
|
|
413
|
+
SwarmTimeline
|
|
414
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { WorkspaceTask, WorkspaceMission, SwarmAgentRef } from './types.js';
|
|
4
|
+
|
|
5
|
+
interface TaskWorkspaceSidebarLabels {
|
|
6
|
+
title: string;
|
|
7
|
+
searchPlaceholder: string;
|
|
8
|
+
collapseSidebar: string;
|
|
9
|
+
openKanban: string;
|
|
10
|
+
closeKanban: string;
|
|
11
|
+
loading: string;
|
|
12
|
+
noTasks: string;
|
|
13
|
+
orphanGroup: string;
|
|
14
|
+
draftBadge: string;
|
|
15
|
+
expand: string;
|
|
16
|
+
collapse: string;
|
|
17
|
+
}
|
|
18
|
+
declare const defaultTaskWorkspaceSidebarLabels: TaskWorkspaceSidebarLabels;
|
|
19
|
+
interface PlanOnlyTaskShape {
|
|
20
|
+
title: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
assignTo?: string;
|
|
23
|
+
}
|
|
24
|
+
interface RenderLinkArgs {
|
|
25
|
+
href: string;
|
|
26
|
+
className?: string;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
onClick?: () => void;
|
|
29
|
+
ariaLabel?: string;
|
|
30
|
+
}
|
|
31
|
+
type RenderLinkFn = (args: RenderLinkArgs) => ReactNode;
|
|
32
|
+
interface TaskWorkspaceSidebarProps {
|
|
33
|
+
/** Runtime tasks — what the orchestrator has actually instantiated. */
|
|
34
|
+
tasks: WorkspaceTask[];
|
|
35
|
+
/** Missions — used to group runtime tasks and to surface plan-only
|
|
36
|
+
* tasks from each mission's `data` blob. */
|
|
37
|
+
missions: WorkspaceMission[];
|
|
38
|
+
/** Agent refs for avatar rendering. */
|
|
39
|
+
agents?: SwarmAgentRef[];
|
|
40
|
+
/** Whether the data is still loading — controls empty/skeleton state. */
|
|
41
|
+
isLoading?: boolean;
|
|
42
|
+
/** Currently-active runtime task id (highlighted row). */
|
|
43
|
+
activeRuntimeId?: string;
|
|
44
|
+
/** Currently-active plan-only task — `{ missionId, title }`. */
|
|
45
|
+
activePlanRef?: {
|
|
46
|
+
missionId: string;
|
|
47
|
+
title: string;
|
|
48
|
+
};
|
|
49
|
+
/** Currently-active mission (highlighted group header). */
|
|
50
|
+
activeMissionId?: string;
|
|
51
|
+
/** Sidebar collapse state — typically driven by a parent shell. */
|
|
52
|
+
closed: boolean;
|
|
53
|
+
onCollapse: () => void;
|
|
54
|
+
/** Kanban toggle state — a sidebar header button flips this. */
|
|
55
|
+
kanbanView: boolean;
|
|
56
|
+
onToggleKanban: () => void;
|
|
57
|
+
/** Compute hrefs. The package doesn't decide URL conventions. */
|
|
58
|
+
runtimeTaskHref: (taskId: string) => string;
|
|
59
|
+
planTaskHref: (missionId: string, title: string) => string;
|
|
60
|
+
missionHref: (missionId: string) => string;
|
|
61
|
+
/** Render an anchor — wire to your router's `<Link>`. */
|
|
62
|
+
renderLink: RenderLinkFn;
|
|
63
|
+
/** Override the built-in mission-data parser. Default reads
|
|
64
|
+
* `JSON.parse(data).tasks[]`. */
|
|
65
|
+
parsePlan?: (raw: string | undefined | null) => PlanOnlyTaskShape[];
|
|
66
|
+
labels?: Partial<TaskWorkspaceSidebarLabels>;
|
|
67
|
+
className?: string;
|
|
68
|
+
}
|
|
69
|
+
declare function TaskWorkspaceSidebar({ tasks, missions, agents, isLoading, activeRuntimeId, activePlanRef, activeMissionId, closed, onCollapse, kanbanView, onToggleKanban, runtimeTaskHref, planTaskHref, missionHref, renderLink, parsePlan, labels, className, }: TaskWorkspaceSidebarProps): react.JSX.Element;
|
|
70
|
+
|
|
71
|
+
export { type PlanOnlyTaskShape, type RenderLinkArgs, type RenderLinkFn, TaskWorkspaceSidebar, type TaskWorkspaceSidebarLabels, type TaskWorkspaceSidebarProps, defaultTaskWorkspaceSidebarLabels };
|