Skip to content

feat(composer): edit Markdown tables as a grid - #3575

Draft
ericm-square wants to merge 2 commits into
block:mainfrom
ericm-square:ericm/composer-table-editing
Draft

ericm-square wants to merge 2 commits into
block:mainfrom
ericm-square:ericm/composer-table-editing

Conversation

@ericm-square

@ericm-square ericm-square commented Nov 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Tables in the message composer become an editable grid instead of raw pipe text. Today a Markdown table stays as | a | b | text while you write, which is hard to edit once it's more than a few cells.

A GFM table becomes a grid when you type it, paste it as Markdown, paste cells from a spreadsheet, or a plugin inserts it with insertText. The grid has a header row and body rows, and every cell is inline-editable with the composer's usual marks and chips. Sending writes the same GFM Markdown back, with | escaped and column alignment kept, so messages render exactly as they do now. Restored drafts keep the table.

Linear: https://linear.app/squareup/issue/AXON-829/buzz-composer-edit-markdown-tables-as-a-grid

Draft to track iterations. It isn't ready for review yet.

image image

Changes

  • composer-table.ts (new): table parsing, serializing and commands (add and delete rows and columns, delete table, cell navigation).
  • composer-document.ts, composer-markdown.ts, composer-blocks.ts: a table node in the schema, Markdown parse and serialize, and block conversion.
  • composer-clipboard.ts, composer-dom.ts, selection-copy.ts: spreadsheet (TSV) and Markdown table paste, and copying a timeline table into the composer. Raw paste (Mod+Shift+V) still pastes plain text.
  • EditableInput.tsx, EditableInput.module.css: the grid, keyboard handling, the active cell tint and in-table scrolling with a sticky header.
  • ComposerTableTools.tsx (new), MessageComposer.tsx: the table toolbar.
  • ComposerFormattingTools.tsx: a Table control (Mod+Alt+T) that starts a 3 by 2 grid.
  • icons/index.ts: 7 Tabler icons for the toolbar.
  • docs/buzz-help.md: a short note on tables.

Keyboard

  • Tab and Shift+Tab move between cells. Tab in the last cell adds a row.
  • Arrow keys at a cell edge move to the next cell. Up on the first row or Down on the last row leaves the table.
  • Enter still sends (or adds a line with the Mod+Enter-to-send setting). Shift+Enter moves down a row and adds one on the last row. This follows the list and code block precedent, where Enter is never captured.
  • Backspace in an empty table removes it.
  • Alt+F10 moves focus to the toolbar, arrows move along it and Escape returns to the editor.

Open questions

  1. Should typed pipe text convert to a grid on its own, or only on paste and insertText?
  2. Should the toolbar float above the table instead of sitting below the editor?
  3. Is Mod+Alt+T free on every platform?
  4. A long table scrolls inside its own area within the composer's scroll area. That keeps the header pinned, at the cost of two nested scrollers.
  5. A literal backslash right before a pipe (a\|b) can't round-trip exactly.

Test plan

  • pnpm check and pnpm lint pass.
  • vitest run src: 21,494 passed.
  • New tests:
    • composer-table.test.ts (43): parse, serialize, round trip, escaping, alignment, ragged rows, spreadsheet paste and every command.
    • EditableInput.test.tsx "editable tables" (15): the keyboard flows, paste, and insertText into a cell.
    • MessageComposer.test.tsx "table editing controls" (2): the toolbar.
    • tests/browser/composer-tables.spec.mjs: native typing, Tab and arrows, scrolling and the toolbar. With the existing composer-* and mention* specs, 150 passed and 2 skipped on Chromium and WebKit.
  • Changed on purpose: one assertion in EditableInput.test.tsx and one in tests/browser/composer-paste.spec.mjs, because pasting spreadsheet cells now gives a grid instead of tab-separated text.

Manual test steps

  1. Run just desktop from this branch and sign in.
  2. In a channel, open the formatting tools and choose Table. A 3 by 2 grid appears with the caret in the first header cell, and the table toolbar shows below the editor.
  3. Type a header, press Tab to walk through the cells, and press Tab in the last cell. A new row is added.
  4. Press Down on the last row. The caret leaves the table, and the toolbar goes away.
  5. Paste a Markdown table, then paste cells copied from a spreadsheet. Both become grids.
  6. Paste an 8 column by 15 row table. It scrolls sideways and down inside the composer, and the header row stays pinned.
  7. With the caret in a cell, add and delete rows and columns from the toolbar, then press Alt+F10 and use the arrow keys and Enter to do the same from the keyboard.
  8. Press Enter. The message sends and renders as the same table it does today.
  9. Start a table, switch channels and come back. The draft still has the grid.
  10. Check both themes, a narrow window and a larger text size.

🤖 Generated with Claude Code

ericm-square and others added 2 commits November 14, 2026 15:12
Tables in the message composer are now an editable grid instead of raw pipe
text. A GFM table that is typed, pasted as Markdown, pasted as spreadsheet
cells, or inserted with insertText becomes a header row plus body rows with
inline-editable cells. Column alignment is kept, and sending writes the same
GFM Markdown back, with pipes escaped.

Tab and Shift+Tab move between cells and Tab in the last cell adds a row.
Arrow keys move across cell edges and leave the table above or below.
Backspace in an empty table removes it. Enter keeps sending, and Shift+Enter
moves down a row, as it adds a line elsewhere.

While the caret is in a table, a toolbar adds rows and columns above, below,
left and right, and deletes the row, column or table. Alt+F10 moves focus to
it. A Table control in the formatting tools starts a 3 by 2 grid. Wide and
long tables scroll inside the composer.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Eric Morgan <ericm@squareup.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Eric Morgan <ericm@squareup.com>

This branch has not been deployed

No deployments
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