Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-admin): Studio authoring UX — typeable identifiers + 5 dogfood fixes - #1926
Merged
Conversation
The object designer's Name field and every field's API-name input are controlled inputs whose onChange re-normalizes on every keystroke via the strict `toFieldName`, which trims a trailing "_". Typing "repair_ticket" therefore produced "repairticket": the moment "_" is typed the value is "repair_" → trimmed to "repair" → the underscore is gone before the next letter arrives. Non-Latin (e.g. Chinese) authors hit this hardest — their label can't derive a Latin slug, so they MUST type the identifier by hand, and could only ever produce single-token names. Add a prefix-stable `toFieldNameLoose` for live keystroke input: it keeps a single trailing "_" (itself a spec-valid identifier char) so typing can continue, and returns "" instead of the "field" placeholder on empty/CJK input so clearing the box actually clears it. Wire the object Name and field API-name inputs to it. Keep strict `toFieldName` for complete-string callers (group-key generation). Switch the label→name derivation to `slugify` (its documented contract: empty for non-Latin → prompt for manual entry) instead of silently seeding "field". Tests: 32 pass, incl. a controlled-input typing simulation that reproduces the old "repairticket" regression and proves the fix yields "repair_ticket". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
derive, picklist rows, source fallback, read-only wording
Five dogfood-found Studio fixes (companion to the identifier-input fix):
F1 — Package switcher didn't show a newly created package until a full
page reload. `useSelectorOptions` now refetches on dropdown-open and on a
global `objectui:packages-changed` event, which `PackagesPage` dispatches
after a successful create.
F3 — A field's API name never derived from its label (only the object
Name did). Added an optional `onBlur` to `InspectorTextField`; the field
inspector derives the API name from the label on blur — but only while the
name is still an auto-generated default (e.g. `text`, `text_2`), so manual
names are never clobbered. Uses the trailing-trimming `slugify` (correct
for a complete string) and stays empty for non-Latin labels.
F4 — Adding a picklist (or clicking "Add value") created an empty option
row that immediately failed spec validation with a developer-oriented
"System identifier must be at least 2 characters" error. `newField` now
seeds no options, and `OptionsEditor` keeps blank rows in a local buffer,
persisting only rows whose value is non-empty.
F5 — The Source tab rendered a permanent blank when Monaco's (CDN-loaded)
core or its web workers were unavailable — offline / air-gapped / CSP
installs. `JsonSourceEditor` now detects that nothing painted and falls
back to a plain, editable textarea showing the JSON source.
F6 — A user's own object, after publishing it to a project package, showed
"This item is shipped by a code package and cannot be modified at runtime",
which is misleading. Reworded `artifactLockedBanner` (en + zh) to "provided
by an installed package … edit it in its source package and republish — or
create a new {type} from scratch", which is accurate for both platform and
project packages and states the actual edit path.
All five verified in-browser against a live backend via the dev console.
object-fields-io unit tests: 32 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Uh oh!
There was an error while loading. Please reload this page.
os-zhuang added a commit
that referenced
this pull request
Jun 24, 2026
…estore F6 wording (#1941) * test(metadata-admin): add data-testid hooks to the Studio object designer Stable selectors for the live-e2e/dogfood gate (no behavior change): object-name-input, field-apiname-input, field-label-input (via an optional InspectorTextField testId prop), package-id-input/package-name-input, package-switcher, source-editor, metadata-validation-banner, readonly-banner. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(metadata-admin): Studio object-designer dogfood gate (F1-F6) A "drive the Studio designer as a user" regression gate for the UX classes fixed in #1926 — the kind static gates (build/unit/spec-liveness) miss because the break only appears when a real user drives the real controlled inputs against the real spec validator. Live e2e (e2e/live/studio-object-designer.spec.ts, runs against the real stack): F2 — object Name accepts a TYPED underscore. Must pressSequentially, not fill(): .fill() sets the value in one shot and hides the per-keystroke trim. Proven to go red when the trailing-trim bug is reintroduced. F3 — a field API name derives from its label on blur. F4 — adding a picklist + an empty option row does NOT trip spec validation. Live validation is debounced, so the negative assertion settles first; proven red when empty rows are persisted again. F1 — a newly created package appears in the switcher without a reload (creates + cleans up a throwaway package). Component/unit (fast layer, no backend): F5 — JsonSourceEditor falls back to an editable textarea when Monaco can't paint (offline/CSP/worker-blocked). fallbackDelayMs added so the test doesn't wait the full grace period. F6 — artifactLockedBanner wording (en + zh): not "shipped by a code package", and states the real edit path. This gate immediately earned its keep: the F6 test failed on first run because a parallel PR (#1937, branched off pre-#1926 main) had silently reverted the banner wording. Re-applied here (i18n.ts) — exactly the regression class the gate exists to catch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Six fixes for the Studio metadata designer, found by dogfooding the console as a business user (building an object end-to-end, then exercising it at runtime). The platform's runtime data layer proved solid (CRUD + required/picklist/number/date validation all enforced server-side); every issue was in the authoring UX.
Each fix was verified in the browser against a live backend via the dev console (
pnpm dev→ Vite on :5181,/apiproxied to a real :3000 instance)._while typing (repair_ticket→repairticket); non-Latin labels collapse to the literalfield. Root cause:InspectorTextFieldis a controlled input that re-runs the stricttoFieldNameon every keystroke, and its trailing-_trim eats the underscore the instant it's typed.toFieldNameLoose(keeps a trailing_, returns''notfieldon empty) for live-typing inputs; keep stricttoFieldNamefor complete-string callers (group keys); switch label→name derivation toslugify(its documented "empty for non-Latin → prompt" contract).useSelectorOptionsrefetches on dropdown-open and on a globalobjectui:packages-changedevent, whichPackagesPagedispatches after a successful create.onBluronInspectorTextField; the field inspector derives the API-name from the label on blur — only while the name is still an auto-generated default (text,text_2, …), so manual names are never clobbered.newFieldseeds no options;OptionsEditorkeeps blank rows in a local buffer and persists only rows whose value is non-empty.JsonSourceEditordetects that nothing painted (.view-line) and falls back to a plain, editable textarea showing the JSON source.artifactLockedBanner(en + zh) to "provided by an installed package … edit it in its source package and republish — or create a new {type} from scratch" — accurate for both platform and project packages, and states the real edit path. (The read-only-after-publish behavior itself is intentional governance and is unchanged.)Verification
object-fields-io.test.ts— 32 pass, incl. a controlled-input typing simulation that reproduces the oldrepairticketregression and proves the fix yieldsrepair_ticket.work_ordertyped keystroke-by-keystroke through the real onChange pipeline), F3 (Asset Code→asset_codeon blur), F4 (no validation banner on empty rows), F5 (textarea fallback shows the draft JSON), F6 (accurate banner on a published object).Notes
🤖 Generated with Claude Code