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.
@@ -7,5 +7,5 @@
7
7
  export declare const SKILL_CATEGORY_ICON = "astroid";
8
8
  /** skill / 未标明的 invocation 通道 → 技能品类 icon;其它 kind 回退 data.icon */
9
9
  export declare function resolveInvocationCategoryIcon(kind?: string, fallback?: string): string | undefined;
10
- /** mention 按 kind 给品类 icon;未知 kind 回退 data.icon */
10
+ /** mention 按 kind 给品类 icon。`mention` 是 @ 文件引用,不画 icon,格式看文件名后缀。 */
11
11
  export declare function resolveMentionCategoryIcon(kind?: string, fallback?: string): string | undefined;
@@ -14,9 +14,10 @@ export function resolveInvocationCategoryIcon(kind, fallback) {
14
14
  return fallback;
15
15
  }
16
16
 
17
- /** mention 按 kind 给品类 icon;未知 kind 回退 data.icon */
17
+ /** mention 按 kind 给品类 icon。`mention` 是 @ 文件引用,不画 icon,格式看文件名后缀。 */
18
18
  export function resolveMentionCategoryIcon(kind, fallback) {
19
19
  if (kind === 'file') return 'file-text';
20
- if (kind === 'user' || kind === 'mention') return 'user';
20
+ if (kind === 'user') return 'user';
21
+ if (kind === 'mention') return undefined;
21
22
  return fallback;
22
23
  }
@@ -131,10 +131,10 @@ export interface ChipData {
131
131
  label: string;
132
132
  icon?: string;
133
133
  /**
134
- * 'invocation' = `/` 或 Skill 按钮;'mention' = `@`(预留)。
134
+ * 'invocation' = `/` 或 Skill 按钮;'mention' = `@`;'slot' = 提示词槽位。
135
135
  * 其他值归 invocation。
136
136
  */
137
- kind?: 'invocation' | 'mention' | (string & NonNullable<unknown>);
137
+ kind?: 'invocation' | 'mention' | 'slot' | (string & NonNullable<unknown>);
138
138
  state?: InlineRefState;
139
139
  payload?: Record<string, unknown>;
140
140
  /**
@@ -222,6 +222,8 @@ export declare const ComposerSegmentType: {
222
222
  readonly Text: "text";
223
223
  readonly Invocation: "invocation";
224
224
  readonly Mention: "mention";
225
+ /** 提示词模板槽位。空槽保留名称,填过的带 value */
226
+ readonly Slot: "slot";
225
227
  /** 派生的 skill 提示文本;发送时按结构谓词追加,见 `ComposerHintSegment` */
226
228
  readonly Hint: "hint";
227
229
  };
@@ -238,6 +240,17 @@ export interface ComposerMentionSegment {
238
240
  type: typeof ComposerSegmentType.Mention;
239
241
  data: MentionData;
240
242
  }
243
+ /** 提示词槽位。`name` 是空槽上的名称,`value` 是填入的字,空字符串表示还没填。 */
244
+ export interface SlotData {
245
+ id: string;
246
+ name: string;
247
+ value: string;
248
+ templateId?: string;
249
+ }
250
+ export interface ComposerSlotSegment {
251
+ type: typeof ComposerSegmentType.Slot;
252
+ data: SlotData;
253
+ }
241
254
  /**
242
255
  * 派生的 skill 提示段:**不是用户输入的内容**,而是「最后一个 skill chip + 其后
243
256
  * 无内容 + hint 未关闭」这一结构谓词成立时,由 Composer 在发送瞬间追加的模板
@@ -254,11 +267,15 @@ export interface ComposerHintSegment {
254
267
  export interface HintData {
255
268
  text: string;
256
269
  }
257
- export type ComposerSegment = ComposerTextSegment | ComposerInvocationSegment | ComposerMentionSegment | ComposerHintSegment;
270
+ export type ComposerSegment = ComposerTextSegment | ComposerInvocationSegment | ComposerMentionSegment | ComposerSlotSegment | ComposerHintSegment;
258
271
  /** `onSend(segments, meta?)` 第二参数:非正文的附加数据。 */
259
272
  export interface SendMeta {
260
273
  attachments?: Attachment[];
261
- webSearch?: boolean;
274
+ /**
275
+ * 推理模式。只有工具栏放了 `mode` 时才会带上。
276
+ * `fast` 是快速,`featured` 是精选。
277
+ */
278
+ mode?: 'fast' | 'featured';
262
279
  }
263
280
  export type AgentEvent = {
264
281
  kind: 'thinking';
@@ -48,12 +48,16 @@ export var ComposerSegmentType = {
48
48
  Text: 'text',
49
49
  Invocation: 'invocation',
50
50
  Mention: 'mention',
51
+ /** 提示词模板槽位。空槽保留名称,填过的带 value */
52
+ Slot: 'slot',
51
53
  /** 派生的 skill 提示文本;发送时按结构谓词追加,见 `ComposerHintSegment` */
52
54
  Hint: 'hint'
53
55
  };
54
56
  // 同名 type 供 `seg.type === ComposerSegmentType.Invocation` 收窄,与 const 对象并存。
55
57
  // eslint-disable-next-line @typescript-eslint/no-redeclare -- 有意的 const + type 同名
56
58
 
59
+ /** 提示词槽位。`name` 是空槽上的名称,`value` 是填入的字,空字符串表示还没填。 */
60
+
57
61
  /**
58
62
  * 派生的 skill 提示段:**不是用户输入的内容**,而是「最后一个 skill chip + 其后
59
63
  * 无内容 + hint 未关闭」这一结构谓词成立时,由 Composer 在发送瞬间追加的模板
@@ -18,6 +18,7 @@
18
18
  */
19
19
  import React from 'react';
20
20
  import type { ChipData } from '../_genui-types';
21
+ import { type ComposerSkillHint } from './segments';
21
22
  /** Composer editor 触发态对外信号——告诉父组件该弹浮层选 skill 了。 */
22
23
  export interface ComposerEditorTriggerInfo {
23
24
  /** 触发字符(@ 或 /) */
@@ -72,6 +73,16 @@ export interface ComposerEditorProps {
72
73
  defaultValue?: string;
73
74
  /** 值变化回调;参数是 raw string,含 marker。 */
74
75
  onChange?: (value: string) => void;
76
+ /**
77
+ * 灰字(skill hint)命中状态变化回调:
78
+ * - 灰字出现(最后一个 chip 命中结构谓词)→ 传入命中的 chip / 文案
79
+ * - 灰字消失(后面有内容 / chip 被删 / hint 已关闭)→ 传入 null
80
+ *
81
+ * 只在状态**真的变化**时抛一次;与灰字渲染共用同一份判定(先落 DOM 再抛,
82
+ * 不会各自算一套)。判定成立但灰字被临时摘掉(IME 合成期)时不跟着抖。
83
+ * 挂载时会先同步一次当前状态(空内容即 null),业务侧不必自己猜初始值。
84
+ */
85
+ onSkillHintChange?: (hint: ComposerSkillHint | null) => void;
75
86
  /** 与 defaultValue / 首次受控 value 配套,把 marker 还原成 chip。 */
76
87
  defaultChips?: ChipData[];
77
88
  placeholder?: string;
@@ -279,11 +279,13 @@ export var COMPOSER_EDITOR_HEIGHT_CAP_EXIT_PX = contentHeightForRows(9);
279
279
  var measureFitsInlineWidth = function measureFitsInlineWidth(el) {
280
280
  var computed = window.getComputedStyle(el);
281
281
  var inlineInset = Number.parseFloat(computed.getPropertyValue('--odn-composer-editor-inline-inset'));
282
+ var inlineInsetEnd = Number.parseFloat(computed.getPropertyValue('--odn-composer-editor-inline-inset-end'));
282
283
  // 非 lite 变体没有这个变量:这里不做中间区判定,一律视为放得下。
283
284
  if (!Number.isFinite(inlineInset) || inlineInset <= 0) return true;
285
+ var endInset = Number.isFinite(inlineInsetEnd) && inlineInsetEnd > 0 ? inlineInsetEnd : inlineInset;
284
286
  var viewport = el.closest('[data-odn-scroll-area-viewport]');
285
287
  var containerWidth = viewport instanceof HTMLElement ? viewport.clientWidth : el.clientWidth;
286
- var inlineContentWidth = containerWidth - inlineInset * 2;
288
+ var inlineContentWidth = containerWidth - inlineInset - endInset;
287
289
  if (inlineContentWidth <= 0) return true;
288
290
 
289
291
  // 逐个文本节点取 rect:Range 覆盖整棵子树时拿到的是行容器(block)的宽度,
@@ -326,6 +328,7 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
326
328
  defaultValue = _ref$defaultValue === void 0 ? '' : _ref$defaultValue,
327
329
  defaultChips = _ref.defaultChips,
328
330
  onChange = _ref.onChange,
331
+ onSkillHintChange = _ref.onSkillHintChange,
329
332
  placeholder = _ref.placeholder,
330
333
  autoSize = _ref.autoSize,
331
334
  onPressEnter = _ref.onPressEnter,
@@ -353,6 +356,9 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
353
356
  var isComposingRef = useRef(false);
354
357
  var onTriggerChangeRef = useRef(onTriggerChange);
355
358
  onTriggerChangeRef.current = onTriggerChange;
359
+ /** 灰字状态回调走 ref:调用方常传内联函数,不能让 fireChange / 挂载同步因此反复重建 */
360
+ var onSkillHintChangeRef = useRef(onSkillHintChange);
361
+ onSkillHintChangeRef.current = onSkillHintChange;
356
362
  var isControlled = valueProp !== undefined;
357
363
  var minRows = (_autoSize$minRows = autoSize === null || autoSize === void 0 ? void 0 : autoSize.minRows) !== null && _autoSize$minRows !== void 0 ? _autoSize$minRows : 1;
358
364
  var maxRows = autoSize === null || autoSize === void 0 ? void 0 : autoSize.maxRows;
@@ -587,6 +593,33 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
587
593
  }
588
594
  }, [removeHintNode]);
589
595
 
596
+ /* 灰字状态的**上一帧快照**:回调只在真的变化时抛一次。
597
+ * fireChange 每次输入都会跑,不去重的话业务侧会被同一条状态反复冲刷。
598
+ * `undefined` = 还没抛过(与「抛过 null」区分),保证挂载时一定会同步一次。 */
599
+ var lastSkillHintRef = useRef(undefined);
600
+
601
+ /**
602
+ * 把「灰字当前状态」通知给业务方(与 syncHintNode 同一份 target,保证同源)。
603
+ *
604
+ * 只在 chipId / refId / 文案任一变化时抛:同一条状态在连续输入里会算很多次。
605
+ */
606
+ var notifySkillHint = useCallback(function (target, chips) {
607
+ var _chips$find, _onSkillHintChangeRef;
608
+ var next = target ? {
609
+ chipId: target.chipId,
610
+ text: target.text,
611
+ refId: (_chips$find = chips.find(function (chip) {
612
+ return chip.id === target.chipId;
613
+ })) === null || _chips$find === void 0 ? void 0 : _chips$find.refId
614
+ } : null;
615
+ var prev = lastSkillHintRef.current;
616
+ if (prev !== undefined && (prev === null || prev === void 0 ? void 0 : prev.chipId) === (next === null || next === void 0 ? void 0 : next.chipId) && (prev === null || prev === void 0 ? void 0 : prev.text) === (next === null || next === void 0 ? void 0 : next.text) && (prev === null || prev === void 0 ? void 0 : prev.refId) === (next === null || next === void 0 ? void 0 : next.refId)) {
617
+ return;
618
+ }
619
+ lastSkillHintRef.current = next;
620
+ (_onSkillHintChangeRef = onSkillHintChangeRef.current) === null || _onSkillHintChangeRef === void 0 || _onSkillHintChangeRef.call(onSkillHintChangeRef, next);
621
+ }, []);
622
+
590
623
  /** 把 caret 直接落到 hint 之前(= chip 右侧);没有 hint 时不动。 */
591
624
  var placeCaretBeforeHint = useCallback(function () {
592
625
  var el = editorRef.current;
@@ -606,6 +639,10 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
606
639
  var pullCaretBeforeHint = useCallback(function () {
607
640
  var el = editorRef.current;
608
641
  var sel = window.getSelection();
642
+ var active = document.activeElement;
643
+ if (active instanceof HTMLElement && active.hasAttribute('data-odn-composer-slot-input')) {
644
+ return;
645
+ }
609
646
  if (!el || !sel || sel.rangeCount !== 1 || !sel.isCollapsed) return;
610
647
  if (!el.contains(sel.anchorNode)) return;
611
648
  if (!normalizeCollapsedCaretBeforeHint(sel.getRangeAt(0), el)) return;
@@ -627,7 +664,12 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
627
664
  * 否则「chip 被删」时残留的 hint 文本会让 placeholder 判定与 lite 升格
628
665
  * 判定晚一拍。 */
629
666
  closeChipHintsRef.current(resolveHintClosedChipIds(raw, chips));
630
- syncHintNode(resolveHintTarget(raw, getChipsRef.current()));
667
+ /* 关闭会改写 chips 快照,谓词必须用关闭后的快照重新取;
668
+ * 渲染(syncHintNode)与对外回调(notifySkillHint)共用这一份 target,
669
+ * 业务拿到的是「这一刻灰字该不该在」的同一份判定,不会各算一套。 */
670
+ var hintChips = getChipsRef.current();
671
+ var hintTarget = resolveHintTarget(raw, hintChips);
672
+ syncHintNode(hintTarget);
631
673
  var showPlaceholder = shouldShowPlaceholder(el);
632
674
  if (forceNonEmptyOnceRef.current && showPlaceholder) {
633
675
  setEmpty(false);
@@ -640,7 +682,23 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
640
682
  scheduleControlledSync();
641
683
  // editor.height 锁定时 RO 不触发,主动同步一次,确保升格判定能拿到 scrollHeight
642
684
  syncHeight();
643
- }, [onChange, pullCaretBeforeHint, scheduleControlledSync, shouldShowPlaceholder, syncHeight, syncHintNode]);
685
+ /* 对外回调放在所有内部状态更新之后:业务在里面同步 setState 甚至
686
+ * updateChipData 都不会插进「DOM 已落、caret / 高度还没收尾」的中间态。 */
687
+ notifySkillHint(hintTarget, hintChips);
688
+ }, [notifySkillHint, onChange, pullCaretBeforeHint, scheduleControlledSync, shouldShowPlaceholder, syncHeight, syncHintNode]);
689
+
690
+ /* 挂载时同步一次灰字状态。
691
+ *
692
+ * 带 chips 的草稿恢复已经在挂载的 layout effect 里走 fireChange 抛过一次,
693
+ * 这里兜的是「空内容 / 只有纯文本挂载」——否则业务侧上一轮留下的状态没人复位。
694
+ * 放在 passive effect:晚于 chips 还原,且 notifySkillHint 自带去重,
695
+ * 与 fireChange 那一次重复时不会再抛。 */
696
+ useEffect(function () {
697
+ var el = editorRef.current;
698
+ if (!el) return;
699
+ var hintChips = getChipsRef.current();
700
+ notifySkillHint(resolveHintTarget(getPlainText(el), hintChips), hintChips);
701
+ }, [notifySkillHint]);
644
702
 
645
703
  /* ─────────────────────────────────────
646
704
  * chip 管理
@@ -785,6 +843,10 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
785
843
  onTriggerChange === null || onTriggerChange === void 0 || onTriggerChange(null);
786
844
  }
787
845
  onChange === null || onChange === void 0 || onChange('');
846
+ /* 这条路不走 fireChange(清空不是「内容变化」链路的一环),
847
+ * 灰字状态要在这里手动同步,否则业务侧会一直以为灰字还显示着。
848
+ * 位置放在 onChange 之后,与 fireChange 的「先 onChange 再灰字」一致。 */
849
+ notifySkillHint(null, []);
788
850
  },
789
851
  insertChip: function insertChip(data) {
790
852
  var el = editorRef.current;
@@ -881,7 +943,7 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
881
943
  return editorRef.current;
882
944
  }
883
945
  };
884
- }, [onChange, _insertChip, insertSerializedAtRange, requestScrollCaretIntoView, removeChip, resetChips, onTriggerChange, getChips, updateChipData, getChipHost]);
946
+ }, [onChange, _insertChip, insertSerializedAtRange, notifySkillHint, requestScrollCaretIntoView, removeChip, resetChips, onTriggerChange, getChips, updateChipData, getChipHost]);
885
947
 
886
948
  /* ─────────────────────────────────────
887
949
  * 监听 selection 变化:方向键 / 鼠标点击移位都要重算 trigger
@@ -1037,6 +1099,8 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
1037
1099
  }
1038
1100
  removeHintNode();
1039
1101
  resetChips();
1102
+ /* 同 clear():外部改写 value 不走 fireChange,灰字状态在这里手动同步 */
1103
+ notifySkillHint(null, []);
1040
1104
  setEmpty(shouldShowPlaceholder(el));
1041
1105
  if (triggerRef.current) {
1042
1106
  var _onTriggerChangeRef$c;
@@ -1045,7 +1109,7 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
1045
1109
  }
1046
1110
  syncHeight();
1047
1111
  }
1048
- }, [controlledSyncTick, valueProp, isControlled, resetChips, shouldShowPlaceholder, syncHeight]);
1112
+ }, [controlledSyncTick, valueProp, isControlled, notifySkillHint, resetChips, shouldShowPlaceholder, syncHeight]);
1049
1113
 
1050
1114
  /* ─────────────────────────────────────
1051
1115
  * 输入事件
@@ -1434,6 +1498,14 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
1434
1498
  return /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement(ComposerInlineRef, {
1435
1499
  data: chip.data,
1436
1500
  active: paramEditChipId === chip.data.id,
1501
+ disabled: disabled,
1502
+ onSlotChange: chip.data.kind === 'slot' ? function (value) {
1503
+ return updateChipData(chip.data.id, {
1504
+ payload: _objectSpread(_objectSpread({}, chip.data.payload), {}, {
1505
+ value: value
1506
+ })
1507
+ });
1508
+ } : undefined,
1437
1509
  onClick: onInlineRefClick && chipHasEditableParams(chip.data) ? function (anchor) {
1438
1510
  return onInlineRefClick(chip.data.id, anchor);
1439
1511
  } : undefined
@@ -25,6 +25,7 @@ function createChipHost(data) {
25
25
  host.dataset.mentionId = data.id;
26
26
  host.contentEditable = 'false';
27
27
  host.setAttribute('data-odn-composer-chip-host', '');
28
+ if (data.kind) host.dataset.chipKind = data.kind;
28
29
  return host;
29
30
  }
30
31
  function createCaretAnchorAtBoundary(parent, offset) {
@@ -3,11 +3,17 @@ import type { Attachment, ChipData, ComposerSegment, SendMeta, SkillItem } from
3
3
  import type { IconName } from '../icon';
4
4
  import { type SkillSlotProps } from '../skill-slot';
5
5
  import { type ComposerInsertOptions, type ComposerInsertPart, type ComposerInsertResult } from './insert';
6
+ import { type ComposerMentionItem } from './mention';
6
7
  import type { ComposerParamPanelContext } from './param-panel';
8
+ import { type ComposerSkillHint } from './segments';
7
9
  import './style';
8
10
  export type { ComposerInsertOptions, ComposerInsertPart, ComposerInsertResult, } from './insert';
11
+ export type { ComposerMentionItem } from './mention';
9
12
  export type { ComposerParamPanelContext } from './param-panel';
10
- export type ComposerTool = 'attachment' | 'webSearch' | 'skill';
13
+ export type { ComposerHintTarget, ComposerSkillHint } from './segments';
14
+ export type ComposerTool = 'attachment' | 'skill';
15
+ /** 快速 / 精选。不是工具按钮,`showMode` 为 true 才出现在发送键左侧。 */
16
+ export type ComposerMode = 'fast' | 'featured';
11
17
  export type ComposerVariant = 'full' | 'lite';
12
18
  export type SubmitType = 'enter' | 'shiftEnter';
13
19
  /**
@@ -18,7 +24,7 @@ export type SubmitType = 'enter' | 'shiftEnter';
18
24
  * - 整个配置对象不传(key 不存在于 `toolConfigs`)时使用内置默认值。
19
25
  * - 仅传 `tooltip`(不传 icon / label)时:保留默认图标与文案,只覆盖说明。
20
26
  * - 传了配置对象且 icon / label 均未设置、也未传 tooltip 时(`{}`),兜底显示工具英文名
21
- * (如 "attachment" / "webSearch" / "skill")作为 label,无图标,
27
+ * (如 "attachment" / "skill")作为 label,无图标,
22
28
  * 方便开发者一眼看出配置遗漏。
23
29
  */
24
30
  export interface ComposerToolConfig {
@@ -65,6 +71,7 @@ export interface ComposerRef {
65
71
  * - `skill` 走与菜单插入相同的 chip 结构(含 `hint` / `initialState`);
66
72
  * `initialState: 'pending'` 且提供 `renderParamPanel` 时自动打开参数面板
67
73
  * (`focus: false` 的后台预填不打开)。
74
+ * - `slot` 是提示词槽位:`name` 显示在空槽上,`value` 是已填的字。
68
75
  * - `chip` 是逃生舱:mention / 自定义 kind / 已存草稿都可以直接传。
69
76
  * - chip id 与编辑器已有 chip 冲突时自动换新 id,以返回的 `chipIds` 为准。
70
77
  * - 受控 `value` 场景请在 onChange 内回写,与手动输入一致(否则一帧后会被
@@ -105,11 +112,27 @@ export interface ComposerProps {
105
112
  defaultChips?: ChipData[];
106
113
  /** 输入框值变化回调。第二参是当前 chip 快照,旧调用方忽略即可。 */
107
114
  onChange?: (value: string, chips?: ChipData[]) => void;
115
+ /**
116
+ * 技能灰字(`SkillItem.hint`)的出现 / 消失回调。
117
+ *
118
+ * 入参与输入框里那行灰字共用**同一份判定**(`resolveHintTarget`):命中时给
119
+ * `{ chipId, text, refId }`,消失时给 `null`。因此业务不需要自己解析
120
+ * value / chips,也不会出现「库里觉得有、业务觉得没有」的分歧。
121
+ *
122
+ * 语义要点:
123
+ * - 只在状态真的变化时抛一次,同一条状态不会因为连续输入重复抛;
124
+ * - 判定成立但灰字被临时摘掉(IME 合成期避开候选串)时不跟着抖;
125
+ * - 挂载时先同步一次当前状态(首屏没有灰字即 `null`),便于业务复位上一轮状态;
126
+ * - 灰字一次性:收起后即使把内容删回空串也不会再抛命中;
127
+ * - 卸载不会再补一次 `null`:需要清理时由业务在自己的卸载路径处理;
128
+ * - 与 `hint`(底部快捷键提示开关)无关。
129
+ */
130
+ onSkillHintChange?: (hint: ComposerSkillHint | null) => void;
108
131
  /**
109
132
  * 发送回调。
110
133
  *
111
134
  * `segments`:有序内容 IR,业务按数组顺序自定义拼后端。
112
- * `meta`:附件 / 联网搜索等非正文数据;为空时不传。
135
+ * `meta`:附件等非正文数据;为空时不传。
113
136
  *
114
137
  * **派生 `hint` 段**:当最后一个 chip 是带 `SkillItem.hint` 的 skill、其后
115
138
  * 一个字符都没有(空格 / 换行都算内容)、且这颗 chip 的 hint 还没关闭时,
@@ -159,7 +182,7 @@ export interface ComposerProps {
159
182
  variant?: ComposerVariant;
160
183
  /**
161
184
  * 左下工具按钮集合(按数组顺序展示)。传入空数组即不渲染任何左下按钮。
162
- * @default ['attachment', 'webSearch', 'skill']
185
+ * @default ['attachment', 'skill']
163
186
  */
164
187
  tools?: ComposerTool[];
165
188
  /**
@@ -173,7 +196,6 @@ export interface ComposerProps {
173
196
  /**
174
197
  * 各工具按钮的自定义配置(icon / label / tooltip)。按 `ComposerTool` 类型做 key:
175
198
  * - `attachment`:附件按钮
176
- * - `webSearch`:联网按钮
177
199
  * - `skill`:技能按钮
178
200
  *
179
201
  * **语义约定**:
@@ -188,7 +210,6 @@ export interface ComposerProps {
188
210
  * ```tsx
189
211
  * toolConfigs={{
190
212
  * attachment: { tooltip: '上传材料,补充上下文' }, // 只改说明
191
- * webSearch: { label: '搜索' }, // 仅文字
192
213
  * skill: { icon: 'command' }, // 仅图标
193
214
  * }}
194
215
  * ```
@@ -200,6 +221,38 @@ export interface ComposerProps {
200
221
  * 菜单宽高由 Composer 按输入框与视口计算,不经本袋覆盖。
201
222
  */
202
223
  skillSlotProps?: ComposerSkillSlotProps;
224
+ /**
225
+ * 为 false 时正文里的 `/` 不打开技能菜单。技能按钮不受影响。
226
+ * @default true
227
+ */
228
+ slash?: boolean;
229
+ /**
230
+ * `@` 引用名单。不传则不响应 `@`。
231
+ * 选中时 `onSelect` 返回 `false` 不插入。块被删掉时调用 `onRemove`。
232
+ */
233
+ mentionProps?: {
234
+ items: ComposerMentionItem[];
235
+ onSelect?: (item: ComposerMentionItem) => boolean | void;
236
+ onRemove?: (mention: {
237
+ id: string;
238
+ refId?: string;
239
+ label: string;
240
+ }) => void;
241
+ };
242
+ /**
243
+ * 是否显示快速 / 精选。它贴在发送键左侧,不属于 `tools`。
244
+ * @default false
245
+ */
246
+ showMode?: boolean;
247
+ /**
248
+ * 当前推理模式。只在 `showMode` 时显示,并随发送放进 `SendMeta.mode`。
249
+ * 不传时组件自己记,默认精选。
250
+ */
251
+ mode?: ComposerMode;
252
+ /** 非受控时的初始模式。@default 'featured' */
253
+ defaultMode?: ComposerMode;
254
+ /** 用户改选快速 / 精选时调用 */
255
+ onModeChange?: (mode: ComposerMode) => void;
203
256
  /**
204
257
  * 提交模式:
205
258
  * - `'enter'`(默认):Enter 发送,Shift+Enter 换行
@@ -289,7 +342,7 @@ export interface ComposerProps {
289
342
  /**
290
343
  * 工具栏按钮被点击时触发。
291
344
  *
292
- * - `tool`:被点击的工具类型(`'attachment'` / `'webSearch'` / `'skill'`)。
345
+ * - `tool`:被点击的工具类型(`'attachment'` / `'skill'`)。
293
346
  * - 对于 `'skill'`:在 skill 弹窗打开时触发(而非选中某个 skill 时)。
294
347
  * - 此回调为纯通知性质,不影响组件内部行为(若需阻止,请使用 `disabled` 等控制)。
295
348
  */