/* Copyright 2026 Marimo. All rights reserved. */ import type { EditorView } from "@codemirror/view"; import { useAtom, useAtomValue, useSetAtom } from "jotai"; import { BetweenHorizontalStartIcon, CircleHelpIcon, EraserIcon, HistoryIcon, PlayIcon, } from "lucide-react"; import type React from "react"; import { Suspense, useRef, useState } from "react"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; import useEvent from "react-use-event-hook"; import { useCellActions, useNotebook } from "@/core/cells/cells"; import { useLastFocusedCellId } from "@/core/cells/focus"; import { HTMLCellId, SCRATCH_CELL_ID } from "@/core/cells/ids"; import { DEFAULT_CELL_NAME } from "@/core/cells/names"; import type { LanguageAdapterType } from "@/core/codemirror/language/types"; import { useResolvedMarimoConfig } from "@/core/config/config"; import { CSSClasses } from "@/core/constants"; import { useRequestClient } from "@/core/network/requests"; import type { CellConfig } from "@/core/network/types"; import { LazyAnyLanguageCodeMirror } from "@/plugins/impl/code/LazyAnyLanguageCodeMirror"; import { useTheme } from "@/theme/useTheme"; import { cn } from "@/utils/cn"; import { Functions } from "@/utils/functions"; import { CellEditor } from "../editor/cell/code/cell-editor"; import { usePanelOrientation, usePanelSection, } from "../editor/chrome/panels/panel-context"; import { HideInKioskMode } from "../editor/kiosk-mode"; import { OutputArea } from "../editor/Output"; import { ConsoleOutput } from "../editor/output/console/ConsoleOutput"; import { Spinner } from "../icons/spinner"; import { renderShortcut } from "../shortcuts/renderShortcut"; import { Button } from "../ui/button"; import { Tooltip } from "../ui/tooltip"; import { addToHistoryAtom, historyVisibleAtom, scratchpadHistoryAtom, } from "./scratchpad-history"; const scratchpadCellConfig: CellConfig = { hide_code: false, disabled: false, }; export const ScratchPad: React.FC = () => { const notebookState = useNotebook(); const [userConfig] = useResolvedMarimoConfig(); const { theme } = useTheme(); const ref = useRef(null); const lastFocusedCellId = useLastFocusedCellId(); const { createNewCell, updateCellCode } = useCellActions(); const { sendRunScratchpad } = useRequestClient(); const orientation = usePanelOrientation(); const section = usePanelSection(); const cellId = SCRATCH_CELL_ID; const cellRuntime = notebookState.cellRuntime[cellId]; const output = cellRuntime?.output; const status = cellRuntime?.status; const consoleOutputs = cellRuntime?.consoleOutputs; const cellData = notebookState.cellData[cellId]; const code = cellData?.code ?? ""; const addToHistory = useSetAtom(addToHistoryAtom); const [historyVisible, setHistoryVisible] = useAtom(historyVisibleAtom); const history = useAtomValue(scratchpadHistoryAtom); const handleRun = useEvent(() => { void sendRunScratchpad({ code }); addToHistory(code); }); const handleInsertCode = useEvent(() => { if (!code.trim()) { return; } createNewCell({ code, before: false, cellId: lastFocusedCellId ?? "__end__", }); }); const handleClearCode = useEvent(() => { updateCellCode({ cellId, code: "", formattingChange: false, }); void sendRunScratchpad({ code: "" }); const ev = ref.current; if (ev) { ev.dispatch({ changes: { from: 0, to: ev.state.doc.length, insert: "", }, }); } }); const handleSelectHistoryItem = useEvent((item: string) => { setHistoryVisible(false); updateCellCode({ cellId, code: item, formattingChange: false, }); const ev = ref.current; if (ev) { ev.dispatch({ changes: { from: 0, to: ev.state.doc.length, insert: item, }, }); } }); const [languageAdapter, setLanguageAdapter] = useState(); const renderHistory = () => { if (!historyVisible) { return null; } return (
{history.map((item, index) => (
handleSelectHistoryItem(item)} >
))}
); }; const renderToolbar = () => (
{(status === "running" || status === "queued") && ( )}
Use this scratchpad to experiment with code without restrictions on variable names. Variables defined here aren't saved to notebook memory, and the code is not saved in the notebook file. } >
); const isVertical = orientation === "vertical"; return (
{/* Editor panel */}
{renderToolbar()}
{ ref.current = ev; }} hidden={false} showHiddenCode={Functions.NOOP} languageAdapter={languageAdapter} setLanguageAdapter={setLanguageAdapter} />
{renderHistory()}
{/* Output panel */}
); };