Skip to content

fix(core): one home for the number-display policy — and percent stops drifting between cell and measure (#4576) - #4589

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-4576-one-number-home
Aug 13, 2026
Merged

fix(core): one home for the number-display policy — and percent stops drifting between cell and measure (#4576)#4589
yinlianghui merged 2 commits into
mainfrom
claude/issue-4576-one-number-home

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Fixes #4576

Two stages, one PR, per the PM ruling on the card: the formatDisplayNumber down-move (PR #4577's open question 1) lands first, and the percent split closes on the moved home.

Stage 1 — the down-move

formatDisplayNumber, shouldGroupDisplayNumber and DisplayNumberFormatOptions move from @object-ui/i18n (src/utils/number-display.ts) into @object-ui/core (src/utils/number-display.ts). @object-ui/i18n re-exports all three under the same names; nothing published was removed.

Cycle check: none. packages/core contains zero import/export … from '@object-ui/i18n' statements (the only matches were four prose mentions inside dataset-format.ts's own comments, all rewritten here). core's dependencies are @object-ui/types, @objectstack/formula, @objectstack/spec, lodash, zod; @object-ui/types depends only on @objectstack/spec and zod. @object-ui/core added to @object-ui/i18n's dependencies; check:phantom-deps green.

Importer census — 7 sites, 3 distinct paths, all preserved:

importer path preserved by
packages/components/.../basic/elements.tsx @object-ui/i18n entry re-export
packages/plugin-dashboard/src/MetricWidget.tsx @object-ui/i18n entry re-export
packages/fields/src/index.tsx @object-ui/i18n entry re-export
packages/fields/src/widgets/CurrencyField.tsx @object-ui/i18n entry re-export
packages/i18n/src/utils/index.ts ./number-display file kept as re-export
packages/i18n/src/index.ts ./utils/index unchanged
packages/i18n/src/__tests__/number-display.test.ts ../utils/number-display file kept as re-export

The relative path is why packages/i18n/src/utils/number-display.ts survives as a re-export file rather than being deleted. Identity is pinned, not assumed: number-display.reexport-identity.test.ts asserts fromEntry === fromCore and fromModule === fromCore by reference — a second copy would pass every behavioural assertion in the repo and fail only there — plus a compile-time both-directions assignability pin on the option type.

dataset-format's formatNumberInLocale is now a thin adapter over the moved formatDisplayNumber instead of a hand-mirrored Intl implementation. #4577 measured that mapping lossless across 32,760 combinations; re-verified here with a spot-check harness over 7 locales x a value grid x formats x currencies x percentScales — 5,390 combinations, 0 byte-level differences — and the whole dataset-format suite is byte-identical apart from the declared pin moves below.

Stage 2 — the percent split, and a route the ruling did not anticipate

formatMeasure appended a literal %; it now renders the locale's own percent convention — the same one formatPercent has used since #4553 / PR #4565. percentDisplayValue's doc promise that a percent renders identically "as a row value and as an aggregated metric — the two surfaces can never drift" is true again, and the comment now records that it was briefly false and why.

Measured convention move (en, ja, zh byte-identical, which is why this was invisible in an English session):

locale before after
de-DE 1.234,5% 1.234,5 + U+00A0 + %
fr-FR / ru-RU / sv-SE / cs-CZ / fi-FI no space U+00A0 before the sign
tr-TR 1.234,5% %1.234,5 — the sign moves to the FRONT
ar-EG ASCII % U+066A + U+061C
en-US / ja-JP / zh-CN 1,234.5% 1,234.5% (unchanged)

⚠️ The ruling's stage-2 premise did not survive measurement — the mechanism changed, the goal did not

The ruling named Intl's style: 'percent' (what formatPercent uses) and accepted a declared cost: "extreme-magnitude en forms move (the #4577 measurement — all >= ~1e20%)", with "ordinary-magnitude en percent MUST stay byte-identical" and "measure the exact threshold". I measured the threshold first, and there is no magnitude threshold. The divergence is rounding-tie-keyed, not magnitude-keyed, and it reaches all the way down:

  • 27,581 of 1,200,013 ordinary-magnitude en-US forms move on a tie-dense grid (0.005 steps to 2,000, decimals 0/1/2). Smallest mover: display 0.175 at 2 decimals, 0.18% becoming 0.17%.
  • A 2,300,045-case scan (every integer 0..200,000, four fractional grids, 500k random doubles) put the smallest diverging magnitude at 0.175, not 1e20.
  • The cause: style: 'percent' expects a FRACTION, so percentage points must be divided by 100 for Intl to multiply them straight back. 0.175 / 100 lands below the tie, so it rounds down. The en moves fix(core): dashboard measures follow the display locale (#4566) #4577 saw at 1e21 / MAX_SAFE_INTEGER are the same artefact at the other end of the range.

That is STOP condition 3 of the ruling ("the percent unification measurably moving an ordinary-magnitude en form"), so I did not implement it. Instead I measured whether the locale's convention is reachable without the round trip, and it is:

style: 'unit' / unit: 'percent' / unitDisplay: 'narrow' produces a byte-identical percent affix to style: 'percent' across all 171 locale tags tested — 171 identical, 0 different — including the German no-break space, Turkish's prefix position, Arabic's own sign plus U+061C, and the Bengali/Marathi percent patterns that group Western-style where their decimal patterns group by lakh (verified A == D there, so that is the CLDR percent convention, not a quirk of the route). Against the literal-suffix form it moves 0 of 1,200,013 en-US forms, ordinary and extreme alike.

So this PR delivers the ruling's stated goal — one percent convention across both surfaces, locale-correct NBSP where the locale says so — without incurring the declared en cost at all. The extreme-magnitude behaviour change the ruling authorised is not taken: MAX_SAFE_INTEGER and 1e23 keep every digit, pinned. The mechanism is exposed as a new, self-documenting style: 'percentPoints' on DisplayNumberFormatOptions ('percent' = value is a fraction, unchanged; 'percentPoints' = value is already in percentage points), so the next caller picks the right one by name instead of rediscovering the trap. Please confirm this substitution — it is a deviation from the mechanism the ruling named, made because the ruling's own STOP condition fired on that mechanism.

One gap deliberately left open, and pinned honestly

The two surfaces now agree on the CONVENTION but still differ at rounding ties, because formatPercent keeps the /100 round trip: formatPercent(1.005, 2, 'en-US') is 1.00% while formatMeasure(1.005, '0.00%', undefined, 'whole', 'en-US') is 1.01%. Half-up on the authored decimal is 1.01, so the MEASURE is the faithful one and the CELL is the artefact. Closing it means editing packages/fields' formatPercent, which this card's surface excludes. It predates this card, it is narrower than the split closed here, and it is pinned as an explicitly NOT-a-defect case in percent-cell-vs-measure-4576.test.ts. Filed as #4590.

Red-first, predicted in writing

Predictions are in each test file's header. Reverse verification removed the fix by patch + git checkout -- (never git stash), keeping the new expectations; restore confirmed byte-identical by sha256 across all 7 files.

Test Files  4 failed (4)
     Tests  17 failed | 9 passed (26)

Verbatim, matching the predictions:

  • Cannot find module '../number-display' — the whole percentPoints suite, red at module load
  • AssertionError: expected '60,8%' to be '60,8 %' (de), expected '1.234,5%' to be '%1.234,5' (tr), Expected: "٪" Received: "٥٠%" (ar), de-DE suffix: expected false to be true (affix parity)
  • expected undefined to be [Function formatDisplayNumber] — the re-export identity pin
  • 8 of 12 cross-surface cases in packages/fields

The 9 green on the red side are the must-not-change pins: every en-US case ordinary and extreme, ja-JP, zh-CN, and the rounding-tie cases. Post-fix: 36 passed (36).

Two of my own cases were mislabelled and the red run caught it — a MUST NOT CHANGE case each contained one de-DE assertion that legitimately moves. Both were split out into separately-named moving cases rather than relabelled in place. A third asserted a display magnitude below 1, which formatPercent cannot reach (it scales stored values under 1 by 100); replaced with 1.005, measured reachable on both surfaces.

Must-not-change

  • Ordinary-magnitude en unmoved, proven twice: the 11-assertion MUST NOT CHANGE case, and — the stronger evidence — the en-US assertion sitting beside each of the three moved pins passed untouched through every run.
  • fix(fields): formatPercent groups and follows the display locale — the last tooltip channel (#4553) #4565's formatPercent suite untouched and green; packages/fields' implementation was verified, not edited.
  • Consumption-radius sweep — every package that consumes either formatter:
    • core + i18n + fields + plugin-dashboard + plugin-report: 281 files, 4,492 tests passed (run twice, identical).
    • app-shell: 394 files, 3,725 passed, 1 skipped.
    • plugin-gantt + plugin-grid + components: 231 files, 2,064 tests passed.
    • Before the pin moves these were 2 failed | 4,490 passed and 1 failed | 3,724 passed, so the three moved lines are the entire blast radius across nine packages.
  • integers-stay-String(v), the unknown-currency fallthrough, the malformed-tag retry and fix(core): dashboard measures follow the display locale (#4566) #4577's other deliberate preservations are untouched and pinned.

Pins moved (3, all declared)

All are #4577 / #4575 pins, all assert the OLD literal-suffix German form, all move only where the percent path moves, and each is annotated in place with PIN MOVED (objectui#4576):

  1. dataset-format.locale.test.tsformatMeasure(0.608333333, '0.0%', undefined, undefined, 'de-DE'): 60,8% becomes 60,8 + NBSP + %
  2. dataset-format.locale.test.tsformatMeasure(1, '0.0%', undefined, 'fraction', 'de-DE'): 100,0% becomes 100,0 + NBSP + %
  3. DatasetDefaultInspector.measureLocale.test.tsx (app-shell, a [plugin-report][app-shell] Dataset measures still render in the MACHINE locale — the consumers #4566 could not reach #4575 pin) — the German format sample: 12,3% becomes 12,3 + NBSP + %

The en-US assertion in each of the three cases is unmoved. No other test in the repo moved.

Declared honestly: the third one cost a CI lap. My local sweep covered the five packages the dispatch named and not app-shell, which is also a formatMeasure consumer (#4577's own body names it). CI shard 3/4 caught it; the sweep above was then widened to the formatters' full consumption radius rather than to a named package list.

Verification

  • Build closure first, then the affected packages; whole workspace turbo run build 43/43 successful, 0 error TS.
  • type-check (both tsc passes) green on @object-ui/core and @object-ui/i18n.
  • Consumer sweeps, direction stated: PREFIX = DOWNSTREAM CONSUMERS. --filter '...@object-ui/i18n' (35 packages, 69 tasks green) and --filter '...@object-ui/core' (72 tasks green), 0 error TS in both.
  • Reverse type verification against the REBUILT .d.ts (probe in packages/fields, which resolves @object-ui/i18n through node_modules, not the source-tree paths): style: 'percentPoints' accepted; style: 'percentpoints' rejected with TS2820: Type '"percentpoints"' is not assignable to type '"percent" | "decimal" | "percentPoints" | undefined'. The message quotes the new union, which is what proves tsc read the rebuilt declaration rather than a cached one. Probe removed.
  • eslint vs the origin/main baseline: net zero — core 529 to 529, i18n 35 to 35, fields 801 to 801, 0 errors throughout; the app-shell pin file reports 0 problems on both sides. Baseline measured in a throwaway origin/main worktree, since removed.
  • check:control-bytes, check:phantom-deps, check:changeset-presence, check:changeset-no-major, check:changeset-fixed, check:type-check-coverage, check:lint-coverage, check:i18n-call-site-keys, check:i18n-en-drift, check:spec-symbol-derivation, check:doc-links — all green.
  • Control-byte self-scan of every touched file clean; every NBSP / NNBSP / U+061C in source and tests is a backslash-u escape or the file's existing NBSP constant, never a raw byte.

.d.ts, measured both ways

Built before and after with dist/ and tsconfig.tsbuildinfo cleared between runs:

Never major.


Generated by Claude Code

… drifting between cell and measure (#4576)

`formatDisplayNumber`, `shouldGroupDisplayNumber` and
`DisplayNumberFormatOptions` move from `@object-ui/i18n` into
`@object-ui/core`; `@object-ui/i18n` re-exports all three, so every
existing import path keeps compiling and both spellings resolve to the
same symbol. `dataset-format`'s duplicate `Intl` implementation is gone.

The move is what fixes the bug: `formatMeasure` needed this policy and
could not import it across the React boundary, so it carried a parallel
implementation, and the two drifted — a German session read `1.234,5 %`
in a list cell and `1.234,5%` in a dashboard measure. The measure now
renders the locale's own percent convention.

No numeral moves in any locale at any magnitude: the value is formatted
as percentage POINTS via a new `style: 'percentPoints'` rather than
divided by 100 for `Intl`'s `style: 'percent'` to multiply back, a round
trip measured to move 27,581 of 1,200,013 ordinary-magnitude en-US forms
at rounding ties plus everything from 1e23 up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 13, 2026 3:23pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.7 KB 350 KB
Entry file index-B54PSc01.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 38.46KB 10.17KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 489.33KB 108.47KB
core (index.js) 3.79KB 1.52KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 163.56KB 44.83KB
fields (index.js) 230.37KB 57.17KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.98KB 10.85KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.86KB 12.91KB
plugin-charts (index.js) 62.10KB 17.67KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 121.04KB 31.57KB
plugin-designer (index.js) 212.58KB 42.83KB
plugin-detail (index.js) 239.93KB 60.01KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 164.30KB 40.02KB
plugin-grid (index.js) 189.37KB 50.33KB
plugin-kanban (index.js) 52.74KB 14.53KB
plugin-list (index.js) 111.13KB 27.12KB
plugin-map (index.js) 18.16KB 5.81KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.38KB 11.09KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.09KB 20.56KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.64KB 9.44KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.26KB 0.67KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

…ale convention (#4576)

`DatasetDefaultInspector`'s format-sample pin asserted the literal-suffix
German form `12,3%`. The measure formatter now renders the locale's own
percent convention, so the sample gained the no-break space before the
sign, exactly as the two `dataset-format.locale.test.ts` pins did.

Third and last authorized pin move for this card. The en sample in the
case below it is unmoved, which is what says this is a convention change
and not a numeral one. Caught by CI shard 3/4: my local sweep covered
core / i18n / fields / plugin-dashboard / plugin-report but not
app-shell, which is also a `formatMeasure` consumer. Re-swept the full
consumption radius since: app-shell 394 files / 3,725 tests green, and
plugin-gantt + plugin-grid + components 231 files / 2,064 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.7 KB 350 KB
Entry file index-B54PSc01.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 38.46KB 10.17KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 489.33KB 108.47KB
core (index.js) 3.79KB 1.52KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 163.56KB 44.83KB
fields (index.js) 230.37KB 57.17KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.98KB 10.85KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.86KB 12.91KB
plugin-charts (index.js) 62.10KB 17.67KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 121.04KB 31.57KB
plugin-designer (index.js) 212.58KB 42.83KB
plugin-detail (index.js) 239.93KB 60.01KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 164.30KB 40.02KB
plugin-grid (index.js) 189.37KB 50.33KB
plugin-kanban (index.js) 52.74KB 14.53KB
plugin-list (index.js) 111.13KB 27.12KB
plugin-map (index.js) 18.16KB 5.81KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.38KB 11.09KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.09KB 20.56KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.64KB 9.44KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.26KB 0.67KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[core][fields] A percent renders as 1.234,5 % in a list cell and 1.234,5% as a dashboard measure — two percent conventions for one number

2 participants