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.
- package/dist/_genui-types.d.ts +36 -10
- package/dist/attachments/index.js +66 -16
- package/dist/attachments/style/index.css +86 -37
- package/dist/composer/chip.d.ts +4 -4
- package/dist/composer/clipboard.d.ts +12 -0
- package/dist/composer/clipboard.js +84 -0
- package/dist/composer/editor.d.ts +3 -1
- package/dist/composer/editor.js +65 -13
- package/dist/composer/hooks/useChipManager.d.ts +6 -1
- package/dist/composer/hooks/useChipManager.js +76 -27
- package/dist/composer/hooks/useChipSelectionMarker.d.ts +7 -0
- package/dist/composer/hooks/useChipSelectionMarker.js +66 -0
- package/dist/composer/index.js +73 -32
- package/dist/composer/inline-ref.d.ts +9 -0
- package/dist/composer/inline-ref.js +21 -0
- package/dist/composer/send-meta.d.ts +6 -0
- package/dist/composer/send-meta.js +67 -0
- package/dist/composer/style/index.css +39 -53
- package/dist/composer/utils.d.ts +9 -0
- package/dist/composer/utils.js +43 -2
- package/dist/fab/index.js +3 -16
- package/dist/fab/style/index.css +1 -3
- package/dist/image/index.d.ts +43 -0
- package/dist/image/index.js +51 -0
- package/dist/image/style/index.css +59 -0
- package/dist/image/style/index.d.ts +2 -0
- package/dist/image/style/index.js +2 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -0
- package/dist/invocation/index.d.ts +17 -0
- package/dist/invocation/index.js +84 -0
- package/dist/invocation/style/index.css +58 -0
- package/dist/invocation/style/index.d.ts +2 -0
- package/dist/invocation/style/index.js +2 -0
- package/dist/mention/index.d.ts +17 -0
- package/dist/mention/index.js +90 -0
- package/dist/mention/style/index.css +58 -0
- package/dist/mention/style/index.d.ts +2 -0
- package/dist/mention/style/index.js +2 -0
- package/dist/message-image/index.d.ts +42 -0
- package/dist/message-image/index.js +46 -0
- package/dist/message-image/style/index.css +60 -0
- package/dist/message-image/style/index.d.ts +2 -0
- package/dist/message-image/style/index.js +2 -0
- package/dist/user-bubble/index.d.ts +11 -1
- package/dist/user-bubble/index.js +30 -5
- package/dist/user-bubble/style/index.css +6 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/composer/index.js
CHANGED
|
@@ -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: '
|
|
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
|
-
* -
|
|
214
|
-
*
|
|
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
|
-
|
|
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: '
|
|
360
|
+
kind: 'invocation'
|
|
353
361
|
};
|
|
354
362
|
if (source === 'trigger') {
|
|
355
|
-
var _editorRef$
|
|
356
|
-
(_editorRef$
|
|
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$
|
|
360
|
-
(_editorRef$
|
|
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$
|
|
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$
|
|
420
|
+
var _editorRef$current12;
|
|
413
421
|
e.preventDefault();
|
|
414
|
-
(_editorRef$
|
|
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
|
-
*
|
|
555
|
-
*
|
|
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(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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$
|
|
668
|
-
(_editorRef$
|
|
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
|
-
/*
|
|
222
|
+
/* 内联引用 host:Portal 注入 Invocation / Mention,视觉由组件自带样式承担 */
|
|
223
223
|
[data-odn-composer] [data-odn-composer-chip-host] {
|
|
224
|
-
display: inline
|
|
224
|
+
display: inline;
|
|
225
225
|
vertical-align: baseline;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
}
|
package/dist/composer/utils.d.ts
CHANGED
|
@@ -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。
|