Gate iframe install on plugin.install, keep the 15s deadline off package
downloads, abort cancelled collection fetches, bound /templates prefetch,
and stop caching partial template-collection failures. Revert unrelated
Main Nav order and icon styling, remove the unused performance E2E
bundle, and expose a standalone Marketplace entry instead of Knip path
aliases.
Restore the current skill/agent stack from origin/main after the merge, then fix marketplace lint, formatting, and type mismatches so Style Check can pass.
Keep marketplace card-wrapper dialog and partner-only carousel autoplay.
Take main's skills permissions, skill card actions, and agent orchestrate
layout. Union oxlint suppressions from both sides.
Three independent defects made catalog search feel unstable.
Stale results flash on every search. `isSearchMode` was derived from the raw
nuqs `q` while the request body used the 500ms-debounced value, so keystroke #1
flipped into search mode with an empty query. That fired a full empty-query
search whose generic top-plugins list rendered for the debounce window before
the real results replaced it. Both now read the same debounced value.
Grid unmount on every query change. The infinite query had no
`placeholderData`, so `data` went undefined whenever the key changed and
ListWrapper's `!isLoading` gate tore the whole result grid out, collapsing the
container and jumping the scroll position. Measured against a live catalog, a
sort change blanked the grid for 152 of 360 sampled frames; with
`keepPreviousData` and an `isRefreshing` dim it is 0 of 461.
Requests that never settle. The Marketplace oRPC link called `globalThis.fetch`
with no deadline, so a stalled connection left the query pending forever with
no error state and no retry — the reported permanent spinner. Requests now
carry a 15s deadline composed with (never replacing) react-query's abort
signal. The server prefetch and the banner fetch get their own 2.5s budget:
awaiting them unbounded held the whole RSC response, pushing time-to-first-byte
to ~7s against a 3s-delayed API while the browser sat on the previous page.
Supporting changes:
- `getMarketplacePlugins` and `getMarketplaceCollectionsAndPlugins` no longer
swallow every rejection into a successful empty page. That turned outages
and aborted keystrokes into "no plugins found", suppressed retries, cached
the emptiness for the full staleTime, and killed `getNextPageParam`
permanently via `total: 0`. ListWrapper now renders a retry affordance.
- The collections fan-out is bounded to 4 concurrent requests instead of
firing one per collection at once, and per-collection failures degrade to an
empty carousel rather than a blank catalog.
- Scroll pagination measures once per frame instead of per scroll event. Each
threshold hit calls `fetchNextPage`, which defaults to `cancelRefetch: true`,
so an unthrottled burst aborted and restarted the in-flight page request —
and the backend counts those aborts against its search circuit breaker.
- Cross-page results are deduped by `org/name`; Meilisearch resolves
`install_count` ties by document order and the sync task rewrites those
documents every minute, so pages can overlap and collide on React keys.
- Search queries no longer inherit `retry: 3`; ~7s of backoff reads as a hang.
- resample the 3MB trending background to a 97KB webp and serve a frozen
recommend banner from the E2E stub so the benchmark covers the carousel
- show the on-page filtered count in template search and redirect
out-of-range pages to the last available page
- localize the carousel controls across all 24 locales and replace the
i18n type casts with typed selectors
- surface Marketplace API failures with a retry action instead of
rendering them as empty results
- use iconify marks for the hero decorations, share the carousel
page-size hook to avoid a hydration mismatch, move banner types into
@dify/contracts, scope the Cmd+K shortcut to the standalone platform,
point the embedded logo at /marketplace, pass resolvedTheme to the
detail dialogs, and reveal the detail iframe after a loading timeout
Co-authored-by: Cursor <cursoragent@cursor.com>
- move the marketplace performance E2E lane out of main CI (opt-in workflow_dispatch)
- add server-rendered pagination for template search and validate page/view/sort params at the route boundary
- match template locale filtering by language prefix with an explicit English fallback
- cache the template collection fan-out (TTL + in-flight dedupe), lower per-collection limit and batch requests
- reset the forced search mode atom when the plugin catalog mounts
- clear stale search suggestions during the debounce window
- let the trending Play control override focus/hover pause reasons
- honor prefers-reduced-motion on the eagerly playing first-collection carousel
- give the Creator Center link an accessible, localized name
- replace the nested main landmark on the templates page with a div
- localize banner read-more actions and backfill marketplace.home keys across all 24 locales
Co-authored-by: Cursor <cursoragent@cursor.com>