@trops/dash-core 0.1.574 → 0.1.575

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -2,9 +2,9 @@ import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
2
2
  import _typeof from '@babel/runtime/helpers/typeof';
3
3
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
4
4
  import * as DashReact from '@trops/dash-react';
5
- import { isObject, ThemeContext, Modal, FontAwesomeIcon, deepCopy, MainSection, getUUID, getStylesForItem, themeObjects, Heading, SearchInput, ButtonIcon, SubHeading3, InputText, Button, Tag, Button3, Button2, Paragraph, Sidebar, Panel, Stepper, Tag3, Card2, Card3, Heading3, MenuItem3, FormLabel, SelectMenu, Switch, SelectInput, CodeEditorVS, SettingsModal, SubHeading2, tailwindHeightFractions, Menu3, Panel3, ButtonIcon2, DropdownPanel, MenuItem2, DragComponent, ConfirmationModal, DropComponent, getStyleName, capitalizeFirstLetter, isHexColor, deriveShades, colorTypes, TAILWIND_PALETTE, Toggle, normalizeHex, contrastRatio, Caption2, Caption, getColorFamilies, getCuratedColorGrid, DashPanel, themeVariants, SubHeading, Heading2, Paragraph2, Paragraph3, ButtonIcon3, Tag2, Alert, Alert2, Alert3, Toast, Toast2, Toast3, ProgressBar, ProgressBar2, ProgressBar3, MenuItem, Breadcrumbs, Breadcrumbs2, Breadcrumbs3, Caption3, Code, Code2, Code3, Divider, Divider2, Divider3, DashPanel2, DashPanel3, Tabs, Tabs2, Tabs3, Accordion, Accordion2, Accordion3, Toggle2, Toggle3, TextArea, Checkbox, Slider, RadioGroup, AlertBanner, StatusBadge, Skeleton, StatCard, DataList, EmptyState, Card, Panel2, ThemeFromUrlPane, CodeEditorInline, Icon2, AlgoliaSearchBox, CommandPalette, useSidebar, Navbar, withRouter, Menu as Menu$1 } from '@trops/dash-react';
5
+ import { isObject, ThemeContext, Modal, FontAwesomeIcon, deepCopy, MainSection, getUUID, getStylesForItem, themeObjects, Heading, SearchInput, ButtonIcon, SubHeading3, InputText, Button, Tag, Button3, Button2, Paragraph, Sidebar, Panel, Stepper, Tag3, Card2, Card3, Heading3, MenuItem3, FormLabel, SelectMenu, Switch, SelectInput, CodeEditorVS, SettingsModal, SubHeading2, tailwindHeightFractions, Menu3, Panel3, ButtonIcon2, DropdownPanel, MenuItem2, DragComponent, ConfirmationModal, DropComponent, getStyleName, capitalizeFirstLetter, isHexColor, deriveShades, colorTypes, TAILWIND_PALETTE, Toggle, normalizeHex, contrastRatio, Caption2, Caption, getColorFamilies, getCuratedColorGrid, DashPanel, themeVariants, SubHeading, Heading2, Paragraph2, Paragraph3, ButtonIcon3, Tag2, Alert, Alert2, Alert3, Toast, Toast2, Toast3, ProgressBar, ProgressBar2, ProgressBar3, MenuItem, Breadcrumbs, Breadcrumbs2, Breadcrumbs3, Caption3, Code, Code2, Code3, Divider, Divider2, Divider3, DashPanel2, DashPanel3, Tabs, Tabs2, Tabs3, Accordion, Accordion2, Accordion3, Toggle2, Toggle3, TextArea, Checkbox, Slider, RadioGroup, AlertBanner, StatusBadge, Skeleton, StatCard, DataList, EmptyState, Card, Panel2, hexToRgb, rgbToHsl, rgbToHex, hslToRgb, ThemeFromUrlPane, CodeEditorInline, Icon2, AlgoliaSearchBox, CommandPalette, useSidebar, Navbar, withRouter, Menu as Menu$1 } from '@trops/dash-react';
6
6
  export * from '@trops/dash-react';
7
- export { ThemeContext } from '@trops/dash-react';
7
+ export { FontAwesomeIcon, ThemeContext } from '@trops/dash-react';
8
8
  import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
9
9
  import * as React from 'react';
10
10
  import React__default, { createContext, useContext, useState, useEffect, useMemo, useCallback, useRef, Fragment as Fragment$1, useReducer, Component, memo, Profiler } from 'react';
@@ -34658,11 +34658,8 @@ var ThemePickerGridPane = function ThemePickerGridPane(_ref) {
34658
34658
  }
34659
34659
  return /*#__PURE__*/jsx(ThemePane, {
34660
34660
  children: /*#__PURE__*/jsx("div", {
34661
- className: "flex flex-row rounded overflow-clip justify-center items-center align-center w-full",
34662
- children: /*#__PURE__*/jsx("div", {
34663
- className: "grid grid-cols-3 gap-4 w-full h-full overflow-y-scroll scrollbar scrollbar-thumb-gray-700 scrollbar-thin scrollbar-track-transparent",
34664
- children: renderCurrentThemes()
34665
- })
34661
+ className: "grid grid-cols-3 gap-4 w-full flex-1 min-h-0 overflow-y-auto pr-2 scrollbar scrollbar-thumb-gray-700 scrollbar-thin scrollbar-track-transparent",
34662
+ children: renderCurrentThemes()
34666
34663
  })
34667
34664
  });
34668
34665
  };
@@ -36906,12 +36903,85 @@ var PanelThemeStudio = function PanelThemeStudio(_ref) {
36906
36903
  });
36907
36904
  };
36908
36905
 
36909
- /**
36910
- * themeGenerator.js
36911
- *
36912
- * Utility for generating themes via presets, random generation,
36913
- * and color-harmony-based generation from a user-selected base color.
36914
- */
36906
+ // Color harmony helpers — inlined so dash-core can ship the new
36907
+ // "From Colors" generator without first publishing matching
36908
+ // exports from @trops/dash-react. Same math as the planned dash-react
36909
+ // counterparts; can be replaced with a re-export once both ship in
36910
+ // the same cycle.
36911
+ function rotateHex(hex, deltaH) {
36912
+ var rgb = hexToRgb(hex);
36913
+ if (!rgb) return null;
36914
+ var _rgbToHsl = rgbToHsl(rgb),
36915
+ h = _rgbToHsl.h,
36916
+ s = _rgbToHsl.s,
36917
+ l = _rgbToHsl.l;
36918
+ var newH = ((h + deltaH) % 360 + 360) % 360;
36919
+ return rgbToHex(hslToRgb({
36920
+ h: newH,
36921
+ s: s,
36922
+ l: l
36923
+ }));
36924
+ }
36925
+ function complementHex(hex) {
36926
+ return rotateHex(hex, 180);
36927
+ }
36928
+ function analogousHexes(hex) {
36929
+ var a = rotateHex(hex, -30);
36930
+ var b = rotateHex(hex, 30);
36931
+ return a && b ? [a, b] : null;
36932
+ }
36933
+ function triadicHexes(hex) {
36934
+ var a = rotateHex(hex, 120);
36935
+ var b = rotateHex(hex, 240);
36936
+ return a && b ? [a, b] : null;
36937
+ }
36938
+ function splitComplementaryHexes(hex) {
36939
+ var a = rotateHex(hex, 150);
36940
+ var b = rotateHex(hex, 210);
36941
+ return a && b ? [a, b] : null;
36942
+ }
36943
+ function tetradicHexes(hex) {
36944
+ var a = rotateHex(hex, 90);
36945
+ var b = rotateHex(hex, 180);
36946
+ var c = rotateHex(hex, 270);
36947
+ return a && b && c ? [a, b, c] : null;
36948
+ }
36949
+ function monochromaticHexes(hex) {
36950
+ var count = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
36951
+ var rgb = hexToRgb(hex);
36952
+ if (!rgb) return null;
36953
+ var _rgbToHsl2 = rgbToHsl(rgb),
36954
+ h = _rgbToHsl2.h,
36955
+ s = _rgbToHsl2.s;
36956
+ var out = [];
36957
+ for (var i = 0; i < count; i++) {
36958
+ var t = count === 1 ? 0.5 : i / (count - 1);
36959
+ var l = 0.15 + t * 0.7;
36960
+ var sAdj = s * (1 - 0.3 * Math.abs(t - 0.5) * 2);
36961
+ out.push(rgbToHex(hslToRgb({
36962
+ h: h,
36963
+ s: sAdj,
36964
+ l: l
36965
+ })));
36966
+ }
36967
+ return out;
36968
+ }
36969
+ function adjustHsl(hex) {
36970
+ var dH = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
36971
+ var dS = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
36972
+ var dL = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
36973
+ var rgb = hexToRgb(hex);
36974
+ if (!rgb) return null;
36975
+ var hsl = rgbToHsl(rgb);
36976
+ var h = ((hsl.h + dH) % 360 + 360) % 360;
36977
+ var s = Math.max(0, Math.min(1, hsl.s + dS));
36978
+ var l = Math.max(0, Math.min(1, hsl.l + dL));
36979
+ return rgbToHex(hslToRgb({
36980
+ h: h,
36981
+ s: s,
36982
+ l: l
36983
+ }));
36984
+ }
36915
36985
 
36916
36986
  // Color wheel mapping for Tailwind palettes (approximate hue angles)
36917
36987
  var COLOR_WHEEL = {
@@ -37079,11 +37149,22 @@ function generateRandomTheme() {
37079
37149
 
37080
37150
  /**
37081
37151
  * Generate a theme using color harmony rules from a base color.
37082
- * @param {string} baseColor - A Tailwind color name (e.g. "blue")
37083
- * @param {"complementary"|"analogous"|"triadic"|"split-complementary"} strategy
37152
+ * Accepts either a Tailwind color name OR a hex string. Hex bases
37153
+ * run through dash-react's harmony math and emit hex channels for
37154
+ * every output; named bases use the legacy COLOR_WHEEL mapping.
37155
+ *
37156
+ * @param {string} baseColor - Tailwind color name (e.g. "blue") OR hex
37157
+ * @param {"complementary"|"analogous"|"triadic"|"split-complementary"|"tetradic"|"monochromatic"} strategy
37158
+ * @param {{ dH?: number, dS?: number, dL?: number }} [hslNudge] - optional
37159
+ * HSL offsets applied to every generated channel after the harmony
37160
+ * math. dH in degrees; dS/dL as fractions in [-1..1].
37084
37161
  */
37085
37162
  function generateHarmonyTheme(baseColor) {
37086
37163
  var strategy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "complementary";
37164
+ var hslNudge = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
37165
+ if (typeof baseColor === "string" && isHexColor(baseColor)) {
37166
+ return generateHexHarmonyTheme(baseColor, strategy, hslNudge);
37167
+ }
37087
37168
  var baseHue = COLOR_WHEEL[baseColor];
37088
37169
  if (baseHue === undefined) {
37089
37170
  return generateRandomTheme();
@@ -37119,17 +37200,114 @@ function generateHarmonyTheme(baseColor) {
37119
37200
  }
37120
37201
 
37121
37202
  /**
37122
- * Generate a theme from three independently chosen colors.
37123
- * @param {string} primary - A Tailwind color name
37124
- * @param {string} secondary - A Tailwind color name
37125
- * @param {string} tertiary - A Tailwind color name
37203
+ * Hex-mode harmony generation. Always emits hex channels; supports
37204
+ * all the named harmony strategies plus Monochromatic and Tetradic.
37205
+ * The neutral channel is derived from the base by desaturating
37206
+ * almost fully, keeping the same hue family for a cohesive feel.
37126
37207
  */
37127
- function generateCustomTheme(primary, secondary, tertiary) {
37128
- var neutral = pick(NEUTRAL_COLORS);
37208
+ function generateHexHarmonyTheme(baseHex, strategy, hslNudge) {
37209
+ var base = normalizeHex(baseHex);
37210
+ if (!base) return generateRandomTheme();
37211
+ var primary = base;
37212
+ var secondary;
37213
+ var tertiary;
37214
+ var neutral;
37215
+ switch (strategy) {
37216
+ case "complementary":
37217
+ {
37218
+ secondary = complementHex(base);
37219
+ var ana = analogousHexes(base);
37220
+ tertiary = ana ? ana[1] : base;
37221
+ break;
37222
+ }
37223
+ case "analogous":
37224
+ {
37225
+ var _ana = analogousHexes(base);
37226
+ if (_ana) {
37227
+ secondary = _ana[0];
37228
+ tertiary = _ana[1];
37229
+ }
37230
+ break;
37231
+ }
37232
+ case "triadic":
37233
+ {
37234
+ var tri = triadicHexes(base);
37235
+ if (tri) {
37236
+ secondary = tri[0];
37237
+ tertiary = tri[1];
37238
+ }
37239
+ break;
37240
+ }
37241
+ case "split-complementary":
37242
+ {
37243
+ var split = splitComplementaryHexes(base);
37244
+ if (split) {
37245
+ secondary = split[0];
37246
+ tertiary = split[1];
37247
+ }
37248
+ break;
37249
+ }
37250
+ case "tetradic":
37251
+ {
37252
+ var tet = tetradicHexes(base);
37253
+ if (tet) {
37254
+ secondary = tet[0];
37255
+ tertiary = tet[1];
37256
+ neutral = tet[2];
37257
+ }
37258
+ break;
37259
+ }
37260
+ case "monochromatic":
37261
+ {
37262
+ var mono = monochromaticHexes(base, 3);
37263
+ if (mono) {
37264
+ primary = mono[1];
37265
+ secondary = mono[0];
37266
+ tertiary = mono[2];
37267
+ }
37268
+ break;
37269
+ }
37270
+ default:
37271
+ {
37272
+ secondary = complementHex(base);
37273
+ var _ana2 = analogousHexes(base);
37274
+ tertiary = _ana2 ? _ana2[1] : base;
37275
+ }
37276
+ }
37277
+
37278
+ // Neutral defaults to a near-grey of the same hue (desaturated).
37279
+ if (!neutral) {
37280
+ neutral = adjustHsl(base, 0, -0.85, 0);
37281
+ }
37282
+
37283
+ // Apply optional HSL nudge uniformly to all channels.
37284
+ if (hslNudge) {
37285
+ var _hslNudge$dH = hslNudge.dH,
37286
+ dH = _hslNudge$dH === void 0 ? 0 : _hslNudge$dH,
37287
+ _hslNudge$dS = hslNudge.dS,
37288
+ dS = _hslNudge$dS === void 0 ? 0 : _hslNudge$dS,
37289
+ _hslNudge$dL = hslNudge.dL,
37290
+ dL = _hslNudge$dL === void 0 ? 0 : _hslNudge$dL;
37291
+ primary = adjustHsl(primary, dH, dS, dL) || primary;
37292
+ secondary = adjustHsl(secondary, dH, dS, dL) || secondary;
37293
+ tertiary = adjustHsl(tertiary, dH, dS, dL) || tertiary;
37294
+ neutral = adjustHsl(neutral, dH, dS, dL) || neutral;
37295
+ }
37129
37296
  var name = generateThemeName(primary, secondary);
37130
37297
  return buildRawTheme(name, primary, secondary, tertiary, neutral);
37131
37298
  }
37132
37299
 
37300
+ /**
37301
+ * Generate a theme from four independently chosen channel values.
37302
+ * Each channel may be a Tailwind name or a hex string.
37303
+ */
37304
+ function generateCustomTheme(primary, secondary, tertiary) {
37305
+ var neutral = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
37306
+ var resolvedNeutral = neutral || pick(NEUTRAL_COLORS);
37307
+ var name = generateThemeName(primary, secondary);
37308
+ return buildRawTheme(name, primary, secondary, tertiary, resolvedNeutral);
37309
+ }
37310
+
37133
37311
  /**
37134
37312
  * All available chromatic colors for the "From Color" picker.
37135
37313
  */
@@ -37150,6 +37328,12 @@ var HARMONY_STRATEGIES = [{
37150
37328
  }, {
37151
37329
  value: "split-complementary",
37152
37330
  label: "Split Complementary"
37331
+ }, {
37332
+ value: "tetradic",
37333
+ label: "Tetradic"
37334
+ }, {
37335
+ value: "monochromatic",
37336
+ label: "Monochromatic"
37153
37337
  }, {
37154
37338
  value: "custom",
37155
37339
  label: "Custom"
@@ -37248,87 +37432,165 @@ var PresetGallery = function PresetGallery(_ref3) {
37248
37432
 
37249
37433
  // ─── Color Harmony Picker ────────────────────────────────────────────────
37250
37434
 
37251
- var ColorSwatchGrid$1 = function ColorSwatchGrid(_ref4) {
37435
+ /**
37436
+ * Render a swatch for a generated palette channel. Hex values render
37437
+ * inline; named Tailwind families fall back to `bg-{name}-500` which
37438
+ * is in the safelist.
37439
+ */
37440
+ var PaletteSwatch = function PaletteSwatch(_ref4) {
37252
37441
  var value = _ref4.value,
37253
- onChange = _ref4.onChange,
37254
37442
  label = _ref4.label;
37443
+ var isHex = isHexColor(value);
37255
37444
  return /*#__PURE__*/jsxs("div", {
37256
- className: "flex flex-col gap-2",
37257
- children: [/*#__PURE__*/jsx("span", {
37258
- className: "text-xs opacity-50",
37445
+ className: "flex flex-col items-center gap-1 flex-1",
37446
+ children: [/*#__PURE__*/jsx("div", {
37447
+ className: "h-10 w-full rounded ".concat(isHex ? "" : "bg-".concat(value, "-500")),
37448
+ style: isHex ? {
37449
+ backgroundColor: value
37450
+ } : undefined
37451
+ }), /*#__PURE__*/jsx("span", {
37452
+ className: "text-xs opacity-60 truncate w-full text-center",
37259
37453
  children: label
37454
+ })]
37455
+ });
37456
+ };
37457
+
37458
+ /**
37459
+ * Compact hex picker reused inline by the wizard. Category chips +
37460
+ * a curated grid of swatches + a custom-hex input below. Same
37461
+ * primitives as ChannelEditorModal but laid out for a narrower
37462
+ * column.
37463
+ */
37464
+ var InlineHexPicker = function InlineHexPicker(_ref5) {
37465
+ var selectedHex = _ref5.selectedHex,
37466
+ onPick = _ref5.onPick;
37467
+ var families = getColorFamilies();
37468
+ var _useState = useState(families[0]),
37469
+ _useState2 = _slicedToArray(_useState, 2),
37470
+ activeFamily = _useState2[0],
37471
+ setActiveFamily = _useState2[1];
37472
+ var swatches = getCuratedColorGrid(activeFamily);
37473
+ return /*#__PURE__*/jsxs("div", {
37474
+ className: "flex flex-col gap-2",
37475
+ children: [/*#__PURE__*/jsx("div", {
37476
+ className: "flex flex-row gap-2 flex-wrap",
37477
+ children: families.map(function (family) {
37478
+ var isActive = activeFamily === family;
37479
+ return /*#__PURE__*/jsx("button", {
37480
+ type: "button",
37481
+ onClick: function onClick() {
37482
+ return setActiveFamily(family);
37483
+ },
37484
+ className: "px-2 py-0.5 rounded-full text-xs font-medium ".concat(isActive ? "bg-blue-600 text-white" : "bg-gray-700 text-gray-300 hover:bg-gray-600"),
37485
+ children: family
37486
+ }, family);
37487
+ })
37260
37488
  }), /*#__PURE__*/jsx("div", {
37261
- className: "grid grid-cols-6 gap-2",
37262
- children: AVAILABLE_COLORS.map(function (color) {
37263
- return /*#__PURE__*/jsx("div", {
37264
- className: "h-8 rounded cursor-pointer transition-all bg-".concat(color, "-500 ").concat(value === color ? "ring-2 ring-white scale-110" : "opacity-70 hover:opacity-100"),
37265
- title: color,
37489
+ className: "grid grid-cols-12 gap-1",
37490
+ children: swatches.map(function (hex) {
37491
+ var isSelected = selectedHex && hex.toLowerCase() === selectedHex.toLowerCase();
37492
+ return /*#__PURE__*/jsx("button", {
37493
+ type: "button",
37266
37494
  onClick: function onClick() {
37267
- return onChange(color);
37268
- }
37269
- }, color);
37495
+ return onPick(hex);
37496
+ },
37497
+ style: {
37498
+ backgroundColor: hex
37499
+ },
37500
+ className: "h-6 rounded transition-transform hover:scale-110 ".concat(isSelected ? "border-2 border-yellow-400 scale-110" : ""),
37501
+ "aria-label": "Pick ".concat(hex),
37502
+ title: hex
37503
+ }, hex);
37270
37504
  })
37505
+ }), /*#__PURE__*/jsx(CustomHexColorPane, {
37506
+ onApply: onPick,
37507
+ label: "Or paste a hex value"
37271
37508
  })]
37272
37509
  });
37273
37510
  };
37274
- var PreviewSwatch = function PreviewSwatch(_ref5) {
37275
- var color = _ref5.color,
37276
- strategy = _ref5.strategy,
37277
- customColors = _ref5.customColors;
37278
- var colors;
37279
- if (strategy === "custom" && customColors) {
37280
- colors = [customColors.primary, customColors.secondary, customColors.tertiary];
37281
- } else {
37282
- var theme = generateHarmonyTheme(color, strategy);
37283
- colors = [theme.primary, theme.secondary, theme.tertiary];
37284
- }
37285
- return /*#__PURE__*/jsx("div", {
37286
- className: "flex flex-row gap-2",
37287
- children: colors.map(function (c, i) {
37288
- return /*#__PURE__*/jsxs("div", {
37289
- className: "flex flex-col items-center gap-1 flex-1",
37290
- children: [/*#__PURE__*/jsx("div", {
37291
- className: "h-10 w-full rounded bg-".concat(c, "-500")
37292
- }), /*#__PURE__*/jsx("span", {
37293
- className: "text-[10px] opacity-40",
37294
- children: c
37295
- })]
37296
- }, i);
37297
- })
37511
+
37512
+ /**
37513
+ * HSL nudge slider — single-axis. Display label + numeric value.
37514
+ */
37515
+ var HslSlider = function HslSlider(_ref6) {
37516
+ var label = _ref6.label,
37517
+ value = _ref6.value,
37518
+ min = _ref6.min,
37519
+ max = _ref6.max,
37520
+ step = _ref6.step,
37521
+ _onChange = _ref6.onChange,
37522
+ _ref6$unit = _ref6.unit,
37523
+ unit = _ref6$unit === void 0 ? "" : _ref6$unit;
37524
+ return /*#__PURE__*/jsxs("div", {
37525
+ className: "flex flex-col gap-1",
37526
+ children: [/*#__PURE__*/jsxs("div", {
37527
+ className: "flex flex-row items-center justify-between",
37528
+ children: [/*#__PURE__*/jsx("span", {
37529
+ className: "text-xs opacity-60",
37530
+ children: label
37531
+ }), /*#__PURE__*/jsxs("span", {
37532
+ className: "text-xs opacity-50 font-mono",
37533
+ children: [value > 0 ? "+".concat(value) : value, unit]
37534
+ })]
37535
+ }), /*#__PURE__*/jsx("input", {
37536
+ type: "range",
37537
+ min: min,
37538
+ max: max,
37539
+ step: step,
37540
+ value: value,
37541
+ onChange: function onChange(e) {
37542
+ return _onChange(parseFloat(e.target.value));
37543
+ },
37544
+ className: "w-full accent-blue-500"
37545
+ })]
37298
37546
  });
37299
37547
  };
37300
- var ColorHarmonyPicker = function ColorHarmonyPicker(_ref6) {
37301
- var onGenerate = _ref6.onGenerate,
37302
- _ref6$inline = _ref6.inline,
37303
- inline = _ref6$inline === void 0 ? false : _ref6$inline;
37304
- var _useState = useState("blue"),
37305
- _useState2 = _slicedToArray(_useState, 2),
37306
- selectedColor = _useState2[0],
37307
- setSelectedColor = _useState2[1];
37308
- var _useState3 = useState("rose"),
37548
+ var ColorHarmonyPicker = function ColorHarmonyPicker(_ref7) {
37549
+ var onGenerate = _ref7.onGenerate,
37550
+ _ref7$inline = _ref7.inline,
37551
+ inline = _ref7$inline === void 0 ? false : _ref7$inline;
37552
+ // Default to a brand-blue hex so the wizard opens in hex mode.
37553
+ var _useState3 = useState("#3b82f6"),
37309
37554
  _useState4 = _slicedToArray(_useState3, 2),
37310
- secondaryColor = _useState4[0],
37311
- setSecondaryColor = _useState4[1];
37312
- var _useState5 = useState("amber"),
37555
+ selectedColor = _useState4[0],
37556
+ setSelectedColor = _useState4[1];
37557
+ var _useState5 = useState("complementary"),
37313
37558
  _useState6 = _slicedToArray(_useState5, 2),
37314
- tertiaryColor = _useState6[0],
37315
- setTertiaryColor = _useState6[1];
37316
- var _useState7 = useState("complementary"),
37559
+ strategy = _useState6[0],
37560
+ setStrategy = _useState6[1];
37561
+ // HSL nudge — applied to every generated channel after harmony math.
37562
+ var _useState7 = useState(0),
37317
37563
  _useState8 = _slicedToArray(_useState7, 2),
37318
- strategy = _useState8[0],
37319
- setStrategy = _useState8[1];
37320
- var isCustom = strategy === "custom";
37564
+ dH = _useState8[0],
37565
+ setDH = _useState8[1];
37566
+ var _useState9 = useState(0),
37567
+ _useState0 = _slicedToArray(_useState9, 2),
37568
+ dS = _useState0[0],
37569
+ setDS = _useState0[1];
37570
+ var _useState1 = useState(0),
37571
+ _useState10 = _slicedToArray(_useState1, 2),
37572
+ dL = _useState10[0],
37573
+ setDL = _useState10[1];
37574
+
37575
+ // Generate the current theme from inputs. Memoized through useEffect
37576
+ // for the inline-preview path; explicit handler for the button path.
37577
+ function buildTheme() {
37578
+ return generateHarmonyTheme(selectedColor, strategy, {
37579
+ dH: dH,
37580
+ dS: dS,
37581
+ dL: dL
37582
+ });
37583
+ }
37321
37584
  useEffect(function () {
37322
37585
  if (inline) {
37323
- var theme = isCustom ? generateCustomTheme(selectedColor, secondaryColor, tertiaryColor) : generateHarmonyTheme(selectedColor, strategy);
37324
- onGenerate(theme);
37586
+ onGenerate(buildTheme());
37325
37587
  }
37326
37588
  // eslint-disable-next-line react-hooks/exhaustive-deps
37327
- }, [selectedColor, secondaryColor, tertiaryColor, strategy, inline]);
37589
+ }, [selectedColor, strategy, dH, dS, dL, inline]);
37328
37590
  function handleGenerate() {
37329
- var theme = isCustom ? generateCustomTheme(selectedColor, secondaryColor, tertiaryColor) : generateHarmonyTheme(selectedColor, strategy);
37330
- onGenerate(theme);
37591
+ onGenerate(buildTheme());
37331
37592
  }
37593
+ var previewTheme = buildTheme();
37332
37594
  return /*#__PURE__*/jsxs("div", {
37333
37595
  className: inline ? "flex flex-col gap-4" : "flex flex-col gap-4 p-6 overflow-y-auto flex-1 min-h-0",
37334
37596
  children: [/*#__PURE__*/jsx("span", {
@@ -37341,7 +37603,9 @@ var ColorHarmonyPicker = function ColorHarmonyPicker(_ref6) {
37341
37603
  children: "Harmony"
37342
37604
  }), /*#__PURE__*/jsx("div", {
37343
37605
  className: "flex flex-row gap-2 flex-wrap",
37344
- children: HARMONY_STRATEGIES.map(function (s) {
37606
+ children: HARMONY_STRATEGIES.filter(function (s) {
37607
+ return s.value !== "custom";
37608
+ }).map(function (s) {
37345
37609
  return /*#__PURE__*/jsx("button", {
37346
37610
  type: "button",
37347
37611
  className: "px-3 py-1.5 rounded text-xs transition-all ".concat(strategy === s.value ? "bg-blue-600 text-white" : "bg-white/10 opacity-70 hover:opacity-100"),
@@ -37352,37 +37616,71 @@ var ColorHarmonyPicker = function ColorHarmonyPicker(_ref6) {
37352
37616
  }, s.value);
37353
37617
  })
37354
37618
  })]
37355
- }), isCustom ? /*#__PURE__*/jsxs(Fragment, {
37356
- children: [/*#__PURE__*/jsx(ColorSwatchGrid$1, {
37357
- value: selectedColor,
37358
- onChange: setSelectedColor,
37359
- label: "Primary"
37360
- }), /*#__PURE__*/jsx(ColorSwatchGrid$1, {
37361
- value: secondaryColor,
37362
- onChange: setSecondaryColor,
37363
- label: "Secondary"
37364
- }), /*#__PURE__*/jsx(ColorSwatchGrid$1, {
37365
- value: tertiaryColor,
37366
- onChange: setTertiaryColor,
37367
- label: "Tertiary"
37619
+ }), /*#__PURE__*/jsxs("div", {
37620
+ className: "flex flex-col gap-2",
37621
+ children: [/*#__PURE__*/jsx("span", {
37622
+ className: "text-xs opacity-50",
37623
+ children: "Base Color"
37624
+ }), /*#__PURE__*/jsx(InlineHexPicker, {
37625
+ selectedHex: isHexColor(selectedColor) ? selectedColor : null,
37626
+ onPick: function onPick(hex) {
37627
+ return setSelectedColor(hex);
37628
+ }
37629
+ })]
37630
+ }), /*#__PURE__*/jsxs("div", {
37631
+ className: "flex flex-col gap-3",
37632
+ children: [/*#__PURE__*/jsx("span", {
37633
+ className: "text-xs opacity-50",
37634
+ children: "Fine-tune (HSL nudge)"
37635
+ }), /*#__PURE__*/jsx(HslSlider, {
37636
+ label: "Hue",
37637
+ value: dH,
37638
+ min: -180,
37639
+ max: 180,
37640
+ step: 1,
37641
+ unit: "\xB0",
37642
+ onChange: setDH
37643
+ }), /*#__PURE__*/jsx(HslSlider, {
37644
+ label: "Saturation",
37645
+ value: Math.round(dS * 100),
37646
+ min: -100,
37647
+ max: 100,
37648
+ step: 1,
37649
+ unit: "%",
37650
+ onChange: function onChange(v) {
37651
+ return setDS(v / 100);
37652
+ }
37653
+ }), /*#__PURE__*/jsx(HslSlider, {
37654
+ label: "Lightness",
37655
+ value: Math.round(dL * 100),
37656
+ min: -100,
37657
+ max: 100,
37658
+ step: 1,
37659
+ unit: "%",
37660
+ onChange: function onChange(v) {
37661
+ return setDL(v / 100);
37662
+ }
37368
37663
  })]
37369
- }) : /*#__PURE__*/jsx(ColorSwatchGrid$1, {
37370
- value: selectedColor,
37371
- onChange: setSelectedColor,
37372
- label: "Base Color"
37373
37664
  }), /*#__PURE__*/jsxs("div", {
37374
37665
  className: "flex flex-col gap-2",
37375
37666
  children: [/*#__PURE__*/jsx("span", {
37376
37667
  className: "text-xs opacity-50",
37377
- children: "Preview"
37378
- }), /*#__PURE__*/jsx(PreviewSwatch, {
37379
- color: selectedColor,
37380
- strategy: strategy,
37381
- customColors: isCustom ? {
37382
- primary: selectedColor,
37383
- secondary: secondaryColor,
37384
- tertiary: tertiaryColor
37385
- } : null
37668
+ children: "Generated Palette"
37669
+ }), /*#__PURE__*/jsxs("div", {
37670
+ className: "flex flex-row gap-2",
37671
+ children: [/*#__PURE__*/jsx(PaletteSwatch, {
37672
+ value: previewTheme.primary,
37673
+ label: "Primary"
37674
+ }), /*#__PURE__*/jsx(PaletteSwatch, {
37675
+ value: previewTheme.secondary,
37676
+ label: "Secondary"
37677
+ }), /*#__PURE__*/jsx(PaletteSwatch, {
37678
+ value: previewTheme.tertiary,
37679
+ label: "Tertiary"
37680
+ }), /*#__PURE__*/jsx(PaletteSwatch, {
37681
+ value: previewTheme.neutral,
37682
+ label: "Neutral"
37683
+ })]
37386
37684
  })]
37387
37685
  }), !inline && /*#__PURE__*/jsx(Button, {
37388
37686
  title: "Generate Theme",
@@ -37394,12 +37692,12 @@ var ColorHarmonyPicker = function ColorHarmonyPicker(_ref6) {
37394
37692
 
37395
37693
  // ─── Wizard Components ──────────────────────────────────────────────────
37396
37694
 
37397
- var MethodCard = function MethodCard(_ref7) {
37398
- var icon = _ref7.icon,
37399
- title = _ref7.title,
37400
- subtitle = _ref7.subtitle,
37401
- selected = _ref7.selected,
37402
- onClick = _ref7.onClick;
37695
+ var MethodCard = function MethodCard(_ref8) {
37696
+ var icon = _ref8.icon,
37697
+ title = _ref8.title,
37698
+ subtitle = _ref8.subtitle,
37699
+ selected = _ref8.selected,
37700
+ onClick = _ref8.onClick;
37403
37701
  return /*#__PURE__*/jsxs("div", {
37404
37702
  className: "flex flex-col gap-1.5 p-3 rounded-lg cursor-pointer transition-all bg-white/5 hover:bg-white/10 ".concat(selected ? "ring-2 ring-blue-500 bg-white/10" : "hover:ring-1 hover:ring-white/20"),
37405
37703
  onClick: onClick,
@@ -37418,9 +37716,9 @@ var MethodCard = function MethodCard(_ref7) {
37418
37716
  })]
37419
37717
  });
37420
37718
  };
37421
- var RandomPreview = function RandomPreview(_ref8) {
37422
- var theme = _ref8.theme,
37423
- onRegenerate = _ref8.onRegenerate;
37719
+ var RandomPreview = function RandomPreview(_ref9) {
37720
+ var theme = _ref9.theme,
37721
+ onRegenerate = _ref9.onRegenerate;
37424
37722
  var colors = theme ? [theme.primary, theme.secondary, theme.tertiary] : [];
37425
37723
  return /*#__PURE__*/jsxs("div", {
37426
37724
  className: "flex flex-col gap-4",
@@ -37447,20 +37745,20 @@ var RandomPreview = function RandomPreview(_ref8) {
37447
37745
  })]
37448
37746
  });
37449
37747
  };
37450
- var ThemeQuickCreate = function ThemeQuickCreate(_ref9) {
37451
- var wizardName = _ref9.wizardName,
37452
- setWizardName = _ref9.setWizardName,
37453
- wizardMethod = _ref9.wizardMethod,
37454
- setWizardMethod = _ref9.setWizardMethod,
37455
- wizardTheme = _ref9.wizardTheme,
37456
- setWizardTheme = _ref9.setWizardTheme,
37457
- onComplete = _ref9.onComplete,
37458
- _ref9$onExtract = _ref9.onExtract,
37459
- onExtract = _ref9$onExtract === void 0 ? null : _ref9$onExtract,
37460
- _ref9$onMapToTheme = _ref9.onMapToTheme,
37461
- onMapToTheme = _ref9$onMapToTheme === void 0 ? null : _ref9$onMapToTheme,
37462
- _ref9$initialMethod = _ref9.initialMethod,
37463
- initialMethod = _ref9$initialMethod === void 0 ? null : _ref9$initialMethod;
37748
+ var ThemeQuickCreate = function ThemeQuickCreate(_ref0) {
37749
+ var wizardName = _ref0.wizardName,
37750
+ setWizardName = _ref0.setWizardName,
37751
+ wizardMethod = _ref0.wizardMethod,
37752
+ setWizardMethod = _ref0.setWizardMethod,
37753
+ wizardTheme = _ref0.wizardTheme,
37754
+ setWizardTheme = _ref0.setWizardTheme,
37755
+ onComplete = _ref0.onComplete,
37756
+ _ref0$onExtract = _ref0.onExtract,
37757
+ onExtract = _ref0$onExtract === void 0 ? null : _ref0$onExtract,
37758
+ _ref0$onMapToTheme = _ref0.onMapToTheme,
37759
+ onMapToTheme = _ref0$onMapToTheme === void 0 ? null : _ref0$onMapToTheme,
37760
+ _ref0$initialMethod = _ref0.initialMethod,
37761
+ initialMethod = _ref0$initialMethod === void 0 ? null : _ref0$initialMethod;
37464
37762
  var canCreate = wizardName.trim().length > 0 && wizardTheme !== null;
37465
37763
  var showMethodPicker = initialMethod === null;
37466
37764
  useEffect(function () {