/* Copyright 2026 Marimo. All rights reserved. */ import type { Extension } from "@codemirror/state"; import { EditorView } from "@codemirror/view"; import { focusPackagesInput } from "@/components/editor/chrome/panels/packages-utils"; import { chromeAtom } from "@/components/editor/chrome/state"; import { Kbd } from "@/components/ui/kbd"; import { userConfigAtom } from "@/core/config/config"; import { getRequestClient } from "@/core/network/requests"; import { store } from "@/core/state/jotai"; import { toast } from "../../../components/ui/use-toast"; interface ReplaceCommand { match: string; onMatch: () => void; title: React.ReactNode; description: React.ReactNode; } const COMMON_MAGIC_COMMANDS = [ // Line magics "%alias", "%cd", "%clear", "%debug", "%env", "%load", "%load_ext", "%lsmagic", "%matplotlib", "%pwd", "%who_ls", "%system", // Cell magics "%%time", "%%timeit", "%%writefile", "%%capture", "%%html", "%%latex", ]; let toastResponse: ReturnType; export function jupyterHelpExtension(): Extension { // Function to update the module reload mode // Updates local config and saves to server const handleUpdateModuleReload = async (mode: "lazy" | "autorun" | "off") => { const config = store.get(userConfigAtom); const { saveUserConfig } = getRequestClient(); // Send only the changed portion to avoid overwriting other config values await saveUserConfig({ config: { runtime: { auto_reload: mode } } }).then( () => // Update local state with merged config store.set(userConfigAtom, { ...config, runtime: { ...config.runtime, auto_reload: mode }, }), ); }; const commands: ReplaceCommand[] = [ // Package installation { match: "!pip install", onMatch: () => { store.set(chromeAtom, (prev) => ({ ...prev, isSidebarOpen: true, selectedPanel: "packages", })); focusPackagesInput(); }, title: "Package Installation", description: ( <> The package manager sidebar has been opened.
Install packages directly from there instead. ), }, { match: "!uv pip install", onMatch: () => { store.set(chromeAtom, (prev) => ({ ...prev, isSidebarOpen: true, selectedPanel: "packages", })); focusPackagesInput(); }, title: "Package Installation", description: ( <> The package manager sidebar has been opened.
Install packages directly from there instead. ), }, { match: "!uv add", onMatch: () => { store.set(chromeAtom, (prev) => ({ ...prev, isSidebarOpen: true, selectedPanel: "packages", })); focusPackagesInput(); }, title: "Package Installation", description: ( <> The package manager sidebar has been opened.
Install packages directly from there instead. ), }, // Module reload { match: "%autoreload 2", onMatch: async () => { await handleUpdateModuleReload("autorun"); }, title: "Module reload", description: ( <> Module reload mode set to autorun - module changes will re-run the cells that import them. ), }, { match: "%autoreload 1", onMatch: async () => { await handleUpdateModuleReload("lazy"); }, title: "Module reload", description: ( <> Module reload mode set to lazy - module changes will mark the cells that import them as stale. ), }, { match: "%autoreload 0", onMatch: async () => { await handleUpdateModuleReload("off"); }, title: "Module reload", description: ( <>Module reload disabled - module changes will not be detected. ), }, // Shell commands { match: "!ls", onMatch: () => { // noop }, title: "Listing files", description: ( <> Shell commands are not directly supported.
Use import subprocess; subprocess.run(['ls']) instead. ), }, // Common magic commands ...COMMON_MAGIC_COMMANDS.map((cmd) => ({ match: cmd, onMatch: () => { // noop }, title: {cmd}, description: ( <> Magic commands are not supported in marimo. Read more about replacements in the{" "} documentation . ), })), ]; const commandMap = new Map(commands.map((cmd) => [cmd.match, cmd])); return EditorView.updateListener.of((update) => { if (update.docChanged) { const cursor = update.state.selection.main; // Get text from the start of the line to the cursor const doc = update.state.doc; const line = doc.lineAt(cursor.head); const text = doc.sliceString(line.from, cursor.anchor).trim(); const cmd = commandMap.get(text); if (cmd) { // Execute the command cmd.onMatch(); // Show toast if (toastResponse) { toastResponse.upsert({ title: cmd.title, description: cmd.description, }); } else { toastResponse = toast({ title: cmd.title, description: cmd.description, }); } } } }); }