editor: a rich-text body followed by two or more item rows fails to load (adjacent text nodes) #230

Closed
opened 2026-10-12 18:04:23 +02:00 by dominik.polakovics · 0 comments

What happens

An items owner (text-media, image, figures) whose file pretty-prints its body and rows onto their own lines cannot be opened in the editor once it has two or more rows:

{{< text-media >}}
Body.
{{< img image="a.jpg" />}}
{{< img image="b.jpg" />}}
{{< /text-media >}}

GET /api/element/body answers 422 {"error":"adjacent text nodes","code":"error.unexpected"} and the form never opens. With one row it loads. This is exactly the shape the shipped text-media.yaml comment and AGENTS.md teach, so every hand-authored or migrated text-media with two images hits it.

Why

handleElementBody (internal/server/body.go) pulls the item rows out of the children with elemops.PartitionItems and serializes what is left. The newline after each self-closing row is its own text node (the parser only swallows the separator after block shortcodes), so removing two rows leaves two text nodes side by side, which content.SerializeFragment refuses.

The save path (elemops.ApplyEdit) already merges those text runs with its own coalesceText; the read path never got the same treatment. Two implementations of one split, only one of them correct.

Fix

Make PartitionItems itself return a serializable rest (merge adjacent text there), so every caller is covered and the save path's separate merge goes away. Regression tests at the elemops and the HTTP layer.

Not in scope here: the one-line rewrite on save (rows and body glued onto one line, noisy diffs, whitespace leaking into the translation hash). That is a canonical-layout change and is tracked separately.

## What happens An items owner (text-media, image, figures) whose file pretty-prints its body and rows onto their own lines cannot be opened in the editor once it has two or more rows: ``` {{< text-media >}} Body. {{< img image="a.jpg" />}} {{< img image="b.jpg" />}} {{< /text-media >}} ``` `GET /api/element/body` answers `422 {"error":"adjacent text nodes","code":"error.unexpected"}` and the form never opens. With one row it loads. This is exactly the shape the shipped `text-media.yaml` comment and AGENTS.md teach, so every hand-authored or migrated text-media with two images hits it. ## Why `handleElementBody` (internal/server/body.go) pulls the item rows out of the children with `elemops.PartitionItems` and serializes what is left. The newline after each self-closing row is its own text node (the parser only swallows the separator after block shortcodes), so removing two rows leaves two text nodes side by side, which `content.SerializeFragment` refuses. The save path (`elemops.ApplyEdit`) already merges those text runs with its own `coalesceText`; the read path never got the same treatment. Two implementations of one split, only one of them correct. ## Fix Make `PartitionItems` itself return a serializable rest (merge adjacent text there), so every caller is covered and the save path's separate merge goes away. Regression tests at the elemops and the HTTP layer. Not in scope here: the one-line rewrite on save (rows and body glued onto one line, noisy diffs, whitespace leaking into the translation hash). That is a canonical-layout change and is tracked separately.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle#230
No description provided.