77 lines
4.1 KiB
Markdown
77 lines
4.1 KiB
Markdown
# Theme Builder -- editor UI
|
|
|
|
This directory documents the two-stage plan for the in-browser theme editor.
|
|
The editor is mounted (admin-only) at `GET /theme-builder/editor`, whose shell
|
|
page (`lib/page.js`, ARCHITECTURE.md 8.2) loads a single static bundle served by
|
|
Saltcorn's built-in plugin static route at
|
|
`/plugins/public/theme-builder/builderApp.js` (no extra route -- plugins.js
|
|
1246-1276).
|
|
|
|
## Phase 1 -- the shipping editor (`public/builderApp.js`)
|
|
|
|
`public/builderApp.js` is the **buildless, dependency-free Phase-1 editor**. It
|
|
is hand-written **vanilla ES2020** -- no React, no Craft.js, no build step, no
|
|
imports. It IS the committed, shipped artifact: an installed plugin works
|
|
without ever running a bundler.
|
|
|
|
What it does (talking only to the REST API under `window.__TB__.apiBase`):
|
|
|
|
- **Manager panel** -- `GET /api/state` lists every theme with `active` and
|
|
`built-in` badges (ARCHITECTURE.md 8.5/8.8). Per-row buttons: Activate, Edit,
|
|
Duplicate, Rename, Delete, Export (plain `<a download>` to
|
|
`/api/themes/:id/export`). Top toolbar: New, Import (file input -> multipart
|
|
`POST /api/import`).
|
|
- **Token panel** -- color / font / spacing inputs rendered **from the manifest**
|
|
returned by `/api/state` (`manifest.tokens[<kebabKey>] = { kind, cssVar,
|
|
selector, prop, derive, default }`, from `lib/apiState.buildManifest`).
|
|
- **Live preview** -- a sandboxed `<iframe>` (`sandbox="allow-same-origin"`, no
|
|
scripts) loads the live `theme.css` plus a representative sample page with an
|
|
empty `<style id="tb-overlay">`. On every edit the editor rewrites that style
|
|
with `--bs-*` custom properties (and the rule-based `selector{prop:value}`
|
|
tokens) -- the **exact same overlay mechanism production uses on activate**
|
|
(`lib/compile.emitOverlayCss`). WYSIWYG with zero compile and zero network.
|
|
|
|
### The cardinal rule: load != activate
|
|
|
|
Loading and editing a theme **never** touches the live site. **Save** (`POST
|
|
/api/themes/:id/save` with `{ tokens, layoutTree, baseVersion }`) and
|
|
**Activate** (`POST /api/themes/:id/activate`) are wired as **separate
|
|
actions**; only Activate publishes (ARCHITECTURE.md 1.4 / 8.3). Save uses
|
|
optimistic concurrency on `baseVersion`; a 409 `version_conflict` offers
|
|
reload-or-overwrite. Editing a built-in is allowed: the first Save transparently
|
|
duplicates it to an editable row, leaving the original untouched.
|
|
|
|
### CSRF
|
|
|
|
The shell (`lib/page.js`) bootstraps
|
|
`window.__TB__ = { apiBase, cssRoute, csrfToken, base, openThemeId }`, reading
|
|
the token via `req.csrfToken()` (core's pattern, e.g. server/wrapper.js:259).
|
|
Every `POST` from the editor sends it back as the **`CSRF-Token`** header
|
|
(plus `X-Requested-With: XMLHttpRequest` and `Accept: application/json` so the
|
|
server treats every request -- including multipart import -- as JSON).
|
|
|
|
## Phase 2 -- the upgrade path (`builder/src`, NOT yet implemented)
|
|
|
|
The Phase-2 editor is a **React + Craft.js** application whose source tree will
|
|
live under **`builder/src`** (ARCHITECTURE.md 8.3, 8.7, 10). It adds a visual
|
|
**canvas panel** (`panel:"canvas"`, enabled only when `caps.phase === 2`) that
|
|
reuses saltcorn-builder's `<Editor>/<Frame>` patterns and the
|
|
`craftToSaltcorn` / `layoutToNodes` round-trip, with `@craftjs/core` and the
|
|
vendored saltcorn-builder element/storage files **bundled directly into the same
|
|
output path** (`public/builderApp.js`) by webpack (`build:ui`).
|
|
|
|
Phase 2 is purely additive and does not change the REST surface or the
|
|
"load != activate" rule:
|
|
|
|
- The token panel, manager panel, and `--bs-*` live-preview overlay carry over
|
|
unchanged; the canvas is a third panel feeding the same single `POST /save`
|
|
(`{ tokens, layoutTree }`).
|
|
- Activate is still just `POST /api/themes/:id/activate`; the server-side engine
|
|
swaps the overlay compiler for a full dart-sass recompile behind the
|
|
`phase2Enabled` config flag (ARCHITECTURE.md 5.7 / 7.5).
|
|
|
|
Until that React tree is built, **`public/builderApp.js` (the buildless vanilla
|
|
file) is the live editor.** When Phase 2 lands, the webpack build overwrites
|
|
`public/builderApp.js` with the compiled React bundle (exposing the same
|
|
`window.themeBuilder.mount` entry point), and this `builder/` directory holds its
|
|
source.
|