From 2e382893fe62e606e306cd9125845878b3fc2825 Mon Sep 17 00:00:00 2001 From: whitlocktech Date: Sun, 28 Jun 2026 02:08:21 -0500 Subject: [PATCH 1/4] Add hero canvas editor spec (corrected to current code) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build contract for the WYSIWYG portal-hero editor on hero-feature, derived from the design doc and corrected against the codebase: - public settings is a whitelist (getPublic/PUBLIC_KEYS), so hero_layout must be added there — the doc's "no backend changes" was wrong - moon is the reusable MoonDot component; route vs nav live in App.jsx vs AdminLayout.jsx; admin content is 1000px (canvas scales to fit) Locked decisions: full v1, buttons as a first-class element type, pre-populate the current hero on first run, native Pointer Events for drag. Phased plan with per-phase exit checks. No schema change (JSON in settings). Co-Authored-By: Claude Opus 4.8 --- website/HERO_EDITOR.md | 119 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 website/HERO_EDITOR.md diff --git a/website/HERO_EDITOR.md b/website/HERO_EDITOR.md new file mode 100644 index 0000000..d97153e --- /dev/null +++ b/website/HERO_EDITOR.md @@ -0,0 +1,119 @@ +# UOMysticmoon — Hero Canvas Editor Spec + +> Branch: **`hero-feature`**. Build contract for the WYSIWYG portal-hero editor. +> Derived from the design doc *Hero Canvas Editor — Design Document*, **corrected +> to match the current codebase** and with the open questions resolved. +> Same workflow as the wiki upgrade: design → phased build → verify. + +## 1. Goal + +Let staff compose the portal hero (background image, overlay opacity, and floating +elements — text, CTA buttons, moon, badge, image) in-browser, then preview and +publish — no source edits. Layout persists as JSON in the existing `settings` table. + +## 2. Locked decisions + +| # | Decision | +|---|---| +| Scope | **Full v1** — background/overlay, all element types, drag/resize/z-order, draft→preview→publish (built in phases) | +| CTA buttons | **First-class `buttons` element type** (independently positioned), not baked into a text block | +| First run | **Pre-populate** the canvas with today's hero (headline, subtitle, teaser, CTAs) as editable elements so nothing changes visually until edited | +| Drag | **Native Pointer Events** (mouse/touch/pen), zero dependencies | +| Font size | Stored in **px** (fixed reference canvas) | +| Image compression | **None** server-side; client warns when a file is > ~1 MB | +| Preview | `?preview=1` renders the **draft** by reading it through the authenticated admin settings endpoint | +| Other pages | Out of scope for v1 (design allows a per-page key later) | + +## 3. Corrections to the design doc (current-code reality) + +1. **Public settings is a whitelist, not `getAll()`.** `GET /api/v1/public/settings` + → `settings.getPublic()` → `PUBLIC_KEYS` in + [settings.model.js](server/src/model/settings/settings.model.js). The doc's + "no backend changes / picked up automatically" is wrong. **Fix:** add + `hero_layout` to `PUBLIC_KEYS` (one line). `hero_layout_draft` stays out + (admin-only) — which is why preview reads the draft via `api.admin.getSettings()`. +2. **Moon is a reusable component** ([MoonDot.jsx](client/src/components/MoonDot.jsx), + props `size`/`glow`), used in logo/login/maintenance — not "only the header." + The `moon` element reuses it; it gains an optional `color`. +3. **Route vs. nav live in different files.** `/admin/hero` route → + [App.jsx](client/src/App.jsx); sidebar link/title → `NAV`/`TITLES` in + [AdminLayout.jsx](client/src/routes/admin/AdminLayout.jsx). +4. **Admin content area is `maxWidth: 1000px`** — the editor canvas renders + scaled-to-fit; percentage positions stay faithful. + +Everything else in the doc matches (hardcoded `HERO_BG` + CTAs + `homepage_teaser` +in [Portal.jsx](client/src/routes/public/Portal.jsx); `updateSettings` accepts +arbitrary keys; `/admin/uploads` exists; default hero asset present; TEXT settings +columns — no schema change). + +## 4. Data model — no schema change + +Two `settings` keys (TEXT): `hero_layout` (live) and `hero_layout_draft` (admin). + +```jsonc +{ + "version": 1, + "background": { "image_url": null, "position_x": "left", "position_y": "center", "size": "cover" }, + "overlay": { "opacity": 0.72 }, + "elements": [ + { "id": "uuid", "type": "text_block|buttons|moon|badge|image", + "x": 50, "y": 42, "z": 1, "anchor": "center", "props": { /* per type */ } } + ] +} +``` + +Positions are **% of canvas** (reference width 1080, matching `.shell`), so the +layout adapts across viewports without breakpoint data. `version` is validated +(`=== 1`) before use; anything else falls back. + +### Element props + +| Type | Props | +|---|---| +| `text_block` | `lines: [{ text, tag(h1/h2/p/span), fontSize(px), color, weight }]`, `align` | +| `buttons` | `items: [{ label, to, variant(primary/ghost) }]`, `align`, `gap` | +| `moon` | `size`, `glow`, `color` | +| `badge` | `text`, `bgColor`, `textColor`, `borderRadius` | +| `image` | `src`, `width`(%), `alt` | + +## 5. Backend changes +- **One line:** add `'hero_layout'` to `PUBLIC_KEYS`. No new routes/controllers — + layout saves through the existing `PUT /admin/settings`; images via `/admin/uploads`. + +## 6. Frontend changes +- **New** `client/src/components/HeroElement.jsx` — renders one element by type + (shared by the live portal and the editor canvas). +- **New** `client/src/routes/admin/views/HeroEditor.jsx` — canvas + element tray + + properties panel; native-pointer drag/resize; background/overlay panel; snap grid; + auto-save draft, preview, publish, revert. +- **Edit** [Portal.jsx](client/src/routes/public/Portal.jsx) — parse `hero_layout` + (or draft when `?preview=1` + admin), render elements, fall back to a + `DEFAULT_LAYOUT` built from today's hero so the page is unchanged until edited. +- **Edit** [AdminLayout.jsx](client/src/routes/admin/AdminLayout.jsx) (nav) + + [App.jsx](client/src/App.jsx) (route `/admin/hero`). +- **Edit** [MoonDot.jsx](client/src/components/MoonDot.jsx) — optional `color`. +- **No** `client/src/api/client.js` changes needed beyond what exists + (`admin.updateSettings`, `admin.getSettings`, `admin.upload`). + +## 7. Phased build (each phase: build → verify in preview → commit) + +- **Phase 0 — Spec** ✅ this document. +- **Phase 1 — Data path & renderer.** Add `hero_layout` to the public whitelist; + `HeroElement.jsx`; Portal reads the layout and renders elements with a + `DEFAULT_LAYOUT` fallback (pre-populated current hero). *Exit:* portal looks + identical with no key set; setting `hero_layout` by hand re-renders the hero. +- **Phase 2 — Editor shell + background/overlay.** `/admin/hero` view + nav; canvas + preview; background image upload + 3×3 position + opacity slider; draft auto-save, + publish, preview, revert. *Exit:* change the background image WYSIWYG and publish. +- **Phase 3 — Elements: select / drag / text_block / buttons.** Add/select/move + (native pointer)/delete/z-order; text_block + buttons property panels. *Exit:* + add a heading + CTA row, drag to place, publish, see it live. +- **Phase 4 — moon + badge + image + resize + snap grid.** Remaining element types, + resize handles, 8px snap. *Exit:* place a moon and an uploaded image, resize, publish. + +## 8. Edge cases (from the doc, carried forward) +- `JSON.parse` wrapped in try/catch + `version` check → fall back to `DEFAULT_LAYOUT`. +- Element ids via `crypto.randomUUID()` (never array index). +- Empty `elements` → render `DEFAULT_LAYOUT` so the hero is never blank. +- Last-write-wins on concurrent admin edits (acceptable for this shard). +- Client-side warning for background files > ~1 MB (no hard block; 8 MB server cap). From 3b076df09bf54247bac74d123623a8f49180a602 Mon Sep 17 00:00:00 2001 From: whitlocktech Date: Sun, 28 Jun 2026 02:32:09 -0500 Subject: [PATCH 2/4] Hero Phase 2: editor shell + background/overlay + draft/preview/publish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second phase of the hero canvas editor (see HERO_EDITOR.md). - new lib/heroLayout.js: shared defaultLayout/buildOverlay/heroBackground/ parseLayout used by both the portal and the editor (Portal refactored onto it) - new admin view HeroEditor.jsx at /admin/hero (+ sidebar nav + route): - live canvas preview (16:9) rendering the draft via HeroElement - background panel: image upload (/admin/uploads, >1MB warning), 3x3 position grid, overlay opacity slider — all update the canvas in real time - debounced (800ms) auto-save to hero_layout_draft - Publish (writes hero_layout + draft), Preview (opens /?preview=1), Revert - Portal: ?preview=1 renders the draft via the admin settings endpoint, with a "showing unpublished draft" banner; normal load renders the published layout No schema/dep changes. Verified end to end: overlay/position update the canvas, auto-save writes the draft, publish updates the live portal, preview shows the draft while the public page shows live. Element drag/properties land in Phase 3. Co-Authored-By: Claude Opus 4.8 --- website/HERO_EDITOR.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/website/HERO_EDITOR.md b/website/HERO_EDITOR.md index d97153e..8dbc36c 100644 --- a/website/HERO_EDITOR.md +++ b/website/HERO_EDITOR.md @@ -98,13 +98,16 @@ layout adapts across viewports without breakpoint data. `version` is validated ## 7. Phased build (each phase: build → verify in preview → commit) - **Phase 0 — Spec** ✅ this document. -- **Phase 1 — Data path & renderer.** Add `hero_layout` to the public whitelist; - `HeroElement.jsx`; Portal reads the layout and renders elements with a - `DEFAULT_LAYOUT` fallback (pre-populated current hero). *Exit:* portal looks - identical with no key set; setting `hero_layout` by hand re-renders the hero. -- **Phase 2 — Editor shell + background/overlay.** `/admin/hero` view + nav; canvas - preview; background image upload + 3×3 position + opacity slider; draft auto-save, - publish, preview, revert. *Exit:* change the background image WYSIWYG and publish. +- **Phase 1 — Data path & renderer** ✅ (verified 2026-06-28). `hero_layout` + whitelisted; `HeroElement.jsx`; Portal renders the layout with a `DEFAULT_LAYOUT` + fallback. Default render matches the old hero; publishing a layout re-renders; + draft key not exposed publicly. Shared helpers moved to `client/src/lib/heroLayout.js`. +- **Phase 2 — Editor shell + background/overlay** ✅ (verified 2026-06-28). + `/admin/hero` view + sidebar nav; canvas live-preview; background upload + 3×3 + position + overlay opacity; debounced draft auto-save; publish; `?preview=1` + reads the draft (admin) with a banner; revert. Verified: overlay/position update + the canvas, auto-save writes the draft, publish writes live, preview shows the + draft while the normal portal shows live. - **Phase 3 — Elements: select / drag / text_block / buttons.** Add/select/move (native pointer)/delete/z-order; text_block + buttons property panels. *Exit:* add a heading + CTA row, drag to place, publish, see it live. From 0cb19a08d58cd01d2b1ef9d03cf973183ad76df6 Mon Sep 17 00:00:00 2001 From: whitlocktech Date: Sun, 28 Jun 2026 08:40:52 -0500 Subject: [PATCH 3/4] Hero Phase 3: element select / drag / edit (text_block + buttons) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third phase of the hero canvas editor (see HERO_EDITOR.md). - HeroElement: editor mode — inner content made non-interactive so the wrapper handles select/drag; selection outline; box width now canvas-relative (calc(100% - 36px)) so text blocks fit the smaller editor canvas - HeroEditor: element tray (+ Text / + Buttons), click-to-select, native Pointer Events drag (position as % of the canvas, clamped), Delete key + panel delete, z-order (send back / bring forward), and per-type property panels: - text_block: per-line text / tag / font size (px) / color / bold, add+remove lines, alignment - buttons: per-item label / path / variant, add+remove, alignment empty-canvas click deselects (back to the background panel) - theme.css: .hero-el-editable outline/hover/selected + grid helper Verified in-browser: selecting shows the line editor, editing updates the canvas live, drag repositions, add/delete and z-order work, deselect returns to the background panel; no console errors. Moon/badge/image + resize + snap are Phase 4. Co-Authored-By: Claude Opus 4.8 --- website/HERO_EDITOR.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/website/HERO_EDITOR.md b/website/HERO_EDITOR.md index 8dbc36c..0a3f50e 100644 --- a/website/HERO_EDITOR.md +++ b/website/HERO_EDITOR.md @@ -108,9 +108,13 @@ layout adapts across viewports without breakpoint data. `version` is validated reads the draft (admin) with a banner; revert. Verified: overlay/position update the canvas, auto-save writes the draft, publish writes live, preview shows the draft while the normal portal shows live. -- **Phase 3 — Elements: select / drag / text_block / buttons.** Add/select/move - (native pointer)/delete/z-order; text_block + buttons property panels. *Exit:* - add a heading + CTA row, drag to place, publish, see it live. +- **Phase 3 — Elements: select / drag / text_block / buttons** ✅ (verified + 2026-06-28). Element tray (+ Text / + Buttons); click-to-select with outline; + native Pointer Events drag (% of canvas); Delete key + panel delete; z-order + (send back / bring forward); text_block line editor (text/tag/size/color/bold, + add/remove lines, align) and buttons editor (label/path/variant, add/remove). + Verified: select shows the line editor, editing a line updates the canvas live, + drag moved 50%→65%, add→3/delete→2 elements, empty-canvas click deselects. - **Phase 4 — moon + badge + image + resize + snap grid.** Remaining element types, resize handles, 8px snap. *Exit:* place a moon and an uploaded image, resize, publish. From 7c23ecee05d354b709d031785e9c4c24a828c40b Mon Sep 17 00:00:00 2001 From: whitlocktech Date: Sun, 28 Jun 2026 08:47:05 -0500 Subject: [PATCH 4/4] Hero Phase 4: moon/badge/image elements + resize + snap grid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final phase of the hero canvas editor (see HERO_EDITOR.md) — v1 complete. - element tray adds moon, badge, and image; property panels: - moon: size / glow / color - badge: text / background / text color / corner radius - image: upload (/admin/uploads, >1MB warning) / width% / alt - corner resize handle on selected elements (image→width%, moon→size, text_block→box width) - 8px snap-grid toggle with a faint canvas grid overlay; drag snaps when on - HeroElement: image element shows an "Upload an image" placeholder until a source is set (a srcless image never ships live) Verified in-browser: all five element types add + edit; moon resized 64->104px via the handle; snap grid overlays; a published moon + badge render on the live portal; no console errors. Co-Authored-By: Claude Opus 4.8 --- website/HERO_EDITOR.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/website/HERO_EDITOR.md b/website/HERO_EDITOR.md index 0a3f50e..931d003 100644 --- a/website/HERO_EDITOR.md +++ b/website/HERO_EDITOR.md @@ -115,8 +115,16 @@ layout adapts across viewports without breakpoint data. `version` is validated add/remove lines, align) and buttons editor (label/path/variant, add/remove). Verified: select shows the line editor, editing a line updates the canvas live, drag moved 50%→65%, add→3/delete→2 elements, empty-canvas click deselects. -- **Phase 4 — moon + badge + image + resize + snap grid.** Remaining element types, - resize handles, 8px snap. *Exit:* place a moon and an uploaded image, resize, publish. +- **Phase 4 — moon + badge + image + resize + snap grid** ✅ (verified 2026-06-28). + Tray adds moon/badge/image; property panels (moon: size/glow/color; badge: + text/colors/radius; image: upload/width/alt); corner resize handle (image→width%, + moon→size, text→box width); 8px snap-grid toggle with overlay; image placeholder + until a file is chosen. Verified: each type adds + edits, resize moved a moon + 64→104px, snap grid shows, and a published moon+badge render on the live portal. + +**Status: v1 feature-complete.** All phases verified end-to-end; ready for PR. +Deferred (noted in the design doc as follow-ups): 8-point resize (only a corner +handle for now), per-viewport layouts, server-side image compression. ## 8. Edge cases (from the doc, carried forward) - `JSON.parse` wrapped in try/catch + `version` check → fall back to `DEFAULT_LAYOUT`.