diff --git a/frontend/src/plugins/layout/OutlinePlugin.tsx b/frontend/src/plugins/layout/OutlinePlugin.tsx new file mode 100644 index 00000000000..d025bd06e7a --- /dev/null +++ b/frontend/src/plugins/layout/OutlinePlugin.tsx @@ -0,0 +1,69 @@ +/* Copyright 2024 Marimo. All rights reserved. */ +import { Provider, useAtomValue } from "jotai"; +import type { JSX } from "react"; +import { z } from "zod"; +import { notebookOutline } from "@/core/cells/cells"; +import { store } from "@/core/state/jotai"; +import { OutlineList } from "../../components/editor/chrome/panels/outline/floating-outline"; +import { + findOutlineElements, + useActiveOutline, +} from "../../components/editor/chrome/panels/outline/useActiveOutline"; +import type { + IStatelessPlugin, + IStatelessPluginProps, +} from "../stateless-plugin"; + +interface Data { + label?: string; +} + +const OutlineContent: React.FC<{ label?: string }> = ({ label }) => { + const { items } = useAtomValue(notebookOutline); + const headerElements = findOutlineElements(items); + const { activeHeaderId, activeOccurrences } = + useActiveOutline(headerElements); + + if (items.length === 0) { + return ( +
+ No outline found. Add markdown headings to your notebook to create an + outline. +
+ ); + } + + return ( +
+ {label && ( +
+ {label} +
+ )} + +
+ ); +}; + +export class OutlinePlugin implements IStatelessPlugin { + tagName = "marimo-outline"; + + validator = z.object({ + label: z.string().optional(), + }); + + render(props: IStatelessPluginProps): JSX.Element { + const { label } = props.data; + + return ( + + + + ); + } +} diff --git a/frontend/src/plugins/plugins.ts b/frontend/src/plugins/plugins.ts index ab0729b6cff..53028d50ed0 100644 --- a/frontend/src/plugins/plugins.ts +++ b/frontend/src/plugins/plugins.ts @@ -45,6 +45,7 @@ import { JsonOutputPlugin } from "./layout/JsonOutputPlugin"; import { LazyPlugin } from "./layout/LazyPlugin"; import { MimeRendererPlugin } from "./layout/MimeRenderPlugin"; import { MermaidPlugin } from "./layout/mermaid/MermaidPlugin"; +import { OutlinePlugin } from "./layout/OutlinePlugin"; import { ProgressPlugin } from "./layout/ProgressPlugin"; import { RoutesPlugin } from "./layout/RoutesPlugin"; import { StatPlugin } from "./layout/StatPlugin"; @@ -99,6 +100,7 @@ const LAYOUT_PLUGINS: Array> = [ new MimeRendererPlugin(), new MermaidPlugin(), new NavigationMenuPlugin(), + new OutlinePlugin(), new ProgressPlugin(), new RoutesPlugin(), new StatPlugin(), diff --git a/marimo/__init__.py b/marimo/__init__.py index 22d097e8ddf..0e96577022b 100644 --- a/marimo/__init__.py +++ b/marimo/__init__.py @@ -60,6 +60,7 @@ "nav_menu", "notebook_dir", "notebook_location", + "outline", "output", "pdf", "persistent_cache", @@ -98,6 +99,7 @@ from marimo._output.hypertext import Html from marimo._output.justify import center, left, right from marimo._output.md import latex, md +from marimo._output.outline import outline from marimo._output.show_code import show_code from marimo._plugins import ui from marimo._plugins.stateless import mpl, status diff --git a/marimo/_output/outline.py b/marimo/_output/outline.py new file mode 100644 index 00000000000..f0148bda0cd --- /dev/null +++ b/marimo/_output/outline.py @@ -0,0 +1,38 @@ +# Copyright 2024 Marimo. All rights reserved. +from __future__ import annotations + +from marimo._output.hypertext import Html +from marimo._output.rich_help import mddoc +from marimo._plugins.core.web_component import build_stateless_plugin + + +@mddoc +def outline(*, label: str = "") -> Html: + """Display a table of contents outline showing all markdown headers in the notebook. + + The outline automatically extracts all markdown headers from executed cells + and displays them in a hierarchical structure with clickable navigation. + + Examples: + Basic outline: + ```python + mo.outline() + ``` + + With custom label: + ```python + mo.outline(label="Table of Contents") + ``` + + Args: + label (str, optional): A descriptive label for the outline. Defaults to "". + + Returns: + Html: An HTML object that renders the outline component. + """ + return Html( + build_stateless_plugin( + component_name="marimo-outline", + args={"label": label}, + ) + ) diff --git a/tests/snapshots/api.txt b/tests/snapshots/api.txt index 629ebce650d..fbfae98788a 100644 --- a/tests/snapshots/api.txt +++ b/tests/snapshots/api.txt @@ -51,6 +51,7 @@ mpl nav_menu notebook_dir notebook_location +outline output append clear