Page management: create, rename, move, delete, reorder, enable/disable with page types #9
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#9
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
What to build
Full tree editing with guardrails (docs/DECISIONS.md D8, D9, D10): create pages/sections constrained by developer-defined page types (sidecar YAML: allowed child types, allowed elements, front-matter fields); rename and move with automatic
aliases:entries so published URLs never 404; delete with confirmation; drag & drop reorder writingweight; per-page enabled/disabled toggle mapping todraft: true. Every page is a leaf bundle. Slugs auto-generate from title, overridable.Acceptance criteria
draft: truealiases; existing aliases are preservedweight(and moves bundles) consistentlydraftand updates the tree's visual stateBlocked by
Agent Brief
Category: enhancement
Summary: Turn #4's read-only page tree into full page management: create pages/sections constrained by developer-defined page types (sidecar YAML), rename/move with automatic
aliases:so published URLs never 404, drag & drop reordering that writesweight, a per-page enabled/disabled toggle mapping todraft, guarded delete, and a generated form for the page-level front-matter fields each page type declares.Dependency note: this issue is hard-blocked by #4 (page tree browsing) — do not start it until #4 is merged. It builds directly on what #4 provides: the tree API and tree pane, the (path, lang) page identity used by every endpoint here, and front-matter metadata extraction (title/weight/draft). It also reuses machinery from #3 (sidecar-YAML loading/validation, the schema-driven form renderer, and the canonical save path through #2's round-trip engine) and from #2 itself; the scheduler handles ordering. The issue body's
docs/DECISIONS.mdD8/D9/D10 references point at a file that is not committed — this brief is self-contained.Current behavior (once #4 lands):
The tree pane shows sections and pages read-only, ordered by weight then title, with drafts visually distinct; selecting a page lists its elements, and (per #3) element params are editable. But the tree itself cannot be changed from the tool: creating, renaming, moving, reordering, deleting, or toggling a page means hand-editing files and directories on disk. No page-type concept exists — nothing constrains what kinds of pages can be created where, and page-level front matter (title, description/SEO) has no form.
Desired behavior:
Page types. Developers define page types as sidecar YAML files under the site's editor config, one file per type (a sibling convention to #3's
editor/elements/*.yaml— e.g.editor/pagetypes/*.yaml). Each page type declares: a human-readable label and icon; which page types are allowed as children beneath it; which element types its pages may contain; and its page-level front-matter fields, declared with the same field vocabulary as #3's element params (name, type, label, default, required, options) — title and description/SEO fields being the typical case. A suggested minimal shape (exact key naming is the implementer's choice; every concept must be representable):Loading follows #3's validation discipline: malformed files or unknown field types produce actionable errors surfaced via the API/UI while valid sidecars still load. There must be a way to express which types are allowed at the root of the content tree. A page records its type in front matter (Hugo's native
typefield is the natural fit), it round-trips through the canonical serializer, and the tree data exposes each node's type so the UI can offer only legal operations. Theelementsallow-list is parsed, validated against known element schemas, and served — enforcing it when adding elements is #6's job, not this issue's.Create. "New page" (and "new section") under a selected parent offers only the page types the parent's type allows as children; at the tree root, only the root-allowed types. Creation asks for a title, auto-generates the slug from it (lowercased, hyphenated, collision-safe against existing siblings), lets the user override the slug, and writes a leaf bundle — a directory named by the slug containing the content file (
index.md;_index.mdfor a section) — with the page type's front-matter defaults, its type recorded, anddraft: trueso new pages never publish accidentally. Every page this tool creates is a bundle; plain single-file pages are a legacy shape it reads (#4) but never writes.Rename and move. Renaming (slug change) and moving to a different section both rewrite the bundle's path on disk — the whole bundle directory, including any page resources in it — and append the page's previous published URL to the
aliases:list in its front matter so already-published URLs keep resolving. Existing aliases are preserved, duplicates are not added. Moving or renaming a section changes every descendant's URL too, so each descendant page gets its old URL appended to its own aliases — "published URLs never 404" is the invariant, not "one alias per operation".Reorder. Drag & drop within a section reorders siblings by rewriting
weightwith a deterministic scheme (e.g. renumbering with gaps); drag & drop across sections is a move (path rewrite + alias) plus placement at the drop position. After any reorder, the tree order and Hugo's rendered order agree.Enable/disable. A per-page toggle flips
draftin front matter and immediately updates the node's visual state in the tree. Disabled pages remain visible in the preview (the supervised server builds drafts) — disabled means "not published", not "not previewable".Delete. Deleting shows a confirmation that names the page and states its descendant count; a section with children can never be deleted without that count being explicit in the confirmation. Confirming removes the bundle directory (for a section, the subtree). There is no trash or undo — recoverability via git is #10.
Page settings form. Selecting a page (as opposed to one of its elements) offers a form for the front-matter fields its page type declares, generated by the same schema-driven renderer as #3's element forms, with the same validation behavior: server-side validation is authoritative, errors map inline to fields, and save writes canonically.
Write safety. Every front-matter mutation (aliases, weight, draft, type, declared fields) preserves front-matter keys the tool does not manage and leaves the page body untouched — all writes go through the canonical round-trip engine, never through string munging. Every operation validates that source and target stay inside the language's content root (reusing #4's identity/path discipline); a target that already exists is a clear error, never an overwrite. Failed operations leave no half-moved state behind where the filesystem allows it (rename over copy-delete). Successful operations reflect in the preview via LiveReload and in the tree without losing expansion state.
Key interfaces:
aliaseslist.Acceptance criteria:
draft: true; slugs auto-generate from title, are collision-safe, and can be overriddenaliases; existing aliases are preserved; renaming/moving a section aliases every descendant's old URLweight; across sections it moves the bundle and places it at the drop position; resulting tree order matches Hugo's render orderdraftand the tree's visual state updates immediatelyOut of scope:
elementsallow-list is parsed and served here, enforced thereClosing as superseded: the maintainer has decided on a clean-slate reset of the project (issue #29, PR #30). The v1 implementation this issue builds on is being removed and the plan will be re-drawn from scratch; this spec is retired rather than actioned.