@weasel-js/diagram 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +696 -4
- package/dist/chunk-2SYY4W3B.js +2055 -0
- package/dist/chunk-2SYY4W3B.js.map +1 -0
- package/dist/chunk-5GIP2LA6.js +312 -0
- package/dist/chunk-5GIP2LA6.js.map +1 -0
- package/dist/chunk-DYDX6EJC.js +3653 -0
- package/dist/chunk-DYDX6EJC.js.map +1 -0
- package/dist/chunk-GA4BX7SH.js +1431 -0
- package/dist/chunk-GA4BX7SH.js.map +1 -0
- package/dist/{chunk-NUMMW5BI.js → chunk-R2XB6D32.js} +416 -36
- package/dist/chunk-R2XB6D32.js.map +1 -0
- package/dist/chunk-WDIE35AK.js +3 -0
- package/dist/chunk-WDIE35AK.js.map +1 -0
- package/dist/{fromData-GKqRgPFs.d.ts → dataTypes-DHdkzCL8.d.ts} +238 -59
- package/dist/effect-audio.d.ts +139 -0
- package/dist/effect-audio.js +533 -0
- package/dist/effect-audio.js.map +1 -0
- package/dist/effect.d.ts +486 -121
- package/dist/effect.js +1632 -347
- package/dist/effect.js.map +1 -1
- package/dist/icons.table.d.ts +6 -0
- package/dist/icons.table.js +32 -0
- package/dist/icons.table.js.map +1 -0
- package/dist/icons.table.json +24 -0
- package/dist/index.d.ts +2159 -92
- package/dist/index.js +1056 -2
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +3 -3
- package/dist/layout.entry-B-jJMc50.d.ts +617 -0
- package/dist/layout.js +3 -1
- package/dist/stageTypes-DvnR8cKQ.d.ts +189 -0
- package/dist/{body-2lC7IiIA.d.ts → types-M_ccNAlk.d.ts} +221 -182
- package/package.json +28 -7
- package/dist/chunk-C4NAI6NV.js +0 -2041
- package/dist/chunk-C4NAI6NV.js.map +0 -1
- package/dist/chunk-NUMMW5BI.js.map +0 -1
- package/dist/layout.entry-BoWFcR4G.d.ts +0 -285
package/dist/effect.js
CHANGED
|
@@ -1,200 +1,398 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { DiagramView, reorderContribution, ANYWHERE, isBodyEnd, sequenceSlots } from './chunk-DYDX6EJC.js';
|
|
2
|
+
import './chunk-GA4BX7SH.js';
|
|
3
|
+
import { stageFromType, LOOK_THEME, drawableWires, uniqueStages, shownPorts, valueRowId, NO_VALUE, previewRowId, formatParam, stageStyle, PROBLEM_COLOR, typeColors, kindOf, boxNode, portOn, wireStyle, isControl, wireEdge, sceneStyles, uniqueGroups, wireEdges, exitLabel, splitProblem, checkPipeline, PREVIEW_DENSITY, runOrder, PREVIEW_SIZE, closeBitmaps, previewsOf, paramValues } from './chunk-5GIP2LA6.js';
|
|
4
|
+
export { LOOK_THEME, NO_VALUE, PREVIEW_SIZE, PROBLEM_COLOR, checkPipeline, drawableWires, exitLabel, formatParam, isControl, kindOf, portOn, previewRowId, runOrder, shownPorts, stageFromType, stageRows, stageStyle, typeColors, uniqueGroups, uniqueStages, valueRowId, wireEdges, wireStyle } from './chunk-5GIP2LA6.js';
|
|
5
|
+
import { diagramScene, diagramPages, edgeIdOf, titleIdOf, groupLabelIdOf, nestGroups, groupMinWidth, LABEL_PLACEMENT, EDGE_DERIVE_PATH } from './chunk-2SYY4W3B.js';
|
|
6
|
+
import { portOf, insetOf, diagramNodeOf, DEFAULT_GROUP_INSET, SIDE_TRACK_GAP, widenForMinWidth, rankGaps, portSitesOf, labelCenter } from './chunk-R2XB6D32.js';
|
|
3
7
|
import { polygonFromPoints } from '@weasel-js/core/math';
|
|
4
|
-
import { useMemo,
|
|
5
|
-
import { bitmapSrc,
|
|
8
|
+
import { forwardRef, useMemo, useRef, useState, useImperativeHandle, useEffect, useCallback, useSyncExternalStore } from 'react';
|
|
9
|
+
import { useLatest, bitmapSrc, ActionDisabledReason, pointAlongPath } from '@weasel-js/core';
|
|
6
10
|
import { releaseImageSource } from '@weasel-js/core/renderer';
|
|
7
11
|
import { jsx } from 'react/jsx-runtime';
|
|
12
|
+
import { useLatest as useLatest$1, adoptCssOnce, useVisibleRaf } from '@weasel-js/react';
|
|
8
13
|
|
|
9
|
-
// src/effect/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (stages.has(stage.id)) {
|
|
26
|
-
out.push({ kind: "duplicate-stage", stage: stage.id, message: `Stage "${stage.id}" appears more than once.` });
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
stages.set(stage.id, stage);
|
|
30
|
-
const seen = /* @__PURE__ */ new Set();
|
|
31
|
-
for (const port of [...stage.inputs ?? [], ...stage.outputs ?? []]) {
|
|
32
|
-
if (seen.has(port.id)) {
|
|
33
|
-
out.push({ kind: "duplicate-port", stage: stage.id, port: port.id, message: `Stage "${stage.id}" has two ports called "${port.id}".` });
|
|
34
|
-
}
|
|
35
|
-
seen.add(port.id);
|
|
36
|
-
}
|
|
37
|
-
const split = splitProblem(stage);
|
|
38
|
-
if (split !== null) out.push({ kind: "bad-split", stage: stage.id, message: `Stage "${stage.id}" ${split}.` });
|
|
14
|
+
// src/effect/groupEdit.ts
|
|
15
|
+
var groupsOf = (doc) => doc.pipeline.groups ?? [];
|
|
16
|
+
function withGroups(doc, groups, boxes = doc.groupBoxes) {
|
|
17
|
+
const ids = new Set(groups.map((g) => g.id));
|
|
18
|
+
const kept = Object.entries(boxes ?? {}).filter(([id]) => ids.has(id));
|
|
19
|
+
const { groupBoxes: _was, ...rest } = doc;
|
|
20
|
+
return { ...rest, pipeline: { ...doc.pipeline, groups }, ...kept.length > 0 ? { groupBoxes: Object.fromEntries(kept) } : {} };
|
|
21
|
+
}
|
|
22
|
+
function pruneGroups(groups, gone, keep = () => false) {
|
|
23
|
+
const out = new Set(gone);
|
|
24
|
+
let left = [...groups];
|
|
25
|
+
for (; ; ) {
|
|
26
|
+
const emptied = left.filter((g) => g.members.length > 0 && g.members.every((m) => out.has(m)) && !keep(g.id));
|
|
27
|
+
if (emptied.length === 0) return left.map((g) => ({ ...g, members: g.members.filter((m) => !out.has(m)) }));
|
|
28
|
+
for (const g of emptied) out.add(g.id);
|
|
29
|
+
left = left.filter((g) => !emptied.includes(g));
|
|
39
30
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
out.push({
|
|
64
|
-
kind: "duplicate-group",
|
|
65
|
-
group: group.id,
|
|
66
|
-
message: repeat ? `Group "${group.id}" appears more than once.` : `Group "${group.id}" has the id of a stage.`
|
|
67
|
-
});
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
groupIds.add(group.id);
|
|
71
|
-
return true;
|
|
31
|
+
}
|
|
32
|
+
var parentOf = (groups, id) => groups.find((g) => g.members.includes(id));
|
|
33
|
+
function nextGroupId(p) {
|
|
34
|
+
const used = /* @__PURE__ */ new Set([...p.stages.map((s) => s.id), ...(p.groups ?? []).map((g) => g.id)]);
|
|
35
|
+
if (!used.has("group")) return "group";
|
|
36
|
+
let n = 2;
|
|
37
|
+
while (used.has(`group-${n}`)) n++;
|
|
38
|
+
return `group-${n}`;
|
|
39
|
+
}
|
|
40
|
+
function groupStages(doc, ids, label) {
|
|
41
|
+
if (ids.length === 0) return doc;
|
|
42
|
+
const id = nextGroupId(doc.pipeline);
|
|
43
|
+
const groups = groupsOf(doc);
|
|
44
|
+
const parents = new Set(ids.map((m) => parentOf(groups, m)?.id));
|
|
45
|
+
const shared = parents.size === 1 ? [...parents][0] : void 0;
|
|
46
|
+
const taken = new Set(ids);
|
|
47
|
+
const made = { id, ...label ? { label } : {}, members: [...ids] };
|
|
48
|
+
const rest = shared === void 0 ? pruneGroups(groups, taken) : groups.map((g) => {
|
|
49
|
+
if (g.id !== shared) return g;
|
|
50
|
+
const at = g.members.findIndex((m) => taken.has(m));
|
|
51
|
+
const members = g.members.filter((m) => !taken.has(m));
|
|
52
|
+
members.splice(at, 0, id);
|
|
53
|
+
return { ...g, members };
|
|
72
54
|
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
55
|
+
return withGroups(doc, [...rest, made]);
|
|
56
|
+
}
|
|
57
|
+
function ungroup(doc, id) {
|
|
58
|
+
const groups = groupsOf(doc);
|
|
59
|
+
const gone = groups.find((g) => g.id === id);
|
|
60
|
+
if (gone === void 0) return doc;
|
|
61
|
+
const kept = groups.filter((g) => g.id !== id).map((g) => g.members.includes(id) ? { ...g, members: g.members.flatMap((m) => m === id ? gone.members : [m]) } : g);
|
|
62
|
+
return withGroups(doc, kept);
|
|
63
|
+
}
|
|
64
|
+
function renameGroup(doc, id, label) {
|
|
65
|
+
return withGroups(doc, groupsOf(doc).map((g) => {
|
|
66
|
+
if (g.id !== id) return g;
|
|
67
|
+
const { label: _was, ...rest } = g;
|
|
68
|
+
return label === "" ? rest : { ...rest, label };
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
function within(groups, inner, outer) {
|
|
72
|
+
for (let at = inner; at !== void 0; at = parentOf(groups, at)?.id) if (at === outer) return true;
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
function moveToGroup(doc, ids, group) {
|
|
76
|
+
const groups = groupsOf(doc);
|
|
77
|
+
if (group !== null && (!groups.some((g) => g.id === group) || ids.some((id) => within(groups, group, id)))) return doc;
|
|
78
|
+
const moving = new Set(ids);
|
|
79
|
+
const boxed = (id) => id === group || doc.groupBoxes?.[id] !== void 0;
|
|
80
|
+
const left = pruneGroups(groups, moving, boxed);
|
|
81
|
+
const placed = group === null ? left : left.map((g) => g.id === group ? { ...g, members: [...g.members, ...ids] } : g);
|
|
82
|
+
return withGroups(doc, placed);
|
|
83
|
+
}
|
|
84
|
+
function setGroupBox(doc, id, box) {
|
|
85
|
+
const { [id]: _was, ...rest } = doc.groupBoxes ?? {};
|
|
86
|
+
return withGroups(doc, groupsOf(doc), box === null ? rest : { ...rest, [id]: { x: box.x, y: box.y, width: box.width, height: box.height } });
|
|
87
|
+
}
|
|
88
|
+
function groupContents(doc, id) {
|
|
89
|
+
const groups = new Map(groupsOf(doc).map((g) => [g.id, g]));
|
|
90
|
+
const out = { stages: /* @__PURE__ */ new Set(), groups: /* @__PURE__ */ new Set() };
|
|
91
|
+
const visit = (g) => {
|
|
92
|
+
if (out.groups.has(g)) return;
|
|
93
|
+
out.groups.add(g);
|
|
94
|
+
for (const m of groups.get(g)?.members ?? []) {
|
|
95
|
+
if (groups.has(m)) visit(m);
|
|
96
|
+
else out.stages.add(m);
|
|
77
97
|
}
|
|
78
|
-
}
|
|
98
|
+
};
|
|
99
|
+
if (groups.has(id)) visit(id);
|
|
79
100
|
return out;
|
|
80
101
|
}
|
|
102
|
+
function moveGroup(doc, id, offset) {
|
|
103
|
+
const { stages, groups } = groupContents(doc, id);
|
|
104
|
+
const by = (p) => ({ x: p.x + offset.x, y: p.y + offset.y });
|
|
105
|
+
const places = Object.fromEntries(Object.entries(doc.places).map(([s, p]) => [s, stages.has(s) ? by(p) : p]));
|
|
106
|
+
const inside = new Set(doc.pipeline.wires.flatMap((w) => w.id !== void 0 && stages.has(w.from.stage) && stages.has(w.to.stage) ? [w.id] : []));
|
|
107
|
+
const routes = doc.routes === void 0 ? void 0 : Object.fromEntries(Object.entries(doc.routes).map(([w, r]) => [w, inside.has(w) ? r.map(by) : r]));
|
|
108
|
+
const boxes = doc.groupBoxes === void 0 ? void 0 : Object.fromEntries(Object.entries(doc.groupBoxes).map(([g, b]) => [g, groups.has(g) ? { ...b, ...by(b) } : b]));
|
|
109
|
+
return {
|
|
110
|
+
...doc,
|
|
111
|
+
places,
|
|
112
|
+
...routes !== void 0 ? { routes } : {},
|
|
113
|
+
...boxes !== void 0 ? { groupBoxes: boxes } : {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
81
116
|
|
|
82
|
-
// src/effect/
|
|
83
|
-
var
|
|
84
|
-
var
|
|
85
|
-
var
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
117
|
+
// src/effect/lookRoutes.ts
|
|
118
|
+
var EPS = 1e-3;
|
|
119
|
+
var near = (a, b) => Math.abs(a.x - b.x) <= EPS && Math.abs(a.y - b.y) <= EPS;
|
|
120
|
+
var copy = (p) => ({ x: p.x, y: p.y });
|
|
121
|
+
function withLookRoutes(doc, routes) {
|
|
122
|
+
const kept = Object.fromEntries(Object.entries(routes).filter(([, r]) => r !== void 0 && Object.keys(r).length > 0));
|
|
123
|
+
const { lookRoutes: _was, ...rest } = doc;
|
|
124
|
+
return Object.keys(kept).length > 0 ? { ...rest, lookRoutes: kept } : rest;
|
|
125
|
+
}
|
|
126
|
+
function setLookRoute(doc, look, wire, waypoints, ends) {
|
|
127
|
+
const { [wire]: _was, ...rest } = doc.lookRoutes?.[look] ?? {};
|
|
128
|
+
const route = { waypoints: waypoints.map(copy), ends: [copy(ends[0]), copy(ends[1])] };
|
|
129
|
+
return withLookRoutes(doc, { ...doc.lookRoutes, [look]: waypoints.length > 0 ? { ...rest, [wire]: route } : rest });
|
|
130
|
+
}
|
|
131
|
+
function pruneLookRoutes(doc, rewired = []) {
|
|
132
|
+
if (doc.lookRoutes === void 0) return doc;
|
|
133
|
+
const gone = new Set(rewired);
|
|
134
|
+
const ids = new Set(doc.pipeline.wires.flatMap((w) => w.id === void 0 || gone.has(w.id) ? [] : [w.id]));
|
|
135
|
+
const keep = (r) => r === void 0 ? void 0 : Object.fromEntries(Object.entries(r).filter(([id]) => ids.has(id)));
|
|
136
|
+
return withLookRoutes(doc, { rack: keep(doc.lookRoutes.rack), lanes: keep(doc.lookRoutes.lanes) });
|
|
137
|
+
}
|
|
138
|
+
function wireEnds(specs, edge) {
|
|
139
|
+
const spec = specs.find((s) => s.id === edge);
|
|
140
|
+
const trait = spec?.data?.diagram;
|
|
141
|
+
const [from, to] = spec?.dependsOn ?? [];
|
|
142
|
+
const point = (node, port) => {
|
|
143
|
+
const n = node === void 0 || port === void 0 ? void 0 : specs.find((s) => s.id === node);
|
|
144
|
+
return n === void 0 ? void 0 : portOf({ id: node, kind: n.kind ?? "leaf", data: n.data }, n.pose, port)?.point;
|
|
145
|
+
};
|
|
146
|
+
const a = point(from, trait?.from?.port);
|
|
147
|
+
const b = point(to, trait?.to?.port);
|
|
148
|
+
return a === void 0 || b === void 0 ? void 0 : [a, b];
|
|
149
|
+
}
|
|
150
|
+
function applyLookRoutes(specs, doc, look, built) {
|
|
151
|
+
const routes = look === "nodeGraph" ? void 0 : doc.lookRoutes?.[look];
|
|
152
|
+
if (routes === void 0) return specs;
|
|
153
|
+
let held = false;
|
|
154
|
+
const edges = built.data.edges.map((e) => {
|
|
155
|
+
const id = edgeIdOf(e);
|
|
156
|
+
const wire = built.wireOf(id);
|
|
157
|
+
const route = wire === void 0 ? void 0 : routes[wire];
|
|
158
|
+
const ends = route === void 0 ? void 0 : wireEnds(specs, id);
|
|
159
|
+
if (route === void 0 || ends === void 0 || !near(ends[0], route.ends[0]) || !near(ends[1], route.ends[1])) return e;
|
|
160
|
+
held = true;
|
|
161
|
+
return { ...e, waypoints: route.waypoints };
|
|
162
|
+
});
|
|
163
|
+
return held ? diagramScene({ ...built.data, edges }, built.scene) : specs;
|
|
97
164
|
}
|
|
98
|
-
|
|
99
|
-
|
|
165
|
+
|
|
166
|
+
// src/effect/edit.ts
|
|
167
|
+
var withPipeline = (doc, pipeline) => ({
|
|
168
|
+
...doc,
|
|
169
|
+
pipeline: { ...doc.pipeline, ...pipeline }
|
|
170
|
+
});
|
|
171
|
+
function nextStageId(p, base) {
|
|
172
|
+
const used = new Set(p.stages.map((s) => s.id));
|
|
173
|
+
if (!used.has(base)) return base;
|
|
174
|
+
let n = 2;
|
|
175
|
+
while (used.has(`${base}-${n}`)) n++;
|
|
176
|
+
return `${base}-${n}`;
|
|
177
|
+
}
|
|
178
|
+
function nextWireId(p) {
|
|
179
|
+
const used = new Set(p.wires.map((w) => w.id));
|
|
180
|
+
let n = 1;
|
|
181
|
+
while (used.has(`w${n}`)) n++;
|
|
182
|
+
return `w${n}`;
|
|
183
|
+
}
|
|
184
|
+
function addStage(doc, stage, at) {
|
|
185
|
+
const next = withPipeline(doc, { stages: [...doc.pipeline.stages, stage] });
|
|
186
|
+
return at ? { ...next, places: { ...doc.places, [stage.id]: { x: at.x, y: at.y } } } : next;
|
|
187
|
+
}
|
|
188
|
+
function removeStages(doc, ids) {
|
|
189
|
+
const gone = new Set(ids);
|
|
190
|
+
const { groups } = doc.pipeline;
|
|
191
|
+
const wires = doc.pipeline.wires.filter((w) => !gone.has(w.from.stage) && !gone.has(w.to.stage));
|
|
192
|
+
const kept = groups ? pruneGroups(groups, gone) : void 0;
|
|
193
|
+
const left = new Set(kept?.map((g) => g.id));
|
|
194
|
+
const boxes = doc.groupBoxes === void 0 ? void 0 : Object.entries(doc.groupBoxes).filter(([id]) => left.has(id));
|
|
195
|
+
const { groupBoxes: _was, ...rest } = doc;
|
|
196
|
+
return pruneRoutes({
|
|
197
|
+
...rest,
|
|
198
|
+
pipeline: {
|
|
199
|
+
...doc.pipeline,
|
|
200
|
+
stages: doc.pipeline.stages.filter((s) => !gone.has(s.id)),
|
|
201
|
+
wires,
|
|
202
|
+
...kept ? { groups: kept } : {}
|
|
203
|
+
},
|
|
204
|
+
places: Object.fromEntries(Object.entries(doc.places).filter(([id]) => !gone.has(id))),
|
|
205
|
+
...boxes !== void 0 && boxes.length > 0 ? { groupBoxes: Object.fromEntries(boxes) } : {}
|
|
206
|
+
});
|
|
100
207
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
208
|
+
function pruneRoutes(doc, rewired = []) {
|
|
209
|
+
const pruned = pruneLookRoutes(doc, rewired);
|
|
210
|
+
if (pruned.routes === void 0) return pruned;
|
|
211
|
+
const ids = new Set(doc.pipeline.wires.flatMap((w) => w.id === void 0 || rewired.includes(w.id) ? [] : [w.id]));
|
|
212
|
+
return { ...pruned, routes: Object.fromEntries(Object.entries(pruned.routes).filter(([id]) => ids.has(id))) };
|
|
103
213
|
}
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
width: 2,
|
|
108
|
-
markerEnd: null,
|
|
109
|
-
...control ? { dash: [5, 4] } : {}
|
|
110
|
-
};
|
|
214
|
+
function setWireRoute(doc, wire, points) {
|
|
215
|
+
const { [wire]: _was, ...rest } = doc.routes ?? {};
|
|
216
|
+
return { ...doc, routes: points.length > 0 ? { ...rest, [wire]: points.map((p) => ({ x: p.x, y: p.y })) } : rest };
|
|
111
217
|
}
|
|
112
|
-
function
|
|
113
|
-
const
|
|
114
|
-
return
|
|
218
|
+
function addWire(doc, wire) {
|
|
219
|
+
const w = wire.id === void 0 ? { ...wire, id: nextWireId(doc.pipeline) } : wire;
|
|
220
|
+
return withPipeline(doc, { wires: [...doc.pipeline.wires, w] });
|
|
115
221
|
}
|
|
116
|
-
function
|
|
117
|
-
const
|
|
118
|
-
return (
|
|
222
|
+
function removeWires(doc, ids) {
|
|
223
|
+
const gone = new Set(ids);
|
|
224
|
+
return pruneRoutes(withPipeline(doc, { wires: doc.pipeline.wires.filter((w) => w.id === void 0 || !gone.has(w.id)) }));
|
|
119
225
|
}
|
|
120
|
-
function
|
|
121
|
-
return
|
|
226
|
+
function reconnectWire(doc, id, end, to) {
|
|
227
|
+
return withPipeline(doc, {
|
|
228
|
+
wires: doc.pipeline.wires.map((w) => w.id === id ? { ...w, [end]: { stage: to.stage, port: to.port } } : w)
|
|
229
|
+
});
|
|
122
230
|
}
|
|
123
|
-
|
|
124
|
-
|
|
231
|
+
var addressed = (p, key) => (p.id ?? p.label) === key;
|
|
232
|
+
function setParam(doc, stage, param2, control) {
|
|
233
|
+
return withPipeline(doc, {
|
|
234
|
+
stages: doc.pipeline.stages.map(
|
|
235
|
+
(s) => s.id !== stage || !s.params ? s : { ...s, params: s.params.map((p) => addressed(p, param2) ? { ...p, control, value: formatParam(control) } : p) }
|
|
236
|
+
)
|
|
237
|
+
});
|
|
125
238
|
}
|
|
126
|
-
function
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
239
|
+
function moveStages(doc, moves) {
|
|
240
|
+
const places = { ...doc.places };
|
|
241
|
+
for (const m of moves) places[m.id] = { x: m.x, y: m.y };
|
|
242
|
+
return { ...doc, places };
|
|
243
|
+
}
|
|
244
|
+
function setLaneOrder(doc, order) {
|
|
245
|
+
return { ...doc, laneOrder: [...order] };
|
|
246
|
+
}
|
|
247
|
+
function ensureWireIds(doc) {
|
|
248
|
+
if (doc.pipeline.wires.every((w) => w.id !== void 0)) return doc;
|
|
249
|
+
const used = new Set(doc.pipeline.wires.map((w) => w.id));
|
|
250
|
+
let n = 1;
|
|
251
|
+
const wires = doc.pipeline.wires.map((w) => {
|
|
252
|
+
if (w.id !== void 0) return w;
|
|
253
|
+
while (used.has(`w${n}`)) n++;
|
|
254
|
+
const id = `w${n}`;
|
|
255
|
+
used.add(id);
|
|
256
|
+
return { ...w, id };
|
|
135
257
|
});
|
|
258
|
+
return withPipeline(doc, { wires });
|
|
136
259
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
function runRowsOf(o) {
|
|
143
|
-
return {
|
|
144
|
-
...o.showValues !== void 0 ? { showValues: o.showValues } : {},
|
|
145
|
-
...o.showPreviews !== void 0 ? { showPreviews: o.showPreviews } : {},
|
|
146
|
-
...o.previewSize !== void 0 ? { previewSize: o.previewSize } : {}
|
|
147
|
-
};
|
|
260
|
+
function canWire(p, from, to) {
|
|
261
|
+
if (from.stage === to.stage) return false;
|
|
262
|
+
const out = p.stages.find((s) => s.id === from.stage)?.outputs?.find((x) => x.id === from.port);
|
|
263
|
+
const inn = p.stages.find((s) => s.id === to.stage)?.inputs?.find((x) => x.id === to.port);
|
|
264
|
+
return out !== void 0 && inn !== void 0 && out.type === inn.type;
|
|
148
265
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
266
|
+
|
|
267
|
+
// src/effect/mainChain.ts
|
|
268
|
+
function mainChain(pipeline) {
|
|
269
|
+
const next = /* @__PURE__ */ new Map();
|
|
270
|
+
for (const d of drawableWires(pipeline)) {
|
|
271
|
+
if (isControl(pipeline, d.type)) continue;
|
|
272
|
+
next.set(d.wire.from.stage, [...next.get(d.wire.from.stage) ?? [], d.wire.to.stage]);
|
|
154
273
|
}
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
274
|
+
const best = /* @__PURE__ */ new Map();
|
|
275
|
+
const walking = /* @__PURE__ */ new Set();
|
|
276
|
+
const from = (id) => {
|
|
277
|
+
const known = best.get(id);
|
|
278
|
+
if (known !== void 0) return known;
|
|
279
|
+
if (walking.has(id)) return [];
|
|
280
|
+
walking.add(id);
|
|
281
|
+
let tail = [];
|
|
282
|
+
for (const to of next.get(id) ?? []) {
|
|
283
|
+
const path2 = from(to);
|
|
284
|
+
if (path2.length > tail.length && !path2.includes(id)) tail = path2;
|
|
163
285
|
}
|
|
164
|
-
|
|
286
|
+
walking.delete(id);
|
|
287
|
+
const path = [id, ...tail];
|
|
288
|
+
best.set(id, path);
|
|
289
|
+
return path;
|
|
290
|
+
};
|
|
291
|
+
let longest = [];
|
|
292
|
+
for (const stage of uniqueStages(pipeline)) {
|
|
293
|
+
const path = from(stage.id);
|
|
294
|
+
if (path.length > longest.length) longest = path;
|
|
165
295
|
}
|
|
166
|
-
|
|
167
|
-
return rows;
|
|
296
|
+
return longest;
|
|
168
297
|
}
|
|
169
|
-
function
|
|
170
|
-
|
|
171
|
-
|
|
298
|
+
function skipDecisions(pipeline, line) {
|
|
299
|
+
const wires = drawableWires(pipeline);
|
|
300
|
+
const stages = new Map(uniqueStages(pipeline).map((s) => [s.id, s]));
|
|
301
|
+
const out = [];
|
|
302
|
+
line.forEach((id, i) => {
|
|
303
|
+
const decision = stages.get(id);
|
|
304
|
+
if (decision === void 0 || kindOf(decision) !== "decision" || i === 0) return;
|
|
305
|
+
if ((decision.inputs ?? []).length !== 1 || (decision.outputs ?? []).length !== 2) return;
|
|
306
|
+
const entering = wires.filter((d) => d.wire.to.stage === id);
|
|
307
|
+
const leaving = wires.filter((d) => d.wire.from.stage === id);
|
|
308
|
+
if (entering.length !== 1 || leaving.length !== 2 || entering[0].wire.from.stage !== line[i - 1]) return;
|
|
309
|
+
const through = leaving.find((d) => d.wire.to.stage === line[i + 1]);
|
|
310
|
+
const skip = leaving.find((d) => d !== through && line.indexOf(d.wire.to.stage) > i + 1);
|
|
311
|
+
const ordinary = (s) => s !== void 0 && kindOf(s) !== "decision";
|
|
312
|
+
if (!ordinary(stages.get(line[i - 1])) || !ordinary(through?.toStage) || !ordinary(skip?.toStage)) return;
|
|
313
|
+
if (through !== void 0 && skip !== void 0) out.push({ decision, into: entering[0], through, skip });
|
|
314
|
+
});
|
|
315
|
+
return out;
|
|
172
316
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
317
|
+
|
|
318
|
+
// src/effect/chainEdit.ts
|
|
319
|
+
function chainLinks(pipeline, chain) {
|
|
320
|
+
const at = new Map(chain.map((id, i) => [id, i]));
|
|
321
|
+
const mainIn = /* @__PURE__ */ new Map();
|
|
322
|
+
const mainOut = /* @__PURE__ */ new Map();
|
|
323
|
+
const between2 = Array.from({ length: Math.max(0, chain.length - 1) }, () => void 0);
|
|
324
|
+
for (const d of drawableWires(pipeline)) {
|
|
325
|
+
if (isControl(pipeline, d.type)) continue;
|
|
326
|
+
const a = at.get(d.wire.from.stage);
|
|
327
|
+
if (a === void 0 || at.get(d.wire.to.stage) !== a + 1 || between2[a] !== void 0) continue;
|
|
328
|
+
between2[a] = d.index;
|
|
329
|
+
mainOut.set(d.wire.from.stage, d.wire.from.port);
|
|
330
|
+
mainIn.set(d.wire.to.stage, d.wire.to.port);
|
|
331
|
+
}
|
|
332
|
+
return { mainIn, mainOut, between: between2 };
|
|
181
333
|
}
|
|
182
|
-
|
|
183
|
-
|
|
334
|
+
var portsOf = (p, stage, side) => p.stages.find((s) => s.id === stage)?.[side] ?? [];
|
|
335
|
+
function movedChain(chain, i, j, to) {
|
|
336
|
+
const run = chain.slice(i, j + 1);
|
|
337
|
+
const out = [...chain.slice(0, i), ...chain.slice(j + 1)];
|
|
338
|
+
out.splice(to > j ? to - run.length : to, 0, ...run);
|
|
339
|
+
return out;
|
|
184
340
|
}
|
|
185
|
-
function
|
|
186
|
-
return
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
341
|
+
function moveInChain(doc, stage, to) {
|
|
342
|
+
return moveRunInChain(doc, stage, stage, to);
|
|
343
|
+
}
|
|
344
|
+
function moveRunInChain(doc, first, last, to) {
|
|
345
|
+
const p = doc.pipeline;
|
|
346
|
+
const chain = mainChain(p);
|
|
347
|
+
const i = chain.indexOf(first);
|
|
348
|
+
const j = chain.indexOf(last);
|
|
349
|
+
if (i < 0 || j < i || !Number.isInteger(to) || to < 0 || to > chain.length || to >= i && to <= j + 1) return null;
|
|
350
|
+
const { mainIn, mainOut, between: between2 } = chainLinks(p, chain);
|
|
351
|
+
const wIn = i > 0 ? between2[i - 1] : void 0;
|
|
352
|
+
const wOut = j < chain.length - 1 ? between2[j] : void 0;
|
|
353
|
+
const wAB = to > 0 && to < chain.length ? between2[to - 1] : void 0;
|
|
354
|
+
const gone = new Set([wIn, wOut, wAB].filter((k) => k !== void 0));
|
|
355
|
+
const kept = p.wires.filter((_, k) => !gone.has(k));
|
|
356
|
+
const free = (ref) => !kept.some((w) => w.to.stage === ref.stage && w.to.port === ref.port);
|
|
357
|
+
const pick = (id, side, type) => portsOf(p, id, side).find((port) => (type === void 0 || port.type === type) && (side === "outputs" || free({ stage: id, port: port.id })))?.id;
|
|
358
|
+
const typeOf = (id, port) => [...portsOf(p, id, "inputs"), ...portsOf(p, id, "outputs")].find((x) => x.id === port)?.type;
|
|
359
|
+
const made = [];
|
|
360
|
+
const prev = chain[i - 1];
|
|
361
|
+
const next = chain[j + 1];
|
|
362
|
+
if (prev !== void 0 && next !== void 0) made.push({ reuse: wIn, from: p.wires[wIn].from, to: p.wires[wOut].to });
|
|
363
|
+
const a = chain[to - 1];
|
|
364
|
+
const b = chain[to];
|
|
365
|
+
const sIn = mainIn.get(first) ?? pick(first, "inputs");
|
|
366
|
+
const sOut = mainOut.get(last) ?? pick(last, "outputs");
|
|
367
|
+
if (a !== void 0) {
|
|
368
|
+
const aOut = wAB !== void 0 ? p.wires[wAB].from.port : mainOut.get(a) ?? pick(a, "outputs", typeOf(first, sIn));
|
|
369
|
+
if (aOut === void 0 || sIn === void 0) return null;
|
|
370
|
+
made.push({ reuse: wAB, from: { stage: a, port: aOut }, to: { stage: first, port: sIn } });
|
|
371
|
+
}
|
|
372
|
+
if (b !== void 0) {
|
|
373
|
+
const bIn = wAB !== void 0 ? p.wires[wAB].to.port : mainIn.get(b) ?? pick(b, "inputs", typeOf(last, sOut));
|
|
374
|
+
if (bIn === void 0 || sOut === void 0) return null;
|
|
375
|
+
made.push({ reuse: wOut, from: { stage: last, port: sOut }, to: { stage: b, port: bIn } });
|
|
376
|
+
}
|
|
377
|
+
if (made.some((m) => !canWire(p, m.from, m.to) || !free(m.to))) return null;
|
|
378
|
+
if (new Set(made.map((m) => `${m.to.stage}\0${m.to.port}`)).size !== made.length) return null;
|
|
379
|
+
const wires = p.wires.flatMap((w, k) => {
|
|
380
|
+
if (!gone.has(k)) return [w];
|
|
381
|
+
const m = made.find((x) => x.reuse === k);
|
|
382
|
+
return m === void 0 ? [] : [{ ...w, from: m.from, to: m.to }];
|
|
383
|
+
});
|
|
384
|
+
for (const m of made) {
|
|
385
|
+
if (m.reuse !== void 0) continue;
|
|
386
|
+
wires.push({ id: nextWireId({ ...p, wires }), from: m.from, to: m.to });
|
|
387
|
+
}
|
|
388
|
+
const pipeline = { ...p, wires };
|
|
389
|
+
const want = movedChain(chain, i, j, to);
|
|
390
|
+
const got = mainChain(pipeline);
|
|
391
|
+
if (got.length !== want.length || got.some((id, k) => id !== want[k])) return null;
|
|
392
|
+
return pruneRoutes({ ...doc, pipeline }, [...gone].flatMap((k) => p.wires[k].id ?? []));
|
|
190
393
|
}
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
const wires = new Map(edges.map((e) => [edgeIdOf(e.edge), e.style]));
|
|
194
|
-
return {
|
|
195
|
-
nodeStyle: (n) => nodes.get(n.id) ?? NORMAL,
|
|
196
|
-
edgeStyle: (e) => wires.get(edgeIdOf(e)) ?? wireStyle(UNTYPED, false, false)
|
|
197
|
-
};
|
|
394
|
+
function canMoveInChain(doc, stage, to) {
|
|
395
|
+
return moveInChain(doc, stage, to) !== null;
|
|
198
396
|
}
|
|
199
397
|
|
|
200
398
|
// src/effect/symbolParts.ts
|
|
@@ -219,11 +417,11 @@ function alongLine(points, t) {
|
|
|
219
417
|
return { u: points[0][0], v: points[0][1] };
|
|
220
418
|
}
|
|
221
419
|
var placeAlong = (sides) => (ports, side) => ports.map((p, i) => ({ id: p.id, type: p.type, at: alongLine(sides[side], (i + 1) / (ports.length + 1)) }));
|
|
222
|
-
function inputs(stage, pipeline, options, steer,
|
|
420
|
+
function inputs(stage, pipeline, options, steer, place3 = spread) {
|
|
223
421
|
const all = stage.inputs ?? [];
|
|
224
422
|
const steering = all.filter((p) => isControl(pipeline, p.type) || p.id === steer && all.length > 1);
|
|
225
423
|
const side = options?.controlSide === "bottom" ? "bottom" : "top";
|
|
226
|
-
return [...
|
|
424
|
+
return [...place3(all.filter((p) => !steering.includes(p)), "left"), ...place3(steering, side)];
|
|
227
425
|
}
|
|
228
426
|
var title = (stage, options, gap = 4) => options?.controlSide === "bottom" ? { text: stage.title, at: { u: 0.5, v: 0 }, self: { u: 0.5, v: 1 }, offset: { x: 0, y: -gap } } : { text: stage.title, at: { u: 0.5, v: 1 }, self: { u: 0.5, v: 0 }, offset: { x: 0, y: gap } };
|
|
229
427
|
var DIAMOND_SIDES = placeAlong({
|
|
@@ -482,11 +680,11 @@ function sidePorts(stage) {
|
|
|
482
680
|
];
|
|
483
681
|
}
|
|
484
682
|
var untyped = (ports) => (ports ?? []).map(({ type: _type, ...port }) => port);
|
|
485
|
-
function nodeOf(stage, pipeline, branch) {
|
|
683
|
+
function nodeOf(stage, pipeline, branch, theme) {
|
|
486
684
|
if (kindOf(stage) !== "decision") {
|
|
487
685
|
return { ...boxNode(stage, { showParams: false, rowPorts: false, ports: sidePorts(stage) }), padding: COMPACT_PADDING };
|
|
488
686
|
}
|
|
489
|
-
const symbol = branchNode(stage, pipeline, branch);
|
|
687
|
+
const symbol = branchNode(stage, pipeline, branch, { style: stageStyle(stage, theme) });
|
|
490
688
|
return { id: stage.id, ...symbol, ports: untyped(symbol.ports), padding: symbol.padding ?? COMPACT_PADDING };
|
|
491
689
|
}
|
|
492
690
|
function lines(pipeline) {
|
|
@@ -499,9 +697,10 @@ function lines(pipeline) {
|
|
|
499
697
|
return [...by.values()];
|
|
500
698
|
}
|
|
501
699
|
var compact = (pipeline, options = {}) => {
|
|
502
|
-
const
|
|
700
|
+
const theme = options.theme ?? LOOK_THEME;
|
|
701
|
+
const color = typeColors(pipeline, options.colors, theme.series);
|
|
503
702
|
const stages = uniqueStages(pipeline);
|
|
504
|
-
const nodes = stages.map((stage) => nodeOf(stage, pipeline, options.branch ?? "hexagon"));
|
|
703
|
+
const nodes = stages.map((stage) => nodeOf(stage, pipeline, options.branch ?? "hexagon", theme));
|
|
505
704
|
const edges = lines(pipeline).map((line) => {
|
|
506
705
|
const first = line.find((d) => !isControl(pipeline, d.type)) ?? line[0];
|
|
507
706
|
return {
|
|
@@ -515,7 +714,7 @@ var compact = (pipeline, options = {}) => {
|
|
|
515
714
|
layout: "layered",
|
|
516
715
|
layoutOptions: { direction: "right", nodeGap: options.nodeGap ?? 16, rankGap: options.rankGap ?? 48 },
|
|
517
716
|
router: options.router ?? "bezier",
|
|
518
|
-
...sceneStyles(stages, edges)
|
|
717
|
+
...sceneStyles(stages, edges, theme)
|
|
519
718
|
},
|
|
520
719
|
color
|
|
521
720
|
};
|
|
@@ -523,9 +722,10 @@ var compact = (pipeline, options = {}) => {
|
|
|
523
722
|
|
|
524
723
|
// src/effect/nodeGraph.ts
|
|
525
724
|
var nodeGraph = (pipeline, options = {}) => {
|
|
526
|
-
const
|
|
725
|
+
const theme = options.theme ?? LOOK_THEME;
|
|
726
|
+
const color = typeColors(pipeline, options.colors, theme.series);
|
|
527
727
|
const stages = uniqueStages(pipeline);
|
|
528
|
-
const nodes = stages.map((stage) => kindOf(stage) === "decision" ? { id: stage.id, ...branchNode(stage, pipeline, options.branch) } : boxNode(stage, { showParams: options.showParams !== false, rowPorts: true, ports: [], run: options }));
|
|
728
|
+
const nodes = stages.map((stage) => kindOf(stage) === "decision" ? { id: stage.id, ...branchNode(stage, pipeline, options.branch, { style: stageStyle(stage, theme) }) } : boxNode(stage, { showParams: options.showParams !== false, rowPorts: true, ports: [], run: options }));
|
|
529
729
|
const edges = wireEdges(pipeline, color);
|
|
530
730
|
return {
|
|
531
731
|
data: { nodes, edges: edges.map((e) => e.edge), groups: uniqueGroups(pipeline) },
|
|
@@ -537,12 +737,26 @@ var nodeGraph = (pipeline, options = {}) => {
|
|
|
537
737
|
...options.rankGap !== void 0 ? { rankGap: options.rankGap } : {}
|
|
538
738
|
},
|
|
539
739
|
router: options.router ?? "bezier",
|
|
540
|
-
...sceneStyles(stages, edges)
|
|
740
|
+
...sceneStyles(stages, edges, theme)
|
|
541
741
|
},
|
|
542
742
|
color
|
|
543
743
|
};
|
|
544
744
|
};
|
|
745
|
+
function useReleaseReplaced(run) {
|
|
746
|
+
const shown = useRef(/* @__PURE__ */ new Set());
|
|
747
|
+
useEffect(() => {
|
|
748
|
+
const now = new Set(Object.values(run?.stages ?? {}).flatMap((s) => Object.values(s.previews ?? {})));
|
|
749
|
+
for (const old of shown.current) if (!now.has(old)) releaseImageSource(old);
|
|
750
|
+
shown.current = now;
|
|
751
|
+
}, [run]);
|
|
752
|
+
useEffect(() => () => {
|
|
753
|
+
for (const old of shown.current) releaseImageSource(old);
|
|
754
|
+
shown.current = /* @__PURE__ */ new Set();
|
|
755
|
+
}, []);
|
|
756
|
+
}
|
|
545
757
|
var DIM = 0.3;
|
|
758
|
+
var AT_COLOR = "#ffc94d";
|
|
759
|
+
var AT_WIDTH = 3;
|
|
546
760
|
var COST_GAP = 2;
|
|
547
761
|
var COST_SCALE = 0.8;
|
|
548
762
|
function paintRun(pipeline, specs, run, exits = /* @__PURE__ */ new Map()) {
|
|
@@ -552,7 +766,7 @@ function paintRun(pipeline, specs, run, exits = /* @__PURE__ */ new Map()) {
|
|
|
552
766
|
const owner = ownerOf(stages);
|
|
553
767
|
const passed = /* @__PURE__ */ new Set();
|
|
554
768
|
for (const spec of specs) {
|
|
555
|
-
const ends =
|
|
769
|
+
const ends = wireEnds2(spec);
|
|
556
770
|
if (ends === null) continue;
|
|
557
771
|
const exit = exits.get(spec.id) ?? { stage: ends.from, port: ends.fromPort };
|
|
558
772
|
const taken = run.stages[exit.stage]?.taken;
|
|
@@ -563,7 +777,7 @@ function paintRun(pipeline, specs, run, exits = /* @__PURE__ */ new Map()) {
|
|
|
563
777
|
const values = /* @__PURE__ */ new Map();
|
|
564
778
|
const previews = /* @__PURE__ */ new Map();
|
|
565
779
|
for (const [id, stage] of stages) {
|
|
566
|
-
for (const p of stage
|
|
780
|
+
for (const p of shownPorts(stage)) {
|
|
567
781
|
values.set(`${id}/${valueRowId(p.id)}`, `${p.label ?? p.id}: ${run.stages[id]?.values?.[p.id] ?? NO_VALUE}`);
|
|
568
782
|
const preview = run.stages[id]?.previews?.[p.id];
|
|
569
783
|
if (preview !== void 0) previews.set(`${id}/${previewRowId(p.id)}`, preview);
|
|
@@ -585,12 +799,16 @@ function paintRun(pipeline, specs, run, exits = /* @__PURE__ */ new Map()) {
|
|
|
585
799
|
const dependsOn = spec.dependsOn;
|
|
586
800
|
const onWire = Array.isArray(dependsOn) && dependsOn.length === 1 && passed.has(dependsOn[0]);
|
|
587
801
|
if (stage !== void 0 && unreached.has(stage) || passed.has(id) || onWire) next = { ...next, data: dimmed(next.data) };
|
|
802
|
+
if (id === run.at && next.data.stroke !== void 0) {
|
|
803
|
+
next = { ...next, data: { ...next.data, stroke: { paint: { color: AT_COLOR }, width: AT_WIDTH } } };
|
|
804
|
+
}
|
|
588
805
|
return next;
|
|
589
806
|
});
|
|
590
807
|
for (const spec of specs) {
|
|
591
808
|
const id = spec.id;
|
|
592
|
-
const
|
|
593
|
-
|
|
809
|
+
const entry = stages.has(id) ? run.stages[id] : void 0;
|
|
810
|
+
const note = entry?.error ?? (entry?.cost !== void 0 ? costText(entry.cost) : void 0);
|
|
811
|
+
if (note !== void 0) out.push(costSpec(spec, specs.filter((s) => owner(s) === id), stages.get(id), note, entry?.error !== void 0));
|
|
594
812
|
}
|
|
595
813
|
return out;
|
|
596
814
|
}
|
|
@@ -609,7 +827,7 @@ function ownerOf(stages) {
|
|
|
609
827
|
return anchor !== void 0 && stages.has(anchor) ? anchor : void 0;
|
|
610
828
|
};
|
|
611
829
|
}
|
|
612
|
-
function
|
|
830
|
+
function wireEnds2(spec) {
|
|
613
831
|
const trait = spec.data?.diagram;
|
|
614
832
|
const deps = spec.dependsOn;
|
|
615
833
|
if (trait?.from === void 0 || trait.to === void 0 || !Array.isArray(deps) || deps.length !== 2) return null;
|
|
@@ -633,13 +851,12 @@ function dimmed(data) {
|
|
|
633
851
|
...d.stroke !== void 0 ? { stroke: fade(d.stroke) } : {}
|
|
634
852
|
};
|
|
635
853
|
}
|
|
636
|
-
function costSpec(stage, own, of,
|
|
854
|
+
function costSpec(stage, own, of, text, problem) {
|
|
637
855
|
const title2 = own.find((s) => s !== stage && s.data.text !== void 0);
|
|
638
856
|
const titleData = title2?.data ?? {};
|
|
639
857
|
const fontSize = Math.round((titleData.style?.fontSize ?? 12) * COST_SCALE);
|
|
640
858
|
const style = { fontFamily: titleData.style?.fontFamily ?? "sans-serif", fontSize };
|
|
641
|
-
const fill = titleData.fill ?? { paint: { color: stageStyle(of).text } };
|
|
642
|
-
const text = costText(cost);
|
|
859
|
+
const fill = problem ? { paint: { color: PROBLEM_COLOR } } : titleData.fill ?? { paint: { color: stageStyle(of).text } };
|
|
643
860
|
const width = text.length * fontSize * 0.6;
|
|
644
861
|
const height = Math.ceil(fontSize * 1.2);
|
|
645
862
|
const box = stage.pose;
|
|
@@ -660,6 +877,19 @@ function costText(ms) {
|
|
|
660
877
|
if (ms >= 100) return `${Math.round(ms)} ms`;
|
|
661
878
|
return `${Number(ms.toPrecision(3))} ms`;
|
|
662
879
|
}
|
|
880
|
+
function useRunPortOptions(color, pipeline, run) {
|
|
881
|
+
const missed = JSON.stringify([...unreachedStages(pipeline, run)].sort());
|
|
882
|
+
return useMemo(() => {
|
|
883
|
+
const faded = new Set(JSON.parse(missed));
|
|
884
|
+
return {
|
|
885
|
+
shape: "circle",
|
|
886
|
+
sizePx: 8,
|
|
887
|
+
// Every port a look declares has a value type; the compass defaults have none.
|
|
888
|
+
showsPort: (port) => port.type !== void 0,
|
|
889
|
+
fill: (port) => ({ paint: { color: color(port.type), ...faded.has(port.nodeId) ? { opacity: DIM } : {} } })
|
|
890
|
+
};
|
|
891
|
+
}, [color, missed]);
|
|
892
|
+
}
|
|
663
893
|
var EFFECT_MIN_SCALE = 0.5;
|
|
664
894
|
var keys = /* @__PURE__ */ new WeakMap();
|
|
665
895
|
var nextKey = 0;
|
|
@@ -686,18 +916,6 @@ function useSameContent(value) {
|
|
|
686
916
|
setKept({ value, content });
|
|
687
917
|
return value;
|
|
688
918
|
}
|
|
689
|
-
function useReleaseReplaced(run) {
|
|
690
|
-
const shown = useRef(/* @__PURE__ */ new Set());
|
|
691
|
-
useEffect(() => {
|
|
692
|
-
const now = new Set(Object.values(run?.stages ?? {}).flatMap((s) => Object.values(s.previews ?? {})));
|
|
693
|
-
for (const old of shown.current) if (!now.has(old)) releaseImageSource(old);
|
|
694
|
-
shown.current = now;
|
|
695
|
-
}, [run]);
|
|
696
|
-
useEffect(() => () => {
|
|
697
|
-
for (const old of shown.current) releaseImageSource(old);
|
|
698
|
-
shown.current = /* @__PURE__ */ new Set();
|
|
699
|
-
}, []);
|
|
700
|
-
}
|
|
701
919
|
function EffectChart({
|
|
702
920
|
pipeline: given,
|
|
703
921
|
run,
|
|
@@ -724,17 +942,7 @@ function EffectChart({
|
|
|
724
942
|
const specs = diagramScene(drawn.data, drawn.scene).map((s) => s.pickable === false || stageIds.has(s.id) ? s : { ...s, pickable: false });
|
|
725
943
|
return { specs, color: drawn.color, exits: drawn.exits };
|
|
726
944
|
}, [pipeline, look, options, stageIds]);
|
|
727
|
-
const
|
|
728
|
-
const portOptions = useMemo(() => {
|
|
729
|
-
const faded = new Set(JSON.parse(missed));
|
|
730
|
-
return {
|
|
731
|
-
shape: "circle",
|
|
732
|
-
sizePx: 8,
|
|
733
|
-
// Every port a look declares has a value type; the compass defaults have none.
|
|
734
|
-
showsPort: (port) => port.type !== void 0,
|
|
735
|
-
fill: (port) => ({ paint: { color: built.color(port.type), ...faded.has(port.nodeId) ? { opacity: DIM } : {} } })
|
|
736
|
-
};
|
|
737
|
-
}, [built, missed]);
|
|
945
|
+
const portOptions = useRunPortOptions(built.color, pipeline, run);
|
|
738
946
|
const painted = useMemo(() => paintRun(pipeline, built.specs, run, built.exits), [pipeline, built, run]);
|
|
739
947
|
useReleaseReplaced(run);
|
|
740
948
|
return /* @__PURE__ */ jsx(
|
|
@@ -755,17 +963,331 @@ function EffectChart({
|
|
|
755
963
|
keyOf(built)
|
|
756
964
|
);
|
|
757
965
|
}
|
|
966
|
+
|
|
967
|
+
// src/effect/groupHandlers.ts
|
|
968
|
+
var parentGroup = (doc, id) => doc.pipeline.groups?.find((g) => g.members.includes(id))?.id;
|
|
969
|
+
function dragEdit(doc, built, reports, boxOf) {
|
|
970
|
+
let next = doc;
|
|
971
|
+
let resized = false;
|
|
972
|
+
let touched = false;
|
|
973
|
+
const carried = /* @__PURE__ */ new Set();
|
|
974
|
+
const reparent = (id, parent) => {
|
|
975
|
+
if (parent === void 0) return;
|
|
976
|
+
if (parent === null || built.groupOf(parent) !== void 0) next = moveToGroup(next, [id], parent);
|
|
977
|
+
};
|
|
978
|
+
const depth = (id) => {
|
|
979
|
+
let n = 0;
|
|
980
|
+
for (let at = parentGroup(doc, id); at !== void 0; at = parentGroup(doc, at)) n++;
|
|
981
|
+
return n;
|
|
982
|
+
};
|
|
983
|
+
for (const edit of [...reports.boxes].sort((a, b) => depth(a.id) - depth(b.id))) {
|
|
984
|
+
const group = built.groupOf(edit.id);
|
|
985
|
+
const was = boxOf(edit.id);
|
|
986
|
+
if (group === void 0 || was === void 0 || carried.has(group)) continue;
|
|
987
|
+
touched = true;
|
|
988
|
+
reparent(group, edit.parent);
|
|
989
|
+
if (edit.box.width === was.width && edit.box.height === was.height) {
|
|
990
|
+
next = moveGroup(next, group, { x: edit.box.x - was.x, y: edit.box.y - was.y });
|
|
991
|
+
const inside = groupContents(doc, group);
|
|
992
|
+
for (const id of [...inside.stages, ...inside.groups]) carried.add(id);
|
|
993
|
+
} else {
|
|
994
|
+
next = setGroupBox(next, group, edit.box);
|
|
995
|
+
resized = true;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
const moves = reports.moves.filter((m) => built.stageOf(m.id) !== void 0 && !carried.has(m.id));
|
|
999
|
+
for (const m of moves) reparent(m.id, m.parent);
|
|
1000
|
+
if (moves.length > 0) {
|
|
1001
|
+
next = moveStages(next, moves.map(({ id, x, y }) => ({ id, x, y })));
|
|
1002
|
+
touched = true;
|
|
1003
|
+
}
|
|
1004
|
+
return touched ? { doc: next, label: resized && moves.length === 0 ? "Resize group" : "Move" } : null;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// src/effect/editorHandlers.ts
|
|
1008
|
+
function orient(p, a, b) {
|
|
1009
|
+
if (canWire(p, a, b)) return { from: a, to: b };
|
|
1010
|
+
if (canWire(p, b, a)) return { from: b, to: a };
|
|
1011
|
+
return null;
|
|
1012
|
+
}
|
|
1013
|
+
var fed = (p, to, except) => p.wires.some((w) => w.to.stage === to.stage && w.to.port === to.port && (except === void 0 || w.id !== except));
|
|
1014
|
+
function wireFor(p, a, b, except) {
|
|
1015
|
+
const wire = orient(p, a, b);
|
|
1016
|
+
return wire !== null && !fed(p, wire.to, except) ? wire : null;
|
|
1017
|
+
}
|
|
1018
|
+
function pinShown(doc, specs) {
|
|
1019
|
+
const shown = Object.fromEntries([...stageBoxes(doc, specs)].map(([id, b]) => [id, { x: b.x, y: b.y }]));
|
|
1020
|
+
return { ...doc, places: { ...shown, ...doc.places } };
|
|
1021
|
+
}
|
|
1022
|
+
function stageBoxes(doc, specs) {
|
|
1023
|
+
const ids = new Set(doc.pipeline.stages.map((s) => s.id));
|
|
1024
|
+
return new Map(specs.filter((s) => ids.has(s.id)).map((s) => [s.id, s.pose]));
|
|
1025
|
+
}
|
|
1026
|
+
function editHandlers(now, unpick, rename = () => {
|
|
1027
|
+
}) {
|
|
1028
|
+
const commit = (label, edit) => {
|
|
1029
|
+
const { doc, built, onChange } = now();
|
|
1030
|
+
const d = ensureWireIds(doc);
|
|
1031
|
+
const next = edit(d, built);
|
|
1032
|
+
if (next !== null) onChange(next, label);
|
|
1033
|
+
};
|
|
1034
|
+
const drag = { moves: [], boxes: [] };
|
|
1035
|
+
let held = false;
|
|
1036
|
+
const hold = () => {
|
|
1037
|
+
if (held) return;
|
|
1038
|
+
held = true;
|
|
1039
|
+
queueMicrotask(() => {
|
|
1040
|
+
held = false;
|
|
1041
|
+
const reports = { moves: drag.moves.splice(0), boxes: drag.boxes.splice(0) };
|
|
1042
|
+
const { doc, built, specs, onChange } = now();
|
|
1043
|
+
const boxes = new Map(specs.map((s) => [s.id, s.pose]));
|
|
1044
|
+
const out = dragEdit(pinShown(ensureWireIds(doc), specs), built, reports, (id) => boxes.get(id));
|
|
1045
|
+
if (out !== null) onChange(out.doc, out.label);
|
|
1046
|
+
});
|
|
1047
|
+
};
|
|
1048
|
+
const canConnect = (from, to, moving) => {
|
|
1049
|
+
if (isBodyEnd(from) || isBodyEnd(to)) return false;
|
|
1050
|
+
const { doc, built } = now();
|
|
1051
|
+
const except = moving === void 0 ? void 0 : built.wireOf(moving.edge);
|
|
1052
|
+
return wireFor(doc.pipeline, { stage: from.nodeId, port: from.id }, { stage: to.nodeId, port: to.id }, except) !== null;
|
|
1053
|
+
};
|
|
1054
|
+
return {
|
|
1055
|
+
canConnect,
|
|
1056
|
+
onConnect: (e) => commit("Connect", (d) => {
|
|
1057
|
+
if (e.fromPort === void 0 || e.toPort === void 0) return null;
|
|
1058
|
+
const wire = wireFor(d.pipeline, { stage: e.from, port: e.fromPort }, { stage: e.to, port: e.toPort });
|
|
1059
|
+
return wire === null ? null : addWire(d, wire);
|
|
1060
|
+
}),
|
|
1061
|
+
onReconnect: (c) => commit("Reconnect", (d, b) => {
|
|
1062
|
+
const id = b.wireOf(c.edge);
|
|
1063
|
+
const wire = d.pipeline.wires.find((w) => w.id === id);
|
|
1064
|
+
if (id === void 0 || wire === void 0 || c.port === void 0) return null;
|
|
1065
|
+
const moved = { stage: c.node, port: c.port };
|
|
1066
|
+
const [from, to] = c.end === "from" ? [moved, wire.to] : [wire.from, moved];
|
|
1067
|
+
return canWire(d.pipeline, from, to) && !fed(d.pipeline, to, id) ? reconnectWire(d, id, c.end, moved) : null;
|
|
1068
|
+
}),
|
|
1069
|
+
onEditRoute: (c) => commit("Edit route", (d, b) => {
|
|
1070
|
+
const id = b.wireOf(c.edge);
|
|
1071
|
+
const { look, specs } = now();
|
|
1072
|
+
if (id === void 0) return null;
|
|
1073
|
+
if (look === "nodeGraph") return setWireRoute(d, id, c.waypoints);
|
|
1074
|
+
if (c.waypoints.length === 0) {
|
|
1075
|
+
const was = d.lookRoutes?.[look]?.[id];
|
|
1076
|
+
return was === void 0 ? null : setLookRoute(d, look, id, [], was.ends);
|
|
1077
|
+
}
|
|
1078
|
+
const ends = wireEnds(specs, c.edge);
|
|
1079
|
+
return ends === void 0 ? null : setLookRoute(d, look, id, c.waypoints, ends);
|
|
1080
|
+
}),
|
|
1081
|
+
onDelete: (r) => commit("Delete", (d, b) => {
|
|
1082
|
+
unpick();
|
|
1083
|
+
const stages = r.nodes.flatMap((n) => b.stageOf(n) ?? []);
|
|
1084
|
+
const wires = r.edges.flatMap((e) => b.wireOf(e) ?? []);
|
|
1085
|
+
return stages.length === 0 && wires.length === 0 ? null : removeWires(removeStages(d, stages), wires);
|
|
1086
|
+
}),
|
|
1087
|
+
onMove: (moves) => {
|
|
1088
|
+
drag.moves.push(...moves);
|
|
1089
|
+
hold();
|
|
1090
|
+
},
|
|
1091
|
+
onEditContainer: (edit) => {
|
|
1092
|
+
if (edit.kind === "title") {
|
|
1093
|
+
const group = now().built.groupOf(edit.id);
|
|
1094
|
+
if (group !== void 0) rename(group);
|
|
1095
|
+
} else if (edit.kind === "box") {
|
|
1096
|
+
drag.boxes.push(edit);
|
|
1097
|
+
hold();
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
var isPart = (built, id) => built.stageOf(id) !== void 0 || built.groupOf(id) !== void 0;
|
|
1103
|
+
function useEditorPicks(built, given) {
|
|
1104
|
+
const [stage, setStage] = useState(null);
|
|
1105
|
+
const [group, setGroup] = useState(null);
|
|
1106
|
+
const [edge, setEdge] = useState(null);
|
|
1107
|
+
const latest = useLatest$1({ built, given });
|
|
1108
|
+
const pick = useCallback((id) => {
|
|
1109
|
+
const { built: b, given: g } = latest.current;
|
|
1110
|
+
const s = id === null ? void 0 : b.stageOf(id);
|
|
1111
|
+
const k = id === null ? void 0 : b.groupOf(id);
|
|
1112
|
+
setStage(s ?? null);
|
|
1113
|
+
setGroup(k ?? null);
|
|
1114
|
+
setEdge(id !== null && s === void 0 && k === void 0 && b.wireOf(id) !== void 0 ? id : null);
|
|
1115
|
+
g.onSelect?.(s ?? null);
|
|
1116
|
+
g.onSelectGroup?.(k ?? null);
|
|
1117
|
+
}, [latest]);
|
|
1118
|
+
const unpick = useCallback(() => setEdge(null), []);
|
|
1119
|
+
const shownStage = given.selected !== void 0 ? given.selected : stage;
|
|
1120
|
+
const shownGroup = given.selectedGroup !== void 0 ? given.selectedGroup : group;
|
|
1121
|
+
const wire = edge === null || built.wireOf(edge) === void 0 ? null : edge;
|
|
1122
|
+
const controlled = given.selected !== void 0 || given.selectedGroup !== void 0;
|
|
1123
|
+
const live = (id) => id !== null && isPart(built, id) ? id : null;
|
|
1124
|
+
const shown = controlled ? live(shownStage) ?? live(shownGroup) ?? wire : void 0;
|
|
1125
|
+
return { pick, unpick, shown };
|
|
1126
|
+
}
|
|
1127
|
+
var FIELD_RULE = ":where([data-wzl-group-title]) { width: 16ch; font: inherit; font-weight: 700; text-align: center; }";
|
|
1128
|
+
var TOP = { side: "top", align: "center" };
|
|
1129
|
+
var adoptFieldRule = (el) => {
|
|
1130
|
+
if (el === null) return;
|
|
1131
|
+
adoptCssOnce(el, FIELD_RULE);
|
|
1132
|
+
el.focus();
|
|
1133
|
+
el.select();
|
|
1134
|
+
};
|
|
1135
|
+
function useGroupTitle(doc, look, renaming, setRenaming, onChange) {
|
|
1136
|
+
const latest = useLatest$1({ doc, onChange });
|
|
1137
|
+
const group = renaming === null ? void 0 : doc.pipeline.groups?.find((g) => g.id === renaming);
|
|
1138
|
+
const id = group?.id;
|
|
1139
|
+
const was = group?.label ?? "";
|
|
1140
|
+
const over = id === void 0 ? void 0 : look === "nodeGraph" ? titleIdOf(id) : was ? groupLabelIdOf(id) : id;
|
|
1141
|
+
return useMemo(() => {
|
|
1142
|
+
if (id === void 0 || over === void 0) return null;
|
|
1143
|
+
const done = (text) => {
|
|
1144
|
+
setRenaming(null);
|
|
1145
|
+
if (text !== null && text !== was) latest.current.onChange(renameGroup(latest.current.doc, id, text), "Rename group");
|
|
1146
|
+
};
|
|
1147
|
+
const placement = over === id ? TOP : "center";
|
|
1148
|
+
return [{ target: over, placement, content: /* @__PURE__ */ jsx(GroupTitleField, { initial: was, onDone: done }, id) }];
|
|
1149
|
+
}, [id, was, over, setRenaming, latest]);
|
|
1150
|
+
}
|
|
1151
|
+
function GroupTitleField({ initial, onDone }) {
|
|
1152
|
+
const [text, setText] = useState(initial);
|
|
1153
|
+
const [done, setDone] = useState(false);
|
|
1154
|
+
const finish = (value) => {
|
|
1155
|
+
if (done) return;
|
|
1156
|
+
setDone(true);
|
|
1157
|
+
onDone(value);
|
|
1158
|
+
};
|
|
1159
|
+
return /* @__PURE__ */ jsx(
|
|
1160
|
+
"input",
|
|
1161
|
+
{
|
|
1162
|
+
ref: adoptFieldRule,
|
|
1163
|
+
"data-wzl-group-title": "",
|
|
1164
|
+
"aria-label": "Group name",
|
|
1165
|
+
value: text,
|
|
1166
|
+
onChange: (e) => setText(e.target.value),
|
|
1167
|
+
onKeyDown: (e) => {
|
|
1168
|
+
e.stopPropagation();
|
|
1169
|
+
if (e.key === "Enter") finish(text.trim());
|
|
1170
|
+
else if (e.key === "Escape") finish(null);
|
|
1171
|
+
},
|
|
1172
|
+
onBlur: () => finish(text.trim())
|
|
1173
|
+
}
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// src/effect/laneColumns.ts
|
|
1178
|
+
function laneColumns(stages, wires, spans, groups, titles = /* @__PURE__ */ new Map()) {
|
|
1179
|
+
const order = new Map(stages.map((s, i) => [s.id, i]));
|
|
1180
|
+
const nested = nestGroups(groups, (id) => order.has(id));
|
|
1181
|
+
const byId = new Map(nested.map((g) => [g.id, g]));
|
|
1182
|
+
const grouped = new Set(nested.flatMap((g) => g.members));
|
|
1183
|
+
const first = (id) => Math.min(...(byId.get(id)?.members ?? [id]).map((m) => order.get(m)));
|
|
1184
|
+
const itemsOf = (ids, depth) => [...ids].sort((a, b) => first(a) - first(b)).map((id) => {
|
|
1185
|
+
const g = byId.get(id);
|
|
1186
|
+
if (g === void 0) return { stages: [id] };
|
|
1187
|
+
const inside = place(itemsOf(g.direct, depth + 1), wires, spans);
|
|
1188
|
+
const minWidth = titles.has(g.id) ? groupMinWidth(titles.get(g.id), DEFAULT_GROUP_INSET) : 0;
|
|
1189
|
+
const own = { group: g.id, first: 0, last: inside.width - 1, depth, minWidth };
|
|
1190
|
+
return { stages: g.members, inner: { ...inside, blocks: [own, ...inside.blocks] } };
|
|
1191
|
+
});
|
|
1192
|
+
const top = [...stages.map((s) => s.id).filter((id) => !grouped.has(id)), ...nested.filter((g) => g.parent === void 0).map((g) => g.id)];
|
|
1193
|
+
const { column, blocks } = place(itemsOf(top, 0), wires, spans);
|
|
1194
|
+
return { column, blocks };
|
|
1195
|
+
}
|
|
1196
|
+
function place(items, wires, spans) {
|
|
1197
|
+
const owner = /* @__PURE__ */ new Map();
|
|
1198
|
+
items.forEach((item, k) => item.stages.forEach((id) => owner.set(id, k)));
|
|
1199
|
+
const feeds = items.map(() => /* @__PURE__ */ new Set());
|
|
1200
|
+
for (const { wire: { from, to } } of wires) {
|
|
1201
|
+
const [a, b] = [owner.get(from.stage), owner.get(to.stage)];
|
|
1202
|
+
if (a !== void 0 && b !== void 0 && a !== b) feeds[b].add(from.stage);
|
|
1203
|
+
}
|
|
1204
|
+
const column = /* @__PURE__ */ new Map();
|
|
1205
|
+
const blocks = [];
|
|
1206
|
+
const held = [];
|
|
1207
|
+
const whole = /* @__PURE__ */ new Set();
|
|
1208
|
+
const fits = (item, c) => {
|
|
1209
|
+
if (item.inner === void 0) {
|
|
1210
|
+
const span = spans.get(item.stages[0]);
|
|
1211
|
+
return !whole.has(c) && !held[c]?.some(([lo, hi]) => lo <= span[1] && span[0] <= hi);
|
|
1212
|
+
}
|
|
1213
|
+
for (let j = c; j < c + item.inner.width; j++) if (whole.has(j) || (held[j]?.length ?? 0) > 0) return false;
|
|
1214
|
+
return true;
|
|
1215
|
+
};
|
|
1216
|
+
const left = items.map((_, k) => k);
|
|
1217
|
+
while (left.length > 0) {
|
|
1218
|
+
const i = Math.max(0, left.findIndex((k2) => [...feeds[k2]].every((f) => column.has(f))));
|
|
1219
|
+
const k = left.splice(i, 1)[0];
|
|
1220
|
+
const item = items[k];
|
|
1221
|
+
let c = Math.max(0, ...[...feeds[k]].flatMap((f) => column.has(f) ? [column.get(f) + 1] : []));
|
|
1222
|
+
while (!fits(item, c)) c++;
|
|
1223
|
+
if (item.inner === void 0) {
|
|
1224
|
+
(held[c] ??= []).push(spans.get(item.stages[0]));
|
|
1225
|
+
column.set(item.stages[0], c);
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
for (let j = c; j < c + item.inner.width; j++) whole.add(j);
|
|
1229
|
+
for (const [id, at] of item.inner.column) column.set(id, c + at);
|
|
1230
|
+
for (const b of item.inner.blocks) blocks.push({ ...b, first: b.first + c, last: b.last + c });
|
|
1231
|
+
}
|
|
1232
|
+
return { width: Math.max(0, ...column.values()) + (column.size > 0 ? 1 : 0), column, blocks };
|
|
1233
|
+
}
|
|
1234
|
+
function columnLefts(widths, column, blocks, gap, start, extent) {
|
|
1235
|
+
const count = widths.length;
|
|
1236
|
+
const room = blockRoom(widths.length, column, blocks, gap);
|
|
1237
|
+
const leftsNow = (now) => {
|
|
1238
|
+
let x = start;
|
|
1239
|
+
return Array.from(widths, (wd, c) => {
|
|
1240
|
+
x += now[c];
|
|
1241
|
+
const at = x;
|
|
1242
|
+
x += wd ?? 0;
|
|
1243
|
+
return at;
|
|
1244
|
+
});
|
|
1245
|
+
};
|
|
1246
|
+
const byId = new Map(blocks.map((b) => [b.group, b]));
|
|
1247
|
+
const span = (group, now) => {
|
|
1248
|
+
const b = byId.get(group);
|
|
1249
|
+
const lefts2 = leftsNow(now);
|
|
1250
|
+
let [lo, hi] = [Infinity, -Infinity];
|
|
1251
|
+
for (const [id, c] of column) {
|
|
1252
|
+
if (c >= b.first && c <= b.last) [lo, hi] = [Math.min(lo, lefts2[c] + extent(id).lo), Math.max(hi, lefts2[c] + extent(id).hi)];
|
|
1253
|
+
}
|
|
1254
|
+
return { lo, hi };
|
|
1255
|
+
};
|
|
1256
|
+
const inset = { lo: DEFAULT_GROUP_INSET, hi: DEFAULT_GROUP_INSET };
|
|
1257
|
+
widenForMinWidth([...blocks].sort((p, q) => q.depth - p.depth).map((b) => ({
|
|
1258
|
+
id: b.group,
|
|
1259
|
+
minWidth: b.minWidth,
|
|
1260
|
+
inset,
|
|
1261
|
+
children: blocks.filter((k) => k.depth === b.depth + 1 && k.first >= b.first && k.last <= b.last).map((k) => k.group),
|
|
1262
|
+
before: b.first,
|
|
1263
|
+
after: b.last + 1
|
|
1264
|
+
})), room, span);
|
|
1265
|
+
const lefts = leftsNow(room);
|
|
1266
|
+
return { lefts, end: count === 0 ? start : lefts[count - 1] + (widths[count - 1] ?? 0) + room[count] };
|
|
1267
|
+
}
|
|
1268
|
+
function blockRoom(count, column, blocks, gap) {
|
|
1269
|
+
if (blocks.length === 0) return Array.from({ length: count + 1 }, (_, c) => c === 0 || c === count ? 0 : gap);
|
|
1270
|
+
const rows = Array.from({ length: count + 2 }, () => []);
|
|
1271
|
+
for (const [id, c] of column) rows[c + 1].push({ id });
|
|
1272
|
+
const inside = (b) => [...column].filter(([, c]) => c >= b.first && c <= b.last).map(([id]) => id);
|
|
1273
|
+
const groups = blocks.map((b) => {
|
|
1274
|
+
const parent = blocks.find((p) => p.depth === b.depth - 1 && p.first <= b.first && b.last <= p.last);
|
|
1275
|
+
return { id: b.group, members: inside(b), inset: insetOf(DEFAULT_GROUP_INSET), ...parent !== void 0 ? { parent: parent.group } : {} };
|
|
1276
|
+
});
|
|
1277
|
+
const gaps = rankGaps({ groups }, rows, { rank: "x", cross: "y", sign: 1 }, gap);
|
|
1278
|
+
return gaps.map((g, c) => c === 0 || c === count ? g - gap : g);
|
|
1279
|
+
}
|
|
758
1280
|
var upright = (n) => n !== null && Math.abs(n.y) > Math.abs(n.x);
|
|
759
1281
|
var facingY = (end, y) => [end, ...end.sites ?? []].find((s) => s.normal !== null && Math.sign(s.normal.y) === Math.sign(y - s.point.y) && upright(s.normal)) ?? end;
|
|
760
1282
|
var TRACK_GAP = SIDE_TRACK_GAP;
|
|
761
1283
|
var CLEAR = 3;
|
|
762
1284
|
var covers = (boxes, y, to = y) => boxes.some((b) => b.y - CLEAR < Math.max(y, to) && Math.min(y, to) < b.y + b.height + CLEAR);
|
|
763
1285
|
var laneAt = (grid, y) => Math.max(0, grid.edges.filter((e) => e <= y).length - 1);
|
|
764
|
-
function corridor(grid, columns,
|
|
1286
|
+
function corridor(grid, columns, near2) {
|
|
765
1287
|
const ink = columns.flatMap((c) => grid.ink[c] ?? []);
|
|
766
|
-
const middle = laneAt(grid,
|
|
767
|
-
const
|
|
768
|
-
return
|
|
1288
|
+
const middle = laneAt(grid, near2) + 0.5;
|
|
1289
|
+
const clear2 = grid.edges.flatMap((y, k) => covers(ink, y) ? [] : [k]);
|
|
1290
|
+
return clear2.reduce((best, k) => Math.abs(k - middle) < Math.abs(best - middle) ? k : best, clear2[0] ?? 0);
|
|
769
1291
|
}
|
|
770
1292
|
var between = (a, b) => Array.from({ length: Math.abs(b - a) + 1 }, (_, i) => a + Math.sign(b - a) * i);
|
|
771
1293
|
function laneCourse(grid, from, to) {
|
|
@@ -859,18 +1381,30 @@ var overlaps = (a, b) => a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y
|
|
|
859
1381
|
function clearLabel(path, size, offset, keepOut) {
|
|
860
1382
|
for (const side of [offset, -offset]) {
|
|
861
1383
|
for (const t of [0.5, 0.4, 0.6, 0.3, 0.7, 0.2, 0.8, 0.1, 0.9]) {
|
|
862
|
-
const
|
|
863
|
-
const center =
|
|
864
|
-
if (
|
|
1384
|
+
const near2 = pointAlongPath(path, t)?.point;
|
|
1385
|
+
const center = near2 === void 0 ? null : labelCenter(path, { at: { near: near2 }, offset: side }, size);
|
|
1386
|
+
if (near2 === void 0 || center === null) continue;
|
|
865
1387
|
const box = { x: center.x - size.width / 2, y: center.y - size.height / 2, width: size.width, height: size.height };
|
|
866
|
-
if (!keepOut.some((k) => overlaps(box, k))) return { near, offset: side, box };
|
|
1388
|
+
if (!keepOut.some((k) => overlaps(box, k))) return { near: near2, offset: side, box };
|
|
867
1389
|
}
|
|
868
1390
|
}
|
|
869
1391
|
return void 0;
|
|
870
1392
|
}
|
|
871
1393
|
var inkOf = (id, specs) => specs.filter((s) => s.id === id || s.parent === id || String(s.id).startsWith(`${id}/mark:`)).map((s) => s.pose);
|
|
1394
|
+
function walledIn(grid, walls) {
|
|
1395
|
+
if (walls.length === 0) return grid;
|
|
1396
|
+
const over = (c) => walls.filter((w) => w.first <= c && c <= w.last);
|
|
1397
|
+
return {
|
|
1398
|
+
edges: grid.edges,
|
|
1399
|
+
ink: grid.ink.map((boxes, c) => boxes.concat(over(c).map((w) => w.box))),
|
|
1400
|
+
columns: grid.columns.map(([lo, hi], c) => over(c).reduce(([l, h], { box, first, last }) => [
|
|
1401
|
+
c === first ? Math.min(l, box.x - CLEAR) : l,
|
|
1402
|
+
c === last ? Math.max(h, box.x + box.width + CLEAR) : h
|
|
1403
|
+
], [lo, hi]))
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
872
1406
|
function routeWires(wires, nodes, laid) {
|
|
873
|
-
const specs = diagramScene({ nodes, edges: wires.map((w) => w.edge) }, {
|
|
1407
|
+
const specs = diagramScene({ nodes, edges: wires.map((w) => w.edge), ...laid.groups !== void 0 ? { groups: laid.groups } : {} }, {
|
|
874
1408
|
layout: "none",
|
|
875
1409
|
router: laid.router,
|
|
876
1410
|
...laid.measure !== void 0 ? { measure: laid.measure } : {}
|
|
@@ -883,7 +1417,13 @@ function routeWires(wires, nodes, laid) {
|
|
|
883
1417
|
const [site, ...sites] = portSitesOf({ id: stage, data: s.data }, s.pose, port);
|
|
884
1418
|
return { column: laid.column.get(stage), point: site?.point ?? { x: 0, y: 0 }, normal: site?.normal ?? null, sites };
|
|
885
1419
|
};
|
|
886
|
-
const
|
|
1420
|
+
const walls = nestGroups(laid.groups ?? [], (id) => ink.has(id)).flatMap((g) => {
|
|
1421
|
+
const box = spec.get(g.id)?.pose;
|
|
1422
|
+
const at = g.members.flatMap((m) => laid.column.has(m) ? [laid.column.get(m)] : []);
|
|
1423
|
+
return box === void 0 || at.length === 0 ? [] : [{ box, first: Math.min(...at), last: Math.max(...at), holds: new Set(g.members) }];
|
|
1424
|
+
});
|
|
1425
|
+
const grids = wires.map(({ edge }) => walledIn(grid, walls.filter((w) => !w.holds.has(edge.from) && !w.holds.has(edge.to))));
|
|
1426
|
+
const courses = wires.map(({ edge }, w) => edge.from === edge.to ? void 0 : laneCourse(grids[w], end(edge.from, edge.fromPort), end(edge.to, edge.toPort)));
|
|
887
1427
|
const { stretches, tracks } = assignTracks(courses);
|
|
888
1428
|
const [top, bottom] = [grid.edges[0] ?? 0, grid.edges[grid.edges.length - 1] ?? 0];
|
|
889
1429
|
const keepOut = [...ink.values()].flat().concat({ x: -1e9, y: top - 1e9, width: 2e9, height: 1e9 }, { x: -1e9, y: bottom, width: 2e9, height: 1e9 });
|
|
@@ -894,7 +1434,7 @@ function routeWires(wires, nodes, laid) {
|
|
|
894
1434
|
const s = stretches[w].find((t) => t.edge === at.edge && t.lo <= column && column <= t.hi);
|
|
895
1435
|
return trackY(grid, at.edge, s.track, tracks.get(at.edge));
|
|
896
1436
|
});
|
|
897
|
-
const waypoints = course === void 0 ? void 0 : waypointsOf(
|
|
1437
|
+
const waypoints = course === void 0 ? void 0 : waypointsOf(grids[w], course, ys);
|
|
898
1438
|
return { waypoints, path: routeOf(spec.get(edge.from), edge.fromPort, spec.get(edge.to), edge.toPort, laid.router, waypoints) };
|
|
899
1439
|
});
|
|
900
1440
|
const segments = routes.map(({ path }) => {
|
|
@@ -910,10 +1450,10 @@ function routeWires(wires, nodes, laid) {
|
|
|
910
1450
|
const label = spec.get(`${edgeIdOf(edge)}/label`)?.pose;
|
|
911
1451
|
const others = keepOut.concat(segments.flatMap((boxes, k) => k === w ? [] : boxes));
|
|
912
1452
|
const offset = LABEL_PLACEMENT.offset ?? 0;
|
|
913
|
-
const
|
|
914
|
-
if (
|
|
915
|
-
keepOut.push(
|
|
916
|
-
return { edge: { ...routed, labelPlacement: { at: { near:
|
|
1453
|
+
const clear2 = path === null || label === void 0 ? void 0 : clearLabel(path, label, offset, others) ?? clearLabel(path, label, offset, keepOut);
|
|
1454
|
+
if (clear2 === void 0) return { edge: routed, style };
|
|
1455
|
+
keepOut.push(clear2.box);
|
|
1456
|
+
return { edge: { ...routed, labelPlacement: { at: { near: clear2.near }, offset: clear2.offset } }, style };
|
|
917
1457
|
});
|
|
918
1458
|
return { edges, tracks };
|
|
919
1459
|
}
|
|
@@ -921,11 +1461,8 @@ function routeWires(wires, nodes, laid) {
|
|
|
921
1461
|
// src/effect/lanes.ts
|
|
922
1462
|
var LANE_INSET = 6;
|
|
923
1463
|
var MARGIN = 16;
|
|
924
|
-
var LANE_STYLES = [
|
|
925
|
-
{ fill: "rgba(255, 255, 255, 0.035)", stroke: "rgba(255, 255, 255, 0.06)", text: "#9fbdd6", strokeWidth: 1 },
|
|
926
|
-
{ fill: "rgba(255, 255, 255, 0)", stroke: "rgba(255, 255, 255, 0.06)", text: "#9fbdd6", strokeWidth: 1 }
|
|
927
|
-
];
|
|
928
1464
|
var laneId = (type) => `~lane:${type}`;
|
|
1465
|
+
var laneTypeOf = (id) => id.startsWith("~lane:") ? id.slice("~lane:".length) : void 0;
|
|
929
1466
|
function laneTypes(stages, first = []) {
|
|
930
1467
|
const used = [...new Set(stages.flatMap((s) => [...s.inputs ?? [], ...s.outputs ?? []].map((p) => p.type)))];
|
|
931
1468
|
const lead = [...new Set(first)].filter((t) => used.includes(t));
|
|
@@ -935,23 +1472,6 @@ function spanOf(stage, lane) {
|
|
|
935
1472
|
const at = [...stage.inputs ?? [], ...stage.outputs ?? []].map((p) => lane.get(p.type));
|
|
936
1473
|
return at.length === 0 ? [0, 0] : [Math.min(...at), Math.max(...at)];
|
|
937
1474
|
}
|
|
938
|
-
function columnsOf(stages, wires, spans) {
|
|
939
|
-
const feeds = new Map(stages.map((s) => [s.id, /* @__PURE__ */ new Set()]));
|
|
940
|
-
for (const d of wires) if (d.wire.from.stage !== d.wire.to.stage) feeds.get(d.wire.to.stage).add(d.wire.from.stage);
|
|
941
|
-
const column = /* @__PURE__ */ new Map();
|
|
942
|
-
const held = [];
|
|
943
|
-
const left = [...stages];
|
|
944
|
-
while (left.length > 0) {
|
|
945
|
-
const i = Math.max(0, left.findIndex((s) => [...feeds.get(s.id)].every((f) => column.has(f))));
|
|
946
|
-
const stage = left.splice(i, 1)[0];
|
|
947
|
-
const span = spans.get(stage.id);
|
|
948
|
-
let c = Math.max(0, ...[...feeds.get(stage.id)].flatMap((f) => column.has(f) ? [column.get(f) + 1] : []));
|
|
949
|
-
while (held[c]?.some(([lo, hi]) => lo <= span[1] && span[0] <= hi)) c++;
|
|
950
|
-
(held[c] ??= []).push(span);
|
|
951
|
-
column.set(stage.id, c);
|
|
952
|
-
}
|
|
953
|
-
return column;
|
|
954
|
-
}
|
|
955
1475
|
function laneHeights(count, least, needs, insets) {
|
|
956
1476
|
const heights = Array.from({ length: count }, () => least);
|
|
957
1477
|
for (const { span: [lo, hi], height } of [...needs].sort((a, b) => a.span[1] - a.span[0] - (b.span[1] - b.span[0]))) {
|
|
@@ -978,20 +1498,28 @@ function controlSideOf(stage, pipeline, lane) {
|
|
|
978
1498
|
const signal = ports.filter((p) => !isControl(pipeline, p.type));
|
|
979
1499
|
return control.length > 0 && signal.length > 0 && mean(control) > mean(signal) ? "bottom" : "top";
|
|
980
1500
|
}
|
|
981
|
-
function sizesOf(nodes, measure) {
|
|
982
|
-
const specs = diagramScene({ nodes, edges: [] }, { layout: "none", ...measure !== void 0 ? { measure } : {} });
|
|
1501
|
+
function sizesOf(nodes, groups, measure) {
|
|
1502
|
+
const specs = diagramScene({ nodes, edges: [], groups }, { layout: "none", ...measure !== void 0 ? { measure } : {} });
|
|
983
1503
|
const pose = new Map(specs.map((s) => [s.id, s.pose]));
|
|
984
|
-
|
|
1504
|
+
const spec = new Map(specs.map((s) => [s.id, s]));
|
|
1505
|
+
const nested = nestGroups(groups, (id) => nodes.some((n) => n.id === id)).map((g) => ({ members: g.members, inset: insetOf(diagramNodeOf({ id: g.id, kind: "leaf", data: spec.get(g.id)?.data })?.group?.inset) }));
|
|
1506
|
+
const boxed = (id, side) => nested.reduce((sum, g) => sum + (g.members.includes(id) ? g.inset[side] : 0), 0);
|
|
1507
|
+
const titles = new Map(groups.flatMap((g) => {
|
|
1508
|
+
const title2 = pose.get(groupLabelIdOf(g.id));
|
|
1509
|
+
return title2 === void 0 ? [] : [[g.id, title2.width]];
|
|
1510
|
+
}));
|
|
1511
|
+
const size = new Map(nodes.map((n) => {
|
|
985
1512
|
const box = pose.get(n.id);
|
|
986
1513
|
const ink = inkOf(n.id, specs);
|
|
987
1514
|
const over = {
|
|
988
1515
|
left: box.x - Math.min(...ink.map((b) => b.x)),
|
|
989
|
-
top: box.y - Math.min(...ink.map((b) => b.y)),
|
|
1516
|
+
top: box.y - Math.min(...ink.map((b) => b.y)) + boxed(n.id, "top"),
|
|
990
1517
|
right: Math.max(...ink.map((b) => b.x + b.width)) - box.x - box.width,
|
|
991
|
-
bottom: Math.max(...ink.map((b) => b.y + b.height)) - box.y - box.height
|
|
1518
|
+
bottom: Math.max(...ink.map((b) => b.y + b.height)) - box.y - box.height + boxed(n.id, "bottom")
|
|
992
1519
|
};
|
|
993
1520
|
return [n.id, { box, over }];
|
|
994
1521
|
}));
|
|
1522
|
+
return { size, titles };
|
|
995
1523
|
}
|
|
996
1524
|
function laneNode(type, box, color) {
|
|
997
1525
|
return {
|
|
@@ -1010,7 +1538,7 @@ function insetsFor(edges, tracks) {
|
|
|
1010
1538
|
return LANE_INSET + (k === 0 || k === edges - 1 ? n * TRACK_GAP : Math.max(0, n - 1) * TRACK_GAP / 2);
|
|
1011
1539
|
});
|
|
1012
1540
|
}
|
|
1013
|
-
function
|
|
1541
|
+
function place2(plan, insets) {
|
|
1014
1542
|
const { stages, size, spans, column } = plan;
|
|
1015
1543
|
const inkHeight = (id) => size.get(id).box.height + size.get(id).over.top + size.get(id).over.bottom;
|
|
1016
1544
|
const heights = laneHeights(plan.rows, plan.least, stages.map((s) => ({ span: spans.get(s.id), height: inkHeight(s.id) })), insets);
|
|
@@ -1029,11 +1557,13 @@ function place(plan, insets) {
|
|
|
1029
1557
|
const inkWidth = (id) => stood.get(id).width + stood.get(id).over.left + stood.get(id).over.right;
|
|
1030
1558
|
const widths = [];
|
|
1031
1559
|
for (const s of stages) widths[column.get(s.id)] = Math.max(widths[column.get(s.id)] ?? 0, inkWidth(s.id));
|
|
1032
|
-
const
|
|
1560
|
+
const offset = (id) => (widths[column.get(id)] - inkWidth(id)) / 2 + stood.get(id).over.left;
|
|
1561
|
+
const extent = (id) => ({ lo: offset(id), hi: offset(id) + stood.get(id).width });
|
|
1562
|
+
const { lefts, end } = columnLefts(widths, column, plan.blocks, plan.gap, plan.start, extent);
|
|
1033
1563
|
const nodes = stages.map((stage, k) => {
|
|
1034
1564
|
const c = column.get(stage.id);
|
|
1035
|
-
const { y, height, width
|
|
1036
|
-
const box = { x: lefts[c] + (
|
|
1565
|
+
const { y, height, width } = stood.get(stage.id);
|
|
1566
|
+
const box = { x: lefts[c] + offset(stage.id), y, width, height };
|
|
1037
1567
|
return {
|
|
1038
1568
|
...plan.drawn[k],
|
|
1039
1569
|
at: { x: box.x, y: box.y },
|
|
@@ -1044,25 +1574,28 @@ function place(plan, insets) {
|
|
|
1044
1574
|
});
|
|
1045
1575
|
const columns = Array.from(widths, (wd, c) => [lefts[c], lefts[c] + (wd ?? 0)]);
|
|
1046
1576
|
const edges = [...tops, tops.length === 0 ? 0 : tops[tops.length - 1] + heights[heights.length - 1]];
|
|
1047
|
-
return { nodes, tops, heights, columns, edges };
|
|
1577
|
+
return { nodes, tops, heights, columns, edges, end };
|
|
1048
1578
|
}
|
|
1049
1579
|
var lanes = (pipeline, options = {}) => {
|
|
1050
|
-
const
|
|
1580
|
+
const theme = options.theme ?? LOOK_THEME;
|
|
1581
|
+
const color = typeColors(pipeline, options.colors, theme.series);
|
|
1051
1582
|
const stages = uniqueStages(pipeline);
|
|
1052
1583
|
const types = laneTypes(stages, options.lanes);
|
|
1053
1584
|
const rows = types.length > 0 ? types : [""];
|
|
1054
1585
|
const lane = new Map(types.map((t, i) => [t, i]));
|
|
1055
1586
|
const spans = new Map(stages.map((s) => [s.id, spanOf(s, lane)]));
|
|
1056
|
-
const drawn = stages.map((stage) => kindOf(stage) === "decision" ? { id: stage.id, ...branchNode(stage, pipeline, options.branch, { controlSide: controlSideOf(stage, pipeline, lane) }) } : boxNode(stage, { showParams: options.showParams !== false, rowPorts: false, ports: [], run: options }));
|
|
1587
|
+
const drawn = stages.map((stage) => kindOf(stage) === "decision" ? { id: stage.id, ...branchNode(stage, pipeline, options.branch, { controlSide: controlSideOf(stage, pipeline, lane), style: stageStyle(stage, theme) }) } : boxNode(stage, { showParams: options.showParams !== false, rowPorts: false, ports: [], run: options }));
|
|
1057
1588
|
const labels = rows.map((t) => ({ id: laneId(t), lines: [t] }));
|
|
1058
|
-
const
|
|
1059
|
-
const
|
|
1589
|
+
const groups = uniqueGroups(pipeline);
|
|
1590
|
+
const { size, titles } = sizesOf([...drawn, ...labels], groups, options.measure);
|
|
1591
|
+
const { column, blocks } = laneColumns(stages, drawableWires(pipeline), spans, groups, titles);
|
|
1060
1592
|
const plan = {
|
|
1061
1593
|
stages,
|
|
1062
1594
|
drawn,
|
|
1063
1595
|
size,
|
|
1064
1596
|
spans,
|
|
1065
1597
|
column,
|
|
1598
|
+
blocks,
|
|
1066
1599
|
lane,
|
|
1067
1600
|
rows: rows.length,
|
|
1068
1601
|
least: Math.max(options.laneHeight ?? 48, ...labels.map((l) => size.get(l.id).box.height)),
|
|
@@ -1072,23 +1605,25 @@ var lanes = (pipeline, options = {}) => {
|
|
|
1072
1605
|
const router = options.router ?? "bezier";
|
|
1073
1606
|
const wires = wireEdges(pipeline, color);
|
|
1074
1607
|
let insets = insetsFor(rows.length + 1, /* @__PURE__ */ new Map());
|
|
1075
|
-
let laid =
|
|
1076
|
-
|
|
1608
|
+
let laid = place2(plan, insets);
|
|
1609
|
+
const route = (at) => routeWires(wires, at.nodes, { grid: at, column, router, measure: options.measure, groups });
|
|
1610
|
+
let routed = route(laid);
|
|
1077
1611
|
for (let round = 0; round < 4; round++) {
|
|
1078
|
-
const
|
|
1079
|
-
if (
|
|
1080
|
-
insets =
|
|
1081
|
-
laid =
|
|
1082
|
-
routed =
|
|
1612
|
+
const need2 = insetsFor(rows.length + 1, routed.tracks);
|
|
1613
|
+
if (need2.every((n, k) => n <= insets[k])) break;
|
|
1614
|
+
insets = need2.map((n, k) => Math.max(n, insets[k]));
|
|
1615
|
+
laid = place2(plan, insets);
|
|
1616
|
+
routed = route(laid);
|
|
1083
1617
|
}
|
|
1084
1618
|
const { columns, tops, heights } = laid;
|
|
1085
|
-
const right = columns.length === 0 ? plan.start :
|
|
1619
|
+
const right = columns.length === 0 ? plan.start : laid.end + MARGIN;
|
|
1086
1620
|
const laneNodes = rows.map((t, i) => laneNode(t, { x: 0, y: tops[i], width: right, height: heights[i] }, color));
|
|
1087
1621
|
const edges = routed.edges;
|
|
1088
|
-
const styles = sceneStyles(stages, edges);
|
|
1089
|
-
const
|
|
1622
|
+
const styles = sceneStyles(stages, edges, theme);
|
|
1623
|
+
const bodies = theme.lane.bodies;
|
|
1624
|
+
const laneStyle = new Map(rows.map((t, i) => [laneId(t), bodies[i % bodies.length]]));
|
|
1090
1625
|
return {
|
|
1091
|
-
data: { nodes: [...laneNodes, ...laid.nodes], edges: edges.map((e) => e.edge), groups
|
|
1626
|
+
data: { nodes: [...laneNodes, ...laid.nodes], edges: edges.map((e) => e.edge), groups },
|
|
1092
1627
|
scene: {
|
|
1093
1628
|
layout: "none",
|
|
1094
1629
|
router,
|
|
@@ -1096,61 +1631,17 @@ var lanes = (pipeline, options = {}) => {
|
|
|
1096
1631
|
...styles,
|
|
1097
1632
|
nodeStyle: (n) => laneStyle.get(n.id) ?? styles.nodeStyle(n)
|
|
1098
1633
|
},
|
|
1099
|
-
color
|
|
1634
|
+
color,
|
|
1635
|
+
order: { axis: "x", items: mainChain(pipeline).map((id) => ({ id })) },
|
|
1636
|
+
...types.length > 0 ? {
|
|
1637
|
+
laneOrder: {
|
|
1638
|
+
axis: "y",
|
|
1639
|
+
items: types.map((t, i) => ({ id: laneId(t), grip: { x: 0, y: tops[i], width: plan.start, height: heights[i] } }))
|
|
1640
|
+
}
|
|
1641
|
+
} : {}
|
|
1100
1642
|
};
|
|
1101
1643
|
};
|
|
1102
1644
|
|
|
1103
|
-
// src/effect/mainChain.ts
|
|
1104
|
-
function mainChain(pipeline) {
|
|
1105
|
-
const next = /* @__PURE__ */ new Map();
|
|
1106
|
-
for (const d of drawableWires(pipeline)) {
|
|
1107
|
-
if (isControl(pipeline, d.type)) continue;
|
|
1108
|
-
next.set(d.wire.from.stage, [...next.get(d.wire.from.stage) ?? [], d.wire.to.stage]);
|
|
1109
|
-
}
|
|
1110
|
-
const best = /* @__PURE__ */ new Map();
|
|
1111
|
-
const walking = /* @__PURE__ */ new Set();
|
|
1112
|
-
const from = (id) => {
|
|
1113
|
-
const known = best.get(id);
|
|
1114
|
-
if (known !== void 0) return known;
|
|
1115
|
-
if (walking.has(id)) return [];
|
|
1116
|
-
walking.add(id);
|
|
1117
|
-
let tail = [];
|
|
1118
|
-
for (const to of next.get(id) ?? []) {
|
|
1119
|
-
const path2 = from(to);
|
|
1120
|
-
if (path2.length > tail.length && !path2.includes(id)) tail = path2;
|
|
1121
|
-
}
|
|
1122
|
-
walking.delete(id);
|
|
1123
|
-
const path = [id, ...tail];
|
|
1124
|
-
best.set(id, path);
|
|
1125
|
-
return path;
|
|
1126
|
-
};
|
|
1127
|
-
let longest = [];
|
|
1128
|
-
for (const stage of uniqueStages(pipeline)) {
|
|
1129
|
-
const path = from(stage.id);
|
|
1130
|
-
if (path.length > longest.length) longest = path;
|
|
1131
|
-
}
|
|
1132
|
-
return longest;
|
|
1133
|
-
}
|
|
1134
|
-
function skipDecisions(pipeline, line) {
|
|
1135
|
-
const wires = drawableWires(pipeline);
|
|
1136
|
-
const stages = new Map(uniqueStages(pipeline).map((s) => [s.id, s]));
|
|
1137
|
-
const out = [];
|
|
1138
|
-
line.forEach((id, i) => {
|
|
1139
|
-
const decision = stages.get(id);
|
|
1140
|
-
if (decision === void 0 || kindOf(decision) !== "decision" || i === 0) return;
|
|
1141
|
-
if ((decision.inputs ?? []).length !== 1 || (decision.outputs ?? []).length !== 2) return;
|
|
1142
|
-
const entering = wires.filter((d) => d.wire.to.stage === id);
|
|
1143
|
-
const leaving = wires.filter((d) => d.wire.from.stage === id);
|
|
1144
|
-
if (entering.length !== 1 || leaving.length !== 2 || entering[0].wire.from.stage !== line[i - 1]) return;
|
|
1145
|
-
const through = leaving.find((d) => d.wire.to.stage === line[i + 1]);
|
|
1146
|
-
const skip = leaving.find((d) => d !== through && line.indexOf(d.wire.to.stage) > i + 1);
|
|
1147
|
-
const ordinary = (s) => s !== void 0 && kindOf(s) !== "decision";
|
|
1148
|
-
if (!ordinary(stages.get(line[i - 1])) || !ordinary(through?.toStage) || !ordinary(skip?.toStage)) return;
|
|
1149
|
-
if (through !== void 0 && skip !== void 0) out.push({ decision, into: entering[0], through, skip });
|
|
1150
|
-
});
|
|
1151
|
-
return out;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
1645
|
// src/effect/rack.ts
|
|
1155
1646
|
var SKIP_OUT = "~skip-out";
|
|
1156
1647
|
var SKIP_IN = "~skip-in";
|
|
@@ -1164,7 +1655,14 @@ function rackWires(pipeline, skips) {
|
|
|
1164
1655
|
const eaten = new Set(skips.flatMap((s) => [s.into.index, s.through.index, s.skip.index]));
|
|
1165
1656
|
const wires = drawableWires(pipeline).filter((d) => !eaten.has(d.index)).map((d) => {
|
|
1166
1657
|
const label = exitLabel(d);
|
|
1167
|
-
return {
|
|
1658
|
+
return {
|
|
1659
|
+
...d.wire.id !== void 0 ? { id: d.wire.id } : {},
|
|
1660
|
+
from: d.wire.from,
|
|
1661
|
+
to: d.wire.to,
|
|
1662
|
+
type: d.type,
|
|
1663
|
+
problem: d.problem,
|
|
1664
|
+
...label !== void 0 ? { label } : {}
|
|
1665
|
+
};
|
|
1168
1666
|
});
|
|
1169
1667
|
for (const s of skips) {
|
|
1170
1668
|
wires.push({
|
|
@@ -1186,22 +1684,12 @@ function rackWires(pipeline, skips) {
|
|
|
1186
1684
|
}
|
|
1187
1685
|
return wires;
|
|
1188
1686
|
}
|
|
1189
|
-
function lineOf(pipeline,
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
const mainOut = /* @__PURE__ */ new Map();
|
|
1193
|
-
for (const wire of wires) {
|
|
1194
|
-
if (wire.arc || isControl(pipeline, wire.type)) continue;
|
|
1195
|
-
const a = at.get(wire.from.stage);
|
|
1196
|
-
const b = at.get(wire.to.stage);
|
|
1197
|
-
if (a === void 0 || b !== a + 1) continue;
|
|
1198
|
-
if (!mainOut.has(wire.from.stage)) mainOut.set(wire.from.stage, wire.from.port);
|
|
1199
|
-
if (!mainIn.has(wire.to.stage)) mainIn.set(wire.to.stage, wire.to.port);
|
|
1200
|
-
}
|
|
1201
|
-
return { at, mainIn, mainOut };
|
|
1687
|
+
function lineOf(pipeline, ordered, chain) {
|
|
1688
|
+
const { mainIn, mainOut } = chainLinks(pipeline, ordered);
|
|
1689
|
+
return { at: new Map(chain.map((id, i) => [id, i])), mainIn, mainOut };
|
|
1202
1690
|
}
|
|
1203
|
-
function
|
|
1204
|
-
if (!line.at.has(stage.id)) return [...spreadAt(stage.outputs ?? [], far), ...spreadAt(stage.inputs ?? [],
|
|
1691
|
+
function portsOf2(stage, line, wires, near2, far) {
|
|
1692
|
+
if (!line.at.has(stage.id)) return [...spreadAt(stage.outputs ?? [], far), ...spreadAt(stage.inputs ?? [], near2)];
|
|
1205
1693
|
const typed = (p, anchor) => ({ id: p.id, type: p.type, at: anchor });
|
|
1206
1694
|
const out = [];
|
|
1207
1695
|
const side = [];
|
|
@@ -1213,7 +1701,7 @@ function portsOf(stage, line, wires, near, far) {
|
|
|
1213
1701
|
if (p.id === line.mainOut.get(stage.id)) out.push(typed(p, { u: 1, v: 0.5 }));
|
|
1214
1702
|
else side.push(p);
|
|
1215
1703
|
}
|
|
1216
|
-
out.push(...spreadAt(side,
|
|
1704
|
+
out.push(...spreadAt(side, near2));
|
|
1217
1705
|
const type = portOn(stage, line.mainOut.get(stage.id) ?? line.mainIn.get(stage.id) ?? "")?.type;
|
|
1218
1706
|
const arc = (id, u) => ({ id, at: { u, v: far }, ...type !== void 0 ? { type } : {} });
|
|
1219
1707
|
if (wires.some((w) => w.arc && w.from.stage === stage.id)) out.push(arc(SKIP_OUT, 0.75));
|
|
@@ -1221,9 +1709,10 @@ function portsOf(stage, line, wires, near, far) {
|
|
|
1221
1709
|
return out;
|
|
1222
1710
|
}
|
|
1223
1711
|
var rack = (pipeline, options = {}) => {
|
|
1224
|
-
const
|
|
1712
|
+
const theme = options.theme ?? LOOK_THEME;
|
|
1713
|
+
const color = typeColors(pipeline, options.colors, theme.series);
|
|
1225
1714
|
const below = options.side !== "above";
|
|
1226
|
-
const
|
|
1715
|
+
const near2 = below ? 1 : 0;
|
|
1227
1716
|
const far = below ? 0 : 1;
|
|
1228
1717
|
const stages = uniqueStages(pipeline);
|
|
1229
1718
|
const given = options.chain?.filter((id) => stages.some((s) => s.id === id));
|
|
@@ -1232,12 +1721,12 @@ var rack = (pipeline, options = {}) => {
|
|
|
1232
1721
|
const gone = new Set(skips.map((s) => s.decision.id));
|
|
1233
1722
|
const chain = ordered.filter((id) => !gone.has(id));
|
|
1234
1723
|
const wires = rackWires(pipeline, skips);
|
|
1235
|
-
const line = lineOf(pipeline,
|
|
1724
|
+
const line = lineOf(pipeline, ordered, chain);
|
|
1236
1725
|
const kept = stages.filter((s) => !gone.has(s.id));
|
|
1237
1726
|
const controlSide = below ? "bottom" : "top";
|
|
1238
1727
|
const nodes = kept.map((stage) => {
|
|
1239
|
-
if (kindOf(stage) === "decision") return { id: stage.id, ...branchNode(stage, pipeline, options.branch, { controlSide }) };
|
|
1240
|
-
const ports =
|
|
1728
|
+
if (kindOf(stage) === "decision") return { id: stage.id, ...branchNode(stage, pipeline, options.branch, { controlSide, style: stageStyle(stage, theme) }) };
|
|
1729
|
+
const ports = portsOf2(stage, line, wires, near2, far);
|
|
1241
1730
|
const node = boxNode(stage, { showParams: options.showParams !== false, rowPorts: false, ports, run: options });
|
|
1242
1731
|
const room = roomFor(ports);
|
|
1243
1732
|
return room > 2 * SIDE_PORT_GAP ? { ...node, width: room } : node;
|
|
@@ -1248,7 +1737,7 @@ var rack = (pipeline, options = {}) => {
|
|
|
1248
1737
|
const sideInput = to !== void 0 && kindOf(to) !== "decision" && !wire.arc && wire.to.port !== line.mainIn.get(wire.to.stage) && line.at.has(wire.to.stage);
|
|
1249
1738
|
const label = wire.label ?? (sideInput ? portOn(to, wire.to.port)?.label ?? wire.to.port : void 0);
|
|
1250
1739
|
return {
|
|
1251
|
-
edge: wireEdge(wire.from, wire.to, label),
|
|
1740
|
+
edge: wireEdge(wire.from, wire.to, label, wire.id),
|
|
1252
1741
|
style: wireStyle(color(wire.type), wire.arc === true || isControl(pipeline, wire.type), wire.problem)
|
|
1253
1742
|
};
|
|
1254
1743
|
});
|
|
@@ -1268,16 +1757,812 @@ var rack = (pipeline, options = {}) => {
|
|
|
1268
1757
|
...options.rankGap !== void 0 ? { rankGap: options.rankGap } : {}
|
|
1269
1758
|
},
|
|
1270
1759
|
router: "bezier",
|
|
1271
|
-
...sceneStyles(kept, edges)
|
|
1760
|
+
...sceneStyles(kept, edges, theme)
|
|
1272
1761
|
},
|
|
1273
1762
|
color,
|
|
1763
|
+
order: { axis: "x", items: chain.map((id) => ({ id })) },
|
|
1274
1764
|
exits: new Map(wires.flatMap((wire, i) => wire.exit === void 0 ? [] : [[edgeIdOf(edges[i].edge), wire.exit]]))
|
|
1275
1765
|
};
|
|
1276
1766
|
};
|
|
1277
1767
|
|
|
1768
|
+
// src/effect/editorMapping.ts
|
|
1769
|
+
var endsKey = (from, to) => JSON.stringify([from.stage, from.port, to.stage, to.port]);
|
|
1770
|
+
var edgeEnds = (e) => endsKey({ stage: e.from, port: e.fromPort ?? "" }, { stage: e.to, port: e.toPort ?? "" });
|
|
1771
|
+
function editorScene(doc, look = "nodeGraph", options) {
|
|
1772
|
+
const drawn = drawLook(doc, look, options);
|
|
1773
|
+
const stages = new Set(doc.pipeline.stages.map((s) => s.id));
|
|
1774
|
+
const groups = new Set((doc.pipeline.groups ?? []).map((g) => g.id));
|
|
1775
|
+
const ids = ensureWireIds(doc).pipeline.wires.map((w) => w.id);
|
|
1776
|
+
const byEnds = /* @__PURE__ */ new Map();
|
|
1777
|
+
for (const d of drawableWires(doc.pipeline)) {
|
|
1778
|
+
const key = endsKey(d.wire.from, d.wire.to);
|
|
1779
|
+
if (!byEnds.has(key)) byEnds.set(key, ids[d.index]);
|
|
1780
|
+
}
|
|
1781
|
+
const wires = new Map(drawn.data.edges.flatMap((e) => {
|
|
1782
|
+
const id = byEnds.get(edgeEnds(e));
|
|
1783
|
+
return id === void 0 ? [] : [[edgeIdOf(e), id]];
|
|
1784
|
+
}));
|
|
1785
|
+
const routes = look === "nodeGraph" ? doc.routes : void 0;
|
|
1786
|
+
const edges = routes === void 0 ? drawn.data.edges : drawn.data.edges.map((e) => {
|
|
1787
|
+
const id = wires.get(edgeIdOf(e));
|
|
1788
|
+
const route = id === void 0 ? void 0 : routes[id];
|
|
1789
|
+
return route === void 0 ? e : { ...e, waypoints: route };
|
|
1790
|
+
});
|
|
1791
|
+
return {
|
|
1792
|
+
...drawn,
|
|
1793
|
+
data: { ...drawn.data, edges },
|
|
1794
|
+
stageOf: (node) => stages.has(node) ? node : void 0,
|
|
1795
|
+
groupOf: (node) => groups.has(node) && !stages.has(node) ? node : void 0,
|
|
1796
|
+
wireOf: (edge) => wires.get(edge)
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
function drawLook(doc, look, options) {
|
|
1800
|
+
if (look === "rack") return rack(doc.pipeline, options);
|
|
1801
|
+
if (look === "lanes") {
|
|
1802
|
+
const opts = options;
|
|
1803
|
+
const order = doc.laneOrder ?? opts?.lanes;
|
|
1804
|
+
return lanes(doc.pipeline, { ...opts, ...order !== void 0 ? { lanes: order } : {} });
|
|
1805
|
+
}
|
|
1806
|
+
const drawn = nodeGraph(doc.pipeline, options);
|
|
1807
|
+
const nodes = drawn.data.nodes.map((n) => {
|
|
1808
|
+
const at = doc.places[n.id];
|
|
1809
|
+
return at === void 0 ? n : { ...n, at: { x: at.x, y: at.y }, pinned: true };
|
|
1810
|
+
});
|
|
1811
|
+
const { groups = [], ...rest } = drawn.data;
|
|
1812
|
+
const containers = groups.map((g) => {
|
|
1813
|
+
const box = doc.groupBoxes?.[g.id];
|
|
1814
|
+
return {
|
|
1815
|
+
id: g.id,
|
|
1816
|
+
role: "container",
|
|
1817
|
+
label: g.label ?? "",
|
|
1818
|
+
members: g.members,
|
|
1819
|
+
...box !== void 0 ? { box: { ...box }, pinned: true } : {}
|
|
1820
|
+
};
|
|
1821
|
+
});
|
|
1822
|
+
return {
|
|
1823
|
+
...drawn,
|
|
1824
|
+
data: {
|
|
1825
|
+
...rest,
|
|
1826
|
+
nodes,
|
|
1827
|
+
...containers.length > 0 ? { containers } : {},
|
|
1828
|
+
...doc.pages !== void 0 ? { pages: doc.pages } : {}
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// src/effect/groupOrder.ts
|
|
1834
|
+
function groupRuns(pipeline, chain) {
|
|
1835
|
+
const at = new Map(chain.map((id, i) => [id, i]));
|
|
1836
|
+
const doc = { pipeline};
|
|
1837
|
+
const runs = /* @__PURE__ */ new Map();
|
|
1838
|
+
for (const g of pipeline.groups ?? []) {
|
|
1839
|
+
const on = [...groupContents(doc, g.id).stages].flatMap((s) => at.get(s) ?? []);
|
|
1840
|
+
if (on.length === 0) continue;
|
|
1841
|
+
const first = Math.min(...on);
|
|
1842
|
+
const last = Math.max(...on);
|
|
1843
|
+
runs.set(g.id, last - first + 1 === on.length ? { first, last } : null);
|
|
1844
|
+
}
|
|
1845
|
+
return runs;
|
|
1846
|
+
}
|
|
1847
|
+
var holds = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
|
|
1848
|
+
function groupGripOf(doc, group, boxOf) {
|
|
1849
|
+
const box = boxOf(group);
|
|
1850
|
+
const members = doc.pipeline.groups?.find((g) => g.id === group)?.members ?? [];
|
|
1851
|
+
const tops = members.flatMap((m) => boxOf(m)?.y ?? []);
|
|
1852
|
+
if (box === void 0 || tops.length === 0) return void 0;
|
|
1853
|
+
const height = Math.min(...tops) - box.y;
|
|
1854
|
+
return height > 0 ? { x: box.x, y: box.y, width: box.width, height } : void 0;
|
|
1855
|
+
}
|
|
1856
|
+
function unitStages(doc, unit) {
|
|
1857
|
+
return doc.pipeline.groups?.some((g) => g.id === unit) ? groupContents(doc, unit).stages : /* @__PURE__ */ new Set([unit]);
|
|
1858
|
+
}
|
|
1859
|
+
function parentsOf(doc) {
|
|
1860
|
+
const parent = /* @__PURE__ */ new Map();
|
|
1861
|
+
for (const g of doc.pipeline.groups ?? []) for (const m of g.members) parent.set(m, g.id);
|
|
1862
|
+
return parent;
|
|
1863
|
+
}
|
|
1864
|
+
function groupAt(doc, boxOf, at, among = () => true) {
|
|
1865
|
+
const parent = parentsOf(doc);
|
|
1866
|
+
const depth = (id) => parent.has(id) ? 1 + depth(parent.get(id)) : 0;
|
|
1867
|
+
const held = (doc.pipeline.groups ?? []).filter((g) => {
|
|
1868
|
+
const grip = among(g.id) ? groupGripOf(doc, g.id, boxOf) : void 0;
|
|
1869
|
+
return grip !== void 0 && holds(grip, at);
|
|
1870
|
+
});
|
|
1871
|
+
return held.sort((a, b) => depth(b.id) - depth(a.id))[0]?.id;
|
|
1872
|
+
}
|
|
1873
|
+
function groupSlotsAt(source, at) {
|
|
1874
|
+
const { doc, order, boxOf } = source;
|
|
1875
|
+
const groups = doc.pipeline.groups ?? [];
|
|
1876
|
+
if (order === void 0 || groups.length === 0) return null;
|
|
1877
|
+
const parent = parentsOf(doc);
|
|
1878
|
+
const runs = groupRuns(doc.pipeline, mainChain(doc.pipeline));
|
|
1879
|
+
const held = groupAt(doc, boxOf, at, (g) => runs.get(g) != null);
|
|
1880
|
+
if (held === void 0) return null;
|
|
1881
|
+
const up = parent.get(held);
|
|
1882
|
+
const siblings = up !== void 0 ? groups.find((g) => g.id === up).members : [...groups, ...doc.pipeline.stages].flatMap((u) => parent.has(u.id) ? [] : [u.id]);
|
|
1883
|
+
const drawnAt = new Map(order.items.map((i, k) => [i.id, k]));
|
|
1884
|
+
const placed = siblings.flatMap((id) => {
|
|
1885
|
+
const drawn = [...unitStages(doc, id)].flatMap((s) => drawnAt.get(s) ?? []);
|
|
1886
|
+
const bounds = boxOf(id);
|
|
1887
|
+
return drawn.length === 0 || bounds === void 0 ? [] : [{ id, bounds, first: Math.min(...drawn) }];
|
|
1888
|
+
}).sort((a, b) => a.first - b.first);
|
|
1889
|
+
const isGroup = new Set(groups.map((g) => g.id));
|
|
1890
|
+
const items = placed.map(({ id, bounds }) => {
|
|
1891
|
+
if (!isGroup.has(id)) return { id, bounds, grip: null };
|
|
1892
|
+
const inside = groupContents(doc, id);
|
|
1893
|
+
const carries = [...inside.groups, ...inside.stages, ...[...inside.groups].map(groupLabelIdOf)];
|
|
1894
|
+
return { id, bounds, grip: groupGripOf(doc, id, boxOf) ?? null, carries };
|
|
1895
|
+
});
|
|
1896
|
+
return items.length < 2 ? null : sequenceSlots(order.axis, items);
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
// src/effect/editorReorder.ts
|
|
1900
|
+
var REORDER_LANES_ACTION_ID = "diagram.reorderLanes";
|
|
1901
|
+
var REORDER_GROUPS_ACTION_ID = "diagram.reorderGroups";
|
|
1902
|
+
function slotsOf(order, boxOf) {
|
|
1903
|
+
if (order === void 0) return null;
|
|
1904
|
+
const items = order.items.flatMap((i) => {
|
|
1905
|
+
const bounds = boxOf(i.id);
|
|
1906
|
+
return bounds === void 0 ? [] : [{ id: i.id, bounds, ...i.grip !== void 0 ? { grip: i.grip } : {} }];
|
|
1907
|
+
});
|
|
1908
|
+
return items.length < 2 ? null : sequenceSlots(order.axis, items);
|
|
1909
|
+
}
|
|
1910
|
+
function keepsGroups(doc, next) {
|
|
1911
|
+
if (next === null) return null;
|
|
1912
|
+
const after = groupRuns(next.pipeline, mainChain(next.pipeline));
|
|
1913
|
+
for (const [id, run] of groupRuns(doc.pipeline, mainChain(doc.pipeline))) if (run !== null && after.get(id) === null) return null;
|
|
1914
|
+
return next;
|
|
1915
|
+
}
|
|
1916
|
+
function dropInGap(doc, chain, drawn, to, at) {
|
|
1917
|
+
const tries = [to];
|
|
1918
|
+
for (let k = to; k > 0 && !drawn.has(chain[k - 1]); k--) tries.push(k - 1);
|
|
1919
|
+
for (let k = to; k < chain.length && !drawn.has(chain[k]); k++) tries.push(k + 1);
|
|
1920
|
+
for (const t of tries) {
|
|
1921
|
+
const next = keepsGroups(doc, at(t));
|
|
1922
|
+
if (next !== null) return next;
|
|
1923
|
+
}
|
|
1924
|
+
return null;
|
|
1925
|
+
}
|
|
1926
|
+
var drawnOf = (order) => new Set(order.items.map((i) => i.id));
|
|
1927
|
+
function stageMove(doc, order, move) {
|
|
1928
|
+
const chain = mainChain(doc.pipeline);
|
|
1929
|
+
const before = order.items[move.to];
|
|
1930
|
+
const to = before === void 0 ? chain.length : chain.indexOf(before.id);
|
|
1931
|
+
const wired = ensureWireIds(doc);
|
|
1932
|
+
return dropInGap(doc, chain, drawnOf(order), to, (t) => moveInChain(wired, move.id, t));
|
|
1933
|
+
}
|
|
1934
|
+
function groupMove(doc, order, units, move) {
|
|
1935
|
+
const chain = mainChain(doc.pipeline);
|
|
1936
|
+
const drawn = drawnOf(order);
|
|
1937
|
+
const indices = (unit, drawnOnly) => [...unitStages(doc, unit)].flatMap((s) => chain.includes(s) && (!drawnOnly || drawn.has(s)) ? [chain.indexOf(s)] : []);
|
|
1938
|
+
const run = indices(move.id, false);
|
|
1939
|
+
if (run.length === 0) return null;
|
|
1940
|
+
const [i, j] = [Math.min(...run), Math.max(...run)];
|
|
1941
|
+
if (j - i + 1 !== run.length) return null;
|
|
1942
|
+
const before = units[move.to];
|
|
1943
|
+
const edge = before !== void 0 ? indices(before, true) : indices(units[units.length - 1] ?? move.id, true).map((k) => k + 1);
|
|
1944
|
+
if (edge.length === 0) return null;
|
|
1945
|
+
const to = before !== void 0 ? Math.min(...edge) : Math.max(...edge);
|
|
1946
|
+
const wired = ensureWireIds(doc);
|
|
1947
|
+
return dropInGap(doc, chain, drawn, to, (t) => moveRunInChain(wired, chain[i], chain[j], t));
|
|
1948
|
+
}
|
|
1949
|
+
function laneMove(doc, order, move) {
|
|
1950
|
+
const types = order.items.flatMap((i) => laneTypeOf(i.id) ?? []);
|
|
1951
|
+
const moved = types.filter((_, k) => k !== move.from);
|
|
1952
|
+
moved.splice(move.to > move.from ? move.to - 1 : move.to, 0, types[move.from]);
|
|
1953
|
+
return setLaneOrder(doc, moved);
|
|
1954
|
+
}
|
|
1955
|
+
function unitsBefore(move) {
|
|
1956
|
+
const units = move.order.filter((id) => id !== move.id);
|
|
1957
|
+
units.splice(move.from, 0, move.id);
|
|
1958
|
+
return units;
|
|
1959
|
+
}
|
|
1960
|
+
function groupReorder(now) {
|
|
1961
|
+
const moved = (move) => {
|
|
1962
|
+
const { doc, order } = now();
|
|
1963
|
+
return order === void 0 ? null : groupMove(doc, order, unitsBefore(move), move);
|
|
1964
|
+
};
|
|
1965
|
+
return reorderContribution({
|
|
1966
|
+
id: REORDER_GROUPS_ACTION_ID,
|
|
1967
|
+
label: "Reorder group",
|
|
1968
|
+
slots: (_, at) => groupSlotsAt(now(), at),
|
|
1969
|
+
canDrop: (move) => moved(move) !== null,
|
|
1970
|
+
commit: (move) => {
|
|
1971
|
+
const next = moved(move);
|
|
1972
|
+
if (next !== null) now().onChange(next, "Reorder group");
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
function editorReorders(now) {
|
|
1977
|
+
return [
|
|
1978
|
+
reorderContribution({
|
|
1979
|
+
label: "Reorder",
|
|
1980
|
+
slots: () => slotsOf(now().order, now().boxOf),
|
|
1981
|
+
canDrop: (move) => {
|
|
1982
|
+
const { doc, order } = now();
|
|
1983
|
+
return order !== void 0 && stageMove(doc, order, move) !== null;
|
|
1984
|
+
},
|
|
1985
|
+
commit: (move) => {
|
|
1986
|
+
const { doc, order, onChange } = now();
|
|
1987
|
+
const next = order === void 0 ? null : stageMove(doc, order, move);
|
|
1988
|
+
if (next !== null) onChange(next, "Reorder");
|
|
1989
|
+
}
|
|
1990
|
+
}),
|
|
1991
|
+
reorderContribution({
|
|
1992
|
+
id: REORDER_LANES_ACTION_ID,
|
|
1993
|
+
label: "Reorder lanes",
|
|
1994
|
+
slots: () => slotsOf(now().laneOrder, now().boxOf),
|
|
1995
|
+
commit: (move) => {
|
|
1996
|
+
const { doc, laneOrder, onChange } = now();
|
|
1997
|
+
if (laneOrder !== void 0) onChange(laneMove(doc, laneOrder, move), "Reorder lanes");
|
|
1998
|
+
}
|
|
1999
|
+
}),
|
|
2000
|
+
groupReorder(now)
|
|
2001
|
+
];
|
|
2002
|
+
}
|
|
2003
|
+
var RENAME_GROUP_ACTION_ID = "effect.renameGroup";
|
|
2004
|
+
function renameGroupContribution(now, rename) {
|
|
2005
|
+
const under = (at) => {
|
|
2006
|
+
const { doc, boxOf } = now();
|
|
2007
|
+
return groupAt(doc, boxOf, at);
|
|
2008
|
+
};
|
|
2009
|
+
return {
|
|
2010
|
+
id: `weasel-diagram-effect:${RENAME_GROUP_ACTION_ID}`,
|
|
2011
|
+
eligibility: { always: true },
|
|
2012
|
+
actions: [{
|
|
2013
|
+
id: RENAME_GROUP_ACTION_ID,
|
|
2014
|
+
label: "Rename group",
|
|
2015
|
+
group: "diagram",
|
|
2016
|
+
enabled: (_, at) => at === void 0 || under(at) !== void 0 ? true : ActionDisabledReason.NotApplicable,
|
|
2017
|
+
invoker: {
|
|
2018
|
+
timing: "immediate",
|
|
2019
|
+
run(_, params) {
|
|
2020
|
+
const group = under({ x: Number(params?.worldX), y: Number(params?.worldY) });
|
|
2021
|
+
if (group !== void 0) rename(group);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
}],
|
|
2025
|
+
// Empty space included: an unlabeled group's grip is padding.
|
|
2026
|
+
bindings: [{
|
|
2027
|
+
spec: { kind: "doubleClick", target: { kindOf: ANYWHERE } },
|
|
2028
|
+
actionId: RENAME_GROUP_ACTION_ID,
|
|
2029
|
+
opts: { id: `${RENAME_GROUP_ACTION_ID}.doubleClick` }
|
|
2030
|
+
}]
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
// src/effect/place.ts
|
|
2035
|
+
var PLACE_GAP = 24;
|
|
2036
|
+
var STEP = 8;
|
|
2037
|
+
var clear = (x, y, size, boxes) => boxes.every((b) => x >= b.x + b.width + PLACE_GAP || x + size.width + PLACE_GAP <= b.x || y >= b.y + b.height + PLACE_GAP || y + size.height + PLACE_GAP <= b.y);
|
|
2038
|
+
function freeSpot(boxes, size, view) {
|
|
2039
|
+
if (view !== null) {
|
|
2040
|
+
const right = view.x + view.width - PLACE_GAP - size.width;
|
|
2041
|
+
const bottom = view.y + view.height - PLACE_GAP - size.height;
|
|
2042
|
+
for (let y = view.y + PLACE_GAP; y <= bottom; y += STEP) {
|
|
2043
|
+
for (let x = view.x + PLACE_GAP; x <= right; x += STEP) {
|
|
2044
|
+
if (clear(x, y, size, boxes)) return { x, y };
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
if (boxes.length === 0) return { x: (view?.x ?? 0) + PLACE_GAP, y: (view?.y ?? 0) + PLACE_GAP };
|
|
2049
|
+
const last = boxes.reduce((a, b) => b.x + b.width > a.x + a.width ? b : a);
|
|
2050
|
+
return { x: last.x + last.width + PLACE_GAP, y: last.y };
|
|
2051
|
+
}
|
|
2052
|
+
var EffectEditor = forwardRef(function EffectEditor2({ doc, onChange, run, selected, onSelect, selectedGroup, onSelectGroup, look = "nodeGraph", options, width, height, background, className }, ref) {
|
|
2053
|
+
const built = useMemo(() => editorScene(doc, look, options), [doc, look, options]);
|
|
2054
|
+
const specs = useMemo(() => applyLookRoutes(diagramScene(built.data, built.scene), doc, look, built).map((s) => s.pickable === false || isPart(built, s.id) ? s : { ...s, pickable: false }), [built, doc, look]);
|
|
2055
|
+
const painted = useMemo(() => paintRun(doc.pipeline, specs, run, built.exits), [doc.pipeline, specs, run, built]);
|
|
2056
|
+
useReleaseReplaced(run);
|
|
2057
|
+
const portOptions = useRunPortOptions(built.color, doc.pipeline, run);
|
|
2058
|
+
const picks = useEditorPicks(built, { selected, selectedGroup, onSelect, onSelectGroup });
|
|
2059
|
+
const now = useLatest({ doc, built, specs, look, options, onChange });
|
|
2060
|
+
const view = useRef(null);
|
|
2061
|
+
const [renaming, setRenaming] = useState(null);
|
|
2062
|
+
const handlers = useMemo(() => editHandlers(() => now.current, picks.unpick, setRenaming), [now, picks.unpick]);
|
|
2063
|
+
const ambient = useMemo(() => {
|
|
2064
|
+
const source = () => {
|
|
2065
|
+
const { doc: d, built: b, specs: shown, onChange: report } = now.current;
|
|
2066
|
+
const boxes = new Map(shown.map((s) => [s.id, s.pose]));
|
|
2067
|
+
return { doc: d, order: b.order, laneOrder: b.laneOrder, boxOf: (id) => boxes.get(id), onChange: report };
|
|
2068
|
+
};
|
|
2069
|
+
return [...editorReorders(source), renameGroupContribution(source, setRenaming)];
|
|
2070
|
+
}, [now]);
|
|
2071
|
+
useImperativeHandle(ref, () => ({
|
|
2072
|
+
addStage: (type, client) => {
|
|
2073
|
+
const { doc: d, specs: shown, look: l, options: opts, onChange: report } = now.current;
|
|
2074
|
+
const stage = stageFromType(type, nextStageId(d.pipeline, type.type));
|
|
2075
|
+
if (l !== "nodeGraph") {
|
|
2076
|
+
report(addStage(ensureWireIds(d), stage), "Add stage");
|
|
2077
|
+
return stage.id;
|
|
2078
|
+
}
|
|
2079
|
+
const pinned = ensureWireIds(pinShown(d, shown));
|
|
2080
|
+
if (client && view.current) {
|
|
2081
|
+
report(addStage(pinned, stage, view.current.worldAt(client.x, client.y)), "Add stage");
|
|
2082
|
+
} else {
|
|
2083
|
+
const boxes = stageBoxes(d, shown);
|
|
2084
|
+
const alone = { pipeline: { ...d.pipeline, stages: [stage], wires: [], groups: [] }, places: {} };
|
|
2085
|
+
const size = stageBoxes(alone, drawnSpecs(alone, opts)).get(stage.id) ?? { width: 0, height: 0 };
|
|
2086
|
+
const at = freeSpot([...boxes.values()], size, view.current?.visible() ?? null);
|
|
2087
|
+
report(addStage(pinned, stage, at), "Add stage");
|
|
2088
|
+
}
|
|
2089
|
+
return stage.id;
|
|
2090
|
+
},
|
|
2091
|
+
group: (ids) => {
|
|
2092
|
+
const { doc: d, specs: shown, look: l, onChange: report } = now.current;
|
|
2093
|
+
if (ids.length === 0) return null;
|
|
2094
|
+
const base = l === "nodeGraph" ? pinShown(ensureWireIds(d), shown) : d;
|
|
2095
|
+
report(groupStages(base, ids), "Group");
|
|
2096
|
+
return nextGroupId(d.pipeline);
|
|
2097
|
+
},
|
|
2098
|
+
ungroup: (id) => {
|
|
2099
|
+
const { doc: d, onChange: report } = now.current;
|
|
2100
|
+
if (d.pipeline.groups?.some((g) => g.id === id)) report(ungroup(d, id), "Ungroup");
|
|
2101
|
+
},
|
|
2102
|
+
renameGroup: (id) => setRenaming(id)
|
|
2103
|
+
}), [now]);
|
|
2104
|
+
const title2 = useGroupTitle(doc, look, renaming, setRenaming, onChange);
|
|
2105
|
+
const fill = (look === "nodeGraph" ? options?.theme ?? LOOK_THEME : LOOK_THEME).page;
|
|
2106
|
+
const placed = useMemo(() => diagramPages(built.data), [built.data.pages, built.data.pageLayout]);
|
|
2107
|
+
const pages = useMemo(() => placed.length > 0 ? { placed, fill } : void 0, [placed, fill]);
|
|
2108
|
+
return /* @__PURE__ */ jsx(
|
|
2109
|
+
DiagramView,
|
|
2110
|
+
{
|
|
2111
|
+
ref: view,
|
|
2112
|
+
specs: painted,
|
|
2113
|
+
width,
|
|
2114
|
+
height,
|
|
2115
|
+
minScale: EFFECT_MIN_SCALE,
|
|
2116
|
+
anchor: "start",
|
|
2117
|
+
ports: "connect",
|
|
2118
|
+
portOptions,
|
|
2119
|
+
canConnect: handlers.canConnect,
|
|
2120
|
+
onConnect: handlers.onConnect,
|
|
2121
|
+
onReconnect: handlers.onReconnect,
|
|
2122
|
+
onEditRoute: handlers.onEditRoute,
|
|
2123
|
+
onDelete: handlers.onDelete,
|
|
2124
|
+
...look === "nodeGraph" ? { onMove: handlers.onMove } : { ambient },
|
|
2125
|
+
...look === "nodeGraph" ? { onEditContainer: handlers.onEditContainer } : {},
|
|
2126
|
+
onSelect: picks.pick,
|
|
2127
|
+
...picks.shown !== void 0 ? { selected: picks.shown } : {},
|
|
2128
|
+
...pages !== void 0 ? { pages } : {},
|
|
2129
|
+
...title2 !== null ? { overlays: title2 } : {},
|
|
2130
|
+
...background !== void 0 ? { background } : {},
|
|
2131
|
+
...className !== void 0 ? { className } : {}
|
|
2132
|
+
},
|
|
2133
|
+
look
|
|
2134
|
+
);
|
|
2135
|
+
});
|
|
2136
|
+
function drawnSpecs(doc, options) {
|
|
2137
|
+
const built = editorScene(doc, "nodeGraph", options);
|
|
2138
|
+
return diagramScene(built.data, built.scene);
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
// src/effect/image/evaluators.ts
|
|
2142
|
+
var BLEND = { normal: "source-over", screen: "screen", multiply: "multiply" };
|
|
2143
|
+
function draw(width, height, paint) {
|
|
2144
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
2145
|
+
paint(canvas.getContext("2d"));
|
|
2146
|
+
return canvas.transferToImageBitmap();
|
|
2147
|
+
}
|
|
2148
|
+
function need(ctx, port) {
|
|
2149
|
+
const value = ctx.inputs[port];
|
|
2150
|
+
if (value === void 0) throw new Error(`${port} is not connected`);
|
|
2151
|
+
return value;
|
|
2152
|
+
}
|
|
2153
|
+
function decide(ctx, value) {
|
|
2154
|
+
const outs = ctx.stage.outputs ?? [];
|
|
2155
|
+
const split = ctx.stage.split;
|
|
2156
|
+
const index = split !== void 0 ? split.at.filter((cut) => value >= cut).length : value > 0 ? 0 : 1;
|
|
2157
|
+
return outs[Math.min(index, outs.length - 1)]?.id;
|
|
2158
|
+
}
|
|
2159
|
+
var imageEvaluators = {
|
|
2160
|
+
source: ({ sources }) => {
|
|
2161
|
+
if (sources.image === void 0) throw new Error("no source image");
|
|
2162
|
+
return { outputs: { out: sources.image } };
|
|
2163
|
+
},
|
|
2164
|
+
blur: (ctx) => {
|
|
2165
|
+
const image2 = need(ctx, "in");
|
|
2166
|
+
const radius = Number(ctx.params.radius ?? 0);
|
|
2167
|
+
return {
|
|
2168
|
+
outputs: {
|
|
2169
|
+
out: draw(image2.width, image2.height, (g) => {
|
|
2170
|
+
g.filter = `blur(${radius}px)`;
|
|
2171
|
+
g.drawImage(image2, 0, 0);
|
|
2172
|
+
})
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
},
|
|
2176
|
+
composite: (ctx) => {
|
|
2177
|
+
const base = need(ctx, "base");
|
|
2178
|
+
const top = need(ctx, "top");
|
|
2179
|
+
return {
|
|
2180
|
+
outputs: {
|
|
2181
|
+
out: draw(base.width, base.height, (g) => {
|
|
2182
|
+
g.drawImage(base, 0, 0);
|
|
2183
|
+
g.globalCompositeOperation = BLEND[String(ctx.params.blend)] ?? "source-over";
|
|
2184
|
+
g.drawImage(top, 0, 0, base.width, base.height);
|
|
2185
|
+
})
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
},
|
|
2189
|
+
mask: (ctx) => {
|
|
2190
|
+
const image2 = need(ctx, "in");
|
|
2191
|
+
const mask = need(ctx, "mask");
|
|
2192
|
+
return {
|
|
2193
|
+
outputs: {
|
|
2194
|
+
out: draw(image2.width, image2.height, (g) => {
|
|
2195
|
+
g.drawImage(image2, 0, 0);
|
|
2196
|
+
g.globalCompositeOperation = "destination-in";
|
|
2197
|
+
g.drawImage(mask, 0, 0, image2.width, image2.height);
|
|
2198
|
+
})
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
2201
|
+
},
|
|
2202
|
+
oscillator: ({ params, time }) => ({ outputs: { value: Math.sin(2 * Math.PI * Number(params.rate ?? 1) * time) } }),
|
|
2203
|
+
decision: (ctx) => {
|
|
2204
|
+
const tested = ctx.stage.split?.input ?? ctx.stage.inputs?.find((p) => p.type === "number")?.id ?? "test";
|
|
2205
|
+
const taken = decide(ctx, need(ctx, tested));
|
|
2206
|
+
const type = ctx.stage.outputs?.find((p) => p.id === taken)?.type;
|
|
2207
|
+
const from = ctx.stage.inputs?.find((p) => p.type === type && ctx.inputs[p.id] !== void 0);
|
|
2208
|
+
return { outputs: taken !== void 0 && from !== void 0 ? { [taken]: ctx.inputs[from.id] } : {}, ...taken !== void 0 ? { taken } : {} };
|
|
2209
|
+
},
|
|
2210
|
+
out: () => ({ outputs: {} })
|
|
2211
|
+
};
|
|
2212
|
+
|
|
2213
|
+
// src/effect/image/stageTypes.ts
|
|
2214
|
+
var param = (label, control) => ({ label, value: formatParam(control), control });
|
|
2215
|
+
var image = (id, label) => ({ id, type: "image", ...label !== void 0 ? { label } : {} });
|
|
2216
|
+
var IMAGE_STAGE_TYPES = [
|
|
2217
|
+
{ type: "source", title: "Source", kind: "source", outputs: [image("out")] },
|
|
2218
|
+
{
|
|
2219
|
+
type: "blur",
|
|
2220
|
+
title: "Blur",
|
|
2221
|
+
inputs: [image("in")],
|
|
2222
|
+
outputs: [image("out")],
|
|
2223
|
+
params: [param("radius", { kind: "number", value: 4, min: 0, max: 50, step: 1, unit: "px" })]
|
|
2224
|
+
},
|
|
2225
|
+
{
|
|
2226
|
+
type: "composite",
|
|
2227
|
+
title: "Composite",
|
|
2228
|
+
inputs: [image("base"), image("top")],
|
|
2229
|
+
outputs: [image("out")],
|
|
2230
|
+
params: [param("blend", {
|
|
2231
|
+
kind: "enum",
|
|
2232
|
+
value: "normal",
|
|
2233
|
+
options: [{ value: "normal", label: "Normal" }, { value: "screen", label: "Screen" }, { value: "multiply", label: "Multiply" }]
|
|
2234
|
+
})]
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
type: "mask",
|
|
2238
|
+
title: "Mask",
|
|
2239
|
+
inputs: [image("in"), { id: "mask", type: "mask" }],
|
|
2240
|
+
outputs: [image("out")]
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
type: "oscillator",
|
|
2244
|
+
title: "Oscillator",
|
|
2245
|
+
outputs: [{ id: "value", type: "number" }],
|
|
2246
|
+
params: [param("rate", { kind: "number", value: 1, min: 0.1, max: 20, step: 0.1, unit: "Hz" })]
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
type: "decision",
|
|
2250
|
+
title: "Decision",
|
|
2251
|
+
kind: "decision",
|
|
2252
|
+
inputs: [image("in"), { id: "test", type: "number" }],
|
|
2253
|
+
outputs: [image("yes", "yes"), image("no", "no")]
|
|
2254
|
+
},
|
|
2255
|
+
{ type: "out", title: "Out", kind: "sink", inputs: [image("in")] }
|
|
2256
|
+
];
|
|
2257
|
+
|
|
2258
|
+
// src/effect/image/testCard.ts
|
|
2259
|
+
var BARS = ["#c0c0c0", "#c0c000", "#00c0c0", "#00c000", "#c000c0", "#c00000", "#0000c0"];
|
|
2260
|
+
function testCard(size) {
|
|
2261
|
+
const { width, height } = typeof size === "number" ? { width: size, height: size } : size;
|
|
2262
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
2263
|
+
const g = canvas.getContext("2d");
|
|
2264
|
+
const barHeight = height * 0.7;
|
|
2265
|
+
BARS.forEach((color, i) => {
|
|
2266
|
+
g.fillStyle = color;
|
|
2267
|
+
const x = Math.floor(i * width / BARS.length);
|
|
2268
|
+
g.fillRect(x, 0, Math.ceil(width / BARS.length) + 1, barHeight);
|
|
2269
|
+
});
|
|
2270
|
+
const ramp = g.createLinearGradient(0, 0, width, 0);
|
|
2271
|
+
ramp.addColorStop(0, "#000");
|
|
2272
|
+
ramp.addColorStop(1, "#fff");
|
|
2273
|
+
g.fillStyle = ramp;
|
|
2274
|
+
g.fillRect(0, barHeight, width, height - barHeight);
|
|
2275
|
+
g.strokeStyle = "#fff";
|
|
2276
|
+
g.lineWidth = Math.max(1, Math.min(width, height) / 32);
|
|
2277
|
+
g.beginPath();
|
|
2278
|
+
g.arc(width / 2, height / 2, Math.min(width, height) * 0.3, 0, Math.PI * 2);
|
|
2279
|
+
g.stroke();
|
|
2280
|
+
return canvas.transferToImageBitmap();
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
// src/effect/image/valueKinds.ts
|
|
2284
|
+
var bitmapKind = {
|
|
2285
|
+
show: (value) => `${value.width}\xD7${value.height}`,
|
|
2286
|
+
preview(value, size) {
|
|
2287
|
+
const width = Math.max(1, Math.round(size.width * PREVIEW_DENSITY));
|
|
2288
|
+
const height = Math.max(1, Math.round(size.height * PREVIEW_DENSITY));
|
|
2289
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
2290
|
+
canvas.getContext("2d").drawImage(value, 0, 0, width, height);
|
|
2291
|
+
return canvas.transferToImageBitmap();
|
|
2292
|
+
},
|
|
2293
|
+
dispose: (value) => value.close()
|
|
2294
|
+
};
|
|
2295
|
+
var imageValueKinds = {
|
|
2296
|
+
image: bitmapKind,
|
|
2297
|
+
mask: bitmapKind,
|
|
2298
|
+
number: { show: (value) => value.toFixed(3) }
|
|
2299
|
+
};
|
|
2300
|
+
|
|
1278
2301
|
// src/effect/looks.ts
|
|
1279
2302
|
var EFFECT_LOOKS = Object.freeze({ nodeGraph, rack, lanes, compact });
|
|
1280
2303
|
|
|
1281
|
-
|
|
2304
|
+
// src/effect/runner/pass.ts
|
|
2305
|
+
function createPass(pipeline, opts) {
|
|
2306
|
+
const stages = new Map(uniqueStages(pipeline).map((s) => [s.id, s]));
|
|
2307
|
+
const queue = runOrder(pipeline).order;
|
|
2308
|
+
const made = /* @__PURE__ */ new Map();
|
|
2309
|
+
const sources = opts.sources ?? {};
|
|
2310
|
+
const size = typeof opts.previewSize === "object" ? opts.previewSize : { width: opts.previewSize ?? PREVIEW_SIZE, height: opts.previewSize ?? PREVIEW_SIZE };
|
|
2311
|
+
const signal = opts.signal ?? new AbortController().signal;
|
|
2312
|
+
let run = { stages: {} };
|
|
2313
|
+
const inputsOf = (stage) => {
|
|
2314
|
+
const inputs2 = {};
|
|
2315
|
+
for (const w of pipeline.wires) {
|
|
2316
|
+
if (w.to.stage !== stage.id || inputs2[w.to.port] !== void 0) continue;
|
|
2317
|
+
const value = made.get(w.from.stage)?.[w.from.port];
|
|
2318
|
+
if (value !== void 0) inputs2[w.to.port] = value;
|
|
2319
|
+
}
|
|
2320
|
+
return inputs2;
|
|
2321
|
+
};
|
|
2322
|
+
const record = async (stage, inputs2, outputs, entry) => {
|
|
2323
|
+
made.set(stage.id, outputs);
|
|
2324
|
+
const values = {};
|
|
2325
|
+
const previews = {};
|
|
2326
|
+
const seen = { ...inputs2, ...outputs };
|
|
2327
|
+
for (const port of shownPorts(stage)) {
|
|
2328
|
+
const value = seen[port.id];
|
|
2329
|
+
if (value === void 0) continue;
|
|
2330
|
+
const kind = opts.kinds?.[port.type];
|
|
2331
|
+
const text = kind?.show !== void 0 ? kind.show(value) : plainText(value);
|
|
2332
|
+
if (text !== void 0) values[port.id] = text;
|
|
2333
|
+
const preview = opts.previews === false ? void 0 : await kind?.preview?.(value, size);
|
|
2334
|
+
if (preview !== void 0) previews[port.id] = preview;
|
|
2335
|
+
}
|
|
2336
|
+
const full = {
|
|
2337
|
+
...entry,
|
|
2338
|
+
...Object.keys(values).length > 0 ? { values } : {},
|
|
2339
|
+
...Object.keys(previews).length > 0 ? { previews } : {}
|
|
2340
|
+
};
|
|
2341
|
+
run = { stages: { ...run.stages, [stage.id]: full } };
|
|
2342
|
+
};
|
|
2343
|
+
const evaluate = async (stage, inputs2) => {
|
|
2344
|
+
if (stage.bypassed === true) return record(stage, inputs2, passThrough(stage, inputs2), {});
|
|
2345
|
+
const evaluator = stage.type === void 0 ? void 0 : opts.evaluators[stage.type];
|
|
2346
|
+
const params = paramValues(stage);
|
|
2347
|
+
const start = performance.now();
|
|
2348
|
+
try {
|
|
2349
|
+
if (evaluator === void 0) throw new Error(`no evaluator for type "${stage.type ?? ""}"`);
|
|
2350
|
+
const result = await evaluator({ stage, inputs: inputs2, params, sources, time: opts.time ?? 0, signal });
|
|
2351
|
+
const cost = performance.now() - start;
|
|
2352
|
+
return record(stage, inputs2, result.outputs, { cost, ...result.taken !== void 0 ? { taken: result.taken } : {} });
|
|
2353
|
+
} catch (err) {
|
|
2354
|
+
signal.throwIfAborted();
|
|
2355
|
+
const cost = performance.now() - start;
|
|
2356
|
+
run = { stages: { ...run.stages, [stage.id]: { cost, error: err instanceof Error ? err.message : String(err) } } };
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
return {
|
|
2360
|
+
get run() {
|
|
2361
|
+
return run;
|
|
2362
|
+
},
|
|
2363
|
+
async step() {
|
|
2364
|
+
while (queue.length > 0) {
|
|
2365
|
+
signal.throwIfAborted();
|
|
2366
|
+
const stage = stages.get(queue.shift());
|
|
2367
|
+
const inputs2 = inputsOf(stage);
|
|
2368
|
+
if ((stage.inputs?.length ?? 0) > 0 && Object.keys(inputs2).length === 0) continue;
|
|
2369
|
+
await evaluate(stage, inputs2);
|
|
2370
|
+
signal.throwIfAborted();
|
|
2371
|
+
return stage.id;
|
|
2372
|
+
}
|
|
2373
|
+
return void 0;
|
|
2374
|
+
},
|
|
2375
|
+
dispose() {
|
|
2376
|
+
const given = new Set(Object.values(sources));
|
|
2377
|
+
const done = /* @__PURE__ */ new Set();
|
|
2378
|
+
for (const [id, outputs] of made) {
|
|
2379
|
+
for (const port of stages.get(id)?.outputs ?? []) {
|
|
2380
|
+
const value = outputs[port.id];
|
|
2381
|
+
if (value === void 0 || given.has(value) || done.has(value)) continue;
|
|
2382
|
+
done.add(value);
|
|
2383
|
+
opts.kinds?.[port.type]?.dispose?.(value);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
made.clear();
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2390
|
+
function passThrough(stage, inputs2) {
|
|
2391
|
+
const out = {};
|
|
2392
|
+
for (const port of stage.outputs ?? []) {
|
|
2393
|
+
const from = (stage.inputs ?? []).find((i) => i.type === port.type && inputs2[i.id] !== void 0);
|
|
2394
|
+
if (from !== void 0) out[port.id] = inputs2[from.id];
|
|
2395
|
+
}
|
|
2396
|
+
return out;
|
|
2397
|
+
}
|
|
2398
|
+
function plainText(value) {
|
|
2399
|
+
return typeof value === "number" || typeof value === "string" || typeof value === "boolean" ? String(value) : void 0;
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
// src/effect/runner/runPipeline.ts
|
|
2403
|
+
async function runPipeline(pipeline, opts) {
|
|
2404
|
+
const pass = createPass(pipeline, opts);
|
|
2405
|
+
try {
|
|
2406
|
+
while (await pass.step() !== void 0) ;
|
|
2407
|
+
return pass.run;
|
|
2408
|
+
} finally {
|
|
2409
|
+
pass.dispose();
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
// src/effect/runner/runner.ts
|
|
2414
|
+
function createEffectRunner(opts) {
|
|
2415
|
+
let { pipeline, ...options } = opts;
|
|
2416
|
+
let state = { run: void 0, playing: false, time: 0 };
|
|
2417
|
+
let stepping = null;
|
|
2418
|
+
let controller = new AbortController();
|
|
2419
|
+
let tail = Promise.resolve();
|
|
2420
|
+
let pending = 0;
|
|
2421
|
+
let published = [];
|
|
2422
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2423
|
+
const set = (next) => {
|
|
2424
|
+
state = { ...state, ...next };
|
|
2425
|
+
for (const l of [...listeners]) l();
|
|
2426
|
+
};
|
|
2427
|
+
const publish = (run) => {
|
|
2428
|
+
published.push(run);
|
|
2429
|
+
set({ run });
|
|
2430
|
+
};
|
|
2431
|
+
const passOptions = (time) => ({ ...options, time, signal: controller.signal });
|
|
2432
|
+
const dropStepping = () => {
|
|
2433
|
+
stepping?.dispose();
|
|
2434
|
+
stepping = null;
|
|
2435
|
+
};
|
|
2436
|
+
const enqueue = (task) => {
|
|
2437
|
+
const signal = controller.signal;
|
|
2438
|
+
pending++;
|
|
2439
|
+
const done = tail.then(() => signal.aborted ? void 0 : task(signal)).catch((err) => {
|
|
2440
|
+
if (!signal.aborted) throw err;
|
|
2441
|
+
}).finally(() => {
|
|
2442
|
+
pending--;
|
|
2443
|
+
});
|
|
2444
|
+
tail = done.catch(() => {
|
|
2445
|
+
});
|
|
2446
|
+
return done;
|
|
2447
|
+
};
|
|
2448
|
+
const wholeRun = (time) => async (signal) => {
|
|
2449
|
+
dropStepping();
|
|
2450
|
+
const run = await runPipeline(pipeline, passOptions(time));
|
|
2451
|
+
if (signal.aborted) closeBitmaps(previewsOf(run));
|
|
2452
|
+
else publish(run);
|
|
2453
|
+
};
|
|
2454
|
+
const stepOnce = (time) => async () => {
|
|
2455
|
+
let pass = stepping ??= createPass(pipeline, passOptions(time));
|
|
2456
|
+
let at = await pass.step();
|
|
2457
|
+
if (at === void 0) {
|
|
2458
|
+
dropStepping();
|
|
2459
|
+
pass = stepping = createPass(pipeline, passOptions(time));
|
|
2460
|
+
at = await pass.step();
|
|
2461
|
+
}
|
|
2462
|
+
publish(at === void 0 ? pass.run : { ...pass.run, at });
|
|
2463
|
+
};
|
|
2464
|
+
return {
|
|
2465
|
+
get state() {
|
|
2466
|
+
return state;
|
|
2467
|
+
},
|
|
2468
|
+
run: () => enqueue(wholeRun(state.time)),
|
|
2469
|
+
step() {
|
|
2470
|
+
return enqueue(stepOnce(state.time));
|
|
2471
|
+
},
|
|
2472
|
+
play: () => {
|
|
2473
|
+
if (!state.playing) set({ playing: true });
|
|
2474
|
+
},
|
|
2475
|
+
pause: () => {
|
|
2476
|
+
if (state.playing) set({ playing: false });
|
|
2477
|
+
},
|
|
2478
|
+
reset() {
|
|
2479
|
+
controller.abort();
|
|
2480
|
+
controller = new AbortController();
|
|
2481
|
+
dropStepping();
|
|
2482
|
+
set({ run: void 0, playing: false, time: 0 });
|
|
2483
|
+
},
|
|
2484
|
+
advance(seconds) {
|
|
2485
|
+
if (!state.playing) return;
|
|
2486
|
+
state = { ...state, time: state.time + seconds };
|
|
2487
|
+
if (pending === 0) void enqueue(wholeRun(state.time));
|
|
2488
|
+
},
|
|
2489
|
+
setPipeline(next) {
|
|
2490
|
+
if (next === pipeline) return;
|
|
2491
|
+
pipeline = next;
|
|
2492
|
+
dropStepping();
|
|
2493
|
+
},
|
|
2494
|
+
setSources(sources) {
|
|
2495
|
+
if (sources === options.sources) return;
|
|
2496
|
+
options = { ...options, sources };
|
|
2497
|
+
dropStepping();
|
|
2498
|
+
},
|
|
2499
|
+
setOptions(next) {
|
|
2500
|
+
options = { ...options, ...next };
|
|
2501
|
+
},
|
|
2502
|
+
drawn(run) {
|
|
2503
|
+
const at = run === void 0 ? published.length : published.indexOf(run);
|
|
2504
|
+
if (at <= 0) return;
|
|
2505
|
+
const keep = new Set(run === void 0 ? [] : previewsOf(run));
|
|
2506
|
+
closeBitmaps(published.slice(0, at).flatMap(previewsOf).filter((b) => !keep.has(b)));
|
|
2507
|
+
published = published.slice(at);
|
|
2508
|
+
},
|
|
2509
|
+
subscribe(listener) {
|
|
2510
|
+
listeners.add(listener);
|
|
2511
|
+
return () => {
|
|
2512
|
+
listeners.delete(listener);
|
|
2513
|
+
};
|
|
2514
|
+
},
|
|
2515
|
+
dispose() {
|
|
2516
|
+
this.reset();
|
|
2517
|
+
closeBitmaps(published.flatMap(previewsOf));
|
|
2518
|
+
published = [];
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
function useEffectRunner(opts) {
|
|
2523
|
+
const { pipeline, sources, evaluators, kinds, previews, previewSize, target, requestFrame, cancelFrame } = opts;
|
|
2524
|
+
const [runner] = useState(() => createEffectRunner(opts));
|
|
2525
|
+
useEffect(() => {
|
|
2526
|
+
runner.setPipeline(pipeline);
|
|
2527
|
+
}, [runner, pipeline]);
|
|
2528
|
+
useEffect(() => {
|
|
2529
|
+
if (sources !== void 0) runner.setSources(sources);
|
|
2530
|
+
}, [runner, sources]);
|
|
2531
|
+
useEffect(() => {
|
|
2532
|
+
runner.setOptions({
|
|
2533
|
+
evaluators,
|
|
2534
|
+
...kinds !== void 0 ? { kinds } : {},
|
|
2535
|
+
...previews !== void 0 ? { previews } : {},
|
|
2536
|
+
...previewSize !== void 0 ? { previewSize } : {}
|
|
2537
|
+
});
|
|
2538
|
+
}, [runner, evaluators, kinds, previews, previewSize]);
|
|
2539
|
+
const state = useSyncExternalStore(runner.subscribe, () => runner.state);
|
|
2540
|
+
const last = useRef(null);
|
|
2541
|
+
const loop = useVisibleRaf((now, self) => {
|
|
2542
|
+
if (!runner.state.playing) return;
|
|
2543
|
+
runner.advance(last.current === null ? 0 : (now - last.current) / 1e3);
|
|
2544
|
+
last.current = now;
|
|
2545
|
+
self.request();
|
|
2546
|
+
}, {
|
|
2547
|
+
...target !== void 0 ? { target } : {},
|
|
2548
|
+
...requestFrame !== void 0 ? { requestFrame } : {},
|
|
2549
|
+
...cancelFrame !== void 0 ? { cancelFrame } : {},
|
|
2550
|
+
onResume: () => {
|
|
2551
|
+
last.current = null;
|
|
2552
|
+
}
|
|
2553
|
+
});
|
|
2554
|
+
useEffect(() => {
|
|
2555
|
+
last.current = null;
|
|
2556
|
+
if (state.playing) loop.request();
|
|
2557
|
+
else loop.cancel();
|
|
2558
|
+
}, [state.playing, loop]);
|
|
2559
|
+
useEffect(() => {
|
|
2560
|
+
runner.drawn(state.run);
|
|
2561
|
+
}, [runner, state.run]);
|
|
2562
|
+
useEffect(() => () => runner.dispose(), [runner]);
|
|
2563
|
+
return { ...state, runner };
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
export { AT_COLOR, BRANCH_SYMBOLS, COMPACT_PADDING, EFFECT_LOOKS, EFFECT_MIN_SCALE, EffectChart, EffectEditor, IMAGE_STAGE_TYPES, LANE_INSET, REORDER_LANES_ACTION_ID, addStage, addWire, applyLookRoutes, branchNode, canMoveInChain, canWire, chainLinks, compact, costText, createEffectRunner, createPass, editorScene, ensureWireIds, groupContents, groupStages, imageEvaluators, imageValueKinds, laneId, laneTypeOf, lanes, mainChain, moveGroup, moveInChain, moveStages, moveToGroup, nextGroupId, nextStageId, nextWireId, nodeGraph, paintRun, pruneLookRoutes, rack, reconnectWire, removeStages, removeWires, renameGroup, runPipeline, setGroupBox, setLaneOrder, setLookRoute, setParam, setWireRoute, symbolNameFor, testCard, ungroup, useEffectRunner, wireEnds };
|
|
1282
2567
|
//# sourceMappingURL=effect.js.map
|
|
1283
2568
|
//# sourceMappingURL=effect.js.map
|