mirror of
				https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
				synced 2025-11-04 16:23:41 +08:00 
			
		
		
		
	fix: typescript error
This commit is contained in:
		@@ -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);
 | 
			
		||||
export type HTMLPreviewHander = {
 | 
			
		||||
  reload: () => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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.)
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    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]);
 | 
			
		||||
 | 
			
		||||
    useImperativeHandle(ref, () => ({
 | 
			
		||||
      reload: () => {
 | 
			
		||||
        setFrameId(nanoid());
 | 
			
		||||
      },
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    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]);
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    window.addEventListener("message", handleMessage);
 | 
			
		||||
    return () => {
 | 
			
		||||
      window.removeEventListener("message", handleMessage);
 | 
			
		||||
    };
 | 
			
		||||
  }, [frameId]);
 | 
			
		||||
 | 
			
		||||
  useImperativeHandle(ref, () => ({
 | 
			
		||||
    reload: () => {
 | 
			
		||||
      setFrameId(nanoid());
 | 
			
		||||
    },
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
  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]);
 | 
			
		||||
 | 
			
		||||
  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}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
    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,
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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";
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user