flowcloudai-ui 0.1.6 → 0.1.8

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/index.css CHANGED
@@ -467,7 +467,7 @@
467
467
  display: flex;
468
468
  flex-direction: column;
469
469
  width: var(--sidebar-width);
470
- height: 100vh;
470
+ height: 100%;
471
471
  background: var(--fc-color-bg-secondary);
472
472
  border-right: 1px solid var(--fc-color-border);
473
473
  overflow: hidden;
@@ -507,6 +507,7 @@
507
507
  padding: var(--fc-space-sm, 8px) 0;
508
508
  }
509
509
  .fc-sidebar__footer {
510
+ flex-shrink: 0;
510
511
  padding: var(--fc-space-sm, 8px) 0;
511
512
  border-top: 1px solid var(--fc-color-border);
512
513
  }
@@ -1894,6 +1895,9 @@
1894
1895
  flex: 1;
1895
1896
  min-width: 0;
1896
1897
  }
1898
+ .fc-tab-bar__nav-outer[data-tauri-drag-region] {
1899
+ -webkit-app-region: drag;
1900
+ }
1897
1901
  .fc-tab-bar__nav-wrap {
1898
1902
  display: flex;
1899
1903
  align-items: stretch;
@@ -1906,7 +1910,7 @@
1906
1910
  display: none;
1907
1911
  }
1908
1912
  .fc-tab-bar__tab {
1909
- flex: 1 1 0;
1913
+ flex: var(--tab-fill-width, 1) 1 0;
1910
1914
  min-width: var(--tab-min-width, 80px);
1911
1915
  max-width: var(--tab-max-width, 200px);
1912
1916
  position: relative;
@@ -2034,10 +2038,13 @@
2034
2038
  -webkit-app-region: no-drag;
2035
2039
  }
2036
2040
  .fc-tab-bar__tab--draggable {
2037
- cursor: default;
2041
+ cursor: grab;
2042
+ will-change: transform;
2038
2043
  }
2039
2044
  .fc-tab-bar__tab--dragging {
2040
2045
  opacity: 0.4;
2046
+ cursor: grabbing;
2047
+ z-index: 1;
2041
2048
  }
2042
2049
  .fc-tab-bar__tab-close {
2043
2050
  display: inline-flex;
@@ -2071,7 +2078,7 @@
2071
2078
  color: var(--fc-color-text-secondary);
2072
2079
  cursor: pointer;
2073
2080
  transition: all var(--fc-transition, 150ms ease);
2074
- border-radius: var(--fc-radius-sm, 4px);
2081
+ border-radius: var(--fc-radius-sm, 2px);
2075
2082
  background-color: var(--tab-bar-bg);
2076
2083
  }
2077
2084
  .fc-tab-bar__add-btn:hover {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React$1 from 'react';
3
- import React__default, { ReactNode, ComponentType, FC, MouseEvent, CSSProperties } from 'react';
3
+ import React__default, { ReactNode, ComponentType, FC } from 'react';
4
4
 
5
5
  type Theme = 'light' | 'dark' | 'system';
6
6
  interface ThemeContextType {
@@ -455,17 +455,25 @@ interface TabBarProps {
455
455
  /** 是否启用拖拽排序 */
456
456
  draggable?: boolean;
457
457
  /**
458
- * Tab 最小宽度(rem 单位)。
458
+ * Tab 最小宽度,支持任意 CSS 长度值(如 "8rem"、"120px")。
459
459
  * Tab 压缩至此宽度后触发横向滚动。
460
- * @default '7rem'
460
+ * @default "80px"
461
461
  */
462
- minWidth?: string;
462
+ minTabWidth?: string;
463
463
  /**
464
- * Tab 最大宽度(rem 单位)。
464
+ * Tab 最大宽度,支持任意 CSS 长度值(如 "16rem"、"200px")。
465
465
  * Tab 较少时不超过此宽度。
466
- * @default '15rem'
466
+ * @default "200px"
467
467
  */
468
468
  maxTabWidth?: string;
469
+ /**
470
+ * 控制 Tab 是否自动填充容器宽度。
471
+ * - true: flex: 1 1 0,Tab 会自动拉伸填满导航栏(默认行为)
472
+ * - false: flex: 0 1 auto,Tab 只占内容宽度,剩余空间留白
473
+ * 当 tauriDragRegion 为 true 且 fillWidth 为 false 时,空白区域可作为窗口拖拽区。
474
+ * @default true
475
+ */
476
+ fillWidth?: boolean;
469
477
  onChange: (activeKey: string) => void;
470
478
  onClose?: (key: string) => void;
471
479
  onAdd?: () => void;
@@ -633,59 +641,32 @@ interface ChatProps {
633
641
  declare const Chat: React__default.FC<ChatProps>;
634
642
 
635
643
  interface RelationNodeData {
636
- iconType?: 'war' | 'target' | 'star' | 'award' | 'flag' | 'default' | 'user' | 'shield';
637
- title: string;
638
- subtitle: string;
644
+ id: string;
645
+ name: string;
646
+ type?: 'person' | 'organization' | 'event' | 'location' | 'concept';
639
647
  description?: string;
640
- imageUrl?: string;
641
- status?: 'active' | 'inactive' | 'warning';
642
- metadata?: Record<string, any>;
648
+ avatar?: string;
649
+ importance?: number;
643
650
  }
644
651
  interface RelationEdgeData {
652
+ source: string;
653
+ target: string;
645
654
  label?: string;
646
- type?: 'solid' | 'dashed' | 'dotted';
647
- color?: string;
648
- animated?: boolean;
649
- metadata?: Record<string, any>;
655
+ type?: 'friend' | 'enemy' | 'subordinate' | 'superior' | 'neutral';
656
+ strength?: number;
650
657
  }
651
658
  interface RelationProps {
652
- nodes?: any[];
653
- edges?: any[];
654
- onNodeClick?: (nodeId: string, nodeData: RelationNodeData, event?: MouseEvent) => void;
655
- onNodeDoubleClick?: (nodeId: string, nodeData: RelationNodeData) => void;
656
- onEdgeClick?: (edgeId: string, edgeData?: RelationEdgeData) => void;
657
- onConnect?: (connection: any) => void;
658
- onNodesChange?: (nodes: any[]) => void;
659
- onEdgesChange?: (edges: any[]) => void;
660
- fitView?: boolean;
661
- fitViewOptions?: any;
662
- className?: string;
663
- style?: CSSProperties;
664
- height?: string | number;
665
- width?: string | number;
666
- defaultViewport?: {
667
- x: number;
668
- y: number;
669
- zoom: number;
659
+ data?: {
660
+ nodes: RelationNodeData[];
661
+ edges: RelationEdgeData[];
670
662
  };
671
- minZoom?: number;
672
- maxZoom?: number;
673
- snapToGrid?: boolean;
674
- snapGrid?: [number, number];
675
- enableEdgeCreation?: boolean;
676
- enableNodeDrag?: boolean;
677
- onNodeContextMenu?: (nodeId: string, nodeData: RelationNodeData) => void;
663
+ onNodeClick?: (node: RelationNodeData) => void;
664
+ onEdgeClick?: (edge: RelationEdgeData) => void;
678
665
  theme?: 'dark' | 'light';
679
- edgeStyles?: {
680
- defaultColor?: string;
681
- hoverColor?: string;
682
- selectedColor?: string;
683
- };
684
- nodeStyles?: {
685
- borderRadius?: string;
686
- minWidth?: string;
687
- };
688
- showHandles?: boolean;
666
+ height?: string | number;
667
+ width?: string | number;
668
+ className?: string;
669
+ style?: React__default.CSSProperties;
689
670
  }
690
671
  declare const Relation: FC<RelationProps>;
691
672
 
package/dist/index.js CHANGED
@@ -2025,8 +2025,37 @@ var Card = ({
2025
2025
  };
2026
2026
 
2027
2027
  // src/components/Bar/TabBar.tsx
2028
- import { useRef as useRef7, useCallback as useCallback8, memo as memo4, useEffect as useEffect8 } from "react";
2028
+ import { useRef as useRef7, useCallback as useCallback8, memo as memo4, useEffect as useEffect8, useMemo as useMemo5 } from "react";
2029
+ import {
2030
+ DndContext as DndContext2,
2031
+ closestCenter,
2032
+ PointerSensor as PointerSensor2,
2033
+ useSensor as useSensor2,
2034
+ useSensors as useSensors2
2035
+ } from "@dnd-kit/core";
2036
+ import {
2037
+ SortableContext,
2038
+ useSortable,
2039
+ horizontalListSortingStrategy,
2040
+ arrayMove
2041
+ } from "@dnd-kit/sortable";
2042
+ import { CSS } from "@dnd-kit/utilities";
2029
2043
  import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
2044
+ function useContainerBoundModifier(containerRef) {
2045
+ return useMemo5(() => ({ transform, activeNodeRect }) => {
2046
+ if (!containerRef.current || !activeNodeRect) {
2047
+ return { ...transform, y: 0 };
2048
+ }
2049
+ const { left, right } = containerRef.current.getBoundingClientRect();
2050
+ const minX = left - activeNodeRect.left;
2051
+ const maxX = right - activeNodeRect.right;
2052
+ return {
2053
+ ...transform,
2054
+ y: 0,
2055
+ x: Math.min(Math.max(transform.x, minX), maxX)
2056
+ };
2057
+ }, [containerRef]);
2058
+ }
2030
2059
  var TabItemView = memo4(({
2031
2060
  item,
2032
2061
  isActive,
@@ -2038,36 +2067,44 @@ var TabItemView = memo4(({
2038
2067
  activeTabStyle,
2039
2068
  renderCloseIcon,
2040
2069
  onClick,
2041
- onClose,
2042
- onDragStart,
2043
- onDragOver,
2044
- onDrop,
2045
- onDragEnd
2070
+ onClose
2046
2071
  }) => {
2072
+ const {
2073
+ attributes,
2074
+ listeners,
2075
+ setNodeRef,
2076
+ transform,
2077
+ transition,
2078
+ isDragging
2079
+ } = useSortable({
2080
+ id: item.key,
2081
+ disabled: !draggable || !!item.disabled
2082
+ });
2047
2083
  const showClose = item.closable !== void 0 ? item.closable : closable;
2048
2084
  const classes = [
2049
2085
  "fc-tab-bar__tab",
2050
2086
  isActive && "fc-tab-bar__tab--active",
2051
2087
  item.disabled && "fc-tab-bar__tab--disabled",
2052
2088
  draggable && !item.disabled && "fc-tab-bar__tab--draggable",
2089
+ isDragging && "fc-tab-bar__tab--dragging",
2053
2090
  tabClassName,
2054
2091
  isActive && activeTabClassName
2055
2092
  ].filter(Boolean).join(" ");
2056
2093
  const mergedStyle = {
2094
+ transform: CSS.Transform.toString(transform),
2095
+ transition,
2057
2096
  ...tabStyle,
2058
2097
  ...isActive ? activeTabStyle : void 0
2059
2098
  };
2060
2099
  return /* @__PURE__ */ jsxs13(
2061
2100
  "div",
2062
2101
  {
2102
+ ref: setNodeRef,
2063
2103
  className: classes,
2064
2104
  style: mergedStyle,
2065
2105
  onClick: () => !item.disabled && onClick(item.key),
2066
- draggable: draggable && !item.disabled,
2067
- onDragStart: (e) => onDragStart(e, item.key),
2068
- onDragOver,
2069
- onDrop: (e) => onDrop(e, item.key),
2070
- onDragEnd,
2106
+ ...attributes,
2107
+ ...listeners,
2071
2108
  role: "tab",
2072
2109
  "aria-selected": isActive,
2073
2110
  "aria-disabled": item.disabled,
@@ -2078,6 +2115,7 @@ var TabItemView = memo4(({
2078
2115
  "span",
2079
2116
  {
2080
2117
  className: "fc-tab-bar__tab-close",
2118
+ onPointerDown: (e) => e.stopPropagation(),
2081
2119
  onClick: (e) => onClose(e, item.key),
2082
2120
  role: "button",
2083
2121
  "aria-label": "\u5173\u95ED",
@@ -2098,8 +2136,9 @@ var TabBar = memo4(({
2098
2136
  closable = false,
2099
2137
  addable = false,
2100
2138
  draggable = false,
2101
- minWidth = "7rem",
2102
- maxTabWidth = "15rem",
2139
+ minTabWidth = "80px",
2140
+ maxTabWidth = "200px",
2141
+ fillWidth = true,
2103
2142
  onChange,
2104
2143
  onClose,
2105
2144
  onAdd,
@@ -2137,8 +2176,8 @@ var TabBar = memo4(({
2137
2176
  }
2138
2177
  }
2139
2178
  const mergedStyle = { ...overrideStyle, ...style };
2140
- const dragKeyRef = useRef7(null);
2141
2179
  const navRef = useRef7(null);
2180
+ const boundModifier = useContainerBoundModifier(navRef);
2142
2181
  const prevItemsLengthRef = useRef7(items.length);
2143
2182
  useEffect8(() => {
2144
2183
  const prev = prevItemsLengthRef.current;
@@ -2172,38 +2211,19 @@ var TabBar = memo4(({
2172
2211
  },
2173
2212
  [onClose]
2174
2213
  );
2175
- const handleDragStart = useCallback8(
2176
- (e, key) => {
2177
- dragKeyRef.current = key;
2178
- e.dataTransfer.effectAllowed = "move";
2179
- const target = e.currentTarget;
2180
- requestAnimationFrame(() => target.classList.add("fc-tab-bar__tab--dragging"));
2181
- },
2182
- []
2214
+ const sensors = useSensors2(
2215
+ useSensor2(PointerSensor2, {
2216
+ activationConstraint: { distance: 5 }
2217
+ })
2183
2218
  );
2184
- const handleDragOver = useCallback8((e) => {
2185
- e.preventDefault();
2186
- e.dataTransfer.dropEffect = "move";
2187
- }, []);
2188
- const handleDrop = useCallback8(
2189
- (e, targetKey) => {
2190
- e.preventDefault();
2191
- const dragKey = dragKeyRef.current;
2192
- if (!dragKey || dragKey === targetKey || !onReorder) return;
2193
- const fromIndex = items.findIndex((i) => i.key === dragKey);
2194
- const toIndex = items.findIndex((i) => i.key === targetKey);
2195
- if (fromIndex === -1 || toIndex === -1) return;
2196
- const reordered = [...items];
2197
- const [moved] = reordered.splice(fromIndex, 1);
2198
- reordered.splice(toIndex, 0, moved);
2199
- onReorder(reordered);
2200
- },
2201
- [items, onReorder]
2202
- );
2203
- const handleDragEnd = useCallback8((e) => {
2204
- dragKeyRef.current = null;
2205
- e.currentTarget.classList.remove("fc-tab-bar__tab--dragging");
2206
- }, []);
2219
+ const handleDragEnd = useCallback8((event) => {
2220
+ const { active, over } = event;
2221
+ if (!over || active.id === over.id || !onReorder) return;
2222
+ const fromIndex = items.findIndex((i) => i.key === active.id);
2223
+ const toIndex = items.findIndex((i) => i.key === over.id);
2224
+ if (fromIndex === -1 || toIndex === -1) return;
2225
+ onReorder(arrayMove(items, fromIndex, toIndex));
2226
+ }, [items, onReorder]);
2207
2227
  const rootClasses = [
2208
2228
  "fc-tab-bar",
2209
2229
  `fc-tab-bar--${variant}`,
@@ -2212,32 +2232,46 @@ var TabBar = memo4(({
2212
2232
  className
2213
2233
  ].filter(Boolean).join(" ");
2214
2234
  const navWrapStyle = {
2215
- "--tab-min-width": minWidth,
2216
- "--tab-max-width": maxTabWidth
2235
+ "--tab-min-width": minTabWidth,
2236
+ "--tab-max-width": maxTabWidth,
2237
+ "--tab-fill-width": fillWidth ? "1" : "0"
2217
2238
  };
2218
2239
  const dragRegion = tauriDragRegion ? { "data-tauri-drag-region": "" } : {};
2240
+ const tabItems = items.map((item) => /* @__PURE__ */ jsx18(
2241
+ TabItemView,
2242
+ {
2243
+ item,
2244
+ isActive: activeKey === item.key,
2245
+ closable,
2246
+ draggable,
2247
+ tabClassName,
2248
+ activeTabClassName,
2249
+ tabStyle,
2250
+ activeTabStyle,
2251
+ renderCloseIcon,
2252
+ onClick: handleClick,
2253
+ onClose: handleClose
2254
+ },
2255
+ item.key
2256
+ ));
2219
2257
  return /* @__PURE__ */ jsx18("div", { className: rootClasses, style: mergedStyle, role: "tablist", ...dragRegion, children: /* @__PURE__ */ jsx18("div", { className: "fc-tab-bar__nav-outer", ...dragRegion, children: /* @__PURE__ */ jsxs13("div", { className: "fc-tab-bar__nav-wrap", ref: navRef, style: navWrapStyle, children: [
2220
- items.map((item) => /* @__PURE__ */ jsx18(
2221
- TabItemView,
2258
+ /* @__PURE__ */ jsx18(
2259
+ DndContext2,
2222
2260
  {
2223
- item,
2224
- isActive: activeKey === item.key,
2225
- closable,
2226
- draggable,
2227
- tabClassName,
2228
- activeTabClassName,
2229
- tabStyle,
2230
- activeTabStyle,
2231
- renderCloseIcon,
2232
- onClick: handleClick,
2233
- onClose: handleClose,
2234
- onDragStart: handleDragStart,
2235
- onDragOver: handleDragOver,
2236
- onDrop: handleDrop,
2237
- onDragEnd: handleDragEnd
2238
- },
2239
- item.key
2240
- )),
2261
+ sensors,
2262
+ collisionDetection: closestCenter,
2263
+ modifiers: [boundModifier],
2264
+ onDragEnd: handleDragEnd,
2265
+ children: /* @__PURE__ */ jsx18(
2266
+ SortableContext,
2267
+ {
2268
+ items: items.map((i) => i.key),
2269
+ strategy: horizontalListSortingStrategy,
2270
+ children: tabItems
2271
+ }
2272
+ )
2273
+ }
2274
+ ),
2241
2275
  addable && /* @__PURE__ */ jsx18(
2242
2276
  "div",
2243
2277
  {
@@ -2553,7 +2587,7 @@ function ContextMenuProvider({
2553
2587
  var useContextMenu = () => useContext4(ContextMenuContext);
2554
2588
 
2555
2589
  // src/components/Chat/Chat.tsx
2556
- import { useState as useState17, useRef as useRef10, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo5 } from "react";
2590
+ import { useState as useState17, useRef as useRef10, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
2557
2591
 
2558
2592
  // src/components/SmartMessage/SmartMessage.tsx
2559
2593
  import { useState as useState16, useCallback as useCallback9 } from "react";
@@ -2663,7 +2697,7 @@ var Chat = ({
2663
2697
  const messagesContainerRef = useRef10(null);
2664
2698
  const messagesEndRef = useRef10(null);
2665
2699
  const historyPanelRef = useRef10(null);
2666
- const currentConversation = useMemo5(
2700
+ const currentConversation = useMemo6(
2667
2701
  () => conversations.find((c) => c.id === currentConversationId),
2668
2702
  [conversations, currentConversationId]
2669
2703
  );
@@ -2691,7 +2725,7 @@ var Chat = ({
2691
2725
  onMessageCopy?.(message);
2692
2726
  }
2693
2727
  }, [messages, onMessageCopy]);
2694
- const containerStyle = useMemo5(() => ({
2728
+ const containerStyle = useMemo6(() => ({
2695
2729
  height,
2696
2730
  width,
2697
2731
  ...style
@@ -2863,484 +2897,353 @@ var Chat = ({
2863
2897
  };
2864
2898
 
2865
2899
  // src/components/Relation/Relation.tsx
2866
- import React13, { useCallback as useCallback11, useEffect as useEffect12, memo as memo5 } from "react";
2900
+ import { useEffect as useEffect12, useState as useState18, useCallback as useCallback11, useRef as useRef11 } from "react";
2867
2901
  import {
2868
2902
  ReactFlow,
2869
2903
  useNodesState,
2870
2904
  useEdgesState,
2871
- addEdge,
2872
- Handle,
2873
- Position,
2874
2905
  MarkerType,
2875
- ConnectionLineType,
2876
2906
  useReactFlow,
2877
- ReactFlowProvider
2907
+ ReactFlowProvider,
2908
+ Background,
2909
+ Position,
2910
+ Handle
2878
2911
  } from "@xyflow/react";
2879
2912
  import "@xyflow/react/dist/style.css";
2880
- import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2881
- var getIconEmoji = (iconType) => {
2882
- const map = {
2883
- war: "\u2694\uFE0F",
2884
- target: "\u{1F3AF}",
2885
- star: "\u2B50",
2886
- award: "\u{1F3C6}",
2887
- flag: "\u{1F6A9}",
2888
- default: "\u{1F465}",
2889
- user: "\u{1F464}",
2890
- shield: "\u{1F6E1}\uFE0F"
2913
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2914
+ var getNodeColor = (type, isDark) => {
2915
+ const colors = {
2916
+ person: isDark ? "#60a5fa" : "#3b82f6",
2917
+ organization: isDark ? "#34d399" : "#10b981",
2918
+ event: isDark ? "#f472b6" : "#ec4899",
2919
+ location: isDark ? "#fb923c" : "#f97316",
2920
+ concept: isDark ? "#a78bfa" : "#8b5cf6",
2921
+ default: isDark ? "#94a3b8" : "#64748b"
2891
2922
  };
2892
- return map[iconType || "default"] || "\u{1F465}";
2923
+ return colors[type] || colors.default;
2893
2924
  };
2894
- var getStatusColor = (status) => {
2895
- const map = {
2896
- active: "#10b981",
2897
- inactive: "#6b7280",
2898
- warning: "#f59e0b"
2925
+ var getEdgeColor = (type, isDark) => {
2926
+ const colors = {
2927
+ friend: isDark ? "#34d399" : "#10b981",
2928
+ enemy: isDark ? "#f87171" : "#ef4444",
2929
+ subordinate: isDark ? "#60a5fa" : "#3b82f6",
2930
+ superior: isDark ? "#a78bfa" : "#8b5cf6",
2931
+ neutral: isDark ? "#94a3b8" : "#64748b",
2932
+ default: isDark ? "#94a3b8" : "#64748b"
2899
2933
  };
2900
- return map[status || "active"];
2934
+ return colors[type] || colors.default;
2901
2935
  };
2902
- var ConnectionHandles = ({ isDark, showHandles }) => {
2903
- const handleStyle = {
2904
- background: isDark ? "#ff8e8e" : "#ff6b6b",
2905
- width: "8px",
2906
- height: "8px",
2907
- borderRadius: "50%",
2908
- border: `1px solid ${isDark ? "#1e293b" : "#ffffff"}`,
2909
- transition: "all 0.2s ease"
2910
- };
2911
- const hiddenHandleStyle = {
2912
- opacity: 0,
2913
- width: "0px",
2914
- height: "0px",
2915
- pointerEvents: "none"
2936
+ var getIconForType = (type) => {
2937
+ const icons = {
2938
+ person: "\u{1F464}",
2939
+ organization: "\u{1F3E2}",
2940
+ event: "\u{1F4C5}",
2941
+ location: "\u{1F4CD}",
2942
+ concept: "\u{1F4A1}",
2943
+ default: "\u{1F517}"
2916
2944
  };
2917
- const actualStyle = showHandles ? handleStyle : hiddenHandleStyle;
2918
- return /* @__PURE__ */ jsxs18(Fragment5, { children: [
2919
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "top-left", style: { ...actualStyle, left: "25%" } }),
2920
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "top-left-source", style: { ...actualStyle, left: "25%" } }),
2921
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "top-center", style: { ...actualStyle, left: "50%" } }),
2922
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "top-center-source", style: { ...actualStyle, left: "50%" } }),
2923
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "top-right", style: { ...actualStyle, left: "75%" } }),
2924
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "top-right-source", style: { ...actualStyle, left: "75%" } }),
2925
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "right-top", style: { ...actualStyle, top: "25%" } }),
2926
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "right-top-source", style: { ...actualStyle, top: "25%" } }),
2927
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "right-center", style: { ...actualStyle, top: "50%" } }),
2928
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "right-center-source", style: { ...actualStyle, top: "50%" } }),
2929
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "right-bottom", style: { ...actualStyle, top: "75%" } }),
2930
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "right-bottom-source", style: { ...actualStyle, top: "75%" } }),
2931
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "bottom-left", style: { ...actualStyle, left: "25%" } }),
2932
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "bottom-left-source", style: { ...actualStyle, left: "25%" } }),
2933
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "bottom-center", style: { ...actualStyle, left: "50%" } }),
2934
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "bottom-center-source", style: { ...actualStyle, left: "50%" } }),
2935
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "bottom-right", style: { ...actualStyle, left: "75%" } }),
2936
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "bottom-right-source", style: { ...actualStyle, left: "75%" } }),
2937
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "left-top", style: { ...actualStyle, top: "25%" } }),
2938
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "left-top-source", style: { ...actualStyle, top: "25%" } }),
2939
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "left-center", style: { ...actualStyle, top: "50%" } }),
2940
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "left-center-source", style: { ...actualStyle, top: "50%" } }),
2941
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "left-bottom", style: { ...actualStyle, top: "75%" } }),
2942
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "left-bottom-source", style: { ...actualStyle, top: "75%" } }),
2943
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "corner-top-left", style: { ...actualStyle, left: "10%" } }),
2944
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "corner-top-left-source", style: { ...actualStyle, left: "10%" } }),
2945
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "corner-top-right", style: { ...actualStyle, left: "90%" } }),
2946
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "corner-top-right-source", style: { ...actualStyle, left: "90%" } }),
2947
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "corner-bottom-left", style: { ...actualStyle, left: "10%" } }),
2948
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "corner-bottom-left-source", style: { ...actualStyle, left: "10%" } }),
2949
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "corner-bottom-right", style: { ...actualStyle, left: "90%" } }),
2950
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "corner-bottom-right-source", style: { ...actualStyle, left: "90%" } }),
2951
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "corner-left-top", style: { ...actualStyle, top: "10%" } }),
2952
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "corner-left-top-source", style: { ...actualStyle, top: "10%" } }),
2953
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "corner-left-bottom", style: { ...actualStyle, top: "90%" } }),
2954
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "corner-left-bottom-source", style: { ...actualStyle, top: "90%" } }),
2955
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "corner-right-top", style: { ...actualStyle, top: "10%" } }),
2956
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "corner-right-top-source", style: { ...actualStyle, top: "10%" } }),
2957
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "corner-right-bottom", style: { ...actualStyle, top: "90%" } }),
2958
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "corner-right-bottom-source", style: { ...actualStyle, top: "90%" } })
2959
- ] });
2945
+ return icons[type] || icons.default;
2960
2946
  };
2961
- var CustomNode = memo5(({ data, theme: propTheme }) => {
2962
- const isDark = (data.theme || propTheme) === "dark";
2963
- const showHandles = data.showHandles !== void 0 ? data.showHandles : false;
2964
- const nodeStyle = {
2965
- background: isDark ? "linear-gradient(135deg, #1e293b 0%, #0f172a 100%)" : "linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)",
2966
- border: `2px solid ${isDark ? "rgba(255, 255, 255, 0.15)" : "rgba(0, 0, 0, 0.08)"}`,
2967
- borderRadius: "14px",
2968
- boxShadow: isDark ? "0 8px 20px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2)" : "0 4px 12px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.05)",
2969
- minWidth: "240px",
2970
- cursor: "pointer",
2971
- transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
2972
- position: "relative"
2973
- };
2974
- const iconStyle = {
2975
- width: "52px",
2976
- height: "52px",
2977
- borderRadius: "14px",
2978
- display: "flex",
2979
- alignItems: "center",
2980
- justifyContent: "center",
2981
- background: isDark ? "linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.05) 100%)" : "linear-gradient(135deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.02) 100%)",
2982
- transition: "all 0.3s ease",
2983
- boxShadow: isDark ? "inset 0 1px 1px rgba(255, 255, 255, 0.1)" : "inset 0 1px 1px rgba(0, 0, 0, 0.02)"
2984
- };
2985
- const titleStyle = {
2986
- fontWeight: 700,
2987
- fontSize: "15px",
2988
- marginBottom: "4px",
2989
- whiteSpace: "nowrap",
2990
- overflow: "hidden",
2991
- textOverflow: "ellipsis",
2992
- color: isDark ? "#ffffff" : "#1e293b",
2993
- letterSpacing: isDark ? "0.3px" : "normal"
2994
- };
2995
- const subtitleStyle = {
2996
- fontSize: "11px",
2997
- fontWeight: 500,
2998
- marginBottom: "2px",
2999
- color: isDark ? "rgba(255, 255, 255, 0.7)" : "#64748b"
3000
- };
3001
- const descriptionStyle = {
3002
- fontSize: "10px",
3003
- marginTop: "6px",
3004
- whiteSpace: "nowrap",
3005
- overflow: "hidden",
3006
- textOverflow: "ellipsis",
3007
- color: isDark ? "rgba(255, 255, 255, 0.45)" : "#94a3b8"
2947
+ var CustomNode = ({ data }) => {
2948
+ const isDark = data.theme === "dark";
2949
+ const nodeColor = getNodeColor(data.type || "default", isDark);
2950
+ return /* @__PURE__ */ jsxs18(
2951
+ "div",
2952
+ {
2953
+ style: {
2954
+ background: isDark ? "#1e293b" : "#ffffff",
2955
+ border: `2px solid ${nodeColor}`,
2956
+ borderRadius: "12px",
2957
+ padding: "8px 12px",
2958
+ minWidth: "140px",
2959
+ cursor: "pointer",
2960
+ boxShadow: isDark ? "0 4px 12px rgba(0,0,0,0.3)" : "0 2px 8px rgba(0,0,0,0.1)",
2961
+ transition: "all 0.2s ease"
2962
+ },
2963
+ onMouseEnter: (e) => {
2964
+ e.currentTarget.style.transform = "translateY(-2px)";
2965
+ e.currentTarget.style.boxShadow = isDark ? "0 6px 16px rgba(0,0,0,0.4)" : "0 4px 12px rgba(0,0,0,0.15)";
2966
+ },
2967
+ onMouseLeave: (e) => {
2968
+ e.currentTarget.style.transform = "translateY(0)";
2969
+ e.currentTarget.style.boxShadow = isDark ? "0 4px 12px rgba(0,0,0,0.3)" : "0 2px 8px rgba(0,0,0,0.1)";
2970
+ },
2971
+ children: [
2972
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, style: { opacity: 0 } }),
2973
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, style: { opacity: 0 } }),
2974
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, style: { opacity: 0 } }),
2975
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, style: { opacity: 0 } }),
2976
+ /* @__PURE__ */ jsxs18("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
2977
+ /* @__PURE__ */ jsx24(
2978
+ "div",
2979
+ {
2980
+ style: {
2981
+ width: "32px",
2982
+ height: "32px",
2983
+ borderRadius: "50%",
2984
+ background: `linear-gradient(135deg, ${nodeColor}20, ${nodeColor}40)`,
2985
+ display: "flex",
2986
+ alignItems: "center",
2987
+ justifyContent: "center",
2988
+ fontSize: "16px"
2989
+ },
2990
+ children: getIconForType(data.type || "default")
2991
+ }
2992
+ ),
2993
+ /* @__PURE__ */ jsxs18("div", { children: [
2994
+ /* @__PURE__ */ jsx24(
2995
+ "div",
2996
+ {
2997
+ style: {
2998
+ fontWeight: 600,
2999
+ fontSize: "12px",
3000
+ color: isDark ? "#ffffff" : "#1e293b"
3001
+ },
3002
+ children: data.name
3003
+ }
3004
+ ),
3005
+ data.description && /* @__PURE__ */ jsx24(
3006
+ "div",
3007
+ {
3008
+ style: {
3009
+ fontSize: "10px",
3010
+ color: isDark ? "#94a3b8" : "#64748b",
3011
+ marginTop: "2px"
3012
+ },
3013
+ children: data.description
3014
+ }
3015
+ )
3016
+ ] })
3017
+ ] })
3018
+ ]
3019
+ }
3020
+ );
3021
+ };
3022
+ var nodeTypes = { custom: CustomNode };
3023
+ var SmartEdge = ({ id, sourceX, sourceY, targetX, targetY, style = {}, markerEnd, data }) => {
3024
+ const dx = targetX - sourceX;
3025
+ const dy = targetY - sourceY;
3026
+ const offsetX = dx * 0.2;
3027
+ const offsetY = dy * 0.2;
3028
+ const path = `M ${sourceX} ${sourceY} C ${sourceX + offsetX} ${sourceY + offsetY}, ${targetX - offsetX} ${targetY - offsetY}, ${targetX} ${targetY}`;
3029
+ const midX = (sourceX + targetX) / 2;
3030
+ const midY = (sourceY + targetY) / 2;
3031
+ const getLabelStyle = () => {
3032
+ if (style.stroke === "#ef4444") return { bg: "#fee2e2", color: "#dc2626" };
3033
+ if (style.stroke === "#10b981") return { bg: "#d1fae5", color: "#065f46" };
3034
+ return { bg: "#e2e8f0", color: "#475569" };
3008
3035
  };
3009
- return /* @__PURE__ */ jsxs18("div", { style: nodeStyle, className: "relation-node", children: [
3010
- /* @__PURE__ */ jsx24(ConnectionHandles, { isDark, showHandles }),
3011
- /* @__PURE__ */ jsxs18("div", { style: { display: "flex", alignItems: "center", gap: "14px", padding: "14px 18px" }, children: [
3012
- /* @__PURE__ */ jsxs18("div", { style: { position: "relative", flexShrink: 0 }, children: [
3013
- /* @__PURE__ */ jsx24("div", { style: iconStyle, children: data.imageUrl ? /* @__PURE__ */ jsx24(
3014
- "img",
3015
- {
3016
- src: data.imageUrl,
3017
- alt: data.title,
3018
- style: { width: "100%", height: "100%", objectFit: "cover", borderRadius: "12px" }
3019
- }
3020
- ) : /* @__PURE__ */ jsx24("span", { style: { fontSize: "28px", filter: isDark ? "drop-shadow(0 1px 2px rgba(0,0,0,0.2))" : "none" }, children: getIconEmoji(data.iconType) }) }),
3021
- data.status && /* @__PURE__ */ jsx24("div", { style: {
3022
- position: "absolute",
3023
- bottom: "-2px",
3024
- right: "-2px",
3025
- width: "12px",
3026
- height: "12px",
3027
- borderRadius: "50%",
3028
- border: `2px solid ${isDark ? "#1e293b" : "#ffffff"}`,
3029
- backgroundColor: getStatusColor(data.status),
3030
- animation: data.status === "warning" ? "pulse 2s infinite" : "none",
3031
- boxShadow: isDark ? "0 0 0 1px rgba(0,0,0,0.2)" : "none"
3032
- } })
3033
- ] }),
3034
- /* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: 0 }, children: [
3035
- /* @__PURE__ */ jsx24("div", { style: titleStyle, children: data.title }),
3036
- /* @__PURE__ */ jsx24("div", { style: subtitleStyle, children: data.subtitle }),
3037
- data.description && /* @__PURE__ */ jsx24("div", { style: descriptionStyle, children: data.description })
3038
- ] })
3039
- ] })
3036
+ const labelStyle = getLabelStyle();
3037
+ return /* @__PURE__ */ jsxs18("g", { children: [
3038
+ /* @__PURE__ */ jsx24("path", { id, style, className: "react-flow__edge-path", d: path, markerEnd, fill: "none" }),
3039
+ data?.label && /* @__PURE__ */ jsx24("foreignObject", { x: midX - 30, y: midY - 10, width: 60, height: 20, style: { overflow: "visible" }, children: /* @__PURE__ */ jsx24(
3040
+ "div",
3041
+ {
3042
+ style: {
3043
+ background: labelStyle.bg,
3044
+ padding: "2px 6px",
3045
+ borderRadius: "10px",
3046
+ fontSize: "10px",
3047
+ fontWeight: 500,
3048
+ color: labelStyle.color,
3049
+ textAlign: "center",
3050
+ whiteSpace: "nowrap",
3051
+ pointerEvents: "none"
3052
+ },
3053
+ children: data.label
3054
+ }
3055
+ ) })
3040
3056
  ] });
3041
- });
3042
- CustomNode.displayName = "CustomNode";
3043
- var nodeTypes = {
3044
- custom: CustomNode
3045
3057
  };
3046
- var getEdgeStyle = (theme, isHovered, isSelected) => {
3047
- const baseColor = theme === "dark" ? "#7c8ba0" : "#a0aec0";
3048
- const hoverColor = "#ff8e8e";
3049
- const selectedColor = "#ff6b6b";
3050
- if (isSelected) return { stroke: selectedColor, strokeWidth: 2.5 };
3051
- if (isHovered) return { stroke: hoverColor, strokeWidth: 2.5 };
3052
- return { stroke: baseColor, strokeWidth: 1.8 };
3058
+ var edgeTypes = { smart: SmartEdge };
3059
+ var calculateTreeLayout = (nodes, edges) => {
3060
+ if (nodes.length === 0) return [];
3061
+ const childrenMap = /* @__PURE__ */ new Map();
3062
+ const parentMap = /* @__PURE__ */ new Map();
3063
+ edges.forEach((edge) => {
3064
+ if (!childrenMap.has(edge.source)) childrenMap.set(edge.source, []);
3065
+ childrenMap.get(edge.source).push(edge.target);
3066
+ parentMap.set(edge.target, edge.source);
3067
+ });
3068
+ const roots = nodes.filter((n) => !parentMap.has(n.id));
3069
+ const actualRoots = roots.length > 0 ? roots : [nodes[0]];
3070
+ const positioned = [];
3071
+ const levelNodes = /* @__PURE__ */ new Map();
3072
+ const queue = actualRoots.map((r) => ({ id: r.id, level: 0 }));
3073
+ const visited = /* @__PURE__ */ new Set();
3074
+ while (queue.length > 0) {
3075
+ const item = queue.shift();
3076
+ if (visited.has(item.id)) continue;
3077
+ visited.add(item.id);
3078
+ if (!levelNodes.has(item.level)) levelNodes.set(item.level, []);
3079
+ levelNodes.get(item.level).push(item.id);
3080
+ const children = childrenMap.get(item.id) || [];
3081
+ children.forEach((childId) => {
3082
+ if (!visited.has(childId)) {
3083
+ queue.push({ id: childId, level: item.level + 1 });
3084
+ }
3085
+ });
3086
+ }
3087
+ const startX = 50;
3088
+ const startY = 50;
3089
+ const levelHeight = 120;
3090
+ const nodeWidth = 180;
3091
+ const nodeGap = 30;
3092
+ for (const [level, nodeIds] of levelNodes.entries()) {
3093
+ const totalWidth = nodeIds.length * nodeWidth + (nodeIds.length - 1) * nodeGap;
3094
+ let currentX = startX + (800 - totalWidth) / 2;
3095
+ nodeIds.forEach((nodeId, index) => {
3096
+ const node = nodes.find((n) => n.id === nodeId);
3097
+ if (node) {
3098
+ positioned.push({
3099
+ ...node,
3100
+ position: {
3101
+ x: currentX + index * (nodeWidth + nodeGap),
3102
+ y: startY + level * levelHeight
3103
+ }
3104
+ });
3105
+ }
3106
+ });
3107
+ }
3108
+ const positionedIds = new Set(positioned.map((p) => p.id));
3109
+ const remaining = nodes.filter((n) => !positionedIds.has(n.id));
3110
+ remaining.forEach((node, idx) => {
3111
+ positioned.push({
3112
+ ...node,
3113
+ position: { x: 50 + idx % 3 * 200, y: 500 + Math.floor(idx / 3) * 100 }
3114
+ });
3115
+ });
3116
+ return positioned;
3053
3117
  };
3054
3118
  var RelationContent = ({
3055
- nodes: propNodes,
3056
- edges: propEdges,
3119
+ data,
3057
3120
  onNodeClick,
3058
- onNodeDoubleClick,
3059
3121
  onEdgeClick,
3060
- onConnect: onConnectProp,
3061
- onNodesChange: onNodesChangeProp,
3062
- onEdgesChange: onEdgesChangeProp,
3063
- fitView = true,
3064
- fitViewOptions,
3065
- className = "",
3066
- style = {},
3067
- height = "70vh",
3122
+ theme = "light",
3123
+ height = "600px",
3068
3124
  width = "100%",
3069
- defaultViewport = { x: 0, y: 0, zoom: 1 },
3070
- minZoom = 0.5,
3071
- maxZoom = 2,
3072
- snapToGrid = false,
3073
- snapGrid = [15, 15],
3074
- enableEdgeCreation = true,
3075
- enableNodeDrag = true,
3076
- onNodeContextMenu,
3077
- theme: propTheme = "light",
3078
- edgeStyles = {},
3079
- nodeStyles = {},
3080
- showHandles = false
3125
+ className = "",
3126
+ style = {}
3081
3127
  }) => {
3082
- const [nodes, setNodes, onNodesChange] = useNodesState(propNodes || []);
3083
- const [edges, setEdges, onEdgesChange] = useEdgesState(propEdges || []);
3084
- const { fitView: fitViewFn } = useReactFlow();
3085
- const theme = propTheme;
3128
+ const [nodes, setNodes, onNodesChange] = useNodesState([]);
3129
+ const [edges, setEdges, onEdgesChange] = useEdgesState([]);
3130
+ const { fitView } = useReactFlow();
3131
+ const [isReady, setIsReady] = useState18(false);
3132
+ const isInitialized = useRef11(false);
3086
3133
  const isDark = theme === "dark";
3087
- const bgColor = isDark ? "#0f172a" : "#f5f7fa";
3088
- useEffect12(() => {
3089
- if (fitView && fitViewFn && (propNodes?.length || 0) > 0) {
3090
- const timer = setTimeout(() => {
3091
- fitViewFn({ duration: 300, padding: 0.2, ...fitViewOptions }).catch((error) => {
3092
- console.warn("Fit view failed:", error);
3093
- });
3094
- }, 100);
3095
- return () => clearTimeout(timer);
3096
- }
3097
- }, [fitView, fitViewFn, fitViewOptions, propNodes]);
3134
+ const bgColor = isDark ? "#0f172a" : "#f8fafc";
3098
3135
  useEffect12(() => {
3099
- if (propNodes) {
3100
- setNodes(propNodes);
3136
+ if (!data?.nodes?.length) {
3137
+ setNodes([]);
3138
+ setEdges([]);
3139
+ setIsReady(false);
3140
+ return;
3101
3141
  }
3102
- }, [propNodes, setNodes]);
3142
+ const positionedNodes = calculateTreeLayout(data.nodes, data.edges);
3143
+ setNodes(
3144
+ positionedNodes.map((node) => ({
3145
+ id: node.id,
3146
+ type: "custom",
3147
+ position: node.position,
3148
+ data: {
3149
+ ...node,
3150
+ theme
3151
+ }
3152
+ }))
3153
+ );
3154
+ setEdges(
3155
+ data.edges.map((edge) => {
3156
+ const edgeColor = getEdgeColor(edge.type || "neutral", isDark);
3157
+ return {
3158
+ id: `${edge.source}-${edge.target}`,
3159
+ source: edge.source,
3160
+ target: edge.target,
3161
+ type: "smart",
3162
+ label: edge.label,
3163
+ data: { label: edge.label },
3164
+ style: { stroke: edgeColor, strokeWidth: 2 },
3165
+ markerEnd: { type: MarkerType.ArrowClosed, width: 12, height: 12, color: edgeColor }
3166
+ };
3167
+ })
3168
+ );
3169
+ setIsReady(true);
3170
+ isInitialized.current = false;
3171
+ }, [data, theme, setNodes, setEdges]);
3103
3172
  useEffect12(() => {
3104
- if (propEdges) {
3105
- setEdges(propEdges);
3173
+ if (isReady && fitView && !isInitialized.current) {
3174
+ isInitialized.current = true;
3175
+ setTimeout(() => {
3176
+ fitView({ duration: 300, padding: 0.15 }).catch(() => {
3177
+ });
3178
+ }, 100);
3106
3179
  }
3107
- }, [propEdges, setEdges]);
3108
- const handleNodesChange = useCallback11(
3109
- (changes) => {
3110
- onNodesChange(changes);
3111
- if (onNodesChangeProp) {
3112
- setTimeout(() => {
3113
- onNodesChangeProp(nodes);
3114
- }, 0);
3115
- }
3116
- },
3117
- [onNodesChange, onNodesChangeProp, nodes]
3118
- );
3119
- const handleEdgesChange = useCallback11(
3120
- (changes) => {
3121
- onEdgesChange(changes);
3122
- if (onEdgesChangeProp) {
3123
- setTimeout(() => {
3124
- onEdgesChangeProp(edges);
3125
- }, 0);
3126
- }
3127
- },
3128
- [onEdgesChange, onEdgesChangeProp, edges]
3129
- );
3130
- const onConnect = useCallback11(
3131
- (params) => {
3132
- const newEdge = {
3133
- ...params,
3134
- id: `edge-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
3135
- type: "straight",
3136
- style: { stroke: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"), strokeWidth: 1.8 },
3137
- markerEnd: {
3138
- type: MarkerType.ArrowClosed,
3139
- color: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"),
3140
- width: 12,
3141
- height: 12
3142
- },
3143
- label: "",
3144
- labelStyle: { fill: isDark ? "#fff" : "#333", fontSize: 11, fontWeight: 400 },
3145
- labelBgStyle: { fill: "transparent", fillOpacity: 0 }
3146
- };
3147
- setEdges((eds) => addEdge(newEdge, eds));
3148
- if (onConnectProp) {
3149
- onConnectProp(params);
3150
- }
3151
- },
3152
- [setEdges, onConnectProp, edgeStyles.defaultColor, isDark]
3153
- );
3180
+ }, [isReady, fitView]);
3154
3181
  const handleNodeClick = useCallback11(
3155
3182
  (_event, node) => {
3156
3183
  if (onNodeClick && node.data) {
3157
- onNodeClick(node.id, node.data, _event);
3184
+ const { id, name, type, description, avatar, importance } = node.data;
3185
+ onNodeClick({ id, name, type, description, avatar, importance });
3158
3186
  }
3159
3187
  },
3160
3188
  [onNodeClick]
3161
3189
  );
3162
- const handleNodeDoubleClick = useCallback11(
3163
- (_event, node) => {
3164
- if (onNodeDoubleClick && node.data) {
3165
- onNodeDoubleClick(node.id, node.data);
3166
- }
3167
- },
3168
- [onNodeDoubleClick]
3169
- );
3170
- const handleNodeContextMenu = useCallback11(
3171
- (event, node) => {
3172
- event.preventDefault();
3173
- if (onNodeContextMenu && node.data) {
3174
- onNodeContextMenu(node.id, node.data);
3175
- }
3176
- },
3177
- [onNodeContextMenu]
3178
- );
3179
3190
  const handleEdgeClick = useCallback11(
3180
3191
  (_event, edge) => {
3181
- if (onEdgeClick) {
3182
- onEdgeClick(edge.id, edge.data);
3192
+ if (onEdgeClick && data?.edges) {
3193
+ const edgeData = data.edges.find((e) => `${e.source}-${e.target}` === edge.id);
3194
+ if (edgeData) onEdgeClick(edgeData);
3183
3195
  }
3184
3196
  },
3185
- [onEdgeClick]
3197
+ [onEdgeClick, data]
3186
3198
  );
3187
- const nodesWithTheme = React13.useMemo(() => {
3188
- return nodes.map((node) => ({
3189
- ...node,
3190
- data: {
3191
- ...node.data,
3192
- theme,
3193
- showHandles
3194
- },
3195
- className: `relation-node ${node.className || ""}`
3196
- }));
3197
- }, [nodes, theme, showHandles]);
3198
- const edgesWithStyle = React13.useMemo(() => {
3199
- return edges.map((edge) => ({
3200
- ...edge,
3201
- type: edge.type || "straight",
3202
- style: {
3203
- ...edge.style,
3204
- ...getEdgeStyle(theme)
3205
- },
3206
- labelStyle: {
3207
- fill: isDark ? "#fff" : "#333",
3208
- fontSize: 11,
3209
- fontWeight: 400,
3210
- ...edge.labelStyle
3211
- },
3212
- labelBgStyle: {
3213
- fill: "transparent",
3214
- fillOpacity: 0,
3215
- ...edge.labelBgStyle
3216
- },
3217
- markerEnd: {
3218
- type: MarkerType.ArrowClosed,
3219
- color: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"),
3220
- width: 12,
3221
- height: 12,
3222
- ...edge.markerEnd
3199
+ if (!data?.nodes?.length) {
3200
+ return /* @__PURE__ */ jsx24(
3201
+ "div",
3202
+ {
3203
+ style: {
3204
+ width,
3205
+ height,
3206
+ backgroundColor: bgColor,
3207
+ borderRadius: "12px",
3208
+ display: "flex",
3209
+ alignItems: "center",
3210
+ justifyContent: "center",
3211
+ color: isDark ? "#94a3b8" : "#64748b"
3212
+ },
3213
+ children: /* @__PURE__ */ jsxs18("div", { style: { textAlign: "center" }, children: [
3214
+ /* @__PURE__ */ jsx24("div", { style: { fontSize: "48px", marginBottom: "16px" }, children: "\u{1F4CA}" }),
3215
+ /* @__PURE__ */ jsx24("div", { children: "\u6682\u65E0\u5173\u7CFB\u6570\u636E" })
3216
+ ] })
3223
3217
  }
3224
- }));
3225
- }, [edges, theme, isDark, edgeStyles.defaultColor]);
3226
- const globalStyle = `
3227
- .relation-container {
3228
- position: relative;
3229
- width: 100%;
3230
- height: 100%;
3231
- overflow: hidden;
3232
- }
3233
- .react-flow__background,
3234
- .react-flow__pane,
3235
- .react-flow__renderer,
3236
- .react-flow__viewport {
3237
- background-color: ${bgColor} !important;
3238
- }
3239
- .relation-node {
3240
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease;
3241
- }
3242
- .relation-node:hover {
3243
- transform: translateY(-4px) scale(1.02);
3244
- box-shadow: ${isDark ? "0 12px 28px rgba(0, 0, 0, 0.4), 0 2px 4px rgba(0, 0, 0, 0.2)" : "0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.05)"};
3245
- }
3246
- .react-flow__edge-path {
3247
- transition: stroke-width 0.2s ease, stroke 0.2s ease;
3248
- }
3249
- .react-flow__edge:hover .react-flow__edge-path {
3250
- stroke-width: 2.5px;
3251
- stroke: ${edgeStyles.hoverColor || (isDark ? "#ffa5a5" : "#ff8e8e")};
3252
- }
3253
- /* \u8FDE\u63A5\u70B9\u60AC\u505C\u6548\u679C */
3254
- .react-flow__handle {
3255
- transition: all 0.2s ease;
3256
- }
3257
- .react-flow__handle:hover {
3258
- transform: scale(1.3);
3259
- background-color: ${isDark ? "#ffa5a5" : "#ff8e8e"} !important;
3260
- }
3261
- @keyframes pulse {
3262
- 0%, 100% {
3263
- opacity: 1;
3264
- transform: scale(1);
3265
- }
3266
- 50% {
3267
- opacity: 0.7;
3268
- transform: scale(1.2);
3269
- }
3270
- }
3271
- /* \u7B80\u7EA6\u6EDA\u52A8\u6761 */
3272
- .relation-container ::-webkit-scrollbar {
3273
- width: 6px;
3274
- height: 6px;
3275
- }
3276
- .relation-container ::-webkit-scrollbar-track {
3277
- background: ${isDark ? "#1e293b" : "#e2e8f0"};
3278
- border-radius: 3px;
3279
- }
3280
- .relation-container ::-webkit-scrollbar-thumb {
3281
- background: ${isDark ? "#475569" : "#cbd5e1"};
3282
- border-radius: 3px;
3283
- }
3284
- .relation-container ::-webkit-scrollbar-thumb:hover {
3285
- background: ${isDark ? "#ff8e8e" : "#ff6b6b"};
3286
- }
3287
- `;
3288
- return /* @__PURE__ */ jsxs18(
3218
+ );
3219
+ }
3220
+ return /* @__PURE__ */ jsx24(
3289
3221
  "div",
3290
3222
  {
3291
3223
  className: `relation-container ${className}`,
3292
- style: {
3293
- width,
3294
- height,
3295
- ...style,
3296
- backgroundColor: bgColor,
3297
- borderRadius: nodeStyles.borderRadius || "12px"
3298
- },
3299
- children: [
3300
- /* @__PURE__ */ jsx24("style", { children: globalStyle }),
3301
- /* @__PURE__ */ jsx24(
3302
- ReactFlow,
3303
- {
3304
- nodes: nodesWithTheme,
3305
- edges: edgesWithStyle,
3306
- onNodesChange: handleNodesChange,
3307
- onEdgesChange: handleEdgesChange,
3308
- onConnect: enableEdgeCreation ? onConnect : void 0,
3309
- onNodeClick: handleNodeClick,
3310
- onNodeDoubleClick: handleNodeDoubleClick,
3311
- onNodeContextMenu: handleNodeContextMenu,
3312
- onEdgeClick: handleEdgeClick,
3313
- nodeTypes,
3314
- fitView: false,
3315
- fitViewOptions,
3316
- defaultViewport,
3317
- minZoom,
3318
- maxZoom,
3319
- snapToGrid,
3320
- snapGrid,
3321
- nodesDraggable: enableNodeDrag,
3322
- nodesConnectable: enableEdgeCreation,
3323
- connectionLineType: ConnectionLineType.Straight,
3324
- connectionLineStyle: {
3325
- stroke: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"),
3326
- strokeWidth: 1.8
3327
- },
3328
- attributionPosition: "bottom-right",
3329
- zoomOnScroll: true,
3330
- zoomOnPinch: true,
3331
- zoomOnDoubleClick: false,
3332
- panOnScroll: false,
3333
- panOnDrag: true,
3334
- proOptions: { hideAttribution: true },
3335
- elevateEdgesOnSelect: true,
3336
- defaultEdgeOptions: {
3337
- type: "straight",
3338
- style: { stroke: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"), strokeWidth: 1.8 },
3339
- markerEnd: { type: MarkerType.ArrowClosed, width: 12, height: 12 }
3340
- }
3341
- }
3342
- )
3343
- ]
3224
+ style: { width, height, ...style, backgroundColor: bgColor, borderRadius: "12px", overflow: "hidden" },
3225
+ children: /* @__PURE__ */ jsx24(
3226
+ ReactFlow,
3227
+ {
3228
+ nodes,
3229
+ edges,
3230
+ onNodesChange,
3231
+ onEdgesChange,
3232
+ onNodeClick: handleNodeClick,
3233
+ onEdgeClick: handleEdgeClick,
3234
+ nodeTypes,
3235
+ edgeTypes,
3236
+ nodesDraggable: false,
3237
+ nodesConnectable: false,
3238
+ fitView: false,
3239
+ minZoom: 0.3,
3240
+ maxZoom: 2,
3241
+ zoomOnScroll: true,
3242
+ panOnDrag: true,
3243
+ proOptions: { hideAttribution: true },
3244
+ children: /* @__PURE__ */ jsx24(Background, { color: isDark ? "#1e293b" : "#e2e8f0", gap: 20, size: 1 })
3245
+ }
3246
+ )
3344
3247
  }
3345
3248
  );
3346
3249
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowcloudai-ui",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "author": "flowcloudai",
6
6
  "module": "dist/index.js",
@@ -35,6 +35,8 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@dnd-kit/core": "^6.3.1",
38
+ "@dnd-kit/sortable": "^10.0.0",
39
+ "@dnd-kit/utilities": "^3.2.2",
38
40
  "@uiw/react-md-editor": "^4.0.4",
39
41
  "@xyflow/react": "^12.10.2"
40
42
  }