Skip to content

Commit

Permalink
fix: typescript error
Browse files Browse the repository at this point in the history
  • Loading branch information
lloydzhou committed Aug 21, 2024
1 parent b0e9a54 commit 4b9697e
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 75 deletions.
151 changes: 78 additions & 73 deletions app/components/artifacts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,84 +23,89 @@ import { Path, ApiPath, REPO_URL } from "@/app/constant";
import { Loading } from "./home";
import styles from "./artifacts.module.scss";

export const HTMLPreview = forwardRef<
{
reload: () => void;
},
{
code: string;
autoHeight?: boolean;
height?: number | string;
onLoad?: (title?: string) => void;
}
>(function HTMLPreview(props, ref) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [frameId, setFrameId] = useState<string>(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
* 2. using src with dataurl:
* easy to share
* length limit (Data URIs cannot be larger than 32,768 characters.)
*/
type HTMLPreviewProps = {
code: string;
autoHeight?: boolean;
height?: number | string;
onLoad?: (title?: string) => void;
};

useEffect(() => {
const handleMessage = (e: any) => {
const { id, height, title } = e.data;
setTitle(title);
if (id == frameId) {
setIframeHeight(height);
}
};
window.addEventListener("message", handleMessage);
return () => {
window.removeEventListener("message", handleMessage);
};
}, [frameId]);
export type HTMLPreviewHander = {
reload: () => void;
};

useImperativeHandle(ref, () => ({
reload: () => {
setFrameId(nanoid());
},
}));
export const HTMLPreview = forwardRef<HTMLPreviewHander, HTMLPreviewProps>(
function HTMLPreview(props, ref) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [frameId, setFrameId] = useState<string>(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
* 2. using src with dataurl:
* easy to share
* length limit (Data URIs cannot be larger than 32,768 characters.)
*/

const height = useMemo(() => {
if (!props.autoHeight) return props.height || 600;
if (typeof props.height === "string") {
return props.height;
}
const parentHeight = props.height || 600;
return iframeHeight + 40 > parentHeight ? parentHeight : iframeHeight + 40;
}, [props.autoHeight, props.height, iframeHeight]);
useEffect(() => {
const handleMessage = (e: any) => {
const { id, height, title } = e.data;
setTitle(title);
if (id == frameId) {
setIframeHeight(height);
}
};
window.addEventListener("message", handleMessage);
return () => {
window.removeEventListener("message", handleMessage);
};
}, [frameId]);

const srcDoc = useMemo(() => {
const script = `<script>new ResizeObserver((entries) => parent.postMessage({id: '${frameId}', height: entries[0].target.clientHeight}, '*')).observe(document.body)</script>`;
if (props.code.includes("</head>")) {
props.code.replace("</head>", "</head>" + script);
}
return props.code + script;
}, [props.code, frameId]);
useImperativeHandle(ref, () => ({
reload: () => {
setFrameId(nanoid());
},
}));

const handleOnLoad = () => {
if (props?.onLoad) {
props.onLoad(title);
}
};
const height = useMemo(() => {
if (!props.autoHeight) return props.height || 600;
if (typeof props.height === "string") {
return props.height;
}
const parentHeight = props.height || 600;
return iframeHeight + 40 > parentHeight
? parentHeight
: iframeHeight + 40;
}, [props.autoHeight, props.height, iframeHeight]);

return (
<iframe
className={styles["artifacts-iframe"]}
key={frameId}
ref={iframeRef}
sandbox="allow-forms allow-modals allow-scripts"
style={{ height }}
srcDoc={srcDoc}
onLoad={handleOnLoad}
/>
);
});
const srcDoc = useMemo(() => {
const script = `<script>new ResizeObserver((entries) => parent.postMessage({id: '${frameId}', height: entries[0].target.clientHeight}, '*')).observe(document.body)</script>`;
if (props.code.includes("</head>")) {
props.code.replace("</head>", "</head>" + script);
}
return props.code + script;
}, [props.code, frameId]);

const handleOnLoad = () => {
if (props?.onLoad) {
props.onLoad(title);
}
};

return (
<iframe
className={styles["artifacts-iframe"]}
key={frameId}
ref={iframeRef}
sandbox="allow-forms allow-modals allow-scripts"
style={{ height }}
srcDoc={srcDoc}
onLoad={handleOnLoad}
/>
);
},
);

export function ArtifactsShareButton({
getCode,
Expand Down Expand Up @@ -203,7 +208,7 @@ export function Artifacts() {
const [code, setCode] = useState("");
const [loading, setLoading] = useState(true);
const [fileName, setFileName] = useState("");
const previewRef = useRef<typeof HTMLPreview>(null);
const previewRef = useRef<HTMLPreviewHander>(null);

useEffect(() => {
if (id) {
Expand Down
8 changes: 6 additions & 2 deletions app/components/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import ReloadButtonIcon from "../icons/reload.svg";
import React from "react";
import { useDebouncedCallback } from "use-debounce";
import { showImageModal, FullScreen } from "./ui-lib";
import { ArtifactsShareButton, HTMLPreview } from "./artifacts";
import {
ArtifactsShareButton,
HTMLPreview,
HTMLPreviewHander,
} from "./artifacts";
import { Plugin } from "../constant";
import { useChatStore } from "../store";
import { IconButton } from "./button";
Expand Down Expand Up @@ -67,7 +71,7 @@ export function Mermaid(props: { code: string }) {

export function PreCode(props: { children: any }) {
const ref = useRef<HTMLPreElement>(null);
const previewRef = useRef<typeof HTMLPreview>(null);
const previewRef = useRef<HTMLPreviewHander>(null);
const [mermaidCode, setMermaidCode] = useState("");
const [htmlCode, setHtmlCode] = useState("");
const { height } = useWindowSize();
Expand Down

0 comments on commit 4b9697e

Please sign in to comment.