@01.software/sdk 0.4.1 → 0.4.3-dev.1774320821495

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/dist/ui/flow.cjs CHANGED
@@ -71,6 +71,9 @@ __export(Flow_exports, {
71
71
  BUILT_IN_EDGE_TYPES: () => BUILT_IN_EDGE_TYPES,
72
72
  BUILT_IN_NODE_TYPES: () => BUILT_IN_NODE_TYPES,
73
73
  FlowRenderer: () => FlowRenderer,
74
+ clearTemplateCache: () => clearTemplateCache,
75
+ compileTemplate: () => compileTemplate,
76
+ getFrameData: () => getFrameData,
74
77
  getFrames: () => getFrames,
75
78
  getNodeBounds: () => getNodeBounds,
76
79
  isDynamicNode: () => isDynamicNode,
@@ -79,8 +82,8 @@ __export(Flow_exports, {
79
82
  useFlowData: () => useFlowData
80
83
  });
81
84
  module.exports = __toCommonJS(Flow_exports);
82
- var import_react3 = __toESM(require("react"), 1);
83
- var import_react4 = require("@xyflow/react");
85
+ var import_react4 = __toESM(require("react"), 1);
86
+ var import_react5 = require("@xyflow/react");
84
87
 
85
88
  // src/ui/Flow/types.ts
86
89
  function isDynamicNode(node) {
@@ -147,7 +150,7 @@ function collectionKeys(collection) {
147
150
 
148
151
  // src/ui/Flow/useFlow.ts
149
152
  function toNodeTypeDef(doc) {
150
- var _a, _b, _c, _d;
153
+ var _a, _b, _c, _d, _e, _f;
151
154
  return {
152
155
  slug: String((_a = doc.slug) != null ? _a : ""),
153
156
  name: String((_b = doc.title) != null ? _b : ""),
@@ -157,7 +160,9 @@ function toNodeTypeDef(doc) {
157
160
  height: 200
158
161
  },
159
162
  fields: Array.isArray(doc.fields) ? doc.fields : [],
160
- transparentBackground: Boolean(doc.transparentBackground)
163
+ transparentBackground: Boolean(doc.transparentBackground),
164
+ template: (_e = doc.template) != null ? _e : null,
165
+ customCSS: (_f = doc.customCSS) != null ? _f : null
161
166
  };
162
167
  }
163
168
  function toEdgeTypeDef(doc) {
@@ -275,37 +280,57 @@ function useFlowData(options) {
275
280
  }
276
281
 
277
282
  // src/ui/Flow/utils.ts
278
- function getNodeBounds(nodes, nodeIds) {
283
+ function getNodeSize(node) {
279
284
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
285
+ return {
286
+ width: (_e = (_d = (_c = (_a = node.style) == null ? void 0 : _a.width) != null ? _c : (_b = node.measured) == null ? void 0 : _b.width) != null ? _d : node.width) != null ? _e : 200,
287
+ height: (_j = (_i = (_h = (_f = node.style) == null ? void 0 : _f.height) != null ? _h : (_g = node.measured) == null ? void 0 : _g.height) != null ? _i : node.height) != null ? _j : 200
288
+ };
289
+ }
290
+ function getAbsolutePosition(node, nodeMap) {
291
+ let x = node.position.x;
292
+ let y = node.position.y;
293
+ let current = node;
294
+ const visited = /* @__PURE__ */ new Set([node.id]);
295
+ while (current.parentId) {
296
+ const parentId = current.parentId;
297
+ if (visited.has(parentId)) break;
298
+ const parent = nodeMap.get(parentId);
299
+ if (!parent) break;
300
+ visited.add(parent.id);
301
+ x += parent.position.x;
302
+ y += parent.position.y;
303
+ current = parent;
304
+ }
305
+ return { x, y };
306
+ }
307
+ function collectDescendants(nodes, rootId) {
308
+ const result = /* @__PURE__ */ new Set([rootId]);
309
+ const queue = [rootId];
310
+ let i = 0;
311
+ while (i < queue.length) {
312
+ const current = queue[i++];
313
+ for (const n of nodes) {
314
+ if (n.parentId === current && !result.has(n.id)) {
315
+ result.add(n.id);
316
+ queue.push(n.id);
317
+ }
318
+ }
319
+ }
320
+ return result;
321
+ }
322
+ function getNodeBounds(nodes, nodeIds) {
280
323
  const idSet = new Set(nodeIds);
281
324
  const targetNodes = nodes.filter((n) => idSet.has(n.id));
282
325
  if (targetNodes.length === 0) return void 0;
283
326
  const nodeMap = new Map(nodes.map((n) => [n.id, n]));
284
- function getAbsolutePosition(node) {
285
- let x = node.position.x;
286
- let y = node.position.y;
287
- let current = node;
288
- const visited = /* @__PURE__ */ new Set([node.id]);
289
- while (current.parentId) {
290
- const parentId = current.parentId;
291
- if (visited.has(parentId)) break;
292
- const parent = nodeMap.get(parentId);
293
- if (!parent) break;
294
- visited.add(parent.id);
295
- x += parent.position.x;
296
- y += parent.position.y;
297
- current = parent;
298
- }
299
- return { x, y };
300
- }
301
327
  let minX = Infinity;
302
328
  let minY = Infinity;
303
329
  let maxX = -Infinity;
304
330
  let maxY = -Infinity;
305
331
  for (const node of targetNodes) {
306
- const abs = getAbsolutePosition(node);
307
- const w = (_e = (_d = (_c = (_a = node.style) == null ? void 0 : _a.width) != null ? _c : (_b = node.measured) == null ? void 0 : _b.width) != null ? _d : node.width) != null ? _e : 200;
308
- const h = (_j = (_i = (_h = (_f = node.style) == null ? void 0 : _f.height) != null ? _h : (_g = node.measured) == null ? void 0 : _g.height) != null ? _i : node.height) != null ? _j : 200;
332
+ const abs = getAbsolutePosition(node, nodeMap);
333
+ const { width: w, height: h } = getNodeSize(node);
309
334
  minX = Math.min(minX, abs.x);
310
335
  minY = Math.min(minY, abs.y);
311
336
  maxX = Math.max(maxX, abs.x + w);
@@ -317,32 +342,140 @@ function getFrames(nodes) {
317
342
  const frames = nodes.filter((n) => n.type === "frame");
318
343
  if (frames.length === 0) return [];
319
344
  const nodeMap = new Map(nodes.map((n) => [n.id, n]));
320
- function getAbsolutePosition(node) {
321
- let x = node.position.x;
322
- let y = node.position.y;
323
- let current = node;
324
- const visited = /* @__PURE__ */ new Set([node.id]);
325
- while (current.parentId) {
326
- const parentId = current.parentId;
327
- if (visited.has(parentId)) break;
328
- const parent = nodeMap.get(parentId);
329
- if (!parent) break;
330
- visited.add(parent.id);
331
- x += parent.position.x;
332
- y += parent.position.y;
333
- current = parent;
334
- }
335
- return { x, y };
336
- }
337
345
  return frames.map((f) => {
338
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
346
+ var _a;
339
347
  const data = f.data;
340
- const abs = getAbsolutePosition(f);
341
- const w = (_e = (_d = (_c = (_a = f.style) == null ? void 0 : _a.width) != null ? _c : (_b = f.measured) == null ? void 0 : _b.width) != null ? _d : f.width) != null ? _e : 200;
342
- const h = (_j = (_i = (_h = (_f = f.style) == null ? void 0 : _f.height) != null ? _h : (_g = f.measured) == null ? void 0 : _g.height) != null ? _i : f.height) != null ? _j : 200;
343
- return { id: f.id, label: (_k = data.label) != null ? _k : "", bounds: { x: abs.x, y: abs.y, width: w, height: h } };
348
+ const abs = getAbsolutePosition(f, nodeMap);
349
+ const { width: w, height: h } = getNodeSize(f);
350
+ return {
351
+ id: f.id,
352
+ label: (_a = data.label) != null ? _a : "",
353
+ bounds: { x: abs.x, y: abs.y, width: w, height: h }
354
+ };
344
355
  }).sort((a, b) => a.bounds.y - b.bounds.y || a.bounds.x - b.bounds.x);
345
356
  }
357
+ function getFrameData(data, frameId) {
358
+ const frame = data.nodes.find((n) => n.id === frameId);
359
+ if (!frame || frame.type !== "frame") return void 0;
360
+ const descendantIds = collectDescendants(data.nodes, frameId);
361
+ const childNodes = data.nodes.filter((n) => descendantIds.has(n.id)).map((n) => __spreadProps(__spreadValues({}, n), { draggable: false }));
362
+ const childEdges = data.edges.filter(
363
+ (e) => descendantIds.has(e.source) && descendantIds.has(e.target)
364
+ );
365
+ const frameBounds = getNodeBounds(data.nodes, [frameId]);
366
+ const { width: w, height: h } = getNodeSize(frame);
367
+ const clampBounds = frameBounds != null ? frameBounds : {
368
+ x: frame.position.x,
369
+ y: frame.position.y,
370
+ width: w,
371
+ height: h
372
+ };
373
+ const contentNodeIds = childNodes.filter((n) => n.id !== frameId).map((n) => n.id);
374
+ const contentBounds = contentNodeIds.length > 0 ? getNodeBounds(data.nodes, contentNodeIds) : void 0;
375
+ const fitBounds = contentBounds != null ? contentBounds : clampBounds;
376
+ return {
377
+ data: {
378
+ nodes: childNodes,
379
+ edges: childEdges,
380
+ viewport: data.viewport
381
+ },
382
+ fitBounds,
383
+ clampBounds,
384
+ bounds: clampBounds
385
+ };
386
+ }
387
+
388
+ // src/ui/Flow/template-compiler.ts
389
+ var import_react3 = __toESM(require("react"), 1);
390
+ var import_sucrase = require("sucrase");
391
+ var MAX_CACHE_SIZE = 100;
392
+ var componentCache = /* @__PURE__ */ new Map();
393
+ function hashCode(str) {
394
+ let hash = 0;
395
+ for (let i = 0; i < str.length; i++) {
396
+ const char = str.charCodeAt(i);
397
+ hash = (hash << 5) - hash + char | 0;
398
+ }
399
+ return hash.toString(36);
400
+ }
401
+ var BLOCKED_PATTERNS = [
402
+ /\bdocument\s*\./,
403
+ /\bwindow\s*\./,
404
+ /\bwindow\s*\[/,
405
+ /\bglobalThis\s*\./,
406
+ /\bfetch\s*\(/,
407
+ /\bXMLHttpRequest/,
408
+ /\beval\s*\(/,
409
+ /\bFunction\s*\(/,
410
+ /\bimport\s*\(/,
411
+ /\blocalStorage/,
412
+ /\bsessionStorage/,
413
+ /\bcookie/,
414
+ /\bpostMessage\s*\(/,
415
+ /\blocation\s*[.=]/,
416
+ /\bnavigator\s*\./,
417
+ /\bsetTimeout\s*\(/,
418
+ /\bsetInterval\s*\(/,
419
+ /\bsetImmediate\s*\(/,
420
+ /\brequire\s*\(/
421
+ ];
422
+ function validateTemplateCode(code) {
423
+ return !BLOCKED_PATTERNS.some((pattern) => pattern.test(code));
424
+ }
425
+ function compileTemplate(code, slug) {
426
+ const cacheKey = `${slug}:${hashCode(code)}`;
427
+ if (componentCache.has(cacheKey)) {
428
+ const cached = componentCache.get(cacheKey);
429
+ componentCache.delete(cacheKey);
430
+ componentCache.set(cacheKey, cached);
431
+ return cached;
432
+ }
433
+ if (!validateTemplateCode(code)) {
434
+ console.warn(`[flow] Template "${slug}" contains blocked patterns`);
435
+ return null;
436
+ }
437
+ try {
438
+ const { code: jsCode } = (0, import_sucrase.transform)(code, {
439
+ transforms: ["typescript", "jsx", "imports"],
440
+ jsxRuntime: "classic",
441
+ jsxPragma: "React.createElement",
442
+ jsxFragmentPragma: "React.Fragment"
443
+ });
444
+ const factory = new Function(
445
+ "React",
446
+ `
447
+ var window = undefined;
448
+ var document = undefined;
449
+ var globalThis = undefined;
450
+ var setTimeout = undefined;
451
+ var setInterval = undefined;
452
+ var setImmediate = undefined;
453
+ var fetch = undefined;
454
+ var XMLHttpRequest = undefined;
455
+ var navigator = undefined;
456
+ var location = undefined;
457
+ var exports = {};
458
+ var module = { exports: exports };
459
+ ${jsCode}
460
+ return module.exports.default || module.exports;
461
+ `
462
+ );
463
+ const Component = factory(import_react3.default);
464
+ if (typeof Component !== "function") return null;
465
+ if (componentCache.size >= MAX_CACHE_SIZE) {
466
+ const oldestKey = componentCache.keys().next().value;
467
+ if (oldestKey) componentCache.delete(oldestKey);
468
+ }
469
+ componentCache.set(cacheKey, Component);
470
+ return Component;
471
+ } catch (e) {
472
+ console.warn(`[flow] Failed to compile template for "${slug}":`, e);
473
+ return null;
474
+ }
475
+ }
476
+ function clearTemplateCache() {
477
+ componentCache.clear();
478
+ }
346
479
 
347
480
  // src/ui/Flow/index.tsx
348
481
  function sanitizeUrl(url) {
@@ -356,8 +489,8 @@ function sanitizeUrl(url) {
356
489
  }
357
490
  }
358
491
  function toMarkerType(value) {
359
- if (value === "arrow") return import_react4.MarkerType.Arrow;
360
- if (value === "arrowclosed") return import_react4.MarkerType.ArrowClosed;
492
+ if (value === "arrow") return import_react5.MarkerType.Arrow;
493
+ if (value === "arrowclosed") return import_react5.MarkerType.ArrowClosed;
361
494
  return void 0;
362
495
  }
363
496
  function renderFieldValue(key, val, fieldDef) {
@@ -367,7 +500,7 @@ function renderFieldValue(key, val, fieldDef) {
367
500
  const imgUrl = typeof val === "string" ? val : val == null ? void 0 : val.url;
368
501
  const safeUrl = sanitizeUrl(imgUrl);
369
502
  if (!safeUrl) return null;
370
- return /* @__PURE__ */ import_react3.default.createElement(
503
+ return /* @__PURE__ */ import_react4.default.createElement(
371
504
  "img",
372
505
  {
373
506
  key,
@@ -378,7 +511,7 @@ function renderFieldValue(key, val, fieldDef) {
378
511
  }
379
512
  );
380
513
  }
381
- return /* @__PURE__ */ import_react3.default.createElement(
514
+ return /* @__PURE__ */ import_react4.default.createElement(
382
515
  "div",
383
516
  {
384
517
  key,
@@ -395,7 +528,7 @@ function renderFieldValue(key, val, fieldDef) {
395
528
  }
396
529
  function DefaultDynamicNode({ data }) {
397
530
  const d = data;
398
- return /* @__PURE__ */ import_react3.default.createElement(
531
+ return /* @__PURE__ */ import_react4.default.createElement(
399
532
  "div",
400
533
  {
401
534
  style: {
@@ -408,11 +541,59 @@ function DefaultDynamicNode({ data }) {
408
541
  d.fields && Object.entries(d.fields).filter(([, v]) => v != null && v !== "").map(([key, val]) => renderFieldValue(key, val))
409
542
  );
410
543
  }
544
+ var TemplateErrorBoundary = class extends import_react4.default.Component {
545
+ constructor() {
546
+ super(...arguments);
547
+ this.state = { error: null };
548
+ }
549
+ static getDerivedStateFromError(error) {
550
+ return { error };
551
+ }
552
+ componentDidUpdate(prevProps) {
553
+ if (prevProps.resetKey !== this.props.resetKey && this.state.error) {
554
+ this.setState({ error: null });
555
+ }
556
+ }
557
+ render() {
558
+ if (this.state.error) {
559
+ return /* @__PURE__ */ import_react4.default.createElement("div", { style: { padding: 8, fontSize: 11, color: "#ef4444" } }, /* @__PURE__ */ import_react4.default.createElement("strong", null, "Render error"), /* @__PURE__ */ import_react4.default.createElement("pre", { style: { fontSize: 10, whiteSpace: "pre-wrap" } }, this.state.error.message));
560
+ }
561
+ return this.props.children;
562
+ }
563
+ };
411
564
  function EnhancedDynamicNode({
412
565
  data,
413
- typeDef
566
+ typeDef,
567
+ width,
568
+ height
414
569
  }) {
415
- return /* @__PURE__ */ import_react3.default.createElement(
570
+ if (typeDef.template) {
571
+ const Component = compileTemplate(typeDef.template, typeDef.slug);
572
+ if (Component) {
573
+ return /* @__PURE__ */ import_react4.default.createElement(
574
+ "div",
575
+ {
576
+ className: `flow-node flow-node--${typeDef.slug}${typeDef.transparentBackground ? " flow-node--transparent-bg" : ""}`,
577
+ style: {
578
+ width: "100%",
579
+ height: "100%"
580
+ }
581
+ },
582
+ /* @__PURE__ */ import_react4.default.createElement(TemplateErrorBoundary, { resetKey: typeDef.template }, /* @__PURE__ */ import_react4.default.createElement(
583
+ Component,
584
+ {
585
+ fields: data.fields,
586
+ label: data.label,
587
+ color: typeDef.color,
588
+ nodeTypeSlug: typeDef.slug,
589
+ width: width != null ? width : typeDef.defaultSize.width,
590
+ height: height != null ? height : typeDef.defaultSize.height
591
+ }
592
+ ))
593
+ );
594
+ }
595
+ }
596
+ return /* @__PURE__ */ import_react4.default.createElement(
416
597
  "div",
417
598
  {
418
599
  style: {
@@ -441,7 +622,7 @@ function DefaultFrameNode({ data }) {
441
622
  if (m) return `rgba(${m[1]},${m[2]},${m[3]},${opacity})`;
442
623
  return baseColor;
443
624
  })();
444
- return /* @__PURE__ */ import_react3.default.createElement(
625
+ return /* @__PURE__ */ import_react4.default.createElement(
445
626
  "div",
446
627
  {
447
628
  style: {
@@ -452,7 +633,7 @@ function DefaultFrameNode({ data }) {
452
633
  border: borderStyle === "none" ? "none" : `2px ${borderStyle} rgba(128,128,128,0.3)`
453
634
  }
454
635
  },
455
- /* @__PURE__ */ import_react3.default.createElement(
636
+ /* @__PURE__ */ import_react4.default.createElement(
456
637
  "div",
457
638
  {
458
639
  style: {
@@ -474,7 +655,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
474
655
  const CustomRenderer = nodeRenderers == null ? void 0 : nodeRenderers[d.nodeTypeSlug];
475
656
  let content;
476
657
  if (CustomRenderer) {
477
- content = /* @__PURE__ */ import_react3.default.createElement(
658
+ content = /* @__PURE__ */ import_react4.default.createElement(
478
659
  CustomRenderer,
479
660
  {
480
661
  id: props.id,
@@ -485,9 +666,17 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
485
666
  }
486
667
  );
487
668
  } else if (typeDef) {
488
- content = /* @__PURE__ */ import_react3.default.createElement(EnhancedDynamicNode, { data: d, typeDef });
669
+ content = /* @__PURE__ */ import_react4.default.createElement(
670
+ EnhancedDynamicNode,
671
+ {
672
+ data: d,
673
+ typeDef,
674
+ width: props.width,
675
+ height: props.height
676
+ }
677
+ );
489
678
  } else {
490
- content = /* @__PURE__ */ import_react3.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
679
+ content = /* @__PURE__ */ import_react4.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
491
680
  }
492
681
  if (renderNode) {
493
682
  const slotProps = {
@@ -502,7 +691,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
502
691
  }
503
692
  if (nodeWrapper) {
504
693
  const Wrapper = nodeWrapper;
505
- content = /* @__PURE__ */ import_react3.default.createElement(
694
+ content = /* @__PURE__ */ import_react4.default.createElement(
506
695
  Wrapper,
507
696
  {
508
697
  id: props.id,
@@ -519,7 +708,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
519
708
  types.frame = frameRenderer ? ((props) => {
520
709
  const d = props.data;
521
710
  const Renderer = frameRenderer;
522
- return /* @__PURE__ */ import_react3.default.createElement(
711
+ return /* @__PURE__ */ import_react4.default.createElement(
523
712
  Renderer,
524
713
  {
525
714
  id: props.id,
@@ -534,13 +723,14 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
534
723
  return types;
535
724
  }
536
725
  function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
537
- if (!edgeRenderers || Object.keys(edgeRenderers).length === 0) return void 0;
726
+ if (!edgeRenderers || Object.keys(edgeRenderers).length === 0)
727
+ return void 0;
538
728
  const types = {};
539
729
  for (const [slug, Renderer] of Object.entries(edgeRenderers)) {
540
730
  types[slug] = ((props) => {
541
731
  var _a;
542
732
  const def = edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.get(slug);
543
- return /* @__PURE__ */ import_react3.default.createElement(
733
+ return /* @__PURE__ */ import_react4.default.createElement(
544
734
  Renderer,
545
735
  {
546
736
  id: props.id,
@@ -557,20 +747,99 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
557
747
  }
558
748
  return types;
559
749
  }
750
+ function clampViewport(vp, cw, ch, extent) {
751
+ const left = -vp.x / vp.zoom;
752
+ const right = (cw - vp.x) / vp.zoom;
753
+ const top = -vp.y / vp.zoom;
754
+ const bottom = (ch - vp.y) / vp.zoom;
755
+ const dx0 = left - extent[0][0];
756
+ const dx1 = right - extent[1][0];
757
+ const dy0 = top - extent[0][1];
758
+ const dy1 = bottom - extent[1][1];
759
+ const cx = dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1);
760
+ const cy = dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1);
761
+ if (cx === 0 && cy === 0) return vp;
762
+ return { x: vp.x + cx * vp.zoom, y: vp.y + cy * vp.zoom, zoom: vp.zoom };
763
+ }
560
764
  function FocusHandler({
561
765
  bounds,
562
766
  padding,
563
- animation
767
+ animation,
768
+ mode,
769
+ responsive,
770
+ extent
564
771
  }) {
565
- const { fitBounds } = (0, import_react4.useReactFlow)();
772
+ const { setViewport } = (0, import_react5.useReactFlow)();
773
+ const containerRef = import_react4.default.useRef(null);
566
774
  const boundsKey = `${bounds.x},${bounds.y},${bounds.width},${bounds.height}`;
567
- const boundsRef = import_react3.default.useRef(bounds);
775
+ const boundsRef = import_react4.default.useRef(bounds);
568
776
  boundsRef.current = bounds;
569
- import_react3.default.useEffect(() => {
570
- const duration = animation === true ? 300 : typeof animation === "number" ? animation : 0;
571
- fitBounds(boundsRef.current, { padding, duration });
572
- }, [boundsKey, padding, animation, fitBounds]);
573
- return null;
777
+ const [containerSize, setContainerSize] = import_react4.default.useState({ w: 0, h: 0 });
778
+ const prevBoundsKeyRef = import_react4.default.useRef(null);
779
+ const prevSizeRef = import_react4.default.useRef({ w: 0, h: 0 });
780
+ import_react4.default.useEffect(() => {
781
+ const el = containerRef.current;
782
+ if (!el) return;
783
+ const observer = new ResizeObserver((entries) => {
784
+ const entry = entries[0];
785
+ if (!entry) return;
786
+ const { width, height } = entry.contentRect;
787
+ setContainerSize({ w: width, h: height });
788
+ });
789
+ observer.observe(el);
790
+ return () => observer.disconnect();
791
+ }, []);
792
+ import_react4.default.useEffect(() => {
793
+ if (containerSize.w === 0 || containerSize.h === 0) return;
794
+ const prevKey = prevBoundsKeyRef.current;
795
+ const prevSize = prevSizeRef.current;
796
+ prevBoundsKeyRef.current = boundsKey;
797
+ prevSizeRef.current = { w: containerSize.w, h: containerSize.h };
798
+ const isBoundsChange = prevKey !== boundsKey;
799
+ const isResizeOnly = !isBoundsChange && (prevSize.w !== containerSize.w || prevSize.h !== containerSize.h);
800
+ const isInitial = prevKey === null;
801
+ if (isResizeOnly && !responsive) return;
802
+ const duration = isInitial || isBoundsChange ? animation === true ? 300 : typeof animation === "number" ? animation : 0 : 0;
803
+ const b = boundsRef.current;
804
+ const padX = padding * b.width;
805
+ const padY = padding * b.height;
806
+ const bw = b.width + padX * 2;
807
+ const bh = b.height + padY * 2;
808
+ if (bw === 0 || bh === 0) return;
809
+ const zoomFn = mode === "cover" ? Math.max : Math.min;
810
+ const zoom = zoomFn(containerSize.w / bw, containerSize.h / bh);
811
+ const cx = b.x + b.width / 2;
812
+ const cy = b.y + b.height / 2;
813
+ const x = containerSize.w / 2 - cx * zoom;
814
+ const y = containerSize.h / 2 - cy * zoom;
815
+ let vp = { x, y, zoom };
816
+ if (extent) {
817
+ vp = clampViewport(vp, containerSize.w, containerSize.h, extent);
818
+ }
819
+ setViewport(vp, { duration });
820
+ }, [
821
+ boundsKey,
822
+ padding,
823
+ animation,
824
+ mode,
825
+ responsive,
826
+ containerSize.w,
827
+ containerSize.h,
828
+ extent,
829
+ setViewport
830
+ ]);
831
+ return /* @__PURE__ */ import_react4.default.createElement(
832
+ "div",
833
+ {
834
+ ref: containerRef,
835
+ style: {
836
+ position: "absolute",
837
+ inset: 0,
838
+ pointerEvents: "none",
839
+ visibility: "hidden"
840
+ }
841
+ }
842
+ );
574
843
  }
575
844
  var EDGE_TYPE_MAP = {
576
845
  step: "step",
@@ -622,6 +891,10 @@ function FlowRenderer({
622
891
  interactive = false,
623
892
  fitView = true,
624
893
  onNodeClick,
894
+ onNodeDoubleClick,
895
+ onNodeContextMenu,
896
+ onNodeMouseEnter,
897
+ onNodeMouseLeave,
625
898
  onEdgeClick,
626
899
  frameRenderer,
627
900
  edgeRenderers,
@@ -634,27 +907,41 @@ function FlowRenderer({
634
907
  onViewportChange,
635
908
  defaultViewport: defaultViewportProp,
636
909
  bounds,
910
+ clampBounds,
637
911
  focusPadding,
638
- focusAnimation
912
+ focusAnimation,
913
+ focusMode = "contain",
914
+ responsiveFit,
915
+ translateExtent: translateExtentProp
639
916
  }) {
640
917
  var _a;
641
- const nodeTypeDefsMap = import_react3.default.useMemo(() => {
918
+ const nodeTypeDefsMap = import_react4.default.useMemo(() => {
642
919
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return void 0;
643
920
  return new Map(nodeTypeDefs.map((d) => [d.slug, d]));
644
921
  }, [nodeTypeDefs]);
645
- const edgeTypeDefsMap = import_react3.default.useMemo(() => {
922
+ const edgeTypeDefsMap = import_react4.default.useMemo(() => {
646
923
  if (!(edgeTypeDefs == null ? void 0 : edgeTypeDefs.length)) return void 0;
647
924
  return new Map(edgeTypeDefs.map((d) => [d.slug, d]));
648
925
  }, [edgeTypeDefs]);
649
- const nodeTypes = import_react3.default.useMemo(
650
- () => createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode),
926
+ const nodeTypes = import_react4.default.useMemo(
927
+ () => createNodeTypes(
928
+ nodeRenderers,
929
+ nodeTypeDefsMap,
930
+ frameRenderer,
931
+ nodeWrapper,
932
+ renderNode
933
+ ),
651
934
  [nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode]
652
935
  );
653
- const customEdgeTypes = import_react3.default.useMemo(
936
+ const customEdgeTypes = import_react4.default.useMemo(
654
937
  () => createEdgeTypes(edgeRenderers, edgeTypeDefsMap),
655
938
  [edgeRenderers, edgeTypeDefsMap]
656
939
  );
657
- const styledEdges = import_react3.default.useMemo(() => {
940
+ const mergedCSS = import_react4.default.useMemo(() => {
941
+ if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return "";
942
+ return nodeTypeDefs.filter((d) => d.customCSS).map((d) => d.customCSS).join("\n");
943
+ }, [nodeTypeDefs]);
944
+ const styledEdges = import_react4.default.useMemo(() => {
658
945
  var _a2;
659
946
  let edges = applyEdgeStyles((_a2 = data == null ? void 0 : data.edges) != null ? _a2 : [], edgeTypeDefsMap);
660
947
  if (edgeRenderers) {
@@ -668,50 +955,74 @@ function FlowRenderer({
668
955
  }
669
956
  return edges;
670
957
  }, [data == null ? void 0 : data.edges, edgeTypeDefsMap, edgeRenderers]);
958
+ const translateExtent = import_react4.default.useMemo(() => {
959
+ if (translateExtentProp) return translateExtentProp;
960
+ const es = clampBounds != null ? clampBounds : bounds;
961
+ if (!es) return void 0;
962
+ const ep = clampBounds ? 0 : focusPadding != null ? focusPadding : 0.1;
963
+ return [
964
+ [es.x - ep * es.width, es.y - ep * es.height],
965
+ [es.x + es.width * (1 + ep), es.y + es.height * (1 + ep)]
966
+ ];
967
+ }, [translateExtentProp, clampBounds, bounds, focusPadding]);
671
968
  if (!data) return null;
672
969
  const resolvedDefaultViewport = defaultViewportProp != null ? defaultViewportProp : !fitView && data.viewport ? data.viewport : void 0;
673
- return /* @__PURE__ */ import_react3.default.createElement(import_react4.ReactFlowProvider, null, /* @__PURE__ */ import_react3.default.createElement(
970
+ return /* @__PURE__ */ import_react4.default.createElement(import_react5.ReactFlowProvider, null, /* @__PURE__ */ import_react4.default.createElement(
674
971
  "div",
675
972
  {
676
973
  className,
677
- style: __spreadValues({ width: "100%", height: "100%" }, style)
974
+ style: __spreadValues({
975
+ width: "100%",
976
+ height: "100%",
977
+ background: "transparent"
978
+ }, style)
678
979
  },
679
- /* @__PURE__ */ import_react3.default.createElement(
680
- import_react4.ReactFlow,
980
+ /* @__PURE__ */ import_react4.default.createElement(
981
+ import_react5.ReactFlow,
681
982
  {
682
983
  nodes: (_a = data.nodes) != null ? _a : [],
683
984
  edges: styledEdges,
684
985
  nodeTypes,
685
986
  edgeTypes: customEdgeTypes,
686
987
  defaultViewport: resolvedDefaultViewport,
687
- fitView,
988
+ fitView: bounds ? false : fitView,
989
+ translateExtent,
688
990
  onNodeClick,
991
+ onNodeDoubleClick,
992
+ onNodeContextMenu,
993
+ onNodeMouseEnter,
994
+ onNodeMouseLeave,
689
995
  onEdgeClick,
690
996
  onMoveEnd: onViewportChange ? ((_, vp) => {
691
997
  onViewportChange(vp);
692
998
  }) : void 0,
693
- nodesDraggable: interactive ? void 0 : false,
999
+ nodesDraggable: interactive,
694
1000
  nodesConnectable: false,
695
- elementsSelectable: interactive || !!onNodeClick || !!onEdgeClick,
1001
+ elementsSelectable: interactive || !!onNodeClick || !!onNodeDoubleClick || !!onEdgeClick,
696
1002
  panOnDrag: interactive,
697
1003
  zoomOnScroll: interactive,
698
1004
  zoomOnPinch: interactive,
699
- zoomOnDoubleClick: false
1005
+ zoomOnDoubleClick: false,
1006
+ proOptions: { hideAttribution: true }
700
1007
  },
701
- background && /* @__PURE__ */ import_react3.default.createElement(import_react4.Background, null),
702
- controls && /* @__PURE__ */ import_react3.default.createElement(import_react4.Controls, null),
703
- minimap && /* @__PURE__ */ import_react3.default.createElement(
704
- import_react4.MiniMap,
1008
+ mergedCSS && /* @__PURE__ */ import_react4.default.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
1009
+ background && /* @__PURE__ */ import_react4.default.createElement(import_react5.Background, null),
1010
+ controls && /* @__PURE__ */ import_react4.default.createElement(import_react5.Controls, null),
1011
+ minimap && /* @__PURE__ */ import_react4.default.createElement(
1012
+ import_react5.MiniMap,
705
1013
  {
706
1014
  nodeColor: minimapNodeColor
707
1015
  }
708
1016
  ),
709
- bounds && /* @__PURE__ */ import_react3.default.createElement(
1017
+ bounds && /* @__PURE__ */ import_react4.default.createElement(
710
1018
  FocusHandler,
711
1019
  {
712
1020
  bounds,
713
1021
  padding: focusPadding != null ? focusPadding : 0.1,
714
- animation: focusAnimation != null ? focusAnimation : true
1022
+ animation: focusAnimation != null ? focusAnimation : true,
1023
+ mode: focusMode,
1024
+ responsive: responsiveFit != null ? responsiveFit : true,
1025
+ extent: translateExtent
715
1026
  }
716
1027
  ),
717
1028
  children