Skip to content

feat: image block with terminal graphics rendering #198

@oobagi

Description

@oobagi

Problem

There's no way to include or view images in notes. Markdown image syntax (![alt](path)) is either ignored or treated as plain text. Terminal graphics protocols (Kitty, iTerm2, Sixel) make inline image rendering possible in modern terminals.

Context

The block model (internal/block/block.go) has 10 block types — no Image type. The parser (internal/block/parse.go) doesn't handle ![alt](url) syntax. There is zero terminal protocol detection or image rendering in the codebase. Dependencies are Bubble Tea v2 + lipgloss — no image libraries.

mdfried (Rust) uses ratatui-image for protocol auto-detection and rendering with a half-block Unicode fallback. In Go, github.com/BourgeoisBear/rasterm supports Kitty, iTerm2, and Sixel protocols. For half-block fallback, github.com/makeworld-the-better-one/dither or manual implementation with ▀▄ characters.

Key files:

  • internal/block/block.go — Block struct, BlockType enum
  • internal/block/parse.go — Markdown → Blocks parser
  • internal/block/serialize.go — Blocks → Markdown serializer
  • internal/editor/render.go — All rendering paths
  • internal/editor/editor.go — Editor model, block operations

Approach

Phase 1: Data model + parsing

  • Add Image BlockType to the enum
  • Block's Content stores the path/URL, could add an Alt field or encode as alt|path
  • Parser detects ![alt](path) lines → creates Image blocks
  • Serializer writes them back as standard markdown image syntax

Phase 2: Terminal protocol detection

  • At startup, detect supported graphics protocol by querying the terminal
  • Priority: Kitty → iTerm2 → Sixel → half-block fallback
  • Check $TERM, $TERM_PROGRAM, and/or send escape sequence queries
  • Store detected protocol in the editor model for rendering

Phase 3: Image rendering

  • In view mode: load image from disk (local paths relative to note file), decode (PNG/JPEG/GIF), resize to fit within viewMaxWidth equivalent pixel width, render via detected protocol
  • In edit mode: show placeholder block (e.g., [Image: filename.png]) — rendering inline images in an editable textarea is impractical
  • Cache decoded images to avoid re-loading on every render cycle
  • Handle missing files gracefully (show alt text or error indicator)

Dependencies to evaluate

  • github.com/BourgeoisBear/rasterm — Kitty/iTerm2/Sixel rendering
  • golang.org/x/image or standard image package — decoding
  • github.com/nfnt/resize or golang.org/x/image/draw — resizing

Tasks

  • Add Image to BlockType enum in internal/block/block.go
  • Update parser in internal/block/parse.go to detect ![alt](path) syntax
  • Update serializer in internal/block/serialize.go to write image markdown
  • Add terminal graphics protocol detection (new file, e.g., internal/terminal/graphics.go)
  • Implement image loading + decoding + resizing pipeline
  • Implement Kitty graphics protocol rendering
  • Implement iTerm2 inline image rendering
  • Implement Sixel rendering
  • Implement half-block Unicode fallback
  • Add image rendering to renderViewBlock() in internal/editor/render.go
  • Add placeholder rendering for edit mode
  • Add image caching to avoid repeated disk/decode operations
  • Handle edge cases: missing files, unsupported formats, large images, relative paths
  • Add Image block to command palette in internal/editor/palette.go

Acceptance criteria

  • ![alt](path) in markdown is parsed into Image blocks and round-trips correctly
  • Images render inline in view mode using the best available terminal protocol
  • Fallback rendering works in terminals without graphics protocol support
  • Edit mode shows a sensible placeholder for image blocks
  • Local file paths resolve relative to the note file's directory
  • Missing/broken images show alt text instead of crashing

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions