Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
// Copyright (c) 2025 Apple Inc. Licensed under MIT License.

import type { ThemeConfig } from "./theme.js";

export interface EmbeddingViewConfig {
/** Color scheme. */
colorScheme?: "light" | "dark" | null;

/** Theme configuration for customizing colors, fonts, and other visual properties. */
theme?: ThemeConfig | null;

/** View mode. */
mode?: "points" | "density" | null;

Expand Down
2 changes: 1 addition & 1 deletion packages/table/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,4 @@ export class Table {
}
}

export type { CustomCell, AdditionalHeaderContent as CustomHeader };
export type { CustomCell, AdditionalHeaderContent as CustomHeader, Theme };
4 changes: 4 additions & 0 deletions packages/viewer/src/EmbeddingAtlas.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
embeddingViewConfig = null,
embeddingViewLabels = null,
colorScheme: colorSchemeProp,
chartColors: customChartColors,
tableCellRenderers,
tableTheme,
onExportApplication,
onExportSelection,
onStateChange,
Expand Down Expand Up @@ -279,6 +281,7 @@
id: data.id,
columns: [],
colorScheme: colorScheme,
customChartColors: customChartColors,
columnStyles: resolvedColumnStyles,
cache: new ChartContextCache(),
persistentCache: cache ?? { get: async () => null, set: async (key, value) => {} },
Expand All @@ -289,6 +292,7 @@
embeddingViewConfig: embeddingViewConfig,
embeddingViewLabels: embeddingViewLabels,
tableCellRenderers: tableCellRenderers,
tableTheme: tableTheme,
};

let charts = $state.raw<Record<string, any>>({});
Expand Down
14 changes: 13 additions & 1 deletion packages/viewer/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
// The component API for embedding viewer.

import type { EmbeddingViewConfig, Label } from "@embedding-atlas/component";
import type { CustomCell } from "@embedding-atlas/table";
import type { CustomCell, Theme as TableTheme } from "@embedding-atlas/table";
import type { Coordinator } from "@uwdata/mosaic-core";
import { createClassComponent } from "svelte/legacy";

import Component from "./EmbeddingAtlas.svelte";

import cssCode from "./app.css?inline";
import type { ChartColors } from "./charts/common/colors.js";

export type { ChartColors, TableTheme };

export interface EmbeddingAtlasProps {
/** The Mosaic coordinator. */
Expand Down Expand Up @@ -41,6 +44,12 @@ export interface EmbeddingAtlasProps {
/** The color scheme. */
colorScheme?: "light" | "dark" | null;

/** Custom chart colors. You can override specific colors for light and dark themes. */
chartColors?: {
light?: Partial<ChartColors>;
dark?: Partial<ChartColors>;
} | null;

/** The initial viewer state. */
initialState?: EmbeddingAtlasState | null;

Expand All @@ -58,6 +67,9 @@ export interface EmbeddingAtlasProps {
/** Custom cell renderers for the table view. */
tableCellRenderers?: Record<string, CustomCell | "markdown">;

/** Custom theme for the table view. Allows customization of colors, fonts, and other visual properties. */
tableTheme?: TableTheme | null;

/** A callback to export the currently selected points. */
onExportSelection?:
| ((predicate: string | null, format: "json" | "jsonl" | "csv" | "parquet") => Promise<void>)
Expand Down
6 changes: 3 additions & 3 deletions packages/viewer/src/charts/basic/BoxPlot.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import ScaleTypePicker from "../controls/ScaleTypePicker.svelte";

import type { ChartViewProps } from "../chart.js";
import { chartColors } from "../common/colors.js";
import { getChartColors } from "../common/colors.js";
import {
distributionAggregate,
distributionStats,
Expand Down Expand Up @@ -51,11 +51,11 @@
onStateChange,
}: ChartViewProps<BoxPlotSpec, State> = $props();

let { colorScheme } = context;
let { colorScheme, customChartColors } = context;

let { brush } = $derived(chartState);

let colors = $derived(chartColors[$colorScheme]);
let colors = $derived(getChartColors($colorScheme, customChartColors));

let chartData: ChartData | null = $state.raw(null);

Expand Down
6 changes: 3 additions & 3 deletions packages/viewer/src/charts/basic/CountPlot.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Container from "../common/Container.svelte";

import type { ChartViewProps } from "../chart.js";
import { chartColors } from "../common/colors.js";
import { getChartColors } from "../common/colors.js";
import {
distributionAggregate,
distributionStats,
Expand All @@ -33,7 +33,7 @@
onStateChange,
onSpecChange,
}: ChartViewProps<CountPlotSpec, State> = $props();
let { coordinator, colorScheme } = context;
let { coordinator, colorScheme, customChartColors } = context;
let { selection } = $derived(chartState);
let { expanded, percentage } = $derived(spec);

Expand All @@ -59,7 +59,7 @@
// Adjust scale so the minimum width for non-zero count is 1px.
let xScaleAdjusted = $derived((v: number) => (v != 0 ? Math.max(1, xScale(v)) : 0));

let colors = $derived(chartColors[$colorScheme]);
let colors = $derived(getChartColors($colorScheme, customChartColors));

function initializeClient(coordinator: Coordinator, table: string, field: string, filter: Selection) {
let stats: any | null = $state.raw(null);
Expand Down
5 changes: 3 additions & 2 deletions packages/viewer/src/charts/basic/CountPlotList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import Container from "../common/Container.svelte";

import type { ChartViewProps } from "../chart.js";
import { chartColors } from "../common/colors.js";
import { getChartColors } from "../common/colors.js";

const MAX_BARS = 10;
const MAX_BARS_EXPANDED = 100;
Expand All @@ -71,6 +71,7 @@
}: ChartViewProps<Spec, State> = $props();

let colorScheme = context.colorScheme;
let customChartColors = context.customChartColors;
let { selection } = $derived(chartState);

interface Bin {
Expand All @@ -93,7 +94,7 @@
// Adjust scale so the minimum width for non-zero count is 1px.
let xScaleAdjusted = $derived((v: number) => (v != 0 ? Math.max(1, xScale(v)) : 0));

let colors = $derived(chartColors[$colorScheme]);
let colors = $derived(getChartColors($colorScheme, customChartColors));

function initializeClient(coordinator: Coordinator, table: string, field: string, filter: Selection, limit: number) {
let stats: any | null = $state.raw(null);
Expand Down
6 changes: 3 additions & 3 deletions packages/viewer/src/charts/basic/Histogram.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import ScaleTypePicker from "../controls/ScaleTypePicker.svelte";

import type { ChartViewProps } from "../chart.js";
import { chartColors } from "../common/colors.js";
import { getChartColors } from "../common/colors.js";
import {
distributionAggregate,
distributionStats,
Expand Down Expand Up @@ -47,11 +47,11 @@
onStateChange,
}: ChartViewProps<HistogramSpec, State> = $props();

let { coordinator, colorScheme } = context;
let { coordinator, colorScheme, customChartColors } = context;

let { brush } = $derived(chartState);

let colors = $derived(chartColors[$colorScheme]);
let colors = $derived(getChartColors($colorScheme, customChartColors));

let chartData: ChartData | null = $state.raw(null);
let defaultScaleType = $state<ScaleType | null>(null);
Expand Down
6 changes: 3 additions & 3 deletions packages/viewer/src/charts/basic/HistogramStack.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import ScaleTypePicker from "../controls/ScaleTypePicker.svelte";

import type { ChartViewProps } from "../chart.js";
import { chartColors } from "../common/colors.js";
import { getChartColors } from "../common/colors.js";
import {
distributionAggregate,
distributionStats,
Expand Down Expand Up @@ -49,11 +49,11 @@
onStateChange,
}: ChartViewProps<HistogramStackSpec, State> = $props();

let { colorScheme } = context;
let { colorScheme, customChartColors } = context;

let { brush } = $derived(chartState);

let colors = $derived(chartColors[$colorScheme]);
let colors = $derived(getChartColors($colorScheme, customChartColors));

let chartData = $state.raw<ChartData | null>(null);
let defaultScaleType = $state<ScaleType | null>(null);
Expand Down
11 changes: 11 additions & 0 deletions packages/viewer/src/charts/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import type { EmbeddingViewConfig, Label } from "@embedding-atlas/component";
import type { CustomCell } from "@embedding-atlas/table";
import type { Theme as TableTheme } from "@embedding-atlas/table";
import type { Coordinator, Selection } from "@uwdata/mosaic-core";
import type { Readable, Writable } from "svelte/store";

import type { ColumnDesc } from "../utils/database.js";
import type { ChartColors } from "./common/colors.js";

export class ChartContextCache {
private contents: Map<string, any>;
Expand Down Expand Up @@ -53,6 +55,12 @@ export interface ChartContext {
/** The current color scheme. */
colorScheme: Readable<"light" | "dark">;

/** Custom chart colors (optional overrides). */
customChartColors?: {
light?: Partial<ChartColors>;
dark?: Partial<ChartColors>;
} | null;

/** The column styles. */
columnStyles: Readable<any>;

Expand Down Expand Up @@ -93,6 +101,9 @@ export interface ChartContext {

/** Custom cell renderers for the table view. */
tableCellRenderers?: Record<string, CustomCell | "markdown">;

/** Custom theme for the table view. */
tableTheme?: TableTheme | null;
}

/** Props passed into a chart view. */
Expand Down
44 changes: 43 additions & 1 deletion packages/viewer/src/charts/common/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,23 @@ function adjustForGray(hex: string, lightnessShift: number = 0): string {
return c.rgb().formatHex8();
}

export const chartColors = {
export interface ChartColors {
scheme: "light" | "dark";
continuousColorScheme: string;
continuousColorSchemeAtZero: string;
markColor: string;
markColorFade: string;
markColorGray: string;
markColorGrayFade: string;
gridColor: string;
labelColor: string;
titleColor: string;
brushBorder: string;
brushBorderBack: string;
brushFill: string;
}

export const chartColors: Record<"light" | "dark", ChartColors> = {
light: {
scheme: "light",
continuousColorScheme: "YlGnBu",
Expand Down Expand Up @@ -53,3 +69,29 @@ export function defaultOrdinalColors(count: number): string[] {
}
return result;
}

/**
* Get chart colors with optional custom overrides.
* @param scheme - The color scheme ("light" or "dark")
* @param customColors - Optional custom color overrides
* @returns The merged chart colors
*/
export function getChartColors(
scheme: "light" | "dark",
customColors?: {
light?: Partial<ChartColors>;
dark?: Partial<ChartColors>;
} | null,
): ChartColors {
const baseColors = chartColors[scheme];
const overrides = customColors?.[scheme];

if (!overrides) {
return baseColors;
}

return {
...baseColors,
...overrides,
};
}
6 changes: 5 additions & 1 deletion packages/viewer/src/charts/table/Table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
let resolvedCustomCellRenderers = $derived(
resolveCustomCellRenderers(context.columns, $columnStyles, context.tableCellRenderers),
);

let resolvedTableTheme = $derived(
context.tableTheme ? { ...tableTheme, ...context.tableTheme } : tableTheme,
);
</script>

<Table
Expand All @@ -61,7 +65,7 @@
}}
numLines={3}
colorScheme={$colorScheme}
theme={tableTheme}
theme={resolvedTableTheme}
highlightHoveredRow={true}
customCells={resolvedCustomCellRenderers}
/>