@tangle-network/sandbox-ui 0.81.0 → 0.83.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.
@@ -23,6 +23,8 @@ import "./chunk-U67V476Y.js";
23
23
  import {
24
24
  Background,
25
25
  Controls,
26
+ getNodesBounds,
27
+ getViewportForBounds,
26
28
  Handle,
27
29
  MarkerType,
28
30
  Panel,
@@ -392,11 +394,17 @@ function NodeMark({
392
394
  // src/workflows/WorkflowGraph.tsx
393
395
  import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
394
396
  var MUTED_TRACK = "color-mix(in srgb, hsl(var(--muted-foreground)) 22%, transparent)";
395
- var FIT_VIEW = { padding: 0.16, minZoom: 0.55, maxZoom: 1 };
396
- function fitViewOnLayoutChange() {
397
- const moves = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === false;
398
- return moves ? { ...FIT_VIEW, duration: 220 } : FIT_VIEW;
397
+ var FIT_VIEW = { padding: 0.16, minZoom: 0.55 };
398
+ function fitZoomCeiling(compact) {
399
+ return compact ? 1.5 : 1;
399
400
  }
401
+ var LAYOUT_TRANSITION_MS = 220;
402
+ var DENSITY_FLIP_AT = { grow: 0.12, shrink: 0.75 };
403
+ function motionAllowed() {
404
+ return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === false;
405
+ }
406
+ var lerp = (a, b, t) => a + (b - a) * t;
407
+ var easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
400
408
  function useColorMode() {
401
409
  const [mode, setMode] = useState(
402
410
  () => typeof document !== "undefined" && document.documentElement.classList.contains("dark") ? "dark" : "light"
@@ -419,7 +427,7 @@ function StatusFooter({
419
427
  elapsed
420
428
  }) {
421
429
  const roundsLabel = rounds !== void 0 ? `${rounds} round${rounds === 1 ? "" : "s"}` : void 0;
422
- return /* @__PURE__ */ jsxs2("div", { className: "mt-auto border-border border-t", children: [
430
+ return /* @__PURE__ */ jsxs2("div", { className: "wf-node-body-in mt-auto border-border border-t", children: [
423
431
  /* @__PURE__ */ jsx3(
424
432
  "div",
425
433
  {
@@ -558,7 +566,7 @@ function WorkflowNode({ data }) {
558
566
  }
559
567
  },
560
568
  children: [
561
- /* @__PURE__ */ jsx3(
569
+ /* @__PURE__ */ jsx3("span", { className: "wf-node-body-in flex items-center justify-center", children: /* @__PURE__ */ jsx3(
562
570
  NodeMark,
563
571
  {
564
572
  kind: d.kind,
@@ -567,7 +575,7 @@ function WorkflowNode({ data }) {
567
575
  accent,
568
576
  tile: Math.round(COMPACT_TILE * 0.62)
569
577
  }
570
- ),
578
+ ) }),
571
579
  d.badge && !state && /* @__PURE__ */ jsx3("span", { className: "-right-1.5 -top-1.5 absolute rounded-full border border-border bg-card px-1.5 py-[1px] font-medium text-[10px] text-muted-foreground", children: d.badge }),
572
580
  state && /* @__PURE__ */ jsx3(
573
581
  "span",
@@ -586,7 +594,7 @@ function WorkflowNode({ data }) {
586
594
  /* @__PURE__ */ jsxs2(
587
595
  "div",
588
596
  {
589
- className: `min-w-0 ${isLR ? "mt-2 w-full px-1 text-center" : "ml-2 flex-1 text-left"}`,
597
+ className: `wf-node-body-in min-w-0 ${isLR ? "mt-2 w-full px-1 text-center" : "ml-2 flex-1 text-left"}`,
590
598
  children: [
591
599
  /* @__PURE__ */ jsx3(
592
600
  "div",
@@ -621,7 +629,7 @@ function WorkflowNode({ data }) {
621
629
  },
622
630
  children: [
623
631
  handles,
624
- /* @__PURE__ */ jsxs2("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden px-3.5 pt-2.5 pb-2", children: [
632
+ /* @__PURE__ */ jsxs2("div", { className: "wf-node-body-in flex min-h-0 flex-1 flex-col overflow-hidden px-3.5 pt-2.5 pb-2", children: [
625
633
  /* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-2.5", children: [
626
634
  /* @__PURE__ */ jsx3(
627
635
  NodeMark,
@@ -719,7 +727,9 @@ function WorkflowGraph({
719
727
  const isPreview = variant === "preview";
720
728
  const [userCompact, setUserCompact] = useState(defaultCompact);
721
729
  const compact = isPreview || userCompact;
730
+ const [displayCompact, setDisplayCompact] = useState(compact);
722
731
  const rfRef = useRef(null);
732
+ const wrapperRef = useRef(null);
723
733
  const hasRunOverlay = nodeState !== void 0;
724
734
  const structural = useMemo(
725
735
  () => buildFlowGraph(yaml, {
@@ -735,9 +745,10 @@ function WorkflowGraph({
735
745
  );
736
746
  const [nodes, setNodes, onNodesChange] = useNodesState(structural.nodes);
737
747
  const [edges, setEdges, onEdgesChange] = useEdgesState(styledEdges);
738
- useEffect(() => {
739
- setNodes(structural.nodes);
740
- }, [structural, setNodes]);
748
+ const nodesRef = useRef(nodes);
749
+ nodesRef.current = nodes;
750
+ const nodeStateRef = useRef(nodeState);
751
+ nodeStateRef.current = nodeState;
741
752
  useEffect(() => {
742
753
  setEdges(styledEdges);
743
754
  }, [styledEdges, setEdges]);
@@ -745,17 +756,88 @@ function WorkflowGraph({
745
756
  const baseById = new Map(structural.nodes.map((n) => [n.id, n.data]));
746
757
  setNodes((prev) => mergeRunState(prev, baseById, nodeState));
747
758
  }, [nodeState, structural, setNodes]);
748
- const didInitialFitRef = useRef(false);
759
+ const didInitialSeedRef = useRef(false);
749
760
  useEffect(() => {
750
- if (!didInitialFitRef.current) {
751
- didInitialFitRef.current = true;
761
+ const withRunState = (nodes2) => {
762
+ const state = nodeStateRef.current;
763
+ if (!state) return nodes2;
764
+ return nodes2.map(
765
+ (n) => state[n.id] ? { ...n, data: { ...n.data, state: state[n.id] } } : n
766
+ );
767
+ };
768
+ if (!didInitialSeedRef.current) {
769
+ didInitialSeedRef.current = true;
770
+ setNodes(withRunState(structural.nodes));
771
+ setDisplayCompact(compact);
752
772
  return;
753
773
  }
754
774
  const inst = rfRef.current;
755
- if (!inst || typeof requestAnimationFrame === "undefined") return;
756
- const raf = requestAnimationFrame(() => inst.fitView(fitViewOnLayoutChange()));
757
- return () => cancelAnimationFrame(raf);
758
- }, [structural]);
775
+ const frame = wrapperRef.current?.getBoundingClientRect();
776
+ if (!inst || !frame || frame.width === 0 || frame.height === 0) {
777
+ setNodes(withRunState(structural.nodes));
778
+ setDisplayCompact(compact);
779
+ return;
780
+ }
781
+ const endViewport = getViewportForBounds(
782
+ getNodesBounds(structural.nodes),
783
+ frame.width,
784
+ frame.height,
785
+ FIT_VIEW.minZoom,
786
+ fitZoomCeiling(compact),
787
+ FIT_VIEW.padding
788
+ );
789
+ const prev = nodesRef.current;
790
+ const prevById = new Map(prev.map((n) => [n.id, n]));
791
+ const sameNodeSet = prev.length === structural.nodes.length && structural.nodes.every((n) => prevById.has(n.id));
792
+ if (!sameNodeSet || !motionAllowed() || typeof requestAnimationFrame === "undefined") {
793
+ setNodes(withRunState(structural.nodes));
794
+ setDisplayCompact(compact);
795
+ inst.setViewport(endViewport);
796
+ return;
797
+ }
798
+ const flipTimer = setTimeout(
799
+ () => setDisplayCompact(compact),
800
+ LAYOUT_TRANSITION_MS * (compact ? DENSITY_FLIP_AT.shrink : DENSITY_FLIP_AT.grow)
801
+ );
802
+ const startViewport = inst.getViewport();
803
+ const startedAt = performance.now();
804
+ let raf = requestAnimationFrame(function step(now) {
805
+ const t = Math.min(1, (now - startedAt) / LAYOUT_TRANSITION_MS);
806
+ const e = easeInOutCubic(t);
807
+ setNodes(
808
+ withRunState(
809
+ t >= 1 ? structural.nodes : structural.nodes.map((n) => {
810
+ const p = prevById.get(n.id);
811
+ if (!p || p.width === void 0 || p.height === void 0) {
812
+ return n;
813
+ }
814
+ const width = lerp(p.width, n.width ?? p.width, e);
815
+ const height = lerp(p.height, n.height ?? p.height, e);
816
+ return {
817
+ ...n,
818
+ position: {
819
+ x: lerp(p.position.x, n.position.x, e),
820
+ y: lerp(p.position.y, n.position.y, e)
821
+ },
822
+ width,
823
+ height,
824
+ style: { ...n.style, width, height }
825
+ };
826
+ })
827
+ )
828
+ );
829
+ inst.setViewport({
830
+ x: lerp(startViewport.x, endViewport.x, e),
831
+ y: lerp(startViewport.y, endViewport.y, e),
832
+ zoom: lerp(startViewport.zoom, endViewport.zoom, e)
833
+ });
834
+ if (t < 1) raf = requestAnimationFrame(step);
835
+ });
836
+ return () => {
837
+ cancelAnimationFrame(raf);
838
+ clearTimeout(flipTimer);
839
+ };
840
+ }, [structural, compact, setNodes]);
759
841
  const handleNodeClick = useCallback(
760
842
  (_event, node) => {
761
843
  onNodeClick?.(node.id, node.data);
@@ -771,7 +853,7 @@ function WorkflowGraph({
771
853
  }
772
854
  );
773
855
  }
774
- return /* @__PURE__ */ jsx3(DirectionContext.Provider, { value: direction, children: /* @__PURE__ */ jsx3(DensityContext.Provider, { value: compact, children: /* @__PURE__ */ jsx3("div", { className: `wf-graph ${className ?? ""}`, children: /* @__PURE__ */ jsxs2(
856
+ return /* @__PURE__ */ jsx3(DirectionContext.Provider, { value: direction, children: /* @__PURE__ */ jsx3(DensityContext.Provider, { value: displayCompact, children: /* @__PURE__ */ jsx3("div", { ref: wrapperRef, className: `wf-graph ${className ?? ""}`, children: /* @__PURE__ */ jsxs2(
775
857
  ReactFlow,
776
858
  {
777
859
  nodes,
@@ -785,7 +867,7 @@ function WorkflowGraph({
785
867
  },
786
868
  onNodeClick: onNodeClick ? handleNodeClick : void 0,
787
869
  fitView: true,
788
- fitViewOptions: FIT_VIEW,
870
+ fitViewOptions: { ...FIT_VIEW, maxZoom: fitZoomCeiling(compact) },
789
871
  proOptions: { hideAttribution: true },
790
872
  nodesDraggable: !isPreview,
791
873
  nodesConnectable: false,
@@ -826,8 +908,9 @@ function WorkflowGraph({
826
908
  export {
827
909
  DensityContext,
828
910
  DirectionContext,
911
+ LAYOUT_TRANSITION_MS,
829
912
  WorkflowGraph,
830
913
  WorkflowNode,
831
914
  buildStyledEdges,
832
- fitViewOnLayoutChange
915
+ fitZoomCeiling
833
916
  };
package/dist/globals.css CHANGED
@@ -1325,6 +1325,9 @@
1325
1325
  .flex-shrink-0 {
1326
1326
  flex-shrink: 0;
1327
1327
  }
1328
+ .shrink {
1329
+ flex-shrink: 1;
1330
+ }
1328
1331
  .shrink-0 {
1329
1332
  flex-shrink: 0;
1330
1333
  }
@@ -6267,6 +6270,22 @@
6267
6270
  .animate-in {
6268
6271
  animation: fade-in 0.25s ease-out;
6269
6272
  }
6273
+ .wf-node-body-in {
6274
+ animation: wf-node-body-in 150ms ease-out both;
6275
+ }
6276
+ @keyframes wf-node-body-in {
6277
+ from {
6278
+ opacity: 0;
6279
+ }
6280
+ to {
6281
+ opacity: 1;
6282
+ }
6283
+ }
6284
+ @media (prefers-reduced-motion: reduce) {
6285
+ .wf-node-body-in {
6286
+ animation: none;
6287
+ }
6288
+ }
6270
6289
  .animate-row-in {
6271
6290
  animation: row-in 0.18s ease-out;
6272
6291
  }
package/dist/styles.css CHANGED
@@ -1325,6 +1325,9 @@
1325
1325
  .flex-shrink-0 {
1326
1326
  flex-shrink: 0;
1327
1327
  }
1328
+ .shrink {
1329
+ flex-shrink: 1;
1330
+ }
1328
1331
  .shrink-0 {
1329
1332
  flex-shrink: 0;
1330
1333
  }
@@ -6267,6 +6270,22 @@
6267
6270
  .animate-in {
6268
6271
  animation: fade-in 0.25s ease-out;
6269
6272
  }
6273
+ .wf-node-body-in {
6274
+ animation: wf-node-body-in 150ms ease-out both;
6275
+ }
6276
+ @keyframes wf-node-body-in {
6277
+ from {
6278
+ opacity: 0;
6279
+ }
6280
+ to {
6281
+ opacity: 1;
6282
+ }
6283
+ }
6284
+ @media (prefers-reduced-motion: reduce) {
6285
+ .wf-node-body-in {
6286
+ animation: none;
6287
+ }
6288
+ }
6270
6289
  .animate-row-in {
6271
6290
  animation: row-in 0.18s ease-out;
6272
6291
  }
@@ -1,4 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ export { M as ModelBrandIdentity, a as ModelBrandStack, m as modelBrandFor } from './model-brand-CsFykzMA.js';
3
+ import '@tangle-network/agent-interface';
2
4
 
3
5
  /**
4
6
  * Turns a workflow YAML definition into a positioned node/edge graph for the
package/dist/workflows.js CHANGED
@@ -1,6 +1,11 @@
1
1
  import {
2
2
  buildWorkflowGraph
3
3
  } from "./chunk-2NS724P4.js";
4
+ import {
5
+ ModelBrandStack,
6
+ modelBrandFor
7
+ } from "./chunk-I3G23XHN.js";
8
+ import "./chunk-EI44GEQ5.js";
4
9
  import "./chunk-U67V476Y.js";
5
10
 
6
11
  // src/workflows/WorkflowGraphLazy.tsx
@@ -26,7 +31,7 @@ function retryImport(factory, retries = 2, delayMs = 350) {
26
31
  function lazyWorkflowGraph() {
27
32
  return lazy(
28
33
  () => retryImport(
29
- () => import("./WorkflowGraph-472MC3ML.js").then((m) => ({ default: m.WorkflowGraph }))
34
+ () => import("./WorkflowGraph-J2SVX3WP.js").then((m) => ({ default: m.WorkflowGraph }))
30
35
  )
31
36
  );
32
37
  }
@@ -97,6 +102,8 @@ function WorkflowGraph(props) {
97
102
  );
98
103
  }
99
104
  export {
105
+ ModelBrandStack,
100
106
  WorkflowGraph as WorkflowGraphLazy,
101
- buildWorkflowGraph
107
+ buildWorkflowGraph,
108
+ modelBrandFor
102
109
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.81.0",
4
- "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
3
+ "version": "0.83.0",
4
+ "description": "Unified UI component library for Tangle Sandbox \u2014 primitives, chat, dashboard, terminal, editor, and workspace components",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/tangle-network/sandbox-ui"