Skip to content

T-DSVW-md-engine-html-details

State: closed/done · Impact: high · Complexity: medium

HTMLBlock nodes (including <details>) parse today but render as plain text in live mode, so a toggle block is dead markup while editing. This task adds the html-blocks plugin: a DetailsToggleWidget that folds the toggle body ephemerally, a sanitized rendered widget for other HTML blocks, and the block.details command.

LocationRole today
packages/ts/obsidian-editor/src/decorations.tsThe enter switch has no HTMLBlock case; raw HTML shows as plain text in live mode
packages/ts/obsidian-md/src/sanitize.tsThe sanitize schema read mode applies; the rendered widget reuses it via renderToHtmlSync
packages/ts/obsidian-editor/src/commands.tsNo block.details
packages/ts/obsidian-editor/test/helpers.ts#makeStateHeadless state builder for the plugin tests

The plugin roster comes from T-4TE2-md-engine-plugin-contract; the catalogue from T-EXQ7-md-engine-command-catalogue; the details.md corpus fixture from T-0GV4-md-engine-line-endings-corpus.

packages/ts/obsidian-editor/src/plugins/html-blocks.ts, a defineEditorPlugin with extensions: codeFolding():

  • A <details> opener block (with its optional <summary> line) paired with its </details> closer → DetailsToggleWidget over the opener: a disclosure triangle plus the summary text. The closer line is hidden; the body lines between them fold and unfold through codeFolding() fold effects driven by the widget’s click handler. Folding is ephemeral view state — the open attribute is never written into the document.
  • Any other HTMLBlock → block widget of renderToHtmlSync markup, sanitized by the same schema read mode uses, so nothing renders in live mode that read mode would strip.
  • Any line of the block intersecting the selection → widgets and hides suppressed, raw HTML visible, as with every construct.
  • Catalogue addition block.details (group block): wraps the selected lines in a <details><summary>…</summary> … </details> skeleton via changeByRange; nothing outside the selection is rewritten.

The theme module gains rules for the toggle widget and the rendered HTML container.

  1. Write packages/ts/obsidian-editor/test/plugins/html-blocks.plugin.test.ts first, over fixtures with a summary toggle, a bare toggle, a nested toggle and a <script> block: widget over the opener; closer hidden; click toggles the fold effect over exactly the body lines; ten toggles leave state.sliceDoc() byte-identical; the <script> block’s widget DOM contains no script element.
  2. Add the block.details case: applied to a two-line selection in a five-line fixture, the other three lines are byte-identical and the cursor lands inside the summary.
  3. Implement the plugin: pairing walk over sibling HTMLBlock nodes, DetailsToggleWidget, fold dispatch, sanitized rendered widget.
  4. Register html-blocks in BUILTIN_PLUGINS; implement block.details in the catalogue; add the theme rules.
  5. bun test, tsc --noEmit, moon run obsidian-editor:bench; confirm the swap-writer corpus passes over the details fixture.
LocationKindChange
packages/ts/obsidian-editor/src/plugins/html-blocks.tsnewhtml-blocks plugin: DetailsToggleWidget, fold wiring, sanitized HTML widget
packages/ts/obsidian-editor/src/plugins/index.tsnewCreated by T-4TE2-md-engine-plugin-contract; html-blocks joins BUILTIN_PLUGINS
packages/ts/obsidian-editor/src/commands/catalogue.tsnewCreated by T-EXQ7-md-engine-command-catalogue; gains block.details
packages/ts/obsidian-editor/src/theme.tsmodifyToggle widget and rendered-HTML container rules
packages/ts/obsidian-editor/test/plugins/html-blocks.plugin.test.tsnewToggle, fold, sanitize and command tests
  • AC-1: bun test packages/ts/obsidian-editor passes, including the new suite.
  • AC-2: clicking the toggle folds the body lines (a fold range spanning exactly the lines between opener and closer exists) and clicking again unfolds; after ten toggles state.sliceDoc() is byte-identical — no open attribute ever appears in the document.
  • AC-3: the widget DOM for a fixture HTMLBlock containing a script tag has no script element and no inline event-handler attribute.
  • AC-4: block.details applied to a two-line selection in the five-line fixture leaves the other three lines byte-identical.
  • AC-5: bun test packages/ts/obsidian-editor/test/swap-writer.test.ts passes with block.details in the matrix over packages/ts/obsidian-editor/test/fixtures/corpus/details.md.
  • AC-6: moon run obsidian-editor:bench stays within 1.5× of the recorded baseline.
  • Persisting toggle state per note; folding is view-local by design.
  • Read-mode toggle behavior (obsidian-md already renders <details> natively).
  • Inline HTML spans; this task covers HTMLBlock nodes only.

Member of milestone M-931L; engine step E9 of decision D-K3EM-markdown-editor-chrome-and-plugin-environment.

T-RCWI-md-obsidian-md-sync-helpers


← Back to Tasks