flowcloudai-ui 0.1.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.js CHANGED
@@ -49,10 +49,39 @@ function Button({
49
49
  iconOnly = false,
50
50
  iconLeft,
51
51
  iconRight,
52
+ background,
53
+ hoverBackground,
54
+ activeBackground,
55
+ color,
56
+ hoverColor,
57
+ activeColor,
58
+ borderColor,
59
+ hoverBorderColor,
52
60
  className,
61
+ style,
53
62
  children,
54
63
  ...props
55
64
  }) {
65
+ const colorVars = {
66
+ "--btn-bg": background,
67
+ "--btn-bg-hover": hoverBackground,
68
+ "--btn-bg-active": activeBackground,
69
+ "--btn-color": color,
70
+ "--btn-color-hover": hoverColor,
71
+ "--btn-color-active": activeColor,
72
+ "--btn-border": borderColor,
73
+ "--btn-border-hover": hoverBorderColor
74
+ };
75
+ const overrideStyle = {};
76
+ for (const [key, value] of Object.entries(colorVars)) {
77
+ if (value !== void 0) {
78
+ overrideStyle[key] = value;
79
+ }
80
+ }
81
+ const mergedStyle = {
82
+ ...overrideStyle,
83
+ ...style
84
+ };
56
85
  const classNames = [
57
86
  "fc-btn",
58
87
  `fc-btn--${variant}`,
@@ -69,6 +98,7 @@ function Button({
69
98
  {
70
99
  className: classNames,
71
100
  disabled: disabled || loading,
101
+ style: mergedStyle,
72
102
  ...props,
73
103
  children: [
74
104
  iconLeft && !loading && /* @__PURE__ */ jsx2("span", { className: "fc-btn__icon fc-btn__icon--left", children: iconLeft }),
@@ -107,6 +137,11 @@ function CheckButton({
107
137
  size = "md",
108
138
  labelLeft,
109
139
  labelRight,
140
+ trackBackground,
141
+ checkedTrackBackground,
142
+ thumbBackground,
143
+ thumbDotColor,
144
+ labelColor,
110
145
  className = "",
111
146
  style
112
147
  }) {
@@ -125,6 +160,23 @@ function CheckButton({
125
160
  toggle();
126
161
  }
127
162
  };
163
+ const colorVars = {
164
+ "--check-track-bg": trackBackground,
165
+ "--check-track-bg-checked": checkedTrackBackground,
166
+ "--check-thumb-bg": thumbBackground,
167
+ "--check-thumb-dot-color": thumbDotColor,
168
+ "--check-label-color": labelColor
169
+ };
170
+ const overrideStyle = {};
171
+ for (const [key, value] of Object.entries(colorVars)) {
172
+ if (value !== void 0) {
173
+ overrideStyle[key] = value;
174
+ }
175
+ }
176
+ const mergedStyle = {
177
+ ...overrideStyle,
178
+ ...style
179
+ };
128
180
  const cls = [
129
181
  "fc-check",
130
182
  `fc-check--${size}`,
@@ -136,7 +188,7 @@ function CheckButton({
136
188
  "div",
137
189
  {
138
190
  className: cls,
139
- style,
191
+ style: mergedStyle,
140
192
  role: "switch",
141
193
  "aria-checked": checked,
142
194
  tabIndex: disabled ? -1 : 0,
@@ -162,8 +214,26 @@ function RollingBox({
162
214
  showTrack = false,
163
215
  children,
164
216
  className,
217
+ thumbColor,
218
+ thumbHoverColor,
219
+ thumbActiveColor,
220
+ trackColor,
221
+ style,
165
222
  ...props
166
223
  }) {
224
+ const colorVars = {
225
+ "--roll-thumb": thumbColor,
226
+ "--roll-thumb-hover": thumbHoverColor,
227
+ "--roll-thumb-active": thumbActiveColor,
228
+ "--roll-track": trackColor
229
+ };
230
+ const overrideStyle = {};
231
+ for (const [key, value] of Object.entries(colorVars)) {
232
+ if (value !== void 0) {
233
+ overrideStyle[key] = value;
234
+ }
235
+ }
236
+ const mergedStyle = { ...overrideStyle, ...style };
167
237
  const containerRef = React2.useRef(null);
168
238
  const [isScrolling, setIsScrolling] = React2.useState(false);
169
239
  const scrollTimeoutRef = React2.useRef(null);
@@ -199,6 +269,7 @@ function RollingBox({
199
269
  {
200
270
  ref: containerRef,
201
271
  className: classNames,
272
+ style: mergedStyle,
202
273
  onScroll: handleScroll,
203
274
  ...props,
204
275
  children: /* @__PURE__ */ jsx4("div", { className: "fc-roll__content", children })
@@ -207,134 +278,100 @@ function RollingBox({
207
278
  }
208
279
 
209
280
  // src/components/Bar/SideBar.tsx
210
- import * as React3 from "react";
281
+ import { memo, useCallback as useCallback2 } from "react";
211
282
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
212
- function SideBar({
213
- items,
214
- selectedKeys: controlledSelected,
215
- defaultSelectedKeys = [],
216
- openKeys: controlledOpen,
217
- defaultOpenKeys = [],
218
- onSelect,
219
- onOpenChange,
220
- collapsed: controlledCollapsed,
221
- defaultCollapsed = false,
222
- onCollapse,
223
- className,
224
- width = 240,
225
- collapsedWidth = 64
226
- }) {
227
- const [internalSelected, setInternalSelected] = React3.useState(defaultSelectedKeys);
228
- const [internalOpen, setInternalOpen] = React3.useState(defaultOpenKeys);
229
- const [internalCollapsed, setInternalCollapsed] = React3.useState(defaultCollapsed);
230
- const currentSelected = controlledSelected ?? internalSelected;
231
- const currentOpen = controlledOpen ?? internalOpen;
232
- const currentCollapsed = controlledCollapsed ?? internalCollapsed;
233
- const updateOpen = React3.useCallback((next) => {
234
- if (controlledOpen === void 0) setInternalOpen(next);
235
- onOpenChange?.(next);
236
- }, [controlledOpen, onOpenChange]);
237
- const toggleOpen = React3.useCallback((key) => {
238
- const next = currentOpen.includes(key) ? currentOpen.filter((k) => k !== key) : [...currentOpen, key];
239
- updateOpen(next);
240
- }, [currentOpen, updateOpen]);
241
- const handleSelect = React3.useCallback((key, item) => {
242
- if (item.disabled) return;
243
- const next = [key];
244
- if (controlledSelected === void 0) setInternalSelected(next);
245
- onSelect?.(next);
246
- }, [controlledSelected, onSelect]);
247
- const toggleCollapse = React3.useCallback(() => {
248
- const next = !currentCollapsed;
249
- if (controlledCollapsed === void 0) setInternalCollapsed(next);
250
- onCollapse?.(next);
251
- }, [currentCollapsed, controlledCollapsed, onCollapse]);
252
- const handleItemClick = React3.useCallback((item) => {
253
- if (item.children?.length) {
254
- toggleOpen(item.key);
255
- } else {
256
- handleSelect(item.key, item);
257
- }
258
- }, [toggleOpen, handleSelect]);
259
- const handleItemKeyDown = React3.useCallback((e, item) => {
260
- if (e.key === "Enter" || e.key === " ") {
261
- e.preventDefault();
262
- handleItemClick(item);
263
- }
264
- }, [handleItemClick]);
265
- const renderMenuItem = (item, level = 0) => {
266
- const hasChildren = (item.children?.length ?? 0) > 0;
267
- const isOpen = currentOpen.includes(item.key);
268
- const isSelected = currentSelected.includes(item.key);
269
- const itemClassName = [
270
- "fc-sidebar__item",
271
- `fc-sidebar__item--level-${level}`,
272
- hasChildren && "fc-sidebar__item--parent",
273
- isOpen && "fc-sidebar__item--open",
274
- isSelected && "fc-sidebar__item--selected",
275
- item.disabled && "fc-sidebar__item--disabled"
276
- ].filter(Boolean).join(" ");
277
- const Tag = item.href ? "a" : "div";
278
- const interactiveProps = {
279
- role: hasChildren ? "treeitem" : "menuitem",
280
- tabIndex: item.disabled ? -1 : 0,
281
- "aria-selected": isSelected || void 0,
282
- "aria-expanded": hasChildren ? isOpen : void 0,
283
- "aria-disabled": item.disabled || void 0,
284
- onClick: () => handleItemClick(item),
285
- onKeyDown: (e) => handleItemKeyDown(e, item),
286
- ...item.href ? { href: item.href } : {}
287
- };
288
- return /* @__PURE__ */ jsxs3("div", { className: "fc-sidebar__item-wrapper", children: [
289
- /* @__PURE__ */ jsxs3(Tag, { className: itemClassName, ...interactiveProps, children: [
290
- item.icon && /* @__PURE__ */ jsx5("span", { className: "fc-sidebar__icon", "aria-hidden": "true", children: item.icon }),
291
- /* @__PURE__ */ jsx5("span", { className: "fc-sidebar__label", children: item.label }),
292
- hasChildren && /* @__PURE__ */ jsx5("span", { className: "fc-sidebar__arrow", "aria-hidden": "true", children: "\u25B6" })
293
- ] }),
294
- hasChildren && /* @__PURE__ */ jsx5(
295
- "div",
296
- {
297
- className: [
298
- "fc-sidebar__submenu",
299
- isOpen && "fc-sidebar__submenu--open"
300
- ].filter(Boolean).join(" "),
301
- role: "group",
302
- children: /* @__PURE__ */ jsx5("div", { className: "fc-sidebar__submenu-inner", children: item.children.map((child) => renderMenuItem(child, level + 1)) })
303
- }
304
- )
305
- ] }, item.key);
306
- };
307
- const sidebarStyle = {
308
- "--sidebar-width": `${currentCollapsed ? collapsedWidth : width}px`,
309
- "--sidebar-collapsed-width": `${collapsedWidth}px`
310
- };
283
+ var SideBarItemView = memo(({ item, isSelected, onClick }) => {
284
+ const classes = [
285
+ "fc-sidebar__item",
286
+ isSelected && "fc-sidebar__item--selected",
287
+ item.disabled && "fc-sidebar__item--disabled"
288
+ ].filter(Boolean).join(" ");
289
+ const Tag = item.href ? "a" : "div";
290
+ const linkProps = item.href ? { href: item.href } : {};
311
291
  return /* @__PURE__ */ jsxs3(
312
- "aside",
292
+ Tag,
313
293
  {
314
- className: [
315
- "fc-sidebar",
316
- currentCollapsed && "fc-sidebar--collapsed",
317
- className
318
- ].filter(Boolean).join(" "),
319
- style: sidebarStyle,
320
- role: "navigation",
321
- "aria-label": "Sidebar",
294
+ className: classes,
295
+ onClick: () => !item.disabled && onClick(item.key),
296
+ ...linkProps,
322
297
  children: [
323
- /* @__PURE__ */ jsx5("div", { className: "fc-sidebar__header", children: /* @__PURE__ */ jsx5(
324
- "button",
325
- {
326
- className: "fc-sidebar__collapse-btn",
327
- onClick: toggleCollapse,
328
- "aria-label": currentCollapsed ? "\u5C55\u5F00\u4FA7\u680F" : "\u6536\u8D77\u4FA7\u680F",
329
- "aria-expanded": !currentCollapsed,
330
- children: currentCollapsed ? "\u2192" : "\u2190"
331
- }
332
- ) }),
333
- /* @__PURE__ */ jsx5("nav", { className: "fc-sidebar__menu", role: "tree", children: items.map((item) => renderMenuItem(item)) })
298
+ item.icon && /* @__PURE__ */ jsx5("span", { className: "fc-sidebar__icon", children: item.icon }),
299
+ /* @__PURE__ */ jsx5("span", { className: "fc-sidebar__label", children: item.label })
334
300
  ]
335
301
  }
336
302
  );
337
- }
303
+ });
304
+ SideBarItemView.displayName = "SideBarItemView";
305
+ var SideBar = memo(({
306
+ items,
307
+ selectedKey,
308
+ collapsed,
309
+ width = 240,
310
+ collapsedWidth = 64,
311
+ onSelect,
312
+ onCollapse,
313
+ className = "",
314
+ style
315
+ }) => {
316
+ const handleClick = useCallback2(
317
+ (key) => onSelect(key),
318
+ [onSelect]
319
+ );
320
+ const toggleCollapse = useCallback2(
321
+ () => onCollapse(!collapsed),
322
+ [collapsed, onCollapse]
323
+ );
324
+ const rootClasses = [
325
+ "fc-sidebar",
326
+ collapsed && "fc-sidebar--collapsed",
327
+ className
328
+ ].filter(Boolean).join(" ");
329
+ const rootStyle = {
330
+ "--sidebar-width": `${collapsed ? collapsedWidth : width}px`,
331
+ "--sidebar-collapsed-width": `${collapsedWidth}px`,
332
+ ...style
333
+ };
334
+ return /* @__PURE__ */ jsxs3("aside", { className: rootClasses, style: rootStyle, children: [
335
+ /* @__PURE__ */ jsx5("div", { className: "fc-sidebar__header", children: /* @__PURE__ */ jsx5(
336
+ "button",
337
+ {
338
+ className: "fc-sidebar__collapse-btn",
339
+ onClick: toggleCollapse,
340
+ children: /* @__PURE__ */ jsx5(
341
+ "svg",
342
+ {
343
+ className: "fc-sidebar__collapse-icon",
344
+ width: "16",
345
+ height: "16",
346
+ viewBox: "0 0 16 16",
347
+ fill: "none",
348
+ xmlns: "http://www.w3.org/2000/svg",
349
+ children: /* @__PURE__ */ jsx5(
350
+ "path",
351
+ {
352
+ d: collapsed ? "M6 3L11 8L6 13" : "M10 3L5 8L10 13",
353
+ stroke: "currentColor",
354
+ strokeWidth: "1.5",
355
+ strokeLinecap: "round",
356
+ strokeLinejoin: "round"
357
+ }
358
+ )
359
+ }
360
+ )
361
+ }
362
+ ) }),
363
+ /* @__PURE__ */ jsx5("nav", { className: "fc-sidebar__menu", children: items.map((item) => /* @__PURE__ */ jsx5(
364
+ SideBarItemView,
365
+ {
366
+ item,
367
+ isSelected: selectedKey === item.key,
368
+ onClick: handleClick
369
+ },
370
+ item.key
371
+ )) })
372
+ ] });
373
+ });
374
+ SideBar.displayName = "SideBar";
338
375
 
339
376
  // src/components/Input/Input.tsx
340
377
  import * as React4 from "react";
@@ -402,8 +439,8 @@ var Input = React4.forwardRef(({
402
439
  );
403
440
  return /* @__PURE__ */ jsxs4("div", { className: classNames, children: [
404
441
  addonBefore && /* @__PURE__ */ jsx6("span", { className: "fc-input__addon fc-input__addon--before", children: addonBefore }),
405
- prefix && /* @__PURE__ */ jsx6("span", { className: "fc-input__prefix", children: prefix }),
406
442
  /* @__PURE__ */ jsxs4("div", { className: "fc-input__wrapper", children: [
443
+ prefix && /* @__PURE__ */ jsx6("span", { className: "fc-input__prefix", children: prefix }),
407
444
  input,
408
445
  /* @__PURE__ */ jsxs4("span", { className: "fc-input__actions", children: [
409
446
  showClear && /* @__PURE__ */ jsx6(
@@ -450,7 +487,16 @@ function Slider({
450
487
  disabled = false,
451
488
  marks,
452
489
  tooltip = false,
453
- className = ""
490
+ className = "",
491
+ style,
492
+ trackBackground,
493
+ fillBackground,
494
+ thumbBackground,
495
+ thumbBorderColor,
496
+ markDotColor,
497
+ markLabelColor,
498
+ tooltipBackground,
499
+ tooltipColor
454
500
  }) {
455
501
  const trackRef = React5.useRef(null);
456
502
  const draggingRef = React5.useRef(null);
@@ -459,6 +505,23 @@ function Slider({
459
505
  const [internalValue, setInternalValue] = React5.useState(initialValue);
460
506
  const isControlled = controlledValue !== void 0;
461
507
  const currentValue = isControlled ? controlledValue : internalValue;
508
+ const colorVars = {
509
+ "--slider-track-bg": trackBackground,
510
+ "--slider-fill-bg": fillBackground,
511
+ "--slider-thumb-bg": thumbBackground,
512
+ "--slider-thumb-border": thumbBorderColor,
513
+ "--slider-mark-dot-bg": markDotColor,
514
+ "--slider-mark-label-color": markLabelColor,
515
+ "--slider-tooltip-bg": tooltipBackground,
516
+ "--slider-tooltip-color": tooltipColor
517
+ };
518
+ const overrideStyle = {};
519
+ for (const [key, value] of Object.entries(colorVars)) {
520
+ if (value !== void 0) {
521
+ overrideStyle[key] = value;
522
+ }
523
+ }
524
+ const mergedStyle = { ...overrideStyle, ...style };
462
525
  const getPercent = (val) => Math.max(0, Math.min(100, (val - min) / (max - min) * 100));
463
526
  const getValueFromPercent = (percent) => {
464
527
  const raw = min + percent / 100 * (max - min);
@@ -512,7 +575,7 @@ function Slider({
512
575
  dragging !== null && "fc-slider--dragging",
513
576
  className
514
577
  ].filter(Boolean).join(" ");
515
- return /* @__PURE__ */ jsx7("div", { className: cls, children: /* @__PURE__ */ jsxs5(
578
+ return /* @__PURE__ */ jsx7("div", { className: cls, style: mergedStyle, children: /* @__PURE__ */ jsxs5(
516
579
  "div",
517
580
  {
518
581
  ref: trackRef,
@@ -574,10 +637,30 @@ function Select({
574
637
  multiple = false,
575
638
  disabled = false,
576
639
  className = "",
640
+ style,
577
641
  virtualScroll = false,
578
642
  virtualItemHeight = 32,
579
- maxHeight = 256
643
+ maxHeight = 256,
644
+ triggerBackground,
645
+ triggerBorderColor,
646
+ selectedColor,
647
+ selectedBackground,
648
+ hoverBackground
580
649
  }) {
650
+ const colorVars = {
651
+ "--select-trigger-bg": triggerBackground,
652
+ "--select-trigger-border": triggerBorderColor,
653
+ "--select-option-selected-color": selectedColor,
654
+ "--select-option-selected-bg": selectedBackground,
655
+ "--select-option-hover-bg": hoverBackground
656
+ };
657
+ const overrideStyle = {};
658
+ for (const [key, value] of Object.entries(colorVars)) {
659
+ if (value !== void 0) {
660
+ overrideStyle[key] = value;
661
+ }
662
+ }
663
+ const mergedStyle = { ...overrideStyle, ...style };
581
664
  const [isOpen, setIsOpen] = React6.useState(false);
582
665
  const [searchValue, setSearchValue] = React6.useState("");
583
666
  const [highlightedIndex, setHighlightedIndex] = React6.useState(0);
@@ -651,14 +734,14 @@ function Select({
651
734
  disabled && "fc-select--disabled",
652
735
  className
653
736
  ].filter(Boolean).join(" ");
654
- return /* @__PURE__ */ jsxs6("div", { ref: containerRef, className: classNames, children: [
737
+ return /* @__PURE__ */ jsxs6("div", { ref: containerRef, className: classNames, style: mergedStyle, children: [
655
738
  /* @__PURE__ */ jsxs6(
656
739
  "div",
657
740
  {
658
741
  className: "fc-select__trigger",
659
742
  onClick: () => !disabled && setIsOpen(!isOpen),
660
743
  children: [
661
- /* @__PURE__ */ jsx8("span", { className: `fc-select__value ${!currentValue && "fc-select__value--placeholder"}`, children: displayLabel() }),
744
+ /* @__PURE__ */ jsx8("span", { className: `fc-select__value ${(currentValue === void 0 || currentValue === null || multiple && !currentValue.length) && "fc-select__value--placeholder"}`, children: displayLabel() }),
662
745
  /* @__PURE__ */ jsx8("span", { className: "fc-select__arrow", children: "\u25BC" })
663
746
  ]
664
747
  }
@@ -725,7 +808,8 @@ function Select({
725
808
  }
726
809
  function highlightText(text, highlight) {
727
810
  if (!highlight) return text;
728
- const parts = text.split(new RegExp(`(${highlight})`, "gi"));
811
+ const escaped = highlight.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
812
+ const parts = text.split(new RegExp(`(${escaped})`, "gi"));
729
813
  return parts.map(
730
814
  (part, i) => part.toLowerCase() === highlight.toLowerCase() ? /* @__PURE__ */ jsx8("mark", { className: "fc-select__highlight", children: part }, i) : part
731
815
  );
@@ -735,13 +819,13 @@ function Select({
735
819
  // src/components/Tree/Tree.tsx
736
820
  import {
737
821
  createContext as createContext2,
738
- memo,
822
+ memo as memo2,
739
823
  useCallback as useCallback4,
740
824
  useContext as useContext2,
741
825
  useEffect as useEffect4,
742
826
  useMemo as useMemo2,
743
827
  useRef as useRef4,
744
- useState as useState9
828
+ useState as useState8
745
829
  } from "react";
746
830
  import {
747
831
  DndContext,
@@ -753,11 +837,11 @@ import {
753
837
  } from "@dnd-kit/core";
754
838
 
755
839
  // src/components/Tree/DeleteDialog.tsx
756
- import { useState as useState8 } from "react";
840
+ import { useState as useState7 } from "react";
757
841
  import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
758
842
  function DeleteDialog({ node, onClose, onDelete }) {
759
- const [phase, setPhase] = useState8("choose");
760
- const [loading, setLoading] = useState8(false);
843
+ const [phase, setPhase] = useState7("choose");
844
+ const [loading, setLoading] = useState7(false);
761
845
  if (!node) return null;
762
846
  const reset = () => {
763
847
  setPhase("choose");
@@ -919,10 +1003,10 @@ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-ru
919
1003
  var TreeActionsCtx = createContext2(null);
920
1004
  var TreeStateCtx = createContext2(null);
921
1005
  var DndStateCtx = createContext2(null);
922
- var CollapsePanel = memo(function CollapsePanel2({ open, children }) {
1006
+ var CollapsePanel = memo2(function CollapsePanel2({ open, children }) {
923
1007
  const innerRef = useRef4(null);
924
- const [height, setHeight] = useState9(0);
925
- const [ready, setReady] = useState9(false);
1008
+ const [height, setHeight] = useState8(0);
1009
+ const [ready, setReady] = useState8(false);
926
1010
  useEffect4(() => {
927
1011
  const el = innerRef.current;
928
1012
  if (!el) return;
@@ -938,7 +1022,7 @@ var CollapsePanel = memo(function CollapsePanel2({ open, children }) {
938
1022
  transition: ready ? "height 0.12s ease-out" : "none"
939
1023
  }, children: /* @__PURE__ */ jsx10("div", { ref: innerRef, children }) });
940
1024
  });
941
- var DndSlot = memo(function DndSlot2({
1025
+ var DndSlot = memo2(function DndSlot2({
942
1026
  nodeKey,
943
1027
  disabled,
944
1028
  children
@@ -959,14 +1043,14 @@ var DndSlot = memo(function DndSlot2({
959
1043
  );
960
1044
  return /* @__PURE__ */ jsx10(Fragment2, { children: children({ setRef, handleProps, isDragging, isDragSource, dropPosition }) });
961
1045
  });
962
- var TreeNodeItem = memo(function TreeNodeItem2({ node, level, hidden = false }) {
1046
+ var TreeNodeItem = memo2(function TreeNodeItem2({ node, level, hidden = false }) {
963
1047
  const actions = useContext2(TreeActionsCtx);
964
1048
  const state = useContext2(TreeStateCtx);
965
1049
  const isExpanded = state.expandedKeys.has(node.key);
966
1050
  const isEditing = state.editingKey === node.key;
967
1051
  const hasChildren = node.children.length > 0;
968
1052
  const indent = level * 20 + 12;
969
- const [localEdit, setLocalEdit] = useState9("");
1053
+ const [localEdit, setLocalEdit] = useState8("");
970
1054
  useEffect4(() => {
971
1055
  if (isEditing) setLocalEdit(node.title);
972
1056
  }, [isEditing, node.title]);
@@ -1093,11 +1177,11 @@ function Tree({
1093
1177
  scrollHeight = "400px",
1094
1178
  className = ""
1095
1179
  }) {
1096
- const [expandedKeys, setExpandedKeys] = useState9(/* @__PURE__ */ new Set());
1097
- const [editingKey, setEditingKey] = useState9(null);
1098
- const [deleteTarget, setDeleteTarget] = useState9(null);
1099
- const [searchValue, setSearchValue] = useState9("");
1100
- const [dndState, setDndState] = useState9({
1180
+ const [expandedKeys, setExpandedKeys] = useState8(/* @__PURE__ */ new Set());
1181
+ const [editingKey, setEditingKey] = useState8(null);
1182
+ const [deleteTarget, setDeleteTarget] = useState8(null);
1183
+ const [searchValue, setSearchValue] = useState8("");
1184
+ const [dndState, setDndState] = useState8({
1101
1185
  dropTargetKey: null,
1102
1186
  dropPosition: null,
1103
1187
  dragKey: null
@@ -1263,10 +1347,10 @@ function Tree({
1263
1347
  }
1264
1348
 
1265
1349
  // src/components/Tree/OrphanDialog.tsx
1266
- import { useState as useState10 } from "react";
1350
+ import { useState as useState9 } from "react";
1267
1351
  import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1268
1352
  function OrphanDialog({ orphans, onResolve, onClose }) {
1269
- const [resolutions, setResolutions] = useState10(
1353
+ const [resolutions, setResolutions] = useState9(
1270
1354
  () => Object.fromEntries(orphans.map((o) => [o.key, "lift"]))
1271
1355
  );
1272
1356
  if (orphans.length === 0) return null;
@@ -1330,7 +1414,7 @@ function OrphanDialog({ orphans, onResolve, onClose }) {
1330
1414
  }
1331
1415
 
1332
1416
  // src/components/Avatar/Avatar.tsx
1333
- import { useState as useState11, useMemo as useMemo3, useCallback as useCallback5, forwardRef as forwardRef2, useEffect as useEffect5 } from "react";
1417
+ import { useState as useState10, useMemo as useMemo3, useCallback as useCallback5, forwardRef as forwardRef2, useEffect as useEffect5 } from "react";
1334
1418
  import { jsx as jsx12 } from "react/jsx-runtime";
1335
1419
  var SIZE_MAP = {
1336
1420
  xs: 20,
@@ -1365,8 +1449,8 @@ var useAvatarStyles = (size, shape, bordered, onClick, loadState, src, children,
1365
1449
  return { classes, style };
1366
1450
  };
1367
1451
  var useImageLoader = (src, fallbackSrc, onImageLoad, onImageError, onStateChange) => {
1368
- const [loadState, setLoadState] = useState11("idle");
1369
- const [currentSrc, setCurrentSrc] = useState11(src);
1452
+ const [loadState, setLoadState] = useState10("idle");
1453
+ const [currentSrc, setCurrentSrc] = useState10(src);
1370
1454
  useEffect5(() => {
1371
1455
  setCurrentSrc(src);
1372
1456
  setLoadState(src ? "loading" : "idle");
@@ -1579,8 +1663,10 @@ var ListGroup = forwardRef3(
1579
1663
  ListGroup.displayName = "ListGroup";
1580
1664
 
1581
1665
  // src/components/VirtualList/VirtualList.tsx
1582
- import { useState as useState12, useRef as useRef5, useCallback as useCallback7, useMemo as useMemo5, useEffect as useEffect6 } from "react";
1666
+ import { useState as useState11, useRef as useRef5, useCallback as useCallback7, useMemo as useMemo5, memo as memo3 } from "react";
1583
1667
  import { jsx as jsx14 } from "react/jsx-runtime";
1668
+ var VirtualItem = memo3(({ children, height }) => /* @__PURE__ */ jsx14("div", { className: "fc-virtual-list__item", style: { height: `${height}px` }, children }));
1669
+ VirtualItem.displayName = "VirtualItem";
1584
1670
  function VirtualList({
1585
1671
  data,
1586
1672
  height,
@@ -1593,7 +1679,7 @@ function VirtualList({
1593
1679
  style
1594
1680
  }) {
1595
1681
  const containerRef = useRef5(null);
1596
- const [scrollTop, setScrollTop] = useState12(0);
1682
+ const [scrollTop, setScrollTop] = useState11(0);
1597
1683
  const totalHeight = data.length * itemHeight;
1598
1684
  const handleScroll = useCallback7((e) => {
1599
1685
  const newScrollTop = e.currentTarget.scrollTop;
@@ -1618,11 +1704,6 @@ function VirtualList({
1618
1704
  return data.slice(visibleRange.startIndex, visibleRange.endIndex);
1619
1705
  }, [data, visibleRange]);
1620
1706
  const offsetY = visibleRange.startIndex * itemHeight;
1621
- useEffect6(() => {
1622
- if (!showScrollbar && containerRef.current) {
1623
- containerRef.current.style.scrollbarWidth = "none";
1624
- }
1625
- }, [showScrollbar]);
1626
1707
  const classNames = [
1627
1708
  "fc-virtual-list",
1628
1709
  !showScrollbar && "fc-virtual-list--hide-scrollbar",
@@ -1647,15 +1728,7 @@ function VirtualList({
1647
1728
  style: { transform: `translateY(${offsetY}px)` },
1648
1729
  children: visibleData.map((item, idx) => {
1649
1730
  const actualIndex = visibleRange.startIndex + idx;
1650
- return /* @__PURE__ */ jsx14(
1651
- "div",
1652
- {
1653
- className: "fc-virtual-list__item",
1654
- style: { height: `${itemHeight}px` },
1655
- children: renderItem(item, actualIndex)
1656
- },
1657
- actualIndex
1658
- );
1731
+ return /* @__PURE__ */ jsx14(VirtualItem, { height: itemHeight, children: renderItem(item, actualIndex) }, actualIndex);
1659
1732
  })
1660
1733
  }
1661
1734
  )
@@ -1666,7 +1739,7 @@ function VirtualList({
1666
1739
  }
1667
1740
 
1668
1741
  // src/components/Alert/AlertContext.tsx
1669
- import { createContext as createContext3, useContext as useContext3, useState as useState13 } from "react";
1742
+ import { createContext as createContext3, useContext as useContext3, useEffect as useEffect6, useState as useState12 } from "react";
1670
1743
  import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
1671
1744
  var AlertContext = createContext3(null);
1672
1745
  var ICONS = {
@@ -1742,55 +1815,72 @@ var ICONS = {
1742
1815
  }
1743
1816
  )
1744
1817
  };
1745
- function AlertProvider({ children }) {
1746
- const [props, setProps] = useState13({
1818
+ function AlertProvider({ children, background, borderColor }) {
1819
+ const [alert, setAlert] = useState12({
1747
1820
  msg: "",
1748
1821
  type: "info",
1822
+ mode: "alert",
1749
1823
  visible: false,
1750
1824
  choice: () => {
1751
1825
  }
1752
1826
  });
1753
- const showAlert = (msg, type, confirm = false) => new Promise((resolve) => {
1754
- setProps({
1827
+ const showAlert = (msg, type, mode = "alert", duration) => new Promise((resolve) => {
1828
+ setAlert({
1755
1829
  msg,
1756
1830
  type,
1831
+ mode,
1757
1832
  visible: true,
1758
- confirm,
1833
+ duration,
1759
1834
  choice: (res) => {
1760
- setProps((p) => ({ ...p, visible: false }));
1835
+ setAlert((p) => ({ ...p, visible: false }));
1761
1836
  resolve(res);
1762
1837
  }
1763
1838
  });
1764
1839
  });
1840
+ useEffect6(() => {
1841
+ if (!alert.visible || !alert.duration) return;
1842
+ const timer = setTimeout(() => alert.choice("auto"), alert.duration);
1843
+ return () => clearTimeout(timer);
1844
+ }, [alert.visible, alert.duration]);
1845
+ const overrideStyle = {};
1846
+ if (background !== void 0) overrideStyle["--alert-bg"] = background;
1847
+ if (borderColor !== void 0) overrideStyle["--alert-border"] = borderColor;
1765
1848
  return /* @__PURE__ */ jsxs10(AlertContext.Provider, { value: { showAlert }, children: [
1766
1849
  children,
1767
- props.visible && /* @__PURE__ */ jsx15("div", { className: "fc-alert-overlay", children: /* @__PURE__ */ jsxs10("div", { className: `fc-alert fc-alert--${props.type}`, children: [
1768
- /* @__PURE__ */ jsxs10("div", { className: "fc-alert__header", children: [
1769
- ICONS[props.type],
1770
- /* @__PURE__ */ jsx15("span", { className: "fc-alert__title", children: "\u63D0\u793A" })
1771
- ] }),
1772
- /* @__PURE__ */ jsx15(RollingBox, { className: "fc-alert__msg", children: props.msg }),
1773
- /* @__PURE__ */ jsxs10("div", { className: "fc-alert__footer", children: [
1774
- props.confirm && /* @__PURE__ */ jsx15(
1775
- Button,
1776
- {
1777
- variant: "secondary",
1778
- size: "sm",
1779
- onClick: () => props.choice("no"),
1780
- children: "\u53D6\u6D88"
1781
- }
1782
- ),
1783
- /* @__PURE__ */ jsx15(
1784
- Button,
1785
- {
1786
- variant: "primary",
1787
- size: "sm",
1788
- onClick: () => props.choice("yes"),
1789
- children: "\u786E\u5B9A"
1790
- }
1791
- )
1792
- ] })
1793
- ] }) })
1850
+ alert.visible && /* @__PURE__ */ jsx15("div", { className: "fc-alert-overlay", children: /* @__PURE__ */ jsxs10(
1851
+ "div",
1852
+ {
1853
+ className: `fc-alert fc-alert--${alert.type} fc-alert--${alert.mode}`,
1854
+ style: overrideStyle,
1855
+ children: [
1856
+ /* @__PURE__ */ jsxs10("div", { className: "fc-alert__header", children: [
1857
+ ICONS[alert.type],
1858
+ /* @__PURE__ */ jsx15("span", { className: "fc-alert__title", children: "\u63D0\u793A" })
1859
+ ] }),
1860
+ /* @__PURE__ */ jsx15(RollingBox, { className: "fc-alert__msg", children: alert.msg }),
1861
+ alert.mode !== "toast" && /* @__PURE__ */ jsxs10("div", { className: "fc-alert__footer", children: [
1862
+ alert.mode === "confirm" && /* @__PURE__ */ jsx15(
1863
+ Button,
1864
+ {
1865
+ variant: "secondary",
1866
+ size: "sm",
1867
+ onClick: () => alert.choice("no"),
1868
+ children: "\u53D6\u6D88"
1869
+ }
1870
+ ),
1871
+ /* @__PURE__ */ jsx15(
1872
+ Button,
1873
+ {
1874
+ variant: "primary",
1875
+ size: "sm",
1876
+ onClick: () => alert.choice("yes"),
1877
+ children: "\u786E\u5B9A"
1878
+ }
1879
+ )
1880
+ ] })
1881
+ ]
1882
+ }
1883
+ ) })
1794
1884
  ] });
1795
1885
  }
1796
1886
  var useAlert = () => useContext3(AlertContext);
@@ -1882,344 +1972,779 @@ var Card = ({
1882
1972
  ] });
1883
1973
  };
1884
1974
 
1885
- // src/components/Tabs/Tabs.tsx
1886
- import { useState as useState14 } from "react";
1975
+ // src/components/Bar/TabBar.tsx
1976
+ import { useRef as useRef6, useCallback as useCallback8, memo as memo4 } from "react";
1887
1977
  import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1888
- var Tabs = ({
1978
+ var TabItemView = memo4(({
1979
+ item,
1980
+ isActive,
1981
+ closable,
1982
+ draggable,
1983
+ tabClassName,
1984
+ activeTabClassName,
1985
+ tabStyle,
1986
+ activeTabStyle,
1987
+ renderCloseIcon,
1988
+ onClick,
1989
+ onClose,
1990
+ onDragStart,
1991
+ onDragOver,
1992
+ onDrop,
1993
+ onDragEnd
1994
+ }) => {
1995
+ const showClose = item.closable !== void 0 ? item.closable : closable;
1996
+ const classes = [
1997
+ "fc-tab-bar__tab",
1998
+ isActive && "fc-tab-bar__tab--active",
1999
+ item.disabled && "fc-tab-bar__tab--disabled",
2000
+ draggable && !item.disabled && "fc-tab-bar__tab--draggable",
2001
+ tabClassName,
2002
+ isActive && activeTabClassName
2003
+ ].filter(Boolean).join(" ");
2004
+ const mergedStyle = {
2005
+ ...tabStyle,
2006
+ ...isActive ? activeTabStyle : void 0
2007
+ };
2008
+ return /* @__PURE__ */ jsxs13(
2009
+ "div",
2010
+ {
2011
+ className: classes,
2012
+ style: mergedStyle,
2013
+ onClick: () => !item.disabled && onClick(item.key),
2014
+ draggable: draggable && !item.disabled,
2015
+ onDragStart: (e) => onDragStart(e, item.key),
2016
+ onDragOver,
2017
+ onDrop: (e) => onDrop(e, item.key),
2018
+ onDragEnd,
2019
+ role: "tab",
2020
+ "aria-selected": isActive,
2021
+ "aria-disabled": item.disabled,
2022
+ tabIndex: item.disabled ? -1 : 0,
2023
+ children: [
2024
+ /* @__PURE__ */ jsx18("span", { className: "fc-tab-bar__tab-label", children: item.label }),
2025
+ showClose && !item.disabled && /* @__PURE__ */ jsx18(
2026
+ "span",
2027
+ {
2028
+ className: "fc-tab-bar__tab-close",
2029
+ onClick: (e) => onClose(e, item.key),
2030
+ role: "button",
2031
+ "aria-label": `\u5173\u95ED ${typeof item.label === "string" ? item.label : ""}`,
2032
+ children: renderCloseIcon ? renderCloseIcon(item.key) : "\xD7"
2033
+ }
2034
+ )
2035
+ ]
2036
+ }
2037
+ );
2038
+ });
2039
+ TabItemView.displayName = "TabItemView";
2040
+ var TabBar = memo4(({
1889
2041
  items,
1890
- activeKey: controlledActiveKey,
1891
- defaultActiveKey,
2042
+ activeKey,
2043
+ variant = "attached",
1892
2044
  radius = "md",
2045
+ tabRadius,
1893
2046
  closable = false,
1894
2047
  addable = false,
2048
+ draggable = false,
1895
2049
  onChange,
1896
2050
  onClose,
1897
2051
  onAdd,
2052
+ onReorder,
2053
+ tabClassName,
2054
+ activeTabClassName,
2055
+ tabStyle,
2056
+ activeTabStyle,
2057
+ renderCloseIcon,
2058
+ renderAddButton,
1898
2059
  className = "",
1899
- style
2060
+ style,
2061
+ background,
2062
+ tabColor,
2063
+ tabHoverColor,
2064
+ tabHoverBackground,
2065
+ tabActiveColor,
2066
+ tabActiveBackground,
2067
+ activeIndicatorColor
1900
2068
  }) => {
1901
- const [internalActiveKey, setInternalActiveKey] = useState14(
1902
- defaultActiveKey || items[0]?.key || ""
1903
- );
1904
- const activeKey = controlledActiveKey !== void 0 ? controlledActiveKey : internalActiveKey;
1905
- const handleTabClick = (key, disabled) => {
1906
- if (disabled) return;
1907
- if (controlledActiveKey === void 0) {
1908
- setInternalActiveKey(key);
1909
- }
1910
- onChange?.(key);
2069
+ const colorVars = {
2070
+ "--tab-bar-bg": background,
2071
+ "--tab-color": tabColor,
2072
+ "--tab-hover-color": tabHoverColor,
2073
+ "--tab-hover-bg": tabHoverBackground,
2074
+ "--tab-active-color": tabActiveColor,
2075
+ "--tab-active-bg": tabActiveBackground,
2076
+ "--tab-active-indicator": activeIndicatorColor
1911
2077
  };
1912
- const handleClose = (e, key) => {
1913
- e.stopPropagation();
1914
- onClose?.(key);
1915
- if (key === activeKey) {
1916
- const currentIndex = items.findIndex((item) => item.key === key);
1917
- const nextItem = items[currentIndex + 1] || items[currentIndex - 1];
1918
- if (nextItem && !nextItem.disabled) {
1919
- handleTabClick(nextItem.key);
1920
- }
2078
+ const overrideStyle = {};
2079
+ for (const [key, value] of Object.entries(colorVars)) {
2080
+ if (value !== void 0) {
2081
+ overrideStyle[key] = value;
1921
2082
  }
1922
- };
1923
- const classes = [
1924
- "fc-tabs",
1925
- `fc-tabs--radius-${radius}`,
2083
+ }
2084
+ const mergedStyle = { ...overrideStyle, ...style };
2085
+ const dragKeyRef = useRef6(null);
2086
+ const handleClick = useCallback8(
2087
+ (key) => onChange(key),
2088
+ [onChange]
2089
+ );
2090
+ const handleClose = useCallback8(
2091
+ (e, key) => {
2092
+ e.stopPropagation();
2093
+ onClose?.(key);
2094
+ },
2095
+ [onClose]
2096
+ );
2097
+ const handleDragStart = useCallback8(
2098
+ (e, key) => {
2099
+ dragKeyRef.current = key;
2100
+ e.dataTransfer.effectAllowed = "move";
2101
+ const target = e.currentTarget;
2102
+ requestAnimationFrame(() => target.classList.add("fc-tab-bar__tab--dragging"));
2103
+ },
2104
+ []
2105
+ );
2106
+ const handleDragOver = useCallback8((e) => {
2107
+ e.preventDefault();
2108
+ e.dataTransfer.dropEffect = "move";
2109
+ }, []);
2110
+ const handleDrop = useCallback8(
2111
+ (e, targetKey) => {
2112
+ e.preventDefault();
2113
+ const dragKey = dragKeyRef.current;
2114
+ if (!dragKey || dragKey === targetKey || !onReorder) return;
2115
+ const fromIndex = items.findIndex((i) => i.key === dragKey);
2116
+ const toIndex = items.findIndex((i) => i.key === targetKey);
2117
+ if (fromIndex === -1 || toIndex === -1) return;
2118
+ const reordered = [...items];
2119
+ const [moved] = reordered.splice(fromIndex, 1);
2120
+ reordered.splice(toIndex, 0, moved);
2121
+ onReorder(reordered);
2122
+ },
2123
+ [items, onReorder]
2124
+ );
2125
+ const handleDragEnd = useCallback8((e) => {
2126
+ dragKeyRef.current = null;
2127
+ e.currentTarget.classList.remove("fc-tab-bar__tab--dragging");
2128
+ }, []);
2129
+ const rootClasses = [
2130
+ "fc-tab-bar",
2131
+ `fc-tab-bar--${variant}`,
2132
+ `fc-tab-bar--radius-${radius}`,
2133
+ tabRadius && `fc-tab-bar--tab-radius-${tabRadius}`,
1926
2134
  className
1927
2135
  ].filter(Boolean).join(" ");
1928
- return /* @__PURE__ */ jsxs13("div", { className: classes, style, children: [
1929
- /* @__PURE__ */ jsx18("div", { className: "fc-tabs__nav", children: /* @__PURE__ */ jsxs13("div", { className: "fc-tabs__nav-wrap", children: [
1930
- items.map((item) => {
1931
- const isActive = activeKey === item.key;
1932
- return /* @__PURE__ */ jsxs13(
1933
- "div",
1934
- {
1935
- className: `fc-tabs__tab ${isActive ? "fc-tabs__tab--active" : ""} ${item.disabled ? "fc-tabs__tab--disabled" : ""}`,
1936
- onClick: () => handleTabClick(item.key, item.disabled),
1937
- children: [
1938
- /* @__PURE__ */ jsx18("span", { className: "fc-tabs__tab-label", children: item.label }),
1939
- closable && /* @__PURE__ */ jsx18(
1940
- "span",
1941
- {
1942
- className: "fc-tabs__tab-close",
1943
- onClick: (e) => handleClose(e, item.key),
1944
- children: "\xD7"
1945
- }
1946
- )
1947
- ]
1948
- },
1949
- item.key
1950
- );
1951
- }),
1952
- addable && /* @__PURE__ */ jsx18("div", { className: "fc-tabs__add-btn", onClick: onAdd, children: "+" })
1953
- ] }) }),
1954
- /* @__PURE__ */ jsx18("div", { className: "fc-tabs__content", children: items.find((item) => item.key === activeKey)?.content })
1955
- ] });
1956
- };
2136
+ return /* @__PURE__ */ jsx18("div", { className: rootClasses, style: mergedStyle, role: "tablist", children: /* @__PURE__ */ jsx18("div", { className: "fc-tab-bar__nav", children: /* @__PURE__ */ jsxs13("div", { className: "fc-tab-bar__nav-wrap", children: [
2137
+ items.map((item) => /* @__PURE__ */ jsx18(
2138
+ TabItemView,
2139
+ {
2140
+ item,
2141
+ isActive: activeKey === item.key,
2142
+ closable,
2143
+ draggable,
2144
+ tabClassName,
2145
+ activeTabClassName,
2146
+ tabStyle,
2147
+ activeTabStyle,
2148
+ renderCloseIcon,
2149
+ onClick: handleClick,
2150
+ onClose: handleClose,
2151
+ onDragStart: handleDragStart,
2152
+ onDragOver: handleDragOver,
2153
+ onDrop: handleDrop,
2154
+ onDragEnd: handleDragEnd
2155
+ },
2156
+ item.key
2157
+ )),
2158
+ addable && /* @__PURE__ */ jsx18(
2159
+ "div",
2160
+ {
2161
+ className: "fc-tab-bar__add-btn",
2162
+ onClick: onAdd,
2163
+ role: "button",
2164
+ "aria-label": "\u6DFB\u52A0\u6807\u7B7E",
2165
+ children: renderAddButton ? renderAddButton() : "+"
2166
+ }
2167
+ )
2168
+ ] }) }) });
2169
+ });
2170
+ TabBar.displayName = "TabBar";
1957
2171
 
1958
- // src/components/Chat/Chat.tsx
1959
- import { useState as useState15, useRef as useRef6, useEffect as useEffect7, useCallback as useCallback8 } from "react";
2172
+ // src/components/Tag/TagItem.tsx
2173
+ import { useEffect as useEffect7, useRef as useRef7, useState as useState13 } from "react";
1960
2174
  import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1961
- var Chat = ({
1962
- messages = [],
1963
- onSendMessage,
1964
- placeholder = "\u8F93\u5165\u6D88\u606F...",
1965
- title = "AI \u52A9\u624B",
1966
- height = "600px",
1967
- showHeader = true,
1968
- showFooter = true,
1969
- loading = false,
1970
- disabled = false,
1971
- theme: propTheme,
1972
- userName = "\u6211",
1973
- assistantName = "AI\u52A9\u624B",
1974
- onTyping,
1975
- maxInputLength = 2e3,
1976
- autoFocus = true,
1977
- enableCopy = true
1978
- }) => {
1979
- const [inputValue, setInputValue] = useState15("");
1980
- const [isTyping, setIsTyping] = useState15(false);
1981
- const [isSending, setIsSending] = useState15(false);
1982
- const [currentTheme, setCurrentTheme] = useState15("light");
1983
- const [copiedMessageId, setCopiedMessageId] = useState15(null);
1984
- const [copyError, setCopyError] = useState15(null);
1985
- const messagesEndRef = useRef6(null);
1986
- const inputRef = useRef6(null);
2175
+ function TagItem({
2176
+ schema,
2177
+ value,
2178
+ onChange,
2179
+ mode = "show",
2180
+ background,
2181
+ color,
2182
+ borderColor
2183
+ }) {
2184
+ const [editing, setEditing] = useState13(false);
2185
+ const [draft, setDraft] = useState13(() => value !== void 0 ? String(value) : "");
2186
+ const inputRef = useRef7(null);
1987
2187
  useEffect7(() => {
1988
- if (propTheme) {
1989
- setCurrentTheme(propTheme);
2188
+ if (!editing) setDraft(value !== void 0 ? String(value) : "");
2189
+ }, [value, editing]);
2190
+ const colorVars = {
2191
+ "--tag-bg": background,
2192
+ "--tag-color": color,
2193
+ "--tag-border": borderColor
2194
+ };
2195
+ const overrideStyle = {};
2196
+ for (const [k, v] of Object.entries(colorVars)) {
2197
+ if (v !== void 0) overrideStyle[k] = v;
2198
+ }
2199
+ if (schema.type === "boolean") {
2200
+ const boolVal = value === true || value === "true";
2201
+ return /* @__PURE__ */ jsxs14(
2202
+ "span",
2203
+ {
2204
+ className: "fc-tag-item fc-tag-item--boolean",
2205
+ style: overrideStyle,
2206
+ onClick: () => onChange?.(!boolVal),
2207
+ title: "\u70B9\u51FB\u5207\u6362",
2208
+ children: [
2209
+ /* @__PURE__ */ jsx19("span", { className: "fc-tag-item__name", children: schema.name }),
2210
+ /* @__PURE__ */ jsx19("span", { className: "fc-tag-item__sep", children: "\xB7" }),
2211
+ /* @__PURE__ */ jsx19("span", { className: boolVal ? "fc-tag-item__bool-true" : "fc-tag-item__bool-false", children: boolVal ? "\u2713" : "\u2717" })
2212
+ ]
2213
+ }
2214
+ );
2215
+ }
2216
+ const commit = () => {
2217
+ if (schema.type === "number") {
2218
+ const n = parseFloat(draft);
2219
+ if (!isNaN(n)) {
2220
+ 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;
2221
+ onChange?.(clamped);
2222
+ }
1990
2223
  } else {
1991
- const checkTheme = () => {
1992
- const dataTheme = document.documentElement.getAttribute("data-theme");
1993
- if (dataTheme === "dark") {
1994
- setCurrentTheme("dark");
1995
- } else if (dataTheme === "light") {
1996
- setCurrentTheme("light");
1997
- } else {
1998
- const hasDarkClass = document.documentElement.classList.contains("dark") || document.body.classList.contains("dark") || document.documentElement.classList.contains("theme-dark") || document.body.classList.contains("theme-dark");
1999
- setCurrentTheme(hasDarkClass ? "dark" : "light");
2000
- }
2001
- };
2002
- checkTheme();
2003
- const observer = new MutationObserver(checkTheme);
2004
- observer.observe(document.documentElement, {
2005
- attributes: true,
2006
- attributeFilter: ["data-theme", "class"]
2007
- });
2008
- observer.observe(document.body, {
2009
- attributes: true,
2010
- attributeFilter: ["class"]
2011
- });
2012
- return () => observer.disconnect();
2013
- }
2014
- }, [propTheme]);
2015
- useEffect7(() => {
2016
- messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
2017
- }, [messages, loading]);
2018
- useEffect7(() => {
2019
- if (autoFocus && !disabled && inputRef.current) {
2020
- inputRef.current.focus();
2224
+ onChange?.(draft);
2021
2225
  }
2022
- }, [autoFocus, disabled]);
2023
- useEffect7(() => {
2024
- if (copyError) {
2025
- const timer = setTimeout(() => {
2026
- setCopyError(null);
2027
- }, 2e3);
2028
- return () => clearTimeout(timer);
2226
+ if (mode === "show") setEditing(false);
2227
+ };
2228
+ const cancel = () => {
2229
+ setDraft(value !== void 0 ? String(value) : "");
2230
+ if (mode === "show") setEditing(false);
2231
+ };
2232
+ const isEditing = mode === "edit" || editing;
2233
+ if (isEditing) {
2234
+ return /* @__PURE__ */ jsxs14("span", { className: "fc-tag-item fc-tag-item--editing", style: overrideStyle, children: [
2235
+ /* @__PURE__ */ jsx19("span", { className: "fc-tag-item__name", children: schema.name }),
2236
+ /* @__PURE__ */ jsx19("span", { className: "fc-tag-item__sep", children: "\xB7" }),
2237
+ /* @__PURE__ */ jsx19(
2238
+ "input",
2239
+ {
2240
+ ref: inputRef,
2241
+ className: "fc-tag-item__input",
2242
+ type: schema.type === "number" ? "number" : "text",
2243
+ value: draft,
2244
+ min: schema.range_min ?? void 0,
2245
+ max: schema.range_max ?? void 0,
2246
+ autoFocus: true,
2247
+ onChange: (e) => setDraft(e.target.value),
2248
+ onBlur: commit,
2249
+ onKeyDown: (e) => {
2250
+ if (e.key === "Enter") {
2251
+ e.preventDefault();
2252
+ commit();
2253
+ }
2254
+ if (e.key === "Escape") {
2255
+ e.preventDefault();
2256
+ cancel();
2257
+ }
2258
+ }
2259
+ }
2260
+ )
2261
+ ] });
2262
+ }
2263
+ return /* @__PURE__ */ jsxs14(
2264
+ "span",
2265
+ {
2266
+ className: "fc-tag-item fc-tag-item--show",
2267
+ style: overrideStyle,
2268
+ onDoubleClick: () => {
2269
+ setDraft(value !== void 0 ? String(value) : "");
2270
+ setEditing(true);
2271
+ setTimeout(() => inputRef.current?.select(), 0);
2272
+ },
2273
+ title: "\u53CC\u51FB\u7F16\u8F91",
2274
+ children: [
2275
+ /* @__PURE__ */ jsx19("span", { className: "fc-tag-item__name", children: schema.name }),
2276
+ value !== void 0 && /* @__PURE__ */ jsxs14(Fragment3, { children: [
2277
+ /* @__PURE__ */ jsx19("span", { className: "fc-tag-item__sep", children: "\xB7" }),
2278
+ /* @__PURE__ */ jsx19("span", { className: "fc-tag-item__value", children: String(value) })
2279
+ ] })
2280
+ ]
2029
2281
  }
2030
- }, [copyError]);
2031
- const handleCopyMessage = useCallback8(async (messageId, content) => {
2032
- if (!navigator.clipboard) {
2033
- setCopyError("\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u590D\u5236\u529F\u80FD");
2034
- return;
2282
+ );
2283
+ }
2284
+
2285
+ // src/components/MarkdownEditor/MarkdownEditor.tsx
2286
+ import MDEditor, { commands } from "@uiw/react-md-editor";
2287
+ import { jsx as jsx20 } from "react/jsx-runtime";
2288
+ var TOOLBAR_COMMANDS = [
2289
+ commands.bold,
2290
+ commands.italic,
2291
+ commands.strikethrough,
2292
+ commands.divider,
2293
+ commands.title1,
2294
+ commands.title2,
2295
+ commands.title3,
2296
+ commands.divider,
2297
+ commands.quote,
2298
+ commands.code,
2299
+ commands.codeBlock,
2300
+ commands.divider,
2301
+ commands.link,
2302
+ commands.unorderedListCommand,
2303
+ commands.orderedListCommand,
2304
+ commands.hr
2305
+ ];
2306
+ function MarkdownEditor({
2307
+ value,
2308
+ onChange,
2309
+ onAiComplete,
2310
+ minHeight = 200,
2311
+ placeholder = "\u5728\u6B64\u8F93\u5165\u5185\u5BB9...",
2312
+ background,
2313
+ toolbarBackground,
2314
+ borderColor
2315
+ }) {
2316
+ const { resolvedTheme } = useTheme();
2317
+ const colorVars = {
2318
+ "--md-bg": background,
2319
+ "--md-toolbar-bg": toolbarBackground,
2320
+ "--md-border": borderColor
2321
+ };
2322
+ const overrideStyle = {};
2323
+ for (const [k, v] of Object.entries(colorVars)) {
2324
+ if (v !== void 0) overrideStyle[k] = v;
2325
+ }
2326
+ const aiCommand = {
2327
+ name: "ai-complete",
2328
+ keyCommand: "ai-complete",
2329
+ buttonProps: { "aria-label": "AI \u8865\u5168", className: "fc-md-ai-btn" },
2330
+ icon: /* @__PURE__ */ jsx20("span", { children: "AI \u8865\u5168" }),
2331
+ execute: () => onAiComplete?.()
2332
+ };
2333
+ const extraCommands = onAiComplete ? [commands.divider, aiCommand, commands.fullscreen] : [commands.fullscreen];
2334
+ return /* @__PURE__ */ jsx20(
2335
+ "div",
2336
+ {
2337
+ className: "fc-md-wrap",
2338
+ style: overrideStyle,
2339
+ "data-color-mode": resolvedTheme,
2340
+ children: /* @__PURE__ */ jsx20(
2341
+ MDEditor,
2342
+ {
2343
+ value,
2344
+ onChange: (v) => onChange(v ?? ""),
2345
+ commands: TOOLBAR_COMMANDS,
2346
+ extraCommands,
2347
+ height: minHeight,
2348
+ preview: "edit",
2349
+ visibleDragbar: false,
2350
+ textareaProps: { placeholder }
2351
+ }
2352
+ )
2035
2353
  }
2354
+ );
2355
+ }
2356
+
2357
+ // src/components/ContextMenu/ContextMenuContext.tsx
2358
+ import { createContext as createContext4, useContext as useContext4, useEffect as useEffect8, useRef as useRef8, useState as useState14 } from "react";
2359
+ import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
2360
+ var ContextMenuContext = createContext4(null);
2361
+ function ContextMenuProvider({
2362
+ children,
2363
+ background,
2364
+ borderColor,
2365
+ hoverBackground
2366
+ }) {
2367
+ const [menu, setMenu] = useState14({
2368
+ visible: false,
2369
+ x: 0,
2370
+ y: 0,
2371
+ items: []
2372
+ });
2373
+ const menuRef = useRef8(null);
2374
+ const showContextMenu = (e, items) => {
2375
+ e.preventDefault();
2376
+ e.stopPropagation();
2377
+ setMenu({ visible: true, x: e.clientX, y: e.clientY, items });
2378
+ };
2379
+ const hide = () => setMenu((s) => ({ ...s, visible: false }));
2380
+ useEffect8(() => {
2381
+ if (!menu.visible) return;
2382
+ const onPointerDown = (e) => {
2383
+ if (menuRef.current && !menuRef.current.contains(e.target)) hide();
2384
+ };
2385
+ const onKeyDown = (e) => {
2386
+ if (e.key === "Escape") hide();
2387
+ };
2388
+ window.addEventListener("pointerdown", onPointerDown);
2389
+ window.addEventListener("keydown", onKeyDown);
2390
+ return () => {
2391
+ window.removeEventListener("pointerdown", onPointerDown);
2392
+ window.removeEventListener("keydown", onKeyDown);
2393
+ };
2394
+ }, [menu.visible]);
2395
+ const getPosition = () => {
2396
+ const W = window.innerWidth;
2397
+ const H = window.innerHeight;
2398
+ const menuW = 180;
2399
+ const menuH = menu.items.length * 32 + 12;
2400
+ return {
2401
+ left: menu.x + menuW > W ? menu.x - menuW : menu.x,
2402
+ top: menu.y + menuH > H ? menu.y - menuH : menu.y
2403
+ };
2404
+ };
2405
+ const colorVars = {
2406
+ "--ctx-bg": background,
2407
+ "--ctx-border": borderColor,
2408
+ "--ctx-item-hover-bg": hoverBackground
2409
+ };
2410
+ const overrideStyle = { ...getPosition() };
2411
+ for (const [k, v] of Object.entries(colorVars)) {
2412
+ if (v !== void 0) overrideStyle[k] = v;
2413
+ }
2414
+ return /* @__PURE__ */ jsxs15(ContextMenuContext.Provider, { value: { showContextMenu }, children: [
2415
+ children,
2416
+ menu.visible && /* @__PURE__ */ jsx21(
2417
+ "ul",
2418
+ {
2419
+ ref: menuRef,
2420
+ className: "fc-context-menu",
2421
+ style: overrideStyle,
2422
+ children: menu.items.map((item, i) => {
2423
+ if ("type" in item && item.type === "divider") {
2424
+ return /* @__PURE__ */ jsx21("li", { className: "fc-context-menu__divider" }, i);
2425
+ }
2426
+ const action = item;
2427
+ return /* @__PURE__ */ jsxs15(
2428
+ "li",
2429
+ {
2430
+ className: [
2431
+ "fc-context-menu__item",
2432
+ action.danger ? "fc-context-menu__item--danger" : "",
2433
+ action.disabled ? "fc-context-menu__item--disabled" : ""
2434
+ ].filter(Boolean).join(" "),
2435
+ onPointerDown: (e) => e.stopPropagation(),
2436
+ onClick: () => {
2437
+ if (action.disabled) return;
2438
+ action.onClick();
2439
+ hide();
2440
+ },
2441
+ children: [
2442
+ action.icon && /* @__PURE__ */ jsx21("span", { className: "fc-context-menu__icon", children: action.icon }),
2443
+ /* @__PURE__ */ jsx21("span", { className: "fc-context-menu__label", children: action.label })
2444
+ ]
2445
+ },
2446
+ i
2447
+ );
2448
+ })
2449
+ }
2450
+ )
2451
+ ] });
2452
+ }
2453
+ var useContextMenu = () => useContext4(ContextMenuContext);
2454
+
2455
+ // src/components/Chat/Chat.tsx
2456
+ import { useState as useState16, useRef as useRef9, useEffect as useEffect9, useCallback as useCallback9, useMemo as useMemo6 } from "react";
2457
+
2458
+ // src/components/SmartMessage/SmartMessage.tsx
2459
+ import { useState as useState15, memo as memo5 } from "react";
2460
+ import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
2461
+ var SmartMessage = memo5(({
2462
+ id,
2463
+ content,
2464
+ role,
2465
+ timestamp,
2466
+ status,
2467
+ toolName,
2468
+ toolResult,
2469
+ onCopy,
2470
+ className = "",
2471
+ style = {}
2472
+ }) => {
2473
+ const [copied, setCopied] = useState15(false);
2474
+ const formattedTime = timestamp ? new Date(timestamp).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }) : "";
2475
+ const handleCopy = async () => {
2036
2476
  try {
2037
- await navigator.clipboard.writeText(content);
2038
- setCopiedMessageId(messageId);
2039
- setTimeout(() => {
2040
- setCopiedMessageId(null);
2041
- }, 2e3);
2477
+ let copyContent = content;
2478
+ if (role === "tool" && toolResult) {
2479
+ copyContent = `\u5DE5\u5177: ${toolName || "\u5DE5\u5177\u8C03\u7528"}
2480
+ \u7ED3\u679C: ${JSON.stringify(toolResult, null, 2)}`;
2481
+ }
2482
+ await navigator.clipboard.writeText(copyContent);
2483
+ setCopied(true);
2484
+ onCopy?.(content, role);
2485
+ setTimeout(() => setCopied(false), 2e3);
2042
2486
  } catch (err) {
2043
2487
  console.error("\u590D\u5236\u5931\u8D25:", err);
2044
- if (err instanceof Error) {
2045
- if (err.name === "NotAllowedError") {
2046
- setCopyError("\u9700\u8981\u526A\u8D34\u677F\u6743\u9650\uFF0C\u8BF7\u5141\u8BB8\u540E\u91CD\u8BD5");
2047
- } else if (err.name === "SecurityError") {
2048
- setCopyError("\u51FA\u4E8E\u5B89\u5168\u539F\u56E0\uFF0C\u65E0\u6CD5\u590D\u5236\u5185\u5BB9");
2049
- } else {
2050
- setCopyError("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
2051
- }
2052
- } else {
2053
- setCopyError("\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
2054
- }
2055
- setTimeout(() => {
2056
- setCopyError(null);
2057
- }, 3e3);
2058
2488
  }
2059
- }, []);
2060
- const handleSend = useCallback8(() => {
2061
- const trimmedMessage = inputValue.trim();
2062
- if (!trimmedMessage || disabled || loading || isSending) return;
2063
- if (trimmedMessage.length > maxInputLength) {
2064
- console.warn(`\u6D88\u606F\u8D85\u8FC7\u6700\u5927\u957F\u5EA6\u9650\u5236: ${maxInputLength}`);
2065
- return;
2066
- }
2067
- setIsSending(true);
2068
- const sendPromise = onSendMessage?.(trimmedMessage);
2069
- if (sendPromise) {
2070
- void sendPromise.then(() => {
2071
- setInputValue("");
2072
- if (inputRef.current) {
2073
- inputRef.current.style.height = "auto";
2074
- }
2075
- if (isTyping) {
2076
- setIsTyping(false);
2077
- onTyping?.(false);
2489
+ };
2490
+ const shouldShowCopyButton = () => {
2491
+ return role === "user" || role === "assistant";
2492
+ };
2493
+ const getContainerClassName = () => {
2494
+ const baseClass = "smart-message";
2495
+ const roleClass = `smart-message-${role}`;
2496
+ const statusClass = status ? `smart-message-${status}` : "";
2497
+ return `${baseClass} ${roleClass} ${statusClass} ${className}`.trim();
2498
+ };
2499
+ const getContentClassName = () => {
2500
+ const baseClass = "smart-message-content";
2501
+ const roleContentClass = `smart-message-content-${role}`;
2502
+ return `${baseClass} ${roleContentClass}`;
2503
+ };
2504
+ const renderSystemMessage = () => /* @__PURE__ */ jsxs16("div", { className: "system-message-wrapper", children: [
2505
+ /* @__PURE__ */ jsx22("div", { className: "system-message-icon", children: /* @__PURE__ */ jsxs16("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
2506
+ /* @__PURE__ */ jsx22("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2" }),
2507
+ /* @__PURE__ */ jsx22("path", { d: "M12 8V12M12 16H12.01", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
2508
+ ] }) }),
2509
+ /* @__PURE__ */ jsx22("div", { className: "system-message-text", children: content }),
2510
+ formattedTime && /* @__PURE__ */ jsx22("div", { className: "system-message-time", children: formattedTime })
2511
+ ] });
2512
+ const renderToolMessage = () => /* @__PURE__ */ jsxs16("div", { className: "tool-message-wrapper", children: [
2513
+ /* @__PURE__ */ jsxs16("div", { className: "tool-message-header", children: [
2514
+ /* @__PURE__ */ jsx22("div", { className: "tool-message-icon", children: /* @__PURE__ */ jsxs16("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [
2515
+ /* @__PURE__ */ jsx22("path", { d: "M14.7 6.3L19 2L22 5L17.7 9.3L14.7 6.3Z", stroke: "currentColor", strokeWidth: "2" }),
2516
+ /* @__PURE__ */ jsx22("path", { d: "M9.3 17.7L5 22L2 19L6.3 14.7L9.3 17.7Z", stroke: "currentColor", strokeWidth: "2" }),
2517
+ /* @__PURE__ */ jsx22("path", { d: "M12 12L14.7 9.3M12 12L9.3 14.7M12 12L8 8M12 12L16 16", stroke: "currentColor", strokeWidth: "2" })
2518
+ ] }) }),
2519
+ /* @__PURE__ */ jsx22("span", { className: "tool-message-name", children: toolName || "\u5DE5\u5177\u8C03\u7528" })
2520
+ ] }),
2521
+ /* @__PURE__ */ jsxs16("div", { className: "tool-message-content", children: [
2522
+ /* @__PURE__ */ jsxs16("div", { className: "tool-message-params", children: [
2523
+ /* @__PURE__ */ jsx22("strong", { children: "\u53C2\u6570:" }),
2524
+ /* @__PURE__ */ jsx22("pre", { children: content })
2525
+ ] }),
2526
+ toolResult && /* @__PURE__ */ jsxs16("div", { className: "tool-message-result", children: [
2527
+ /* @__PURE__ */ jsx22("strong", { children: "\u7ED3\u679C:" }),
2528
+ /* @__PURE__ */ jsx22("pre", { children: typeof toolResult === "object" ? JSON.stringify(toolResult, null, 2) : toolResult })
2529
+ ] })
2530
+ ] }),
2531
+ formattedTime && /* @__PURE__ */ jsx22("div", { className: "tool-message-time", children: formattedTime })
2532
+ ] });
2533
+ const renderUserAssistantMessage = () => /* @__PURE__ */ jsxs16(Fragment4, { children: [
2534
+ /* @__PURE__ */ jsxs16("div", { className: "message-header", children: [
2535
+ /* @__PURE__ */ jsx22("span", { className: "message-sender", children: role === "user" ? "\u6211" : "AI\u52A9\u624B" }),
2536
+ formattedTime && /* @__PURE__ */ jsx22("span", { className: "message-time", children: formattedTime })
2537
+ ] }),
2538
+ /* @__PURE__ */ jsxs16("div", { className: getContentClassName(), children: [
2539
+ /* @__PURE__ */ jsx22("div", { className: "message-text", children: content }),
2540
+ shouldShowCopyButton() && /* @__PURE__ */ jsx22(
2541
+ "button",
2542
+ {
2543
+ className: `copy-btn ${copied ? "copied" : ""}`,
2544
+ onClick: handleCopy,
2545
+ title: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5185\u5BB9",
2546
+ children: copied ? /* @__PURE__ */ jsxs16(Fragment4, { children: [
2547
+ /* @__PURE__ */ jsx22("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx22("path", { d: "M20 6L9 17L4 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
2548
+ /* @__PURE__ */ jsx22("span", { children: "\u5DF2\u590D\u5236" })
2549
+ ] }) : /* @__PURE__ */ jsxs16(Fragment4, { children: [
2550
+ /* @__PURE__ */ jsxs16("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
2551
+ /* @__PURE__ */ jsx22("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", stroke: "currentColor", strokeWidth: "2" }),
2552
+ /* @__PURE__ */ jsx22("path", { d: "M5 15H4C2.9 15 2 14.1 2 13V4C2 2.9 2.9 2 4 2H13C14.1 2 15 2.9 15 4V5", stroke: "currentColor", strokeWidth: "2" })
2553
+ ] }),
2554
+ /* @__PURE__ */ jsx22("span", { children: "\u590D\u5236" })
2555
+ ] })
2078
2556
  }
2079
- }).catch((error) => {
2080
- console.error("\u53D1\u9001\u6D88\u606F\u5931\u8D25:", error);
2081
- }).finally(() => {
2082
- setIsSending(false);
2083
- });
2084
- } else {
2085
- setInputValue("");
2086
- if (inputRef.current) {
2087
- inputRef.current.style.height = "auto";
2088
- }
2089
- if (isTyping) {
2090
- setIsTyping(false);
2091
- onTyping?.(false);
2092
- }
2093
- setIsSending(false);
2557
+ ),
2558
+ status === "sending" && /* @__PURE__ */ jsx22("span", { className: "message-status sending", children: "\u53D1\u9001\u4E2D..." }),
2559
+ status === "error" && /* @__PURE__ */ jsx22("span", { className: "message-status error", children: "\u53D1\u9001\u5931\u8D25" })
2560
+ ] })
2561
+ ] });
2562
+ const renderMessage = () => {
2563
+ switch (role) {
2564
+ case "system":
2565
+ return renderSystemMessage();
2566
+ case "tool":
2567
+ return renderToolMessage();
2568
+ case "user":
2569
+ case "assistant":
2570
+ default:
2571
+ return renderUserAssistantMessage();
2094
2572
  }
2095
- }, [inputValue, disabled, loading, isSending, onSendMessage, maxInputLength, isTyping, onTyping]);
2096
- const handleKeyDown = useCallback8((e) => {
2097
- if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
2098
- e.preventDefault();
2099
- handleSend();
2573
+ };
2574
+ return /* @__PURE__ */ jsx22("div", { className: getContainerClassName(), style, "data-message-id": id, children: renderMessage() });
2575
+ });
2576
+ SmartMessage.displayName = "SmartMessage";
2577
+ var SmartMessage_default = SmartMessage;
2578
+
2579
+ // src/components/Chat/Chat.tsx
2580
+ import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
2581
+ var Chat = ({
2582
+ messages = [],
2583
+ title = "AI \u52A9\u624B",
2584
+ loading = false,
2585
+ conversations = [],
2586
+ currentConversationId,
2587
+ emptyText = "\u6682\u65E0\u5386\u53F2\u5BF9\u8BDD",
2588
+ newConversationText = "\u65B0\u5EFA\u5BF9\u8BDD",
2589
+ historyTitle = "\u5386\u53F2\u5BF9\u8BDD",
2590
+ showHistoryButton = true,
2591
+ showMinimizeButton = true,
2592
+ showHeader = true,
2593
+ showFooter = false,
2594
+ autoScroll = true,
2595
+ onSwitchConversation,
2596
+ onNewConversation,
2597
+ onDeleteConversation,
2598
+ onMinimize,
2599
+ onRestore,
2600
+ onMessageCopy,
2601
+ className = "",
2602
+ style = {},
2603
+ headerClassName = "",
2604
+ headerStyle = {},
2605
+ messagesClassName = "",
2606
+ messagesStyle = {},
2607
+ bubbleClassName = "",
2608
+ height = "600px",
2609
+ width
2610
+ }) => {
2611
+ const [showHistory, setShowHistory] = useState16(false);
2612
+ const [isMinimized, setIsMinimized] = useState16(false);
2613
+ const messagesContainerRef = useRef9(null);
2614
+ const messagesEndRef = useRef9(null);
2615
+ const historyPanelRef = useRef9(null);
2616
+ const currentConversation = useMemo6(
2617
+ () => conversations.find((c) => c.id === currentConversationId),
2618
+ [conversations, currentConversationId]
2619
+ );
2620
+ const currentTitle = currentConversation?.title || title;
2621
+ useEffect9(() => {
2622
+ if (autoScroll && messagesContainerRef.current && !showHistory && !isMinimized) {
2623
+ messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
2100
2624
  }
2101
- }, [handleSend]);
2102
- const handleInputChange = useCallback8((e) => {
2103
- const value = e.target.value;
2104
- if (value.length <= maxInputLength) {
2105
- setInputValue(value);
2106
- e.target.style.height = "auto";
2107
- e.target.style.height = `${Math.min(e.target.scrollHeight, 100)}px`;
2108
- const hasContent = value.length > 0;
2109
- if (hasContent !== isTyping) {
2110
- setIsTyping(hasContent);
2111
- onTyping?.(hasContent);
2625
+ }, [messages, loading, showHistory, isMinimized, autoScroll]);
2626
+ useEffect9(() => {
2627
+ const handleClickOutside = (event) => {
2628
+ if (showHistory && historyPanelRef.current && !historyPanelRef.current.contains(event.target)) {
2629
+ setShowHistory(false);
2112
2630
  }
2631
+ };
2632
+ document.addEventListener("mousedown", handleClickOutside);
2633
+ return () => document.removeEventListener("mousedown", handleClickOutside);
2634
+ }, [showHistory]);
2635
+ const handleDeleteConversation = useCallback9((conversationId) => {
2636
+ onDeleteConversation?.(conversationId);
2637
+ }, [onDeleteConversation]);
2638
+ const handleMinimize = useCallback9(() => {
2639
+ setIsMinimized(true);
2640
+ onMinimize?.();
2641
+ }, [onMinimize]);
2642
+ const handleRestore = useCallback9(() => {
2643
+ setIsMinimized(false);
2644
+ onRestore?.();
2645
+ }, [onRestore]);
2646
+ const handleCopy = useCallback9((content) => {
2647
+ const message = messages.find((m) => m.content === content);
2648
+ if (message) {
2649
+ onMessageCopy?.(message);
2113
2650
  }
2114
- }, [maxInputLength, isTyping, onTyping]);
2115
- const formatTime = useCallback8((date) => {
2116
- return new Date(date).toLocaleTimeString("zh-CN", {
2117
- hour: "2-digit",
2118
- minute: "2-digit"
2119
- });
2120
- }, []);
2121
- const renderMessage = (message) => {
2122
- const isUser = message.type === "user";
2123
- const isSystem = message.type === "system";
2124
- const isCopied = copiedMessageId === message.id;
2125
- if (isSystem) {
2126
- return /* @__PURE__ */ jsx19("div", { className: "chat-message-system", children: /* @__PURE__ */ jsx19("span", { className: "system-content", children: message.content }) }, message.id);
2127
- }
2128
- return /* @__PURE__ */ jsxs14("div", { className: `chat-message ${isUser ? "user" : "assistant"}`, children: [
2129
- !isUser && /* @__PURE__ */ jsx19("div", { className: "message-avatar", children: assistantName[0] }),
2130
- /* @__PURE__ */ jsxs14("div", { className: "message-content-wrapper", children: [
2131
- /* @__PURE__ */ jsxs14("div", { className: "message-header", children: [
2132
- /* @__PURE__ */ jsx19("span", { className: "message-sender", children: isUser ? userName : assistantName }),
2133
- /* @__PURE__ */ jsx19("span", { className: "message-time", children: formatTime(message.timestamp) })
2134
- ] }),
2135
- /* @__PURE__ */ jsxs14("div", { className: "message-bubble", children: [
2136
- /* @__PURE__ */ jsx19("div", { className: "message-text", children: message.content }),
2137
- message.status === "sending" && /* @__PURE__ */ jsx19("span", { className: "message-status sending", children: "\u53D1\u9001\u4E2D..." }),
2138
- message.status === "error" && /* @__PURE__ */ jsx19("span", { className: "message-status error", children: "\u53D1\u9001\u5931\u8D25" })
2139
- ] }),
2140
- enableCopy && /* @__PURE__ */ jsx19(
2141
- "button",
2142
- {
2143
- className: `copy-btn ${isCopied ? "copied" : ""}`,
2144
- onClick: () => handleCopyMessage(message.id, message.content),
2145
- title: isCopied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5185\u5BB9",
2146
- "aria-label": isCopied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5185\u5BB9",
2147
- children: isCopied ? /* @__PURE__ */ jsxs14(Fragment3, { children: [
2148
- /* @__PURE__ */ jsx19("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx19("path", { d: "M20 6L9 17L4 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
2149
- /* @__PURE__ */ jsx19("span", { children: "\u5DF2\u590D\u5236" })
2150
- ] }) : /* @__PURE__ */ jsxs14(Fragment3, { children: [
2151
- /* @__PURE__ */ jsxs14("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
2152
- /* @__PURE__ */ jsx19("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", stroke: "currentColor", strokeWidth: "2" }),
2153
- /* @__PURE__ */ jsx19("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" })
2154
- ] }),
2155
- /* @__PURE__ */ jsx19("span", { children: "\u590D\u5236" })
2156
- ] })
2157
- }
2158
- )
2159
- ] })
2160
- ] }, message.id);
2161
- };
2162
- return /* @__PURE__ */ jsxs14("div", { className: `chat-container ${currentTheme}`, style: { height }, children: [
2163
- showHeader && /* @__PURE__ */ jsxs14("div", { className: "chat-header", children: [
2164
- /* @__PURE__ */ jsx19("div", { className: "chat-title", children: title }),
2165
- /* @__PURE__ */ jsxs14("div", { className: "chat-status", children: [
2166
- (loading || isSending) && /* @__PURE__ */ jsx19("span", { className: "status-dot" }),
2167
- /* @__PURE__ */ jsx19("span", { children: loading || isSending ? "AI \u6B63\u5728\u601D\u8003..." : "\u5728\u7EBF" })
2651
+ }, [messages, onMessageCopy]);
2652
+ const containerStyle = useMemo6(() => ({
2653
+ height,
2654
+ width,
2655
+ ...style
2656
+ }), [height, width, style]);
2657
+ if (isMinimized) {
2658
+ return /* @__PURE__ */ jsx23("div", { className: `chat-container-minimized ${className}`, style: containerStyle, children: /* @__PURE__ */ jsx23("button", { className: "restore-btn-only", onClick: handleRestore, title: "\u5C55\u5F00", children: /* @__PURE__ */ jsx23("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23("path", { d: "M12 5V19M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }) });
2659
+ }
2660
+ return /* @__PURE__ */ jsxs17("div", { className: `chat-container ${className}`, style: containerStyle, children: [
2661
+ showHeader && /* @__PURE__ */ jsxs17("div", { className: `chat-header ${headerClassName}`, style: headerStyle, children: [
2662
+ /* @__PURE__ */ jsx23("div", { className: "chat-title", children: currentTitle }),
2663
+ /* @__PURE__ */ jsxs17("div", { className: "chat-header-actions", children: [
2664
+ showHistoryButton && /* @__PURE__ */ jsx23("button", { className: "history-btn", onClick: () => setShowHistory(!showHistory), title: "\u5386\u53F2\u5BF9\u8BDD", children: /* @__PURE__ */ jsxs17("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: [
2665
+ /* @__PURE__ */ jsx23("path", { d: "M12 8V12L15 15", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2666
+ /* @__PURE__ */ jsx23("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeWidth: "2" })
2667
+ ] }) }),
2668
+ showMinimizeButton && /* @__PURE__ */ jsx23("button", { className: "minimize-btn", onClick: handleMinimize, title: "\u6700\u5C0F\u5316", children: /* @__PURE__ */ jsx23("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23("path", { d: "M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2168
2669
  ] })
2169
2670
  ] }),
2170
- /* @__PURE__ */ jsxs14("div", { className: "chat-messages", children: [
2171
- messages.map(renderMessage),
2172
- (loading || isSending) && /* @__PURE__ */ jsxs14("div", { className: "chat-message assistant", children: [
2173
- /* @__PURE__ */ jsx19("div", { className: "message-avatar", children: assistantName[0] }),
2174
- /* @__PURE__ */ jsxs14("div", { className: "message-content-wrapper", children: [
2175
- /* @__PURE__ */ jsx19("div", { className: "message-header", children: /* @__PURE__ */ jsx19("span", { className: "message-sender", children: assistantName }) }),
2176
- /* @__PURE__ */ jsx19("div", { className: "message-bubble", children: /* @__PURE__ */ jsxs14("div", { className: "typing-indicator", children: [
2177
- /* @__PURE__ */ jsx19("span", {}),
2178
- /* @__PURE__ */ jsx19("span", {}),
2179
- /* @__PURE__ */ jsx19("span", {})
2180
- ] }) })
2181
- ] })
2182
- ] }),
2183
- /* @__PURE__ */ jsx19("div", { ref: messagesEndRef })
2671
+ /* @__PURE__ */ jsxs17("div", { className: `chat-messages ${messagesClassName}`, ref: messagesContainerRef, style: messagesStyle, children: [
2672
+ messages.map((message) => /* @__PURE__ */ jsx23(
2673
+ SmartMessage_default,
2674
+ {
2675
+ id: message.id,
2676
+ content: message.content,
2677
+ role: message.type,
2678
+ timestamp: message.timestamp,
2679
+ status: message.status,
2680
+ toolName: message.toolName,
2681
+ toolResult: message.toolResult,
2682
+ onCopy: handleCopy,
2683
+ className: bubbleClassName
2684
+ },
2685
+ message.id
2686
+ )),
2687
+ loading && /* @__PURE__ */ jsx23("div", { className: "typing-wrapper", children: /* @__PURE__ */ jsxs17("div", { className: "typing-indicator", children: [
2688
+ /* @__PURE__ */ jsx23("span", {}),
2689
+ /* @__PURE__ */ jsx23("span", {}),
2690
+ /* @__PURE__ */ jsx23("span", {})
2691
+ ] }) }),
2692
+ /* @__PURE__ */ jsx23("div", { ref: messagesEndRef })
2184
2693
  ] }),
2185
- copyError && /* @__PURE__ */ jsx19("div", { className: "copy-error-toast", children: copyError }),
2186
- showFooter && /* @__PURE__ */ jsxs14("div", { className: "chat-footer", children: [
2187
- /* @__PURE__ */ jsxs14("div", { className: "chat-input-wrapper", children: [
2188
- /* @__PURE__ */ jsx19(
2189
- "textarea",
2190
- {
2191
- ref: inputRef,
2192
- className: "chat-input",
2193
- value: inputValue,
2194
- onChange: handleInputChange,
2195
- onKeyDown: handleKeyDown,
2196
- placeholder,
2197
- disabled: disabled || loading || isSending,
2198
- rows: 1,
2199
- maxLength: maxInputLength,
2200
- spellCheck: false,
2201
- autoCorrect: "off",
2202
- autoCapitalize: "off",
2203
- autoComplete: "off"
2204
- }
2205
- ),
2206
- /* @__PURE__ */ jsx19(
2207
- "button",
2208
- {
2209
- className: `chat-send-btn ${!inputValue.trim() || disabled || loading || isSending ? "disabled" : ""}`,
2210
- onClick: handleSend,
2211
- disabled: !inputValue.trim() || disabled || loading || isSending,
2212
- children: "\u53D1\u9001"
2213
- }
2214
- )
2694
+ showFooter && /* @__PURE__ */ jsx23("div", { className: "chat-footer" }),
2695
+ showHistory && /* @__PURE__ */ jsxs17("div", { className: "history-panel", ref: historyPanelRef, children: [
2696
+ /* @__PURE__ */ jsxs17("div", { className: "history-header", children: [
2697
+ /* @__PURE__ */ jsx23("h3", { children: historyTitle }),
2698
+ /* @__PURE__ */ jsx23("button", { className: "close-history-btn", onClick: () => setShowHistory(false), children: "\u2715" })
2215
2699
  ] }),
2216
- /* @__PURE__ */ jsxs14("div", { className: "chat-tips", children: [
2217
- /* @__PURE__ */ jsx19("span", { children: "\u6309 Enter \u53D1\u9001\uFF0CShift + Enter \u6362\u884C" }),
2218
- maxInputLength && /* @__PURE__ */ jsxs14("span", { className: "char-count", children: [
2219
- inputValue.length,
2220
- "/",
2221
- maxInputLength
2222
- ] })
2700
+ /* @__PURE__ */ jsxs17("div", { className: "history-list", children: [
2701
+ /* @__PURE__ */ jsxs17("button", { className: "new-conversation-btn-large", onClick: () => {
2702
+ onNewConversation?.();
2703
+ setShowHistory(false);
2704
+ }, children: [
2705
+ /* @__PURE__ */ jsx23("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx23("path", { d: "M12 5V19M5 12H19", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
2706
+ /* @__PURE__ */ jsx23("span", { children: newConversationText })
2707
+ ] }),
2708
+ conversations.length === 0 ? /* @__PURE__ */ jsx23("div", { className: "empty-history", children: /* @__PURE__ */ jsx23("p", { children: emptyText }) }) : conversations.map((conv) => /* @__PURE__ */ jsxs17(
2709
+ "div",
2710
+ {
2711
+ className: `history-item ${currentConversationId === conv.id ? "active" : ""}`,
2712
+ onClick: () => {
2713
+ onSwitchConversation?.(conv.id);
2714
+ setShowHistory(false);
2715
+ },
2716
+ children: [
2717
+ /* @__PURE__ */ jsxs17("div", { className: "history-item-content", children: [
2718
+ /* @__PURE__ */ jsx23("div", { className: "history-item-title", children: /* @__PURE__ */ jsx23("span", { children: conv.title }) }),
2719
+ /* @__PURE__ */ jsx23("div", { className: "history-item-preview", children: conv.lastMessage }),
2720
+ /* @__PURE__ */ jsxs17("div", { className: "history-item-meta", children: [
2721
+ /* @__PURE__ */ jsx23("span", { children: new Date(conv.timestamp).toLocaleDateString() }),
2722
+ /* @__PURE__ */ jsxs17("span", { children: [
2723
+ conv.messages.length,
2724
+ "\u6761\u6D88\u606F"
2725
+ ] })
2726
+ ] })
2727
+ ] }),
2728
+ /* @__PURE__ */ jsx23("div", { className: "history-item-actions", children: /* @__PURE__ */ jsx23(
2729
+ "button",
2730
+ {
2731
+ className: "delete-conversation-btn",
2732
+ onClick: (e) => {
2733
+ e.stopPropagation();
2734
+ handleDeleteConversation(conv.id);
2735
+ },
2736
+ title: "\u5220\u9664\u5BF9\u8BDD",
2737
+ children: /* @__PURE__ */ jsxs17("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: [
2738
+ /* @__PURE__ */ jsx23("path", { d: "M3 6H21", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
2739
+ /* @__PURE__ */ jsx23("path", { d: "M19 6V20C19 21.1 18.1 22 17 22H7C5.9 22 5 21.1 5 20V6", stroke: "currentColor", strokeWidth: "2" }),
2740
+ /* @__PURE__ */ jsx23("path", { d: "M8 6V4C8 2.9 8.9 2 10 2H14C15.1 2 16 2.9 16 4V6", stroke: "currentColor", strokeWidth: "2" })
2741
+ ] })
2742
+ }
2743
+ ) })
2744
+ ]
2745
+ },
2746
+ conv.id
2747
+ ))
2223
2748
  ] })
2224
2749
  ] })
2225
2750
  ] });
@@ -2233,16 +2758,20 @@ export {
2233
2758
  Card,
2234
2759
  Chat,
2235
2760
  CheckButton,
2761
+ ContextMenuProvider,
2236
2762
  DeleteDialog,
2237
2763
  Input,
2238
2764
  ListGroup,
2239
2765
  ListGroupItem,
2766
+ MarkdownEditor,
2240
2767
  OrphanDialog,
2241
2768
  RollingBox,
2242
2769
  Select,
2243
2770
  SideBar,
2244
2771
  Slider,
2245
- Tabs,
2772
+ SmartMessage,
2773
+ TabBar,
2774
+ TagItem,
2246
2775
  ThemeProvider,
2247
2776
  Tree,
2248
2777
  VirtualList,
@@ -2251,5 +2780,6 @@ export {
2251
2780
  isDescendantOf,
2252
2781
  lazyLoad,
2253
2782
  useAlert,
2783
+ useContextMenu,
2254
2784
  useTheme
2255
2785
  };