Windows & Curation
A window is the practical Level 2 unit of persistent curation: a user-scoped collection that stores inspiration, product context, and the latest personalized retrieval results together.
What A Window Is
If you are building on Prism and Mosaic, think of a window as the saved container around a curation workflow. It can be seeded from captures, product handles, targeted retailer products, or cluster-derived recommendations. The same object can then support browsing, re-curation, sharing, and search.
- User-created windows start from explicit inputs like captures or selected products
- Auto windows can be generated from cluster or preference signals derived by Mosaic
- Focused windows are domain-specific windows built from retailer-targeted product seeds
Core Flow
- Collect a seed set from captures, products, or a Mosaic-derived recommendation set
- Create window metadata in your own application layer
- Run Prism curation against the seed set
- Store entries, curation results, and provenance metadata so the window can be refreshed or searched later
TypeScript Example
A practical pattern is to let Mosaic choose retailer-specific seeds, then use Prism to produce the personalized product set you store inside your window.
type FocusedProduct = {
product_variant_handle: string;
name?: string;
image_url?: string | null;
cover_image_url?: string | null;
window_title?: string | null;
};
type WindowRecord = {
id: string;
userId: string;
title: string;
domain: string;
previewImageUrl?: string;
source: {
name: "mosaic-focused";
domain: string;
product_variant_handle: string;
generatedAt: string;
};
entries: Array<{ product_variant_handle: string }>;
results: unknown[];
};
async function getTargetedProducts(domain: string, token: string): Promise<FocusedProduct[]> {
const response = await fetch(`${process.env.MOSAIC_URL}/domains/${domain}/targeted-products`, {
headers: { Authorization: `Bearer ${token}` }
});
if (!response.ok) {
throw new Error(`Mosaic targeted-products failed: ${response.status}`);
}
const data = await response.json();
return data.products as FocusedProduct[];
}
async function curateWindow(
userId: string,
domain: string,
productHandles: string[],
prismToken: string
) {
const response = await fetch(`${process.env.PRISM_URL}/search/unified`, {
method: "POST",
headers: {
Authorization: `Bearer ${prismToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
queries: productHandles.map((handle) => ({ product_variant_handle: handle })),
user_id: userId,
domains: [domain],
n_products: 48,
n_pool: 300,
noise: 0.0
})
});
if (!response.ok) {
throw new Error(`Prism curation failed: ${response.status}`);
}
const data = await response.json();
return data.results;
}
async function createFocusedWindow(
userId: string,
domain: string,
prismToken: string,
mosaicToken: string
): Promise<WindowRecord[]> {
const targetedProducts = await getTargetedProducts(domain, mosaicToken);
const deduped = Array.from(
new Map(
targetedProducts.map((product) => [product.product_variant_handle, product])
).values()
);
return Promise.all(
deduped.map(async (product) => {
const results = await curateWindow(
userId,
domain,
[product.product_variant_handle],
prismToken
);
return {
id: crypto.randomUUID(),
userId,
title: product.window_title ?? product.name ?? `Focused: ${domain}`,
domain,
previewImageUrl: product.cover_image_url ?? product.image_url ?? undefined,
source: {
name: "mosaic-focused",
domain,
product_variant_handle: product.product_variant_handle,
generatedAt: new Date().toISOString()
},
entries: [{ product_variant_handle: product.product_variant_handle }],
results
};
})
);
} Refresh Pattern
Keep the window metadata stable and rerun Prism against the saved seed set when the user asks for a refresh.
async function refreshWindow(window: WindowRecord, prismToken: string) {
const productHandles = window.entries.map((entry) => entry.product_variant_handle);
const results = await curateWindow(
window.userId,
window.domain,
productHandles,
prismToken
);
return {
...window,
results
};
} Implementation Notes
- Source metadata matters because you will want to distinguish user-created, auto-generated, and retailer-focused windows
- Viewer vs owner behavior should usually differ: owners can refresh and mutate, while public viewers mainly get cached reads
- Approval or moderation state is useful when you generate windows automatically and do not want to surface all of them immediately
- Window search should be treated as its own retrieval mode, not just a UI grouping abstraction
When To Use It
Use windows when you need durable curation state rather than a one-off search response. They are the right abstraction for saved discovery flows, personalized landing surfaces, retailer-specific collections, and any product experience that needs to evolve as user taste changes.