@malette/agent-sdk 0.1.1-alpha.0 → 0.1.2

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
@@ -13,6 +13,7 @@ var DialogPrimitive = require('@radix-ui/react-dialog');
13
13
  var reactSlot = require('@radix-ui/react-slot');
14
14
  var ScrollAreaPrimitive = require('@radix-ui/react-scroll-area');
15
15
  require('react-dom');
16
+ var marked = require('marked');
16
17
 
17
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
19
 
@@ -426,6 +427,52 @@ AI\u56DE\u590D\u6458\u8981\uFF1A${assistantMessage.slice(0, 200)}` : ""}`
426
427
  return userMessage.slice(0, 20) + (userMessage.length > 20 ? "..." : "");
427
428
  }
428
429
  }
430
+ var artifactService = {
431
+ /**
432
+ * 获取会话的所有产物列表
433
+ */
434
+ list: async (sessionId) => {
435
+ return fetcher(`${API_BASE}/sessions/${sessionId}/artifacts`);
436
+ },
437
+ /**
438
+ * 获取产物详情
439
+ */
440
+ get: async (artifactId) => {
441
+ return fetcher(`${API_BASE}/artifacts/${artifactId}`);
442
+ },
443
+ /**
444
+ * 更新产物内容(用户编辑后保存)
445
+ */
446
+ updateContent: async (artifactId, content) => {
447
+ return fetcher(`${API_BASE}/artifacts/${artifactId}/content`, {
448
+ method: "PUT",
449
+ body: JSON.stringify({ content })
450
+ });
451
+ },
452
+ /**
453
+ * 删除产物
454
+ */
455
+ delete: async (artifactId) => {
456
+ return fetcher(`${API_BASE}/artifacts/${artifactId}`, {
457
+ method: "DELETE"
458
+ });
459
+ },
460
+ /**
461
+ * 获取产物版本历史
462
+ */
463
+ getVersions: async (artifactId) => {
464
+ return fetcher(`${API_BASE}/artifacts/${artifactId}/versions`);
465
+ },
466
+ /**
467
+ * 回滚到指定版本
468
+ */
469
+ revert: async (artifactId, version) => {
470
+ return fetcher(`${API_BASE}/artifacts/${artifactId}/revert`, {
471
+ method: "POST",
472
+ body: JSON.stringify({ version })
473
+ });
474
+ }
475
+ };
429
476
  var shareService = {
430
477
  /**
431
478
  * 创建分享链接
@@ -814,6 +861,9 @@ var useAgentStore = zustand.create()(
814
861
  subAgents: [],
815
862
  tools: [],
816
863
  skills: [],
864
+ artifacts: {},
865
+ artifactOrder: [],
866
+ activeArtifactId: null,
817
867
  // ============ Session Actions ============
818
868
  setSessions: (sessions) => set({ sessions }, false, "setSessions"),
819
869
  setTools: (tools) => set({ tools }, false, "setTools"),
@@ -1178,19 +1228,99 @@ var useAgentStore = zustand.create()(
1178
1228
  false,
1179
1229
  "setActiveSubAgent"
1180
1230
  ),
1231
+ // ============ Artifact Actions ============
1232
+ upsertArtifact: (artifact) => set(
1233
+ (state) => {
1234
+ const isNew = !state.artifacts[artifact.id];
1235
+ return {
1236
+ artifacts: {
1237
+ ...state.artifacts,
1238
+ [artifact.id]: artifact
1239
+ },
1240
+ artifactOrder: isNew ? [...state.artifactOrder, artifact.id] : state.artifactOrder
1241
+ };
1242
+ },
1243
+ false,
1244
+ "upsertArtifact"
1245
+ ),
1246
+ updateArtifactContent: (artifactId, content, source) => set(
1247
+ (state) => {
1248
+ const existing = state.artifacts[artifactId];
1249
+ if (!existing) return state;
1250
+ return {
1251
+ artifacts: {
1252
+ ...state.artifacts,
1253
+ [artifactId]: {
1254
+ ...existing,
1255
+ currentContent: content,
1256
+ version: existing.version + 1,
1257
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
1258
+ }
1259
+ }
1260
+ };
1261
+ },
1262
+ false,
1263
+ "updateArtifactContent"
1264
+ ),
1265
+ setActiveArtifact: (artifactId) => set(
1266
+ { activeArtifactId: artifactId },
1267
+ false,
1268
+ "setActiveArtifact"
1269
+ ),
1270
+ removeArtifact: (artifactId) => set(
1271
+ (state) => {
1272
+ const { [artifactId]: _, ...remaining } = state.artifacts;
1273
+ return {
1274
+ artifacts: remaining,
1275
+ artifactOrder: state.artifactOrder.filter((id) => id !== artifactId),
1276
+ activeArtifactId: state.activeArtifactId === artifactId ? null : state.activeArtifactId
1277
+ };
1278
+ },
1279
+ false,
1280
+ "removeArtifact"
1281
+ ),
1282
+ clearArtifacts: () => set(
1283
+ { artifacts: {}, artifactOrder: [], activeArtifactId: null },
1284
+ false,
1285
+ "clearArtifacts"
1286
+ ),
1287
+ setArtifacts: (artifacts) => set(
1288
+ {
1289
+ artifacts: artifacts.reduce((acc, a) => {
1290
+ acc[a.id] = a;
1291
+ return acc;
1292
+ }, {}),
1293
+ artifactOrder: artifacts.map((a) => a.id)
1294
+ },
1295
+ false,
1296
+ "setArtifacts"
1297
+ ),
1298
+ reorderArtifacts: (fromIndex, toIndex) => set(
1299
+ (state) => {
1300
+ const newOrder = [...state.artifactOrder];
1301
+ const [movedId] = newOrder.splice(fromIndex, 1);
1302
+ newOrder.splice(toIndex, 0, movedId);
1303
+ return { artifactOrder: newOrder };
1304
+ },
1305
+ false,
1306
+ "reorderArtifacts"
1307
+ ),
1181
1308
  // ============ 复合操作 ============
1182
1309
  startNewChat: (sessionId) => {
1183
1310
  set(
1184
1311
  (state) => ({
1185
1312
  messages: [],
1186
- chatUI: initialChatUIState
1313
+ chatUI: initialChatUIState,
1314
+ artifacts: {},
1315
+ artifactOrder: [],
1316
+ activeArtifactId: null
1187
1317
  }),
1188
1318
  false,
1189
1319
  "startNewChat"
1190
1320
  );
1191
1321
  },
1192
1322
  resetChatState: () => set(
1193
- { chatUI: initialChatUIState },
1323
+ { chatUI: initialChatUIState, artifacts: {}, artifactOrder: [], activeArtifactId: null },
1194
1324
  false,
1195
1325
  "resetChatState"
1196
1326
  )
@@ -1217,6 +1347,23 @@ var useCurrentSession = () => useAgentStore((state) => state.currentSession);
1217
1347
  var useSessions = () => useAgentStore((state) => state.sessions);
1218
1348
  var useSubAgents = () => useAgentStore((state) => state.subAgents);
1219
1349
  var useChatUI = () => useAgentStore((state) => state.chatUI);
1350
+ var useArtifacts = () => useAgentStore((state) => state.artifacts);
1351
+ var useActiveArtifactId = () => useAgentStore((state) => state.activeArtifactId);
1352
+ var useActiveArtifact = () => {
1353
+ const artifacts = useAgentStore((state) => state.artifacts);
1354
+ const activeId = useAgentStore((state) => state.activeArtifactId);
1355
+ return activeId ? artifacts[activeId] ?? null : null;
1356
+ };
1357
+ var useArtifactList = () => {
1358
+ const artifacts = useAgentStore((state) => state.artifacts);
1359
+ const artifactOrder = useAgentStore((state) => state.artifactOrder);
1360
+ const ordered = artifactOrder.map((id) => artifacts[id]).filter(Boolean);
1361
+ const unordered = Object.values(artifacts).filter(
1362
+ (a) => !artifactOrder.includes(a.id)
1363
+ );
1364
+ return [...ordered, ...unordered];
1365
+ };
1366
+ var useArtifactOrder = () => useAgentStore((state) => state.artifactOrder);
1220
1367
  var useSessionsLoading = () => useAgentStore((state) => state.sessionsLoading);
1221
1368
  var findMessageById = (messageId) => {
1222
1369
  return useAgentStore.getState().messages.find((m) => m.messageId === messageId);
@@ -1405,7 +1552,11 @@ function useSSE(options) {
1405
1552
  setShowPlanConfirmDialog,
1406
1553
  setShowHumanInputDialog,
1407
1554
  setWaitingStepId,
1408
- clearPlanState
1555
+ clearPlanState,
1556
+ // Artifact actions
1557
+ upsertArtifact,
1558
+ updateArtifactContent,
1559
+ setActiveArtifact
1409
1560
  } = useAgentStore();
1410
1561
  const clearHeartbeatTimeout = React20.useCallback(() => {
1411
1562
  if (heartbeatTimeoutRef.current) {
@@ -1603,6 +1754,37 @@ function useSSE(options) {
1603
1754
  });
1604
1755
  return;
1605
1756
  }
1757
+ if (eventType === "artifact_created") {
1758
+ const sessionId = useAgentStore.getState().currentSession?.sessionId;
1759
+ if (data.artifactId && sessionId) {
1760
+ const artifactType = data.type || "code";
1761
+ const artifactEntry = {
1762
+ id: data.artifactId,
1763
+ sessionId,
1764
+ type: artifactType,
1765
+ title: data.title || "Untitled",
1766
+ currentContent: data.content || "",
1767
+ language: data.language,
1768
+ version: 1,
1769
+ source: "tool",
1770
+ sourceId: data.sourceToolCallId,
1771
+ metadata: data.metadata,
1772
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
1773
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
1774
+ };
1775
+ upsertArtifact(artifactEntry);
1776
+ setActiveArtifact(data.artifactId);
1777
+ console.log("[SSE] Artifact created:", data.artifactId, data.title);
1778
+ }
1779
+ return;
1780
+ }
1781
+ if (eventType === "artifact_updated") {
1782
+ if (data.artifactId && data.content) {
1783
+ updateArtifactContent(data.artifactId, data.content, "ai");
1784
+ console.log("[SSE] Artifact updated:", data.artifactId, "version:", data.version);
1785
+ }
1786
+ return;
1787
+ }
1606
1788
  const rawToolCalls = data.toolCalls || data.tool_calls || data.actions || [];
1607
1789
  const normalizedToolCalls = rawToolCalls.length > 0 ? normalizeToolCalls2(rawToolCalls) : void 0;
1608
1790
  let finalToolCalls = normalizedToolCalls;
@@ -2308,7 +2490,7 @@ function useSSE(options) {
2308
2490
  try {
2309
2491
  console.log("[SSE] Step 1: Sending message...");
2310
2492
  const response = await messageService.sendStream(
2311
- { sessionId, content, frontendTools: sendOptions?.frontendTools },
2493
+ { sessionId, content, frontendTools: sendOptions?.frontendTools, artifactContext: sendOptions?.artifactContext },
2312
2494
  abortControllerRef.current.signal
2313
2495
  );
2314
2496
  const reader = response.body?.getReader();
@@ -2448,6 +2630,92 @@ function useSSE(options) {
2448
2630
  isConnected
2449
2631
  };
2450
2632
  }
2633
+ var CHANNEL_ID = "adic-agent-canvas-bridge";
2634
+ function useCanvasBridge(options = {}) {
2635
+ const {
2636
+ canvasIframeRef,
2637
+ targetOrigin = "*",
2638
+ enabled = false,
2639
+ onArtifactSelected,
2640
+ onArtifactContentChanged
2641
+ } = options;
2642
+ const optionsRef = React20.useRef(options);
2643
+ optionsRef.current = options;
2644
+ const postToCanvas = React20.useCallback((type, payload) => {
2645
+ const iframe = canvasIframeRef?.current;
2646
+ if (!iframe?.contentWindow) {
2647
+ console.warn("[CanvasBridge] Canvas iframe not available");
2648
+ return;
2649
+ }
2650
+ const envelope = {
2651
+ channel: CHANNEL_ID,
2652
+ message: {
2653
+ type,
2654
+ payload,
2655
+ timestamp: Date.now()
2656
+ }
2657
+ };
2658
+ iframe.contentWindow.postMessage(envelope, targetOrigin);
2659
+ console.log("[CanvasBridge] Sent:", type, payload.artifactId || payload.artifact?.id);
2660
+ }, [canvasIframeRef, targetOrigin]);
2661
+ React20.useEffect(() => {
2662
+ if (!enabled) return;
2663
+ const handleMessage = (event) => {
2664
+ if (targetOrigin !== "*" && event.origin !== targetOrigin) return;
2665
+ const envelope = event.data;
2666
+ if (!envelope || envelope.channel !== CHANNEL_ID) return;
2667
+ const { type, payload } = envelope.message;
2668
+ console.log("[CanvasBridge] Received:", type, payload);
2669
+ switch (type) {
2670
+ case "artifact_selected": {
2671
+ if (payload.artifactId) {
2672
+ optionsRef.current.onArtifactSelected?.(payload.artifactId);
2673
+ useAgentStore.getState().setActiveArtifact(payload.artifactId);
2674
+ }
2675
+ break;
2676
+ }
2677
+ case "artifact_content_changed": {
2678
+ if (payload.artifactId && payload.content !== void 0) {
2679
+ optionsRef.current.onArtifactContentChanged?.(payload.artifactId, payload.content);
2680
+ useAgentStore.getState().updateArtifactContent(
2681
+ payload.artifactId,
2682
+ payload.content,
2683
+ "user"
2684
+ );
2685
+ }
2686
+ break;
2687
+ }
2688
+ default:
2689
+ console.log("[CanvasBridge] Unknown message type:", type);
2690
+ }
2691
+ };
2692
+ window.addEventListener("message", handleMessage);
2693
+ return () => window.removeEventListener("message", handleMessage);
2694
+ }, [enabled, targetOrigin]);
2695
+ const notifyArtifactCreated = React20.useCallback((artifact) => {
2696
+ if (!enabled) return;
2697
+ postToCanvas("artifact_created", { artifact });
2698
+ }, [enabled, postToCanvas]);
2699
+ const notifyArtifactUpdated = React20.useCallback((artifact) => {
2700
+ if (!enabled) return;
2701
+ postToCanvas("artifact_updated", { artifact });
2702
+ }, [enabled, postToCanvas]);
2703
+ const notifyArtifactDeleted = React20.useCallback((artifactId) => {
2704
+ if (!enabled) return;
2705
+ postToCanvas("artifact_deleted", { artifactId });
2706
+ }, [enabled, postToCanvas]);
2707
+ const syncAllArtifacts = React20.useCallback(() => {
2708
+ if (!enabled) return;
2709
+ const artifacts = Object.values(useAgentStore.getState().artifacts);
2710
+ postToCanvas("artifacts_sync", { artifacts });
2711
+ }, [enabled, postToCanvas]);
2712
+ return {
2713
+ notifyArtifactCreated,
2714
+ notifyArtifactUpdated,
2715
+ notifyArtifactDeleted,
2716
+ syncAllArtifacts
2717
+ };
2718
+ }
2451
2719
 
2452
2720
  // src/hooks/sseConnection.ts
2453
2721
  var SSE_HEARTBEAT_TIMEOUT2 = 45e3;
@@ -2910,14 +3178,14 @@ function getParamsSummary(params, maxItems = 2) {
2910
3178
  }
2911
3179
  function generateToolCallDescription(toolName, params) {
2912
3180
  const formatted = formatParameters(params);
2913
- const prompt = formatted.find((p) => p.key === "prompt" || p.key === "query" || p.key === "content");
3181
+ const prompt2 = formatted.find((p) => p.key === "prompt" || p.key === "query" || p.key === "content");
2914
3182
  const image2 = formatted.find((p) => p.key === "image" || p.key === "image_url" || p.key === "imageUrl");
2915
3183
  const style = formatted.find((p) => p.key === "style");
2916
3184
  formatted.find((p) => p.key === "width" || p.key === "size");
2917
3185
  const toolLower = toolName.toLowerCase();
2918
3186
  if (toolLower.includes("generate") || toolLower.includes("image") || toolLower.includes("art")) {
2919
- if (prompt) {
2920
- return `\u751F\u6210\u56FE\u50CF\uFF1A${prompt.displayValue.slice(0, 50)}${prompt.displayValue.length > 50 ? "..." : ""}`;
3187
+ if (prompt2) {
3188
+ return `\u751F\u6210\u56FE\u50CF\uFF1A${prompt2.displayValue.slice(0, 50)}${prompt2.displayValue.length > 50 ? "..." : ""}`;
2921
3189
  }
2922
3190
  if (style) {
2923
3191
  return `\u751F\u6210 ${style.displayValue} \u98CE\u683C\u7684\u56FE\u50CF`;
@@ -2925,14 +3193,14 @@ function generateToolCallDescription(toolName, params) {
2925
3193
  return "\u751F\u6210\u56FE\u50CF";
2926
3194
  }
2927
3195
  if (toolLower.includes("search") || toolLower.includes("query")) {
2928
- if (prompt) {
2929
- return `\u641C\u7D22\uFF1A${prompt.displayValue}`;
3196
+ if (prompt2) {
3197
+ return `\u641C\u7D22\uFF1A${prompt2.displayValue}`;
2930
3198
  }
2931
3199
  return "\u6267\u884C\u641C\u7D22";
2932
3200
  }
2933
3201
  if (toolLower.includes("translate")) {
2934
- if (prompt) {
2935
- return `\u7FFB\u8BD1\uFF1A${prompt.displayValue.slice(0, 30)}...`;
3202
+ if (prompt2) {
3203
+ return `\u7FFB\u8BD1\uFF1A${prompt2.displayValue.slice(0, 30)}...`;
2936
3204
  }
2937
3205
  return "\u6267\u884C\u7FFB\u8BD1";
2938
3206
  }
@@ -2942,8 +3210,8 @@ function generateToolCallDescription(toolName, params) {
2942
3210
  }
2943
3211
  return "\u6267\u884C\u5206\u6790";
2944
3212
  }
2945
- if (prompt) {
2946
- return prompt.displayValue.slice(0, 60) + (prompt.displayValue.length > 60 ? "..." : "");
3213
+ if (prompt2) {
3214
+ return prompt2.displayValue.slice(0, 60) + (prompt2.displayValue.length > 60 ? "..." : "");
2947
3215
  }
2948
3216
  const paramCount = formatted.length;
2949
3217
  if (paramCount === 0) {
@@ -6938,7 +7206,6 @@ var createAssetFromSource = (options) => {
6938
7206
  var resolveAssetForDisplay = async (asset, config) => {
6939
7207
  let working = asset;
6940
7208
  const strategy = config?.asset;
6941
- console.log("Resolving asset for display:", asset, "with strategy:", strategy);
6942
7209
  if (strategy?.resolve && !working.url) {
6943
7210
  working = await strategy.resolve(working);
6944
7211
  }
@@ -6953,6 +7220,9 @@ var resolveAssetForDisplay = async (asset, config) => {
6953
7220
  if (resolved) return resolved;
6954
7221
  }
6955
7222
  if (working.url) {
7223
+ if (working.url.includes("industryai")) {
7224
+ working.url = working.url.split("?")[0];
7225
+ }
6956
7226
  return {
6957
7227
  url: working.url,
6958
7228
  hdUrl: working.url,
@@ -6962,7 +7232,7 @@ var resolveAssetForDisplay = async (asset, config) => {
6962
7232
  return null;
6963
7233
  };
6964
7234
 
6965
- // ../../node_modules/clsx/dist/clsx.mjs
7235
+ // node_modules/clsx/dist/clsx.mjs
6966
7236
  function r(e) {
6967
7237
  var t, f, n = "";
6968
7238
  if ("string" == typeof e || "number" == typeof e) n += e;
@@ -10571,7 +10841,7 @@ var MessageImageInternal = React20.memo(function MessageImageInternal2({ src, al
10571
10841
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u56FE\u7247\u52A0\u8F7D\u5931\u8D25" })
10572
10842
  ] });
10573
10843
  }
10574
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative cursor-pointer overflow-hidden rounded-lg my-2", children: [
10844
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative inline-block max-w-full overflow-hidden rounded-lg my-2", children: [
10575
10845
  previewUrl && /* @__PURE__ */ jsxRuntime.jsx(
10576
10846
  ImagePreviewComp,
10577
10847
  {
@@ -10579,7 +10849,7 @@ var MessageImageInternal = React20.memo(function MessageImageInternal2({ src, al
10579
10849
  onClose: () => setPreviewUrl(null)
10580
10850
  }
10581
10851
  ),
10582
- !loaded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-zinc-800 animate-pulse rounded-lg", style: { minHeight: "100px" } }),
10852
+ !loaded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-zinc-800 animate-pulse rounded-lg", style: { minHeight: "100px", minWidth: "150px" } }),
10583
10853
  /* @__PURE__ */ jsxRuntime.jsx(
10584
10854
  "img",
10585
10855
  {
@@ -10589,23 +10859,284 @@ var MessageImageInternal = React20.memo(function MessageImageInternal2({ src, al
10589
10859
  onLoad: handleLoad,
10590
10860
  onError: handleError,
10591
10861
  onClick: () => setPreviewUrl(src),
10592
- className: cn("rounded-lg cursor-zoom-in", className, !loaded && "opacity-0"),
10593
- style: { maxHeight: "400px" }
10862
+ className: cn(
10863
+ "rounded-lg cursor-zoom-in max-w-full h-auto block",
10864
+ className,
10865
+ !loaded && "opacity-0 absolute"
10866
+ ),
10867
+ style: { maxHeight: "400px", objectFit: "contain" }
10594
10868
  }
10595
10869
  )
10596
10870
  ] });
10597
10871
  });
10598
10872
  var MessageVideoInternal = React20.memo(function MessageVideoInternal2({ src, className }) {
10599
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-2 relative rounded-lg overflow-hidden bg-zinc-900 border border-zinc-800", children: /* @__PURE__ */ jsxRuntime.jsx(
10600
- "video",
10601
- {
10602
- src,
10603
- controls: true,
10604
- className: cn("max-w-full rounded-lg max-h-[400px] w-full", className),
10605
- preload: "metadata",
10606
- children: "\u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u89C6\u9891\u64AD\u653E"
10873
+ const videoRef = React20__namespace.default.useRef(null);
10874
+ const canvasRef = React20__namespace.default.useRef(null);
10875
+ const [posterUrl, setPosterUrl] = React20.useState(null);
10876
+ const [isHovering, setIsHovering] = React20.useState(false);
10877
+ const [isPlaying, setIsPlaying] = React20.useState(false);
10878
+ const [isMuted, setIsMuted] = React20.useState(true);
10879
+ const [duration, setDuration] = React20.useState(0);
10880
+ const [currentTime, setCurrentTime] = React20.useState(0);
10881
+ const [isFullPlaying, setIsFullPlaying] = React20.useState(false);
10882
+ const hoverTimerRef = React20__namespace.default.useRef(null);
10883
+ const VideoPreviewComp = useComponent("VideoPreview") || VideoPreviewInternal;
10884
+ const [showPreview, setShowPreview] = React20.useState(false);
10885
+ React20.useEffect(() => {
10886
+ const video = document.createElement("video");
10887
+ video.crossOrigin = "anonymous";
10888
+ video.preload = "metadata";
10889
+ video.muted = true;
10890
+ video.addEventListener("loadeddata", () => {
10891
+ video.currentTime = Math.min(0.5, video.duration * 0.1);
10892
+ });
10893
+ video.addEventListener("seeked", () => {
10894
+ try {
10895
+ const canvas = document.createElement("canvas");
10896
+ canvas.width = video.videoWidth;
10897
+ canvas.height = video.videoHeight;
10898
+ const ctx = canvas.getContext("2d");
10899
+ if (ctx) {
10900
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
10901
+ const dataUrl = canvas.toDataURL("image/jpeg", 0.8);
10902
+ setPosterUrl(dataUrl);
10903
+ }
10904
+ } catch {
10905
+ }
10906
+ video.remove();
10907
+ });
10908
+ video.addEventListener("error", () => {
10909
+ video.remove();
10910
+ });
10911
+ video.src = src;
10912
+ video.load();
10913
+ return () => {
10914
+ video.remove();
10915
+ };
10916
+ }, [src]);
10917
+ const handleLoadedMetadata = () => {
10918
+ const video = videoRef.current;
10919
+ if (video) {
10920
+ setDuration(video.duration);
10607
10921
  }
10608
- ) });
10922
+ };
10923
+ const handleTimeUpdate = () => {
10924
+ const video = videoRef.current;
10925
+ if (video) {
10926
+ setCurrentTime(video.currentTime);
10927
+ }
10928
+ };
10929
+ const handleMouseEnter = () => {
10930
+ setIsHovering(true);
10931
+ if (isFullPlaying) return;
10932
+ hoverTimerRef.current = setTimeout(() => {
10933
+ const video = videoRef.current;
10934
+ if (video) {
10935
+ video.muted = true;
10936
+ video.currentTime = 0;
10937
+ video.play().then(() => setIsPlaying(true)).catch(() => {
10938
+ });
10939
+ }
10940
+ }, 300);
10941
+ };
10942
+ const handleMouseLeave = () => {
10943
+ setIsHovering(false);
10944
+ if (hoverTimerRef.current) {
10945
+ clearTimeout(hoverTimerRef.current);
10946
+ hoverTimerRef.current = null;
10947
+ }
10948
+ if (!isFullPlaying) {
10949
+ const video = videoRef.current;
10950
+ if (video) {
10951
+ video.pause();
10952
+ video.currentTime = 0;
10953
+ setIsPlaying(false);
10954
+ }
10955
+ }
10956
+ };
10957
+ const handleClick = (e) => {
10958
+ e.stopPropagation();
10959
+ const video = videoRef.current;
10960
+ if (!video) return;
10961
+ if (isFullPlaying) {
10962
+ video.pause();
10963
+ setIsPlaying(false);
10964
+ setIsFullPlaying(false);
10965
+ } else {
10966
+ video.muted = isMuted;
10967
+ video.play().then(() => {
10968
+ setIsPlaying(true);
10969
+ setIsFullPlaying(true);
10970
+ }).catch(() => {
10971
+ });
10972
+ }
10973
+ };
10974
+ const handleToggleMute = (e) => {
10975
+ e.stopPropagation();
10976
+ const video = videoRef.current;
10977
+ if (video) {
10978
+ video.muted = !isMuted;
10979
+ setIsMuted(!isMuted);
10980
+ }
10981
+ };
10982
+ const handleProgressClick = (e) => {
10983
+ e.stopPropagation();
10984
+ const video = videoRef.current;
10985
+ if (!video || !duration) return;
10986
+ const rect = e.currentTarget.getBoundingClientRect();
10987
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
10988
+ video.currentTime = ratio * duration;
10989
+ setCurrentTime(ratio * duration);
10990
+ };
10991
+ const formatTime2 = (seconds) => {
10992
+ const mins = Math.floor(seconds / 60);
10993
+ const secs = Math.floor(seconds % 60);
10994
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
10995
+ };
10996
+ const progressPercent = duration > 0 ? currentTime / duration * 100 : 0;
10997
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10998
+ showPreview && /* @__PURE__ */ jsxRuntime.jsx(VideoPreviewComp, { src, onClose: () => setShowPreview(false) }),
10999
+ /* @__PURE__ */ jsxRuntime.jsxs(
11000
+ "div",
11001
+ {
11002
+ className: cn(
11003
+ "my-2 relative rounded-xl overflow-hidden bg-black group/video cursor-pointer",
11004
+ "border border-zinc-800/60 agent-sdk-light:border-zinc-200",
11005
+ "transition-shadow duration-200 hover:shadow-xl hover:shadow-black/20",
11006
+ className
11007
+ ),
11008
+ style: { maxHeight: "400px", maxWidth: "100%" },
11009
+ onMouseEnter: handleMouseEnter,
11010
+ onMouseLeave: handleMouseLeave,
11011
+ onClick: handleClick,
11012
+ children: [
11013
+ posterUrl && !isPlaying && /* @__PURE__ */ jsxRuntime.jsx(
11014
+ "img",
11015
+ {
11016
+ src: posterUrl,
11017
+ alt: "Video cover",
11018
+ className: "absolute inset-0 w-full h-full object-contain z-[1]"
11019
+ }
11020
+ ),
11021
+ /* @__PURE__ */ jsxRuntime.jsx(
11022
+ "video",
11023
+ {
11024
+ ref: videoRef,
11025
+ src,
11026
+ muted: isMuted,
11027
+ playsInline: true,
11028
+ preload: "metadata",
11029
+ onLoadedMetadata: handleLoadedMetadata,
11030
+ onTimeUpdate: handleTimeUpdate,
11031
+ onEnded: () => {
11032
+ setIsPlaying(false);
11033
+ setIsFullPlaying(false);
11034
+ },
11035
+ className: cn(
11036
+ "w-full max-h-[400px] object-contain",
11037
+ !isPlaying && posterUrl && "opacity-0"
11038
+ )
11039
+ }
11040
+ ),
11041
+ /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "hidden" }),
11042
+ !isFullPlaying && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
11043
+ "absolute inset-0 z-[2] flex items-center justify-center",
11044
+ "bg-gradient-to-t from-black/50 via-transparent to-transparent",
11045
+ "transition-opacity duration-200",
11046
+ isHovering && isPlaying ? "opacity-0" : "opacity-100"
11047
+ ), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
11048
+ "w-14 h-14 rounded-full bg-white/20 backdrop-blur-md",
11049
+ "flex items-center justify-center",
11050
+ "transition-all duration-200",
11051
+ "group-hover/video:scale-110 group-hover/video:bg-white/30",
11052
+ "shadow-lg shadow-black/20"
11053
+ ), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "white", className: "ml-1", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) }) }) }),
11054
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
11055
+ "absolute bottom-0 left-0 right-0 z-[3]",
11056
+ "bg-gradient-to-t from-black/80 via-black/40 to-transparent",
11057
+ "px-3 pb-2.5 pt-8",
11058
+ "transition-opacity duration-200",
11059
+ isHovering || isFullPlaying ? "opacity-100" : "opacity-0"
11060
+ ), children: [
11061
+ /* @__PURE__ */ jsxRuntime.jsx(
11062
+ "div",
11063
+ {
11064
+ className: "w-full h-1 bg-white/20 rounded-full mb-2 cursor-pointer group/progress",
11065
+ onClick: handleProgressClick,
11066
+ children: /* @__PURE__ */ jsxRuntime.jsx(
11067
+ "div",
11068
+ {
11069
+ className: "h-full bg-[#d8ff00] rounded-full relative transition-all duration-75",
11070
+ style: { width: `${progressPercent}%` },
11071
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-1/2 -translate-y-1/2 w-3 h-3 bg-[#d8ff00] rounded-full opacity-0 group-hover/progress:opacity-100 transition-opacity shadow-md" })
11072
+ }
11073
+ )
11074
+ }
11075
+ ),
11076
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
11077
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
11078
+ /* @__PURE__ */ jsxRuntime.jsx(
11079
+ "button",
11080
+ {
11081
+ onClick: handleClick,
11082
+ className: "p-1 text-white/80 hover:text-white transition-colors",
11083
+ children: isPlaying ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: [
11084
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1" }),
11085
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1" })
11086
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) })
11087
+ }
11088
+ ),
11089
+ /* @__PURE__ */ jsxRuntime.jsx(
11090
+ "button",
11091
+ {
11092
+ onClick: handleToggleMute,
11093
+ className: "p-1 text-white/80 hover:text-white transition-colors",
11094
+ children: isMuted ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
11095
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
11096
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "9", x2: "17", y2: "15" }),
11097
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "17", y1: "9", x2: "23", y2: "15" })
11098
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
11099
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
11100
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" }),
11101
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" })
11102
+ ] })
11103
+ }
11104
+ ),
11105
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-white/60 font-mono tabular-nums", children: [
11106
+ formatTime2(currentTime),
11107
+ " / ",
11108
+ formatTime2(duration)
11109
+ ] })
11110
+ ] }),
11111
+ /* @__PURE__ */ jsxRuntime.jsx(
11112
+ "button",
11113
+ {
11114
+ onClick: (e) => {
11115
+ e.stopPropagation();
11116
+ const video = videoRef.current;
11117
+ if (video) {
11118
+ video.pause();
11119
+ setIsPlaying(false);
11120
+ setIsFullPlaying(false);
11121
+ }
11122
+ setShowPreview(true);
11123
+ },
11124
+ className: "p-1 text-white/60 hover:text-white transition-colors",
11125
+ title: "\u5168\u5C4F\u9884\u89C8",
11126
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
11127
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
11128
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 21 3 21 3 15" }),
11129
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
11130
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
11131
+ ] })
11132
+ }
11133
+ )
11134
+ ] })
11135
+ ] })
11136
+ ]
11137
+ }
11138
+ )
11139
+ ] });
10609
11140
  });
10610
11141
  var ImagePreviewInternal = React20.memo(function ImagePreviewInternal2({
10611
11142
  src,
@@ -12251,6 +12782,7 @@ var ToolResultRenderer = React20.memo(function ToolResultRenderer2({
12251
12782
  result,
12252
12783
  mediaUrls,
12253
12784
  config,
12785
+ toolCallData,
12254
12786
  onOpenArtifact
12255
12787
  }) {
12256
12788
  const [imgLoaded, setImgLoaded] = React20.useState({});
@@ -12317,32 +12849,134 @@ var ToolResultRenderer = React20.memo(function ToolResultRenderer2({
12317
12849
  }
12318
12850
  ),
12319
12851
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
12320
- hasMedia && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2", children: [
12321
- videoUrls.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 mb-2", children: videoUrls.map((video, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative rounded-lg overflow-hidden bg-black", children: /* @__PURE__ */ jsxRuntime.jsx(
12322
- "video",
12323
- {
12324
- src: video.url,
12325
- controls: true,
12326
- className: "w-full max-h-60 object-contain",
12327
- preload: "metadata",
12328
- children: "\u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u89C6\u9891\u64AD\u653E"
12852
+ hasMedia && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2", children: (() => {
12853
+ const totalMedia = imageUrls.length + videoUrls.length;
12854
+ const isMultiple = totalMedia > 1;
12855
+ const buildMediaMetadata = (mediaIndex) => {
12856
+ if (!toolCallData) return void 0;
12857
+ const resultAny = toolCallData.result;
12858
+ const metadata = {};
12859
+ if (toolCallData.name) metadata.toolName = toolCallData.name;
12860
+ if (resultAny?.relation?.toolName) metadata.toolName = resultAny.relation.toolName;
12861
+ if (toolCallData.arguments) metadata.generationParams = toolCallData.arguments;
12862
+ if (resultAny?.relation) metadata.relation = resultAny.relation;
12863
+ if (resultAny?.taskId) metadata.taskId = resultAny.taskId;
12864
+ const work = resultAny?.works?.[mediaIndex];
12865
+ if (work) {
12866
+ if (work.publicId) metadata.workId = work.publicId;
12867
+ if (work.fileId) metadata.fileId = work.fileId;
12329
12868
  }
12330
- ) }, `video-${i}`)) }),
12331
- imageUrls.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { children: imageUrls.map((img, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative group/img overflow-hidden ${imageUrls.length > 1 ? "aspect-square bg-black" : ""}`, children: [
12332
- !imgLoaded[i] && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `bg-zinc-800 animate-pulse flex items-center justify-center`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-5 w-5 border-b-2 border-[#d8ff00]" }) }),
12333
- /* @__PURE__ */ jsxRuntime.jsx(
12334
- "img",
12869
+ return metadata;
12870
+ };
12871
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: isMultiple ? "grid grid-cols-2 gap-2" : "flex", children: [
12872
+ videoUrls.map((video, i) => /* @__PURE__ */ jsxRuntime.jsxs(
12873
+ "div",
12335
12874
  {
12336
- src: img.url,
12337
- alt: "Generated",
12338
- onLoad: () => handleImgLoad(i),
12339
- className: `cursor-zoom-in ${imgLoaded[i] ? "" : "hidden"}`,
12340
- onClick: () => setPreviewUrl(img.hdUrl),
12341
- style: { height: 80 }
12342
- }
12343
- )
12344
- ] }, `img-${i}`)) })
12345
- ] }),
12875
+ className: `relative group/media overflow-hidden rounded-lg bg-black ${isMultiple ? "aspect-square" : "max-h-[375px]"}`,
12876
+ children: [
12877
+ /* @__PURE__ */ jsxRuntime.jsx(
12878
+ MessageVideoInternal,
12879
+ {
12880
+ src: video.hdUrl || video.url,
12881
+ className: isMultiple ? "w-full h-full object-cover" : "max-h-[375px] max-w-full object-contain"
12882
+ }
12883
+ ),
12884
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-0 left-0 right-0 p-2 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover/media:opacity-100 transition-opacity duration-200 flex items-center justify-between z-[10]", children: [
12885
+ onOpenArtifact && /* @__PURE__ */ jsxRuntime.jsxs(
12886
+ "button",
12887
+ {
12888
+ onClick: (e) => {
12889
+ e.stopPropagation();
12890
+ const resultAny = toolCallData?.result;
12891
+ const displayName = resultAny?.relation?.toolName || toolCallData?.name || "\u89C6\u9891\u751F\u6210";
12892
+ onOpenArtifact({
12893
+ type: "video",
12894
+ title: `${displayName} #${i + 1}`,
12895
+ content: video.hdUrl || video.url,
12896
+ metadata: buildMediaMetadata(imageUrls.length + i)
12897
+ });
12898
+ },
12899
+ className: "flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-medium text-white bg-white/15 hover:bg-white/25 backdrop-blur-sm rounded-md transition-colors",
12900
+ children: [
12901
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 12 }),
12902
+ "\u67E5\u770B\u8BE6\u60C5"
12903
+ ]
12904
+ }
12905
+ ),
12906
+ /* @__PURE__ */ jsxRuntime.jsx(
12907
+ "button",
12908
+ {
12909
+ onClick: (e) => {
12910
+ e.stopPropagation();
12911
+ window.open(video.hdUrl || video.url, "_blank");
12912
+ },
12913
+ className: "p-1.5 text-white/80 hover:text-white bg-white/15 hover:bg-white/25 backdrop-blur-sm rounded-md transition-colors",
12914
+ title: "\u65B0\u7A97\u53E3\u6253\u5F00",
12915
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 14 })
12916
+ }
12917
+ )
12918
+ ] })
12919
+ ]
12920
+ },
12921
+ `video-${i}`
12922
+ )),
12923
+ imageUrls.map((img, i) => /* @__PURE__ */ jsxRuntime.jsxs(
12924
+ "div",
12925
+ {
12926
+ className: `relative group/media overflow-hidden rounded-lg bg-black ${isMultiple ? "aspect-square" : "max-h-[375px]"}`,
12927
+ children: [
12928
+ !imgLoaded[i] && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-zinc-800 animate-pulse flex items-center justify-center w-full aspect-square", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-5 w-5 border-b-2 border-[#d8ff00]" }) }),
12929
+ /* @__PURE__ */ jsxRuntime.jsx(
12930
+ "img",
12931
+ {
12932
+ src: img.url,
12933
+ alt: "Generated",
12934
+ onLoad: () => handleImgLoad(i),
12935
+ className: `cursor-zoom-in ${imgLoaded[i] ? "" : "hidden"} ${isMultiple ? "w-full h-full object-cover" : "max-h-[375px] max-w-full h-auto object-contain"}`,
12936
+ onClick: () => setPreviewUrl(img.hdUrl)
12937
+ }
12938
+ ),
12939
+ imgLoaded[i] && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-0 left-0 right-0 p-2 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover/media:opacity-100 transition-opacity duration-200 flex items-center justify-between", children: [
12940
+ onOpenArtifact && /* @__PURE__ */ jsxRuntime.jsxs(
12941
+ "button",
12942
+ {
12943
+ onClick: (e) => {
12944
+ e.stopPropagation();
12945
+ const resultAny = toolCallData?.result;
12946
+ const displayName = resultAny?.relation?.toolName || toolCallData?.name || "\u56FE\u7247\u751F\u6210";
12947
+ onOpenArtifact({
12948
+ type: "image",
12949
+ title: `${displayName} #${i + 1}`,
12950
+ content: img.hdUrl || img.url,
12951
+ metadata: buildMediaMetadata(i)
12952
+ });
12953
+ },
12954
+ className: "flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-medium text-white bg-white/15 hover:bg-white/25 backdrop-blur-sm rounded-md transition-colors",
12955
+ children: [
12956
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 12 }),
12957
+ "\u67E5\u770B\u8BE6\u60C5"
12958
+ ]
12959
+ }
12960
+ ),
12961
+ /* @__PURE__ */ jsxRuntime.jsx(
12962
+ "button",
12963
+ {
12964
+ onClick: (e) => {
12965
+ e.stopPropagation();
12966
+ setPreviewUrl(img.hdUrl);
12967
+ },
12968
+ className: "p-1.5 text-white/80 hover:text-white bg-white/15 hover:bg-white/25 backdrop-blur-sm rounded-md transition-colors",
12969
+ title: "\u653E\u5927\u9884\u89C8",
12970
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Image, { size: 14 })
12971
+ }
12972
+ )
12973
+ ] })
12974
+ ]
12975
+ },
12976
+ `img-${i}`
12977
+ ))
12978
+ ] });
12979
+ })() }),
12346
12980
  hasCustomResponses && customResult
12347
12981
  ] })
12348
12982
  ] });
@@ -12468,6 +13102,7 @@ var ToolCallCard3 = React20.memo(function ToolCallCard4({
12468
13102
  result: toolCall.result,
12469
13103
  mediaUrls,
12470
13104
  config,
13105
+ toolCallData: toolCall,
12471
13106
  defaultExpanded: false,
12472
13107
  onOpenArtifact
12473
13108
  }
@@ -12738,7 +13373,7 @@ var ComponentPendingCard = React20.memo(function ComponentPendingCard2({
12738
13373
  })() })
12739
13374
  ] });
12740
13375
  });
12741
- var ThoughtTimelineCard = React20.memo(function ThoughtTimelineCard2({ thought, config, isLoading = false }) {
13376
+ var ThoughtTimelineCard = React20.memo(function ThoughtTimelineCard2({ thought, config, isLoading = false, skipImages = false }) {
12742
13377
  const [expanded, setExpanded] = React20.useState(true);
12743
13378
  const { showItemTime } = useAgentStore();
12744
13379
  const thoughtType = thought?.type || "";
@@ -12762,9 +13397,9 @@ var ThoughtTimelineCard = React20.memo(function ThoughtTimelineCard2({ thought,
12762
13397
  return null;
12763
13398
  }
12764
13399
  if (thought?.raw && Array.isArray(thought.raw)) {
12765
- const hasConfirmationEvent = thought.raw.some((r2) => {
12766
- const type = r2?.type || "";
12767
- return excludedTypes.includes(type) || r2?.content && (r2.content.includes('"type":"tool_confirmed"') || r2.content.includes('"type":"component_submitted"') || r2.content.includes("\u7528\u6237\u786E\u8BA4\u6267\u884C\u5DE5\u5177\u8C03\u7528"));
13400
+ const hasConfirmationEvent = thought.raw.some((r3) => {
13401
+ const type = r3?.type || "";
13402
+ return excludedTypes.includes(type) || r3?.content && (r3.content.includes('"type":"tool_confirmed"') || r3.content.includes('"type":"component_submitted"') || r3.content.includes("\u7528\u6237\u786E\u8BA4\u6267\u884C\u5DE5\u5177\u8C03\u7528"));
12768
13403
  });
12769
13404
  if (hasConfirmationEvent) {
12770
13405
  return null;
@@ -12776,12 +13411,19 @@ var ThoughtTimelineCard = React20.memo(function ThoughtTimelineCard2({ thought,
12776
13411
  if (!cleaned || !cleaned.trim()) return null;
12777
13412
  return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 py-2", children: [
12778
13413
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm leading-relaxed prose prose-invert prose-sm max-w-none [&_*]:break-words", children: [
12779
- /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { content: cleaned, skipImages: false, config, variant: "thought" }),
13414
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { content: cleaned, skipImages, config, variant: "thought" }),
12780
13415
  isLoading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-2 h-4 ml-0.5 bg-[#d8ff00] animate-pulse rounded-sm" })
12781
13416
  ] }) }),
12782
13417
  thought?.raw?.length > 0 && showItemTime && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-zinc-500 mt-2", children: new Date(
12783
13418
  thought.raw[thought.raw.length - 1]?.gmt_create || thought.raw[thought.raw.length - 1]?.gmtCreate || thought.raw[thought.raw.length - 1]?.timestamp || ""
12784
- ).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit", second: "2-digit" }) })
13419
+ ).toLocaleTimeString("zh-CN", {
13420
+ year: "numeric",
13421
+ month: "2-digit",
13422
+ day: "2-digit",
13423
+ hour: "2-digit",
13424
+ minute: "2-digit",
13425
+ second: "2-digit"
13426
+ }) })
12785
13427
  ] }) });
12786
13428
  });
12787
13429
  var COMPONENT_TAGS_TO_STRIP = [
@@ -13781,7 +14423,8 @@ ${m.content}`;
13781
14423
  }
13782
14424
  var ThoughtBlockItem = React20.memo(function ThoughtBlockItem2({
13783
14425
  content,
13784
- config
14426
+ config,
14427
+ skipImages = false
13785
14428
  }) {
13786
14429
  const [expanded, setExpanded] = React20.useState(false);
13787
14430
  const lineCount = React20.useMemo(() => content.split("\n").length, [content]);
@@ -13794,7 +14437,7 @@ var ThoughtBlockItem = React20.memo(function ThoughtBlockItem2({
13794
14437
  "text-sm text-zinc-400 leading-relaxed prose prose-invert prose-sm max-w-none [&_*]:break-words",
13795
14438
  shouldToggle && !expanded ? "max-h-[3.5em] overflow-hidden" : ""
13796
14439
  ),
13797
- children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { content, skipImages: false, config, variant: "thought" })
14440
+ children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { content, skipImages, config, variant: "thought" })
13798
14441
  }
13799
14442
  ),
13800
14443
  shouldToggle && /* @__PURE__ */ jsxRuntime.jsx(
@@ -13980,7 +14623,6 @@ var MessageBubble = React20.memo(function MessageBubble2({
13980
14623
  const shouldShowAvatar = isUser ? showUserAvatar : showAssistantAvatar;
13981
14624
  const shouldShowName = isUser ? showUserName : showAssistantName;
13982
14625
  const shouldShowDescription = isUser ? showUserDescription : showAssistantDescription;
13983
- console.log("[MessageBubble] \u6E32\u67D3\u6D88\u606F\u6C14\u6CE1, config?.identity:", config?.identity);
13984
14626
  const resolveFileName = (url) => {
13985
14627
  try {
13986
14628
  const pathname = new URL(url, "http://dummy").pathname;
@@ -14041,7 +14683,8 @@ var MessageBubble = React20.memo(function MessageBubble2({
14041
14683
  {
14042
14684
  thought: item.data,
14043
14685
  config,
14044
- isLoading: showLoadingCursor
14686
+ isLoading: showLoadingCursor,
14687
+ skipImages: hasToolCallImages
14045
14688
  },
14046
14689
  item.id
14047
14690
  );
@@ -14141,7 +14784,8 @@ var MessageBubble = React20.memo(function MessageBubble2({
14141
14784
  ThoughtBlockItem,
14142
14785
  {
14143
14786
  content: block.content,
14144
- config
14787
+ config,
14788
+ skipImages: hasToolCallImages
14145
14789
  },
14146
14790
  `${block.ts}-${index}`
14147
14791
  )),
@@ -14186,15 +14830,23 @@ var MessageBubble = React20.memo(function MessageBubble2({
14186
14830
  const isVideo = att.type === "video" || att.type?.startsWith("video/") || isVideoUrl2(att.url);
14187
14831
  const isImage2 = att.type === "image" || att.type?.startsWith("image/") || !isVideo && /\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?|#|$)/i.test(att.url);
14188
14832
  const fileName = att.name || resolveFileName(att.url);
14833
+ if (att.url.includes("industryai")) {
14834
+ att.url = att.url.split("?")[0];
14835
+ }
14836
+ const assets = config?.asset?.transform?.({
14837
+ type: att.type,
14838
+ url: att.url,
14839
+ name: fileName
14840
+ }) || { url: att.url, type: att.type };
14189
14841
  if (isImage2 || isVideo) {
14190
14842
  return /* @__PURE__ */ jsxRuntime.jsxs(
14191
14843
  "button",
14192
14844
  {
14193
14845
  type: "button",
14194
- onClick: () => setPreviewMedia({ url: att.url, isVideo }),
14846
+ onClick: () => setPreviewMedia({ url: assets.url, isVideo }),
14195
14847
  className: "group/att relative w-[88px] h-[88px] rounded-lg overflow-hidden border border-zinc-700/40 bg-zinc-900/60 hover:border-zinc-500/60 transition-colors",
14196
14848
  children: [
14197
- isVideo ? /* @__PURE__ */ jsxRuntime.jsx(MessageVideoComp, { src: att.url, className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(MessageImageComp, { src: att.url, alt: fileName, className: "w-full h-full object-cover" }),
14849
+ isVideo ? /* @__PURE__ */ jsxRuntime.jsx(MessageVideoComp, { src: assets.url, className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(MessageImageComp, { src: att.url, alt: fileName, className: "w-full h-full object-cover" }),
14198
14850
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/20 opacity-0 group-hover/att:opacity-100 transition-opacity" })
14199
14851
  ]
14200
14852
  },
@@ -14299,7 +14951,14 @@ var MessageBubble = React20.memo(function MessageBubble2({
14299
14951
  ]
14300
14952
  }
14301
14953
  ) }),
14302
- message.gmtCreate && !isStreaming && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-zinc-600 text-[10px] mt-1", children: new Date(message.gmtCreate).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit", second: "2-digit" }) })
14954
+ message.gmtCreate && !isStreaming && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-zinc-600 text-[10px] mt-1 opacity-0 group-hover:opacity-100 transition-opacity", children: new Date(message.gmtCreate).toLocaleTimeString("zh-CN", {
14955
+ year: "numeric",
14956
+ month: "2-digit",
14957
+ day: "2-digit",
14958
+ hour: "2-digit",
14959
+ minute: "2-digit",
14960
+ second: "2-digit"
14961
+ }) })
14303
14962
  ] })
14304
14963
  ] });
14305
14964
  });
@@ -15174,9 +15833,24 @@ var DialogDescription = React20__namespace.forwardRef(({ className, ...props },
15174
15833
  ));
15175
15834
  DialogDescription.displayName = DialogPrimitive__namespace.Description.displayName;
15176
15835
 
15836
+ // ../../node_modules/clsx/dist/clsx.mjs
15837
+ function r2(e) {
15838
+ var t, f, n = "";
15839
+ if ("string" == typeof e || "number" == typeof e) n += e;
15840
+ else if ("object" == typeof e) if (Array.isArray(e)) {
15841
+ var o = e.length;
15842
+ for (t = 0; t < o; t++) e[t] && (f = r2(e[t])) && (n && (n += " "), n += f);
15843
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
15844
+ return n;
15845
+ }
15846
+ function clsx2() {
15847
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r2(e)) && (n && (n += " "), n += t);
15848
+ return n;
15849
+ }
15850
+
15177
15851
  // ../../node_modules/class-variance-authority/dist/index.mjs
15178
15852
  var falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
15179
- var cx = clsx;
15853
+ var cx = clsx2;
15180
15854
  var cva = (base, config) => (props) => {
15181
15855
  var _config_compoundVariants;
15182
15856
  if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
@@ -15994,10 +16668,10 @@ var Observer = class {
15994
16668
  });
15995
16669
  }
15996
16670
  };
15997
- this.custom = (jsx50, data) => {
16671
+ this.custom = (jsx59, data) => {
15998
16672
  const id = (data == null ? void 0 : data.id) || toastsCounter++;
15999
16673
  this.create({
16000
- jsx: jsx50(id),
16674
+ jsx: jsx59(id),
16001
16675
  id,
16002
16676
  ...data
16003
16677
  });
@@ -18498,7 +19172,57 @@ var ToolbarButton = React20.memo(function ToolbarButton2({
18498
19172
  }
18499
19173
  );
18500
19174
  });
18501
- var CodeBlock3 = React20.memo(function CodeBlock4({
19175
+ var artifactTypeConfig = {
19176
+ html: {
19177
+ icon: lucideReact.Globe,
19178
+ label: "HTML",
19179
+ color: "text-orange-400",
19180
+ bgColor: "bg-orange-500/10"
19181
+ },
19182
+ svg: {
19183
+ icon: lucideReact.Globe,
19184
+ label: "SVG",
19185
+ color: "text-orange-300",
19186
+ bgColor: "bg-orange-500/10"
19187
+ },
19188
+ markdown: {
19189
+ icon: lucideReact.FileText,
19190
+ label: "Markdown",
19191
+ color: "text-blue-400",
19192
+ bgColor: "bg-blue-500/10"
19193
+ },
19194
+ json: {
19195
+ icon: lucideReact.FileJson,
19196
+ label: "JSON",
19197
+ color: "text-yellow-400",
19198
+ bgColor: "bg-yellow-500/10"
19199
+ },
19200
+ code: {
19201
+ icon: lucideReact.FileCode,
19202
+ label: "Code",
19203
+ color: "text-purple-400",
19204
+ bgColor: "bg-purple-500/10"
19205
+ },
19206
+ text: {
19207
+ icon: lucideReact.FileText,
19208
+ label: "Text",
19209
+ color: "text-zinc-400",
19210
+ bgColor: "bg-zinc-500/10"
19211
+ },
19212
+ image: {
19213
+ icon: lucideReact.FileImage,
19214
+ label: "Image",
19215
+ color: "text-green-400",
19216
+ bgColor: "bg-green-500/10"
19217
+ },
19218
+ video: {
19219
+ icon: lucideReact.Video,
19220
+ label: "Video",
19221
+ color: "text-pink-400",
19222
+ bgColor: "bg-pink-500/10"
19223
+ }
19224
+ };
19225
+ var CodeBlock3 = React20.memo(function CodeBlock4({
18502
19226
  code: code3,
18503
19227
  language,
18504
19228
  showLineNumbers = true
@@ -18786,38 +19510,1305 @@ React20.memo(function JsonPreview2({ content }) {
18786
19510
  }
18787
19511
  return /* @__PURE__ */ jsxRuntime.jsx(CodeBlock3, { code: formattedJson, language: "json" });
18788
19512
  });
18789
- var artifactTypeConfig = {
18790
- html: {
18791
- icon: lucideReact.Globe,
18792
- label: "HTML",
18793
- color: "text-orange-400",
18794
- bgColor: "bg-orange-500/10"
18795
- },
18796
- markdown: {
18797
- icon: lucideReact.FileText,
18798
- label: "Markdown",
18799
- color: "text-blue-400",
18800
- bgColor: "bg-blue-500/10"
18801
- },
18802
- json: {
18803
- icon: lucideReact.FileJson,
18804
- label: "JSON",
18805
- color: "text-yellow-400",
18806
- bgColor: "bg-yellow-500/10"
18807
- },
18808
- code: {
18809
- icon: lucideReact.FileCode,
18810
- label: "Code",
18811
- color: "text-purple-400",
18812
- bgColor: "bg-purple-500/10"
18813
- },
18814
- text: {
18815
- icon: lucideReact.FileText,
18816
- label: "Text",
18817
- color: "text-zinc-400",
18818
- bgColor: "bg-zinc-500/10"
18819
- }
18820
- };
19513
+ var CodeEditor = React20.memo(function CodeEditor2({
19514
+ code: code3,
19515
+ language,
19516
+ onChange
19517
+ }) {
19518
+ const textareaRef = React20.useRef(null);
19519
+ const handleKeyDown = React20.useCallback((e) => {
19520
+ if (e.key === "Tab") {
19521
+ e.preventDefault();
19522
+ const textarea = e.currentTarget;
19523
+ const start = textarea.selectionStart;
19524
+ const end = textarea.selectionEnd;
19525
+ const newValue = code3.substring(0, start) + " " + code3.substring(end);
19526
+ onChange(newValue);
19527
+ requestAnimationFrame(() => {
19528
+ textarea.selectionStart = textarea.selectionEnd = start + 2;
19529
+ });
19530
+ }
19531
+ }, [code3, onChange]);
19532
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-950 agent-sdk-light:bg-white", children: [
19533
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between px-4 py-2 border-b border-zinc-800/50 agent-sdk-light:border-zinc-200 bg-zinc-900/30 agent-sdk-light:bg-zinc-50 flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
19534
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { size: 12, className: "text-[#d8ff00]" }),
19535
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-[#d8ff00] font-medium", children: "\u7F16\u8F91\u6A21\u5F0F" }),
19536
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-zinc-500 agent-sdk-light:text-zinc-600 font-mono uppercase ml-2", children: language || "text" })
19537
+ ] }) }),
19538
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
19539
+ "textarea",
19540
+ {
19541
+ ref: textareaRef,
19542
+ value: code3,
19543
+ onChange: (e) => onChange(e.target.value),
19544
+ onKeyDown: handleKeyDown,
19545
+ className: "w-full h-full p-4 bg-zinc-950 agent-sdk-light:bg-white text-xs font-mono text-zinc-300 agent-sdk-light:text-zinc-800 leading-relaxed resize-none focus:outline-none",
19546
+ spellCheck: false,
19547
+ autoComplete: "off",
19548
+ autoCorrect: "off",
19549
+ autoCapitalize: "off"
19550
+ }
19551
+ ) })
19552
+ ] });
19553
+ });
19554
+ function useResolvedMediaUrl(content, options) {
19555
+ const { fileId, type = "image", config } = options || {};
19556
+ const [resolvedUrl, setResolvedUrl] = React20.useState(content);
19557
+ const [resolvedHdUrl, setResolvedHdUrl] = React20.useState(content);
19558
+ const [isLoading, setIsLoading] = React20.useState(true);
19559
+ React20.useEffect(() => {
19560
+ let active = true;
19561
+ setIsLoading(true);
19562
+ const resolve = async () => {
19563
+ const httpUrl = isHttpUrl(content) ? content : void 0;
19564
+ const asset = createAssetFromSource({
19565
+ fileId: fileId || (!httpUrl ? content : void 0),
19566
+ fileUrl: httpUrl,
19567
+ type
19568
+ });
19569
+ if (!asset) {
19570
+ if (active) {
19571
+ setResolvedUrl(content);
19572
+ setResolvedHdUrl(content);
19573
+ setIsLoading(false);
19574
+ }
19575
+ return;
19576
+ }
19577
+ const resolved = await resolveAssetForDisplay(asset, config);
19578
+ if (!active) return;
19579
+ if (resolved) {
19580
+ setResolvedUrl(resolved.url);
19581
+ setResolvedHdUrl(resolved.hdUrl || resolved.url);
19582
+ } else {
19583
+ setResolvedUrl(content);
19584
+ setResolvedHdUrl(content);
19585
+ }
19586
+ setIsLoading(false);
19587
+ };
19588
+ resolve();
19589
+ return () => {
19590
+ active = false;
19591
+ };
19592
+ }, [content, fileId, type, config]);
19593
+ return { url: resolvedUrl, hdUrl: resolvedHdUrl, isLoading };
19594
+ }
19595
+ function useResolvedThumbnailUrl(content, options) {
19596
+ const { fileId, type = "image", config, enabled = true } = options || {};
19597
+ const [url, setUrl] = React20.useState(null);
19598
+ React20.useEffect(() => {
19599
+ if (!enabled || !content) {
19600
+ setUrl(null);
19601
+ return;
19602
+ }
19603
+ if (type === "video") {
19604
+ setUrl(null);
19605
+ return;
19606
+ }
19607
+ let active = true;
19608
+ const resolve = async () => {
19609
+ const httpUrl = isHttpUrl(content) ? content : void 0;
19610
+ const asset = createAssetFromSource({
19611
+ fileId: fileId || (!httpUrl ? content : void 0),
19612
+ fileUrl: httpUrl,
19613
+ type
19614
+ });
19615
+ if (!asset) {
19616
+ if (active) setUrl(isHttpUrl(content) ? content : null);
19617
+ return;
19618
+ }
19619
+ const resolved = await resolveAssetForDisplay(asset, config);
19620
+ if (!active) return;
19621
+ setUrl(resolved?.url || (isHttpUrl(content) ? content : null));
19622
+ };
19623
+ resolve();
19624
+ return () => {
19625
+ active = false;
19626
+ };
19627
+ }, [content, fileId, type, config, enabled]);
19628
+ return url;
19629
+ }
19630
+ var ImageArtifactPreview = React20.memo(function ImageArtifactPreview2({
19631
+ content,
19632
+ metadata,
19633
+ config
19634
+ }) {
19635
+ const [isZoomed, setIsZoomed] = React20.useState(false);
19636
+ const [showInfo, setShowInfo] = React20.useState(true);
19637
+ const [imgNaturalSize, setImgNaturalSize] = React20.useState(null);
19638
+ const [copied, setCopied] = React20.useState(null);
19639
+ const { hdUrl: resolvedHdUrl } = useResolvedMediaUrl(content, {
19640
+ fileId: metadata?.fileId,
19641
+ type: "image",
19642
+ config
19643
+ });
19644
+ const generationParams = metadata?.generationParams;
19645
+ const toolName = metadata?.toolName;
19646
+ const relation = metadata?.relation;
19647
+ const handleCopy = React20.useCallback(async (text3, label) => {
19648
+ try {
19649
+ await navigator.clipboard.writeText(text3);
19650
+ setCopied(label);
19651
+ setTimeout(() => setCopied(null), 2e3);
19652
+ } catch {
19653
+ }
19654
+ }, []);
19655
+ const handleDownload = React20.useCallback(() => {
19656
+ const link2 = document.createElement("a");
19657
+ link2.href = resolvedHdUrl;
19658
+ link2.download = `generated-${Date.now()}.png`;
19659
+ link2.target = "_blank";
19660
+ document.body.appendChild(link2);
19661
+ link2.click();
19662
+ document.body.removeChild(link2);
19663
+ }, [resolvedHdUrl]);
19664
+ const formatParamKey = (key) => {
19665
+ const keyMap = {
19666
+ content: "Prompt",
19667
+ width: "\u5BBD\u5EA6",
19668
+ height: "\u9AD8\u5EA6",
19669
+ batchSize: "\u751F\u6210\u6570\u91CF",
19670
+ steps: "\u6B65\u6570",
19671
+ cfg_scale: "CFG Scale",
19672
+ seed: "\u79CD\u5B50",
19673
+ sampler: "\u91C7\u6837\u5668",
19674
+ model: "\u6A21\u578B",
19675
+ negative_prompt: "\u53CD\u5411\u63D0\u793A\u8BCD",
19676
+ style: "\u98CE\u683C",
19677
+ quality: "\u8D28\u91CF"
19678
+ };
19679
+ return keyMap[key] || key.replace(/([A-Z])/g, " $1").replace(/_/g, " ").replace(/^./, (s) => s.toUpperCase());
19680
+ };
19681
+ const promptText = generationParams?.content ? String(generationParams.content) : null;
19682
+ const otherParams = generationParams ? Object.entries(generationParams).filter(([key]) => key !== "content") : [];
19683
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-950 agent-sdk-light:bg-white", children: [
19684
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto flex items-center justify-center p-6 bg-zinc-950 agent-sdk-light:bg-zinc-50 relative", children: [
19685
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 opacity-[0.03]", style: {
19686
+ backgroundImage: "linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%)",
19687
+ backgroundSize: "20px 20px",
19688
+ backgroundPosition: "0 0, 0 10px, 10px -10px, -10px 0px"
19689
+ } }),
19690
+ /* @__PURE__ */ jsxRuntime.jsx(
19691
+ "div",
19692
+ {
19693
+ className: cn2(
19694
+ "relative transition-transform duration-300 cursor-zoom-in z-10",
19695
+ isZoomed && "cursor-zoom-out scale-[2] z-50"
19696
+ ),
19697
+ onClick: () => setIsZoomed((prev) => !prev),
19698
+ children: /* @__PURE__ */ jsxRuntime.jsx(
19699
+ "img",
19700
+ {
19701
+ src: resolvedHdUrl,
19702
+ alt: "Generated image",
19703
+ className: "max-w-full max-h-[60vh] object-contain rounded-lg shadow-2xl",
19704
+ loading: "lazy",
19705
+ onLoad: (e) => {
19706
+ const img = e.target;
19707
+ setImgNaturalSize({ width: img.naturalWidth, height: img.naturalHeight });
19708
+ }
19709
+ }
19710
+ )
19711
+ }
19712
+ ),
19713
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-4 right-4 flex items-center gap-1.5 z-20", children: [
19714
+ /* @__PURE__ */ jsxRuntime.jsx(
19715
+ "button",
19716
+ {
19717
+ onClick: () => setShowInfo((prev) => !prev),
19718
+ className: cn2(
19719
+ "p-2 rounded-lg backdrop-blur-sm transition-colors",
19720
+ showInfo ? "bg-[#d8ff00]/20 text-[#d8ff00]" : "bg-black/30 text-white/70 hover:text-white hover:bg-black/50"
19721
+ ),
19722
+ title: showInfo ? "\u9690\u85CF\u8BE6\u60C5" : "\u663E\u793A\u8BE6\u60C5",
19723
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 16 })
19724
+ }
19725
+ ),
19726
+ /* @__PURE__ */ jsxRuntime.jsx(
19727
+ "button",
19728
+ {
19729
+ onClick: handleDownload,
19730
+ className: "p-2 bg-black/30 text-white/70 hover:text-white hover:bg-black/50 rounded-lg backdrop-blur-sm transition-colors",
19731
+ title: "\u4E0B\u8F7D\u56FE\u7247",
19732
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 16 })
19733
+ }
19734
+ )
19735
+ ] }),
19736
+ imgNaturalSize && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-4 left-4 z-20 flex items-center gap-2 px-2.5 py-1.5 bg-black/40 backdrop-blur-sm rounded-lg", children: [
19737
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileImage, { size: 12, className: "text-white/60" }),
19738
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-white/80 font-mono", children: [
19739
+ imgNaturalSize.width,
19740
+ " \xD7 ",
19741
+ imgNaturalSize.height
19742
+ ] })
19743
+ ] })
19744
+ ] }),
19745
+ showInfo && (generationParams || toolName) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 border-t border-zinc-800/50 agent-sdk-light:border-zinc-200 bg-zinc-900/80 agent-sdk-light:bg-zinc-50 max-h-[40%] overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-5 py-4 space-y-4", children: [
19746
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
19747
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
19748
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-lg bg-[#d8ff00]/10 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 14, className: "text-[#d8ff00]" }) }),
19749
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
19750
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-zinc-200 agent-sdk-light:text-zinc-800", children: toolName || "\u56FE\u7247\u751F\u6210" }),
19751
+ relation?.workflowCode && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 text-[10px] text-zinc-500 font-mono bg-zinc-800/50 agent-sdk-light:bg-zinc-200 px-1.5 py-0.5 rounded", children: relation.workflowCode })
19752
+ ] })
19753
+ ] }),
19754
+ metadata?.taskId && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-zinc-600 font-mono", children: [
19755
+ "Task: ",
19756
+ metadata.taskId
19757
+ ] })
19758
+ ] }),
19759
+ promptText && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
19760
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
19761
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-500 uppercase tracking-wider font-medium", children: "Prompt" }),
19762
+ /* @__PURE__ */ jsxRuntime.jsx(
19763
+ "button",
19764
+ {
19765
+ onClick: () => handleCopy(promptText, "prompt"),
19766
+ className: "text-[10px] text-zinc-500 hover:text-zinc-300 transition-colors flex items-center gap-1",
19767
+ children: copied === "prompt" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19768
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCheck, { size: 10, className: "text-green-400" }),
19769
+ " \u5DF2\u590D\u5236"
19770
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19771
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { size: 10 }),
19772
+ " \u590D\u5236"
19773
+ ] })
19774
+ }
19775
+ )
19776
+ ] }),
19777
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 bg-zinc-800/50 agent-sdk-light:bg-zinc-100 rounded-lg border border-zinc-700/30 agent-sdk-light:border-zinc-200", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-zinc-300 agent-sdk-light:text-zinc-700 leading-relaxed whitespace-pre-wrap", children: promptText }) })
19778
+ ] }),
19779
+ otherParams.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
19780
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-500 uppercase tracking-wider font-medium", children: "\u53C2\u6570" }),
19781
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-2", children: otherParams.map(([key, value]) => {
19782
+ const displayValue = String(value);
19783
+ if (displayValue.length > 100) return null;
19784
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19785
+ "div",
19786
+ {
19787
+ className: "flex flex-col gap-0.5 p-2 bg-zinc-800/30 agent-sdk-light:bg-zinc-100 rounded-lg border border-zinc-700/20 agent-sdk-light:border-zinc-200",
19788
+ children: [
19789
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-500 agent-sdk-light:text-zinc-500", children: formatParamKey(key) }),
19790
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-zinc-200 agent-sdk-light:text-zinc-800 font-mono font-medium truncate", title: displayValue, children: displayValue })
19791
+ ]
19792
+ },
19793
+ key
19794
+ );
19795
+ }) })
19796
+ ] }),
19797
+ (metadata?.fileId || metadata?.workId) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-2 border-t border-zinc-800/30 agent-sdk-light:border-zinc-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 text-[10px] text-zinc-600", children: [
19798
+ metadata.workId && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
19799
+ "ID: ",
19800
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-zinc-500", children: metadata.workId })
19801
+ ] }),
19802
+ metadata.fileId && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate max-w-[200px]", title: metadata.fileId, children: [
19803
+ "File: ",
19804
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-zinc-500", children: metadata.fileId })
19805
+ ] })
19806
+ ] }) })
19807
+ ] }) })
19808
+ ] });
19809
+ });
19810
+ var VideoArtifactPreview = React20.memo(function VideoArtifactPreview2({
19811
+ content,
19812
+ metadata,
19813
+ config
19814
+ }) {
19815
+ const { hdUrl: resolvedUrl } = useResolvedMediaUrl(content, {
19816
+ fileId: metadata?.fileId,
19817
+ type: "video",
19818
+ config
19819
+ });
19820
+ const videoRef = React20.useRef(null);
19821
+ const progressRef = React20.useRef(null);
19822
+ const [isPlaying, setIsPlaying] = React20.useState(false);
19823
+ const [isMuted, setIsMuted] = React20.useState(false);
19824
+ const [duration, setDuration] = React20.useState(0);
19825
+ const [currentTime, setCurrentTime] = React20.useState(0);
19826
+ const [volume, setVolume] = React20.useState(1);
19827
+ const [isLoaded, setIsLoaded] = React20.useState(false);
19828
+ const [showVolumeSlider, setShowVolumeSlider] = React20.useState(false);
19829
+ const [posterUrl, setPosterUrl] = React20.useState(null);
19830
+ const [isDraggingProgress, setIsDraggingProgress] = React20.useState(false);
19831
+ const generationParams = metadata?.generationParams;
19832
+ const toolName = metadata?.toolName;
19833
+ React20.useEffect(() => {
19834
+ const video = document.createElement("video");
19835
+ video.crossOrigin = "anonymous";
19836
+ video.preload = "metadata";
19837
+ video.muted = true;
19838
+ video.addEventListener("loadeddata", () => {
19839
+ video.currentTime = Math.min(0.5, video.duration * 0.1);
19840
+ });
19841
+ video.addEventListener("seeked", () => {
19842
+ try {
19843
+ const canvas = document.createElement("canvas");
19844
+ canvas.width = video.videoWidth;
19845
+ canvas.height = video.videoHeight;
19846
+ const ctx = canvas.getContext("2d");
19847
+ if (ctx) {
19848
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
19849
+ setPosterUrl(canvas.toDataURL("image/jpeg", 0.8));
19850
+ }
19851
+ } catch {
19852
+ }
19853
+ video.remove();
19854
+ });
19855
+ video.addEventListener("error", () => video.remove());
19856
+ video.src = resolvedUrl;
19857
+ video.load();
19858
+ return () => {
19859
+ video.remove();
19860
+ };
19861
+ }, [resolvedUrl]);
19862
+ const handleLoadedMetadata = () => {
19863
+ const video = videoRef.current;
19864
+ if (video) {
19865
+ setDuration(video.duration);
19866
+ setIsLoaded(true);
19867
+ }
19868
+ };
19869
+ const handleTimeUpdate = () => {
19870
+ if (!isDraggingProgress && videoRef.current) {
19871
+ setCurrentTime(videoRef.current.currentTime);
19872
+ }
19873
+ };
19874
+ const togglePlay = React20.useCallback(() => {
19875
+ const video = videoRef.current;
19876
+ if (!video) return;
19877
+ if (isPlaying) {
19878
+ video.pause();
19879
+ setIsPlaying(false);
19880
+ } else {
19881
+ video.play().then(() => setIsPlaying(true)).catch(() => {
19882
+ });
19883
+ }
19884
+ }, [isPlaying]);
19885
+ const toggleMute = React20.useCallback(() => {
19886
+ const video = videoRef.current;
19887
+ if (video) {
19888
+ video.muted = !isMuted;
19889
+ setIsMuted(!isMuted);
19890
+ }
19891
+ }, [isMuted]);
19892
+ const handleVolumeChange = React20.useCallback((e) => {
19893
+ const newVolume = parseFloat(e.target.value);
19894
+ setVolume(newVolume);
19895
+ if (videoRef.current) {
19896
+ videoRef.current.volume = newVolume;
19897
+ if (newVolume === 0) {
19898
+ setIsMuted(true);
19899
+ videoRef.current.muted = true;
19900
+ } else if (isMuted) {
19901
+ setIsMuted(false);
19902
+ videoRef.current.muted = false;
19903
+ }
19904
+ }
19905
+ }, [isMuted]);
19906
+ const handleProgressClick = React20.useCallback((e) => {
19907
+ const video = videoRef.current;
19908
+ const bar = progressRef.current;
19909
+ if (!video || !bar || !duration) return;
19910
+ const rect = bar.getBoundingClientRect();
19911
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
19912
+ video.currentTime = ratio * duration;
19913
+ setCurrentTime(ratio * duration);
19914
+ }, [duration]);
19915
+ const handleProgressDrag = React20.useCallback((e) => {
19916
+ if (!isDraggingProgress) return;
19917
+ const bar = progressRef.current;
19918
+ if (!bar || !duration) return;
19919
+ const rect = bar.getBoundingClientRect();
19920
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
19921
+ setCurrentTime(ratio * duration);
19922
+ }, [isDraggingProgress, duration]);
19923
+ const handleProgressDragEnd = React20.useCallback(() => {
19924
+ if (isDraggingProgress && videoRef.current) {
19925
+ videoRef.current.currentTime = currentTime;
19926
+ }
19927
+ setIsDraggingProgress(false);
19928
+ }, [isDraggingProgress, currentTime]);
19929
+ React20.useEffect(() => {
19930
+ if (!isDraggingProgress) return;
19931
+ const handleMouseUp = () => handleProgressDragEnd();
19932
+ const handleMouseMove = (e) => {
19933
+ const bar = progressRef.current;
19934
+ if (!bar || !duration) return;
19935
+ const rect = bar.getBoundingClientRect();
19936
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
19937
+ setCurrentTime(ratio * duration);
19938
+ };
19939
+ window.addEventListener("mouseup", handleMouseUp);
19940
+ window.addEventListener("mousemove", handleMouseMove);
19941
+ return () => {
19942
+ window.removeEventListener("mouseup", handleMouseUp);
19943
+ window.removeEventListener("mousemove", handleMouseMove);
19944
+ };
19945
+ }, [isDraggingProgress, duration, handleProgressDragEnd]);
19946
+ const handleDownload = React20.useCallback(() => {
19947
+ const link2 = document.createElement("a");
19948
+ link2.href = resolvedUrl;
19949
+ link2.download = `video-${Date.now()}.mp4`;
19950
+ document.body.appendChild(link2);
19951
+ link2.click();
19952
+ document.body.removeChild(link2);
19953
+ }, [resolvedUrl]);
19954
+ const formatTime2 = (seconds) => {
19955
+ const mins = Math.floor(seconds / 60);
19956
+ const secs = Math.floor(seconds % 60);
19957
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
19958
+ };
19959
+ const progressPercent = duration > 0 ? currentTime / duration * 100 : 0;
19960
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-950 agent-sdk-light:bg-white", children: [
19961
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-hidden flex items-center justify-center bg-black relative group/player", children: [
19962
+ posterUrl && !isPlaying && !currentTime && /* @__PURE__ */ jsxRuntime.jsx(
19963
+ "img",
19964
+ {
19965
+ src: posterUrl,
19966
+ alt: "Video cover",
19967
+ className: "absolute inset-0 w-full h-full object-contain z-[1]"
19968
+ }
19969
+ ),
19970
+ /* @__PURE__ */ jsxRuntime.jsx(
19971
+ "video",
19972
+ {
19973
+ ref: videoRef,
19974
+ src: resolvedUrl,
19975
+ muted: isMuted,
19976
+ playsInline: true,
19977
+ preload: "metadata",
19978
+ onLoadedMetadata: handleLoadedMetadata,
19979
+ onTimeUpdate: handleTimeUpdate,
19980
+ onEnded: () => setIsPlaying(false),
19981
+ onClick: togglePlay,
19982
+ className: cn2(
19983
+ "max-w-full max-h-full object-contain cursor-pointer",
19984
+ !isPlaying && posterUrl && !currentTime && "opacity-0"
19985
+ )
19986
+ }
19987
+ ),
19988
+ !isPlaying && /* @__PURE__ */ jsxRuntime.jsx(
19989
+ "div",
19990
+ {
19991
+ className: "absolute inset-0 z-[2] flex items-center justify-center cursor-pointer",
19992
+ onClick: togglePlay,
19993
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn2(
19994
+ "w-16 h-16 rounded-full bg-white/15 backdrop-blur-md",
19995
+ "flex items-center justify-center",
19996
+ "transition-all duration-200",
19997
+ "group-hover/player:scale-110 group-hover/player:bg-white/25",
19998
+ "shadow-2xl shadow-black/30"
19999
+ ), children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "26", height: "26", viewBox: "0 0 24 24", fill: "white", className: "ml-1", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) }) })
20000
+ }
20001
+ ),
20002
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn2(
20003
+ "absolute bottom-0 left-0 right-0 z-[3]",
20004
+ "bg-gradient-to-t from-black/90 via-black/50 to-transparent",
20005
+ "px-4 pb-3 pt-10",
20006
+ "transition-opacity duration-300",
20007
+ "opacity-0 group-hover/player:opacity-100",
20008
+ isPlaying ? "" : "opacity-100"
20009
+ ), children: [
20010
+ /* @__PURE__ */ jsxRuntime.jsx(
20011
+ "div",
20012
+ {
20013
+ ref: progressRef,
20014
+ className: "w-full h-1.5 bg-white/20 rounded-full mb-3 cursor-pointer group/progress hover:h-2 transition-all",
20015
+ onClick: handleProgressClick,
20016
+ onMouseDown: () => setIsDraggingProgress(true),
20017
+ onMouseMove: handleProgressDrag,
20018
+ children: /* @__PURE__ */ jsxRuntime.jsx(
20019
+ "div",
20020
+ {
20021
+ className: "h-full bg-[#d8ff00] rounded-full relative",
20022
+ style: { width: `${progressPercent}%` },
20023
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-1/2 -translate-y-1/2 w-3.5 h-3.5 bg-[#d8ff00] rounded-full opacity-0 group-hover/progress:opacity-100 transition-opacity shadow-lg -mr-1.5" })
20024
+ }
20025
+ )
20026
+ }
20027
+ ),
20028
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
20029
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
20030
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: togglePlay, className: "p-1 text-white/90 hover:text-white transition-colors", children: isPlaying ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", children: [
20031
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1" }),
20032
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1" })
20033
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) }) }),
20034
+ /* @__PURE__ */ jsxRuntime.jsxs(
20035
+ "div",
20036
+ {
20037
+ className: "flex items-center gap-1.5 relative",
20038
+ onMouseEnter: () => setShowVolumeSlider(true),
20039
+ onMouseLeave: () => setShowVolumeSlider(false),
20040
+ children: [
20041
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: toggleMute, className: "p-1 text-white/80 hover:text-white transition-colors", children: isMuted || volume === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
20042
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
20043
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "9", x2: "17", y2: "15" }),
20044
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "17", y1: "9", x2: "23", y2: "15" })
20045
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
20046
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
20047
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" }),
20048
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" })
20049
+ ] }) }),
20050
+ showVolumeSlider && /* @__PURE__ */ jsxRuntime.jsx(
20051
+ "input",
20052
+ {
20053
+ type: "range",
20054
+ min: "0",
20055
+ max: "1",
20056
+ step: "0.05",
20057
+ value: isMuted ? 0 : volume,
20058
+ onChange: handleVolumeChange,
20059
+ className: "w-20 h-1 appearance-none bg-white/20 rounded-full cursor-pointer accent-[#d8ff00]"
20060
+ }
20061
+ )
20062
+ ]
20063
+ }
20064
+ ),
20065
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/60 font-mono tabular-nums", children: [
20066
+ formatTime2(currentTime),
20067
+ " / ",
20068
+ formatTime2(duration)
20069
+ ] })
20070
+ ] }),
20071
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
20072
+ "button",
20073
+ {
20074
+ onClick: handleDownload,
20075
+ className: "p-1.5 text-white/60 hover:text-white transition-colors",
20076
+ title: "\u4E0B\u8F7D\u89C6\u9891",
20077
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 16 })
20078
+ }
20079
+ ) })
20080
+ ] })
20081
+ ] })
20082
+ ] }),
20083
+ generationParams && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 border-t border-zinc-800/50 agent-sdk-light:border-zinc-200 bg-zinc-900/50 agent-sdk-light:bg-zinc-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3", children: [
20084
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
20085
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 12, className: "text-[#d8ff00]" }),
20086
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-zinc-300 agent-sdk-light:text-zinc-700", children: toolName || "\u751F\u6210\u53C2\u6570" })
20087
+ ] }),
20088
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-4 gap-y-1.5", children: Object.entries(generationParams).map(([key, value]) => {
20089
+ const displayValue = String(value);
20090
+ if (displayValue.length > 80) return null;
20091
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
20092
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-500 agent-sdk-light:text-zinc-500 capitalize", children: key.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase()) }),
20093
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-300 agent-sdk-light:text-zinc-700 font-mono", children: displayValue })
20094
+ ] }, key);
20095
+ }) }),
20096
+ generationParams.content && String(generationParams.content).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 pt-2 border-t border-zinc-800/30 agent-sdk-light:border-zinc-200", children: [
20097
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-500 agent-sdk-light:text-zinc-500 uppercase tracking-wider", children: "Prompt" }),
20098
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-zinc-300 agent-sdk-light:text-zinc-700 mt-1 leading-relaxed line-clamp-3", children: String(generationParams.content) })
20099
+ ] })
20100
+ ] }) })
20101
+ ] });
20102
+ });
20103
+ function htmlToMarkdown(element) {
20104
+ const lines = [];
20105
+ function processNode(node2, listDepth = 0) {
20106
+ if (node2.nodeType === Node.TEXT_NODE) {
20107
+ return node2.textContent || "";
20108
+ }
20109
+ if (node2.nodeType !== Node.ELEMENT_NODE) return "";
20110
+ const el = node2;
20111
+ const tag = el.tagName.toLowerCase();
20112
+ const childContent = () => {
20113
+ let result = "";
20114
+ el.childNodes.forEach((child) => {
20115
+ result += processNode(child, listDepth);
20116
+ });
20117
+ return result;
20118
+ };
20119
+ switch (tag) {
20120
+ case "h1":
20121
+ return `# ${childContent().trim()}
20122
+
20123
+ `;
20124
+ case "h2":
20125
+ return `## ${childContent().trim()}
20126
+
20127
+ `;
20128
+ case "h3":
20129
+ return `### ${childContent().trim()}
20130
+
20131
+ `;
20132
+ case "h4":
20133
+ return `#### ${childContent().trim()}
20134
+
20135
+ `;
20136
+ case "h5":
20137
+ return `##### ${childContent().trim()}
20138
+
20139
+ `;
20140
+ case "h6":
20141
+ return `###### ${childContent().trim()}
20142
+
20143
+ `;
20144
+ case "p": {
20145
+ const inner = childContent().trim();
20146
+ return inner ? `${inner}
20147
+
20148
+ ` : "\n";
20149
+ }
20150
+ case "br":
20151
+ return "\n";
20152
+ case "strong":
20153
+ case "b":
20154
+ return `**${childContent()}**`;
20155
+ case "em":
20156
+ case "i":
20157
+ return `*${childContent()}*`;
20158
+ case "del":
20159
+ case "s":
20160
+ return `~~${childContent()}~~`;
20161
+ case "code": {
20162
+ if (el.parentElement?.tagName.toLowerCase() === "pre") {
20163
+ return childContent();
20164
+ }
20165
+ return `\`${childContent()}\``;
20166
+ }
20167
+ case "pre": {
20168
+ const codeEl = el.querySelector("code");
20169
+ const codeContent = codeEl ? codeEl.textContent || "" : el.textContent || "";
20170
+ const langClass = codeEl?.className?.match(/language-(\w+)/);
20171
+ const lang = langClass ? langClass[1] : "";
20172
+ return `\`\`\`${lang}
20173
+ ${codeContent.trim()}
20174
+ \`\`\`
20175
+
20176
+ `;
20177
+ }
20178
+ case "blockquote": {
20179
+ const inner = childContent().trim();
20180
+ return inner.split("\n").map((line) => `> ${line}`).join("\n") + "\n\n";
20181
+ }
20182
+ case "ul": {
20183
+ let result = "";
20184
+ el.childNodes.forEach((child) => {
20185
+ if (child.tagName?.toLowerCase() === "li") {
20186
+ const indent2 = " ".repeat(listDepth);
20187
+ const liContent = processListItem(child, listDepth);
20188
+ result += `${indent2}- ${liContent}
20189
+ `;
20190
+ }
20191
+ });
20192
+ return listDepth === 0 ? result + "\n" : result;
20193
+ }
20194
+ case "ol": {
20195
+ let result = "";
20196
+ let index = 1;
20197
+ el.childNodes.forEach((child) => {
20198
+ if (child.tagName?.toLowerCase() === "li") {
20199
+ const indent2 = " ".repeat(listDepth);
20200
+ const liContent = processListItem(child, listDepth);
20201
+ result += `${indent2}${index}. ${liContent}
20202
+ `;
20203
+ index++;
20204
+ }
20205
+ });
20206
+ return listDepth === 0 ? result + "\n" : result;
20207
+ }
20208
+ case "li": {
20209
+ return childContent();
20210
+ }
20211
+ case "a": {
20212
+ const href = el.getAttribute("href") || "";
20213
+ const text3 = childContent();
20214
+ return `[${text3}](${href})`;
20215
+ }
20216
+ case "img": {
20217
+ const src = el.getAttribute("src") || "";
20218
+ const alt = el.getAttribute("alt") || "";
20219
+ return `![${alt}](${src})`;
20220
+ }
20221
+ case "hr":
20222
+ return "---\n\n";
20223
+ case "table": {
20224
+ return processTable(el) + "\n";
20225
+ }
20226
+ case "input": {
20227
+ if (el.getAttribute("type") === "checkbox") {
20228
+ return el.hasAttribute("checked") ? "[x] " : "[ ] ";
20229
+ }
20230
+ return "";
20231
+ }
20232
+ case "div":
20233
+ case "span":
20234
+ case "section":
20235
+ case "article":
20236
+ case "main":
20237
+ case "header":
20238
+ case "footer":
20239
+ return childContent();
20240
+ default:
20241
+ return childContent();
20242
+ }
20243
+ }
20244
+ function processListItem(li, depth, _ordered) {
20245
+ let textParts = [];
20246
+ let subLists = "";
20247
+ li.childNodes.forEach((child) => {
20248
+ const childTag = child.tagName?.toLowerCase();
20249
+ if (childTag === "ul" || childTag === "ol") {
20250
+ subLists += processNode(child, depth + 1);
20251
+ } else {
20252
+ textParts.push(processNode(child, depth));
20253
+ }
20254
+ });
20255
+ let result = textParts.join("").trim();
20256
+ if (subLists) {
20257
+ result += "\n" + subLists;
20258
+ }
20259
+ return result;
20260
+ }
20261
+ function processTable(table) {
20262
+ const rows = [];
20263
+ table.querySelectorAll("tr").forEach((tr) => {
20264
+ const cells = [];
20265
+ tr.querySelectorAll("th, td").forEach((cell) => {
20266
+ cells.push((cell.textContent || "").trim());
20267
+ });
20268
+ rows.push(cells);
20269
+ });
20270
+ if (rows.length === 0) return "";
20271
+ const colCount = Math.max(...rows.map((r3) => r3.length));
20272
+ const colWidths = Array(colCount).fill(3);
20273
+ rows.forEach((row) => {
20274
+ row.forEach((cell, i) => {
20275
+ colWidths[i] = Math.max(colWidths[i], cell.length);
20276
+ });
20277
+ });
20278
+ let result = "";
20279
+ rows.forEach((row, rowIndex) => {
20280
+ const paddedCells = Array(colCount).fill("").map((_, i) => {
20281
+ const cell = row[i] || "";
20282
+ return cell.padEnd(colWidths[i]);
20283
+ });
20284
+ result += `| ${paddedCells.join(" | ")} |
20285
+ `;
20286
+ if (rowIndex === 0) {
20287
+ const separators = colWidths.map((w) => "-".repeat(w));
20288
+ result += `| ${separators.join(" | ")} |
20289
+ `;
20290
+ }
20291
+ });
20292
+ return result;
20293
+ }
20294
+ element.childNodes.forEach((child) => {
20295
+ lines.push(processNode(child));
20296
+ });
20297
+ return lines.join("").replace(/\n{3,}/g, "\n\n").trim() + "\n";
20298
+ }
20299
+ var MarkdownEditor = React20.memo(function MarkdownEditor2({
20300
+ content,
20301
+ onChange,
20302
+ config
20303
+ }) {
20304
+ const editorRef = React20.useRef(null);
20305
+ const textareaRef = React20.useRef(null);
20306
+ const [isSourceMode, setIsSourceMode] = React20.useState(false);
20307
+ const isInternalUpdate = React20.useRef(false);
20308
+ const lastMarkdownRef = React20.useRef(content);
20309
+ const contentToHtml = React20.useCallback((markdown) => {
20310
+ return marked.marked.parse(markdown, { breaks: true, gfm: true });
20311
+ }, []);
20312
+ React20.useEffect(() => {
20313
+ if (isSourceMode) return;
20314
+ if (isInternalUpdate.current) {
20315
+ isInternalUpdate.current = false;
20316
+ return;
20317
+ }
20318
+ if (editorRef.current && content !== lastMarkdownRef.current) {
20319
+ const html2 = contentToHtml(content);
20320
+ editorRef.current.innerHTML = html2;
20321
+ lastMarkdownRef.current = content;
20322
+ }
20323
+ }, [content, isSourceMode, contentToHtml]);
20324
+ React20.useEffect(() => {
20325
+ if (editorRef.current && !isSourceMode) {
20326
+ const html2 = contentToHtml(content);
20327
+ editorRef.current.innerHTML = html2;
20328
+ }
20329
+ }, []);
20330
+ const handleInput = React20.useCallback(() => {
20331
+ if (!editorRef.current) return;
20332
+ const markdown = htmlToMarkdown(editorRef.current);
20333
+ lastMarkdownRef.current = markdown;
20334
+ isInternalUpdate.current = true;
20335
+ onChange(markdown);
20336
+ }, [onChange]);
20337
+ const execCommand = React20.useCallback((command, value) => {
20338
+ editorRef.current?.focus();
20339
+ document.execCommand(command, false, value);
20340
+ requestAnimationFrame(() => handleInput());
20341
+ }, [handleInput]);
20342
+ const formatBlock = React20.useCallback((tag) => {
20343
+ execCommand("formatBlock", tag);
20344
+ }, [execCommand]);
20345
+ const insertHorizontalRule = React20.useCallback(() => {
20346
+ execCommand("insertHorizontalRule");
20347
+ }, [execCommand]);
20348
+ const insertLink = React20.useCallback(() => {
20349
+ const selection = window.getSelection();
20350
+ const selectedText = selection?.toString() || "\u94FE\u63A5\u6587\u672C";
20351
+ const url = prompt("\u8BF7\u8F93\u5165\u94FE\u63A5\u5730\u5740:", "https://");
20352
+ if (url) {
20353
+ if (selection && selection.rangeCount > 0) {
20354
+ const range = selection.getRangeAt(0);
20355
+ range.deleteContents();
20356
+ const anchor = document.createElement("a");
20357
+ anchor.href = url;
20358
+ anchor.textContent = selectedText;
20359
+ range.insertNode(anchor);
20360
+ range.setStartAfter(anchor);
20361
+ range.collapse(true);
20362
+ selection.removeAllRanges();
20363
+ selection.addRange(range);
20364
+ }
20365
+ requestAnimationFrame(() => handleInput());
20366
+ }
20367
+ }, [handleInput]);
20368
+ const insertImage = React20.useCallback(() => {
20369
+ const url = prompt("\u8BF7\u8F93\u5165\u56FE\u7247\u5730\u5740:", "https://");
20370
+ if (url) {
20371
+ execCommand("insertImage", url);
20372
+ }
20373
+ }, [execCommand]);
20374
+ const insertCodeBlock = React20.useCallback(() => {
20375
+ const editor = editorRef.current;
20376
+ if (!editor) return;
20377
+ const selection = window.getSelection();
20378
+ if (selection && selection.rangeCount > 0) {
20379
+ const range = selection.getRangeAt(0);
20380
+ const pre = document.createElement("pre");
20381
+ const code3 = document.createElement("code");
20382
+ code3.textContent = selection.toString() || "\u4EE3\u7801\u5185\u5BB9";
20383
+ pre.appendChild(code3);
20384
+ range.deleteContents();
20385
+ range.insertNode(pre);
20386
+ const paragraph2 = document.createElement("p");
20387
+ paragraph2.innerHTML = "<br>";
20388
+ pre.parentNode?.insertBefore(paragraph2, pre.nextSibling);
20389
+ range.setStart(paragraph2, 0);
20390
+ range.collapse(true);
20391
+ selection.removeAllRanges();
20392
+ selection.addRange(range);
20393
+ }
20394
+ requestAnimationFrame(() => handleInput());
20395
+ }, [handleInput]);
20396
+ const handleKeyDown = React20.useCallback((e) => {
20397
+ const isMod = e.metaKey || e.ctrlKey;
20398
+ if (isMod && e.key === "b") {
20399
+ e.preventDefault();
20400
+ execCommand("bold");
20401
+ } else if (isMod && e.key === "i") {
20402
+ e.preventDefault();
20403
+ execCommand("italic");
20404
+ } else if (isMod && e.key === "u") {
20405
+ e.preventDefault();
20406
+ execCommand("underline");
20407
+ } else if (isMod && e.key === "e") {
20408
+ e.preventDefault();
20409
+ const selection = window.getSelection();
20410
+ if (selection && selection.rangeCount > 0) {
20411
+ const selectedText = selection.toString() || "code";
20412
+ execCommand("insertHTML", `<code>${selectedText}</code>`);
20413
+ }
20414
+ } else if (e.key === "Tab") {
20415
+ e.preventDefault();
20416
+ execCommand("insertHTML", "&nbsp;&nbsp;&nbsp;&nbsp;");
20417
+ }
20418
+ }, [execCommand]);
20419
+ const handleSourceKeyDown = React20.useCallback((e) => {
20420
+ if (e.key === "Tab") {
20421
+ e.preventDefault();
20422
+ const textarea = e.currentTarget;
20423
+ const start = textarea.selectionStart;
20424
+ const end = textarea.selectionEnd;
20425
+ const newValue = content.substring(0, start) + " " + content.substring(end);
20426
+ onChange(newValue);
20427
+ requestAnimationFrame(() => {
20428
+ textarea.selectionStart = textarea.selectionEnd = start + 2;
20429
+ });
20430
+ }
20431
+ }, [content, onChange]);
20432
+ const toggleSourceMode = React20.useCallback(() => {
20433
+ if (isSourceMode) {
20434
+ setIsSourceMode(false);
20435
+ requestAnimationFrame(() => {
20436
+ if (editorRef.current) {
20437
+ const html2 = contentToHtml(content);
20438
+ editorRef.current.innerHTML = html2;
20439
+ lastMarkdownRef.current = content;
20440
+ }
20441
+ });
20442
+ } else {
20443
+ if (editorRef.current) {
20444
+ const markdown = htmlToMarkdown(editorRef.current);
20445
+ lastMarkdownRef.current = markdown;
20446
+ isInternalUpdate.current = true;
20447
+ onChange(markdown);
20448
+ }
20449
+ setIsSourceMode(true);
20450
+ }
20451
+ }, [isSourceMode, content, contentToHtml, onChange]);
20452
+ const toolbarGroups = React20.useMemo(() => [
20453
+ {
20454
+ items: [
20455
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-[11px]", children: "B" }), label: "\u52A0\u7C97 (\u2318B)", onClick: () => execCommand("bold") },
20456
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic text-[11px]", children: "I" }), label: "\u659C\u4F53 (\u2318I)", onClick: () => execCommand("italic") },
20457
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-through text-[11px]", children: "S" }), label: "\u5220\u9664\u7EBF", onClick: () => execCommand("strikeThrough") },
20458
+ { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 13 }), label: "\u884C\u5185\u4EE3\u7801 (\u2318E)", onClick: () => {
20459
+ const selection = window.getSelection();
20460
+ const text3 = selection?.toString() || "code";
20461
+ execCommand("insertHTML", `<code>${text3}</code>`);
20462
+ } }
20463
+ ]
20464
+ },
20465
+ {
20466
+ items: [
20467
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold", children: "H1" }), label: "\u4E00\u7EA7\u6807\u9898", onClick: () => formatBlock("h1") },
20468
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold", children: "H2" }), label: "\u4E8C\u7EA7\u6807\u9898", onClick: () => formatBlock("h2") },
20469
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold", children: "H3" }), label: "\u4E09\u7EA7\u6807\u9898", onClick: () => formatBlock("h3") }
20470
+ ]
20471
+ },
20472
+ {
20473
+ items: [
20474
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: "\u2022" }), label: "\u65E0\u5E8F\u5217\u8868", onClick: () => execCommand("insertUnorderedList") },
20475
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: "1." }), label: "\u6709\u5E8F\u5217\u8868", onClick: () => execCommand("insertOrderedList") },
20476
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: "\u275D" }), label: "\u5F15\u7528", onClick: () => formatBlock("blockquote") }
20477
+ ]
20478
+ },
20479
+ {
20480
+ items: [
20481
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: "\u2014" }), label: "\u5206\u5272\u7EBF", onClick: insertHorizontalRule },
20482
+ { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { size: 13 }), label: "\u94FE\u63A5", onClick: insertLink },
20483
+ { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileImage, { size: 13 }), label: "\u56FE\u7247", onClick: insertImage },
20484
+ { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 13, className: "opacity-60" }), label: "\u4EE3\u7801\u5757", onClick: insertCodeBlock }
20485
+ ]
20486
+ }
20487
+ ], [execCommand, formatBlock, insertHorizontalRule, insertLink, insertImage, insertCodeBlock]);
20488
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-950 agent-sdk-light:bg-white", children: [
20489
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 px-3 py-1.5 border-b border-zinc-800/50 agent-sdk-light:border-zinc-200 bg-zinc-900/50 agent-sdk-light:bg-zinc-50 flex-shrink-0 overflow-x-auto", style: { scrollbarWidth: "none" }, children: [
20490
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 mr-2", children: [
20491
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { size: 12, className: "text-[#d8ff00]" }),
20492
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-[#d8ff00] font-medium whitespace-nowrap", children: isSourceMode ? "Markdown \u6E90\u7801" : "\u5BCC\u6587\u672C\u7F16\u8F91" })
20493
+ ] }),
20494
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-zinc-700/50 agent-sdk-light:bg-zinc-300 mx-1" }),
20495
+ !isSourceMode && toolbarGroups.map((group, groupIndex) => /* @__PURE__ */ jsxRuntime.jsxs(React20__namespace.default.Fragment, { children: [
20496
+ group.items.map((action, actionIndex) => /* @__PURE__ */ jsxRuntime.jsx(
20497
+ "button",
20498
+ {
20499
+ onClick: action.onClick,
20500
+ className: "p-1.5 min-w-[28px] h-7 flex items-center justify-center text-zinc-400 agent-sdk-light:text-zinc-600 hover:text-zinc-100 agent-sdk-light:hover:text-zinc-900 hover:bg-zinc-700/50 agent-sdk-light:hover:bg-zinc-200 rounded transition-colors",
20501
+ title: action.label,
20502
+ children: action.icon
20503
+ },
20504
+ actionIndex
20505
+ )),
20506
+ groupIndex < toolbarGroups.length - 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-zinc-700/50 agent-sdk-light:bg-zinc-300 mx-0.5" })
20507
+ ] }, groupIndex)),
20508
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-zinc-700/50 agent-sdk-light:bg-zinc-300 mx-1 ml-auto" }),
20509
+ /* @__PURE__ */ jsxRuntime.jsxs(
20510
+ "button",
20511
+ {
20512
+ onClick: toggleSourceMode,
20513
+ className: cn2(
20514
+ "p-1.5 flex items-center gap-1 text-[11px] rounded transition-colors whitespace-nowrap",
20515
+ isSourceMode ? "bg-[#d8ff00]/10 text-[#d8ff00]" : "text-zinc-400 agent-sdk-light:text-zinc-600 hover:text-zinc-100 agent-sdk-light:hover:text-zinc-900 hover:bg-zinc-700/50 agent-sdk-light:hover:bg-zinc-200"
20516
+ ),
20517
+ title: isSourceMode ? "\u5207\u6362\u5230\u5BCC\u6587\u672C\u6A21\u5F0F" : "\u5207\u6362\u5230\u6E90\u7801\u6A21\u5F0F",
20518
+ children: [
20519
+ isSourceMode ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 13 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 13 }),
20520
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: isSourceMode ? "\u5BCC\u6587\u672C" : "\u6E90\u7801" })
20521
+ ]
20522
+ }
20523
+ )
20524
+ ] }),
20525
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: isSourceMode ? (
20526
+ /* 源码编辑模式 */
20527
+ /* @__PURE__ */ jsxRuntime.jsx(
20528
+ "textarea",
20529
+ {
20530
+ ref: textareaRef,
20531
+ value: content,
20532
+ onChange: (e) => onChange(e.target.value),
20533
+ onKeyDown: handleSourceKeyDown,
20534
+ className: "w-full h-full p-5 bg-zinc-950 agent-sdk-light:bg-white text-sm text-zinc-300 agent-sdk-light:text-zinc-800 leading-[1.8] resize-none focus:outline-none",
20535
+ style: { fontFamily: '"SF Mono", "Fira Code", "JetBrains Mono", Menlo, Monaco, monospace' },
20536
+ spellCheck: false,
20537
+ autoComplete: "off",
20538
+ autoCorrect: "off",
20539
+ autoCapitalize: "off",
20540
+ placeholder: "\u7F16\u5199 Markdown \u6E90\u7801..."
20541
+ }
20542
+ )
20543
+ ) : (
20544
+ /* WYSIWYG 富文本编辑模式 */
20545
+ /* @__PURE__ */ jsxRuntime.jsx(
20546
+ "div",
20547
+ {
20548
+ ref: editorRef,
20549
+ contentEditable: true,
20550
+ suppressContentEditableWarning: true,
20551
+ onInput: handleInput,
20552
+ onKeyDown: handleKeyDown,
20553
+ className: "w-full h-full p-5 overflow-auto bg-zinc-950 agent-sdk-light:bg-white text-sm text-zinc-300 agent-sdk-light:text-zinc-800 leading-[1.8] focus:outline-none prose prose-invert prose-zinc agent-sdk-light:prose prose-sm max-w-none\n [&_h1]:text-2xl [&_h1]:font-bold [&_h1]:text-zinc-100 [&_h1]:agent-sdk-light:text-zinc-900 [&_h1]:mb-4 [&_h1]:mt-6 [&_h1]:border-b [&_h1]:border-zinc-800/30 [&_h1]:agent-sdk-light:border-zinc-200 [&_h1]:pb-2\n [&_h2]:text-xl [&_h2]:font-bold [&_h2]:text-zinc-200 [&_h2]:agent-sdk-light:text-zinc-800 [&_h2]:mb-3 [&_h2]:mt-5\n [&_h3]:text-lg [&_h3]:font-semibold [&_h3]:text-zinc-200 [&_h3]:agent-sdk-light:text-zinc-800 [&_h3]:mb-2 [&_h3]:mt-4\n [&_p]:mb-3 [&_p]:leading-relaxed\n [&_strong]:text-zinc-100 [&_strong]:agent-sdk-light:text-zinc-900 [&_strong]:font-semibold\n [&_em]:text-zinc-300 [&_em]:agent-sdk-light:text-zinc-700\n [&_code]:bg-zinc-800/60 [&_code]:agent-sdk-light:bg-zinc-100 [&_code]:text-[#d8ff00] [&_code]:agent-sdk-light:text-pink-600 [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-[13px] [&_code]:font-mono\n [&_pre]:bg-zinc-900/80 [&_pre]:agent-sdk-light:bg-zinc-50 [&_pre]:border [&_pre]:border-zinc-800/50 [&_pre]:agent-sdk-light:border-zinc-200 [&_pre]:rounded-lg [&_pre]:p-4 [&_pre]:my-3 [&_pre]:overflow-x-auto\n [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:text-zinc-300 [&_pre_code]:agent-sdk-light:text-zinc-800\n [&_blockquote]:border-l-3 [&_blockquote]:border-[#d8ff00]/40 [&_blockquote]:pl-4 [&_blockquote]:my-3 [&_blockquote]:text-zinc-400 [&_blockquote]:agent-sdk-light:text-zinc-600 [&_blockquote]:italic\n [&_ul]:list-disc [&_ul]:pl-6 [&_ul]:my-2\n [&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:my-2\n [&_li]:mb-1\n [&_a]:text-[#d8ff00] [&_a]:agent-sdk-light:text-blue-600 [&_a]:underline [&_a]:underline-offset-2\n [&_hr]:border-zinc-700/50 [&_hr]:agent-sdk-light:border-zinc-300 [&_hr]:my-6\n [&_img]:max-w-full [&_img]:rounded-lg [&_img]:my-3\n [&_table]:w-full [&_table]:border-collapse [&_table]:my-3\n [&_th]:border [&_th]:border-zinc-700/50 [&_th]:agent-sdk-light:border-zinc-300 [&_th]:px-3 [&_th]:py-2 [&_th]:bg-zinc-800/30 [&_th]:agent-sdk-light:bg-zinc-100 [&_th]:text-left [&_th]:font-medium\n [&_td]:border [&_td]:border-zinc-700/50 [&_td]:agent-sdk-light:border-zinc-300 [&_td]:px-3 [&_td]:py-2",
20554
+ "data-placeholder": "\u5F00\u59CB\u7F16\u5199\u5185\u5BB9..."
20555
+ }
20556
+ )
20557
+ ) })
20558
+ ] });
20559
+ });
20560
+ var ArtifactThumbnail = React20.memo(function ArtifactThumbnail2({
20561
+ artifact,
20562
+ config
20563
+ }) {
20564
+ const typeConf = artifactTypeConfig[artifact.type] || artifactTypeConfig.text;
20565
+ const TypeIcon = typeConf.icon;
20566
+ const isMedia = artifact.type === "image" || artifact.type === "video";
20567
+ const thumbnailUrl = useResolvedThumbnailUrl(
20568
+ artifact.currentContent,
20569
+ {
20570
+ fileId: artifact.metadata?.fileId,
20571
+ type: artifact.type,
20572
+ config,
20573
+ enabled: isMedia
20574
+ }
20575
+ );
20576
+ if (isMedia && thumbnailUrl) {
20577
+ return /* @__PURE__ */ jsxRuntime.jsx(
20578
+ "img",
20579
+ {
20580
+ src: thumbnailUrl,
20581
+ alt: "",
20582
+ className: "w-6 h-6 rounded object-cover flex-shrink-0",
20583
+ loading: "lazy"
20584
+ }
20585
+ );
20586
+ }
20587
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn2("w-6 h-6 rounded flex items-center justify-center flex-shrink-0", typeConf.bgColor), children: /* @__PURE__ */ jsxRuntime.jsx(TypeIcon, { size: 12, className: typeConf.color }) });
20588
+ });
20589
+ var ArtifactTabs = React20.memo(function ArtifactTabs2({
20590
+ artifacts,
20591
+ activeId,
20592
+ onSwitch,
20593
+ onClose,
20594
+ onReorder,
20595
+ config
20596
+ }) {
20597
+ const scrollContainerRef = React20.useRef(null);
20598
+ const [canScrollLeft, setCanScrollLeft] = React20.useState(false);
20599
+ const [canScrollRight, setCanScrollRight] = React20.useState(false);
20600
+ const [showAllPanel, setShowAllPanel] = React20.useState(false);
20601
+ const allPanelRef = React20.useRef(null);
20602
+ const [dragState, setDragState] = React20.useState({ draggingId: null, dragOverId: null, dragStartX: 0, isDragging: false });
20603
+ const checkScrollState = React20.useCallback(() => {
20604
+ const container = scrollContainerRef.current;
20605
+ if (!container) return;
20606
+ setCanScrollLeft(container.scrollLeft > 2);
20607
+ setCanScrollRight(container.scrollLeft < container.scrollWidth - container.clientWidth - 2);
20608
+ }, []);
20609
+ React20.useEffect(() => {
20610
+ checkScrollState();
20611
+ const container = scrollContainerRef.current;
20612
+ if (!container) return;
20613
+ container.addEventListener("scroll", checkScrollState, { passive: true });
20614
+ const resizeObserver = new ResizeObserver(checkScrollState);
20615
+ resizeObserver.observe(container);
20616
+ return () => {
20617
+ container.removeEventListener("scroll", checkScrollState);
20618
+ resizeObserver.disconnect();
20619
+ };
20620
+ }, [checkScrollState, artifacts.length]);
20621
+ React20.useEffect(() => {
20622
+ if (!showAllPanel) return;
20623
+ const handleClickOutside = (e) => {
20624
+ if (allPanelRef.current && !allPanelRef.current.contains(e.target)) {
20625
+ setShowAllPanel(false);
20626
+ }
20627
+ };
20628
+ document.addEventListener("mousedown", handleClickOutside);
20629
+ return () => document.removeEventListener("mousedown", handleClickOutside);
20630
+ }, [showAllPanel]);
20631
+ const scrollBy = React20.useCallback((direction) => {
20632
+ const container = scrollContainerRef.current;
20633
+ if (!container) return;
20634
+ const scrollAmount = 200;
20635
+ container.scrollBy({
20636
+ left: direction === "left" ? -scrollAmount : scrollAmount,
20637
+ behavior: "smooth"
20638
+ });
20639
+ }, []);
20640
+ const handleDragStart = React20.useCallback((e, artifactId) => {
20641
+ e.dataTransfer.effectAllowed = "move";
20642
+ e.dataTransfer.setData("text/plain", artifactId);
20643
+ setDragState((prev) => ({ ...prev, draggingId: artifactId, dragStartX: e.clientX, isDragging: true }));
20644
+ }, []);
20645
+ const handleDragOver = React20.useCallback((e, artifactId) => {
20646
+ e.preventDefault();
20647
+ e.dataTransfer.dropEffect = "move";
20648
+ setDragState((prev) => ({ ...prev, dragOverId: artifactId }));
20649
+ }, []);
20650
+ const handleDragEnd = React20.useCallback(() => {
20651
+ setDragState({ draggingId: null, dragOverId: null, dragStartX: 0, isDragging: false });
20652
+ }, []);
20653
+ const handleDrop = React20.useCallback((e, targetId) => {
20654
+ e.preventDefault();
20655
+ const sourceId = e.dataTransfer.getData("text/plain");
20656
+ if (!sourceId || sourceId === targetId || !onReorder) {
20657
+ handleDragEnd();
20658
+ return;
20659
+ }
20660
+ const fromIndex = artifacts.findIndex((a) => a.id === sourceId);
20661
+ const toIndex = artifacts.findIndex((a) => a.id === targetId);
20662
+ if (fromIndex !== -1 && toIndex !== -1 && fromIndex !== toIndex) {
20663
+ onReorder(fromIndex, toIndex);
20664
+ }
20665
+ handleDragEnd();
20666
+ }, [artifacts, onReorder, handleDragEnd]);
20667
+ const handleCloseTab = React20.useCallback((e, artifactId) => {
20668
+ e.stopPropagation();
20669
+ if (!onClose) return;
20670
+ if (artifactId === activeId && artifacts.length > 1) {
20671
+ const currentIndex = artifacts.findIndex((a) => a.id === artifactId);
20672
+ const nextIndex = currentIndex < artifacts.length - 1 ? currentIndex + 1 : currentIndex - 1;
20673
+ if (nextIndex >= 0 && nextIndex < artifacts.length) {
20674
+ onSwitch(artifacts[nextIndex].id);
20675
+ }
20676
+ }
20677
+ onClose(artifactId);
20678
+ }, [onClose, activeId, artifacts, onSwitch]);
20679
+ if (artifacts.length <= 1) return null;
20680
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center border-b border-zinc-800/50 agent-sdk-light:border-zinc-200 bg-zinc-950/50 agent-sdk-light:bg-zinc-100 flex-shrink-0 relative", children: [
20681
+ canScrollLeft && /* @__PURE__ */ jsxRuntime.jsx(
20682
+ "button",
20683
+ {
20684
+ onClick: () => scrollBy("left"),
20685
+ className: "flex-shrink-0 p-1 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800/50 transition-colors z-10",
20686
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 14 })
20687
+ }
20688
+ ),
20689
+ /* @__PURE__ */ jsxRuntime.jsx(
20690
+ "div",
20691
+ {
20692
+ ref: scrollContainerRef,
20693
+ className: "flex items-center gap-0.5 px-1 py-1 overflow-x-auto flex-1 min-w-0",
20694
+ style: { scrollbarWidth: "none", msOverflowStyle: "none" },
20695
+ children: artifacts.map((artifact) => {
20696
+ const typeConf = artifactTypeConfig[artifact.type] || artifactTypeConfig.text;
20697
+ const isActive = artifact.id === activeId;
20698
+ const isDragging = dragState.draggingId === artifact.id;
20699
+ const isDragOver = dragState.dragOverId === artifact.id && dragState.draggingId !== artifact.id;
20700
+ const TypeIcon = typeConf.icon;
20701
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20702
+ "div",
20703
+ {
20704
+ draggable: !!onReorder,
20705
+ onDragStart: (e) => handleDragStart(e, artifact.id),
20706
+ onDragOver: (e) => handleDragOver(e, artifact.id),
20707
+ onDragEnd: handleDragEnd,
20708
+ onDrop: (e) => handleDrop(e, artifact.id),
20709
+ onClick: () => onSwitch(artifact.id),
20710
+ className: cn2(
20711
+ "group/tab flex items-center gap-1 pl-2.5 pr-1 py-1 text-xs rounded-md transition-all whitespace-nowrap flex-shrink-0 cursor-pointer select-none",
20712
+ isActive ? "bg-zinc-800 text-zinc-100 agent-sdk-light:bg-zinc-200 agent-sdk-light:text-zinc-900" : "text-zinc-500 agent-sdk-light:text-zinc-600 hover:text-zinc-300 agent-sdk-light:hover:text-zinc-900 hover:bg-zinc-800/50 agent-sdk-light:hover:bg-zinc-200/50",
20713
+ isDragging && "opacity-40",
20714
+ isDragOver && "ring-1 ring-[#d8ff00]/50",
20715
+ onReorder && "cursor-grab active:cursor-grabbing"
20716
+ ),
20717
+ title: artifact.title,
20718
+ children: [
20719
+ /* @__PURE__ */ jsxRuntime.jsx(TypeIcon, { size: 12, className: cn2("flex-shrink-0", isActive ? typeConf.color : "") }),
20720
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[120px] truncate", children: artifact.title }),
20721
+ onClose && /* @__PURE__ */ jsxRuntime.jsx(
20722
+ "button",
20723
+ {
20724
+ onClick: (e) => handleCloseTab(e, artifact.id),
20725
+ className: cn2(
20726
+ "ml-0.5 p-0.5 rounded transition-colors flex-shrink-0",
20727
+ isActive ? "text-zinc-400 hover:text-zinc-100 hover:bg-zinc-700" : "text-transparent group-hover/tab:text-zinc-500 hover:!text-zinc-300 hover:!bg-zinc-700"
20728
+ ),
20729
+ title: "\u5173\u95ED",
20730
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 10 })
20731
+ }
20732
+ )
20733
+ ]
20734
+ },
20735
+ artifact.id
20736
+ );
20737
+ })
20738
+ }
20739
+ ),
20740
+ canScrollRight && /* @__PURE__ */ jsxRuntime.jsx(
20741
+ "button",
20742
+ {
20743
+ onClick: () => scrollBy("right"),
20744
+ className: "flex-shrink-0 p-1 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800/50 transition-colors z-10",
20745
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 14 })
20746
+ }
20747
+ ),
20748
+ artifacts.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-shrink-0", ref: allPanelRef, children: [
20749
+ /* @__PURE__ */ jsxRuntime.jsx(
20750
+ "button",
20751
+ {
20752
+ onClick: () => setShowAllPanel((prev) => !prev),
20753
+ className: cn2(
20754
+ "p-1.5 mr-1 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800/50 rounded transition-colors",
20755
+ showAllPanel && "bg-zinc-800 text-zinc-300"
20756
+ ),
20757
+ title: "\u5168\u90E8\u4EA7\u7269",
20758
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGrid, { size: 14 })
20759
+ }
20760
+ ),
20761
+ showAllPanel && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-full mt-1 z-50 w-72 max-h-80 overflow-y-auto bg-zinc-900 agent-sdk-light:bg-white border border-zinc-700 agent-sdk-light:border-zinc-300 rounded-lg shadow-xl", children: [
20762
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 border-b border-zinc-800 agent-sdk-light:border-zinc-200", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-zinc-400 agent-sdk-light:text-zinc-600", children: [
20763
+ "\u5168\u90E8\u4EA7\u7269 (",
20764
+ artifacts.length,
20765
+ ")"
20766
+ ] }) }),
20767
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-1.5 grid grid-cols-1 gap-0.5", children: artifacts.map((artifact) => {
20768
+ const typeConf = artifactTypeConfig[artifact.type] || artifactTypeConfig.text;
20769
+ const isActive = artifact.id === activeId;
20770
+ typeConf.icon;
20771
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20772
+ "button",
20773
+ {
20774
+ onClick: () => {
20775
+ onSwitch(artifact.id);
20776
+ setShowAllPanel(false);
20777
+ },
20778
+ className: cn2(
20779
+ "flex items-center gap-2.5 px-2.5 py-2 text-xs rounded-md transition-all w-full text-left",
20780
+ isActive ? "bg-zinc-800 text-zinc-100 agent-sdk-light:bg-zinc-100 agent-sdk-light:text-zinc-900" : "text-zinc-400 agent-sdk-light:text-zinc-600 hover:text-zinc-200 agent-sdk-light:hover:text-zinc-900 hover:bg-zinc-800/50 agent-sdk-light:hover:bg-zinc-100"
20781
+ ),
20782
+ children: [
20783
+ /* @__PURE__ */ jsxRuntime.jsx(ArtifactThumbnail, { artifact, config }),
20784
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
20785
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate font-medium", children: artifact.title }),
20786
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[10px] text-zinc-600 agent-sdk-light:text-zinc-500 mt-0.5", children: [
20787
+ typeConf.label,
20788
+ artifact.language && artifact.language !== artifact.type && ` \u2022 ${artifact.language}`
20789
+ ] })
20790
+ ] }),
20791
+ onClose && /* @__PURE__ */ jsxRuntime.jsx(
20792
+ "button",
20793
+ {
20794
+ onClick: (e) => {
20795
+ e.stopPropagation();
20796
+ handleCloseTab(e, artifact.id);
20797
+ },
20798
+ className: "p-1 text-zinc-600 hover:text-zinc-300 hover:bg-zinc-700 rounded transition-colors flex-shrink-0",
20799
+ title: "\u5173\u95ED",
20800
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 12 })
20801
+ }
20802
+ )
20803
+ ]
20804
+ },
20805
+ artifact.id
20806
+ );
20807
+ }) })
20808
+ ] })
20809
+ ] })
20810
+ ] });
20811
+ });
18821
20812
  var ArtifactViewer = React20.memo(function ArtifactViewer2({
18822
20813
  artifact,
18823
20814
  isOpen,
@@ -18826,41 +20817,127 @@ var ArtifactViewer = React20.memo(function ArtifactViewer2({
18826
20817
  defaultView = "preview",
18827
20818
  isFullscreen,
18828
20819
  onFullscreenToggle,
18829
- embedded = false
20820
+ embedded = false,
20821
+ artifacts = [],
20822
+ activeArtifactId,
20823
+ onSwitchArtifact,
20824
+ onCloseArtifact,
20825
+ onReorderArtifacts,
20826
+ onContentChange,
20827
+ onSave
18830
20828
  }) {
18831
20829
  const [viewMode, setViewMode] = React20.useState(defaultView);
20830
+ const [isEditing, setIsEditing] = React20.useState(false);
20831
+ const [editContent, setEditContent] = React20.useState("");
20832
+ const [undoStack, setUndoStack] = React20.useState([]);
20833
+ const [redoStack, setRedoStack] = React20.useState([]);
18832
20834
  React20.useEffect(() => {
18833
20835
  if (artifact) {
18834
- if (artifact.type === "html" || artifact.type === "markdown") {
20836
+ if (artifact.type === "html" || artifact.type === "markdown" || artifact.type === "svg") {
18835
20837
  setViewMode("preview");
18836
20838
  } else {
18837
20839
  setViewMode("code");
18838
20840
  }
20841
+ setIsEditing(false);
20842
+ setEditContent(artifact.content);
20843
+ setUndoStack([]);
20844
+ setRedoStack([]);
18839
20845
  }
18840
20846
  }, [artifact?.id]);
20847
+ React20.useEffect(() => {
20848
+ if (artifact && !isEditing) {
20849
+ setEditContent(artifact.content);
20850
+ }
20851
+ }, [artifact?.content, isEditing]);
18841
20852
  if (!artifact) return null;
18842
- const typeConfig = artifactTypeConfig[artifact.type];
18843
- const canPreview = artifact.type === "html" || artifact.type === "markdown";
20853
+ const typeConfig = artifactTypeConfig[artifact.type] || artifactTypeConfig.text;
20854
+ const canPreview = artifact.type === "html" || artifact.type === "markdown" || artifact.type === "svg";
20855
+ const canEdit = artifact.type !== "image" && artifact.type !== "video";
20856
+ const handleStartEdit = () => {
20857
+ setEditContent(artifact.content);
20858
+ setUndoStack([artifact.content]);
20859
+ setRedoStack([]);
20860
+ setIsEditing(true);
20861
+ setViewMode("code");
20862
+ };
20863
+ const handleEditChange = (newContent) => {
20864
+ setUndoStack((prev) => [...prev, editContent]);
20865
+ setRedoStack([]);
20866
+ setEditContent(newContent);
20867
+ onContentChange?.(newContent);
20868
+ };
20869
+ const handleUndo = () => {
20870
+ if (undoStack.length === 0) return;
20871
+ const previous2 = undoStack[undoStack.length - 1];
20872
+ setUndoStack((prev) => prev.slice(0, -1));
20873
+ setRedoStack((prev) => [...prev, editContent]);
20874
+ setEditContent(previous2);
20875
+ onContentChange?.(previous2);
20876
+ };
20877
+ const handleRedo = () => {
20878
+ if (redoStack.length === 0) return;
20879
+ const next = redoStack[redoStack.length - 1];
20880
+ setRedoStack((prev) => prev.slice(0, -1));
20881
+ setUndoStack((prev) => [...prev, editContent]);
20882
+ setEditContent(next);
20883
+ onContentChange?.(next);
20884
+ };
20885
+ const handleSave = () => {
20886
+ onSave?.(artifact.id, editContent);
20887
+ setIsEditing(false);
20888
+ };
20889
+ const handleCancelEdit = () => {
20890
+ setEditContent(artifact.content);
20891
+ setIsEditing(false);
20892
+ };
20893
+ const displayContent = isEditing ? editContent : artifact.content;
18844
20894
  const renderContent = () => {
20895
+ if (isEditing) {
20896
+ if (artifact.type === "markdown") {
20897
+ return /* @__PURE__ */ jsxRuntime.jsx(
20898
+ MarkdownEditor,
20899
+ {
20900
+ content: editContent,
20901
+ onChange: handleEditChange,
20902
+ config
20903
+ }
20904
+ );
20905
+ }
20906
+ return /* @__PURE__ */ jsxRuntime.jsx(
20907
+ CodeEditor,
20908
+ {
20909
+ code: editContent,
20910
+ language: artifact.language || artifact.type,
20911
+ onChange: handleEditChange
20912
+ }
20913
+ );
20914
+ }
20915
+ if (artifact.type === "image") {
20916
+ return /* @__PURE__ */ jsxRuntime.jsx(ImageArtifactPreview, { content: displayContent, metadata: artifact.metadata, config });
20917
+ }
20918
+ if (artifact.type === "video") {
20919
+ return /* @__PURE__ */ jsxRuntime.jsx(VideoArtifactPreview, { content: displayContent, metadata: artifact.metadata, config });
20920
+ }
18845
20921
  if (viewMode === "code" || !canPreview) {
18846
20922
  return /* @__PURE__ */ jsxRuntime.jsx(
18847
20923
  CodeBlock3,
18848
20924
  {
18849
- code: artifact.content,
20925
+ code: displayContent,
18850
20926
  language: artifact.language || artifact.type
18851
20927
  }
18852
20928
  );
18853
20929
  }
18854
20930
  switch (artifact.type) {
18855
20931
  case "html":
18856
- return /* @__PURE__ */ jsxRuntime.jsx(HtmlPreview3, { content: artifact.content });
20932
+ case "svg":
20933
+ return /* @__PURE__ */ jsxRuntime.jsx(HtmlPreview3, { content: displayContent });
18857
20934
  case "markdown":
18858
- return /* @__PURE__ */ jsxRuntime.jsx(MarkdownPreview, { content: artifact.content, config });
20935
+ return /* @__PURE__ */ jsxRuntime.jsx(MarkdownPreview, { content: displayContent, config });
18859
20936
  default:
18860
20937
  return /* @__PURE__ */ jsxRuntime.jsx(
18861
20938
  CodeBlock3,
18862
20939
  {
18863
- code: artifact.content,
20940
+ code: displayContent,
18864
20941
  language: artifact.language || artifact.type
18865
20942
  }
18866
20943
  );
@@ -18883,26 +20960,79 @@ var ArtifactViewer = React20.memo(function ArtifactViewer2({
18883
20960
  ] })
18884
20961
  ] }),
18885
20962
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
18886
- onFullscreenToggle && /* @__PURE__ */ jsxRuntime.jsx(
18887
- "button",
18888
- {
18889
- onClick: onFullscreenToggle,
18890
- className: "p-1.5 text-zinc-500 agent-sdk-light:text-zinc-600 hover:text-zinc-300 agent-sdk-light:hover:text-zinc-900 hover:bg-zinc-800 agent-sdk-light:hover:bg-zinc-200 rounded-lg transition-colors",
18891
- title: isFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
18892
- children: isFullscreen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize2, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { size: 14 })
18893
- }
18894
- ),
18895
- canPreview && /* @__PURE__ */ jsxRuntime.jsx(
18896
- ViewTabs,
18897
- {
18898
- activeView: viewMode,
18899
- onViewChange: (v) => setViewMode(v),
18900
- views: [
18901
- { id: "preview", label: "Preview", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 12 }) },
18902
- { id: "code", label: "Code", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 12 }) }
18903
- ]
18904
- }
18905
- ),
20963
+ isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20964
+ /* @__PURE__ */ jsxRuntime.jsx(
20965
+ "button",
20966
+ {
20967
+ onClick: handleUndo,
20968
+ disabled: undoStack.length === 0,
20969
+ className: "p-1.5 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 rounded-lg transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
20970
+ title: "\u64A4\u9500",
20971
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Undo2, { size: 14 })
20972
+ }
20973
+ ),
20974
+ /* @__PURE__ */ jsxRuntime.jsx(
20975
+ "button",
20976
+ {
20977
+ onClick: handleRedo,
20978
+ disabled: redoStack.length === 0,
20979
+ className: "p-1.5 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 rounded-lg transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
20980
+ title: "\u91CD\u505A",
20981
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Redo2, { size: 14 })
20982
+ }
20983
+ ),
20984
+ /* @__PURE__ */ jsxRuntime.jsxs(
20985
+ "button",
20986
+ {
20987
+ onClick: handleSave,
20988
+ className: "flex items-center gap-1 px-2.5 py-1 text-xs bg-[#d8ff00] hover:bg-[#e5ff4d] text-black rounded-lg transition-colors font-medium",
20989
+ title: "\u4FDD\u5B58",
20990
+ children: [
20991
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Save, { size: 12 }),
20992
+ "\u4FDD\u5B58"
20993
+ ]
20994
+ }
20995
+ ),
20996
+ /* @__PURE__ */ jsxRuntime.jsx(
20997
+ "button",
20998
+ {
20999
+ onClick: handleCancelEdit,
21000
+ className: "px-2.5 py-1 text-xs text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-colors",
21001
+ title: "\u53D6\u6D88\u7F16\u8F91",
21002
+ children: "\u53D6\u6D88"
21003
+ }
21004
+ )
21005
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
21006
+ canEdit && onContentChange && /* @__PURE__ */ jsxRuntime.jsx(
21007
+ "button",
21008
+ {
21009
+ onClick: handleStartEdit,
21010
+ className: "p-1.5 text-zinc-500 agent-sdk-light:text-zinc-600 hover:text-zinc-300 agent-sdk-light:hover:text-zinc-900 hover:bg-zinc-800 agent-sdk-light:hover:bg-zinc-200 rounded-lg transition-colors",
21011
+ title: "\u7F16\u8F91",
21012
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { size: 14 })
21013
+ }
21014
+ ),
21015
+ onFullscreenToggle && /* @__PURE__ */ jsxRuntime.jsx(
21016
+ "button",
21017
+ {
21018
+ onClick: onFullscreenToggle,
21019
+ className: "p-1.5 text-zinc-500 agent-sdk-light:text-zinc-600 hover:text-zinc-300 agent-sdk-light:hover:text-zinc-900 hover:bg-zinc-800 agent-sdk-light:hover:bg-zinc-200 rounded-lg transition-colors",
21020
+ title: isFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
21021
+ children: isFullscreen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize2, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { size: 14 })
21022
+ }
21023
+ ),
21024
+ canPreview && /* @__PURE__ */ jsxRuntime.jsx(
21025
+ ViewTabs,
21026
+ {
21027
+ activeView: viewMode,
21028
+ onViewChange: (v) => setViewMode(v),
21029
+ views: [
21030
+ { id: "preview", label: "Preview", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 12 }) },
21031
+ { id: "code", label: "Code", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 12 }) }
21032
+ ]
21033
+ }
21034
+ )
21035
+ ] }),
18906
21036
  /* @__PURE__ */ jsxRuntime.jsx(
18907
21037
  "button",
18908
21038
  {
@@ -18917,6 +21047,16 @@ var ArtifactViewer = React20.memo(function ArtifactViewer2({
18917
21047
  )
18918
21048
  ] })
18919
21049
  ] }),
21050
+ onSwitchArtifact && /* @__PURE__ */ jsxRuntime.jsx(
21051
+ ArtifactTabs,
21052
+ {
21053
+ artifacts,
21054
+ activeId: activeArtifactId ?? artifact.id,
21055
+ onSwitch: onSwitchArtifact,
21056
+ onClose: onCloseArtifact,
21057
+ onReorder: onReorderArtifacts
21058
+ }
21059
+ ),
18920
21060
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden", children: renderContent() })
18921
21061
  ] });
18922
21062
  }
@@ -18962,6 +21102,145 @@ var ArtifactViewer = React20.memo(function ArtifactViewer2({
18962
21102
  }
18963
21103
  );
18964
21104
  });
21105
+
21106
+ // src/utils/artifactExtractor.ts
21107
+ function getMediaTypeFromParamType(paramType) {
21108
+ const upper = paramType?.toUpperCase();
21109
+ if (upper === "IMAGE") return "image";
21110
+ if (upper === "VIDEO") return "video";
21111
+ return null;
21112
+ }
21113
+ function inferMediaTypeFromUrl(url) {
21114
+ if (!url) return null;
21115
+ const lower = url.toLowerCase();
21116
+ if (/\.(jpg|jpeg|png|gif|webp|svg|bmp)(\?|#|$)/.test(lower)) return "image";
21117
+ if (/\.(mp4|mov|webm|avi|mkv|m4v)(\?|#|$)/.test(lower)) return "video";
21118
+ if (/\.(jpg|jpeg|png|gif|webp)/.test(lower)) return "image";
21119
+ if (/\.(mp4|mov|webm)/.test(lower)) return "video";
21120
+ return null;
21121
+ }
21122
+ function extractArtifactsFromToolCall(toolCall, sessionId, messageId) {
21123
+ const artifacts = [];
21124
+ if (!toolCall || toolCall.status?.toLowerCase() !== "completed") return artifacts;
21125
+ const result = toolCall.result;
21126
+ if (!result) return artifacts;
21127
+ const toolName = result?.relation?.toolName || toolCall.name || "AI Tool";
21128
+ const args = toolCall.arguments || {};
21129
+ if (result.works && Array.isArray(result.works)) {
21130
+ result.works.forEach((work, index) => {
21131
+ const paramType = work.customResponse?.paramType;
21132
+ const fileId = work.fileId;
21133
+ const fileUrl = work.fileUrl || work.customResponse?.url;
21134
+ if (!fileId && !fileUrl) return;
21135
+ const mediaType = getMediaTypeFromParamType(paramType) || (fileUrl ? inferMediaTypeFromUrl(fileUrl) : null) || (fileId ? inferMediaTypeFromUrl(fileId) : null);
21136
+ if (!mediaType) return;
21137
+ const artifactId = work.publicId || `media-${toolCall.id}-${index}`;
21138
+ artifacts.push({
21139
+ id: artifactId,
21140
+ sessionId,
21141
+ type: mediaType,
21142
+ title: `${toolName} #${index + 1}`,
21143
+ // 优先存 fileId,resolveAssetForDisplay 会将其转换为可用 URL
21144
+ currentContent: fileId || fileUrl,
21145
+ version: 1,
21146
+ source: "tool",
21147
+ sourceId: toolCall.id,
21148
+ metadata: {
21149
+ toolName,
21150
+ generationParams: args,
21151
+ messageId,
21152
+ workflowCode: result.workflowCode,
21153
+ taskId: result.taskId,
21154
+ fileId: work.fileId,
21155
+ fileUrl: work.fileUrl
21156
+ },
21157
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
21158
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
21159
+ });
21160
+ });
21161
+ }
21162
+ if (artifacts.length === 0 && result.outputs?.images) {
21163
+ const imgUrl = typeof result.outputs.images === "string" ? result.outputs.images : null;
21164
+ if (imgUrl) {
21165
+ artifacts.push({
21166
+ id: `media-${toolCall.id}-output`,
21167
+ sessionId,
21168
+ type: "image",
21169
+ title: `${toolName}`,
21170
+ currentContent: imgUrl,
21171
+ version: 1,
21172
+ source: "tool",
21173
+ sourceId: toolCall.id,
21174
+ metadata: {
21175
+ toolName,
21176
+ generationParams: args,
21177
+ messageId
21178
+ },
21179
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
21180
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
21181
+ });
21182
+ }
21183
+ }
21184
+ if (result.works && Array.isArray(result.works)) {
21185
+ result.works.forEach((work, index) => {
21186
+ if (work.customResponse?.value) {
21187
+ const parsed = detectContentType(work.customResponse.value);
21188
+ if (parsed.content && parsed.type !== "text") {
21189
+ artifacts.push({
21190
+ id: `content-${toolCall.id}-${index}`,
21191
+ sessionId,
21192
+ type: parsed.type,
21193
+ title: parsed.title || `${toolName} - ${parsed.type.toUpperCase()}`,
21194
+ currentContent: parsed.content,
21195
+ language: parsed.language,
21196
+ version: 1,
21197
+ source: "tool",
21198
+ sourceId: toolCall.id,
21199
+ metadata: {
21200
+ toolName,
21201
+ messageId
21202
+ },
21203
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
21204
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
21205
+ });
21206
+ }
21207
+ }
21208
+ });
21209
+ }
21210
+ return artifacts;
21211
+ }
21212
+ function extractArtifactsFromMessages(messages, sessionId) {
21213
+ const allArtifacts = [];
21214
+ const seenIds = /* @__PURE__ */ new Set();
21215
+ for (const message of messages) {
21216
+ if (message.role !== "assistant") continue;
21217
+ const toolCalls = message.extraData?.tool_calls || [];
21218
+ for (const tc of toolCalls) {
21219
+ const extracted = extractArtifactsFromToolCall(tc, sessionId, message.messageId);
21220
+ for (const artifact of extracted) {
21221
+ if (!seenIds.has(artifact.id)) {
21222
+ seenIds.add(artifact.id);
21223
+ allArtifacts.push(artifact);
21224
+ }
21225
+ }
21226
+ }
21227
+ const thoughts = message.extraData?.thoughts || message.thoughts || [];
21228
+ for (const thought of thoughts) {
21229
+ if (thought.toolCalls) {
21230
+ for (const tc of thought.toolCalls) {
21231
+ const extracted = extractArtifactsFromToolCall(tc, sessionId, message.messageId);
21232
+ for (const artifact of extracted) {
21233
+ if (!seenIds.has(artifact.id)) {
21234
+ seenIds.add(artifact.id);
21235
+ allArtifacts.push(artifact);
21236
+ }
21237
+ }
21238
+ }
21239
+ }
21240
+ }
21241
+ }
21242
+ return allArtifacts;
21243
+ }
18965
21244
  var Field = ({ label, value }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 text-xs text-zinc-400", children: [
18966
21245
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
18967
21246
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-200 truncate max-w-[60%]", children: value ?? "-" })
@@ -19293,7 +21572,9 @@ function ChatInputArea({
19293
21572
  onSend,
19294
21573
  onStop,
19295
21574
  isStreaming,
19296
- config
21575
+ config,
21576
+ activeArtifact,
21577
+ onDetachArtifact
19297
21578
  }) {
19298
21579
  const textareaRef = React20.useRef(null);
19299
21580
  const fileInputRef = React20.useRef(null);
@@ -19374,6 +21655,20 @@ function ChatInputArea({
19374
21655
  };
19375
21656
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-zinc-950 px-4 py-4 flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto", children: [
19376
21657
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative bg-zinc-900 border border-zinc-800 rounded-2xl shadow-lg shadow-black/20 focus-within:border-zinc-700 focus-within:shadow-xl focus-within:shadow-black/30 transition-all duration-200", children: [
21658
+ activeArtifact && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 px-4 pt-3 pb-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-2.5 py-1 bg-zinc-800/80 rounded-lg text-xs", children: [
21659
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileCode, { size: 12, className: "text-[#d8ff00] flex-shrink-0" }),
21660
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400", children: "\u5173\u8054\u4EA7\u7269\uFF1A" }),
21661
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-200 font-medium max-w-[200px] truncate", children: activeArtifact.title }),
21662
+ onDetachArtifact && /* @__PURE__ */ jsxRuntime.jsx(
21663
+ "button",
21664
+ {
21665
+ onClick: onDetachArtifact,
21666
+ className: "p-0.5 text-zinc-500 hover:text-zinc-300 rounded transition-colors",
21667
+ title: "\u53D6\u6D88\u5173\u8054",
21668
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Unlink, { size: 10 })
21669
+ }
21670
+ )
21671
+ ] }) }),
19377
21672
  images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-3 p-4 pb-2", children: images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
19378
21673
  /* @__PURE__ */ jsxRuntime.jsx(
19379
21674
  "img",
@@ -19538,6 +21833,188 @@ var DragHandle = React20__namespace.default.memo(function DragHandle2({
19538
21833
  }
19539
21834
  );
19540
21835
  });
21836
+ var artifactTypeIconMap = {
21837
+ html: lucideReact.Globe,
21838
+ svg: lucideReact.Globe,
21839
+ markdown: lucideReact.FileText,
21840
+ json: lucideReact.FileJson,
21841
+ code: lucideReact.FileCode,
21842
+ text: lucideReact.FileText,
21843
+ image: lucideReact.FileImage,
21844
+ video: lucideReact.Video
21845
+ };
21846
+ var ArtifactBarItemThumbnail = React20.memo(function ArtifactBarItemThumbnail2({
21847
+ artifact,
21848
+ config
21849
+ }) {
21850
+ const TypeIcon = artifactTypeIconMap[artifact.type] || lucideReact.FileText;
21851
+ const isMedia = artifact.type === "image" || artifact.type === "video";
21852
+ const thumbnailUrl = useResolvedThumbnailUrl(
21853
+ artifact.currentContent,
21854
+ {
21855
+ fileId: artifact.metadata?.fileId,
21856
+ type: artifact.type,
21857
+ config,
21858
+ enabled: isMedia
21859
+ }
21860
+ );
21861
+ if (isMedia && thumbnailUrl) {
21862
+ return /* @__PURE__ */ jsxRuntime.jsx(
21863
+ "img",
21864
+ {
21865
+ src: thumbnailUrl,
21866
+ alt: "",
21867
+ className: "w-10 h-10 rounded-lg object-cover flex-shrink-0 border border-zinc-700/50 agent-sdk-light:border-zinc-300",
21868
+ loading: "lazy"
21869
+ }
21870
+ );
21871
+ }
21872
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-lg bg-zinc-800/50 agent-sdk-light:bg-zinc-100 flex items-center justify-center flex-shrink-0 border border-zinc-700/30 agent-sdk-light:border-zinc-300", children: /* @__PURE__ */ jsxRuntime.jsx(TypeIcon, { size: 18, className: "text-zinc-400 agent-sdk-light:text-zinc-500" }) });
21873
+ });
21874
+ var ArtifactBarMiniThumbnail = React20.memo(function ArtifactBarMiniThumbnail2({
21875
+ artifact,
21876
+ config
21877
+ }) {
21878
+ const TypeIcon = artifactTypeIconMap[artifact.type] || lucideReact.FileText;
21879
+ const isMedia = artifact.type === "image" || artifact.type === "video";
21880
+ const thumbnailUrl = useResolvedThumbnailUrl(
21881
+ artifact.currentContent,
21882
+ {
21883
+ fileId: artifact.metadata?.fileId,
21884
+ type: artifact.type,
21885
+ config,
21886
+ enabled: isMedia
21887
+ }
21888
+ );
21889
+ if (isMedia && thumbnailUrl) {
21890
+ return /* @__PURE__ */ jsxRuntime.jsx(
21891
+ "img",
21892
+ {
21893
+ src: thumbnailUrl,
21894
+ alt: "",
21895
+ className: "w-6 h-6 rounded border border-zinc-700 agent-sdk-light:border-zinc-300 object-cover",
21896
+ loading: "lazy"
21897
+ }
21898
+ );
21899
+ }
21900
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-6 h-6 rounded border border-zinc-700 agent-sdk-light:border-zinc-300 bg-zinc-800 agent-sdk-light:bg-zinc-200 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(TypeIcon, { size: 10, className: "text-zinc-400" }) });
21901
+ });
21902
+ var ArtifactBar = React20__namespace.default.memo(function ArtifactBar2({
21903
+ artifacts,
21904
+ onOpenArtifact,
21905
+ config
21906
+ }) {
21907
+ const [expanded, setExpanded] = React20.useState(false);
21908
+ const barRef = React20.useRef(null);
21909
+ React20.useEffect(() => {
21910
+ if (!expanded) return;
21911
+ const handleClickOutside = (e) => {
21912
+ if (barRef.current && !barRef.current.contains(e.target)) {
21913
+ setExpanded(false);
21914
+ }
21915
+ };
21916
+ document.addEventListener("mousedown", handleClickOutside);
21917
+ return () => document.removeEventListener("mousedown", handleClickOutside);
21918
+ }, [expanded]);
21919
+ if (artifacts.length === 0) return null;
21920
+ const imageCount = artifacts.filter((a) => a.type === "image").length;
21921
+ const codeCount = artifacts.filter((a) => ["code", "html", "svg", "json"].includes(a.type)).length;
21922
+ const docCount = artifacts.filter((a) => ["markdown", "text"].includes(a.type)).length;
21923
+ const videoCount = artifacts.filter((a) => a.type === "video").length;
21924
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: barRef, className: "relative flex-shrink-0 mx-auto max-w-md", children: [
21925
+ expanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-0 right-0 mb-1 z-30 max-h-[50vh] overflow-y-auto bg-zinc-900/95 agent-sdk-light:bg-white/95 backdrop-blur-md border border-zinc-700/50 agent-sdk-light:border-zinc-300 rounded-xl shadow-2xl", children: [
21926
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 border-b border-zinc-800/50 agent-sdk-light:border-zinc-200 flex items-center justify-between", children: [
21927
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
21928
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGrid, { size: 14, className: "text-[#d8ff00]" }),
21929
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-zinc-200 agent-sdk-light:text-zinc-800", children: "\u5168\u90E8\u4EA7\u7269" }),
21930
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-zinc-500 bg-zinc-800/50 agent-sdk-light:bg-zinc-200 px-1.5 py-0.5 rounded-full", children: artifacts.length })
21931
+ ] }),
21932
+ /* @__PURE__ */ jsxRuntime.jsx(
21933
+ "button",
21934
+ {
21935
+ onClick: () => setExpanded(false),
21936
+ className: "p-1 text-zinc-500 hover:text-zinc-300 agent-sdk-light:hover:text-zinc-700 rounded transition-colors",
21937
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 16 })
21938
+ }
21939
+ )
21940
+ ] }),
21941
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 grid grid-cols-1 gap-1", children: artifacts.map((artifact) => {
21942
+ return /* @__PURE__ */ jsxRuntime.jsxs(
21943
+ "button",
21944
+ {
21945
+ onClick: () => {
21946
+ onOpenArtifact(artifact.id);
21947
+ setExpanded(false);
21948
+ },
21949
+ className: "flex items-center gap-3 px-3 py-2.5 rounded-lg text-left w-full hover:bg-zinc-800/60 agent-sdk-light:hover:bg-zinc-100 transition-colors group",
21950
+ children: [
21951
+ /* @__PURE__ */ jsxRuntime.jsx(ArtifactBarItemThumbnail, { artifact, config }),
21952
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
21953
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-zinc-200 agent-sdk-light:text-zinc-800 truncate font-medium group-hover:text-white agent-sdk-light:group-hover:text-zinc-900", children: artifact.title }),
21954
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] text-zinc-500 agent-sdk-light:text-zinc-500 mt-0.5", children: [
21955
+ artifact.type.toUpperCase(),
21956
+ artifact.language && artifact.language !== artifact.type && ` \u2022 ${artifact.language}`
21957
+ ] })
21958
+ ] })
21959
+ ]
21960
+ },
21961
+ artifact.id
21962
+ );
21963
+ }) })
21964
+ ] }),
21965
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 border-t border-zinc-800/30 agent-sdk-light:border-zinc-200/50 bg-zinc-900/30 agent-sdk-light:bg-zinc-50/50", children: /* @__PURE__ */ jsxRuntime.jsxs(
21966
+ "button",
21967
+ {
21968
+ onClick: () => setExpanded((prev) => !prev),
21969
+ className: "w-full flex items-center gap-2.5 px-3 py-2 rounded-lg bg-zinc-800/40 agent-sdk-light:bg-zinc-100 hover:bg-zinc-800/60 agent-sdk-light:hover:bg-zinc-200/80 border border-zinc-700/30 agent-sdk-light:border-zinc-300/50 transition-colors group",
21970
+ children: [
21971
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
21972
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 14, className: "text-[#d8ff00]" }),
21973
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-zinc-300 agent-sdk-light:text-zinc-700", children: [
21974
+ artifacts.length,
21975
+ " \u4E2A\u4EA7\u7269"
21976
+ ] })
21977
+ ] }),
21978
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 flex-1 min-w-0", children: [
21979
+ imageCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-[10px] text-zinc-500 bg-zinc-700/30 agent-sdk-light:bg-zinc-200 px-1.5 py-0.5 rounded", children: [
21980
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileImage, { size: 10 }),
21981
+ " ",
21982
+ imageCount
21983
+ ] }),
21984
+ codeCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-[10px] text-zinc-500 bg-zinc-700/30 agent-sdk-light:bg-zinc-200 px-1.5 py-0.5 rounded", children: [
21985
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileCode, { size: 10 }),
21986
+ " ",
21987
+ codeCount
21988
+ ] }),
21989
+ docCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-[10px] text-zinc-500 bg-zinc-700/30 agent-sdk-light:bg-zinc-200 px-1.5 py-0.5 rounded", children: [
21990
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 10 }),
21991
+ " ",
21992
+ docCount
21993
+ ] }),
21994
+ videoCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-[10px] text-zinc-500 bg-zinc-700/30 agent-sdk-light:bg-zinc-200 px-1.5 py-0.5 rounded", children: [
21995
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 10 }),
21996
+ " ",
21997
+ videoCount
21998
+ ] })
21999
+ ] }),
22000
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center -space-x-1.5", children: [
22001
+ artifacts.slice(0, 4).map((artifact) => {
22002
+ return /* @__PURE__ */ jsxRuntime.jsx(ArtifactBarMiniThumbnail, { artifact, config }, artifact.id);
22003
+ }),
22004
+ artifacts.length > 4 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-6 h-6 rounded border border-zinc-700 agent-sdk-light:border-zinc-300 bg-zinc-800 agent-sdk-light:bg-zinc-200 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[9px] text-zinc-400", children: [
22005
+ "+",
22006
+ artifacts.length - 4
22007
+ ] }) })
22008
+ ] }),
22009
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { size: 14, className: cn2(
22010
+ "text-zinc-500 transition-transform flex-shrink-0",
22011
+ expanded && "rotate-180"
22012
+ ) })
22013
+ ]
22014
+ }
22015
+ ) })
22016
+ ] });
22017
+ });
19541
22018
  var AgentChat = React20__namespace.default.forwardRef(({
19542
22019
  agentId,
19543
22020
  projectId,
@@ -19580,7 +22057,7 @@ var AgentChat = React20__namespace.default.forwardRef(({
19580
22057
  ComponentRegistry: mergedRegistry
19581
22058
  };
19582
22059
  }, [config?.components, outerComponents]);
19583
- const { sessions, setSessions, currentSession, setCurrentSession, addSession, removeSession, tools: _tools, setTools, setSkills, setShowItemTime } = useAgentStore();
22060
+ const { sessions, setSessions, currentSession, setCurrentSession, addSession, removeSession, tools: _tools, setTools, setSkills, setShowItemTime, artifacts, artifactOrder, activeArtifactId, upsertArtifact, updateArtifactContent, setActiveArtifact, removeArtifact, clearArtifacts, setArtifacts, reorderArtifacts } = useAgentStore();
19584
22061
  const [loading, setLoading] = React20.useState(true);
19585
22062
  const [messagesLoading, setMessagesLoading] = React20.useState(false);
19586
22063
  const [collapsed, setCollapsed] = React20.useState(false);
@@ -19592,7 +22069,23 @@ var AgentChat = React20__namespace.default.forwardRef(({
19592
22069
  const lastUserMessageRef = React20.useRef("");
19593
22070
  const [shareModalOpen, setShareModalOpen] = React20.useState(false);
19594
22071
  const [shareSession, setShareSession] = React20.useState(null);
19595
- const [currentArtifact, setCurrentArtifact] = React20.useState(null);
22072
+ const artifactList = React20.useMemo(() => {
22073
+ const ordered = artifactOrder.map((id) => artifacts[id]).filter(Boolean);
22074
+ const unordered = Object.values(artifacts).filter(
22075
+ (a) => !artifactOrder.includes(a.id)
22076
+ );
22077
+ return [...ordered, ...unordered];
22078
+ }, [artifacts, artifactOrder]);
22079
+ const activeArtifact = activeArtifactId ? artifacts[activeArtifactId] ?? null : null;
22080
+ const currentArtifact = activeArtifact ? {
22081
+ id: activeArtifact.id,
22082
+ type: activeArtifact.type,
22083
+ title: activeArtifact.title,
22084
+ content: activeArtifact.currentContent,
22085
+ language: activeArtifact.language,
22086
+ source: activeArtifact.sourceId,
22087
+ metadata: activeArtifact.metadata
22088
+ } : null;
19596
22089
  const [isArtifactFullscreen, setIsArtifactFullscreen] = React20.useState(false);
19597
22090
  const [artifactPanelWidth, setArtifactPanelWidth] = React20.useState(50);
19598
22091
  const [isDragging, setIsDragging] = React20.useState(false);
@@ -19618,7 +22111,11 @@ var AgentChat = React20__namespace.default.forwardRef(({
19618
22111
  const deltaX = dragStartRef.current.startX - e.clientX;
19619
22112
  const deltaPercent = deltaX / containerWidth * 100;
19620
22113
  const newWidth = dragStartRef.current.startWidth + deltaPercent;
19621
- const clampedWidth = Math.max(25, Math.min(75, newWidth));
22114
+ const minChatPx = 350;
22115
+ const minArtifactPx = 400;
22116
+ const maxArtifactPercent = (containerWidth - minChatPx) / containerWidth * 100;
22117
+ const minArtifactPercent = minArtifactPx / containerWidth * 100;
22118
+ const clampedWidth = Math.max(minArtifactPercent, Math.min(maxArtifactPercent, newWidth));
19622
22119
  setArtifactPanelWidth(clampedWidth);
19623
22120
  };
19624
22121
  const handleMouseUp = () => {
@@ -19640,38 +22137,52 @@ var AgentChat = React20__namespace.default.forwardRef(({
19640
22137
  setShowItemTime(showItemTime);
19641
22138
  }, [showItemTime]);
19642
22139
  const handleOpenArtifact = React20.useCallback((data) => {
19643
- if (currentArtifact && currentArtifact.content === data.content && currentArtifact.type === data.type) {
19644
- setCurrentArtifact(null);
22140
+ if (activeArtifact && activeArtifact.currentContent === data.content && activeArtifact.type === data.type) {
22141
+ setActiveArtifact(null);
19645
22142
  setIsArtifactFullscreen(false);
19646
22143
  return;
19647
22144
  }
19648
- const artifact = {
19649
- id: `artifact-${Date.now()}`,
19650
- type: data.type,
19651
- title: data.title,
19652
- content: data.content,
19653
- language: data.language,
19654
- timestamp: Date.now()
19655
- };
19656
- setCurrentArtifact(artifact);
19657
- }, [currentArtifact]);
22145
+ const existingArtifact = Object.values(artifacts).find(
22146
+ (a) => a.currentContent === data.content && a.type === data.type
22147
+ );
22148
+ if (existingArtifact) {
22149
+ setActiveArtifact(existingArtifact.id);
22150
+ } else {
22151
+ const sessionId = currentSession?.sessionId || "";
22152
+ const newArtifactEntry = {
22153
+ id: `artifact-${Date.now()}`,
22154
+ sessionId,
22155
+ type: data.type,
22156
+ title: data.title,
22157
+ currentContent: data.content,
22158
+ language: data.language,
22159
+ metadata: data.metadata,
22160
+ version: 1,
22161
+ source: "llm",
22162
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
22163
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
22164
+ };
22165
+ upsertArtifact(newArtifactEntry);
22166
+ setActiveArtifact(newArtifactEntry.id);
22167
+ }
22168
+ }, [activeArtifact, artifacts, currentSession, upsertArtifact, setActiveArtifact]);
19658
22169
  const handleCloseArtifact = React20.useCallback(() => {
19659
- setCurrentArtifact(null);
22170
+ setActiveArtifact(null);
19660
22171
  setIsArtifactFullscreen(false);
19661
- }, []);
22172
+ }, [setActiveArtifact]);
19662
22173
  React20.useEffect(() => {
19663
22174
  const handleKeyDown = (e) => {
19664
- if (e.key === "Escape" && currentArtifact) {
22175
+ if (e.key === "Escape" && activeArtifactId) {
19665
22176
  if (isArtifactFullscreen) {
19666
22177
  setIsArtifactFullscreen(false);
19667
22178
  } else {
19668
- setCurrentArtifact(null);
22179
+ setActiveArtifact(null);
19669
22180
  }
19670
22181
  }
19671
22182
  };
19672
22183
  document.addEventListener("keydown", handleKeyDown);
19673
22184
  return () => document.removeEventListener("keydown", handleKeyDown);
19674
- }, [currentArtifact, isArtifactFullscreen]);
22185
+ }, [activeArtifactId, isArtifactFullscreen, setActiveArtifact]);
19675
22186
  const handleToggleArtifactFullscreen = React20.useCallback(() => {
19676
22187
  setIsArtifactFullscreen((prev) => !prev);
19677
22188
  }, []);
@@ -19873,8 +22384,9 @@ var AgentChat = React20__namespace.default.forwardRef(({
19873
22384
  addSession(s);
19874
22385
  setCurrentSession(s);
19875
22386
  useAgentStore.getState().setMessages([]);
22387
+ clearArtifacts();
19876
22388
  }
19877
- }, [projectId, agentId]);
22389
+ }, [projectId, agentId, clearArtifacts]);
19878
22390
  const handlePrompt = React20.useCallback(async (p) => {
19879
22391
  await handleNew();
19880
22392
  setInput(p);
@@ -19885,10 +22397,29 @@ var AgentChat = React20__namespace.default.forwardRef(({
19885
22397
  onSessionIdChange?.(s.sessionId);
19886
22398
  setMobileOpen(false);
19887
22399
  useAgentStore.getState().setMessages([]);
22400
+ clearArtifacts();
19888
22401
  setMessagesLoading(true);
19889
- await loadMessages(s.sessionId);
22402
+ await Promise.all([
22403
+ loadMessages(s.sessionId),
22404
+ artifactService.list(s.sessionId).then((res) => {
22405
+ if (res.success && res.data && res.data.length > 0) {
22406
+ setArtifacts(res.data);
22407
+ }
22408
+ }).catch((err) => {
22409
+ console.error("[AgentChat] Load artifacts failed:", err);
22410
+ })
22411
+ ]);
22412
+ const currentArtifacts = useAgentStore.getState().artifacts;
22413
+ if (Object.keys(currentArtifacts).length === 0) {
22414
+ const currentMessages = useAgentStore.getState().messages;
22415
+ const extracted = extractArtifactsFromMessages(currentMessages, s.sessionId);
22416
+ if (extracted.length > 0) {
22417
+ console.log("[AgentChat] Fallback: extracted", extracted.length, "artifacts from messages");
22418
+ extracted.forEach((artifact) => upsertArtifact(artifact));
22419
+ }
22420
+ }
19890
22421
  setMessagesLoading(false);
19891
- }, []);
22422
+ }, [clearArtifacts, setArtifacts, upsertArtifact]);
19892
22423
  const handleDelete2 = React20.useCallback(async (id) => {
19893
22424
  toast("\u5220\u9664\u6B64\u5BF9\u8BDD\uFF1F", {
19894
22425
  action: {
@@ -19984,8 +22515,14 @@ var AgentChat = React20__namespace.default.forwardRef(({
19984
22515
  }
19985
22516
  });
19986
22517
  setImages([]);
19987
- await sendMessage(currentSession.sessionId, messageContent);
19988
- }, [input, images, currentSession, sendMessage, config]);
22518
+ const currentActiveArtifact = activeArtifactId ? artifacts[activeArtifactId] : null;
22519
+ const artifactContext = currentActiveArtifact ? {
22520
+ artifactId: currentActiveArtifact.id,
22521
+ title: currentActiveArtifact.title,
22522
+ currentContent: currentActiveArtifact.currentContent
22523
+ } : void 0;
22524
+ await sendMessage(currentSession.sessionId, messageContent, { artifactContext });
22525
+ }, [input, images, currentSession, sendMessage, config, activeArtifactId, artifacts]);
19989
22526
  const sendTextMessage = React20.useCallback(async (content) => {
19990
22527
  if (!content.trim()) return;
19991
22528
  if (!currentSession) return;
@@ -20150,6 +22687,13 @@ var AgentChat = React20__namespace.default.forwardRef(({
20150
22687
  onOpenArtifact: handleOpenArtifact
20151
22688
  }
20152
22689
  ),
22690
+ artifactList.length > 0 && !currentArtifact && /* @__PURE__ */ jsxRuntime.jsx(
22691
+ ArtifactBar,
22692
+ {
22693
+ artifacts: artifactList,
22694
+ onOpenArtifact: (id) => setActiveArtifact(id)
22695
+ }
22696
+ ),
20153
22697
  !hideInput && /* @__PURE__ */ jsxRuntime.jsx(
20154
22698
  ChatInputArea,
20155
22699
  {
@@ -20160,7 +22704,9 @@ var AgentChat = React20__namespace.default.forwardRef(({
20160
22704
  onSend: handleSend,
20161
22705
  onStop: handleStop,
20162
22706
  isStreaming: isCurrentSessionStreaming,
20163
- config
22707
+ config,
22708
+ activeArtifact,
22709
+ onDetachArtifact: () => setActiveArtifact(null)
20164
22710
  }
20165
22711
  )
20166
22712
  ]
@@ -20199,7 +22745,36 @@ var AgentChat = React20__namespace.default.forwardRef(({
20199
22745
  config,
20200
22746
  isFullscreen: isArtifactFullscreen,
20201
22747
  onFullscreenToggle: handleToggleArtifactFullscreen,
20202
- embedded: true
22748
+ embedded: true,
22749
+ artifacts: artifactList,
22750
+ activeArtifactId,
22751
+ onSwitchArtifact: (id) => setActiveArtifact(id),
22752
+ onCloseArtifact: (id) => {
22753
+ removeArtifact(id);
22754
+ if (artifactList.length <= 1) {
22755
+ setIsArtifactFullscreen(false);
22756
+ }
22757
+ },
22758
+ onReorderArtifacts: reorderArtifacts,
22759
+ onContentChange: (content) => {
22760
+ if (activeArtifactId) {
22761
+ updateArtifactContent(activeArtifactId, content, "user");
22762
+ }
22763
+ },
22764
+ onSave: async (artifactId, content) => {
22765
+ try {
22766
+ const res = await artifactService.updateContent(artifactId, content);
22767
+ if (res.success) {
22768
+ updateArtifactContent(artifactId, content, "user");
22769
+ toast.success("\u4EA7\u7269\u5DF2\u4FDD\u5B58");
22770
+ } else {
22771
+ toast.error("\u4FDD\u5B58\u5931\u8D25");
22772
+ }
22773
+ } catch (err) {
22774
+ console.error("[AgentChat] Save artifact failed:", err);
22775
+ toast.error("\u4FDD\u5B58\u4EA7\u7269\u5931\u8D25");
22776
+ }
22777
+ }
20203
22778
  }
20204
22779
  )
20205
22780
  ]
@@ -20709,6 +23284,7 @@ exports.VoiceWaveform = VoiceWaveform;
20709
23284
  exports.agentService = agentService;
20710
23285
  exports.agentSkillService = agentSkillService;
20711
23286
  exports.agentToolService = agentToolService;
23287
+ exports.artifactService = artifactService;
20712
23288
  exports.calculateReconnectDelay = calculateReconnectDelay;
20713
23289
  exports.componentService = componentService;
20714
23290
  exports.confirmAllPendingToolCalls = confirmAllPendingToolCalls;
@@ -20764,8 +23340,14 @@ exports.shareService = shareService;
20764
23340
  exports.templateService = templateService;
20765
23341
  exports.toolCallService = toolCallService;
20766
23342
  exports.uploadOSS = uploadOSS;
23343
+ exports.useActiveArtifact = useActiveArtifact;
23344
+ exports.useActiveArtifactId = useActiveArtifactId;
20767
23345
  exports.useActiveSubAgent = useActiveSubAgent;
20768
23346
  exports.useAgentStore = useAgentStore;
23347
+ exports.useArtifactList = useArtifactList;
23348
+ exports.useArtifactOrder = useArtifactOrder;
23349
+ exports.useArtifacts = useArtifacts;
23350
+ exports.useCanvasBridge = useCanvasBridge;
20769
23351
  exports.useChatError = useChatError;
20770
23352
  exports.useChatUI = useChatUI;
20771
23353
  exports.useComponent = useComponent;