From 7ba0fe0a4bfbf837d6f7d4c85d3131281fa25cd9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 09:49:43 +0000 Subject: [PATCH 1/5] docs: stop teaching keys the strict authoring face refuses (objectui#12131) Interface blocks (content/docs/components/form): - button.mdx: drop `fullWidth` and `href`; neither is declared on ButtonSchema and the button renders byte-identically with either. The full-width spelling the page's own fixture uses, `className: "w-full"`, is now named on the `className` line. - form.mdx: `submitButton` -> `submitLabel` (the declared key the form renderer reads for its submit text); FormField `defaultValue` (undeclared, renders identically) -> FormSchema `defaultValues`, the key the renderer seeds react-hook-form from. - label.mdx: drop `required`; LabelSchema declares no indicator key and the renderer only leaks it as a stray `required` attribute. Catalog fixture: components-form-label/required-label.json is renamed password-label.json (a label paired with a password input, which is what it shows); the generated index is regenerated and the objectui#8761 pin and the label page follow the new id. READMEs (outside the JSON-fence gate's scan surface): - README.md form: drop the undeclared root `title` (it only reached the DOM as a native tooltip on the form element). - README.md dashboard: rewrite onto declared keys: `metric-card` entries with `title`/`trendValue` and literal values (metric-card does not evaluate `${...}`), dataset-bound `line`/`pie` chart widgets, and the flat `w`/`h` moved into `layout` boxes; drop the undeclared root `title`. - packages/plugin-dashboard/README.md: the object-bound table example is an `object-data-table` node; a bare `table` is the static component and declares neither `objectName` nor a column `format`. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude --- README.md | 14 ++++++-------- content/docs/components/form/button.mdx | 4 +--- content/docs/components/form/form.mdx | 9 ++------- content/docs/components/form/label.mdx | 5 ++--- examples/schema-catalog/src/index.ts | 10 +++++----- .../{required-label.json => password-label.json} | 0 .../test/flex-direction-col-8761.test.tsx | 2 +- packages/plugin-dashboard/README.md | 6 +++++- 8 files changed, 22 insertions(+), 28 deletions(-) rename examples/schema-catalog/src/schemas/components-form-label/{required-label.json => password-label.json} (100%) diff --git a/README.md b/README.md index 65fae9bac5..ead8677d8c 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,6 @@ published is not an error: the expression is returned as its own source text. ```json { "type": "form", - "title": "Contact Us", "fields": [ { "name": "name", "type": "text", "label": "Full Name", "required": true }, { "name": "email", "type": "email", "label": "Email", "required": true }, @@ -234,14 +233,13 @@ published is not an error: the expression is returned as its own source text. ```json { "type": "dashboard", - "title": "Sales Dashboard", "widgets": [ - { "type": "statistic", "label": "Revenue", "value": "${stats.revenue}", "trend": "up", "description": "+12%", "w": 3, "h": 1 }, - { "type": "statistic", "label": "Orders", "value": "${stats.orders}", "trend": "up", "description": "+8%", "w": 3, "h": 1 }, - { "type": "statistic", "label": "Customers", "value": "${stats.customers}", "trend": "up", "description": "+5%", "w": 3, "h": 1 }, - { "type": "statistic", "label": "Conversion", "value": "${stats.conversion}", "trend": "down", "description": "-2%", "w": 3, "h": 1 }, - { "type": "chart", "chartType": "line", "title": "Revenue Over Time", "w": 8, "h": 3 }, - { "type": "chart", "chartType": "pie", "title": "Sales by Region", "w": 4, "h": 3 } + { "type": "metric-card", "title": "Revenue", "value": "$56,789", "trend": "up", "trendValue": "+12%", "layout": { "x": 0, "y": 0, "w": 3, "h": 1 } }, + { "type": "metric-card", "title": "Orders", "value": "1,234", "trend": "up", "trendValue": "+8%", "layout": { "x": 3, "y": 0, "w": 3, "h": 1 } }, + { "type": "metric-card", "title": "Customers", "value": "432", "trend": "up", "trendValue": "+5%", "layout": { "x": 6, "y": 0, "w": 3, "h": 1 } }, + { "type": "metric-card", "title": "Conversion", "value": "3.2%", "trend": "down", "trendValue": "-2%", "layout": { "x": 9, "y": 0, "w": 3, "h": 1 } }, + { "id": "revenue_over_time", "type": "line", "title": "Revenue Over Time", "dataset": "sales", "dimensions": ["month"], "values": ["revenue"], "layout": { "x": 0, "y": 1, "w": 8, "h": 3 } }, + { "id": "sales_by_region", "type": "pie", "title": "Sales by Region", "dataset": "sales", "dimensions": ["region"], "values": ["revenue"], "layout": { "x": 8, "y": 1, "w": 4, "h": 3 } } ] } ``` diff --git a/content/docs/components/form/button.mdx b/content/docs/components/form/button.mdx index 834961fa03..e07539bf7c 100644 --- a/content/docs/components/form/button.mdx +++ b/content/docs/components/form/button.mdx @@ -64,11 +64,9 @@ interface ButtonSchema { // RUNTIME SLOT (objectui#6124): a React host supplies it; JSON refuses it // by name. Author behaviour as a node type, e.g. an action:button node. onClick?: () => void | Promise; // Click handler - href?: string; // Link URL (renders as link) // Styling - className?: string; - fullWidth?: boolean; + className?: string; // Tailwind classes, e.g. "w-full" for a full-width button } ``` diff --git a/content/docs/components/form/form.mdx b/content/docs/components/form/form.mdx index b10424ec63..01cea9c2e2 100644 --- a/content/docs/components/form/form.mdx +++ b/content/docs/components/form/form.mdx @@ -33,18 +33,13 @@ interface FormField { // Options (for select, radio-group) options?: Array<{ value: string; label: string }>; - - // Default value - defaultValue?: any; } interface FormSchema { type: 'form'; fields: FormField[]; // Form fields - submitButton?: { - label: string; - variant?: string; - }; + defaultValues?: Record; // Initial values, keyed by field name + submitLabel?: string; // Submit button text (default 'Submit') // Events // RUNTIME SLOT (objectui#6124): a React host supplies it; JSON refuses it diff --git a/content/docs/components/form/label.mdx b/content/docs/components/form/label.mdx index 8e8e342825..33e02ff4cb 100644 --- a/content/docs/components/form/label.mdx +++ b/content/docs/components/form/label.mdx @@ -9,9 +9,9 @@ The Label component renders accessible labels for form fields. -## Required Field +## With a Password Input - + ## Schema @@ -20,7 +20,6 @@ interface LabelSchema { type: 'label'; text: string; // Label text htmlFor?: string; // Associated input ID - required?: boolean; // Show required indicator className?: string; } ``` diff --git a/examples/schema-catalog/src/index.ts b/examples/schema-catalog/src/index.ts index bc176d0945..cfd9381c4b 100644 --- a/examples/schema-catalog/src/index.ts +++ b/examples/schema-catalog/src/index.ts @@ -223,7 +223,7 @@ import components_form_input_otp_8_digit from './schemas/components-form-input-o import components_form_input_otp_verification_form from './schemas/components-form-input-otp/verification-form.json' with { type: 'json' }; import components_form_input_otp_with_visual_separator from './schemas/components-form-input-otp/with-visual-separator.json' with { type: 'json' }; import components_form_label_form_label from './schemas/components-form-label/form-label.json' with { type: 'json' }; -import components_form_label_required_label from './schemas/components-form-label/required-label.json' with { type: 'json' }; +import components_form_label_password_label from './schemas/components-form-label/password-label.json' with { type: 'json' }; import components_form_radio_group_basic_radio_group from './schemas/components-form-radio-group/basic-radio-group.json' with { type: 'json' }; import components_form_radio_group_disabled from './schemas/components-form-radio-group/disabled.json' with { type: 'json' }; import components_form_radio_group_form_field from './schemas/components-form-radio-group/form-field.json' with { type: 'json' }; @@ -2370,14 +2370,14 @@ const REGISTRY: Record = { }, schema: components_form_label_form_label, }, - 'components-form-label/required-label': { - id: 'components-form-label/required-label', + 'components-form-label/password-label': { + id: 'components-form-label/password-label', meta: { - title: "Required Label", + title: "Password Label", description: "", category: 'components-form-label', }, - schema: components_form_label_required_label, + schema: components_form_label_password_label, }, 'components-form-radio-group/basic-radio-group': { id: 'components-form-radio-group/basic-radio-group', diff --git a/examples/schema-catalog/src/schemas/components-form-label/required-label.json b/examples/schema-catalog/src/schemas/components-form-label/password-label.json similarity index 100% rename from examples/schema-catalog/src/schemas/components-form-label/required-label.json rename to examples/schema-catalog/src/schemas/components-form-label/password-label.json diff --git a/examples/schema-catalog/test/flex-direction-col-8761.test.tsx b/examples/schema-catalog/test/flex-direction-col-8761.test.tsx index 108c87b1fa..04d03c1051 100644 --- a/examples/schema-catalog/test/flex-direction-col-8761.test.tsx +++ b/examples/schema-catalog/test/flex-direction-col-8761.test.tsx @@ -57,7 +57,7 @@ import { getExample } from '../src/index.js'; const IDS = [ 'components-form-label/form-label', - 'components-form-label/required-label', + 'components-form-label/password-label', 'components-form-date-picker/form-field', 'components-form-input-otp/verification-form', 'components-form-radio-group/form-field', diff --git a/packages/plugin-dashboard/README.md b/packages/plugin-dashboard/README.md index 8156509aa5..7b64b9f5db 100644 --- a/packages/plugin-dashboard/README.md +++ b/packages/plugin-dashboard/README.md @@ -797,9 +797,13 @@ the schema arrives, or for good when the read failed (objectui#10657). A column that authors its `type` draws from it meanwhile. The record drawer a row opens (record drill-down) draws every value as the mask in that window too. +Authored as a node, the object-bound table is `object-data-table`. A bare `table` +is the static component: it takes its rows in `data` and declares neither +`objectName` nor a column `format`. + ```jsonc { - "type": "table", + "type": "object-data-table", "objectName": "opportunity", "columns": [ { "accessorKey": "name", "header": "Opportunity" }, From dfda40fa72e32bc44f4382b8b8362bf0223d8afc Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 09:52:28 +0000 Subject: [PATCH 2/5] chore(scripts): declare the root README dashboard's widget-slot vocabulary (objectui#12131) The root README's "Dashboard" copy-paste schema passed check:doc-types only because it taught registered COMPONENT keys (`statistic`, `chart`) in a dashboard's `widgets[]`, which the strict authoring face refused. Rewritten onto the slot's own vocabulary, its `type` values are `metric-card`, `line` and `pie`: widget-slot / widget-kind values that are not bare registry keys. They are declared in DOC_TYPE_EXEMPTIONS under `README.md`, worded after the `packages/plugin-dashboard/README.md` entries that already declare the same three values; the gate's stale-exemption check keeps the entries tied to the sites. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude --- scripts/check-doc-component-types.mjs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts/check-doc-component-types.mjs b/scripts/check-doc-component-types.mjs index caf2657f6d..dcaf868be7 100644 --- a/scripts/check-doc-component-types.mjs +++ b/scripts/check-doc-component-types.mjs @@ -1548,6 +1548,32 @@ const DOC_TYPE_EXEMPTIONS = { '(phase 2b) gave that registration `skipFallback: true`, which is the only reason this value ' + 'used to pass here as a registered key.', }, + // The root README's "Dashboard" copy-paste schema (objectui#12131). It used to + // pass here by teaching registered COMPONENT keys (`statistic`, `chart`) in a + // dashboard's `widgets[]`, a slot that reads neither, which the strict + // authoring face refused; written in the slot's own vocabulary, its values are + // these three. + 'README.md': { + 'metric-card': + 'Dashboard widget-slot entry directly in a `dashboard`\'s `widgets[]` — ' + + '`DASHBOARD_COMPONENT_WIDGET_TYPES` (`@object-ui/types`\' `complex` module; its TypeScript face ' + + 'is `DashboardWidgetSlotComponentSchema`), the closed ' + + 'component slot of the 2026-08-14 ruling (objectstack#8593). Not a node position: the node ' + + 'type is `dashboard`, which the enclosing snippet spells. The dashboard hands the entry to ' + + 'the registry as `plugin-dashboard:metric-card`, a registration with `skipFallback: true` ' + + '(objectui#10859 batch 8), so the bare value is not a registry key. Same vocabulary as the ' + + '`packages/plugin-dashboard/README.md` entry above.', + line: + 'Dashboard widget kind under `widgets[].type` — `DashboardWidgetTypeName` (`@object-ui/types`\' ' + + '`complex` module) declares that CLOSED vocabulary, and its spec half flows in BY REFERENCE ' + + 'from `ChartTypeSchema` (@objectstack/spec/ui), where `line` is a member. Not a node type: the ' + + 'node type is `dashboard`, which the enclosing snippet spells. Same vocabulary as the ' + + '`packages/plugin-dashboard/README.md` entry above.', + pie: + 'Dashboard widget kind under `widgets[].type` — a member of `ChartTypeSchema` ' + + '(@objectstack/spec/ui) reaching this repo by reference through `DashboardWidgetTypeName`. ' + + 'Same vocabulary as `line` above.', + }, }; /** From 74a3bead82677800468d76c5ff0378f2196a771f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 10:03:02 +0000 Subject: [PATCH 3/5] Revert "chore(scripts): declare the root README dashboard's widget-slot vocabulary (objectui#12131)" This reverts commit dfda40fa. The root README's dashboard example is re-shaped in the next commit onto registered node types only (`grid`, `statistic`), so the three `README.md` exemptions would fail as `stale-exemption`. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude --- scripts/check-doc-component-types.mjs | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/scripts/check-doc-component-types.mjs b/scripts/check-doc-component-types.mjs index dcaf868be7..caf2657f6d 100644 --- a/scripts/check-doc-component-types.mjs +++ b/scripts/check-doc-component-types.mjs @@ -1548,32 +1548,6 @@ const DOC_TYPE_EXEMPTIONS = { '(phase 2b) gave that registration `skipFallback: true`, which is the only reason this value ' + 'used to pass here as a registered key.', }, - // The root README's "Dashboard" copy-paste schema (objectui#12131). It used to - // pass here by teaching registered COMPONENT keys (`statistic`, `chart`) in a - // dashboard's `widgets[]`, a slot that reads neither, which the strict - // authoring face refused; written in the slot's own vocabulary, its values are - // these three. - 'README.md': { - 'metric-card': - 'Dashboard widget-slot entry directly in a `dashboard`\'s `widgets[]` — ' + - '`DASHBOARD_COMPONENT_WIDGET_TYPES` (`@object-ui/types`\' `complex` module; its TypeScript face ' + - 'is `DashboardWidgetSlotComponentSchema`), the closed ' + - 'component slot of the 2026-08-14 ruling (objectstack#8593). Not a node position: the node ' + - 'type is `dashboard`, which the enclosing snippet spells. The dashboard hands the entry to ' + - 'the registry as `plugin-dashboard:metric-card`, a registration with `skipFallback: true` ' + - '(objectui#10859 batch 8), so the bare value is not a registry key. Same vocabulary as the ' + - '`packages/plugin-dashboard/README.md` entry above.', - line: - 'Dashboard widget kind under `widgets[].type` — `DashboardWidgetTypeName` (`@object-ui/types`\' ' + - '`complex` module) declares that CLOSED vocabulary, and its spec half flows in BY REFERENCE ' + - 'from `ChartTypeSchema` (@objectstack/spec/ui), where `line` is a member. Not a node type: the ' + - 'node type is `dashboard`, which the enclosing snippet spells. Same vocabulary as the ' + - '`packages/plugin-dashboard/README.md` entry above.', - pie: - 'Dashboard widget kind under `widgets[].type` — a member of `ChartTypeSchema` ' + - '(@objectstack/spec/ui) reaching this repo by reference through `DashboardWidgetTypeName`. ' + - 'Same vocabulary as `line` above.', - }, }; /** From e7d0ebce32913e071ee64442ecd6a6bff9b673bb Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 10:03:02 +0000 Subject: [PATCH 4/5] docs(readme): keep the ruled `statistic` tiles, in a grid the strict face accepts (objectui#12131) The 2026-09-01 ruling on objectui#7115 (maintainer-approved) set this example's content to four `statistic` tiles whose `${stats.*}` values evaluate, and rejected spelling the numbers out. Since then the dashboard's `widgets[]` slot has closed: the strict authoring face refuses a `statistic` entry there, and measured through the real `SchemaRenderer` on main the example drew six "chart type is not supported yet" panels, with no tile drawn and no expression evaluated. The previous commit moved it onto `metric-card` entries with literal values, which reverses that ruling (`metric-card` has no carriage row). This keeps the four ruled tiles byte-for-byte (`label`, `${stats.*}` `value`, `trend`, `description`) and changes only their container to a `grid` with `columns: 4`: strict-valid, and measured, each tile draws its evaluated value. Dropped: the undeclared root `title`, the flat `w`/`h`, and the two charts, which carried no data and drew only the "not supported" panel. A one-line lead-in points at the plugin-dashboard README for the `dashboard` node and dataset charts. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ead8677d8c..e11d7bb8bc 100644 --- a/README.md +++ b/README.md @@ -230,16 +230,18 @@ published is not an error: the expression is returned as its own source text. #### 📈 Dashboard +A row of live KPI tiles. A `dashboard` node's `widgets[]` takes `metric-card` entries and +dataset-bound charts instead; see the [`@object-ui/plugin-dashboard` README](packages/plugin-dashboard/README.md). + ```json { - "type": "dashboard", - "widgets": [ - { "type": "metric-card", "title": "Revenue", "value": "$56,789", "trend": "up", "trendValue": "+12%", "layout": { "x": 0, "y": 0, "w": 3, "h": 1 } }, - { "type": "metric-card", "title": "Orders", "value": "1,234", "trend": "up", "trendValue": "+8%", "layout": { "x": 3, "y": 0, "w": 3, "h": 1 } }, - { "type": "metric-card", "title": "Customers", "value": "432", "trend": "up", "trendValue": "+5%", "layout": { "x": 6, "y": 0, "w": 3, "h": 1 } }, - { "type": "metric-card", "title": "Conversion", "value": "3.2%", "trend": "down", "trendValue": "-2%", "layout": { "x": 9, "y": 0, "w": 3, "h": 1 } }, - { "id": "revenue_over_time", "type": "line", "title": "Revenue Over Time", "dataset": "sales", "dimensions": ["month"], "values": ["revenue"], "layout": { "x": 0, "y": 1, "w": 8, "h": 3 } }, - { "id": "sales_by_region", "type": "pie", "title": "Sales by Region", "dataset": "sales", "dimensions": ["region"], "values": ["revenue"], "layout": { "x": 8, "y": 1, "w": 4, "h": 3 } } + "type": "grid", + "columns": 4, + "children": [ + { "type": "statistic", "label": "Revenue", "value": "${stats.revenue}", "trend": "up", "description": "+12%" }, + { "type": "statistic", "label": "Orders", "value": "${stats.orders}", "trend": "up", "description": "+8%" }, + { "type": "statistic", "label": "Customers", "value": "${stats.customers}", "trend": "up", "description": "+5%" }, + { "type": "statistic", "label": "Conversion", "value": "${stats.conversion}", "trend": "down", "description": "-2%" } ] } ``` From 926c3a4bd37eda1ecb7622d6401ef813ea875bd0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 10:34:21 +0000 Subject: [PATCH 5/5] docs(form): restore FormField `defaultValue` in the interface block (objectui#12131) objectui#12130 is open on exactly this key (declare it, or rule it runtime-only), so the docs do not pre-empt it: the `// Default value` line and `defaultValue?: any;` are restored byte-for-byte as main has them, as the last member of `interface FormField`. The FormSchema `defaultValues` and `submitLabel` lines stay. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude --- content/docs/components/form/form.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/docs/components/form/form.mdx b/content/docs/components/form/form.mdx index 01cea9c2e2..da3fc03c16 100644 --- a/content/docs/components/form/form.mdx +++ b/content/docs/components/form/form.mdx @@ -33,6 +33,9 @@ interface FormField { // Options (for select, radio-group) options?: Array<{ value: string; label: string }>; + + // Default value + defaultValue?: any; } interface FormSchema {