flowcloudai-ui 0.1.3 → 0.1.5

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
@@ -1846,6 +1846,8 @@
1846
1846
  --tab-active-color: var(--fc-color-primary);
1847
1847
  --tab-active-bg: var(--fc-color-bg);
1848
1848
  --tab-active-indicator: var(--fc-color-primary);
1849
+ --tab-min-width: 8rem;
1850
+ --tab-max-width: 18rem;
1849
1851
  --tab-bar-radius-tl: var(--fc-radius-md);
1850
1852
  --tab-bar-radius-tr: var(--fc-radius-md);
1851
1853
  --tab-bar-radius-br: 0px;
@@ -1892,27 +1894,18 @@
1892
1894
  display: flex;
1893
1895
  align-items: stretch;
1894
1896
  }
1895
- .fc-tab-bar__nav-outer--scroll {
1896
- overflow: hidden;
1897
- }
1898
- .fc-tab-bar__nav {
1899
- flex: 1;
1900
- min-width: 0;
1897
+ .fc-tab-bar__nav-wrap {
1901
1898
  display: flex;
1902
1899
  align-items: stretch;
1903
- position: relative;
1904
- z-index: 0;
1905
1900
  }
1906
- .fc-tab-bar__nav--scroll {
1907
- overflow: hidden;
1908
- flex-shrink: 0;
1901
+ .fc-tab-bar__nav-wrap--scroll {
1902
+ flex: 1;
1903
+ min-width: 0;
1904
+ overflow-x: auto;
1905
+ scrollbar-width: none;
1909
1906
  }
1910
- .fc-tab-bar__nav-wrap {
1911
- display: flex;
1912
- gap: 0;
1913
- align-items: stretch;
1914
- max-width: 100%;
1915
- box-sizing: border-box;
1907
+ .fc-tab-bar__nav-wrap--scroll::-webkit-scrollbar {
1908
+ display: none;
1916
1909
  }
1917
1910
  .fc-tab-bar__tab {
1918
1911
  position: relative;
@@ -1929,11 +1922,13 @@
1929
1922
  border: none;
1930
1923
  user-select: none;
1931
1924
  border-radius: var(--tab-item-radius-tl) var(--tab-item-radius-tr) var(--tab-item-radius-br) var(--tab-item-radius-bl);
1925
+ flex: 1 1 var(--tab-max-width);
1926
+ min-width: var(--tab-min-width);
1927
+ max-width: var(--tab-max-width);
1928
+ overflow: hidden;
1929
+ box-sizing: border-box;
1932
1930
  }
1933
- .fc-tab-bar__nav--scroll .fc-tab-bar__tab {
1934
- justify-content: space-between;
1935
- }
1936
- .fc-tab-bar__tab:hover:not(.fc-tab-bar__tab--disabled):not(.fc-tab-bar__tab--dragging) {
1931
+ .fc-tab-bar__tab:hover:not(.fc-tab-bar__tab--disabled):not(.fc-tab-bar__tab--dragging):not(.fc-tab-bar__tab--active) {
1937
1932
  color: var(--tab-hover-color);
1938
1933
  background-color: var(--tab-hover-bg);
1939
1934
  }
@@ -1946,6 +1941,13 @@
1946
1941
  cursor: not-allowed;
1947
1942
  opacity: 0.6;
1948
1943
  }
1944
+ .fc-tab-bar__tab-label {
1945
+ flex: 1;
1946
+ min-width: 0;
1947
+ overflow: hidden;
1948
+ white-space: nowrap;
1949
+ text-overflow: ellipsis;
1950
+ }
1949
1951
  .fc-tab-bar--attached {
1950
1952
  border-bottom: 2px solid var(--fc-color-border);
1951
1953
  }
@@ -2022,31 +2024,10 @@
2022
2024
  .fc-tab-bar__add-btn {
2023
2025
  -webkit-app-region: no-drag;
2024
2026
  }
2025
- .fc-tab-bar--shrink .fc-tab-bar__tab {
2026
- overflow: hidden;
2027
- min-width: 0;
2028
- }
2029
- .fc-tab-bar--shrink .fc-tab-bar__tab[data-compact=true] {
2030
- padding-left: var(--fc-space-xs, 6px);
2031
- padding-right: var(--fc-space-xs, 6px);
2032
- }
2033
- .fc-tab-bar--shrink .fc-tab-bar__tab-label {
2034
- flex: 1;
2035
- min-width: 0;
2027
+ .fc-tab-bar__drag-handle {
2028
+ flex: 0 0 0px;
2036
2029
  overflow: hidden;
2037
- white-space: nowrap;
2038
- -webkit-mask-image:
2039
- linear-gradient(
2040
- to right,
2041
- black 0%,
2042
- black calc(100% - 20px),
2043
- transparent 100%);
2044
- mask-image:
2045
- linear-gradient(
2046
- to right,
2047
- black 0%,
2048
- black calc(100% - 20px),
2049
- transparent 100%);
2030
+ -webkit-app-region: drag;
2050
2031
  }
2051
2032
  .fc-tab-bar__tab--draggable {
2052
2033
  cursor: default;
@@ -2076,6 +2057,7 @@
2076
2057
  align-items: center;
2077
2058
  justify-content: center;
2078
2059
  width: 32px;
2060
+ flex-shrink: 0;
2079
2061
  margin: 0 var(--fc-space-sm, 8px);
2080
2062
  font-size: 20px;
2081
2063
  font-weight: bold;
@@ -2092,24 +2074,9 @@
2092
2074
  .fc-tab-bar--floating .fc-tab-bar__add-btn {
2093
2075
  border-radius: var(--fc-radius-full, 9999px);
2094
2076
  }
2095
- [data-theme=dark] .fc-tab-bar--attached {
2096
- border-bottom-color: var(--fc-color-border);
2097
- }
2098
- [data-theme=dark] .fc-tab-bar__tab--active {
2099
- background-color: var(--tab-active-bg);
2100
- }
2101
- [data-theme=dark] .fc-tab-bar__tab:hover:not(.fc-tab-bar__tab--disabled) {
2102
- background-color: var(--tab-hover-bg);
2103
- }
2104
2077
  [data-theme=dark] .fc-tab-bar--floating .fc-tab-bar__tab--active {
2105
2078
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2);
2106
2079
  }
2107
- [data-theme=dark] .fc-tab-bar__add-btn:hover {
2108
- background-color: var(--fc-color-bg-tertiary);
2109
- }
2110
- [data-theme=dark] .fc-tab-bar__tab-close:hover {
2111
- background-color: var(--fc-color-border-light);
2112
- }
2113
2080
  @media (max-width: 768px) {
2114
2081
  .fc-tab-bar__tab {
2115
2082
  padding: var(--fc-space-xs, 6px) var(--fc-space-md, 12px);
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, MouseEvent, CSSProperties } from 'react';
3
+ import React__default, { ReactNode, ComponentType, FC, MouseEvent, CSSProperties } from 'react';
4
4
 
5
5
  type Theme = 'light' | 'dark' | 'system';
6
6
  interface ThemeContextType {
@@ -423,87 +423,41 @@ interface CardProps {
423
423
  declare const Card: ({ image, imageSlot, imageHeight, title, description, actions, extraInfo, variant, hoverable, disabled, className, style, onClick, }: CardProps) => react_jsx_runtime.JSX.Element;
424
424
 
425
425
  interface TabItem {
426
- /** 唯一标识 */
427
426
  key: string;
428
- /** 标签显示内容 */
429
427
  label: React__default.ReactNode;
430
- /** 是否禁用 */
431
428
  disabled?: boolean;
432
- /** 是否可关闭(覆盖全局 closable) */
433
429
  closable?: boolean;
434
430
  }
435
431
  interface TabBarProps {
436
- /** Tab 列表(受控) */
437
432
  items: TabItem[];
438
- /** 当前激活的 Tab key(受控) */
439
433
  activeKey: string;
440
- /**
441
- * 布局变体
442
- * - attached: 贴合模式 — 标签底部紧贴导航栏下边缘,底部线条作为激活指示器
443
- * - floating: 悬浮模式 — 标签垂直居中悬浮,胶囊形态,背景填充作为激活指示器
444
- * @default 'attached'
445
- */
446
434
  variant?: 'attached' | 'floating';
447
- /** TabBar 容器圆角 */
448
435
  radius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
449
- /** 单个 Tab 项的圆角(仅在 floating 模式下或需要四周圆角时使用) */
450
436
  tabRadius?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
451
- /** 是否显示关闭按钮 */
452
437
  closable?: boolean;
453
- /** 是否显示添加按钮 */
454
438
  addable?: boolean;
455
- /** 是否启用拖拽排序 */
456
439
  draggable?: boolean;
457
- /**
458
- * Tab 最小宽度比例(相对 window.innerWidth)。
459
- * 当均分后每个 Tab 宽度 < window.innerWidth * minWidthRatio 时,切换为横向滚动模式。
460
- * @default 0.07
461
- */
462
- minWidthRatio?: number;
463
- /**
464
- * Tab 最大宽度比例(相对 window.innerWidth)。
465
- * 在固定模式下,单个 Tab 的宽度不会超过此值,防止子项很少时占满过多空间。
466
- * @default 0.15
467
- */
468
- maxTabWidthRatio?: number;
440
+ minTabWidth?: number;
441
+ maxTabWidth?: number;
469
442
  onChange: (activeKey: string) => void;
470
443
  onClose?: (key: string) => void;
471
444
  onAdd?: () => void;
472
445
  onReorder?: (reorderedItems: TabItem[]) => void;
473
- /** 每个 Tab 的自定义 className */
474
446
  tabClassName?: string;
475
- /** 激活态 Tab 的额外 className */
476
447
  activeTabClassName?: string;
477
- /** 每个 Tab 的自定义 inline style */
478
448
  tabStyle?: React__default.CSSProperties;
479
- /** 激活态 Tab 的自定义 inline style(会合并到 tabStyle 之上) */
480
449
  activeTabStyle?: React__default.CSSProperties;
481
- /** 自定义关闭图标渲染 */
482
450
  renderCloseIcon?: (key: string) => React__default.ReactNode;
483
- /** 自定义添加按钮渲染 */
484
451
  renderAddButton?: () => React__default.ReactNode;
485
452
  className?: string;
486
453
  style?: React__default.CSSProperties;
487
- /** 容器背景色 */
488
454
  background?: string;
489
- /** 标签默认文字色 */
490
455
  tabColor?: string;
491
- /** 标签 hover 文字色 */
492
456
  tabHoverColor?: string;
493
- /** 标签 hover 背景色 */
494
457
  tabHoverBackground?: string;
495
- /** 激活态文字色 */
496
458
  tabActiveColor?: string;
497
- /** 激活态背景色 */
498
459
  tabActiveBackground?: string;
499
- /** 激活态指示器颜色(attached 模式底线 / floating 模式无效) */
500
460
  activeIndicatorColor?: string;
501
- /**
502
- * 将 TabBar 空白区域标记为 Tauri 窗口拖拽区域。
503
- * 开启后,标签之外的空白处可拖动窗口;标签、关闭、添加按钮已内置 no-drag 保护。
504
- * @default false
505
- */
506
- tauriDragRegion?: boolean;
507
461
  }
508
462
  declare const TabBar: React__default.NamedExoticComponent<TabBarProps>;
509
463
 
@@ -676,7 +630,17 @@ interface RelationProps {
676
630
  enableNodeDrag?: boolean;
677
631
  onNodeContextMenu?: (nodeId: string, nodeData: RelationNodeData) => void;
678
632
  theme?: 'dark' | 'light';
633
+ edgeStyles?: {
634
+ defaultColor?: string;
635
+ hoverColor?: string;
636
+ selectedColor?: string;
637
+ };
638
+ nodeStyles?: {
639
+ borderRadius?: string;
640
+ minWidth?: string;
641
+ };
642
+ showHandles?: boolean;
679
643
  }
680
- declare const Relation: React.FC<RelationProps>;
644
+ declare const Relation: FC<RelationProps>;
681
645
 
682
646
  export { type AlertMode, type AlertProps, AlertProvider, type AlertProviderProps, type AlertType, Avatar, type AvatarProps, type AvatarShape, Button, ButtonGroup, ButtonToolbar, Card, type CardProps, type CategoryTreeNode, Chat, type ChatProps, CheckButton, type ContextMenuAction, type ContextMenuDivider, type ContextMenuItem, ContextMenuProvider, type ContextMenuProviderProps, type Conversation, DeleteDialog, type DeleteMode, type DropPosition, type FlatCategory, type FlatToTreeResult, Input, ListGroup, ListGroupItem, type ListGroupItemProps, type ListGroupProps, MarkdownEditor, type MarkdownEditorProps, type Message, type MessageRole, OrphanDialog, type OrphanResolution, type OrphanResolutionMap, Relation, type RelationEdgeData, type RelationNodeData, type RelationProps, RollingBox, Select, SideBar, type SideBarItem, type SideBarProps, Slider, SmartMessage, type SmartMessageProps, TabBar, type TabBarProps, type TabItem, TagItem, type TagItemProps, type TagSchema, type TagValue, type Theme, ThemeProvider, Tree, type TreeProps, VirtualList, findNodeInfo, flatToTree, isDescendantOf, lazyLoad, useAlert, useContextMenu, useTheme };
package/dist/index.js CHANGED
@@ -2025,107 +2025,13 @@ var Card = ({
2025
2025
  };
2026
2026
 
2027
2027
  // src/components/Bar/TabBar.tsx
2028
- import { useRef as useRef8, useCallback as useCallback9, memo as memo4, useEffect as useEffect9 } from "react";
2029
-
2030
- // src/components/Bar/useAdaptiveTabLayout.ts
2031
- import { useState as useState13, useRef as useRef7, useCallback as useCallback8, useEffect as useEffect8 } from "react";
2032
- function useAdaptiveTabLayout(navRef, options) {
2033
- const { itemsLength, addable, minWidthRatio, maxTabWidthRatio } = options;
2034
- const itemsLengthRef = useRef7(itemsLength);
2035
- itemsLengthRef.current = itemsLength;
2036
- const addableRef = useRef7(addable);
2037
- addableRef.current = addable;
2038
- const minWidthRatioRef = useRef7(minWidthRatio);
2039
- minWidthRatioRef.current = minWidthRatio;
2040
- const maxTabWidthRatioRef = useRef7(maxTabWidthRatio);
2041
- maxTabWidthRatioRef.current = maxTabWidthRatio;
2042
- const [layout, setLayout] = useState13({ scrollMode: false, tabWidth: void 0 });
2043
- const calculateTimeoutRef = useRef7(null);
2044
- const lastLayoutRef = useRef7({ scrollMode: false, tabWidth: void 0 });
2045
- const addBtnWidthRef = useRef7(0);
2046
- const lastAddableRef = useRef7(void 0);
2047
- const calculate = useCallback8(() => {
2048
- const navOuter = navRef.current?.parentElement;
2049
- const nav = navRef.current;
2050
- const currentItemsLength = itemsLengthRef.current;
2051
- const currentAddable = addableRef.current;
2052
- const currentMinWidthRatio = minWidthRatioRef.current;
2053
- const currentMaxTabWidthRatio = maxTabWidthRatioRef.current;
2054
- if (!nav || !navOuter || currentItemsLength === 0) return;
2055
- if (currentAddable !== lastAddableRef.current) {
2056
- lastAddableRef.current = currentAddable;
2057
- if (currentAddable) {
2058
- const addBtnElement = navOuter.querySelector(".fc-tab-bar__add-btn");
2059
- if (addBtnElement) {
2060
- const btnStyle = getComputedStyle(addBtnElement);
2061
- const marginL = parseFloat(btnStyle.marginLeft) || 0;
2062
- const marginR = parseFloat(btnStyle.marginRight) || 0;
2063
- addBtnWidthRef.current = addBtnElement.getBoundingClientRect().width + marginL + marginR;
2064
- }
2065
- } else {
2066
- addBtnWidthRef.current = 0;
2067
- }
2068
- }
2069
- const navOuterCS = getComputedStyle(navOuter);
2070
- const navWrap = nav.querySelector(".fc-tab-bar__nav-wrap");
2071
- const navOuterW = navOuter.clientWidth - parseFloat(navOuterCS.paddingLeft) - parseFloat(navOuterCS.paddingRight);
2072
- const navW = navOuterW - addBtnWidthRef.current;
2073
- const tabGap = navWrap ? parseFloat(getComputedStyle(navWrap).gap) : 0;
2074
- const totalGapWidth = (currentItemsLength - 1) * tabGap;
2075
- const idealW = (navW - totalGapWidth) / currentItemsLength;
2076
- const threshold = window.innerWidth * currentMinWidthRatio;
2077
- const maxAllowedWidth = window.innerWidth * currentMaxTabWidthRatio;
2078
- const minAllowedWidth = 42;
2079
- const prev = lastLayoutRef.current;
2080
- const hysteresisFactor = 1.1;
2081
- let newScrollMode;
2082
- let newTabWidth;
2083
- if (idealW < threshold) {
2084
- newScrollMode = true;
2085
- newTabWidth = Math.max(threshold, minAllowedWidth);
2086
- } else if (prev.scrollMode && idealW < threshold * hysteresisFactor) {
2087
- newScrollMode = true;
2088
- newTabWidth = Math.max(threshold, minAllowedWidth);
2089
- } else {
2090
- newScrollMode = false;
2091
- newTabWidth = Math.min(Math.max(idealW, minAllowedWidth), maxAllowedWidth);
2092
- }
2093
- if (prev.tabWidth !== newTabWidth || prev.scrollMode !== newScrollMode) {
2094
- const newLayout = { scrollMode: newScrollMode, tabWidth: newTabWidth };
2095
- lastLayoutRef.current = newLayout;
2096
- setLayout(newLayout);
2097
- }
2098
- }, [navRef]);
2099
- const calculateDebounced = useCallback8(() => {
2100
- if (calculateTimeoutRef.current) clearTimeout(calculateTimeoutRef.current);
2101
- calculateTimeoutRef.current = setTimeout(calculate, 50);
2102
- }, [calculate]);
2103
- const calculateDebouncedRef = useRef7(calculateDebounced);
2104
- calculateDebouncedRef.current = calculateDebounced;
2105
- useEffect8(() => {
2106
- const rafId = requestAnimationFrame(calculate);
2107
- const handleResize = () => calculateDebouncedRef.current();
2108
- window.addEventListener("resize", handleResize);
2109
- return () => {
2110
- cancelAnimationFrame(rafId);
2111
- window.removeEventListener("resize", handleResize);
2112
- if (calculateTimeoutRef.current) clearTimeout(calculateTimeoutRef.current);
2113
- };
2114
- }, []);
2115
- useEffect8(() => {
2116
- calculate();
2117
- }, [itemsLength, calculate]);
2118
- return layout;
2119
- }
2120
-
2121
- // src/components/Bar/TabBar.tsx
2028
+ import { useRef as useRef7, useCallback as useCallback8, memo as memo4, useEffect as useEffect8 } from "react";
2122
2029
  import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
2123
2030
  var TabItemView = memo4(({
2124
2031
  item,
2125
2032
  isActive,
2126
2033
  closable,
2127
2034
  draggable,
2128
- tabWidth,
2129
2035
  tabClassName,
2130
2036
  activeTabClassName,
2131
2037
  tabStyle,
@@ -2139,7 +2045,6 @@ var TabItemView = memo4(({
2139
2045
  onDragEnd
2140
2046
  }) => {
2141
2047
  const showClose = item.closable !== void 0 ? item.closable : closable;
2142
- const isCompact = tabWidth !== void 0 && tabWidth < 42;
2143
2048
  const classes = [
2144
2049
  "fc-tab-bar__tab",
2145
2050
  isActive && "fc-tab-bar__tab--active",
@@ -2149,7 +2054,6 @@ var TabItemView = memo4(({
2149
2054
  isActive && activeTabClassName
2150
2055
  ].filter(Boolean).join(" ");
2151
2056
  const mergedStyle = {
2152
- ...tabWidth !== void 0 ? { width: tabWidth, boxSizing: "border-box", flexShrink: 0 } : void 0,
2153
2057
  ...tabStyle,
2154
2058
  ...isActive ? activeTabStyle : void 0
2155
2059
  };
@@ -2158,7 +2062,6 @@ var TabItemView = memo4(({
2158
2062
  {
2159
2063
  className: classes,
2160
2064
  style: mergedStyle,
2161
- "data-compact": isCompact || void 0,
2162
2065
  onClick: () => !item.disabled && onClick(item.key),
2163
2066
  draggable: draggable && !item.disabled,
2164
2067
  onDragStart: (e) => onDragStart(e, item.key),
@@ -2177,7 +2080,7 @@ var TabItemView = memo4(({
2177
2080
  className: "fc-tab-bar__tab-close",
2178
2081
  onClick: (e) => onClose(e, item.key),
2179
2082
  role: "button",
2180
- "aria-label": `\u5173\u95ED`,
2083
+ "aria-label": "\u5173\u95ED",
2181
2084
  children: renderCloseIcon ? renderCloseIcon(item.key) : "\xD7"
2182
2085
  }
2183
2086
  )
@@ -2195,8 +2098,8 @@ var TabBar = memo4(({
2195
2098
  closable = false,
2196
2099
  addable = false,
2197
2100
  draggable = false,
2198
- minWidthRatio = 0.07,
2199
- maxTabWidthRatio = 0.15,
2101
+ minTabWidth = 8,
2102
+ maxTabWidth = 18,
2200
2103
  onChange,
2201
2104
  onClose,
2202
2105
  onAdd,
@@ -2209,7 +2112,6 @@ var TabBar = memo4(({
2209
2112
  renderAddButton,
2210
2113
  className = "",
2211
2114
  style,
2212
- tauriDragRegion = false,
2213
2115
  background,
2214
2116
  tabColor,
2215
2117
  tabHoverColor,
@@ -2218,135 +2120,131 @@ var TabBar = memo4(({
2218
2120
  tabActiveBackground,
2219
2121
  activeIndicatorColor
2220
2122
  }) => {
2221
- const colorVars = {
2123
+ const cssVarMap = {
2222
2124
  "--tab-bar-bg": background,
2223
2125
  "--tab-color": tabColor,
2224
2126
  "--tab-hover-color": tabHoverColor,
2225
2127
  "--tab-hover-bg": tabHoverBackground,
2226
2128
  "--tab-active-color": tabActiveColor,
2227
2129
  "--tab-active-bg": tabActiveBackground,
2228
- "--tab-active-indicator": activeIndicatorColor
2130
+ "--tab-active-indicator": activeIndicatorColor,
2131
+ "--tab-min-width": `${minTabWidth}rem`,
2132
+ "--tab-max-width": `${maxTabWidth}rem`
2229
2133
  };
2230
- const overrideStyle = {};
2231
- for (const [key, value] of Object.entries(colorVars)) {
2232
- if (value !== void 0) {
2233
- overrideStyle[key] = value;
2234
- }
2134
+ const cssVars = {};
2135
+ for (const [k, v] of Object.entries(cssVarMap)) {
2136
+ if (v !== void 0) cssVars[k] = v;
2235
2137
  }
2236
- const mergedStyle = { ...overrideStyle, ...style };
2237
- const dragKeyRef = useRef8(null);
2238
- const navRef = useRef8(null);
2239
- const layout = useAdaptiveTabLayout(navRef, {
2240
- itemsLength: items.length,
2241
- addable,
2242
- minWidthRatio,
2243
- maxTabWidthRatio
2244
- });
2245
- const prevItemsLengthRef = useRef8(items.length);
2246
- useEffect9(() => {
2247
- const prev = prevItemsLengthRef.current;
2248
- prevItemsLengthRef.current = items.length;
2249
- if (items.length <= prev || !layout.scrollMode) return;
2250
- const roll = navRef.current?.querySelector(".fc-roll");
2251
- if (!roll) return;
2138
+ const mergedStyle = { ...cssVars, ...style };
2139
+ const navOuterRef = useRef7(null);
2140
+ const addBtnRef = useRef7(null);
2141
+ const navWrapRef = useRef7(null);
2142
+ const dragKeyRef = useRef7(null);
2143
+ useEffect8(() => {
2144
+ const el = navWrapRef.current;
2145
+ if (!el) return;
2146
+ const handler = (e) => {
2147
+ if (e.deltaY === 0 || e.deltaX !== 0) return;
2148
+ el.scrollLeft += e.deltaY;
2149
+ e.preventDefault();
2150
+ };
2151
+ el.addEventListener("wheel", handler, { passive: false });
2152
+ return () => el.removeEventListener("wheel", handler);
2153
+ }, []);
2154
+ const prevLenRef = useRef7(items.length);
2155
+ useEffect8(() => {
2156
+ const prev = prevLenRef.current;
2157
+ prevLenRef.current = items.length;
2158
+ if (items.length <= prev) return;
2159
+ const el = navWrapRef.current;
2160
+ if (!el) return;
2252
2161
  requestAnimationFrame(() => {
2253
- roll.scrollLeft = roll.scrollWidth;
2162
+ el.scrollLeft = el.scrollWidth;
2254
2163
  });
2255
- }, [items.length, layout.scrollMode]);
2256
- const handleClick = useCallback9(
2257
- (key) => onChange(key),
2258
- [onChange]
2259
- );
2260
- const handleClose = useCallback9(
2261
- (e, key) => {
2262
- e.stopPropagation();
2263
- onClose?.(key);
2264
- },
2265
- [onClose]
2266
- );
2267
- const handleDragStart = useCallback9(
2268
- (e, key) => {
2269
- dragKeyRef.current = key;
2270
- e.dataTransfer.effectAllowed = "move";
2271
- const target = e.currentTarget;
2272
- requestAnimationFrame(() => target.classList.add("fc-tab-bar__tab--dragging"));
2273
- },
2274
- []
2275
- );
2276
- const handleDragOver = useCallback9((e) => {
2164
+ }, [items.length]);
2165
+ const handleClick = useCallback8((key) => onChange(key), [onChange]);
2166
+ const handleClose = useCallback8((e, key) => {
2167
+ e.stopPropagation();
2168
+ onClose?.(key);
2169
+ }, [onClose]);
2170
+ const handleDragStart = useCallback8((e, key) => {
2171
+ dragKeyRef.current = key;
2172
+ e.dataTransfer.effectAllowed = "move";
2173
+ const target = e.currentTarget;
2174
+ requestAnimationFrame(() => target.classList.add("fc-tab-bar__tab--dragging"));
2175
+ }, []);
2176
+ const handleDragOver = useCallback8((e) => {
2277
2177
  e.preventDefault();
2278
2178
  e.dataTransfer.dropEffect = "move";
2279
2179
  }, []);
2280
- const handleDrop = useCallback9(
2281
- (e, targetKey) => {
2282
- e.preventDefault();
2283
- const dragKey = dragKeyRef.current;
2284
- if (!dragKey || dragKey === targetKey || !onReorder) return;
2285
- const fromIndex = items.findIndex((i) => i.key === dragKey);
2286
- const toIndex = items.findIndex((i) => i.key === targetKey);
2287
- if (fromIndex === -1 || toIndex === -1) return;
2288
- const reordered = [...items];
2289
- const [moved] = reordered.splice(fromIndex, 1);
2290
- reordered.splice(toIndex, 0, moved);
2291
- onReorder(reordered);
2292
- },
2293
- [items, onReorder]
2294
- );
2295
- const handleDragEnd = useCallback9((e) => {
2180
+ const handleDrop = useCallback8((e, targetKey) => {
2181
+ e.preventDefault();
2182
+ const dragKey = dragKeyRef.current;
2183
+ if (!dragKey || dragKey === targetKey || !onReorder) return;
2184
+ const from = items.findIndex((i) => i.key === dragKey);
2185
+ const to = items.findIndex((i) => i.key === targetKey);
2186
+ if (from === -1 || to === -1) return;
2187
+ const reordered = [...items];
2188
+ const [moved] = reordered.splice(from, 1);
2189
+ reordered.splice(to, 0, moved);
2190
+ onReorder(reordered);
2191
+ }, [items, onReorder]);
2192
+ const handleDragEnd = useCallback8((e) => {
2296
2193
  dragKeyRef.current = null;
2297
2194
  e.currentTarget.classList.remove("fc-tab-bar__tab--dragging");
2298
2195
  }, []);
2299
- const { scrollMode, tabWidth } = layout;
2300
2196
  const rootClasses = [
2301
2197
  "fc-tab-bar",
2302
2198
  `fc-tab-bar--${variant}`,
2303
2199
  `fc-tab-bar--radius-${radius}`,
2304
2200
  tabRadius && `fc-tab-bar--tab-radius-${tabRadius}`,
2305
- !scrollMode && tabWidth !== void 0 && "fc-tab-bar--shrink",
2306
2201
  className
2307
2202
  ].filter(Boolean).join(" ");
2308
- const tabList = /* @__PURE__ */ jsx18("div", { className: "fc-tab-bar__nav-wrap", children: items.map((item) => /* @__PURE__ */ jsx18(
2309
- TabItemView,
2310
- {
2311
- item,
2312
- isActive: activeKey === item.key,
2313
- closable,
2314
- draggable,
2315
- tabWidth,
2316
- tabClassName,
2317
- activeTabClassName,
2318
- tabStyle,
2319
- activeTabStyle,
2320
- renderCloseIcon,
2321
- onClick: handleClick,
2322
- onClose: handleClose,
2323
- onDragStart: handleDragStart,
2324
- onDragOver: handleDragOver,
2325
- onDrop: handleDrop,
2326
- onDragEnd: handleDragEnd
2327
- },
2328
- item.key
2329
- )) });
2330
- const addBtn = addable && /* @__PURE__ */ jsx18(
2331
- "div",
2332
- {
2333
- className: "fc-tab-bar__add-btn",
2334
- onClick: onAdd,
2335
- role: "button",
2336
- "aria-label": "\u6DFB\u52A0\u6807\u7B7E",
2337
- children: renderAddButton ? renderAddButton() : "+"
2338
- }
2339
- );
2340
- const dragRegion = tauriDragRegion ? { "data-tauri-drag-region": "" } : {};
2341
- return /* @__PURE__ */ jsx18("div", { className: rootClasses, style: mergedStyle, role: "tablist", ...dragRegion, children: /* @__PURE__ */ jsxs13("div", { className: `fc-tab-bar__nav-outer${scrollMode ? " fc-tab-bar__nav-outer--scroll" : ""}`, ...dragRegion, children: [
2342
- /* @__PURE__ */ jsx18("div", { className: `fc-tab-bar__nav${scrollMode ? " fc-tab-bar__nav--scroll" : ""}`, ref: navRef, children: scrollMode ? /* @__PURE__ */ jsx18(RollingBox, { horizontal: true, showThumb: "hide", style: { flex: 1, minWidth: 0, height: "auto" }, children: tabList }) : tabList }),
2343
- addBtn
2203
+ const navWrapClasses = [
2204
+ "fc-tab-bar__nav-wrap",
2205
+ "fc-tab-bar__nav-wrap--scroll"
2206
+ // 始终启用滚动模式,由 CSS 控制
2207
+ ].filter(Boolean).join(" ");
2208
+ return /* @__PURE__ */ jsx18("div", { className: rootClasses, style: mergedStyle, role: "tablist", children: /* @__PURE__ */ jsxs13("div", { className: "fc-tab-bar__nav-outer", ref: navOuterRef, children: [
2209
+ /* @__PURE__ */ jsx18("div", { className: navWrapClasses, ref: navWrapRef, children: items.map((item) => /* @__PURE__ */ jsx18(
2210
+ TabItemView,
2211
+ {
2212
+ item,
2213
+ isActive: activeKey === item.key,
2214
+ closable,
2215
+ draggable,
2216
+ tabClassName,
2217
+ activeTabClassName,
2218
+ tabStyle,
2219
+ activeTabStyle,
2220
+ renderCloseIcon,
2221
+ onClick: handleClick,
2222
+ onClose: handleClose,
2223
+ onDragStart: handleDragStart,
2224
+ onDragOver: handleDragOver,
2225
+ onDrop: handleDrop,
2226
+ onDragEnd: handleDragEnd
2227
+ },
2228
+ item.key
2229
+ )) }),
2230
+ addable && /* @__PURE__ */ jsx18(
2231
+ "div",
2232
+ {
2233
+ className: "fc-tab-bar__add-btn",
2234
+ ref: addBtnRef,
2235
+ onClick: onAdd,
2236
+ role: "button",
2237
+ "aria-label": "\u6DFB\u52A0\u6807\u7B7E",
2238
+ children: renderAddButton ? renderAddButton() : "+"
2239
+ }
2240
+ ),
2241
+ /* @__PURE__ */ jsx18("div", { className: "fc-tab-bar__drag-handle", "data-tauri-drag-region": true })
2344
2242
  ] }) });
2345
2243
  });
2346
2244
  TabBar.displayName = "TabBar";
2347
2245
 
2348
2246
  // src/components/Tag/TagItem.tsx
2349
- import { useEffect as useEffect10, useRef as useRef9, useState as useState14 } from "react";
2247
+ import { useEffect as useEffect9, useRef as useRef8, useState as useState13 } from "react";
2350
2248
  import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
2351
2249
  function TagItem({
2352
2250
  schema,
@@ -2357,10 +2255,10 @@ function TagItem({
2357
2255
  color,
2358
2256
  borderColor
2359
2257
  }) {
2360
- const [editing, setEditing] = useState14(false);
2361
- const [draft, setDraft] = useState14(() => value !== void 0 ? String(value) : "");
2362
- const inputRef = useRef9(null);
2363
- useEffect10(() => {
2258
+ const [editing, setEditing] = useState13(false);
2259
+ const [draft, setDraft] = useState13(() => value !== void 0 ? String(value) : "");
2260
+ const inputRef = useRef8(null);
2261
+ useEffect9(() => {
2364
2262
  if (!editing) setDraft(value !== void 0 ? String(value) : "");
2365
2263
  }, [value, editing]);
2366
2264
  const colorVars = {
@@ -2459,7 +2357,7 @@ function TagItem({
2459
2357
  }
2460
2358
 
2461
2359
  // src/components/MarkdownEditor/MarkdownEditor.tsx
2462
- import { useState as useState15 } from "react";
2360
+ import { useState as useState14 } from "react";
2463
2361
  import MDEditor, { commands } from "@uiw/react-md-editor";
2464
2362
  import { jsx as jsx20 } from "react/jsx-runtime";
2465
2363
  function withTitle(cmd, title) {
@@ -2495,7 +2393,7 @@ function MarkdownEditor({
2495
2393
  borderColor
2496
2394
  }) {
2497
2395
  const { resolvedTheme } = useTheme();
2498
- const [showSplit, setShowSplit] = useState15(false);
2396
+ const [showSplit, setShowSplit] = useState14(false);
2499
2397
  const colorVars = {
2500
2398
  "--md-bg": background,
2501
2399
  "--md-toolbar-bg": toolbarBackground,
@@ -2554,7 +2452,7 @@ function MarkdownEditor({
2554
2452
  }
2555
2453
 
2556
2454
  // src/components/ContextMenu/ContextMenuContext.tsx
2557
- import { createContext as createContext4, useContext as useContext4, useEffect as useEffect11, useRef as useRef10, useState as useState16 } from "react";
2455
+ import { createContext as createContext4, useContext as useContext4, useEffect as useEffect10, useRef as useRef9, useState as useState15 } from "react";
2558
2456
  import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
2559
2457
  var ContextMenuContext = createContext4(null);
2560
2458
  function ContextMenuProvider({
@@ -2563,13 +2461,13 @@ function ContextMenuProvider({
2563
2461
  borderColor,
2564
2462
  hoverBackground
2565
2463
  }) {
2566
- const [menu, setMenu] = useState16({
2464
+ const [menu, setMenu] = useState15({
2567
2465
  visible: false,
2568
2466
  x: 0,
2569
2467
  y: 0,
2570
2468
  items: []
2571
2469
  });
2572
- const menuRef = useRef10(null);
2470
+ const menuRef = useRef9(null);
2573
2471
  const showContextMenu = (e, items) => {
2574
2472
  e.preventDefault();
2575
2473
  e.stopPropagation();
@@ -2577,7 +2475,7 @@ function ContextMenuProvider({
2577
2475
  };
2578
2476
  const hide = () => setMenu((s) => ({ ...s, visible: false }));
2579
2477
  useClickOutside(menuRef, hide, menu.visible);
2580
- useEffect11(() => {
2478
+ useEffect10(() => {
2581
2479
  if (!menu.visible) return;
2582
2480
  const onKeyDown = (e) => {
2583
2481
  if (e.key === "Escape") hide();
@@ -2646,10 +2544,10 @@ function ContextMenuProvider({
2646
2544
  var useContextMenu = () => useContext4(ContextMenuContext);
2647
2545
 
2648
2546
  // src/components/Chat/Chat.tsx
2649
- import { useState as useState18, useRef as useRef11, useEffect as useEffect12, useCallback as useCallback11, useMemo as useMemo5 } from "react";
2547
+ import { useState as useState17, useRef as useRef10, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo5 } from "react";
2650
2548
 
2651
2549
  // src/components/SmartMessage/SmartMessage.tsx
2652
- import { useState as useState17, useCallback as useCallback10 } from "react";
2550
+ import { useState as useState16, useCallback as useCallback9 } from "react";
2653
2551
  import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
2654
2552
  var SmartMessage = ({
2655
2553
  id,
@@ -2662,8 +2560,8 @@ var SmartMessage = ({
2662
2560
  className = "",
2663
2561
  style = {}
2664
2562
  }) => {
2665
- const [copied, setCopied] = useState17(false);
2666
- const handleCopy = useCallback10(async () => {
2563
+ const [copied, setCopied] = useState16(false);
2564
+ const handleCopy = useCallback9(async () => {
2667
2565
  try {
2668
2566
  await navigator.clipboard.writeText(content);
2669
2567
  setCopied(true);
@@ -2751,34 +2649,34 @@ var Chat = ({
2751
2649
  height = "600px",
2752
2650
  width
2753
2651
  }) => {
2754
- const [showHistory, setShowHistory] = useState18(false);
2755
- const [isMinimized, setIsMinimized] = useState18(false);
2756
- const messagesContainerRef = useRef11(null);
2757
- const messagesEndRef = useRef11(null);
2758
- const historyPanelRef = useRef11(null);
2652
+ const [showHistory, setShowHistory] = useState17(false);
2653
+ const [isMinimized, setIsMinimized] = useState17(false);
2654
+ const messagesContainerRef = useRef10(null);
2655
+ const messagesEndRef = useRef10(null);
2656
+ const historyPanelRef = useRef10(null);
2759
2657
  const currentConversation = useMemo5(
2760
2658
  () => conversations.find((c) => c.id === currentConversationId),
2761
2659
  [conversations, currentConversationId]
2762
2660
  );
2763
2661
  const currentTitle = currentConversation?.title || title;
2764
- useEffect12(() => {
2662
+ useEffect11(() => {
2765
2663
  if (autoScroll && messagesContainerRef.current && !showHistory && !isMinimized) {
2766
2664
  messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
2767
2665
  }
2768
2666
  }, [messages, loading, showHistory, isMinimized, autoScroll]);
2769
2667
  useClickOutside(historyPanelRef, () => setShowHistory(false), showHistory);
2770
- const handleDeleteConversation = useCallback11((conversationId) => {
2668
+ const handleDeleteConversation = useCallback10((conversationId) => {
2771
2669
  onDeleteConversation?.(conversationId);
2772
2670
  }, [onDeleteConversation]);
2773
- const handleMinimize = useCallback11(() => {
2671
+ const handleMinimize = useCallback10(() => {
2774
2672
  setIsMinimized(true);
2775
2673
  onMinimize?.();
2776
2674
  }, [onMinimize]);
2777
- const handleRestore = useCallback11(() => {
2675
+ const handleRestore = useCallback10(() => {
2778
2676
  setIsMinimized(false);
2779
2677
  onRestore?.();
2780
2678
  }, [onRestore]);
2781
- const handleCopy = useCallback11((content) => {
2679
+ const handleCopy = useCallback10((content) => {
2782
2680
  const message = messages.find((m) => m.content === content);
2783
2681
  if (message) {
2784
2682
  onMessageCopy?.(message);
@@ -2956,7 +2854,7 @@ var Chat = ({
2956
2854
  };
2957
2855
 
2958
2856
  // src/components/Relation/Relation.tsx
2959
- import { useCallback as useCallback12, useEffect as useEffect13 } from "react";
2857
+ import React13, { useCallback as useCallback11, useEffect as useEffect12, memo as memo5 } from "react";
2960
2858
  import {
2961
2859
  ReactFlow,
2962
2860
  useNodesState,
@@ -2970,7 +2868,7 @@ import {
2970
2868
  ReactFlowProvider
2971
2869
  } from "@xyflow/react";
2972
2870
  import "@xyflow/react/dist/style.css";
2973
- import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2871
+ import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2974
2872
  var getIconEmoji = (iconType) => {
2975
2873
  const map = {
2976
2874
  war: "\u2694\uFE0F",
@@ -2992,16 +2890,77 @@ var getStatusColor = (status) => {
2992
2890
  };
2993
2891
  return map[status || "active"];
2994
2892
  };
2995
- var CustomNode = ({ data, theme = "light" }) => {
2996
- const isDark = theme === "dark";
2893
+ var ConnectionHandles = ({ isDark, showHandles }) => {
2894
+ const handleStyle = {
2895
+ background: isDark ? "#ff8e8e" : "#ff6b6b",
2896
+ width: "8px",
2897
+ height: "8px",
2898
+ borderRadius: "50%",
2899
+ border: `1px solid ${isDark ? "#1e293b" : "#ffffff"}`,
2900
+ transition: "all 0.2s ease"
2901
+ };
2902
+ const hiddenHandleStyle = {
2903
+ opacity: 0,
2904
+ width: "0px",
2905
+ height: "0px",
2906
+ pointerEvents: "none"
2907
+ };
2908
+ const actualStyle = showHandles ? handleStyle : hiddenHandleStyle;
2909
+ return /* @__PURE__ */ jsxs18(Fragment5, { children: [
2910
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "top-left", style: { ...actualStyle, left: "25%" } }),
2911
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "top-left-source", style: { ...actualStyle, left: "25%" } }),
2912
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "top-center", style: { ...actualStyle, left: "50%" } }),
2913
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "top-center-source", style: { ...actualStyle, left: "50%" } }),
2914
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "top-right", style: { ...actualStyle, left: "75%" } }),
2915
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "top-right-source", style: { ...actualStyle, left: "75%" } }),
2916
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "right-top", style: { ...actualStyle, top: "25%" } }),
2917
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "right-top-source", style: { ...actualStyle, top: "25%" } }),
2918
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "right-center", style: { ...actualStyle, top: "50%" } }),
2919
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "right-center-source", style: { ...actualStyle, top: "50%" } }),
2920
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "right-bottom", style: { ...actualStyle, top: "75%" } }),
2921
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "right-bottom-source", style: { ...actualStyle, top: "75%" } }),
2922
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "bottom-left", style: { ...actualStyle, left: "25%" } }),
2923
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "bottom-left-source", style: { ...actualStyle, left: "25%" } }),
2924
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "bottom-center", style: { ...actualStyle, left: "50%" } }),
2925
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "bottom-center-source", style: { ...actualStyle, left: "50%" } }),
2926
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "bottom-right", style: { ...actualStyle, left: "75%" } }),
2927
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "bottom-right-source", style: { ...actualStyle, left: "75%" } }),
2928
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "left-top", style: { ...actualStyle, top: "25%" } }),
2929
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "left-top-source", style: { ...actualStyle, top: "25%" } }),
2930
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "left-center", style: { ...actualStyle, top: "50%" } }),
2931
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "left-center-source", style: { ...actualStyle, top: "50%" } }),
2932
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "left-bottom", style: { ...actualStyle, top: "75%" } }),
2933
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "left-bottom-source", style: { ...actualStyle, top: "75%" } }),
2934
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "corner-top-left", style: { ...actualStyle, left: "10%" } }),
2935
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "corner-top-left-source", style: { ...actualStyle, left: "10%" } }),
2936
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, id: "corner-top-right", style: { ...actualStyle, left: "90%" } }),
2937
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Top, id: "corner-top-right-source", style: { ...actualStyle, left: "90%" } }),
2938
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "corner-bottom-left", style: { ...actualStyle, left: "10%" } }),
2939
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "corner-bottom-left-source", style: { ...actualStyle, left: "10%" } }),
2940
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Bottom, id: "corner-bottom-right", style: { ...actualStyle, left: "90%" } }),
2941
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, id: "corner-bottom-right-source", style: { ...actualStyle, left: "90%" } }),
2942
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "corner-left-top", style: { ...actualStyle, top: "10%" } }),
2943
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "corner-left-top-source", style: { ...actualStyle, top: "10%" } }),
2944
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Left, id: "corner-left-bottom", style: { ...actualStyle, top: "90%" } }),
2945
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Left, id: "corner-left-bottom-source", style: { ...actualStyle, top: "90%" } }),
2946
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "corner-right-top", style: { ...actualStyle, top: "10%" } }),
2947
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "corner-right-top-source", style: { ...actualStyle, top: "10%" } }),
2948
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Right, id: "corner-right-bottom", style: { ...actualStyle, top: "90%" } }),
2949
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Right, id: "corner-right-bottom-source", style: { ...actualStyle, top: "90%" } })
2950
+ ] });
2951
+ };
2952
+ var CustomNode = memo5(({ data, theme: propTheme }) => {
2953
+ const isDark = (data.theme || propTheme) === "dark";
2954
+ const showHandles = data.showHandles !== void 0 ? data.showHandles : false;
2997
2955
  const nodeStyle = {
2998
2956
  background: isDark ? "linear-gradient(135deg, #1e293b 0%, #0f172a 100%)" : "linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)",
2999
- border: `2px solid ${isDark ? "rgba(255, 255, 255, 0.12)" : "rgba(0, 0, 0, 0.08)"}`,
2957
+ border: `2px solid ${isDark ? "rgba(255, 255, 255, 0.15)" : "rgba(0, 0, 0, 0.08)"}`,
3000
2958
  borderRadius: "14px",
3001
- boxShadow: isDark ? "0 8px 20px rgba(0, 0, 0, 0.25)" : "0 4px 12px rgba(0, 0, 0, 0.08)",
2959
+ 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)",
3002
2960
  minWidth: "240px",
3003
2961
  cursor: "pointer",
3004
- transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
2962
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
2963
+ position: "relative"
3005
2964
  };
3006
2965
  const iconStyle = {
3007
2966
  width: "52px",
@@ -3010,8 +2969,9 @@ var CustomNode = ({ data, theme = "light" }) => {
3010
2969
  display: "flex",
3011
2970
  alignItems: "center",
3012
2971
  justifyContent: "center",
3013
- background: isDark ? "rgba(255, 255, 255, 0.08)" : "rgba(0, 0, 0, 0.04)",
3014
- transition: "all 0.3s ease"
2972
+ 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%)",
2973
+ transition: "all 0.3s ease",
2974
+ boxShadow: isDark ? "inset 0 1px 1px rgba(255, 255, 255, 0.1)" : "inset 0 1px 1px rgba(0, 0, 0, 0.02)"
3015
2975
  };
3016
2976
  const titleStyle = {
3017
2977
  fontWeight: 700,
@@ -3020,7 +2980,8 @@ var CustomNode = ({ data, theme = "light" }) => {
3020
2980
  whiteSpace: "nowrap",
3021
2981
  overflow: "hidden",
3022
2982
  textOverflow: "ellipsis",
3023
- color: isDark ? "#ffffff" : "#1e293b"
2983
+ color: isDark ? "#ffffff" : "#1e293b",
2984
+ letterSpacing: isDark ? "0.3px" : "normal"
3024
2985
  };
3025
2986
  const subtitleStyle = {
3026
2987
  fontSize: "11px",
@@ -3036,11 +2997,18 @@ var CustomNode = ({ data, theme = "light" }) => {
3036
2997
  textOverflow: "ellipsis",
3037
2998
  color: isDark ? "rgba(255, 255, 255, 0.45)" : "#94a3b8"
3038
2999
  };
3039
- return /* @__PURE__ */ jsxs18("div", { style: nodeStyle, children: [
3040
- /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, isConnectable: true }),
3000
+ return /* @__PURE__ */ jsxs18("div", { style: nodeStyle, className: "relation-node", children: [
3001
+ /* @__PURE__ */ jsx24(ConnectionHandles, { isDark, showHandles }),
3041
3002
  /* @__PURE__ */ jsxs18("div", { style: { display: "flex", alignItems: "center", gap: "14px", padding: "14px 18px" }, children: [
3042
3003
  /* @__PURE__ */ jsxs18("div", { style: { position: "relative", flexShrink: 0 }, children: [
3043
- /* @__PURE__ */ jsx24("div", { style: iconStyle, children: data.imageUrl ? /* @__PURE__ */ jsx24("img", { src: data.imageUrl, alt: data.title, style: { width: "100%", height: "100%", objectFit: "cover", borderRadius: "12px" } }) : /* @__PURE__ */ jsx24("span", { style: { fontSize: "28px" }, children: getIconEmoji(data.iconType) }) }),
3004
+ /* @__PURE__ */ jsx24("div", { style: iconStyle, children: data.imageUrl ? /* @__PURE__ */ jsx24(
3005
+ "img",
3006
+ {
3007
+ src: data.imageUrl,
3008
+ alt: data.title,
3009
+ style: { width: "100%", height: "100%", objectFit: "cover", borderRadius: "12px" }
3010
+ }
3011
+ ) : /* @__PURE__ */ jsx24("span", { style: { fontSize: "28px", filter: isDark ? "drop-shadow(0 1px 2px rgba(0,0,0,0.2))" : "none" }, children: getIconEmoji(data.iconType) }) }),
3044
3012
  data.status && /* @__PURE__ */ jsx24("div", { style: {
3045
3013
  position: "absolute",
3046
3014
  bottom: "-2px",
@@ -3049,7 +3017,9 @@ var CustomNode = ({ data, theme = "light" }) => {
3049
3017
  height: "12px",
3050
3018
  borderRadius: "50%",
3051
3019
  border: `2px solid ${isDark ? "#1e293b" : "#ffffff"}`,
3052
- backgroundColor: getStatusColor(data.status)
3020
+ backgroundColor: getStatusColor(data.status),
3021
+ animation: data.status === "warning" ? "pulse 2s infinite" : "none",
3022
+ boxShadow: isDark ? "0 0 0 1px rgba(0,0,0,0.2)" : "none"
3053
3023
  } })
3054
3024
  ] }),
3055
3025
  /* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: 0 }, children: [
@@ -3057,13 +3027,21 @@ var CustomNode = ({ data, theme = "light" }) => {
3057
3027
  /* @__PURE__ */ jsx24("div", { style: subtitleStyle, children: data.subtitle }),
3058
3028
  data.description && /* @__PURE__ */ jsx24("div", { style: descriptionStyle, children: data.description })
3059
3029
  ] })
3060
- ] }),
3061
- /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, isConnectable: true })
3030
+ ] })
3062
3031
  ] });
3063
- };
3032
+ });
3033
+ CustomNode.displayName = "CustomNode";
3064
3034
  var nodeTypes = {
3065
3035
  custom: CustomNode
3066
3036
  };
3037
+ var getEdgeStyle = (theme, isHovered, isSelected) => {
3038
+ const baseColor = theme === "dark" ? "#7c8ba0" : "#a0aec0";
3039
+ const hoverColor = "#ff8e8e";
3040
+ const selectedColor = "#ff6b6b";
3041
+ if (isSelected) return { stroke: selectedColor, strokeWidth: 2.5 };
3042
+ if (isHovered) return { stroke: hoverColor, strokeWidth: 2.5 };
3043
+ return { stroke: baseColor, strokeWidth: 1.8 };
3044
+ };
3067
3045
  var RelationContent = ({
3068
3046
  nodes: propNodes,
3069
3047
  edges: propEdges,
@@ -3087,59 +3065,84 @@ var RelationContent = ({
3087
3065
  enableEdgeCreation = true,
3088
3066
  enableNodeDrag = true,
3089
3067
  onNodeContextMenu,
3090
- theme: propTheme = "light"
3068
+ theme: propTheme = "light",
3069
+ edgeStyles = {},
3070
+ nodeStyles = {},
3071
+ showHandles = false
3091
3072
  }) => {
3092
- const [nodes, , onNodesChange] = useNodesState(propNodes || []);
3073
+ const [nodes, setNodes, onNodesChange] = useNodesState(propNodes || []);
3093
3074
  const [edges, setEdges, onEdgesChange] = useEdgesState(propEdges || []);
3094
3075
  const { fitView: fitViewFn } = useReactFlow();
3095
3076
  const theme = propTheme;
3096
3077
  const isDark = theme === "dark";
3097
3078
  const bgColor = isDark ? "#0f172a" : "#f5f7fa";
3098
- useEffect13(() => {
3079
+ useEffect12(() => {
3099
3080
  if (fitView && fitViewFn && (propNodes?.length || 0) > 0) {
3100
- setTimeout(() => {
3101
- fitViewFn({ duration: 300, ...fitViewOptions }).catch((error) => {
3081
+ const timer = setTimeout(() => {
3082
+ fitViewFn({ duration: 300, padding: 0.2, ...fitViewOptions }).catch((error) => {
3102
3083
  console.warn("Fit view failed:", error);
3103
3084
  });
3104
3085
  }, 100);
3086
+ return () => clearTimeout(timer);
3105
3087
  }
3106
3088
  }, [fitView, fitViewFn, fitViewOptions, propNodes]);
3107
- const handleNodesChange = useCallback12(
3089
+ useEffect12(() => {
3090
+ if (propNodes) {
3091
+ setNodes(propNodes);
3092
+ }
3093
+ }, [propNodes, setNodes]);
3094
+ useEffect12(() => {
3095
+ if (propEdges) {
3096
+ setEdges(propEdges);
3097
+ }
3098
+ }, [propEdges, setEdges]);
3099
+ const handleNodesChange = useCallback11(
3108
3100
  (changes) => {
3109
3101
  onNodesChange(changes);
3110
3102
  if (onNodesChangeProp) {
3111
- onNodesChangeProp(nodes);
3103
+ setTimeout(() => {
3104
+ onNodesChangeProp(nodes);
3105
+ }, 0);
3112
3106
  }
3113
3107
  },
3114
3108
  [onNodesChange, onNodesChangeProp, nodes]
3115
3109
  );
3116
- const handleEdgesChange = useCallback12(
3110
+ const handleEdgesChange = useCallback11(
3117
3111
  (changes) => {
3118
3112
  onEdgesChange(changes);
3119
3113
  if (onEdgesChangeProp) {
3120
- onEdgesChangeProp(edges);
3114
+ setTimeout(() => {
3115
+ onEdgesChangeProp(edges);
3116
+ }, 0);
3121
3117
  }
3122
3118
  },
3123
3119
  [onEdgesChange, onEdgesChangeProp, edges]
3124
3120
  );
3125
- const onConnect = useCallback12(
3121
+ const onConnect = useCallback11(
3126
3122
  (params) => {
3127
3123
  const newEdge = {
3128
3124
  ...params,
3129
- id: `edge-${Date.now()}-${Math.random()}`,
3130
- type: "smoothstep",
3131
- style: { stroke: "#ff6b6b", strokeWidth: 2 },
3132
- markerEnd: { type: MarkerType.ArrowClosed, color: "#ff6b6b" },
3133
- label: "\u65B0\u8FDE\u63A5"
3125
+ id: `edge-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
3126
+ type: "straight",
3127
+ style: { stroke: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"), strokeWidth: 1.8 },
3128
+ markerEnd: {
3129
+ type: MarkerType.ArrowClosed,
3130
+ color: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"),
3131
+ width: 12,
3132
+ height: 12
3133
+ },
3134
+ label: "",
3135
+ labelStyle: { fill: isDark ? "#fff" : "#333", fontSize: 11, fontWeight: 400 },
3136
+ labelBgStyle: { fill: "transparent", fillOpacity: 0 }
3134
3137
  };
3135
3138
  setEdges((eds) => addEdge(newEdge, eds));
3136
3139
  if (onConnectProp) {
3137
3140
  onConnectProp(params);
3138
3141
  }
3139
3142
  },
3140
- [setEdges, onConnectProp]
3143
+ [setEdges, onConnectProp, edgeStyles.defaultColor, isDark]
3141
3144
  );
3142
- const handleNodeClick = useCallback12(
3145
+ const handleNodeClick = useCallback11(
3143
3146
  (_event, node) => {
3144
3147
  if (onNodeClick && node.data) {
3145
3148
  onNodeClick(node.id, node.data, _event);
@@ -3147,7 +3150,7 @@ var RelationContent = ({
3147
3150
  },
3148
3151
  [onNodeClick]
3149
3152
  );
3150
- const handleNodeDoubleClick = useCallback12(
3153
+ const handleNodeDoubleClick = useCallback11(
3151
3154
  (_event, node) => {
3152
3155
  if (onNodeDoubleClick && node.data) {
3153
3156
  onNodeDoubleClick(node.id, node.data);
@@ -3155,7 +3158,7 @@ var RelationContent = ({
3155
3158
  },
3156
3159
  [onNodeDoubleClick]
3157
3160
  );
3158
- const handleNodeContextMenu = useCallback12(
3161
+ const handleNodeContextMenu = useCallback11(
3159
3162
  (event, node) => {
3160
3163
  event.preventDefault();
3161
3164
  if (onNodeContextMenu && node.data) {
@@ -3164,7 +3167,7 @@ var RelationContent = ({
3164
3167
  },
3165
3168
  [onNodeContextMenu]
3166
3169
  );
3167
- const handleEdgeClick = useCallback12(
3170
+ const handleEdgeClick = useCallback11(
3168
3171
  (_event, edge) => {
3169
3172
  if (onEdgeClick) {
3170
3173
  onEdgeClick(edge.id, edge.data);
@@ -3172,18 +3175,107 @@ var RelationContent = ({
3172
3175
  },
3173
3176
  [onEdgeClick]
3174
3177
  );
3175
- const nodesWithTheme = nodes.map((node) => ({
3176
- ...node,
3177
- data: { ...node.data, theme }
3178
- }));
3178
+ const nodesWithTheme = React13.useMemo(() => {
3179
+ return nodes.map((node) => ({
3180
+ ...node,
3181
+ data: {
3182
+ ...node.data,
3183
+ theme,
3184
+ showHandles
3185
+ },
3186
+ className: `relation-node ${node.className || ""}`
3187
+ }));
3188
+ }, [nodes, theme, showHandles]);
3189
+ const edgesWithStyle = React13.useMemo(() => {
3190
+ return edges.map((edge) => ({
3191
+ ...edge,
3192
+ type: edge.type || "straight",
3193
+ style: {
3194
+ ...edge.style,
3195
+ ...getEdgeStyle(theme)
3196
+ },
3197
+ labelStyle: {
3198
+ fill: isDark ? "#fff" : "#333",
3199
+ fontSize: 11,
3200
+ fontWeight: 400,
3201
+ ...edge.labelStyle
3202
+ },
3203
+ labelBgStyle: {
3204
+ fill: "transparent",
3205
+ fillOpacity: 0,
3206
+ ...edge.labelBgStyle
3207
+ },
3208
+ markerEnd: {
3209
+ type: MarkerType.ArrowClosed,
3210
+ color: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"),
3211
+ width: 12,
3212
+ height: 12,
3213
+ ...edge.markerEnd
3214
+ }
3215
+ }));
3216
+ }, [edges, theme, isDark, edgeStyles.defaultColor]);
3179
3217
  const globalStyle = `
3180
- .react-flow__background,
3181
- .react-flow__pane,
3182
- .react-flow__renderer,
3183
- .react-flow__viewport {
3184
- background-color: ${bgColor} !important;
3185
- }
3186
- `;
3218
+ .relation-container {
3219
+ position: relative;
3220
+ width: 100%;
3221
+ height: 100%;
3222
+ overflow: hidden;
3223
+ }
3224
+ .react-flow__background,
3225
+ .react-flow__pane,
3226
+ .react-flow__renderer,
3227
+ .react-flow__viewport {
3228
+ background-color: ${bgColor} !important;
3229
+ }
3230
+ .relation-node {
3231
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease;
3232
+ }
3233
+ .relation-node:hover {
3234
+ transform: translateY(-4px) scale(1.02);
3235
+ 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)"};
3236
+ }
3237
+ .react-flow__edge-path {
3238
+ transition: stroke-width 0.2s ease, stroke 0.2s ease;
3239
+ }
3240
+ .react-flow__edge:hover .react-flow__edge-path {
3241
+ stroke-width: 2.5px;
3242
+ stroke: ${edgeStyles.hoverColor || (isDark ? "#ffa5a5" : "#ff8e8e")};
3243
+ }
3244
+ /* \u8FDE\u63A5\u70B9\u60AC\u505C\u6548\u679C */
3245
+ .react-flow__handle {
3246
+ transition: all 0.2s ease;
3247
+ }
3248
+ .react-flow__handle:hover {
3249
+ transform: scale(1.3);
3250
+ background-color: ${isDark ? "#ffa5a5" : "#ff8e8e"} !important;
3251
+ }
3252
+ @keyframes pulse {
3253
+ 0%, 100% {
3254
+ opacity: 1;
3255
+ transform: scale(1);
3256
+ }
3257
+ 50% {
3258
+ opacity: 0.7;
3259
+ transform: scale(1.2);
3260
+ }
3261
+ }
3262
+ /* \u7B80\u7EA6\u6EDA\u52A8\u6761 */
3263
+ .relation-container ::-webkit-scrollbar {
3264
+ width: 6px;
3265
+ height: 6px;
3266
+ }
3267
+ .relation-container ::-webkit-scrollbar-track {
3268
+ background: ${isDark ? "#1e293b" : "#e2e8f0"};
3269
+ border-radius: 3px;
3270
+ }
3271
+ .relation-container ::-webkit-scrollbar-thumb {
3272
+ background: ${isDark ? "#475569" : "#cbd5e1"};
3273
+ border-radius: 3px;
3274
+ }
3275
+ .relation-container ::-webkit-scrollbar-thumb:hover {
3276
+ background: ${isDark ? "#ff8e8e" : "#ff6b6b"};
3277
+ }
3278
+ `;
3187
3279
  return /* @__PURE__ */ jsxs18(
3188
3280
  "div",
3189
3281
  {
@@ -3192,9 +3284,8 @@ var RelationContent = ({
3192
3284
  width,
3193
3285
  height,
3194
3286
  ...style,
3195
- position: "relative",
3196
- overflow: "hidden",
3197
- backgroundColor: bgColor
3287
+ backgroundColor: bgColor,
3288
+ borderRadius: nodeStyles.borderRadius || "12px"
3198
3289
  },
3199
3290
  children: [
3200
3291
  /* @__PURE__ */ jsx24("style", { children: globalStyle }),
@@ -3202,7 +3293,7 @@ var RelationContent = ({
3202
3293
  ReactFlow,
3203
3294
  {
3204
3295
  nodes: nodesWithTheme,
3205
- edges,
3296
+ edges: edgesWithStyle,
3206
3297
  onNodesChange: handleNodesChange,
3207
3298
  onEdgesChange: handleEdgesChange,
3208
3299
  onConnect: enableEdgeCreation ? onConnect : void 0,
@@ -3220,15 +3311,24 @@ var RelationContent = ({
3220
3311
  snapGrid,
3221
3312
  nodesDraggable: enableNodeDrag,
3222
3313
  nodesConnectable: enableEdgeCreation,
3223
- connectionLineType: ConnectionLineType.SmoothStep,
3224
- connectionLineStyle: { stroke: "#ff6b6b", strokeWidth: 2 },
3314
+ connectionLineType: ConnectionLineType.Straight,
3315
+ connectionLineStyle: {
3316
+ stroke: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"),
3317
+ strokeWidth: 1.8
3318
+ },
3225
3319
  attributionPosition: "bottom-right",
3226
3320
  zoomOnScroll: true,
3227
3321
  zoomOnPinch: true,
3228
3322
  zoomOnDoubleClick: false,
3229
3323
  panOnScroll: false,
3230
3324
  panOnDrag: true,
3231
- proOptions: { hideAttribution: true }
3325
+ proOptions: { hideAttribution: true },
3326
+ elevateEdgesOnSelect: true,
3327
+ defaultEdgeOptions: {
3328
+ type: "straight",
3329
+ style: { stroke: edgeStyles.defaultColor || (isDark ? "#7c8ba0" : "#a0aec0"), strokeWidth: 1.8 },
3330
+ markerEnd: { type: MarkerType.ArrowClosed, width: 12, height: 12 }
3331
+ }
3232
3332
  }
3233
3333
  )
3234
3334
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowcloudai-ui",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "author": "flowcloudai",
6
6
  "module": "dist/index.js",