@ekkolyth/ui 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -132
  3. package/dist/base.css +263 -0
  4. package/dist/blocks.d.ts +4 -0
  5. package/dist/blocks.js +4 -0
  6. package/dist/container-BBg21poC.js +10 -0
  7. package/dist/convex.d.ts +2 -0
  8. package/dist/convex.js +2 -0
  9. package/dist/hooks.d.ts +2 -0
  10. package/dist/hooks.js +2 -0
  11. package/dist/icon-picker-ByfHyJ7i.js +4078 -0
  12. package/dist/index-Bwp0YMmG.d.ts +5 -0
  13. package/dist/index-BxcqwZNT.d.ts +30 -0
  14. package/dist/index-DHF8OR1R.d.ts +60 -0
  15. package/dist/index-DsLGf2HX.d.ts +9 -0
  16. package/dist/index-qji4ohJZ.d.ts +24 -0
  17. package/dist/index.d.ts +793 -533
  18. package/dist/index.js +3236 -5578
  19. package/dist/layout.d.ts +2 -0
  20. package/dist/layout.js +2 -0
  21. package/dist/loading-screen-DB3youyT.js +63 -0
  22. package/dist/shiki-code-Bg__Vnt1.js +29 -0
  23. package/dist/text.d.ts +17 -0
  24. package/dist/text.js +177 -0
  25. package/dist/themes/catpuccin/frappe.css +33 -33
  26. package/dist/themes/catpuccin/latte.css +33 -33
  27. package/dist/themes/catpuccin/macchiato.css +33 -33
  28. package/dist/themes/catpuccin/mocha.css +33 -33
  29. package/dist/themes/ekko-os.css +92 -37
  30. package/dist/themes/ekkolyth.css +2 -2
  31. package/dist/themes/index.css +7 -66
  32. package/dist/themes/provider.d.ts +62 -0
  33. package/dist/themes/provider.js +271 -0
  34. package/dist/themes/tokyo-night.css +33 -33
  35. package/dist/themes.css +7 -66
  36. package/dist/toggle-B2PFPImC.js +182 -0
  37. package/dist/use-mobile-BVGje-2e.d.ts +4 -0
  38. package/dist/use-mobile-CqHXB_Nc.js +18 -0
  39. package/dist/utils-C_uf36nf.js +8 -0
  40. package/dist/widget-C2vjxQms.js +94 -0
  41. package/dist/widget.d.ts +6 -0
  42. package/dist/widget.js +19 -0
  43. package/package.json +125 -170
  44. package/dist/catpuccin/frappe.css +0 -35
  45. package/dist/catpuccin/latte.css +0 -35
  46. package/dist/catpuccin/macchiato.css +0 -35
  47. package/dist/catpuccin/mocha.css +0 -35
  48. package/dist/ekko-os.css +0 -71
  49. package/dist/ekko-playlist.css +0 -71
  50. package/dist/ekkolyth.css +0 -70
  51. package/dist/index-C3ceanPz.d.ts +0 -18
  52. package/dist/index-C7t_lwpi.d.ts +0 -13
  53. package/dist/next.d.ts +0 -13
  54. package/dist/next.js +0 -271
  55. package/dist/providers/next.d.ts +0 -25
  56. package/dist/providers/next.js +0 -216
  57. package/dist/providers/tanstack.d.ts +0 -24
  58. package/dist/providers/tanstack.js +0 -120
  59. package/dist/tanstack.d.ts +0 -12
  60. package/dist/tanstack.js +0 -203
  61. package/dist/themes/ekko-playlist.css +0 -71
  62. package/dist/themes/theme-select.d.ts +0 -12
  63. package/dist/themes/theme-select.js +0 -357
  64. package/dist/themes/theme-toggle.d.ts +0 -15
  65. package/dist/themes/theme-toggle.js +0 -179
  66. package/dist/tokyo-night.css +0 -35
  67. package/dist/types-fUfvw1Mz.d.ts +0 -8
@@ -1,8 +1,61 @@
1
- /* EkkoOS - Light (from EkkoOS apps/web globals.css :root) */
2
- html[data-theme="ekkoos-light"] {
3
- --radius: 0.25rem;
4
- --background: oklch(1 0 0);
1
+ /* Canonical ekko-os tokens. apps/web imports this and no longer carries
2
+ * its own :root / .dark blocks, so edits land here, not there. */
3
+
4
+ @theme inline {
5
+ --color-background: var(--background);
6
+ --color-foreground: var(--foreground);
7
+ --color-background-notes: var(--background-notes);
8
+ --color-foreground-notes: var(--foreground-notes);
9
+ --color-sidebar-ring: var(--sidebar-ring);
10
+ --color-sidebar-border: var(--sidebar-border);
11
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
12
+ --color-sidebar-accent: var(--sidebar-accent);
13
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
14
+ --color-sidebar-primary: var(--sidebar-primary);
15
+ --color-sidebar-foreground: var(--sidebar-foreground);
16
+ --color-sidebar: var(--sidebar);
17
+ --color-chart-5: var(--chart-5);
18
+ --color-chart-4: var(--chart-4);
19
+ --color-chart-3: var(--chart-3);
20
+ --color-chart-2: var(--chart-2);
21
+ --color-chart-1: var(--chart-1);
22
+ --color-ring: var(--ring);
23
+ --color-input: var(--input);
24
+ --color-border: var(--border);
25
+ --color-destructive: var(--destructive);
26
+ --color-success: var(--success);
27
+ --color-warning: var(--warning);
28
+ --color-info: var(--info);
29
+ --color-accent-foreground: var(--accent-foreground);
30
+ --color-accent: var(--accent);
31
+ --color-muted-foreground: var(--muted-foreground);
32
+ --color-muted: var(--muted);
33
+ --color-secondary-foreground: var(--secondary-foreground);
34
+ --color-secondary: var(--secondary);
35
+ --color-primary-foreground: var(--primary-foreground);
36
+ --color-primary: var(--primary);
37
+ --color-popover-foreground: var(--popover-foreground);
38
+ --color-popover: var(--popover);
39
+ --color-card-foreground: var(--card-foreground);
40
+ --color-card: var(--card);
41
+ --radius-sm: calc(var(--radius) - 4px);
42
+ --radius-md: calc(var(--radius) - 2px);
43
+ --radius-lg: var(--radius);
44
+ --radius-xl: calc(var(--radius) + 4px);
45
+ }
46
+
47
+ :root,
48
+ html[data-theme='ekkoos-light'] {
49
+ /* EkkoOS is the one palette that overrides the package's default face.
50
+ * Scoped, not global: a @theme entry here would set it for every theme. */
51
+ --font-sans: 'SatoshiVariable', sans-serif;
52
+ --font-atkinson-mono:
53
+ 'Atkinson Hyperlegible Mono Variable', ui-monospace, monospace;
54
+ --radius: 0.625rem;
55
+ --background: oklch(0.98 0.001 286.375);
5
56
  --foreground: oklch(0.141 0.005 285.823);
57
+ --background-notes: oklch(0.98 0.001 286.375);
58
+ --foreground-notes: oklch(0.141 0.005 285.823);
6
59
  --card: oklch(1 0 0);
7
60
  --card-foreground: oklch(0.141 0.005 285.823);
8
61
  --popover: oklch(1 0 0);
@@ -15,31 +68,37 @@ html[data-theme="ekkoos-light"] {
15
68
  --muted-foreground: oklch(0.552 0.016 285.938);
16
69
  --accent: oklch(0.967 0.001 286.375);
17
70
  --accent-foreground: oklch(0.21 0.006 285.885);
18
- --destructive: oklch(0.577 0.245 27.325);
19
- --border: oklch(0.92 0.004 286.32);
20
- --input: oklch(0.92 0.004 286.32);
71
+ --destructive: oklch(0.645 0.246 16.439);
72
+ --success: oklch(0.696 0.17 162.48);
73
+ --warning: oklch(0.769 0.188 70.08);
74
+ --info: oklch(0.62 0.14 250);
75
+ --border: oklch(0.885 0.004 286.32);
76
+ --input: oklch(0.94 0.002 286.375);
21
77
  --ring: oklch(0.705 0.015 286.067);
22
78
  --chart-1: oklch(0.646 0.222 41.116);
23
79
  --chart-2: oklch(0.6 0.118 184.704);
24
80
  --chart-3: oklch(0.398 0.07 227.392);
25
81
  --chart-4: oklch(0.828 0.189 84.429);
26
82
  --chart-5: oklch(0.769 0.188 70.08);
27
- --sidebar: oklch(0.985 0 0);
28
- --sidebar-foreground: oklch(0.141 0.005 285.823);
29
- --sidebar-primary: oklch(0.21 0.006 285.885);
30
- --sidebar-primary-foreground: oklch(0.985 0 0);
31
- --sidebar-accent: oklch(0.967 0.001 286.375);
32
- --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
33
- --sidebar-border: oklch(0.92 0.004 286.32);
34
- --sidebar-ring: oklch(0.705 0.015 286.067);
83
+ --sidebar: oklch(0.1221 0 0);
84
+ --sidebar-foreground: oklch(1 0 0);
85
+ --sidebar-primary: oklch(0.8 0 0);
86
+ --sidebar-primary-foreground: oklch(0 0 0);
87
+ --sidebar-accent: oklch(0.2 0 0);
88
+ --sidebar-accent-foreground: oklch(0.9 0 0);
89
+ --sidebar-border: oklch(0.25 0 0);
90
+ --sidebar-ring: oklch(0.8 0 0);
35
91
  }
36
92
 
37
- /* EkkoOS - Dark (from EkkoOS apps/web globals.css .dark - monochrome) */
38
- html[data-theme="ekkoos-dark"] {
39
- --radius: 0.25rem;
40
- --background: oklch(0 0 0);
93
+ .dark,
94
+ html[data-theme='ekkoos-dark'] {
95
+ --font-sans: 'SatoshiVariable', sans-serif;
96
+ --background: oklch(0.1221 0 0);
41
97
  --foreground: oklch(1 0 0);
98
+ --background-notes: oklch(0.1719 0.0016 197);
99
+ --foreground-notes: oklch(1 0 0);
42
100
  --card: oklch(0.05 0 0 / 0.8);
101
+ --sidebar: oklch(0.05 0 0 / 0.8);
43
102
  --card-foreground: oklch(1 0 0);
44
103
  --popover: oklch(0.05 0 0);
45
104
  --popover-foreground: oklch(1 0 0);
@@ -49,23 +108,19 @@ html[data-theme="ekkoos-dark"] {
49
108
  --secondary-foreground: oklch(0.9 0 0);
50
109
  --muted: oklch(0.1 0 0);
51
110
  --muted-foreground: oklch(0.6 0 0);
52
- --accent: oklch(0.2 0 0);
53
- --accent-foreground: oklch(1 0 0);
54
- --destructive: oklch(0.62 0.25 29);
55
- --border: oklch(0.15 0 0);
56
- --input: oklch(0.08 0 0);
111
+ --accent: oklch(0.206 0.006 286);
112
+ --accent-foreground: oklch(0.985 0 0);
113
+ --destructive: oklch(0.712 0.194 13.428);
114
+ --success: oklch(0.765 0.177 163.223);
115
+ --warning: oklch(0.828 0.189 84.429);
116
+ --info: oklch(0.7 0.14 250);
117
+ --border: oklch(0.25 0 0);
118
+ --input: oklch(0.18 0 0);
57
119
  --ring: oklch(0.8 0 0);
58
- --chart-1: oklch(0.9 0 0);
59
- --chart-2: oklch(0.8 0 0);
60
- --chart-3: oklch(0.7 0 0);
61
- --chart-4: oklch(0.6 0 0);
62
- --chart-5: oklch(0.5 0 0);
63
- --sidebar: oklch(0 0 0);
64
- --sidebar-foreground: oklch(1 0 0);
65
- --sidebar-primary: oklch(0.8 0 0);
66
- --sidebar-primary-foreground: oklch(0 0 0);
67
- --sidebar-accent: oklch(0.2 0 0);
68
- --sidebar-accent-foreground: oklch(0.9 0 0);
69
- --sidebar-border: oklch(0.1 0 0);
70
- --sidebar-ring: oklch(0.8 0 0);
120
+ --chart-1: oklch(0.646 0.222 41.116);
121
+ --chart-2: oklch(0.6 0.118 184.704);
122
+ --chart-3: oklch(0.398 0.07 227.392);
123
+ --chart-4: oklch(0.828 0.189 84.429);
124
+ --chart-5: oklch(0.769 0.188 70.08);
125
+ --radius: 0.75rem;
71
126
  }
@@ -1,5 +1,5 @@
1
1
  /* Ekkolyth - Light */
2
- html[data-theme="ekkolyth-light"] {
2
+ html[data-theme='ekkolyth-light'] {
3
3
  --background: oklch(1 0 0);
4
4
  --foreground: oklch(0.141 0.005 285.823);
5
5
  --card: oklch(1 0 0);
@@ -35,7 +35,7 @@ html[data-theme="ekkolyth-light"] {
35
35
  }
36
36
 
37
37
  /* Ekkolyth - Dark */
38
- html[data-theme="ekkolyth-dark"] {
38
+ html[data-theme='ekkolyth-dark'] {
39
39
  --background: oklch(0.141 0.005 285.823);
40
40
  --foreground: oklch(0.985 0 0);
41
41
  --card: oklch(0.21 0.006 285.885);
@@ -1,66 +1,7 @@
1
- @import "tailwindcss";
2
- @import "tw-animate-css";
3
-
4
- /* Import all theme CSS files */
5
- @import "./themes/catpuccin/latte.css";
6
- @import "./themes/catpuccin/frappe.css";
7
- @import "./themes/catpuccin/macchiato.css";
8
- @import "./themes/catpuccin/mocha.css";
9
- @import "./themes/ekko-os.css";
10
- @import "./themes/tokyo-night.css";
11
- @import "./themes/ekkolyth.css";
12
- @import "./themes/ekko-playlist.css";
13
-
14
- @custom-variant dark (&:is(.dark *));
15
-
16
- @theme inline {
17
- --color-background: var(--background);
18
- --color-foreground: var(--foreground);
19
- --font-sans: var(--font-sans);
20
- --font-mono: var(--font-geist-mono);
21
- --color-sidebar-ring: var(--sidebar-ring);
22
- --color-sidebar-border: var(--sidebar-border);
23
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
24
- --color-sidebar-accent: var(--sidebar-accent);
25
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
26
- --color-sidebar-primary: var(--sidebar-primary);
27
- --color-sidebar-foreground: var(--sidebar-foreground);
28
- --color-sidebar: var(--sidebar);
29
- --color-chart-5: var(--chart-5);
30
- --color-chart-4: var(--chart-4);
31
- --color-chart-3: var(--chart-3);
32
- --color-chart-2: var(--chart-2);
33
- --color-chart-1: var(--chart-1);
34
- --color-ring: var(--ring);
35
- --color-input: var(--input);
36
- --color-border: var(--border);
37
- --color-destructive: var(--destructive);
38
- --color-accent-foreground: var(--accent-foreground);
39
- --color-accent: var(--accent);
40
- --color-muted-foreground: var(--muted-foreground);
41
- --color-muted: var(--muted);
42
- --color-secondary-foreground: var(--secondary-foreground);
43
- --color-secondary: var(--secondary);
44
- --color-primary-foreground: var(--primary-foreground);
45
- --color-primary: var(--primary);
46
- --color-popover-foreground: var(--popover-foreground);
47
- --color-popover: var(--popover);
48
- --color-card-foreground: var(--card-foreground);
49
- --color-card: var(--card);
50
- --radius-sm: calc(var(--radius) - 4px);
51
- --radius-md: calc(var(--radius) - 2px);
52
- --radius-lg: var(--radius);
53
- --radius-xl: calc(var(--radius) + 4px);
54
- --radius-2xl: calc(var(--radius) + 8px);
55
- --radius-3xl: calc(var(--radius) + 12px);
56
- --radius-4xl: calc(var(--radius) + 16px);
57
- }
58
-
59
- @layer base {
60
- * {
61
- @apply border-border outline-ring/50;
62
- }
63
- body {
64
- @apply bg-background text-foreground;
65
- }
66
- }
1
+ @import './catpuccin/latte.css';
2
+ @import './catpuccin/frappe.css';
3
+ @import './catpuccin/macchiato.css';
4
+ @import './catpuccin/mocha.css';
5
+ @import './ekko-os.css';
6
+ @import './tokyo-night.css';
7
+ @import './ekkolyth.css';
@@ -0,0 +1,62 @@
1
+ import { i as Select, r as Toaster, t as Toggle } from "../index-BxcqwZNT.js";
2
+ import * as React from "react";
3
+ import { PropsWithChildren, ReactNode } from "react";
4
+ //#region src/components/themes/components/theme-select/index.d.ts
5
+ interface ThemeSelectProps extends React.ComponentProps<typeof Select> {
6
+ className?: string;
7
+ size?: 'sm' | 'default';
8
+ }
9
+ declare function ThemeSelect({ size, className, ...props }: ThemeSelectProps): React.JSX.Element;
10
+ //#endregion
11
+ //#region src/components/themes/components/theme-toggle/index.d.ts
12
+ interface ThemeToggleProps extends React.ComponentProps<typeof Toggle> {
13
+ className?: string;
14
+ size?: 'default' | 'sm' | 'lg';
15
+ variant?: 'default' | 'outline';
16
+ }
17
+ declare function ThemeToggle({ variant, size, className, ...props }: ThemeToggleProps): React.JSX.Element;
18
+ //#endregion
19
+ //#region src/lib/types.d.ts
20
+ declare const APPEARANCE: readonly ["light", "dark", "system", "normal"];
21
+ type Appearance = (typeof APPEARANCE)[number];
22
+ declare const LIGHT_THEMES: readonly ["latte", "ekkoos-light", "ekkolyth-light"];
23
+ declare const DARK_THEMES: readonly ["mocha", "frappe", "macchiato", "tokyo-night", "ekkoos-dark", "ekkolyth-dark"];
24
+ type LightThemes = (typeof LIGHT_THEMES)[number];
25
+ type DarkThemes = (typeof DARK_THEMES)[number];
26
+ type ThemeName = LightThemes | DarkThemes;
27
+ //#endregion
28
+ //#region src/components/themes/context.d.ts
29
+ interface ThemeContextState {
30
+ appearance: Appearance;
31
+ availableThemes: {
32
+ dark: DarkThemes[];
33
+ light: LightThemes[];
34
+ };
35
+ resolvedMode: 'light' | 'dark';
36
+ resolvedTheme: ThemeName;
37
+ setAppearance: (appearance: Appearance) => void;
38
+ setSpecificTheme: (mode: 'light' | 'dark', theme: LightThemes | DarkThemes) => void;
39
+ }
40
+ interface ThemeProviderProps {
41
+ children?: ReactNode;
42
+ darkTheme?: DarkThemes | DarkThemes[];
43
+ defaultAppearance?: Appearance;
44
+ lightTheme?: LightThemes | LightThemes[];
45
+ storageKey?: string;
46
+ }
47
+ declare function useTheme(): ThemeContextState;
48
+ declare function useThemeContext(): ThemeContextState | null;
49
+ //#endregion
50
+ //#region src/components/themes/provider.d.ts
51
+ declare function ThemeProvider({ children, darkTheme, defaultAppearance, lightTheme, storageKey }: PropsWithChildren<ThemeProviderProps>): import("react").JSX.Element;
52
+ //#endregion
53
+ //#region src/components/themes/theme-script.d.ts
54
+ interface ThemeScriptProps {
55
+ darkTheme?: DarkThemes | DarkThemes[];
56
+ defaultAppearance?: Appearance;
57
+ lightTheme?: LightThemes | LightThemes[];
58
+ storageKey?: string;
59
+ }
60
+ declare function ThemeScript({ darkTheme, defaultAppearance, lightTheme, storageKey }: ThemeScriptProps): import("react").JSX.Element;
61
+ //#endregion
62
+ export { type Appearance, type DarkThemes, type LightThemes, type ThemeContextState, type ThemeName, ThemeProvider, type ThemeProviderProps, ThemeScript, type ThemeScriptProps, ThemeSelect, ThemeToggle, Toaster, useTheme, useThemeContext };
@@ -0,0 +1,271 @@
1
+ import { t as cn } from "../utils-C_uf36nf.js";
2
+ import { _ as SelectTrigger, a as applyTheme, c as useThemeContext, d as SelectGroup, f as SelectItem, i as ThemeContext, l as Select, o as normalizeMode, p as SelectLabel, r as Toaster, s as useTheme, t as Toggle, u as SelectContent, v as SelectValue } from "../toggle-B2PFPImC.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { Monitor, Moon, Sun } from "lucide-react";
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
+ //#region src/lib/types.ts
7
+ const APPEARANCE = [
8
+ "light",
9
+ "dark",
10
+ "system",
11
+ "normal"
12
+ ];
13
+ const LIGHT_THEMES = [
14
+ "latte",
15
+ "ekkoos-light",
16
+ "ekkolyth-light"
17
+ ];
18
+ const DARK_THEMES = [
19
+ "mocha",
20
+ "frappe",
21
+ "macchiato",
22
+ "tokyo-night",
23
+ "ekkoos-dark",
24
+ "ekkolyth-dark"
25
+ ];
26
+ //#endregion
27
+ //#region src/lib/theme.ts
28
+ function isLightTheme(theme) {
29
+ return LIGHT_THEMES.includes(theme);
30
+ }
31
+ function isDarkTheme(theme) {
32
+ return DARK_THEMES.includes(theme);
33
+ }
34
+ //#endregion
35
+ //#region src/components/themes/components/theme-select/index.tsx
36
+ function ThemeSelect({ size = "default", className, ...props }) {
37
+ const { availableThemes, resolvedTheme, setSpecificTheme, setAppearance } = useTheme();
38
+ const handleThemeChange = (value) => {
39
+ const theme = value;
40
+ if (isLightTheme(theme)) {
41
+ setSpecificTheme("light", theme);
42
+ setAppearance("light");
43
+ } else if (isDarkTheme(theme)) {
44
+ setSpecificTheme("dark", theme);
45
+ setAppearance("dark");
46
+ }
47
+ };
48
+ const formatThemeName = (theme) => {
49
+ if (theme === "ekkoos-light") return "EkkoOS Light";
50
+ if (theme === "ekkoos-dark") return "EkkoOS Dark";
51
+ if (theme === "tokyo-night") return "Tokyo Night";
52
+ return theme.charAt(0).toUpperCase() + theme.slice(1);
53
+ };
54
+ return /* @__PURE__ */ jsxs(Select, {
55
+ onValueChange: handleThemeChange,
56
+ value: resolvedTheme,
57
+ ...props,
58
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
59
+ className: cn(className),
60
+ size,
61
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select theme" })
62
+ }), /* @__PURE__ */ jsxs(SelectContent, { children: [availableThemes.light.length > 0 && /* @__PURE__ */ jsxs(SelectGroup, { children: [/* @__PURE__ */ jsx(SelectLabel, { children: "Light Themes" }), availableThemes.light.map((theme) => /* @__PURE__ */ jsx(SelectItem, {
63
+ value: theme,
64
+ children: formatThemeName(theme)
65
+ }, theme))] }), availableThemes.dark.length > 0 && /* @__PURE__ */ jsxs(SelectGroup, { children: [/* @__PURE__ */ jsx(SelectLabel, { children: "Dark Themes" }), availableThemes.dark.map((theme) => /* @__PURE__ */ jsx(SelectItem, {
66
+ value: theme,
67
+ children: formatThemeName(theme)
68
+ }, theme))] })] })]
69
+ });
70
+ }
71
+ //#endregion
72
+ //#region src/components/themes/components/theme-toggle/index.tsx
73
+ function ThemeToggle({ variant = "default", size = "default", className, ...props }) {
74
+ const { setAppearance, appearance } = useTheme();
75
+ const cycleTheme = () => {
76
+ const cycle = [
77
+ "light",
78
+ "dark",
79
+ "system"
80
+ ];
81
+ const nextIndex = (cycle.indexOf(appearance === "normal" ? "light" : appearance) + 1) % cycle.length;
82
+ setAppearance(cycle[nextIndex]);
83
+ };
84
+ const getIcon = () => {
85
+ switch (appearance === "normal" ? "light" : appearance) {
86
+ case "light": return /* @__PURE__ */ jsx(Sun, { className: "size-4" });
87
+ case "dark": return /* @__PURE__ */ jsx(Moon, { className: "size-4" });
88
+ case "system": return /* @__PURE__ */ jsx(Monitor, { className: "size-4" });
89
+ default: return /* @__PURE__ */ jsx(Monitor, { className: "size-4" });
90
+ }
91
+ };
92
+ return /* @__PURE__ */ jsx(Toggle, {
93
+ "aria-label": `Switch theme. Current: ${appearance}`,
94
+ className: cn(className),
95
+ onClick: cycleTheme,
96
+ size,
97
+ variant,
98
+ ...props,
99
+ children: getIcon()
100
+ });
101
+ }
102
+ //#endregion
103
+ //#region src/components/themes/storage.ts
104
+ const isMemberOf = (values) => (value) => values.includes(value);
105
+ const isAppearance = isMemberOf(APPEARANCE);
106
+ function readItem(key) {
107
+ if (typeof window === "undefined") return null;
108
+ try {
109
+ return window.localStorage.getItem(key);
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ function readStoredTheme(storageKey, lightThemes, darkThemes) {
115
+ const stored = {};
116
+ const isLightTheme = isMemberOf(lightThemes);
117
+ const isDarkTheme = isMemberOf(darkThemes);
118
+ const mode = readItem(`${storageKey}.theme-mode`);
119
+ if (mode !== null && isAppearance(mode)) stored.mode = mode;
120
+ const light = readItem(`${storageKey}.theme-light`);
121
+ if (light !== null && isLightTheme(light)) stored.light = light;
122
+ const dark = readItem(`${storageKey}.theme-dark`);
123
+ if (dark !== null && isDarkTheme(dark)) stored.dark = dark;
124
+ return stored;
125
+ }
126
+ function writeStoredTheme(storageKey, key, value) {
127
+ if (typeof window === "undefined") return;
128
+ try {
129
+ window.localStorage.setItem(`${storageKey}.${key}`, value);
130
+ } catch {
131
+ return;
132
+ }
133
+ }
134
+ //#endregion
135
+ //#region src/components/themes/provider.tsx
136
+ function systemPrefersDark() {
137
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
138
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
139
+ }
140
+ function ThemeProvider({ children, darkTheme = ["ekkolyth-dark"], defaultAppearance = "system", lightTheme = ["ekkolyth-light"], storageKey = "ekko-ui" }) {
141
+ const lightThemes = useMemo(() => Array.isArray(lightTheme) ? lightTheme : [lightTheme], [lightTheme]);
142
+ const darkThemes = useMemo(() => Array.isArray(darkTheme) ? darkTheme : [darkTheme], [darkTheme]);
143
+ const [appearance, setAppearanceState] = useState(defaultAppearance);
144
+ const [lightName, setLightName] = useState(lightThemes[0]);
145
+ const [darkName, setDarkName] = useState(darkThemes[0]);
146
+ const [systemIsDark, setSystemIsDark] = useState(false);
147
+ const [hasRestored, setHasRestored] = useState(false);
148
+ const normalizedAppearance = normalizeMode(appearance);
149
+ const resolvedMode = normalizedAppearance === "system" ? systemIsDark ? "dark" : "light" : normalizedAppearance;
150
+ const resolvedTheme = resolvedMode === "light" ? lightName : darkName;
151
+ const restoring = useRef(false);
152
+ useEffect(() => {
153
+ if (restoring.current) return;
154
+ restoring.current = true;
155
+ const stored = readStoredTheme(storageKey, lightThemes, darkThemes);
156
+ if (stored.mode !== void 0) setAppearanceState(stored.mode);
157
+ if (stored.light !== void 0) setLightName(stored.light);
158
+ if (stored.dark !== void 0) setDarkName(stored.dark);
159
+ setSystemIsDark(systemPrefersDark());
160
+ setHasRestored(true);
161
+ }, [
162
+ darkThemes,
163
+ lightThemes,
164
+ storageKey
165
+ ]);
166
+ useEffect(() => {
167
+ if (!hasRestored) return;
168
+ applyTheme(resolvedTheme, resolvedMode);
169
+ }, [
170
+ hasRestored,
171
+ resolvedMode,
172
+ resolvedTheme
173
+ ]);
174
+ useEffect(() => {
175
+ if (normalizeMode(appearance) !== "system") return;
176
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
177
+ const sync = () => setSystemIsDark(media.matches);
178
+ sync();
179
+ media.addEventListener("change", sync);
180
+ return () => media.removeEventListener("change", sync);
181
+ }, [appearance]);
182
+ useEffect(() => {
183
+ const onStorage = (event) => {
184
+ if (event.key !== null && !event.key.startsWith(`${storageKey}.`)) return;
185
+ const stored = readStoredTheme(storageKey, lightThemes, darkThemes);
186
+ if (stored.mode !== void 0) setAppearanceState(stored.mode);
187
+ if (stored.light !== void 0) setLightName(stored.light);
188
+ if (stored.dark !== void 0) setDarkName(stored.dark);
189
+ };
190
+ window.addEventListener("storage", onStorage);
191
+ return () => window.removeEventListener("storage", onStorage);
192
+ }, [
193
+ darkThemes,
194
+ lightThemes,
195
+ storageKey
196
+ ]);
197
+ const setAppearance = useCallback((next) => {
198
+ writeStoredTheme(storageKey, "theme-mode", next);
199
+ setAppearanceState(next);
200
+ }, [storageKey]);
201
+ const setSpecificTheme = useCallback((mode, theme) => {
202
+ if (mode === "light" && lightThemes.includes(theme)) {
203
+ writeStoredTheme(storageKey, "theme-light", theme);
204
+ setLightName(theme);
205
+ } else if (mode === "dark" && darkThemes.includes(theme)) {
206
+ writeStoredTheme(storageKey, "theme-dark", theme);
207
+ setDarkName(theme);
208
+ }
209
+ setAppearance(mode);
210
+ }, [
211
+ darkThemes,
212
+ lightThemes,
213
+ setAppearance,
214
+ storageKey
215
+ ]);
216
+ const value = useMemo(() => ({
217
+ appearance,
218
+ availableThemes: {
219
+ dark: darkThemes,
220
+ light: lightThemes
221
+ },
222
+ resolvedMode,
223
+ resolvedTheme,
224
+ setAppearance,
225
+ setSpecificTheme
226
+ }), [
227
+ appearance,
228
+ darkThemes,
229
+ lightThemes,
230
+ resolvedMode,
231
+ resolvedTheme,
232
+ setAppearance,
233
+ setSpecificTheme
234
+ ]);
235
+ return /* @__PURE__ */ jsx(ThemeContext, {
236
+ value,
237
+ children
238
+ });
239
+ }
240
+ //#endregion
241
+ //#region src/components/themes/theme-script.tsx
242
+ function applyStoredTheme(storageKey, lightThemes, darkThemes, defaultAppearance) {
243
+ try {
244
+ const read = (key) => window.localStorage.getItem(`${storageKey}.${key}`);
245
+ const stored = read("theme-mode");
246
+ const appearance = stored !== null && [
247
+ "light",
248
+ "dark",
249
+ "system",
250
+ "normal"
251
+ ].includes(stored) ? stored : defaultAppearance;
252
+ const normalized = appearance === "normal" ? "light" : appearance;
253
+ const mode = normalized === "dark" ? "dark" : normalized === "light" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
254
+ const themes = mode === "dark" ? darkThemes : lightThemes;
255
+ const storedTheme = read(mode === "dark" ? "theme-dark" : "theme-light");
256
+ const theme = storedTheme !== null && themes.includes(storedTheme) ? storedTheme : themes[0];
257
+ document.documentElement.setAttribute("data-theme", theme);
258
+ document.documentElement.classList.toggle("dark", mode === "dark");
259
+ } catch {}
260
+ }
261
+ function ThemeScript({ darkTheme = "ekkolyth-dark", defaultAppearance = "system", lightTheme = "ekkolyth-light", storageKey = "ekko-ui" }) {
262
+ const lightThemes = Array.isArray(lightTheme) ? lightTheme : [lightTheme];
263
+ const darkThemes = Array.isArray(darkTheme) ? darkTheme : [darkTheme];
264
+ const call = `(${applyStoredTheme.toString()})(${JSON.stringify(storageKey)},${JSON.stringify(lightThemes)},${JSON.stringify(darkThemes)},${JSON.stringify(defaultAppearance)})`;
265
+ return /* @__PURE__ */ jsx("script", {
266
+ dangerouslySetInnerHTML: { __html: call },
267
+ suppressHydrationWarning: true
268
+ });
269
+ }
270
+ //#endregion
271
+ export { ThemeProvider, ThemeScript, ThemeSelect, ThemeToggle, Toaster, useTheme, useThemeContext };
@@ -1,35 +1,35 @@
1
1
  /* Tokyo Night Theme - Dark Mode */
2
- html[data-theme="tokyo-night"] {
3
- --radius: 0.625rem;
4
- --background: hsl(233 16.00% 13.33%); /* #1a1b26 - dark blue-gray */
5
- --foreground: hsl(228 23.53% 87.06%); /* #c0caf5 - light blue-gray */
6
- --card: hsl(233 16.00% 13.33%); /* #1a1b26 */
7
- --card-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
8
- --popover: hsl(233 16.00% 13.33%); /* #1a1b26 */
9
- --popover-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
10
- --primary: hsl(217 91.67% 79.80%); /* #7aa2f7 - Tokyo Night blue */
11
- --primary-foreground: hsl(233 16.00% 13.33%); /* #1a1b26 */
12
- --secondary: hsl(233 18.18% 15.29%); /* #24283b - darker blue-gray */
13
- --secondary-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
14
- --muted: hsl(233 18.18% 15.29%); /* #24283b */
15
- --muted-foreground: hsl(230 13.33% 66.67%); /* #565f89 - muted text */
16
- --accent: hsl(233 18.18% 15.29%); /* #24283b */
17
- --accent-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
18
- --destructive: hsl(354 65.22% 78.24%); /* #f7768e - Tokyo Night red */
19
- --border: hsl(233 18.18% 20.00%); /* #2f3549 - border color */
20
- --input: hsl(233 18.18% 20.00%); /* #2f3549 */
21
- --ring: hsl(217 91.67% 79.80%); /* #7aa2f7 - primary blue */
22
- --chart-1: hsl(354 65.22% 78.24%); /* #f7768e - red */
23
- --chart-2: hsl(158 64.00% 71.37%); /* #9ece6a - green */
24
- --chart-3: hsl(217 91.67% 79.80%); /* #7aa2f7 - blue */
25
- --chart-4: hsl(38 92.00% 72.16%); /* #e0af68 - yellow */
26
- --chart-5: hsl(266 85.00% 78.24%); /* #bb9af7 - purple */
27
- --sidebar: hsl(233 18.18% 12.94%); /* #16161e - darker background */
28
- --sidebar-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
29
- --sidebar-primary: hsl(217 91.67% 79.80%); /* #7aa2f7 */
30
- --sidebar-primary-foreground: hsl(233 16.00% 13.33%); /* #1a1b26 */
31
- --sidebar-accent: hsl(233 18.18% 15.29%); /* #24283b */
32
- --sidebar-accent-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
33
- --sidebar-border: hsl(233 18.18% 20.00%); /* #2f3549 */
34
- --sidebar-ring: hsl(233 18.18% 20.00%); /* #2f3549 */
2
+ html[data-theme='tokyo-night'] {
3
+ --radius: 0.625rem;
4
+ --background: hsl(233 16.00% 13.33%); /* #1a1b26 - dark blue-gray */
5
+ --foreground: hsl(228 23.53% 87.06%); /* #c0caf5 - light blue-gray */
6
+ --card: hsl(233 16.00% 13.33%); /* #1a1b26 */
7
+ --card-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
8
+ --popover: hsl(233 16.00% 13.33%); /* #1a1b26 */
9
+ --popover-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
10
+ --primary: hsl(217 91.67% 79.80%); /* #7aa2f7 - Tokyo Night blue */
11
+ --primary-foreground: hsl(233 16.00% 13.33%); /* #1a1b26 */
12
+ --secondary: hsl(233 18.18% 15.29%); /* #24283b - darker blue-gray */
13
+ --secondary-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
14
+ --muted: hsl(233 18.18% 15.29%); /* #24283b */
15
+ --muted-foreground: hsl(230 13.33% 66.67%); /* #565f89 - muted text */
16
+ --accent: hsl(233 18.18% 15.29%); /* #24283b */
17
+ --accent-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
18
+ --destructive: hsl(354 65.22% 78.24%); /* #f7768e - Tokyo Night red */
19
+ --border: hsl(233 18.18% 20.00%); /* #2f3549 - border color */
20
+ --input: hsl(233 18.18% 20.00%); /* #2f3549 */
21
+ --ring: hsl(217 91.67% 79.80%); /* #7aa2f7 - primary blue */
22
+ --chart-1: hsl(354 65.22% 78.24%); /* #f7768e - red */
23
+ --chart-2: hsl(158 64.00% 71.37%); /* #9ece6a - green */
24
+ --chart-3: hsl(217 91.67% 79.80%); /* #7aa2f7 - blue */
25
+ --chart-4: hsl(38 92.00% 72.16%); /* #e0af68 - yellow */
26
+ --chart-5: hsl(266 85.00% 78.24%); /* #bb9af7 - purple */
27
+ --sidebar: hsl(233 18.18% 12.94%); /* #16161e - darker background */
28
+ --sidebar-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
29
+ --sidebar-primary: hsl(217 91.67% 79.80%); /* #7aa2f7 */
30
+ --sidebar-primary-foreground: hsl(233 16.00% 13.33%); /* #1a1b26 */
31
+ --sidebar-accent: hsl(233 18.18% 15.29%); /* #24283b */
32
+ --sidebar-accent-foreground: hsl(228 23.53% 87.06%); /* #c0caf5 */
33
+ --sidebar-border: hsl(233 18.18% 20.00%); /* #2f3549 */
34
+ --sidebar-ring: hsl(233 18.18% 20.00%); /* #2f3549 */
35
35
  }