flexdesk 0.16.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/LICENSE +0 -0
  3. package/README.md +2217 -42
  4. package/THIRD_PARTY_LICENSES.md +0 -0
  5. package/css/base.css +1834 -75
  6. package/css/flexdesk.css +3146 -164
  7. package/css/overrides.css +16 -0
  8. package/css/reset.css +0 -0
  9. package/css/tokens.css +0 -0
  10. package/dist/canvas.js +6 -239
  11. package/dist/canvas.js.map +3 -3
  12. package/dist/charts.js +709 -18
  13. package/dist/charts.js.map +4 -4
  14. package/dist/chunk-3PHCPZHT.js +0 -0
  15. package/dist/chunk-3PHCPZHT.js.map +0 -0
  16. package/dist/chunk-4A7SJC3T.js +503 -0
  17. package/dist/chunk-4A7SJC3T.js.map +7 -0
  18. package/dist/{chunk-6JSOVNID.js → chunk-CVMCZZX2.js} +635 -10
  19. package/dist/chunk-CVMCZZX2.js.map +7 -0
  20. package/dist/chunk-D3COLZZ2.js +1311 -0
  21. package/dist/chunk-D3COLZZ2.js.map +7 -0
  22. package/dist/chunk-FOOS3T5L.js +0 -0
  23. package/dist/chunk-FOOS3T5L.js.map +0 -0
  24. package/dist/{chunk-SN4LSOAR.js → chunk-GOQLR5WR.js} +452 -2
  25. package/dist/chunk-GOQLR5WR.js.map +7 -0
  26. package/dist/{chunk-FL5KFNQH.js → chunk-HNU6MM2B.js} +52 -17
  27. package/dist/chunk-HNU6MM2B.js.map +7 -0
  28. package/dist/chunk-HYJYYEPK.js +375 -0
  29. package/dist/chunk-HYJYYEPK.js.map +7 -0
  30. package/dist/chunk-JKZLNKG6.js +2041 -0
  31. package/dist/chunk-JKZLNKG6.js.map +7 -0
  32. package/dist/chunk-JYWURG5T.js +0 -0
  33. package/dist/chunk-JYWURG5T.js.map +0 -0
  34. package/dist/{chunk-RQM5Z2MU.js → chunk-MCIXFNVF.js} +633 -311
  35. package/dist/chunk-MCIXFNVF.js.map +7 -0
  36. package/dist/chunk-N7HC3ADV.js +30 -0
  37. package/dist/chunk-N7HC3ADV.js.map +7 -0
  38. package/dist/{chunk-FBGP5KN5.js → chunk-Q36EPT2R.js} +3268 -203
  39. package/dist/chunk-Q36EPT2R.js.map +7 -0
  40. package/dist/chunk-QWQJUDPD.js +610 -0
  41. package/dist/chunk-QWQJUDPD.js.map +7 -0
  42. package/dist/{chunk-NLTOY57M.js → chunk-UURZ37SD.js} +28 -48
  43. package/dist/chunk-UURZ37SD.js.map +7 -0
  44. package/dist/{chunk-B5Q53LAJ.js → chunk-UWO43BZU.js} +2 -2
  45. package/dist/chunk-UWO43BZU.js.map +7 -0
  46. package/dist/chunk-WVFGV5FT.js +0 -0
  47. package/dist/chunk-WVFGV5FT.js.map +0 -0
  48. package/dist/core.js +170 -118
  49. package/dist/core.js.map +2 -2
  50. package/dist/editor.js +156 -70
  51. package/dist/editor.js.map +2 -2
  52. package/dist/flexdesk.css +3146 -164
  53. package/dist/flow.js +2789 -306
  54. package/dist/flow.js.map +4 -4
  55. package/dist/host.js +109 -3
  56. package/dist/host.js.map +3 -3
  57. package/dist/reset.css +0 -0
  58. package/dist/tile_registry-6WZPTUZV.js +0 -0
  59. package/dist/tile_registry-6WZPTUZV.js.map +0 -0
  60. package/dist/tiles.js +657 -234
  61. package/dist/tiles.js.map +4 -4
  62. package/dist/tokens.css +0 -0
  63. package/dist/widgets.js +3252 -2124
  64. package/dist/widgets.js.map +4 -4
  65. package/dist/wm.js +1227 -347
  66. package/dist/wm.js.map +4 -4
  67. package/package.json +3 -1
  68. package/src/canvas/canvas_adapter.js +5 -0
  69. package/src/canvas/edge_router.js +0 -0
  70. package/src/canvas/graph/connector_router.js +0 -0
  71. package/src/canvas/graph/history_service.js +0 -0
  72. package/src/canvas/graph/node_platform.js +0 -0
  73. package/src/canvas/index.js +1 -0
  74. package/src/canvas/layered_layout.js +244 -0
  75. package/src/charts/chart_types.js +0 -0
  76. package/src/charts/data_series_plot_window.js +0 -0
  77. package/src/charts/downsample.js +2 -2
  78. package/src/charts/plot_config.js +53 -29
  79. package/src/charts/plot_config_editor.js +627 -0
  80. package/src/charts/plot_grid.js +449 -0
  81. package/src/charts/plot_popout_window.js +103 -317
  82. package/src/charts/plotly_wrapper.js +22 -2
  83. package/src/charts/series_transforms.js +206 -0
  84. package/src/core/event_bus.js +0 -0
  85. package/src/core/logging.js +1 -1
  86. package/src/core/settings.js +105 -19
  87. package/src/core/state_guard.js +93 -51
  88. package/src/core/state_guard_config.js +85 -74
  89. package/src/core/state_machine.js +0 -0
  90. package/src/editor/editor_pane.js +3 -3
  91. package/src/editor/monaco_editor_factory.js +124 -60
  92. package/src/editor/monaco_loader.js +103 -28
  93. package/src/editor/notebook_search.js +0 -0
  94. package/src/editor/notebook_tab_bar.js +38 -52
  95. package/src/editor/notebook_undo_manager.js +0 -0
  96. package/src/editor/split_pane_container.js +6 -3
  97. package/src/flow/canvas_editor/connections.js +90 -0
  98. package/src/flow/canvas_editor/editor.js +2351 -153
  99. package/src/flow/canvas_editor/index.js +2 -1
  100. package/src/flow/canvas_editor/layout.js +119 -12
  101. package/src/flow/canvas_editor/strings.js +80 -0
  102. package/src/flow/kit/always_before.js +0 -0
  103. package/src/flow/kit/catalogue.js +97 -3
  104. package/src/flow/kit/chip_input.js +0 -0
  105. package/src/flow/kit/dom.js +0 -0
  106. package/src/flow/kit/findings.js +0 -0
  107. package/src/flow/kit/history.js +0 -0
  108. package/src/flow/kit/index.js +3 -3
  109. package/src/flow/kit/keys.js +7 -1
  110. package/src/flow/kit/logic_graph.js +97 -15
  111. package/src/flow/kit/references.js +0 -0
  112. package/src/flow/kit/settings_panel.js +0 -0
  113. package/src/flow/kit/settings_schema.js +2 -1
  114. package/src/flow/kit/step_picker.js +0 -0
  115. package/src/flow/kit/strings.js +3 -1
  116. package/src/flow/kit/value_picker.js +0 -0
  117. package/src/flow/kit/widgets.js +86 -109
  118. package/src/flow/lanes/editor.js +136 -14
  119. package/src/flow/lanes/index.js +3 -1
  120. package/src/flow/lanes/layout.js +0 -0
  121. package/src/flow/lanes/pipeline.js +44 -2
  122. package/src/flow/lanes/preview.js +0 -0
  123. package/src/flow/lanes/strings.js +1 -0
  124. package/src/flow/outline/editor.js +0 -0
  125. package/src/flow/outline/index.js +0 -0
  126. package/src/flow/outline/mapping.js +0 -0
  127. package/src/flow/outline/menu.js +0 -0
  128. package/src/flow/outline/operations.js +0 -0
  129. package/src/flow/outline/recognise.js +0 -0
  130. package/src/flow/outline/strings.js +0 -0
  131. package/src/help/help_modal.js +168 -42
  132. package/src/help/help_registry.js +12 -1
  133. package/src/host/external_links.js +0 -0
  134. package/src/host/file_picker.js +117 -0
  135. package/src/host/host.js +10 -2
  136. package/src/host/photino_host.js +9 -0
  137. package/src/host/photino_host.md +21 -3
  138. package/src/host/pywebview_host.js +10 -0
  139. package/src/tiles/chart_tiles.js +53 -245
  140. package/src/tiles/config_schema.js +8 -0
  141. package/src/tiles/figure_tile.js +322 -0
  142. package/src/tiles/layout_persistence.js +3 -3
  143. package/src/tiles/list_tiles.js +7 -7
  144. package/src/tiles/scope_picker.js +0 -0
  145. package/src/tiles/tile_base.js +1 -1
  146. package/src/tiles/tile_config_form.js +442 -0
  147. package/src/tiles/tile_grid.js +2 -0
  148. package/src/tiles/tile_registry.js +0 -0
  149. package/src/tiling/bottom_panel_tabs.js +67 -99
  150. package/src/tiling/command_palette.js +64 -19
  151. package/src/tiling/content_registry.js +4 -1
  152. package/src/tiling/desktops.js +11 -1
  153. package/src/tiling/entity_sources.js +0 -0
  154. package/src/tiling/keymap.js +403 -128
  155. package/src/tiling/kind_taxonomy.js +78 -0
  156. package/src/tiling/landing_table.js +0 -0
  157. package/src/tiling/loading_overlay.js +0 -0
  158. package/src/tiling/nav_panel.js +59 -10
  159. package/src/tiling/page_factory.js +5 -5
  160. package/src/tiling/panel_availability.js +49 -0
  161. package/src/tiling/panel_keys.js +0 -0
  162. package/src/tiling/shell.js +114 -12
  163. package/src/tiling/tab_strip.js +0 -0
  164. package/src/tiling/tile_breadcrumb.js +0 -0
  165. package/src/tiling/tile_renderer.js +105 -9
  166. package/src/tiling/tile_tab_menu.js +0 -0
  167. package/src/tiling/tile_tree.js +0 -0
  168. package/src/tiling/wm.js +562 -30
  169. package/src/tiling/zoom.js +0 -0
  170. package/src/ui/base/component_base.js +0 -0
  171. package/src/ui/base/controller_base.js +0 -0
  172. package/src/ui/base/page_base.js +0 -0
  173. package/src/ui/components/about_dialog.js +0 -0
  174. package/src/ui/components/action_dropdown.js +30 -2
  175. package/src/ui/components/alert_log.js +0 -0
  176. package/src/ui/components/alerts_pane.js +0 -0
  177. package/src/ui/components/attribute_list_editor.js +0 -0
  178. package/src/ui/components/autocomplete_field.js +0 -0
  179. package/src/ui/components/cell_editor_host.js +211 -0
  180. package/src/ui/components/cell_grid.js +648 -0
  181. package/src/ui/components/cell_selection.js +220 -0
  182. package/src/ui/components/choice_controls.js +351 -0
  183. package/src/ui/components/color_field.js +211 -0
  184. package/src/ui/components/color_list_field.js +210 -0
  185. package/src/ui/components/column_fit.js +0 -0
  186. package/src/ui/components/column_groups.js +314 -0
  187. package/src/ui/components/column_order.js +142 -0
  188. package/src/ui/components/computing_status_window.js +98 -25
  189. package/src/ui/components/confirm_dialog.js +81 -38
  190. package/src/ui/components/context_menu.js +2 -2
  191. package/src/ui/components/conversation_view.js +440 -0
  192. package/src/ui/components/crosstab.js +289 -0
  193. package/src/ui/components/data_table.js +601 -62
  194. package/src/ui/components/detail_header.js +22 -11
  195. package/src/ui/components/document_outline.js +397 -0
  196. package/src/ui/components/drag_reorder.js +0 -0
  197. package/src/ui/components/fact_table.js +0 -0
  198. package/src/ui/components/fill_handle.js +322 -0
  199. package/src/ui/components/gallery_picker.js +19 -8
  200. package/src/ui/components/header_actions.js +55 -0
  201. package/src/ui/components/inline_renamer.js +0 -0
  202. package/src/ui/components/job_progress.js +51 -0
  203. package/src/ui/components/managed_window.js +27 -4
  204. package/src/ui/components/menu_bar.js +745 -0
  205. package/src/ui/components/modal.js +449 -75
  206. package/src/ui/components/notification_history.js +3 -3
  207. package/src/ui/components/progress.js +483 -0
  208. package/src/ui/components/record_list.js +0 -0
  209. package/src/ui/components/row_activation.js +0 -0
  210. package/src/ui/components/section_stack.js +298 -0
  211. package/src/ui/components/series.js +173 -0
  212. package/src/ui/components/settings_view.js +634 -0
  213. package/src/ui/components/slide_out_panel.js +43 -6
  214. package/src/ui/components/slider_field.js +275 -18
  215. package/src/ui/components/sortable_list.js +0 -0
  216. package/src/ui/components/staged_edits.js +343 -0
  217. package/src/ui/components/staged_history.js +172 -0
  218. package/src/ui/components/table_filter_chips.js +0 -0
  219. package/src/ui/components/table_state_store.js +0 -0
  220. package/src/ui/components/table_structure.js +182 -0
  221. package/src/ui/components/table_values.js +0 -0
  222. package/src/ui/components/tabs.js +497 -0
  223. package/src/ui/components/tag_input.js +215 -0
  224. package/src/ui/components/toast.js +25 -8
  225. package/src/ui/components/toggle_field.js +175 -0
  226. package/src/ui/components/tree_view.js +55 -34
  227. package/src/ui/components/window_taskbar.js +0 -0
  228. package/src/ui/components/wizard.js +563 -0
  229. package/src/ui/controllers/window_chrome_controller.js +2 -2
  230. package/src/ui/notification_center.js +375 -119
  231. package/src/ui/utils/auto_scrollbars.js +0 -0
  232. package/src/ui/utils/markdown.js +709 -0
  233. package/src/ui/utils/native_context_menu.js +0 -0
  234. package/src/ui/utils/overlay_scrollbar.js +16 -12
  235. package/src/ui/utils/raf_resize_observer.js +0 -0
  236. package/src/ui/utils/scroll_lock.js +0 -0
  237. package/src/ui/utils/strip_scroll.js +55 -0
  238. package/src/ui/utils/tooltip_service.js +0 -8
  239. package/dist/chunk-6JSOVNID.js.map +0 -7
  240. package/dist/chunk-B5Q53LAJ.js.map +0 -7
  241. package/dist/chunk-FBGP5KN5.js.map +0 -7
  242. package/dist/chunk-FL5KFNQH.js.map +0 -7
  243. package/dist/chunk-NLTOY57M.js.map +0 -7
  244. package/dist/chunk-NNFTETK3.js +0 -633
  245. package/dist/chunk-NNFTETK3.js.map +0 -7
  246. package/dist/chunk-O5OHMWBB.js +0 -284
  247. package/dist/chunk-O5OHMWBB.js.map +0 -7
  248. package/dist/chunk-RQM5Z2MU.js.map +0 -7
  249. package/dist/chunk-SN4LSOAR.js.map +0 -7
  250. package/dist/chunk-W6FBJLPW.js +0 -367
  251. package/dist/chunk-W6FBJLPW.js.map +0 -7
  252. package/src/ui/controllers/panel_state_machine.js +0 -634
@@ -4,6 +4,10 @@ var MAX_SCALE = 4;
4
4
  var COARSE_GRID_EVERY = 5;
5
5
  var SUB_PIXEL = 0.5;
6
6
  var CanvasAdapter = class {
7
+ /** The zoom limits every `setViewport`, `zoomAt` and `fit` clamps to, for an
8
+ * embedder whose own zoom controls must stop where the kernel stops. */
9
+ static MIN_SCALE = MIN_SCALE;
10
+ static MAX_SCALE = MAX_SCALE;
7
11
  /**
8
12
  * @param {object} o
9
13
  * @param {HTMLElement} o.container the box the canvas fills (it needs a height)
@@ -518,6 +522,448 @@ function arrange(nodes, edges, { columnGap = 340, rowGap = 40 } = {}) {
518
522
  return positions;
519
523
  }
520
524
 
525
+ // src/canvas/layered_layout.js
526
+ var DEFAULT_SIZE = Object.freeze({ width: 160, height: 80 });
527
+ var defaultSizeOf = (node) => ({
528
+ width: Number(node?.width) || DEFAULT_SIZE.width,
529
+ height: Number(node?.height) || DEFAULT_SIZE.height
530
+ });
531
+ function crossingsBetween(pairs) {
532
+ let n = 0;
533
+ for (let i = 0; i < pairs.length; i++) {
534
+ const [a1, b1] = pairs[i];
535
+ for (let j = i + 1; j < pairs.length; j++) {
536
+ const [a2, b2] = pairs[j];
537
+ if ((a1 - a2) * (b1 - b2) < 0) n++;
538
+ }
539
+ }
540
+ return n;
541
+ }
542
+ function layeredLayout(nodes, edges, {
543
+ rank = null,
544
+ sizeOf = null,
545
+ columnGap = 64,
546
+ rowGap = 32,
547
+ componentGap = 80,
548
+ passes = 8
549
+ } = {}) {
550
+ const list = [];
551
+ const index = /* @__PURE__ */ new Map();
552
+ for (const node of Array.isArray(nodes) ? nodes : []) {
553
+ if (!node || node.id === void 0 || node.id === null || index.has(String(node.id))) continue;
554
+ index.set(String(node.id), list.length);
555
+ list.push(node);
556
+ }
557
+ const count = list.length;
558
+ const positions = /* @__PURE__ */ new Map();
559
+ if (!count) return positions;
560
+ const hint = list.map((n) => {
561
+ const r = typeof rank === "function" ? rank(n) : null;
562
+ return r === "source" || r === "sink" ? r : null;
563
+ });
564
+ const measure = typeof sizeOf === "function" ? sizeOf : defaultSizeOf;
565
+ const size = list.map((n) => {
566
+ const s = measure(n) || {};
567
+ return { width: Math.max(0, Number(s.width) || 0), height: Math.max(0, Number(s.height) || 0) };
568
+ });
569
+ const out = Array.from({ length: count }, () => []);
570
+ const near = Array.from({ length: count }, () => []);
571
+ const seen = /* @__PURE__ */ new Set();
572
+ const lines = [];
573
+ for (const e of Array.isArray(edges) ? edges : []) {
574
+ const u = index.get(String(e?.from));
575
+ const v = index.get(String(e?.to));
576
+ if (u === void 0 || v === void 0 || u === v) continue;
577
+ const key = `${u}>${v}`;
578
+ if (seen.has(key)) continue;
579
+ seen.add(key);
580
+ out[u].push(v);
581
+ lines.push([u, v]);
582
+ if (!seen.has(`${u}~${v}`)) {
583
+ seen.add(`${u}~${v}`);
584
+ seen.add(`${v}~${u}`);
585
+ near[u].push(v);
586
+ near[v].push(u);
587
+ }
588
+ }
589
+ const componentOf = new Int32Array(count).fill(-1);
590
+ const components = [];
591
+ for (let s = 0; s < count; s++) {
592
+ if (componentOf[s] >= 0) continue;
593
+ const members = [];
594
+ const stack = [s];
595
+ componentOf[s] = components.length;
596
+ while (stack.length) {
597
+ const u = stack.pop();
598
+ members.push(u);
599
+ for (const v of near[u]) if (componentOf[v] < 0) {
600
+ componentOf[v] = components.length;
601
+ stack.push(v);
602
+ }
603
+ }
604
+ members.sort((a, b) => a - b);
605
+ components.push(members);
606
+ }
607
+ const ordered = components.map((m, i) => ({ m, i })).sort((a, b) => b.m.length - a.m.length || a.i - b.i);
608
+ const linesOf = components.map(() => []);
609
+ for (const line of lines) linesOf[componentOf[line[0]]].push(line);
610
+ const reversed = /* @__PURE__ */ new Set();
611
+ const state = new Uint8Array(count);
612
+ for (let s = 0; s < count; s++) {
613
+ if (state[s]) continue;
614
+ const stack = [[s, 0]];
615
+ state[s] = 1;
616
+ while (stack.length) {
617
+ const top2 = stack[stack.length - 1];
618
+ const [u, k] = top2;
619
+ if (k < out[u].length) {
620
+ top2[1] = k + 1;
621
+ const v = out[u][k];
622
+ if (state[v] === 1) reversed.add(`${u}>${v}`);
623
+ else if (state[v] === 0) {
624
+ state[v] = 1;
625
+ stack.push([v, 0]);
626
+ }
627
+ } else {
628
+ state[u] = 2;
629
+ stack.pop();
630
+ }
631
+ }
632
+ }
633
+ const preds = Array.from({ length: count }, () => []);
634
+ const succs = Array.from({ length: count }, () => []);
635
+ for (const [u0, v0] of lines) {
636
+ const [u, v] = reversed.has(`${u0}>${v0}`) ? [v0, u0] : [u0, v0];
637
+ if (hint[v] === "source" || hint[u] === "sink") continue;
638
+ preds[v].push(u);
639
+ succs[u].push(v);
640
+ }
641
+ const layer = new Int32Array(count);
642
+ const waiting = preds.map((p) => p.length);
643
+ const queue = [];
644
+ for (let u = 0; u < count; u++) if (!waiting[u]) queue.push(u);
645
+ for (let q = 0; q < queue.length; q++) {
646
+ const u = queue[q];
647
+ for (const v of succs[u]) {
648
+ if (layer[u] + 1 > layer[v]) layer[v] = layer[u] + 1;
649
+ if (--waiting[v] === 0) queue.push(v);
650
+ }
651
+ }
652
+ let top = 0;
653
+ for (const { m, i: c } of ordered) {
654
+ let last = -1;
655
+ for (const u of m) if (hint[u] !== "sink") last = Math.max(last, hint[u] === "source" ? 0 : layer[u]);
656
+ for (const u of m) {
657
+ if (hint[u] === "source") layer[u] = 0;
658
+ else if (hint[u] === "sink") layer[u] = last + 1;
659
+ }
660
+ const used = [...new Set(m.map((u) => layer[u]))].sort((a, b) => a - b);
661
+ const renumber = new Map(used.map((l, i) => [l, i]));
662
+ const columns = used.map(() => []);
663
+ for (const u of m) {
664
+ layer[u] = renumber.get(layer[u]);
665
+ columns[layer[u]].push(u);
666
+ }
667
+ const place = /* @__PURE__ */ new Map();
668
+ const remember = () => {
669
+ for (const c2 of columns) c2.forEach((u, i) => place.set(u, i));
670
+ };
671
+ remember();
672
+ const between = columns.map(() => []);
673
+ for (const [u, v] of linesOf[c]) {
674
+ const a = Math.min(layer[u], layer[v]);
675
+ if (Math.abs(layer[u] - layer[v]) === 1) between[a].push(layer[u] < layer[v] ? [u, v] : [v, u]);
676
+ }
677
+ const crossings = () => {
678
+ let n = 0;
679
+ for (let l = 0; l + 1 < columns.length; l++) {
680
+ n += crossingsBetween(between[l].map(([u, v]) => [place.get(u), place.get(v)]));
681
+ }
682
+ return n;
683
+ };
684
+ const sweep = (l, from) => {
685
+ const column = columns[l];
686
+ const centre = /* @__PURE__ */ new Map();
687
+ for (const u of column) {
688
+ let sum = 0;
689
+ let k = 0;
690
+ for (const v of near[u]) {
691
+ if (from(layer[v])) {
692
+ sum += place.get(v);
693
+ k++;
694
+ }
695
+ }
696
+ centre.set(u, k ? sum / k : place.get(u));
697
+ }
698
+ column.sort((a, b) => centre.get(a) - centre.get(b) || place.get(a) - place.get(b));
699
+ column.forEach((u, i) => place.set(u, i));
700
+ };
701
+ let best = crossings();
702
+ let bestColumns = columns.map((c2) => [...c2]);
703
+ for (let p = 0; p < passes && best > 0; p++) {
704
+ for (let l = 1; l < columns.length; l++) sweep(l, (x2) => x2 < l);
705
+ for (let l = columns.length - 2; l >= 0; l--) sweep(l, (x2) => x2 > l);
706
+ const now = crossings();
707
+ if (now < best) {
708
+ best = now;
709
+ bestColumns = columns.map((c2) => [...c2]);
710
+ }
711
+ }
712
+ const widths = bestColumns.map((c2) => Math.max(0, ...c2.map((u) => size[u].width)));
713
+ const heights = bestColumns.map((c2) => c2.reduce((h, u) => h + size[u].height, 0) + rowGap * Math.max(0, c2.length - 1));
714
+ const tallest = Math.max(0, ...heights);
715
+ let x = 0;
716
+ bestColumns.forEach((c2, l) => {
717
+ let y = top + (tallest - heights[l]) / 2;
718
+ for (const u of c2) {
719
+ positions.set(String(list[u].id), { x, y });
720
+ y += size[u].height + rowGap;
721
+ }
722
+ x += widths[l] + columnGap;
723
+ });
724
+ top += tallest + componentGap;
725
+ }
726
+ return positions;
727
+ }
728
+
729
+ // src/canvas/graph/node_platform.js
730
+ var NodePlatform = class {
731
+ /**
732
+ * @param {object} [options]
733
+ * @param {(node: object) => object[]} [options.collapsedPorts] the ports a COLLAPSED node
734
+ * still offers; by default none
735
+ */
736
+ constructor({ collapsedPorts = null } = {}) {
737
+ this.nodes = /* @__PURE__ */ new Map();
738
+ this.listeners = /* @__PURE__ */ new Map();
739
+ this.collapsedPorts = typeof collapsedPorts === "function" ? collapsedPorts : null;
740
+ }
741
+ on(event, handler) {
742
+ if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
743
+ this.listeners.get(event).add(handler);
744
+ return () => this.listeners.get(event).delete(handler);
745
+ }
746
+ emit(event, payload) {
747
+ for (const handler of this.listeners.get(event) || []) handler(payload);
748
+ }
749
+ /**
750
+ * Replace the whole graph. Hydration, not an edit — see `HistoryService`.
751
+ *
752
+ * Each node is `{id, position?, size?, collapsed?, ports?, …}`: the position
753
+ * is copied (a missing one is (0, 0)), `size` is `{w, h}` or null, `ports`
754
+ * an array (none when absent), and every other key is kept as it is.
755
+ */
756
+ hydrate(nodes) {
757
+ this.nodes.clear();
758
+ for (const raw of Array.isArray(nodes) ? nodes : []) {
759
+ if (!raw || raw.id === void 0 || raw.id === null) continue;
760
+ const id = raw.id;
761
+ this.nodes.set(id, {
762
+ ...raw,
763
+ id,
764
+ // `size` and `collapsed` are in the snapshot, not derived: at
765
+ // hundreds of nodes a node showing forty ports is unreadable, and
766
+ // a collapsed node's ports fold away.
767
+ position: raw.position ? { x: raw.position.x, y: raw.position.y } : { x: 0, y: 0 },
768
+ size: raw.size ? { w: raw.size.w, h: raw.size.h } : null,
769
+ collapsed: Boolean(raw.collapsed),
770
+ ports: Array.isArray(raw.ports) ? raw.ports : []
771
+ });
772
+ }
773
+ this.emit("hydrated", { count: this.nodes.size });
774
+ }
775
+ get(id) {
776
+ return this.nodes.get(id);
777
+ }
778
+ all() {
779
+ return [...this.nodes.values()];
780
+ }
781
+ port(nodeId, portId) {
782
+ return this.nodes.get(nodeId)?.ports.find((p) => p.id === portId) || null;
783
+ }
784
+ setPosition(id, position, { silent = false } = {}) {
785
+ const node = this.nodes.get(id);
786
+ if (!node) return null;
787
+ const before = { ...node.position };
788
+ node.position = { ...position };
789
+ if (!silent) this.emit("moved", { id, before, after: { ...position } });
790
+ return before;
791
+ }
792
+ setCollapsed(id, collapsed, { silent = false } = {}) {
793
+ const node = this.nodes.get(id);
794
+ if (!node) return null;
795
+ const before = node.collapsed;
796
+ node.collapsed = Boolean(collapsed);
797
+ if (!silent) this.emit("collapsed", { id, before, after: node.collapsed });
798
+ return before;
799
+ }
800
+ /**
801
+ * Ports that are visible right now.
802
+ *
803
+ * A collapsed node offers only what `collapsedPorts` says it keeps (by
804
+ * default nothing). Answering from one place is what stops a renderer and
805
+ * the legality guard disagreeing about what can be dropped on.
806
+ */
807
+ visiblePorts(id) {
808
+ const node = this.nodes.get(id);
809
+ if (!node) return [];
810
+ if (node.collapsed) return this.collapsedPorts ? this.collapsedPorts(node) : [];
811
+ return node.ports;
812
+ }
813
+ };
814
+
815
+ // src/canvas/graph/connector_router.js
816
+ var CONNECTOR_STRINGS = Object.freeze({
817
+ nodeGone: "That node is no longer on the canvas.",
818
+ portGone: "That port is no longer on the canvas.",
819
+ portDisabled: "That port cannot be connected.",
820
+ selfReference: "A node cannot connect to itself on this canvas.",
821
+ portFull: (max) => max === 1 ? "That port already carries its one connection." : `That port already carries its ${max} connections.`
822
+ });
823
+ var say = (strings, key, ...args) => {
824
+ const s = strings[key];
825
+ return typeof s === "function" ? s(...args) : String(s);
826
+ };
827
+ var defaultEndsOf = (edge) => ({ from: edge?.fromPort ?? null, to: edge?.toPort ?? null });
828
+ function capacityOf(port) {
829
+ if (Number.isFinite(port?.maxConnections)) return port.maxConnections;
830
+ if (port?.allowMultiple === false) return 1;
831
+ return Infinity;
832
+ }
833
+ var ConnectorRouter = class {
834
+ /**
835
+ * @param {object} o
836
+ * @param {object} o.platform a `NodePlatform`
837
+ * @param {boolean} [o.selfReference] may a node connect to itself
838
+ * @param {(ctx: object) => object|null|undefined} [o.policy] the embedder's rules, asked last
839
+ * @param {(edge: object) => {from, to}} [o.endsOf] the port ids an edge uses at each end
840
+ * @param {('source'|'target')[]} [o.disabledEnds] which ends a disabled port refuses
841
+ * @param {object} [o.strings] the generic refusals' words
842
+ */
843
+ constructor({
844
+ platform,
845
+ selfReference = false,
846
+ policy = null,
847
+ endsOf = null,
848
+ disabledEnds = ["source", "target"],
849
+ strings = null
850
+ } = {}) {
851
+ this.platform = platform;
852
+ this.selfReference = selfReference;
853
+ this.policy = typeof policy === "function" ? policy : null;
854
+ this.endsOf = typeof endsOf === "function" ? endsOf : defaultEndsOf;
855
+ this.disabledEnds = new Set(disabledEnds);
856
+ this.strings = Object.freeze({ ...CONNECTOR_STRINGS, ...strings || {} });
857
+ this.edges = /* @__PURE__ */ new Map();
858
+ }
859
+ hydrate(edges) {
860
+ this.edges.clear();
861
+ for (const edge of edges) this.edges.set(edge.id, edge);
862
+ }
863
+ /** Every edge already touching this node, in either direction. */
864
+ edgesFor(nodeId) {
865
+ return [...this.edges.values()].filter((e) => e.from === nodeId || e.to === nodeId);
866
+ }
867
+ /** How many edges (applied and `pending`) already use this port at this node. */
868
+ connectionsAt(nodeId, portId, { pendingEdges = [] } = {}) {
869
+ let n = 0;
870
+ for (const edge of [...this.edges.values(), ...pendingEdges]) {
871
+ const ends = this.endsOf(edge) || {};
872
+ if (edge.from === nodeId && ends.from === portId) n += 1;
873
+ if (edge.to === nodeId && ends.to === portId) n += 1;
874
+ }
875
+ return n;
876
+ }
877
+ /**
878
+ * Can this drop become an edge?
879
+ *
880
+ * `source` and `target` are `{nodeId, portId}`. The answer is a verdict the
881
+ * caller can render directly — the reason strings ARE the notice text, so a
882
+ * policy should name the objects rather than describe the rule.
883
+ */
884
+ canConnect(source, target, { pendingEdges = [] } = {}) {
885
+ const S = this.strings;
886
+ const fromNode = this.platform.get(source.nodeId);
887
+ const toNode = this.platform.get(target.nodeId);
888
+ if (!fromNode || !toNode) return { ok: false, reason: say(S, "nodeGone") };
889
+ const fromPort = this.platform.port(source.nodeId, source.portId);
890
+ const toPort = this.platform.port(target.nodeId, target.portId);
891
+ if (!fromPort || !toPort) return { ok: false, reason: say(S, "portGone") };
892
+ if (fromPort.disabled && this.disabledEnds.has("source")) {
893
+ return { ok: false, reason: fromPort.disabledReason || say(S, "portDisabled") };
894
+ }
895
+ if (toPort.disabled && this.disabledEnds.has("target")) {
896
+ return { ok: false, reason: toPort.disabledReason || say(S, "portDisabled") };
897
+ }
898
+ const selfReference = fromNode.id === toNode.id;
899
+ if (selfReference && !this.selfReference) return { ok: false, reason: say(S, "selfReference") };
900
+ for (const [node, port] of [[fromNode, fromPort], [toNode, toPort]]) {
901
+ const max = capacityOf(port);
902
+ if (max !== Infinity && this.connectionsAt(node.id, port.id, { pendingEdges }) >= max) {
903
+ return { ok: false, reason: say(S, "portFull", max) };
904
+ }
905
+ }
906
+ if (this.policy) {
907
+ const verdict = this.policy({
908
+ source,
909
+ target,
910
+ fromNode,
911
+ toNode,
912
+ fromPort,
913
+ toPort,
914
+ pendingEdges,
915
+ selfReference,
916
+ edges: [...this.edges.values()],
917
+ router: this
918
+ });
919
+ if (verdict && typeof verdict === "object") return verdict;
920
+ }
921
+ return { ok: true, selfReference };
922
+ }
923
+ /**
924
+ * Record a STAGED edge. Nothing here talks to a server — the embedder turns
925
+ * a staged edge into whatever its own write is, and only that makes it real.
926
+ */
927
+ stage(edge) {
928
+ this.edges.set(edge.id, edge);
929
+ return edge;
930
+ }
931
+ unstage(id) {
932
+ const edge = this.edges.get(id);
933
+ this.edges.delete(id);
934
+ return edge || null;
935
+ }
936
+ /**
937
+ * Re-map edges when a node's port set is rebuilt.
938
+ *
939
+ * A node gains or loses a port constantly in a live diagram. Matches by port
940
+ * id, drops what cannot be mapped, and reports both — a silently dropped
941
+ * edge is a line that vanished from the drawing while what it stands for
942
+ * still exists. `endsOf` (else the router's own) names the ports an edge
943
+ * uses; an end that names none is not checked.
944
+ */
945
+ remapNodeConnections(nodeId, portIds, { endsOf = null } = {}) {
946
+ const ends = typeof endsOf === "function" ? endsOf : this.endsOf;
947
+ const available = new Set(portIds);
948
+ let remapped = 0;
949
+ const removed = [];
950
+ for (const [id, edge] of [...this.edges]) {
951
+ const at = ends(edge) || {};
952
+ const touches = [];
953
+ if (edge.from === nodeId && at.from !== null && at.from !== void 0) touches.push(at.from);
954
+ if (edge.to === nodeId && at.to !== null && at.to !== void 0) touches.push(at.to);
955
+ if (edge.from !== nodeId && edge.to !== nodeId) continue;
956
+ if (touches.every((portId) => available.has(portId))) {
957
+ remapped += 1;
958
+ continue;
959
+ }
960
+ this.edges.delete(id);
961
+ removed.push(edge);
962
+ }
963
+ return { remapped, removed };
964
+ }
965
+ };
966
+
521
967
  export {
522
968
  CanvasAdapter,
523
969
  OBSTACLE_ROUTING_NODE_LIMIT,
@@ -527,6 +973,10 @@ export {
527
973
  cardinality,
528
974
  selfLoop,
529
975
  returnEdge,
530
- arrange
976
+ arrange,
977
+ layeredLayout,
978
+ NodePlatform,
979
+ CONNECTOR_STRINGS,
980
+ ConnectorRouter
531
981
  };
532
- //# sourceMappingURL=chunk-SN4LSOAR.js.map
982
+ //# sourceMappingURL=chunk-GOQLR5WR.js.map