@malette/agent-sdk 0.1.1-alpha.0 → 0.1.3-beta.1

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,285 @@ 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 inline-block 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
+ "max-w-full",
11007
+ className
11008
+ ),
11009
+ style: { maxHeight: "400px" },
11010
+ onMouseEnter: handleMouseEnter,
11011
+ onMouseLeave: handleMouseLeave,
11012
+ onClick: handleClick,
11013
+ children: [
11014
+ posterUrl && !isPlaying && /* @__PURE__ */ jsxRuntime.jsx(
11015
+ "img",
11016
+ {
11017
+ src: posterUrl,
11018
+ alt: "Video cover",
11019
+ className: "absolute inset-0 w-full h-full object-contain z-[1]"
11020
+ }
11021
+ ),
11022
+ /* @__PURE__ */ jsxRuntime.jsx(
11023
+ "video",
11024
+ {
11025
+ ref: videoRef,
11026
+ src,
11027
+ muted: isMuted,
11028
+ playsInline: true,
11029
+ preload: "metadata",
11030
+ onLoadedMetadata: handleLoadedMetadata,
11031
+ onTimeUpdate: handleTimeUpdate,
11032
+ onEnded: () => {
11033
+ setIsPlaying(false);
11034
+ setIsFullPlaying(false);
11035
+ },
11036
+ className: cn(
11037
+ "w-full max-h-[400px] object-contain",
11038
+ !isPlaying && posterUrl && "opacity-0"
11039
+ )
11040
+ }
11041
+ ),
11042
+ /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "hidden" }),
11043
+ !isFullPlaying && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
11044
+ "absolute inset-0 z-[2] flex items-center justify-center",
11045
+ "bg-gradient-to-t from-black/50 via-transparent to-transparent",
11046
+ "transition-opacity duration-200",
11047
+ isHovering && isPlaying ? "opacity-0" : "opacity-100"
11048
+ ), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
11049
+ "w-14 h-14 rounded-full bg-white/20 backdrop-blur-md",
11050
+ "flex items-center justify-center",
11051
+ "transition-all duration-200",
11052
+ "group-hover/video:scale-110 group-hover/video:bg-white/30",
11053
+ "shadow-lg shadow-black/20"
11054
+ ), 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" }) }) }) }),
11055
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
11056
+ "absolute bottom-0 left-0 right-0 z-[3]",
11057
+ "bg-gradient-to-t from-black/80 via-black/40 to-transparent",
11058
+ "px-3 pb-2.5 pt-8",
11059
+ "transition-opacity duration-200",
11060
+ isHovering || isFullPlaying ? "opacity-100" : "opacity-0"
11061
+ ), children: [
11062
+ /* @__PURE__ */ jsxRuntime.jsx(
11063
+ "div",
11064
+ {
11065
+ className: "w-full h-1 bg-white/20 rounded-full mb-2 cursor-pointer group/progress",
11066
+ onClick: handleProgressClick,
11067
+ children: /* @__PURE__ */ jsxRuntime.jsx(
11068
+ "div",
11069
+ {
11070
+ className: "h-full bg-[#d8ff00] rounded-full relative transition-all duration-75",
11071
+ style: { width: `${progressPercent}%` },
11072
+ 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" })
11073
+ }
11074
+ )
11075
+ }
11076
+ ),
11077
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
11078
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
11079
+ /* @__PURE__ */ jsxRuntime.jsx(
11080
+ "button",
11081
+ {
11082
+ onClick: handleClick,
11083
+ className: "p-1 text-white/80 hover:text-white transition-colors",
11084
+ children: isPlaying ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: [
11085
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1" }),
11086
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1" })
11087
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) })
11088
+ }
11089
+ ),
11090
+ /* @__PURE__ */ jsxRuntime.jsx(
11091
+ "button",
11092
+ {
11093
+ onClick: handleToggleMute,
11094
+ className: "p-1 text-white/80 hover:text-white transition-colors",
11095
+ 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: [
11096
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
11097
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "9", x2: "17", y2: "15" }),
11098
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "17", y1: "9", x2: "23", y2: "15" })
11099
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
11100
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
11101
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" }),
11102
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" })
11103
+ ] })
11104
+ }
11105
+ ),
11106
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-white/60 font-mono tabular-nums", children: [
11107
+ formatTime2(currentTime),
11108
+ " / ",
11109
+ formatTime2(duration)
11110
+ ] })
11111
+ ] }),
11112
+ /* @__PURE__ */ jsxRuntime.jsx(
11113
+ "button",
11114
+ {
11115
+ onClick: (e) => {
11116
+ e.stopPropagation();
11117
+ const video = videoRef.current;
11118
+ if (video) {
11119
+ video.pause();
11120
+ setIsPlaying(false);
11121
+ setIsFullPlaying(false);
11122
+ }
11123
+ setShowPreview(true);
11124
+ },
11125
+ className: "p-1 text-white/60 hover:text-white transition-colors",
11126
+ title: "\u5168\u5C4F\u9884\u89C8",
11127
+ 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: [
11128
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
11129
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 21 3 21 3 15" }),
11130
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
11131
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
11132
+ ] })
11133
+ }
11134
+ )
11135
+ ] })
11136
+ ] })
11137
+ ]
11138
+ }
11139
+ )
11140
+ ] });
10609
11141
  });
10610
11142
  var ImagePreviewInternal = React20.memo(function ImagePreviewInternal2({
10611
11143
  src,
@@ -12251,6 +12783,7 @@ var ToolResultRenderer = React20.memo(function ToolResultRenderer2({
12251
12783
  result,
12252
12784
  mediaUrls,
12253
12785
  config,
12786
+ toolCallData,
12254
12787
  onOpenArtifact
12255
12788
  }) {
12256
12789
  const [imgLoaded, setImgLoaded] = React20.useState({});
@@ -12317,32 +12850,134 @@ var ToolResultRenderer = React20.memo(function ToolResultRenderer2({
12317
12850
  }
12318
12851
  ),
12319
12852
  /* @__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"
12853
+ hasMedia && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2", children: (() => {
12854
+ const totalMedia = imageUrls.length + videoUrls.length;
12855
+ const isMultiple = totalMedia > 1;
12856
+ const buildMediaMetadata = (mediaIndex) => {
12857
+ if (!toolCallData) return void 0;
12858
+ const resultAny = toolCallData.result;
12859
+ const metadata = {};
12860
+ if (toolCallData.name) metadata.toolName = toolCallData.name;
12861
+ if (resultAny?.relation?.toolName) metadata.toolName = resultAny.relation.toolName;
12862
+ if (toolCallData.arguments) metadata.generationParams = toolCallData.arguments;
12863
+ if (resultAny?.relation) metadata.relation = resultAny.relation;
12864
+ if (resultAny?.taskId) metadata.taskId = resultAny.taskId;
12865
+ const work = resultAny?.works?.[mediaIndex];
12866
+ if (work) {
12867
+ if (work.publicId) metadata.workId = work.publicId;
12868
+ if (work.fileId) metadata.fileId = work.fileId;
12329
12869
  }
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",
12870
+ return metadata;
12871
+ };
12872
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: isMultiple ? "grid grid-cols-2 gap-2" : "flex", children: [
12873
+ videoUrls.map((video, i) => /* @__PURE__ */ jsxRuntime.jsxs(
12874
+ "div",
12335
12875
  {
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
- ] }),
12876
+ className: `relative group/media overflow-hidden rounded-lg bg-black ${isMultiple ? "aspect-square" : "max-h-[375px]"}`,
12877
+ children: [
12878
+ /* @__PURE__ */ jsxRuntime.jsx(
12879
+ MessageVideoInternal,
12880
+ {
12881
+ src: video.hdUrl || video.url,
12882
+ className: isMultiple ? "w-full h-full object-cover" : "max-h-[375px] max-w-full object-contain"
12883
+ }
12884
+ ),
12885
+ /* @__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: [
12886
+ onOpenArtifact && /* @__PURE__ */ jsxRuntime.jsxs(
12887
+ "button",
12888
+ {
12889
+ onClick: (e) => {
12890
+ e.stopPropagation();
12891
+ const resultAny = toolCallData?.result;
12892
+ const displayName = resultAny?.relation?.toolName || toolCallData?.name || "\u89C6\u9891\u751F\u6210";
12893
+ onOpenArtifact({
12894
+ type: "video",
12895
+ title: `${displayName} #${i + 1}`,
12896
+ content: video.hdUrl || video.url,
12897
+ metadata: buildMediaMetadata(imageUrls.length + i)
12898
+ });
12899
+ },
12900
+ 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",
12901
+ children: [
12902
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 12 }),
12903
+ "\u67E5\u770B\u8BE6\u60C5"
12904
+ ]
12905
+ }
12906
+ ),
12907
+ /* @__PURE__ */ jsxRuntime.jsx(
12908
+ "button",
12909
+ {
12910
+ onClick: (e) => {
12911
+ e.stopPropagation();
12912
+ window.open(video.hdUrl || video.url, "_blank");
12913
+ },
12914
+ className: "p-1.5 text-white/80 hover:text-white bg-white/15 hover:bg-white/25 backdrop-blur-sm rounded-md transition-colors",
12915
+ title: "\u65B0\u7A97\u53E3\u6253\u5F00",
12916
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 14 })
12917
+ }
12918
+ )
12919
+ ] })
12920
+ ]
12921
+ },
12922
+ `video-${i}`
12923
+ )),
12924
+ imageUrls.map((img, i) => /* @__PURE__ */ jsxRuntime.jsxs(
12925
+ "div",
12926
+ {
12927
+ className: `relative group/media overflow-hidden rounded-lg bg-black ${isMultiple ? "aspect-square" : "max-h-[375px]"}`,
12928
+ children: [
12929
+ !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]" }) }),
12930
+ /* @__PURE__ */ jsxRuntime.jsx(
12931
+ "img",
12932
+ {
12933
+ src: img.url,
12934
+ alt: "Generated",
12935
+ onLoad: () => handleImgLoad(i),
12936
+ className: `cursor-zoom-in ${imgLoaded[i] ? "" : "hidden"} ${isMultiple ? "w-full h-full object-cover" : "max-h-[375px] max-w-full h-auto object-contain"}`,
12937
+ onClick: () => setPreviewUrl(img.hdUrl)
12938
+ }
12939
+ ),
12940
+ 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: [
12941
+ onOpenArtifact && /* @__PURE__ */ jsxRuntime.jsxs(
12942
+ "button",
12943
+ {
12944
+ onClick: (e) => {
12945
+ e.stopPropagation();
12946
+ const resultAny = toolCallData?.result;
12947
+ const displayName = resultAny?.relation?.toolName || toolCallData?.name || "\u56FE\u7247\u751F\u6210";
12948
+ onOpenArtifact({
12949
+ type: "image",
12950
+ title: `${displayName} #${i + 1}`,
12951
+ content: img.hdUrl || img.url,
12952
+ metadata: buildMediaMetadata(i)
12953
+ });
12954
+ },
12955
+ 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",
12956
+ children: [
12957
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 12 }),
12958
+ "\u67E5\u770B\u8BE6\u60C5"
12959
+ ]
12960
+ }
12961
+ ),
12962
+ /* @__PURE__ */ jsxRuntime.jsx(
12963
+ "button",
12964
+ {
12965
+ onClick: (e) => {
12966
+ e.stopPropagation();
12967
+ setPreviewUrl(img.hdUrl);
12968
+ },
12969
+ className: "p-1.5 text-white/80 hover:text-white bg-white/15 hover:bg-white/25 backdrop-blur-sm rounded-md transition-colors",
12970
+ title: "\u653E\u5927\u9884\u89C8",
12971
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Image, { size: 14 })
12972
+ }
12973
+ )
12974
+ ] })
12975
+ ]
12976
+ },
12977
+ `img-${i}`
12978
+ ))
12979
+ ] });
12980
+ })() }),
12346
12981
  hasCustomResponses && customResult
12347
12982
  ] })
12348
12983
  ] });
@@ -12468,6 +13103,7 @@ var ToolCallCard3 = React20.memo(function ToolCallCard4({
12468
13103
  result: toolCall.result,
12469
13104
  mediaUrls,
12470
13105
  config,
13106
+ toolCallData: toolCall,
12471
13107
  defaultExpanded: false,
12472
13108
  onOpenArtifact
12473
13109
  }
@@ -12738,7 +13374,7 @@ var ComponentPendingCard = React20.memo(function ComponentPendingCard2({
12738
13374
  })() })
12739
13375
  ] });
12740
13376
  });
12741
- var ThoughtTimelineCard = React20.memo(function ThoughtTimelineCard2({ thought, config, isLoading = false }) {
13377
+ var ThoughtTimelineCard = React20.memo(function ThoughtTimelineCard2({ thought, config, isLoading = false, skipImages = false }) {
12742
13378
  const [expanded, setExpanded] = React20.useState(true);
12743
13379
  const { showItemTime } = useAgentStore();
12744
13380
  const thoughtType = thought?.type || "";
@@ -12762,9 +13398,9 @@ var ThoughtTimelineCard = React20.memo(function ThoughtTimelineCard2({ thought,
12762
13398
  return null;
12763
13399
  }
12764
13400
  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"));
13401
+ const hasConfirmationEvent = thought.raw.some((r3) => {
13402
+ const type = r3?.type || "";
13403
+ 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
13404
  });
12769
13405
  if (hasConfirmationEvent) {
12770
13406
  return null;
@@ -12776,12 +13412,19 @@ var ThoughtTimelineCard = React20.memo(function ThoughtTimelineCard2({ thought,
12776
13412
  if (!cleaned || !cleaned.trim()) return null;
12777
13413
  return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 py-2", children: [
12778
13414
  /* @__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" }),
13415
+ /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { content: cleaned, skipImages, config, variant: "thought" }),
12780
13416
  isLoading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-2 h-4 ml-0.5 bg-[#d8ff00] animate-pulse rounded-sm" })
12781
13417
  ] }) }),
12782
13418
  thought?.raw?.length > 0 && showItemTime && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-zinc-500 mt-2", children: new Date(
12783
13419
  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" }) })
13420
+ ).toLocaleTimeString("zh-CN", {
13421
+ year: "numeric",
13422
+ month: "2-digit",
13423
+ day: "2-digit",
13424
+ hour: "2-digit",
13425
+ minute: "2-digit",
13426
+ second: "2-digit"
13427
+ }) })
12785
13428
  ] }) });
12786
13429
  });
12787
13430
  var COMPONENT_TAGS_TO_STRIP = [
@@ -13781,7 +14424,8 @@ ${m.content}`;
13781
14424
  }
13782
14425
  var ThoughtBlockItem = React20.memo(function ThoughtBlockItem2({
13783
14426
  content,
13784
- config
14427
+ config,
14428
+ skipImages = false
13785
14429
  }) {
13786
14430
  const [expanded, setExpanded] = React20.useState(false);
13787
14431
  const lineCount = React20.useMemo(() => content.split("\n").length, [content]);
@@ -13794,7 +14438,7 @@ var ThoughtBlockItem = React20.memo(function ThoughtBlockItem2({
13794
14438
  "text-sm text-zinc-400 leading-relaxed prose prose-invert prose-sm max-w-none [&_*]:break-words",
13795
14439
  shouldToggle && !expanded ? "max-h-[3.5em] overflow-hidden" : ""
13796
14440
  ),
13797
- children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { content, skipImages: false, config, variant: "thought" })
14441
+ children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownContent, { content, skipImages, config, variant: "thought" })
13798
14442
  }
13799
14443
  ),
13800
14444
  shouldToggle && /* @__PURE__ */ jsxRuntime.jsx(
@@ -13980,7 +14624,6 @@ var MessageBubble = React20.memo(function MessageBubble2({
13980
14624
  const shouldShowAvatar = isUser ? showUserAvatar : showAssistantAvatar;
13981
14625
  const shouldShowName = isUser ? showUserName : showAssistantName;
13982
14626
  const shouldShowDescription = isUser ? showUserDescription : showAssistantDescription;
13983
- console.log("[MessageBubble] \u6E32\u67D3\u6D88\u606F\u6C14\u6CE1, config?.identity:", config?.identity);
13984
14627
  const resolveFileName = (url) => {
13985
14628
  try {
13986
14629
  const pathname = new URL(url, "http://dummy").pathname;
@@ -14041,7 +14684,8 @@ var MessageBubble = React20.memo(function MessageBubble2({
14041
14684
  {
14042
14685
  thought: item.data,
14043
14686
  config,
14044
- isLoading: showLoadingCursor
14687
+ isLoading: showLoadingCursor,
14688
+ skipImages: hasToolCallImages
14045
14689
  },
14046
14690
  item.id
14047
14691
  );
@@ -14141,7 +14785,8 @@ var MessageBubble = React20.memo(function MessageBubble2({
14141
14785
  ThoughtBlockItem,
14142
14786
  {
14143
14787
  content: block.content,
14144
- config
14788
+ config,
14789
+ skipImages: hasToolCallImages
14145
14790
  },
14146
14791
  `${block.ts}-${index}`
14147
14792
  )),
@@ -14186,15 +14831,23 @@ var MessageBubble = React20.memo(function MessageBubble2({
14186
14831
  const isVideo = att.type === "video" || att.type?.startsWith("video/") || isVideoUrl2(att.url);
14187
14832
  const isImage2 = att.type === "image" || att.type?.startsWith("image/") || !isVideo && /\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?|#|$)/i.test(att.url);
14188
14833
  const fileName = att.name || resolveFileName(att.url);
14834
+ if (att.url.includes("industryai")) {
14835
+ att.url = att.url.split("?")[0];
14836
+ }
14837
+ const assets = config?.asset?.transform?.({
14838
+ type: att.type,
14839
+ url: att.url,
14840
+ name: fileName
14841
+ }) || { url: att.url, type: att.type };
14189
14842
  if (isImage2 || isVideo) {
14190
14843
  return /* @__PURE__ */ jsxRuntime.jsxs(
14191
14844
  "button",
14192
14845
  {
14193
14846
  type: "button",
14194
- onClick: () => setPreviewMedia({ url: att.url, isVideo }),
14847
+ onClick: () => setPreviewMedia({ url: assets.url, isVideo }),
14195
14848
  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
14849
  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" }),
14850
+ 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
14851
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/20 opacity-0 group-hover/att:opacity-100 transition-opacity" })
14199
14852
  ]
14200
14853
  },
@@ -14299,7 +14952,14 @@ var MessageBubble = React20.memo(function MessageBubble2({
14299
14952
  ]
14300
14953
  }
14301
14954
  ) }),
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" }) })
14955
+ 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", {
14956
+ year: "numeric",
14957
+ month: "2-digit",
14958
+ day: "2-digit",
14959
+ hour: "2-digit",
14960
+ minute: "2-digit",
14961
+ second: "2-digit"
14962
+ }) })
14303
14963
  ] })
14304
14964
  ] });
14305
14965
  });
@@ -15174,9 +15834,24 @@ var DialogDescription = React20__namespace.forwardRef(({ className, ...props },
15174
15834
  ));
15175
15835
  DialogDescription.displayName = DialogPrimitive__namespace.Description.displayName;
15176
15836
 
15837
+ // ../../node_modules/clsx/dist/clsx.mjs
15838
+ function r2(e) {
15839
+ var t, f, n = "";
15840
+ if ("string" == typeof e || "number" == typeof e) n += e;
15841
+ else if ("object" == typeof e) if (Array.isArray(e)) {
15842
+ var o = e.length;
15843
+ for (t = 0; t < o; t++) e[t] && (f = r2(e[t])) && (n && (n += " "), n += f);
15844
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
15845
+ return n;
15846
+ }
15847
+ function clsx2() {
15848
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r2(e)) && (n && (n += " "), n += t);
15849
+ return n;
15850
+ }
15851
+
15177
15852
  // ../../node_modules/class-variance-authority/dist/index.mjs
15178
15853
  var falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
15179
- var cx = clsx;
15854
+ var cx = clsx2;
15180
15855
  var cva = (base, config) => (props) => {
15181
15856
  var _config_compoundVariants;
15182
15857
  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 +16669,10 @@ var Observer = class {
15994
16669
  });
15995
16670
  }
15996
16671
  };
15997
- this.custom = (jsx50, data) => {
16672
+ this.custom = (jsx59, data) => {
15998
16673
  const id = (data == null ? void 0 : data.id) || toastsCounter++;
15999
16674
  this.create({
16000
- jsx: jsx50(id),
16675
+ jsx: jsx59(id),
16001
16676
  id,
16002
16677
  ...data
16003
16678
  });
@@ -18498,7 +19173,57 @@ var ToolbarButton = React20.memo(function ToolbarButton2({
18498
19173
  }
18499
19174
  );
18500
19175
  });
18501
- var CodeBlock3 = React20.memo(function CodeBlock4({
19176
+ var artifactTypeConfig = {
19177
+ html: {
19178
+ icon: lucideReact.Globe,
19179
+ label: "HTML",
19180
+ color: "text-orange-400",
19181
+ bgColor: "bg-orange-500/10"
19182
+ },
19183
+ svg: {
19184
+ icon: lucideReact.Globe,
19185
+ label: "SVG",
19186
+ color: "text-orange-300",
19187
+ bgColor: "bg-orange-500/10"
19188
+ },
19189
+ markdown: {
19190
+ icon: lucideReact.FileText,
19191
+ label: "Markdown",
19192
+ color: "text-blue-400",
19193
+ bgColor: "bg-blue-500/10"
19194
+ },
19195
+ json: {
19196
+ icon: lucideReact.FileJson,
19197
+ label: "JSON",
19198
+ color: "text-yellow-400",
19199
+ bgColor: "bg-yellow-500/10"
19200
+ },
19201
+ code: {
19202
+ icon: lucideReact.FileCode,
19203
+ label: "Code",
19204
+ color: "text-purple-400",
19205
+ bgColor: "bg-purple-500/10"
19206
+ },
19207
+ text: {
19208
+ icon: lucideReact.FileText,
19209
+ label: "Text",
19210
+ color: "text-zinc-400",
19211
+ bgColor: "bg-zinc-500/10"
19212
+ },
19213
+ image: {
19214
+ icon: lucideReact.FileImage,
19215
+ label: "Image",
19216
+ color: "text-green-400",
19217
+ bgColor: "bg-green-500/10"
19218
+ },
19219
+ video: {
19220
+ icon: lucideReact.Video,
19221
+ label: "Video",
19222
+ color: "text-pink-400",
19223
+ bgColor: "bg-pink-500/10"
19224
+ }
19225
+ };
19226
+ var CodeBlock3 = React20.memo(function CodeBlock4({
18502
19227
  code: code3,
18503
19228
  language,
18504
19229
  showLineNumbers = true
@@ -18786,38 +19511,1305 @@ React20.memo(function JsonPreview2({ content }) {
18786
19511
  }
18787
19512
  return /* @__PURE__ */ jsxRuntime.jsx(CodeBlock3, { code: formattedJson, language: "json" });
18788
19513
  });
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
- };
19514
+ var CodeEditor = React20.memo(function CodeEditor2({
19515
+ code: code3,
19516
+ language,
19517
+ onChange
19518
+ }) {
19519
+ const textareaRef = React20.useRef(null);
19520
+ const handleKeyDown = React20.useCallback((e) => {
19521
+ if (e.key === "Tab") {
19522
+ e.preventDefault();
19523
+ const textarea = e.currentTarget;
19524
+ const start = textarea.selectionStart;
19525
+ const end = textarea.selectionEnd;
19526
+ const newValue = code3.substring(0, start) + " " + code3.substring(end);
19527
+ onChange(newValue);
19528
+ requestAnimationFrame(() => {
19529
+ textarea.selectionStart = textarea.selectionEnd = start + 2;
19530
+ });
19531
+ }
19532
+ }, [code3, onChange]);
19533
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-950 agent-sdk-light:bg-white", children: [
19534
+ /* @__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: [
19535
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { size: 12, className: "text-[#d8ff00]" }),
19536
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-[#d8ff00] font-medium", children: "\u7F16\u8F91\u6A21\u5F0F" }),
19537
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-zinc-500 agent-sdk-light:text-zinc-600 font-mono uppercase ml-2", children: language || "text" })
19538
+ ] }) }),
19539
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
19540
+ "textarea",
19541
+ {
19542
+ ref: textareaRef,
19543
+ value: code3,
19544
+ onChange: (e) => onChange(e.target.value),
19545
+ onKeyDown: handleKeyDown,
19546
+ 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",
19547
+ spellCheck: false,
19548
+ autoComplete: "off",
19549
+ autoCorrect: "off",
19550
+ autoCapitalize: "off"
19551
+ }
19552
+ ) })
19553
+ ] });
19554
+ });
19555
+ function useResolvedMediaUrl(content, options) {
19556
+ const { fileId, type = "image", config } = options || {};
19557
+ const [resolvedUrl, setResolvedUrl] = React20.useState(content);
19558
+ const [resolvedHdUrl, setResolvedHdUrl] = React20.useState(content);
19559
+ const [isLoading, setIsLoading] = React20.useState(true);
19560
+ React20.useEffect(() => {
19561
+ let active = true;
19562
+ setIsLoading(true);
19563
+ const resolve = async () => {
19564
+ const httpUrl = isHttpUrl(content) ? content : void 0;
19565
+ const asset = createAssetFromSource({
19566
+ fileId: fileId || (!httpUrl ? content : void 0),
19567
+ fileUrl: httpUrl,
19568
+ type
19569
+ });
19570
+ if (!asset) {
19571
+ if (active) {
19572
+ setResolvedUrl(content);
19573
+ setResolvedHdUrl(content);
19574
+ setIsLoading(false);
19575
+ }
19576
+ return;
19577
+ }
19578
+ const resolved = await resolveAssetForDisplay(asset, config);
19579
+ if (!active) return;
19580
+ if (resolved) {
19581
+ setResolvedUrl(resolved.url);
19582
+ setResolvedHdUrl(resolved.hdUrl || resolved.url);
19583
+ } else {
19584
+ setResolvedUrl(content);
19585
+ setResolvedHdUrl(content);
19586
+ }
19587
+ setIsLoading(false);
19588
+ };
19589
+ resolve();
19590
+ return () => {
19591
+ active = false;
19592
+ };
19593
+ }, [content, fileId, type, config]);
19594
+ return { url: resolvedUrl, hdUrl: resolvedHdUrl, isLoading };
19595
+ }
19596
+ function useResolvedThumbnailUrl(content, options) {
19597
+ const { fileId, type = "image", config, enabled = true } = options || {};
19598
+ const [url, setUrl] = React20.useState(null);
19599
+ React20.useEffect(() => {
19600
+ if (!enabled || !content) {
19601
+ setUrl(null);
19602
+ return;
19603
+ }
19604
+ if (type === "video") {
19605
+ setUrl(null);
19606
+ return;
19607
+ }
19608
+ let active = true;
19609
+ const resolve = async () => {
19610
+ const httpUrl = isHttpUrl(content) ? content : void 0;
19611
+ const asset = createAssetFromSource({
19612
+ fileId: fileId || (!httpUrl ? content : void 0),
19613
+ fileUrl: httpUrl,
19614
+ type
19615
+ });
19616
+ if (!asset) {
19617
+ if (active) setUrl(isHttpUrl(content) ? content : null);
19618
+ return;
19619
+ }
19620
+ const resolved = await resolveAssetForDisplay(asset, config);
19621
+ if (!active) return;
19622
+ setUrl(resolved?.url || (isHttpUrl(content) ? content : null));
19623
+ };
19624
+ resolve();
19625
+ return () => {
19626
+ active = false;
19627
+ };
19628
+ }, [content, fileId, type, config, enabled]);
19629
+ return url;
19630
+ }
19631
+ var ImageArtifactPreview = React20.memo(function ImageArtifactPreview2({
19632
+ content,
19633
+ metadata,
19634
+ config
19635
+ }) {
19636
+ const [isZoomed, setIsZoomed] = React20.useState(false);
19637
+ const [showInfo, setShowInfo] = React20.useState(true);
19638
+ const [imgNaturalSize, setImgNaturalSize] = React20.useState(null);
19639
+ const [copied, setCopied] = React20.useState(null);
19640
+ const { hdUrl: resolvedHdUrl } = useResolvedMediaUrl(content, {
19641
+ fileId: metadata?.fileId,
19642
+ type: "image",
19643
+ config
19644
+ });
19645
+ const generationParams = metadata?.generationParams;
19646
+ const toolName = metadata?.toolName;
19647
+ const relation = metadata?.relation;
19648
+ const handleCopy = React20.useCallback(async (text3, label) => {
19649
+ try {
19650
+ await navigator.clipboard.writeText(text3);
19651
+ setCopied(label);
19652
+ setTimeout(() => setCopied(null), 2e3);
19653
+ } catch {
19654
+ }
19655
+ }, []);
19656
+ const handleDownload = React20.useCallback(() => {
19657
+ const link2 = document.createElement("a");
19658
+ link2.href = resolvedHdUrl;
19659
+ link2.download = `generated-${Date.now()}.png`;
19660
+ link2.target = "_blank";
19661
+ document.body.appendChild(link2);
19662
+ link2.click();
19663
+ document.body.removeChild(link2);
19664
+ }, [resolvedHdUrl]);
19665
+ const formatParamKey = (key) => {
19666
+ const keyMap = {
19667
+ content: "Prompt",
19668
+ width: "\u5BBD\u5EA6",
19669
+ height: "\u9AD8\u5EA6",
19670
+ batchSize: "\u751F\u6210\u6570\u91CF",
19671
+ steps: "\u6B65\u6570",
19672
+ cfg_scale: "CFG Scale",
19673
+ seed: "\u79CD\u5B50",
19674
+ sampler: "\u91C7\u6837\u5668",
19675
+ model: "\u6A21\u578B",
19676
+ negative_prompt: "\u53CD\u5411\u63D0\u793A\u8BCD",
19677
+ style: "\u98CE\u683C",
19678
+ quality: "\u8D28\u91CF"
19679
+ };
19680
+ return keyMap[key] || key.replace(/([A-Z])/g, " $1").replace(/_/g, " ").replace(/^./, (s) => s.toUpperCase());
19681
+ };
19682
+ const promptText = generationParams?.content ? String(generationParams.content) : null;
19683
+ const otherParams = generationParams ? Object.entries(generationParams).filter(([key]) => key !== "content") : [];
19684
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-950 agent-sdk-light:bg-white", children: [
19685
+ /* @__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: [
19686
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 opacity-[0.03]", style: {
19687
+ 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%)",
19688
+ backgroundSize: "20px 20px",
19689
+ backgroundPosition: "0 0, 0 10px, 10px -10px, -10px 0px"
19690
+ } }),
19691
+ /* @__PURE__ */ jsxRuntime.jsx(
19692
+ "div",
19693
+ {
19694
+ className: cn2(
19695
+ "relative transition-transform duration-300 cursor-zoom-in z-10",
19696
+ isZoomed && "cursor-zoom-out scale-[2] z-50"
19697
+ ),
19698
+ onClick: () => setIsZoomed((prev) => !prev),
19699
+ children: /* @__PURE__ */ jsxRuntime.jsx(
19700
+ "img",
19701
+ {
19702
+ src: resolvedHdUrl,
19703
+ alt: "Generated image",
19704
+ className: "max-w-full max-h-[60vh] object-contain rounded-lg shadow-2xl",
19705
+ loading: "lazy",
19706
+ onLoad: (e) => {
19707
+ const img = e.target;
19708
+ setImgNaturalSize({ width: img.naturalWidth, height: img.naturalHeight });
19709
+ }
19710
+ }
19711
+ )
19712
+ }
19713
+ ),
19714
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-4 right-4 flex items-center gap-1.5 z-20", children: [
19715
+ /* @__PURE__ */ jsxRuntime.jsx(
19716
+ "button",
19717
+ {
19718
+ onClick: () => setShowInfo((prev) => !prev),
19719
+ className: cn2(
19720
+ "p-2 rounded-lg backdrop-blur-sm transition-colors",
19721
+ showInfo ? "bg-[#d8ff00]/20 text-[#d8ff00]" : "bg-black/30 text-white/70 hover:text-white hover:bg-black/50"
19722
+ ),
19723
+ title: showInfo ? "\u9690\u85CF\u8BE6\u60C5" : "\u663E\u793A\u8BE6\u60C5",
19724
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 16 })
19725
+ }
19726
+ ),
19727
+ /* @__PURE__ */ jsxRuntime.jsx(
19728
+ "button",
19729
+ {
19730
+ onClick: handleDownload,
19731
+ className: "p-2 bg-black/30 text-white/70 hover:text-white hover:bg-black/50 rounded-lg backdrop-blur-sm transition-colors",
19732
+ title: "\u4E0B\u8F7D\u56FE\u7247",
19733
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 16 })
19734
+ }
19735
+ )
19736
+ ] }),
19737
+ 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: [
19738
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileImage, { size: 12, className: "text-white/60" }),
19739
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-white/80 font-mono", children: [
19740
+ imgNaturalSize.width,
19741
+ " \xD7 ",
19742
+ imgNaturalSize.height
19743
+ ] })
19744
+ ] })
19745
+ ] }),
19746
+ 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: [
19747
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
19748
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
19749
+ /* @__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]" }) }),
19750
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
19751
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-zinc-200 agent-sdk-light:text-zinc-800", children: toolName || "\u56FE\u7247\u751F\u6210" }),
19752
+ 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 })
19753
+ ] })
19754
+ ] }),
19755
+ metadata?.taskId && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-zinc-600 font-mono", children: [
19756
+ "Task: ",
19757
+ metadata.taskId
19758
+ ] })
19759
+ ] }),
19760
+ promptText && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
19761
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
19762
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-500 uppercase tracking-wider font-medium", children: "Prompt" }),
19763
+ /* @__PURE__ */ jsxRuntime.jsx(
19764
+ "button",
19765
+ {
19766
+ onClick: () => handleCopy(promptText, "prompt"),
19767
+ className: "text-[10px] text-zinc-500 hover:text-zinc-300 transition-colors flex items-center gap-1",
19768
+ children: copied === "prompt" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19769
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCheck, { size: 10, className: "text-green-400" }),
19770
+ " \u5DF2\u590D\u5236"
19771
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19772
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { size: 10 }),
19773
+ " \u590D\u5236"
19774
+ ] })
19775
+ }
19776
+ )
19777
+ ] }),
19778
+ /* @__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 }) })
19779
+ ] }),
19780
+ otherParams.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
19781
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-500 uppercase tracking-wider font-medium", children: "\u53C2\u6570" }),
19782
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-2", children: otherParams.map(([key, value]) => {
19783
+ const displayValue = String(value);
19784
+ if (displayValue.length > 100) return null;
19785
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19786
+ "div",
19787
+ {
19788
+ 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",
19789
+ children: [
19790
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-500 agent-sdk-light:text-zinc-500", children: formatParamKey(key) }),
19791
+ /* @__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 })
19792
+ ]
19793
+ },
19794
+ key
19795
+ );
19796
+ }) })
19797
+ ] }),
19798
+ (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: [
19799
+ metadata.workId && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
19800
+ "ID: ",
19801
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-zinc-500", children: metadata.workId })
19802
+ ] }),
19803
+ metadata.fileId && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate max-w-[200px]", title: metadata.fileId, children: [
19804
+ "File: ",
19805
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-zinc-500", children: metadata.fileId })
19806
+ ] })
19807
+ ] }) })
19808
+ ] }) })
19809
+ ] });
19810
+ });
19811
+ var VideoArtifactPreview = React20.memo(function VideoArtifactPreview2({
19812
+ content,
19813
+ metadata,
19814
+ config
19815
+ }) {
19816
+ const { hdUrl: resolvedUrl } = useResolvedMediaUrl(content, {
19817
+ fileId: metadata?.fileId,
19818
+ type: "video",
19819
+ config
19820
+ });
19821
+ const videoRef = React20.useRef(null);
19822
+ const progressRef = React20.useRef(null);
19823
+ const [isPlaying, setIsPlaying] = React20.useState(false);
19824
+ const [isMuted, setIsMuted] = React20.useState(false);
19825
+ const [duration, setDuration] = React20.useState(0);
19826
+ const [currentTime, setCurrentTime] = React20.useState(0);
19827
+ const [volume, setVolume] = React20.useState(1);
19828
+ const [isLoaded, setIsLoaded] = React20.useState(false);
19829
+ const [showVolumeSlider, setShowVolumeSlider] = React20.useState(false);
19830
+ const [posterUrl, setPosterUrl] = React20.useState(null);
19831
+ const [isDraggingProgress, setIsDraggingProgress] = React20.useState(false);
19832
+ const generationParams = metadata?.generationParams;
19833
+ const toolName = metadata?.toolName;
19834
+ React20.useEffect(() => {
19835
+ const video = document.createElement("video");
19836
+ video.crossOrigin = "anonymous";
19837
+ video.preload = "metadata";
19838
+ video.muted = true;
19839
+ video.addEventListener("loadeddata", () => {
19840
+ video.currentTime = Math.min(0.5, video.duration * 0.1);
19841
+ });
19842
+ video.addEventListener("seeked", () => {
19843
+ try {
19844
+ const canvas = document.createElement("canvas");
19845
+ canvas.width = video.videoWidth;
19846
+ canvas.height = video.videoHeight;
19847
+ const ctx = canvas.getContext("2d");
19848
+ if (ctx) {
19849
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
19850
+ setPosterUrl(canvas.toDataURL("image/jpeg", 0.8));
19851
+ }
19852
+ } catch {
19853
+ }
19854
+ video.remove();
19855
+ });
19856
+ video.addEventListener("error", () => video.remove());
19857
+ video.src = resolvedUrl;
19858
+ video.load();
19859
+ return () => {
19860
+ video.remove();
19861
+ };
19862
+ }, [resolvedUrl]);
19863
+ const handleLoadedMetadata = () => {
19864
+ const video = videoRef.current;
19865
+ if (video) {
19866
+ setDuration(video.duration);
19867
+ setIsLoaded(true);
19868
+ }
19869
+ };
19870
+ const handleTimeUpdate = () => {
19871
+ if (!isDraggingProgress && videoRef.current) {
19872
+ setCurrentTime(videoRef.current.currentTime);
19873
+ }
19874
+ };
19875
+ const togglePlay = React20.useCallback(() => {
19876
+ const video = videoRef.current;
19877
+ if (!video) return;
19878
+ if (isPlaying) {
19879
+ video.pause();
19880
+ setIsPlaying(false);
19881
+ } else {
19882
+ video.play().then(() => setIsPlaying(true)).catch(() => {
19883
+ });
19884
+ }
19885
+ }, [isPlaying]);
19886
+ const toggleMute = React20.useCallback(() => {
19887
+ const video = videoRef.current;
19888
+ if (video) {
19889
+ video.muted = !isMuted;
19890
+ setIsMuted(!isMuted);
19891
+ }
19892
+ }, [isMuted]);
19893
+ const handleVolumeChange = React20.useCallback((e) => {
19894
+ const newVolume = parseFloat(e.target.value);
19895
+ setVolume(newVolume);
19896
+ if (videoRef.current) {
19897
+ videoRef.current.volume = newVolume;
19898
+ if (newVolume === 0) {
19899
+ setIsMuted(true);
19900
+ videoRef.current.muted = true;
19901
+ } else if (isMuted) {
19902
+ setIsMuted(false);
19903
+ videoRef.current.muted = false;
19904
+ }
19905
+ }
19906
+ }, [isMuted]);
19907
+ const handleProgressClick = React20.useCallback((e) => {
19908
+ const video = videoRef.current;
19909
+ const bar = progressRef.current;
19910
+ if (!video || !bar || !duration) return;
19911
+ const rect = bar.getBoundingClientRect();
19912
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
19913
+ video.currentTime = ratio * duration;
19914
+ setCurrentTime(ratio * duration);
19915
+ }, [duration]);
19916
+ const handleProgressDrag = React20.useCallback((e) => {
19917
+ if (!isDraggingProgress) return;
19918
+ const bar = progressRef.current;
19919
+ if (!bar || !duration) return;
19920
+ const rect = bar.getBoundingClientRect();
19921
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
19922
+ setCurrentTime(ratio * duration);
19923
+ }, [isDraggingProgress, duration]);
19924
+ const handleProgressDragEnd = React20.useCallback(() => {
19925
+ if (isDraggingProgress && videoRef.current) {
19926
+ videoRef.current.currentTime = currentTime;
19927
+ }
19928
+ setIsDraggingProgress(false);
19929
+ }, [isDraggingProgress, currentTime]);
19930
+ React20.useEffect(() => {
19931
+ if (!isDraggingProgress) return;
19932
+ const handleMouseUp = () => handleProgressDragEnd();
19933
+ const handleMouseMove = (e) => {
19934
+ const bar = progressRef.current;
19935
+ if (!bar || !duration) return;
19936
+ const rect = bar.getBoundingClientRect();
19937
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
19938
+ setCurrentTime(ratio * duration);
19939
+ };
19940
+ window.addEventListener("mouseup", handleMouseUp);
19941
+ window.addEventListener("mousemove", handleMouseMove);
19942
+ return () => {
19943
+ window.removeEventListener("mouseup", handleMouseUp);
19944
+ window.removeEventListener("mousemove", handleMouseMove);
19945
+ };
19946
+ }, [isDraggingProgress, duration, handleProgressDragEnd]);
19947
+ const handleDownload = React20.useCallback(() => {
19948
+ const link2 = document.createElement("a");
19949
+ link2.href = resolvedUrl;
19950
+ link2.download = `video-${Date.now()}.mp4`;
19951
+ document.body.appendChild(link2);
19952
+ link2.click();
19953
+ document.body.removeChild(link2);
19954
+ }, [resolvedUrl]);
19955
+ const formatTime2 = (seconds) => {
19956
+ const mins = Math.floor(seconds / 60);
19957
+ const secs = Math.floor(seconds % 60);
19958
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
19959
+ };
19960
+ const progressPercent = duration > 0 ? currentTime / duration * 100 : 0;
19961
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-950 agent-sdk-light:bg-white", children: [
19962
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-hidden flex items-center justify-center bg-black relative group/player", children: [
19963
+ posterUrl && !isPlaying && !currentTime && /* @__PURE__ */ jsxRuntime.jsx(
19964
+ "img",
19965
+ {
19966
+ src: posterUrl,
19967
+ alt: "Video cover",
19968
+ className: "absolute inset-0 w-full h-full object-contain z-[1]"
19969
+ }
19970
+ ),
19971
+ /* @__PURE__ */ jsxRuntime.jsx(
19972
+ "video",
19973
+ {
19974
+ ref: videoRef,
19975
+ src: resolvedUrl,
19976
+ muted: isMuted,
19977
+ playsInline: true,
19978
+ preload: "metadata",
19979
+ onLoadedMetadata: handleLoadedMetadata,
19980
+ onTimeUpdate: handleTimeUpdate,
19981
+ onEnded: () => setIsPlaying(false),
19982
+ onClick: togglePlay,
19983
+ className: cn2(
19984
+ "max-w-full max-h-full object-contain cursor-pointer",
19985
+ !isPlaying && posterUrl && !currentTime && "opacity-0"
19986
+ )
19987
+ }
19988
+ ),
19989
+ !isPlaying && /* @__PURE__ */ jsxRuntime.jsx(
19990
+ "div",
19991
+ {
19992
+ className: "absolute inset-0 z-[2] flex items-center justify-center cursor-pointer",
19993
+ onClick: togglePlay,
19994
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn2(
19995
+ "w-16 h-16 rounded-full bg-white/15 backdrop-blur-md",
19996
+ "flex items-center justify-center",
19997
+ "transition-all duration-200",
19998
+ "group-hover/player:scale-110 group-hover/player:bg-white/25",
19999
+ "shadow-2xl shadow-black/30"
20000
+ ), 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" }) }) })
20001
+ }
20002
+ ),
20003
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn2(
20004
+ "absolute bottom-0 left-0 right-0 z-[3]",
20005
+ "bg-gradient-to-t from-black/90 via-black/50 to-transparent",
20006
+ "px-4 pb-3 pt-10",
20007
+ "transition-opacity duration-300",
20008
+ "opacity-0 group-hover/player:opacity-100",
20009
+ isPlaying ? "" : "opacity-100"
20010
+ ), children: [
20011
+ /* @__PURE__ */ jsxRuntime.jsx(
20012
+ "div",
20013
+ {
20014
+ ref: progressRef,
20015
+ className: "w-full h-1.5 bg-white/20 rounded-full mb-3 cursor-pointer group/progress hover:h-2 transition-all",
20016
+ onClick: handleProgressClick,
20017
+ onMouseDown: () => setIsDraggingProgress(true),
20018
+ onMouseMove: handleProgressDrag,
20019
+ children: /* @__PURE__ */ jsxRuntime.jsx(
20020
+ "div",
20021
+ {
20022
+ className: "h-full bg-[#d8ff00] rounded-full relative",
20023
+ style: { width: `${progressPercent}%` },
20024
+ 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" })
20025
+ }
20026
+ )
20027
+ }
20028
+ ),
20029
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
20030
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
20031
+ /* @__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: [
20032
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1" }),
20033
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1" })
20034
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" }) }) }),
20035
+ /* @__PURE__ */ jsxRuntime.jsxs(
20036
+ "div",
20037
+ {
20038
+ className: "flex items-center gap-1.5 relative",
20039
+ onMouseEnter: () => setShowVolumeSlider(true),
20040
+ onMouseLeave: () => setShowVolumeSlider(false),
20041
+ children: [
20042
+ /* @__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: [
20043
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
20044
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "9", x2: "17", y2: "15" }),
20045
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "17", y1: "9", x2: "23", y2: "15" })
20046
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
20047
+ /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
20048
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" }),
20049
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" })
20050
+ ] }) }),
20051
+ showVolumeSlider && /* @__PURE__ */ jsxRuntime.jsx(
20052
+ "input",
20053
+ {
20054
+ type: "range",
20055
+ min: "0",
20056
+ max: "1",
20057
+ step: "0.05",
20058
+ value: isMuted ? 0 : volume,
20059
+ onChange: handleVolumeChange,
20060
+ className: "w-20 h-1 appearance-none bg-white/20 rounded-full cursor-pointer accent-[#d8ff00]"
20061
+ }
20062
+ )
20063
+ ]
20064
+ }
20065
+ ),
20066
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/60 font-mono tabular-nums", children: [
20067
+ formatTime2(currentTime),
20068
+ " / ",
20069
+ formatTime2(duration)
20070
+ ] })
20071
+ ] }),
20072
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
20073
+ "button",
20074
+ {
20075
+ onClick: handleDownload,
20076
+ className: "p-1.5 text-white/60 hover:text-white transition-colors",
20077
+ title: "\u4E0B\u8F7D\u89C6\u9891",
20078
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 16 })
20079
+ }
20080
+ ) })
20081
+ ] })
20082
+ ] })
20083
+ ] }),
20084
+ 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: [
20085
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
20086
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 12, className: "text-[#d8ff00]" }),
20087
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-zinc-300 agent-sdk-light:text-zinc-700", children: toolName || "\u751F\u6210\u53C2\u6570" })
20088
+ ] }),
20089
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-4 gap-y-1.5", children: Object.entries(generationParams).map(([key, value]) => {
20090
+ const displayValue = String(value);
20091
+ if (displayValue.length > 80) return null;
20092
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs", children: [
20093
+ /* @__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()) }),
20094
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-300 agent-sdk-light:text-zinc-700 font-mono", children: displayValue })
20095
+ ] }, key);
20096
+ }) }),
20097
+ 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: [
20098
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-500 agent-sdk-light:text-zinc-500 uppercase tracking-wider", children: "Prompt" }),
20099
+ /* @__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) })
20100
+ ] })
20101
+ ] }) })
20102
+ ] });
20103
+ });
20104
+ function htmlToMarkdown(element) {
20105
+ const lines = [];
20106
+ function processNode(node2, listDepth = 0) {
20107
+ if (node2.nodeType === Node.TEXT_NODE) {
20108
+ return node2.textContent || "";
20109
+ }
20110
+ if (node2.nodeType !== Node.ELEMENT_NODE) return "";
20111
+ const el = node2;
20112
+ const tag = el.tagName.toLowerCase();
20113
+ const childContent = () => {
20114
+ let result = "";
20115
+ el.childNodes.forEach((child) => {
20116
+ result += processNode(child, listDepth);
20117
+ });
20118
+ return result;
20119
+ };
20120
+ switch (tag) {
20121
+ case "h1":
20122
+ return `# ${childContent().trim()}
20123
+
20124
+ `;
20125
+ case "h2":
20126
+ return `## ${childContent().trim()}
20127
+
20128
+ `;
20129
+ case "h3":
20130
+ return `### ${childContent().trim()}
20131
+
20132
+ `;
20133
+ case "h4":
20134
+ return `#### ${childContent().trim()}
20135
+
20136
+ `;
20137
+ case "h5":
20138
+ return `##### ${childContent().trim()}
20139
+
20140
+ `;
20141
+ case "h6":
20142
+ return `###### ${childContent().trim()}
20143
+
20144
+ `;
20145
+ case "p": {
20146
+ const inner = childContent().trim();
20147
+ return inner ? `${inner}
20148
+
20149
+ ` : "\n";
20150
+ }
20151
+ case "br":
20152
+ return "\n";
20153
+ case "strong":
20154
+ case "b":
20155
+ return `**${childContent()}**`;
20156
+ case "em":
20157
+ case "i":
20158
+ return `*${childContent()}*`;
20159
+ case "del":
20160
+ case "s":
20161
+ return `~~${childContent()}~~`;
20162
+ case "code": {
20163
+ if (el.parentElement?.tagName.toLowerCase() === "pre") {
20164
+ return childContent();
20165
+ }
20166
+ return `\`${childContent()}\``;
20167
+ }
20168
+ case "pre": {
20169
+ const codeEl = el.querySelector("code");
20170
+ const codeContent = codeEl ? codeEl.textContent || "" : el.textContent || "";
20171
+ const langClass = codeEl?.className?.match(/language-(\w+)/);
20172
+ const lang = langClass ? langClass[1] : "";
20173
+ return `\`\`\`${lang}
20174
+ ${codeContent.trim()}
20175
+ \`\`\`
20176
+
20177
+ `;
20178
+ }
20179
+ case "blockquote": {
20180
+ const inner = childContent().trim();
20181
+ return inner.split("\n").map((line) => `> ${line}`).join("\n") + "\n\n";
20182
+ }
20183
+ case "ul": {
20184
+ let result = "";
20185
+ el.childNodes.forEach((child) => {
20186
+ if (child.tagName?.toLowerCase() === "li") {
20187
+ const indent2 = " ".repeat(listDepth);
20188
+ const liContent = processListItem(child, listDepth);
20189
+ result += `${indent2}- ${liContent}
20190
+ `;
20191
+ }
20192
+ });
20193
+ return listDepth === 0 ? result + "\n" : result;
20194
+ }
20195
+ case "ol": {
20196
+ let result = "";
20197
+ let index = 1;
20198
+ el.childNodes.forEach((child) => {
20199
+ if (child.tagName?.toLowerCase() === "li") {
20200
+ const indent2 = " ".repeat(listDepth);
20201
+ const liContent = processListItem(child, listDepth);
20202
+ result += `${indent2}${index}. ${liContent}
20203
+ `;
20204
+ index++;
20205
+ }
20206
+ });
20207
+ return listDepth === 0 ? result + "\n" : result;
20208
+ }
20209
+ case "li": {
20210
+ return childContent();
20211
+ }
20212
+ case "a": {
20213
+ const href = el.getAttribute("href") || "";
20214
+ const text3 = childContent();
20215
+ return `[${text3}](${href})`;
20216
+ }
20217
+ case "img": {
20218
+ const src = el.getAttribute("src") || "";
20219
+ const alt = el.getAttribute("alt") || "";
20220
+ return `![${alt}](${src})`;
20221
+ }
20222
+ case "hr":
20223
+ return "---\n\n";
20224
+ case "table": {
20225
+ return processTable(el) + "\n";
20226
+ }
20227
+ case "input": {
20228
+ if (el.getAttribute("type") === "checkbox") {
20229
+ return el.hasAttribute("checked") ? "[x] " : "[ ] ";
20230
+ }
20231
+ return "";
20232
+ }
20233
+ case "div":
20234
+ case "span":
20235
+ case "section":
20236
+ case "article":
20237
+ case "main":
20238
+ case "header":
20239
+ case "footer":
20240
+ return childContent();
20241
+ default:
20242
+ return childContent();
20243
+ }
20244
+ }
20245
+ function processListItem(li, depth, _ordered) {
20246
+ let textParts = [];
20247
+ let subLists = "";
20248
+ li.childNodes.forEach((child) => {
20249
+ const childTag = child.tagName?.toLowerCase();
20250
+ if (childTag === "ul" || childTag === "ol") {
20251
+ subLists += processNode(child, depth + 1);
20252
+ } else {
20253
+ textParts.push(processNode(child, depth));
20254
+ }
20255
+ });
20256
+ let result = textParts.join("").trim();
20257
+ if (subLists) {
20258
+ result += "\n" + subLists;
20259
+ }
20260
+ return result;
20261
+ }
20262
+ function processTable(table) {
20263
+ const rows = [];
20264
+ table.querySelectorAll("tr").forEach((tr) => {
20265
+ const cells = [];
20266
+ tr.querySelectorAll("th, td").forEach((cell) => {
20267
+ cells.push((cell.textContent || "").trim());
20268
+ });
20269
+ rows.push(cells);
20270
+ });
20271
+ if (rows.length === 0) return "";
20272
+ const colCount = Math.max(...rows.map((r3) => r3.length));
20273
+ const colWidths = Array(colCount).fill(3);
20274
+ rows.forEach((row) => {
20275
+ row.forEach((cell, i) => {
20276
+ colWidths[i] = Math.max(colWidths[i], cell.length);
20277
+ });
20278
+ });
20279
+ let result = "";
20280
+ rows.forEach((row, rowIndex) => {
20281
+ const paddedCells = Array(colCount).fill("").map((_, i) => {
20282
+ const cell = row[i] || "";
20283
+ return cell.padEnd(colWidths[i]);
20284
+ });
20285
+ result += `| ${paddedCells.join(" | ")} |
20286
+ `;
20287
+ if (rowIndex === 0) {
20288
+ const separators = colWidths.map((w) => "-".repeat(w));
20289
+ result += `| ${separators.join(" | ")} |
20290
+ `;
20291
+ }
20292
+ });
20293
+ return result;
20294
+ }
20295
+ element.childNodes.forEach((child) => {
20296
+ lines.push(processNode(child));
20297
+ });
20298
+ return lines.join("").replace(/\n{3,}/g, "\n\n").trim() + "\n";
20299
+ }
20300
+ var MarkdownEditor = React20.memo(function MarkdownEditor2({
20301
+ content,
20302
+ onChange,
20303
+ config
20304
+ }) {
20305
+ const editorRef = React20.useRef(null);
20306
+ const textareaRef = React20.useRef(null);
20307
+ const [isSourceMode, setIsSourceMode] = React20.useState(false);
20308
+ const isInternalUpdate = React20.useRef(false);
20309
+ const lastMarkdownRef = React20.useRef(content);
20310
+ const contentToHtml = React20.useCallback((markdown) => {
20311
+ return marked.marked.parse(markdown, { breaks: true, gfm: true });
20312
+ }, []);
20313
+ React20.useEffect(() => {
20314
+ if (isSourceMode) return;
20315
+ if (isInternalUpdate.current) {
20316
+ isInternalUpdate.current = false;
20317
+ return;
20318
+ }
20319
+ if (editorRef.current && content !== lastMarkdownRef.current) {
20320
+ const html2 = contentToHtml(content);
20321
+ editorRef.current.innerHTML = html2;
20322
+ lastMarkdownRef.current = content;
20323
+ }
20324
+ }, [content, isSourceMode, contentToHtml]);
20325
+ React20.useEffect(() => {
20326
+ if (editorRef.current && !isSourceMode) {
20327
+ const html2 = contentToHtml(content);
20328
+ editorRef.current.innerHTML = html2;
20329
+ }
20330
+ }, []);
20331
+ const handleInput = React20.useCallback(() => {
20332
+ if (!editorRef.current) return;
20333
+ const markdown = htmlToMarkdown(editorRef.current);
20334
+ lastMarkdownRef.current = markdown;
20335
+ isInternalUpdate.current = true;
20336
+ onChange(markdown);
20337
+ }, [onChange]);
20338
+ const execCommand = React20.useCallback((command, value) => {
20339
+ editorRef.current?.focus();
20340
+ document.execCommand(command, false, value);
20341
+ requestAnimationFrame(() => handleInput());
20342
+ }, [handleInput]);
20343
+ const formatBlock = React20.useCallback((tag) => {
20344
+ execCommand("formatBlock", tag);
20345
+ }, [execCommand]);
20346
+ const insertHorizontalRule = React20.useCallback(() => {
20347
+ execCommand("insertHorizontalRule");
20348
+ }, [execCommand]);
20349
+ const insertLink = React20.useCallback(() => {
20350
+ const selection = window.getSelection();
20351
+ const selectedText = selection?.toString() || "\u94FE\u63A5\u6587\u672C";
20352
+ const url = prompt("\u8BF7\u8F93\u5165\u94FE\u63A5\u5730\u5740:", "https://");
20353
+ if (url) {
20354
+ if (selection && selection.rangeCount > 0) {
20355
+ const range = selection.getRangeAt(0);
20356
+ range.deleteContents();
20357
+ const anchor = document.createElement("a");
20358
+ anchor.href = url;
20359
+ anchor.textContent = selectedText;
20360
+ range.insertNode(anchor);
20361
+ range.setStartAfter(anchor);
20362
+ range.collapse(true);
20363
+ selection.removeAllRanges();
20364
+ selection.addRange(range);
20365
+ }
20366
+ requestAnimationFrame(() => handleInput());
20367
+ }
20368
+ }, [handleInput]);
20369
+ const insertImage = React20.useCallback(() => {
20370
+ const url = prompt("\u8BF7\u8F93\u5165\u56FE\u7247\u5730\u5740:", "https://");
20371
+ if (url) {
20372
+ execCommand("insertImage", url);
20373
+ }
20374
+ }, [execCommand]);
20375
+ const insertCodeBlock = React20.useCallback(() => {
20376
+ const editor = editorRef.current;
20377
+ if (!editor) return;
20378
+ const selection = window.getSelection();
20379
+ if (selection && selection.rangeCount > 0) {
20380
+ const range = selection.getRangeAt(0);
20381
+ const pre = document.createElement("pre");
20382
+ const code3 = document.createElement("code");
20383
+ code3.textContent = selection.toString() || "\u4EE3\u7801\u5185\u5BB9";
20384
+ pre.appendChild(code3);
20385
+ range.deleteContents();
20386
+ range.insertNode(pre);
20387
+ const paragraph2 = document.createElement("p");
20388
+ paragraph2.innerHTML = "<br>";
20389
+ pre.parentNode?.insertBefore(paragraph2, pre.nextSibling);
20390
+ range.setStart(paragraph2, 0);
20391
+ range.collapse(true);
20392
+ selection.removeAllRanges();
20393
+ selection.addRange(range);
20394
+ }
20395
+ requestAnimationFrame(() => handleInput());
20396
+ }, [handleInput]);
20397
+ const handleKeyDown = React20.useCallback((e) => {
20398
+ const isMod = e.metaKey || e.ctrlKey;
20399
+ if (isMod && e.key === "b") {
20400
+ e.preventDefault();
20401
+ execCommand("bold");
20402
+ } else if (isMod && e.key === "i") {
20403
+ e.preventDefault();
20404
+ execCommand("italic");
20405
+ } else if (isMod && e.key === "u") {
20406
+ e.preventDefault();
20407
+ execCommand("underline");
20408
+ } else if (isMod && e.key === "e") {
20409
+ e.preventDefault();
20410
+ const selection = window.getSelection();
20411
+ if (selection && selection.rangeCount > 0) {
20412
+ const selectedText = selection.toString() || "code";
20413
+ execCommand("insertHTML", `<code>${selectedText}</code>`);
20414
+ }
20415
+ } else if (e.key === "Tab") {
20416
+ e.preventDefault();
20417
+ execCommand("insertHTML", "&nbsp;&nbsp;&nbsp;&nbsp;");
20418
+ }
20419
+ }, [execCommand]);
20420
+ const handleSourceKeyDown = React20.useCallback((e) => {
20421
+ if (e.key === "Tab") {
20422
+ e.preventDefault();
20423
+ const textarea = e.currentTarget;
20424
+ const start = textarea.selectionStart;
20425
+ const end = textarea.selectionEnd;
20426
+ const newValue = content.substring(0, start) + " " + content.substring(end);
20427
+ onChange(newValue);
20428
+ requestAnimationFrame(() => {
20429
+ textarea.selectionStart = textarea.selectionEnd = start + 2;
20430
+ });
20431
+ }
20432
+ }, [content, onChange]);
20433
+ const toggleSourceMode = React20.useCallback(() => {
20434
+ if (isSourceMode) {
20435
+ setIsSourceMode(false);
20436
+ requestAnimationFrame(() => {
20437
+ if (editorRef.current) {
20438
+ const html2 = contentToHtml(content);
20439
+ editorRef.current.innerHTML = html2;
20440
+ lastMarkdownRef.current = content;
20441
+ }
20442
+ });
20443
+ } else {
20444
+ if (editorRef.current) {
20445
+ const markdown = htmlToMarkdown(editorRef.current);
20446
+ lastMarkdownRef.current = markdown;
20447
+ isInternalUpdate.current = true;
20448
+ onChange(markdown);
20449
+ }
20450
+ setIsSourceMode(true);
20451
+ }
20452
+ }, [isSourceMode, content, contentToHtml, onChange]);
20453
+ const toolbarGroups = React20.useMemo(() => [
20454
+ {
20455
+ items: [
20456
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-[11px]", children: "B" }), label: "\u52A0\u7C97 (\u2318B)", onClick: () => execCommand("bold") },
20457
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic text-[11px]", children: "I" }), label: "\u659C\u4F53 (\u2318I)", onClick: () => execCommand("italic") },
20458
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-through text-[11px]", children: "S" }), label: "\u5220\u9664\u7EBF", onClick: () => execCommand("strikeThrough") },
20459
+ { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 13 }), label: "\u884C\u5185\u4EE3\u7801 (\u2318E)", onClick: () => {
20460
+ const selection = window.getSelection();
20461
+ const text3 = selection?.toString() || "code";
20462
+ execCommand("insertHTML", `<code>${text3}</code>`);
20463
+ } }
20464
+ ]
20465
+ },
20466
+ {
20467
+ items: [
20468
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold", children: "H1" }), label: "\u4E00\u7EA7\u6807\u9898", onClick: () => formatBlock("h1") },
20469
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold", children: "H2" }), label: "\u4E8C\u7EA7\u6807\u9898", onClick: () => formatBlock("h2") },
20470
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-bold", children: "H3" }), label: "\u4E09\u7EA7\u6807\u9898", onClick: () => formatBlock("h3") }
20471
+ ]
20472
+ },
20473
+ {
20474
+ items: [
20475
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: "\u2022" }), label: "\u65E0\u5E8F\u5217\u8868", onClick: () => execCommand("insertUnorderedList") },
20476
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: "1." }), label: "\u6709\u5E8F\u5217\u8868", onClick: () => execCommand("insertOrderedList") },
20477
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: "\u275D" }), label: "\u5F15\u7528", onClick: () => formatBlock("blockquote") }
20478
+ ]
20479
+ },
20480
+ {
20481
+ items: [
20482
+ { icon: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px]", children: "\u2014" }), label: "\u5206\u5272\u7EBF", onClick: insertHorizontalRule },
20483
+ { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { size: 13 }), label: "\u94FE\u63A5", onClick: insertLink },
20484
+ { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileImage, { size: 13 }), label: "\u56FE\u7247", onClick: insertImage },
20485
+ { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 13, className: "opacity-60" }), label: "\u4EE3\u7801\u5757", onClick: insertCodeBlock }
20486
+ ]
20487
+ }
20488
+ ], [execCommand, formatBlock, insertHorizontalRule, insertLink, insertImage, insertCodeBlock]);
20489
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-950 agent-sdk-light:bg-white", children: [
20490
+ /* @__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: [
20491
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 mr-2", children: [
20492
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { size: 12, className: "text-[#d8ff00]" }),
20493
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-[#d8ff00] font-medium whitespace-nowrap", children: isSourceMode ? "Markdown \u6E90\u7801" : "\u5BCC\u6587\u672C\u7F16\u8F91" })
20494
+ ] }),
20495
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-zinc-700/50 agent-sdk-light:bg-zinc-300 mx-1" }),
20496
+ !isSourceMode && toolbarGroups.map((group, groupIndex) => /* @__PURE__ */ jsxRuntime.jsxs(React20__namespace.default.Fragment, { children: [
20497
+ group.items.map((action, actionIndex) => /* @__PURE__ */ jsxRuntime.jsx(
20498
+ "button",
20499
+ {
20500
+ onClick: action.onClick,
20501
+ 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",
20502
+ title: action.label,
20503
+ children: action.icon
20504
+ },
20505
+ actionIndex
20506
+ )),
20507
+ 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" })
20508
+ ] }, groupIndex)),
20509
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-zinc-700/50 agent-sdk-light:bg-zinc-300 mx-1 ml-auto" }),
20510
+ /* @__PURE__ */ jsxRuntime.jsxs(
20511
+ "button",
20512
+ {
20513
+ onClick: toggleSourceMode,
20514
+ className: cn2(
20515
+ "p-1.5 flex items-center gap-1 text-[11px] rounded transition-colors whitespace-nowrap",
20516
+ 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"
20517
+ ),
20518
+ title: isSourceMode ? "\u5207\u6362\u5230\u5BCC\u6587\u672C\u6A21\u5F0F" : "\u5207\u6362\u5230\u6E90\u7801\u6A21\u5F0F",
20519
+ children: [
20520
+ isSourceMode ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 13 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 13 }),
20521
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: isSourceMode ? "\u5BCC\u6587\u672C" : "\u6E90\u7801" })
20522
+ ]
20523
+ }
20524
+ )
20525
+ ] }),
20526
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: isSourceMode ? (
20527
+ /* 源码编辑模式 */
20528
+ /* @__PURE__ */ jsxRuntime.jsx(
20529
+ "textarea",
20530
+ {
20531
+ ref: textareaRef,
20532
+ value: content,
20533
+ onChange: (e) => onChange(e.target.value),
20534
+ onKeyDown: handleSourceKeyDown,
20535
+ 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",
20536
+ style: { fontFamily: '"SF Mono", "Fira Code", "JetBrains Mono", Menlo, Monaco, monospace' },
20537
+ spellCheck: false,
20538
+ autoComplete: "off",
20539
+ autoCorrect: "off",
20540
+ autoCapitalize: "off",
20541
+ placeholder: "\u7F16\u5199 Markdown \u6E90\u7801..."
20542
+ }
20543
+ )
20544
+ ) : (
20545
+ /* WYSIWYG 富文本编辑模式 */
20546
+ /* @__PURE__ */ jsxRuntime.jsx(
20547
+ "div",
20548
+ {
20549
+ ref: editorRef,
20550
+ contentEditable: true,
20551
+ suppressContentEditableWarning: true,
20552
+ onInput: handleInput,
20553
+ onKeyDown: handleKeyDown,
20554
+ 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",
20555
+ "data-placeholder": "\u5F00\u59CB\u7F16\u5199\u5185\u5BB9..."
20556
+ }
20557
+ )
20558
+ ) })
20559
+ ] });
20560
+ });
20561
+ var ArtifactThumbnail = React20.memo(function ArtifactThumbnail2({
20562
+ artifact,
20563
+ config
20564
+ }) {
20565
+ const typeConf = artifactTypeConfig[artifact.type] || artifactTypeConfig.text;
20566
+ const TypeIcon = typeConf.icon;
20567
+ const isMedia = artifact.type === "image" || artifact.type === "video";
20568
+ const thumbnailUrl = useResolvedThumbnailUrl(
20569
+ artifact.currentContent,
20570
+ {
20571
+ fileId: artifact.metadata?.fileId,
20572
+ type: artifact.type,
20573
+ config,
20574
+ enabled: isMedia
20575
+ }
20576
+ );
20577
+ if (isMedia && thumbnailUrl) {
20578
+ return /* @__PURE__ */ jsxRuntime.jsx(
20579
+ "img",
20580
+ {
20581
+ src: thumbnailUrl,
20582
+ alt: "",
20583
+ className: "w-6 h-6 rounded object-cover flex-shrink-0",
20584
+ loading: "lazy"
20585
+ }
20586
+ );
20587
+ }
20588
+ 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 }) });
20589
+ });
20590
+ var ArtifactTabs = React20.memo(function ArtifactTabs2({
20591
+ artifacts,
20592
+ activeId,
20593
+ onSwitch,
20594
+ onClose,
20595
+ onReorder,
20596
+ config
20597
+ }) {
20598
+ const scrollContainerRef = React20.useRef(null);
20599
+ const [canScrollLeft, setCanScrollLeft] = React20.useState(false);
20600
+ const [canScrollRight, setCanScrollRight] = React20.useState(false);
20601
+ const [showAllPanel, setShowAllPanel] = React20.useState(false);
20602
+ const allPanelRef = React20.useRef(null);
20603
+ const [dragState, setDragState] = React20.useState({ draggingId: null, dragOverId: null, dragStartX: 0, isDragging: false });
20604
+ const checkScrollState = React20.useCallback(() => {
20605
+ const container = scrollContainerRef.current;
20606
+ if (!container) return;
20607
+ setCanScrollLeft(container.scrollLeft > 2);
20608
+ setCanScrollRight(container.scrollLeft < container.scrollWidth - container.clientWidth - 2);
20609
+ }, []);
20610
+ React20.useEffect(() => {
20611
+ checkScrollState();
20612
+ const container = scrollContainerRef.current;
20613
+ if (!container) return;
20614
+ container.addEventListener("scroll", checkScrollState, { passive: true });
20615
+ const resizeObserver = new ResizeObserver(checkScrollState);
20616
+ resizeObserver.observe(container);
20617
+ return () => {
20618
+ container.removeEventListener("scroll", checkScrollState);
20619
+ resizeObserver.disconnect();
20620
+ };
20621
+ }, [checkScrollState, artifacts.length]);
20622
+ React20.useEffect(() => {
20623
+ if (!showAllPanel) return;
20624
+ const handleClickOutside = (e) => {
20625
+ if (allPanelRef.current && !allPanelRef.current.contains(e.target)) {
20626
+ setShowAllPanel(false);
20627
+ }
20628
+ };
20629
+ document.addEventListener("mousedown", handleClickOutside);
20630
+ return () => document.removeEventListener("mousedown", handleClickOutside);
20631
+ }, [showAllPanel]);
20632
+ const scrollBy = React20.useCallback((direction) => {
20633
+ const container = scrollContainerRef.current;
20634
+ if (!container) return;
20635
+ const scrollAmount = 200;
20636
+ container.scrollBy({
20637
+ left: direction === "left" ? -scrollAmount : scrollAmount,
20638
+ behavior: "smooth"
20639
+ });
20640
+ }, []);
20641
+ const handleDragStart = React20.useCallback((e, artifactId) => {
20642
+ e.dataTransfer.effectAllowed = "move";
20643
+ e.dataTransfer.setData("text/plain", artifactId);
20644
+ setDragState((prev) => ({ ...prev, draggingId: artifactId, dragStartX: e.clientX, isDragging: true }));
20645
+ }, []);
20646
+ const handleDragOver = React20.useCallback((e, artifactId) => {
20647
+ e.preventDefault();
20648
+ e.dataTransfer.dropEffect = "move";
20649
+ setDragState((prev) => ({ ...prev, dragOverId: artifactId }));
20650
+ }, []);
20651
+ const handleDragEnd = React20.useCallback(() => {
20652
+ setDragState({ draggingId: null, dragOverId: null, dragStartX: 0, isDragging: false });
20653
+ }, []);
20654
+ const handleDrop = React20.useCallback((e, targetId) => {
20655
+ e.preventDefault();
20656
+ const sourceId = e.dataTransfer.getData("text/plain");
20657
+ if (!sourceId || sourceId === targetId || !onReorder) {
20658
+ handleDragEnd();
20659
+ return;
20660
+ }
20661
+ const fromIndex = artifacts.findIndex((a) => a.id === sourceId);
20662
+ const toIndex = artifacts.findIndex((a) => a.id === targetId);
20663
+ if (fromIndex !== -1 && toIndex !== -1 && fromIndex !== toIndex) {
20664
+ onReorder(fromIndex, toIndex);
20665
+ }
20666
+ handleDragEnd();
20667
+ }, [artifacts, onReorder, handleDragEnd]);
20668
+ const handleCloseTab = React20.useCallback((e, artifactId) => {
20669
+ e.stopPropagation();
20670
+ if (!onClose) return;
20671
+ if (artifactId === activeId && artifacts.length > 1) {
20672
+ const currentIndex = artifacts.findIndex((a) => a.id === artifactId);
20673
+ const nextIndex = currentIndex < artifacts.length - 1 ? currentIndex + 1 : currentIndex - 1;
20674
+ if (nextIndex >= 0 && nextIndex < artifacts.length) {
20675
+ onSwitch(artifacts[nextIndex].id);
20676
+ }
20677
+ }
20678
+ onClose(artifactId);
20679
+ }, [onClose, activeId, artifacts, onSwitch]);
20680
+ if (artifacts.length <= 1) return null;
20681
+ 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: [
20682
+ canScrollLeft && /* @__PURE__ */ jsxRuntime.jsx(
20683
+ "button",
20684
+ {
20685
+ onClick: () => scrollBy("left"),
20686
+ className: "flex-shrink-0 p-1 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800/50 transition-colors z-10",
20687
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 14 })
20688
+ }
20689
+ ),
20690
+ /* @__PURE__ */ jsxRuntime.jsx(
20691
+ "div",
20692
+ {
20693
+ ref: scrollContainerRef,
20694
+ className: "flex items-center gap-0.5 px-1 py-1 overflow-x-auto flex-1 min-w-0",
20695
+ style: { scrollbarWidth: "none", msOverflowStyle: "none" },
20696
+ children: artifacts.map((artifact) => {
20697
+ const typeConf = artifactTypeConfig[artifact.type] || artifactTypeConfig.text;
20698
+ const isActive = artifact.id === activeId;
20699
+ const isDragging = dragState.draggingId === artifact.id;
20700
+ const isDragOver = dragState.dragOverId === artifact.id && dragState.draggingId !== artifact.id;
20701
+ const TypeIcon = typeConf.icon;
20702
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20703
+ "div",
20704
+ {
20705
+ draggable: !!onReorder,
20706
+ onDragStart: (e) => handleDragStart(e, artifact.id),
20707
+ onDragOver: (e) => handleDragOver(e, artifact.id),
20708
+ onDragEnd: handleDragEnd,
20709
+ onDrop: (e) => handleDrop(e, artifact.id),
20710
+ onClick: () => onSwitch(artifact.id),
20711
+ className: cn2(
20712
+ "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",
20713
+ 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",
20714
+ isDragging && "opacity-40",
20715
+ isDragOver && "ring-1 ring-[#d8ff00]/50",
20716
+ onReorder && "cursor-grab active:cursor-grabbing"
20717
+ ),
20718
+ title: artifact.title,
20719
+ children: [
20720
+ /* @__PURE__ */ jsxRuntime.jsx(TypeIcon, { size: 12, className: cn2("flex-shrink-0", isActive ? typeConf.color : "") }),
20721
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[120px] truncate", children: artifact.title }),
20722
+ onClose && /* @__PURE__ */ jsxRuntime.jsx(
20723
+ "button",
20724
+ {
20725
+ onClick: (e) => handleCloseTab(e, artifact.id),
20726
+ className: cn2(
20727
+ "ml-0.5 p-0.5 rounded transition-colors flex-shrink-0",
20728
+ 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"
20729
+ ),
20730
+ title: "\u5173\u95ED",
20731
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 10 })
20732
+ }
20733
+ )
20734
+ ]
20735
+ },
20736
+ artifact.id
20737
+ );
20738
+ })
20739
+ }
20740
+ ),
20741
+ canScrollRight && /* @__PURE__ */ jsxRuntime.jsx(
20742
+ "button",
20743
+ {
20744
+ onClick: () => scrollBy("right"),
20745
+ className: "flex-shrink-0 p-1 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800/50 transition-colors z-10",
20746
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 14 })
20747
+ }
20748
+ ),
20749
+ artifacts.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-shrink-0", ref: allPanelRef, children: [
20750
+ /* @__PURE__ */ jsxRuntime.jsx(
20751
+ "button",
20752
+ {
20753
+ onClick: () => setShowAllPanel((prev) => !prev),
20754
+ className: cn2(
20755
+ "p-1.5 mr-1 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800/50 rounded transition-colors",
20756
+ showAllPanel && "bg-zinc-800 text-zinc-300"
20757
+ ),
20758
+ title: "\u5168\u90E8\u4EA7\u7269",
20759
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGrid, { size: 14 })
20760
+ }
20761
+ ),
20762
+ 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: [
20763
+ /* @__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: [
20764
+ "\u5168\u90E8\u4EA7\u7269 (",
20765
+ artifacts.length,
20766
+ ")"
20767
+ ] }) }),
20768
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-1.5 grid grid-cols-1 gap-0.5", children: artifacts.map((artifact) => {
20769
+ const typeConf = artifactTypeConfig[artifact.type] || artifactTypeConfig.text;
20770
+ const isActive = artifact.id === activeId;
20771
+ typeConf.icon;
20772
+ return /* @__PURE__ */ jsxRuntime.jsxs(
20773
+ "button",
20774
+ {
20775
+ onClick: () => {
20776
+ onSwitch(artifact.id);
20777
+ setShowAllPanel(false);
20778
+ },
20779
+ className: cn2(
20780
+ "flex items-center gap-2.5 px-2.5 py-2 text-xs rounded-md transition-all w-full text-left",
20781
+ 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"
20782
+ ),
20783
+ children: [
20784
+ /* @__PURE__ */ jsxRuntime.jsx(ArtifactThumbnail, { artifact, config }),
20785
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
20786
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate font-medium", children: artifact.title }),
20787
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[10px] text-zinc-600 agent-sdk-light:text-zinc-500 mt-0.5", children: [
20788
+ typeConf.label,
20789
+ artifact.language && artifact.language !== artifact.type && ` \u2022 ${artifact.language}`
20790
+ ] })
20791
+ ] }),
20792
+ onClose && /* @__PURE__ */ jsxRuntime.jsx(
20793
+ "button",
20794
+ {
20795
+ onClick: (e) => {
20796
+ e.stopPropagation();
20797
+ handleCloseTab(e, artifact.id);
20798
+ },
20799
+ className: "p-1 text-zinc-600 hover:text-zinc-300 hover:bg-zinc-700 rounded transition-colors flex-shrink-0",
20800
+ title: "\u5173\u95ED",
20801
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 12 })
20802
+ }
20803
+ )
20804
+ ]
20805
+ },
20806
+ artifact.id
20807
+ );
20808
+ }) })
20809
+ ] })
20810
+ ] })
20811
+ ] });
20812
+ });
18821
20813
  var ArtifactViewer = React20.memo(function ArtifactViewer2({
18822
20814
  artifact,
18823
20815
  isOpen,
@@ -18826,41 +20818,127 @@ var ArtifactViewer = React20.memo(function ArtifactViewer2({
18826
20818
  defaultView = "preview",
18827
20819
  isFullscreen,
18828
20820
  onFullscreenToggle,
18829
- embedded = false
20821
+ embedded = false,
20822
+ artifacts = [],
20823
+ activeArtifactId,
20824
+ onSwitchArtifact,
20825
+ onCloseArtifact,
20826
+ onReorderArtifacts,
20827
+ onContentChange,
20828
+ onSave
18830
20829
  }) {
18831
20830
  const [viewMode, setViewMode] = React20.useState(defaultView);
20831
+ const [isEditing, setIsEditing] = React20.useState(false);
20832
+ const [editContent, setEditContent] = React20.useState("");
20833
+ const [undoStack, setUndoStack] = React20.useState([]);
20834
+ const [redoStack, setRedoStack] = React20.useState([]);
18832
20835
  React20.useEffect(() => {
18833
20836
  if (artifact) {
18834
- if (artifact.type === "html" || artifact.type === "markdown") {
20837
+ if (artifact.type === "html" || artifact.type === "markdown" || artifact.type === "svg") {
18835
20838
  setViewMode("preview");
18836
20839
  } else {
18837
20840
  setViewMode("code");
18838
20841
  }
20842
+ setIsEditing(false);
20843
+ setEditContent(artifact.content);
20844
+ setUndoStack([]);
20845
+ setRedoStack([]);
18839
20846
  }
18840
20847
  }, [artifact?.id]);
20848
+ React20.useEffect(() => {
20849
+ if (artifact && !isEditing) {
20850
+ setEditContent(artifact.content);
20851
+ }
20852
+ }, [artifact?.content, isEditing]);
18841
20853
  if (!artifact) return null;
18842
- const typeConfig = artifactTypeConfig[artifact.type];
18843
- const canPreview = artifact.type === "html" || artifact.type === "markdown";
20854
+ const typeConfig = artifactTypeConfig[artifact.type] || artifactTypeConfig.text;
20855
+ const canPreview = artifact.type === "html" || artifact.type === "markdown" || artifact.type === "svg";
20856
+ const canEdit = artifact.type !== "image" && artifact.type !== "video";
20857
+ const handleStartEdit = () => {
20858
+ setEditContent(artifact.content);
20859
+ setUndoStack([artifact.content]);
20860
+ setRedoStack([]);
20861
+ setIsEditing(true);
20862
+ setViewMode("code");
20863
+ };
20864
+ const handleEditChange = (newContent) => {
20865
+ setUndoStack((prev) => [...prev, editContent]);
20866
+ setRedoStack([]);
20867
+ setEditContent(newContent);
20868
+ onContentChange?.(newContent);
20869
+ };
20870
+ const handleUndo = () => {
20871
+ if (undoStack.length === 0) return;
20872
+ const previous2 = undoStack[undoStack.length - 1];
20873
+ setUndoStack((prev) => prev.slice(0, -1));
20874
+ setRedoStack((prev) => [...prev, editContent]);
20875
+ setEditContent(previous2);
20876
+ onContentChange?.(previous2);
20877
+ };
20878
+ const handleRedo = () => {
20879
+ if (redoStack.length === 0) return;
20880
+ const next = redoStack[redoStack.length - 1];
20881
+ setRedoStack((prev) => prev.slice(0, -1));
20882
+ setUndoStack((prev) => [...prev, editContent]);
20883
+ setEditContent(next);
20884
+ onContentChange?.(next);
20885
+ };
20886
+ const handleSave = () => {
20887
+ onSave?.(artifact.id, editContent);
20888
+ setIsEditing(false);
20889
+ };
20890
+ const handleCancelEdit = () => {
20891
+ setEditContent(artifact.content);
20892
+ setIsEditing(false);
20893
+ };
20894
+ const displayContent = isEditing ? editContent : artifact.content;
18844
20895
  const renderContent = () => {
20896
+ if (isEditing) {
20897
+ if (artifact.type === "markdown") {
20898
+ return /* @__PURE__ */ jsxRuntime.jsx(
20899
+ MarkdownEditor,
20900
+ {
20901
+ content: editContent,
20902
+ onChange: handleEditChange,
20903
+ config
20904
+ }
20905
+ );
20906
+ }
20907
+ return /* @__PURE__ */ jsxRuntime.jsx(
20908
+ CodeEditor,
20909
+ {
20910
+ code: editContent,
20911
+ language: artifact.language || artifact.type,
20912
+ onChange: handleEditChange
20913
+ }
20914
+ );
20915
+ }
20916
+ if (artifact.type === "image") {
20917
+ return /* @__PURE__ */ jsxRuntime.jsx(ImageArtifactPreview, { content: displayContent, metadata: artifact.metadata, config });
20918
+ }
20919
+ if (artifact.type === "video") {
20920
+ return /* @__PURE__ */ jsxRuntime.jsx(VideoArtifactPreview, { content: displayContent, metadata: artifact.metadata, config });
20921
+ }
18845
20922
  if (viewMode === "code" || !canPreview) {
18846
20923
  return /* @__PURE__ */ jsxRuntime.jsx(
18847
20924
  CodeBlock3,
18848
20925
  {
18849
- code: artifact.content,
20926
+ code: displayContent,
18850
20927
  language: artifact.language || artifact.type
18851
20928
  }
18852
20929
  );
18853
20930
  }
18854
20931
  switch (artifact.type) {
18855
20932
  case "html":
18856
- return /* @__PURE__ */ jsxRuntime.jsx(HtmlPreview3, { content: artifact.content });
20933
+ case "svg":
20934
+ return /* @__PURE__ */ jsxRuntime.jsx(HtmlPreview3, { content: displayContent });
18857
20935
  case "markdown":
18858
- return /* @__PURE__ */ jsxRuntime.jsx(MarkdownPreview, { content: artifact.content, config });
20936
+ return /* @__PURE__ */ jsxRuntime.jsx(MarkdownPreview, { content: displayContent, config });
18859
20937
  default:
18860
20938
  return /* @__PURE__ */ jsxRuntime.jsx(
18861
20939
  CodeBlock3,
18862
20940
  {
18863
- code: artifact.content,
20941
+ code: displayContent,
18864
20942
  language: artifact.language || artifact.type
18865
20943
  }
18866
20944
  );
@@ -18883,26 +20961,79 @@ var ArtifactViewer = React20.memo(function ArtifactViewer2({
18883
20961
  ] })
18884
20962
  ] }),
18885
20963
  /* @__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
- ),
20964
+ isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20965
+ /* @__PURE__ */ jsxRuntime.jsx(
20966
+ "button",
20967
+ {
20968
+ onClick: handleUndo,
20969
+ disabled: undoStack.length === 0,
20970
+ 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",
20971
+ title: "\u64A4\u9500",
20972
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Undo2, { size: 14 })
20973
+ }
20974
+ ),
20975
+ /* @__PURE__ */ jsxRuntime.jsx(
20976
+ "button",
20977
+ {
20978
+ onClick: handleRedo,
20979
+ disabled: redoStack.length === 0,
20980
+ 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",
20981
+ title: "\u91CD\u505A",
20982
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Redo2, { size: 14 })
20983
+ }
20984
+ ),
20985
+ /* @__PURE__ */ jsxRuntime.jsxs(
20986
+ "button",
20987
+ {
20988
+ onClick: handleSave,
20989
+ 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",
20990
+ title: "\u4FDD\u5B58",
20991
+ children: [
20992
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Save, { size: 12 }),
20993
+ "\u4FDD\u5B58"
20994
+ ]
20995
+ }
20996
+ ),
20997
+ /* @__PURE__ */ jsxRuntime.jsx(
20998
+ "button",
20999
+ {
21000
+ onClick: handleCancelEdit,
21001
+ className: "px-2.5 py-1 text-xs text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800 rounded-lg transition-colors",
21002
+ title: "\u53D6\u6D88\u7F16\u8F91",
21003
+ children: "\u53D6\u6D88"
21004
+ }
21005
+ )
21006
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
21007
+ canEdit && onContentChange && /* @__PURE__ */ jsxRuntime.jsx(
21008
+ "button",
21009
+ {
21010
+ onClick: handleStartEdit,
21011
+ 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",
21012
+ title: "\u7F16\u8F91",
21013
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { size: 14 })
21014
+ }
21015
+ ),
21016
+ onFullscreenToggle && /* @__PURE__ */ jsxRuntime.jsx(
21017
+ "button",
21018
+ {
21019
+ onClick: onFullscreenToggle,
21020
+ 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",
21021
+ title: isFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
21022
+ children: isFullscreen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minimize2, { size: 14 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { size: 14 })
21023
+ }
21024
+ ),
21025
+ canPreview && /* @__PURE__ */ jsxRuntime.jsx(
21026
+ ViewTabs,
21027
+ {
21028
+ activeView: viewMode,
21029
+ onViewChange: (v) => setViewMode(v),
21030
+ views: [
21031
+ { id: "preview", label: "Preview", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 12 }) },
21032
+ { id: "code", label: "Code", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code2, { size: 12 }) }
21033
+ ]
21034
+ }
21035
+ )
21036
+ ] }),
18906
21037
  /* @__PURE__ */ jsxRuntime.jsx(
18907
21038
  "button",
18908
21039
  {
@@ -18917,6 +21048,16 @@ var ArtifactViewer = React20.memo(function ArtifactViewer2({
18917
21048
  )
18918
21049
  ] })
18919
21050
  ] }),
21051
+ onSwitchArtifact && /* @__PURE__ */ jsxRuntime.jsx(
21052
+ ArtifactTabs,
21053
+ {
21054
+ artifacts,
21055
+ activeId: activeArtifactId ?? artifact.id,
21056
+ onSwitch: onSwitchArtifact,
21057
+ onClose: onCloseArtifact,
21058
+ onReorder: onReorderArtifacts
21059
+ }
21060
+ ),
18920
21061
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden", children: renderContent() })
18921
21062
  ] });
18922
21063
  }
@@ -18962,6 +21103,145 @@ var ArtifactViewer = React20.memo(function ArtifactViewer2({
18962
21103
  }
18963
21104
  );
18964
21105
  });
21106
+
21107
+ // src/utils/artifactExtractor.ts
21108
+ function getMediaTypeFromParamType(paramType) {
21109
+ const upper = paramType?.toUpperCase();
21110
+ if (upper === "IMAGE") return "image";
21111
+ if (upper === "VIDEO") return "video";
21112
+ return null;
21113
+ }
21114
+ function inferMediaTypeFromUrl(url) {
21115
+ if (!url) return null;
21116
+ const lower = url.toLowerCase();
21117
+ if (/\.(jpg|jpeg|png|gif|webp|svg|bmp)(\?|#|$)/.test(lower)) return "image";
21118
+ if (/\.(mp4|mov|webm|avi|mkv|m4v)(\?|#|$)/.test(lower)) return "video";
21119
+ if (/\.(jpg|jpeg|png|gif|webp)/.test(lower)) return "image";
21120
+ if (/\.(mp4|mov|webm)/.test(lower)) return "video";
21121
+ return null;
21122
+ }
21123
+ function extractArtifactsFromToolCall(toolCall, sessionId, messageId) {
21124
+ const artifacts = [];
21125
+ if (!toolCall || toolCall.status?.toLowerCase() !== "completed") return artifacts;
21126
+ const result = toolCall.result;
21127
+ if (!result) return artifacts;
21128
+ const toolName = result?.relation?.toolName || toolCall.name || "AI Tool";
21129
+ const args = toolCall.arguments || {};
21130
+ if (result.works && Array.isArray(result.works)) {
21131
+ result.works.forEach((work, index) => {
21132
+ const paramType = work.customResponse?.paramType;
21133
+ const fileId = work.fileId;
21134
+ const fileUrl = work.fileUrl || work.customResponse?.url;
21135
+ if (!fileId && !fileUrl) return;
21136
+ const mediaType = getMediaTypeFromParamType(paramType) || (fileUrl ? inferMediaTypeFromUrl(fileUrl) : null) || (fileId ? inferMediaTypeFromUrl(fileId) : null);
21137
+ if (!mediaType) return;
21138
+ const artifactId = work.publicId || `media-${toolCall.id}-${index}`;
21139
+ artifacts.push({
21140
+ id: artifactId,
21141
+ sessionId,
21142
+ type: mediaType,
21143
+ title: `${toolName} #${index + 1}`,
21144
+ // 优先存 fileId,resolveAssetForDisplay 会将其转换为可用 URL
21145
+ currentContent: fileId || fileUrl,
21146
+ version: 1,
21147
+ source: "tool",
21148
+ sourceId: toolCall.id,
21149
+ metadata: {
21150
+ toolName,
21151
+ generationParams: args,
21152
+ messageId,
21153
+ workflowCode: result.workflowCode,
21154
+ taskId: result.taskId,
21155
+ fileId: work.fileId,
21156
+ fileUrl: work.fileUrl
21157
+ },
21158
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
21159
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
21160
+ });
21161
+ });
21162
+ }
21163
+ if (artifacts.length === 0 && result.outputs?.images) {
21164
+ const imgUrl = typeof result.outputs.images === "string" ? result.outputs.images : null;
21165
+ if (imgUrl) {
21166
+ artifacts.push({
21167
+ id: `media-${toolCall.id}-output`,
21168
+ sessionId,
21169
+ type: "image",
21170
+ title: `${toolName}`,
21171
+ currentContent: imgUrl,
21172
+ version: 1,
21173
+ source: "tool",
21174
+ sourceId: toolCall.id,
21175
+ metadata: {
21176
+ toolName,
21177
+ generationParams: args,
21178
+ messageId
21179
+ },
21180
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
21181
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
21182
+ });
21183
+ }
21184
+ }
21185
+ if (result.works && Array.isArray(result.works)) {
21186
+ result.works.forEach((work, index) => {
21187
+ if (work.customResponse?.value) {
21188
+ const parsed = detectContentType(work.customResponse.value);
21189
+ if (parsed.content && parsed.type !== "text") {
21190
+ artifacts.push({
21191
+ id: `content-${toolCall.id}-${index}`,
21192
+ sessionId,
21193
+ type: parsed.type,
21194
+ title: parsed.title || `${toolName} - ${parsed.type.toUpperCase()}`,
21195
+ currentContent: parsed.content,
21196
+ language: parsed.language,
21197
+ version: 1,
21198
+ source: "tool",
21199
+ sourceId: toolCall.id,
21200
+ metadata: {
21201
+ toolName,
21202
+ messageId
21203
+ },
21204
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
21205
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
21206
+ });
21207
+ }
21208
+ }
21209
+ });
21210
+ }
21211
+ return artifacts;
21212
+ }
21213
+ function extractArtifactsFromMessages(messages, sessionId) {
21214
+ const allArtifacts = [];
21215
+ const seenIds = /* @__PURE__ */ new Set();
21216
+ for (const message of messages) {
21217
+ if (message.role !== "assistant") continue;
21218
+ const toolCalls = message.extraData?.tool_calls || [];
21219
+ for (const tc of toolCalls) {
21220
+ const extracted = extractArtifactsFromToolCall(tc, sessionId, message.messageId);
21221
+ for (const artifact of extracted) {
21222
+ if (!seenIds.has(artifact.id)) {
21223
+ seenIds.add(artifact.id);
21224
+ allArtifacts.push(artifact);
21225
+ }
21226
+ }
21227
+ }
21228
+ const thoughts = message.extraData?.thoughts || message.thoughts || [];
21229
+ for (const thought of thoughts) {
21230
+ if (thought.toolCalls) {
21231
+ for (const tc of thought.toolCalls) {
21232
+ const extracted = extractArtifactsFromToolCall(tc, sessionId, message.messageId);
21233
+ for (const artifact of extracted) {
21234
+ if (!seenIds.has(artifact.id)) {
21235
+ seenIds.add(artifact.id);
21236
+ allArtifacts.push(artifact);
21237
+ }
21238
+ }
21239
+ }
21240
+ }
21241
+ }
21242
+ }
21243
+ return allArtifacts;
21244
+ }
18965
21245
  var Field = ({ label, value }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 text-xs text-zinc-400", children: [
18966
21246
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
18967
21247
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-200 truncate max-w-[60%]", children: value ?? "-" })
@@ -19293,7 +21573,9 @@ function ChatInputArea({
19293
21573
  onSend,
19294
21574
  onStop,
19295
21575
  isStreaming,
19296
- config
21576
+ config,
21577
+ activeArtifact,
21578
+ onDetachArtifact
19297
21579
  }) {
19298
21580
  const textareaRef = React20.useRef(null);
19299
21581
  const fileInputRef = React20.useRef(null);
@@ -19374,6 +21656,20 @@ function ChatInputArea({
19374
21656
  };
19375
21657
  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
21658
  /* @__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: [
21659
+ 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: [
21660
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileCode, { size: 12, className: "text-[#d8ff00] flex-shrink-0" }),
21661
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400", children: "\u5173\u8054\u4EA7\u7269\uFF1A" }),
21662
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-200 font-medium max-w-[200px] truncate", children: activeArtifact.title }),
21663
+ onDetachArtifact && /* @__PURE__ */ jsxRuntime.jsx(
21664
+ "button",
21665
+ {
21666
+ onClick: onDetachArtifact,
21667
+ className: "p-0.5 text-zinc-500 hover:text-zinc-300 rounded transition-colors",
21668
+ title: "\u53D6\u6D88\u5173\u8054",
21669
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Unlink, { size: 10 })
21670
+ }
21671
+ )
21672
+ ] }) }),
19377
21673
  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
21674
  /* @__PURE__ */ jsxRuntime.jsx(
19379
21675
  "img",
@@ -19538,6 +21834,188 @@ var DragHandle = React20__namespace.default.memo(function DragHandle2({
19538
21834
  }
19539
21835
  );
19540
21836
  });
21837
+ var artifactTypeIconMap = {
21838
+ html: lucideReact.Globe,
21839
+ svg: lucideReact.Globe,
21840
+ markdown: lucideReact.FileText,
21841
+ json: lucideReact.FileJson,
21842
+ code: lucideReact.FileCode,
21843
+ text: lucideReact.FileText,
21844
+ image: lucideReact.FileImage,
21845
+ video: lucideReact.Video
21846
+ };
21847
+ var ArtifactBarItemThumbnail = React20.memo(function ArtifactBarItemThumbnail2({
21848
+ artifact,
21849
+ config
21850
+ }) {
21851
+ const TypeIcon = artifactTypeIconMap[artifact.type] || lucideReact.FileText;
21852
+ const isMedia = artifact.type === "image" || artifact.type === "video";
21853
+ const thumbnailUrl = useResolvedThumbnailUrl(
21854
+ artifact.currentContent,
21855
+ {
21856
+ fileId: artifact.metadata?.fileId,
21857
+ type: artifact.type,
21858
+ config,
21859
+ enabled: isMedia
21860
+ }
21861
+ );
21862
+ if (isMedia && thumbnailUrl) {
21863
+ return /* @__PURE__ */ jsxRuntime.jsx(
21864
+ "img",
21865
+ {
21866
+ src: thumbnailUrl,
21867
+ alt: "",
21868
+ className: "w-10 h-10 rounded-lg object-cover flex-shrink-0 border border-zinc-700/50 agent-sdk-light:border-zinc-300",
21869
+ loading: "lazy"
21870
+ }
21871
+ );
21872
+ }
21873
+ 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" }) });
21874
+ });
21875
+ var ArtifactBarMiniThumbnail = React20.memo(function ArtifactBarMiniThumbnail2({
21876
+ artifact,
21877
+ config
21878
+ }) {
21879
+ const TypeIcon = artifactTypeIconMap[artifact.type] || lucideReact.FileText;
21880
+ const isMedia = artifact.type === "image" || artifact.type === "video";
21881
+ const thumbnailUrl = useResolvedThumbnailUrl(
21882
+ artifact.currentContent,
21883
+ {
21884
+ fileId: artifact.metadata?.fileId,
21885
+ type: artifact.type,
21886
+ config,
21887
+ enabled: isMedia
21888
+ }
21889
+ );
21890
+ if (isMedia && thumbnailUrl) {
21891
+ return /* @__PURE__ */ jsxRuntime.jsx(
21892
+ "img",
21893
+ {
21894
+ src: thumbnailUrl,
21895
+ alt: "",
21896
+ className: "w-6 h-6 rounded border border-zinc-700 agent-sdk-light:border-zinc-300 object-cover",
21897
+ loading: "lazy"
21898
+ }
21899
+ );
21900
+ }
21901
+ 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" }) });
21902
+ });
21903
+ var ArtifactBar = React20__namespace.default.memo(function ArtifactBar2({
21904
+ artifacts,
21905
+ onOpenArtifact,
21906
+ config
21907
+ }) {
21908
+ const [expanded, setExpanded] = React20.useState(false);
21909
+ const barRef = React20.useRef(null);
21910
+ React20.useEffect(() => {
21911
+ if (!expanded) return;
21912
+ const handleClickOutside = (e) => {
21913
+ if (barRef.current && !barRef.current.contains(e.target)) {
21914
+ setExpanded(false);
21915
+ }
21916
+ };
21917
+ document.addEventListener("mousedown", handleClickOutside);
21918
+ return () => document.removeEventListener("mousedown", handleClickOutside);
21919
+ }, [expanded]);
21920
+ if (artifacts.length === 0) return null;
21921
+ const imageCount = artifacts.filter((a) => a.type === "image").length;
21922
+ const codeCount = artifacts.filter((a) => ["code", "html", "svg", "json"].includes(a.type)).length;
21923
+ const docCount = artifacts.filter((a) => ["markdown", "text"].includes(a.type)).length;
21924
+ const videoCount = artifacts.filter((a) => a.type === "video").length;
21925
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: barRef, className: "relative flex-shrink-0 mx-auto max-w-md", children: [
21926
+ 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: [
21927
+ /* @__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: [
21928
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
21929
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGrid, { size: 14, className: "text-[#d8ff00]" }),
21930
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-zinc-200 agent-sdk-light:text-zinc-800", children: "\u5168\u90E8\u4EA7\u7269" }),
21931
+ /* @__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 })
21932
+ ] }),
21933
+ /* @__PURE__ */ jsxRuntime.jsx(
21934
+ "button",
21935
+ {
21936
+ onClick: () => setExpanded(false),
21937
+ className: "p-1 text-zinc-500 hover:text-zinc-300 agent-sdk-light:hover:text-zinc-700 rounded transition-colors",
21938
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 16 })
21939
+ }
21940
+ )
21941
+ ] }),
21942
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 grid grid-cols-1 gap-1", children: artifacts.map((artifact) => {
21943
+ return /* @__PURE__ */ jsxRuntime.jsxs(
21944
+ "button",
21945
+ {
21946
+ onClick: () => {
21947
+ onOpenArtifact(artifact.id);
21948
+ setExpanded(false);
21949
+ },
21950
+ 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",
21951
+ children: [
21952
+ /* @__PURE__ */ jsxRuntime.jsx(ArtifactBarItemThumbnail, { artifact, config }),
21953
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
21954
+ /* @__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 }),
21955
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-[11px] text-zinc-500 agent-sdk-light:text-zinc-500 mt-0.5", children: [
21956
+ artifact.type.toUpperCase(),
21957
+ artifact.language && artifact.language !== artifact.type && ` \u2022 ${artifact.language}`
21958
+ ] })
21959
+ ] })
21960
+ ]
21961
+ },
21962
+ artifact.id
21963
+ );
21964
+ }) })
21965
+ ] }),
21966
+ /* @__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(
21967
+ "button",
21968
+ {
21969
+ onClick: () => setExpanded((prev) => !prev),
21970
+ 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",
21971
+ children: [
21972
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
21973
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { size: 14, className: "text-[#d8ff00]" }),
21974
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-zinc-300 agent-sdk-light:text-zinc-700", children: [
21975
+ artifacts.length,
21976
+ " \u4E2A\u4EA7\u7269"
21977
+ ] })
21978
+ ] }),
21979
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 flex-1 min-w-0", children: [
21980
+ 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: [
21981
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileImage, { size: 10 }),
21982
+ " ",
21983
+ imageCount
21984
+ ] }),
21985
+ 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: [
21986
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileCode, { size: 10 }),
21987
+ " ",
21988
+ codeCount
21989
+ ] }),
21990
+ 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: [
21991
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 10 }),
21992
+ " ",
21993
+ docCount
21994
+ ] }),
21995
+ 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: [
21996
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Video, { size: 10 }),
21997
+ " ",
21998
+ videoCount
21999
+ ] })
22000
+ ] }),
22001
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center -space-x-1.5", children: [
22002
+ artifacts.slice(0, 4).map((artifact) => {
22003
+ return /* @__PURE__ */ jsxRuntime.jsx(ArtifactBarMiniThumbnail, { artifact, config }, artifact.id);
22004
+ }),
22005
+ 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: [
22006
+ "+",
22007
+ artifacts.length - 4
22008
+ ] }) })
22009
+ ] }),
22010
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { size: 14, className: cn2(
22011
+ "text-zinc-500 transition-transform flex-shrink-0",
22012
+ expanded && "rotate-180"
22013
+ ) })
22014
+ ]
22015
+ }
22016
+ ) })
22017
+ ] });
22018
+ });
19541
22019
  var AgentChat = React20__namespace.default.forwardRef(({
19542
22020
  agentId,
19543
22021
  projectId,
@@ -19580,7 +22058,7 @@ var AgentChat = React20__namespace.default.forwardRef(({
19580
22058
  ComponentRegistry: mergedRegistry
19581
22059
  };
19582
22060
  }, [config?.components, outerComponents]);
19583
- const { sessions, setSessions, currentSession, setCurrentSession, addSession, removeSession, tools: _tools, setTools, setSkills, setShowItemTime } = useAgentStore();
22061
+ const { sessions, setSessions, currentSession, setCurrentSession, addSession, removeSession, tools: _tools, setTools, setSkills, setShowItemTime, artifacts, artifactOrder, activeArtifactId, upsertArtifact, updateArtifactContent, setActiveArtifact, removeArtifact, clearArtifacts, setArtifacts, reorderArtifacts } = useAgentStore();
19584
22062
  const [loading, setLoading] = React20.useState(true);
19585
22063
  const [messagesLoading, setMessagesLoading] = React20.useState(false);
19586
22064
  const [collapsed, setCollapsed] = React20.useState(false);
@@ -19592,7 +22070,23 @@ var AgentChat = React20__namespace.default.forwardRef(({
19592
22070
  const lastUserMessageRef = React20.useRef("");
19593
22071
  const [shareModalOpen, setShareModalOpen] = React20.useState(false);
19594
22072
  const [shareSession, setShareSession] = React20.useState(null);
19595
- const [currentArtifact, setCurrentArtifact] = React20.useState(null);
22073
+ const artifactList = React20.useMemo(() => {
22074
+ const ordered = artifactOrder.map((id) => artifacts[id]).filter(Boolean);
22075
+ const unordered = Object.values(artifacts).filter(
22076
+ (a) => !artifactOrder.includes(a.id)
22077
+ );
22078
+ return [...ordered, ...unordered];
22079
+ }, [artifacts, artifactOrder]);
22080
+ const activeArtifact = activeArtifactId ? artifacts[activeArtifactId] ?? null : null;
22081
+ const currentArtifact = activeArtifact ? {
22082
+ id: activeArtifact.id,
22083
+ type: activeArtifact.type,
22084
+ title: activeArtifact.title,
22085
+ content: activeArtifact.currentContent,
22086
+ language: activeArtifact.language,
22087
+ source: activeArtifact.sourceId,
22088
+ metadata: activeArtifact.metadata
22089
+ } : null;
19596
22090
  const [isArtifactFullscreen, setIsArtifactFullscreen] = React20.useState(false);
19597
22091
  const [artifactPanelWidth, setArtifactPanelWidth] = React20.useState(50);
19598
22092
  const [isDragging, setIsDragging] = React20.useState(false);
@@ -19618,7 +22112,11 @@ var AgentChat = React20__namespace.default.forwardRef(({
19618
22112
  const deltaX = dragStartRef.current.startX - e.clientX;
19619
22113
  const deltaPercent = deltaX / containerWidth * 100;
19620
22114
  const newWidth = dragStartRef.current.startWidth + deltaPercent;
19621
- const clampedWidth = Math.max(25, Math.min(75, newWidth));
22115
+ const minChatPx = 350;
22116
+ const minArtifactPx = 400;
22117
+ const maxArtifactPercent = (containerWidth - minChatPx) / containerWidth * 100;
22118
+ const minArtifactPercent = minArtifactPx / containerWidth * 100;
22119
+ const clampedWidth = Math.max(minArtifactPercent, Math.min(maxArtifactPercent, newWidth));
19622
22120
  setArtifactPanelWidth(clampedWidth);
19623
22121
  };
19624
22122
  const handleMouseUp = () => {
@@ -19640,38 +22138,52 @@ var AgentChat = React20__namespace.default.forwardRef(({
19640
22138
  setShowItemTime(showItemTime);
19641
22139
  }, [showItemTime]);
19642
22140
  const handleOpenArtifact = React20.useCallback((data) => {
19643
- if (currentArtifact && currentArtifact.content === data.content && currentArtifact.type === data.type) {
19644
- setCurrentArtifact(null);
22141
+ if (activeArtifact && activeArtifact.currentContent === data.content && activeArtifact.type === data.type) {
22142
+ setActiveArtifact(null);
19645
22143
  setIsArtifactFullscreen(false);
19646
22144
  return;
19647
22145
  }
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]);
22146
+ const existingArtifact = Object.values(artifacts).find(
22147
+ (a) => a.currentContent === data.content && a.type === data.type
22148
+ );
22149
+ if (existingArtifact) {
22150
+ setActiveArtifact(existingArtifact.id);
22151
+ } else {
22152
+ const sessionId = currentSession?.sessionId || "";
22153
+ const newArtifactEntry = {
22154
+ id: `artifact-${Date.now()}`,
22155
+ sessionId,
22156
+ type: data.type,
22157
+ title: data.title,
22158
+ currentContent: data.content,
22159
+ language: data.language,
22160
+ metadata: data.metadata,
22161
+ version: 1,
22162
+ source: "llm",
22163
+ gmtCreate: (/* @__PURE__ */ new Date()).toISOString(),
22164
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
22165
+ };
22166
+ upsertArtifact(newArtifactEntry);
22167
+ setActiveArtifact(newArtifactEntry.id);
22168
+ }
22169
+ }, [activeArtifact, artifacts, currentSession, upsertArtifact, setActiveArtifact]);
19658
22170
  const handleCloseArtifact = React20.useCallback(() => {
19659
- setCurrentArtifact(null);
22171
+ setActiveArtifact(null);
19660
22172
  setIsArtifactFullscreen(false);
19661
- }, []);
22173
+ }, [setActiveArtifact]);
19662
22174
  React20.useEffect(() => {
19663
22175
  const handleKeyDown = (e) => {
19664
- if (e.key === "Escape" && currentArtifact) {
22176
+ if (e.key === "Escape" && activeArtifactId) {
19665
22177
  if (isArtifactFullscreen) {
19666
22178
  setIsArtifactFullscreen(false);
19667
22179
  } else {
19668
- setCurrentArtifact(null);
22180
+ setActiveArtifact(null);
19669
22181
  }
19670
22182
  }
19671
22183
  };
19672
22184
  document.addEventListener("keydown", handleKeyDown);
19673
22185
  return () => document.removeEventListener("keydown", handleKeyDown);
19674
- }, [currentArtifact, isArtifactFullscreen]);
22186
+ }, [activeArtifactId, isArtifactFullscreen, setActiveArtifact]);
19675
22187
  const handleToggleArtifactFullscreen = React20.useCallback(() => {
19676
22188
  setIsArtifactFullscreen((prev) => !prev);
19677
22189
  }, []);
@@ -19873,8 +22385,9 @@ var AgentChat = React20__namespace.default.forwardRef(({
19873
22385
  addSession(s);
19874
22386
  setCurrentSession(s);
19875
22387
  useAgentStore.getState().setMessages([]);
22388
+ clearArtifacts();
19876
22389
  }
19877
- }, [projectId, agentId]);
22390
+ }, [projectId, agentId, clearArtifacts]);
19878
22391
  const handlePrompt = React20.useCallback(async (p) => {
19879
22392
  await handleNew();
19880
22393
  setInput(p);
@@ -19885,10 +22398,29 @@ var AgentChat = React20__namespace.default.forwardRef(({
19885
22398
  onSessionIdChange?.(s.sessionId);
19886
22399
  setMobileOpen(false);
19887
22400
  useAgentStore.getState().setMessages([]);
22401
+ clearArtifacts();
19888
22402
  setMessagesLoading(true);
19889
- await loadMessages(s.sessionId);
22403
+ await Promise.all([
22404
+ loadMessages(s.sessionId),
22405
+ artifactService.list(s.sessionId).then((res) => {
22406
+ if (res.success && res.data && res.data.length > 0) {
22407
+ setArtifacts(res.data);
22408
+ }
22409
+ }).catch((err) => {
22410
+ console.error("[AgentChat] Load artifacts failed:", err);
22411
+ })
22412
+ ]);
22413
+ const currentArtifacts = useAgentStore.getState().artifacts;
22414
+ if (Object.keys(currentArtifacts).length === 0) {
22415
+ const currentMessages = useAgentStore.getState().messages;
22416
+ const extracted = extractArtifactsFromMessages(currentMessages, s.sessionId);
22417
+ if (extracted.length > 0) {
22418
+ console.log("[AgentChat] Fallback: extracted", extracted.length, "artifacts from messages");
22419
+ extracted.forEach((artifact) => upsertArtifact(artifact));
22420
+ }
22421
+ }
19890
22422
  setMessagesLoading(false);
19891
- }, []);
22423
+ }, [clearArtifacts, setArtifacts, upsertArtifact]);
19892
22424
  const handleDelete2 = React20.useCallback(async (id) => {
19893
22425
  toast("\u5220\u9664\u6B64\u5BF9\u8BDD\uFF1F", {
19894
22426
  action: {
@@ -19961,10 +22493,6 @@ var AgentChat = React20__namespace.default.forwardRef(({
19961
22493
  working = { ...working, fileId: uploadRes.key, url };
19962
22494
  }
19963
22495
  }
19964
- const rendered = await resolveAssetForDisplay(working, config);
19965
- if (rendered?.url) {
19966
- urls.push(rendered.url);
19967
- }
19968
22496
  }
19969
22497
  if (urls.length > 0) {
19970
22498
  const imageMarkdown = urls.map((url) => `![image](${url})`).join("\n");
@@ -19984,8 +22512,14 @@ var AgentChat = React20__namespace.default.forwardRef(({
19984
22512
  }
19985
22513
  });
19986
22514
  setImages([]);
19987
- await sendMessage(currentSession.sessionId, messageContent);
19988
- }, [input, images, currentSession, sendMessage, config]);
22515
+ const currentActiveArtifact = activeArtifactId ? artifacts[activeArtifactId] : null;
22516
+ const artifactContext = currentActiveArtifact ? {
22517
+ artifactId: currentActiveArtifact.id,
22518
+ title: currentActiveArtifact.title,
22519
+ currentContent: currentActiveArtifact.currentContent
22520
+ } : void 0;
22521
+ await sendMessage(currentSession.sessionId, messageContent, { artifactContext });
22522
+ }, [input, images, currentSession, sendMessage, config, activeArtifactId, artifacts]);
19989
22523
  const sendTextMessage = React20.useCallback(async (content) => {
19990
22524
  if (!content.trim()) return;
19991
22525
  if (!currentSession) return;
@@ -20150,6 +22684,13 @@ var AgentChat = React20__namespace.default.forwardRef(({
20150
22684
  onOpenArtifact: handleOpenArtifact
20151
22685
  }
20152
22686
  ),
22687
+ artifactList.length > 0 && !currentArtifact && /* @__PURE__ */ jsxRuntime.jsx(
22688
+ ArtifactBar,
22689
+ {
22690
+ artifacts: artifactList,
22691
+ onOpenArtifact: (id) => setActiveArtifact(id)
22692
+ }
22693
+ ),
20153
22694
  !hideInput && /* @__PURE__ */ jsxRuntime.jsx(
20154
22695
  ChatInputArea,
20155
22696
  {
@@ -20160,7 +22701,9 @@ var AgentChat = React20__namespace.default.forwardRef(({
20160
22701
  onSend: handleSend,
20161
22702
  onStop: handleStop,
20162
22703
  isStreaming: isCurrentSessionStreaming,
20163
- config
22704
+ config,
22705
+ activeArtifact,
22706
+ onDetachArtifact: () => setActiveArtifact(null)
20164
22707
  }
20165
22708
  )
20166
22709
  ]
@@ -20199,7 +22742,36 @@ var AgentChat = React20__namespace.default.forwardRef(({
20199
22742
  config,
20200
22743
  isFullscreen: isArtifactFullscreen,
20201
22744
  onFullscreenToggle: handleToggleArtifactFullscreen,
20202
- embedded: true
22745
+ embedded: true,
22746
+ artifacts: artifactList,
22747
+ activeArtifactId,
22748
+ onSwitchArtifact: (id) => setActiveArtifact(id),
22749
+ onCloseArtifact: (id) => {
22750
+ removeArtifact(id);
22751
+ if (artifactList.length <= 1) {
22752
+ setIsArtifactFullscreen(false);
22753
+ }
22754
+ },
22755
+ onReorderArtifacts: reorderArtifacts,
22756
+ onContentChange: (content) => {
22757
+ if (activeArtifactId) {
22758
+ updateArtifactContent(activeArtifactId, content, "user");
22759
+ }
22760
+ },
22761
+ onSave: async (artifactId, content) => {
22762
+ try {
22763
+ const res = await artifactService.updateContent(artifactId, content);
22764
+ if (res.success) {
22765
+ updateArtifactContent(artifactId, content, "user");
22766
+ toast.success("\u4EA7\u7269\u5DF2\u4FDD\u5B58");
22767
+ } else {
22768
+ toast.error("\u4FDD\u5B58\u5931\u8D25");
22769
+ }
22770
+ } catch (err) {
22771
+ console.error("[AgentChat] Save artifact failed:", err);
22772
+ toast.error("\u4FDD\u5B58\u4EA7\u7269\u5931\u8D25");
22773
+ }
22774
+ }
20203
22775
  }
20204
22776
  )
20205
22777
  ]
@@ -20709,6 +23281,7 @@ exports.VoiceWaveform = VoiceWaveform;
20709
23281
  exports.agentService = agentService;
20710
23282
  exports.agentSkillService = agentSkillService;
20711
23283
  exports.agentToolService = agentToolService;
23284
+ exports.artifactService = artifactService;
20712
23285
  exports.calculateReconnectDelay = calculateReconnectDelay;
20713
23286
  exports.componentService = componentService;
20714
23287
  exports.confirmAllPendingToolCalls = confirmAllPendingToolCalls;
@@ -20764,8 +23337,14 @@ exports.shareService = shareService;
20764
23337
  exports.templateService = templateService;
20765
23338
  exports.toolCallService = toolCallService;
20766
23339
  exports.uploadOSS = uploadOSS;
23340
+ exports.useActiveArtifact = useActiveArtifact;
23341
+ exports.useActiveArtifactId = useActiveArtifactId;
20767
23342
  exports.useActiveSubAgent = useActiveSubAgent;
20768
23343
  exports.useAgentStore = useAgentStore;
23344
+ exports.useArtifactList = useArtifactList;
23345
+ exports.useArtifactOrder = useArtifactOrder;
23346
+ exports.useArtifacts = useArtifacts;
23347
+ exports.useCanvasBridge = useCanvasBridge;
20769
23348
  exports.useChatError = useChatError;
20770
23349
  exports.useChatUI = useChatUI;
20771
23350
  exports.useComponent = useComponent;