feat(types,components): declare and draw input-otp's separator (objectui#11365) - #11469
Conversation
…w it (objectui#11365) The docs page's "With Separator" example and two catalog entries author `separator: true` on `input-otp`, but no type declared it and the renderer read nothing for it: both entries rendered zero `role="separator"` elements through the real SchemaRenderer. Triage ruled the enforce arm (ADR-0049). - `InputOTPSchema.separator?: boolean` on the TS face and the zod mirror. - The renderer splits the slots at the midpoint (odd length: the extra slot goes first) and draws one InputOTPSeparator between two groups. Absent or false keeps the one-group markup. - The registration publishes `separator` as a boolean input. - Pins: the catalog render/parse pin and the types face pin. Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
…put (objectui#11365) Without the boolean `separator` row in the `input-otp` registration's `inputs`, the html/jsx page tier's whitelist answers `unknown-prop` for a key the renderer honours (measured once with the row removed). Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
…or layout (objectui#11365) Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs: card #11365 (body and all five comments, 5928214045 through the os-dev-report 5953952211), PR #11469 (body, six-file list, net diff against ① Derived judgmentsEach accept-set or public-surface change the diff implies, with a verdict:
Nothing wrong found. The diff is six files, every one inside the claim's file surface (5952611327). ② Semver levelChangeset Clause-②: yes. The PR body's line matches the claim's (5952611327, ③ Boundary flags
Dev deviations, each answered:
Declared narrowing: Out-of-scope finding (noted, not filed): Check-runs on Also noted: the PR is Implemented-by: VERDICT: PASS |
Fixes #11365
Clause-②: yes
What this changes
Triage ruled the enforce arm of ADR-0049 (answer
5943561653on the card). The shipped primitive already exportsInputOTPSeparator(role="separator"), so the key is declared and drawn. The docs page and both catalog entries stay as they are, and they are now valid.InputOTPSchemagainsseparator?: booleanon the TypeScript face (packages/types/src/form.ts) and on the zod mirror (packages/types/src/zod/form.zod.ts). The node'sbody/childrenrefusal messages list what the node renders instead, and that list now namesseparator.separator: true,packages/components/src/renderers/form/input-otp.tsxsplits thelengthslots into twoInputOTPGroups at the midpoint and draws oneInputOTPSeparatorbetween them, the shadcn "with separator" layout.length: the extra slot goes in the first group, so 5 slots draw as 3 and 2, and 7 as 4 and 3.length: 1: one slot has nothing to separate, so it draws no separator.false: one group holding every slot, byte-identical to before (proof below).input-otpregistration publishes{ name: 'separator', type: 'boolean' }ininputs.minorfor@object-ui/typesand@object-ui/components: the published face gains a member.Accept-set change: before and after
Before is BASE
5988b6b53; after is headda4d38ef.components-form-input-otp/with-visual-separatorthrough the realSchemaRenderer:role="separator"countcomponents-form-input-otp/verification-form(itsinput-otpsits atproperties.children.1of aflexbag):role="separator"countwith-visual-separatorslot groupscomponents-form-input-otp/6-digit-otp(noseparator)safeValidateSchemaon both authoring entries.passthrough()left the key unjudgedStrictAnyComponentSchema) onwith-visual-separatorunrecognized_keysat the root, keyseparatorverification-formunrecognized_keysatproperties.children.1, keyseparator{ type: 'input-otp', separator: 'yes' }undersafeValidateSchemainvalid_typeatseparatorinput-otpinside aflexbaginvalid_typeatproperties.children.0.separatorInputOTPSchemaliteral withseparator: 'yes'BaseSchema's index signature isany@ts-expect-errorinput-otptag written withseparator={true}unknown-propwarning, "has no prop separator"The html/jsx tier row was measured once with a throwaway probe. It ran
compilewith the samemanifestFromConfigsmapping aspage.tsx'sgetJsxManifest, on head and on head with only theinputsrow removed. BASE has the sameinputs. No gate requires the row:check:component-surface-parityis report-only andinputsstays inside its union either way, andregistry-inputs-spec-paritycovers only types with a spec row, whichinput-otplacks. The row is there so that tier stops warning off a key the renderer honours.Corpus re-judged
Every document that authors
input-otp:examples/schema-catalog/src/schemas/components-form-input-otp/*entries:with-visual-separatorandverification-formauthorseparator: true. Both are now drawn, and both are green on both faces (table above).4-digit,6-digit-otpand8-digitdo not author it. Their rendered markup is byte-identical to BASE (proof below).content/docs/components/form/input-otp.mdx: its interface block listsseparator?: boolean; // Show visual separator, which is now true. The page is unedited.@objectstack/spec17.5.0 has noinput-otprow, so there is nothing to conflict with:dist/forinput-otp,InputOTPand their spellings exits 1, while a control grep for'input'underdist/uiexits 0;separatorhas 0 hits indist/ui/index.d.mts.1d0600bf) authors noinput-otpanywhere:git grep -c input-otpexits 1, and a control grep inpackages/spec/src/uiexits 0.Pins
examples/schema-catalog/test/input-otp-separator-11365.test.tsxrenders through the realSchemaRendererand checks:role="separator";lengthgives 3 and 2, and one slot draws no separator;6-digit-otpdraws none, in one group;separator: falsegives the sameinnerHTMLas noseparatorat all;It also checks parsing:
safeValidateSchemaand the strict face are green on both entries, and a non-booleanseparatoris refused withinvalid_typeat its path, flat and nested in a bag.packages/types/src/__tests__/input-otp-separator-11365.test.ts:InputOTPSchema['separator']is exactlyboolean | undefined, the declared spelling compiles, andseparator: 'yes'is a@ts-expect-error;separator, and refuses a string withinvalid_typeatseparator. A control shows an undeclared key still passes through.Reverse verification (each run from a committed head)
role="separator". The strict face reportedunrecognized_keysforseparator, at the root forwith-visual-separatorand atproperties.children.1forverification-form.form.ts,zod/form.zod.tsand the renderer from BASE under a restore trap, then confirmed on disk thatInputOTPSeparatorwas 0 in the renderer,separator?: booleanwas 0 inform.tsandseparator: zwas 0 in the zod file.falseequivalence.tsc -p tsconfig.test.jsonforpackages/typesexited 2: TS2344 on theboolean | undefinedequality, and TS2578, an unused@ts-expect-error.git checkout HEAD -- ....git diff HEADis 0 bytes, and every hash matches its HEAD blob.inputsrow. The row count read 0 on disk. The registration pin went red: 1 failed, 10 passed. Restored, and the hash matches HEAD.separator. I rendered4-digit,6-digit-otpand8-digitthroughSchemaRendererwith the head renderer and with the BASE renderer. Both outputs are 6201 bytes andcmpexits 0.Gates run locally
Node 22.23.3 was used, the latest 22.x that CI's
22.xresolves to; see the report for why.pnpm --filter '@object-ui/components^...' build, then@object-ui/componentsitselfpnpm --filter @object-ui/types --filter @object-ui/components run type-check(both scripts echoed)pnpm --filter @object-ui/example-schema-catalog run type-check, on the doc-gate build closurepnpm exec vitest run packages/types/pnpm exec vitest run packages/components/ examples/schema-catalog/pnpm exec vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.ts packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx(readers ofinputsand ofui:input-otp)da4d38efcheck:doc-snippets, after the scoped--build-filterturbo buildcheck:doc-examplescheck:doc-typescheck:component-surface-parity(report-only)input-otp: reads 3 to 4, interface 9 to 10, inputs 2 to 3. Findings stay at the same 2 (see Acceptance notes), and the full report is otherwise identical to BASEnode scripts/check-changeset-no-major.mjs,check:new-line-citations,check:control-bytes,check-changeset-presenceda4d38efDeclared narrowing.
check:sdui-registration-pinswas not run locally:sideEffectsarrays promise.sideEffectsarray.performance-budget.ymlruns it.The repo-wide lint and the full farm also belong to CI.
Acceptance notes
check:component-surface-paritystill reports two olderinput-otpfindings, unchanged by this PR:InputOTPSchemadeclaresdefaultValueanderror, and the renderer reads neither by name. They belong to that census's family (objectui#4631). They are recorded here, not filed.Session:
https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPCGenerated by Claude Code