flowcloudai-ui 0.0.0 → 0.1.1

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.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,37 +17,2821 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
23
- Button: () => Button
33
+ AlertProvider: () => AlertProvider,
34
+ Avatar: () => Avatar,
35
+ Button: () => Button,
36
+ ButtonGroup: () => ButtonGroup,
37
+ ButtonToolbar: () => ButtonToolbar,
38
+ Card: () => Card,
39
+ Chat: () => Chat,
40
+ CheckButton: () => CheckButton,
41
+ ContextMenuProvider: () => ContextMenuProvider,
42
+ DeleteDialog: () => DeleteDialog,
43
+ Input: () => Input,
44
+ ListGroup: () => ListGroup,
45
+ ListGroupItem: () => ListGroupItem,
46
+ MarkdownEditor: () => MarkdownEditor,
47
+ OrphanDialog: () => OrphanDialog,
48
+ RollingBox: () => RollingBox,
49
+ Select: () => Select,
50
+ SideBar: () => SideBar,
51
+ Slider: () => Slider,
52
+ SmartMessage: () => SmartMessage,
53
+ TabBar: () => TabBar,
54
+ TagItem: () => TagItem,
55
+ ThemeProvider: () => ThemeProvider,
56
+ Tree: () => Tree,
57
+ VirtualList: () => VirtualList,
58
+ findNodeInfo: () => findNodeInfo,
59
+ flatToTree: () => flatToTree,
60
+ isDescendantOf: () => isDescendantOf,
61
+ lazyLoad: () => lazyLoad,
62
+ useAlert: () => useAlert,
63
+ useContextMenu: () => useContextMenu,
64
+ useTheme: () => useTheme
24
65
  });
25
66
  module.exports = __toCommonJS(index_exports);
26
67
 
27
- // src/Button.tsx
68
+ // src/ThemeProvider.tsx
69
+ var import_react = require("react");
28
70
  var import_jsx_runtime = require("react/jsx-runtime");
29
- function Button({ children, style, ...props }) {
30
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
71
+ var ThemeContext = (0, import_react.createContext)(null);
72
+ function useTheme() {
73
+ const ctx = (0, import_react.useContext)(ThemeContext);
74
+ if (!ctx) throw new Error("useTheme must be used within <ThemeProvider>");
75
+ return ctx;
76
+ }
77
+ function getSystemTheme() {
78
+ return typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
79
+ }
80
+ function ThemeProvider({ children, defaultTheme = "system", target }) {
81
+ const [theme, setTheme] = (0, import_react.useState)(defaultTheme);
82
+ const [systemTheme, setSystemTheme] = (0, import_react.useState)(getSystemTheme);
83
+ const resolvedTheme = theme === "system" ? systemTheme : theme;
84
+ (0, import_react.useEffect)(() => {
85
+ const el = target ?? document.documentElement;
86
+ el.classList.remove("theme-light", "theme-dark");
87
+ el.classList.add(`theme-${resolvedTheme}`);
88
+ el.setAttribute("data-theme", resolvedTheme);
89
+ if (resolvedTheme === "dark") {
90
+ document.body.style.backgroundColor = "#0F0F0F";
91
+ document.body.style.color = "#E8E8E6";
92
+ } else {
93
+ document.body.style.backgroundColor = "";
94
+ document.body.style.color = "";
95
+ }
96
+ }, [resolvedTheme, target]);
97
+ (0, import_react.useEffect)(() => {
98
+ if (theme !== "system") return;
99
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
100
+ const handler = () => setSystemTheme(mq.matches ? "dark" : "light");
101
+ mq.addEventListener("change", handler);
102
+ return () => mq.removeEventListener("change", handler);
103
+ }, [theme]);
104
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: { theme, resolvedTheme, setTheme }, children });
105
+ }
106
+
107
+ // src/components/Button/Button.tsx
108
+ var import_jsx_runtime2 = require("react/jsx-runtime");
109
+ function Button({
110
+ variant = "primary",
111
+ size = "md",
112
+ disabled = false,
113
+ loading = false,
114
+ block = false,
115
+ circle = false,
116
+ iconOnly = false,
117
+ iconLeft,
118
+ iconRight,
119
+ background,
120
+ hoverBackground,
121
+ activeBackground,
122
+ color,
123
+ hoverColor,
124
+ activeColor,
125
+ borderColor,
126
+ hoverBorderColor,
127
+ className,
128
+ style,
129
+ children,
130
+ ...props
131
+ }) {
132
+ const colorVars = {
133
+ "--btn-bg": background,
134
+ "--btn-bg-hover": hoverBackground,
135
+ "--btn-bg-active": activeBackground,
136
+ "--btn-color": color,
137
+ "--btn-color-hover": hoverColor,
138
+ "--btn-color-active": activeColor,
139
+ "--btn-border": borderColor,
140
+ "--btn-border-hover": hoverBorderColor
141
+ };
142
+ const overrideStyle = {};
143
+ for (const [key, value] of Object.entries(colorVars)) {
144
+ if (value !== void 0) {
145
+ overrideStyle[key] = value;
146
+ }
147
+ }
148
+ const mergedStyle = {
149
+ ...overrideStyle,
150
+ ...style
151
+ };
152
+ const classNames = [
153
+ "fc-btn",
154
+ `fc-btn--${variant}`,
155
+ `fc-btn--${size}`,
156
+ block && "fc-btn--block",
157
+ circle && "fc-btn--circle",
158
+ iconOnly && "fc-btn--icon-only",
159
+ loading && "is-loading",
160
+ disabled && "is-disabled",
161
+ className
162
+ ].filter(Boolean).join(" ");
163
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
31
164
  "button",
32
165
  {
166
+ className: classNames,
167
+ disabled: disabled || loading,
168
+ style: mergedStyle,
169
+ ...props,
170
+ children: [
171
+ iconLeft && !loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "fc-btn__icon fc-btn__icon--left", children: iconLeft }),
172
+ children,
173
+ iconRight && !loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "fc-btn__icon fc-btn__icon--right", children: iconRight })
174
+ ]
175
+ }
176
+ );
177
+ }
178
+ function ButtonGroup({ children, className, ...props }) {
179
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `fc-btn-group ${className ?? ""}`, ...props, children });
180
+ }
181
+ function ButtonToolbar({
182
+ children,
183
+ align = "left",
184
+ className,
185
+ ...props
186
+ }) {
187
+ const alignClass = {
188
+ left: "",
189
+ center: "fc-btn-toolbar--center",
190
+ right: "fc-btn-toolbar--right",
191
+ between: "fc-btn-toolbar--between"
192
+ }[align];
193
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `fc-btn-toolbar ${alignClass} ${className ?? ""}`, ...props, children });
194
+ }
195
+
196
+ // src/components/Button/CheckButton.tsx
197
+ var React = __toESM(require("react"), 1);
198
+ var import_jsx_runtime3 = require("react/jsx-runtime");
199
+ function CheckButton({
200
+ checked: controlledChecked,
201
+ defaultChecked = false,
202
+ onChange,
203
+ disabled = false,
204
+ size = "md",
205
+ labelLeft,
206
+ labelRight,
207
+ trackBackground,
208
+ checkedTrackBackground,
209
+ thumbBackground,
210
+ thumbDotColor,
211
+ labelColor,
212
+ className = "",
213
+ style
214
+ }) {
215
+ const isControlled = controlledChecked !== void 0;
216
+ const [internalChecked, setInternalChecked] = React.useState(defaultChecked);
217
+ const checked = isControlled ? controlledChecked : internalChecked;
218
+ const toggle = () => {
219
+ if (disabled) return;
220
+ const next = !checked;
221
+ if (!isControlled) setInternalChecked(next);
222
+ onChange?.(next);
223
+ };
224
+ const handleKeyDown = (e) => {
225
+ if (e.key === "Enter" || e.key === " ") {
226
+ e.preventDefault();
227
+ toggle();
228
+ }
229
+ };
230
+ const colorVars = {
231
+ "--check-track-bg": trackBackground,
232
+ "--check-track-bg-checked": checkedTrackBackground,
233
+ "--check-thumb-bg": thumbBackground,
234
+ "--check-thumb-dot-color": thumbDotColor,
235
+ "--check-label-color": labelColor
236
+ };
237
+ const overrideStyle = {};
238
+ for (const [key, value] of Object.entries(colorVars)) {
239
+ if (value !== void 0) {
240
+ overrideStyle[key] = value;
241
+ }
242
+ }
243
+ const mergedStyle = {
244
+ ...overrideStyle,
245
+ ...style
246
+ };
247
+ const cls = [
248
+ "fc-check",
249
+ `fc-check--${size}`,
250
+ checked && "fc-check--checked",
251
+ disabled && "fc-check--disabled",
252
+ className
253
+ ].filter(Boolean).join(" ");
254
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
255
+ "div",
256
+ {
257
+ className: cls,
258
+ style: mergedStyle,
259
+ role: "switch",
260
+ "aria-checked": checked,
261
+ tabIndex: disabled ? -1 : 0,
262
+ onClick: toggle,
263
+ onKeyDown: handleKeyDown,
264
+ children: [
265
+ labelLeft && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__label", children: labelLeft }),
266
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__track", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__thumb", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__thumb-inner" }) }) }),
267
+ labelRight && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "fc-check__label", children: labelRight })
268
+ ]
269
+ }
270
+ );
271
+ }
272
+
273
+ // src/components/Box/RollingBox.tsx
274
+ var React2 = __toESM(require("react"), 1);
275
+ var import_jsx_runtime4 = require("react/jsx-runtime");
276
+ function RollingBox({
277
+ showThumb = "auto",
278
+ horizontal = false,
279
+ vertical = true,
280
+ thumbSize = "normal",
281
+ showTrack = false,
282
+ children,
283
+ className,
284
+ thumbColor,
285
+ thumbHoverColor,
286
+ thumbActiveColor,
287
+ trackColor,
288
+ style,
289
+ ...props
290
+ }) {
291
+ const colorVars = {
292
+ "--roll-thumb": thumbColor,
293
+ "--roll-thumb-hover": thumbHoverColor,
294
+ "--roll-thumb-active": thumbActiveColor,
295
+ "--roll-track": trackColor
296
+ };
297
+ const overrideStyle = {};
298
+ for (const [key, value] of Object.entries(colorVars)) {
299
+ if (value !== void 0) {
300
+ overrideStyle[key] = value;
301
+ }
302
+ }
303
+ const mergedStyle = { ...overrideStyle, ...style };
304
+ const containerRef = React2.useRef(null);
305
+ const [isScrolling, setIsScrolling] = React2.useState(false);
306
+ const scrollTimeoutRef = React2.useRef(null);
307
+ const handleScroll = React2.useCallback(() => {
308
+ if (showThumb !== "auto") return;
309
+ setIsScrolling(true);
310
+ if (scrollTimeoutRef.current) {
311
+ clearTimeout(scrollTimeoutRef.current);
312
+ }
313
+ scrollTimeoutRef.current = setTimeout(() => {
314
+ setIsScrolling(false);
315
+ }, 1e3);
316
+ }, [showThumb]);
317
+ React2.useEffect(() => {
318
+ return () => {
319
+ if (scrollTimeoutRef.current) {
320
+ clearTimeout(scrollTimeoutRef.current);
321
+ }
322
+ };
323
+ }, []);
324
+ const resolvedDirection = horizontal ? "horizontal" : "vertical";
325
+ const classNames = [
326
+ "fc-roll",
327
+ `fc-roll--thumb-${showThumb}`,
328
+ `fc-roll--size-${thumbSize}`,
329
+ `fc-roll--${resolvedDirection}`,
330
+ showTrack && "fc-roll--track",
331
+ isScrolling && "fc-roll--scrolling",
332
+ className
333
+ ].filter(Boolean).join(" ");
334
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
335
+ "div",
336
+ {
337
+ ref: containerRef,
338
+ className: classNames,
339
+ style: mergedStyle,
340
+ onScroll: handleScroll,
33
341
  ...props,
34
- style: {
35
- padding: "10px 16px",
36
- border: "none",
37
- borderRadius: 10,
38
- background: "#863bff",
39
- color: "#fff",
40
- cursor: "pointer",
41
- fontSize: 14,
42
- ...style
342
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "fc-roll__content", children })
343
+ }
344
+ );
345
+ }
346
+
347
+ // src/components/Bar/SideBar.tsx
348
+ var import_react2 = require("react");
349
+ var import_jsx_runtime5 = require("react/jsx-runtime");
350
+ var SideBarItemView = (0, import_react2.memo)(({ item, isSelected, onClick }) => {
351
+ const classes = [
352
+ "fc-sidebar__item",
353
+ isSelected && "fc-sidebar__item--selected",
354
+ item.disabled && "fc-sidebar__item--disabled"
355
+ ].filter(Boolean).join(" ");
356
+ const Tag = item.href ? "a" : "div";
357
+ const linkProps = item.href ? { href: item.href } : {};
358
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
359
+ Tag,
360
+ {
361
+ className: classes,
362
+ onClick: () => !item.disabled && onClick(item.key),
363
+ ...linkProps,
364
+ children: [
365
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "fc-sidebar__icon", children: item.icon }),
366
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "fc-sidebar__label", children: item.label })
367
+ ]
368
+ }
369
+ );
370
+ });
371
+ SideBarItemView.displayName = "SideBarItemView";
372
+ var SideBar = (0, import_react2.memo)(({
373
+ items,
374
+ selectedKey,
375
+ collapsed,
376
+ width = 240,
377
+ collapsedWidth = 64,
378
+ onSelect,
379
+ onCollapse,
380
+ className = "",
381
+ style
382
+ }) => {
383
+ const handleClick = (0, import_react2.useCallback)(
384
+ (key) => onSelect(key),
385
+ [onSelect]
386
+ );
387
+ const toggleCollapse = (0, import_react2.useCallback)(
388
+ () => onCollapse(!collapsed),
389
+ [collapsed, onCollapse]
390
+ );
391
+ const rootClasses = [
392
+ "fc-sidebar",
393
+ collapsed && "fc-sidebar--collapsed",
394
+ className
395
+ ].filter(Boolean).join(" ");
396
+ const rootStyle = {
397
+ "--sidebar-width": `${collapsed ? collapsedWidth : width}px`,
398
+ "--sidebar-collapsed-width": `${collapsedWidth}px`,
399
+ ...style
400
+ };
401
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("aside", { className: rootClasses, style: rootStyle, children: [
402
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "fc-sidebar__header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
403
+ "button",
404
+ {
405
+ className: "fc-sidebar__collapse-btn",
406
+ onClick: toggleCollapse,
407
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
408
+ "svg",
409
+ {
410
+ className: "fc-sidebar__collapse-icon",
411
+ width: "16",
412
+ height: "16",
413
+ viewBox: "0 0 16 16",
414
+ fill: "none",
415
+ xmlns: "http://www.w3.org/2000/svg",
416
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
417
+ "path",
418
+ {
419
+ d: collapsed ? "M6 3L11 8L6 13" : "M10 3L5 8L10 13",
420
+ stroke: "currentColor",
421
+ strokeWidth: "1.5",
422
+ strokeLinecap: "round",
423
+ strokeLinejoin: "round"
424
+ }
425
+ )
426
+ }
427
+ )
428
+ }
429
+ ) }),
430
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("nav", { className: "fc-sidebar__menu", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
431
+ SideBarItemView,
432
+ {
433
+ item,
434
+ isSelected: selectedKey === item.key,
435
+ onClick: handleClick
43
436
  },
44
- children
437
+ item.key
438
+ )) })
439
+ ] });
440
+ });
441
+ SideBar.displayName = "SideBar";
442
+
443
+ // src/components/Input/Input.tsx
444
+ var React4 = __toESM(require("react"), 1);
445
+ var import_jsx_runtime6 = require("react/jsx-runtime");
446
+ var Input = React4.forwardRef(({
447
+ size = "md",
448
+ status = "default",
449
+ prefix,
450
+ suffix,
451
+ allowClear = false,
452
+ passwordToggle = false,
453
+ addonBefore,
454
+ addonAfter,
455
+ helperText,
456
+ className = "",
457
+ type: initialType = "text",
458
+ value,
459
+ defaultValue,
460
+ onChange,
461
+ onClear,
462
+ disabled,
463
+ ...props
464
+ }, ref) => {
465
+ const [type, setType] = React4.useState(initialType);
466
+ const [internalValue, setInternalValue] = React4.useState(defaultValue || "");
467
+ const isControlled = value !== void 0;
468
+ const currentValue = isControlled ? value : internalValue;
469
+ const handleChange = (e) => {
470
+ if (!isControlled) setInternalValue(e.target.value);
471
+ onChange?.(e);
472
+ };
473
+ const handleClear = () => {
474
+ if (!isControlled) setInternalValue("");
475
+ onClear?.();
476
+ const event = { target: { value: "" } };
477
+ onChange?.(event);
478
+ };
479
+ const togglePassword = () => {
480
+ setType((prev) => prev === "password" ? "text" : "password");
481
+ };
482
+ const showClear = allowClear && currentValue && !disabled;
483
+ const showPasswordToggle = passwordToggle && initialType === "password" && !disabled;
484
+ const classNames = [
485
+ "fc-input",
486
+ `fc-input--${size}`,
487
+ `fc-input--${status}`,
488
+ (prefix || addonBefore) && "fc-input--has-prefix",
489
+ (suffix || addonAfter || showClear || showPasswordToggle) && "fc-input--has-suffix",
490
+ addonBefore && "fc-input--addon-before",
491
+ addonAfter && "fc-input--addon-after",
492
+ disabled && "fc-input--disabled",
493
+ className
494
+ ].filter(Boolean).join(" ");
495
+ const input = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
496
+ "input",
497
+ {
498
+ ref,
499
+ type,
500
+ value: currentValue,
501
+ onChange: handleChange,
502
+ disabled,
503
+ className: "fc-input__field",
504
+ ...props
505
+ }
506
+ );
507
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: classNames, children: [
508
+ addonBefore && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fc-input__addon fc-input__addon--before", children: addonBefore }),
509
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "fc-input__wrapper", children: [
510
+ prefix && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fc-input__prefix", children: prefix }),
511
+ input,
512
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "fc-input__actions", children: [
513
+ showClear && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
514
+ "button",
515
+ {
516
+ type: "button",
517
+ className: "fc-input__action fc-input__clear",
518
+ onClick: handleClear,
519
+ tabIndex: -1,
520
+ children: "\u2715"
521
+ }
522
+ ),
523
+ showPasswordToggle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
524
+ "button",
525
+ {
526
+ type: "button",
527
+ className: "fc-input__action fc-input__eye",
528
+ onClick: togglePassword,
529
+ tabIndex: -1,
530
+ children: type === "password" ? "\u{1F441}" : "\u{1F648}"
531
+ }
532
+ ),
533
+ suffix && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fc-input__suffix", children: suffix })
534
+ ] })
535
+ ] }),
536
+ addonAfter && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "fc-input__addon fc-input__addon--after", children: addonAfter }),
537
+ helperText && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `fc-input__helper fc-input__helper--${status}`, children: helperText })
538
+ ] });
539
+ });
540
+ Input.displayName = "Input";
541
+
542
+ // src/components/Slider/Slider.tsx
543
+ var React5 = __toESM(require("react"), 1);
544
+ var import_jsx_runtime7 = require("react/jsx-runtime");
545
+ function Slider({
546
+ value: controlledValue,
547
+ defaultValue,
548
+ onChange,
549
+ min = 0,
550
+ max = 100,
551
+ step = 1,
552
+ range = false,
553
+ orientation = "horizontal",
554
+ disabled = false,
555
+ marks,
556
+ tooltip = false,
557
+ className = "",
558
+ style,
559
+ trackBackground,
560
+ fillBackground,
561
+ thumbBackground,
562
+ thumbBorderColor,
563
+ markDotColor,
564
+ markLabelColor,
565
+ tooltipBackground,
566
+ tooltipColor
567
+ }) {
568
+ const trackRef = React5.useRef(null);
569
+ const draggingRef = React5.useRef(null);
570
+ const [dragging, setDragging] = React5.useState(null);
571
+ const initialValue = defaultValue ?? (range ? [min, max] : min);
572
+ const [internalValue, setInternalValue] = React5.useState(initialValue);
573
+ const isControlled = controlledValue !== void 0;
574
+ const currentValue = isControlled ? controlledValue : internalValue;
575
+ const colorVars = {
576
+ "--slider-track-bg": trackBackground,
577
+ "--slider-fill-bg": fillBackground,
578
+ "--slider-thumb-bg": thumbBackground,
579
+ "--slider-thumb-border": thumbBorderColor,
580
+ "--slider-mark-dot-bg": markDotColor,
581
+ "--slider-mark-label-color": markLabelColor,
582
+ "--slider-tooltip-bg": tooltipBackground,
583
+ "--slider-tooltip-color": tooltipColor
584
+ };
585
+ const overrideStyle = {};
586
+ for (const [key, value] of Object.entries(colorVars)) {
587
+ if (value !== void 0) {
588
+ overrideStyle[key] = value;
589
+ }
590
+ }
591
+ const mergedStyle = { ...overrideStyle, ...style };
592
+ const getPercent = (val) => Math.max(0, Math.min(100, (val - min) / (max - min) * 100));
593
+ const getValueFromPercent = (percent) => {
594
+ const raw = min + percent / 100 * (max - min);
595
+ const stepped = Math.round(raw / step) * step;
596
+ return Math.max(min, Math.min(max, stepped));
597
+ };
598
+ const handleMove = React5.useCallback((clientX, clientY) => {
599
+ if (!trackRef.current || draggingRef.current === null || disabled) return;
600
+ const rect = trackRef.current.getBoundingClientRect();
601
+ const percent = orientation === "horizontal" ? (clientX - rect.left) / rect.width * 100 : (rect.bottom - clientY) / rect.height * 100;
602
+ const newValue = getValueFromPercent(Math.max(0, Math.min(100, percent)));
603
+ let nextValue;
604
+ if (range) {
605
+ const [start, end] = currentValue;
606
+ nextValue = draggingRef.current === 0 ? [Math.min(newValue, end), end] : [start, Math.max(newValue, start)];
607
+ } else {
608
+ nextValue = newValue;
609
+ }
610
+ if (!isControlled) setInternalValue(nextValue);
611
+ onChange?.(nextValue);
612
+ }, [disabled, orientation, range, currentValue, isControlled, onChange, min, max, step]);
613
+ const handleMouseDown = (index) => (e) => {
614
+ if (disabled) return;
615
+ e.preventDefault();
616
+ draggingRef.current = index;
617
+ setDragging(index);
618
+ const handleMouseMove = (e2) => handleMove(e2.clientX, e2.clientY);
619
+ const handleMouseUp = () => {
620
+ draggingRef.current = null;
621
+ setDragging(null);
622
+ document.removeEventListener("mousemove", handleMouseMove);
623
+ document.removeEventListener("mouseup", handleMouseUp);
624
+ };
625
+ document.addEventListener("mousemove", handleMouseMove);
626
+ document.addEventListener("mouseup", handleMouseUp);
627
+ };
628
+ const handleTrackClick = (e) => {
629
+ if (disabled || draggingRef.current !== null) return;
630
+ handleMove(e.clientX, e.clientY);
631
+ };
632
+ const [startVal, endVal] = range ? currentValue : [min, currentValue];
633
+ const startPercent = getPercent(startVal);
634
+ const endPercent = getPercent(endVal);
635
+ const isHorizontal = orientation === "horizontal";
636
+ const thumbStyle = (percent) => isHorizontal ? { left: `${percent}%` } : { bottom: `${percent}%` };
637
+ const cls = [
638
+ "fc-slider",
639
+ `fc-slider--${orientation}`,
640
+ range && "fc-slider--range",
641
+ disabled && "fc-slider--disabled",
642
+ dragging !== null && "fc-slider--dragging",
643
+ className
644
+ ].filter(Boolean).join(" ");
645
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: cls, style: mergedStyle, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
646
+ "div",
647
+ {
648
+ ref: trackRef,
649
+ className: "fc-slider__track",
650
+ onClick: handleTrackClick,
651
+ children: [
652
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
653
+ "div",
654
+ {
655
+ className: "fc-slider__fill",
656
+ style: isHorizontal ? { left: `${startPercent}%`, width: `${endPercent - startPercent}%` } : { bottom: `${startPercent}%`, height: `${endPercent - startPercent}%` }
657
+ }
658
+ ),
659
+ range && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
660
+ "div",
661
+ {
662
+ className: `fc-slider__thumb ${dragging === 0 ? "fc-slider__thumb--active" : ""}`,
663
+ style: thumbStyle(startPercent),
664
+ onMouseDown: handleMouseDown(0),
665
+ children: tooltip && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fc-slider__tooltip", children: startVal })
666
+ }
667
+ ),
668
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
669
+ "div",
670
+ {
671
+ className: `fc-slider__thumb ${dragging === (range ? 1 : 0) ? "fc-slider__thumb--active" : ""}`,
672
+ style: thumbStyle(endPercent),
673
+ onMouseDown: handleMouseDown(range ? 1 : 0),
674
+ children: tooltip && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fc-slider__tooltip", children: endVal })
675
+ }
676
+ ),
677
+ marks && Object.entries(marks).map(([val, label]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
678
+ "div",
679
+ {
680
+ className: "fc-slider__mark",
681
+ style: thumbStyle(getPercent(Number(val))),
682
+ children: [
683
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fc-slider__mark-dot" }),
684
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "fc-slider__mark-label", children: label })
685
+ ]
686
+ },
687
+ val
688
+ ))
689
+ ]
690
+ }
691
+ ) });
692
+ }
693
+
694
+ // src/components/Select/Select.tsx
695
+ var React6 = __toESM(require("react"), 1);
696
+ var import_jsx_runtime8 = require("react/jsx-runtime");
697
+ function Select({
698
+ options,
699
+ value: controlledValue,
700
+ defaultValue,
701
+ onChange,
702
+ placeholder = "\u8BF7\u9009\u62E9",
703
+ searchable = false,
704
+ multiple = false,
705
+ disabled = false,
706
+ className = "",
707
+ style,
708
+ virtualScroll = false,
709
+ virtualItemHeight = 32,
710
+ maxHeight = 256,
711
+ triggerBackground,
712
+ triggerBorderColor,
713
+ selectedColor,
714
+ selectedBackground,
715
+ hoverBackground
716
+ }) {
717
+ const colorVars = {
718
+ "--select-trigger-bg": triggerBackground,
719
+ "--select-trigger-border": triggerBorderColor,
720
+ "--select-option-selected-color": selectedColor,
721
+ "--select-option-selected-bg": selectedBackground,
722
+ "--select-option-hover-bg": hoverBackground
723
+ };
724
+ const overrideStyle = {};
725
+ for (const [key, value] of Object.entries(colorVars)) {
726
+ if (value !== void 0) {
727
+ overrideStyle[key] = value;
728
+ }
729
+ }
730
+ const mergedStyle = { ...overrideStyle, ...style };
731
+ const [isOpen, setIsOpen] = React6.useState(false);
732
+ const [searchValue, setSearchValue] = React6.useState("");
733
+ const [highlightedIndex, setHighlightedIndex] = React6.useState(0);
734
+ const containerRef = React6.useRef(null);
735
+ const listRef = React6.useRef(null);
736
+ const isControlled = controlledValue !== void 0;
737
+ const [internalValue, setInternalValue] = React6.useState(
738
+ defaultValue ?? (multiple ? [] : void 0)
739
+ );
740
+ const currentValue = isControlled ? controlledValue : internalValue;
741
+ const groupedOptions = React6.useMemo(() => {
742
+ const filtered = searchable && searchValue ? options.filter((o) => o.label.toLowerCase().includes(searchValue.toLowerCase())) : options;
743
+ const groups = {};
744
+ filtered.forEach((opt) => {
745
+ const group = opt.group || "";
746
+ if (!groups[group]) groups[group] = [];
747
+ groups[group].push(opt);
748
+ });
749
+ return groups;
750
+ }, [options, searchValue, searchable]);
751
+ const flatOptions = React6.useMemo(() => {
752
+ return Object.values(groupedOptions).flat();
753
+ }, [groupedOptions]);
754
+ const [scrollTop, setScrollTop] = React6.useState(0);
755
+ const visibleCount = Math.ceil(maxHeight / virtualItemHeight);
756
+ const startIndex = Math.floor(scrollTop / virtualItemHeight);
757
+ const endIndex = Math.min(startIndex + visibleCount + 1, flatOptions.length);
758
+ const visibleOptions = virtualScroll ? flatOptions.slice(startIndex, endIndex) : flatOptions;
759
+ const totalHeight = flatOptions.length * virtualItemHeight;
760
+ const offsetY = startIndex * virtualItemHeight;
761
+ const handleSelect = (option) => {
762
+ if (option.disabled) return;
763
+ let nextValue;
764
+ if (multiple) {
765
+ const arr = currentValue || [];
766
+ const exists = arr.includes(option.value);
767
+ nextValue = exists ? arr.filter((v) => v !== option.value) : [...arr, option.value];
768
+ } else {
769
+ nextValue = option.value;
770
+ setIsOpen(false);
771
+ }
772
+ if (!isControlled) setInternalValue(nextValue);
773
+ onChange?.(nextValue);
774
+ if (!multiple) setSearchValue("");
775
+ };
776
+ const isSelected = (value) => {
777
+ if (multiple) return currentValue?.includes(value);
778
+ return currentValue === value;
779
+ };
780
+ const displayLabel = () => {
781
+ if (multiple) {
782
+ const count = currentValue?.length || 0;
783
+ return count > 0 ? `\u5DF2\u9009\u62E9 ${count} \u9879` : placeholder;
784
+ }
785
+ const selected = options.find((o) => o.value === currentValue);
786
+ return selected?.label || placeholder;
787
+ };
788
+ React6.useEffect(() => {
789
+ const handleClick = (e) => {
790
+ if (!containerRef.current?.contains(e.target)) {
791
+ setIsOpen(false);
792
+ }
793
+ };
794
+ document.addEventListener("mousedown", handleClick);
795
+ return () => document.removeEventListener("mousedown", handleClick);
796
+ }, []);
797
+ const classNames = [
798
+ "fc-select",
799
+ isOpen && "fc-select--open",
800
+ multiple && "fc-select--multiple",
801
+ disabled && "fc-select--disabled",
802
+ className
803
+ ].filter(Boolean).join(" ");
804
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: containerRef, className: classNames, style: mergedStyle, children: [
805
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
806
+ "div",
807
+ {
808
+ className: "fc-select__trigger",
809
+ onClick: () => !disabled && setIsOpen(!isOpen),
810
+ children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `fc-select__value ${(currentValue === void 0 || currentValue === null || multiple && !currentValue.length) && "fc-select__value--placeholder"}`, children: displayLabel() }),
812
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "fc-select__arrow", children: "\u25BC" })
813
+ ]
814
+ }
815
+ ),
816
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "fc-select__dropdown", children: [
817
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "fc-select__search", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
818
+ "input",
819
+ {
820
+ type: "text",
821
+ value: searchValue,
822
+ onChange: (e) => setSearchValue(e.target.value),
823
+ placeholder: "\u641C\u7D22...",
824
+ className: "fc-select__search-input",
825
+ autoFocus: true
826
+ }
827
+ ) }),
828
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
829
+ "div",
830
+ {
831
+ ref: listRef,
832
+ className: "fc-select__list",
833
+ style: { maxHeight },
834
+ onScroll: (e) => virtualScroll && setScrollTop(e.target.scrollTop),
835
+ children: [
836
+ virtualScroll && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { height: totalHeight, position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { transform: `translateY(${offsetY}px)` }, children: renderOptions(visibleOptions, startIndex) }) }),
837
+ !virtualScroll && renderGroupedOptions()
838
+ ]
839
+ }
840
+ )
841
+ ] })
842
+ ] });
843
+ function renderOptions(opts, baseIndex = 0) {
844
+ return opts.map((option, idx) => {
845
+ const actualIndex = baseIndex + idx;
846
+ const selected = isSelected(option.value);
847
+ const highlighted = actualIndex === highlightedIndex;
848
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
849
+ "div",
850
+ {
851
+ className: [
852
+ "fc-select__option",
853
+ selected && "fc-select__option--selected",
854
+ option.disabled && "fc-select__option--disabled",
855
+ highlighted && "fc-select__option--highlighted"
856
+ ].filter(Boolean).join(" "),
857
+ onClick: () => handleSelect(option),
858
+ onMouseEnter: () => setHighlightedIndex(actualIndex),
859
+ style: { height: virtualItemHeight },
860
+ children: [
861
+ multiple && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `fc-select__checkbox ${selected && "fc-select__checkbox--checked"}`, children: selected && "\u2713" }),
862
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "fc-select__option-label", children: highlightText(option.label, searchValue) }),
863
+ !multiple && selected && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "fc-select__check", children: "\u2713" })
864
+ ]
865
+ },
866
+ option.value
867
+ );
868
+ });
869
+ }
870
+ function renderGroupedOptions() {
871
+ return Object.entries(groupedOptions).map(([group, opts]) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "fc-select__group", children: [
872
+ group && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "fc-select__group-label", children: group }),
873
+ renderOptions(opts)
874
+ ] }, group));
875
+ }
876
+ function highlightText(text, highlight) {
877
+ if (!highlight) return text;
878
+ const escaped = highlight.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
879
+ const parts = text.split(new RegExp(`(${escaped})`, "gi"));
880
+ return parts.map(
881
+ (part, i) => part.toLowerCase() === highlight.toLowerCase() ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("mark", { className: "fc-select__highlight", children: part }, i) : part
882
+ );
883
+ }
884
+ }
885
+
886
+ // src/components/Tree/Tree.tsx
887
+ var import_react4 = require("react");
888
+ var import_core = require("@dnd-kit/core");
889
+
890
+ // src/components/Tree/DeleteDialog.tsx
891
+ var import_react3 = require("react");
892
+ var import_jsx_runtime9 = require("react/jsx-runtime");
893
+ function DeleteDialog({ node, onClose, onDelete }) {
894
+ const [phase, setPhase] = (0, import_react3.useState)("choose");
895
+ const [loading, setLoading] = (0, import_react3.useState)(false);
896
+ if (!node) return null;
897
+ const reset = () => {
898
+ setPhase("choose");
899
+ setLoading(false);
900
+ };
901
+ const handleClose = () => {
902
+ reset();
903
+ onClose();
904
+ };
905
+ const handleLift = async () => {
906
+ setLoading(true);
907
+ try {
908
+ await onDelete(node.key, "lift");
909
+ reset();
910
+ onClose();
911
+ } catch {
912
+ setLoading(false);
913
+ }
914
+ };
915
+ const handleCascade = async () => {
916
+ setLoading(true);
917
+ try {
918
+ await onDelete(node.key, "cascade");
919
+ reset();
920
+ onClose();
921
+ } catch {
922
+ setLoading(false);
923
+ }
924
+ };
925
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fc-dialog-overlay", onMouseDown: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog", onMouseDown: (e) => e.stopPropagation(), children: [
926
+ phase === "choose" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
927
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog__header", children: [
928
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__icon", children: "\u{1F5C2}" }),
929
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("h3", { className: "fc-dialog__title", children: [
930
+ "\u5220\u9664\u300C",
931
+ node.title,
932
+ "\u300D"
933
+ ] })
934
+ ] }),
935
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "fc-dialog__desc", children: "\u8BE5\u5206\u7C7B\u4E0B\u53EF\u80FD\u5305\u542B\u5B50\u5206\u7C7B\u548C\u8BCD\u6761\uFF0C\u8BF7\u9009\u62E9\u5220\u9664\u65B9\u5F0F\uFF1A" }),
936
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog__options", children: [
937
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
938
+ "button",
939
+ {
940
+ className: "fc-dialog__option",
941
+ onClick: handleLift,
942
+ disabled: loading,
943
+ children: [
944
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-icon", children: "\u{1F4E4}" }),
945
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "fc-dialog__option-body", children: [
946
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-title", children: "\u79FB\u4EA4\u7ED9\u4E0A\u7EA7\u5206\u7C7B" }),
947
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-desc", children: "\u6240\u6709\u5B50\u5206\u7C7B\u548C\u8BCD\u6761\u5C06\u79FB\u81F3\u4E0A\u7EA7\u5206\u7C7B\uFF1B\u82E5\u5DF2\u662F\u6839\u5206\u7C7B\uFF0C\u5219\u53D8\u4E3A\u65E0\u5206\u7C7B" })
948
+ ] })
949
+ ]
950
+ }
951
+ ),
952
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
953
+ "button",
954
+ {
955
+ className: "fc-dialog__option fc-dialog__option--danger",
956
+ onClick: () => setPhase("confirm-cascade"),
957
+ disabled: loading,
958
+ children: [
959
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-icon", children: "\u{1F5D1}" }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "fc-dialog__option-body", children: [
961
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-title", children: "\u5F7B\u5E95\u5220\u9664" }),
962
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__option-desc", children: "\u5220\u9664\u8BE5\u5206\u7C7B\u53CA\u6240\u6709\u5B50\u5206\u7C7B\uFF1B\u8BCD\u6761\u4E0D\u4F1A\u88AB\u5220\u9664\uFF0C\u5C06\u53D8\u4E3A\u65E0\u5206\u7C7B" })
963
+ ] })
964
+ ]
965
+ }
966
+ )
967
+ ] }),
968
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fc-dialog__footer", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { className: "fc-dialog__btn", onClick: handleClose, disabled: loading, children: "\u53D6\u6D88" }) })
969
+ ] }),
970
+ phase === "confirm-cascade" && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
971
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog__header", children: [
972
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "fc-dialog__icon", children: "\u26A0\uFE0F" }),
973
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { className: "fc-dialog__title fc-dialog__title--danger", children: "\u786E\u8BA4\u5F7B\u5E95\u5220\u9664\uFF1F" })
974
+ ] }),
975
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "fc-dialog__desc", children: [
976
+ "\u6B64\u64CD\u4F5C",
977
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: "\u4E0D\u53EF\u9006" }),
978
+ "\u3002\u300C",
979
+ node.title,
980
+ "\u300D\u53CA\u5176\u6240\u6709\u5B50\u5206\u7C7B\u5C06\u88AB\u6C38\u4E45\u5220\u9664\uFF0C \u5176\u4E0B\u8BCD\u6761\u5C06\u53D8\u4E3A\u65E0\u5206\u7C7B\u3002"
981
+ ] }),
982
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "fc-dialog__footer", children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
984
+ "button",
985
+ {
986
+ className: "fc-dialog__btn",
987
+ onClick: () => setPhase("choose"),
988
+ disabled: loading,
989
+ children: "\u8FD4\u56DE"
990
+ }
991
+ ),
992
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
993
+ "button",
994
+ {
995
+ className: "fc-dialog__btn fc-dialog__btn--danger",
996
+ onClick: handleCascade,
997
+ disabled: loading,
998
+ children: loading ? "\u5220\u9664\u4E2D\u2026" : "\u786E\u8BA4\u5220\u9664"
999
+ }
1000
+ )
1001
+ ] })
1002
+ ] })
1003
+ ] }) });
1004
+ }
1005
+
1006
+ // src/components/Tree/flatToTree.ts
1007
+ function flatToTree(list) {
1008
+ const nodeMap = /* @__PURE__ */ new Map();
1009
+ for (const item of list) {
1010
+ nodeMap.set(item.id, {
1011
+ key: item.id,
1012
+ title: item.name,
1013
+ children: [],
1014
+ raw: item
1015
+ });
1016
+ }
1017
+ const roots = [];
1018
+ const orphans = [];
1019
+ for (const item of list) {
1020
+ const node = nodeMap.get(item.id);
1021
+ if (item.parent_id === null) {
1022
+ roots.push(node);
1023
+ } else if (nodeMap.has(item.parent_id)) {
1024
+ nodeMap.get(item.parent_id).children.push(node);
1025
+ } else {
1026
+ orphans.push(node);
45
1027
  }
1028
+ }
1029
+ const sortLevel = (nodes) => {
1030
+ nodes.sort((a, b) => a.raw.sort_order - b.raw.sort_order);
1031
+ for (const node of nodes) sortLevel(node.children);
1032
+ };
1033
+ sortLevel(roots);
1034
+ return { roots, orphans };
1035
+ }
1036
+ function findNodeInfo(nodes, key, parent = null) {
1037
+ for (let i = 0; i < nodes.length; i++) {
1038
+ if (nodes[i].key === key)
1039
+ return { node: nodes[i], parent, siblings: nodes, index: i };
1040
+ const found = findNodeInfo(nodes[i].children, key, nodes[i]);
1041
+ if (found) return found;
1042
+ }
1043
+ return null;
1044
+ }
1045
+ function isDescendantOf(roots, ancestorKey, targetKey) {
1046
+ const info = findNodeInfo(roots, ancestorKey);
1047
+ if (!info) return false;
1048
+ const check = (children) => children.some((n) => n.key === targetKey || check(n.children));
1049
+ return check(info.node.children);
1050
+ }
1051
+
1052
+ // src/components/Tree/Tree.tsx
1053
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1054
+ var TreeActionsCtx = (0, import_react4.createContext)(null);
1055
+ var TreeStateCtx = (0, import_react4.createContext)(null);
1056
+ var DndStateCtx = (0, import_react4.createContext)(null);
1057
+ var CollapsePanel = (0, import_react4.memo)(function CollapsePanel2({ open, children }) {
1058
+ const innerRef = (0, import_react4.useRef)(null);
1059
+ const [height, setHeight] = (0, import_react4.useState)(0);
1060
+ const [ready, setReady] = (0, import_react4.useState)(false);
1061
+ (0, import_react4.useEffect)(() => {
1062
+ const el = innerRef.current;
1063
+ if (!el) return;
1064
+ const ro = new ResizeObserver(() => setHeight(el.offsetHeight));
1065
+ ro.observe(el);
1066
+ setHeight(el.offsetHeight);
1067
+ requestAnimationFrame(() => requestAnimationFrame(() => setReady(true)));
1068
+ return () => ro.disconnect();
1069
+ }, []);
1070
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: {
1071
+ height: open ? height : 0,
1072
+ overflow: "hidden",
1073
+ transition: ready ? "height 0.12s ease-out" : "none"
1074
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: innerRef, children }) });
1075
+ });
1076
+ var DndSlot = (0, import_react4.memo)(function DndSlot2({
1077
+ nodeKey,
1078
+ disabled,
1079
+ children
1080
+ }) {
1081
+ const dndState = (0, import_react4.useContext)(DndStateCtx);
1082
+ const isDropTarget = dndState.dropTargetKey === nodeKey;
1083
+ const dropPosition = isDropTarget ? dndState.dropPosition : null;
1084
+ const isDragSource = dndState.dragKey === nodeKey;
1085
+ const { attributes, listeners, setNodeRef: setDragRef, isDragging } = (0, import_core.useDraggable)({ id: nodeKey, disabled });
1086
+ const { setNodeRef: setDropRef } = (0, import_core.useDroppable)({ id: nodeKey, disabled });
1087
+ const setRef = (0, import_react4.useCallback)((el) => {
1088
+ setDragRef(el);
1089
+ setDropRef(el);
1090
+ }, [setDragRef, setDropRef]);
1091
+ const handleProps = (0, import_react4.useMemo)(
1092
+ () => ({ ...attributes, ...listeners }),
1093
+ [attributes, listeners]
46
1094
  );
1095
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: children({ setRef, handleProps, isDragging, isDragSource, dropPosition }) });
1096
+ });
1097
+ var TreeNodeItem = (0, import_react4.memo)(function TreeNodeItem2({ node, level, hidden = false }) {
1098
+ const actions = (0, import_react4.useContext)(TreeActionsCtx);
1099
+ const state = (0, import_react4.useContext)(TreeStateCtx);
1100
+ const isExpanded = state.expandedKeys.has(node.key);
1101
+ const isEditing = state.editingKey === node.key;
1102
+ const hasChildren = node.children.length > 0;
1103
+ const indent = level * 20 + 12;
1104
+ const [localEdit, setLocalEdit] = (0, import_react4.useState)("");
1105
+ (0, import_react4.useEffect)(() => {
1106
+ if (isEditing) setLocalEdit(node.title);
1107
+ }, [isEditing, node.title]);
1108
+ const handleEditKeyDown = (0, import_react4.useCallback)((e) => {
1109
+ e.stopPropagation();
1110
+ if (e.key === "Enter") actions.commitEdit(node.key, localEdit).then();
1111
+ if (e.key === "Escape") actions.cancelEdit();
1112
+ }, [actions, node.key, localEdit]);
1113
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DndSlot, { nodeKey: node.key, disabled: isEditing || hidden, children: ({ setRef, handleProps, isDragging, isDragSource, dropPosition }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `fc-tree__node ${isDragging ? "is-dragging" : ""}`, children: [
1114
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1115
+ "div",
1116
+ {
1117
+ ref: setRef,
1118
+ className: [
1119
+ "fc-tree__item",
1120
+ isDragSource && "fc-tree__item--drag-source",
1121
+ dropPosition === "into" && "fc-tree__item--drop-into",
1122
+ dropPosition === "before" && "fc-tree__item--drop-before",
1123
+ dropPosition === "after" && "fc-tree__item--drop-after"
1124
+ ].filter(Boolean).join(" "),
1125
+ style: {
1126
+ paddingLeft: dropPosition === "into" ? indent + 8 : indent,
1127
+ "--fc-indent": `${indent}px`
1128
+ },
1129
+ children: [
1130
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1131
+ "span",
1132
+ {
1133
+ className: "fc-tree__drag-handle",
1134
+ title: "\u62D6\u62FD\u79FB\u52A8",
1135
+ ...handleProps,
1136
+ onMouseDown: (e) => e.stopPropagation(),
1137
+ children: "\u283F"
1138
+ }
1139
+ ),
1140
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1141
+ "span",
1142
+ {
1143
+ className: [
1144
+ "fc-tree__switcher",
1145
+ !hasChildren && "fc-tree__switcher--hidden",
1146
+ isExpanded && "fc-tree__switcher--open"
1147
+ ].filter(Boolean).join(" "),
1148
+ onClick: () => hasChildren && actions.toggleExpand(node.key),
1149
+ children: hasChildren ? "\u25B6" : ""
1150
+ }
1151
+ ),
1152
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1153
+ "input",
1154
+ {
1155
+ autoFocus: true,
1156
+ className: "fc-tree__edit-input",
1157
+ value: localEdit,
1158
+ onChange: (e) => setLocalEdit(e.target.value),
1159
+ onBlur: () => actions.commitEdit(node.key, localEdit),
1160
+ onKeyDown: handleEditKeyDown,
1161
+ onClick: (e) => e.stopPropagation()
1162
+ }
1163
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1164
+ "span",
1165
+ {
1166
+ className: "fc-tree__title",
1167
+ onClick: () => {
1168
+ actions.select(node.key);
1169
+ if (hasChildren) actions.toggleExpand(node.key);
1170
+ },
1171
+ onDoubleClick: () => actions.startEdit(node.key),
1172
+ children: node.title
1173
+ }
1174
+ ),
1175
+ !isEditing && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "fc-tree__actions", children: [
1176
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1177
+ "button",
1178
+ {
1179
+ className: "fc-tree__action",
1180
+ title: "\u65B0\u5EFA\u5B50\u5206\u7C7B",
1181
+ onClick: (e) => {
1182
+ e.stopPropagation();
1183
+ actions.requestCreate(node.key).then();
1184
+ },
1185
+ children: "+"
1186
+ }
1187
+ ),
1188
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1189
+ "button",
1190
+ {
1191
+ className: "fc-tree__action",
1192
+ title: "\u91CD\u547D\u540D\uFF08\u53CC\u51FB\u4E5F\u53EF\uFF09",
1193
+ onClick: (e) => {
1194
+ e.stopPropagation();
1195
+ actions.startEdit(node.key);
1196
+ },
1197
+ children: "\u270F"
1198
+ }
1199
+ ),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1201
+ "button",
1202
+ {
1203
+ className: "fc-tree__action fc-tree__action--danger",
1204
+ title: "\u5220\u9664",
1205
+ onClick: (e) => {
1206
+ e.stopPropagation();
1207
+ actions.requestDelete(node);
1208
+ },
1209
+ children: "\u{1F5D1}"
1210
+ }
1211
+ )
1212
+ ] })
1213
+ ]
1214
+ }
1215
+ ),
1216
+ hasChildren && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CollapsePanel, { open: isExpanded, children: node.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TreeNodeItem2, { node: child, level: level + 1, hidden: hidden || !isExpanded }, child.key)) })
1217
+ ] }) });
1218
+ });
1219
+ function Tree({
1220
+ treeData,
1221
+ onRename,
1222
+ onCreate,
1223
+ onDelete,
1224
+ onMove,
1225
+ onSelect,
1226
+ selectedKey,
1227
+ searchable = false,
1228
+ scrollHeight = "400px",
1229
+ className = ""
1230
+ }) {
1231
+ const [expandedKeys, setExpandedKeys] = (0, import_react4.useState)(/* @__PURE__ */ new Set());
1232
+ const [editingKey, setEditingKey] = (0, import_react4.useState)(null);
1233
+ const [deleteTarget, setDeleteTarget] = (0, import_react4.useState)(null);
1234
+ const [searchValue, setSearchValue] = (0, import_react4.useState)("");
1235
+ const [dndState, setDndState] = (0, import_react4.useState)({
1236
+ dropTargetKey: null,
1237
+ dropPosition: null,
1238
+ dragKey: null
1239
+ });
1240
+ const dropRef = (0, import_react4.useRef)({ key: null, pos: null });
1241
+ const pointerYRef = (0, import_react4.useRef)(0);
1242
+ (0, import_react4.useEffect)(() => {
1243
+ const handler = (e) => {
1244
+ pointerYRef.current = e.clientY;
1245
+ };
1246
+ window.addEventListener("pointermove", handler);
1247
+ return () => window.removeEventListener("pointermove", handler);
1248
+ }, []);
1249
+ const sensors = (0, import_core.useSensors)(
1250
+ (0, import_core.useSensor)(import_core.PointerSensor, { activationConstraint: { distance: 8 } })
1251
+ );
1252
+ const toggleExpand = (0, import_react4.useCallback)((key) => {
1253
+ setExpandedKeys((prev) => {
1254
+ const next = new Set(prev);
1255
+ next.has(key) ? next.delete(key) : next.add(key);
1256
+ return next;
1257
+ });
1258
+ }, []);
1259
+ const select = (0, import_react4.useCallback)((key) => onSelect?.(key), [onSelect]);
1260
+ const startEdit = (0, import_react4.useCallback)((key) => setEditingKey(key), []);
1261
+ const cancelEdit = (0, import_react4.useCallback)(() => setEditingKey(null), []);
1262
+ const commitEdit = (0, import_react4.useCallback)(async (key, newTitle) => {
1263
+ setEditingKey(null);
1264
+ const trimmed = newTitle.trim();
1265
+ if (trimmed && onRename) await onRename(key, trimmed);
1266
+ }, [onRename]);
1267
+ const requestCreate = (0, import_react4.useCallback)(async (parentKey) => {
1268
+ if (!onCreate) return;
1269
+ const newKey = await onCreate(parentKey);
1270
+ if (parentKey) setExpandedKeys((prev) => /* @__PURE__ */ new Set([...prev, parentKey]));
1271
+ setEditingKey(newKey);
1272
+ }, [onCreate]);
1273
+ const requestDelete = (0, import_react4.useCallback)((node) => {
1274
+ setDeleteTarget(node);
1275
+ }, []);
1276
+ const treeDataRef = (0, import_react4.useRef)(treeData);
1277
+ const expandedRef = (0, import_react4.useRef)(expandedKeys);
1278
+ treeDataRef.current = treeData;
1279
+ expandedRef.current = expandedKeys;
1280
+ const handleDragStart = (0, import_react4.useCallback)(({ active }) => {
1281
+ dropRef.current = { key: null, pos: null };
1282
+ setDndState({ dropTargetKey: null, dropPosition: null, dragKey: active.id });
1283
+ }, []);
1284
+ const handleDragMove = (0, import_react4.useCallback)(({ over, active }) => {
1285
+ if (!over || over.id === active.id) {
1286
+ if (dropRef.current.key !== null) {
1287
+ dropRef.current = { key: null, pos: null };
1288
+ setDndState((prev) => ({ ...prev, dropTargetKey: null, dropPosition: null }));
1289
+ }
1290
+ return;
1291
+ }
1292
+ const targetKey = over.id;
1293
+ if (isDescendantOf(treeDataRef.current, active.id, targetKey)) {
1294
+ if (dropRef.current.key !== null) {
1295
+ dropRef.current = { key: null, pos: null };
1296
+ setDndState((prev) => ({ ...prev, dropTargetKey: null, dropPosition: null }));
1297
+ }
1298
+ return;
1299
+ }
1300
+ const rect = over.rect;
1301
+ const y = pointerYRef.current;
1302
+ const ratio = Math.max(0, Math.min(1, (y - rect.top) / rect.height));
1303
+ let position;
1304
+ if (ratio < 0.2) position = "before";
1305
+ else if (ratio > 0.8) position = "after";
1306
+ else position = "into";
1307
+ if (dropRef.current.key === targetKey && dropRef.current.pos === position) return;
1308
+ dropRef.current = { key: targetKey, pos: position };
1309
+ setDndState((prev) => ({ ...prev, dropTargetKey: targetKey, dropPosition: position }));
1310
+ }, []);
1311
+ const handleDragEnd = (0, import_react4.useCallback)(({ active }) => {
1312
+ const { key: target, pos: position } = dropRef.current;
1313
+ dropRef.current = { key: null, pos: null };
1314
+ setDndState({ dropTargetKey: null, dropPosition: null, dragKey: null });
1315
+ if (!target || !position || active.id === target) return;
1316
+ onMove?.(active.id, target, position);
1317
+ }, [onMove]);
1318
+ const handleDragCancel = (0, import_react4.useCallback)(() => {
1319
+ dropRef.current = { key: null, pos: null };
1320
+ setDndState({ dropTargetKey: null, dropPosition: null, dragKey: null });
1321
+ }, []);
1322
+ const displayData = (0, import_react4.useMemo)(() => {
1323
+ if (!searchValue) return treeData;
1324
+ const kw = searchValue.toLowerCase();
1325
+ const filter = (nodes) => nodes.reduce((acc, node) => {
1326
+ const match = node.title.toLowerCase().includes(kw);
1327
+ const filteredChildren = filter(node.children);
1328
+ if (match || filteredChildren.length > 0)
1329
+ acc.push({ ...node, children: filteredChildren });
1330
+ return acc;
1331
+ }, []);
1332
+ return filter(treeData);
1333
+ }, [treeData, searchValue]);
1334
+ const actionsValue = (0, import_react4.useMemo)(() => ({
1335
+ toggleExpand,
1336
+ select,
1337
+ startEdit,
1338
+ commitEdit,
1339
+ cancelEdit,
1340
+ requestCreate,
1341
+ requestDelete
1342
+ }), [toggleExpand, select, startEdit, commitEdit, cancelEdit, requestCreate, requestDelete]);
1343
+ const stateValue = (0, import_react4.useMemo)(() => ({
1344
+ expandedKeys,
1345
+ selectedKey: selectedKey ?? null,
1346
+ editingKey
1347
+ }), [expandedKeys, selectedKey, editingKey]);
1348
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TreeActionsCtx.Provider, { value: actionsValue, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TreeStateCtx.Provider, { value: stateValue, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DndStateCtx.Provider, { value: dndState, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1349
+ import_core.DndContext,
1350
+ {
1351
+ sensors,
1352
+ onDragStart: handleDragStart,
1353
+ onDragMove: handleDragMove,
1354
+ onDragEnd: handleDragEnd,
1355
+ onDragCancel: handleDragCancel,
1356
+ children: [
1357
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `fc-tree ${className}`, children: [
1358
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "fc-tree__search", children: [
1359
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1360
+ "input",
1361
+ {
1362
+ type: "text",
1363
+ value: searchValue,
1364
+ onChange: (e) => setSearchValue(e.target.value),
1365
+ placeholder: "\u641C\u7D22\u5206\u7C7B\u2026",
1366
+ className: "fc-tree__search-input"
1367
+ }
1368
+ ),
1369
+ searchValue && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "fc-tree__search-clear", onClick: () => setSearchValue(""), children: "\u2715" })
1370
+ ] }),
1371
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(RollingBox, { showThumb: "show", style: { height: scrollHeight }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "fc-tree__list", children: [
1372
+ displayData.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "fc-tree__empty", children: searchValue ? "\u65E0\u5339\u914D\u5206\u7C7B" : "\u6682\u65E0\u5206\u7C7B" }),
1373
+ displayData.map((node) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TreeNodeItem, { node, level: 0 }, node.key))
1374
+ ] }) }),
1375
+ onCreate && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "fc-tree__add-root", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1376
+ "button",
1377
+ {
1378
+ className: "fc-tree__add-root-btn",
1379
+ onClick: () => requestCreate(null),
1380
+ children: "+ \u65B0\u5EFA\u9876\u7EA7\u5206\u7C7B"
1381
+ }
1382
+ ) })
1383
+ ] }),
1384
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1385
+ DeleteDialog,
1386
+ {
1387
+ node: deleteTarget,
1388
+ onClose: () => setDeleteTarget(null),
1389
+ onDelete: async (key, mode) => {
1390
+ await onDelete?.(key, mode);
1391
+ setDeleteTarget(null);
1392
+ }
1393
+ }
1394
+ )
1395
+ ]
1396
+ }
1397
+ ) }) }) });
47
1398
  }
1399
+
1400
+ // src/components/Tree/OrphanDialog.tsx
1401
+ var import_react5 = require("react");
1402
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1403
+ function OrphanDialog({ orphans, onResolve, onClose }) {
1404
+ const [resolutions, setResolutions] = (0, import_react5.useState)(
1405
+ () => Object.fromEntries(orphans.map((o) => [o.key, "lift"]))
1406
+ );
1407
+ if (orphans.length === 0) return null;
1408
+ const set = (key, val) => setResolutions((prev) => ({ ...prev, [key]: val }));
1409
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "fc-dialog-overlay", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-dialog fc-dialog--wide", children: [
1410
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-dialog__header", children: [
1411
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "fc-dialog__icon", children: "\u{1F50D}" }),
1412
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("h3", { className: "fc-dialog__title fc-dialog__title--warning", children: [
1413
+ "\u68C0\u6D4B\u5230 ",
1414
+ orphans.length,
1415
+ " \u4E2A\u5B64\u7ACB\u5206\u7C7B"
1416
+ ] })
1417
+ ] }),
1418
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "fc-dialog__desc", children: "\u4EE5\u4E0B\u5206\u7C7B\u7684\u7236\u5206\u7C7B\u5DF2\u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u662F\u6570\u636E\u8FC1\u79FB\u6216\u5F02\u5E38\u5220\u9664\u5BFC\u81F4\u3002 \u8FD9\u4E9B\u5206\u7C7B\u76EE\u524D\u4E0D\u4F1A\u663E\u793A\u5728\u6811\u4E2D\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F\uFF1A" }),
1419
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "fc-orphan-list", children: orphans.map((node) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-orphan-item", children: [
1420
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "fc-orphan-name", title: node.key, children: node.title }),
1421
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "fc-orphan-id", children: [
1422
+ "id: ",
1423
+ node.key.slice(0, 8),
1424
+ "\u2026"
1425
+ ] }),
1426
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-orphan-radios", children: [
1427
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: `fc-orphan-radio ${resolutions[node.key] === "lift" ? "is-active" : ""}`, children: [
1428
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1429
+ "input",
1430
+ {
1431
+ type: "radio",
1432
+ name: node.key,
1433
+ checked: resolutions[node.key] === "lift",
1434
+ onChange: () => set(node.key, "lift")
1435
+ }
1436
+ ),
1437
+ "\u63D0\u5347\u4E3A\u6839\u5206\u7C7B"
1438
+ ] }),
1439
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: `fc-orphan-radio fc-orphan-radio--danger ${resolutions[node.key] === "remove" ? "is-active" : ""}`, children: [
1440
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1441
+ "input",
1442
+ {
1443
+ type: "radio",
1444
+ name: node.key,
1445
+ checked: resolutions[node.key] === "remove",
1446
+ onChange: () => set(node.key, "remove")
1447
+ }
1448
+ ),
1449
+ "\u5220\u9664"
1450
+ ] })
1451
+ ] })
1452
+ ] }, node.key)) }),
1453
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fc-dialog__footer", children: [
1454
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "fc-dialog__btn", onClick: onClose, children: "\u6682\u65F6\u5FFD\u7565" }),
1455
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1456
+ "button",
1457
+ {
1458
+ className: "fc-dialog__btn fc-dialog__btn--primary",
1459
+ onClick: () => onResolve(resolutions),
1460
+ children: "\u786E\u8BA4\u5904\u7406"
1461
+ }
1462
+ )
1463
+ ] })
1464
+ ] }) });
1465
+ }
1466
+
1467
+ // src/components/Avatar/Avatar.tsx
1468
+ var import_react6 = require("react");
1469
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1470
+ var SIZE_MAP = {
1471
+ xs: 20,
1472
+ sm: 28,
1473
+ md: 40,
1474
+ lg: 56,
1475
+ xl: 72
1476
+ };
1477
+ 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";
1478
+ var useAvatarStyles = (size, shape, bordered, onClick, loadState, src, children, colorVariant, className, color, customStyle) => {
1479
+ const classes = (0, import_react6.useMemo)(() => {
1480
+ const classNames = [
1481
+ "ui-avatar",
1482
+ `ui-avatar-${size}`,
1483
+ `ui-avatar-${shape}`,
1484
+ bordered && "ui-avatar-bordered",
1485
+ onClick && "ui-avatar-clickable",
1486
+ loadState === "loading" && "ui-avatar-loading",
1487
+ !src && !children && "ui-avatar-empty",
1488
+ colorVariant && `ui-avatar--${colorVariant}`,
1489
+ className
1490
+ ];
1491
+ return classNames.filter(Boolean).join(" ");
1492
+ }, [size, shape, bordered, onClick, loadState, src, children, colorVariant, className]);
1493
+ const style = (0, import_react6.useMemo)(() => ({
1494
+ width: SIZE_MAP[size],
1495
+ height: SIZE_MAP[size],
1496
+ fontSize: `${SIZE_MAP[size] * 0.4}px`,
1497
+ ...color && { backgroundColor: color },
1498
+ ...customStyle
1499
+ }), [size, color, customStyle]);
1500
+ return { classes, style };
1501
+ };
1502
+ var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError, onStateChange) => {
1503
+ const [loadState, setLoadState] = (0, import_react6.useState)("idle");
1504
+ const [currentSrc, setCurrentSrc] = (0, import_react6.useState)(src);
1505
+ (0, import_react6.useEffect)(() => {
1506
+ setCurrentSrc(src);
1507
+ setLoadState(src ? "loading" : "idle");
1508
+ }, [src]);
1509
+ (0, import_react6.useEffect)(() => {
1510
+ onStateChange?.(loadState);
1511
+ }, [loadState, onStateChange]);
1512
+ const handleLoad = (0, import_react6.useCallback)(() => {
1513
+ setLoadState("loaded");
1514
+ onImageLoad?.();
1515
+ }, [onImageLoad]);
1516
+ const handleError = (0, import_react6.useCallback)((e) => {
1517
+ if (fallbackSrc && currentSrc !== fallbackSrc) {
1518
+ setCurrentSrc(fallbackSrc);
1519
+ setLoadState("loading");
1520
+ } else {
1521
+ setLoadState("error");
1522
+ onImageError?.(e.nativeEvent);
1523
+ }
1524
+ }, [fallbackSrc, currentSrc, onImageError]);
1525
+ return {
1526
+ loadState,
1527
+ currentSrc,
1528
+ handleLoad,
1529
+ handleError
1530
+ };
1531
+ };
1532
+ var useKeyboardInteraction = (onClick) => {
1533
+ const handleKeyDown = (0, import_react6.useCallback)((e) => {
1534
+ if (onClick && (e.key === "Enter" || e.key === " ")) {
1535
+ e.preventDefault();
1536
+ onClick(e);
1537
+ }
1538
+ }, [onClick]);
1539
+ return { handleKeyDown };
1540
+ };
1541
+ var renderContent = (currentSrc, fallbackSrc, loadState, alt, lazyLoad2, children, handleLoad, handleError) => {
1542
+ const showImage = currentSrc && loadState !== "error";
1543
+ const showFallback = !showImage && children;
1544
+ const imageSrc = loadState === "error" && currentSrc !== fallbackSrc ? fallbackSrc : currentSrc;
1545
+ if (showImage && imageSrc) {
1546
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1547
+ "img",
1548
+ {
1549
+ src: imageSrc,
1550
+ alt,
1551
+ className: "ui-avatar-img",
1552
+ loading: lazyLoad2 ? "lazy" : "eager",
1553
+ onLoad: handleLoad,
1554
+ onError: handleError,
1555
+ decoding: "async"
1556
+ }
1557
+ );
1558
+ }
1559
+ if (showFallback) {
1560
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "ui-avatar-text", children });
1561
+ }
1562
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "ui-avatar-placeholder", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("svg", { viewBox: "0 0 24 24", width: "40%", height: "40%", fill: "currentColor", opacity: 0.4, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("path", { d: DEFAULT_ICON }) }) });
1563
+ };
1564
+ var useAriaAttributes = (onClick, loadState, alt) => {
1565
+ return {
1566
+ role: onClick ? "button" : "img",
1567
+ tabIndex: onClick ? 0 : void 0,
1568
+ "aria-label": alt,
1569
+ "aria-busy": loadState === "loading",
1570
+ "data-load-state": loadState
1571
+ };
1572
+ };
1573
+ var Avatar = (0, import_react6.forwardRef)(
1574
+ ({
1575
+ children,
1576
+ src,
1577
+ fallbackSrc,
1578
+ color,
1579
+ colorVariant,
1580
+ size = "md",
1581
+ shape = "circle",
1582
+ alt = "\u7528\u6237\u5934\u50CF",
1583
+ lazyLoad: lazyLoad2 = false,
1584
+ onImageLoad,
1585
+ onImageError,
1586
+ onStateChange,
1587
+ bordered = false,
1588
+ className = "",
1589
+ onClick,
1590
+ style: customStyle,
1591
+ ...restProps
1592
+ }, ref) => {
1593
+ const { loadState, currentSrc, handleLoad, handleError } = useImageLoader(
1594
+ src,
1595
+ fallbackSrc,
1596
+ onImageLoad,
1597
+ onImageError,
1598
+ onStateChange
1599
+ );
1600
+ const { classes, style } = useAvatarStyles(
1601
+ size,
1602
+ shape,
1603
+ bordered,
1604
+ onClick,
1605
+ loadState,
1606
+ src,
1607
+ children,
1608
+ colorVariant,
1609
+ className,
1610
+ color,
1611
+ customStyle
1612
+ );
1613
+ const { handleKeyDown } = useKeyboardInteraction(onClick);
1614
+ const ariaAttributes = useAriaAttributes(onClick, loadState, alt);
1615
+ const content = renderContent(
1616
+ currentSrc,
1617
+ fallbackSrc,
1618
+ loadState,
1619
+ alt,
1620
+ lazyLoad2,
1621
+ children,
1622
+ handleLoad,
1623
+ handleError
1624
+ );
1625
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1626
+ "div",
1627
+ {
1628
+ ref,
1629
+ className: classes,
1630
+ style,
1631
+ onClick,
1632
+ onKeyDown: handleKeyDown,
1633
+ ...ariaAttributes,
1634
+ ...restProps,
1635
+ children: content
1636
+ }
1637
+ );
1638
+ }
1639
+ );
1640
+ Avatar.displayName = "Avatar";
1641
+
1642
+ // src/components/ListGroup/ListGroup.tsx
1643
+ var import_react7 = require("react");
1644
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1645
+ var combineClassNames = (...classNames) => {
1646
+ return classNames.filter(Boolean).join(" ");
1647
+ };
1648
+ var ListGroupItem = (0, import_react7.forwardRef)(
1649
+ ({
1650
+ active = false,
1651
+ disabled = false,
1652
+ onClick,
1653
+ className,
1654
+ children,
1655
+ ...props
1656
+ }, ref) => {
1657
+ const handleClick = (0, import_react7.useCallback)((e) => {
1658
+ if (disabled) return;
1659
+ onClick?.(e);
1660
+ }, [disabled, onClick]);
1661
+ const classNames = (0, import_react7.useMemo)(() => {
1662
+ return combineClassNames(
1663
+ "fc-list-group-item",
1664
+ active && "fc-list-group-item--active",
1665
+ disabled && "fc-list-group-item--disabled",
1666
+ className
1667
+ );
1668
+ }, [active, disabled, className]);
1669
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1670
+ "li",
1671
+ {
1672
+ ref,
1673
+ className: classNames,
1674
+ onClick: handleClick,
1675
+ "aria-disabled": disabled,
1676
+ "aria-selected": active,
1677
+ role: onClick ? "button" : void 0,
1678
+ tabIndex: onClick && !disabled ? 0 : void 0,
1679
+ ...props,
1680
+ children
1681
+ }
1682
+ );
1683
+ }
1684
+ );
1685
+ ListGroupItem.displayName = "ListGroupItem";
1686
+ var ListGroup = (0, import_react7.forwardRef)(
1687
+ ({
1688
+ bordered = true,
1689
+ flush = false,
1690
+ className,
1691
+ children,
1692
+ ...props
1693
+ }, ref) => {
1694
+ const classNames = (0, import_react7.useMemo)(() => {
1695
+ return combineClassNames(
1696
+ "fc-list-group",
1697
+ bordered && "fc-list-group--bordered",
1698
+ flush && "fc-list-group--flush",
1699
+ className
1700
+ );
1701
+ }, [bordered, flush, className]);
1702
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1703
+ "ul",
1704
+ {
1705
+ ref,
1706
+ className: classNames,
1707
+ role: "list",
1708
+ ...props,
1709
+ children
1710
+ }
1711
+ );
1712
+ }
1713
+ );
1714
+ ListGroup.displayName = "ListGroup";
1715
+
1716
+ // src/components/VirtualList/VirtualList.tsx
1717
+ var import_react8 = require("react");
1718
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1719
+ var VirtualItem = (0, import_react8.memo)(({ children, height }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "fc-virtual-list__item", style: { height: `${height}px` }, children }));
1720
+ VirtualItem.displayName = "VirtualItem";
1721
+ function VirtualList({
1722
+ data,
1723
+ height,
1724
+ itemHeight,
1725
+ renderItem,
1726
+ className = "",
1727
+ overscan = 3,
1728
+ showScrollbar = true,
1729
+ onScrollEnd,
1730
+ style
1731
+ }) {
1732
+ const containerRef = (0, import_react8.useRef)(null);
1733
+ const [scrollTop, setScrollTop] = (0, import_react8.useState)(0);
1734
+ const totalHeight = data.length * itemHeight;
1735
+ const handleScroll = (0, import_react8.useCallback)((e) => {
1736
+ const newScrollTop = e.currentTarget.scrollTop;
1737
+ setScrollTop(newScrollTop);
1738
+ if (onScrollEnd) {
1739
+ const scrollHeight = e.currentTarget.scrollHeight;
1740
+ const clientHeight = e.currentTarget.clientHeight;
1741
+ if (newScrollTop + clientHeight >= scrollHeight - 5) {
1742
+ onScrollEnd();
1743
+ }
1744
+ }
1745
+ }, [onScrollEnd]);
1746
+ const visibleRange = (0, import_react8.useMemo)(() => {
1747
+ const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
1748
+ const endIndex = Math.min(
1749
+ data.length,
1750
+ Math.ceil((scrollTop + height) / itemHeight) + overscan
1751
+ );
1752
+ return { startIndex, endIndex };
1753
+ }, [scrollTop, height, itemHeight, data.length, overscan]);
1754
+ const visibleData = (0, import_react8.useMemo)(() => {
1755
+ return data.slice(visibleRange.startIndex, visibleRange.endIndex);
1756
+ }, [data, visibleRange]);
1757
+ const offsetY = visibleRange.startIndex * itemHeight;
1758
+ const classNames = [
1759
+ "fc-virtual-list",
1760
+ !showScrollbar && "fc-virtual-list--hide-scrollbar",
1761
+ className
1762
+ ].filter(Boolean).join(" ");
1763
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1764
+ "div",
1765
+ {
1766
+ ref: containerRef,
1767
+ className: classNames,
1768
+ style: { height: `${height}px`, ...style },
1769
+ onScroll: handleScroll,
1770
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1771
+ "div",
1772
+ {
1773
+ className: "fc-virtual-list__container",
1774
+ style: { height: `${totalHeight}px` },
1775
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1776
+ "div",
1777
+ {
1778
+ className: "fc-virtual-list__content",
1779
+ style: { transform: `translateY(${offsetY}px)` },
1780
+ children: visibleData.map((item, idx) => {
1781
+ const actualIndex = visibleRange.startIndex + idx;
1782
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(VirtualItem, { height: itemHeight, children: renderItem(item, actualIndex) }, actualIndex);
1783
+ })
1784
+ }
1785
+ )
1786
+ }
1787
+ )
1788
+ }
1789
+ );
1790
+ }
1791
+
1792
+ // src/components/Alert/AlertContext.tsx
1793
+ var import_react9 = require("react");
1794
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1795
+ var AlertContext = (0, import_react9.createContext)(null);
1796
+ var ICONS = {
1797
+ success: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1798
+ "svg",
1799
+ {
1800
+ className: "fc-alert__icon",
1801
+ xmlns: "http://www.w3.org/2000/svg",
1802
+ viewBox: "0 0 24 24",
1803
+ fill: "none",
1804
+ stroke: "currentColor",
1805
+ strokeWidth: "2",
1806
+ strokeLinecap: "round",
1807
+ strokeLinejoin: "round",
1808
+ children: [
1809
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1810
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("polyline", { points: "17 9 11 17 6 12" })
1811
+ ]
1812
+ }
1813
+ ),
1814
+ error: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1815
+ "svg",
1816
+ {
1817
+ className: "fc-alert__icon",
1818
+ xmlns: "http://www.w3.org/2000/svg",
1819
+ viewBox: "0 0 24 24",
1820
+ fill: "none",
1821
+ stroke: "currentColor",
1822
+ strokeWidth: "2",
1823
+ strokeLinecap: "round",
1824
+ strokeLinejoin: "round",
1825
+ children: [
1826
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1827
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "15", y1: "9", x2: "9", y2: "15" }),
1828
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
1829
+ ]
1830
+ }
1831
+ ),
1832
+ warning: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1833
+ "svg",
1834
+ {
1835
+ className: "fc-alert__icon",
1836
+ xmlns: "http://www.w3.org/2000/svg",
1837
+ viewBox: "0 0 24 24",
1838
+ fill: "none",
1839
+ stroke: "currentColor",
1840
+ strokeWidth: "2",
1841
+ strokeLinecap: "round",
1842
+ strokeLinejoin: "round",
1843
+ children: [
1844
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
1845
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
1846
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
1847
+ ]
1848
+ }
1849
+ ),
1850
+ info: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1851
+ "svg",
1852
+ {
1853
+ className: "fc-alert__icon",
1854
+ xmlns: "http://www.w3.org/2000/svg",
1855
+ viewBox: "0 0 24 24",
1856
+ fill: "none",
1857
+ stroke: "currentColor",
1858
+ strokeWidth: "2",
1859
+ strokeLinecap: "round",
1860
+ strokeLinejoin: "round",
1861
+ children: [
1862
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1863
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
1864
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
1865
+ ]
1866
+ }
1867
+ )
1868
+ };
1869
+ function AlertProvider({ children, background, borderColor }) {
1870
+ const [alert, setAlert] = (0, import_react9.useState)({
1871
+ msg: "",
1872
+ type: "info",
1873
+ mode: "alert",
1874
+ visible: false,
1875
+ choice: () => {
1876
+ }
1877
+ });
1878
+ const showAlert = (msg, type, mode = "alert", duration) => new Promise((resolve) => {
1879
+ setAlert({
1880
+ msg,
1881
+ type,
1882
+ mode,
1883
+ visible: true,
1884
+ duration,
1885
+ choice: (res) => {
1886
+ setAlert((p) => ({ ...p, visible: false }));
1887
+ resolve(res);
1888
+ }
1889
+ });
1890
+ });
1891
+ (0, import_react9.useEffect)(() => {
1892
+ if (!alert.visible || !alert.duration) return;
1893
+ const timer = setTimeout(() => alert.choice("auto"), alert.duration);
1894
+ return () => clearTimeout(timer);
1895
+ }, [alert.visible, alert.duration]);
1896
+ const overrideStyle = {};
1897
+ if (background !== void 0) overrideStyle["--alert-bg"] = background;
1898
+ if (borderColor !== void 0) overrideStyle["--alert-border"] = borderColor;
1899
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(AlertContext.Provider, { value: { showAlert }, children: [
1900
+ children,
1901
+ alert.visible && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "fc-alert-overlay", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1902
+ "div",
1903
+ {
1904
+ className: `fc-alert fc-alert--${alert.type} fc-alert--${alert.mode}`,
1905
+ style: overrideStyle,
1906
+ children: [
1907
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "fc-alert__header", children: [
1908
+ ICONS[alert.type],
1909
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "fc-alert__title", children: "\u63D0\u793A" })
1910
+ ] }),
1911
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(RollingBox, { className: "fc-alert__msg", children: alert.msg }),
1912
+ alert.mode !== "toast" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "fc-alert__footer", children: [
1913
+ alert.mode === "confirm" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1914
+ Button,
1915
+ {
1916
+ variant: "secondary",
1917
+ size: "sm",
1918
+ onClick: () => alert.choice("no"),
1919
+ children: "\u53D6\u6D88"
1920
+ }
1921
+ ),
1922
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1923
+ Button,
1924
+ {
1925
+ variant: "primary",
1926
+ size: "sm",
1927
+ onClick: () => alert.choice("yes"),
1928
+ children: "\u786E\u5B9A"
1929
+ }
1930
+ )
1931
+ ] })
1932
+ ]
1933
+ }
1934
+ ) })
1935
+ ] });
1936
+ }
1937
+ var useAlert = () => (0, import_react9.useContext)(AlertContext);
1938
+
1939
+ // src/components/LazyLoad/LazyLoad.tsx
1940
+ var import_react10 = require("react");
1941
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1942
+ function lazyLoad(importFn, options = {}) {
1943
+ const { fallback = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(LazySpinner, {}), timeout = 1e4 } = options;
1944
+ const LazyComponent = (0, import_react10.lazy)(() => {
1945
+ let timeoutId;
1946
+ const loadPromise = Promise.race([
1947
+ importFn(),
1948
+ new Promise((_, reject) => {
1949
+ timeoutId = setTimeout(() => reject(new Error(`\u52A0\u8F7D\u8D85\u65F6`)), timeout);
1950
+ })
1951
+ ]).finally(() => clearTimeout(timeoutId));
1952
+ return loadPromise;
1953
+ });
1954
+ return (props) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react10.Suspense, { fallback, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(LazyComponent, { ...props }) });
1955
+ }
1956
+ function LazySpinner() {
1957
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "fc-lazy-spinner", children: [
1958
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fc-lazy-spinner-dot" }),
1959
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fc-lazy-spinner-dot" }),
1960
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fc-lazy-spinner-dot" })
1961
+ ] });
1962
+ }
1963
+
1964
+ // src/components/Card/Card.tsx
1965
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1966
+ var Card = ({
1967
+ image,
1968
+ imageSlot,
1969
+ imageHeight = 200,
1970
+ title,
1971
+ description,
1972
+ actions,
1973
+ extraInfo,
1974
+ variant = "default",
1975
+ hoverable = false,
1976
+ disabled = false,
1977
+ className = "",
1978
+ style,
1979
+ onClick
1980
+ }) => {
1981
+ const handleClick = () => {
1982
+ if (!disabled && onClick) {
1983
+ onClick();
1984
+ }
1985
+ };
1986
+ const classes = [
1987
+ "fc-card",
1988
+ `fc-card--${variant}`,
1989
+ hoverable && "fc-card--hoverable",
1990
+ disabled && "fc-card--disabled",
1991
+ onClick && "fc-card--clickable",
1992
+ className
1993
+ ].filter(Boolean).join(" ");
1994
+ const renderImage = () => {
1995
+ if (imageSlot) return imageSlot;
1996
+ if (image) {
1997
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1998
+ "img",
1999
+ {
2000
+ className: "fc-card__image",
2001
+ src: image,
2002
+ alt: typeof title === "string" ? title : "card image"
2003
+ }
2004
+ );
2005
+ }
2006
+ return null;
2007
+ };
2008
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: classes, style, onClick: handleClick, children: [
2009
+ renderImage() && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2010
+ "div",
2011
+ {
2012
+ className: "fc-card__image-wrapper",
2013
+ style: { height: imageHeight },
2014
+ children: renderImage()
2015
+ }
2016
+ ),
2017
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "fc-card__content", children: [
2018
+ title && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fc-card__title", children: title }),
2019
+ description && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fc-card__description", children: description }),
2020
+ extraInfo && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fc-card__extra-info", children: extraInfo }),
2021
+ actions && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "fc-card__actions", children: actions })
2022
+ ] })
2023
+ ] });
2024
+ };
2025
+
2026
+ // src/components/Bar/TabBar.tsx
2027
+ var import_react11 = require("react");
2028
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2029
+ var TabItemView = (0, import_react11.memo)(({
2030
+ item,
2031
+ isActive,
2032
+ closable,
2033
+ draggable,
2034
+ tabClassName,
2035
+ activeTabClassName,
2036
+ tabStyle,
2037
+ activeTabStyle,
2038
+ renderCloseIcon,
2039
+ onClick,
2040
+ onClose,
2041
+ onDragStart,
2042
+ onDragOver,
2043
+ onDrop,
2044
+ onDragEnd
2045
+ }) => {
2046
+ const showClose = item.closable !== void 0 ? item.closable : closable;
2047
+ const classes = [
2048
+ "fc-tab-bar__tab",
2049
+ isActive && "fc-tab-bar__tab--active",
2050
+ item.disabled && "fc-tab-bar__tab--disabled",
2051
+ draggable && !item.disabled && "fc-tab-bar__tab--draggable",
2052
+ tabClassName,
2053
+ isActive && activeTabClassName
2054
+ ].filter(Boolean).join(" ");
2055
+ const mergedStyle = {
2056
+ ...tabStyle,
2057
+ ...isActive ? activeTabStyle : void 0
2058
+ };
2059
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2060
+ "div",
2061
+ {
2062
+ className: classes,
2063
+ style: mergedStyle,
2064
+ onClick: () => !item.disabled && onClick(item.key),
2065
+ draggable: draggable && !item.disabled,
2066
+ onDragStart: (e) => onDragStart(e, item.key),
2067
+ onDragOver,
2068
+ onDrop: (e) => onDrop(e, item.key),
2069
+ onDragEnd,
2070
+ role: "tab",
2071
+ "aria-selected": isActive,
2072
+ "aria-disabled": item.disabled,
2073
+ tabIndex: item.disabled ? -1 : 0,
2074
+ children: [
2075
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "fc-tab-bar__tab-label", children: item.label }),
2076
+ showClose && !item.disabled && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2077
+ "span",
2078
+ {
2079
+ className: "fc-tab-bar__tab-close",
2080
+ onClick: (e) => onClose(e, item.key),
2081
+ role: "button",
2082
+ "aria-label": `\u5173\u95ED ${typeof item.label === "string" ? item.label : ""}`,
2083
+ children: renderCloseIcon ? renderCloseIcon(item.key) : "\xD7"
2084
+ }
2085
+ )
2086
+ ]
2087
+ }
2088
+ );
2089
+ });
2090
+ TabItemView.displayName = "TabItemView";
2091
+ var TabBar = (0, import_react11.memo)(({
2092
+ items,
2093
+ activeKey,
2094
+ variant = "attached",
2095
+ radius = "md",
2096
+ tabRadius,
2097
+ closable = false,
2098
+ addable = false,
2099
+ draggable = false,
2100
+ onChange,
2101
+ onClose,
2102
+ onAdd,
2103
+ onReorder,
2104
+ tabClassName,
2105
+ activeTabClassName,
2106
+ tabStyle,
2107
+ activeTabStyle,
2108
+ renderCloseIcon,
2109
+ renderAddButton,
2110
+ className = "",
2111
+ style,
2112
+ background,
2113
+ tabColor,
2114
+ tabHoverColor,
2115
+ tabHoverBackground,
2116
+ tabActiveColor,
2117
+ tabActiveBackground,
2118
+ activeIndicatorColor
2119
+ }) => {
2120
+ const colorVars = {
2121
+ "--tab-bar-bg": background,
2122
+ "--tab-color": tabColor,
2123
+ "--tab-hover-color": tabHoverColor,
2124
+ "--tab-hover-bg": tabHoverBackground,
2125
+ "--tab-active-color": tabActiveColor,
2126
+ "--tab-active-bg": tabActiveBackground,
2127
+ "--tab-active-indicator": activeIndicatorColor
2128
+ };
2129
+ const overrideStyle = {};
2130
+ for (const [key, value] of Object.entries(colorVars)) {
2131
+ if (value !== void 0) {
2132
+ overrideStyle[key] = value;
2133
+ }
2134
+ }
2135
+ const mergedStyle = { ...overrideStyle, ...style };
2136
+ const dragKeyRef = (0, import_react11.useRef)(null);
2137
+ const handleClick = (0, import_react11.useCallback)(
2138
+ (key) => onChange(key),
2139
+ [onChange]
2140
+ );
2141
+ const handleClose = (0, import_react11.useCallback)(
2142
+ (e, key) => {
2143
+ e.stopPropagation();
2144
+ onClose?.(key);
2145
+ },
2146
+ [onClose]
2147
+ );
2148
+ const handleDragStart = (0, import_react11.useCallback)(
2149
+ (e, key) => {
2150
+ dragKeyRef.current = key;
2151
+ e.dataTransfer.effectAllowed = "move";
2152
+ const target = e.currentTarget;
2153
+ requestAnimationFrame(() => target.classList.add("fc-tab-bar__tab--dragging"));
2154
+ },
2155
+ []
2156
+ );
2157
+ const handleDragOver = (0, import_react11.useCallback)((e) => {
2158
+ e.preventDefault();
2159
+ e.dataTransfer.dropEffect = "move";
2160
+ }, []);
2161
+ const handleDrop = (0, import_react11.useCallback)(
2162
+ (e, targetKey) => {
2163
+ e.preventDefault();
2164
+ const dragKey = dragKeyRef.current;
2165
+ if (!dragKey || dragKey === targetKey || !onReorder) return;
2166
+ const fromIndex = items.findIndex((i) => i.key === dragKey);
2167
+ const toIndex = items.findIndex((i) => i.key === targetKey);
2168
+ if (fromIndex === -1 || toIndex === -1) return;
2169
+ const reordered = [...items];
2170
+ const [moved] = reordered.splice(fromIndex, 1);
2171
+ reordered.splice(toIndex, 0, moved);
2172
+ onReorder(reordered);
2173
+ },
2174
+ [items, onReorder]
2175
+ );
2176
+ const handleDragEnd = (0, import_react11.useCallback)((e) => {
2177
+ dragKeyRef.current = null;
2178
+ e.currentTarget.classList.remove("fc-tab-bar__tab--dragging");
2179
+ }, []);
2180
+ const rootClasses = [
2181
+ "fc-tab-bar",
2182
+ `fc-tab-bar--${variant}`,
2183
+ `fc-tab-bar--radius-${radius}`,
2184
+ tabRadius && `fc-tab-bar--tab-radius-${tabRadius}`,
2185
+ className
2186
+ ].filter(Boolean).join(" ");
2187
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: rootClasses, style: mergedStyle, role: "tablist", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "fc-tab-bar__nav", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "fc-tab-bar__nav-wrap", children: [
2188
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2189
+ TabItemView,
2190
+ {
2191
+ item,
2192
+ isActive: activeKey === item.key,
2193
+ closable,
2194
+ draggable,
2195
+ tabClassName,
2196
+ activeTabClassName,
2197
+ tabStyle,
2198
+ activeTabStyle,
2199
+ renderCloseIcon,
2200
+ onClick: handleClick,
2201
+ onClose: handleClose,
2202
+ onDragStart: handleDragStart,
2203
+ onDragOver: handleDragOver,
2204
+ onDrop: handleDrop,
2205
+ onDragEnd: handleDragEnd
2206
+ },
2207
+ item.key
2208
+ )),
2209
+ addable && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2210
+ "div",
2211
+ {
2212
+ className: "fc-tab-bar__add-btn",
2213
+ onClick: onAdd,
2214
+ role: "button",
2215
+ "aria-label": "\u6DFB\u52A0\u6807\u7B7E",
2216
+ children: renderAddButton ? renderAddButton() : "+"
2217
+ }
2218
+ )
2219
+ ] }) }) });
2220
+ });
2221
+ TabBar.displayName = "TabBar";
2222
+
2223
+ // src/components/Tag/TagItem.tsx
2224
+ var import_react12 = require("react");
2225
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2226
+ function TagItem({
2227
+ schema,
2228
+ value,
2229
+ onChange,
2230
+ mode = "show",
2231
+ background,
2232
+ color,
2233
+ borderColor
2234
+ }) {
2235
+ const [editing, setEditing] = (0, import_react12.useState)(false);
2236
+ const [draft, setDraft] = (0, import_react12.useState)(() => value !== void 0 ? String(value) : "");
2237
+ const inputRef = (0, import_react12.useRef)(null);
2238
+ (0, import_react12.useEffect)(() => {
2239
+ if (!editing) setDraft(value !== void 0 ? String(value) : "");
2240
+ }, [value, editing]);
2241
+ const colorVars = {
2242
+ "--tag-bg": background,
2243
+ "--tag-color": color,
2244
+ "--tag-border": borderColor
2245
+ };
2246
+ const overrideStyle = {};
2247
+ for (const [k, v] of Object.entries(colorVars)) {
2248
+ if (v !== void 0) overrideStyle[k] = v;
2249
+ }
2250
+ if (schema.type === "boolean") {
2251
+ const boolVal = value === true || value === "true";
2252
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2253
+ "span",
2254
+ {
2255
+ className: "fc-tag-item fc-tag-item--boolean",
2256
+ style: overrideStyle,
2257
+ onClick: () => onChange?.(!boolVal),
2258
+ title: "\u70B9\u51FB\u5207\u6362",
2259
+ children: [
2260
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "fc-tag-item__name", children: schema.name }),
2261
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "fc-tag-item__sep", children: "\xB7" }),
2262
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: boolVal ? "fc-tag-item__bool-true" : "fc-tag-item__bool-false", children: boolVal ? "\u2713" : "\u2717" })
2263
+ ]
2264
+ }
2265
+ );
2266
+ }
2267
+ const commit = () => {
2268
+ if (schema.type === "number") {
2269
+ const n = parseFloat(draft);
2270
+ if (!isNaN(n)) {
2271
+ const clamped = schema.range_min != null && n < schema.range_min ? schema.range_min : schema.range_max != null && n > schema.range_max ? schema.range_max : n;
2272
+ onChange?.(clamped);
2273
+ }
2274
+ } else {
2275
+ onChange?.(draft);
2276
+ }
2277
+ if (mode === "show") setEditing(false);
2278
+ };
2279
+ const cancel = () => {
2280
+ setDraft(value !== void 0 ? String(value) : "");
2281
+ if (mode === "show") setEditing(false);
2282
+ };
2283
+ const isEditing = mode === "edit" || editing;
2284
+ if (isEditing) {
2285
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "fc-tag-item fc-tag-item--editing", style: overrideStyle, children: [
2286
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "fc-tag-item__name", children: schema.name }),
2287
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "fc-tag-item__sep", children: "\xB7" }),
2288
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2289
+ "input",
2290
+ {
2291
+ ref: inputRef,
2292
+ className: "fc-tag-item__input",
2293
+ type: schema.type === "number" ? "number" : "text",
2294
+ value: draft,
2295
+ min: schema.range_min ?? void 0,
2296
+ max: schema.range_max ?? void 0,
2297
+ autoFocus: true,
2298
+ onChange: (e) => setDraft(e.target.value),
2299
+ onBlur: commit,
2300
+ onKeyDown: (e) => {
2301
+ if (e.key === "Enter") {
2302
+ e.preventDefault();
2303
+ commit();
2304
+ }
2305
+ if (e.key === "Escape") {
2306
+ e.preventDefault();
2307
+ cancel();
2308
+ }
2309
+ }
2310
+ }
2311
+ )
2312
+ ] });
2313
+ }
2314
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2315
+ "span",
2316
+ {
2317
+ className: "fc-tag-item fc-tag-item--show",
2318
+ style: overrideStyle,
2319
+ onDoubleClick: () => {
2320
+ setDraft(value !== void 0 ? String(value) : "");
2321
+ setEditing(true);
2322
+ setTimeout(() => inputRef.current?.select(), 0);
2323
+ },
2324
+ title: "\u53CC\u51FB\u7F16\u8F91",
2325
+ children: [
2326
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "fc-tag-item__name", children: schema.name }),
2327
+ value !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2328
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "fc-tag-item__sep", children: "\xB7" }),
2329
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "fc-tag-item__value", children: String(value) })
2330
+ ] })
2331
+ ]
2332
+ }
2333
+ );
2334
+ }
2335
+
2336
+ // src/components/MarkdownEditor/MarkdownEditor.tsx
2337
+ var import_react_md_editor = __toESM(require("@uiw/react-md-editor"), 1);
2338
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2339
+ var TOOLBAR_COMMANDS = [
2340
+ import_react_md_editor.commands.bold,
2341
+ import_react_md_editor.commands.italic,
2342
+ import_react_md_editor.commands.strikethrough,
2343
+ import_react_md_editor.commands.divider,
2344
+ import_react_md_editor.commands.title1,
2345
+ import_react_md_editor.commands.title2,
2346
+ import_react_md_editor.commands.title3,
2347
+ import_react_md_editor.commands.divider,
2348
+ import_react_md_editor.commands.quote,
2349
+ import_react_md_editor.commands.code,
2350
+ import_react_md_editor.commands.codeBlock,
2351
+ import_react_md_editor.commands.divider,
2352
+ import_react_md_editor.commands.link,
2353
+ import_react_md_editor.commands.unorderedListCommand,
2354
+ import_react_md_editor.commands.orderedListCommand,
2355
+ import_react_md_editor.commands.hr
2356
+ ];
2357
+ function MarkdownEditor({
2358
+ value,
2359
+ onChange,
2360
+ onAiComplete,
2361
+ minHeight = 200,
2362
+ placeholder = "\u5728\u6B64\u8F93\u5165\u5185\u5BB9...",
2363
+ background,
2364
+ toolbarBackground,
2365
+ borderColor
2366
+ }) {
2367
+ const { resolvedTheme } = useTheme();
2368
+ const colorVars = {
2369
+ "--md-bg": background,
2370
+ "--md-toolbar-bg": toolbarBackground,
2371
+ "--md-border": borderColor
2372
+ };
2373
+ const overrideStyle = {};
2374
+ for (const [k, v] of Object.entries(colorVars)) {
2375
+ if (v !== void 0) overrideStyle[k] = v;
2376
+ }
2377
+ const aiCommand = {
2378
+ name: "ai-complete",
2379
+ keyCommand: "ai-complete",
2380
+ buttonProps: { "aria-label": "AI \u8865\u5168", className: "fc-md-ai-btn" },
2381
+ icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "AI \u8865\u5168" }),
2382
+ execute: () => onAiComplete?.()
2383
+ };
2384
+ const extraCommands = onAiComplete ? [import_react_md_editor.commands.divider, aiCommand, import_react_md_editor.commands.fullscreen] : [import_react_md_editor.commands.fullscreen];
2385
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2386
+ "div",
2387
+ {
2388
+ className: "fc-md-wrap",
2389
+ style: overrideStyle,
2390
+ "data-color-mode": resolvedTheme,
2391
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2392
+ import_react_md_editor.default,
2393
+ {
2394
+ value,
2395
+ onChange: (v) => onChange(v ?? ""),
2396
+ commands: TOOLBAR_COMMANDS,
2397
+ extraCommands,
2398
+ height: minHeight,
2399
+ preview: "edit",
2400
+ visibleDragbar: false,
2401
+ textareaProps: { placeholder }
2402
+ }
2403
+ )
2404
+ }
2405
+ );
2406
+ }
2407
+
2408
+ // src/components/ContextMenu/ContextMenuContext.tsx
2409
+ var import_react13 = require("react");
2410
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2411
+ var ContextMenuContext = (0, import_react13.createContext)(null);
2412
+ function ContextMenuProvider({
2413
+ children,
2414
+ background,
2415
+ borderColor,
2416
+ hoverBackground
2417
+ }) {
2418
+ const [menu, setMenu] = (0, import_react13.useState)({
2419
+ visible: false,
2420
+ x: 0,
2421
+ y: 0,
2422
+ items: []
2423
+ });
2424
+ const menuRef = (0, import_react13.useRef)(null);
2425
+ const showContextMenu = (e, items) => {
2426
+ e.preventDefault();
2427
+ e.stopPropagation();
2428
+ setMenu({ visible: true, x: e.clientX, y: e.clientY, items });
2429
+ };
2430
+ const hide = () => setMenu((s) => ({ ...s, visible: false }));
2431
+ (0, import_react13.useEffect)(() => {
2432
+ if (!menu.visible) return;
2433
+ const onPointerDown = (e) => {
2434
+ if (menuRef.current && !menuRef.current.contains(e.target)) hide();
2435
+ };
2436
+ const onKeyDown = (e) => {
2437
+ if (e.key === "Escape") hide();
2438
+ };
2439
+ window.addEventListener("pointerdown", onPointerDown);
2440
+ window.addEventListener("keydown", onKeyDown);
2441
+ return () => {
2442
+ window.removeEventListener("pointerdown", onPointerDown);
2443
+ window.removeEventListener("keydown", onKeyDown);
2444
+ };
2445
+ }, [menu.visible]);
2446
+ const getPosition = () => {
2447
+ const W = window.innerWidth;
2448
+ const H = window.innerHeight;
2449
+ const menuW = 180;
2450
+ const menuH = menu.items.length * 32 + 12;
2451
+ return {
2452
+ left: menu.x + menuW > W ? menu.x - menuW : menu.x,
2453
+ top: menu.y + menuH > H ? menu.y - menuH : menu.y
2454
+ };
2455
+ };
2456
+ const colorVars = {
2457
+ "--ctx-bg": background,
2458
+ "--ctx-border": borderColor,
2459
+ "--ctx-item-hover-bg": hoverBackground
2460
+ };
2461
+ const overrideStyle = { ...getPosition() };
2462
+ for (const [k, v] of Object.entries(colorVars)) {
2463
+ if (v !== void 0) overrideStyle[k] = v;
2464
+ }
2465
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(ContextMenuContext.Provider, { value: { showContextMenu }, children: [
2466
+ children,
2467
+ menu.visible && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2468
+ "ul",
2469
+ {
2470
+ ref: menuRef,
2471
+ className: "fc-context-menu",
2472
+ style: overrideStyle,
2473
+ children: menu.items.map((item, i) => {
2474
+ if ("type" in item && item.type === "divider") {
2475
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { className: "fc-context-menu__divider" }, i);
2476
+ }
2477
+ const action = item;
2478
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2479
+ "li",
2480
+ {
2481
+ className: [
2482
+ "fc-context-menu__item",
2483
+ action.danger ? "fc-context-menu__item--danger" : "",
2484
+ action.disabled ? "fc-context-menu__item--disabled" : ""
2485
+ ].filter(Boolean).join(" "),
2486
+ onPointerDown: (e) => e.stopPropagation(),
2487
+ onClick: () => {
2488
+ if (action.disabled) return;
2489
+ action.onClick();
2490
+ hide();
2491
+ },
2492
+ children: [
2493
+ action.icon && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "fc-context-menu__icon", children: action.icon }),
2494
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "fc-context-menu__label", children: action.label })
2495
+ ]
2496
+ },
2497
+ i
2498
+ );
2499
+ })
2500
+ }
2501
+ )
2502
+ ] });
2503
+ }
2504
+ var useContextMenu = () => (0, import_react13.useContext)(ContextMenuContext);
2505
+
2506
+ // src/components/Chat/Chat.tsx
2507
+ var import_react15 = require("react");
2508
+
2509
+ // src/components/SmartMessage/SmartMessage.tsx
2510
+ var import_react14 = require("react");
2511
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2512
+ var SmartMessage = (0, import_react14.memo)(({
2513
+ id,
2514
+ content,
2515
+ role,
2516
+ timestamp,
2517
+ status,
2518
+ toolName,
2519
+ toolResult,
2520
+ onCopy,
2521
+ className = "",
2522
+ style = {}
2523
+ }) => {
2524
+ const [copied, setCopied] = (0, import_react14.useState)(false);
2525
+ const formattedTime = timestamp ? new Date(timestamp).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }) : "";
2526
+ const handleCopy = async () => {
2527
+ try {
2528
+ let copyContent = content;
2529
+ if (role === "tool" && toolResult) {
2530
+ copyContent = `\u5DE5\u5177: ${toolName || "\u5DE5\u5177\u8C03\u7528"}
2531
+ \u7ED3\u679C: ${JSON.stringify(toolResult, null, 2)}`;
2532
+ }
2533
+ await navigator.clipboard.writeText(copyContent);
2534
+ setCopied(true);
2535
+ onCopy?.(content, role);
2536
+ setTimeout(() => setCopied(false), 2e3);
2537
+ } catch (err) {
2538
+ console.error("\u590D\u5236\u5931\u8D25:", err);
2539
+ }
2540
+ };
2541
+ const shouldShowCopyButton = () => {
2542
+ return role === "user" || role === "assistant";
2543
+ };
2544
+ const getContainerClassName = () => {
2545
+ const baseClass = "smart-message";
2546
+ const roleClass = `smart-message-${role}`;
2547
+ const statusClass = status ? `smart-message-${status}` : "";
2548
+ return `${baseClass} ${roleClass} ${statusClass} ${className}`.trim();
2549
+ };
2550
+ const getContentClassName = () => {
2551
+ const baseClass = "smart-message-content";
2552
+ const roleContentClass = `smart-message-content-${role}`;
2553
+ return `${baseClass} ${roleContentClass}`;
2554
+ };
2555
+ const renderSystemMessage = () => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "system-message-wrapper", children: [
2556
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "system-message-icon", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
2557
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }),
2558
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M12 8V12M12 16H12.01", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2559
+ ] }) }),
2560
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "system-message-text", children: content }),
2561
+ formattedTime && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "system-message-time", children: formattedTime })
2562
+ ] });
2563
+ const renderToolMessage = () => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "tool-message-wrapper", children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "tool-message-header", children: [
2565
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "tool-message-icon", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
2566
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M14.7 6.3L19 2L22 5L17.7 9.3L14.7 6.3Z", stroke: "currentColor", strokeWidth: "2" }),
2567
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M9.3 17.7L5 22L2 19L6.3 14.7L9.3 17.7Z", stroke: "currentColor", strokeWidth: "2" }),
2568
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M12 12L14.7 9.3M12 12L9.3 14.7M12 12L8 8M12 12L16 16", stroke: "currentColor", strokeWidth: "2" })
2569
+ ] }) }),
2570
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "tool-message-name", children: toolName || "\u5DE5\u5177\u8C03\u7528" })
2571
+ ] }),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "tool-message-content", children: [
2573
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "tool-message-params", children: [
2574
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("strong", { children: "\u53C2\u6570:" }),
2575
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("pre", { children: content })
2576
+ ] }),
2577
+ toolResult && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "tool-message-result", children: [
2578
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("strong", { children: "\u7ED3\u679C:" }),
2579
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("pre", { children: typeof toolResult === "object" ? JSON.stringify(toolResult, null, 2) : toolResult })
2580
+ ] })
2581
+ ] }),
2582
+ formattedTime && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "tool-message-time", children: formattedTime })
2583
+ ] });
2584
+ const renderUserAssistantMessage = () => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
2585
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "message-header", children: [
2586
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "message-sender", children: role === "user" ? "\u6211" : "AI\u52A9\u624B" }),
2587
+ formattedTime && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "message-time", children: formattedTime })
2588
+ ] }),
2589
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: getContentClassName(), children: [
2590
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "message-text", children: content }),
2591
+ shouldShowCopyButton() && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2592
+ "button",
2593
+ {
2594
+ className: `copy-btn ${copied ? "copied" : ""}`,
2595
+ onClick: handleCopy,
2596
+ title: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5185\u5BB9",
2597
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
2598
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M20 6L9 17L4 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
2599
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: "\u5DF2\u590D\u5236" })
2600
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
2601
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
2602
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", stroke: "currentColor", strokeWidth: "2" }),
2603
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("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" })
2604
+ ] }),
2605
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: "\u590D\u5236" })
2606
+ ] })
2607
+ }
2608
+ ),
2609
+ status === "sending" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "message-status sending", children: "\u53D1\u9001\u4E2D..." }),
2610
+ status === "error" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "message-status error", children: "\u53D1\u9001\u5931\u8D25" })
2611
+ ] })
2612
+ ] });
2613
+ const renderMessage = () => {
2614
+ switch (role) {
2615
+ case "system":
2616
+ return renderSystemMessage();
2617
+ case "tool":
2618
+ return renderToolMessage();
2619
+ case "user":
2620
+ case "assistant":
2621
+ default:
2622
+ return renderUserAssistantMessage();
2623
+ }
2624
+ };
2625
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: getContainerClassName(), style, "data-message-id": id, children: renderMessage() });
2626
+ });
2627
+ SmartMessage.displayName = "SmartMessage";
2628
+ var SmartMessage_default = SmartMessage;
2629
+
2630
+ // src/components/Chat/Chat.tsx
2631
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2632
+ var Chat = ({
2633
+ messages = [],
2634
+ title = "AI \u52A9\u624B",
2635
+ loading = false,
2636
+ conversations = [],
2637
+ currentConversationId,
2638
+ emptyText = "\u6682\u65E0\u5386\u53F2\u5BF9\u8BDD",
2639
+ newConversationText = "\u65B0\u5EFA\u5BF9\u8BDD",
2640
+ historyTitle = "\u5386\u53F2\u5BF9\u8BDD",
2641
+ showHistoryButton = true,
2642
+ showMinimizeButton = true,
2643
+ showHeader = true,
2644
+ showFooter = false,
2645
+ autoScroll = true,
2646
+ onSwitchConversation,
2647
+ onNewConversation,
2648
+ onDeleteConversation,
2649
+ onMinimize,
2650
+ onRestore,
2651
+ onMessageCopy,
2652
+ className = "",
2653
+ style = {},
2654
+ headerClassName = "",
2655
+ headerStyle = {},
2656
+ messagesClassName = "",
2657
+ messagesStyle = {},
2658
+ bubbleClassName = "",
2659
+ height = "600px",
2660
+ width
2661
+ }) => {
2662
+ const [showHistory, setShowHistory] = (0, import_react15.useState)(false);
2663
+ const [isMinimized, setIsMinimized] = (0, import_react15.useState)(false);
2664
+ const messagesContainerRef = (0, import_react15.useRef)(null);
2665
+ const messagesEndRef = (0, import_react15.useRef)(null);
2666
+ const historyPanelRef = (0, import_react15.useRef)(null);
2667
+ const currentConversation = (0, import_react15.useMemo)(
2668
+ () => conversations.find((c) => c.id === currentConversationId),
2669
+ [conversations, currentConversationId]
2670
+ );
2671
+ const currentTitle = currentConversation?.title || title;
2672
+ (0, import_react15.useEffect)(() => {
2673
+ if (autoScroll && messagesContainerRef.current && !showHistory && !isMinimized) {
2674
+ messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
2675
+ }
2676
+ }, [messages, loading, showHistory, isMinimized, autoScroll]);
2677
+ (0, import_react15.useEffect)(() => {
2678
+ const handleClickOutside = (event) => {
2679
+ if (showHistory && historyPanelRef.current && !historyPanelRef.current.contains(event.target)) {
2680
+ setShowHistory(false);
2681
+ }
2682
+ };
2683
+ document.addEventListener("mousedown", handleClickOutside);
2684
+ return () => document.removeEventListener("mousedown", handleClickOutside);
2685
+ }, [showHistory]);
2686
+ const handleDeleteConversation = (0, import_react15.useCallback)((conversationId) => {
2687
+ onDeleteConversation?.(conversationId);
2688
+ }, [onDeleteConversation]);
2689
+ const handleMinimize = (0, import_react15.useCallback)(() => {
2690
+ setIsMinimized(true);
2691
+ onMinimize?.();
2692
+ }, [onMinimize]);
2693
+ const handleRestore = (0, import_react15.useCallback)(() => {
2694
+ setIsMinimized(false);
2695
+ onRestore?.();
2696
+ }, [onRestore]);
2697
+ const handleCopy = (0, import_react15.useCallback)((content) => {
2698
+ const message = messages.find((m) => m.content === content);
2699
+ if (message) {
2700
+ onMessageCopy?.(message);
2701
+ }
2702
+ }, [messages, onMessageCopy]);
2703
+ const containerStyle = (0, import_react15.useMemo)(() => ({
2704
+ height,
2705
+ width,
2706
+ ...style
2707
+ }), [height, width, style]);
2708
+ if (isMinimized) {
2709
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: `chat-container-minimized ${className}`, style: containerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { className: "restore-btn-only", onClick: handleRestore, title: "\u5C55\u5F00", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { d: "M12 5V19M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }) });
2710
+ }
2711
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: `chat-container ${className}`, style: containerStyle, children: [
2712
+ showHeader && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: `chat-header ${headerClassName}`, style: headerStyle, children: [
2713
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "chat-title", children: currentTitle }),
2714
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "chat-header-actions", children: [
2715
+ showHistoryButton && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { className: "history-btn", onClick: () => setShowHistory(!showHistory), title: "\u5386\u53F2\u5BF9\u8BDD", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: [
2716
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { d: "M12 8V12L15 15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2717
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeWidth: "2" })
2718
+ ] }) }),
2719
+ showMinimizeButton && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { className: "minimize-btn", onClick: handleMinimize, title: "\u6700\u5C0F\u5316", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { d: "M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2720
+ ] })
2721
+ ] }),
2722
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: `chat-messages ${messagesClassName}`, ref: messagesContainerRef, style: messagesStyle, children: [
2723
+ messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2724
+ SmartMessage_default,
2725
+ {
2726
+ id: message.id,
2727
+ content: message.content,
2728
+ role: message.type,
2729
+ timestamp: message.timestamp,
2730
+ status: message.status,
2731
+ toolName: message.toolName,
2732
+ toolResult: message.toolResult,
2733
+ onCopy: handleCopy,
2734
+ className: bubbleClassName
2735
+ },
2736
+ message.id
2737
+ )),
2738
+ loading && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "typing-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "typing-indicator", children: [
2739
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", {}),
2740
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", {}),
2741
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", {})
2742
+ ] }) }),
2743
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { ref: messagesEndRef })
2744
+ ] }),
2745
+ showFooter && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "chat-footer" }),
2746
+ showHistory && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "history-panel", ref: historyPanelRef, children: [
2747
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "history-header", children: [
2748
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h3", { children: historyTitle }),
2749
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { className: "close-history-btn", onClick: () => setShowHistory(false), children: "\u2715" })
2750
+ ] }),
2751
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "history-list", children: [
2752
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("button", { className: "new-conversation-btn-large", onClick: () => {
2753
+ onNewConversation?.();
2754
+ setShowHistory(false);
2755
+ }, children: [
2756
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { d: "M12 5V19M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
2757
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: newConversationText })
2758
+ ] }),
2759
+ conversations.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "empty-history", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { children: emptyText }) }) : conversations.map((conv) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2760
+ "div",
2761
+ {
2762
+ className: `history-item ${currentConversationId === conv.id ? "active" : ""}`,
2763
+ onClick: () => {
2764
+ onSwitchConversation?.(conv.id);
2765
+ setShowHistory(false);
2766
+ },
2767
+ children: [
2768
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "history-item-content", children: [
2769
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "history-item-title", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: conv.title }) }),
2770
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "history-item-preview", children: conv.lastMessage }),
2771
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "history-item-meta", children: [
2772
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: new Date(conv.timestamp).toLocaleDateString() }),
2773
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { children: [
2774
+ conv.messages.length,
2775
+ "\u6761\u6D88\u606F"
2776
+ ] })
2777
+ ] })
2778
+ ] }),
2779
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "history-item-actions", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2780
+ "button",
2781
+ {
2782
+ className: "delete-conversation-btn",
2783
+ onClick: (e) => {
2784
+ e.stopPropagation();
2785
+ handleDeleteConversation(conv.id);
2786
+ },
2787
+ title: "\u5220\u9664\u5BF9\u8BDD",
2788
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
2789
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { d: "M3 6H21", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2790
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { d: "M19 6V20C19 21.1 18.1 22 17 22H7C5.9 22 5 21.1 5 20V6", stroke: "currentColor", strokeWidth: "2" }),
2791
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { d: "M8 6V4C8 2.9 8.9 2 10 2H14C15.1 2 16 2.9 16 4V6", stroke: "currentColor", strokeWidth: "2" })
2792
+ ] })
2793
+ }
2794
+ ) })
2795
+ ]
2796
+ },
2797
+ conv.id
2798
+ ))
2799
+ ] })
2800
+ ] })
2801
+ ] });
2802
+ };
48
2803
  // Annotate the CommonJS export names for ESM import in node:
49
2804
  0 && (module.exports = {
50
- Button
2805
+ AlertProvider,
2806
+ Avatar,
2807
+ Button,
2808
+ ButtonGroup,
2809
+ ButtonToolbar,
2810
+ Card,
2811
+ Chat,
2812
+ CheckButton,
2813
+ ContextMenuProvider,
2814
+ DeleteDialog,
2815
+ Input,
2816
+ ListGroup,
2817
+ ListGroupItem,
2818
+ MarkdownEditor,
2819
+ OrphanDialog,
2820
+ RollingBox,
2821
+ Select,
2822
+ SideBar,
2823
+ Slider,
2824
+ SmartMessage,
2825
+ TabBar,
2826
+ TagItem,
2827
+ ThemeProvider,
2828
+ Tree,
2829
+ VirtualList,
2830
+ findNodeInfo,
2831
+ flatToTree,
2832
+ isDescendantOf,
2833
+ lazyLoad,
2834
+ useAlert,
2835
+ useContextMenu,
2836
+ useTheme
51
2837
  });