K KodePressdocumentation
Laravel 11 · MySQL 8 Specification Built 2026-10-10

05 — Page builder#

The editor is the product. Everything else supports it. One editor edits pages, posts, headers, footers, mega-menu panels, global blocks and templates — the chrome around it changes, the editing model never does.

5.1 Screen layout#

┌───────────────────────────────────────────────────────────────────────────────┐
│ ← Pages   About us            [Draft saved 12:04]   [Desktop|Tablet|Mobile]  │
│                                            [Preview] [Save draft] [Publish ▾] │
├──────────────┬──────────────────────────────────────────────┬────────────────┤
│  OUTLINE     │               LIVE PREVIEW (iframe)          │  SETTINGS      │
│              │                                              │                │
│ ▾ Hero       │   ┌────────────────────────────────────────┐  │  Heading       │
│   ▾ Col 1    │   │  Welcome                               │  │  ──────────    │
│     Heading  │   │  Short intro                           │  │  ▾ Content     │
│     Text     │   │  [Contact us]                          │  │   Text  [___]  │
│     Button   │   │                                        │  │   Level [H1 ▾] │
│   ▾ Col 2    │   └────────────────────────────────────────┘  │  ▸ Style       │
│     Image    │                                              │  ▸ Advanced    │
│ ▸ Features   │   [ + Add section ]                          │                │
│ ▸ CTA        │                                              │  [Duplicate]   │
│              │                                              │  [Delete]      │
│ [+ Section]  │                                              │                │
└──────────────┴──────────────────────────────────────────────┴────────────────┘
Region Width Owner Behaviour
Top bar full PageEditor title, autosave status, device switch, Preview, Save draft, Publish (with a dropdown: Publish, Schedule, Submit for review)
Outline 260 px, collapsible OutlineTree nested list of sections, columns, blocks; drag to reorder; click to select; keyboard navigable
Preview fills iframe renders the draft tree through the real public renderer; click an element to select it
Settings 320 px SettingsPanel generated from schema.php; Content open, Style and Advanced collapsed

On screens narrower than 1280 px the outline collapses to an icon rail. Below 1024 px the editor switches to a stacked mode: preview on top, settings in a bottom sheet. The editor is usable on a tablet; it does not claim to be usable on a phone.

5.2 The non-negotiable defaults#

  1. Content tab only. Style and Advanced are collapsed on every selection, every time. They do not remember being open across selections — a beginner must never find themselves in a panel of CSS they did not ask for.
  2. No blank page. New page opens a template picker. Add section opens a gallery. The only way to get an empty section is Add section -> Blank, which is the last item.
  3. No colour pickers in Content. Colour fields show theme token swatches. A free hex input exists only in Advanced.
  4. No browser dialogs. Delete opens an in-app modal naming the thing; autosave reports through a quiet status text; errors are toasts.

Eight ready-made section designs ship in Phase 1, each a templates row with kind = section:

Category What it contains
Hero heading + text + button + image, two columns
Features 3 columns of icon_list + heading + text
Pricing 3 columns of heading + text + icon_list + button
FAQ accordion, single column, container width
Testimonial quote text + author, centred
CTA heading + button on a primary background
Gallery gallery block, 3 columns, lightbox on
Contact form + address text + map image, two columns

The gallery shows preview.png-style thumbnails, a search box and a category filter. Inserting a section deep-copies the template tree and assigns new ids. User-saved sections appear in a My sections tab, and global blocks in a Reusable tab (inserting from there creates a reference, not a copy — the UI says so).

5.4 Selection, drag and drop, and the toolbar#

5.5 State, autosave, undo/redo#

The Livewire component holds the tree as one PHP array, mirrored in Alpine for instant local UI.

Concern Rule
Dirty tracking any mutation marks the tree dirty and bumps an in-memory revision counter
Autosave debounced 3 s after the last change, and at most once every 10 s; writes pages.draft_content only
Autosave failure the status text turns to a retry state, the tree stays in memory, and a retry happens on the next change or every 15 s; the browser warns on unload while dirty
Undo stack last 50 tree snapshots, client-side, in memory; cleared on publish
Redo invalidated by any new mutation
Conflict the editor holds draft_updated_at; if the stored value is newer (another tab or user), saving shows a modal: Keep mine / Load theirs / Open the other version in a new tab. Last write never silently wins
Leaving navigating away while dirty asks in an in-app modal, not onbeforeunload alone

Undo snapshots are whole trees. A page large enough for that to hurt (hundreds of blocks) is already a page the user should split; a soft warning appears past 150 blocks.

5.6 Preview#

5.7 Publishing#

Publish ▾ offers:

Action Requires Result
Publish pages.publish permission validate, snapshot a version, status published, published_at = now(), clear that page in the cache
Schedule same status scheduled, published_at in the future; the cron publishes it and clears the cache
Submit for review pages.create only (Writer) status pending, notifies Editors; no public change
Unpublish pages.publish status draft, page removed from the cache and from the sitemap, a 410 or 404 is served
Save as template templates.manage copies the tree into templates with a generated thumbnail

Publish is blocked, with the offending block named, when: a required field is empty, a referenced media or form row is missing, the tree fails structure validation, or a non-Admin left content in an html block. "Blocked" means a toast plus the outline marking the block in red — never a silent failure and never a 500.

5.8 Version history#

Page -> History lists versions newest first: version number, time, author, label, and a short change summary (3 blocks added, 1 removed, Hero settings changed), computed by diffing the two trees structurally.

5.9 Pages screen#

Feature Detail
List title, path, status pill, author, last updated, template, actions
Search title and path, case-insensitive, Bengali-safe
Filters status, type, author, category (posts), date range
Sort last updated (default), title, published date
Bulk publish, unpublish, trash, restore, set category/tags (posts)
Create New page -> template picker -> title -> opens the editor
Duplicate deep copy with (copy) in the title and a free slug; always a draft
Trash soft delete; a Trash tab lists them with Restore and Delete permanently
Permanent delete confirm modal naming the page; removes versions, pivots and cache entries; keeps the audit log
Homepage a Set as homepage action; the current homepage is marked in the list
Hierarchy pages display nested by parent_id; moving a parent rewrites descendant paths and writes 301s

The Blog screen is the same component with type = post, plus category/tag columns and the scheduled tab.

5.10 Editing other things with the same editor#

Target Entry point Context Differences
Post Blog -> post page adds excerpt, categories, tags, featured image, author in a right-panel Post tab
Header Design -> Header part kind header; three zones (top bar, main, sticky); part settings tab; preview over any chosen page
Footer Design -> Footer part kind footer; column presets 1–6
Mega panel Menus -> item -> Mega menu mega panel width/background settings; preview inside a simulated header
Global block Design -> Saved sections page single section; shows a usage count and warns before publishing
Template Design -> Templates page or section no publish; Save template instead

In every case the centre is the same preview, the right panel is the same generated form, and the outline is the same tree. A user who learned to edit a page can edit the header.

5.11 Inline editing from the public site (Phase 4)#

When a user with edit rights views the public site, each section shows a small Edit affordance on hover, and text blocks become directly editable in place.

5.12 Performance budget for the editor#

Interaction Budget How
Select a block < 100 ms Alpine handles selection locally; the panel is pre-rendered per block type
Edit a text field < 50 ms visible local state; Livewire sync is debounced
Preview refresh < 600 ms targeted re-render of the changed section where possible, full reload otherwise
Autosave < 400 ms one UPDATE of a JSON column, no re-render
Insert a section < 500 ms template tree is fetched once and cached client-side
Open the editor < 1.5 s one page query, one version query, the cached block manifest

A tree of 100 blocks is the benchmark size; the gallery and media picker paginate so neither grows with the library.

KodePress documentation · generated from the Markdown sources by tools/build-docs-site.py · internal preview, not indexed.