/* Copyright 2026 Marimo. All rights reserved. */ import type { Notebook } from "@marimo-team/marimo-api"; import { SaveIcon } from "lucide-react"; import type { JSX } from "react"; import { Button as EditorButton } from "@/components/editor/inputs/Inputs"; import { Button } from "@/components/ui/button"; import { getNotebook } from "@/core/cells/cells"; import { notebookCells } from "@/core/cells/utils"; import { getMarimoVersion } from "@/core/meta/globals"; import { useEvent } from "@/hooks/useEvent"; import { downloadBlob } from "@/utils/download"; import { Paths } from "@/utils/paths"; import { useHotkey } from "../../hooks/useHotkey"; import { useImperativeModal } from "../modal/ImperativeModal"; import { renderShortcut } from "../shortcuts/renderShortcut"; import { DialogContent, DialogDescription, DialogFooter, DialogTitle, } from "../ui/dialog"; import { Tooltip } from "../ui/tooltip"; const RecoveryModal = (props: { proposedName: string; closeModal: () => void; }): JSX.Element => { const downloadRecoveryFile = () => { downloadBlob( new Blob([getNotebookJSON()], { type: "text/plain" }), `${props.proposedName}.json`, ); }; const getNotebookJSON = useEvent(() => { const notebook = getNotebook(); const cells = notebookCells(notebook); const notbook: Notebook["NotebookV1"] = { version: "1", metadata: { marimo_version: getMarimoVersion(), }, cells: cells.map((cell) => { return { id: cell.id, name: cell.name, code: cell.code, code_hash: null, config: { column: 0, ...cell.config, }, }; }), }; return JSON.stringify(notbook, null, 2); }); // NB: we use markdown class to have sane styling for list, paragraph return (
{ e.preventDefault(); downloadRecoveryFile(); props.closeModal(); }} onKeyDown={(e) => { if (e.key === "Escape") { props.closeModal(); } }} > Download unsaved changes?

This app has unsaved changes. To recover:

  1. Click the "Download" button. This will download a file called  {props.proposedName}.json. This file contains your code.
  2. In your terminal, type marimo recover {props.proposedName}.json {">"}{" "} {props.proposedName}.py to overwrite {props.proposedName}.py with the recovered changes.
); }; export const RecoveryButton = (props: { filename: string | null; needsSave: boolean; }): JSX.Element => { const { filename, needsSave } = props; const { openModal, closeModal } = useImperativeModal(); const proposedName = filename === null ? "app" : Paths.basename(filename).split(".")[0]; const openRecoveryModal = () => { if (needsSave) { openModal( , ); } }; useHotkey("global.save", openRecoveryModal); return ( ); };