diff --git a/content/docs/guide/component-registry.md b/content/docs/guide/component-registry.md index 601109512e..e83c545de4 100644 --- a/content/docs/guide/component-registry.md +++ b/content/docs/guide/component-registry.md @@ -88,6 +88,7 @@ Now you can use it in schemas: All registered components receive the schema as props: + ```tsx interface ComponentProps { // The complete schema object @@ -119,6 +120,7 @@ function MyRenderer(props: ComponentProps) { Register components with additional metadata: + ```tsx ComponentRegistry.register('my-component', MyComponent, { label: 'My Custom Component', @@ -137,6 +139,7 @@ This metadata is used by the Visual Designer to provide better editing experienc Register components that load on demand: + ```tsx // The loader runs the first time a schema asks for `heavy-component`. ComponentRegistry.registerLazy('heavy-component', () => import('./HeavyComponent')) @@ -146,6 +149,7 @@ ComponentRegistry.registerLazy('heavy-component', () => import('./HeavyComponent Override default components with your own: + ```tsx import { ComponentRegistry } from '@object-ui/core' import { initializeComponents } from '@object-ui/components' @@ -163,6 +167,7 @@ ComponentRegistry.register('button', MyCustomButton) Default components are organized by category: ### Form Components + ```tsx - input - textarea @@ -178,6 +183,7 @@ Default components are organized by category: ``` ### Data Display + ```tsx - table - list @@ -189,6 +195,7 @@ Default components are organized by category: ``` ### Layout + ```tsx - page - container @@ -201,6 +208,7 @@ Default components are organized by category: ``` ### Feedback + ```tsx - alert - toast @@ -214,6 +222,7 @@ Default components are organized by category: ``` ### Navigation + ```tsx - menu - breadcrumb @@ -222,6 +231,7 @@ Default components are organized by category: ``` ### Other + ```tsx - button - link @@ -238,6 +248,8 @@ Default components are organized by category: ### Get All Registered Types ```tsx +import { ComponentRegistry } from '@object-ui/core' + const types = ComponentRegistry.getAllTypes() console.log(types) // ['input', 'button', 'form', ...] ``` @@ -245,6 +257,8 @@ console.log(types) // ['input', 'button', 'form', ...] ### Check if Type is Registered ```tsx +import { ComponentRegistry } from '@object-ui/core' + if (ComponentRegistry.has('my-component')) { console.log('Component is registered') } @@ -253,6 +267,8 @@ if (ComponentRegistry.has('my-component')) { ### Get Component Metadata ```tsx +import { ComponentRegistry } from '@object-ui/core' + const metadata = ComponentRegistry.getMeta('input') console.log(metadata) // { @@ -302,6 +318,7 @@ Use kebab-case for component types: ### 4. Provide Meaningful Metadata + ```tsx ComponentRegistry.register('rating', RatingComponent, { label: 'Star Rating', @@ -313,6 +330,7 @@ ComponentRegistry.register('rating', RatingComponent, { ### 5. Handle Missing Props Gracefully + ```tsx function MyComponent(props: ComponentProps) { const { schema } = props @@ -332,6 +350,7 @@ function MyComponent(props: ComponentProps) { Group related components into plugin packages: + ```tsx // @my-org/objectui-plugin-charts import { ComponentRegistry } from '@object-ui/core' @@ -348,6 +367,7 @@ export function registerChartComponents() { Usage: + ```tsx import { initializeComponents } from '@object-ui/components' import '@object-ui/fields' @@ -362,10 +382,10 @@ registerChartComponents() Here's a complete example of a custom form component: ```tsx -import { forwardRef } from 'react' +import { forwardRef, useState } from 'react' import { ComponentRegistry } from '@object-ui/core' import type { BaseSchema } from '@object-ui/types' -import { cn } from '@/lib/utils' +import { cn } from '@object-ui/components' interface RatingSchema extends BaseSchema { type: 'rating' diff --git a/content/docs/guide/layout.md b/content/docs/guide/layout.md index 89b4c3683b..8b9fb21f0c 100644 --- a/content/docs/guide/layout.md +++ b/content/docs/guide/layout.md @@ -40,6 +40,7 @@ your JSON pages *inside* it. `app-shell` is not a component key either — what ### Basic Usage + ```tsx import { AppShell, SidebarNav, type NavItem } from '@object-ui/layout'; import { SchemaRenderer } from '@object-ui/react'; @@ -171,6 +172,7 @@ The `Page` component provides a consistent wrapper for individual pages with opt ### Schema API + ```typescript { type: 'page', @@ -258,6 +260,7 @@ Unresolvable tokens collapse to an empty string rather than leaking the raw temp ### Schema API + ```typescript { type: 'page-header', @@ -316,6 +319,7 @@ React, or use `navigation-renderer` when the tree has to come from metadata. `SidebarNav` renders a Shadcn `Sidebar`, so it must be inside a `SidebarProvider` — `AppShell` supplies one. Its rows are `NavLink`s, so it also needs a router above it. + ```tsx import { AppShell, SidebarNav, type NavItem } from '@object-ui/layout'; import { SchemaRenderer } from '@object-ui/react'; @@ -418,6 +422,7 @@ branding for a whole-shell-from-metadata setup. The shell is React; the page inside it is your JSON. + ```tsx import { AppShell, SidebarNav, type NavItem } from '@object-ui/layout'; import { SchemaRenderer } from '@object-ui/react'; @@ -453,6 +458,7 @@ and `defaultOpen` is the shell's own initial-state prop. Omit `sidebar` and the content fills the width under the top bar. + ```tsx Welcome}> @@ -562,6 +568,7 @@ them reads `lg` (1024px), so 800px and 1400px get the same layout. Add Tailwind classes to layout components: + ```tsx ```tsx // One shell for the whole app; `pageSchema` is whatever the route resolves to. }> @@ -660,6 +668,7 @@ Group related items with `NavGroup`. Pass groups instead of a flat list and `Sid labels each section and draws the separator between them itself — there is no `divider` item, and a row is either a link or a group, never both: + ```tsx import { SidebarNav, type NavGroup } from '@object-ui/layout'; import { DollarSign, Home, Settings } from 'lucide-react'; diff --git a/scripts/check-doc-snippet-types.mjs b/scripts/check-doc-snippet-types.mjs index d87ec80496..6f9434d0f5 100644 --- a/scripts/check-doc-snippet-types.mjs +++ b/scripts/check-doc-snippet-types.mjs @@ -227,29 +227,44 @@ const TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']); * Documents whose snippets are NOT compiled, each with the reason. The default * is covered; this list is the debt, by name, and it can only shrink. * - * ⚠️ 2 of these entries are `.md` pages under `content/docs` that objectui#5174 - * made visible: the collector now reads `.md`, and an entry with a measured reason - * is what a page that cannot pass yet is owed. They are DISCLOSED debt, not new - * debt — every one of them was equally unverified before, just unnamed. Their - * reasons carry the same measured diagnostic mix as the rest, and a symbol a - * package does not export is called out by name, because that is the - * reader-visible half (objectui#5160). - * - * That count was 19 until objectui#5174's triage batches, which walk pages OFF this - * list rather than re-wording their reasons. The direction on that card is entries - * LEAVING. Batch 1 took ten: `api/schema-reference`, `plugins/index`, and the - * `guide/` pages `architecture-overview`, `deployment`, `expressions`, - * `notifications`, `public-forms`, `schema-overview`, `troubleshooting` and - * `user-state-persistence`. Batch 2 took four more — `guide/plugins`, + * ⚠️ ZERO of these entries are now `.md` pages under `content/docs`. There were 19 + * when objectui#5174 made them visible — the collector reads `.md`, and an entry + * with a measured reason is what a page that cannot pass yet is owed — and that + * card then walked every one of them back OFF this list rather than re-wording its + * reason. The direction on that card was entries LEAVING, and it finished: what + * remains here is 12 `.mdx` pages and 32 package READMEs. + * + * Batch 1 took ten: `api/schema-reference`, `plugins/index`, and the `guide/` pages + * `architecture-overview`, `deployment`, `expressions`, `notifications`, + * `public-forms`, `schema-overview`, `troubleshooting` and `user-state-persistence` + * — clearing 90 diagnostics. Batch 2 took four more — `guide/plugins`, * `guide/building-crud-app`, `rfcs/0001-clipboard-paste` and `guide/architecture` - * — clearing 89 diagnostics. Batch 3 took three — `guide/plugin-development`, - * `guide/schema-rendering` and `guide/theming` — clearing 81, and left - * `guide/layout` and `guide/component-registry` as the final pair. - * Each page reached zero the honest two ways — a block + * — clearing 89. Batch 3 took three — `guide/plugin-development`, + * `guide/schema-rendering` and `guide/theming` — clearing 81. Batch 4 took the + * final pair, `guide/component-registry` (59) and `guide/layout` (45), clearing + * 104. Each page reached zero the honest two ways — a block * that should compile was made self-contained against the built `dist/`, and a * block that genuinely cannot compile got a `FRAGMENT_MARKER` declaration with a * written reason. Nothing about this gate's strictness moved to get them there. * + * Batch 4 in the same terms as the batches below: 32 blocks brought under the gate + * — 23 declared fragments and 9 that compile, of which 5 already compiled untouched + * and 4 were edited to. Its defect was in the page's ONE complete copy-paste + * example: `guide/component-registry`'s `RatingComponent` calls `useState` while + * importing only `forwardRef` from `react`, so the block a reader is invited to + * take whole was broken at the first hook. The same block took `cn` from the + * reader's own `@/lib/utils` alias when `@object-ui/components` exports `cn` + * itself. Two things this gate CANNOT see on that page, stated because a green run + * must not be read as more than it is: `ComponentRegistry` is a `Registry`, so + * `register()`'s component argument is `any` and no registered component's props + * are checked against what `SchemaRenderer` passes; and `BaseSchema` carries + * `[key: string]: any`, so a wrong key on a `BaseSchema`-shaped literal is + * structurally invisible here. What IS sealed — and therefore really checked — + * is `ComponentMeta`, `ComponentInput`, `NavItem`, `NavGroup`, `AppShellProps` and + * `SidebarNavProps`; every literal of those six on the two pages was measured + * clean, the `lucide-react` blocks by probing them with the icon import shimmed + * rather than by inferring it from the TS2307 that hid them. + * * Batch 2's two routes in proportion, because the ratio is the reviewable part: it * brought 42 blocks under the gate — 34 declared fragments and 8 that compile, of * which 4 already compiled untouched and 4 were edited to. Three of those four @@ -292,6 +307,17 @@ const TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']); * app but is not a root dependency here. * `guide/plugins` and the clipboard-paste RFC document packages the reader is being * taught to create, and an RFC's signature excerpts have no bodies by design. + * `guide/layout` is the same shape as `guide/theming`, one layer out: four of its + * nine blocks import `lucide-react` for the icon COMPONENTS `NavItem.icon` takes, + * and that package — a dependency of ten workspace packages here, including + * `@object-ui/layout` — is not a root dependency, so the specifier does not resolve + * in this program. Hoisting a package to the repo root to buy a doc snippet + * coverage is a real dependency edge, so those blocks are declared instead, with + * what sits underneath them measured (via a shimmed icon import) rather than left + * unknown. `guide/component-registry`'s six category lists are markdown BULLET + * LISTS inside `tsx` fences — the fence language is the defect there, and + * correcting it is a change to the page's rendering rather than to a snippet, so it + * is declared here and left to its own change. * * objectui#5343 then read that list back and cleared it for the getting-started * pages: no entry for `content/docs/guide/**` or for @@ -319,21 +345,6 @@ const TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']); * @type {Record} */ const UNGATED_DOCS = { - 'content/docs/guide/component-registry.md': - '3 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + - '50 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + - 'page never defines; 6 unresolved-module diagnostic(s). This entry read TS2305x13 TS2339x1 ' + - 'until objectui#5343, which cleared the page of fabricated exports: `getComponentRegistry` ' + - '(@object-ui/react) x5 → the `ComponentRegistry` singleton @object-ui/core exports, ' + - '`registerDefaultRenderers` (@object-ui/components) x4 → `initializeComponents()` plus the ' + - 'side-effect `@object-ui/fields` import, `BaseSchema` (@object-ui/core) x3 → @object-ui/types ' + - '(which is also what retired the TS2339, since the real `BaseSchema` declares `className`), ' + - 'and `InputRenderer` (@object-ui/components), which nothing replaces — the built-in renderers ' + - 'are registered by loading their package, never handed out one by one', - 'content/docs/guide/layout.md': - '21 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + - '20 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' + - 'page never defines; 4 unresolved-module diagnostic(s)', 'content/docs/guide/objectos-integration.mdx': '36 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; ' + '10 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the ' +