feat(editor): element cards show a served children digest instead of nested cards #262

Merged
dominik.polakovics merged 1 commit from afk/260 into main 2026-10-13 14:13:53 +02:00

Summary

  • Server: each tree node's verdict carries an optional digest ({of, count, label, i18n?, rows[≤3]: {name, text?|image?, disabled}}), built in elemops.ChildrenDigest from ChildRole and the refactored elemops.Summarize (the single summary owner; ElementSummary = its text). elemops.ItemsLabel is shared with the form's items section.
  • Overview: cards render the digest ("3 Cards · … · …", "2 × Accordion item · …", thumbnails, "+n", disabled rows dimmed), including the unused-content list. No nested cards, nested insert rows or nested drop targets; hasSubList/elementImageRefs/sourceChildrenOf removed.
  • Paste into a container: a container card's ⋮ menu gains Paste inside (and ⌘V on the focused card), reusing pasteFor (same refusals, same move/transfer requests).
  • Counts/translations: gridCardCount counts top-level cards only; the Columns view shows a translated container as one card with no nested placeholders; a change anywhere inside a card marks that card.
  • Docs: AGENTS.md ("Summary and children digest", Containers), README (tree verdict fields, itemDisplay), and the add-element, edit-content, verify-site, preview-site and redo-theme skills.

Decisions to review

  • Thumbnails for img rows with alt text. The issue's literal rule (a thumbnail only when the summary came from an image param) would show alt texts on the showcase Image card, which contradicts the AC and the mockup. A row therefore also shows as a thumbnail when its element declares an image param with itemDisplay: readonly (img.yaml: "the file is the row's identity"; card.yaml deliberately has no itemDisplay, so its rows keep their headings). This is documented in AGENTS.md and in the element package comments.
  • Count wording: items collections read "{count} {label}" (items labels are plural); child elements read "{count} × {label}" (element labels are singular). With no label, a generic plural ("n items" / "n elements") is used.
  • Link picker keeps the element summary alone; the unused-content list shows the digest.
  • Unused-content rows and inherited-slot cards now list top-level elements only, consistent with "no card nests cards".

Verification

  • go vet ./..., gofmt, go test ./... (with web/dist built): all green. New tests pin the digest for an items owner, an image owner (rules a and b), a container, a disabled child, more than three children, mixed names, no children and a slot wrapper.
  • cd web && npm test: 124 files / 3178 tests pass; tsc --noEmit is clean; npm run build succeeds.
  • harness check passes on a fresh harness init site with the showcase page. GET /api/page for it serves: cards → 3 heading rows; image → 2 image rows; text-media → 1 image row; accordion → of: element, label "Accordion item", 2 summaries; hero (only button chips) → no digest.

Closes #260

🤖 Generated with Claude Code

## Summary - **Server**: each tree node's verdict carries an optional `digest` (`{of, count, label, i18n?, rows[≤3]: {name, text?|image?, disabled}}`), built in `elemops.ChildrenDigest` from `ChildRole` and the refactored `elemops.Summarize` (the single summary owner; `ElementSummary` = its text). `elemops.ItemsLabel` is shared with the form's items section. - **Overview**: cards render the digest ("3 Cards · … · …", "2 × Accordion item · …", thumbnails, "+n", disabled rows dimmed), including the unused-content list. No nested cards, nested insert rows or nested drop targets; `hasSubList`/`elementImageRefs`/`sourceChildrenOf` removed. - **Paste into a container**: a container card's ⋮ menu gains **Paste inside** (and ⌘V on the focused card), reusing `pasteFor` (same refusals, same move/transfer requests). - **Counts/translations**: `gridCardCount` counts top-level cards only; the Columns view shows a translated container as one card with no nested placeholders; a change anywhere inside a card marks that card. - **Docs**: AGENTS.md ("Summary and children digest", Containers), README (tree verdict fields, itemDisplay), and the add-element, edit-content, verify-site, preview-site and redo-theme skills. ## Decisions to review - **Thumbnails for `img` rows with alt text.** The issue's literal rule (a thumbnail only when the summary came from an image param) would show alt texts on the showcase Image card, which contradicts the AC and the mockup. A row therefore also shows as a thumbnail when its element declares an image param with `itemDisplay: readonly` (img.yaml: "the file is the row's identity"; card.yaml deliberately has no itemDisplay, so its rows keep their headings). This is documented in AGENTS.md and in the element package comments. - **Count wording**: items collections read "{count} {label}" (items labels are plural); child elements read "{count} × {label}" (element labels are singular). With no label, a generic plural ("n items" / "n elements") is used. - **Link picker** keeps the element summary alone; the unused-content list shows the digest. - Unused-content rows and inherited-slot cards now list top-level elements only, consistent with "no card nests cards". ## Verification - `go vet ./...`, `gofmt`, `go test ./...` (with `web/dist` built): all green. New tests pin the digest for an items owner, an image owner (rules a and b), a container, a disabled child, more than three children, mixed names, no children and a slot wrapper. - `cd web && npm test`: 124 files / 3178 tests pass; `tsc --noEmit` is clean; `npm run build` succeeds. - `harness check` passes on a fresh `harness init` site with the showcase page. `GET /api/page` for it serves: cards → 3 heading rows; image → 2 image rows; text-media → 1 image row; accordion → `of: element`, label "Accordion item", 2 summaries; hero (only button chips) → no digest. Closes #260 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(editor): element cards show a served children digest instead of nested cards
All checks were successful
ci / build-and-test (pull_request) Successful in 3m4s
ci / release (pull_request) Has been skipped
1d6bfe23c5
Every shortcode node of the served tree now carries an optional `digest`
in its verdict, beside `role` and `summary`: the count of its item or
element children, the collection's label (items.label, else the item
element's label; for element children the shared child label), and the
first three children's summaries, a row being a media reference instead
of text when its summary came from an image param or its element declares
an image param `itemDisplay: readonly` (the file is the row's identity,
as in `img`). elemops.Summarize is the one summary owner; ElementSummary
is its text, and ChildrenDigest builds the digest from ChildRole.
elemops.ItemsLabel is now shared by the form's items section and the
digest.

The page overview renders the digest under each card's summary ("3 Cards
· … · …", thumbnails, "+n", disabled rows dimmed), also in the unused
content list, and no card nests cards or insert rows any more: a
container's children are managed in its form (#259). A container card's
⋮ menu offers "Paste inside" (and ⌘V on the focused card), going through
the same paste as an insert row. The Content tab counts top-level cards
only, the translation grid shows a translated container as one card with
no nested placeholders, and a change anywhere inside a card marks that
card.

AGENTS.md documents the summary rule, itemDisplay and the digest; the
add-element, edit-content, verify-site, preview-site and redo-theme
skills and the README follow.

Closes #260

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Author
Owner

[autoland] verdict: pass

Lander verdict: CONCERNS (non-blocking) — approved, not merged

Signal relied on: labctl pr checks 262 → success (ci / build-and-test: npm ci + vite build + vitest run, then go vet ./..., go test ./..., go build; release skipped as expected on a PR). Nothing re-run locally beyond gofmt -l on the changed Go files (clean). Branch is current with main (merge-base = origin/main 90f5e81), so there's no conflict.

Conventions: the title follows Conventional Commits (feat(editor): …), and Closes #260 is present. The diff stays within #260: server digest in elemops, client rendering, nested cards/insert rows/drop targets removed, Paste inside, top-level-only counts and translation grid, AGENTS.md + README + the add-element / edit-content / verify-site / preview-site / redo-theme skills. The ItemsLabel extraction in internal/server/forms.go moves an existing rule to one shared owner, as CLAUDE.md asks. I found no client re-derivation of summaries or field types. ElementSummary's text precedence is unchanged, so the link picker and chips behave as before.

Concern (for the maintainer's eye, not blocking)

itemDisplay: readonly now also controls the digest, which goes past the issue's stated rule. The PR flags this itself under "Decisions to review".

  • #260 says a row is a thumbnail when "its summary came from an image-typed param", lists "Any sidecar key controlling the digest" as out of scope, and says "No sidecar syntax". The showcase img rows all carry alt, so that rule alone would give the Image card text rather than the thumbnails the AC asks for. The PR closes that gap with a second trigger: an image param with itemDisplay: readonly holding a file (internal/elemops/summary.go:93-96, identityImage).
  • That changes a documented sidecar key. itemDisplay used to be "client-only, inert outside items rows". It now has a server-side display meaning (internal/element/schema.go:19-22, internal/element/load.go buildParam comment). AGENTS.md documents the new behaviour (scaffold/site/AGENTS.md:562), but then says at :567-568 (and README.md in the tree-verdict section) "No sidecar key controls the digest: items, summary, itemDisplay, … decide it". Those two statements pull against each other.
  • This is a reasonable way to meet the AC: card.yaml deliberately has no itemDisplay, so cards keep their headings. But a site developer's itemDisplay choice now decides how their rows appear on the overview, so this needs an explicit yes from the maintainer before it becomes part of the documented contract.

No other findings. The Go tests pin the digest for an items owner, a container, a disabled child, more than three children, mixed and unknown children, a slot wrapper and the JSON placement. Vitest covers the digest line, Paste inside / ⌘V, and the top-level-only translation grid and counts.

[autoland] verdict: pass ## Lander verdict: CONCERNS (non-blocking) — approved, not merged **Signal relied on:** `labctl pr checks 262` → `success` (`ci / build-and-test`: npm ci + `vite build` + `vitest run`, then `go vet ./...`, `go test ./...`, `go build`; `release` skipped as expected on a PR). Nothing re-run locally beyond `gofmt -l` on the changed Go files (clean). Branch is current with `main` (merge-base = `origin/main` 90f5e81), so there's no conflict. **Conventions:** the title follows Conventional Commits (`feat(editor): …`), and `Closes #260` is present. The diff stays within #260: server digest in `elemops`, client rendering, nested cards/insert rows/drop targets removed, Paste inside, top-level-only counts and translation grid, AGENTS.md + README + the add-element / edit-content / verify-site / preview-site / redo-theme skills. The `ItemsLabel` extraction in `internal/server/forms.go` moves an existing rule to one shared owner, as CLAUDE.md asks. I found no client re-derivation of summaries or field types. `ElementSummary`'s text precedence is unchanged, so the link picker and chips behave as before. ### Concern (for the maintainer's eye, not blocking) **`itemDisplay: readonly` now also controls the digest, which goes past the issue's stated rule.** The PR flags this itself under "Decisions to review". - #260 says a row is a thumbnail when "its summary came from an image-typed param", lists "Any sidecar key controlling the digest" as out of scope, and says "No sidecar syntax". The showcase `img` rows all carry `alt`, so that rule alone would give the Image card text rather than the thumbnails the AC asks for. The PR closes that gap with a second trigger: an image param with `itemDisplay: readonly` holding a file (`internal/elemops/summary.go:93-96`, `identityImage`). - That changes a documented sidecar key. `itemDisplay` used to be "client-only, inert outside items rows". It now has a server-side display meaning (`internal/element/schema.go:19-22`, `internal/element/load.go` buildParam comment). AGENTS.md documents the new behaviour (`scaffold/site/AGENTS.md:562`), but then says at `:567-568` (and `README.md` in the tree-verdict section) "No sidecar key controls the digest: `items`, `summary`, `itemDisplay`, … decide it". Those two statements pull against each other. - This is a reasonable way to meet the AC: `card.yaml` deliberately has no `itemDisplay`, so cards keep their headings. But a site developer's `itemDisplay` choice now decides how their rows appear on the overview, so this needs an explicit yes from the maintainer before it becomes part of the documented contract. No other findings. The Go tests pin the digest for an items owner, a container, a disabled child, more than three children, mixed and unknown children, a slot wrapper and the JSON placement. Vitest covers the digest line, Paste inside / ⌘V, and the top-level-only translation grid and counts.
Sign in to join this conversation.
No reviewers
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!262
No description provided.