@zero-library/chat-copilot 3.1.5 → 3.1.7

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.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CloudUploadOutlined, PaperClipOutlined, createFromIconfontCN, LoadingOutlined, EnterOutlined, DownloadOutlined, CloseOutlined, PlayCircleOutlined, CopyOutlined, DotChartOutlined, LikeOutlined, DislikeOutlined, ToolOutlined, SearchOutlined, UpOutlined, DownOutlined, PlusOutlined, CommentOutlined, RedoOutlined, DeleteOutlined, ClockCircleOutlined, CheckCircleOutlined, FileImageOutlined, FilePdfOutlined, FileWordOutlined, FileExcelOutlined, FilePptOutlined, FileZipOutlined, FileTextOutlined, FileUnknownOutlined } from '@ant-design/icons';
2
2
  import { Attachments, Sender, FileCard, Bubble, Think, XProvider, Mermaid, CodeHighlighter, Welcome, Prompts, Conversations } from '@ant-design/x-v2';
3
- import { useRefState, useDebounce, useSyncInput, createTokenManager, isEmptyObj, markdownToText, RenderWrapper, shouldRender, isFunction, downloadFile, LuyaFilePreview, MarkdownEditor, useDeepEffect, isObject, useWebSocket, isNullOrUnDef, isNumber, getFileSuffixName, FileIcon, copyText, LazyComponent, isBoolean, UserAvatar, htmlToMarkdown, buildUrlParams, isExternal, deepCopy, transforms, deepMerge, createRequest, HttpStatus, isArray, isString, transform, emit, getWebSocketUrl, safeParseJson } from '@zero-library/common';
4
- import { App, Badge, Button, Flex, Typography, Tooltip, Layout, Tag, Spin, Splitter, Cascader, Image as Image$1, Popover, Skeleton, Alert, theme, Collapse, Divider as Divider$1, Select as Select$1, Avatar, Space, Drawer, Empty, Modal, Checkbox, Input, message, List, Card, Table, Progress as Progress$1, Switch as Switch$1 } from 'antd';
3
+ import { useRefState, useDebounce, createTokenManager, useSyncInput, isEmptyObj, markdownToText, RenderWrapper, shouldRender, isFunction, downloadFile, LuyaFilePreview, MarkdownEditor, useDeepEffect, isObject, useWebSocket, isNullOrUnDef, isNumber, getFileSuffixName, FileIcon, copyText, LazyComponent, isBoolean, UserAvatar, htmlToMarkdown, buildUrlParams, isExternal, deepCopy, transforms, deepMerge, createRequest, HttpStatus, isArray, isString, transform, emit, getWebSocketUrl, safeParseJson } from '@zero-library/common';
4
+ import { App, Badge, Button, Flex, Typography, Tooltip, Layout, Tag, Spin, Splitter, Image as Image$1, Popover, Skeleton, Alert, theme, Collapse, Divider as Divider$1, Select as Select$1, Avatar, Space, Drawer, Empty, Modal, Checkbox, Input, message, List, Card, Table, Progress as Progress$1, Switch as Switch$1 } from 'antd';
5
5
  import * as React10 from 'react';
6
6
  import React10__default, { createContext, forwardRef, useRef, useEffect, useImperativeHandle, useMemo, useCallback, memo, useState, useContext, useLayoutEffect } from 'react';
7
7
  import { useSnapshot, proxy } from 'valtio';
@@ -129,7 +129,10 @@ var Attachments_default = forwardRef(({ fileUpload, fileUploadConfig, fileList =
129
129
  const uploadedFile = data[0];
130
130
  let previewUrl = uploadedFile.id;
131
131
  if (uploadedFile.conversationId && uploadedFile.path) {
132
- previewUrl = chatStore.config.services.request.getPreviewUrl(uploadedFile.conversationId, uploadedFile.path);
132
+ previewUrl = chatStore.config.services.request.getPreviewUrl({
133
+ path: uploadedFile.path,
134
+ workspaceId: uploadedFile.conversationId
135
+ });
133
136
  }
134
137
  setAttachedFiles(
135
138
  getAttachedFiles().map((f) => {
@@ -222,8 +225,12 @@ var index_module_default = {
222
225
  promptEditorPlaceholder: "index_module_promptEditorPlaceholder",
223
226
  resourceSelect: "index_module_resourceSelect",
224
227
  fileMenu: "index_module_fileMenu",
228
+ fileMenuGroup: "index_module_fileMenuGroup",
229
+ fileMenuGroupTitle: "index_module_fileMenuGroupTitle",
230
+ fileMenuEmpty: "index_module_fileMenuEmpty",
225
231
  fileMenuItem: "index_module_fileMenuItem",
226
232
  fileMenuItemActive: "index_module_fileMenuItemActive",
233
+ fileIcon: "index_module_fileIcon",
227
234
  fileName: "index_module_fileName",
228
235
  resourceTag: "index_module_resourceTag"
229
236
  };
@@ -231,6 +238,16 @@ var index_module_default = {
231
238
  // src/core/constants.ts
232
239
  var CONV_MODE = {
233
240
  PROD: 1};
241
+ var TOKEN_KEY = "NS-TOKEN";
242
+ var tokenManager = createTokenManager({
243
+ key: TOKEN_KEY
244
+ });
245
+ var redirectUrl = () => {
246
+ const path = localStorage.getItem("SIGNPATH");
247
+ emit("jumpLink", { url: `/uc${path || "/sign-in"}` });
248
+ };
249
+
250
+ // src/core/utils.ts
234
251
  var classifyTime = (timestamp) => {
235
252
  const now = dayjs();
236
253
  const target = dayjs(timestamp);
@@ -277,27 +294,52 @@ var getFileIcon = (extension, fontSize = 24) => {
277
294
  }
278
295
  return createIcon(FileUnknownOutlined, "#94a3b8");
279
296
  };
280
- function FilePickerMenu({
281
- options,
297
+ var getFileUrl = (params, baseUrl, url) => {
298
+ const data = {
299
+ [TOKEN_KEY]: tokenManager.get(),
300
+ workspaceType: "conversation" /* CONVERSATION */,
301
+ ...params
302
+ };
303
+ return buildUrlParams(data, url || `${baseUrl}/files/access`);
304
+ };
305
+ function GroupPicker({
306
+ groups,
282
307
  selectedIndex,
283
308
  onSelect,
284
309
  onHover
285
310
  }) {
286
- return /* @__PURE__ */ jsx("div", { className: index_module_default.fileMenu, role: "listbox", children: options.map((option, index) => /* @__PURE__ */ jsxs(
287
- "div",
288
- {
289
- role: "option",
290
- "aria-selected": index === selectedIndex,
291
- className: `${index_module_default.fileMenuItem} ${index === selectedIndex ? index_module_default.fileMenuItemActive : ""}`,
292
- onMouseEnter: () => onHover(index),
293
- onClick: () => onSelect(option),
294
- children: [
295
- /* @__PURE__ */ jsx("span", { children: getFileIcon(option.ext || getFileSuffixName(option.label), 14) }),
296
- /* @__PURE__ */ jsx("span", { className: index_module_default.fileName, children: option.label })
297
- ]
298
- },
299
- option.value
300
- )) });
311
+ const itemRefs = useRef([]);
312
+ let currentIndex = -1;
313
+ useEffect(() => {
314
+ itemRefs.current[selectedIndex]?.scrollIntoView({
315
+ block: "nearest"
316
+ });
317
+ }, [groups, selectedIndex]);
318
+ return /* @__PURE__ */ jsx("div", { className: index_module_default.fileMenu, role: "listbox", children: groups.map(({ group, options }) => /* @__PURE__ */ jsxs("div", { className: index_module_default.fileMenuGroup, children: [
319
+ /* @__PURE__ */ jsx("div", { className: index_module_default.fileMenuGroupTitle, children: group.label }),
320
+ options.length ? options.map((option) => {
321
+ currentIndex += 1;
322
+ const optionIndex = currentIndex;
323
+ return /* @__PURE__ */ jsxs(
324
+ "div",
325
+ {
326
+ ref: (element) => {
327
+ itemRefs.current[optionIndex] = element;
328
+ },
329
+ role: "option",
330
+ "aria-selected": optionIndex === selectedIndex,
331
+ className: `${index_module_default.fileMenuItem} ${optionIndex === selectedIndex ? index_module_default.fileMenuItemActive : ""}`,
332
+ onMouseEnter: () => onHover(optionIndex),
333
+ onClick: () => onSelect(group, option),
334
+ children: [
335
+ option.ext && /* @__PURE__ */ jsx("span", { className: index_module_default.fileIcon, children: getFileIcon(option.ext, 14) }),
336
+ /* @__PURE__ */ jsx("span", { className: index_module_default.fileName, children: option.label })
337
+ ]
338
+ },
339
+ `${group.value}-${option.value}`
340
+ );
341
+ }) : /* @__PURE__ */ jsx("div", { className: index_module_default.fileMenuEmpty, children: "\u6682\u65E0\u6570\u636E" })
342
+ ] }, group.value)) });
301
343
  }
302
344
  function ResourcePickerMenu({
303
345
  anchorElementRef,
@@ -346,7 +388,6 @@ function ResourceTag({ resource }) {
346
388
  formatResourceName(resource.resourceName)
347
389
  ] }) });
348
390
  }
349
- var EMPTY_CASCADE_OPTION_VALUE = "__empty__";
350
391
  var VariableNode = class _VariableNode extends DecoratorNode {
351
392
  __resourceLabel;
352
393
  __resourceType;
@@ -437,34 +478,25 @@ var VariableNode = class _VariableNode extends DecoratorNode {
437
478
  function $createVariableNode(resourceLabel, resourceName, resourceType, resourceId) {
438
479
  return new VariableNode(resourceLabel, resourceName, resourceType, resourceId);
439
480
  }
440
- var CascadeOption = class extends MenuOption {
441
- category;
481
+ var ResourceOption = class extends MenuOption {
482
+ group;
442
483
  item;
443
484
  /**
444
- * 将级联菜单中的父子节点包装成 Lexical 菜单选项对象。
485
+ * 将分组列表中的资源节点包装成 Lexical 菜单选项对象。
445
486
  */
446
- constructor(category, item) {
447
- super(`${category.value}:${item.value}`);
448
- this.category = category;
487
+ constructor(group, item) {
488
+ super(`${group.value}:${item.value}`);
489
+ this.group = group;
449
490
  this.item = item;
450
491
  }
451
492
  };
452
- var FileOption = class extends MenuOption {
453
- file;
454
- /**
455
- * 将文件列表项包装成 Lexical 菜单选项对象。
456
- */
457
- constructor(file) {
458
- super(file.value);
459
- this.file = file;
460
- }
461
- };
462
493
  function VariablePickerPlugin({ commandConfig = {} }) {
463
494
  const [editor] = useLexicalComposerContext();
464
495
  const chatStore = useChatStore();
465
496
  const [commandDataSource, setCommandDataSource] = useState(commandConfig);
466
497
  const [queryString, setQueryString] = useState(null);
467
498
  const [isEditorFocused, setIsEditorFocused] = useState(false);
499
+ const [menuResetKey, setMenuResetKey] = useState(0);
468
500
  const blurTimerRef = useRef(null);
469
501
  useEffect(() => {
470
502
  const unregister = mergeRegister(
@@ -481,6 +513,7 @@ function VariablePickerPlugin({ commandConfig = {} }) {
481
513
  BLUR_COMMAND,
482
514
  () => {
483
515
  setIsEditorFocused(false);
516
+ setMenuResetKey((current) => current + 1);
484
517
  return false;
485
518
  },
486
519
  COMMAND_PRIORITY_LOW
@@ -495,6 +528,7 @@ function VariablePickerPlugin({ commandConfig = {} }) {
495
528
  setCommandDataSource(commandConfig);
496
529
  }, [commandConfig]);
497
530
  const commandKey = queryString?.[0] || "";
531
+ const searchKeyword = queryString?.slice(commandKey.length).trim().toLocaleLowerCase() || "";
498
532
  const categoryConfig = useMemo(
499
533
  () => ({
500
534
  skill: {
@@ -512,58 +546,82 @@ function VariablePickerPlugin({ commandConfig = {} }) {
512
546
  }),
513
547
  [chatStore]
514
548
  );
515
- const commandOptions = useMemo(() => {
516
- if (!isEditorFocused) return [];
517
- return (commandDataSource[commandKey]?.dataSource || []).flatMap(
518
- (category) => category.children?.filter((item) => !item.disabled).map((item) => new CascadeOption(category, item)) || []
519
- );
520
- }, [commandDataSource, commandKey, isEditorFocused]);
521
549
  const formatDataSource = useCallback(
522
550
  (items, category) => {
523
551
  const currentCategoryConfig = categoryConfig[category.value];
524
- if (!items.length) {
525
- return [
526
- {
527
- value: EMPTY_CASCADE_OPTION_VALUE,
528
- label: "\u6682\u65E0\u6570\u636E",
529
- isLeaf: true,
530
- disabled: true
531
- }
532
- ];
533
- }
552
+ if (!items.length) return [];
534
553
  return items.map((item) => ({
535
554
  value: item.id,
536
- label: currentCategoryConfig?.getLabel(item) || item.name
555
+ label: currentCategoryConfig?.getLabel(item) || item.name,
556
+ children: []
537
557
  }));
538
558
  },
539
559
  [categoryConfig]
540
560
  );
541
- const loadData = useCallback(
542
- (selectedOptions) => {
543
- const category = selectedOptions[selectedOptions.length - 1];
544
- if (!category || category.children?.length || category.isLeaf) return;
545
- const currentCategoryConfig = categoryConfig[category.value];
546
- if (!currentCategoryConfig) return;
547
- currentCategoryConfig.load().then((response) => {
548
- const items = response.data || [];
549
- setCommandDataSource((current) => ({
550
- ...current,
551
- [commandKey]: {
552
- ...current[commandKey],
553
- dataSource: current[commandKey]?.dataSource?.map(
554
- (item) => item.value === category.value ? { ...item, children: formatDataSource(items, category) } : item
555
- ) || []
556
- }
557
- }));
558
- });
559
- },
560
- [categoryConfig, commandKey, formatDataSource]
561
+ useEffect(() => {
562
+ let isUnmounted = false;
563
+ const initializeCommandDataSource = async () => {
564
+ const nextEntries = await Promise.all(
565
+ Object.entries(commandConfig).map(async ([trigger, currentCommandConfig]) => {
566
+ const nextDataSource = await Promise.all(
567
+ (currentCommandConfig.dataSource || []).map(async (category) => {
568
+ const normalizedChildren = category.children || [];
569
+ const nextCategory = {
570
+ ...category,
571
+ children: normalizedChildren
572
+ };
573
+ const currentCategoryConfig = categoryConfig[category.value];
574
+ if (!currentCategoryConfig) {
575
+ return nextCategory;
576
+ }
577
+ if (normalizedChildren.length) {
578
+ return nextCategory;
579
+ }
580
+ const response = await currentCategoryConfig.load();
581
+ const items = response.data || [];
582
+ return {
583
+ ...nextCategory,
584
+ children: formatDataSource(items, category)
585
+ };
586
+ })
587
+ );
588
+ return [
589
+ trigger,
590
+ {
591
+ ...currentCommandConfig,
592
+ dataSource: nextDataSource
593
+ }
594
+ ];
595
+ })
596
+ );
597
+ if (isUnmounted) return;
598
+ setCommandDataSource(Object.fromEntries(nextEntries));
599
+ };
600
+ initializeCommandDataSource();
601
+ return () => {
602
+ isUnmounted = true;
603
+ };
604
+ }, [categoryConfig, commandConfig, formatDataSource]);
605
+ const commandGroups = useMemo(() => {
606
+ if (!isEditorFocused) return [];
607
+ return (commandDataSource[commandKey]?.dataSource || []).map((group) => {
608
+ const options = group.children || [];
609
+ if (!searchKeyword) return { group, options };
610
+ return {
611
+ group,
612
+ options: options.filter((option) => option.label.toLocaleLowerCase().includes(searchKeyword))
613
+ };
614
+ }).filter(({ options }) => !searchKeyword || options.length);
615
+ }, [commandDataSource, commandKey, isEditorFocused, searchKeyword]);
616
+ const commandOptions = useMemo(
617
+ () => commandGroups.flatMap(({ group, options }) => options.map((item) => new ResourceOption(group, item))),
618
+ [commandGroups]
561
619
  );
562
620
  const onSelectOption = useCallback(
563
621
  (selectedOption, nodeToReplace, closeMenu, matchingString) => {
564
- const resource = selectedOption instanceof FileOption ? selectedOption.file : selectedOption.item;
565
- const resourceLabel = selectedOption instanceof FileOption ? "\u6587\u4EF6" : selectedOption.category.label;
566
- const resourceType = selectedOption instanceof FileOption ? "file" : selectedOption.category.value;
622
+ const resource = selectedOption.item;
623
+ const resourceLabel = selectedOption.group.label;
624
+ const resourceType = selectedOption.group.value;
567
625
  editor.update(() => {
568
626
  const variableNode = $createVariableNode(resourceLabel, resource.label, resourceType, resource.value);
569
627
  if (nodeToReplace) {
@@ -573,7 +631,7 @@ function VariablePickerPlugin({ commandConfig = {} }) {
573
631
  });
574
632
  closeMenu();
575
633
  },
576
- [commandDataSource, editor]
634
+ [editor]
577
635
  );
578
636
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
579
637
  LexicalTypeaheadMenuPlugin,
@@ -587,40 +645,24 @@ function VariablePickerPlugin({ commandConfig = {} }) {
587
645
  if (!match) return null;
588
646
  return { leadOffset: match.index, matchingString: match[0], replaceableString: match[0] };
589
647
  },
590
- options: commandOptions.map((option) => new CascadeOption(option.category, option.item)),
591
- menuRenderFn: (anchorElementRef, { selectedIndex, setHighlightedIndex, selectOptionAndCleanUp }, matchingString) => {
592
- if (!anchorElementRef.current) return null;
648
+ options: commandOptions,
649
+ menuRenderFn: (anchorElementRef, { selectedIndex, setHighlightedIndex, selectOptionAndCleanUp }) => {
650
+ if (!isEditorFocused || !anchorElementRef.current || searchKeyword && !commandGroups.length) return null;
593
651
  return ReactDOM.createPortal(
594
- /* @__PURE__ */ jsxs(ResourcePickerMenu, { anchorElementRef, children: [
595
- commandDataSource[matchingString]?.component === "cascade" && /* @__PURE__ */ jsx(
596
- Cascader.Panel,
597
- {
598
- options: commandDataSource[matchingString]?.dataSource,
599
- expandTrigger: "hover",
600
- loadData,
601
- onChange: (value) => {
602
- const [categoryValue, itemValue] = value;
603
- const option = commandOptions.find(
604
- (currentOption) => currentOption.category.value === categoryValue && currentOption.item.value === itemValue
605
- );
606
- if (option) selectOptionAndCleanUp(option);
607
- }
608
- }
609
- ),
610
- commandDataSource[matchingString]?.component === "list" && /* @__PURE__ */ jsx(
611
- FilePickerMenu,
612
- {
613
- options: commandDataSource[matchingString]?.dataSource || [],
614
- selectedIndex: selectedIndex ?? 0,
615
- onHover: setHighlightedIndex,
616
- onSelect: (option) => selectOptionAndCleanUp(new FileOption(option))
617
- }
618
- )
619
- ] }),
652
+ /* @__PURE__ */ jsx(ResourcePickerMenu, { anchorElementRef, children: /* @__PURE__ */ jsx(
653
+ GroupPicker,
654
+ {
655
+ groups: commandGroups,
656
+ selectedIndex: selectedIndex ?? 0,
657
+ onHover: setHighlightedIndex,
658
+ onSelect: (group, option) => selectOptionAndCleanUp(new ResourceOption(group, option))
659
+ }
660
+ ) }),
620
661
  anchorElementRef.current
621
662
  );
622
663
  }
623
- }
664
+ },
665
+ menuResetKey
624
666
  ) });
625
667
  }
626
668
  function InitialStatePlugin({ value }) {
@@ -843,14 +885,6 @@ var ChatSender_default = forwardRef(
843
885
  );
844
886
  }
845
887
  );
846
- var TOKEN_KEY = "NS-TOKEN";
847
- var tokenManager = createTokenManager({
848
- key: TOKEN_KEY
849
- });
850
- var redirectUrl = () => {
851
- const path = localStorage.getItem("SIGNPATH");
852
- emit("jumpLink", { url: `/uc${path || "/sign-in"}` });
853
- };
854
888
  var createChatService = (request, config) => {
855
889
  const fetchModelList = () => {
856
890
  return request.get(`/providers/models/type/llm`);
@@ -891,14 +925,8 @@ var createChatService = (request, config) => {
891
925
  signal
892
926
  });
893
927
  };
894
- const getPreviewUrl = (conversationId, path) => {
895
- const params = {
896
- path,
897
- [TOKEN_KEY]: tokenManager.get(),
898
- workspaceId: conversationId,
899
- workspaceType: "conversation" /* CONVERSATION */
900
- };
901
- return buildUrlParams(params, `${config?.baseURL}/files/access`);
928
+ const getPreviewUrl = (params) => {
929
+ return getFileUrl(params, config?.baseURL);
902
930
  };
903
931
  const docQuery = (params) => {
904
932
  return request.get(`/library/cloud/document?${params}`);
@@ -2707,7 +2735,10 @@ var PreviewLink_default = ({ data, loading, message: message2, ...rest }) => {
2707
2735
  const fileName = getLinkFileName(href);
2708
2736
  const suffix = getFileSuffixName(fileName);
2709
2737
  chatStore.setPreview({
2710
- fileUrl: chatStore.config.services.request.getPreviewUrl(message2.conversationId, href),
2738
+ fileUrl: chatStore.config.services.request.getPreviewUrl({
2739
+ path: href,
2740
+ workspaceId: message2.conversationId
2741
+ }),
2711
2742
  fileName,
2712
2743
  suffix
2713
2744
  });
@@ -3231,6 +3262,135 @@ var A2uiComponents = {
3231
3262
  ReportViewer
3232
3263
  };
3233
3264
 
3265
+ // src/components/CustomComponents/A2uiRuntime/controller/extract.ts
3266
+ var A2UI_TAG_REGEX = /<A2UI\s+id="([^"]*)"\s*>([\s\S]*?)<\/A2UI>/g;
3267
+ function hasA2uiTag(text) {
3268
+ return typeof text === "string" && text.includes("<A2UI");
3269
+ }
3270
+ function parseA2uiTagsFromText(text) {
3271
+ if (!hasA2uiTag(text)) return [];
3272
+ const tags = [];
3273
+ const regex = new RegExp(A2UI_TAG_REGEX);
3274
+ let match;
3275
+ while ((match = regex.exec(text)) !== null) {
3276
+ const tagId = match[1];
3277
+ const json = match[2].trim();
3278
+ if (!json) continue;
3279
+ try {
3280
+ const parsed = JSON.parse(json);
3281
+ const commands = Array.isArray(parsed) ? parsed : [parsed];
3282
+ if (commands.length) tags.push({ tagId, commands });
3283
+ } catch {
3284
+ }
3285
+ }
3286
+ return tags;
3287
+ }
3288
+ function stripA2uiTags(text) {
3289
+ if (!hasA2uiTag(text)) return text;
3290
+ let result = text.replace(A2UI_TAG_REGEX, "");
3291
+ result = result.replace(/<A2UI\b[\s\S]*$/, "");
3292
+ return result.trim();
3293
+ }
3294
+ function splitA2uiTextSegments(text) {
3295
+ if (typeof text !== "string" || !text.includes("<A2UI")) {
3296
+ return text ? [{ type: "text", content: text }] : [];
3297
+ }
3298
+ const segments = [];
3299
+ const regex = new RegExp(A2UI_TAG_REGEX);
3300
+ let lastIndex = 0;
3301
+ let match;
3302
+ while ((match = regex.exec(text)) !== null) {
3303
+ if (match.index > lastIndex) {
3304
+ const before = text.slice(lastIndex, match.index);
3305
+ if (before) segments.push({ type: "text", content: before });
3306
+ }
3307
+ const json = match[2].trim();
3308
+ if (json) {
3309
+ try {
3310
+ const parsed = JSON.parse(json);
3311
+ const commands = Array.isArray(parsed) ? parsed : [parsed];
3312
+ const surfaceIds = commands.map((cmd) => String(cmd?.createSurface?.surfaceId || "").trim()).filter(Boolean);
3313
+ if (surfaceIds.length) {
3314
+ segments.push({ type: "a2ui", surfaceIds });
3315
+ }
3316
+ } catch {
3317
+ }
3318
+ }
3319
+ lastIndex = regex.lastIndex;
3320
+ }
3321
+ if (lastIndex < text.length) {
3322
+ let remaining = text.slice(lastIndex);
3323
+ remaining = remaining.replace(/<A2UI\b[\s\S]*$/, "");
3324
+ if (remaining) segments.push({ type: "text", content: remaining });
3325
+ }
3326
+ return segments.length ? segments : [];
3327
+ }
3328
+ function extractInlineSurfaceIdsFromMessage(message2) {
3329
+ const surfaceIds = /* @__PURE__ */ new Set();
3330
+ const items = Array.isArray(message2?.content) ? message2.content : [];
3331
+ for (const item of items) {
3332
+ if (item?.type !== "text") continue;
3333
+ const text = item.messageContent || item.text || "";
3334
+ if (typeof text !== "string" || !text.includes("<A2UI")) continue;
3335
+ const tags = parseA2uiTagsFromText(text);
3336
+ for (const tag of tags) {
3337
+ for (const cmd of tag.commands) {
3338
+ const sid = String(cmd?.createSurface?.surfaceId || "").trim();
3339
+ if (sid) surfaceIds.add(sid);
3340
+ }
3341
+ }
3342
+ }
3343
+ return surfaceIds;
3344
+ }
3345
+ function extractDismissedSurfaceIdsFromMessages(messages) {
3346
+ const lastCreateIndexBySurface = /* @__PURE__ */ new Map();
3347
+ const lastActionIndexBySurface = /* @__PURE__ */ new Map();
3348
+ for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
3349
+ const message2 = messages[messageIndex];
3350
+ const items = Array.isArray(message2?.content) ? message2.content : [];
3351
+ for (const item of items) {
3352
+ if (message2?.role !== 1) {
3353
+ if (item?.type === "a2uiCommand" && item.command) {
3354
+ const surfaceId = String(item.command?.createSurface?.surfaceId || "").trim();
3355
+ if (surfaceId) lastCreateIndexBySurface.set(surfaceId, messageIndex);
3356
+ }
3357
+ if (item?.type === "text") {
3358
+ const text = item.messageContent || item.text || "";
3359
+ if (typeof text === "string" && text.includes("<A2UI")) {
3360
+ const tags = parseA2uiTagsFromText(text);
3361
+ for (const tag of tags) {
3362
+ for (const cmd of tag.commands) {
3363
+ const surfaceId = String(cmd?.createSurface?.surfaceId || "").trim();
3364
+ if (surfaceId) lastCreateIndexBySurface.set(surfaceId, messageIndex);
3365
+ }
3366
+ }
3367
+ }
3368
+ }
3369
+ }
3370
+ if (message2?.role === 1 && item?.type === "text") {
3371
+ const text = item.messageContent || item.text || "";
3372
+ if (typeof text !== "string" || !text.includes("a2ui_action.v1")) continue;
3373
+ try {
3374
+ const parsed = JSON.parse(text.trim());
3375
+ if (parsed?.type !== "a2ui_action.v1") continue;
3376
+ const surfaceId = String(parsed.surfaceId || "").trim();
3377
+ if (!surfaceId) continue;
3378
+ lastActionIndexBySurface.set(surfaceId, messageIndex);
3379
+ } catch {
3380
+ }
3381
+ }
3382
+ }
3383
+ }
3384
+ const dismissed = [];
3385
+ for (const [surfaceId, actionIndex] of lastActionIndexBySurface) {
3386
+ const createIndex = lastCreateIndexBySurface.get(surfaceId);
3387
+ if (createIndex === void 0 || actionIndex > createIndex) {
3388
+ dismissed.push(surfaceId);
3389
+ }
3390
+ }
3391
+ return dismissed;
3392
+ }
3393
+
3234
3394
  // src/components/CustomComponents/A2uiRuntime/controller/meta.ts
3235
3395
  function readProp(current, props, key) {
3236
3396
  return current[key] ?? props?.[key];
@@ -3398,19 +3558,8 @@ function inferModelPathFromSurfaceCommands(surfaceId, commands) {
3398
3558
  });
3399
3559
  return commonParentPath(paths);
3400
3560
  }
3401
- function isSubmitLikeActionName(name) {
3402
- const trimmed = typeof name === "string" ? name.trim() : "";
3403
- return trimmed === "submit" || trimmed === "confirm" || trimmed.startsWith("submit_") || trimmed.endsWith(".confirm");
3404
- }
3405
- function isCancelLikeActionName(name) {
3406
- const trimmed = typeof name === "string" ? name.trim() : "";
3407
- return trimmed === "cancel" || trimmed === "qf.cancel" || trimmed.endsWith(".cancel");
3408
- }
3409
3561
  function shouldOptimisticallyDismissSurface(actionName, context) {
3410
- if (isSubmitLikeActionName(actionName) || isCancelLikeActionName(actionName)) return true;
3411
- if (actionName === "qf.submit" || actionName === "qf.cancel") return true;
3412
- const submitFlag = context?.submit;
3413
- return submitFlag === true;
3562
+ return true;
3414
3563
  }
3415
3564
  function buildDispatchContext(surfaceId, rawContext, commands) {
3416
3565
  let normalizedContext = normalizeActionContext(rawContext);
@@ -3709,6 +3858,7 @@ function buildWizardUpdateComponentsCommand(schema, stepId) {
3709
3858
  function createA2uiRuntimeState() {
3710
3859
  return {
3711
3860
  processedMessageCursors: [],
3861
+ processedTagIds: [],
3712
3862
  serverCommands: [],
3713
3863
  localCommands: [],
3714
3864
  anchorIndexBySurface: {},
@@ -3768,6 +3918,57 @@ function initializeWizardSurface(state, surfaceId, effects, nextWizardSteps) {
3768
3918
  nextWizardSteps[surfaceId] = firstStepId.trim();
3769
3919
  }
3770
3920
  }
3921
+ function processA2uiCommand(cmd, messageIndex, messageId, nextState, effects, deletedSurfaceIds, nextWizardSteps) {
3922
+ nextState.serverCommands.push(cmd);
3923
+ nextState.lastA2uiHost = { id: messageId, index: messageIndex };
3924
+ const surfaceId = extractSurfaceId2(cmd);
3925
+ if (!surfaceId) return;
3926
+ if (cmd?.createSurface) {
3927
+ nextState.anchorIndexBySurface[surfaceId] = messageIndex;
3928
+ } else if (nextState.anchorIndexBySurface[surfaceId] === void 0 && !cmd?.deleteSurface) {
3929
+ nextState.anchorIndexBySurface[surfaceId] = messageIndex;
3930
+ }
3931
+ if (cmd?.createSurface) {
3932
+ nextState.latestCreateCommandIndexBySurface[surfaceId] = nextState.serverCommands.length - 1;
3933
+ nextState.optimisticallyDismissedSurfaceIds = nextState.optimisticallyDismissedSurfaceIds.filter((id) => id !== surfaceId);
3934
+ initializeWizardSurface(nextState, surfaceId, effects, nextWizardSteps);
3935
+ }
3936
+ if (cmd?.updateDataModel) {
3937
+ const { path, value } = cmd.updateDataModel;
3938
+ if (typeof path === "string" && path.trim() && value && typeof value === "object" && !Array.isArray(value)) {
3939
+ effects.push({ type: "write-model", modelPath: path, values: value });
3940
+ }
3941
+ }
3942
+ if (cmd?.deleteSurface) {
3943
+ deletedSurfaceIds.add(surfaceId);
3944
+ const modelPath = nextState.modelPathBySurface[surfaceId];
3945
+ if (modelPath) effects.push({ type: "clear-model", modelPath });
3946
+ delete nextState.initializedWizardRevisionBySurface[surfaceId];
3947
+ delete nextState.modelPathBySurface[surfaceId];
3948
+ }
3949
+ }
3950
+ function scanA2uiTagsFromMessages(messages, processedTagIds) {
3951
+ const newTags = [];
3952
+ const newTagIds = [];
3953
+ for (let messageIndex = 0; messageIndex < messages.length; messageIndex += 1) {
3954
+ const message2 = messages[messageIndex];
3955
+ const messageId = String(message2?.messageId ?? messageIndex);
3956
+ const items = Array.isArray(message2?.content) ? message2.content : [];
3957
+ for (const item of items) {
3958
+ if (item?.type !== "text") continue;
3959
+ const text = item.messageContent || item.text || "";
3960
+ if (typeof text !== "string" || !text.includes("<A2UI")) continue;
3961
+ const tags = parseA2uiTagsFromText(text);
3962
+ for (const tag of tags) {
3963
+ if (processedTagIds.has(tag.tagId)) continue;
3964
+ processedTagIds.add(tag.tagId);
3965
+ newTagIds.push(tag.tagId);
3966
+ newTags.push({ commands: tag.commands, messageIndex, messageId });
3967
+ }
3968
+ }
3969
+ }
3970
+ return { newTags, newTagIds };
3971
+ }
3771
3972
  function reduceA2uiRuntimeMessages(state, messages) {
3772
3973
  const { shouldReplayAll, pendingItems, nextCursors } = diffConversationMessages(messages || [], state.processedMessageCursors);
3773
3974
  const itemsToProcess = shouldReplayAll ? messages.flatMap((message2, messageIndex) => {
@@ -3775,11 +3976,14 @@ function reduceA2uiRuntimeMessages(state, messages) {
3775
3976
  const items = Array.isArray(message2?.content) ? message2.content : [];
3776
3977
  return items.map((item) => ({ messageIndex, messageId, item }));
3777
3978
  }) : pendingItems;
3778
- if (!shouldReplayAll && itemsToProcess.length === 0) {
3979
+ const tagProcessedSet = new Set(shouldReplayAll ? [] : state.processedTagIds);
3980
+ const { newTags, newTagIds } = scanA2uiTagsFromMessages(messages || [], tagProcessedSet);
3981
+ if (!shouldReplayAll && itemsToProcess.length === 0 && newTags.length === 0) {
3779
3982
  return { state, effects: [] };
3780
3983
  }
3781
3984
  const nextState = shouldReplayAll ? createA2uiRuntimeState() : { ...state };
3782
3985
  nextState.processedMessageCursors = shouldReplayAll ? [] : [...state.processedMessageCursors];
3986
+ nextState.processedTagIds = shouldReplayAll ? [] : [...state.processedTagIds, ...newTagIds];
3783
3987
  nextState.serverCommands = shouldReplayAll ? [] : [...state.serverCommands];
3784
3988
  nextState.localCommands = shouldReplayAll ? [] : [...state.localCommands];
3785
3989
  nextState.anchorIndexBySurface = shouldReplayAll ? {} : { ...state.anchorIndexBySurface };
@@ -3802,24 +4006,13 @@ function reduceA2uiRuntimeMessages(state, messages) {
3802
4006
  }
3803
4007
  return;
3804
4008
  }
3805
- if (item?.type !== "a2uiCommand" || !item.command) return;
3806
- nextState.serverCommands.push(item.command);
3807
- nextState.lastA2uiHost = { id: messageId, index: messageIndex };
3808
- const surfaceId = extractSurfaceId2(item.command);
3809
- if (!surfaceId) return;
3810
- if (nextState.anchorIndexBySurface[surfaceId] === void 0 && !item.command?.deleteSurface) {
3811
- nextState.anchorIndexBySurface[surfaceId] = messageIndex;
3812
- }
3813
- if (item.command?.createSurface) {
3814
- nextState.latestCreateCommandIndexBySurface[surfaceId] = nextState.serverCommands.length - 1;
3815
- initializeWizardSurface(nextState, surfaceId, effects, nextWizardSteps);
3816
- }
3817
- if (item.command?.deleteSurface) {
3818
- deletedSurfaceIds.add(surfaceId);
3819
- const modelPath = nextState.modelPathBySurface[surfaceId];
3820
- if (modelPath) effects.push({ type: "clear-model", modelPath });
3821
- delete nextState.initializedWizardRevisionBySurface[surfaceId];
3822
- delete nextState.modelPathBySurface[surfaceId];
4009
+ if (item?.type === "a2uiCommand" && item.command) {
4010
+ processA2uiCommand(item.command, messageIndex, messageId, nextState, effects, deletedSurfaceIds, nextWizardSteps);
4011
+ }
4012
+ });
4013
+ newTags.forEach(({ commands, messageIndex, messageId }) => {
4014
+ for (const cmd of commands) {
4015
+ processA2uiCommand(cmd, messageIndex, messageId, nextState, effects, deletedSurfaceIds, nextWizardSteps);
3823
4016
  }
3824
4017
  });
3825
4018
  nextState.processedMessageCursors = shouldReplayAll ? messages.map((message2, messageIndex) => ({
@@ -3839,7 +4032,7 @@ function reduceA2uiRuntimeMessages(state, messages) {
3839
4032
  return { state: nextState, effects };
3840
4033
  }
3841
4034
  function reduceA2uiRuntimeAction(state, input) {
3842
- const { surfaceId, actionName, rawContext } = input;
4035
+ const { surfaceId, actionName} = input;
3843
4036
  if (!surfaceId) {
3844
4037
  return { state, handled: actionName === "wizard.next" || actionName === "wizard.back" };
3845
4038
  }
@@ -3869,9 +4062,6 @@ function reduceA2uiRuntimeAction(state, input) {
3869
4062
  if (actionName === "wizard.next" || actionName === "wizard.back") {
3870
4063
  return { state, handled: true };
3871
4064
  }
3872
- if (!shouldOptimisticallyDismissSurface(actionName, rawContext)) {
3873
- return { state, handled: false };
3874
- }
3875
4065
  if (state.optimisticallyDismissedSurfaceIds.includes(surfaceId)) {
3876
4066
  return { state, handled: false };
3877
4067
  }
@@ -3936,12 +4126,20 @@ function useA2uiController(params) {
3936
4126
  applyA2uiRuntimeEffects(result.effects);
3937
4127
  setRuntimeState(result.state);
3938
4128
  }, [conversationMessages, conversationId]);
3939
- const view = useMemo(() => selectA2uiRuntimeView(runtimeState, dismissedSurfaceIds), [runtimeState, dismissedSurfaceIds]);
4129
+ const permanentDismissedSurfaceIds = useMemo(
4130
+ () => extractDismissedSurfaceIdsFromMessages(conversationMessages || []),
4131
+ [conversationMessages]
4132
+ );
4133
+ const mergedDismissedSurfaceIds = useMemo(
4134
+ () => Array.from(/* @__PURE__ */ new Set([...dismissedSurfaceIds || [], ...permanentDismissedSurfaceIds])),
4135
+ [dismissedSurfaceIds, permanentDismissedSurfaceIds]
4136
+ );
4137
+ const view = useMemo(() => selectA2uiRuntimeView(runtimeState, mergedDismissedSurfaceIds), [runtimeState, mergedDismissedSurfaceIds]);
3940
4138
  const handleRuntimeAction = useCallback((payload) => {
3941
4139
  const surfaceId = String(payload?.surfaceId || payload?.cardId || "");
3942
4140
  const name = String(payload?.name || payload?.event?.name || "").trim();
3943
4141
  const rawContext = payload?.context || payload?.event?.context || {};
3944
- const runtimeActionResult = reduceA2uiRuntimeAction(runtimeRef.current, { surfaceId, actionName: name, rawContext });
4142
+ const runtimeActionResult = reduceA2uiRuntimeAction(runtimeRef.current, { surfaceId, actionName: name});
3945
4143
  if (runtimeActionResult.state !== runtimeRef.current) {
3946
4144
  runtimeRef.current = runtimeActionResult.state;
3947
4145
  setRuntimeState(runtimeActionResult.state);
@@ -3968,26 +4166,26 @@ var A2uiRuntimeContext = React10__default.createContext({
3968
4166
  function useA2uiRuntimeView() {
3969
4167
  return React10__default.useContext(A2uiRuntimeContext);
3970
4168
  }
3971
- function A2uiMessageCards({ messageIndex }) {
4169
+ function A2uiMessageCards({ messageIndex, message: message2 }) {
3972
4170
  const { surfaceIdsByMessageIndex } = useA2uiRuntimeView();
3973
- const surfaceIds = surfaceIdsByMessageIndex[messageIndex] || [];
4171
+ const allSurfaceIds = surfaceIdsByMessageIndex[messageIndex] || [];
4172
+ const inlineSurfaceIds = message2 ? extractInlineSurfaceIdsFromMessage(message2) : /* @__PURE__ */ new Set();
4173
+ const surfaceIds = allSurfaceIds.filter((id) => !inlineSurfaceIds.has(id));
3974
4174
  if (!surfaceIds.length) return null;
3975
4175
  return /* @__PURE__ */ jsx("div", { children: surfaceIds.map((surfaceId) => /* @__PURE__ */ jsx(XCard.Card, { id: surfaceId }, surfaceId)) });
3976
4176
  }
3977
4177
 
3978
4178
  // src/components/CustomComponents/A2uiRuntime/renderer/protocol.ts
3979
- function isSubmitLikeActionName2(name) {
4179
+ function isSubmitLikeActionName(name) {
3980
4180
  const normalized = String(name || "").trim();
3981
4181
  return normalized === "submit" || normalized === "confirm" || normalized.startsWith("submit_") || normalized.endsWith(".confirm");
3982
4182
  }
3983
- function isCancelLikeActionName2(name) {
4183
+ function isCancelLikeActionName(name) {
3984
4184
  const normalized = String(name || "").trim();
3985
4185
  return normalized === "cancel" || normalized === "qf.cancel" || normalized.endsWith(".cancel");
3986
4186
  }
3987
4187
  function shouldDismissA2uiSurface(name, context) {
3988
- if (isSubmitLikeActionName2(name) || isCancelLikeActionName2(name)) return true;
3989
- if (name === "qf.submit" || name === "qf.cancel") return true;
3990
- return context?.submit === true;
4188
+ return true;
3991
4189
  }
3992
4190
  function normalizeA2uiActionPayload(payload) {
3993
4191
  return {
@@ -4021,7 +4219,7 @@ function isQuestionActionPayload(payload) {
4021
4219
  const mode = payload.context?.form_mode;
4022
4220
  if (payload.name === "qf.submit" || payload.name === "qf.cancel") return true;
4023
4221
  if (payload.name.startsWith("qf.")) return true;
4024
- return typeof mode === "string" && mode.toLowerCase() === "ephemeral" ? isSubmitLikeActionName2(payload.name) || isCancelLikeActionName2(payload.name) : false;
4222
+ return typeof mode === "string" && mode.toLowerCase() === "ephemeral" ? isSubmitLikeActionName(payload.name) || isCancelLikeActionName(payload.name) : false;
4025
4223
  }
4026
4224
  function formatActionValue(value, displayOverride) {
4027
4225
  if (typeof displayOverride === "string" && displayOverride.trim()) return displayOverride.trim();
@@ -4031,7 +4229,7 @@ function formatActionValue(value, displayOverride) {
4031
4229
  return String(value);
4032
4230
  }
4033
4231
  function buildQuestionSummary(payload) {
4034
- if (isCancelLikeActionName2(payload.name)) return "Question: \u5DF2\u53D6\u6D88";
4232
+ if (isCancelLikeActionName(payload.name)) return "Question: \u5DF2\u53D6\u6D88";
4035
4233
  const values = payload.context.values && typeof payload.context.values === "object" && !Array.isArray(payload.context.values) ? payload.context.values : payload.context;
4036
4234
  const labels = payload.context.labels && typeof payload.context.labels === "object" && !Array.isArray(payload.context.labels) ? payload.context.labels : {};
4037
4235
  const valueLabels = payload.context.valueLabels && typeof payload.context.valueLabels === "object" && !Array.isArray(payload.context.valueLabels) ? payload.context.valueLabels : {};
@@ -4053,10 +4251,22 @@ function formatA2uiActionDisplayText(payload) {
4053
4251
  back_to_params: "\u5DF2\u8FD4\u56DE\u4FEE\u6539\u53C2\u6570",
4054
4252
  confirm: "\u5DF2\u786E\u8BA4\u521B\u5EFA"
4055
4253
  };
4056
- const actionLabel = actionLabels[payload.name] || `\u5DF2\u63D0\u4EA4\u52A8\u4F5C (${payload.name})`;
4254
+ let actionLabel = actionLabels[payload.name];
4255
+ if (!actionLabel) {
4256
+ if (payload.name.startsWith("confirm_")) {
4257
+ actionLabel = "\u5DF2\u9009\u62E9";
4258
+ } else if (payload.name.startsWith("submit_")) {
4259
+ actionLabel = "\u5DF2\u63D0\u4EA4";
4260
+ } else {
4261
+ actionLabel = `\u5DF2\u63D0\u4EA4\u52A8\u4F5C (${payload.name})`;
4262
+ }
4263
+ }
4264
+ const isConfirmLike = payload.name.startsWith("confirm_");
4057
4265
  const details = Object.entries(values).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => {
4266
+ const formatted = formatActionValue(value, valueLabels[key]);
4267
+ if (isConfirmLike) return formatted;
4058
4268
  const label = typeof labels[key] === "string" && String(labels[key]).trim() ? String(labels[key]).trim() : key;
4059
- return `${label}: ${formatActionValue(value, valueLabels[key])}`;
4269
+ return `${label}: ${formatted}`;
4060
4270
  });
4061
4271
  return details.length ? `${actionLabel}\uFF1A${details.join("\uFF1B ")}` : actionLabel;
4062
4272
  }
@@ -4154,66 +4364,46 @@ function readContextModelPath(context) {
4154
4364
  const path = context?.model && typeof context.model === "object" ? context.model.path : "";
4155
4365
  return typeof path === "string" ? path.trim() : "";
4156
4366
  }
4157
- function diffMessages(messages, prevCursors) {
4158
- if (messages.length < prevCursors.length) {
4159
- return { shouldReplayAll: true, nextCursors: [], pendingItems: [] };
4160
- }
4161
- const nextCursors = [];
4162
- const pendingItems = [];
4163
- for (let messageIndex = 0; messageIndex < messages.length; messageIndex += 1) {
4164
- const message2 = messages[messageIndex];
4165
- const messageId = String(message2?.messageId ?? messageIndex);
4367
+ function buildLatestCommandIndexBySurface(messages) {
4368
+ const indexBySurface = {};
4369
+ let commandIndex = -1;
4370
+ for (const message2 of messages) {
4166
4371
  const items = Array.isArray(message2?.content) ? message2.content : [];
4167
- const prevCursor = prevCursors[messageIndex];
4168
- const startIndex = prevCursor ? prevCursor.contentLength : 0;
4169
- if (prevCursor && (prevCursor.messageId !== messageId || items.length < prevCursor.contentLength)) {
4170
- return { shouldReplayAll: true, nextCursors: [], pendingItems: [] };
4171
- }
4172
- for (let itemIndex = startIndex; itemIndex < items.length; itemIndex += 1) {
4173
- pendingItems.push({ item: items[itemIndex] });
4372
+ for (const item of items) {
4373
+ if (item?.type === "a2uiCommand" && item.command) {
4374
+ commandIndex += 1;
4375
+ const surfaceId = extractSurfaceId3(item.command);
4376
+ if (surfaceId) indexBySurface[surfaceId] = commandIndex;
4377
+ continue;
4378
+ }
4379
+ if (item?.type === "text") {
4380
+ const text = item.messageContent || item.text || "";
4381
+ if (typeof text !== "string" || !text.includes("<A2UI")) continue;
4382
+ const tags = parseA2uiTagsFromText(text);
4383
+ for (const tag of tags) {
4384
+ for (const cmd of tag.commands) {
4385
+ commandIndex += 1;
4386
+ const surfaceId = extractSurfaceId3(cmd);
4387
+ if (surfaceId) indexBySurface[surfaceId] = commandIndex;
4388
+ }
4389
+ }
4390
+ }
4174
4391
  }
4175
- nextCursors.push({ messageId, contentLength: items.length });
4176
4392
  }
4177
- return { shouldReplayAll: false, nextCursors, pendingItems };
4393
+ return indexBySurface;
4178
4394
  }
4179
4395
  function A2uiRuntimeProvider({ messages, children }) {
4180
4396
  const chatStore = useChatStore();
4181
4397
  const conversationState = useSnapshot(chatStore.conversation);
4182
4398
  const conversationId = conversationState.active.id;
4183
- const processedMessageCursorsRef = React10__default.useRef([]);
4184
- const latestCommandIndexBySurfaceRef = React10__default.useRef({});
4185
- const commandIndexRef = React10__default.useRef(-1);
4186
4399
  const [latestCommandIndexBySurface, setLatestCommandIndexBySurface] = React10__default.useState({});
4187
4400
  const [dismissedAtBySurface, setDismissedAtBySurface] = React10__default.useState({});
4188
4401
  React10__default.useEffect(() => {
4189
- processedMessageCursorsRef.current = [];
4190
- latestCommandIndexBySurfaceRef.current = {};
4191
- commandIndexRef.current = -1;
4192
4402
  setLatestCommandIndexBySurface({});
4193
4403
  setDismissedAtBySurface({});
4194
4404
  }, [conversationId]);
4195
4405
  React10__default.useEffect(() => {
4196
- const { shouldReplayAll, nextCursors, pendingItems } = diffMessages(messages, processedMessageCursorsRef.current);
4197
- const itemsToProcess = shouldReplayAll ? messages.flatMap((message2) => {
4198
- const items = Array.isArray(message2?.content) ? message2.content : [];
4199
- return items.map((item) => ({ item }));
4200
- }) : pendingItems;
4201
- if (!shouldReplayAll && itemsToProcess.length === 0) return;
4202
- const nextLatestCommandIndexBySurface = shouldReplayAll ? {} : { ...latestCommandIndexBySurfaceRef.current };
4203
- let nextCommandIndex = shouldReplayAll ? -1 : commandIndexRef.current;
4204
- itemsToProcess.forEach(({ item }) => {
4205
- if (item?.type !== "a2uiCommand" || !item.command) return;
4206
- nextCommandIndex += 1;
4207
- const surfaceId = extractSurfaceId3(item.command);
4208
- if (surfaceId) nextLatestCommandIndexBySurface[surfaceId] = nextCommandIndex;
4209
- });
4210
- processedMessageCursorsRef.current = shouldReplayAll ? messages.map((message2, messageIndex) => ({
4211
- messageId: String(message2?.messageId ?? messageIndex),
4212
- contentLength: Array.isArray(message2?.content) ? message2.content.length : 0
4213
- })) : nextCursors;
4214
- latestCommandIndexBySurfaceRef.current = nextLatestCommandIndexBySurface;
4215
- commandIndexRef.current = nextCommandIndex;
4216
- setLatestCommandIndexBySurface(nextLatestCommandIndexBySurface);
4406
+ setLatestCommandIndexBySurface(buildLatestCommandIndexBySurface(messages));
4217
4407
  }, [messages]);
4218
4408
  const dismissedSurfaceIds = React10__default.useMemo(() => {
4219
4409
  return Object.entries(dismissedAtBySurface).filter(([surfaceId, dismissedAt]) => (latestCommandIndexBySurface[surfaceId] ?? -1) <= dismissedAt).map(([surfaceId]) => surfaceId);
@@ -4270,11 +4460,13 @@ function A2uiRenderer(props) {
4270
4460
  var FilesNode = React10__default.memo(({ item }) => {
4271
4461
  const chatStore = useChatStore();
4272
4462
  const configState = useSnapshot(chatStore.config);
4273
- useSnapshot(chatStore.agent);
4274
4463
  const conversationState = useSnapshot(chatStore.conversation);
4275
4464
  const fileItems = useMemo(() => {
4276
4465
  return (item.files || []).map((file) => {
4277
- const previewUrl = configState.services.request.getPreviewUrl(conversationState.active.id, file.path);
4466
+ const previewUrl = configState.services.request.getPreviewUrl({
4467
+ path: file.path,
4468
+ workspaceId: conversationState.active.id
4469
+ });
4278
4470
  let fileType = "file";
4279
4471
  const ext = file.ext?.toLowerCase() || "";
4280
4472
  if (["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "webp"].includes(ext)) {
@@ -4340,14 +4532,37 @@ function QuestionSummary({ content }) {
4340
4532
  ] });
4341
4533
  }
4342
4534
  var TextNode2 = React10__default.memo(
4343
- ({ item, role, customComponents, message: message2 }) => {
4344
- const content = toA2uiDisplayText(role, item?.messageContent);
4345
- if (typeof content === "string" && content.startsWith("Question:")) {
4346
- return /* @__PURE__ */ jsx(QuestionSummary, { content });
4535
+ ({
4536
+ item,
4537
+ role,
4538
+ customComponents,
4539
+ message: message2,
4540
+ messageIndex = -1
4541
+ }) => {
4542
+ const { surfaceIdsByMessageIndex } = useA2uiRuntimeView();
4543
+ const rawContent = toA2uiDisplayText(role, item?.messageContent);
4544
+ const segments = splitA2uiTextSegments(rawContent);
4545
+ const visibleSurfaceIds = messageIndex >= 0 ? new Set(surfaceIdsByMessageIndex[messageIndex] || []) : null;
4546
+ const firstSeg = segments[0];
4547
+ const isPureText = segments.length === 0 || segments.length === 1 && firstSeg.type === "text";
4548
+ if (isPureText) {
4549
+ const content = firstSeg && firstSeg.type === "text" ? firstSeg.content : "";
4550
+ if (typeof content === "string" && content.startsWith("Question:")) {
4551
+ return /* @__PURE__ */ jsx(QuestionSummary, { content });
4552
+ }
4553
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4554
+ !!item.reasoningContent && /* @__PURE__ */ jsx(Think, { title: "\u6DF1\u5EA6\u601D\u8003", children: item.reasoningContent }),
4555
+ !!content && /* @__PURE__ */ jsx(XMarkdown_default, { message: message2, components: customComponents, content })
4556
+ ] });
4347
4557
  }
4348
4558
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4349
4559
  !!item.reasoningContent && /* @__PURE__ */ jsx(Think, { title: "\u6DF1\u5EA6\u601D\u8003", children: item.reasoningContent }),
4350
- !!content && /* @__PURE__ */ jsx(XMarkdown_default, { message: message2, components: customComponents, content })
4560
+ segments.map((seg, i) => {
4561
+ if (seg.type === "text") {
4562
+ return seg.content.trim() ? /* @__PURE__ */ jsx(XMarkdown_default, { message: message2, components: customComponents, content: seg.content }, `text-${i}`) : null;
4563
+ }
4564
+ return seg.surfaceIds.filter((sid) => !visibleSurfaceIds || visibleSurfaceIds.has(sid)).map((sid) => /* @__PURE__ */ jsx(XCard.Card, { id: sid }, `a2ui-${i}-${sid}`));
4565
+ })
4351
4566
  ] });
4352
4567
  }
4353
4568
  );
@@ -4575,6 +4790,12 @@ var MessageRender_default = React10__default.memo(({ role, message: message2, me
4575
4790
  if (item.type === "plan") {
4576
4791
  return !shouldHideA2uiPlan(item);
4577
4792
  }
4793
+ if (item.type === "text") {
4794
+ const rawText = item.messageContent || item.text || "";
4795
+ const visibleText = stripA2uiTags(rawText);
4796
+ const hasA2uiTag2 = typeof rawText === "string" && rawText.includes("<A2UI");
4797
+ return !!visibleText.trim() || !!item.reasoningContent || hasA2uiTag2;
4798
+ }
4578
4799
  return true;
4579
4800
  });
4580
4801
  }, [loading, message2.content]);
@@ -4603,7 +4824,7 @@ var MessageRender_default = React10__default.memo(({ role, message: message2, me
4603
4824
  content: /* @__PURE__ */ jsxs(Fragment, { children: [
4604
4825
  item.type === "runStarted" && /* @__PURE__ */ jsx(RunStartedNode, { loading }, `flow-start-${index}`),
4605
4826
  (item.type === "toolCall" || item.type === "toolResult") && /* @__PURE__ */ jsx(tool_call_item_default, { item }, `tool-call-${item.toolCallId || index}`),
4606
- item.type === "text" && /* @__PURE__ */ jsx(TextNode2, { item, role: message2.role, customComponents, message: message2 }, `message-${index}`),
4827
+ item.type === "text" && /* @__PURE__ */ jsx(TextNode2, { item, role: message2.role, customComponents, message: message2, messageIndex }, `message-${index}`),
4607
4828
  item.type === "stepError" && (item.errorMessage ? /* @__PURE__ */ jsx(XMarkdown_default, { message: message2, components: customComponents, content: item.errorMessage }) : null),
4608
4829
  item.type === "files" && /* @__PURE__ */ jsx(FilesNode, { item }, `files-${index}`),
4609
4830
  item.type === "plan" && /* @__PURE__ */ jsx(A2uiPlanNode, { item }, `plan-${index}`)
@@ -4613,7 +4834,7 @@ var MessageRender_default = React10__default.memo(({ role, message: message2, me
4613
4834
  );
4614
4835
  }),
4615
4836
  quoteMsg && /* @__PURE__ */ jsx(QuoteMsgNode, { quoteMsg, role }),
4616
- messageIndex >= 0 ? /* @__PURE__ */ jsx(A2uiMessageCards, { messageIndex }) : null
4837
+ messageIndex >= 0 ? /* @__PURE__ */ jsx(A2uiMessageCards, { messageIndex, message: message2 }) : null
4617
4838
  ] });
4618
4839
  });
4619
4840
  var WelcomeItem_default = ({ icon = true, title = true, description = true, prompts = true }) => {
@@ -5555,6 +5776,6 @@ var layouts_default = forwardRef(({ theme: theme4, params, hooks, layout, config
5555
5776
  ] }) }) }) });
5556
5777
  });
5557
5778
 
5558
- export { Attachments_default as Attachments, layouts_default as ChatCopilot, ChatSender_default as ChatSender };
5779
+ export { Attachments_default as Attachments, layouts_default as ChatCopilot, ChatSender_default as ChatSender, getFileIcon, getFileUrl };
5559
5780
  //# sourceMappingURL=index.esm.js.map
5560
5781
  //# sourceMappingURL=index.esm.js.map