@activecollab/components 2.0.379 → 2.0.380

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.
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isDarkTheme = exports.THEME_NAMES = exports.THEMES = exports.DARK_THEME_SELECTOR = exports.DARK_THEME_NAMES = exports.DARK_THEME_MATCH = void 0;
6
+ exports.resolveTheme = exports.isDarkTheme = exports.THEME_NAMES = exports.THEME_MODE_SYSTEM = exports.THEME_MODE_SINGLE = exports.THEMES = exports.LIGHT_THEME_NAMES = exports.DEFAULT_THEME = exports.DEFAULT_DARK_THEME = exports.DARK_THEME_SELECTOR = exports.DARK_THEME_NAMES = exports.DARK_THEME_MATCH = void 0;
7
7
  // Single source of truth for theme identity. The theme name is the only class put on <html>;
8
8
  // styling that targets "any dark theme" uses DARK_THEME_SELECTOR, which is derived from this list
9
9
  // so a new theme never needs the selector updating by hand.
@@ -11,27 +11,33 @@ exports.isDarkTheme = exports.THEME_NAMES = exports.THEMES = exports.DARK_THEME_
11
11
  var THEMES = exports.THEMES = [{
12
12
  name: "indigo",
13
13
  title: "Indigo",
14
- dark: false
14
+ dark: false,
15
+ swatch: ["#5d2bff", "#5d2bff", "#f4f5f7"]
15
16
  }, {
16
17
  name: "cupcake",
17
18
  title: "Cupcake",
18
- dark: false
19
+ dark: false,
20
+ swatch: ["#29bcdd", "#fe6fa7", "#f4f5f7"]
19
21
  }, {
20
22
  name: "classic",
21
23
  title: "Classic",
22
- dark: false
24
+ dark: false,
25
+ swatch: ["#4182f2", "#4182f2", "#f4f5f7"]
23
26
  }, {
24
27
  name: "neon",
25
28
  title: "Neon",
26
- dark: true
29
+ dark: true,
30
+ swatch: ["#67ffc8", "#67ffc8", "#2c2e38"]
27
31
  }, {
28
32
  name: "dracula",
29
33
  title: "Dracula",
30
- dark: true
34
+ dark: true,
35
+ swatch: ["#bd93f9", "#ff79c6", "#282a36"]
31
36
  }, {
32
37
  name: "one-dark",
33
38
  title: "One Dark",
34
- dark: true
39
+ dark: true,
40
+ swatch: ["#61afef", "#c678dd", "#282c34"]
35
41
  }];
36
42
  var THEME_NAMES = exports.THEME_NAMES = THEMES.map(function (theme) {
37
43
  return theme.name;
@@ -41,9 +47,33 @@ var DARK_THEME_NAMES = exports.DARK_THEME_NAMES = THEMES.filter(function (theme)
41
47
  }).map(function (theme) {
42
48
  return theme.name;
43
49
  });
50
+ var LIGHT_THEME_NAMES = exports.LIGHT_THEME_NAMES = THEMES.filter(function (theme) {
51
+ return !theme.dark;
52
+ }).map(function (theme) {
53
+ return theme.name;
54
+ });
55
+ var DEFAULT_THEME = exports.DEFAULT_THEME = "indigo";
56
+ var DEFAULT_DARK_THEME = exports.DEFAULT_DARK_THEME = "neon";
57
+ var THEME_MODE_SINGLE = exports.THEME_MODE_SINGLE = "single";
58
+ var THEME_MODE_SYSTEM = exports.THEME_MODE_SYSTEM = "system";
44
59
  var isDarkTheme = exports.isDarkTheme = function isDarkTheme(name) {
45
60
  return Boolean(name) && DARK_THEME_NAMES.includes(name);
46
61
  };
62
+ var knownTheme = function knownTheme(name, fallback) {
63
+ return name && THEME_NAMES.includes(name) ? name : fallback;
64
+ };
65
+
66
+ // Resolution is derived per render, never persisted — writing it back would destroy the user's pick.
67
+ var resolveTheme = exports.resolveTheme = function resolveTheme(_ref, prefersDark) {
68
+ var mode = _ref.mode,
69
+ theme = _ref.theme,
70
+ themeLight = _ref.themeLight,
71
+ themeDark = _ref.themeDark;
72
+ if (mode !== THEME_MODE_SYSTEM) {
73
+ return knownTheme(theme, DEFAULT_THEME);
74
+ }
75
+ return prefersDark ? knownTheme(themeDark, DEFAULT_DARK_THEME) : knownTheme(themeLight, DEFAULT_THEME);
76
+ };
47
77
 
48
78
  // :is() and not :where() — :where() is zero-specificity and would silently lose to the light
49
79
  // declarations it is meant to override.
@@ -1 +1 @@
1
- {"version":3,"file":"themes.js","names":["THEMES","exports","name","title","dark","THEME_NAMES","map","theme","DARK_THEME_NAMES","filter","isDarkTheme","Boolean","includes","DARK_THEME_MATCH","concat","join","DARK_THEME_SELECTOR"],"sources":["../../../src/utils/themes.ts"],"sourcesContent":["// Single source of truth for theme identity. The theme name is the only class put on <html>;\n// styling that targets \"any dark theme\" uses DARK_THEME_SELECTOR, which is derived from this list\n// so a new theme never needs the selector updating by hand.\nexport interface ThemeDescriptor {\n name: string;\n title: string;\n dark: boolean;\n}\n\nexport const THEMES: ThemeDescriptor[] = [\n { name: \"indigo\", title: \"Indigo\", dark: false },\n { name: \"cupcake\", title: \"Cupcake\", dark: false },\n { name: \"classic\", title: \"Classic\", dark: false },\n { name: \"neon\", title: \"Neon\", dark: true },\n { name: \"dracula\", title: \"Dracula\", dark: true },\n { name: \"one-dark\", title: \"One Dark\", dark: true },\n];\n\nexport const THEME_NAMES = THEMES.map((theme) => theme.name);\n\nexport const DARK_THEME_NAMES = THEMES.filter((theme) => theme.dark).map(\n (theme) => theme.name\n);\n\nexport const isDarkTheme = (name?: string | null): boolean =>\n Boolean(name) && DARK_THEME_NAMES.includes(name as string);\n\n// :is() and not :where() — :where() is zero-specificity and would silently lose to the light\n// declarations it is meant to override.\n\n// Compound onto an element that carries the theme class itself: `html${DARK_THEME_MATCH}`.\nexport const DARK_THEME_MATCH = `:is(${DARK_THEME_NAMES.map(\n (name) => `.${name}`\n).join(\", \")})`;\n\n// Stand-alone ancestor selector: `${DARK_THEME_SELECTOR} &`. The leading `*` adds no specificity\n// but stops stylis from reading a leading `:` as \"attach to the parent\" and gluing it onto `&`.\nexport const DARK_THEME_SELECTOR = `*${DARK_THEME_MATCH}`;\n"],"mappings":";;;;;;AAAA;AACA;AACA;;AAOO,IAAMA,MAAyB,GAAAC,OAAA,CAAAD,MAAA,GAAG,CACvC;EAAEE,IAAI,EAAE,QAAQ;EAAEC,KAAK,EAAE,QAAQ;EAAEC,IAAI,EAAE;AAAM,CAAC,EAChD;EAAEF,IAAI,EAAE,SAAS;EAAEC,KAAK,EAAE,SAAS;EAAEC,IAAI,EAAE;AAAM,CAAC,EAClD;EAAEF,IAAI,EAAE,SAAS;EAAEC,KAAK,EAAE,SAAS;EAAEC,IAAI,EAAE;AAAM,CAAC,EAClD;EAAEF,IAAI,EAAE,MAAM;EAAEC,KAAK,EAAE,MAAM;EAAEC,IAAI,EAAE;AAAK,CAAC,EAC3C;EAAEF,IAAI,EAAE,SAAS;EAAEC,KAAK,EAAE,SAAS;EAAEC,IAAI,EAAE;AAAK,CAAC,EACjD;EAAEF,IAAI,EAAE,UAAU;EAAEC,KAAK,EAAE,UAAU;EAAEC,IAAI,EAAE;AAAK,CAAC,CACpD;AAEM,IAAMC,WAAW,GAAAJ,OAAA,CAAAI,WAAA,GAAGL,MAAM,CAACM,GAAG,CAAC,UAACC,KAAK;EAAA,OAAKA,KAAK,CAACL,IAAI;AAAA,EAAC;AAErD,IAAMM,gBAAgB,GAAAP,OAAA,CAAAO,gBAAA,GAAGR,MAAM,CAACS,MAAM,CAAC,UAACF,KAAK;EAAA,OAAKA,KAAK,CAACH,IAAI;AAAA,EAAC,CAACE,GAAG,CACtE,UAACC,KAAK;EAAA,OAAKA,KAAK,CAACL,IAAI;AAAA,CACvB,CAAC;AAEM,IAAMQ,WAAW,GAAAT,OAAA,CAAAS,WAAA,GAAG,SAAdA,WAAWA,CAAIR,IAAoB;EAAA,OAC9CS,OAAO,CAACT,IAAI,CAAC,IAAIM,gBAAgB,CAACI,QAAQ,CAACV,IAAc,CAAC;AAAA;;AAE5D;AACA;;AAEA;AACO,IAAMW,gBAAgB,GAAAZ,OAAA,CAAAY,gBAAA,UAAAC,MAAA,CAAUN,gBAAgB,CAACF,GAAG,CACzD,UAACJ,IAAI;EAAA,WAAAY,MAAA,CAASZ,IAAI;AAAA,CACpB,CAAC,CAACa,IAAI,CAAC,IAAI,CAAC,MAAG;;AAEf;AACA;AACO,IAAMC,mBAAmB,GAAAf,OAAA,CAAAe,mBAAA,OAAAF,MAAA,CAAOD,gBAAgB,CAAE","ignoreList":[]}
1
+ {"version":3,"file":"themes.js","names":["THEMES","exports","name","title","dark","swatch","THEME_NAMES","map","theme","DARK_THEME_NAMES","filter","LIGHT_THEME_NAMES","DEFAULT_THEME","DEFAULT_DARK_THEME","THEME_MODE_SINGLE","THEME_MODE_SYSTEM","isDarkTheme","Boolean","includes","knownTheme","fallback","resolveTheme","_ref","prefersDark","mode","themeLight","themeDark","DARK_THEME_MATCH","concat","join","DARK_THEME_SELECTOR"],"sources":["../../../src/utils/themes.ts"],"sourcesContent":["// Single source of truth for theme identity. The theme name is the only class put on <html>;\n// styling that targets \"any dark theme\" uses DARK_THEME_SELECTOR, which is derived from this list\n// so a new theme never needs the selector updating by hand.\nexport interface ThemeDescriptor {\n name: string;\n title: string;\n dark: boolean;\n // [primary, secondary, page background] — accents lead so the swatch reads as the theme's colour.\n // Mirrors GlobalStyle; themes.swatch.test.ts fails if the two drift apart.\n swatch: [string, string, string];\n}\n\nexport const THEMES: ThemeDescriptor[] = [\n {\n name: \"indigo\",\n title: \"Indigo\",\n dark: false,\n swatch: [\"#5d2bff\", \"#5d2bff\", \"#f4f5f7\"],\n },\n {\n name: \"cupcake\",\n title: \"Cupcake\",\n dark: false,\n swatch: [\"#29bcdd\", \"#fe6fa7\", \"#f4f5f7\"],\n },\n {\n name: \"classic\",\n title: \"Classic\",\n dark: false,\n swatch: [\"#4182f2\", \"#4182f2\", \"#f4f5f7\"],\n },\n {\n name: \"neon\",\n title: \"Neon\",\n dark: true,\n swatch: [\"#67ffc8\", \"#67ffc8\", \"#2c2e38\"],\n },\n {\n name: \"dracula\",\n title: \"Dracula\",\n dark: true,\n swatch: [\"#bd93f9\", \"#ff79c6\", \"#282a36\"],\n },\n {\n name: \"one-dark\",\n title: \"One Dark\",\n dark: true,\n swatch: [\"#61afef\", \"#c678dd\", \"#282c34\"],\n },\n];\n\nexport const THEME_NAMES = THEMES.map((theme) => theme.name);\n\nexport const DARK_THEME_NAMES = THEMES.filter((theme) => theme.dark).map(\n (theme) => theme.name\n);\n\nexport const LIGHT_THEME_NAMES = THEMES.filter((theme) => !theme.dark).map(\n (theme) => theme.name\n);\n\nexport const DEFAULT_THEME = \"indigo\";\nexport const DEFAULT_DARK_THEME = \"neon\";\n\nexport const THEME_MODE_SINGLE = \"single\";\nexport const THEME_MODE_SYSTEM = \"system\";\n\nexport interface ThemeSelection {\n mode?: string | null;\n theme?: string | null;\n themeLight?: string | null;\n themeDark?: string | null;\n}\n\nexport const isDarkTheme = (name?: string | null): boolean =>\n Boolean(name) && DARK_THEME_NAMES.includes(name as string);\n\nconst knownTheme = (\n name: string | null | undefined,\n fallback: string\n): string => (name && THEME_NAMES.includes(name) ? name : fallback);\n\n// Resolution is derived per render, never persisted — writing it back would destroy the user's pick.\nexport const resolveTheme = (\n { mode, theme, themeLight, themeDark }: ThemeSelection,\n prefersDark: boolean\n): string => {\n if (mode !== THEME_MODE_SYSTEM) {\n return knownTheme(theme, DEFAULT_THEME);\n }\n\n return prefersDark\n ? knownTheme(themeDark, DEFAULT_DARK_THEME)\n : knownTheme(themeLight, DEFAULT_THEME);\n};\n\n// :is() and not :where() — :where() is zero-specificity and would silently lose to the light\n// declarations it is meant to override.\n\n// Compound onto an element that carries the theme class itself: `html${DARK_THEME_MATCH}`.\nexport const DARK_THEME_MATCH = `:is(${DARK_THEME_NAMES.map(\n (name) => `.${name}`\n).join(\", \")})`;\n\n// Stand-alone ancestor selector: `${DARK_THEME_SELECTOR} &`. The leading `*` adds no specificity\n// but stops stylis from reading a leading `:` as \"attach to the parent\" and gluing it onto `&`.\nexport const DARK_THEME_SELECTOR = `*${DARK_THEME_MATCH}`;\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ var _fs = require("fs");
4
+ var _path = require("path");
5
+ var _themes = require("./themes");
6
+ var GLOBAL_STYLE = (0, _fs.readFileSync)((0, _path.join)(__dirname, "../components/GlobalStyle.ts"), "utf-8");
7
+ var SWATCH_VARS = ["color-primary", "color-secondary", "body-bg-main"];
8
+ var collectBlocks = function collectBlocks(selector) {
9
+ var re = new RegExp("".concat(selector, "\\s*\\{([^}]*)\\}"), "g");
10
+ var declarations = "";
11
+ var match;
12
+ while (match = re.exec(GLOBAL_STYLE)) declarations += "".concat(match[1], ";");
13
+ return declarations;
14
+ };
15
+ var readVar = function readVar(block, name) {
16
+ var _block$match;
17
+ return (_block$match = block.match(new RegExp("--".concat(name, "\\s*:\\s*([^;}]+)")))) === null || _block$match === void 0 ? void 0 : _block$match[1].trim();
18
+ };
19
+ var toHex = function toHex(value) {
20
+ var rgb = value.match(/rgb\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)\s*\)/);
21
+ if (!rgb) return value.toLowerCase();
22
+ return "#".concat(rgb.slice(1).map(function (channel) {
23
+ return Number(channel).toString(16).padStart(2, "0");
24
+ }).join(""));
25
+ };
26
+
27
+ // indigo has no class block of its own — it is the :root default every other theme overrides.
28
+ var declarationsFor = function declarationsFor(name) {
29
+ return (name === "indigo" ? "" : collectBlocks("\\.".concat(name))) + collectBlocks(":root");
30
+ };
31
+ describe("theme swatches", function () {
32
+ it("should match the colours GlobalStyle defines for each theme", function () {
33
+ var fromStylesheet = _themes.THEMES.map(function (_ref) {
34
+ var name = _ref.name;
35
+ var block = declarationsFor(name);
36
+ return [name, SWATCH_VARS.map(function (cssVar) {
37
+ return toHex(readVar(block, cssVar));
38
+ })];
39
+ });
40
+ expect(fromStylesheet).toEqual(_themes.THEMES.map(function (_ref2) {
41
+ var name = _ref2.name,
42
+ swatch = _ref2.swatch;
43
+ return [name, swatch];
44
+ }));
45
+ });
46
+ });
47
+ //# sourceMappingURL=themes.swatch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.swatch.test.js","names":["_fs","require","_path","_themes","GLOBAL_STYLE","readFileSync","join","__dirname","SWATCH_VARS","collectBlocks","selector","re","RegExp","concat","declarations","match","exec","readVar","block","name","_block$match","trim","toHex","value","rgb","toLowerCase","slice","map","channel","Number","toString","padStart","declarationsFor","describe","it","fromStylesheet","THEMES","_ref","cssVar","expect","toEqual","_ref2","swatch"],"sources":["../../../src/utils/themes.swatch.test.ts"],"sourcesContent":["import { readFileSync } from \"fs\";\nimport { join } from \"path\";\n\nimport { THEMES } from \"./themes\";\n\nconst GLOBAL_STYLE = readFileSync(\n join(__dirname, \"../components/GlobalStyle.ts\"),\n \"utf-8\"\n);\n\nconst SWATCH_VARS = [\"color-primary\", \"color-secondary\", \"body-bg-main\"];\n\nconst collectBlocks = (selector: string): string => {\n const re = new RegExp(`${selector}\\\\s*\\\\{([^}]*)\\\\}`, \"g\");\n let declarations = \"\";\n let match;\n while ((match = re.exec(GLOBAL_STYLE))) declarations += `${match[1]};`;\n return declarations;\n};\n\nconst readVar = (block: string, name: string): string | undefined =>\n block.match(new RegExp(`--${name}\\\\s*:\\\\s*([^;}]+)`))?.[1].trim();\n\nconst toHex = (value: string): string => {\n const rgb = value.match(/rgb\\(\\s*(\\d+)[,\\s]+(\\d+)[,\\s]+(\\d+)\\s*\\)/);\n if (!rgb) return value.toLowerCase();\n return `#${rgb\n .slice(1)\n .map((channel) => Number(channel).toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n};\n\n// indigo has no class block of its own — it is the :root default every other theme overrides.\nconst declarationsFor = (name: string): string =>\n (name === \"indigo\" ? \"\" : collectBlocks(`\\\\.${name}`)) +\n collectBlocks(\":root\");\n\ndescribe(\"theme swatches\", () => {\n it(\"should match the colours GlobalStyle defines for each theme\", () => {\n const fromStylesheet = THEMES.map(({ name }) => {\n const block = declarationsFor(name);\n return [\n name,\n SWATCH_VARS.map((cssVar) => toHex(readVar(block, cssVar) as string)),\n ];\n });\n\n expect(fromStylesheet).toEqual(\n THEMES.map(({ name, swatch }) => [name, swatch])\n );\n });\n});\n"],"mappings":";;AAAA,IAAAA,GAAA,GAAAC,OAAA;AACA,IAAAC,KAAA,GAAAD,OAAA;AAEA,IAAAE,OAAA,GAAAF,OAAA;AAEA,IAAMG,YAAY,GAAG,IAAAC,gBAAY,EAC/B,IAAAC,UAAI,EAACC,SAAS,EAAE,8BAA8B,CAAC,EAC/C,OACF,CAAC;AAED,IAAMC,WAAW,GAAG,CAAC,eAAe,EAAE,iBAAiB,EAAE,cAAc,CAAC;AAExE,IAAMC,aAAa,GAAG,SAAhBA,aAAaA,CAAIC,QAAgB,EAAa;EAClD,IAAMC,EAAE,GAAG,IAAIC,MAAM,IAAAC,MAAA,CAAIH,QAAQ,wBAAqB,GAAG,CAAC;EAC1D,IAAII,YAAY,GAAG,EAAE;EACrB,IAAIC,KAAK;EACT,OAAQA,KAAK,GAAGJ,EAAE,CAACK,IAAI,CAACZ,YAAY,CAAC,EAAGU,YAAY,OAAAD,MAAA,CAAOE,KAAK,CAAC,CAAC,CAAC,MAAG;EACtE,OAAOD,YAAY;AACrB,CAAC;AAED,IAAMG,OAAO,GAAG,SAAVA,OAAOA,CAAIC,KAAa,EAAEC,IAAY;EAAA,IAAAC,YAAA;EAAA,QAAAA,YAAA,GAC1CF,KAAK,CAACH,KAAK,CAAC,IAAIH,MAAM,MAAAC,MAAA,CAAMM,IAAI,sBAAmB,CAAC,CAAC,cAAAC,YAAA,uBAArDA,YAAA,CAAwD,CAAC,CAAC,CAACC,IAAI,CAAC,CAAC;AAAA;AAEnE,IAAMC,KAAK,GAAG,SAARA,KAAKA,CAAIC,KAAa,EAAa;EACvC,IAAMC,GAAG,GAAGD,KAAK,CAACR,KAAK,CAAC,0CAA0C,CAAC;EACnE,IAAI,CAACS,GAAG,EAAE,OAAOD,KAAK,CAACE,WAAW,CAAC,CAAC;EACpC,WAAAZ,MAAA,CAAWW,GAAG,CACXE,KAAK,CAAC,CAAC,CAAC,CACRC,GAAG,CAAC,UAACC,OAAO;IAAA,OAAKC,MAAM,CAACD,OAAO,CAAC,CAACE,QAAQ,CAAC,EAAE,CAAC,CAACC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC;EAAA,EAAC,CAC/DzB,IAAI,CAAC,EAAE,CAAC;AACb,CAAC;;AAED;AACA,IAAM0B,eAAe,GAAG,SAAlBA,eAAeA,CAAIb,IAAY;EAAA,OACnC,CAACA,IAAI,KAAK,QAAQ,GAAG,EAAE,GAAGV,aAAa,OAAAI,MAAA,CAAOM,IAAI,CAAE,CAAC,IACrDV,aAAa,CAAC,OAAO,CAAC;AAAA;AAExBwB,QAAQ,CAAC,gBAAgB,EAAE,YAAM;EAC/BC,EAAE,CAAC,6DAA6D,EAAE,YAAM;IACtE,IAAMC,cAAc,GAAGC,cAAM,CAACT,GAAG,CAAC,UAAAU,IAAA,EAAc;MAAA,IAAXlB,IAAI,GAAAkB,IAAA,CAAJlB,IAAI;MACvC,IAAMD,KAAK,GAAGc,eAAe,CAACb,IAAI,CAAC;MACnC,OAAO,CACLA,IAAI,EACJX,WAAW,CAACmB,GAAG,CAAC,UAACW,MAAM;QAAA,OAAKhB,KAAK,CAACL,OAAO,CAACC,KAAK,EAAEoB,MAAM,CAAW,CAAC;MAAA,EAAC,CACrE;IACH,CAAC,CAAC;IAEFC,MAAM,CAACJ,cAAc,CAAC,CAACK,OAAO,CAC5BJ,cAAM,CAACT,GAAG,CAAC,UAAAc,KAAA;MAAA,IAAGtB,IAAI,GAAAsB,KAAA,CAAJtB,IAAI;QAAEuB,MAAM,GAAAD,KAAA,CAANC,MAAM;MAAA,OAAO,CAACvB,IAAI,EAAEuB,MAAM,CAAC;IAAA,EACjD,CAAC;EACH,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ var _themes = require("./themes");
4
+ describe("resolveTheme", function () {
5
+ it("should split the theme list into light and dark without overlap", function () {
6
+ expect(_themes.LIGHT_THEME_NAMES).toEqual(["indigo", "cupcake", "classic"]);
7
+ expect(_themes.DARK_THEME_NAMES).toEqual(["neon", "dracula", "one-dark"]);
8
+ });
9
+ it("should ignore the system appearance in single mode", function () {
10
+ var selection = {
11
+ mode: _themes.THEME_MODE_SINGLE,
12
+ theme: "dracula",
13
+ themeLight: "cupcake",
14
+ themeDark: "one-dark"
15
+ };
16
+ expect((0, _themes.resolveTheme)(selection, false)).toBe("dracula");
17
+ expect((0, _themes.resolveTheme)(selection, true)).toBe("dracula");
18
+ });
19
+ it("should follow the system appearance in system mode", function () {
20
+ var selection = {
21
+ mode: _themes.THEME_MODE_SYSTEM,
22
+ theme: "dracula",
23
+ themeLight: "cupcake",
24
+ themeDark: "one-dark"
25
+ };
26
+ expect((0, _themes.resolveTheme)(selection, false)).toBe("cupcake");
27
+ expect((0, _themes.resolveTheme)(selection, true)).toBe("one-dark");
28
+ });
29
+ it("should fall back to the defaults when a slot is missing or unknown", function () {
30
+ expect((0, _themes.resolveTheme)({
31
+ mode: _themes.THEME_MODE_SYSTEM
32
+ }, false)).toBe(_themes.DEFAULT_THEME);
33
+ expect((0, _themes.resolveTheme)({
34
+ mode: _themes.THEME_MODE_SYSTEM
35
+ }, true)).toBe(_themes.DEFAULT_DARK_THEME);
36
+ expect((0, _themes.resolveTheme)({
37
+ mode: _themes.THEME_MODE_SYSTEM,
38
+ themeLight: "solarized",
39
+ themeDark: "solarized"
40
+ }, false)).toBe(_themes.DEFAULT_THEME);
41
+ expect((0, _themes.resolveTheme)({
42
+ mode: _themes.THEME_MODE_SYSTEM,
43
+ themeLight: "solarized",
44
+ themeDark: "solarized"
45
+ }, true)).toBe(_themes.DEFAULT_DARK_THEME);
46
+ expect((0, _themes.resolveTheme)({
47
+ theme: "solarized"
48
+ }, false)).toBe(_themes.DEFAULT_THEME);
49
+ });
50
+ it("should treat an absent or unrecognised mode as single", function () {
51
+ expect((0, _themes.resolveTheme)({
52
+ theme: "neon"
53
+ }, false)).toBe("neon");
54
+ expect((0, _themes.resolveTheme)({
55
+ mode: "auto",
56
+ theme: "neon"
57
+ }, true)).toBe("neon");
58
+ });
59
+ });
60
+ //# sourceMappingURL=themes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.test.js","names":["_themes","require","describe","it","expect","LIGHT_THEME_NAMES","toEqual","DARK_THEME_NAMES","selection","mode","THEME_MODE_SINGLE","theme","themeLight","themeDark","resolveTheme","toBe","THEME_MODE_SYSTEM","DEFAULT_THEME","DEFAULT_DARK_THEME"],"sources":["../../../src/utils/themes.test.ts"],"sourcesContent":["import {\n DEFAULT_DARK_THEME,\n DEFAULT_THEME,\n DARK_THEME_NAMES,\n LIGHT_THEME_NAMES,\n THEME_MODE_SINGLE,\n THEME_MODE_SYSTEM,\n resolveTheme,\n} from \"./themes\";\n\ndescribe(\"resolveTheme\", () => {\n it(\"should split the theme list into light and dark without overlap\", () => {\n expect(LIGHT_THEME_NAMES).toEqual([\"indigo\", \"cupcake\", \"classic\"]);\n expect(DARK_THEME_NAMES).toEqual([\"neon\", \"dracula\", \"one-dark\"]);\n });\n\n it(\"should ignore the system appearance in single mode\", () => {\n const selection = {\n mode: THEME_MODE_SINGLE,\n theme: \"dracula\",\n themeLight: \"cupcake\",\n themeDark: \"one-dark\",\n };\n\n expect(resolveTheme(selection, false)).toBe(\"dracula\");\n expect(resolveTheme(selection, true)).toBe(\"dracula\");\n });\n\n it(\"should follow the system appearance in system mode\", () => {\n const selection = {\n mode: THEME_MODE_SYSTEM,\n theme: \"dracula\",\n themeLight: \"cupcake\",\n themeDark: \"one-dark\",\n };\n\n expect(resolveTheme(selection, false)).toBe(\"cupcake\");\n expect(resolveTheme(selection, true)).toBe(\"one-dark\");\n });\n\n it(\"should fall back to the defaults when a slot is missing or unknown\", () => {\n expect(resolveTheme({ mode: THEME_MODE_SYSTEM }, false)).toBe(\n DEFAULT_THEME\n );\n expect(resolveTheme({ mode: THEME_MODE_SYSTEM }, true)).toBe(\n DEFAULT_DARK_THEME\n );\n expect(\n resolveTheme(\n {\n mode: THEME_MODE_SYSTEM,\n themeLight: \"solarized\",\n themeDark: \"solarized\",\n },\n false\n )\n ).toBe(DEFAULT_THEME);\n expect(\n resolveTheme(\n {\n mode: THEME_MODE_SYSTEM,\n themeLight: \"solarized\",\n themeDark: \"solarized\",\n },\n true\n )\n ).toBe(DEFAULT_DARK_THEME);\n expect(resolveTheme({ theme: \"solarized\" }, false)).toBe(DEFAULT_THEME);\n });\n\n it(\"should treat an absent or unrecognised mode as single\", () => {\n expect(resolveTheme({ theme: \"neon\" }, false)).toBe(\"neon\");\n expect(resolveTheme({ mode: \"auto\", theme: \"neon\" }, true)).toBe(\"neon\");\n });\n});\n"],"mappings":";;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAUAC,QAAQ,CAAC,cAAc,EAAE,YAAM;EAC7BC,EAAE,CAAC,iEAAiE,EAAE,YAAM;IAC1EC,MAAM,CAACC,yBAAiB,CAAC,CAACC,OAAO,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACnEF,MAAM,CAACG,wBAAgB,CAAC,CAACD,OAAO,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;EACnE,CAAC,CAAC;EAEFH,EAAE,CAAC,oDAAoD,EAAE,YAAM;IAC7D,IAAMK,SAAS,GAAG;MAChBC,IAAI,EAAEC,yBAAiB;MACvBC,KAAK,EAAE,SAAS;MAChBC,UAAU,EAAE,SAAS;MACrBC,SAAS,EAAE;IACb,CAAC;IAEDT,MAAM,CAAC,IAAAU,oBAAY,EAACN,SAAS,EAAE,KAAK,CAAC,CAAC,CAACO,IAAI,CAAC,SAAS,CAAC;IACtDX,MAAM,CAAC,IAAAU,oBAAY,EAACN,SAAS,EAAE,IAAI,CAAC,CAAC,CAACO,IAAI,CAAC,SAAS,CAAC;EACvD,CAAC,CAAC;EAEFZ,EAAE,CAAC,oDAAoD,EAAE,YAAM;IAC7D,IAAMK,SAAS,GAAG;MAChBC,IAAI,EAAEO,yBAAiB;MACvBL,KAAK,EAAE,SAAS;MAChBC,UAAU,EAAE,SAAS;MACrBC,SAAS,EAAE;IACb,CAAC;IAEDT,MAAM,CAAC,IAAAU,oBAAY,EAACN,SAAS,EAAE,KAAK,CAAC,CAAC,CAACO,IAAI,CAAC,SAAS,CAAC;IACtDX,MAAM,CAAC,IAAAU,oBAAY,EAACN,SAAS,EAAE,IAAI,CAAC,CAAC,CAACO,IAAI,CAAC,UAAU,CAAC;EACxD,CAAC,CAAC;EAEFZ,EAAE,CAAC,oEAAoE,EAAE,YAAM;IAC7EC,MAAM,CAAC,IAAAU,oBAAY,EAAC;MAAEL,IAAI,EAAEO;IAAkB,CAAC,EAAE,KAAK,CAAC,CAAC,CAACD,IAAI,CAC3DE,qBACF,CAAC;IACDb,MAAM,CAAC,IAAAU,oBAAY,EAAC;MAAEL,IAAI,EAAEO;IAAkB,CAAC,EAAE,IAAI,CAAC,CAAC,CAACD,IAAI,CAC1DG,0BACF,CAAC;IACDd,MAAM,CACJ,IAAAU,oBAAY,EACV;MACEL,IAAI,EAAEO,yBAAiB;MACvBJ,UAAU,EAAE,WAAW;MACvBC,SAAS,EAAE;IACb,CAAC,EACD,KACF,CACF,CAAC,CAACE,IAAI,CAACE,qBAAa,CAAC;IACrBb,MAAM,CACJ,IAAAU,oBAAY,EACV;MACEL,IAAI,EAAEO,yBAAiB;MACvBJ,UAAU,EAAE,WAAW;MACvBC,SAAS,EAAE;IACb,CAAC,EACD,IACF,CACF,CAAC,CAACE,IAAI,CAACG,0BAAkB,CAAC;IAC1Bd,MAAM,CAAC,IAAAU,oBAAY,EAAC;MAAEH,KAAK,EAAE;IAAY,CAAC,EAAE,KAAK,CAAC,CAAC,CAACI,IAAI,CAACE,qBAAa,CAAC;EACzE,CAAC,CAAC;EAEFd,EAAE,CAAC,uDAAuD,EAAE,YAAM;IAChEC,MAAM,CAAC,IAAAU,oBAAY,EAAC;MAAEH,KAAK,EAAE;IAAO,CAAC,EAAE,KAAK,CAAC,CAAC,CAACI,IAAI,CAAC,MAAM,CAAC;IAC3DX,MAAM,CAAC,IAAAU,oBAAY,EAAC;MAAEL,IAAI,EAAE,MAAM;MAAEE,KAAK,EAAE;IAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAACI,IAAI,CAAC,MAAM,CAAC;EAC1E,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -2,11 +2,24 @@ export interface ThemeDescriptor {
2
2
  name: string;
3
3
  title: string;
4
4
  dark: boolean;
5
+ swatch: [string, string, string];
5
6
  }
6
7
  export declare const THEMES: ThemeDescriptor[];
7
8
  export declare const THEME_NAMES: string[];
8
9
  export declare const DARK_THEME_NAMES: string[];
10
+ export declare const LIGHT_THEME_NAMES: string[];
11
+ export declare const DEFAULT_THEME = "indigo";
12
+ export declare const DEFAULT_DARK_THEME = "neon";
13
+ export declare const THEME_MODE_SINGLE = "single";
14
+ export declare const THEME_MODE_SYSTEM = "system";
15
+ export interface ThemeSelection {
16
+ mode?: string | null;
17
+ theme?: string | null;
18
+ themeLight?: string | null;
19
+ themeDark?: string | null;
20
+ }
9
21
  export declare const isDarkTheme: (name?: string | null) => boolean;
22
+ export declare const resolveTheme: ({ mode, theme, themeLight, themeDark }: ThemeSelection, prefersDark: boolean) => string;
10
23
  export declare const DARK_THEME_MATCH: string;
11
24
  export declare const DARK_THEME_SELECTOR: string;
12
25
  //# sourceMappingURL=themes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../../../src/utils/themes.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,eAAO,MAAM,MAAM,EAAE,eAAe,EAOnC,CAAC;AAEF,eAAO,MAAM,WAAW,UAAoC,CAAC;AAE7D,eAAO,MAAM,gBAAgB,UAE5B,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,GAAG,IAAI,KAAG,OACS,CAAC;AAM7D,eAAO,MAAM,gBAAgB,QAEd,CAAC;AAIhB,eAAO,MAAM,mBAAmB,QAAyB,CAAC"}
1
+ {"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../../../src/utils/themes.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IAGd,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,eAAO,MAAM,MAAM,EAAE,eAAe,EAqCnC,CAAC;AAEF,eAAO,MAAM,WAAW,UAAoC,CAAC;AAE7D,eAAO,MAAM,gBAAgB,UAE5B,CAAC;AAEF,eAAO,MAAM,iBAAiB,UAE7B,CAAC;AAEF,eAAO,MAAM,aAAa,WAAW,CAAC;AACtC,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAC1C,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,GAAG,IAAI,KAAG,OACS,CAAC;AAQ7D,eAAO,MAAM,YAAY,GACvB,wCAAwC,cAAc,EACtD,aAAa,OAAO,KACnB,MAQF,CAAC;AAMF,eAAO,MAAM,gBAAgB,QAEd,CAAC;AAIhB,eAAO,MAAM,mBAAmB,QAAyB,CAAC"}
@@ -5,31 +5,55 @@
5
5
  export const THEMES = [{
6
6
  name: "indigo",
7
7
  title: "Indigo",
8
- dark: false
8
+ dark: false,
9
+ swatch: ["#5d2bff", "#5d2bff", "#f4f5f7"]
9
10
  }, {
10
11
  name: "cupcake",
11
12
  title: "Cupcake",
12
- dark: false
13
+ dark: false,
14
+ swatch: ["#29bcdd", "#fe6fa7", "#f4f5f7"]
13
15
  }, {
14
16
  name: "classic",
15
17
  title: "Classic",
16
- dark: false
18
+ dark: false,
19
+ swatch: ["#4182f2", "#4182f2", "#f4f5f7"]
17
20
  }, {
18
21
  name: "neon",
19
22
  title: "Neon",
20
- dark: true
23
+ dark: true,
24
+ swatch: ["#67ffc8", "#67ffc8", "#2c2e38"]
21
25
  }, {
22
26
  name: "dracula",
23
27
  title: "Dracula",
24
- dark: true
28
+ dark: true,
29
+ swatch: ["#bd93f9", "#ff79c6", "#282a36"]
25
30
  }, {
26
31
  name: "one-dark",
27
32
  title: "One Dark",
28
- dark: true
33
+ dark: true,
34
+ swatch: ["#61afef", "#c678dd", "#282c34"]
29
35
  }];
30
36
  export const THEME_NAMES = THEMES.map(theme => theme.name);
31
37
  export const DARK_THEME_NAMES = THEMES.filter(theme => theme.dark).map(theme => theme.name);
38
+ export const LIGHT_THEME_NAMES = THEMES.filter(theme => !theme.dark).map(theme => theme.name);
39
+ export const DEFAULT_THEME = "indigo";
40
+ export const DEFAULT_DARK_THEME = "neon";
41
+ export const THEME_MODE_SINGLE = "single";
42
+ export const THEME_MODE_SYSTEM = "system";
32
43
  export const isDarkTheme = name => Boolean(name) && DARK_THEME_NAMES.includes(name);
44
+ const knownTheme = (name, fallback) => name && THEME_NAMES.includes(name) ? name : fallback;
45
+
46
+ // Resolution is derived per render, never persisted — writing it back would destroy the user's pick.
47
+ export const resolveTheme = (_ref, prefersDark) => {
48
+ let mode = _ref.mode,
49
+ theme = _ref.theme,
50
+ themeLight = _ref.themeLight,
51
+ themeDark = _ref.themeDark;
52
+ if (mode !== THEME_MODE_SYSTEM) {
53
+ return knownTheme(theme, DEFAULT_THEME);
54
+ }
55
+ return prefersDark ? knownTheme(themeDark, DEFAULT_DARK_THEME) : knownTheme(themeLight, DEFAULT_THEME);
56
+ };
33
57
 
34
58
  // :is() and not :where() — :where() is zero-specificity and would silently lose to the light
35
59
  // declarations it is meant to override.
@@ -1 +1 @@
1
- {"version":3,"file":"themes.js","names":["THEMES","name","title","dark","THEME_NAMES","map","theme","DARK_THEME_NAMES","filter","isDarkTheme","Boolean","includes","DARK_THEME_MATCH","join","DARK_THEME_SELECTOR"],"sources":["../../../src/utils/themes.ts"],"sourcesContent":["// Single source of truth for theme identity. The theme name is the only class put on <html>;\n// styling that targets \"any dark theme\" uses DARK_THEME_SELECTOR, which is derived from this list\n// so a new theme never needs the selector updating by hand.\nexport interface ThemeDescriptor {\n name: string;\n title: string;\n dark: boolean;\n}\n\nexport const THEMES: ThemeDescriptor[] = [\n { name: \"indigo\", title: \"Indigo\", dark: false },\n { name: \"cupcake\", title: \"Cupcake\", dark: false },\n { name: \"classic\", title: \"Classic\", dark: false },\n { name: \"neon\", title: \"Neon\", dark: true },\n { name: \"dracula\", title: \"Dracula\", dark: true },\n { name: \"one-dark\", title: \"One Dark\", dark: true },\n];\n\nexport const THEME_NAMES = THEMES.map((theme) => theme.name);\n\nexport const DARK_THEME_NAMES = THEMES.filter((theme) => theme.dark).map(\n (theme) => theme.name\n);\n\nexport const isDarkTheme = (name?: string | null): boolean =>\n Boolean(name) && DARK_THEME_NAMES.includes(name as string);\n\n// :is() and not :where() — :where() is zero-specificity and would silently lose to the light\n// declarations it is meant to override.\n\n// Compound onto an element that carries the theme class itself: `html${DARK_THEME_MATCH}`.\nexport const DARK_THEME_MATCH = `:is(${DARK_THEME_NAMES.map(\n (name) => `.${name}`\n).join(\", \")})`;\n\n// Stand-alone ancestor selector: `${DARK_THEME_SELECTOR} &`. The leading `*` adds no specificity\n// but stops stylis from reading a leading `:` as \"attach to the parent\" and gluing it onto `&`.\nexport const DARK_THEME_SELECTOR = `*${DARK_THEME_MATCH}`;\n"],"mappings":"AAAA;AACA;AACA;;AAOA,OAAO,MAAMA,MAAyB,GAAG,CACvC;EAAEC,IAAI,EAAE,QAAQ;EAAEC,KAAK,EAAE,QAAQ;EAAEC,IAAI,EAAE;AAAM,CAAC,EAChD;EAAEF,IAAI,EAAE,SAAS;EAAEC,KAAK,EAAE,SAAS;EAAEC,IAAI,EAAE;AAAM,CAAC,EAClD;EAAEF,IAAI,EAAE,SAAS;EAAEC,KAAK,EAAE,SAAS;EAAEC,IAAI,EAAE;AAAM,CAAC,EAClD;EAAEF,IAAI,EAAE,MAAM;EAAEC,KAAK,EAAE,MAAM;EAAEC,IAAI,EAAE;AAAK,CAAC,EAC3C;EAAEF,IAAI,EAAE,SAAS;EAAEC,KAAK,EAAE,SAAS;EAAEC,IAAI,EAAE;AAAK,CAAC,EACjD;EAAEF,IAAI,EAAE,UAAU;EAAEC,KAAK,EAAE,UAAU;EAAEC,IAAI,EAAE;AAAK,CAAC,CACpD;AAED,OAAO,MAAMC,WAAW,GAAGJ,MAAM,CAACK,GAAG,CAAEC,KAAK,IAAKA,KAAK,CAACL,IAAI,CAAC;AAE5D,OAAO,MAAMM,gBAAgB,GAAGP,MAAM,CAACQ,MAAM,CAAEF,KAAK,IAAKA,KAAK,CAACH,IAAI,CAAC,CAACE,GAAG,CACrEC,KAAK,IAAKA,KAAK,CAACL,IACnB,CAAC;AAED,OAAO,MAAMQ,WAAW,GAAIR,IAAoB,IAC9CS,OAAO,CAACT,IAAI,CAAC,IAAIM,gBAAgB,CAACI,QAAQ,CAACV,IAAc,CAAC;;AAE5D;AACA;;AAEA;AACA,OAAO,MAAMW,gBAAgB,YAAUL,gBAAgB,CAACF,GAAG,CACxDJ,IAAI,UAASA,IAChB,CAAC,CAACY,IAAI,CAAC,IAAI,CAAC,MAAG;;AAEf;AACA;AACA,OAAO,MAAMC,mBAAmB,SAAOF,gBAAkB","ignoreList":[]}
1
+ {"version":3,"file":"themes.js","names":["THEMES","name","title","dark","swatch","THEME_NAMES","map","theme","DARK_THEME_NAMES","filter","LIGHT_THEME_NAMES","DEFAULT_THEME","DEFAULT_DARK_THEME","THEME_MODE_SINGLE","THEME_MODE_SYSTEM","isDarkTheme","Boolean","includes","knownTheme","fallback","resolveTheme","_ref","prefersDark","mode","themeLight","themeDark","DARK_THEME_MATCH","join","DARK_THEME_SELECTOR"],"sources":["../../../src/utils/themes.ts"],"sourcesContent":["// Single source of truth for theme identity. The theme name is the only class put on <html>;\n// styling that targets \"any dark theme\" uses DARK_THEME_SELECTOR, which is derived from this list\n// so a new theme never needs the selector updating by hand.\nexport interface ThemeDescriptor {\n name: string;\n title: string;\n dark: boolean;\n // [primary, secondary, page background] — accents lead so the swatch reads as the theme's colour.\n // Mirrors GlobalStyle; themes.swatch.test.ts fails if the two drift apart.\n swatch: [string, string, string];\n}\n\nexport const THEMES: ThemeDescriptor[] = [\n {\n name: \"indigo\",\n title: \"Indigo\",\n dark: false,\n swatch: [\"#5d2bff\", \"#5d2bff\", \"#f4f5f7\"],\n },\n {\n name: \"cupcake\",\n title: \"Cupcake\",\n dark: false,\n swatch: [\"#29bcdd\", \"#fe6fa7\", \"#f4f5f7\"],\n },\n {\n name: \"classic\",\n title: \"Classic\",\n dark: false,\n swatch: [\"#4182f2\", \"#4182f2\", \"#f4f5f7\"],\n },\n {\n name: \"neon\",\n title: \"Neon\",\n dark: true,\n swatch: [\"#67ffc8\", \"#67ffc8\", \"#2c2e38\"],\n },\n {\n name: \"dracula\",\n title: \"Dracula\",\n dark: true,\n swatch: [\"#bd93f9\", \"#ff79c6\", \"#282a36\"],\n },\n {\n name: \"one-dark\",\n title: \"One Dark\",\n dark: true,\n swatch: [\"#61afef\", \"#c678dd\", \"#282c34\"],\n },\n];\n\nexport const THEME_NAMES = THEMES.map((theme) => theme.name);\n\nexport const DARK_THEME_NAMES = THEMES.filter((theme) => theme.dark).map(\n (theme) => theme.name\n);\n\nexport const LIGHT_THEME_NAMES = THEMES.filter((theme) => !theme.dark).map(\n (theme) => theme.name\n);\n\nexport const DEFAULT_THEME = \"indigo\";\nexport const DEFAULT_DARK_THEME = \"neon\";\n\nexport const THEME_MODE_SINGLE = \"single\";\nexport const THEME_MODE_SYSTEM = \"system\";\n\nexport interface ThemeSelection {\n mode?: string | null;\n theme?: string | null;\n themeLight?: string | null;\n themeDark?: string | null;\n}\n\nexport const isDarkTheme = (name?: string | null): boolean =>\n Boolean(name) && DARK_THEME_NAMES.includes(name as string);\n\nconst knownTheme = (\n name: string | null | undefined,\n fallback: string\n): string => (name && THEME_NAMES.includes(name) ? name : fallback);\n\n// Resolution is derived per render, never persisted — writing it back would destroy the user's pick.\nexport const resolveTheme = (\n { mode, theme, themeLight, themeDark }: ThemeSelection,\n prefersDark: boolean\n): string => {\n if (mode !== THEME_MODE_SYSTEM) {\n return knownTheme(theme, DEFAULT_THEME);\n }\n\n return prefersDark\n ? knownTheme(themeDark, DEFAULT_DARK_THEME)\n : knownTheme(themeLight, DEFAULT_THEME);\n};\n\n// :is() and not :where() — :where() is zero-specificity and would silently lose to the light\n// declarations it is meant to override.\n\n// Compound onto an element that carries the theme class itself: `html${DARK_THEME_MATCH}`.\nexport const DARK_THEME_MATCH = `:is(${DARK_THEME_NAMES.map(\n (name) => `.${name}`\n).join(\", \")})`;\n\n// Stand-alone ancestor selector: `${DARK_THEME_SELECTOR} &`. The leading `*` adds no specificity\n// but stops stylis from reading a leading `:` as \"attach to the parent\" and gluing it onto `&`.\nexport const DARK_THEME_SELECTOR = `*${DARK_THEME_MATCH}`;\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=themes.swatch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.swatch.test.d.ts","sourceRoot":"","sources":["../../../src/utils/themes.swatch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,39 @@
1
+ import { readFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { THEMES } from "./themes";
4
+ const GLOBAL_STYLE = readFileSync(join(__dirname, "../components/GlobalStyle.ts"), "utf-8");
5
+ const SWATCH_VARS = ["color-primary", "color-secondary", "body-bg-main"];
6
+ const collectBlocks = selector => {
7
+ const re = new RegExp(selector + "\\s*\\{([^}]*)\\}", "g");
8
+ let declarations = "";
9
+ let match;
10
+ while (match = re.exec(GLOBAL_STYLE)) declarations += match[1] + ";";
11
+ return declarations;
12
+ };
13
+ const readVar = (block, name) => {
14
+ var _block$match;
15
+ return (_block$match = block.match(new RegExp("--" + name + "\\s*:\\s*([^;}]+)"))) == null ? void 0 : _block$match[1].trim();
16
+ };
17
+ const toHex = value => {
18
+ const rgb = value.match(/rgb\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)\s*\)/);
19
+ if (!rgb) return value.toLowerCase();
20
+ return "#" + rgb.slice(1).map(channel => Number(channel).toString(16).padStart(2, "0")).join("");
21
+ };
22
+
23
+ // indigo has no class block of its own — it is the :root default every other theme overrides.
24
+ const declarationsFor = name => (name === "indigo" ? "" : collectBlocks("\\." + name)) + collectBlocks(":root");
25
+ describe("theme swatches", () => {
26
+ it("should match the colours GlobalStyle defines for each theme", () => {
27
+ const fromStylesheet = THEMES.map(_ref => {
28
+ let name = _ref.name;
29
+ const block = declarationsFor(name);
30
+ return [name, SWATCH_VARS.map(cssVar => toHex(readVar(block, cssVar)))];
31
+ });
32
+ expect(fromStylesheet).toEqual(THEMES.map(_ref2 => {
33
+ let name = _ref2.name,
34
+ swatch = _ref2.swatch;
35
+ return [name, swatch];
36
+ }));
37
+ });
38
+ });
39
+ //# sourceMappingURL=themes.swatch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.swatch.test.js","names":["readFileSync","join","THEMES","GLOBAL_STYLE","__dirname","SWATCH_VARS","collectBlocks","selector","re","RegExp","declarations","match","exec","readVar","block","name","_block$match","trim","toHex","value","rgb","toLowerCase","slice","map","channel","Number","toString","padStart","declarationsFor","describe","it","fromStylesheet","_ref","cssVar","expect","toEqual","_ref2","swatch"],"sources":["../../../src/utils/themes.swatch.test.ts"],"sourcesContent":["import { readFileSync } from \"fs\";\nimport { join } from \"path\";\n\nimport { THEMES } from \"./themes\";\n\nconst GLOBAL_STYLE = readFileSync(\n join(__dirname, \"../components/GlobalStyle.ts\"),\n \"utf-8\"\n);\n\nconst SWATCH_VARS = [\"color-primary\", \"color-secondary\", \"body-bg-main\"];\n\nconst collectBlocks = (selector: string): string => {\n const re = new RegExp(`${selector}\\\\s*\\\\{([^}]*)\\\\}`, \"g\");\n let declarations = \"\";\n let match;\n while ((match = re.exec(GLOBAL_STYLE))) declarations += `${match[1]};`;\n return declarations;\n};\n\nconst readVar = (block: string, name: string): string | undefined =>\n block.match(new RegExp(`--${name}\\\\s*:\\\\s*([^;}]+)`))?.[1].trim();\n\nconst toHex = (value: string): string => {\n const rgb = value.match(/rgb\\(\\s*(\\d+)[,\\s]+(\\d+)[,\\s]+(\\d+)\\s*\\)/);\n if (!rgb) return value.toLowerCase();\n return `#${rgb\n .slice(1)\n .map((channel) => Number(channel).toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n};\n\n// indigo has no class block of its own — it is the :root default every other theme overrides.\nconst declarationsFor = (name: string): string =>\n (name === \"indigo\" ? \"\" : collectBlocks(`\\\\.${name}`)) +\n collectBlocks(\":root\");\n\ndescribe(\"theme swatches\", () => {\n it(\"should match the colours GlobalStyle defines for each theme\", () => {\n const fromStylesheet = THEMES.map(({ name }) => {\n const block = declarationsFor(name);\n return [\n name,\n SWATCH_VARS.map((cssVar) => toHex(readVar(block, cssVar) as string)),\n ];\n });\n\n expect(fromStylesheet).toEqual(\n THEMES.map(({ name, swatch }) => [name, swatch])\n );\n });\n});\n"],"mappings":"AAAA,SAASA,YAAY,QAAQ,IAAI;AACjC,SAASC,IAAI,QAAQ,MAAM;AAE3B,SAASC,MAAM,QAAQ,UAAU;AAEjC,MAAMC,YAAY,GAAGH,YAAY,CAC/BC,IAAI,CAACG,SAAS,EAAE,8BAA8B,CAAC,EAC/C,OACF,CAAC;AAED,MAAMC,WAAW,GAAG,CAAC,eAAe,EAAE,iBAAiB,EAAE,cAAc,CAAC;AAExE,MAAMC,aAAa,GAAIC,QAAgB,IAAa;EAClD,MAAMC,EAAE,GAAG,IAAIC,MAAM,CAAIF,QAAQ,wBAAqB,GAAG,CAAC;EAC1D,IAAIG,YAAY,GAAG,EAAE;EACrB,IAAIC,KAAK;EACT,OAAQA,KAAK,GAAGH,EAAE,CAACI,IAAI,CAACT,YAAY,CAAC,EAAGO,YAAY,IAAOC,KAAK,CAAC,CAAC,CAAC,MAAG;EACtE,OAAOD,YAAY;AACrB,CAAC;AAED,MAAMG,OAAO,GAAGA,CAACC,KAAa,EAAEC,IAAY;EAAA,IAAAC,YAAA;EAAA,QAAAA,YAAA,GAC1CF,KAAK,CAACH,KAAK,CAAC,IAAIF,MAAM,QAAMM,IAAI,sBAAmB,CAAC,CAAC,qBAArDC,YAAA,CAAwD,CAAC,CAAC,CAACC,IAAI,CAAC,CAAC;AAAA;AAEnE,MAAMC,KAAK,GAAIC,KAAa,IAAa;EACvC,MAAMC,GAAG,GAAGD,KAAK,CAACR,KAAK,CAAC,0CAA0C,CAAC;EACnE,IAAI,CAACS,GAAG,EAAE,OAAOD,KAAK,CAACE,WAAW,CAAC,CAAC;EACpC,aAAWD,GAAG,CACXE,KAAK,CAAC,CAAC,CAAC,CACRC,GAAG,CAAEC,OAAO,IAAKC,MAAM,CAACD,OAAO,CAAC,CAACE,QAAQ,CAAC,EAAE,CAAC,CAACC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAC/D1B,IAAI,CAAC,EAAE,CAAC;AACb,CAAC;;AAED;AACA,MAAM2B,eAAe,GAAIb,IAAY,IACnC,CAACA,IAAI,KAAK,QAAQ,GAAG,EAAE,GAAGT,aAAa,SAAOS,IAAM,CAAC,IACrDT,aAAa,CAAC,OAAO,CAAC;AAExBuB,QAAQ,CAAC,gBAAgB,EAAE,MAAM;EAC/BC,EAAE,CAAC,6DAA6D,EAAE,MAAM;IACtE,MAAMC,cAAc,GAAG7B,MAAM,CAACqB,GAAG,CAACS,IAAA,IAAc;MAAA,IAAXjB,IAAI,GAAAiB,IAAA,CAAJjB,IAAI;MACvC,MAAMD,KAAK,GAAGc,eAAe,CAACb,IAAI,CAAC;MACnC,OAAO,CACLA,IAAI,EACJV,WAAW,CAACkB,GAAG,CAAEU,MAAM,IAAKf,KAAK,CAACL,OAAO,CAACC,KAAK,EAAEmB,MAAM,CAAW,CAAC,CAAC,CACrE;IACH,CAAC,CAAC;IAEFC,MAAM,CAACH,cAAc,CAAC,CAACI,OAAO,CAC5BjC,MAAM,CAACqB,GAAG,CAACa,KAAA;MAAA,IAAGrB,IAAI,GAAAqB,KAAA,CAAJrB,IAAI;QAAEsB,MAAM,GAAAD,KAAA,CAANC,MAAM;MAAA,OAAO,CAACtB,IAAI,EAAEsB,MAAM,CAAC;IAAA,EACjD,CAAC;EACH,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=themes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.test.d.ts","sourceRoot":"","sources":["../../../src/utils/themes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ import { DEFAULT_DARK_THEME, DEFAULT_THEME, DARK_THEME_NAMES, LIGHT_THEME_NAMES, THEME_MODE_SINGLE, THEME_MODE_SYSTEM, resolveTheme } from "./themes";
2
+ describe("resolveTheme", () => {
3
+ it("should split the theme list into light and dark without overlap", () => {
4
+ expect(LIGHT_THEME_NAMES).toEqual(["indigo", "cupcake", "classic"]);
5
+ expect(DARK_THEME_NAMES).toEqual(["neon", "dracula", "one-dark"]);
6
+ });
7
+ it("should ignore the system appearance in single mode", () => {
8
+ const selection = {
9
+ mode: THEME_MODE_SINGLE,
10
+ theme: "dracula",
11
+ themeLight: "cupcake",
12
+ themeDark: "one-dark"
13
+ };
14
+ expect(resolveTheme(selection, false)).toBe("dracula");
15
+ expect(resolveTheme(selection, true)).toBe("dracula");
16
+ });
17
+ it("should follow the system appearance in system mode", () => {
18
+ const selection = {
19
+ mode: THEME_MODE_SYSTEM,
20
+ theme: "dracula",
21
+ themeLight: "cupcake",
22
+ themeDark: "one-dark"
23
+ };
24
+ expect(resolveTheme(selection, false)).toBe("cupcake");
25
+ expect(resolveTheme(selection, true)).toBe("one-dark");
26
+ });
27
+ it("should fall back to the defaults when a slot is missing or unknown", () => {
28
+ expect(resolveTheme({
29
+ mode: THEME_MODE_SYSTEM
30
+ }, false)).toBe(DEFAULT_THEME);
31
+ expect(resolveTheme({
32
+ mode: THEME_MODE_SYSTEM
33
+ }, true)).toBe(DEFAULT_DARK_THEME);
34
+ expect(resolveTheme({
35
+ mode: THEME_MODE_SYSTEM,
36
+ themeLight: "solarized",
37
+ themeDark: "solarized"
38
+ }, false)).toBe(DEFAULT_THEME);
39
+ expect(resolveTheme({
40
+ mode: THEME_MODE_SYSTEM,
41
+ themeLight: "solarized",
42
+ themeDark: "solarized"
43
+ }, true)).toBe(DEFAULT_DARK_THEME);
44
+ expect(resolveTheme({
45
+ theme: "solarized"
46
+ }, false)).toBe(DEFAULT_THEME);
47
+ });
48
+ it("should treat an absent or unrecognised mode as single", () => {
49
+ expect(resolveTheme({
50
+ theme: "neon"
51
+ }, false)).toBe("neon");
52
+ expect(resolveTheme({
53
+ mode: "auto",
54
+ theme: "neon"
55
+ }, true)).toBe("neon");
56
+ });
57
+ });
58
+ //# sourceMappingURL=themes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.test.js","names":["DEFAULT_DARK_THEME","DEFAULT_THEME","DARK_THEME_NAMES","LIGHT_THEME_NAMES","THEME_MODE_SINGLE","THEME_MODE_SYSTEM","resolveTheme","describe","it","expect","toEqual","selection","mode","theme","themeLight","themeDark","toBe"],"sources":["../../../src/utils/themes.test.ts"],"sourcesContent":["import {\n DEFAULT_DARK_THEME,\n DEFAULT_THEME,\n DARK_THEME_NAMES,\n LIGHT_THEME_NAMES,\n THEME_MODE_SINGLE,\n THEME_MODE_SYSTEM,\n resolveTheme,\n} from \"./themes\";\n\ndescribe(\"resolveTheme\", () => {\n it(\"should split the theme list into light and dark without overlap\", () => {\n expect(LIGHT_THEME_NAMES).toEqual([\"indigo\", \"cupcake\", \"classic\"]);\n expect(DARK_THEME_NAMES).toEqual([\"neon\", \"dracula\", \"one-dark\"]);\n });\n\n it(\"should ignore the system appearance in single mode\", () => {\n const selection = {\n mode: THEME_MODE_SINGLE,\n theme: \"dracula\",\n themeLight: \"cupcake\",\n themeDark: \"one-dark\",\n };\n\n expect(resolveTheme(selection, false)).toBe(\"dracula\");\n expect(resolveTheme(selection, true)).toBe(\"dracula\");\n });\n\n it(\"should follow the system appearance in system mode\", () => {\n const selection = {\n mode: THEME_MODE_SYSTEM,\n theme: \"dracula\",\n themeLight: \"cupcake\",\n themeDark: \"one-dark\",\n };\n\n expect(resolveTheme(selection, false)).toBe(\"cupcake\");\n expect(resolveTheme(selection, true)).toBe(\"one-dark\");\n });\n\n it(\"should fall back to the defaults when a slot is missing or unknown\", () => {\n expect(resolveTheme({ mode: THEME_MODE_SYSTEM }, false)).toBe(\n DEFAULT_THEME\n );\n expect(resolveTheme({ mode: THEME_MODE_SYSTEM }, true)).toBe(\n DEFAULT_DARK_THEME\n );\n expect(\n resolveTheme(\n {\n mode: THEME_MODE_SYSTEM,\n themeLight: \"solarized\",\n themeDark: \"solarized\",\n },\n false\n )\n ).toBe(DEFAULT_THEME);\n expect(\n resolveTheme(\n {\n mode: THEME_MODE_SYSTEM,\n themeLight: \"solarized\",\n themeDark: \"solarized\",\n },\n true\n )\n ).toBe(DEFAULT_DARK_THEME);\n expect(resolveTheme({ theme: \"solarized\" }, false)).toBe(DEFAULT_THEME);\n });\n\n it(\"should treat an absent or unrecognised mode as single\", () => {\n expect(resolveTheme({ theme: \"neon\" }, false)).toBe(\"neon\");\n expect(resolveTheme({ mode: \"auto\", theme: \"neon\" }, true)).toBe(\"neon\");\n });\n});\n"],"mappings":"AAAA,SACEA,kBAAkB,EAClBC,aAAa,EACbC,gBAAgB,EAChBC,iBAAiB,EACjBC,iBAAiB,EACjBC,iBAAiB,EACjBC,YAAY,QACP,UAAU;AAEjBC,QAAQ,CAAC,cAAc,EAAE,MAAM;EAC7BC,EAAE,CAAC,iEAAiE,EAAE,MAAM;IAC1EC,MAAM,CAACN,iBAAiB,CAAC,CAACO,OAAO,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACnED,MAAM,CAACP,gBAAgB,CAAC,CAACQ,OAAO,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;EACnE,CAAC,CAAC;EAEFF,EAAE,CAAC,oDAAoD,EAAE,MAAM;IAC7D,MAAMG,SAAS,GAAG;MAChBC,IAAI,EAAER,iBAAiB;MACvBS,KAAK,EAAE,SAAS;MAChBC,UAAU,EAAE,SAAS;MACrBC,SAAS,EAAE;IACb,CAAC;IAEDN,MAAM,CAACH,YAAY,CAACK,SAAS,EAAE,KAAK,CAAC,CAAC,CAACK,IAAI,CAAC,SAAS,CAAC;IACtDP,MAAM,CAACH,YAAY,CAACK,SAAS,EAAE,IAAI,CAAC,CAAC,CAACK,IAAI,CAAC,SAAS,CAAC;EACvD,CAAC,CAAC;EAEFR,EAAE,CAAC,oDAAoD,EAAE,MAAM;IAC7D,MAAMG,SAAS,GAAG;MAChBC,IAAI,EAAEP,iBAAiB;MACvBQ,KAAK,EAAE,SAAS;MAChBC,UAAU,EAAE,SAAS;MACrBC,SAAS,EAAE;IACb,CAAC;IAEDN,MAAM,CAACH,YAAY,CAACK,SAAS,EAAE,KAAK,CAAC,CAAC,CAACK,IAAI,CAAC,SAAS,CAAC;IACtDP,MAAM,CAACH,YAAY,CAACK,SAAS,EAAE,IAAI,CAAC,CAAC,CAACK,IAAI,CAAC,UAAU,CAAC;EACxD,CAAC,CAAC;EAEFR,EAAE,CAAC,oEAAoE,EAAE,MAAM;IAC7EC,MAAM,CAACH,YAAY,CAAC;MAAEM,IAAI,EAAEP;IAAkB,CAAC,EAAE,KAAK,CAAC,CAAC,CAACW,IAAI,CAC3Df,aACF,CAAC;IACDQ,MAAM,CAACH,YAAY,CAAC;MAAEM,IAAI,EAAEP;IAAkB,CAAC,EAAE,IAAI,CAAC,CAAC,CAACW,IAAI,CAC1DhB,kBACF,CAAC;IACDS,MAAM,CACJH,YAAY,CACV;MACEM,IAAI,EAAEP,iBAAiB;MACvBS,UAAU,EAAE,WAAW;MACvBC,SAAS,EAAE;IACb,CAAC,EACD,KACF,CACF,CAAC,CAACC,IAAI,CAACf,aAAa,CAAC;IACrBQ,MAAM,CACJH,YAAY,CACV;MACEM,IAAI,EAAEP,iBAAiB;MACvBS,UAAU,EAAE,WAAW;MACvBC,SAAS,EAAE;IACb,CAAC,EACD,IACF,CACF,CAAC,CAACC,IAAI,CAAChB,kBAAkB,CAAC;IAC1BS,MAAM,CAACH,YAAY,CAAC;MAAEO,KAAK,EAAE;IAAY,CAAC,EAAE,KAAK,CAAC,CAAC,CAACG,IAAI,CAACf,aAAa,CAAC;EACzE,CAAC,CAAC;EAEFO,EAAE,CAAC,uDAAuD,EAAE,MAAM;IAChEC,MAAM,CAACH,YAAY,CAAC;MAAEO,KAAK,EAAE;IAAO,CAAC,EAAE,KAAK,CAAC,CAAC,CAACG,IAAI,CAAC,MAAM,CAAC;IAC3DP,MAAM,CAACH,YAAY,CAAC;MAAEM,IAAI,EAAE,MAAM;MAAEC,KAAK,EAAE;IAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAACG,IAAI,CAAC,MAAM,CAAC;EAC1E,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
package/dist/index.js CHANGED
@@ -626,27 +626,33 @@
626
626
  var THEMES = [{
627
627
  name: "indigo",
628
628
  title: "Indigo",
629
- dark: false
629
+ dark: false,
630
+ swatch: ["#5d2bff", "#5d2bff", "#f4f5f7"]
630
631
  }, {
631
632
  name: "cupcake",
632
633
  title: "Cupcake",
633
- dark: false
634
+ dark: false,
635
+ swatch: ["#29bcdd", "#fe6fa7", "#f4f5f7"]
634
636
  }, {
635
637
  name: "classic",
636
638
  title: "Classic",
637
- dark: false
639
+ dark: false,
640
+ swatch: ["#4182f2", "#4182f2", "#f4f5f7"]
638
641
  }, {
639
642
  name: "neon",
640
643
  title: "Neon",
641
- dark: true
644
+ dark: true,
645
+ swatch: ["#67ffc8", "#67ffc8", "#2c2e38"]
642
646
  }, {
643
647
  name: "dracula",
644
648
  title: "Dracula",
645
- dark: true
649
+ dark: true,
650
+ swatch: ["#bd93f9", "#ff79c6", "#282a36"]
646
651
  }, {
647
652
  name: "one-dark",
648
653
  title: "One Dark",
649
- dark: true
654
+ dark: true,
655
+ swatch: ["#61afef", "#c678dd", "#282c34"]
650
656
  }];
651
657
  var THEME_NAMES = THEMES.map(function (theme) {
652
658
  return theme.name;
@@ -656,9 +662,33 @@
656
662
  }).map(function (theme) {
657
663
  return theme.name;
658
664
  });
665
+ var LIGHT_THEME_NAMES = THEMES.filter(function (theme) {
666
+ return !theme.dark;
667
+ }).map(function (theme) {
668
+ return theme.name;
669
+ });
670
+ var DEFAULT_THEME = "indigo";
671
+ var DEFAULT_DARK_THEME = "neon";
672
+ var THEME_MODE_SINGLE = "single";
673
+ var THEME_MODE_SYSTEM = "system";
659
674
  var isDarkTheme = function isDarkTheme(name) {
660
675
  return Boolean(name) && DARK_THEME_NAMES.includes(name);
661
676
  };
677
+ var knownTheme = function knownTheme(name, fallback) {
678
+ return name && THEME_NAMES.includes(name) ? name : fallback;
679
+ };
680
+
681
+ // Resolution is derived per render, never persisted — writing it back would destroy the user's pick.
682
+ var resolveTheme = function resolveTheme(_ref, prefersDark) {
683
+ var mode = _ref.mode,
684
+ theme = _ref.theme,
685
+ themeLight = _ref.themeLight,
686
+ themeDark = _ref.themeDark;
687
+ if (mode !== THEME_MODE_SYSTEM) {
688
+ return knownTheme(theme, DEFAULT_THEME);
689
+ }
690
+ return prefersDark ? knownTheme(themeDark, DEFAULT_DARK_THEME) : knownTheme(themeLight, DEFAULT_THEME);
691
+ };
662
692
 
663
693
  // :is() and not :where() — :where() is zero-specificity and would silently lose to the light
664
694
  // declarations it is meant to override.
@@ -28821,7 +28851,9 @@
28821
28851
  exports.DARK_THEME_MATCH = DARK_THEME_MATCH;
28822
28852
  exports.DARK_THEME_NAMES = DARK_THEME_NAMES;
28823
28853
  exports.DARK_THEME_SELECTOR = DARK_THEME_SELECTOR;
28854
+ exports.DEFAULT_DARK_THEME = DEFAULT_DARK_THEME;
28824
28855
  exports.DEFAULT_KEYBOARD_STEP = DEFAULT_KEYBOARD_STEP;
28856
+ exports.DEFAULT_THEME = DEFAULT_THEME;
28825
28857
  exports.DataTable = DataTable;
28826
28858
  exports.DatePicker = DatePicker;
28827
28859
  exports.DateStepper = DateStepper;
@@ -28926,6 +28958,7 @@
28926
28958
  exports.Item = Item;
28927
28959
  exports.ItemContext = ItemContext;
28928
28960
  exports.KeyIcon = KeyIcon$1;
28961
+ exports.LIGHT_THEME_NAMES = LIGHT_THEME_NAMES;
28929
28962
  exports.Label = Label;
28930
28963
  exports.LabelsIcon = LabelsIcon$1;
28931
28964
  exports.LayerBackIcon = LayerBackIcon$1;
@@ -29099,6 +29132,8 @@
29099
29132
  exports.SunIcon = SunIcon$1;
29100
29133
  exports.SystemSettingsIcon = SystemSettingsIcon$1;
29101
29134
  exports.THEMES = THEMES;
29135
+ exports.THEME_MODE_SINGLE = THEME_MODE_SINGLE;
29136
+ exports.THEME_MODE_SYSTEM = THEME_MODE_SYSTEM;
29102
29137
  exports.THEME_NAMES = THEME_NAMES;
29103
29138
  exports.Table = Table;
29104
29139
  exports.Tag = Tag;
@@ -29166,6 +29201,7 @@
29166
29201
  exports.makeStart = makeStart;
29167
29202
  exports.measurePercent = measurePercent;
29168
29203
  exports.numberWithSeparator = numberWithSeparator;
29204
+ exports.resolveTheme = resolveTheme;
29169
29205
  exports.signifierTypes = signifierTypes;
29170
29206
  exports.snap = snap;
29171
29207
  exports.toMoment = toMoment;