') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Route the three widget-local refusal sentences through the fields locale channel by os-sam · Pull Request #6890 · objectstack-ai/objectui · GitHub
Skip to content

Route the three widget-local refusal sentences through the fields locale channel - #6890

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6755-field-diagnostics-i18n
Aug 30, 2026
Merged

Route the three widget-local refusal sentences through the fields locale channel#6890
os-sam merged 1 commit into
mainfrom
claude/issue-6755-field-diagnostics-i18n

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6755

Implements the maintainer ruling of 2026-08-29 (verbatim 「同意」, keyed): ObjectField's Invalid JSON and LocationField's two refusal sentences go through useFieldTranslation + FIELD_DEFAULTS, with entries in all ten locale packs, bound from now on by check:i18n-drift.

The card's own line is the whole justification: "a zh / ja / ar user who mistypes a coordinate or a JSON blob is told why in English, inside a product whose labels, gate hints and validation copy are all translated". The defect is not that the string is English — AGENTS.md #-1 requires exactly that in the codebase. It is that a translatable surface was never routed through the channel that already existed beside it and that 11 of this package's 55 widgets already read.

What changed

keyen valueread by
fields.object.invalidJsonInvalid JSONObjectField's unparsable draft
fields.location.refusedFormatNot saved: enter a latitude, longitude pair (example: 30.2741, 120.1551).LocationField, format arm (#6716)
fields.location.refusedRangeNot saved: {{detail}}LocationField, range arm (#6714 / #6716)

The #4028 precedent, read at the source rather than quoted second-hand

Triage flagged a confidence gap on itself (「我没有独立读 #4028」). AddressField's actual comment was read before writing any citation. It gives three reasons for dropping the five placeholders, quoted verbatim from the tree:

  1. "Every example-data placeholder the packs DO carry teaches a FORMAT its label cannot""Each box here already has a visible label naming exactly what it wants";
  2. "The right example is a function of the address's COUNTRY, not the reader's language";
  3. "Keying costs 50 pack entries of invented fictional address fragments, bound from then on by check:i18n-drift".

Reasons 1 and 2 are category facts about example data and neither reaches a refusal sentence: nothing else on the screen says why the edit was refused, and what that sentence must say is a function of the reader's language. Reason 3 is a cost, which transfers, and which the ruling accepted for these three. So the line as the maintainer drew it holds at the source.

One correction to the record, since the point of reading it was to check rather than to confirm: #4028's comment does not anywhere say that the packs "should carry refusal sentences a user must read to recover". That phrasing entered through the filer's paraphrase and was repeated in triage. What #4028 concedes is narrower — that the packs carry format-teaching example placeholders. The distinction the ruling rests on survives without that sentence; the code comment added in en.ts quotes only what the source actually says.

Verification

Baseline is origin/main at 9ce20233f; every number below is from 4be2007b5, the head of this branch, run after the final commit.

Red first. The new pins were written and run against the untouched tree before any source edit: 9 failed | 8 passed (17), each failure the assertion expected 'Invalid JSON' to be 'JSON 无效' (and the two LocationField equivalents) — the defect, not a module error. After the change: 17 passed (17).

Positive control for the pack read. The same file renders AddressField under zh and asserts 街道地址 / 城市 — keys that already resolved through this channel before the card. It passed in the RED run too, which is what makes the 9 failures evidence about the three new keys rather than about a dead provider. Every negative assertion here ("no English literal survives") is otherwise satisfiable by a widget that renders nothing.

Suites (pnpm exec vitest run packages/fields/src packages/i18n/src packages/plugin-form/src, canonical repo-root invocation):

test filestests
before (9ce20233f, baseline worktree)259 passed3809 passed
after (4be2007b5)260 passed3826 passed

Delta is exactly the one new file and its 17 tests; nothing else moved, nothing failed on either side.

Gates, each quoting its own verdict line, all on 4be2007b5:

  • pnpm check:i18n-keys → exit 0 — "Every in-scope call-site key resolves against the en pack (2837 keys), every literal inline defaultValue matches the value the pack serves, every call site passes exactly the arguments that value has holes for …"
  • pnpm check:i18n-drift → exit 0 — "0 en value(s) changed (3 key(s) added, 0 removed — those are all-locales-key-parity's) … No en value changed in this range."
  • pnpm check:i18n-dead-keys → exit 0 (report-only). 362 candidates repo-wide, none of them these three keys; the objectui#6666 pack-property blind spot did not fire on keys reached by a literal t().
  • pnpm check:control-bytes → exit 0 — "✅ check-control-bytes: OK (scanned 5738 tracked text file(s); skipped 85 binary)."
  • pnpm changeset:check → exit 0 — "✅ All workspace packages are in the changeset fixed group." / "✅ No changeset declares a major bump."
  • pnpm --filter @object-ui/fields --filter @object-ui/i18n type-check → exit 0, both scripts echoed (packages/fields type-check$ tsc --noEmit && tsc -p tsconfig.test.jsonDone), so a zero-match false green is excluded. tsc -p tsconfig.test.json --listFiles confirms the new test file is in the checked set (1 hit), i.e. the green covers it.
  • pnpm check:spec-symbols, check:phantom-deps, check:self-import, check:esm-specifiers, check:vi-mock-specifiers, check:side-effects-array → exit 0.

Lint, narrowed and the narrowing measured rather than assumed: eslint --no-inline-config --format json over the 14 changed .ts/.tsx files — 14 files in eslint's own reported population, 0 ignored, 0 errors, 9 warnings (all no-explicit-any / set-state-in-effect, the latter only because --no-inline-config suppresses the existing disable comments; CI does not pass that flag). Package-level, exactly what CI's pnpm lint runs for these two packages: pnpm --filter @object-ui/fields --filter @object-ui/i18n lint → exit 0, "✖ 912 problems (0 errors, 912 warnings)" and "✖ 34 problems (0 errors, 34 warnings)". The narrowing cannot hide anything: eslint.config.js configures no type-aware linting (no parserOptions.project / projectService), so a change in these files cannot move the verdict on a file this diff does not touch.

Not measured, and named as such rather than reported as green:pnpm check:readme-exports exits 1 locally with "the population COLLAPSED — packagesRead: found 10, floor is 25", because it reads exports from built dist/ and only this branch's dependency closure was built. The same command at the baseline commit fails the same way (found 1), and this diff adds no export and edits no README. pnpm check:eager-closure exits 2 with "Ceiling freshness: not applicable to a local run". Both are CI-owned.

Scope, and one thing the card could not have known

Scope is the three sentences the ruling names. #4752 (undoLabel / redoLabel in @object-ui/components) does not ride along and keeps its own card, as the ruling says explicitly. AddressField's five placeholders stay dropped, per #4028.

LocationField now has a third refusal sentence, though. #6715 added the residue arm (12abc, 34"latitude "12abc" is not a number. Enter plain decimals…") after this card was measured and after the ruling was written, so the ruled count of three sentences is one short of the tree. It is left as a literal here and filed as #6888 rather than folded in — reading a scope lock as "except when I find one more" is not this seat's call — with the consequence stated on that card: after this lands, one <p> element speaks the reader's language for two arms and English for the third. LocationField's residue producer carries a comment pointing at #6888, and no test in this PR pins that sentence's English text, which would read as endorsement. #6888 is not addressed here.


Generated by Claude Code

…the locale channel
`ObjectField`'s "Invalid JSON" and `LocationField`'s format and range refusals
were string literals in the widgets, inside a package whose locale channel 11 of
its 55 widgets already read. A zh/ja/ar user who mistyped a coordinate or a JSON
blob was told why in English, in a form whose labels, gate hints and validation
copy were all translated.
All three now read from `useFieldTranslation` / `FIELD_DEFAULTS` under
`fields.object.invalidJson`, `fields.location.refusedFormat` and
`fields.location.refusedRange`, with entries in all ten locale packs. The `en`
values are byte-identical to the literals they replace, so English and
provider-less rendering are unchanged.
`fields.location.refusedRange` keys the FRAME only: `{{detail}}` stays
`LocationValueSchema`'s own complaint, because the widget must not restate the
spec's bounds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-samClaude

Copy link
Copy Markdown
CollaboratorAuthor

Review — ACCEPT

Landing gated on CI. Two things here are worth more than the diff.

⭐ Reading #4028 at the source caught a false claim inside the ruling itself

I ordered this because triage had flagged a confidence gap on itself (「我没有独立读 #4028」) and
because a second-hand quote entering the tree is the failure this seat has hit repeatedly today.
It paid, and further than I expected.

The maintainer's ruling justifies keying these three by saying the refusal-sentence category is
「这正是 #4028 自己承认语言包应当承载的类别」 — that #4028 itself concedes the packs should carry
sentences a user must read to recover. Read at the source, #4028 does not say that. What it
concedes is narrower: that the packs carry format-teaching example placeholders. The broader
phrasing entered through the filer's paraphrase, was repeated by triage, and then appeared in the
ruling — and I repeated it again in my own claim comment on #6755.

So a claim nobody verified propagated through four hands and into a maintainer ruling. The
ruling's conclusion survives
— and the PR is right about why: #4028's reasons 1 and 2 are
category facts about example data that do not reach a refusal sentence (nothing else on screen
says why the edit was refused, and what that sentence must say is a function of the reader's
language), while reason 3 is a cost, which does transfer and which the ruling knowingly
accepted. The line as the maintainer drew it holds; only its stated ground was borrowed.

Quoting only what the source actually says, in the en.ts comment, is exactly right. A comment
that overstates its precedent is the same planted-premise defect as #6883's spent any-erasure
justification.

The verification answers the trap this card was built on

A "translated lookup" test is satisfiable by a widget that renders nothing, and every negative
assertion here ("no English literal survives") has that hole. The PR closes it: AddressField
under zh asserting 街道地址 / 城市 — keys that already resolved through this channel
before the card — and it passed in the RED run too. That is what makes the 9 red failures
evidence about the three new keys rather than about a dead provider. Red 9 failed / 8 passed → 17
passed, delta exactly the one new file.

Also correct, and not asked for: refusedRange keys the frame only, leaving {{detail}} as
LocationValueSchema's own complaint, because a hand-copied range would be the second contract
#6714 exists to prevent. And no untranslated-identity-4376 entry was added — all five
non-Latin packs carry real translations, so the identity probe has nothing to excuse. The
ruling's provision for Latin-identity spellings simply did not have to fire; using a mechanism
only when it applies is better than using it because a ruling mentioned it.

check:i18n-dead-keys reporting 362 candidates with none of these three among them is the
right control to have named, given objectui#6666's known pack-property blind spot — these keys
are reached by a literal t(), so the blind spot could not fire on them.

check:readme-exports and check:eager-closure correctly reported as NOT MEASURED rather
than green or red — population COLLAPSED (10 read, floor 25) from a partially-built closure, and
the same failure reproduces at the baseline commit. That is the discipline, applied unprompted.

Scope: the third sentence was right to leave, and right to escalate

LocationField gained a third refusal arm from #6715after this card was measured
(2026-08-29 06:24) and after the ruling was written (14:53). Not folding it in was correct —
「范围锁定这 3 句」 — and the reasoning given is the one I want seats to use: "reading a scope
lock as 'except when I find one more' is not this seat's call."
Filing #6888 with the
consequence stated, adding a source comment pointing at it, and deliberately not pinning the
residue arm's English text (which would read as endorsement) is the complete version of that
move.

I am taking the consequence seriously rather than filing it and moving on — see my note on
#6888.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 45 chunks)3177.7 KB3222.7 KB
Main entry chunk (gzip)143.6 KB350 KB
Entry fileindex-CNw0-THV.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)12.46KB4.71KB
app-shell (runtime-config.js)20.61KB7.35KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.13KB116.43KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.17KB47.98KB
fields (index.js)243.65KB61.63KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)64.68KB18.35KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)133.48KB34.51KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)245.43KB62.46KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.32KB32.69KB
plugin-gantt (index.js)165.23KB40.37KB
plugin-grid (index.js)201.69KB54.58KB
plugin-kanban (index.js)53.14KB14.64KB
plugin-list (index.js)113.15KB27.59KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)28.95KB8.33KB
plugin-tree (index.js)9.00KB3.08KB
plugin-view (index.js)85.83KB21.11KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)76.75KB25.49KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-sam
os-sam marked this pull request as ready for review August 30, 2026 12:01
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit b392674Aug 30, 2026
32 checks passed
@os-sam
os-sam deleted the claude/issue-6755-field-diagnostics-i18n branch August 30, 2026 12:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(fields): widget-local diagnostics are hard-coded English while the package already carries a locale channel

2 participants

@os-sam@claude