@tangle-network/charts 2.0.16 → 2.0.18
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 +2 -0
- package/dist/{AgentGraphCanvas-B74GN1o6.js → AgentGraphCanvas-C5Ee8Z6q.js} +61 -42
- package/dist/{elk-loader-CrLO557o.d.ts → elk-loader-uOcDmUG_.d.ts} +3 -3
- package/dist/{latest-events-iiWwpIck.js → latest-events-CmbyqEIe.js} +26 -29
- package/dist/react/agent-graph/index.d.ts +1 -1
- package/dist/react/agent-graph/index.js +6 -6
- package/dist/react/agent-graph/presentation.d.ts +1 -1
- package/dist/react/agent-graph.css +31 -5
- package/dist/react/index.d.ts +62 -1
- package/dist/react/index.js +238 -3
- package/dist/react/live.css +39 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Benchmark figures as static SVG and HTML tables, plus optional React timing prim
|
|
|
4
4
|
|
|
5
5
|
The React entry also exports `DayBars`, `HBars`, `Histogram`, `Lines`, `Meter`, `Spark`, and `Gantt` for live operational pages. Import `@tangle-network/charts/react/live.css` for their layout and set `--chart-live-*` theme tokens on an ancestor. Data, state colors, destination links, labels, and interpretation remain with the caller. `DayBars` accepts an `onOpen(index)` action with keyboard activation.
|
|
6
6
|
|
|
7
|
+
`VersionGraph` and `layoutVersionGraph` from `@tangle-network/charts/react` render version histories with lane and merge geometry, controlled selection, filtering, keyboard navigation and bounded rows. Import `react/live.css` and Brand tokens as above. Producers supply commit identities, parents, lane labels and scored chips; Charts preserves those facts and owns drawing, arrival motion and reduced-motion behavior.
|
|
8
|
+
|
|
7
9
|
`FlowWaterfall` from `@tangle-network/charts/react` is the shared live trace inspector: an agent timeline (one row per agent, elbow handoffs, hatched waiting) and a virtualized span waterfall over one clock. It owns hierarchy layout, folding, search, playback, zoom, span cards and running-span growth. Producers own identity, `parentId`, explicit `status` and observed totals. Import `@tangle-network/charts/react/flow-waterfall.css`; its `fw-*` classes and six-field summary support hosts without a Tailwind build. Every color, radius and shadow in it is a Brand role (text, card, hairline, well, chart grid, status inks, the categorical tones for agents), so the host loads `@tangle-network/brand` `tokens.css` (or `legacy-light.css`) and sets its theme; the stylesheet has no color of its own. The React-free root exports the `FlowSpan` and `FlowTrace` types.
|
|
8
10
|
|
|
9
11
|
`FlowWaterfall` accepts optional `graph` and `latestEvents` React nodes. Supplying `graph` adds Graph to its existing Agents/Steps view control, including `defaultView="graph"`. The trace clock and controlled selection survive switches. Graph hides waterfall-only controls; `latestEvents` stays mounted in the header across all views.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as statusMeta, O as createLucideIcon, T as RunStatusIcon } from "./flow-waterfall-model-DvrgPLGS.js";
|
|
2
|
-
import { a as LIVE_AGENT_CARD, c as AgentMark, d as Wrench, n as AGENT_CARD, o as buildAgentTopology, s as layoutAgents, t as LatestEvents } from "./latest-events-
|
|
2
|
+
import { a as LIVE_AGENT_CARD, c as AgentMark, d as Wrench, n as AGENT_CARD, o as buildAgentTopology, s as layoutAgents, t as LatestEvents } from "./latest-events-CmbyqEIe.js";
|
|
3
3
|
import { C as fmtCost, E as fmtTokenTotal, T as fmtEventTime, _ as syncAmbient, b as useMotionAllowed, g as ambientStyle, h as agentQuietActivity, i as SELECTION_OUTLINE, n as RESTING_SHADOW, o as useColorMode, r as edgeColor, t as RESTING_BORDER, u as shortModel, v as tokenMilliseconds, x as useRunNow, y as useAttention } from "./graph-style-fealnLLn.js";
|
|
4
4
|
import { t as MOTION_CONTROL } from "./motion-DILryczn.js";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -116,13 +116,10 @@ function AgentCardView({ agent, selected, direction, now: fixed, onOpen, parentL
|
|
|
116
116
|
/* @__PURE__ */ jsxs("div", {
|
|
117
117
|
className: "flex min-w-0 items-center gap-2",
|
|
118
118
|
children: [
|
|
119
|
-
/* @__PURE__ */ jsx(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
layout: "tree",
|
|
124
|
-
size: 18
|
|
125
|
-
})
|
|
119
|
+
/* @__PURE__ */ jsx(AgentMark, {
|
|
120
|
+
agent,
|
|
121
|
+
layout: "tree",
|
|
122
|
+
size: 24
|
|
126
123
|
}),
|
|
127
124
|
/* @__PURE__ */ jsxs("span", {
|
|
128
125
|
className: "min-w-0 flex-1 truncate text-sm font-semibold leading-[18px]",
|
|
@@ -132,35 +129,50 @@ function AgentCardView({ agent, selected, direction, now: fixed, onOpen, parentL
|
|
|
132
129
|
}), agent.label]
|
|
133
130
|
}),
|
|
134
131
|
/* @__PURE__ */ jsx("span", {
|
|
135
|
-
className: "text-
|
|
132
|
+
className: "text-[13px] text-muted-foreground tabular-nums",
|
|
136
133
|
title: agent.costUsd == null ? "Cost not reported" : "Cost",
|
|
137
134
|
children: fmtCost(agent.costUsd ?? void 0) ?? "—"
|
|
138
135
|
})
|
|
139
136
|
]
|
|
140
137
|
}),
|
|
141
138
|
/* @__PURE__ */ jsx("p", {
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
"data-agent-identity": "",
|
|
140
|
+
className: "m-0 truncate text-[13px] leading-[18px] text-muted-foreground",
|
|
141
|
+
title: [agent.harness, agent.model].filter(Boolean).join(" · "),
|
|
142
|
+
children: [agent.harness, agent.model ? shortModel(agent.model) : null].filter(Boolean).join(" · ") || "Identity not recorded"
|
|
143
|
+
}),
|
|
144
|
+
agent.status === "failed" && /* @__PURE__ */ jsx("p", {
|
|
145
|
+
className: "m-0 line-clamp-2 text-[13px] leading-4",
|
|
146
|
+
children: agent.result ?? agent.turn ?? "Failure cause not recorded"
|
|
144
147
|
}),
|
|
145
148
|
/* @__PURE__ */ jsxs("div", {
|
|
146
|
-
className: "mt-auto flex items-center justify-between gap-1 text-
|
|
149
|
+
className: "mt-auto flex items-center justify-between gap-1 text-[13px]",
|
|
147
150
|
style: { color: quiet ? "var(--surface-warning-text)" : s.color },
|
|
148
|
-
children: [
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ jsxs("span", {
|
|
153
|
+
className: "flex items-center gap-1",
|
|
154
|
+
children: [/* @__PURE__ */ jsx(StatusGlyph, {
|
|
155
|
+
status: agent.status,
|
|
156
|
+
size: 16
|
|
157
|
+
}), quiet?.label ?? s.label]
|
|
158
|
+
}),
|
|
159
|
+
/* @__PURE__ */ jsx("span", {
|
|
160
|
+
"data-agent-tokens": "",
|
|
161
|
+
title: agent.tokens == null ? "Tokens not recorded" : "Tokens",
|
|
162
|
+
className: "text-muted-foreground tabular-nums whitespace-nowrap",
|
|
163
|
+
children: fmtTokenTotal(agent.tokens) ?? "—"
|
|
164
|
+
}),
|
|
165
|
+
agent.status === "needs-you" && /* @__PURE__ */ jsx("button", {
|
|
166
|
+
type: "button",
|
|
167
|
+
className: "ag-live-respond nodrag nopan",
|
|
168
|
+
disabled: !onOpen,
|
|
169
|
+
onClick: (event) => {
|
|
170
|
+
event.stopPropagation();
|
|
171
|
+
onOpen?.();
|
|
172
|
+
},
|
|
173
|
+
children: "Respond"
|
|
174
|
+
})
|
|
175
|
+
]
|
|
164
176
|
})
|
|
165
177
|
]
|
|
166
178
|
});
|
|
@@ -174,14 +186,14 @@ function AgentCard({ data }) {
|
|
|
174
186
|
function AgentFrameBox({ data }) {
|
|
175
187
|
const { frame, direction } = data;
|
|
176
188
|
return /* @__PURE__ */ jsxs("div", {
|
|
177
|
-
className: "relative h-full w-full rounded-2xl border",
|
|
189
|
+
className: "ag-team-frame relative h-full w-full rounded-2xl border",
|
|
178
190
|
style: {
|
|
179
191
|
background: "color-mix(in srgb, hsl(var(--muted-foreground)) 7%, transparent)",
|
|
180
192
|
borderColor: "hsl(var(--foreground) / 0.08)"
|
|
181
193
|
},
|
|
182
194
|
children: [/* @__PURE__ */ jsx(Handles, { direction }), /* @__PURE__ */ jsxs("button", {
|
|
183
195
|
type: "button",
|
|
184
|
-
className:
|
|
196
|
+
className: `nodrag nopan absolute top-2 flex h-16 items-center justify-center gap-2 rounded text-sm text-muted-foreground hover:bg-muted focus-visible:outline ${data.groupParents ? "right-3 w-8 flex-col" : "left-3 right-3 justify-start"}`,
|
|
185
197
|
"aria-label": `${frame.collapsed ? "Expand" : "Collapse"} ${data.label} team`,
|
|
186
198
|
"aria-expanded": !frame.collapsed,
|
|
187
199
|
title: `${data.label} · ${frame.group.memberIds.length} agents`,
|
|
@@ -189,10 +201,14 @@ function AgentFrameBox({ data }) {
|
|
|
189
201
|
event.stopPropagation();
|
|
190
202
|
data.onToggle();
|
|
191
203
|
},
|
|
192
|
-
children: [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
204
|
+
children: [
|
|
205
|
+
/* @__PURE__ */ jsx(ChevronDown, {
|
|
206
|
+
size: 16,
|
|
207
|
+
className: frame.collapsed ? "-rotate-90" : void 0
|
|
208
|
+
}),
|
|
209
|
+
!data.groupParents && /* @__PURE__ */ jsxs("strong", { children: [data.label, " team"] }),
|
|
210
|
+
/* @__PURE__ */ jsx("span", { children: frame.group.memberIds.length })
|
|
211
|
+
]
|
|
196
212
|
})]
|
|
197
213
|
});
|
|
198
214
|
}
|
|
@@ -528,7 +544,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
528
544
|
const columnRef = useRef(null);
|
|
529
545
|
if (canvas && !narrow && columnRef.current?.key !== columnKey) columnRef.current = {
|
|
530
546
|
key: columnKey,
|
|
531
|
-
columns: Math.max(1, Math.min(6, Math.floor((canvas.width - 32) / (card.width + 10))))
|
|
547
|
+
columns: Math.max(1, Math.min(6, Math.floor((canvas.width - 32 - (!live && direction === "LR" ? card.width + 70 : 0)) / (card.width + 10))))
|
|
532
548
|
};
|
|
533
549
|
const columns = columnRef.current?.columns ?? 1;
|
|
534
550
|
const layoutKey = JSON.stringify([
|
|
@@ -542,7 +558,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
542
558
|
useEffect(() => {
|
|
543
559
|
if (narrow) return;
|
|
544
560
|
let active = true;
|
|
545
|
-
layoutAgents(topology, direction, columns, card, collapsed).then((layout) => {
|
|
561
|
+
layoutAgents(topology, direction, columns, card, collapsed, live).then((layout) => {
|
|
546
562
|
if (active) setPlaced({
|
|
547
563
|
key: layoutKey,
|
|
548
564
|
layout
|
|
@@ -583,6 +599,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
583
599
|
data: {
|
|
584
600
|
frame,
|
|
585
601
|
direction,
|
|
602
|
+
groupParents: live,
|
|
586
603
|
label: byId.get(frame.group.parentId)?.label ?? "Agent",
|
|
587
604
|
onToggle: () => {
|
|
588
605
|
if (!frame.collapsed && selectedId && frame.group.memberIds.includes(selectedId)) onSelect?.(frame.group.parentId);
|
|
@@ -599,7 +616,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
599
616
|
const agent = byId.get(id);
|
|
600
617
|
const position = layout.positions.get(id);
|
|
601
618
|
if (!agent || !position) continue;
|
|
602
|
-
const selected = selectedId === id, owned = layout.frames.find((frame) => frame.group.parentId === id);
|
|
619
|
+
const selected = selectedId === id, owned = live && layout.frames.find((frame) => frame.group.parentId === id);
|
|
603
620
|
const width = owned ? owned.width - 64 : card.width, height = owned ? 64 : card.height;
|
|
604
621
|
const measured = rfRef.current?.getInternalNode(id)?.measured;
|
|
605
622
|
cards.push({
|
|
@@ -646,7 +663,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
646
663
|
const out = [];
|
|
647
664
|
const seen = /* @__PURE__ */ new Set();
|
|
648
665
|
for (const s of topology.spawns) {
|
|
649
|
-
const source = topology.groups.find((group) => group.parentId === s.from)?.id
|
|
666
|
+
const source = live && topology.groups.find((group) => group.parentId === s.from)?.id || s.from, target = topology.groupOf.get(s.to) ?? s.to;
|
|
650
667
|
if (source === target) continue;
|
|
651
668
|
const id = `spawn:${source}:${target}`;
|
|
652
669
|
if (seen.has(id)) continue;
|
|
@@ -720,7 +737,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
720
737
|
}, [narrow]);
|
|
721
738
|
const layoutIdentity = layout ? `${layoutKey}:${layout.columns}` : null;
|
|
722
739
|
const cameraKey = `${direction}:${topology.key}`;
|
|
723
|
-
const minZoom = layout && canvas ? Math.max(.02, Math.min(.2, (canvas.width - 32) / layout.width, (canvas.height - 32) / layout.height)) : .2;
|
|
740
|
+
const minZoom = !live ? 1 : layout && canvas ? Math.max(.02, Math.min(.2, (canvas.width - 32) / layout.width, (canvas.height - 32) / layout.height)) : .2;
|
|
724
741
|
useEffect(() => {
|
|
725
742
|
const inst = rfRef.current;
|
|
726
743
|
if (!ready || !inst || !layoutIdentity || !canvas) return;
|
|
@@ -888,7 +905,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
888
905
|
wrapperRef.current = el;
|
|
889
906
|
measureCanvas(el);
|
|
890
907
|
},
|
|
891
|
-
className: `agent-graph ag-live relative flex flex-col ${className ?? ""}`,
|
|
908
|
+
className: `agent-graph ag-live${live ? "" : " ag-topology"} relative flex flex-col ${className ?? ""}`,
|
|
892
909
|
tabIndex: -1,
|
|
893
910
|
onKeyDownCapture: (event) => {
|
|
894
911
|
if (event.target instanceof HTMLElement && event.target.closest("[data-agent-metrics]")) return;
|
|
@@ -949,7 +966,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
949
966
|
select(agent.id);
|
|
950
967
|
}
|
|
951
968
|
},
|
|
952
|
-
children: live
|
|
969
|
+
children: live ? /* @__PURE__ */ jsx(LiveAgentCardView, {
|
|
953
970
|
agent,
|
|
954
971
|
parentLabel: byId.get(parentOf.get(agent.id) ?? "")?.label,
|
|
955
972
|
selected: selectedId === agent.id,
|
|
@@ -1060,7 +1077,9 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
1060
1077
|
position: "bottom-right",
|
|
1061
1078
|
fitViewOptions: {
|
|
1062
1079
|
padding: `${FIT_PADDING}px`,
|
|
1063
|
-
|
|
1080
|
+
minZoom,
|
|
1081
|
+
maxZoom: 1,
|
|
1082
|
+
nodes: live ? void 0 : topology.agentIds.slice(0, 2).map((id) => ({ id }))
|
|
1064
1083
|
}
|
|
1065
1084
|
})
|
|
1066
1085
|
]
|
|
@@ -67,8 +67,8 @@ interface AgentGraphEdge {
|
|
|
67
67
|
type AgentGraphDirection = "TB" | "LR";
|
|
68
68
|
/** Every agent card's size. Fixed, so a status or turn update never moves a card. */
|
|
69
69
|
declare const AGENT_CARD: {
|
|
70
|
-
readonly width:
|
|
71
|
-
readonly height:
|
|
70
|
+
readonly width: 232;
|
|
71
|
+
readonly height: 120;
|
|
72
72
|
};
|
|
73
73
|
/** A live card: room for the header, a two-line thinking line and the last three events. Fixed for the same reason. */
|
|
74
74
|
declare const LIVE_AGENT_CARD: {
|
|
@@ -125,7 +125,7 @@ interface AgentLayout {
|
|
|
125
125
|
height: number;
|
|
126
126
|
}
|
|
127
127
|
/** Lay the run out with every grid `columns` wide. */
|
|
128
|
-
declare function layoutAgents(topology: AgentTopology, direction: AgentGraphDirection, columns: number, card?: CardSize, collapsed?: ReadonlySet<string
|
|
128
|
+
declare function layoutAgents(topology: AgentTopology, direction: AgentGraphDirection, columns: number, card?: CardSize, collapsed?: ReadonlySet<string>, groupParents?: boolean): Promise<AgentLayout>;
|
|
129
129
|
//#endregion
|
|
130
130
|
//#region src/react/agent-graph/format.d.ts
|
|
131
131
|
/**
|
|
@@ -85,8 +85,8 @@ function ToolMark({ tool, label, size }) {
|
|
|
85
85
|
//#region src/react/agent-graph/agent-graph-layout.ts
|
|
86
86
|
/** Every agent card's size. Fixed, so a status or turn update never moves a card. */
|
|
87
87
|
const AGENT_CARD = {
|
|
88
|
-
width:
|
|
89
|
-
height:
|
|
88
|
+
width: 232,
|
|
89
|
+
height: 120
|
|
90
90
|
};
|
|
91
91
|
/** A live card: room for the header, a two-line thinking line and the last three events. Fixed for the same reason. */
|
|
92
92
|
const LIVE_AGENT_CARD = {
|
|
@@ -160,35 +160,29 @@ function gridSize(members, columns, card, collapsed = false) {
|
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
/** The boxes the layouter places: each ungrouped agent, and each grid as one block. */
|
|
163
|
-
function layoutUnits(topology, columns, card, collapsed) {
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
for (const id of topology.agentIds) if (!topology.groupOf.has(id) && !ownerGroup.has(id)) units.push({
|
|
163
|
+
function layoutUnits(topology, columns, card, collapsed, groupParents) {
|
|
164
|
+
const ownerGroup = new Map(groupParents ? topology.groups.map((group) => [group.parentId, group.id]) : []);
|
|
165
|
+
const units = topology.agentIds.filter((id) => !topology.groupOf.has(id) && !ownerGroup.has(id)).map((id) => ({
|
|
167
166
|
id,
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
});
|
|
167
|
+
...card
|
|
168
|
+
}));
|
|
169
|
+
units.push(...topology.groups.map((group) => ({
|
|
170
|
+
id: group.id,
|
|
171
|
+
...gridSize(group.memberIds.length, columns, card, collapsed.has(group.id))
|
|
172
|
+
})));
|
|
175
173
|
const unitOf = (id) => topology.groupOf.get(id) ?? ownerGroup.get(id) ?? id;
|
|
176
174
|
const seen = /* @__PURE__ */ new Set();
|
|
177
|
-
const links = [];
|
|
178
|
-
for (const s of topology.spawns) {
|
|
179
|
-
const from = unitOf(s.from);
|
|
180
|
-
const to = unitOf(s.to);
|
|
181
|
-
const key = `${from}\u0000${to}`;
|
|
182
|
-
if (from === to || seen.has(key)) continue;
|
|
183
|
-
seen.add(key);
|
|
184
|
-
links.push({
|
|
185
|
-
from,
|
|
186
|
-
to
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
175
|
return {
|
|
190
176
|
units,
|
|
191
|
-
links
|
|
177
|
+
links: topology.spawns.flatMap((spawn) => {
|
|
178
|
+
const from = unitOf(spawn.from), to = unitOf(spawn.to), key = `${from}\u0000${to}`;
|
|
179
|
+
if (from === to || seen.has(key)) return [];
|
|
180
|
+
seen.add(key);
|
|
181
|
+
return [{
|
|
182
|
+
from,
|
|
183
|
+
to
|
|
184
|
+
}];
|
|
185
|
+
})
|
|
192
186
|
};
|
|
193
187
|
}
|
|
194
188
|
async function placeWithElk(units, links, direction) {
|
|
@@ -271,8 +265,8 @@ function placeByDepth(units, links, direction) {
|
|
|
271
265
|
return at;
|
|
272
266
|
}
|
|
273
267
|
/** Lay the run out with every grid `columns` wide. */
|
|
274
|
-
async function layoutAgents(topology, direction, columns, card = AGENT_CARD, collapsed = /* @__PURE__ */ new Set()) {
|
|
275
|
-
const { units, links } = layoutUnits(topology, columns, card, collapsed);
|
|
268
|
+
async function layoutAgents(topology, direction, columns, card = AGENT_CARD, collapsed = /* @__PURE__ */ new Set(), groupParents = true) {
|
|
269
|
+
const { units, links } = layoutUnits(topology, columns, card, collapsed, groupParents);
|
|
276
270
|
const placed = await placeWithElk(units, links, direction) ?? placeByDepth(units, links, direction);
|
|
277
271
|
const positions = /* @__PURE__ */ new Map();
|
|
278
272
|
const frames = [];
|
|
@@ -301,9 +295,12 @@ async function layoutAgents(topology, direction, columns, card = AGENT_CARD, col
|
|
|
301
295
|
...size,
|
|
302
296
|
collapsed: closed
|
|
303
297
|
});
|
|
304
|
-
positions.set(g.parentId, {
|
|
298
|
+
positions.set(g.parentId, groupParents ? {
|
|
305
299
|
x: origin.x + FRAME_PAD,
|
|
306
300
|
y: origin.y + 8
|
|
301
|
+
} : {
|
|
302
|
+
...positions.get(g.parentId),
|
|
303
|
+
y: origin.y
|
|
307
304
|
});
|
|
308
305
|
if (closed) continue;
|
|
309
306
|
const cols = Math.max(1, Math.min(columns, g.memberIds.length));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as AGENT_CARD, B as AgentLeafGroup, C as shortModel, D as fmtEventTime, E as fmtDuration, F as AgentGraphEdgeKind, G as LIVE_AGENT_CARD, H as CardSize, I as AgentGraphEvent, K as buildAgentTopology, L as AgentGraphNode, M as AgentGraphActivity, N as AgentGraphDirection, O as fmtTokenTotal, P as AgentGraphEdge, R as AgentGraphStatus, S as parseActionPath, T as fmtCost, U as FRAME_CAPTION, V as AgentTopology, W as GRID_MIN_LEAVES, _ as useRunNow, a as RESTING_SHADOW, b as actionPathLabel, c as motionAllowed, d as agentQuietActivity, f as ambientStyle, g as useMotionAllowed, h as useAttention, i as RESTING_BORDER, j as AgentFrame, k as fmtTokens, l as useColorMode, m as tokenMilliseconds, n as GraphErrorBoundary, o as edgeColor, p as syncAmbient, q as layoutAgents, r as retryImport, s as SELECTION_OUTLINE, t as loadElk, u as activityTiming, v as ActionPathParts, w as clampPreview, x as humanizeIdentifier, y as LeadCase, z as AgentLayout } from "../../elk-loader-
|
|
1
|
+
import { A as AGENT_CARD, B as AgentLeafGroup, C as shortModel, D as fmtEventTime, E as fmtDuration, F as AgentGraphEdgeKind, G as LIVE_AGENT_CARD, H as CardSize, I as AgentGraphEvent, K as buildAgentTopology, L as AgentGraphNode, M as AgentGraphActivity, N as AgentGraphDirection, O as fmtTokenTotal, P as AgentGraphEdge, R as AgentGraphStatus, S as parseActionPath, T as fmtCost, U as FRAME_CAPTION, V as AgentTopology, W as GRID_MIN_LEAVES, _ as useRunNow, a as RESTING_SHADOW, b as actionPathLabel, c as motionAllowed, d as agentQuietActivity, f as ambientStyle, g as useMotionAllowed, h as useAttention, i as RESTING_BORDER, j as AgentFrame, k as fmtTokens, l as useColorMode, m as tokenMilliseconds, n as GraphErrorBoundary, o as edgeColor, p as syncAmbient, q as layoutAgents, r as retryImport, s as SELECTION_OUTLINE, t as loadElk, u as activityTiming, v as ActionPathParts, w as clampPreview, x as humanizeIdentifier, y as LeadCase, z as AgentLayout } from "../../elk-loader-uOcDmUG_.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
//#region src/react/agent-graph/agent-hub-layout.d.ts
|
|
4
4
|
/** A tool or service an agent calls: drawn as an outer node with its brand mark. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as statusMeta, E as runStatus, O as createLucideIcon, T as RunStatusIcon } from "../../flow-waterfall-model-DvrgPLGS.js";
|
|
2
|
-
import { a as LIVE_AGENT_CARD, c as AgentMark, i as GRID_MIN_LEAVES, l as GraphMarksProvider, n as AGENT_CARD, o as buildAgentTopology, r as FRAME_CAPTION, s as layoutAgents, t as LatestEvents, u as ToolMark } from "../../latest-events-
|
|
2
|
+
import { a as LIVE_AGENT_CARD, c as AgentMark, i as GRID_MIN_LEAVES, l as GraphMarksProvider, n as AGENT_CARD, o as buildAgentTopology, r as FRAME_CAPTION, s as layoutAgents, t as LatestEvents, u as ToolMark } from "../../latest-events-CmbyqEIe.js";
|
|
3
3
|
import { C as fmtCost, D as fmtTokens, E as fmtTokenTotal, S as clampPreview, T as fmtEventTime, _ as syncAmbient, a as motionAllowed, b as useMotionAllowed, c as humanizeIdentifier, d as loadElk, f as GraphErrorBoundary, g as ambientStyle, h as agentQuietActivity, i as SELECTION_OUTLINE, l as parseActionPath, m as activityTiming, n as RESTING_SHADOW, o as useColorMode, p as retryImport, r as edgeColor, s as actionPathLabel, t as RESTING_BORDER, u as shortModel, v as tokenMilliseconds, w as fmtDuration, x as useRunNow, y as useAttention } from "../../graph-style-fealnLLn.js";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { Suspense, lazy, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
@@ -1122,7 +1122,7 @@ function AgentHubCanvas(props) {
|
|
|
1122
1122
|
* taking the pane down.
|
|
1123
1123
|
*/
|
|
1124
1124
|
function lazyCanvas() {
|
|
1125
|
-
return lazy(() => retryImport(() => import("../../AgentGraphCanvas-
|
|
1125
|
+
return lazy(() => retryImport(() => import("../../AgentGraphCanvas-C5Ee8Z6q.js").then((m) => ({ default: m.AgentGraphCanvas }))));
|
|
1126
1126
|
}
|
|
1127
1127
|
/** The agents as a list, for when the canvas cannot load. */
|
|
1128
1128
|
function AgentListFallback({ nodes, selectedId, onSelect, className, onRetry }) {
|
|
@@ -1131,12 +1131,12 @@ function AgentListFallback({ nodes, selectedId, onSelect, className, onRetry })
|
|
|
1131
1131
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
1132
1132
|
className: "mb-1 flex items-center justify-between gap-2",
|
|
1133
1133
|
children: [/* @__PURE__ */ jsx("p", {
|
|
1134
|
-
className: "text-muted-foreground text-
|
|
1134
|
+
className: "text-muted-foreground text-[13px]",
|
|
1135
1135
|
children: "Couldn't draw the graph — showing the agents."
|
|
1136
1136
|
}), /* @__PURE__ */ jsx("button", {
|
|
1137
1137
|
type: "button",
|
|
1138
1138
|
onClick: onRetry,
|
|
1139
|
-
className: "shrink-0 rounded border border-border px-1.5 py-0.5 text-muted-foreground text-
|
|
1139
|
+
className: "shrink-0 rounded border border-border px-1.5 py-0.5 text-muted-foreground text-[13px] transition hover:text-foreground",
|
|
1140
1140
|
children: "Retry"
|
|
1141
1141
|
})]
|
|
1142
1142
|
}), /* @__PURE__ */ jsx("ul", {
|
|
@@ -1145,7 +1145,7 @@ function AgentListFallback({ nodes, selectedId, onSelect, className, onRetry })
|
|
|
1145
1145
|
type: "button",
|
|
1146
1146
|
"aria-pressed": selectedId === n.id,
|
|
1147
1147
|
onClick: () => onSelect?.(n.id),
|
|
1148
|
-
className: "flex w-full items-center justify-between gap-2 rounded px-2 py-1 text-left text-foreground text-
|
|
1148
|
+
className: "flex w-full items-center justify-between gap-2 rounded px-2 py-1 text-left text-foreground text-[13px] hover:bg-muted aria-pressed:bg-muted",
|
|
1149
1149
|
children: [/* @__PURE__ */ jsx("span", {
|
|
1150
1150
|
className: "truncate",
|
|
1151
1151
|
children: n.label
|
|
@@ -1174,7 +1174,7 @@ function AgentGraphTree(props) {
|
|
|
1174
1174
|
}),
|
|
1175
1175
|
children: /* @__PURE__ */ jsx(Suspense, {
|
|
1176
1176
|
fallback: /* @__PURE__ */ jsx("div", {
|
|
1177
|
-
className: `agent-graph flex items-center justify-center rounded-lg border border-border border-dashed text-muted-foreground text-
|
|
1177
|
+
className: `agent-graph flex items-center justify-center rounded-lg border border-border border-dashed text-muted-foreground text-[13px] ${props.className ?? ""}`,
|
|
1178
1178
|
children: "Loading graph…"
|
|
1179
1179
|
}),
|
|
1180
1180
|
children: /* @__PURE__ */ jsx(Canvas, { ...props })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as shortModel, D as fmtEventTime, E as fmtDuration, O as fmtTokenTotal, S as parseActionPath, T as fmtCost, _ as useRunNow, a as RESTING_SHADOW, b as actionPathLabel, c as motionAllowed, d as agentQuietActivity, f as ambientStyle, g as useMotionAllowed, h as useAttention, i as RESTING_BORDER, k as fmtTokens, l as useColorMode, m as tokenMilliseconds, n as GraphErrorBoundary, o as edgeColor, p as syncAmbient, r as retryImport, s as SELECTION_OUTLINE, t as loadElk, u as activityTiming, v as ActionPathParts, w as clampPreview, x as humanizeIdentifier, y as LeadCase } from "../../elk-loader-
|
|
1
|
+
import { C as shortModel, D as fmtEventTime, E as fmtDuration, O as fmtTokenTotal, S as parseActionPath, T as fmtCost, _ as useRunNow, a as RESTING_SHADOW, b as actionPathLabel, c as motionAllowed, d as agentQuietActivity, f as ambientStyle, g as useMotionAllowed, h as useAttention, i as RESTING_BORDER, k as fmtTokens, l as useColorMode, m as tokenMilliseconds, n as GraphErrorBoundary, o as edgeColor, p as syncAmbient, r as retryImport, s as SELECTION_OUTLINE, t as loadElk, u as activityTiming, v as ActionPathParts, w as clampPreview, x as humanizeIdentifier, y as LeadCase } from "../../elk-loader-uOcDmUG_.js";
|
|
2
2
|
//#region src/react/agent-graph/motion.d.ts
|
|
3
3
|
/** Brand owns complete duration/easing roles. Keep properties explicit. */
|
|
4
4
|
export declare const MOTION_CONTROL = "[transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none";
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
--radius-full: 999px;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
@layer base {
|
|
26
|
+
@layer utilities.agent-graph-base {
|
|
27
27
|
@scope (.agent-graph, .ag-root, .ag-latest, [data-agent-metrics]) {
|
|
28
28
|
*, :scope:is(*), ::after, :scope:is(::after), ::before, :scope:is(::before), ::backdrop, :scope:is(::backdrop), ::file-selector-button, :scope:is(::file-selector-button) {
|
|
29
29
|
box-sizing: border-box;
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
|
-
@layer utilities {
|
|
176
|
+
@layer utilities.agent-graph-utilities {
|
|
177
177
|
@scope (.agent-graph, .ag-root, .ag-latest, [data-agent-metrics]) {
|
|
178
178
|
.pointer-events-auto, :scope:is(.pointer-events-auto) {
|
|
179
179
|
pointer-events: auto;
|
|
@@ -210,6 +210,9 @@
|
|
|
210
210
|
.right-3, :scope:is(.right-3) {
|
|
211
211
|
right: calc(var(--spacing) * 3);
|
|
212
212
|
}
|
|
213
|
+
.left-3, :scope:is(.left-3) {
|
|
214
|
+
left: calc(var(--spacing) * 3);
|
|
215
|
+
}
|
|
213
216
|
.container, :scope:is(.container) {
|
|
214
217
|
width: 100%;
|
|
215
218
|
@media (width >= 40rem) {
|
|
@@ -279,9 +282,6 @@
|
|
|
279
282
|
.min-h-0, :scope:is(.min-h-0) {
|
|
280
283
|
min-height: 0px;
|
|
281
284
|
}
|
|
282
|
-
.min-h-4, :scope:is(.min-h-4) {
|
|
283
|
-
min-height: calc(var(--spacing) * 4);
|
|
284
|
-
}
|
|
285
285
|
.min-h-10, :scope:is(.min-h-10) {
|
|
286
286
|
min-height: calc(var(--spacing) * 10);
|
|
287
287
|
}
|
|
@@ -336,6 +336,9 @@
|
|
|
336
336
|
.justify-center, :scope:is(.justify-center) {
|
|
337
337
|
justify-content: center;
|
|
338
338
|
}
|
|
339
|
+
.justify-start, :scope:is(.justify-start) {
|
|
340
|
+
justify-content: flex-start;
|
|
341
|
+
}
|
|
339
342
|
.gap-0\.5, :scope:is(.gap-0\.5) {
|
|
340
343
|
gap: calc(var(--spacing) * 0.5);
|
|
341
344
|
}
|
|
@@ -466,6 +469,9 @@
|
|
|
466
469
|
.text-\[10px\], :scope:is(.text-\[10px\]) {
|
|
467
470
|
font-size: 10px;
|
|
468
471
|
}
|
|
472
|
+
.text-\[13px\], :scope:is(.text-\[13px\]) {
|
|
473
|
+
font-size: 13px;
|
|
474
|
+
}
|
|
469
475
|
.leading-4, :scope:is(.leading-4) {
|
|
470
476
|
--tw-leading: calc(var(--spacing) * 4);
|
|
471
477
|
line-height: calc(var(--spacing) * 4);
|
|
@@ -1568,6 +1574,26 @@ svg.react-flow__connectionline {
|
|
|
1568
1574
|
min-width: 44px;
|
|
1569
1575
|
}
|
|
1570
1576
|
}
|
|
1577
|
+
.ag-topology {
|
|
1578
|
+
--ag-surface: hsl(var(--hsl-raised));
|
|
1579
|
+
--font-size-xs: 13px;
|
|
1580
|
+
--text-xs: 13px;
|
|
1581
|
+
}
|
|
1582
|
+
.ag-topology :is(.text-xs, .ag-parent-label) {
|
|
1583
|
+
font-size: 13px;
|
|
1584
|
+
}
|
|
1585
|
+
.ag-topology .ag-team-frame {
|
|
1586
|
+
background: hsl(var(--hsl-card)) !important;
|
|
1587
|
+
}
|
|
1588
|
+
.ag-topology .ag-agent-card {
|
|
1589
|
+
background: var(--ag-surface);
|
|
1590
|
+
}
|
|
1591
|
+
.ag-topology .ag-agent-card[data-selected] {
|
|
1592
|
+
background: hsl(var(--hsl-raised-2));
|
|
1593
|
+
}
|
|
1594
|
+
.ag-topology .react-flow__node-agentFrame .react-flow__handle-left {
|
|
1595
|
+
top: 40px;
|
|
1596
|
+
}
|
|
1571
1597
|
@property --tw-rotate-x {
|
|
1572
1598
|
syntax: "*";
|
|
1573
1599
|
inherits: false;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -538,4 +538,65 @@ export declare function FlowWaterfall({ trace, live, startedAt, label, defaultCo
|
|
|
538
538
|
/** Generic tool identity by name: a terminal, a file, a pencil, a lens, a globe or a branch, never a vendor mark. */
|
|
539
539
|
export declare function toolGlyph(name: string | undefined): import("react").JSX.Element | undefined;
|
|
540
540
|
//#endregion
|
|
541
|
-
|
|
541
|
+
//#region src/react/version-graph.d.ts
|
|
542
|
+
/** `muted` chips sit beside the title; any other tone gets the second line. */
|
|
543
|
+
/** A status tone from `statusOf`, or a non-status chip: `accent` for a series, `info` and `muted` for plain facts. */
|
|
544
|
+
type VersionGraphTone = 'pass' | 'live' | 'attention' | 'fail' | 'neutral' | 'accent' | 'info' | 'muted';
|
|
545
|
+
interface VersionGraphChip {
|
|
546
|
+
readonly label: string;
|
|
547
|
+
readonly tone?: VersionGraphTone;
|
|
548
|
+
readonly title?: string;
|
|
549
|
+
}
|
|
550
|
+
interface VersionGraphNode {
|
|
551
|
+
readonly id: string;
|
|
552
|
+
/** Parents in the model; one that is not in it (or is hidden) is skipped, and a hidden one passes its own on. */
|
|
553
|
+
readonly parents: readonly string[];
|
|
554
|
+
readonly lane: string;
|
|
555
|
+
readonly at: string | null;
|
|
556
|
+
readonly title: string;
|
|
557
|
+
readonly detail?: string | null;
|
|
558
|
+
/** A style hook and a filter key: `write`, `integrate`, `profile`, `version`, `authored`, `ghost`. */
|
|
559
|
+
readonly kind: string;
|
|
560
|
+
readonly chips?: readonly VersionGraphChip[];
|
|
561
|
+
}
|
|
562
|
+
interface VersionGraphLane {
|
|
563
|
+
readonly id: string;
|
|
564
|
+
readonly label: string;
|
|
565
|
+
readonly detail?: string | null;
|
|
566
|
+
}
|
|
567
|
+
/** Nodes newest first, every child above its parents. */
|
|
568
|
+
interface VersionGraphModel {
|
|
569
|
+
readonly lanes: readonly VersionGraphLane[];
|
|
570
|
+
readonly nodes: readonly VersionGraphNode[];
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Rows in model order, one column per lane that holds a shown node, and one edge from each shown node to each nearest
|
|
574
|
+
* shown ancestor: a hidden node (an integration when integrations are hidden, a commit of a lane filtered out) passes
|
|
575
|
+
* its parents on, so a lane's line stays connected.
|
|
576
|
+
*/
|
|
577
|
+
export declare function layoutVersionGraph(model: VersionGraphModel, shown?: (node: VersionGraphNode) => boolean): {
|
|
578
|
+
rows: {
|
|
579
|
+
node: VersionGraphNode;
|
|
580
|
+
row: number;
|
|
581
|
+
column: number;
|
|
582
|
+
}[];
|
|
583
|
+
edges: {
|
|
584
|
+
from: string;
|
|
585
|
+
to: string;
|
|
586
|
+
fromRow: number;
|
|
587
|
+
fromColumn: number;
|
|
588
|
+
toRow: number;
|
|
589
|
+
toColumn: number;
|
|
590
|
+
}[];
|
|
591
|
+
lanes: VersionGraphLane[];
|
|
592
|
+
};
|
|
593
|
+
export declare function VersionGraph({ model, selected, onSelect, label, shown, maxHeight }: {
|
|
594
|
+
model: VersionGraphModel;
|
|
595
|
+
selected: string | null;
|
|
596
|
+
onSelect: (id: string) => void;
|
|
597
|
+
label: string;
|
|
598
|
+
shown?: (node: VersionGraphNode) => boolean;
|
|
599
|
+
maxHeight?: number;
|
|
600
|
+
}): import("react").JSX.Element;
|
|
601
|
+
//#endregion
|
|
602
|
+
export type { BandKind, ChartBand, ChartMark, DotMapLabel, DotMapProps, DotMapRect, Figure, FigureAtWidth, FigureViewProps, FlowWaterfallProps, FlowWaterfallRow, ViewWindow as FlowWaterfallWindow, GapBand, GapProps, LineSeries, MarkKind, MarkVerdict, MarkerShape, MeterSegment, PointFields, PointGlyph, Refusal, SparklineDirection, SparklineGeometry, SparklineGeometryOptions, SparklineLabelOptions, SparklinePoint, SparklineProps, SparklineValues, StackedBarBucket, StackedBarChartProps, StackedBarSegment, StackedBarSeries, TickOutcome, TimelineLane, TimelineOptions, TimelineSpan, TimelineTick, TimelineViewProps, VersionGraphChip, VersionGraphLane, VersionGraphModel, VersionGraphNode, VersionGraphTone, WaterfallBarProps, WaterfallHeaderProps, WaterfallRowProps, WaterfallSpanGeometry, WaterfallTimeAxisProps, WaterfallWindow, XCategory, YScale, YTarget, YTick };
|
package/dist/react/index.js
CHANGED
|
@@ -3354,7 +3354,7 @@ function useRowMotion(rows, element) {
|
|
|
3354
3354
|
const previous = known.current;
|
|
3355
3355
|
let order = 0;
|
|
3356
3356
|
for (const row of rows) {
|
|
3357
|
-
const isRunning = spanRunning(row.span);
|
|
3357
|
+
const isRunning = !!row.span && spanRunning(row.span);
|
|
3358
3358
|
if (!previous || !previous.has(row.id)) active.current.set(row.id, {
|
|
3359
3359
|
enter: Math.min(order++ * stagger, cap),
|
|
3360
3360
|
until: now + hold + cap
|
|
@@ -3365,7 +3365,7 @@ function useRowMotion(rows, element) {
|
|
|
3365
3365
|
});
|
|
3366
3366
|
}
|
|
3367
3367
|
known.current = new Set(rows.map((row) => row.id));
|
|
3368
|
-
running.current = new Set(rows.filter((row) => spanRunning(row.span)).map((row) => row.id));
|
|
3368
|
+
running.current = new Set(rows.filter((row) => row.span && spanRunning(row.span)).map((row) => row.id));
|
|
3369
3369
|
return new Map(active.current);
|
|
3370
3370
|
}, [
|
|
3371
3371
|
rows,
|
|
@@ -5062,4 +5062,239 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
5062
5062
|
});
|
|
5063
5063
|
}
|
|
5064
5064
|
//#endregion
|
|
5065
|
-
|
|
5065
|
+
//#region src/react/version-graph.tsx
|
|
5066
|
+
/** Recorded version lanes; callers supply their domain model, filters and selection. */
|
|
5067
|
+
/**
|
|
5068
|
+
* Rows in model order, one column per lane that holds a shown node, and one edge from each shown node to each nearest
|
|
5069
|
+
* shown ancestor: a hidden node (an integration when integrations are hidden, a commit of a lane filtered out) passes
|
|
5070
|
+
* its parents on, so a lane's line stays connected.
|
|
5071
|
+
*/
|
|
5072
|
+
function layoutVersionGraph(model, shown = () => true) {
|
|
5073
|
+
const byId = new Map(model.nodes.map((node) => [node.id, node]));
|
|
5074
|
+
const visible = model.nodes.filter(shown);
|
|
5075
|
+
const visibleIds = new Set(visible.map((node) => node.id));
|
|
5076
|
+
const lanes = model.lanes.filter((lane) => visible.some((node) => node.lane === lane.id));
|
|
5077
|
+
for (const node of visible) if (!lanes.some((lane) => lane.id === node.lane)) lanes.push({
|
|
5078
|
+
id: node.lane,
|
|
5079
|
+
label: node.lane
|
|
5080
|
+
});
|
|
5081
|
+
const column = new Map(lanes.map((lane, i) => [lane.id, i]));
|
|
5082
|
+
const rowOf = new Map(visible.map((node, i) => [node.id, i]));
|
|
5083
|
+
const nearest = (id, seen) => {
|
|
5084
|
+
if (seen.has(id)) return [];
|
|
5085
|
+
seen.add(id);
|
|
5086
|
+
if (visibleIds.has(id)) return [id];
|
|
5087
|
+
const node = byId.get(id);
|
|
5088
|
+
return node ? node.parents.flatMap((parent) => nearest(parent, seen)) : [];
|
|
5089
|
+
};
|
|
5090
|
+
const edges = [];
|
|
5091
|
+
for (const node of visible) {
|
|
5092
|
+
const seen = /* @__PURE__ */ new Set([node.id]);
|
|
5093
|
+
const targets = [...new Set(node.parents.flatMap((parent) => nearest(parent, seen)))];
|
|
5094
|
+
for (const target of targets) {
|
|
5095
|
+
const toRow = rowOf.get(target);
|
|
5096
|
+
if (toRow <= rowOf.get(node.id)) continue;
|
|
5097
|
+
edges.push({
|
|
5098
|
+
from: node.id,
|
|
5099
|
+
to: target,
|
|
5100
|
+
fromRow: rowOf.get(node.id),
|
|
5101
|
+
fromColumn: column.get(node.lane),
|
|
5102
|
+
toRow,
|
|
5103
|
+
toColumn: column.get(byId.get(target).lane)
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
return {
|
|
5108
|
+
rows: visible.map((node, i) => ({
|
|
5109
|
+
node,
|
|
5110
|
+
row: i,
|
|
5111
|
+
column: column.get(node.lane)
|
|
5112
|
+
})),
|
|
5113
|
+
edges,
|
|
5114
|
+
lanes
|
|
5115
|
+
};
|
|
5116
|
+
}
|
|
5117
|
+
const WIDE = {
|
|
5118
|
+
column: 16,
|
|
5119
|
+
gutter: 14
|
|
5120
|
+
};
|
|
5121
|
+
const NARROW = {
|
|
5122
|
+
column: 5,
|
|
5123
|
+
gutter: 4
|
|
5124
|
+
};
|
|
5125
|
+
const LINE = 30;
|
|
5126
|
+
const TALL = 54;
|
|
5127
|
+
const BEND = 22;
|
|
5128
|
+
const COLORS = 8;
|
|
5129
|
+
const tall = (node) => (node.chips ?? []).some((chip) => chip.tone && chip.tone !== "muted");
|
|
5130
|
+
function VersionGraph({ model, selected, onSelect, label, shown, maxHeight = 600 }) {
|
|
5131
|
+
const laid = useMemo(() => layoutVersionGraph(model, shown), [model, shown]);
|
|
5132
|
+
const tops = useMemo(() => {
|
|
5133
|
+
const out = [];
|
|
5134
|
+
let y = 0;
|
|
5135
|
+
for (const row of laid.rows) {
|
|
5136
|
+
out.push(y);
|
|
5137
|
+
y += tall(row.node) ? TALL : LINE;
|
|
5138
|
+
}
|
|
5139
|
+
out.push(y);
|
|
5140
|
+
return out;
|
|
5141
|
+
}, [laid]);
|
|
5142
|
+
const height = tops.at(-1) ?? 0;
|
|
5143
|
+
const [measure, measuredWidth] = useWidth();
|
|
5144
|
+
const { column: COLUMN, gutter: GUTTER } = measuredWidth < 700 ? NARROW : WIDE;
|
|
5145
|
+
const width = GUTTER + laid.lanes.length * COLUMN;
|
|
5146
|
+
const x = (column) => GUTTER / 2 + column * COLUMN + COLUMN / 2;
|
|
5147
|
+
const y = (row) => tops[row] + LINE / 2;
|
|
5148
|
+
const path = (fromRow, fromColumn, toRow, toColumn) => {
|
|
5149
|
+
const [x1, y1, x2, y2] = [
|
|
5150
|
+
x(fromColumn),
|
|
5151
|
+
y(fromRow),
|
|
5152
|
+
x(toColumn),
|
|
5153
|
+
y(toRow)
|
|
5154
|
+
];
|
|
5155
|
+
if (x1 === x2) return `M${x1},${y1} V${y2}`;
|
|
5156
|
+
const bend = Math.min(BEND, y2 - y1);
|
|
5157
|
+
return `M${x1},${y1} C${x1},${y1 + bend * .7} ${x2},${y1 + bend * .3} ${x2},${y1 + bend} V${y2}`;
|
|
5158
|
+
};
|
|
5159
|
+
const pane = useRef(null);
|
|
5160
|
+
const focusTarget = useRef(null);
|
|
5161
|
+
const arrived = useRef(/* @__PURE__ */ new Set());
|
|
5162
|
+
const motion = useRowMotion(model.nodes, pane.current);
|
|
5163
|
+
const [scrollTop, setScrollTop] = useState(0);
|
|
5164
|
+
const first = Math.max(0, tops.findIndex((top) => top >= scrollTop) - 7);
|
|
5165
|
+
const after = tops.findIndex((top) => top > scrollTop + maxHeight);
|
|
5166
|
+
const end = after < 0 ? laid.rows.length : Math.min(laid.rows.length, after + 7);
|
|
5167
|
+
const visible = laid.rows.slice(first, end);
|
|
5168
|
+
const focusRow = (index) => {
|
|
5169
|
+
const at = Math.max(0, Math.min(laid.rows.length - 1, index)), top = tops[at] ?? 0;
|
|
5170
|
+
focusTarget.current = laid.rows[at]?.node.id ?? null;
|
|
5171
|
+
if (pane.current) pane.current.scrollTop = top;
|
|
5172
|
+
setScrollTop(top);
|
|
5173
|
+
const target = pane.current?.querySelector(`[data-row="${at}"]`);
|
|
5174
|
+
if (target) {
|
|
5175
|
+
focusTarget.current = null;
|
|
5176
|
+
target.focus();
|
|
5177
|
+
}
|
|
5178
|
+
};
|
|
5179
|
+
useEffect(() => {
|
|
5180
|
+
const at = laid.rows.findIndex((row) => row.node.id === selected), element = pane.current;
|
|
5181
|
+
if (at < 0 || !element) return;
|
|
5182
|
+
const top = tops[at], bottom = tops[at + 1];
|
|
5183
|
+
if (top < element.scrollTop) element.scrollTop = Math.max(0, top - 8);
|
|
5184
|
+
else if (bottom > element.scrollTop + element.clientHeight) element.scrollTop = bottom - element.clientHeight + 8;
|
|
5185
|
+
setScrollTop(element.scrollTop);
|
|
5186
|
+
}, [selected]);
|
|
5187
|
+
const allLanes = useMemo(() => layoutVersionGraph(model).lanes, [model]);
|
|
5188
|
+
const colorOf = new Map(allLanes.map((item, i) => [item.id, i % COLORS]));
|
|
5189
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5190
|
+
ref: measure,
|
|
5191
|
+
className: "tgc-version-graph",
|
|
5192
|
+
"data-version-graph": label,
|
|
5193
|
+
children: laid.rows.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
5194
|
+
className: "ov-empty",
|
|
5195
|
+
children: "Nothing to show with these filters."
|
|
5196
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
5197
|
+
ref: pane,
|
|
5198
|
+
className: "vg-scroll",
|
|
5199
|
+
style: {
|
|
5200
|
+
maxHeight,
|
|
5201
|
+
overflow: "auto"
|
|
5202
|
+
},
|
|
5203
|
+
onScroll: (event) => setScrollTop(event.currentTarget.scrollTop),
|
|
5204
|
+
onKeyDown: (event) => {
|
|
5205
|
+
const at = Number(event.target.closest("[data-row]")?.getAttribute("data-row"));
|
|
5206
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? laid.rows.length - 1 : event.key === "ArrowDown" ? at + 1 : event.key === "ArrowUp" ? at - 1 : null;
|
|
5207
|
+
if (next !== null) {
|
|
5208
|
+
event.preventDefault();
|
|
5209
|
+
focusRow(next);
|
|
5210
|
+
}
|
|
5211
|
+
},
|
|
5212
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
5213
|
+
className: "vg-host",
|
|
5214
|
+
style: { height },
|
|
5215
|
+
children: [/* @__PURE__ */ jsxs("svg", {
|
|
5216
|
+
className: "vg-lines",
|
|
5217
|
+
width,
|
|
5218
|
+
height,
|
|
5219
|
+
"aria-hidden": "true",
|
|
5220
|
+
children: [laid.edges.filter((edge) => y(edge.fromRow) <= scrollTop + maxHeight + TALL && y(edge.toRow) >= scrollTop - TALL).map((edge) => /* @__PURE__ */ jsx("path", {
|
|
5221
|
+
"data-from": edge.from,
|
|
5222
|
+
"data-to": edge.to,
|
|
5223
|
+
className: `vg-c${colorOf.get(laid.lanes[edge.toColumn].id)} ${selected === edge.from || selected === edge.to ? "hot" : ""}`,
|
|
5224
|
+
d: path(edge.fromRow, edge.fromColumn, edge.toRow, edge.toColumn)
|
|
5225
|
+
}, `${edge.from}>${edge.to}`)), visible.map((row) => /* @__PURE__ */ jsx("circle", {
|
|
5226
|
+
className: `vg-dot vg-c${colorOf.get(row.node.lane)} kind-${row.node.kind} ${selected === row.node.id ? "on" : ""}`,
|
|
5227
|
+
cx: x(row.column),
|
|
5228
|
+
cy: y(row.row),
|
|
5229
|
+
r: row.node.kind === "integrate" ? 3 : 5
|
|
5230
|
+
}, row.node.id))]
|
|
5231
|
+
}), /* @__PURE__ */ jsx("ol", {
|
|
5232
|
+
className: "vg-rows",
|
|
5233
|
+
"aria-label": label,
|
|
5234
|
+
style: {
|
|
5235
|
+
paddingLeft: width,
|
|
5236
|
+
paddingTop: tops[first]
|
|
5237
|
+
},
|
|
5238
|
+
children: visible.map((row) => {
|
|
5239
|
+
const node = row.node;
|
|
5240
|
+
const on = selected === node.id;
|
|
5241
|
+
const arriving = motion.get(node.id)?.enter !== void 0 && !arrived.current.has(node.id);
|
|
5242
|
+
const loud = (node.chips ?? []).filter((chip) => chip.tone && chip.tone !== "muted");
|
|
5243
|
+
const quiet = (node.chips ?? []).filter((chip) => !chip.tone || chip.tone === "muted");
|
|
5244
|
+
return /* @__PURE__ */ jsx("li", {
|
|
5245
|
+
"aria-posinset": row.row + 1,
|
|
5246
|
+
"aria-setsize": laid.rows.length,
|
|
5247
|
+
style: { height: tops[row.row + 1] - tops[row.row] },
|
|
5248
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
5249
|
+
ref: (element) => {
|
|
5250
|
+
if (!element) return;
|
|
5251
|
+
if (arriving) arrived.current.add(node.id);
|
|
5252
|
+
if (focusTarget.current === node.id) {
|
|
5253
|
+
focusTarget.current = null;
|
|
5254
|
+
element.focus();
|
|
5255
|
+
}
|
|
5256
|
+
},
|
|
5257
|
+
type: "button",
|
|
5258
|
+
className: `vg-row kind-${node.kind} ${on ? "on" : ""}`,
|
|
5259
|
+
"aria-pressed": on,
|
|
5260
|
+
disabled: node.kind === "ghost",
|
|
5261
|
+
"data-node": node.id,
|
|
5262
|
+
"data-row": row.row,
|
|
5263
|
+
"data-arriving": arriving || void 0,
|
|
5264
|
+
onClick: () => onSelect(node.id),
|
|
5265
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
5266
|
+
className: "vg-line",
|
|
5267
|
+
children: [
|
|
5268
|
+
/* @__PURE__ */ jsx("b", {
|
|
5269
|
+
className: "vg-title",
|
|
5270
|
+
title: node.title,
|
|
5271
|
+
children: node.title
|
|
5272
|
+
}),
|
|
5273
|
+
node.detail && /* @__PURE__ */ jsx("span", {
|
|
5274
|
+
className: "vg-detail",
|
|
5275
|
+
children: node.detail
|
|
5276
|
+
}),
|
|
5277
|
+
quiet.map((chip, i) => /* @__PURE__ */ jsx("span", {
|
|
5278
|
+
className: "vg-chip tone-muted",
|
|
5279
|
+
title: chip.title,
|
|
5280
|
+
children: chip.label
|
|
5281
|
+
}, i))
|
|
5282
|
+
]
|
|
5283
|
+
}), loud.length > 0 && /* @__PURE__ */ jsx("span", {
|
|
5284
|
+
className: "vg-line vg-tags",
|
|
5285
|
+
children: loud.map((chip, i) => /* @__PURE__ */ jsx("span", {
|
|
5286
|
+
className: `vg-chip tone-${chip.tone}`,
|
|
5287
|
+
title: chip.title,
|
|
5288
|
+
children: chip.label
|
|
5289
|
+
}, i))
|
|
5290
|
+
})]
|
|
5291
|
+
})
|
|
5292
|
+
}, node.id);
|
|
5293
|
+
})
|
|
5294
|
+
})]
|
|
5295
|
+
})
|
|
5296
|
+
})
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
5299
|
+
//#endregion
|
|
5300
|
+
export { DEFAULT_SPARKLINE_EMPTY_LABEL, DEFAULT_SPARKLINE_HEIGHT, DEFAULT_SPARKLINE_LABEL, DEFAULT_SPARKLINE_UNAVAILABLE_LABEL, DEFAULT_SPARKLINE_WIDTH, DOT_MAP_CATEGORIES, DayBars, DotMap, Events, FigureView, FlowWaterfall, Gantt, Gap, HBars, Histogram, LIVE_SERIES, Lines, Meter, Spark, Sparkline, StackedBarChart, TimelineView, VersionGraph, WaterfallBar, WaterfallHeader, WaterfallRow, WaterfallTimeAxis, criticalPathIds, flowWaterfallRows, formatCompact, formatSparklineValue, formatWaterfallDuration, layoutVersionGraph, sparklineGeometry, sparklineLabel, sparklinePointsAttribute, sparklineReadings, toolGlyph, waterfallSpanGeometry, waterfallTicks };
|
package/dist/react/live.css
CHANGED
|
@@ -129,3 +129,42 @@ line.ov-gridline { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 22%,
|
|
|
129
129
|
|
|
130
130
|
.ov-meter-steps span { overflow-wrap: anywhere; }
|
|
131
131
|
@media (max-width: 720px) { .ov-meter-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); } .ov-meter-steps li { padding: 12px; min-height: 94px; } }
|
|
132
|
+
|
|
133
|
+
/* Recorded version history shares chart/Brand roles with the live timeline. */
|
|
134
|
+
.tgc-version-graph { min-width: 0; font: 14px/1.4 var(--font-sans, sans-serif); color: var(--chart-live-fg, var(--text-primary));
|
|
135
|
+
--vg-c0: var(--chart-live-accent, var(--accent-text)); --vg-c1: light-dark(#2a7fa8, #6cc0e8); --vg-c2: light-dark(#7b4fc0, #b99af0); --vg-c3: light-dark(#1f7f88, #5fc2c9); --vg-c4: light-dark(#a8418f, #e58ccf); --vg-c5: light-dark(#5a6ad8, #8fa4ff); --vg-c6: light-dark(#45507a, #c0c4dc); --vg-c7: var(--chart-live-muted, var(--text-muted));
|
|
136
|
+
}
|
|
137
|
+
.tgc-version-graph .vg-host { position: relative; min-width: max-content; }
|
|
138
|
+
.tgc-version-graph .vg-lines { position: absolute; left: 0; top: 0; pointer-events: none; overflow: visible; }
|
|
139
|
+
.tgc-version-graph .vg-lines path { fill: none; stroke: currentColor; stroke-width: 2; opacity: .55; }
|
|
140
|
+
.tgc-version-graph .vg-lines path.hot { opacity: 1; stroke-width: 2.5; }
|
|
141
|
+
.tgc-version-graph .vg-lines .vg-dot { fill: currentColor; stroke: var(--chart-live-surface, var(--bg-surface)); stroke-width: 2; }
|
|
142
|
+
.tgc-version-graph .vg-lines .vg-dot.kind-profile, .tgc-version-graph .vg-lines .vg-dot.kind-authored { fill: var(--chart-live-surface, var(--bg-surface)); stroke: currentColor; stroke-width: 2.5; }
|
|
143
|
+
.tgc-version-graph .vg-lines .vg-dot.kind-ghost { fill: var(--chart-live-surface, var(--bg-surface)); stroke: var(--chart-live-faint, var(--text-muted)); stroke-dasharray: 2 2; }
|
|
144
|
+
.tgc-version-graph .vg-lines .vg-dot.on { stroke: var(--chart-live-fg, var(--text-primary)); stroke-width: 3; }
|
|
145
|
+
.tgc-version-graph .vg-c0 { color: var(--vg-c0); } .tgc-version-graph .vg-c1 { color: var(--vg-c1); } .tgc-version-graph .vg-c2 { color: var(--vg-c2); } .tgc-version-graph .vg-c3 { color: var(--vg-c3); }
|
|
146
|
+
.tgc-version-graph .vg-c4 { color: var(--vg-c4); } .tgc-version-graph .vg-c5 { color: var(--vg-c5); } .tgc-version-graph .vg-c6 { color: var(--vg-c6); } .tgc-version-graph .vg-c7 { color: var(--vg-c7); }
|
|
147
|
+
.tgc-version-graph .vg-rows { list-style: none; margin: 0; padding-right: 8px; }
|
|
148
|
+
.tgc-version-graph .vg-rows > li { display: flex; }
|
|
149
|
+
.tgc-version-graph .vg-row { display: flex; flex-direction: column; justify-content: flex-start; gap: 4px; width: 100%; min-width: 0; padding: 0 8px; border: 0; border-radius: 6px; background: none; color: var(--chart-live-fg, var(--text-primary)); font: inherit; text-align: left; cursor: pointer; }
|
|
150
|
+
.tgc-version-graph .vg-row:hover, .tgc-version-graph .vg-row:focus-visible { background: var(--chart-live-raised, var(--bg-elevated)); }
|
|
151
|
+
.tgc-version-graph .vg-row:focus-visible { outline: 2px solid var(--chart-live-accent, var(--accent-text)); outline-offset: -2px; }
|
|
152
|
+
.tgc-version-graph .vg-row.on { background: color-mix(in srgb, var(--chart-live-accent, var(--accent-text)) 13%, var(--chart-live-surface, var(--bg-surface))); box-shadow: inset 2px 0 var(--chart-live-border, var(--border-color)); }
|
|
153
|
+
.tgc-version-graph .vg-row:disabled { cursor: default; opacity: .8; }
|
|
154
|
+
.tgc-version-graph .vg-line { display: flex; align-items: center; gap: 8px; min-width: 0; height: 30px; flex: none; }
|
|
155
|
+
.tgc-version-graph .vg-line.vg-tags { height: 22px; flex-wrap: nowrap; overflow: hidden; }
|
|
156
|
+
.tgc-version-graph .vg-title { font-weight: 600; max-width: 46ch; }
|
|
157
|
+
.tgc-version-graph .vg-row.kind-integrate .vg-title { font-weight: 400; color: var(--chart-live-muted, var(--text-muted)); }
|
|
158
|
+
.tgc-version-graph .vg-row.kind-ghost .vg-title { font-weight: 400; color: var(--chart-live-faint, var(--text-muted)); }
|
|
159
|
+
.tgc-version-graph .vg-detail { font-size: var(--font-size-sm, 13px); color: var(--chart-live-muted, var(--text-muted)); }
|
|
160
|
+
.tgc-version-graph .vg-chip { display: inline-block; flex: none; margin-left: 4px; padding: 0 8px; border-radius: 4px; font: 600 var(--font-size-sm, 13px)/1.6 var(--font-sans); white-space: nowrap; background: var(--chart-live-raised, var(--bg-elevated)); color: var(--chart-live-muted, var(--text-muted)); }
|
|
161
|
+
.tgc-version-graph .vg-chip.tone-neutral { background: none; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--chart-live-muted, var(--text-muted)) 55%, transparent); }
|
|
162
|
+
.tgc-version-graph .vg-chip.tone-accent { color: var(--chart-live-accent, var(--accent-text)); background: color-mix(in srgb, var(--chart-live-accent, var(--accent-text)) 14%, transparent); }
|
|
163
|
+
.tgc-version-graph .vg-chip.tone-info { color: var(--chart-live-fg, var(--text-primary)); background: var(--chart-live-raised, var(--bg-elevated)); font-weight: 400; }
|
|
164
|
+
|
|
165
|
+
.tgc-version-graph :is(.vg-title,.vg-detail) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
166
|
+
@media (forced-colors: active) { .tgc-version-graph .vg-chip { forced-color-adjust: none; border: 1px solid CanvasText; } }
|
|
167
|
+
|
|
168
|
+
.tgc-version-graph .vg-row[data-arriving] { transition: opacity var(--motion-arrive); }
|
|
169
|
+
@starting-style { .tgc-version-graph .vg-row[data-arriving] { opacity: 0; } }
|
|
170
|
+
@media (prefers-reduced-motion: reduce) { .tgc-version-graph .vg-row[data-arriving] { transition: none; } }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/charts",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18",
|
|
4
4
|
"description": "Static SVG and HTML figures for benchmarks and operational timing. Draws from producer data and refuses what it cannot draw honestly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|