flowcloudai-ui 0.1.1 → 0.1.2

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.js CHANGED
@@ -16,16 +16,7 @@ function ThemeProvider({ children, defaultTheme = "system", target }) {
16
16
  const resolvedTheme = theme === "system" ? systemTheme : theme;
17
17
  useEffect(() => {
18
18
  const el = target ?? document.documentElement;
19
- el.classList.remove("theme-light", "theme-dark");
20
- el.classList.add(`theme-${resolvedTheme}`);
21
19
  el.setAttribute("data-theme", resolvedTheme);
22
- if (resolvedTheme === "dark") {
23
- document.body.style.backgroundColor = "#0F0F0F";
24
- document.body.style.color = "#E8E8E6";
25
- } else {
26
- document.body.style.backgroundColor = "";
27
- document.body.style.color = "";
28
- }
29
20
  }, [resolvedTheme, target]);
30
21
  useEffect(() => {
31
22
  if (theme !== "system") return;
@@ -42,6 +33,7 @@ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
42
33
  function Button({
43
34
  variant = "primary",
44
35
  size = "md",
36
+ radius,
45
37
  disabled = false,
46
38
  loading = false,
47
39
  block = false,
@@ -86,6 +78,7 @@ function Button({
86
78
  "fc-btn",
87
79
  `fc-btn--${variant}`,
88
80
  `fc-btn--${size}`,
81
+ radius && `fc-btn--radius-${radius}`,
89
82
  block && "fc-btn--block",
90
83
  circle && "fc-btn--circle",
91
84
  iconOnly && "fc-btn--icon-only",
@@ -135,6 +128,7 @@ function CheckButton({
135
128
  onChange,
136
129
  disabled = false,
137
130
  size = "md",
131
+ radius,
138
132
  labelLeft,
139
133
  labelRight,
140
134
  trackBackground,
@@ -180,6 +174,7 @@ function CheckButton({
180
174
  const cls = [
181
175
  "fc-check",
182
176
  `fc-check--${size}`,
177
+ radius && `fc-check--radius-${radius}`,
183
178
  checked && "fc-check--checked",
184
179
  disabled && "fc-check--disabled",
185
180
  className
@@ -254,6 +249,17 @@ function RollingBox({
254
249
  }
255
250
  };
256
251
  }, []);
252
+ React2.useEffect(() => {
253
+ const el = containerRef.current;
254
+ if (!el || !horizontal) return;
255
+ const handler = (e) => {
256
+ if (e.deltaY === 0) return;
257
+ e.preventDefault();
258
+ el.scrollLeft += e.deltaY;
259
+ };
260
+ el.addEventListener("wheel", handler, { passive: false });
261
+ return () => el.removeEventListener("wheel", handler);
262
+ }, [horizontal]);
257
263
  const resolvedDirection = horizontal ? "horizontal" : "vertical";
258
264
  const classNames = [
259
265
  "fc-roll",
@@ -304,6 +310,7 @@ var SideBarItemView = memo(({ item, isSelected, onClick }) => {
304
310
  SideBarItemView.displayName = "SideBarItemView";
305
311
  var SideBar = memo(({
306
312
  items,
313
+ bottomItems,
307
314
  selectedKey,
308
315
  collapsed,
309
316
  width = 240,
@@ -368,6 +375,15 @@ var SideBar = memo(({
368
375
  onClick: handleClick
369
376
  },
370
377
  item.key
378
+ )) }),
379
+ bottomItems && bottomItems.length > 0 && /* @__PURE__ */ jsx5("div", { className: "fc-sidebar__footer", children: bottomItems.map((item) => /* @__PURE__ */ jsx5(
380
+ SideBarItemView,
381
+ {
382
+ item,
383
+ isSelected: selectedKey === item.key,
384
+ onClick: handleClick
385
+ },
386
+ item.key
371
387
  )) })
372
388
  ] });
373
389
  });
@@ -379,6 +395,7 @@ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
379
395
  var Input = React4.forwardRef(({
380
396
  size = "md",
381
397
  status = "default",
398
+ radius,
382
399
  prefix,
383
400
  suffix,
384
401
  allowClear = false,
@@ -401,13 +418,12 @@ var Input = React4.forwardRef(({
401
418
  const currentValue = isControlled ? value : internalValue;
402
419
  const handleChange = (e) => {
403
420
  if (!isControlled) setInternalValue(e.target.value);
404
- onChange?.(e);
421
+ onChange?.(e.target.value);
405
422
  };
406
423
  const handleClear = () => {
407
424
  if (!isControlled) setInternalValue("");
408
425
  onClear?.();
409
- const event = { target: { value: "" } };
410
- onChange?.(event);
426
+ onChange?.("");
411
427
  };
412
428
  const togglePassword = () => {
413
429
  setType((prev) => prev === "password" ? "text" : "password");
@@ -418,6 +434,7 @@ var Input = React4.forwardRef(({
418
434
  "fc-input",
419
435
  `fc-input--${size}`,
420
436
  `fc-input--${status}`,
437
+ radius && `fc-input--radius-${radius}`,
421
438
  (prefix || addonBefore) && "fc-input--has-prefix",
422
439
  (suffix || addonAfter || showClear || showPasswordToggle) && "fc-input--has-suffix",
423
440
  addonBefore && "fc-input--addon-before",
@@ -499,8 +516,13 @@ function Slider({
499
516
  tooltipColor
500
517
  }) {
501
518
  const trackRef = React5.useRef(null);
502
- const draggingRef = React5.useRef(null);
503
519
  const [dragging, setDragging] = React5.useState(null);
520
+ const dragCleanupRef = React5.useRef(null);
521
+ React5.useEffect(() => {
522
+ return () => {
523
+ dragCleanupRef.current?.();
524
+ };
525
+ }, []);
504
526
  const initialValue = defaultValue ?? (range ? [min, max] : min);
505
527
  const [internalValue, setInternalValue] = React5.useState(initialValue);
506
528
  const isControlled = controlledValue !== void 0;
@@ -528,39 +550,66 @@ function Slider({
528
550
  const stepped = Math.round(raw / step) * step;
529
551
  return Math.max(min, Math.min(max, stepped));
530
552
  };
531
- const handleMove = React5.useCallback((clientX, clientY) => {
532
- if (!trackRef.current || draggingRef.current === null || disabled) return;
553
+ const handleMove = React5.useCallback((clientX, clientY, activeIndex) => {
554
+ if (!trackRef.current || disabled) return;
533
555
  const rect = trackRef.current.getBoundingClientRect();
534
556
  const percent = orientation === "horizontal" ? (clientX - rect.left) / rect.width * 100 : (rect.bottom - clientY) / rect.height * 100;
535
557
  const newValue = getValueFromPercent(Math.max(0, Math.min(100, percent)));
536
558
  let nextValue;
537
559
  if (range) {
538
560
  const [start, end] = currentValue;
539
- nextValue = draggingRef.current === 0 ? [Math.min(newValue, end), end] : [start, Math.max(newValue, start)];
561
+ nextValue = activeIndex === 0 ? [Math.min(newValue, end), end] : [start, Math.max(newValue, start)];
540
562
  } else {
541
563
  nextValue = newValue;
542
564
  }
543
565
  if (!isControlled) setInternalValue(nextValue);
544
566
  onChange?.(nextValue);
545
567
  }, [disabled, orientation, range, currentValue, isControlled, onChange, min, max, step]);
546
- const handleMouseDown = (index) => (e) => {
568
+ const startDrag = (index, clientX, clientY) => {
547
569
  if (disabled) return;
548
- e.preventDefault();
549
- draggingRef.current = index;
550
570
  setDragging(index);
551
- const handleMouseMove = (e2) => handleMove(e2.clientX, e2.clientY);
552
- const handleMouseUp = () => {
553
- draggingRef.current = null;
554
- setDragging(null);
571
+ const handleMouseMove = (ev) => handleMove(ev.clientX, ev.clientY, index);
572
+ const handleTouchMove = (ev) => {
573
+ ev.preventDefault();
574
+ const t = ev.touches[0];
575
+ handleMove(t.clientX, t.clientY, index);
576
+ };
577
+ const cleanup = () => {
555
578
  document.removeEventListener("mousemove", handleMouseMove);
556
579
  document.removeEventListener("mouseup", handleMouseUp);
580
+ document.removeEventListener("touchmove", handleTouchMove);
581
+ document.removeEventListener("touchend", handleTouchEnd);
582
+ dragCleanupRef.current = null;
583
+ };
584
+ const handleMouseUp = () => {
585
+ setDragging(null);
586
+ cleanup();
557
587
  };
588
+ const handleTouchEnd = () => {
589
+ setDragging(null);
590
+ cleanup();
591
+ };
592
+ dragCleanupRef.current = cleanup;
558
593
  document.addEventListener("mousemove", handleMouseMove);
559
594
  document.addEventListener("mouseup", handleMouseUp);
595
+ document.addEventListener("touchmove", handleTouchMove, { passive: false });
596
+ document.addEventListener("touchend", handleTouchEnd);
597
+ handleMove(clientX, clientY, index);
598
+ };
599
+ const handleMouseDown = (index) => (e) => {
600
+ if (disabled) return;
601
+ e.preventDefault();
602
+ startDrag(index, e.clientX, e.clientY);
603
+ };
604
+ const handleTouchStart = (index) => (e) => {
605
+ if (disabled) return;
606
+ e.preventDefault();
607
+ const t = e.touches[0];
608
+ startDrag(index, t.clientX, t.clientY);
560
609
  };
561
610
  const handleTrackClick = (e) => {
562
- if (disabled || draggingRef.current !== null) return;
563
- handleMove(e.clientX, e.clientY);
611
+ if (disabled || dragging !== null) return;
612
+ handleMove(e.clientX, e.clientY, 0);
564
613
  };
565
614
  const [startVal, endVal] = range ? currentValue : [min, currentValue];
566
615
  const startPercent = getPercent(startVal);
@@ -595,6 +644,7 @@ function Slider({
595
644
  className: `fc-slider__thumb ${dragging === 0 ? "fc-slider__thumb--active" : ""}`,
596
645
  style: thumbStyle(startPercent),
597
646
  onMouseDown: handleMouseDown(0),
647
+ onTouchStart: handleTouchStart(0),
598
648
  children: tooltip && /* @__PURE__ */ jsx7("span", { className: "fc-slider__tooltip", children: startVal })
599
649
  }
600
650
  ),
@@ -604,6 +654,7 @@ function Slider({
604
654
  className: `fc-slider__thumb ${dragging === (range ? 1 : 0) ? "fc-slider__thumb--active" : ""}`,
605
655
  style: thumbStyle(endPercent),
606
656
  onMouseDown: handleMouseDown(range ? 1 : 0),
657
+ onTouchStart: handleTouchStart(range ? 1 : 0),
607
658
  children: tooltip && /* @__PURE__ */ jsx7("span", { className: "fc-slider__tooltip", children: endVal })
608
659
  }
609
660
  ),
@@ -626,6 +677,23 @@ function Slider({
626
677
 
627
678
  // src/components/Select/Select.tsx
628
679
  import * as React6 from "react";
680
+
681
+ // src/hooks/useClickOutside.ts
682
+ import { useEffect as useEffect4 } from "react";
683
+ function useClickOutside(ref, handler, enabled = true) {
684
+ useEffect4(() => {
685
+ if (!enabled) return;
686
+ const listener = (e) => {
687
+ if (ref.current && !ref.current.contains(e.target)) {
688
+ handler();
689
+ }
690
+ };
691
+ document.addEventListener("pointerdown", listener);
692
+ return () => document.removeEventListener("pointerdown", listener);
693
+ }, [ref, handler, enabled]);
694
+ }
695
+
696
+ // src/components/Select/Select.tsx
629
697
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
630
698
  function Select({
631
699
  options,
@@ -636,6 +704,7 @@ function Select({
636
704
  searchable = false,
637
705
  multiple = false,
638
706
  disabled = false,
707
+ radius,
639
708
  className = "",
640
709
  style,
641
710
  virtualScroll = false,
@@ -718,28 +787,63 @@ function Select({
718
787
  const selected = options.find((o) => o.value === currentValue);
719
788
  return selected?.label || placeholder;
720
789
  };
721
- React6.useEffect(() => {
722
- const handleClick = (e) => {
723
- if (!containerRef.current?.contains(e.target)) {
790
+ useClickOutside(containerRef, () => setIsOpen(false));
791
+ const handleKeyDown = (e) => {
792
+ if (disabled) return;
793
+ switch (e.key) {
794
+ case "Enter":
795
+ case " ":
796
+ e.preventDefault();
797
+ if (!isOpen) {
798
+ setIsOpen(true);
799
+ break;
800
+ }
801
+ if (flatOptions[highlightedIndex]) handleSelect(flatOptions[highlightedIndex]);
802
+ break;
803
+ case "ArrowDown":
804
+ e.preventDefault();
805
+ if (!isOpen) {
806
+ setIsOpen(true);
807
+ break;
808
+ }
809
+ setHighlightedIndex((i) => Math.min(i + 1, flatOptions.length - 1));
810
+ break;
811
+ case "ArrowUp":
812
+ e.preventDefault();
813
+ setHighlightedIndex((i) => Math.max(i - 1, 0));
814
+ break;
815
+ case "Escape":
816
+ e.preventDefault();
724
817
  setIsOpen(false);
725
- }
726
- };
727
- document.addEventListener("mousedown", handleClick);
728
- return () => document.removeEventListener("mousedown", handleClick);
729
- }, []);
818
+ break;
819
+ case "Home":
820
+ e.preventDefault();
821
+ setHighlightedIndex(0);
822
+ break;
823
+ case "End":
824
+ e.preventDefault();
825
+ setHighlightedIndex(flatOptions.length - 1);
826
+ break;
827
+ }
828
+ };
730
829
  const classNames = [
731
830
  "fc-select",
732
831
  isOpen && "fc-select--open",
733
832
  multiple && "fc-select--multiple",
734
833
  disabled && "fc-select--disabled",
834
+ radius && `fc-select--radius-${radius}`,
735
835
  className
736
836
  ].filter(Boolean).join(" ");
737
- return /* @__PURE__ */ jsxs6("div", { ref: containerRef, className: classNames, style: mergedStyle, children: [
837
+ return /* @__PURE__ */ jsxs6("div", { ref: containerRef, className: classNames, style: mergedStyle, onKeyDown: handleKeyDown, children: [
738
838
  /* @__PURE__ */ jsxs6(
739
839
  "div",
740
840
  {
741
841
  className: "fc-select__trigger",
742
842
  onClick: () => !disabled && setIsOpen(!isOpen),
843
+ tabIndex: disabled ? -1 : 0,
844
+ role: "combobox",
845
+ "aria-expanded": isOpen,
846
+ "aria-haspopup": "listbox",
743
847
  children: [
744
848
  /* @__PURE__ */ jsx8("span", { className: `fc-select__value ${(currentValue === void 0 || currentValue === null || multiple && !currentValue.length) && "fc-select__value--placeholder"}`, children: displayLabel() }),
745
849
  /* @__PURE__ */ jsx8("span", { className: "fc-select__arrow", children: "\u25BC" })
@@ -822,7 +926,7 @@ import {
822
926
  memo as memo2,
823
927
  useCallback as useCallback4,
824
928
  useContext as useContext2,
825
- useEffect as useEffect4,
929
+ useEffect as useEffect5,
826
930
  useMemo as useMemo2,
827
931
  useRef as useRef4,
828
932
  useState as useState8
@@ -1007,7 +1111,7 @@ var CollapsePanel = memo2(function CollapsePanel2({ open, children }) {
1007
1111
  const innerRef = useRef4(null);
1008
1112
  const [height, setHeight] = useState8(0);
1009
1113
  const [ready, setReady] = useState8(false);
1010
- useEffect4(() => {
1114
+ useEffect5(() => {
1011
1115
  const el = innerRef.current;
1012
1116
  if (!el) return;
1013
1117
  const ro = new ResizeObserver(() => setHeight(el.offsetHeight));
@@ -1051,7 +1155,7 @@ var TreeNodeItem = memo2(function TreeNodeItem2({ node, level, hidden = false })
1051
1155
  const hasChildren = node.children.length > 0;
1052
1156
  const indent = level * 20 + 12;
1053
1157
  const [localEdit, setLocalEdit] = useState8("");
1054
- useEffect4(() => {
1158
+ useEffect5(() => {
1055
1159
  if (isEditing) setLocalEdit(node.title);
1056
1160
  }, [isEditing, node.title]);
1057
1161
  const handleEditKeyDown = useCallback4((e) => {
@@ -1188,7 +1292,7 @@ function Tree({
1188
1292
  });
1189
1293
  const dropRef = useRef4({ key: null, pos: null });
1190
1294
  const pointerYRef = useRef4(0);
1191
- useEffect4(() => {
1295
+ useEffect5(() => {
1192
1296
  const handler = (e) => {
1193
1297
  pointerYRef.current = e.clientY;
1194
1298
  };
@@ -1414,50 +1518,16 @@ function OrphanDialog({ orphans, onResolve, onClose }) {
1414
1518
  }
1415
1519
 
1416
1520
  // src/components/Avatar/Avatar.tsx
1417
- import { useState as useState10, useMemo as useMemo3, useCallback as useCallback5, forwardRef as forwardRef2, useEffect as useEffect5 } from "react";
1521
+ import { useState as useState10, useCallback as useCallback5, forwardRef as forwardRef2, useEffect as useEffect6 } from "react";
1418
1522
  import { jsx as jsx12 } from "react/jsx-runtime";
1419
- var SIZE_MAP = {
1420
- xs: 20,
1421
- sm: 28,
1422
- md: 40,
1423
- lg: 56,
1424
- xl: 72
1425
- };
1426
1523
  var DEFAULT_ICON = "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z";
1427
- var useAvatarStyles = (size, shape, bordered, onClick, loadState, src, children, colorVariant, className, color, customStyle) => {
1428
- const classes = useMemo3(() => {
1429
- const classNames = [
1430
- "ui-avatar",
1431
- `ui-avatar-${size}`,
1432
- `ui-avatar-${shape}`,
1433
- bordered && "ui-avatar-bordered",
1434
- onClick && "ui-avatar-clickable",
1435
- loadState === "loading" && "ui-avatar-loading",
1436
- !src && !children && "ui-avatar-empty",
1437
- colorVariant && `ui-avatar--${colorVariant}`,
1438
- className
1439
- ];
1440
- return classNames.filter(Boolean).join(" ");
1441
- }, [size, shape, bordered, onClick, loadState, src, children, colorVariant, className]);
1442
- const style = useMemo3(() => ({
1443
- width: SIZE_MAP[size],
1444
- height: SIZE_MAP[size],
1445
- fontSize: `${SIZE_MAP[size] * 0.4}px`,
1446
- ...color && { backgroundColor: color },
1447
- ...customStyle
1448
- }), [size, color, customStyle]);
1449
- return { classes, style };
1450
- };
1451
- var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError, onStateChange) => {
1524
+ var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError) => {
1452
1525
  const [loadState, setLoadState] = useState10("idle");
1453
1526
  const [currentSrc, setCurrentSrc] = useState10(src);
1454
- useEffect5(() => {
1527
+ useEffect6(() => {
1455
1528
  setCurrentSrc(src);
1456
1529
  setLoadState(src ? "loading" : "idle");
1457
1530
  }, [src]);
1458
- useEffect5(() => {
1459
- onStateChange?.(loadState);
1460
- }, [loadState, onStateChange]);
1461
1531
  const handleLoad = useCallback5(() => {
1462
1532
  setLoadState("loaded");
1463
1533
  onImageLoad?.();
@@ -1478,18 +1548,8 @@ var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError, onStateChange
1478
1548
  handleError
1479
1549
  };
1480
1550
  };
1481
- var useKeyboardInteraction = (onClick) => {
1482
- const handleKeyDown = useCallback5((e) => {
1483
- if (onClick && (e.key === "Enter" || e.key === " ")) {
1484
- e.preventDefault();
1485
- onClick(e);
1486
- }
1487
- }, [onClick]);
1488
- return { handleKeyDown };
1489
- };
1490
- var renderContent = (currentSrc, fallbackSrc, loadState, alt, lazyLoad2, children, handleLoad, handleError) => {
1551
+ var renderContent = (currentSrc, fallbackSrc, loadState, alt, lazyLoad2, handleLoad, handleError) => {
1491
1552
  const showImage = currentSrc && loadState !== "error";
1492
- const showFallback = !showImage && children;
1493
1553
  const imageSrc = loadState === "error" && currentSrc !== fallbackSrc ? fallbackSrc : currentSrc;
1494
1554
  if (showImage && imageSrc) {
1495
1555
  return /* @__PURE__ */ jsx12(
@@ -1505,37 +1565,20 @@ var renderContent = (currentSrc, fallbackSrc, loadState, alt, lazyLoad2, childre
1505
1565
  }
1506
1566
  );
1507
1567
  }
1508
- if (showFallback) {
1509
- return /* @__PURE__ */ jsx12("span", { className: "ui-avatar-text", children });
1510
- }
1511
- return /* @__PURE__ */ jsx12("span", { className: "ui-avatar-placeholder", children: /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 24 24", width: "40%", height: "40%", fill: "currentColor", opacity: 0.4, children: /* @__PURE__ */ jsx12("path", { d: DEFAULT_ICON }) }) });
1512
- };
1513
- var useAriaAttributes = (onClick, loadState, alt) => {
1514
- return {
1515
- role: onClick ? "button" : "img",
1516
- tabIndex: onClick ? 0 : void 0,
1517
- "aria-label": alt,
1518
- "aria-busy": loadState === "loading",
1519
- "data-load-state": loadState
1520
- };
1568
+ return /* @__PURE__ */ jsx12("span", { className: "ui-avatar-placeholder", children: /* @__PURE__ */ jsx12("svg", { viewBox: "0 0 24 24", width: "40%", height: "40%", fill: "currentColor", children: /* @__PURE__ */ jsx12("path", { d: DEFAULT_ICON }) }) });
1521
1569
  };
1522
1570
  var Avatar = forwardRef2(
1523
1571
  ({
1524
- children,
1525
1572
  src,
1526
1573
  fallbackSrc,
1527
- color,
1528
- colorVariant,
1529
- size = "md",
1574
+ size = 40,
1530
1575
  shape = "circle",
1531
1576
  alt = "\u7528\u6237\u5934\u50CF",
1532
1577
  lazyLoad: lazyLoad2 = false,
1533
1578
  onImageLoad,
1534
1579
  onImageError,
1535
- onStateChange,
1536
1580
  bordered = false,
1537
1581
  className = "",
1538
- onClick,
1539
1582
  style: customStyle,
1540
1583
  ...restProps
1541
1584
  }, ref) => {
@@ -1543,31 +1586,28 @@ var Avatar = forwardRef2(
1543
1586
  src,
1544
1587
  fallbackSrc,
1545
1588
  onImageLoad,
1546
- onImageError,
1547
- onStateChange
1548
- );
1549
- const { classes, style } = useAvatarStyles(
1550
- size,
1551
- shape,
1552
- bordered,
1553
- onClick,
1554
- loadState,
1555
- src,
1556
- children,
1557
- colorVariant,
1558
- className,
1559
- color,
1560
- customStyle
1589
+ onImageError
1561
1590
  );
1562
- const { handleKeyDown } = useKeyboardInteraction(onClick);
1563
- const ariaAttributes = useAriaAttributes(onClick, loadState, alt);
1591
+ const sizeValue = `${size}px`;
1592
+ const classes = [
1593
+ "ui-avatar",
1594
+ `ui-avatar-${shape}`,
1595
+ bordered && "ui-avatar-bordered",
1596
+ loadState === "loading" && "ui-avatar-loading",
1597
+ className
1598
+ ].filter(Boolean).join(" ");
1599
+ const style = {
1600
+ width: sizeValue,
1601
+ height: sizeValue,
1602
+ fontSize: `calc(${sizeValue} * 0.4)`,
1603
+ ...customStyle
1604
+ };
1564
1605
  const content = renderContent(
1565
1606
  currentSrc,
1566
1607
  fallbackSrc,
1567
1608
  loadState,
1568
1609
  alt,
1569
1610
  lazyLoad2,
1570
- children,
1571
1611
  handleLoad,
1572
1612
  handleError
1573
1613
  );
@@ -1577,9 +1617,9 @@ var Avatar = forwardRef2(
1577
1617
  ref,
1578
1618
  className: classes,
1579
1619
  style,
1580
- onClick,
1581
- onKeyDown: handleKeyDown,
1582
- ...ariaAttributes,
1620
+ role: "img",
1621
+ "aria-label": alt,
1622
+ "data-load-state": loadState,
1583
1623
  ...restProps,
1584
1624
  children: content
1585
1625
  }
@@ -1589,7 +1629,7 @@ var Avatar = forwardRef2(
1589
1629
  Avatar.displayName = "Avatar";
1590
1630
 
1591
1631
  // src/components/ListGroup/ListGroup.tsx
1592
- import { forwardRef as forwardRef3, useMemo as useMemo4, useCallback as useCallback6 } from "react";
1632
+ import { forwardRef as forwardRef3, useMemo as useMemo3, useCallback as useCallback6 } from "react";
1593
1633
  import { jsx as jsx13 } from "react/jsx-runtime";
1594
1634
  var combineClassNames = (...classNames) => {
1595
1635
  return classNames.filter(Boolean).join(" ");
@@ -1607,7 +1647,7 @@ var ListGroupItem = forwardRef3(
1607
1647
  if (disabled) return;
1608
1648
  onClick?.(e);
1609
1649
  }, [disabled, onClick]);
1610
- const classNames = useMemo4(() => {
1650
+ const classNames = useMemo3(() => {
1611
1651
  return combineClassNames(
1612
1652
  "fc-list-group-item",
1613
1653
  active && "fc-list-group-item--active",
@@ -1640,7 +1680,7 @@ var ListGroup = forwardRef3(
1640
1680
  children,
1641
1681
  ...props
1642
1682
  }, ref) => {
1643
- const classNames = useMemo4(() => {
1683
+ const classNames = useMemo3(() => {
1644
1684
  return combineClassNames(
1645
1685
  "fc-list-group",
1646
1686
  bordered && "fc-list-group--bordered",
@@ -1663,7 +1703,7 @@ var ListGroup = forwardRef3(
1663
1703
  ListGroup.displayName = "ListGroup";
1664
1704
 
1665
1705
  // src/components/VirtualList/VirtualList.tsx
1666
- import { useState as useState11, useRef as useRef5, useCallback as useCallback7, useMemo as useMemo5, memo as memo3 } from "react";
1706
+ import { useState as useState11, useRef as useRef5, useCallback as useCallback7, useMemo as useMemo4, memo as memo3 } from "react";
1667
1707
  import { jsx as jsx14 } from "react/jsx-runtime";
1668
1708
  var VirtualItem = memo3(({ children, height }) => /* @__PURE__ */ jsx14("div", { className: "fc-virtual-list__item", style: { height: `${height}px` }, children }));
1669
1709
  VirtualItem.displayName = "VirtualItem";
@@ -1692,7 +1732,7 @@ function VirtualList({
1692
1732
  }
1693
1733
  }
1694
1734
  }, [onScrollEnd]);
1695
- const visibleRange = useMemo5(() => {
1735
+ const visibleRange = useMemo4(() => {
1696
1736
  const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
1697
1737
  const endIndex = Math.min(
1698
1738
  data.length,
@@ -1700,7 +1740,7 @@ function VirtualList({
1700
1740
  );
1701
1741
  return { startIndex, endIndex };
1702
1742
  }, [scrollTop, height, itemHeight, data.length, overscan]);
1703
- const visibleData = useMemo5(() => {
1743
+ const visibleData = useMemo4(() => {
1704
1744
  return data.slice(visibleRange.startIndex, visibleRange.endIndex);
1705
1745
  }, [data, visibleRange]);
1706
1746
  const offsetY = visibleRange.startIndex * itemHeight;
@@ -1739,7 +1779,7 @@ function VirtualList({
1739
1779
  }
1740
1780
 
1741
1781
  // src/components/Alert/AlertContext.tsx
1742
- import { createContext as createContext3, useContext as useContext3, useEffect as useEffect6, useState as useState12 } from "react";
1782
+ import { createContext as createContext3, useContext as useContext3, useEffect as useEffect7, useRef as useRef6, useState as useState12 } from "react";
1743
1783
  import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
1744
1784
  var AlertContext = createContext3(null);
1745
1785
  var ICONS = {
@@ -1824,7 +1864,18 @@ function AlertProvider({ children, background, borderColor }) {
1824
1864
  choice: () => {
1825
1865
  }
1826
1866
  });
1827
- const showAlert = (msg, type, mode = "alert", duration) => new Promise((resolve) => {
1867
+ const mountedRef = useRef6(true);
1868
+ useEffect7(() => {
1869
+ mountedRef.current = true;
1870
+ return () => {
1871
+ mountedRef.current = false;
1872
+ };
1873
+ }, []);
1874
+ const showAlert = (msg, type, mode = "alert", duration) => new Promise((resolve, reject) => {
1875
+ if (!mountedRef.current) {
1876
+ reject(new Error("AlertProvider unmounted"));
1877
+ return;
1878
+ }
1828
1879
  setAlert({
1829
1880
  msg,
1830
1881
  type,
@@ -1832,12 +1883,13 @@ function AlertProvider({ children, background, borderColor }) {
1832
1883
  visible: true,
1833
1884
  duration,
1834
1885
  choice: (res) => {
1886
+ if (!mountedRef.current) return;
1835
1887
  setAlert((p) => ({ ...p, visible: false }));
1836
1888
  resolve(res);
1837
1889
  }
1838
1890
  });
1839
1891
  });
1840
- useEffect6(() => {
1892
+ useEffect7(() => {
1841
1893
  if (!alert.visible || !alert.duration) return;
1842
1894
  const timer = setTimeout(() => alert.choice("auto"), alert.duration);
1843
1895
  return () => clearTimeout(timer);
@@ -1973,13 +2025,107 @@ var Card = ({
1973
2025
  };
1974
2026
 
1975
2027
  // src/components/Bar/TabBar.tsx
1976
- import { useRef as useRef6, useCallback as useCallback8, memo as memo4 } from "react";
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
1977
2122
  import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1978
2123
  var TabItemView = memo4(({
1979
2124
  item,
1980
2125
  isActive,
1981
2126
  closable,
1982
2127
  draggable,
2128
+ tabWidth,
1983
2129
  tabClassName,
1984
2130
  activeTabClassName,
1985
2131
  tabStyle,
@@ -1993,6 +2139,7 @@ var TabItemView = memo4(({
1993
2139
  onDragEnd
1994
2140
  }) => {
1995
2141
  const showClose = item.closable !== void 0 ? item.closable : closable;
2142
+ const isCompact = tabWidth !== void 0 && tabWidth < 42;
1996
2143
  const classes = [
1997
2144
  "fc-tab-bar__tab",
1998
2145
  isActive && "fc-tab-bar__tab--active",
@@ -2002,6 +2149,7 @@ var TabItemView = memo4(({
2002
2149
  isActive && activeTabClassName
2003
2150
  ].filter(Boolean).join(" ");
2004
2151
  const mergedStyle = {
2152
+ ...tabWidth !== void 0 ? { width: tabWidth, boxSizing: "border-box", flexShrink: 0 } : void 0,
2005
2153
  ...tabStyle,
2006
2154
  ...isActive ? activeTabStyle : void 0
2007
2155
  };
@@ -2010,6 +2158,7 @@ var TabItemView = memo4(({
2010
2158
  {
2011
2159
  className: classes,
2012
2160
  style: mergedStyle,
2161
+ "data-compact": isCompact || void 0,
2013
2162
  onClick: () => !item.disabled && onClick(item.key),
2014
2163
  draggable: draggable && !item.disabled,
2015
2164
  onDragStart: (e) => onDragStart(e, item.key),
@@ -2028,7 +2177,7 @@ var TabItemView = memo4(({
2028
2177
  className: "fc-tab-bar__tab-close",
2029
2178
  onClick: (e) => onClose(e, item.key),
2030
2179
  role: "button",
2031
- "aria-label": `\u5173\u95ED ${typeof item.label === "string" ? item.label : ""}`,
2180
+ "aria-label": `\u5173\u95ED`,
2032
2181
  children: renderCloseIcon ? renderCloseIcon(item.key) : "\xD7"
2033
2182
  }
2034
2183
  )
@@ -2046,6 +2195,8 @@ var TabBar = memo4(({
2046
2195
  closable = false,
2047
2196
  addable = false,
2048
2197
  draggable = false,
2198
+ minWidthRatio = 0.07,
2199
+ maxTabWidthRatio = 0.15,
2049
2200
  onChange,
2050
2201
  onClose,
2051
2202
  onAdd,
@@ -2082,19 +2233,37 @@ var TabBar = memo4(({
2082
2233
  }
2083
2234
  }
2084
2235
  const mergedStyle = { ...overrideStyle, ...style };
2085
- const dragKeyRef = useRef6(null);
2086
- const handleClick = useCallback8(
2236
+ const dragKeyRef = useRef8(null);
2237
+ const navRef = useRef8(null);
2238
+ const layout = useAdaptiveTabLayout(navRef, {
2239
+ itemsLength: items.length,
2240
+ addable,
2241
+ minWidthRatio,
2242
+ maxTabWidthRatio
2243
+ });
2244
+ const prevItemsLengthRef = useRef8(items.length);
2245
+ useEffect9(() => {
2246
+ const prev = prevItemsLengthRef.current;
2247
+ prevItemsLengthRef.current = items.length;
2248
+ if (items.length <= prev || !layout.scrollMode) return;
2249
+ const roll = navRef.current?.querySelector(".fc-roll");
2250
+ if (!roll) return;
2251
+ requestAnimationFrame(() => {
2252
+ roll.scrollLeft = roll.scrollWidth;
2253
+ });
2254
+ }, [items.length, layout.scrollMode]);
2255
+ const handleClick = useCallback9(
2087
2256
  (key) => onChange(key),
2088
2257
  [onChange]
2089
2258
  );
2090
- const handleClose = useCallback8(
2259
+ const handleClose = useCallback9(
2091
2260
  (e, key) => {
2092
2261
  e.stopPropagation();
2093
2262
  onClose?.(key);
2094
2263
  },
2095
2264
  [onClose]
2096
2265
  );
2097
- const handleDragStart = useCallback8(
2266
+ const handleDragStart = useCallback9(
2098
2267
  (e, key) => {
2099
2268
  dragKeyRef.current = key;
2100
2269
  e.dataTransfer.effectAllowed = "move";
@@ -2103,11 +2272,11 @@ var TabBar = memo4(({
2103
2272
  },
2104
2273
  []
2105
2274
  );
2106
- const handleDragOver = useCallback8((e) => {
2275
+ const handleDragOver = useCallback9((e) => {
2107
2276
  e.preventDefault();
2108
2277
  e.dataTransfer.dropEffect = "move";
2109
2278
  }, []);
2110
- const handleDrop = useCallback8(
2279
+ const handleDrop = useCallback9(
2111
2280
  (e, targetKey) => {
2112
2281
  e.preventDefault();
2113
2282
  const dragKey = dragKeyRef.current;
@@ -2122,55 +2291,60 @@ var TabBar = memo4(({
2122
2291
  },
2123
2292
  [items, onReorder]
2124
2293
  );
2125
- const handleDragEnd = useCallback8((e) => {
2294
+ const handleDragEnd = useCallback9((e) => {
2126
2295
  dragKeyRef.current = null;
2127
2296
  e.currentTarget.classList.remove("fc-tab-bar__tab--dragging");
2128
2297
  }, []);
2298
+ const { scrollMode, tabWidth } = layout;
2129
2299
  const rootClasses = [
2130
2300
  "fc-tab-bar",
2131
2301
  `fc-tab-bar--${variant}`,
2132
2302
  `fc-tab-bar--radius-${radius}`,
2133
2303
  tabRadius && `fc-tab-bar--tab-radius-${tabRadius}`,
2304
+ !scrollMode && tabWidth !== void 0 && "fc-tab-bar--shrink",
2134
2305
  className
2135
2306
  ].filter(Boolean).join(" ");
2136
- return /* @__PURE__ */ jsx18("div", { className: rootClasses, style: mergedStyle, role: "tablist", children: /* @__PURE__ */ jsx18("div", { className: "fc-tab-bar__nav", children: /* @__PURE__ */ jsxs13("div", { className: "fc-tab-bar__nav-wrap", children: [
2137
- items.map((item) => /* @__PURE__ */ jsx18(
2138
- TabItemView,
2139
- {
2140
- item,
2141
- isActive: activeKey === item.key,
2142
- closable,
2143
- draggable,
2144
- tabClassName,
2145
- activeTabClassName,
2146
- tabStyle,
2147
- activeTabStyle,
2148
- renderCloseIcon,
2149
- onClick: handleClick,
2150
- onClose: handleClose,
2151
- onDragStart: handleDragStart,
2152
- onDragOver: handleDragOver,
2153
- onDrop: handleDrop,
2154
- onDragEnd: handleDragEnd
2155
- },
2156
- item.key
2157
- )),
2158
- addable && /* @__PURE__ */ jsx18(
2159
- "div",
2160
- {
2161
- className: "fc-tab-bar__add-btn",
2162
- onClick: onAdd,
2163
- role: "button",
2164
- "aria-label": "\u6DFB\u52A0\u6807\u7B7E",
2165
- children: renderAddButton ? renderAddButton() : "+"
2166
- }
2167
- )
2168
- ] }) }) });
2307
+ const tabList = /* @__PURE__ */ jsx18("div", { className: "fc-tab-bar__nav-wrap", children: items.map((item) => /* @__PURE__ */ jsx18(
2308
+ TabItemView,
2309
+ {
2310
+ item,
2311
+ isActive: activeKey === item.key,
2312
+ closable,
2313
+ draggable,
2314
+ tabWidth,
2315
+ tabClassName,
2316
+ activeTabClassName,
2317
+ tabStyle,
2318
+ activeTabStyle,
2319
+ renderCloseIcon,
2320
+ onClick: handleClick,
2321
+ onClose: handleClose,
2322
+ onDragStart: handleDragStart,
2323
+ onDragOver: handleDragOver,
2324
+ onDrop: handleDrop,
2325
+ onDragEnd: handleDragEnd
2326
+ },
2327
+ item.key
2328
+ )) });
2329
+ const addBtn = addable && /* @__PURE__ */ jsx18(
2330
+ "div",
2331
+ {
2332
+ className: "fc-tab-bar__add-btn",
2333
+ onClick: onAdd,
2334
+ role: "button",
2335
+ "aria-label": "\u6DFB\u52A0\u6807\u7B7E",
2336
+ children: renderAddButton ? renderAddButton() : "+"
2337
+ }
2338
+ );
2339
+ return /* @__PURE__ */ jsx18("div", { className: rootClasses, style: mergedStyle, role: "tablist", children: /* @__PURE__ */ jsxs13("div", { className: `fc-tab-bar__nav-outer${scrollMode ? " fc-tab-bar__nav-outer--scroll" : ""}`, children: [
2340
+ /* @__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 }),
2341
+ addBtn
2342
+ ] }) });
2169
2343
  });
2170
2344
  TabBar.displayName = "TabBar";
2171
2345
 
2172
2346
  // src/components/Tag/TagItem.tsx
2173
- import { useEffect as useEffect7, useRef as useRef7, useState as useState13 } from "react";
2347
+ import { useEffect as useEffect10, useRef as useRef9, useState as useState14 } from "react";
2174
2348
  import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
2175
2349
  function TagItem({
2176
2350
  schema,
@@ -2181,10 +2355,10 @@ function TagItem({
2181
2355
  color,
2182
2356
  borderColor
2183
2357
  }) {
2184
- const [editing, setEditing] = useState13(false);
2185
- const [draft, setDraft] = useState13(() => value !== void 0 ? String(value) : "");
2186
- const inputRef = useRef7(null);
2187
- useEffect7(() => {
2358
+ const [editing, setEditing] = useState14(false);
2359
+ const [draft, setDraft] = useState14(() => value !== void 0 ? String(value) : "");
2360
+ const inputRef = useRef9(null);
2361
+ useEffect10(() => {
2188
2362
  if (!editing) setDraft(value !== void 0 ? String(value) : "");
2189
2363
  }, [value, editing]);
2190
2364
  const colorVars = {
@@ -2283,25 +2457,29 @@ function TagItem({
2283
2457
  }
2284
2458
 
2285
2459
  // src/components/MarkdownEditor/MarkdownEditor.tsx
2460
+ import { useState as useState15 } from "react";
2286
2461
  import MDEditor, { commands } from "@uiw/react-md-editor";
2287
2462
  import { jsx as jsx20 } from "react/jsx-runtime";
2463
+ function withTitle(cmd, title) {
2464
+ return { ...cmd, buttonProps: { ...cmd.buttonProps ?? {}, title } };
2465
+ }
2288
2466
  var TOOLBAR_COMMANDS = [
2289
- commands.bold,
2290
- commands.italic,
2291
- commands.strikethrough,
2467
+ withTitle(commands.bold, "\u52A0\u7C97"),
2468
+ withTitle(commands.italic, "\u659C\u4F53"),
2469
+ withTitle(commands.strikethrough, "\u5220\u9664\u7EBF"),
2292
2470
  commands.divider,
2293
- commands.title1,
2294
- commands.title2,
2295
- commands.title3,
2471
+ withTitle(commands.title1, "\u4E00\u7EA7\u6807\u9898"),
2472
+ withTitle(commands.title2, "\u4E8C\u7EA7\u6807\u9898"),
2473
+ withTitle(commands.title3, "\u4E09\u7EA7\u6807\u9898"),
2296
2474
  commands.divider,
2297
- commands.quote,
2298
- commands.code,
2299
- commands.codeBlock,
2475
+ withTitle(commands.quote, "\u5F15\u7528"),
2476
+ withTitle(commands.code, "\u884C\u5185\u4EE3\u7801"),
2477
+ withTitle(commands.codeBlock, "\u4EE3\u7801\u5757"),
2300
2478
  commands.divider,
2301
- commands.link,
2302
- commands.unorderedListCommand,
2303
- commands.orderedListCommand,
2304
- commands.hr
2479
+ withTitle(commands.link, "\u94FE\u63A5"),
2480
+ withTitle(commands.unorderedListCommand, "\u65E0\u5E8F\u5217\u8868"),
2481
+ withTitle(commands.orderedListCommand, "\u6709\u5E8F\u5217\u8868"),
2482
+ withTitle(commands.hr, "\u5206\u5272\u7EBF")
2305
2483
  ];
2306
2484
  function MarkdownEditor({
2307
2485
  value,
@@ -2309,11 +2487,13 @@ function MarkdownEditor({
2309
2487
  onAiComplete,
2310
2488
  minHeight = 200,
2311
2489
  placeholder = "\u5728\u6B64\u8F93\u5165\u5185\u5BB9...",
2490
+ mode = "edit",
2312
2491
  background,
2313
2492
  toolbarBackground,
2314
2493
  borderColor
2315
2494
  }) {
2316
2495
  const { resolvedTheme } = useTheme();
2496
+ const [showSplit, setShowSplit] = useState15(false);
2317
2497
  const colorVars = {
2318
2498
  "--md-bg": background,
2319
2499
  "--md-toolbar-bg": toolbarBackground,
@@ -2326,11 +2506,27 @@ function MarkdownEditor({
2326
2506
  const aiCommand = {
2327
2507
  name: "ai-complete",
2328
2508
  keyCommand: "ai-complete",
2329
- buttonProps: { "aria-label": "AI \u8865\u5168", className: "fc-md-ai-btn" },
2330
- icon: /* @__PURE__ */ jsx20("span", { children: "AI \u8865\u5168" }),
2509
+ buttonProps: { "aria-label": "AI \u8865\u5168", title: "AI \u8865\u5168", className: "fc-md-ai-btn" },
2510
+ icon: /* @__PURE__ */ jsx20("span", { children: "AI" }),
2331
2511
  execute: () => onAiComplete?.()
2332
2512
  };
2333
- const extraCommands = onAiComplete ? [commands.divider, aiCommand, commands.fullscreen] : [commands.fullscreen];
2513
+ const splitCommand = {
2514
+ name: "split-view",
2515
+ keyCommand: "split-view",
2516
+ buttonProps: {
2517
+ "aria-label": showSplit ? "\u7EAF\u7F16\u8F91" : "\u53CC\u680F\u9884\u89C8",
2518
+ title: showSplit ? "\u7EAF\u7F16\u8F91" : "\u53CC\u680F\u9884\u89C8",
2519
+ className: `fc-md-split-btn${showSplit ? " fc-md-split-btn--active" : ""}`
2520
+ },
2521
+ icon: /* @__PURE__ */ jsx20("span", { className: "fc-md-split-icon", children: "\u229F" }),
2522
+ execute: () => setShowSplit((p) => !p)
2523
+ };
2524
+ const extraCommands = [
2525
+ splitCommand,
2526
+ ...onAiComplete ? [commands.divider, aiCommand] : [],
2527
+ withTitle(commands.fullscreen, "\u5168\u5C4F")
2528
+ ];
2529
+ const editorPreview = mode === "preview" ? "preview" : showSplit ? "live" : "edit";
2334
2530
  return /* @__PURE__ */ jsx20(
2335
2531
  "div",
2336
2532
  {
@@ -2345,7 +2541,8 @@ function MarkdownEditor({
2345
2541
  commands: TOOLBAR_COMMANDS,
2346
2542
  extraCommands,
2347
2543
  height: minHeight,
2348
- preview: "edit",
2544
+ preview: editorPreview,
2545
+ hideToolbar: mode === "preview",
2349
2546
  visibleDragbar: false,
2350
2547
  textareaProps: { placeholder }
2351
2548
  }
@@ -2355,7 +2552,7 @@ function MarkdownEditor({
2355
2552
  }
2356
2553
 
2357
2554
  // src/components/ContextMenu/ContextMenuContext.tsx
2358
- import { createContext as createContext4, useContext as useContext4, useEffect as useEffect8, useRef as useRef8, useState as useState14 } from "react";
2555
+ import { createContext as createContext4, useContext as useContext4, useEffect as useEffect11, useRef as useRef10, useState as useState16 } from "react";
2359
2556
  import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
2360
2557
  var ContextMenuContext = createContext4(null);
2361
2558
  function ContextMenuProvider({
@@ -2364,33 +2561,27 @@ function ContextMenuProvider({
2364
2561
  borderColor,
2365
2562
  hoverBackground
2366
2563
  }) {
2367
- const [menu, setMenu] = useState14({
2564
+ const [menu, setMenu] = useState16({
2368
2565
  visible: false,
2369
2566
  x: 0,
2370
2567
  y: 0,
2371
2568
  items: []
2372
2569
  });
2373
- const menuRef = useRef8(null);
2570
+ const menuRef = useRef10(null);
2374
2571
  const showContextMenu = (e, items) => {
2375
2572
  e.preventDefault();
2376
2573
  e.stopPropagation();
2377
2574
  setMenu({ visible: true, x: e.clientX, y: e.clientY, items });
2378
2575
  };
2379
2576
  const hide = () => setMenu((s) => ({ ...s, visible: false }));
2380
- useEffect8(() => {
2577
+ useClickOutside(menuRef, hide, menu.visible);
2578
+ useEffect11(() => {
2381
2579
  if (!menu.visible) return;
2382
- const onPointerDown = (e) => {
2383
- if (menuRef.current && !menuRef.current.contains(e.target)) hide();
2384
- };
2385
2580
  const onKeyDown = (e) => {
2386
2581
  if (e.key === "Escape") hide();
2387
2582
  };
2388
- window.addEventListener("pointerdown", onPointerDown);
2389
2583
  window.addEventListener("keydown", onKeyDown);
2390
- return () => {
2391
- window.removeEventListener("pointerdown", onPointerDown);
2392
- window.removeEventListener("keydown", onKeyDown);
2393
- };
2584
+ return () => window.removeEventListener("keydown", onKeyDown);
2394
2585
  }, [menu.visible]);
2395
2586
  const getPosition = () => {
2396
2587
  const W = window.innerWidth;
@@ -2453,16 +2644,15 @@ function ContextMenuProvider({
2453
2644
  var useContextMenu = () => useContext4(ContextMenuContext);
2454
2645
 
2455
2646
  // src/components/Chat/Chat.tsx
2456
- import { useState as useState16, useRef as useRef9, useEffect as useEffect9, useCallback as useCallback9, useMemo as useMemo6 } from "react";
2647
+ import { useState as useState18, useRef as useRef11, useEffect as useEffect12, useCallback as useCallback11, useMemo as useMemo5 } from "react";
2457
2648
 
2458
2649
  // src/components/SmartMessage/SmartMessage.tsx
2459
- import { useState as useState15, memo as memo5 } from "react";
2650
+ import { useState as useState17, useCallback as useCallback10 } from "react";
2460
2651
  import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
2461
- var SmartMessage = memo5(({
2652
+ var SmartMessage = ({
2462
2653
  id,
2463
2654
  content,
2464
2655
  role,
2465
- timestamp,
2466
2656
  status,
2467
2657
  toolName,
2468
2658
  toolResult,
@@ -2470,117 +2660,68 @@ var SmartMessage = memo5(({
2470
2660
  className = "",
2471
2661
  style = {}
2472
2662
  }) => {
2473
- const [copied, setCopied] = useState15(false);
2474
- const formattedTime = timestamp ? new Date(timestamp).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }) : "";
2475
- const handleCopy = async () => {
2663
+ const [copied, setCopied] = useState17(false);
2664
+ const handleCopy = useCallback10(async () => {
2476
2665
  try {
2477
- let copyContent = content;
2478
- if (role === "tool" && toolResult) {
2479
- copyContent = `\u5DE5\u5177: ${toolName || "\u5DE5\u5177\u8C03\u7528"}
2480
- \u7ED3\u679C: ${JSON.stringify(toolResult, null, 2)}`;
2481
- }
2482
- await navigator.clipboard.writeText(copyContent);
2666
+ await navigator.clipboard.writeText(content);
2483
2667
  setCopied(true);
2484
2668
  onCopy?.(content, role);
2485
2669
  setTimeout(() => setCopied(false), 2e3);
2486
2670
  } catch (err) {
2487
2671
  console.error("\u590D\u5236\u5931\u8D25:", err);
2488
2672
  }
2673
+ }, [content, role, onCopy]);
2674
+ const getMessageClass = () => {
2675
+ const baseClass = "fc-smart-message";
2676
+ const roleClass = `fc-smart-message--${role}`;
2677
+ const statusClass = status ? `fc-smart-message--${status}` : "";
2678
+ return `${baseClass} ${roleClass} ${statusClass} ${className}`.trim();
2489
2679
  };
2490
2680
  const shouldShowCopyButton = () => {
2491
2681
  return role === "user" || role === "assistant";
2492
2682
  };
2493
- const getContainerClassName = () => {
2494
- const baseClass = "smart-message";
2495
- const roleClass = `smart-message-${role}`;
2496
- const statusClass = status ? `smart-message-${status}` : "";
2497
- return `${baseClass} ${roleClass} ${statusClass} ${className}`.trim();
2498
- };
2499
- const getContentClassName = () => {
2500
- const baseClass = "smart-message-content";
2501
- const roleContentClass = `smart-message-content-${role}`;
2502
- return `${baseClass} ${roleContentClass}`;
2503
- };
2504
- const renderSystemMessage = () => /* @__PURE__ */ jsxs16("div", { className: "system-message-wrapper", children: [
2505
- /* @__PURE__ */ jsx22("div", { className: "system-message-icon", children: /* @__PURE__ */ jsxs16("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
2506
- /* @__PURE__ */ jsx22("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }),
2507
- /* @__PURE__ */ jsx22("path", { d: "M12 8V12M12 16H12.01", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2508
- ] }) }),
2509
- /* @__PURE__ */ jsx22("div", { className: "system-message-text", children: content }),
2510
- formattedTime && /* @__PURE__ */ jsx22("div", { className: "system-message-time", children: formattedTime })
2511
- ] });
2512
- const renderToolMessage = () => /* @__PURE__ */ jsxs16("div", { className: "tool-message-wrapper", children: [
2513
- /* @__PURE__ */ jsxs16("div", { className: "tool-message-header", children: [
2514
- /* @__PURE__ */ jsx22("div", { className: "tool-message-icon", children: /* @__PURE__ */ jsxs16("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
2515
- /* @__PURE__ */ jsx22("path", { d: "M14.7 6.3L19 2L22 5L17.7 9.3L14.7 6.3Z", stroke: "currentColor", strokeWidth: "2" }),
2516
- /* @__PURE__ */ jsx22("path", { d: "M9.3 17.7L5 22L2 19L6.3 14.7L9.3 17.7Z", stroke: "currentColor", strokeWidth: "2" }),
2517
- /* @__PURE__ */ jsx22("path", { d: "M12 12L14.7 9.3M12 12L9.3 14.7M12 12L8 8M12 12L16 16", stroke: "currentColor", strokeWidth: "2" })
2518
- ] }) }),
2519
- /* @__PURE__ */ jsx22("span", { className: "tool-message-name", children: toolName || "\u5DE5\u5177\u8C03\u7528" })
2520
- ] }),
2521
- /* @__PURE__ */ jsxs16("div", { className: "tool-message-content", children: [
2522
- /* @__PURE__ */ jsxs16("div", { className: "tool-message-params", children: [
2523
- /* @__PURE__ */ jsx22("strong", { children: "\u53C2\u6570:" }),
2524
- /* @__PURE__ */ jsx22("pre", { children: content })
2525
- ] }),
2526
- toolResult && /* @__PURE__ */ jsxs16("div", { className: "tool-message-result", children: [
2527
- /* @__PURE__ */ jsx22("strong", { children: "\u7ED3\u679C:" }),
2528
- /* @__PURE__ */ jsx22("pre", { children: typeof toolResult === "object" ? JSON.stringify(toolResult, null, 2) : toolResult })
2529
- ] })
2530
- ] }),
2531
- formattedTime && /* @__PURE__ */ jsx22("div", { className: "tool-message-time", children: formattedTime })
2532
- ] });
2533
- const renderUserAssistantMessage = () => /* @__PURE__ */ jsxs16(Fragment4, { children: [
2534
- /* @__PURE__ */ jsxs16("div", { className: "message-header", children: [
2535
- /* @__PURE__ */ jsx22("span", { className: "message-sender", children: role === "user" ? "\u6211" : "AI\u52A9\u624B" }),
2536
- formattedTime && /* @__PURE__ */ jsx22("span", { className: "message-time", children: formattedTime })
2537
- ] }),
2538
- /* @__PURE__ */ jsxs16("div", { className: getContentClassName(), children: [
2539
- /* @__PURE__ */ jsx22("div", { className: "message-text", children: content }),
2540
- shouldShowCopyButton() && /* @__PURE__ */ jsx22(
2541
- "button",
2542
- {
2543
- className: `copy-btn ${copied ? "copied" : ""}`,
2544
- onClick: handleCopy,
2545
- title: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5185\u5BB9",
2546
- children: copied ? /* @__PURE__ */ jsxs16(Fragment4, { children: [
2547
- /* @__PURE__ */ jsx22("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx22("path", { d: "M20 6L9 17L4 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
2548
- /* @__PURE__ */ jsx22("span", { children: "\u5DF2\u590D\u5236" })
2549
- ] }) : /* @__PURE__ */ jsxs16(Fragment4, { children: [
2550
- /* @__PURE__ */ jsxs16("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
2551
- /* @__PURE__ */ jsx22("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", stroke: "currentColor", strokeWidth: "2" }),
2552
- /* @__PURE__ */ jsx22("path", { d: "M5 15H4C2.9 15 2 14.1 2 13V4C2 2.9 2.9 2 4 2H13C14.1 2 15 2.9 15 4V5", stroke: "currentColor", strokeWidth: "2" })
2553
- ] }),
2554
- /* @__PURE__ */ jsx22("span", { children: "\u590D\u5236" })
2555
- ] })
2556
- }
2557
- ),
2558
- status === "sending" && /* @__PURE__ */ jsx22("span", { className: "message-status sending", children: "\u53D1\u9001\u4E2D..." }),
2559
- status === "error" && /* @__PURE__ */ jsx22("span", { className: "message-status error", children: "\u53D1\u9001\u5931\u8D25" })
2560
- ] })
2561
- ] });
2562
- const renderMessage = () => {
2683
+ const renderContent2 = () => {
2563
2684
  switch (role) {
2564
- case "system":
2565
- return renderSystemMessage();
2566
2685
  case "tool":
2567
- return renderToolMessage();
2568
- case "user":
2569
- case "assistant":
2686
+ return /* @__PURE__ */ jsxs16(Fragment4, { children: [
2687
+ toolName && /* @__PURE__ */ jsxs16("div", { className: "fc-smart-message-tool-info", children: [
2688
+ /* @__PURE__ */ jsx22("span", { className: "fc-smart-message-tool-icon", children: "\u{1F527}" }),
2689
+ /* @__PURE__ */ jsx22("span", { className: "fc-smart-message-tool-name", children: toolName })
2690
+ ] }),
2691
+ /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-content", children: /* @__PURE__ */ jsx22("pre", { className: "fc-smart-message-tool-result", children: (() => {
2692
+ try {
2693
+ return JSON.stringify(toolResult || content, null, 2);
2694
+ } catch {
2695
+ return "[\u5E8F\u5217\u5316\u5931\u8D25]";
2696
+ }
2697
+ })() }) })
2698
+ ] });
2699
+ case "system":
2700
+ return /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-content", children: /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-text fc-smart-message-system-text", children: content }) });
2570
2701
  default:
2571
- return renderUserAssistantMessage();
2702
+ return /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-content", children: /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-text", children: content }) });
2572
2703
  }
2573
2704
  };
2574
- return /* @__PURE__ */ jsx22("div", { className: getContainerClassName(), style, "data-message-id": id, children: renderMessage() });
2575
- });
2576
- SmartMessage.displayName = "SmartMessage";
2705
+ return /* @__PURE__ */ jsxs16("div", { className: getMessageClass(), style, "data-message-id": id, children: [
2706
+ /* @__PURE__ */ jsx22("div", { className: "fc-smart-message-content-wrapper", children: renderContent2() }),
2707
+ shouldShowCopyButton() && /* @__PURE__ */ jsx22(
2708
+ "button",
2709
+ {
2710
+ className: "fc-smart-message-copy-btn",
2711
+ onClick: handleCopy,
2712
+ title: "\u590D\u5236\u5185\u5BB9",
2713
+ children: copied ? /* @__PURE__ */ jsx22("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx22("path", { d: "M20 6L9 17L4 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) : /* @__PURE__ */ jsx22("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx22("path", { d: "M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z", fill: "currentColor" }) })
2714
+ }
2715
+ )
2716
+ ] });
2717
+ };
2577
2718
  var SmartMessage_default = SmartMessage;
2578
2719
 
2579
2720
  // src/components/Chat/Chat.tsx
2580
2721
  import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
2581
2722
  var Chat = ({
2582
2723
  messages = [],
2583
- title = "AI \u52A9\u624B",
2724
+ title = "\u6D41\u4E91AI",
2584
2725
  loading = false,
2585
2726
  conversations = [],
2586
2727
  currentConversationId,
@@ -2608,52 +2749,128 @@ var Chat = ({
2608
2749
  height = "600px",
2609
2750
  width
2610
2751
  }) => {
2611
- const [showHistory, setShowHistory] = useState16(false);
2612
- const [isMinimized, setIsMinimized] = useState16(false);
2613
- const messagesContainerRef = useRef9(null);
2614
- const messagesEndRef = useRef9(null);
2615
- const historyPanelRef = useRef9(null);
2616
- const currentConversation = useMemo6(
2752
+ const [showHistory, setShowHistory] = useState18(false);
2753
+ const [isMinimized, setIsMinimized] = useState18(false);
2754
+ const messagesContainerRef = useRef11(null);
2755
+ const messagesEndRef = useRef11(null);
2756
+ const historyPanelRef = useRef11(null);
2757
+ const currentConversation = useMemo5(
2617
2758
  () => conversations.find((c) => c.id === currentConversationId),
2618
2759
  [conversations, currentConversationId]
2619
2760
  );
2620
2761
  const currentTitle = currentConversation?.title || title;
2621
- useEffect9(() => {
2762
+ useEffect12(() => {
2622
2763
  if (autoScroll && messagesContainerRef.current && !showHistory && !isMinimized) {
2623
2764
  messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
2624
2765
  }
2625
2766
  }, [messages, loading, showHistory, isMinimized, autoScroll]);
2626
- useEffect9(() => {
2627
- const handleClickOutside = (event) => {
2628
- if (showHistory && historyPanelRef.current && !historyPanelRef.current.contains(event.target)) {
2629
- setShowHistory(false);
2630
- }
2631
- };
2632
- document.addEventListener("mousedown", handleClickOutside);
2633
- return () => document.removeEventListener("mousedown", handleClickOutside);
2634
- }, [showHistory]);
2635
- const handleDeleteConversation = useCallback9((conversationId) => {
2767
+ useClickOutside(historyPanelRef, () => setShowHistory(false), showHistory);
2768
+ const handleDeleteConversation = useCallback11((conversationId) => {
2636
2769
  onDeleteConversation?.(conversationId);
2637
2770
  }, [onDeleteConversation]);
2638
- const handleMinimize = useCallback9(() => {
2771
+ const handleMinimize = useCallback11(() => {
2639
2772
  setIsMinimized(true);
2640
2773
  onMinimize?.();
2641
2774
  }, [onMinimize]);
2642
- const handleRestore = useCallback9(() => {
2775
+ const handleRestore = useCallback11(() => {
2643
2776
  setIsMinimized(false);
2644
2777
  onRestore?.();
2645
2778
  }, [onRestore]);
2646
- const handleCopy = useCallback9((content) => {
2779
+ const handleCopy = useCallback11((content) => {
2647
2780
  const message = messages.find((m) => m.content === content);
2648
2781
  if (message) {
2649
2782
  onMessageCopy?.(message);
2650
2783
  }
2651
2784
  }, [messages, onMessageCopy]);
2652
- const containerStyle = useMemo6(() => ({
2785
+ const containerStyle = useMemo5(() => ({
2653
2786
  height,
2654
2787
  width,
2655
2788
  ...style
2656
2789
  }), [height, width, style]);
2790
+ const renderSystemMessage = (message) => {
2791
+ return /* @__PURE__ */ jsx23("div", { className: "system-message-container", children: /* @__PURE__ */ jsx23(
2792
+ SmartMessage_default,
2793
+ {
2794
+ id: message.id,
2795
+ content: message.content,
2796
+ role: message.type,
2797
+ timestamp: message.timestamp,
2798
+ status: message.status,
2799
+ toolName: message.toolName,
2800
+ toolResult: message.toolResult,
2801
+ onCopy: handleCopy,
2802
+ className: bubbleClassName
2803
+ }
2804
+ ) }, message.id);
2805
+ };
2806
+ const renderUserAssistantMessage = (message) => {
2807
+ const isUser = message.type === "user";
2808
+ const showCopyButton = message.type === "user" || message.type === "assistant";
2809
+ return /* @__PURE__ */ jsxs17(
2810
+ "div",
2811
+ {
2812
+ className: `message-wrapper message-wrapper--${isUser ? "user" : "assistant"}`,
2813
+ children: [
2814
+ /* @__PURE__ */ jsx23(
2815
+ SmartMessage_default,
2816
+ {
2817
+ id: message.id,
2818
+ content: message.content,
2819
+ role: message.type,
2820
+ timestamp: message.timestamp,
2821
+ status: message.status,
2822
+ toolName: message.toolName,
2823
+ toolResult: message.toolResult,
2824
+ onCopy: handleCopy,
2825
+ className: bubbleClassName
2826
+ }
2827
+ ),
2828
+ showCopyButton && /* @__PURE__ */ jsx23("div", { className: `message-copy-wrapper message-copy-wrapper--${isUser ? "user" : "assistant"}`, children: /* @__PURE__ */ jsx23(
2829
+ "button",
2830
+ {
2831
+ className: "message-copy-btn",
2832
+ onClick: () => handleCopy(message.content),
2833
+ title: "\u590D\u5236\u5185\u5BB9",
2834
+ children: /* @__PURE__ */ jsx23("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23(
2835
+ "path",
2836
+ {
2837
+ d: "M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM19 5H8C6.9 5 6 5.9 6 7V21C6 22.1 6.9 23 8 23H19C20.1 23 21 22.1 21 21V7C21 5.9 20.1 5 19 5ZM19 21H8V7H19V21Z",
2838
+ fill: "currentColor"
2839
+ }
2840
+ ) })
2841
+ }
2842
+ ) })
2843
+ ]
2844
+ },
2845
+ message.id
2846
+ );
2847
+ };
2848
+ const renderToolMessage = (message) => {
2849
+ return /* @__PURE__ */ jsx23("div", { className: "tool-message-container", children: /* @__PURE__ */ jsx23(
2850
+ SmartMessage_default,
2851
+ {
2852
+ id: message.id,
2853
+ content: message.content,
2854
+ role: message.type,
2855
+ timestamp: message.timestamp,
2856
+ status: message.status,
2857
+ toolName: message.toolName,
2858
+ toolResult: message.toolResult,
2859
+ onCopy: handleCopy,
2860
+ className: bubbleClassName
2861
+ }
2862
+ ) }, message.id);
2863
+ };
2864
+ const renderMessage = (message) => {
2865
+ switch (message.type) {
2866
+ case "system":
2867
+ return renderSystemMessage(message);
2868
+ case "tool":
2869
+ return renderToolMessage(message);
2870
+ default:
2871
+ return renderUserAssistantMessage(message);
2872
+ }
2873
+ };
2657
2874
  if (isMinimized) {
2658
2875
  return /* @__PURE__ */ jsx23("div", { className: `chat-container-minimized ${className}`, style: containerStyle, children: /* @__PURE__ */ jsx23("button", { className: "restore-btn-only", onClick: handleRestore, title: "\u5C55\u5F00", children: /* @__PURE__ */ jsx23("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23("path", { d: "M12 5V19M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }) });
2659
2876
  }
@@ -2669,21 +2886,7 @@ var Chat = ({
2669
2886
  ] })
2670
2887
  ] }),
2671
2888
  /* @__PURE__ */ jsxs17("div", { className: `chat-messages ${messagesClassName}`, ref: messagesContainerRef, style: messagesStyle, children: [
2672
- messages.map((message) => /* @__PURE__ */ jsx23(
2673
- SmartMessage_default,
2674
- {
2675
- id: message.id,
2676
- content: message.content,
2677
- role: message.type,
2678
- timestamp: message.timestamp,
2679
- status: message.status,
2680
- toolName: message.toolName,
2681
- toolResult: message.toolResult,
2682
- onCopy: handleCopy,
2683
- className: bubbleClassName
2684
- },
2685
- message.id
2686
- )),
2889
+ messages.map((message) => renderMessage(message)),
2687
2890
  loading && /* @__PURE__ */ jsx23("div", { className: "typing-wrapper", children: /* @__PURE__ */ jsxs17("div", { className: "typing-indicator", children: [
2688
2891
  /* @__PURE__ */ jsx23("span", {}),
2689
2892
  /* @__PURE__ */ jsx23("span", {}),
@@ -2749,6 +2952,290 @@ var Chat = ({
2749
2952
  ] })
2750
2953
  ] });
2751
2954
  };
2955
+
2956
+ // src/components/Relation/Relation.tsx
2957
+ import { useCallback as useCallback12, useEffect as useEffect13 } from "react";
2958
+ import {
2959
+ ReactFlow,
2960
+ useNodesState,
2961
+ useEdgesState,
2962
+ addEdge,
2963
+ Handle,
2964
+ Position,
2965
+ MarkerType,
2966
+ ConnectionLineType,
2967
+ useReactFlow,
2968
+ ReactFlowProvider
2969
+ } from "@xyflow/react";
2970
+ import "@xyflow/react/dist/style.css";
2971
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
2972
+ var getIconEmoji = (iconType) => {
2973
+ const map = {
2974
+ war: "\u2694\uFE0F",
2975
+ target: "\u{1F3AF}",
2976
+ star: "\u2B50",
2977
+ award: "\u{1F3C6}",
2978
+ flag: "\u{1F6A9}",
2979
+ default: "\u{1F465}",
2980
+ user: "\u{1F464}",
2981
+ shield: "\u{1F6E1}\uFE0F"
2982
+ };
2983
+ return map[iconType || "default"] || "\u{1F465}";
2984
+ };
2985
+ var getStatusColor = (status) => {
2986
+ const map = {
2987
+ active: "#10b981",
2988
+ inactive: "#6b7280",
2989
+ warning: "#f59e0b"
2990
+ };
2991
+ return map[status || "active"];
2992
+ };
2993
+ var CustomNode = ({ data, theme = "light" }) => {
2994
+ const isDark = theme === "dark";
2995
+ const nodeStyle = {
2996
+ background: isDark ? "linear-gradient(135deg, #1e293b 0%, #0f172a 100%)" : "linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)",
2997
+ border: `2px solid ${isDark ? "rgba(255, 255, 255, 0.12)" : "rgba(0, 0, 0, 0.08)"}`,
2998
+ borderRadius: "14px",
2999
+ boxShadow: isDark ? "0 8px 20px rgba(0, 0, 0, 0.25)" : "0 4px 12px rgba(0, 0, 0, 0.08)",
3000
+ minWidth: "240px",
3001
+ cursor: "pointer",
3002
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
3003
+ };
3004
+ const iconStyle = {
3005
+ width: "52px",
3006
+ height: "52px",
3007
+ borderRadius: "14px",
3008
+ display: "flex",
3009
+ alignItems: "center",
3010
+ justifyContent: "center",
3011
+ background: isDark ? "rgba(255, 255, 255, 0.08)" : "rgba(0, 0, 0, 0.04)",
3012
+ transition: "all 0.3s ease"
3013
+ };
3014
+ const titleStyle = {
3015
+ fontWeight: 700,
3016
+ fontSize: "15px",
3017
+ marginBottom: "4px",
3018
+ whiteSpace: "nowrap",
3019
+ overflow: "hidden",
3020
+ textOverflow: "ellipsis",
3021
+ color: isDark ? "#ffffff" : "#1e293b"
3022
+ };
3023
+ const subtitleStyle = {
3024
+ fontSize: "11px",
3025
+ fontWeight: 500,
3026
+ marginBottom: "2px",
3027
+ color: isDark ? "rgba(255, 255, 255, 0.7)" : "#64748b"
3028
+ };
3029
+ const descriptionStyle = {
3030
+ fontSize: "10px",
3031
+ marginTop: "6px",
3032
+ whiteSpace: "nowrap",
3033
+ overflow: "hidden",
3034
+ textOverflow: "ellipsis",
3035
+ color: isDark ? "rgba(255, 255, 255, 0.45)" : "#94a3b8"
3036
+ };
3037
+ return /* @__PURE__ */ jsxs18("div", { style: nodeStyle, children: [
3038
+ /* @__PURE__ */ jsx24(Handle, { type: "target", position: Position.Top, isConnectable: true }),
3039
+ /* @__PURE__ */ jsxs18("div", { style: { display: "flex", alignItems: "center", gap: "14px", padding: "14px 18px" }, children: [
3040
+ /* @__PURE__ */ jsxs18("div", { style: { position: "relative", flexShrink: 0 }, children: [
3041
+ /* @__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) }) }),
3042
+ data.status && /* @__PURE__ */ jsx24("div", { style: {
3043
+ position: "absolute",
3044
+ bottom: "-2px",
3045
+ right: "-2px",
3046
+ width: "12px",
3047
+ height: "12px",
3048
+ borderRadius: "50%",
3049
+ border: `2px solid ${isDark ? "#1e293b" : "#ffffff"}`,
3050
+ backgroundColor: getStatusColor(data.status)
3051
+ } })
3052
+ ] }),
3053
+ /* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: 0 }, children: [
3054
+ /* @__PURE__ */ jsx24("div", { style: titleStyle, children: data.title }),
3055
+ /* @__PURE__ */ jsx24("div", { style: subtitleStyle, children: data.subtitle }),
3056
+ data.description && /* @__PURE__ */ jsx24("div", { style: descriptionStyle, children: data.description })
3057
+ ] })
3058
+ ] }),
3059
+ /* @__PURE__ */ jsx24(Handle, { type: "source", position: Position.Bottom, isConnectable: true })
3060
+ ] });
3061
+ };
3062
+ var nodeTypes = {
3063
+ custom: CustomNode
3064
+ };
3065
+ var RelationContent = ({
3066
+ nodes: propNodes,
3067
+ edges: propEdges,
3068
+ onNodeClick,
3069
+ onNodeDoubleClick,
3070
+ onEdgeClick,
3071
+ onConnect: onConnectProp,
3072
+ onNodesChange: onNodesChangeProp,
3073
+ onEdgesChange: onEdgesChangeProp,
3074
+ fitView = true,
3075
+ fitViewOptions,
3076
+ className = "",
3077
+ style = {},
3078
+ height = "70vh",
3079
+ width = "100%",
3080
+ defaultViewport = { x: 0, y: 0, zoom: 1 },
3081
+ minZoom = 0.5,
3082
+ maxZoom = 2,
3083
+ snapToGrid = false,
3084
+ snapGrid = [15, 15],
3085
+ enableEdgeCreation = true,
3086
+ enableNodeDrag = true,
3087
+ onNodeContextMenu,
3088
+ theme: propTheme = "light"
3089
+ }) => {
3090
+ const [nodes, , onNodesChange] = useNodesState(propNodes || []);
3091
+ const [edges, setEdges, onEdgesChange] = useEdgesState(propEdges || []);
3092
+ const { fitView: fitViewFn } = useReactFlow();
3093
+ const theme = propTheme;
3094
+ const isDark = theme === "dark";
3095
+ const bgColor = isDark ? "#0f172a" : "#f5f7fa";
3096
+ useEffect13(() => {
3097
+ if (fitView && fitViewFn && (propNodes?.length || 0) > 0) {
3098
+ setTimeout(() => {
3099
+ fitViewFn({ duration: 300, ...fitViewOptions }).catch((error) => {
3100
+ console.warn("Fit view failed:", error);
3101
+ });
3102
+ }, 100);
3103
+ }
3104
+ }, [fitView, fitViewFn, fitViewOptions, propNodes]);
3105
+ const handleNodesChange = useCallback12(
3106
+ (changes) => {
3107
+ onNodesChange(changes);
3108
+ if (onNodesChangeProp) {
3109
+ onNodesChangeProp(nodes);
3110
+ }
3111
+ },
3112
+ [onNodesChange, onNodesChangeProp, nodes]
3113
+ );
3114
+ const handleEdgesChange = useCallback12(
3115
+ (changes) => {
3116
+ onEdgesChange(changes);
3117
+ if (onEdgesChangeProp) {
3118
+ onEdgesChangeProp(edges);
3119
+ }
3120
+ },
3121
+ [onEdgesChange, onEdgesChangeProp, edges]
3122
+ );
3123
+ const onConnect = useCallback12(
3124
+ (params) => {
3125
+ const newEdge = {
3126
+ ...params,
3127
+ id: `edge-${Date.now()}-${Math.random()}`,
3128
+ type: "smoothstep",
3129
+ style: { stroke: "#ff6b6b", strokeWidth: 2 },
3130
+ markerEnd: { type: MarkerType.ArrowClosed, color: "#ff6b6b" },
3131
+ label: "\u65B0\u8FDE\u63A5"
3132
+ };
3133
+ setEdges((eds) => addEdge(newEdge, eds));
3134
+ if (onConnectProp) {
3135
+ onConnectProp(params);
3136
+ }
3137
+ },
3138
+ [setEdges, onConnectProp]
3139
+ );
3140
+ const handleNodeClick = useCallback12(
3141
+ (_event, node) => {
3142
+ if (onNodeClick && node.data) {
3143
+ onNodeClick(node.id, node.data, _event);
3144
+ }
3145
+ },
3146
+ [onNodeClick]
3147
+ );
3148
+ const handleNodeDoubleClick = useCallback12(
3149
+ (_event, node) => {
3150
+ if (onNodeDoubleClick && node.data) {
3151
+ onNodeDoubleClick(node.id, node.data);
3152
+ }
3153
+ },
3154
+ [onNodeDoubleClick]
3155
+ );
3156
+ const handleNodeContextMenu = useCallback12(
3157
+ (event, node) => {
3158
+ event.preventDefault();
3159
+ if (onNodeContextMenu && node.data) {
3160
+ onNodeContextMenu(node.id, node.data);
3161
+ }
3162
+ },
3163
+ [onNodeContextMenu]
3164
+ );
3165
+ const handleEdgeClick = useCallback12(
3166
+ (_event, edge) => {
3167
+ if (onEdgeClick) {
3168
+ onEdgeClick(edge.id, edge.data);
3169
+ }
3170
+ },
3171
+ [onEdgeClick]
3172
+ );
3173
+ const nodesWithTheme = nodes.map((node) => ({
3174
+ ...node,
3175
+ data: { ...node.data, theme }
3176
+ }));
3177
+ const globalStyle = `
3178
+ .react-flow__background,
3179
+ .react-flow__pane,
3180
+ .react-flow__renderer,
3181
+ .react-flow__viewport {
3182
+ background-color: ${bgColor} !important;
3183
+ }
3184
+ `;
3185
+ return /* @__PURE__ */ jsxs18(
3186
+ "div",
3187
+ {
3188
+ className: `relation-container ${className}`,
3189
+ style: {
3190
+ width,
3191
+ height,
3192
+ ...style,
3193
+ position: "relative",
3194
+ overflow: "hidden",
3195
+ backgroundColor: bgColor
3196
+ },
3197
+ children: [
3198
+ /* @__PURE__ */ jsx24("style", { children: globalStyle }),
3199
+ /* @__PURE__ */ jsx24(
3200
+ ReactFlow,
3201
+ {
3202
+ nodes: nodesWithTheme,
3203
+ edges,
3204
+ onNodesChange: handleNodesChange,
3205
+ onEdgesChange: handleEdgesChange,
3206
+ onConnect: enableEdgeCreation ? onConnect : void 0,
3207
+ onNodeClick: handleNodeClick,
3208
+ onNodeDoubleClick: handleNodeDoubleClick,
3209
+ onNodeContextMenu: handleNodeContextMenu,
3210
+ onEdgeClick: handleEdgeClick,
3211
+ nodeTypes,
3212
+ fitView: false,
3213
+ fitViewOptions,
3214
+ defaultViewport,
3215
+ minZoom,
3216
+ maxZoom,
3217
+ snapToGrid,
3218
+ snapGrid,
3219
+ nodesDraggable: enableNodeDrag,
3220
+ nodesConnectable: enableEdgeCreation,
3221
+ connectionLineType: ConnectionLineType.SmoothStep,
3222
+ connectionLineStyle: { stroke: "#ff6b6b", strokeWidth: 2 },
3223
+ attributionPosition: "bottom-right",
3224
+ zoomOnScroll: true,
3225
+ zoomOnPinch: true,
3226
+ zoomOnDoubleClick: false,
3227
+ panOnScroll: false,
3228
+ panOnDrag: true,
3229
+ proOptions: { hideAttribution: true }
3230
+ }
3231
+ )
3232
+ ]
3233
+ }
3234
+ );
3235
+ };
3236
+ var Relation = (props) => {
3237
+ return /* @__PURE__ */ jsx24(ReactFlowProvider, { children: /* @__PURE__ */ jsx24(RelationContent, { ...props }) });
3238
+ };
2752
3239
  export {
2753
3240
  AlertProvider,
2754
3241
  Avatar,
@@ -2765,6 +3252,7 @@ export {
2765
3252
  ListGroupItem,
2766
3253
  MarkdownEditor,
2767
3254
  OrphanDialog,
3255
+ Relation,
2768
3256
  RollingBox,
2769
3257
  Select,
2770
3258
  SideBar,