@pactor-app/chat 1.15.6 → 1.16.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/README.md +7 -3
- package/dist/index.cjs +490 -22
- package/dist/index.d.cts +147 -37
- package/dist/index.d.ts +147 -37
- package/dist/index.js +481 -22
- package/package.json +6 -6
- package/styles.css +169 -8
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
ARTIFACT_IMAGE_EXTENSIONS: () => ARTIFACT_IMAGE_EXTENSIONS,
|
|
23
24
|
ActivityTrace: () => ActivityTrace,
|
|
24
25
|
AppShell: () => AppShell,
|
|
25
26
|
ArtifactPanel: () => ArtifactPanel,
|
|
@@ -43,7 +44,10 @@ __export(index_exports, {
|
|
|
43
44
|
MessageBubble: () => MessageBubble,
|
|
44
45
|
PartRenderer: () => PartRenderer,
|
|
45
46
|
applyChatEvent: () => applyChatEvent,
|
|
47
|
+
buildArtifactBlob: () => buildArtifactBlob,
|
|
48
|
+
buildArtifactTree: () => buildArtifactTree,
|
|
46
49
|
buildChatScope: () => buildChatScope,
|
|
50
|
+
collectArtifacts: () => collectArtifacts,
|
|
47
51
|
collectMessageText: () => collectMessageText,
|
|
48
52
|
createChatComponentRegistry: () => createChatComponentRegistry,
|
|
49
53
|
createChatId: () => createChatId,
|
|
@@ -52,14 +56,20 @@ __export(index_exports, {
|
|
|
52
56
|
createManualDriver: () => createManualDriver,
|
|
53
57
|
createSafeRegistry: () => createSafeRegistry,
|
|
54
58
|
detectStreamingMode: () => detectStreamingMode,
|
|
59
|
+
downloadArtifact: () => downloadArtifact,
|
|
55
60
|
guardChatDsl: () => guardChatDsl,
|
|
61
|
+
isImageArtifact: () => isImageArtifact,
|
|
62
|
+
mergeArtifacts: () => mergeArtifacts,
|
|
56
63
|
pairToolTraces: () => pairToolTraces,
|
|
57
64
|
registerCapabilityAction: () => registerCapabilityAction,
|
|
58
65
|
registerChatComponent: () => registerChatComponent,
|
|
59
66
|
registerChatLayout: () => registerChatLayout,
|
|
60
67
|
registerChatShellComponent: () => registerChatShellComponent,
|
|
68
|
+
resolveArtifactFileType: () => resolveArtifactFileType,
|
|
69
|
+
resolveArtifactFilename: () => resolveArtifactFilename,
|
|
61
70
|
runChatTurn: () => runChatTurn,
|
|
62
71
|
runRegenerateTurn: () => runRegenerateTurn,
|
|
72
|
+
saveBlob: () => saveBlob,
|
|
63
73
|
useChatAutoScroll: () => useChatAutoScroll,
|
|
64
74
|
useChatServices: () => useChatServices,
|
|
65
75
|
useSidebarDrawer: () => useSidebarDrawer,
|
|
@@ -187,35 +197,113 @@ var ConversationStore = class {
|
|
|
187
197
|
};
|
|
188
198
|
|
|
189
199
|
// src/artifact.ts
|
|
200
|
+
var ARTIFACT_IMAGE_EXTENSIONS = [
|
|
201
|
+
"png",
|
|
202
|
+
"jpg",
|
|
203
|
+
"jpeg",
|
|
204
|
+
"gif",
|
|
205
|
+
"svg",
|
|
206
|
+
"webp",
|
|
207
|
+
"avif",
|
|
208
|
+
"bmp",
|
|
209
|
+
"ico"
|
|
210
|
+
];
|
|
211
|
+
function resolveArtifactFileType(artifact) {
|
|
212
|
+
const declared = artifact.fileType?.trim().toLowerCase().replace(/^\./, "");
|
|
213
|
+
if (declared) {
|
|
214
|
+
return declared;
|
|
215
|
+
}
|
|
216
|
+
const match = /\.([A-Za-z0-9]+)$/.exec(artifact.title.trim());
|
|
217
|
+
return match ? match[1].toLowerCase() : null;
|
|
218
|
+
}
|
|
219
|
+
function isImageArtifact(artifact) {
|
|
220
|
+
const ext = resolveArtifactFileType(artifact);
|
|
221
|
+
return ext !== null && ARTIFACT_IMAGE_EXTENSIONS.includes(ext);
|
|
222
|
+
}
|
|
223
|
+
function collectArtifacts(messages) {
|
|
224
|
+
const order = [];
|
|
225
|
+
const byId = /* @__PURE__ */ new Map();
|
|
226
|
+
for (const message of messages) {
|
|
227
|
+
for (const part of message.parts) {
|
|
228
|
+
if (part.type !== "artifact" || !part.artifact) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (part.status !== void 0 && part.status !== "ready") {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (!byId.has(part.artifact.id)) {
|
|
235
|
+
order.push(part.artifact.id);
|
|
236
|
+
}
|
|
237
|
+
byId.set(part.artifact.id, part.artifact);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return order.map((id) => byId.get(id));
|
|
241
|
+
}
|
|
242
|
+
function mergeArtifacts(stream, opened) {
|
|
243
|
+
const openedById = new Map(opened.map((item) => [item.id, item]));
|
|
244
|
+
const merged = stream.map((item) => openedById.get(item.id) ?? item);
|
|
245
|
+
for (const item of opened) {
|
|
246
|
+
if (!stream.some((s) => s.id === item.id)) {
|
|
247
|
+
merged.push(item);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return merged;
|
|
251
|
+
}
|
|
190
252
|
var ArtifactStore = class {
|
|
191
253
|
constructor() {
|
|
192
|
-
this.
|
|
254
|
+
this.artifacts = [];
|
|
255
|
+
this.activeId = null;
|
|
193
256
|
this.listeners = /* @__PURE__ */ new Set();
|
|
194
257
|
}
|
|
195
|
-
/**
|
|
258
|
+
/** 当前激活产出物(未打开时为 null;引用在不变时保持稳定) */
|
|
196
259
|
get() {
|
|
197
|
-
|
|
260
|
+
if (this.activeId === null) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
return this.artifacts.find((item) => item.id === this.activeId) ?? null;
|
|
198
264
|
}
|
|
199
|
-
/**
|
|
265
|
+
/** 全部产出物(插入序;数组不可变,引用稳定) */
|
|
266
|
+
list() {
|
|
267
|
+
return this.artifacts;
|
|
268
|
+
}
|
|
269
|
+
/** 打开(或按 id 替换)产出物并激活 */
|
|
200
270
|
open(artifact) {
|
|
201
|
-
this.
|
|
271
|
+
const index = this.artifacts.findIndex((item) => item.id === artifact.id);
|
|
272
|
+
this.artifacts = index === -1 ? [...this.artifacts, artifact] : this.artifacts.map((item, i) => i === index ? artifact : item);
|
|
273
|
+
this.activeId = artifact.id;
|
|
202
274
|
this.notify();
|
|
203
275
|
}
|
|
204
|
-
/**
|
|
276
|
+
/** 切换激活项(未知 id 告警并忽略,不抛错) */
|
|
277
|
+
activate(id) {
|
|
278
|
+
if (this.activeId === id) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (!this.artifacts.some((item) => item.id === id)) {
|
|
282
|
+
console.warn("[@pactor-app/chat] unknown artifact id, activate ignored");
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
this.activeId = id;
|
|
286
|
+
this.notify();
|
|
287
|
+
}
|
|
288
|
+
/** 部分更新当前激活产出物(id 不可改);未 open 时告警并忽略 */
|
|
205
289
|
update(patch) {
|
|
206
|
-
|
|
290
|
+
const current = this.get();
|
|
291
|
+
if (!current) {
|
|
207
292
|
console.warn("[@pactor-app/chat] artifact not open, update ignored");
|
|
208
293
|
return;
|
|
209
294
|
}
|
|
210
|
-
|
|
295
|
+
const next = { ...current, ...patch, id: current.id };
|
|
296
|
+
this.artifacts = this.artifacts.map(
|
|
297
|
+
(item) => item.id === current.id ? next : item
|
|
298
|
+
);
|
|
211
299
|
this.notify();
|
|
212
300
|
}
|
|
213
|
-
/**
|
|
301
|
+
/** 关闭面板:清空激活项,列表保留(未打开时幂等,不重复通知) */
|
|
214
302
|
close() {
|
|
215
|
-
if (this.
|
|
303
|
+
if (this.activeId === null) {
|
|
216
304
|
return;
|
|
217
305
|
}
|
|
218
|
-
this.
|
|
306
|
+
this.activeId = null;
|
|
219
307
|
this.notify();
|
|
220
308
|
}
|
|
221
309
|
/** 订阅变更;返回退订函数 */
|
|
@@ -232,6 +320,94 @@ var ArtifactStore = class {
|
|
|
232
320
|
}
|
|
233
321
|
};
|
|
234
322
|
|
|
323
|
+
// src/artifact-download.ts
|
|
324
|
+
var KIND_EXTENSIONS = {
|
|
325
|
+
markdown: "md",
|
|
326
|
+
text: "txt",
|
|
327
|
+
code: "txt",
|
|
328
|
+
dsl: "json"
|
|
329
|
+
};
|
|
330
|
+
function resolveArtifactFilename(artifact) {
|
|
331
|
+
const title = artifact.title.trim() || "artifact";
|
|
332
|
+
if (/\.[A-Za-z0-9]+$/.test(title)) {
|
|
333
|
+
return title;
|
|
334
|
+
}
|
|
335
|
+
const ext = resolveArtifactFileType(artifact) ?? KIND_EXTENSIONS[artifact.kind];
|
|
336
|
+
return `${title}.${ext}`;
|
|
337
|
+
}
|
|
338
|
+
function dataUrlToBlob(dataUrl) {
|
|
339
|
+
const comma = dataUrl.indexOf(",");
|
|
340
|
+
const meta = dataUrl.slice(0, comma);
|
|
341
|
+
const payload = dataUrl.slice(comma + 1);
|
|
342
|
+
const mime = /^data:([^;,]+)/.exec(meta)?.[1] ?? "application/octet-stream";
|
|
343
|
+
if (meta.includes(";base64")) {
|
|
344
|
+
const binary = atob(payload);
|
|
345
|
+
const bytes = new Uint8Array(binary.length);
|
|
346
|
+
for (let i = 0; i < binary.length; i++) {
|
|
347
|
+
bytes[i] = binary.charCodeAt(i);
|
|
348
|
+
}
|
|
349
|
+
return new Blob([bytes], { type: mime });
|
|
350
|
+
}
|
|
351
|
+
return new Blob([decodeURIComponent(payload)], { type: mime });
|
|
352
|
+
}
|
|
353
|
+
function isRemoteUrl(raw) {
|
|
354
|
+
return /^https?:\/\//.test(raw.trim());
|
|
355
|
+
}
|
|
356
|
+
function buildArtifactBlob(artifact) {
|
|
357
|
+
if (artifact.kind === "dsl" && typeof artifact.content === "object") {
|
|
358
|
+
return new Blob([JSON.stringify(artifact.content, null, 2)], {
|
|
359
|
+
type: "application/json"
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
const raw = String(artifact.content);
|
|
363
|
+
if (isImageArtifact(artifact)) {
|
|
364
|
+
const trimmed = raw.trim();
|
|
365
|
+
if (trimmed.startsWith("data:")) {
|
|
366
|
+
return dataUrlToBlob(trimmed);
|
|
367
|
+
}
|
|
368
|
+
if (trimmed.startsWith("<svg") || trimmed.startsWith("<?xml")) {
|
|
369
|
+
return new Blob([raw], { type: "image/svg+xml" });
|
|
370
|
+
}
|
|
371
|
+
if (isRemoteUrl(trimmed)) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const ext = resolveArtifactFileType(artifact);
|
|
376
|
+
const mime = ext === "md" || artifact.kind === "markdown" ? "text/markdown" : "text/plain";
|
|
377
|
+
return new Blob([raw], { type: `${mime};charset=utf-8` });
|
|
378
|
+
}
|
|
379
|
+
function saveBlob(blob, filename) {
|
|
380
|
+
const url = URL.createObjectURL(blob);
|
|
381
|
+
const anchor = document.createElement("a");
|
|
382
|
+
anchor.href = url;
|
|
383
|
+
anchor.download = filename;
|
|
384
|
+
document.body.appendChild(anchor);
|
|
385
|
+
anchor.click();
|
|
386
|
+
anchor.remove();
|
|
387
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
388
|
+
}
|
|
389
|
+
async function downloadArtifact(artifact) {
|
|
390
|
+
const filename = resolveArtifactFilename(artifact);
|
|
391
|
+
const raw = String(artifact.content ?? "").trim();
|
|
392
|
+
if (isImageArtifact(artifact) && isRemoteUrl(raw)) {
|
|
393
|
+
try {
|
|
394
|
+
const response = await fetch(raw);
|
|
395
|
+
if (!response.ok) {
|
|
396
|
+
throw new Error(`HTTP ${response.status}`);
|
|
397
|
+
}
|
|
398
|
+
saveBlob(await response.blob(), filename);
|
|
399
|
+
return;
|
|
400
|
+
} catch {
|
|
401
|
+
window.open(raw, "_blank", "noopener");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const blob = buildArtifactBlob(artifact);
|
|
406
|
+
if (blob) {
|
|
407
|
+
saveBlob(blob, filename);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
235
411
|
// src/events.ts
|
|
236
412
|
function applyChatEvent(store, event) {
|
|
237
413
|
switch (event.type) {
|
|
@@ -1104,6 +1280,12 @@ function ensureChatChromeActions(actions) {
|
|
|
1104
1280
|
function invokeChatPanelClose() {
|
|
1105
1281
|
handlers.panelClose?.();
|
|
1106
1282
|
}
|
|
1283
|
+
function invokeChatPanelToggleList() {
|
|
1284
|
+
handlers.panelToggleList?.();
|
|
1285
|
+
}
|
|
1286
|
+
function invokeChatPanelDownload() {
|
|
1287
|
+
handlers.panelDownload?.();
|
|
1288
|
+
}
|
|
1107
1289
|
|
|
1108
1290
|
// src/view/chrome/ChromeSlot.tsx
|
|
1109
1291
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -1314,10 +1496,47 @@ function ChatPanelCloseButton() {
|
|
|
1314
1496
|
}
|
|
1315
1497
|
);
|
|
1316
1498
|
}
|
|
1317
|
-
var PANEL_HEADER_INTERNAL_COMPONENTS = {
|
|
1499
|
+
var PANEL_HEADER_INTERNAL_COMPONENTS = {
|
|
1500
|
+
ChatPanelCloseButton,
|
|
1501
|
+
ChatPanelDownloadButton,
|
|
1502
|
+
ChatPanelListButton
|
|
1503
|
+
};
|
|
1504
|
+
function ChatPanelListButton() {
|
|
1505
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1506
|
+
"button",
|
|
1507
|
+
{
|
|
1508
|
+
type: "button",
|
|
1509
|
+
className: "pactor-chat-icon-button pactor-chat-inspector__list-toggle",
|
|
1510
|
+
title: "\u4EA7\u7269\u76EE\u5F55",
|
|
1511
|
+
"aria-label": "\u4EA7\u7269\u76EE\u5F55",
|
|
1512
|
+
onClick: () => invokeChatPanelToggleList(),
|
|
1513
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_components3.Icon, { name: "arrow-left", size: 16 })
|
|
1514
|
+
}
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
function ChatPanelDownloadButton() {
|
|
1518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1519
|
+
"button",
|
|
1520
|
+
{
|
|
1521
|
+
type: "button",
|
|
1522
|
+
className: "pactor-chat-icon-button pactor-chat-inspector__download",
|
|
1523
|
+
title: "\u4E0B\u8F7D\u4EA7\u7269",
|
|
1524
|
+
"aria-label": "\u4E0B\u8F7D\u4EA7\u7269",
|
|
1525
|
+
onClick: () => invokeChatPanelDownload(),
|
|
1526
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_components3.Icon, { name: "download", size: 16 })
|
|
1527
|
+
}
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1318
1530
|
function buildPanelHeaderFragment(props) {
|
|
1319
1531
|
return {
|
|
1320
|
-
requires: [
|
|
1532
|
+
requires: [
|
|
1533
|
+
"Flex",
|
|
1534
|
+
"Text",
|
|
1535
|
+
"Badge",
|
|
1536
|
+
"ChatPanelCloseButton",
|
|
1537
|
+
"ChatPanelDownloadButton",
|
|
1538
|
+
"ChatPanelListButton"
|
|
1539
|
+
],
|
|
1321
1540
|
node: {
|
|
1322
1541
|
type: "Flex",
|
|
1323
1542
|
props: {
|
|
@@ -1331,6 +1550,7 @@ function buildPanelHeaderFragment(props) {
|
|
|
1331
1550
|
type: "Flex",
|
|
1332
1551
|
props: { align: "center", gap: "small" },
|
|
1333
1552
|
children: [
|
|
1553
|
+
...props.showListButton ? [{ type: "ChatPanelListButton" }] : [],
|
|
1334
1554
|
{
|
|
1335
1555
|
type: "Text",
|
|
1336
1556
|
props: {
|
|
@@ -1349,7 +1569,14 @@ function buildPanelHeaderFragment(props) {
|
|
|
1349
1569
|
}
|
|
1350
1570
|
]
|
|
1351
1571
|
},
|
|
1352
|
-
{
|
|
1572
|
+
{
|
|
1573
|
+
type: "Flex",
|
|
1574
|
+
props: { align: "center", gap: "small" },
|
|
1575
|
+
children: [
|
|
1576
|
+
...props.showDownload ? [{ type: "ChatPanelDownloadButton" }] : [],
|
|
1577
|
+
{ type: "ChatPanelCloseButton" }
|
|
1578
|
+
]
|
|
1579
|
+
}
|
|
1353
1580
|
]
|
|
1354
1581
|
}
|
|
1355
1582
|
};
|
|
@@ -1400,10 +1627,126 @@ function ArtifactDslContent({
|
|
|
1400
1627
|
const guarded = guardChatDsl(node, runtime.context.components);
|
|
1401
1628
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_runtime4.DslRenderer, { context: runtime.context, engine: runtime.engine, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactDslGuard, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactDslNode, { node: guarded }) }) });
|
|
1402
1629
|
}
|
|
1630
|
+
function ArtifactKindIcon({ kind }) {
|
|
1631
|
+
const name = kind === "code" ? "code" : kind === "dsl" ? "layout-grid" : "file-text";
|
|
1632
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name, size: 13 });
|
|
1633
|
+
}
|
|
1634
|
+
function buildArtifactTree(artifacts) {
|
|
1635
|
+
const root = { name: "", path: "", children: [] };
|
|
1636
|
+
for (const artifact of artifacts) {
|
|
1637
|
+
const segments = artifact.id.split("/").filter(Boolean);
|
|
1638
|
+
let node = root;
|
|
1639
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
1640
|
+
const path = segments.slice(0, i + 1).join("/");
|
|
1641
|
+
let child = node.children.find(
|
|
1642
|
+
(item) => item.path === path && item.artifact === void 0
|
|
1643
|
+
);
|
|
1644
|
+
if (!child) {
|
|
1645
|
+
child = { name: segments[i], path, children: [] };
|
|
1646
|
+
node.children.push(child);
|
|
1647
|
+
}
|
|
1648
|
+
node = child;
|
|
1649
|
+
}
|
|
1650
|
+
node.children.push({
|
|
1651
|
+
name: artifact.title,
|
|
1652
|
+
path: artifact.id,
|
|
1653
|
+
artifact,
|
|
1654
|
+
children: []
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
const sortLevel = (nodes) => {
|
|
1658
|
+
nodes.sort((a, b) => {
|
|
1659
|
+
const dirDelta = Number(b.artifact === void 0) - Number(a.artifact === void 0);
|
|
1660
|
+
return dirDelta !== 0 ? dirDelta : a.name.localeCompare(b.name);
|
|
1661
|
+
});
|
|
1662
|
+
for (const item of nodes) {
|
|
1663
|
+
sortLevel(item.children);
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
sortLevel(root.children);
|
|
1667
|
+
return root.children;
|
|
1668
|
+
}
|
|
1669
|
+
function ArtifactListView({
|
|
1670
|
+
nodes,
|
|
1671
|
+
activeId,
|
|
1672
|
+
onSelect
|
|
1673
|
+
}) {
|
|
1674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "pactor-chat-inspector__list", children: nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1675
|
+
ArtifactTreeRow,
|
|
1676
|
+
{
|
|
1677
|
+
node,
|
|
1678
|
+
activeId,
|
|
1679
|
+
onSelect
|
|
1680
|
+
},
|
|
1681
|
+
node.path
|
|
1682
|
+
)) });
|
|
1683
|
+
}
|
|
1684
|
+
function ArtifactTreeRow({
|
|
1685
|
+
node,
|
|
1686
|
+
activeId,
|
|
1687
|
+
onSelect
|
|
1688
|
+
}) {
|
|
1689
|
+
const [open, setOpen] = (0, import_react6.useState)(true);
|
|
1690
|
+
if (node.artifact) {
|
|
1691
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1692
|
+
"button",
|
|
1693
|
+
{
|
|
1694
|
+
type: "button",
|
|
1695
|
+
className: "pactor-chat-inspector__list-item",
|
|
1696
|
+
"data-active": node.artifact.id === activeId,
|
|
1697
|
+
onClick: () => onSelect?.(node.artifact),
|
|
1698
|
+
children: [
|
|
1699
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactKindIcon, { kind: node.artifact.kind }),
|
|
1700
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__list-item-title", children: node.artifact.title }),
|
|
1701
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__kind", children: node.artifact.kind })
|
|
1702
|
+
]
|
|
1703
|
+
}
|
|
1704
|
+
) });
|
|
1705
|
+
}
|
|
1706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { children: [
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1708
|
+
"button",
|
|
1709
|
+
{
|
|
1710
|
+
type: "button",
|
|
1711
|
+
className: "pactor-chat-inspector__list-folder",
|
|
1712
|
+
"aria-expanded": open,
|
|
1713
|
+
"data-open": open,
|
|
1714
|
+
onClick: () => setOpen((value) => !value),
|
|
1715
|
+
children: [
|
|
1716
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-trace__chevron", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "chevron-right", size: 12 }) }),
|
|
1717
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "folder", size: 13 }),
|
|
1718
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: node.name })
|
|
1719
|
+
]
|
|
1720
|
+
}
|
|
1721
|
+
),
|
|
1722
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-inspector__list-children", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1723
|
+
ArtifactListView,
|
|
1724
|
+
{
|
|
1725
|
+
nodes: node.children,
|
|
1726
|
+
activeId,
|
|
1727
|
+
onSelect
|
|
1728
|
+
}
|
|
1729
|
+
) })
|
|
1730
|
+
] });
|
|
1731
|
+
}
|
|
1732
|
+
function ArtifactImageContent({ artifact }) {
|
|
1733
|
+
const raw = String(artifact.content).trim();
|
|
1734
|
+
const src = raw.startsWith("<svg") || raw.startsWith("<?xml") ? `data:image/svg+xml;utf8,${encodeURIComponent(raw)}` : raw;
|
|
1735
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-inspector__image", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src, alt: artifact.title }) });
|
|
1736
|
+
}
|
|
1403
1737
|
function ArtifactContent({
|
|
1404
1738
|
artifact,
|
|
1405
|
-
runtime
|
|
1739
|
+
runtime,
|
|
1740
|
+
viewers
|
|
1406
1741
|
}) {
|
|
1742
|
+
const ext = resolveArtifactFileType(artifact);
|
|
1743
|
+
const custom = (ext ? viewers?.[ext] : void 0) ?? viewers?.[artifact.kind];
|
|
1744
|
+
if (custom) {
|
|
1745
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: custom(artifact) });
|
|
1746
|
+
}
|
|
1747
|
+
if (isImageArtifact(artifact)) {
|
|
1748
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactImageContent, { artifact });
|
|
1749
|
+
}
|
|
1407
1750
|
switch (artifact.kind) {
|
|
1408
1751
|
case "markdown":
|
|
1409
1752
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_markdown2.MarkdownRenderer, { content: String(artifact.content) });
|
|
@@ -1422,17 +1765,41 @@ function ArtifactContent({
|
|
|
1422
1765
|
}
|
|
1423
1766
|
function ArtifactPanel({
|
|
1424
1767
|
artifact,
|
|
1768
|
+
artifacts,
|
|
1769
|
+
activeArtifactId,
|
|
1770
|
+
onSelectArtifact,
|
|
1425
1771
|
inspector,
|
|
1426
1772
|
onClose,
|
|
1427
1773
|
safeRegistry,
|
|
1428
1774
|
user,
|
|
1775
|
+
artifactViewers,
|
|
1429
1776
|
className
|
|
1430
1777
|
}) {
|
|
1431
1778
|
const runtime = useArtifactRuntime(safeRegistry, user);
|
|
1779
|
+
const [view, setView] = (0, import_react6.useState)("content");
|
|
1780
|
+
const activeId = activeArtifactId ?? artifact?.id;
|
|
1781
|
+
(0, import_react6.useEffect)(() => {
|
|
1782
|
+
setView("content");
|
|
1783
|
+
}, [activeId]);
|
|
1784
|
+
const showListButton = artifacts !== void 0 && artifacts.length > 0;
|
|
1785
|
+
const toggleListRef = (0, import_react6.useRef)(() => {
|
|
1786
|
+
});
|
|
1787
|
+
toggleListRef.current = () => setView((value) => value === "list" ? "content" : "list");
|
|
1432
1788
|
const closeRef = (0, import_react6.useRef)(onClose);
|
|
1433
1789
|
closeRef.current = onClose;
|
|
1790
|
+
const artifactRef = (0, import_react6.useRef)(artifact);
|
|
1791
|
+
artifactRef.current = artifact;
|
|
1434
1792
|
(0, import_react6.useEffect)(() => {
|
|
1435
|
-
setChatChromeHandlers({
|
|
1793
|
+
setChatChromeHandlers({
|
|
1794
|
+
panelClose: () => closeRef.current?.(),
|
|
1795
|
+
panelToggleList: () => toggleListRef.current(),
|
|
1796
|
+
panelDownload: () => {
|
|
1797
|
+
const current = artifactRef.current;
|
|
1798
|
+
if (current) {
|
|
1799
|
+
void downloadArtifact(current);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
});
|
|
1436
1803
|
}, []);
|
|
1437
1804
|
const panelRef = (0, import_react6.useRef)(null);
|
|
1438
1805
|
const [width, setWidth] = (0, import_react6.useState)(null);
|
|
@@ -1451,7 +1818,7 @@ function ArtifactPanel({
|
|
|
1451
1818
|
document.body.style.userSelect = "none";
|
|
1452
1819
|
const onMove = (moveEvent) => {
|
|
1453
1820
|
const next = startWidth + (startX - moveEvent.clientX);
|
|
1454
|
-
setWidth(Math.round(Math.min(Math.max(next,
|
|
1821
|
+
setWidth(Math.round(Math.min(Math.max(next, 320), maxWidth)));
|
|
1455
1822
|
};
|
|
1456
1823
|
const onUp = () => {
|
|
1457
1824
|
document.body.style.userSelect = prevUserSelect;
|
|
@@ -1484,12 +1851,36 @@ function ArtifactPanel({
|
|
|
1484
1851
|
{
|
|
1485
1852
|
fragment: buildPanelHeaderFragment({
|
|
1486
1853
|
title: artifact?.title ?? "\u4EA7\u51FA\u7269",
|
|
1487
|
-
kind: artifact?.kind ?? "text"
|
|
1854
|
+
kind: artifact?.kind ?? "text",
|
|
1855
|
+
showListButton,
|
|
1856
|
+
showDownload: artifact != null
|
|
1488
1857
|
}),
|
|
1489
1858
|
components: PANEL_HEADER_INTERNAL_COMPONENTS,
|
|
1490
1859
|
fallback: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
1860
|
+
showListButton ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1861
|
+
"button",
|
|
1862
|
+
{
|
|
1863
|
+
type: "button",
|
|
1864
|
+
className: "pactor-chat-icon-button pactor-chat-inspector__list-toggle",
|
|
1865
|
+
title: "\u4EA7\u7269\u76EE\u5F55",
|
|
1866
|
+
"aria-label": "\u4EA7\u7269\u76EE\u5F55",
|
|
1867
|
+
onClick: () => toggleListRef.current(),
|
|
1868
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "arrow-left", size: 16, strokeWidth: 1.7 })
|
|
1869
|
+
}
|
|
1870
|
+
) : null,
|
|
1491
1871
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__title", children: artifact?.title ?? "\u4EA7\u51FA\u7269" }),
|
|
1492
1872
|
artifact ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__kind", children: artifact.kind }) : null,
|
|
1873
|
+
artifact ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1874
|
+
"button",
|
|
1875
|
+
{
|
|
1876
|
+
type: "button",
|
|
1877
|
+
className: "pactor-chat-icon-button pactor-chat-inspector__download",
|
|
1878
|
+
title: "\u4E0B\u8F7D\u4EA7\u7269",
|
|
1879
|
+
"aria-label": "\u4E0B\u8F7D\u4EA7\u7269",
|
|
1880
|
+
onClick: () => void downloadArtifact(artifact),
|
|
1881
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_components4.Icon, { name: "download", size: 16, strokeWidth: 1.7 })
|
|
1882
|
+
}
|
|
1883
|
+
) : null,
|
|
1493
1884
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1494
1885
|
"button",
|
|
1495
1886
|
{
|
|
@@ -1503,7 +1894,38 @@ function ArtifactPanel({
|
|
|
1503
1894
|
] })
|
|
1504
1895
|
}
|
|
1505
1896
|
) }),
|
|
1506
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-
|
|
1897
|
+
artifacts !== void 0 && artifacts.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-inspector__tabs", role: "tablist", children: artifacts.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1898
|
+
"button",
|
|
1899
|
+
{
|
|
1900
|
+
type: "button",
|
|
1901
|
+
role: "tab",
|
|
1902
|
+
"aria-selected": item.id === activeId,
|
|
1903
|
+
"data-active": item.id === activeId,
|
|
1904
|
+
className: "pactor-chat-inspector__tab",
|
|
1905
|
+
title: item.title,
|
|
1906
|
+
onClick: () => onSelectArtifact?.(item),
|
|
1907
|
+
children: [
|
|
1908
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ArtifactKindIcon, { kind: item.kind }),
|
|
1909
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pactor-chat-inspector__tab-title", children: item.title })
|
|
1910
|
+
]
|
|
1911
|
+
},
|
|
1912
|
+
item.id
|
|
1913
|
+
)) }) : null,
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pactor-chat-inspector__body", children: view === "list" && artifacts !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1915
|
+
ArtifactListView,
|
|
1916
|
+
{
|
|
1917
|
+
nodes: buildArtifactTree(artifacts),
|
|
1918
|
+
activeId,
|
|
1919
|
+
onSelect: onSelectArtifact
|
|
1920
|
+
}
|
|
1921
|
+
) : inspector !== void 0 && inspector !== null ? inspector : artifact ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1922
|
+
ArtifactContent,
|
|
1923
|
+
{
|
|
1924
|
+
artifact,
|
|
1925
|
+
runtime,
|
|
1926
|
+
viewers: artifactViewers
|
|
1927
|
+
}
|
|
1928
|
+
) : null })
|
|
1507
1929
|
]
|
|
1508
1930
|
}
|
|
1509
1931
|
);
|
|
@@ -2529,7 +2951,11 @@ var import_react12 = require("react");
|
|
|
2529
2951
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2530
2952
|
function usePinnedCollapse(autoOpen) {
|
|
2531
2953
|
const [state, setState] = (0, import_react12.useState)("auto");
|
|
2532
|
-
const
|
|
2954
|
+
const autoCollapsedRef = (0, import_react12.useRef)(false);
|
|
2955
|
+
if (state === "auto" && !autoOpen) {
|
|
2956
|
+
autoCollapsedRef.current = true;
|
|
2957
|
+
}
|
|
2958
|
+
const open = state === "auto" ? autoOpen && !autoCollapsedRef.current : state === "pinned-open";
|
|
2533
2959
|
const toggle = () => {
|
|
2534
2960
|
setState(open ? "pinned-closed" : "pinned-open");
|
|
2535
2961
|
};
|
|
@@ -3674,6 +4100,8 @@ function ChatComposition({
|
|
|
3674
4100
|
onSend,
|
|
3675
4101
|
artifact,
|
|
3676
4102
|
panelOpen,
|
|
4103
|
+
artifacts: artifactsProp,
|
|
4104
|
+
onSelectArtifact,
|
|
3677
4105
|
onPanelClose,
|
|
3678
4106
|
inspector,
|
|
3679
4107
|
onArtifact,
|
|
@@ -3694,8 +4122,28 @@ function ChatComposition({
|
|
|
3694
4122
|
(listener) => artifactStore.subscribe(listener),
|
|
3695
4123
|
() => artifactStore.get()
|
|
3696
4124
|
);
|
|
4125
|
+
const storeArtifacts = (0, import_react17.useSyncExternalStore)(
|
|
4126
|
+
(listener) => artifactStore.subscribe(listener),
|
|
4127
|
+
() => artifactStore.list()
|
|
4128
|
+
);
|
|
4129
|
+
const messages = (0, import_react17.useSyncExternalStore)(
|
|
4130
|
+
(listener) => store.subscribe(listener),
|
|
4131
|
+
() => store.getMessages()
|
|
4132
|
+
);
|
|
4133
|
+
const streamArtifacts = (0, import_react17.useMemo)(() => collectArtifacts(messages), [messages]);
|
|
4134
|
+
const mergedArtifacts = (0, import_react17.useMemo)(
|
|
4135
|
+
() => mergeArtifacts(streamArtifacts, storeArtifacts),
|
|
4136
|
+
[streamArtifacts, storeArtifacts]
|
|
4137
|
+
);
|
|
3697
4138
|
const effectiveArtifact = panelControlled ? artifact ?? null : storeArtifact;
|
|
3698
4139
|
const effectivePanelOpen = panelControlled ? panelOpen ?? artifact != null : storeArtifact !== null;
|
|
4140
|
+
const effectiveArtifacts = artifactsProp ?? mergedArtifacts;
|
|
4141
|
+
const handleSelectArtifact = (next) => {
|
|
4142
|
+
onSelectArtifact?.(next);
|
|
4143
|
+
if (!panelControlled) {
|
|
4144
|
+
artifactStore.open(next);
|
|
4145
|
+
}
|
|
4146
|
+
};
|
|
3699
4147
|
const handlePanelClose = () => {
|
|
3700
4148
|
onPanelClose?.();
|
|
3701
4149
|
if (!panelControlled) {
|
|
@@ -3837,10 +4285,14 @@ function ChatComposition({
|
|
|
3837
4285
|
ArtifactPanel,
|
|
3838
4286
|
{
|
|
3839
4287
|
artifact: effectiveArtifact,
|
|
4288
|
+
artifacts: effectiveArtifacts,
|
|
4289
|
+
activeArtifactId: effectiveArtifact?.id,
|
|
4290
|
+
onSelectArtifact: handleSelectArtifact,
|
|
3840
4291
|
inspector,
|
|
3841
4292
|
onClose: handlePanelClose,
|
|
3842
4293
|
safeRegistry: services.safeRegistry,
|
|
3843
|
-
user: services.user
|
|
4294
|
+
user: services.user,
|
|
4295
|
+
artifactViewers: services.artifactViewers
|
|
3844
4296
|
}
|
|
3845
4297
|
)
|
|
3846
4298
|
] }) : null
|
|
@@ -3883,6 +4335,7 @@ function createChatWorkspace(services) {
|
|
|
3883
4335
|
inspector,
|
|
3884
4336
|
onArtifact,
|
|
3885
4337
|
onOpenArtifact,
|
|
4338
|
+
onSelectArtifact,
|
|
3886
4339
|
headerActions,
|
|
3887
4340
|
editableTitle,
|
|
3888
4341
|
onExport,
|
|
@@ -3923,6 +4376,7 @@ function createChatWorkspace(services) {
|
|
|
3923
4376
|
inspector: inspector ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DslNodeSlot, { node: inspector }) : void 0,
|
|
3924
4377
|
onArtifact,
|
|
3925
4378
|
onOpenArtifact,
|
|
4379
|
+
onSelectArtifact,
|
|
3926
4380
|
headerActions,
|
|
3927
4381
|
editableTitle,
|
|
3928
4382
|
onExport,
|
|
@@ -4132,6 +4586,7 @@ function Chat({
|
|
|
4132
4586
|
inspector,
|
|
4133
4587
|
onArtifact,
|
|
4134
4588
|
onOpenArtifact,
|
|
4589
|
+
onSelectArtifact,
|
|
4135
4590
|
className,
|
|
4136
4591
|
onSend,
|
|
4137
4592
|
onMenuSelect
|
|
@@ -4196,7 +4651,8 @@ function Chat({
|
|
|
4196
4651
|
onPanelClose,
|
|
4197
4652
|
inspector: resolveSlot(inspector),
|
|
4198
4653
|
onArtifact,
|
|
4199
|
-
onOpenArtifact
|
|
4654
|
+
onOpenArtifact,
|
|
4655
|
+
onSelectArtifact
|
|
4200
4656
|
}
|
|
4201
4657
|
);
|
|
4202
4658
|
}
|
|
@@ -4207,6 +4663,7 @@ function ChatDslAdapter({
|
|
|
4207
4663
|
onPanelClose,
|
|
4208
4664
|
onArtifact,
|
|
4209
4665
|
onOpenArtifact,
|
|
4666
|
+
onSelectArtifact,
|
|
4210
4667
|
onModelChange,
|
|
4211
4668
|
onContextsChange,
|
|
4212
4669
|
onMessageAction,
|
|
@@ -4225,6 +4682,7 @@ function ChatDslAdapter({
|
|
|
4225
4682
|
onPanelClose: onPanelClose ? () => onPanelClose({}) : void 0,
|
|
4226
4683
|
onArtifact: onArtifact ? (artifact) => onArtifact({ artifact }) : void 0,
|
|
4227
4684
|
onOpenArtifact: onOpenArtifact ? (artifact) => onOpenArtifact({ artifact }) : void 0,
|
|
4685
|
+
onSelectArtifact: onSelectArtifact ? (artifact) => onSelectArtifact({ artifact }) : void 0,
|
|
4228
4686
|
onModelChange: onModelChange ? (model) => onModelChange({ model }) : void 0,
|
|
4229
4687
|
onContextsChange: onContextsChange ? (contexts) => onContextsChange({ contexts }) : void 0,
|
|
4230
4688
|
onMessageAction: onMessageAction ? (action, message) => onMessageAction({ action, message }) : void 0,
|
|
@@ -4263,6 +4721,7 @@ function registerChatLayout(layoutRegistry) {
|
|
|
4263
4721
|
}
|
|
4264
4722
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4265
4723
|
0 && (module.exports = {
|
|
4724
|
+
ARTIFACT_IMAGE_EXTENSIONS,
|
|
4266
4725
|
ActivityTrace,
|
|
4267
4726
|
AppShell,
|
|
4268
4727
|
ArtifactPanel,
|
|
@@ -4286,7 +4745,10 @@ function registerChatLayout(layoutRegistry) {
|
|
|
4286
4745
|
MessageBubble,
|
|
4287
4746
|
PartRenderer,
|
|
4288
4747
|
applyChatEvent,
|
|
4748
|
+
buildArtifactBlob,
|
|
4749
|
+
buildArtifactTree,
|
|
4289
4750
|
buildChatScope,
|
|
4751
|
+
collectArtifacts,
|
|
4290
4752
|
collectMessageText,
|
|
4291
4753
|
createChatComponentRegistry,
|
|
4292
4754
|
createChatId,
|
|
@@ -4295,14 +4757,20 @@ function registerChatLayout(layoutRegistry) {
|
|
|
4295
4757
|
createManualDriver,
|
|
4296
4758
|
createSafeRegistry,
|
|
4297
4759
|
detectStreamingMode,
|
|
4760
|
+
downloadArtifact,
|
|
4298
4761
|
guardChatDsl,
|
|
4762
|
+
isImageArtifact,
|
|
4763
|
+
mergeArtifacts,
|
|
4299
4764
|
pairToolTraces,
|
|
4300
4765
|
registerCapabilityAction,
|
|
4301
4766
|
registerChatComponent,
|
|
4302
4767
|
registerChatLayout,
|
|
4303
4768
|
registerChatShellComponent,
|
|
4769
|
+
resolveArtifactFileType,
|
|
4770
|
+
resolveArtifactFilename,
|
|
4304
4771
|
runChatTurn,
|
|
4305
4772
|
runRegenerateTurn,
|
|
4773
|
+
saveBlob,
|
|
4306
4774
|
useChatAutoScroll,
|
|
4307
4775
|
useChatServices,
|
|
4308
4776
|
useSidebarDrawer,
|