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.css +378 -483
- package/dist/index.d.ts +90 -16
- package/dist/index.js +828 -340
- package/package.json +41 -42
- package/dist/index.cjs +0 -2837
- package/dist/index.d.cts +0 -602
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
|
-
|
|
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 ||
|
|
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 =
|
|
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
|
|
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 = (
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
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 ||
|
|
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
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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
|
-
|
|
728
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1563
|
-
const
|
|
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
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
2086
|
-
const
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2275
|
+
const handleDragOver = useCallback9((e) => {
|
|
2107
2276
|
e.preventDefault();
|
|
2108
2277
|
e.dataTransfer.dropEffect = "move";
|
|
2109
2278
|
}, []);
|
|
2110
|
-
const handleDrop =
|
|
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 =
|
|
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
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
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
|
|
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] =
|
|
2185
|
-
const [draft, setDraft] =
|
|
2186
|
-
const inputRef =
|
|
2187
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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] =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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] =
|
|
2474
|
-
const
|
|
2475
|
-
const handleCopy = async () => {
|
|
2663
|
+
const [copied, setCopied] = useState17(false);
|
|
2664
|
+
const handleCopy = useCallback10(async () => {
|
|
2476
2665
|
try {
|
|
2477
|
-
|
|
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
|
|
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
|
|
2568
|
-
|
|
2569
|
-
|
|
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
|
|
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__ */
|
|
2575
|
-
})
|
|
2576
|
-
|
|
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 = "
|
|
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] =
|
|
2612
|
-
const [isMinimized, setIsMinimized] =
|
|
2613
|
-
const messagesContainerRef =
|
|
2614
|
-
const messagesEndRef =
|
|
2615
|
-
const historyPanelRef =
|
|
2616
|
-
const currentConversation =
|
|
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
|
-
|
|
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
|
-
|
|
2627
|
-
|
|
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 =
|
|
2771
|
+
const handleMinimize = useCallback11(() => {
|
|
2639
2772
|
setIsMinimized(true);
|
|
2640
2773
|
onMinimize?.();
|
|
2641
2774
|
}, [onMinimize]);
|
|
2642
|
-
const handleRestore =
|
|
2775
|
+
const handleRestore = useCallback11(() => {
|
|
2643
2776
|
setIsMinimized(false);
|
|
2644
2777
|
onRestore?.();
|
|
2645
2778
|
}, [onRestore]);
|
|
2646
|
-
const handleCopy =
|
|
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 =
|
|
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) =>
|
|
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,
|