basalt-ui 1.29.2 → 1.30.1

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.
Files changed (149) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/MIGRATING.md +901 -366
  3. package/README.md +23 -2
  4. package/agent/rules/basalt-controls.md +34 -34
  5. package/agent/skills/basalt-app/SKILL.md +4 -2
  6. package/agent/templates/query-client.ts.tpl +18 -4
  7. package/configs/lefthook.yml +51 -19
  8. package/configs/oxlint-plugin.js +210 -84
  9. package/configs/oxlint-plugin.test.ts +182 -5
  10. package/configs/oxlint.json +4 -4
  11. package/dist/charts/index.d.ts +1 -1
  12. package/dist/charts/index.d.ts.map +1 -1
  13. package/dist/charts/kinds/BandStrip.d.ts +5 -3
  14. package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
  15. package/dist/charts/kinds/Bars.d.ts +4 -2
  16. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  17. package/dist/charts/kinds/Donut.d.ts +4 -2
  18. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  19. package/dist/charts/kinds/DualPanel.d.ts +4 -2
  20. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  21. package/dist/charts/kinds/Heatmap.d.ts +5 -2
  22. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  23. package/dist/charts/kinds/MirroredBars.d.ts +4 -2
  24. package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
  25. package/dist/charts/kinds/MultiLine.d.ts +4 -2
  26. package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
  27. package/dist/charts/kinds/StackedArea.d.ts +4 -2
  28. package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
  29. package/dist/charts/kinds/ZonedLine.d.ts +4 -2
  30. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  31. package/dist/charts/primitives/CartesianChart.d.ts +4 -1
  32. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  33. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  34. package/dist/charts/primitives/ChartCard.js +2 -2
  35. package/dist/charts/primitives/ChartFrame.d.ts +25 -4
  36. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  37. package/dist/charts/primitives/ChartFrame.js +23 -16
  38. package/dist/charts/primitives/chart-frame-layout.d.ts +115 -6
  39. package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
  40. package/dist/charts/primitives/chart-frame-layout.js +46 -6
  41. package/dist/cli/check-theme.d.ts.map +1 -1
  42. package/dist/cli/check-theme.js +6 -0
  43. package/dist/cli/doctor.d.ts +10 -4
  44. package/dist/cli/doctor.d.ts.map +1 -1
  45. package/dist/cli/doctor.js +28 -1
  46. package/dist/cli/help.d.ts.map +1 -1
  47. package/dist/cli/help.js +2 -1
  48. package/dist/cli/index.d.ts +12 -0
  49. package/dist/cli/index.d.ts.map +1 -1
  50. package/dist/cli/index.js +29 -0
  51. package/dist/cli/init.d.ts.map +1 -1
  52. package/dist/cli/init.js +4 -15
  53. package/dist/cli/sync.d.ts.map +1 -1
  54. package/dist/cli/sync.js +8 -14
  55. package/dist/controls/actions.d.ts.map +1 -1
  56. package/dist/controls/actions.js +12 -2
  57. package/dist/dashboard/section.module.css +15 -7
  58. package/dist/dashboard/stat-card.d.ts.map +1 -1
  59. package/dist/dashboard/stat-card.js +7 -2
  60. package/dist/dashboard/stat-group.d.ts +8 -2
  61. package/dist/dashboard/stat-group.d.ts.map +1 -1
  62. package/dist/dashboard/stat-group.js +23 -15
  63. package/dist/dashboard/stat-group.module.css +85 -9
  64. package/dist/dashboard/stat-group.module.css.d.ts +1 -0
  65. package/dist/dashboard/widget-grid.d.ts.map +1 -1
  66. package/dist/dashboard/widget-grid.js +4 -5
  67. package/dist/dashboard/widget-grid.module.css +64 -21
  68. package/dist/dashboard/widget-grid.module.css.d.ts +1 -0
  69. package/dist/guard/index.d.ts +19 -2
  70. package/dist/guard/index.d.ts.map +1 -1
  71. package/dist/guard/index.js +27 -4
  72. package/dist/index.d.ts +9 -0
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +2 -0
  75. package/dist/shell/app-brand.module.css +12 -3
  76. package/dist/shell/app-header.module.css +49 -9
  77. package/dist/shell/app-main.module.css +24 -4
  78. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  79. package/dist/shell/app-mobile-nav.js +98 -51
  80. package/dist/shell/app-mobile-nav.module.css +99 -11
  81. package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
  82. package/dist/shell/app-sidebar.module.css +55 -2
  83. package/dist/shell/index.d.ts.map +1 -1
  84. package/dist/shell/index.js +3 -3
  85. package/dist/shell/mobile-nav-model.d.ts +26 -7
  86. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  87. package/dist/shell/mobile-nav-model.js +12 -3
  88. package/dist/shell/page-aside.module.css +8 -6
  89. package/dist/shell/page-bar.d.ts +13 -0
  90. package/dist/shell/page-bar.d.ts.map +1 -1
  91. package/dist/shell/page-bar.js +13 -3
  92. package/dist/shell/page-bar.module.css +20 -12
  93. package/dist/shell/page-title.module.css +3 -1
  94. package/dist/theme/use-basalt-spacing.d.ts.map +1 -1
  95. package/dist/tokens/index.d.ts.map +1 -1
  96. package/dist/tokens/index.js +51 -7
  97. package/dist/tokens/palette.d.ts +98 -17
  98. package/dist/tokens/palette.d.ts.map +1 -1
  99. package/dist/tokens/palette.js +110 -18
  100. package/dist/tokens.css +17 -5
  101. package/llms.txt +2 -2
  102. package/package.json +1 -1
  103. package/src/charts/index.ts +1 -0
  104. package/src/charts/kinds/BandStrip.tsx +5 -3
  105. package/src/charts/kinds/Bars.tsx +4 -2
  106. package/src/charts/kinds/Donut.tsx +4 -2
  107. package/src/charts/kinds/DualPanel.tsx +4 -2
  108. package/src/charts/kinds/Heatmap.tsx +5 -2
  109. package/src/charts/kinds/MirroredBars.tsx +4 -2
  110. package/src/charts/kinds/MultiLine.tsx +4 -2
  111. package/src/charts/kinds/StackedArea.tsx +4 -2
  112. package/src/charts/kinds/ZonedLine.tsx +4 -2
  113. package/src/charts/primitives/CartesianChart.tsx +4 -1
  114. package/src/charts/primitives/ChartCard.tsx +12 -7
  115. package/src/charts/primitives/ChartFrame.tsx +51 -37
  116. package/src/charts/primitives/chart-frame-layout.ts +172 -13
  117. package/src/cli/check-theme.ts +13 -0
  118. package/src/cli/doctor.ts +49 -8
  119. package/src/cli/help.ts +5 -3
  120. package/src/cli/index.ts +49 -0
  121. package/src/cli/init.ts +6 -27
  122. package/src/cli/sync.ts +11 -22
  123. package/src/controls/actions.tsx +51 -0
  124. package/src/dashboard/section.module.css +15 -7
  125. package/src/dashboard/stat-card.tsx +7 -2
  126. package/src/dashboard/stat-group.module.css +85 -9
  127. package/src/dashboard/stat-group.module.css.d.ts +1 -0
  128. package/src/dashboard/stat-group.tsx +40 -20
  129. package/src/dashboard/widget-grid.module.css +64 -21
  130. package/src/dashboard/widget-grid.module.css.d.ts +1 -0
  131. package/src/dashboard/widget-grid.tsx +30 -12
  132. package/src/guard/index.ts +79 -11
  133. package/src/index.ts +9 -0
  134. package/src/shell/app-brand.module.css +12 -3
  135. package/src/shell/app-header.module.css +49 -9
  136. package/src/shell/app-main.module.css +24 -4
  137. package/src/shell/app-mobile-nav.module.css +99 -11
  138. package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
  139. package/src/shell/app-mobile-nav.tsx +225 -83
  140. package/src/shell/app-sidebar.module.css +55 -2
  141. package/src/shell/index.tsx +26 -6
  142. package/src/shell/mobile-nav-model.ts +37 -9
  143. package/src/shell/page-aside.module.css +8 -6
  144. package/src/shell/page-bar.module.css +20 -12
  145. package/src/shell/page-bar.tsx +39 -5
  146. package/src/shell/page-title.module.css +3 -1
  147. package/src/theme/use-basalt-spacing.ts +8 -6
  148. package/src/tokens/index.ts +90 -6
  149. package/src/tokens/palette.ts +111 -18
@@ -4,6 +4,7 @@ import type { BasaltProps } from '../../common/props.js';
4
4
  import type { ChartMargin } from '../../tokens/index.js';
5
5
  import type { CursorResolution } from '../cursor/resolve.js';
6
6
  import type { ChartLegendConfig, ChartSeries } from '../series.js';
7
+ import type { ResponsiveChartHeight } from './ChartFrame.js';
7
8
  import type { ChartState } from './ChartPending.js';
8
9
  import type { XZoneSpec } from './XZoneRects.js';
9
10
  import type { ZoneSpec } from './ZoneRects.js';
@@ -186,7 +187,9 @@ export type CartesianChartProps<T> = BasaltProps & {
186
187
  dashed?: boolean;
187
188
  axis?: 'left' | 'right';
188
189
  }[];
189
- height?: number;
190
+ /** Height in px, or one per size step (`{ base, sm?, md?, lg? }`). Default 240. Forwarded
191
+ * unchanged; `ChartFrameProps.height` owns the law — steps resolve off the MEASURED width. */
192
+ height?: number | ResponsiveChartHeight;
190
193
  aspectRatio?: number;
191
194
  fill?: boolean;
192
195
  legend?: ChartLegendConfig | false;
@@ -1 +1 @@
1
- {"version":3,"file":"CartesianChart.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/CartesianChart.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CartesianChart.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/CartesianChart.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChartCard.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAGtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAEhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAqChD;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GACtC,eAAe,CAAC,aAAa,CAAC,GAAG;IAC/B,mGAAmG;IACnG,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;8FAC0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;kEAC8D;IAC9D,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+FAA+F;IAC/F,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,8FAA8F;IAC9F,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;sDACkD;IAClD,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;oGACgG;IAChG,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAEH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,KAAK,EACL,WAAW,EACX,aAAa,EACb,KAAK,EACL,OAAO,EACP,KAAK,EACL,iBAAuB,EACvB,WAAW,EACX,SAAS,EACT,UAAU,EACV,KAAK,EACL,QAAQ,GACT,EAAE,cAAc,+BAgEhB"}
1
+ {"version":3,"file":"ChartCard.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAGtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAEhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AA0ChD;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GACtC,eAAe,CAAC,aAAa,CAAC,GAAG;IAC/B,mGAAmG;IACnG,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;8FAC0F;IAC1F,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;kEAC8D;IAC9D,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+FAA+F;IAC/F,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,8FAA8F;IAC9F,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;sDACkD;IAClD,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;oGACgG;IAChG,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAEH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,KAAK,EACL,WAAW,EACX,aAAa,EACb,KAAK,EACL,OAAO,EACP,KAAK,EACL,iBAAuB,EACvB,WAAW,EACX,SAAS,EACT,UAAU,EACV,KAAK,EACL,QAAQ,GACT,EAAE,cAAc,+BAgEhB"}
@@ -17,10 +17,10 @@ const bodyClipStyle = {
17
17
  borderBottomLeftRadius: VX.radiusCard,
18
18
  borderBottomRightRadius: VX.radiusCard,
19
19
  overflow: "hidden",
20
- padding: "2px var(--mantine-spacing-sm, 0.8125rem) var(--mantine-spacing-xs, 0.6875rem)"
20
+ padding: "2px var(--vx-space-card-inset-x, 0.8125rem) var(--vx-space-card-inset-y, 0.6875rem)"
21
21
  };
22
22
  const headerWrapStyle = {
23
- padding: "var(--mantine-spacing-xs, 0.6875rem) var(--mantine-spacing-sm, 0.8125rem) 4px"
23
+ padding: "var(--vx-space-card-inset-y, 0.6875rem) var(--vx-space-card-inset-x, 0.8125rem) 4px"
24
24
  };
25
25
  function ChartCard({
26
26
  title,
@@ -1,16 +1,29 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { BasaltProps } from '../../common/props.js';
3
3
  import type { ChartLegendConfig, LegendPlacement, SeriesStyle } from '../series.js';
4
+ import type { ResponsiveChartHeight } from './chart-frame-layout.js';
4
5
  import type { ChartState } from './ChartPending.js';
5
6
  /** Re-exported for `ChartFrame.test.tsx` and any other consumer that previously reached these
6
7
  * through `ChartFrame` — the layout math itself lives in `./chart-frame-layout` (pure, DOM-free). */
7
- export { legendEntryCap, resolvePlotRect, resolveChartTier } from './chart-frame-layout.js';
8
+ export { legendEntryCap, resolveLegendRollup, resolvePlotRect, resolveChartTier, } from './chart-frame-layout.js';
9
+ export type { ResponsiveChartHeight } from './chart-frame-layout.js';
8
10
  /** Legend configuration for {@link ChartFrame}. Omit entirely (or pass `{}`) for the default
9
11
  * bottom-placed legend; pass `false` only for the sparkline exemption. */
10
12
  export type ChartFrameLegend = {
11
13
  /** Default 'bottom'. */
12
14
  placement?: LegendPlacement;
13
- /** Wrap cap → "+N more" rollup at high cardinality. */
15
+ /**
16
+ * Wrap cap → "+N more" rollup at high cardinality.
17
+ *
18
+ * An explicit value WINS OUTRIGHT — over the phone tier's two-row default (`chartTierMetrics`)
19
+ * AND over a `fill` frame's measured fit (`legendEntryCap`). 1.30.0 fixed only the first half:
20
+ * the fit still ran `Math.min(fitted, caller)` on top, so meteo's 7-entry meteogram legend in a
21
+ * 150px docked row rendered 2 entries at `maxRows` 3, 6 AND 99 — five series drawn in colours
22
+ * nothing named. Both losers are DEFAULTS (the tier's, and `VX.minPlotHeight`); this is the one
23
+ * number a caller stated, so under `fill` the PLOT yields the height instead
24
+ * ({@link resolvePlotRect}'s `legendWins`). State a number too large for the box and the plot
25
+ * gets what is left — visible, and yours.
26
+ */
14
27
  maxRows?: number;
15
28
  /** Visually separate role: series | overlay | reference. */
16
29
  groups?: boolean;
@@ -28,8 +41,16 @@ export type ChartFrameLegend = {
28
41
  export type ChartFrameProps = BasaltProps & {
29
42
  /** Series identity — drives the derived legend. Pass the SAME array the kind draws + tooltips from. */
30
43
  series: readonly SeriesStyle[];
31
- /** Fixed height in pixels. Used when neither `aspectRatio` nor `fill` is set. Default 240. */
32
- height?: number;
44
+ /**
45
+ * Height in pixels, or one per size step. Used when neither `aspectRatio` nor `fill` is set.
46
+ * Default 240.
47
+ *
48
+ * `{ base: 180, md: 260 }` is the declarative escape from a page of fixed-height charts: the
49
+ * steps are compared against the frame's own MEASURED width, not the viewport, so a chart squeezed
50
+ * by a `PageAside` gets the short height on a desktop too. A plain number is unchanged and stays
51
+ * supported ({@link resolveFrameHeight}).
52
+ */
53
+ height?: number | ResponsiveChartHeight;
33
54
  /** height = Math.round(containerWidth / aspectRatio). Ignored when `fill` is set. */
34
55
  aspectRatio?: number;
35
56
  /** Fill the parent flex/grid cell's measured height instead of a fixed/derived one. */
@@ -1 +1 @@
1
- {"version":3,"file":"ChartFrame.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAIrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAUhF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD;qGACqG;AACrG,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAKxF;0EAC0E;AAC1E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wBAAwB;IACxB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC1C;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG;IAC1C,uGAAuG;IACvG,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;IAC9B,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAA;IACjC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;oDACgD;IAChD,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,SAAS,CAAA;CACxC,CAAA;AAED,0FAA0F;AAC1F,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAC5B,CAAA;AAcD;oFACoF;AACpF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC1C,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,iBAAiB,GAAG,KAAK,GAAG,SAAS,EAC7C,KAAK,CAAC,EAAE,qBAAqB,EAC7B,WAAW,CAAC,EAAE,MAAM,GACnB,gBAAgB,GAAG,KAAK,CAU1B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,MAAM,EACN,WAAW,EACX,IAAY,EACZ,QAA4B,EAC5B,OAAO,EACP,MAAW,EACX,SAAS,EACT,SAAiB,EACjB,KAAK,EACL,SAAS,EACT,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,GAAG,SAAS,CAmH7B"}
1
+ {"version":3,"file":"ChartFrame.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartFrame.tsx"],"names":[],"mappings":"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"}
@@ -4,15 +4,20 @@ import { VX } from "../../tokens/index.js";
4
4
  import { useChartSize } from "../hooks/useChartSize.js";
5
5
  import { deriveLegend } from "../series.js";
6
6
  import {
7
- chartTierMetrics,
8
- legendEntryCap,
9
7
  resolveChartTier,
8
+ resolveFrameHeight,
9
+ resolveLegendRollup,
10
10
  resolvePlotRect
11
11
  } from "./chart-frame-layout.js";
12
12
  import { ChartTierProvider } from "./chart-tier.js";
13
13
  import { ChartLegend } from "./ChartLegend.js";
14
14
  import { ChartEmpty, ChartError, ChartPending, resolveChartState } from "./ChartPending.js";
15
- import { legendEntryCap as legendEntryCap2, resolvePlotRect as resolvePlotRect2, resolveChartTier as resolveChartTier2 } from "./chart-frame-layout.js";
15
+ import {
16
+ legendEntryCap,
17
+ resolveLegendRollup as resolveLegendRollup2,
18
+ resolvePlotRect as resolvePlotRect2,
19
+ resolveChartTier as resolveChartTier2
20
+ } from "./chart-frame-layout.js";
16
21
  const DEFAULT_HEIGHT = 240;
17
22
  const DEFAULT_MIN_WIDTH = 200;
18
23
  const outerStyle = (fill, vertical) => ({
@@ -67,26 +72,27 @@ function ChartFrame({
67
72
  const resolvedState = resolveChartState({ ...state !== void 0 && { state }, isPending });
68
73
  const legendVisible = legend !== false && resolvedState === null;
69
74
  const tier = resolveChartTier(containerW);
70
- const resolvedHeight = fill ? containerH : aspectRatio !== void 0 ? Math.round(containerW / aspectRatio) : height ?? DEFAULT_HEIGHT;
75
+ const resolvedHeight = fill ? containerH : aspectRatio !== void 0 ? Math.round(containerW / aspectRatio) : resolveFrameHeight(height ?? DEFAULT_HEIGHT, containerW);
71
76
  const sideLegendWidth = legendVisible && vertical ? legendW : 0;
72
77
  const topBottomLegendHeight = legendVisible && !vertical ? legendH : 0;
78
+ const legendItems = legend === false ? [] : deriveLegend(series);
79
+ const togglable = legend !== false && (legend.toggle ?? legendItems.length > 1);
80
+ const { maxRows, legendWins } = resolveLegendRollup({
81
+ ...legend !== false && legend.maxRows !== void 0 && { statedMaxRows: legend.maxRows },
82
+ tier,
83
+ fillBand: legendVisible && fill && !vertical,
84
+ items: legendItems,
85
+ containerW,
86
+ available: resolvedHeight - VX.minPlotHeight
87
+ });
73
88
  const plot = resolvePlotRect({
74
89
  containerW,
75
90
  resolvedHeight,
76
91
  minWidth,
77
92
  sideLegendWidth,
78
- topBottomLegendHeight
93
+ topBottomLegendHeight,
94
+ ...legendWins && { legendWins }
79
95
  });
80
- const legendItems = legend === false ? [] : deriveLegend(series);
81
- const togglable = legend !== false && (legend.toggle ?? legendItems.length > 1);
82
- const tierMaxRows = chartTierMetrics(tier).legendMaxRows;
83
- const callerMaxRows = legend === false ? void 0 : legend.maxRows === void 0 ? tierMaxRows : tierMaxRows === void 0 ? legend.maxRows : Math.min(legend.maxRows, tierMaxRows);
84
- const maxRows = legend === false ? void 0 : fill && !vertical ? legendEntryCap({
85
- items: legendItems,
86
- containerW,
87
- available: resolvedHeight - VX.minPlotHeight,
88
- ...callerMaxRows !== void 0 && { callerMaxRows }
89
- }) : callerMaxRows;
90
96
  const legendNode = legend === false || resolvedState !== null ? null : /* @__PURE__ */ jsx("div", { ref: legendRef, style: legendWrapperStyle(vertical), children: /* @__PURE__ */ jsx(
91
97
  ChartLegend,
92
98
  {
@@ -126,8 +132,9 @@ function ChartFrame({
126
132
  }
127
133
  export {
128
134
  ChartFrame,
129
- legendEntryCap2 as legendEntryCap,
135
+ legendEntryCap,
130
136
  resolveChartTier2 as resolveChartTier,
131
137
  resolveLegend,
138
+ resolveLegendRollup2 as resolveLegendRollup,
132
139
  resolvePlotRect2 as resolvePlotRect
133
140
  };
@@ -24,6 +24,42 @@ export type ChartTier = 'phone' | 'desktop';
24
24
  * later, and `resolvePlotRect` already treats an unmeasured width as the first-frame case.
25
25
  */
26
26
  export declare function resolveChartTier(containerW: number): ChartTier;
27
+ /**
28
+ * A chart height stated per size step instead of as one number — the declarative escape from "every
29
+ * chart on this page is 260px tall on a 375px phone too".
30
+ *
31
+ * `base` is required and is the height a box narrower than every named step gets; each further key
32
+ * is the height from that step UP. Keys are Mantine's breakpoint names so a consumer writes the
33
+ * vocabulary they already write, and nothing more than `sm`/`md`/`lg` is offered: `xs`/`xl` would
34
+ * be two more rungs nobody has asked a chart to have.
35
+ *
36
+ * ```tsx
37
+ * <ChartFrame series={series} height={{ base: 180, md: 260 }}>{…}</ChartFrame>
38
+ * ```
39
+ */
40
+ export type ResponsiveChartHeight = {
41
+ /** Below the narrowest STATED step — and NOT the first-frame value; see {@link resolveFrameHeight}. */
42
+ base: number;
43
+ /** From 768px of MEASURED container width up. */
44
+ sm?: number;
45
+ /** From 992px up. */
46
+ md?: number;
47
+ /** From 1200px up. */
48
+ lg?: number;
49
+ };
50
+ /**
51
+ * Resolve a `height` prop — a plain number, or a {@link ResponsiveChartHeight} — against the
52
+ * measured container width.
53
+ *
54
+ * A plain number passes through untouched: this is a WIDENING of the prop, not a replacement, and
55
+ * every existing `height={240}` must keep meaning exactly 240.
56
+ *
57
+ * An UNMEASURED box (`containerW <= 0` — SSR, or before the `ResizeObserver`'s first callback)
58
+ * resolves to the LARGEST stated step, matching {@link resolveChartTier}'s first-frame rule: the
59
+ * first paint must not be phone-shaped chrome that grows one frame later. Falling to `base` there
60
+ * would make every SSR'd chart short and then jump.
61
+ */
62
+ export declare function resolveFrameHeight(height: number | ResponsiveChartHeight, containerW: number): number;
27
63
  /** Every size a chart's chrome resolves per tier. One object so a new tier-sensitive size is added
28
64
  * in one place and read by name, rather than each primitive branching on the tier itself. */
29
65
  export type ChartTierMetrics = {
@@ -39,13 +75,67 @@ export type ChartTierMetrics = {
39
75
  /** Floating tooltip `minWidth`, px. */
40
76
  tooltipMinWidth: number;
41
77
  /** Default entry cap on the legend, or `undefined` for no default cap. An explicit
42
- * `legend.maxRows` still wins as the upper bound. */
78
+ * `legend.maxRows` replaces it outright ({@link resolveLegendMaxRows}). */
43
79
  legendMaxRows: number | undefined;
44
80
  /** Per-side margin FLOORS (`VX.margin` is a floor, never a ceiling — §1). */
45
81
  margin: ChartMargin;
46
82
  };
47
83
  /** The resolved sizes for one tier. Frozen module constants — never a new object per render. */
48
84
  export declare function chartTierMetrics(tier: ChartTier): ChartTierMetrics;
85
+ /**
86
+ * Which legend cap applies: the caller's, or the tier's own default.
87
+ *
88
+ * **An explicit `callerMaxRows` wins OUTRIGHT.** This used to be `Math.min(caller, tier)`, which
89
+ * made the documented per-chart opt-out ("opt back out with an explicit `legend.maxRows`") false in
90
+ * the one direction anybody needs it. The tier's `PHONE_LEGEND_MAX_ROWS` is a DEFAULT for charts
91
+ * that said nothing — a chart that names a number has already answered the question the default
92
+ * exists to answer, and it matters because the tier keys on the measured box: a 380px inspector
93
+ * panel on a 1440px desktop is "a phone", so a six-series legend rolled up to two rows + `+4 more`
94
+ * with no way to say no. `margin` and `xLabelRotate` always had that escape; the legend did not.
95
+ *
96
+ * The measured {@link legendEntryCap} a `fill` frame applies is NOT a second ceiling on top of
97
+ * this: it resolves an ABSENT cap only. 1.30.0 ran it over the caller's number as well
98
+ * (`Math.min(fitted, caller)`), which made "wins outright" false again in the one place it was
99
+ * needed — meteo measured a 7-entry legend pinned to 2 entries at `maxRows` 3, 6 AND 99, with 5
100
+ * series drawn in colours the legend refused to name. The fit is not physics either: it derives
101
+ * from `VX.minPlotHeight`, which is a framework DEFAULT about the plot. Two defaults do not
102
+ * outvote the one number a caller stated, so the PLOT yields the height instead
103
+ * ({@link resolvePlotRect}'s `legendWins`) — visibly, in the frame the caller sized.
104
+ */
105
+ export declare function resolveLegendMaxRows(input: {
106
+ callerMaxRows?: number | undefined;
107
+ tier: ChartTier;
108
+ }): number | undefined;
109
+ /**
110
+ * The whole legend-cap decision in one place: which cap `ChartLegend` gets, and whether the plot
111
+ * pays for it.
112
+ *
113
+ * It lives here rather than inline in `ChartFrame` because the COMPOSITION is what broke. 1.30.0
114
+ * had both halves right on their own — {@link resolveLegendMaxRows} honoured a stated cap,
115
+ * {@link legendEntryCap} measured a fit — and then ran the second over the first, which no test
116
+ * could see because neither pure function was wrong and the frame that combined them needs a
117
+ * `ResizeObserver` to test. Now the combination is a pure function too.
118
+ *
119
+ * Three cases, in order:
120
+ * 1. The caller STATED a number → it is the cap, and on a `fill` band the plot yields
121
+ * (`legendWins`). Nothing measured or defaulted trims it.
122
+ * 2. No `fill` band → the tier's default (or none), because the frame simply grows.
123
+ * 3. A `fill` band with nothing stated → the measured fit, bounded by the tier's default.
124
+ */
125
+ export declare function resolveLegendRollup(input: {
126
+ /** The caller's own `legend.maxRows`. */
127
+ statedMaxRows?: number | undefined;
128
+ tier: ChartTier;
129
+ /** A visible top/bottom legend on a `fill` frame — the one shape whose box cannot grow. */
130
+ fillBand: boolean;
131
+ items: readonly LegendEntry[];
132
+ containerW: number;
133
+ /** Frame height the legend may consume — `resolvedHeight - VX.minPlotHeight`. */
134
+ available: number;
135
+ }): {
136
+ maxRows: number | undefined;
137
+ legendWins: boolean;
138
+ };
49
139
  /**
50
140
  * Resolve the plot rect from the measured box and the measured legend band. Pure and exported so
51
141
  * the two floors below are testable without a `ResizeObserver`.
@@ -60,9 +150,11 @@ export declare function chartTierMetrics(tier: ChartTier): ChartTierMetrics;
60
150
  * `height={240}` toward zero and the body stopped rendering entirely. The plot stops at
61
151
  * `VX.minPlotHeight` and the frame's own box grows by the difference instead — it is a flex
62
152
  * column with `height: auto`, so that growth is automatic. Under `fill` the box CANNOT grow, so
63
- * the legend is capped instead ({@link legendEntryCap}). An unmeasured box (`resolvedHeight <=
64
- * 0`, i.e. a `fill` frame before its first measurement) stays at 0 and renders nothing, exactly
65
- * as before — the floor must never invent a height for a box nobody has measured.
153
+ * the legend is capped instead ({@link legendEntryCap}) — UNLESS the caller stated
154
+ * `legend.maxRows`, which is `legendWins`: there the floor itself yields, because a floor is a
155
+ * default and the caller's number is not. An unmeasured box (`resolvedHeight <= 0`, i.e. a
156
+ * `fill` frame before its first measurement) stays at 0 and renders nothing, exactly as before —
157
+ * the floor must never invent a height for a box nobody has measured.
66
158
  */
67
159
  export declare function resolvePlotRect(input: {
68
160
  containerW: number;
@@ -70,6 +162,15 @@ export declare function resolvePlotRect(input: {
70
162
  minWidth: number;
71
163
  sideLegendWidth: number;
72
164
  topBottomLegendHeight: number;
165
+ /**
166
+ * The caller stated `legend.maxRows` on a `fill` frame, so the legend takes the rows it asked
167
+ * for and the plot takes what is left — `VX.minPlotHeight` stops being a floor. Without this the
168
+ * honoured cap would push the frame's content PAST its own cell (the plot holds 120px, the legend
169
+ * band adds its own) and paint over whatever sits below it; with it, the cost of the caller's
170
+ * number lands where they can see it. Spend the whole box on legend rows and there is no plot
171
+ * left to draw — that is their arithmetic, made visible rather than silently rolled up.
172
+ */
173
+ legendWins?: boolean;
73
174
  }): {
74
175
  width: number;
75
176
  height: number;
@@ -83,13 +184,21 @@ export declare function resolvePlotRect(input: {
83
184
  * MEASURING the labels, not from assuming one entry per row — otherwise a five-entry legend that
84
185
  * fits on one line would roll up to `+2 more` in every 240px cell, moving rendering for charts
85
186
  * that never had the bug. Returns `undefined` when the whole legend already fits.
187
+ *
188
+ * This resolves an ABSENT cap. A caller who stated `legend.maxRows` never reaches here at all —
189
+ * `ChartFrame` routes around it — because `Math.min`-ing a stated number against this one made the
190
+ * documented escape inert in exactly the narrow `fill` panel it exists for
191
+ * ({@link resolveLegendMaxRows}).
86
192
  */
87
193
  export declare function legendEntryCap(input: {
88
194
  items: readonly LegendEntry[];
89
195
  containerW: number;
90
196
  /** Frame height the legend may consume — `resolvedHeight - VX.minPlotHeight`. */
91
197
  available: number;
92
- /** An explicit caller cap always wins as the upper bound. */
93
- callerMaxRows?: number;
198
+ /**
199
+ * The cap that applies when nothing explicit was stated — today only the tier's own default
200
+ * ({@link chartTierMetrics}). Two defaults contending: the smaller wins.
201
+ */
202
+ defaultMaxRows?: number;
94
203
  }): number | undefined;
95
204
  //# sourceMappingURL=chart-frame-layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chart-frame-layout.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/chart-frame-layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAUhD;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,CAAA;AAE3C;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAE9D;AAqBD;6FAC6F;AAC7F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2FAA2F;IAC3F,IAAI,EAAE,SAAS,CAAA;IACf;uFACmF;IACnF,QAAQ,EAAE,MAAM,CAAA;IAChB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAA;IACvB;yDACqD;IACrD,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,6EAA6E;IAC7E,MAAM,EAAE,WAAW,CAAA;CACpB,CAAA;AA6BD,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,gBAAgB,CAElE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,qBAAqB,EAAE,MAAM,CAAA;CAC9B,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAOpC;AAmCD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,GAAG,MAAM,GAAG,SAAS,CAerB"}
1
+ {"version":3,"file":"chart-frame-layout.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/chart-frame-layout.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,17 @@ const LEGEND_SWATCH_W = 24;
6
6
  function resolveChartTier(containerW) {
7
7
  return containerW > 0 && containerW < VX.phoneChartWidth ? "phone" : "desktop";
8
8
  }
9
+ const HEIGHT_STEP_WIDTH = { sm: 768, md: 992, lg: 1200 };
10
+ const HEIGHT_STEPS = ["lg", "md", "sm"];
11
+ function resolveFrameHeight(height, containerW) {
12
+ if (typeof height === "number") return height;
13
+ const widest = HEIGHT_STEPS.find((step2) => height[step2] !== void 0);
14
+ if (containerW <= 0) return widest === void 0 ? height.base : height[widest] ?? height.base;
15
+ const step = HEIGHT_STEPS.find(
16
+ (name) => containerW >= HEIGHT_STEP_WIDTH[name] && height[name] !== void 0
17
+ );
18
+ return step === void 0 ? height.base : height[step] ?? height.base;
19
+ }
9
20
  const PHONE_MARGIN_SCALE = 0.75;
10
21
  const PHONE_DOT_R = Math.max(VX.dotR - 1, 2);
11
22
  const PHONE_LEGEND_MAX_ROWS = 2;
@@ -38,11 +49,37 @@ const PHONE_METRICS = {
38
49
  function chartTierMetrics(tier) {
39
50
  return tier === "phone" ? PHONE_METRICS : DESKTOP_METRICS;
40
51
  }
52
+ function resolveLegendMaxRows(input) {
53
+ return input.callerMaxRows ?? chartTierMetrics(input.tier).legendMaxRows;
54
+ }
55
+ function resolveLegendRollup(input) {
56
+ const { statedMaxRows, tier, fillBand, items, containerW, available } = input;
57
+ if (statedMaxRows !== void 0) return { maxRows: statedMaxRows, legendWins: fillBand };
58
+ const tierMaxRows = resolveLegendMaxRows({ callerMaxRows: void 0, tier });
59
+ if (!fillBand) return { maxRows: tierMaxRows, legendWins: false };
60
+ return {
61
+ maxRows: legendEntryCap({
62
+ items,
63
+ containerW,
64
+ available,
65
+ ...tierMaxRows !== void 0 && { defaultMaxRows: tierMaxRows }
66
+ }),
67
+ legendWins: false
68
+ };
69
+ }
41
70
  function resolvePlotRect(input) {
42
- const { containerW, resolvedHeight, minWidth, sideLegendWidth, topBottomLegendHeight } = input;
71
+ const {
72
+ containerW,
73
+ resolvedHeight,
74
+ minWidth,
75
+ sideLegendWidth,
76
+ topBottomLegendHeight,
77
+ legendWins = false
78
+ } = input;
79
+ const plotFloor = legendWins ? 0 : VX.minPlotHeight;
43
80
  return {
44
81
  width: containerW === 0 ? minWidth : Math.max(containerW - sideLegendWidth, 1),
45
- height: resolvedHeight <= 0 ? 0 : Math.max(resolvedHeight - topBottomLegendHeight, VX.minPlotHeight)
82
+ height: resolvedHeight <= 0 ? 0 : Math.max(resolvedHeight - topBottomLegendHeight, plotFloor)
46
83
  };
47
84
  }
48
85
  const legendBandHeight = (rows) => LEGEND_PAD_Y + rows * LEGEND_LINE_H + Math.max(rows - 1, 0) * VX.legendGap;
@@ -69,21 +106,24 @@ function entriesWithinRows(items, width, rows) {
69
106
  return fitted;
70
107
  }
71
108
  function legendEntryCap(input) {
72
- const { items, containerW, available, callerMaxRows } = input;
73
- if (containerW <= 0 || items.length === 0) return callerMaxRows;
109
+ const { items, containerW, available, defaultMaxRows } = input;
110
+ if (containerW <= 0 || items.length === 0) return defaultMaxRows;
74
111
  const rows = Math.max(
75
112
  1,
76
113
  Math.floor((available - LEGEND_PAD_Y + VX.legendGap) / (LEGEND_LINE_H + VX.legendGap))
77
114
  );
78
115
  if (legendBandHeight(rows) <= available && entriesWithinRows(items, containerW, rows) >= items.length) {
79
- return callerMaxRows;
116
+ return defaultMaxRows;
80
117
  }
81
118
  const fitted = Math.max(1, entriesWithinRows(items, containerW, rows));
82
- return callerMaxRows === void 0 ? fitted : Math.min(fitted, callerMaxRows);
119
+ return defaultMaxRows === void 0 ? fitted : Math.min(fitted, defaultMaxRows);
83
120
  }
84
121
  export {
85
122
  chartTierMetrics,
86
123
  legendEntryCap,
87
124
  resolveChartTier,
125
+ resolveFrameHeight,
126
+ resolveLegendMaxRows,
127
+ resolveLegendRollup,
88
128
  resolvePlotRect
89
129
  };
@@ -1 +1 @@
1
- {"version":3,"file":"check-theme.d.ts","sourceRoot":"","sources":["../../src/cli/check-theme.ts"],"names":[],"mappings":"AA8BA;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,aAAa,GAAE,MAAsB,EACrC,KAAK,GAAE,SAAS,MAAM,EAAO,GAC5B,MAAM,CA2JR"}
1
+ {"version":3,"file":"check-theme.d.ts","sourceRoot":"","sources":["../../src/cli/check-theme.ts"],"names":[],"mappings":"AA+BA;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,aAAa,GAAE,MAAsB,EACrC,KAAK,GAAE,SAAS,MAAM,EAAO,GAC5B,MAAM,CAuKR"}
@@ -12,6 +12,7 @@ import {
12
12
  conflictingProfileFlags,
13
13
  declaredProfile,
14
14
  DEFAULT_ROOTS,
15
+ hasBasaltKey,
15
16
  readBasaltConfig,
16
17
  resolveExemptRules,
17
18
  resolveProjectDir,
@@ -69,6 +70,11 @@ function checkTheme(invocationCwd = process.cwd(), flags = []) {
69
70
  findings.push(...checkSource(readFileSync(resolve(cwd, rel), "utf8"), rel, guardCfg));
70
71
  }
71
72
  if (scanned.length === 0) {
73
+ if (!hasBasaltKey(cwd)) {
74
+ console.error(
75
+ `\u2716 basalt-ui check-theme: ${cwd}/package.json has no "basalt" key \u2014 very likely not the package you meant to scan (BASALT_CWD, else the invocation cwd; nothing is inferred). At a repo root? Give the command \`root: <pkg>/\` (lefthook) or \`working-directory: <pkg>\` (Actions), or set BASALT_CWD=<pkg>. If this directory IS it, run \`basalt-ui init\`.`
76
+ );
77
+ }
72
78
  if (cfg.roots === void 0) {
73
79
  console.error(
74
80
  `\u2716 basalt-ui check-theme: 0 files scanned \u2014 no "basalt.roots" configured in package.json, and the built-in default roots (${DEFAULT_ROOTS.join(", ")}) matched zero files. Set "basalt": { "roots": [...] } in package.json to point at your source directories.`
@@ -15,16 +15,22 @@ export type DoctorResult = {
15
15
  * Check a consumer repo's basalt integration and print a pass/warn report.
16
16
  *
17
17
  * Narrowed to what a single-directory-scoped run can actually verify — no workspace-glob walking,
18
- * no ascend/descend project discovery (see {@link resolveProjectDir}). Four checks, numbered in
18
+ * no ascend/descend project discovery (see {@link resolveProjectDir}). Six checks, numbered in
19
19
  * the order they run and print:
20
20
  *
21
21
  * Hard failures (exit non-zero):
22
22
  * 1. `.basalt/manifest.json` exists (`basalt-ui init` was run).
23
- * 2. `oxlint-preset` ("rules in sync"): the consumer's `.oxlintrc.json` extends the shipped
23
+ * 2. `install-parity`: basalt-ui resolves, doctor prints WHERE and at what version, and that
24
+ * version agrees with the manifest's `basaltVersion`.
25
+ * 3. `guard-scan`: `check-theme` would scan more than zero files, sharing `scannableFiles` so the
26
+ * two cannot disagree. Both RESTORED in 1.30.0 after four minors gone with no MIGRATING row —
27
+ * README § `doctor` carries what their absence cost (a no-op CI gate, a green guard scanning
28
+ * nothing, and two basalt-WRITTEN docs describing output the CLI no longer produced).
29
+ * 4. `oxlint-preset` ("rules in sync"): the consumer's `.oxlintrc.json` extends the shipped
24
30
  * preset AND that path resolves. `init` keeps an existing config, so the framework's whole
25
31
  * lint half can be silently off — one repo carried six real violations invisibly across five
26
32
  * minors this way.
27
- * 4. `ai-major-parity`: within THIS package.json, `dependencies`/`devDependencies`/
33
+ * 6. `ai-major-parity`: within THIS package.json, `dependencies`/`devDependencies`/
28
34
  * `peerDependencies` agree on the `ai` package's major. `basalt/ai-sdk-major` (the oxlint
29
35
  * plugin rule) already catches this per linted FILE against its nearest package.json; this
30
36
  * check exists for the shape a lint run cannot see — a package whose own manifest asks its
@@ -34,7 +40,7 @@ export type DoctorResult = {
34
40
  * already agree warns that the exemption is stale.
35
41
  *
36
42
  * Warning (non-fatal):
37
- * 3. `lefthook-preset` ("lefthook wired"): a pre-commit command runs `check-theme`, read from
43
+ * 5. `lefthook-preset` ("lefthook wired"): a pre-commit command runs `check-theme`, read from
38
44
  * `lefthook dump --format json` (resolves `extends`/`include`/`root:` the way lefthook itself
39
45
  * does — see {@link inspectLefthookGate}). No lefthook config at all is a pass (a consumer may
40
46
  * gate elsewhere); a config that resolves but wires no guard command warns; a config lefthook
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AA0BA,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AA8DD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,MAAM,CAAC,aAAa,GAAE,MAAsB,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,CAsM1F"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AA6BA,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AA8DD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,MAAM,CAAC,aAAa,GAAE,MAAsB,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,CAsO1F"}
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
- import { resolve } from "node:path";
2
+ import { relative, resolve } from "node:path";
3
3
  import {
4
4
  MANIFEST_PATH,
5
5
  basaltBinCommand,
@@ -12,7 +12,10 @@ import {
12
12
  parseJsonc,
13
13
  readBasaltConfig,
14
14
  readIfExists,
15
+ readManifest,
15
16
  resolveProjectDir,
17
+ resolveRoots,
18
+ scannableFiles,
16
19
  shippedAssetPath
17
20
  } from "./index.js";
18
21
  import { findManifestAbove, parentInstallAdvice } from "./sync.js";
@@ -93,6 +96,30 @@ basalt-ui doctor \u2014 ${cwd}
93
96
  fail(`${MANIFEST_PATH} missing \u2014 run \`basalt-ui init\` to scaffold the consumer repo`);
94
97
  }
95
98
  const install = findBasaltInstall(cwd);
99
+ const manifestVersion = profile === "tokens-only" ? void 0 : readManifest(cwd).basaltVersion;
100
+ const at = install.dir === null ? "" : `${relative(cwd, install.dir)} (${install.version})`;
101
+ if (install.dir === null) {
102
+ fail(
103
+ "install-parity: basalt-ui does not resolve here, above, or in a sibling workspace package."
104
+ );
105
+ } else if (manifestVersion === void 0 || install.version === manifestVersion) {
106
+ const matches = manifestVersion === void 0 ? "" : " and matches the manifest's basaltVersion";
107
+ pass(`basalt-ui resolves at ${at}${matches}`);
108
+ } else {
109
+ fail(
110
+ `install-parity: basalt-ui resolves at ${at}, but ${MANIFEST_PATH} was last written by ${manifestVersion} \u2014 the managed layer is from a different release than the code. Run \`basalt-ui sync\` (or \`bun install\`, if the lockfile is what is stale).`
111
+ );
112
+ }
113
+ const scanned = profile === "tokens-only" ? null : scannableFiles(cwd, cfg).length;
114
+ if (scanned === null) {
115
+ pass("guard-scan: n/a \u2014 a tokens-only consumer runs the token lane, not the file walk");
116
+ } else if (scanned === 0) {
117
+ fail(
118
+ "guard-scan: `check-theme` would scan ZERO files here \u2014 either `basalt.roots` points at paths that do not exist, or this is not the package the config lives in (BASALT_CWD, else the cwd; nothing is inferred)."
119
+ );
120
+ } else {
121
+ pass(`guard-scan: check-theme covers ${scanned} file(s) under ${resolveRoots(cfg).join(", ")}`);
122
+ }
96
123
  if (profile === "tokens-only") {
97
124
  pass("oxlint-preset: n/a \u2014 the shipped preset is a Mantine/React preset");
98
125
  } else {
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,CAAC,CAiC1F,CAAA;AAED;yCACyC;AACzC,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAWhF;AAED,0GAA0G;AAC1G,eAAO,MAAM,KAAK,QAmC0E,CAAA"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,CAAC,CAkC1F,CAAA;AAED;yCACyC;AACzC,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAWhF;AAED,0GAA0G;AAC1G,eAAO,MAAM,KAAK,QAoC0E,CAAA"}
package/dist/cli/help.js CHANGED
@@ -8,6 +8,7 @@ const COMMAND_OPTIONS = {
8
8
  sync: {
9
9
  force: { type: "boolean" },
10
10
  check: { type: "boolean" },
11
+ verbose: { type: "boolean" },
11
12
  "tokens-only": { type: "boolean" },
12
13
  framework: { type: "boolean" }
13
14
  },
@@ -45,7 +46,7 @@ function unknownFlag(cmd, flags) {
45
46
  return match?.[1] ?? flags.find((f) => f.startsWith("-")) ?? flags[0] ?? null;
46
47
  }
47
48
  }
48
- const USAGE = "Usage: basalt-ui <--version | init [--with-router] [--with-query] [--merge-lint] |\n sync [--force] [--check] [--tokens-only|--framework] |\n check-theme [--audit-allows] |\n doctor [--tokens-only|--framework] | guard-hook | tokens:css | fonts:css | help>\n\ncheck-theme [--tokens-only|--framework] [--audit-allows]\n --audit-allows reports instead of scanning: every `theme-allow` annotation and every\n `basalt.exemptRules` entry, with what each one still suppresses \u2014 proved by re-running the\n guard with that one waiver neutralized, not by reading its text. Exits 1 on a waiver that\n suppresses nothing (dead), which is what makes it usable as a CI gate.\n\ntokens:css [--out <path>] [--check] [--selector-attribute <attr> | --selector-class <class>]\n [--light-class <class>] [--dark-value <v>] [--light-value <v>]\n [--default-scheme <dark|light|none>] [--media-fallback] [--only <core|all>]\n [--no-legacy-aliases]\n Emit the --vx-* stylesheet (stdout unless --out). Defaults reproduce basalt-ui/tokens.css.\n --selector-class emits `:root.dark` instead of an attribute selector (the Tailwind convention).\n --check writes nothing and exits 1 when --out differs from what would be emitted (a CI gate).\n --no-legacy-aliases drops the deprecated camelCase spellings (--vx-accentFill and friends),\n which are emitted by default as aliases of the canonical kebab-case names.\n\nfonts:css [--out <path>] [--check]\n Emit the shipped --basalt-font-* stacks as plain CSS \u2014 the typeface half of the token layer,\n otherwise reachable only by importing styles.css.\n\ndoctor [--tokens-only|--framework]\n Checks THIS project only: the manifest exists, the oxlint preset is wired, the pre-commit\n hook runs check-theme (via `lefthook dump`), and the ai package major is consistent within\n this package.json. Profile is DECLARED, same as check-theme \u2014 never inferred.\n\nsync [--force] [--check] [--tokens-only|--framework]\n Refreshes an EXISTING install; it never creates one (that is init). A tokens-only consumer\n has no scaffold to reconcile, so sync reports n/a and exits 0 there \u2014 as doctor does.\n\ncheck-theme / doctor / sync all resolve their project the same way: BASALT_CWD, else the\ninvocation cwd \u2014 nothing inferred, nothing ascended or descended into.\n--tokens-only and --framework are alternatives; passing both is an error.\n\nEvery subcommand accepts --help / -h to print this message and exit without running.\n--version / -v prints the resolved package version and exits 0 \u2014 the one command that proves\nWHICH basalt-ui ran, which a `bunx` cache can otherwise make a lie. An unknown command or an\nunrecognized flag exits 1 and names what it did not understand; neither is ever ignored.";
49
+ const USAGE = "Usage: basalt-ui <--version | init [--with-router] [--with-query] [--merge-lint] |\n sync [--force] [--check] [--verbose] [--tokens-only|--framework] |\n check-theme [--audit-allows] |\n doctor [--tokens-only|--framework] | guard-hook | tokens:css | fonts:css | help>\n\ncheck-theme [--tokens-only|--framework] [--audit-allows]\n --audit-allows reports instead of scanning: every `theme-allow` annotation and every\n `basalt.exemptRules` entry, with what each one still suppresses \u2014 proved by re-running the\n guard with that one waiver neutralized, not by reading its text. Exits 1 on a waiver that\n suppresses nothing (dead), which is what makes it usable as a CI gate.\n\ntokens:css [--out <path>] [--check] [--selector-attribute <attr> | --selector-class <class>]\n [--light-class <class>] [--dark-value <v>] [--light-value <v>]\n [--default-scheme <dark|light|none>] [--media-fallback] [--only <core|all>]\n [--no-legacy-aliases]\n Emit the --vx-* stylesheet (stdout unless --out). Defaults reproduce basalt-ui/tokens.css.\n --selector-class emits `:root.dark` instead of an attribute selector (the Tailwind convention).\n --check writes nothing and exits 1 when --out differs from what would be emitted (a CI gate).\n --no-legacy-aliases drops the deprecated camelCase spellings (--vx-accentFill and friends),\n which are emitted by default as aliases of the canonical kebab-case names.\n\nfonts:css [--out <path>] [--check]\n Emit the shipped --basalt-font-* stacks as plain CSS \u2014 the typeface half of the token layer,\n otherwise reachable only by importing styles.css.\n\ndoctor [--tokens-only|--framework]\n Checks THIS project only: the manifest exists, the oxlint preset is wired, the pre-commit\n hook runs check-theme (via `lefthook dump`), and the ai package major is consistent within\n this package.json. Profile is DECLARED, same as check-theme \u2014 never inferred.\n\nsync [--force] [--check] [--verbose] [--tokens-only|--framework]\n Refreshes an EXISTING install; it never creates one (that is init). A tokens-only consumer\n has no scaffold to reconcile, so sync reports n/a and exits 0 there \u2014 as doctor does.\n --check prints the permanent placement skips as a COUNT; --verbose spells them out again.\n\ncheck-theme / doctor / sync all resolve their project the same way: BASALT_CWD, else the\ninvocation cwd \u2014 nothing inferred, nothing ascended or descended into.\n--tokens-only and --framework are alternatives; passing both is an error.\n\nEvery subcommand accepts --help / -h to print this message and exit without running.\n--version / -v prints the resolved package version and exits 0 \u2014 the one command that proves\nWHICH basalt-ui ran, which a `bunx` cache can otherwise make a lie. An unknown command or an\nunrecognized flag exits 1 and names what it did not understand; neither is ever ignored.";
49
50
  export {
50
51
  COMMAND_OPTIONS,
51
52
  USAGE,