Skip to content
Merged
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
324 changes: 158 additions & 166 deletions pixi.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ rust = "=1.97.0"
rust-src = "=1.97.0"
cargo-deny = "=0.20.2"
nodejs = ">=24.11.0"
pnpm = "=11.9.0"
pnpm = ">=11.19.0"
libprotobuf = ">=7"
cmake = ">=3.24"
cxx-compiler = ">=1"
Expand Down
6 changes: 3 additions & 3 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
"type": "module",
"engines": {
"node": ">=24.11.0",
"pnpm": ">=11.6.0"
"pnpm": ">=11.19.0"
},
"packageManager": "pnpm@11.6.0",
"packageManager": "pnpm@11.19.0",
"volta": {
"node": "24.11.0",
"pnpm": "11.6.0"
"pnpm": "11.19.0"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
Expand Down
17 changes: 16 additions & 1 deletion ui/packages/@quent/components/src/dag/DAGChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
getOperationTypeColor,
buildOperatorColorMap,
inferFieldFormatter,
type QuantitySpec,
} from '@quent/utils';

// Edge geometry constants
Expand Down Expand Up @@ -321,6 +322,20 @@ const FlowLayout = ({
[data.nodes]
);

const statQuantitySpecs = useMemo((): Record<string, QuantitySpec> => {
if (!data.quantitySpecs) return {};
const result: Record<string, QuantitySpec> = {};
for (const node of data.nodes) {
for (const stat of parseCustomStatistics(node.metadata?.rawNode)) {
if (stat.quantity && !(stat.key in result)) {
const spec = data.quantitySpecs[stat.quantity];
if (spec) result[stat.key] = spec;
}
}
}
return result;
}, [data.nodes, data.quantitySpecs]);

// Convert DAGData to ReactFlow format
const convertToReactFlow = useCallback(() => {
// Determine which nodes have incoming/outgoing edges
Expand Down Expand Up @@ -460,7 +475,7 @@ const FlowLayout = ({
defaultEdgeOptions={{ type: 'smoothstep' }}
>
<Background />
<DAGLegend isDark={isDark} />
<DAGLegend isDark={isDark} statQuantitySpecs={statQuantitySpecs} />
<MiniMap
pannable
zoomable
Expand Down
34 changes: 30 additions & 4 deletions ui/packages/@quent/components/src/dag/DAGLegend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
getLegendGradientStops,
type PaletteTheme,
} from '@quent/utils';
import { inferFieldFormatter } from '@quent/utils';
import { inferFieldFormatter, formatQuantity, type QuantitySpec } from '@quent/utils';
import { DataFlowTierLegend } from './DataFlowTierLegend';
import type { NodeColoring, EdgeColoring } from '../services/query-plan/types';
import type { ContinuousPaletteName } from '@quent/utils';
Expand All @@ -33,10 +33,18 @@ interface ContinuousLegendProps {
max: number;
palette: ContinuousPaletteName;
isDark: boolean;
formatValue?: (v: number) => string;
}

const ContinuousLegend = ({ field, min, max, palette, isDark }: ContinuousLegendProps) => {
const fmt = inferFieldFormatter(field);
const ContinuousLegend = ({
field,
min,
max,
palette,
isDark,
formatValue,
}: ContinuousLegendProps) => {
const fmt = formatValue ?? inferFieldFormatter(field);
return (
<div className="flex flex-col gap-1">
<span className="text-[10px] font-semibold text-muted-foreground uppercase tracking-wide">
Expand Down Expand Up @@ -130,16 +138,26 @@ export const CategoricalLegend = ({
);
};

function resolveFormatter(
field: string,
statQuantitySpecs: Record<string, QuantitySpec>
): ((v: number) => string) | undefined {
const spec = statQuantitySpecs[field];
return spec ? (v: number) => formatQuantity(v, spec, 'Occupancy') : undefined;
}

function NodeLegendContent({
coloring,
field,
palette,
isDark,
statQuantitySpecs,
}: {
coloring: NodeColoring;
field: string | null;
palette: ContinuousPaletteName;
isDark: boolean;
statQuantitySpecs: Record<string, QuantitySpec>;
}) {
if (!coloring || !field) return null;
if (coloring.type === 'continuous') {
Expand All @@ -150,6 +168,7 @@ function NodeLegendContent({
max={coloring.max}
palette={palette}
isDark={isDark}
formatValue={resolveFormatter(field, statQuantitySpecs)}
/>
);
}
Expand All @@ -161,11 +180,13 @@ function EdgeLegendContent({
field,
palette,
isDark,
statQuantitySpecs,
}: {
coloring: EdgeColoring;
field: string | null;
palette: ContinuousPaletteName;
isDark: boolean;
statQuantitySpecs: Record<string, QuantitySpec>;
}) {
if (!coloring || !field) return null;
if (coloring.type === 'continuous') {
Expand All @@ -176,6 +197,7 @@ function EdgeLegendContent({
max={coloring.max}
palette={palette}
isDark={isDark}
formatValue={resolveFormatter(field, statQuantitySpecs)}
/>
);
}
Expand All @@ -185,10 +207,12 @@ function EdgeLegendContent({
interface DAGLegendProps {
/** Whether dark mode is active. Passed explicitly to decouple from ThemeContext. */
isDark: boolean;
/** Pre-resolved stat-key → QuantitySpec for quantity-aware legend formatting. */
statQuantitySpecs?: Record<string, QuantitySpec>;
}

/** Panel overlay showing node/edge coloring legends within the ReactFlow canvas. */
export const DAGLegend = ({ isDark }: DAGLegendProps) => {
export const DAGLegend = ({ isDark, statQuantitySpecs = {} }: DAGLegendProps) => {
const nodeColoring = useNodeColoringValue();
const edgeColoring = useEdgeColoring();
const [nodePalette] = useNodeColorPalette();
Expand Down Expand Up @@ -247,13 +271,15 @@ export const DAGLegend = ({ isDark }: DAGLegendProps) => {
field={nodeField}
palette={nodePalette}
isDark={isDark}
statQuantitySpecs={statQuantitySpecs}
/>
{hasNode && hasEdge && <div className="border-t border-border" />}
<EdgeLegendContent
coloring={edgeColoring}
field={edgeField}
palette={edgePalette}
isDark={isDark}
statQuantitySpecs={statQuantitySpecs}
/>
{(hasNode || hasEdge) && hasDataFlow && <div className="border-t border-border" />}
{hasDataFlow && (
Expand Down
Loading
Loading