Skip to content

feat: preserve markdown tables and add raw ProseMirror ingest flags (#42)#48

Merged
teslakoile merged 5 commits into
masterfrom
feat/issue-42-markdown-tables
Apr 19, 2026
Merged

feat: preserve markdown tables and add raw ProseMirror ingest flags (#42)#48
teslakoile merged 5 commits into
masterfrom
feat/issue-42-markdown-tables

Conversation

@teslakoile

Copy link
Copy Markdown
Owner

Summary

Fixes #42. Markdown tables sent via huly documents describe --set /
--set-file now parse into real ProseMirror table / tableRow /
tableHeader / tableCell nodes instead of being stored as paragraphs
full of literal | pipes. A raw-ingest escape hatch is also added for
ProseMirror features the markdown parser does not cover.

Changes

Path B — raw ProseMirror ingest (shipped first)

  • New flags on documents describe:
    • --set-raw JSON_STRING — pass arbitrary ProseMirror JSON straight to
      client.set_content / client.create_content.
    • --set-raw-file PATH — same, but read from disk.
  • Mutual-exclusion guard now covers all four write flags (--set,
    --set-file, --set-raw, --set-raw-file).
  • Minimum structural validation: the root must be
    {"type": "doc", "content": [...]}. Invalid JSON or wrong shape is
    rejected with a clear error before any network IO.
  • Only src/huly_cli/commands/documents.py touched on the command side.
    client.py is untouched — set_content / create_content already
    accept arbitrary ProseMirror JSON.

Path A — GFM table parsing in markdown_to_prosemirror

  • markdown_to_prosemirror in src/huly_cli/markup.py recognises GFM
    pipe tables:
    | a | b |
    | --- | --- |
    | 1 | 2 |
    
    First row → tableHeader, body rows → tableCell, alignment row
    consumed but not stored (1x1 cells per the issue non-goal).
  • Cells are wrapped in a single paragraph so inline marks (bold,
    italic, links, code) survive.
  • Escaped pipes (\|) inside cells are preserved as literal pipes.
  • prosemirror_to_markdown already emits GFM tables via _table_to_md,
    so markdown → PM → markdown round-trips; a test asserts exact
    equality after .strip().
  • README.md and skills/huly-cli/SKILL.md updated.

Test plan

  • uv run pytest -x -q — 293 passing (up from 278: +15 new tests)
  • uv run ruff format . && uv run ruff check . clean
  • Parser-level tests in tests/test_markup.py:
    • basic table → table node
    • header/body cell type distinction
    • {"colspan": 1, "rowspan": 1} attrs
    • alignment row with :---/---: consumed
    • inline marks inside cells preserved
    • escaped-pipe (\|) cells
    • markdown → PM → markdown round-trip (exact equality after strip)
    • table followed by paragraph
    • non-table markdown unchanged (regression guard)
    • single | row with no separator stays a paragraph
    • | in plain prose stays a paragraph
    • uneven rows padded on round-trip
  • Command-layer tests in tests/test_issue_write_path.py:
    • --set-raw writes verbatim (markup payload handed to create_content
      is the exact JSON, parsed result contains a table node)
    • --set-raw-file reads and writes
    • --set-raw-file with missing file exits 1
    • --set-raw with invalid JSON exits 1, no network call
    • --set-raw with non-doc root exits 1
    • --set-raw with a doc root missing content exits 1
    • all six mutual-exclusion pairs parameterised and asserted
    • existing --set "" --set-file regression updated to the new
      four-flag error wording
    • integration: --set with a markdown table emits a PM table node
      with tableHeader header row and tableCell body row

Closes #42

teslakoile and others added 5 commits April 19, 2026 21:03
…ngest (#42)

Path B from #42: expose raw ProseMirror ingest on `documents describe` so
agents and humans can write arbitrary ProseMirror JSON (tables, colspan,
task lists, custom nodes) without passing through the markdown parser.

Two new flags on `documents describe`:
- `--set-raw JSON_STRING`  inline ProseMirror doc, validated then written
  verbatim via `client.set_content` / `client.create_content`.
- `--set-raw-file PATH`    same, but read from disk.

Mutual exclusion now covers all four write flags
(`--set` / `--set-file` / `--set-raw` / `--set-raw-file`) with a single
clear error. Invalid JSON and non-doc root structures are rejected before
any network IO.

Command-layer regression tests cover: verbatim write, file read + write,
missing file, invalid JSON, non-doc root, missing-content root, and all
six mutual-exclusion pairs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Path A from #42: add GFM table support to `markdown_to_prosemirror` so
`documents describe --set` / `--set-file` (and every other markdown → PM
call site) preserves table structure instead of storing rows as literal
`|`-pipe paragraphs.

Rules:
- first row after a `| --- | --- |` separator → `tableHeader` cells
- body rows → `tableCell` cells
- alignment row consumed but not stored (1x1 cells per the issue non-goal)
- cells default to `{"colspan": 1, "rowspan": 1}` attrs
- escaped pipes (`\\|`) inside cells remain literal

Round-trip: `prosemirror_to_markdown` already emits GFM via `_table_to_md`,
so markdown → PM → markdown now reproduces the table intact.

Parser-level tests cover header/body distinction, attrs, alignment-row
variants, inline marks inside cells, escaped pipes, round-trip equality,
followed-by-paragraph, regression guards for non-table markdown, and
false-positive guards (single `|` row with no separator; `|` in prose).

Command-layer integration test: `documents describe --set "| a | b |..."`
intercepts the markup handed to `create_content` and asserts it contains a
`table` node with `tableHeader` / `tableCell` children.

README and skill updated to describe markdown-table round-trip and the
`--set-raw` / `--set-raw-file` escape hatches added in the preceding
commit.

Closes #42.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…own-tables

# Conflicts:
#	skills/huly-cli/SKILL.md
…own-tables

# Conflicts:
#	skills/huly-cli/SKILL.md
…own-tables

# Conflicts:
#	README.md
#	src/huly_cli/commands/documents.py
@teslakoile teslakoile merged commit eeb6594 into master Apr 19, 2026
1 check passed
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.

documents: preserve markdown tables when setting content via --set / --set-file

1 participant