one-design-next 0.0.32 → 0.0.34

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.
@@ -25,6 +25,16 @@ export interface Attachment {
25
25
  type: string;
26
26
  url?: string;
27
27
  size?: number;
28
+ /**
29
+ * 附件状态:
30
+ * - uploading: 上传中(发送按钮应禁用)
31
+ * - error: 上传失败(可重试)
32
+ * - expired: 已过期(消息流只读态)
33
+ * - done: 上传完成(默认)
34
+ */
35
+ status?: 'uploading' | 'error' | 'expired' | 'done';
36
+ /** status='error' 时展示的失败原因文案。 */
37
+ errorMessage?: string;
28
38
  /**
29
39
  * 特殊变体:
30
40
  * - 'report':AI 生成报告的大卡片(含创建时间 + 右侧预览缩略图)。
@@ -32,9 +42,13 @@ export interface Attachment {
32
42
  * 其余类型为分类色 icon)、右侧两行(文件名 + 类型标签)。适合长聊天流回看 /
33
43
  * 引用 / 有正文 + 多图列表等"占位省、识别强"的场景。与默认的「图片大预览
34
44
  * imageCardInner」并列,由调用方按上下文选择。
45
+ * - 'image-thumb':纯图大缩略方块(56×56 cover、无边框),承载 image/* + url。
46
+ * 与文件信息卡共享同一套三态机(uploading/error/expired)、remove 角标与
47
+ * fresh-done 行为——composer 待发送区即以此变体让"图片大预览 + 文件信息卡"
48
+ * 并列且同源,不再各写一套。
35
49
  * - 未传:走普通文件卡片(image/* + url 自动展开为内嵌大预览 imageCardInner)。
36
50
  */
37
- variant?: 'report' | 'thumb-card';
51
+ variant?: 'report' | 'thumb-card' | 'image-thumb';
38
52
  /** variant='report' 时展示在标题下的创建时间,例如 "03-09 15:46"。 */
39
53
  createdAt?: string;
40
54
  /** variant='report' 时右侧预览缩略图地址。未传时渲染为浅色占位层。 */
@@ -186,7 +200,7 @@ export interface Conversation {
186
200
  title: string;
187
201
  preview?: string;
188
202
  timestamp?: number;
189
- status?: 'generating' | 'unread';
203
+ status?: 'generating' | 'unread' | 'error';
190
204
  }
191
205
  export interface MessageSnapshot {
192
206
  content: string;
@@ -9,8 +9,6 @@ import { useState, useEffect, useRef, useCallback } from 'react';
9
9
  import { AnimatePresence, motion } from 'motion/react';
10
10
  import HoverFill from "../hover-fill";
11
11
  import Icon from "../icon";
12
- import ThinkingIcon from "./ThinkingIcon";
13
- import ThinkingDoneIcon from "./ThinkingDoneIcon";
14
12
  import "./style";
15
13
  export function AgentThink(_ref) {
16
14
  var content = _ref.content,
@@ -117,19 +115,13 @@ function AgentThinkBlock(_ref2) {
117
115
  }, isThinking && expanded ? /*#__PURE__*/React.createElement("span", {
118
116
  "data-odn-agent-think-trigger": true,
119
117
  "data-odn-agent-think-trigger-active": true
120
- }, icon !== null && icon !== void 0 ? icon : /*#__PURE__*/React.createElement(ThinkingIcon, {
121
- size: 18,
122
- "data-odn-agent-think-icon-active": true
123
- }), /*#__PURE__*/React.createElement("span", null, "\u601D\u8003\u4E2D\u2026", timeLabel)) : /*#__PURE__*/React.createElement(HoverFill, {
118
+ }, icon, /*#__PURE__*/React.createElement("span", null, "\u601D\u8003\u4E2D\u2026", timeLabel)) : /*#__PURE__*/React.createElement(HoverFill, {
124
119
  "data-odn-agent-think-trigger-wrap": true,
125
120
  bgClassName: "odn-agent-think-trigger-bg"
126
121
  }, /*#__PURE__*/React.createElement("button", {
127
122
  onClick: handleToggle,
128
123
  "data-odn-agent-think-trigger": true
129
- }, /*#__PURE__*/React.createElement(ThinkingDoneIcon, {
130
- size: 18,
131
- "data-odn-agent-think-icon-muted": true
132
- }), /*#__PURE__*/React.createElement("span", null, "\u5DF2\u5B8C\u6210\u601D\u8003", timeLabel), /*#__PURE__*/React.createElement(Icon, _extends({
124
+ }, /*#__PURE__*/React.createElement("span", null, "\u5DF2\u5B8C\u6210\u601D\u8003", timeLabel), /*#__PURE__*/React.createElement(Icon, _extends({
133
125
  name: "chevron-down",
134
126
  size: 12
135
127
  }, collapsed ? {
@@ -5,11 +5,13 @@ export interface AttachmentsProps {
5
5
  attachments: Attachment[];
6
6
  onRemove?: (index: number) => void;
7
7
  onFileClick?: (attachment: Attachment) => void;
8
+ /** 上传失败时重试回调(status='error' 生效)。 */
9
+ onRetry?: (index: number) => void;
8
10
  /** report 变体 hover 时「下载」按钮回调;未传则 hover 时只显示「打开」按钮。 */
9
11
  onDownload?: (attachment: Attachment) => void;
10
12
  compact?: boolean;
11
13
  }
12
- export declare function Attachments({ attachments, onRemove, onFileClick, onDownload, compact }: AttachmentsProps): import("react").JSX.Element | null;
14
+ export declare function Attachments({ attachments, onRemove, onFileClick, onRetry, onDownload, compact }: AttachmentsProps): import("react").JSX.Element | null;
13
15
  export declare namespace Attachments {
14
16
  var displayName: string;
15
17
  }
@@ -6,38 +6,38 @@ var categoryMap = {
6
6
  image: {
7
7
  icon: 'image',
8
8
  color: 'var(--odn-color-green-6)',
9
- bg: 'var(--odn-color-green-1)'
9
+ bg: 'var(--odn-color-green-2)'
10
10
  },
11
11
  code: {
12
12
  icon: 'file-code',
13
13
  color: 'var(--odn-color-blue-8)',
14
- bg: 'var(--odn-color-blue-1)'
14
+ bg: 'var(--odn-color-blue-2)'
15
15
  },
16
16
  doc: {
17
17
  icon: 'file-text',
18
18
  color: 'var(--odn-color-blue-6)',
19
- bg: 'var(--odn-color-blue-1)'
19
+ bg: 'var(--odn-color-blue-2)'
20
20
  },
21
21
  data: {
22
22
  icon: 'layers',
23
23
  color: 'var(--odn-color-green-6)',
24
- bg: 'var(--odn-color-green-1)'
24
+ bg: 'var(--odn-color-green-2)'
25
25
  },
26
26
  archive: {
27
27
  icon: 'archive',
28
28
  color: 'var(--odn-color-orange-6)',
29
- bg: 'var(--odn-color-orange-1)'
29
+ bg: 'var(--odn-color-orange-2)'
30
30
  },
31
31
  text: {
32
32
  icon: 'file-text',
33
33
  color: 'var(--odn-color-solid-black-9)',
34
- bg: 'var(--odn-color-solid-black-2)'
34
+ bg: 'var(--odn-color-solid-black-3)'
35
35
  }
36
36
  };
37
37
  var defaultMeta = {
38
38
  icon: 'file',
39
39
  color: 'var(--odn-color-solid-black-9)',
40
- bg: 'var(--odn-color-solid-black-2)'
40
+ bg: 'var(--odn-color-solid-black-3)'
41
41
  };
42
42
  var mimeCategory = {
43
43
  'application/pdf': 'doc',
@@ -123,10 +123,15 @@ function formatSize(bytes) {
123
123
  if (bytes < 1024 * 1024) return "".concat((bytes / 1024).toFixed(1), " KB");
124
124
  return "".concat((bytes / (1024 * 1024)).toFixed(1), " MB");
125
125
  }
126
+ function truncateMessage(text, maxChars) {
127
+ if (text.length <= maxChars) return text;
128
+ return "".concat(text.slice(0, maxChars), "...");
129
+ }
126
130
  export function Attachments(_ref) {
127
131
  var attachments = _ref.attachments,
128
132
  onRemove = _ref.onRemove,
129
133
  onFileClick = _ref.onFileClick,
134
+ onRetry = _ref.onRetry,
130
135
  onDownload = _ref.onDownload,
131
136
  _ref$compact = _ref.compact,
132
137
  compact = _ref$compact === void 0 ? false : _ref$compact;
@@ -141,14 +146,34 @@ export function Attachments(_ref) {
141
146
  * 退化成 chip,而想要"小卡片 + 缩略图"承担引用语义。
142
147
  */
143
148
  function renderThumbCard(a, i) {
149
+ var _a$status;
144
150
  var meta = getFileMeta(a.type);
145
151
  var isImage = a.type.startsWith('image/') && !!a.url;
152
+ var status = (_a$status = a.status) !== null && _a$status !== void 0 ? _a$status : 'done';
153
+ var isUploading = status === 'uploading';
154
+ var isError = status === 'error';
155
+ var isExpired = status === 'expired';
156
+ var clickable = !!onFileClick && !isUploading && !isExpired;
157
+ var retryable = !!onRetry && isError;
146
158
  var thumbCardInner = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
147
159
  "data-odn-attachments-thumb-card-thumb": true,
148
- style: isImage ? undefined : {
160
+ style: isImage && !isUploading && !isError ? undefined : isUploading || isError ? undefined : {
149
161
  backgroundColor: meta.bg
150
162
  }
151
- }, isImage ? /*#__PURE__*/React.createElement("img", {
163
+ }, isUploading ? /*#__PURE__*/React.createElement(Icon, {
164
+ name: "loading",
165
+ size: 16,
166
+ spin: true,
167
+ style: {
168
+ color: 'var(--odn-color-black-9)'
169
+ }
170
+ }) : isError ? /*#__PURE__*/React.createElement(Icon, {
171
+ name: "alert-triangle",
172
+ size: 16,
173
+ style: {
174
+ color: 'var(--odn-color-error)'
175
+ }
176
+ }) : isImage ? /*#__PURE__*/React.createElement("img", {
152
177
  src: a.url,
153
178
  alt: ""
154
179
  }) : /*#__PURE__*/React.createElement(Icon, {
@@ -163,20 +188,31 @@ export function Attachments(_ref) {
163
188
  "data-odn-attachments-thumb-card-name": true
164
189
  }, a.name), /*#__PURE__*/React.createElement("div", {
165
190
  "data-odn-attachments-thumb-card-type": true
166
- }, formatType(a.type, a.name))));
191
+ }, isError ? truncateMessage(a.errorMessage || '上传失败,请重试', 18) : isExpired ? '已过期' : formatType(a.type, a.name))));
167
192
  return /*#__PURE__*/React.createElement("div", {
168
193
  key: i,
169
194
  "data-odn-attachments-item": true,
195
+ "data-odn-attachments-state": status,
170
196
  "data-odn-attachments-item-thumb-card": ""
171
- }, onFileClick ? /*#__PURE__*/React.createElement("button", {
197
+ }, clickable ? /*#__PURE__*/React.createElement("button", {
172
198
  type: "button",
173
199
  onClick: function onClick() {
174
200
  return onFileClick(a);
175
201
  },
176
202
  "data-odn-attachments-thumb-card": true,
203
+ "data-odn-attachments-thumb-card-image": isImage || undefined,
177
204
  "data-odn-attachments-clickable": ""
205
+ }, thumbCardInner) : retryable ? /*#__PURE__*/React.createElement("button", {
206
+ type: "button",
207
+ onClick: function onClick() {
208
+ return onRetry(i);
209
+ },
210
+ "data-odn-attachments-thumb-card": true,
211
+ "data-odn-attachments-thumb-card-image": isImage || undefined,
212
+ "data-odn-attachments-retryable": ""
178
213
  }, thumbCardInner) : /*#__PURE__*/React.createElement("div", {
179
- "data-odn-attachments-thumb-card": true
214
+ "data-odn-attachments-thumb-card": true,
215
+ "data-odn-attachments-thumb-card-image": isImage || undefined
180
216
  }, thumbCardInner), onRemove && /*#__PURE__*/React.createElement("button", {
181
217
  onClick: function onClick() {
182
218
  return onRemove(i);
@@ -187,12 +223,80 @@ export function Attachments(_ref) {
187
223
  size: 10
188
224
  })));
189
225
  }
226
+
227
+ /**
228
+ * image-thumb 渲染:纯图大缩略方块(56×56 cover、无边框)。
229
+ *
230
+ * 与文件信息卡(data-odn-attachments-card)严格同源——共享
231
+ * data-odn-attachments-state 三态机、remove 角标。composer 待发送区把 image/*
232
+ * 附件映射到本变体,让"图片大预览 + 文件信息卡"并列且同行等高,不再各写一套。
233
+ */
234
+ function renderImageThumb(a, i) {
235
+ var _a$status2;
236
+ var status = (_a$status2 = a.status) !== null && _a$status2 !== void 0 ? _a$status2 : 'done';
237
+ var isUploading = status === 'uploading';
238
+ var isError = status === 'error';
239
+ var isExpired = status === 'expired';
240
+ var clickable = !!onFileClick && !isUploading && !isError && !isExpired;
241
+ var thumb = /*#__PURE__*/React.createElement("img", {
242
+ src: a.url,
243
+ alt: a.name
244
+ });
245
+ return /*#__PURE__*/React.createElement("div", {
246
+ key: i,
247
+ "data-odn-attachments-item": true,
248
+ "data-odn-attachments-state": status,
249
+ "data-odn-attachments-item-image-thumb": ""
250
+ }, clickable ? /*#__PURE__*/React.createElement("button", {
251
+ type: "button",
252
+ onClick: function onClick() {
253
+ return onFileClick(a);
254
+ },
255
+ "data-odn-attachments-image-thumb": true,
256
+ "data-odn-attachments-clickable": ""
257
+ }, thumb) : /*#__PURE__*/React.createElement("div", {
258
+ "data-odn-attachments-image-thumb": true
259
+ }, thumb), isUploading && /*#__PURE__*/React.createElement("div", {
260
+ "data-odn-attachments-image-overlay": true
261
+ }, /*#__PURE__*/React.createElement(Icon, {
262
+ name: "loading",
263
+ size: 16,
264
+ spin: true
265
+ })), isError && (onRetry ? /*#__PURE__*/React.createElement("button", {
266
+ type: "button",
267
+ "data-odn-attachments-image-overlay": true,
268
+ "data-odn-attachments-image-overlay-error": "",
269
+ onClick: function onClick() {
270
+ return onRetry(i);
271
+ },
272
+ title: a.errorMessage || '上传失败,点击重试'
273
+ }, /*#__PURE__*/React.createElement(Icon, {
274
+ name: "alert-triangle",
275
+ size: 16
276
+ }), /*#__PURE__*/React.createElement("span", null, "\u91CD\u8BD5")) : /*#__PURE__*/React.createElement("div", {
277
+ "data-odn-attachments-image-overlay": true,
278
+ "data-odn-attachments-image-overlay-error": "",
279
+ title: a.errorMessage || '上传失败'
280
+ }, /*#__PURE__*/React.createElement(Icon, {
281
+ name: "alert-triangle",
282
+ size: 16
283
+ }), /*#__PURE__*/React.createElement("span", null, "\u5931\u8D25"))), onRemove && /*#__PURE__*/React.createElement("button", {
284
+ onClick: function onClick() {
285
+ return onRemove(i);
286
+ },
287
+ "data-odn-attachments-remove": true
288
+ }, /*#__PURE__*/React.createElement(Icon, {
289
+ name: "x",
290
+ size: 10
291
+ })));
292
+ }
190
293
  if (compact) {
191
294
  return /*#__PURE__*/React.createElement("div", {
192
295
  "data-odn-attachments-compact": true
193
296
  }, attachments.map(function (a, i) {
194
297
  // thumb-card 优先于 chip 默认路径——见上方 renderThumbCard 注释。
195
298
  if (a.variant === 'thumb-card') return renderThumbCard(a, i);
299
+ if (a.variant === 'image-thumb') return renderImageThumb(a, i);
196
300
  var meta = getFileMeta(a.type);
197
301
  var chip = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Icon, {
198
302
  name: meta.icon,
@@ -223,6 +327,7 @@ export function Attachments(_ref) {
223
327
  return /*#__PURE__*/React.createElement("div", {
224
328
  "data-odn-attachments": true
225
329
  }, attachments.map(function (a, i) {
330
+ var _a$status3;
226
331
  // Report 变体:hero 卡片(标题 + 创建时间 + 右侧预览)
227
332
  if (a.variant === 'report') {
228
333
  var previewInner = /*#__PURE__*/React.createElement(React.Fragment, null, a.thumbnailUrl ? /*#__PURE__*/React.createElement("img", {
@@ -311,14 +416,34 @@ export function Attachments(_ref) {
311
416
  })));
312
417
  }
313
418
  if (a.variant === 'thumb-card') return renderThumbCard(a, i);
419
+ if (a.variant === 'image-thumb') return renderImageThumb(a, i);
314
420
  var meta = getFileMeta(a.type);
315
421
  var isImage = a.type.startsWith('image/') && a.url;
422
+ var status = (_a$status3 = a.status) !== null && _a$status3 !== void 0 ? _a$status3 : 'done';
423
+ var isUploading = status === 'uploading';
424
+ var isError = status === 'error';
425
+ var isExpired = status === 'expired';
426
+ var clickable = !!onFileClick && !isUploading && !isExpired;
427
+ var retryable = !!onRetry && isError;
316
428
  var fileCardInner = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
317
429
  "data-odn-attachments-file-icon": true,
318
- style: {
430
+ style: isUploading || isError ? undefined : {
319
431
  backgroundColor: meta.bg
320
432
  }
321
- }, /*#__PURE__*/React.createElement(Icon, {
433
+ }, isUploading ? /*#__PURE__*/React.createElement(Icon, {
434
+ name: "loading",
435
+ size: 16,
436
+ spin: true,
437
+ style: {
438
+ color: 'var(--odn-color-black-9)'
439
+ }
440
+ }) : isError ? /*#__PURE__*/React.createElement(Icon, {
441
+ name: "alert-triangle",
442
+ size: 16,
443
+ style: {
444
+ color: 'var(--odn-color-error)'
445
+ }
446
+ }) : /*#__PURE__*/React.createElement(Icon, {
322
447
  name: meta.icon,
323
448
  size: 16,
324
449
  style: {
@@ -330,19 +455,27 @@ export function Attachments(_ref) {
330
455
  "data-odn-attachments-name": true
331
456
  }, a.name), /*#__PURE__*/React.createElement("div", {
332
457
  "data-odn-attachments-meta": true
333
- }, formatType(a.type, a.name), a.size ? " \xB7 ".concat(formatSize(a.size)) : '')));
458
+ }, isError ? truncateMessage(a.errorMessage || '上传失败,请重试', 18) : isExpired ? '已过期' : "".concat(formatType(a.type, a.name)).concat(a.size ? " \xB7 ".concat(formatSize(a.size)) : ''))));
334
459
  return /*#__PURE__*/React.createElement("div", _extends({
335
460
  key: i,
336
- "data-odn-attachments-item": true
461
+ "data-odn-attachments-item": true,
462
+ "data-odn-attachments-state": status
337
463
  }, isImage ? {
338
464
  'data-odn-attachments-item-image-card': ''
339
- } : {}), onFileClick ? /*#__PURE__*/React.createElement("button", {
465
+ } : {}), clickable ? /*#__PURE__*/React.createElement("button", {
340
466
  type: "button",
341
467
  onClick: function onClick() {
342
468
  return onFileClick(a);
343
469
  },
344
470
  "data-odn-attachments-card": true,
345
471
  "data-odn-attachments-clickable": ""
472
+ }, fileCardInner) : retryable ? /*#__PURE__*/React.createElement("button", {
473
+ type: "button",
474
+ onClick: function onClick() {
475
+ return onRetry(i);
476
+ },
477
+ "data-odn-attachments-card": true,
478
+ "data-odn-attachments-retryable": ""
346
479
  }, fileCardInner) : /*#__PURE__*/React.createElement("div", {
347
480
  "data-odn-attachments-card": true
348
481
  }, fileCardInner), onRemove && /*#__PURE__*/React.createElement("button", {
@@ -59,7 +59,7 @@
59
59
  [data-odn-attachments-item] {
60
60
  position: relative;
61
61
  }
62
- [data-odn-attachments-item]:hover [data-odn-attachments-remove] {
62
+ [data-odn-attachments-item]:hover [data-odn-attachments-remove], [data-odn-attachments-item]:focus-within [data-odn-attachments-remove] {
63
63
  opacity: 1;
64
64
  }
65
65
 
@@ -83,6 +83,16 @@
83
83
  [data-odn-attachments-card][data-odn-attachments-clickable]:hover {
84
84
  border-color: color-mix(in srgb, var(--odn-color-primary) 50%, transparent);
85
85
  }
86
+ [data-odn-attachments-card][data-odn-attachments-retryable] {
87
+ cursor: pointer;
88
+ border: 1px solid color-mix(in srgb, var(--odn-color-error) 35%, var(--odn-color-black-6));
89
+ font: inherit;
90
+ text-align: left;
91
+ appearance: none;
92
+ }
93
+ [data-odn-attachments-card][data-odn-attachments-retryable]:hover {
94
+ border-color: color-mix(in srgb, var(--odn-color-error) 55%, transparent);
95
+ }
86
96
 
87
97
  [data-odn-attachments-file-icon] {
88
98
  display: flex;
@@ -114,6 +124,21 @@
114
124
  font-size: 10px;
115
125
  line-height: 1.25;
116
126
  color: var(--odn-color-black-9);
127
+ overflow: hidden;
128
+ text-overflow: ellipsis;
129
+ white-space: nowrap;
130
+ }
131
+
132
+ [data-odn-attachments-item][data-odn-attachments-state=uploading] [data-odn-attachments-meta] {
133
+ color: var(--odn-color-black-8);
134
+ }
135
+
136
+ [data-odn-attachments-item][data-odn-attachments-state=error] [data-odn-attachments-meta] {
137
+ color: var(--odn-color-error);
138
+ }
139
+
140
+ [data-odn-attachments-item][data-odn-attachments-state=expired] {
141
+ opacity: 0.55;
117
142
  }
118
143
 
119
144
  [data-odn-attachments-remove] {
@@ -135,9 +160,17 @@
135
160
  padding: 0;
136
161
  }
137
162
  [data-odn-attachments-remove]:hover {
138
- color: var(--odn-color-error);
163
+ color: var(--odn-color-black-12);
164
+ background: linear-gradient(var(--odn-color-black-3), var(--odn-color-black-3)), var(--odn-color-solid-black-1);
165
+ border-color: var(--odn-color-black-7);
139
166
  }
140
167
 
168
+ /* 触屏 / 无 hover 设备:无从 hover 揭示,删除角标常驻可达。 */
169
+ @media (hover: none) {
170
+ [data-odn-attachments-remove] {
171
+ opacity: 1;
172
+ }
173
+ }
141
174
  /* ==========================================================================
142
175
  * Thumb-card 变体(紧凑缩略卡)
143
176
  * 左侧缩略图(image/* + url 时为图本身;其余类型为分类色 icon),
@@ -157,6 +190,7 @@
157
190
  grid-template-columns: auto 1fr;
158
191
  gap: 8px;
159
192
  padding: 6px 10px 6px 6px;
193
+ min-height: 42px;
160
194
  border-radius: 6px;
161
195
  border: 1px solid var(--odn-color-black-6);
162
196
  background: var(--odn-color-solid-black-1);
@@ -173,6 +207,22 @@
173
207
  [data-odn-attachments-thumb-card][data-odn-attachments-clickable]:hover {
174
208
  border-color: color-mix(in srgb, var(--odn-color-primary) 50%, transparent);
175
209
  }
210
+ [data-odn-attachments-thumb-card][data-odn-attachments-retryable] {
211
+ cursor: pointer;
212
+ border: 1px solid color-mix(in srgb, var(--odn-color-error) 35%, var(--odn-color-black-6));
213
+ }
214
+ [data-odn-attachments-thumb-card][data-odn-attachments-retryable]:hover {
215
+ border-color: color-mix(in srgb, var(--odn-color-error) 55%, transparent);
216
+ }
217
+
218
+ /* 图片保持原先「无边框」视觉,仅与文件卡片对齐高度。 */
219
+ [data-odn-attachments-thumb-card][data-odn-attachments-thumb-card-image] {
220
+ border: none;
221
+ background: transparent;
222
+ }
223
+ [data-odn-attachments-thumb-card][data-odn-attachments-thumb-card-image][data-odn-attachments-clickable]:hover, [data-odn-attachments-thumb-card][data-odn-attachments-thumb-card-image][data-odn-attachments-retryable]:hover {
224
+ border: none;
225
+ }
176
226
 
177
227
  [data-odn-attachments-thumb-card-thumb] {
178
228
  display: flex;
@@ -213,6 +263,9 @@
213
263
  font-size: 10px;
214
264
  line-height: 1.25;
215
265
  color: var(--odn-color-black-9);
266
+ overflow: hidden;
267
+ text-overflow: ellipsis;
268
+ white-space: nowrap;
216
269
  }
217
270
 
218
271
  [data-odn-attachments-item-image-card] {
@@ -222,6 +275,86 @@
222
275
  [data-odn-attachments-card]:not([data-odn-attachments-image-card]),
223
276
  [data-odn-attachments-thumb-card] {
224
277
  width: 192px;
278
+ min-height: 42px;
279
+ }
280
+
281
+ /* ==========================================================================
282
+ * image-thumb 变体(纯图大缩略方块)
283
+ * 56×56 cover、无边框,承载 image/* + url。与文件信息卡(data-odn-attachments-card)
284
+ * 同源:共享 data-odn-attachments-state 三态机、remove 角标、fresh-done。
285
+ * composer 待发送区即以此变体让"图片大预览 + 文件信息卡"并列同行、等高同源。
286
+ * ========================================================================== */
287
+ [data-odn-attachments-item-image-thumb] {
288
+ display: inline-flex;
289
+ }
290
+
291
+ [data-odn-attachments-image-thumb] {
292
+ position: relative;
293
+ width: 56px;
294
+ height: 56px;
295
+ border-radius: 6px;
296
+ overflow: hidden;
297
+ padding: 0;
298
+ border: 1px solid var(--odn-color-black-6);
299
+ background: var(--odn-color-black-2);
300
+ box-sizing: border-box;
301
+ }
302
+ [data-odn-attachments-image-thumb] img {
303
+ width: 100%;
304
+ height: 100%;
305
+ object-fit: cover;
306
+ display: block;
307
+ }
308
+ [data-odn-attachments-image-thumb][data-odn-attachments-clickable] {
309
+ cursor: pointer;
310
+ appearance: none;
311
+ font: inherit;
312
+ transition: border-color 0.15s;
313
+ }
314
+ [data-odn-attachments-image-thumb][data-odn-attachments-clickable]:hover {
315
+ border-color: color-mix(in srgb, var(--odn-color-primary) 50%, transparent);
316
+ }
317
+
318
+ [data-odn-attachments-image-overlay] {
319
+ position: absolute;
320
+ inset: 0;
321
+ border-radius: 6px;
322
+ display: inline-flex;
323
+ align-items: center;
324
+ justify-content: center;
325
+ background: color-mix(in srgb, var(--odn-color-solid-black-12) 45%, transparent);
326
+ border: none;
327
+ color: #fff;
328
+ font-size: 10px;
329
+ line-height: 1.25;
330
+ gap: 2px;
331
+ padding: 0 6px;
332
+ text-align: center;
333
+ pointer-events: none;
334
+ }
335
+ [data-odn-attachments-image-overlay] span {
336
+ display: -webkit-box;
337
+ overflow: hidden;
338
+ text-overflow: ellipsis;
339
+ -webkit-line-clamp: 2;
340
+ -webkit-box-orient: vertical;
341
+ max-width: 100%;
342
+ }
343
+ [data-odn-attachments-image-overlay][data-odn-attachments-image-overlay-error] {
344
+ background: var(--odn-color-solid-black-1);
345
+ flex-direction: column;
346
+ border: 1px solid color-mix(in srgb, var(--odn-color-error) 35%, var(--odn-color-black-6));
347
+ color: var(--odn-color-error);
348
+ }
349
+ [data-odn-attachments-image-overlay][data-odn-attachments-image-overlay-error]:is(button) {
350
+ cursor: pointer;
351
+ appearance: none;
352
+ font: inherit;
353
+ font-size: 10px;
354
+ pointer-events: auto;
355
+ }
356
+ [data-odn-attachments-image-overlay][data-odn-attachments-image-overlay-error]:is(button):hover {
357
+ border-color: color-mix(in srgb, var(--odn-color-error) 55%, transparent);
225
358
  }
226
359
 
227
360
  /* ==========================================================================
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import './style';
3
- type StatusIndicator = 'generating' | 'unread';
3
+ type StatusIndicator = 'generating' | 'unread' | 'error';
4
4
  export interface ChatItemProps {
5
5
  id: string;
6
6
  title: string;
@@ -9,12 +9,18 @@ export interface ChatItemProps {
9
9
  onClick?: (id: string) => void;
10
10
  onRename?: (id: string, newTitle: string) => void;
11
11
  onDelete?: (id: string) => void;
12
- /**
13
- * 行内「…」菜单的 z-index。父级为高 z 浮层(如会话列表 Portal)时需高于父层,避免菜单被遮挡。
14
- */
15
12
  actionMenuZIndex?: number | string;
13
+ renaming?: boolean;
14
+ defaultRenaming?: boolean;
15
+ onRenamingChange?: (renaming: boolean) => void;
16
+ menuOpen?: boolean;
17
+ defaultMenuOpen?: boolean;
18
+ onMenuOpenChange?: (open: boolean) => void;
19
+ deleteOpen?: boolean;
20
+ defaultDeleteOpen?: boolean;
21
+ onDeleteOpenChange?: (open: boolean) => void;
16
22
  }
17
- export declare function ChatItem({ id, title, active, status, onClick, onRename, onDelete, actionMenuZIndex, }: ChatItemProps): import("react").JSX.Element;
23
+ export declare function ChatItem({ id, title, active, status, onClick, onRename, onDelete, actionMenuZIndex, renaming: renamingProp, defaultRenaming, onRenamingChange, menuOpen: menuOpenProp, defaultMenuOpen, onMenuOpenChange, deleteOpen: deleteOpenProp, defaultDeleteOpen, onDeleteOpenChange, }: ChatItemProps): import("react").JSX.Element;
18
24
  export declare namespace ChatItem {
19
25
  var displayName: string;
20
26
  }