Skip to content

fix: keep a header-only table without a filled body row - #350

Merged
Azganoth merged 1 commit into
mainfrom
bug/header-only-table-body-row
Sep 1, 2026
Merged

fix: keep a header-only table without a filled body row#350
Azganoth merged 1 commit into
mainfrom
bug/header-only-table-body-row

Conversation

@Azganoth

@Azganoth Azganoth commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Override the GFM preset's table content expression from table_header_row table_row+ to table_header_row table_row*. The + left a table authored without body rows with no legal representation, so ProseMirror filled a cell-less row into every one of them.
  • Record in docs/specification.md that a header-only table is kept and rendered as one, and make the Delete row rule say what it does now that a header-only table is a valid shape.

Related Issue

Closes #323

Verification

src/features/editor/plugins/tableShape.test.ts asserts the document rather than the bytes: a header-only table holds no body rows on open, preserves its document across a save, and writes back without a row. It also pins that a body row shorter than the header is still filled, which is #254's behavior through the same code.

src/features/editor/commands/formatting/tables.test.ts covers the command paths the relaxed schema reaches: Add row below from the header of a header-only table inserts the first body row, and deleting the only body row still removes the table.

  • pnpm check:frontend passes.

Manually verified in the editor mount used by the plugin tests, on Windows:

  1. Opening | Header only | No body rows | with its delimiter row renders one <tr>; before the change it rendered a second, empty <tr> holding no cells.
  2. The saved file is byte-identical across the change, which is why this was never visible in the file.

Not verified: no assembled-desktop run, and no manual pass in the built application.

Notes

The measurement in #323 said the save appended the row. It does not, and the issue has been corrected. The row was only ever in the document and the rendered table: the serializer writes the same two lines whether or not the empty row is present, and the document round-trips because the fill is deterministic, so neither the corpus guard nor a byte comparison could see it. The defect is document fidelity, not file content.

Deleting the last body row still removes the table rather than leaving its header. That is now a choice rather than a consequence of the schema, and changing it is a command decision this fix deliberately does not take.

The padding in the expected file output is the normalization Preserve the form a file was written in accepts, recorded in #348. The omitted outer pipes from the original #323 are #349.

The GFM preset declares the table node as `table_header_row table_row+`,
so a table authored without body rows has no legal representation and
ProseMirror fills a cell-less row into it. That row reaches the rendered
table as an empty `<tr>` but never reaches the file: the serializer
writes the same two lines either way, and the document round-trips
because the fill is deterministic, which is why neither the corpus guard
nor a byte comparison can see it.

Deleting the last body row still removes the table. A header-only table
is now a shape a file can open, but producing one from a command is a
separate decision.
@Azganoth Azganoth added the Bug Something isn't working label Sep 1, 2026
@Azganoth Azganoth self-assigned this Sep 1, 2026
@Azganoth
Azganoth merged commit 97d5628 into main Sep 1, 2026
3 checks passed
@Azganoth
Azganoth deleted the bug/header-only-table-body-row branch September 1, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A header-only table gains an empty body row

1 participant