From e31bec3aff673a23cc3b113d1a9d32d9dceb0a58 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Wed, 24 Jul 2024 20:36:11 +0800 Subject: [PATCH] save artifact content to cloudflare workers kv --- app/components/artifact.tsx | 77 ++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/app/components/artifact.tsx b/app/components/artifact.tsx index e08a713dc..dfcd45064 100644 --- a/app/components/artifact.tsx +++ b/app/components/artifact.tsx @@ -17,10 +17,12 @@ export function HTMLPreview(props: { code: string; autoHeight?: boolean; height?: number; + onLoad?: (title?: string) => void; }) { const ref = useRef(null); const frameId = useRef(nanoid()); const [iframeHeight, setIframeHeight] = useState(600); + const [title, setTitle] = useState(""); /* * https://stackoverflow.com/questions/19739001/what-is-the-difference-between-srcdoc-and-src-datatext-html-in-an * 1. using srcdoc @@ -31,9 +33,9 @@ export function HTMLPreview(props: { useEffect(() => { window.addEventListener("message", (e) => { - const { id, height } = e.data; + const { id, height, title } = e.data; + setTitle(title); if (id == frameId.current) { - console.log("setHeight", height); setIframeHeight(height); } }); @@ -65,32 +67,34 @@ export function HTMLPreview(props: { style={{ width: "100%", height }} // src={`data:text/html,${encodeURIComponent(srcDoc)}`} srcDoc={srcDoc} + onLoad={(e) => props?.onLoad(title)} > ); } -export function ArtifactShareButton({ getCode, id, style }) { +export function ArtifactShareButton({ getCode, id, style, fileName }) { const [name, setName] = useState(id); const [show, setShow] = useState(false); const shareUrl = useMemo(() => [location.origin, "#", Path.Artifact, "/", name].join(""), ); const upload = (code) => - fetch(ApiPath.Artifact, { - method: "POST", - body: getCode(), - }) - .then((res) => res.json()) - .then(({ id }) => { - if (id) { - setShow(true); - return setName(id); - } - throw Error(); - }) - .catch((e) => { - showToast(Locale.Export.Artifact.Error); - }); + id + ? Promise.resolve({ id }) + : fetch(ApiPath.Artifact, { + method: "POST", + body: getCode(), + }) + .then((res) => res.json()) + .then(({ id }) => { + if (id) { + return { id }; + } + throw Error(); + }) + .catch((e) => { + showToast(Locale.Export.Artifact.Error); + }); return ( <>
@@ -99,7 +103,10 @@ export function ArtifactShareButton({ getCode, id, style }) { bordered title={Locale.Export.Artifact.Title} onClick={() => { - upload(getCode()); + upload(getCode()).then(({ id }) => { + setShow(true); + setName(id); + }); }} />
@@ -115,7 +122,7 @@ export function ArtifactShareButton({ getCode, id, style }) { bordered text={Locale.Export.Download} onClick={() => { - downloadAs(getCode(), `${id}.html`).then(() => + downloadAs(getCode(), `${fileName || name}.html`).then(() => setShow(false), ); }} @@ -146,6 +153,8 @@ export function ArtifactShareButton({ getCode, id, style }) { export function Artifact() { const { id } = useParams(); const [code, setCode] = useState(""); + const [loading, setLoading] = useState(true); + const [fileName, setFileName] = useState(""); const { height } = useWindowSize(); useEffect(() => { @@ -167,23 +176,29 @@ export function Artifact() { >
-
- - } shadow /> - -
- code} /> + + } shadow /> + +
NextChat Artifact
+ code} fileName={fileName} />
- {code ? ( - - ) : ( - + {loading && } + {code && ( + { + setFileName(title); + setLoading(false); + }} + /> )} );