/* Copyright 2026 Marimo. All rights reserved. */ import { CopyIcon } from "lucide-react"; import React, { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { toast } from "@/components/ui/use-toast"; import { Constants } from "@/core/constants"; import { useRequestClient } from "@/core/network/requests"; import { VirtualFileTracker } from "@/core/static/virtual-file-tracker"; import { copyToClipboard } from "@/utils/copy"; import { Events } from "@/utils/events"; import { Input } from "../ui/input"; import { Tooltip } from "../ui/tooltip"; const BASE_URL = "https://static.marimo.app"; export const ShareStaticNotebookModal: React.FC<{ onClose: () => void; }> = ({ onClose }) => { const [slug, setSlug] = useState(""); const { exportAsHTML } = useRequestClient(); // 4 character random string const randomHash = useMemo(() => Math.random().toString(36).slice(2, 6), []); // Globally unique path const path = `${slug}-${randomHash}`; const url = `${BASE_URL}/static/${path}`; return ( { e.preventDefault(); onClose(); const html = await exportAsHTML({ download: false, includeCode: true, files: VirtualFileTracker.INSTANCE.filenames(), }); const prevToast = toast({ title: "Uploading static notebook...", description: "Please wait.", }); await fetch(`${BASE_URL}/api/static`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ html: html, path: path, }), }).catch(() => { prevToast.dismiss(); toast({ title: "Error uploading static page", description: ( Please try again later. If the problem persists, please file a bug report on{" "} GitHub . ), }); }); prevToast.dismiss(); toast({ title: "Static page uploaded!", description: ( The URL has been copied to your clipboard. You can share it with anyone. ), }); }} > Share static notebook You can publish a static, non-interactive version of this notebook to the public web. We will create a link for you that lives on{" "} {BASE_URL} . { const newSlug = e.target.value .toLowerCase() .replaceAll(/\s/g, "-") .replaceAll(/[^\da-z-]/g, ""); setSlug(newSlug); }} required={true} autoComplete="off" /> Anyone will be able to access your notebook at this URL: {url} Cancel { await copyToClipboard(url); }} > Create ); }; const CopyButton = (props: { text: string }) => { const [copied, setCopied] = React.useState(false); const copy = Events.stopPropagation(async (e) => { e.preventDefault(); await copyToClipboard(props.text); setCopied(true); setTimeout(() => setCopied(false), 2000); }); return ( ); };