feat(blocks-ui): default widget registry for JSON UI documents - #36
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
Review complete. 🟡 1 medium 💬 Inline comments (1)
🧹 Nitpicks (2) — 🟢 2 low
The change adds a new
The sweep high-recall pass did not publish its findings; the review relies on the lens, domain, and holistic passes, all of which completed and were adjudicated. Reviewed commit: 6dacca3 |
There was a problem hiding this comment.
This PR introduces the new @constructive-io/blocks-ui default widget registry package, adds a JSON documents page with nav/sitemap/llms.txt wiring, and updates packaging scripts and docs.
Key findings
- 🟡 DateTimePicker write-back drops the timezone — widgets.tsx:227
| const local = raw.length > 16 ? raw.slice(0, 16) : raw; | ||
|
|
||
| return ( | ||
| <FieldShell props={props} id={field.id} error={field.error}> | ||
| <Input | ||
| id={field.id} | ||
| name={field.name} | ||
| type="datetime-local" | ||
| value={local} | ||
| onChange={(event) => field.setValue(event.target.value)} |
There was a problem hiding this comment.
🟡 bug · medium
DateTimePicker write-back drops the timezone
DateTimePickerBlock slices a zoned ISO-8601 value to YYYY-MM-DDTHH:mm for the datetime-local input (widgets.tsx:227), then onChange stores event.target.value verbatim (widgets.tsx:236), persisting the trimmed, zone-less local string. The doc comment says the value is 'trimmed for display only', but the edit path overwrites the document value with a zone-less, seconds-less form, so any user edit silently strips the timezone (and seconds) from the persisted value, changing the instant it represents for downstream consumers.
📋 Prompt for AI Agents
In packages/blocks-ui/src/widgets.tsx, DateTimePickerBlock (lines 224-242): the onChange at line 236 stores event.target.value verbatim, which is a zone-less, seconds-less YYYY-MM-DDTHH:mm string, contradicting the comment that the document/Postgres value is ISO-8601 with a zone and only trimmed for display. Change the onChange to preserve the timezone and seconds when writing back (e.g. parse the edited local datetime and serialize to ISO-8601 with the local offset, or re-append the original value's zone suffix) so the persisted form-state value keeps its zone instead of being replaced by the raw datetime-local string.
Summary
blocks-rendererships the walker, bindings and registry contract but no components, so a generatedUIDocumentrenders nothing until a host hand-writes an adapter for every node type. This adds@constructive-io/blocks-ui: the defaultnodeType -> componentmap wired to@constructive-io/ui, plus a live docs page where a JSON Schema becomes a working form with no hand-written UI.It is a separate package rather than an export of
@constructive-io/uiso the design system never depends onblocks-renderer/blocks-schema(primitives shouldn't know about documents), and so a host can layer or replace it:What's in the registry
Input,Textarea,Select,RadioGroup,Checkbox,Switch,NumberInput,DatePicker,DateTimePicker,TimePicker,PhoneInput,FileUpload, andCodeEditor/MarkdownEditor/JsonEditoron a monospace-textarea fallback (Ace stays opt-in via a host override).Page,Form,Section,Grid,GridColumn,Tabs,Tab. Blocks:Button,ActionBar,Markdown,StatCard.DataTable,DetailPanel,RelationList,Chart,AgentChat. They need a query/agent runtime, so they fall through to the renderer's unknown-node handling until a host registers them.widgetRegistry/containerRegistry/blockRegistryare exported separately for hosts that want a subset.Adapters own no form state — it all lives in renderer context, so validation and
onSubmitkeep working with a mixed registry:NumberInputandFileUploadpreserve absence asnullrather than''/NaN;FileUploadstores the selected filename only, since bytes belong to a host storage adapter.Packaging
Follows the newer tsup packages (
command-palette,sheets): scoped name,exportsmap, publishes from the package root. It is not on the makage publish-from-distlayout, so the naked-name assertions incheck-packed-packages.tsdon't apply to it;docs/RELEASING.mdrecords that and the fact its peers publish first. Wired intobuild:packages,pack-local.ts, and the packed-consumer check, which now installs the tarball and asserts a generated document renders real inputs with no unknown-node fallback. Version is0.1.0and nothing is published — Lerna bumps it on the next manual release.Verified locally:
pnpm check,pnpm pack:check,pnpm build:pages(new/blocks/documentsroute prerenders).Link to Devin session: https://app.devin.ai/sessions/027937d092794c92a31c6ee49c513f59
Requested by: @pyramation