@oomol-lab/open-flow 0.1.0-beta.82 → 0.1.0-beta.83

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.
@@ -1,8 +1,8 @@
1
1
  import { o as __toESM, t as __commonJSMin } from "./rolldown-runtime-CEgmsyvS.js";
2
- import { A as DropdownMenuLabel, D as DropdownMenuContent, E as DropdownMenuCheckboxItem, G as MenuItem, H as MenuPositioner, J as useContextMenuRootContext, K as MenuGroup, L as DropdownMenuTrigger, M as DropdownMenuRadioItem, N as DropdownMenuSeparator, O as DropdownMenuGroup, Q as waitBranchDescription, R as findRootOwnerId, T as DropdownMenu, U as MenuPortal, V as MenuRoot, W as MenuPopup, X as useMenuRootContext, Y as MenuRootContext, Z as uiLocales, _ as ContentIcon, a as ACTIVE_COMPOSITE_ITEM, at as fr_default, d as PopoverTitle, dt as useFieldItemContext, et as zh_TW_default, f as PopoverTrigger, ft as IconThemeContext, i as CompositeRoot, it as ja_default, j as DropdownMenuRadioGroup, k as DropdownMenuItem, lt as Checkbox, m as ScrollArea, n as canvasDirection, nt as ru_default, o as Popover, ot as en_default, p as NativeScrollArea, q as ContextMenuRootContext, r as Textarea, rt as ko_default, s as PopoverContent, st as I18n, t as wrapLayout, tt as zh_CN_default, ut as useAriaLabelledBy, z as CompositeItem } from "./wrapLayout-DZvW9dBs.js";
2
+ import { $ as waitBranchDescription, A as DropdownMenuItem, B as CompositeItem, D as DropdownMenuCheckboxItem, E as DropdownMenu, G as MenuPopup, H as MenuRoot, J as ContextMenuRootContext, K as MenuItem, M as DropdownMenuRadioGroup, N as DropdownMenuRadioItem, O as DropdownMenuContent, P as DropdownMenuSeparator, Q as uiLocales, R as DropdownMenuTrigger, U as MenuPositioner, W as MenuPortal, X as MenuRootContext, Y as useContextMenuRootContext, Z as useMenuRootContext, a as CompositeRoot, at as ja_default, c as PopoverContent, ct as I18n, dt as useAriaLabelledBy, f as PopoverTitle, ft as useFieldItemContext, h as ScrollArea, i as Textarea, it as ko_default, j as DropdownMenuLabel, k as DropdownMenuGroup, m as NativeScrollArea, n as wrapLayout, nt as zh_CN_default, o as ACTIVE_COMPOSITE_ITEM, ot as fr_default, p as PopoverTrigger, pt as IconThemeContext, q as MenuGroup, r as canvasDirection, rt as ru_default, s as Popover, st as en_default, t as layoutAxes, tt as zh_TW_default, ut as Checkbox, v as ContentIcon, z as findRootOwnerId } from "./wrapLayout-C4moMKgb.js";
3
3
  import { a as derive, c as strictEqual, i as compute, l as val, n as attachSetter, r as combine, s as readonlyVal } from "./dist-bO8nvHFH.js";
4
4
  import { c as useLang, i as isString, l as useTranslate, n as isBoolean, o as I18nProvider, r as isPlainObject, t as isArray$1, u as useVal } from "./dist-BFmpkX4P.js";
5
- import { A as string, Bn as transitionStatusMapping, C as literal, Cr as cn$1, Dn as getTarget, En as contains, Er as useRenderElement, Fn as createLucideIcon, Fr as formatErrorMessage, J as SHIFT, Kn as cancelOpen, Lr as useIsoLayoutEffect, Mn as ownerDocument, Mr as useButton, Nn as useLabelableId, Or as EMPTY_OBJECT, Sn as stopEvent$1, T as number, Tr as clsx, Un as useValueChanged, Wn as createChangeEventDetails, Yt as addEventListener, _r as fieldValidityMapping, br as useControlled, cr as triggerPress, dr as useFormContext, fr as useRegisterFieldControl, gt as serializeValue, hr as useId$1, i as TooltipTrigger, jr as useMergedRefs, k as strictObject, kr as NOOP, mr as useBaseUiId, n as TooltipContent, p as timeZoneOffsetMinutes, pr as useFieldRootContext, pt as pressableTriggerOpenStateMapping, r as TooltipProvider, rr as none$1, sn as useTimeout, t as Tooltip, tn as require_with_selector, ur as useLabelableContext, vr as visuallyHidden, xr as Button, y as discriminatedUnion, yr as visuallyHiddenInput, zr as useStableCallback } from "./tooltip-BDcVTNpu.js";
5
+ import { A as string, Bn as transitionStatusMapping, Br as useStableCallback, C as literal, Cr as cn$1, Dn as getTarget, En as contains, Er as useRenderElement, Fn as createLucideIcon, Fr as formatErrorMessage, J as SHIFT, Kn as cancelOpen, Lr as useIsoLayoutEffect, Mn as ownerDocument, Mr as useButton, Nn as useLabelableId, Or as EMPTY_OBJECT, Sn as stopEvent$1, T as number, Tr as clsx, Un as useValueChanged, Wn as createChangeEventDetails, Yt as addEventListener, _r as fieldValidityMapping, br as useControlled, cr as triggerPress, dr as useFormContext, fr as useRegisterFieldControl, gt as serializeValue, hr as useId$1, i as TooltipTrigger, jr as useMergedRefs, k as strictObject, kr as NOOP, mr as useBaseUiId, n as TooltipContent, p as timeZoneOffsetMinutes, pr as useFieldRootContext, pt as pressableTriggerOpenStateMapping, r as TooltipProvider, rr as none$1, sn as useTimeout, t as Tooltip, tn as require_with_selector, ur as useLabelableContext, vr as visuallyHidden, xr as Button, y as discriminatedUnion, yr as visuallyHiddenInput } from "./tooltip-DGj5pudg.js";
6
6
  import { r as resolveUiLanguage } from "./languages-gYzoQqCi.js";
7
7
  import * as React from "react";
8
8
  import reactExports, { Children, Suspense, createContext, forwardRef, isValidElement, lazy, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
@@ -33,6 +33,234 @@ function useFieldsetRootContext(optional = false) {
33
33
  return context;
34
34
  }
35
35
  //#endregion
36
+ //#region src/render/common/png.ts
37
+ var flowPngAspectRatios = [
38
+ "auto",
39
+ "1:1",
40
+ "4:3",
41
+ "3:4",
42
+ "16:9",
43
+ "9:16"
44
+ ];
45
+ function getFlowPngAspectRatio(value) {
46
+ if (value == null || value === "auto") return void 0;
47
+ if (!flowPngAspectRatios.includes(value)) throw new TypeError("Unsupported PNG canvas aspect ratio.");
48
+ const [width, height] = value.split(":").map(Number);
49
+ return [width, height];
50
+ }
51
+ //#endregion
52
+ //#region ../../node_modules/.bun/@wopjs+disposable@0.1.24/node_modules/@wopjs/disposable/dist/index.mjs
53
+ var isFn = (value) => !!(value && value.constructor && value.call && value.apply);
54
+ /**
55
+ * Check if a value is a {@link DisposableType}.
56
+ * @param value The value to check.
57
+ * @returns True if the value is a {@link DisposableType}, false otherwise.
58
+ */
59
+ var isDisposable = (value) => isFn(value) || isFn(value?.dispose);
60
+ /**
61
+ * Dispose a disposable object or a disposer function. Log the error if any.
62
+ * @param disposable A disposable object or a disposer function. Do nothing otherwise.
63
+ */
64
+ function dispose(disposable) {
65
+ try {
66
+ if (disposable) {
67
+ if (isFn(disposable.dispose)) disposable.dispose();
68
+ else if (isFn(disposable)) disposable();
69
+ }
70
+ } catch (e) {
71
+ console.error(e);
72
+ }
73
+ }
74
+ var isAbortable = (disposable) => isDisposable(disposable) && isFn(disposable.abortable);
75
+ /**
76
+ * Create a {@link DisposableStore} that manages {@link Disposer}s and {@link IDisposable}s.
77
+ *
78
+ * A {@link DisposableStore} is also a {@link Disposer}, which means it can be the `dispose` method of an {@link IDisposable}.
79
+ *
80
+ * A {@link DisposableStore} is also an {@link IDisposable}, which means it can be managed by another {@link DisposableStore}.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * import { type IDisposable, disposableStore } from "@wopjs/disposable";
85
+ *
86
+ * class A implements IDisposable {
87
+ * dispose = disposableStore()
88
+ * constructor() {
89
+ * this.dispose.add(...);
90
+ * }
91
+ * }
92
+ *
93
+ * class B implements IDisposable {
94
+ * dispose = disposableStore()
95
+ * a = this.dispose.add(new A())
96
+ * constructor() {
97
+ * this.dispose.add(...);
98
+ * }
99
+ * }
100
+ *
101
+ * const b = new B();
102
+ * b.dispose(); // dispose both `b` and `b.a`.
103
+ * ```
104
+ *
105
+ * @param disposables Optional array of {@link DisposableType}s added to the store.
106
+ * @returns A disposable store.
107
+ */
108
+ function disposableStore(disposables) {
109
+ let isDisposing;
110
+ function disposableStore() {
111
+ if (!isDisposing) {
112
+ isDisposing = 1;
113
+ isDisposing = disposableStore._disposables_ = disposableStore._disposables_?.forEach(dispose);
114
+ }
115
+ }
116
+ disposableStore.dispose = disposableStore;
117
+ disposableStore.size = size;
118
+ disposableStore.has = has;
119
+ disposableStore.add = add;
120
+ disposableStore.make = make;
121
+ disposableStore.remove = remove$1;
122
+ disposableStore.flush = flush;
123
+ if (disposables) disposableStore.add(disposables);
124
+ return disposableStore;
125
+ }
126
+ function _addOne(disposable) {
127
+ if (!this._disposables_?.has(disposable)) {
128
+ (this._disposables_ ??= /* @__PURE__ */ new Set()).add(disposable);
129
+ if (isAbortable(disposable)) disposable.abortable(remove$1.bind(this, disposable));
130
+ }
131
+ }
132
+ function add(disposables) {
133
+ Array.isArray(disposables) ? disposables.forEach(_addOne, this) : _addOne.call(this, disposables);
134
+ return disposables;
135
+ }
136
+ function flush(disposable) {
137
+ dispose(disposable ? this.remove(disposable) && disposable : this);
138
+ }
139
+ function has(disposable) {
140
+ return !!this._disposables_?.has(disposable);
141
+ }
142
+ function make(executor) {
143
+ const disposable = executor();
144
+ return disposable && this.add(disposable);
145
+ }
146
+ function remove$1(disposable) {
147
+ return !!this._disposables_?.delete(disposable);
148
+ }
149
+ function size() {
150
+ return this._disposables_?.size || 0;
151
+ }
152
+ //#endregion
153
+ //#region ../../node_modules/.bun/value-enhancer@5.7.2/node_modules/value-enhancer/dist/collections.mjs
154
+ var ReactiveMapImpl = class extends Map {
155
+ constructor(entries, config) {
156
+ super();
157
+ if (config) this.#onDeleted = config.onDeleted;
158
+ const [$, set$] = readonlyVal(this, { equal: false });
159
+ this.$ = $;
160
+ this.#notify = () => set$(this);
161
+ if (entries) for (const [key, value] of entries) this.set(key, value);
162
+ }
163
+ $;
164
+ #notify;
165
+ #onDeleted;
166
+ #delete(key) {
167
+ if (this.#onDeleted) {
168
+ if (super.has(key)) {
169
+ const value = super.get(key);
170
+ super.delete(key);
171
+ this.#onDeleted(value);
172
+ return true;
173
+ }
174
+ return false;
175
+ }
176
+ return super.delete(key);
177
+ }
178
+ #clear() {
179
+ if (this.#onDeleted) {
180
+ const deleted = new Set(this.values());
181
+ super.clear();
182
+ for (const value of deleted) this.#onDeleted(value);
183
+ } else super.clear();
184
+ }
185
+ delete(key) {
186
+ const deleted = this.#delete(key);
187
+ if (deleted) this.#notify();
188
+ return deleted;
189
+ }
190
+ batchDelete(keys) {
191
+ let deleted = false;
192
+ for (const key of keys) deleted = this.#delete(key) || deleted;
193
+ if (deleted) this.#notify();
194
+ return deleted;
195
+ }
196
+ clear() {
197
+ if (this.size > 0) {
198
+ this.#clear();
199
+ this.#notify();
200
+ }
201
+ }
202
+ #set(key, value) {
203
+ if (this.has(key)) {
204
+ const oldValue = this.get(key);
205
+ if (strictEqual(oldValue, value)) return false;
206
+ super.set(key, value);
207
+ if (this.#onDeleted) this.#onDeleted(oldValue);
208
+ } else super.set(key, value);
209
+ return true;
210
+ }
211
+ set(key, value) {
212
+ if (this.#set(key, value)) this.#notify();
213
+ return this;
214
+ }
215
+ batchSet(entries) {
216
+ let isDirty = false;
217
+ for (const [key, value] of entries) isDirty = this.#set(key, value) || isDirty;
218
+ if (isDirty) this.#notify();
219
+ return this;
220
+ }
221
+ rename(oldKey, newKey) {
222
+ if (!strictEqual(oldKey, newKey)) {
223
+ const value = this.get(oldKey);
224
+ if (super.delete(oldKey)) {
225
+ this.set(newKey, value);
226
+ return true;
227
+ }
228
+ }
229
+ return false;
230
+ }
231
+ replace(entries) {
232
+ const oldMap = new Map(this);
233
+ const deletedValues = new Set(this.values());
234
+ let hasNewValue = false;
235
+ super.clear();
236
+ for (const [key, value] of entries) {
237
+ super.set(key, value);
238
+ deletedValues.delete(value);
239
+ hasNewValue = hasNewValue || !oldMap.has(key) || !Object.is(oldMap.get(key), value);
240
+ oldMap.delete(key);
241
+ }
242
+ if (hasNewValue || oldMap.size > 0 || deletedValues.size > 0) {
243
+ if (this.#onDeleted) for (const value of deletedValues) this.#onDeleted(value);
244
+ this.#notify();
245
+ }
246
+ return deletedValues.values();
247
+ }
248
+ toJSON() {
249
+ const result = {};
250
+ for (const [key, value] of this) if (key != null) {
251
+ const k = String(key);
252
+ const v = value;
253
+ result[k] = v && v.toJSON ? v.toJSON(k) : v;
254
+ }
255
+ return result;
256
+ }
257
+ dispose() {
258
+ this.$.dispose();
259
+ if (this.size > 0) this.#clear();
260
+ }
261
+ };
262
+ var reactiveMap = (entries, config) => new ReactiveMapImpl(entries, config);
263
+ //#endregion
36
264
  //#region ../../node_modules/.bun/@dagrejs+dagre@3.1.1/node_modules/@dagrejs/dagre/dist/dagre.esm.js
37
265
  var Te = Object.defineProperty;
38
266
  var In = (e, n, t) => n in e ? Te(e, n, {
@@ -2269,105 +2497,463 @@ function _e(e) {
2269
2497
  }
2270
2498
  /*! For license information please see dagre.esm.js.LEGAL.txt */
2271
2499
  //#endregion
2272
- //#region ../../node_modules/.bun/@wopjs+disposable@0.1.24/node_modules/@wopjs/disposable/dist/index.mjs
2273
- var isFn = (value) => !!(value && value.constructor && value.call && value.apply);
2274
- /**
2275
- * Check if a value is a {@link DisposableType}.
2276
- * @param value The value to check.
2277
- * @returns True if the value is a {@link DisposableType}, false otherwise.
2278
- */
2279
- var isDisposable = (value) => isFn(value) || isFn(value?.dispose);
2280
- /**
2281
- * Dispose a disposable object or a disposer function. Log the error if any.
2282
- * @param disposable A disposable object or a disposer function. Do nothing otherwise.
2283
- */
2284
- function dispose(disposable) {
2285
- try {
2286
- if (disposable) {
2287
- if (isFn(disposable.dispose)) disposable.dispose();
2288
- else if (isFn(disposable)) disposable();
2500
+ //#region src/canvas/browser/base/routeLayout.ts
2501
+ var clearance = 16;
2502
+ var portOffset = 24;
2503
+ /** Orthogonal channels around the measured cards, in primary/cross coordinates. */
2504
+ function routeLayout(nodes, edges) {
2505
+ const byId = new Map(nodes.map((node) => [node.id, node]));
2506
+ const connections = edges.map((edge) => {
2507
+ const source = byId.get(edge.source);
2508
+ const target = byId.get(edge.target);
2509
+ const output = {
2510
+ x: source.x + (edge.sourceOffset?.x ?? source.width),
2511
+ y: source.y + (edge.sourceOffset?.y ?? source.height / 2)
2512
+ };
2513
+ const input = {
2514
+ x: target.x + (edge.targetOffset?.x ?? 0),
2515
+ y: target.y + (edge.targetOffset?.y ?? target.height / 2)
2516
+ };
2517
+ const offset = output.x < input.x ? Math.min(portOffset, (input.x - output.x) / 2) : portOffset;
2518
+ return {
2519
+ id: edge.id,
2520
+ source: output,
2521
+ target: input,
2522
+ returnSide: input.x <= output.x ? Math.sign(input.y - output.y) : 0,
2523
+ start: {
2524
+ x: output.x + offset,
2525
+ y: output.y
2526
+ },
2527
+ end: {
2528
+ x: input.x - offset,
2529
+ y: input.y
2530
+ }
2531
+ };
2532
+ });
2533
+ const obstacles = nodes.map((node) => ({
2534
+ left: node.x - clearance,
2535
+ right: node.x + node.width + clearance,
2536
+ top: node.y - clearance,
2537
+ bottom: node.y + node.height + clearance
2538
+ }));
2539
+ const routes = /* @__PURE__ */ new Map();
2540
+ const pending = connections.filter((edge) => {
2541
+ if (edge.start.x > edge.end.x) return true;
2542
+ const middle = (edge.start.x + edge.end.x) / 2;
2543
+ const points = [
2544
+ edge.start,
2545
+ {
2546
+ x: middle,
2547
+ y: edge.start.y
2548
+ },
2549
+ {
2550
+ x: middle,
2551
+ y: edge.end.y
2552
+ },
2553
+ edge.end
2554
+ ];
2555
+ for (let index = 1; index < points.length; index++) {
2556
+ const a = points[index - 1];
2557
+ const b = points[index];
2558
+ if (obstacles.some((rect) => a.x === b.x ? a.x > rect.left && a.x < rect.right && Math.max(a.y, b.y) > rect.top && Math.min(a.y, b.y) < rect.bottom : a.y > rect.top && a.y < rect.bottom && Math.max(a.x, b.x) > rect.left && Math.min(a.x, b.x) < rect.right)) return true;
2289
2559
  }
2290
- } catch (e) {
2291
- console.error(e);
2292
- }
2293
- }
2294
- var isAbortable = (disposable) => isDisposable(disposable) && isFn(disposable.abortable);
2295
- /**
2296
- * Create a {@link DisposableStore} that manages {@link Disposer}s and {@link IDisposable}s.
2297
- *
2298
- * A {@link DisposableStore} is also a {@link Disposer}, which means it can be the `dispose` method of an {@link IDisposable}.
2299
- *
2300
- * A {@link DisposableStore} is also an {@link IDisposable}, which means it can be managed by another {@link DisposableStore}.
2301
- *
2302
- * @example
2303
- * ```ts
2304
- * import { type IDisposable, disposableStore } from "@wopjs/disposable";
2305
- *
2306
- * class A implements IDisposable {
2307
- * dispose = disposableStore()
2308
- * constructor() {
2309
- * this.dispose.add(...);
2310
- * }
2311
- * }
2312
- *
2313
- * class B implements IDisposable {
2314
- * dispose = disposableStore()
2315
- * a = this.dispose.add(new A())
2316
- * constructor() {
2317
- * this.dispose.add(...);
2318
- * }
2319
- * }
2320
- *
2321
- * const b = new B();
2322
- * b.dispose(); // dispose both `b` and `b.a`.
2323
- * ```
2324
- *
2325
- * @param disposables Optional array of {@link DisposableType}s added to the store.
2326
- * @returns A disposable store.
2327
- */
2328
- function disposableStore(disposables) {
2329
- let isDisposing;
2330
- function disposableStore() {
2331
- if (!isDisposing) {
2332
- isDisposing = 1;
2333
- isDisposing = disposableStore._disposables_ = disposableStore._disposables_?.forEach(dispose);
2560
+ routes.set(edge.id, [
2561
+ edge.source,
2562
+ points[1],
2563
+ points[2],
2564
+ edge.target
2565
+ ]);
2566
+ return false;
2567
+ });
2568
+ if (!pending.length) return routes;
2569
+ const xs = [.../* @__PURE__ */ new Set([...obstacles.flatMap((rect) => [rect.left, rect.right]), ...pending.flatMap((edge) => [edge.start.x, edge.end.x])])].toSorted((a, b) => a - b);
2570
+ const ys = [.../* @__PURE__ */ new Set([...obstacles.flatMap((rect) => [rect.top, rect.bottom]), ...pending.flatMap((edge) => [edge.start.y, edge.end.y])])].toSorted((a, b) => a - b);
2571
+ const xIndex = new Map(xs.map((value, index) => [value, index]));
2572
+ const yIndex = new Map(ys.map((value, index) => [value, index]));
2573
+ const columns = xs.length;
2574
+ const size = columns * ys.length;
2575
+ const horizontal = new Uint8Array(size);
2576
+ const vertical = new Uint8Array(size);
2577
+ for (const rect of obstacles) {
2578
+ const left = xIndex.get(rect.left);
2579
+ const right = xIndex.get(rect.right);
2580
+ const top = yIndex.get(rect.top);
2581
+ const bottom = yIndex.get(rect.bottom);
2582
+ for (let y = top + 1; y < bottom; y++) for (let x = left; x < right; x++) horizontal[y * columns + x] = 1;
2583
+ for (let x = left + 1; x < right; x++) for (let y = top; y < bottom; y++) vertical[y * columns + x] = 1;
2584
+ }
2585
+ const distances = new Float64Array(size * 2);
2586
+ const previous = new Int32Array(size * 2);
2587
+ const visited = new Uint32Array(size * 2);
2588
+ for (const [routeIndex, edge] of pending.entries()) {
2589
+ const generation = routeIndex + 1;
2590
+ const start = yIndex.get(edge.start.y) * columns + xIndex.get(edge.start.x);
2591
+ const end = yIndex.get(edge.end.y) * columns + xIndex.get(edge.end.x);
2592
+ const queue = [];
2593
+ const heuristic = (vertex) => Math.abs(xs[vertex % columns] - edge.end.x) + Math.abs(ys[Math.floor(vertex / columns)] - edge.end.y);
2594
+ distances[start * 2] = 0;
2595
+ previous[start * 2] = -1;
2596
+ visited[start * 2] = generation;
2597
+ enqueue(queue, {
2598
+ state: start * 2,
2599
+ score: heuristic(start)
2600
+ });
2601
+ let finish = -1;
2602
+ while (queue.length) {
2603
+ const entry = dequeue(queue);
2604
+ const vertex = Math.floor(entry.state / 2);
2605
+ if (entry.score > distances[entry.state] + heuristic(vertex) + 1e-6) continue;
2606
+ if (vertex === end) {
2607
+ finish = entry.state;
2608
+ break;
2609
+ }
2610
+ const x = vertex % columns;
2611
+ const y = Math.floor(vertex / columns);
2612
+ const moves = [
2613
+ x > 0 && !horizontal[vertex - 1] ? vertex - 1 : -1,
2614
+ x + 1 < columns && !horizontal[vertex] ? vertex + 1 : -1,
2615
+ y > 0 && !vertical[vertex - columns] ? vertex - columns : -1,
2616
+ y + 1 < ys.length && !vertical[vertex] ? vertex + columns : -1
2617
+ ];
2618
+ for (const [index, next] of moves.entries()) {
2619
+ if (next < 0 || vertex === start && index === 0 || next === end && index === 0) continue;
2620
+ if (edge.returnSide * (ys[Math.floor(next / columns)] - edge.start.y) < -1e-6) continue;
2621
+ const axis = index < 2 ? 0 : 1;
2622
+ const state = next * 2 + axis;
2623
+ const length = Math.abs(xs[next % columns] - xs[x]) + Math.abs(ys[Math.floor(next / columns)] - ys[y]);
2624
+ const distance = distances[entry.state] + length + (entry.state % 2 === axis ? 0 : 8);
2625
+ if (visited[state] === generation && distance >= distances[state]) continue;
2626
+ visited[state] = generation;
2627
+ distances[state] = distance;
2628
+ previous[state] = entry.state;
2629
+ enqueue(queue, {
2630
+ state,
2631
+ score: distance + heuristic(next)
2632
+ });
2633
+ }
2634
+ }
2635
+ if (finish < 0) throw new Error("Could not route the compact workflow connection.");
2636
+ const points = [];
2637
+ for (let state = finish; state >= 0; state = previous[state]) {
2638
+ const vertex = Math.floor(state / 2);
2639
+ points.push({
2640
+ x: xs[vertex % columns],
2641
+ y: ys[Math.floor(vertex / columns)]
2642
+ });
2334
2643
  }
2644
+ points.reverse();
2645
+ const turns = points.filter((point, index) => {
2646
+ const before = points[index - 1];
2647
+ const after = points[index + 1];
2648
+ return !before || !after || !(before.x === point.x && point.x === after.x || before.y === point.y && point.y === after.y);
2649
+ });
2650
+ routes.set(edge.id, [
2651
+ edge.source,
2652
+ ...turns,
2653
+ edge.target
2654
+ ]);
2335
2655
  }
2336
- disposableStore.dispose = disposableStore;
2337
- disposableStore.size = size;
2338
- disposableStore.has = has;
2339
- disposableStore.add = add;
2340
- disposableStore.make = make;
2341
- disposableStore.remove = remove$1;
2342
- disposableStore.flush = flush;
2343
- if (disposables) disposableStore.add(disposables);
2344
- return disposableStore;
2656
+ return routes;
2657
+ }
2658
+ function enqueue(queue, entry) {
2659
+ let index = queue.length;
2660
+ queue.push(entry);
2661
+ while (index > 0) {
2662
+ const parent = Math.floor((index - 1) / 2);
2663
+ if (queue[parent].score <= entry.score) break;
2664
+ queue[index] = queue[parent];
2665
+ index = parent;
2666
+ }
2667
+ queue[index] = entry;
2668
+ }
2669
+ function dequeue(queue) {
2670
+ const first = queue[0];
2671
+ const last = queue.pop();
2672
+ if (!queue.length) return first;
2673
+ let index = 0;
2674
+ while (index * 2 + 1 < queue.length) {
2675
+ let child = index * 2 + 1;
2676
+ if (child + 1 < queue.length && queue[child + 1].score < queue[child].score) child++;
2677
+ if (last.score <= queue[child].score) break;
2678
+ queue[index] = queue[child];
2679
+ index = child;
2680
+ }
2681
+ queue[index] = last;
2682
+ return first;
2345
2683
  }
2346
- function _addOne(disposable) {
2347
- if (!this._disposables_?.has(disposable)) {
2348
- (this._disposables_ ??= /* @__PURE__ */ new Set()).add(disposable);
2349
- if (isAbortable(disposable)) disposable.abortable(remove$1.bind(this, disposable));
2684
+ //#endregion
2685
+ //#region src/canvas/browser/base/compactLayout.ts
2686
+ function bounds(nodes, routes = /* @__PURE__ */ new Map()) {
2687
+ const points = [...routes.values()].flat();
2688
+ for (const node of nodes) points.push(node, {
2689
+ x: node.x + node.width,
2690
+ y: node.y + node.height
2691
+ });
2692
+ const x = Math.min(...points.map((point) => point.x));
2693
+ const y = Math.min(...points.map((point) => point.y));
2694
+ return {
2695
+ x,
2696
+ y,
2697
+ width: Math.max(...points.map((point) => point.x)) - x,
2698
+ height: Math.max(...points.map((point) => point.y)) - y
2699
+ };
2700
+ }
2701
+ /** Place branches at their earliest dependency level, rather than delaying short
2702
+ * branches until the longest sibling reaches a shared join. Cycles remain one
2703
+ * component whose internal order follows the measured base layout. */
2704
+ function dependencyLevels(nodes, edges, gap) {
2705
+ const graph = new ie.Graph();
2706
+ for (const node of nodes) graph.setNode(node.id);
2707
+ for (const edge of edges) graph.setEdge(edge.source, edge.target);
2708
+ const components = ie.alg.tarjan(graph);
2709
+ const componentOf = new Map(components.flatMap((members, index) => members.map((id) => [id, String(index)])));
2710
+ const dependencies = new ie.Graph();
2711
+ for (let index = 0; index < components.length; index++) dependencies.setNode(String(index));
2712
+ for (const edge of edges) {
2713
+ const source = componentOf.get(edge.source);
2714
+ const target = componentOf.get(edge.target);
2715
+ if (source !== target) dependencies.setEdge(source, target);
2716
+ }
2717
+ const byId = new Map(nodes.map((node) => [node.id, node]));
2718
+ const levels = /* @__PURE__ */ new Map();
2719
+ const ends = /* @__PURE__ */ new Map();
2720
+ for (const id of ie.alg.topsort(dependencies)) {
2721
+ const start = Math.max(0, ...(dependencies.predecessors(id) ?? []).map((parent) => ends.get(parent) + 1));
2722
+ const members = components[Number(id)].map((member) => byId.get(member)).toSorted((a, b) => a.column - b.column || a.y - b.y);
2723
+ for (const [index, member] of members.entries()) levels.set(member.id, start + index);
2724
+ ends.set(id, start + members.length - 1);
2725
+ }
2726
+ const isolated = nodes.filter((node) => graph.nodeEdges(node.id)?.length === 0);
2727
+ const isolatedColumns = [...new Set(isolated.map((node) => node.column))].toSorted((a, b) => a - b);
2728
+ for (const node of isolated) levels.set(node.id, isolatedColumns.indexOf(node.column));
2729
+ let primary = 0;
2730
+ const groups = [...Map.groupBy(nodes, (node) => levels.get(node.id)).entries()].toSorted(([a], [b]) => a - b);
2731
+ for (const [level, members] of groups) {
2732
+ const size = Math.max(...members.map((node) => node.width));
2733
+ for (const node of members) Object.assign(node, {
2734
+ column: level,
2735
+ x: primary + (size - node.width) / 2
2736
+ });
2737
+ primary += size + gap;
2350
2738
  }
2351
2739
  }
2352
- function add(disposables) {
2353
- Array.isArray(disposables) ? disposables.forEach(_addOne, this) : _addOne.call(this, disposables);
2354
- return disposables;
2355
- }
2356
- function flush(disposable) {
2357
- dispose(disposable ? this.remove(disposable) && disposable : this);
2358
- }
2359
- function has(disposable) {
2360
- return !!this._disposables_?.has(disposable);
2361
- }
2362
- function make(executor) {
2363
- const disposable = executor();
2364
- return disposable && this.add(disposable);
2365
- }
2366
- function remove$1(disposable) {
2367
- return !!this._disposables_?.delete(disposable);
2740
+ /** Compact each connected subgraph, then wrap the forest to the requested ratio. */
2741
+ function compactLayout(graph, insets = {
2742
+ width: 0,
2743
+ height: 0
2744
+ }, aspectRatio = 1) {
2745
+ if (!graph.nodes.length) return {
2746
+ positions: {},
2747
+ routes: /* @__PURE__ */ new Map(),
2748
+ bounds: {
2749
+ x: 0,
2750
+ y: 0,
2751
+ width: 320,
2752
+ height: 120
2753
+ }
2754
+ };
2755
+ const connected = new ie.Graph();
2756
+ for (const node of graph.nodes) connected.setNode(node.id);
2757
+ for (const edge of graph.edges) connected.setEdge(edge.source, edge.target);
2758
+ const members = ie.alg.components(connected);
2759
+ if (members.length === 1) return compactComponent(graph, insets, aspectRatio);
2760
+ const componentOf = new Map(members.flatMap((ids, index) => ids.map((id) => [id, index])));
2761
+ const edges = Map.groupBy(graph.edges, (edge) => componentOf.get(edge.source));
2762
+ return packComponents([...Map.groupBy(graph.nodes, (node) => componentOf.get(node.id))].map(([id, nodes]) => ({
2763
+ nodes,
2764
+ edges: edges.get(id) ?? [],
2765
+ bounds: bounds(nodes)
2766
+ })).toSorted((a, b) => a.bounds.x - b.bounds.x || a.bounds.y - b.bounds.y).map((component) => component.nodes.length === 1 && !component.edges.length ? {
2767
+ positions: { [component.nodes[0].id]: component.nodes[0] },
2768
+ routes: /* @__PURE__ */ new Map(),
2769
+ bounds: component.bounds
2770
+ } : compactComponent(component, {
2771
+ width: 0,
2772
+ height: 0
2773
+ }, aspectRatio)), insets, aspectRatio);
2774
+ }
2775
+ /** Keep reading order, with a larger gap between independent trees than within one. */
2776
+ function packComponents(layouts, insets, aspectRatio) {
2777
+ const gap = 96;
2778
+ const largestWidth = Math.max(...layouts.map((layout) => layout.bounds.width));
2779
+ const contentArea = layouts.reduce((sum, layout) => sum + layout.bounds.width * layout.bounds.height, 0);
2780
+ const limits = /* @__PURE__ */ new Set([largestWidth, Math.max(largestWidth, Math.sqrt(contentArea * aspectRatio))]);
2781
+ let prefixWidth = -96;
2782
+ for (const layout of layouts) {
2783
+ prefixWidth += layout.bounds.width + gap;
2784
+ limits.add(Math.max(largestWidth, prefixWidth));
2785
+ }
2786
+ let best;
2787
+ for (const limit of limits) {
2788
+ let x = 0;
2789
+ let y = 0;
2790
+ let width = 0;
2791
+ let rowHeight = 0;
2792
+ let rows = 1;
2793
+ const positions = layouts.map((layout) => {
2794
+ if (x > 0 && x + layout.bounds.width > limit + 1e-6) {
2795
+ y += rowHeight + gap;
2796
+ x = rowHeight = 0;
2797
+ rows++;
2798
+ }
2799
+ const position = {
2800
+ x: x - layout.bounds.x,
2801
+ y: y - layout.bounds.y
2802
+ };
2803
+ width = Math.max(width, x + layout.bounds.width);
2804
+ rowHeight = Math.max(rowHeight, layout.bounds.height);
2805
+ x += layout.bounds.width + gap;
2806
+ return position;
2807
+ });
2808
+ const height = y + rowHeight;
2809
+ const score = Math.max((width + insets.width) / aspectRatio, height + insets.height);
2810
+ const area = (width + insets.width) * (height + insets.height);
2811
+ if (!best || score < best.score - 1e-9 || Math.abs(score - best.score) < 1e-9 && (area < best.area || area === best.area && rows < best.rows)) best = {
2812
+ positions,
2813
+ width,
2814
+ height,
2815
+ score,
2816
+ area,
2817
+ rows
2818
+ };
2819
+ }
2820
+ const translate = (point, index) => ({
2821
+ x: point.x + best.positions[index].x,
2822
+ y: point.y + best.positions[index].y
2823
+ });
2824
+ return {
2825
+ positions: Object.fromEntries(layouts.flatMap((layout, index) => Object.entries(layout.positions).map(([id, point]) => [id, translate(point, index)]))),
2826
+ routes: new Map(layouts.flatMap((layout, index) => [...layout.routes].map(([id, points]) => [id, points.map((point) => translate(point, index))]))),
2827
+ bounds: {
2828
+ x: 0,
2829
+ y: 0,
2830
+ width: best.width,
2831
+ height: best.height
2832
+ }
2833
+ };
2368
2834
  }
2369
- function size() {
2370
- return this._disposables_?.size || 0;
2835
+ /** Bend dependency paths continuously; the ports retain their original orientation. */
2836
+ function compactComponent(graph, insets, aspectRatio) {
2837
+ const normalized = graph.nodes.map((node) => ({
2838
+ ...node,
2839
+ ...layoutAxes(node, "vertical"),
2840
+ width: node.height,
2841
+ height: node.width
2842
+ }));
2843
+ dependencyLevels(normalized, graph.edges, 48);
2844
+ const incoming = new Set(graph.edges.map((edge) => edge.target));
2845
+ const outgoing = new Set(graph.edges.map((edge) => edge.source));
2846
+ const roots = normalized.filter((node) => !incoming.has(node.id) && outgoing.has(node.id));
2847
+ const centers = (roots.length ? roots : normalized).map((node) => node.y + node.height / 2);
2848
+ const center = centers.reduce((sum, value) => sum + value, 0) / centers.length;
2849
+ const gap = 48;
2850
+ const pathNodes = normalized.filter((node) => incoming.has(node.id) || outgoing.has(node.id));
2851
+ const crossSize = Math.max(...(pathNodes.length ? pathNodes : normalized).map((node) => node.height));
2852
+ const bow = crossSize * .75;
2853
+ const separation = crossSize + 48;
2854
+ const bucketSize = Math.max(...normalized.map((node) => node.width)) + gap;
2855
+ const byId = new Map(normalized.map((node) => [node.id, node]));
2856
+ const forwardEdges = graph.edges.filter((edge) => byId.get(edge.source).column < byId.get(edge.target).column);
2857
+ const parents = Map.groupBy(forwardEdges, (edge) => edge.target);
2858
+ const children = Map.groupBy(forwardEdges, (edge) => edge.source);
2859
+ const ordered = normalized.toSorted((a, b) => a.column - b.column || a.y - b.y);
2860
+ const remaining = /* @__PURE__ */ new Map();
2861
+ for (const node of ordered.toReversed()) remaining.set(node.id, 1 + Math.max(0, ...(children.get(node.id) ?? []).filter((edge) => parents.get(edge.target)?.length === 1).map((edge) => remaining.get(edge.target))));
2862
+ const rankEnds = new Map([...Map.groupBy(normalized, (node) => node.column)].map(([rank, nodes]) => [rank, Math.max(...nodes.map((node) => node.x + node.width))]));
2863
+ const spans = /* @__PURE__ */ new Set([Infinity, ...rankEnds.values()]);
2864
+ let best;
2865
+ for (const span of spans) {
2866
+ const placed = /* @__PURE__ */ new Map();
2867
+ const buckets = /* @__PURE__ */ new Map();
2868
+ let turns = 0;
2869
+ for (const original of ordered) {
2870
+ const upstream = parents.get(original.id)?.map((edge) => placed.get(edge.source)) ?? [];
2871
+ const parent = upstream[0];
2872
+ const side = original.y + original.height / 2 < center - 1e-6 ? -1 : 1;
2873
+ let primary = upstream.length ? Math.max(...upstream.map(({ node }) => {
2874
+ const source = byId.get(node.id);
2875
+ return node.x + rankEnds.get(source.column) - source.x;
2876
+ })) + gap : original.x;
2877
+ let shift = 0;
2878
+ let start = 0;
2879
+ if (upstream.length > 1) {
2880
+ primary = Math.max(...upstream.map((entry) => entry.extent)) + gap;
2881
+ shift = upstream.reduce((sum, { node }) => sum + node.y + node.height / 2, 0) / upstream.length - original.y - original.height / 2;
2882
+ start = primary;
2883
+ } else if (parent) {
2884
+ shift = parent.shift;
2885
+ start = parent.start;
2886
+ if ((children.get(parent.node.id)?.length ?? 0) > 1) {
2887
+ shift = parent.node.y - byId.get(parent.node.id).y;
2888
+ start = primary;
2889
+ } else if (primary + original.width > span && remaining.get(original.id) > 2) {
2890
+ primary = Math.max(0, start - span / 3);
2891
+ shift = parent.shift + side * separation;
2892
+ start = primary;
2893
+ turns++;
2894
+ }
2895
+ }
2896
+ const progress = Number.isFinite(span) && span > start ? Math.min(1, Math.max(0, (primary - start) / (span - start))) : 0;
2897
+ const drift = side * bow * Math.sin(progress * Math.PI);
2898
+ const node = {
2899
+ ...original,
2900
+ x: primary,
2901
+ y: original.y + shift + drift
2902
+ };
2903
+ const firstBucket = Math.floor(node.x / bucketSize);
2904
+ const lastBucket = Math.floor((node.x + node.width + gap) / bucketSize);
2905
+ const nearby = /* @__PURE__ */ new Set();
2906
+ for (let bucket = firstBucket; bucket <= lastBucket; bucket++) for (const entry of buckets.get(bucket) ?? []) nearby.add(entry);
2907
+ const candidates = [...nearby].toSorted((a, b) => a.order - b.order);
2908
+ while (true) {
2909
+ const collision = candidates.find(({ node: other }) => node.x < other.x + other.width + 48 - 1e-6 && other.x < node.x + node.width + 48 - 1e-6 && node.y < other.y + other.height + 48 - 1e-6 && other.y < node.y + node.height + 48 - 1e-6);
2910
+ if (!collision) break;
2911
+ node.y = side < 0 ? collision.node.y - node.height - 48 : collision.node.y + collision.node.height + 48;
2912
+ }
2913
+ const placement = {
2914
+ node,
2915
+ order: placed.size,
2916
+ shift: node.y - original.y - drift,
2917
+ start,
2918
+ extent: Math.max(node.x + node.width, ...upstream.map((entry) => entry.extent))
2919
+ };
2920
+ placed.set(node.id, placement);
2921
+ for (let bucket = firstBucket; bucket <= lastBucket; bucket++) {
2922
+ const entries = buckets.get(bucket);
2923
+ if (entries) entries.push(placement);
2924
+ else buckets.set(bucket, [placement]);
2925
+ }
2926
+ }
2927
+ const nodes = [...placed.values()].map((entry) => entry.node);
2928
+ const extent = bounds(nodes);
2929
+ const width = extent.height + insets.width + 48;
2930
+ const height = extent.width + insets.height + 48;
2931
+ const area = width * height;
2932
+ const score = Math.max(width / aspectRatio, height);
2933
+ if (!best || score < best.score - 1e-9 || Math.abs(score - best.score) < 1e-9 && (area < best.area || area === best.area && turns < best.turns)) best = {
2934
+ nodes,
2935
+ score,
2936
+ area,
2937
+ turns
2938
+ };
2939
+ }
2940
+ const routes = routeLayout(best.nodes, graph.edges.map((edge) => Object.assign({}, edge, {
2941
+ sourceOffset: edge.sourceOffset && layoutAxes(edge.sourceOffset, "vertical"),
2942
+ targetOffset: edge.targetOffset && layoutAxes(edge.targetOffset, "vertical")
2943
+ })));
2944
+ const physicalNodes = best.nodes.map((node) => Object.assign({}, node, layoutAxes(node, "vertical"), {
2945
+ width: node.height,
2946
+ height: node.width
2947
+ }));
2948
+ const physicalRoutes = new Map([...routes].map(([id, points]) => [id, points.map((point) => layoutAxes(point, "vertical"))]));
2949
+ return {
2950
+ positions: Object.fromEntries(physicalNodes.map(({ id, x, y }) => [id, {
2951
+ x,
2952
+ y
2953
+ }])),
2954
+ routes: physicalRoutes,
2955
+ bounds: bounds(physicalNodes, physicalRoutes)
2956
+ };
2371
2957
  }
2372
2958
  Array.isArray;
2373
2959
  var isNumber = (value) => {
@@ -2389,117 +2975,6 @@ var cluster = (list2, size = 2) => {
2389
2975
  });
2390
2976
  };
2391
2977
  //#endregion
2392
- //#region ../../node_modules/.bun/value-enhancer@5.7.2/node_modules/value-enhancer/dist/collections.mjs
2393
- var ReactiveMapImpl = class extends Map {
2394
- constructor(entries, config) {
2395
- super();
2396
- if (config) this.#onDeleted = config.onDeleted;
2397
- const [$, set$] = readonlyVal(this, { equal: false });
2398
- this.$ = $;
2399
- this.#notify = () => set$(this);
2400
- if (entries) for (const [key, value] of entries) this.set(key, value);
2401
- }
2402
- $;
2403
- #notify;
2404
- #onDeleted;
2405
- #delete(key) {
2406
- if (this.#onDeleted) {
2407
- if (super.has(key)) {
2408
- const value = super.get(key);
2409
- super.delete(key);
2410
- this.#onDeleted(value);
2411
- return true;
2412
- }
2413
- return false;
2414
- }
2415
- return super.delete(key);
2416
- }
2417
- #clear() {
2418
- if (this.#onDeleted) {
2419
- const deleted = new Set(this.values());
2420
- super.clear();
2421
- for (const value of deleted) this.#onDeleted(value);
2422
- } else super.clear();
2423
- }
2424
- delete(key) {
2425
- const deleted = this.#delete(key);
2426
- if (deleted) this.#notify();
2427
- return deleted;
2428
- }
2429
- batchDelete(keys) {
2430
- let deleted = false;
2431
- for (const key of keys) deleted = this.#delete(key) || deleted;
2432
- if (deleted) this.#notify();
2433
- return deleted;
2434
- }
2435
- clear() {
2436
- if (this.size > 0) {
2437
- this.#clear();
2438
- this.#notify();
2439
- }
2440
- }
2441
- #set(key, value) {
2442
- if (this.has(key)) {
2443
- const oldValue = this.get(key);
2444
- if (strictEqual(oldValue, value)) return false;
2445
- super.set(key, value);
2446
- if (this.#onDeleted) this.#onDeleted(oldValue);
2447
- } else super.set(key, value);
2448
- return true;
2449
- }
2450
- set(key, value) {
2451
- if (this.#set(key, value)) this.#notify();
2452
- return this;
2453
- }
2454
- batchSet(entries) {
2455
- let isDirty = false;
2456
- for (const [key, value] of entries) isDirty = this.#set(key, value) || isDirty;
2457
- if (isDirty) this.#notify();
2458
- return this;
2459
- }
2460
- rename(oldKey, newKey) {
2461
- if (!strictEqual(oldKey, newKey)) {
2462
- const value = this.get(oldKey);
2463
- if (super.delete(oldKey)) {
2464
- this.set(newKey, value);
2465
- return true;
2466
- }
2467
- }
2468
- return false;
2469
- }
2470
- replace(entries) {
2471
- const oldMap = new Map(this);
2472
- const deletedValues = new Set(this.values());
2473
- let hasNewValue = false;
2474
- super.clear();
2475
- for (const [key, value] of entries) {
2476
- super.set(key, value);
2477
- deletedValues.delete(value);
2478
- hasNewValue = hasNewValue || !oldMap.has(key) || !Object.is(oldMap.get(key), value);
2479
- oldMap.delete(key);
2480
- }
2481
- if (hasNewValue || oldMap.size > 0 || deletedValues.size > 0) {
2482
- if (this.#onDeleted) for (const value of deletedValues) this.#onDeleted(value);
2483
- this.#notify();
2484
- }
2485
- return deletedValues.values();
2486
- }
2487
- toJSON() {
2488
- const result = {};
2489
- for (const [key, value] of this) if (key != null) {
2490
- const k = String(key);
2491
- const v = value;
2492
- result[k] = v && v.toJSON ? v.toJSON(k) : v;
2493
- }
2494
- return result;
2495
- }
2496
- dispose() {
2497
- this.$.dispose();
2498
- if (this.size > 0) this.#clear();
2499
- }
2500
- };
2501
- var reactiveMap = (entries, config) => new ReactiveMapImpl(entries, config);
2502
- //#endregion
2503
2978
  //#region src/canvas/browser/base/compare.ts
2504
2979
  var isSameXYPosition = (p1, p2) => !p1 || !p2 ? p1 === p2 : p1.x === p2.x && p1.y === p2.y;
2505
2980
  var isSize = (s) => isPlainObject(s) && isNumber(s.width) && isNumber(s.height);
@@ -2550,6 +3025,63 @@ function isInside(target, selector) {
2550
3025
  return target?.closest?.(selector) != null;
2551
3026
  }
2552
3027
  //#endregion
3028
+ //#region src/canvas/browser/base/graphLayout.ts
3029
+ /** The measured dependency layout shared by canvas arrangement and image export. */
3030
+ function graphLayout(nodes, edges, direction) {
3031
+ if (!nodes.length) return {
3032
+ nodes: [],
3033
+ edges: []
3034
+ };
3035
+ const graph = new ie.Graph().setDefaultEdgeLabel(() => ({}));
3036
+ graph.setGraph({
3037
+ rankdir: direction === "vertical" ? "TB" : "LR",
3038
+ ranksep: direction === "vertical" ? 48 : 80
3039
+ });
3040
+ const singles = new Set(nodes.map((node) => node.id));
3041
+ for (const node of nodes) graph.setNode(node.id, {
3042
+ width: node.width,
3043
+ height: node.height
3044
+ });
3045
+ const connections = edges.filter((edge) => graph.hasNode(edge.source) && graph.hasNode(edge.target));
3046
+ for (const edge of connections) {
3047
+ singles.delete(edge.source);
3048
+ singles.delete(edge.target);
3049
+ graph.setEdge(edge.source, edge.target);
3050
+ }
3051
+ const isolated = [...singles];
3052
+ for (let start = 0; start < isolated.length; start += 10) for (let index = start; index < Math.min(start + 9, isolated.length - 1); index++) graph.setEdge(isolated[index], isolated[index + 1]);
3053
+ const constraints = [];
3054
+ const ordering = new ie.Graph();
3055
+ for (const node of nodes) {
3056
+ const targets = [...new Set(node.branchHandles?.flatMap((handle) => connections.filter((edge) => edge.source === node.id && edge.sourceHandle === handle).map((edge) => edge.target)))];
3057
+ for (let index = 1; index < targets.length; index++) {
3058
+ const left = targets[index - 1];
3059
+ const right = targets[index];
3060
+ ordering.setEdge(left, right);
3061
+ if (ie.alg.isAcyclic(ordering)) constraints.push({
3062
+ left,
3063
+ right
3064
+ });
3065
+ else ordering.removeEdge(left, right);
3066
+ }
3067
+ }
3068
+ Oe(graph, { constraints });
3069
+ return {
3070
+ nodes: nodes.map((node) => {
3071
+ const { x, y, width, height, rank } = graph.node(node.id);
3072
+ return {
3073
+ id: node.id,
3074
+ column: rank,
3075
+ x: x - width / 2,
3076
+ y: y - height / 2,
3077
+ width,
3078
+ height
3079
+ };
3080
+ }),
3081
+ edges: connections.map((edge) => Object.assign({}, edge, { points: graph.edge(edge.source, edge.target).points }))
3082
+ };
3083
+ }
3084
+ //#endregion
2553
3085
  //#region src/canvas/browser/stores/node/constants.ts
2554
3086
  var NODE_TYPE = /* @__PURE__ */ function(NODE_TYPE) {
2555
3087
  NODE_TYPE["TaskNode"] = "task_node";
@@ -3183,6 +3715,7 @@ var CanvasStore = class {
3183
3715
  constructor(model, editable, language, callbacks, autoLayout = false, interactiveMode$) {
3184
3716
  this.canDeleteNodes = true;
3185
3717
  this.dispose = disposableStore();
3718
+ this.connectionRoutes = this.dispose.add(val(/* @__PURE__ */ new Map()));
3186
3719
  this.ignoredNodeIds = this.dispose.add(val([]));
3187
3720
  this.ignoreNodes = (nodeIds, ignored) => {
3188
3721
  if (this.$.editable.value) this.#callbacks.onIgnoreNodes?.(nodeIds, ignored);
@@ -3467,67 +4000,52 @@ var CanvasStore = class {
3467
4000
  if (position) node.$$.position.set(position);
3468
4001
  }
3469
4002
  }
3470
- layoutPositions(nodes, availableWidth) {
3471
- const g = new ie.Graph().setDefaultEdgeLabel(() => ({}));
3472
- const vertical = this.$.direction.value === "vertical";
3473
- g.setGraph({
3474
- rankdir: vertical ? "TB" : "LR",
3475
- ranksep: vertical ? 48 : 80
3476
- });
3477
- const singles = /* @__PURE__ */ new Set();
3478
- for (const node of nodes) {
3479
- const { id, measured } = node.$.rfNode.value;
3480
- singles.add(id);
3481
- g.setNode(id, {
3482
- width: measured?.width ?? 0,
3483
- height: measured?.height ?? 0
3484
- });
3485
- }
3486
- for (const rfEdge of this.$.rfEdges.value) {
3487
- if (!g.hasNode(rfEdge.source) || !g.hasNode(rfEdge.target)) continue;
3488
- singles.delete(rfEdge.source);
3489
- singles.delete(rfEdge.target);
3490
- g.setEdge(rfEdge.source, rfEdge.target);
3491
- }
3492
- if (singles.size > 0) for (const list of cluster([...singles], 10)) for (let i = 0; i < list.length - 1; i++) g.setEdge(list[i], list[i + 1]);
3493
- const constraints = [];
3494
- const ordering = new ie.Graph();
3495
- for (const node of nodes) {
3496
- if (!NodeStore.is(node)) continue;
3497
- const content = node.content$.value;
3498
- if (content.kind !== "condition" && content.kind !== "approval" && content.kind !== "wait") continue;
3499
- const targets = [...new Set(content.outputs.flatMap((port) => "handle" in port ? this.#connections.value.filter((edge) => edge.source === node.nodeId && edge.sourceHandle === `$branch:${port.handle}`).flatMap((edge) => {
3500
- const target = this.$.nodes.get(edge.target);
3501
- return target && g.hasNode(target.rfNodeId) ? [target.rfNodeId] : [];
3502
- }) : []))];
3503
- for (let index = 1; index < targets.length; index++) {
3504
- const left = targets[index - 1];
3505
- const right = targets[index];
3506
- ordering.setEdge(left, right);
3507
- if (ie.alg.isAcyclic(ordering)) constraints.push({
3508
- left,
3509
- right
3510
- });
3511
- else ordering.removeEdge(left, right);
3512
- }
3513
- }
3514
- Oe(g, { constraints });
3515
- const layoutNodes = nodes.map((node) => {
3516
- const { x, y, width, height, rank } = g.node(node.rfNodeId);
4003
+ measuredLayout(nodes) {
4004
+ return graphLayout(nodes.map((node) => {
4005
+ const measured = node.$.measured.value;
4006
+ const content = NodeStore.is(node) ? node.content$.value : void 0;
3517
4007
  return {
3518
4008
  id: node.nodeId,
3519
- column: rank,
3520
- x: x - width / 2,
3521
- y: y - height / 2,
3522
- width,
3523
- height
4009
+ width: measured?.width ?? 0,
4010
+ height: measured?.height ?? 0,
4011
+ branchHandles: content?.kind === "condition" || content?.kind === "approval" || content?.kind === "wait" ? content.outputs.flatMap((port) => "handle" in port ? ["$branch:" + port.handle] : []) : void 0
3524
4012
  };
3525
- });
4013
+ }), this.#connections.value.map((edge) => ({
4014
+ ...edge,
4015
+ id: getRFEdgeId(edge)
4016
+ })), this.$.direction.value);
4017
+ }
4018
+ layoutPositions(nodes, availableWidth) {
4019
+ const { nodes: layoutNodes } = this.measuredLayout(nodes);
3526
4020
  return this.$.direction.value === "horizontal" ? wrapLayout(layoutNodes, availableWidth) : Object.fromEntries(layoutNodes.map(({ id, x, y }) => [id, {
3527
4021
  x,
3528
4022
  y
3529
4023
  }]));
3530
4024
  }
4025
+ /** Arrange the isolated export store without saving presentation or history. */
4026
+ compactForExport(insets, instance, aspectRatio = 1) {
4027
+ const graph = this.measuredLayout(this.allLayoutNodes());
4028
+ const port = (id, handle, type) => {
4029
+ const node = this.$.nodes.get(id);
4030
+ const measured = (node && instance?.getInternalNode(node.rfNodeId)?.internals.handleBounds?.[type])?.find((entry) => entry.id === `h:${handle}`);
4031
+ return measured && {
4032
+ x: measured.x + measured.width / 2,
4033
+ y: measured.y + (type === "source" ? measured.height : 0)
4034
+ };
4035
+ };
4036
+ const connections = new Map(this.#connections.value.map((edge) => [getRFEdgeId(edge), edge]));
4037
+ const edges = graph.edges.map((edge) => Object.assign({}, edge, {
4038
+ sourceOffset: port(edge.source, connections.get(edge.id)?.sourceHandle, "source"),
4039
+ targetOffset: port(edge.target, connections.get(edge.id)?.targetHandle, "target")
4040
+ }));
4041
+ const result = compactLayout({
4042
+ nodes: graph.nodes,
4043
+ edges
4044
+ }, insets, aspectRatio);
4045
+ this.applyPositions(result.positions);
4046
+ this.connectionRoutes.set(result.routes);
4047
+ return result.bounds;
4048
+ }
3531
4049
  doRemoveNodes(nodes) {
3532
4050
  if (this.disposed || !nodes?.size) return;
3533
4051
  for (const node of nodes) this.pendingNodeDeletes.add(node);
@@ -14700,11 +15218,47 @@ function connectionNodeBounds(node) {
14700
15218
  };
14701
15219
  }
14702
15220
  /** Align arrows with their ports and route returns outside both cards. */
14703
- function getConnectionPath({ sourceBounds, targetBounds, ...params }) {
15221
+ function getConnectionPath({ sourceBounds, targetBounds, waypoints, ...params }) {
14704
15222
  const { sourcePosition = Position.Bottom, targetPosition = Position.Top } = params;
14705
15223
  const vertical = sourcePosition === Position.Bottom && targetPosition === Position.Top || sourcePosition === Position.Top && targetPosition === Position.Bottom;
14706
15224
  const reverse = sourcePosition === Position.Left && targetPosition === Position.Right || sourcePosition === Position.Top && targetPosition === Position.Bottom;
14707
15225
  const horizontal = sourcePosition === Position.Right && targetPosition === Position.Left || sourcePosition === Position.Left && targetPosition === Position.Right;
15226
+ if (waypoints && waypoints.length > 2) {
15227
+ const interior = waypoints.slice(1, -1);
15228
+ const first = interior[0];
15229
+ const last = interior.at(-1);
15230
+ const points = [
15231
+ {
15232
+ x: params.sourceX,
15233
+ y: params.sourceY
15234
+ },
15235
+ vertical ? {
15236
+ x: params.sourceX,
15237
+ y: first.y
15238
+ } : {
15239
+ x: first.x,
15240
+ y: params.sourceY
15241
+ },
15242
+ ...interior,
15243
+ vertical ? {
15244
+ x: params.targetX,
15245
+ y: last.y
15246
+ } : {
15247
+ x: last.x,
15248
+ y: params.targetY
15249
+ },
15250
+ {
15251
+ x: params.targetX,
15252
+ y: params.targetY
15253
+ }
15254
+ ];
15255
+ const label = interior[Math.floor(interior.length / 2)];
15256
+ return [
15257
+ roundedConnectionPath(points),
15258
+ label.x,
15259
+ label.y
15260
+ ];
15261
+ }
14708
15262
  const primary = (x, y) => vertical ? y : x;
14709
15263
  const cross = (x, y) => vertical ? x : y;
14710
15264
  const sourceX = primary(params.sourceX, params.sourceY);
@@ -14789,6 +15343,15 @@ function getConnectionPath({ sourceBounds, targetBounds, ...params }) {
14789
15343
  y: point.x
14790
15344
  } : point);
14791
15345
  if (reverse) points.reverse();
15346
+ const middle = (left + right) / 2;
15347
+ return [
15348
+ roundedConnectionPath(points),
15349
+ vertical ? laneY : middle,
15350
+ vertical ? middle : laneY
15351
+ ];
15352
+ }
15353
+ function roundedConnectionPath(input) {
15354
+ const points = input.filter((point, index) => index === 0 || point.x !== input[index - 1].x || point.y !== input[index - 1].y);
14792
15355
  const radius = 16;
14793
15356
  let path = `M ${points[0].x} ${points[0].y}`;
14794
15357
  for (let i = 1; i < points.length - 1; i++) {
@@ -14802,12 +15365,7 @@ function getConnectionPath({ sourceBounds, targetBounds, ...params }) {
14802
15365
  path += ` Q ${corner.x} ${corner.y} ${corner.x + (next.x - corner.x) * r / after} ${corner.y + (next.y - corner.y) * r / after}`;
14803
15366
  }
14804
15367
  const end = points.at(-1);
14805
- const middle = (left + right) / 2;
14806
- return [
14807
- `${path} L ${end.x} ${end.y}`,
14808
- vertical ? laneY : middle,
14809
- vertical ? middle : laneY
14810
- ];
15368
+ return `${path} L ${end.x} ${end.y}`;
14811
15369
  }
14812
15370
  //#endregion
14813
15371
  //#region src/canvas/browser/graph/Edges/EdgeGradient.tsx
@@ -14836,12 +15394,14 @@ function BasicEdge(props) {
14836
15394
  const error = useVal(edgeStore?.$.error);
14837
15395
  const canvasStore = useCanvasStore();
14838
15396
  const scale = useVal(canvasStore.$.scale);
15397
+ const routes = useVal(canvasStore.connectionRoutes);
14839
15398
  const gap = 3;
14840
15399
  const targetX = props.targetX + (props.targetPosition == Position.Left ? -3 : props.targetPosition == Position.Right ? gap : 0);
14841
15400
  const targetY = props.targetY + (props.targetPosition == Position.Top ? -3 : props.targetPosition == Position.Bottom ? gap : 0);
14842
15401
  const sourceNode = useInternalNode(props.source);
14843
15402
  const targetNode = useInternalNode(props.target);
14844
15403
  const [path, labelX, labelY] = getConnectionPath({
15404
+ waypoints: routes.get(props.id),
14845
15405
  sourceBounds: connectionNodeBounds(sourceNode),
14846
15406
  targetBounds: connectionNodeBounds(targetNode),
14847
15407
  sourceX: props.sourceX,
@@ -20278,7 +20838,7 @@ var markdownPreview_module_default = {
20278
20838
  };
20279
20839
  //#endregion
20280
20840
  //#region src/canvas/browser/preview/markdownPreview.tsx
20281
- var MarkdownContent = lazy(() => import("./markdownContent-IS8xXDoc.js"));
20841
+ var MarkdownContent = lazy(() => import("./markdownContent-B-GLj5DU.js"));
20282
20842
  var MarkdownPreview = /* @__PURE__ */ memo(({ dark, unstyled, components, contentClassName, content, draggable, onDoubleClick }) => {
20283
20843
  const [focus, setFocus] = useState(false);
20284
20844
  return /* @__PURE__ */ jsx("div", {
@@ -23503,7 +24063,7 @@ function tooShort(from, to) {
23503
24063
  }
23504
24064
  //#endregion
23505
24065
  //#region src/canvas/browser/graph/FlowCanvas/FlowCanvas.tsx
23506
- var FlowCanvas = ({ flowCanvasStore, annotationContext, onActivateSelection, onSelectionStart, onSelectionEnd, onInspectSelection, inspectorOpen = false, onRequestAddNode, cornerTools, cornerLeading, topLeftTools, bottomRightTools, toolbar, dark, dottedBackground, fitView, nodesDraggable, layoutMotion, className, addNodeRequest, addItemRequest, onMove, onMoveEnd, onNodeDragStop, onSelectionChange, isValidConnection, onDropAddItem }) => {
24066
+ var FlowCanvas = ({ flowCanvasStore, annotationContext, onActivateSelection, onSelectionStart, onSelectionEnd, onInspectSelection, inspectorOpen = false, onRequestAddNode, cornerTools, cornerLeading, topLeftTools, bottomRightTools, toolbar, dark, dottedBackground, fitView, nodesDraggable, layoutMotion, className, addNodeRequest, addItemRequest, onInit, onMove, onMoveEnd, onNodeDragStop, onSelectionChange, isValidConnection, onDropAddItem }) => {
23507
24067
  const editable = useVal(flowCanvasStore.$.editable);
23508
24068
  const direction = useVal(flowCanvasStore.$.direction);
23509
24069
  const changingDirection = useVal(flowCanvasStore.$.changingDirection);
@@ -23567,7 +24127,10 @@ var FlowCanvas = ({ flowCanvasStore, annotationContext, onActivateSelection, onS
23567
24127
  onRelayout: editable ? flowCanvasStore.onRelayout : void 0,
23568
24128
  onLayoutMeasured: flowCanvasStore.completeLayout,
23569
24129
  onInstance: flowCanvasStore.rfCommand.onRFInstance,
23570
- onInit: flowCanvasStore.onInit,
24130
+ onInit: (instance) => {
24131
+ flowCanvasStore.onInit();
24132
+ onInit?.(instance);
24133
+ },
23571
24134
  onCopy: flowCanvasStore.onCopy,
23572
24135
  onPaste: flowCanvasStore.onPaste,
23573
24136
  waitNode: flowCanvasStore.waitNode,
@@ -23579,4 +24142,4 @@ var FlowCanvas = ({ flowCanvasStore, annotationContext, onActivateSelection, onS
23579
24142
  });
23580
24143
  };
23581
24144
  //#endregion
23582
- export { isMac as C, toManifestNodeId as S, E as _, CanvasNodeIcon as a, toViewEdge as b, collapseAllNested as c, IconStack as d, selectableTimeZones as f, cronDescription as g, timeZoneOffset as h, pasteVideoLink as i, formatDuration as l, timeZoneLongLabel as m, setAddItemId as n, defaultTriggerIcon as o, timeZoneLabel as p, getVideoLink as r, JSONViewer as s, FlowCanvas as t, useDebouncedValue as u, CanvasTooltip as v, isSameViewport as w, toManifestHandleName as x, CanvasStore as y };
24145
+ export { isMac as C, getFlowPngAspectRatio as E, toManifestNodeId as S, flowPngAspectRatios as T, E as _, CanvasNodeIcon as a, toViewEdge as b, collapseAllNested as c, IconStack as d, selectableTimeZones as f, cronDescription as g, timeZoneOffset as h, pasteVideoLink as i, formatDuration as l, timeZoneLongLabel as m, setAddItemId as n, defaultTriggerIcon as o, timeZoneLabel as p, getVideoLink as r, JSONViewer as s, FlowCanvas as t, useDebouncedValue as u, CanvasTooltip as v, isSameViewport as w, toManifestHandleName as x, CanvasStore as y };