Repository navigation
feat(fields,plugin-grid): a declared object-ref / recipient-picker widget names what the value references in every list (objectui#12613) - #12646
Conversation
…dget picks a read cell that names what the value references (objectui#12613) resolveCellRendererType promotes a textual field declaring widget 'object-ref' or 'recipient-picker' to a read cell registered under the widget key. The object-ref cell draws the object's label from the catalog ObjectRefField lists (one read per data source), with the picker's "Label (name)" spelling as its tooltip. The recipient cell reads the row's recipient_type through a new optional CellRendererProps.dependentValues and names the recipient with the picker's own kind table, batched per directory object with one $in query. A value nothing names draws muted with the shared unresolved-reference mark; a call site that passes no row draws the text it drew before. The grid passes the widget hint and the row on every column path. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
…lls on every grid path and classify them in the cell censuses (objectui#12613) The grid case draws a neutral object declaring the same two widget hints through column paths A-D, with a plain text control column holding the same strings. The object-literal and Badge-fit censuses name the two new registered types. The directory and catalog modules read the DataSource type instead of any; README and changeset describe the new cells and the optional dependentValues prop. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
…aring-rule-labels
❌ 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. Which half objected:
📦 Bundle Size Report
Size Limits
|
…aring-rule-labels
…ist, through one catalog mechanism (objectui#12613) Every packages/fields module rides the console's eager ui-components chunk, and the batched $in reader for the record kinds put that chunk over its per-chunk ceiling. The record kinds now read the picker's one candidate query (moved into recipientKinds as loadRecipientCandidates and shared with the picker), so all five lists go through one catalog-per-data-source mechanism and the directory is half its minified size. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
…pp-shell, so the eager ui-components chunk keeps only the rule and the keys (objectui#12613) Every packages/fields module rides the console's eager ui-components chunk whatever imports it lazily, and that chunk's per-chunk ceiling refused the label directory. @object-ui/fields now keeps the widget promotion, the two keys with a text default face (the RECORD_POINTER_CARD_TYPE arrangement) and the optional dependentValues prop, and publishes the edit widgets' reads and kind table plus UnresolvedReferenceMark. @object-ui/app-shell registers the naming faces under the two keys and loads them, with the directory, lazily on the first cell that draws one. The grid test now probes the grid's half of the contract (key and row on every path); the faces are pinned in app-shell. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
…aring-rule-labels
✅ 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
|
…he object face draws its icon, and fields publishes only what the face imports (objectui#12613) The app-shell directory reads record-kind recipients by exact id, one $in query per kind per tick, and builds the label at render with the viewer's denied fields removed (withoutDeniedFields), so a recipient past the picker's 500-row candidate page is named. The object face reads the console metadata store's object list for label and icon and draws the icon with LazyIcon. @object-ui/fields stops publishing loadObjectCatalog and loadRecipientCandidates: the widgets keep their own reads inline, and only the kind table, recipientCandidateLabel, objectRefOptionLabel and UnresolvedReferenceMark remain exported for the face. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
|
Generated by Claude Code |
✅ 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
|
…aring-rule-labels # Conflicts: # packages/plugin-grid/src/ObjectGrid.tsx
✅ 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: Face: the review is owed by the Inputs:
The seat's earlier browser check on ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 — ACCEPT ·
|
Fixes #12613
Clause-②: yes — corrected from the claim's provisional
no(claim6214243680). Measured on the builtpackages/fields/dist/index.d.tsatd47fb07: six new exported names (UnresolvedReferenceMark,objectRefOptionLabel,RECIPIENT_RECORD_KINDS,RECIPIENT_REGISTRY_KINDS,FIELD_RECIPIENT_TYPE,recipientCandidateLabel), and two widened members:CellRendererPropsgains the optionaldependentValues, a string-keyedRecordofunknownvalues (the generic is spelled in words because the body sanitizer drops angle-bracket shapes), andresolveCellRendererType's object arm gainswidget?: string.@object-ui/app-shell'sdist/index.d.tsgains only a side-effect import, and itspackage.jsonsideEffectsnames the new registration module.@object-ui/plugin-grid's published closure is unchanged. Stored values and the authored metadata accept set are unchanged.What changed
The platform already declares the display intent:
sys_sharing_rule.object_nameisField.text({ widget: 'object-ref' })andrecipient_idisField.text({ widget: 'recipient-picker', dependsOn: ['recipient_type', 'object_name'] })(objectstackorigin/main43567a8fc). The grid ignored it:resolveCellRendererTyperead onlytypeandformat, so both columns drew through the text cell. The producer is right; the renderer did not read a declared key. So the fix is in the renderer, keyed on the declaredwidget, never on an object or field name.@object-ui/fields(the rule)resolveCellRendererTypepromotes a textual field that declareswidget: 'object-ref'orwidget: 'recipient-picker'to the cell registered under that widget key. Other widgets (filter-condition,position-ref, …) and non-textual types keep their answer.RECORD_POINTER_CARD_TYPEarrangement). A host without the label faces therefore draws exactly what it drew before.CellRendererProps.dependentValues(optional): the row, the cell-side counterpart of the edit widget'sdependentValues. A call site that passes nothing gets the text it drew before.widgets/recipientKinds.ts. It is published withrecipientCandidateLabel,objectRefOptionLabel(the picker'sLabel (name)spelling) andUnresolvedReferenceMark, for the host face. The edit widgets keep their own reads inline, exactly as onmain, and list and store what they did.@object-ui/app-shell(new: the label faces)WidgetRefCellFaces-*.js, outside the eager closure.ensureType('object'), the sameGET /meta/objectlist the picker offers), and the tooltip readsLabel (name). The icon is drawn as the console's navigation draws an object's (LazyIcon, falling back to the Database glyph).$inquery per kind (chunked at 100).withoutDeniedFields), as the lookup picker drops them.fieldkind is read off the shared object's schema.@object-ui/plugin-gridresolveGridCellRenderingcarries thewidgethint (GridCellFieldLike.widget). Column paths A–C pass it explicitly, and path D and the panel pass the whole field def.dependentValues.declaredType. The grouped-band region objectui#12633 changed is untouched.Why app-shell holds the faces: every
packages/fieldsmodule rides the eagerui-componentschunk, whatever imports it (scripts/vite-ineffective-dynamic-imports.ts). That chunk's per-chunk ceiling refused round 0's label reader: 289,748 / 289,000 gzip bytes measured on round 0's head.mainalone sat at 288,585 at8ed7e5a.Premise measurements (dispatch §2)
recipient_type/access_levelselects are onsys_sharing_rule, and the fourlistViewsname the columns as strings (path B).origin/mainby ablation: with the widget promotion disabled, the promotion cases go red (below)./api/v1/security/explain. The faces reuse the edit widgets' kind table and spellings instead.access_levelalready draws as a chip:SelectCellRendererdraws a Badge per option label. The platform declares no option colour, and the permission matrix's tones are private functions insidePermissionMatrixEditor, so no generic cell is coupled to them.Card targets
recipient_idalso shows therecipient_typecolumn beside it, and the chip's text is that sibling select's translated option label, which a cell does not hold.Tests
packages/app-shell/src/views/widget-ref-cells/__tests__/WidgetRefCellFaces-12613.test.tsx.sales_managerrenders "Sales Manager".$inquery per kind.packages/plugin-grid/src/__tests__/widgetHintedColumns-12613.test.tsxpins the widget key and the row on paths A–D with probe faces.packages/fields/src/__tests__/widgetRefCells-12613.test.tsxpins the promotion, the default faces and the published spellings.d47fb07: the targeted 21 files →Test Files 21 passed (21),Tests 572 passed (572). Ablations on the committed head (scripts/ablation-replace.mjs, each restore proven: blob == HEAD,git diff HEADempty):withoutDeniedFieldsremoved → 1 red.packages/fields/,packages/plugin-grid/andpackages/plugin-detail/suites (751 files, 8776 tests green). Round 1 ablations: promotion guard disabled → 7 red across the three files; therecipient-pickerregistration deleted → 2 red.object-ref/recipient-pickerare classified incellRenderers.objectLiteral-8596and insummaryChip.badgeFitCensus-8464(whose fit-side count moves 36 → 38).Gates (local subset, on
d47fb07)turbo run build --filter='./packages/*'0 ·fields/app-shell/plugin-gridtype-check 0 ·pnpm --filter @object-ui/console build0 ·node scripts/check-eager-closure-budget.mjs0 ·pnpm check:sdui-registration-pins0 ·check-side-effects-array0 ·check-phantom-dependencies0 · eslint on the changed files: 0 errors ·check-changeset-presence0 ·check:new-line-citations0 new · governed-queue guard NOT GOVERNED · control-byte scan: none.Merge round, head
f667f86(mergesorigin/mainf78efdd; report6216423846): one conflict, inpackages/plugin-grid/src/ObjectGrid.tsxagainst objectui#12608. The same seven cell closures changed on both sides. Each keeps main'spointerFace?.(row) ??first, and this branch'sdependentValues={row}on the fallback renderer element. The 21 targeted files plusObjectGrid.recordPointerCell-12608→Test Files 22 passed (22),Tests 575 passed (575). The package build, the three type-checks, the console build andcheck-eager-closure-budgetexit 0.check:sdui-registration-pins,check-side-effects-array, eslint on the resolved file,check-changeset-presenceandcheck:new-line-citationsexit 0.Bundle (on
f667f86):ui-components288,876 / 289,000 gzip bytes; eager closure 3118.4 KB of 3119.4 KB.main's v18 dependency bump took most of the aggregate. Ond47fb07the closure was 3096.6 KB. The faces chunk is lazy. No ceiling, budget file, allow-list or chunk group was touched.Browser check (1280 px): run by the seat on
d47fb07. The readings are in the seat's ACCEPT on this PR.Surface notes
@object-ui/app-shellwas added on the dev's measurement (corrected claim6214243680):src/views/widget-ref-cells/**,src/index.ts(the side-effect import) andpackage.jsonsideEffects. The changeset lists@object-ui/app-shell: minor.packages/plugin-detail/src/__tests__/summaryChip.badgeFitCensus-8464.test.tsx, and the@object-ui/fieldsREADME cell-resolution example (AGENTS.md Add automated testing infrastructure and CI/CD workflows #2).Acceptance notes (not done here; carrier: none)
DetailSection(@object-ui/plugin-detail) resolvesobject_nameto the object face but passes no row, so a record page'srecipient_idstill reads as text. PassingdependentValues={data}there is one line.@objectstack/spec'swidgetdescription says "Form widget override"; list cells now read it for these two hints too.f667f86,ui-componentshas 124 gzip bytes of headroom, and the eager closure as a whole has 1.0 KB (vendor-objectstack0.0 KB). The next page-load change of any size may trip one of them. The ceiling change is objectui#12653's (in flight, another seat); nothing was raised here.Session:
https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4Generated by Claude Code