Skip to content

Support preselected examples in create listing modal#4283

Merged
tintinthong merged 9 commits into
mainfrom
CS-10547/implement-preselected-logic-on-modal-card-chooser
Apr 1, 2026
Merged

Support preselected examples in create listing modal#4283
tintinthong merged 9 commits into
mainfrom
CS-10547/implement-preselected-logic-on-modal-card-chooser

Conversation

@lucaslyl
Copy link
Copy Markdown
Contributor

@lucaslyl lucaslyl commented Mar 31, 2026

linear: https://linear.app/cardstack/issue/CS-10547/support-preselected-examples-in-create-listing-modal

Summary:

Add support for pre-selecting example cards when opening the create listing modal using ModalCardChooser. Display the atom template of selected example instances, preselect the realm and type pickers based on the consuming realm and card type filter, and show only target realm prerendered data.

Key changes:

  • Extend card chooser API with preselectedCardUrls and preselectConsumingRealm parameters to support multi-select pre-selection
  • Pass consumingRealm through the search flow to preselect the realm picker and auto-focus results in that realm
  • Determine declarationKind in OpenCreateListingModalCommand to conditionally render the examples UI (hidden for field definitions)
  • Replace CardInstancePicker with PrerenderedCardSearch displaying selected examples as atoms in a grey container, with individual remove buttons
  • Add "Add Examples" button (muted style with plus icon) that opens the card chooser with current selections pre-checked
  • Auto-preselect type filter based on baseFilter from card definition
  • Add fallback to fetch first instance when creating a listing from a module definition

Demo:

Screen.Recording.2026-03-31.at.4.15.42.PM.mov

@lucaslyl
Copy link
Copy Markdown
Contributor Author

@codex review

@lucaslyl lucaslyl self-assigned this Mar 31, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Preview deployments

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 888f041ed7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

display: inline-flex;
align-items: center;
}
.selected-example-atom :deep([data-test-card-format='atom']) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Replace test-only selector in runtime CSS

The new style rule uses data-test-* as a runtime selector, but AGENTS.md explicitly says these attributes are stripped from production builds and must not be used for app behavior/styling. In production this selector will never match, so the selected-example atom layout can regress (for example, chips not shrinking as intended). Please switch this to a production-safe hook (class or non-test data-*) instead.

Useful? React with 👍 / 👎.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 31, 2026

Realm Server Test Results

  1 files  ±0    1 suites  ±0   12m 3s ⏱️ +18s
802 tests ±0  802 ✅ ±0  0 💤 ±0  0 ❌ ±0 
873 runs  ±0  873 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit b873a46. ± Comparison against base commit bbc3db5.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 31, 2026

Host Test Results

    1 files  ±    0      1 suites  ±0   4h 24m 1s ⏱️ + 2h 12m 2s
2 064 tests +    2  2 048 ✅ +    1  15 💤 ± 0  0 ❌ ±0  1 🔥 +1 
4 158 runs  +2 081  4 126 ✅ +2 064  30 💤 +15  1 ❌ +1  1 🔥 +1 

For more details on these errors, see this check.

Results for commit b873a46. ± Comparison against base commit bbc3db5.

This pull request removes 2 and adds 4 tests. Note that renamed tests count towards both.
Chrome ‑ Integration | components | create-listing-modal: auto-selects the first instance when opened from a module
Chrome ‑ Integration | components | create-listing-modal: auto-selects the instance when opened from an instance
Chrome ‑ Integration | commands | open-create-listing-modal: stores modal payload with examples hidden for field defs
Chrome ‑ Integration | components | create-listing-modal: clicking a selected example remove icon removes it
Chrome ‑ Integration | components | create-listing-modal: hides examples for field listings
Chrome ‑ Integration | components | create-listing-modal: shows selected example atom when opened from an instance

♻️ This comment has been updated with latest results.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e9bf82d81c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

.filter((ref) => !ref.negated)
.map((ref) => ref.ref)
.filter(isResolvedCodeRef)
.map((ref) => ref.name),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use display-name keys for default type preselection

defaultSelectedTypeNames is populated from ref.name (the exported class name), but the picker options are keyed by card type display labels (card.cardType / cardTypeDisplayName). When those differ (for example BlogPost vs Blog Post), the new preselection logic never matches, so the chooser opens on “Any Type” instead of the base filter’s type and shows unrelated results.

Useful? React with 👍 / 👎.

Comment on lines +457 to +460
let instances = await this.store.search(
{ filter: { type: codeRef } },
targetRealms,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle preselection search errors gracefully

This newly added store.search runs before opening the modal, and it is awaited without error handling. store.search throws on failed search responses, so if the selected declaration type is not indexed yet (for example newly edited/unsaved modules or stale index state), the action rejects and the Create Listing modal never opens. We should catch this lookup failure and continue with empty openCardIds so listing creation still works.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aeeddfe7de

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@cardstack cardstack deleted a comment from chatgpt-codex-connector Bot Mar 31, 2026
@cardstack cardstack deleted a comment from chatgpt-codex-connector Bot Mar 31, 2026
@lucaslyl lucaslyl requested a review from a team March 31, 2026 07:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for preselecting and managing example cards when opening the “Create Listing” modal, including multi-select preselection and hiding examples for field definitions.

Changes:

  • Extended the card chooser API/state to support preselecting multiple cards and optionally preselecting the consuming realm.
  • Reworked the Create Listing modal examples UI to render selected examples as prerendered atom templates with remove controls and an “Add Examples” flow.
  • Added logic to classify declarations as card vs field to conditionally hide the examples row.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/runtime-common/index.ts Extends chooseCard option types to support preselected URLs and consuming-realm preselection.
packages/host/app/components/card-catalog/modal.gts Implements multi-card preselection and passes consuming-realm preselection through to search UI.
packages/host/app/components/card-search/panel.gts Adds consuming realm inputs; initializes realm selection + focused section for search results.
packages/host/app/components/card-search/search-content.gts Allows initial focused section to be set from the parent panel.
packages/host/app/components/operator-mode/create-listing-modal.gts Replaces instance picker with prerendered selected-example atoms + remove + “Add Examples” chooser.
packages/host/app/components/operator-mode/detail-panel.gts Adds a fallback search to preselect an instance when launching listing creation from a module.
packages/host/app/commands/open-create-listing-modal.ts Determines declarationKind (card vs field) to control examples UI visibility.
packages/host/app/services/operator-mode-state-service.ts Extends modal payload with declarationKind.
packages/host/tests/integration/components/create-listing-modal-test.gts Updates integration tests for the new examples UI and field-definition behavior.
packages/host/tests/integration/commands/open-create-listing-modal-test.gts Adds coverage for declarationKind classification in modal payload.
packages/host/tests/acceptance/code-submode/inspector-test.ts Updates acceptance assertions to match atom-based selected example rendering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +102 to +110
let consumingRealm = this.args.consumingRealm;
if (!this.shouldPreselectConsumingRealm || !consumingRealm) {
return [];
}
let realmURL = consumingRealm.href;
let info = this.realm.info(realmURL);
let label = info?.name ?? realmURL;
let icon = info?.iconURL ?? undefined;
return [{ id: realmURL, icon, label, type: 'option' }];
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

selectedRealms is initialized with a newly constructed PickerOption when preselectConsumingRealm is enabled. The Boxel Picker (used by RealmPicker) determines selection via object identity (includes(selected, option)), so this preselected option will not match the options instances created inside RealmPicker. This can cause the consuming realm to not appear selected in the dropdown and can lead to toggle/deselect issues (duplicates) when interacting with the picker. Consider preselecting by realm URL and mapping to the actual option objects used by the picker (or providing an id-based matcher) so selected references come from the current @options array.

Suggested change
let consumingRealm = this.args.consumingRealm;
if (!this.shouldPreselectConsumingRealm || !consumingRealm) {
return [];
}
let realmURL = consumingRealm.href;
let info = this.realm.info(realmURL);
let label = info?.name ?? realmURL;
let icon = info?.iconURL ?? undefined;
return [{ id: realmURL, icon, label, type: 'option' }];
// Do not construct a new PickerOption instance here, since the Boxel Picker
// determines selection by object identity. The Picker should own the
// selection instances based on its own @options array.
return [];

Copilot uses AI. Check for mistakes.
Comment on lines +381 to +390
.selected-example-atom
:deep(.field-component-card.atom-format.display-container-true) {
min-width: 0;
}
.selected-example-atom
:deep(.field-component-card.atom-format.display-container-true) {
padding-right: calc(
var(--boxel-sp-xs) + var(--boxel-icon-sm) + var(--boxel-sp-6xs)
);
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .selected-example-atom :deep(.field-component-card.atom-format.display-container-true) selector is duplicated in two consecutive blocks; this can be consolidated into a single rule to avoid drift and make future styling changes easier.

Copilot uses AI. Check for mistakes.
Comment on lines +455 to +471
if (!openCardIds.length) {
let targetRealms = [targetRealm];
let firstInstanceId: string | undefined;
try {
let instances = await this.store.search(
{ filter: { type: codeRef } },
targetRealms,
);
firstInstanceId = instances.find((c) => c.id)?.id;
} catch (error) {
console.warn(
'Failed to prefetch instances for create listing modal',
error,
);
}
openCardIds = firstInstanceId ? [firstInstanceId] : [];
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new fallback prefetch (await this.store.search(...)) runs before OpenCreateListingModalCommand.execute(), which means opening the Create Listing modal from a module definition now blocks on a network/search request. That’s a user-visible latency regression compared to opening the modal immediately and loading examples asynchronously. Consider opening the modal right away (with no or existing openCardIds) and then populating a default example in the modal once the search resolves, or otherwise running the prefetch in the background so the modal doesn’t wait on it.

Copilot uses AI. Check for mistakes.
@tintinthong tintinthong merged commit 293328d into main Apr 1, 2026
79 of 82 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants