@opendata-ai/openchart-core 6.28.5 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -261,10 +261,13 @@ interface MarkDef {
261
261
  * Show point markers on line/area marks.
262
262
  * - true: filled circles at each data point
263
263
  * - 'transparent': invisible hover targets (legacy behavior)
264
- * - 'endpoints': show only first and last point per series
264
+ * - 'endpoints': show only first and last point per series (hollow dots)
265
+ * - 'last' / 'first': show only the last (or first) point — filled dot,
266
+ * no white halo. Useful for highlighting the latest reading on a line
267
+ * chart and the default for sparkline mode.
265
268
  * - false: no point marks (default; uses voronoi overlay for tooltips)
266
269
  */
267
- point?: boolean | 'transparent' | 'endpoints';
270
+ point?: boolean | 'transparent' | 'endpoints' | 'last' | 'first';
268
271
  /**
269
272
  * Curve interpolation for line/area marks.
270
273
  * Maps to d3-shape curve factories.
@@ -333,6 +336,14 @@ interface AxisConfig {
333
336
  labelColor?: string;
334
337
  /** Secondary data field to display alongside each tick label. Renders in lighter weight/color. Only effective on categorical y-axis labels (horizontal bar charts). */
335
338
  labelField?: string;
339
+ /**
340
+ * Where tick labels render relative to the chart area. Editorial line/area
341
+ * y-axes default to `'inline'`: labels sit above their gridlines at the
342
+ * chart's left edge, with no left gutter, axis line, or tick marks. Other
343
+ * axis types default to `'gutter'` (the classic placement outside the chart
344
+ * area).
345
+ */
346
+ tickPosition?: 'inline' | 'gutter';
336
347
  }
337
348
  /** Scale configuration for an encoding channel. */
338
349
  interface ScaleConfig {
@@ -373,10 +384,21 @@ type ScaleType = 'linear' | 'log' | 'pow' | 'sqrt' | 'symlog' | 'time' | 'utc' |
373
384
  * Follows the Vega-Lite encoding model: field identifies the column,
374
385
  * type determines how the engine interprets values, aggregate applies
375
386
  * a transformation before encoding.
387
+ *
388
+ * @template TData - The shape of a data row. When `ChartSpec<TData>` is used
389
+ * with a typed data array, `field` is constrained to `keyof TData & string`,
390
+ * giving IDE autocomplete and compile-time typo detection. Defaults to `DataRow`
391
+ * which degrades to plain `string` (no constraint), keeping untyped specs working.
376
392
  */
377
- interface EncodingChannel {
378
- /** Data field name (column in the data array). */
379
- field: string;
393
+ interface EncodingChannel<TData extends DataRow = DataRow> {
394
+ /**
395
+ * Data field name (column in the data array).
396
+ *
397
+ * When using `ChartSpec<TData>` with a typed data array, this is constrained
398
+ * to the actual column names of `TData`. Typos fail at compile time and IDEs
399
+ * autocomplete your column names.
400
+ */
401
+ field: keyof TData & string;
380
402
  /**
381
403
  * How to interpret the field values.
382
404
  * - quantitative: continuous numbers (scale: linear)
@@ -393,18 +415,33 @@ interface EncodingChannel {
393
415
  scale?: ScaleConfig;
394
416
  /**
395
417
  * Stacking behavior for quantitative channels (Vega-Lite aligned).
396
- * - undefined | true | 'zero': stack from zero baseline (default)
418
+ *
419
+ * Vega-Lite accepts `'zero' | 'normalize' | 'center' | null`. OpenChart adds
420
+ * `true` (sugar for `'zero'`) and `false` (sugar for `null`) so callers can
421
+ * write `stack: true` / `stack: false` without learning the string forms.
422
+ * The string forms are still the canonical input — the boolean shorthand is
423
+ * normalized to the matching string before reaching layout.
424
+ *
425
+ * - undefined: chart-type default (see below)
426
+ * - true | 'zero': stack from zero baseline
397
427
  * - 'normalize': stack and normalize to fraction of total (0-1 per category)
398
428
  * - 'center': center stacks around zero (streamgraph style)
399
- * - null | false: no stacking -- renders grouped (side-by-side) bars instead
429
+ * - null | false: no stacking -- renders overlap (area) or grouped/dodged (bar)
400
430
  *
401
- * Use `stack: null` to get grouped/dodged bars. This is the idiomatic way to
402
- * compare values across categories side-by-side rather than stacked on top of
403
- * each other. Without it, multi-series bar data stacks by default.
431
+ * **Defaults differ by chart type:**
432
+ * - **Bar**: defaults to stacked. Use `stack: null` for grouped (side-by-side) bars.
433
+ * - **Area**: defaults to overlap (v6 breaking change). Use `stack: 'zero'` (or `true`)
434
+ * to opt into stacked areas. Each overlapping series renders as a translucent
435
+ * gradient band anchored at the y-domain baseline.
436
+ * - **Line**: stacking is not applied (lines always overlap).
404
437
  *
405
438
  * @example
406
439
  * // Side-by-side grouped bars (comparing 2018 vs 2022 wages by firm size):
407
440
  * "x": { "field": "pay", "type": "quantitative", "stack": null }
441
+ *
442
+ * @example
443
+ * // Stacked area (opt-in; default is overlap):
444
+ * "y": { "field": "value", "type": "quantitative", "stack": "zero" }
408
445
  */
409
446
  stack?: boolean | 'zero' | 'normalize' | 'center' | null;
410
447
  /**
@@ -445,44 +482,97 @@ interface EncodingChannel {
445
482
  }
446
483
  /**
447
484
  * Encoding object mapping visual channels to data fields.
448
- * Which channels are required depends on the mark type.
449
- * See MARK_ENCODING_RULES in encoding.ts for per-type requirements.
450
- */
451
- interface Encoding {
452
- /** Horizontal position channel. */
453
- x?: EncodingChannel;
454
- /** Vertical position channel. */
455
- y?: EncodingChannel;
456
- /** Color channel (series differentiation or heatmap). Accepts conditional definitions. */
457
- color?: EncodingChannel | ConditionalValueDef;
458
- /** Size channel (bubble charts, dot plots). Accepts conditional definitions. */
459
- size?: EncodingChannel | ConditionalValueDef;
460
- /** Detail channel (group without encoding to a visual property). */
461
- detail?: EncodingChannel;
462
- /** Secondary x position (for ranges, error bars). */
463
- x2?: EncodingChannel;
464
- /** Secondary y position (for ranges, error bars). */
465
- y2?: EncodingChannel;
466
- /** Data-driven opacity (0-1). Accepts conditional definitions. */
467
- opacity?: EncodingChannel | ConditionalValueDef;
468
- /** Point shape encoding (circle, square, diamond, triangle-up, etc.). */
469
- shape?: EncodingChannel;
470
- /** Data-driven stroke dash patterns. */
471
- strokeDash?: EncodingChannel;
472
- /** Rotation angle encoding. */
473
- angle?: EncodingChannel;
474
- /** Text content for text marks. */
475
- text?: EncodingChannel;
476
- /** Tooltip field(s). Can be a single channel or array. */
477
- tooltip?: EncodingChannel | EncodingChannel[];
478
- /** Hyperlink encoding. */
479
- href?: EncodingChannel;
480
- /** Stacking/drawing order. */
481
- order?: EncodingChannel;
482
- /** Angular position for arc marks. */
483
- theta?: EncodingChannel;
484
- /** Radial position for arc marks. */
485
- radius?: EncodingChannel;
485
+ * Which channels are required depends on the mark type — see the per-mark
486
+ * encoding interfaces (ArcEncoding, LineEncoding, etc.) and MARK_ENCODING_RULES
487
+ * in encoding.ts for the full requirements table.
488
+ *
489
+ * @template TData - Propagated from ChartSpec<TData>. Constrains `field` in
490
+ * every channel to `keyof TData & string` when a typed data row is provided.
491
+ */
492
+ interface Encoding<TData extends DataRow = DataRow> {
493
+ /**
494
+ * Horizontal position channel. Required for: bar, line, area, point, tick, rect, lollipop.
495
+ * Maps a field to the x-axis. Use `type: 'temporal'` for dates, `'nominal'` for categories,
496
+ * `'quantitative'` for numbers.
497
+ */
498
+ x?: EncodingChannel<TData>;
499
+ /**
500
+ * Vertical position channel. Required for: bar, line, area, point, tick, rect, arc, lollipop.
501
+ * For arc marks, this is the value field (slice size). For all others it's the y-axis position.
502
+ */
503
+ y?: EncodingChannel<TData>;
504
+ /**
505
+ * Color channel. Required for arc marks (determines pie/donut slice coloring).
506
+ * Optional for all other marks -- used for series differentiation on multi-series charts,
507
+ * or heatmap intensity. Accepts a conditional definition to apply colors based on data predicates.
508
+ */
509
+ color?: EncodingChannel<TData> | ConditionalValueDef<TData>;
510
+ /**
511
+ * Size channel. Used by point/bubble charts to scale dot area by a quantitative field.
512
+ * Accepts a conditional definition to vary size based on data predicates.
513
+ */
514
+ size?: EncodingChannel<TData> | ConditionalValueDef<TData>;
515
+ /**
516
+ * Detail channel. Groups data into multiple series without mapping to a visual property.
517
+ * Useful when you want separate lines per category but don't need the color to differ.
518
+ */
519
+ detail?: EncodingChannel<TData>;
520
+ /**
521
+ * Secondary x position. Used with `x` to define a horizontal span (rect marks, error bars).
522
+ * Both `x` and `x2` must be quantitative.
523
+ */
524
+ x2?: EncodingChannel<TData>;
525
+ /**
526
+ * Secondary y position. Used with `y` to define a vertical span (rect marks, error bands).
527
+ * Both `y` and `y2` must be quantitative.
528
+ */
529
+ y2?: EncodingChannel<TData>;
530
+ /**
531
+ * Data-driven opacity (0-1 range). Accepts a conditional definition to vary opacity
532
+ * based on data predicates (e.g., highlight selected points).
533
+ */
534
+ opacity?: EncodingChannel<TData> | ConditionalValueDef<TData>;
535
+ /**
536
+ * Point shape encoding. Valid values: 'circle', 'square', 'diamond', 'triangle-up',
537
+ * 'triangle-down', 'cross'. Used on point/scatter marks to differentiate series by shape.
538
+ */
539
+ shape?: EncodingChannel<TData>;
540
+ /**
541
+ * Stroke dash pattern encoding. Maps a nominal field to different dash patterns
542
+ * on line marks. Useful when color alone doesn't distinguish series well.
543
+ */
544
+ strokeDash?: EncodingChannel<TData>;
545
+ /** Rotation angle encoding for point marks. Maps a quantitative field to 0-360 degrees. */
546
+ angle?: EncodingChannel<TData>;
547
+ /**
548
+ * Text content for `text` marks. Required when mark is `'text'`.
549
+ * Not meaningful for other mark types.
550
+ */
551
+ text?: EncodingChannel<TData>;
552
+ /**
553
+ * Tooltip field(s). Shown on hover. Can be a single channel or an array for
554
+ * multi-field tooltips. Independent of the x/y/color encoding.
555
+ */
556
+ tooltip?: EncodingChannel<TData> | EncodingChannel<TData>[];
557
+ /** Hyperlink encoding. Maps a field containing URLs to clickable marks. */
558
+ href?: EncodingChannel<TData>;
559
+ /**
560
+ * Drawing order. Controls z-order and stacking sort order for bar/area marks.
561
+ * Lower values are drawn first (behind higher values).
562
+ */
563
+ order?: EncodingChannel<TData>;
564
+ /**
565
+ * Angular position for arc marks (pie/donut).
566
+ * Optional -- defaults to the `y` channel value when omitted.
567
+ * Not used by any other mark type.
568
+ */
569
+ theta?: EncodingChannel<TData>;
570
+ /**
571
+ * Radial distance from center for arc marks.
572
+ * Optional -- only meaningful on donut charts (controls inner radius boundary).
573
+ * Not used by any other mark type.
574
+ */
575
+ radius?: EncodingChannel<TData>;
486
576
  }
487
577
  /** Encoding channel for graph nodes and edges. */
488
578
  interface GraphEncodingChannel {
@@ -549,11 +639,18 @@ interface ChromeText {
549
639
  offset?: AnnotationOffset;
550
640
  }
551
641
  /**
552
- * Editorial chrome elements: title, subtitle, source attribution, byline, footer.
642
+ * Editorial chrome elements: eyebrow, title, subtitle, source attribution, byline, footer.
553
643
  * These are first-class structural elements, not string-only afterthoughts.
554
644
  * Each element can be a simple string or a ChromeText object with style overrides.
555
645
  */
556
646
  interface Chrome {
647
+ /**
648
+ * Editorial kicker/category label rendered above the title. Typically
649
+ * uppercase, tracked, and tinted with the accent color (e.g.
650
+ * "Equities · Single Ticker"). Term follows IBM Carbon and Atlassian
651
+ * Design System conventions; not part of Vega-Lite's title model.
652
+ */
653
+ eyebrow?: string | ChromeText;
557
654
  /** Main title displayed above the visualization. */
558
655
  title?: string | ChromeText;
559
656
  /** Subtitle displayed below the title, typically providing context. */
@@ -564,6 +661,12 @@ interface Chrome {
564
661
  byline?: string | ChromeText;
565
662
  /** Footer text, displayed at the very bottom. */
566
663
  footer?: string | ChromeText;
664
+ /**
665
+ * Right-anchored brand block on the footer row, paired with a small accent
666
+ * dot to its left. Visually balances the source/byline left-anchored text.
667
+ * When set, suppresses the default `tryOpenData.ai` watermark for this chart.
668
+ */
669
+ brand?: string | ChromeText;
567
670
  }
568
671
  /** Pixel offset for fine-grained annotation positioning. */
569
672
  interface AnnotationOffset {
@@ -574,6 +677,17 @@ interface AnnotationOffset {
574
677
  }
575
678
  /** Anchor direction for annotation label placement relative to the data point. */
576
679
  type AnnotationAnchor = 'top' | 'bottom' | 'left' | 'right' | 'auto';
680
+ /** Style overrides for the dot marker drawn at the connector's data-point endpoint. */
681
+ interface AnnotationDot {
682
+ /** Circle radius in pixels. Default 5. */
683
+ radius?: number;
684
+ /** Fill color. Defaults to theme background for an "open ring" look. */
685
+ fill?: string;
686
+ /** Stroke color. Defaults to theme text color. */
687
+ stroke?: string;
688
+ /** Stroke width in pixels. Default 2. */
689
+ strokeWidth?: number;
690
+ }
577
691
  /** Base properties shared by all annotation types. */
578
692
  interface AnnotationBase {
579
693
  /** Stable identifier for selection and edit callbacks. When provided, edit events include this ID for reliable element matching. */
@@ -603,6 +717,18 @@ interface TextAnnotation extends AnnotationBase {
603
717
  y: string | number;
604
718
  /** The annotation text. Required for text annotations. */
605
719
  text: string;
720
+ /**
721
+ * Optional muted second-tone text rendered below the primary `text`.
722
+ * Used for supporting context (e.g. methodology, source). Newlines in
723
+ * `text` still produce multi-line primary; subtitle is a separate block.
724
+ */
725
+ subtitle?: string;
726
+ /**
727
+ * Optional dot marker drawn at the connector's data-point endpoint.
728
+ * `true` enables the default open-ring style. Pass an object to override
729
+ * radius, fill, stroke, or strokeWidth.
730
+ */
731
+ dot?: boolean | AnnotationDot;
606
732
  /** Font size override. */
607
733
  fontSize?: number;
608
734
  /** Font weight override. */
@@ -614,10 +740,12 @@ interface TextAnnotation extends AnnotationBase {
614
740
  /**
615
741
  * Connector from label to anchor point.
616
742
  * - `true` (default): straight line
743
+ * - `'straight'`: straight line (alias of `true`)
617
744
  * - `'curve'`: curved arrow with arrowhead
745
+ * - `'drop-line'`: vertical line through the data point's x; label sits beside the line and auto-flips to the opposite side if it would overflow the chart area
618
746
  * - `false`: no connector
619
747
  */
620
- connector?: boolean | 'curve';
748
+ connector?: boolean | 'straight' | 'curve' | 'drop-line';
621
749
  /** Per-endpoint offsets for the connector line. Allows fine-tuning where the connector starts and ends. */
622
750
  connectorOffset?: {
623
751
  /** Offset for the label-end of the connector. */
@@ -770,6 +898,59 @@ interface LegendConfig {
770
898
  /** Series names to exclude from the legend. Excluded series still render in the chart. */
771
899
  exclude?: string[];
772
900
  }
901
+ /**
902
+ * Configuration for the endpoint labels column rendered at the chart's right edge
903
+ * for multi-series line/area charts. Each entry pairs the series name with its
904
+ * last formatted value, optionally anchored to the line by an open-circle marker.
905
+ *
906
+ * The column is independent of the traditional `legend` and the legacy
907
+ * end-of-line labels. Together with `legend.show`, the three suppression toggles
908
+ * follow this truth table for ≥2-series line/area charts:
909
+ *
910
+ * | `legend.show` | `endpointLabels` | Traditional legend | Endpoint column | End-of-line labels |
911
+ * |--|--|--|--|--|
912
+ * | unset | unset | hidden (auto-suppressed) | shown (default) | hidden |
913
+ * | true | unset | shown | shown | hidden |
914
+ * | unset | false | shown (auto-suppress revoked) | hidden | hidden |
915
+ * | false | false | hidden | hidden | shown (last-resort) |
916
+ * | true | false | shown | hidden | hidden |
917
+ * | false | true | hidden | shown | hidden |
918
+ * | true | true | shown | shown | hidden |
919
+ *
920
+ * Single-series charts: column is hidden by default (nothing to identify).
921
+ *
922
+ * The implementation is in `packages/engine/src/legend/suppression.ts` —
923
+ * `resolveSuppression` is the single source of truth. The table above is
924
+ * a user-facing mirror; if the two ever diverge, the engine wins. Tests
925
+ * in `legend/__tests__/suppression.test.ts` enforce every cell.
926
+ */
927
+ interface EndpointLabelsConfig {
928
+ /** Explicit on/off. When undefined, the chart auto-decides based on series count. */
929
+ show?: boolean;
930
+ /** Field to read the displayed value from. Defaults to `encoding.y.field`. */
931
+ valueField?: string;
932
+ /** d3-format string for the value. Defaults to `encoding.y.axis.format`. */
933
+ format?: string;
934
+ /** Max wrap width in pixels for long series names. Default 96. */
935
+ width?: number;
936
+ /** Render an open-circle marker on the line at the right edge. Default true. */
937
+ showMarker?: boolean;
938
+ /** Override the marker style. */
939
+ markerStyle?: {
940
+ fill?: string;
941
+ stroke?: string;
942
+ strokeWidth?: number;
943
+ radius?: number;
944
+ };
945
+ /**
946
+ * Render a thin leader line from the swatch row back to the line endpoint
947
+ * when collision-sweep displaces a label off its data point. Default false:
948
+ * the marker on the line plus the swatch in the column already pair label
949
+ * to data; the connector tends to add visual noise for small displacements.
950
+ * Opt in when labels collide hard and you need the explicit tie-back.
951
+ */
952
+ showLeader?: boolean;
953
+ }
773
954
  /** Data row: a plain object with string keys. */
774
955
  type DataRow = Record<string, unknown>;
775
956
  /** Per-series visual style overrides for line/area charts. */
@@ -866,33 +1047,153 @@ interface ChartSpecOverride {
866
1047
  crosshair?: boolean;
867
1048
  }
868
1049
  /**
869
- * Chart specification: the primary input for standard chart types.
1050
+ * A KPI/metric cell rendered above the chart in a horizontal row.
870
1051
  *
871
- * Uses the Vega-Lite `mark` property instead of `type` to specify
872
- * the visualization mark. The mark can be a string shorthand or an
873
- * object with additional properties (interpolation, point markers, etc.).
1052
+ * Used for editorial dashboards (e.g. "CLOSE $186.10 +1.4%") where the chart
1053
+ * is paired with summary statistics. Cells lay out evenly across the chart
1054
+ * width. Hidden in sparkline mode; auto-stripped when the container can't
1055
+ * fit the laid-out values.
874
1056
  */
875
- interface ChartSpec {
876
- /**
877
- * The mark type to render.
878
- * String shorthand: `mark: 'bar'`
879
- * Object with properties: `mark: { type: 'line', interpolate: 'step' }`
880
- */
881
- mark: MarkType | MarkDef;
1057
+ interface Metric {
1058
+ /** Uppercase eyebrow label, e.g. "CLOSE". */
1059
+ label: string;
1060
+ /** Primary numeric value, e.g. "$186.10". */
1061
+ value: string;
1062
+ /** Optional change indicator, e.g. "+1.4%". Rendered next to the value. */
1063
+ delta?: string;
1064
+ /** Tone for the delta. 'up' = positive (green), 'down' = negative (red). Default 'up'. */
1065
+ deltaTone?: 'up' | 'down';
1066
+ /** Optional secondary value (e.g. multiplier "10.3×"). Rendered after the delta. */
1067
+ secondary?: string;
1068
+ }
1069
+ /**
1070
+ * Encoding for arc marks (pie/donut charts).
1071
+ * - `y`: required (quantitative — the slice value)
1072
+ * - `color`: required (nominal/ordinal — the category)
1073
+ * - `theta`: optional (defaults to `y` channel)
1074
+ * - `radius`: optional (donut inner radius)
1075
+ */
1076
+ interface ArcEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1077
+ y: EncodingChannel<TData>;
1078
+ color: EncodingChannel<TData>;
1079
+ }
1080
+ /**
1081
+ * Encoding for line marks.
1082
+ * - `x`: required (temporal or ordinal — the time/category axis)
1083
+ * - `y`: required (quantitative — the value axis)
1084
+ */
1085
+ interface LineEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1086
+ x: EncodingChannel<TData>;
1087
+ y: EncodingChannel<TData>;
1088
+ }
1089
+ /**
1090
+ * Encoding for bar marks (vertical columns and horizontal bars).
1091
+ * - `x`: required
1092
+ * - `y`: required
1093
+ */
1094
+ interface BarEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1095
+ x: EncodingChannel<TData>;
1096
+ y: EncodingChannel<TData>;
1097
+ }
1098
+ /**
1099
+ * Encoding for area marks.
1100
+ * - `x`: required (temporal or ordinal)
1101
+ * - `y`: required (quantitative)
1102
+ */
1103
+ interface AreaEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1104
+ x: EncodingChannel<TData>;
1105
+ y: EncodingChannel<TData>;
1106
+ }
1107
+ /**
1108
+ * Encoding for point marks (scatter plots).
1109
+ * - `x`: required
1110
+ * - `y`: required
1111
+ */
1112
+ interface PointEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1113
+ x: EncodingChannel<TData>;
1114
+ y: EncodingChannel<TData>;
1115
+ }
1116
+ /**
1117
+ * Encoding for circle marks (dot plots).
1118
+ * - `x`: required (quantitative)
1119
+ * - `y`: required (nominal/ordinal — the category axis)
1120
+ */
1121
+ interface CircleEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1122
+ x: EncodingChannel<TData>;
1123
+ y: EncodingChannel<TData>;
1124
+ }
1125
+ /**
1126
+ * Encoding for lollipop marks.
1127
+ * - `x`: required (quantitative)
1128
+ * - `y`: required (nominal/ordinal)
1129
+ */
1130
+ interface LollipopEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1131
+ x: EncodingChannel<TData>;
1132
+ y: EncodingChannel<TData>;
1133
+ }
1134
+ /**
1135
+ * Encoding for text marks (data-positioned labels).
1136
+ * - `text`: required (the field to render as text)
1137
+ * - `x`, `y`: optional positioning
1138
+ */
1139
+ interface TextEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1140
+ text: EncodingChannel<TData>;
1141
+ }
1142
+ /**
1143
+ * Encoding for tick marks (strip/rug plots).
1144
+ * - `x`: required
1145
+ * - `y`: required
1146
+ */
1147
+ interface TickEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1148
+ x: EncodingChannel<TData>;
1149
+ y: EncodingChannel<TData>;
1150
+ }
1151
+ /**
1152
+ * Encoding for rect marks (heatmaps, 2D binned plots).
1153
+ * - `x`: required
1154
+ * - `y`: required
1155
+ */
1156
+ interface RectEncoding<TData extends DataRow = DataRow> extends Encoding<TData> {
1157
+ x: EncodingChannel<TData>;
1158
+ y: EncodingChannel<TData>;
1159
+ }
1160
+ /**
1161
+ * Properties shared across all ChartSpec mark variants.
1162
+ * Extracted to avoid repeating them in every union member.
1163
+ *
1164
+ * @internal
1165
+ */
1166
+ interface BaseChartSpec<TData extends DataRow = DataRow> {
882
1167
  /** Data array: each element is a row with field values. */
883
- data: DataRow[];
1168
+ data: TData[];
884
1169
  /** Data transforms applied in order before encoding (filter, bin, calculate, timeUnit). */
885
1170
  transform?: Transform[];
886
- /** Encoding mapping data fields to visual channels. */
887
- encoding: Encoding;
888
1171
  /** Editorial chrome (title, subtitle, source, etc.). */
889
1172
  chrome?: Chrome;
1173
+ /**
1174
+ * KPI/metric cells rendered as a horizontal row between subtitle and chart
1175
+ * area. Each cell shows a label/value pair with optional delta and secondary
1176
+ * value. Hidden in sparkline mode and auto-stripped when the container is
1177
+ * too narrow or short, or when value text would overflow its cell.
1178
+ */
1179
+ metrics?: Metric[];
890
1180
  /** Data annotations (text callouts, highlighted ranges, reference lines). */
891
1181
  annotations?: Annotation[];
892
1182
  /** Label display configuration. `false` disables all labels, `true` uses defaults. */
893
1183
  labels?: LabelSpec;
894
1184
  /** Legend display configuration (position override). */
895
1185
  legend?: LegendConfig;
1186
+ /**
1187
+ * Right-side endpoint labels column for multi-series line/area charts.
1188
+ *
1189
+ * - `true` or `EndpointLabelsConfig`: render the column.
1190
+ * - `false`: hide the column.
1191
+ * - omitted: auto-enable for multi-series line/area charts, hide otherwise.
1192
+ *
1193
+ * See {@link EndpointLabelsConfig} for the full suppression truth table that
1194
+ * relates this flag to `legend.show` and the legacy end-of-line labels.
1195
+ */
1196
+ endpointLabels?: boolean | EndpointLabelsConfig;
896
1197
  /** Whether the chart adapts to container width. Defaults to true. */
897
1198
  responsive?: boolean;
898
1199
  /** Theme configuration overrides. */
@@ -948,6 +1249,97 @@ interface ChartSpec {
948
1249
  */
949
1250
  zIndex?: number;
950
1251
  }
1252
+ /**
1253
+ * Chart specification: the primary input for standard chart types.
1254
+ *
1255
+ * Uses the Vega-Lite `mark` property instead of `type` to specify
1256
+ * the visualization mark. The mark can be a string shorthand or an
1257
+ * object with additional properties (interpolation, point markers, etc.).
1258
+ *
1259
+ * This is a discriminated union — the `mark` value determines which encoding
1260
+ * channels are required. TypeScript enforces required channels at compile time,
1261
+ * matching the runtime rules in `MARK_ENCODING_RULES`.
1262
+ *
1263
+ * @template TData - The shape of a single data row. When provided, `encoding.*.field`
1264
+ * values are constrained to `keyof TData` and IDEs autocomplete your column names.
1265
+ * Defaults to `DataRow` (no constraint) — existing untyped specs work unchanged.
1266
+ *
1267
+ * @example
1268
+ * // Typed: field autocomplete + typo detection
1269
+ * type SalesRow = { date: string; revenue: number; region: string };
1270
+ * const spec: ChartSpec<SalesRow> = {
1271
+ * mark: 'line',
1272
+ * data: rows,
1273
+ * encoding: {
1274
+ * x: { field: 'date', type: 'temporal' }, // autocompletes
1275
+ * y: { field: 'revenue', type: 'quantitative' }, // typos fail at compile time
1276
+ * }
1277
+ * };
1278
+ *
1279
+ * @example
1280
+ * // Untyped: works exactly as before (no migration needed)
1281
+ * const spec: ChartSpec = {
1282
+ * mark: 'bar',
1283
+ * data: myData,
1284
+ * encoding: { x: { field: 'category', type: 'nominal' }, y: { field: 'value', type: 'quantitative' } }
1285
+ * };
1286
+ */
1287
+ type ChartSpec<TData extends DataRow = DataRow> = (BaseChartSpec<TData> & {
1288
+ mark: 'arc' | (MarkDef & {
1289
+ type: 'arc';
1290
+ });
1291
+ encoding: ArcEncoding<TData>;
1292
+ }) | (BaseChartSpec<TData> & {
1293
+ mark: 'line' | (MarkDef & {
1294
+ type: 'line';
1295
+ });
1296
+ encoding: LineEncoding<TData>;
1297
+ }) | (BaseChartSpec<TData> & {
1298
+ mark: 'bar' | (MarkDef & {
1299
+ type: 'bar';
1300
+ });
1301
+ encoding: BarEncoding<TData>;
1302
+ }) | (BaseChartSpec<TData> & {
1303
+ mark: 'area' | (MarkDef & {
1304
+ type: 'area';
1305
+ });
1306
+ encoding: AreaEncoding<TData>;
1307
+ }) | (BaseChartSpec<TData> & {
1308
+ mark: 'point' | (MarkDef & {
1309
+ type: 'point';
1310
+ });
1311
+ encoding: PointEncoding<TData>;
1312
+ }) | (BaseChartSpec<TData> & {
1313
+ mark: 'circle' | (MarkDef & {
1314
+ type: 'circle';
1315
+ });
1316
+ encoding: CircleEncoding<TData>;
1317
+ }) | (BaseChartSpec<TData> & {
1318
+ mark: 'lollipop' | (MarkDef & {
1319
+ type: 'lollipop';
1320
+ });
1321
+ encoding: LollipopEncoding<TData>;
1322
+ }) | (BaseChartSpec<TData> & {
1323
+ mark: 'text' | (MarkDef & {
1324
+ type: 'text';
1325
+ });
1326
+ encoding: TextEncoding<TData>;
1327
+ }) | (BaseChartSpec<TData> & {
1328
+ mark: 'tick' | (MarkDef & {
1329
+ type: 'tick';
1330
+ });
1331
+ encoding: TickEncoding<TData>;
1332
+ }) | (BaseChartSpec<TData> & {
1333
+ mark: 'rect' | (MarkDef & {
1334
+ type: 'rect';
1335
+ });
1336
+ encoding: RectEncoding<TData>;
1337
+ }) | (BaseChartSpec<TData> & {
1338
+ mark: 'rule' | (MarkDef & {
1339
+ type: 'rule';
1340
+ });
1341
+ encoding: Encoding<TData>;
1342
+ });
951
1343
  /**
952
1344
  * Table specification: input for data table visualizations.
953
1345
  *
@@ -1060,14 +1452,19 @@ interface ResolveConfig {
1060
1452
  * Each element in `layer` is either a ChartSpec or another LayerSpec (nested).
1061
1453
  * Shared data, encoding, and transforms at the LayerSpec level are inherited
1062
1454
  * by children that don't define their own.
1455
+ *
1456
+ * @template TData - The shape of a single data row. When all layers share the
1457
+ * same data shape, pass it here to get field autocomplete across the layer.
1458
+ * For layers with different data shapes per child, omit TData (defaults to
1459
+ * `DataRow`) — children can be independently typed.
1063
1460
  */
1064
- interface LayerSpec {
1461
+ interface LayerSpec<TData extends DataRow = DataRow> {
1065
1462
  /** Array of child layers (ChartSpec or nested LayerSpec). */
1066
- layer: (ChartSpec | LayerSpec)[];
1463
+ layer: (ChartSpec<TData> | LayerSpec<TData>)[];
1067
1464
  /** Shared data inherited by children without their own data. */
1068
- data?: DataRow[];
1465
+ data?: TData[];
1069
1466
  /** Shared encoding inherited by children (overridden per-channel by child). */
1070
- encoding?: Encoding;
1467
+ encoding?: Encoding<TData>;
1071
1468
  /** Shared transforms. Parent transforms run before child transforms. */
1072
1469
  transform?: Transform[];
1073
1470
  /** Editorial chrome (title, subtitle, source, etc.). */
@@ -1265,8 +1662,11 @@ interface BarListEncoding {
1265
1662
  * - BarListSpec: has `type: 'barlist'`
1266
1663
  */
1267
1664
  type VizSpec = ChartSpec | LayerSpec | TableSpec | GraphSpec | SankeySpec | TileMapSpec | BarListSpec;
1268
- /** Chart spec without runtime data, for persistence/storage. */
1269
- type ChartSpecWithoutData = Omit<ChartSpec, 'data'>;
1665
+ /**
1666
+ * Chart spec without runtime data, for persistence/storage.
1667
+ * Generic: `ChartSpecWithoutData<MyRow>` constrains encoding field names.
1668
+ */
1669
+ type ChartSpecWithoutData<TData extends DataRow = DataRow> = Omit<ChartSpec<TData>, 'data'>;
1270
1670
  /** Table spec without runtime data and columns, for persistence/storage. Columns can be auto-generated via dataTable(). */
1271
1671
  type TableSpecWithoutData = Omit<TableSpec, 'data' | 'columns'>;
1272
1672
  /** Graph spec without runtime data, for persistence/storage. */
@@ -1299,7 +1699,12 @@ interface RelativeTimeRef {
1299
1699
  }
1300
1700
  /** A predicate that tests a field value against a condition. */
1301
1701
  interface FieldPredicate {
1302
- /** Data field to test. */
1702
+ /**
1703
+ * Data field to test.
1704
+ * Note: FieldPredicate is used in transforms (FilterTransform) which operate
1705
+ * on the raw DataRow type — field is typed as string here since transforms
1706
+ * can reference computed/derived fields not present in the original TData.
1707
+ */
1303
1708
  field: string;
1304
1709
  /** Equals comparison. */
1305
1710
  equal?: unknown;
@@ -1420,37 +1825,51 @@ type Transform = FilterTransform | BinTransform | CalculateTransform | TimeUnitT
1420
1825
  /**
1421
1826
  * A single condition with a test predicate and resulting value/field.
1422
1827
  * When the test passes for a datum, the condition's value/field is used.
1828
+ *
1829
+ * @template TData - Propagated from ChartSpec<TData>. Constrains `field` to
1830
+ * `keyof TData & string` when a typed data row is provided.
1423
1831
  */
1424
- interface Condition {
1832
+ interface Condition<TData extends DataRow = DataRow> {
1425
1833
  /** Predicate to test against each datum. */
1426
1834
  test: FilterPredicate;
1427
- /** Static value to use when the condition is true. */
1428
- value?: unknown;
1429
- /** Data field to use when the condition is true. */
1430
- field?: string;
1835
+ /**
1836
+ * Static value to use when the condition is true.
1837
+ * Accepted values: CSS color string, opacity (0-1), size number, or boolean flag.
1838
+ */
1839
+ value?: string | number | boolean | null;
1840
+ /**
1841
+ * Data field to use when the condition is true.
1842
+ * Constrained to column names of `TData` when using `ChartSpec<TData>`.
1843
+ */
1844
+ field?: keyof TData & string;
1431
1845
  /** Field type for the conditional field. */
1432
1846
  type?: FieldType;
1433
1847
  }
1434
1848
  /**
1435
1849
  * A conditional value definition for an encoding channel.
1436
1850
  * Evaluates conditions in order, falling back to the default value.
1851
+ *
1852
+ * @template TData - Propagated from ChartSpec<TData>.
1437
1853
  */
1438
- interface ConditionalValueDef {
1854
+ interface ConditionalValueDef<TData extends DataRow = DataRow> {
1439
1855
  /** One or more conditions to evaluate. */
1440
- condition: Condition | Condition[];
1441
- /** Default value when no condition matches. */
1442
- value?: unknown;
1856
+ condition: Condition<TData> | Condition<TData>[];
1857
+ /**
1858
+ * Default value when no condition matches.
1859
+ * Accepted values: CSS color string, opacity (0-1), size number, or boolean flag.
1860
+ */
1861
+ value?: string | number | boolean | null;
1443
1862
  }
1444
1863
  /**
1445
1864
  * Check if a channel definition is a regular EncodingChannel (has 'field' at top level).
1446
1865
  * Use this to narrow `EncodingChannel | ConditionalValueDef` in encoding channels
1447
1866
  * that support conditional encoding (color, size, opacity).
1448
1867
  */
1449
- declare function isEncodingChannel(def: EncodingChannel | ConditionalValueDef | undefined): def is EncodingChannel;
1868
+ declare function isEncodingChannel<TData extends DataRow = DataRow>(def: EncodingChannel<TData> | ConditionalValueDef<TData> | undefined): def is EncodingChannel<TData>;
1450
1869
  /**
1451
1870
  * Check if a channel definition is a ConditionalValueDef.
1452
1871
  */
1453
- declare function isConditionalDef(def: EncodingChannel | ConditionalValueDef | undefined): def is ConditionalValueDef;
1872
+ declare function isConditionalDef<TData extends DataRow = DataRow>(def: EncodingChannel<TData> | ConditionalValueDef<TData> | undefined): def is ConditionalValueDef<TData>;
1454
1873
  /** All valid mark type strings for runtime checking. */
1455
1874
  declare const MARK_TYPES: ReadonlySet<string>;
1456
1875
  /** @deprecated Use MARK_TYPES instead. */
@@ -1727,6 +2146,10 @@ interface ThemeColors {
1727
2146
  annotationFill: string;
1728
2147
  /** Annotation text color. */
1729
2148
  annotationText: string;
2149
+ /** Semantic color for positive/up-trend values (e.g. sparkline trend coloring). */
2150
+ positive: string;
2151
+ /** Semantic color for negative/down-trend values (e.g. sparkline trend coloring). */
2152
+ negative: string;
1730
2153
  }
1731
2154
  /** Font size presets for the typography scale. */
1732
2155
  interface ThemeFontSizes {
@@ -1789,6 +2212,7 @@ interface ChromeDefaults {
1789
2212
  }
1790
2213
  /** Default chrome styles for each element type. */
1791
2214
  interface ThemeChromeDefaults {
2215
+ eyebrow: ChromeDefaults;
1792
2216
  title: ChromeDefaults;
1793
2217
  subtitle: ChromeDefaults;
1794
2218
  source: ChromeDefaults;
@@ -1904,11 +2328,13 @@ interface ResolvedChrome {
1904
2328
  /** Total height consumed by chrome elements below the chart area. */
1905
2329
  bottomHeight: number;
1906
2330
  /** Resolved chrome elements. Only present if specified in the spec. */
2331
+ eyebrow?: ResolvedChromeElement;
1907
2332
  title?: ResolvedChromeElement;
1908
2333
  subtitle?: ResolvedChromeElement;
1909
2334
  source?: ResolvedChromeElement;
1910
2335
  byline?: ResolvedChromeElement;
1911
2336
  footer?: ResolvedChromeElement;
2337
+ brand?: ResolvedChromeElement;
1912
2338
  }
1913
2339
  /** A single axis tick with computed position and formatted label. */
1914
2340
  interface AxisTick {
@@ -1962,15 +2388,27 @@ interface AxisLayout {
1962
2388
  labelOverlap?: boolean | 'parity' | 'greedy';
1963
2389
  /** Whether to flush labels to the axis edges. */
1964
2390
  labelFlush?: boolean;
2391
+ /**
2392
+ * Where tick labels render relative to the chart area.
2393
+ * `'inline'` puts y-axis labels above their gridlines at chart-area x and
2394
+ * suppresses the axis line and tick marks. `'gutter'` (default) keeps the
2395
+ * classic outside-the-area placement.
2396
+ */
2397
+ tickPosition?: 'inline' | 'gutter';
1965
2398
  }
1966
2399
  /** Accessibility attributes for a mark. */
1967
2400
  interface MarkAria {
1968
- /** ARIA label for the mark. */
1969
- label: string;
2401
+ /** ARIA label for the mark. Optional when `decorative: true` — decorative
2402
+ * marks render with `aria-hidden="true"` and don't need a label. */
2403
+ label?: string;
1970
2404
  /** Optional longer description. */
1971
2405
  description?: string;
1972
2406
  /** ARIA role override. */
1973
2407
  role?: string;
2408
+ /** When true, the mark is purely decorative (e.g. a sparkline endpoint dot
2409
+ * that duplicates an existing data point). Renderers translate this into
2410
+ * `aria-hidden="true"` and skip the mark from the screen-reader data table. */
2411
+ decorative?: boolean;
1974
2412
  }
1975
2413
  /**
1976
2414
  * Line mark: a series of connected points.
@@ -2079,6 +2517,18 @@ interface RectMark {
2079
2517
  strokeWidth?: number;
2080
2518
  /** Corner radius. */
2081
2519
  cornerRadius?: number;
2520
+ /**
2521
+ * Which corners receive `cornerRadius`. Defaults to all four when unset.
2522
+ * Used by stacked bars/columns to round only the leading edge of the
2523
+ * topmost (vertical) or rightmost (horizontal) segment so the seams
2524
+ * between stacked segments stay square and visually contiguous.
2525
+ */
2526
+ cornerRadiusSides?: {
2527
+ tl?: boolean;
2528
+ tr?: boolean;
2529
+ br?: boolean;
2530
+ bl?: boolean;
2531
+ };
2082
2532
  /** Original data row. */
2083
2533
  data: Record<string, unknown>;
2084
2534
  /** Resolved label. */
@@ -2280,12 +2730,14 @@ interface ResolvedLabel {
2280
2730
  connector?: {
2281
2731
  /** Connector start (at the label). */
2282
2732
  from: Point;
2283
- /** Connector end (at the data point). */
2733
+ /** Connector end (pulled back from the data point so the line doesn't touch it). */
2284
2734
  to: Point;
2735
+ /** Actual data point the connector is calling out. Renderer uses this for the endpoint marker. */
2736
+ endpoint?: Point;
2285
2737
  /** Connector line color. */
2286
2738
  stroke: string;
2287
- /** Connector style: straight line or curved arrow. */
2288
- style: 'straight' | 'curve';
2739
+ /** Connector style: straight line, curved arrow, or vertical drop-line through the data point. */
2740
+ style: 'straight' | 'curve' | 'drop-line';
2289
2741
  };
2290
2742
  /** Background color behind the label text. */
2291
2743
  background?: string;
@@ -2319,6 +2771,28 @@ interface ResolvedAnnotation {
2319
2771
  strokeWidth?: number;
2320
2772
  /** Z-index for render ordering. Higher values render on top. */
2321
2773
  zIndex?: number;
2774
+ /**
2775
+ * For text annotations: optional dot marker drawn at the connector's
2776
+ * data-point endpoint. Coordinates match `label.connector.to` exactly.
2777
+ */
2778
+ dot?: {
2779
+ x: number;
2780
+ y: number;
2781
+ radius: number;
2782
+ fill: string;
2783
+ stroke: string;
2784
+ strokeWidth: number;
2785
+ };
2786
+ /**
2787
+ * For text annotations: optional muted subtitle rendered below the primary
2788
+ * label. Positioned `lineHeight * primaryLineCount + gap` below `label.y`.
2789
+ */
2790
+ subtitle?: {
2791
+ text: string;
2792
+ x: number;
2793
+ y: number;
2794
+ style: TextStyle;
2795
+ };
2322
2796
  }
2323
2797
  /** A single entry in a categorical legend. */
2324
2798
  interface LegendEntry {
@@ -2354,6 +2828,8 @@ interface CategoricalLegendLayout extends BaseLegendLayout {
2354
2828
  swatchGap: number;
2355
2829
  /** Gap between entries. */
2356
2830
  entryGap: number;
2831
+ /** Fill color for the rounded chip background behind the colored bar. */
2832
+ swatchChipFill: string;
2357
2833
  }
2358
2834
  /** A color stop in a gradient legend. */
2359
2835
  interface GradientColorStop {
@@ -2377,6 +2853,55 @@ interface GradientLegendLayout extends BaseLegendLayout {
2377
2853
  }
2378
2854
  /** Resolved legend layout — either categorical (swatches) or gradient (continuous bar). */
2379
2855
  type LegendLayout = CategoricalLegendLayout | GradientLegendLayout;
2856
+ /** A single resolved endpoint-label entry, fully positioned. */
2857
+ interface EndpointLabelEntry {
2858
+ /** Series identifier (matches mark.seriesKey). */
2859
+ seriesKey: string;
2860
+ /** Pre-wrapped label text lines. */
2861
+ labelLines: string[];
2862
+ /** Formatted value string for the last data point. */
2863
+ value: string;
2864
+ /** Series color. */
2865
+ color: string;
2866
+ /** True pixel y of the last data point on the line. */
2867
+ dataY: number;
2868
+ /** Displaced y after the bidirectional collision sweep. */
2869
+ labelY: number;
2870
+ /** True when |labelY - dataY| exceeds the threshold (renderer draws a leader line). */
2871
+ showLeader: boolean;
2872
+ /** Optional anchor circle drawn on the line at the chart's right edge. */
2873
+ marker?: {
2874
+ x: number;
2875
+ y: number;
2876
+ fill: string;
2877
+ stroke: string;
2878
+ strokeWidth: number;
2879
+ radius: number;
2880
+ };
2881
+ }
2882
+ /** Resolved layout for the endpoint labels column. */
2883
+ interface EndpointLabelsLayout {
2884
+ /** Per-series resolved entries. */
2885
+ entries: EndpointLabelEntry[];
2886
+ /** Bounding box for the column. */
2887
+ bounds: Rect;
2888
+ /** Style for the series-name text. */
2889
+ labelStyle: TextStyle;
2890
+ /** Style for the formatted value text. */
2891
+ valueStyle: TextStyle;
2892
+ /** Width of the colored swatch line drawn left of the label. */
2893
+ swatchSize: number;
2894
+ /** Horizontal gap between swatch and label text. */
2895
+ gap: number;
2896
+ /**
2897
+ * Vertical gap between the last wrapped label line and the value text
2898
+ * directly below it. Resolved by the engine so the renderer can't drift
2899
+ * away from the height the collision sweep budgeted for each entry.
2900
+ */
2901
+ valueGap: number;
2902
+ /** Fill color for the rounded chip background behind the colored bar. */
2903
+ swatchChipFill: string;
2904
+ }
2380
2905
  /** A single field-value pair in a tooltip. */
2381
2906
  interface TooltipField {
2382
2907
  /** Field label (e.g. "GDP Growth"). */
@@ -2419,6 +2944,36 @@ interface ResolvedAnimation {
2419
2944
  /** Delay before annotations animate in (ms after marks). */
2420
2945
  annotationDelay: number;
2421
2946
  }
2947
+ /**
2948
+ * A resolved KPI metric cell with computed positions for label, value, and
2949
+ * supplementary text spans. Cells render as a row above the chart area.
2950
+ */
2951
+ interface ResolvedMetricCell {
2952
+ /** Cell left x in layout coordinates. */
2953
+ x: number;
2954
+ /** Cell width. */
2955
+ cellWidth: number;
2956
+ /** Baseline y for the uppercase label. */
2957
+ labelY: number;
2958
+ /** Baseline y for the primary value. */
2959
+ valueY: number;
2960
+ /** The original metric spec (label/value/delta/etc.). */
2961
+ metric: Metric;
2962
+ /** True if value+delta+secondary would overflow cellWidth. Set by layout for diagnostics. */
2963
+ overflowed: boolean;
2964
+ }
2965
+ /**
2966
+ * The full metric-bar layout. Present only when spec.metrics is supplied
2967
+ * and the bar fits the container.
2968
+ */
2969
+ interface ResolvedMetricBar {
2970
+ /** Top y of the metric row in layout coordinates. */
2971
+ y: number;
2972
+ /** Total reserved height of the row. */
2973
+ height: number;
2974
+ /** Cells laid out evenly across the chart width. */
2975
+ cells: ResolvedMetricCell[];
2976
+ }
2422
2977
  /**
2423
2978
  * ChartLayout: the complete engine output for chart visualizations.
2424
2979
  *
@@ -2432,6 +2987,8 @@ interface ChartLayout {
2432
2987
  area: Rect;
2433
2988
  /** Resolved chrome text elements with positions and styles. */
2434
2989
  chrome: ResolvedChrome;
2990
+ /** Resolved KPI metric bar. Present only when spec.metrics is supplied and fits. */
2991
+ metrics?: ResolvedMetricBar;
2435
2992
  /** Resolved axis layouts. */
2436
2993
  axes: {
2437
2994
  x?: AxisLayout;
@@ -2445,6 +3002,12 @@ interface ChartLayout {
2445
3002
  annotations: ResolvedAnnotation[];
2446
3003
  /** Legend layout (position, entries, bounds). */
2447
3004
  legend: LegendLayout;
3005
+ /**
3006
+ * Right-side endpoint labels column for multi-series line/area charts.
3007
+ * Empty `entries` means the column is suppressed (single-series, opt-out, or
3008
+ * auto-suppressed by the truth table).
3009
+ */
3010
+ endpointLabels?: EndpointLabelsLayout;
2448
3011
  /** Tooltip descriptors keyed by a mark identifier. */
2449
3012
  tooltipDescriptors: Map<string, TooltipContent>;
2450
3013
  /** Accessibility metadata. */
@@ -3045,25 +3608,14 @@ declare function meetsAA(fg: string, bg: string, largeText?: boolean): boolean;
3045
3608
  declare function findAccessibleColor(baseColor: string, bg: string, targetRatio?: number): string;
3046
3609
 
3047
3610
  /**
3048
- * Color palettes for @opendata-ai.
3049
- *
3050
- * Categorical palette is Infrographic-influenced with WCAG AA contrast
3051
- * for large text (3:1 ratio) on both light (#ffffff) and dark (#1a1a2e)
3052
- * backgrounds. Several colors do not meet the stricter 4.5:1 ratio
3053
- * required for normal-sized body text. This is acceptable because chart
3054
- * marks (bars, lines, areas, points) are large visual elements.
3611
+ * Default categorical palette. Cyan-led OKLCH-balanced multi-hue ramp.
3055
3612
  *
3056
- * Sequential palettes: 5-7 stops from light to dark.
3057
- * Diverging palettes: 7 stops with a neutral midpoint.
3613
+ * Hex values precomputed from OKLCH via the standard OKLab -> linear sRGB
3614
+ * -> sRGB pipeline. Documented OKLCH source values are the contract; if
3615
+ * the conversion math changes, regenerate from the source rather than
3616
+ * editing hex literals directly.
3058
3617
  */
3059
- /**
3060
- * Default categorical palette. 10 visually distinct colors that meet
3061
- * WCAG AA contrast for large text (3:1) on both white and near-black
3062
- * backgrounds. Some colors fall below the 4.5:1 threshold for normal
3063
- * body text. Influenced by Infrographic's editorial palette with tweaks
3064
- * for accessibility and colorblind distinguishability.
3065
- */
3066
- declare const CATEGORICAL_PALETTE: readonly ["#1b7fa3", "#c44e52", "#6a9f58", "#d47215", "#507e79", "#9a6a8d", "#c4636b", "#9c755f", "#a88f22", "#858078"];
3618
+ declare const CATEGORICAL_PALETTE: readonly ["#06b6d4", "#eb7289", "#3bb974", "#ad87ed", "#e69c3a", "#4ba3f7", "#eb8656", "#8494fa", "#00b9c3"];
3067
3619
  type CategoricalPalette = typeof CATEGORICAL_PALETTE;
3068
3620
  /** Sequential palette definition: an array of color stops from light to dark. */
3069
3621
  interface SequentialPalette {
@@ -3106,14 +3658,16 @@ declare function adaptTheme(theme: ResolvedTheme): ResolvedTheme;
3106
3658
  /**
3107
3659
  * Default theme definition.
3108
3660
  *
3109
- * Tuned to match Infrographic's visual weight and editorial style.
3110
- * Inter font family, typography hierarchy for chrome, and color
3111
- * palettes from the colors module.
3661
+ * Editorial design system with cyan-led categorical palette, Inter
3662
+ * Variable typography (weights 400/510/590), and zinc-based achromatic
3663
+ * surfaces. Light is the default surface; dark adaptation lives in
3664
+ * dark-mode.ts.
3665
+ *
3666
+ * resolveTheme() deep-merges user overrides onto this base.
3112
3667
  */
3113
3668
 
3114
3669
  /**
3115
3670
  * The default theme. All fields are required and fully specified.
3116
- * resolveTheme() deep-merges user overrides onto this base.
3117
3671
  */
3118
3672
  declare const DEFAULT_THEME: Theme;
3119
3673
 
@@ -3169,8 +3723,14 @@ declare function resolveTheme(userTheme?: ThemeConfig, base?: Theme): ResolvedTh
3169
3723
  * @param measureText - Optional real text measurement function from the adapter.
3170
3724
  * @param chromeMode - Chrome display mode: full, compact (title only), or hidden.
3171
3725
  * @param padding - Override padding (for scaled padding from dimensions).
3172
- */
3173
- declare function computeChrome(chrome: Chrome | undefined, theme: ResolvedTheme, width: number, measureText?: MeasureTextFn, chromeMode?: ChromeMode, padding?: number, watermark?: boolean): ResolvedChrome;
3726
+ * @param watermark - Whether the brand watermark renders (affects bottom space).
3727
+ * @param bottomLegendHeight - Reserved height for a bottom-positioned legend
3728
+ * (legend bounds height + gap). When > 0, source/byline/footer y positions
3729
+ * are shifted down by this amount so the chrome stacks BELOW the legend
3730
+ * rather than colliding with it. The returned `bottomHeight` includes this
3731
+ * reservation, so callers should not double-reserve it in margin math.
3732
+ */
3733
+ declare function computeChrome(chrome: Chrome | undefined, theme: ResolvedTheme, width: number, measureText?: MeasureTextFn, chromeMode?: ChromeMode, padding?: number, watermark?: boolean, bottomLegendHeight?: number): ResolvedChrome;
3174
3734
 
3175
3735
  /**
3176
3736
  * Estimate the rendered width of a text string.
@@ -3646,4 +4206,4 @@ interface TileMapBuilderOptions {
3646
4206
  */
3647
4207
  declare function tileMap(data: Record<string, number | null> | DataRow[], options?: TileMapBuilderOptions): TileMapSpec;
3648
4208
 
3649
- export { type A11yMetadata, AXIS_TITLE_OFFSET_COMPACT, AXIS_TITLE_OFFSET_DEFAULT, AXIS_TITLE_TRAILING_PAD, type AggregateOp, type AggregateTransform, type AnimationConfig, type AnimationEase, type AnimationPhaseConfig, type AnimationSpec, type AnimationStagger, type Annotation, type AnnotationAnchor, type AnnotationOffset, type AnnotationPosition, type ArcMark, type AreaMark, type AxisConfig, type AxisLabelDensity, type AxisLayout, type AxisTick, BRAND_FONT_SIZE, BRAND_MIN_WIDTH, BRAND_RESERVE_WIDTH, BREAKPOINT_COMPACT_MAX, BREAKPOINT_MEDIUM_MAX, type BarColumnConfig, type BarListEncoding, type BarListLayout, type BarListRowMark, type BarListSpec, type BarListSpecWithoutData, type BarTableCell, type BaseLegendLayout, type BinParams, type BinTransform, type Breakpoint, CATEGORICAL_PALETTE, CHART_ENCODING_RULES, CHART_TYPES, COMPACT_WIDTH, type CalculateExpression, type CalculateTransform, type CategoricalLegendLayout, type CategoricalPalette, type CategoryColorsConfig, type CategoryTableCell, type CellStyle, type ChannelRule, type ChartBuilderOptions, type ChartEventHandlers, type ChartLayout, type ChartSpec, type ChartSpecOverride, type ChartSpecWithoutData, type ChartType, type Chrome, type ChromeDefaults, type ChromeKey, type ChromeMode, type ChromeText, type ChromeTextStyle, type ColorBlindnessType, type ColumnConfig, type CompileOptions, type CompileTableOptions, type Condition, type ConditionalValueDef, DEFAULT_THEME, DIVERGING_PALETTES, type DarkMode, type DataRow, type DateGranularity, type Display, type DivergingPalette, EXTENDED_OFFSET_STRATEGIES, type ElementEdit, type ElementRef, type Encoding, type EncodingChannel, type EncodingRule, type FieldPredicate, type FieldRef, type FieldType, type FilterPredicate, type FilterTransform, type FlagTableCell, type FoldTransform, GRAPH_ENCODING_RULES, type GradientColorStop, type GradientDef, type GradientLegendLayout, type GradientStop, type GraphChannelRule, type GraphEdge, type GraphEdgeLayout, type GraphEncoding, type GraphEncodingChannel, type GraphLayout, type GraphLayoutConfig, type GraphNode, type GraphNodeLayout, type GraphSpec, type GraphSpecWithoutData, type Gridline, HEIGHT_CRAMPED_MAX, HEIGHT_SHORT_MAX, HPAD_COMPACT_FRACTION, HPAD_COMPACT_MIN, type HeatmapColumnConfig, type HeatmapTableCell, type HeightClass, type ImageColumnConfig, type ImageTableCell, LABEL_GAP_COMPACT, LABEL_GAP_DEFAULT, LABEL_GAP_NARROW_MAX, type LabelCandidate, type LabelConfig, type LabelDensity, type LabelMode, type LabelPriority, type LabelSpec, type LayerSpec, type LayoutStrategy, type LegendConfig, type LegendEntry, type LegendLayout, type LegendPosition, type LineMark, type LinearGradient, type LogicalAnd, type LogicalNot, type LogicalOr, MARK_DISPLAY_NAMES, MARK_ENCODING_RULES, MARK_TYPES, MAX_LEFT_LABEL_FRACTION_COMPACT, MAX_LEFT_LABEL_FRACTION_DEFAULT, MAX_LEFT_LABEL_FRACTION_MEDIUM, MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX, type Margins, type Mark, type MarkAria, type MarkDef, type MarkEvent, type MarkType, type MeasureTextFn, NARROW_VIEWPORT_MAX, type NodeOverride, OFFSET_STRATEGIES, type OffsetStrategy, type PaginationState, type Point, type PointMark, type RadialGradient, type RangeAnnotation, type Rect, type RectMark, type RefLineAnnotation, type RelativeTimeRef, type ResolveConfig, type ResolveMode, type ResolvedAnimation, type ResolvedAnnotation, type ResolvedChrome, type ResolvedChromeElement, type ResolvedColumn, type ResolvedLabel, type ResolvedTheme, type RuleMarkLayout, SEQUENTIAL_PALETTES, type SankeyEncoding, type SankeyLayout, type SankeyLinkColor, type SankeyLinkMark, type SankeyNodeAlign, type SankeyNodeMark, type SankeySpec, type SankeySpecWithoutData, type ScaleConfig, type ScaleType, type SequentialPalette, type SeriesStyle, type SortState, type SparklineColumnConfig, type SparklineData, type SparklineTableCell, type StoredVizSpec, TICK_LABEL_OFFSET, TOP_PAD_EXTRA_NARROW, TOP_PAD_NARROW_MAX, type TableBuilderOptions, type TableCell, type TableCellBase, type TableLayout, type TableRow, type TableSpec, type TableSpecWithoutData, type TextAnnotation, type TextMarkLayout, type TextStyle, type TextTableCell, type Theme, type ThemeChromeDefaults, type ThemeColors, type ThemeConfig, type ThemeFontSizes, type ThemeFontWeights, type ThemeFonts, type ThemeSpacing, type TickMarkLayout, type TileMapBuilderOptions, type TileMapEncoding, type TileMapLayout, type TileMapPalette, type TileMapSpec, type TileMapSpecWithoutData, type TileMapTileMark, type TimeUnit, type TimeUnitTransform, type TooltipContent, type TooltipField, type Transform, type VizSpec, type WindowFieldDef, type WindowOp, type WindowSortField, type WindowTransform, abbreviateNumber, adaptColorForDarkMode, adaptTheme, areaChart, barChart, buildD3Formatter, buildTemporalFormatter, checkPaletteDistinguishability, columnChart, computeChrome, computeLabelBounds, contrastRatio, dataTable, detectCollision, donutChart, dotChart, elementRef, estimateTextWidth, findAccessibleColor, formatDate, formatNumber, generateAltText, generateAriaLabels, generateDataTable, getAxisTitleOffset, getBreakpoint, getHeightClass, getLayoutStrategy, getRepresentativeColor, inferFieldType, isBarListSpec, isChartSpec, isConditionalDef, isEncodingChannel, isGradientDef, isGraphSpec, isLayerSpec, isRangeAnnotation, isRefLineAnnotation, isSankeySpec, isTableSpec, isTextAnnotation, isTileMapSpec, lineChart, meetsAA, pieChart, resolveCollisions, resolveMarkDef, resolveMarkType, resolveTheme, scatterChart, simulateColorBlindness, tileMap, wrapText };
4209
+ export { type A11yMetadata, AXIS_TITLE_OFFSET_COMPACT, AXIS_TITLE_OFFSET_DEFAULT, AXIS_TITLE_TRAILING_PAD, type AggregateOp, type AggregateTransform, type AnimationConfig, type AnimationEase, type AnimationPhaseConfig, type AnimationSpec, type AnimationStagger, type Annotation, type AnnotationAnchor, type AnnotationOffset, type AnnotationPosition, type ArcEncoding, type ArcMark, type AreaEncoding, type AreaMark, type AxisConfig, type AxisLabelDensity, type AxisLayout, type AxisTick, BRAND_FONT_SIZE, BRAND_MIN_WIDTH, BRAND_RESERVE_WIDTH, BREAKPOINT_COMPACT_MAX, BREAKPOINT_MEDIUM_MAX, type BarColumnConfig, type BarEncoding, type BarListEncoding, type BarListLayout, type BarListRowMark, type BarListSpec, type BarListSpecWithoutData, type BarTableCell, type BaseLegendLayout, type BinParams, type BinTransform, type Breakpoint, CATEGORICAL_PALETTE, CHART_ENCODING_RULES, CHART_TYPES, COMPACT_WIDTH, type CalculateExpression, type CalculateTransform, type CategoricalLegendLayout, type CategoricalPalette, type CategoryColorsConfig, type CategoryTableCell, type CellStyle, type ChannelRule, type ChartBuilderOptions, type ChartEventHandlers, type ChartLayout, type ChartSpec, type ChartSpecOverride, type ChartSpecWithoutData, type ChartType, type Chrome, type ChromeDefaults, type ChromeKey, type ChromeMode, type ChromeText, type ChromeTextStyle, type CircleEncoding, type ColorBlindnessType, type ColumnConfig, type CompileOptions, type CompileTableOptions, type Condition, type ConditionalValueDef, DEFAULT_THEME, DIVERGING_PALETTES, type DarkMode, type DataRow, type DateGranularity, type Display, type DivergingPalette, EXTENDED_OFFSET_STRATEGIES, type ElementEdit, type ElementRef, type Encoding, type EncodingChannel, type EncodingRule, type EndpointLabelEntry, type EndpointLabelsConfig, type EndpointLabelsLayout, type FieldPredicate, type FieldRef, type FieldType, type FilterPredicate, type FilterTransform, type FlagTableCell, type FoldTransform, GRAPH_ENCODING_RULES, type GradientColorStop, type GradientDef, type GradientLegendLayout, type GradientStop, type GraphChannelRule, type GraphEdge, type GraphEdgeLayout, type GraphEncoding, type GraphEncodingChannel, type GraphLayout, type GraphLayoutConfig, type GraphNode, type GraphNodeLayout, type GraphSpec, type GraphSpecWithoutData, type Gridline, HEIGHT_CRAMPED_MAX, HEIGHT_SHORT_MAX, HPAD_COMPACT_FRACTION, HPAD_COMPACT_MIN, type HeatmapColumnConfig, type HeatmapTableCell, type HeightClass, type ImageColumnConfig, type ImageTableCell, LABEL_GAP_COMPACT, LABEL_GAP_DEFAULT, LABEL_GAP_NARROW_MAX, type LabelCandidate, type LabelConfig, type LabelDensity, type LabelMode, type LabelPriority, type LabelSpec, type LayerSpec, type LayoutStrategy, type LegendConfig, type LegendEntry, type LegendLayout, type LegendPosition, type LineEncoding, type LineMark, type LinearGradient, type LogicalAnd, type LogicalNot, type LogicalOr, type LollipopEncoding, MARK_DISPLAY_NAMES, MARK_ENCODING_RULES, MARK_TYPES, MAX_LEFT_LABEL_FRACTION_COMPACT, MAX_LEFT_LABEL_FRACTION_DEFAULT, MAX_LEFT_LABEL_FRACTION_MEDIUM, MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX, type Margins, type Mark, type MarkAria, type MarkDef, type MarkEvent, type MarkType, type MeasureTextFn, type Metric, NARROW_VIEWPORT_MAX, type NodeOverride, OFFSET_STRATEGIES, type OffsetStrategy, type PaginationState, type Point, type PointEncoding, type PointMark, type RadialGradient, type RangeAnnotation, type Rect, type RectEncoding, type RectMark, type RefLineAnnotation, type RelativeTimeRef, type ResolveConfig, type ResolveMode, type ResolvedAnimation, type ResolvedAnnotation, type ResolvedChrome, type ResolvedChromeElement, type ResolvedColumn, type ResolvedLabel, type ResolvedMetricBar, type ResolvedMetricCell, type ResolvedTheme, type RuleMarkLayout, SEQUENTIAL_PALETTES, type SankeyEncoding, type SankeyLayout, type SankeyLinkColor, type SankeyLinkMark, type SankeyNodeAlign, type SankeyNodeMark, type SankeySpec, type SankeySpecWithoutData, type ScaleConfig, type ScaleType, type SequentialPalette, type SeriesStyle, type SortState, type SparklineColumnConfig, type SparklineData, type SparklineTableCell, type StoredVizSpec, TICK_LABEL_OFFSET, TOP_PAD_EXTRA_NARROW, TOP_PAD_NARROW_MAX, type TableBuilderOptions, type TableCell, type TableCellBase, type TableLayout, type TableRow, type TableSpec, type TableSpecWithoutData, type TextAnnotation, type TextEncoding, type TextMarkLayout, type TextStyle, type TextTableCell, type Theme, type ThemeChromeDefaults, type ThemeColors, type ThemeConfig, type ThemeFontSizes, type ThemeFontWeights, type ThemeFonts, type ThemeSpacing, type TickEncoding, type TickMarkLayout, type TileMapBuilderOptions, type TileMapEncoding, type TileMapLayout, type TileMapPalette, type TileMapSpec, type TileMapSpecWithoutData, type TileMapTileMark, type TimeUnit, type TimeUnitTransform, type TooltipContent, type TooltipField, type Transform, type VizSpec, type WindowFieldDef, type WindowOp, type WindowSortField, type WindowTransform, abbreviateNumber, adaptColorForDarkMode, adaptTheme, areaChart, barChart, buildD3Formatter, buildTemporalFormatter, checkPaletteDistinguishability, columnChart, computeChrome, computeLabelBounds, contrastRatio, dataTable, detectCollision, donutChart, dotChart, elementRef, estimateTextWidth, findAccessibleColor, formatDate, formatNumber, generateAltText, generateAriaLabels, generateDataTable, getAxisTitleOffset, getBreakpoint, getHeightClass, getLayoutStrategy, getRepresentativeColor, inferFieldType, isBarListSpec, isChartSpec, isConditionalDef, isEncodingChannel, isGradientDef, isGraphSpec, isLayerSpec, isRangeAnnotation, isRefLineAnnotation, isSankeySpec, isTableSpec, isTextAnnotation, isTileMapSpec, lineChart, meetsAA, pieChart, resolveCollisions, resolveMarkDef, resolveMarkType, resolveTheme, scatterChart, simulateColorBlindness, tileMap, wrapText };