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
package/dist/_genui-types.d.ts
CHANGED
|
@@ -25,8 +25,16 @@ export interface Attachment {
|
|
|
25
25
|
type: string;
|
|
26
26
|
url?: string;
|
|
27
27
|
size?: number;
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* 特殊变体:
|
|
30
|
+
* - 'report':AI 生成报告的大卡片(含创建时间 + 右侧预览缩略图)。
|
|
31
|
+
* - 'thumb-card':紧凑缩略卡——左侧 32×32 缩略图(image/* + url 时为图本身,
|
|
32
|
+
* 其余类型为分类色 icon)、右侧两行(文件名 + 类型标签)。适合长聊天流回看 /
|
|
33
|
+
* 引用 / 有正文 + 多图列表等"占位省、识别强"的场景。与默认的「图片大预览
|
|
34
|
+
* imageCardInner」并列,由调用方按上下文选择。
|
|
35
|
+
* - 未传:走普通文件卡片(image/* + url 自动展开为内嵌大预览 imageCardInner)。
|
|
36
|
+
*/
|
|
37
|
+
variant?: 'report' | 'thumb-card';
|
|
30
38
|
/** variant='report' 时展示在标题下的创建时间,例如 "03-09 15:46"。 */
|
|
31
39
|
createdAt?: string;
|
|
32
40
|
/** variant='report' 时右侧预览缩略图地址。未传时渲染为浅色占位层。 */
|
|
@@ -46,6 +54,25 @@ export interface PreviewTab {
|
|
|
46
54
|
content: string;
|
|
47
55
|
type: string;
|
|
48
56
|
}
|
|
57
|
+
/** Composer 正文内 `/` 触发的内联引用(Invocation 组件数据契约)。 */
|
|
58
|
+
export interface InvocationData {
|
|
59
|
+
id: string;
|
|
60
|
+
/** 业务类型:skill / command / 自定义 */
|
|
61
|
+
kind: string;
|
|
62
|
+
refId: string;
|
|
63
|
+
label: string;
|
|
64
|
+
icon?: string;
|
|
65
|
+
params?: Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
/** Composer 正文内 `@` 触发的内联引用(Mention 组件数据契约)。 */
|
|
68
|
+
export interface MentionData {
|
|
69
|
+
id: string;
|
|
70
|
+
kind: string;
|
|
71
|
+
refId: string;
|
|
72
|
+
label: string;
|
|
73
|
+
icon?: string;
|
|
74
|
+
params?: Record<string, unknown>;
|
|
75
|
+
}
|
|
49
76
|
export interface SkillItem {
|
|
50
77
|
id: string;
|
|
51
78
|
label: string;
|
|
@@ -61,14 +88,13 @@ export interface SkillItem {
|
|
|
61
88
|
export interface SendMeta {
|
|
62
89
|
attachments?: Attachment[];
|
|
63
90
|
webSearch?: boolean;
|
|
91
|
+
/** `/` 内联引用列表(Invocation),按编辑器内出现顺序。 */
|
|
92
|
+
invocations?: InvocationData[];
|
|
93
|
+
/** `@` 内联引用列表(Mention),按编辑器内出现顺序。 */
|
|
94
|
+
mentions?: MentionData[];
|
|
64
95
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* - 通过 `kind` 字段区分 skill / mention / 自定义类型
|
|
68
|
-
* - 业务方按需 filter,例如:`meta.chips?.filter(c => c.kind === 'skill')`
|
|
69
|
-
*
|
|
70
|
-
* BREAKING(pre-1.0):取代 v0.0.x 的 `skill?: string` 单值字段。
|
|
71
|
-
* 老业务迁移:`meta.skill` → `meta.chips?.find(c => c.kind === 'skill')?.label`
|
|
96
|
+
* @deprecated 请改用 `invocations` / `mentions`。本字段仍双写一版,便于旧业务迁移。
|
|
97
|
+
* 编辑器内 chip 列表(按出现顺序);`kind: 'skill'` 对应 invocation。
|
|
72
98
|
*/
|
|
73
99
|
chips?: ComposerChipMeta[];
|
|
74
100
|
}
|
|
@@ -78,7 +104,7 @@ export interface ComposerChipMeta {
|
|
|
78
104
|
skillId: string;
|
|
79
105
|
label: string;
|
|
80
106
|
icon: string;
|
|
81
|
-
kind?: 'skill' | 'mention' | (string & {});
|
|
107
|
+
kind?: 'skill' | 'invocation' | 'mention' | (string & {});
|
|
82
108
|
}
|
|
83
109
|
export type AgentEvent = {
|
|
84
110
|
kind: 'thinking';
|
|
@@ -131,10 +131,68 @@ export function Attachments(_ref) {
|
|
|
131
131
|
_ref$compact = _ref.compact,
|
|
132
132
|
compact = _ref$compact === void 0 ? false : _ref$compact;
|
|
133
133
|
if (attachments.length === 0) return null;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* thumb-card 渲染:紧凑卡(左缩略图 + 右两行文件名/类型)。
|
|
137
|
+
*
|
|
138
|
+
* 抽成组件内部 helper 而非内联,是为了让 compact 分支与 default 分支共用同一份
|
|
139
|
+
* 渲染逻辑——variant 是「调用方显式 opt-in」的信号,必须优先于 compact 隐式推导:
|
|
140
|
+
* 「有正文 + 附件」场景(compact=true)下,调用方传 thumb-card 就是不想让附件
|
|
141
|
+
* 退化成 chip,而想要"小卡片 + 缩略图"承担引用语义。
|
|
142
|
+
*/
|
|
143
|
+
function renderThumbCard(a, i) {
|
|
144
|
+
var meta = getFileMeta(a.type);
|
|
145
|
+
var isImage = a.type.startsWith('image/') && !!a.url;
|
|
146
|
+
var thumbCardInner = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
147
|
+
"data-odn-attachments-thumb-card-thumb": true,
|
|
148
|
+
style: isImage ? undefined : {
|
|
149
|
+
backgroundColor: meta.bg
|
|
150
|
+
}
|
|
151
|
+
}, isImage ? /*#__PURE__*/React.createElement("img", {
|
|
152
|
+
src: a.url,
|
|
153
|
+
alt: ""
|
|
154
|
+
}) : /*#__PURE__*/React.createElement(Icon, {
|
|
155
|
+
name: meta.icon,
|
|
156
|
+
size: 16,
|
|
157
|
+
style: {
|
|
158
|
+
color: meta.color
|
|
159
|
+
}
|
|
160
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
161
|
+
"data-odn-attachments-thumb-card-info": true
|
|
162
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
163
|
+
"data-odn-attachments-thumb-card-name": true
|
|
164
|
+
}, a.name), /*#__PURE__*/React.createElement("div", {
|
|
165
|
+
"data-odn-attachments-thumb-card-type": true
|
|
166
|
+
}, formatType(a.type, a.name))));
|
|
167
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
168
|
+
key: i,
|
|
169
|
+
"data-odn-attachments-item": true,
|
|
170
|
+
"data-odn-attachments-item-thumb-card": ""
|
|
171
|
+
}, onFileClick ? /*#__PURE__*/React.createElement("button", {
|
|
172
|
+
type: "button",
|
|
173
|
+
onClick: function onClick() {
|
|
174
|
+
return onFileClick(a);
|
|
175
|
+
},
|
|
176
|
+
"data-odn-attachments-thumb-card": true,
|
|
177
|
+
"data-odn-attachments-clickable": ""
|
|
178
|
+
}, thumbCardInner) : /*#__PURE__*/React.createElement("div", {
|
|
179
|
+
"data-odn-attachments-thumb-card": true
|
|
180
|
+
}, thumbCardInner), onRemove && /*#__PURE__*/React.createElement("button", {
|
|
181
|
+
onClick: function onClick() {
|
|
182
|
+
return onRemove(i);
|
|
183
|
+
},
|
|
184
|
+
"data-odn-attachments-remove": true
|
|
185
|
+
}, /*#__PURE__*/React.createElement(Icon, {
|
|
186
|
+
name: "x",
|
|
187
|
+
size: 10
|
|
188
|
+
})));
|
|
189
|
+
}
|
|
134
190
|
if (compact) {
|
|
135
191
|
return /*#__PURE__*/React.createElement("div", {
|
|
136
192
|
"data-odn-attachments-compact": true
|
|
137
193
|
}, attachments.map(function (a, i) {
|
|
194
|
+
// thumb-card 优先于 chip 默认路径——见上方 renderThumbCard 注释。
|
|
195
|
+
if (a.variant === 'thumb-card') return renderThumbCard(a, i);
|
|
138
196
|
var meta = getFileMeta(a.type);
|
|
139
197
|
var chip = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Icon, {
|
|
140
198
|
name: meta.icon,
|
|
@@ -252,6 +310,7 @@ export function Attachments(_ref) {
|
|
|
252
310
|
size: 10
|
|
253
311
|
})));
|
|
254
312
|
}
|
|
313
|
+
if (a.variant === 'thumb-card') return renderThumbCard(a, i);
|
|
255
314
|
var meta = getFileMeta(a.type);
|
|
256
315
|
var isImage = a.type.startsWith('image/') && a.url;
|
|
257
316
|
var fileCardInner = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
@@ -272,30 +331,21 @@ export function Attachments(_ref) {
|
|
|
272
331
|
}, a.name), /*#__PURE__*/React.createElement("div", {
|
|
273
332
|
"data-odn-attachments-meta": true
|
|
274
333
|
}, formatType(a.type, a.name), a.size ? " \xB7 ".concat(formatSize(a.size)) : '')));
|
|
275
|
-
|
|
276
|
-
"data-odn-attachments-img-wrapper": true
|
|
277
|
-
}, /*#__PURE__*/React.createElement("img", {
|
|
278
|
-
src: a.url,
|
|
279
|
-
alt: a.name
|
|
280
|
-
}));
|
|
281
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
334
|
+
return /*#__PURE__*/React.createElement("div", _extends({
|
|
282
335
|
key: i,
|
|
283
336
|
"data-odn-attachments-item": true
|
|
284
|
-
},
|
|
337
|
+
}, isImage ? {
|
|
338
|
+
'data-odn-attachments-item-image-card': ''
|
|
339
|
+
} : {}), onFileClick ? /*#__PURE__*/React.createElement("button", {
|
|
285
340
|
type: "button",
|
|
286
341
|
onClick: function onClick() {
|
|
287
342
|
return onFileClick(a);
|
|
288
343
|
},
|
|
289
|
-
"data-odn-attachments-card": true
|
|
290
|
-
}, isImage ? {
|
|
291
|
-
'data-odn-attachments-image-card': ''
|
|
292
|
-
} : {}, {
|
|
344
|
+
"data-odn-attachments-card": true,
|
|
293
345
|
"data-odn-attachments-clickable": ""
|
|
294
|
-
}
|
|
346
|
+
}, fileCardInner) : /*#__PURE__*/React.createElement("div", {
|
|
295
347
|
"data-odn-attachments-card": true
|
|
296
|
-
},
|
|
297
|
-
'data-odn-attachments-image-card': ''
|
|
298
|
-
} : {}), isImage ? imageCardInner : fileCardInner), onRemove && /*#__PURE__*/React.createElement("button", {
|
|
348
|
+
}, fileCardInner), onRemove && /*#__PURE__*/React.createElement("button", {
|
|
299
349
|
onClick: function onClick() {
|
|
300
350
|
return onRemove(i);
|
|
301
351
|
},
|
|
@@ -84,43 +84,6 @@
|
|
|
84
84
|
border-color: color-mix(in srgb, var(--odn-color-primary) 50%, transparent);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
[data-odn-attachments-img-wrapper] {
|
|
88
|
-
height: 0;
|
|
89
|
-
min-height: 100%;
|
|
90
|
-
aspect-ratio: 1;
|
|
91
|
-
border-radius: 3px;
|
|
92
|
-
overflow: hidden;
|
|
93
|
-
align-self: center;
|
|
94
|
-
}
|
|
95
|
-
[data-odn-attachments-img-wrapper] img {
|
|
96
|
-
width: 100%;
|
|
97
|
-
height: 100%;
|
|
98
|
-
object-fit: cover;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** 图片附件卡:仅内嵌预览(非「文件卡」双列),须写在通用 img-wrapper 规则之后以覆盖 grid 方形缩略策略 */
|
|
102
|
-
[data-odn-attachments-card][data-odn-attachments-image-card] {
|
|
103
|
-
display: block;
|
|
104
|
-
padding: 4px;
|
|
105
|
-
max-width: min(100%, 280px);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
[data-odn-attachments-card][data-odn-attachments-image-card] [data-odn-attachments-img-wrapper] {
|
|
109
|
-
height: auto;
|
|
110
|
-
min-height: 0;
|
|
111
|
-
aspect-ratio: auto;
|
|
112
|
-
max-height: 220px;
|
|
113
|
-
border-radius: 4px;
|
|
114
|
-
align-self: stretch;
|
|
115
|
-
}
|
|
116
|
-
[data-odn-attachments-card][data-odn-attachments-image-card] [data-odn-attachments-img-wrapper] img {
|
|
117
|
-
width: 100%;
|
|
118
|
-
height: auto;
|
|
119
|
-
max-height: 220px;
|
|
120
|
-
object-fit: contain;
|
|
121
|
-
vertical-align: middle;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
87
|
[data-odn-attachments-file-icon] {
|
|
125
88
|
display: flex;
|
|
126
89
|
align-items: center;
|
|
@@ -175,6 +138,92 @@
|
|
|
175
138
|
color: var(--odn-color-error);
|
|
176
139
|
}
|
|
177
140
|
|
|
141
|
+
/* ==========================================================================
|
|
142
|
+
* Thumb-card 变体(紧凑缩略卡)
|
|
143
|
+
* 左侧缩略图(image/* + url 时为图本身;其余类型为分类色 icon),
|
|
144
|
+
* 右侧两行(文件名 + 类型标签)。
|
|
145
|
+
*
|
|
146
|
+
* 节奏与默认「文件附件卡」(data-odn-attachments-card) 严格对齐:同样的
|
|
147
|
+
* padding / border-radius / border / 字号,仅 icon 槽内容由「彩色 icon」
|
|
148
|
+
* 替换为「图片缩略 or 彩色 icon」——保证 thumb-card 与 file-card 同行 wrap
|
|
149
|
+
* 时高度 / 圆角 / 边线一致,不会出现错位。
|
|
150
|
+
* ========================================================================== */
|
|
151
|
+
[data-odn-attachments-item-thumb-card] {
|
|
152
|
+
display: inline-flex;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
[data-odn-attachments-thumb-card] {
|
|
156
|
+
display: grid;
|
|
157
|
+
grid-template-columns: auto 1fr;
|
|
158
|
+
gap: 8px;
|
|
159
|
+
padding: 6px 10px 6px 6px;
|
|
160
|
+
border-radius: 6px;
|
|
161
|
+
border: 1px solid var(--odn-color-black-6);
|
|
162
|
+
background: var(--odn-color-solid-black-1);
|
|
163
|
+
box-sizing: border-box;
|
|
164
|
+
font: inherit;
|
|
165
|
+
color: inherit;
|
|
166
|
+
text-align: left;
|
|
167
|
+
appearance: none;
|
|
168
|
+
transition: border-color 0.15s;
|
|
169
|
+
}
|
|
170
|
+
[data-odn-attachments-thumb-card][data-odn-attachments-clickable] {
|
|
171
|
+
cursor: pointer;
|
|
172
|
+
}
|
|
173
|
+
[data-odn-attachments-thumb-card][data-odn-attachments-clickable]:hover {
|
|
174
|
+
border-color: color-mix(in srgb, var(--odn-color-primary) 50%, transparent);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
[data-odn-attachments-thumb-card-thumb] {
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
justify-content: center;
|
|
181
|
+
height: 0;
|
|
182
|
+
min-height: 100%;
|
|
183
|
+
aspect-ratio: 1;
|
|
184
|
+
border-radius: 3px;
|
|
185
|
+
overflow: hidden;
|
|
186
|
+
}
|
|
187
|
+
[data-odn-attachments-thumb-card-thumb] img {
|
|
188
|
+
width: 100%;
|
|
189
|
+
height: 100%;
|
|
190
|
+
object-fit: cover;
|
|
191
|
+
display: block;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
[data-odn-attachments-thumb-card-info] {
|
|
195
|
+
min-width: 0;
|
|
196
|
+
display: flex;
|
|
197
|
+
flex-direction: column;
|
|
198
|
+
justify-content: center;
|
|
199
|
+
gap: 2px;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
[data-odn-attachments-thumb-card-name] {
|
|
203
|
+
font-size: 12px;
|
|
204
|
+
line-height: normal;
|
|
205
|
+
color: var(--odn-color-black-12);
|
|
206
|
+
overflow: hidden;
|
|
207
|
+
text-overflow: ellipsis;
|
|
208
|
+
white-space: nowrap;
|
|
209
|
+
max-width: 120px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
[data-odn-attachments-thumb-card-type] {
|
|
213
|
+
font-size: 10px;
|
|
214
|
+
line-height: 1.25;
|
|
215
|
+
color: var(--odn-color-black-9);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
[data-odn-attachments-item-image-card] {
|
|
219
|
+
flex-basis: 100%;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
[data-odn-attachments-card]:not([data-odn-attachments-image-card]),
|
|
223
|
+
[data-odn-attachments-thumb-card] {
|
|
224
|
+
width: 192px;
|
|
225
|
+
}
|
|
226
|
+
|
|
178
227
|
/* ==========================================================================
|
|
179
228
|
* Report 变体(AI 生成报告入口卡片)
|
|
180
229
|
* 设计对齐:豆包报告卡片 —— 左侧图标 + 标题/创建时间,右侧预览缩略图,
|
package/dist/composer/chip.d.ts
CHANGED
|
@@ -22,11 +22,11 @@ export interface ChipData {
|
|
|
22
22
|
/**
|
|
23
23
|
* chip 语义来源标记,业务方自定义。Composer 内部不消费 kind,
|
|
24
24
|
* 仅原样透传到 onSend(meta.chips),方便业务方按 kind 分类。
|
|
25
|
-
* 约定:'
|
|
26
|
-
* 'mention' = `@`
|
|
27
|
-
*
|
|
25
|
+
* 约定:'invocation' = 工具栏按钮 / `/` 触发(SendMeta.invocations);
|
|
26
|
+
* 'mention' = `@` 触发(SendMeta.mentions);
|
|
27
|
+
* 'skill' 为兼容别名,等同 invocation。
|
|
28
28
|
*/
|
|
29
|
-
kind?: '
|
|
29
|
+
kind?: 'invocation' | 'mention' | 'skill' | (string & {});
|
|
30
30
|
}
|
|
31
31
|
export interface ChipProps {
|
|
32
32
|
data: ChipData;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChipData } from './chip';
|
|
2
|
+
import type { ManagedChip } from './hooks/useChipManager';
|
|
3
|
+
export declare const COMPOSER_CLIPBOARD_MIME = "application/x-odn-composer-chips";
|
|
4
|
+
export interface ComposerClipboardPayload {
|
|
5
|
+
value: string;
|
|
6
|
+
chips: ChipData[];
|
|
7
|
+
}
|
|
8
|
+
export declare function buildClipboardPayload(range: Range, chips: ManagedChip[]): ComposerClipboardPayload | null;
|
|
9
|
+
export declare function parseClipboardPayload(raw: string): ComposerClipboardPayload | null;
|
|
10
|
+
/** 粘贴时重映射 chip id,避免同编辑器内 id 冲突。 */
|
|
11
|
+
export declare function remapClipboardPayload(payload: ComposerClipboardPayload): ComposerClipboardPayload;
|
|
12
|
+
export declare function plainTextFromClipboardPayload(payload: ComposerClipboardPayload): string;
|
|
@@ -0,0 +1,84 @@
|
|
|
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 _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
3
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
4
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
5
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
6
|
+
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; }
|
|
7
|
+
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; }
|
|
8
|
+
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; }
|
|
9
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
10
|
+
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); }
|
|
11
|
+
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; } } }; }
|
|
12
|
+
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); }
|
|
13
|
+
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; }
|
|
14
|
+
import { createMarker, getPlainText, stripMarkers } from "./utils";
|
|
15
|
+
export var COMPOSER_CLIPBOARD_MIME = 'application/x-odn-composer-chips';
|
|
16
|
+
export function buildClipboardPayload(range, chips) {
|
|
17
|
+
var fragment = range.cloneContents();
|
|
18
|
+
var wrapper = document.createElement('div');
|
|
19
|
+
wrapper.appendChild(fragment);
|
|
20
|
+
var value = getPlainText(wrapper);
|
|
21
|
+
var selected = [];
|
|
22
|
+
var _iterator = _createForOfIteratorHelper(chips),
|
|
23
|
+
_step;
|
|
24
|
+
try {
|
|
25
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
26
|
+
var chip = _step.value;
|
|
27
|
+
if (range.intersectsNode(chip.host)) {
|
|
28
|
+
selected.push(chip.data);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
_iterator.e(err);
|
|
33
|
+
} finally {
|
|
34
|
+
_iterator.f();
|
|
35
|
+
}
|
|
36
|
+
if (!value && selected.length === 0) return null;
|
|
37
|
+
return {
|
|
38
|
+
value: value,
|
|
39
|
+
chips: selected
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function parseClipboardPayload(raw) {
|
|
43
|
+
try {
|
|
44
|
+
var parsed = JSON.parse(raw);
|
|
45
|
+
if (typeof parsed.value !== 'string' || !Array.isArray(parsed.chips)) return null;
|
|
46
|
+
return parsed;
|
|
47
|
+
} catch (_unused) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 粘贴时重映射 chip id,避免同编辑器内 id 冲突。 */
|
|
53
|
+
export function remapClipboardPayload(payload) {
|
|
54
|
+
var idMap = new Map();
|
|
55
|
+
var chips = payload.chips.map(function (chip) {
|
|
56
|
+
var newId = "c_".concat(Date.now(), "_").concat(Math.random().toString(36).slice(2, 8));
|
|
57
|
+
idMap.set(chip.id, newId);
|
|
58
|
+
return _objectSpread(_objectSpread({}, chip), {}, {
|
|
59
|
+
id: newId
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
var value = payload.value;
|
|
63
|
+
var _iterator2 = _createForOfIteratorHelper(idMap),
|
|
64
|
+
_step2;
|
|
65
|
+
try {
|
|
66
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
67
|
+
var _step2$value = _slicedToArray(_step2.value, 2),
|
|
68
|
+
oldId = _step2$value[0],
|
|
69
|
+
newId = _step2$value[1];
|
|
70
|
+
value = value.split(createMarker(oldId)).join(createMarker(newId));
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {
|
|
73
|
+
_iterator2.e(err);
|
|
74
|
+
} finally {
|
|
75
|
+
_iterator2.f();
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
value: value,
|
|
79
|
+
chips: chips
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function plainTextFromClipboardPayload(payload) {
|
|
83
|
+
return stripMarkers(payload.value);
|
|
84
|
+
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* - chip 状态(id ↔ host)由 useChipManager 维护
|
|
18
18
|
*/
|
|
19
19
|
/// <reference types="react" />
|
|
20
|
-
import {
|
|
20
|
+
import type { ChipData } from './chip';
|
|
21
21
|
/** Composer editor 触发态对外信号——告诉父组件该弹浮层选 skill 了。 */
|
|
22
22
|
export interface ComposerEditorTriggerInfo {
|
|
23
23
|
/** 触发字符(@ 或 /) */
|
|
@@ -86,6 +86,8 @@ export interface ComposerEditorProps {
|
|
|
86
86
|
* 若提供且 `files.length > 0`,编辑器会 `preventDefault` 且不再插入纯文本。
|
|
87
87
|
*/
|
|
88
88
|
onPasteFiles?: (files: File[]) => void;
|
|
89
|
+
/** 复制 / 剪切时回调(选区含 chip 时触发)。 */
|
|
90
|
+
onCopyChips?: (chips: ChipData[]) => void;
|
|
89
91
|
/**
|
|
90
92
|
* 禁用:contenteditable 关闭,editor 不接受输入 / 聚焦 / 粘贴。
|
|
91
93
|
* 已有的文本与 chip 仍渲染(仅冻结,不清空),便于"等接通后继续编辑"。
|
package/dist/composer/editor.js
CHANGED
|
@@ -31,9 +31,11 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
|
31
31
|
|
|
32
32
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from 'react';
|
|
33
33
|
import { createPortal } from 'react-dom';
|
|
34
|
-
import {
|
|
34
|
+
import { buildClipboardPayload, COMPOSER_CLIPBOARD_MIME, parseClipboardPayload, plainTextFromClipboardPayload, remapClipboardPayload } from "./clipboard";
|
|
35
35
|
import { useChipManager } from "./hooks/useChipManager";
|
|
36
|
-
import {
|
|
36
|
+
import { useChipSelectionMarker } from "./hooks/useChipSelectionMarker";
|
|
37
|
+
import { findChipHostBeforeCaret, getPlainText, isEditorEmpty, probeTrigger, resolveCaretJumpAroundChip, ZWSP } from "./utils";
|
|
38
|
+
import { ComposerInlineRef } from "./inline-ref";
|
|
37
39
|
import ScrollArea from "../scroll-area";
|
|
38
40
|
|
|
39
41
|
/** Composer editor 触发态对外信号——告诉父组件该弹浮层选 skill 了。 */
|
|
@@ -99,6 +101,7 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
99
101
|
onTriggerKeyDown = _ref.onTriggerKeyDown,
|
|
100
102
|
onHeightChange = _ref.onHeightChange,
|
|
101
103
|
onPasteFiles = _ref.onPasteFiles,
|
|
104
|
+
onCopyChips = _ref.onCopyChips,
|
|
102
105
|
_ref$disabled = _ref.disabled,
|
|
103
106
|
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
|
|
104
107
|
className = _ref.className,
|
|
@@ -184,13 +187,15 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
184
187
|
* ───────────────────────────────────── */
|
|
185
188
|
var _useChipManager = useChipManager(function () {
|
|
186
189
|
fireChange();
|
|
187
|
-
// 插入 / 删除 chip 后 caret 位置变了,重算 trigger
|
|
188
190
|
recomputeTrigger();
|
|
189
191
|
}),
|
|
190
192
|
chips = _useChipManager.chips,
|
|
191
193
|
_insertChip = _useChipManager.insertChip,
|
|
194
|
+
insertSerializedAtRange = _useChipManager.insertSerializedAtRange,
|
|
195
|
+
syncChipsAfterDomMutation = _useChipManager.syncChipsAfterDomMutation,
|
|
192
196
|
removeChip = _useChipManager.removeChip,
|
|
193
197
|
resetChips = _useChipManager.resetChips;
|
|
198
|
+
useChipSelectionMarker(editorRef, chips);
|
|
194
199
|
|
|
195
200
|
/* ─────────────────────────────────────
|
|
196
201
|
* Trigger 探测
|
|
@@ -485,10 +490,45 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
485
490
|
insertLineBreak();
|
|
486
491
|
}
|
|
487
492
|
}, [onPressEnter, insertLineBreak, removeChip, onTriggerKeyDown]);
|
|
493
|
+
var writeClipboardPayload = useCallback(function (e, payload) {
|
|
494
|
+
if (!payload) return false;
|
|
495
|
+
e.clipboardData.setData(COMPOSER_CLIPBOARD_MIME, JSON.stringify(payload));
|
|
496
|
+
e.clipboardData.setData('text/plain', plainTextFromClipboardPayload(payload));
|
|
497
|
+
onCopyChips === null || onCopyChips === void 0 || onCopyChips(payload.chips);
|
|
498
|
+
return true;
|
|
499
|
+
}, [onCopyChips]);
|
|
500
|
+
var handleCopy = useCallback(function (e) {
|
|
501
|
+
var el = editorRef.current;
|
|
502
|
+
if (!el || disabled) return;
|
|
503
|
+
var sel = window.getSelection();
|
|
504
|
+
if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
|
|
505
|
+
var range = sel.getRangeAt(0);
|
|
506
|
+
if (!el.contains(range.commonAncestorContainer)) return;
|
|
507
|
+
var payload = buildClipboardPayload(range, chips);
|
|
508
|
+
if (!payload) return;
|
|
509
|
+
e.preventDefault();
|
|
510
|
+
writeClipboardPayload(e, payload);
|
|
511
|
+
}, [chips, disabled, writeClipboardPayload]);
|
|
512
|
+
var handleCut = useCallback(function (e) {
|
|
513
|
+
var el = editorRef.current;
|
|
514
|
+
if (!el || disabled) return;
|
|
515
|
+
var sel = window.getSelection();
|
|
516
|
+
if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
|
|
517
|
+
var range = sel.getRangeAt(0);
|
|
518
|
+
if (!el.contains(range.commonAncestorContainer)) return;
|
|
519
|
+
var payload = buildClipboardPayload(range, chips);
|
|
520
|
+
if (!payload) return;
|
|
521
|
+
e.preventDefault();
|
|
522
|
+
if (!writeClipboardPayload(e, payload)) return;
|
|
523
|
+
range.deleteContents();
|
|
524
|
+
syncChipsAfterDomMutation(el);
|
|
525
|
+
fireChange();
|
|
526
|
+
recomputeTrigger();
|
|
527
|
+
requestScrollCaretIntoView();
|
|
528
|
+
}, [chips, disabled, writeClipboardPayload, syncChipsAfterDomMutation, fireChange, recomputeTrigger, requestScrollCaretIntoView]);
|
|
488
529
|
|
|
489
530
|
/* ─────────────────────────────────────
|
|
490
|
-
* Paste
|
|
491
|
-
* 避免外部富文本污染 DOM;无 handler 时文件粘贴仍 preventDefault 以免整页乱入。
|
|
531
|
+
* Paste:优先私有 mime 还原 chip;纯文本走受控插入;文件交给 onPasteFiles。
|
|
492
532
|
* ───────────────────────────────────── */
|
|
493
533
|
var handlePaste = useCallback(function (e) {
|
|
494
534
|
var fileList = e.clipboardData.files;
|
|
@@ -514,13 +554,26 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
514
554
|
onPasteFiles === null || onPasteFiles === void 0 || onPasteFiles(fromItems);
|
|
515
555
|
return;
|
|
516
556
|
}
|
|
557
|
+
var el = editorRef.current;
|
|
558
|
+
if (!el) return;
|
|
559
|
+
var rawPayload = e.clipboardData.getData(COMPOSER_CLIPBOARD_MIME);
|
|
560
|
+
var parsed = rawPayload ? parseClipboardPayload(rawPayload) : null;
|
|
517
561
|
e.preventDefault();
|
|
518
|
-
var text = e.clipboardData.getData('text/plain');
|
|
519
|
-
if (!text) return;
|
|
520
562
|
var sel = window.getSelection();
|
|
521
563
|
if (!sel || sel.rangeCount === 0) return;
|
|
522
564
|
var range = sel.getRangeAt(0);
|
|
565
|
+
if (!el.contains(range.commonAncestorContainer)) return;
|
|
523
566
|
range.deleteContents();
|
|
567
|
+
if (parsed) {
|
|
568
|
+
var remapped = remapClipboardPayload(parsed);
|
|
569
|
+
insertSerializedAtRange(remapped.value, remapped.chips, range, el);
|
|
570
|
+
fireChange();
|
|
571
|
+
recomputeTrigger();
|
|
572
|
+
requestScrollCaretIntoView();
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
var text = e.clipboardData.getData('text/plain');
|
|
576
|
+
if (!text) return;
|
|
524
577
|
var node = document.createTextNode(text);
|
|
525
578
|
range.insertNode(node);
|
|
526
579
|
var newRange = document.createRange();
|
|
@@ -530,7 +583,7 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
530
583
|
sel.addRange(newRange);
|
|
531
584
|
fireChange();
|
|
532
585
|
requestScrollCaretIntoView();
|
|
533
|
-
}, [fireChange, requestScrollCaretIntoView, onPasteFiles]);
|
|
586
|
+
}, [fireChange, requestScrollCaretIntoView, onPasteFiles, insertSerializedAtRange, recomputeTrigger]);
|
|
534
587
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ScrollArea, {
|
|
535
588
|
controlRef: scrollRef,
|
|
536
589
|
autoHide: true,
|
|
@@ -554,13 +607,12 @@ export var ComposerEditor = /*#__PURE__*/forwardRef(function ComposerEditor(_ref
|
|
|
554
607
|
onCompositionStart: handleCompositionStart,
|
|
555
608
|
onCompositionEnd: handleCompositionEnd,
|
|
556
609
|
onKeyDown: handleKeyDown,
|
|
610
|
+
onCopy: handleCopy,
|
|
611
|
+
onCut: handleCut,
|
|
557
612
|
onPaste: handlePaste
|
|
558
613
|
})), chips.map(function (chip) {
|
|
559
|
-
return /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement(
|
|
560
|
-
data: chip.data
|
|
561
|
-
onRemove: function onRemove() {
|
|
562
|
-
return removeChip(chip.data.id);
|
|
563
|
-
}
|
|
614
|
+
return /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement(ComposerInlineRef, {
|
|
615
|
+
data: chip.data
|
|
564
616
|
}), chip.host, chip.data.id);
|
|
565
617
|
}));
|
|
566
618
|
});
|
|
@@ -24,9 +24,14 @@ export interface UseChipManagerReturn {
|
|
|
24
24
|
* 在编辑器当前光标位置插入 chip:
|
|
25
25
|
* - selection 在编辑器内 → 插入光标处
|
|
26
26
|
* - selection 在编辑器外 / 无 selection → fallback 到末尾
|
|
27
|
-
* 插入后光标自动移到 chip 之后的 ZWSP 锚点之后,方便用户继续输入。
|
|
28
27
|
*/
|
|
29
28
|
insertChip: (data: ChipData, editor: HTMLElement) => void;
|
|
29
|
+
/** 在指定 range 处插入 chip(调用方负责 deleteContents)。 */
|
|
30
|
+
insertChipAtRange: (data: ChipData, range: Range, editor: HTMLElement) => HTMLSpanElement;
|
|
31
|
+
/** 将含 marker 的 raw value + chips 插入 range(粘贴用)。 */
|
|
32
|
+
insertSerializedAtRange: (value: string, chips: ChipData[], range: Range, editor: HTMLElement) => void;
|
|
33
|
+
/** 移除已从 DOM 摘下的 chip 状态(cut / 选区删除后)。 */
|
|
34
|
+
syncChipsAfterDomMutation: (editor: HTMLElement) => void;
|
|
30
35
|
/** 按 chip id 移除(同时清理伴随的 ZWSP 锚点)。 */
|
|
31
36
|
removeChip: (id: string) => void;
|
|
32
37
|
/** 清空所有 chip 的 React 状态(DOM 由调用方 editor.innerHTML='' 一并处理)。 */
|