/* Copyright 2026 Marimo. All rights reserved. */ import { CommandList } from "cmdk"; import { BetweenHorizontalStartIcon, PlusIcon, XIcon } from "lucide-react"; import React from "react"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; import { Command, CommandEmpty, CommandInput, CommandItem, } from "@/components/ui/command"; import type { Snippet } from "@/core/network/types"; import { useAsyncData } from "@/hooks/useAsyncData"; import { renderHTML } from "@/plugins/core/RenderHTML"; import { ErrorBanner } from "@/plugins/impl/common/error-banner"; import { PanelEmptyState } from "./empty-state"; import "./snippets-panel.css"; import { EditorView } from "@codemirror/view"; import { Suspense } from "react"; import { Spinner } from "@/components/icons/spinner"; import { Button } from "@/components/ui/button"; import { Tooltip } from "@/components/ui/tooltip"; import { useCellActions } from "@/core/cells/cells"; import { useLastFocusedCellId } from "@/core/cells/focus"; import { useRequestClient } from "@/core/network/requests"; import { LazyAnyLanguageCodeMirror } from "@/plugins/impl/code/LazyAnyLanguageCodeMirror"; import { useTheme } from "@/theme/useTheme"; import { cn } from "@/utils/cn"; import { HideInKioskMode } from "../../kiosk-mode"; import { ContributeSnippetButton } from "../components/contribute-snippet-button"; import { usePanelOrientation, usePanelSection } from "./panel-context"; const extensions = [EditorView.lineWrapping]; const SnippetsPanel: React.FC = () => { const { readSnippets } = useRequestClient(); const [selectedSnippet, setSelectedSnippet] = React.useState(); const orientation = usePanelOrientation(); const section = usePanelSection(); const { data: snippets, error, isPending, } = useAsyncData(() => { return readSnippets(); }, []); if (error) { return ; } if (isPending || !snippets) { return ; } const isVertical = orientation === "vertical"; return (
{/* Snippet list panel */}
No results setSelectedSnippet(snippet)} snippets={snippets.snippets} />
{/* Snippet viewer panel */}
{selectedSnippet ? ( setSelectedSnippet(undefined)} /> ) : ( )}
); }; export default SnippetsPanel; const SnippetViewer: React.FC<{ snippet: Snippet; onClose: () => void }> = ({ snippet, onClose, }) => { const { theme } = useTheme(); const { createNewCell } = useCellActions(); const lastFocusedCellId = useLastFocusedCellId(); const handleInsertSnippet = () => { // Add below last focused cell in reverse order for (const section of snippet.sections.toReversed()) { if (section.code) { createNewCell({ code: section.code, before: false, cellId: lastFocusedCellId ?? "__end__", // If the code already exists, skip creation skipIfCodeExists: true, }); } } }; const handleInsertCode = (code: string) => { createNewCell({ code, before: false, cellId: lastFocusedCellId ?? "__end__", }); }; return ( <>
{snippet.title}
{snippet.sections.map((section) => { const { code, html, id } = section; if (html) { return (
{renderHTML({ html: html })}
); } if (!code) { return null; } return (
); })}
); }; const SnippetList: React.FC<{ onSelect: (snippet: Snippet) => void; snippets: Snippet[]; }> = ({ snippets, onSelect }) => { return ( {snippets.map((snippet) => ( onSelect(snippet)} >
{snippet.title}
))}
); };