@weasel-js/diagram 1.11.0 → 1.11.1
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-43LDZ6L4.js +2053 -0
- package/dist/chunk-43LDZ6L4.js.map +1 -0
- package/dist/chunk-CI3I3I6T.js +3600 -0
- package/dist/chunk-CI3I3I6T.js.map +1 -0
- package/dist/{chunk-NUMMW5BI.js → chunk-IM6DHWZO.js} +388 -33
- package/dist/chunk-IM6DHWZO.js.map +1 -0
- package/dist/chunk-QKSWZEJH.js +312 -0
- package/dist/chunk-QKSWZEJH.js.map +1 -0
- package/dist/chunk-T6DQT553.js +1431 -0
- package/dist/chunk-T6DQT553.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-B-iEwKZp.d.ts} +229 -55
- package/dist/effect-audio.d.ts +138 -0
- package/dist/effect-audio.js +527 -0
- package/dist/effect-audio.js.map +1 -0
- package/dist/effect.d.ts +486 -121
- package/dist/effect.js +1599 -340
- 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 +2149 -88
- package/dist/index.js +1056 -2
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +3 -3
- package/dist/{layout.entry-BoWFcR4G.d.ts → layout.entry-CH9gc0CY.d.ts} +230 -3
- package/dist/layout.js +3 -1
- package/dist/stageTypes-DvnR8cKQ.d.ts +189 -0
- package/dist/{body-2lC7IiIA.d.ts → types-BbHunLXm.d.ts} +147 -7
- 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/effect.js
CHANGED
|
@@ -1,200 +1,398 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { DiagramView, reorderContribution, ANYWHERE, isBodyEnd, sequenceSlots } from './chunk-CI3I3I6T.js';
|
|
2
|
+
import './chunk-T6DQT553.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-QKSWZEJH.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-QKSWZEJH.js';
|
|
5
|
+
import { diagramScene, diagramPages, edgeIdOf, titleIdOf, groupLabelIdOf, nestGroups, LABEL_PLACEMENT, groupMinWidth, EDGE_DERIVE_PATH } from './chunk-43LDZ6L4.js';
|
|
6
|
+
import { portOf, DEFAULT_GROUP_INSET, SIDE_TRACK_GAP, widenForMinWidth, portSitesOf, labelCenter, insetOf, rankGaps } from './chunk-IM6DHWZO.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,11 +1381,11 @@ 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;
|
|
@@ -910,10 +1432,10 @@ function routeWires(wires, nodes, laid) {
|
|
|
910
1432
|
const label = spec.get(`${edgeIdOf(edge)}/label`)?.pose;
|
|
911
1433
|
const others = keepOut.concat(segments.flatMap((boxes, k) => k === w ? [] : boxes));
|
|
912
1434
|
const offset = LABEL_PLACEMENT.offset ?? 0;
|
|
913
|
-
const
|
|
914
|
-
if (
|
|
915
|
-
keepOut.push(
|
|
916
|
-
return { edge: { ...routed, labelPlacement: { at: { near:
|
|
1435
|
+
const clear2 = path === null || label === void 0 ? void 0 : clearLabel(path, label, offset, others) ?? clearLabel(path, label, offset, keepOut);
|
|
1436
|
+
if (clear2 === void 0) return { edge: routed, style };
|
|
1437
|
+
keepOut.push(clear2.box);
|
|
1438
|
+
return { edge: { ...routed, labelPlacement: { at: { near: clear2.near }, offset: clear2.offset } }, style };
|
|
917
1439
|
});
|
|
918
1440
|
return { edges, tracks };
|
|
919
1441
|
}
|
|
@@ -921,11 +1443,8 @@ function routeWires(wires, nodes, laid) {
|
|
|
921
1443
|
// src/effect/lanes.ts
|
|
922
1444
|
var LANE_INSET = 6;
|
|
923
1445
|
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
1446
|
var laneId = (type) => `~lane:${type}`;
|
|
1447
|
+
var laneTypeOf = (id) => id.startsWith("~lane:") ? id.slice("~lane:".length) : void 0;
|
|
929
1448
|
function laneTypes(stages, first = []) {
|
|
930
1449
|
const used = [...new Set(stages.flatMap((s) => [...s.inputs ?? [], ...s.outputs ?? []].map((p) => p.type)))];
|
|
931
1450
|
const lead = [...new Set(first)].filter((t) => used.includes(t));
|
|
@@ -935,23 +1454,6 @@ function spanOf(stage, lane) {
|
|
|
935
1454
|
const at = [...stage.inputs ?? [], ...stage.outputs ?? []].map((p) => lane.get(p.type));
|
|
936
1455
|
return at.length === 0 ? [0, 0] : [Math.min(...at), Math.max(...at)];
|
|
937
1456
|
}
|
|
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
1457
|
function laneHeights(count, least, needs, insets) {
|
|
956
1458
|
const heights = Array.from({ length: count }, () => least);
|
|
957
1459
|
for (const { span: [lo, hi], height } of [...needs].sort((a, b) => a.span[1] - a.span[0] - (b.span[1] - b.span[0]))) {
|
|
@@ -978,10 +1480,14 @@ function controlSideOf(stage, pipeline, lane) {
|
|
|
978
1480
|
const signal = ports.filter((p) => !isControl(pipeline, p.type));
|
|
979
1481
|
return control.length > 0 && signal.length > 0 && mean(control) > mean(signal) ? "bottom" : "top";
|
|
980
1482
|
}
|
|
981
|
-
function sizesOf(nodes, measure) {
|
|
982
|
-
const specs = diagramScene({ nodes, edges: [] }, { layout: "none", ...measure !== void 0 ? { measure } : {} });
|
|
1483
|
+
function sizesOf(nodes, groups, measure) {
|
|
1484
|
+
const specs = diagramScene({ nodes, edges: [], groups }, { layout: "none", ...measure !== void 0 ? { measure } : {} });
|
|
983
1485
|
const pose = new Map(specs.map((s) => [s.id, s.pose]));
|
|
984
|
-
|
|
1486
|
+
const titles = new Map(groups.flatMap((g) => {
|
|
1487
|
+
const title2 = pose.get(groupLabelIdOf(g.id));
|
|
1488
|
+
return title2 === void 0 ? [] : [[g.id, title2.width]];
|
|
1489
|
+
}));
|
|
1490
|
+
const size = new Map(nodes.map((n) => {
|
|
985
1491
|
const box = pose.get(n.id);
|
|
986
1492
|
const ink = inkOf(n.id, specs);
|
|
987
1493
|
const over = {
|
|
@@ -992,6 +1498,7 @@ function sizesOf(nodes, measure) {
|
|
|
992
1498
|
};
|
|
993
1499
|
return [n.id, { box, over }];
|
|
994
1500
|
}));
|
|
1501
|
+
return { size, titles };
|
|
995
1502
|
}
|
|
996
1503
|
function laneNode(type, box, color) {
|
|
997
1504
|
return {
|
|
@@ -1010,7 +1517,7 @@ function insetsFor(edges, tracks) {
|
|
|
1010
1517
|
return LANE_INSET + (k === 0 || k === edges - 1 ? n * TRACK_GAP : Math.max(0, n - 1) * TRACK_GAP / 2);
|
|
1011
1518
|
});
|
|
1012
1519
|
}
|
|
1013
|
-
function
|
|
1520
|
+
function place2(plan, insets) {
|
|
1014
1521
|
const { stages, size, spans, column } = plan;
|
|
1015
1522
|
const inkHeight = (id) => size.get(id).box.height + size.get(id).over.top + size.get(id).over.bottom;
|
|
1016
1523
|
const heights = laneHeights(plan.rows, plan.least, stages.map((s) => ({ span: spans.get(s.id), height: inkHeight(s.id) })), insets);
|
|
@@ -1029,11 +1536,13 @@ function place(plan, insets) {
|
|
|
1029
1536
|
const inkWidth = (id) => stood.get(id).width + stood.get(id).over.left + stood.get(id).over.right;
|
|
1030
1537
|
const widths = [];
|
|
1031
1538
|
for (const s of stages) widths[column.get(s.id)] = Math.max(widths[column.get(s.id)] ?? 0, inkWidth(s.id));
|
|
1032
|
-
const
|
|
1539
|
+
const offset = (id) => (widths[column.get(id)] - inkWidth(id)) / 2 + stood.get(id).over.left;
|
|
1540
|
+
const extent = (id) => ({ lo: offset(id), hi: offset(id) + stood.get(id).width });
|
|
1541
|
+
const { lefts, end } = columnLefts(widths, column, plan.blocks, plan.gap, plan.start, extent);
|
|
1033
1542
|
const nodes = stages.map((stage, k) => {
|
|
1034
1543
|
const c = column.get(stage.id);
|
|
1035
|
-
const { y, height, width
|
|
1036
|
-
const box = { x: lefts[c] + (
|
|
1544
|
+
const { y, height, width } = stood.get(stage.id);
|
|
1545
|
+
const box = { x: lefts[c] + offset(stage.id), y, width, height };
|
|
1037
1546
|
return {
|
|
1038
1547
|
...plan.drawn[k],
|
|
1039
1548
|
at: { x: box.x, y: box.y },
|
|
@@ -1044,25 +1553,28 @@ function place(plan, insets) {
|
|
|
1044
1553
|
});
|
|
1045
1554
|
const columns = Array.from(widths, (wd, c) => [lefts[c], lefts[c] + (wd ?? 0)]);
|
|
1046
1555
|
const edges = [...tops, tops.length === 0 ? 0 : tops[tops.length - 1] + heights[heights.length - 1]];
|
|
1047
|
-
return { nodes, tops, heights, columns, edges };
|
|
1556
|
+
return { nodes, tops, heights, columns, edges, end };
|
|
1048
1557
|
}
|
|
1049
1558
|
var lanes = (pipeline, options = {}) => {
|
|
1050
|
-
const
|
|
1559
|
+
const theme = options.theme ?? LOOK_THEME;
|
|
1560
|
+
const color = typeColors(pipeline, options.colors, theme.series);
|
|
1051
1561
|
const stages = uniqueStages(pipeline);
|
|
1052
1562
|
const types = laneTypes(stages, options.lanes);
|
|
1053
1563
|
const rows = types.length > 0 ? types : [""];
|
|
1054
1564
|
const lane = new Map(types.map((t, i) => [t, i]));
|
|
1055
1565
|
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 }));
|
|
1566
|
+
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
1567
|
const labels = rows.map((t) => ({ id: laneId(t), lines: [t] }));
|
|
1058
|
-
const
|
|
1059
|
-
const
|
|
1568
|
+
const groups = uniqueGroups(pipeline);
|
|
1569
|
+
const { size, titles } = sizesOf([...drawn, ...labels], groups, options.measure);
|
|
1570
|
+
const { column, blocks } = laneColumns(stages, drawableWires(pipeline), spans, groups, titles);
|
|
1060
1571
|
const plan = {
|
|
1061
1572
|
stages,
|
|
1062
1573
|
drawn,
|
|
1063
1574
|
size,
|
|
1064
1575
|
spans,
|
|
1065
1576
|
column,
|
|
1577
|
+
blocks,
|
|
1066
1578
|
lane,
|
|
1067
1579
|
rows: rows.length,
|
|
1068
1580
|
least: Math.max(options.laneHeight ?? 48, ...labels.map((l) => size.get(l.id).box.height)),
|
|
@@ -1072,23 +1584,24 @@ var lanes = (pipeline, options = {}) => {
|
|
|
1072
1584
|
const router = options.router ?? "bezier";
|
|
1073
1585
|
const wires = wireEdges(pipeline, color);
|
|
1074
1586
|
let insets = insetsFor(rows.length + 1, /* @__PURE__ */ new Map());
|
|
1075
|
-
let laid =
|
|
1587
|
+
let laid = place2(plan, insets);
|
|
1076
1588
|
let routed = routeWires(wires, laid.nodes, { grid: laid, column, router, measure: options.measure });
|
|
1077
1589
|
for (let round = 0; round < 4; round++) {
|
|
1078
|
-
const
|
|
1079
|
-
if (
|
|
1080
|
-
insets =
|
|
1081
|
-
laid =
|
|
1590
|
+
const need2 = insetsFor(rows.length + 1, routed.tracks);
|
|
1591
|
+
if (need2.every((n, k) => n <= insets[k])) break;
|
|
1592
|
+
insets = need2.map((n, k) => Math.max(n, insets[k]));
|
|
1593
|
+
laid = place2(plan, insets);
|
|
1082
1594
|
routed = routeWires(wires, laid.nodes, { grid: laid, column, router, measure: options.measure });
|
|
1083
1595
|
}
|
|
1084
1596
|
const { columns, tops, heights } = laid;
|
|
1085
|
-
const right = columns.length === 0 ? plan.start :
|
|
1597
|
+
const right = columns.length === 0 ? plan.start : laid.end + MARGIN;
|
|
1086
1598
|
const laneNodes = rows.map((t, i) => laneNode(t, { x: 0, y: tops[i], width: right, height: heights[i] }, color));
|
|
1087
1599
|
const edges = routed.edges;
|
|
1088
|
-
const styles = sceneStyles(stages, edges);
|
|
1089
|
-
const
|
|
1600
|
+
const styles = sceneStyles(stages, edges, theme);
|
|
1601
|
+
const bodies = theme.lane.bodies;
|
|
1602
|
+
const laneStyle = new Map(rows.map((t, i) => [laneId(t), bodies[i % bodies.length]]));
|
|
1090
1603
|
return {
|
|
1091
|
-
data: { nodes: [...laneNodes, ...laid.nodes], edges: edges.map((e) => e.edge), groups
|
|
1604
|
+
data: { nodes: [...laneNodes, ...laid.nodes], edges: edges.map((e) => e.edge), groups },
|
|
1092
1605
|
scene: {
|
|
1093
1606
|
layout: "none",
|
|
1094
1607
|
router,
|
|
@@ -1096,61 +1609,17 @@ var lanes = (pipeline, options = {}) => {
|
|
|
1096
1609
|
...styles,
|
|
1097
1610
|
nodeStyle: (n) => laneStyle.get(n.id) ?? styles.nodeStyle(n)
|
|
1098
1611
|
},
|
|
1099
|
-
color
|
|
1612
|
+
color,
|
|
1613
|
+
order: { axis: "x", items: mainChain(pipeline).map((id) => ({ id })) },
|
|
1614
|
+
...types.length > 0 ? {
|
|
1615
|
+
laneOrder: {
|
|
1616
|
+
axis: "y",
|
|
1617
|
+
items: types.map((t, i) => ({ id: laneId(t), grip: { x: 0, y: tops[i], width: plan.start, height: heights[i] } }))
|
|
1618
|
+
}
|
|
1619
|
+
} : {}
|
|
1100
1620
|
};
|
|
1101
1621
|
};
|
|
1102
1622
|
|
|
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
1623
|
// src/effect/rack.ts
|
|
1155
1624
|
var SKIP_OUT = "~skip-out";
|
|
1156
1625
|
var SKIP_IN = "~skip-in";
|
|
@@ -1164,7 +1633,14 @@ function rackWires(pipeline, skips) {
|
|
|
1164
1633
|
const eaten = new Set(skips.flatMap((s) => [s.into.index, s.through.index, s.skip.index]));
|
|
1165
1634
|
const wires = drawableWires(pipeline).filter((d) => !eaten.has(d.index)).map((d) => {
|
|
1166
1635
|
const label = exitLabel(d);
|
|
1167
|
-
return {
|
|
1636
|
+
return {
|
|
1637
|
+
...d.wire.id !== void 0 ? { id: d.wire.id } : {},
|
|
1638
|
+
from: d.wire.from,
|
|
1639
|
+
to: d.wire.to,
|
|
1640
|
+
type: d.type,
|
|
1641
|
+
problem: d.problem,
|
|
1642
|
+
...label !== void 0 ? { label } : {}
|
|
1643
|
+
};
|
|
1168
1644
|
});
|
|
1169
1645
|
for (const s of skips) {
|
|
1170
1646
|
wires.push({
|
|
@@ -1186,22 +1662,12 @@ function rackWires(pipeline, skips) {
|
|
|
1186
1662
|
}
|
|
1187
1663
|
return wires;
|
|
1188
1664
|
}
|
|
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 };
|
|
1665
|
+
function lineOf(pipeline, ordered, chain) {
|
|
1666
|
+
const { mainIn, mainOut } = chainLinks(pipeline, ordered);
|
|
1667
|
+
return { at: new Map(chain.map((id, i) => [id, i])), mainIn, mainOut };
|
|
1202
1668
|
}
|
|
1203
|
-
function
|
|
1204
|
-
if (!line.at.has(stage.id)) return [...spreadAt(stage.outputs ?? [], far), ...spreadAt(stage.inputs ?? [],
|
|
1669
|
+
function portsOf2(stage, line, wires, near2, far) {
|
|
1670
|
+
if (!line.at.has(stage.id)) return [...spreadAt(stage.outputs ?? [], far), ...spreadAt(stage.inputs ?? [], near2)];
|
|
1205
1671
|
const typed = (p, anchor) => ({ id: p.id, type: p.type, at: anchor });
|
|
1206
1672
|
const out = [];
|
|
1207
1673
|
const side = [];
|
|
@@ -1213,7 +1679,7 @@ function portsOf(stage, line, wires, near, far) {
|
|
|
1213
1679
|
if (p.id === line.mainOut.get(stage.id)) out.push(typed(p, { u: 1, v: 0.5 }));
|
|
1214
1680
|
else side.push(p);
|
|
1215
1681
|
}
|
|
1216
|
-
out.push(...spreadAt(side,
|
|
1682
|
+
out.push(...spreadAt(side, near2));
|
|
1217
1683
|
const type = portOn(stage, line.mainOut.get(stage.id) ?? line.mainIn.get(stage.id) ?? "")?.type;
|
|
1218
1684
|
const arc = (id, u) => ({ id, at: { u, v: far }, ...type !== void 0 ? { type } : {} });
|
|
1219
1685
|
if (wires.some((w) => w.arc && w.from.stage === stage.id)) out.push(arc(SKIP_OUT, 0.75));
|
|
@@ -1221,9 +1687,10 @@ function portsOf(stage, line, wires, near, far) {
|
|
|
1221
1687
|
return out;
|
|
1222
1688
|
}
|
|
1223
1689
|
var rack = (pipeline, options = {}) => {
|
|
1224
|
-
const
|
|
1690
|
+
const theme = options.theme ?? LOOK_THEME;
|
|
1691
|
+
const color = typeColors(pipeline, options.colors, theme.series);
|
|
1225
1692
|
const below = options.side !== "above";
|
|
1226
|
-
const
|
|
1693
|
+
const near2 = below ? 1 : 0;
|
|
1227
1694
|
const far = below ? 0 : 1;
|
|
1228
1695
|
const stages = uniqueStages(pipeline);
|
|
1229
1696
|
const given = options.chain?.filter((id) => stages.some((s) => s.id === id));
|
|
@@ -1232,12 +1699,12 @@ var rack = (pipeline, options = {}) => {
|
|
|
1232
1699
|
const gone = new Set(skips.map((s) => s.decision.id));
|
|
1233
1700
|
const chain = ordered.filter((id) => !gone.has(id));
|
|
1234
1701
|
const wires = rackWires(pipeline, skips);
|
|
1235
|
-
const line = lineOf(pipeline,
|
|
1702
|
+
const line = lineOf(pipeline, ordered, chain);
|
|
1236
1703
|
const kept = stages.filter((s) => !gone.has(s.id));
|
|
1237
1704
|
const controlSide = below ? "bottom" : "top";
|
|
1238
1705
|
const nodes = kept.map((stage) => {
|
|
1239
|
-
if (kindOf(stage) === "decision") return { id: stage.id, ...branchNode(stage, pipeline, options.branch, { controlSide }) };
|
|
1240
|
-
const ports =
|
|
1706
|
+
if (kindOf(stage) === "decision") return { id: stage.id, ...branchNode(stage, pipeline, options.branch, { controlSide, style: stageStyle(stage, theme) }) };
|
|
1707
|
+
const ports = portsOf2(stage, line, wires, near2, far);
|
|
1241
1708
|
const node = boxNode(stage, { showParams: options.showParams !== false, rowPorts: false, ports, run: options });
|
|
1242
1709
|
const room = roomFor(ports);
|
|
1243
1710
|
return room > 2 * SIDE_PORT_GAP ? { ...node, width: room } : node;
|
|
@@ -1248,7 +1715,7 @@ var rack = (pipeline, options = {}) => {
|
|
|
1248
1715
|
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
1716
|
const label = wire.label ?? (sideInput ? portOn(to, wire.to.port)?.label ?? wire.to.port : void 0);
|
|
1250
1717
|
return {
|
|
1251
|
-
edge: wireEdge(wire.from, wire.to, label),
|
|
1718
|
+
edge: wireEdge(wire.from, wire.to, label, wire.id),
|
|
1252
1719
|
style: wireStyle(color(wire.type), wire.arc === true || isControl(pipeline, wire.type), wire.problem)
|
|
1253
1720
|
};
|
|
1254
1721
|
});
|
|
@@ -1268,16 +1735,808 @@ var rack = (pipeline, options = {}) => {
|
|
|
1268
1735
|
...options.rankGap !== void 0 ? { rankGap: options.rankGap } : {}
|
|
1269
1736
|
},
|
|
1270
1737
|
router: "bezier",
|
|
1271
|
-
...sceneStyles(kept, edges)
|
|
1738
|
+
...sceneStyles(kept, edges, theme)
|
|
1272
1739
|
},
|
|
1273
1740
|
color,
|
|
1741
|
+
order: { axis: "x", items: chain.map((id) => ({ id })) },
|
|
1274
1742
|
exits: new Map(wires.flatMap((wire, i) => wire.exit === void 0 ? [] : [[edgeIdOf(edges[i].edge), wire.exit]]))
|
|
1275
1743
|
};
|
|
1276
1744
|
};
|
|
1277
1745
|
|
|
1746
|
+
// src/effect/editorMapping.ts
|
|
1747
|
+
var endsKey = (from, to) => JSON.stringify([from.stage, from.port, to.stage, to.port]);
|
|
1748
|
+
var edgeEnds = (e) => endsKey({ stage: e.from, port: e.fromPort ?? "" }, { stage: e.to, port: e.toPort ?? "" });
|
|
1749
|
+
function editorScene(doc, look = "nodeGraph", options) {
|
|
1750
|
+
const drawn = drawLook(doc, look, options);
|
|
1751
|
+
const stages = new Set(doc.pipeline.stages.map((s) => s.id));
|
|
1752
|
+
const groups = new Set((doc.pipeline.groups ?? []).map((g) => g.id));
|
|
1753
|
+
const ids = ensureWireIds(doc).pipeline.wires.map((w) => w.id);
|
|
1754
|
+
const byEnds = /* @__PURE__ */ new Map();
|
|
1755
|
+
for (const d of drawableWires(doc.pipeline)) {
|
|
1756
|
+
const key = endsKey(d.wire.from, d.wire.to);
|
|
1757
|
+
if (!byEnds.has(key)) byEnds.set(key, ids[d.index]);
|
|
1758
|
+
}
|
|
1759
|
+
const wires = new Map(drawn.data.edges.flatMap((e) => {
|
|
1760
|
+
const id = byEnds.get(edgeEnds(e));
|
|
1761
|
+
return id === void 0 ? [] : [[edgeIdOf(e), id]];
|
|
1762
|
+
}));
|
|
1763
|
+
const routes = look === "nodeGraph" ? doc.routes : void 0;
|
|
1764
|
+
const edges = routes === void 0 ? drawn.data.edges : drawn.data.edges.map((e) => {
|
|
1765
|
+
const id = wires.get(edgeIdOf(e));
|
|
1766
|
+
const route = id === void 0 ? void 0 : routes[id];
|
|
1767
|
+
return route === void 0 ? e : { ...e, waypoints: route };
|
|
1768
|
+
});
|
|
1769
|
+
return {
|
|
1770
|
+
...drawn,
|
|
1771
|
+
data: { ...drawn.data, edges },
|
|
1772
|
+
stageOf: (node) => stages.has(node) ? node : void 0,
|
|
1773
|
+
groupOf: (node) => groups.has(node) && !stages.has(node) ? node : void 0,
|
|
1774
|
+
wireOf: (edge) => wires.get(edge)
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
function drawLook(doc, look, options) {
|
|
1778
|
+
if (look === "rack") return rack(doc.pipeline, options);
|
|
1779
|
+
if (look === "lanes") {
|
|
1780
|
+
const opts = options;
|
|
1781
|
+
const order = doc.laneOrder ?? opts?.lanes;
|
|
1782
|
+
return lanes(doc.pipeline, { ...opts, ...order !== void 0 ? { lanes: order } : {} });
|
|
1783
|
+
}
|
|
1784
|
+
const drawn = nodeGraph(doc.pipeline, options);
|
|
1785
|
+
const nodes = drawn.data.nodes.map((n) => {
|
|
1786
|
+
const at = doc.places[n.id];
|
|
1787
|
+
return at === void 0 ? n : { ...n, at: { x: at.x, y: at.y }, pinned: true };
|
|
1788
|
+
});
|
|
1789
|
+
const { groups = [], ...rest } = drawn.data;
|
|
1790
|
+
const containers = groups.map((g) => {
|
|
1791
|
+
const box = doc.groupBoxes?.[g.id];
|
|
1792
|
+
return {
|
|
1793
|
+
id: g.id,
|
|
1794
|
+
role: "container",
|
|
1795
|
+
label: g.label ?? "",
|
|
1796
|
+
members: g.members,
|
|
1797
|
+
...box !== void 0 ? { box: { ...box }, pinned: true } : {}
|
|
1798
|
+
};
|
|
1799
|
+
});
|
|
1800
|
+
return {
|
|
1801
|
+
...drawn,
|
|
1802
|
+
data: {
|
|
1803
|
+
...rest,
|
|
1804
|
+
nodes,
|
|
1805
|
+
...containers.length > 0 ? { containers } : {},
|
|
1806
|
+
...doc.pages !== void 0 ? { pages: doc.pages } : {}
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// src/effect/groupOrder.ts
|
|
1812
|
+
function groupRuns(pipeline, chain) {
|
|
1813
|
+
const at = new Map(chain.map((id, i) => [id, i]));
|
|
1814
|
+
const doc = { pipeline};
|
|
1815
|
+
const runs = /* @__PURE__ */ new Map();
|
|
1816
|
+
for (const g of pipeline.groups ?? []) {
|
|
1817
|
+
const on = [...groupContents(doc, g.id).stages].flatMap((s) => at.get(s) ?? []);
|
|
1818
|
+
if (on.length === 0) continue;
|
|
1819
|
+
const first = Math.min(...on);
|
|
1820
|
+
const last = Math.max(...on);
|
|
1821
|
+
runs.set(g.id, last - first + 1 === on.length ? { first, last } : null);
|
|
1822
|
+
}
|
|
1823
|
+
return runs;
|
|
1824
|
+
}
|
|
1825
|
+
var holds = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
|
|
1826
|
+
function groupGripOf(doc, group, boxOf) {
|
|
1827
|
+
const box = boxOf(group);
|
|
1828
|
+
const members = doc.pipeline.groups?.find((g) => g.id === group)?.members ?? [];
|
|
1829
|
+
const tops = members.flatMap((m) => boxOf(m)?.y ?? []);
|
|
1830
|
+
if (box === void 0 || tops.length === 0) return void 0;
|
|
1831
|
+
const height = Math.min(...tops) - box.y;
|
|
1832
|
+
return height > 0 ? { x: box.x, y: box.y, width: box.width, height } : void 0;
|
|
1833
|
+
}
|
|
1834
|
+
function unitStages(doc, unit) {
|
|
1835
|
+
return doc.pipeline.groups?.some((g) => g.id === unit) ? groupContents(doc, unit).stages : /* @__PURE__ */ new Set([unit]);
|
|
1836
|
+
}
|
|
1837
|
+
function parentsOf(doc) {
|
|
1838
|
+
const parent = /* @__PURE__ */ new Map();
|
|
1839
|
+
for (const g of doc.pipeline.groups ?? []) for (const m of g.members) parent.set(m, g.id);
|
|
1840
|
+
return parent;
|
|
1841
|
+
}
|
|
1842
|
+
function groupAt(doc, boxOf, at, among = () => true) {
|
|
1843
|
+
const parent = parentsOf(doc);
|
|
1844
|
+
const depth = (id) => parent.has(id) ? 1 + depth(parent.get(id)) : 0;
|
|
1845
|
+
const held = (doc.pipeline.groups ?? []).filter((g) => {
|
|
1846
|
+
const grip = among(g.id) ? groupGripOf(doc, g.id, boxOf) : void 0;
|
|
1847
|
+
return grip !== void 0 && holds(grip, at);
|
|
1848
|
+
});
|
|
1849
|
+
return held.sort((a, b) => depth(b.id) - depth(a.id))[0]?.id;
|
|
1850
|
+
}
|
|
1851
|
+
function groupSlotsAt(source, at) {
|
|
1852
|
+
const { doc, order, boxOf } = source;
|
|
1853
|
+
const groups = doc.pipeline.groups ?? [];
|
|
1854
|
+
if (order === void 0 || groups.length === 0) return null;
|
|
1855
|
+
const parent = parentsOf(doc);
|
|
1856
|
+
const runs = groupRuns(doc.pipeline, mainChain(doc.pipeline));
|
|
1857
|
+
const held = groupAt(doc, boxOf, at, (g) => runs.get(g) != null);
|
|
1858
|
+
if (held === void 0) return null;
|
|
1859
|
+
const up = parent.get(held);
|
|
1860
|
+
const siblings = up !== void 0 ? groups.find((g) => g.id === up).members : [...groups, ...doc.pipeline.stages].flatMap((u) => parent.has(u.id) ? [] : [u.id]);
|
|
1861
|
+
const drawnAt = new Map(order.items.map((i, k) => [i.id, k]));
|
|
1862
|
+
const placed = siblings.flatMap((id) => {
|
|
1863
|
+
const drawn = [...unitStages(doc, id)].flatMap((s) => drawnAt.get(s) ?? []);
|
|
1864
|
+
const bounds = boxOf(id);
|
|
1865
|
+
return drawn.length === 0 || bounds === void 0 ? [] : [{ id, bounds, first: Math.min(...drawn) }];
|
|
1866
|
+
}).sort((a, b) => a.first - b.first);
|
|
1867
|
+
const isGroup = new Set(groups.map((g) => g.id));
|
|
1868
|
+
const items = placed.map(({ id, bounds }) => {
|
|
1869
|
+
if (!isGroup.has(id)) return { id, bounds, grip: null };
|
|
1870
|
+
const inside = groupContents(doc, id);
|
|
1871
|
+
const carries = [...inside.groups, ...inside.stages, ...[...inside.groups].map(groupLabelIdOf)];
|
|
1872
|
+
return { id, bounds, grip: groupGripOf(doc, id, boxOf) ?? null, carries };
|
|
1873
|
+
});
|
|
1874
|
+
return items.length < 2 ? null : sequenceSlots(order.axis, items);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// src/effect/editorReorder.ts
|
|
1878
|
+
var REORDER_LANES_ACTION_ID = "diagram.reorderLanes";
|
|
1879
|
+
var REORDER_GROUPS_ACTION_ID = "diagram.reorderGroups";
|
|
1880
|
+
function slotsOf(order, boxOf) {
|
|
1881
|
+
if (order === void 0) return null;
|
|
1882
|
+
const items = order.items.flatMap((i) => {
|
|
1883
|
+
const bounds = boxOf(i.id);
|
|
1884
|
+
return bounds === void 0 ? [] : [{ id: i.id, bounds, ...i.grip !== void 0 ? { grip: i.grip } : {} }];
|
|
1885
|
+
});
|
|
1886
|
+
return items.length < 2 ? null : sequenceSlots(order.axis, items);
|
|
1887
|
+
}
|
|
1888
|
+
function keepsGroups(doc, next) {
|
|
1889
|
+
if (next === null) return null;
|
|
1890
|
+
const after = groupRuns(next.pipeline, mainChain(next.pipeline));
|
|
1891
|
+
for (const [id, run] of groupRuns(doc.pipeline, mainChain(doc.pipeline))) if (run !== null && after.get(id) === null) return null;
|
|
1892
|
+
return next;
|
|
1893
|
+
}
|
|
1894
|
+
function dropInGap(doc, chain, drawn, to, at) {
|
|
1895
|
+
const tries = [to];
|
|
1896
|
+
for (let k = to; k > 0 && !drawn.has(chain[k - 1]); k--) tries.push(k - 1);
|
|
1897
|
+
for (let k = to; k < chain.length && !drawn.has(chain[k]); k++) tries.push(k + 1);
|
|
1898
|
+
for (const t of tries) {
|
|
1899
|
+
const next = keepsGroups(doc, at(t));
|
|
1900
|
+
if (next !== null) return next;
|
|
1901
|
+
}
|
|
1902
|
+
return null;
|
|
1903
|
+
}
|
|
1904
|
+
var drawnOf = (order) => new Set(order.items.map((i) => i.id));
|
|
1905
|
+
function stageMove(doc, order, move) {
|
|
1906
|
+
const chain = mainChain(doc.pipeline);
|
|
1907
|
+
const before = order.items[move.to];
|
|
1908
|
+
const to = before === void 0 ? chain.length : chain.indexOf(before.id);
|
|
1909
|
+
const wired = ensureWireIds(doc);
|
|
1910
|
+
return dropInGap(doc, chain, drawnOf(order), to, (t) => moveInChain(wired, move.id, t));
|
|
1911
|
+
}
|
|
1912
|
+
function groupMove(doc, order, units, move) {
|
|
1913
|
+
const chain = mainChain(doc.pipeline);
|
|
1914
|
+
const drawn = drawnOf(order);
|
|
1915
|
+
const indices = (unit, drawnOnly) => [...unitStages(doc, unit)].flatMap((s) => chain.includes(s) && (!drawnOnly || drawn.has(s)) ? [chain.indexOf(s)] : []);
|
|
1916
|
+
const run = indices(move.id, false);
|
|
1917
|
+
if (run.length === 0) return null;
|
|
1918
|
+
const [i, j] = [Math.min(...run), Math.max(...run)];
|
|
1919
|
+
if (j - i + 1 !== run.length) return null;
|
|
1920
|
+
const before = units[move.to];
|
|
1921
|
+
const edge = before !== void 0 ? indices(before, true) : indices(units[units.length - 1] ?? move.id, true).map((k) => k + 1);
|
|
1922
|
+
if (edge.length === 0) return null;
|
|
1923
|
+
const to = before !== void 0 ? Math.min(...edge) : Math.max(...edge);
|
|
1924
|
+
const wired = ensureWireIds(doc);
|
|
1925
|
+
return dropInGap(doc, chain, drawn, to, (t) => moveRunInChain(wired, chain[i], chain[j], t));
|
|
1926
|
+
}
|
|
1927
|
+
function laneMove(doc, order, move) {
|
|
1928
|
+
const types = order.items.flatMap((i) => laneTypeOf(i.id) ?? []);
|
|
1929
|
+
const moved = types.filter((_, k) => k !== move.from);
|
|
1930
|
+
moved.splice(move.to > move.from ? move.to - 1 : move.to, 0, types[move.from]);
|
|
1931
|
+
return setLaneOrder(doc, moved);
|
|
1932
|
+
}
|
|
1933
|
+
function unitsBefore(move) {
|
|
1934
|
+
const units = move.order.filter((id) => id !== move.id);
|
|
1935
|
+
units.splice(move.from, 0, move.id);
|
|
1936
|
+
return units;
|
|
1937
|
+
}
|
|
1938
|
+
function groupReorder(now) {
|
|
1939
|
+
const moved = (move) => {
|
|
1940
|
+
const { doc, order } = now();
|
|
1941
|
+
return order === void 0 ? null : groupMove(doc, order, unitsBefore(move), move);
|
|
1942
|
+
};
|
|
1943
|
+
return reorderContribution({
|
|
1944
|
+
id: REORDER_GROUPS_ACTION_ID,
|
|
1945
|
+
label: "Reorder group",
|
|
1946
|
+
slots: (_, at) => groupSlotsAt(now(), at),
|
|
1947
|
+
canDrop: (move) => moved(move) !== null,
|
|
1948
|
+
commit: (move) => {
|
|
1949
|
+
const next = moved(move);
|
|
1950
|
+
if (next !== null) now().onChange(next, "Reorder group");
|
|
1951
|
+
}
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
function editorReorders(now) {
|
|
1955
|
+
return [
|
|
1956
|
+
reorderContribution({
|
|
1957
|
+
label: "Reorder",
|
|
1958
|
+
slots: () => slotsOf(now().order, now().boxOf),
|
|
1959
|
+
canDrop: (move) => {
|
|
1960
|
+
const { doc, order } = now();
|
|
1961
|
+
return order !== void 0 && stageMove(doc, order, move) !== null;
|
|
1962
|
+
},
|
|
1963
|
+
commit: (move) => {
|
|
1964
|
+
const { doc, order, onChange } = now();
|
|
1965
|
+
const next = order === void 0 ? null : stageMove(doc, order, move);
|
|
1966
|
+
if (next !== null) onChange(next, "Reorder");
|
|
1967
|
+
}
|
|
1968
|
+
}),
|
|
1969
|
+
reorderContribution({
|
|
1970
|
+
id: REORDER_LANES_ACTION_ID,
|
|
1971
|
+
label: "Reorder lanes",
|
|
1972
|
+
slots: () => slotsOf(now().laneOrder, now().boxOf),
|
|
1973
|
+
commit: (move) => {
|
|
1974
|
+
const { doc, laneOrder, onChange } = now();
|
|
1975
|
+
if (laneOrder !== void 0) onChange(laneMove(doc, laneOrder, move), "Reorder lanes");
|
|
1976
|
+
}
|
|
1977
|
+
}),
|
|
1978
|
+
groupReorder(now)
|
|
1979
|
+
];
|
|
1980
|
+
}
|
|
1981
|
+
var RENAME_GROUP_ACTION_ID = "effect.renameGroup";
|
|
1982
|
+
function renameGroupContribution(now, rename) {
|
|
1983
|
+
const under = (at) => {
|
|
1984
|
+
const { doc, boxOf } = now();
|
|
1985
|
+
return groupAt(doc, boxOf, at);
|
|
1986
|
+
};
|
|
1987
|
+
return {
|
|
1988
|
+
id: `weasel-diagram-effect:${RENAME_GROUP_ACTION_ID}`,
|
|
1989
|
+
eligibility: { always: true },
|
|
1990
|
+
actions: [{
|
|
1991
|
+
id: RENAME_GROUP_ACTION_ID,
|
|
1992
|
+
label: "Rename group",
|
|
1993
|
+
group: "diagram",
|
|
1994
|
+
enabled: (_, at) => at === void 0 || under(at) !== void 0 ? true : ActionDisabledReason.NotApplicable,
|
|
1995
|
+
invoker: {
|
|
1996
|
+
timing: "immediate",
|
|
1997
|
+
run(_, params) {
|
|
1998
|
+
const group = under({ x: Number(params?.worldX), y: Number(params?.worldY) });
|
|
1999
|
+
if (group !== void 0) rename(group);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}],
|
|
2003
|
+
// Empty space included: an unlabeled group's grip is padding.
|
|
2004
|
+
bindings: [{ spec: { kind: "doubleClick", target: { kindOf: ANYWHERE } }, actionId: RENAME_GROUP_ACTION_ID }]
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
// src/effect/place.ts
|
|
2009
|
+
var PLACE_GAP = 24;
|
|
2010
|
+
var STEP = 8;
|
|
2011
|
+
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);
|
|
2012
|
+
function freeSpot(boxes, size, view) {
|
|
2013
|
+
if (view !== null) {
|
|
2014
|
+
const right = view.x + view.width - PLACE_GAP - size.width;
|
|
2015
|
+
const bottom = view.y + view.height - PLACE_GAP - size.height;
|
|
2016
|
+
for (let y = view.y + PLACE_GAP; y <= bottom; y += STEP) {
|
|
2017
|
+
for (let x = view.x + PLACE_GAP; x <= right; x += STEP) {
|
|
2018
|
+
if (clear(x, y, size, boxes)) return { x, y };
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
if (boxes.length === 0) return { x: (view?.x ?? 0) + PLACE_GAP, y: (view?.y ?? 0) + PLACE_GAP };
|
|
2023
|
+
const last = boxes.reduce((a, b) => b.x + b.width > a.x + a.width ? b : a);
|
|
2024
|
+
return { x: last.x + last.width + PLACE_GAP, y: last.y };
|
|
2025
|
+
}
|
|
2026
|
+
var EffectEditor = forwardRef(function EffectEditor2({ doc, onChange, run, selected, onSelect, selectedGroup, onSelectGroup, look = "nodeGraph", options, width, height, background, className }, ref) {
|
|
2027
|
+
const built = useMemo(() => editorScene(doc, look, options), [doc, look, options]);
|
|
2028
|
+
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]);
|
|
2029
|
+
const painted = useMemo(() => paintRun(doc.pipeline, specs, run, built.exits), [doc.pipeline, specs, run, built]);
|
|
2030
|
+
useReleaseReplaced(run);
|
|
2031
|
+
const portOptions = useRunPortOptions(built.color, doc.pipeline, run);
|
|
2032
|
+
const picks = useEditorPicks(built, { selected, selectedGroup, onSelect, onSelectGroup });
|
|
2033
|
+
const now = useLatest({ doc, built, specs, look, options, onChange });
|
|
2034
|
+
const view = useRef(null);
|
|
2035
|
+
const [renaming, setRenaming] = useState(null);
|
|
2036
|
+
const handlers = useMemo(() => editHandlers(() => now.current, picks.unpick, setRenaming), [now, picks.unpick]);
|
|
2037
|
+
const ambient = useMemo(() => {
|
|
2038
|
+
const source = () => {
|
|
2039
|
+
const { doc: d, built: b, specs: shown, onChange: report } = now.current;
|
|
2040
|
+
const boxes = new Map(shown.map((s) => [s.id, s.pose]));
|
|
2041
|
+
return { doc: d, order: b.order, laneOrder: b.laneOrder, boxOf: (id) => boxes.get(id), onChange: report };
|
|
2042
|
+
};
|
|
2043
|
+
return [...editorReorders(source), renameGroupContribution(source, setRenaming)];
|
|
2044
|
+
}, [now]);
|
|
2045
|
+
useImperativeHandle(ref, () => ({
|
|
2046
|
+
addStage: (type, client) => {
|
|
2047
|
+
const { doc: d, specs: shown, look: l, options: opts, onChange: report } = now.current;
|
|
2048
|
+
const stage = stageFromType(type, nextStageId(d.pipeline, type.type));
|
|
2049
|
+
if (l !== "nodeGraph") {
|
|
2050
|
+
report(addStage(ensureWireIds(d), stage), "Add stage");
|
|
2051
|
+
return stage.id;
|
|
2052
|
+
}
|
|
2053
|
+
const pinned = ensureWireIds(pinShown(d, shown));
|
|
2054
|
+
if (client && view.current) {
|
|
2055
|
+
report(addStage(pinned, stage, view.current.worldAt(client.x, client.y)), "Add stage");
|
|
2056
|
+
} else {
|
|
2057
|
+
const boxes = stageBoxes(d, shown);
|
|
2058
|
+
const alone = { pipeline: { ...d.pipeline, stages: [stage], wires: [], groups: [] }, places: {} };
|
|
2059
|
+
const size = stageBoxes(alone, drawnSpecs(alone, opts)).get(stage.id) ?? { width: 0, height: 0 };
|
|
2060
|
+
const at = freeSpot([...boxes.values()], size, view.current?.visible() ?? null);
|
|
2061
|
+
report(addStage(pinned, stage, at), "Add stage");
|
|
2062
|
+
}
|
|
2063
|
+
return stage.id;
|
|
2064
|
+
},
|
|
2065
|
+
group: (ids) => {
|
|
2066
|
+
const { doc: d, specs: shown, look: l, onChange: report } = now.current;
|
|
2067
|
+
if (ids.length === 0) return null;
|
|
2068
|
+
const base = l === "nodeGraph" ? pinShown(ensureWireIds(d), shown) : d;
|
|
2069
|
+
report(groupStages(base, ids), "Group");
|
|
2070
|
+
return nextGroupId(d.pipeline);
|
|
2071
|
+
},
|
|
2072
|
+
ungroup: (id) => {
|
|
2073
|
+
const { doc: d, onChange: report } = now.current;
|
|
2074
|
+
if (d.pipeline.groups?.some((g) => g.id === id)) report(ungroup(d, id), "Ungroup");
|
|
2075
|
+
},
|
|
2076
|
+
renameGroup: (id) => setRenaming(id)
|
|
2077
|
+
}), [now]);
|
|
2078
|
+
const title2 = useGroupTitle(doc, look, renaming, setRenaming, onChange);
|
|
2079
|
+
const fill = (look === "nodeGraph" ? options?.theme ?? LOOK_THEME : LOOK_THEME).page;
|
|
2080
|
+
const placed = useMemo(() => diagramPages(built.data), [built.data.pages, built.data.pageLayout]);
|
|
2081
|
+
const pages = useMemo(() => placed.length > 0 ? { placed, fill } : void 0, [placed, fill]);
|
|
2082
|
+
return /* @__PURE__ */ jsx(
|
|
2083
|
+
DiagramView,
|
|
2084
|
+
{
|
|
2085
|
+
ref: view,
|
|
2086
|
+
specs: painted,
|
|
2087
|
+
width,
|
|
2088
|
+
height,
|
|
2089
|
+
minScale: EFFECT_MIN_SCALE,
|
|
2090
|
+
anchor: "start",
|
|
2091
|
+
ports: "connect",
|
|
2092
|
+
portOptions,
|
|
2093
|
+
canConnect: handlers.canConnect,
|
|
2094
|
+
onConnect: handlers.onConnect,
|
|
2095
|
+
onReconnect: handlers.onReconnect,
|
|
2096
|
+
onEditRoute: handlers.onEditRoute,
|
|
2097
|
+
onDelete: handlers.onDelete,
|
|
2098
|
+
...look === "nodeGraph" ? { onMove: handlers.onMove } : { ambient },
|
|
2099
|
+
...look === "nodeGraph" ? { onEditContainer: handlers.onEditContainer } : {},
|
|
2100
|
+
onSelect: picks.pick,
|
|
2101
|
+
...picks.shown !== void 0 ? { selected: picks.shown } : {},
|
|
2102
|
+
...pages !== void 0 ? { pages } : {},
|
|
2103
|
+
...title2 !== null ? { overlays: title2 } : {},
|
|
2104
|
+
...background !== void 0 ? { background } : {},
|
|
2105
|
+
...className !== void 0 ? { className } : {}
|
|
2106
|
+
},
|
|
2107
|
+
look
|
|
2108
|
+
);
|
|
2109
|
+
});
|
|
2110
|
+
function drawnSpecs(doc, options) {
|
|
2111
|
+
const built = editorScene(doc, "nodeGraph", options);
|
|
2112
|
+
return diagramScene(built.data, built.scene);
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
// src/effect/image/evaluators.ts
|
|
2116
|
+
var BLEND = { normal: "source-over", screen: "screen", multiply: "multiply" };
|
|
2117
|
+
function draw(width, height, paint) {
|
|
2118
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
2119
|
+
paint(canvas.getContext("2d"));
|
|
2120
|
+
return canvas.transferToImageBitmap();
|
|
2121
|
+
}
|
|
2122
|
+
function need(ctx, port) {
|
|
2123
|
+
const value = ctx.inputs[port];
|
|
2124
|
+
if (value === void 0) throw new Error(`${port} is not connected`);
|
|
2125
|
+
return value;
|
|
2126
|
+
}
|
|
2127
|
+
function decide(ctx, value) {
|
|
2128
|
+
const outs = ctx.stage.outputs ?? [];
|
|
2129
|
+
const split = ctx.stage.split;
|
|
2130
|
+
const index = split !== void 0 ? split.at.filter((cut) => value >= cut).length : value > 0 ? 0 : 1;
|
|
2131
|
+
return outs[Math.min(index, outs.length - 1)]?.id;
|
|
2132
|
+
}
|
|
2133
|
+
var imageEvaluators = {
|
|
2134
|
+
source: ({ sources }) => {
|
|
2135
|
+
if (sources.image === void 0) throw new Error("no source image");
|
|
2136
|
+
return { outputs: { out: sources.image } };
|
|
2137
|
+
},
|
|
2138
|
+
blur: (ctx) => {
|
|
2139
|
+
const image2 = need(ctx, "in");
|
|
2140
|
+
const radius = Number(ctx.params.radius ?? 0);
|
|
2141
|
+
return {
|
|
2142
|
+
outputs: {
|
|
2143
|
+
out: draw(image2.width, image2.height, (g) => {
|
|
2144
|
+
g.filter = `blur(${radius}px)`;
|
|
2145
|
+
g.drawImage(image2, 0, 0);
|
|
2146
|
+
})
|
|
2147
|
+
}
|
|
2148
|
+
};
|
|
2149
|
+
},
|
|
2150
|
+
composite: (ctx) => {
|
|
2151
|
+
const base = need(ctx, "base");
|
|
2152
|
+
const top = need(ctx, "top");
|
|
2153
|
+
return {
|
|
2154
|
+
outputs: {
|
|
2155
|
+
out: draw(base.width, base.height, (g) => {
|
|
2156
|
+
g.drawImage(base, 0, 0);
|
|
2157
|
+
g.globalCompositeOperation = BLEND[String(ctx.params.blend)] ?? "source-over";
|
|
2158
|
+
g.drawImage(top, 0, 0, base.width, base.height);
|
|
2159
|
+
})
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2162
|
+
},
|
|
2163
|
+
mask: (ctx) => {
|
|
2164
|
+
const image2 = need(ctx, "in");
|
|
2165
|
+
const mask = need(ctx, "mask");
|
|
2166
|
+
return {
|
|
2167
|
+
outputs: {
|
|
2168
|
+
out: draw(image2.width, image2.height, (g) => {
|
|
2169
|
+
g.drawImage(image2, 0, 0);
|
|
2170
|
+
g.globalCompositeOperation = "destination-in";
|
|
2171
|
+
g.drawImage(mask, 0, 0, image2.width, image2.height);
|
|
2172
|
+
})
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
},
|
|
2176
|
+
oscillator: ({ params, time }) => ({ outputs: { value: Math.sin(2 * Math.PI * Number(params.rate ?? 1) * time) } }),
|
|
2177
|
+
decision: (ctx) => {
|
|
2178
|
+
const tested = ctx.stage.split?.input ?? ctx.stage.inputs?.find((p) => p.type === "number")?.id ?? "test";
|
|
2179
|
+
const taken = decide(ctx, need(ctx, tested));
|
|
2180
|
+
const type = ctx.stage.outputs?.find((p) => p.id === taken)?.type;
|
|
2181
|
+
const from = ctx.stage.inputs?.find((p) => p.type === type && ctx.inputs[p.id] !== void 0);
|
|
2182
|
+
return { outputs: taken !== void 0 && from !== void 0 ? { [taken]: ctx.inputs[from.id] } : {}, ...taken !== void 0 ? { taken } : {} };
|
|
2183
|
+
},
|
|
2184
|
+
out: () => ({ outputs: {} })
|
|
2185
|
+
};
|
|
2186
|
+
|
|
2187
|
+
// src/effect/image/stageTypes.ts
|
|
2188
|
+
var param = (label, control) => ({ label, value: formatParam(control), control });
|
|
2189
|
+
var image = (id, label) => ({ id, type: "image", ...label !== void 0 ? { label } : {} });
|
|
2190
|
+
var IMAGE_STAGE_TYPES = [
|
|
2191
|
+
{ type: "source", title: "Source", kind: "source", outputs: [image("out")] },
|
|
2192
|
+
{
|
|
2193
|
+
type: "blur",
|
|
2194
|
+
title: "Blur",
|
|
2195
|
+
inputs: [image("in")],
|
|
2196
|
+
outputs: [image("out")],
|
|
2197
|
+
params: [param("radius", { kind: "number", value: 4, min: 0, max: 50, step: 1, unit: "px" })]
|
|
2198
|
+
},
|
|
2199
|
+
{
|
|
2200
|
+
type: "composite",
|
|
2201
|
+
title: "Composite",
|
|
2202
|
+
inputs: [image("base"), image("top")],
|
|
2203
|
+
outputs: [image("out")],
|
|
2204
|
+
params: [param("blend", {
|
|
2205
|
+
kind: "enum",
|
|
2206
|
+
value: "normal",
|
|
2207
|
+
options: [{ value: "normal", label: "Normal" }, { value: "screen", label: "Screen" }, { value: "multiply", label: "Multiply" }]
|
|
2208
|
+
})]
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
type: "mask",
|
|
2212
|
+
title: "Mask",
|
|
2213
|
+
inputs: [image("in"), { id: "mask", type: "mask" }],
|
|
2214
|
+
outputs: [image("out")]
|
|
2215
|
+
},
|
|
2216
|
+
{
|
|
2217
|
+
type: "oscillator",
|
|
2218
|
+
title: "Oscillator",
|
|
2219
|
+
outputs: [{ id: "value", type: "number" }],
|
|
2220
|
+
params: [param("rate", { kind: "number", value: 1, min: 0.1, max: 20, step: 0.1, unit: "Hz" })]
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
type: "decision",
|
|
2224
|
+
title: "Decision",
|
|
2225
|
+
kind: "decision",
|
|
2226
|
+
inputs: [image("in"), { id: "test", type: "number" }],
|
|
2227
|
+
outputs: [image("yes", "yes"), image("no", "no")]
|
|
2228
|
+
},
|
|
2229
|
+
{ type: "out", title: "Out", kind: "sink", inputs: [image("in")] }
|
|
2230
|
+
];
|
|
2231
|
+
|
|
2232
|
+
// src/effect/image/testCard.ts
|
|
2233
|
+
var BARS = ["#c0c0c0", "#c0c000", "#00c0c0", "#00c000", "#c000c0", "#c00000", "#0000c0"];
|
|
2234
|
+
function testCard(size) {
|
|
2235
|
+
const { width, height } = typeof size === "number" ? { width: size, height: size } : size;
|
|
2236
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
2237
|
+
const g = canvas.getContext("2d");
|
|
2238
|
+
const barHeight = height * 0.7;
|
|
2239
|
+
BARS.forEach((color, i) => {
|
|
2240
|
+
g.fillStyle = color;
|
|
2241
|
+
const x = Math.floor(i * width / BARS.length);
|
|
2242
|
+
g.fillRect(x, 0, Math.ceil(width / BARS.length) + 1, barHeight);
|
|
2243
|
+
});
|
|
2244
|
+
const ramp = g.createLinearGradient(0, 0, width, 0);
|
|
2245
|
+
ramp.addColorStop(0, "#000");
|
|
2246
|
+
ramp.addColorStop(1, "#fff");
|
|
2247
|
+
g.fillStyle = ramp;
|
|
2248
|
+
g.fillRect(0, barHeight, width, height - barHeight);
|
|
2249
|
+
g.strokeStyle = "#fff";
|
|
2250
|
+
g.lineWidth = Math.max(1, Math.min(width, height) / 32);
|
|
2251
|
+
g.beginPath();
|
|
2252
|
+
g.arc(width / 2, height / 2, Math.min(width, height) * 0.3, 0, Math.PI * 2);
|
|
2253
|
+
g.stroke();
|
|
2254
|
+
return canvas.transferToImageBitmap();
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// src/effect/image/valueKinds.ts
|
|
2258
|
+
var bitmapKind = {
|
|
2259
|
+
show: (value) => `${value.width}\xD7${value.height}`,
|
|
2260
|
+
preview(value, size) {
|
|
2261
|
+
const width = Math.max(1, Math.round(size.width * PREVIEW_DENSITY));
|
|
2262
|
+
const height = Math.max(1, Math.round(size.height * PREVIEW_DENSITY));
|
|
2263
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
2264
|
+
canvas.getContext("2d").drawImage(value, 0, 0, width, height);
|
|
2265
|
+
return canvas.transferToImageBitmap();
|
|
2266
|
+
},
|
|
2267
|
+
dispose: (value) => value.close()
|
|
2268
|
+
};
|
|
2269
|
+
var imageValueKinds = {
|
|
2270
|
+
image: bitmapKind,
|
|
2271
|
+
mask: bitmapKind,
|
|
2272
|
+
number: { show: (value) => value.toFixed(3) }
|
|
2273
|
+
};
|
|
2274
|
+
|
|
1278
2275
|
// src/effect/looks.ts
|
|
1279
2276
|
var EFFECT_LOOKS = Object.freeze({ nodeGraph, rack, lanes, compact });
|
|
1280
2277
|
|
|
1281
|
-
|
|
2278
|
+
// src/effect/runner/pass.ts
|
|
2279
|
+
function createPass(pipeline, opts) {
|
|
2280
|
+
const stages = new Map(uniqueStages(pipeline).map((s) => [s.id, s]));
|
|
2281
|
+
const queue = runOrder(pipeline).order;
|
|
2282
|
+
const made = /* @__PURE__ */ new Map();
|
|
2283
|
+
const sources = opts.sources ?? {};
|
|
2284
|
+
const size = typeof opts.previewSize === "object" ? opts.previewSize : { width: opts.previewSize ?? PREVIEW_SIZE, height: opts.previewSize ?? PREVIEW_SIZE };
|
|
2285
|
+
const signal = opts.signal ?? new AbortController().signal;
|
|
2286
|
+
let run = { stages: {} };
|
|
2287
|
+
const inputsOf = (stage) => {
|
|
2288
|
+
const inputs2 = {};
|
|
2289
|
+
for (const w of pipeline.wires) {
|
|
2290
|
+
if (w.to.stage !== stage.id || inputs2[w.to.port] !== void 0) continue;
|
|
2291
|
+
const value = made.get(w.from.stage)?.[w.from.port];
|
|
2292
|
+
if (value !== void 0) inputs2[w.to.port] = value;
|
|
2293
|
+
}
|
|
2294
|
+
return inputs2;
|
|
2295
|
+
};
|
|
2296
|
+
const record = async (stage, inputs2, outputs, entry) => {
|
|
2297
|
+
made.set(stage.id, outputs);
|
|
2298
|
+
const values = {};
|
|
2299
|
+
const previews = {};
|
|
2300
|
+
const seen = { ...inputs2, ...outputs };
|
|
2301
|
+
for (const port of shownPorts(stage)) {
|
|
2302
|
+
const value = seen[port.id];
|
|
2303
|
+
if (value === void 0) continue;
|
|
2304
|
+
const kind = opts.kinds?.[port.type];
|
|
2305
|
+
const text = kind?.show !== void 0 ? kind.show(value) : plainText(value);
|
|
2306
|
+
if (text !== void 0) values[port.id] = text;
|
|
2307
|
+
const preview = opts.previews === false ? void 0 : await kind?.preview?.(value, size);
|
|
2308
|
+
if (preview !== void 0) previews[port.id] = preview;
|
|
2309
|
+
}
|
|
2310
|
+
const full = {
|
|
2311
|
+
...entry,
|
|
2312
|
+
...Object.keys(values).length > 0 ? { values } : {},
|
|
2313
|
+
...Object.keys(previews).length > 0 ? { previews } : {}
|
|
2314
|
+
};
|
|
2315
|
+
run = { stages: { ...run.stages, [stage.id]: full } };
|
|
2316
|
+
};
|
|
2317
|
+
const evaluate = async (stage, inputs2) => {
|
|
2318
|
+
if (stage.bypassed === true) return record(stage, inputs2, passThrough(stage, inputs2), {});
|
|
2319
|
+
const evaluator = stage.type === void 0 ? void 0 : opts.evaluators[stage.type];
|
|
2320
|
+
const params = paramValues(stage);
|
|
2321
|
+
const start = performance.now();
|
|
2322
|
+
try {
|
|
2323
|
+
if (evaluator === void 0) throw new Error(`no evaluator for type "${stage.type ?? ""}"`);
|
|
2324
|
+
const result = await evaluator({ stage, inputs: inputs2, params, sources, time: opts.time ?? 0, signal });
|
|
2325
|
+
const cost = performance.now() - start;
|
|
2326
|
+
return record(stage, inputs2, result.outputs, { cost, ...result.taken !== void 0 ? { taken: result.taken } : {} });
|
|
2327
|
+
} catch (err) {
|
|
2328
|
+
signal.throwIfAborted();
|
|
2329
|
+
const cost = performance.now() - start;
|
|
2330
|
+
run = { stages: { ...run.stages, [stage.id]: { cost, error: err instanceof Error ? err.message : String(err) } } };
|
|
2331
|
+
}
|
|
2332
|
+
};
|
|
2333
|
+
return {
|
|
2334
|
+
get run() {
|
|
2335
|
+
return run;
|
|
2336
|
+
},
|
|
2337
|
+
async step() {
|
|
2338
|
+
while (queue.length > 0) {
|
|
2339
|
+
signal.throwIfAborted();
|
|
2340
|
+
const stage = stages.get(queue.shift());
|
|
2341
|
+
const inputs2 = inputsOf(stage);
|
|
2342
|
+
if ((stage.inputs?.length ?? 0) > 0 && Object.keys(inputs2).length === 0) continue;
|
|
2343
|
+
await evaluate(stage, inputs2);
|
|
2344
|
+
signal.throwIfAborted();
|
|
2345
|
+
return stage.id;
|
|
2346
|
+
}
|
|
2347
|
+
return void 0;
|
|
2348
|
+
},
|
|
2349
|
+
dispose() {
|
|
2350
|
+
const given = new Set(Object.values(sources));
|
|
2351
|
+
const done = /* @__PURE__ */ new Set();
|
|
2352
|
+
for (const [id, outputs] of made) {
|
|
2353
|
+
for (const port of stages.get(id)?.outputs ?? []) {
|
|
2354
|
+
const value = outputs[port.id];
|
|
2355
|
+
if (value === void 0 || given.has(value) || done.has(value)) continue;
|
|
2356
|
+
done.add(value);
|
|
2357
|
+
opts.kinds?.[port.type]?.dispose?.(value);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
made.clear();
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
function passThrough(stage, inputs2) {
|
|
2365
|
+
const out = {};
|
|
2366
|
+
for (const port of stage.outputs ?? []) {
|
|
2367
|
+
const from = (stage.inputs ?? []).find((i) => i.type === port.type && inputs2[i.id] !== void 0);
|
|
2368
|
+
if (from !== void 0) out[port.id] = inputs2[from.id];
|
|
2369
|
+
}
|
|
2370
|
+
return out;
|
|
2371
|
+
}
|
|
2372
|
+
function plainText(value) {
|
|
2373
|
+
return typeof value === "number" || typeof value === "string" || typeof value === "boolean" ? String(value) : void 0;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
// src/effect/runner/runPipeline.ts
|
|
2377
|
+
async function runPipeline(pipeline, opts) {
|
|
2378
|
+
const pass = createPass(pipeline, opts);
|
|
2379
|
+
try {
|
|
2380
|
+
while (await pass.step() !== void 0) ;
|
|
2381
|
+
return pass.run;
|
|
2382
|
+
} finally {
|
|
2383
|
+
pass.dispose();
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
// src/effect/runner/runner.ts
|
|
2388
|
+
function createEffectRunner(opts) {
|
|
2389
|
+
let { pipeline, ...options } = opts;
|
|
2390
|
+
let state = { run: void 0, playing: false, time: 0 };
|
|
2391
|
+
let stepping = null;
|
|
2392
|
+
let controller = new AbortController();
|
|
2393
|
+
let tail = Promise.resolve();
|
|
2394
|
+
let pending = 0;
|
|
2395
|
+
let published = [];
|
|
2396
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2397
|
+
const set = (next) => {
|
|
2398
|
+
state = { ...state, ...next };
|
|
2399
|
+
for (const l of [...listeners]) l();
|
|
2400
|
+
};
|
|
2401
|
+
const publish = (run) => {
|
|
2402
|
+
published.push(run);
|
|
2403
|
+
set({ run });
|
|
2404
|
+
};
|
|
2405
|
+
const passOptions = (time) => ({ ...options, time, signal: controller.signal });
|
|
2406
|
+
const dropStepping = () => {
|
|
2407
|
+
stepping?.dispose();
|
|
2408
|
+
stepping = null;
|
|
2409
|
+
};
|
|
2410
|
+
const enqueue = (task) => {
|
|
2411
|
+
const signal = controller.signal;
|
|
2412
|
+
pending++;
|
|
2413
|
+
const done = tail.then(() => signal.aborted ? void 0 : task(signal)).catch((err) => {
|
|
2414
|
+
if (!signal.aborted) throw err;
|
|
2415
|
+
}).finally(() => {
|
|
2416
|
+
pending--;
|
|
2417
|
+
});
|
|
2418
|
+
tail = done.catch(() => {
|
|
2419
|
+
});
|
|
2420
|
+
return done;
|
|
2421
|
+
};
|
|
2422
|
+
const wholeRun = (time) => async (signal) => {
|
|
2423
|
+
dropStepping();
|
|
2424
|
+
const run = await runPipeline(pipeline, passOptions(time));
|
|
2425
|
+
if (signal.aborted) closeBitmaps(previewsOf(run));
|
|
2426
|
+
else publish(run);
|
|
2427
|
+
};
|
|
2428
|
+
const stepOnce = (time) => async () => {
|
|
2429
|
+
let pass = stepping ??= createPass(pipeline, passOptions(time));
|
|
2430
|
+
let at = await pass.step();
|
|
2431
|
+
if (at === void 0) {
|
|
2432
|
+
dropStepping();
|
|
2433
|
+
pass = stepping = createPass(pipeline, passOptions(time));
|
|
2434
|
+
at = await pass.step();
|
|
2435
|
+
}
|
|
2436
|
+
publish(at === void 0 ? pass.run : { ...pass.run, at });
|
|
2437
|
+
};
|
|
2438
|
+
return {
|
|
2439
|
+
get state() {
|
|
2440
|
+
return state;
|
|
2441
|
+
},
|
|
2442
|
+
run: () => enqueue(wholeRun(state.time)),
|
|
2443
|
+
step() {
|
|
2444
|
+
return enqueue(stepOnce(state.time));
|
|
2445
|
+
},
|
|
2446
|
+
play: () => {
|
|
2447
|
+
if (!state.playing) set({ playing: true });
|
|
2448
|
+
},
|
|
2449
|
+
pause: () => {
|
|
2450
|
+
if (state.playing) set({ playing: false });
|
|
2451
|
+
},
|
|
2452
|
+
reset() {
|
|
2453
|
+
controller.abort();
|
|
2454
|
+
controller = new AbortController();
|
|
2455
|
+
dropStepping();
|
|
2456
|
+
set({ run: void 0, playing: false, time: 0 });
|
|
2457
|
+
},
|
|
2458
|
+
advance(seconds) {
|
|
2459
|
+
if (!state.playing) return;
|
|
2460
|
+
state = { ...state, time: state.time + seconds };
|
|
2461
|
+
if (pending === 0) void enqueue(wholeRun(state.time));
|
|
2462
|
+
},
|
|
2463
|
+
setPipeline(next) {
|
|
2464
|
+
if (next === pipeline) return;
|
|
2465
|
+
pipeline = next;
|
|
2466
|
+
dropStepping();
|
|
2467
|
+
},
|
|
2468
|
+
setSources(sources) {
|
|
2469
|
+
if (sources === options.sources) return;
|
|
2470
|
+
options = { ...options, sources };
|
|
2471
|
+
dropStepping();
|
|
2472
|
+
},
|
|
2473
|
+
setOptions(next) {
|
|
2474
|
+
options = { ...options, ...next };
|
|
2475
|
+
},
|
|
2476
|
+
drawn(run) {
|
|
2477
|
+
const at = run === void 0 ? published.length : published.indexOf(run);
|
|
2478
|
+
if (at <= 0) return;
|
|
2479
|
+
const keep = new Set(run === void 0 ? [] : previewsOf(run));
|
|
2480
|
+
closeBitmaps(published.slice(0, at).flatMap(previewsOf).filter((b) => !keep.has(b)));
|
|
2481
|
+
published = published.slice(at);
|
|
2482
|
+
},
|
|
2483
|
+
subscribe(listener) {
|
|
2484
|
+
listeners.add(listener);
|
|
2485
|
+
return () => {
|
|
2486
|
+
listeners.delete(listener);
|
|
2487
|
+
};
|
|
2488
|
+
},
|
|
2489
|
+
dispose() {
|
|
2490
|
+
this.reset();
|
|
2491
|
+
closeBitmaps(published.flatMap(previewsOf));
|
|
2492
|
+
published = [];
|
|
2493
|
+
}
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
function useEffectRunner(opts) {
|
|
2497
|
+
const { pipeline, sources, evaluators, kinds, previews, previewSize, target, requestFrame, cancelFrame } = opts;
|
|
2498
|
+
const [runner] = useState(() => createEffectRunner(opts));
|
|
2499
|
+
useEffect(() => {
|
|
2500
|
+
runner.setPipeline(pipeline);
|
|
2501
|
+
}, [runner, pipeline]);
|
|
2502
|
+
useEffect(() => {
|
|
2503
|
+
if (sources !== void 0) runner.setSources(sources);
|
|
2504
|
+
}, [runner, sources]);
|
|
2505
|
+
useEffect(() => {
|
|
2506
|
+
runner.setOptions({
|
|
2507
|
+
evaluators,
|
|
2508
|
+
...kinds !== void 0 ? { kinds } : {},
|
|
2509
|
+
...previews !== void 0 ? { previews } : {},
|
|
2510
|
+
...previewSize !== void 0 ? { previewSize } : {}
|
|
2511
|
+
});
|
|
2512
|
+
}, [runner, evaluators, kinds, previews, previewSize]);
|
|
2513
|
+
const state = useSyncExternalStore(runner.subscribe, () => runner.state);
|
|
2514
|
+
const last = useRef(null);
|
|
2515
|
+
const loop = useVisibleRaf((now, self) => {
|
|
2516
|
+
if (!runner.state.playing) return;
|
|
2517
|
+
runner.advance(last.current === null ? 0 : (now - last.current) / 1e3);
|
|
2518
|
+
last.current = now;
|
|
2519
|
+
self.request();
|
|
2520
|
+
}, {
|
|
2521
|
+
...target !== void 0 ? { target } : {},
|
|
2522
|
+
...requestFrame !== void 0 ? { requestFrame } : {},
|
|
2523
|
+
...cancelFrame !== void 0 ? { cancelFrame } : {},
|
|
2524
|
+
onResume: () => {
|
|
2525
|
+
last.current = null;
|
|
2526
|
+
}
|
|
2527
|
+
});
|
|
2528
|
+
useEffect(() => {
|
|
2529
|
+
last.current = null;
|
|
2530
|
+
if (state.playing) loop.request();
|
|
2531
|
+
else loop.cancel();
|
|
2532
|
+
}, [state.playing, loop]);
|
|
2533
|
+
useEffect(() => {
|
|
2534
|
+
runner.drawn(state.run);
|
|
2535
|
+
}, [runner, state.run]);
|
|
2536
|
+
useEffect(() => () => runner.dispose(), [runner]);
|
|
2537
|
+
return { ...state, runner };
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
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
2541
|
//# sourceMappingURL=effect.js.map
|
|
1283
2542
|
//# sourceMappingURL=effect.js.map
|