Skip to content

feat: per-chart settings, serial axes, and URL sync#127

Merged
fahimfaisaal merged 24 commits into
mainfrom
feat/per-chart-settings
Jun 15, 2026
Merged

feat: per-chart settings, serial axes, and URL sync#127
fahimfaisaal merged 24 commits into
mainfrom
feat/per-chart-settings

Conversation

@fahimfaisaal

@fahimfaisaal fahimfaisaal commented Jun 14, 2026

Copy link
Copy Markdown
Member

Summary

  • Go schema: Replace flat axisLabels with ordered axes[] (Axis{Key,Label}) and add ChartSettings (swap/sort/scale/showLabels/autoRotate) per chart type in DatasetSettings
  • CLI --chart flag: Repeatable per-chart overrides — --chart bar:swap=yx,sort=asc --chart pie:labels — validated against active charts and axis permutations
  • Serial axis derivation: GroupAxes() preserves --group-pattern/--group-regex dimension order; GroupAxisLabels() removed
  • Migration: MigrateDataset two-pass unmarshal recovers legacy axisLabels from old JSON files (including per-element array branch via []json.RawMessage)
  • Merge: deepCloneDataset deep-copies Axes slice and ChartSettings map (including *Sort pointers)
  • UI store: resolved(key) returns per-chart override or global default; setForActiveChart writes for active type; selectedSwapIndexMap keyed "${benchmarkId}:${chartType}"
  • Swap: arrangementMap keyed "${datasetId}:${chartType}" — switching chart types applies that type's stored arrangement; identity derived from axes[] key order
  • Pipeline: sort/showLabels/scale/autoRotate come from resolved() so each chart type uses its own effective values
  • Settings controls: All controls read/write per-chart via resolved/setForActiveChart; isAxisChart now excludes heatmap (scale not valid)
  • URL sync: New <type>.so/.l/.sc/.rt/.sw params per chart type; legacy bare s/l/sc still parsed for back-compat; swap restore deferred until data loads
  • Docs: --chart flag documented in commands/root.mdx with key table and examples

…ttings to named types

Remove AxisLabels struct and field from Dataset. Add named types Axis, Sort,
ChartSettings, and DatasetSettings to support per-chart overrides and axes
labeling in a structured, extensible way. Drop the GroupAxisLabels() call from
prepareDatasetFromResults (will be replaced by GroupAxes() in a follow-up task).
- Restore axis label population in prepareDatasetFromResults using
  parser.GroupAxisLabels(); converts flat map to []Axis in canonical
  key order (name/x/y/z) — temporary until GroupAxes() lands in Task 3
- Document Sort.Order valid values ("asc" or "desc")
- Document ChartSettings.Sort pointer semantics (nil = inherit global)
- Add Axis type for ordered axis dimensions with label overrides
- Add ChartSettings type for per-chart type overrides (swap, sort, scale, showLabels, autoRotate)
- Update Settings to include axes and chartSettings fields
- Mark DataSet.axisLabels as @deprecated; keep AxisLabels type for legacy JSON reading
- Fix 1: Change chartSettings from Record<ChartType, ChartSettings> to
  Partial<Record<ChartType, ChartSettings>> since ChartType has 4 values
  (bar|line|pie|heatmap) and the intent is a sparse override map
- Fix 2: Update autoRotate comment to clarify it overrides the
  store-level autoRotate when set per-chart
- Fix 3: Remove redundant inline comment on axes field (already
  documented in Axis type block comment above)
…shared.Axis

Add GroupAxes() to parse_pattern.go supporting both pattern mode (ordered
dims with positional --group labels) and regex mode (named captures mapped
to canonical key order name/x/y/z, labels always empty). Replace the
temporary inline block in prepareDatasetFromResults with a direct call to
parser.GroupAxes(). Remove GroupAxisLabels() and its tests; add TestGroupAxes.
…oupAxes label misalignment

- ValidateGroupPattern now tracks seen expanded dimension names and
  returns an error on duplicates (e.g. "x/y/x" → "duplicate dimension 'xAxis' in pattern")
- GroupAxes pattern-mode loop uses a separate groupIdx counter instead
  of the raw slice index i, so empty parts (e.g. "x//y") no longer
  shift group labels to the wrong dimension
- Add TestValidateGroupPattern case for duplicate dimension
- Add TestGroupAxes case for empty-part index alignment
…ge notAllowed maps

- Parse labels=<val> and rotate=<val> using strconv.ParseBool instead of
  silently ignoring the value and always setting true
- Return an error when the settings portion after ':' is empty (e.g. "bar:")
- Merge scaleNotAllowed and rotateNotAllowed into a single notAllowedForLimitedCharts map
- Fix hardcoded "rotate" literal in bare-flag error to use the key variable
- Replace duplicate "unknown chart type" test case with distinct cases:
  empty-rest error and labels=false setting ShowLabels to &false
Adds MigrateDataset to shared/migrate.go which performs a second-pass
unmarshal of the old top-level axisLabels field and populates
Settings.Axes in canonical order when it is empty. Call sites in
convertToDataset (root.go) and parseInputFile (merge.go) are updated
so both the html and merge commands transparently upgrade old JSON files.
Re-marshaling a Dataset after Unmarshal loses the legacy axisLabels field
(no longer in the struct). Instead, decode the array as []json.RawMessage
so each element's raw bytes are preserved for the MigrateDataset second pass.
…taset

Slice and map fields are reference types; shallow copy via dst:=src shares
backing storage. Deep-copy Sort pointers inside ChartSettings too.
Migration is already handled by parseInputFile. Apply ParseChartSpecs
to each loaded dataset (using its own axes for swap validation) before
marshaling to HTML. Per-chart settings in loaded JSON are preserved
when --chart is not given.
useSettingsStore:
- chartSettings: Partial<Record<ChartType, ChartSettings>> in reactive state
- resolved(key) returns per-chart override or global default
- setForActiveChart(update) writes per-chart overrides for active chart type
- initializeFromDataSet seeds chartSettings from dataset.settings.chartSettings
- selectedSwapIndexMap keyed "${benchmarkId}:${chartType}" for per-chart swap

useDataPoint:
- arrangementMap keyed "${datasetId}:${chartType}" (was plain datasetId)
- setArrangement now takes (datasetId, chartType, target) — chart-type-aware
- activeArrangement reads identity from axes[] key order if present, else presentKeys(data)
- chartType.value used in composite arrangement key so chart-type switch re-applies its swap

AxisSwapper:
- passes chartType.value to get/setSelectedSwapIndex and setArrangement
- getInitialSwapIndex uses axes[] for identity when available
- Pipeline sort/showLabels/scale now come from resolved() (per-chart override
  or global default) so each chart type uses its own effective settings
- activeLabels prefers settings.axes[] (new schema) over legacy axisLabels field,
  with fallback for old JSON files that haven't been migrated
- isAxisChart now excludes heatmap (scale not valid for pie or heatmap)
- ChartSettings sort/showLabels read resolved(), write setForActiveChart()
- ScaleSelector and AutoRotateToggle read resolved(), write setForActiveChart()
- ChartCard sort/showLabels/scale/autoRotate from resolved() so chart rendering
  uses per-chart effective values rather than global store fields
…e, swap)

Rewrites useUrlRouter to persist and restore per-chart-type settings via
URL params (<type>.so/.l/.sc/.rt/.sw), with back-compat for legacy bare
s/l/sc params. AxisSwapper now initialises from activeArrangement so
URL-restored swaps are reflected immediately.
@codecov-commenter

codecov-commenter commented Jun 14, 2026

Copy link
Copy Markdown

Group cpu/os/arch/pkg under a single Meta struct on both Dataset and
HistoryEntry, slimming HistoryEntry to {tag, timestamp, meta}. Keeps the
core schema generic for arbitrary tabular data, not just benchmarks.
Meta field on Dataset is now *Meta (pointer, omitempty) so datasets
with no machine metadata emit no "meta" key in JSON. Cascades into
deepCloneDataset, buildHistory, and the TS DataSet type (meta?: Meta).
Also tightens lefthook ui-gen-check to skip the gen.go requirement
when the UI bundle is genuinely unchanged (e.g. type-only edits).
axisLabels was never released so no files need migrating.
cpu/os/arch/pkg were released as flat fields on Dataset and
HistoryEntry before 61f6745 grouped them under Meta — migrate
those instead. History entries migrated per-entry, skipping any
that already carry a Meta value.
@fahimfaisaal
fahimfaisaal merged commit e04f245 into main Jun 15, 2026
1 check passed
@fahimfaisaal
fahimfaisaal deleted the feat/per-chart-settings branch June 15, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants