T-U81R-table-cell-editing-and-hover-controls
State: planning/proposed · Impact: medium · Complexity: large
TableWidget renders a table as static HTML in live mode; clicking a cell only moves the cursor
to the matching source position (handleCellClick), so editing a table means dropping to raw
markdown. This task makes cells editable in place, adds hover controls for inserting rows and
columns, and keeps the source padded so a table stays readable as plain text between edits.
| Location | Role today |
|---|---|
packages/ts/obsidian-editor/src/plugins/tables.ts#TableWidget | Renders a table to static HTML; handleCellClick moves the cursor to source, it does not edit in place |
packages/ts/obsidian-editor/src/plugins/tables.ts#tableModel | Reads the Lezer Table node into rows, TableCellSpan cells and column alignment |
packages/ts/obsidian-editor/src/plugins/tables.ts | table.row.insert-above/insert-below/delete, table.col.insert-left/insert-right/delete, table.align.* splice one line each; no source re-padding |
packages/ts/obsidian-editor/src/options.ts#MutableOptions | Runtime-mutable option bag (today only mermaidTheme); no tablePadding |
packages/ts/markdown-ui/app/components/MdTableMenu.vue | Row/column/alignment button strip anchored to the table under the cursor; dispatches the same table.* ids |
packages/ts/markdown-ui/app/utils/mdTableCommands.ts | MD_TABLE_MENU_GROUPS roster and mdTableLiveGroups pruning against the surface’s live command set |
packages/ts/obsidian-editor/README.md | States the swap-writer rule: every edit splices exactly the bytes it covers |
packages/ts/obsidian-editor/test/plugins/tables.test.ts | Table geometry, catalogue commands, and cell-local byte identity per command |
Proposed
Section titled “Proposed”TableWidget cells are editable in place. Hovering a row or column edge shows an insert
affordance that calls the existing table.row.insert-*/table.col.insert-* commands.
MdTableMenu.vue keeps its alignment and delete buttons unchanged. Every table edit — through
the widget or a table.* command — re-pads the whole table: pipes align, each column’s width is
the longest cell up to a cap (tablePadding: { maxColumnWidth: 40 }), delimiter and alignment
colons are kept. A cell past the cap is left as is and does not widen its column. This is a
documented exception to the swap-writer rule: the swap span becomes the table block, never the
surrounding document, and padding is idempotent. tablePadding: false restores today’s minimal
splices; the toggle rides the editor settings section from
T-FG8R-editor-settings-toolbar-and-vim.
Approach
Section titled “Approach”- Add
tablePaddingtoObsidianEditorOptions/MutableOptions({ maxColumnWidth: number } | false, default{ maxColumnWidth: 40 }), wired through the compartment the same way asmermaidTheme. - Write a
padTable(model, opts)helper intables.ts: computes each column’s rendered width capped atmaxColumnWidth, rewrites the header, delimiter and body rows with aligned pipes and kept alignment colons, and returns oneChangeSpeccovering exactly the table’s line range. - Route every
table.*command’s dispatch throughpadTablebefore commit whentablePaddingis enabled; add a swap-writer test asserting nothing outside the table’s span changes and that padding the same table twice is a no-op. - Make
TableWidgetcells editable: replacehandleCellClick’s cursor-only jump with eithercontenteditableper<td>/<th>or a nestedEditorViewper active cell — decide during implementation behind the existingTableWidgetseam, falling back to today’s click-to-source behavior if the byte-diff tests fail (per the plan’s stated risk). Commit keystrokes through the same cell-splice functionsinsertCellSplice/newCellTextalready use, then re-pad. - Add hover affordances on row and column edges that call
table.row.insert-above/-belowandtable.col.insert-left/-right. - Add
tablePaddingto theeditorsettings section from T-FG8R-editor-settings-toolbar-and-vim, wired the same way asvimMode/showToolbar. - Document the padding exception beside the swap-writer rule in
packages/ts/obsidian-editor/README.md. - Extend
test/plugins/tables.test.tsand the swap-writer matrix with padding cases: aligned pipes, a cell pastmaxColumnWidth,tablePadding: false, and idempotence.
| Area | Note |
|---|---|
packages/ts/obsidian-editor/src/plugins/tables.ts | Editable cells, hover row/column controls, padTable |
packages/ts/obsidian-editor/src/options.ts | tablePadding mutable option |
packages/ts/obsidian-editor/src/types.ts | tablePadding on ObsidianEditorOptions |
packages/ts/obsidian-editor/README.md | Document the padding exception to swap-writer |
packages/ts/obsidian-editor/test/plugins/tables.test.ts | Padding, cap, idempotence and tablePadding: false cases |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: Clicking into a table cell in live mode edits its text in place, without switching to
source mode, verified by a test in
test/plugins/tables.test.ts. - AC-2: Hovering a row or column edge shows an insert affordance; activating it inserts a
row or column via the existing
table.*commands. - AC-3: After any table edit, the swap-writer assertions pass: bytes outside the table’s span are unchanged, and padding the same table a second time changes nothing.
- AC-4: A cell whose content exceeds
tablePadding.maxColumnWidthis left unpadded and does not widen its column, verified by a fixture in the same test file. - AC-5: Setting
tablePadding: falserestores the minimal single-line splices from today, verified by a byte-diff test.
Out of scope
Section titled “Out of scope”- Merged cells or nested tables; GFM tables support neither.
- A full spreadsheet-style multi-cell select or paste.
MdTableMenu.vue’s own alignment and delete buttons; they are unchanged.
Dependencies
Section titled “Dependencies”- T-FG8R-editor-settings-toolbar-and-vim supplies the
editorsettings section thetablePaddingtoggle is added to.
Discovery context
Section titled “Discovery context”From the user’s Agent Manager planning request (2026-09-07): the markdown editor’s tables, vim,
styling and mermaid handling came up together as one item. In-cell editing and the re-padding
exception are worked out in M-BMFD’s Stage 6 table row and its risk notes on nested
EditorView cells and on re-padding widening a hand-formatted table.
Depends on
Section titled “Depends on”T-FG8R-editor-settings-toolbar-and-vim