multisite fieldtype: wraps one child field, stores {sitehandle: value}, augments to the current site's value (optional inheritance). multisiteswitcher: runtime-only site toggle, stores nothing.
php artisan boost:add-skill daun/statamic-multisite-field
Save this content to: AGENTS.md
# AGENTS.md
`multisite` fieldtype: wraps one child field, stores `{site_handle: value}`,
augments to the current site's value (optional inheritance). `multisite_switcher`:
runtime-only site toggle, stores nothing.
## Commands
```bash
composer test && composer lint && composer analyse # Pest, Pint, PHPStan
pnpm test # Vitest (tests/js/)
pnpm build # resources/dist is committed — rebuild after JS changes
```
## Invariants
- **Storage**: `{site: rawChildValue}`, no inner-handle level. Empty values are
pruned on save; a missing key means "inherit". In single-site mode values are
stored flat (no pruning); `normalizeValue()` handles both shapes on read.
- **Meta contract** (PHP `preload()` ↔ `MultisiteFieldtype.vue`): change both
sides together. `fields.<site>`/`new` are the child's own meta; Vue wraps them
as `{[innerHandle]: meta}`.
- **Child renders in an isolated `PublishContainer`** per site, not the ambient
one (site-keyed shape doesn't match ambient paths). Visited sites stay mounted
behind `v-show`; remounting kills editor performance.
- **Props passed to `PublishContainer` must be referentially stable** (cached
computeds). It re-emits on identity change, so fresh objects loop forever.
- **Active site is a shared singleton** (`resources/js/activeSite.js`), never
field meta, so switching doesn't dirty the form.
- **Validation/error keys**: PHP uses `{handle}.{site}[.{rest}]`; Vue remaps
ambient errors to `<innerHandle>[.<rest>]`. Keep the two in sync.
- **`placeholder` is display-only.** It never enters augmentation, stored data or
validation. Resolution order: site value, then default site (if `inherit`),
then the sibling `fallback` field.
- **Wrapped field actions** come from `field.$.refs.fieldtype`. `setupState` is
empty in production builds; the test stub in `tests/js/setup.js` mimics prod.
- **Addon CSS is plain CSS, not Tailwind.** `resources/css/addon.css` holds the
few custom rules. Tailwind classes in templates must already exist in core CP CSS:
`grep -c '\.CLASS' vendor/statamic/cms/resources/dist/build/assets/*.css`.
- Site labels are resolved in PHP (`SiteLabels`, config file). Vue uses
`siteLabel()`/`siteShortLabel()`, not `site.name`.
## Gotchas
- Vitest needs its own `vitest.config.js` (plain vue plugin); the prod Vite
config rewrites `vue`/`@statamic` imports.
- Verify assumed Statamic core APIs in `vendor/` before coding against them.
multisite fieldtype: wraps one child field, stores {site_handle: value},
augments to the current site's value (optional inheritance). multisite_switcher:
runtime-only site toggle, stores nothing.
composer test && composer lint && composer analyse # Pest, Pint, PHPStan
pnpm test # Vitest (tests/js/)
pnpm build # resources/dist is committed — rebuild after JS changes
{site: rawChildValue}, no inner-handle level. Empty values are
pruned on save; a missing key means "inherit". In single-site mode values are
stored flat (no pruning); normalizeValue() handles both shapes on read.preload() ↔ MultisiteFieldtype.vue): change both
sides together. fields.<site>/new are the child's own meta; Vue wraps them
as {[innerHandle]: meta}.PublishContainer per site, not the ambient
one (site-keyed shape doesn't match ambient paths). Visited sites stay mounted
behind v-show; remounting kills editor performance.PublishContainer must be referentially stable (cached
computeds). It re-emits on identity change, so fresh objects loop forever.resources/js/activeSite.js), never
field meta, so switching doesn't dirty the form.{handle}.{site}[.{rest}]; Vue remaps
ambient errors to <innerHandle>[.<rest>]. Keep the two in sync.placeholder is display-only. It never enters augmentation, stored data or
validation. Resolution order: site value, then default site (if inherit),
then the sibling fallback field.field.$.refs.fieldtype. setupState is
empty in production builds; the test stub in tests/js/setup.js mimics prod.resources/css/addon.css holds the
few custom rules. Tailwind classes in templates must already exist in core CP CSS:
grep -c '\.CLASS' vendor/statamic/cms/resources/dist/build/assets/*.css.SiteLabels, config file). Vue uses
siteLabel()/siteShortLabel(), not site.name.vitest.config.js (plain vue plugin); the prod Vite
config rewrites vue/@statamic imports.vendor/ before coding against them.How can I help you explore Laravel packages today?