@theoremjs/studio 0.3.4-next.482.g7e608f2 → 0.3.4-next.508.geda6db0
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 +3 -3
- package/compile.d.ts +1 -1
- package/draft.d.ts +1 -1
- package/example.js +5 -2
- package/map-line.d.ts +12 -0
- package/map-line.js +98 -0
- package/map.d.ts +1 -1
- package/map.js +0 -1
- package/package.json +4 -4
- package/page/assets/{Banner-CrUhb26_.js → Banner-BcHZFaJb.js} +2 -2
- package/page/assets/{ConsoleFrame-6cEEvO4i.js → ConsoleFrame-Byq_hSpu.js} +3 -3
- package/page/assets/{ContextMenu-WnsgC14_.js → ContextMenu-C6S-GYrc.js} +2 -2
- package/page/assets/{ToolResultCharts-BEhuo0Z4.js → ToolResultCharts-CkEf5ub5.js} +1 -1
- package/page/assets/{TraceInspector-k1w1q0XO.js → TraceInspector-BsYHHhGv.js} +1 -1
- package/page/assets/{TraceOverview-Crxd-sTS.js → TraceOverview-CMFfbfGM.js} +2 -2
- package/page/assets/{_virtual_studio-type-sources-DAabvYbE.js → _virtual_studio-type-sources-CTIGQ210.js} +56 -48
- package/page/assets/browser-engine-DjYv346h.js +11 -0
- package/page/assets/browser-transport-Bm3uY9Fj.js +47 -0
- package/page/assets/{crypto-iT_LZ8Et.js → crypto-DAIn4qnM.js} +1 -1
- package/page/assets/{egress-automata-B5ZGE1mT.js → egress-automata-SiHQTP1i.js} +1 -1
- package/page/assets/{encode-files-8AFMw7Er.js → encode-files-oYzzs_iG.js} +1 -1
- package/page/assets/{export-agent-BedkRp13.js → export-agent-BeUTYowB.js} +3 -3
- package/page/assets/{guardrail-tester-XwnZWNsg.js → guardrail-tester-DLT99rnO.js} +2 -2
- package/page/assets/index-C9rhrCGT.css +1 -0
- package/page/assets/{index-BVHT-9mR.js → index-DNA-Y8Mp.js} +69 -69
- package/page/assets/{labels-provider-BSiD6Lrb.js → labels-provider-Gus3heEH.js} +1 -1
- package/page/assets/{live-messages-B65rUlNM.js → live-messages-BnBioidx.js} +1 -1
- package/page/assets/reader-BFYIhA73.js +7 -0
- package/page/assets/shaped-data-1Qn2111E.js +16 -0
- package/page/assets/{stub-DG5wxM-A.js → stub-B3GersQP.js} +2 -2
- package/page/assets/{studio-code-pane-p4fjtQhL.js → studio-code-pane-CCi7-lyE.js} +3 -3
- package/page/assets/{studio-console-BtZY4aUM.js → studio-console-CJT5FkWm.js} +1 -1
- package/page/assets/{studio-decision-BovTBIwV.js → studio-decision-DhHdHb7Z.js} +1 -1
- package/page/assets/{studio-embedding-BY6UQk0k.js → studio-embedding-B8vBhuZU.js} +1 -1
- package/page/assets/studio-host-console-CE_5_tEn.js +2 -0
- package/page/assets/{studio-labels-C3yQoEd0.js → studio-labels-C2SI_T2p.js} +1 -1
- package/page/assets/{studio-live-B_cVDiqs.js → studio-live-CKSSyYZj.js} +3 -3
- package/page/assets/studio-map-CKm_FCd9.js +1 -0
- package/page/assets/studio-trace-replay-BRMD6pex.js +2 -0
- package/page/assets/{studio-type-checker-Bljo3amv.js → studio-type-checker-DoaPHYNy.js} +1 -1
- package/page/assets/tool-schema-DXYwL5aE.js +18 -0
- package/page/assets/{trace-record-D4xAUHLZ.js → trace-record-DWxIfO7D.js} +1 -1
- package/page/assets/tree-Chi6mqzx.js +3 -0
- package/page/assets/{turn-client-CBWNoFgo.js → turn-client-DF0ije7A.js} +2 -2
- package/page/assets/types-2I4NfLiS.js +1 -0
- package/page/assets/{use-one-call-rh-5EjBB.js → use-one-call-Bhnpve1Y.js} +1 -1
- package/page/assets/{workspace-kAEf4Zn4.js → workspace-U3w2ELtN.js} +1 -1
- package/page/index.html +17 -17
- package/run-payload.d.ts +1 -1
- package/runtime.d.ts +1 -1
- package/runtime.js +1 -1
- package/server/draft-decide.d.ts +1 -4
- package/server/draft-decide.js +1 -1
- package/server/draft-replies.js +2 -2
- package/source.js +1 -1
- package/ui/brand-icons.d.ts +2 -2
- package/ui/brand-icons.js +6 -4
- package/ui/lib/tree-map-layout.d.ts +120 -18
- package/ui/lib/tree-map-layout.js +1292 -190
- package/ui/map-token.css +25 -0
- package/ui/node-icon.js +1 -4
- package/ui/profile-editor.js +1 -1
- package/ui/read-map.css +1 -24
- package/ui/read-map.js +52 -4
- package/ui/studio-map.js +1 -3
- package/ui/tree-map.css +9 -0
- package/ui/tree-map.d.ts +27 -6
- package/ui/tree-map.js +215 -75
- package/page/assets/browser-engine-C78edv3x.js +0 -11
- package/page/assets/browser-transport-mGxdvtBL.js +0 -47
- package/page/assets/index-Cbs7B46A.css +0 -1
- package/page/assets/reader-XebgyCzF.js +0 -7
- package/page/assets/shaped-data-Bc6u4pow.js +0 -16
- package/page/assets/studio-host-console-D9NFWXxV.js +0 -2
- package/page/assets/studio-map-iN6-MOzK.js +0 -1
- package/page/assets/studio-trace-replay-B3k21TSg.js +0 -2
- package/page/assets/tool-schema-BmScISma.js +0 -18
- package/page/assets/tree-BiZScKs7.js +0 -3
- package/page/assets/types-zRlFhC9U.js +0 -1
package/ui/tree-map.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { Text } from '@astryxdesign/core/Text';
|
|
3
|
+
import { IconArrowsMinimize, IconCloud, IconCpu, IconDatabase, IconKey, IconLock, IconPlugConnected, IconRepeat, IconRotateClockwise, IconSettingsAutomation, IconShieldCheck, IconStack2, IconTimeline, IconVault, IconWorldCheck, } from '@tabler/icons-react';
|
|
4
|
+
import { useMemo, useRef, useState, } from 'react';
|
|
5
|
+
import { mapBracket, mapPolyline, mapRoute } from "../map-line.js";
|
|
5
6
|
import { createBlankDraft, libraryDraft } from "../mod.js";
|
|
6
|
-
import {
|
|
7
|
+
import { modelFamilyMark } from "./brand-icons.js";
|
|
8
|
+
import { atRest, beside, drawnAt, goneAt, HALF, INDENT, lineAt, lineKey, PAD, placeAt, rowId, settledAt, settling, treeMapLayout, underKey, } from "./lib/tree-map-layout.js";
|
|
7
9
|
import { AGENT_ICON, profileTypeIcon, toolTypeIcon } from "./lib/type-icons.js";
|
|
8
10
|
import { MapToken, useTokenWidths } from "./map-token.js";
|
|
9
11
|
import { nodeIcon } from "./node-icon.js";
|
|
@@ -15,19 +17,20 @@ const TRUNK = 13;
|
|
|
15
17
|
const TURN = 8;
|
|
16
18
|
/** Between a token's side and its line. */
|
|
17
19
|
const LINE_GAP = 4;
|
|
18
|
-
/** How far right of
|
|
19
|
-
const
|
|
20
|
+
/** How far right of the feeds it passes a line between two feeds of one column runs. */
|
|
21
|
+
const BOW = 16;
|
|
22
|
+
/** How far a feed going drifts off. */
|
|
23
|
+
const DRIFT = 8;
|
|
20
24
|
/** A token's width until it is measured. */
|
|
21
25
|
const GUESS = 120;
|
|
22
26
|
const STEP_ICONS = {
|
|
23
|
-
resolve: IconAdjustments,
|
|
24
27
|
sanitize: IconShieldCheck,
|
|
25
28
|
compaction: IconArrowsMinimize,
|
|
26
29
|
stream: IconCloud,
|
|
27
30
|
tools: IconRepeat,
|
|
28
|
-
validation: IconChecks,
|
|
29
31
|
egress: IconShieldCheck,
|
|
30
32
|
trace: IconTimeline,
|
|
33
|
+
loop: IconRotateClockwise,
|
|
31
34
|
};
|
|
32
35
|
const CHECK_ICONS = {
|
|
33
36
|
permission: IconLock,
|
|
@@ -37,8 +40,6 @@ const CHECK_ICONS = {
|
|
|
37
40
|
};
|
|
38
41
|
function feedIcon(feed) {
|
|
39
42
|
const of = feed.of ?? '';
|
|
40
|
-
if (feed.kind === 'runner')
|
|
41
|
-
return IconEngine;
|
|
42
43
|
if (feed.kind === 'step')
|
|
43
44
|
return STEP_ICONS[of] ?? IconRepeat;
|
|
44
45
|
if (feed.kind === 'provider')
|
|
@@ -47,20 +48,22 @@ function feedIcon(feed) {
|
|
|
47
48
|
return IconPlugConnected;
|
|
48
49
|
if (feed.kind === 'tool')
|
|
49
50
|
return toolTypeIcon(of);
|
|
50
|
-
if (feed.kind === 'branch')
|
|
51
|
+
if (feed.kind === 'branch' || feed.kind === 'stream')
|
|
51
52
|
return IconStack2;
|
|
52
53
|
if (feed.kind === 'subagent')
|
|
53
54
|
return profileTypeIcon(of) ?? AGENT_ICON;
|
|
54
|
-
if (feed.kind === 'tool-
|
|
55
|
+
if (feed.kind === 'tool-runtime')
|
|
55
56
|
return IconSettingsAutomation;
|
|
57
|
+
if (feed.kind === 'guardrail-runtime')
|
|
58
|
+
return IconShieldCheck;
|
|
59
|
+
if (feed.kind === 'destinations')
|
|
60
|
+
return IconDatabase;
|
|
61
|
+
if (feed.kind === 'vault')
|
|
62
|
+
return IconVault;
|
|
63
|
+
if (feed.kind === 'model')
|
|
64
|
+
return IconCpu;
|
|
56
65
|
return CHECK_ICONS[of] ?? IconShieldCheck;
|
|
57
66
|
}
|
|
58
|
-
/** Right from `from` to `rail`, up or down it, and back left to `to`, its corners turned. */
|
|
59
|
-
function sidePath(from, to, rail) {
|
|
60
|
-
const up = Math.sign(to.y - from.y);
|
|
61
|
-
return (`M ${from.x} ${from.y} H ${rail - TURN} Q ${rail} ${from.y} ${rail} ${from.y + up * TURN}` +
|
|
62
|
-
` V ${to.y - up * TURN} Q ${rail} ${to.y} ${rail - TURN} ${to.y} H ${to.x}`);
|
|
63
|
-
}
|
|
64
67
|
/** Down from under the parent's icon, then a turn across to the child. */
|
|
65
68
|
function linePath(from, to) {
|
|
66
69
|
const x = from.x + TRUNK;
|
|
@@ -77,9 +80,28 @@ function underPath(from, to) {
|
|
|
77
80
|
return (`M ${from.x + TRUNK} ${from.y + HALF} V ${across} H ${trunk} V ${to.y - TURN}` +
|
|
78
81
|
` Q ${trunk} ${to.y} ${trunk + TURN} ${to.y} H ${to.x - LINE_GAP}`);
|
|
79
82
|
}
|
|
80
|
-
const clamp = (value) => Math.min(Math.max(value, 0), 1);
|
|
81
83
|
const NONE = { x: 0, y: 0 };
|
|
82
|
-
|
|
84
|
+
/**
|
|
85
|
+
* How large the map stands, and where in `room`, this far settled: closed and grown alike it fits
|
|
86
|
+
* the box it stands in, never larger than itself, in the middle of it, easing from the one to the
|
|
87
|
+
* other. Closed at given places, which are its own pixels, it stands at its own size from the top left.
|
|
88
|
+
*/
|
|
89
|
+
function frameAt(closed, grown, room, settled, given) {
|
|
90
|
+
const fitting = (size) => room ? Math.min(1, room.width / size.width, room.height / size.height) : 1;
|
|
91
|
+
const middle = (size) => room
|
|
92
|
+
? {
|
|
93
|
+
x: Math.max(0, room.width - size.width * fitting(size)) / 2,
|
|
94
|
+
y: Math.max(0, room.height - size.height * fitting(size)) / 2,
|
|
95
|
+
}
|
|
96
|
+
: NONE;
|
|
97
|
+
const closedScale = given ? 1 : fitting(closed);
|
|
98
|
+
const [from, to] = [given ? NONE : middle(closed), middle(grown)];
|
|
99
|
+
return {
|
|
100
|
+
scale: closedScale + (fitting(grown) - closedScale) * settled,
|
|
101
|
+
centre: { x: from.x + (to.x - from.x) * settled, y: from.y + (to.y - from.y) * settled },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function StudioTreeMap({ workspace, agents, grow = 0, fit, room = fit, still, closed: given, draggable = false, }) {
|
|
83
105
|
const root = useRef(null);
|
|
84
106
|
const [moved, setMoved] = useState({});
|
|
85
107
|
const [field, setField] = useState(NONE);
|
|
@@ -87,54 +109,157 @@ export function StudioTreeMap({ workspace, agents, grow = 0, fit, draggable = fa
|
|
|
87
109
|
// why: Every token is drawn from the start, the undrawn ones unseen, so each is measured before it shows.
|
|
88
110
|
const widths = useTokenWidths(root, workspace);
|
|
89
111
|
const { width: fitWidth, height: fitHeight } = fit ?? { width: 0, height: 0 };
|
|
90
|
-
const layout = useMemo(() => treeMapLayout(workspace, agents, (id) => widths[id] ?? GUESS, fitWidth > 0 && fitHeight > 0 ? { width: fitWidth, height: fitHeight } : undefined), [workspace, agents, widths, fitWidth, fitHeight]);
|
|
112
|
+
const layout = useMemo(() => treeMapLayout(workspace, agents, (id) => widths[id] ?? GUESS, fitWidth > 0 && fitHeight > 0 ? { width: fitWidth, height: fitHeight } : undefined, still, given), [workspace, agents, widths, fitWidth, fitHeight, still, given]);
|
|
91
113
|
const width = (id) => widths[id] ?? GUESS;
|
|
92
114
|
const drafts = new Map(workspace.agents.map((agent) => [
|
|
93
115
|
agent.key,
|
|
94
116
|
libraryDraft(workspace, agent.key) ?? createBlankDraft(),
|
|
95
117
|
]));
|
|
96
|
-
const { rows, feeds } = layout;
|
|
118
|
+
const { rows, feeds, leaving } = layout;
|
|
119
|
+
const gone = goneAt(grow);
|
|
97
120
|
const at = (id) => {
|
|
98
121
|
const place = placeAt(layout, id, grow);
|
|
99
122
|
const by = moved[id] ?? NONE;
|
|
100
123
|
return { ...place, x: place.x + by.x, y: place.y + by.y };
|
|
101
124
|
};
|
|
125
|
+
// why: What growing adds draws as the growing goes, and goes back as it comes back: no clock of its own.
|
|
102
126
|
const drawn = (id) => drawnAt(layout, id, grow);
|
|
103
|
-
const
|
|
104
|
-
/**
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
127
|
+
const on = (id) => (drawn(id) ?? 1) > 0;
|
|
128
|
+
/**
|
|
129
|
+
* A line drawing runs out from its source at the drawing's pace, as a pen draws it, the loader's
|
|
130
|
+
* map's way: the length of it drawn so far shows, and the rest is still to come.
|
|
131
|
+
*/
|
|
132
|
+
const lineDrawn = (source, target) => {
|
|
133
|
+
const run = lineAt(layout, source, target, grow);
|
|
134
|
+
if (run <= 0)
|
|
135
|
+
return undefined;
|
|
136
|
+
if (run >= 1)
|
|
137
|
+
return { className: 'map-line' };
|
|
138
|
+
return {
|
|
139
|
+
className: 'map-line',
|
|
140
|
+
pathLength: 1,
|
|
141
|
+
style: { strokeDasharray: 1, strokeDashoffset: 1 - run },
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
/** A token, as solid as it has shown since the drawing reached it. */
|
|
145
|
+
const tokenDrawn = (id) => {
|
|
146
|
+
const out = drawn(id) ?? 1;
|
|
147
|
+
const zIndex = atRest(layout, id, grow) ? 2 : 1;
|
|
148
|
+
return { style: out < 1 ? { opacity: settling(out), zIndex } : { zIndex } };
|
|
111
149
|
};
|
|
112
|
-
const shown = feeds.filter((feed) => (
|
|
150
|
+
const shown = feeds.filter((feed) => on(feed.id));
|
|
113
151
|
const right = (id) => at(id).x + width(id);
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
152
|
+
// why: A model wears its family's mark, the Gemini sparkle or the provider's, as the editor's tree does.
|
|
153
|
+
const iconOf = (feed) => (feed.kind === 'model' && feed.of !== undefined && modelFamilyMark(feed.of)) || feedIcon(feed);
|
|
154
|
+
/**
|
|
155
|
+
* The rail the lines from a source to feeds in its own column run down, right of it: clear of every
|
|
156
|
+
* token they pass, and one for all of them, so they leave the source as one line.
|
|
157
|
+
*/
|
|
158
|
+
const railOf = (source) => {
|
|
159
|
+
const ends = feeds.filter((feed) => feed.from.includes(source) && beside(layout, source, feed));
|
|
160
|
+
const ys = [at(source).y, ...ends.map((feed) => at(feed.id).y)];
|
|
161
|
+
const [top, bottom] = [Math.min(...ys), Math.max(...ys)];
|
|
162
|
+
const column = ends[0]?.column;
|
|
163
|
+
const passed = feeds
|
|
164
|
+
.filter((each) => each.column === column)
|
|
165
|
+
.filter((each) => at(each.id).y >= top && at(each.id).y <= bottom)
|
|
166
|
+
.map((each) => right(each.id) + LINE_GAP);
|
|
167
|
+
return Math.max(right(source) + LINE_GAP, ...passed) + BOW;
|
|
122
168
|
};
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
169
|
+
// why: Closed, a row's lines run down one trunk to what it shows; grown, each runs down its feed's
|
|
170
|
+
// lane like the tree's, its steps aside growing out of its row.
|
|
171
|
+
const square = settledAt(grow);
|
|
172
|
+
const toward = (from, to, at) => ({
|
|
173
|
+
x: from.x + (to.x - from.x) * at,
|
|
174
|
+
y: from.y + (to.y - from.y) * at,
|
|
126
175
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
176
|
+
const lines = feeds.flatMap((feed) => {
|
|
177
|
+
return [...feed.from, ...(feed.late ?? [])].flatMap((source) => {
|
|
178
|
+
if (!on(source) || lineAt(layout, source, feed.id, grow) <= 0)
|
|
179
|
+
return [];
|
|
180
|
+
const end = at(feed.id);
|
|
181
|
+
const start = { x: right(source) + LINE_GAP, y: at(source).y };
|
|
182
|
+
if (beside(layout, source, feed)) {
|
|
183
|
+
const into = { x: end.x + width(feed.id) + LINE_GAP, y: end.y };
|
|
184
|
+
return [
|
|
185
|
+
{
|
|
186
|
+
key: lineKey(source, feed.id),
|
|
187
|
+
d: mapBracket(start, into, railOf(source), TURN),
|
|
188
|
+
target: feed.id,
|
|
189
|
+
source,
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
const lane = layout.lanes.get(lineKey(source, feed.id));
|
|
194
|
+
const grownRail = lane ? at(lane.of).x - lane.by : (start.x + end.x) / 2;
|
|
195
|
+
const trunk = layout.trunks.get(source);
|
|
196
|
+
const steps = (layout.jogs.get(lineKey(source, feed.id)) ?? []).map((jog) => ({
|
|
197
|
+
x: at(jog.lane.of).x - jog.lane.by,
|
|
198
|
+
y: at(jog.near).y + (jog.side * layout.row) / 2,
|
|
199
|
+
}));
|
|
200
|
+
const into = { x: end.x - LINE_GAP, y: end.y };
|
|
201
|
+
const d = trunk === undefined
|
|
202
|
+
? mapRoute(start, into, steps, grownRail, TURN, square)
|
|
203
|
+
: mapRoute(start, into, steps.map((step) => toward(start, step, square)), trunk + (grownRail - trunk) * square, TURN, 1);
|
|
204
|
+
return [{ key: lineKey(source, feed.id), d, source, target: feed.id }];
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
const agentsOf = new Set(layout.becomes.values());
|
|
208
|
+
/**
|
|
209
|
+
* The line from an agent tool to the agent of the still it grows into. why: Closed, the agent
|
|
210
|
+
* stands as its own in the tree, so the line leaves the tool's right, runs down right of what
|
|
211
|
+
* stands between, and comes back into the agent's right; grown, it is the line from under the
|
|
212
|
+
* tool's icon into the agent in its slot. Each corner eases from the one to the other.
|
|
213
|
+
*/
|
|
214
|
+
const backPath = (holder, agent) => {
|
|
215
|
+
const [tool, to] = [at(holder), at(agent)];
|
|
216
|
+
const [top, bottom] = [Math.min(tool.y, to.y), Math.max(tool.y, to.y)];
|
|
217
|
+
const passed = [...shown.map((feed) => feed.id), ...leaving.feeds.map((feed) => feed.id)]
|
|
218
|
+
.filter((id) => at(id).y >= top && at(id).y <= bottom)
|
|
219
|
+
.map((id) => right(id) + LINE_GAP);
|
|
220
|
+
const rail = Math.max(right(holder) + LINE_GAP, right(agent) + LINE_GAP, ...passed) + BOW;
|
|
221
|
+
const closed = [
|
|
222
|
+
{ x: right(holder) + LINE_GAP, y: tool.y },
|
|
223
|
+
{ x: rail, y: tool.y },
|
|
224
|
+
{ x: rail, y: (tool.y + to.y) / 2 },
|
|
225
|
+
{ x: rail, y: to.y },
|
|
226
|
+
{ x: right(agent) + LINE_GAP, y: to.y },
|
|
227
|
+
];
|
|
228
|
+
const trunk = to.x - INDENT + TRUNK;
|
|
229
|
+
const across = tool.y + HALF + LINE_GAP;
|
|
230
|
+
const grown = [
|
|
231
|
+
{ x: tool.x + TRUNK, y: tool.y + HALF },
|
|
232
|
+
{ x: tool.x + TRUNK, y: across },
|
|
233
|
+
{ x: trunk, y: across },
|
|
234
|
+
{ x: trunk, y: to.y },
|
|
235
|
+
{ x: to.x - LINE_GAP, y: to.y },
|
|
236
|
+
];
|
|
237
|
+
return mapPolyline(closed.map((point, index) => toward(point, grown[index] ?? point, square)), TURN);
|
|
134
238
|
};
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
const
|
|
239
|
+
// What growing takes away goes under the rest: a row folding into its agent, a feed drifting off.
|
|
240
|
+
const going = gone < 1;
|
|
241
|
+
const leavingStyle = { opacity: 1 - gone, zIndex: 0 };
|
|
242
|
+
const parentOf = (id) => layout.becomes.get(id) ?? id;
|
|
243
|
+
const leavingLines = going
|
|
244
|
+
? leaving.lines.map(({ source, feed }) => {
|
|
245
|
+
const end = at(feed);
|
|
246
|
+
const start = { x: right(source) + LINE_GAP, y: at(source).y };
|
|
247
|
+
const rail = layout.trunks.get(source) ?? (start.x + end.x) / 2;
|
|
248
|
+
return {
|
|
249
|
+
key: lineKey(source, feed),
|
|
250
|
+
d: mapRoute(start, { x: end.x - LINE_GAP, y: end.y }, [], rail, TURN, 1),
|
|
251
|
+
};
|
|
252
|
+
})
|
|
253
|
+
: [];
|
|
254
|
+
// why: What growing takes away holds the map open until it has gone.
|
|
255
|
+
const places = [
|
|
256
|
+
...rows.map(rowId),
|
|
257
|
+
...shown.map((feed) => feed.id),
|
|
258
|
+
...(going ? [...leaving.rows.map(rowId), ...leaving.feeds.map((feed) => feed.id)] : []),
|
|
259
|
+
];
|
|
260
|
+
// why: An agent tool's line back to its agent runs down right of the feeds.
|
|
261
|
+
const back = layout.becomes.size > 0 ? LINE_GAP + BOW : 0;
|
|
262
|
+
const mapWidth = Math.max(GUESS, ...places.map(right)) + back + PAD;
|
|
138
263
|
const height = Math.max(...places.map((id) => at(id).y)) + HALF + PAD;
|
|
139
264
|
const grown = useMemo(() => {
|
|
140
265
|
const ids = [...layout.rows.map(rowId), ...layout.feeds.map((feed) => feed.id)];
|
|
@@ -144,15 +269,17 @@ export function StudioTreeMap({ workspace, agents, grow = 0, fit, draggable = fa
|
|
|
144
269
|
height: Math.max(...ends.map((end) => end.y)) + HALF + PAD,
|
|
145
270
|
};
|
|
146
271
|
}, [layout, widths]);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
272
|
+
// why: The map eases to the size of the box it stands in with its nodes' move, so it never
|
|
273
|
+
// outgrows it, and grows with it after.
|
|
274
|
+
const closedSize = useMemo(() => {
|
|
275
|
+
const ends = [...layout.closed];
|
|
276
|
+
return {
|
|
277
|
+
width: Math.max(...ends.map(([id, end]) => end.x + (widths[id] ?? GUESS))) + back + PAD,
|
|
278
|
+
height: Math.max(...ends.map(([, end]) => end.y)) + HALF + PAD,
|
|
279
|
+
};
|
|
280
|
+
}, [layout, widths, back]);
|
|
281
|
+
const settled = settledAt(grow);
|
|
282
|
+
const { scale, centre } = frameAt(closedSize, grown, room, settled, given !== undefined);
|
|
156
283
|
// why: The pointer moves in the page's pixels; the map is scaled, so a move is divided back into its own.
|
|
157
284
|
const drag = (event) => {
|
|
158
285
|
if (!draggable || !drags(event))
|
|
@@ -177,24 +304,37 @@ export function StudioTreeMap({ workspace, agents, grow = 0, fit, draggable = fa
|
|
|
177
304
|
});
|
|
178
305
|
};
|
|
179
306
|
const shift = { x: field.x + centre.x, y: field.y + centre.y };
|
|
307
|
+
const rowToken = (row, look) => {
|
|
308
|
+
const draft = drafts.get(row.agent) ?? createBlankDraft();
|
|
309
|
+
const { x, y } = at(rowId(row));
|
|
310
|
+
return (_jsx(MapToken, { id: rowId(row), label: row.node.label, icon: nodeIcon(draft, row.node.ref), x: x, y: y, ...look }, rowId(row)));
|
|
311
|
+
};
|
|
312
|
+
/** A feed's token, `drift` right of where it stands. */
|
|
313
|
+
const feedToken = (feed, look, drift = 0) => {
|
|
314
|
+
const { x, y } = at(feed.id);
|
|
315
|
+
return (_jsx(MapToken, { id: feed.id, label: feed.label, icon: iconOf(feed), x: x + drift, y: y, ...look }, feed.id));
|
|
316
|
+
};
|
|
180
317
|
const translate = shift.x || shift.y ? { translate: `${shift.x}px ${shift.y}px` } : {};
|
|
181
318
|
return (_jsxs("div", { ref: root, className: "studio-tree-map", style: {
|
|
182
319
|
width: mapWidth,
|
|
183
320
|
height,
|
|
184
321
|
...translate,
|
|
185
322
|
...(scale < 1 ? { scale: String(scale) } : {}),
|
|
186
|
-
}, "data-draggable": draggable ? '' : undefined, "data-dragging": dragging ? '' : undefined, onPointerDown: drag, children: [_jsxs("svg", { className: "studio-tree-map-lines", width: mapWidth, height: height, "aria-hidden": "true", children: [rows.map((row) =>
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
323
|
+
}, "data-draggable": draggable ? '' : undefined, "data-dragging": dragging ? '' : undefined, onPointerDown: drag, children: [_jsxs("svg", { className: "studio-tree-map-lines", width: mapWidth, height: height, "aria-hidden": "true", children: [rows.map((row) => {
|
|
324
|
+
const drawing = row.parent !== undefined && lineDrawn(row.parent, rowId(row));
|
|
325
|
+
return (row.parent &&
|
|
326
|
+
drawing && (_jsx("path", { d: linePath(at(row.parent), at(rowId(row))), ...drawing }, rowId(row))));
|
|
327
|
+
}), going &&
|
|
328
|
+
leaving.rows.map((row) => row.parent && (_jsx("path", { className: "map-line", d: linePath(at(parentOf(row.parent)), at(rowId(row))), style: leavingStyle }, rowId(row)))), leavingLines.map(({ key, d }) => (_jsx("path", { className: "map-line", d: d, style: leavingStyle }, key))), lines.map(({ key, d, source, target }) => {
|
|
329
|
+
const drawing = lineDrawn(source, target);
|
|
330
|
+
return drawing && _jsx("path", { d: d, ...drawing }, key);
|
|
331
|
+
}), feeds.map((feed) => {
|
|
332
|
+
const holder = feed.under;
|
|
333
|
+
const drawing = holder !== undefined && on(holder) && lineDrawn(holder, feed.id);
|
|
334
|
+
return (holder !== undefined &&
|
|
335
|
+
drawing && (_jsx("path", { d: agentsOf.has(feed.id)
|
|
336
|
+
? backPath(holder, feed.id)
|
|
337
|
+
: underPath(at(holder), at(feed.id)), ...drawing }, underKey(feed.id))));
|
|
338
|
+
})] }), settled > 0 &&
|
|
339
|
+
layout.headings.map(({ label, note, x, y }) => (_jsxs("div", { className: "studio-tree-map-heading", style: { left: x, top: y, opacity: settled }, children: [_jsx(Text, { type: "supporting", color: "secondary", weight: "semibold", children: label }), _jsx(Text, { type: "supporting", color: "placeholder", children: note })] }, label))), rows.map((row) => rowToken(row, tokenDrawn(rowId(row)))), going && leaving.rows.map((row) => rowToken(row, { style: leavingStyle })), going && leaving.feeds.map((feed) => feedToken(feed, { style: leavingStyle }, DRIFT * gone)), feeds.map((feed) => feedToken(feed, tokenDrawn(feed.id)))] }));
|
|
200
340
|
}
|