Add realm config templates#4973
Draft
backspace wants to merge 42 commits into
Draft
Conversation
Contributor
Preview deploymentsHost Test Results 1 files ±0 1 suites ±0 1h 35m 2s ⏱️ - 1m 27s Results for commit 1580773. ± Comparison against earlier commit ac9bfa4. For more details on these errors, see this check. Realm Server Test Results 1 files ±0 1 suites ±0 10m 26s ⏱️ -2s Results for commit 1580773. ± Comparison against earlier commit ac9bfa4. |
Renders the realm name, icon, and host routing rules so the config card is legible in catalog and stack views, not just the default field-by-field form. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…10056 # Conflicts: # packages/base/card-api.gts
The Realm Settings tile-menu action opened the realm config card behind the still-open chooser overlay, so it looked like nothing happened. Dismiss the chooser after opening, and cover it with an acceptance test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Opens the RealmConfig editor, triggers the routing rule instance chooser, and asserts the catalog realm picker is locked so only same-realm cards can be linked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a sectioned edit template on RealmConfig — Appearance, Host Routing Rules, Advanced — so the realm config form reads as a real settings screen rather than a stack of raw fields. Rework the routing rule editor into a compact `path → instance` row that mirrors the atom's shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workspace A's setup already gets its RealmConfig card via the realmConfigCardJSON helper a few lines above, so the second inline realm.json entry was a duplicate-key error. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Non-blocking advisory: surface duplicate paths above the hostRoutingRules list so a realm owner notices before saving. Uniqueness validation is intentionally out of scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Show a per-rule advisory under the path input when the path is non-empty but doesn't start with / or contains characters outside the allowed set for a static path (letters, numbers, /, -, _, ., or ~). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The advisory regex rejected %XX escapes, which are valid per RFC 3986. Accept a `%` followed by exactly two hex digits in addition to the unreserved character set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the two pure helpers out of the editor template into runtime-common: validateRoutingPath checks a single rule's path format, findDuplicateRoutingPaths scans the full list for repeats. The RealmConfig and RoutingRuleField editors now delegate to them. Cover both helpers in runtime-common/tests as a SharedTests module, wired into the realm-server qunit suite via the standard wrapper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add two host integration tests that prove the editor template wires the routing rule advisory validators through to rendered warnings: one for the per-rule malformed-path message, one for the aggregate duplicate-path banner. Existing realm-lock test refactored to share the same setup helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the default card-edit template (cardInfo header, FieldContainer- wrapped non-routing fields, notes footer) so iconURL, backgroundURL, and includePrerenderedDefaultRealmIndex render with their standard chrome. Only the hostRoutingRules FieldContainer carries custom UI — the duplicate-path advisory. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The A/B comparison against the default template is done. Wire the custom edit back in and drop the temporary `export` on the class. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A field declaration can now supply its own edit component via the `edit:` option, overriding the FieldDef's `static edit` for that specific usage. Two integration points: - Atomic fields (linksTo / contains): the override replaces the resolved edit component in field-component's lookup. - containsMany fields: the override receives `@model` (the containing card), `@values` (the array of items), and `@defaultEditor` (a pre-bound ContainsManyEditor) so it can wrap — not replace — the standard iteration / add / remove / sortable UI. Rewrite RealmConfig to use the wrap-style override for hostRoutingRules: the duplicate-path advisory now lives in a small standalone editor attached to the field, and every other field renders via the default card edit template. Drops the previous RealmConfigEdit that mirrored the default layout to splice the banner in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The override branch in field-component sees the same field descriptor for both the outer collection render and each iterated item inside containsMany. Without a gate it would replace each item's edit template with the collection-level override component, blanking every row. Restrict it to atomic field types; collection fields go through their own wrap-style handler. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The path input shows a non-editable "/" accessory before the editable text. The stored path always carries a leading "/", so users can't accidentally drop it by backspacing through the field, and the missing-slash advisory is unreachable from the editor (it stays correct for hand-edited realm.json files). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the BoxelInputGroup horizontal padding on the path cell so the leading "/" sits visually next to the editable text rather than floating with a wide margin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier --boxel-input-group-padding-x override on .path-cell was shadowed by BoxelInputGroup's own scoped CSS resetting the variable on its inner container. Target the consumer classes directly via :deep instead so the right-of-accessory and left-of-input padding collapse as intended. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire the same branch into LinksToComponent (atomic, replaces LinksToEditor) and LinksToMany's wrap-style path (with @defaultEditor matching containsMany). Add `readonly edit?` to each Field class so reading `field.edit` from those templates typechecks. Also cover the realm config editor's wrap contract and path normalizer behavior with integration tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…10056 # Conflicts: # packages/base/card-api.gts # packages/base/contains-many-component.gts # packages/base/field-component.gts # packages/base/links-to-many-component.gts
The Named-args type for `<@fields.foo />` invocations needs to
accept `lockConsumingRealm` so that
`<@fields.instance @lockConsumingRealm={{true}} />` typechecks.
The addition was originally made when the lock-realm option
landed but got dropped during the last merge from main when the
field-component.gts conflict was resolved in favor of main's
version (which carries the per-usage edit override but not the
lock-realm plumbing).
The path input renders an empty field next to a fixed "/" accessory, so a rule with no stored path is visually identical to one with an explicit "/". The duplicate-path warning compared the underlying data, treating those visually-equal rules as distinct because one held undefined and the other "/", so two "/" rules — one fresh, one explicit — never tripped the warning even though the user clearly saw the conflict. Normalizing unset paths to "/" on mount aligns the data with what's shown and removes the only "draft path" state, which has no runtime meaning for the realm-server router.
Setting model.path inside the constructor triggered autosave synchronously, which wrote autoSaveState.isSaving inside the same render computation that had already read it via the CardHeader's saving indicator — Glimmer rejects read-then-write on a tracked cell within one computation. Wrap the assignment in queueMicrotask so it runs once the current render has settled, guarded against firing after the component is torn down.
A disabled Picker still rendered each selected item with the X remove affordance, but clicking it did nothing — the underlying power-select rejected the action because the select is disabled. That left a dead UI element on the realm picker when the card chooser is locked to the consuming realm. Honour `@select.disabled` in the selected-item alongside the existing select-all carve-out, mirroring how the trigger already hides its caret when disabled.
The isolated template read `rule.instance.title`, but CardDef exposes the display name as `cardTitle` (computed from `cardInfo.name`). The typo meant each rule rendered the arrow followed by nothing, even when the rule was wired to a real card.
Iterating @fields.hostRoutingRules instead of @model.hostRoutingRules hands each item to RoutingRuleAtom, whose template already wires up <@fields.instance @Format='atom' />. That gives readers a clickable pill for the linked card instead of a plain text title — same behavior they get everywhere else a CardDef is rendered as an atom.
Drop the per-usage `edit:` option on hostRoutingRules and the RealmConfigRoutingRulesEditor wrap component that consumed it. The per-usage field-edit primitive is going to be removed while its API is reworked, so this branch can't rely on it. The replacement is a `static edit` on RealmConfig that wraps DefaultCardDefTemplate so the standard CardInfo header, generic field section, and notes footer keep rendering as before; the only addition is the cross-rule duplicate-path advisory banner above the form. Per-rule routing path UI (slash accessory, validation, unset-path normalization) stays unchanged because RoutingRuleField still owns its own `static edit = RoutingRuleEdit` and runs through the default ContainsManyEditor now.
Previously the warning sat above the entire form because RealmConfigEdit wrapped DefaultCardDefTemplate as a single block. Replace the wrap with an inlined copy of the standard CardDef edit scaffold — CardInfo header, displayFields iteration, notes footer — so the warning can render directly above the hostRoutingRules row inside the form. Per-field rendering is unchanged: each field still uses its own default Component; only the surrounding chrome is replicated, kept in sync with default-templates/isolated-and-edit.gts.
…10056 # Conflicts: # packages/base/card-api.gts
* `super(owner, args)` in RoutingRuleEdit was passing `owner: unknown` where the GlimmerComponent constructor wants `Owner` — type the parameter as `Owner` and import it. * `CardInfoTemplates.edit` declares `@model: CardDef`, but `Component<typeof RealmConfig>` exposes `@model` as `PartialFields<RealmConfig>` (every field, including `id`, possibly undefined). Add a `baseModel` getter that casts to the looser shape the template actually exercises.
The chooser-locked test waited on `[data-test-realm="Local Workspace"]`, but that selector reads the user-visible name from `realm.info()` — an async fetch. search-result-section.gts spells out the race: until `info()` resolves the section renders with the "Unknown Workspace" placeholder, so the name-based selector misses and `waitFor` times out. Switch the wait and the cross-realm assertion to `data-test-realm-url`, which is URL-keyed and stable.
Temporary: emit a data-test attribute on LinksToEditor showing what
`@lockConsumingRealm` arg actually arrives, plus an early test
assertion checking it. Tells us whether the value flows from
RoutingRuleEdit's <@fields.instance @lockConsumingRealm={{true}} />
through to LinksToEditor, which will pinpoint where the chain breaks
in CI. Will revert once we have an answer.
Use hasAttribute instead of selector matching so when the assertion fails the message reports the actual attribute value, telling us whether @filter.locked arrived as undefined, false, or some other falsy shape.
Operator-mode's SearchSheet renders its own SearchPanel + RealmPicker alongside the card-catalog modal's, and the SearchSheet's panel never receives `@lockSelectedRealms`. The bare `[data-test-realm-picker]` selector matched the SearchSheet's picker first, so the locked assertion always read its undefined `@filter.locked` even when the modal's picker was rendered correctly with locked=true. Scope every realm-related selector in the test to `[data-test-card-catalog-modal]` so it can only resolve to the modal's picker / realm sections. Also drops the [lock-diag] console.logs and data-test-lockarg-value attribute that surfaced the picker mix-up.
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 free
to 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.
This adds templates and other changes for managing host routing.
The card chooser won’t let you choose from another realm:
There‘s a warning when there’s a duplicate path or invalid segment:
The interact template for the realm config shows the routes and what cards they route to: