@01.software/sdk 0.4.1 → 0.4.3-dev.260324.6dc30aa

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}:${code.length}:${hashCode(code)}:${code.slice(0, 64)}`;
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,9 @@ 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(EnhancedDynamicNode, { data: d, typeDef, width: props.width, height: props.height });
489
670
  } else {
490
- content = /* @__PURE__ */ import_react3.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
671
+ content = /* @__PURE__ */ import_react4.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
491
672
  }
492
673
  if (renderNode) {
493
674
  const slotProps = {
@@ -502,7 +683,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
502
683
  }
503
684
  if (nodeWrapper) {
504
685
  const Wrapper = nodeWrapper;
505
- content = /* @__PURE__ */ import_react3.default.createElement(
686
+ content = /* @__PURE__ */ import_react4.default.createElement(
506
687
  Wrapper,
507
688
  {
508
689
  id: props.id,
@@ -519,7 +700,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
519
700
  types.frame = frameRenderer ? ((props) => {
520
701
  const d = props.data;
521
702
  const Renderer = frameRenderer;
522
- return /* @__PURE__ */ import_react3.default.createElement(
703
+ return /* @__PURE__ */ import_react4.default.createElement(
523
704
  Renderer,
524
705
  {
525
706
  id: props.id,
@@ -540,7 +721,7 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
540
721
  types[slug] = ((props) => {
541
722
  var _a;
542
723
  const def = edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.get(slug);
543
- return /* @__PURE__ */ import_react3.default.createElement(
724
+ return /* @__PURE__ */ import_react4.default.createElement(
544
725
  Renderer,
545
726
  {
546
727
  id: props.id,
@@ -560,17 +741,64 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
560
741
  function FocusHandler({
561
742
  bounds,
562
743
  padding,
563
- animation
744
+ animation,
745
+ mode,
746
+ responsive
564
747
  }) {
565
- const { fitBounds } = (0, import_react4.useReactFlow)();
748
+ const { fitBounds, setViewport } = (0, import_react5.useReactFlow)();
749
+ const containerRef = import_react4.default.useRef(null);
566
750
  const boundsKey = `${bounds.x},${bounds.y},${bounds.width},${bounds.height}`;
567
- const boundsRef = import_react3.default.useRef(bounds);
751
+ const boundsRef = import_react4.default.useRef(bounds);
568
752
  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;
753
+ const [containerSize, setContainerSize] = import_react4.default.useState({ w: 0, h: 0 });
754
+ const prevBoundsKeyRef = import_react4.default.useRef(null);
755
+ const prevSizeRef = import_react4.default.useRef({ w: 0, h: 0 });
756
+ import_react4.default.useEffect(() => {
757
+ const el = containerRef.current;
758
+ if (!el) return;
759
+ const observer = new ResizeObserver((entries) => {
760
+ const entry = entries[0];
761
+ if (!entry) return;
762
+ const { width, height } = entry.contentRect;
763
+ setContainerSize({ w: width, h: height });
764
+ });
765
+ observer.observe(el);
766
+ return () => observer.disconnect();
767
+ }, []);
768
+ import_react4.default.useEffect(() => {
769
+ if (containerSize.w === 0 || containerSize.h === 0) return;
770
+ const prevKey = prevBoundsKeyRef.current;
771
+ const prevSize = prevSizeRef.current;
772
+ prevBoundsKeyRef.current = boundsKey;
773
+ prevSizeRef.current = { w: containerSize.w, h: containerSize.h };
774
+ const isBoundsChange = prevKey !== boundsKey;
775
+ const isResizeOnly = !isBoundsChange && (prevSize.w !== containerSize.w || prevSize.h !== containerSize.h);
776
+ const isInitial = prevKey === null;
777
+ if (isResizeOnly && !responsive) return;
778
+ const duration = isInitial || isBoundsChange ? animation === true ? 300 : typeof animation === "number" ? animation : 0 : 0;
779
+ const b = boundsRef.current;
780
+ const padX = padding * b.width;
781
+ const padY = padding * b.height;
782
+ const bw = b.width + padX * 2;
783
+ const bh = b.height + padY * 2;
784
+ if (mode === "cover") {
785
+ const zoom = Math.max(containerSize.w / bw, containerSize.h / bh);
786
+ const cx = b.x + b.width / 2;
787
+ const cy = b.y + b.height / 2;
788
+ const x = containerSize.w / 2 - cx * zoom;
789
+ const y = containerSize.h / 2 - cy * zoom;
790
+ setViewport({ x, y, zoom }, { duration });
791
+ } else {
792
+ fitBounds(boundsRef.current, { padding, duration });
793
+ }
794
+ }, [boundsKey, padding, animation, mode, responsive, containerSize.w, containerSize.h, fitBounds, setViewport]);
795
+ return /* @__PURE__ */ import_react4.default.createElement(
796
+ "div",
797
+ {
798
+ ref: containerRef,
799
+ style: { position: "absolute", inset: 0, pointerEvents: "none", visibility: "hidden" }
800
+ }
801
+ );
574
802
  }
575
803
  var EDGE_TYPE_MAP = {
576
804
  step: "step",
@@ -634,27 +862,35 @@ function FlowRenderer({
634
862
  onViewportChange,
635
863
  defaultViewport: defaultViewportProp,
636
864
  bounds,
865
+ clampBounds,
637
866
  focusPadding,
638
- focusAnimation
867
+ focusAnimation,
868
+ focusMode = "contain",
869
+ responsiveFit,
870
+ translateExtent: translateExtentProp
639
871
  }) {
640
872
  var _a;
641
- const nodeTypeDefsMap = import_react3.default.useMemo(() => {
873
+ const nodeTypeDefsMap = import_react4.default.useMemo(() => {
642
874
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return void 0;
643
875
  return new Map(nodeTypeDefs.map((d) => [d.slug, d]));
644
876
  }, [nodeTypeDefs]);
645
- const edgeTypeDefsMap = import_react3.default.useMemo(() => {
877
+ const edgeTypeDefsMap = import_react4.default.useMemo(() => {
646
878
  if (!(edgeTypeDefs == null ? void 0 : edgeTypeDefs.length)) return void 0;
647
879
  return new Map(edgeTypeDefs.map((d) => [d.slug, d]));
648
880
  }, [edgeTypeDefs]);
649
- const nodeTypes = import_react3.default.useMemo(
881
+ const nodeTypes = import_react4.default.useMemo(
650
882
  () => createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode),
651
883
  [nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode]
652
884
  );
653
- const customEdgeTypes = import_react3.default.useMemo(
885
+ const customEdgeTypes = import_react4.default.useMemo(
654
886
  () => createEdgeTypes(edgeRenderers, edgeTypeDefsMap),
655
887
  [edgeRenderers, edgeTypeDefsMap]
656
888
  );
657
- const styledEdges = import_react3.default.useMemo(() => {
889
+ const mergedCSS = import_react4.default.useMemo(() => {
890
+ if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return "";
891
+ return nodeTypeDefs.filter((d) => d.customCSS).map((d) => d.customCSS).join("\n");
892
+ }, [nodeTypeDefs]);
893
+ const styledEdges = import_react4.default.useMemo(() => {
658
894
  var _a2;
659
895
  let edges = applyEdgeStyles((_a2 = data == null ? void 0 : data.edges) != null ? _a2 : [], edgeTypeDefsMap);
660
896
  if (edgeRenderers) {
@@ -670,27 +906,38 @@ function FlowRenderer({
670
906
  }, [data == null ? void 0 : data.edges, edgeTypeDefsMap, edgeRenderers]);
671
907
  if (!data) return null;
672
908
  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(
909
+ const pad = focusPadding != null ? focusPadding : 0.1;
910
+ const extentSource = clampBounds != null ? clampBounds : bounds;
911
+ const extentPad = clampBounds ? 0 : pad;
912
+ const translateExtent = translateExtentProp != null ? translateExtentProp : extentSource ? [
913
+ [extentSource.x - extentPad * extentSource.width, extentSource.y - extentPad * extentSource.height],
914
+ [
915
+ extentSource.x + extentSource.width * (1 + extentPad),
916
+ extentSource.y + extentSource.height * (1 + extentPad)
917
+ ]
918
+ ] : void 0;
919
+ return /* @__PURE__ */ import_react4.default.createElement(import_react5.ReactFlowProvider, null, /* @__PURE__ */ import_react4.default.createElement(
674
920
  "div",
675
921
  {
676
922
  className,
677
- style: __spreadValues({ width: "100%", height: "100%" }, style)
923
+ style: __spreadValues({ width: "100%", height: "100%", background: "transparent" }, style)
678
924
  },
679
- /* @__PURE__ */ import_react3.default.createElement(
680
- import_react4.ReactFlow,
925
+ /* @__PURE__ */ import_react4.default.createElement(
926
+ import_react5.ReactFlow,
681
927
  {
682
928
  nodes: (_a = data.nodes) != null ? _a : [],
683
929
  edges: styledEdges,
684
930
  nodeTypes,
685
931
  edgeTypes: customEdgeTypes,
686
932
  defaultViewport: resolvedDefaultViewport,
687
- fitView,
933
+ fitView: bounds ? false : fitView,
934
+ translateExtent,
688
935
  onNodeClick,
689
936
  onEdgeClick,
690
937
  onMoveEnd: onViewportChange ? ((_, vp) => {
691
938
  onViewportChange(vp);
692
939
  }) : void 0,
693
- nodesDraggable: interactive ? void 0 : false,
940
+ nodesDraggable: interactive,
694
941
  nodesConnectable: false,
695
942
  elementsSelectable: interactive || !!onNodeClick || !!onEdgeClick,
696
943
  panOnDrag: interactive,
@@ -698,20 +945,23 @@ function FlowRenderer({
698
945
  zoomOnPinch: interactive,
699
946
  zoomOnDoubleClick: false
700
947
  },
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,
948
+ mergedCSS && /* @__PURE__ */ import_react4.default.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
949
+ background && /* @__PURE__ */ import_react4.default.createElement(import_react5.Background, null),
950
+ controls && /* @__PURE__ */ import_react4.default.createElement(import_react5.Controls, null),
951
+ minimap && /* @__PURE__ */ import_react4.default.createElement(
952
+ import_react5.MiniMap,
705
953
  {
706
954
  nodeColor: minimapNodeColor
707
955
  }
708
956
  ),
709
- bounds && /* @__PURE__ */ import_react3.default.createElement(
957
+ bounds && /* @__PURE__ */ import_react4.default.createElement(
710
958
  FocusHandler,
711
959
  {
712
960
  bounds,
713
961
  padding: focusPadding != null ? focusPadding : 0.1,
714
- animation: focusAnimation != null ? focusAnimation : true
962
+ animation: focusAnimation != null ? focusAnimation : true,
963
+ mode: focusMode,
964
+ responsive: responsiveFit != null ? responsiveFit : true
715
965
  }
716
966
  ),
717
967
  children