flowcloudai-ui 0.1.7 → 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
  }
@@ -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,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,8 @@ var TabBar = memo4(({
2098
2136
  closable = false,
2099
2137
  addable = false,
2100
2138
  draggable = false,
2101
- minWidthRatio = 0.07,
2102
- maxTabWidthRatio = 0.15,
2139
+ minTabWidth = "80px",
2140
+ maxTabWidth = "200px",
2103
2141
  fillWidth = true,
2104
2142
  onChange,
2105
2143
  onClose,
@@ -2138,8 +2176,8 @@ var TabBar = memo4(({
2138
2176
  }
2139
2177
  }
2140
2178
  const mergedStyle = { ...overrideStyle, ...style };
2141
- const dragKeyRef = useRef7(null);
2142
2179
  const navRef = useRef7(null);
2180
+ const boundModifier = useContainerBoundModifier(navRef);
2143
2181
  const prevItemsLengthRef = useRef7(items.length);
2144
2182
  useEffect8(() => {
2145
2183
  const prev = prevItemsLengthRef.current;
@@ -2173,38 +2211,19 @@ var TabBar = memo4(({
2173
2211
  },
2174
2212
  [onClose]
2175
2213
  );
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
- []
2214
+ const sensors = useSensors2(
2215
+ useSensor2(PointerSensor2, {
2216
+ activationConstraint: { distance: 5 }
2217
+ })
2184
2218
  );
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
- }, []);
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]);
2208
2227
  const rootClasses = [
2209
2228
  "fc-tab-bar",
2210
2229
  `fc-tab-bar--${variant}`,
@@ -2213,33 +2232,46 @@ var TabBar = memo4(({
2213
2232
  className
2214
2233
  ].filter(Boolean).join(" ");
2215
2234
  const navWrapStyle = {
2216
- "--tab-min-width": `${Math.round(screen.width * minWidthRatio)}px`,
2217
- "--tab-max-width": `${Math.round(screen.width * maxTabWidthRatio)}px`,
2235
+ "--tab-min-width": minTabWidth,
2236
+ "--tab-max-width": maxTabWidth,
2218
2237
  "--tab-fill-width": fillWidth ? "1" : "0"
2219
2238
  };
2220
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
+ ));
2221
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: [
2222
- items.map((item) => /* @__PURE__ */ jsx18(
2223
- TabItemView,
2258
+ /* @__PURE__ */ jsx18(
2259
+ DndContext2,
2224
2260
  {
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
- )),
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
+ ),
2243
2275
  addable && /* @__PURE__ */ jsx18(
2244
2276
  "div",
2245
2277
  {
@@ -2555,7 +2587,7 @@ function ContextMenuProvider({
2555
2587
  var useContextMenu = () => useContext4(ContextMenuContext);
2556
2588
 
2557
2589
  // src/components/Chat/Chat.tsx
2558
- 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";
2559
2591
 
2560
2592
  // src/components/SmartMessage/SmartMessage.tsx
2561
2593
  import { useState as useState16, useCallback as useCallback9 } from "react";
@@ -2665,7 +2697,7 @@ var Chat = ({
2665
2697
  const messagesContainerRef = useRef10(null);
2666
2698
  const messagesEndRef = useRef10(null);
2667
2699
  const historyPanelRef = useRef10(null);
2668
- const currentConversation = useMemo5(
2700
+ const currentConversation = useMemo6(
2669
2701
  () => conversations.find((c) => c.id === currentConversationId),
2670
2702
  [conversations, currentConversationId]
2671
2703
  );
@@ -2693,7 +2725,7 @@ var Chat = ({
2693
2725
  onMessageCopy?.(message);
2694
2726
  }
2695
2727
  }, [messages, onMessageCopy]);
2696
- const containerStyle = useMemo5(() => ({
2728
+ const containerStyle = useMemo6(() => ({
2697
2729
  height,
2698
2730
  width,
2699
2731
  ...style
@@ -2865,484 +2897,353 @@ var Chat = ({
2865
2897
  };
2866
2898
 
2867
2899
  // src/components/Relation/Relation.tsx
2868
- 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";
2869
2901
  import {
2870
2902
  ReactFlow,
2871
2903
  useNodesState,
2872
2904
  useEdgesState,
2873
- addEdge,
2874
- Handle,
2875
- Position,
2876
2905
  MarkerType,
2877
- ConnectionLineType,
2878
2906
  useReactFlow,
2879
- ReactFlowProvider
2907
+ ReactFlowProvider,
2908
+ Background,
2909
+ Position,
2910
+ Handle
2880
2911
  } from "@xyflow/react";
2881
2912
  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"
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"
2893
2922
  };
2894
- return map[iconType || "default"] || "\u{1F465}";
2923
+ return colors[type] || colors.default;
2895
2924
  };
2896
- var getStatusColor = (status) => {
2897
- const map = {
2898
- active: "#10b981",
2899
- inactive: "#6b7280",
2900
- 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"
2901
2933
  };
2902
- return map[status || "active"];
2934
+ return colors[type] || colors.default;
2903
2935
  };
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"
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}"
2918
2944
  };
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
- ] });
2945
+ return icons[type] || icons.default;
2962
2946
  };
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"
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" };
3010
3035
  };
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
- ] })
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
+ ) })
3042
3056
  ] });
3043
- });
3044
- CustomNode.displayName = "CustomNode";
3045
- var nodeTypes = {
3046
- custom: CustomNode
3047
3057
  };
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 };
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;
3055
3117
  };
3056
3118
  var RelationContent = ({
3057
- nodes: propNodes,
3058
- edges: propEdges,
3119
+ data,
3059
3120
  onNodeClick,
3060
- onNodeDoubleClick,
3061
3121
  onEdgeClick,
3062
- onConnect: onConnectProp,
3063
- onNodesChange: onNodesChangeProp,
3064
- onEdgesChange: onEdgesChangeProp,
3065
- fitView = true,
3066
- fitViewOptions,
3067
- className = "",
3068
- style = {},
3069
- height = "70vh",
3122
+ theme = "light",
3123
+ height = "600px",
3070
3124
  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
3125
+ className = "",
3126
+ style = {}
3083
3127
  }) => {
3084
- const [nodes, setNodes, onNodesChange] = useNodesState(propNodes || []);
3085
- const [edges, setEdges, onEdgesChange] = useEdgesState(propEdges || []);
3086
- const { fitView: fitViewFn } = useReactFlow();
3087
- 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);
3088
3133
  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]);
3134
+ const bgColor = isDark ? "#0f172a" : "#f8fafc";
3100
3135
  useEffect12(() => {
3101
- if (propNodes) {
3102
- setNodes(propNodes);
3136
+ if (!data?.nodes?.length) {
3137
+ setNodes([]);
3138
+ setEdges([]);
3139
+ setIsReady(false);
3140
+ return;
3103
3141
  }
3104
- }, [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]);
3105
3172
  useEffect12(() => {
3106
- if (propEdges) {
3107
- 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);
3108
3179
  }
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
- );
3180
+ }, [isReady, fitView]);
3156
3181
  const handleNodeClick = useCallback11(
3157
3182
  (_event, node) => {
3158
3183
  if (onNodeClick && node.data) {
3159
- onNodeClick(node.id, node.data, _event);
3184
+ const { id, name, type, description, avatar, importance } = node.data;
3185
+ onNodeClick({ id, name, type, description, avatar, importance });
3160
3186
  }
3161
3187
  },
3162
3188
  [onNodeClick]
3163
3189
  );
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
3190
  const handleEdgeClick = useCallback11(
3182
3191
  (_event, edge) => {
3183
- if (onEdgeClick) {
3184
- 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);
3185
3195
  }
3186
3196
  },
3187
- [onEdgeClick]
3197
+ [onEdgeClick, data]
3188
3198
  );
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
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
+ ] })
3225
3217
  }
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(
3218
+ );
3219
+ }
3220
+ return /* @__PURE__ */ jsx24(
3291
3221
  "div",
3292
3222
  {
3293
3223
  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
- ]
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
+ )
3346
3247
  }
3347
3248
  );
3348
3249
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowcloudai-ui",
3
- "version": "0.1.7",
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
  }