one-design-next 0.0.122 → 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: '技能',
@@ -94,6 +106,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
94
106
  defaultValue = _ref$defaultValue === void 0 ? '' : _ref$defaultValue,
95
107
  defaultChips = _ref.defaultChips,
96
108
  onChangeProp = _ref.onChange,
109
+ onSkillHintChange = _ref.onSkillHintChange,
97
110
  onSend = _ref.onSend,
98
111
  onStop = _ref.onStop,
99
112
  _ref$isGenerating = _ref.isGenerating,
@@ -114,6 +127,15 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
114
127
  toolsLabeled = _ref$toolsLabeled === void 0 ? true : _ref$toolsLabeled,
115
128
  toolConfigs = _ref.toolConfigs,
116
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,
117
139
  _ref$submitType = _ref.submitType,
118
140
  submitType = _ref$submitType === void 0 ? 'enter' : _ref$submitType,
119
141
  onEnter = _ref.onEnter,
@@ -148,81 +170,94 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
148
170
  _useControlledState4 = _slicedToArray(_useControlledState3, 2),
149
171
  attachments = _useControlledState4[0],
150
172
  setAttachments = _useControlledState4[1];
173
+ var _useControlledState5 = useControlledState(defaultMode, modeProp),
174
+ _useControlledState6 = _slicedToArray(_useControlledState5, 2),
175
+ mode = _useControlledState6[0],
176
+ setMode = _useControlledState6[1];
151
177
  var _useState = useState(false),
152
178
  _useState2 = _slicedToArray(_useState, 2),
153
- webSearch = _useState2[0],
154
- 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];
155
186
  /**
156
187
  * 工具栏「技能」按钮触发的 menu 显隐。
157
188
  * 跟 trigger 触发的 menu 共用 SkillSlot 渲染,但 anchor / 关闭路径不同。
158
189
  */
159
- var _useState3 = useState(false),
160
- _useState4 = _slicedToArray(_useState3, 2),
161
- toolMenuOpen = _useState4[0],
162
- setToolMenuOpen = _useState4[1];
190
+ var _useState5 = useState(false),
191
+ _useState6 = _slicedToArray(_useState5, 2),
192
+ toolMenuOpen = _useState6[0],
193
+ setToolMenuOpen = _useState6[1];
163
194
  /**
164
195
  * editor 内 `@` / `/` 触发态的实时信号。null 表示无活动 trigger。
165
196
  * 由 editor 的 onTriggerChange 写入;选定 chip 或 cancelTrigger 后置 null。
166
197
  */
167
- var _useState5 = useState(null),
168
- _useState6 = _slicedToArray(_useState5, 2),
169
- triggerInfo = _useState6[0],
170
- setTriggerInfo = _useState6[1];
171
- /** trigger 浮层内键盘高亮项 index;query 变化或 menu 关闭时复位 0。 */
172
- var _useState7 = useState(0),
198
+ var _useState7 = useState(null),
173
199
  _useState8 = _slicedToArray(_useState7, 2),
174
- triggerActiveIndex = _useState8[0],
175
- 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];
176
211
  /** trigger menu 的命令式句柄:导航语义在 SkillSlot 内,composer 只转发 ↑↓。 */
177
212
  var triggerSkillSlotRef = useRef(null);
178
213
  /** lite 模式下由内容高度驱动的单行 / 多行布局状态。 */
179
- var _useState9 = useState(false),
180
- _useState10 = _slicedToArray(_useState9, 2),
181
- expanded = _useState10[0],
182
- setExpanded = _useState10[1];
214
+ var _useState13 = useState(false),
215
+ _useState14 = _slicedToArray(_useState13, 2),
216
+ expanded = _useState14[0],
217
+ setExpanded = _useState14[1];
183
218
  /** 右上角按钮:拉到当前生效上限(10 行盒高,矮屏被整框 50vh 截住)。 */
184
- var _useState11 = useState(false),
185
- _useState12 = _slicedToArray(_useState11, 2),
186
- fullExpanded = _useState12[0],
187
- setFullExpanded = _useState12[1];
219
+ var _useState15 = useState(false),
220
+ _useState16 = _slicedToArray(_useState15, 2),
221
+ fullExpanded = _useState16[0],
222
+ setFullExpanded = _useState16[1];
188
223
  /** ComposerEditor 反馈的 editor 自然高度(未 clamp),用于 10 行上限判定 */
189
- var _useState13 = useState(0),
190
- _useState14 = _slicedToArray(_useState13, 2),
191
- editorHeight = _useState14[0],
192
- setEditorHeight = _useState14[1];
224
+ var _useState17 = useState(0),
225
+ _useState18 = _slicedToArray(_useState17, 2),
226
+ editorHeight = _useState18[0],
227
+ setEditorHeight = _useState18[1];
193
228
  /** ComposerEditor 反馈的内容行数(含软换行),lite 升降格的主判据 */
194
- var _useState15 = useState(0),
195
- _useState16 = _slicedToArray(_useState15, 2),
196
- editorLineCount = _useState16[0],
197
- setEditorLineCount = _useState16[1];
229
+ var _useState19 = useState(0),
230
+ _useState20 = _slicedToArray(_useState19, 2),
231
+ editorLineCount = _useState20[0],
232
+ setEditorLineCount = _useState20[1];
198
233
  /** 内容能否放进单行态宽度;false = 处于「两种形态都合理」的中间区 */
199
- var _useState17 = useState(true),
200
- _useState18 = _slicedToArray(_useState17, 2),
201
- editorFitsInlineWidth = _useState18[0],
202
- setEditorFitsInlineWidth = _useState18[1];
234
+ var _useState21 = useState(true),
235
+ _useState22 = _slicedToArray(_useState21, 2),
236
+ editorFitsInlineWidth = _useState22[0],
237
+ setEditorFitsInlineWidth = _useState22[1];
203
238
  /** 内容已超过 10 行,或整框 50vh 把编辑器挤到放不下当前内容 */
204
- var _useState19 = useState(false),
205
- _useState20 = _slicedToArray(_useState19, 2),
206
- atMax = _useState20[0],
207
- setAtMax = _useState20[1];
239
+ var _useState23 = useState(false),
240
+ _useState24 = _slicedToArray(_useState23, 2),
241
+ atMax = _useState24[0],
242
+ setAtMax = _useState24[1];
208
243
  var fileInputRef = useRef(null);
209
244
  var editorRef = useRef(null);
210
245
  var boxRef = useRef(null);
211
246
  var skillTriggerRef = useRef(null);
212
247
  var triggerAnchorRef = useRef(null);
213
- var _useState21 = useState(0),
214
- _useState22 = _slicedToArray(_useState21, 2),
215
- skillMenuCrossAxis = _useState22[0],
216
- setSkillMenuCrossAxis = _useState22[1];
217
- /** composer box 实宽;技能菜单比它略窄。 */
218
- var _useState23 = useState(0),
219
- _useState24 = _slicedToArray(_useState23, 2),
220
- boxWidth = _useState24[0],
221
- setBoxWidth = _useState24[1];
222
- var _useState25 = useState(COMPOSER_SKILL_MENU_DENSITY_CAP_PX),
248
+ var _useState25 = useState(0),
223
249
  _useState26 = _slicedToArray(_useState25, 2),
224
- skillMenuMaxHeight = _useState26[0],
225
- 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];
226
261
  useLayoutEffect(function () {
227
262
  var el = boxRef.current;
228
263
  if (!el) return undefined;
@@ -256,10 +291,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
256
291
  preventScroll: true
257
292
  });
258
293
  }, [disabled]);
259
- var _useState27 = useState(null),
260
- _useState28 = _slicedToArray(_useState27, 2),
261
- paramEdit = _useState28[0],
262
- setParamEdit = _useState28[1];
294
+ var _useState31 = useState(null),
295
+ _useState32 = _slicedToArray(_useState31, 2),
296
+ paramEdit = _useState32[0],
297
+ setParamEdit = _useState32[1];
263
298
  var openParamPanel = useCallback(function (chipId) {
264
299
  if (!renderParamPanel) return;
265
300
  requestAnimationFrame(function () {
@@ -373,11 +408,41 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
373
408
  }
374
409
  };
375
410
  });
411
+ var mentionChipsRef = useRef([]);
412
+ var onMentionRemove = mentionProps === null || mentionProps === void 0 ? void 0 : mentionProps.onRemove;
376
413
  var updateText = useCallback(function (v) {
377
414
  var _editorRef$current$ge2, _editorRef$current12;
378
415
  setText(v);
379
- 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 : []);
380
- }, [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]);
381
446
 
382
447
  /* ---- Attachments controlled writer ----
383
448
  * useControlledState 只做"value vs innerValue"合流;onAttachmentsChange 需要手动派发。
@@ -437,10 +502,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
437
502
  * 窗口结束后恢复正常过渡。 */
438
503
  var previousExpandedRef = useRef(expanded);
439
504
  var layoutSwitchTimerRef = useRef(null);
440
- var _useState29 = useState(false),
441
- _useState30 = _slicedToArray(_useState29, 2),
442
- layoutSwitching = _useState30[0],
443
- setLayoutSwitching = _useState30[1];
505
+ var _useState33 = useState(false),
506
+ _useState34 = _slicedToArray(_useState33, 2),
507
+ layoutSwitching = _useState34[0],
508
+ setLayoutSwitching = _useState34[1];
444
509
  useLayoutEffect(function () {
445
510
  if (previousExpandedRef.current === expanded) return;
446
511
  previousExpandedRef.current = expanded;
@@ -487,10 +552,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
487
552
  * 用滞回而不是直接比大小:顶满后内容区变宽,同一段文本可能从 10 行重排
488
553
  * 成 9 行。若退出条件也看 10 行,就会「顶满 → 变宽 → 9 行 → 退出 →
489
554
  * 变窄 → 10 行 → 顶满」来回。进入看 10 行,退出要回落到 9 行以下。 */
490
- var _useState31 = useState(false),
491
- _useState32 = _slicedToArray(_useState31, 2),
492
- heightCapReached = _useState32[0],
493
- setHeightCapReached = _useState32[1];
555
+ var _useState35 = useState(false),
556
+ _useState36 = _slicedToArray(_useState35, 2),
557
+ heightCapReached = _useState36[0],
558
+ setHeightCapReached = _useState36[1];
494
559
  useLayoutEffect(function () {
495
560
  if (!editorHeight) return;
496
561
  setHeightCapReached(function (prev) {
@@ -512,7 +577,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
512
577
  * 内部 text 是 raw value(chip 序列化为零宽 marker)。
513
578
  * 对外只有一份契约:`onSend(segments, meta?)`
514
579
  * - segments:有序 IR(text / invocation / mention 交替)
515
- * - meta:attachments / webSearch 等非正文数据;都为空时不传
580
+ * - meta:attachments 等非正文数据;为空时不传
516
581
  *
517
582
  * 派生 hint:最后一个 chip 是 skill、其后没有内容、且它的 hint 还没关闭时,
518
583
  * `rawValueToSegments` 会在末尾追加一段 `hint`(与该 chip 后面渲染的灰字
@@ -533,8 +598,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
533
598
  * 立即回写,等于组件强行清空 value,违反受控合约。
534
599
  * - 受控 attachments:useControlledState 的 setter 写不进 mergedValue;
535
600
  * 也不能代抛 onAttachmentsChange([])——同样会被业务方常见的
536
- * `onAttachmentsChange={setAttachments}` 直接回写。
537
- * - webSearch 无受控接口,直接清。 */
601
+ * `onAttachmentsChange={setAttachments}` 直接回写。 */
538
602
  var handleSend = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
539
603
  var _editorRef$current$ge3, _editorRef$current13;
540
604
  var chips, segments, hasAttachments, meta, hasMeta, result, _editorRef$current14;
@@ -567,8 +631,8 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
567
631
  case 9:
568
632
  meta = {};
569
633
  if (hasAttachments) meta.attachments = attachments;
570
- if (webSearch) meta.webSearch = true;
571
- hasMeta = hasAttachments || webSearch;
634
+ if (showMode) meta.mode = mode;
635
+ hasMeta = hasAttachments || showMode;
572
636
  _context.next = 15;
573
637
  return onSend(segments, hasMeta ? meta : undefined);
574
638
  case 15:
@@ -586,13 +650,12 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
586
650
  if (attachmentsProp === undefined) {
587
651
  setAttachments([]);
588
652
  }
589
- setWebSearch(false);
590
- case 22:
653
+ case 21:
591
654
  case "end":
592
655
  return _context.stop();
593
656
  }
594
657
  }, _callee);
595
- })), [text, attachments, webSearch, onSend, disabled, closeParamPanel, valueProp, attachmentsProp, setAttachments]);
658
+ })), [text, attachments, mode, showMode, onSend, disabled, closeParamPanel, valueProp, attachmentsProp, setAttachments]);
596
659
 
597
660
  /* ---- File upload ----
598
661
  * 三入口(按钮 / 拖入 / 粘贴)共用此函数。先做内置校验(数量、单文件体积、重名),
@@ -618,11 +681,11 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
618
681
  var unsupportedExt = '';
619
682
  var oversizedName = '';
620
683
  var duplicated = 0;
621
- var _iterator = _createForOfIteratorHelper(fileArray),
622
- _step;
684
+ var _iterator2 = _createForOfIteratorHelper(fileArray),
685
+ _step2;
623
686
  try {
624
687
  var _loop = function _loop() {
625
- var f = _step.value;
688
+ var f = _step2.value;
626
689
  if (!isFileAccepted(f, accept)) {
627
690
  unsupportedExt = getFileExt(f.name) || f.type || '该类型';
628
691
  return 0; // continue
@@ -642,14 +705,14 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
642
705
  acceptedFiles.push(f);
643
706
  },
644
707
  _ret;
645
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
708
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
646
709
  _ret = _loop();
647
710
  if (_ret === 0) continue;
648
711
  }
649
712
  } catch (err) {
650
- _iterator.e(err);
713
+ _iterator2.e(err);
651
714
  } finally {
652
- _iterator.f();
715
+ _iterator2.f();
653
716
  }
654
717
  if (unsupportedExt) {
655
718
  var _ext = unsupportedExt.replace(/^\./, '').toUpperCase();
@@ -701,10 +764,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
701
764
  * 不夺回焦点时 Enter 会再次激活按钮而不是发送 */
702
765
  (_editorRef$current15 = editorRef.current) === null || _editorRef$current15 === void 0 || _editorRef$current15.focus();
703
766
  };
704
- var _useState33 = useState(false),
705
- _useState34 = _slicedToArray(_useState33, 2),
706
- fileDragOver = _useState34[0],
707
- setFileDragOver = _useState34[1];
767
+ var _useState37 = useState(false),
768
+ _useState38 = _slicedToArray(_useState37, 2),
769
+ fileDragOver = _useState38[0],
770
+ setFileDragOver = _useState38[1];
708
771
  var composerBoxFileDragAttrs = fileDragOver ? {
709
772
  'data-odn-composer-file-drag': ''
710
773
  } : {};
@@ -752,40 +815,101 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
752
815
  }
753
816
  if (skill.initialState === 'pending') openParamPanel(data.id);
754
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]);
755
824
 
756
825
  /* trigger query 实时筛选 skills(substring match,case-insensitive)。
757
826
  * trigger 已结束时返回空列表,避免 Popover 关闭动画那一帧把子节点切回全量目录。
758
827
  * 依赖 query / trigger 而不是整个 triggerInfo:rect 变化不应重建列表。 */
759
828
  var slashTriggerActive = (triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger) === '/';
829
+ var atTriggerActive = (triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger) === '@';
760
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]);
761
838
  var filteredSkills = useMemo(function () {
762
839
  if (!skills) return undefined;
763
840
  if (!slashTriggerActive) return [];
764
841
  return filterSkillsByQuery(skills, slashTriggerQuery);
765
842
  }, [skills, slashTriggerActive, slashTriggerQuery]);
766
843
 
767
- /* trigger 浮层:`/` 触发态 + 目录非空 + 过滤后仍有命中。
768
- * 无匹配时菜单关闭,Enter 不再被当成「选 skill」。`@` 本轮不接。 */
844
+ /* `/` 无匹配时关闭,Enter 不再被当成选技能。
845
+ * `@` 只要接了引用名单就打开,没有命中也留「暂无文件」。 */
769
846
  var hasSkills = !!(skills && skills.length > 0);
770
847
  var triggerHitCount = (_filteredSkills$lengt = filteredSkills === null || filteredSkills === void 0 ? void 0 : filteredSkills.length) !== null && _filteredSkills$lengt !== void 0 ? _filteredSkills$lengt : 0;
771
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);
772
850
 
773
851
  /* query 变化或浮层关闭 → 高亮项重置到「最上方的可用项」(跳过 disabled)。
774
852
  * 用 query 而不是 triggerInfo 整体当依赖:rect 变化(光标移位)不应该 reset。 */
775
853
  useEffect(function () {
776
854
  var _ref3;
855
+ if ((triggerInfo === null || triggerInfo === void 0 ? void 0 : triggerInfo.trigger) === '@') {
856
+ setMentionActiveIndex(0);
857
+ return;
858
+ }
777
859
  var list = (_ref3 = filteredSkills !== null && filteredSkills !== void 0 ? filteredSkills : skills) !== null && _ref3 !== void 0 ? _ref3 : [];
778
860
  var first = list.findIndex(function (s) {
779
861
  return !s.disabled;
780
862
  });
781
863
  setTriggerActiveIndex(first === -1 ? 0 : first);
782
- }, [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]);
783
865
 
784
866
  /* editor 在 trigger 激活时把 ↑↓Enter Esc Tab 丢过来。返回 true = editor 别管了。
785
867
  * - ↑↓ 改高亮 index(夹在 [0, max] 内)
786
868
  * - Enter / Tab 选中当前高亮项(Tab 默认落在第一个可用技能上)
787
869
  * - Escape 关 menu,焦点保留在 editor */
788
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
+ }
789
913
  if (!triggerMenuVisible) return false;
790
914
 
791
915
  /* ↑↓ 导航完全委托给 SkillSlot(唯一事实源:跳过 disabled、夹边、滚动进视口),
@@ -825,14 +949,14 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
825
949
  return true;
826
950
  }
827
951
  if (e.key === 'Escape') {
828
- var _editorRef$current19;
952
+ var _editorRef$current21;
829
953
  e.preventDefault();
830
- (_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();
831
955
  setTriggerInfo(null);
832
956
  return true;
833
957
  }
834
958
  return false;
835
- }, [filteredSkills, handleSelectSkill, triggerActiveIndex, triggerMenuVisible]);
959
+ }, [filteredMentions, filteredSkills, handleSelectMention, handleSelectSkill, mentionActiveIndex, mentionMenuVisible, triggerActiveIndex, triggerMenuVisible]);
836
960
 
837
961
  /* ---- Derived ---- */
838
962
  var hasUploadingAttachment = attachments.some(function (a) {
@@ -845,7 +969,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
845
969
  var isInlineLite = isLite && !expanded;
846
970
  // lite 模式下强制仅图标(包括升格后)
847
971
  var effectiveLabeled = isLite ? false : toolsLabeled;
848
- // lite 模式下仅允许一个工具按钮(数量在两态下保持恒定)
972
+ // lite 仍只留一个左侧工具。快速 / 精选不在这组里。
849
973
  var effectiveTools = isLite ? tools.slice(0, 1) : tools;
850
974
  useLayoutEffect(function () {
851
975
  var el = triggerAnchorRef.current;
@@ -855,7 +979,9 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
855
979
  el.style.top = "".concat(triggerInfo.rect.top - boxRect.top, "px");
856
980
  }, [triggerInfo]);
857
981
  useLayoutEffect(function () {
858
- if (!toolMenuOpen && !triggerMenuVisible) return undefined;
982
+ if (!toolMenuOpen && !triggerMenuVisible && !mentionMenuVisible) {
983
+ return undefined;
984
+ }
859
985
  var sync = function sync() {
860
986
  var _skillTriggerRef$curr, _skillTriggerRef$curr2, _ref6, _triggerInfo$rect$top, _triggerAnchorRef$cur;
861
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;
@@ -877,7 +1003,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
877
1003
  window.removeEventListener('resize', sync);
878
1004
  ro === null || ro === void 0 || ro.disconnect();
879
1005
  };
880
- }, [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]);
881
1007
  useLayoutEffect(function () {
882
1008
  var box = boxRef.current;
883
1009
  var trigger = skillTriggerRef.current;
@@ -932,7 +1058,6 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
932
1058
  };
933
1059
  return {
934
1060
  attachment: resolve('attachment'),
935
- webSearch: resolve('webSearch'),
936
1061
  skill: resolve('skill')
937
1062
  };
938
1063
  }();
@@ -944,7 +1069,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
944
1069
  /* ---- Tool buttons ----
945
1070
  * disabled(整体禁用)合并到各按钮的 disabled 状态:
946
1071
  * - attachment:disabled || attachmentsDisabled
947
- * - webSearch / skill:disabled(attachmentsDisabled 不影响这两个)
1072
+ * - skill:disabled(attachmentsDisabled 不影响技能)
948
1073
  * skill 的 Popover 在 disabled 时禁用 trigger,避免弹出菜单。 */
949
1074
  var renderAttachmentBtn = function renderAttachmentBtn() {
950
1075
  var btnDisabled = disabled || attachmentsDisabled;
@@ -989,55 +1114,73 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
989
1114
  size: 16
990
1115
  }), showLabel && /*#__PURE__*/React.createElement("span", null, label))));
991
1116
  };
992
- var renderWebSearchBtn = function renderWebSearchBtn() {
993
- var _resolvedToolConfigs$2 = resolvedToolConfigs.webSearch,
994
- icon = _resolvedToolConfigs$2.icon,
995
- label = _resolvedToolConfigs$2.label,
996
- tooltip = _resolvedToolConfigs$2.tooltip;
997
- var showIcon = !!icon;
998
- var showLabel = effectiveLabeled && !!label;
999
- return /*#__PURE__*/React.createElement(Tooltip, {
1000
- key: "webSearch",
1001
- popup: tooltip,
1002
- placement: "top"
1003
- }, /*#__PURE__*/React.createElement(HoverFill, {
1004
- "data-odn-composer-tool-btn-wrap": true,
1005
- bgClassName: "odn-composer-tool-btn-bg",
1006
- activeColor: TOOL_BTN_ACTIVE,
1007
- 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
+ }
1008
1157
  }, /*#__PURE__*/React.createElement("button", {
1009
1158
  type: "button",
1010
1159
  tabIndex: -1,
1011
- onClick: function onClick() {
1012
- onToolClick === null || onToolClick === void 0 || onToolClick('webSearch');
1013
- setWebSearch(function (v) {
1014
- return !v;
1015
- });
1016
- }
1017
- /* 同附件按钮:防止点击后焦点留在按钮上,Enter 误触发 toggle 而不是发送 */,
1160
+ disabled: disabled,
1018
1161
  onMouseDown: function onMouseDown(e) {
1019
- return e.preventDefault();
1162
+ e.preventDefault();
1163
+ if (!modeOpen) setPanelMode(mode);
1020
1164
  },
1021
- disabled: disabled,
1022
- "data-odn-composer-tool-btn": true,
1023
- "data-odn-composer-tool-iconic": !showLabel || undefined,
1024
- "data-odn-composer-tool-active": webSearch || undefined,
1025
- "data-odn-composer-tool-disabled": disabled || undefined,
1026
- "aria-label": label || 'webSearch',
1027
- "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,
1028
1170
  "aria-disabled": disabled || undefined
1029
- }, showIcon && /*#__PURE__*/React.createElement(Icon, {
1030
- name: icon,
1031
- size: 16
1032
- }), 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
+ })));
1033
1176
  };
1034
1177
  var renderSkillBtn = function renderSkillBtn() {
1035
1178
  var hasAnySkills = skills && skills.length > 0;
1036
1179
  if (!hasAnySkills) return null;
1037
- var _resolvedToolConfigs$3 = resolvedToolConfigs.skill,
1038
- icon = _resolvedToolConfigs$3.icon,
1039
- label = _resolvedToolConfigs$3.label,
1040
- 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;
1041
1184
  var showIcon = !!icon;
1042
1185
  var showLabel = effectiveLabeled && !!label;
1043
1186
  return /*#__PURE__*/React.createElement(Tooltip, {
@@ -1104,7 +1247,6 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1104
1247
  };
1105
1248
  var toolNodes = effectiveTools.map(function (t) {
1106
1249
  if (t === 'attachment') return renderAttachmentBtn();
1107
- if (t === 'webSearch') return renderWebSearchBtn();
1108
1250
  if (t === 'skill') return renderSkillBtn();
1109
1251
  return null;
1110
1252
  }).filter(Boolean);
@@ -1178,6 +1320,10 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1178
1320
  name: "arrow-up",
1179
1321
  size: 18
1180
1322
  }));
1323
+ var modeButton = showMode ? renderModeBtn() : null;
1324
+ var sendCluster = /*#__PURE__*/React.createElement("div", {
1325
+ "data-odn-composer-toolbar-right": true
1326
+ }, modeButton, sendOrStopButton);
1181
1327
 
1182
1328
  // 工具栏(full 专用,作为 textarea 的 bottomElement)
1183
1329
  var toolbarSlot = /*#__PURE__*/React.createElement("div", {
@@ -1187,7 +1333,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1187
1333
  placement: "top",
1188
1334
  "data-odn-composer-toolbar-left": true,
1189
1335
  visible: toolMenuOpen ? false : undefined
1190
- }, toolNodes), sendOrStopButton);
1336
+ }, toolNodes), sendCluster);
1191
1337
 
1192
1338
  // 文件 input 独立渲染一次,在两种形态下都由同一 ref 控制
1193
1339
  var hiddenFileInput = /*#__PURE__*/React.createElement("input", {
@@ -1198,9 +1344,9 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1198
1344
  "data-odn-composer-file-input": true,
1199
1345
  onChange: handleFileChange
1200
1346
  });
1201
- var paramPanelNode = function (_editorRef$current$ge4, _editorRef$current20, _boxRef$current) {
1347
+ var paramPanelNode = function (_editorRef$current$ge4, _editorRef$current22, _boxRef$current) {
1202
1348
  if (!paramEdit || !renderParamPanel) return null;
1203
- 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 : [];
1204
1350
  var chip = chips.find(function (c) {
1205
1351
  return c.id === paramEdit.chipId;
1206
1352
  });
@@ -1210,8 +1356,8 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1210
1356
  chip: chip,
1211
1357
  anchor: paramEdit.anchor,
1212
1358
  onApply: function onApply(patch) {
1213
- var _editorRef$current21;
1214
- (_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);
1215
1361
  },
1216
1362
  onClose: closeParamPanel
1217
1363
  };
@@ -1235,11 +1381,11 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1235
1381
  value: text,
1236
1382
  defaultChips: defaultChips,
1237
1383
  onChange: updateText,
1384
+ onSkillHintChange: onSkillHintChange,
1238
1385
  disabled: disabled,
1239
1386
  paramEditChipId: (_paramEdit$chipId = paramEdit === null || paramEdit === void 0 ? void 0 : paramEdit.chipId) !== null && _paramEdit$chipId !== void 0 ? _paramEdit$chipId : null,
1240
- onInlineRefClick: renderParamPanel ? handleInlineRefClick : undefined
1241
- /* 本轮只接 `/`,`@` 留给业务接 mention 搜索时再加 */,
1242
- triggers: ['/'],
1387
+ onInlineRefClick: renderParamPanel ? handleInlineRefClick : undefined,
1388
+ triggers: [].concat(_toConsumableArray(slash ? ['/'] : []), _toConsumableArray(hasMentions ? ['@'] : [])),
1243
1389
  onTriggerChange: setTriggerInfo,
1244
1390
  onTriggerKeyDown: handleTriggerKeyDown,
1245
1391
  onPasteFiles: addFilesFromList,
@@ -1258,7 +1404,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1258
1404
  onPressEnter: function onPressEnter(e) {
1259
1405
  if (e.nativeEvent.isComposing || e.keyCode === 229) return;
1260
1406
  // trigger 浮层有可选中项时,普通 Enter 留给浮层做选择;Shift+Enter 继续换行
1261
- if (triggerMenuVisible && !e.shiftKey) {
1407
+ if ((triggerMenuVisible || mentionMenuVisible) && !e.shiftKey) {
1262
1408
  e.preventDefault();
1263
1409
  return false;
1264
1410
  }
@@ -1274,24 +1420,31 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1274
1420
  }
1275
1421
  });
1276
1422
 
1277
- /* `/` 浮层:anchor 水平铺满 composer box、竖直跟 caret,菜单与输入框水平居中。
1278
- * 共用 SkillSlot 渲染;过滤后无命中时 triggerMenuVisible=false,不挂菜单。 */
1279
- var triggerMenu = hasSkills ? /*#__PURE__*/React.createElement(Popover, {
1423
+ /* `/` 与 `@` 共用锚点。`/` 无命中不挂菜单;`@` 无命中仍显示空态。 */
1424
+ var triggerPopupVisible = triggerMenuVisible || mentionMenuVisible;
1425
+ var triggerMenu = hasSkills || hasMentions ? /*#__PURE__*/React.createElement(Popover, {
1280
1426
  trigger: "click",
1281
1427
  arrowed: false,
1282
1428
  placement: "top",
1283
1429
  shift: false,
1284
1430
  offset: 4,
1285
- visible: triggerMenuVisible,
1431
+ visible: triggerPopupVisible,
1286
1432
  onVisibleChange: function onVisibleChange(v) {
1287
1433
  if (!v) {
1288
- var _editorRef$current22;
1289
- (_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();
1290
1436
  setTriggerInfo(null);
1291
1437
  }
1292
1438
  },
1293
- popupClassName: "odn-composer-skill-popup",
1294
- 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, {
1295
1448
  ref: triggerSkillSlotRef,
1296
1449
  variant: "menu",
1297
1450
  skills: filteredSkills !== null && filteredSkills !== void 0 ? filteredSkills : [],
@@ -1332,6 +1485,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1332
1485
  "data-odn-composer-height-cap": atHeightCap ? '' : undefined,
1333
1486
  "data-odn-composer-layout-switching": layoutSwitching ? '' : undefined,
1334
1487
  "data-odn-composer-disabled": disabled || undefined,
1488
+ "data-odn-composer-mode-dock": showMode ? '' : undefined,
1335
1489
  "data-odn-composer-spacing": spacing === 'drawer' ? 'drawer' : undefined,
1336
1490
  "aria-disabled": disabled || undefined,
1337
1491
  className: className !== null && className !== void 0 ? className : spacing === 'drawer' ? 'shrink-0' : undefined,
@@ -1373,7 +1527,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
1373
1527
  visible: toolMenuOpen ? false : undefined
1374
1528
  }, toolNodes), isLite && /*#__PURE__*/React.createElement("div", {
1375
1529
  "data-odn-composer-lite-send": true
1376
- }, sendOrStopButton)), hint && /*#__PURE__*/React.createElement("p", {
1530
+ }, sendCluster)), hint && /*#__PURE__*/React.createElement("p", {
1377
1531
  "data-odn-composer-hint": true
1378
1532
  }, submitType === 'enter' ? 'Enter 发送 · Shift+Enter 换行 · 可拖入或粘贴文件' : 'Shift+Enter 发送 · Enter 换行 · 可拖入或粘贴文件')));
1379
1533
  });