Skip to content

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.

LocationRole today
packages/ts/obsidian-editor/src/plugins/tables.ts#TableWidgetRenders 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#tableModelReads the Lezer Table node into rows, TableCellSpan cells and column alignment
packages/ts/obsidian-editor/src/plugins/tables.tstable.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#MutableOptionsRuntime-mutable option bag (today only mermaidTheme); no tablePadding
packages/ts/markdown-ui/app/components/MdTableMenu.vueRow/column/alignment button strip anchored to the table under the cursor; dispatches the same table.* ids
packages/ts/markdown-ui/app/utils/mdTableCommands.tsMD_TABLE_MENU_GROUPS roster and mdTableLiveGroups pruning against the surface’s live command set
packages/ts/obsidian-editor/README.mdStates the swap-writer rule: every edit splices exactly the bytes it covers
packages/ts/obsidian-editor/test/plugins/tables.test.tsTable geometry, catalogue commands, and cell-local byte identity per command

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.

  1. Add tablePadding to ObsidianEditorOptions/MutableOptions ({ maxColumnWidth: number } | false, default { maxColumnWidth: 40 }), wired through the compartment the same way as mermaidTheme.
  2. Write a padTable(model, opts) helper in tables.ts: computes each column’s rendered width capped at maxColumnWidth, rewrites the header, delimiter and body rows with aligned pipes and kept alignment colons, and returns one ChangeSpec covering exactly the table’s line range.
  3. Route every table.* command’s dispatch through padTable before commit when tablePadding is 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.
  4. Make TableWidget cells editable: replace handleCellClick’s cursor-only jump with either contenteditable per <td>/<th> or a nested EditorView per active cell — decide during implementation behind the existing TableWidget seam, 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 functions insertCellSplice/newCellText already use, then re-pad.
  5. Add hover affordances on row and column edges that call table.row.insert-above/-below and table.col.insert-left/-right.
  6. Add tablePadding to the editor settings section from T-FG8R-editor-settings-toolbar-and-vim, wired the same way as vimMode/showToolbar.
  7. Document the padding exception beside the swap-writer rule in packages/ts/obsidian-editor/README.md.
  8. Extend test/plugins/tables.test.ts and the swap-writer matrix with padding cases: aligned pipes, a cell past maxColumnWidth, tablePadding: false, and idempotence.
AreaNote
packages/ts/obsidian-editor/src/plugins/tables.tsEditable cells, hover row/column controls, padTable
packages/ts/obsidian-editor/src/options.tstablePadding mutable option
packages/ts/obsidian-editor/src/types.tstablePadding on ObsidianEditorOptions
packages/ts/obsidian-editor/README.mdDocument the padding exception to swap-writer
packages/ts/obsidian-editor/test/plugins/tables.test.tsPadding, cap, idempotence and tablePadding: false cases
  • 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.maxColumnWidth is left unpadded and does not widen its column, verified by a fixture in the same test file.
  • AC-5: Setting tablePadding: false restores the minimal single-line splices from today, verified by a byte-diff test.
  • 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.

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.

T-FG8R-editor-settings-toolbar-and-vim


← Back to Tasks