@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,125 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { RotateCcw, Square } from "lucide-react";
|
|
3
|
+
import {
|
|
4
|
+
defaultSwarmLabels
|
|
5
|
+
} from "./types";
|
|
6
|
+
import { elapsedSeconds } from "./lib/format";
|
|
7
|
+
const DOT = {
|
|
8
|
+
scheduled: "bg-[#7B3FE4]",
|
|
9
|
+
pending: "bg-p-ink-3",
|
|
10
|
+
running: "bg-p-accent",
|
|
11
|
+
review: "bg-[#2B44FF]",
|
|
12
|
+
done: "bg-p-green",
|
|
13
|
+
failed: "bg-[#E63946]",
|
|
14
|
+
cancelled: "bg-p-ink-3/50"
|
|
15
|
+
};
|
|
16
|
+
function SwarmRunRow({
|
|
17
|
+
run,
|
|
18
|
+
now,
|
|
19
|
+
renderAvatar,
|
|
20
|
+
onCancel,
|
|
21
|
+
onRetry,
|
|
22
|
+
onOpen,
|
|
23
|
+
active,
|
|
24
|
+
labels,
|
|
25
|
+
className
|
|
26
|
+
}) {
|
|
27
|
+
const L = { ...defaultSwarmLabels, ...labels };
|
|
28
|
+
const clock = now ?? Date.now();
|
|
29
|
+
const elapsed = elapsedSeconds(run.startedAt, run.finishedAt, clock);
|
|
30
|
+
const isLive = run.status === "running" || run.status === "pending";
|
|
31
|
+
const showCancel = onCancel && isLive;
|
|
32
|
+
const showRetry = onRetry && (run.status === "failed" || run.status === "cancelled");
|
|
33
|
+
const glyph = run.agent.glyph || (run.agent.displayName || run.agent.name).charAt(0).toUpperCase();
|
|
34
|
+
return /* @__PURE__ */ React.createElement(
|
|
35
|
+
"div",
|
|
36
|
+
{
|
|
37
|
+
role: onOpen ? "button" : void 0,
|
|
38
|
+
tabIndex: onOpen ? 0 : void 0,
|
|
39
|
+
onClick: onOpen ? () => onOpen(run.id) : void 0,
|
|
40
|
+
onKeyDown: onOpen ? (e) => {
|
|
41
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
onOpen(run.id);
|
|
44
|
+
}
|
|
45
|
+
} : void 0,
|
|
46
|
+
className: [
|
|
47
|
+
"group relative flex items-center gap-3 border-b border-p-line/70 px-3 py-1.5 transition-colors",
|
|
48
|
+
active ? "bg-p-warm" : onOpen ? "cursor-pointer hover:bg-p-warm/60" : "",
|
|
49
|
+
className || ""
|
|
50
|
+
].join(" ")
|
|
51
|
+
},
|
|
52
|
+
/* @__PURE__ */ React.createElement("span", { className: "relative inline-flex size-2 shrink-0" }, /* @__PURE__ */ React.createElement(
|
|
53
|
+
"span",
|
|
54
|
+
{
|
|
55
|
+
"aria-hidden": true,
|
|
56
|
+
className: [
|
|
57
|
+
"absolute inset-0 rounded-full",
|
|
58
|
+
DOT[run.status],
|
|
59
|
+
run.status === "running" ? "animate-ping opacity-60" : ""
|
|
60
|
+
].join(" ")
|
|
61
|
+
}
|
|
62
|
+
), /* @__PURE__ */ React.createElement(
|
|
63
|
+
"span",
|
|
64
|
+
{
|
|
65
|
+
"aria-hidden": true,
|
|
66
|
+
className: ["relative inline-block size-2 rounded-full", DOT[run.status]].join(" ")
|
|
67
|
+
}
|
|
68
|
+
)),
|
|
69
|
+
renderAvatar ? renderAvatar(run.agent) : /* @__PURE__ */ React.createElement(
|
|
70
|
+
"span",
|
|
71
|
+
{
|
|
72
|
+
"aria-hidden": true,
|
|
73
|
+
className: "grid size-[18px] shrink-0 place-items-center rounded font-display text-[10px] font-bold text-white",
|
|
74
|
+
style: { background: run.agent.color || "#999" }
|
|
75
|
+
},
|
|
76
|
+
glyph
|
|
77
|
+
),
|
|
78
|
+
/* @__PURE__ */ React.createElement("span", { className: "w-[68px] shrink-0 truncate font-mono text-[10.5px] font-bold uppercase tracking-[0.14em] text-p-ink" }, run.agent.displayName || run.agent.name),
|
|
79
|
+
/* @__PURE__ */ React.createElement("span", { className: "min-w-0 flex-1 truncate font-body text-[12.5px] leading-tight text-p-ink" }, run.title),
|
|
80
|
+
run.lastLog ? /* @__PURE__ */ React.createElement("span", { className: "hidden min-w-0 max-w-[28%] truncate font-mono text-[10.5px] text-p-ink-3 md:inline" }, run.lastLog) : null,
|
|
81
|
+
/* @__PURE__ */ React.createElement("span", { className: "w-12 shrink-0 text-right font-mono text-[10.5px] tabular-nums text-p-ink-3" }, run.startedAt ? isLive ? L.startedAgo(elapsed) : L.duration(elapsed) : "\u2014"),
|
|
82
|
+
/* @__PURE__ */ React.createElement("span", { className: "w-14 shrink-0 text-right" }, showCancel ? /* @__PURE__ */ React.createElement(
|
|
83
|
+
"button",
|
|
84
|
+
{
|
|
85
|
+
type: "button",
|
|
86
|
+
onClick: (e) => {
|
|
87
|
+
e.stopPropagation();
|
|
88
|
+
onCancel(run.id);
|
|
89
|
+
},
|
|
90
|
+
className: "inline-flex items-center gap-1 rounded px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase tracking-[0.12em] text-p-ink-3 opacity-0 transition-all cursor-pointer group-hover:opacity-100 hover:bg-p-surface hover:text-[#E63946]"
|
|
91
|
+
},
|
|
92
|
+
/* @__PURE__ */ React.createElement(Square, { className: "size-2.5" }),
|
|
93
|
+
L.cancel
|
|
94
|
+
) : showRetry ? /* @__PURE__ */ React.createElement(
|
|
95
|
+
"button",
|
|
96
|
+
{
|
|
97
|
+
type: "button",
|
|
98
|
+
onClick: (e) => {
|
|
99
|
+
e.stopPropagation();
|
|
100
|
+
onRetry(run.id);
|
|
101
|
+
},
|
|
102
|
+
className: "inline-flex items-center gap-1 rounded px-1.5 py-0.5 font-mono text-[10px] font-bold uppercase tracking-[0.12em] text-p-ink-3 opacity-0 transition-all cursor-pointer group-hover:opacity-100 hover:bg-p-surface hover:text-p-ink"
|
|
103
|
+
},
|
|
104
|
+
/* @__PURE__ */ React.createElement(RotateCcw, { className: "size-2.5" }),
|
|
105
|
+
L.retry
|
|
106
|
+
) : null),
|
|
107
|
+
run.progress !== void 0 && isLive ? /* @__PURE__ */ React.createElement(
|
|
108
|
+
"span",
|
|
109
|
+
{
|
|
110
|
+
"aria-hidden": true,
|
|
111
|
+
className: "absolute inset-x-0 bottom-0 h-px bg-p-warm"
|
|
112
|
+
},
|
|
113
|
+
/* @__PURE__ */ React.createElement(
|
|
114
|
+
"span",
|
|
115
|
+
{
|
|
116
|
+
className: "block h-full bg-p-accent transition-[width] duration-500",
|
|
117
|
+
style: { width: `${Math.max(0, Math.min(100, run.progress * 100))}%` }
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
) : null
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
export {
|
|
124
|
+
SwarmRunRow
|
|
125
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Skeleton loaders for orchestrator surfaces — mirror the layout of
|
|
5
|
+
* `<SwarmRunDetail/>` and `<SwarmRunActivityStream/>` so the loading
|
|
6
|
+
* state feels native rather than a generic spinner.
|
|
7
|
+
*
|
|
8
|
+
* Pattern stolen from `@lumea/chat`'s ChatSkeleton: a `<Bone/>` primitive
|
|
9
|
+
* + composed silhouettes of the real components. Pure presentation, no
|
|
10
|
+
* data dependencies — drop in while the SwarmRun is fetching.
|
|
11
|
+
*/
|
|
12
|
+
interface SwarmRunDetailSkeletonProps {
|
|
13
|
+
/** Mirror the variant prop of `<SwarmRunDetail/>` so the silhouette
|
|
14
|
+
* matches the consumer's chosen layout. Default `"tabs"`. */
|
|
15
|
+
variant?: "tabs" | "flat";
|
|
16
|
+
/** Hide the tools row so the silhouette matches `hideTools` consumers. */
|
|
17
|
+
hideTools?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function SwarmRunDetailSkeleton({ variant, hideTools, className, }: SwarmRunDetailSkeletonProps): react.JSX.Element;
|
|
21
|
+
interface SwarmRunActivityStreamSkeletonProps {
|
|
22
|
+
/** How many event rows to fake. Default `4`. */
|
|
23
|
+
count?: number;
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
declare function SwarmRunActivityStreamSkeleton({ count, className, }: SwarmRunActivityStreamSkeletonProps): react.JSX.Element;
|
|
27
|
+
|
|
28
|
+
export { SwarmRunActivityStreamSkeleton, type SwarmRunActivityStreamSkeletonProps, SwarmRunDetailSkeleton, type SwarmRunDetailSkeletonProps };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
function Bone({
|
|
2
|
+
width,
|
|
3
|
+
height = 14,
|
|
4
|
+
rounded
|
|
5
|
+
}) {
|
|
6
|
+
return /* @__PURE__ */ React.createElement(
|
|
7
|
+
"div",
|
|
8
|
+
{
|
|
9
|
+
className: "bg-p-warm animate-pulse",
|
|
10
|
+
style: {
|
|
11
|
+
width,
|
|
12
|
+
height,
|
|
13
|
+
borderRadius: rounded === "full" ? 9999 : rounded ?? (height > 20 ? 8 : 4)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
function SwarmRunDetailSkeleton({
|
|
19
|
+
variant = "tabs",
|
|
20
|
+
hideTools = false,
|
|
21
|
+
className
|
|
22
|
+
}) {
|
|
23
|
+
return /* @__PURE__ */ React.createElement(
|
|
24
|
+
"section",
|
|
25
|
+
{
|
|
26
|
+
"aria-busy": true,
|
|
27
|
+
"aria-label": "Loading run",
|
|
28
|
+
className: [
|
|
29
|
+
"flex h-full min-h-0 flex-col overflow-hidden rounded-2xl border border-p-line bg-p-surface",
|
|
30
|
+
className || ""
|
|
31
|
+
].join(" ")
|
|
32
|
+
},
|
|
33
|
+
/* @__PURE__ */ React.createElement("header", { className: "flex shrink-0 items-center gap-2 border-b border-p-line px-4 py-3" }, /* @__PURE__ */ React.createElement("div", { className: "size-6 shrink-0 rounded-md bg-p-warm animate-pulse" }), /* @__PURE__ */ React.createElement(Bone, { width: "9rem", height: 13 }), /* @__PURE__ */ React.createElement("span", { className: "ml-auto inline-flex items-center gap-1" }, /* @__PURE__ */ React.createElement(Bone, { width: 56, height: 20, rounded: 6 }), /* @__PURE__ */ React.createElement("div", { className: "size-7 rounded-md bg-p-warm animate-pulse" }))),
|
|
34
|
+
/* @__PURE__ */ React.createElement("div", { className: "grid shrink-0 grid-cols-4 gap-3 border-b border-p-line px-4 py-3" }, Array.from({ length: 4 }, (_, i) => /* @__PURE__ */ React.createElement("div", { key: i, className: "flex flex-col gap-1.5" }, /* @__PURE__ */ React.createElement(Bone, { width: "3.5rem", height: 9 }), /* @__PURE__ */ React.createElement(Bone, { width: "4.5rem", height: 16 })))),
|
|
35
|
+
/* @__PURE__ */ React.createElement("div", { className: "shrink-0 border-b border-p-line px-4 py-3" }, /* @__PURE__ */ React.createElement(Bone, { width: "2.5rem", height: 9 }), /* @__PURE__ */ React.createElement("div", { className: "mt-2 flex flex-col gap-1.5" }, /* @__PURE__ */ React.createElement(Bone, { width: "92%", height: 14 }), /* @__PURE__ */ React.createElement(Bone, { width: "74%", height: 14 }))),
|
|
36
|
+
variant === "tabs" ? /* @__PURE__ */ React.createElement(TabsBodySkeleton, { hideTools }) : /* @__PURE__ */ React.createElement(FlatBodySkeleton, { hideTools })
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
function TabsBodySkeleton({ hideTools }) {
|
|
40
|
+
const tabCount = hideTools ? 2 : 3;
|
|
41
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex shrink-0 items-center gap-1 border-b border-p-line px-3 py-2" }, Array.from({ length: tabCount }, (_, i) => /* @__PURE__ */ React.createElement(Bone, { key: i, width: "5rem", height: 20, rounded: 6 }))), /* @__PURE__ */ React.createElement("div", { className: "min-h-0 flex-1 overflow-hidden px-4 py-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-2" }, /* @__PURE__ */ React.createElement(Bone, { width: "88%" }), /* @__PURE__ */ React.createElement(Bone, { width: "76%" }), /* @__PURE__ */ React.createElement(Bone, { width: "92%" }), /* @__PURE__ */ React.createElement(Bone, { width: "60%" }), /* @__PURE__ */ React.createElement(Bone, { width: "80%" }))));
|
|
42
|
+
}
|
|
43
|
+
function FlatBodySkeleton({ hideTools }) {
|
|
44
|
+
return /* @__PURE__ */ React.createElement("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 py-4" }, /* @__PURE__ */ React.createElement(FlatSectionSkeleton, { lines: 3 }), !hideTools ? /* @__PURE__ */ React.createElement(FlatSectionSkeleton, { lines: 2, chips: true }) : null, /* @__PURE__ */ React.createElement(FlatSectionSkeleton, { lines: 4 }));
|
|
45
|
+
}
|
|
46
|
+
function FlatSectionSkeleton({
|
|
47
|
+
lines,
|
|
48
|
+
chips
|
|
49
|
+
}) {
|
|
50
|
+
const widths = ["88%", "76%", "92%", "60%", "82%"];
|
|
51
|
+
return /* @__PURE__ */ React.createElement("div", { className: "border-b border-p-line/60 py-3 last:border-0" }, /* @__PURE__ */ React.createElement(Bone, { width: "3.5rem", height: 9 }), /* @__PURE__ */ React.createElement("div", { className: "mt-2 flex flex-col gap-1.5" }, chips ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-1.5" }, Array.from({ length: lines * 2 }, (_, i) => /* @__PURE__ */ React.createElement(Bone, { key: i, width: "6rem", height: 22, rounded: 8 }))) : Array.from({ length: lines }, (_, i) => /* @__PURE__ */ React.createElement(Bone, { key: i, width: widths[i % widths.length] }))));
|
|
52
|
+
}
|
|
53
|
+
function SwarmRunActivityStreamSkeleton({
|
|
54
|
+
count = 4,
|
|
55
|
+
className
|
|
56
|
+
}) {
|
|
57
|
+
return /* @__PURE__ */ React.createElement(
|
|
58
|
+
"ol",
|
|
59
|
+
{
|
|
60
|
+
"aria-busy": true,
|
|
61
|
+
"aria-label": "Loading activity",
|
|
62
|
+
className: [
|
|
63
|
+
"flex flex-col divide-y divide-p-line/60",
|
|
64
|
+
className || ""
|
|
65
|
+
].join(" ")
|
|
66
|
+
},
|
|
67
|
+
Array.from({ length: count }, (_, i) => /* @__PURE__ */ React.createElement(ActivityRowSkeleton, { key: i, index: i }))
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
function ActivityRowSkeleton({ index }) {
|
|
71
|
+
const bodyWidths = ["88%", "62%", "94%", "70%", "78%"];
|
|
72
|
+
const showAgent = index % 2 === 0;
|
|
73
|
+
return /* @__PURE__ */ React.createElement("li", { className: "grid grid-cols-[80px_24px_minmax(0,1fr)] items-start gap-3 py-3" }, /* @__PURE__ */ React.createElement(Bone, { width: "3.25rem", height: 9 }), /* @__PURE__ */ React.createElement("div", { className: "mt-1 size-5 rounded-full bg-p-warm animate-pulse" }), /* @__PURE__ */ React.createElement("div", { className: "min-w-0 flex flex-col gap-1.5" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-baseline gap-2" }, /* @__PURE__ */ React.createElement(Bone, { width: "4.5rem", height: 9 }), showAgent ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1" }, /* @__PURE__ */ React.createElement("div", { className: "size-3.5 rounded-[3px] bg-p-warm animate-pulse" }), /* @__PURE__ */ React.createElement(Bone, { width: "4rem", height: 9 })) : null), /* @__PURE__ */ React.createElement(Bone, { width: bodyWidths[index % bodyWidths.length], height: 13 }), index % 3 === 0 ? /* @__PURE__ */ React.createElement(Bone, { width: "55%", height: 13 }) : null));
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
SwarmRunActivityStreamSkeleton,
|
|
77
|
+
SwarmRunDetailSkeleton
|
|
78
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { SwarmCounts, SwarmLabels } from './types.js';
|
|
4
|
+
|
|
5
|
+
interface SwarmStatusBarProps {
|
|
6
|
+
counts: SwarmCounts;
|
|
7
|
+
onCancelAll?: () => void;
|
|
8
|
+
onRetryFailed?: () => void;
|
|
9
|
+
trailing?: ReactNode;
|
|
10
|
+
labels?: Partial<SwarmLabels>;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function SwarmStatusBar({ counts, onCancelAll, onRetryFailed, trailing, labels, className, }: SwarmStatusBarProps): react.JSX.Element;
|
|
14
|
+
|
|
15
|
+
export { SwarmStatusBar, type SwarmStatusBarProps };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { RotateCcw, Square } from "lucide-react";
|
|
3
|
+
import {
|
|
4
|
+
defaultSwarmLabels
|
|
5
|
+
} from "./types";
|
|
6
|
+
function SwarmStatusBar({
|
|
7
|
+
counts,
|
|
8
|
+
onCancelAll,
|
|
9
|
+
onRetryFailed,
|
|
10
|
+
trailing,
|
|
11
|
+
labels,
|
|
12
|
+
className
|
|
13
|
+
}) {
|
|
14
|
+
const L = { ...defaultSwarmLabels, ...labels };
|
|
15
|
+
const showCancelAll = onCancelAll && counts.running + counts.pending > 0;
|
|
16
|
+
const showRetryFailed = onRetryFailed && counts.failed > 0;
|
|
17
|
+
return /* @__PURE__ */ React.createElement(
|
|
18
|
+
"div",
|
|
19
|
+
{
|
|
20
|
+
className: [
|
|
21
|
+
"flex flex-wrap items-center gap-3 rounded-xl border border-p-line bg-p-surface px-4 py-2.5",
|
|
22
|
+
className || ""
|
|
23
|
+
].join(" ")
|
|
24
|
+
},
|
|
25
|
+
/* @__PURE__ */ React.createElement(Stat, { label: L.running, value: counts.running, tone: "accent", pulse: counts.running > 0 }),
|
|
26
|
+
/* @__PURE__ */ React.createElement("span", { className: "h-4 w-px bg-p-line" }),
|
|
27
|
+
/* @__PURE__ */ React.createElement(Stat, { label: L.done, value: counts.done, tone: "green" }),
|
|
28
|
+
/* @__PURE__ */ React.createElement("span", { className: "h-4 w-px bg-p-line" }),
|
|
29
|
+
/* @__PURE__ */ React.createElement(Stat, { label: L.failed, value: counts.failed, tone: "red" }),
|
|
30
|
+
/* @__PURE__ */ React.createElement("span", { className: "ml-auto inline-flex items-center gap-1.5" }, showCancelAll ? /* @__PURE__ */ React.createElement(
|
|
31
|
+
"button",
|
|
32
|
+
{
|
|
33
|
+
type: "button",
|
|
34
|
+
onClick: onCancelAll,
|
|
35
|
+
className: "inline-flex items-center gap-1 rounded-md px-2 py-1 font-mono text-[10.5px] font-bold uppercase tracking-[0.16em] text-p-ink-3 transition-colors cursor-pointer hover:bg-p-warm hover:text-[#E63946]"
|
|
36
|
+
},
|
|
37
|
+
/* @__PURE__ */ React.createElement(Square, { className: "size-2.5" }),
|
|
38
|
+
L.cancelAll
|
|
39
|
+
) : null, showRetryFailed ? /* @__PURE__ */ React.createElement(
|
|
40
|
+
"button",
|
|
41
|
+
{
|
|
42
|
+
type: "button",
|
|
43
|
+
onClick: onRetryFailed,
|
|
44
|
+
className: "inline-flex items-center gap-1 rounded-md px-2 py-1 font-mono text-[10.5px] font-bold uppercase tracking-[0.16em] text-p-ink-3 transition-colors cursor-pointer hover:bg-p-warm hover:text-p-ink"
|
|
45
|
+
},
|
|
46
|
+
/* @__PURE__ */ React.createElement(RotateCcw, { className: "size-2.5" }),
|
|
47
|
+
L.retryFailed
|
|
48
|
+
) : null, trailing)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
function Stat({
|
|
52
|
+
label,
|
|
53
|
+
value,
|
|
54
|
+
tone,
|
|
55
|
+
pulse
|
|
56
|
+
}) {
|
|
57
|
+
const fg = tone === "accent" ? "text-p-accent" : tone === "green" ? "text-p-green" : "text-[#E63946]";
|
|
58
|
+
const dot = tone === "accent" ? "bg-p-accent" : tone === "green" ? "bg-p-green" : "bg-[#E63946]";
|
|
59
|
+
return /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1.5" }, /* @__PURE__ */ React.createElement("span", { className: "relative inline-flex size-2 shrink-0" }, pulse ? /* @__PURE__ */ React.createElement(
|
|
60
|
+
"span",
|
|
61
|
+
{
|
|
62
|
+
className: [
|
|
63
|
+
"absolute inset-0 rounded-full opacity-60",
|
|
64
|
+
dot,
|
|
65
|
+
"animate-ping"
|
|
66
|
+
].join(" "),
|
|
67
|
+
"aria-hidden": true
|
|
68
|
+
}
|
|
69
|
+
) : null, /* @__PURE__ */ React.createElement(
|
|
70
|
+
"span",
|
|
71
|
+
{
|
|
72
|
+
className: ["relative inline-block size-2 rounded-full", dot].join(" "),
|
|
73
|
+
"aria-hidden": true
|
|
74
|
+
}
|
|
75
|
+
)), /* @__PURE__ */ React.createElement("span", { className: ["font-display text-[14px] font-bold tabular-nums", fg].join(" ") }, value), /* @__PURE__ */ React.createElement("span", { className: "font-mono text-[10px] font-bold uppercase tracking-[0.18em] text-p-ink-3" }, label));
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
SwarmStatusBar
|
|
79
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { SwarmRunStatus } from './types.js';
|
|
3
|
+
|
|
4
|
+
interface SwarmStatusPillProps {
|
|
5
|
+
status: SwarmRunStatus;
|
|
6
|
+
label?: string;
|
|
7
|
+
size?: "sm" | "md";
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
declare function SwarmStatusPill({ status, label, size, className, }: SwarmStatusPillProps): react.JSX.Element;
|
|
11
|
+
|
|
12
|
+
export { SwarmStatusPill, type SwarmStatusPillProps };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
CalendarClock,
|
|
4
|
+
CheckCircle2,
|
|
5
|
+
Circle,
|
|
6
|
+
Loader2,
|
|
7
|
+
Pause,
|
|
8
|
+
Scale,
|
|
9
|
+
XCircle
|
|
10
|
+
} from "lucide-react";
|
|
11
|
+
const TONE = {
|
|
12
|
+
scheduled: {
|
|
13
|
+
fg: "text-[#7B3FE4]",
|
|
14
|
+
bg: "bg-[#F3EEFF]",
|
|
15
|
+
ring: "ring-[#7B3FE4]/30"
|
|
16
|
+
},
|
|
17
|
+
pending: { fg: "text-p-ink-3", bg: "bg-p-warm", ring: "ring-p-line" },
|
|
18
|
+
running: {
|
|
19
|
+
fg: "text-p-accent",
|
|
20
|
+
bg: "bg-p-accent-light",
|
|
21
|
+
ring: "ring-p-accent/30"
|
|
22
|
+
},
|
|
23
|
+
review: {
|
|
24
|
+
fg: "text-[#2B44FF]",
|
|
25
|
+
bg: "bg-[#EEF2FF]",
|
|
26
|
+
ring: "ring-[#2B44FF]/30"
|
|
27
|
+
},
|
|
28
|
+
done: {
|
|
29
|
+
fg: "text-p-green",
|
|
30
|
+
bg: "bg-p-green-light",
|
|
31
|
+
ring: "ring-p-green/30"
|
|
32
|
+
},
|
|
33
|
+
failed: {
|
|
34
|
+
fg: "text-[#E63946]",
|
|
35
|
+
bg: "bg-[#FEF2F2]",
|
|
36
|
+
ring: "ring-[#E63946]/30"
|
|
37
|
+
},
|
|
38
|
+
cancelled: { fg: "text-p-ink-3", bg: "bg-p-warm", ring: "ring-p-line" }
|
|
39
|
+
};
|
|
40
|
+
const ICON = {
|
|
41
|
+
scheduled: CalendarClock,
|
|
42
|
+
pending: Pause,
|
|
43
|
+
running: Loader2,
|
|
44
|
+
review: Scale,
|
|
45
|
+
done: CheckCircle2,
|
|
46
|
+
failed: XCircle,
|
|
47
|
+
cancelled: Circle
|
|
48
|
+
};
|
|
49
|
+
function SwarmStatusPill({
|
|
50
|
+
status,
|
|
51
|
+
label,
|
|
52
|
+
size = "md",
|
|
53
|
+
className
|
|
54
|
+
}) {
|
|
55
|
+
const Icon = ICON[status];
|
|
56
|
+
const tone = TONE[status];
|
|
57
|
+
const text = label ?? status;
|
|
58
|
+
const px = size === "sm" ? "px-1.5 py-[1px]" : "px-2 py-0.5";
|
|
59
|
+
const txt = size === "sm" ? "text-[9.5px]" : "text-[10px]";
|
|
60
|
+
const icon = size === "sm" ? "size-2.5" : "size-3";
|
|
61
|
+
return /* @__PURE__ */ React.createElement(
|
|
62
|
+
"span",
|
|
63
|
+
{
|
|
64
|
+
className: [
|
|
65
|
+
"inline-flex items-center gap-1 rounded-full ring-1 font-mono font-bold uppercase tracking-[0.16em]",
|
|
66
|
+
tone.fg,
|
|
67
|
+
tone.bg,
|
|
68
|
+
tone.ring,
|
|
69
|
+
px,
|
|
70
|
+
txt,
|
|
71
|
+
className || ""
|
|
72
|
+
].join(" ")
|
|
73
|
+
},
|
|
74
|
+
/* @__PURE__ */ React.createElement(
|
|
75
|
+
Icon,
|
|
76
|
+
{
|
|
77
|
+
"aria-hidden": true,
|
|
78
|
+
className: [icon, status === "running" ? "animate-spin" : ""].join(" ")
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
text
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
SwarmStatusPill
|
|
86
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { SwarmRun, SwarmLabels } from './types.js';
|
|
4
|
+
|
|
5
|
+
interface SwarmTimelineProps {
|
|
6
|
+
runs: SwarmRun[];
|
|
7
|
+
/** Live clock — pass `state.now` from `useSwarm()` to make active
|
|
8
|
+
* bars extend in real time and the now-cursor advance. */
|
|
9
|
+
now?: number;
|
|
10
|
+
renderAvatar?: (agent: SwarmRun["agent"]) => ReactNode;
|
|
11
|
+
onOpen?: (id: string) => void;
|
|
12
|
+
/** Pixel height per row. Default 32. */
|
|
13
|
+
rowHeight?: number;
|
|
14
|
+
/** Width reserved for the agent + goal label column. Default 264. */
|
|
15
|
+
labelWidth?: number;
|
|
16
|
+
labels?: Partial<SwarmLabels>;
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
declare function SwarmTimeline({ runs, now, renderAvatar, onOpen, rowHeight, labelWidth, labels, className, }: SwarmTimelineProps): react.JSX.Element;
|
|
20
|
+
|
|
21
|
+
export { SwarmTimeline, type SwarmTimelineProps };
|