Repository navigation
fix(types,app-shell,plugin-dashboard,plugin-designer): the widget width / height editors write a whole four-number layout (objectui#11388) - #11398
Conversation
…th / height editors write a whole four-number layout (objectui#11388)
The three widget width / height editors (the Studio widget inspector,
DashboardWithConfig and DashboardEditor) each spread one edited dimension
onto a possibly-absent `layout` and cast the result past
`DashboardWidgetSchema['layout']`. On a widget with no `layout` they stored
`{ w }` or `{ h }`, which the spec's widget `layout` refuses at `x`, `y` and
the other dimension.
`@object-ui/types` gains `completeWidgetLayout(layout, patch, placement)` and
`defaultWidgetPlacement(index)`. All three editors write through the helper,
seeding the untouched coordinates from the grid's auto-placement for the
widget's index, and the casts are gone. `DashboardGridLayout` places a
layout-less widget through the same default, so the grid and the editors agree
on where it is. The inputs show the completed box, so the dimension an edit
leaves alone is the one on screen.
Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42
Co-authored-by: Claude <noreply@anthropic.com>
…he one the input now shows (objectui#11388) The height input of a widget with no `layout` now shows the grid's auto-placed 4, so re-entering 4 fires no change and the pin saw no patch. Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <noreply@anthropic.com>
…out, and the changeset (objectui#11388) Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <noreply@anthropic.com>
…11388) Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 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
|
Fixes #11388
Clause-②: yes
The three dashboard widget width / height editors now write a whole four-number
layoutthrough one shared helper, and the casts pastDashboardWidgetSchema['layout']are gone, so the compiler judges each write.What changed
@object-ui/typesgains two exports on its main entry, in a new zod-free moduledashboard-widget-layout.ts:defaultWidgetPlacement(index): the box the dashboard grid auto-places a widget with nolayoutin. It is the fallback the spec states in the doc comment of its widgetlayoutmember:x: (i % 4) * 3,y: Math.floor(i / 4) * 4,w: 3,h: 4.completeWidgetLayout(layout, patch, placement): the whole box after an edit. Each coordinate comes from the edit, else from the storedlayout, else from the placement.defaultWidgetPlacement(index)as the placement:DashboardWidgetInspector(width and height inputs);DashboardWithConfig's live writer (handleWidgetFieldChange, forlayoutW/layoutH);DashboardEditor'sWidgetPropertyPanel, which now takes the widget'sindex.DashboardGridLayout'sbuildDefaultLayoutsplaces a widget with nolayoutthrough the samedefaultWidgetPlacement. Where widgets land does not change. The grid's default is now stated once, and the editors seed from that same statement instead of a copy.DashboardWithConfig's two sliders, shows the same completed box the writer stores. For a widget with nolayoutthat box is 3 × 4, where the inputs used to show 1 × 1. Without this, editing the width would storeh: 4while the height input still showed 1. InDashboardWithConfigit would be worse: the slider seed also feeds the save payload, so the live schema would holdh: 4and the savedlayoutHwould be 1.layout" section in the@object-ui/typesREADME (its snippet is judged bycheck:readme-exports), and one paragraph each in the plugin-dashboard README andcontent/docs/plugins/plugin-dashboard.mdx. Changeset.changeset/11388-widget-layout-complete.md: types minor, app-shell, plugin-dashboard and plugin-designer patch.Where the helper lives, and the export it adds
I checked each package's
package.json. The production@object-ui/*deps that all three writers share arecomponents,core,i18n,reactandtypes. plugin-designer does not depend on plugin-dashboard.@object-ui/typesis the lowest of these (core depends on it). It already holds the type the helper returns (DashboardWidgetLayout), and it already exports zod-free runtime helpers from its main entry, so no new dependency edge is added.Because
@object-ui/typesis published, this is a new export from a published package's entry point. Under the claim's own condition, itsClause-②: nogoes back to the seat to re-judge, so this body carries noClause-②line. The new module stays clear ofpackages/types/src/zod/objectql.zod.ts(objectui#11276). The only othertypesfile touched is one export line insrc/index.ts, which that branch does not touch.What each writer can actually see
No writer can see a react-grid-layout placement:
DashboardWithConfigboth renderDashboardRenderer, which places widgets by span and array order and computes nox/y.DashboardEditor's own grid places widgets by array order.So all three seed from
defaultWidgetPlacement(index). That is exactly whereDashboardGridLayoutputs a widget with nolayout. Each writer says this in a comment at the call.Measurements
@objectstack/spec17.5.0, installed):layout: { w: 6 }→ REFUSE,invalid_typeatlayout.x,layout.y,layout.h.{ h: 3 }→ refused atx,y,w. Four numbers → ACCEPT. No box → ACCEPT.5ad9f5dcwith the new pins kept, all inside anEXIT INT TERMtrap. Result:Tests 8 failed | 3 passed. The three that pass are the controls, which hold on both trees. The stored boxes were{ w: 6 }(Studio),{ h: 2 }, and{ w: 2 }(theDashboardWithConfigslider, seeded at 1). The inputs showed'1'. Restored withgit checkout HEAD, then confirmed:git diff HEADempty and all three blob hashes equal HEAD.Test Files 4 passed (4),Tests 18 passed (18).DashboardEditor({ ...widget.layout, w: … }) and rantsc --noEmit: exit 2,TS2322: Type '{ w: number; x?: number | undefined; y?: number | undefined; h?: number | undefined; }' is not assignable to type 'DashboardWidgetLayout'. So the cast was what hid the write. Restored and checked by hash.?only=dashboard), Chromium, no backend. I clicked thekpi_revenuewidget (it has nolayout), filled Width = 6, and read the stored draft:3/4before the edit; storedlayout: { x: 0, y: 0, w: 6, h: 4 }; the renderer drewgridColumn: span 6,gridRow: span 4; parsing with the spec gives nolayout.*issue, and the box parses on a spec-valid widget.1/1; storedlayout: { w: 6 }; the renderer drewgridRow: span undefined; the spec refuses withinvalid_typeatlayout.x,layout.y,layout.h.objectstackcheckout in this container has no install and no build, and standing up its dev server was outside this run's foreground and shared-box budget. The browser legs above measure the same stored draft up to the point where it would be sent.Gates
Toolchain: Node v22.22.2 from
/opt/nvmviaPATH. A plainpnpm installpassed engine-strict, with no override.All gates below were read on head
a831be79(maind59f11c0merged in).turbo run build --filter='@object-ui/app-shell^...' --concurrency=2, 28/28 tasks. This closure contains types, plugin-dashboard and plugin-designer. I used turbo instead ofpnpm --filterso a capped run could resume from cache. The other unbuilt packages were then built socheck:readme-exportscould judge every README.type-checkfor types, plugin-dashboard, plugin-designer and app-shell: allDone, exit 0. Each package'stsconfig.test.jsonlists its new pin (--listFilesOnly). The consumers read the rebuilt@object-ui/typesdist that carries both exports.packages/plugin-dashboard/,packages/plugin-designer/, and the outside suites that import a touched module:Test Files 196 passed (196),Tests 1733 passed | 6 skipped. The outside suites are schema-catalogdashboard-root-title-retired-7624, componentsinline-locale-label-read-sites, two plugin-charts dashboard suites, and sdui-parserdashboard-widget-options-census.dashboard-*plus the export-surface and parity pins, and app-shell'sDashboard*inspector / preview suites plus every inspector suite that importsDashboardWidgetInspector:Test Files 31 passed (31),Tests 486 passed (486).packages/types/plus all of app-shellmetadata-admin/hit the 530 s cap (exit 124). It measured nothing and was narrowed to the set above. CI runs the full set.check:new-line-citations(0 new citation(s)),check:control-bytes,check-changeset-presence(11 source file(s) of 4 releas…, 1 changeset added),changeset:check,check-changeset-overwrite,check-changeset-claims,check:readme-exports(561 real, 0 wrong-path, 0 fabricated, 0 unbuilt),check-vi-mock-specifiers/-inherit/-override-shape,check-test-path-roots,check:doc-examples,check-doc-snippet-types,check-doc-links,check-doc-fence-languages,check:unreferenced-sources,check:self-import,check:esm-specifiers,check:phantom-deps,check-type-check-coverage,check:eager-closure.check:doc-example-ids,check:doc-example-readers,check-doc-component-types,check-doc-expression-carriage,check-lint-coverage,check:published-dist,check:side-effects-array,check:entry-guard,check:spec-symbols.check:node-esm-loadexited 1 on provenance: it refused two entries the shared turbo cache replayed from other worktrees (@object-ui/auth,@object-ui/react-runtime). Neither is touched here, and both are NOT MEASURED. Its load leg imported 32 of 39 entries, including@object-ui/typesand@object-ui/plugin-designer. app-shell and plugin-dashboard are unbundled-Node unsupported by ruling.eslint --format jsonon the 11 changed.ts/.tsxfiles: 11 results, 0 errors, 31 warnings. The warnings are the same per-file counts base5ad9f5dcgives the five modified files. The two new files add none.lintiseslint .under the rooteslint.config.js.parserOptions.project/projectService), and no rule ineslint-rules/reads the filesystem. The fullpnpm lintis CI's.check-governed-queue-guard --testover the 15 changed paths → NOT GOVERNED.Acceptance notes
DashboardRenderergives a widget with nolayouta span of its own: 3 × 2 for a metric, 6 × 4 otherwise. That is not the spec-stated auto-placement (3 × 4) thatDashboardGridLayoutuses and that the editors now seed from, though its comment says it matches. One visible effect in Studio: after a width edit, a metric that had nolayoutgetsh: 4, where the preview had drawn it 2 rows tall. Not changed here. Carrier: none.DashboardWithConfig's save payload is the documented flattenedlayoutW/layoutHwith nox/y, and the host un-flattens it. Neither objectui nor objectstack has a host for it. The shape is unchanged. For a widget with nolayoutthat the author did not resize, the values are now 3 / 4 where they were 1 / 1. Carrier: none.DashboardEditor'saddWidgetseeds its own complete box (x: 0,y= index,w=columns ?? 2,h: 1). It is spec-valid and not changed here.DashboardWidgetInspector.test.tsxused "height = 4" as its unrelated edit. The input now shows 4 for a widget with nolayout, so that edit fired no change. It now edits 2, with a comment saying why.Session:
https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42(seatdomain:ui#2, claim on the card).Generated by Claude Code