@fluid-app/portal-core 0.1.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 (195) hide show
  1. package/dist/chunk-CKQMccvm.cjs +28 -0
  2. package/dist/data-sources/DataAwareWidget.cjs +50 -0
  3. package/dist/data-sources/DataAwareWidget.cjs.map +1 -0
  4. package/dist/data-sources/DataAwareWidget.d.cts +24 -0
  5. package/dist/data-sources/DataAwareWidget.d.cts.map +1 -0
  6. package/dist/data-sources/DataAwareWidget.d.mts +24 -0
  7. package/dist/data-sources/DataAwareWidget.d.mts.map +1 -0
  8. package/dist/data-sources/DataAwareWidget.mjs +48 -0
  9. package/dist/data-sources/DataAwareWidget.mjs.map +1 -0
  10. package/dist/data-sources/ErrorState.cjs +20 -0
  11. package/dist/data-sources/ErrorState.cjs.map +1 -0
  12. package/dist/data-sources/ErrorState.d.cts +7 -0
  13. package/dist/data-sources/ErrorState.d.cts.map +1 -0
  14. package/dist/data-sources/ErrorState.d.mts +7 -0
  15. package/dist/data-sources/ErrorState.d.mts.map +1 -0
  16. package/dist/data-sources/ErrorState.mjs +18 -0
  17. package/dist/data-sources/ErrorState.mjs.map +1 -0
  18. package/dist/data-sources/context.cjs +24 -0
  19. package/dist/data-sources/context.cjs.map +1 -0
  20. package/dist/data-sources/context.d.cts +21 -0
  21. package/dist/data-sources/context.d.cts.map +1 -0
  22. package/dist/data-sources/context.d.mts +21 -0
  23. package/dist/data-sources/context.d.mts.map +1 -0
  24. package/dist/data-sources/context.mjs +21 -0
  25. package/dist/data-sources/context.mjs.map +1 -0
  26. package/dist/data-sources/fetchers/api.cjs +65 -0
  27. package/dist/data-sources/fetchers/api.cjs.map +1 -0
  28. package/dist/data-sources/fetchers/api.d.cts +10 -0
  29. package/dist/data-sources/fetchers/api.d.cts.map +1 -0
  30. package/dist/data-sources/fetchers/api.d.mts +10 -0
  31. package/dist/data-sources/fetchers/api.d.mts.map +1 -0
  32. package/dist/data-sources/fetchers/api.mjs +64 -0
  33. package/dist/data-sources/fetchers/api.mjs.map +1 -0
  34. package/dist/data-sources/fetchers/custom.cjs +108 -0
  35. package/dist/data-sources/fetchers/custom.cjs.map +1 -0
  36. package/dist/data-sources/fetchers/custom.d.cts +17 -0
  37. package/dist/data-sources/fetchers/custom.d.cts.map +1 -0
  38. package/dist/data-sources/fetchers/custom.d.mts +17 -0
  39. package/dist/data-sources/fetchers/custom.d.mts.map +1 -0
  40. package/dist/data-sources/fetchers/custom.mjs +107 -0
  41. package/dist/data-sources/fetchers/custom.mjs.map +1 -0
  42. package/dist/data-sources/fetchers/static.cjs +161 -0
  43. package/dist/data-sources/fetchers/static.cjs.map +1 -0
  44. package/dist/data-sources/fetchers/static.d.cts +40 -0
  45. package/dist/data-sources/fetchers/static.d.cts.map +1 -0
  46. package/dist/data-sources/fetchers/static.d.mts +40 -0
  47. package/dist/data-sources/fetchers/static.d.mts.map +1 -0
  48. package/dist/data-sources/fetchers/static.mjs +158 -0
  49. package/dist/data-sources/fetchers/static.mjs.map +1 -0
  50. package/dist/data-sources/preview-context.cjs +21 -0
  51. package/dist/data-sources/preview-context.cjs.map +1 -0
  52. package/dist/data-sources/preview-context.d.cts +13 -0
  53. package/dist/data-sources/preview-context.d.cts.map +1 -0
  54. package/dist/data-sources/preview-context.d.mts +13 -0
  55. package/dist/data-sources/preview-context.d.mts.map +1 -0
  56. package/dist/data-sources/preview-context.mjs +18 -0
  57. package/dist/data-sources/preview-context.mjs.map +1 -0
  58. package/dist/data-sources/registry-context.cjs +53 -0
  59. package/dist/data-sources/registry-context.cjs.map +1 -0
  60. package/dist/data-sources/registry-context.d.cts +48 -0
  61. package/dist/data-sources/registry-context.d.cts.map +1 -0
  62. package/dist/data-sources/registry-context.d.mts +48 -0
  63. package/dist/data-sources/registry-context.d.mts.map +1 -0
  64. package/dist/data-sources/registry-context.mjs +49 -0
  65. package/dist/data-sources/registry-context.mjs.map +1 -0
  66. package/dist/data-sources/registry.cjs +31 -0
  67. package/dist/data-sources/registry.cjs.map +1 -0
  68. package/dist/data-sources/registry.d.cts +19 -0
  69. package/dist/data-sources/registry.d.cts.map +1 -0
  70. package/dist/data-sources/registry.d.mts +19 -0
  71. package/dist/data-sources/registry.d.mts.map +1 -0
  72. package/dist/data-sources/registry.mjs +29 -0
  73. package/dist/data-sources/registry.mjs.map +1 -0
  74. package/dist/data-sources/transformers.cjs +154 -0
  75. package/dist/data-sources/transformers.cjs.map +1 -0
  76. package/dist/data-sources/transformers.d.cts +10 -0
  77. package/dist/data-sources/transformers.d.cts.map +1 -0
  78. package/dist/data-sources/transformers.d.mts +10 -0
  79. package/dist/data-sources/transformers.d.mts.map +1 -0
  80. package/dist/data-sources/transformers.mjs +153 -0
  81. package/dist/data-sources/transformers.mjs.map +1 -0
  82. package/dist/data-sources/types.cjs +0 -0
  83. package/dist/data-sources/types.d.cts +2 -0
  84. package/dist/data-sources/types.d.mts +2 -0
  85. package/dist/data-sources/types.mjs +1 -0
  86. package/dist/data-sources/use-widget-data.cjs +111 -0
  87. package/dist/data-sources/use-widget-data.cjs.map +1 -0
  88. package/dist/data-sources/use-widget-data.d.cts +17 -0
  89. package/dist/data-sources/use-widget-data.d.cts.map +1 -0
  90. package/dist/data-sources/use-widget-data.d.mts +17 -0
  91. package/dist/data-sources/use-widget-data.d.mts.map +1 -0
  92. package/dist/data-sources/use-widget-data.mjs +109 -0
  93. package/dist/data-sources/use-widget-data.mjs.map +1 -0
  94. package/dist/index-B5cTNde-.d.cts +246 -0
  95. package/dist/index-B5cTNde-.d.cts.map +1 -0
  96. package/dist/index-Cqt2JzkQ.d.mts +246 -0
  97. package/dist/index-Cqt2JzkQ.d.mts.map +1 -0
  98. package/dist/registries/index.cjs +243 -0
  99. package/dist/registries/index.cjs.map +1 -0
  100. package/dist/registries/index.d.cts +338 -0
  101. package/dist/registries/index.d.cts.map +1 -0
  102. package/dist/registries/index.d.mts +338 -0
  103. package/dist/registries/index.d.mts.map +1 -0
  104. package/dist/registries/index.mjs +229 -0
  105. package/dist/registries/index.mjs.map +1 -0
  106. package/dist/shell/AppShellLayout.cjs +49 -0
  107. package/dist/shell/AppShellLayout.cjs.map +1 -0
  108. package/dist/shell/AppShellLayout.d.cts +39 -0
  109. package/dist/shell/AppShellLayout.d.cts.map +1 -0
  110. package/dist/shell/AppShellLayout.d.mts +39 -0
  111. package/dist/shell/AppShellLayout.d.mts.map +1 -0
  112. package/dist/shell/AppShellLayout.mjs +46 -0
  113. package/dist/shell/AppShellLayout.mjs.map +1 -0
  114. package/dist/shell/ScreenHeader.cjs +44 -0
  115. package/dist/shell/ScreenHeader.cjs.map +1 -0
  116. package/dist/shell/ScreenHeader.d.cts +12 -0
  117. package/dist/shell/ScreenHeader.d.cts.map +1 -0
  118. package/dist/shell/ScreenHeader.d.mts +12 -0
  119. package/dist/shell/ScreenHeader.d.mts.map +1 -0
  120. package/dist/shell/ScreenHeader.mjs +42 -0
  121. package/dist/shell/ScreenHeader.mjs.map +1 -0
  122. package/dist/shell/ScreenHeaderContext.cjs +91 -0
  123. package/dist/shell/ScreenHeaderContext.cjs.map +1 -0
  124. package/dist/shell/ScreenHeaderContext.d.cts +35 -0
  125. package/dist/shell/ScreenHeaderContext.d.cts.map +1 -0
  126. package/dist/shell/ScreenHeaderContext.d.mts +35 -0
  127. package/dist/shell/ScreenHeaderContext.d.mts.map +1 -0
  128. package/dist/shell/ScreenHeaderContext.mjs +86 -0
  129. package/dist/shell/ScreenHeaderContext.mjs.map +1 -0
  130. package/dist/shell/ThemeModeContext.cjs +70 -0
  131. package/dist/shell/ThemeModeContext.cjs.map +1 -0
  132. package/dist/shell/ThemeModeContext.d.cts +33 -0
  133. package/dist/shell/ThemeModeContext.d.cts.map +1 -0
  134. package/dist/shell/ThemeModeContext.d.mts +33 -0
  135. package/dist/shell/ThemeModeContext.d.mts.map +1 -0
  136. package/dist/shell/ThemeModeContext.mjs +66 -0
  137. package/dist/shell/ThemeModeContext.mjs.map +1 -0
  138. package/dist/shell/index.cjs +43 -0
  139. package/dist/shell/index.d.cts +7 -0
  140. package/dist/shell/index.d.mts +7 -0
  141. package/dist/shell/index.mjs +7 -0
  142. package/dist/shell/sidebar.cjs +390 -0
  143. package/dist/shell/sidebar.cjs.map +1 -0
  144. package/dist/shell/sidebar.d.cts +85 -0
  145. package/dist/shell/sidebar.d.cts.map +1 -0
  146. package/dist/shell/sidebar.d.mts +85 -0
  147. package/dist/shell/sidebar.d.mts.map +1 -0
  148. package/dist/shell/sidebar.mjs +364 -0
  149. package/dist/shell/sidebar.mjs.map +1 -0
  150. package/dist/shell/use-mobile.cjs +51 -0
  151. package/dist/shell/use-mobile.cjs.map +1 -0
  152. package/dist/shell/use-mobile.d.cts +7 -0
  153. package/dist/shell/use-mobile.d.cts.map +1 -0
  154. package/dist/shell/use-mobile.d.mts +7 -0
  155. package/dist/shell/use-mobile.d.mts.map +1 -0
  156. package/dist/shell/use-mobile.mjs +47 -0
  157. package/dist/shell/use-mobile.mjs.map +1 -0
  158. package/dist/theme/index.cjs +758 -0
  159. package/dist/theme/index.cjs.map +1 -0
  160. package/dist/theme/index.d.cts +131 -0
  161. package/dist/theme/index.d.cts.map +1 -0
  162. package/dist/theme/index.d.mts +131 -0
  163. package/dist/theme/index.d.mts.map +1 -0
  164. package/dist/theme/index.mjs +728 -0
  165. package/dist/theme/index.mjs.map +1 -0
  166. package/dist/types/index.cjs +18 -0
  167. package/dist/types/index.d.cts +4 -0
  168. package/dist/types/index.d.mts +4 -0
  169. package/dist/types/index.mjs +2 -0
  170. package/dist/types-27AHMek-.d.cts +85 -0
  171. package/dist/types-27AHMek-.d.cts.map +1 -0
  172. package/dist/types-BXFX9bXp.cjs +303 -0
  173. package/dist/types-BXFX9bXp.cjs.map +1 -0
  174. package/dist/types-Bjmd7Fdx.mjs +208 -0
  175. package/dist/types-Bjmd7Fdx.mjs.map +1 -0
  176. package/dist/types-C5Zs5V3E.d.mts +155 -0
  177. package/dist/types-C5Zs5V3E.d.mts.map +1 -0
  178. package/dist/types-CeCPKvOv.d.mts +85 -0
  179. package/dist/types-CeCPKvOv.d.mts.map +1 -0
  180. package/dist/types-DrzvahW8.d.cts +155 -0
  181. package/dist/types-DrzvahW8.d.cts.map +1 -0
  182. package/dist/widget-schema-BKZgsNG7.d.mts +119 -0
  183. package/dist/widget-schema-BKZgsNG7.d.mts.map +1 -0
  184. package/dist/widget-schema-BSX2fVhW.d.cts +119 -0
  185. package/dist/widget-schema-BSX2fVhW.d.cts.map +1 -0
  186. package/dist/widget-utils/index.cjs +130 -0
  187. package/dist/widget-utils/index.cjs.map +1 -0
  188. package/dist/widget-utils/index.d.cts +47 -0
  189. package/dist/widget-utils/index.d.cts.map +1 -0
  190. package/dist/widget-utils/index.d.mts +47 -0
  191. package/dist/widget-utils/index.d.mts.map +1 -0
  192. package/dist/widget-utils/index.mjs +119 -0
  193. package/dist/widget-utils/index.mjs.map +1 -0
  194. package/package.json +200 -0
  195. package/src/styles/globals.css +1 -0
@@ -0,0 +1,758 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_chunk = require("../chunk-CKQMccvm.cjs");
3
+ let colorjs_io = require("colorjs.io");
4
+ colorjs_io = require_chunk.__toESM(colorjs_io);
5
+ //#region src/theme/types.ts
6
+ const SEMANTIC_COLOR_NAMES = [
7
+ "background",
8
+ "foreground",
9
+ "primary",
10
+ "secondary",
11
+ "accent",
12
+ "muted",
13
+ "destructive"
14
+ ];
15
+ const SHADE_STEPS = [
16
+ 100,
17
+ 200,
18
+ 300,
19
+ 400,
20
+ 500,
21
+ 600,
22
+ 700,
23
+ 800,
24
+ 900
25
+ ];
26
+ const FONT_SIZE_KEYS = [
27
+ "extraSmall",
28
+ "small",
29
+ "regular",
30
+ "large",
31
+ "extraLarge",
32
+ "giant"
33
+ ];
34
+ const FONT_FAMILY_KEYS = ["header", "body"];
35
+ const RADIUS_KEYS = [
36
+ "small",
37
+ "medium",
38
+ "large",
39
+ "extraLarge"
40
+ ];
41
+ //#endregion
42
+ //#region src/theme/color-engine.ts
43
+ /**
44
+ * Attempt to convert any string into a Color using colorjs.io.
45
+ * If the string is exactly 6 characters it is assumed to be a bare hex value
46
+ * (e.g. "3b82f6") and a "#" prefix is added before parsing.
47
+ *
48
+ * @returns the parsed Color, or a neutral gray (`oklch(0.5 0 0)`) on failure
49
+ */
50
+ function parseColor(value) {
51
+ if (value.length === 6) value = `#${value}`;
52
+ try {
53
+ return new colorjs_io.default(value);
54
+ } catch (error) {
55
+ console.warn("[theme] Failed to parse color:", value, error);
56
+ return new colorjs_io.default("oklch", [
57
+ .5,
58
+ 0,
59
+ 0
60
+ ]);
61
+ }
62
+ }
63
+ /**
64
+ * Returns either the original foreground or a corrected lightness variant,
65
+ * whichever provides better contrast against `color`.
66
+ * Inversion triggers when the APCA contrast is below 50.
67
+ */
68
+ function getForegroundColor(foreground, color) {
69
+ if (foreground.oklch.l == null || color.oklch.l == null) return foreground;
70
+ if (color.contrastAPCA(foreground) < 50) return new colorjs_io.default("oklch", [
71
+ color.oklch.l < .7 ? .95 : .15,
72
+ foreground.oklch.c || 0,
73
+ foreground.oklch.h || 0
74
+ ]);
75
+ return foreground;
76
+ }
77
+ /**
78
+ * Generate a 100–900 shade ramp from a base color.
79
+ * Base anchors at 500. Light shades (100–400) step toward white,
80
+ * dark shades (600–900) step toward black. Dark steps use an asymmetric
81
+ * multiplier (1.6×, 1.875×, 3×, 4× of `darkStep`) for a more gradual
82
+ * initial descent. Chroma is nudged per step for perceptually natural ramps.
83
+ */
84
+ function generateShades(base) {
85
+ const l = base.oklch.l ?? 0;
86
+ const c = base.oklch.c ?? 0;
87
+ const h = base.oklch.h ?? 0;
88
+ const safeMax = l >= .885 ? .995 : .97;
89
+ const safeMin = l <= .33 ? 0 : .21;
90
+ const lightStep = (safeMax - l) / 5;
91
+ const darkStep = -(l - safeMin) / 8;
92
+ const shade = (lDelta, cDelta) => {
93
+ return new colorjs_io.default("oklch", [
94
+ Math.max(0, Math.min(1, l + lDelta)),
95
+ c <= .001 ? c : Math.max(0, c + cDelta),
96
+ h
97
+ ]);
98
+ };
99
+ return {
100
+ 100: shade(5 * lightStep, -.00375),
101
+ 200: shade(4 * lightStep, -.00375),
102
+ 300: shade(3 * lightStep, -.00375),
103
+ 400: shade(2 * lightStep, -.00375),
104
+ 500: new colorjs_io.default("oklch", [
105
+ l,
106
+ c,
107
+ h
108
+ ]),
109
+ 600: shade(1.6 * darkStep, .025),
110
+ 700: shade(1.875 * 2 * darkStep, .05),
111
+ 800: shade(6 * darkStep, .075),
112
+ 900: shade(8 * darkStep, .1)
113
+ };
114
+ }
115
+ const DARK_DERIVATION_CONFIG = {
116
+ background: {
117
+ baseLightness: .15,
118
+ fgLightness: .93
119
+ },
120
+ foreground: {
121
+ baseLightness: .93,
122
+ fgLightness: .15
123
+ },
124
+ muted: {
125
+ baseLightness: .22,
126
+ fgLightness: .75
127
+ },
128
+ primary: {
129
+ baseLightness: "invert",
130
+ fgLightness: .95,
131
+ chromaScale: .9
132
+ },
133
+ secondary: {
134
+ baseLightness: "invert",
135
+ fgLightness: .93,
136
+ chromaScale: .85
137
+ },
138
+ accent: {
139
+ baseLightness: "invert",
140
+ fgLightness: .95,
141
+ chromaScale: .9
142
+ },
143
+ destructive: {
144
+ baseLightness: "invert",
145
+ fgLightness: .95,
146
+ chromaScale: .95
147
+ }
148
+ };
149
+ /** Invert OKLCH lightness (1 - l), clamped to [0.35, 0.75] to avoid extremes. */
150
+ function invertLightness(l) {
151
+ const inverted = 1 - l;
152
+ return Math.max(.35, Math.min(.75, inverted));
153
+ }
154
+ /**
155
+ * Derive a dark-mode ThemeColorInput from its light-mode counterpart.
156
+ */
157
+ function deriveDarkVariant(name, light) {
158
+ const config = DARK_DERIVATION_CONFIG[name];
159
+ const chromaScale = config.chromaScale ?? 1;
160
+ const baseLightness = config.baseLightness === "invert" ? invertLightness(light.base.oklch.l ?? 0) : config.baseLightness;
161
+ const fgLightness = config.fgLightness === "invert" ? invertLightness(light.foreground.oklch.l ?? 0) : config.fgLightness;
162
+ return {
163
+ base: new colorjs_io.default("oklch", [
164
+ baseLightness,
165
+ (light.base.oklch.c || 0) * chromaScale,
166
+ light.base.oklch.h || 0
167
+ ]),
168
+ foreground: new colorjs_io.default("oklch", [
169
+ fgLightness,
170
+ (light.foreground.oklch.c || 0) * chromaScale,
171
+ light.foreground.oklch.h || 0
172
+ ])
173
+ };
174
+ }
175
+ /**
176
+ * Merge auto-derived dark colors with any user-specified overrides.
177
+ * For each semantic color, if the user has fully overridden both base and
178
+ * foreground those are used; otherwise the missing channels are derived.
179
+ */
180
+ function mergeDarkOverrides(def) {
181
+ const darkColors = {};
182
+ for (const name of SEMANTIC_COLOR_NAMES) {
183
+ const lightInput = def.light[name];
184
+ const darkOverride = def.dark[name];
185
+ if (darkOverride?.base && darkOverride?.foreground) darkColors[name] = darkOverride;
186
+ else if (darkOverride) {
187
+ const base = darkOverride.base ?? deriveDarkVariant(name, lightInput).base;
188
+ darkColors[name] = {
189
+ base,
190
+ foreground: darkOverride.foreground ?? getForegroundColor(def.light.foreground.base, base)
191
+ };
192
+ } else darkColors[name] = deriveDarkVariant(name, lightInput);
193
+ }
194
+ return darkColors;
195
+ }
196
+ function resolveColorSet(colors) {
197
+ const resolved = {};
198
+ for (const name of SEMANTIC_COLOR_NAMES) {
199
+ const input = colors[name];
200
+ const shades = generateShades(input.base);
201
+ const resolvedShades = {};
202
+ for (const step of SHADE_STEPS) resolvedShades[step] = shades[step];
203
+ resolved[name] = {
204
+ base: input.base.clone(),
205
+ foreground: input.foreground.clone(),
206
+ shades: resolvedShades
207
+ };
208
+ }
209
+ return resolved;
210
+ }
211
+ /**
212
+ * Resolve a ThemeDefinition into a complete ResolvedTheme.
213
+ * Dark mode colors are derived from light where not overridden.
214
+ */
215
+ function resolveTheme(def) {
216
+ return {
217
+ id: def.id,
218
+ name: def.name,
219
+ light: resolveColorSet(def.light),
220
+ dark: resolveColorSet(mergeDarkOverrides(def)),
221
+ fontSizes: { ...def.fontSizes },
222
+ fontFamilies: { ...def.fontFamilies },
223
+ spacing: def.spacing,
224
+ radii: { ...def.radii }
225
+ };
226
+ }
227
+ //#endregion
228
+ //#region src/theme/tailwind-overrides.ts
229
+ /**
230
+ * Specific overrides, otherwise all the overrides are generated using emitTailwindOverrides
231
+ */
232
+ const OVERRIDES = {
233
+ "--color-gray-50": "var(--color-muted)",
234
+ "--color-gray-100": "var(--color-muted-600)",
235
+ "--color-gray-200": "var(--color-border)"
236
+ };
237
+ /**
238
+ * Returns the inverted shade for dark mode foreground colors.
239
+ * In dark mode, light shades (50, 100) should map to dark values (950, 900) and vice versa.
240
+ */
241
+ function getInvertedStep(shade) {
242
+ const shadeIndex = SHADE_STEPS.indexOf(shade);
243
+ return SHADE_STEPS[SHADE_STEPS.length - 1 - shadeIndex] || 500;
244
+ }
245
+ /**
246
+ * Map semantic colors to Tailwind built-in color names.
247
+ */
248
+ function emitTailwindOverrides(darkMode = false) {
249
+ const TAILWIND_COLOR_MAP = {
250
+ gray: "foreground",
251
+ red: "destructive",
252
+ blue: "primary",
253
+ green: "accent"
254
+ };
255
+ const TAILWIND_SHADES = [
256
+ 50,
257
+ 100,
258
+ 200,
259
+ 300,
260
+ 400,
261
+ 500,
262
+ 600,
263
+ 700,
264
+ 800,
265
+ 900,
266
+ 950
267
+ ];
268
+ const SHADE_REMAP = {
269
+ 50: 100,
270
+ 950: 900
271
+ };
272
+ const lines = [];
273
+ for (const [twName, semantic] of Object.entries(TAILWIND_COLOR_MAP)) for (const shade of TAILWIND_SHADES) {
274
+ const step = SHADE_REMAP[shade] ?? shade;
275
+ const override = OVERRIDES[`--color-${twName}-${shade}`];
276
+ lines.push(`--color-${twName}-${shade}: ${override ? override : `var(--color-${semantic}-${semantic === "foreground" && darkMode === true ? getInvertedStep(step) : step})`};`);
277
+ }
278
+ lines.push("--color-white: var(--color-background);");
279
+ lines.push("--color-black: var(--color-foreground);");
280
+ return lines;
281
+ }
282
+ //#endregion
283
+ //#region src/theme/css-generator.ts
284
+ function colorToCSS(color) {
285
+ const result = color.toString({ format: "oklch" });
286
+ if (result.includes("NaN")) {
287
+ console.warn("[theme] colorToCSS produced NaN, using neutral fallback:", result);
288
+ return "oklch(0.5 0 0)";
289
+ }
290
+ return result;
291
+ }
292
+ function camelToKebab(str) {
293
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
294
+ }
295
+ /**
296
+ * Emit --color-{name}, --color-{name}-foreground, --color-{name}-{shade} vars.
297
+ * Uses --color- prefix to match portal-widgets/tailwind.config.ts.
298
+ */
299
+ function emitColorVars(colors) {
300
+ const lines = [];
301
+ for (const name of SEMANTIC_COLOR_NAMES) {
302
+ const color = colors[name];
303
+ lines.push(`--color-${name}: ${colorToCSS(color.base)};`);
304
+ lines.push(`--color-${name}-foreground: ${colorToCSS(color.foreground)};`);
305
+ for (const step of SHADE_STEPS) lines.push(`--color-${name}-${step}: ${colorToCSS(color.shades[step])};`);
306
+ }
307
+ return lines;
308
+ }
309
+ /**
310
+ * Emit non-color CSS variables (font sizes, families, spacing, radii).
311
+ */
312
+ function emitNonColorVars(theme) {
313
+ const lines = [];
314
+ for (const key of FONT_SIZE_KEYS) lines.push(`--font-size-${camelToKebab(key)}: ${theme.fontSizes[key]};`);
315
+ for (const key of FONT_FAMILY_KEYS) lines.push(`--font-${key}: ${theme.fontFamilies[key]};`);
316
+ lines.push(`--spacing: ${theme.spacing};`);
317
+ for (const key of RADIUS_KEYS) lines.push(`--radius-${camelToKebab(key)}: ${theme.radii[key]};`);
318
+ return lines;
319
+ }
320
+ /**
321
+ * Static CSS alias variables that bridge theme var names to Tailwind/component conventions.
322
+ * These are always emitted and not mode-dependent.
323
+ */
324
+ const globalCSSOverride = [
325
+ "--color-background-foreground: var(--color-foreground);",
326
+ "--color-foreground-foreground: var(--color-background);",
327
+ "--color-contrast: var(--color-foreground);",
328
+ ...SEMANTIC_COLOR_NAMES.map((value) => `--${value}: var(--color-${value});`),
329
+ ...SEMANTIC_COLOR_NAMES.map((value) => `--${value}-foreground: var(--color-${value}-foreground);`),
330
+ "--sidebar-ring: var(--color-primary);",
331
+ "--sidebar-border: var(--color-border);",
332
+ "--sidebar-accent-foreground: var(--color-accent-foreground);",
333
+ "--sidebar-accent: var(--color-accent);",
334
+ "--sidebar-primary-foreground: var(--color-primary-foreground);",
335
+ "--sidebar-primary: var(--color-primary);",
336
+ "--sidebar-foreground: var(--color-muted-foreground);",
337
+ "--sidebar: var(--color-muted);",
338
+ "--border: var(--color-background-600);",
339
+ "--ring: var(--color-primary);",
340
+ "--popover: var(--color-background);",
341
+ "--popover-foreground: var(--color-foreground);",
342
+ "--card: var(--color-muted);",
343
+ "--card-foreground: var(--color-muted-foreground);",
344
+ "--radius-sm: var(--radius-small);",
345
+ "--radius-md: var(--radius-medium);",
346
+ "--radius-lg: var(--radius-large);",
347
+ "--radius-xl: var(--radius-extra-large);",
348
+ "--text-xs: var(--font-size-extra-small);",
349
+ "--text-sm: var(--font-size-small);",
350
+ "--text-base: var(--font-size-regular);",
351
+ "--text-lg: var(--font-size-large);",
352
+ "--text-xl: var(--font-size-extra-large);",
353
+ "--text-2xl: var(--font-size-giant);",
354
+ "--font-sans: var(--font-body);",
355
+ "--font-mono: var(--font-header);"
356
+ ];
357
+ /**
358
+ * Overrides for global tailwindcss for specifically dark mode.
359
+ */
360
+ const globalDarkCSSOverride = ["--border: var(--color-background-400);"];
361
+ /**
362
+ * Generate a complete CSS string for a resolved theme.
363
+ * Outputs 2–3 blocks: light default, dark explicit via `[data-theme-mode="dark"]`,
364
+ * and (unless `disableAutoTheme`) a `prefers-color-scheme: dark` media query block.
365
+ */
366
+ function generateThemeCSS(theme, options = {}) {
367
+ const sel = `[data-theme="${theme.id}"]`;
368
+ const tw = options.mapTailwindColors ?? true;
369
+ const blocks = [];
370
+ blocks.push(`${sel} {`);
371
+ blocks.push(...globalCSSOverride);
372
+ blocks.push(...emitNonColorVars(theme));
373
+ blocks.push(...emitColorVars(theme.light));
374
+ if (tw) blocks.push(...emitTailwindOverrides());
375
+ blocks.push(`}`);
376
+ blocks.push(`${sel}[data-theme-mode="dark"] {`);
377
+ blocks.push(...globalDarkCSSOverride);
378
+ blocks.push(...emitColorVars(theme.dark));
379
+ if (tw) blocks.push(...emitTailwindOverrides(true));
380
+ blocks.push(`}`);
381
+ if (!options.disableAutoTheme) {
382
+ blocks.push(`@media (prefers-color-scheme: dark) {`);
383
+ blocks.push(`${sel}:not([data-theme-mode]) {`);
384
+ blocks.push(...globalDarkCSSOverride);
385
+ blocks.push(...emitColorVars(theme.dark).map((l) => `${l}`));
386
+ if (tw) blocks.push(...emitTailwindOverrides(true).map((l) => `${l}`));
387
+ blocks.push(`}`);
388
+ blocks.push(`}`);
389
+ }
390
+ return blocks.join("\n");
391
+ }
392
+ //#endregion
393
+ //#region src/theme/defaults.ts
394
+ const DEFAULT_FONT_SIZES = {
395
+ extraSmall: "0.75rem",
396
+ small: "0.875rem",
397
+ regular: "1rem",
398
+ large: "1.125rem",
399
+ extraLarge: "1.25rem",
400
+ giant: "1.5rem"
401
+ };
402
+ const DEFAULT_FONT_FAMILIES = {
403
+ header: "var(--font-inter)",
404
+ body: "var(--font-inter)"
405
+ };
406
+ const DEFAULT_SPACING = "0.25rem";
407
+ const DEFAULT_RADII = {
408
+ small: "0.25rem",
409
+ medium: "0.5rem",
410
+ large: "0.75rem",
411
+ extraLarge: "1rem"
412
+ };
413
+ const DEFAULT_COLORS = {
414
+ background: "#ffffff",
415
+ foreground: "#1a1a1a",
416
+ primary: "#3b82f6",
417
+ secondary: "#6b7280",
418
+ accent: "#10b981",
419
+ muted: "#f3f4f6",
420
+ destructive: "#ef4444",
421
+ mutedForeground: "#6b7280"
422
+ };
423
+ const DEFAULT_THEME_ID = "default";
424
+ const DEFAULT_THEME_NAME = "Default Theme";
425
+ /**
426
+ * Build a fresh ThemeDefinition populated with all defaults.
427
+ * Returns a new object each call because Color instances are mutable — do not cache the result.
428
+ */
429
+ function getDefaultThemeDefinition() {
430
+ const bg = new colorjs_io.default(DEFAULT_COLORS.background);
431
+ const fg = new colorjs_io.default(DEFAULT_COLORS.foreground);
432
+ const primary = new colorjs_io.default(DEFAULT_COLORS.primary);
433
+ const secondary = new colorjs_io.default(DEFAULT_COLORS.secondary);
434
+ const accent = new colorjs_io.default(DEFAULT_COLORS.accent);
435
+ const muted = new colorjs_io.default(DEFAULT_COLORS.muted);
436
+ const destructive = new colorjs_io.default(DEFAULT_COLORS.destructive);
437
+ const mutedFg = new colorjs_io.default(DEFAULT_COLORS.mutedForeground);
438
+ const darkBg = new colorjs_io.default("#0a0a0a");
439
+ const darkFg = new colorjs_io.default("#fafafa");
440
+ const darkMuted = new colorjs_io.default("#171717");
441
+ const darkMutedForeground = new colorjs_io.default("#dddddd");
442
+ return {
443
+ id: DEFAULT_THEME_ID,
444
+ name: DEFAULT_THEME_NAME,
445
+ light: {
446
+ background: {
447
+ base: bg,
448
+ foreground: fg
449
+ },
450
+ foreground: {
451
+ base: fg,
452
+ foreground: bg
453
+ },
454
+ primary: {
455
+ base: primary,
456
+ foreground: getForegroundColor(fg, primary)
457
+ },
458
+ secondary: {
459
+ base: secondary,
460
+ foreground: getForegroundColor(fg, secondary)
461
+ },
462
+ accent: {
463
+ base: accent,
464
+ foreground: getForegroundColor(fg, accent)
465
+ },
466
+ muted: {
467
+ base: muted,
468
+ foreground: mutedFg
469
+ },
470
+ destructive: {
471
+ base: destructive,
472
+ foreground: getForegroundColor(fg, destructive)
473
+ }
474
+ },
475
+ dark: {
476
+ background: {
477
+ base: darkBg,
478
+ foreground: darkFg
479
+ },
480
+ foreground: {
481
+ base: darkFg,
482
+ foreground: darkBg
483
+ },
484
+ muted: {
485
+ base: darkMuted,
486
+ foreground: darkMutedForeground
487
+ }
488
+ },
489
+ fontSizes: { ...DEFAULT_FONT_SIZES },
490
+ fontFamilies: { ...DEFAULT_FONT_FAMILIES },
491
+ spacing: DEFAULT_SPACING,
492
+ radii: { ...DEFAULT_RADII }
493
+ };
494
+ }
495
+ //#endregion
496
+ //#region src/theme/serialisation.ts
497
+ function colorToPlain(color) {
498
+ return {
499
+ l: color.oklch.l ?? 0,
500
+ c: color.oklch.c ?? 0,
501
+ h: color.oklch.h ?? 0
502
+ };
503
+ }
504
+ function plainToColor(plain) {
505
+ return new colorjs_io.default("oklch", [
506
+ plain.l,
507
+ plain.c,
508
+ plain.h
509
+ ]);
510
+ }
511
+ /**
512
+ * Serialise a ThemeDefinition (with Color objects) to a plain JSON payload
513
+ * suitable for backend storage.
514
+ */
515
+ function serialiseTheme(def) {
516
+ const light = {};
517
+ for (const name of SEMANTIC_COLOR_NAMES) light[name] = {
518
+ base: colorToPlain(def.light[name].base),
519
+ foreground: colorToPlain(def.light[name].foreground)
520
+ };
521
+ const dark = {};
522
+ for (const [name, value] of Object.entries(def.dark)) {
523
+ if (!value) continue;
524
+ dark[name] = {
525
+ ...value.base ? { base: colorToPlain(value.base) } : {},
526
+ ...value.foreground ? { foreground: colorToPlain(value.foreground) } : {}
527
+ };
528
+ }
529
+ return {
530
+ id: def.id,
531
+ name: def.name,
532
+ light,
533
+ dark,
534
+ fontSizes: { ...def.fontSizes },
535
+ fontFamilies: { ...def.fontFamilies },
536
+ spacing: def.spacing,
537
+ radii: { ...def.radii },
538
+ ...def.syncWithBrandColors ? { syncWithBrandColors: true } : {}
539
+ };
540
+ }
541
+ /**
542
+ * Deserialise a backend payload into a ThemeDefinition with Color objects.
543
+ * Accepts `Record<string, unknown>` because API data is untyped at the boundary.
544
+ * Falls back to default colors for any missing light-mode entries.
545
+ */
546
+ function deserialiseTheme(payload) {
547
+ const lightRaw = payload.light ?? {};
548
+ const darkRaw = payload.dark ?? {};
549
+ const defaults = getDefaultThemeDefinition();
550
+ const light = {};
551
+ for (const name of SEMANTIC_COLOR_NAMES) {
552
+ const entry = lightRaw[name];
553
+ if (entry) light[name] = {
554
+ base: plainToColor(entry.base),
555
+ foreground: plainToColor(entry.foreground)
556
+ };
557
+ else {
558
+ console.warn(`[theme] deserialiseTheme: missing light color "${name}", using default`);
559
+ light[name] = defaults.light[name];
560
+ }
561
+ }
562
+ const dark = {};
563
+ for (const [name, value] of Object.entries(darkRaw)) {
564
+ if (!value) continue;
565
+ dark[name] = {
566
+ ...value.base ? { base: plainToColor(value.base) } : {},
567
+ ...value.foreground ? { foreground: plainToColor(value.foreground) } : {}
568
+ };
569
+ }
570
+ return {
571
+ id: payload.id,
572
+ name: payload.name,
573
+ light,
574
+ dark,
575
+ fontSizes: payload.fontSizes ?? DEFAULT_FONT_SIZES,
576
+ fontFamilies: payload.fontFamilies ?? DEFAULT_FONT_FAMILIES,
577
+ spacing: payload.spacing ?? "0.25rem",
578
+ radii: payload.radii ?? DEFAULT_RADII,
579
+ ...payload.syncWithBrandColors === true ? { syncWithBrandColors: true } : {}
580
+ };
581
+ }
582
+ //#endregion
583
+ //#region src/theme/transforms.ts
584
+ /**
585
+ * Check if a theme config uses the new structured format (has a `light` key
586
+ * that is an object) vs the legacy flat format.
587
+ */
588
+ function isNewThemeFormat(config) {
589
+ return config.light != null && typeof config.light === "object";
590
+ }
591
+ /**
592
+ * Convert a legacy flat config to a ThemeDefinition.
593
+ * Legacy format: { base: "#fff", text: "#000", primary: "oklch(0.6 0.2 250)", ... }
594
+ */
595
+ function legacyConfigToDefinition(id, name, config) {
596
+ const bg = parseColor(config.base ?? config.background ?? DEFAULT_COLORS.background);
597
+ const fg = parseColor(config.text ?? config.foreground ?? DEFAULT_COLORS.foreground);
598
+ const primary = parseColor(config.primary ?? DEFAULT_COLORS.primary);
599
+ const secondary = parseColor(config.secondary ?? DEFAULT_COLORS.secondary);
600
+ const accent = parseColor(config.accent ?? DEFAULT_COLORS.accent);
601
+ const muted = parseColor(config.muted ?? DEFAULT_COLORS.muted);
602
+ const destructive = parseColor(config.destructive ?? DEFAULT_COLORS.destructive);
603
+ const mutedFg = parseColor(config.mutedForeground ?? DEFAULT_COLORS.mutedForeground);
604
+ return {
605
+ id: String(id),
606
+ name,
607
+ light: {
608
+ background: {
609
+ base: bg,
610
+ foreground: fg
611
+ },
612
+ foreground: {
613
+ base: fg,
614
+ foreground: bg
615
+ },
616
+ primary: {
617
+ base: primary,
618
+ foreground: getForegroundColor(fg, primary)
619
+ },
620
+ secondary: {
621
+ base: secondary,
622
+ foreground: getForegroundColor(fg, secondary)
623
+ },
624
+ accent: {
625
+ base: accent,
626
+ foreground: getForegroundColor(fg, accent)
627
+ },
628
+ muted: {
629
+ base: muted,
630
+ foreground: mutedFg
631
+ },
632
+ destructive: {
633
+ base: destructive,
634
+ foreground: getForegroundColor(fg, destructive)
635
+ }
636
+ },
637
+ dark: {},
638
+ fontSizes: {
639
+ extraSmall: config.extraSmall ?? DEFAULT_FONT_SIZES.extraSmall,
640
+ small: config.small ?? DEFAULT_FONT_SIZES.small,
641
+ regular: config.regular ?? DEFAULT_FONT_SIZES.regular,
642
+ large: config.large ?? DEFAULT_FONT_SIZES.large,
643
+ extraLarge: config.extraLarge ?? DEFAULT_FONT_SIZES.extraLarge,
644
+ giant: config.giant ?? DEFAULT_FONT_SIZES.giant
645
+ },
646
+ fontFamilies: {
647
+ header: config.headerFont ?? DEFAULT_FONT_FAMILIES.header,
648
+ body: config.bodyFont ?? DEFAULT_FONT_FAMILIES.body
649
+ },
650
+ spacing: config.globalSpacing ?? "0.25rem",
651
+ radii: {
652
+ small: config.radiusSmall ?? DEFAULT_RADII.small,
653
+ medium: config.radiusMedium ?? DEFAULT_RADII.medium,
654
+ large: config.radiusLarge ?? DEFAULT_RADII.large,
655
+ extraLarge: config.radiusExtraLarge ?? DEFAULT_RADII.extraLarge
656
+ }
657
+ };
658
+ }
659
+ /**
660
+ * Build a ThemeDefinition from a single API theme object.
661
+ * Handles both new structured format and legacy flat format.
662
+ */
663
+ function buildThemeDefinition(theme) {
664
+ const config = theme.config ?? {};
665
+ if (isNewThemeFormat(config)) return deserialiseTheme({
666
+ ...config,
667
+ id: String(theme.id),
668
+ name: theme.name ?? "Untitled Theme"
669
+ });
670
+ return legacyConfigToDefinition(theme.id, theme.name ?? "Untitled Theme", config);
671
+ }
672
+ /**
673
+ * Transform raw API themes to ThemeDefinition[].
674
+ * Catches and logs errors per theme (graceful degradation).
675
+ */
676
+ function transformThemes(themes) {
677
+ return themes.flatMap((theme) => {
678
+ try {
679
+ return [buildThemeDefinition(theme)];
680
+ } catch (error) {
681
+ console.error(`[theme] Failed to build theme id=${theme.id}:`, error);
682
+ return [];
683
+ }
684
+ });
685
+ }
686
+ /**
687
+ * Get the active theme ID from a list of raw API themes.
688
+ * Falls back to the first theme if none is marked active.
689
+ */
690
+ function getActiveThemeId(themes) {
691
+ const active = themes.find((t) => t.active) ?? themes[0];
692
+ return active ? String(active.id) : void 0;
693
+ }
694
+ //#endregion
695
+ //#region src/theme/theme-applicator.ts
696
+ const STYLE_PREFIX = "theme-style-";
697
+ /**
698
+ * Inject or update a `<style>` element in `<head>` for the given theme.
699
+ * The element ID is deterministic (`theme-style-{themeId}`) so repeated calls
700
+ * for the same theme are idempotent — the existing element is updated in place.
701
+ * No-op when `document` is unavailable (SSR).
702
+ */
703
+ function applyTheme(theme, options) {
704
+ if (typeof document === "undefined") return;
705
+ try {
706
+ const styleId = `${STYLE_PREFIX}${theme.id}`;
707
+ let el = document.getElementById(styleId);
708
+ if (!el) {
709
+ el = document.createElement("style");
710
+ el.id = styleId;
711
+ document.head.appendChild(el);
712
+ }
713
+ el.textContent = generateThemeCSS(theme, options);
714
+ } catch (error) {
715
+ console.error(`[theme] applyTheme failed for "${theme.id}":`, error);
716
+ }
717
+ }
718
+ /** Remove an injected theme stylesheet. No-op during SSR. */
719
+ function removeTheme(themeId) {
720
+ if (typeof document === "undefined") return;
721
+ document.getElementById(`${STYLE_PREFIX}${themeId}`)?.remove();
722
+ }
723
+ /** Remove all injected theme stylesheets. No-op during SSR. */
724
+ function removeAllThemes() {
725
+ if (typeof document === "undefined") return;
726
+ document.querySelectorAll(`style[id^="${STYLE_PREFIX}"]`).forEach((el) => el.remove());
727
+ }
728
+ //#endregion
729
+ exports.DEFAULT_COLORS = DEFAULT_COLORS;
730
+ exports.DEFAULT_FONT_FAMILIES = DEFAULT_FONT_FAMILIES;
731
+ exports.DEFAULT_FONT_SIZES = DEFAULT_FONT_SIZES;
732
+ exports.DEFAULT_RADII = DEFAULT_RADII;
733
+ exports.DEFAULT_SPACING = DEFAULT_SPACING;
734
+ exports.DEFAULT_THEME_ID = DEFAULT_THEME_ID;
735
+ exports.DEFAULT_THEME_NAME = DEFAULT_THEME_NAME;
736
+ exports.FONT_FAMILY_KEYS = FONT_FAMILY_KEYS;
737
+ exports.FONT_SIZE_KEYS = FONT_SIZE_KEYS;
738
+ exports.RADIUS_KEYS = RADIUS_KEYS;
739
+ exports.SEMANTIC_COLOR_NAMES = SEMANTIC_COLOR_NAMES;
740
+ exports.SHADE_STEPS = SHADE_STEPS;
741
+ exports.applyTheme = applyTheme;
742
+ exports.buildThemeDefinition = buildThemeDefinition;
743
+ exports.deriveDarkVariant = deriveDarkVariant;
744
+ exports.deserialiseTheme = deserialiseTheme;
745
+ exports.generateShades = generateShades;
746
+ exports.generateThemeCSS = generateThemeCSS;
747
+ exports.getActiveThemeId = getActiveThemeId;
748
+ exports.getDefaultThemeDefinition = getDefaultThemeDefinition;
749
+ exports.getForegroundColor = getForegroundColor;
750
+ exports.mergeDarkOverrides = mergeDarkOverrides;
751
+ exports.parseColor = parseColor;
752
+ exports.removeAllThemes = removeAllThemes;
753
+ exports.removeTheme = removeTheme;
754
+ exports.resolveTheme = resolveTheme;
755
+ exports.serialiseTheme = serialiseTheme;
756
+ exports.transformThemes = transformThemes;
757
+
758
+ //# sourceMappingURL=index.cjs.map