one-design-next 0.0.12 → 0.0.13

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.
Files changed (48) hide show
  1. package/dist/_genui-types.d.ts +36 -10
  2. package/dist/attachments/index.js +66 -16
  3. package/dist/attachments/style/index.css +86 -37
  4. package/dist/composer/chip.d.ts +4 -4
  5. package/dist/composer/clipboard.d.ts +12 -0
  6. package/dist/composer/clipboard.js +84 -0
  7. package/dist/composer/editor.d.ts +3 -1
  8. package/dist/composer/editor.js +65 -13
  9. package/dist/composer/hooks/useChipManager.d.ts +6 -1
  10. package/dist/composer/hooks/useChipManager.js +76 -27
  11. package/dist/composer/hooks/useChipSelectionMarker.d.ts +7 -0
  12. package/dist/composer/hooks/useChipSelectionMarker.js +66 -0
  13. package/dist/composer/index.js +73 -32
  14. package/dist/composer/inline-ref.d.ts +9 -0
  15. package/dist/composer/inline-ref.js +21 -0
  16. package/dist/composer/send-meta.d.ts +6 -0
  17. package/dist/composer/send-meta.js +67 -0
  18. package/dist/composer/style/index.css +39 -53
  19. package/dist/composer/utils.d.ts +9 -0
  20. package/dist/composer/utils.js +43 -2
  21. package/dist/fab/index.js +3 -16
  22. package/dist/fab/style/index.css +1 -3
  23. package/dist/image/index.d.ts +43 -0
  24. package/dist/image/index.js +51 -0
  25. package/dist/image/style/index.css +59 -0
  26. package/dist/image/style/index.d.ts +2 -0
  27. package/dist/image/style/index.js +2 -0
  28. package/dist/index.d.ts +5 -1
  29. package/dist/index.js +4 -0
  30. package/dist/invocation/index.d.ts +17 -0
  31. package/dist/invocation/index.js +84 -0
  32. package/dist/invocation/style/index.css +58 -0
  33. package/dist/invocation/style/index.d.ts +2 -0
  34. package/dist/invocation/style/index.js +2 -0
  35. package/dist/mention/index.d.ts +17 -0
  36. package/dist/mention/index.js +90 -0
  37. package/dist/mention/style/index.css +58 -0
  38. package/dist/mention/style/index.d.ts +2 -0
  39. package/dist/mention/style/index.js +2 -0
  40. package/dist/message-image/index.d.ts +42 -0
  41. package/dist/message-image/index.js +46 -0
  42. package/dist/message-image/style/index.css +60 -0
  43. package/dist/message-image/style/index.d.ts +2 -0
  44. package/dist/message-image/style/index.js +2 -0
  45. package/dist/user-bubble/index.d.ts +11 -1
  46. package/dist/user-bubble/index.js +30 -5
  47. package/dist/user-bubble/style/index.css +6 -0
  48. package/package.json +2 -2
@@ -23,21 +23,37 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
23
23
  */
24
24
 
25
25
  import { useCallback, useRef, useState } from 'react';
26
+ import { forEachMarkerSegment } from "../utils";
26
27
 
27
28
  /** chip 在 React 状态中的形态:data + 对应的 DOM host。 */
28
29
 
30
+ function createChipHost(data) {
31
+ var host = document.createElement('span');
32
+ host.dataset.mentionId = data.id;
33
+ host.contentEditable = 'false';
34
+ host.setAttribute('data-odn-composer-chip-host', '');
35
+ return host;
36
+ }
29
37
  export function useChipManager( /** 每次 chip mutation 后调用,给上层(editor)一个抛 onChange 的钩子。 */
30
38
  onAfterMutate) {
31
39
  var _useState = useState([]),
32
40
  _useState2 = _slicedToArray(_useState, 2),
33
41
  chips = _useState2[0],
34
42
  setChips = _useState2[1];
35
-
36
- /** ref 镜像:命令式 API 调用 setState 之后立即再用列表时拿不到最新值,用 ref 兜底。 */
37
43
  var chipsRef = useRef(chips);
38
44
  chipsRef.current = chips;
45
+ var insertChipAtRange = useCallback(function (data, range, _editor) {
46
+ var host = createChipHost(data);
47
+ range.insertNode(host);
48
+ var next = [].concat(_toConsumableArray(chipsRef.current), [{
49
+ data: data,
50
+ host: host
51
+ }]);
52
+ chipsRef.current = next;
53
+ setChips(next);
54
+ return host;
55
+ }, []);
39
56
  var insertChip = useCallback(function (data, editor) {
40
- // ── 定位插入点 ────────────────────────────────
41
57
  var sel = window.getSelection();
42
58
  var range = null;
43
59
  if (sel && sel.rangeCount > 0 && editor.contains(sel.anchorNode)) {
@@ -47,41 +63,70 @@ onAfterMutate) {
47
63
  editor.focus();
48
64
  range = document.createRange();
49
65
  range.selectNodeContents(editor);
50
- range.collapse(false); // 末尾
66
+ range.collapse(false);
51
67
  sel = window.getSelection();
52
68
  (_sel = sel) === null || _sel === void 0 || _sel.removeAllRanges();
53
69
  (_sel2 = sel) === null || _sel2 === void 0 || _sel2.addRange(range);
54
70
  }
55
71
  if (!range || !sel) return;
56
72
  range.deleteContents();
57
-
58
- // ── 创建 host span(contenteditable=false,承载 data-mention-id) ──
59
- // 不在 chip 后塞 ZWSP 锚点:浏览器原生把 contentEditable=false 当原子块,
60
- // Backspace / 方向键已经能正确跨越;多塞一个 ZWSP 反而会撑出多个等价
61
- // caret 位置,导致「字母和 chip 之间卡一下」的体验问题。
62
- var host = document.createElement('span');
63
- host.dataset.mentionId = data.id;
64
- host.contentEditable = 'false';
65
- host.setAttribute('data-odn-composer-chip-host', '');
66
- range.insertNode(host);
67
-
68
- // 光标 → chip 之后;用户继续输入时浏览器会自动建/接续邻接 text node
73
+ var host = insertChipAtRange(data, range, editor);
69
74
  var newRange = document.createRange();
70
75
  newRange.setStartAfter(host);
71
76
  newRange.collapse(true);
72
77
  sel.removeAllRanges();
73
78
  sel.addRange(newRange);
74
- var next = [].concat(_toConsumableArray(chipsRef.current), [{
75
- data: data,
76
- host: host
77
- }]);
78
- setChips(next);
79
-
80
- // 防御 Portal 渲染过程中浏览器把光标弹回 chip 之前的边界
81
79
  requestAnimationFrame(function () {
82
80
  return editor.focus();
83
81
  });
84
82
  onAfterMutate === null || onAfterMutate === void 0 || onAfterMutate();
83
+ }, [insertChipAtRange, onAfterMutate]);
84
+ var insertSerializedAtRange = useCallback(function (value, chipList, range, editor) {
85
+ var chipById = new Map(chipList.map(function (c) {
86
+ return [c.id, c];
87
+ }));
88
+ var frag = document.createDocumentFragment();
89
+ var inserted = [];
90
+ forEachMarkerSegment(value, function (seg) {
91
+ if (seg.type === 'text') {
92
+ if (seg.text) frag.appendChild(document.createTextNode(seg.text));
93
+ return;
94
+ }
95
+ var data = chipById.get(seg.id);
96
+ if (!data) return;
97
+ var host = createChipHost(data);
98
+ frag.appendChild(host);
99
+ inserted.push({
100
+ data: data,
101
+ host: host
102
+ });
103
+ });
104
+ range.insertNode(frag);
105
+ range.collapse(false);
106
+ if (inserted.length > 0) {
107
+ var next = [].concat(_toConsumableArray(chipsRef.current), inserted);
108
+ chipsRef.current = next;
109
+ setChips(next);
110
+ }
111
+ var sel = window.getSelection();
112
+ if (sel) {
113
+ sel.removeAllRanges();
114
+ sel.addRange(range);
115
+ }
116
+ requestAnimationFrame(function () {
117
+ return editor.focus();
118
+ });
119
+ onAfterMutate === null || onAfterMutate === void 0 || onAfterMutate();
120
+ }, [onAfterMutate]);
121
+ var syncChipsAfterDomMutation = useCallback(function (editor) {
122
+ var next = chipsRef.current.filter(function (c) {
123
+ return c.host.isConnected && editor.contains(c.host);
124
+ });
125
+ if (next.length !== chipsRef.current.length) {
126
+ chipsRef.current = next;
127
+ setChips(next);
128
+ onAfterMutate === null || onAfterMutate === void 0 || onAfterMutate();
129
+ }
85
130
  }, [onAfterMutate]);
86
131
  var removeChip = useCallback(function (id) {
87
132
  var target = chipsRef.current.find(function (c) {
@@ -89,19 +134,23 @@ onAfterMutate) {
89
134
  });
90
135
  if (!target) return;
91
136
  target.host.remove();
92
- setChips(chipsRef.current.filter(function (c) {
137
+ var next = chipsRef.current.filter(function (c) {
93
138
  return c.data.id !== id;
94
- }));
139
+ });
140
+ chipsRef.current = next;
141
+ setChips(next);
95
142
  onAfterMutate === null || onAfterMutate === void 0 || onAfterMutate();
96
143
  }, [onAfterMutate]);
97
144
  var resetChips = useCallback(function () {
98
145
  setChips([]);
99
- // 不调 onAfterMutate —— resetChips 通常和 editor.innerHTML='' 配套,
100
- // 由调用方统一抛一次 onChange 即可。
146
+ chipsRef.current = [];
101
147
  }, []);
102
148
  return {
103
149
  chips: chips,
104
150
  insertChip: insertChip,
151
+ insertChipAtRange: insertChipAtRange,
152
+ insertSerializedAtRange: insertSerializedAtRange,
153
+ syncChipsAfterDomMutation: syncChipsAfterDomMutation,
105
154
  removeChip: removeChip,
106
155
  resetChips: resetChips
107
156
  };
@@ -0,0 +1,7 @@
1
+ import { type RefObject } from 'react';
2
+ import type { ManagedChip } from './useChipManager';
3
+ /**
4
+ * chip host 在原生 selection 下无高亮(contentEditable=false + user-select:none),
5
+ * 用 data-selected 自绘与浏览器 selection 一致的浅灰底。
6
+ */
7
+ export declare function useChipSelectionMarker(editorRef: RefObject<HTMLElement | null>, chips: ManagedChip[]): void;
@@ -0,0 +1,66 @@
1
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
2
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
3
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
4
+ import { useEffect, useRef } from 'react';
5
+ /**
6
+ * chip host 在原生 selection 下无高亮(contentEditable=false + user-select:none),
7
+ * 用 data-selected 自绘与浏览器 selection 一致的浅灰底。
8
+ */
9
+ export function useChipSelectionMarker(editorRef, chips) {
10
+ var chipsRef = useRef(chips);
11
+ chipsRef.current = chips;
12
+ useEffect(function () {
13
+ var clearSelected = function clearSelected() {
14
+ var _iterator = _createForOfIteratorHelper(chipsRef.current),
15
+ _step;
16
+ try {
17
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
18
+ var chip = _step.value;
19
+ chip.host.removeAttribute('data-selected');
20
+ }
21
+ } catch (err) {
22
+ _iterator.e(err);
23
+ } finally {
24
+ _iterator.f();
25
+ }
26
+ };
27
+ var handler = function handler() {
28
+ var editor = editorRef.current;
29
+ if (!editor) {
30
+ clearSelected();
31
+ return;
32
+ }
33
+ var sel = window.getSelection();
34
+ if (!sel || sel.rangeCount === 0) {
35
+ clearSelected();
36
+ return;
37
+ }
38
+ var range = sel.getRangeAt(0);
39
+ if (range.collapsed || !editor.contains(range.commonAncestorContainer)) {
40
+ clearSelected();
41
+ return;
42
+ }
43
+ var _iterator2 = _createForOfIteratorHelper(chipsRef.current),
44
+ _step2;
45
+ try {
46
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
47
+ var chip = _step2.value;
48
+ if (range.intersectsNode(chip.host)) {
49
+ chip.host.setAttribute('data-selected', '');
50
+ } else {
51
+ chip.host.removeAttribute('data-selected');
52
+ }
53
+ }
54
+ } catch (err) {
55
+ _iterator2.e(err);
56
+ } finally {
57
+ _iterator2.f();
58
+ }
59
+ };
60
+ document.addEventListener('selectionchange', handler);
61
+ return function () {
62
+ document.removeEventListener('selectionchange', handler);
63
+ clearSelected();
64
+ };
65
+ }, [editorRef]);
66
+ }
@@ -13,12 +13,14 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
13
13
  import { useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo, forwardRef, useImperativeHandle } from 'react';
14
14
  import HoverFill from "../hover-fill";
15
15
  import Icon from "../icon";
16
+ import Image from "../image";
16
17
  import Popover from "../popover";
17
18
  import Attachments from "../attachments";
18
19
  import { Message } from "../message";
19
20
  import SkillSlot from "../skill-slot";
20
21
  import useControlledState from "../_util/useControlledState";
21
22
  import { ComposerEditor } from "./editor";
23
+ import { buildSendMetaFromChips } from "./send-meta";
22
24
  import { stripMarkers } from "./utils";
23
25
  import "./style";
24
26
  var TOOL_BTN_ACTIVE = 'color-mix(in srgb, var(--odn-color-cyan-5) 10%, transparent)';
@@ -139,7 +141,7 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
139
141
  skillId: skill.id,
140
142
  label: skill.label,
141
143
  icon: skill.icon,
142
- kind: 'skill'
144
+ kind: 'invocation'
143
145
  };
144
146
  (_editorRef$current4 = editorRef.current) === null || _editorRef$current4 === void 0 || _editorRef$current4.insertChip(data);
145
147
  (_editorRef$current5 = editorRef.current) === null || _editorRef$current5 === void 0 || _editorRef$current5.focus();
@@ -210,20 +212,26 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
210
212
  /* ---- Send ----
211
213
  * 内部 text 是 raw value(chip 序列化为不可见 marker 字符)。对外 onSend:
212
214
  * - text 给 stripMarkers 之后的纯文本(旧业务收到的字符串保持纯净)
213
- * - chips 从 editor.getChips() 拿,按 DOM 顺序,业务方按 kind 自行分类
214
- * (BREAKING from v0.0.x:取代 meta.skill 单值字段) */
215
+ * - invocations / mentions 从 editor.getChips() buildSendMetaFromChips 拆分;
216
+ * chips 仍双写(deprecated)
217
+ *
218
+ * 发送后内部状态全部清空:text、attachments、webSearch——与 ChatGPT / Claude /
219
+ * iMessage 等业界对话产品一致。attachments 不 revoke blob URL:业务方在 onSend
220
+ * 回调里可能仍持有 url(消息记录、本地预览等),由消费方负责回收。 */
215
221
  var handleSend = useCallback(function () {
216
- var _editorRef$current$ge, _editorRef$current7;
222
+ var _editorRef$current$ge, _editorRef$current7, _editorRef$current8;
217
223
  if (disabled) return;
218
224
  var plain = stripMarkers(text).trim();
219
225
  if (!plain && attachments.length === 0) return;
220
226
  var chips = (_editorRef$current$ge = (_editorRef$current7 = editorRef.current) === null || _editorRef$current7 === void 0 ? void 0 : _editorRef$current7.getChips()) !== null && _editorRef$current$ge !== void 0 ? _editorRef$current$ge : [];
221
- var meta = {
227
+ var meta = buildSendMetaFromChips(chips, {
222
228
  attachments: attachments.length > 0 ? attachments : undefined,
223
- webSearch: webSearch || undefined,
224
- chips: chips.length > 0 ? chips : undefined
225
- };
229
+ webSearch: webSearch || undefined
230
+ });
226
231
  onSend(plain, meta);
232
+ (_editorRef$current8 = editorRef.current) === null || _editorRef$current8 === void 0 || _editorRef$current8.clear();
233
+ setAttachments([]);
234
+ setWebSearch(false);
227
235
  }, [text, attachments, webSearch, onSend]);
228
236
 
229
237
  /* ---- File upload ----
@@ -349,17 +357,17 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
349
357
  skillId: skill.id,
350
358
  label: skill.label,
351
359
  icon: skill.icon,
352
- kind: 'skill'
360
+ kind: 'invocation'
353
361
  };
354
362
  if (source === 'trigger') {
355
- var _editorRef$current8;
356
- (_editorRef$current8 = editorRef.current) === null || _editorRef$current8 === void 0 || _editorRef$current8.replaceTriggerWithChip(data);
363
+ var _editorRef$current9;
364
+ (_editorRef$current9 = editorRef.current) === null || _editorRef$current9 === void 0 || _editorRef$current9.replaceTriggerWithChip(data);
357
365
  setTriggerInfo(null);
358
366
  } else {
359
- var _editorRef$current9, _editorRef$current10;
360
- (_editorRef$current9 = editorRef.current) === null || _editorRef$current9 === void 0 || _editorRef$current9.insertChip(data);
367
+ var _editorRef$current10, _editorRef$current11;
368
+ (_editorRef$current10 = editorRef.current) === null || _editorRef$current10 === void 0 || _editorRef$current10.insertChip(data);
361
369
  setToolMenuOpen(false);
362
- (_editorRef$current10 = editorRef.current) === null || _editorRef$current10 === void 0 || _editorRef$current10.focus();
370
+ (_editorRef$current11 = editorRef.current) === null || _editorRef$current11 === void 0 || _editorRef$current11.focus();
363
371
  }
364
372
  }, []);
365
373
 
@@ -409,9 +417,9 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
409
417
  return true;
410
418
  }
411
419
  if (e.key === 'Escape') {
412
- var _editorRef$current11;
420
+ var _editorRef$current12;
413
421
  e.preventDefault();
414
- (_editorRef$current11 = editorRef.current) === null || _editorRef$current11 === void 0 || _editorRef$current11.cancelTrigger();
422
+ (_editorRef$current12 = editorRef.current) === null || _editorRef$current12 === void 0 || _editorRef$current12.cancelTrigger();
415
423
  setTriggerInfo(null);
416
424
  return true;
417
425
  }
@@ -551,23 +559,56 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
551
559
  var hasTools = toolNodes.length > 0;
552
560
 
553
561
  /* ---- Slots ----
554
- * 删除:按 internalBlobUrlsRef 判断是否需要 revoke,避免误 revoke 业务方的云端 url;
555
- * 同时派发 onAttachmentRemove(语义钩子,便于业务调"服务端删文件")与 onAttachmentsChange。 */
562
+ * 待发送区按 type 分流:
563
+ * - 图片(image/* + url)→ Image leaf + 72×72 cover wrap,输入态用模板
564
+ * 驱动尺寸(整齐划一比保留长宽比要紧)
565
+ * - 其他附件 → 走 Attachments,保持原 file-card 渲染
566
+ *
567
+ * 删除:按 internalBlobUrlsRef 判断是否需要 revoke,避免误 revoke 业务方
568
+ * 的云端 url;同时派发 onAttachmentRemove(语义钩子,便于业务调"服务端
569
+ * 删文件")与 onAttachmentsChange。
570
+ *
571
+ * 分流子集与原 attachments 的 index 不一致,对外回调(onAttachmentRemove)
572
+ * 用 attachments.indexOf 拿到原始 index,保持业务方语义。 */
573
+ var isImageAttachment = function isImageAttachment(a) {
574
+ return a.type.startsWith('image/') && !!a.url;
575
+ };
576
+ var composerImages = attachments.filter(isImageAttachment);
577
+ var composerFiles = attachments.filter(function (a) {
578
+ return !isImageAttachment(a);
579
+ });
580
+ var removeAttachmentAt = function removeAttachmentAt(i) {
581
+ var removed = attachments[i];
582
+ if (removed !== null && removed !== void 0 && removed.url && internalBlobUrlsRef.current.has(removed.url)) {
583
+ URL.revokeObjectURL(removed.url);
584
+ internalBlobUrlsRef.current.delete(removed.url);
585
+ }
586
+ var next = attachments.filter(function (_, idx) {
587
+ return idx !== i;
588
+ });
589
+ if (removed) onAttachmentRemove === null || onAttachmentRemove === void 0 || onAttachmentRemove(i, removed);
590
+ updateAttachments(next);
591
+ };
556
592
  var attachmentsSlot = attachments.length > 0 ? /*#__PURE__*/React.createElement("div", {
557
593
  "data-odn-composer-attachments": true
558
- }, /*#__PURE__*/React.createElement(Attachments, {
559
- attachments: attachments,
560
- onRemove: function onRemove(i) {
561
- var removed = attachments[i];
562
- if (removed !== null && removed !== void 0 && removed.url && internalBlobUrlsRef.current.has(removed.url)) {
563
- URL.revokeObjectURL(removed.url);
564
- internalBlobUrlsRef.current.delete(removed.url);
594
+ }, composerImages.length > 0 && /*#__PURE__*/React.createElement("div", {
595
+ "data-odn-composer-images": true
596
+ }, composerImages.map(function (img) {
597
+ var realIdx = attachments.indexOf(img);
598
+ return /*#__PURE__*/React.createElement("div", {
599
+ key: "".concat(img.name, "-").concat(realIdx),
600
+ "data-odn-composer-image-thumb": true
601
+ }, /*#__PURE__*/React.createElement(Image, {
602
+ url: img.url,
603
+ alt: img.name,
604
+ onRemove: function onRemove() {
605
+ return removeAttachmentAt(realIdx);
565
606
  }
566
- var next = attachments.filter(function (_, idx) {
567
- return idx !== i;
568
- });
569
- if (removed) onAttachmentRemove === null || onAttachmentRemove === void 0 || onAttachmentRemove(i, removed);
570
- updateAttachments(next);
607
+ }));
608
+ })), composerFiles.length > 0 && /*#__PURE__*/React.createElement(Attachments, {
609
+ attachments: composerFiles,
610
+ onRemove: function onRemove(i) {
611
+ return removeAttachmentAt(attachments.indexOf(composerFiles[i]));
571
612
  }
572
613
  })) : null;
573
614
 
@@ -664,8 +705,8 @@ export var Composer = /*#__PURE__*/forwardRef(function Composer(_ref, ref) {
664
705
  visible: triggerMenuVisible,
665
706
  onVisibleChange: function onVisibleChange(v) {
666
707
  if (!v) {
667
- var _editorRef$current12;
668
- (_editorRef$current12 = editorRef.current) === null || _editorRef$current12 === void 0 || _editorRef$current12.cancelTrigger();
708
+ var _editorRef$current13;
709
+ (_editorRef$current13 = editorRef.current) === null || _editorRef$current13 === void 0 || _editorRef$current13.cancelTrigger();
669
710
  setTriggerInfo(null);
670
711
  }
671
712
  },
@@ -0,0 +1,9 @@
1
+ /// <reference types="react" />
2
+ import type { ChipData } from './chip';
3
+ export declare function isMentionChip(chip: ChipData): boolean;
4
+ export interface ComposerInlineRefProps {
5
+ data: ChipData;
6
+ }
7
+ /** Composer 内联引用视觉:按 kind 派发 Invocation(/)或 Mention(@)。 */
8
+ export declare function ComposerInlineRef({ data }: ComposerInlineRefProps): import("react").JSX.Element;
9
+ export default ComposerInlineRef;
@@ -0,0 +1,21 @@
1
+ import { Invocation } from "../invocation";
2
+ import { Mention } from "../mention";
3
+ import { chipToInvocationData, chipToMentionData } from "./send-meta";
4
+ export function isMentionChip(chip) {
5
+ return chip.kind === 'mention';
6
+ }
7
+ /** Composer 内联引用视觉:按 kind 派发 Invocation(/)或 Mention(@)。 */
8
+ export function ComposerInlineRef(_ref) {
9
+ var data = _ref.data;
10
+ if (isMentionChip(data)) {
11
+ return /*#__PURE__*/React.createElement(Mention, {
12
+ data: chipToMentionData(data),
13
+ interactive: true
14
+ });
15
+ }
16
+ return /*#__PURE__*/React.createElement(Invocation, {
17
+ data: chipToInvocationData(data),
18
+ interactive: true
19
+ });
20
+ }
21
+ export default ComposerInlineRef;
@@ -0,0 +1,6 @@
1
+ import type { InvocationData, MentionData, SendMeta } from '../_genui-types';
2
+ import type { ChipData } from './chip';
3
+ export declare function chipToInvocationData(chip: ChipData): InvocationData;
4
+ export declare function chipToMentionData(chip: ChipData): MentionData;
5
+ /** 从 editor chip 列表构建 SendMeta(含 invocations / mentions + chips 兼容字段)。 */
6
+ export declare function buildSendMetaFromChips(chips: ChipData[], base?: Omit<SendMeta, 'chips' | 'invocations' | 'mentions'>): SendMeta;
@@ -0,0 +1,67 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
6
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
8
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
9
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
10
+ export function chipToInvocationData(chip) {
11
+ var kind = chip.kind === 'skill' || chip.kind === 'invocation' || !chip.kind ? 'skill' : chip.kind;
12
+ return {
13
+ id: chip.id,
14
+ kind: kind,
15
+ refId: chip.skillId,
16
+ label: chip.label,
17
+ icon: chip.icon
18
+ };
19
+ }
20
+ export function chipToMentionData(chip) {
21
+ var _chip$kind;
22
+ return {
23
+ id: chip.id,
24
+ kind: (_chip$kind = chip.kind) !== null && _chip$kind !== void 0 ? _chip$kind : 'mention',
25
+ refId: chip.skillId,
26
+ label: chip.label,
27
+ icon: chip.icon
28
+ };
29
+ }
30
+ function chipToLegacyMeta(chip) {
31
+ var kind = chip.kind === 'mention' ? 'mention' : chip.kind === 'invocation' || chip.kind === 'skill' || !chip.kind ? 'skill' : chip.kind;
32
+ return {
33
+ id: chip.id,
34
+ skillId: chip.skillId,
35
+ label: chip.label,
36
+ icon: chip.icon,
37
+ kind: kind
38
+ };
39
+ }
40
+
41
+ /** 从 editor chip 列表构建 SendMeta(含 invocations / mentions + chips 兼容字段)。 */
42
+ export function buildSendMetaFromChips(chips) {
43
+ var base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
44
+ var invocations = [];
45
+ var mentions = [];
46
+ var _iterator = _createForOfIteratorHelper(chips),
47
+ _step;
48
+ try {
49
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
50
+ var chip = _step.value;
51
+ if (chip.kind === 'mention') {
52
+ mentions.push(chipToMentionData(chip));
53
+ } else {
54
+ invocations.push(chipToInvocationData(chip));
55
+ }
56
+ }
57
+ } catch (err) {
58
+ _iterator.e(err);
59
+ } finally {
60
+ _iterator.f();
61
+ }
62
+ return _objectSpread(_objectSpread({}, base), {}, {
63
+ invocations: invocations.length > 0 ? invocations : undefined,
64
+ mentions: mentions.length > 0 ? mentions : undefined,
65
+ chips: chips.length > 0 ? chips.map(chipToLegacyMeta) : undefined
66
+ });
67
+ }
@@ -219,63 +219,18 @@
219
219
  white-space: nowrap;
220
220
  }
221
221
 
222
- /* chip 视觉:与 demo 临时 CSS 一致,正式归位到 composer 命名空间 */
222
+ /* 内联引用 host:Portal 注入 Invocation / Mention,视觉由组件自带样式承担 */
223
223
  [data-odn-composer] [data-odn-composer-chip-host] {
224
- display: inline-flex;
224
+ display: inline;
225
225
  vertical-align: baseline;
226
226
  }
227
227
 
228
- [data-odn-composer] [data-odn-composer-chip] {
229
- display: inline-flex;
230
- align-items: center;
231
- gap: 4px;
232
- padding: 0 6px 0 4px;
233
- margin: 0 3px;
234
- height: 22px;
235
- border-radius: 4px;
236
- background: color-mix(in srgb, var(--odn-color-cyan-5) 12%, transparent);
237
- color: var(--odn-color-cyan-5);
238
- font-size: 13px;
239
- line-height: 1;
240
- font-weight: 500;
241
- cursor: default;
242
- user-select: none;
243
- vertical-align: -3px;
244
- }
245
-
246
- [data-odn-composer] [data-odn-composer-chip-icon],
247
- [data-odn-composer] [data-odn-composer-chip-remove] {
248
- display: inline-flex;
249
- align-items: center;
250
- justify-content: center;
251
- width: 14px;
252
- height: 14px;
253
- }
254
-
255
- [data-odn-composer] [data-odn-composer-chip-remove] {
256
- border: none;
257
- background: transparent;
258
- color: inherit;
259
- padding: 0;
260
- cursor: pointer;
261
- border-radius: 3px;
262
- display: none;
263
- }
264
-
265
- [data-odn-composer] [data-odn-composer-chip-icon] {
266
- display: inline-flex;
267
- }
268
-
269
- [data-odn-composer] [data-odn-composer-chip]:hover [data-odn-composer-chip-icon] {
270
- display: none;
271
- }
272
-
273
- [data-odn-composer] [data-odn-composer-chip]:hover [data-odn-composer-chip-remove] {
274
- display: inline-flex;
275
- }
276
-
277
- [data-odn-composer] [data-odn-composer-chip-remove]:hover {
278
- background: color-mix(in srgb, var(--odn-color-cyan-5) 18%, transparent);
228
+ /* chip 在 contentEditable=false 下无原生 selection 高亮,自绘选中态 */
229
+ [data-odn-composer] [data-odn-composer-chip-host][data-selected] [data-odn-invocation],
230
+ [data-odn-composer] [data-odn-composer-chip-host][data-selected] [data-odn-mention] {
231
+ background: color-mix(in srgb, var(--odn-color-solid-black-12) 12%, transparent);
232
+ border-radius: 0;
233
+ transition: none;
279
234
  }
280
235
 
281
236
  [data-odn-composer-toolbar] {
@@ -426,4 +381,35 @@ p[data-odn-composer-hint] {
426
381
  text-align: center;
427
382
  pointer-events: none;
428
383
  user-select: none;
384
+ }
385
+
386
+ /* ------------------------------------------------------------------
387
+ * 待发送区图片缩略
388
+ * 待发送态用模板驱动尺寸(72×72 cover),与 MessageImage 消息态的
389
+ * 数据驱动尺寸(单图 max 280×360 contain)形成对比:
390
+ * - 输入态用户聚焦于"我加了哪些",整齐划一比保留长宽比要紧
391
+ * - 模板尺寸还能让多图横排清爽不参差
392
+ * 通过给 Image leaf 容器套尺寸 + 内部 img 用 cover 实现。
393
+ * ----------------------------------------------------------------- */
394
+ [data-odn-composer-images] {
395
+ display: flex;
396
+ flex-wrap: wrap;
397
+ gap: 6px;
398
+ }
399
+
400
+ [data-odn-composer-image-thumb] {
401
+ width: 72px;
402
+ height: 72px;
403
+ border-radius: 6px;
404
+ overflow: hidden;
405
+ }
406
+ [data-odn-composer-image-thumb] [data-odn-image] {
407
+ display: block;
408
+ width: 100%;
409
+ height: 100%;
410
+ }
411
+ [data-odn-composer-image-thumb] [data-odn-image] img {
412
+ width: 100%;
413
+ height: 100%;
414
+ object-fit: cover;
429
415
  }
@@ -13,6 +13,7 @@
13
13
  export declare const ZWSP = "\u200B";
14
14
  /** Invisible Separator (U+2063):包裹 marker 的 id,业务文本不会出现。 */
15
15
  export declare const SEP = "\u2063";
16
+ export declare function stripInvisibleChars(s: string): string;
16
17
  /** 把 mention id 包成 marker token,插入 value 的 string 里。 */
17
18
  export declare function createMarker(id: string): string;
18
19
  /** 单条 marker 的解析结果。 */
@@ -32,6 +33,14 @@ export interface MarkerMatch {
32
33
  export declare function parseMarkers(raw: string): MarkerMatch[];
33
34
  /** 去掉所有 marker 的纯文本(人看的)。 */
34
35
  export declare function stripMarkers(raw: string): string;
36
+ /** 按 marker 切分 raw string,用于粘贴还原 DOM。 */
37
+ export declare function forEachMarkerSegment(raw: string, visit: (segment: {
38
+ type: 'text';
39
+ text: string;
40
+ } | {
41
+ type: 'marker';
42
+ id: string;
43
+ }) => void): void;
35
44
  /**
36
45
  * 从 contenteditable 里把所有 chip 替换成 marker,得到上层 onChange / onSend
37
46
  * 关心的 raw string。