@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.js CHANGED
@@ -118,35 +118,204 @@ var ConversationStore = class {
118
118
  };
119
119
 
120
120
  // src/artifact.ts
121
+ var ARTIFACT_IMAGE_EXTENSIONS = [
122
+ "png",
123
+ "jpg",
124
+ "jpeg",
125
+ "gif",
126
+ "svg",
127
+ "webp",
128
+ "avif",
129
+ "bmp",
130
+ "ico"
131
+ ];
132
+ function resolveArtifactFileType(artifact) {
133
+ const declared = artifact.fileType?.trim().toLowerCase().replace(/^\./, "");
134
+ if (declared) {
135
+ return declared;
136
+ }
137
+ const match = /\.([A-Za-z0-9]+)$/.exec(artifact.title.trim());
138
+ return match ? match[1].toLowerCase() : null;
139
+ }
140
+ function isImageArtifact(artifact) {
141
+ const ext = resolveArtifactFileType(artifact);
142
+ return ext !== null && ARTIFACT_IMAGE_EXTENSIONS.includes(ext);
143
+ }
144
+ var ARTIFACT_ICON_BY_EXTENSION = {
145
+ // 表格数据(绿)
146
+ csv: { name: "table", tone: "green" },
147
+ tsv: { name: "table", tone: "green" },
148
+ xls: { name: "table", tone: "green" },
149
+ xlsx: { name: "table", tone: "green" },
150
+ // 数据 / 数据库(琥珀)
151
+ json: { name: "database", tone: "amber" },
152
+ sql: { name: "database", tone: "amber" },
153
+ // 脚本(蓝,终端图标)
154
+ sh: { name: "terminal", tone: "blue" },
155
+ bash: { name: "terminal", tone: "blue" },
156
+ zsh: { name: "terminal", tone: "blue" },
157
+ // 代码 / 配置(蓝)
158
+ js: { name: "code", tone: "blue" },
159
+ mjs: { name: "code", tone: "blue" },
160
+ cjs: { name: "code", tone: "blue" },
161
+ jsx: { name: "code", tone: "blue" },
162
+ ts: { name: "code", tone: "blue" },
163
+ mts: { name: "code", tone: "blue" },
164
+ cts: { name: "code", tone: "blue" },
165
+ tsx: { name: "code", tone: "blue" },
166
+ py: { name: "code", tone: "blue" },
167
+ rb: { name: "code", tone: "blue" },
168
+ go: { name: "code", tone: "blue" },
169
+ rs: { name: "code", tone: "blue" },
170
+ java: { name: "code", tone: "blue" },
171
+ kt: { name: "code", tone: "blue" },
172
+ c: { name: "code", tone: "blue" },
173
+ h: { name: "code", tone: "blue" },
174
+ cc: { name: "code", tone: "blue" },
175
+ cpp: { name: "code", tone: "blue" },
176
+ cs: { name: "code", tone: "blue" },
177
+ swift: { name: "code", tone: "blue" },
178
+ php: { name: "code", tone: "blue" },
179
+ css: { name: "code", tone: "blue" },
180
+ scss: { name: "code", tone: "blue" },
181
+ less: { name: "code", tone: "blue" },
182
+ html: { name: "code", tone: "blue" },
183
+ vue: { name: "code", tone: "blue" },
184
+ svelte: { name: "code", tone: "blue" },
185
+ yaml: { name: "code", tone: "blue" },
186
+ yml: { name: "code", tone: "blue" },
187
+ toml: { name: "code", tone: "blue" },
188
+ xml: { name: "code", tone: "blue" },
189
+ // 文档 / 文本(石板灰)
190
+ md: { name: "file-text", tone: "slate" },
191
+ markdown: { name: "file-text", tone: "slate" },
192
+ mdx: { name: "file-text", tone: "slate" },
193
+ txt: { name: "file-text", tone: "slate" },
194
+ text: { name: "file-text", tone: "slate" },
195
+ log: { name: "file-text", tone: "slate" },
196
+ rst: { name: "file-text", tone: "slate" },
197
+ pdf: { name: "file-text", tone: "slate" },
198
+ doc: { name: "file-text", tone: "slate" },
199
+ docx: { name: "file-text", tone: "slate" }
200
+ };
201
+ function resolveArtifactIcon(artifact) {
202
+ const ext = resolveArtifactFileType(artifact);
203
+ if (ext !== null) {
204
+ if (ARTIFACT_IMAGE_EXTENSIONS.includes(ext)) {
205
+ return { name: "image", tone: "violet" };
206
+ }
207
+ const spec = ARTIFACT_ICON_BY_EXTENSION[ext];
208
+ if (spec !== void 0) {
209
+ return spec;
210
+ }
211
+ }
212
+ if (artifact.kind === "code") {
213
+ return { name: "code", tone: "blue" };
214
+ }
215
+ if (artifact.kind === "dsl") {
216
+ return { name: "layout-grid", tone: "slate" };
217
+ }
218
+ return ext === null ? { name: "file-text", tone: "slate" } : { name: "file", tone: "slate" };
219
+ }
220
+ function resolveArtifactIconName(artifact) {
221
+ return resolveArtifactIcon(artifact).name;
222
+ }
223
+ function formatArtifactSize(size) {
224
+ if (typeof size !== "number" || !Number.isFinite(size) || size < 0) {
225
+ return null;
226
+ }
227
+ if (size < 1024) {
228
+ return `${size} B`;
229
+ }
230
+ if (size < 1024 * 1024) {
231
+ return `${(size / 1024).toFixed(1)} KB`;
232
+ }
233
+ return `${(size / 1024 / 1024).toFixed(1)} MB`;
234
+ }
235
+ function collectArtifacts(messages) {
236
+ const order = [];
237
+ const byId = /* @__PURE__ */ new Map();
238
+ for (const message of messages) {
239
+ for (const part of message.parts) {
240
+ if (part.type !== "artifact" || !part.artifact) {
241
+ continue;
242
+ }
243
+ if (part.status !== void 0 && part.status !== "ready") {
244
+ continue;
245
+ }
246
+ if (!byId.has(part.artifact.id)) {
247
+ order.push(part.artifact.id);
248
+ }
249
+ byId.set(part.artifact.id, part.artifact);
250
+ }
251
+ }
252
+ return order.map((id) => byId.get(id));
253
+ }
254
+ function mergeArtifacts(stream, opened) {
255
+ const openedById = new Map(opened.map((item) => [item.id, item]));
256
+ const merged = stream.map((item) => openedById.get(item.id) ?? item);
257
+ for (const item of opened) {
258
+ if (!stream.some((s) => s.id === item.id)) {
259
+ merged.push(item);
260
+ }
261
+ }
262
+ return merged;
263
+ }
121
264
  var ArtifactStore = class {
122
265
  constructor() {
123
- this.artifact = null;
266
+ this.artifacts = [];
267
+ this.activeId = null;
124
268
  this.listeners = /* @__PURE__ */ new Set();
125
269
  }
126
- /** 当前产出物(未打开时为 null;引用在不变时保持稳定) */
270
+ /** 当前激活产出物(未打开时为 null;引用在不变时保持稳定) */
127
271
  get() {
128
- return this.artifact;
272
+ if (this.activeId === null) {
273
+ return null;
274
+ }
275
+ return this.artifacts.find((item) => item.id === this.activeId) ?? null;
129
276
  }
130
- /** 打开(或替换)当前产出物 */
277
+ /** 全部产出物(插入序;数组不可变,引用稳定) */
278
+ list() {
279
+ return this.artifacts;
280
+ }
281
+ /** 打开(或按 id 替换)产出物并激活 */
131
282
  open(artifact) {
132
- this.artifact = artifact;
283
+ const index = this.artifacts.findIndex((item) => item.id === artifact.id);
284
+ this.artifacts = index === -1 ? [...this.artifacts, artifact] : this.artifacts.map((item, i) => i === index ? artifact : item);
285
+ this.activeId = artifact.id;
133
286
  this.notify();
134
287
  }
135
- /** 部分更新当前产出物(id 不可改);未 open 时告警并忽略 */
288
+ /** 切换激活项(未知 id 告警并忽略,不抛错) */
289
+ activate(id) {
290
+ if (this.activeId === id) {
291
+ return;
292
+ }
293
+ if (!this.artifacts.some((item) => item.id === id)) {
294
+ console.warn("[@pactor-app/chat] unknown artifact id, activate ignored");
295
+ return;
296
+ }
297
+ this.activeId = id;
298
+ this.notify();
299
+ }
300
+ /** 部分更新当前激活产出物(id 不可改);未 open 时告警并忽略 */
136
301
  update(patch) {
137
- if (!this.artifact) {
302
+ const current = this.get();
303
+ if (!current) {
138
304
  console.warn("[@pactor-app/chat] artifact not open, update ignored");
139
305
  return;
140
306
  }
141
- this.artifact = { ...this.artifact, ...patch, id: this.artifact.id };
307
+ const next = { ...current, ...patch, id: current.id };
308
+ this.artifacts = this.artifacts.map(
309
+ (item) => item.id === current.id ? next : item
310
+ );
142
311
  this.notify();
143
312
  }
144
- /** 关闭面板(未打开时幂等,不重复通知) */
313
+ /** 关闭面板:清空激活项,列表保留(未打开时幂等,不重复通知) */
145
314
  close() {
146
- if (this.artifact === null) {
315
+ if (this.activeId === null) {
147
316
  return;
148
317
  }
149
- this.artifact = null;
318
+ this.activeId = null;
150
319
  this.notify();
151
320
  }
152
321
  /** 订阅变更;返回退订函数 */
@@ -163,6 +332,94 @@ var ArtifactStore = class {
163
332
  }
164
333
  };
165
334
 
335
+ // src/artifact-download.ts
336
+ var KIND_EXTENSIONS = {
337
+ markdown: "md",
338
+ text: "txt",
339
+ code: "txt",
340
+ dsl: "json"
341
+ };
342
+ function resolveArtifactFilename(artifact) {
343
+ const title = artifact.title.trim() || "artifact";
344
+ if (/\.[A-Za-z0-9]+$/.test(title)) {
345
+ return title;
346
+ }
347
+ const ext = resolveArtifactFileType(artifact) ?? KIND_EXTENSIONS[artifact.kind];
348
+ return `${title}.${ext}`;
349
+ }
350
+ function dataUrlToBlob(dataUrl) {
351
+ const comma = dataUrl.indexOf(",");
352
+ const meta = dataUrl.slice(0, comma);
353
+ const payload = dataUrl.slice(comma + 1);
354
+ const mime = /^data:([^;,]+)/.exec(meta)?.[1] ?? "application/octet-stream";
355
+ if (meta.includes(";base64")) {
356
+ const binary = atob(payload);
357
+ const bytes = new Uint8Array(binary.length);
358
+ for (let i = 0; i < binary.length; i++) {
359
+ bytes[i] = binary.charCodeAt(i);
360
+ }
361
+ return new Blob([bytes], { type: mime });
362
+ }
363
+ return new Blob([decodeURIComponent(payload)], { type: mime });
364
+ }
365
+ function isRemoteUrl(raw) {
366
+ return /^https?:\/\//.test(raw.trim());
367
+ }
368
+ function buildArtifactBlob(artifact) {
369
+ if (artifact.kind === "dsl" && typeof artifact.content === "object") {
370
+ return new Blob([JSON.stringify(artifact.content, null, 2)], {
371
+ type: "application/json"
372
+ });
373
+ }
374
+ const raw = String(artifact.content);
375
+ if (isImageArtifact(artifact)) {
376
+ const trimmed = raw.trim();
377
+ if (trimmed.startsWith("data:")) {
378
+ return dataUrlToBlob(trimmed);
379
+ }
380
+ if (trimmed.startsWith("<svg") || trimmed.startsWith("<?xml")) {
381
+ return new Blob([raw], { type: "image/svg+xml" });
382
+ }
383
+ if (isRemoteUrl(trimmed)) {
384
+ return null;
385
+ }
386
+ }
387
+ const ext = resolveArtifactFileType(artifact);
388
+ const mime = ext === "md" || artifact.kind === "markdown" ? "text/markdown" : "text/plain";
389
+ return new Blob([raw], { type: `${mime};charset=utf-8` });
390
+ }
391
+ function saveBlob(blob, filename) {
392
+ const url = URL.createObjectURL(blob);
393
+ const anchor = document.createElement("a");
394
+ anchor.href = url;
395
+ anchor.download = filename;
396
+ document.body.appendChild(anchor);
397
+ anchor.click();
398
+ anchor.remove();
399
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
400
+ }
401
+ async function downloadArtifact(artifact) {
402
+ const filename = resolveArtifactFilename(artifact);
403
+ const raw = String(artifact.content ?? "").trim();
404
+ if (isImageArtifact(artifact) && isRemoteUrl(raw)) {
405
+ try {
406
+ const response = await fetch(raw);
407
+ if (!response.ok) {
408
+ throw new Error(`HTTP ${response.status}`);
409
+ }
410
+ saveBlob(await response.blob(), filename);
411
+ return;
412
+ } catch {
413
+ window.open(raw, "_blank", "noopener");
414
+ return;
415
+ }
416
+ }
417
+ const blob = buildArtifactBlob(artifact);
418
+ if (blob) {
419
+ saveBlob(blob, filename);
420
+ }
421
+ }
422
+
166
423
  // src/events.ts
167
424
  function applyChatEvent(store, event) {
168
425
  switch (event.type) {
@@ -806,8 +1063,9 @@ import { useRenderer as useRenderer4 } from "@pactor-app/runtime";
806
1063
  import {
807
1064
  Component as Component3,
808
1065
  isValidElement,
1066
+ useMemo as useMemo4,
809
1067
  useRef as useRef13,
810
- useState as useState15,
1068
+ useState as useState16,
811
1069
  useSyncExternalStore as useSyncExternalStore2
812
1070
  } from "react";
813
1071
 
@@ -866,7 +1124,7 @@ function AppShell({
866
1124
  }
867
1125
 
868
1126
  // src/view/ArtifactPanel.tsx
869
- import { Icon as Icon3 } from "@pactor-app/ui/components";
1127
+ import { Icon as Icon4 } from "@pactor-app/ui/components";
870
1128
  import { Registry as Registry2 } from "@pactor-app/core";
871
1129
  import { MarkdownRenderer as MarkdownRenderer2 } from "@pactor-app/markdown";
872
1130
  import { DslRenderer as DslRenderer2, useRenderer as useRenderer3 } from "@pactor-app/runtime";
@@ -875,11 +1133,11 @@ import {
875
1133
  useEffect as useEffect3,
876
1134
  useMemo,
877
1135
  useRef as useRef4,
878
- useState as useState5
1136
+ useState as useState6
879
1137
  } from "react";
880
1138
 
881
1139
  // src/view/PartRenderer.tsx
882
- import { Icon } from "@pactor-app/ui/components";
1140
+ import { Icon as Icon2 } from "@pactor-app/ui/components";
883
1141
  import { MarkdownRenderer } from "@pactor-app/markdown";
884
1142
  import { DslRenderer, useRenderer as useRenderer2 } from "@pactor-app/runtime";
885
1143
 
@@ -921,60 +1179,8 @@ function useThrottledValue(value, intervalMs, enabled) {
921
1179
  }
922
1180
 
923
1181
  // src/view/chrome/artifact-card.tsx
924
- function buildArtifactCardFragment(artifact) {
925
- return {
926
- requires: ["Card", "Flex", "Text", "Badge", "Button"],
927
- node: {
928
- type: "Card",
929
- props: { className: "pactor-chat-artifact-card" },
930
- children: [
931
- {
932
- type: "Flex",
933
- props: { justify: "space-between", align: "center", gap: "small" },
934
- children: [
935
- {
936
- type: "Text",
937
- props: {
938
- text: artifact.title,
939
- strong: true,
940
- className: "pactor-chat-artifact-card__title"
941
- }
942
- },
943
- {
944
- type: "Badge",
945
- props: {
946
- text: artifact.kind,
947
- variant: "secondary",
948
- className: "pactor-chat-artifact-card__kind"
949
- }
950
- },
951
- {
952
- type: "Button",
953
- props: {
954
- text: "\u5728\u9762\u677F\u4E2D\u6253\u5F00",
955
- variant: "default",
956
- size: "small",
957
- className: "pactor-chat-artifact-card__open"
958
- },
959
- events: {
960
- onClick: [
961
- {
962
- action: "chat.openArtifact",
963
- params: { artifact }
964
- }
965
- ]
966
- }
967
- }
968
- ]
969
- }
970
- ]
971
- }
972
- };
973
- }
974
-
975
- // src/view/chrome/ChromeSlot.tsx
976
- import { useRenderer } from "@pactor-app/runtime";
977
- import { Component } from "react";
1182
+ import { Icon } from "@pactor-app/ui/components";
1183
+ import { useState as useState5 } from "react";
978
1184
 
979
1185
  // src/view/chrome/actions.ts
980
1186
  var handlers = {};
@@ -1054,9 +1260,125 @@ function ensureChatChromeActions(actions) {
1054
1260
  function invokeChatPanelClose() {
1055
1261
  handlers.panelClose?.();
1056
1262
  }
1263
+ function invokeChatOpenArtifact(artifact) {
1264
+ handlers.openArtifact?.(artifact);
1265
+ }
1266
+ function invokeChatOpenArtifactList() {
1267
+ handlers.openArtifactList?.();
1268
+ }
1269
+ function invokeChatPanelToggleList() {
1270
+ handlers.panelToggleList?.();
1271
+ }
1272
+ function invokeChatPanelDownload() {
1273
+ handlers.panelDownload?.();
1274
+ }
1275
+
1276
+ // src/view/chrome/artifact-card.tsx
1277
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1278
+ function artifactCardMeta(artifact) {
1279
+ const ext = resolveArtifactFileType(artifact);
1280
+ const typeLabel = (ext ?? artifact.kind).toUpperCase();
1281
+ const sizeLabel = formatArtifactSize(artifact.meta?.["size"]);
1282
+ return [sizeLabel, typeLabel].filter(Boolean).join(" \xB7 ");
1283
+ }
1284
+ function ChatArtifactCard({
1285
+ artifact
1286
+ }) {
1287
+ const icon = resolveArtifactIcon(artifact);
1288
+ const meta = artifactCardMeta(artifact);
1289
+ return /* @__PURE__ */ jsxs(
1290
+ "button",
1291
+ {
1292
+ type: "button",
1293
+ className: `pactor-chat-artifact-card pactor-chat-artifact-card--${icon.tone}`,
1294
+ "data-artifact-kind": artifact.kind,
1295
+ "aria-label": `\u5728\u9762\u677F\u4E2D\u6253\u5F00\uFF1A${artifact.title}`,
1296
+ title: `\u5728\u9762\u677F\u4E2D\u6253\u5F00\uFF1A${artifact.title}`,
1297
+ onClick: () => invokeChatOpenArtifact(artifact),
1298
+ children: [
1299
+ /* @__PURE__ */ jsx2("span", { className: "pactor-chat-artifact-card__icon", children: /* @__PURE__ */ jsx2(Icon, { name: icon.name, size: 20 }) }),
1300
+ /* @__PURE__ */ jsxs("span", { className: "pactor-chat-artifact-card__text", children: [
1301
+ /* @__PURE__ */ jsx2("span", { className: "pactor-chat-artifact-card__title", children: artifact.title }),
1302
+ meta ? /* @__PURE__ */ jsx2("span", { className: "pactor-chat-artifact-card__meta", children: meta }) : null
1303
+ ] }),
1304
+ /* @__PURE__ */ jsx2("span", { className: "pactor-chat-artifact-card__arrow", children: /* @__PURE__ */ jsx2(Icon, { name: "arrow-up-right", size: 14 }) })
1305
+ ]
1306
+ }
1307
+ );
1308
+ }
1309
+ var ARTIFACT_CARD_INTERNAL_COMPONENTS = {
1310
+ ChatArtifactCard,
1311
+ ChatArtifactCardGroup
1312
+ };
1313
+ var ARTIFACT_CARD_GROUP_LIMIT = 4;
1314
+ function ChatArtifactCardGroup({
1315
+ artifacts
1316
+ }) {
1317
+ const [expanded, setExpanded] = useState5(false);
1318
+ const overflow = artifacts.length > ARTIFACT_CARD_GROUP_LIMIT;
1319
+ const visible = overflow && !expanded ? artifacts.slice(0, ARTIFACT_CARD_GROUP_LIMIT) : artifacts;
1320
+ return /* @__PURE__ */ jsxs("div", { className: "pactor-chat-artifact-group", children: [
1321
+ visible.map((artifact) => /* @__PURE__ */ jsx2(ChatArtifactCard, { artifact }, artifact.id)),
1322
+ overflow ? /* @__PURE__ */ jsxs("div", { className: "pactor-chat-artifact-group__footer", children: [
1323
+ /* @__PURE__ */ jsxs(
1324
+ "button",
1325
+ {
1326
+ type: "button",
1327
+ className: "pactor-chat-artifact-group__toggle",
1328
+ "aria-expanded": expanded,
1329
+ onClick: () => setExpanded((value) => !value),
1330
+ children: [
1331
+ /* @__PURE__ */ jsx2(
1332
+ Icon,
1333
+ {
1334
+ name: expanded ? "chevron-up" : "chevron-down",
1335
+ size: 12
1336
+ }
1337
+ ),
1338
+ expanded ? "\u6536\u8D77" : "\u5C55\u5F00"
1339
+ ]
1340
+ }
1341
+ ),
1342
+ /* @__PURE__ */ jsxs(
1343
+ "button",
1344
+ {
1345
+ type: "button",
1346
+ className: "pactor-chat-artifact-group__all",
1347
+ onClick: () => invokeChatOpenArtifactList(),
1348
+ children: [
1349
+ "\u67E5\u770B\u6240\u6709\u4EA7\u7269(",
1350
+ artifacts.length,
1351
+ ")",
1352
+ /* @__PURE__ */ jsx2(Icon, { name: "chevron-right", size: 12 })
1353
+ ]
1354
+ }
1355
+ )
1356
+ ] }) : null
1357
+ ] });
1358
+ }
1359
+ function buildArtifactCardFragment(artifact) {
1360
+ return {
1361
+ requires: ["ChatArtifactCard"],
1362
+ node: {
1363
+ type: "ChatArtifactCard",
1364
+ props: { artifact }
1365
+ }
1366
+ };
1367
+ }
1368
+ function buildArtifactCardGroupFragment(artifacts) {
1369
+ return {
1370
+ requires: ["ChatArtifactCardGroup"],
1371
+ node: {
1372
+ type: "ChatArtifactCardGroup",
1373
+ props: { artifacts }
1374
+ }
1375
+ };
1376
+ }
1057
1377
 
1058
1378
  // src/view/chrome/ChromeSlot.tsx
1059
- import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
1379
+ import { useRenderer } from "@pactor-app/runtime";
1380
+ import { Component } from "react";
1381
+ import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
1060
1382
  var warnedMissing = /* @__PURE__ */ new Set();
1061
1383
  function warnMissingOnce(key, detail) {
1062
1384
  if (!warnedMissing.has(key)) {
@@ -1074,7 +1396,7 @@ function ChromeSlotInner({
1074
1396
  try {
1075
1397
  renderer = useRenderer();
1076
1398
  } catch {
1077
- return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
1399
+ return /* @__PURE__ */ jsx3(Fragment, { children: fallback });
1078
1400
  }
1079
1401
  ensureChatChromeActions(renderer.context.actions);
1080
1402
  if (components) {
@@ -1085,7 +1407,7 @@ function ChromeSlotInner({
1085
1407
  `conflict:${name}`,
1086
1408
  `[@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`
1087
1409
  );
1088
- return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
1410
+ return /* @__PURE__ */ jsx3(Fragment, { children: fallback });
1089
1411
  }
1090
1412
  if (existing === void 0) {
1091
1413
  renderer.context.components.register(
@@ -1103,10 +1425,10 @@ function ChromeSlotInner({
1103
1425
  `${fragment.node.type}:${missing.join(",")}`,
1104
1426
  `[@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`
1105
1427
  );
1106
- return /* @__PURE__ */ jsx2(Fragment, { children: fallback });
1428
+ return /* @__PURE__ */ jsx3(Fragment, { children: fallback });
1107
1429
  }
1108
- const rendered = /* @__PURE__ */ jsx2("div", { className: "pactor-chat-chrome", children: renderer.renderChildren([fragment.node]) });
1109
- return wrap ? /* @__PURE__ */ jsx2(Fragment, { children: wrap(rendered) }) : rendered;
1430
+ const rendered = /* @__PURE__ */ jsx3("div", { className: "pactor-chat-chrome", children: renderer.renderChildren([fragment.node]) });
1431
+ return wrap ? /* @__PURE__ */ jsx3(Fragment, { children: wrap(rendered) }) : rendered;
1110
1432
  }
1111
1433
  var ChromeSlotGuard = class extends Component {
1112
1434
  constructor() {
@@ -1121,7 +1443,7 @@ var ChromeSlotGuard = class extends Component {
1121
1443
  }
1122
1444
  };
1123
1445
  function ChromeSlot(props) {
1124
- return /* @__PURE__ */ jsx2(ChromeSlotGuard, { fallback: props.fallback, children: /* @__PURE__ */ jsx2(ChromeSlotInner, { ...props }) });
1446
+ return /* @__PURE__ */ jsx3(ChromeSlotGuard, { fallback: props.fallback, children: /* @__PURE__ */ jsx3(ChromeSlotInner, { ...props }) });
1125
1447
  }
1126
1448
 
1127
1449
  // src/view/chrome/feedback.tsx
@@ -1145,9 +1467,9 @@ function buildStatusFragment(text) {
1145
1467
  }
1146
1468
 
1147
1469
  // src/view/PartRenderer.tsx
1148
- import { Fragment as Fragment2, jsx as jsx3, jsxs } from "react/jsx-runtime";
1470
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1149
1471
  function ChatDenied({ deniedType }) {
1150
- return /* @__PURE__ */ jsxs("div", { className: "pactor-chat-denied", "data-pactor-chat-denied": deniedType, children: [
1472
+ return /* @__PURE__ */ jsxs2("div", { className: "pactor-chat-denied", "data-pactor-chat-denied": deniedType, children: [
1151
1473
  "\u8BE5\u7EC4\u4EF6\u4E0D\u5141\u8BB8\u5728 Chat \u4E2D\u4F7F\u7528",
1152
1474
  deniedType ? `\uFF1A${deniedType}` : ""
1153
1475
  ] });
@@ -1158,89 +1480,68 @@ function ChatDslNode({
1158
1480
  }) {
1159
1481
  const renderer = useRenderer2();
1160
1482
  const extraScope = scope ? buildChatScope(scope) : void 0;
1161
- return /* @__PURE__ */ jsx3(Fragment2, { children: renderer.renderChildren([node], extraScope) });
1483
+ return /* @__PURE__ */ jsx4(Fragment2, { children: renderer.renderChildren([node], extraScope) });
1162
1484
  }
1163
1485
  function SmoothText({ content, streaming }) {
1164
1486
  const shown = useSmoothStreamText(content, streaming);
1165
- return /* @__PURE__ */ jsx3("div", { className: "pactor-chat-text", children: shown });
1487
+ return /* @__PURE__ */ jsx4("div", { className: "pactor-chat-text", children: shown });
1166
1488
  }
1167
1489
  function SmoothMarkdown({ content, streaming }) {
1168
1490
  const shown = useSmoothStreamText(content, streaming);
1169
1491
  const throttled = useThrottledValue(shown, 100, streaming);
1170
- return /* @__PURE__ */ jsx3(MarkdownRenderer, { content: throttled });
1492
+ return /* @__PURE__ */ jsx4(MarkdownRenderer, { content: throttled });
1171
1493
  }
1172
- function PartRenderer({ part, runtime, scope, streaming = false, onOpenArtifact }) {
1494
+ function PartRenderer({ part, runtime, scope, streaming = false }) {
1173
1495
  switch (part.type) {
1174
1496
  case "text":
1175
- return /* @__PURE__ */ jsx3(SmoothText, { content: part.content, streaming });
1497
+ return /* @__PURE__ */ jsx4(SmoothText, { content: part.content, streaming });
1176
1498
  case "markdown":
1177
- return /* @__PURE__ */ jsx3(SmoothMarkdown, { content: part.content, streaming });
1499
+ return /* @__PURE__ */ jsx4(SmoothMarkdown, { content: part.content, streaming });
1178
1500
  case "thinking":
1179
1501
  case "tool-call":
1180
1502
  case "tool-result":
1181
1503
  return null;
1182
1504
  case "dsl": {
1183
1505
  if (!part.schema || !runtime) {
1184
- return /* @__PURE__ */ jsx3(
1506
+ return /* @__PURE__ */ jsx4(
1185
1507
  ChromeSlot,
1186
1508
  {
1187
1509
  fragment: buildStatusFragment(
1188
1510
  part.schema ? "DSL \u6E32\u67D3\u4E0D\u53EF\u7528\uFF08\u672A\u6CE8\u5165 Safe Registry\uFF09" : "\u5185\u5BB9\u751F\u6210\u4E2D\u2026"
1189
1511
  ),
1190
- fallback: /* @__PURE__ */ jsx3("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" })
1512
+ fallback: /* @__PURE__ */ jsx4("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" })
1191
1513
  }
1192
1514
  );
1193
1515
  }
1194
1516
  const guarded = guardChatDsl(part.schema, runtime.context.components);
1195
- return /* @__PURE__ */ jsx3("div", { className: "pactor-chat-dsl", children: /* @__PURE__ */ jsx3(DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ jsx3(ChatDslNode, { node: guarded, scope }) }) });
1517
+ return /* @__PURE__ */ jsx4("div", { className: "pactor-chat-dsl", children: /* @__PURE__ */ jsx4(DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ jsx4(ChatDslNode, { node: guarded, scope }) }) });
1196
1518
  }
1197
1519
  case "error":
1198
- return /* @__PURE__ */ jsx3(
1520
+ return /* @__PURE__ */ jsx4(
1199
1521
  ChromeSlot,
1200
1522
  {
1201
1523
  fragment: buildErrorFragment(part.message),
1202
- fallback: /* @__PURE__ */ jsxs("div", { className: "pactor-chat-error", role: "alert", children: [
1203
- /* @__PURE__ */ jsx3(Icon, { name: "error", size: 14 }),
1204
- /* @__PURE__ */ jsx3("span", { children: part.message })
1524
+ fallback: /* @__PURE__ */ jsxs2("div", { className: "pactor-chat-error", role: "alert", children: [
1525
+ /* @__PURE__ */ jsx4(Icon2, { name: "error", size: 14 }),
1526
+ /* @__PURE__ */ jsx4("span", { children: part.message })
1205
1527
  ] })
1206
1528
  }
1207
1529
  );
1208
1530
  case "status":
1209
- return /* @__PURE__ */ jsx3(
1531
+ return /* @__PURE__ */ jsx4(
1210
1532
  ChromeSlot,
1211
1533
  {
1212
1534
  fragment: buildStatusFragment(part.text),
1213
- fallback: /* @__PURE__ */ jsx3("div", { className: "pactor-chat-status", children: part.text })
1535
+ fallback: /* @__PURE__ */ jsx4("div", { className: "pactor-chat-status", children: part.text })
1214
1536
  }
1215
1537
  );
1216
1538
  case "artifact":
1217
- return /* @__PURE__ */ jsx3(
1539
+ return /* @__PURE__ */ jsx4(
1218
1540
  ChromeSlot,
1219
1541
  {
1220
1542
  fragment: buildArtifactCardFragment(part.artifact),
1221
- fallback: /* @__PURE__ */ jsxs(
1222
- "div",
1223
- {
1224
- className: "pactor-chat-artifact-card",
1225
- "data-artifact-kind": part.artifact.kind,
1226
- children: [
1227
- /* @__PURE__ */ jsx3("span", { className: "pactor-chat-artifact-card__title", children: part.artifact.title }),
1228
- /* @__PURE__ */ jsx3("span", { className: "pactor-chat-artifact-card__kind", children: part.artifact.kind }),
1229
- /* @__PURE__ */ jsxs(
1230
- "button",
1231
- {
1232
- type: "button",
1233
- className: "pactor-chat-artifact-card__open",
1234
- onClick: () => onOpenArtifact?.(part.artifact),
1235
- children: [
1236
- /* @__PURE__ */ jsx3(Icon, { name: "panel-right-open", size: 13, strokeWidth: 1.7 }),
1237
- "\u5728\u9762\u677F\u4E2D\u6253\u5F00"
1238
- ]
1239
- }
1240
- )
1241
- ]
1242
- }
1243
- )
1543
+ components: ARTIFACT_CARD_INTERNAL_COMPONENTS,
1544
+ fallback: /* @__PURE__ */ jsx4(ChatArtifactCard, { artifact: part.artifact })
1244
1545
  }
1245
1546
  );
1246
1547
  default:
@@ -1249,10 +1550,17 @@ function PartRenderer({ part, runtime, scope, streaming = false, onOpenArtifact
1249
1550
  }
1250
1551
 
1251
1552
  // src/view/chrome/panel-header.tsx
1252
- import { Icon as Icon2 } from "@pactor-app/ui/components";
1253
- import { jsx as jsx4 } from "react/jsx-runtime";
1553
+ import { DropdownMenu, Icon as Icon3 } from "@pactor-app/ui/components";
1554
+ import { createContext, useContext } from "react";
1555
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1556
+ var ChatPanelContext = createContext(
1557
+ null
1558
+ );
1559
+ function artifactIconName(artifact) {
1560
+ return artifact ? resolveArtifactIconName(artifact) : "file";
1561
+ }
1254
1562
  function ChatPanelCloseButton() {
1255
- return /* @__PURE__ */ jsx4(
1563
+ return /* @__PURE__ */ jsx5(
1256
1564
  "button",
1257
1565
  {
1258
1566
  type: "button",
@@ -1260,14 +1568,99 @@ function ChatPanelCloseButton() {
1260
1568
  title: "\u5173\u95ED\u9762\u677F",
1261
1569
  "aria-label": "\u5173\u95ED\u9762\u677F",
1262
1570
  onClick: () => invokeChatPanelClose(),
1263
- children: /* @__PURE__ */ jsx4(Icon2, { name: "x", size: 16 })
1571
+ children: /* @__PURE__ */ jsx5(Icon3, { name: "x", size: 16 })
1572
+ }
1573
+ );
1574
+ }
1575
+ function ChatPanelListButton() {
1576
+ const ctx = useContext(ChatPanelContext);
1577
+ if (ctx?.view === "list") {
1578
+ return null;
1579
+ }
1580
+ return /* @__PURE__ */ jsx5(
1581
+ "button",
1582
+ {
1583
+ type: "button",
1584
+ className: "pactor-chat-icon-button pactor-chat-inspector__list-toggle",
1585
+ title: "\u4EA7\u7269\u76EE\u5F55",
1586
+ "aria-label": "\u4EA7\u7269\u76EE\u5F55",
1587
+ onClick: () => invokeChatPanelToggleList(),
1588
+ children: /* @__PURE__ */ jsx5(Icon3, { name: "arrow-left", size: 16 })
1264
1589
  }
1265
1590
  );
1266
1591
  }
1267
- var PANEL_HEADER_INTERNAL_COMPONENTS = { ChatPanelCloseButton };
1592
+ function ChatPanelDownloadButton() {
1593
+ return /* @__PURE__ */ jsx5(
1594
+ "button",
1595
+ {
1596
+ type: "button",
1597
+ className: "pactor-chat-icon-button pactor-chat-inspector__download",
1598
+ title: "\u4E0B\u8F7D\u4EA7\u7269",
1599
+ "aria-label": "\u4E0B\u8F7D\u4EA7\u7269",
1600
+ onClick: () => invokeChatPanelDownload(),
1601
+ children: /* @__PURE__ */ jsx5(Icon3, { name: "download", size: 16 })
1602
+ }
1603
+ );
1604
+ }
1605
+ function ChatPanelArtifactMenu() {
1606
+ const ctx = useContext(ChatPanelContext);
1607
+ if (!ctx || ctx.artifacts.length === 0) {
1608
+ return null;
1609
+ }
1610
+ const active = ctx.artifacts.find((item) => item.id === ctx.activeId);
1611
+ const inList = ctx.view === "list";
1612
+ return /* @__PURE__ */ jsx5(
1613
+ DropdownMenu,
1614
+ {
1615
+ items: ctx.artifacts.map((item) => ({
1616
+ label: item.title,
1617
+ value: item.id,
1618
+ icon: artifactIconName(item)
1619
+ })),
1620
+ onSelect: (value) => {
1621
+ const target = ctx.artifacts.find((item) => item.id === value);
1622
+ if (target) {
1623
+ ctx.onSelectArtifact?.(target);
1624
+ }
1625
+ },
1626
+ children: /* @__PURE__ */ jsxs3(
1627
+ "span",
1628
+ {
1629
+ className: "pactor-chat-inspector__menu-trigger",
1630
+ "data-view": ctx.view,
1631
+ children: [
1632
+ /* @__PURE__ */ jsx5(
1633
+ Icon3,
1634
+ {
1635
+ name: inList ? "folder" : artifactIconName(active),
1636
+ size: 13
1637
+ }
1638
+ ),
1639
+ /* @__PURE__ */ jsx5("span", { className: "pactor-chat-inspector__title", children: inList ? "\u4EA7\u7269\u76EE\u5F55" : active?.title ?? "\u4EA7\u51FA\u7269" }),
1640
+ /* @__PURE__ */ jsx5(Icon3, { name: "chevron-down", size: 12 })
1641
+ ]
1642
+ }
1643
+ )
1644
+ }
1645
+ );
1646
+ }
1647
+ var PANEL_HEADER_INTERNAL_COMPONENTS = {
1648
+ ChatPanelCloseButton,
1649
+ ChatPanelDownloadButton,
1650
+ ChatPanelListButton,
1651
+ ChatPanelArtifactMenu
1652
+ };
1268
1653
  function buildPanelHeaderFragment(props) {
1269
1654
  return {
1270
- requires: ["Flex", "Text", "Badge", "ChatPanelCloseButton"],
1655
+ requires: [
1656
+ "Flex",
1657
+ "Text",
1658
+ "Badge",
1659
+ "ChatPanelCloseButton",
1660
+ "ChatPanelDownloadButton",
1661
+ "ChatPanelListButton",
1662
+ "ChatPanelArtifactMenu"
1663
+ ],
1271
1664
  node: {
1272
1665
  type: "Flex",
1273
1666
  props: {
@@ -1281,7 +1674,8 @@ function buildPanelHeaderFragment(props) {
1281
1674
  type: "Flex",
1282
1675
  props: { align: "center", gap: "small" },
1283
1676
  children: [
1284
- {
1677
+ ...props.showListButton ? [{ type: "ChatPanelListButton" }] : [],
1678
+ props.showMenu ? { type: "ChatPanelArtifactMenu" } : {
1285
1679
  type: "Text",
1286
1680
  props: {
1287
1681
  text: props.title,
@@ -1289,24 +1683,33 @@ function buildPanelHeaderFragment(props) {
1289
1683
  className: "pactor-chat-inspector__title"
1290
1684
  }
1291
1685
  },
1292
- {
1293
- type: "Badge",
1294
- props: {
1295
- text: props.kind,
1296
- variant: "secondary",
1297
- className: "pactor-chat-inspector__kind"
1686
+ ...props.view === "list" ? [] : [
1687
+ {
1688
+ type: "Badge",
1689
+ props: {
1690
+ text: props.kind,
1691
+ variant: "secondary",
1692
+ className: "pactor-chat-inspector__kind"
1693
+ }
1298
1694
  }
1299
- }
1695
+ ]
1300
1696
  ]
1301
1697
  },
1302
- { type: "ChatPanelCloseButton" }
1698
+ {
1699
+ type: "Flex",
1700
+ props: { align: "center", gap: "small" },
1701
+ children: [
1702
+ ...props.showDownload ? [{ type: "ChatPanelDownloadButton" }] : [],
1703
+ { type: "ChatPanelCloseButton" }
1704
+ ]
1705
+ }
1303
1706
  ]
1304
1707
  }
1305
1708
  };
1306
1709
  }
1307
1710
 
1308
1711
  // src/view/ArtifactPanel.tsx
1309
- import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
1712
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1310
1713
  function useArtifactRuntime(safeRegistry, user) {
1311
1714
  return useMemo(() => {
1312
1715
  if (!safeRegistry) {
@@ -1332,12 +1735,12 @@ var ArtifactDslGuard = class extends Component2 {
1332
1735
  console.warn("[@pactor-app/chat] DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A", error);
1333
1736
  }
1334
1737
  render() {
1335
- return this.state.failed ? /* @__PURE__ */ jsx5("div", { className: "pactor-chat-denied", children: "DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A" }) : this.props.children;
1738
+ return this.state.failed ? /* @__PURE__ */ jsx6("div", { className: "pactor-chat-denied", children: "DSL \u4EA7\u51FA\u7269\u6E32\u67D3\u5931\u8D25\uFF0C\u5DF2\u964D\u7EA7\u5C55\u793A" }) : this.props.children;
1336
1739
  }
1337
1740
  };
1338
1741
  function ArtifactDslNode({ node }) {
1339
1742
  const { renderChildren } = useRenderer3();
1340
- return /* @__PURE__ */ jsx5(Fragment3, { children: renderChildren([node]) });
1743
+ return /* @__PURE__ */ jsx6(Fragment3, { children: renderChildren([node]) });
1341
1744
  }
1342
1745
  function ArtifactDslContent({
1343
1746
  content,
@@ -1345,47 +1748,195 @@ function ArtifactDslContent({
1345
1748
  }) {
1346
1749
  const node = typeof content === "object" && content !== null && !Array.isArray(content) && typeof content.type === "string" ? content : void 0;
1347
1750
  if (!node || !runtime) {
1348
- return /* @__PURE__ */ jsx5("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" });
1751
+ return /* @__PURE__ */ jsx6("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" });
1349
1752
  }
1350
1753
  const guarded = guardChatDsl(node, runtime.context.components);
1351
- return /* @__PURE__ */ jsx5(DslRenderer2, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ jsx5(ArtifactDslGuard, { children: /* @__PURE__ */ jsx5(ArtifactDslNode, { node: guarded }) }) });
1754
+ return /* @__PURE__ */ jsx6(DslRenderer2, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ jsx6(ArtifactDslGuard, { children: /* @__PURE__ */ jsx6(ArtifactDslNode, { node: guarded }) }) });
1755
+ }
1756
+ function ArtifactKindIcon({ artifact }) {
1757
+ return /* @__PURE__ */ jsx6(Icon4, { name: resolveArtifactIconName(artifact), size: 13 });
1758
+ }
1759
+ function buildArtifactTree(artifacts) {
1760
+ const root = { name: "", path: "", children: [] };
1761
+ for (const artifact of artifacts) {
1762
+ const segments = artifact.id.split("/").filter(Boolean);
1763
+ let node = root;
1764
+ for (let i = 0; i < segments.length - 1; i++) {
1765
+ const path = segments.slice(0, i + 1).join("/");
1766
+ let child = node.children.find(
1767
+ (item) => item.path === path && item.artifact === void 0
1768
+ );
1769
+ if (!child) {
1770
+ child = { name: segments[i], path, children: [] };
1771
+ node.children.push(child);
1772
+ }
1773
+ node = child;
1774
+ }
1775
+ node.children.push({
1776
+ name: artifact.title,
1777
+ path: artifact.id,
1778
+ artifact,
1779
+ children: []
1780
+ });
1781
+ }
1782
+ const sortLevel = (nodes) => {
1783
+ nodes.sort((a, b) => {
1784
+ const dirDelta = Number(b.artifact === void 0) - Number(a.artifact === void 0);
1785
+ return dirDelta !== 0 ? dirDelta : a.name.localeCompare(b.name);
1786
+ });
1787
+ for (const item of nodes) {
1788
+ sortLevel(item.children);
1789
+ }
1790
+ };
1791
+ sortLevel(root.children);
1792
+ return root.children;
1793
+ }
1794
+ function ArtifactListView({
1795
+ nodes,
1796
+ activeId,
1797
+ onSelect
1798
+ }) {
1799
+ return /* @__PURE__ */ jsx6("ul", { className: "pactor-chat-inspector__list", children: nodes.map((node) => /* @__PURE__ */ jsx6(
1800
+ ArtifactTreeRow,
1801
+ {
1802
+ node,
1803
+ activeId,
1804
+ onSelect
1805
+ },
1806
+ node.path
1807
+ )) });
1808
+ }
1809
+ function ArtifactTreeRow({
1810
+ node,
1811
+ activeId,
1812
+ onSelect
1813
+ }) {
1814
+ const [open, setOpen] = useState6(true);
1815
+ if (node.artifact) {
1816
+ return /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsxs4(
1817
+ "button",
1818
+ {
1819
+ type: "button",
1820
+ className: "pactor-chat-inspector__list-item",
1821
+ "data-active": node.artifact.id === activeId,
1822
+ onClick: () => onSelect?.(node.artifact),
1823
+ children: [
1824
+ /* @__PURE__ */ jsx6(ArtifactKindIcon, { artifact: node.artifact }),
1825
+ /* @__PURE__ */ jsx6("span", { className: "pactor-chat-inspector__list-item-title", children: node.artifact.title }),
1826
+ /* @__PURE__ */ jsx6("span", { className: "pactor-chat-inspector__kind", children: node.artifact.kind })
1827
+ ]
1828
+ }
1829
+ ) });
1830
+ }
1831
+ return /* @__PURE__ */ jsxs4("li", { children: [
1832
+ /* @__PURE__ */ jsxs4(
1833
+ "button",
1834
+ {
1835
+ type: "button",
1836
+ className: "pactor-chat-inspector__list-folder",
1837
+ "aria-expanded": open,
1838
+ "data-open": open,
1839
+ onClick: () => setOpen((value) => !value),
1840
+ children: [
1841
+ /* @__PURE__ */ jsx6("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ jsx6(Icon4, { name: "chevron-right", size: 12 }) }),
1842
+ /* @__PURE__ */ jsx6(Icon4, { name: "folder", size: 13 }),
1843
+ /* @__PURE__ */ jsx6("span", { children: node.name })
1844
+ ]
1845
+ }
1846
+ ),
1847
+ open && /* @__PURE__ */ jsx6("div", { className: "pactor-chat-inspector__list-children", children: /* @__PURE__ */ jsx6(
1848
+ ArtifactListView,
1849
+ {
1850
+ nodes: node.children,
1851
+ activeId,
1852
+ onSelect
1853
+ }
1854
+ ) })
1855
+ ] });
1856
+ }
1857
+ function ArtifactImageContent({ artifact }) {
1858
+ const raw = String(artifact.content).trim();
1859
+ const src = raw.startsWith("<svg") || raw.startsWith("<?xml") ? `data:image/svg+xml;utf8,${encodeURIComponent(raw)}` : raw;
1860
+ return /* @__PURE__ */ jsx6("div", { className: "pactor-chat-inspector__image", children: /* @__PURE__ */ jsx6("img", { src, alt: artifact.title }) });
1352
1861
  }
1353
1862
  function ArtifactContent({
1354
1863
  artifact,
1355
- runtime
1864
+ runtime,
1865
+ viewers
1356
1866
  }) {
1867
+ const ext = resolveArtifactFileType(artifact);
1868
+ const custom = (ext ? viewers?.[ext] : void 0) ?? viewers?.[artifact.kind];
1869
+ if (custom) {
1870
+ return /* @__PURE__ */ jsx6(Fragment3, { children: custom(artifact) });
1871
+ }
1872
+ if (isImageArtifact(artifact)) {
1873
+ return /* @__PURE__ */ jsx6(ArtifactImageContent, { artifact });
1874
+ }
1357
1875
  switch (artifact.kind) {
1358
1876
  case "markdown":
1359
- return /* @__PURE__ */ jsx5(MarkdownRenderer2, { content: String(artifact.content) });
1877
+ return /* @__PURE__ */ jsx6(MarkdownRenderer2, { content: String(artifact.content) });
1360
1878
  case "code":
1361
- return /* @__PURE__ */ jsxs2("div", { className: "pactor-chat-inspector__code", children: [
1362
- artifact.language ? /* @__PURE__ */ jsx5("span", { className: "pactor-chat-inspector__code-lang", children: artifact.language }) : null,
1363
- /* @__PURE__ */ jsx5("pre", { children: /* @__PURE__ */ jsx5("code", { children: String(artifact.content) }) })
1879
+ return /* @__PURE__ */ jsxs4("div", { className: "pactor-chat-inspector__code", children: [
1880
+ artifact.language ? /* @__PURE__ */ jsx6("span", { className: "pactor-chat-inspector__code-lang", children: artifact.language }) : null,
1881
+ /* @__PURE__ */ jsx6("pre", { children: /* @__PURE__ */ jsx6("code", { children: String(artifact.content) }) })
1364
1882
  ] });
1365
1883
  case "text":
1366
- return /* @__PURE__ */ jsx5("pre", { className: "pactor-chat-inspector__text", children: String(artifact.content) });
1884
+ return /* @__PURE__ */ jsx6("pre", { className: "pactor-chat-inspector__text", children: String(artifact.content) });
1367
1885
  case "dsl":
1368
- return /* @__PURE__ */ jsx5(ArtifactDslContent, { content: artifact.content, runtime });
1886
+ return /* @__PURE__ */ jsx6(ArtifactDslContent, { content: artifact.content, runtime });
1369
1887
  default:
1370
1888
  return null;
1371
1889
  }
1372
1890
  }
1373
1891
  function ArtifactPanel({
1374
1892
  artifact,
1893
+ artifacts,
1894
+ activeArtifactId,
1895
+ onSelectArtifact,
1896
+ viewRequest,
1897
+ onViewRequestConsumed,
1375
1898
  inspector,
1376
1899
  onClose,
1377
1900
  safeRegistry,
1378
1901
  user,
1902
+ artifactViewers,
1379
1903
  className
1380
1904
  }) {
1381
1905
  const runtime = useArtifactRuntime(safeRegistry, user);
1906
+ const [view, setView] = useState6("content");
1907
+ const activeId = activeArtifactId ?? artifact?.id;
1908
+ useEffect3(() => {
1909
+ setView("content");
1910
+ }, [activeId]);
1911
+ useEffect3(() => {
1912
+ if (viewRequest != null) {
1913
+ setView("list");
1914
+ onViewRequestConsumed?.();
1915
+ }
1916
+ }, [viewRequest]);
1917
+ const showListButton = artifacts !== void 0 && artifacts.length > 0;
1918
+ const showMenu = artifacts !== void 0 && artifacts.length > 0;
1919
+ const toggleListRef = useRef4(() => {
1920
+ });
1921
+ toggleListRef.current = () => setView((value) => value === "list" ? "content" : "list");
1382
1922
  const closeRef = useRef4(onClose);
1383
1923
  closeRef.current = onClose;
1924
+ const artifactRef = useRef4(artifact);
1925
+ artifactRef.current = artifact;
1384
1926
  useEffect3(() => {
1385
- setChatChromeHandlers({ panelClose: () => closeRef.current?.() });
1927
+ setChatChromeHandlers({
1928
+ panelClose: () => closeRef.current?.(),
1929
+ panelToggleList: () => toggleListRef.current(),
1930
+ panelDownload: () => {
1931
+ const current = artifactRef.current;
1932
+ if (current) {
1933
+ void downloadArtifact(current);
1934
+ }
1935
+ }
1936
+ });
1386
1937
  }, []);
1387
1938
  const panelRef = useRef4(null);
1388
- const [width, setWidth] = useState5(null);
1939
+ const [width, setWidth] = useState6(null);
1389
1940
  const handleResizeStart = (event) => {
1390
1941
  event.preventDefault();
1391
1942
  const panel = panelRef.current;
@@ -1394,14 +1945,14 @@ function ArtifactPanel({
1394
1945
  }
1395
1946
  const hostRect = panel.parentElement?.getBoundingClientRect();
1396
1947
  const hostWidth = hostRect !== void 0 && hostRect.width > 0 ? hostRect.width : window.innerWidth;
1397
- const maxWidth = hostWidth * 0.7;
1948
+ const maxWidth = hostWidth * 2 / 3;
1398
1949
  const startWidth = panel.getBoundingClientRect().width;
1399
1950
  const startX = event.clientX;
1400
1951
  const prevUserSelect = document.body.style.userSelect;
1401
1952
  document.body.style.userSelect = "none";
1402
1953
  const onMove = (moveEvent) => {
1403
1954
  const next = startWidth + (startX - moveEvent.clientX);
1404
- setWidth(Math.round(Math.min(Math.max(next, 240), maxWidth)));
1955
+ setWidth(Math.round(Math.min(Math.max(next, 480), maxWidth)));
1405
1956
  };
1406
1957
  const onUp = () => {
1407
1958
  document.body.style.userSelect = prevUserSelect;
@@ -1411,71 +1962,93 @@ function ArtifactPanel({
1411
1962
  document.addEventListener("pointermove", onMove);
1412
1963
  document.addEventListener("pointerup", onUp);
1413
1964
  };
1414
- return /* @__PURE__ */ jsxs2(
1415
- "aside",
1965
+ return /* @__PURE__ */ jsx6(
1966
+ ChatPanelContext.Provider,
1416
1967
  {
1417
- ref: panelRef,
1418
- className: `pactor-chat-inspector${className ? ` ${className}` : ""}`,
1419
- "data-kind": artifact?.kind,
1420
- style: width !== null ? { width: `${width}px` } : void 0,
1421
- children: [
1422
- /* @__PURE__ */ jsx5(
1423
- "div",
1424
- {
1425
- className: "pactor-chat-inspector__resize",
1426
- role: "separator",
1427
- "aria-orientation": "vertical",
1428
- "aria-label": "\u62D6\u52A8\u8C03\u6574\u9762\u677F\u5BBD\u5EA6",
1429
- onPointerDown: handleResizeStart
1430
- }
1431
- ),
1432
- /* @__PURE__ */ jsx5("div", { className: "pactor-chat-inspector__header", children: /* @__PURE__ */ jsx5(
1433
- ChromeSlot,
1434
- {
1435
- fragment: buildPanelHeaderFragment({
1436
- title: artifact?.title ?? "\u4EA7\u51FA\u7269",
1437
- kind: artifact?.kind ?? "text"
1438
- }),
1439
- components: PANEL_HEADER_INTERNAL_COMPONENTS,
1440
- fallback: /* @__PURE__ */ jsxs2(Fragment3, { children: [
1441
- /* @__PURE__ */ jsx5("span", { className: "pactor-chat-inspector__title", children: artifact?.title ?? "\u4EA7\u51FA\u7269" }),
1442
- artifact ? /* @__PURE__ */ jsx5("span", { className: "pactor-chat-inspector__kind", children: artifact.kind }) : null,
1443
- /* @__PURE__ */ jsx5(
1444
- "button",
1445
- {
1446
- type: "button",
1447
- className: "pactor-chat-icon-button pactor-chat-inspector__close",
1448
- "aria-label": "\u5173\u95ED\u9762\u677F",
1449
- onClick: onClose,
1450
- children: /* @__PURE__ */ jsx5(Icon3, { name: "x", size: 16, strokeWidth: 1.7 })
1451
- }
1452
- )
1453
- ] })
1454
- }
1455
- ) }),
1456
- /* @__PURE__ */ jsx5("div", { className: "pactor-chat-inspector__body", children: inspector !== void 0 && inspector !== null ? inspector : artifact ? /* @__PURE__ */ jsx5(ArtifactContent, { artifact, runtime }) : null })
1457
- ]
1968
+ value: {
1969
+ artifacts: artifacts ?? [],
1970
+ activeId,
1971
+ view,
1972
+ onSelectArtifact
1973
+ },
1974
+ children: /* @__PURE__ */ jsxs4(
1975
+ "aside",
1976
+ {
1977
+ ref: panelRef,
1978
+ className: `pactor-chat-inspector${className ? ` ${className}` : ""}`,
1979
+ "data-kind": artifact?.kind,
1980
+ style: width !== null ? { width: `${width}px` } : void 0,
1981
+ children: [
1982
+ /* @__PURE__ */ jsx6(
1983
+ "div",
1984
+ {
1985
+ className: "pactor-chat-inspector__resize",
1986
+ role: "separator",
1987
+ "aria-orientation": "vertical",
1988
+ "aria-label": "\u62D6\u52A8\u8C03\u6574\u9762\u677F\u5BBD\u5EA6",
1989
+ onPointerDown: handleResizeStart
1990
+ }
1991
+ ),
1992
+ /* @__PURE__ */ jsx6("div", { className: "pactor-chat-inspector__header", children: /* @__PURE__ */ jsx6(
1993
+ ChromeSlot,
1994
+ {
1995
+ fragment: buildPanelHeaderFragment({
1996
+ title: view === "list" ? "\u4EA7\u7269\u76EE\u5F55" : artifact?.title ?? "\u4EA7\u51FA\u7269",
1997
+ kind: artifact?.kind ?? "text",
1998
+ showListButton,
1999
+ showDownload: artifact != null,
2000
+ showMenu,
2001
+ view
2002
+ }),
2003
+ components: PANEL_HEADER_INTERNAL_COMPONENTS,
2004
+ fallback: /* @__PURE__ */ jsxs4(Fragment3, { children: [
2005
+ showListButton ? /* @__PURE__ */ jsx6(ChatPanelListButton, {}) : null,
2006
+ showMenu ? /* @__PURE__ */ jsx6(ChatPanelArtifactMenu, {}) : /* @__PURE__ */ jsx6("span", { className: "pactor-chat-inspector__title", children: view === "list" ? "\u4EA7\u7269\u76EE\u5F55" : artifact?.title ?? "\u4EA7\u51FA\u7269" }),
2007
+ view !== "list" && artifact ? /* @__PURE__ */ jsx6("span", { className: "pactor-chat-inspector__kind", children: artifact.kind }) : null,
2008
+ artifact ? /* @__PURE__ */ jsx6(ChatPanelDownloadButton, {}) : null,
2009
+ /* @__PURE__ */ jsx6(ChatPanelCloseButton, {})
2010
+ ] })
2011
+ }
2012
+ ) }),
2013
+ /* @__PURE__ */ jsx6("div", { className: "pactor-chat-inspector__body", children: view === "list" && artifacts !== void 0 ? /* @__PURE__ */ jsx6(
2014
+ ArtifactListView,
2015
+ {
2016
+ nodes: buildArtifactTree(artifacts),
2017
+ activeId,
2018
+ onSelect: onSelectArtifact
2019
+ }
2020
+ ) : inspector !== void 0 && inspector !== null ? inspector : artifact ? /* @__PURE__ */ jsx6(
2021
+ ArtifactContent,
2022
+ {
2023
+ artifact,
2024
+ runtime,
2025
+ viewers: artifactViewers
2026
+ }
2027
+ ) : null })
2028
+ ]
2029
+ }
2030
+ )
1458
2031
  }
1459
2032
  );
1460
2033
  }
1461
2034
 
1462
2035
  // src/view/ChatView.tsx
1463
- import { Icon as Icon9 } from "@pactor-app/ui/components";
2036
+ import { Icon as Icon10 } from "@pactor-app/ui/components";
1464
2037
  import { Registry as Registry3 } from "@pactor-app/core";
1465
- import { useEffect as useEffect9, useMemo as useMemo3, useRef as useRef11, useState as useState13, useSyncExternalStore } from "react";
2038
+ import { useEffect as useEffect9, useMemo as useMemo3, useRef as useRef11, useState as useState14, useSyncExternalStore } from "react";
1466
2039
 
1467
2040
  // src/view/Composer.tsx
1468
- import { Icon as Icon6 } from "@pactor-app/ui/components";
1469
- import { useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef7, useState as useState9 } from "react";
2041
+ import { Icon as Icon7 } from "@pactor-app/ui/components";
2042
+ import { useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef7, useState as useState10 } from "react";
1470
2043
 
1471
2044
  // src/view/AnchoredPopover.tsx
1472
2045
  import {
1473
2046
  useEffect as useEffect4,
1474
2047
  useLayoutEffect as useLayoutEffect2,
1475
2048
  useRef as useRef5,
1476
- useState as useState6
2049
+ useState as useState7
1477
2050
  } from "react";
1478
- import { jsx as jsx6 } from "react/jsx-runtime";
2051
+ import { jsx as jsx7 } from "react/jsx-runtime";
1479
2052
  function AnchoredPopover({
1480
2053
  anchorRect,
1481
2054
  onClose,
@@ -1485,7 +2058,7 @@ function AnchoredPopover({
1485
2058
  sideOffset = 8
1486
2059
  }) {
1487
2060
  const ref = useRef5(null);
1488
- const [position, setPosition] = useState6(null);
2061
+ const [position, setPosition] = useState7(null);
1489
2062
  useLayoutEffect2(() => {
1490
2063
  const element = ref.current;
1491
2064
  if (!element) return;
@@ -1532,7 +2105,7 @@ function AnchoredPopover({
1532
2105
  position?.below ? "pactor-chat-popover--below" : "",
1533
2106
  className ?? ""
1534
2107
  ].filter(Boolean).join(" ");
1535
- return /* @__PURE__ */ jsx6(
2108
+ return /* @__PURE__ */ jsx7(
1536
2109
  "div",
1537
2110
  {
1538
2111
  ref,
@@ -1551,8 +2124,8 @@ function AnchoredPopover({
1551
2124
  }
1552
2125
 
1553
2126
  // src/view/ModelChip.tsx
1554
- import { Icon as Icon4 } from "@pactor-app/ui/components";
1555
- import { useEffect as useEffect5, useRef as useRef6, useState as useState7 } from "react";
2127
+ import { Icon as Icon5 } from "@pactor-app/ui/components";
2128
+ import { useEffect as useEffect5, useRef as useRef6, useState as useState8 } from "react";
1556
2129
 
1557
2130
  // src/view/chrome/model-select.tsx
1558
2131
  function buildModelSelectFragment(props) {
@@ -1578,17 +2151,17 @@ function buildModelSelectFragment(props) {
1578
2151
  }
1579
2152
 
1580
2153
  // src/view/ModelChip.tsx
1581
- import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
2154
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1582
2155
  function ModelChip({
1583
2156
  models,
1584
2157
  model,
1585
2158
  onModelChange
1586
2159
  }) {
1587
2160
  const chipRef = useRef6(null);
1588
- const [anchor, setAnchor] = useState7(null);
2161
+ const [anchor, setAnchor] = useState8(null);
1589
2162
  const currentOption = models.find((option) => option.value === model) ?? models[0];
1590
- return /* @__PURE__ */ jsxs3(Fragment4, { children: [
1591
- /* @__PURE__ */ jsxs3(
2163
+ return /* @__PURE__ */ jsxs5(Fragment4, { children: [
2164
+ /* @__PURE__ */ jsxs5(
1592
2165
  "button",
1593
2166
  {
1594
2167
  ref: chipRef,
@@ -1598,18 +2171,18 @@ function ModelChip({
1598
2171
  "aria-label": "\u9009\u62E9\u6A21\u578B",
1599
2172
  onClick: () => setAnchor(chipRef.current.getBoundingClientRect()),
1600
2173
  children: [
1601
- /* @__PURE__ */ jsx7("span", { className: "pactor-chat-model-chip__label", children: currentOption.label }),
1602
- /* @__PURE__ */ jsx7(Icon4, { name: "chevrons-up-down", size: 12 })
2174
+ /* @__PURE__ */ jsx8("span", { className: "pactor-chat-model-chip__label", children: currentOption.label }),
2175
+ /* @__PURE__ */ jsx8(Icon5, { name: "chevrons-up-down", size: 12 })
1603
2176
  ]
1604
2177
  }
1605
2178
  ),
1606
- anchor && /* @__PURE__ */ jsx7(
2179
+ anchor && /* @__PURE__ */ jsx8(
1607
2180
  AnchoredPopover,
1608
2181
  {
1609
2182
  anchorRect: anchor,
1610
2183
  onClose: () => setAnchor(null),
1611
2184
  role: "listbox",
1612
- children: models.map((option) => /* @__PURE__ */ jsxs3(
2185
+ children: models.map((option) => /* @__PURE__ */ jsxs5(
1613
2186
  "button",
1614
2187
  {
1615
2188
  type: "button",
@@ -1621,8 +2194,8 @@ function ModelChip({
1621
2194
  setAnchor(null);
1622
2195
  },
1623
2196
  children: [
1624
- /* @__PURE__ */ jsx7("span", { className: "pactor-chat-popover__item-label", children: option.label }),
1625
- option.value === currentOption.value && /* @__PURE__ */ jsx7(Icon4, { name: "check", size: 13, className: "pactor-chat-popover__item-check" })
2197
+ /* @__PURE__ */ jsx8("span", { className: "pactor-chat-popover__item-label", children: option.label }),
2198
+ option.value === currentOption.value && /* @__PURE__ */ jsx8(Icon5, { name: "check", size: 13, className: "pactor-chat-popover__item-check" })
1626
2199
  ]
1627
2200
  },
1628
2201
  option.value
@@ -1643,24 +2216,24 @@ function ModelSelect({
1643
2216
  modelChange: (value) => changeRef.current?.(value)
1644
2217
  });
1645
2218
  }, []);
1646
- return /* @__PURE__ */ jsx7(
2219
+ return /* @__PURE__ */ jsx8(
1647
2220
  ChromeSlot,
1648
2221
  {
1649
2222
  fragment: buildModelSelectFragment({ models, model }),
1650
- fallback: /* @__PURE__ */ jsx7(ModelChip, { models, model, onModelChange })
2223
+ fallback: /* @__PURE__ */ jsx8(ModelChip, { models, model, onModelChange })
1651
2224
  }
1652
2225
  );
1653
2226
  }
1654
2227
 
1655
2228
  // src/view/chrome/composer.tsx
1656
- import { Icon as Icon5 } from "@pactor-app/ui/components";
1657
- import { createContext, useContext } from "react";
1658
- import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
1659
- var ComposerBridgeContext = createContext(
2229
+ import { Icon as Icon6 } from "@pactor-app/ui/components";
2230
+ import { createContext as createContext2, useContext as useContext2 } from "react";
2231
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2232
+ var ComposerBridgeContext = createContext2(
1660
2233
  null
1661
2234
  );
1662
2235
  function useComposerBridge() {
1663
- const bridge = useContext(ComposerBridgeContext);
2236
+ const bridge = useContext2(ComposerBridgeContext);
1664
2237
  if (!bridge) {
1665
2238
  throw new Error(
1666
2239
  "[@pactor-app/chat] ChatComposer* \u5185\u90E8\u7EC4\u4EF6\u4EC5\u53EF\u5728 Composer chrome \u7247\u6BB5\u5185\u6E32\u67D3"
@@ -1685,21 +2258,21 @@ function ComposerAttachments({
1685
2258
  if (attachments.length === 0) {
1686
2259
  return null;
1687
2260
  }
1688
- return /* @__PURE__ */ jsx8("div", { className: "pactor-chat-composer__attachments", children: attachments.map((attachment) => {
2261
+ return /* @__PURE__ */ jsx9("div", { className: "pactor-chat-composer__attachments", children: attachments.map((attachment) => {
1689
2262
  const isImage = attachment.mimeType.startsWith("image/");
1690
- return /* @__PURE__ */ jsxs4(
2263
+ return /* @__PURE__ */ jsxs6(
1691
2264
  "div",
1692
2265
  {
1693
2266
  className: "pactor-chat-composer__attachment",
1694
2267
  "data-attachment-id": attachment.id,
1695
2268
  onClick: () => onClick?.(attachment),
1696
2269
  children: [
1697
- isImage && attachment.url ? /* @__PURE__ */ jsx8("span", { className: "pactor-chat-composer__attachment-thumb", children: /* @__PURE__ */ jsx8("img", { src: attachment.url, alt: attachment.name }) }) : /* @__PURE__ */ jsxs4("span", { className: "pactor-chat-composer__attachment-info", children: [
1698
- /* @__PURE__ */ jsx8(Icon5, { name: "file-text", size: 14 }),
1699
- /* @__PURE__ */ jsx8("span", { className: "pactor-chat-composer__attachment-name", children: attachment.name }),
1700
- attachment.size !== void 0 && /* @__PURE__ */ jsx8("span", { className: "pactor-chat-composer__attachment-size", children: formatSize(attachment.size) })
2270
+ isImage && attachment.url ? /* @__PURE__ */ jsx9("span", { className: "pactor-chat-composer__attachment-thumb", children: /* @__PURE__ */ jsx9("img", { src: attachment.url, alt: attachment.name }) }) : /* @__PURE__ */ jsxs6("span", { className: "pactor-chat-composer__attachment-info", children: [
2271
+ /* @__PURE__ */ jsx9(Icon6, { name: "file-text", size: 14 }),
2272
+ /* @__PURE__ */ jsx9("span", { className: "pactor-chat-composer__attachment-name", children: attachment.name }),
2273
+ attachment.size !== void 0 && /* @__PURE__ */ jsx9("span", { className: "pactor-chat-composer__attachment-size", children: formatSize(attachment.size) })
1701
2274
  ] }),
1702
- /* @__PURE__ */ jsx8(
2275
+ /* @__PURE__ */ jsx9(
1703
2276
  "button",
1704
2277
  {
1705
2278
  type: "button",
@@ -1710,7 +2283,7 @@ function ComposerAttachments({
1710
2283
  event.stopPropagation();
1711
2284
  onRemove(attachment.id);
1712
2285
  },
1713
- children: /* @__PURE__ */ jsx8(Icon5, { name: "x", size: 12 })
2286
+ children: /* @__PURE__ */ jsx9(Icon6, { name: "x", size: 12 })
1714
2287
  }
1715
2288
  )
1716
2289
  ]
@@ -1726,14 +2299,14 @@ function ComposerContextChips({
1726
2299
  if (chips.length === 0) {
1727
2300
  return null;
1728
2301
  }
1729
- return /* @__PURE__ */ jsx8("div", { className: "pactor-chat-composer__contexts", children: chips.map((chip) => /* @__PURE__ */ jsxs4(
2302
+ return /* @__PURE__ */ jsx9("div", { className: "pactor-chat-composer__contexts", children: chips.map((chip) => /* @__PURE__ */ jsxs6(
1730
2303
  "span",
1731
2304
  {
1732
2305
  className: "pactor-chat-context-chip",
1733
2306
  "data-context-id": chip.id,
1734
2307
  children: [
1735
- /* @__PURE__ */ jsx8("span", { className: "pactor-chat-context-chip__label", children: chip.label }),
1736
- /* @__PURE__ */ jsx8(
2308
+ /* @__PURE__ */ jsx9("span", { className: "pactor-chat-context-chip__label", children: chip.label }),
2309
+ /* @__PURE__ */ jsx9(
1737
2310
  "button",
1738
2311
  {
1739
2312
  type: "button",
@@ -1741,7 +2314,7 @@ function ComposerContextChips({
1741
2314
  title: `\u79FB\u9664\u4E0A\u4E0B\u6587 ${chip.label}`,
1742
2315
  "aria-label": `\u79FB\u9664\u4E0A\u4E0B\u6587 ${chip.label}`,
1743
2316
  onClick: () => onRemove(chip),
1744
- children: /* @__PURE__ */ jsx8(Icon5, { name: "x", size: 12 })
2317
+ children: /* @__PURE__ */ jsx9(Icon6, { name: "x", size: 12 })
1745
2318
  }
1746
2319
  )
1747
2320
  ]
@@ -1751,8 +2324,8 @@ function ComposerContextChips({
1751
2324
  }
1752
2325
  function ChatComposerHeader() {
1753
2326
  const bridge = useComposerBridge();
1754
- return /* @__PURE__ */ jsxs4(Fragment5, { children: [
1755
- bridge.attachments !== void 0 && /* @__PURE__ */ jsx8(
2327
+ return /* @__PURE__ */ jsxs6(Fragment5, { children: [
2328
+ bridge.attachments !== void 0 && /* @__PURE__ */ jsx9(
1756
2329
  ComposerAttachments,
1757
2330
  {
1758
2331
  attachments: bridge.attachments,
@@ -1760,7 +2333,7 @@ function ChatComposerHeader() {
1760
2333
  onClick: bridge.attachmentClick
1761
2334
  }
1762
2335
  ),
1763
- bridge.contextChips !== void 0 && /* @__PURE__ */ jsx8(
2336
+ bridge.contextChips !== void 0 && /* @__PURE__ */ jsx9(
1764
2337
  ComposerContextChips,
1765
2338
  {
1766
2339
  chips: bridge.contextChips,
@@ -1771,9 +2344,9 @@ function ChatComposerHeader() {
1771
2344
  }
1772
2345
  function ChatComposerToolbarStart() {
1773
2346
  const bridge = useComposerBridge();
1774
- return /* @__PURE__ */ jsxs4(Fragment5, { children: [
2347
+ return /* @__PURE__ */ jsxs6(Fragment5, { children: [
1775
2348
  bridge.toolbarStart,
1776
- bridge.showPlus && /* @__PURE__ */ jsx8(
2349
+ bridge.showPlus && /* @__PURE__ */ jsx9(
1777
2350
  "button",
1778
2351
  {
1779
2352
  type: "button",
@@ -1781,10 +2354,10 @@ function ChatComposerToolbarStart() {
1781
2354
  title: "\u66F4\u591A\u529F\u80FD",
1782
2355
  "aria-label": "\u66F4\u591A\u529F\u80FD",
1783
2356
  onClick: (event) => bridge.plusClick(event.currentTarget.getBoundingClientRect()),
1784
- children: /* @__PURE__ */ jsx8(Icon5, { name: "plus", size: 14 })
2357
+ children: /* @__PURE__ */ jsx9(Icon6, { name: "plus", size: 14 })
1785
2358
  }
1786
2359
  ),
1787
- bridge.showAttach && /* @__PURE__ */ jsx8(
2360
+ bridge.showAttach && /* @__PURE__ */ jsx9(
1788
2361
  "button",
1789
2362
  {
1790
2363
  type: "button",
@@ -1792,15 +2365,15 @@ function ChatComposerToolbarStart() {
1792
2365
  title: "\u6DFB\u52A0\u9644\u4EF6",
1793
2366
  "aria-label": "\u6DFB\u52A0\u9644\u4EF6",
1794
2367
  onClick: bridge.openFilePicker,
1795
- children: /* @__PURE__ */ jsx8(Icon5, { name: "paperclip", size: 14 })
2368
+ children: /* @__PURE__ */ jsx9(Icon6, { name: "paperclip", size: 14 })
1796
2369
  }
1797
2370
  )
1798
2371
  ] });
1799
2372
  }
1800
2373
  function ChatComposerToolbarEnd() {
1801
2374
  const bridge = useComposerBridge();
1802
- return /* @__PURE__ */ jsxs4(Fragment5, { children: [
1803
- bridge.modelChipEnabled && /* @__PURE__ */ jsx8(
2375
+ return /* @__PURE__ */ jsxs6(Fragment5, { children: [
2376
+ bridge.modelChipEnabled && /* @__PURE__ */ jsx9(
1804
2377
  ModelSelect,
1805
2378
  {
1806
2379
  models: bridge.models,
@@ -1809,7 +2382,7 @@ function ChatComposerToolbarEnd() {
1809
2382
  }
1810
2383
  ),
1811
2384
  bridge.toolbarEnd,
1812
- /* @__PURE__ */ jsx8(
2385
+ /* @__PURE__ */ jsx9(
1813
2386
  "button",
1814
2387
  {
1815
2388
  type: "button",
@@ -1818,14 +2391,14 @@ function ChatComposerToolbarEnd() {
1818
2391
  "aria-label": bridge.streaming ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001",
1819
2392
  disabled: bridge.streaming ? false : bridge.disabled || !bridge.text.trim(),
1820
2393
  onClick: bridge.streaming ? bridge.stop : bridge.send,
1821
- children: bridge.streaming ? /* @__PURE__ */ jsx8(Icon5, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ jsx8(Icon5, { name: "arrow-up", size: 16 })
2394
+ children: bridge.streaming ? /* @__PURE__ */ jsx9(Icon6, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ jsx9(Icon6, { name: "arrow-up", size: 16 })
1822
2395
  }
1823
2396
  )
1824
2397
  ] });
1825
2398
  }
1826
2399
  function ChatComposerFooter() {
1827
2400
  const bridge = useComposerBridge();
1828
- return /* @__PURE__ */ jsx8(Fragment5, { children: bridge.composerFooter });
2401
+ return /* @__PURE__ */ jsx9(Fragment5, { children: bridge.composerFooter });
1829
2402
  }
1830
2403
  var COMPOSER_INTERNAL_COMPONENTS = {
1831
2404
  ChatComposerHeader,
@@ -1866,8 +2439,8 @@ function buildComposerFragment(props) {
1866
2439
  }
1867
2440
 
1868
2441
  // src/view/context-sources.tsx
1869
- import { useEffect as useEffect6, useState as useState8 } from "react";
1870
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
2442
+ import { useEffect as useEffect6, useState as useState9 } from "react";
2443
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1871
2444
  function sourceCacheKey(source) {
1872
2445
  return `${source.service ?? ""}:${JSON.stringify(source.params ?? {})}`;
1873
2446
  }
@@ -1902,7 +2475,7 @@ function ContextSourceList({
1902
2475
  cache,
1903
2476
  onSelect
1904
2477
  }) {
1905
- const [resolutions, setResolutions] = useState8([]);
2478
+ const [resolutions, setResolutions] = useState9([]);
1906
2479
  useEffect6(() => {
1907
2480
  let cancelled = false;
1908
2481
  const initial = sources.map(
@@ -1948,12 +2521,12 @@ function ContextSourceList({
1948
2521
  totalMatched += items.length;
1949
2522
  return { source, resolution, items };
1950
2523
  });
1951
- return /* @__PURE__ */ jsxs5("div", { className: "pactor-chat-source-list", children: [
1952
- groups.map(({ source, resolution, items }) => /* @__PURE__ */ jsxs5("div", { className: "pactor-chat-source-list__group", children: [
1953
- sources.length > 1 && /* @__PURE__ */ jsx9("div", { className: "pactor-chat-source-list__group-label", children: source.label }),
1954
- resolution.status === "loading" && /* @__PURE__ */ jsx9("div", { className: "pactor-chat-source-list__hint", children: "\u52A0\u8F7D\u4E2D\u2026" }),
1955
- resolution.status === "error" && /* @__PURE__ */ jsx9("div", { className: "pactor-chat-source-list__error", role: "alert", children: resolution.message }),
1956
- items.map((item) => /* @__PURE__ */ jsx9(
2524
+ return /* @__PURE__ */ jsxs7("div", { className: "pactor-chat-source-list", children: [
2525
+ groups.map(({ source, resolution, items }) => /* @__PURE__ */ jsxs7("div", { className: "pactor-chat-source-list__group", children: [
2526
+ sources.length > 1 && /* @__PURE__ */ jsx10("div", { className: "pactor-chat-source-list__group-label", children: source.label }),
2527
+ resolution.status === "loading" && /* @__PURE__ */ jsx10("div", { className: "pactor-chat-source-list__hint", children: "\u52A0\u8F7D\u4E2D\u2026" }),
2528
+ resolution.status === "error" && /* @__PURE__ */ jsx10("div", { className: "pactor-chat-source-list__error", role: "alert", children: resolution.message }),
2529
+ items.map((item) => /* @__PURE__ */ jsx10(
1957
2530
  "button",
1958
2531
  {
1959
2532
  type: "button",
@@ -1964,12 +2537,12 @@ function ContextSourceList({
1964
2537
  item.id
1965
2538
  ))
1966
2539
  ] }, `${source.trigger}:${source.label}`)),
1967
- totalMatched === 0 && groups.every(({ resolution }) => resolution.status === "ready") && /* @__PURE__ */ jsx9("div", { className: "pactor-chat-source-list__hint", children: "\u65E0\u5339\u914D\u9879" })
2540
+ totalMatched === 0 && groups.every(({ resolution }) => resolution.status === "ready") && /* @__PURE__ */ jsx10("div", { className: "pactor-chat-source-list__hint", children: "\u65E0\u5339\u914D\u9879" })
1968
2541
  ] });
1969
2542
  }
1970
2543
 
1971
2544
  // src/view/Composer.tsx
1972
- import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
2545
+ import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1973
2546
  var MAX_INPUT_HEIGHT = 200;
1974
2547
  function detectMention(text, caret) {
1975
2548
  const beforeCaret = text.slice(0, caret);
@@ -2003,8 +2576,8 @@ function Composer({
2003
2576
  composerFooter,
2004
2577
  hideToolbar = false
2005
2578
  }) {
2006
- const [text, setText] = useState9("");
2007
- const [dragOver, setDragOver] = useState9(false);
2579
+ const [text, setText] = useState10("");
2580
+ const [dragOver, setDragOver] = useState10(false);
2008
2581
  const inputRef = useRef7(null);
2009
2582
  const fileInputRef = useRef7(null);
2010
2583
  const plusButtonRef = useRef7(null);
@@ -2020,8 +2593,8 @@ function Composer({
2020
2593
  );
2021
2594
  const builtinMentionEnabled = onMentionTrigger === void 0 && mentionSources.length > 0;
2022
2595
  const builtinPlusEnabled = onPlusMenu === void 0 && plusSources.length > 0;
2023
- const [mentionState, setMentionState] = useState9(null);
2024
- const [plusAnchor, setPlusAnchor] = useState9(null);
2596
+ const [mentionState, setMentionState] = useState10(null);
2597
+ const [plusAnchor, setPlusAnchor] = useState10(null);
2025
2598
  const sourceCacheRef = useRef7(null);
2026
2599
  if (sourceCacheRef.current === null) {
2027
2600
  sourceCacheRef.current = /* @__PURE__ */ new Map();
@@ -2116,7 +2689,7 @@ function Composer({
2116
2689
  }
2117
2690
  };
2118
2691
  const showToolbar = !hideToolbar && (toolbarStart !== void 0 || toolbarEnd !== void 0 || onPlusMenu !== void 0 || builtinPlusEnabled || onAttachFiles !== void 0 || modelChipEnabled);
2119
- const fileInputNode = onAttachFiles ? /* @__PURE__ */ jsx10(
2692
+ const fileInputNode = onAttachFiles ? /* @__PURE__ */ jsx11(
2120
2693
  "input",
2121
2694
  {
2122
2695
  ref: fileInputRef,
@@ -2132,14 +2705,14 @@ function Composer({
2132
2705
  }
2133
2706
  }
2134
2707
  ) : null;
2135
- const popoverNodes = /* @__PURE__ */ jsxs6(Fragment6, { children: [
2136
- builtinMentionEnabled && mentionState && /* @__PURE__ */ jsx10(
2708
+ const popoverNodes = /* @__PURE__ */ jsxs8(Fragment6, { children: [
2709
+ builtinMentionEnabled && mentionState && /* @__PURE__ */ jsx11(
2137
2710
  AnchoredPopover,
2138
2711
  {
2139
2712
  anchorRect: mentionState.anchorRect,
2140
2713
  onClose: () => setMentionState(null),
2141
2714
  role: "listbox",
2142
- children: /* @__PURE__ */ jsx10(
2715
+ children: /* @__PURE__ */ jsx11(
2143
2716
  ContextSourceList,
2144
2717
  {
2145
2718
  sources: mentionSources,
@@ -2151,13 +2724,13 @@ function Composer({
2151
2724
  )
2152
2725
  }
2153
2726
  ),
2154
- builtinPlusEnabled && plusAnchor && /* @__PURE__ */ jsx10(
2727
+ builtinPlusEnabled && plusAnchor && /* @__PURE__ */ jsx11(
2155
2728
  AnchoredPopover,
2156
2729
  {
2157
2730
  anchorRect: plusAnchor,
2158
2731
  onClose: () => setPlusAnchor(null),
2159
2732
  role: "listbox",
2160
- children: /* @__PURE__ */ jsx10(
2733
+ children: /* @__PURE__ */ jsx11(
2161
2734
  ContextSourceList,
2162
2735
  {
2163
2736
  sources: plusSources,
@@ -2170,13 +2743,13 @@ function Composer({
2170
2743
  }
2171
2744
  )
2172
2745
  ] });
2173
- const fallback = /* @__PURE__ */ jsxs6(
2746
+ const fallback = /* @__PURE__ */ jsxs8(
2174
2747
  "div",
2175
2748
  {
2176
2749
  className: `pactor-chat-composer${dragOver ? " pactor-chat-composer--dragover" : ""}`,
2177
2750
  ...dragHandlers,
2178
2751
  children: [
2179
- attachments !== void 0 && /* @__PURE__ */ jsx10(
2752
+ attachments !== void 0 && /* @__PURE__ */ jsx11(
2180
2753
  ComposerAttachments,
2181
2754
  {
2182
2755
  attachments,
@@ -2184,7 +2757,7 @@ function Composer({
2184
2757
  onClick: onAttachmentClick
2185
2758
  }
2186
2759
  ),
2187
- contextChips !== void 0 && /* @__PURE__ */ jsx10(
2760
+ contextChips !== void 0 && /* @__PURE__ */ jsx11(
2188
2761
  ComposerContextChips,
2189
2762
  {
2190
2763
  chips: contextChips,
@@ -2195,8 +2768,8 @@ function Composer({
2195
2768
  )
2196
2769
  }
2197
2770
  ),
2198
- /* @__PURE__ */ jsxs6("div", { className: "pactor-chat-composer__row", children: [
2199
- /* @__PURE__ */ jsx10(
2771
+ /* @__PURE__ */ jsxs8("div", { className: "pactor-chat-composer__row", children: [
2772
+ /* @__PURE__ */ jsx11(
2200
2773
  "textarea",
2201
2774
  {
2202
2775
  ref: inputRef,
@@ -2228,7 +2801,7 @@ function Composer({
2228
2801
  }
2229
2802
  }
2230
2803
  ),
2231
- hideToolbar ? null : /* @__PURE__ */ jsx10(
2804
+ hideToolbar ? null : /* @__PURE__ */ jsx11(
2232
2805
  "button",
2233
2806
  {
2234
2807
  type: "button",
@@ -2237,14 +2810,14 @@ function Composer({
2237
2810
  "aria-label": streaming ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001",
2238
2811
  disabled: streaming ? false : disabled || !text.trim(),
2239
2812
  onClick: streaming ? onStop : send,
2240
- children: streaming ? /* @__PURE__ */ jsx10(Icon6, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ jsx10(Icon6, { name: "arrow-up", size: 16 })
2813
+ children: streaming ? /* @__PURE__ */ jsx11(Icon7, { name: "square", size: 14, className: "pactor-chat-icon-fill" }) : /* @__PURE__ */ jsx11(Icon7, { name: "arrow-up", size: 16 })
2241
2814
  }
2242
2815
  )
2243
2816
  ] }),
2244
- showToolbar && /* @__PURE__ */ jsxs6("div", { className: "pactor-chat-composer__toolbar", children: [
2817
+ showToolbar && /* @__PURE__ */ jsxs8("div", { className: "pactor-chat-composer__toolbar", children: [
2245
2818
  toolbarStart,
2246
- /* @__PURE__ */ jsx10("span", { className: "pactor-chat-composer__toolbar-spacer" }),
2247
- (onPlusMenu || builtinPlusEnabled) && /* @__PURE__ */ jsx10(
2819
+ /* @__PURE__ */ jsx11("span", { className: "pactor-chat-composer__toolbar-spacer" }),
2820
+ (onPlusMenu || builtinPlusEnabled) && /* @__PURE__ */ jsx11(
2248
2821
  "button",
2249
2822
  {
2250
2823
  ref: plusButtonRef,
@@ -2260,10 +2833,10 @@ function Composer({
2260
2833
  setPlusAnchor(rect);
2261
2834
  }
2262
2835
  },
2263
- children: /* @__PURE__ */ jsx10(Icon6, { name: "plus", size: 14 })
2836
+ children: /* @__PURE__ */ jsx11(Icon7, { name: "plus", size: 14 })
2264
2837
  }
2265
2838
  ),
2266
- onAttachFiles && /* @__PURE__ */ jsx10(
2839
+ onAttachFiles && /* @__PURE__ */ jsx11(
2267
2840
  "button",
2268
2841
  {
2269
2842
  type: "button",
@@ -2271,10 +2844,10 @@ function Composer({
2271
2844
  title: "\u6DFB\u52A0\u9644\u4EF6",
2272
2845
  "aria-label": "\u6DFB\u52A0\u9644\u4EF6",
2273
2846
  onClick: () => fileInputRef.current?.click(),
2274
- children: /* @__PURE__ */ jsx10(Icon6, { name: "paperclip", size: 14 })
2847
+ children: /* @__PURE__ */ jsx11(Icon7, { name: "paperclip", size: 14 })
2275
2848
  }
2276
2849
  ),
2277
- modelChipEnabled && /* @__PURE__ */ jsx10(
2850
+ modelChipEnabled && /* @__PURE__ */ jsx11(
2278
2851
  ModelSelect,
2279
2852
  {
2280
2853
  models,
@@ -2284,7 +2857,7 @@ function Composer({
2284
2857
  ),
2285
2858
  toolbarEnd
2286
2859
  ] }),
2287
- composerFooter !== void 0 && composerFooter !== null ? /* @__PURE__ */ jsx10("div", { className: "pactor-chat-composer__footer", children: composerFooter }) : null,
2860
+ composerFooter !== void 0 && composerFooter !== null ? /* @__PURE__ */ jsx11("div", { className: "pactor-chat-composer__footer", children: composerFooter }) : null,
2288
2861
  fileInputNode,
2289
2862
  popoverNodes
2290
2863
  ]
@@ -2323,7 +2896,7 @@ function Composer({
2323
2896
  ),
2324
2897
  modelChange: onModelChange
2325
2898
  };
2326
- return /* @__PURE__ */ jsx10(
2899
+ return /* @__PURE__ */ jsx11(
2327
2900
  ChromeSlot,
2328
2901
  {
2329
2902
  fragment: buildComposerFragment({
@@ -2334,7 +2907,7 @@ function Composer({
2334
2907
  }),
2335
2908
  components: COMPOSER_INTERNAL_COMPONENTS,
2336
2909
  fallback,
2337
- wrap: (children) => /* @__PURE__ */ jsx10(ComposerBridgeContext.Provider, { value: bridge, children: /* @__PURE__ */ jsxs6(
2910
+ wrap: (children) => /* @__PURE__ */ jsx11(ComposerBridgeContext.Provider, { value: bridge, children: /* @__PURE__ */ jsxs8(
2338
2911
  "div",
2339
2912
  {
2340
2913
  className: `pactor-chat-composer pactor-chat-composer--dsl${dragOver ? " pactor-chat-composer--dragover" : ""}`,
@@ -2422,7 +2995,7 @@ function buildEmptyStateFragment(props) {
2422
2995
  }
2423
2996
 
2424
2997
  // src/view/EmptyState.tsx
2425
- import { Fragment as Fragment7, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
2998
+ import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
2426
2999
  var DEFAULT_CHAT_GREETING = "\u4F60\u597D\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u4F60\uFF1F";
2427
3000
  function suggestionLabel2(suggestion) {
2428
3001
  return typeof suggestion === "string" ? suggestion : suggestion.label;
@@ -2436,10 +3009,10 @@ function EmptyStateFallback({
2436
3009
  onSuggestionClick,
2437
3010
  greeting
2438
3011
  }) {
2439
- return /* @__PURE__ */ jsxs7(Fragment7, { children: [
2440
- /* @__PURE__ */ jsx11("div", { className: "pactor-chat-empty__greeting", children: greeting }),
2441
- subtitle && /* @__PURE__ */ jsx11("div", { className: "pactor-chat-empty__subtitle", children: subtitle }),
2442
- suggestions && suggestions.length > 0 && /* @__PURE__ */ jsx11("div", { className: "pactor-chat-empty__suggestions", children: suggestions.map((suggestion) => /* @__PURE__ */ jsx11(
3012
+ return /* @__PURE__ */ jsxs9(Fragment7, { children: [
3013
+ /* @__PURE__ */ jsx12("div", { className: "pactor-chat-empty__greeting", children: greeting }),
3014
+ subtitle && /* @__PURE__ */ jsx12("div", { className: "pactor-chat-empty__subtitle", children: subtitle }),
3015
+ suggestions && suggestions.length > 0 && /* @__PURE__ */ jsx12("div", { className: "pactor-chat-empty__suggestions", children: suggestions.map((suggestion) => /* @__PURE__ */ jsx12(
2443
3016
  "button",
2444
3017
  {
2445
3018
  type: "button",
@@ -2457,11 +3030,11 @@ function EmptyState({
2457
3030
  suggestions,
2458
3031
  onSuggestionClick
2459
3032
  }) {
2460
- return /* @__PURE__ */ jsx11("div", { className: "pactor-chat-empty", children: /* @__PURE__ */ jsx11(
3033
+ return /* @__PURE__ */ jsx12("div", { className: "pactor-chat-empty", children: /* @__PURE__ */ jsx12(
2461
3034
  ChromeSlot,
2462
3035
  {
2463
3036
  fragment: buildEmptyStateFragment({ greeting, subtitle, suggestions }),
2464
- fallback: /* @__PURE__ */ jsx11(
3037
+ fallback: /* @__PURE__ */ jsx12(
2465
3038
  EmptyStateFallback,
2466
3039
  {
2467
3040
  greeting,
@@ -2475,16 +3048,20 @@ function EmptyState({
2475
3048
  }
2476
3049
 
2477
3050
  // src/view/MessageBubble.tsx
2478
- import { useRef as useRef10, useState as useState12 } from "react";
3051
+ import { useRef as useRef10, useState as useState13 } from "react";
2479
3052
 
2480
3053
  // src/view/ActivityTrace.tsx
2481
- import { Icon as Icon7 } from "@pactor-app/ui/components";
3054
+ import { Icon as Icon8 } from "@pactor-app/ui/components";
2482
3055
  import { MarkdownRenderer as MarkdownRenderer3 } from "@pactor-app/markdown";
2483
- import { useEffect as useEffect7, useRef as useRef8, useState as useState10 } from "react";
2484
- import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
3056
+ import { useEffect as useEffect7, useRef as useRef8, useState as useState11 } from "react";
3057
+ import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
2485
3058
  function usePinnedCollapse(autoOpen) {
2486
- const [state, setState] = useState10("auto");
2487
- const open = state === "auto" ? autoOpen : state === "pinned-open";
3059
+ const [state, setState] = useState11("auto");
3060
+ const autoCollapsedRef = useRef8(false);
3061
+ if (state === "auto" && !autoOpen) {
3062
+ autoCollapsedRef.current = true;
3063
+ }
3064
+ const open = state === "auto" ? autoOpen && !autoCollapsedRef.current : state === "pinned-open";
2488
3065
  const toggle = () => {
2489
3066
  setState(open ? "pinned-closed" : "pinned-open");
2490
3067
  };
@@ -2509,7 +3086,7 @@ function statusText(message, mode, toolCount) {
2509
3086
  }
2510
3087
  function useElapsedSeconds(startedAt, active) {
2511
3088
  const compute = () => Math.max(0, Math.floor((Date.now() - startedAt) / 1e3));
2512
- const [elapsed, setElapsed] = useState10(compute);
3089
+ const [elapsed, setElapsed] = useState11(compute);
2513
3090
  useEffect7(() => {
2514
3091
  if (!active) {
2515
3092
  setElapsed(compute());
@@ -2524,15 +3101,15 @@ function useElapsedSeconds(startedAt, active) {
2524
3101
  }
2525
3102
  function TraceStatusIcon({ status }) {
2526
3103
  if (status === "running") {
2527
- return /* @__PURE__ */ jsx12(Icon7, { name: "loading", size: 13, className: "pactor-chat-spin" });
3104
+ return /* @__PURE__ */ jsx13(Icon8, { name: "loading", size: 13, className: "pactor-chat-spin" });
2528
3105
  }
2529
3106
  if (status === "success") {
2530
- return /* @__PURE__ */ jsx12(Icon7, { name: "check-circle", size: 13 });
3107
+ return /* @__PURE__ */ jsx13(Icon8, { name: "check-circle", size: 13 });
2531
3108
  }
2532
3109
  if (status === "failed") {
2533
- return /* @__PURE__ */ jsx12(Icon7, { name: "x-circle", size: 13 });
3110
+ return /* @__PURE__ */ jsx13(Icon8, { name: "x-circle", size: 13 });
2534
3111
  }
2535
- return /* @__PURE__ */ jsx12(Icon7, { name: "clock", size: 13 });
3112
+ return /* @__PURE__ */ jsx13(Icon8, { name: "clock", size: 13 });
2536
3113
  }
2537
3114
  function summarizeArgs(args) {
2538
3115
  if (args === void 0) {
@@ -2561,16 +3138,16 @@ function formatDetail(value) {
2561
3138
  }
2562
3139
  }
2563
3140
  function TraceRow({ entry }) {
2564
- const [open, setOpen] = useState10(false);
3141
+ const [open, setOpen] = useState11(false);
2565
3142
  const summary = summarizeArgs(entry.args);
2566
- return /* @__PURE__ */ jsxs8(
3143
+ return /* @__PURE__ */ jsxs10(
2567
3144
  "div",
2568
3145
  {
2569
3146
  className: "pactor-chat-trace__entry",
2570
3147
  "data-status": entry.status,
2571
3148
  "data-open": open,
2572
3149
  children: [
2573
- /* @__PURE__ */ jsxs8(
3150
+ /* @__PURE__ */ jsxs10(
2574
3151
  "button",
2575
3152
  {
2576
3153
  type: "button",
@@ -2578,16 +3155,16 @@ function TraceRow({ entry }) {
2578
3155
  "aria-expanded": open,
2579
3156
  onClick: () => setOpen((value) => !value),
2580
3157
  children: [
2581
- /* @__PURE__ */ jsx12("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ jsx12(Icon7, { name: "chevron-right", size: 12 }) }),
2582
- /* @__PURE__ */ jsx12("span", { className: "pactor-chat-trace__entry-icon", children: /* @__PURE__ */ jsx12(TraceStatusIcon, { status: entry.status }) }),
2583
- /* @__PURE__ */ jsx12("span", { className: "pactor-chat-trace__entry-title", children: entry.title ?? entry.name }),
2584
- summary && /* @__PURE__ */ jsx12("span", { className: "pactor-chat-trace__entry-args", children: summary })
3158
+ /* @__PURE__ */ jsx13("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ jsx13(Icon8, { name: "chevron-right", size: 12 }) }),
3159
+ /* @__PURE__ */ jsx13("span", { className: "pactor-chat-trace__entry-icon", children: /* @__PURE__ */ jsx13(TraceStatusIcon, { status: entry.status }) }),
3160
+ /* @__PURE__ */ jsx13("span", { className: "pactor-chat-trace__entry-title", children: entry.title ?? entry.name }),
3161
+ summary && /* @__PURE__ */ jsx13("span", { className: "pactor-chat-trace__entry-args", children: summary })
2585
3162
  ]
2586
3163
  }
2587
3164
  ),
2588
- open && /* @__PURE__ */ jsxs8("div", { className: "pactor-chat-trace__entry-body", children: [
2589
- entry.args !== void 0 && /* @__PURE__ */ jsx12("pre", { className: "pactor-chat-trace__entry-detail", children: formatDetail(entry.args) }),
2590
- /* @__PURE__ */ jsx12("pre", { className: "pactor-chat-trace__entry-detail", children: entry.status === "failed" ? entry.error ?? "\u6267\u884C\u5931\u8D25" : formatDetail(entry.result) })
3165
+ open && /* @__PURE__ */ jsxs10("div", { className: "pactor-chat-trace__entry-body", children: [
3166
+ entry.args !== void 0 && /* @__PURE__ */ jsx13("pre", { className: "pactor-chat-trace__entry-detail", children: formatDetail(entry.args) }),
3167
+ /* @__PURE__ */ jsx13("pre", { className: "pactor-chat-trace__entry-detail", children: entry.status === "failed" ? entry.error ?? "\u6267\u884C\u5931\u8D25" : formatDetail(entry.result) })
2591
3168
  ] })
2592
3169
  ]
2593
3170
  }
@@ -2600,14 +3177,14 @@ function ThinkingCard({
2600
3177
  const partStreaming = part.status === "streaming";
2601
3178
  const shown = useThrottledValue(part.content, 120, partStreaming);
2602
3179
  const { open, toggle } = usePinnedCollapse(partStreaming && !answerPhase);
2603
- return /* @__PURE__ */ jsxs8(
3180
+ return /* @__PURE__ */ jsxs10(
2604
3181
  "div",
2605
3182
  {
2606
3183
  className: "pactor-chat-thinking",
2607
3184
  "data-open": open,
2608
3185
  "data-streaming": partStreaming,
2609
3186
  children: [
2610
- /* @__PURE__ */ jsxs8(
3187
+ /* @__PURE__ */ jsxs10(
2611
3188
  "button",
2612
3189
  {
2613
3190
  type: "button",
@@ -2615,13 +3192,13 @@ function ThinkingCard({
2615
3192
  "aria-expanded": open,
2616
3193
  onClick: toggle,
2617
3194
  children: [
2618
- /* @__PURE__ */ jsx12("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ jsx12(Icon7, { name: "chevron-right", size: 12 }) }),
2619
- /* @__PURE__ */ jsx12("span", { className: "pactor-chat-thinking__icon", children: partStreaming ? /* @__PURE__ */ jsx12(Icon7, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ jsx12(Icon7, { name: "brain", size: 13 }) }),
2620
- /* @__PURE__ */ jsx12("span", { children: "\u601D\u8003\u8FC7\u7A0B" })
3195
+ /* @__PURE__ */ jsx13("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ jsx13(Icon8, { name: "chevron-right", size: 12 }) }),
3196
+ /* @__PURE__ */ jsx13("span", { className: "pactor-chat-thinking__icon", children: partStreaming ? /* @__PURE__ */ jsx13(Icon8, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ jsx13(Icon8, { name: "brain", size: 13 }) }),
3197
+ /* @__PURE__ */ jsx13("span", { children: "\u601D\u8003\u8FC7\u7A0B" })
2621
3198
  ]
2622
3199
  }
2623
3200
  ),
2624
- open && /* @__PURE__ */ jsx12("div", { className: "pactor-chat-thinking__body", children: partStreaming ? /* @__PURE__ */ jsx12("div", { className: "pactor-chat-thinking__text", children: shown }) : /* @__PURE__ */ jsx12(MarkdownRenderer3, { content: part.content }) })
3201
+ open && /* @__PURE__ */ jsx13("div", { className: "pactor-chat-thinking__body", children: partStreaming ? /* @__PURE__ */ jsx13("div", { className: "pactor-chat-thinking__text", children: shown }) : /* @__PURE__ */ jsx13(MarkdownRenderer3, { content: part.content }) })
2625
3202
  ]
2626
3203
  }
2627
3204
  );
@@ -2644,23 +3221,23 @@ function ActivityTrace({ message }) {
2644
3221
  message.createdAt ?? mountTimeRef.current,
2645
3222
  streaming
2646
3223
  );
2647
- const header = /* @__PURE__ */ jsxs8(Fragment8, { children: [
2648
- /* @__PURE__ */ jsx12("span", { className: "pactor-chat-trace__status-icon", children: streaming ? /* @__PURE__ */ jsx12(Icon7, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ jsx12(Icon7, { name: "check-circle", size: 13 }) }),
2649
- /* @__PURE__ */ jsx12(
3224
+ const header = /* @__PURE__ */ jsxs10(Fragment8, { children: [
3225
+ /* @__PURE__ */ jsx13("span", { className: "pactor-chat-trace__status-icon", children: streaming ? /* @__PURE__ */ jsx13(Icon8, { name: "loading", size: 13, className: "pactor-chat-spin" }) : /* @__PURE__ */ jsx13(Icon8, { name: "check-circle", size: 13 }) }),
3226
+ /* @__PURE__ */ jsx13(
2650
3227
  "span",
2651
3228
  {
2652
3229
  className: `pactor-chat-trace__status-text${streaming ? " pactor-chat-trace__status-text--breathing" : ""}`,
2653
3230
  children: statusText(message, mode, entries.length)
2654
3231
  }
2655
3232
  ),
2656
- /* @__PURE__ */ jsxs8("span", { className: "pactor-chat-trace__elapsed", children: [
3233
+ /* @__PURE__ */ jsxs10("span", { className: "pactor-chat-trace__elapsed", children: [
2657
3234
  elapsed,
2658
3235
  "s"
2659
3236
  ] }),
2660
- collapsible && /* @__PURE__ */ jsx12("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ jsx12(Icon7, { name: "chevron-right", size: 12 }) })
3237
+ collapsible && /* @__PURE__ */ jsx13("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ jsx13(Icon8, { name: "chevron-right", size: 12 }) })
2661
3238
  ] });
2662
- return /* @__PURE__ */ jsxs8("div", { className: "pactor-chat-trace", "data-open": open, children: [
2663
- collapsible ? /* @__PURE__ */ jsx12(
3239
+ return /* @__PURE__ */ jsxs10("div", { className: "pactor-chat-trace", "data-open": open, children: [
3240
+ collapsible ? /* @__PURE__ */ jsx13(
2664
3241
  "button",
2665
3242
  {
2666
3243
  type: "button",
@@ -2669,10 +3246,10 @@ function ActivityTrace({ message }) {
2669
3246
  onClick: toggle,
2670
3247
  children: header
2671
3248
  }
2672
- ) : /* @__PURE__ */ jsx12("div", { className: "pactor-chat-trace__header", children: header }),
2673
- open && /* @__PURE__ */ jsxs8(Fragment8, { children: [
2674
- entries.length > 0 && /* @__PURE__ */ jsx12("div", { className: "pactor-chat-trace__entries", children: entries.map((entry) => /* @__PURE__ */ jsx12(TraceRow, { entry }, entry.callIndex)) }),
2675
- thinkingParts.map((part, index) => /* @__PURE__ */ jsx12(
3249
+ ) : /* @__PURE__ */ jsx13("div", { className: "pactor-chat-trace__header", children: header }),
3250
+ open && /* @__PURE__ */ jsxs10(Fragment8, { children: [
3251
+ entries.length > 0 && /* @__PURE__ */ jsx13("div", { className: "pactor-chat-trace__entries", children: entries.map((entry) => /* @__PURE__ */ jsx13(TraceRow, { entry }, entry.callIndex)) }),
3252
+ thinkingParts.map((part, index) => /* @__PURE__ */ jsx13(
2676
3253
  ThinkingCard,
2677
3254
  {
2678
3255
  part,
@@ -2685,8 +3262,8 @@ function ActivityTrace({ message }) {
2685
3262
  }
2686
3263
 
2687
3264
  // src/view/MessageActions.tsx
2688
- import { Icon as Icon8 } from "@pactor-app/ui/components";
2689
- import { useEffect as useEffect8, useRef as useRef9, useState as useState11 } from "react";
3265
+ import { Icon as Icon9 } from "@pactor-app/ui/components";
3266
+ import { useEffect as useEffect8, useRef as useRef9, useState as useState12 } from "react";
2690
3267
 
2691
3268
  // src/view/chrome/message-actions.tsx
2692
3269
  function buildMessageActionsFragment(args) {
@@ -2739,7 +3316,7 @@ function buildMessageActionsFragment(args) {
2739
3316
  }
2740
3317
 
2741
3318
  // src/view/MessageActions.tsx
2742
- import { jsx as jsx13 } from "react/jsx-runtime";
3319
+ import { jsx as jsx14 } from "react/jsx-runtime";
2743
3320
  var BUILTIN_ACTION_DEFAULTS = {
2744
3321
  copy: {
2745
3322
  label: "\u590D\u5236",
@@ -2812,7 +3389,7 @@ function MessageActions({
2812
3389
  callService,
2813
3390
  onMessageAction
2814
3391
  }) {
2815
- const [copied, setCopied] = useState11(false);
3392
+ const [copied, setCopied] = useState12(false);
2816
3393
  const dispatchRef = useRef9(
2817
3394
  () => {
2818
3395
  }
@@ -2892,7 +3469,7 @@ function MessageActions({
2892
3469
  const copiedFeedback = action.builtin === "copy" && copied;
2893
3470
  const label = copiedFeedback ? "\u5DF2\u590D\u5236" : action.label;
2894
3471
  const iconName = copiedFeedback ? "check" : action.icon;
2895
- return /* @__PURE__ */ jsx13(
3472
+ return /* @__PURE__ */ jsx14(
2896
3473
  "button",
2897
3474
  {
2898
3475
  type: "button",
@@ -2901,16 +3478,16 @@ function MessageActions({
2901
3478
  "aria-label": label,
2902
3479
  "data-message-action": action.id,
2903
3480
  onClick: () => dispatch(action),
2904
- children: iconName !== void 0 ? /* @__PURE__ */ jsx13(Icon8, { name: iconName, size: 14 }) : /* @__PURE__ */ jsx13(Icon8, { name: "help-circle", size: 14 })
3481
+ children: iconName !== void 0 ? /* @__PURE__ */ jsx14(Icon9, { name: iconName, size: 14 }) : /* @__PURE__ */ jsx14(Icon9, { name: "help-circle", size: 14 })
2905
3482
  },
2906
3483
  action.id
2907
3484
  );
2908
3485
  });
2909
- return /* @__PURE__ */ jsx13(
3486
+ return /* @__PURE__ */ jsx14(
2910
3487
  "div",
2911
3488
  {
2912
3489
  className: `pactor-chat-message__actions${forceVisible ? " pactor-chat-message__actions--visible" : ""}`,
2913
- children: /* @__PURE__ */ jsx13(
3490
+ children: /* @__PURE__ */ jsx14(
2914
3491
  ChromeSlot,
2915
3492
  {
2916
3493
  fragment: buildMessageActionsFragment({
@@ -2926,13 +3503,38 @@ function MessageActions({
2926
3503
  }
2927
3504
 
2928
3505
  // src/view/MessageBubble.tsx
2929
- import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
3506
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
3507
+ function groupArtifactParts(parts) {
3508
+ const segments = [];
3509
+ for (const part of parts) {
3510
+ if (part.type === "artifact" && part.artifact) {
3511
+ const last = segments[segments.length - 1];
3512
+ if (Array.isArray(last)) {
3513
+ last.push(part.artifact);
3514
+ } else {
3515
+ segments.push([part.artifact]);
3516
+ }
3517
+ } else {
3518
+ segments.push(part);
3519
+ }
3520
+ }
3521
+ return segments;
3522
+ }
3523
+ function formatMessageTime(ts, now = Date.now()) {
3524
+ const date = new Date(ts);
3525
+ const hh = String(date.getHours()).padStart(2, "0");
3526
+ const mm = String(date.getMinutes()).padStart(2, "0");
3527
+ if (new Date(now).toDateString() === date.toDateString()) {
3528
+ return `${hh}:${mm}`;
3529
+ }
3530
+ return `${date.getMonth() + 1}\u6708${date.getDate()}\u65E5 ${hh}:${mm}`;
3531
+ }
2930
3532
  function MessageEditForm({
2931
3533
  initialText,
2932
3534
  onConfirm,
2933
3535
  onCancel
2934
3536
  }) {
2935
- const [text, setText] = useState12(initialText);
3537
+ const [text, setText] = useState13(initialText);
2936
3538
  const confirm = () => {
2937
3539
  const value = text.trim();
2938
3540
  if (!value) {
@@ -2940,8 +3542,8 @@ function MessageEditForm({
2940
3542
  }
2941
3543
  onConfirm(value);
2942
3544
  };
2943
- return /* @__PURE__ */ jsxs9("div", { className: "pactor-chat-message__edit", children: [
2944
- /* @__PURE__ */ jsx14(
3545
+ return /* @__PURE__ */ jsxs11("div", { className: "pactor-chat-message__edit", children: [
3546
+ /* @__PURE__ */ jsx15(
2945
3547
  "textarea",
2946
3548
  {
2947
3549
  className: "pactor-chat-message__edit-input",
@@ -2959,8 +3561,8 @@ function MessageEditForm({
2959
3561
  }
2960
3562
  }
2961
3563
  ),
2962
- /* @__PURE__ */ jsxs9("div", { className: "pactor-chat-message__edit-actions", children: [
2963
- /* @__PURE__ */ jsx14(
3564
+ /* @__PURE__ */ jsxs11("div", { className: "pactor-chat-message__edit-actions", children: [
3565
+ /* @__PURE__ */ jsx15(
2964
3566
  "button",
2965
3567
  {
2966
3568
  type: "button",
@@ -2970,7 +3572,7 @@ function MessageEditForm({
2970
3572
  children: "\u53D1\u9001"
2971
3573
  }
2972
3574
  ),
2973
- /* @__PURE__ */ jsx14(
3575
+ /* @__PURE__ */ jsx15(
2974
3576
  "button",
2975
3577
  {
2976
3578
  type: "button",
@@ -2993,10 +3595,9 @@ function MessageBubble({
2993
3595
  onEditResend,
2994
3596
  messageActions,
2995
3597
  callService,
2996
- onMessageAction,
2997
- onOpenArtifact
3598
+ onMessageAction
2998
3599
  }) {
2999
- const [editing, setEditing] = useState12(false);
3600
+ const [editing, setEditing] = useState13(false);
3000
3601
  const bubbleScope = {
3001
3602
  conversation: scope?.conversation ?? {},
3002
3603
  message: { id: message.id },
@@ -3011,14 +3612,15 @@ function MessageBubble({
3011
3612
  const showTrace = message.role === "assistant" && (wasLiveRef.current || message.parts.some(
3012
3613
  (part) => part.type === "thinking" || part.type === "tool-call" || part.type === "tool-result"
3013
3614
  ));
3014
- return /* @__PURE__ */ jsxs9(
3615
+ const showTime = message.role === "assistant" && !live && message.createdAt !== void 0;
3616
+ return /* @__PURE__ */ jsxs11(
3015
3617
  "div",
3016
3618
  {
3017
3619
  className: `pactor-chat-message pactor-chat-message--${message.role}`,
3018
3620
  "data-message-id": message.id,
3019
3621
  "data-message-status": message.status,
3020
3622
  children: [
3021
- editing ? /* @__PURE__ */ jsx14(
3623
+ editing ? /* @__PURE__ */ jsx15(
3022
3624
  MessageEditForm,
3023
3625
  {
3024
3626
  initialText: collectMessageText(message),
@@ -3028,21 +3630,53 @@ function MessageBubble({
3028
3630
  },
3029
3631
  onCancel: () => setEditing(false)
3030
3632
  }
3031
- ) : /* @__PURE__ */ jsxs9("div", { className: "pactor-chat-message__body", children: [
3032
- showTrace && /* @__PURE__ */ jsx14(ActivityTrace, { message }),
3033
- message.parts.length === 0 && message.status === "pending" && message.role !== "assistant" ? /* @__PURE__ */ jsx14("span", { className: "pactor-chat-message__pending", children: "\u2026" }) : message.parts.map((part, index) => /* @__PURE__ */ jsx14(
3034
- PartRenderer,
3035
- {
3036
- part,
3037
- runtime,
3038
- scope: bubbleScope,
3039
- streaming,
3040
- onOpenArtifact
3041
- },
3042
- part.id ?? index
3043
- ))
3633
+ ) : /* @__PURE__ */ jsxs11("div", { className: "pactor-chat-message__body", children: [
3634
+ showTrace && /* @__PURE__ */ jsx15(ActivityTrace, { message }),
3635
+ message.parts.length === 0 && message.status === "pending" && message.role !== "assistant" ? /* @__PURE__ */ jsx15("span", { className: "pactor-chat-message__pending", children: "\u2026" }) : groupArtifactParts(message.parts).map(
3636
+ (segment, index) => Array.isArray(segment) ? /* @__PURE__ */ jsx15(
3637
+ ChromeSlot,
3638
+ {
3639
+ fragment: buildArtifactCardGroupFragment(segment),
3640
+ components: ARTIFACT_CARD_INTERNAL_COMPONENTS,
3641
+ fallback: /* @__PURE__ */ jsx15(ChatArtifactCardGroup, { artifacts: segment })
3642
+ },
3643
+ `artifacts:${segment[0].id}`
3644
+ ) : /* @__PURE__ */ jsx15(
3645
+ PartRenderer,
3646
+ {
3647
+ part: segment,
3648
+ runtime,
3649
+ scope: bubbleScope,
3650
+ streaming
3651
+ },
3652
+ segment.id ?? index
3653
+ )
3654
+ )
3044
3655
  ] }),
3045
- !editing && /* @__PURE__ */ jsx14(
3656
+ !editing && (showTime ? /* @__PURE__ */ jsxs11("div", { className: "pactor-chat-message__footer", children: [
3657
+ /* @__PURE__ */ jsx15(
3658
+ MessageActions,
3659
+ {
3660
+ message,
3661
+ actions: messageActions,
3662
+ showRegenerate,
3663
+ onRegenerate,
3664
+ onRetry,
3665
+ onEdit: onEditResend ? () => setEditing(true) : void 0,
3666
+ forceVisible: message.status === "error",
3667
+ callService,
3668
+ onMessageAction
3669
+ }
3670
+ ),
3671
+ /* @__PURE__ */ jsx15(
3672
+ "span",
3673
+ {
3674
+ className: "pactor-chat-message__time",
3675
+ title: new Date(message.createdAt).toLocaleString(),
3676
+ children: formatMessageTime(message.createdAt)
3677
+ }
3678
+ )
3679
+ ] }) : /* @__PURE__ */ jsx15(
3046
3680
  MessageActions,
3047
3681
  {
3048
3682
  message,
@@ -3055,14 +3689,14 @@ function MessageBubble({
3055
3689
  callService,
3056
3690
  onMessageAction
3057
3691
  }
3058
- )
3692
+ ))
3059
3693
  ]
3060
3694
  }
3061
3695
  );
3062
3696
  }
3063
3697
 
3064
3698
  // src/view/ChatView.tsx
3065
- import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
3699
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
3066
3700
  function lastContentLength(messages) {
3067
3701
  const last = messages[messages.length - 1];
3068
3702
  if (!last) {
@@ -3090,6 +3724,7 @@ function ChatView({
3090
3724
  className,
3091
3725
  onArtifact,
3092
3726
  onOpenArtifact,
3727
+ onOpenArtifactList,
3093
3728
  attachments,
3094
3729
  onAttachmentsChange,
3095
3730
  onAttachFiles,
@@ -3170,7 +3805,7 @@ function ChatView({
3170
3805
  model,
3171
3806
  onModelChange
3172
3807
  });
3173
- const [contextChips, setContextChips] = useState13([]);
3808
+ const [contextChips, setContextChips] = useState14([]);
3174
3809
  const handleContextChipsChange = (chips) => {
3175
3810
  setContextChips(chips);
3176
3811
  onContextsChange?.(chips);
@@ -3206,10 +3841,13 @@ function ChatView({
3206
3841
  handleSendRef.current = handleSend;
3207
3842
  const onOpenArtifactRef = useRef11(onOpenArtifact);
3208
3843
  onOpenArtifactRef.current = onOpenArtifact;
3844
+ const onOpenArtifactListRef = useRef11(onOpenArtifactList);
3845
+ onOpenArtifactListRef.current = onOpenArtifactList;
3209
3846
  useEffect9(() => {
3210
3847
  setChatChromeHandlers({
3211
3848
  sendSuggestion: (text) => handleSendRef.current(text),
3212
- openArtifact: (artifact) => onOpenArtifactRef.current?.(artifact)
3849
+ openArtifact: (artifact) => onOpenArtifactRef.current?.(artifact),
3850
+ openArtifactList: () => onOpenArtifactListRef.current?.()
3213
3851
  });
3214
3852
  }, []);
3215
3853
  const handleRegenerate = () => {
@@ -3229,7 +3867,7 @@ function ChatView({
3229
3867
  const lastMessage = messages[messages.length - 1];
3230
3868
  const regenerableMessageId = lastMessage?.role === "assistant" && lastMessage.status === "completed" ? lastMessage.id : void 0;
3231
3869
  const isEmpty = messages.length === 0;
3232
- const composerNode = /* @__PURE__ */ jsx15(
3870
+ const composerNode = /* @__PURE__ */ jsx16(
3233
3871
  Composer,
3234
3872
  {
3235
3873
  onSend: handleSend,
@@ -3254,18 +3892,18 @@ function ChatView({
3254
3892
  callService
3255
3893
  }
3256
3894
  );
3257
- const composerDock = /* @__PURE__ */ jsxs10("div", { className: "pactor-chat-view__composer-dock", children: [
3895
+ const composerDock = /* @__PURE__ */ jsxs12("div", { className: "pactor-chat-view__composer-dock", children: [
3258
3896
  composerNode,
3259
- composerBottom !== void 0 && composerBottom !== null ? /* @__PURE__ */ jsx15("div", { className: "pactor-chat-view__composer-bottom", children: composerBottom }) : null
3897
+ composerBottom !== void 0 && composerBottom !== null ? /* @__PURE__ */ jsx16("div", { className: "pactor-chat-view__composer-bottom", children: composerBottom }) : null
3260
3898
  ] });
3261
- return /* @__PURE__ */ jsxs10(
3899
+ return /* @__PURE__ */ jsxs12(
3262
3900
  "div",
3263
3901
  {
3264
3902
  className: `pactor-chat-view${isEmpty ? " pactor-chat-view--empty" : ""}${className ? ` ${className}` : ""}`,
3265
3903
  children: [
3266
- /* @__PURE__ */ jsxs10("div", { className: "pactor-chat-messages", ref: containerRef, children: [
3267
- isEmpty ? /* @__PURE__ */ jsxs10("div", { className: "pactor-chat-view__empty-stack", children: [
3268
- /* @__PURE__ */ jsx15(
3904
+ /* @__PURE__ */ jsxs12("div", { className: "pactor-chat-messages", ref: containerRef, children: [
3905
+ isEmpty ? /* @__PURE__ */ jsxs12("div", { className: "pactor-chat-view__empty-stack", children: [
3906
+ /* @__PURE__ */ jsx16(
3269
3907
  EmptyState,
3270
3908
  {
3271
3909
  greeting,
@@ -3275,7 +3913,7 @@ function ChatView({
3275
3913
  }
3276
3914
  ),
3277
3915
  composerDock
3278
- ] }) : messages.map((message) => /* @__PURE__ */ jsx15(
3916
+ ] }) : messages.map((message) => /* @__PURE__ */ jsx16(
3279
3917
  MessageBubble,
3280
3918
  {
3281
3919
  message,
@@ -3287,19 +3925,18 @@ function ChatView({
3287
3925
  onEditResend: (text) => handleEditResend(message.id, text),
3288
3926
  messageActions,
3289
3927
  callService,
3290
- onMessageAction,
3291
- onOpenArtifact
3928
+ onMessageAction
3292
3929
  },
3293
3930
  message.id
3294
3931
  )),
3295
- !isFollowing && /* @__PURE__ */ jsxs10(
3932
+ !isFollowing && /* @__PURE__ */ jsxs12(
3296
3933
  "button",
3297
3934
  {
3298
3935
  type: "button",
3299
3936
  className: "pactor-chat-scroll-bottom",
3300
3937
  onClick: scrollToBottom,
3301
3938
  children: [
3302
- /* @__PURE__ */ jsx15(Icon9, { name: "arrow-down", size: 13 }),
3939
+ /* @__PURE__ */ jsx16(Icon10, { name: "arrow-down", size: 13 }),
3303
3940
  "\u56DE\u5230\u5E95\u90E8"
3304
3941
  ]
3305
3942
  }
@@ -3312,8 +3949,8 @@ function ChatView({
3312
3949
  }
3313
3950
 
3314
3951
  // src/view/WorkspaceHeader.tsx
3315
- import { Icon as Icon10 } from "@pactor-app/ui/components";
3316
- import { useEffect as useEffect10, useRef as useRef12, useState as useState14 } from "react";
3952
+ import { Icon as Icon11 } from "@pactor-app/ui/components";
3953
+ import { useEffect as useEffect10, useRef as useRef12, useState as useState15 } from "react";
3317
3954
 
3318
3955
  // src/export-markdown.ts
3319
3956
  var ROLE_LABELS = {
@@ -3390,7 +4027,7 @@ function buildHeaderActionsFragment(props) {
3390
4027
  }
3391
4028
 
3392
4029
  // src/view/WorkspaceHeader.tsx
3393
- import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
4030
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
3394
4031
  function readCurrentTheme() {
3395
4032
  if (typeof document === "undefined") {
3396
4033
  return "light";
@@ -3410,9 +4047,9 @@ function WorkspaceHeader({
3410
4047
  onTitleChange
3411
4048
  }) {
3412
4049
  const hasActions = headerActions !== void 0 && headerActions.length > 0;
3413
- const [overrideTitle, setOverrideTitle] = useState14(null);
3414
- const [editing, setEditing] = useState14(false);
3415
- const [draft, setDraft] = useState14("");
4050
+ const [overrideTitle, setOverrideTitle] = useState15(null);
4051
+ const [editing, setEditing] = useState15(false);
4052
+ const [draft, setDraft] = useState15("");
3416
4053
  const prevTitleRef = useRef12(title);
3417
4054
  if (prevTitleRef.current !== title) {
3418
4055
  prevTitleRef.current = title;
@@ -3421,7 +4058,7 @@ function WorkspaceHeader({
3421
4058
  }
3422
4059
  }
3423
4060
  const displayTitle = overrideTitle ?? title;
3424
- const [theme, setTheme] = useState14(readCurrentTheme);
4061
+ const [theme, setTheme] = useState15(readCurrentTheme);
3425
4062
  const commitTitle = () => {
3426
4063
  setEditing(false);
3427
4064
  const next = draft.trim();
@@ -3465,7 +4102,7 @@ function WorkspaceHeader({
3465
4102
  });
3466
4103
  }, []);
3467
4104
  const actionButtons = {
3468
- export: /* @__PURE__ */ jsx16(
4105
+ export: /* @__PURE__ */ jsx17(
3469
4106
  "button",
3470
4107
  {
3471
4108
  type: "button",
@@ -3473,11 +4110,11 @@ function WorkspaceHeader({
3473
4110
  title: "\u5BFC\u51FA Markdown",
3474
4111
  "aria-label": "\u5BFC\u51FA Markdown",
3475
4112
  onClick: handleExport,
3476
- children: /* @__PURE__ */ jsx16(Icon10, { name: "download", size: 15 })
4113
+ children: /* @__PURE__ */ jsx17(Icon11, { name: "download", size: 15 })
3477
4114
  },
3478
4115
  "export"
3479
4116
  ),
3480
- theme: /* @__PURE__ */ jsx16(
4117
+ theme: /* @__PURE__ */ jsx17(
3481
4118
  "button",
3482
4119
  {
3483
4120
  type: "button",
@@ -3485,11 +4122,11 @@ function WorkspaceHeader({
3485
4122
  title: theme === "dark" ? "\u5207\u6362\u4E3A\u6D45\u8272" : "\u5207\u6362\u4E3A\u6DF1\u8272",
3486
4123
  "aria-label": "\u5207\u6362\u660E\u6697\u4E3B\u9898",
3487
4124
  onClick: handleTheme,
3488
- children: theme === "dark" ? /* @__PURE__ */ jsx16(Icon10, { name: "sun", size: 15 }) : /* @__PURE__ */ jsx16(Icon10, { name: "moon", size: 15 })
4125
+ children: theme === "dark" ? /* @__PURE__ */ jsx17(Icon11, { name: "sun", size: 15 }) : /* @__PURE__ */ jsx17(Icon11, { name: "moon", size: 15 })
3489
4126
  },
3490
4127
  "theme"
3491
4128
  ),
3492
- clear: /* @__PURE__ */ jsx16(
4129
+ clear: /* @__PURE__ */ jsx17(
3493
4130
  "button",
3494
4131
  {
3495
4132
  type: "button",
@@ -3497,12 +4134,12 @@ function WorkspaceHeader({
3497
4134
  title: "\u6E05\u7A7A\u4F1A\u8BDD",
3498
4135
  "aria-label": "\u6E05\u7A7A\u4F1A\u8BDD",
3499
4136
  onClick: handleClear,
3500
- children: /* @__PURE__ */ jsx16(Icon10, { name: "trash", size: 15 })
4137
+ children: /* @__PURE__ */ jsx17(Icon11, { name: "trash", size: 15 })
3501
4138
  },
3502
4139
  "clear"
3503
4140
  )
3504
4141
  };
3505
- const titleNode = editableTitle ? editing ? /* @__PURE__ */ jsx16(
4142
+ const titleNode = editableTitle ? editing ? /* @__PURE__ */ jsx17(
3506
4143
  "input",
3507
4144
  {
3508
4145
  className: "pactor-chat-workspace__title-input",
@@ -3519,7 +4156,7 @@ function WorkspaceHeader({
3519
4156
  }
3520
4157
  }
3521
4158
  }
3522
- ) : /* @__PURE__ */ jsx16(
4159
+ ) : /* @__PURE__ */ jsx17(
3523
4160
  "button",
3524
4161
  {
3525
4162
  type: "button",
@@ -3531,17 +4168,17 @@ function WorkspaceHeader({
3531
4168
  },
3532
4169
  children: displayTitle
3533
4170
  }
3534
- ) : /* @__PURE__ */ jsx16("div", { className: "pactor-chat-workspace__title", children: title });
3535
- return /* @__PURE__ */ jsxs11("header", { className: "pactor-chat-workspace__header", children: [
4171
+ ) : /* @__PURE__ */ jsx17("div", { className: "pactor-chat-workspace__title", children: title });
4172
+ return /* @__PURE__ */ jsxs13("header", { className: "pactor-chat-workspace__header", children: [
3536
4173
  headerStart !== void 0 ? (
3537
4174
  // header 位模型 chip(决策 D2):与 title 组成左端组,chip 在前
3538
- /* @__PURE__ */ jsxs11("div", { className: "pactor-chat-workspace__header-left", children: [
4175
+ /* @__PURE__ */ jsxs13("div", { className: "pactor-chat-workspace__header-left", children: [
3539
4176
  headerStart,
3540
4177
  titleNode
3541
4178
  ] })
3542
4179
  ) : titleNode,
3543
- hasActions ? /* @__PURE__ */ jsxs11("div", { className: "pactor-chat-workspace__header-right", children: [
3544
- /* @__PURE__ */ jsx16("div", { className: "pactor-chat-workspace__header-actions", children: /* @__PURE__ */ jsx16(
4180
+ hasActions ? /* @__PURE__ */ jsxs13("div", { className: "pactor-chat-workspace__header-right", children: [
4181
+ /* @__PURE__ */ jsx17("div", { className: "pactor-chat-workspace__header-actions", children: /* @__PURE__ */ jsx17(
3545
4182
  ChromeSlot,
3546
4183
  {
3547
4184
  fragment: buildHeaderActionsFragment({
@@ -3551,13 +4188,13 @@ function WorkspaceHeader({
3551
4188
  fallback: headerActions.map((action) => actionButtons[action])
3552
4189
  }
3553
4190
  ) }),
3554
- headerExtra !== void 0 ? /* @__PURE__ */ jsx16("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
3555
- ] }) : headerExtra !== void 0 ? /* @__PURE__ */ jsx16("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
4191
+ headerExtra !== void 0 ? /* @__PURE__ */ jsx17("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
4192
+ ] }) : headerExtra !== void 0 ? /* @__PURE__ */ jsx17("div", { className: "pactor-chat-workspace__header-extra", children: headerExtra }) : null
3556
4193
  ] });
3557
4194
  }
3558
4195
 
3559
4196
  // src/chat-composition.tsx
3560
- import { Fragment as Fragment9, jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
4197
+ import { Fragment as Fragment9, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
3561
4198
  function DslNodeSlotInner({ node }) {
3562
4199
  const { renderChildren } = useRenderer4();
3563
4200
  return renderChildren([node]);
@@ -3580,7 +4217,7 @@ var DslSlotGuard = class extends Component3 {
3580
4217
  }
3581
4218
  };
3582
4219
  function DslNodeSlot({ node }) {
3583
- return /* @__PURE__ */ jsx17(DslSlotGuard, { children: /* @__PURE__ */ jsx17(DslNodeSlotInner, { node }) });
4220
+ return /* @__PURE__ */ jsx18(DslSlotGuard, { children: /* @__PURE__ */ jsx18(DslNodeSlotInner, { node }) });
3584
4221
  }
3585
4222
  function isDslNodeValue(value) {
3586
4223
  return typeof value === "object" && value !== null && !Array.isArray(value) && !isValidElement(value) && typeof value.type === "string";
@@ -3590,7 +4227,7 @@ function resolveSlot(slot) {
3590
4227
  return void 0;
3591
4228
  }
3592
4229
  if (isDslNodeValue(slot)) {
3593
- return /* @__PURE__ */ jsx17(DslNodeSlot, { node: slot });
4230
+ return /* @__PURE__ */ jsx18(DslNodeSlot, { node: slot });
3594
4231
  }
3595
4232
  return slot;
3596
4233
  }
@@ -3629,10 +4266,13 @@ function ChatComposition({
3629
4266
  onSend,
3630
4267
  artifact,
3631
4268
  panelOpen,
4269
+ artifacts: artifactsProp,
4270
+ onSelectArtifact,
3632
4271
  onPanelClose,
3633
4272
  inspector,
3634
4273
  onArtifact,
3635
- onOpenArtifact
4274
+ onOpenArtifact,
4275
+ onOpenArtifactList
3636
4276
  }) {
3637
4277
  const storeRef = useRef13(null);
3638
4278
  if (storeRef.current === null) {
@@ -3649,8 +4289,28 @@ function ChatComposition({
3649
4289
  (listener) => artifactStore.subscribe(listener),
3650
4290
  () => artifactStore.get()
3651
4291
  );
4292
+ const storeArtifacts = useSyncExternalStore2(
4293
+ (listener) => artifactStore.subscribe(listener),
4294
+ () => artifactStore.list()
4295
+ );
4296
+ const messages = useSyncExternalStore2(
4297
+ (listener) => store.subscribe(listener),
4298
+ () => store.getMessages()
4299
+ );
4300
+ const streamArtifacts = useMemo4(() => collectArtifacts(messages), [messages]);
4301
+ const mergedArtifacts = useMemo4(
4302
+ () => mergeArtifacts(streamArtifacts, storeArtifacts),
4303
+ [streamArtifacts, storeArtifacts]
4304
+ );
3652
4305
  const effectiveArtifact = panelControlled ? artifact ?? null : storeArtifact;
3653
4306
  const effectivePanelOpen = panelControlled ? panelOpen ?? artifact != null : storeArtifact !== null;
4307
+ const effectiveArtifacts = artifactsProp ?? mergedArtifacts;
4308
+ const handleSelectArtifact = (next) => {
4309
+ onSelectArtifact?.(next);
4310
+ if (!panelControlled) {
4311
+ artifactStore.open(next);
4312
+ }
4313
+ };
3654
4314
  const handlePanelClose = () => {
3655
4315
  onPanelClose?.();
3656
4316
  if (!panelControlled) {
@@ -3663,7 +4323,18 @@ function ChatComposition({
3663
4323
  artifactStore.open(next);
3664
4324
  }
3665
4325
  };
3666
- const [attachments, setAttachments] = useState15([]);
4326
+ const [listViewRequest, setListViewRequest] = useState16(null);
4327
+ const handleOpenArtifactList = () => {
4328
+ onOpenArtifactList?.();
4329
+ setListViewRequest((value) => (value ?? 0) + 1);
4330
+ if (!panelControlled && artifactStore.get() === null) {
4331
+ const first = mergedArtifacts[0];
4332
+ if (first !== void 0) {
4333
+ artifactStore.open(first);
4334
+ }
4335
+ }
4336
+ };
4337
+ const [attachments, setAttachments] = useState16([]);
3667
4338
  const handleAttachFiles = services.onAttachFiles ? (files) => {
3668
4339
  void Promise.resolve(services.onAttachFiles(files)).then(
3669
4340
  (created) => {
@@ -3683,7 +4354,7 @@ function ChatComposition({
3683
4354
  model,
3684
4355
  onModelChange
3685
4356
  });
3686
- const headerModelChip = modelPlacement === "header" && modelsEnabled ? /* @__PURE__ */ jsx17(
4357
+ const headerModelChip = modelPlacement === "header" && modelsEnabled ? /* @__PURE__ */ jsx18(
3687
4358
  ModelSelect,
3688
4359
  {
3689
4360
  models,
@@ -3691,133 +4362,144 @@ function ChatComposition({
3691
4362
  onModelChange: handleModelChange
3692
4363
  }
3693
4364
  ) : void 0;
3694
- const mergedToolbarEnd = services.toolbarEnd !== void 0 || toolbarEnd !== void 0 ? /* @__PURE__ */ jsxs12(Fragment9, { children: [
4365
+ const mergedToolbarEnd = services.toolbarEnd !== void 0 || toolbarEnd !== void 0 ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
3695
4366
  services.toolbarEnd,
3696
4367
  toolbarEnd
3697
4368
  ] }) : void 0;
3698
- const mergedToolbarStart = services.toolbarStart !== void 0 || toolbarStart !== void 0 ? /* @__PURE__ */ jsxs12(Fragment9, { children: [
4369
+ const mergedToolbarStart = services.toolbarStart !== void 0 || toolbarStart !== void 0 ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
3699
4370
  services.toolbarStart,
3700
4371
  toolbarStart
3701
4372
  ] }) : void 0;
3702
- const mergedComposerFooter = services.composerFooter !== void 0 || composerFooter !== void 0 ? /* @__PURE__ */ jsxs12(Fragment9, { children: [
4373
+ const mergedComposerFooter = services.composerFooter !== void 0 || composerFooter !== void 0 ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
3703
4374
  services.composerFooter,
3704
4375
  composerFooter
3705
4376
  ] }) : void 0;
3706
- const mergedComposerBottom = services.composerBottom !== void 0 || composerBottom !== void 0 ? /* @__PURE__ */ jsxs12(Fragment9, { children: [
4377
+ const mergedComposerBottom = services.composerBottom !== void 0 || composerBottom !== void 0 ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
3707
4378
  services.composerBottom,
3708
4379
  composerBottom
3709
4380
  ] }) : void 0;
3710
4381
  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;
3711
4382
  const renderHeader = showHeader !== false && hasHeaderContent;
3712
- const workspace = /* @__PURE__ */ jsxs12(
4383
+ const workspaceHeader = renderHeader ? header ?? /* @__PURE__ */ jsx18(
4384
+ WorkspaceHeader,
4385
+ {
4386
+ title,
4387
+ headerExtra,
4388
+ headerActions,
4389
+ editableTitle,
4390
+ headerStart: headerModelChip,
4391
+ store,
4392
+ onExport,
4393
+ onThemeChange,
4394
+ onClear,
4395
+ onTitleChange
4396
+ }
4397
+ ) : null;
4398
+ const workspace = /* @__PURE__ */ jsx18(
3713
4399
  "div",
3714
4400
  {
3715
4401
  className: `pactor-chat-workspace${className ? ` ${className}` : ""}`,
3716
- children: [
3717
- renderHeader ? header ?? /* @__PURE__ */ jsx17(
3718
- WorkspaceHeader,
4402
+ children: /* @__PURE__ */ jsx18(
4403
+ ChatView,
4404
+ {
4405
+ store,
4406
+ transport: services.transport,
4407
+ onSend: handleSend,
4408
+ safeRegistry: services.safeRegistry,
4409
+ capabilities: services.capabilities,
4410
+ permission: services.permission,
4411
+ confirm: services.confirm,
4412
+ conversationId,
4413
+ user: services.user,
4414
+ params,
4415
+ greeting,
4416
+ subtitle,
4417
+ suggestions,
4418
+ attachments,
4419
+ onAttachmentsChange: setAttachments,
4420
+ onAttachFiles: handleAttachFiles,
4421
+ onAttachmentClick: services.onAttachmentClick,
4422
+ toolbarStart: mergedToolbarStart,
4423
+ toolbarEnd: mergedToolbarEnd,
4424
+ composerFooter: mergedComposerFooter,
4425
+ composerBottom: mergedComposerBottom,
4426
+ hideToolbar,
4427
+ onMentionTrigger: services.onMentionTrigger,
4428
+ onPlusMenu: services.onPlusMenu,
4429
+ onArtifact,
4430
+ onOpenArtifact: handleOpenArtifact,
4431
+ onOpenArtifactList: handleOpenArtifactList,
4432
+ models,
4433
+ model: currentModel,
4434
+ onModelChange: handleModelChange,
4435
+ modelPlacement,
4436
+ contexts,
4437
+ onContextsChange,
4438
+ callService: services.callService,
4439
+ messageActions,
4440
+ onMessageAction
4441
+ }
4442
+ )
4443
+ }
4444
+ );
4445
+ const main = sidebar ? /* @__PURE__ */ jsx18(AppShell, { sidebar, children: workspace }) : workspace;
4446
+ 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;
4447
+ if (!panelFeatureUsed && !effectivePanelOpen) {
4448
+ return workspaceHeader ? /* @__PURE__ */ jsxs14("div", { className: "pactor-chat-with-header", children: [
4449
+ workspaceHeader,
4450
+ main
4451
+ ] }) : main;
4452
+ }
4453
+ return /* @__PURE__ */ jsxs14("div", { className: "pactor-chat-inspector-host", children: [
4454
+ workspaceHeader,
4455
+ /* @__PURE__ */ jsxs14("div", { className: "pactor-chat-inspector-host__row", children: [
4456
+ main,
4457
+ effectivePanelOpen ? /* @__PURE__ */ jsxs14(Fragment9, { children: [
4458
+ /* @__PURE__ */ jsx18(
4459
+ "div",
3719
4460
  {
3720
- title,
3721
- headerExtra,
3722
- headerActions,
3723
- editableTitle,
3724
- headerStart: headerModelChip,
3725
- store,
3726
- onExport,
3727
- onThemeChange,
3728
- onClear,
3729
- onTitleChange
4461
+ className: "pactor-chat-inspector__scrim",
4462
+ "aria-hidden": true,
4463
+ onClick: handlePanelClose
3730
4464
  }
3731
- ) : null,
3732
- /* @__PURE__ */ jsx17(
3733
- ChatView,
4465
+ ),
4466
+ /* @__PURE__ */ jsx18(
4467
+ ArtifactPanel,
3734
4468
  {
3735
- store,
3736
- transport: services.transport,
3737
- onSend: handleSend,
4469
+ artifact: effectiveArtifact,
4470
+ artifacts: effectiveArtifacts,
4471
+ activeArtifactId: effectiveArtifact?.id,
4472
+ onSelectArtifact: handleSelectArtifact,
4473
+ inspector,
4474
+ onClose: handlePanelClose,
3738
4475
  safeRegistry: services.safeRegistry,
3739
- capabilities: services.capabilities,
3740
- permission: services.permission,
3741
- confirm: services.confirm,
3742
- conversationId,
3743
4476
  user: services.user,
3744
- params,
3745
- greeting,
3746
- subtitle,
3747
- suggestions,
3748
- attachments,
3749
- onAttachmentsChange: setAttachments,
3750
- onAttachFiles: handleAttachFiles,
3751
- onAttachmentClick: services.onAttachmentClick,
3752
- toolbarStart: mergedToolbarStart,
3753
- toolbarEnd: mergedToolbarEnd,
3754
- composerFooter: mergedComposerFooter,
3755
- composerBottom: mergedComposerBottom,
3756
- hideToolbar,
3757
- onMentionTrigger: services.onMentionTrigger,
3758
- onPlusMenu: services.onPlusMenu,
3759
- onArtifact,
3760
- onOpenArtifact: handleOpenArtifact,
3761
- models,
3762
- model: currentModel,
3763
- onModelChange: handleModelChange,
3764
- modelPlacement,
3765
- contexts,
3766
- onContextsChange,
3767
- callService: services.callService,
3768
- messageActions,
3769
- onMessageAction
4477
+ artifactViewers: services.artifactViewers,
4478
+ viewRequest: listViewRequest,
4479
+ onViewRequestConsumed: () => setListViewRequest(null)
3770
4480
  }
3771
4481
  )
3772
- ]
3773
- }
3774
- );
3775
- const main = sidebar ? /* @__PURE__ */ jsx17(AppShell, { sidebar, children: workspace }) : workspace;
3776
- const panelFeatureUsed = artifact !== void 0 || panelOpen !== void 0 || inspector !== void 0 || onPanelClose !== void 0 || onArtifact !== void 0 || onOpenArtifact !== void 0 || services.artifactStore !== void 0;
3777
- if (!panelFeatureUsed && !effectivePanelOpen) {
3778
- return main;
3779
- }
3780
- return /* @__PURE__ */ jsxs12("div", { className: "pactor-chat-inspector-host", children: [
3781
- main,
3782
- effectivePanelOpen ? /* @__PURE__ */ jsxs12(Fragment9, { children: [
3783
- /* @__PURE__ */ jsx17(
3784
- "div",
3785
- {
3786
- className: "pactor-chat-inspector__scrim",
3787
- "aria-hidden": true,
3788
- onClick: handlePanelClose
3789
- }
3790
- ),
3791
- /* @__PURE__ */ jsx17(
3792
- ArtifactPanel,
3793
- {
3794
- artifact: effectiveArtifact,
3795
- inspector,
3796
- onClose: handlePanelClose,
3797
- safeRegistry: services.safeRegistry,
3798
- user: services.user
3799
- }
3800
- )
3801
- ] }) : null
4482
+ ] }) : null
4483
+ ] })
3802
4484
  ] });
3803
4485
  }
3804
4486
 
3805
4487
  // src/chat-services.tsx
3806
- import { createContext as createContext2, useContext as useContext2 } from "react";
3807
- import { jsx as jsx18 } from "react/jsx-runtime";
3808
- var ChatServicesContext = createContext2(null);
4488
+ import { createContext as createContext3, useContext as useContext3 } from "react";
4489
+ import { jsx as jsx19 } from "react/jsx-runtime";
4490
+ var ChatServicesContext = createContext3(null);
3809
4491
  function ChatServicesProvider({
3810
4492
  value,
3811
4493
  children
3812
4494
  }) {
3813
- return /* @__PURE__ */ jsx18(ChatServicesContext.Provider, { value, children });
4495
+ return /* @__PURE__ */ jsx19(ChatServicesContext.Provider, { value, children });
3814
4496
  }
3815
4497
  function useChatServices() {
3816
- return useContext2(ChatServicesContext);
4498
+ return useContext3(ChatServicesContext);
3817
4499
  }
3818
4500
 
3819
4501
  // src/chat-workspace.tsx
3820
- import { jsx as jsx19 } from "react/jsx-runtime";
4502
+ import { jsx as jsx20 } from "react/jsx-runtime";
3821
4503
  function createChatWorkspace(services) {
3822
4504
  function ChatWorkspace({
3823
4505
  title,
@@ -3838,6 +4520,8 @@ function createChatWorkspace(services) {
3838
4520
  inspector,
3839
4521
  onArtifact,
3840
4522
  onOpenArtifact,
4523
+ onOpenArtifactList,
4524
+ onSelectArtifact,
3841
4525
  headerActions,
3842
4526
  editableTitle,
3843
4527
  onExport,
@@ -3854,7 +4538,7 @@ function createChatWorkspace(services) {
3854
4538
  onMessageAction,
3855
4539
  className
3856
4540
  }) {
3857
- return /* @__PURE__ */ jsx19(ChatServicesProvider, { value: services, children: /* @__PURE__ */ jsx19(
4541
+ return /* @__PURE__ */ jsx20(ChatServicesProvider, { value: services, children: /* @__PURE__ */ jsx20(
3858
4542
  ChatComposition,
3859
4543
  {
3860
4544
  services,
@@ -3866,18 +4550,20 @@ function createChatWorkspace(services) {
3866
4550
  params,
3867
4551
  className,
3868
4552
  headerExtra: services.headerExtra,
3869
- sidebar: sidebar ? /* @__PURE__ */ jsx19(DslNodeSlot, { node: sidebar }) : void 0,
3870
- toolbarStart: toolbarStart ? /* @__PURE__ */ jsx19(DslNodeSlot, { node: toolbarStart }) : void 0,
3871
- toolbarEnd: toolbarEnd ? /* @__PURE__ */ jsx19(DslNodeSlot, { node: toolbarEnd }) : void 0,
3872
- composerFooter: composerFooter ? /* @__PURE__ */ jsx19(DslNodeSlot, { node: composerFooter }) : void 0,
3873
- composerBottom: composerBottom ? /* @__PURE__ */ jsx19(DslNodeSlot, { node: composerBottom }) : void 0,
4553
+ sidebar: sidebar ? /* @__PURE__ */ jsx20(DslNodeSlot, { node: sidebar }) : void 0,
4554
+ toolbarStart: toolbarStart ? /* @__PURE__ */ jsx20(DslNodeSlot, { node: toolbarStart }) : void 0,
4555
+ toolbarEnd: toolbarEnd ? /* @__PURE__ */ jsx20(DslNodeSlot, { node: toolbarEnd }) : void 0,
4556
+ composerFooter: composerFooter ? /* @__PURE__ */ jsx20(DslNodeSlot, { node: composerFooter }) : void 0,
4557
+ composerBottom: composerBottom ? /* @__PURE__ */ jsx20(DslNodeSlot, { node: composerBottom }) : void 0,
3874
4558
  hideToolbar,
3875
4559
  artifact,
3876
4560
  panelOpen,
3877
4561
  onPanelClose,
3878
- inspector: inspector ? /* @__PURE__ */ jsx19(DslNodeSlot, { node: inspector }) : void 0,
4562
+ inspector: inspector ? /* @__PURE__ */ jsx20(DslNodeSlot, { node: inspector }) : void 0,
3879
4563
  onArtifact,
3880
4564
  onOpenArtifact,
4565
+ onOpenArtifactList,
4566
+ onSelectArtifact,
3881
4567
  headerActions,
3882
4568
  editableTitle,
3883
4569
  onExport,
@@ -3899,7 +4585,7 @@ function createChatWorkspace(services) {
3899
4585
  }
3900
4586
 
3901
4587
  // src/view/Chat.tsx
3902
- import { createComponentRegistry, Icon as Icon11, RailTooltip } from "@pactor-app/ui/components";
4588
+ import { createComponentRegistry, Icon as Icon12, RailTooltip } from "@pactor-app/ui/components";
3903
4589
  import { Fragment as Fragment10, useEffect as useEffect11, useRef as useRef14 } from "react";
3904
4590
 
3905
4591
  // src/view/chrome/menu.tsx
@@ -3929,7 +4615,7 @@ function buildSidebarMenuFragment(props) {
3929
4615
  }
3930
4616
 
3931
4617
  // src/view/ChatShell.tsx
3932
- import { jsx as jsx20 } from "react/jsx-runtime";
4618
+ import { jsx as jsx21 } from "react/jsx-runtime";
3933
4619
  function ChatShell({
3934
4620
  menu,
3935
4621
  activeMenuKey,
@@ -3941,7 +4627,7 @@ function ChatShell({
3941
4627
  className,
3942
4628
  onMenuSelect
3943
4629
  }) {
3944
- const sidebarContent = resolveSlot(sidebar) ?? (menu && menu.length > 0 ? /* @__PURE__ */ jsx20(
4630
+ const sidebarContent = resolveSlot(sidebar) ?? (menu && menu.length > 0 ? /* @__PURE__ */ jsx21(
3945
4631
  ChatSidebarMenu,
3946
4632
  {
3947
4633
  menu,
@@ -3949,7 +4635,7 @@ function ChatShell({
3949
4635
  onMenuSelect
3950
4636
  }
3951
4637
  ) : null);
3952
- return /* @__PURE__ */ jsx20(
4638
+ return /* @__PURE__ */ jsx21(
3953
4639
  AppShell,
3954
4640
  {
3955
4641
  sidebar: sidebarContent,
@@ -3966,7 +4652,7 @@ function ChatShellDslAdapter({
3966
4652
  onMenuSelect,
3967
4653
  ...rest
3968
4654
  }) {
3969
- return /* @__PURE__ */ jsx20(
4655
+ return /* @__PURE__ */ jsx21(
3970
4656
  ChatShell,
3971
4657
  {
3972
4658
  ...rest,
@@ -3981,7 +4667,7 @@ function registerChatShellComponent(registry) {
3981
4667
  }
3982
4668
 
3983
4669
  // src/view/Chat.tsx
3984
- import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
4670
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
3985
4671
  function ChatSidebarMenu({
3986
4672
  menu,
3987
4673
  activeMenuKey,
@@ -4008,12 +4694,12 @@ function ChatSidebarMenu({
4008
4694
  }, []);
4009
4695
  const fallback = (() => {
4010
4696
  let lastGroup;
4011
- return /* @__PURE__ */ jsx21("nav", { className: "pactor-chat-menu", "aria-label": "\u4F1A\u8BDD\u5BFC\u822A", children: menu.map((item) => {
4697
+ return /* @__PURE__ */ jsx22("nav", { className: "pactor-chat-menu", "aria-label": "\u4F1A\u8BDD\u5BFC\u822A", children: menu.map((item) => {
4012
4698
  const groupLabel = item.group !== void 0 && item.group !== lastGroup ? item.group : void 0;
4013
4699
  lastGroup = item.group;
4014
4700
  const active = item.key === activeMenuKey;
4015
4701
  const collapsed = drawer?.collapsed === true;
4016
- const button = /* @__PURE__ */ jsxs13(
4702
+ const button = /* @__PURE__ */ jsxs15(
4017
4703
  "button",
4018
4704
  {
4019
4705
  type: "button",
@@ -4028,20 +4714,20 @@ function ChatSidebarMenu({
4028
4714
  drawer?.close();
4029
4715
  },
4030
4716
  children: [
4031
- item.icon !== void 0 ? /* @__PURE__ */ jsx21("span", { className: "pactor-chat-menu__icon", children: /* @__PURE__ */ jsx21(Icon11, { name: item.icon, size: 15, strokeWidth: 1.7 }) }) : null,
4032
- /* @__PURE__ */ jsx21("span", { className: "pactor-chat-menu__label", children: item.label }),
4033
- item.dot === true ? /* @__PURE__ */ jsx21("span", { className: "pactor-chat-menu__dot", "aria-hidden": true }) : null,
4034
- item.badge !== void 0 ? /* @__PURE__ */ jsx21("span", { className: "pactor-chat-menu__badge", children: item.badge }) : null
4717
+ item.icon !== void 0 ? /* @__PURE__ */ jsx22("span", { className: "pactor-chat-menu__icon", children: /* @__PURE__ */ jsx22(Icon12, { name: item.icon, size: 15, strokeWidth: 1.7 }) }) : null,
4718
+ /* @__PURE__ */ jsx22("span", { className: "pactor-chat-menu__label", children: item.label }),
4719
+ item.dot === true ? /* @__PURE__ */ jsx22("span", { className: "pactor-chat-menu__dot", "aria-hidden": true }) : null,
4720
+ item.badge !== void 0 ? /* @__PURE__ */ jsx22("span", { className: "pactor-chat-menu__badge", children: item.badge }) : null
4035
4721
  ]
4036
4722
  }
4037
4723
  );
4038
- return /* @__PURE__ */ jsxs13(Fragment10, { children: [
4039
- groupLabel !== void 0 ? /* @__PURE__ */ jsx21("div", { className: "pactor-chat-menu__group", children: groupLabel }) : null,
4040
- collapsed ? /* @__PURE__ */ jsx21(RailTooltip, { label: item.label, children: button }) : button
4724
+ return /* @__PURE__ */ jsxs15(Fragment10, { children: [
4725
+ groupLabel !== void 0 ? /* @__PURE__ */ jsx22("div", { className: "pactor-chat-menu__group", children: groupLabel }) : null,
4726
+ collapsed ? /* @__PURE__ */ jsx22(RailTooltip, { label: item.label, children: button }) : button
4041
4727
  ] }, item.key);
4042
4728
  }) });
4043
4729
  })();
4044
- return /* @__PURE__ */ jsx21(
4730
+ return /* @__PURE__ */ jsx22(
4045
4731
  ChromeSlot,
4046
4732
  {
4047
4733
  fragment: buildSidebarMenuFragment({ menu, activeMenuKey }),
@@ -4087,6 +4773,8 @@ function Chat({
4087
4773
  inspector,
4088
4774
  onArtifact,
4089
4775
  onOpenArtifact,
4776
+ onOpenArtifactList,
4777
+ onSelectArtifact,
4090
4778
  className,
4091
4779
  onSend,
4092
4780
  onMenuSelect
@@ -4100,10 +4788,10 @@ function Chat({
4100
4788
  }
4101
4789
  }, [services]);
4102
4790
  if (!services) {
4103
- return /* @__PURE__ */ jsx21("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" });
4791
+ return /* @__PURE__ */ jsx22("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" });
4104
4792
  }
4105
4793
  const sidebarSlot = resolveSlot(sidebar);
4106
- const sidebarContent = sidebarSlot ?? (menu && menu.length > 0 ? /* @__PURE__ */ jsx21(
4794
+ const sidebarContent = sidebarSlot ?? (menu && menu.length > 0 ? /* @__PURE__ */ jsx22(
4107
4795
  ChatSidebarMenu,
4108
4796
  {
4109
4797
  menu,
@@ -4111,7 +4799,7 @@ function Chat({
4111
4799
  onMenuSelect
4112
4800
  }
4113
4801
  ) : void 0);
4114
- return /* @__PURE__ */ jsx21(
4802
+ return /* @__PURE__ */ jsx22(
4115
4803
  ChatComposition,
4116
4804
  {
4117
4805
  services,
@@ -4151,7 +4839,9 @@ function Chat({
4151
4839
  onPanelClose,
4152
4840
  inspector: resolveSlot(inspector),
4153
4841
  onArtifact,
4154
- onOpenArtifact
4842
+ onOpenArtifact,
4843
+ onOpenArtifactList,
4844
+ onSelectArtifact
4155
4845
  }
4156
4846
  );
4157
4847
  }
@@ -4162,6 +4852,8 @@ function ChatDslAdapter({
4162
4852
  onPanelClose,
4163
4853
  onArtifact,
4164
4854
  onOpenArtifact,
4855
+ onOpenArtifactList,
4856
+ onSelectArtifact,
4165
4857
  onModelChange,
4166
4858
  onContextsChange,
4167
4859
  onMessageAction,
@@ -4171,7 +4863,7 @@ function ChatDslAdapter({
4171
4863
  onTitleChange,
4172
4864
  ...rest
4173
4865
  }) {
4174
- return /* @__PURE__ */ jsx21(
4866
+ return /* @__PURE__ */ jsx22(
4175
4867
  Chat,
4176
4868
  {
4177
4869
  ...rest,
@@ -4180,6 +4872,8 @@ function ChatDslAdapter({
4180
4872
  onPanelClose: onPanelClose ? () => onPanelClose({}) : void 0,
4181
4873
  onArtifact: onArtifact ? (artifact) => onArtifact({ artifact }) : void 0,
4182
4874
  onOpenArtifact: onOpenArtifact ? (artifact) => onOpenArtifact({ artifact }) : void 0,
4875
+ onOpenArtifactList: onOpenArtifactList ? () => onOpenArtifactList({}) : void 0,
4876
+ onSelectArtifact: onSelectArtifact ? (artifact) => onSelectArtifact({ artifact }) : void 0,
4183
4877
  onModelChange: onModelChange ? (model) => onModelChange({ model }) : void 0,
4184
4878
  onContextsChange: onContextsChange ? (contexts) => onContextsChange({ contexts }) : void 0,
4185
4879
  onMessageAction: onMessageAction ? (action, message) => onMessageAction({ action, message }) : void 0,
@@ -4203,11 +4897,11 @@ function createChatComponentRegistry() {
4203
4897
  }
4204
4898
 
4205
4899
  // src/view/ChatLayout.tsx
4206
- import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
4900
+ import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
4207
4901
  function ChatLayout({ title, children }) {
4208
- return /* @__PURE__ */ jsxs14("div", { className: "pactor-chat-layout", children: [
4209
- /* @__PURE__ */ jsx22("header", { className: "pactor-chat-layout__header", children: title ?? "Chat" }),
4210
- /* @__PURE__ */ jsx22("main", { className: "pactor-chat-layout__main", children })
4902
+ return /* @__PURE__ */ jsxs16("div", { className: "pactor-chat-layout", children: [
4903
+ /* @__PURE__ */ jsx23("header", { className: "pactor-chat-layout__header", children: title ?? "Chat" }),
4904
+ /* @__PURE__ */ jsx23("main", { className: "pactor-chat-layout__main", children })
4211
4905
  ] });
4212
4906
  }
4213
4907
  var CHAT_LAYOUT_TYPE = "chat";
@@ -4217,6 +4911,7 @@ function registerChatLayout(layoutRegistry) {
4217
4911
  }
4218
4912
  }
4219
4913
  export {
4914
+ ARTIFACT_IMAGE_EXTENSIONS,
4220
4915
  ActivityTrace,
4221
4916
  AppShell,
4222
4917
  ArtifactPanel,
@@ -4226,6 +4921,7 @@ export {
4226
4921
  CHAT_LAYOUT_TYPE,
4227
4922
  CHAT_SHELL_COMPONENT_TYPE,
4228
4923
  Chat,
4924
+ ChatArtifactCard,
4229
4925
  ChatDenied,
4230
4926
  ChatLayout,
4231
4927
  ChatServicesProvider,
@@ -4240,7 +4936,11 @@ export {
4240
4936
  MessageBubble,
4241
4937
  PartRenderer,
4242
4938
  applyChatEvent,
4939
+ artifactCardMeta,
4940
+ buildArtifactBlob,
4941
+ buildArtifactTree,
4243
4942
  buildChatScope,
4943
+ collectArtifacts,
4244
4944
  collectMessageText,
4245
4945
  createChatComponentRegistry,
4246
4946
  createChatId,
@@ -4249,14 +4949,24 @@ export {
4249
4949
  createManualDriver,
4250
4950
  createSafeRegistry,
4251
4951
  detectStreamingMode,
4952
+ downloadArtifact,
4953
+ formatArtifactSize,
4954
+ formatMessageTime,
4252
4955
  guardChatDsl,
4956
+ isImageArtifact,
4957
+ mergeArtifacts,
4253
4958
  pairToolTraces,
4254
4959
  registerCapabilityAction,
4255
4960
  registerChatComponent,
4256
4961
  registerChatLayout,
4257
4962
  registerChatShellComponent,
4963
+ resolveArtifactFileType,
4964
+ resolveArtifactFilename,
4965
+ resolveArtifactIcon,
4966
+ resolveArtifactIconName,
4258
4967
  runChatTurn,
4259
4968
  runRegenerateTurn,
4969
+ saveBlob,
4260
4970
  useChatAutoScroll,
4261
4971
  useChatServices,
4262
4972
  useSidebarDrawer,