Skip to content

feat: table block type #184

@oobagi

Description

@oobagi

Context

The block editor currently supports 10 block types: Paragraph, Heading1-3, BulletList, NumberedList, Checklist, CodeBlock, Quote, and Divider. Tables are a common markdown element with no support today — they can't be created, parsed, rendered, or edited.

Goal

Add a Table block type that supports creating, editing, rendering, and round-tripping markdown tables through the existing block pipeline.

Markdown format

Standard GFM pipe tables:

| Name  | Role    |
| ----- | ------- |
| Alice | Engineer|

Architecture fit

Each block type touches these layers — table would follow the same pattern:

  1. internal/block/block.go — Add Table constant to BlockType enum, String(), Short() (e.g. tb)
  2. internal/block/parse.go — Detect |-prefixed lines, group consecutive table rows into one Table block. Store raw pipe-delimited content in Block.Content
  3. internal/block/serialize.go — Emit pipe table markdown from Block.Content, auto-pad column widths
  4. internal/editor/palette.go — Add Table entry to the / command palette
  5. internal/editor/render.go — Render table with box-drawing borders (inactive + active + view mode). Active mode needs per-cell cursor navigation
  6. internal/theme/theme.go — Add TableStyle struct (border color, header style, alignment)

Design considerations

  • Data model: Store table as a flat string in Block.Content using pipe syntax (| col1 | col2 |\n| --- | --- |\n| val1 | val2 |). The parser and renderer interpret it; no new struct fields needed beyond Content
  • Editing: The hardest part. Options in order of complexity:
    1. Phase 1 — raw text editing: Edit the pipe-delimited text as-is in a single textarea (like CodeBlock). Simple, ships fast, already works for power users
    2. Phase 2 — grid navigation: Tab/Shift+Tab to move between cells, auto-pad columns on blur. Requires custom cursor logic within the textarea
    3. Phase 3 — structural ops: Add/remove rows/columns via keyboard shortcuts (e.g. Ctrl+Shift+R for row, Ctrl+Shift+C for column)
  • Rendering: Use box-drawing characters (similar to CodeBlock's bordered box) for inactive/view mode. Header row rendered bold
  • Column alignment: Parse the ---/:---/:---:/---: separator to determine left/center/right alignment
  • Word wrap: Tables should not wrap — horizontal scroll (like CodeBlock in no-wrap mode) is more appropriate

Tasks

  • Add Table to BlockType enum with String() and Short() methods
  • Parse pipe tables in parse.go — detect |-prefixed lines, group into single block
  • Serialize tables in serialize.go with auto-padded columns
  • Round-trip tests: Parse(Serialize(Parse(table_md))) == Parse(table_md)
  • Add Table entry to / command palette with default 2x2 template
  • Render inactive table with box-drawing grid and bold header
  • Render active table (Phase 1: raw textarea editing)
  • Render view mode table
  • Add TableStyle to theme system
  • Update blockPrefixWidth() and isMultiLine() for Table type
  • Handle isBlockStart() detection for table lines
  • Integration test: create table via palette, edit, save, reload

Out of scope (future)

  • Cell-level cursor navigation (Phase 2)
  • Structural add/remove row/column shortcuts (Phase 3)
  • Column resizing
  • Merged cells

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