feat(rte): opt-in 'table' token with GFM-constrained round-trip #102
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle!102
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/94"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds
tableas a ninth rte formatting token — opt-in only. The default allow-set stays the current 8 tokens;tablemust be named explicitly in an element'sallow:list.text.yamlopts in with an explicit 9-token list.Closes #94
Engine (Go)
internal/element: the valid token vocabulary (rteAllowTokens, 9 tokens) is decoupled from the default set (defaultRTEAllow, unchanged 8). Unknown-token errors now list the full vocabulary. The agentsdoc drift test iterates the full vocabulary, so AGENTS.md must nametable.internal/server/body.go: tables move from never-allowed to allow-gated ("tables are not allowed in this field" when the token is absent). When allowed, the walk descends into cells so their inline content is still policed — the no-raw-HTML gate is untouched (raw HTML in a cell is still rejected, as are disallowed marks). Markdown-policy bodies accept tables unconditionally, as before.Editor (web)
allow.tsmirror:tablejoinsAllowToken; newALL_RTE_TOKENS(9) feeds validation;DEFAULT_RTE_ALLOWstays 8.rte/table.ts: tiptap table extensions (pinned 2.27.2), GFM-constrained — inline-only cells (inline*), merge/split and header-toggle commands inert, first row is structurally the header, no column resizing (no colwidth ever persists). Loaded only when the field's allow-set grantstable; with the default set the schema has no table nodes at all.markdown.ts: markdown-it's coretablerule is enabled on opt-in; parser maps table tokens onto the tiptap nodes with GFM alignment captured per cell (textAlign). The serializer emits canonical single-space-padded pipe tables (delimiter row derived from the header row's alignment;|escaped as\|; hard breaks in cells degrade to a space; smuggled colspans/ragged rows are repaired to valid GFM — never HTML). Round-trip is byte-equivalent for canonical tables; a non-canonical hand-authored table normalizes once, then is a fixed point.Theme
layouts/_default/_markup/render-table.htmlwraps every rendered markdown table in<div class="ce-table" tabindex="0">(horizontal scroll, keyboard-focusable), honoring per-column GFM alignment via inlinetext-align— the documented exception to the classes-only rule. Styled intheme.css, documented in the theme README. Verified with a live Hugo build.Contract surfaces (per CLAUDE.md, same change)
Go allow-set, TS mirror,
scaffold/site/AGENTS.md(token named; "omitting allow" rewording to "default set — every token above excepttable"), drift test.Verification
go vet ./...,go test ./...(all packages, fresh run),go build— green (CGO_ENABLED=0; this sandbox has no gcc).vite buildsucceeds,tsc --noEmitclean (CI parity: npm ci/build/test).<thead>, and alignment styles in emitted HTML.🤖 Generated with Claude Code
Validation verdict: PASS.
Verification signal relied on: the green Forgejo Actions run
ci / build-and-test(success, 7m8s) — fullgo vet/go test ./.../go buildand the web vitest suite, including the round-trip identity table intest/rte/markdown.test.tsand the server allow-gating tests. Not re-run.Checked:
main(base had moved; no conflict).feat(rte): …); body carriesCloses #94onafk/94.rteAllowTokens/ALL_RTE_TOKENSaddtableas opt-in-only vocabulary. Elements omittingallownever gain tables, server- and client-side.text.yamlopts in explicitly (8 defaults +table).body.go) allow-gates tables and still descends into cells to police inline content (raw HTML in a cell still rejected).scaffold/site/AGENTS.md, and theagentsdoc_test.godrift test (now iterating the full vocabulary).No blockers. Awaiting a free-text merge go-ahead before merging.