Cs 10383 create listing and make a PR should have a loading user experience#4190
Cs 10383 create listing and make a PR should have a loading user experience#4190
Conversation
…re-selected source
…g-user-experience
Preview deployments |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0606903eb1
ℹ️ 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".
| targetRealm, | ||
| }); | ||
| if (this.isCardInstance) { | ||
| const openCardId = this.args.readyFile.url; |
There was a problem hiding this comment.
Use extensionless card id for listing creation
When the action runs from an instance, openCardId is sourced from readyFile.url, and this path is guaranteed to include .json (isCardInstance checks that). ListingCreateCommand then uses that value as the canonical instance identifier (for examples.0.links.self and in linkSpecs() via _dependencies?url=...), but instance entries are indexed by extensionless ids (for example .../Pet/mango). In this path, passing the file URL causes dependency/spec resolution to miss and can produce listings without the expected linked example/specs; use the instance id (this.args.cardInstance?.id) or strip .json before executing the command.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e825a478e
ℹ️ 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".
| @action private onClose() { | ||
| this.errorMessage = undefined; | ||
| this.operatorModeStateService.closeCreateListingModal(); | ||
| } |
There was a problem hiding this comment.
Reset source override when closing the listing modal
Closing the modal only clears errorMessage, so _userSelectedKey/_userSelectedValue persist across sessions. If a user opens from an instance, switches Source to the definition, cancels, and then reopens from that same instance, activeOpenCardId will reuse the stale override instead of pre-selecting the triggering instance. That breaks the instance-driven preselection flow and can cause creating a listing from the wrong source when users immediately confirm.
Useful? React with 👍 / 👎.
| targetRealm: request.targetRealm, | ||
| openCardId: this.activeOpenCardId ?? undefined, | ||
| }); | ||
| this.operatorModeStateService.closeCreateListingModal(); |
There was a problem hiding this comment.
Prevent stale create task from closing newer modal requests
The create task always calls closeCreateListingModal() after ListingCreateCommand resolves, even if the modal request changed while the task was running. In a long-running create, a user can dismiss and reopen the modal for a different card; when the first task finishes, it clears the newer request and dismisses the new modal unexpectedly.
Useful? React with 👍 / 👎.
…g-user-experience
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…g-user-experience
There was a problem hiding this comment.
Pull request overview
This PR improves the “Create Listing” flow in operator mode by introducing a confirmation modal with a loading experience, supporting multi-example selection, and navigating to the new listing immediately while background auto-patching completes.
Changes:
- Adds an operator-mode “Create Listing” confirmation modal (with examples selection + loading state).
- Introduces
OpenCreateListingModalCommandand updates menu items/indexing expectations to open the modal instead of directly creating a listing. - Updates listing creation to accept
openCardIds(plural) and exposes background work completion so the modal can auto-dismiss when patching finishes.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/runtime-common/code-ref.ts | Adds isListingDef / isListingInstance helpers for reliable listing detection. |
| packages/host/app/services/operator-mode-state-service.ts | Stores modal payload in cross-submode state service. |
| packages/host/app/components/operator-mode/container.gts | Renders the new modal at operator-mode top level. |
| packages/host/app/components/operator-mode/detail-panel.gts | Switches “Create Listing” action to open modal; hides action for listings. |
| packages/host/app/components/operator-mode/create-listing-modal.gts | New confirmation modal UI + loading state + navigation behavior. |
| packages/host/app/commands/open-create-listing-modal.ts | New command that sets modal payload in operator-mode state. |
| packages/host/app/commands/listing-create.ts | Renames openCardId→openCardIds, links multiple examples, runs autopatching as background work. |
| packages/host/app/commands/index.ts | Shims/registers the new open-create-listing-modal command. |
| packages/catalog-realm/catalog-app/listing/listing.gts | Marks listing definition with static isListingDef = true and updates menu item filtering. |
| packages/base/menu-items.ts | Updates default menu item to “Create Listing” and opens the modal command. |
| packages/base/command.gts | Updates ListingCreateInput to openCardIds (containsMany). |
| packages/host/tests/* | Adds/updates tests for modal behavior, command behavior, and indexing deps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private instancesSearch = getSearch<CardDef>(this, getOwner(this)!, () => | ||
| this.codeRef ? { filter: { type: this.codeRef } } : undefined, |
| let targetRealm = payload.targetRealm; | ||
| let openCardIds = | ||
| this.selectedExampleIds === null | ||
| ? this.instances.map((i) => i.id) | ||
| : [...this.selectedExampleIds]; |
| @title='Create Listing' | ||
| @size='small' | ||
| @isOpen={{this.isModalOpen}} | ||
| @onClose={{this.onClose}} | ||
| data-test-create-listing-modal |
There was a problem hiding this comment.
I agree with this. But follow the request, we should allow users to close the modal instead of forcing them to wait for the listing creation to finish, as the background processing can take quite a long time.
| <template> | ||
| <div class='operator-mode'> | ||
| <ChooseFileModal /> | ||
| <CreateListingModal /> |
There was a problem hiding this comment.
seems like a very similar modal to ChooseFileModal
There was a problem hiding this comment.
From my understanding, the main reason we can't reuse ChooseFileModal is that it's a host-only component that lives in code submode. But the listing creation gets triggered from a menu item in packages/base, which runs in the realm's headless Chrome process — it has no way to reach any host UI component directly. The only bridge i can see is using the command pattern (openlistingmodal command).
The LHS detail panel "create-lisitng" dont have this issue because it was under host packages
| private moduleInspectorHistory: Record<string, ModuleInspectorView>; | ||
|
|
||
| @tracked profileSettingsOpen = false; | ||
| @tracked createListingModalPayload?: CreateListingModalPayload; |
There was a problem hiding this comment.
why was this state not INSIDE of the modal
There was a problem hiding this comment.
As for why createListingModalPayload is stored in OperatorModeStateService — the modal doesn't exist until the payload is set, so the command needs somewhere that's always alive to drop off the data. For now i can see the OperatorModeStateService fits because it persists across all modes, unlike CreateFileModal which only lives in code submode. The payload also carries the context (targetRealm, codeRef, openCardIds) that the modal needs to know what to display, since that info has to travel from the base package through the command pattern to reach the host side. Not sure if my understanding is precise or not
…g-user-experience
- Extract CardInstancePicker as a thin Picker wrapper (mirrors RealmPicker/TypePicker pattern: selectAll option, passthrough onChange) - Refactor CreateListingModal to own selection state as PickerOption[] instead of string[], with effectiveSelected derived from user interaction or initial payload (openCardIds / first instance) - Fix autoLinkExample in ListingCreateCommand to respect user's explicit selection instead of auto-filling additional examples via LLM - Update create-listing-modal tests for new picker UI and add coverage for auto-select behavior (from instance vs from module) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@codex feedback |
Summary
Testing
|
…g-user-experience
…g-user-experience
…rname instead of the Matrix bot user identity.
…g-user-experience
…g-user-experience
linear: https://linear.app/cardstack/issue/CS-10383/create-listing-should-have-a-loading-user-experience
Summary
New Added :
Architecture
The "Create Listing" action can be triggered from multiple places:
Flow after clicking "Create"
Demo:
Screen.Recording.2026-03-19.at.4.57.55.PM.mov