@theoremjs/studio 0.3.4-next.458.gbf7b487 → 0.3.4-next.464.g5fc5eed
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/compile.js +1 -1
- package/package.json +4 -4
- package/page/assets/{Banner-C4MPbvft.js → Banner-fUKWqHmI.js} +2 -2
- package/page/assets/{ConsoleFrame-BkzieVRC.js → ConsoleFrame-B3cd6mje.js} +1 -1
- package/page/assets/{ContextMenu-BGP2gdee.js → ContextMenu-C1HQbsdS.js} +2 -2
- package/page/assets/{ToolResultCharts-xDwNDd-T.js → ToolResultCharts-CngV4ccU.js} +1 -1
- package/page/assets/{TraceInspector-CHpdhiVX.js → TraceInspector-BJS_1iXX.js} +1 -1
- package/page/assets/{TraceOverview-DCGew7UN.js → TraceOverview-6hlA2pmX.js} +1 -1
- package/page/assets/{browser-engine-Ca2pvYX0.js → browser-engine-BReGdZx4.js} +1 -1
- package/page/assets/browser-transport-Bsz-KNbB.js +47 -0
- package/page/assets/{export-agent-5rXmF-g1.js → export-agent-DDZ8cq19.js} +1 -1
- package/page/assets/{guardrail-tester-B-ZfQFVu.js → guardrail-tester-C_T6-YOZ.js} +1 -1
- package/page/assets/{index-Dajtrq_Z.js → index-Cw3Spve0.js} +6 -6
- package/page/assets/{studio-code-pane-CKYRitdD.js → studio-code-pane-xsBtdlWI.js} +1 -1
- package/page/assets/{studio-console-DwhqnNyw.js → studio-console-C3uStRGc.js} +1 -1
- package/page/assets/{studio-decision-Ckj4UNNt.js → studio-decision-MvC7eEPm.js} +1 -1
- package/page/assets/{studio-embedding-8qL_dVlz.js → studio-embedding-mvARkljv.js} +1 -1
- package/page/assets/{studio-host-console-BXwwloN4.js → studio-host-console-CzHR2V7s.js} +2 -2
- package/page/assets/{studio-labels-C1pvg0JC.js → studio-labels-DsQLZ9_e.js} +1 -1
- package/page/assets/{studio-live-26srJ74m.js → studio-live-DXV_PScy.js} +1 -1
- package/page/assets/{studio-map-C5zsJusF.js → studio-map-DBLOQt2Q.js} +1 -1
- package/page/assets/{studio-trace-replay-B38TicvO.js → studio-trace-replay-DHJTvGZO.js} +1 -1
- package/page/assets/{use-one-call-DjVkallj.js → use-one-call-B3x4YoMP.js} +1 -1
- package/page/index.html +5 -5
- package/ui/tree-map.css +0 -43
- package/ui/tree-map.d.ts +7 -5
- package/ui/tree-map.js +221 -42
- package/page/assets/browser-transport-DYgEItHi.js +0 -47
package/ui/tree-map.css
CHANGED
|
@@ -13,46 +13,3 @@
|
|
|
13
13
|
.studio-tree-map-lines .map-line {
|
|
14
14
|
stroke: color-mix(in srgb, var(--color-border-emphasized) 60%, var(--color-background-surface));
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
/*
|
|
18
|
-
* Feeds draw in as the loader's map draws (read-map.css), one after another: the line from the
|
|
19
|
-
* tree, then the token at its end. Hidden, they leave in the reverse order.
|
|
20
|
-
* invariant: Transitions, not keyframes, so drawing and leaving both ease, and a turn mid-draw
|
|
21
|
-
* goes back from where it is.
|
|
22
|
-
*/
|
|
23
|
-
.studio-tree-map {
|
|
24
|
-
--feed-step: 70ms;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.studio-tree-map-lines [data-feed] {
|
|
28
|
-
stroke-dasharray: 1;
|
|
29
|
-
stroke-dashoffset: 0;
|
|
30
|
-
transition: stroke-dashoffset var(--duration-medium, 240ms) var(--ease-standard, ease-out)
|
|
31
|
-
calc(var(--feed-i) * var(--feed-step));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.studio-tree-map > [data-feed] {
|
|
35
|
-
transition: opacity var(--duration-medium, 240ms) var(--ease-standard, ease-out)
|
|
36
|
-
calc(var(--feed-i) * var(--feed-step) + var(--duration-medium, 240ms));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.studio-tree-map[data-feeds="hidden"] > [data-feed] {
|
|
40
|
-
opacity: 0;
|
|
41
|
-
transition-delay: calc((var(--feed-n) - var(--feed-i)) * var(--feed-step));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.studio-tree-map[data-feeds="hidden"] .studio-tree-map-lines [data-feed] {
|
|
45
|
-
stroke-dashoffset: var(--feed-from);
|
|
46
|
-
transition-delay: calc(
|
|
47
|
-
(var(--feed-n) - var(--feed-i)) *
|
|
48
|
-
var(--feed-step) +
|
|
49
|
-
var(--duration-medium, 240ms)
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@media (prefers-reduced-motion: reduce) {
|
|
54
|
-
.studio-tree-map-lines [data-feed],
|
|
55
|
-
.studio-tree-map > [data-feed] {
|
|
56
|
-
transition: none;
|
|
57
|
-
}
|
|
58
|
-
}
|
package/ui/tree-map.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ export interface StudioTreeMapProps {
|
|
|
4
4
|
/** The keys of the agents to draw, in the workspace's order; every agent when unset. */
|
|
5
5
|
agents?: readonly string[] | undefined;
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* How far the tree has grown, from 0 to 1. As it grows each section drifts to its own zone: the
|
|
8
|
+
* providers to a band along the top, each agent's tree apart from the next with its runner level
|
|
9
|
+
* with it, and the tool registry to a field of its own. What growing adds draws in on the way,
|
|
10
|
+
* each over its own stretch, its line and then its token: the steps of a turn under each turn
|
|
11
|
+
* runner, and every tool in the registry. Shrinking takes it all back the same way.
|
|
10
12
|
*/
|
|
11
|
-
|
|
13
|
+
grow?: number | undefined;
|
|
12
14
|
}
|
|
13
|
-
export declare function StudioTreeMap({ workspace, agents,
|
|
15
|
+
export declare function StudioTreeMap({ workspace, agents, grow }: StudioTreeMapProps): import("react").JSX.Element;
|
package/ui/tree-map.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { IconCloud, IconEngine, IconSettingsAutomation, IconTool } from '@tabler/icons-react';
|
|
2
|
+
import { IconAdjustments, IconArrowsMinimize, IconChecks, IconCloud, IconEngine, IconFlag, IconKey, IconRepeat, IconSettingsAutomation, IconShieldCheck, IconTimeline, IconTool, } from '@tabler/icons-react';
|
|
3
3
|
import { useRef } from 'react';
|
|
4
4
|
import { mapCurve } from "../map-line.js";
|
|
5
5
|
import { createBlankDraft, libraryDraft, workspaceTree, } from "../mod.js";
|
|
@@ -34,7 +34,7 @@ function rowsOf(workspace, only) {
|
|
|
34
34
|
agent,
|
|
35
35
|
x: PAD + depth * INDENT,
|
|
36
36
|
y: PAD + HALF + rows.length * ROW,
|
|
37
|
-
...(parent ? { parent:
|
|
37
|
+
...(parent ? { parent: rowId(parent) } : {}),
|
|
38
38
|
};
|
|
39
39
|
rows.push(row);
|
|
40
40
|
for (const child of node.children)
|
|
@@ -72,7 +72,26 @@ function runnerLabel(type) {
|
|
|
72
72
|
return 'Embedding runner';
|
|
73
73
|
return 'Turn runner';
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* What a turn runner does in one turn, in order.
|
|
77
|
+
* why: The kernel contract's turn lifecycle (docs/contracts/kernel.md, "Turn lifecycle"), step for
|
|
78
|
+
* step; a decision and a session have no such list there, so their runners open into nothing.
|
|
79
|
+
*/
|
|
80
|
+
const TURN_STEPS = [
|
|
81
|
+
{ id: 'resolve', label: 'Resolve', icon: IconAdjustments },
|
|
82
|
+
{ id: 'sanitize', label: 'Sanitize', icon: IconShieldCheck },
|
|
83
|
+
{ id: 'compaction', label: 'Compaction', icon: IconArrowsMinimize },
|
|
84
|
+
{ id: 'canary', label: 'Canary bind', icon: IconKey },
|
|
85
|
+
{ id: 'pre_turn', label: 'pre_turn', icon: IconFlag },
|
|
86
|
+
{ id: 'stream', label: 'Provider stream', icon: IconCloud },
|
|
87
|
+
{ id: 'tools', label: 'Tool loop', icon: IconRepeat },
|
|
88
|
+
{ id: 'before_end', label: 'before_end', icon: IconFlag },
|
|
89
|
+
{ id: 'validation', label: 'Validation', icon: IconChecks },
|
|
90
|
+
{ id: 'egress', label: 'Egress', icon: IconShieldCheck },
|
|
91
|
+
{ id: 'trace', label: 'Trace', icon: IconTimeline },
|
|
92
|
+
{ id: 'post_turn', label: 'post_turn', icon: IconFlag },
|
|
93
|
+
];
|
|
94
|
+
function feedsOf(workspace, rows, detail) {
|
|
76
95
|
const feeds = [];
|
|
77
96
|
const add = (id, label, icon, into, under, depth = 0) => {
|
|
78
97
|
const below = (feeds.at(-1)?.y ?? Number.NEGATIVE_INFINITY) + ROW;
|
|
@@ -93,8 +112,13 @@ function feedsOf(workspace, rows) {
|
|
|
93
112
|
if (!name)
|
|
94
113
|
continue;
|
|
95
114
|
const runner = runnerLabel(agent.identity.profileType);
|
|
96
|
-
if (runner)
|
|
97
|
-
|
|
115
|
+
if (runner) {
|
|
116
|
+
const id = `${agent.key}/runner`;
|
|
117
|
+
add(id, runner, IconEngine, [name]);
|
|
118
|
+
const steps = detail && runner === 'Turn runner' ? TURN_STEPS : [];
|
|
119
|
+
for (const step of steps)
|
|
120
|
+
add(`${id}/${step.id}`, step.label, step.icon, [], id, 1);
|
|
121
|
+
}
|
|
98
122
|
const providers = new Map();
|
|
99
123
|
for (const binding of agent.modelBindings) {
|
|
100
124
|
const row = mine.find((each) => each.node.ref.facet === 'modelBinding' && each.node.ref.key === binding.key);
|
|
@@ -113,7 +137,7 @@ function feedsOf(workspace, rows) {
|
|
|
113
137
|
else if (tools && workspace.toolSpecs.length > 0) {
|
|
114
138
|
const registry = REGISTRY;
|
|
115
139
|
add(registry, 'Tool registry', IconTool, [tools]);
|
|
116
|
-
for (const tool of
|
|
140
|
+
for (const tool of listedTools(workspace.toolSpecs, detail)) {
|
|
117
141
|
const icon = toolTypeIcon(tool.toolType);
|
|
118
142
|
add(`${registry}/${tool.key}`, tool.toolName, icon, [], registry, 1);
|
|
119
143
|
}
|
|
@@ -122,14 +146,18 @@ function feedsOf(workspace, rows) {
|
|
|
122
146
|
}
|
|
123
147
|
return feeds;
|
|
124
148
|
}
|
|
125
|
-
/**
|
|
126
|
-
|
|
149
|
+
/**
|
|
150
|
+
* The registry's tools: the first of each type, then the rest in the registry's order. A few of
|
|
151
|
+
* them, or with `detail` every one.
|
|
152
|
+
*/
|
|
153
|
+
function listedTools(tools, detail) {
|
|
127
154
|
const firsts = TOOL_TYPES.flatMap((type) => {
|
|
128
155
|
const tool = tools.find((each) => each.toolType === type);
|
|
129
156
|
return tool ? [tool] : [];
|
|
130
157
|
});
|
|
131
158
|
const rest = tools.filter((tool) => !firsts.includes(tool));
|
|
132
|
-
|
|
159
|
+
const listed = [...firsts, ...rest];
|
|
160
|
+
return detail ? listed : listed.slice(0, SHOWN_TOOLS);
|
|
133
161
|
}
|
|
134
162
|
/** Right from `from` to `rail`, up or down it, and back left to `to`, its corners turned. */
|
|
135
163
|
function sidePath(from, to, rail) {
|
|
@@ -142,54 +170,205 @@ function linePath(from, to) {
|
|
|
142
170
|
const x = from.x + TRUNK;
|
|
143
171
|
return `M ${x} ${from.y + HALF} V ${to.y - TURN} Q ${x} ${to.y} ${x + TURN} ${to.y} H ${to.x - LINE_GAP}`;
|
|
144
172
|
}
|
|
145
|
-
/**
|
|
146
|
-
|
|
147
|
-
|
|
173
|
+
/**
|
|
174
|
+
* From under the parent's icon to a child in any column of the rows under it: across just under
|
|
175
|
+
* the parent to the child's own trunk, a level in from the child, then down it and across. A child
|
|
176
|
+
* one level in has its trunk under the parent's icon, so it is the tree's line.
|
|
177
|
+
*/
|
|
178
|
+
function underPath(from, to) {
|
|
179
|
+
const trunk = to.x - INDENT + TRUNK;
|
|
180
|
+
const across = from.y + HALF + LINE_GAP;
|
|
181
|
+
return (`M ${from.x + TRUNK} ${from.y + HALF} V ${across} H ${trunk} V ${to.y - TURN}` +
|
|
182
|
+
` Q ${trunk} ${to.y} ${trunk + TURN} ${to.y} H ${to.x - LINE_GAP}`);
|
|
183
|
+
}
|
|
184
|
+
/** Grown, a row's height and the room between two agents' trees. */
|
|
185
|
+
const GROWN_ROW = 28;
|
|
186
|
+
const AGENT_GAP = 16;
|
|
187
|
+
/** Grown, the providers' band and the room under it. */
|
|
188
|
+
const BAND = GROWN_ROW + AGENT_GAP;
|
|
189
|
+
/** Grown, a turn's steps in rows under its runner, so many to a row. */
|
|
190
|
+
const STEPS_ACROSS = 3;
|
|
191
|
+
const STEP_SLOT = 160;
|
|
192
|
+
/** Grown, the registry's field: right of the turn's steps, its tools in columns this tall. */
|
|
193
|
+
const REGISTRY_IN = 186;
|
|
194
|
+
const TOOLS_DOWN = 16;
|
|
195
|
+
const TOOL_SLOT = 210;
|
|
196
|
+
/** Over what stretch of growing the added nodes draw, and how long each takes of it. */
|
|
197
|
+
const DRAW_FROM = 0.2;
|
|
198
|
+
const DRAW_TO = 0.95;
|
|
199
|
+
const DRAW_SPAN = 0.1;
|
|
200
|
+
const clamp = (value) => Math.min(Math.max(value, 0), 1);
|
|
201
|
+
const between = (from, to, at) => from + (to - from) * at;
|
|
202
|
+
/** Every other one from each list, while both last: the turn's steps and the tools draw by turns. */
|
|
203
|
+
function byTurns(first, second) {
|
|
204
|
+
return Array.from({ length: Math.max(first.length, second.length) }, (_, index) => [first[index], second[index]].filter((each) => each !== undefined)).flat();
|
|
205
|
+
}
|
|
206
|
+
/** The rows grown: restacked, each agent apart from the next, under the providers' band. */
|
|
207
|
+
function grownRows(rows) {
|
|
208
|
+
const places = new Map();
|
|
209
|
+
let agents = -1;
|
|
210
|
+
rows.forEach((row, index) => {
|
|
211
|
+
if (index === 0 || rows[index - 1]?.agent !== row.agent)
|
|
212
|
+
agents += 1;
|
|
213
|
+
places.set(rowId(row), {
|
|
214
|
+
x: row.x,
|
|
215
|
+
y: PAD + HALF + BAND + index * GROWN_ROW + agents * AGENT_GAP,
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
return places;
|
|
219
|
+
}
|
|
220
|
+
/** The next place under a holder, counting it. */
|
|
221
|
+
function nextUnder(growing, holder) {
|
|
222
|
+
const index = growing.under.get(holder) ?? 0;
|
|
223
|
+
growing.under.set(holder, index + 1);
|
|
224
|
+
return index;
|
|
225
|
+
}
|
|
226
|
+
/** A provider's place in the band, by name: an agent that has one too shares it. */
|
|
227
|
+
function bandPlace(growing, feed) {
|
|
228
|
+
const held = growing.band.get(feed.label);
|
|
229
|
+
if (held)
|
|
230
|
+
return held;
|
|
231
|
+
const place = { x: growing.bandX, y: PAD + HALF };
|
|
232
|
+
growing.band.set(feed.label, place);
|
|
233
|
+
growing.bandX += growing.width(feed.id) + AGENT_GAP;
|
|
234
|
+
return place;
|
|
235
|
+
}
|
|
236
|
+
/** Under the registry: its tools in columns, then the tool runner below them. */
|
|
237
|
+
function registryPlace(growing, feed, above) {
|
|
238
|
+
if (feed.depth === 0) {
|
|
239
|
+
const tools = Math.min(growing.under.get(REGISTRY) ?? 0, TOOLS_DOWN);
|
|
240
|
+
return { x: above.x, y: above.y + (tools + 1) * GROWN_ROW };
|
|
241
|
+
}
|
|
242
|
+
const index = nextUnder(growing, REGISTRY);
|
|
243
|
+
return {
|
|
244
|
+
x: above.x + INDENT + Math.floor(index / TOOLS_DOWN) * TOOL_SLOT,
|
|
245
|
+
y: above.y + ((index % TOOLS_DOWN) + 1) * GROWN_ROW,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/** A step under its runner, in rows so many across. */
|
|
249
|
+
function stepPlace(growing, holder, above) {
|
|
250
|
+
const index = nextUnder(growing, holder);
|
|
251
|
+
const y = above.y + (Math.floor(index / STEPS_ACROSS) + 1) * GROWN_ROW;
|
|
252
|
+
growing.stepsEnd = Math.max(growing.stepsEnd, y);
|
|
253
|
+
return { x: above.x + INDENT + (index % STEPS_ACROSS) * STEP_SLOT, y };
|
|
254
|
+
}
|
|
255
|
+
function feedPlace(growing, feed) {
|
|
256
|
+
if (feed.id.includes('/provider:'))
|
|
257
|
+
return bandPlace(growing, feed);
|
|
258
|
+
if (feed.id === REGISTRY) {
|
|
259
|
+
return { x: growing.feedX + REGISTRY_IN, y: growing.stepsEnd + GROWN_ROW * 1.5 };
|
|
260
|
+
}
|
|
261
|
+
const above = feed.under === undefined ? undefined : growing.places.get(feed.under);
|
|
262
|
+
if (feed.under === REGISTRY && above)
|
|
263
|
+
return registryPlace(growing, feed, above);
|
|
264
|
+
if (feed.under !== undefined && above)
|
|
265
|
+
return stepPlace(growing, feed.under, above);
|
|
266
|
+
const into = growing.places.get(feed.into[0] ?? '');
|
|
267
|
+
return { x: growing.feedX, y: into?.y ?? feed.y };
|
|
268
|
+
}
|
|
269
|
+
/** Each node's place grown: by its token id, rows and feeds alike. */
|
|
270
|
+
function grownPlaces(rows, feeds, feedX, width) {
|
|
271
|
+
const growing = {
|
|
272
|
+
places: grownRows(rows),
|
|
273
|
+
band: new Map(),
|
|
274
|
+
bandX: feedX,
|
|
275
|
+
under: new Map(),
|
|
276
|
+
stepsEnd: PAD + HALF + BAND,
|
|
277
|
+
feedX,
|
|
278
|
+
width,
|
|
279
|
+
};
|
|
280
|
+
for (const feed of feeds)
|
|
281
|
+
growing.places.set(feed.id, feedPlace(growing, feed));
|
|
282
|
+
return growing.places;
|
|
283
|
+
}
|
|
284
|
+
export function StudioTreeMap({ workspace, agents, grow = 0 }) {
|
|
148
285
|
const root = useRef(null);
|
|
149
286
|
const rows = rowsOf(workspace, agents);
|
|
150
|
-
const
|
|
287
|
+
const few = feedsOf(workspace, rows, false);
|
|
288
|
+
const full = feedsOf(workspace, rows, true);
|
|
151
289
|
const widths = useTokenWidths(root, workspace);
|
|
290
|
+
const width = (id) => widths[id] ?? GUESS;
|
|
152
291
|
const drafts = new Map(workspace.agents.map((agent) => [
|
|
153
292
|
agent.key,
|
|
154
293
|
libraryDraft(workspace, agent.key) ?? createBlankDraft(),
|
|
155
294
|
]));
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
/**
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
295
|
+
const feedX = Math.max(0, ...rows.map((row) => row.x + width(rowId(row)))) + GAP;
|
|
296
|
+
// The tree as it stands: the rows, and the few feeds beside them.
|
|
297
|
+
const closed = new Map(rows.map((row) => [rowId(row), row]));
|
|
298
|
+
for (const feed of few)
|
|
299
|
+
closed.set(feed.id, { x: feedX + feed.depth * INDENT, y: feed.y });
|
|
300
|
+
const grown = grownPlaces(rows, full, feedX, width);
|
|
301
|
+
const feedById = new Map(full.map((feed) => [feed.id, feed]));
|
|
302
|
+
/**
|
|
303
|
+
* Where a node is, this far grown. One growing adds keeps its place in its zone and rides with
|
|
304
|
+
* the feed it comes under, so a zone drifts whole.
|
|
305
|
+
*/
|
|
306
|
+
const at = (id) => {
|
|
307
|
+
const to = grown.get(id) ?? closed.get(id) ?? { x: 0, y: 0 };
|
|
308
|
+
const from = closed.get(id);
|
|
309
|
+
if (from)
|
|
310
|
+
return { x: between(from.x, to.x, grow), y: between(from.y, to.y, grow) };
|
|
311
|
+
const under = feedById.get(id)?.under;
|
|
312
|
+
const holder = under === undefined ? undefined : grown.get(under);
|
|
313
|
+
if (under === undefined || holder === undefined)
|
|
314
|
+
return to;
|
|
315
|
+
const now = at(under);
|
|
316
|
+
return { x: to.x + now.x - holder.x, y: to.y + now.y - holder.y };
|
|
317
|
+
};
|
|
318
|
+
// What growing adds draws in order, the turn's steps and the tools by turns.
|
|
319
|
+
const fewIds = new Set(few.map((feed) => feed.id));
|
|
320
|
+
const added = full.filter((feed) => !fewIds.has(feed.id));
|
|
321
|
+
const order = byTurns(added.filter((feed) => feed.under !== REGISTRY), added.filter((feed) => feed.under === REGISTRY));
|
|
322
|
+
/** How far an added node has drawn, 0 to 1: its line in the first half, its token in the second. */
|
|
323
|
+
const drawn = new Map(order.map((feed, index) => {
|
|
324
|
+
const start = DRAW_FROM + ((DRAW_TO - DRAW_SPAN - DRAW_FROM) * index) / Math.max(order.length - 1, 1);
|
|
325
|
+
return [feed.id, clamp((grow - start) / DRAW_SPAN)];
|
|
326
|
+
}));
|
|
327
|
+
const lineDrawn = (id) => {
|
|
328
|
+
const part = drawn.get(id);
|
|
329
|
+
return part === undefined
|
|
330
|
+
? {}
|
|
331
|
+
: { pathLength: 1, style: { strokeDasharray: 1, strokeDashoffset: 1 - clamp(part * 2) } };
|
|
332
|
+
};
|
|
333
|
+
const shown = full.filter((feed) => (drawn.get(feed.id) ?? 1) > 0);
|
|
334
|
+
const right = (id) => at(id).x + width(id);
|
|
335
|
+
/** Right of every feed from `above` down to `feed`, where each stands now. */
|
|
336
|
+
const railFor = (feed, above) => {
|
|
337
|
+
const [top, bottom] = [at(above.id).y, at(feed.id).y].sort((a, b) => a - b);
|
|
338
|
+
const lying = shown.filter((each) => {
|
|
339
|
+
const { y } = at(each.id);
|
|
340
|
+
return y >= (top ?? 0) && y <= (bottom ?? 0) && each.id !== feed.id;
|
|
341
|
+
});
|
|
342
|
+
return Math.max(right(feed.id), ...lying.map((each) => right(each.id))) + RAIL;
|
|
343
|
+
};
|
|
344
|
+
const places = [...rows.map(rowId), ...shown.map((feed) => feed.id)];
|
|
345
|
+
const rails = shown.flatMap((feed) => {
|
|
167
346
|
const above = feed.under === undefined ? undefined : feedById.get(feed.under);
|
|
168
347
|
return above && feed.depth <= above.depth ? [railFor(feed, above)] : [];
|
|
169
348
|
});
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
return (_jsxs("div", { ref: root, className: "studio-tree-map",
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// The line runs from the feed to the row; it draws from the row.
|
|
178
|
-
return (_jsx("path", { className: "map-line", d: d, pathLength: 1, "data-feed": "", style: feedStyle(order.get(feed.id) ?? 0, -1) }, `${feed.id}>${id}`));
|
|
179
|
-
})), feeds.map((feed) => {
|
|
349
|
+
const mapWidth = Math.max(GUESS, ...places.map(right), ...rails) + PAD;
|
|
350
|
+
const height = Math.max(...places.map((id) => at(id).y)) + HALF + PAD;
|
|
351
|
+
return (_jsxs("div", { ref: root, className: "studio-tree-map", style: { width: mapWidth, height }, children: [_jsxs("svg", { className: "studio-tree-map-lines", width: mapWidth, height: height, "aria-hidden": "true", children: [rows.map((row) => row.parent && (_jsx("path", { className: "map-line", d: linePath(at(row.parent), at(rowId(row))) }, rowId(row)))), shown.flatMap((feed) => feed.into.map((id) => {
|
|
352
|
+
const from = at(feed.id);
|
|
353
|
+
const { d } = mapCurve({ x: from.x - LINE_GAP, y: from.y }, { x: right(id) + LINE_GAP, y: at(id).y });
|
|
354
|
+
return _jsx("path", { className: "map-line", d: d }, `${feed.id}>${id}`);
|
|
355
|
+
})), shown.map((feed) => {
|
|
180
356
|
const above = feed.under === undefined ? undefined : feedById.get(feed.under);
|
|
181
357
|
if (above === undefined)
|
|
182
358
|
return null;
|
|
183
|
-
|
|
184
|
-
// One of its own turns in from the trunk, as a tree row does; one level with it comes round
|
|
359
|
+
// One of its own comes down from it, as a tree row does; one level with it comes round
|
|
185
360
|
// the right of what lies between, so it reads as joining the feed, not as one of its own.
|
|
186
361
|
const d = feed.depth > above.depth
|
|
187
|
-
?
|
|
188
|
-
: sidePath({ x:
|
|
189
|
-
|
|
190
|
-
return (_jsx("path", { className: "map-line", d: d, pathLength: 1, "data-feed": "", style: feedStyle(order.get(feed.id) ?? 0, feed.depth > above.depth ? 1 : -1) }, `${feed.id}^`));
|
|
362
|
+
? underPath(at(above.id), at(feed.id))
|
|
363
|
+
: sidePath({ x: right(feed.id) + LINE_GAP, y: at(feed.id).y }, { x: right(above.id) + LINE_GAP, y: at(above.id).y }, railFor(feed, above));
|
|
364
|
+
return _jsx("path", { className: "map-line", d: d, ...lineDrawn(feed.id) }, `${feed.id}^`);
|
|
191
365
|
})] }), rows.map((row) => {
|
|
192
366
|
const draft = drafts.get(row.agent) ?? createBlankDraft();
|
|
193
|
-
|
|
194
|
-
|
|
367
|
+
const { x, y } = at(rowId(row));
|
|
368
|
+
return (_jsx(MapToken, { id: rowId(row), label: row.node.label, icon: nodeIcon(draft, row.node.ref), x: x, y: y }, rowId(row)));
|
|
369
|
+
}), shown.map((feed) => {
|
|
370
|
+
const { x, y } = at(feed.id);
|
|
371
|
+
const part = drawn.get(feed.id);
|
|
372
|
+
return (_jsx(MapToken, { id: feed.id, label: feed.label, icon: feed.icon, x: x, y: y, ...(part === undefined ? {} : { style: { opacity: clamp(part * 2 - 1) } }) }, feed.id));
|
|
373
|
+
})] }));
|
|
195
374
|
}
|