Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ docs/
│ ├── plugin-system.md ← plugin SDK, sandbox, lifecycle, permissions
│ ├── publisher.md ← page tree → static HTML/CSS pipeline
│ ├── visual-components.md ← VCs, slots, params, instantiation
│ ├── localization.md ← languages, translations and per-language publication
│ ├── content-storage.md ← data_tables + data_rows (the universal store)
│ ├── content-workspace.md ← Content workspace: collections, entries, body editor
│ ├── auth-and-access.md ← sessions, MFA, capabilities, roles
Expand Down Expand Up @@ -140,6 +141,7 @@ Three categories, three voices:
| [features/publisher.md](features/publisher.md) | The page-tree-to-HTML/CSS renderer + server-side publishing wrappers |
| [features/visual-components.md](features/visual-components.md) | VCs, slots, params, instantiation, recursion guard |
| [features/content-storage.md](features/content-storage.md) | `data_tables` + `data_rows` — the universal content store |
| [features/localization.md](features/localization.md) | Languages, inheritance, translation review and independent publication |
| [features/content-workspace.md](features/content-workspace.md) | Content workspace UI: collections, entries, body editor, settings panel |
| [features/data-workspace.md](features/data-workspace.md) | Data workspace UI: DataInspector, field management, DataGrid |
| [features/auth-and-access.md](features/auth-and-access.md) | Sessions, MFA, step-up, lockout, CSRF, capabilities |
Expand Down
4 changes: 4 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,10 @@ bun run test:e2e # run specs in tests/e2e/*.e2e.ts

---

## Localization

Logical content identities share structure while per-language records own draft values, availability, publication history and schedules. Site and Content authoring, collaboration, public routes and integrations carry an explicit locale. See [`localization.md`](features/localization.md) for the storage model and lifecycle.

## Related

- `CLAUDE.md` — the agent rule book (start there before changing code)
Expand Down
11 changes: 8 additions & 3 deletions docs/editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,13 +603,14 @@ The sidebar shell expands/collapses by animating `--*-panel-width`. The panel sl

`src/admin/modals/Settings/SettingsModal.tsx`. Shares the visual language of the Spotlight palette and Module Inserter: a direct-token panel shell, `--bg-surface-2` rail with categorical accent icon chips, accent-bar section header, card-group rows (`--bg-surface-2` fills, `--panel-radius` corners, 1px gaps showing the darker panel surface through) for section content, and an Esc keycap affordance. Backdrop click and Esc both close — there is no dedicated close button.

**Sections** (rail nav, four entries):
**Sections** (rail nav):

| Section | What it contains |
|---------------|------------------------------------------------------------------------------|
| General | Site name, meta title, meta description, language, favicon |
| General | Site name, meta title, meta description, favicon |
| Languages | Source and translation languages, URL prefixes, direction and availability |
| Shortcuts | Auto-rendered keyboard shortcut reference from the keybindings registry |
| Publishing | Self-hosted runtime info + framework CSS tree-shaking toggle |
| Publishing | Public website URL, self-hosted runtime info and framework CSS tree-shaking toggle |
| Preferences | Catalog-driven editor preferences (auto-rendered from `PREFERENCE_CATALOG`) |

Site-specific controls that were previously sections of this modal (Pages roster, Breakpoints/Viewports, Conditions) now live in their dedicated surfaces: the Site Explorer panel and `CanvasContextSelector` (unified condition axis).
Expand Down Expand Up @@ -721,6 +722,10 @@ See [docs/features/plugin-system.md](features/plugin-system.md) for the plugin S

---

## Localization

Logical content identities share structure while per-language records own draft values, availability, publication history and schedules. Site and Content authoring, collaboration, public routes and integrations carry an explicit locale. See [`localization.md`](features/localization.md) for the storage model and lifecycle.

## Related

- [docs/architecture.md](architecture.md) — system overview
Expand Down
4 changes: 4 additions & 0 deletions docs/features/content-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,10 @@ Events are emitted from `server/publish/contentEvents.ts`, which also exports `a

---

## Localization

Logical content identities share structure while per-language records own draft values, availability, publication history and schedules. Site and Content authoring, collaboration, public routes and integrations carry an explicit locale. See [`localization.md`](localization.md) for the storage model and lifecycle.

## Related

- [docs/architecture.md](../architecture.md) — system overview ("All content lives in `data_tables` + `data_rows`")
Expand Down
20 changes: 17 additions & 3 deletions docs/features/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,12 @@ The dashboard fans out into **per-domain** endpoints under `/admin/api/cms/dashb

| Endpoint | Hook | Capability gate | Response shape (summary) |
|-----------------------------|--------------------------|-----------------|--------------------------|
| `/dashboard/pages` | `usePagesStats` | authenticated user | `{ total, published, drafts, scheduled, deltaPublishedThisWeek }` |
| `/dashboard/posts` | `usePostsStats` | authenticated user | `{ total, categories, scheduled, daily28 }` |
| `/dashboard/pages` | `usePagesStats` | authenticated user | `{ total, variants, published, drafts, offline, scheduled, deltaPublishedThisWeek }` |
| `/dashboard/posts` | `usePostsStats` | authenticated user | `{ total, variants, categories, scheduled, daily28 }` |
| `/dashboard/media` | `useMediaStats` | `media.read` | `{ count, totalBytes, latestThumbs[] }` |
| `/dashboard/plugins` | `usePluginsStats` | `plugins.read` | `{ total, active, disabled, errored, rows[] }` |
| `/dashboard/storage` | `useStorageStats` | authenticated user | `{ imageBytes, videoBytes, documentBytes, pluginBytes, databaseBytes, totalBytes, dialect }` |
| `/dashboard/publish-lineup` | `usePublishLineupStats` | authenticated user | `{ rows: [{ id, path, status, at }] }` |
| `/dashboard/publish-lineup` | `usePublishLineupStats` | authenticated user | `{ rows: [{ id, localeId, localeCode, localeEnabled, title, path, status, at }] }` |
| `/dashboard/activity` | `useRecentActivityStats` | `audit.read` | `{ rows: [{ id, action, actor, targetCode, targetText, createdAt }] }` |

Non-CMS first-party widgets:
Expand All @@ -238,6 +238,20 @@ Non-CMS first-party widgets:
| `domain` | Local component rows | Shows the current placeholder primary-domain / HTTPS rows. |
| `status` | Local component rows | Shows the current placeholder site/build/backup/plugin status rows. |

### Language-aware content statistics

The Pages and Posts headline totals count logical content identities. `variants` counts authored language variants; published, draft, offline, and schedule counters describe those variants. A published variant can also have a frozen scheduled update. Disabled languages contribute no publicly online variants, while their paused schedules remain visible. The weekly delta and daily histogram count immutable locale publication versions, including versions superseded by later releases.

The lineup carries row identity plus locale identity. Published paths come from the active version; scheduled paths come from the frozen scheduled revision. Draft paths use localized collection routes and the language prefix. A page named `index` displays the language homepage, while unfinished draft paths display their title. Templates and generic data rows are excluded from the route lineup. Activity resolves content against the event's locale, using the source language for older events without locale metadata.

The shared TypeBox contracts live in `src/core/dashboard/contentStatsSchemas.ts`; server readers and client hooks consume the same definitions. `server/handlers/cms/dashboard/__tests__/localizedDashboard.test.ts` covers logical-versus-variant counts, disabled languages, concurrent live/scheduled states, frozen URLs, and locale activity titles.

### Publishing from the dashboard

The **Publish pages…** action opens the shared `SitePublishDialog` (`src/admin/shared/SitePublishDialog/`). Authors explicitly select page-language variants; disabled languages cannot be selected. `DashboardPublishButton` gates the action on `pages.publish`, runs the same step-up flow as the Site toolbar, and submits the selected `{rowId, localeId}` pairs. A successful publication emits `CMS_PUBLICATION_CHANGED_EVENT`; dashboard hooks refresh independently on that event and on `CMS_SITE_RELOAD_EVENT`. Failures keep the selection open and surface through the global toast bus. The Site toolbar keeps its additional collaboration-sync and runtime-validation checks.

The lineup displays the server-provided title, language, status, and frozen address. A live version and its scheduled update have separate entries keyed by row, language, and status. `src/admin/pages/dashboard/components/DashboardPublishButton.test.tsx` covers explicit selection, disabled languages, capabilities, step-up cancellation, and failure handling; `src/admin/pages/dashboard/widgets/localizedWidgets.test.tsx` covers variant labels, frozen paths, and event-driven refresh.

### Timezone-aware day bucketing

Every dashboard stats request includes a `?tz=<IANA>` query parameter (`Intl.DateTimeFormat().resolvedOptions().timeZone` from the viewer's browser). The server reads it in `handleDashboardRoutes` via `resolveTimeZone` (`server/time.ts`) and threads the resolved zone into `DashboardRequestContext.timeZone`. Readers that bin timestamps per calendar day — currently the Posts histogram — use `localDayKeyFactory(ctx.timeZone)` to map each `published_at` to a local day key rather than the UTC date. A post published at 23:30 local time lands on the correct bar instead of rolling into the next UTC day.
Expand Down
Loading