# Theme Library

The admin theme workflow is **Theme Library -> Customize -> Preview -> Apply**. It replaces the generic theme-record editing habit with one owner/admin flow for installed theme instances, catalogue/local definitions, pending installs, and diagnostics.

![Theme Library admin workflow](../images/generated/admin/theme-library-admin-flow.png)

## What The Page Shows

| Section          | Source                                                                          | What admins do                                                                                           |
| ---------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Installed themes | `themes` records plus registered package definitions                            | Customize, preview, and apply a theme instance.                                                          |
| Available themes | registered `ThemeDefinitionData` entries without a matching `themes.key` record | Create the missing installed theme instance from the unified **Add theme** slide-over.                   |
| Pending installs | Marketplace install intents when the Marketplace table exists                   | See that a theme install is still waiting for Composer/deployment work.                                  |
| Diagnostics      | `ValidateThemeDefinitionAction` output                                          | Find missing renderer, preset, section, asset, preview image, and parent-theme problems before applying. |

The installed themes table is a normal Filament table with compact columns for preview, name/description, active preset, site usage, status, diagnostics, and actions. Diagnostics are shown as a clickable badge; errors block **Apply theme** but do not expand noisy details into the row.

Theme table images prefer package definition data. The admin card image fallback order is:

1. theme media image
2. `admin.editor.image`
3. generated admin image
4. `ThemeDefinitionData::previewImage`
5. initials fallback

## Add Themes

Use **Add theme** for all new theme entry points:

- **Add my own theme** creates a custom installed theme record.
- **Create from installed package** creates a theme from a registered package/local `ThemeDefinitionData`.
- **Search marketplace** is reserved for Marketplace installs when that package is present.

Package definition creation uses the definition name, assets, and first preset. The first preset is saved as `meta.editor.preset.active`, so the theme can be customized, previewed, and applied immediately after creation. Creating a theme does not make it the global default; admins still use **Apply theme** for activation. Definitions with diagnostics errors stay blocked until the missing parent, renderer, or preset problem is fixed.

## Customize

Use **Customize** on an installed theme. The slide-over is a guided editor with sections on the left and a sticky sandboxed iframe preview on the right. The preview is built from unsaved form state and only persists when the admin explicitly saves.

Core groups:

- Quick setup: active preset plus admin editor metadata in `admin.editor.*`.
- Brand: `meta.editor.brand.*`.
- Header: `meta.editor.header.*`.
- Page surface: `meta.editor.surface.*`.
- Footer: `meta.editor.footer.*`.
- Assets: `meta.editor.assets.paths` and `meta.editor.assets.buildPath`.
- Advanced: `meta.editor.advanced.*`, including sandboxed custom CSS.

Do not expose blueprint/default/status as the main workflow. They can still exist in advanced/admin areas for maintainers who need them.

## Preview

Preview asks for a site, page, and preset. It does not silently pick the first default page.

The preview URL is generated by `CreateThemePreviewUrlAction` and signed through the normal admin preview route. When a preset is selected, the URL also carries a `ThemePreviewSigner` token. The token resolves inside theme runtime only for the preview request, so the selected preset can be checked before saving/applying it broadly.

Preview is an authenticated admin surface:

- unsigned requests are rejected
- non-admin users are rejected
- site-scoped admins can preview only assigned sites
- responses are private/no-store
- authoring metadata is not printed into the preview HTML

## Apply

**Apply theme** uses `SetActiveThemeForSitesAction`.

| Scope          | Effect                                                             | Cache behavior                                                                               |
| -------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| Global default | marks the selected theme active and default, clears other defaults | `ThemeObserver` invalidates frontend surrogate keys for affected sites.                      |
| Selected sites | saves `theme_id` on each selected `Site` model                     | `SiteObserver` flushes site/theme relation caches and emits frontend surrogate invalidation. |

The Filament action re-checks scope server-side. Site-scoped admins cannot submit a tampered request to set the global default or apply a theme to unassigned sites.

![Customize, preview, and apply flow](../images/generated/admin/theme-customize-preview-apply.png)

## Diagnostics

Run diagnostics from the UI or the command line:

```bash
php artisan capell:themes:validate
php artisan capell:themes:validate foundation
```

Diagnostics check:

- package definition registration
- installed `themes.key` match
- `themeKey` consistency
- renderer registration
- parent/extends chain
- standard section coverage
- declared frontend assets
- preset availability
- preview image metadata

Warnings mean the theme may still be previewable, but the package author should fix the manifest/registration before release. Errors mean the theme should not be applied until the missing definition or renderer contract is fixed.

## Public Safety

Theme library changes must not change the public-output safety contract. Anonymous/public HTML must not include admin labels, package internals, field paths, model IDs, signed editor URLs, or authoring scripts.

Runtime reads the selected theme, active preset, brand profile, and package definition server-side. Public output receives ordinary frontend HTML and token CSS only.