@zero-library/chat-copilot 3.1.4 → 3.1.6

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.js CHANGED
@@ -158,7 +158,10 @@ var Attachments_default = React10.forwardRef(({ fileUpload, fileUploadConfig, fi
158
158
  const uploadedFile = data[0];
159
159
  let previewUrl = uploadedFile.id;
160
160
  if (uploadedFile.conversationId && uploadedFile.path) {
161
- previewUrl = chatStore.config.services.request.getPreviewUrl(uploadedFile.conversationId, uploadedFile.path);
161
+ previewUrl = chatStore.config.services.request.getPreviewUrl({
162
+ path: uploadedFile.path,
163
+ workspaceId: uploadedFile.conversationId
164
+ });
162
165
  }
163
166
  setAttachedFiles(
164
167
  getAttachedFiles().map((f) => {
@@ -246,20 +249,34 @@ var Attachments_default = React10.forwardRef(({ fileUpload, fileUploadConfig, fi
246
249
 
247
250
  // src/components/ChatInput/index.module.less
248
251
  var index_module_default = {
252
+ promptEditorInputContainer: "index_module_promptEditorInputContainer",
253
+ promptEditorContent: "index_module_promptEditorContent",
254
+ promptEditorPlaceholder: "index_module_promptEditorPlaceholder",
255
+ resourceSelect: "index_module_resourceSelect",
249
256
  fileMenu: "index_module_fileMenu",
257
+ fileMenuGroup: "index_module_fileMenuGroup",
258
+ fileMenuGroupTitle: "index_module_fileMenuGroupTitle",
259
+ fileMenuEmpty: "index_module_fileMenuEmpty",
250
260
  fileMenuItem: "index_module_fileMenuItem",
251
261
  fileMenuItemActive: "index_module_fileMenuItemActive",
262
+ fileIcon: "index_module_fileIcon",
252
263
  fileName: "index_module_fileName",
253
- resourceSelect: "index_module_resourceSelect",
254
- promptEditorInputContainer: "index_module_promptEditorInputContainer",
255
- promptEditorContent: "index_module_promptEditorContent",
256
- promptEditorPlaceholder: "index_module_promptEditorPlaceholder",
257
264
  resourceTag: "index_module_resourceTag"
258
265
  };
259
266
 
260
267
  // src/core/constants.ts
261
268
  var CONV_MODE = {
262
269
  PROD: 1};
270
+ var TOKEN_KEY = "NS-TOKEN";
271
+ var tokenManager = common.createTokenManager({
272
+ key: TOKEN_KEY
273
+ });
274
+ var redirectUrl = () => {
275
+ const path = localStorage.getItem("SIGNPATH");
276
+ common.emit("jumpLink", { url: `/uc${path || "/sign-in"}` });
277
+ };
278
+
279
+ // src/core/utils.ts
263
280
  var classifyTime = (timestamp) => {
264
281
  const now = dayjs__default.default();
265
282
  const target = dayjs__default.default(timestamp);
@@ -306,27 +323,52 @@ var getFileIcon = (extension, fontSize = 24) => {
306
323
  }
307
324
  return createIcon(icons.FileUnknownOutlined, "#94a3b8");
308
325
  };
309
- function FilePickerMenu({
310
- options,
326
+ var getFileUrl = (params, baseUrl, url) => {
327
+ const data = {
328
+ [TOKEN_KEY]: tokenManager.get(),
329
+ workspaceType: "conversation" /* CONVERSATION */,
330
+ ...params
331
+ };
332
+ return common.buildUrlParams(data, url || `${baseUrl}/files/access`);
333
+ };
334
+ function GroupPicker({
335
+ groups,
311
336
  selectedIndex,
312
337
  onSelect,
313
338
  onHover
314
339
  }) {
315
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: index_module_default.fileMenu, role: "listbox", children: options.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
316
- "div",
317
- {
318
- role: "option",
319
- "aria-selected": index === selectedIndex,
320
- className: `${index_module_default.fileMenuItem} ${index === selectedIndex ? index_module_default.fileMenuItemActive : ""}`,
321
- onMouseEnter: () => onHover(index),
322
- onClick: () => onSelect(option),
323
- children: [
324
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: getFileIcon(option.ext || common.getFileSuffixName(option.label), 14) }),
325
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: index_module_default.fileName, children: option.label })
326
- ]
327
- },
328
- option.value
329
- )) });
340
+ const itemRefs = React10.useRef([]);
341
+ let currentIndex = -1;
342
+ React10.useEffect(() => {
343
+ itemRefs.current[selectedIndex]?.scrollIntoView({
344
+ block: "nearest"
345
+ });
346
+ }, [groups, selectedIndex]);
347
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: index_module_default.fileMenu, role: "listbox", children: groups.map(({ group, options }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: index_module_default.fileMenuGroup, children: [
348
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: index_module_default.fileMenuGroupTitle, children: group.label }),
349
+ options.length ? options.map((option) => {
350
+ currentIndex += 1;
351
+ const optionIndex = currentIndex;
352
+ return /* @__PURE__ */ jsxRuntime.jsxs(
353
+ "div",
354
+ {
355
+ ref: (element) => {
356
+ itemRefs.current[optionIndex] = element;
357
+ },
358
+ role: "option",
359
+ "aria-selected": optionIndex === selectedIndex,
360
+ className: `${index_module_default.fileMenuItem} ${optionIndex === selectedIndex ? index_module_default.fileMenuItemActive : ""}`,
361
+ onMouseEnter: () => onHover(optionIndex),
362
+ onClick: () => onSelect(group, option),
363
+ children: [
364
+ option.ext && /* @__PURE__ */ jsxRuntime.jsx("span", { className: index_module_default.fileIcon, children: getFileIcon(option.ext, 14) }),
365
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: index_module_default.fileName, children: option.label })
366
+ ]
367
+ },
368
+ `${group.value}-${option.value}`
369
+ );
370
+ }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: index_module_default.fileMenuEmpty, children: "\u6682\u65E0\u6570\u636E" })
371
+ ] }, group.value)) });
330
372
  }
331
373
  function ResourcePickerMenu({
332
374
  anchorElementRef,
@@ -375,7 +417,6 @@ function ResourceTag({ resource }) {
375
417
  formatResourceName(resource.resourceName)
376
418
  ] }) });
377
419
  }
378
- var EMPTY_CASCADE_OPTION_VALUE = "__empty__";
379
420
  var VariableNode = class _VariableNode extends lexical.DecoratorNode {
380
421
  __resourceLabel;
381
422
  __resourceType;
@@ -466,34 +507,25 @@ var VariableNode = class _VariableNode extends lexical.DecoratorNode {
466
507
  function $createVariableNode(resourceLabel, resourceName, resourceType, resourceId) {
467
508
  return new VariableNode(resourceLabel, resourceName, resourceType, resourceId);
468
509
  }
469
- var CascadeOption = class extends LexicalTypeaheadMenuPlugin.MenuOption {
470
- category;
510
+ var ResourceOption = class extends LexicalTypeaheadMenuPlugin.MenuOption {
511
+ group;
471
512
  item;
472
513
  /**
473
- * 将级联菜单中的父子节点包装成 Lexical 菜单选项对象。
514
+ * 将分组列表中的资源节点包装成 Lexical 菜单选项对象。
474
515
  */
475
- constructor(category, item) {
476
- super(`${category.value}:${item.value}`);
477
- this.category = category;
516
+ constructor(group, item) {
517
+ super(`${group.value}:${item.value}`);
518
+ this.group = group;
478
519
  this.item = item;
479
520
  }
480
521
  };
481
- var FileOption = class extends LexicalTypeaheadMenuPlugin.MenuOption {
482
- file;
483
- /**
484
- * 将文件列表项包装成 Lexical 菜单选项对象。
485
- */
486
- constructor(file) {
487
- super(file.value);
488
- this.file = file;
489
- }
490
- };
491
522
  function VariablePickerPlugin({ commandConfig = {} }) {
492
523
  const [editor] = LexicalComposerContext.useLexicalComposerContext();
493
524
  const chatStore = useChatStore();
494
525
  const [commandDataSource, setCommandDataSource] = React10.useState(commandConfig);
495
526
  const [queryString, setQueryString] = React10.useState(null);
496
527
  const [isEditorFocused, setIsEditorFocused] = React10.useState(false);
528
+ const [menuResetKey, setMenuResetKey] = React10.useState(0);
497
529
  const blurTimerRef = React10.useRef(null);
498
530
  React10.useEffect(() => {
499
531
  const unregister = utils.mergeRegister(
@@ -510,6 +542,7 @@ function VariablePickerPlugin({ commandConfig = {} }) {
510
542
  lexical.BLUR_COMMAND,
511
543
  () => {
512
544
  setIsEditorFocused(false);
545
+ setMenuResetKey((current) => current + 1);
513
546
  return false;
514
547
  },
515
548
  lexical.COMMAND_PRIORITY_LOW
@@ -524,6 +557,7 @@ function VariablePickerPlugin({ commandConfig = {} }) {
524
557
  setCommandDataSource(commandConfig);
525
558
  }, [commandConfig]);
526
559
  const commandKey = queryString?.[0] || "";
560
+ const searchKeyword = queryString?.slice(commandKey.length).trim().toLocaleLowerCase() || "";
527
561
  const categoryConfig = React10.useMemo(
528
562
  () => ({
529
563
  skill: {
@@ -541,58 +575,82 @@ function VariablePickerPlugin({ commandConfig = {} }) {
541
575
  }),
542
576
  [chatStore]
543
577
  );
544
- const commandOptions = React10.useMemo(() => {
545
- if (!isEditorFocused) return [];
546
- return (commandDataSource[commandKey]?.dataSource || []).flatMap(
547
- (category) => category.children?.filter((item) => !item.disabled).map((item) => new CascadeOption(category, item)) || []
548
- );
549
- }, [commandDataSource, commandKey, isEditorFocused]);
550
578
  const formatDataSource = React10.useCallback(
551
579
  (items, category) => {
552
580
  const currentCategoryConfig = categoryConfig[category.value];
553
- if (!items.length) {
554
- return [
555
- {
556
- value: EMPTY_CASCADE_OPTION_VALUE,
557
- label: "\u6682\u65E0\u6570\u636E",
558
- isLeaf: true,
559
- disabled: true
560
- }
561
- ];
562
- }
581
+ if (!items.length) return [];
563
582
  return items.map((item) => ({
564
583
  value: item.id,
565
- label: currentCategoryConfig?.getLabel(item) || item.name
584
+ label: currentCategoryConfig?.getLabel(item) || item.name,
585
+ children: []
566
586
  }));
567
587
  },
568
588
  [categoryConfig]
569
589
  );
570
- const loadData = React10.useCallback(
571
- (selectedOptions) => {
572
- const category = selectedOptions[selectedOptions.length - 1];
573
- if (!category || category.children?.length || category.isLeaf) return;
574
- const currentCategoryConfig = categoryConfig[category.value];
575
- if (!currentCategoryConfig) return;
576
- currentCategoryConfig.load().then((response) => {
577
- const items = response.data || [];
578
- setCommandDataSource((current) => ({
579
- ...current,
580
- [commandKey]: {
581
- ...current[commandKey],
582
- dataSource: current[commandKey]?.dataSource?.map(
583
- (item) => item.value === category.value ? { ...item, children: formatDataSource(items, category) } : item
584
- ) || []
585
- }
586
- }));
587
- });
588
- },
589
- [categoryConfig, commandKey, formatDataSource]
590
+ React10.useEffect(() => {
591
+ let isUnmounted = false;
592
+ const initializeCommandDataSource = async () => {
593
+ const nextEntries = await Promise.all(
594
+ Object.entries(commandConfig).map(async ([trigger, currentCommandConfig]) => {
595
+ const nextDataSource = await Promise.all(
596
+ (currentCommandConfig.dataSource || []).map(async (category) => {
597
+ const normalizedChildren = category.children || [];
598
+ const nextCategory = {
599
+ ...category,
600
+ children: normalizedChildren
601
+ };
602
+ const currentCategoryConfig = categoryConfig[category.value];
603
+ if (!currentCategoryConfig) {
604
+ return nextCategory;
605
+ }
606
+ if (normalizedChildren.length) {
607
+ return nextCategory;
608
+ }
609
+ const response = await currentCategoryConfig.load();
610
+ const items = response.data || [];
611
+ return {
612
+ ...nextCategory,
613
+ children: formatDataSource(items, category)
614
+ };
615
+ })
616
+ );
617
+ return [
618
+ trigger,
619
+ {
620
+ ...currentCommandConfig,
621
+ dataSource: nextDataSource
622
+ }
623
+ ];
624
+ })
625
+ );
626
+ if (isUnmounted) return;
627
+ setCommandDataSource(Object.fromEntries(nextEntries));
628
+ };
629
+ initializeCommandDataSource();
630
+ return () => {
631
+ isUnmounted = true;
632
+ };
633
+ }, [categoryConfig, commandConfig, formatDataSource]);
634
+ const commandGroups = React10.useMemo(() => {
635
+ if (!isEditorFocused) return [];
636
+ return (commandDataSource[commandKey]?.dataSource || []).map((group) => {
637
+ const options = group.children || [];
638
+ if (!searchKeyword) return { group, options };
639
+ return {
640
+ group,
641
+ options: options.filter((option) => option.label.toLocaleLowerCase().includes(searchKeyword))
642
+ };
643
+ }).filter(({ options }) => !searchKeyword || options.length);
644
+ }, [commandDataSource, commandKey, isEditorFocused, searchKeyword]);
645
+ const commandOptions = React10.useMemo(
646
+ () => commandGroups.flatMap(({ group, options }) => options.map((item) => new ResourceOption(group, item))),
647
+ [commandGroups]
590
648
  );
591
649
  const onSelectOption = React10.useCallback(
592
650
  (selectedOption, nodeToReplace, closeMenu, matchingString) => {
593
- const resource = selectedOption instanceof FileOption ? selectedOption.file : selectedOption.item;
594
- const resourceLabel = selectedOption instanceof FileOption ? "\u6587\u4EF6" : selectedOption.category.label;
595
- const resourceType = selectedOption instanceof FileOption ? "file" : selectedOption.category.value;
651
+ const resource = selectedOption.item;
652
+ const resourceLabel = selectedOption.group.label;
653
+ const resourceType = selectedOption.group.value;
596
654
  editor.update(() => {
597
655
  const variableNode = $createVariableNode(resourceLabel, resource.label, resourceType, resource.value);
598
656
  if (nodeToReplace) {
@@ -602,7 +660,7 @@ function VariablePickerPlugin({ commandConfig = {} }) {
602
660
  });
603
661
  closeMenu();
604
662
  },
605
- [commandDataSource, editor]
663
+ [editor]
606
664
  );
607
665
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(
608
666
  LexicalTypeaheadMenuPlugin.LexicalTypeaheadMenuPlugin,
@@ -616,40 +674,24 @@ function VariablePickerPlugin({ commandConfig = {} }) {
616
674
  if (!match) return null;
617
675
  return { leadOffset: match.index, matchingString: match[0], replaceableString: match[0] };
618
676
  },
619
- options: commandOptions.map((option) => new CascadeOption(option.category, option.item)),
620
- menuRenderFn: (anchorElementRef, { selectedIndex, setHighlightedIndex, selectOptionAndCleanUp }, matchingString) => {
621
- if (!isEditorFocused || !anchorElementRef.current) return null;
677
+ options: commandOptions,
678
+ menuRenderFn: (anchorElementRef, { selectedIndex, setHighlightedIndex, selectOptionAndCleanUp }) => {
679
+ if (!isEditorFocused || !anchorElementRef.current || searchKeyword && !commandGroups.length) return null;
622
680
  return ReactDOM__namespace.createPortal(
623
- /* @__PURE__ */ jsxRuntime.jsxs(ResourcePickerMenu, { anchorElementRef, children: [
624
- commandDataSource[matchingString]?.component === "cascade" && /* @__PURE__ */ jsxRuntime.jsx(
625
- antd.Cascader.Panel,
626
- {
627
- options: commandDataSource[matchingString]?.dataSource,
628
- expandTrigger: "hover",
629
- loadData,
630
- onChange: (value) => {
631
- const [categoryValue, itemValue] = value;
632
- const option = commandOptions.find(
633
- (currentOption) => currentOption.category.value === categoryValue && currentOption.item.value === itemValue
634
- );
635
- if (option) selectOptionAndCleanUp(option);
636
- }
637
- }
638
- ),
639
- commandDataSource[matchingString]?.component === "list" && /* @__PURE__ */ jsxRuntime.jsx(
640
- FilePickerMenu,
641
- {
642
- options: commandDataSource[matchingString]?.dataSource || [],
643
- selectedIndex: selectedIndex ?? 0,
644
- onHover: setHighlightedIndex,
645
- onSelect: (option) => selectOptionAndCleanUp(new FileOption(option))
646
- }
647
- )
648
- ] }),
681
+ /* @__PURE__ */ jsxRuntime.jsx(ResourcePickerMenu, { anchorElementRef, children: /* @__PURE__ */ jsxRuntime.jsx(
682
+ GroupPicker,
683
+ {
684
+ groups: commandGroups,
685
+ selectedIndex: selectedIndex ?? 0,
686
+ onHover: setHighlightedIndex,
687
+ onSelect: (group, option) => selectOptionAndCleanUp(new ResourceOption(group, option))
688
+ }
689
+ ) }),
649
690
  anchorElementRef.current
650
691
  );
651
692
  }
652
- }
693
+ },
694
+ menuResetKey
653
695
  ) });
654
696
  }
655
697
  function InitialStatePlugin({ value }) {
@@ -872,14 +914,6 @@ var ChatSender_default = React10.forwardRef(
872
914
  );
873
915
  }
874
916
  );
875
- var TOKEN_KEY = "NS-TOKEN";
876
- var tokenManager = common.createTokenManager({
877
- key: TOKEN_KEY
878
- });
879
- var redirectUrl = () => {
880
- const path = localStorage.getItem("SIGNPATH");
881
- common.emit("jumpLink", { url: `/uc${path || "/sign-in"}` });
882
- };
883
917
  var createChatService = (request, config) => {
884
918
  const fetchModelList = () => {
885
919
  return request.get(`/providers/models/type/llm`);
@@ -920,14 +954,8 @@ var createChatService = (request, config) => {
920
954
  signal
921
955
  });
922
956
  };
923
- const getPreviewUrl = (conversationId, path) => {
924
- const params = {
925
- path,
926
- [TOKEN_KEY]: tokenManager.get(),
927
- workspaceId: conversationId,
928
- workspaceType: "conversation" /* CONVERSATION */
929
- };
930
- return common.buildUrlParams(params, `${config?.baseURL}/files/access`);
957
+ const getPreviewUrl = (params) => {
958
+ return getFileUrl(params, config?.baseURL);
931
959
  };
932
960
  const docQuery = (params) => {
933
961
  return request.get(`/library/cloud/document?${params}`);
@@ -2736,7 +2764,10 @@ var PreviewLink_default = ({ data, loading, message: message2, ...rest }) => {
2736
2764
  const fileName = getLinkFileName(href);
2737
2765
  const suffix = common.getFileSuffixName(fileName);
2738
2766
  chatStore.setPreview({
2739
- fileUrl: chatStore.config.services.request.getPreviewUrl(message2.conversationId, href),
2767
+ fileUrl: chatStore.config.services.request.getPreviewUrl({
2768
+ path: href,
2769
+ workspaceId: message2.conversationId
2770
+ }),
2740
2771
  fileName,
2741
2772
  suffix
2742
2773
  });
@@ -4299,11 +4330,13 @@ function A2uiRenderer(props) {
4299
4330
  var FilesNode = React10__namespace.default.memo(({ item }) => {
4300
4331
  const chatStore = useChatStore();
4301
4332
  const configState = valtio.useSnapshot(chatStore.config);
4302
- valtio.useSnapshot(chatStore.agent);
4303
4333
  const conversationState = valtio.useSnapshot(chatStore.conversation);
4304
4334
  const fileItems = React10.useMemo(() => {
4305
4335
  return (item.files || []).map((file) => {
4306
- const previewUrl = configState.services.request.getPreviewUrl(conversationState.active.id, file.path);
4336
+ const previewUrl = configState.services.request.getPreviewUrl({
4337
+ path: file.path,
4338
+ workspaceId: conversationState.active.id
4339
+ });
4307
4340
  let fileType = "file";
4308
4341
  const ext = file.ext?.toLowerCase() || "";
4309
4342
  if (["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "webp"].includes(ext)) {
@@ -5587,5 +5620,7 @@ var layouts_default = React10.forwardRef(({ theme: theme4, params, hooks, layout
5587
5620
  exports.Attachments = Attachments_default;
5588
5621
  exports.ChatCopilot = layouts_default;
5589
5622
  exports.ChatSender = ChatSender_default;
5623
+ exports.getFileIcon = getFileIcon;
5624
+ exports.getFileUrl = getFileUrl;
5590
5625
  //# sourceMappingURL=index.cjs.js.map
5591
5626
  //# sourceMappingURL=index.cjs.js.map