@pactor-app/chat 1.15.6 → 1.17.0

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/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ ARTIFACT_IMAGE_EXTENSIONS: () => ARTIFACT_IMAGE_EXTENSIONS,
23
24
  ActivityTrace: () => ActivityTrace,
24
25
  AppShell: () => AppShell,
25
26
  ArtifactPanel: () => ArtifactPanel,
@@ -29,6 +30,7 @@ __export(index_exports, {
29
30
  CHAT_LAYOUT_TYPE: () => CHAT_LAYOUT_TYPE,
30
31
  CHAT_SHELL_COMPONENT_TYPE: () => CHAT_SHELL_COMPONENT_TYPE,
31
32
  Chat: () => Chat,
33
+ ChatArtifactCard: () => ChatArtifactCard,
32
34
  ChatDenied: () => ChatDenied,
33
35
  ChatLayout: () => ChatLayout,
34
36
  ChatServicesProvider: () => ChatServicesProvider,
@@ -43,7 +45,11 @@ __export(index_exports, {
43
45
  MessageBubble: () => MessageBubble,
44
46
  PartRenderer: () => PartRenderer,
45
47
  applyChatEvent: () => applyChatEvent,
48
+ artifactCardMeta: () => artifactCardMeta,
49
+ buildArtifactBlob: () => buildArtifactBlob,
50
+ buildArtifactTree: () => buildArtifactTree,
46
51
  buildChatScope: () => buildChatScope,
52
+ collectArtifacts: () => collectArtifacts,
47
53
  collectMessageText: () => collectMessageText,
48
54
  createChatComponentRegistry: () => createChatComponentRegistry,
49
55
  createChatId: () => createChatId,
@@ -52,14 +58,24 @@ __export(index_exports, {
52
58
  createManualDriver: () => createManualDriver,
53
59
  createSafeRegistry: () => createSafeRegistry,
54
60
  detectStreamingMode: () => detectStreamingMode,
61
+ downloadArtifact: () => downloadArtifact,
62
+ formatArtifactSize: () => formatArtifactSize,
63
+ formatMessageTime: () => formatMessageTime,
55
64
  guardChatDsl: () => guardChatDsl,
65
+ isImageArtifact: () => isImageArtifact,
66
+ mergeArtifacts: () => mergeArtifacts,
56
67
  pairToolTraces: () => pairToolTraces,
57
68
  registerCapabilityAction: () => registerCapabilityAction,
58
69
  registerChatComponent: () => registerChatComponent,
59
70
  registerChatLayout: () => registerChatLayout,
60
71
  registerChatShellComponent: () => registerChatShellComponent,
72
+ resolveArtifactFileType: () => resolveArtifactFileType,
73
+ resolveArtifactFilename: () => resolveArtifactFilename,
74
+ resolveArtifactIcon: () => resolveArtifactIcon,
75
+ resolveArtifactIconName: () => resolveArtifactIconName,
61
76
  runChatTurn: () => runChatTurn,
62
77
  runRegenerateTurn: () => runRegenerateTurn,
78
+ saveBlob: () => saveBlob,
63
79
  useChatAutoScroll: () => useChatAutoScroll,
64
80
  useChatServices: () => useChatServices,
65
81
  useSidebarDrawer: () => useSidebarDrawer,
@@ -187,35 +203,204 @@ var ConversationStore = class {
187
203
  };
188
204
 
189
205
  // src/artifact.ts
206
+ var ARTIFACT_IMAGE_EXTENSIONS = [
207
+ "png",
208
+ "jpg",
209
+ "jpeg",
210
+ "gif",
211
+ "svg",
212
+ "webp",
213
+ "avif",
214
+ "bmp",
215
+ "ico"
216
+ ];
217
+ function resolveArtifactFileType(artifact) {
218
+ const declared = artifact.fileType?.trim().toLowerCase().replace(/^\./, "");
219
+ if (declared) {
220
+ return declared;
221
+ }
222
+ const match = /\.([A-Za-z0-9]+)$/.exec(artifact.title.trim());
223
+ return match ? match[1].toLowerCase() : null;
224
+ }
225
+ function isImageArtifact(artifact) {
226
+ const ext = resolveArtifactFileType(artifact);
227
+ return ext !== null && ARTIFACT_IMAGE_EXTENSIONS.includes(ext);
228
+ }
229
+ var ARTIFACT_ICON_BY_EXTENSION = {
230
+ // 表格数据(绿)
231
+ csv: { name: "table", tone: "green" },
232
+ tsv: { name: "table", tone: "green" },
233
+ xls: { name: "table", tone: "green" },
234
+ xlsx: { name: "table", tone: "green" },
235
+ // 数据 / 数据库(琥珀)
236
+ json: { name: "database", tone: "amber" },
237
+ sql: { name: "database", tone: "amber" },
238
+ // 脚本(蓝,终端图标)
239
+ sh: { name: "terminal", tone: "blue" },
240
+ bash: { name: "terminal", tone: "blue" },
241
+ zsh: { name: "terminal", tone: "blue" },
242
+ // 代码 / 配置(蓝)
243
+ js: { name: "code", tone: "blue" },
244
+ mjs: { name: "code", tone: "blue" },
245
+ cjs: { name: "code", tone: "blue" },
246
+ jsx: { name: "code", tone: "blue" },
247
+ ts: { name: "code", tone: "blue" },
248
+ mts: { name: "code", tone: "blue" },
249
+ cts: { name: "code", tone: "blue" },
250
+ tsx: { name: "code", tone: "blue" },
251
+ py: { name: "code", tone: "blue" },
252
+ rb: { name: "code", tone: "blue" },
253
+ go: { name: "code", tone: "blue" },
254
+ rs: { name: "code", tone: "blue" },
255
+ java: { name: "code", tone: "blue" },
256
+ kt: { name: "code", tone: "blue" },
257
+ c: { name: "code", tone: "blue" },
258
+ h: { name: "code", tone: "blue" },
259
+ cc: { name: "code", tone: "blue" },
260
+ cpp: { name: "code", tone: "blue" },
261
+ cs: { name: "code", tone: "blue" },
262
+ swift: { name: "code", tone: "blue" },
263
+ php: { name: "code", tone: "blue" },
264
+ css: { name: "code", tone: "blue" },
265
+ scss: { name: "code", tone: "blue" },
266
+ less: { name: "code", tone: "blue" },
267
+ html: { name: "code", tone: "blue" },
268
+ vue: { name: "code", tone: "blue" },
269
+ svelte: { name: "code", tone: "blue" },
270
+ yaml: { name: "code", tone: "blue" },
271
+ yml: { name: "code", tone: "blue" },
272
+ toml: { name: "code", tone: "blue" },
273
+ xml: { name: "code", tone: "blue" },
274
+ // 文档 / 文本(石板灰)
275
+ md: { name: "file-text", tone: "slate" },
276
+ markdown: { name: "file-text", tone: "slate" },
277
+ mdx: { name: "file-text", tone: "slate" },
278
+ txt: { name: "file-text", tone: "slate" },
279
+ text: { name: "file-text", tone: "slate" },
280
+ log: { name: "file-text", tone: "slate" },
281
+ rst: { name: "file-text", tone: "slate" },
282
+ pdf: { name: "file-text", tone: "slate" },
283
+ doc: { name: "file-text", tone: "slate" },
284
+ docx: { name: "file-text", tone: "slate" }
285
+ };
286
+ function resolveArtifactIcon(artifact) {
287
+ const ext = resolveArtifactFileType(artifact);
288
+ if (ext !== null) {
289
+ if (ARTIFACT_IMAGE_EXTENSIONS.includes(ext)) {
290
+ return { name: "image", tone: "violet" };
291
+ }
292
+ const spec = ARTIFACT_ICON_BY_EXTENSION[ext];
293
+ if (spec !== void 0) {
294
+ return spec;
295
+ }
296
+ }
297
+ if (artifact.kind === "code") {
298
+ return { name: "code", tone: "blue" };
299
+ }
300
+ if (artifact.kind === "dsl") {
301
+ return { name: "layout-grid", tone: "slate" };
302
+ }
303
+ return ext === null ? { name: "file-text", tone: "slate" } : { name: "file", tone: "slate" };
304
+ }
305
+ function resolveArtifactIconName(artifact) {
306
+ return resolveArtifactIcon(artifact).name;
307
+ }
308
+ function formatArtifactSize(size) {
309
+ if (typeof size !== "number" || !Number.isFinite(size) || size < 0) {
310
+ return null;
311
+ }
312
+ if (size < 1024) {
313
+ return `${size} B`;
314
+ }
315
+ if (size < 1024 * 1024) {
316
+ return `${(size / 1024).toFixed(1)} KB`;
317
+ }
318
+ return `${(size / 1024 / 1024).toFixed(1)} MB`;
319
+ }
320
+ function collectArtifacts(messages) {
321
+ const order = [];
322
+ const byId = /* @__PURE__ */ new Map();
323
+ for (const message of messages) {
324
+ for (const part of message.parts) {
325
+ if (part.type !== "artifact" || !part.artifact) {
326
+ continue;
327
+ }
328
+ if (part.status !== void 0 && part.status !== "ready") {
329
+ continue;
330
+ }
331
+ if (!byId.has(part.artifact.id)) {
332
+ order.push(part.artifact.id);
333
+ }
334
+ byId.set(part.artifact.id, part.artifact);
335
+ }
336
+ }
337
+ return order.map((id) => byId.get(id));
338
+ }
339
+ function mergeArtifacts(stream, opened) {
340
+ const openedById = new Map(opened.map((item) => [item.id, item]));
341
+ const merged = stream.map((item) => openedById.get(item.id) ?? item);
342
+ for (const item of opened) {
343
+ if (!stream.some((s) => s.id === item.id)) {
344
+ merged.push(item);
345
+ }
346
+ }
347
+ return merged;
348
+ }
190
349
  var ArtifactStore = class {
191
350
  constructor() {
192
- this.artifact = null;
351
+ this.artifacts = [];
352
+ this.activeId = null;
193
353
  this.listeners = /* @__PURE__ */ new Set();
194
354
  }
195
- /** 当前产出物(未打开时为 null;引用在不变时保持稳定) */
355
+ /** 当前激活产出物(未打开时为 null;引用在不变时保持稳定) */
196
356
  get() {
197
- return this.artifact;
357
+ if (this.activeId === null) {
358
+ return null;
359
+ }
360
+ return this.artifacts.find((item) => item.id === this.activeId) ?? null;
198
361
  }
199
- /** 打开(或替换)当前产出物 */
362
+ /** 全部产出物(插入序;数组不可变,引用稳定) */
363
+ list() {
364
+ return this.artifacts;
365
+ }
366
+ /** 打开(或按 id 替换)产出物并激活 */
200
367
  open(artifact) {
201
- this.artifact = artifact;
368
+ const index = this.artifacts.findIndex((item) => item.id === artifact.id);
369
+ this.artifacts = index === -1 ? [...this.artifacts, artifact] : this.artifacts.map((item, i) => i === index ? artifact : item);
370
+ this.activeId = artifact.id;
371
+ this.notify();
372
+ }
373
+ /** 切换激活项(未知 id 告警并忽略,不抛错) */
374
+ activate(id) {
375
+ if (this.activeId === id) {
376
+ return;
377
+ }
378
+ if (!this.artifacts.some((item) => item.id === id)) {
379
+ console.warn("[@pactor-app/chat] unknown artifact id, activate ignored");
380
+ return;
381
+ }
382
+ this.activeId = id;
202
383
  this.notify();
203
384
  }
204
- /** 部分更新当前产出物(id 不可改);未 open 时告警并忽略 */
385
+ /** 部分更新当前激活产出物(id 不可改);未 open 时告警并忽略 */
205
386
  update(patch) {
206
- if (!this.artifact) {
387
+ const current = this.get();
388
+ if (!current) {
207
389
  console.warn("[@pactor-app/chat] artifact not open, update ignored");
208
390
  return;
209
391
  }
210
- this.artifact = { ...this.artifact, ...patch, id: this.artifact.id };
392
+ const next = { ...current, ...patch, id: current.id };
393
+ this.artifacts = this.artifacts.map(
394
+ (item) => item.id === current.id ? next : item
395
+ );
211
396
  this.notify();
212
397
  }
213
- /** 关闭面板(未打开时幂等,不重复通知) */
398
+ /** 关闭面板:清空激活项,列表保留(未打开时幂等,不重复通知) */
214
399
  close() {
215
- if (this.artifact === null) {
400
+ if (this.activeId === null) {
216
401
  return;
217
402
  }
218
- this.artifact = null;
403
+ this.activeId = null;
219
404
  this.notify();
220
405
  }
221
406
  /** 订阅变更;返回退订函数 */
@@ -232,6 +417,94 @@ var ArtifactStore = class {
232
417
  }
233
418
  };
234
419
 
420
+ // src/artifact-download.ts
421
+ var KIND_EXTENSIONS = {
422
+ markdown: "md",
423
+ text: "txt",
424
+ code: "txt",
425
+ dsl: "json"
426
+ };
427
+ function resolveArtifactFilename(artifact) {
428
+ const title = artifact.title.trim() || "artifact";
429
+ if (/\.[A-Za-z0-9]+$/.test(title)) {
430
+ return title;
431
+ }
432
+ const ext = resolveArtifactFileType(artifact) ?? KIND_EXTENSIONS[artifact.kind];
433
+ return `${title}.${ext}`;
434
+ }
435
+ function dataUrlToBlob(dataUrl) {
436
+ const comma = dataUrl.indexOf(",");
437
+ const meta = dataUrl.slice(0, comma);
438
+ const payload = dataUrl.slice(comma + 1);
439
+ const mime = /^data:([^;,]+)/.exec(meta)?.[1] ?? "application/octet-stream";
440
+ if (meta.includes(";base64")) {
441
+ const binary = atob(payload);
442
+ const bytes = new Uint8Array(binary.length);
443
+ for (let i = 0; i < binary.length; i++) {
444
+ bytes[i] = binary.charCodeAt(i);
445
+ }
446
+ return new Blob([bytes], { type: mime });
447
+ }
448
+ return new Blob([decodeURIComponent(payload)], { type: mime });
449
+ }
450
+ function isRemoteUrl(raw) {
451
+ return /^https?:\/\//.test(raw.trim());
452
+ }
453
+ function buildArtifactBlob(artifact) {
454
+ if (artifact.kind === "dsl" && typeof artifact.content === "object") {
455
+ return new Blob([JSON.stringify(artifact.content, null, 2)], {
456
+ type: "application/json"
457
+ });
458
+ }
459
+ const raw = String(artifact.content);
460
+ if (isImageArtifact(artifact)) {
461
+ const trimmed = raw.trim();
462
+ if (trimmed.startsWith("data:")) {
463
+ return dataUrlToBlob(trimmed);
464
+ }
465
+ if (trimmed.startsWith("<svg") || trimmed.startsWith("<?xml")) {
466
+ return new Blob([raw], { type: "image/svg+xml" });
467
+ }
468
+ if (isRemoteUrl(trimmed)) {
469
+ return null;
470
+ }
471
+ }
472
+ const ext = resolveArtifactFileType(artifact);
473
+ const mime = ext === "md" || artifact.kind === "markdown" ? "text/markdown" : "text/plain";
474
+ return new Blob([raw], { type: `${mime};charset=utf-8` });
475
+ }
476
+ function saveBlob(blob, filename) {
477
+ const url = URL.createObjectURL(blob);
478
+ const anchor = document.createElement("a");
479
+ anchor.href = url;
480
+ anchor.download = filename;
481
+ document.body.appendChild(anchor);
482
+ anchor.click();
483
+ anchor.remove();
484
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
485
+ }
486
+ async function downloadArtifact(artifact) {
487
+ const filename = resolveArtifactFilename(artifact);
488
+ const raw = String(artifact.content ?? "").trim();
489
+ if (isImageArtifact(artifact) && isRemoteUrl(raw)) {
490
+ try {
491
+ const response = await fetch(raw);
492
+ if (!response.ok) {
493
+ throw new Error(`HTTP ${response.status}`);
494
+ }
495
+ saveBlob(await response.blob(), filename);
496
+ return;
497
+ } catch {
498
+ window.open(raw, "_blank", "noopener");
499
+ return;
500
+ }
501
+ }
502
+ const blob = buildArtifactBlob(artifact);
503
+ if (blob) {
504
+ saveBlob(blob, filename);
505
+ }
506
+ }
507
+
235
508
  // src/events.ts
236
509
  function applyChatEvent(store, event) {
237
510
  switch (event.type) {
@@ -868,7 +1141,7 @@ function useChatAutoScroll({
868
1141
 
869
1142
  // src/chat-composition.tsx
870
1143
  var import_runtime5 = require("@pactor-app/runtime");
871
- var import_react17 = require("react");
1144
+ var import_react19 = require("react");
872
1145
 
873
1146
  // src/hooks/use-chat-model.ts
874
1147
  var import_react3 = require("react");
@@ -922,14 +1195,14 @@ function AppShell({
922
1195
  }
923
1196
 
924
1197
  // src/view/ArtifactPanel.tsx
925
- var import_components4 = require("@pactor-app/ui/components");
1198
+ var import_components5 = require("@pactor-app/ui/components");
926
1199
  var import_core3 = require("@pactor-app/core");
927
1200
  var import_markdown2 = require("@pactor-app/markdown");
928
1201
  var import_runtime4 = require("@pactor-app/runtime");
929
- var import_react6 = require("react");
1202
+ var import_react8 = require("react");
930
1203
 
931
1204
  // src/view/PartRenderer.tsx
932
- var import_components2 = require("@pactor-app/ui/components");
1205
+ var import_components3 = require("@pactor-app/ui/components");
933
1206
  var import_markdown = require("@pactor-app/markdown");
934
1207
  var import_runtime3 = require("@pactor-app/runtime");
935
1208
 
@@ -971,59 +1244,7 @@ function useThrottledValue(value, intervalMs, enabled) {
971
1244
  }
972
1245
 
973
1246
  // src/view/chrome/artifact-card.tsx
974
- function buildArtifactCardFragment(artifact) {
975
- return {
976
- requires: ["Card", "Flex", "Text", "Badge", "Button"],
977
- node: {
978
- type: "Card",
979
- props: { className: "pactor-chat-artifact-card" },
980
- children: [
981
- {
982
- type: "Flex",
983
- props: { justify: "space-between", align: "center", gap: "small" },
984
- children: [
985
- {
986
- type: "Text",
987
- props: {
988
- text: artifact.title,
989
- strong: true,
990
- className: "pactor-chat-artifact-card__title"
991
- }
992
- },
993
- {
994
- type: "Badge",
995
- props: {
996
- text: artifact.kind,
997
- variant: "secondary",
998
- className: "pactor-chat-artifact-card__kind"
999
- }
1000
- },
1001
- {
1002
- type: "Button",
1003
- props: {
1004
- text: "\u5728\u9762\u677F\u4E2D\u6253\u5F00",
1005
- variant: "default",
1006
- size: "small",
1007
- className: "pactor-chat-artifact-card__open"
1008
- },
1009
- events: {
1010
- onClick: [
1011
- {
1012
- action: "chat.openArtifact",
1013
- params: { artifact }
1014
- }
1015
- ]
1016
- }
1017
- }
1018
- ]
1019
- }
1020
- ]
1021
- }
1022
- };
1023
- }
1024
-
1025
- // src/view/chrome/ChromeSlot.tsx
1026
- var import_runtime2 = require("@pactor-app/runtime");
1247
+ var import_components2 = require("@pactor-app/ui/components");
1027
1248
  var import_react5 = require("react");
1028
1249
 
1029
1250
  // src/view/chrome/actions.ts
@@ -1104,9 +1325,125 @@ function ensureChatChromeActions(actions) {
1104
1325
  function invokeChatPanelClose() {
1105
1326
  handlers.panelClose?.();
1106
1327
  }
1328
+ function invokeChatOpenArtifact(artifact) {
1329
+ handlers.openArtifact?.(artifact);
1330
+ }
1331
+ function invokeChatOpenArtifactList() {
1332
+ handlers.openArtifactList?.();
1333
+ }
1334
+ function invokeChatPanelToggleList() {
1335
+ handlers.panelToggleList?.();
1336
+ }
1337
+ function invokeChatPanelDownload() {
1338
+ handlers.panelDownload?.();
1339
+ }
1107
1340
 
1108
- // src/view/chrome/ChromeSlot.tsx
1341
+ // src/view/chrome/artifact-card.tsx
1109
1342
  var import_jsx_runtime2 = require("react/jsx-runtime");
1343
+ function artifactCardMeta(artifact) {
1344
+ const ext = resolveArtifactFileType(artifact);
1345
+ const typeLabel = (ext ?? artifact.kind).toUpperCase();
1346
+ const sizeLabel = formatArtifactSize(artifact.meta?.["size"]);
1347
+ return [sizeLabel, typeLabel].filter(Boolean).join(" \xB7 ");
1348
+ }
1349
+ function ChatArtifactCard({
1350
+ artifact
1351
+ }) {
1352
+ const icon = resolveArtifactIcon(artifact);
1353
+ const meta = artifactCardMeta(artifact);
1354
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1355
+ "button",
1356
+ {
1357
+ type: "button",
1358
+ className: `pactor-chat-artifact-card pactor-chat-artifact-card--${icon.tone}`,
1359
+ "data-artifact-kind": artifact.kind,
1360
+ "aria-label": `\u5728\u9762\u677F\u4E2D\u6253\u5F00\uFF1A${artifact.title}`,
1361
+ title: `\u5728\u9762\u677F\u4E2D\u6253\u5F00\uFF1A${artifact.title}`,
1362
+ onClick: () => invokeChatOpenArtifact(artifact),
1363
+ children: [
1364
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "pactor-chat-artifact-card__icon", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components2.Icon, { name: icon.name, size: 20 }) }),
1365
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "pactor-chat-artifact-card__text", children: [
1366
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "pactor-chat-artifact-card__title", children: artifact.title }),
1367
+ meta ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "pactor-chat-artifact-card__meta", children: meta }) : null
1368
+ ] }),
1369
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "pactor-chat-artifact-card__arrow", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components2.Icon, { name: "arrow-up-right", size: 14 }) })
1370
+ ]
1371
+ }
1372
+ );
1373
+ }
1374
+ var ARTIFACT_CARD_INTERNAL_COMPONENTS = {
1375
+ ChatArtifactCard,
1376
+ ChatArtifactCardGroup
1377
+ };
1378
+ var ARTIFACT_CARD_GROUP_LIMIT = 4;
1379
+ function ChatArtifactCardGroup({
1380
+ artifacts
1381
+ }) {
1382
+ const [expanded, setExpanded] = (0, import_react5.useState)(false);
1383
+ const overflow = artifacts.length > ARTIFACT_CARD_GROUP_LIMIT;
1384
+ const visible = overflow && !expanded ? artifacts.slice(0, ARTIFACT_CARD_GROUP_LIMIT) : artifacts;
1385
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pactor-chat-artifact-group", children: [
1386
+ visible.map((artifact) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChatArtifactCard, { artifact }, artifact.id)),
1387
+ overflow ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "pactor-chat-artifact-group__footer", children: [
1388
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1389
+ "button",
1390
+ {
1391
+ type: "button",
1392
+ className: "pactor-chat-artifact-group__toggle",
1393
+ "aria-expanded": expanded,
1394
+ onClick: () => setExpanded((value) => !value),
1395
+ children: [
1396
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1397
+ import_components2.Icon,
1398
+ {
1399
+ name: expanded ? "chevron-up" : "chevron-down",
1400
+ size: 12
1401
+ }
1402
+ ),
1403
+ expanded ? "\u6536\u8D77" : "\u5C55\u5F00"
1404
+ ]
1405
+ }
1406
+ ),
1407
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1408
+ "button",
1409
+ {
1410
+ type: "button",
1411
+ className: "pactor-chat-artifact-group__all",
1412
+ onClick: () => invokeChatOpenArtifactList(),
1413
+ children: [
1414
+ "\u67E5\u770B\u6240\u6709\u4EA7\u7269(",
1415
+ artifacts.length,
1416
+ ")",
1417
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_components2.Icon, { name: "chevron-right", size: 12 })
1418
+ ]
1419
+ }
1420
+ )
1421
+ ] }) : null
1422
+ ] });
1423
+ }
1424
+ function buildArtifactCardFragment(artifact) {
1425
+ return {
1426
+ requires: ["ChatArtifactCard"],
1427
+ node: {
1428
+ type: "ChatArtifactCard",
1429
+ props: { artifact }
1430
+ }
1431
+ };
1432
+ }
1433
+ function buildArtifactCardGroupFragment(artifacts) {
1434
+ return {
1435
+ requires: ["ChatArtifactCardGroup"],
1436
+ node: {
1437
+ type: "ChatArtifactCardGroup",
1438
+ props: { artifacts }
1439
+ }
1440
+ };
1441
+ }
1442
+
1443
+ // src/view/chrome/ChromeSlot.tsx
1444
+ var import_runtime2 = require("@pactor-app/runtime");
1445
+ var import_react6 = require("react");
1446
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1110
1447
  var warnedMissing = /* @__PURE__ */ new Set();
1111
1448
  function warnMissingOnce(key, detail) {
1112
1449
  if (!warnedMissing.has(key)) {
@@ -1124,7 +1461,7 @@ function ChromeSlotInner({
1124
1461
  try {
1125
1462
  renderer = (0, import_runtime2.useRenderer)();
1126
1463
  } catch {
1127
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
1464
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
1128
1465
  }
1129
1466
  ensureChatChromeActions(renderer.context.actions);
1130
1467
  if (components) {
@@ -1135,7 +1472,7 @@ function ChromeSlotInner({
1135
1472
  `conflict:${name}`,
1136
1473
  `[@pactor-app/chat] chrome \u5185\u90E8\u7EC4\u4EF6\u540D\u300C${name}\u300D\u88AB\u5BBF\u4E3B\u5360\u7528\uFF0C\u5DF2\u56DE\u9000\u5185\u5EFA\u5B9E\u73B0`
1137
1474
  );
1138
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
1475
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
1139
1476
  }
1140
1477
  if (existing === void 0) {
1141
1478
  renderer.context.components.register(
@@ -1153,12 +1490,12 @@ function ChromeSlotInner({
1153
1490
  `${fragment.node.type}:${missing.join(",")}`,
1154
1491
  `[@pactor-app/chat] chrome DSL \u7247\u6BB5\u6240\u9700\u7EC4\u4EF6\u7C7B\u578B\u7F3A\u5931\uFF08${missing.join(" / ")}\uFF09\uFF0C\u5DF2\u56DE\u9000\u5185\u5EFA\u5B9E\u73B0`
1155
1492
  );
1156
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: fallback });
1493
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
1157
1494
  }
1158
- const rendered = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "pactor-chat-chrome", children: renderer.renderChildren([fragment.node]) });
1159
- return wrap ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: wrap(rendered) }) : rendered;
1495
+ const rendered = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-chrome", children: renderer.renderChildren([fragment.node]) });
1496
+ return wrap ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: wrap(rendered) }) : rendered;
1160
1497
  }
1161
- var ChromeSlotGuard = class extends import_react5.Component {
1498
+ var ChromeSlotGuard = class extends import_react6.Component {
1162
1499
  constructor() {
1163
1500
  super(...arguments);
1164
1501
  this.state = { failed: false };
@@ -1171,7 +1508,7 @@ var ChromeSlotGuard = class extends import_react5.Component {
1171
1508
  }
1172
1509
  };
1173
1510
  function ChromeSlot(props) {
1174
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChromeSlotGuard, { fallback: props.fallback, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChromeSlotInner, { ...props }) });
1511
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChromeSlotGuard, { fallback: props.fallback, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChromeSlotInner, { ...props }) });
1175
1512
  }
1176
1513
 
1177
1514
  // src/view/chrome/feedback.tsx
@@ -1195,9 +1532,9 @@ function buildStatusFragment(text) {
1195
1532
  }
1196
1533
 
1197
1534
  // src/view/PartRenderer.tsx
1198
- var import_jsx_runtime3 = require("react/jsx-runtime");
1535
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1199
1536
  function ChatDenied({ deniedType }) {
1200
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "pactor-chat-denied", "data-pactor-chat-denied": deniedType, children: [
1537
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "pactor-chat-denied", "data-pactor-chat-denied": deniedType, children: [
1201
1538
  "\u8BE5\u7EC4\u4EF6\u4E0D\u5141\u8BB8\u5728 Chat \u4E2D\u4F7F\u7528",
1202
1539
  deniedType ? `\uFF1A${deniedType}` : ""
1203
1540
  ] });
@@ -1208,89 +1545,68 @@ function ChatDslNode({
1208
1545
  }) {
1209
1546
  const renderer = (0, import_runtime3.useRenderer)();
1210
1547
  const extraScope = scope ? buildChatScope(scope) : void 0;
1211
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderer.renderChildren([node], extraScope) });
1548
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderer.renderChildren([node], extraScope) });
1212
1549
  }
1213
1550
  function SmoothText({ content, streaming }) {
1214
1551
  const shown = useSmoothStreamText(content, streaming);
1215
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-text", children: shown });
1552
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pactor-chat-text", children: shown });
1216
1553
  }
1217
1554
  function SmoothMarkdown({ content, streaming }) {
1218
1555
  const shown = useSmoothStreamText(content, streaming);
1219
1556
  const throttled = useThrottledValue(shown, 100, streaming);
1220
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_markdown.MarkdownRenderer, { content: throttled });
1557
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_markdown.MarkdownRenderer, { content: throttled });
1221
1558
  }
1222
- function PartRenderer({ part, runtime, scope, streaming = false, onOpenArtifact }) {
1559
+ function PartRenderer({ part, runtime, scope, streaming = false }) {
1223
1560
  switch (part.type) {
1224
1561
  case "text":
1225
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SmoothText, { content: part.content, streaming });
1562
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SmoothText, { content: part.content, streaming });
1226
1563
  case "markdown":
1227
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SmoothMarkdown, { content: part.content, streaming });
1564
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SmoothMarkdown, { content: part.content, streaming });
1228
1565
  case "thinking":
1229
1566
  case "tool-call":
1230
1567
  case "tool-result":
1231
1568
  return null;
1232
1569
  case "dsl": {
1233
1570
  if (!part.schema || !runtime) {
1234
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1571
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1235
1572
  ChromeSlot,
1236
1573
  {
1237
1574
  fragment: buildStatusFragment(
1238
1575
  part.schema ? "DSL \u6E32\u67D3\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "\u5185\u5BB9\u751F\u6210\u4E2D\u2026"
1239
1576
  ),
1240
- fallback: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-dsl-pending", children: part.schema ? "DSL \u6E32\u67D3\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "\u5185\u5BB9\u751F\u6210\u4E2D\u2026" })
1577
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pactor-chat-dsl-pending", children: part.schema ? "DSL \u6E32\u67D3\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "\u5185\u5BB9\u751F\u6210\u4E2D\u2026" })
1241
1578
  }
1242
1579
  );
1243
1580
  }
1244
1581
  const guarded = guardChatDsl(part.schema, runtime.context.components);
1245
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-dsl", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_runtime3.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChatDslNode, { node: guarded, scope }) }) });
1582
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pactor-chat-dsl", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_runtime3.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChatDslNode, { node: guarded, scope }) }) });
1246
1583
  }
1247
1584
  case "error":
1248
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1585
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1249
1586
  ChromeSlot,
1250
1587
  {
1251
1588
  fragment: buildErrorFragment(part.message),
1252
- fallback: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "pactor-chat-error", role: "alert", children: [
1253
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_components2.Icon, { name: "error", size: 14 }),
1254
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: part.message })
1589
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "pactor-chat-error", role: "alert", children: [
1590
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_components3.Icon, { name: "error", size: 14 }),
1591
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: part.message })
1255
1592
  ] })
1256
1593
  }
1257
1594
  );
1258
1595
  case "status":
1259
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1596
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1260
1597
  ChromeSlot,
1261
1598
  {
1262
1599
  fragment: buildStatusFragment(part.text),
1263
- fallback: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pactor-chat-status", children: part.text })
1600
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "pactor-chat-status", children: part.text })
1264
1601
  }
1265
1602
  );
1266
1603
  case "artifact":
1267
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1604
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1268
1605
  ChromeSlot,
1269
1606
  {
1270
1607
  fragment: buildArtifactCardFragment(part.artifact),
1271
- fallback: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1272
- "div",
1273
- {
1274
- className: "pactor-chat-artifact-card",
1275
- "data-artifact-kind": part.artifact.kind,
1276
- children: [
1277
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "pactor-chat-artifact-card__title", children: part.artifact.title }),
1278
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "pactor-chat-artifact-card__kind", children: part.artifact.kind }),
1279
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1280
- "button",
1281
- {
1282
- type: "button",
1283
- className: "pactor-chat-artifact-card__open",
1284
- onClick: () => onOpenArtifact?.(part.artifact),
1285
- children: [
1286
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_components2.Icon, { name: "panel-right-open", size: 13, strokeWidth: 1.7 }),
1287
- "\u5728\u9762\u677F\u4E2D\u6253\u5F00"
1288
- ]
1289
- }
1290
- )
1291
- ]
1292
- }
1293
- )
1608
+ components: ARTIFACT_CARD_INTERNAL_COMPONENTS,
1609
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChatArtifactCard, { artifact: part.artifact })
1294
1610
  }
1295
1611
  );
1296
1612
  default:
@@ -1299,10 +1615,17 @@ function PartRenderer({ part, runtime, scope, streaming = false, onOpenArtifact
1299
1615
  }
1300
1616
 
1301
1617
  // src/view/chrome/panel-header.tsx
1302
- var import_components3 = require("@pactor-app/ui/components");
1303
- var import_jsx_runtime4 = require("react/jsx-runtime");
1618
+ var import_components4 = require("@pactor-app/ui/components");
1619
+ var import_react7 = require("react");
1620
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1621
+ var ChatPanelContext = (0, import_react7.createContext)(
1622
+ null
1623
+ );
1624
+ function artifactIconName(artifact) {
1625
+ return artifact ? resolveArtifactIconName(artifact) : "file";
1626
+ }
1304
1627
  function ChatPanelCloseButton() {
1305
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1628
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1306
1629
  "button",
1307
1630
  {
1308
1631
  type: "button",
@@ -1310,14 +1633,99 @@ function ChatPanelCloseButton() {
1310
1633
  title: "\u5173\u95ED\u9762\u677F",
1311
1634
  "aria-label": "\u5173\u95ED\u9762\u677F",
1312
1635
  onClick: () => invokeChatPanelClose(),
1313
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_components3.Icon, { name: "x", size: 16 })
1636
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "x", size: 16 })
1637
+ }
1638
+ );
1639
+ }
1640
+ function ChatPanelListButton() {
1641
+ const ctx = (0, import_react7.useContext)(ChatPanelContext);
1642
+ if (ctx?.view === "list") {
1643
+ return null;
1644
+ }
1645
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1646
+ "button",
1647
+ {
1648
+ type: "button",
1649
+ className: "pactor-chat-icon-button pactor-chat-inspector__list-toggle",
1650
+ title: "\u4EA7\u7269\u76EE\u5F55",
1651
+ "aria-label": "\u4EA7\u7269\u76EE\u5F55",
1652
+ onClick: () => invokeChatPanelToggleList(),
1653
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "arrow-left", size: 16 })
1654
+ }
1655
+ );
1656
+ }
1657
+ function ChatPanelDownloadButton() {
1658
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1659
+ "button",
1660
+ {
1661
+ type: "button",
1662
+ className: "pactor-chat-icon-button pactor-chat-inspector__download",
1663
+ title: "\u4E0B\u8F7D\u4EA7\u7269",
1664
+ "aria-label": "\u4E0B\u8F7D\u4EA7\u7269",
1665
+ onClick: () => invokeChatPanelDownload(),
1666
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "download", size: 16 })
1667
+ }
1668
+ );
1669
+ }
1670
+ function ChatPanelArtifactMenu() {
1671
+ const ctx = (0, import_react7.useContext)(ChatPanelContext);
1672
+ if (!ctx || ctx.artifacts.length === 0) {
1673
+ return null;
1674
+ }
1675
+ const active = ctx.artifacts.find((item) => item.id === ctx.activeId);
1676
+ const inList = ctx.view === "list";
1677
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1678
+ import_components4.DropdownMenu,
1679
+ {
1680
+ items: ctx.artifacts.map((item) => ({
1681
+ label: item.title,
1682
+ value: item.id,
1683
+ icon: artifactIconName(item)
1684
+ })),
1685
+ onSelect: (value) => {
1686
+ const target = ctx.artifacts.find((item) => item.id === value);
1687
+ if (target) {
1688
+ ctx.onSelectArtifact?.(target);
1689
+ }
1690
+ },
1691
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1692
+ "span",
1693
+ {
1694
+ className: "pactor-chat-inspector__menu-trigger",
1695
+ "data-view": ctx.view,
1696
+ children: [
1697
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1698
+ import_components4.Icon,
1699
+ {
1700
+ name: inList ? "folder" : artifactIconName(active),
1701
+ size: 13
1702
+ }
1703
+ ),
1704
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__title", children: inList ? "\u4EA7\u7269\u76EE\u5F55" : active?.title ?? "\u4EA7\u51FA\u7269" }),
1705
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "chevron-down", size: 12 })
1706
+ ]
1707
+ }
1708
+ )
1314
1709
  }
1315
1710
  );
1316
1711
  }
1317
- var PANEL_HEADER_INTERNAL_COMPONENTS = { ChatPanelCloseButton };
1712
+ var PANEL_HEADER_INTERNAL_COMPONENTS = {
1713
+ ChatPanelCloseButton,
1714
+ ChatPanelDownloadButton,
1715
+ ChatPanelListButton,
1716
+ ChatPanelArtifactMenu
1717
+ };
1318
1718
  function buildPanelHeaderFragment(props) {
1319
1719
  return {
1320
- requires: ["Flex", "Text", "Badge", "ChatPanelCloseButton"],
1720
+ requires: [
1721
+ "Flex",
1722
+ "Text",
1723
+ "Badge",
1724
+ "ChatPanelCloseButton",
1725
+ "ChatPanelDownloadButton",
1726
+ "ChatPanelListButton",
1727
+ "ChatPanelArtifactMenu"
1728
+ ],
1321
1729
  node: {
1322
1730
  type: "Flex",
1323
1731
  props: {
@@ -1331,7 +1739,8 @@ function buildPanelHeaderFragment(props) {
1331
1739
  type: "Flex",
1332
1740
  props: { align: "center", gap: "small" },
1333
1741
  children: [
1334
- {
1742
+ ...props.showListButton ? [{ type: "ChatPanelListButton" }] : [],
1743
+ props.showMenu ? { type: "ChatPanelArtifactMenu" } : {
1335
1744
  type: "Text",
1336
1745
  props: {
1337
1746
  text: props.title,
@@ -1339,26 +1748,35 @@ function buildPanelHeaderFragment(props) {
1339
1748
  className: "pactor-chat-inspector__title"
1340
1749
  }
1341
1750
  },
1342
- {
1343
- type: "Badge",
1344
- props: {
1345
- text: props.kind,
1346
- variant: "secondary",
1347
- className: "pactor-chat-inspector__kind"
1751
+ ...props.view === "list" ? [] : [
1752
+ {
1753
+ type: "Badge",
1754
+ props: {
1755
+ text: props.kind,
1756
+ variant: "secondary",
1757
+ className: "pactor-chat-inspector__kind"
1758
+ }
1348
1759
  }
1349
- }
1760
+ ]
1350
1761
  ]
1351
1762
  },
1352
- { type: "ChatPanelCloseButton" }
1763
+ {
1764
+ type: "Flex",
1765
+ props: { align: "center", gap: "small" },
1766
+ children: [
1767
+ ...props.showDownload ? [{ type: "ChatPanelDownloadButton" }] : [],
1768
+ { type: "ChatPanelCloseButton" }
1769
+ ]
1770
+ }
1353
1771
  ]
1354
1772
  }
1355
1773
  };
1356
1774
  }
1357
1775
 
1358
1776
  // src/view/ArtifactPanel.tsx
1359
- var import_jsx_runtime5 = require("react/jsx-runtime");
1777
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1360
1778
  function useArtifactRuntime(safeRegistry, user) {
1361
- return (0, import_react6.useMemo)(() => {
1779
+ return (0, import_react8.useMemo)(() => {
1362
1780
  if (!safeRegistry) {
1363
1781
  return void 0;
1364
1782
  }
@@ -1370,7 +1788,7 @@ function useArtifactRuntime(safeRegistry, user) {
1370
1788
  return createChatRuntime({ safeRegistry: components, user });
1371
1789
  }, [safeRegistry, user]);
1372
1790
  }
1373
- var ArtifactDslGuard = class extends import_react6.Component {
1791
+ var ArtifactDslGuard = class extends import_react8.Component {
1374
1792
  constructor() {
1375
1793
  super(...arguments);
1376
1794
  this.state = { failed: false };
@@ -1382,12 +1800,12 @@ var ArtifactDslGuard = class extends import_react6.Component {
1382
1800
  console.warn("[@pactor-app/chat] DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A", error);
1383
1801
  }
1384
1802
  render() {
1385
- return this.state.failed ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-denied", children: "DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A" }) : this.props.children;
1803
+ return this.state.failed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-denied", children: "DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A" }) : this.props.children;
1386
1804
  }
1387
1805
  };
1388
1806
  function ArtifactDslNode({ node }) {
1389
1807
  const { renderChildren } = (0, import_runtime4.useRenderer)();
1390
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: renderChildren([node]) });
1808
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: renderChildren([node]) });
1391
1809
  }
1392
1810
  function ArtifactDslContent({
1393
1811
  content,
@@ -1395,47 +1813,195 @@ function ArtifactDslContent({
1395
1813
  }) {
1396
1814
  const node = typeof content === "object" && content !== null && !Array.isArray(content) && typeof content.type === "string" ? content : void 0;
1397
1815
  if (!node || !runtime) {
1398
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-denied", children: node ? "DSL \u9884\u89C8\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "DSL \u4EA7\u51FA\u7269\u5185\u5BB9\u65E0\u6548" });
1816
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-denied", children: node ? "DSL \u9884\u89C8\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "DSL \u4EA7\u51FA\u7269\u5185\u5BB9\u65E0\u6548" });
1399
1817
  }
1400
1818
  const guarded = guardChatDsl(node, runtime.context.components);
1401
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_runtime4.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactDslGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactDslNode, { node: guarded }) }) });
1819
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_runtime4.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArtifactDslGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArtifactDslNode, { node: guarded }) }) });
1820
+ }
1821
+ function ArtifactKindIcon({ artifact }) {
1822
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_components5.Icon, { name: resolveArtifactIconName(artifact), size: 13 });
1823
+ }
1824
+ function buildArtifactTree(artifacts) {
1825
+ const root = { name: "", path: "", children: [] };
1826
+ for (const artifact of artifacts) {
1827
+ const segments = artifact.id.split("/").filter(Boolean);
1828
+ let node = root;
1829
+ for (let i = 0; i < segments.length - 1; i++) {
1830
+ const path = segments.slice(0, i + 1).join("/");
1831
+ let child = node.children.find(
1832
+ (item) => item.path === path && item.artifact === void 0
1833
+ );
1834
+ if (!child) {
1835
+ child = { name: segments[i], path, children: [] };
1836
+ node.children.push(child);
1837
+ }
1838
+ node = child;
1839
+ }
1840
+ node.children.push({
1841
+ name: artifact.title,
1842
+ path: artifact.id,
1843
+ artifact,
1844
+ children: []
1845
+ });
1846
+ }
1847
+ const sortLevel = (nodes) => {
1848
+ nodes.sort((a, b) => {
1849
+ const dirDelta = Number(b.artifact === void 0) - Number(a.artifact === void 0);
1850
+ return dirDelta !== 0 ? dirDelta : a.name.localeCompare(b.name);
1851
+ });
1852
+ for (const item of nodes) {
1853
+ sortLevel(item.children);
1854
+ }
1855
+ };
1856
+ sortLevel(root.children);
1857
+ return root.children;
1858
+ }
1859
+ function ArtifactListView({
1860
+ nodes,
1861
+ activeId,
1862
+ onSelect
1863
+ }) {
1864
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "pactor-chat-inspector__list", children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1865
+ ArtifactTreeRow,
1866
+ {
1867
+ node,
1868
+ activeId,
1869
+ onSelect
1870
+ },
1871
+ node.path
1872
+ )) });
1873
+ }
1874
+ function ArtifactTreeRow({
1875
+ node,
1876
+ activeId,
1877
+ onSelect
1878
+ }) {
1879
+ const [open, setOpen] = (0, import_react8.useState)(true);
1880
+ if (node.artifact) {
1881
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1882
+ "button",
1883
+ {
1884
+ type: "button",
1885
+ className: "pactor-chat-inspector__list-item",
1886
+ "data-active": node.artifact.id === activeId,
1887
+ onClick: () => onSelect?.(node.artifact),
1888
+ children: [
1889
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArtifactKindIcon, { artifact: node.artifact }),
1890
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "pactor-chat-inspector__list-item-title", children: node.artifact.title }),
1891
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "pactor-chat-inspector__kind", children: node.artifact.kind })
1892
+ ]
1893
+ }
1894
+ ) });
1895
+ }
1896
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1897
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1898
+ "button",
1899
+ {
1900
+ type: "button",
1901
+ className: "pactor-chat-inspector__list-folder",
1902
+ "aria-expanded": open,
1903
+ "data-open": open,
1904
+ onClick: () => setOpen((value) => !value),
1905
+ children: [
1906
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_components5.Icon, { name: "chevron-right", size: 12 }) }),
1907
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_components5.Icon, { name: "folder", size: 13 }),
1908
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: node.name })
1909
+ ]
1910
+ }
1911
+ ),
1912
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-inspector__list-children", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1913
+ ArtifactListView,
1914
+ {
1915
+ nodes: node.children,
1916
+ activeId,
1917
+ onSelect
1918
+ }
1919
+ ) })
1920
+ ] });
1921
+ }
1922
+ function ArtifactImageContent({ artifact }) {
1923
+ const raw = String(artifact.content).trim();
1924
+ const src = raw.startsWith("<svg") || raw.startsWith("<?xml") ? `data:image/svg+xml;utf8,${encodeURIComponent(raw)}` : raw;
1925
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-inspector__image", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src, alt: artifact.title }) });
1402
1926
  }
1403
1927
  function ArtifactContent({
1404
1928
  artifact,
1405
- runtime
1929
+ runtime,
1930
+ viewers
1406
1931
  }) {
1932
+ const ext = resolveArtifactFileType(artifact);
1933
+ const custom = (ext ? viewers?.[ext] : void 0) ?? viewers?.[artifact.kind];
1934
+ if (custom) {
1935
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: custom(artifact) });
1936
+ }
1937
+ if (isImageArtifact(artifact)) {
1938
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArtifactImageContent, { artifact });
1939
+ }
1407
1940
  switch (artifact.kind) {
1408
1941
  case "markdown":
1409
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_markdown2.MarkdownRenderer, { content: String(artifact.content) });
1942
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_markdown2.MarkdownRenderer, { content: String(artifact.content) });
1410
1943
  case "code":
1411
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "pactor-chat-inspector__code", children: [
1412
- artifact.language ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__code-lang", children: artifact.language }) : null,
1413
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: String(artifact.content) }) })
1944
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "pactor-chat-inspector__code", children: [
1945
+ artifact.language ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "pactor-chat-inspector__code-lang", children: artifact.language }) : null,
1946
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { children: String(artifact.content) }) })
1414
1947
  ] });
1415
1948
  case "text":
1416
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { className: "pactor-chat-inspector__text", children: String(artifact.content) });
1949
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { className: "pactor-chat-inspector__text", children: String(artifact.content) });
1417
1950
  case "dsl":
1418
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactDslContent, { content: artifact.content, runtime });
1951
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ArtifactDslContent, { content: artifact.content, runtime });
1419
1952
  default:
1420
1953
  return null;
1421
1954
  }
1422
1955
  }
1423
1956
  function ArtifactPanel({
1424
1957
  artifact,
1958
+ artifacts,
1959
+ activeArtifactId,
1960
+ onSelectArtifact,
1961
+ viewRequest,
1962
+ onViewRequestConsumed,
1425
1963
  inspector,
1426
1964
  onClose,
1427
1965
  safeRegistry,
1428
1966
  user,
1967
+ artifactViewers,
1429
1968
  className
1430
1969
  }) {
1431
1970
  const runtime = useArtifactRuntime(safeRegistry, user);
1432
- const closeRef = (0, import_react6.useRef)(onClose);
1971
+ const [view, setView] = (0, import_react8.useState)("content");
1972
+ const activeId = activeArtifactId ?? artifact?.id;
1973
+ (0, import_react8.useEffect)(() => {
1974
+ setView("content");
1975
+ }, [activeId]);
1976
+ (0, import_react8.useEffect)(() => {
1977
+ if (viewRequest != null) {
1978
+ setView("list");
1979
+ onViewRequestConsumed?.();
1980
+ }
1981
+ }, [viewRequest]);
1982
+ const showListButton = artifacts !== void 0 && artifacts.length > 0;
1983
+ const showMenu = artifacts !== void 0 && artifacts.length > 0;
1984
+ const toggleListRef = (0, import_react8.useRef)(() => {
1985
+ });
1986
+ toggleListRef.current = () => setView((value) => value === "list" ? "content" : "list");
1987
+ const closeRef = (0, import_react8.useRef)(onClose);
1433
1988
  closeRef.current = onClose;
1434
- (0, import_react6.useEffect)(() => {
1435
- setChatChromeHandlers({ panelClose: () => closeRef.current?.() });
1989
+ const artifactRef = (0, import_react8.useRef)(artifact);
1990
+ artifactRef.current = artifact;
1991
+ (0, import_react8.useEffect)(() => {
1992
+ setChatChromeHandlers({
1993
+ panelClose: () => closeRef.current?.(),
1994
+ panelToggleList: () => toggleListRef.current(),
1995
+ panelDownload: () => {
1996
+ const current = artifactRef.current;
1997
+ if (current) {
1998
+ void downloadArtifact(current);
1999
+ }
2000
+ }
2001
+ });
1436
2002
  }, []);
1437
- const panelRef = (0, import_react6.useRef)(null);
1438
- const [width, setWidth] = (0, import_react6.useState)(null);
2003
+ const panelRef = (0, import_react8.useRef)(null);
2004
+ const [width, setWidth] = (0, import_react8.useState)(null);
1439
2005
  const handleResizeStart = (event) => {
1440
2006
  event.preventDefault();
1441
2007
  const panel = panelRef.current;
@@ -1444,14 +2010,14 @@ function ArtifactPanel({
1444
2010
  }
1445
2011
  const hostRect = panel.parentElement?.getBoundingClientRect();
1446
2012
  const hostWidth = hostRect !== void 0 && hostRect.width > 0 ? hostRect.width : window.innerWidth;
1447
- const maxWidth = hostWidth * 0.7;
2013
+ const maxWidth = hostWidth * 2 / 3;
1448
2014
  const startWidth = panel.getBoundingClientRect().width;
1449
2015
  const startX = event.clientX;
1450
2016
  const prevUserSelect = document.body.style.userSelect;
1451
2017
  document.body.style.userSelect = "none";
1452
2018
  const onMove = (moveEvent) => {
1453
2019
  const next = startWidth + (startX - moveEvent.clientX);
1454
- setWidth(Math.round(Math.min(Math.max(next, 240), maxWidth)));
2020
+ setWidth(Math.round(Math.min(Math.max(next, 480), maxWidth)));
1455
2021
  };
1456
2022
  const onUp = () => {
1457
2023
  document.body.style.userSelect = prevUserSelect;
@@ -1461,66 +2027,88 @@ function ArtifactPanel({
1461
2027
  document.addEventListener("pointermove", onMove);
1462
2028
  document.addEventListener("pointerup", onUp);
1463
2029
  };
1464
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1465
- "aside",
2030
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2031
+ ChatPanelContext.Provider,
1466
2032
  {
1467
- ref: panelRef,
1468
- className: `pactor-chat-inspector${className ? ` ${className}` : ""}`,
1469
- "data-kind": artifact?.kind,
1470
- style: width !== null ? { width: `${width}px` } : void 0,
1471
- children: [
1472
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1473
- "div",
1474
- {
1475
- className: "pactor-chat-inspector__resize",
1476
- role: "separator",
1477
- "aria-orientation": "vertical",
1478
- "aria-label": "\u62D6\u52A8\u8C03\u6574\u9762\u677F\u5BBD\u5EA6",
1479
- onPointerDown: handleResizeStart
1480
- }
1481
- ),
1482
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-inspector__header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1483
- ChromeSlot,
1484
- {
1485
- fragment: buildPanelHeaderFragment({
1486
- title: artifact?.title ?? "\u4EA7\u51FA\u7269",
1487
- kind: artifact?.kind ?? "text"
1488
- }),
1489
- components: PANEL_HEADER_INTERNAL_COMPONENTS,
1490
- fallback: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1491
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__title", children: artifact?.title ?? "\u4EA7\u51FA\u7269" }),
1492
- artifact ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__kind", children: artifact.kind }) : null,
1493
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1494
- "button",
1495
- {
1496
- type: "button",
1497
- className: "pactor-chat-icon-button pactor-chat-inspector__close",
1498
- "aria-label": "\u5173\u95ED\u9762\u677F",
1499
- onClick: onClose,
1500
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "x", size: 16, strokeWidth: 1.7 })
1501
- }
1502
- )
1503
- ] })
1504
- }
1505
- ) }),
1506
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-inspector__body", children: inspector !== void 0 && inspector !== null ? inspector : artifact ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactContent, { artifact, runtime }) : null })
1507
- ]
2033
+ value: {
2034
+ artifacts: artifacts ?? [],
2035
+ activeId,
2036
+ view,
2037
+ onSelectArtifact
2038
+ },
2039
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2040
+ "aside",
2041
+ {
2042
+ ref: panelRef,
2043
+ className: `pactor-chat-inspector${className ? ` ${className}` : ""}`,
2044
+ "data-kind": artifact?.kind,
2045
+ style: width !== null ? { width: `${width}px` } : void 0,
2046
+ children: [
2047
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2048
+ "div",
2049
+ {
2050
+ className: "pactor-chat-inspector__resize",
2051
+ role: "separator",
2052
+ "aria-orientation": "vertical",
2053
+ "aria-label": "\u62D6\u52A8\u8C03\u6574\u9762\u677F\u5BBD\u5EA6",
2054
+ onPointerDown: handleResizeStart
2055
+ }
2056
+ ),
2057
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-inspector__header", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2058
+ ChromeSlot,
2059
+ {
2060
+ fragment: buildPanelHeaderFragment({
2061
+ title: view === "list" ? "\u4EA7\u7269\u76EE\u5F55" : artifact?.title ?? "\u4EA7\u51FA\u7269",
2062
+ kind: artifact?.kind ?? "text",
2063
+ showListButton,
2064
+ showDownload: artifact != null,
2065
+ showMenu,
2066
+ view
2067
+ }),
2068
+ components: PANEL_HEADER_INTERNAL_COMPONENTS,
2069
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2070
+ showListButton ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChatPanelListButton, {}) : null,
2071
+ showMenu ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChatPanelArtifactMenu, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "pactor-chat-inspector__title", children: view === "list" ? "\u4EA7\u7269\u76EE\u5F55" : artifact?.title ?? "\u4EA7\u51FA\u7269" }),
2072
+ view !== "list" && artifact ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "pactor-chat-inspector__kind", children: artifact.kind }) : null,
2073
+ artifact ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChatPanelDownloadButton, {}) : null,
2074
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChatPanelCloseButton, {})
2075
+ ] })
2076
+ }
2077
+ ) }),
2078
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "pactor-chat-inspector__body", children: view === "list" && artifacts !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2079
+ ArtifactListView,
2080
+ {
2081
+ nodes: buildArtifactTree(artifacts),
2082
+ activeId,
2083
+ onSelect: onSelectArtifact
2084
+ }
2085
+ ) : inspector !== void 0 && inspector !== null ? inspector : artifact ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2086
+ ArtifactContent,
2087
+ {
2088
+ artifact,
2089
+ runtime,
2090
+ viewers: artifactViewers
2091
+ }
2092
+ ) : null })
2093
+ ]
2094
+ }
2095
+ )
1508
2096
  }
1509
2097
  );
1510
2098
  }
1511
2099
 
1512
2100
  // src/view/ChatView.tsx
1513
- var import_components10 = require("@pactor-app/ui/components");
2101
+ var import_components11 = require("@pactor-app/ui/components");
1514
2102
  var import_core4 = require("@pactor-app/core");
1515
- var import_react15 = require("react");
2103
+ var import_react17 = require("react");
1516
2104
 
1517
2105
  // src/view/Composer.tsx
1518
- var import_components7 = require("@pactor-app/ui/components");
1519
- var import_react11 = require("react");
2106
+ var import_components8 = require("@pactor-app/ui/components");
2107
+ var import_react13 = require("react");
1520
2108
 
1521
2109
  // src/view/AnchoredPopover.tsx
1522
- var import_react7 = require("react");
1523
- var import_jsx_runtime6 = require("react/jsx-runtime");
2110
+ var import_react9 = require("react");
2111
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1524
2112
  function AnchoredPopover({
1525
2113
  anchorRect,
1526
2114
  onClose,
@@ -1529,9 +2117,9 @@ function AnchoredPopover({
1529
2117
  role = "dialog",
1530
2118
  sideOffset = 8
1531
2119
  }) {
1532
- const ref = (0, import_react7.useRef)(null);
1533
- const [position, setPosition] = (0, import_react7.useState)(null);
1534
- (0, import_react7.useLayoutEffect)(() => {
2120
+ const ref = (0, import_react9.useRef)(null);
2121
+ const [position, setPosition] = (0, import_react9.useState)(null);
2122
+ (0, import_react9.useLayoutEffect)(() => {
1535
2123
  const element = ref.current;
1536
2124
  if (!element) return;
1537
2125
  const height = element.offsetHeight;
@@ -1549,7 +2137,7 @@ function AnchoredPopover({
1549
2137
  }
1550
2138
  );
1551
2139
  }, [anchorRect, sideOffset]);
1552
- (0, import_react7.useEffect)(() => {
2140
+ (0, import_react9.useEffect)(() => {
1553
2141
  const onKeyDown = (event) => {
1554
2142
  if (event.key !== "Escape") return;
1555
2143
  const element = ref.current;
@@ -1577,7 +2165,7 @@ function AnchoredPopover({
1577
2165
  position?.below ? "pactor-chat-popover--below" : "",
1578
2166
  className ?? ""
1579
2167
  ].filter(Boolean).join(" ");
1580
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2168
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1581
2169
  "div",
1582
2170
  {
1583
2171
  ref,
@@ -1596,8 +2184,8 @@ function AnchoredPopover({
1596
2184
  }
1597
2185
 
1598
2186
  // src/view/ModelChip.tsx
1599
- var import_components5 = require("@pactor-app/ui/components");
1600
- var import_react8 = require("react");
2187
+ var import_components6 = require("@pactor-app/ui/components");
2188
+ var import_react10 = require("react");
1601
2189
 
1602
2190
  // src/view/chrome/model-select.tsx
1603
2191
  function buildModelSelectFragment(props) {
@@ -1623,17 +2211,17 @@ function buildModelSelectFragment(props) {
1623
2211
  }
1624
2212
 
1625
2213
  // src/view/ModelChip.tsx
1626
- var import_jsx_runtime7 = require("react/jsx-runtime");
2214
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1627
2215
  function ModelChip({
1628
2216
  models,
1629
2217
  model,
1630
2218
  onModelChange
1631
2219
  }) {
1632
- const chipRef = (0, import_react8.useRef)(null);
1633
- const [anchor, setAnchor] = (0, import_react8.useState)(null);
2220
+ const chipRef = (0, import_react10.useRef)(null);
2221
+ const [anchor, setAnchor] = (0, import_react10.useState)(null);
1634
2222
  const currentOption = models.find((option) => option.value === model) ?? models[0];
1635
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1636
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2223
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2224
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1637
2225
  "button",
1638
2226
  {
1639
2227
  ref: chipRef,
@@ -1643,18 +2231,18 @@ function ModelChip({
1643
2231
  "aria-label": "\u9009\u62E9\u6A21\u578B",
1644
2232
  onClick: () => setAnchor(chipRef.current.getBoundingClientRect()),
1645
2233
  children: [
1646
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-model-chip__label", children: currentOption.label }),
1647
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components5.Icon, { name: "chevrons-up-down", size: 12 })
2234
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-model-chip__label", children: currentOption.label }),
2235
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "chevrons-up-down", size: 12 })
1648
2236
  ]
1649
2237
  }
1650
2238
  ),
1651
- anchor && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2239
+ anchor && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1652
2240
  AnchoredPopover,
1653
2241
  {
1654
2242
  anchorRect: anchor,
1655
2243
  onClose: () => setAnchor(null),
1656
2244
  role: "listbox",
1657
- children: models.map((option) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2245
+ children: models.map((option) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1658
2246
  "button",
1659
2247
  {
1660
2248
  type: "button",
@@ -1666,8 +2254,8 @@ function ModelChip({
1666
2254
  setAnchor(null);
1667
2255
  },
1668
2256
  children: [
1669
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "pactor-chat-popover__item-label", children: option.label }),
1670
- option.value === currentOption.value && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_components5.Icon, { name: "check", size: 13, className: "pactor-chat-popover__item-check" })
2257
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-popover__item-label", children: option.label }),
2258
+ option.value === currentOption.value && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "check", size: 13, className: "pactor-chat-popover__item-check" })
1671
2259
  ]
1672
2260
  },
1673
2261
  option.value
@@ -1681,31 +2269,31 @@ function ModelSelect({
1681
2269
  model,
1682
2270
  onModelChange
1683
2271
  }) {
1684
- const changeRef = (0, import_react8.useRef)(onModelChange);
2272
+ const changeRef = (0, import_react10.useRef)(onModelChange);
1685
2273
  changeRef.current = onModelChange;
1686
- (0, import_react8.useEffect)(() => {
2274
+ (0, import_react10.useEffect)(() => {
1687
2275
  setChatChromeHandlers({
1688
2276
  modelChange: (value) => changeRef.current?.(value)
1689
2277
  });
1690
2278
  }, []);
1691
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2279
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1692
2280
  ChromeSlot,
1693
2281
  {
1694
2282
  fragment: buildModelSelectFragment({ models, model }),
1695
- fallback: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ModelChip, { models, model, onModelChange })
2283
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ModelChip, { models, model, onModelChange })
1696
2284
  }
1697
2285
  );
1698
2286
  }
1699
2287
 
1700
2288
  // src/view/chrome/composer.tsx
1701
- var import_components6 = require("@pactor-app/ui/components");
1702
- var import_react9 = require("react");
1703
- var import_jsx_runtime8 = require("react/jsx-runtime");
1704
- var ComposerBridgeContext = (0, import_react9.createContext)(
2289
+ var import_components7 = require("@pactor-app/ui/components");
2290
+ var import_react11 = require("react");
2291
+ var import_jsx_runtime9 = require("react/jsx-runtime");
2292
+ var ComposerBridgeContext = (0, import_react11.createContext)(
1705
2293
  null
1706
2294
  );
1707
2295
  function useComposerBridge() {
1708
- const bridge = (0, import_react9.useContext)(ComposerBridgeContext);
2296
+ const bridge = (0, import_react11.useContext)(ComposerBridgeContext);
1709
2297
  if (!bridge) {
1710
2298
  throw new Error(
1711
2299
  "[@pactor-app/chat] ChatComposer* \u5185\u90E8\u7EC4\u4EF6\u4EC5\u53EF\u5728 Composer chrome \u7247\u6BB5\u5185\u6E32\u67D3"
@@ -1730,21 +2318,21 @@ function ComposerAttachments({
1730
2318
  if (attachments.length === 0) {
1731
2319
  return null;
1732
2320
  }
1733
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "pactor-chat-composer__attachments", children: attachments.map((attachment) => {
2321
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-composer__attachments", children: attachments.map((attachment) => {
1734
2322
  const isImage = attachment.mimeType.startsWith("image/");
1735
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2323
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1736
2324
  "div",
1737
2325
  {
1738
2326
  className: "pactor-chat-composer__attachment",
1739
2327
  "data-attachment-id": attachment.id,
1740
2328
  onClick: () => onClick?.(attachment),
1741
2329
  children: [
1742
- isImage && attachment.url ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-composer__attachment-thumb", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: attachment.url, alt: attachment.name }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "pactor-chat-composer__attachment-info", children: [
1743
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "file-text", size: 14 }),
1744
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-composer__attachment-name", children: attachment.name }),
1745
- attachment.size !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-composer__attachment-size", children: formatSize(attachment.size) })
2330
+ isImage && attachment.url ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-composer__attachment-thumb", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: attachment.url, alt: attachment.name }) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "pactor-chat-composer__attachment-info", children: [
2331
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_components7.Icon, { name: "file-text", size: 14 }),
2332
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-composer__attachment-name", children: attachment.name }),
2333
+ attachment.size !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-composer__attachment-size", children: formatSize(attachment.size) })
1746
2334
  ] }),
1747
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2335
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1748
2336
  "button",
1749
2337
  {
1750
2338
  type: "button",
@@ -1755,7 +2343,7 @@ function ComposerAttachments({
1755
2343
  event.stopPropagation();
1756
2344
  onRemove(attachment.id);
1757
2345
  },
1758
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "x", size: 12 })
2346
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_components7.Icon, { name: "x", size: 12 })
1759
2347
  }
1760
2348
  )
1761
2349
  ]
@@ -1771,14 +2359,14 @@ function ComposerContextChips({
1771
2359
  if (chips.length === 0) {
1772
2360
  return null;
1773
2361
  }
1774
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "pactor-chat-composer__contexts", children: chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2362
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-composer__contexts", children: chips.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1775
2363
  "span",
1776
2364
  {
1777
2365
  className: "pactor-chat-context-chip",
1778
2366
  "data-context-id": chip.id,
1779
2367
  children: [
1780
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "pactor-chat-context-chip__label", children: chip.label }),
1781
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2368
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "pactor-chat-context-chip__label", children: chip.label }),
2369
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1782
2370
  "button",
1783
2371
  {
1784
2372
  type: "button",
@@ -1786,7 +2374,7 @@ function ComposerContextChips({
1786
2374
  title: `\u79FB\u9664\u4E0A\u4E0B\u6587 ${chip.label}`,
1787
2375
  "aria-label": `\u79FB\u9664\u4E0A\u4E0B\u6587 ${chip.label}`,
1788
2376
  onClick: () => onRemove(chip),
1789
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "x", size: 12 })
2377
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_components7.Icon, { name: "x", size: 12 })
1790
2378
  }
1791
2379
  )
1792
2380
  ]
@@ -1796,8 +2384,8 @@ function ComposerContextChips({
1796
2384
  }
1797
2385
  function ChatComposerHeader() {
1798
2386
  const bridge = useComposerBridge();
1799
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1800
- bridge.attachments !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2387
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2388
+ bridge.attachments !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1801
2389
  ComposerAttachments,
1802
2390
  {
1803
2391
  attachments: bridge.attachments,
@@ -1805,7 +2393,7 @@ function ChatComposerHeader() {
1805
2393
  onClick: bridge.attachmentClick
1806
2394
  }
1807
2395
  ),
1808
- bridge.contextChips !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2396
+ bridge.contextChips !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1809
2397
  ComposerContextChips,
1810
2398
  {
1811
2399
  chips: bridge.contextChips,
@@ -1816,9 +2404,9 @@ function ChatComposerHeader() {
1816
2404
  }
1817
2405
  function ChatComposerToolbarStart() {
1818
2406
  const bridge = useComposerBridge();
1819
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
2407
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1820
2408
  bridge.toolbarStart,
1821
- bridge.showPlus && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2409
+ bridge.showPlus && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1822
2410
  "button",
1823
2411
  {
1824
2412
  type: "button",
@@ -1826,10 +2414,10 @@ function ChatComposerToolbarStart() {
1826
2414
  title: "\u66F4\u591A\u529F\u80FD",
1827
2415
  "aria-label": "\u66F4\u591A\u529F\u80FD",
1828
2416
  onClick: (event) => bridge.plusClick(event.currentTarget.getBoundingClientRect()),
1829
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "plus", size: 14 })
2417
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_components7.Icon, { name: "plus", size: 14 })
1830
2418
  }
1831
2419
  ),
1832
- bridge.showAttach && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2420
+ bridge.showAttach && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1833
2421
  "button",
1834
2422
  {
1835
2423
  type: "button",
@@ -1837,15 +2425,15 @@ function ChatComposerToolbarStart() {
1837
2425
  title: "\u6DFB\u52A0\u9644\u4EF6",
1838
2426
  "aria-label": "\u6DFB\u52A0\u9644\u4EF6",
1839
2427
  onClick: bridge.openFilePicker,
1840
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "paperclip", size: 14 })
2428
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_components7.Icon, { name: "paperclip", size: 14 })
1841
2429
  }
1842
2430
  )
1843
2431
  ] });
1844
2432
  }
1845
2433
  function ChatComposerToolbarEnd() {
1846
2434
  const bridge = useComposerBridge();
1847
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1848
- bridge.modelChipEnabled && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2435
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2436
+ bridge.modelChipEnabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1849
2437
  ModelSelect,
1850
2438
  {
1851
2439
  models: bridge.models,
@@ -1854,7 +2442,7 @@ function ChatComposerToolbarEnd() {
1854
2442
  }
1855
2443
  ),
1856
2444
  bridge.toolbarEnd,
1857
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2445
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1858
2446
  "button",
1859
2447
  {
1860
2448
  type: "button",
@@ -1863,14 +2451,14 @@ function ChatComposerToolbarEnd() {
1863
2451
  "aria-label": bridge.streaming ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001",
1864
2452
  disabled: bridge.streaming ? false : bridge.disabled || !bridge.text.trim(),
1865
2453
  onClick: bridge.streaming ? bridge.stop : bridge.send,
1866
- children: bridge.streaming ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.Icon, { name: "arrow-up", size: 16 })
2454
+ children: bridge.streaming ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_components7.Icon, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_components7.Icon, { name: "arrow-up", size: 16 })
1867
2455
  }
1868
2456
  )
1869
2457
  ] });
1870
2458
  }
1871
2459
  function ChatComposerFooter() {
1872
2460
  const bridge = useComposerBridge();
1873
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: bridge.composerFooter });
2461
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: bridge.composerFooter });
1874
2462
  }
1875
2463
  var COMPOSER_INTERNAL_COMPONENTS = {
1876
2464
  ChatComposerHeader,
@@ -1911,8 +2499,8 @@ function buildComposerFragment(props) {
1911
2499
  }
1912
2500
 
1913
2501
  // src/view/context-sources.tsx
1914
- var import_react10 = require("react");
1915
- var import_jsx_runtime9 = require("react/jsx-runtime");
2502
+ var import_react12 = require("react");
2503
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1916
2504
  function sourceCacheKey(source) {
1917
2505
  return `${source.service ?? ""}:${JSON.stringify(source.params ?? {})}`;
1918
2506
  }
@@ -1947,8 +2535,8 @@ function ContextSourceList({
1947
2535
  cache,
1948
2536
  onSelect
1949
2537
  }) {
1950
- const [resolutions, setResolutions] = (0, import_react10.useState)([]);
1951
- (0, import_react10.useEffect)(() => {
2538
+ const [resolutions, setResolutions] = (0, import_react12.useState)([]);
2539
+ (0, import_react12.useEffect)(() => {
1952
2540
  let cancelled = false;
1953
2541
  const initial = sources.map(
1954
2542
  (source) => source.items ? { status: "ready", items: source.items } : { status: "loading" }
@@ -1993,12 +2581,12 @@ function ContextSourceList({
1993
2581
  totalMatched += items.length;
1994
2582
  return { source, resolution, items };
1995
2583
  });
1996
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "pactor-chat-source-list", children: [
1997
- groups.map(({ source, resolution, items }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "pactor-chat-source-list__group", children: [
1998
- sources.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-source-list__group-label", children: source.label }),
1999
- resolution.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-source-list__hint", children: "\u52A0\u8F7D\u4E2D\u2026" }),
2000
- resolution.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-source-list__error", role: "alert", children: resolution.message }),
2001
- items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2584
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "pactor-chat-source-list", children: [
2585
+ groups.map(({ source, resolution, items }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "pactor-chat-source-list__group", children: [
2586
+ sources.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "pactor-chat-source-list__group-label", children: source.label }),
2587
+ resolution.status === "loading" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "pactor-chat-source-list__hint", children: "\u52A0\u8F7D\u4E2D\u2026" }),
2588
+ resolution.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "pactor-chat-source-list__error", role: "alert", children: resolution.message }),
2589
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2002
2590
  "button",
2003
2591
  {
2004
2592
  type: "button",
@@ -2009,12 +2597,12 @@ function ContextSourceList({
2009
2597
  item.id
2010
2598
  ))
2011
2599
  ] }, `${source.trigger}:${source.label}`)),
2012
- totalMatched === 0 && groups.every(({ resolution }) => resolution.status === "ready") && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pactor-chat-source-list__hint", children: "\u65E0\u5339\u914D\u9879" })
2600
+ totalMatched === 0 && groups.every(({ resolution }) => resolution.status === "ready") && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "pactor-chat-source-list__hint", children: "\u65E0\u5339\u914D\u9879" })
2013
2601
  ] });
2014
2602
  }
2015
2603
 
2016
2604
  // src/view/Composer.tsx
2017
- var import_jsx_runtime10 = require("react/jsx-runtime");
2605
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2018
2606
  var MAX_INPUT_HEIGHT = 200;
2019
2607
  function detectMention(text, caret) {
2020
2608
  const beforeCaret = text.slice(0, caret);
@@ -2048,26 +2636,26 @@ function Composer({
2048
2636
  composerFooter,
2049
2637
  hideToolbar = false
2050
2638
  }) {
2051
- const [text, setText] = (0, import_react11.useState)("");
2052
- const [dragOver, setDragOver] = (0, import_react11.useState)(false);
2053
- const inputRef = (0, import_react11.useRef)(null);
2054
- const fileInputRef = (0, import_react11.useRef)(null);
2055
- const plusButtonRef = (0, import_react11.useRef)(null);
2056
- const composingRef = (0, import_react11.useRef)(false);
2639
+ const [text, setText] = (0, import_react13.useState)("");
2640
+ const [dragOver, setDragOver] = (0, import_react13.useState)(false);
2641
+ const inputRef = (0, import_react13.useRef)(null);
2642
+ const fileInputRef = (0, import_react13.useRef)(null);
2643
+ const plusButtonRef = (0, import_react13.useRef)(null);
2644
+ const composingRef = (0, import_react13.useRef)(false);
2057
2645
  const modelChipEnabled = models !== void 0 && models.length > 0;
2058
- const mentionSources = (0, import_react11.useMemo)(
2646
+ const mentionSources = (0, import_react13.useMemo)(
2059
2647
  () => contextSources?.filter((source) => source.trigger === "@") ?? [],
2060
2648
  [contextSources]
2061
2649
  );
2062
- const plusSources = (0, import_react11.useMemo)(
2650
+ const plusSources = (0, import_react13.useMemo)(
2063
2651
  () => contextSources?.filter((source) => source.trigger === "+") ?? [],
2064
2652
  [contextSources]
2065
2653
  );
2066
2654
  const builtinMentionEnabled = onMentionTrigger === void 0 && mentionSources.length > 0;
2067
2655
  const builtinPlusEnabled = onPlusMenu === void 0 && plusSources.length > 0;
2068
- const [mentionState, setMentionState] = (0, import_react11.useState)(null);
2069
- const [plusAnchor, setPlusAnchor] = (0, import_react11.useState)(null);
2070
- const sourceCacheRef = (0, import_react11.useRef)(null);
2656
+ const [mentionState, setMentionState] = (0, import_react13.useState)(null);
2657
+ const [plusAnchor, setPlusAnchor] = (0, import_react13.useState)(null);
2658
+ const sourceCacheRef = (0, import_react13.useRef)(null);
2071
2659
  if (sourceCacheRef.current === null) {
2072
2660
  sourceCacheRef.current = /* @__PURE__ */ new Map();
2073
2661
  }
@@ -2086,7 +2674,7 @@ function Composer({
2086
2674
  setMentionState(null);
2087
2675
  setPlusAnchor(null);
2088
2676
  };
2089
- (0, import_react11.useLayoutEffect)(() => {
2677
+ (0, import_react13.useLayoutEffect)(() => {
2090
2678
  const input = inputRef.current;
2091
2679
  if (input) {
2092
2680
  input.style.height = "auto";
@@ -2101,9 +2689,9 @@ function Composer({
2101
2689
  onSend?.(value);
2102
2690
  setText("");
2103
2691
  };
2104
- const sendRef = (0, import_react11.useRef)(send);
2692
+ const sendRef = (0, import_react13.useRef)(send);
2105
2693
  sendRef.current = send;
2106
- (0, import_react11.useLayoutEffect)(() => {
2694
+ (0, import_react13.useLayoutEffect)(() => {
2107
2695
  setChatChromeHandlers({ composerSubmit: () => sendRef.current() });
2108
2696
  }, []);
2109
2697
  const syncText = (value, caret, rect) => {
@@ -2161,7 +2749,7 @@ function Composer({
2161
2749
  }
2162
2750
  };
2163
2751
  const showToolbar = !hideToolbar && (toolbarStart !== void 0 || toolbarEnd !== void 0 || onPlusMenu !== void 0 || builtinPlusEnabled || onAttachFiles !== void 0 || modelChipEnabled);
2164
- const fileInputNode = onAttachFiles ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2752
+ const fileInputNode = onAttachFiles ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2165
2753
  "input",
2166
2754
  {
2167
2755
  ref: fileInputRef,
@@ -2177,14 +2765,14 @@ function Composer({
2177
2765
  }
2178
2766
  }
2179
2767
  ) : null;
2180
- const popoverNodes = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2181
- builtinMentionEnabled && mentionState && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2768
+ const popoverNodes = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2769
+ builtinMentionEnabled && mentionState && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2182
2770
  AnchoredPopover,
2183
2771
  {
2184
2772
  anchorRect: mentionState.anchorRect,
2185
2773
  onClose: () => setMentionState(null),
2186
2774
  role: "listbox",
2187
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2775
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2188
2776
  ContextSourceList,
2189
2777
  {
2190
2778
  sources: mentionSources,
@@ -2196,13 +2784,13 @@ function Composer({
2196
2784
  )
2197
2785
  }
2198
2786
  ),
2199
- builtinPlusEnabled && plusAnchor && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2787
+ builtinPlusEnabled && plusAnchor && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2200
2788
  AnchoredPopover,
2201
2789
  {
2202
2790
  anchorRect: plusAnchor,
2203
2791
  onClose: () => setPlusAnchor(null),
2204
2792
  role: "listbox",
2205
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2793
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2206
2794
  ContextSourceList,
2207
2795
  {
2208
2796
  sources: plusSources,
@@ -2215,13 +2803,13 @@ function Composer({
2215
2803
  }
2216
2804
  )
2217
2805
  ] });
2218
- const fallback = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2806
+ const fallback = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2219
2807
  "div",
2220
2808
  {
2221
2809
  className: `pactor-chat-composer${dragOver ? " pactor-chat-composer--dragover" : ""}`,
2222
2810
  ...dragHandlers,
2223
2811
  children: [
2224
- attachments !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2812
+ attachments !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2225
2813
  ComposerAttachments,
2226
2814
  {
2227
2815
  attachments,
@@ -2229,7 +2817,7 @@ function Composer({
2229
2817
  onClick: onAttachmentClick
2230
2818
  }
2231
2819
  ),
2232
- contextChips !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2820
+ contextChips !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2233
2821
  ComposerContextChips,
2234
2822
  {
2235
2823
  chips: contextChips,
@@ -2240,8 +2828,8 @@ function Composer({
2240
2828
  )
2241
2829
  }
2242
2830
  ),
2243
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "pactor-chat-composer__row", children: [
2244
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2831
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "pactor-chat-composer__row", children: [
2832
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2245
2833
  "textarea",
2246
2834
  {
2247
2835
  ref: inputRef,
@@ -2273,7 +2861,7 @@ function Composer({
2273
2861
  }
2274
2862
  }
2275
2863
  ),
2276
- hideToolbar ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2864
+ hideToolbar ? null : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2277
2865
  "button",
2278
2866
  {
2279
2867
  type: "button",
@@ -2282,14 +2870,14 @@ function Composer({
2282
2870
  "aria-label": streaming ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001",
2283
2871
  disabled: streaming ? false : disabled || !text.trim(),
2284
2872
  onClick: streaming ? onStop : send,
2285
- children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_components7.Icon, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_components7.Icon, { name: "arrow-up", size: 16 })
2873
+ children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components8.Icon, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components8.Icon, { name: "arrow-up", size: 16 })
2286
2874
  }
2287
2875
  )
2288
2876
  ] }),
2289
- showToolbar && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "pactor-chat-composer__toolbar", children: [
2877
+ showToolbar && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "pactor-chat-composer__toolbar", children: [
2290
2878
  toolbarStart,
2291
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "pactor-chat-composer__toolbar-spacer" }),
2292
- (onPlusMenu || builtinPlusEnabled) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2879
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "pactor-chat-composer__toolbar-spacer" }),
2880
+ (onPlusMenu || builtinPlusEnabled) && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2293
2881
  "button",
2294
2882
  {
2295
2883
  ref: plusButtonRef,
@@ -2305,10 +2893,10 @@ function Composer({
2305
2893
  setPlusAnchor(rect);
2306
2894
  }
2307
2895
  },
2308
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_components7.Icon, { name: "plus", size: 14 })
2896
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components8.Icon, { name: "plus", size: 14 })
2309
2897
  }
2310
2898
  ),
2311
- onAttachFiles && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2899
+ onAttachFiles && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2312
2900
  "button",
2313
2901
  {
2314
2902
  type: "button",
@@ -2316,10 +2904,10 @@ function Composer({
2316
2904
  title: "\u6DFB\u52A0\u9644\u4EF6",
2317
2905
  "aria-label": "\u6DFB\u52A0\u9644\u4EF6",
2318
2906
  onClick: () => fileInputRef.current?.click(),
2319
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_components7.Icon, { name: "paperclip", size: 14 })
2907
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components8.Icon, { name: "paperclip", size: 14 })
2320
2908
  }
2321
2909
  ),
2322
- modelChipEnabled && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2910
+ modelChipEnabled && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2323
2911
  ModelSelect,
2324
2912
  {
2325
2913
  models,
@@ -2329,7 +2917,7 @@ function Composer({
2329
2917
  ),
2330
2918
  toolbarEnd
2331
2919
  ] }),
2332
- composerFooter !== void 0 && composerFooter !== null ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "pactor-chat-composer__footer", children: composerFooter }) : null,
2920
+ composerFooter !== void 0 && composerFooter !== null ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-composer__footer", children: composerFooter }) : null,
2333
2921
  fileInputNode,
2334
2922
  popoverNodes
2335
2923
  ]
@@ -2368,7 +2956,7 @@ function Composer({
2368
2956
  ),
2369
2957
  modelChange: onModelChange
2370
2958
  };
2371
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2959
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2372
2960
  ChromeSlot,
2373
2961
  {
2374
2962
  fragment: buildComposerFragment({
@@ -2379,7 +2967,7 @@ function Composer({
2379
2967
  }),
2380
2968
  components: COMPOSER_INTERNAL_COMPONENTS,
2381
2969
  fallback,
2382
- wrap: (children) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ComposerBridgeContext.Provider, { value: bridge, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2970
+ wrap: (children) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ComposerBridgeContext.Provider, { value: bridge, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2383
2971
  "div",
2384
2972
  {
2385
2973
  className: `pactor-chat-composer pactor-chat-composer--dsl${dragOver ? " pactor-chat-composer--dragover" : ""}`,
@@ -2467,7 +3055,7 @@ function buildEmptyStateFragment(props) {
2467
3055
  }
2468
3056
 
2469
3057
  // src/view/EmptyState.tsx
2470
- var import_jsx_runtime11 = require("react/jsx-runtime");
3058
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2471
3059
  var DEFAULT_CHAT_GREETING = "\u4F60\u597D\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u4F60\uFF1F";
2472
3060
  function suggestionLabel2(suggestion) {
2473
3061
  return typeof suggestion === "string" ? suggestion : suggestion.label;
@@ -2481,10 +3069,10 @@ function EmptyStateFallback({
2481
3069
  onSuggestionClick,
2482
3070
  greeting
2483
3071
  }) {
2484
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2485
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-empty__greeting", children: greeting }),
2486
- subtitle && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-empty__subtitle", children: subtitle }),
2487
- suggestions && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-empty__suggestions", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3072
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3073
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-empty__greeting", children: greeting }),
3074
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-empty__subtitle", children: subtitle }),
3075
+ suggestions && suggestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-empty__suggestions", children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2488
3076
  "button",
2489
3077
  {
2490
3078
  type: "button",
@@ -2502,11 +3090,11 @@ function EmptyState({
2502
3090
  suggestions,
2503
3091
  onSuggestionClick
2504
3092
  }) {
2505
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "pactor-chat-empty", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3093
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-empty", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2506
3094
  ChromeSlot,
2507
3095
  {
2508
3096
  fragment: buildEmptyStateFragment({ greeting, subtitle, suggestions }),
2509
- fallback: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3097
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2510
3098
  EmptyStateFallback,
2511
3099
  {
2512
3100
  greeting,
@@ -2520,16 +3108,20 @@ function EmptyState({
2520
3108
  }
2521
3109
 
2522
3110
  // src/view/MessageBubble.tsx
2523
- var import_react14 = require("react");
3111
+ var import_react16 = require("react");
2524
3112
 
2525
3113
  // src/view/ActivityTrace.tsx
2526
- var import_components8 = require("@pactor-app/ui/components");
3114
+ var import_components9 = require("@pactor-app/ui/components");
2527
3115
  var import_markdown3 = require("@pactor-app/markdown");
2528
- var import_react12 = require("react");
2529
- var import_jsx_runtime12 = require("react/jsx-runtime");
3116
+ var import_react14 = require("react");
3117
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2530
3118
  function usePinnedCollapse(autoOpen) {
2531
- const [state, setState] = (0, import_react12.useState)("auto");
2532
- const open = state === "auto" ? autoOpen : state === "pinned-open";
3119
+ const [state, setState] = (0, import_react14.useState)("auto");
3120
+ const autoCollapsedRef = (0, import_react14.useRef)(false);
3121
+ if (state === "auto" && !autoOpen) {
3122
+ autoCollapsedRef.current = true;
3123
+ }
3124
+ const open = state === "auto" ? autoOpen && !autoCollapsedRef.current : state === "pinned-open";
2533
3125
  const toggle = () => {
2534
3126
  setState(open ? "pinned-closed" : "pinned-open");
2535
3127
  };
@@ -2554,8 +3146,8 @@ function statusText(message, mode, toolCount) {
2554
3146
  }
2555
3147
  function useElapsedSeconds(startedAt, active) {
2556
3148
  const compute = () => Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
2557
- const [elapsed, setElapsed] = (0, import_react12.useState)(compute);
2558
- (0, import_react12.useEffect)(() => {
3149
+ const [elapsed, setElapsed] = (0, import_react14.useState)(compute);
3150
+ (0, import_react14.useEffect)(() => {
2559
3151
  if (!active) {
2560
3152
  setElapsed(compute());
2561
3153
  return;
@@ -2569,15 +3161,15 @@ function useElapsedSeconds(startedAt, active) {
2569
3161
  }
2570
3162
  function TraceStatusIcon({ status }) {
2571
3163
  if (status === "running") {
2572
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" });
3164
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" });
2573
3165
  }
2574
3166
  if (status === "success") {
2575
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "check-circle", size: 13 });
3167
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "check-circle", size: 13 });
2576
3168
  }
2577
3169
  if (status === "failed") {
2578
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "x-circle", size: 13 });
3170
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "x-circle", size: 13 });
2579
3171
  }
2580
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "clock", size: 13 });
3172
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "clock", size: 13 });
2581
3173
  }
2582
3174
  function summarizeArgs(args) {
2583
3175
  if (args === void 0) {
@@ -2606,16 +3198,16 @@ function formatDetail(value) {
2606
3198
  }
2607
3199
  }
2608
3200
  function TraceRow({ entry }) {
2609
- const [open, setOpen] = (0, import_react12.useState)(false);
3201
+ const [open, setOpen] = (0, import_react14.useState)(false);
2610
3202
  const summary = summarizeArgs(entry.args);
2611
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3203
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2612
3204
  "div",
2613
3205
  {
2614
3206
  className: "pactor-chat-trace__entry",
2615
3207
  "data-status": entry.status,
2616
3208
  "data-open": open,
2617
3209
  children: [
2618
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3210
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2619
3211
  "button",
2620
3212
  {
2621
3213
  type: "button",
@@ -2623,16 +3215,16 @@ function TraceRow({ entry }) {
2623
3215
  "aria-expanded": open,
2624
3216
  onClick: () => setOpen((value) => !value),
2625
3217
  children: [
2626
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "chevron-right", size: 12 }) }),
2627
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TraceStatusIcon, { status: entry.status }) }),
2628
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__entry-title", children: entry.title ?? entry.name }),
2629
- summary && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__entry-args", children: summary })
3218
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "chevron-right", size: 12 }) }),
3219
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "pactor-chat-trace__entry-icon", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TraceStatusIcon, { status: entry.status }) }),
3220
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "pactor-chat-trace__entry-title", children: entry.title ?? entry.name }),
3221
+ summary && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "pactor-chat-trace__entry-args", children: summary })
2630
3222
  ]
2631
3223
  }
2632
3224
  ),
2633
- open && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "pactor-chat-trace__entry-body", children: [
2634
- entry.args !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { className: "pactor-chat-trace__entry-detail", children: formatDetail(entry.args) }),
2635
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { className: "pactor-chat-trace__entry-detail", children: entry.status === "failed" ? entry.error ?? "\u6267\u884C\u5931\u8D25" : formatDetail(entry.result) })
3225
+ open && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "pactor-chat-trace__entry-body", children: [
3226
+ entry.args !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("pre", { className: "pactor-chat-trace__entry-detail", children: formatDetail(entry.args) }),
3227
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("pre", { className: "pactor-chat-trace__entry-detail", children: entry.status === "failed" ? entry.error ?? "\u6267\u884C\u5931\u8D25" : formatDetail(entry.result) })
2636
3228
  ] })
2637
3229
  ]
2638
3230
  }
@@ -2645,14 +3237,14 @@ function ThinkingCard({
2645
3237
  const partStreaming = part.status === "streaming";
2646
3238
  const shown = useThrottledValue(part.content, 120, partStreaming);
2647
3239
  const { open, toggle } = usePinnedCollapse(partStreaming && !answerPhase);
2648
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3240
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2649
3241
  "div",
2650
3242
  {
2651
3243
  className: "pactor-chat-thinking",
2652
3244
  "data-open": open,
2653
3245
  "data-streaming": partStreaming,
2654
3246
  children: [
2655
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3247
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2656
3248
  "button",
2657
3249
  {
2658
3250
  type: "button",
@@ -2660,13 +3252,13 @@ function ThinkingCard({
2660
3252
  "aria-expanded": open,
2661
3253
  onClick: toggle,
2662
3254
  children: [
2663
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "chevron-right", size: 12 }) }),
2664
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-thinking__icon", children: partStreaming ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "brain", size: 13 }) }),
2665
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "\u601D\u8003\u8FC7\u7A0B" })
3255
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "chevron-right", size: 12 }) }),
3256
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "pactor-chat-thinking__icon", children: partStreaming ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "brain", size: 13 }) }),
3257
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "\u601D\u8003\u8FC7\u7A0B" })
2666
3258
  ]
2667
3259
  }
2668
3260
  ),
2669
- open && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-thinking__body", children: partStreaming ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-thinking__text", children: shown }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_markdown3.MarkdownRenderer, { content: part.content }) })
3261
+ open && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pactor-chat-thinking__body", children: partStreaming ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pactor-chat-thinking__text", children: shown }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_markdown3.MarkdownRenderer, { content: part.content }) })
2670
3262
  ]
2671
3263
  }
2672
3264
  );
@@ -2681,7 +3273,7 @@ function ActivityTrace({ message }) {
2681
3273
  const answerPhase = mode === "responding" || !streaming;
2682
3274
  const { open, toggle } = usePinnedCollapse(!answerPhase);
2683
3275
  const collapsible = entries.length > 0 || thinkingParts.length > 0;
2684
- const mountTimeRef = (0, import_react12.useRef)(0);
3276
+ const mountTimeRef = (0, import_react14.useRef)(0);
2685
3277
  if (mountTimeRef.current === 0) {
2686
3278
  mountTimeRef.current = Date.now();
2687
3279
  }
@@ -2689,23 +3281,23 @@ function ActivityTrace({ message }) {
2689
3281
  message.createdAt ?? mountTimeRef.current,
2690
3282
  streaming
2691
3283
  );
2692
- const header = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2693
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__status-icon", children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "check-circle", size: 13 }) }),
2694
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3284
+ const header = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
3285
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "pactor-chat-trace__status-icon", children: streaming ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "check-circle", size: 13 }) }),
3286
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2695
3287
  "span",
2696
3288
  {
2697
3289
  className: `pactor-chat-trace__status-text${streaming ? " pactor-chat-trace__status-text--breathing" : ""}`,
2698
3290
  children: statusText(message, mode, entries.length)
2699
3291
  }
2700
3292
  ),
2701
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "pactor-chat-trace__elapsed", children: [
3293
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "pactor-chat-trace__elapsed", children: [
2702
3294
  elapsed,
2703
3295
  "s"
2704
3296
  ] }),
2705
- collapsible && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_components8.Icon, { name: "chevron-right", size: 12 }) })
3297
+ collapsible && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "chevron-right", size: 12 }) })
2706
3298
  ] });
2707
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "pactor-chat-trace", "data-open": open, children: [
2708
- collapsible ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3299
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "pactor-chat-trace", "data-open": open, children: [
3300
+ collapsible ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2709
3301
  "button",
2710
3302
  {
2711
3303
  type: "button",
@@ -2714,10 +3306,10 @@ function ActivityTrace({ message }) {
2714
3306
  onClick: toggle,
2715
3307
  children: header
2716
3308
  }
2717
- ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-trace__header", children: header }),
2718
- open && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2719
- entries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pactor-chat-trace__entries", children: entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TraceRow, { entry }, entry.callIndex)) }),
2720
- thinkingParts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3309
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pactor-chat-trace__header", children: header }),
3310
+ open && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
3311
+ entries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "pactor-chat-trace__entries", children: entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TraceRow, { entry }, entry.callIndex)) }),
3312
+ thinkingParts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2721
3313
  ThinkingCard,
2722
3314
  {
2723
3315
  part,
@@ -2730,8 +3322,8 @@ function ActivityTrace({ message }) {
2730
3322
  }
2731
3323
 
2732
3324
  // src/view/MessageActions.tsx
2733
- var import_components9 = require("@pactor-app/ui/components");
2734
- var import_react13 = require("react");
3325
+ var import_components10 = require("@pactor-app/ui/components");
3326
+ var import_react15 = require("react");
2735
3327
 
2736
3328
  // src/view/chrome/message-actions.tsx
2737
3329
  function buildMessageActionsFragment(args) {
@@ -2784,7 +3376,7 @@ function buildMessageActionsFragment(args) {
2784
3376
  }
2785
3377
 
2786
3378
  // src/view/MessageActions.tsx
2787
- var import_jsx_runtime13 = require("react/jsx-runtime");
3379
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2788
3380
  var BUILTIN_ACTION_DEFAULTS = {
2789
3381
  copy: {
2790
3382
  label: "\u590D\u5236",
@@ -2857,13 +3449,13 @@ function MessageActions({
2857
3449
  callService,
2858
3450
  onMessageAction
2859
3451
  }) {
2860
- const [copied, setCopied] = (0, import_react13.useState)(false);
2861
- const dispatchRef = (0, import_react13.useRef)(
3452
+ const [copied, setCopied] = (0, import_react15.useState)(false);
3453
+ const dispatchRef = (0, import_react15.useRef)(
2862
3454
  () => {
2863
3455
  }
2864
3456
  );
2865
- const visibleActionsRef = (0, import_react13.useRef)([]);
2866
- (0, import_react13.useEffect)(() => {
3457
+ const visibleActionsRef = (0, import_react15.useRef)([]);
3458
+ (0, import_react15.useEffect)(() => {
2867
3459
  setMessageActionDispatcher(message.id, (actionId) => {
2868
3460
  const action = visibleActionsRef.current.find((a) => a.id === actionId);
2869
3461
  if (action) {
@@ -2937,7 +3529,7 @@ function MessageActions({
2937
3529
  const copiedFeedback = action.builtin === "copy" && copied;
2938
3530
  const label = copiedFeedback ? "\u5DF2\u590D\u5236" : action.label;
2939
3531
  const iconName = copiedFeedback ? "check" : action.icon;
2940
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3532
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2941
3533
  "button",
2942
3534
  {
2943
3535
  type: "button",
@@ -2946,16 +3538,16 @@ function MessageActions({
2946
3538
  "aria-label": label,
2947
3539
  "data-message-action": action.id,
2948
3540
  onClick: () => dispatch(action),
2949
- children: iconName !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: iconName, size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components9.Icon, { name: "help-circle", size: 14 })
3541
+ children: iconName !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_components10.Icon, { name: iconName, size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_components10.Icon, { name: "help-circle", size: 14 })
2950
3542
  },
2951
3543
  action.id
2952
3544
  );
2953
3545
  });
2954
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3546
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2955
3547
  "div",
2956
3548
  {
2957
3549
  className: `pactor-chat-message__actions${forceVisible ? " pactor-chat-message__actions--visible" : ""}`,
2958
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3550
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2959
3551
  ChromeSlot,
2960
3552
  {
2961
3553
  fragment: buildMessageActionsFragment({
@@ -2971,13 +3563,38 @@ function MessageActions({
2971
3563
  }
2972
3564
 
2973
3565
  // src/view/MessageBubble.tsx
2974
- var import_jsx_runtime14 = require("react/jsx-runtime");
3566
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3567
+ function groupArtifactParts(parts) {
3568
+ const segments = [];
3569
+ for (const part of parts) {
3570
+ if (part.type === "artifact" && part.artifact) {
3571
+ const last = segments[segments.length - 1];
3572
+ if (Array.isArray(last)) {
3573
+ last.push(part.artifact);
3574
+ } else {
3575
+ segments.push([part.artifact]);
3576
+ }
3577
+ } else {
3578
+ segments.push(part);
3579
+ }
3580
+ }
3581
+ return segments;
3582
+ }
3583
+ function formatMessageTime(ts, now = Date.now()) {
3584
+ const date = new Date(ts);
3585
+ const hh = String(date.getHours()).padStart(2, "0");
3586
+ const mm = String(date.getMinutes()).padStart(2, "0");
3587
+ if (new Date(now).toDateString() === date.toDateString()) {
3588
+ return `${hh}:${mm}`;
3589
+ }
3590
+ return `${date.getMonth() + 1}\u6708${date.getDate()}\u65E5 ${hh}:${mm}`;
3591
+ }
2975
3592
  function MessageEditForm({
2976
3593
  initialText,
2977
3594
  onConfirm,
2978
3595
  onCancel
2979
3596
  }) {
2980
- const [text, setText] = (0, import_react14.useState)(initialText);
3597
+ const [text, setText] = (0, import_react16.useState)(initialText);
2981
3598
  const confirm = () => {
2982
3599
  const value = text.trim();
2983
3600
  if (!value) {
@@ -2985,8 +3602,8 @@ function MessageEditForm({
2985
3602
  }
2986
3603
  onConfirm(value);
2987
3604
  };
2988
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pactor-chat-message__edit", children: [
2989
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3605
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-message__edit", children: [
3606
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2990
3607
  "textarea",
2991
3608
  {
2992
3609
  className: "pactor-chat-message__edit-input",
@@ -3004,8 +3621,8 @@ function MessageEditForm({
3004
3621
  }
3005
3622
  }
3006
3623
  ),
3007
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pactor-chat-message__edit-actions", children: [
3008
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3624
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-message__edit-actions", children: [
3625
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3009
3626
  "button",
3010
3627
  {
3011
3628
  type: "button",
@@ -3015,7 +3632,7 @@ function MessageEditForm({
3015
3632
  children: "\u53D1\u9001"
3016
3633
  }
3017
3634
  ),
3018
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3635
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3019
3636
  "button",
3020
3637
  {
3021
3638
  type: "button",
@@ -3038,10 +3655,9 @@ function MessageBubble({
3038
3655
  onEditResend,
3039
3656
  messageActions,
3040
3657
  callService,
3041
- onMessageAction,
3042
- onOpenArtifact
3658
+ onMessageAction
3043
3659
  }) {
3044
- const [editing, setEditing] = (0, import_react14.useState)(false);
3660
+ const [editing, setEditing] = (0, import_react16.useState)(false);
3045
3661
  const bubbleScope = {
3046
3662
  conversation: scope?.conversation ?? {},
3047
3663
  message: { id: message.id },
@@ -3049,21 +3665,22 @@ function MessageBubble({
3049
3665
  };
3050
3666
  const streaming = message.status === "streaming";
3051
3667
  const live = message.status === "pending" || message.status === "streaming";
3052
- const wasLiveRef = (0, import_react14.useRef)(live);
3668
+ const wasLiveRef = (0, import_react16.useRef)(live);
3053
3669
  if (live) {
3054
3670
  wasLiveRef.current = true;
3055
3671
  }
3056
3672
  const showTrace = message.role === "assistant" && (wasLiveRef.current || message.parts.some(
3057
3673
  (part) => part.type === "thinking" || part.type === "tool-call" || part.type === "tool-result"
3058
3674
  ));
3059
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3675
+ const showTime = message.role === "assistant" && !live && message.createdAt !== void 0;
3676
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3060
3677
  "div",
3061
3678
  {
3062
3679
  className: `pactor-chat-message pactor-chat-message--${message.role}`,
3063
3680
  "data-message-id": message.id,
3064
3681
  "data-message-status": message.status,
3065
3682
  children: [
3066
- editing ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3683
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3067
3684
  MessageEditForm,
3068
3685
  {
3069
3686
  initialText: collectMessageText(message),
@@ -3073,21 +3690,53 @@ function MessageBubble({
3073
3690
  },
3074
3691
  onCancel: () => setEditing(false)
3075
3692
  }
3076
- ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "pactor-chat-message__body", children: [
3077
- showTrace && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ActivityTrace, { message }),
3078
- message.parts.length === 0 && message.status === "pending" && message.role !== "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "pactor-chat-message__pending", children: "\u2026" }) : message.parts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3079
- PartRenderer,
3080
- {
3081
- part,
3082
- runtime,
3083
- scope: bubbleScope,
3084
- streaming,
3085
- onOpenArtifact
3086
- },
3087
- part.id ?? index
3088
- ))
3693
+ ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-message__body", children: [
3694
+ showTrace && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ActivityTrace, { message }),
3695
+ message.parts.length === 0 && message.status === "pending" && message.role !== "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "pactor-chat-message__pending", children: "\u2026" }) : groupArtifactParts(message.parts).map(
3696
+ (segment, index) => Array.isArray(segment) ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3697
+ ChromeSlot,
3698
+ {
3699
+ fragment: buildArtifactCardGroupFragment(segment),
3700
+ components: ARTIFACT_CARD_INTERNAL_COMPONENTS,
3701
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ChatArtifactCardGroup, { artifacts: segment })
3702
+ },
3703
+ `artifacts:${segment[0].id}`
3704
+ ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3705
+ PartRenderer,
3706
+ {
3707
+ part: segment,
3708
+ runtime,
3709
+ scope: bubbleScope,
3710
+ streaming
3711
+ },
3712
+ segment.id ?? index
3713
+ )
3714
+ )
3089
3715
  ] }),
3090
- !editing && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3716
+ !editing && (showTime ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-message__footer", children: [
3717
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3718
+ MessageActions,
3719
+ {
3720
+ message,
3721
+ actions: messageActions,
3722
+ showRegenerate,
3723
+ onRegenerate,
3724
+ onRetry,
3725
+ onEdit: onEditResend ? () => setEditing(true) : void 0,
3726
+ forceVisible: message.status === "error",
3727
+ callService,
3728
+ onMessageAction
3729
+ }
3730
+ ),
3731
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3732
+ "span",
3733
+ {
3734
+ className: "pactor-chat-message__time",
3735
+ title: new Date(message.createdAt).toLocaleString(),
3736
+ children: formatMessageTime(message.createdAt)
3737
+ }
3738
+ )
3739
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3091
3740
  MessageActions,
3092
3741
  {
3093
3742
  message,
@@ -3100,14 +3749,14 @@ function MessageBubble({
3100
3749
  callService,
3101
3750
  onMessageAction
3102
3751
  }
3103
- )
3752
+ ))
3104
3753
  ]
3105
3754
  }
3106
3755
  );
3107
3756
  }
3108
3757
 
3109
3758
  // src/view/ChatView.tsx
3110
- var import_jsx_runtime15 = require("react/jsx-runtime");
3759
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3111
3760
  function lastContentLength(messages) {
3112
3761
  const last = messages[messages.length - 1];
3113
3762
  if (!last) {
@@ -3135,6 +3784,7 @@ function ChatView({
3135
3784
  className,
3136
3785
  onArtifact,
3137
3786
  onOpenArtifact,
3787
+ onOpenArtifactList,
3138
3788
  attachments,
3139
3789
  onAttachmentsChange,
3140
3790
  onAttachFiles,
@@ -3156,14 +3806,14 @@ function ChatView({
3156
3806
  onMessageAction,
3157
3807
  composerBottom
3158
3808
  }) {
3159
- const messages = (0, import_react15.useSyncExternalStore)(
3809
+ const messages = (0, import_react17.useSyncExternalStore)(
3160
3810
  (listener) => store.subscribe(listener),
3161
3811
  () => store.getMessages()
3162
3812
  );
3163
3813
  const isStreaming = messages.some(
3164
3814
  (message) => message.status === "pending" || message.status === "streaming"
3165
3815
  );
3166
- const runtime = (0, import_react15.useMemo)(() => {
3816
+ const runtime = (0, import_react17.useMemo)(() => {
3167
3817
  if (!safeRegistry) {
3168
3818
  return void 0;
3169
3819
  }
@@ -3180,12 +3830,12 @@ function ChatView({
3180
3830
  user
3181
3831
  });
3182
3832
  }, [safeRegistry, capabilities, permission, confirm, user]);
3183
- const scope = (0, import_react15.useMemo)(
3833
+ const scope = (0, import_react17.useMemo)(
3184
3834
  () => ({ conversation: { id: conversationId }, params }),
3185
3835
  [conversationId, params]
3186
3836
  );
3187
- const notifiedArtifactsRef = (0, import_react15.useRef)(/* @__PURE__ */ new Set());
3188
- (0, import_react15.useEffect)(() => {
3837
+ const notifiedArtifactsRef = (0, import_react17.useRef)(/* @__PURE__ */ new Set());
3838
+ (0, import_react17.useEffect)(() => {
3189
3839
  if (!onArtifact) {
3190
3840
  return;
3191
3841
  }
@@ -3204,7 +3854,7 @@ function ChatView({
3204
3854
  });
3205
3855
  }
3206
3856
  }, [messages, onArtifact]);
3207
- const abortRef = (0, import_react15.useRef)(null);
3857
+ const abortRef = (0, import_react17.useRef)(null);
3208
3858
  const { containerRef, isFollowing, scrollToBottom } = useChatAutoScroll({
3209
3859
  messageCount: messages.length,
3210
3860
  lastContentLength: lastContentLength(messages),
@@ -3215,7 +3865,7 @@ function ChatView({
3215
3865
  model,
3216
3866
  onModelChange
3217
3867
  });
3218
- const [contextChips, setContextChips] = (0, import_react15.useState)([]);
3868
+ const [contextChips, setContextChips] = (0, import_react17.useState)([]);
3219
3869
  const handleContextChipsChange = (chips) => {
3220
3870
  setContextChips(chips);
3221
3871
  onContextsChange?.(chips);
@@ -3247,14 +3897,17 @@ function ChatView({
3247
3897
  abortRef.current?.abort();
3248
3898
  abortRef.current = null;
3249
3899
  };
3250
- const handleSendRef = (0, import_react15.useRef)(handleSend);
3900
+ const handleSendRef = (0, import_react17.useRef)(handleSend);
3251
3901
  handleSendRef.current = handleSend;
3252
- const onOpenArtifactRef = (0, import_react15.useRef)(onOpenArtifact);
3902
+ const onOpenArtifactRef = (0, import_react17.useRef)(onOpenArtifact);
3253
3903
  onOpenArtifactRef.current = onOpenArtifact;
3254
- (0, import_react15.useEffect)(() => {
3904
+ const onOpenArtifactListRef = (0, import_react17.useRef)(onOpenArtifactList);
3905
+ onOpenArtifactListRef.current = onOpenArtifactList;
3906
+ (0, import_react17.useEffect)(() => {
3255
3907
  setChatChromeHandlers({
3256
3908
  sendSuggestion: (text) => handleSendRef.current(text),
3257
- openArtifact: (artifact) => onOpenArtifactRef.current?.(artifact)
3909
+ openArtifact: (artifact) => onOpenArtifactRef.current?.(artifact),
3910
+ openArtifactList: () => onOpenArtifactListRef.current?.()
3258
3911
  });
3259
3912
  }, []);
3260
3913
  const handleRegenerate = () => {
@@ -3274,7 +3927,7 @@ function ChatView({
3274
3927
  const lastMessage = messages[messages.length - 1];
3275
3928
  const regenerableMessageId = lastMessage?.role === "assistant" && lastMessage.status === "completed" ? lastMessage.id : void 0;
3276
3929
  const isEmpty = messages.length === 0;
3277
- const composerNode = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3930
+ const composerNode = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3278
3931
  Composer,
3279
3932
  {
3280
3933
  onSend: handleSend,
@@ -3299,18 +3952,18 @@ function ChatView({
3299
3952
  callService
3300
3953
  }
3301
3954
  );
3302
- const composerDock = /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-view__composer-dock", children: [
3955
+ const composerDock = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "pactor-chat-view__composer-dock", children: [
3303
3956
  composerNode,
3304
- composerBottom !== void 0 && composerBottom !== null ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "pactor-chat-view__composer-bottom", children: composerBottom }) : null
3957
+ composerBottom !== void 0 && composerBottom !== null ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-view__composer-bottom", children: composerBottom }) : null
3305
3958
  ] });
3306
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3959
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3307
3960
  "div",
3308
3961
  {
3309
3962
  className: `pactor-chat-view${isEmpty ? " pactor-chat-view--empty" : ""}${className ? ` ${className}` : ""}`,
3310
3963
  children: [
3311
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-messages", ref: containerRef, children: [
3312
- isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "pactor-chat-view__empty-stack", children: [
3313
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3964
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "pactor-chat-messages", ref: containerRef, children: [
3965
+ isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "pactor-chat-view__empty-stack", children: [
3966
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3314
3967
  EmptyState,
3315
3968
  {
3316
3969
  greeting,
@@ -3320,7 +3973,7 @@ function ChatView({
3320
3973
  }
3321
3974
  ),
3322
3975
  composerDock
3323
- ] }) : messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3976
+ ] }) : messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3324
3977
  MessageBubble,
3325
3978
  {
3326
3979
  message,
@@ -3332,19 +3985,18 @@ function ChatView({
3332
3985
  onEditResend: (text) => handleEditResend(message.id, text),
3333
3986
  messageActions,
3334
3987
  callService,
3335
- onMessageAction,
3336
- onOpenArtifact
3988
+ onMessageAction
3337
3989
  },
3338
3990
  message.id
3339
3991
  )),
3340
- !isFollowing && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3992
+ !isFollowing && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3341
3993
  "button",
3342
3994
  {
3343
3995
  type: "button",
3344
3996
  className: "pactor-chat-scroll-bottom",
3345
3997
  onClick: scrollToBottom,
3346
3998
  children: [
3347
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_components10.Icon, { name: "arrow-down", size: 13 }),
3999
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "arrow-down", size: 13 }),
3348
4000
  "\u56DE\u5230\u5E95\u90E8"
3349
4001
  ]
3350
4002
  }
@@ -3357,8 +4009,8 @@ function ChatView({
3357
4009
  }
3358
4010
 
3359
4011
  // src/view/WorkspaceHeader.tsx
3360
- var import_components11 = require("@pactor-app/ui/components");
3361
- var import_react16 = require("react");
4012
+ var import_components12 = require("@pactor-app/ui/components");
4013
+ var import_react18 = require("react");
3362
4014
 
3363
4015
  // src/export-markdown.ts
3364
4016
  var ROLE_LABELS = {
@@ -3435,7 +4087,7 @@ function buildHeaderActionsFragment(props) {
3435
4087
  }
3436
4088
 
3437
4089
  // src/view/WorkspaceHeader.tsx
3438
- var import_jsx_runtime16 = require("react/jsx-runtime");
4090
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3439
4091
  function readCurrentTheme() {
3440
4092
  if (typeof document === "undefined") {
3441
4093
  return "light";
@@ -3455,10 +4107,10 @@ function WorkspaceHeader({
3455
4107
  onTitleChange
3456
4108
  }) {
3457
4109
  const hasActions = headerActions !== void 0 && headerActions.length > 0;
3458
- const [overrideTitle, setOverrideTitle] = (0, import_react16.useState)(null);
3459
- const [editing, setEditing] = (0, import_react16.useState)(false);
3460
- const [draft, setDraft] = (0, import_react16.useState)("");
3461
- const prevTitleRef = (0, import_react16.useRef)(title);
4110
+ const [overrideTitle, setOverrideTitle] = (0, import_react18.useState)(null);
4111
+ const [editing, setEditing] = (0, import_react18.useState)(false);
4112
+ const [draft, setDraft] = (0, import_react18.useState)("");
4113
+ const prevTitleRef = (0, import_react18.useRef)(title);
3462
4114
  if (prevTitleRef.current !== title) {
3463
4115
  prevTitleRef.current = title;
3464
4116
  if (overrideTitle !== null) {
@@ -3466,7 +4118,7 @@ function WorkspaceHeader({
3466
4118
  }
3467
4119
  }
3468
4120
  const displayTitle = overrideTitle ?? title;
3469
- const [theme, setTheme] = (0, import_react16.useState)(readCurrentTheme);
4121
+ const [theme, setTheme] = (0, import_react18.useState)(readCurrentTheme);
3470
4122
  const commitTitle = () => {
3471
4123
  setEditing(false);
3472
4124
  const next = draft.trim();
@@ -3493,9 +4145,9 @@ function WorkspaceHeader({
3493
4145
  store.clear();
3494
4146
  onClear?.();
3495
4147
  };
3496
- const handlersRef = (0, import_react16.useRef)({ handleExport, handleTheme, handleClear });
4148
+ const handlersRef = (0, import_react18.useRef)({ handleExport, handleTheme, handleClear });
3497
4149
  handlersRef.current = { handleExport, handleTheme, handleClear };
3498
- (0, import_react16.useEffect)(() => {
4150
+ (0, import_react18.useEffect)(() => {
3499
4151
  setChatChromeHandlers({
3500
4152
  headerAction: (name) => {
3501
4153
  const handlers2 = handlersRef.current;
@@ -3510,7 +4162,7 @@ function WorkspaceHeader({
3510
4162
  });
3511
4163
  }, []);
3512
4164
  const actionButtons = {
3513
- export: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4165
+ export: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3514
4166
  "button",
3515
4167
  {
3516
4168
  type: "button",
@@ -3518,11 +4170,11 @@ function WorkspaceHeader({
3518
4170
  title: "\u5BFC\u51FA Markdown",
3519
4171
  "aria-label": "\u5BFC\u51FA Markdown",
3520
4172
  onClick: handleExport,
3521
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "download", size: 15 })
4173
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_components12.Icon, { name: "download", size: 15 })
3522
4174
  },
3523
4175
  "export"
3524
4176
  ),
3525
- theme: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4177
+ theme: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3526
4178
  "button",
3527
4179
  {
3528
4180
  type: "button",
@@ -3530,11 +4182,11 @@ function WorkspaceHeader({
3530
4182
  title: theme === "dark" ? "\u5207\u6362\u4E3A\u6D45\u8272" : "\u5207\u6362\u4E3A\u6DF1\u8272",
3531
4183
  "aria-label": "\u5207\u6362\u660E\u6697\u4E3B\u9898",
3532
4184
  onClick: handleTheme,
3533
- children: theme === "dark" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "sun", size: 15 }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "moon", size: 15 })
4185
+ children: theme === "dark" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_components12.Icon, { name: "sun", size: 15 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_components12.Icon, { name: "moon", size: 15 })
3534
4186
  },
3535
4187
  "theme"
3536
4188
  ),
3537
- clear: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4189
+ clear: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3538
4190
  "button",
3539
4191
  {
3540
4192
  type: "button",
@@ -3542,12 +4194,12 @@ function WorkspaceHeader({
3542
4194
  title: "\u6E05\u7A7A\u4F1A\u8BDD",
3543
4195
  "aria-label": "\u6E05\u7A7A\u4F1A\u8BDD",
3544
4196
  onClick: handleClear,
3545
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_components11.Icon, { name: "trash", size: 15 })
4197
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_components12.Icon, { name: "trash", size: 15 })
3546
4198
  },
3547
4199
  "clear"
3548
4200
  )
3549
4201
  };
3550
- const titleNode = editableTitle ? editing ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4202
+ const titleNode = editableTitle ? editing ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3551
4203
  "input",
3552
4204
  {
3553
4205
  className: "pactor-chat-workspace__title-input",
@@ -3564,7 +4216,7 @@ function WorkspaceHeader({
3564
4216
  }
3565
4217
  }
3566
4218
  }
3567
- ) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4219
+ ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3568
4220
  "button",
3569
4221
  {
3570
4222
  type: "button",
@@ -3576,17 +4228,17 @@ function WorkspaceHeader({
3576
4228
  },
3577
4229
  children: displayTitle
3578
4230
  }
3579
- ) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-workspace__title", children: title });
3580
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("header", { className: "pactor-chat-workspace__header", children: [
4231
+ ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "pactor-chat-workspace__title", children: title });
4232
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("header", { className: "pactor-chat-workspace__header", children: [
3581
4233
  headerStart !== void 0 ? (
3582
4234
  // header 位模型 chip(决策 D2):与 title 组成左端组,chip 在前
3583
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "pactor-chat-workspace__header-left", children: [
4235
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "pactor-chat-workspace__header-left", children: [
3584
4236
  headerStart,
3585
4237
  titleNode
3586
4238
  ] })
3587
4239
  ) : titleNode,
3588
- hasActions ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "pactor-chat-workspace__header-right", children: [
3589
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-workspace__header-actions", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4240
+ hasActions ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "pactor-chat-workspace__header-right", children: [
4241
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "pactor-chat-workspace__header-actions", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3590
4242
  ChromeSlot,
3591
4243
  {
3592
4244
  fragment: buildHeaderActionsFragment({
@@ -3596,18 +4248,18 @@ function WorkspaceHeader({
3596
4248
  fallback: headerActions.map((action) => actionButtons[action])
3597
4249
  }
3598
4250
  ) }),
3599
- headerExtra !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
3600
- ] }) : headerExtra !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
4251
+ headerExtra !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
4252
+ ] }) : headerExtra !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
3601
4253
  ] });
3602
4254
  }
3603
4255
 
3604
4256
  // src/chat-composition.tsx
3605
- var import_jsx_runtime17 = require("react/jsx-runtime");
4257
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3606
4258
  function DslNodeSlotInner({ node }) {
3607
4259
  const { renderChildren } = (0, import_runtime5.useRenderer)();
3608
4260
  return renderChildren([node]);
3609
4261
  }
3610
- var DslSlotGuard = class extends import_react17.Component {
4262
+ var DslSlotGuard = class extends import_react19.Component {
3611
4263
  constructor() {
3612
4264
  super(...arguments);
3613
4265
  this.state = { failed: false };
@@ -3625,17 +4277,17 @@ var DslSlotGuard = class extends import_react17.Component {
3625
4277
  }
3626
4278
  };
3627
4279
  function DslNodeSlot({ node }) {
3628
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DslSlotGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DslNodeSlotInner, { node }) });
4280
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DslSlotGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DslNodeSlotInner, { node }) });
3629
4281
  }
3630
4282
  function isDslNodeValue(value) {
3631
- return typeof value === "object" && value !== null && !Array.isArray(value) && !(0, import_react17.isValidElement)(value) && typeof value.type === "string";
4283
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(0, import_react19.isValidElement)(value) && typeof value.type === "string";
3632
4284
  }
3633
4285
  function resolveSlot(slot) {
3634
4286
  if (slot === void 0 || slot === null) {
3635
4287
  return void 0;
3636
4288
  }
3637
4289
  if (isDslNodeValue(slot)) {
3638
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DslNodeSlot, { node: slot });
4290
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DslNodeSlot, { node: slot });
3639
4291
  }
3640
4292
  return slot;
3641
4293
  }
@@ -3674,28 +4326,51 @@ function ChatComposition({
3674
4326
  onSend,
3675
4327
  artifact,
3676
4328
  panelOpen,
4329
+ artifacts: artifactsProp,
4330
+ onSelectArtifact,
3677
4331
  onPanelClose,
3678
4332
  inspector,
3679
4333
  onArtifact,
3680
- onOpenArtifact
4334
+ onOpenArtifact,
4335
+ onOpenArtifactList
3681
4336
  }) {
3682
- const storeRef = (0, import_react17.useRef)(null);
4337
+ const storeRef = (0, import_react19.useRef)(null);
3683
4338
  if (storeRef.current === null) {
3684
4339
  storeRef.current = services.store ?? services.storeFactory?.() ?? new ConversationStore();
3685
4340
  }
3686
4341
  const store = storeRef.current;
3687
- const artifactStoreRef = (0, import_react17.useRef)(null);
4342
+ const artifactStoreRef = (0, import_react19.useRef)(null);
3688
4343
  if (artifactStoreRef.current === null) {
3689
4344
  artifactStoreRef.current = services.artifactStore ?? new ArtifactStore();
3690
4345
  }
3691
4346
  const artifactStore = artifactStoreRef.current;
3692
4347
  const panelControlled = panelOpen !== void 0 || artifact !== void 0;
3693
- const storeArtifact = (0, import_react17.useSyncExternalStore)(
4348
+ const storeArtifact = (0, import_react19.useSyncExternalStore)(
3694
4349
  (listener) => artifactStore.subscribe(listener),
3695
4350
  () => artifactStore.get()
3696
4351
  );
4352
+ const storeArtifacts = (0, import_react19.useSyncExternalStore)(
4353
+ (listener) => artifactStore.subscribe(listener),
4354
+ () => artifactStore.list()
4355
+ );
4356
+ const messages = (0, import_react19.useSyncExternalStore)(
4357
+ (listener) => store.subscribe(listener),
4358
+ () => store.getMessages()
4359
+ );
4360
+ const streamArtifacts = (0, import_react19.useMemo)(() => collectArtifacts(messages), [messages]);
4361
+ const mergedArtifacts = (0, import_react19.useMemo)(
4362
+ () => mergeArtifacts(streamArtifacts, storeArtifacts),
4363
+ [streamArtifacts, storeArtifacts]
4364
+ );
3697
4365
  const effectiveArtifact = panelControlled ? artifact ?? null : storeArtifact;
3698
4366
  const effectivePanelOpen = panelControlled ? panelOpen ?? artifact != null : storeArtifact !== null;
4367
+ const effectiveArtifacts = artifactsProp ?? mergedArtifacts;
4368
+ const handleSelectArtifact = (next) => {
4369
+ onSelectArtifact?.(next);
4370
+ if (!panelControlled) {
4371
+ artifactStore.open(next);
4372
+ }
4373
+ };
3699
4374
  const handlePanelClose = () => {
3700
4375
  onPanelClose?.();
3701
4376
  if (!panelControlled) {
@@ -3708,7 +4383,18 @@ function ChatComposition({
3708
4383
  artifactStore.open(next);
3709
4384
  }
3710
4385
  };
3711
- const [attachments, setAttachments] = (0, import_react17.useState)([]);
4386
+ const [listViewRequest, setListViewRequest] = (0, import_react19.useState)(null);
4387
+ const handleOpenArtifactList = () => {
4388
+ onOpenArtifactList?.();
4389
+ setListViewRequest((value) => (value ?? 0) + 1);
4390
+ if (!panelControlled && artifactStore.get() === null) {
4391
+ const first = mergedArtifacts[0];
4392
+ if (first !== void 0) {
4393
+ artifactStore.open(first);
4394
+ }
4395
+ }
4396
+ };
4397
+ const [attachments, setAttachments] = (0, import_react19.useState)([]);
3712
4398
  const handleAttachFiles = services.onAttachFiles ? (files) => {
3713
4399
  void Promise.resolve(services.onAttachFiles(files)).then(
3714
4400
  (created) => {
@@ -3728,7 +4414,7 @@ function ChatComposition({
3728
4414
  model,
3729
4415
  onModelChange
3730
4416
  });
3731
- const headerModelChip = modelPlacement === "header" && modelsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
4417
+ const headerModelChip = modelPlacement === "header" && modelsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3732
4418
  ModelSelect,
3733
4419
  {
3734
4420
  models,
@@ -3736,133 +4422,144 @@ function ChatComposition({
3736
4422
  onModelChange: handleModelChange
3737
4423
  }
3738
4424
  ) : void 0;
3739
- const mergedToolbarEnd = services.toolbarEnd !== void 0 || toolbarEnd !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4425
+ const mergedToolbarEnd = services.toolbarEnd !== void 0 || toolbarEnd !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
3740
4426
  services.toolbarEnd,
3741
4427
  toolbarEnd
3742
4428
  ] }) : void 0;
3743
- const mergedToolbarStart = services.toolbarStart !== void 0 || toolbarStart !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4429
+ const mergedToolbarStart = services.toolbarStart !== void 0 || toolbarStart !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
3744
4430
  services.toolbarStart,
3745
4431
  toolbarStart
3746
4432
  ] }) : void 0;
3747
- const mergedComposerFooter = services.composerFooter !== void 0 || composerFooter !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4433
+ const mergedComposerFooter = services.composerFooter !== void 0 || composerFooter !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
3748
4434
  services.composerFooter,
3749
4435
  composerFooter
3750
4436
  ] }) : void 0;
3751
- const mergedComposerBottom = services.composerBottom !== void 0 || composerBottom !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4437
+ const mergedComposerBottom = services.composerBottom !== void 0 || composerBottom !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
3752
4438
  services.composerBottom,
3753
4439
  composerBottom
3754
4440
  ] }) : void 0;
3755
4441
  const hasHeaderContent = header !== void 0 && header !== null ? true : title !== void 0 || headerExtra !== void 0 && headerExtra !== null || headerActions !== void 0 && headerActions.length > 0 || headerModelChip !== void 0;
3756
4442
  const renderHeader = showHeader !== false && hasHeaderContent;
3757
- const workspace = /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
4443
+ const workspaceHeader = renderHeader ? header ?? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4444
+ WorkspaceHeader,
4445
+ {
4446
+ title,
4447
+ headerExtra,
4448
+ headerActions,
4449
+ editableTitle,
4450
+ headerStart: headerModelChip,
4451
+ store,
4452
+ onExport,
4453
+ onThemeChange,
4454
+ onClear,
4455
+ onTitleChange
4456
+ }
4457
+ ) : null;
4458
+ const workspace = /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3758
4459
  "div",
3759
4460
  {
3760
4461
  className: `pactor-chat-workspace${className ? ` ${className}` : ""}`,
3761
- children: [
3762
- renderHeader ? header ?? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3763
- WorkspaceHeader,
4462
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4463
+ ChatView,
4464
+ {
4465
+ store,
4466
+ transport: services.transport,
4467
+ onSend: handleSend,
4468
+ safeRegistry: services.safeRegistry,
4469
+ capabilities: services.capabilities,
4470
+ permission: services.permission,
4471
+ confirm: services.confirm,
4472
+ conversationId,
4473
+ user: services.user,
4474
+ params,
4475
+ greeting,
4476
+ subtitle,
4477
+ suggestions,
4478
+ attachments,
4479
+ onAttachmentsChange: setAttachments,
4480
+ onAttachFiles: handleAttachFiles,
4481
+ onAttachmentClick: services.onAttachmentClick,
4482
+ toolbarStart: mergedToolbarStart,
4483
+ toolbarEnd: mergedToolbarEnd,
4484
+ composerFooter: mergedComposerFooter,
4485
+ composerBottom: mergedComposerBottom,
4486
+ hideToolbar,
4487
+ onMentionTrigger: services.onMentionTrigger,
4488
+ onPlusMenu: services.onPlusMenu,
4489
+ onArtifact,
4490
+ onOpenArtifact: handleOpenArtifact,
4491
+ onOpenArtifactList: handleOpenArtifactList,
4492
+ models,
4493
+ model: currentModel,
4494
+ onModelChange: handleModelChange,
4495
+ modelPlacement,
4496
+ contexts,
4497
+ onContextsChange,
4498
+ callService: services.callService,
4499
+ messageActions,
4500
+ onMessageAction
4501
+ }
4502
+ )
4503
+ }
4504
+ );
4505
+ const main = sidebar ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AppShell, { sidebar, children: workspace }) : workspace;
4506
+ const panelFeatureUsed = artifact !== void 0 || panelOpen !== void 0 || inspector !== void 0 || onPanelClose !== void 0 || onArtifact !== void 0 || onOpenArtifact !== void 0 || onOpenArtifactList !== void 0 || services.artifactStore !== void 0;
4507
+ if (!panelFeatureUsed && !effectivePanelOpen) {
4508
+ return workspaceHeader ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "pactor-chat-with-header", children: [
4509
+ workspaceHeader,
4510
+ main
4511
+ ] }) : main;
4512
+ }
4513
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "pactor-chat-inspector-host", children: [
4514
+ workspaceHeader,
4515
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "pactor-chat-inspector-host__row", children: [
4516
+ main,
4517
+ effectivePanelOpen ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4518
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4519
+ "div",
3764
4520
  {
3765
- title,
3766
- headerExtra,
3767
- headerActions,
3768
- editableTitle,
3769
- headerStart: headerModelChip,
3770
- store,
3771
- onExport,
3772
- onThemeChange,
3773
- onClear,
3774
- onTitleChange
4521
+ className: "pactor-chat-inspector__scrim",
4522
+ "aria-hidden": true,
4523
+ onClick: handlePanelClose
3775
4524
  }
3776
- ) : null,
3777
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3778
- ChatView,
4525
+ ),
4526
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4527
+ ArtifactPanel,
3779
4528
  {
3780
- store,
3781
- transport: services.transport,
3782
- onSend: handleSend,
4529
+ artifact: effectiveArtifact,
4530
+ artifacts: effectiveArtifacts,
4531
+ activeArtifactId: effectiveArtifact?.id,
4532
+ onSelectArtifact: handleSelectArtifact,
4533
+ inspector,
4534
+ onClose: handlePanelClose,
3783
4535
  safeRegistry: services.safeRegistry,
3784
- capabilities: services.capabilities,
3785
- permission: services.permission,
3786
- confirm: services.confirm,
3787
- conversationId,
3788
4536
  user: services.user,
3789
- params,
3790
- greeting,
3791
- subtitle,
3792
- suggestions,
3793
- attachments,
3794
- onAttachmentsChange: setAttachments,
3795
- onAttachFiles: handleAttachFiles,
3796
- onAttachmentClick: services.onAttachmentClick,
3797
- toolbarStart: mergedToolbarStart,
3798
- toolbarEnd: mergedToolbarEnd,
3799
- composerFooter: mergedComposerFooter,
3800
- composerBottom: mergedComposerBottom,
3801
- hideToolbar,
3802
- onMentionTrigger: services.onMentionTrigger,
3803
- onPlusMenu: services.onPlusMenu,
3804
- onArtifact,
3805
- onOpenArtifact: handleOpenArtifact,
3806
- models,
3807
- model: currentModel,
3808
- onModelChange: handleModelChange,
3809
- modelPlacement,
3810
- contexts,
3811
- onContextsChange,
3812
- callService: services.callService,
3813
- messageActions,
3814
- onMessageAction
4537
+ artifactViewers: services.artifactViewers,
4538
+ viewRequest: listViewRequest,
4539
+ onViewRequestConsumed: () => setListViewRequest(null)
3815
4540
  }
3816
4541
  )
3817
- ]
3818
- }
3819
- );
3820
- const main = sidebar ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AppShell, { sidebar, children: workspace }) : workspace;
3821
- const panelFeatureUsed = artifact !== void 0 || panelOpen !== void 0 || inspector !== void 0 || onPanelClose !== void 0 || onArtifact !== void 0 || onOpenArtifact !== void 0 || services.artifactStore !== void 0;
3822
- if (!panelFeatureUsed && !effectivePanelOpen) {
3823
- return main;
3824
- }
3825
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "pactor-chat-inspector-host", children: [
3826
- main,
3827
- effectivePanelOpen ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3828
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3829
- "div",
3830
- {
3831
- className: "pactor-chat-inspector__scrim",
3832
- "aria-hidden": true,
3833
- onClick: handlePanelClose
3834
- }
3835
- ),
3836
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3837
- ArtifactPanel,
3838
- {
3839
- artifact: effectiveArtifact,
3840
- inspector,
3841
- onClose: handlePanelClose,
3842
- safeRegistry: services.safeRegistry,
3843
- user: services.user
3844
- }
3845
- )
3846
- ] }) : null
4542
+ ] }) : null
4543
+ ] })
3847
4544
  ] });
3848
4545
  }
3849
4546
 
3850
4547
  // src/chat-services.tsx
3851
- var import_react18 = require("react");
3852
- var import_jsx_runtime18 = require("react/jsx-runtime");
3853
- var ChatServicesContext = (0, import_react18.createContext)(null);
4548
+ var import_react20 = require("react");
4549
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4550
+ var ChatServicesContext = (0, import_react20.createContext)(null);
3854
4551
  function ChatServicesProvider({
3855
4552
  value,
3856
4553
  children
3857
4554
  }) {
3858
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ChatServicesContext.Provider, { value, children });
4555
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatServicesContext.Provider, { value, children });
3859
4556
  }
3860
4557
  function useChatServices() {
3861
- return (0, import_react18.useContext)(ChatServicesContext);
4558
+ return (0, import_react20.useContext)(ChatServicesContext);
3862
4559
  }
3863
4560
 
3864
4561
  // src/chat-workspace.tsx
3865
- var import_jsx_runtime19 = require("react/jsx-runtime");
4562
+ var import_jsx_runtime20 = require("react/jsx-runtime");
3866
4563
  function createChatWorkspace(services) {
3867
4564
  function ChatWorkspace({
3868
4565
  title,
@@ -3883,6 +4580,8 @@ function createChatWorkspace(services) {
3883
4580
  inspector,
3884
4581
  onArtifact,
3885
4582
  onOpenArtifact,
4583
+ onOpenArtifactList,
4584
+ onSelectArtifact,
3886
4585
  headerActions,
3887
4586
  editableTitle,
3888
4587
  onExport,
@@ -3899,7 +4598,7 @@ function createChatWorkspace(services) {
3899
4598
  onMessageAction,
3900
4599
  className
3901
4600
  }) {
3902
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatServicesProvider, { value: services, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4601
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ChatServicesProvider, { value: services, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3903
4602
  ChatComposition,
3904
4603
  {
3905
4604
  services,
@@ -3911,18 +4610,20 @@ function createChatWorkspace(services) {
3911
4610
  params,
3912
4611
  className,
3913
4612
  headerExtra: services.headerExtra,
3914
- sidebar: sidebar ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: sidebar }) : void 0,
3915
- toolbarStart: toolbarStart ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: toolbarStart }) : void 0,
3916
- toolbarEnd: toolbarEnd ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: toolbarEnd }) : void 0,
3917
- composerFooter: composerFooter ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: composerFooter }) : void 0,
3918
- composerBottom: composerBottom ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: composerBottom }) : void 0,
4613
+ sidebar: sidebar ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DslNodeSlot, { node: sidebar }) : void 0,
4614
+ toolbarStart: toolbarStart ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DslNodeSlot, { node: toolbarStart }) : void 0,
4615
+ toolbarEnd: toolbarEnd ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DslNodeSlot, { node: toolbarEnd }) : void 0,
4616
+ composerFooter: composerFooter ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DslNodeSlot, { node: composerFooter }) : void 0,
4617
+ composerBottom: composerBottom ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DslNodeSlot, { node: composerBottom }) : void 0,
3919
4618
  hideToolbar,
3920
4619
  artifact,
3921
4620
  panelOpen,
3922
4621
  onPanelClose,
3923
- inspector: inspector ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: inspector }) : void 0,
4622
+ inspector: inspector ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DslNodeSlot, { node: inspector }) : void 0,
3924
4623
  onArtifact,
3925
4624
  onOpenArtifact,
4625
+ onOpenArtifactList,
4626
+ onSelectArtifact,
3926
4627
  headerActions,
3927
4628
  editableTitle,
3928
4629
  onExport,
@@ -3944,8 +4645,8 @@ function createChatWorkspace(services) {
3944
4645
  }
3945
4646
 
3946
4647
  // src/view/Chat.tsx
3947
- var import_components12 = require("@pactor-app/ui/components");
3948
- var import_react19 = require("react");
4648
+ var import_components13 = require("@pactor-app/ui/components");
4649
+ var import_react21 = require("react");
3949
4650
 
3950
4651
  // src/view/chrome/menu.tsx
3951
4652
  function buildSidebarMenuFragment(props) {
@@ -3974,7 +4675,7 @@ function buildSidebarMenuFragment(props) {
3974
4675
  }
3975
4676
 
3976
4677
  // src/view/ChatShell.tsx
3977
- var import_jsx_runtime20 = require("react/jsx-runtime");
4678
+ var import_jsx_runtime21 = require("react/jsx-runtime");
3978
4679
  function ChatShell({
3979
4680
  menu,
3980
4681
  activeMenuKey,
@@ -3986,7 +4687,7 @@ function ChatShell({
3986
4687
  className,
3987
4688
  onMenuSelect
3988
4689
  }) {
3989
- const sidebarContent = resolveSlot(sidebar) ?? (menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4690
+ const sidebarContent = resolveSlot(sidebar) ?? (menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3990
4691
  ChatSidebarMenu,
3991
4692
  {
3992
4693
  menu,
@@ -3994,7 +4695,7 @@ function ChatShell({
3994
4695
  onMenuSelect
3995
4696
  }
3996
4697
  ) : null);
3997
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4698
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3998
4699
  AppShell,
3999
4700
  {
4000
4701
  sidebar: sidebarContent,
@@ -4011,7 +4712,7 @@ function ChatShellDslAdapter({
4011
4712
  onMenuSelect,
4012
4713
  ...rest
4013
4714
  }) {
4014
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4715
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4015
4716
  ChatShell,
4016
4717
  {
4017
4718
  ...rest,
@@ -4026,20 +4727,20 @@ function registerChatShellComponent(registry) {
4026
4727
  }
4027
4728
 
4028
4729
  // src/view/Chat.tsx
4029
- var import_jsx_runtime21 = require("react/jsx-runtime");
4730
+ var import_jsx_runtime22 = require("react/jsx-runtime");
4030
4731
  function ChatSidebarMenu({
4031
4732
  menu,
4032
4733
  activeMenuKey,
4033
4734
  onMenuSelect
4034
4735
  }) {
4035
4736
  const drawer = useSidebarDrawer();
4036
- const menuRef = (0, import_react19.useRef)(menu);
4737
+ const menuRef = (0, import_react21.useRef)(menu);
4037
4738
  menuRef.current = menu;
4038
- const selectRef = (0, import_react19.useRef)(onMenuSelect);
4739
+ const selectRef = (0, import_react21.useRef)(onMenuSelect);
4039
4740
  selectRef.current = onMenuSelect;
4040
- const drawerRef = (0, import_react19.useRef)(drawer);
4741
+ const drawerRef = (0, import_react21.useRef)(drawer);
4041
4742
  drawerRef.current = drawer;
4042
- (0, import_react19.useEffect)(() => {
4743
+ (0, import_react21.useEffect)(() => {
4043
4744
  setChatChromeHandlers({
4044
4745
  menuSelect: (key) => {
4045
4746
  const item = menuRef.current.find((entry) => entry.key === key);
@@ -4053,12 +4754,12 @@ function ChatSidebarMenu({
4053
4754
  }, []);
4054
4755
  const fallback = (() => {
4055
4756
  let lastGroup;
4056
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("nav", { className: "pactor-chat-menu", "aria-label": "\u4F1A\u8BDD\u5BFC\u822A", children: menu.map((item) => {
4757
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("nav", { className: "pactor-chat-menu", "aria-label": "\u4F1A\u8BDD\u5BFC\u822A", children: menu.map((item) => {
4057
4758
  const groupLabel = item.group !== void 0 && item.group !== lastGroup ? item.group : void 0;
4058
4759
  lastGroup = item.group;
4059
4760
  const active = item.key === activeMenuKey;
4060
4761
  const collapsed = drawer?.collapsed === true;
4061
- const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4762
+ const button = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4062
4763
  "button",
4063
4764
  {
4064
4765
  type: "button",
@@ -4073,20 +4774,20 @@ function ChatSidebarMenu({
4073
4774
  drawer?.close();
4074
4775
  },
4075
4776
  children: [
4076
- item.icon !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pactor-chat-menu__icon", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_components12.Icon, { name: item.icon, size: 15, strokeWidth: 1.7 }) }) : null,
4077
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pactor-chat-menu__label", children: item.label }),
4078
- item.dot === true ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pactor-chat-menu__dot", "aria-hidden": true }) : null,
4079
- item.badge !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "pactor-chat-menu__badge", children: item.badge }) : null
4777
+ item.icon !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "pactor-chat-menu__icon", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_components13.Icon, { name: item.icon, size: 15, strokeWidth: 1.7 }) }) : null,
4778
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "pactor-chat-menu__label", children: item.label }),
4779
+ item.dot === true ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "pactor-chat-menu__dot", "aria-hidden": true }) : null,
4780
+ item.badge !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "pactor-chat-menu__badge", children: item.badge }) : null
4080
4781
  ]
4081
4782
  }
4082
4783
  );
4083
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react19.Fragment, { children: [
4084
- groupLabel !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "pactor-chat-menu__group", children: groupLabel }) : null,
4085
- collapsed ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_components12.RailTooltip, { label: item.label, children: button }) : button
4784
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react21.Fragment, { children: [
4785
+ groupLabel !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "pactor-chat-menu__group", children: groupLabel }) : null,
4786
+ collapsed ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_components13.RailTooltip, { label: item.label, children: button }) : button
4086
4787
  ] }, item.key);
4087
4788
  }) });
4088
4789
  })();
4089
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4790
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4090
4791
  ChromeSlot,
4091
4792
  {
4092
4793
  fragment: buildSidebarMenuFragment({ menu, activeMenuKey }),
@@ -4132,12 +4833,14 @@ function Chat({
4132
4833
  inspector,
4133
4834
  onArtifact,
4134
4835
  onOpenArtifact,
4836
+ onOpenArtifactList,
4837
+ onSelectArtifact,
4135
4838
  className,
4136
4839
  onSend,
4137
4840
  onMenuSelect
4138
4841
  }) {
4139
4842
  const services = useChatServices();
4140
- (0, import_react19.useEffect)(() => {
4843
+ (0, import_react21.useEffect)(() => {
4141
4844
  if (!services) {
4142
4845
  console.warn(
4143
4846
  "[@pactor-app/chat] Chat \u672A\u68C0\u6D4B\u5230 ChatServicesProvider\uFF1A\u8BF7\u5728\u5E94\u7528\u6839\u90E8\u4EE5 <ChatServicesProvider> \u6CE8\u5165 transport \u7B49\u670D\u52A1"
@@ -4145,10 +4848,10 @@ function Chat({
4145
4848
  }
4146
4849
  }, [services]);
4147
4850
  if (!services) {
4148
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "pactor-chat-notice", role: "note", children: "\u672A\u914D\u7F6E\u804A\u5929\u670D\u52A1\uFF1A\u8BF7\u5728 Chat \u5916\u5C42\u4F7F\u7528 ChatServicesProvider \u6CE8\u5165 transport \u7B49\u670D\u52A1\u540E\u91CD\u8BD5\u3002" });
4851
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "pactor-chat-notice", role: "note", children: "\u672A\u914D\u7F6E\u804A\u5929\u670D\u52A1\uFF1A\u8BF7\u5728 Chat \u5916\u5C42\u4F7F\u7528 ChatServicesProvider \u6CE8\u5165 transport \u7B49\u670D\u52A1\u540E\u91CD\u8BD5\u3002" });
4149
4852
  }
4150
4853
  const sidebarSlot = resolveSlot(sidebar);
4151
- const sidebarContent = sidebarSlot ?? (menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4854
+ const sidebarContent = sidebarSlot ?? (menu && menu.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4152
4855
  ChatSidebarMenu,
4153
4856
  {
4154
4857
  menu,
@@ -4156,7 +4859,7 @@ function Chat({
4156
4859
  onMenuSelect
4157
4860
  }
4158
4861
  ) : void 0);
4159
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4862
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4160
4863
  ChatComposition,
4161
4864
  {
4162
4865
  services,
@@ -4196,7 +4899,9 @@ function Chat({
4196
4899
  onPanelClose,
4197
4900
  inspector: resolveSlot(inspector),
4198
4901
  onArtifact,
4199
- onOpenArtifact
4902
+ onOpenArtifact,
4903
+ onOpenArtifactList,
4904
+ onSelectArtifact
4200
4905
  }
4201
4906
  );
4202
4907
  }
@@ -4207,6 +4912,8 @@ function ChatDslAdapter({
4207
4912
  onPanelClose,
4208
4913
  onArtifact,
4209
4914
  onOpenArtifact,
4915
+ onOpenArtifactList,
4916
+ onSelectArtifact,
4210
4917
  onModelChange,
4211
4918
  onContextsChange,
4212
4919
  onMessageAction,
@@ -4216,7 +4923,7 @@ function ChatDslAdapter({
4216
4923
  onTitleChange,
4217
4924
  ...rest
4218
4925
  }) {
4219
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4926
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4220
4927
  Chat,
4221
4928
  {
4222
4929
  ...rest,
@@ -4225,6 +4932,8 @@ function ChatDslAdapter({
4225
4932
  onPanelClose: onPanelClose ? () => onPanelClose({}) : void 0,
4226
4933
  onArtifact: onArtifact ? (artifact) => onArtifact({ artifact }) : void 0,
4227
4934
  onOpenArtifact: onOpenArtifact ? (artifact) => onOpenArtifact({ artifact }) : void 0,
4935
+ onOpenArtifactList: onOpenArtifactList ? () => onOpenArtifactList({}) : void 0,
4936
+ onSelectArtifact: onSelectArtifact ? (artifact) => onSelectArtifact({ artifact }) : void 0,
4228
4937
  onModelChange: onModelChange ? (model) => onModelChange({ model }) : void 0,
4229
4938
  onContextsChange: onContextsChange ? (contexts) => onContextsChange({ contexts }) : void 0,
4230
4939
  onMessageAction: onMessageAction ? (action, message) => onMessageAction({ action, message }) : void 0,
@@ -4242,17 +4951,17 @@ function registerChatComponent(registry) {
4242
4951
  registerChatShellComponent(registry);
4243
4952
  }
4244
4953
  function createChatComponentRegistry() {
4245
- const registry = (0, import_components12.createComponentRegistry)();
4954
+ const registry = (0, import_components13.createComponentRegistry)();
4246
4955
  registerChatComponent(registry);
4247
4956
  return registry;
4248
4957
  }
4249
4958
 
4250
4959
  // src/view/ChatLayout.tsx
4251
- var import_jsx_runtime22 = require("react/jsx-runtime");
4960
+ var import_jsx_runtime23 = require("react/jsx-runtime");
4252
4961
  function ChatLayout({ title, children }) {
4253
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "pactor-chat-layout", children: [
4254
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("header", { className: "pactor-chat-layout__header", children: title ?? "Chat" }),
4255
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("main", { className: "pactor-chat-layout__main", children })
4962
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "pactor-chat-layout", children: [
4963
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("header", { className: "pactor-chat-layout__header", children: title ?? "Chat" }),
4964
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("main", { className: "pactor-chat-layout__main", children })
4256
4965
  ] });
4257
4966
  }
4258
4967
  var CHAT_LAYOUT_TYPE = "chat";
@@ -4263,6 +4972,7 @@ function registerChatLayout(layoutRegistry) {
4263
4972
  }
4264
4973
  // Annotate the CommonJS export names for ESM import in node:
4265
4974
  0 && (module.exports = {
4975
+ ARTIFACT_IMAGE_EXTENSIONS,
4266
4976
  ActivityTrace,
4267
4977
  AppShell,
4268
4978
  ArtifactPanel,
@@ -4272,6 +4982,7 @@ function registerChatLayout(layoutRegistry) {
4272
4982
  CHAT_LAYOUT_TYPE,
4273
4983
  CHAT_SHELL_COMPONENT_TYPE,
4274
4984
  Chat,
4985
+ ChatArtifactCard,
4275
4986
  ChatDenied,
4276
4987
  ChatLayout,
4277
4988
  ChatServicesProvider,
@@ -4286,7 +4997,11 @@ function registerChatLayout(layoutRegistry) {
4286
4997
  MessageBubble,
4287
4998
  PartRenderer,
4288
4999
  applyChatEvent,
5000
+ artifactCardMeta,
5001
+ buildArtifactBlob,
5002
+ buildArtifactTree,
4289
5003
  buildChatScope,
5004
+ collectArtifacts,
4290
5005
  collectMessageText,
4291
5006
  createChatComponentRegistry,
4292
5007
  createChatId,
@@ -4295,14 +5010,24 @@ function registerChatLayout(layoutRegistry) {
4295
5010
  createManualDriver,
4296
5011
  createSafeRegistry,
4297
5012
  detectStreamingMode,
5013
+ downloadArtifact,
5014
+ formatArtifactSize,
5015
+ formatMessageTime,
4298
5016
  guardChatDsl,
5017
+ isImageArtifact,
5018
+ mergeArtifacts,
4299
5019
  pairToolTraces,
4300
5020
  registerCapabilityAction,
4301
5021
  registerChatComponent,
4302
5022
  registerChatLayout,
4303
5023
  registerChatShellComponent,
5024
+ resolveArtifactFileType,
5025
+ resolveArtifactFilename,
5026
+ resolveArtifactIcon,
5027
+ resolveArtifactIconName,
4304
5028
  runChatTurn,
4305
5029
  runRegenerateTurn,
5030
+ saveBlob,
4306
5031
  useChatAutoScroll,
4307
5032
  useChatServices,
4308
5033
  useSidebarDrawer,