Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .changeset/23610-page-isdefault-template-slots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
'@objectstack/spec': minor
'@objectstack/objectql': patch
---

feat(spec)!: a page declares nothing the console does not honour — `isDefault: true` is refused off a record page bound to an object, `template` closes to the registered layouts, and a region the template does not place is refused

Clause-②: yes (narrowing)

**BREAKING (authoring):** two accept sets of `PageSchema` narrow to what the console runs, and the `PageTypeSchema` text stops promising what it does not.

| Writing | Before | Now |
| :--- | :--- | :--- |
| `isDefault: true` on a `home`, `app`, `utility` or `list` page, or on a record page with no `object` | accepted, stored and served; nothing read it | refused at `isDefault` — by `definePage`, `defineStack` (so `os validate` and `os build`, `STACK_SCHEMA_INVALID` 422), the metadata save door (`422 INVALID_METADATA`) and package registration (`SchemaRegistry.installPackage` / `registerItem`, `422 INVALID_METADATA`) |
| `template: 'full-width'` | accepted; drawn exactly as `default` | refused by name: "Page `template: 'full-width'` was removed in @objectstack/spec 18 … Use `template: 'default'`" |
| any other `template` outside `default`, `header-sidebar-main`, `three-column`, `dashboard` (`two-column`, `single-column`, …) | accepted; drawn as the page type's default layout | refused at `template` |
| on a `kind: 'full'` page, a region its template does not place (`left_sidebar`, `right_sidebar`, `content`, …) | accepted; stacked under `main` at full width, its `width` ignored | refused at `regions.N.name`, naming the template's slots and the rename |
| on a `kind: 'full'` page, a second region filling a slot an earlier one fills (two `main` regions; on `dashboard`, a second `header` or `footer`) | accepted; not drawn at all — the console draws the first region of a name and leaves that name out of the rest | refused at `regions.N.name`, naming the region that already fills the slot |

Migration:

- **`isDefault` on a non-record page** → delete it. A page does not choose where an app opens: an app's landing page is its **first navigation item** (by `order`), so to land on the page put its entry first in the app's `navigation`; the root landing follows the app's own `isDefault`. A record page meant as an object's default record page binds that object with `object`.
- **`template: 'full-width'`** → `template: 'default'` (or delete the key — `default` is the default). The page renders exactly as before.
- **A `template` held in a widened `string`** (a shared base object, `{ template: 'default' }` spread into `definePage`) no longer type-checks against the closed set → `template: 'default' as const`.
- **An unregistered template** → one of `default` (header, sidebar, main, aside, footer), `header-sidebar-main` (header, sidebar, main), `three-column` (header, sidebar, main, aside, footer) or `dashboard` (header, footer, and every other region a grid cell).
- **An unplaced region** → name it for the slot it fills: `left_sidebar` → `sidebar`, `right_sidebar` → `aside`, `content` → `main`; or choose `dashboard` when the regions are a grid. Names match case-insensitively, as the console's do. `slotted`, `html` and `react` pages are not judged on region names.
- **A second region for a filled slot** → move its components into the first region of that name, or name it for an unfilled slot. On `dashboard`, two regions of any other one name stay two grid cells and are accepted.

The template set and each template's slots are now published: `PageTemplateSchema`, `PAGE_TEMPLATE_SLOTS` and `PAGE_TEMPLATE_OPEN_GRID` (`@objectstack/spec/ui`), with the two checks `checkPageDefaultPlacement` and `checkPageRegionPlacement` and the one refusal sentence `misplacedDefaultPageRefusal` that the parse and the registry share.

`os migrate meta` lists both as hand edits — the D3 entries `page-is-default-off-record-page-refused` and `page-template-closed-region-unplaced-refused`. No D2 conversion rewrites them: where an app opens, and which slot a region fills, are the author's calls.

**Stored metadata keeps loading.** No conversion touches these shapes and the read path does not refuse them, so a `sys_metadata` page row already stored with a non-record `isDefault: true`, `template: 'full-width'` or an unplaced region is served unchanged and renders as before (a stored row hydrates into the registry without a package, so the registry does not judge it either). Saving such a row again through the metadata API is refused until it is fixed.

The text, without a contract change: `PageTypeSchema`'s docblock and enum comments now say that `home`, `app` and `utility` are the same region layout at three content widths — `home` is not a landing declaration, and `utility` is an ordinary inline page (the console has no floating or docked shell slot for it); `utility` stays a page type, read by the print door and the page-type width. The `print`-on-`utility` refusal no longer calls a utility page "a floating panel".

`@objectstack/objectql`: the registry refuses `isDefault: true` on a non-record page a package registers (`MisplacedDefaultPageError`, `INVALID_METADATA` 422), at the package door and at the package-bound item seam, with the page parse's sentence.

<!-- adr-0087: registered page-is-default-off-record-page-refused, page-template-closed-region-unplaced-refused -->
18 changes: 18 additions & 0 deletions .changeset/console-daeb77fc84dd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@objectstack/console": minor
---

Console (objectui) refreshed to `daeb77fc84dd`. Frontend changes in this range:

Derived from the changesets objectui declared over the range — 4 releasing of 4 changesets added across 5 non-merge commits; omitted: 1 commit carrying no changeset (they ship no package code).

- **minor** — Studio, Interfaces pillar: an html or react page reads like a code page. The source editor's plain-text fallback numbers its lines and wraps long ones as the code editor does, and… (objectui `830eb1f2d`)
- **patch** — A new record page seeded in Studio is saved with the `default` layout template instead of `full-width`, and the page node's `template` member is typed by reference to the spec's (… (objectui `daeb77fc8`)
- **patch** — Two Studio pickers are named by the labels shown beside them (objectui#12510). Nothing changes on screen. (objectui `3e09c1e8d`)
- **patch** — Studio's empty surfaces now carry the action that fills them (objectui#12514). (objectui `f23eb01cb`)

**In this console build, declared nowhere** — objectui merged 1 commit in this range with no `.changeset/*.md`. The code is inside the pin above and ships here, but nothing upstream declared it, so it appears in no objectui CHANGELOG and in no entry above. Listed by subject rather than counted, because a count cannot tell a dependency bump from a form-behaviour change (objectstack#6174); the upstream gate that would prevent this is objectui#3387.

- _(no changeset)_ docs(studio-redesign): move the 61 PR screenshots under after/ to an evidence branch and remove them from main (#12583) (objectui `7066dae53`)

objectui range: `f96f771140f1...daeb77fc84dd`
2 changes: 1 addition & 1 deletion .objectui-sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f96f771140f1ba488245e0d0d3e7420d720bff9a
daeb77fc84dd824e74c95b87cdede85afdb248e7
34 changes: 21 additions & 13 deletions content/docs/protocol/objectui/layout-dsl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,15 @@ Page

## Page Templates

Top-level layout structures that define macro organization.
Top-level layout structures that define macro organization. `template` is one of the
layouts the console registers — `default` (`header`, `sidebar`, `main`, `aside`,
`footer`), `header-sidebar-main` (`header`, `sidebar`, `main`), `three-column` (the same
five as `default`) or `dashboard` (`header`, `footer`, and every other region a grid cell)
— and each region's `name` is a slot that template places. Any other template name, and
on a `kind: 'full'` page any region name the template does not place, is refused when the
page is parsed (`os validate`, `os build`, the metadata save door): the console would draw
an unregistered template as the default layout, and stack an unplaced region under `main`
at full width with its `width` ignored.

### Standard Template

Expand Down Expand Up @@ -102,7 +110,7 @@ name: account_record
label: Account Record Page
type: record
object: account
template: standard
template: default
regions:
- name: header
components:
Expand Down Expand Up @@ -150,22 +158,23 @@ regions:

High-density layout for power users (like Salesforce Service Console).

It is the `header-sidebar-main` template: the list in `sidebar`, the record in `main`.

{/* os:check-yaml page */}
```yaml
name: service_console
label: Service Console
type: app
template: console
template: header-sidebar-main
regions:
- name: left
- name: sidebar
width: small
components:
- type: object-grid
properties:
objectName: case

- name: right
width: full
- name: main
components:
- type: page:tabs
properties:
Expand Down Expand Up @@ -218,9 +227,8 @@ regions:
a matching `Multi-Step Onboarding Wizard` example further down. **`steps` exists
on no schema.** `PageSchema` is `.strict()`, so it is refused by name
(`Unrecognized key(s) on this page: steps`), and no other shape in the spec
declares it either. `template: wizard` compounds it quietly: `template` is a
free-form string, so that half *parses* while naming a layout template no
renderer provides.
declares it either. `template: wizard` is refused as well: `template` names
one of the layouts the console registers, and no renderer provides a wizard.

Both are **removed rather than implemented** — multi-step authoring is an
implementation card first, exactly like the deferred tab loading and the
Expand Down Expand Up @@ -751,7 +759,7 @@ properties:
**There is no page-level `layout:` block.** Earlier revisions of this page
described three advanced layouts as a top-level `layout:` mapping —
`split_view`, `card_grid` and `kanban`. On `PageSchema`, `layout` is an
**alias for `template`**, a plain string naming a layout template, so a mapping
**alias for `template`**, which names one of the registered layout templates, so a mapping
written there is refused with exactly that rename hint (`Did you mean layout →
template?`). None of `split_view`, `card_grid`, `list_view` or `detail_view` is
a type on any schema.
Expand Down Expand Up @@ -837,12 +845,12 @@ interface Page {
// ever shipped). They live in PAGE_TYPE_ROADMAP; authoring one now fails
// validation. See page.zod.ts (PageTypeSchema / PAGE_TYPE_ROADMAP).
object?: string; // bound object for record pages
template?: string; // layout template name, default 'default'
template?: 'default' | 'header-sidebar-main' | 'three-column' | 'dashboard'; // default 'default'
regions?: PageRegion[]; // named regions, each holding components (optional; defaults to [])
}

interface PageRegion {
name: string; // e.g. "header", "main", "sidebar"
name: string; // a slot the page's template places: header, sidebar, main, aside, footer
width?: 'small' | 'medium' | 'large' | 'full';
components: PageComponent[];
}
Expand Down Expand Up @@ -1010,7 +1018,7 @@ name: customer_360
label: Customer 360
type: record
object: customer
template: standard
template: default
regions:
- name: header
components:
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Protocol reference — every schema by module
navTitle: Protocol Reference
description: Every schema published by @objectstack/spec — 1535 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1536 schemas across 14 protocol modules
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
Expand Down Expand Up @@ -33,8 +33,8 @@ counts are sums of the rows they head. Regenerate with
| [Shared Protocol](/docs/references/shared) | 10 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
| [System Protocol](/docs/references/system) | 36 | 284 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [UI Protocol](/docs/references/ui) | 16 | 167 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **197** | **1535** | 14 protocol modules |
| [UI Protocol](/docs/references/ui) | 16 | 168 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **197** | **1536** | 14 protocol modules |

---

Expand Down Expand Up @@ -364,7 +364,7 @@ The runtime environment — logging, jobs, cache, metrics, notifications, i18n a

## UI Protocol

**Source:** `packages/spec/src/ui/` · **Import:** `@objectstack/spec/ui` · **16 pages, 167 schemas**
**Source:** `packages/spec/src/ui/` · **Import:** `@objectstack/spec/ui` · **16 pages, 168 schemas**

Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer.

Expand All @@ -381,7 +381,7 @@ Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI lay
| [`expression-bindable-text-keys.zod.ts`](/docs/references/ui/expression-bindable-text-keys) | `ExpressionBindableTextKey` |
| [`i18n.zod.ts`](/docs/references/ui/i18n) | `AriaProps`, `I18nLabel`, `InlineLocaleMap` |
| [`notification.zod.ts`](/docs/references/ui/notification) | `NotificationPosition`, `NotificationSeverity`, `NotificationType` |
| [`page.zod.ts`](/docs/references/ui/page) | `ElementApiDataSource`, `ElementDataSource`, `InterfacePageConfig`, `Page`, `PageComponent`, `PageComponentType`, `PagePrint`, `PageRegion`, `PageType`, `PageVariable` |
| [`page.zod.ts`](/docs/references/ui/page) | `ElementApiDataSource`, `ElementDataSource`, `InterfacePageConfig`, `Page`, `PageComponent`, `PageComponentType`, `PagePrint`, `PageRegion`, `PageTemplate`, `PageType`, `PageVariable` |
| [`report.zod.ts`](/docs/references/ui/report) | `JoinedReportBlock`, `Report`, `ReportChart`, `ReportSort`, `ReportType` |
| [`responsive.zod.ts`](/docs/references/ui/responsive) | `ResponsiveStyles`, `StyleMap` |
| [`sharing.zod.ts`](/docs/references/ui/sharing) | `SharingConfig` |
Expand Down
Loading
Loading