one-design-next 0.0.123 → 0.0.124

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.
@@ -1,14 +1,15 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
3
- import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
4
3
  import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime";
5
4
  import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
5
+ import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
6
6
  import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
7
7
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
8
8
  import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
9
9
  import { SKILL_CATEGORY_ICON } from "../_genui-inline-ref";
10
10
  import useControlledState from "../_util/useControlledState";
11
11
  import Attachments from "../attachments";
12
+ import Dropdown from "../dropdown";
12
13
  import HoverFill from "../hover-fill";
13
14
  import Icon from "../icon";
14
15
  import { InvocationParamPopover } from "../invocation/param-popover";
@@ -20,9 +21,11 @@ import { COMPOSER_EDITOR_HEIGHT_CAP_EXIT_PX, COMPOSER_EDITOR_MAX_ROWS_PX, Compos
20
21
  import { chipHasEditableParams, isMentionChip } from "./inline-ref";
21
22
  import { buildInsertPayload, createSkillChipData } from "./insert";
22
23
  import { resolveComposerLiteExpanded } from "./lite-expand";
24
+ import { createMentionChipData } from "./mention";
25
+ import { MentionMenu } from "./mention-menu";
23
26
  import { rawValueToSegments, segmentsHasContent } from "./segments";
24
27
  import "./style";
25
- import { COMPOSER_SKILL_MENU_DENSITY_CAP_PX, filterSkillsByQuery, resolveComposerSkillMenuMaxHeight, resolveComposerSkillMenuWidth, shouldOpenSlashTriggerMenu } from "./utils";
28
+ import { COMPOSER_SKILL_MENU_DENSITY_CAP_PX, filterMentionsByQuery, filterSkillsByQuery, resolveComposerSkillMenuMaxHeight, resolveComposerSkillMenuWidth, shouldOpenMentionTriggerMenu, shouldOpenSlashTriggerMenu } from "./utils";
26
29
  var TOOL_BTN_ACTIVE = 'color-mix(in srgb, var(--odn-color-brand-6) 10%, transparent)';
27
30
  var DEFAULT_ACCEPT = 'image/*,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.md,.csv,.json,.xml,.html,.css,.js,.ts,.jsx,.tsx,.py,.java,.go,.rs,.c,.cpp,.zip,.rar,.7z';
28
31
  function formatSizeMb(bytes) {
@@ -50,6 +53,8 @@ function isFileAccepted(file, accept) {
50
53
  });
51
54
  }
52
55
 
56
+ /** 快速 / 精选。不是工具按钮,`showMode` 为 true 才出现在发送键左侧。 */
57
+
53
58
  /**
54
59
  * 工具按钮的自定义配置。
55
60
  * - `icon`:图标名称(Icon 组件的 name),字段不传则不渲染图标。
@@ -58,22 +63,29 @@ function isFileAccepted(file, accept) {
58
63
  * - 整个配置对象不传(key 不存在于 `toolConfigs`)时使用内置默认值。
59
64
  * - 仅传 `tooltip`(不传 icon / label)时:保留默认图标与文案,只覆盖说明。
60
65
  * - 传了配置对象且 icon / label 均未设置、也未传 tooltip 时(`{}`),兜底显示工具英文名
61
- * (如 "attachment" / "webSearch" / "skill")作为 label,无图标,
66
+ * (如 "attachment" / "skill")作为 label,无图标,
62
67
  * 方便开发者一眼看出配置遗漏。
63
68
  */
64
69
 
65
- var DEFAULT_TOOLS = ['attachment', 'webSearch', 'skill'];
70
+ var DEFAULT_TOOLS = ['attachment', 'skill'];
71
+ var COMPOSER_MODE_OPTIONS = [{
72
+ value: 'fast',
73
+ title: '快速',
74
+ desc: '快速覆盖,海量达人任你挑选'
75
+ }, {
76
+ value: 'featured',
77
+ title: '精选',
78
+ desc: '深度理解,为你优中选优'
79
+ }];
80
+ function isComposerMode(value) {
81
+ return value === 'fast' || value === 'featured';
82
+ }
66
83
  var DEFAULT_TOOL_CONFIGS = {
67
84
  attachment: {
68
85
  icon: 'paperclip',
69
86
  label: '附件',
70
87
  tooltip: '添加附件,支持直接拖拽或粘贴文件'
71
88
  },
72
- webSearch: {
73
- icon: 'globe',
74
- label: '联网',
75
- tooltip: '联网搜索'
76
- },
77
89
  skill: {
78
90
  icon: SKILL_CATEGORY_ICON,
79
91
  label: '技能',
@@ -115,6 +127,15 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
115
127
  toolsLabeled = _ref$toolsLabeled === void 0 ? true : _ref$toolsLabeled,
116
128
  toolConfigs = _ref.toolConfigs,
117
129
  skillSlotProps = _ref.skillSlotProps,
130
+ _ref$slash = _ref.slash,
131
+ slash = _ref$slash === void 0 ? true : _ref$slash,
132
+ mentionProps = _ref.mentionProps,
133
+ _ref$showMode = _ref.showMode,
134
+ showMode = _ref$showMode === void 0 ? false : _ref$showMode,
135
+ modeProp = _ref.mode,
136
+ _ref$defaultMode = _ref.defaultMode,
137
+ defaultMode = _ref$defaultMode === void 0 ? 'featured' : _ref$defaultMode,
138
+ onModeChange = _ref.onModeChange,
118
139
  _ref$submitType = _ref.submitType,
119
140
  submitType = _ref$submitType === void 0 ? 'enter' : _ref$submitType,
120
141
  onEnter = _ref.onEnter,
@@ -149,81 +170,94 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
149
170
  _useControlledState4 = _slicedToArray(_useControlledState3, 2),
150
171
  attachments = _useControlledState4[0],
151
172
  setAttachments = _useControlledState4[1];
173
+ var _useControlledState5 = useControlledState(defaultMode, modeProp),
174
+ _useControlledState6 = _slicedToArray(_useControlledState5, 2),
175
+ mode = _useControlledState6[0],
176
+ setMode = _useControlledState6[1];
152
177
  var _useState = useState(false),
153
178
  _useState2 = _slicedToArray(_useState, 2),
154
- webSearch = _useState2[0],
155
- setWebSearch = _useState2[1];
179
+ modeOpen = _useState2[0],
180
+ setModeOpen = _useState2[1];
181
+ /** 面板里勾的位置。打开前同步;点选后留到下次打开再换,避免关闭动画里跳行。 */
182
+ var _useState3 = useState(mode),
183
+ _useState4 = _slicedToArray(_useState3, 2),
184
+ panelMode = _useState4[0],
185
+ setPanelMode = _useState4[1];
156
186
  /**
157
187
  * 工具栏「技能」按钮触发的 menu 显隐。
158
188
  * 跟 trigger 触发的 menu 共用 SkillSlot 渲染,但 anchor / 关闭路径不同。
159
189
  */
160
- var _useState3 = useState(false),
161
- _useState4 = _slicedToArray(_useState3, 2),
162
- toolMenuOpen = _useState4[0],
163
- setToolMenuOpen = _useState4[1];
190
+ var _useState5 = useState(false),
191
+ _useState6 = _slicedToArray(_useState5, 2),
192
+ toolMenuOpen = _useState6[0],
193
+ setToolMenuOpen = _useState6[1];
164
194
  /**
165
195
  * editor 内 `@` / `/` 触发态的实时信号。null 表示无活动 trigger。
166
196
  * 由 editor 的 onTriggerChange 写入;选定 chip 或 cancelTrigger 后置 null。
167
197
  */
168
- var _useState5 = useState(null),
169
- _useState6 = _slicedToArray(_useState5, 2),
170
- triggerInfo = _useState6[0],
171
- setTriggerInfo = _useState6[1];
172
- /** trigger 浮层内键盘高亮项 index;query 变化或 menu 关闭时复位 0。 */
173
- var _useState7 = useState(0),
198
+ var _useState7 = useState(null),
174
199
  _useState8 = _slicedToArray(_useState7, 2),
175
- triggerActiveIndex = _useState8[0],
176
- setTriggerActiveIndex = _useState8[1];
200
+ triggerInfo = _useState8[0],
201
+ setTriggerInfo = _useState8[1];
202
+ /** trigger 浮层内键盘高亮项 index;query 变化或 menu 关闭时复位 0。 */
203
+ var _useState9 = useState(0),
204
+ _useState10 = _slicedToArray(_useState9, 2),
205
+ triggerActiveIndex = _useState10[0],
206
+ setTriggerActiveIndex = _useState10[1];
207
+ var _useState11 = useState(0),
208
+ _useState12 = _slicedToArray(_useState11, 2),
209
+ mentionActiveIndex = _useState12[0],
210
+ setMentionActiveIndex = _useState12[1];
177
211
  /** trigger menu 的命令式句柄:导航语义在 SkillSlot 内,composer 只转发 ↑↓。 */
178
212
  var triggerSkillSlotRef = useRef(null);
179
213
  /** lite 模式下由内容高度驱动的单行 / 多行布局状态。 */
180
- var _useState9 = useState(false),
181
- _useState10 = _slicedToArray(_useState9, 2),
182
- expanded = _useState10[0],
183
- setExpanded = _useState10[1];
214
+ var _useState13 = useState(false),
215
+ _useState14 = _slicedToArray(_useState13, 2),
216
+ expanded = _useState14[0],
217
+ setExpanded = _useState14[1];
184
218
  /** 右上角按钮:拉到当前生效上限(10 行盒高,矮屏被整框 50vh 截住)。 */
185
- var _useState11 = useState(false),
186
- _useState12 = _slicedToArray(_useState11, 2),
187
- fullExpanded = _useState12[0],
188
- setFullExpanded = _useState12[1];
219
+ var _useState15 = useState(false),
220
+ _useState16 = _slicedToArray(_useState15, 2),
221
+ fullExpanded = _useState16[0],
222
+ setFullExpanded = _useState16[1];
189
223
  /** ComposerEditor 反馈的 editor 自然高度(未 clamp),用于 10 行上限判定 */
190
- var _useState13 = useState(0),
191
- _useState14 = _slicedToArray(_useState13, 2),
192
- editorHeight = _useState14[0],
193
- setEditorHeight = _useState14[1];
224
+ var _useState17 = useState(0),
225
+ _useState18 = _slicedToArray(_useState17, 2),
226
+ editorHeight = _useState18[0],
227
+ setEditorHeight = _useState18[1];
194
228
  /** ComposerEditor 反馈的内容行数(含软换行),lite 升降格的主判据 */
195
- var _useState15 = useState(0),
196
- _useState16 = _slicedToArray(_useState15, 2),
197
- editorLineCount = _useState16[0],
198
- setEditorLineCount = _useState16[1];
229
+ var _useState19 = useState(0),
230
+ _useState20 = _slicedToArray(_useState19, 2),
231
+ editorLineCount = _useState20[0],
232
+ setEditorLineCount = _useState20[1];
199
233
  /** 内容能否放进单行态宽度;false = 处于「两种形态都合理」的中间区 */
200
- var _useState17 = useState(true),
201
- _useState18 = _slicedToArray(_useState17, 2),
202
- editorFitsInlineWidth = _useState18[0],
203
- setEditorFitsInlineWidth = _useState18[1];
234
+ var _useState21 = useState(true),
235
+ _useState22 = _slicedToArray(_useState21, 2),
236
+ editorFitsInlineWidth = _useState22[0],
237
+ setEditorFitsInlineWidth = _useState22[1];
204
238
  /** 内容已超过 10 行,或整框 50vh 把编辑器挤到放不下当前内容 */
205
- var _useState19 = useState(false),
206
- _useState20 = _slicedToArray(_useState19, 2),
207
- atMax = _useState20[0],
208
- setAtMax = _useState20[1];
239
+ var _useState23 = useState(false),
240
+ _useState24 = _slicedToArray(_useState23, 2),
241
+ atMax = _useState24[0],
242
+ setAtMax = _useState24[1];
209
243
  var fileInputRef = useRef(null);
210
244
  var editorRef = useRef(null);
211
245
  var boxRef = useRef(null);
212
246
  var skillTriggerRef = useRef(null);
213
247
  var triggerAnchorRef = useRef(null);
214
- var _useState21 = useState(0),
215
- _useState22 = _slicedToArray(_useState21, 2),
216
- skillMenuCrossAxis = _useState22[0],
217
- setSkillMenuCrossAxis = _useState22[1];
218
- /** composer box 实宽;技能菜单比它略窄。 */
219
- var _useState23 = useState(0),
220
- _useState24 = _slicedToArray(_useState23, 2),
221
- boxWidth = _useState24[0],
222
- setBoxWidth = _useState24[1];
223
- var _useState25 = useState(COMPOSER_SKILL_MENU_DENSITY_CAP_PX),
248
+ var _useState25 = useState(0),
224
249
  _useState26 = _slicedToArray(_useState25, 2),
225
- skillMenuMaxHeight = _useState26[0],
226
- setSkillMenuMaxHeight = _useState26[1];
250
+ skillMenuCrossAxis = _useState26[0],
251
+ setSkillMenuCrossAxis = _useState26[1];
252
+ /** composer box 实宽;技能菜单比它略窄。 */
253
+ var _useState27 = useState(0),
254
+ _useState28 = _slicedToArray(_useState27, 2),
255
+ boxWidth = _useState28[0],
256
+ setBoxWidth = _useState28[1];
257
+ var _useState29 = useState(COMPOSER_SKILL_MENU_DENSITY_CAP_PX),
258
+ _useState30 = _slicedToArray(_useState29, 2),
259
+ skillMenuMaxHeight = _useState30[0],
260
+ setSkillMenuMaxHeight = _useState30[1];
227
261
  useLayoutEffect(function () {
228
262
  var el = boxRef.current;
229
263
  if (!el) return undefined;
@@ -257,10 +291,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
257
291
  preventScroll: true
258
292
  });
259
293
  }, [disabled]);
260
- var _useState27 = useState(null),
261
- _useState28 = _slicedToArray(_useState27, 2),
262
- paramEdit = _useState28[0],
263
- setParamEdit = _useState28[1];
294
+ var _useState31 = useState(null),
295
+ _useState32 = _slicedToArray(_useState31, 2),
296
+ paramEdit = _useState32[0],
297
+ setParamEdit = _useState32[1];
264
298
  var openParamPanel = useCallback(function (chipId) {
265
299
  if (!renderParamPanel) return;
266
300
  requestAnimationFrame(function () {
@@ -374,11 +408,41 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
374
408
  }
375
409
  };
376
410
  });
411
+ var mentionChipsRef = useRef([]);
412
+ var onMentionRemove = mentionProps === null || mentionProps === void 0 ? void 0 : mentionProps.onRemove;
377
413
  var updateText = useCallback(function (v) {
378
414
  var _editorRef$current$ge2, _editorRef$current12;
379
415
  setText(v);
380
- onChangeProp === null || onChangeProp === void 0 || onChangeProp(v, (_editorRef$current$ge2 = (_editorRef$current12 = editorRef.current) === null || _editorRef$current12 === void 0 ? void 0 : _editorRef$current12.getChips()) !== null && _editorRef$current$ge2 !== void 0 ? _editorRef$current$ge2 : []);
381
- }, [setText, onChangeProp]);
416
+ var chips = (_editorRef$current$ge2 = (_editorRef$current12 = editorRef.current) === null || _editorRef$current12 === void 0 ? void 0 : _editorRef$current12.getChips()) !== null && _editorRef$current$ge2 !== void 0 ? _editorRef$current$ge2 : [];
417
+ onChangeProp === null || onChangeProp === void 0 || onChangeProp(v, chips);
418
+ var nextMentions = chips.filter(function (chip) {
419
+ return chip.kind === 'mention';
420
+ });
421
+ if (onMentionRemove) {
422
+ var nextIds = new Set(nextMentions.map(function (chip) {
423
+ return chip.id;
424
+ }));
425
+ var _iterator = _createForOfIteratorHelper(mentionChipsRef.current),
426
+ _step;
427
+ try {
428
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
429
+ var chip = _step.value;
430
+ if (!nextIds.has(chip.id)) {
431
+ onMentionRemove({
432
+ id: chip.id,
433
+ refId: chip.refId,
434
+ label: chip.label
435
+ });
436
+ }
437
+ }
438
+ } catch (err) {
439
+ _iterator.e(err);
440
+ } finally {
441
+ _iterator.f();
442
+ }
443
+ }
444
+ mentionChipsRef.current = nextMentions;
445
+ }, [setText, onChangeProp, onMentionRemove]);
382
446
 
383
447
  /* ---- Attachments controlled writer ----
384
448
  * useControlledState 只做"value vs innerValue"合流;onAttachmentsChange 需要手动派发。
@@ -438,10 +502,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
438
502
  * 窗口结束后恢复正常过渡。 */
439
503
  var previousExpandedRef = useRef(expanded);
440
504
  var layoutSwitchTimerRef = useRef(null);
441
- var _useState29 = useState(false),
442
- _useState30 = _slicedToArray(_useState29, 2),
443
- layoutSwitching = _useState30[0],
444
- setLayoutSwitching = _useState30[1];
505
+ var _useState33 = useState(false),
506
+ _useState34 = _slicedToArray(_useState33, 2),
507
+ layoutSwitching = _useState34[0],
508
+ setLayoutSwitching = _useState34[1];
445
509
  useLayoutEffect(function () {
446
510
  if (previousExpandedRef.current === expanded) return;
447
511
  previousExpandedRef.current = expanded;
@@ -488,10 +552,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
488
552
  * 用滞回而不是直接比大小:顶满后内容区变宽,同一段文本可能从 10 行重排
489
553
  * 成 9 行。若退出条件也看 10 行,就会「顶满 → 变宽 → 9 行 → 退出 →
490
554
  * 变窄 → 10 行 → 顶满」来回。进入看 10 行,退出要回落到 9 行以下。 */
491
- var _useState31 = useState(false),
492
- _useState32 = _slicedToArray(_useState31, 2),
493
- heightCapReached = _useState32[0],
494
- setHeightCapReached = _useState32[1];
555
+ var _useState35 = useState(false),
556
+ _useState36 = _slicedToArray(_useState35, 2),
557
+ heightCapReached = _useState36[0],
558
+ setHeightCapReached = _useState36[1];
495
559
  useLayoutEffect(function () {
496
560
  if (!editorHeight) return;
497
561
  setHeightCapReached(function (prev) {
@@ -513,7 +577,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
513
577
  * 内部 text 是 raw value(chip 序列化为零宽 marker)。
514
578
  * 对外只有一份契约:`onSend(segments, meta?)`
515
579
  * - segments:有序 IR(text / invocation / mention 交替)
516
- * - meta:attachments / webSearch 等非正文数据;都为空时不传
580
+ * - meta:attachments 等非正文数据;为空时不传
517
581
  *
518
582
  * 派生 hint:最后一个 chip 是 skill、其后没有内容、且它的 hint 还没关闭时,
519
583
  * `rawValueToSegments` 会在末尾追加一段 `hint`(与该 chip 后面渲染的灰字
@@ -534,8 +598,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
534
598
  * 立即回写,等于组件强行清空 value,违反受控合约。
535
599
  * - 受控 attachments:useControlledState 的 setter 写不进 mergedValue;
536
600
  * 也不能代抛 onAttachmentsChange([])——同样会被业务方常见的
537
- * `onAttachmentsChange={setAttachments}` 直接回写。
538
- * - webSearch 无受控接口,直接清。 */
601
+ * `onAttachmentsChange={setAttachments}` 直接回写。 */
539
602
  var handleSend = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
540
603
  var _editorRef$current$ge3, _editorRef$current13;
541
604
  var chips, segments, hasAttachments, meta, hasMeta, result, _editorRef$current14;
@@ -568,8 +631,8 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
568
631
  case 9:
569
632
  meta = {};
570
633
  if (hasAttachments) meta.attachments = attachments;
571
- if (webSearch) meta.webSearch = true;
572
- hasMeta = hasAttachments || webSearch;
634
+ if (showMode) meta.mode = mode;
635
+ hasMeta = hasAttachments || showMode;
573
636
  _context.next = 15;
574
637
  return onSend(segments, hasMeta ? meta : undefined);
575
638
  case 15:
@@ -587,13 +650,12 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
587
650
  if (attachmentsProp === undefined) {
588
651
  setAttachments([]);
589
652
  }
590
- setWebSearch(false);
591
- case 22:
653
+ case 21:
592
654
  case "end":
593
655
  return _context.stop();
594
656
  }
595
657
  }, _callee);
596
- })), [text, attachments, webSearch, onSend, disabled, closeParamPanel, valueProp, attachmentsProp, setAttachments]);
658
+ })), [text, attachments, mode, showMode, onSend, disabled, closeParamPanel, valueProp, attachmentsProp, setAttachments]);
597
659
 
598
660
  /* ---- File upload ----
599
661
  * 三入口(按钮 / 拖入 / 粘贴)共用此函数。先做内置校验(数量、单文件体积、重名),
@@ -619,11 +681,11 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
619
681
  var unsupportedExt = '';
620
682
  var oversizedName = '';
621
683
  var duplicated = 0;
622
- var _iterator = _createForOfIteratorHelper(fileArray),
623
- _step;
684
+ var _iterator2 = _createForOfIteratorHelper(fileArray),
685
+ _step2;
624
686
  try {
625
687
  var _loop = function _loop() {
626
- var f = _step.value;
688
+ var f = _step2.value;
627
689
  if (!isFileAccepted(f, accept)) {
628
690
  unsupportedExt = getFileExt(f.name) || f.type || '该类型';
629
691
  return 0; // continue
@@ -643,14 +705,14 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
643
705
  acceptedFiles.push(f);
644
706
  },
645
707
  _ret;
646
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
708
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
647
709
  _ret = _loop();
648
710
  if (_ret === 0) continue;
649
711
  }
650
712
  } catch (err) {
651
- _iterator.e(err);
713
+ _iterator2.e(err);
652
714
  } finally {
653
- _iterator.f();
715
+ _iterator2.f();
654
716
  }
655
717
  if (unsupportedExt) {
656
718
  var _ext = unsupportedExt.replace(/^\./, '').toUpperCase();
@@ -702,10 +764,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
702
764
  * 不夺回焦点时 Enter 会再次激活按钮而不是发送 */
703
765
  (_editorRef$current15 = editorRef.current) === null || _editorRef$current15 === void 0 || _editorRef$current15.focus();
704
766
  };
705
- var _useState33 = useState(false),
706
- _useState34 = _slicedToArray(_useState33, 2),
707
- fileDragOver = _useState34[0],
708
- setFileDragOver = _useState34[1];
767
+ var _useState37 = useState(false),
768
+ _useState38 = _slicedToArray(_useState37, 2),
769
+ fileDragOver = _useState38[0],
770
+ setFileDragOver = _useState38[1];
709
771
  var composerBoxFileDragAttrs = fileDragOver ? {
710
772
  'data-odn-composer-file-drag': ''
711
773
  } : {};
@@ -753,40 +815,101 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
753
815
  }
754
816
  if (skill.initialState === 'pending') openParamPanel(data.id);
755
817
  }, [openParamPanel]);
818
+ var handleSelectMention = useCallback(function (item) {
819
+ var _mentionProps$onSelec, _editorRef$current19;
820
+ if ((mentionProps === null || mentionProps === void 0 || (_mentionProps$onSelec = mentionProps.onSelect) === null || _mentionProps$onSelec === void 0 ? void 0 : _mentionProps$onSelec.call(mentionProps, item)) === false) return;
821
+ (_editorRef$current19 = editorRef.current) === null || _editorRef$current19 === void 0 || _editorRef$current19.replaceTriggerWithChip(createMentionChipData(item));
822
+ setTriggerInfo(null);
823
+ }, [mentionProps]);
756
824
 
757
825
  /* trigger query 实时筛选 skills(substring match,case-insensitive)。
758
826
  * trigger 已结束时返回空列表,避免 Popover 关闭动画那一帧把子节点切回全量目录。
759
827
  * 依赖 query / trigger 而不是整个 triggerInfo:rect 变化不应重建列表。 */
760
828
  var slashTriggerActive = (triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger) === '/';
829
+ var atTriggerActive = (triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger) === '@';
761
830
  var slashTriggerQuery = slashTriggerActive ? triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.query : undefined;
831
+ var atTriggerQuery = atTriggerActive ? triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.query : undefined;
832
+ var mentionItems = mentionProps === null || mentionProps === void 0 ? void 0 : mentionProps.items;
833
+ var hasMentions = mentionItems !== undefined;
834
+ var filteredMentions = useMemo(function () {
835
+ if (!mentionItems || !atTriggerActive) return [];
836
+ return filterMentionsByQuery(mentionItems, atTriggerQuery);
837
+ }, [mentionItems, atTriggerActive, atTriggerQuery]);
762
838
  var filteredSkills = useMemo(function () {
763
839
  if (!skills) return undefined;
764
840
  if (!slashTriggerActive) return [];
765
841
  return filterSkillsByQuery(skills, slashTriggerQuery);
766
842
  }, [skills, slashTriggerActive, slashTriggerQuery]);
767
843
 
768
- /* trigger 浮层:`/` 触发态 + 目录非空 + 过滤后仍有命中。
769
- * 无匹配时菜单关闭,Enter 不再被当成「选 skill」。`@` 本轮不接。 */
844
+ /* `/` 无匹配时关闭,Enter 不再被当成选技能。
845
+ * `@` 只要接了引用名单就打开,没有命中也留「暂无文件」。 */
770
846
  var hasSkills = !!(skills && skills.length > 0);
771
847
  var triggerHitCount = (_filteredSkills$lengt = filteredSkills === null || filteredSkills === void 0 ? void 0 : filteredSkills.length) !== null && _filteredSkills$lengt !== void 0 ? _filteredSkills$lengt : 0;
772
848
  var triggerMenuVisible = shouldOpenSlashTriggerMenu(triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger, hasSkills ? triggerHitCount : 0);
849
+ var mentionMenuVisible = shouldOpenMentionTriggerMenu(triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger, hasMentions);
773
850
 
774
851
  /* query 变化或浮层关闭 → 高亮项重置到「最上方的可用项」(跳过 disabled)。
775
852
  * 用 query 而不是 triggerInfo 整体当依赖:rect 变化(光标移位)不应该 reset。 */
776
853
  useEffect(function () {
777
854
  var _ref3;
855
+ if ((triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger) === '@') {
856
+ setMentionActiveIndex(0);
857
+ return;
858
+ }
778
859
  var list = (_ref3 = filteredSkills !== null && filteredSkills !== void 0 ? filteredSkills : skills) !== null && _ref3 !== void 0 ? _ref3 : [];
779
860
  var first = list.findIndex(function (s) {
780
861
  return !s.disabled;
781
862
  });
782
863
  setTriggerActiveIndex(first === -1 ? 0 : first);
783
- }, [filteredSkills, skills, triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.query, triggerInfo === null]);
864
+ }, [filteredMentions, filteredSkills, skills, triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.query, triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger, triggerInfo === null]);
784
865
 
785
866
  /* editor 在 trigger 激活时把 ↑↓Enter Esc Tab 丢过来。返回 true = editor 别管了。
786
867
  * - ↑↓ 改高亮 index(夹在 [0, max] 内)
787
868
  * - Enter / Tab 选中当前高亮项(Tab 默认落在第一个可用技能上)
788
869
  * - Escape 关 menu,焦点保留在 editor */
789
870
  var handleTriggerKeyDown = useCallback(function (e) {
871
+ if (mentionMenuVisible) {
872
+ var max = filteredMentions.length - 1;
873
+ if (e.key === 'ArrowDown') {
874
+ e.preventDefault();
875
+ if (max >= 0) {
876
+ setMentionActiveIndex(function (index) {
877
+ return Math.min(max, index + 1);
878
+ });
879
+ }
880
+ return true;
881
+ }
882
+ if (e.key === 'ArrowUp') {
883
+ e.preventDefault();
884
+ if (max >= 0) {
885
+ setMentionActiveIndex(function (index) {
886
+ return Math.max(0, index - 1);
887
+ });
888
+ }
889
+ return true;
890
+ }
891
+ if (e.key === 'Enter' && !e.shiftKey) {
892
+ e.preventDefault();
893
+ var _item = filteredMentions[mentionActiveIndex];
894
+ if (_item) handleSelectMention(_item);
895
+ return true;
896
+ }
897
+ if (e.key === 'Tab') {
898
+ var _item2 = filteredMentions[mentionActiveIndex];
899
+ if (!_item2) return false;
900
+ e.preventDefault();
901
+ handleSelectMention(_item2);
902
+ return true;
903
+ }
904
+ if (e.key === 'Escape') {
905
+ var _editorRef$current20;
906
+ e.preventDefault();
907
+ (_editorRef$current20 = editorRef.current) === null || _editorRef$current20 === void 0 || _editorRef$current20.cancelTrigger();
908
+ setTriggerInfo(null);
909
+ return true;
910
+ }
911
+ return false;
912
+ }
790
913
  if (!triggerMenuVisible) return false;
791
914
 
792
915
  /* ↑↓ 导航完全委托给 SkillSlot(唯一事实源:跳过 disabled、夹边、滚动进视口),
@@ -826,14 +949,14 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
826
949
  return true;
827
950
  }
828
951
  if (e.key === 'Escape') {
829
- var _editorRef$current19;
952
+ var _editorRef$current21;
830
953
  e.preventDefault();
831
- (_editorRef$current19 = editorRef.current) === null || _editorRef$current19 === void 0 || _editorRef$current19.cancelTrigger();
954
+ (_editorRef$current21 = editorRef.current) === null || _editorRef$current21 === void 0 || _editorRef$current21.cancelTrigger();
832
955
  setTriggerInfo(null);
833
956
  return true;
834
957
  }
835
958
  return false;
836
- }, [filteredSkills, handleSelectSkill, triggerActiveIndex, triggerMenuVisible]);
959
+ }, [filteredMentions, filteredSkills, handleSelectMention, handleSelectSkill, mentionActiveIndex, mentionMenuVisible, triggerActiveIndex, triggerMenuVisible]);
837
960
 
838
961
  /* ---- Derived ---- */
839
962
  var hasUploadingAttachment = attachments.some(function (a) {
@@ -846,7 +969,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
846
969
  var isInlineLite = isLite && !expanded;
847
970
  // lite 模式下强制仅图标(包括升格后)
848
971
  var effectiveLabeled = isLite ? false : toolsLabeled;
849
- // lite 模式下仅允许一个工具按钮(数量在两态下保持恒定)
972
+ // lite 仍只留一个左侧工具。快速 / 精选不在这组里。
850
973
  var effectiveTools = isLite ? tools.slice(0, 1) : tools;
851
974
  useLayoutEffect(function () {
852
975
  var el = triggerAnchorRef.current;
@@ -856,7 +979,9 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
856
979
  el.style.top = "".concat(triggerInfo.rect.top - boxRect.top, "px");
857
980
  }, [triggerInfo]);
858
981
  useLayoutEffect(function () {
859
- if (!toolMenuOpen && !triggerMenuVisible) return undefined;
982
+ if (!toolMenuOpen && !triggerMenuVisible && !mentionMenuVisible) {
983
+ return undefined;
984
+ }
860
985
  var sync = function sync() {
861
986
  var _skillTriggerRef$curr, _skillTriggerRef$curr2, _ref6, _triggerInfo$rect$top, _triggerAnchorRef$cur;
862
987
  var triggerTop = toolMenuOpen ? (_skillTriggerRef$curr = (_skillTriggerRef$curr2 = skillTriggerRef.current) === null || _skillTriggerRef$curr2 === void 0 ? void 0 : _skillTriggerRef$curr2.getBoundingClientRect().top) !== null && _skillTriggerRef$curr !== void 0 ? _skillTriggerRef$curr : null : (_ref6 = (_triggerInfo$rect$top = triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.rect.top) !== null && _triggerInfo$rect$top !== void 0 ? _triggerInfo$rect$top : (_triggerAnchorRef$cur = triggerAnchorRef.current) === null || _triggerAnchorRef$cur === void 0 ? void 0 : _triggerAnchorRef$cur.getBoundingClientRect().top) !== null && _ref6 !== void 0 ? _ref6 : null;
@@ -878,7 +1003,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
878
1003
  window.removeEventListener('resize', sync);
879
1004
  ro === null || ro === void 0 || ro.disconnect();
880
1005
  };
881
- }, [toolMenuOpen, triggerMenuVisible, triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.rect.top]);
1006
+ }, [mentionMenuVisible, toolMenuOpen, triggerMenuVisible, triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.rect.top]);
882
1007
  useLayoutEffect(function () {
883
1008
  var box = boxRef.current;
884
1009
  var trigger = skillTriggerRef.current;
@@ -933,7 +1058,6 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
933
1058
  };
934
1059
  return {
935
1060
  attachment: resolve('attachment'),
936
- webSearch: resolve('webSearch'),
937
1061
  skill: resolve('skill')
938
1062
  };
939
1063
  }();
@@ -945,7 +1069,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
945
1069
  /* ---- Tool buttons ----
946
1070
  * disabled(整体禁用)合并到各按钮的 disabled 状态:
947
1071
  * - attachment:disabled || attachmentsDisabled
948
- * - webSearch / skill:disabled(attachmentsDisabled 不影响这两个)
1072
+ * - skill:disabled(attachmentsDisabled 不影响技能)
949
1073
  * skill 的 Popover 在 disabled 时禁用 trigger,避免弹出菜单。 */
950
1074
  var renderAttachmentBtn = function renderAttachmentBtn() {
951
1075
  var btnDisabled = disabled || attachmentsDisabled;
@@ -990,55 +1114,73 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
990
1114
  size: 16
991
1115
  }), showLabel && /*#__PURE__*/React.createElement("span", null, label))));
992
1116
  };
993
- var renderWebSearchBtn = function renderWebSearchBtn() {
994
- var _resolvedToolConfigs$2 = resolvedToolConfigs.webSearch,
995
- icon = _resolvedToolConfigs$2.icon,
996
- label = _resolvedToolConfigs$2.label,
997
- tooltip = _resolvedToolConfigs$2.tooltip;
998
- var showIcon = !!icon;
999
- var showLabel = effectiveLabeled && !!label;
1000
- return /*#__PURE__*/React.createElement(Tooltip, {
1001
- key: "webSearch",
1002
- popup: tooltip,
1003
- placement: "top"
1004
- }, /*#__PURE__*/React.createElement(HoverFill, {
1005
- "data-odn-composer-tool-btn-wrap": true,
1006
- bgClassName: "odn-composer-tool-btn-bg",
1007
- activeColor: TOOL_BTN_ACTIVE,
1008
- disabled: disabled || webSearch
1117
+ var renderModeBtn = function renderModeBtn() {
1118
+ var _COMPOSER_MODE_OPTION;
1119
+ var current = (_COMPOSER_MODE_OPTION = COMPOSER_MODE_OPTIONS.find(function (item) {
1120
+ return item.value === mode;
1121
+ })) !== null && _COMPOSER_MODE_OPTION !== void 0 ? _COMPOSER_MODE_OPTION : COMPOSER_MODE_OPTIONS[1];
1122
+ return /*#__PURE__*/React.createElement(Dropdown, {
1123
+ key: "mode",
1124
+ trigger: "click",
1125
+ placement: "topLeft",
1126
+ disabled: disabled,
1127
+ popupClassName: "odn-composer-mode-menu",
1128
+ itemActiveColor: "var(--odn-color-black-2)",
1129
+ menu: COMPOSER_MODE_OPTIONS.map(function (item) {
1130
+ return {
1131
+ value: item.value,
1132
+ label: /*#__PURE__*/React.createElement("span", {
1133
+ "data-odn-composer-mode-row": true
1134
+ }, /*#__PURE__*/React.createElement("span", {
1135
+ "data-odn-composer-mode-copy": true
1136
+ }, /*#__PURE__*/React.createElement("span", {
1137
+ "data-odn-composer-mode-title": true
1138
+ }, item.title), /*#__PURE__*/React.createElement("span", {
1139
+ "data-odn-composer-mode-desc": true
1140
+ }, item.desc)), /*#__PURE__*/React.createElement(Icon, {
1141
+ name: "check",
1142
+ size: 16,
1143
+ "data-odn-composer-mode-tick": item.value === panelMode ? 'on' : 'off',
1144
+ "aria-hidden": true
1145
+ }))
1146
+ };
1147
+ }),
1148
+ onVisibleChange: function onVisibleChange(open) {
1149
+ if (disabled) return;
1150
+ setModeOpen(open);
1151
+ },
1152
+ onSelect: function onSelect(value) {
1153
+ if (!isComposerMode(value) || value === mode) return;
1154
+ setMode(value);
1155
+ onModeChange === null || onModeChange === void 0 || onModeChange(value);
1156
+ }
1009
1157
  }, /*#__PURE__*/React.createElement("button", {
1010
1158
  type: "button",
1011
1159
  tabIndex: -1,
1012
- onClick: function onClick() {
1013
- onToolClick === null || onToolClick === void 0 || onToolClick('webSearch');
1014
- setWebSearch(function (v) {
1015
- return !v;
1016
- });
1017
- }
1018
- /* 同附件按钮:防止点击后焦点留在按钮上,Enter 误触发 toggle 而不是发送 */,
1160
+ disabled: disabled,
1019
1161
  onMouseDown: function onMouseDown(e) {
1020
- return e.preventDefault();
1162
+ e.preventDefault();
1163
+ if (!modeOpen) setPanelMode(mode);
1021
1164
  },
1022
- disabled: disabled,
1023
- "data-odn-composer-tool-btn": true,
1024
- "data-odn-composer-tool-iconic": !showLabel || undefined,
1025
- "data-odn-composer-tool-active": webSearch || undefined,
1026
- "data-odn-composer-tool-disabled": disabled || undefined,
1027
- "aria-label": label || 'webSearch',
1028
- "aria-pressed": webSearch,
1165
+ "data-odn-composer-mode-trigger": true,
1166
+ "data-odn-composer-mode-open": modeOpen || undefined,
1167
+ "data-odn-composer-mode": mode,
1168
+ "aria-label": "\u63A8\u7406\u6A21\u5F0F\uFF1A".concat(current.title),
1169
+ "aria-expanded": modeOpen,
1029
1170
  "aria-disabled": disabled || undefined
1030
- }, showIcon && /*#__PURE__*/React.createElement(Icon, {
1031
- name: icon,
1032
- size: 16
1033
- }), showLabel && /*#__PURE__*/React.createElement("span", null, label))));
1171
+ }, /*#__PURE__*/React.createElement("span", null, current.title), /*#__PURE__*/React.createElement(Icon, {
1172
+ name: "chevron-down",
1173
+ size: 12,
1174
+ "data-odn-composer-mode-chevron": modeOpen ? 'open' : 'closed'
1175
+ })));
1034
1176
  };
1035
1177
  var renderSkillBtn = function renderSkillBtn() {
1036
1178
  var hasAnySkills = skills && skills.length > 0;
1037
1179
  if (!hasAnySkills) return null;
1038
- var _resolvedToolConfigs$3 = resolvedToolConfigs.skill,
1039
- icon = _resolvedToolConfigs$3.icon,
1040
- label = _resolvedToolConfigs$3.label,
1041
- tooltip = _resolvedToolConfigs$3.tooltip;
1180
+ var _resolvedToolConfigs$2 = resolvedToolConfigs.skill,
1181
+ icon = _resolvedToolConfigs$2.icon,
1182
+ label = _resolvedToolConfigs$2.label,
1183
+ tooltip = _resolvedToolConfigs$2.tooltip;
1042
1184
  var showIcon = !!icon;
1043
1185
  var showLabel = effectiveLabeled && !!label;
1044
1186
  return /*#__PURE__*/React.createElement(Tooltip, {
@@ -1105,7 +1247,6 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1105
1247
  };
1106
1248
  var toolNodes = effectiveTools.map(function (t) {
1107
1249
  if (t === 'attachment') return renderAttachmentBtn();
1108
- if (t === 'webSearch') return renderWebSearchBtn();
1109
1250
  if (t === 'skill') return renderSkillBtn();
1110
1251
  return null;
1111
1252
  }).filter(Boolean);
@@ -1179,6 +1320,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1179
1320
  name: "arrow-up",
1180
1321
  size: 18
1181
1322
  }));
1323
+ var modeButton = showMode ? renderModeBtn() : null;
1324
+ var sendCluster = /*#__PURE__*/React.createElement("div", {
1325
+ "data-odn-composer-toolbar-right": true
1326
+ }, modeButton, sendOrStopButton);
1182
1327
 
1183
1328
  // 工具栏(full 专用,作为 textarea 的 bottomElement)
1184
1329
  var toolbarSlot = /*#__PURE__*/React.createElement("div", {
@@ -1188,7 +1333,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1188
1333
  placement: "top",
1189
1334
  "data-odn-composer-toolbar-left": true,
1190
1335
  visible: toolMenuOpen ? false : undefined
1191
- }, toolNodes), sendOrStopButton);
1336
+ }, toolNodes), sendCluster);
1192
1337
 
1193
1338
  // 文件 input 独立渲染一次,在两种形态下都由同一 ref 控制
1194
1339
  var hiddenFileInput = /*#__PURE__*/React.createElement("input", {
@@ -1199,9 +1344,9 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1199
1344
  "data-odn-composer-file-input": true,
1200
1345
  onChange: handleFileChange
1201
1346
  });
1202
- var paramPanelNode = function (_editorRef$current$ge4, _editorRef$current20, _boxRef$current) {
1347
+ var paramPanelNode = function (_editorRef$current$ge4, _editorRef$current22, _boxRef$current) {
1203
1348
  if (!paramEdit || !renderParamPanel) return null;
1204
- var chips = (_editorRef$current$ge4 = (_editorRef$current20 = editorRef.current) === null || _editorRef$current20 === void 0 ? void 0 : _editorRef$current20.getChips()) !== null && _editorRef$current$ge4 !== void 0 ? _editorRef$current$ge4 : [];
1349
+ var chips = (_editorRef$current$ge4 = (_editorRef$current22 = editorRef.current) === null || _editorRef$current22 === void 0 ? void 0 : _editorRef$current22.getChips()) !== null && _editorRef$current$ge4 !== void 0 ? _editorRef$current$ge4 : [];
1205
1350
  var chip = chips.find(function (c) {
1206
1351
  return c.id === paramEdit.chipId;
1207
1352
  });
@@ -1211,8 +1356,8 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1211
1356
  chip: chip,
1212
1357
  anchor: paramEdit.anchor,
1213
1358
  onApply: function onApply(patch) {
1214
- var _editorRef$current21;
1215
- (_editorRef$current21 = editorRef.current) === null || _editorRef$current21 === void 0 || _editorRef$current21.updateChipData(chip.id, patch);
1359
+ var _editorRef$current23;
1360
+ (_editorRef$current23 = editorRef.current) === null || _editorRef$current23 === void 0 || _editorRef$current23.updateChipData(chip.id, patch);
1216
1361
  },
1217
1362
  onClose: closeParamPanel
1218
1363
  };
@@ -1239,9 +1384,8 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1239
1384
  onSkillHintChange: onSkillHintChange,
1240
1385
  disabled: disabled,
1241
1386
  paramEditChipId: (_paramEdit$chipId = paramEdit === null || paramEdit === void 0 ? void 0 : paramEdit.chipId) !== null && _paramEdit$chipId !== void 0 ? _paramEdit$chipId : null,
1242
- onInlineRefClick: renderParamPanel ? handleInlineRefClick : undefined
1243
- /* 本轮只接 `/`,`@` 留给业务接 mention 搜索时再加 */,
1244
- triggers: ['/'],
1387
+ onInlineRefClick: renderParamPanel ? handleInlineRefClick : undefined,
1388
+ triggers: [].concat(_toConsumableArray(slash ? ['/'] : []), _toConsumableArray(hasMentions ? ['@'] : [])),
1245
1389
  onTriggerChange: setTriggerInfo,
1246
1390
  onTriggerKeyDown: handleTriggerKeyDown,
1247
1391
  onPasteFiles: addFilesFromList,
@@ -1260,7 +1404,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1260
1404
  onPressEnter: function onPressEnter(e) {
1261
1405
  if (e.nativeEvent.isComposing || e.keyCode === 229) return;
1262
1406
  // trigger 浮层有可选中项时,普通 Enter 留给浮层做选择;Shift+Enter 继续换行
1263
- if (triggerMenuVisible && !e.shiftKey) {
1407
+ if ((triggerMenuVisible || mentionMenuVisible) && !e.shiftKey) {
1264
1408
  e.preventDefault();
1265
1409
  return false;
1266
1410
  }
@@ -1276,24 +1420,31 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1276
1420
  }
1277
1421
  });
1278
1422
 
1279
- /* `/` 浮层:anchor 水平铺满 composer box、竖直跟 caret,菜单与输入框水平居中。
1280
- * 共用 SkillSlot 渲染;过滤后无命中时 triggerMenuVisible=false,不挂菜单。 */
1281
- var triggerMenu = hasSkills ? /*#__PURE__*/React.createElement(Popover, {
1423
+ /* `/` 与 `@` 共用锚点。`/` 无命中不挂菜单;`@` 无命中仍显示空态。 */
1424
+ var triggerPopupVisible = triggerMenuVisible || mentionMenuVisible;
1425
+ var triggerMenu = hasSkills || hasMentions ? /*#__PURE__*/React.createElement(Popover, {
1282
1426
  trigger: "click",
1283
1427
  arrowed: false,
1284
1428
  placement: "top",
1285
1429
  shift: false,
1286
1430
  offset: 4,
1287
- visible: triggerMenuVisible,
1431
+ visible: triggerPopupVisible,
1288
1432
  onVisibleChange: function onVisibleChange(v) {
1289
1433
  if (!v) {
1290
- var _editorRef$current22;
1291
- (_editorRef$current22 = editorRef.current) === null || _editorRef$current22 === void 0 || _editorRef$current22.cancelTrigger();
1434
+ var _editorRef$current24;
1435
+ (_editorRef$current24 = editorRef.current) === null || _editorRef$current24 === void 0 || _editorRef$current24.cancelTrigger();
1292
1436
  setTriggerInfo(null);
1293
1437
  }
1294
1438
  },
1295
- popupClassName: "odn-composer-skill-popup",
1296
- popup: /*#__PURE__*/React.createElement(SkillSlot, {
1439
+ popupClassName: mentionMenuVisible ? 'odn-composer-mention-popup' : 'odn-composer-skill-popup',
1440
+ popup: mentionMenuVisible ? /*#__PURE__*/React.createElement(MentionMenu, {
1441
+ items: filteredMentions,
1442
+ activeIndex: mentionActiveIndex,
1443
+ width: resolvedMenuWidth,
1444
+ maxHeight: skillMenuMaxHeight,
1445
+ onActiveIndexChange: setMentionActiveIndex,
1446
+ onSelect: handleSelectMention
1447
+ }) : /*#__PURE__*/React.createElement(SkillSlot, {
1297
1448
  ref: triggerSkillSlotRef,
1298
1449
  variant: "menu",
1299
1450
  skills: filteredSkills !== null && filteredSkills !== void 0 ? filteredSkills : [],
@@ -1334,6 +1485,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1334
1485
  "data-odn-composer-height-cap": atHeightCap ? '' : undefined,
1335
1486
  "data-odn-composer-layout-switching": layoutSwitching ? '' : undefined,
1336
1487
  "data-odn-composer-disabled": disabled || undefined,
1488
+ "data-odn-composer-mode-dock": showMode ? '' : undefined,
1337
1489
  "data-odn-composer-spacing": spacing === 'drawer' ? 'drawer' : undefined,
1338
1490
  "aria-disabled": disabled || undefined,
1339
1491
  className: className !== null && className !== void 0 ? className : spacing === 'drawer' ? 'shrink-0' : undefined,
@@ -1375,7 +1527,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1375
1527
  visible: toolMenuOpen ? false : undefined
1376
1528
  }, toolNodes), isLite && /*#__PURE__*/React.createElement("div", {
1377
1529
  "data-odn-composer-lite-send": true
1378
- }, sendOrStopButton)), hint && /*#__PURE__*/React.createElement("p", {
1530
+ }, sendCluster)), hint && /*#__PURE__*/React.createElement("p", {
1379
1531
  "data-odn-composer-hint": true
1380
1532
  }, submitType === 'enter' ? 'Enter 发送 · Shift+Enter 换行 · 可拖入或粘贴文件' : 'Shift+Enter 发送 · Enter 换行 · 可拖入或粘贴文件')));
1381
1533
  });