# Content and widget editing

CMS pages and blog posts, and widget configuration, use shared editor controls and workflow presentation. Their field definitions, validation, identities, and publication services remain separate. Provider-specific setup remains outside the shared field renderer.

## Client access

Assign **Client editor** for each website that an administrator may edit. A website manager controls the website's client publication mode under **Website settings → Permissions**:

| Mode | Client workflow | Publication authority |
| --- | --- | --- |
| Draft and review (default) | Edit content, save a private draft, inspect history, and upload associated media. | A manager reviews and publishes the content. |
| Direct content publishing | Edit and save drafts; explicitly publish or unpublish CMS content. | The assigned client may publish content for this website. |

Changing this setting does not grant access to another website. Schema changes, widget design, integrations, credentials, API tokens, site builds, deployment, and access management retain their existing manager/owner boundaries. Content publishing never builds or deploys a site automatically through the editor; existing configured publication webhooks retain their behavior.

Permissions are enforced by the server before mutation, including direct POST requests, page-section operations, batch operations, and publication history actions. Hiding a button is only the interface projection of that authority. Existing moderator and viewer roles retain their previous behavior.

## Drafts and live content

**Save draft** updates private work. A published entry keeps its live content, slug, title, route, and collection position until an authorized person explicitly publishes a new revision. The published URL remains reserved even if its draft uses another slug. Publishing the new slug releases the previous URL.

Collection API summaries and pagination use publication timestamps and published metadata. A private edit cannot reorder public pages or alter a collection release's published slug. API field names and cursor encoding stay compatible; `updated_at` in a published collection represents the publication time. Draft previews continue to show draft metadata.

For a configured native website, **Preview saved draft** uses the existing authenticated page renderer for the selected page or blog post. Previewing neither publishes content nor builds a deployable artifact. Externally generated website previews remain local or in CI.

Inspect history to compare revisions. Restoring editorial history creates a new private draft. Restoring a historical publication also copies it into a private draft. Publishing or unpublishing that draft remains a separate operation requiring publication authority.

Media can be shared by published consumers. Changes to existing media that can alter live output require the authority of those consumers; a CMS content publication grant does not grant widget or design authority. Upload a new asset and select it in a draft when preparing media changes for review.

An image in a reusable fragment remains in use while referenced by the fragment's draft, its live revision, or an entry pinned to that revision. Removing a fragment placement from a page draft preserves the placement used by its live publication. Media deletion rechecks these retained revisions even when an older installation has incomplete dependency rows.

Shared-media copies receive the same protection as directly selected website media. An unresolved conflict branch does not become a live dependency merely because it shares a revision number with a published revision.

## Shared component contract

Feature adapters derive their fields, choices, bounds, and normalization from the existing CMS schemas or widget definitions. Shared editor presentation handles navigation, field grouping, validation, status, preview controls, and actions. It does not supply a second content model or a duplicate validation schema.

Forms remain server-rendered. JavaScript enhances field interaction and validation while native form actions remain available. Provider setup, provider retention and attribution, and executable website templates remain feature-owned.

When a field fails browser validation, the shared editor lists its label and validation message. Use its message button to focus the field with the mouse or keyboard. Errors remain listed until their fields are corrected; editing an unrelated field does not dismiss them. Existing server errors remain visible. Field errors preserve existing accessibility descriptions and are removed when the editor is unmounted. Browser messages use existing native constraints; the server remains authoritative for CMS and widget validation.

The supported local Astro workflow is a separate roadmap milestone. This package does not introduce an Astro visual editor or change local workspace `push`, build, or deployment semantics.

## Media picker search supersession

The shared media picker advances its request sequence immediately when search input changes, before the debounce delay. Aborting fetch alone cannot suppress a response that has already reached asynchronous decoding. Older success/error completions must not replace results or clear the busy state while the replacement search waits to start.

The shared-editor browser contract now holds the debounce timer and releases an earlier media-page success or failure for both CMS and widget form contexts. It verifies stale output is ignored, the prior signal is aborted, busy state survives and the latest query completes normally. These are controlled async-response cases using the real picker UI, not new HTTP authority tests.

All 162 checks pass across Chromium, Firefox and WebKit with exit 0. The 12 new cases failed before the fix. The expanded harness's expected count was corrected from 150 to 162; an intermediate run had all 162 behavioral checks pass but failed that old count assertion. Syntax, whitespace and zero-debt JavaScript complexity pass. Evidence: coordinator `media-debounce-before.log`, `media-debounce-after-before-count-fix.log` and `media-debounce-after.log`.

### Paging while a new media search is pending

Search input also clears the previous search's cursors, disables Newer/Older immediately and marks the picker busy during debounce. A stale paging click cannot combine the new query with an old cursor. The first request for the new search uses an empty cursor; the response supplies fresh paging controls. Existing visible results remain until normal replacement/error handling.

Run `node tests/Browser/media-picker-search.mjs` for 24 focused checks across all three engines. The complete shared-editor runner includes the same cases and passes 168/168 with exit 0. Six new paging cases failed before correction; they cover disabled controls, direct event dispatch, no stale-cursor request, first-page query dispatch and recovered paging. JavaScript syntax, zero-debt complexity and whitespace pass. Evidence: coordinator `media-cursors-before.log`, `media-cursors-after.log`, and `media-cursors-editor.log`. These remain UI/controlled-response tests; server cursor validation is unchanged.

### Retry belongs to its failed search

A retry button now belongs to the request sequence that produced the error. Search changes disable that old action; even a dispatched click cannot resend its captured cursor. Closing the picker also invalidates retry actions. A valid retry disables its button immediately and advances the request sequence, preventing duplicate requests while preserving the original query/cursor for that retry.

Six additional cases reproduce the stale-retry issue before correction. The focused runner passes 24/24; the complete editor contract passes 174/174 across all three engines, both exit 0. Tests include successful current-query retry and duplicate-click suppression. Syntax, zero-debt complexity and whitespace pass. Evidence: coordinator `media-retry-before.log`, `media-retry-after.log`, and `media-retry-editor.log`.

## Draft review, offline proposals and website builds

Client editing in the CMS saves drafts. In draft-and-review mode, an authorized manager reviews and publishes that content through the CMS. In direct-content mode, the assigned client may explicitly publish CMS content. Neither workflow requires creating an offline workspace proposal.

Offline proposals are a separate workflow: staging submits resource changes for an authorized reviewer to inspect and explicitly apply. Applying follows the proposed operations and their publication effects; it is not a synonym for publishing every CMS draft. Existing explicit-apply `push` remains compatible.

Media uploaded through an offline workspace uses the proposal upload/recovery workflow. Ordinary CMS media uploads do not require an offline proposal. In either path, changes to existing media that affect live output require the corresponding consumer publication authority.

For Astro, use the generated project's `npm run dev` or `npm run build` locally or in CI. Its loader reads validated local exports; `export-content` produces content data, not an Astro site. The workspace CLI's `site-build` command builds a native PHP-theme preview and does not compile Astro source. Building and deploying remain separate operations. There is no visual Astro editor.

See [Offline content projects](offline-projects.md) for proposal commands and the [Astro package guide](../packages/astro-workspace/README.md) for local development/build commands.

## Choosing a native theme version

Managers choose native themes under **Site publishing → Website theme**. A theme with multiple bundled versions has a separate **Version** selector. Starter offers retained 1.1.0 and the explicit 1.2.0 upgrade. **Default version (1.1.0)** keeps the existing default; ordinary saves do not silently upgrade older websites. Other themes display their own bundled version.

Choose **1.2.0** to save an exact Starter upgrade choice. Subsequent ordinary saves preserve it. Choose an exact older version to retain an explicit older pin, or choose **Default version** to deliberately clear the pin and use that theme's default. Selecting another theme does not carry the previous theme's pin across. Validation-error retries preserve the submitted per-theme choice.

Use the saved-draft preview to review the result. Saving a theme choice does not build or deploy a website; native release, build and deployment remain separate actions. Appearance still controls colors, mode, corner style and custom CSS. Client content-editor access does not grant theme-selection authority.

Developer compatibility and verification limits are recorded in [Native theme-version policy](native-theme-version-policy.md) and [Retained native themes](native-theme-versions.md). The retained native-theme acceptance records include three-engine browser checks and database-backed starter installation and v5/v6 release reconstruction (3 tests / 251 assertions at `2d011a8499f9c8247759417865ff11b9955da4f0`); see [Bilingual native theme release acceptance](native-bilingual-release-acceptance.md). These are historical, source-bound results, not fresh qualification of the current refactor. Current supported-platform, migration and installable-archive qualification remains open under the [foundation runbook](foundation-audit-refactor-plan.md).
