@oxyhq/bloom 0.6.14 → 0.6.16

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 (48) hide show
  1. package/lib/commonjs/theme/BloomThemeProvider.js +16 -2
  2. package/lib/commonjs/theme/BloomThemeProvider.js.map +1 -1
  3. package/lib/commonjs/theme/color-presets.js +4 -1
  4. package/lib/commonjs/theme/color-presets.js.map +1 -1
  5. package/lib/commonjs/theme/index.js +19 -0
  6. package/lib/commonjs/theme/index.js.map +1 -1
  7. package/lib/commonjs/theme/persistence.js +20 -0
  8. package/lib/commonjs/theme/persistence.js.map +1 -1
  9. package/lib/commonjs/theme/preset-vars.js +98 -0
  10. package/lib/commonjs/theme/preset-vars.js.map +1 -0
  11. package/lib/module/theme/BloomThemeProvider.js +17 -3
  12. package/lib/module/theme/BloomThemeProvider.js.map +1 -1
  13. package/lib/module/theme/color-presets.js +3 -0
  14. package/lib/module/theme/color-presets.js.map +1 -1
  15. package/lib/module/theme/index.js +2 -1
  16. package/lib/module/theme/index.js.map +1 -1
  17. package/lib/module/theme/persistence.js +19 -0
  18. package/lib/module/theme/persistence.js.map +1 -1
  19. package/lib/module/theme/preset-vars.js +93 -0
  20. package/lib/module/theme/preset-vars.js.map +1 -0
  21. package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts +6 -0
  22. package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts.map +1 -1
  23. package/lib/typescript/commonjs/theme/color-presets.d.ts +2 -0
  24. package/lib/typescript/commonjs/theme/color-presets.d.ts.map +1 -1
  25. package/lib/typescript/commonjs/theme/index.d.ts +3 -1
  26. package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
  27. package/lib/typescript/commonjs/theme/persistence.d.ts +2 -0
  28. package/lib/typescript/commonjs/theme/persistence.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/theme/preset-vars.d.ts +28 -0
  30. package/lib/typescript/commonjs/theme/preset-vars.d.ts.map +1 -0
  31. package/lib/typescript/module/theme/BloomThemeProvider.d.ts +6 -0
  32. package/lib/typescript/module/theme/BloomThemeProvider.d.ts.map +1 -1
  33. package/lib/typescript/module/theme/color-presets.d.ts +2 -0
  34. package/lib/typescript/module/theme/color-presets.d.ts.map +1 -1
  35. package/lib/typescript/module/theme/index.d.ts +3 -1
  36. package/lib/typescript/module/theme/index.d.ts.map +1 -1
  37. package/lib/typescript/module/theme/persistence.d.ts +2 -0
  38. package/lib/typescript/module/theme/persistence.d.ts.map +1 -1
  39. package/lib/typescript/module/theme/preset-vars.d.ts +28 -0
  40. package/lib/typescript/module/theme/preset-vars.d.ts.map +1 -0
  41. package/package.json +1 -1
  42. package/src/__tests__/BloomThemeProvider.persistence.test.tsx +104 -0
  43. package/src/__tests__/preset-vars.test.ts +58 -0
  44. package/src/theme/BloomThemeProvider.tsx +35 -3
  45. package/src/theme/color-presets.ts +3 -0
  46. package/src/theme/index.ts +6 -0
  47. package/src/theme/persistence.ts +25 -0
  48. package/src/theme/preset-vars.ts +115 -0
@@ -25,6 +25,9 @@ export interface AppColorPreset {
25
25
 
26
26
  export const APP_COLOR_NAMES: readonly AppColorName[] = ['teal', 'blue', 'green', 'amber', 'yellow', 'red', 'purple', 'pink', 'sky', 'orange', 'mint', 'oxy', 'faircoin'];
27
27
 
28
+ /** Premium-exclusive presets, hidden from the standard color picker. */
29
+ export const PREMIUM_COLOR_NAMES: readonly AppColorName[] = ['oxy', 'faircoin'];
30
+
28
31
  export const HEX_TO_APP_COLOR: Record<string, AppColorName> = {
29
32
  '#005c67': 'teal',
30
33
  '#1d9bf0': 'blue',
@@ -10,10 +10,16 @@ export type { Theme, ThemeColors, ThemeMode } from './types';
10
10
  export type { AppColorName, AppColorPreset, PresetTokens } from './color-presets';
11
11
  export {
12
12
  APP_COLOR_NAMES,
13
+ PREMIUM_COLOR_NAMES,
13
14
  APP_COLOR_PRESETS,
14
15
  HEX_TO_APP_COLOR,
15
16
  hexToAppColorName,
16
17
  } from './color-presets';
18
+ export {
19
+ getPresetVars,
20
+ applyPresetVarsToDocument,
21
+ } from './preset-vars';
22
+ export type { PresetVarsOptions } from './preset-vars';
17
23
  export { applyDarkClass } from './apply-dark-class';
18
24
  export { setColorSchemeSafe } from './set-color-scheme-safe';
19
25
  export { initCssInteropDarkMode } from './init-css-interop';
@@ -20,6 +20,7 @@ const VALID_MODES = new Set<string>(['light', 'dark', 'system', 'adaptive']);
20
20
  export interface BloomThemeStorage {
21
21
  getItem(key: string): string | null | Promise<string | null>;
22
22
  setItem(key: string, value: string): void | Promise<void>;
23
+ removeItem?(key: string): void | Promise<void>;
23
24
  }
24
25
 
25
26
  export interface PersistedThemeState {
@@ -119,6 +120,23 @@ export async function writePersistedTheme(
119
120
  }
120
121
  }
121
122
 
123
+ export async function removePersistedTheme(
124
+ persistKey: string | undefined,
125
+ storage: BloomThemeStorage | undefined,
126
+ ): Promise<void> {
127
+ if (!persistKey || !storage) return;
128
+
129
+ try {
130
+ if (typeof storage.removeItem === 'function') {
131
+ await storage.removeItem(persistKey);
132
+ return;
133
+ }
134
+ await storage.setItem(persistKey, '');
135
+ } catch {
136
+ // Best-effort cleanup.
137
+ }
138
+ }
139
+
122
140
  /**
123
141
  * `localStorage`-backed storage adapter. Only defined on web; on native the
124
142
  * export is `undefined` so consumers pass an `AsyncStorage` adapter explicitly.
@@ -145,5 +163,12 @@ export const webLocalStorage: BloomThemeStorage | undefined = (() => {
145
163
  // Swallow quota / privacy-mode errors.
146
164
  }
147
165
  },
166
+ removeItem: (key) => {
167
+ try {
168
+ ls.removeItem(key);
169
+ } catch {
170
+ // Swallow privacy-mode errors.
171
+ }
172
+ },
148
173
  };
149
174
  })();
@@ -0,0 +1,115 @@
1
+ import { Platform } from 'react-native';
2
+ import { APP_COLOR_PRESETS, type AppColorName, type PresetTokens } from './color-presets';
3
+
4
+ function extractHue(hslVar: string): number {
5
+ return parseInt(hslVar.split(' ')[0] ?? '0', 10);
6
+ }
7
+
8
+ function extractSat(hslVar: string): number {
9
+ return parseInt(hslVar.split(' ')[1] ?? '0', 10);
10
+ }
11
+
12
+ const RESOLVED_COLOR_MAP: Record<string, string> = {
13
+ '--background': '--color-background',
14
+ '--foreground': '--color-foreground',
15
+ '--primary': '--color-primary',
16
+ '--primary-foreground': '--color-primary-foreground',
17
+ '--secondary': '--color-secondary',
18
+ '--secondary-foreground': '--color-secondary-foreground',
19
+ '--muted': '--color-muted',
20
+ '--muted-foreground': '--color-muted-foreground',
21
+ '--accent': '--color-accent',
22
+ '--accent-foreground': '--color-accent-foreground',
23
+ '--destructive': '--color-destructive',
24
+ '--border': '--color-border',
25
+ '--input': '--color-input',
26
+ '--ring': '--color-ring',
27
+ '--popover': '--color-popover',
28
+ '--popover-foreground': '--color-popover-foreground',
29
+ '--surface': '--color-surface',
30
+ '--surface-foreground': '--color-surface-foreground',
31
+ '--card': '--color-card',
32
+ '--card-foreground': '--color-card-foreground',
33
+ };
34
+
35
+ export interface PresetVarsOptions {
36
+ /**
37
+ * Also emit Tailwind v4 resolved `--color-*` vars (wrapped in `hsl(...)`)
38
+ * alongside the raw HSL triples. Needed when scoping a subtree where
39
+ * Tailwind's `@theme` block has already precomputed `--color-*` at `:root`,
40
+ * so overriding `--background` alone wouldn't cascade to `bg-background`.
41
+ * Default `false`.
42
+ */
43
+ includeResolvedColorVars?: boolean;
44
+ }
45
+
46
+ /**
47
+ * Bloom's base preset tokens extended with the surface/card/chart/sidebar
48
+ * tokens that apps layer on top of the core shadcn palette. Synthesized from
49
+ * the preset's primary hue so every preset stays in sync automatically.
50
+ *
51
+ * This is the single source of truth for extended theming vars — consumer apps
52
+ * must not redefine these per-app.
53
+ */
54
+ export function getPresetVars(
55
+ colorName: AppColorName,
56
+ mode: 'light' | 'dark',
57
+ options: PresetVarsOptions = {},
58
+ ): PresetTokens {
59
+ const preset = APP_COLOR_PRESETS[colorName];
60
+ const base = mode === 'light' ? preset.light : preset.dark;
61
+ const get = (key: string): string => base[key] ?? '0 0% 0%';
62
+ const primary = get('--primary');
63
+ const foreground = get('--foreground');
64
+ const hue = extractHue(primary);
65
+ const sat = Math.min(extractSat(primary), 80);
66
+ const isDark = mode === 'dark';
67
+
68
+ const extended: PresetTokens = {
69
+ ...base,
70
+ '--card': isDark ? `${hue} 30% 10%` : '0 0% 100%',
71
+ '--card-foreground': foreground,
72
+ '--chart-1': `${hue} ${sat}% 85%`,
73
+ '--chart-2': `${hue} ${sat}% 75%`,
74
+ '--chart-3': `${hue} ${sat}% 65%`,
75
+ '--chart-4': `${hue} ${sat}% ${isDark ? 55 : 75}%`,
76
+ '--chart-5': `${hue} ${sat}% ${isDark ? 45 : 65}%`,
77
+ '--content-area': isDark ? `${hue} 30% 8%` : get('--surface'),
78
+ '--sidebar-foreground': foreground,
79
+ '--sidebar-primary': primary,
80
+ '--sidebar-primary-foreground': get('--primary-foreground'),
81
+ '--sidebar-accent': isDark ? get('--sidebar') : get('--accent'),
82
+ '--sidebar-accent-foreground': isDark ? foreground : get('--accent-foreground'),
83
+ '--sidebar-border': get('--border'),
84
+ '--sidebar-ring': get('--ring'),
85
+ };
86
+
87
+ if (!options.includeResolvedColorVars) return extended;
88
+
89
+ const resolved: PresetTokens = { ...extended };
90
+ for (const [rawKey, colorKey] of Object.entries(RESOLVED_COLOR_MAP)) {
91
+ const value = extended[rawKey];
92
+ if (value) resolved[colorKey] = `hsl(${value})`;
93
+ }
94
+ return resolved;
95
+ }
96
+
97
+ /**
98
+ * Apply a preset's extended vars to `document.documentElement`. Web-only no-op
99
+ * on native. `BloomThemeProvider` already writes the base preset vars on web;
100
+ * call this only when an app needs the extended (card/chart/sidebar) tokens on
101
+ * the document root.
102
+ */
103
+ export function applyPresetVarsToDocument(
104
+ colorName: AppColorName,
105
+ mode: 'light' | 'dark',
106
+ options?: PresetVarsOptions,
107
+ ): void {
108
+ if (Platform.OS !== 'web' || typeof document === 'undefined') return;
109
+
110
+ const vars = getPresetVars(colorName, mode, options);
111
+ const root = document.documentElement.style;
112
+ for (const [key, value] of Object.entries(vars)) {
113
+ root.setProperty(key, value);
114
+ }
115
+ }