Skip to content

Render block embeds with full content #17

@avelino

Description

@avelino

Summary

Render {{embed: ((block-uid))}} blocks with their full content (including children) instead of the current placeholder text. Block embeds are one of Roam's key features for reusing content across pages.

Roam web behavior: Block embeds render the referenced block AND all its children inline, in an indented container. The embedded content is editable in-place — edits propagate to the original block.

TUI adaptation: Render embedded blocks read-only with full content (block text + children tree). Editing the embed should navigate to the original block instead of editing in-place (TUI complexity constraint). Visual indicator (border or color) distinguishes embedded content from native blocks.

Implementation hints

Current state

  • src/markdown.rs:52-62parse_embed_inner() extracts the block UID from {{embed: ((uid))}}
  • Currently renders as: ▸ embed: block text (magenta italic) using the block_ref_cache
  • Only shows the top-level block text, no children

Data fetching

  • src/app.rs:84block_ref_cache: HashMap<String, String> stores uid → text
  • Need a richer cache: uid → Block (with children) for embeds
  • Add embed_cache: HashMap<String, Block> to AppState
  • Fetch via pull() with a recursive selector: [:block/string :block/uid :block/order :block/open {:block/children ...}]
  • Spawn async fetch same pattern as spawn_resolve_block_refs

Rendering

  • src/ui/main_area.rs — add VisibleLine variants for embed content:
    • EmbedStart { depth, block_index } — visual indicator (e.g., ┌─ embed ─)
    • EmbedBlock { depth, text, block_index } — each block in the embed
    • EmbedEnd { depth, block_index } — visual indicator (e.g., └─────────)
  • Render children recursively with increased depth
  • Different background or border color to visually separate embed from surrounding content

Embed rendering in markdown.rs

  • Instead of rendering inline, render_spans_with_refs() should return a signal that this block is an embed
  • The flatten_blocks() function in main_area.rs handles the expansion

Interaction

  • Embeds are read-only in the TUI
  • Pressing Enter / i on an embedded block could navigate to the original block's page (if page navigation exists, issue Page navigation: follow [[links]] to open pages #1)
  • Or show a status message: "Navigate to original block? (Enter to confirm)"

Edge cases

  • Circular embeds (A embeds B, B embeds A) — detect and break with max depth
  • {{embed: [[Page Name]]}} — page embed, not just block embed. Render page's top-level blocks.
  • Missing/deleted blocks — show "Block not found" placeholder

Acceptance criteria

  • {{embed: ((uid))}} renders full block content with children
  • Visual border/indicator distinguishes embedded content
  • Children are indented properly within the embed
  • Circular embed detection prevents infinite recursion
  • Missing blocks show placeholder instead of crashing
  • Page embeds ({{embed: [[Page]]}}) also work

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: apiRoam Research API integrationarea: renderingUI rendering, markdown, syntax highlightingpriority: lowNice to have, low urgencyroam parityFeature that exists in Roam web, ported to TUI

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions