Component Marketplace Preview — Design

Date: 2026-04-13 Status: Proposed

Problem

The component marketplace makes it hard to understand what a component actually does before adding it to a workflow.

Two surfaces both have gaps:

  1. Fullscreen marketplace (ComponentMarketplaceModal with variant="fullscreen", opened from the canvas toolbar). Cards show name, preview media, and output-type pills, but never show the component’s inputs, outputs, or exposed settings. The description is rendered but is clamped to two lines and disappears behind a hover overlay. Clicking a card adds the component immediately — users have no chance to review what they’re picking.

  2. Popup marketplace (ComponentMarketplaceModal with variant="popup", opened from the add-node / right-click menu). Each list item shows the component name plus the creator’s display name. The creator name isn’t useful for deciding whether to add the component — the description would be.

Both gaps are about the same root question: “what does this component do?”. All the data needed to answer it (description, componentMetadata.inputs/outputs/exposedSettings, previewMediaUrl) is already on the AppBrowseCard returned by the marketplace API — it just isn’t rendered.

Goals

Non-goals

Design

Surface 1 — Fullscreen marketplace: Component Preview Modal

New interaction:

User action Result
Single click on grid card Opens preview modal (does not add component)
Double click on grid card Adds component directly, closes marketplace (skips preview)
Click “+ Add to Workflow” in preview modal Adds component, closes modal and marketplace
Click outside / Esc / × in preview modal Closes preview modal, returns to marketplace grid

The onSelect callback (which adds the component to the canvas) fires only from the “+ Add to Workflow” button or a grid double-click.

Modal layout (desktop):

┌─────────────────────────────────────────────────────────────────┐
│ [icon] Component Name                           ♥   ✕           │
│ by Creator · 5 CR · Category pill                               │
│ Full description (no line clamp).                               │
│ Falls back to italic "No description provided." when empty.     │
├─────────────────────────────────────────────────────────────────┤
│  INPUTS                          │  PREVIEW                     │
│  ◦ [img] Reference Image · req   │  ┌───────────────────────┐   │
│  ◦ [txt] Prompt · req            │  │                       │   │
│  ◦ [img] Style Reference         │  │   previewMediaUrl     │   │
│                                  │  │   (image or video,    │   │
│  OUTPUTS                         │  │    16:9)              │   │
│  ◦ [img] Generated Image         │  │                       │   │
│  ◦ [txt] Caption                 │  └───────────────────────┘   │
│                                  │                              │
│  SETTINGS (3)                    │                              │
│  ◦ Aspect ratio                  │                              │
│  ◦ Quality                       │                              │
│  ◦ Style                         │                              │
├─────────────────────────────────────────────────────────────────┤
│                                    [ + Add to Workflow ]        │
└─────────────────────────────────────────────────────────────────┘

On mobile the two columns stack: header → preview → inputs → outputs → settings → CTA (preview stays near the top so the primary visual isn’t pushed below the fold).

Row format:

Handle/label text uses truncate + a title attribute with the full string for hover tooltip.

Sections hide themselves when their list is empty. If all three sections (inputs, outputs, exposedSettings) are empty, show a single line: “No metadata published for this component.”

Preview pane:

Data source:

The modal reads everything from the AppBrowseCard object that the grid already has in memory. No new fetch. Fields used:

Field Where shown Fallback
name Header — (required)
iconUrl Header (leading 20×20 thumb) <Puzzle /> — same pattern as ComponentListItem line 188-192
description Header (full, whitespace-pre-wrap) italic “No description provided.”
creatorDisplayName Header subtitle (“by X”) "Community" — same fallback as popup
estimatedCredits Header subtitle 0 rendered as “0 CR”
category Header subtitle pill Label "Other" + CATEGORY_COLORS.other — same pattern as AppMarketplaceCard line 22-23
previewMediaUrl + previewMediaType Preview pane “No preview available” placeholder with Sparkles icon
componentMetadata.inputs Inputs section Hidden if empty; “No metadata published” if all three empty
componentMetadata.outputs Outputs section Hidden if empty
componentMetadata.exposedSettings Settings section Hidden if empty
id Favorite toggle target — (required)

My Components tab: preview is not added here. That tab uses a custom renderCard (not AppMarketplaceCard) with Open/Edit/Archive buttons, and owners already have the “Open” button that navigates to /app/:slug — richer than our read-only preview. The popup marketplace and the fullscreen Browse/Favorites tabs are the only surfaces that get the new behavior.

Surface 2 — Popup marketplace: description instead of creator name

In ComponentListItem (currently showing {card.name} over {card.creatorDisplayName || "Community"}), replace the second line:

card.description || card.creatorDisplayName || "Community"

Single line, truncated with truncate, same font size and color. Preserves the fallback ladder so the row is never visually empty.

No other popup changes.

Component Boundaries

All new code lives inside the existing ComponentMarketplaceModal file tree to keep the change focused:

Edge Cases

Testing

This is a presentational change — all logic is a trivial a || b || c fallback and UI wiring. No unit tests; validate manually in the dev server per the project’s UI-change convention.

Manual test checklist:

Out of Scope