@eifi1/ui-kit 0.4.0 → 0.5.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/LICENSE +32 -0
- package/README.md +498 -57
- package/dist/chart.d.ts +10 -0
- package/dist/chart.js +10 -0
- package/dist/chart.js.map +1 -0
- package/dist/components/account-settings.d.ts +11 -0
- package/dist/components/account-settings.js +19 -12
- package/dist/components/account-settings.js.map +1 -1
- package/dist/components/alert-banner.d.ts +10 -6
- package/dist/components/alert-banner.js +20 -16
- package/dist/components/alert-banner.js.map +1 -1
- package/dist/components/amount-input.d.ts +30 -4
- package/dist/components/amount-input.js +36 -14
- package/dist/components/amount-input.js.map +1 -1
- package/dist/components/calculator.d.ts +24 -7
- package/dist/components/calculator.js +37 -20
- package/dist/components/calculator.js.map +1 -1
- package/dist/components/chart-zoom.d.ts +156 -0
- package/dist/components/chart-zoom.js +262 -0
- package/dist/components/chart-zoom.js.map +1 -0
- package/dist/components/chart.d.ts +28 -3
- package/dist/components/chart.js +71 -32
- package/dist/components/chart.js.map +1 -1
- package/dist/components/checkbox.d.ts +51 -0
- package/dist/components/checkbox.js +123 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/chip.d.ts +128 -0
- package/dist/components/chip.js +357 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/choice-parts.d.ts +24 -0
- package/dist/components/choice-parts.js +18 -0
- package/dist/components/choice-parts.js.map +1 -0
- package/dist/components/combobox-core.d.ts +52 -9
- package/dist/components/combobox-core.js +91 -20
- package/dist/components/combobox-core.js.map +1 -1
- package/dist/components/combobox.d.ts +43 -33
- package/dist/components/combobox.js +409 -340
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/currency-select.d.ts +48 -8
- package/dist/components/currency-select.js +107 -65
- package/dist/components/currency-select.js.map +1 -1
- package/dist/components/data-table-filter-popover.d.ts +15 -3
- package/dist/components/data-table-filter-popover.js +23 -17
- package/dist/components/data-table-filter-popover.js.map +1 -1
- package/dist/components/data-table-filters.d.ts +1 -1
- package/dist/components/data-table-filters.js +3 -2
- package/dist/components/data-table-filters.js.map +1 -1
- package/dist/components/data-table-labels.d.ts +53 -2
- package/dist/components/data-table-labels.js +31 -2
- package/dist/components/data-table-labels.js.map +1 -1
- package/dist/components/data-table-pagination.d.ts +3 -1
- package/dist/components/data-table-pagination.js +16 -10
- package/dist/components/data-table-pagination.js.map +1 -1
- package/dist/components/data-table-sort.js +1 -0
- package/dist/components/data-table-sort.js.map +1 -1
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +201 -85
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.d.ts +25 -9
- package/dist/components/date-picker.js +232 -94
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/dropdown.d.ts +96 -20
- package/dist/components/dropdown.js +77 -33
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/entity-combobox.d.ts +5 -4
- package/dist/components/entity-combobox.js +107 -76
- package/dist/components/entity-combobox.js.map +1 -1
- package/dist/components/facing-pair.d.ts +36 -0
- package/dist/components/facing-pair.js +40 -0
- package/dist/components/facing-pair.js.map +1 -0
- package/dist/components/field-sync.d.ts +164 -0
- package/dist/components/field-sync.js +264 -0
- package/dist/components/field-sync.js.map +1 -0
- package/dist/components/file-dropzone.d.ts +38 -6
- package/dist/components/file-dropzone.js +34 -9
- package/dist/components/file-dropzone.js.map +1 -1
- package/dist/components/full-bleed-dialog.d.ts +37 -26
- package/dist/components/full-bleed-dialog.js +20 -5
- package/dist/components/full-bleed-dialog.js.map +1 -1
- package/dist/components/grouped-picker.d.ts +21 -10
- package/dist/components/grouped-picker.js +66 -52
- package/dist/components/grouped-picker.js.map +1 -1
- package/dist/components/hover-menu.d.ts +20 -4
- package/dist/components/hover-menu.js +15 -4
- package/dist/components/hover-menu.js.map +1 -1
- package/dist/components/mini-calendar.d.ts +102 -14
- package/dist/components/mini-calendar.js +256 -70
- package/dist/components/mini-calendar.js.map +1 -1
- package/dist/components/modal.d.ts +9 -3
- package/dist/components/modal.js +34 -45
- package/dist/components/modal.js.map +1 -1
- package/dist/components/month-picker.d.ts +79 -0
- package/dist/components/month-picker.js +342 -0
- package/dist/components/month-picker.js.map +1 -0
- package/dist/components/multi-entity-combobox.d.ts +7 -6
- package/dist/components/multi-entity-combobox.js +112 -72
- package/dist/components/multi-entity-combobox.js.map +1 -1
- package/dist/components/multi-select.d.ts +13 -11
- package/dist/components/multi-select.js +161 -84
- package/dist/components/multi-select.js.map +1 -1
- package/dist/components/number-field.d.ts +92 -0
- package/dist/components/number-field.js +111 -0
- package/dist/components/number-field.js.map +1 -0
- package/dist/components/number-input.d.ts +28 -2
- package/dist/components/number-input.js +12 -2
- package/dist/components/number-input.js.map +1 -1
- package/dist/components/numpad-sheet.d.ts +32 -4
- package/dist/components/numpad-sheet.js +30 -13
- package/dist/components/numpad-sheet.js.map +1 -1
- package/dist/components/page-contents.d.ts +90 -0
- package/dist/components/page-contents.js +170 -0
- package/dist/components/page-contents.js.map +1 -0
- package/dist/components/password-strength.d.ts +106 -0
- package/dist/components/password-strength.js +132 -0
- package/dist/components/password-strength.js.map +1 -0
- package/dist/components/picker-sheet.d.ts +23 -15
- package/dist/components/picker-sheet.js +34 -14
- package/dist/components/picker-sheet.js.map +1 -1
- package/dist/components/popover.d.ts +62 -4
- package/dist/components/popover.js +52 -2
- package/dist/components/popover.js.map +1 -1
- package/dist/components/search-field.d.ts +25 -7
- package/dist/components/search-field.js +7 -5
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/series-chart-labels.d.ts +14 -0
- package/dist/components/series-chart-labels.js +11 -0
- package/dist/components/series-chart-labels.js.map +1 -0
- package/dist/components/series-chart.d.ts +207 -0
- package/dist/components/series-chart.js +223 -0
- package/dist/components/series-chart.js.map +1 -0
- package/dist/components/settings-fields.d.ts +14 -4
- package/dist/components/settings-fields.js +19 -8
- package/dist/components/settings-fields.js.map +1 -1
- package/dist/components/signature-pad.d.ts +119 -0
- package/dist/components/signature-pad.js +405 -0
- package/dist/components/signature-pad.js.map +1 -0
- package/dist/components/slider.d.ts +80 -0
- package/dist/components/slider.js +155 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/components/sparkline.d.ts +93 -0
- package/dist/components/sparkline.js +205 -0
- package/dist/components/sparkline.js.map +1 -0
- package/dist/components/stat-tile.d.ts +151 -0
- package/dist/components/stat-tile.js +236 -0
- package/dist/components/stat-tile.js.map +1 -0
- package/dist/components/swipeable-row.d.ts +25 -2
- package/dist/components/swipeable-row.js +31 -0
- package/dist/components/swipeable-row.js.map +1 -1
- package/dist/components/switch.d.ts +60 -0
- package/dist/components/switch.js +81 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/time-input.d.ts +58 -0
- package/dist/components/time-input.js +50 -0
- package/dist/components/time-input.js.map +1 -0
- package/dist/components/toggle-group.d.ts +18 -3
- package/dist/components/toggle-group.js +10 -6
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle-legend.d.ts +115 -0
- package/dist/components/toggle-legend.js +114 -0
- package/dist/components/toggle-legend.js.map +1 -0
- package/dist/components/tooltip.d.ts +40 -11
- package/dist/components/tooltip.js +77 -20
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/treemap.d.ts +132 -0
- package/dist/components/treemap.js +179 -0
- package/dist/components/treemap.js.map +1 -0
- package/dist/components/ui.d.ts +159 -46
- package/dist/components/ui.js +220 -115
- package/dist/components/ui.js.map +1 -1
- package/dist/components/use-mobile-reveal.js +1 -0
- package/dist/components/use-mobile-reveal.js.map +1 -1
- package/dist/components/use-table-state.d.ts +1 -1
- package/dist/components/use-table-state.js +38 -17
- package/dist/components/use-table-state.js.map +1 -1
- package/dist/components/user-avatar.d.ts +15 -4
- package/dist/components/user-avatar.js +5 -4
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/components/wizard-stepper.js +3 -2
- package/dist/components/wizard-stepper.js.map +1 -1
- package/dist/{data-table-filters-aXyf0Xub.d.ts → data-table-filters-DM1auUfH.d.ts} +27 -5
- package/dist/data-table.d.ts +7 -0
- package/dist/data-table.js +12 -0
- package/dist/data-table.js.map +1 -0
- package/dist/feedback/feedback-attachment.js +12 -11
- package/dist/feedback/feedback-attachment.js.map +1 -1
- package/dist/feedback/feedback-dialog.js +2 -1
- package/dist/feedback/feedback-dialog.js.map +1 -1
- package/dist/feedback/feedback-inbox.d.ts +33 -7
- package/dist/feedback/feedback-inbox.js +49 -37
- package/dist/feedback/feedback-inbox.js.map +1 -1
- package/dist/feedback.d.ts +5 -0
- package/dist/feedback.js +5 -0
- package/dist/feedback.js.map +1 -0
- package/dist/hooks/use-anchored-panel.js +1 -0
- package/dist/hooks/use-anchored-panel.js.map +1 -1
- package/dist/hooks/use-anchored-rect.js +1 -0
- package/dist/hooks/use-anchored-rect.js.map +1 -1
- package/dist/hooks/use-announce.d.ts +46 -0
- package/dist/hooks/use-announce.js +37 -0
- package/dist/hooks/use-announce.js.map +1 -0
- package/dist/hooks/use-body-scroll-lock.js +1 -0
- package/dist/hooks/use-body-scroll-lock.js.map +1 -1
- package/dist/hooks/use-close-transition.js +1 -0
- package/dist/hooks/use-close-transition.js.map +1 -1
- package/dist/hooks/use-dismiss.js +1 -0
- package/dist/hooks/use-dismiss.js.map +1 -1
- package/dist/hooks/use-focus-trap.d.ts +16 -0
- package/dist/hooks/use-focus-trap.js +67 -0
- package/dist/hooks/use-focus-trap.js.map +1 -0
- package/dist/hooks/use-media-query.js +1 -0
- package/dist/hooks/use-media-query.js.map +1 -1
- package/dist/hooks/use-overlay-history.js +27 -1
- package/dist/hooks/use-overlay-history.js.map +1 -1
- package/dist/hooks/use-row-swipe.js +1 -0
- package/dist/hooks/use-row-swipe.js.map +1 -1
- package/dist/i18n/defaults.d.ts +31 -0
- package/dist/i18n/defaults.js +63 -0
- package/dist/i18n/defaults.js.map +1 -0
- package/dist/i18n/kit-labels.d.ts +250 -0
- package/dist/i18n/kit-labels.js +184 -0
- package/dist/i18n/kit-labels.js.map +1 -0
- package/dist/index.d.ts +47 -22
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/calc.js +1 -0
- package/dist/lib/calc.js.map +1 -1
- package/dist/lib/cn.js +1 -0
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/dates.js +1 -0
- package/dist/lib/dates.js.map +1 -1
- package/dist/lib/logger.js +1 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/safe-storage.js +1 -0
- package/dist/lib/safe-storage.js.map +1 -1
- package/dist/search/command-palette.d.ts +11 -1
- package/dist/search/command-palette.js +21 -15
- package/dist/search/command-palette.js.map +1 -1
- package/dist/search.d.ts +2 -0
- package/dist/search.js +3 -0
- package/dist/search.js.map +1 -0
- package/dist/shell/app-shell.d.ts +33 -4
- package/dist/shell/app-shell.js +275 -78
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/option-switcher-menu.js +3 -2
- package/dist/shell/option-switcher-menu.js.map +1 -1
- package/dist/shell/role-switcher.d.ts +6 -0
- package/dist/shell/role-switcher.js +7 -4
- package/dist/shell/role-switcher.js.map +1 -1
- package/dist/shell/top-bar.d.ts +15 -7
- package/dist/shell/top-bar.js +3 -5
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/shell/topbar-action-menu.js +3 -2
- package/dist/shell/topbar-action-menu.js.map +1 -1
- package/dist/shell/topbar-controls.d.ts +5 -2
- package/dist/shell/topbar-controls.js +20 -15
- package/dist/shell/topbar-controls.js.map +1 -1
- package/dist/shell.d.ts +13 -0
- package/dist/shell.js +8 -0
- package/dist/shell.js.map +1 -0
- package/dist/theme/chart-palette.d.ts +19 -12
- package/dist/theme/chart-palette.js +24 -30
- package/dist/theme/chart-palette.js.map +1 -1
- package/dist/theme/color.d.ts +78 -0
- package/dist/theme/color.js +148 -0
- package/dist/theme/color.js.map +1 -0
- package/dist/theme/palette-derive.d.ts +140 -0
- package/dist/theme/palette-derive.js +376 -0
- package/dist/theme/palette-derive.js.map +1 -0
- package/dist/theme/palette-presets.d.ts +27 -5
- package/dist/theme/palette-presets.js +231 -33
- package/dist/theme/palette-presets.js.map +1 -1
- package/dist/theme/palette-store.js +1 -0
- package/dist/theme/palette-store.js.map +1 -1
- package/dist/theme/theme-store.js +1 -0
- package/dist/theme/theme-store.js.map +1 -1
- package/dist/tour/tour.d.ts +2 -1
- package/dist/tour/tour.js +15 -9
- package/dist/tour/tour.js.map +1 -1
- package/dist/tour.d.ts +2 -0
- package/dist/tour.js +3 -0
- package/dist/tour.js.map +1 -0
- package/dist/wizard/stepper-nav.js +10 -8
- package/dist/wizard/stepper-nav.js.map +1 -1
- package/dist/wizard/types.d.ts +18 -2
- package/dist/wizard/types.js +4 -1
- package/dist/wizard/types.js.map +1 -1
- package/dist/wizard/use-wizard.js +12 -4
- package/dist/wizard/use-wizard.js.map +1 -1
- package/dist/wizard/validation.js +1 -0
- package/dist/wizard/validation.js.map +1 -1
- package/dist/wizard/wizard-context.js +1 -0
- package/dist/wizard/wizard-context.js.map +1 -1
- package/dist/wizard/wizard-step.js +1 -0
- package/dist/wizard/wizard-step.js.map +1 -1
- package/dist/wizard/wizard-summary.js +5 -3
- package/dist/wizard/wizard-summary.js.map +1 -1
- package/dist/wizard.d.ts +8 -0
- package/dist/wizard.js +9 -0
- package/dist/wizard.js.map +1 -0
- package/package.json +67 -14
- package/src/chart.ts +22 -0
- package/src/components/account-settings.tsx +275 -0
- package/src/components/alert-banner.tsx +75 -0
- package/src/components/amount-input.tsx +471 -0
- package/src/components/calculator.tsx +252 -0
- package/src/components/chart-zoom.tsx +499 -0
- package/src/components/chart.tsx +363 -0
- package/src/components/checkbox.tsx +222 -0
- package/src/components/chip.tsx +570 -0
- package/src/components/choice-parts.ts +33 -0
- package/src/components/combobox-core.tsx +540 -0
- package/src/components/combobox.tsx +1026 -0
- package/src/components/currency-select.tsx +269 -0
- package/src/components/data-table-filter-popover.tsx +252 -0
- package/src/components/data-table-filters.ts +176 -0
- package/src/components/data-table-labels.ts +190 -0
- package/src/components/data-table-pagination.tsx +126 -0
- package/src/components/data-table-sort.ts +70 -0
- package/src/components/data-table.tsx +1740 -0
- package/src/components/date-picker.tsx +631 -0
- package/src/components/dropdown.tsx +341 -0
- package/src/components/entity-combobox.tsx +215 -0
- package/src/components/facing-pair.ts +86 -0
- package/src/components/field-sync.tsx +472 -0
- package/src/components/file-dropzone.tsx +181 -0
- package/src/components/full-bleed-dialog.tsx +179 -0
- package/src/components/grouped-picker.tsx +157 -0
- package/src/components/hover-menu.tsx +183 -0
- package/src/components/mini-calendar.tsx +554 -0
- package/src/components/modal.tsx +287 -0
- package/src/components/month-picker.tsx +549 -0
- package/src/components/multi-entity-combobox.tsx +226 -0
- package/src/components/multi-select.tsx +303 -0
- package/src/components/number-field.tsx +225 -0
- package/src/components/number-input.tsx +211 -0
- package/src/components/numpad-sheet.tsx +264 -0
- package/src/components/page-contents.tsx +289 -0
- package/src/components/password-strength.tsx +258 -0
- package/src/components/picker-sheet.tsx +251 -0
- package/src/components/popover.tsx +195 -0
- package/src/components/search-field.tsx +146 -0
- package/src/components/series-chart-labels.ts +26 -0
- package/src/components/series-chart.tsx +481 -0
- package/src/components/settings-fields.tsx +68 -0
- package/src/components/signature-pad.tsx +630 -0
- package/src/components/slider.tsx +313 -0
- package/src/components/sparkline.tsx +329 -0
- package/src/components/stat-tile.tsx +448 -0
- package/src/components/swipeable-row.tsx +271 -0
- package/src/components/switch.tsx +170 -0
- package/src/components/time-input.tsx +107 -0
- package/src/components/toggle-group.tsx +153 -0
- package/src/components/toggle-legend.tsx +229 -0
- package/src/components/tooltip.tsx +477 -0
- package/src/components/treemap.tsx +390 -0
- package/src/components/ui.tsx +1284 -0
- package/src/components/use-mobile-reveal.ts +54 -0
- package/src/components/use-table-state.ts +295 -0
- package/src/components/user-avatar.tsx +61 -0
- package/src/components/wizard-stepper.tsx +37 -0
- package/src/data-table.ts +19 -0
- package/src/feedback/feedback-attachment.tsx +246 -0
- package/src/feedback/feedback-dialog.tsx +209 -0
- package/src/feedback/feedback-inbox.tsx +615 -0
- package/src/feedback.ts +8 -0
- package/src/hooks/use-anchored-panel.ts +169 -0
- package/src/hooks/use-anchored-rect.ts +78 -0
- package/src/hooks/use-announce.ts +85 -0
- package/src/hooks/use-body-scroll-lock.ts +94 -0
- package/src/hooks/use-close-transition.ts +99 -0
- package/src/hooks/use-dismiss.ts +77 -0
- package/src/hooks/use-focus-trap.ts +160 -0
- package/src/hooks/use-media-query.ts +23 -0
- package/src/hooks/use-overlay-history.ts +345 -0
- package/src/hooks/use-row-swipe.ts +212 -0
- package/src/i18n/defaults.ts +68 -0
- package/src/i18n/kit-labels.tsx +447 -0
- package/src/index.ts +169 -0
- package/src/lib/calc.ts +309 -0
- package/src/lib/cn.ts +5 -0
- package/src/lib/dates.ts +187 -0
- package/src/lib/logger.ts +73 -0
- package/src/lib/safe-storage.ts +37 -0
- package/src/search/command-palette.tsx +342 -0
- package/src/search.ts +3 -0
- package/src/shell/app-shell.tsx +772 -0
- package/src/shell/option-switcher-menu.tsx +72 -0
- package/src/shell/role-switcher.tsx +46 -0
- package/src/shell/top-bar.tsx +39 -0
- package/src/shell/topbar-action-menu.tsx +91 -0
- package/src/shell/topbar-controls.tsx +229 -0
- package/src/shell.ts +10 -0
- package/src/theme/chart-palette.ts +145 -0
- package/src/theme/color.ts +266 -0
- package/src/theme/palette-derive.ts +681 -0
- package/src/theme/palette-presets.ts +760 -0
- package/src/theme/palette-store.ts +94 -0
- package/src/theme/theme-store.ts +145 -0
- package/src/tour/tour.tsx +586 -0
- package/src/tour.ts +3 -0
- package/src/vite-env.d.ts +26 -0
- package/src/wizard/stepper-nav.tsx +221 -0
- package/src/wizard/types.ts +178 -0
- package/src/wizard/use-wizard.ts +345 -0
- package/src/wizard/validation.ts +37 -0
- package/src/wizard/wizard-context.tsx +29 -0
- package/src/wizard/wizard-step.tsx +14 -0
- package/src/wizard/wizard-summary.tsx +69 -0
- package/src/wizard.ts +14 -0
- package/tokens.css +222 -20
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
const DEFAULT_PRESET = {
|
|
2
3
|
id: "default",
|
|
3
4
|
name: "Default",
|
|
@@ -15,7 +16,7 @@ const DEFAULT_PRESET = {
|
|
|
15
16
|
brandHover: "#4338ca",
|
|
16
17
|
brandContrast: "#ffffff",
|
|
17
18
|
moneyIncome: "#0b6650",
|
|
18
|
-
moneyExpense: "#
|
|
19
|
+
moneyExpense: "#905902",
|
|
19
20
|
moneyNet: "#7a4fa3",
|
|
20
21
|
moneyNeutral: "#6b6453",
|
|
21
22
|
chart: ["#332288", "#88ccee", "#44aa99", "#117733", "#999933", "#ddcc77", "#cc6677", "#882255", "#aa4499"],
|
|
@@ -243,7 +244,7 @@ const ALTERNATIVE_PRESETS = [
|
|
|
243
244
|
brandHover: "#1f515f",
|
|
244
245
|
brandContrast: "#ffffff",
|
|
245
246
|
moneyIncome: "#0b6650",
|
|
246
|
-
moneyExpense: "#
|
|
247
|
+
moneyExpense: "#905902",
|
|
247
248
|
moneyNet: "#7a4fa3",
|
|
248
249
|
moneyNeutral: "#6b6453",
|
|
249
250
|
chart: ["#332288", "#88ccee", "#44aa99", "#117733", "#999933", "#ddcc77", "#cc6677", "#882255", "#aa4499"],
|
|
@@ -378,35 +379,236 @@ const IMPRINT_PRESET = {
|
|
|
378
379
|
light: { ...DEFAULT_PRESET.light, chart: IMPRINT_HUES_LIGHT },
|
|
379
380
|
dark: { ...DEFAULT_PRESET.dark, chart: IMPRINT_HUES_DARK }
|
|
380
381
|
};
|
|
381
|
-
const
|
|
382
|
+
const PAUL_TOL = {
|
|
383
|
+
light: DEFAULT_PRESET.light.chart,
|
|
384
|
+
dark: DEFAULT_PRESET.dark.chart
|
|
385
|
+
};
|
|
386
|
+
const DERIVED_PRESETS = [
|
|
387
|
+
{
|
|
388
|
+
id: "ink",
|
|
389
|
+
name: "Ink",
|
|
390
|
+
blurb: "Cool blue-grey ground \xB7 solved for AAA body text",
|
|
391
|
+
light: {
|
|
392
|
+
bgPage: "#dce2ef",
|
|
393
|
+
bgSurface: "#e6edfa",
|
|
394
|
+
bgSurface2: "#e1e8f5",
|
|
395
|
+
border: "#b0bace",
|
|
396
|
+
textPrimary: "#2e3139",
|
|
397
|
+
textSecondary: "#454950",
|
|
398
|
+
textMuted: "#5f646c",
|
|
399
|
+
brand: "#2f5fd0",
|
|
400
|
+
brandHover: "#1f4cbc",
|
|
401
|
+
brandContrast: "#ffffff",
|
|
402
|
+
moneyIncome: "#00735f",
|
|
403
|
+
moneyExpense: "#8e5900",
|
|
404
|
+
moneyNet: "#7356a2",
|
|
405
|
+
moneyNeutral: "#656970",
|
|
406
|
+
chart: PAUL_TOL.light,
|
|
407
|
+
heat: {
|
|
408
|
+
neutral: "#e1e8f5",
|
|
409
|
+
under: "#00735f",
|
|
410
|
+
over: "#8e5900",
|
|
411
|
+
seqLow: "#fee3c5",
|
|
412
|
+
seqHigh: "#8e5900",
|
|
413
|
+
empty: "rgba(120,110,95,0.14)"
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
dark: {
|
|
417
|
+
bgPage: "#090d15",
|
|
418
|
+
bgSurface: "#131720",
|
|
419
|
+
bgSurface2: "#1d212b",
|
|
420
|
+
border: "#363e4e",
|
|
421
|
+
textPrimary: "#c7ccd5",
|
|
422
|
+
textSecondary: "#a7abb4",
|
|
423
|
+
textMuted: "#858992",
|
|
424
|
+
brand: "#3364d5",
|
|
425
|
+
brandHover: "#4477ea",
|
|
426
|
+
brandContrast: "#ffffff",
|
|
427
|
+
moneyIncome: "#00997f",
|
|
428
|
+
moneyExpense: "#b77b29",
|
|
429
|
+
moneyNet: "#9679c9",
|
|
430
|
+
moneyNeutral: "#a1a5ac",
|
|
431
|
+
chart: PAUL_TOL.dark,
|
|
432
|
+
heat: {
|
|
433
|
+
neutral: "#1d212b",
|
|
434
|
+
under: "#00997f",
|
|
435
|
+
over: "#b77b29",
|
|
436
|
+
seqLow: "#331f05",
|
|
437
|
+
seqHigh: "#b77b29",
|
|
438
|
+
empty: "rgba(160,170,190,0.10)"
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
id: "moss",
|
|
444
|
+
name: "Moss",
|
|
445
|
+
blurb: "Green ground \xB7 solved for AAA body text",
|
|
446
|
+
light: {
|
|
447
|
+
bgPage: "#d8e6dd",
|
|
448
|
+
bgSurface: "#e3f1e8",
|
|
449
|
+
bgSurface2: "#deece3",
|
|
450
|
+
border: "#abbfb2",
|
|
451
|
+
textPrimary: "#2c342e",
|
|
452
|
+
textSecondary: "#434b45",
|
|
453
|
+
textMuted: "#5d6660",
|
|
454
|
+
brand: "#2f7a52",
|
|
455
|
+
brandHover: "#196842",
|
|
456
|
+
brandContrast: "#ffffff",
|
|
457
|
+
moneyIncome: "#00735f",
|
|
458
|
+
moneyExpense: "#8f5a00",
|
|
459
|
+
moneyNet: "#7457a3",
|
|
460
|
+
moneyNeutral: "#646b66",
|
|
461
|
+
chart: PAUL_TOL.light,
|
|
462
|
+
heat: {
|
|
463
|
+
neutral: "#deece3",
|
|
464
|
+
under: "#00735f",
|
|
465
|
+
over: "#8f5a00",
|
|
466
|
+
seqLow: "#fee3c4",
|
|
467
|
+
seqHigh: "#8f5a00",
|
|
468
|
+
empty: "rgba(120,110,95,0.14)"
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
dark: {
|
|
472
|
+
bgPage: "#060f0a",
|
|
473
|
+
bgSurface: "#101a14",
|
|
474
|
+
bgSurface2: "#1a241e",
|
|
475
|
+
border: "#314237",
|
|
476
|
+
textPrimary: "#c5cfc9",
|
|
477
|
+
textSecondary: "#a4aea8",
|
|
478
|
+
textMuted: "#838c86",
|
|
479
|
+
brand: "#2f7a52",
|
|
480
|
+
brandHover: "#428c63",
|
|
481
|
+
brandContrast: "#ffffff",
|
|
482
|
+
moneyIncome: "#009a80",
|
|
483
|
+
moneyExpense: "#b87b2a",
|
|
484
|
+
moneyNet: "#9779c9",
|
|
485
|
+
moneyNeutral: "#9fa7a2",
|
|
486
|
+
chart: PAUL_TOL.dark,
|
|
487
|
+
heat: {
|
|
488
|
+
neutral: "#1a241e",
|
|
489
|
+
under: "#009a80",
|
|
490
|
+
over: "#b87b2a",
|
|
491
|
+
seqLow: "#331f05",
|
|
492
|
+
seqHigh: "#b87b2a",
|
|
493
|
+
empty: "rgba(160,170,190,0.10)"
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
id: "plum",
|
|
499
|
+
name: "Plum",
|
|
500
|
+
blurb: "Violet ground \xB7 solved for AAA body text",
|
|
501
|
+
light: {
|
|
502
|
+
bgPage: "#e7dfea",
|
|
503
|
+
bgSurface: "#f2e9f5",
|
|
504
|
+
bgSurface2: "#ece4ef",
|
|
505
|
+
border: "#c2b6c6",
|
|
506
|
+
textPrimary: "#342f36",
|
|
507
|
+
textSecondary: "#4c474e",
|
|
508
|
+
textMuted: "#676169",
|
|
509
|
+
brand: "#7b3f8f",
|
|
510
|
+
brandHover: "#692d7d",
|
|
511
|
+
brandContrast: "#ffffff",
|
|
512
|
+
moneyIncome: "#00725e",
|
|
513
|
+
moneyExpense: "#8e5900",
|
|
514
|
+
moneyNet: "#7356a2",
|
|
515
|
+
moneyNeutral: "#6c676e",
|
|
516
|
+
chart: PAUL_TOL.light,
|
|
517
|
+
heat: {
|
|
518
|
+
neutral: "#ece4ef",
|
|
519
|
+
under: "#00725e",
|
|
520
|
+
over: "#8e5900",
|
|
521
|
+
seqLow: "#fee3c5",
|
|
522
|
+
seqHigh: "#8e5900",
|
|
523
|
+
empty: "rgba(120,110,95,0.14)"
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
dark: {
|
|
527
|
+
bgPage: "#100b12",
|
|
528
|
+
bgSurface: "#1b151d",
|
|
529
|
+
bgSurface2: "#251f27",
|
|
530
|
+
border: "#453a48",
|
|
531
|
+
textPrimary: "#d0cad3",
|
|
532
|
+
textSecondary: "#afa9b1",
|
|
533
|
+
textMuted: "#8d878f",
|
|
534
|
+
brand: "#8f52a4",
|
|
535
|
+
brandHover: "#a264b7",
|
|
536
|
+
brandContrast: "#ffffff",
|
|
537
|
+
moneyIncome: "#00997f",
|
|
538
|
+
moneyExpense: "#b77b29",
|
|
539
|
+
moneyNet: "#9679c8",
|
|
540
|
+
moneyNeutral: "#a8a2aa",
|
|
541
|
+
chart: PAUL_TOL.dark,
|
|
542
|
+
heat: {
|
|
543
|
+
neutral: "#251f27",
|
|
544
|
+
under: "#00997f",
|
|
545
|
+
over: "#b77b29",
|
|
546
|
+
seqLow: "#331f05",
|
|
547
|
+
seqHigh: "#b77b29",
|
|
548
|
+
empty: "rgba(160,170,190,0.10)"
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
id: "contrast",
|
|
554
|
+
name: "High contrast",
|
|
555
|
+
blurb: "Near-neutral ground \xB7 text solved to 13:1 / 8.5:1 / 5.5:1",
|
|
556
|
+
light: {
|
|
557
|
+
bgPage: "#e0e2e5",
|
|
558
|
+
bgSurface: "#ebedef",
|
|
559
|
+
bgSurface2: "#e6e8ea",
|
|
560
|
+
border: "#b7babe",
|
|
561
|
+
textPrimary: "#191d24",
|
|
562
|
+
textSecondary: "#373c44",
|
|
563
|
+
textMuted: "#535860",
|
|
564
|
+
brand: "#1f2937",
|
|
565
|
+
brandHover: "#111b28",
|
|
566
|
+
brandContrast: "#ffffff",
|
|
567
|
+
moneyIncome: "#00735f",
|
|
568
|
+
moneyExpense: "#8e5900",
|
|
569
|
+
moneyNet: "#7456a2",
|
|
570
|
+
moneyNeutral: "#656970",
|
|
571
|
+
chart: PAUL_TOL.light,
|
|
572
|
+
heat: {
|
|
573
|
+
neutral: "#e6e8ea",
|
|
574
|
+
under: "#00735f",
|
|
575
|
+
over: "#8e5900",
|
|
576
|
+
seqLow: "#fee3c5",
|
|
577
|
+
seqHigh: "#8e5900",
|
|
578
|
+
empty: "rgba(120,110,95,0.14)"
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
dark: {
|
|
582
|
+
bgPage: "#0c0d0f",
|
|
583
|
+
bgSurface: "#161719",
|
|
584
|
+
bgSurface2: "#202123",
|
|
585
|
+
border: "#3c3e41",
|
|
586
|
+
textPrimary: "#e1e7f0",
|
|
587
|
+
textSecondary: "#b7bdc5",
|
|
588
|
+
textMuted: "#9197a0",
|
|
589
|
+
brand: "#546c8d",
|
|
590
|
+
brandHover: "#657e9f",
|
|
591
|
+
brandContrast: "#ffffff",
|
|
592
|
+
moneyIncome: "#00997f",
|
|
593
|
+
moneyExpense: "#b77b29",
|
|
594
|
+
moneyNet: "#9679c8",
|
|
595
|
+
moneyNeutral: "#a0a5ac",
|
|
596
|
+
chart: PAUL_TOL.dark,
|
|
597
|
+
heat: {
|
|
598
|
+
neutral: "#202123",
|
|
599
|
+
under: "#00997f",
|
|
600
|
+
over: "#b77b29",
|
|
601
|
+
seqLow: "#331f05",
|
|
602
|
+
seqHigh: "#b77b29",
|
|
603
|
+
empty: "rgba(160,170,190,0.10)"
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
];
|
|
608
|
+
const PALETTES = [DEFAULT_PRESET, IMPRINT_PRESET, ...DERIVED_PRESETS];
|
|
382
609
|
function presetById(id) {
|
|
383
610
|
return PALETTES.find((p) => p.id === id) ?? DEFAULT_PRESET;
|
|
384
611
|
}
|
|
385
|
-
const TOKEN_VARS = [
|
|
386
|
-
"--bg-page",
|
|
387
|
-
"--bg-surface",
|
|
388
|
-
"--bg-surface-2",
|
|
389
|
-
"--border",
|
|
390
|
-
"--text-primary",
|
|
391
|
-
"--text-secondary",
|
|
392
|
-
"--text-muted",
|
|
393
|
-
"--brand",
|
|
394
|
-
"--brand-hover",
|
|
395
|
-
"--brand-contrast",
|
|
396
|
-
"--money-income",
|
|
397
|
-
"--money-expense",
|
|
398
|
-
"--money-net",
|
|
399
|
-
"--money-neutral",
|
|
400
|
-
"--chart-1",
|
|
401
|
-
"--chart-2",
|
|
402
|
-
"--chart-3",
|
|
403
|
-
"--chart-4",
|
|
404
|
-
"--chart-5",
|
|
405
|
-
"--chart-6",
|
|
406
|
-
"--chart-7",
|
|
407
|
-
"--chart-8",
|
|
408
|
-
"--chart-9"
|
|
409
|
-
];
|
|
410
612
|
function applyTokenSet(el, t) {
|
|
411
613
|
el.style.setProperty("--bg-page", t.bgPage);
|
|
412
614
|
el.style.setProperty("--bg-surface", t.bgSurface);
|
|
@@ -424,17 +626,13 @@ function applyTokenSet(el, t) {
|
|
|
424
626
|
el.style.setProperty("--money-neutral", t.moneyNeutral);
|
|
425
627
|
t.chart.forEach((c, i) => el.style.setProperty(`--chart-${i + 1}`, c));
|
|
426
628
|
}
|
|
427
|
-
function clearTokenSet(el) {
|
|
428
|
-
for (const v of TOKEN_VARS) el.style.removeProperty(v);
|
|
429
|
-
}
|
|
430
629
|
export {
|
|
431
630
|
ALTERNATIVE_PRESETS,
|
|
432
631
|
DEFAULT_PRESET,
|
|
632
|
+
DERIVED_PRESETS,
|
|
433
633
|
IMPRINT_PRESET,
|
|
434
634
|
PALETTES,
|
|
435
|
-
TOKEN_VARS,
|
|
436
635
|
applyTokenSet,
|
|
437
|
-
clearTokenSet,
|
|
438
636
|
presetById
|
|
439
637
|
};
|
|
440
638
|
//# sourceMappingURL=palette-presets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/theme/palette-presets.ts"],"sourcesContent":["// Appearance presets — the single source of truth for the \"palette switcher\"\n// (feedback #307). Each preset is a full set of design-token values for BOTH\n// light and dark, covering the page/surface backgrounds, the brand accent, the\n// semantic money trio and the 9-colour categorical chart palette, plus the\n// heatmap stops. A preset is applied at runtime by writing these as CSS custom\n// properties onto <html> (see palette-store.ts), which instantly re-skins every\n// token-driven surface and chart. The two heatmaps and the treemap interpolate\n// concrete hex in JS, so they read the same values from here via the hooks in\n// palette-store.\n//\n// The alternative presets below were designed + accessibility-vetted as a set\n// (each is CVD-safe across protan/deutan/tritan, money is never red/green, and\n// text/background contrast clears WCAG AA on both themes). To promote one to the\n// app-wide default, copy its values into the :root/.dark blocks in tokens.css\n// (the default preset here mirrors those exactly).\n\nimport type { HeatStops } from \"./chart-palette\";\n\nexport interface TokenSet {\n // Backgrounds & chrome\n bgPage: string;\n bgSurface: string;\n bgSurface2: string;\n border: string;\n textPrimary: string;\n // The two quieter text roles. Derived per preset by blending `textPrimary` toward\n // that preset's own surfaces until the contrast against the WORST of bgPage /\n // bgSurface / bgSurface2 hits 7:1 and 4.8:1 — so both clear WCAG AA for normal text\n // (4.5:1) on every surface they can land on, and both inherit the preset's warmth\n // instead of importing a cool grey from Tailwind's palette.\n textSecondary: string;\n textMuted: string;\n // Interactive brand accent\n brand: string;\n brandHover: string;\n brandContrast: string;\n // Semantic money (never red/green — CVD-safe, paired with +/- cues elsewhere)\n moneyIncome: string;\n moneyExpense: string;\n moneyNet: string;\n moneyNeutral: string;\n // Categorical chart series (Paul Tol \"Muted\" by default — CVD-safe), exactly 9\n chart: string[];\n // Heatmap interpolation stops (variance diverging + spending sequential)\n heat: HeatStops;\n}\n\nexport interface PalettePreset {\n id: string;\n name: string;\n blurb: string;\n light: TokenSet;\n dark: TokenSet;\n}\n\n// The shipped default (feedback #328): SOLAR-DUSK warm light + INDIGO-NOIR deep\n// dark surfaces — the two curated presets the user picked per mode — but with a\n// SINGLE unified brand accent (indigo) across both modes rather than each\n// preset's own accent (the user asked for the brand accent to stay the same).\n// So the light block is Solar Dusk with its teal brand overridden to indigo, and\n// the dark block is Indigo Noir (already indigo). The money trio\n// (income/expense/net) drives the chart bars and stays palette-driven so those\n// semantic colours adapt to each mode's contrast (best practice — feedback #328).\nexport const DEFAULT_PRESET: PalettePreset = {\n id: \"default\",\n name: \"Default\",\n blurb: \"Solar-dusk light · indigo-noir dark\",\n light: {\n // Solar Dusk (light) surfaces, with the brand unified to indigo (see above).\n bgPage: \"#ede3cf\",\n bgSurface: \"#f6ecd9\",\n bgSurface2: \"#f3ead6\",\n border: \"#e2d6bd\",\n textPrimary: \"#463f33\",\n textSecondary: \"#50493c\",\n textMuted: \"#696153\",\n brand: \"#4f46e5\",\n brandHover: \"#4338ca\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0b6650\",\n moneyExpense: \"#9c6418\",\n moneyNet: \"#7a4fa3\",\n moneyNeutral: \"#6b6453\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#f1e7d2\",\n under: \"#0b6650\",\n over: \"#9c6418\",\n seqLow: \"#f6ecd0\",\n seqHigh: \"#9c6418\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n // Indigo Noir (dark)\n bgPage: \"#0e0f1c\",\n bgSurface: \"#181a2c\",\n bgSurface2: \"#21243a\",\n border: \"#2e3252\",\n textPrimary: \"#e6e8f4\",\n textSecondary: \"#adafbe\",\n textMuted: \"#8d90a0\",\n brand: \"#8b93f8\",\n brandHover: \"#aab1fb\",\n brandContrast: \"#16182b\",\n moneyIncome: \"#22c3b6\",\n // Amber-400 (#fbbf24) sat at OKLab L 0.84 while every other dark token lives\n // at 0.68–0.74, so outflows outshouted the inflows they are paired with. Same\n // hue and chroma, pulled to L 0.785 (dev#434). The heat stops below keep the\n // brighter amber on purpose: a gradient endpoint is meant to be the loudest\n // thing in its scale, and it is a fill, not a figure.\n moneyExpense: \"#e4b035\",\n moneyNet: \"#b39aef\",\n moneyNeutral: \"#9aa1bd\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#cc78be\"],\n heat: {\n neutral: \"#363a5a\",\n under: \"#22c3b6\",\n over: \"#fbbf24\",\n seqLow: \"#3a2c14\",\n seqHigh: \"#fbbf24\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n};\n\n// Vetted alternatives to preview (CVD-safe, WCAG-AA on both themes).\nexport const ALTERNATIVE_PRESETS: PalettePreset[] = [\n {\n id: \"warm-paper\",\n name: \"Warm Paper\",\n blurb: \"Cream paper · warm charcoal\",\n light: {\n bgPage: \"#ece5d6\",\n bgSurface: \"#f5efe3\",\n bgSurface2: \"#efe9dc\",\n border: \"#e2dcce\",\n textPrimary: \"#2b2622\",\n textSecondary: \"#4f4a44\",\n textMuted: \"#68625b\",\n brand: \"#9a5b34\",\n brandHover: \"#824827\",\n brandContrast: \"#fdf8f2\",\n moneyIncome: \"#0e7490\",\n moneyExpense: \"#8f3f0a\",\n moneyNet: \"#7c4dab\",\n moneyNeutral: \"#6b6258\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#efe9dc\",\n under: \"#0e7490\",\n over: \"#b45309\",\n seqLow: \"#fbeed2\",\n seqHigh: \"#b45309\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#211e1a\",\n bgSurface: \"#2a2622\",\n bgSurface2: \"#322d27\",\n border: \"#3d372f\",\n textPrimary: \"#e8e2d6\",\n textSecondary: \"#bfb9af\",\n textMuted: \"#9e998f\",\n brand: \"#e0986a\",\n brandHover: \"#ecae85\",\n brandContrast: \"#2a1c10\",\n moneyIncome: \"#4dd4c4\",\n moneyExpense: \"#fbbf3a\",\n moneyNet: \"#bfa0e0\",\n moneyNeutral: \"#a39a8c\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#cf6488\", \"#cc78be\"],\n heat: {\n neutral: \"#3a352e\",\n under: \"#4dd4c4\",\n over: \"#fbbf3a\",\n seqLow: \"#2c2014\",\n seqHigh: \"#fbbf3a\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n {\n id: \"nord-frost\",\n name: \"Nord Frost\",\n blurb: \"Cool snow · polar night\",\n light: {\n bgPage: \"#e2e7ef\",\n bgSurface: \"#f0f3f8\",\n bgSurface2: \"#e1e6ee\",\n border: \"#d3dae6\",\n textPrimary: \"#2e3440\",\n textSecondary: \"#454b56\",\n textMuted: \"#5e636e\",\n brand: \"#3b6ea5\",\n brandHover: \"#305a86\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0f8060\",\n moneyExpense: \"#b8431a\",\n moneyNet: \"#6b4fa0\",\n moneyNeutral: \"#5b6577\",\n chart: [\"#3a6ea5\", \"#56b6c2\", \"#2f7d4f\", \"#cdb52f\", \"#d05f7a\", \"#9a3f93\", \"#4d4bbf\", \"#dd6a2c\", \"#7a8aa8\"],\n heat: {\n neutral: \"#e1e6ee\",\n under: \"#0f8060\",\n over: \"#b8431a\",\n seqLow: \"#f0f3f7\",\n seqHigh: \"#a14708\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#242933\",\n bgSurface: \"#2e3440\",\n bgSurface2: \"#3b4252\",\n border: \"#434c5e\",\n textPrimary: \"#eceff4\",\n textSecondary: \"#d4d7de\",\n textMuted: \"#afb3bc\",\n brand: \"#88c0d0\",\n brandHover: \"#a3d4e0\",\n brandContrast: \"#1c2027\",\n moneyIncome: \"#4ecf9c\",\n moneyExpense: \"#e8a24f\",\n moneyNet: \"#caa0e0\",\n moneyNeutral: \"#9aa5b8\",\n chart: [\"#7fb4e6\", \"#5ec9d6\", \"#5cc483\", \"#dccb6b\", \"#e87e96\", \"#bd6fce\", \"#9389f2\", \"#ef8f4a\", \"#8693ad\"],\n heat: {\n neutral: \"#3b4252\",\n under: \"#4ecf9c\",\n over: \"#e8a24f\",\n seqLow: \"#33373f\",\n seqHigh: \"#e8a24f\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n {\n id: \"indigo-noir\",\n name: \"Indigo Noir\",\n blurb: \"Crisp light · deep indigo\",\n light: {\n bgPage: \"#e8eaf2\",\n bgSurface: \"#f6f7fb\",\n bgSurface2: \"#eef1f8\",\n border: \"#dadfeb\",\n textPrimary: \"#1b1f2e\",\n textSecondary: \"#494d5a\",\n textMuted: \"#626572\",\n brand: \"#4338ca\",\n brandHover: \"#3730a3\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0c6478\",\n moneyExpense: \"#b45309\",\n moneyNet: \"#7c3aed\",\n moneyNeutral: \"#5b6478\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#eef1f7\",\n under: \"#0c6478\",\n over: \"#b45309\",\n seqLow: \"#fdeed4\",\n seqHigh: \"#b45309\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#0e0f1c\",\n bgSurface: \"#181a2c\",\n bgSurface2: \"#21243a\",\n border: \"#2e3252\",\n textPrimary: \"#e6e8f4\",\n textSecondary: \"#adafbe\",\n textMuted: \"#8d90a0\",\n brand: \"#8b93f8\",\n brandHover: \"#aab1fb\",\n brandContrast: \"#16182b\",\n moneyIncome: \"#22c3b6\",\n // Kept byte-identical to DEFAULT_PRESET.dark, which IS this preset (dev#434).\n moneyExpense: \"#e4b035\",\n moneyNet: \"#b39aef\",\n moneyNeutral: \"#9aa1bd\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#cc78be\"],\n heat: {\n neutral: \"#363a5a\",\n under: \"#22c3b6\",\n over: \"#fbbf24\",\n seqLow: \"#3a2c14\",\n seqHigh: \"#fbbf24\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n {\n id: \"solar-dusk\",\n name: \"Solar Dusk\",\n blurb: \"Solarized warmth, easy on the eyes\",\n light: {\n bgPage: \"#ede3cf\",\n bgSurface: \"#f6ecd9\",\n bgSurface2: \"#f3ead6\",\n border: \"#e2d6bd\",\n textPrimary: \"#463f33\",\n textSecondary: \"#50493c\",\n textMuted: \"#696153\",\n brand: \"#2c6979\",\n brandHover: \"#1f515f\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0b6650\",\n moneyExpense: \"#9c6418\",\n moneyNet: \"#7a4fa3\",\n moneyNeutral: \"#6b6453\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#f1e7d2\",\n under: \"#0b6650\",\n over: \"#9c6418\",\n seqLow: \"#f6ecd0\",\n seqHigh: \"#9c6418\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#0e1a1f\",\n bgSurface: \"#15272e\",\n bgSurface2: \"#1d333c\",\n border: \"#2a4750\",\n textPrimary: \"#d6cdb8\",\n textSecondary: \"#c2bdab\",\n textMuted: \"#9c9d91\",\n brand: \"#5fb8cc\",\n brandHover: \"#82cfe0\",\n brandContrast: \"#0a161a\",\n moneyIncome: \"#22b884\",\n moneyExpense: \"#f0b94e\",\n moneyNet: \"#bfa0e0\",\n moneyNeutral: \"#9aa3a0\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#c873c0\"],\n heat: {\n neutral: \"#26424b\",\n under: \"#22b884\",\n over: \"#f0b94e\",\n seqLow: \"#1f3138\",\n seqHigh: \"#f0b94e\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n {\n id: \"zinc-mono\",\n name: \"Zinc Mono\",\n blurb: \"True-neutral chrome · data pops\",\n light: {\n bgPage: \"#ececec\",\n bgSurface: \"#f7f7f7\",\n bgSurface2: \"#f4f4f5\",\n border: \"#e4e4e7\",\n textPrimary: \"#18181b\",\n textSecondary: \"#4e4e50\",\n textMuted: \"#676769\",\n brand: \"#1d4ed8\",\n brandHover: \"#1e40af\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0e7490\",\n moneyExpense: \"#cf4310\",\n moneyNet: \"#7c3aed\",\n moneyNeutral: \"#52525b\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#f4f4f5\",\n under: \"#0e7490\",\n over: \"#cf4310\",\n seqLow: \"#fdeede\",\n seqHigh: \"#cf4310\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#09090b\",\n bgSurface: \"#18181b\",\n bgSurface2: \"#27272a\",\n border: \"#3f3f46\",\n textPrimary: \"#f4f4f5\",\n textSecondary: \"#b2b2b3\",\n textMuted: \"#929294\",\n brand: \"#60a5fa\",\n brandHover: \"#93c5fd\",\n brandContrast: \"#0b1220\",\n moneyIncome: \"#22d3ee\",\n moneyExpense: \"#f97316\",\n moneyNet: \"#c4b5fd\",\n moneyNeutral: \"#a1a1aa\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#cc78be\"],\n heat: {\n neutral: \"#3f3f46\",\n under: \"#22d3ee\",\n over: \"#f97316\",\n seqLow: \"#3a2410\",\n seqHigh: \"#f97316\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n];\n\n// ── \"Imprint\" chart hues (feedback #119) ──────────────────────────────────────\n// anyplot's categorical palette (https://anyplot.ai/palette), by a colleague of\n// Marcel's. 8 hues + an amber semantic anchor, derived from Wong 2011 by a\n// Petroff-style max-min ΔE search in CAM02-UCS with Machado 2009 CVD simulation —\n// the same lineage as our default Paul Tol \"Muted\", one generation on, and tuned\n// for exactly the warm cream paper our light theme uses.\n//\n// This preset is DEFAULT_PRESET with ONLY the 9 categorical chart hues swapped;\n// surfaces, brand and the semantic money trio stay put, because those were their\n// own deliberate decisions (#307/#328) and are not what the palette is for. The\n// heatmap stops likewise stay teal/amber: they encode under/over, and imprint's\n// own diverging ramp is red↔blue, which would fight the money semantics.\n//\n// Measured on our surfaces (CIEDE2000, Machado severity-100, 8 like-for-like\n// slots on the cream light surface #f6ecd9) — neither palette dominates:\n// Paul Tol Muted min-contrast 1.38 · worst-CVD ΔE00 11.8 · normal ΔE00 16.1\n// imprint min-contrast 1.95 · worst-CVD ΔE00 7.2 · normal ΔE00 22.2\n// i.e. imprint is punchier and has no near-invisible pale slot (Tol's #88ccee\n// sits at 1.5:1 on cream), while Tol separates better under colour-vision\n// deficiency. So this ships as a CHOICE, not as a new default.\n//\n// The dark set keeps imprint's hues verbatim except matte-red and rose, whose\n// lightness is lifted (oklch L 0.50→0.55) because verbatim they land at 2.67:1\n// and 2.76:1 on our deep-indigo dark surface — under WCAG 2.1 SC 1.4.11's 3:1\n// non-text minimum. Lifted they clear it at 3.30:1 / 3.28:1. anyplot documents\n// the same shortfall on their own dark ground and answers it with a stroke halo;\n// lifting keeps the fix in the token layer instead of every chart component.\nconst IMPRINT_HUES_LIGHT = [\n \"#009E73\", // brand green — first series\n \"#C475FD\", // lavender — creative\n \"#4467A3\", // blue — cool / info\n \"#BD8233\", // ochre — earth / commodity\n \"#AE3030\", // matte red — bad / loss / error\n \"#2ABCCD\", // cyan — sky / tech-cool\n \"#954477\", // rose — wellness / health\n \"#99B314\", // lime — growth / nature\n \"#DDCC77\", // amber — anyplot's semantic anchor, used here as slot 9\n];\nconst IMPRINT_HUES_DARK = [\n \"#009E73\",\n \"#C475FD\",\n \"#4467A3\",\n \"#BD8233\",\n \"#BF413E\", // matte red, L-lifted for our dark surface\n \"#2ABCCD\",\n \"#A25083\", // rose, L-lifted for our dark surface\n \"#99B314\",\n \"#DDCC77\",\n];\n\nexport const IMPRINT_PRESET: PalettePreset = {\n id: \"imprint\",\n name: \"Imprint\",\n blurb: \"Colourblind-safe chart hues by anyplot.ai\",\n light: { ...DEFAULT_PRESET.light, chart: IMPRINT_HUES_LIGHT },\n dark: { ...DEFAULT_PRESET.dark, chart: IMPRINT_HUES_DARK },\n};\n\n// The full-theme switcher from #307 stayed retired (feedback #328) — the default\n// is still the single source of truth for surfaces, brand and money, and\n// ALTERNATIVE_PRESETS above remains a reference bank only. What IS selectable is\n// the categorical chart ramp (feedback #119), so these two presets are the live\n// set; presetById falls back to the default for any stale persisted id.\nexport const PALETTES: PalettePreset[] = [DEFAULT_PRESET, IMPRINT_PRESET];\n\nexport function presetById(id: string): PalettePreset {\n return PALETTES.find((p) => p.id === id) ?? DEFAULT_PRESET;\n}\n\n// The CSS custom properties a preset writes onto <html>. Also used to clear the\n// overrides when returning to the default.\nexport const TOKEN_VARS = [\n \"--bg-page\",\n \"--bg-surface\",\n \"--bg-surface-2\",\n \"--border\",\n \"--text-primary\",\n \"--text-secondary\",\n \"--text-muted\",\n \"--brand\",\n \"--brand-hover\",\n \"--brand-contrast\",\n \"--money-income\",\n \"--money-expense\",\n \"--money-net\",\n \"--money-neutral\",\n \"--chart-1\",\n \"--chart-2\",\n \"--chart-3\",\n \"--chart-4\",\n \"--chart-5\",\n \"--chart-6\",\n \"--chart-7\",\n \"--chart-8\",\n \"--chart-9\",\n] as const;\n\n/** Write a token set onto an element's inline style as CSS custom properties. */\nexport function applyTokenSet(el: HTMLElement, t: TokenSet): void {\n el.style.setProperty(\"--bg-page\", t.bgPage);\n el.style.setProperty(\"--bg-surface\", t.bgSurface);\n el.style.setProperty(\"--bg-surface-2\", t.bgSurface2);\n el.style.setProperty(\"--border\", t.border);\n el.style.setProperty(\"--text-primary\", t.textPrimary);\n el.style.setProperty(\"--text-secondary\", t.textSecondary);\n el.style.setProperty(\"--text-muted\", t.textMuted);\n el.style.setProperty(\"--brand\", t.brand);\n el.style.setProperty(\"--brand-hover\", t.brandHover);\n el.style.setProperty(\"--brand-contrast\", t.brandContrast);\n el.style.setProperty(\"--money-income\", t.moneyIncome);\n el.style.setProperty(\"--money-expense\", t.moneyExpense);\n el.style.setProperty(\"--money-net\", t.moneyNet);\n el.style.setProperty(\"--money-neutral\", t.moneyNeutral);\n t.chart.forEach((c, i) => el.style.setProperty(`--chart-${i + 1}`, c));\n}\n\n/** Remove all preset overrides so the tokens.css :root/.dark defaults take over. */\nexport function clearTokenSet(el: HTMLElement): void {\n for (const v of TOKEN_VARS) el.style.removeProperty(v);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/theme/palette-presets.ts"],"sourcesContent":["// Appearance presets — the single source of truth for the \"palette switcher\"\n// (feedback #307). Each preset is a full set of design-token values for BOTH\n// light and dark, covering the page/surface backgrounds, the brand accent, the\n// semantic money trio and the 9-colour categorical chart palette, plus the\n// heatmap stops. A preset is applied at runtime by writing these as CSS custom\n// properties onto <html> (see palette-store.ts), which instantly re-skins every\n// token-driven surface and chart. The two heatmaps and the treemap interpolate\n// concrete hex in JS, so they read the same values from here via the hooks in\n// palette-store.\n//\n// The alternative presets below were designed + accessibility-vetted as a set\n// (each is CVD-safe across protan/deutan/tritan, money is never red/green, and\n// text/background contrast clears WCAG AA on both themes). To promote one to the\n// app-wide default, copy its values into the :root/.dark blocks in tokens.css\n// (the default preset here mirrors those exactly).\n\nimport type { HeatStops } from \"./chart-palette\";\n\nexport interface TokenSet {\n // Backgrounds & chrome\n bgPage: string;\n bgSurface: string;\n bgSurface2: string;\n border: string;\n textPrimary: string;\n // The two quieter text roles. Derived per preset by blending `textPrimary` toward\n // that preset's own surfaces until the contrast against the WORST of bgPage /\n // bgSurface / bgSurface2 hits 7:1 and 4.8:1 — so both clear WCAG AA for normal text\n // (4.5:1) on every surface they can land on, and both inherit the preset's warmth\n // instead of importing a cool grey from Tailwind's palette.\n textSecondary: string;\n textMuted: string;\n // Interactive brand accent\n brand: string;\n brandHover: string;\n brandContrast: string;\n // Semantic money (never red/green — CVD-safe, paired with +/- cues elsewhere)\n moneyIncome: string;\n moneyExpense: string;\n moneyNet: string;\n moneyNeutral: string;\n // Categorical chart series (Paul Tol \"Muted\" by default — CVD-safe), exactly 9\n chart: string[];\n // Heatmap interpolation stops (variance diverging + spending sequential)\n heat: HeatStops;\n}\n\nexport interface PalettePreset {\n id: string;\n name: string;\n blurb: string;\n light: TokenSet;\n dark: TokenSet;\n}\n\n// The shipped default (feedback #328): SOLAR-DUSK warm light + INDIGO-NOIR deep\n// dark surfaces — the two curated presets the user picked per mode — but with a\n// SINGLE unified brand accent (indigo) across both modes rather than each\n// preset's own accent (the user asked for the brand accent to stay the same).\n// So the light block is Solar Dusk with its teal brand overridden to indigo, and\n// the dark block is Indigo Noir (already indigo). The money trio\n// (income/expense/net) drives the chart bars and stays palette-driven so those\n// semantic colours adapt to each mode's contrast (best practice — feedback #328).\nexport const DEFAULT_PRESET: PalettePreset = {\n id: \"default\",\n name: \"Default\",\n blurb: \"Solar-dusk light · indigo-noir dark\",\n light: {\n // Solar Dusk (light) surfaces, with the brand unified to indigo (see above).\n bgPage: \"#ede3cf\",\n bgSurface: \"#f6ecd9\",\n bgSurface2: \"#f3ead6\",\n border: \"#e2d6bd\",\n textPrimary: \"#463f33\",\n textSecondary: \"#50493c\",\n textMuted: \"#696153\",\n brand: \"#4f46e5\",\n brandHover: \"#4338ca\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0b6650\",\n moneyExpense: \"#905902\",\n moneyNet: \"#7a4fa3\",\n moneyNeutral: \"#6b6453\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#f1e7d2\",\n under: \"#0b6650\",\n over: \"#9c6418\",\n seqLow: \"#f6ecd0\",\n seqHigh: \"#9c6418\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n // Indigo Noir (dark)\n bgPage: \"#0e0f1c\",\n bgSurface: \"#181a2c\",\n bgSurface2: \"#21243a\",\n border: \"#2e3252\",\n textPrimary: \"#e6e8f4\",\n textSecondary: \"#adafbe\",\n textMuted: \"#8d90a0\",\n brand: \"#8b93f8\",\n brandHover: \"#aab1fb\",\n brandContrast: \"#16182b\",\n moneyIncome: \"#22c3b6\",\n // Amber-400 (#fbbf24) sat at OKLab L 0.84 while every other dark token lives\n // at 0.68–0.74, so outflows outshouted the inflows they are paired with. Same\n // hue and chroma, pulled to L 0.785 (dev#434). The heat stops below keep the\n // brighter amber on purpose: a gradient endpoint is meant to be the loudest\n // thing in its scale, and it is a fill, not a figure.\n moneyExpense: \"#e4b035\",\n moneyNet: \"#b39aef\",\n moneyNeutral: \"#9aa1bd\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#cc78be\"],\n heat: {\n neutral: \"#363a5a\",\n under: \"#22c3b6\",\n over: \"#fbbf24\",\n seqLow: \"#3a2c14\",\n seqHigh: \"#fbbf24\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n};\n\n// Vetted alternatives to preview (CVD-safe, WCAG-AA on both themes).\nexport const ALTERNATIVE_PRESETS: PalettePreset[] = [\n {\n id: \"warm-paper\",\n name: \"Warm Paper\",\n blurb: \"Cream paper · warm charcoal\",\n light: {\n bgPage: \"#ece5d6\",\n bgSurface: \"#f5efe3\",\n bgSurface2: \"#efe9dc\",\n border: \"#e2dcce\",\n textPrimary: \"#2b2622\",\n textSecondary: \"#4f4a44\",\n textMuted: \"#68625b\",\n brand: \"#9a5b34\",\n brandHover: \"#824827\",\n brandContrast: \"#fdf8f2\",\n moneyIncome: \"#0e7490\",\n moneyExpense: \"#8f3f0a\",\n moneyNet: \"#7c4dab\",\n moneyNeutral: \"#6b6258\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#efe9dc\",\n under: \"#0e7490\",\n over: \"#b45309\",\n seqLow: \"#fbeed2\",\n seqHigh: \"#b45309\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#211e1a\",\n bgSurface: \"#2a2622\",\n bgSurface2: \"#322d27\",\n border: \"#3d372f\",\n textPrimary: \"#e8e2d6\",\n textSecondary: \"#bfb9af\",\n textMuted: \"#9e998f\",\n brand: \"#e0986a\",\n brandHover: \"#ecae85\",\n brandContrast: \"#2a1c10\",\n moneyIncome: \"#4dd4c4\",\n moneyExpense: \"#fbbf3a\",\n moneyNet: \"#bfa0e0\",\n moneyNeutral: \"#a39a8c\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#cf6488\", \"#cc78be\"],\n heat: {\n neutral: \"#3a352e\",\n under: \"#4dd4c4\",\n over: \"#fbbf3a\",\n seqLow: \"#2c2014\",\n seqHigh: \"#fbbf3a\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n {\n id: \"nord-frost\",\n name: \"Nord Frost\",\n blurb: \"Cool snow · polar night\",\n light: {\n bgPage: \"#e2e7ef\",\n bgSurface: \"#f0f3f8\",\n bgSurface2: \"#e1e6ee\",\n border: \"#d3dae6\",\n textPrimary: \"#2e3440\",\n textSecondary: \"#454b56\",\n textMuted: \"#5e636e\",\n brand: \"#3b6ea5\",\n brandHover: \"#305a86\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0f8060\",\n moneyExpense: \"#b8431a\",\n moneyNet: \"#6b4fa0\",\n moneyNeutral: \"#5b6577\",\n chart: [\"#3a6ea5\", \"#56b6c2\", \"#2f7d4f\", \"#cdb52f\", \"#d05f7a\", \"#9a3f93\", \"#4d4bbf\", \"#dd6a2c\", \"#7a8aa8\"],\n heat: {\n neutral: \"#e1e6ee\",\n under: \"#0f8060\",\n over: \"#b8431a\",\n seqLow: \"#f0f3f7\",\n seqHigh: \"#a14708\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#242933\",\n bgSurface: \"#2e3440\",\n bgSurface2: \"#3b4252\",\n border: \"#434c5e\",\n textPrimary: \"#eceff4\",\n textSecondary: \"#d4d7de\",\n textMuted: \"#afb3bc\",\n brand: \"#88c0d0\",\n brandHover: \"#a3d4e0\",\n brandContrast: \"#1c2027\",\n moneyIncome: \"#4ecf9c\",\n moneyExpense: \"#e8a24f\",\n moneyNet: \"#caa0e0\",\n moneyNeutral: \"#9aa5b8\",\n chart: [\"#7fb4e6\", \"#5ec9d6\", \"#5cc483\", \"#dccb6b\", \"#e87e96\", \"#bd6fce\", \"#9389f2\", \"#ef8f4a\", \"#8693ad\"],\n heat: {\n neutral: \"#3b4252\",\n under: \"#4ecf9c\",\n over: \"#e8a24f\",\n seqLow: \"#33373f\",\n seqHigh: \"#e8a24f\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n {\n id: \"indigo-noir\",\n name: \"Indigo Noir\",\n blurb: \"Crisp light · deep indigo\",\n light: {\n bgPage: \"#e8eaf2\",\n bgSurface: \"#f6f7fb\",\n bgSurface2: \"#eef1f8\",\n border: \"#dadfeb\",\n textPrimary: \"#1b1f2e\",\n textSecondary: \"#494d5a\",\n textMuted: \"#626572\",\n brand: \"#4338ca\",\n brandHover: \"#3730a3\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0c6478\",\n moneyExpense: \"#b45309\",\n moneyNet: \"#7c3aed\",\n moneyNeutral: \"#5b6478\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#eef1f7\",\n under: \"#0c6478\",\n over: \"#b45309\",\n seqLow: \"#fdeed4\",\n seqHigh: \"#b45309\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#0e0f1c\",\n bgSurface: \"#181a2c\",\n bgSurface2: \"#21243a\",\n border: \"#2e3252\",\n textPrimary: \"#e6e8f4\",\n textSecondary: \"#adafbe\",\n textMuted: \"#8d90a0\",\n brand: \"#8b93f8\",\n brandHover: \"#aab1fb\",\n brandContrast: \"#16182b\",\n moneyIncome: \"#22c3b6\",\n // Kept byte-identical to DEFAULT_PRESET.dark, which IS this preset (dev#434).\n moneyExpense: \"#e4b035\",\n moneyNet: \"#b39aef\",\n moneyNeutral: \"#9aa1bd\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#cc78be\"],\n heat: {\n neutral: \"#363a5a\",\n under: \"#22c3b6\",\n over: \"#fbbf24\",\n seqLow: \"#3a2c14\",\n seqHigh: \"#fbbf24\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n {\n id: \"solar-dusk\",\n name: \"Solar Dusk\",\n blurb: \"Solarized warmth, easy on the eyes\",\n light: {\n bgPage: \"#ede3cf\",\n bgSurface: \"#f6ecd9\",\n bgSurface2: \"#f3ead6\",\n border: \"#e2d6bd\",\n textPrimary: \"#463f33\",\n textSecondary: \"#50493c\",\n textMuted: \"#696153\",\n brand: \"#2c6979\",\n brandHover: \"#1f515f\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0b6650\",\n moneyExpense: \"#905902\",\n moneyNet: \"#7a4fa3\",\n moneyNeutral: \"#6b6453\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#f1e7d2\",\n under: \"#0b6650\",\n over: \"#9c6418\",\n seqLow: \"#f6ecd0\",\n seqHigh: \"#9c6418\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#0e1a1f\",\n bgSurface: \"#15272e\",\n bgSurface2: \"#1d333c\",\n border: \"#2a4750\",\n textPrimary: \"#d6cdb8\",\n textSecondary: \"#c2bdab\",\n textMuted: \"#9c9d91\",\n brand: \"#5fb8cc\",\n brandHover: \"#82cfe0\",\n brandContrast: \"#0a161a\",\n moneyIncome: \"#22b884\",\n moneyExpense: \"#f0b94e\",\n moneyNet: \"#bfa0e0\",\n moneyNeutral: \"#9aa3a0\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#c873c0\"],\n heat: {\n neutral: \"#26424b\",\n under: \"#22b884\",\n over: \"#f0b94e\",\n seqLow: \"#1f3138\",\n seqHigh: \"#f0b94e\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n {\n id: \"zinc-mono\",\n name: \"Zinc Mono\",\n blurb: \"True-neutral chrome · data pops\",\n light: {\n bgPage: \"#ececec\",\n bgSurface: \"#f7f7f7\",\n bgSurface2: \"#f4f4f5\",\n border: \"#e4e4e7\",\n textPrimary: \"#18181b\",\n textSecondary: \"#4e4e50\",\n textMuted: \"#676769\",\n brand: \"#1d4ed8\",\n brandHover: \"#1e40af\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#0e7490\",\n moneyExpense: \"#cf4310\",\n moneyNet: \"#7c3aed\",\n moneyNeutral: \"#52525b\",\n chart: [\"#332288\", \"#88ccee\", \"#44aa99\", \"#117733\", \"#999933\", \"#ddcc77\", \"#cc6677\", \"#882255\", \"#aa4499\"],\n heat: {\n neutral: \"#f4f4f5\",\n under: \"#0e7490\",\n over: \"#cf4310\",\n seqLow: \"#fdeede\",\n seqHigh: \"#cf4310\",\n empty: \"rgba(148,163,184,0.14)\",\n },\n },\n dark: {\n bgPage: \"#09090b\",\n bgSurface: \"#18181b\",\n bgSurface2: \"#27272a\",\n border: \"#3f3f46\",\n textPrimary: \"#f4f4f5\",\n textSecondary: \"#b2b2b3\",\n textMuted: \"#929294\",\n brand: \"#60a5fa\",\n brandHover: \"#93c5fd\",\n brandContrast: \"#0b1220\",\n moneyIncome: \"#22d3ee\",\n moneyExpense: \"#f97316\",\n moneyNet: \"#c4b5fd\",\n moneyNeutral: \"#a1a1aa\",\n chart: [\"#7e72d6\", \"#9fd8f2\", \"#5fc4b0\", \"#3fa45f\", \"#bfbf5e\", \"#e8dda0\", \"#e08c9a\", \"#c25a78\", \"#cc78be\"],\n heat: {\n neutral: \"#3f3f46\",\n under: \"#22d3ee\",\n over: \"#f97316\",\n seqLow: \"#3a2410\",\n seqHigh: \"#f97316\",\n empty: \"rgba(148,163,184,0.10)\",\n },\n },\n },\n];\n\n// ── \"Imprint\" chart hues (feedback #119) ──────────────────────────────────────\n// anyplot's categorical palette (https://anyplot.ai/palette), by a colleague of\n// Marcel's. 8 hues + an amber semantic anchor, derived from Wong 2011 by a\n// Petroff-style max-min ΔE search in CAM02-UCS with Machado 2009 CVD simulation —\n// the same lineage as our default Paul Tol \"Muted\", one generation on, and tuned\n// for exactly the warm cream paper our light theme uses.\n//\n// This preset is DEFAULT_PRESET with ONLY the 9 categorical chart hues swapped;\n// surfaces, brand and the semantic money trio stay put, because those were their\n// own deliberate decisions (#307/#328) and are not what the palette is for. The\n// heatmap stops likewise stay teal/amber: they encode under/over, and imprint's\n// own diverging ramp is red↔blue, which would fight the money semantics.\n//\n// Measured on our surfaces (CIEDE2000, Machado severity-100, 8 like-for-like\n// slots on the cream light surface #f6ecd9) — neither palette dominates:\n// Paul Tol Muted min-contrast 1.38 · worst-CVD ΔE00 11.8 · normal ΔE00 16.1\n// imprint min-contrast 1.95 · worst-CVD ΔE00 7.2 · normal ΔE00 22.2\n// i.e. imprint is punchier and has no near-invisible pale slot (Tol's #88ccee\n// sits at 1.5:1 on cream), while Tol separates better under colour-vision\n// deficiency. So this ships as a CHOICE, not as a new default.\n//\n// The dark set keeps imprint's hues verbatim except matte-red and rose, whose\n// lightness is lifted (oklch L 0.50→0.55) because verbatim they land at 2.67:1\n// and 2.76:1 on our deep-indigo dark surface — under WCAG 2.1 SC 1.4.11's 3:1\n// non-text minimum. Lifted they clear it at 3.30:1 / 3.28:1. anyplot documents\n// the same shortfall on their own dark ground and answers it with a stroke halo;\n// lifting keeps the fix in the token layer instead of every chart component.\nconst IMPRINT_HUES_LIGHT = [\n \"#009E73\", // brand green — first series\n \"#C475FD\", // lavender — creative\n \"#4467A3\", // blue — cool / info\n \"#BD8233\", // ochre — earth / commodity\n \"#AE3030\", // matte red — bad / loss / error\n \"#2ABCCD\", // cyan — sky / tech-cool\n \"#954477\", // rose — wellness / health\n \"#99B314\", // lime — growth / nature\n \"#DDCC77\", // amber — anyplot's semantic anchor, used here as slot 9\n];\nconst IMPRINT_HUES_DARK = [\n \"#009E73\",\n \"#C475FD\",\n \"#4467A3\",\n \"#BD8233\",\n \"#BF413E\", // matte red, L-lifted for our dark surface\n \"#2ABCCD\",\n \"#A25083\", // rose, L-lifted for our dark surface\n \"#99B314\",\n \"#DDCC77\",\n];\n\nexport const IMPRINT_PRESET: PalettePreset = {\n id: \"imprint\",\n name: \"Imprint\",\n blurb: \"Colourblind-safe chart hues by anyplot.ai\",\n light: { ...DEFAULT_PRESET.light, chart: IMPRINT_HUES_LIGHT },\n dark: { ...DEFAULT_PRESET.dark, chart: IMPRINT_HUES_DARK },\n};\n\n// The full-theme switcher from #307 stayed retired (feedback #328) — the default\n// is still the single source of truth for surfaces, brand and money, and\n// ALTERNATIVE_PRESETS above remains a reference bank only. What IS selectable is\n// the categorical chart ramp (feedback #119), so these two presets are the live\n// set; presetById falls back to the default for any stale persisted id.\n/**\n * The categorical chart ramp, shared by every derived preset below.\n *\n * Paul Tol's \"Muted\" set, unchanged, and deliberately NOT re-derived per preset. A\n * categorical ramp solves a different problem from a UI palette — separability among\n * nine unknown siblings, rather than contrast against one known surface — and a set\n * optimised against real dichromat confusion lines beats one spaced evenly by formula.\n * `auditChartRamp` in palette-derive.ts measures it: 0.059 minimum separation in the\n * light variant, which is the number MIN_SEPARATION was calibrated under.\n *\n * It pays for that in contrast: four of the nine light hues sit below the 3:1 WCAG\n * 1.4.11 asks of a filled shape. That is not fixable by darkening them — see\n * SINGLE_LIGHTNESS_MAX_SERIES — so a chart with more than four series must carry a\n * second channel (a direct label, a pattern, a legend adjacent to the mark).\n */\nconst PAUL_TOL = {\n light: DEFAULT_PRESET.light.chart,\n dark: DEFAULT_PRESET.dark.chart,\n} as const;\n\n/**\n * Presets DERIVED rather than picked, by `derivePalette` from one brand colour each.\n *\n * Every text role here was solved to a contrast target against the worst of the three\n * surfaces it can land on, every semantic colour against both the page and its own badge\n * tint, and the whole set is re-checked by `derived-presets.test.ts` — which re-runs the\n * deriver and fails if these committed values drift from what it now produces.\n *\n * They are committed rather than derived at import because deriving eight token sets\n * costs ~34ms, and charging every consumer that at startup for palettes most of them\n * will never select is the wrong trade. The test is what keeps the two in agreement.\n */\nexport const DERIVED_PRESETS: PalettePreset[] = [\n {\n id: \"ink\",\n name: \"Ink\",\n blurb: \"Cool blue-grey ground · solved for AAA body text\",\n light: {\n bgPage: \"#dce2ef\",\n bgSurface: \"#e6edfa\",\n bgSurface2: \"#e1e8f5\",\n border: \"#b0bace\",\n textPrimary: \"#2e3139\",\n textSecondary: \"#454950\",\n textMuted: \"#5f646c\",\n brand: \"#2f5fd0\",\n brandHover: \"#1f4cbc\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#00735f\",\n moneyExpense: \"#8e5900\",\n moneyNet: \"#7356a2\",\n moneyNeutral: \"#656970\",\n chart: PAUL_TOL.light,\n heat: {\n neutral: \"#e1e8f5\",\n under: \"#00735f\",\n over: \"#8e5900\",\n seqLow: \"#fee3c5\",\n seqHigh: \"#8e5900\",\n empty: \"rgba(120,110,95,0.14)\",\n },\n },\n dark: {\n bgPage: \"#090d15\",\n bgSurface: \"#131720\",\n bgSurface2: \"#1d212b\",\n border: \"#363e4e\",\n textPrimary: \"#c7ccd5\",\n textSecondary: \"#a7abb4\",\n textMuted: \"#858992\",\n brand: \"#3364d5\",\n brandHover: \"#4477ea\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#00997f\",\n moneyExpense: \"#b77b29\",\n moneyNet: \"#9679c9\",\n moneyNeutral: \"#a1a5ac\",\n chart: PAUL_TOL.dark,\n heat: {\n neutral: \"#1d212b\",\n under: \"#00997f\",\n over: \"#b77b29\",\n seqLow: \"#331f05\",\n seqHigh: \"#b77b29\",\n empty: \"rgba(160,170,190,0.10)\",\n },\n },\n },\n {\n id: \"moss\",\n name: \"Moss\",\n blurb: \"Green ground · solved for AAA body text\",\n light: {\n bgPage: \"#d8e6dd\",\n bgSurface: \"#e3f1e8\",\n bgSurface2: \"#deece3\",\n border: \"#abbfb2\",\n textPrimary: \"#2c342e\",\n textSecondary: \"#434b45\",\n textMuted: \"#5d6660\",\n brand: \"#2f7a52\",\n brandHover: \"#196842\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#00735f\",\n moneyExpense: \"#8f5a00\",\n moneyNet: \"#7457a3\",\n moneyNeutral: \"#646b66\",\n chart: PAUL_TOL.light,\n heat: {\n neutral: \"#deece3\",\n under: \"#00735f\",\n over: \"#8f5a00\",\n seqLow: \"#fee3c4\",\n seqHigh: \"#8f5a00\",\n empty: \"rgba(120,110,95,0.14)\",\n },\n },\n dark: {\n bgPage: \"#060f0a\",\n bgSurface: \"#101a14\",\n bgSurface2: \"#1a241e\",\n border: \"#314237\",\n textPrimary: \"#c5cfc9\",\n textSecondary: \"#a4aea8\",\n textMuted: \"#838c86\",\n brand: \"#2f7a52\",\n brandHover: \"#428c63\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#009a80\",\n moneyExpense: \"#b87b2a\",\n moneyNet: \"#9779c9\",\n moneyNeutral: \"#9fa7a2\",\n chart: PAUL_TOL.dark,\n heat: {\n neutral: \"#1a241e\",\n under: \"#009a80\",\n over: \"#b87b2a\",\n seqLow: \"#331f05\",\n seqHigh: \"#b87b2a\",\n empty: \"rgba(160,170,190,0.10)\",\n },\n },\n },\n {\n id: \"plum\",\n name: \"Plum\",\n blurb: \"Violet ground · solved for AAA body text\",\n light: {\n bgPage: \"#e7dfea\",\n bgSurface: \"#f2e9f5\",\n bgSurface2: \"#ece4ef\",\n border: \"#c2b6c6\",\n textPrimary: \"#342f36\",\n textSecondary: \"#4c474e\",\n textMuted: \"#676169\",\n brand: \"#7b3f8f\",\n brandHover: \"#692d7d\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#00725e\",\n moneyExpense: \"#8e5900\",\n moneyNet: \"#7356a2\",\n moneyNeutral: \"#6c676e\",\n chart: PAUL_TOL.light,\n heat: {\n neutral: \"#ece4ef\",\n under: \"#00725e\",\n over: \"#8e5900\",\n seqLow: \"#fee3c5\",\n seqHigh: \"#8e5900\",\n empty: \"rgba(120,110,95,0.14)\",\n },\n },\n dark: {\n bgPage: \"#100b12\",\n bgSurface: \"#1b151d\",\n bgSurface2: \"#251f27\",\n border: \"#453a48\",\n textPrimary: \"#d0cad3\",\n textSecondary: \"#afa9b1\",\n textMuted: \"#8d878f\",\n brand: \"#8f52a4\",\n brandHover: \"#a264b7\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#00997f\",\n moneyExpense: \"#b77b29\",\n moneyNet: \"#9679c8\",\n moneyNeutral: \"#a8a2aa\",\n chart: PAUL_TOL.dark,\n heat: {\n neutral: \"#251f27\",\n under: \"#00997f\",\n over: \"#b77b29\",\n seqLow: \"#331f05\",\n seqHigh: \"#b77b29\",\n empty: \"rgba(160,170,190,0.10)\",\n },\n },\n },\n {\n id: \"contrast\",\n name: \"High contrast\",\n blurb: \"Near-neutral ground · text solved to 13:1 / 8.5:1 / 5.5:1\",\n light: {\n bgPage: \"#e0e2e5\",\n bgSurface: \"#ebedef\",\n bgSurface2: \"#e6e8ea\",\n border: \"#b7babe\",\n textPrimary: \"#191d24\",\n textSecondary: \"#373c44\",\n textMuted: \"#535860\",\n brand: \"#1f2937\",\n brandHover: \"#111b28\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#00735f\",\n moneyExpense: \"#8e5900\",\n moneyNet: \"#7456a2\",\n moneyNeutral: \"#656970\",\n chart: PAUL_TOL.light,\n heat: {\n neutral: \"#e6e8ea\",\n under: \"#00735f\",\n over: \"#8e5900\",\n seqLow: \"#fee3c5\",\n seqHigh: \"#8e5900\",\n empty: \"rgba(120,110,95,0.14)\",\n },\n },\n dark: {\n bgPage: \"#0c0d0f\",\n bgSurface: \"#161719\",\n bgSurface2: \"#202123\",\n border: \"#3c3e41\",\n textPrimary: \"#e1e7f0\",\n textSecondary: \"#b7bdc5\",\n textMuted: \"#9197a0\",\n brand: \"#546c8d\",\n brandHover: \"#657e9f\",\n brandContrast: \"#ffffff\",\n moneyIncome: \"#00997f\",\n moneyExpense: \"#b77b29\",\n moneyNet: \"#9679c8\",\n moneyNeutral: \"#a0a5ac\",\n chart: PAUL_TOL.dark,\n heat: {\n neutral: \"#202123\",\n under: \"#00997f\",\n over: \"#b77b29\",\n seqLow: \"#331f05\",\n seqHigh: \"#b77b29\",\n empty: \"rgba(160,170,190,0.10)\",\n },\n },\n },\n];\n\nexport const PALETTES: PalettePreset[] = [DEFAULT_PRESET, IMPRINT_PRESET, ...DERIVED_PRESETS];\n\nexport function presetById(id: string): PalettePreset {\n return PALETTES.find((p) => p.id === id) ?? DEFAULT_PRESET;\n}\n\n/**\n * Write a token set onto an element's inline style as CSS custom properties.\n *\n * This is the only writer, and there is no un-writer: `presetById` falls back to\n * `DEFAULT_PRESET` and this then writes the full set, so returning to the default\n * APPLIES it rather than clearing anything. A `clearTokenSet` and the hand-kept\n * 24-name `TOKEN_VARS` list it looped over used to live here, both exported, both\n * called by nobody in either consumer — a second copy of these property names with\n * nothing comparing the two, so adding a token here and forgetting it there would\n * have been silent and only a dead function would have noticed. The list is gone;\n * the test beside this asserts instead that every field of a `TokenSet` reaches the\n * element, which is what that list was standing in for.\n */\nexport function applyTokenSet(el: HTMLElement, t: TokenSet): void {\n el.style.setProperty(\"--bg-page\", t.bgPage);\n el.style.setProperty(\"--bg-surface\", t.bgSurface);\n el.style.setProperty(\"--bg-surface-2\", t.bgSurface2);\n el.style.setProperty(\"--border\", t.border);\n el.style.setProperty(\"--text-primary\", t.textPrimary);\n el.style.setProperty(\"--text-secondary\", t.textSecondary);\n el.style.setProperty(\"--text-muted\", t.textMuted);\n el.style.setProperty(\"--brand\", t.brand);\n el.style.setProperty(\"--brand-hover\", t.brandHover);\n el.style.setProperty(\"--brand-contrast\", t.brandContrast);\n el.style.setProperty(\"--money-income\", t.moneyIncome);\n el.style.setProperty(\"--money-expense\", t.moneyExpense);\n el.style.setProperty(\"--money-net\", t.moneyNet);\n el.style.setProperty(\"--money-neutral\", t.moneyNeutral);\n t.chart.forEach((c, i) => el.style.setProperty(`--chart-${i + 1}`, c));\n}\n"],"mappings":";AA+DO,MAAM,iBAAgC;AAAA,EAC3C,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA;AAAA,IAEL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,aAAa;AAAA,IACb,cAAc;AAAA,IACd,UAAU;AAAA,IACV,cAAc;AAAA,IACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,IACzG,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,MAAM;AAAA;AAAA,IAEJ,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMb,cAAc;AAAA,IACd,UAAU;AAAA,IACV,cAAc;AAAA,IACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,IACzG,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAGO,MAAM,sBAAuC;AAAA,EAClD;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA;AAAA,MAEb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,CAAC,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,WAAW,SAAS;AAAA,MACzG,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AA6BA,MAAM,qBAAqB;AAAA,EACzB;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AACA,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,iBAAgC;AAAA,EAC3C,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO,EAAE,GAAG,eAAe,OAAO,OAAO,mBAAmB;AAAA,EAC5D,MAAM,EAAE,GAAG,eAAe,MAAM,OAAO,kBAAkB;AAC3D;AAsBA,MAAM,WAAW;AAAA,EACf,OAAO,eAAe,MAAM;AAAA,EAC5B,MAAM,eAAe,KAAK;AAC5B;AAcO,MAAM,kBAAmC;AAAA,EAC9C;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,eAAe;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,UAAU;AAAA,MACV,cAAc;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,OAAO;AAAA,QACP,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,WAA4B,CAAC,gBAAgB,gBAAgB,GAAG,eAAe;AAErF,SAAS,WAAW,IAA2B;AACpD,SAAO,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK;AAC9C;AAeO,SAAS,cAAc,IAAiB,GAAmB;AAChE,KAAG,MAAM,YAAY,aAAa,EAAE,MAAM;AAC1C,KAAG,MAAM,YAAY,gBAAgB,EAAE,SAAS;AAChD,KAAG,MAAM,YAAY,kBAAkB,EAAE,UAAU;AACnD,KAAG,MAAM,YAAY,YAAY,EAAE,MAAM;AACzC,KAAG,MAAM,YAAY,kBAAkB,EAAE,WAAW;AACpD,KAAG,MAAM,YAAY,oBAAoB,EAAE,aAAa;AACxD,KAAG,MAAM,YAAY,gBAAgB,EAAE,SAAS;AAChD,KAAG,MAAM,YAAY,WAAW,EAAE,KAAK;AACvC,KAAG,MAAM,YAAY,iBAAiB,EAAE,UAAU;AAClD,KAAG,MAAM,YAAY,oBAAoB,EAAE,aAAa;AACxD,KAAG,MAAM,YAAY,kBAAkB,EAAE,WAAW;AACpD,KAAG,MAAM,YAAY,mBAAmB,EAAE,YAAY;AACtD,KAAG,MAAM,YAAY,eAAe,EAAE,QAAQ;AAC9C,KAAG,MAAM,YAAY,mBAAmB,EAAE,YAAY;AACtD,IAAE,MAAM,QAAQ,CAAC,GAAG,MAAM,GAAG,MAAM,YAAY,WAAW,IAAI,CAAC,IAAI,CAAC,CAAC;AACvE;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/theme/palette-store.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { create } from \"zustand\";\nimport { persist } from \"zustand/middleware\";\nimport type { StoreApi, UseBoundStore } from \"zustand\";\nimport { logger } from \"../lib/logger\";\nimport type { HeatStops } from \"./chart-palette\";\nimport { applyTokenSet, DEFAULT_PRESET, presetById, type TokenSet } from \"./palette-presets\";\nimport type { ThemeState, ThemeMode } from \"./theme-store\";\n\n/**\n * Pre-hydration counterpart to `applyPersistedTheme`: read the palette id\n * persisted under `storageKey` straight from localStorage and apply that preset's\n * tokens for the given resolved `mode` to <html>, BEFORE React mounts (no flash of\n * the default palette). Falls back to the default preset for missing/malformed\n * state, so an app entrypoint never re-types the persist envelope.\n */\nexport function applyPersistedPalette(storageKey: string, mode: ThemeMode): void {\n let id = DEFAULT_PRESET.id;\n try {\n const blob = JSON.parse(localStorage.getItem(storageKey) ?? \"{}\") as {\n state?: { id?: string };\n };\n if (blob.state?.id) id = blob.state.id;\n } catch {\n /* malformed storage → default preset */\n }\n applyTokenSet(document.documentElement, presetById(id)[mode]);\n}\n\n// The palette layer flips the whole app between candidate appearance presets at\n// runtime (feedback #307), re-skinning every token-driven surface and chart.\n// Persisted like the theme so a reload keeps the selection.\ninterface PaletteState {\n id: string;\n setId: (id: string) => void;\n}\n\n/**\n * Build a palette store bound to a consumer-supplied localStorage key, wired to\n * that app's theme store (for the active light/dark mode). Returns the palette\n * hook plus the effect + selector hooks that read the active preset's tokens.\n */\nexport function createPaletteStore(\n storageKey: string,\n useTheme: UseBoundStore<StoreApi<ThemeState>>,\n) {\n const usePalette = create<PaletteState>()(\n logger(\n persist(\n (set) => ({\n id: DEFAULT_PRESET.id,\n setId: (id) => set({ id }),\n }),\n { name: storageKey, version: 1 },\n ),\n \"palette\",\n ),\n );\n\n /** Apply the active preset's tokens to <html> as inline CSS custom properties,\n * re-applying when the preset or the light/dark theme changes. Tailwind v4\n * strips a bare root token block from the bundle, so this inline write from\n * the TS source of truth is what actually drives the colours. */\n function useApplyPalette(): void {\n const id = usePalette((s) => s.id);\n const mode = useTheme((s) => s.mode);\n useEffect(() => {\n applyTokenSet(document.documentElement, presetById(id)[mode]);\n }, [id, mode]);\n }\n\n /** The active preset's token set for the current theme — the source for the\n * JS-interpolated charts (treemap fills, heatmap stops) that can't read CSS\n * vars. */\n function useActiveTokenSet(): TokenSet {\n const id = usePalette((s) => s.id);\n const mode = useTheme((s) => s.mode);\n return presetById(id)[mode];\n }\n\n /** The 9 categorical chart hues for the active preset/theme (treemap cells). */\n function useChartHex(): string[] {\n return useActiveTokenSet().chart;\n }\n\n /** The active preset/theme heatmap interpolation stops. */\n function useHeatStops(): HeatStops {\n return useActiveTokenSet().heat;\n }\n\n return { usePalette, useApplyPalette, useActiveTokenSet, useChartHex, useHeatStops };\n}\n\nexport type PaletteStore = ReturnType<typeof createPaletteStore>;\n"],"mappings":"AAAA,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,eAAe;AAExB,SAAS,cAAc;AAEvB,SAAS,eAAe,gBAAgB,kBAAiC;AAUlE,SAAS,sBAAsB,YAAoB,MAAuB;AAC/E,MAAI,KAAK,eAAe;AACxB,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,aAAa,QAAQ,UAAU,KAAK,IAAI;AAGhE,QAAI,KAAK,OAAO,GAAI,MAAK,KAAK,MAAM;AAAA,EACtC,QAAQ;AAAA,EAER;AACA,gBAAc,SAAS,iBAAiB,WAAW,EAAE,EAAE,IAAI,CAAC;AAC9D;AAeO,SAAS,mBACd,YACA,UACA;AACA,QAAM,aAAa,OAAqB;AAAA,IACtC;AAAA,MACE;AAAA,QACE,CAAC,SAAS;AAAA,UACR,IAAI,eAAe;AAAA,UACnB,OAAO,CAAC,OAAO,IAAI,EAAE,GAAG,CAAC;AAAA,QAC3B;AAAA,QACA,EAAE,MAAM,YAAY,SAAS,EAAE;AAAA,MACjC;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAMA,WAAS,kBAAwB;AAC/B,UAAM,KAAK,WAAW,CAAC,MAAM,EAAE,EAAE;AACjC,UAAM,OAAO,SAAS,CAAC,MAAM,EAAE,IAAI;AACnC,cAAU,MAAM;AACd,oBAAc,SAAS,iBAAiB,WAAW,EAAE,EAAE,IAAI,CAAC;AAAA,IAC9D,GAAG,CAAC,IAAI,IAAI,CAAC;AAAA,EACf;AAKA,WAAS,oBAA8B;AACrC,UAAM,KAAK,WAAW,CAAC,MAAM,EAAE,EAAE;AACjC,UAAM,OAAO,SAAS,CAAC,MAAM,EAAE,IAAI;AACnC,WAAO,WAAW,EAAE,EAAE,IAAI;AAAA,EAC5B;AAGA,WAAS,cAAwB;AAC/B,WAAO,kBAAkB,EAAE;AAAA,EAC7B;AAGA,WAAS,eAA0B;AACjC,WAAO,kBAAkB,EAAE;AAAA,EAC7B;AAEA,SAAO,EAAE,YAAY,iBAAiB,mBAAmB,aAAa,aAAa;AACrF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/theme/palette-store.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { create } from \"zustand\";\nimport { persist } from \"zustand/middleware\";\nimport type { StoreApi, UseBoundStore } from \"zustand\";\nimport { logger } from \"../lib/logger\";\nimport type { HeatStops } from \"./chart-palette\";\nimport { applyTokenSet, DEFAULT_PRESET, presetById, type TokenSet } from \"./palette-presets\";\nimport type { ThemeState, ThemeMode } from \"./theme-store\";\n\n/**\n * Pre-hydration counterpart to `applyPersistedTheme`: read the palette id\n * persisted under `storageKey` straight from localStorage and apply that preset's\n * tokens for the given resolved `mode` to <html>, BEFORE React mounts (no flash of\n * the default palette). Falls back to the default preset for missing/malformed\n * state, so an app entrypoint never re-types the persist envelope.\n */\nexport function applyPersistedPalette(storageKey: string, mode: ThemeMode): void {\n let id = DEFAULT_PRESET.id;\n try {\n const blob = JSON.parse(localStorage.getItem(storageKey) ?? \"{}\") as {\n state?: { id?: string };\n };\n if (blob.state?.id) id = blob.state.id;\n } catch {\n /* malformed storage → default preset */\n }\n applyTokenSet(document.documentElement, presetById(id)[mode]);\n}\n\n// The palette layer flips the whole app between candidate appearance presets at\n// runtime (feedback #307), re-skinning every token-driven surface and chart.\n// Persisted like the theme so a reload keeps the selection.\ninterface PaletteState {\n id: string;\n setId: (id: string) => void;\n}\n\n/**\n * Build a palette store bound to a consumer-supplied localStorage key, wired to\n * that app's theme store (for the active light/dark mode). Returns the palette\n * hook plus the effect + selector hooks that read the active preset's tokens.\n */\nexport function createPaletteStore(\n storageKey: string,\n useTheme: UseBoundStore<StoreApi<ThemeState>>,\n) {\n const usePalette = create<PaletteState>()(\n logger(\n persist(\n (set) => ({\n id: DEFAULT_PRESET.id,\n setId: (id) => set({ id }),\n }),\n { name: storageKey, version: 1 },\n ),\n \"palette\",\n ),\n );\n\n /** Apply the active preset's tokens to <html> as inline CSS custom properties,\n * re-applying when the preset or the light/dark theme changes. Tailwind v4\n * strips a bare root token block from the bundle, so this inline write from\n * the TS source of truth is what actually drives the colours. */\n function useApplyPalette(): void {\n const id = usePalette((s) => s.id);\n const mode = useTheme((s) => s.mode);\n useEffect(() => {\n applyTokenSet(document.documentElement, presetById(id)[mode]);\n }, [id, mode]);\n }\n\n /** The active preset's token set for the current theme — the source for the\n * JS-interpolated charts (treemap fills, heatmap stops) that can't read CSS\n * vars. */\n function useActiveTokenSet(): TokenSet {\n const id = usePalette((s) => s.id);\n const mode = useTheme((s) => s.mode);\n return presetById(id)[mode];\n }\n\n /** The 9 categorical chart hues for the active preset/theme (treemap cells). */\n function useChartHex(): string[] {\n return useActiveTokenSet().chart;\n }\n\n /** The active preset/theme heatmap interpolation stops. */\n function useHeatStops(): HeatStops {\n return useActiveTokenSet().heat;\n }\n\n return { usePalette, useApplyPalette, useActiveTokenSet, useChartHex, useHeatStops };\n}\n\nexport type PaletteStore = ReturnType<typeof createPaletteStore>;\n"],"mappings":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,eAAe;AAExB,SAAS,cAAc;AAEvB,SAAS,eAAe,gBAAgB,kBAAiC;AAUlE,SAAS,sBAAsB,YAAoB,MAAuB;AAC/E,MAAI,KAAK,eAAe;AACxB,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,aAAa,QAAQ,UAAU,KAAK,IAAI;AAGhE,QAAI,KAAK,OAAO,GAAI,MAAK,KAAK,MAAM;AAAA,EACtC,QAAQ;AAAA,EAER;AACA,gBAAc,SAAS,iBAAiB,WAAW,EAAE,EAAE,IAAI,CAAC;AAC9D;AAeO,SAAS,mBACd,YACA,UACA;AACA,QAAM,aAAa,OAAqB;AAAA,IACtC;AAAA,MACE;AAAA,QACE,CAAC,SAAS;AAAA,UACR,IAAI,eAAe;AAAA,UACnB,OAAO,CAAC,OAAO,IAAI,EAAE,GAAG,CAAC;AAAA,QAC3B;AAAA,QACA,EAAE,MAAM,YAAY,SAAS,EAAE;AAAA,MACjC;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAMA,WAAS,kBAAwB;AAC/B,UAAM,KAAK,WAAW,CAAC,MAAM,EAAE,EAAE;AACjC,UAAM,OAAO,SAAS,CAAC,MAAM,EAAE,IAAI;AACnC,cAAU,MAAM;AACd,oBAAc,SAAS,iBAAiB,WAAW,EAAE,EAAE,IAAI,CAAC;AAAA,IAC9D,GAAG,CAAC,IAAI,IAAI,CAAC;AAAA,EACf;AAKA,WAAS,oBAA8B;AACrC,UAAM,KAAK,WAAW,CAAC,MAAM,EAAE,EAAE;AACjC,UAAM,OAAO,SAAS,CAAC,MAAM,EAAE,IAAI;AACnC,WAAO,WAAW,EAAE,EAAE,IAAI;AAAA,EAC5B;AAGA,WAAS,cAAwB;AAC/B,WAAO,kBAAkB,EAAE;AAAA,EAC7B;AAGA,WAAS,eAA0B;AACjC,WAAO,kBAAkB,EAAE;AAAA,EAC7B;AAEA,SAAO,EAAE,YAAY,iBAAiB,mBAAmB,aAAa,aAAa;AACrF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/theme/theme-store.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { create } from \"zustand\";\nimport { persist } from \"zustand/middleware\";\nimport { logger } from \"../lib/logger\";\n\nexport type ThemeMode = \"light\" | \"dark\";\n/** What the user chose; \"system\" tracks the OS `prefers-color-scheme`. */\nexport type ThemePreference = \"light\" | \"dark\" | \"system\";\n\nexport interface ThemeState {\n /** The user's choice (persisted). */\n preference: ThemePreference;\n /** The RESOLVED light/dark actually in effect (system → OS). Read this for\n * anything that needs the concrete theme (e.g. token selection, toasts). */\n mode: ThemeMode;\n setPreference: (preference: ThemePreference) => void;\n /** Set an explicit light/dark choice (preference follows). */\n setMode: (mode: ThemeMode) => void;\n /** Flip the effective theme to the opposite explicit choice. */\n toggle: () => void;\n}\n\nfunction systemPreference(): ThemeMode {\n try {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n } catch {\n return \"light\";\n }\n}\n\nfunction resolve(preference: ThemePreference): ThemeMode {\n return preference === \"system\" ? systemPreference() : preference;\n}\n\n/** Normalize a persisted theme-state blob to a valid preference. Older blobs\n * persisted the concrete `mode` (light/dark); carry that over as an explicit\n * preference so the choice survives. Shared by the store's `migrate` and the\n * pre-hydration {@link applyPersistedTheme}. */\nfunction preferenceFromState(\n s: { mode?: string; preference?: string } | null | undefined,\n): ThemePreference {\n if (s?.preference === \"light\" || s?.preference === \"dark\" || s?.preference === \"system\") {\n return s.preference;\n }\n if (s?.mode === \"light\" || s?.mode === \"dark\") return s.mode;\n return \"system\";\n}\n\n/** Toggle the `.dark` class on <html> for a resolved light/dark mode. */\nfunction applyThemeClass(mode: ThemeMode): void {\n const root = document.documentElement;\n if (mode === \"dark\") root.classList.add(\"dark\");\n else root.classList.remove(\"dark\");\n}\n\n/**\n * Pre-hydration: read the theme PREFERENCE persisted under `storageKey` straight\n * from localStorage, resolve it to a concrete light/dark mode, and toggle the\n * `.dark` class on <html> — BEFORE React and the store mount, so the first paint\n * already has the right theme (no flash). Returns the resolved mode so the caller\n * can chain {@link applyPersistedPalette}. Encapsulates the zustand-persist\n * envelope + the legacy `mode`→`preference` fallback so apps never re-type the\n * storage shape in their entrypoint.\n */\nexport function applyPersistedTheme(storageKey: string): ThemeMode {\n let preference: ThemePreference = \"system\";\n try {\n const blob = JSON.parse(localStorage.getItem(storageKey) ?? \"{}\") as {\n state?: { mode?: string; preference?: string };\n };\n preference = preferenceFromState(blob.state);\n } catch {\n /* malformed storage → system */\n }\n const mode = resolve(preference);\n applyThemeClass(mode);\n return mode;\n}\n\n/**\n * Build a theme store bound to a consumer-supplied localStorage key. Tracks a\n * light/dark/system PREFERENCE (feedback #330) and exposes the resolved `mode`.\n * Returns the `useTheme` hook plus a `useApplyTheme` effect that toggles the\n * `.dark` class on <html> and, while on \"system\", follows OS theme changes live.\n */\nexport function createThemeStore(storageKey: string) {\n const useTheme = create<ThemeState>()(\n logger(\n persist(\n (set, get) => ({\n preference: \"system\" as ThemePreference,\n mode: systemPreference(),\n setPreference: (preference) => set({ preference, mode: resolve(preference) }),\n setMode: (mode) => set({ preference: mode, mode }),\n toggle: () => {\n const next: ThemeMode = get().mode === \"dark\" ? \"light\" : \"dark\";\n set({ preference: next, mode: next });\n },\n }),\n {\n name: storageKey,\n version: 2,\n // Only the preference is persisted; `mode` is derived on load.\n partialize: (s) => ({ preference: s.preference }),\n migrate: (persistedState): { preference: ThemePreference } => ({\n preference: preferenceFromState(\n persistedState as { mode?: string; preference?: string } | null,\n ),\n }),\n onRehydrateStorage: () => (state) => {\n // The persisted preference decides the concrete mode at load.\n if (state) state.mode = resolve(state.preference);\n },\n },\n ),\n \"theme\",\n ),\n );\n\n function useApplyTheme(): void {\n const mode = useTheme((s) => s.mode);\n const preference = useTheme((s) => s.preference);\n useEffect(() => {\n applyThemeClass(mode);\n }, [mode]);\n // While tracking the system, follow OS theme changes live.\n useEffect(() => {\n if (preference !== \"system\") return;\n let mq: MediaQueryList;\n try {\n mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n } catch {\n return;\n }\n const onChange = () => useTheme.setState({ mode: mq.matches ? \"dark\" : \"light\" });\n onChange();\n mq.addEventListener?.(\"change\", onChange);\n return () => mq.removeEventListener?.(\"change\", onChange);\n }, [preference]);\n }\n\n return { useTheme, useApplyTheme };\n}\n\nexport type ThemeStore = ReturnType<typeof createThemeStore>;\n"],"mappings":"AAAA,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,cAAc;AAmBvB,SAAS,mBAA8B;AACrC,MAAI;AACF,WAAO,OAAO,WAAW,8BAA8B,EAAE,UAAU,SAAS;AAAA,EAC9E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,YAAwC;AACvD,SAAO,eAAe,WAAW,iBAAiB,IAAI;AACxD;AAMA,SAAS,oBACP,GACiB;AACjB,MAAI,GAAG,eAAe,WAAW,GAAG,eAAe,UAAU,GAAG,eAAe,UAAU;AACvF,WAAO,EAAE;AAAA,EACX;AACA,MAAI,GAAG,SAAS,WAAW,GAAG,SAAS,OAAQ,QAAO,EAAE;AACxD,SAAO;AACT;AAGA,SAAS,gBAAgB,MAAuB;AAC9C,QAAM,OAAO,SAAS;AACtB,MAAI,SAAS,OAAQ,MAAK,UAAU,IAAI,MAAM;AAAA,MACzC,MAAK,UAAU,OAAO,MAAM;AACnC;AAWO,SAAS,oBAAoB,YAA+B;AACjE,MAAI,aAA8B;AAClC,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,aAAa,QAAQ,UAAU,KAAK,IAAI;AAGhE,iBAAa,oBAAoB,KAAK,KAAK;AAAA,EAC7C,QAAQ;AAAA,EAER;AACA,QAAM,OAAO,QAAQ,UAAU;AAC/B,kBAAgB,IAAI;AACpB,SAAO;AACT;AAQO,SAAS,iBAAiB,YAAoB;AACnD,QAAM,WAAW,OAAmB;AAAA,IAClC;AAAA,MACE;AAAA,QACE,CAAC,KAAK,SAAS;AAAA,UACb,YAAY;AAAA,UACZ,MAAM,iBAAiB;AAAA,UACvB,eAAe,CAAC,eAAe,IAAI,EAAE,YAAY,MAAM,QAAQ,UAAU,EAAE,CAAC;AAAA,UAC5E,SAAS,CAAC,SAAS,IAAI,EAAE,YAAY,MAAM,KAAK,CAAC;AAAA,UACjD,QAAQ,MAAM;AACZ,kBAAM,OAAkB,IAAI,EAAE,SAAS,SAAS,UAAU;AAC1D,gBAAI,EAAE,YAAY,MAAM,MAAM,KAAK,CAAC;AAAA,UACtC;AAAA,QACF;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA;AAAA,UAET,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,WAAW;AAAA,UAC/C,SAAS,CAAC,oBAAqD;AAAA,YAC7D,YAAY;AAAA,cACV;AAAA,YACF;AAAA,UACF;AAAA,UACA,oBAAoB,MAAM,CAAC,UAAU;AAEnC,gBAAI,MAAO,OAAM,OAAO,QAAQ,MAAM,UAAU;AAAA,UAClD;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,WAAS,gBAAsB;AAC7B,UAAM,OAAO,SAAS,CAAC,MAAM,EAAE,IAAI;AACnC,UAAM,aAAa,SAAS,CAAC,MAAM,EAAE,UAAU;AAC/C,cAAU,MAAM;AACd,sBAAgB,IAAI;AAAA,IACtB,GAAG,CAAC,IAAI,CAAC;AAET,cAAU,MAAM;AACd,UAAI,eAAe,SAAU;AAC7B,UAAI;AACJ,UAAI;AACF,aAAK,OAAO,WAAW,8BAA8B;AAAA,MACvD,QAAQ;AACN;AAAA,MACF;AACA,YAAM,WAAW,MAAM,SAAS,SAAS,EAAE,MAAM,GAAG,UAAU,SAAS,QAAQ,CAAC;AAChF,eAAS;AACT,SAAG,mBAAmB,UAAU,QAAQ;AACxC,aAAO,MAAM,GAAG,sBAAsB,UAAU,QAAQ;AAAA,IAC1D,GAAG,CAAC,UAAU,CAAC;AAAA,EACjB;AAEA,SAAO,EAAE,UAAU,cAAc;AACnC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/theme/theme-store.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { create } from \"zustand\";\nimport { persist } from \"zustand/middleware\";\nimport { logger } from \"../lib/logger\";\n\nexport type ThemeMode = \"light\" | \"dark\";\n/** What the user chose; \"system\" tracks the OS `prefers-color-scheme`. */\nexport type ThemePreference = \"light\" | \"dark\" | \"system\";\n\nexport interface ThemeState {\n /** The user's choice (persisted). */\n preference: ThemePreference;\n /** The RESOLVED light/dark actually in effect (system → OS). Read this for\n * anything that needs the concrete theme (e.g. token selection, toasts). */\n mode: ThemeMode;\n setPreference: (preference: ThemePreference) => void;\n /** Set an explicit light/dark choice (preference follows). */\n setMode: (mode: ThemeMode) => void;\n /** Flip the effective theme to the opposite explicit choice. */\n toggle: () => void;\n}\n\nfunction systemPreference(): ThemeMode {\n try {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n } catch {\n return \"light\";\n }\n}\n\nfunction resolve(preference: ThemePreference): ThemeMode {\n return preference === \"system\" ? systemPreference() : preference;\n}\n\n/** Normalize a persisted theme-state blob to a valid preference. Older blobs\n * persisted the concrete `mode` (light/dark); carry that over as an explicit\n * preference so the choice survives. Shared by the store's `migrate` and the\n * pre-hydration {@link applyPersistedTheme}. */\nfunction preferenceFromState(\n s: { mode?: string; preference?: string } | null | undefined,\n): ThemePreference {\n if (s?.preference === \"light\" || s?.preference === \"dark\" || s?.preference === \"system\") {\n return s.preference;\n }\n if (s?.mode === \"light\" || s?.mode === \"dark\") return s.mode;\n return \"system\";\n}\n\n/** Toggle the `.dark` class on <html> for a resolved light/dark mode. */\nfunction applyThemeClass(mode: ThemeMode): void {\n const root = document.documentElement;\n if (mode === \"dark\") root.classList.add(\"dark\");\n else root.classList.remove(\"dark\");\n}\n\n/**\n * Pre-hydration: read the theme PREFERENCE persisted under `storageKey` straight\n * from localStorage, resolve it to a concrete light/dark mode, and toggle the\n * `.dark` class on <html> — BEFORE React and the store mount, so the first paint\n * already has the right theme (no flash). Returns the resolved mode so the caller\n * can chain {@link applyPersistedPalette}. Encapsulates the zustand-persist\n * envelope + the legacy `mode`→`preference` fallback so apps never re-type the\n * storage shape in their entrypoint.\n */\nexport function applyPersistedTheme(storageKey: string): ThemeMode {\n let preference: ThemePreference = \"system\";\n try {\n const blob = JSON.parse(localStorage.getItem(storageKey) ?? \"{}\") as {\n state?: { mode?: string; preference?: string };\n };\n preference = preferenceFromState(blob.state);\n } catch {\n /* malformed storage → system */\n }\n const mode = resolve(preference);\n applyThemeClass(mode);\n return mode;\n}\n\n/**\n * Build a theme store bound to a consumer-supplied localStorage key. Tracks a\n * light/dark/system PREFERENCE (feedback #330) and exposes the resolved `mode`.\n * Returns the `useTheme` hook plus a `useApplyTheme` effect that toggles the\n * `.dark` class on <html> and, while on \"system\", follows OS theme changes live.\n */\nexport function createThemeStore(storageKey: string) {\n const useTheme = create<ThemeState>()(\n logger(\n persist(\n (set, get) => ({\n preference: \"system\" as ThemePreference,\n mode: systemPreference(),\n setPreference: (preference) => set({ preference, mode: resolve(preference) }),\n setMode: (mode) => set({ preference: mode, mode }),\n toggle: () => {\n const next: ThemeMode = get().mode === \"dark\" ? \"light\" : \"dark\";\n set({ preference: next, mode: next });\n },\n }),\n {\n name: storageKey,\n version: 2,\n // Only the preference is persisted; `mode` is derived on load.\n partialize: (s) => ({ preference: s.preference }),\n migrate: (persistedState): { preference: ThemePreference } => ({\n preference: preferenceFromState(\n persistedState as { mode?: string; preference?: string } | null,\n ),\n }),\n onRehydrateStorage: () => (state) => {\n // The persisted preference decides the concrete mode at load.\n if (state) state.mode = resolve(state.preference);\n },\n },\n ),\n \"theme\",\n ),\n );\n\n function useApplyTheme(): void {\n const mode = useTheme((s) => s.mode);\n const preference = useTheme((s) => s.preference);\n useEffect(() => {\n applyThemeClass(mode);\n }, [mode]);\n // While tracking the system, follow OS theme changes live.\n useEffect(() => {\n if (preference !== \"system\") return;\n let mq: MediaQueryList;\n try {\n mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n } catch {\n return;\n }\n const onChange = () => useTheme.setState({ mode: mq.matches ? \"dark\" : \"light\" });\n onChange();\n mq.addEventListener?.(\"change\", onChange);\n return () => mq.removeEventListener?.(\"change\", onChange);\n }, [preference]);\n }\n\n return { useTheme, useApplyTheme };\n}\n\nexport type ThemeStore = ReturnType<typeof createThemeStore>;\n"],"mappings":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,cAAc;AAmBvB,SAAS,mBAA8B;AACrC,MAAI;AACF,WAAO,OAAO,WAAW,8BAA8B,EAAE,UAAU,SAAS;AAAA,EAC9E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,YAAwC;AACvD,SAAO,eAAe,WAAW,iBAAiB,IAAI;AACxD;AAMA,SAAS,oBACP,GACiB;AACjB,MAAI,GAAG,eAAe,WAAW,GAAG,eAAe,UAAU,GAAG,eAAe,UAAU;AACvF,WAAO,EAAE;AAAA,EACX;AACA,MAAI,GAAG,SAAS,WAAW,GAAG,SAAS,OAAQ,QAAO,EAAE;AACxD,SAAO;AACT;AAGA,SAAS,gBAAgB,MAAuB;AAC9C,QAAM,OAAO,SAAS;AACtB,MAAI,SAAS,OAAQ,MAAK,UAAU,IAAI,MAAM;AAAA,MACzC,MAAK,UAAU,OAAO,MAAM;AACnC;AAWO,SAAS,oBAAoB,YAA+B;AACjE,MAAI,aAA8B;AAClC,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,aAAa,QAAQ,UAAU,KAAK,IAAI;AAGhE,iBAAa,oBAAoB,KAAK,KAAK;AAAA,EAC7C,QAAQ;AAAA,EAER;AACA,QAAM,OAAO,QAAQ,UAAU;AAC/B,kBAAgB,IAAI;AACpB,SAAO;AACT;AAQO,SAAS,iBAAiB,YAAoB;AACnD,QAAM,WAAW,OAAmB;AAAA,IAClC;AAAA,MACE;AAAA,QACE,CAAC,KAAK,SAAS;AAAA,UACb,YAAY;AAAA,UACZ,MAAM,iBAAiB;AAAA,UACvB,eAAe,CAAC,eAAe,IAAI,EAAE,YAAY,MAAM,QAAQ,UAAU,EAAE,CAAC;AAAA,UAC5E,SAAS,CAAC,SAAS,IAAI,EAAE,YAAY,MAAM,KAAK,CAAC;AAAA,UACjD,QAAQ,MAAM;AACZ,kBAAM,OAAkB,IAAI,EAAE,SAAS,SAAS,UAAU;AAC1D,gBAAI,EAAE,YAAY,MAAM,MAAM,KAAK,CAAC;AAAA,UACtC;AAAA,QACF;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA;AAAA,UAET,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,WAAW;AAAA,UAC/C,SAAS,CAAC,oBAAqD;AAAA,YAC7D,YAAY;AAAA,cACV;AAAA,YACF;AAAA,UACF;AAAA,UACA,oBAAoB,MAAM,CAAC,UAAU;AAEnC,gBAAI,MAAO,OAAM,OAAO,QAAQ,MAAM,UAAU;AAAA,UAClD;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,WAAS,gBAAsB;AAC7B,UAAM,OAAO,SAAS,CAAC,MAAM,EAAE,IAAI;AACnC,UAAM,aAAa,SAAS,CAAC,MAAM,EAAE,UAAU;AAC/C,cAAU,MAAM;AACd,sBAAgB,IAAI;AAAA,IACtB,GAAG,CAAC,IAAI,CAAC;AAET,cAAU,MAAM;AACd,UAAI,eAAe,SAAU;AAC7B,UAAI;AACJ,UAAI;AACF,aAAK,OAAO,WAAW,8BAA8B;AAAA,MACvD,QAAQ;AACN;AAAA,MACF;AACA,YAAM,WAAW,MAAM,SAAS,SAAS,EAAE,MAAM,GAAG,UAAU,SAAS,QAAQ,CAAC;AAChF,eAAS;AACT,SAAG,mBAAmB,UAAU,QAAQ;AACxC,aAAO,MAAM,GAAG,sBAAsB,UAAU,QAAQ;AAAA,IAC1D,GAAG,CAAC,UAAU,CAAC;AAAA,EACjB;AAEA,SAAO,EAAE,UAAU,cAAc;AACnC;","names":[]}
|
package/dist/tour/tour.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ interface TourLabels {
|
|
|
36
36
|
/** Step counter, e.g. (2, 7) => "2 / 7". */
|
|
37
37
|
step: (current: number, total: number) => string;
|
|
38
38
|
}
|
|
39
|
+
declare const DEFAULT_TOUR_LABELS: TourLabels;
|
|
39
40
|
interface StartOptions {
|
|
40
41
|
/** Stable id of the tour, surfaced as `activeId` so the app can mirror it to
|
|
41
42
|
* the URL (deep-link / evaluation tracking) and record completion. */
|
|
@@ -101,4 +102,4 @@ declare function TourProvider({ children, labels, }: {
|
|
|
101
102
|
labels?: Partial<TourLabels>;
|
|
102
103
|
}): react.JSX.Element;
|
|
103
104
|
|
|
104
|
-
export { type TourLabels, type TourPlacement, TourProvider, type TourStep, useTour, useTourOptional };
|
|
105
|
+
export { DEFAULT_TOUR_LABELS, type TourLabels, type TourPlacement, TourProvider, type TourStep, useTour, useTourOptional };
|