Skip to content

feat: parse and render markdown frontmatter metadata#13

Merged
rrbe merged 4 commits into
masterfrom
feat/metadata-block
May 29, 2026
Merged

feat: parse and render markdown frontmatter metadata#13
rrbe merged 4 commits into
masterfrom
feat/metadata-block

Conversation

@rrbe
Copy link
Copy Markdown
Owner

@rrbe rrbe commented May 28, 2026

Summary

  • YAML (---) and TOML (+++) frontmatter blocks at the top of a doc are now parsed via pulldown-cmark's metadata extensions and never leak into body content (the long-standing setext-H2-rasterized-as-PNG bug at the top of any file with frontmatter is gone).
  • --cat renders a single dim summary line: · metadata · title=…, author=…, … (truncated to terminal width).
  • TUI shows the same folded line by default and adds the m key to expand it into an inline key/value box. Default state: folded.
  • Single config knob in ~/.termdown/config.toml:
    [metadata]
    show = true   # false hides metadata in both cat and TUI
    Parsing always runs (so frontmatter can't leak); show only gates display.

Design

Full rationale, alternatives considered, and rejected designs in docs/adr/0001-metadata-block-handling.md. Key choices:

  • Heuristic line-based parser, not a real YAML/TOML library — termdown's output is a 1-line summary, not a validator.
  • Default folded, not glow-style "completely hidden" — readers of agent-skill / Hugo / Jekyll files care about title/author/date enough to deserve a discoverable summary, but not enough to deserve permanent screen real estate.
  • m keybinding is new; added to the ? help screen.

A glossary lives in the new CONTEXT.md for future maintainers.

Test plan

  • make check clean (fmt, clippy -D warnings, full test suite).
  • New focused fixtures under fixtures/specialized/:
    • metadata-yaml.md — happy path for YAML.
    • metadata-toml.md — happy path for TOML.
    • metadata-malformed.md — nested + multi-line YAML, exercises the heuristic's edge behavior.
    • metadata-none.md — regression guard: mid-doc --- must stay a horizontal rule.
  • Existing fixtures/expected/supported-syntax.ansi updated (the top-of-doc setext-H2 PNG noise → one dim summary line). Eyeballed the diff.
  • Unit tests for the heuristic in src/frontmatter.rs (empty block, multi-line continuation, value containing :, fallback path).
  • Cat smoke test on a synthetic 6-field YAML probe — output matches design.
  • Manual TUI verification: open a doc with frontmatter, press m to expand/collapse. TODO before merge.

Drive-by

tests/cli.rs::TempMarkdownFile used pid + nanos for uniqueness — with the four new snapshot tests pushing parallelism higher, two test threads occasionally landed on the same nanosecond and shared a filename. Added a process-local atomic counter; flake is gone (5 consecutive clean cargo test runs verified).

🤖 Generated with Claude Code

rrbe and others added 4 commits May 28, 2026 23:13
YAML (---) and TOML (+++) frontmatter blocks at the top of a document
are now parsed via pulldown-cmark's metadata extensions and never leak
into body content.

cat mode renders a single dim line:
  · metadata · title=…, author=…, tags=[…], …

TUI mode shows the same line by default and adds a new `m` key to
expand it into an inline key/value box. Disable entirely via
`[metadata] show = false` in ~/.termdown/config.toml.

Field extraction uses a line-based heuristic (no real YAML/TOML
parser); see docs/adr/0001-metadata-block-handling.md for the
rationale and rejected alternatives.

Drive-by: fix flaky TempMarkdownFile uniqueness in tests/cli.rs by
adding a process-local atomic counter — clock granularity alone
collided once test parallelism grew.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Folded one-line summary becomes:
  [metadata · key=value, key=value, …]

Truncation now preserves the closing `]` after the ellipsis (`…]`),
so the chip never looks unterminated. Both --cat and TUI folded state
use the same string. After the metadata block (folded or expanded),
emit one blank row so the body element below isn't visually crammed
against the chip / inline box.

Drive-by: tests/snapshots.rs check_snapshot now sanitizes `/` out of
fixture names when composing the temp-file path, so specialized/
fixtures can write their actual output for diffing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `fixtures/expected/*.ansi` and `fixtures/*.md` patterns only matched
top-level files, so the new `fixtures/expected/specialized/*.ansi`
snapshots were checked out with CRLF on Windows and the snapshot tests
failed. Recurse with `**`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address review findings on PR #13:

- Use a sentinel logical_index (NO_LOGICAL) for metadata visual rows so
  search-jump and heading navigation no longer mis-resolve a real line 0
  to a metadata row; guard ToC highlight and reorder draw()'s is_spacer
  check so a metadata-only doc can't panic on doc.lines[0].
- Share the folded `[metadata · …]` summary + width-correct truncation
  via frontmatter::folded_summary so cat and TUI folded state can't drift.
- Compute the expanded-box layout in display columns (not char counts)
  so CJK/wide chars keep the border aligned.
- Make `m` a true no-op when `[metadata] show = false`.
- Skip empty quoted values (`key: ""`) so they don't render as `key=`.
- Correct the malformed fixture's prose: nested keys are lifted to
  top-level, not skipped; regenerate its golden snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rrbe rrbe merged commit ea052d3 into master May 29, 2026
5 checks passed
@rrbe rrbe deleted the feat/metadata-block branch May 29, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant