@malette/agent-sdk 0.1.3-beta.12 → 0.1.3-beta.13

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
@@ -499,10 +499,16 @@ var artifactService = {
499
499
  /**
500
500
  * 更新产物内容(用户编辑后保存)
501
501
  */
502
- updateContent: async (artifactId, content) => {
502
+ updateContent: async (artifactId, content, options) => {
503
503
  return fetcher(`${API_BASE}/artifacts/${artifactId}/content`, {
504
504
  method: "PUT",
505
- body: JSON.stringify({ artifactId, content })
505
+ body: JSON.stringify({
506
+ artifactId,
507
+ content,
508
+ ...options?.type && { type: options.type },
509
+ ...options?.language !== void 0 && { language: options.language },
510
+ ...options?.title !== void 0 && { title: options.title }
511
+ })
506
512
  });
507
513
  },
508
514
  /**
@@ -1707,6 +1713,25 @@ var useAgentStore = zustand.create()(
1707
1713
  false,
1708
1714
  "updateArtifactVisibility"
1709
1715
  ),
1716
+ updateArtifactType: (artifactId, type, language) => set(
1717
+ (state) => {
1718
+ const existing = state.artifacts[artifactId];
1719
+ if (!existing) return state;
1720
+ return {
1721
+ artifacts: {
1722
+ ...state.artifacts,
1723
+ [artifactId]: {
1724
+ ...existing,
1725
+ type,
1726
+ language: language ?? existing.language,
1727
+ gmtModified: (/* @__PURE__ */ new Date()).toISOString()
1728
+ }
1729
+ }
1730
+ };
1731
+ },
1732
+ false,
1733
+ "updateArtifactType"
1734
+ ),
1710
1735
  // ============ 复合操作 ============
1711
1736
  startNewChat: (sessionId) => {
1712
1737
  set(
@@ -7809,6 +7834,104 @@ var resolveAssetForDisplay = async (asset, config) => {
7809
7834
  }
7810
7835
  return null;
7811
7836
  };
7837
+ var resolveHtmlAssetUrls = (html2) => {
7838
+ const origin = typeof window !== "undefined" ? window.location.origin : "";
7839
+ const resolveSrc = (src, isVideo) => {
7840
+ if (!src.trim()) return src;
7841
+ if (/^(data:|blob:|javascript:|#)/i.test(src)) return src;
7842
+ if (src.includes("${")) return src;
7843
+ if (/^https?:/i.test(src)) {
7844
+ if (src.includes("industryai")) {
7845
+ return src.split("?")[0];
7846
+ }
7847
+ return src;
7848
+ }
7849
+ const resolved = isVideo ? getHDImageUrl(src) : getImageUrl(src);
7850
+ return resolved.startsWith("http") ? resolved : `${origin}${resolved}`;
7851
+ };
7852
+ const processHtmlPart = (part) => {
7853
+ let result = part.replace(
7854
+ /(<(?:img|video|source)\s[^>]*?\bsrc\s*=\s*["'])([^"']*)(["'])/gi,
7855
+ (match, prefix, src, suffix) => {
7856
+ const tagMatch = match.match(/^<(\w+)/i);
7857
+ const tag = tagMatch?.[1]?.toLowerCase() ?? "";
7858
+ const isVideo = tag === "video" || tag === "source";
7859
+ return `${prefix}${resolveSrc(src, isVideo)}${suffix}`;
7860
+ }
7861
+ );
7862
+ result = result.replace(
7863
+ /(<video\s[^>]*?\bposter\s*=\s*["'])([^"']*)(["'])/gi,
7864
+ (match, prefix, src, suffix) => `${prefix}${resolveSrc(src, false)}${suffix}`
7865
+ );
7866
+ return result;
7867
+ };
7868
+ const parts = html2.split(/(<script\b[\s\S]*?<\/script>)/gi);
7869
+ return parts.map((part, i) => i % 2 === 1 ? part : processHtmlPart(part)).join("");
7870
+ };
7871
+ var resolveHtmlAssetUrlsAsync = async (html2, config) => {
7872
+ if (!config?.asset) return resolveHtmlAssetUrls(html2);
7873
+ const srcPattern = /(<(?:img|video|source)\s[^>]*?\bsrc\s*=\s*["'])([^"']*)(["'])/gi;
7874
+ const posterPattern = /(<video\s[^>]*?\bposter\s*=\s*["'])([^"']*)(["'])/gi;
7875
+ const scriptSplitPattern = /(<script\b[\s\S]*?<\/script>)/gi;
7876
+ const parts = html2.split(scriptSplitPattern);
7877
+ const nonScriptHtml = parts.filter((_, i) => i % 2 === 0).join("");
7878
+ const urlsToResolve = /* @__PURE__ */ new Map();
7879
+ const collectUrl = (src) => {
7880
+ if (!src.trim()) return;
7881
+ if (/^(data:|blob:|javascript:|#)/i.test(src)) return;
7882
+ if (src.includes("${")) return;
7883
+ urlsToResolve.set(src, src);
7884
+ };
7885
+ let match;
7886
+ const srcRe = new RegExp(srcPattern.source, srcPattern.flags);
7887
+ while ((match = srcRe.exec(nonScriptHtml)) !== null) {
7888
+ collectUrl(match[2]);
7889
+ }
7890
+ const posterRe = new RegExp(posterPattern.source, posterPattern.flags);
7891
+ while ((match = posterRe.exec(nonScriptHtml)) !== null) {
7892
+ collectUrl(match[2]);
7893
+ }
7894
+ await Promise.all(
7895
+ Array.from(urlsToResolve.keys()).map(async (src) => {
7896
+ const tagMatch = nonScriptHtml.match(
7897
+ new RegExp(`<(img|video|source)[^>]*(?:src|poster)=["']${src.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']`, "i")
7898
+ );
7899
+ const tag = tagMatch?.[1]?.toLowerCase() ?? "";
7900
+ const isVideo = tag === "video" || tag === "source";
7901
+ const type = isVideo ? "video" : "image";
7902
+ const isHttp = /^https?:/i.test(src);
7903
+ const asset = createAssetFromSource({
7904
+ fileId: !isHttp ? src : void 0,
7905
+ fileUrl: isHttp ? src : void 0,
7906
+ type
7907
+ });
7908
+ if (!asset) return;
7909
+ const resolved = await resolveAssetForDisplay(asset, config);
7910
+ if (resolved) {
7911
+ urlsToResolve.set(src, resolved.url);
7912
+ }
7913
+ })
7914
+ );
7915
+ const processedParts = parts.map((part, i) => {
7916
+ if (i % 2 === 1) return part;
7917
+ let result = part.replace(
7918
+ /(<(?:img|video|source)\s[^>]*?\bsrc\s*=\s*["'])([^"']*)(["'])/gi,
7919
+ (m, prefix, src, suffix) => {
7920
+ const resolved = urlsToResolve.get(src);
7921
+ return resolved && resolved !== src ? `${prefix}${resolved}${suffix}` : m;
7922
+ }
7923
+ );
7924
+ result = result.replace(
7925
+ /(<video\s[^>]*?\bposter\s*=\s*["'])([^"']*)(["'])/gi,
7926
+ (m, prefix, src, suffix) => {
7927
+ const resolved = urlsToResolve.get(src);
7928
+ return resolved && resolved !== src ? `${prefix}${resolved}${suffix}` : m;
7929
+ }
7930
+ );
7931
+ return result;
7932
+ });
7933
+ return processedParts.join("");
7934
+ };
7812
7935
  var ComponentContext = React16.createContext(void 0);
7813
7936
  var ComponentProvider = ({
7814
7937
  components = {},
@@ -16260,10 +16383,10 @@ var Observer = class {
16260
16383
  });
16261
16384
  }
16262
16385
  };
16263
- this.custom = (jsx66, data) => {
16386
+ this.custom = (jsx68, data) => {
16264
16387
  const id = (data == null ? void 0 : data.id) || toastsCounter++;
16265
16388
  this.create({
16266
- jsx: jsx66(id),
16389
+ jsx: jsx68(id),
16267
16390
  id,
16268
16391
  ...data
16269
16392
  });
@@ -21772,15 +21895,24 @@ var CodeBlock3 = React16.memo(function CodeBlock4({
21772
21895
  ] }, i)) }) }) })
21773
21896
  ] });
21774
21897
  });
21775
- var HtmlPreview3 = React16.memo(function HtmlPreview4({ content }) {
21898
+ var HtmlPreview3 = React16.memo(function HtmlPreview4({
21899
+ content,
21900
+ config
21901
+ }) {
21776
21902
  const [scale, setScale] = React16.useState(1);
21777
21903
  const [deviceMode, setDeviceMode] = React16.useState("responsive");
21778
21904
  const [isLoading, setIsLoading] = React16.useState(true);
21779
21905
  const [iframeHeight, setIframeHeight] = React16.useState(600);
21780
21906
  const iframeRef = React16.useRef(null);
21781
21907
  const containerRef = React16.useRef(null);
21782
- const blobUrl = React16.useMemo(() => {
21783
- const styleInjection = `
21908
+ const [blobUrl, setBlobUrl] = React16.useState("");
21909
+ React16.useEffect(() => {
21910
+ let active = true;
21911
+ let currentUrl = "";
21912
+ const buildBlobUrl = async () => {
21913
+ const processed = config?.asset ? await resolveHtmlAssetUrlsAsync(content, config) : resolveHtmlAssetUrls(content);
21914
+ if (!active) return;
21915
+ const styleInjection = `
21784
21916
  <style>
21785
21917
  html, body {
21786
21918
  margin: 0;
@@ -21794,24 +21926,26 @@ var HtmlPreview3 = React16.memo(function HtmlPreview4({ content }) {
21794
21926
  }
21795
21927
  </style>
21796
21928
  `;
21797
- let modifiedContent = content;
21798
- if (content.includes("<head>")) {
21799
- modifiedContent = content.replace("<head>", "<head>" + styleInjection);
21800
- } else if (content.includes("<html>")) {
21801
- modifiedContent = content.replace("<html>", "<html><head>" + styleInjection + "</head>");
21802
- } else if (content.includes("<!DOCTYPE") || content.includes("<!doctype")) {
21803
- modifiedContent = content.replace(/(<!DOCTYPE[^>]*>|<!doctype[^>]*>)/i, "$1<html><head>" + styleInjection + "</head><body>") + "</body></html>";
21804
- } else {
21805
- modifiedContent = `<!DOCTYPE html><html><head>${styleInjection}</head><body>${content}</body></html>`;
21806
- }
21807
- const blob = new Blob([modifiedContent], { type: "text/html" });
21808
- return URL.createObjectURL(blob);
21809
- }, [content]);
21810
- React16.useEffect(() => {
21929
+ let modifiedContent = processed;
21930
+ if (processed.includes("<head>")) {
21931
+ modifiedContent = processed.replace("<head>", "<head>" + styleInjection);
21932
+ } else if (processed.includes("<html>")) {
21933
+ modifiedContent = processed.replace("<html>", "<html><head>" + styleInjection + "</head>");
21934
+ } else if (processed.includes("<!DOCTYPE") || processed.includes("<!doctype")) {
21935
+ modifiedContent = processed.replace(/(<!DOCTYPE[^>]*>|<!doctype[^>]*>)/i, "$1<html><head>" + styleInjection + "</head><body>") + "</body></html>";
21936
+ } else {
21937
+ modifiedContent = `<!DOCTYPE html><html><head>${styleInjection}</head><body>${processed}</body></html>`;
21938
+ }
21939
+ const blob = new Blob([modifiedContent], { type: "text/html" });
21940
+ currentUrl = URL.createObjectURL(blob);
21941
+ setBlobUrl(currentUrl);
21942
+ };
21943
+ buildBlobUrl();
21811
21944
  return () => {
21812
- URL.revokeObjectURL(blobUrl);
21945
+ active = false;
21946
+ if (currentUrl) URL.revokeObjectURL(currentUrl);
21813
21947
  };
21814
- }, [blobUrl]);
21948
+ }, [content, config]);
21815
21949
  const deviceConfig = {
21816
21950
  desktop: { width: "100%", maxWidth: "1400px", height: 800, icon: lucideReact.Monitor, label: "Desktop" },
21817
21951
  tablet: { width: "768px", height: 1024, icon: lucideReact.Tablet, label: "Tablet" },
@@ -21966,7 +22100,7 @@ var HtmlPreview3 = React16.memo(function HtmlPreview4({ content }) {
21966
22100
  },
21967
22101
  children: [
21968
22102
  deviceMode === "mobile" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 bg-zinc-900 agent-sdk-light:bg-zinc-200 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-4 bg-zinc-800 agent-sdk-light:bg-zinc-300 rounded-full" }) }),
21969
- /* @__PURE__ */ jsxRuntime.jsx(
22103
+ blobUrl && /* @__PURE__ */ jsxRuntime.jsx(
21970
22104
  "iframe",
21971
22105
  {
21972
22106
  ref: iframeRef,
@@ -23863,6 +23997,237 @@ function ArtifactDeleteButton({
23863
23997
  modal
23864
23998
  ] });
23865
23999
  }
24000
+ var CHANGEABLE_TYPES = ["code", "html", "svg", "markdown", "json", "text"];
24001
+ function ArtifactTypeSelector({
24002
+ currentType,
24003
+ currentLanguage,
24004
+ onTypeChange,
24005
+ disabled = false
24006
+ }) {
24007
+ const [isOpen, setIsOpen] = React16.useState(false);
24008
+ const [isChanging, setIsChanging] = React16.useState(false);
24009
+ const dropdownRef = React16.useRef(null);
24010
+ const typeConfig2 = artifactTypeConfig[currentType] || artifactTypeConfig.text;
24011
+ const isChangeable = CHANGEABLE_TYPES.includes(currentType);
24012
+ React16.useEffect(() => {
24013
+ if (!isOpen) return;
24014
+ const handleClickOutside = (e) => {
24015
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
24016
+ setIsOpen(false);
24017
+ }
24018
+ };
24019
+ document.addEventListener("mousedown", handleClickOutside);
24020
+ return () => document.removeEventListener("mousedown", handleClickOutside);
24021
+ }, [isOpen]);
24022
+ const handleSelectType = React16.useCallback(async (newType) => {
24023
+ if (newType === currentType || isChanging) return;
24024
+ setIsChanging(true);
24025
+ try {
24026
+ await onTypeChange(newType);
24027
+ setIsOpen(false);
24028
+ } finally {
24029
+ setIsChanging(false);
24030
+ }
24031
+ }, [currentType, isChanging, onTypeChange]);
24032
+ if (!isChangeable) {
24033
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-7 h-7 rounded-lg ${typeConfig2.bgColor} flex items-center justify-center flex-shrink-0`, children: /* @__PURE__ */ jsxRuntime.jsx(typeConfig2.icon, { size: 14, className: typeConfig2.color }) });
24034
+ }
24035
+ const availableTypes = CHANGEABLE_TYPES.filter((t) => t !== currentType);
24036
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: dropdownRef, children: [
24037
+ /* @__PURE__ */ jsxRuntime.jsxs(
24038
+ "button",
24039
+ {
24040
+ onClick: () => !disabled && !isChanging && setIsOpen(!isOpen),
24041
+ disabled: disabled || isChanging,
24042
+ className: cn3(
24043
+ "flex items-center gap-1 p-1 rounded-lg transition-colors",
24044
+ "hover:bg-zinc-800 agent-sdk-light:hover:bg-zinc-200",
24045
+ (disabled || isChanging) && "opacity-50 cursor-not-allowed"
24046
+ ),
24047
+ title: "\u5207\u6362\u4EA7\u7269\u7C7B\u578B",
24048
+ children: [
24049
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-7 h-7 rounded-lg ${typeConfig2.bgColor} flex items-center justify-center flex-shrink-0`, children: isChanging ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 14, className: `${typeConfig2.color} animate-spin` }) : /* @__PURE__ */ jsxRuntime.jsx(typeConfig2.icon, { size: 14, className: typeConfig2.color }) }),
24050
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 10, className: "text-zinc-500" })
24051
+ ]
24052
+ }
24053
+ ),
24054
+ isOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-0 top-full mt-1 z-50 w-44 bg-zinc-900 agent-sdk-light:bg-white border border-zinc-700/50 agent-sdk-light:border-zinc-200 rounded-lg shadow-xl overflow-hidden", children: [
24055
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 border-b border-zinc-800/50 agent-sdk-light:border-zinc-100", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-zinc-500 agent-sdk-light:text-zinc-400", children: "\u5207\u6362\u7C7B\u578B" }) }),
24056
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-1", children: availableTypes.map((type) => {
24057
+ const config = artifactTypeConfig[type];
24058
+ const TypeIcon = config.icon;
24059
+ return /* @__PURE__ */ jsxRuntime.jsxs(
24060
+ "button",
24061
+ {
24062
+ onClick: () => handleSelectType(type),
24063
+ disabled: isChanging,
24064
+ className: cn3(
24065
+ "flex items-center gap-2.5 px-2 py-1.5 text-xs rounded-md transition-all w-full text-left",
24066
+ "text-zinc-300 agent-sdk-light:text-zinc-700",
24067
+ "hover:bg-zinc-800/70 agent-sdk-light:hover:bg-zinc-100",
24068
+ isChanging && "opacity-50 cursor-not-allowed"
24069
+ ),
24070
+ children: [
24071
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn3("w-5 h-5 rounded flex items-center justify-center flex-shrink-0", config.bgColor), children: /* @__PURE__ */ jsxRuntime.jsx(TypeIcon, { size: 11, className: config.color }) }),
24072
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: config.label })
24073
+ ]
24074
+ },
24075
+ type
24076
+ );
24077
+ }) })
24078
+ ] })
24079
+ ] });
24080
+ }
24081
+ var COMMON_LANGUAGES = [
24082
+ "javascript",
24083
+ "typescript",
24084
+ "python",
24085
+ "java",
24086
+ "go",
24087
+ "rust",
24088
+ "c",
24089
+ "cpp",
24090
+ "csharp",
24091
+ "ruby",
24092
+ "php",
24093
+ "swift",
24094
+ "kotlin",
24095
+ "sql",
24096
+ "shell",
24097
+ "html",
24098
+ "css",
24099
+ "json",
24100
+ "yaml",
24101
+ "xml",
24102
+ "markdown"
24103
+ ];
24104
+ function ArtifactLanguageSelector({
24105
+ currentLanguage,
24106
+ onLanguageChange,
24107
+ disabled = false
24108
+ }) {
24109
+ const [isOpen, setIsOpen] = React16.useState(false);
24110
+ const [isChanging, setIsChanging] = React16.useState(false);
24111
+ const [search, setSearch] = React16.useState("");
24112
+ const dropdownRef = React16.useRef(null);
24113
+ const inputRef = React16.useRef(null);
24114
+ React16.useEffect(() => {
24115
+ if (!isOpen) return;
24116
+ const handleClickOutside = (e) => {
24117
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
24118
+ setIsOpen(false);
24119
+ setSearch("");
24120
+ }
24121
+ };
24122
+ document.addEventListener("mousedown", handleClickOutside);
24123
+ return () => document.removeEventListener("mousedown", handleClickOutside);
24124
+ }, [isOpen]);
24125
+ React16.useEffect(() => {
24126
+ if (isOpen && inputRef.current) {
24127
+ inputRef.current.focus();
24128
+ }
24129
+ }, [isOpen]);
24130
+ const handleSelect = React16.useCallback(async (language) => {
24131
+ if (language === currentLanguage || isChanging) return;
24132
+ setIsChanging(true);
24133
+ try {
24134
+ await onLanguageChange(language);
24135
+ setIsOpen(false);
24136
+ setSearch("");
24137
+ } finally {
24138
+ setIsChanging(false);
24139
+ }
24140
+ }, [currentLanguage, isChanging, onLanguageChange]);
24141
+ const handleKeyDown = React16.useCallback((e) => {
24142
+ if (e.key === "Enter" && search.trim()) {
24143
+ e.preventDefault();
24144
+ handleSelect(search.trim().toLowerCase());
24145
+ } else if (e.key === "Escape") {
24146
+ setIsOpen(false);
24147
+ setSearch("");
24148
+ }
24149
+ }, [search, handleSelect]);
24150
+ const filteredLanguages = search.trim() ? COMMON_LANGUAGES.filter((lang) => lang.includes(search.trim().toLowerCase())) : COMMON_LANGUAGES;
24151
+ const showCustomOption = search.trim() && !COMMON_LANGUAGES.includes(search.trim().toLowerCase()) && search.trim().toLowerCase() !== currentLanguage;
24152
+ if (!currentLanguage) return null;
24153
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: dropdownRef, children: [
24154
+ /* @__PURE__ */ jsxRuntime.jsx(
24155
+ "button",
24156
+ {
24157
+ onClick: () => !disabled && !isChanging && setIsOpen(!isOpen),
24158
+ disabled: disabled || isChanging,
24159
+ className: cn3(
24160
+ "text-zinc-500 agent-sdk-light:text-zinc-500 ml-1 text-xs transition-colors",
24161
+ "hover:text-zinc-300 agent-sdk-light:hover:text-zinc-700",
24162
+ (disabled || isChanging) && "opacity-50 cursor-not-allowed",
24163
+ !disabled && !isChanging && "cursor-pointer"
24164
+ ),
24165
+ children: isChanging ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
24166
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 10, className: "animate-spin" }),
24167
+ currentLanguage
24168
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
24169
+ "\u2022 ",
24170
+ currentLanguage
24171
+ ] })
24172
+ }
24173
+ ),
24174
+ isOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-0 top-full mt-1 z-50 w-48 bg-zinc-900 agent-sdk-light:bg-white border border-zinc-700/50 agent-sdk-light:border-zinc-200 rounded-lg shadow-xl overflow-hidden", children: [
24175
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 border-b border-zinc-800/50 agent-sdk-light:border-zinc-100", children: /* @__PURE__ */ jsxRuntime.jsx(
24176
+ "input",
24177
+ {
24178
+ ref: inputRef,
24179
+ type: "text",
24180
+ value: search,
24181
+ onChange: (e) => setSearch(e.target.value),
24182
+ onKeyDown: handleKeyDown,
24183
+ placeholder: "\u641C\u7D22\u8BED\u8A00...",
24184
+ className: "w-full px-2 py-1.5 text-xs bg-zinc-800 agent-sdk-light:bg-zinc-100 text-zinc-200 agent-sdk-light:text-zinc-800 rounded-md border border-zinc-700/50 agent-sdk-light:border-zinc-200 outline-none focus:border-zinc-500 agent-sdk-light:focus:border-zinc-400 placeholder-zinc-600 agent-sdk-light:placeholder-zinc-400"
24185
+ }
24186
+ ) }),
24187
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-h-48 overflow-y-auto p-1", children: [
24188
+ showCustomOption && /* @__PURE__ */ jsxRuntime.jsxs(
24189
+ "button",
24190
+ {
24191
+ onClick: () => handleSelect(search.trim().toLowerCase()),
24192
+ disabled: isChanging,
24193
+ className: cn3(
24194
+ "flex items-center justify-between px-2 py-1.5 text-xs rounded-md transition-all w-full text-left",
24195
+ "text-zinc-300 agent-sdk-light:text-zinc-700",
24196
+ "hover:bg-zinc-800/70 agent-sdk-light:hover:bg-zinc-100",
24197
+ isChanging && "opacity-50 cursor-not-allowed"
24198
+ ),
24199
+ children: [
24200
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
24201
+ '"',
24202
+ search.trim().toLowerCase(),
24203
+ '"'
24204
+ ] }),
24205
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-zinc-600 agent-sdk-light:text-zinc-400", children: "\u56DE\u8F66\u786E\u8BA4" })
24206
+ ]
24207
+ }
24208
+ ),
24209
+ filteredLanguages.map((lang) => /* @__PURE__ */ jsxRuntime.jsxs(
24210
+ "button",
24211
+ {
24212
+ onClick: () => handleSelect(lang),
24213
+ disabled: isChanging,
24214
+ className: cn3(
24215
+ "flex items-center justify-between px-2 py-1.5 text-xs rounded-md transition-all w-full text-left",
24216
+ lang === currentLanguage ? "text-zinc-100 agent-sdk-light:text-zinc-900 bg-zinc-800/50 agent-sdk-light:bg-zinc-100" : "text-zinc-300 agent-sdk-light:text-zinc-700 hover:bg-zinc-800/70 agent-sdk-light:hover:bg-zinc-100",
24217
+ isChanging && "opacity-50 cursor-not-allowed"
24218
+ ),
24219
+ children: [
24220
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: lang }),
24221
+ lang === currentLanguage && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 12, className: "text-green-400 agent-sdk-light:text-green-600 flex-shrink-0" })
24222
+ ]
24223
+ },
24224
+ lang
24225
+ )),
24226
+ filteredLanguages.length === 0 && !showCustomOption && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-3 text-xs text-zinc-600 agent-sdk-light:text-zinc-400 text-center", children: "\u65E0\u5339\u914D\u7ED3\u679C" })
24227
+ ] })
24228
+ ] })
24229
+ ] });
24230
+ }
23866
24231
  var ArtifactViewer = React16.memo(function ArtifactViewer2({
23867
24232
  artifact,
23868
24233
  isOpen,
@@ -23880,13 +24245,20 @@ var ArtifactViewer = React16.memo(function ArtifactViewer2({
23880
24245
  onContentChange,
23881
24246
  onSave,
23882
24247
  onVisibilityChange,
23883
- onDelete
24248
+ onDelete,
24249
+ onTypeChange,
24250
+ onLanguageChange,
24251
+ onTitleChange
23884
24252
  }) {
23885
24253
  const [viewMode, setViewMode] = React16.useState(defaultView);
23886
24254
  const [isEditing, setIsEditing] = React16.useState(false);
23887
24255
  const [editContent, setEditContent] = React16.useState("");
23888
24256
  const [undoStack, setUndoStack] = React16.useState([]);
23889
24257
  const [redoStack, setRedoStack] = React16.useState([]);
24258
+ const [isEditingTitle, setIsEditingTitle] = React16.useState(false);
24259
+ const [editingTitle, setEditingTitle] = React16.useState("");
24260
+ const [isSavingTitle, setIsSavingTitle] = React16.useState(false);
24261
+ const titleInputRef = React16.useRef(null);
23890
24262
  React16.useEffect(() => {
23891
24263
  if (artifact) {
23892
24264
  if (artifact.type === "html" || artifact.type === "markdown" || artifact.type === "svg") {
@@ -23898,8 +24270,9 @@ var ArtifactViewer = React16.memo(function ArtifactViewer2({
23898
24270
  setEditContent(artifact.content);
23899
24271
  setUndoStack([]);
23900
24272
  setRedoStack([]);
24273
+ setIsEditingTitle(false);
23901
24274
  }
23902
- }, [artifact?.id]);
24275
+ }, [artifact?.id, artifact?.type]);
23903
24276
  React16.useEffect(() => {
23904
24277
  if (artifact && !isEditing) {
23905
24278
  setEditContent(artifact.content);
@@ -23912,6 +24285,7 @@ var ArtifactViewer = React16.memo(function ArtifactViewer2({
23912
24285
  const handleStartEdit = () => {
23913
24286
  setEditContent(artifact.content);
23914
24287
  setUndoStack([artifact.content]);
24288
+ handleStartEditTitle();
23915
24289
  setRedoStack([]);
23916
24290
  setIsEditing(true);
23917
24291
  setViewMode("code");
@@ -23946,6 +24320,43 @@ var ArtifactViewer = React16.memo(function ArtifactViewer2({
23946
24320
  setEditContent(artifact.content);
23947
24321
  setIsEditing(false);
23948
24322
  };
24323
+ const handleStartEditTitle = React16.useCallback(() => {
24324
+ if (!onTitleChange || isEditing || isSavingTitle) return;
24325
+ setEditingTitle(artifact.title);
24326
+ setIsEditingTitle(true);
24327
+ }, [onTitleChange, isEditing, isSavingTitle, artifact.title]);
24328
+ const handleSaveTitle = React16.useCallback(async () => {
24329
+ const trimmed = editingTitle.trim();
24330
+ if (!trimmed || trimmed === artifact.title) {
24331
+ setIsEditingTitle(false);
24332
+ return;
24333
+ }
24334
+ setIsSavingTitle(true);
24335
+ try {
24336
+ await onTitleChange?.(artifact.id, trimmed);
24337
+ } finally {
24338
+ setIsSavingTitle(false);
24339
+ setIsEditingTitle(false);
24340
+ }
24341
+ }, [editingTitle, artifact.title, artifact.id, onTitleChange]);
24342
+ const handleCancelEditTitle = React16.useCallback(() => {
24343
+ setIsEditingTitle(false);
24344
+ }, []);
24345
+ const handleTitleKeyDown = React16.useCallback((e) => {
24346
+ if (e.key === "Enter") {
24347
+ e.preventDefault();
24348
+ handleSaveTitle();
24349
+ } else if (e.key === "Escape") {
24350
+ e.preventDefault();
24351
+ handleCancelEditTitle();
24352
+ }
24353
+ }, [handleSaveTitle, handleCancelEditTitle]);
24354
+ React16.useEffect(() => {
24355
+ if (isEditingTitle && titleInputRef.current) {
24356
+ titleInputRef.current.focus();
24357
+ titleInputRef.current.select();
24358
+ }
24359
+ }, [isEditingTitle]);
23949
24360
  const displayContent = isEditing ? editContent : artifact.content;
23950
24361
  const renderContent = () => {
23951
24362
  if (isEditing) {
@@ -23986,7 +24397,7 @@ var ArtifactViewer = React16.memo(function ArtifactViewer2({
23986
24397
  switch (artifact.type) {
23987
24398
  case "html":
23988
24399
  case "svg":
23989
- return /* @__PURE__ */ jsxRuntime.jsx(HtmlPreview3, { content: displayContent });
24400
+ return /* @__PURE__ */ jsxRuntime.jsx(HtmlPreview3, { content: displayContent, config });
23990
24401
  case "markdown":
23991
24402
  return /* @__PURE__ */ jsxRuntime.jsx(MarkdownPreview, { content: displayContent, config });
23992
24403
  default:
@@ -24003,15 +24414,56 @@ var ArtifactViewer = React16.memo(function ArtifactViewer2({
24003
24414
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col bg-zinc-900 agent-sdk-light:bg-white relative", children: [
24004
24415
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-zinc-800/50 agent-sdk-light:border-zinc-200 bg-zinc-900/95 agent-sdk-light:bg-zinc-50 backdrop-blur-sm flex-shrink-0", children: [
24005
24416
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
24006
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-7 h-7 rounded-lg ${typeConfig2.bgColor} flex items-center justify-center flex-shrink-0`, children: /* @__PURE__ */ jsxRuntime.jsx(typeConfig2.icon, { size: 14, className: typeConfig2.color }) }),
24417
+ onTypeChange ? /* @__PURE__ */ jsxRuntime.jsx(
24418
+ ArtifactTypeSelector,
24419
+ {
24420
+ currentType: artifact.type,
24421
+ currentLanguage: artifact.language,
24422
+ onTypeChange: (newType, language) => onTypeChange(artifact.id, newType, language),
24423
+ disabled: isEditing
24424
+ }
24425
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-7 h-7 rounded-lg ${typeConfig2.bgColor} flex items-center justify-center flex-shrink-0`, children: /* @__PURE__ */ jsxRuntime.jsx(typeConfig2.icon, { size: 14, className: typeConfig2.color }) }),
24007
24426
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
24008
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium text-zinc-100 agent-sdk-light:text-zinc-900 truncate", children: artifact.title }),
24427
+ isEditingTitle ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
24428
+ /* @__PURE__ */ jsxRuntime.jsx(
24429
+ "input",
24430
+ {
24431
+ ref: titleInputRef,
24432
+ type: "text",
24433
+ value: editingTitle,
24434
+ onChange: (e) => setEditingTitle(e.target.value),
24435
+ onKeyDown: handleTitleKeyDown,
24436
+ onBlur: handleSaveTitle,
24437
+ disabled: isSavingTitle,
24438
+ className: "text-sm font-medium text-zinc-100 agent-sdk-light:text-zinc-900 bg-zinc-800 agent-sdk-light:bg-zinc-100 border border-zinc-600 agent-sdk-light:border-zinc-300 rounded px-1.5 py-0.5 outline-none focus:border-zinc-400 agent-sdk-light:focus:border-zinc-500 w-full min-w-0"
24439
+ }
24440
+ ),
24441
+ isSavingTitle && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 12, className: "text-zinc-500 animate-spin flex-shrink-0" })
24442
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
24443
+ "h3",
24444
+ {
24445
+ className: cn3(
24446
+ "text-sm font-medium text-zinc-100 agent-sdk-light:text-zinc-900 truncate",
24447
+ onTitleChange && !isEditing && "cursor-pointer hover:text-zinc-300 agent-sdk-light:hover:text-zinc-600"
24448
+ ),
24449
+ onDoubleClick: handleStartEditTitle,
24450
+ title: onTitleChange && !isEditing ? "\u53CC\u51FB\u7F16\u8F91\u6807\u9898" : void 0,
24451
+ children: artifact.title
24452
+ }
24453
+ ),
24009
24454
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: `text-xs ${typeConfig2.color}`, children: [
24010
24455
  typeConfig2.label,
24011
- artifact.language && artifact.language !== artifact.type && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-zinc-500 agent-sdk-light:text-zinc-500 ml-1", children: [
24456
+ artifact.language && artifact.language !== artifact.type && (onLanguageChange ? /* @__PURE__ */ jsxRuntime.jsx(
24457
+ ArtifactLanguageSelector,
24458
+ {
24459
+ currentLanguage: artifact.language,
24460
+ onLanguageChange: (lang) => onLanguageChange(artifact.id, lang),
24461
+ disabled: isEditing
24462
+ }
24463
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-zinc-500 agent-sdk-light:text-zinc-500 ml-1", children: [
24012
24464
  "\u2022 ",
24013
24465
  artifact.language
24014
- ] })
24466
+ ] }))
24015
24467
  ] })
24016
24468
  ] })
24017
24469
  ] }),
@@ -24136,15 +24588,54 @@ var ArtifactViewer = React16.memo(function ArtifactViewer2({
24136
24588
  isOpen,
24137
24589
  onClose,
24138
24590
  title: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
24139
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-6 h-6 rounded-md ${typeConfig2.bgColor} flex items-center justify-center`, children: /* @__PURE__ */ jsxRuntime.jsx(typeConfig2.icon, { size: 14, className: typeConfig2.color }) }),
24140
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: artifact.title })
24591
+ onTypeChange ? /* @__PURE__ */ jsxRuntime.jsx(
24592
+ ArtifactTypeSelector,
24593
+ {
24594
+ currentType: artifact.type,
24595
+ currentLanguage: artifact.language,
24596
+ onTypeChange: (newType, language) => onTypeChange(artifact.id, newType, language),
24597
+ disabled: isEditing
24598
+ }
24599
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-6 h-6 rounded-md ${typeConfig2.bgColor} flex items-center justify-center`, children: /* @__PURE__ */ jsxRuntime.jsx(typeConfig2.icon, { size: 14, className: typeConfig2.color }) }),
24600
+ isEditingTitle ? /* @__PURE__ */ jsxRuntime.jsx(
24601
+ "input",
24602
+ {
24603
+ ref: titleInputRef,
24604
+ type: "text",
24605
+ value: editingTitle,
24606
+ onChange: (e) => setEditingTitle(e.target.value),
24607
+ onKeyDown: handleTitleKeyDown,
24608
+ onBlur: handleSaveTitle,
24609
+ disabled: isSavingTitle,
24610
+ className: "text-sm font-medium text-zinc-100 agent-sdk-light:text-zinc-900 bg-zinc-800 agent-sdk-light:bg-zinc-100 border border-zinc-600 agent-sdk-light:border-zinc-300 rounded px-1.5 py-0.5 outline-none focus:border-zinc-400 agent-sdk-light:focus:border-zinc-500 min-w-0 flex-1"
24611
+ }
24612
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
24613
+ "span",
24614
+ {
24615
+ className: cn3(
24616
+ "truncate",
24617
+ onTitleChange && !isEditing && "cursor-pointer hover:text-zinc-300 agent-sdk-light:hover:text-zinc-600"
24618
+ ),
24619
+ onDoubleClick: handleStartEditTitle,
24620
+ title: onTitleChange && !isEditing ? "\u53CC\u51FB\u7F16\u8F91\u6807\u9898" : void 0,
24621
+ children: artifact.title
24622
+ }
24623
+ ),
24624
+ isSavingTitle && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 12, className: "text-zinc-500 animate-spin flex-shrink-0" })
24141
24625
  ] }),
24142
24626
  subtitle: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: typeConfig2.color, children: [
24143
24627
  typeConfig2.label,
24144
- artifact.language && artifact.language !== artifact.type && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-zinc-500 agent-sdk-light:text-zinc-500 ml-1", children: [
24628
+ artifact.language && artifact.language !== artifact.type && (onLanguageChange ? /* @__PURE__ */ jsxRuntime.jsx(
24629
+ ArtifactLanguageSelector,
24630
+ {
24631
+ currentLanguage: artifact.language,
24632
+ onLanguageChange: (lang) => onLanguageChange(artifact.id, lang),
24633
+ disabled: isEditing
24634
+ }
24635
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-zinc-500 agent-sdk-light:text-zinc-500 ml-1", children: [
24145
24636
  "\u2022 ",
24146
24637
  artifact.language
24147
- ] })
24638
+ ] }))
24148
24639
  ] }),
24149
24640
  isFullscreen,
24150
24641
  onFullscreenToggle,
@@ -25057,7 +25548,7 @@ function useArtifactPanel({ currentSessionId }) {
25057
25548
  const existingArtifact = Object.values(artifacts).find(
25058
25549
  (a) => {
25059
25550
  const sameContent = a.currentContent === data.content && a.type === data.type;
25060
- const sameId = a?.metadata?.workId === data.metadata?.workId;
25551
+ const sameId = !!data.metadata?.workId && a?.metadata?.workId === data.metadata?.workId;
25061
25552
  return sameContent || sameId;
25062
25553
  }
25063
25554
  );
@@ -25119,7 +25610,8 @@ function useArtifactPanel({ currentSessionId }) {
25119
25610
  setActiveArtifact,
25120
25611
  removeArtifact,
25121
25612
  reorderArtifacts,
25122
- updateArtifactContent
25613
+ updateArtifactContent,
25614
+ upsertArtifact
25123
25615
  };
25124
25616
  }
25125
25617
 
@@ -25935,7 +26427,8 @@ var AgentChat = React16__namespace.default.forwardRef(({
25935
26427
  setActiveArtifact,
25936
26428
  removeArtifact,
25937
26429
  reorderArtifacts,
25938
- updateArtifactContent
26430
+ updateArtifactContent,
26431
+ upsertArtifact
25939
26432
  } = useArtifactPanel({ currentSessionId: useAgentStore((state) => state.currentSession?.sessionId) });
25940
26433
  const reconnectRef = React16.useRef(null);
25941
26434
  const reconnectWrapper = React16.useCallback(async (messageId) => {
@@ -26409,6 +26902,52 @@ var AgentChat = React16__namespace.default.forwardRef(({
26409
26902
  toast.error("\u5220\u9664\u4EA7\u7269\u5931\u8D25");
26410
26903
  }
26411
26904
  },
26905
+ onTypeChange: async (artifactId, newType, language) => {
26906
+ try {
26907
+ const content = activeArtifact?.currentContent || "";
26908
+ const res = await artifactService.updateContent(artifactId, content, {
26909
+ type: newType,
26910
+ language
26911
+ });
26912
+ if (res.success && res.data) {
26913
+ upsertArtifact(artifactService.normalizeEntry(res.data));
26914
+ toast.success("\u7C7B\u578B\u5DF2\u66F4\u65B0");
26915
+ } else {
26916
+ toast.error("\u7C7B\u578B\u66F4\u65B0\u5931\u8D25");
26917
+ }
26918
+ } catch (err) {
26919
+ console.error("[AgentChat] Change artifact type failed:", err);
26920
+ toast.error("\u7C7B\u578B\u66F4\u65B0\u5931\u8D25");
26921
+ }
26922
+ },
26923
+ onLanguageChange: async (artifactId, language) => {
26924
+ try {
26925
+ const content = activeArtifact?.currentContent || "";
26926
+ const res = await artifactService.updateContent(artifactId, content, { language });
26927
+ if (res.success && res.data) {
26928
+ upsertArtifact(artifactService.normalizeEntry(res.data));
26929
+ } else {
26930
+ toast.error("\u8BED\u8A00\u66F4\u65B0\u5931\u8D25");
26931
+ }
26932
+ } catch (err) {
26933
+ console.error("[AgentChat] Change artifact language failed:", err);
26934
+ toast.error("\u8BED\u8A00\u66F4\u65B0\u5931\u8D25");
26935
+ }
26936
+ },
26937
+ onTitleChange: async (artifactId, title) => {
26938
+ try {
26939
+ const content = activeArtifact?.currentContent || "";
26940
+ const res = await artifactService.updateContent(artifactId, content, { title });
26941
+ if (res.success && res.data) {
26942
+ upsertArtifact(artifactService.normalizeEntry(res.data));
26943
+ } else {
26944
+ toast.error("\u6807\u9898\u66F4\u65B0\u5931\u8D25");
26945
+ }
26946
+ } catch (err) {
26947
+ console.error("[AgentChat] Change artifact title failed:", err);
26948
+ toast.error("\u6807\u9898\u66F4\u65B0\u5931\u8D25");
26949
+ }
26950
+ },
26412
26951
  onVisibilityChange: async (artifactId, isPublic) => {
26413
26952
  try {
26414
26953
  const res = await artifactService.updateVisibility(artifactId, isPublic);
@@ -26955,20 +27494,21 @@ var typeConfig = {
26955
27494
  };
26956
27495
  function FullscreenHtmlPreview({ content }) {
26957
27496
  const blobUrl = React16.useMemo(() => {
27497
+ const processed = resolveHtmlAssetUrls(content);
26958
27498
  const styleInjection = `
26959
27499
  <style>
26960
27500
  html, body { margin: 0; padding: 0; width: 100%; min-height: 100%; box-sizing: border-box; }
26961
27501
  * { box-sizing: border-box; }
26962
27502
  </style>`;
26963
- let modified = content;
26964
- if (content.includes("<head>")) {
26965
- modified = content.replace("<head>", "<head>" + styleInjection);
26966
- } else if (content.includes("<html>")) {
26967
- modified = content.replace("<html>", "<html><head>" + styleInjection + "</head>");
26968
- } else if (/<!(DOCTYPE|doctype)/i.test(content)) {
26969
- modified = content.replace(/(<!DOCTYPE[^>]*>|<!doctype[^>]*>)/i, "$1<html><head>" + styleInjection + "</head><body>") + "</body></html>";
27503
+ let modified = processed;
27504
+ if (processed.includes("<head>")) {
27505
+ modified = processed.replace("<head>", "<head>" + styleInjection);
27506
+ } else if (processed.includes("<html>")) {
27507
+ modified = processed.replace("<html>", "<html><head>" + styleInjection + "</head>");
27508
+ } else if (/<!(DOCTYPE|doctype)/i.test(processed)) {
27509
+ modified = processed.replace(/(<!DOCTYPE[^>]*>|<!doctype[^>]*>)/i, "$1<html><head>" + styleInjection + "</head><body>") + "</body></html>";
26970
27510
  } else {
26971
- modified = `<!DOCTYPE html><html><head>${styleInjection}</head><body>${content}</body></html>`;
27511
+ modified = `<!DOCTYPE html><html><head>${styleInjection}</head><body>${processed}</body></html>`;
26972
27512
  }
26973
27513
  const blob = new Blob([modified], { type: "text/html" });
26974
27514
  return URL.createObjectURL(blob);