@adea-ai/ui 0.22.2 → 0.23.1

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 (92) hide show
  1. package/README.md +6 -3
  2. package/dist/components/account-drawer.d.ts +3 -3
  3. package/dist/components/account-drawer.js +119 -19
  4. package/dist/components/on-screen-controls.d.ts +2 -1
  5. package/dist/components/on-screen-controls.js +158 -46
  6. package/dist/components/scene-settings.d.ts +3 -3
  7. package/dist/components/scene-settings.js +247 -28
  8. package/dist/components/theme-provider.d.ts +27 -5
  9. package/dist/components/theme-provider.js +96 -7
  10. package/dist/components/theme-toggle.d.ts +4 -4
  11. package/dist/components/theme-toggle.js +62 -18
  12. package/dist/components/ui/badge.d.ts +4 -3
  13. package/dist/components/ui/badge.js +31 -30
  14. package/dist/components/ui/button.d.ts +4 -3
  15. package/dist/components/ui/button.js +49 -32
  16. package/dist/components/ui/card.d.ts +3 -3
  17. package/dist/components/ui/card.js +25 -6
  18. package/dist/components/ui/dialog.d.ts +13 -13
  19. package/dist/components/ui/dialog.js +94 -26
  20. package/dist/components/ui/drawer.d.ts +23 -15
  21. package/dist/components/ui/drawer.js +109 -64
  22. package/dist/components/ui/dropdown-menu.d.ts +28 -22
  23. package/dist/components/ui/dropdown-menu.js +165 -36
  24. package/dist/components/ui/empty.d.ts +7 -6
  25. package/dist/components/ui/empty.js +78 -26
  26. package/dist/components/ui/field.d.ts +15 -14
  27. package/dist/components/ui/field.js +177 -55
  28. package/dist/components/ui/input.d.ts +2 -2
  29. package/dist/components/ui/input.js +20 -5
  30. package/dist/components/ui/label.d.ts +2 -2
  31. package/dist/components/ui/label.js +15 -5
  32. package/dist/components/ui/radio-group.d.ts +4 -4
  33. package/dist/components/ui/radio-group.js +36 -9
  34. package/dist/components/ui/separator.d.ts +6 -2
  35. package/dist/components/ui/separator.js +18 -6
  36. package/dist/components/ui/skeleton.d.ts +2 -1
  37. package/dist/components/ui/skeleton.js +15 -4
  38. package/dist/components/ui/spinner.d.ts +4 -3
  39. package/dist/components/ui/spinner.js +15 -5
  40. package/dist/components/ui/switch.d.ts +6 -3
  41. package/dist/components/ui/switch.js +57 -6
  42. package/dist/components/ui/tabs.d.ts +6 -5
  43. package/dist/components/ui/tabs.js +52 -24
  44. package/dist/components/ui/toggle-group.d.ts +10 -8
  45. package/dist/components/ui/toggle-group.js +91 -20
  46. package/dist/components/ui/toggle.d.ts +4 -2
  47. package/dist/components/ui/toggle.js +38 -23
  48. package/dist/components/ui/tooltip.d.ts +13 -6
  49. package/dist/components/ui/tooltip.js +51 -14
  50. package/dist/components/version-dialog.d.ts +2 -2
  51. package/dist/components/version-dialog.js +322 -129
  52. package/dist/components/workspace-brand.d.ts +1 -1
  53. package/dist/components/workspace-brand.js +12 -3
  54. package/dist/components/workspace-logo.d.ts +2 -2
  55. package/dist/components/workspace-logo.js +15 -3
  56. package/dist/index.d.ts +1 -1
  57. package/dist/index.js +20 -19
  58. package/dist/lib/utils.js +7 -4
  59. package/dist/lib/version-notes.js +14 -29
  60. package/package.json +18 -16
  61. package/src/components/account-drawer.tsx +48 -67
  62. package/src/components/on-screen-controls.tsx +55 -62
  63. package/src/components/scene-settings.tsx +97 -100
  64. package/src/components/theme-provider.tsx +122 -22
  65. package/src/components/theme-toggle.tsx +29 -54
  66. package/src/components/ui/badge.tsx +14 -23
  67. package/src/components/ui/button.tsx +9 -11
  68. package/src/components/ui/card.tsx +9 -6
  69. package/src/components/ui/dialog.tsx +58 -55
  70. package/src/components/ui/drawer.tsx +84 -125
  71. package/src/components/ui/dropdown-menu.tsx +130 -153
  72. package/src/components/ui/empty.tsx +29 -26
  73. package/src/components/ui/field.tsx +83 -87
  74. package/src/components/ui/input.tsx +8 -8
  75. package/src/components/ui/label.tsx +6 -7
  76. package/src/components/ui/radio-group.tsx +22 -19
  77. package/src/components/ui/separator.tsx +16 -9
  78. package/src/components/ui/skeleton.tsx +6 -3
  79. package/src/components/ui/spinner.tsx +9 -5
  80. package/src/components/ui/switch.tsx +47 -20
  81. package/src/components/ui/tabs.tsx +31 -30
  82. package/src/components/ui/toggle-group.tsx +65 -54
  83. package/src/components/ui/toggle.tsx +9 -12
  84. package/src/components/ui/tooltip.tsx +41 -36
  85. package/src/components/version-dialog.tsx +144 -144
  86. package/src/components/workspace-brand.tsx +5 -5
  87. package/src/components/workspace-logo.tsx +9 -8
  88. package/src/index.tsx +7 -1
  89. package/src/styles/base.css +637 -0
  90. package/src/styles/globals.css +1 -1
  91. package/tsconfig.json +2 -1
  92. package/components.json +0 -19
@@ -1,34 +1,253 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { createPortal } from 'react-dom';
4
- import { useEffect, useState } from 'react';
5
- import { Box, Camera, Focus, Grid3X3, UserRoundPen } from 'lucide-react';
6
- import { AccountDrawer } from './account-drawer';
7
- import { Button } from '#components/ui/button';
1
+ import { Button } from "./ui/button.js";
2
+ import { AccountDrawer } from "./account-drawer.js";
3
+ import { Portal, createComponent, insert, template } from "solid-js/web";
4
+ import { Box, Camera, Focus, Grid3X3, UserRoundPen } from "lucide-solid";
5
+ import { Show, createEffect, createSignal } from "solid-js";
6
+ //#region src/components/scene-settings.tsx
7
+ var _tmpl$ = /*#__PURE__*/ template(`<div class=workspace-camera-control role=group aria-label="Camera view">`);
8
+ var _tmpl$2 = /*#__PURE__*/ template(`<span class=workspace-character-designer-label>Character`);
9
+ var _tmpl$3 = /*#__PURE__*/ template(`<span class=workspace-room-designer-label>Room designer`);
10
+ var _tmpl$4 = /*#__PURE__*/ template(`<span class=workspace-scene-editor-label>Scene editor`);
11
+ var _tmpl$5 = /*#__PURE__*/ template(`<div class="fixed right-4 top-16 z-40">`);
8
12
  function usePortalTarget(targetId) {
9
- const [target, setTarget] = useState(null);
10
- useEffect(() => {
11
- setTarget(targetId ? document.getElementById(targetId) : null);
12
- }, [targetId]);
13
- return target;
13
+ const [target, setTarget] = createSignal(null);
14
+ createEffect(() => {
15
+ const id = targetId();
16
+ setTarget(id ? document.getElementById(id) : null);
17
+ });
18
+ return target;
14
19
  }
15
20
  function renderInTarget(content, target) {
16
- return target ? createPortal(content, target) : content;
21
+ return target ? createComponent(Portal, {
22
+ mount: target,
23
+ children: content
24
+ }) : content;
17
25
  }
18
26
  /** Compact scene controls shared by the HQ shell and other scene hosts. */
19
- export function SceneSettings({ cameraViewMode, onCameraViewModeChange, allowCameraViewModeChange = true, characterDesignerEnabled, onCharacterDesignerChange, characterDesignerTargetId, onOpenRoomDesigner, accountTargetId, accountLabel, accountAuthenticated, accountBusy, accountMusicControl, onAccountSignIn, onAccountSignOut, showAccountDrawer = true, cameraTargetId, roomDesignerTargetId, sceneEditorTargetId, sceneEditorEnabled, onSceneEditorChange, }) {
20
- const cameraTarget = usePortalTarget(cameraTargetId);
21
- const characterDesignerTarget = usePortalTarget(characterDesignerTargetId);
22
- const roomDesignerTarget = usePortalTarget(roomDesignerTargetId);
23
- const sceneEditorTarget = usePortalTarget(sceneEditorTargetId);
24
- const cameraControl = (_jsxs("div", { className: "workspace-camera-control", role: "group", "aria-label": "Camera view", children: [_jsxs(Button, { type: "button", size: "sm", variant: cameraViewMode === 'perspective' ? 'default' : 'outline', className: "workspace-camera-button", "aria-label": "Perspective camera", title: "Perspective camera", "aria-pressed": cameraViewMode === 'perspective', onClick: () => onCameraViewModeChange('perspective'), children: [_jsx(Camera, { "aria-hidden": "true" }), "Perspective"] }), _jsxs(Button, { type: "button", size: "sm", variant: cameraViewMode === 'orthographic' ? 'default' : 'outline', className: "workspace-camera-button", "aria-label": "Top-down camera", title: "Top-down camera", "aria-pressed": cameraViewMode === 'orthographic', onClick: () => onCameraViewModeChange('orthographic'), children: [_jsx(Focus, { "aria-hidden": "true" }), "Top-down"] })] }));
25
- const characterDesignerControl = (_jsxs(Button, { type: "button", variant: characterDesignerEnabled ? 'default' : 'outline', size: "sm", "aria-haspopup": "dialog", "aria-label": characterDesignerEnabled ? 'Close character designer' : 'Open character designer', title: characterDesignerEnabled ? 'Close character designer' : 'Open character designer', "aria-pressed": characterDesignerEnabled, onClick: () => onCharacterDesignerChange?.(!characterDesignerEnabled), children: [_jsx(UserRoundPen, { className: "size-4", "aria-hidden": "true" }), _jsx("span", { className: "workspace-character-designer-label", children: "Character" })] }));
26
- const roomDesignerControl = (_jsxs(Button, { type: "button", variant: "outline", size: "sm", "aria-haspopup": "dialog", "aria-label": "Open room designer", title: "Open room designer", onClick: onOpenRoomDesigner, children: [_jsx(Grid3X3, { className: "size-4", "aria-hidden": "true" }), _jsx("span", { className: "workspace-room-designer-label", children: "Room designer" })] }));
27
- const sceneEditorControl = (_jsxs(Button, { type: "button", variant: sceneEditorEnabled ? 'default' : 'outline', size: "sm", "aria-haspopup": "dialog", "aria-label": sceneEditorEnabled ? 'Close scene editor' : 'Open scene editor', title: sceneEditorEnabled ? 'Close scene editor' : 'Open scene editor', "aria-pressed": sceneEditorEnabled, onClick: () => onSceneEditorChange?.(!sceneEditorEnabled), children: [_jsx(Box, { className: "size-4", "aria-hidden": "true" }), _jsx("span", { className: "workspace-scene-editor-label", children: "Scene editor" })] }));
28
- return (_jsxs(_Fragment, { children: [showAccountDrawer ? (_jsx(AccountDrawer, { accountLabel: accountLabel, authenticated: accountAuthenticated, busy: accountBusy, musicControl: accountMusicControl, triggerTargetId: accountTargetId, onSignIn: onAccountSignIn, onSignOut: onAccountSignOut })) : null, allowCameraViewModeChange ? renderInTarget(cameraControl, cameraTarget) : null, characterDesignerEnabled != null &&
29
- !characterDesignerEnabled &&
30
- onCharacterDesignerChange ? (characterDesignerTarget ? (createPortal(characterDesignerControl, characterDesignerTarget)) : (_jsx("div", { className: "fixed right-4 top-16 z-40", children: characterDesignerControl }))) : null, onOpenRoomDesigner && cameraViewMode === 'orthographic' ? (roomDesignerTarget ? (createPortal(roomDesignerControl, roomDesignerTarget)) : (_jsx("div", { className: "fixed right-4 top-16 z-40", children: roomDesignerControl }))) : null, sceneEditorEnabled != null &&
31
- !sceneEditorEnabled &&
32
- onSceneEditorChange &&
33
- cameraViewMode === 'perspective' ? (sceneEditorTarget ? (createPortal(sceneEditorControl, sceneEditorTarget)) : (_jsx("div", { className: "fixed right-4 top-16 z-40", children: sceneEditorControl }))) : null] }));
27
+ function SceneSettings(props) {
28
+ const cameraTarget = usePortalTarget(() => props.cameraTargetId);
29
+ const characterDesignerTarget = usePortalTarget(() => props.characterDesignerTargetId);
30
+ const roomDesignerTarget = usePortalTarget(() => props.roomDesignerTargetId);
31
+ const sceneEditorTarget = usePortalTarget(() => props.sceneEditorTargetId);
32
+ const cameraControl = (() => {
33
+ var _el$ = _tmpl$();
34
+ insert(_el$, createComponent(Button, {
35
+ type: "button",
36
+ size: "sm",
37
+ get variant() {
38
+ return props.cameraViewMode === "perspective" ? "default" : "outline";
39
+ },
40
+ "class": "workspace-camera-button",
41
+ "aria-label": "Perspective camera",
42
+ title: "Perspective camera",
43
+ get ["aria-pressed"]() {
44
+ return props.cameraViewMode === "perspective";
45
+ },
46
+ onClick: () => props.onCameraViewModeChange("perspective"),
47
+ get children() {
48
+ return [createComponent(Camera, { "aria-hidden": "true" }), "Perspective"];
49
+ }
50
+ }), null);
51
+ insert(_el$, createComponent(Button, {
52
+ type: "button",
53
+ size: "sm",
54
+ get variant() {
55
+ return props.cameraViewMode === "orthographic" ? "default" : "outline";
56
+ },
57
+ "class": "workspace-camera-button",
58
+ "aria-label": "Top-down camera",
59
+ title: "Top-down camera",
60
+ get ["aria-pressed"]() {
61
+ return props.cameraViewMode === "orthographic";
62
+ },
63
+ onClick: () => props.onCameraViewModeChange("orthographic"),
64
+ get children() {
65
+ return [createComponent(Focus, { "aria-hidden": "true" }), "Top-down"];
66
+ }
67
+ }), null);
68
+ return _el$;
69
+ })();
70
+ const characterDesignerControl = createComponent(Button, {
71
+ type: "button",
72
+ get variant() {
73
+ return props.characterDesignerEnabled ? "default" : "outline";
74
+ },
75
+ size: "sm",
76
+ "aria-haspopup": "dialog",
77
+ get ["aria-label"]() {
78
+ return props.characterDesignerEnabled ? "Close character designer" : "Open character designer";
79
+ },
80
+ get title() {
81
+ return props.characterDesignerEnabled ? "Close character designer" : "Open character designer";
82
+ },
83
+ get ["aria-pressed"]() {
84
+ return props.characterDesignerEnabled;
85
+ },
86
+ onClick: () => props.onCharacterDesignerChange?.(!props.characterDesignerEnabled),
87
+ get children() {
88
+ return [createComponent(UserRoundPen, {
89
+ "class": "size-4",
90
+ "aria-hidden": "true"
91
+ }), _tmpl$2()];
92
+ }
93
+ });
94
+ const roomDesignerControl = createComponent(Button, {
95
+ type: "button",
96
+ variant: "outline",
97
+ size: "sm",
98
+ "aria-haspopup": "dialog",
99
+ "aria-label": "Open room designer",
100
+ title: "Open room designer",
101
+ onClick: () => props.onOpenRoomDesigner?.(),
102
+ get children() {
103
+ return [createComponent(Grid3X3, {
104
+ "class": "size-4",
105
+ "aria-hidden": "true"
106
+ }), _tmpl$3()];
107
+ }
108
+ });
109
+ const sceneEditorControl = createComponent(Button, {
110
+ type: "button",
111
+ get variant() {
112
+ return props.sceneEditorEnabled ? "default" : "outline";
113
+ },
114
+ size: "sm",
115
+ "aria-haspopup": "dialog",
116
+ get ["aria-label"]() {
117
+ return props.sceneEditorEnabled ? "Close scene editor" : "Open scene editor";
118
+ },
119
+ get title() {
120
+ return props.sceneEditorEnabled ? "Close scene editor" : "Open scene editor";
121
+ },
122
+ get ["aria-pressed"]() {
123
+ return props.sceneEditorEnabled;
124
+ },
125
+ onClick: () => props.onSceneEditorChange?.(!props.sceneEditorEnabled),
126
+ get children() {
127
+ return [createComponent(Box, {
128
+ "class": "size-4",
129
+ "aria-hidden": "true"
130
+ }), _tmpl$4()];
131
+ }
132
+ });
133
+ const showCharacterDesignerControl = () => props.characterDesignerEnabled != null && !props.characterDesignerEnabled && Boolean(props.onCharacterDesignerChange);
134
+ const showRoomDesignerControl = () => Boolean(props.onOpenRoomDesigner) && props.cameraViewMode === "orthographic";
135
+ const showSceneEditorControl = () => props.sceneEditorEnabled != null && !props.sceneEditorEnabled && Boolean(props.onSceneEditorChange) && props.cameraViewMode === "perspective";
136
+ return [
137
+ createComponent(Show, {
138
+ get when() {
139
+ return props.showAccountDrawer ?? true;
140
+ },
141
+ get children() {
142
+ return createComponent(AccountDrawer, {
143
+ get accountLabel() {
144
+ return props.accountLabel;
145
+ },
146
+ get authenticated() {
147
+ return props.accountAuthenticated;
148
+ },
149
+ get busy() {
150
+ return props.accountBusy;
151
+ },
152
+ get musicControl() {
153
+ return props.accountMusicControl;
154
+ },
155
+ get triggerTargetId() {
156
+ return props.accountTargetId;
157
+ },
158
+ get onSignIn() {
159
+ return props.onAccountSignIn;
160
+ },
161
+ get onSignOut() {
162
+ return props.onAccountSignOut;
163
+ }
164
+ });
165
+ }
166
+ }),
167
+ createComponent(Show, {
168
+ get when() {
169
+ return props.allowCameraViewModeChange ?? true;
170
+ },
171
+ get children() {
172
+ return renderInTarget(cameraControl, cameraTarget());
173
+ }
174
+ }),
175
+ createComponent(Show, {
176
+ get when() {
177
+ return showCharacterDesignerControl();
178
+ },
179
+ get children() {
180
+ return createComponent(Show, {
181
+ get when() {
182
+ return characterDesignerTarget();
183
+ },
184
+ get fallback() {
185
+ return (() => {
186
+ var _el$5 = _tmpl$5();
187
+ insert(_el$5, characterDesignerControl);
188
+ return _el$5;
189
+ })();
190
+ },
191
+ children: (target) => createComponent(Portal, {
192
+ get mount() {
193
+ return target();
194
+ },
195
+ children: characterDesignerControl
196
+ })
197
+ });
198
+ }
199
+ }),
200
+ createComponent(Show, {
201
+ get when() {
202
+ return showRoomDesignerControl();
203
+ },
204
+ get children() {
205
+ return createComponent(Show, {
206
+ get when() {
207
+ return roomDesignerTarget();
208
+ },
209
+ get fallback() {
210
+ return (() => {
211
+ var _el$6 = _tmpl$5();
212
+ insert(_el$6, roomDesignerControl);
213
+ return _el$6;
214
+ })();
215
+ },
216
+ children: (target) => createComponent(Portal, {
217
+ get mount() {
218
+ return target();
219
+ },
220
+ children: roomDesignerControl
221
+ })
222
+ });
223
+ }
224
+ }),
225
+ createComponent(Show, {
226
+ get when() {
227
+ return showSceneEditorControl();
228
+ },
229
+ get children() {
230
+ return createComponent(Show, {
231
+ get when() {
232
+ return sceneEditorTarget();
233
+ },
234
+ get fallback() {
235
+ return (() => {
236
+ var _el$7 = _tmpl$5();
237
+ insert(_el$7, sceneEditorControl);
238
+ return _el$7;
239
+ })();
240
+ },
241
+ children: (target) => createComponent(Portal, {
242
+ get mount() {
243
+ return target();
244
+ },
245
+ children: sceneEditorControl
246
+ })
247
+ });
248
+ }
249
+ })
250
+ ];
34
251
  }
252
+ //#endregion
253
+ export { SceneSettings };
@@ -1,5 +1,27 @@
1
- import type { ReactNode } from 'react';
2
- /** Shared theme provider used by each Next app in the workspace. */
3
- export declare function ThemeProvider({ children }: {
4
- children: ReactNode;
5
- }): import("react").JSX.Element;
1
+ import { type Accessor, type JSX, type ParentProps } from 'solid-js';
2
+ export type Theme = 'light' | 'dark' | 'system';
3
+ export type ResolvedTheme = 'light' | 'dark';
4
+ type ThemeContextValue = {
5
+ theme: Accessor<Theme>;
6
+ resolvedTheme: Accessor<ResolvedTheme>;
7
+ setTheme: (theme: Theme) => void;
8
+ };
9
+ type ThemeProviderProps = ParentProps<{
10
+ defaultTheme?: Theme;
11
+ disableTransitionOnChange?: boolean;
12
+ }>;
13
+ /**
14
+ * The shared Solid theme provider. It owns the same contract the previous
15
+ * provider exposed to the token layer: a `dark` class on the document element
16
+ * plus a `color-scheme` style, persisted in `localStorage` and defaulting to
17
+ * the system preference.
18
+ */
19
+ export declare function ThemeProvider(props: ThemeProviderProps): JSX.Element;
20
+ export declare function useTheme(): ThemeContextValue;
21
+ /**
22
+ * Pre-paint theme restore for server-rendered documents. Rendered in the
23
+ * document head, it applies the stored or system theme before first paint so
24
+ * hydration never shows the wrong palette.
25
+ */
26
+ export declare function ThemeScript(): JSX.Element;
27
+ export {};
@@ -1,8 +1,97 @@
1
- 'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { ThemeProvider as NextThemesProvider } from 'next-themes';
4
- const CompatibleNextThemesProvider = NextThemesProvider;
5
- /** Shared theme provider used by each Next app in the workspace. */
6
- export function ThemeProvider({ children }) {
7
- return (_jsx(CompatibleNextThemesProvider, { attribute: "class", defaultTheme: "system", enableSystem: true, disableTransitionOnChange: true, children: children }));
1
+ import { createComponent, template } from "solid-js/web";
2
+ import { createContext, createEffect, createSignal, onCleanup, useContext } from "solid-js";
3
+ //#region src/components/theme-provider.tsx
4
+ var _tmpl$ = /*#__PURE__*/ template(`<script>`);
5
+ var STORAGE_KEY = "theme";
6
+ var DARK_QUERY = "(prefers-color-scheme: dark)";
7
+ var ThemeContext = createContext();
8
+ function readStoredTheme() {
9
+ if (typeof window === "undefined") return "system";
10
+ try {
11
+ const stored = window.localStorage.getItem(STORAGE_KEY);
12
+ return stored === "light" || stored === "dark" || stored === "system" ? stored : "system";
13
+ } catch {
14
+ return "system";
15
+ }
8
16
  }
17
+ function systemPrefersDark() {
18
+ return typeof window !== "undefined" && window.matchMedia(DARK_QUERY).matches;
19
+ }
20
+ /**
21
+ * Applies the resolved theme to the document and suppresses transitions for the
22
+ * frame that carries the change, so a theme switch cannot animate page-wide.
23
+ */
24
+ function applyTheme(theme, disableTransitionOnChange) {
25
+ if (typeof document === "undefined") return;
26
+ const root = document.documentElement;
27
+ let restoreTransitions;
28
+ if (disableTransitionOnChange) {
29
+ const style = document.createElement("style");
30
+ style.appendChild(document.createTextNode("*{transition:none !important}"));
31
+ document.head.appendChild(style);
32
+ style.offsetHeight;
33
+ restoreTransitions = () => style.remove();
34
+ }
35
+ root.classList.toggle("dark", theme === "dark");
36
+ root.style.colorScheme = theme;
37
+ if (restoreTransitions) requestAnimationFrame(() => requestAnimationFrame(() => restoreTransitions?.()));
38
+ }
39
+ /**
40
+ * The shared Solid theme provider. It owns the same contract the previous
41
+ * provider exposed to the token layer: a `dark` class on the document element
42
+ * plus a `color-scheme` style, persisted in `localStorage` and defaulting to
43
+ * the system preference.
44
+ */
45
+ function ThemeProvider(props) {
46
+ const [theme, setThemeSignal] = createSignal(props.defaultTheme ?? readStoredTheme());
47
+ const [prefersDark, setPrefersDark] = createSignal(systemPrefersDark());
48
+ const resolvedTheme = () => {
49
+ const current = theme();
50
+ if (current === "system") return prefersDark() ? "dark" : "light";
51
+ return current;
52
+ };
53
+ if (typeof window !== "undefined") {
54
+ const media = window.matchMedia(DARK_QUERY);
55
+ const onChange = (event) => setPrefersDark(event.matches);
56
+ media.addEventListener("change", onChange);
57
+ onCleanup(() => media.removeEventListener("change", onChange));
58
+ }
59
+ createEffect(() => {
60
+ applyTheme(resolvedTheme(), props.disableTransitionOnChange ?? true);
61
+ });
62
+ const setTheme = (next) => {
63
+ setThemeSignal(next);
64
+ try {
65
+ window.localStorage.setItem(STORAGE_KEY, next);
66
+ } catch {}
67
+ };
68
+ return createComponent(ThemeContext.Provider, {
69
+ value: {
70
+ theme,
71
+ resolvedTheme,
72
+ setTheme
73
+ },
74
+ get children() {
75
+ return props.children;
76
+ }
77
+ });
78
+ }
79
+ function useTheme() {
80
+ const context = useContext(ThemeContext);
81
+ if (!context) throw new Error("useTheme must be used within a ThemeProvider.");
82
+ return context;
83
+ }
84
+ /**
85
+ * Pre-paint theme restore for server-rendered documents. Rendered in the
86
+ * document head, it applies the stored or system theme before first paint so
87
+ * hydration never shows the wrong palette.
88
+ */
89
+ function ThemeScript() {
90
+ return (() => {
91
+ var _el$ = _tmpl$();
92
+ _el$.innerHTML = "(function(){try{var t=localStorage.getItem('theme')||'system';var d=t==='dark'||(t==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);var r=document.documentElement;r.classList.toggle('dark',d);r.style.colorScheme=d?'dark':'light'}catch(e){}})();";
93
+ return _el$;
94
+ })();
95
+ }
96
+ //#endregion
97
+ export { ThemeProvider, ThemeScript, useTheme };
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Compact sun/moon theme selector. Wired to the next-themes provider so the
2
+ * Compact sun/moon theme selector. Wired to the shared theme provider so the
3
3
  * choice persists across the app (localStorage) and applies to every scene.
4
4
  */
5
- export declare function ThemeToggle({ className }: {
6
- className?: string;
7
- }): import("react").JSX.Element;
5
+ export declare function ThemeToggle(props: {
6
+ class?: string;
7
+ }): import("solid-js").JSX.Element;
@@ -1,20 +1,64 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useSyncExternalStore } from 'react';
4
- import { Monitor, Moon, Sun } from 'lucide-react';
5
- import { useTheme } from 'next-themes';
6
- import { Button } from '#components/ui/button';
7
- import { cn } from '#lib/utils';
1
+ import { cn } from "../lib/utils.js";
2
+ import { Button } from "./ui/button.js";
3
+ import { useTheme } from "./theme-provider.js";
4
+ import { className, createComponent, effect, insert, template } from "solid-js/web";
5
+ import { Monitor, Moon, Sun } from "lucide-solid";
6
+ import { For } from "solid-js";
7
+ //#region src/components/theme-toggle.tsx
8
+ var _tmpl$ = /*#__PURE__*/ template(`<div role=radiogroup aria-label=Theme>`);
9
+ var THEMES = [
10
+ {
11
+ value: "system",
12
+ label: "System theme",
13
+ icon: Monitor
14
+ },
15
+ {
16
+ value: "light",
17
+ label: "Light theme",
18
+ icon: Sun
19
+ },
20
+ {
21
+ value: "dark",
22
+ label: "Dark theme",
23
+ icon: Moon
24
+ }
25
+ ];
8
26
  /**
9
- * Compact sun/moon theme selector. Wired to the next-themes provider so the
10
- * choice persists across the app (localStorage) and applies to every scene.
11
- */
12
- export function ThemeToggle({ className }) {
13
- const { theme, setTheme } = useTheme();
14
- const mounted = useSyncExternalStore(() => () => undefined, () => true, () => false);
15
- // next-themes resolves the saved/system theme after mount. Use the stable
16
- // light presentation for both SSR and the first client render so the
17
- // aria/class attributes cannot mismatch during hydration.
18
- const selectedTheme = mounted ? theme : 'system';
19
- return (_jsxs("div", { className: cn('inline-flex items-center gap-2 rounded-full border border-input bg-background p-2', className), role: "radiogroup", "aria-label": "Theme", children: [_jsx(Button, { type: "button", role: "radio", "aria-checked": selectedTheme === 'system', "aria-label": "System theme", variant: selectedTheme === 'system' ? 'default' : 'ghost', size: "icon-sm", onClick: () => setTheme('system'), className: "rounded-full", children: _jsx(Monitor, { className: "size-4", "aria-hidden": "true" }) }), _jsx(Button, { type: "button", role: "radio", "aria-checked": selectedTheme === 'light', "aria-label": "Light theme", variant: selectedTheme === 'light' ? 'default' : 'ghost', size: "icon-sm", onClick: () => setTheme('light'), className: "rounded-full", children: _jsx(Sun, { className: "size-4", "aria-hidden": "true" }) }), _jsx(Button, { type: "button", role: "radio", "aria-checked": selectedTheme === 'dark', "aria-label": "Dark theme", variant: selectedTheme === 'dark' ? 'default' : 'ghost', size: "icon-sm", onClick: () => setTheme('dark'), className: "rounded-full", children: _jsx(Moon, { className: "size-4", "aria-hidden": "true" }) })] }));
27
+ * Compact sun/moon theme selector. Wired to the shared theme provider so the
28
+ * choice persists across the app (localStorage) and applies to every scene.
29
+ */
30
+ function ThemeToggle(props) {
31
+ const { theme, setTheme } = useTheme();
32
+ return (() => {
33
+ var _el$ = _tmpl$();
34
+ insert(_el$, createComponent(For, {
35
+ each: THEMES,
36
+ children: (option) => createComponent(Button, {
37
+ type: "button",
38
+ role: "radio",
39
+ get ["aria-checked"]() {
40
+ return theme() === option.value;
41
+ },
42
+ get ["aria-label"]() {
43
+ return option.label;
44
+ },
45
+ get variant() {
46
+ return theme() === option.value ? "default" : "ghost";
47
+ },
48
+ size: "icon-sm",
49
+ onClick: () => setTheme(option.value),
50
+ "class": "rounded-full",
51
+ get children() {
52
+ return createComponent(option.icon, {
53
+ "class": "size-4",
54
+ "aria-hidden": "true"
55
+ });
56
+ }
57
+ })
58
+ }));
59
+ effect(() => className(_el$, cn("inline-flex items-center gap-2 rounded-full border border-input bg-background p-2", props.class)));
60
+ return _el$;
61
+ })();
20
62
  }
63
+ //#endregion
64
+ export { ThemeToggle };
@@ -1,7 +1,8 @@
1
- import { useRender } from '@base-ui/react/use-render';
2
1
  import { type VariantProps } from 'class-variance-authority';
2
+ import { type ComponentProps } from 'solid-js';
3
3
  declare const badgeVariants: (props?: ({
4
4
  variant?: "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
- declare function Badge({ className, variant, render, ...props }: useRender.ComponentProps<'span'> & VariantProps<typeof badgeVariants>): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
7
- export { Badge, badgeVariants };
6
+ type BadgeProps = ComponentProps<'span'> & VariantProps<typeof badgeVariants>;
7
+ declare function Badge(props: BadgeProps): import("solid-js").JSX.Element;
8
+ export { Badge, badgeVariants, type BadgeProps };
@@ -1,33 +1,34 @@
1
- import { mergeProps } from '@base-ui/react/merge-props';
2
- import { useRender } from '@base-ui/react/use-render';
3
- import { cva } from 'class-variance-authority';
4
- import { cn } from '#lib/utils';
5
- const badgeVariants = cva('group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!', {
6
- variants: {
7
- variant: {
8
- default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
9
- secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
10
- destructive: 'bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20',
11
- outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
12
- ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
13
- link: 'text-primary underline-offset-4 hover:underline',
14
- },
15
- },
16
- defaultVariants: {
17
- variant: 'default',
18
- },
1
+ import { cn } from "../../lib/utils.js";
2
+ import { mergeProps, spread, template } from "solid-js/web";
3
+ import { splitProps } from "solid-js";
4
+ import { cva } from "class-variance-authority";
5
+ //#region src/components/ui/badge.tsx
6
+ var _tmpl$ = /*#__PURE__*/ template(`<span data-slot=badge>`);
7
+ var badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", {
8
+ variants: { variant: {
9
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
10
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
11
+ destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
12
+ outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
13
+ ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
14
+ link: "text-primary underline-offset-4 hover:underline"
15
+ } },
16
+ defaultVariants: { variant: "default" }
19
17
  });
20
- function Badge({ className, variant = 'default', render, ...props }) {
21
- return useRender({
22
- defaultTagName: 'span',
23
- props: mergeProps({
24
- className: cn(badgeVariants({ variant }), className),
25
- }, props),
26
- render,
27
- state: {
28
- slot: 'badge',
29
- variant,
30
- },
31
- });
18
+ function Badge(props) {
19
+ const [local, rest] = splitProps(props, ["class", "variant"]);
20
+ return (() => {
21
+ var _el$ = _tmpl$();
22
+ spread(_el$, mergeProps({
23
+ get ["data-variant"]() {
24
+ return local.variant ?? "default";
25
+ },
26
+ get ["class"]() {
27
+ return cn(badgeVariants({ variant: local.variant }), local.class);
28
+ }
29
+ }, rest), false, false);
30
+ return _el$;
31
+ })();
32
32
  }
33
+ //#endregion
33
34
  export { Badge, badgeVariants };
@@ -1,8 +1,9 @@
1
- import { Button as ButtonPrimitive } from '@base-ui/react/button';
2
1
  import { type VariantProps } from 'class-variance-authority';
2
+ import { type ComponentProps } from 'solid-js';
3
3
  declare const buttonVariants: (props?: ({
4
4
  variant?: "default" | "outline" | "secondary" | "ghost" | "destructive" | "success" | "link" | null | undefined;
5
5
  size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
- declare function Button({ className, variant, size, ...props }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>): import("react").JSX.Element;
8
- export { Button, buttonVariants };
7
+ type ButtonProps = ComponentProps<'button'> & VariantProps<typeof buttonVariants>;
8
+ declare function Button(props: ButtonProps): import("solid-js").JSX.Element;
9
+ export { Button, buttonVariants, type ButtonProps };