---
id: fragment.editor-palette.001
freshvibe_way_version: v8
date: 2026-07-31 (Phase 7 — page builder, per oscar-website-mavis operator directive 2026-07-31 12:32 BST)
last_rewritten: 2026-07-31 (new fragment, Phase 7)
---

# editor-palette — the module palette (Phase 7 page-builder manual path)

## What it does

When the operator opens the palette panel (from the context menu on a group, or from the "+ Add" affordance on an empty group), they see:

- A search input at the top (filters the 16 canonical modules by label or id)
- A grid of module cards, one per canonical module, with: label, id, description
- A footer that points to the AI path ("Or use the AI composer → add 3 book cards to this group")

Clicking a card inserts the module into the active group. The default config is built from the module's schema (every field with a `default` is set). The store write goes through `Store.createModule` with a `group` scope, which `assertOp` validates.

After insert, the chrome:
- Refreshes the outline + navigator (so the new module shows up in the structure tree)
- Opens the inspector for the new module (so the operator can immediately edit fields)
- Shows a "✓ added" confirmation in the palette (3s timeout)

## Why this is its own feature (cluster rule §3.9)

The palette is a cluster of capabilities:
1. **Render the 16 canonical modules** — reads from `modules/index.js`, the source of truth for the module library
2. **Search + filter** — operator typing reduces the list live
3. **Build default config from schema** — every field's default is set so the module renders with reasonable values immediately
4. **Insert via store** — calls `Store.createModule` with scope guards; this is the same function the AI composer calls
5. **Open the inspector after insert** — operator is one click from editing fields
6. **Show the AI path** — the footer reminds the operator that AI reimagine is also available, never hidden

Removing any one of these breaks the page-builder manual path.

## Inputs (public API)

- `renderPalette(container, { groupId, regionId?, pageId?, onInsert? })` — render the palette UI in an existing element. The `container` is the panel body (not the panel itself). Returns nothing; mutates the container in place.
- `insertCanonicalModule(groupId, moduleType, config?, scope?)` — insert a module programmatically. Used by the AI composer + the layout-templates fragment. NOT used by the palette UI directly (the palette calls `Store.createModule` so it can also fire the onInsert callback).

## What this fragment does NOT do

- Does NOT edit a module after insert — that's the editor-inspector fragment's job
- Does NOT remove a module — that's the editor-context-menu fragment's job (right-click → Delete)
- Does NOT create groups or regions — that's the editor-region-actions fragment's job
- Does NOT decide which modules are canonical — that's `modules/index.js`'s job
- Does NOT do AI reimagine — that's a separate fragment (`app-fragments/ai-composer/`, Phase 4 work, NOT in this PR)

## Manual + AI paths (per pact §3.11)

This fragment is the **manual path** for adding modules. The AI path is the AI composer fragment (Phase 4, not yet built). Both call `Store.createModule`, so the write is identical — only the trigger differs (click vs natural-language command).

The footer of the palette UI explicitly mentions the AI path so the operator never wonders "is there an AI way to do this?".

## Why this is in scope for Phase 7 (per the plan v2, artifact 413)

The plan v2 §Phase 7 lists:
- Drag-to-add region/module
- Region create/delete/rename
- Module insertion from a palette of 17 canonical modules
- Layout templates

The palette covers "module insertion from a palette". The other items are separate fragments (region-actions + layout-templates). Phase 7 splits the page-builder layer into 3 small fragments instead of 1 big one — each is testable on its own, each can ship independently.

## Reflexes for any future Mavis session

- See a "where do I add a module?" question → it's this fragment.
- See a "let AI add a module" question → it's the AI composer (Phase 4) calling `insertCanonicalModule` from this fragment.
- Want to add a 17th canonical module → don't touch this fragment. Add the module to `modules/index.js`. The palette picks it up automatically.
- Want to add a 5th layout template → don't touch this fragment. That's the layout-templates fragment's job.

## Provenance

- Created 2026-07-31 12:38 BST by oscar-website-mavis (session 415655444668635)
- Per operator directive 2026-07-31 12:32 BST "do 2 then tell hopfan"
- Per cross-thread agreement (bulletin b221, 2026-07-31) with Hopfan App (id 22): oscar-website owns Phase 7 (page builder), Hopfan App owns Phase 8 (CMS admin) + canonical-pipeline.md
- Per pact §3.11 (manual+AI both, operator-locked 2026-07-31) and §3.14 (store is source of truth)
- Depends on: `Store.createModule` (added to `runtime/store.js` in this PR) and `assertOp` from `runtime/scope.js` (pre-existing)
