Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Notebook Plugin

Status: Implemented in the first rollout with some richer notebook chrome and explorer surfaces still pending. packages/notebook now owns notebook authoring commands, note-level and scoped notebook execution, notebook document parsing, per-note runtime sessions, notebook execution transport selection, DuckDB-backed query services, notebook-owned output renderers, markdown preview mounting, and notebook generated-state persistence.

The notebook feature is a first-party official plugin that adds reactive, executable notebook behavior to markdown notes while using a dedicated notebook-flavored markdown file identity, .notebook.md.

This page describes the planned component boundary. The current runtime and markdown stack are documented in Plugin Packages, Workspace App, and Editor Rendering and Views.

Purpose

The plugin’s job is to let a .notebook.md note behave like a notebook through a notebook-owned file view, parsing pipeline, runtime, and output surfaces.

The current first-rollout feature set is:

  • multiple executable cells mixed with ordinary markdown prose
  • deterministic DAG-based execution similar to marimo or Pluto-style dataflow
  • browser and PWA-compatible execution with no required server process
  • DuckDB-backed data work over vault files and app-derived tables
  • typed rich outputs such as tables, charts, markdown, HTML, structured input outputs, trusted ephemeral DOM, images, and errors
  • plain const|let|var name = view(expression) inputs powered by the notebook-input runtime, with supported controls rendered from @lapis-notes/ui primitives, notebook-input-owned input styles imported by the plugin, and htl html and svg helpers for trusted live DOM outputs
  • generated outputs and session state stored outside the canonical markdown file

Package and Identity

The plugin identity is:

  • package name: @lapis-notes/notebook
  • manifest id: lapis-notebook
  • command prefix: notebook:

Notebook installs from the official registry under lapis-notebook. The workspace no longer bundles it by default; when no installed handler is present, opening a .notebook.md file uses the on-demand official install prompt before retrying the file open.

Official notebook releases package DuckDB wasm/workers and other heavy runtime chunks inside the signed .lapis-plugin bundle. First DuckDB-backed SQL execution resolves those already-installed plugin asset URLs through the shared plugin asset server. The main Vite build uses emptyOutDir: true so stale hashed chunks from prior builds are not copied into release inputs; wasm and workers land under dist/duckdb/ instead of inlined JS payloads.

The package root is also expected to stay startup-thin. Boot-critical imports should stop at plugin registration surfaces, while worker-backed execution code and notebook command helpers load lazily on first render or command use.

The plugin should follow the same reversible lifecycle rules described in Plugin Runtime.

Planned Package Refactor

The current rollout keeps notebook behavior in one first-party plugin package, but the intended direction is to split runtime-heavy and reusable notebook pieces into smaller packages while preserving @lapis-notes/notebook as the product plugin identity.

The target package boundaries are:

PackagePlanned responsibility
@lapis-notes/notebookProduct plugin: manifest, commands, views, settings, renderers, persistence integration, and lazy runtime wiring.
@lapis-notes/notebook-coreNotebook model, parser-facing types, output protocol, graph/session state contracts, execution contracts, and runtime feature flags.
@lapis-notes/notebook-stdlibLapis builtin registry, host-neutral lapis.* helper implementations, raw DOM helper loading, bundled dependency descriptors, and standard-library-style helper contracts.
@lapis-notes/notebook-inputPlain view(...) transform/runtime, the tracked wrapper over the DOM helper surface, UI-backed input factories for supported controls, and input value lifecycle hooks.
@lapis-notes/notebook-duckdbDuckDB runtime contracts, query normalization, catalog/preview helpers, vault file registration helpers, and app-table snapshot registration helpers.
@lapis-notes/notebook-duckdb-assetsDuckDB-Wasm and DuckDB worker asset ownership plus bundle URL resolution.
@lapis-notes/notebook-workerWorker protocol, worker shell transport helpers, command/host request brokers, host response helpers, and worker eligibility policy.

The split should happen incrementally. The first extraction should introduce contracts and package seams without changing notebook behavior. Each package must preserve the current startup-thin rule: opening the workspace or activating the product plugin must not eagerly load DuckDB-Wasm, TypeScript, Observable inputs, htl, d3, Plot, or dynamic dependency resolvers.

The first implementation slice created @lapis-notes/notebook-core for the output protocol. Later slices expanded notebook core with model contracts, session state/progress contracts, runtime feature flags, generic graph helpers, and execution contracts, and created @lapis-notes/notebook-stdlib for builtin registry contracts, the pure user-executed lapis helper implementations, documented bare global names, dependency-global validation helpers, source-level runtime feature detection, host-neutral vault/file helper implementations, the raw html/svg DOM helper loader, and shared Promises/Generators stdlib helpers, @lapis-notes/notebook-duckdb-assets for DuckDB-Wasm and worker asset URL resolution, @lapis-notes/notebook-input for the host-neutral plain view(...) source transform, tracked input metadata, DOM input binding, callback-driven view runtime helper, and the local UI-backed control factories, @lapis-notes/notebook-duckdb for DuckDB runtime contracts and pure query normalization helpers, and @lapis-notes/notebook-worker for generic worker transport contracts. @lapis-notes/notebook consumes those packages while the rest of the runtime remains in the product plugin.

The package split is implemented for the current official plugin boundary. Remaining notebook tasks track product features and runtime language-compatibility decisions rather than package extraction work.

The current execution boundary exposes host-neutral DuckDB, notebook file-read, and JavaScript-like compile provider seams. Browser and PWA sessions continue to use DuckDB-Wasm, vault adapter reads, and the lazy TypeScript compiler by default, while Electron sessions inject the native DuckDB sidecar provider when the desktop capability registry marks notebook support as available. Electron DuckDB vault-file registration sends the resolved notebook-relative path plus the native vault root through validated main-process IPC so CSV, Parquet, and JSON aliases can be linked or copied by the sidecar without renderer byte-buffer transfer. Native TypeScript compilation was evaluated and is not part of the current architecture because it would not move JavaScript-like cell evaluation or make in-process synchronous code preemptively cancellable. Host-only function providers stay on the in-process execution path rather than being serialized to the notebook worker.

Styling Ownership

Notebook-specific chrome and selectors are owned by the notebook package under the first-party Plugin CSS Contract.

  • notebook-owned wrapper classes use the notebook- prefix, for example notebook-cell, notebook-output, and notebook-dataframe
  • notebook BEM hooks cover the view, cells, outputs, dataframe viewer, inspector panel, and dependencies panel
  • notebook presentation may continue to mix Tailwind utility classes with those stable wrapper classes, but the stable selectors are the supported override surface
  • notebook-specific CSS rules should live in the notebook package stylesheet instead of workspace-level overrides
  • notebook-specific variables use the --notebook-... prefix and default to core theme variables such as --background-primary, --background-modifier-border, --text-normal, --text-muted, --interactive-accent, and --text-on-accent
  • the markdown plugin may still apply a notebook layout marker class when it owns the editor DOM for .notebook.md leaves, but the notebook-specific visual rules for that marker belong with the notebook plugin
  • the workspace imports notebook dist/app.css, which contains notebook BEM/custom rules only
  • standalone/native notebook installs and plugin-local testing use notebook dist/styles.css, which includes UI theme/full Tailwind output plus notebook addon rules
  • the current rollout loads notebook workspace CSS through an explicit workspace import of the built plugin CSS asset rather than through plugin-manager lifecycle CSS loading

File Ownership

Notebook documents remain ordinary markdown notes, but they are owned as a distinct file flavor.

  • canonical file extension: .notebook.md
  • canonical authoring surface: notebook-owned markdown source and preview modes
  • notebook identity: inferred from the file path suffix
  • optional notebook config: note frontmatter under a top-level notebook: key for runtime settings such as mode and mounts
  • plugin settings: app-level notebook preferences persisted by notebook, including dependency-view defaults, panel auto-open behavior, the validated runtime dependency registry, and a settings handoff into the dedicated Packages side view

The markdown plugin continues to own base .md parsing, file loading, and non-notebook rendering. The notebook plugin owns .notebook.md files through its own view registration and runtime lifecycle, instead of patching markdown leaves after they load.

Responsibilities

The plugin owns the following runtime surfaces.

Notebook document parsing

  • detect notebook files from the .notebook.md path suffix
  • parse optional notebook frontmatter config when present
  • parse notebook cell directives and their attributes
  • maintain stable cell ids and notebook document metadata

Cell UI and editing

  • render cell chrome and outputs, including a per-cell inline run control on the code action rail that flips to an inline stop control only for the currently running cell while queued cells stay visibly scheduled, plus subtle stale-state highlighting for cells whose inputs changed
  • split notebook cell presentation into a code card and a separate output surface, each with its own single-column left action rail that uses the normal app border color by default and promotes to the interactive accent for the currently selected cell so the source and output surfaces stay visually linked, a border-style collapse toggle, a clickable rail spacer below the top action icon that shares the same collapse behavior, independent collapse state, selection highlight, and collapsed summary row when the section body is hidden, with collapsed source previews truncated to a single-line ellipsis and collapsed code or output bodies reopening when clicked
  • persist per-cell code and output collapse state as notebook cell directive attributes so collapsed sections survive note reloads
  • show per-cell execution status and duration in the code surface footer, plus per-output actions such as clearing cell outputs from the output rail menu
  • expose a notebook-owned session inspector in the right side split with variables and DuckDB data sources for the active notebook, including clickable cell-id navigation from variable declarations and usages plus expandable DuckDB table previews with live row counts, inline row and column counts in each data-source row, and source and column type icons that match the shared property icon language
  • expose a separate notebook-owned dependencies side view in the left split for the active notebook, with a Marimo-inspired minimap tab for notebook-order dataflow tracing plus a graph tab that renders a fit-to-viewport notebook DAG with vertical or horizontal tree layouts, syntax-highlighted code nodes, and popup cell or edge details
  • persist notebook plugin preferences such as dependency-view default tab, dependency-graph layout, dependency-graph wheel zoom sensitivity, whether notebook sources or dependencies panels auto-open when a notebook becomes active, validated runtime dependency descriptors that the Packages side view manages for later lazy loading, and per-note notebook.autorun frontmatter overrides for stale-cell reruns that fall back to the global auto-run setting when omitted
  • provide insertion commands for code, SQL, and dynamic markdown cells
  • support notebook-aware reading-mode presentation and markdown-source authoring commands

Output rendering

  • define a typed notebook output protocol for cell results
  • map output kinds onto Svelte renderers through a renderer registry
  • render first-class table and chart outputs instead of treating them as opaque HTML blobs
  • render structured input outputs through notebook-owned input wrappers that can mount live input DOM nodes by notebook cell and variable identity, and render trusted live DOM outputs produced by htl or explicit DOM helpers through notebook-owned DOM mount points rather than persisting the DOM itself
  • preserve explicit error, loading, and stale-state renderers as notebook-owned UI, with loading and stale feedback kept in the per-cell footer while output is pending

Notebook session runtime

  • create and dispose per-notebook execution sessions and notebook-scoped execution runtimes so different notebook notes can run independently and stopping one note does not cancel another
  • build and update the dependency DAG
  • track stale, running, errored, and disabled cells
  • run notebook execution only from explicit user actions such as per-cell run and notebook-level run commands, while still reusing persisted outputs for unchanged notes when they are already available

Data access and query services

  • host a DuckDB-backed data engine for notebook sessions
  • register vault files and app-derived tables with notebook sessions
  • expose browser-safe helpers for loading CSV, Parquet, JSON, and SQLite-derived app state
  • expose a standard-library-style builtin surface where Lapis-provided helpers live canonically under lapis.*, including output helpers, DOM helpers, inputs, DuckDB, vault access, and bundled visualization libraries
  • support user-installed notebook dependencies as named globals that are not mirrored under lapis.*

Persistence

  • persist outputs, run state, and cached notebook session artifacts as generated state
  • keep canonical note bytes free of rendered outputs and transient session state

User-Facing Surfaces

The first rollout exposes notebook behavior through notebook-owned markdown files instead of through a separate JSON notebook format.

Current user-facing surfaces include:

  • notebook view for .notebook.md leaves, including a grouped notebook pane menu for auto-run on cell changes plus notebook packages, dependencies, and sources, with auto-run stored in notebook.autorun frontmatter for the current note and falling back to the global plugin setting when the field is omitted
  • a notebook session side view on the right split that lists active notebook variables and DuckDB-backed data sources for the active note, lets declared-by and used-by cell ids jump back to the corresponding notebook cell, and can expand live DuckDB table previews in place
  • a notebook dependencies side view on the left split that follows the active notebook, provides a minimap tab for direct and transitive dependency tracing in notebook order, and renders a fit-to-viewport dependency graph with selectable syntax-highlighted code nodes, edge overlays, duplicate-definition and cycle summaries, and vertical or horizontal tree modes
  • a notebook packages side view on the right split that follows the active notebook, groups configured runtime package descriptors into direct imports, indirect imports, and available imports, resolves transitive package metadata from pinned direct imports when registry metadata is available, offers npm-registry-backed package-name autocomplete plus inline install progress, keeps the search, custom-install, and available-import sections available even when no notebook is active while hiding notebook-specific direct and indirect package usage sections in that state, keeps the package-detail pane hidden until the user explicitly selects a package row, starts the count-based direct, indirect, and available package accordions collapsed when their counts are zero while reopening them when packages first appear, lets direct, indirect, and available package rows toggle selection off again to hide the detail pane, highlights the selected available import with the theme secondary color scheme, keeps selected search results visible so their descriptions remain readable, renders search results as a flat white divider list with a separate selected-row highlight, shows inline install and customize actions below the selected uninstalled search result description, expands and scrolls to the manual custom-install accordion when Customize is chosen, marks already-installed search results with uninstall and in-place update actions, lets the user add, update, or remove validated remote-ESM package descriptors, recognizes direct notebook package usage from configured globals or explicit package imports, and shows per-package usage and validation details without replacing the existing dependency graph panel
  • notebook leaves use a full-width reading and editing layout instead of the centered readable-line-width markdown layout
  • convert active markdown note to notebook file, insert notebook cell, and demo notebook scaffolding commands
  • slash-menu notebook authoring suggestions in .notebook.md source editors for inserting canonical code, SQL, input, and markdown cell templates via /code, /sql, /input, and /markdown
  • run notebook now from commands, the notebook view header action, and a notebook-owned status-bar action for the active notebook leaf
  • notebook execution commands and runtime entrypoints now short-circuit when the app is in Safe Mode with notebook execution disabled, surfacing a user-visible notice instead of starting execution against a recovery session
  • active notebook leaves register notebook-owned status-bar items that surface Safe Mode execution disablement, whether the current notebook is using the worker runtime or an in-process fallback plus the concrete fallback reason when applicable, whether visible outputs were restored from generated state or recomputed in the current session, and compact notebook-owned run, rerun-stale, and clear-output actions independent of workspace header chrome
  • run or rerun a single cell from the inline code-rail action without requiring the cell output surface to be empty first, while showing an inline stop control only on the actively running cell during execution and using both the rail border control and the empty rail spacer beneath the top icon to collapse the code or output section
  • live-preview notebook cell chrome with a status footer for scheduled, running, stale, errored, completed, and idle cells, including setup-phase detail text, elapsed runtime display, stale styling, and an output options menu for clearing cell outputs
  • run current notebook cell from notebook source or live preview based on the active cursor
  • run stale notebook cells from commands, the notebook view header action, and a notebook-owned status-bar action for the active notebook leaf
  • insert notebook input cell creates a TypeScript cell with plain const|let|var name = view(Inputs...) syntax
  • clear all notebook outputs from the notebook view header action, the notebook-owned status-bar action for the active notebook leaf, and a command-palette command while keeping .notebook.md source cells untouched
  • notebook-aware cell outputs in reading mode
  • notebook-aware cell outputs beneath cell blocks in live preview
  • interactive table outputs for SQL and data-oriented code cells, including a notebook-owned dataframe viewer with marimo-inspired stacked column headers, compact per-column summary previews, client-side search and filtering, column visibility toggles, a right-side column profile sheet, row expansion for smaller result sets, and virtualized scrolling when rendered pages grow beyond a couple hundred rows
  • plain view(...) inputs such as const gain = view(Inputs.range(...)); input changes update the session value and automatically rerun downstream notebook cells while avoiding reruns of the input-producing cell itself, waking only affected cell renderers and preserving existing outputs while those cells are queued or running
  • htl-backed trusted DOM outputs using the injected html and svg tagged template helpers; these DOM nodes are live only for the current runtime session and are recreated by rerunning the cell
  • spec-driven chart outputs for notebook visualizations
  • a demo-notebook/ vault-root sample suite with representative notes and assets for notebook command, DuckDB, renderer, plain view(...) input, and htl testing

Runtime Services

The plugin uses dedicated notebook services layered beside the current markdown renderer.

Current internal service boundaries:

  • NotebookDocument - parsed notebook note structure
  • NotebookRuntimeController - product-local facade over per-note runtime sessions that owns session lookup, graph-backed snapshots and state queries for notebook chrome, commands, and render services, render-layer cell output clearing, input value updates plus downstream dependent resolution, execution preparation, checkpoint rollback, execution-state snapshots, filtered runtime subscriptions, and the shared boundary used by notebook commands, render services, and plugin chrome
  • NotebookSessionManager - lower-level lifecycle owner for per-note runtime sessions used underneath the controller facade
  • NotebookGraph - refs, defs, topological sort, stale propagation
  • NotebookWorkerClient - notebook execution boundary used by the plugin today, loaded lazily by the plugin on first notebook execution or render that needs execution, backed by a dedicated worker transport for notebooks that do not require DuckDB, live browser DOM features, or host-only execution providers plus an in-process path for DuckDB-backed, live-DOM-backed, or provider-injected notebooks, with helper-only DOM usages such as DOM.uid remaining worker-eligible; the client, worker shell, DuckDB service, and TypeScript compiler are split so simple notebook runs do not load DuckDB-Wasm and JavaScript cells do not load the TypeScript compiler
  • Notebook execution providers - host-neutral DuckDB, file reader, and JavaScript-like compile contracts keep cell syntax stable while allowing the product plugin or a desktop host to replace DuckDB execution, notebook-relative file reads, or TypeScript/static-import compilation. Electron currently supplies the DuckDB provider through a native sidecar and registers vault-backed CSV, Parquet, and JSON aliases from validated native vault paths when the active vault is an Electron desktop folder; native TypeScript compilation is outside the current architecture because it needs separate telemetry, compiler timeout, and JavaScript-like evaluation cancellation designs; function-backed host-only providers stay on the in-process path instead of being serialized to the worker, while default browser execution continues to use worker host requests, the app vault adapter, DuckDB-Wasm, byte-buffer file registration, and the lazy TypeScript compiler.
  • notebook input runtime - a notebook-local in-process runtime feature that exposes Lapis-provided input and DOM helpers canonically under lapis.*, may expose documented bare convenience globals such as Inputs, html, and svg, tracks scalar input values in the session, emits structured input outputs when tracked metadata is available, mounts supported live input DOM nodes through @lapis-notes/ui primitives, emits downstream rerun requests when inputs change, and clears same-cell runtime definitions before reruns so input cells can be executed repeatedly
  • @lapis-notes/notebook-input - reusable host-neutral input source helpers, lazy DOM helper loading, UI-backed Inputs factories for supported controls, explicit unsupported errors for the remaining unimplemented factories, DOM input node binding, and callback-driven view runtime helpers consumed by notebook execution
  • @lapis-notes/notebook-duckdb - reusable DuckDB runtime contracts, app-table and vault-file registration contracts, query-result types, catalog/preview types, DuckDB type mapping, pure query normalization helpers, and pure catalog/identifier helpers consumed by the concrete product-plugin DuckDB service and inspector surfaces
  • NotebookDuckDbService - DuckDB-Wasm lifecycle and table registration
  • @lapis-notes/notebook-core - reusable notebook model contracts, output protocol, generic execution result/options contracts, pure lapis output helper functions, and generic notebook graph helpers consumed by the product plugin
  • notebook language-service projection - pure helpers in notebook core concatenate TypeScript cells into a serializable virtual TypeScript document, track per-cell segment offsets/lines, and map provider positions/ranges back to cell-local positions
  • @lapis-notes/notebook-duckdb-assets - reusable DuckDB-Wasm and DuckDB worker asset URL resolver consumed by the product plugin’s DuckDB service
  • @lapis-notes/notebook-stdlib - reusable notebook builtin registry contracts, documented bare global names, dependency-global validation helpers, source-level runtime feature detection, host-neutral vault/file helper implementations, and the bundled stdlib bootstrap consumed by the product plugin
  • @lapis-notes/notebook-worker - reusable generic worker transport message contracts consumed through a product-plugin alias layer, main-thread command promise plumbing and host request response wrapping used by the product-plugin worker client, worker-side shell dispatch and host request promise plumbing used by the concrete product-plugin worker entrypoint, and pure worker eligibility policy consumed by the product-plugin worker client
  • NotebookOutputRenderer - notebook output protocol and renderer registry
  • NotebookOutputStore - generated-state persistence for outputs and session snapshots

Notebook execution reports per-note progress through app.notifications. Command-triggered runs can surface as progress notifications with cancel actions, while automatic or input-triggered reruns use lower-priority status progress. Cancellation delegates to the existing per-notebook runtime termination path.

Notebook also registers the browser TypeScript provider from @lapis-notes/language-service and contributes minimal global declaration text for documented notebook globals such as lapis, Inputs, html, svg, Generators, and Promises. Direct cell editors pass a languageServiceDocumentContext Facet into their CodeMirror instance so diagnostics, completions, and hover operate against the full virtual notebook TypeScript document while displaying ranges in the focused cell.

Builtins And Runtime Dependencies

The intended standard-library direction is inspired by Observable’s standard library but adapted to Lapis’ markdown-first, vault-backed, generated-state model.

Lapis-provided builtins are canonically exposed under the lapis namespace. Planned examples include:

  • lapis.table, lapis.chart, lapis.text, lapis.markdown, lapis.html, lapis.svg, lapis.image, and lapis.error for typed notebook outputs and trusted DOM helpers
  • lapis.Inputs for Observable-style input controls, with supported controls rendered from shared @lapis-notes/ui primitives during the current migration
  • lapis.duckdb for query execution and vault file registration
  • lapis.vault for browser-safe note and asset reads through readText and readBinary
  • lapis.FileAttachment for Observable-style file attachment access backed by vault files, currently including arrayBuffer, text, json, and blob
  • lapis.d3 and lapis.Plot for bundled visualization helpers, also exposed as documented bare d3 and Plot globals
  • lapis.DOM for Observable-style DOM helpers such as canvas, context2d, download, and uid, also exposed as the documented bare DOM global
  • lapis.Generators and lapis.Promises for Observable-style generator and promise helpers, also exposed as documented bare Generators and Promises globals

Documented bare convenience globals may also exist for notebook-native helpers, for example Inputs, html, svg, d3, Plot, DOM, Generators, and Promises, but lapis.* remains the canonical Lapis-owned namespace. User-installed dependencies are different: they are available only through the selected global name, or a validated package-name-derived identifier when the user does not choose a name. They must not be attached to lapis, including under a nested namespace such as lapis.user.

The dependency-management UI should validate global names as JavaScript identifiers, pin package versions, block collisions with lapis, documented bare builtins, and other dependency globals, and surface load errors before a dependency is used by notebook execution. Shared descriptor, validation, and referenced-runtime-loader helpers now live in @lapis-notes/notebook-stdlib, and the product plugin now exposes a dedicated Packages side view from settings and notebook chrome to manage validated runtime dependency descriptors. That Packages surface probes pinned package metadata before saving, shows inline progress while installing, and uses npm-registry search suggestions to accelerate package-name entry. JavaScript-like cells lazily resolve referenced remote ESM dependencies during execution, including worker-backed execution when descriptors are serializable, and expose them only as their selected or derived bare globals. Future desktop hosts may add cached or offline resolvers behind the same registry contract.

The runtime should keep builtins and user dependencies startup-thin so unused builtins and dependency resolvers do not affect workspace boot or simple notebook execution. Host-neutral helpers such as vault-backed file access and bundled visualization bootstrap may still live in the shared stdlib package even when concrete host wiring remains lazy in the product runtime.

Data Model Direction

The notebook plugin should treat the existing app database as a source of read-only generated state rather than as the notebook runtime itself.

The current AppDatabase contract is optimized for metadata and search persistence, not arbitrary notebook execution. The proposed notebook runtime therefore uses its own execution model and exposes app-derived data to cells through notebook tables or notebook helpers.

Examples of proposed built-in notebook data sources:

  • notes metadata
  • resolved and unresolved links
  • tags
  • frontmatter properties
  • search documents and snippets

Current rollout boundaries

The first notebook rollout is intentionally narrower than the longer-term design:

  • notebook reading mode and live preview beneath rendered cell blocks are the notebook-owned presentation surfaces today
  • notebook authoring happens in notebook-owned markdown source with notebook commands, not in a separate non-markdown editor
  • execution is explicit-only: notebooks do not auto-run on load, and fresh execution happens only through note-level, current-cell, stale-cell, or inline per-cell run actions
  • notebook execution uses a dedicated worker transport for non-DuckDB/non-DOM notebooks, while DuckDB-backed and live DOM notebooks currently fall back to the in-process runtime path so browser and PWA data access stays reliable
  • notebooks that use DuckDB or live DOM features such as Observable inputs and htl execute in process; non-DuckDB, non-DOM notebooks keep the worker path
  • notebook-owned status-bar chrome makes that transport choice visible to users and distinguishes restored generated outputs from outputs recomputed after an explicit run so reload behavior is understandable without inspecting generated state
  • notebook outputs persist through typed app-database notebook state keyed by note path with file-level mtime and per-cell source fingerprints, with live DOM outputs omitted and serializable input values stored separately as generated state; tracked view(...) inputs can now persist structured input outputs alongside scalar values, reconstructable controls can rebuild directly from that stored metadata when no live input node is available, and only non-reconstructable inputs still fall back to rerunning the owning cell

First-Rollout Non-Goals

The initial notebook plugin should not try to solve every notebook problem at once.

Planned non-goals for the first rollout:

  • full Jupyter kernel compatibility
  • a primary .ipynb file format
  • unrestricted community-plugin notebook kernels
  • hidden mutable kernel state that bypasses the notebook graph
  • server-required execution for the default browser and PWA path
  • full Observable JavaScript compatibility beyond constrained top-level const|let|var name = view(expression)
  • persistence of live DOM nodes or event handlers

The first rollout should still include first-class rich outputs, but with an intentionally narrow scope:

  • table outputs are in scope
  • chart outputs are in scope when they use approved chart-spec renderers
  • trusted DOM outputs from notebook-authored htl and Observable inputs are in scope, while untrusted arbitrary community DOM capability gating remains a later host/runtime concern