Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion packages/viewer/src/EmbeddingAtlas.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
onExportSelection,
onStateChange,
cache,
highlight: highlightProp = null,
}: EmbeddingAtlasProps = $props();

const { colorScheme, userColorScheme } = makeColorSchemeStore();
Expand Down Expand Up @@ -285,6 +286,23 @@
chartThemeStore.set(chartTheme ?? undefined);
});

// Highlight store for single point (animation + tooltip)
let highlightStore = writable<RowID | null>(null);

// Highlight IDs store for multiple points (overlay circles)
let highlightIdsStore = writable<RowID[] | null>(null);

// Sync external highlight prop to internal stores
$effect.pre(() => {
if (highlightProp != null && highlightProp.length > 0) {
highlightStore.set(highlightProp[0]);
highlightIdsStore.set(highlightProp);
} else {
highlightStore.set(null);
highlightIdsStore.set(null);
}
});

let chartContext: ChartContext = {
coordinator: coordinator,
filter: crossFilter,
Expand All @@ -299,7 +317,8 @@
searchModes: searchModes,
search: doSearch,
searchResult: searchResultStore,
highlight: writable(null),
highlight: highlightStore,
highlightIds: highlightIdsStore,
embeddingViewConfig: embeddingViewConfig,
embeddingViewLabels: embeddingViewLabels,
tableCellRenderers: tableCellRenderers,
Expand Down
8 changes: 8 additions & 0 deletions packages/viewer/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ export interface EmbeddingAtlasProps {

/** A cache to speed up initialization of the viewer. */
cache?: Cache | null;

/**
* An array of row IDs to highlight on the embedding view.
* When set, orange circles will be drawn at the specified points.
* The view will animate to show the first point in the array.
* Pass null or an empty array to clear the highlight.
*/
highlight?: any[] | null;
}

export interface EmbeddingAtlasState {
Expand Down
3 changes: 3 additions & 0 deletions packages/viewer/src/charts/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ export interface ChartContext {
/** The current highlight point. When this changes, supported views will highlight the given point. */
highlight: Writable<RowID | null>;

/** Multiple highlight points. When set, supported views will show overlay circles at these points. */
highlightIds?: Readable<RowID[] | null>;

/** Configuration for the embedding view. See docs for the EmbeddingView. */
embeddingViewConfig?: EmbeddingViewConfig | null;

Expand Down
27 changes: 27 additions & 0 deletions packages/viewer/src/charts/embedding/Embedding.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,33 @@
}),
);

// Subscribe to highlightIds for programmatic multi-point highlighting
$effect.pre(() => {
if (context.highlightIds == null) return;
return context.highlightIds.subscribe(async (ids) => {
if (ids == null || ids.length == 0) {
overlayProps = null;
return;
}
let r = Array.from(
await context.coordinator.query(
SQL.Query.from(context.table)
.select({ identifier: SQL.column(context.id), x: SQL.column(spec.data.x), y: SQL.column(spec.data.y) })
.where(
SQL.isIn(
context.id,
ids.map((x) => SQL.literal(x)),
),
),
),
) as DataPoint[];
overlayProps = {
center: null,
points: r,
};
});
});

async function animateToPoint(identifier: RowID): Promise<void> {
let defaultScale = await context.cache.value(`embedding/default-viewport-scale/${spec.data.x},${spec.data.y}`, () =>
defaultViewportScale(context.coordinator, context.table, spec.data.x, spec.data.y),
Expand Down