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:
internal/block/block.go — Add Table constant to BlockType enum, String(), Short() (e.g. tb)
internal/block/parse.go — Detect |-prefixed lines, group consecutive table rows into one Table block. Store raw pipe-delimited content in Block.Content
internal/block/serialize.go — Emit pipe table markdown from Block.Content, auto-pad column widths
internal/editor/palette.go — Add Table entry to the / command palette
internal/editor/render.go — Render table with box-drawing borders (inactive + active + view mode). Active mode needs per-cell cursor navigation
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:
- 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
- Phase 2 — grid navigation: Tab/Shift+Tab to move between cells, auto-pad columns on blur. Requires custom cursor logic within the textarea
- 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
Out of scope (future)
- Cell-level cursor navigation (Phase 2)
- Structural add/remove row/column shortcuts (Phase 3)
- Column resizing
- Merged cells
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
Tableblock type that supports creating, editing, rendering, and round-tripping markdown tables through the existing block pipeline.Markdown format
Standard GFM pipe tables:
Architecture fit
Each block type touches these layers — table would follow the same pattern:
internal/block/block.go— AddTableconstant toBlockTypeenum,String(),Short()(e.g.tb)internal/block/parse.go— Detect|-prefixed lines, group consecutive table rows into oneTableblock. Store raw pipe-delimited content inBlock.Contentinternal/block/serialize.go— Emit pipe table markdown fromBlock.Content, auto-pad column widthsinternal/editor/palette.go— Add Table entry to the/command paletteinternal/editor/render.go— Render table with box-drawing borders (inactive + active + view mode). Active mode needs per-cell cursor navigationinternal/theme/theme.go— AddTableStylestruct (border color, header style, alignment)Design considerations
Block.Contentusing pipe syntax (| col1 | col2 |\n| --- | --- |\n| val1 | val2 |). The parser and renderer interpret it; no new struct fields needed beyondContent---/:---/:---:/---:separator to determine left/center/right alignmentTasks
TabletoBlockTypeenum withString()andShort()methodsparse.go— detect|-prefixed lines, group into single blockserialize.gowith auto-padded columnsParse(Serialize(Parse(table_md))) == Parse(table_md)/command palette with default 2x2 templateTableStyleto theme systemblockPrefixWidth()andisMultiLine()for Table typeisBlockStart()detection for table linesOut of scope (future)