flowcloudai-ui 0.1.7 → 0.1.9

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