@refraction-ui/react 0.4.2 → 0.6.0
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.
- package/README.md +9 -1
- package/dist/chunk-O4453CBF.js +137 -0
- package/dist/chunk-O4453CBF.js.map +1 -0
- package/dist/chunk-XWP763SH.js +76 -0
- package/dist/chunk-XWP763SH.js.map +1 -0
- package/dist/faro-engine-47HGRAQH-JKINJPMH.js +3 -0
- package/dist/faro-engine-47HGRAQH-JKINJPMH.js.map +1 -0
- package/dist/form.cjs +340 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.cts +1 -0
- package/dist/form.d.ts +1 -0
- package/dist/form.js +204 -0
- package/dist/form.js.map +1 -0
- package/dist/index.cjs +1438 -237
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -76
- package/dist/index.d.ts +76 -76
- package/dist/index.js +1317 -312
- package/dist/index.js.map +1 -1
- package/dist/internal/analytics/index.d.cts +448 -0
- package/dist/internal/analytics/index.d.ts +448 -0
- package/dist/internal/animated-text/index.d.cts +47 -0
- package/dist/internal/animated-text/index.d.ts +47 -0
- package/dist/internal/app-shell/index.d.cts +128 -0
- package/dist/internal/app-shell/index.d.ts +128 -0
- package/dist/internal/auth/index.d.cts +74 -0
- package/dist/internal/auth/index.d.ts +74 -0
- package/dist/internal/avatar/index.d.cts +53 -0
- package/dist/internal/avatar/index.d.ts +53 -0
- package/dist/internal/avatar-group/index.d.cts +70 -0
- package/dist/internal/avatar-group/index.d.ts +70 -0
- package/dist/internal/badge/index.d.cts +26 -0
- package/dist/internal/badge/index.d.ts +26 -0
- package/dist/internal/bottom-nav/index.d.cts +29 -0
- package/dist/internal/bottom-nav/index.d.ts +29 -0
- package/dist/internal/breadcrumbs/index.d.cts +38 -0
- package/dist/internal/breadcrumbs/index.d.ts +38 -0
- package/dist/internal/button/index.d.cts +34 -0
- package/dist/internal/button/index.d.ts +34 -0
- package/dist/internal/calendar/index.d.cts +73 -0
- package/dist/internal/calendar/index.d.ts +73 -0
- package/dist/internal/card/index.d.cts +62 -0
- package/dist/internal/card/index.d.ts +62 -0
- package/dist/internal/card-grid/index.d.cts +10 -0
- package/dist/internal/card-grid/index.d.ts +10 -0
- package/dist/internal/checkbox/index.d.cts +40 -0
- package/dist/internal/checkbox/index.d.ts +40 -0
- package/dist/internal/code-editor/index.d.cts +47 -0
- package/dist/internal/code-editor/index.d.ts +47 -0
- package/dist/internal/collapsible/index.d.cts +43 -0
- package/dist/internal/collapsible/index.d.ts +43 -0
- package/dist/internal/command/index.d.cts +80 -0
- package/dist/internal/command/index.d.ts +80 -0
- package/dist/internal/content-protection/index.d.cts +47 -0
- package/dist/internal/content-protection/index.d.ts +47 -0
- package/dist/internal/data-table/index.d.cts +73 -0
- package/dist/internal/data-table/index.d.ts +73 -0
- package/dist/internal/date-picker/index.d.cts +98 -0
- package/dist/internal/date-picker/index.d.ts +98 -0
- package/dist/internal/device-frame/index.d.cts +40 -0
- package/dist/internal/device-frame/index.d.ts +40 -0
- package/dist/internal/dialog/index.d.cts +54 -0
- package/dist/internal/dialog/index.d.ts +54 -0
- package/dist/internal/dropdown-menu/index.d.cts +55 -0
- package/dist/internal/dropdown-menu/index.d.ts +55 -0
- package/dist/internal/emoji-picker/index.d.cts +77 -0
- package/dist/internal/emoji-picker/index.d.ts +77 -0
- package/dist/internal/feedback-dialog/index.d.cts +55 -0
- package/dist/internal/feedback-dialog/index.d.ts +55 -0
- package/dist/internal/file-upload/index.d.cts +91 -0
- package/dist/internal/file-upload/index.d.ts +91 -0
- package/dist/internal/footer/index.d.cts +30 -0
- package/dist/internal/footer/index.d.ts +30 -0
- package/dist/internal/inline-editor/index.d.cts +52 -0
- package/dist/internal/inline-editor/index.d.ts +52 -0
- package/dist/internal/input/index.d.cts +38 -0
- package/dist/internal/input/index.d.ts +38 -0
- package/dist/internal/input-group/index.d.cts +35 -0
- package/dist/internal/input-group/index.d.ts +35 -0
- package/dist/internal/install-prompt/index.d.cts +36 -0
- package/dist/internal/install-prompt/index.d.ts +36 -0
- package/dist/internal/keyboard-shortcut/index.d.cts +49 -0
- package/dist/internal/keyboard-shortcut/index.d.ts +49 -0
- package/dist/internal/language-selector/index.d.cts +63 -0
- package/dist/internal/language-selector/index.d.ts +63 -0
- package/dist/internal/logger/index.d.cts +229 -0
- package/dist/internal/logger/index.d.ts +229 -0
- package/dist/internal/markdown-renderer/index.d.cts +40 -0
- package/dist/internal/markdown-renderer/index.d.ts +40 -0
- package/dist/internal/mobile-nav/index.d.cts +45 -0
- package/dist/internal/mobile-nav/index.d.ts +45 -0
- package/dist/internal/navbar/index.d.cts +30 -0
- package/dist/internal/navbar/index.d.ts +30 -0
- package/dist/internal/otp-input/index.d.cts +66 -0
- package/dist/internal/otp-input/index.d.ts +66 -0
- package/dist/internal/payment/index.d.cts +9 -0
- package/dist/internal/payment/index.d.ts +9 -0
- package/dist/internal/popover/index.d.cts +46 -0
- package/dist/internal/popover/index.d.ts +46 -0
- package/dist/internal/presence-indicator/index.d.cts +38 -0
- package/dist/internal/presence-indicator/index.d.ts +38 -0
- package/dist/internal/progress-display/index.d.cts +60 -0
- package/dist/internal/progress-display/index.d.ts +60 -0
- package/dist/internal/radio/index.d.ts +43 -0
- package/dist/internal/react-accordion/index.d.ts +22 -0
- package/dist/internal/react-analytics/index.d.cts +44 -0
- package/dist/internal/react-analytics/index.d.ts +44 -0
- package/dist/internal/react-animated-text/index.d.cts +12 -0
- package/dist/internal/react-animated-text/index.d.ts +12 -0
- package/dist/internal/react-app-shell/index.d.cts +169 -0
- package/dist/internal/react-app-shell/index.d.ts +169 -0
- package/dist/internal/react-auth/index.d.cts +59 -0
- package/dist/internal/react-auth/index.d.ts +59 -0
- package/dist/internal/react-avatar/index.d.cts +20 -0
- package/dist/internal/react-avatar/index.d.ts +20 -0
- package/dist/internal/react-avatar-group/index.d.cts +33 -0
- package/dist/internal/react-avatar-group/index.d.ts +33 -0
- package/dist/internal/react-badge/index.d.cts +17 -0
- package/dist/internal/react-badge/index.d.ts +17 -0
- package/dist/internal/react-bottom-nav/index.d.cts +19 -0
- package/dist/internal/react-bottom-nav/index.d.ts +19 -0
- package/dist/internal/react-breadcrumbs/index.d.cts +24 -0
- package/dist/internal/react-breadcrumbs/index.d.ts +24 -0
- package/dist/internal/react-button/index.d.cts +21 -0
- package/dist/internal/react-button/index.d.ts +21 -0
- package/dist/internal/react-calendar/index.d.cts +44 -0
- package/dist/internal/react-calendar/index.d.ts +44 -0
- package/dist/internal/react-callout/index.d.cts +12 -0
- package/dist/internal/react-callout/index.d.ts +12 -0
- package/dist/internal/react-card/index.d.cts +29 -0
- package/dist/internal/react-card/index.d.ts +29 -0
- package/dist/internal/react-card-grid/index.d.cts +8 -0
- package/dist/internal/react-card-grid/index.d.ts +8 -0
- package/dist/internal/react-carousel/index.d.ts +22 -0
- package/dist/internal/react-checkbox/index.d.cts +18 -0
- package/dist/internal/react-checkbox/index.d.ts +18 -0
- package/dist/internal/react-code-block/index.d.cts +7 -0
- package/dist/internal/react-code-block/index.d.ts +7 -0
- package/dist/internal/react-code-editor/index.d.cts +28 -0
- package/dist/internal/react-code-editor/index.d.ts +28 -0
- package/dist/internal/react-collapsible/index.d.cts +29 -0
- package/dist/internal/react-collapsible/index.d.ts +29 -0
- package/dist/internal/react-combobox/index.d.cts +94 -0
- package/dist/internal/react-combobox/index.d.ts +94 -0
- package/dist/internal/react-command/index.d.cts +51 -0
- package/dist/internal/react-command/index.d.ts +51 -0
- package/dist/internal/react-content-protection/index.d.cts +22 -0
- package/dist/internal/react-content-protection/index.d.ts +22 -0
- package/dist/internal/react-data-table/index.d.cts +22 -0
- package/dist/internal/react-data-table/index.d.ts +22 -0
- package/dist/internal/react-date-picker/index.d.cts +23 -0
- package/dist/internal/react-date-picker/index.d.ts +23 -0
- package/dist/internal/react-device-frame/index.d.cts +37 -0
- package/dist/internal/react-device-frame/index.d.ts +37 -0
- package/dist/internal/react-dialog/index.d.cts +49 -0
- package/dist/internal/react-dialog/index.d.ts +49 -0
- package/dist/internal/react-dropdown-menu/index.d.cts +37 -0
- package/dist/internal/react-dropdown-menu/index.d.ts +37 -0
- package/dist/internal/react-emoji-picker/index.d.cts +32 -0
- package/dist/internal/react-emoji-picker/index.d.ts +32 -0
- package/dist/internal/react-feedback-dialog/index.d.cts +40 -0
- package/dist/internal/react-feedback-dialog/index.d.ts +40 -0
- package/dist/internal/react-file-tree/index.d.cts +5 -0
- package/dist/internal/react-file-tree/index.d.ts +5 -0
- package/dist/internal/react-file-upload/index.d.cts +22 -0
- package/dist/internal/react-file-upload/index.d.ts +22 -0
- package/dist/internal/react-footer/index.d.cts +20 -0
- package/dist/internal/react-footer/index.d.ts +20 -0
- package/dist/internal/react-form/index.d.cts +85 -0
- package/dist/internal/react-form/index.d.ts +85 -0
- package/dist/internal/react-icon-system/index.d.cts +5 -0
- package/dist/internal/react-icon-system/index.d.ts +5 -0
- package/dist/internal/react-inline-editor/index.d.cts +17 -0
- package/dist/internal/react-inline-editor/index.d.ts +17 -0
- package/dist/internal/react-input/index.d.cts +31 -0
- package/dist/internal/react-input/index.d.ts +31 -0
- package/dist/internal/react-input-group/index.d.cts +34 -0
- package/dist/internal/react-input-group/index.d.ts +34 -0
- package/dist/internal/react-install-prompt/index.d.cts +24 -0
- package/dist/internal/react-install-prompt/index.d.ts +24 -0
- package/dist/internal/react-keyboard-shortcut/index.d.cts +57 -0
- package/dist/internal/react-keyboard-shortcut/index.d.ts +57 -0
- package/dist/internal/react-language-selector/index.d.cts +26 -0
- package/dist/internal/react-language-selector/index.d.ts +26 -0
- package/dist/internal/react-link-card/index.d.cts +5 -0
- package/dist/internal/react-link-card/index.d.ts +5 -0
- package/dist/internal/react-logger/index.d.cts +107 -0
- package/dist/internal/react-logger/index.d.ts +107 -0
- package/dist/internal/react-markdown-renderer/index.d.cts +20 -0
- package/dist/internal/react-markdown-renderer/index.d.ts +20 -0
- package/dist/internal/react-mobile-nav/index.d.cts +33 -0
- package/dist/internal/react-mobile-nav/index.d.ts +33 -0
- package/dist/internal/react-navbar/index.d.cts +25 -0
- package/dist/internal/react-navbar/index.d.ts +25 -0
- package/dist/internal/react-otp-input/index.d.cts +21 -0
- package/dist/internal/react-otp-input/index.d.ts +21 -0
- package/dist/internal/react-pagination/index.d.ts +7 -0
- package/dist/internal/react-payment/index.d.cts +9 -0
- package/dist/internal/react-payment/index.d.ts +9 -0
- package/dist/internal/react-popover/index.d.cts +33 -0
- package/dist/internal/react-popover/index.d.ts +33 -0
- package/dist/internal/react-presence-indicator/index.d.cts +34 -0
- package/dist/internal/react-presence-indicator/index.d.ts +34 -0
- package/dist/internal/react-progress-display/index.d.cts +21 -0
- package/dist/internal/react-progress-display/index.d.ts +21 -0
- package/dist/internal/react-radio/index.d.ts +33 -0
- package/dist/internal/react-reaction-bar/index.d.cts +34 -0
- package/dist/internal/react-reaction-bar/index.d.ts +34 -0
- package/dist/internal/react-resizable-layout/index.d.cts +36 -0
- package/dist/internal/react-resizable-layout/index.d.ts +36 -0
- package/dist/internal/react-rich-editor/index.d.cts +2 -0
- package/dist/internal/react-rich-editor/index.d.ts +2 -0
- package/dist/internal/react-search-bar/index.d.cts +32 -0
- package/dist/internal/react-search-bar/index.d.ts +32 -0
- package/dist/internal/react-select/index.d.cts +30 -0
- package/dist/internal/react-select/index.d.ts +30 -0
- package/dist/internal/react-sheet/index.d.cts +67 -0
- package/dist/internal/react-sheet/index.d.ts +67 -0
- package/dist/internal/react-sidebar/index.d.cts +23 -0
- package/dist/internal/react-sidebar/index.d.ts +23 -0
- package/dist/internal/react-skeleton/index.d.cts +31 -0
- package/dist/internal/react-skeleton/index.d.ts +31 -0
- package/dist/internal/react-skip-to-content/index.d.cts +9 -0
- package/dist/internal/react-skip-to-content/index.d.ts +9 -0
- package/dist/internal/react-slide-viewer/index.d.cts +23 -0
- package/dist/internal/react-slide-viewer/index.d.ts +23 -0
- package/dist/internal/react-slider/index.d.cts +1 -0
- package/dist/internal/react-slider/index.d.ts +1 -0
- package/dist/internal/react-status-indicator/index.d.cts +34 -0
- package/dist/internal/react-status-indicator/index.d.ts +34 -0
- package/dist/internal/react-steps/index.d.cts +19 -0
- package/dist/internal/react-steps/index.d.ts +19 -0
- package/dist/internal/react-switch/index.d.cts +17 -0
- package/dist/internal/react-switch/index.d.ts +17 -0
- package/dist/internal/react-table-of-contents/index.d.ts +10 -0
- package/dist/internal/react-tabs/index.d.cts +31 -0
- package/dist/internal/react-tabs/index.d.ts +31 -0
- package/dist/internal/react-textarea/index.d.cts +16 -0
- package/dist/internal/react-textarea/index.d.ts +16 -0
- package/dist/internal/react-theme/index.d.cts +44 -0
- package/dist/internal/react-theme/index.d.ts +44 -0
- package/dist/internal/react-thread-view/index.d.cts +34 -0
- package/dist/internal/react-thread-view/index.d.ts +34 -0
- package/dist/internal/react-toast/index.d.cts +36 -0
- package/dist/internal/react-toast/index.d.ts +36 -0
- package/dist/internal/react-tooltip/index.d.cts +33 -0
- package/dist/internal/react-tooltip/index.d.ts +33 -0
- package/dist/internal/react-version-selector/index.d.cts +24 -0
- package/dist/internal/react-version-selector/index.d.ts +24 -0
- package/dist/internal/react-video-player/index.d.cts +9 -0
- package/dist/internal/react-video-player/index.d.ts +9 -0
- package/dist/internal/react-voice-pill/index.d.cts +10 -0
- package/dist/internal/react-voice-pill/index.d.ts +10 -0
- package/dist/internal/react-waveform/index.d.cts +9 -0
- package/dist/internal/react-waveform/index.d.ts +9 -0
- package/dist/internal/reaction-bar/index.d.cts +50 -0
- package/dist/internal/reaction-bar/index.d.ts +50 -0
- package/dist/internal/resizable-layout/index.d.cts +47 -0
- package/dist/internal/resizable-layout/index.d.ts +47 -0
- package/dist/internal/search-bar/index.d.cts +51 -0
- package/dist/internal/search-bar/index.d.ts +51 -0
- package/dist/internal/select/index.d.cts +69 -0
- package/dist/internal/select/index.d.ts +69 -0
- package/dist/internal/shared/index.d.cts +419 -0
- package/dist/internal/shared/index.d.ts +419 -0
- package/dist/internal/sidebar/index.d.cts +40 -0
- package/dist/internal/sidebar/index.d.ts +40 -0
- package/dist/internal/skeleton/index.d.cts +27 -0
- package/dist/internal/skeleton/index.d.ts +27 -0
- package/dist/internal/slide-viewer/index.d.cts +72 -0
- package/dist/internal/slide-viewer/index.d.ts +72 -0
- package/dist/internal/slider/index.d.cts +4 -0
- package/dist/internal/slider/index.d.ts +4 -0
- package/dist/internal/status-indicator/index.d.cts +42 -0
- package/dist/internal/status-indicator/index.d.ts +42 -0
- package/dist/internal/switch/index.d.cts +41 -0
- package/dist/internal/switch/index.d.ts +41 -0
- package/dist/internal/tabs/index.d.cts +45 -0
- package/dist/internal/tabs/index.d.ts +45 -0
- package/dist/internal/textarea/index.d.cts +28 -0
- package/dist/internal/textarea/index.d.ts +28 -0
- package/dist/internal/theme/index.d.cts +60 -0
- package/dist/internal/theme/index.d.ts +60 -0
- package/dist/internal/thread-view/index.d.cts +95 -0
- package/dist/internal/thread-view/index.d.ts +95 -0
- package/dist/internal/toast/index.d.cts +64 -0
- package/dist/internal/toast/index.d.ts +64 -0
- package/dist/internal/tooltip/index.d.cts +45 -0
- package/dist/internal/tooltip/index.d.ts +45 -0
- package/dist/internal/version-selector/index.d.cts +68 -0
- package/dist/internal/version-selector/index.d.ts +68 -0
- package/dist/internal/video-player/index.d.cts +50 -0
- package/dist/internal/video-player/index.d.ts +50 -0
- package/dist/internal/voice-pill/index.d.cts +86 -0
- package/dist/internal/voice-pill/index.d.ts +86 -0
- package/dist/internal/waveform/index.d.cts +83 -0
- package/dist/internal/waveform/index.d.ts +83 -0
- package/dist/theme.cjs +23 -0
- package/dist/theme.cjs.map +1 -1
- package/dist/theme.d.cts +1 -1
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +5 -2
- package/dist/theme.js.map +1 -1
- package/package.json +10 -7
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AccessibilityProps, TokenContract } from '../shared/index.js';
|
|
2
|
+
|
|
3
|
+
type VoicePillSpeaker = 'ai' | 'user' | (string & {});
|
|
4
|
+
type VoicePillPosition = 'inline' | 'top-start' | 'top-center' | 'top-end' | 'bottom-start' | 'bottom-center' | 'bottom-end' | 'left-start' | 'left-center' | 'left-end' | 'right-start' | 'right-center' | 'right-end';
|
|
5
|
+
interface VoicePillProps {
|
|
6
|
+
/** Speaker identity used for labels and data-speaker theming */
|
|
7
|
+
speaker?: VoicePillSpeaker;
|
|
8
|
+
/** Primary voice status text */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Optional secondary status text */
|
|
11
|
+
sub?: string;
|
|
12
|
+
/** Voice activity intensity, clamped from 0 to 1 */
|
|
13
|
+
intensity?: number;
|
|
14
|
+
/** Whether voice output/input is muted */
|
|
15
|
+
muted?: boolean;
|
|
16
|
+
/** Callback for the optional mute toggle */
|
|
17
|
+
onToggleMute?: () => void;
|
|
18
|
+
/** Viewport position, or inline for embedding inside another layout */
|
|
19
|
+
position?: VoicePillPosition;
|
|
20
|
+
}
|
|
21
|
+
interface VoicePillStyleVars {
|
|
22
|
+
[key: `--${string}`]: string;
|
|
23
|
+
}
|
|
24
|
+
interface VoicePillAPI {
|
|
25
|
+
/** Display speaker label */
|
|
26
|
+
speaker: string;
|
|
27
|
+
/** Normalized key for data-speaker */
|
|
28
|
+
speakerKey: string;
|
|
29
|
+
/** Primary label */
|
|
30
|
+
label: string;
|
|
31
|
+
/** Secondary label */
|
|
32
|
+
sub?: string;
|
|
33
|
+
/** Clamped raw intensity */
|
|
34
|
+
intensity: number;
|
|
35
|
+
/** Intensity used for visual animation after muted state is applied */
|
|
36
|
+
visualIntensity: number;
|
|
37
|
+
/** Muted state */
|
|
38
|
+
muted: boolean;
|
|
39
|
+
/** Viewport position, or inline for embedding inside another layout */
|
|
40
|
+
position: VoicePillPosition;
|
|
41
|
+
/** Fallback initials for the speaker avatar */
|
|
42
|
+
initials: string;
|
|
43
|
+
/** Invoke the mute callback when provided */
|
|
44
|
+
toggleMute(): void;
|
|
45
|
+
/** ARIA props for the live status pill */
|
|
46
|
+
ariaProps: Partial<AccessibilityProps> & Record<string, unknown>;
|
|
47
|
+
/** ARIA props for the mute toggle */
|
|
48
|
+
toggleMuteAriaProps: Record<string, unknown>;
|
|
49
|
+
/** Data attributes reflecting speaker/state */
|
|
50
|
+
dataAttributes: Record<string, string>;
|
|
51
|
+
/** CSS custom properties that drive pulse visuals */
|
|
52
|
+
style: VoicePillStyleVars;
|
|
53
|
+
}
|
|
54
|
+
declare const DEFAULT_VOICE_PILL_SPEAKER = "ai";
|
|
55
|
+
declare const DEFAULT_VOICE_PILL_POSITION: VoicePillPosition;
|
|
56
|
+
declare function clampVoicePillIntensity(intensity?: number): number;
|
|
57
|
+
declare function getVoicePillSpeakerKey(speaker?: VoicePillSpeaker): string;
|
|
58
|
+
declare function getVoicePillSpeakerLabel(speaker?: VoicePillSpeaker): string;
|
|
59
|
+
declare function getVoicePillInitials(speaker?: VoicePillSpeaker): string;
|
|
60
|
+
declare function getVoicePillPosition(position?: VoicePillPosition): VoicePillPosition;
|
|
61
|
+
declare function getVoicePillAriaLabel(props: {
|
|
62
|
+
speaker?: VoicePillSpeaker;
|
|
63
|
+
label: string;
|
|
64
|
+
sub?: string;
|
|
65
|
+
muted?: boolean;
|
|
66
|
+
}): string;
|
|
67
|
+
declare function getVoicePillPulseStyle(intensity?: number, muted?: boolean): VoicePillStyleVars;
|
|
68
|
+
declare function createVoicePill(props: VoicePillProps): VoicePillAPI;
|
|
69
|
+
|
|
70
|
+
declare const voicePillTokens: TokenContract;
|
|
71
|
+
declare const voicePillRootStyles = "inline-flex min-w-0 max-w-[min(calc(100vw-2rem),22rem)] items-center gap-3 rounded-full border border-border bg-background px-3 py-2 text-foreground shadow-lg ring-1 ring-border/50 transition-opacity data-[muted=true]:opacity-80";
|
|
72
|
+
declare const voicePillSpeakerStyles = "[--rfr-voice-pill-accent:hsl(var(--primary))] [--rfr-voice-pill-accent-foreground:hsl(var(--primary-foreground))] data-[speaker=user]:[--rfr-voice-pill-accent:hsl(var(--accent-foreground))] data-[speaker=user]:[--rfr-voice-pill-accent-foreground:hsl(var(--accent))] data-[muted=true]:[--rfr-voice-pill-accent:hsl(var(--muted-foreground))]";
|
|
73
|
+
declare const voicePillPositionVariants: (props?: ({
|
|
74
|
+
position?: "inline" | "top-start" | "top-center" | "top-end" | "bottom-start" | "bottom-center" | "bottom-end" | "left-start" | "left-center" | "left-end" | "right-start" | "right-center" | "right-end" | undefined;
|
|
75
|
+
} & {
|
|
76
|
+
className?: string;
|
|
77
|
+
}) | undefined) => string;
|
|
78
|
+
declare const voicePillAvatarWrapStyles = "relative flex h-10 w-10 shrink-0 items-center justify-center";
|
|
79
|
+
declare const voicePillPulseRingStyles = "pointer-events-none absolute inset-0 rounded-full border border-[var(--rfr-voice-pill-accent)] opacity-[var(--rfr-voice-pill-ring-opacity)] motion-safe:animate-ping motion-reduce:animate-none";
|
|
80
|
+
declare const voicePillAvatarStyles = "relative z-10 flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-[var(--rfr-voice-pill-accent)] text-[var(--rfr-voice-pill-accent-foreground)] text-xs font-semibold uppercase";
|
|
81
|
+
declare const voicePillTextStyles = "min-w-0 flex-1";
|
|
82
|
+
declare const voicePillLabelStyles = "block truncate text-sm font-medium leading-tight";
|
|
83
|
+
declare const voicePillSubStyles = "block truncate text-xs leading-tight text-muted-foreground";
|
|
84
|
+
declare const voicePillMuteButtonStyles = "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
85
|
+
|
|
86
|
+
export { DEFAULT_VOICE_PILL_POSITION, DEFAULT_VOICE_PILL_SPEAKER, type VoicePillAPI, type VoicePillPosition, type VoicePillProps, type VoicePillSpeaker, type VoicePillStyleVars, clampVoicePillIntensity, createVoicePill, getVoicePillAriaLabel, getVoicePillInitials, getVoicePillPosition, getVoicePillPulseStyle, getVoicePillSpeakerKey, getVoicePillSpeakerLabel, voicePillAvatarStyles, voicePillAvatarWrapStyles, voicePillLabelStyles, voicePillMuteButtonStyles, voicePillPositionVariants, voicePillPulseRingStyles, voicePillRootStyles, voicePillSpeakerStyles, voicePillSubStyles, voicePillTextStyles, voicePillTokens };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { AccessibilityProps } from '../shared/index.cjs';
|
|
2
|
+
|
|
3
|
+
type WaveformVariant = 'bars' | 'line' | 'rings';
|
|
4
|
+
type WaveformSampleInput = Float32Array | number[];
|
|
5
|
+
type WaveformSource = AnalyserNode | MediaStream | WaveformSampleInput;
|
|
6
|
+
interface WaveformProps {
|
|
7
|
+
source?: WaveformSource;
|
|
8
|
+
samples?: WaveformSampleInput;
|
|
9
|
+
intensity?: number;
|
|
10
|
+
amplitude?: number;
|
|
11
|
+
variant?: WaveformVariant;
|
|
12
|
+
height?: number | string;
|
|
13
|
+
width?: number | string;
|
|
14
|
+
barCount?: number;
|
|
15
|
+
smoothing?: number;
|
|
16
|
+
color?: string;
|
|
17
|
+
paused?: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface NormalizedWaveformConfig {
|
|
20
|
+
variant: WaveformVariant;
|
|
21
|
+
intensity: number;
|
|
22
|
+
amplitude: number;
|
|
23
|
+
height: number | string;
|
|
24
|
+
width: number | string;
|
|
25
|
+
barCount: number;
|
|
26
|
+
smoothing: number;
|
|
27
|
+
color: string;
|
|
28
|
+
paused: boolean;
|
|
29
|
+
}
|
|
30
|
+
interface WaveformAPI {
|
|
31
|
+
config: NormalizedWaveformConfig;
|
|
32
|
+
samples: Float32Array;
|
|
33
|
+
ariaProps: Partial<AccessibilityProps>;
|
|
34
|
+
dataAttributes: Record<'data-variant' | 'data-paused', string>;
|
|
35
|
+
getSamples: (barCount?: number) => Float32Array;
|
|
36
|
+
}
|
|
37
|
+
declare const DEFAULT_WAVEFORM_BAR_COUNT = 48;
|
|
38
|
+
declare const DEFAULT_WAVEFORM_COLOR = "var(--accent-2)";
|
|
39
|
+
declare const DEFAULT_WAVEFORM_HEIGHT = 80;
|
|
40
|
+
declare const DEFAULT_WAVEFORM_INTENSITY = 1;
|
|
41
|
+
declare const DEFAULT_WAVEFORM_SMOOTHING = 0.8;
|
|
42
|
+
declare function normalizeBarCount(value: number | undefined): number;
|
|
43
|
+
declare function normalizeSmoothing(value: number | undefined): number;
|
|
44
|
+
declare function normalizeIntensity(value: number | undefined): number;
|
|
45
|
+
declare function normalizeWaveformConfig(props?: WaveformProps): NormalizedWaveformConfig;
|
|
46
|
+
declare function isWaveformSampleInput(value: unknown): value is WaveformSampleInput;
|
|
47
|
+
declare function createSilentSamples(count?: number): Float32Array;
|
|
48
|
+
declare function createIntensitySamples(intensity?: number, count?: number, phase?: number): Float32Array;
|
|
49
|
+
declare function normalizeWaveformSamples(input: WaveformSampleInput | undefined, targetCount?: number): Float32Array;
|
|
50
|
+
declare function resampleWaveformSamples(samples: WaveformSampleInput, targetCount: number): Float32Array;
|
|
51
|
+
declare function smoothWaveformSamples(previous: WaveformSampleInput | undefined, next: WaveformSampleInput, smoothing?: number): Float32Array;
|
|
52
|
+
declare function scaleWaveformSamples(samples: WaveformSampleInput, intensity?: number): Float32Array;
|
|
53
|
+
declare function getWaveformPeak(samples: WaveformSampleInput): number;
|
|
54
|
+
declare function toCssDimension(value: number | string | undefined): string | undefined;
|
|
55
|
+
declare function createWaveform(props?: WaveformProps): WaveformAPI;
|
|
56
|
+
|
|
57
|
+
interface WaveformCanvasSize {
|
|
58
|
+
width: number;
|
|
59
|
+
height: number;
|
|
60
|
+
pixelRatio?: number;
|
|
61
|
+
}
|
|
62
|
+
interface WaveformRenderOptions {
|
|
63
|
+
variant?: WaveformVariant;
|
|
64
|
+
color?: string;
|
|
65
|
+
intensity?: number;
|
|
66
|
+
amplitude?: number;
|
|
67
|
+
barCount?: number;
|
|
68
|
+
}
|
|
69
|
+
declare function prepareWaveformCanvas(canvas: HTMLCanvasElement, size: WaveformCanvasSize): CanvasRenderingContext2D | null;
|
|
70
|
+
declare function drawWaveform(context: CanvasRenderingContext2D, samples: WaveformSampleInput, size: WaveformCanvasSize, options?: WaveformRenderOptions): void;
|
|
71
|
+
|
|
72
|
+
declare const waveformVariants: (props?: ({
|
|
73
|
+
variant?: "bars" | "line" | "rings" | undefined;
|
|
74
|
+
} & {
|
|
75
|
+
className?: string;
|
|
76
|
+
}) | undefined) => string;
|
|
77
|
+
declare const waveformCanvasVariants: (props?: ({
|
|
78
|
+
[x: string]: string | undefined;
|
|
79
|
+
} & {
|
|
80
|
+
className?: string;
|
|
81
|
+
}) | undefined) => string;
|
|
82
|
+
|
|
83
|
+
export { DEFAULT_WAVEFORM_BAR_COUNT, DEFAULT_WAVEFORM_COLOR, DEFAULT_WAVEFORM_HEIGHT, DEFAULT_WAVEFORM_INTENSITY, DEFAULT_WAVEFORM_SMOOTHING, type NormalizedWaveformConfig, type WaveformAPI, type WaveformCanvasSize, type WaveformProps, type WaveformRenderOptions, type WaveformSampleInput, type WaveformSource, type WaveformVariant, createIntensitySamples, createSilentSamples, createWaveform, drawWaveform, getWaveformPeak, isWaveformSampleInput, normalizeBarCount, normalizeIntensity, normalizeSmoothing, normalizeWaveformConfig, normalizeWaveformSamples, prepareWaveformCanvas, resampleWaveformSamples, scaleWaveformSamples, smoothWaveformSamples, toCssDimension, waveformCanvasVariants, waveformVariants };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { AccessibilityProps } from '../shared/index.js';
|
|
2
|
+
|
|
3
|
+
type WaveformVariant = 'bars' | 'line' | 'rings';
|
|
4
|
+
type WaveformSampleInput = Float32Array | number[];
|
|
5
|
+
type WaveformSource = AnalyserNode | MediaStream | WaveformSampleInput;
|
|
6
|
+
interface WaveformProps {
|
|
7
|
+
source?: WaveformSource;
|
|
8
|
+
samples?: WaveformSampleInput;
|
|
9
|
+
intensity?: number;
|
|
10
|
+
amplitude?: number;
|
|
11
|
+
variant?: WaveformVariant;
|
|
12
|
+
height?: number | string;
|
|
13
|
+
width?: number | string;
|
|
14
|
+
barCount?: number;
|
|
15
|
+
smoothing?: number;
|
|
16
|
+
color?: string;
|
|
17
|
+
paused?: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface NormalizedWaveformConfig {
|
|
20
|
+
variant: WaveformVariant;
|
|
21
|
+
intensity: number;
|
|
22
|
+
amplitude: number;
|
|
23
|
+
height: number | string;
|
|
24
|
+
width: number | string;
|
|
25
|
+
barCount: number;
|
|
26
|
+
smoothing: number;
|
|
27
|
+
color: string;
|
|
28
|
+
paused: boolean;
|
|
29
|
+
}
|
|
30
|
+
interface WaveformAPI {
|
|
31
|
+
config: NormalizedWaveformConfig;
|
|
32
|
+
samples: Float32Array;
|
|
33
|
+
ariaProps: Partial<AccessibilityProps>;
|
|
34
|
+
dataAttributes: Record<'data-variant' | 'data-paused', string>;
|
|
35
|
+
getSamples: (barCount?: number) => Float32Array;
|
|
36
|
+
}
|
|
37
|
+
declare const DEFAULT_WAVEFORM_BAR_COUNT = 48;
|
|
38
|
+
declare const DEFAULT_WAVEFORM_COLOR = "var(--accent-2)";
|
|
39
|
+
declare const DEFAULT_WAVEFORM_HEIGHT = 80;
|
|
40
|
+
declare const DEFAULT_WAVEFORM_INTENSITY = 1;
|
|
41
|
+
declare const DEFAULT_WAVEFORM_SMOOTHING = 0.8;
|
|
42
|
+
declare function normalizeBarCount(value: number | undefined): number;
|
|
43
|
+
declare function normalizeSmoothing(value: number | undefined): number;
|
|
44
|
+
declare function normalizeIntensity(value: number | undefined): number;
|
|
45
|
+
declare function normalizeWaveformConfig(props?: WaveformProps): NormalizedWaveformConfig;
|
|
46
|
+
declare function isWaveformSampleInput(value: unknown): value is WaveformSampleInput;
|
|
47
|
+
declare function createSilentSamples(count?: number): Float32Array;
|
|
48
|
+
declare function createIntensitySamples(intensity?: number, count?: number, phase?: number): Float32Array;
|
|
49
|
+
declare function normalizeWaveformSamples(input: WaveformSampleInput | undefined, targetCount?: number): Float32Array;
|
|
50
|
+
declare function resampleWaveformSamples(samples: WaveformSampleInput, targetCount: number): Float32Array;
|
|
51
|
+
declare function smoothWaveformSamples(previous: WaveformSampleInput | undefined, next: WaveformSampleInput, smoothing?: number): Float32Array;
|
|
52
|
+
declare function scaleWaveformSamples(samples: WaveformSampleInput, intensity?: number): Float32Array;
|
|
53
|
+
declare function getWaveformPeak(samples: WaveformSampleInput): number;
|
|
54
|
+
declare function toCssDimension(value: number | string | undefined): string | undefined;
|
|
55
|
+
declare function createWaveform(props?: WaveformProps): WaveformAPI;
|
|
56
|
+
|
|
57
|
+
interface WaveformCanvasSize {
|
|
58
|
+
width: number;
|
|
59
|
+
height: number;
|
|
60
|
+
pixelRatio?: number;
|
|
61
|
+
}
|
|
62
|
+
interface WaveformRenderOptions {
|
|
63
|
+
variant?: WaveformVariant;
|
|
64
|
+
color?: string;
|
|
65
|
+
intensity?: number;
|
|
66
|
+
amplitude?: number;
|
|
67
|
+
barCount?: number;
|
|
68
|
+
}
|
|
69
|
+
declare function prepareWaveformCanvas(canvas: HTMLCanvasElement, size: WaveformCanvasSize): CanvasRenderingContext2D | null;
|
|
70
|
+
declare function drawWaveform(context: CanvasRenderingContext2D, samples: WaveformSampleInput, size: WaveformCanvasSize, options?: WaveformRenderOptions): void;
|
|
71
|
+
|
|
72
|
+
declare const waveformVariants: (props?: ({
|
|
73
|
+
variant?: "bars" | "line" | "rings" | undefined;
|
|
74
|
+
} & {
|
|
75
|
+
className?: string;
|
|
76
|
+
}) | undefined) => string;
|
|
77
|
+
declare const waveformCanvasVariants: (props?: ({
|
|
78
|
+
[x: string]: string | undefined;
|
|
79
|
+
} & {
|
|
80
|
+
className?: string;
|
|
81
|
+
}) | undefined) => string;
|
|
82
|
+
|
|
83
|
+
export { DEFAULT_WAVEFORM_BAR_COUNT, DEFAULT_WAVEFORM_COLOR, DEFAULT_WAVEFORM_HEIGHT, DEFAULT_WAVEFORM_INTENSITY, DEFAULT_WAVEFORM_SMOOTHING, type NormalizedWaveformConfig, type WaveformAPI, type WaveformCanvasSize, type WaveformProps, type WaveformRenderOptions, type WaveformSampleInput, type WaveformSource, type WaveformVariant, createIntensitySamples, createSilentSamples, createWaveform, drawWaveform, getWaveformPeak, isWaveformSampleInput, normalizeBarCount, normalizeIntensity, normalizeSmoothing, normalizeWaveformConfig, normalizeWaveformSamples, prepareWaveformCanvas, resampleWaveformSamples, scaleWaveformSamples, smoothWaveformSamples, toCssDimension, waveformCanvasVariants, waveformVariants };
|
package/dist/theme.cjs
CHANGED
|
@@ -137,6 +137,25 @@ function applyThemeToDOM(resolved, attribute = "class") {
|
|
|
137
137
|
root.style.colorScheme = resolved;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// ../shared/dist/index.js
|
|
141
|
+
var seen = /* @__PURE__ */ new Set();
|
|
142
|
+
function isDev() {
|
|
143
|
+
return typeof process === "undefined" || process.env?.NODE_ENV !== "production";
|
|
144
|
+
}
|
|
145
|
+
function emit(level, code, message, detail) {
|
|
146
|
+
if (!isDev()) return;
|
|
147
|
+
const key = `${level}:${code}`;
|
|
148
|
+
if (seen.has(key)) return;
|
|
149
|
+
seen.add(key);
|
|
150
|
+
const text = `[refraction-ui] ${code}: ${message}`;
|
|
151
|
+
{
|
|
152
|
+
console.warn(text, "");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function devWarn(code, message, detail) {
|
|
156
|
+
emit("warn", code, message);
|
|
157
|
+
}
|
|
158
|
+
|
|
140
159
|
// ../react-theme/dist/index.js
|
|
141
160
|
var ThemeContext = React2__namespace.createContext(null);
|
|
142
161
|
function ThemeProvider({
|
|
@@ -180,6 +199,10 @@ function ThemeProvider({
|
|
|
180
199
|
function useTheme() {
|
|
181
200
|
const context = React2__namespace.useContext(ThemeContext);
|
|
182
201
|
if (!context) {
|
|
202
|
+
devWarn(
|
|
203
|
+
"react-theme/use-theme-outside-provider",
|
|
204
|
+
"useTheme() was called outside a <ThemeProvider>. Wrap your app (or the consuming subtree) in <ThemeProvider> so the theme context is available."
|
|
205
|
+
);
|
|
183
206
|
throw new Error("useTheme must be used within a <ThemeProvider>");
|
|
184
207
|
}
|
|
185
208
|
return context;
|
package/dist/theme.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../theme/src/theme-machine.ts","../../theme/src/theme-script.ts","../../theme/src/dom-adapters.ts","../../react-theme/src/theme-provider.tsx","../../react-theme/src/theme-toggle.tsx","../../react-theme/src/theme-script-component.tsx"],"names":["React","React2","React3"],"mappings":"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","file":"theme.cjs","sourcesContent":["/**\n * Headless theme state machine — pure TypeScript, zero DOM dependencies.\n * Manages light/dark/system mode with system preference tracking.\n */\n\nexport type ThemeMode = 'light' | 'dark' | 'system'\nexport type ResolvedTheme = 'light' | 'dark'\n\nexport interface ThemeState {\n /** User's chosen mode */\n mode: ThemeMode\n /** Resolved theme after system preference detection */\n resolved: ResolvedTheme\n}\n\nexport interface ThemeConfig {\n /** Initial mode. Default: 'system' */\n defaultMode?: ThemeMode\n /** localStorage key. Default: 'rfr-theme' */\n storageKey?: string\n /** HTML attribute to set. Default: 'class' */\n attribute?: 'class' | 'data-theme'\n}\n\nexport interface StorageAdapter {\n get(key: string): string | null\n set(key: string, value: string): void\n}\n\nexport interface MediaQueryAdapter {\n matches(query: string): boolean\n subscribe(query: string, callback: (matches: boolean) => void): () => void\n}\n\nexport interface ThemeAPI {\n /** Get current state */\n getState(): ThemeState\n /** Set mode (light/dark/system) */\n setMode(mode: ThemeMode): void\n /** Subscribe to state changes */\n subscribe(fn: (state: ThemeState) => void): () => void\n /** Clean up subscriptions */\n destroy(): void\n}\n\nfunction resolveTheme(mode: ThemeMode, systemPrefersDark: boolean): ResolvedTheme {\n if (mode === 'system') {\n return systemPrefersDark ? 'dark' : 'light'\n }\n return mode\n}\n\nexport function createTheme(\n config: ThemeConfig = {},\n storage?: StorageAdapter,\n mediaQuery?: MediaQueryAdapter,\n): ThemeAPI {\n const {\n defaultMode = 'system',\n storageKey = 'rfr-theme',\n } = config\n\n const listeners = new Set<(state: ThemeState) => void>()\n let cleanupMediaQuery: (() => void) | null = null\n\n // Read persisted mode or use default\n const persisted = storage?.get(storageKey) as ThemeMode | null\n let mode: ThemeMode = persisted && ['light', 'dark', 'system'].includes(persisted)\n ? persisted\n : defaultMode\n\n // Detect system preference\n let systemPrefersDark = mediaQuery?.matches('(prefers-color-scheme: dark)') ?? false\n\n let state: ThemeState = {\n mode,\n resolved: resolveTheme(mode, systemPrefersDark),\n }\n\n function notify() {\n for (const fn of listeners) {\n fn(state)\n }\n }\n\n function updateState(newMode: ThemeMode) {\n mode = newMode\n state = { mode, resolved: resolveTheme(mode, systemPrefersDark) }\n storage?.set(storageKey, mode)\n notify()\n }\n\n // Listen for system preference changes\n if (mediaQuery) {\n cleanupMediaQuery = mediaQuery.subscribe(\n '(prefers-color-scheme: dark)',\n (matches) => {\n systemPrefersDark = matches\n if (mode === 'system') {\n state = { mode, resolved: resolveTheme(mode, systemPrefersDark) }\n notify()\n }\n },\n )\n }\n\n return {\n getState() {\n return state\n },\n\n setMode(newMode: ThemeMode) {\n if (newMode !== mode) {\n updateState(newMode)\n }\n },\n\n subscribe(fn: (state: ThemeState) => void) {\n listeners.add(fn)\n return () => {\n listeners.delete(fn)\n }\n },\n\n destroy() {\n listeners.clear()\n cleanupMediaQuery?.()\n },\n }\n}\n","/**\n * Inline script for preventing theme flash on page load.\n * Inject this as a <script> tag in the <head> before any CSS.\n * Works with any framework (React, Angular, Astro, plain HTML).\n */\n\nexport function getThemeScript(\n storageKey = 'rfr-theme',\n attribute: 'class' | 'data-theme' = 'class',\n): string {\n // This string is injected as innerHTML of a <script> tag.\n // It runs before any CSS/JS loads, preventing flash of wrong theme.\n return `(function(){try{var m=localStorage.getItem('${storageKey}');var s=window.matchMedia('(prefers-color-scheme:dark)').matches;var t=m==='dark'||(m!=='light'&&s)?'dark':'light';var d=document.documentElement;${\n attribute === 'class'\n ? \"d.classList.remove('light','dark');d.classList.add(t);\"\n : `d.setAttribute('${attribute}',t);`\n }d.style.colorScheme=t;}catch(e){}})()`\n}\n","/**\n * Browser DOM adapters for the theme machine.\n * These bridge the headless core to browser APIs.\n */\n\nimport type { StorageAdapter, MediaQueryAdapter, ResolvedTheme } from './theme-machine.js'\n\n/** localStorage adapter */\nexport function createLocalStorageAdapter(): StorageAdapter {\n return {\n get(key) {\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n },\n set(key, value) {\n try {\n localStorage.setItem(key, value)\n } catch {\n // localStorage unavailable (SSR, incognito quota exceeded, etc.)\n }\n },\n }\n}\n\n/** matchMedia adapter */\nexport function createMediaQueryAdapter(): MediaQueryAdapter {\n return {\n matches(query) {\n if (typeof window === 'undefined') return false\n return window.matchMedia(query).matches\n },\n subscribe(query, callback) {\n if (typeof window === 'undefined') return () => {}\n const mql = window.matchMedia(query)\n const handler = (e: MediaQueryListEvent) => callback(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n },\n }\n}\n\n/** Apply resolved theme to the document */\nexport function applyThemeToDOM(\n resolved: ResolvedTheme,\n attribute: 'class' | 'data-theme' = 'class',\n): void {\n if (typeof document === 'undefined') return\n\n const root = document.documentElement\n if (attribute === 'class') {\n root.classList.remove('light', 'dark')\n root.classList.add(resolved)\n } else {\n root.setAttribute(attribute, resolved)\n }\n root.style.colorScheme = resolved\n}\n","import * as React from 'react'\nimport {\n createTheme,\n createLocalStorageAdapter,\n createMediaQueryAdapter,\n applyThemeToDOM,\n type ThemeMode,\n type ResolvedTheme,\n type ThemeConfig,\n type ThemeAPI,\n} from '@refraction-ui/theme'\n\nexport interface ThemeContextValue {\n mode: ThemeMode\n resolved: ResolvedTheme\n setMode: (mode: ThemeMode) => void\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null)\n\nexport interface ThemeProviderProps extends ThemeConfig {\n children: React.ReactNode\n}\n\nexport function ThemeProvider({\n children,\n defaultMode = 'system',\n storageKey = 'rfr-theme',\n attribute = 'class',\n}: ThemeProviderProps) {\n const themeRef = React.useRef<ThemeAPI | null>(null)\n\n // Initialize theme machine once (client-side only for adapters)\n if (!themeRef.current) {\n const isBrowser = typeof window !== 'undefined'\n themeRef.current = createTheme(\n { defaultMode, storageKey, attribute },\n isBrowser ? createLocalStorageAdapter() : undefined,\n isBrowser ? createMediaQueryAdapter() : undefined,\n )\n }\n\n const [state, setState] = React.useState(() => themeRef.current!.getState())\n\n React.useEffect(() => {\n const theme = themeRef.current!\n // Apply initial theme to DOM\n applyThemeToDOM(theme.getState().resolved, attribute)\n\n // Subscribe to changes\n const unsub = theme.subscribe((newState) => {\n setState(newState)\n applyThemeToDOM(newState.resolved, attribute)\n })\n\n return () => {\n unsub()\n theme.destroy()\n }\n }, [attribute])\n\n const contextValue = React.useMemo<ThemeContextValue>(\n () => ({\n mode: state.mode,\n resolved: state.resolved,\n setMode: (mode: ThemeMode) => themeRef.current?.setMode(mode),\n }),\n [state.mode, state.resolved],\n )\n\n return React.createElement(ThemeContext.Provider, { value: contextValue }, children)\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = React.useContext(ThemeContext)\n if (!context) {\n throw new Error('useTheme must be used within a <ThemeProvider>')\n }\n return context\n}\n","import * as React from 'react'\nimport { useTheme } from './theme-provider.js'\nimport type { ThemeMode } from '@refraction-ui/theme'\n\nconst modes: { value: ThemeMode; label: string; icon: string }[] = [\n { value: 'light', label: 'Light', icon: 'sun' },\n { value: 'dark', label: 'Dark', icon: 'moon' },\n { value: 'system', label: 'System', icon: 'monitor' },\n]\n\n// Inline SVG icons — no external icon library dependency\nconst icons: Record<string, React.ReactNode> = {\n sun: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('circle', { cx: 12, cy: 12, r: 5 }),\n React.createElement('path', { d: 'M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42' }),\n ),\n moon: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('path', { d: 'M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z' }),\n ),\n monitor: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('rect', { x: 2, y: 3, width: 20, height: 14, rx: 2, ry: 2 }),\n React.createElement('line', { x1: 8, y1: 21, x2: 16, y2: 21 }),\n React.createElement('line', { x1: 12, y1: 17, x2: 12, y2: 21 }),\n ),\n}\n\nexport interface ThemeToggleProps {\n className?: string\n /** 'dropdown' shows a menu, 'segmented' shows inline buttons */\n variant?: 'dropdown' | 'segmented'\n}\n\nexport function ThemeToggle({ className, variant = 'segmented' }: ThemeToggleProps) {\n const { mode, setMode } = useTheme()\n\n if (variant === 'segmented') {\n return React.createElement('div', {\n className: `inline-flex items-center gap-1 rounded-lg border p-1 ${className ?? ''}`,\n role: 'radiogroup',\n 'aria-label': 'Theme',\n },\n modes.map(({ value, label, icon }) =>\n React.createElement('button', {\n key: value,\n type: 'button',\n role: 'radio',\n 'aria-checked': mode === value,\n 'aria-label': label,\n className: `inline-flex items-center justify-center rounded-md p-1.5 text-sm transition-colors ${\n mode === value\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted'\n }`,\n onClick: () => setMode(value),\n }, icons[icon]),\n ),\n )\n }\n\n // Dropdown variant — simplified, no external dropdown dependency\n const [open, setOpen] = React.useState(false)\n const ref = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n if (!open) return\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)\n }\n document.addEventListener('mousedown', handler)\n return () => document.removeEventListener('mousedown', handler)\n }, [open])\n\n const currentIcon = modes.find((m) => m.value === mode)?.icon ?? 'monitor'\n\n return React.createElement('div', { ref, className: `relative ${className ?? ''}` },\n React.createElement('button', {\n type: 'button',\n 'aria-label': 'Toggle theme',\n 'aria-expanded': open,\n className: 'inline-flex items-center justify-center rounded-md p-2 text-sm transition-colors hover:bg-muted',\n onClick: () => setOpen(!open),\n }, icons[currentIcon]),\n open && React.createElement('div', {\n className: 'absolute right-0 top-full mt-1 z-50 min-w-[8rem] rounded-md border bg-popover p-1 shadow-md',\n role: 'menu',\n },\n modes.map(({ value, label, icon }) =>\n React.createElement('button', {\n key: value,\n type: 'button',\n role: 'menuitem',\n className: `flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors hover:bg-accent ${\n mode === value ? 'bg-accent' : ''\n }`,\n onClick: () => { setMode(value); setOpen(false) },\n }, icons[icon], label),\n ),\n ),\n )\n}\n","import * as React from 'react'\nimport { getThemeScript } from '@refraction-ui/theme'\n\nexport interface ThemeScriptProps {\n storageKey?: string\n attribute?: 'class' | 'data-theme'\n}\n\n/**\n * Renders an inline <script> that prevents theme flash on SSR pages.\n * Place this in the <head> of your document (in Next.js layout.tsx, Remix root, etc.)\n */\nexport function ThemeScript({\n storageKey = 'rfr-theme',\n attribute = 'class',\n}: ThemeScriptProps) {\n return React.createElement('script', {\n dangerouslySetInnerHTML: {\n __html: getThemeScript(storageKey, attribute),\n },\n })\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../theme/src/theme-machine.ts","../../theme/src/theme-script.ts","../../theme/src/dom-adapters.ts","../../shared/src/dev-feedback.ts","../../react-theme/src/theme-provider.tsx","../../react-theme/src/theme-toggle.tsx","../../react-theme/src/theme-script-component.tsx"],"names":["React","React2","React3"],"mappings":"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","file":"theme.cjs","sourcesContent":["/**\n * Headless theme state machine — pure TypeScript, zero DOM dependencies.\n * Manages light/dark/system mode with system preference tracking.\n */\n\nexport type ThemeMode = 'light' | 'dark' | 'system'\nexport type ResolvedTheme = 'light' | 'dark'\n\nexport interface ThemeState {\n /** User's chosen mode */\n mode: ThemeMode\n /** Resolved theme after system preference detection */\n resolved: ResolvedTheme\n}\n\nexport interface ThemeConfig {\n /** Initial mode. Default: 'system' */\n defaultMode?: ThemeMode\n /** localStorage key. Default: 'rfr-theme' */\n storageKey?: string\n /** HTML attribute to set. Default: 'class' */\n attribute?: 'class' | 'data-theme'\n}\n\nexport interface StorageAdapter {\n get(key: string): string | null\n set(key: string, value: string): void\n}\n\nexport interface MediaQueryAdapter {\n matches(query: string): boolean\n subscribe(query: string, callback: (matches: boolean) => void): () => void\n}\n\nexport interface ThemeAPI {\n /** Get current state */\n getState(): ThemeState\n /** Set mode (light/dark/system) */\n setMode(mode: ThemeMode): void\n /** Subscribe to state changes */\n subscribe(fn: (state: ThemeState) => void): () => void\n /** Clean up subscriptions */\n destroy(): void\n}\n\nfunction resolveTheme(mode: ThemeMode, systemPrefersDark: boolean): ResolvedTheme {\n if (mode === 'system') {\n return systemPrefersDark ? 'dark' : 'light'\n }\n return mode\n}\n\nexport function createTheme(\n config: ThemeConfig = {},\n storage?: StorageAdapter,\n mediaQuery?: MediaQueryAdapter,\n): ThemeAPI {\n const {\n defaultMode = 'system',\n storageKey = 'rfr-theme',\n } = config\n\n const listeners = new Set<(state: ThemeState) => void>()\n let cleanupMediaQuery: (() => void) | null = null\n\n // Read persisted mode or use default\n const persisted = storage?.get(storageKey) as ThemeMode | null\n let mode: ThemeMode = persisted && ['light', 'dark', 'system'].includes(persisted)\n ? persisted\n : defaultMode\n\n // Detect system preference\n let systemPrefersDark = mediaQuery?.matches('(prefers-color-scheme: dark)') ?? false\n\n let state: ThemeState = {\n mode,\n resolved: resolveTheme(mode, systemPrefersDark),\n }\n\n function notify() {\n for (const fn of listeners) {\n fn(state)\n }\n }\n\n function updateState(newMode: ThemeMode) {\n mode = newMode\n state = { mode, resolved: resolveTheme(mode, systemPrefersDark) }\n storage?.set(storageKey, mode)\n notify()\n }\n\n // Listen for system preference changes\n if (mediaQuery) {\n cleanupMediaQuery = mediaQuery.subscribe(\n '(prefers-color-scheme: dark)',\n (matches) => {\n systemPrefersDark = matches\n if (mode === 'system') {\n state = { mode, resolved: resolveTheme(mode, systemPrefersDark) }\n notify()\n }\n },\n )\n }\n\n return {\n getState() {\n return state\n },\n\n setMode(newMode: ThemeMode) {\n if (newMode !== mode) {\n updateState(newMode)\n }\n },\n\n subscribe(fn: (state: ThemeState) => void) {\n listeners.add(fn)\n return () => {\n listeners.delete(fn)\n }\n },\n\n destroy() {\n listeners.clear()\n cleanupMediaQuery?.()\n },\n }\n}\n","/**\n * Inline script for preventing theme flash on page load.\n * Inject this as a <script> tag in the <head> before any CSS.\n * Works with any framework (React, Angular, Astro, plain HTML).\n */\n\nexport function getThemeScript(\n storageKey = 'rfr-theme',\n attribute: 'class' | 'data-theme' = 'class',\n): string {\n // This string is injected as innerHTML of a <script> tag.\n // It runs before any CSS/JS loads, preventing flash of wrong theme.\n return `(function(){try{var m=localStorage.getItem('${storageKey}');var s=window.matchMedia('(prefers-color-scheme:dark)').matches;var t=m==='dark'||(m!=='light'&&s)?'dark':'light';var d=document.documentElement;${\n attribute === 'class'\n ? \"d.classList.remove('light','dark');d.classList.add(t);\"\n : `d.setAttribute('${attribute}',t);`\n }d.style.colorScheme=t;}catch(e){}})()`\n}\n","/**\n * Browser DOM adapters for the theme machine.\n * These bridge the headless core to browser APIs.\n */\n\nimport type { StorageAdapter, MediaQueryAdapter, ResolvedTheme } from './theme-machine.js'\n\n/** localStorage adapter */\nexport function createLocalStorageAdapter(): StorageAdapter {\n return {\n get(key) {\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n },\n set(key, value) {\n try {\n localStorage.setItem(key, value)\n } catch {\n // localStorage unavailable (SSR, incognito quota exceeded, etc.)\n }\n },\n }\n}\n\n/** matchMedia adapter */\nexport function createMediaQueryAdapter(): MediaQueryAdapter {\n return {\n matches(query) {\n if (typeof window === 'undefined') return false\n return window.matchMedia(query).matches\n },\n subscribe(query, callback) {\n if (typeof window === 'undefined') return () => {}\n const mql = window.matchMedia(query)\n const handler = (e: MediaQueryListEvent) => callback(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n },\n }\n}\n\n/** Apply resolved theme to the document */\nexport function applyThemeToDOM(\n resolved: ResolvedTheme,\n attribute: 'class' | 'data-theme' = 'class',\n): void {\n if (typeof document === 'undefined') return\n\n const root = document.documentElement\n if (attribute === 'class') {\n root.classList.remove('light', 'dark')\n root.classList.add(resolved)\n } else {\n root.setAttribute(attribute, resolved)\n }\n root.style.colorScheme = resolved\n}\n","/**\n * dev-feedback — zero-dependency `devWarn` / `devError` primitives.\n *\n * Design constraints (epic #247, issue #248):\n * - Guarded by `process.env.NODE_ENV !== 'production'` so production bundlers\n * dead-code-strip every call (the guard is a static string compare that\n * minifiers fold to `false` in prod builds).\n * - Warn-once dedupe per `code` — a footgun is reported once, not on every\n * render.\n * - NO import of `@refraction-ui/logger` (no hard dependency on the telemetry\n * lib). Forwarding to a telemetry sink happens ONLY if the consumer\n * explicitly injects one (dependency inversion, never an import).\n */\n\n/**\n * Minimal structural shape of the record a telemetry sink consumes. This is a\n * deliberate structural mirror of `@refraction-ui/logger`'s `LogRecord` — it is\n * NOT imported, so `@refraction-ui/shared` keeps zero dependency on the\n * telemetry lib. A consumer that wires the real logger sink satisfies this\n * shape structurally.\n */\nexport interface DevFeedbackRecord {\n level: 'warn' | 'error'\n message: string\n timestamp: number\n /** Structured detail — the library-origin envelope lives here. */\n context: Record<string, unknown>\n}\n\n/**\n * The narrow contract a consumer-injected telemetry sink must satisfy. Kept\n * intentionally minimal and structural so the real `TelemetrySink` from\n * `@refraction-ui/logger` is assignable WITHOUT shared importing the logger.\n */\nexport interface DevFeedbackSink {\n /** Receive a single dev-feedback record. Must never throw to the caller. */\n log(record: DevFeedbackRecord): void\n}\n\n/**\n * Minimal ambient view of `process.env` so we can read `NODE_ENV` WITHOUT\n * pulling `@types/node` into this zero-dependency package. Accessed defensively\n * (the `typeof process === 'undefined'` guard) so this is safe in browsers too.\n */\ndeclare const process:\n | { env?: { NODE_ENV?: string } }\n | undefined\n\n/** Per-code dedupe set — module-scoped so it survives across calls. */\nconst seen = new Set<string>()\n\n/**\n * Optional, consumer-injected sink. `null` until a consumer explicitly wires\n * one via {@link setDevFeedbackSink}. Nothing phones home implicitly.\n */\nlet injectedSink: DevFeedbackSink | null = null\n\n/**\n * Wire an optional telemetry sink that {@link devWarn} / {@link devError}\n * forward to (in addition to the console). Inversion of control: the consumer\n * owns the sink; this package never imports it. Pass `null` to unwire.\n *\n * Forwarding still only happens in non-production (the calls themselves are\n * stripped in prod), and still respects warn-once dedupe.\n */\nexport function setDevFeedbackSink(sink: DevFeedbackSink | null): void {\n injectedSink = sink\n}\n\n/** Test-only / consumer-only escape hatch to reset warn-once dedupe state. */\nexport function resetDevFeedback(): void {\n seen.clear()\n}\n\nfunction isDev(): boolean {\n // String compare (not a negated truthiness) so bundlers can statically fold\n // `process.env.NODE_ENV` and strip the whole branch in production builds.\n return (\n typeof process === 'undefined' ||\n process.env?.NODE_ENV !== 'production'\n )\n}\n\nfunction emit(\n level: 'warn' | 'error',\n code: string,\n message: string,\n detail?: Record<string, unknown>,\n): void {\n if (!isDev()) return\n\n // Warn-once dedupe, keyed by (level, code) so an error and a warning sharing\n // a code are not collapsed into one.\n const key = `${level}:${code}`\n if (seen.has(key)) return\n seen.add(key)\n\n const text = `[refraction-ui] ${code}: ${message}`\n\n if (level === 'error') {\n console.error(text, detail ?? '')\n } else {\n console.warn(text, detail ?? '')\n }\n\n // Forward to the consumer-injected sink ONLY if one was explicitly wired.\n if (injectedSink) {\n const record: DevFeedbackRecord = {\n level,\n message: `${code}: ${message}`,\n timestamp: Date.now(),\n context: { code, ...(detail ?? {}) },\n }\n try {\n injectedSink.log(record)\n } catch {\n // A broken sink must never break the consumer app.\n }\n }\n}\n\n/**\n * Emit a development-only warning for a refraction-ui footgun.\n *\n * @param code Stable, greppable identifier (e.g. `'react/no-controlled-prop'`).\n * @param message Human-readable explanation.\n * @param detail Optional structured detail (forwarded to an injected sink).\n *\n * Stripped entirely in production. Warned at most once per `code`.\n */\nexport function devWarn(\n code: string,\n message: string,\n detail?: Record<string, unknown>,\n): void {\n emit('warn', code, message, detail)\n}\n\n/**\n * Emit a development-only error for a refraction-ui misuse / invariant break.\n *\n * @param code Stable, greppable identifier.\n * @param message Human-readable explanation.\n * @param detail Optional structured detail (forwarded to an injected sink).\n *\n * Stripped entirely in production. Reported at most once per `code`.\n */\nexport function devError(\n code: string,\n message: string,\n detail?: Record<string, unknown>,\n): void {\n emit('error', code, message, detail)\n}\n","import * as React from 'react'\nimport {\n createTheme,\n createLocalStorageAdapter,\n createMediaQueryAdapter,\n applyThemeToDOM,\n type ThemeMode,\n type ResolvedTheme,\n type ThemeConfig,\n type ThemeAPI,\n} from '@refraction-ui/theme'\nimport { devWarn } from '@refraction-ui/shared'\n\nexport interface ThemeContextValue {\n mode: ThemeMode\n resolved: ResolvedTheme\n setMode: (mode: ThemeMode) => void\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null)\n\nexport interface ThemeProviderProps extends ThemeConfig {\n children: React.ReactNode\n}\n\nexport function ThemeProvider({\n children,\n defaultMode = 'system',\n storageKey = 'rfr-theme',\n attribute = 'class',\n}: ThemeProviderProps) {\n const themeRef = React.useRef<ThemeAPI | null>(null)\n\n // Initialize theme machine once (client-side only for adapters)\n if (!themeRef.current) {\n const isBrowser = typeof window !== 'undefined'\n themeRef.current = createTheme(\n { defaultMode, storageKey, attribute },\n isBrowser ? createLocalStorageAdapter() : undefined,\n isBrowser ? createMediaQueryAdapter() : undefined,\n )\n }\n\n const [state, setState] = React.useState(() => themeRef.current!.getState())\n\n React.useEffect(() => {\n const theme = themeRef.current!\n // Apply initial theme to DOM\n applyThemeToDOM(theme.getState().resolved, attribute)\n\n // Subscribe to changes\n const unsub = theme.subscribe((newState) => {\n setState(newState)\n applyThemeToDOM(newState.resolved, attribute)\n })\n\n return () => {\n unsub()\n theme.destroy()\n }\n }, [attribute])\n\n const contextValue = React.useMemo<ThemeContextValue>(\n () => ({\n mode: state.mode,\n resolved: state.resolved,\n setMode: (mode: ThemeMode) => themeRef.current?.setMode(mode),\n }),\n [state.mode, state.resolved],\n )\n\n return React.createElement(ThemeContext.Provider, { value: contextValue }, children)\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = React.useContext(ThemeContext)\n if (!context) {\n devWarn(\n 'react-theme/use-theme-outside-provider',\n 'useTheme() was called outside a <ThemeProvider>. Wrap your app (or the consuming subtree) in <ThemeProvider> so the theme context is available.',\n )\n throw new Error('useTheme must be used within a <ThemeProvider>')\n }\n return context\n}\n","import * as React from 'react'\nimport { useTheme } from './theme-provider.js'\nimport type { ThemeMode } from '@refraction-ui/theme'\n\nconst modes: { value: ThemeMode; label: string; icon: string }[] = [\n { value: 'light', label: 'Light', icon: 'sun' },\n { value: 'dark', label: 'Dark', icon: 'moon' },\n { value: 'system', label: 'System', icon: 'monitor' },\n]\n\n// Inline SVG icons — no external icon library dependency\nconst icons: Record<string, React.ReactNode> = {\n sun: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('circle', { cx: 12, cy: 12, r: 5 }),\n React.createElement('path', { d: 'M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42' }),\n ),\n moon: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('path', { d: 'M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z' }),\n ),\n monitor: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('rect', { x: 2, y: 3, width: 20, height: 14, rx: 2, ry: 2 }),\n React.createElement('line', { x1: 8, y1: 21, x2: 16, y2: 21 }),\n React.createElement('line', { x1: 12, y1: 17, x2: 12, y2: 21 }),\n ),\n}\n\nexport interface ThemeToggleProps {\n className?: string\n /** 'dropdown' shows a menu, 'segmented' shows inline buttons */\n variant?: 'dropdown' | 'segmented'\n}\n\nexport function ThemeToggle({ className, variant = 'segmented' }: ThemeToggleProps) {\n const { mode, setMode } = useTheme()\n\n if (variant === 'segmented') {\n return React.createElement('div', {\n className: `inline-flex items-center gap-1 rounded-lg border p-1 ${className ?? ''}`,\n role: 'radiogroup',\n 'aria-label': 'Theme',\n },\n modes.map(({ value, label, icon }) =>\n React.createElement('button', {\n key: value,\n type: 'button',\n role: 'radio',\n 'aria-checked': mode === value,\n 'aria-label': label,\n className: `inline-flex items-center justify-center rounded-md p-1.5 text-sm transition-colors ${\n mode === value\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted'\n }`,\n onClick: () => setMode(value),\n }, icons[icon]),\n ),\n )\n }\n\n // Dropdown variant — simplified, no external dropdown dependency\n const [open, setOpen] = React.useState(false)\n const ref = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n if (!open) return\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)\n }\n document.addEventListener('mousedown', handler)\n return () => document.removeEventListener('mousedown', handler)\n }, [open])\n\n const currentIcon = modes.find((m) => m.value === mode)?.icon ?? 'monitor'\n\n return React.createElement('div', { ref, className: `relative ${className ?? ''}` },\n React.createElement('button', {\n type: 'button',\n 'aria-label': 'Toggle theme',\n 'aria-expanded': open,\n className: 'inline-flex items-center justify-center rounded-md p-2 text-sm transition-colors hover:bg-muted',\n onClick: () => setOpen(!open),\n }, icons[currentIcon]),\n open && React.createElement('div', {\n className: 'absolute right-0 top-full mt-1 z-50 min-w-[8rem] rounded-md border bg-popover p-1 shadow-md',\n role: 'menu',\n },\n modes.map(({ value, label, icon }) =>\n React.createElement('button', {\n key: value,\n type: 'button',\n role: 'menuitem',\n className: `flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors hover:bg-accent ${\n mode === value ? 'bg-accent' : ''\n }`,\n onClick: () => { setMode(value); setOpen(false) },\n }, icons[icon], label),\n ),\n ),\n )\n}\n","import * as React from 'react'\nimport { getThemeScript } from '@refraction-ui/theme'\n\nexport interface ThemeScriptProps {\n storageKey?: string\n attribute?: 'class' | 'data-theme'\n}\n\n/**\n * Renders an inline <script> that prevents theme flash on SSR pages.\n * Place this in the <head> of your document (in Next.js layout.tsx, Remix root, etc.)\n */\nexport function ThemeScript({\n storageKey = 'rfr-theme',\n attribute = 'class',\n}: ThemeScriptProps) {\n return React.createElement('script', {\n dangerouslySetInnerHTML: {\n __html: getThemeScript(storageKey, attribute),\n },\n })\n}\n"]}
|
package/dist/theme.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from '
|
|
1
|
+
export * from './internal/react-theme/index.cjs';
|
package/dist/theme.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from '
|
|
1
|
+
export * from './internal/react-theme/index.js';
|
package/dist/theme.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { devWarn } from './chunk-O4453CBF.js';
|
|
1
2
|
import * as React2 from 'react';
|
|
2
3
|
|
|
3
|
-
// ../react-theme/dist/index.js
|
|
4
|
-
|
|
5
4
|
// ../theme/dist/index.js
|
|
6
5
|
function resolveTheme(mode, systemPrefersDark) {
|
|
7
6
|
if (mode === "system") {
|
|
@@ -158,6 +157,10 @@ function ThemeProvider({
|
|
|
158
157
|
function useTheme() {
|
|
159
158
|
const context = React2.useContext(ThemeContext);
|
|
160
159
|
if (!context) {
|
|
160
|
+
devWarn(
|
|
161
|
+
"react-theme/use-theme-outside-provider",
|
|
162
|
+
"useTheme() was called outside a <ThemeProvider>. Wrap your app (or the consuming subtree) in <ThemeProvider> so the theme context is available."
|
|
163
|
+
);
|
|
161
164
|
throw new Error("useTheme must be used within a <ThemeProvider>");
|
|
162
165
|
}
|
|
163
166
|
return context;
|
package/dist/theme.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../theme/src/theme-machine.ts","../../theme/src/theme-script.ts","../../theme/src/dom-adapters.ts","../../react-theme/src/theme-provider.tsx","../../react-theme/src/theme-toggle.tsx","../../react-theme/src/theme-script-component.tsx"],"names":["React","React3"],"mappings":"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","file":"theme.js","sourcesContent":["/**\n * Headless theme state machine — pure TypeScript, zero DOM dependencies.\n * Manages light/dark/system mode with system preference tracking.\n */\n\nexport type ThemeMode = 'light' | 'dark' | 'system'\nexport type ResolvedTheme = 'light' | 'dark'\n\nexport interface ThemeState {\n /** User's chosen mode */\n mode: ThemeMode\n /** Resolved theme after system preference detection */\n resolved: ResolvedTheme\n}\n\nexport interface ThemeConfig {\n /** Initial mode. Default: 'system' */\n defaultMode?: ThemeMode\n /** localStorage key. Default: 'rfr-theme' */\n storageKey?: string\n /** HTML attribute to set. Default: 'class' */\n attribute?: 'class' | 'data-theme'\n}\n\nexport interface StorageAdapter {\n get(key: string): string | null\n set(key: string, value: string): void\n}\n\nexport interface MediaQueryAdapter {\n matches(query: string): boolean\n subscribe(query: string, callback: (matches: boolean) => void): () => void\n}\n\nexport interface ThemeAPI {\n /** Get current state */\n getState(): ThemeState\n /** Set mode (light/dark/system) */\n setMode(mode: ThemeMode): void\n /** Subscribe to state changes */\n subscribe(fn: (state: ThemeState) => void): () => void\n /** Clean up subscriptions */\n destroy(): void\n}\n\nfunction resolveTheme(mode: ThemeMode, systemPrefersDark: boolean): ResolvedTheme {\n if (mode === 'system') {\n return systemPrefersDark ? 'dark' : 'light'\n }\n return mode\n}\n\nexport function createTheme(\n config: ThemeConfig = {},\n storage?: StorageAdapter,\n mediaQuery?: MediaQueryAdapter,\n): ThemeAPI {\n const {\n defaultMode = 'system',\n storageKey = 'rfr-theme',\n } = config\n\n const listeners = new Set<(state: ThemeState) => void>()\n let cleanupMediaQuery: (() => void) | null = null\n\n // Read persisted mode or use default\n const persisted = storage?.get(storageKey) as ThemeMode | null\n let mode: ThemeMode = persisted && ['light', 'dark', 'system'].includes(persisted)\n ? persisted\n : defaultMode\n\n // Detect system preference\n let systemPrefersDark = mediaQuery?.matches('(prefers-color-scheme: dark)') ?? false\n\n let state: ThemeState = {\n mode,\n resolved: resolveTheme(mode, systemPrefersDark),\n }\n\n function notify() {\n for (const fn of listeners) {\n fn(state)\n }\n }\n\n function updateState(newMode: ThemeMode) {\n mode = newMode\n state = { mode, resolved: resolveTheme(mode, systemPrefersDark) }\n storage?.set(storageKey, mode)\n notify()\n }\n\n // Listen for system preference changes\n if (mediaQuery) {\n cleanupMediaQuery = mediaQuery.subscribe(\n '(prefers-color-scheme: dark)',\n (matches) => {\n systemPrefersDark = matches\n if (mode === 'system') {\n state = { mode, resolved: resolveTheme(mode, systemPrefersDark) }\n notify()\n }\n },\n )\n }\n\n return {\n getState() {\n return state\n },\n\n setMode(newMode: ThemeMode) {\n if (newMode !== mode) {\n updateState(newMode)\n }\n },\n\n subscribe(fn: (state: ThemeState) => void) {\n listeners.add(fn)\n return () => {\n listeners.delete(fn)\n }\n },\n\n destroy() {\n listeners.clear()\n cleanupMediaQuery?.()\n },\n }\n}\n","/**\n * Inline script for preventing theme flash on page load.\n * Inject this as a <script> tag in the <head> before any CSS.\n * Works with any framework (React, Angular, Astro, plain HTML).\n */\n\nexport function getThemeScript(\n storageKey = 'rfr-theme',\n attribute: 'class' | 'data-theme' = 'class',\n): string {\n // This string is injected as innerHTML of a <script> tag.\n // It runs before any CSS/JS loads, preventing flash of wrong theme.\n return `(function(){try{var m=localStorage.getItem('${storageKey}');var s=window.matchMedia('(prefers-color-scheme:dark)').matches;var t=m==='dark'||(m!=='light'&&s)?'dark':'light';var d=document.documentElement;${\n attribute === 'class'\n ? \"d.classList.remove('light','dark');d.classList.add(t);\"\n : `d.setAttribute('${attribute}',t);`\n }d.style.colorScheme=t;}catch(e){}})()`\n}\n","/**\n * Browser DOM adapters for the theme machine.\n * These bridge the headless core to browser APIs.\n */\n\nimport type { StorageAdapter, MediaQueryAdapter, ResolvedTheme } from './theme-machine.js'\n\n/** localStorage adapter */\nexport function createLocalStorageAdapter(): StorageAdapter {\n return {\n get(key) {\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n },\n set(key, value) {\n try {\n localStorage.setItem(key, value)\n } catch {\n // localStorage unavailable (SSR, incognito quota exceeded, etc.)\n }\n },\n }\n}\n\n/** matchMedia adapter */\nexport function createMediaQueryAdapter(): MediaQueryAdapter {\n return {\n matches(query) {\n if (typeof window === 'undefined') return false\n return window.matchMedia(query).matches\n },\n subscribe(query, callback) {\n if (typeof window === 'undefined') return () => {}\n const mql = window.matchMedia(query)\n const handler = (e: MediaQueryListEvent) => callback(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n },\n }\n}\n\n/** Apply resolved theme to the document */\nexport function applyThemeToDOM(\n resolved: ResolvedTheme,\n attribute: 'class' | 'data-theme' = 'class',\n): void {\n if (typeof document === 'undefined') return\n\n const root = document.documentElement\n if (attribute === 'class') {\n root.classList.remove('light', 'dark')\n root.classList.add(resolved)\n } else {\n root.setAttribute(attribute, resolved)\n }\n root.style.colorScheme = resolved\n}\n","import * as React from 'react'\nimport {\n createTheme,\n createLocalStorageAdapter,\n createMediaQueryAdapter,\n applyThemeToDOM,\n type ThemeMode,\n type ResolvedTheme,\n type ThemeConfig,\n type ThemeAPI,\n} from '@refraction-ui/theme'\n\nexport interface ThemeContextValue {\n mode: ThemeMode\n resolved: ResolvedTheme\n setMode: (mode: ThemeMode) => void\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null)\n\nexport interface ThemeProviderProps extends ThemeConfig {\n children: React.ReactNode\n}\n\nexport function ThemeProvider({\n children,\n defaultMode = 'system',\n storageKey = 'rfr-theme',\n attribute = 'class',\n}: ThemeProviderProps) {\n const themeRef = React.useRef<ThemeAPI | null>(null)\n\n // Initialize theme machine once (client-side only for adapters)\n if (!themeRef.current) {\n const isBrowser = typeof window !== 'undefined'\n themeRef.current = createTheme(\n { defaultMode, storageKey, attribute },\n isBrowser ? createLocalStorageAdapter() : undefined,\n isBrowser ? createMediaQueryAdapter() : undefined,\n )\n }\n\n const [state, setState] = React.useState(() => themeRef.current!.getState())\n\n React.useEffect(() => {\n const theme = themeRef.current!\n // Apply initial theme to DOM\n applyThemeToDOM(theme.getState().resolved, attribute)\n\n // Subscribe to changes\n const unsub = theme.subscribe((newState) => {\n setState(newState)\n applyThemeToDOM(newState.resolved, attribute)\n })\n\n return () => {\n unsub()\n theme.destroy()\n }\n }, [attribute])\n\n const contextValue = React.useMemo<ThemeContextValue>(\n () => ({\n mode: state.mode,\n resolved: state.resolved,\n setMode: (mode: ThemeMode) => themeRef.current?.setMode(mode),\n }),\n [state.mode, state.resolved],\n )\n\n return React.createElement(ThemeContext.Provider, { value: contextValue }, children)\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = React.useContext(ThemeContext)\n if (!context) {\n throw new Error('useTheme must be used within a <ThemeProvider>')\n }\n return context\n}\n","import * as React from 'react'\nimport { useTheme } from './theme-provider.js'\nimport type { ThemeMode } from '@refraction-ui/theme'\n\nconst modes: { value: ThemeMode; label: string; icon: string }[] = [\n { value: 'light', label: 'Light', icon: 'sun' },\n { value: 'dark', label: 'Dark', icon: 'moon' },\n { value: 'system', label: 'System', icon: 'monitor' },\n]\n\n// Inline SVG icons — no external icon library dependency\nconst icons: Record<string, React.ReactNode> = {\n sun: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('circle', { cx: 12, cy: 12, r: 5 }),\n React.createElement('path', { d: 'M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42' }),\n ),\n moon: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('path', { d: 'M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z' }),\n ),\n monitor: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('rect', { x: 2, y: 3, width: 20, height: 14, rx: 2, ry: 2 }),\n React.createElement('line', { x1: 8, y1: 21, x2: 16, y2: 21 }),\n React.createElement('line', { x1: 12, y1: 17, x2: 12, y2: 21 }),\n ),\n}\n\nexport interface ThemeToggleProps {\n className?: string\n /** 'dropdown' shows a menu, 'segmented' shows inline buttons */\n variant?: 'dropdown' | 'segmented'\n}\n\nexport function ThemeToggle({ className, variant = 'segmented' }: ThemeToggleProps) {\n const { mode, setMode } = useTheme()\n\n if (variant === 'segmented') {\n return React.createElement('div', {\n className: `inline-flex items-center gap-1 rounded-lg border p-1 ${className ?? ''}`,\n role: 'radiogroup',\n 'aria-label': 'Theme',\n },\n modes.map(({ value, label, icon }) =>\n React.createElement('button', {\n key: value,\n type: 'button',\n role: 'radio',\n 'aria-checked': mode === value,\n 'aria-label': label,\n className: `inline-flex items-center justify-center rounded-md p-1.5 text-sm transition-colors ${\n mode === value\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted'\n }`,\n onClick: () => setMode(value),\n }, icons[icon]),\n ),\n )\n }\n\n // Dropdown variant — simplified, no external dropdown dependency\n const [open, setOpen] = React.useState(false)\n const ref = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n if (!open) return\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)\n }\n document.addEventListener('mousedown', handler)\n return () => document.removeEventListener('mousedown', handler)\n }, [open])\n\n const currentIcon = modes.find((m) => m.value === mode)?.icon ?? 'monitor'\n\n return React.createElement('div', { ref, className: `relative ${className ?? ''}` },\n React.createElement('button', {\n type: 'button',\n 'aria-label': 'Toggle theme',\n 'aria-expanded': open,\n className: 'inline-flex items-center justify-center rounded-md p-2 text-sm transition-colors hover:bg-muted',\n onClick: () => setOpen(!open),\n }, icons[currentIcon]),\n open && React.createElement('div', {\n className: 'absolute right-0 top-full mt-1 z-50 min-w-[8rem] rounded-md border bg-popover p-1 shadow-md',\n role: 'menu',\n },\n modes.map(({ value, label, icon }) =>\n React.createElement('button', {\n key: value,\n type: 'button',\n role: 'menuitem',\n className: `flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors hover:bg-accent ${\n mode === value ? 'bg-accent' : ''\n }`,\n onClick: () => { setMode(value); setOpen(false) },\n }, icons[icon], label),\n ),\n ),\n )\n}\n","import * as React from 'react'\nimport { getThemeScript } from '@refraction-ui/theme'\n\nexport interface ThemeScriptProps {\n storageKey?: string\n attribute?: 'class' | 'data-theme'\n}\n\n/**\n * Renders an inline <script> that prevents theme flash on SSR pages.\n * Place this in the <head> of your document (in Next.js layout.tsx, Remix root, etc.)\n */\nexport function ThemeScript({\n storageKey = 'rfr-theme',\n attribute = 'class',\n}: ThemeScriptProps) {\n return React.createElement('script', {\n dangerouslySetInnerHTML: {\n __html: getThemeScript(storageKey, attribute),\n },\n })\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../theme/src/theme-machine.ts","../../theme/src/theme-script.ts","../../theme/src/dom-adapters.ts","../../react-theme/src/theme-provider.tsx","../../react-theme/src/theme-toggle.tsx","../../react-theme/src/theme-script-component.tsx"],"names":["React","React3"],"mappings":"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","file":"theme.js","sourcesContent":["/**\n * Headless theme state machine — pure TypeScript, zero DOM dependencies.\n * Manages light/dark/system mode with system preference tracking.\n */\n\nexport type ThemeMode = 'light' | 'dark' | 'system'\nexport type ResolvedTheme = 'light' | 'dark'\n\nexport interface ThemeState {\n /** User's chosen mode */\n mode: ThemeMode\n /** Resolved theme after system preference detection */\n resolved: ResolvedTheme\n}\n\nexport interface ThemeConfig {\n /** Initial mode. Default: 'system' */\n defaultMode?: ThemeMode\n /** localStorage key. Default: 'rfr-theme' */\n storageKey?: string\n /** HTML attribute to set. Default: 'class' */\n attribute?: 'class' | 'data-theme'\n}\n\nexport interface StorageAdapter {\n get(key: string): string | null\n set(key: string, value: string): void\n}\n\nexport interface MediaQueryAdapter {\n matches(query: string): boolean\n subscribe(query: string, callback: (matches: boolean) => void): () => void\n}\n\nexport interface ThemeAPI {\n /** Get current state */\n getState(): ThemeState\n /** Set mode (light/dark/system) */\n setMode(mode: ThemeMode): void\n /** Subscribe to state changes */\n subscribe(fn: (state: ThemeState) => void): () => void\n /** Clean up subscriptions */\n destroy(): void\n}\n\nfunction resolveTheme(mode: ThemeMode, systemPrefersDark: boolean): ResolvedTheme {\n if (mode === 'system') {\n return systemPrefersDark ? 'dark' : 'light'\n }\n return mode\n}\n\nexport function createTheme(\n config: ThemeConfig = {},\n storage?: StorageAdapter,\n mediaQuery?: MediaQueryAdapter,\n): ThemeAPI {\n const {\n defaultMode = 'system',\n storageKey = 'rfr-theme',\n } = config\n\n const listeners = new Set<(state: ThemeState) => void>()\n let cleanupMediaQuery: (() => void) | null = null\n\n // Read persisted mode or use default\n const persisted = storage?.get(storageKey) as ThemeMode | null\n let mode: ThemeMode = persisted && ['light', 'dark', 'system'].includes(persisted)\n ? persisted\n : defaultMode\n\n // Detect system preference\n let systemPrefersDark = mediaQuery?.matches('(prefers-color-scheme: dark)') ?? false\n\n let state: ThemeState = {\n mode,\n resolved: resolveTheme(mode, systemPrefersDark),\n }\n\n function notify() {\n for (const fn of listeners) {\n fn(state)\n }\n }\n\n function updateState(newMode: ThemeMode) {\n mode = newMode\n state = { mode, resolved: resolveTheme(mode, systemPrefersDark) }\n storage?.set(storageKey, mode)\n notify()\n }\n\n // Listen for system preference changes\n if (mediaQuery) {\n cleanupMediaQuery = mediaQuery.subscribe(\n '(prefers-color-scheme: dark)',\n (matches) => {\n systemPrefersDark = matches\n if (mode === 'system') {\n state = { mode, resolved: resolveTheme(mode, systemPrefersDark) }\n notify()\n }\n },\n )\n }\n\n return {\n getState() {\n return state\n },\n\n setMode(newMode: ThemeMode) {\n if (newMode !== mode) {\n updateState(newMode)\n }\n },\n\n subscribe(fn: (state: ThemeState) => void) {\n listeners.add(fn)\n return () => {\n listeners.delete(fn)\n }\n },\n\n destroy() {\n listeners.clear()\n cleanupMediaQuery?.()\n },\n }\n}\n","/**\n * Inline script for preventing theme flash on page load.\n * Inject this as a <script> tag in the <head> before any CSS.\n * Works with any framework (React, Angular, Astro, plain HTML).\n */\n\nexport function getThemeScript(\n storageKey = 'rfr-theme',\n attribute: 'class' | 'data-theme' = 'class',\n): string {\n // This string is injected as innerHTML of a <script> tag.\n // It runs before any CSS/JS loads, preventing flash of wrong theme.\n return `(function(){try{var m=localStorage.getItem('${storageKey}');var s=window.matchMedia('(prefers-color-scheme:dark)').matches;var t=m==='dark'||(m!=='light'&&s)?'dark':'light';var d=document.documentElement;${\n attribute === 'class'\n ? \"d.classList.remove('light','dark');d.classList.add(t);\"\n : `d.setAttribute('${attribute}',t);`\n }d.style.colorScheme=t;}catch(e){}})()`\n}\n","/**\n * Browser DOM adapters for the theme machine.\n * These bridge the headless core to browser APIs.\n */\n\nimport type { StorageAdapter, MediaQueryAdapter, ResolvedTheme } from './theme-machine.js'\n\n/** localStorage adapter */\nexport function createLocalStorageAdapter(): StorageAdapter {\n return {\n get(key) {\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n },\n set(key, value) {\n try {\n localStorage.setItem(key, value)\n } catch {\n // localStorage unavailable (SSR, incognito quota exceeded, etc.)\n }\n },\n }\n}\n\n/** matchMedia adapter */\nexport function createMediaQueryAdapter(): MediaQueryAdapter {\n return {\n matches(query) {\n if (typeof window === 'undefined') return false\n return window.matchMedia(query).matches\n },\n subscribe(query, callback) {\n if (typeof window === 'undefined') return () => {}\n const mql = window.matchMedia(query)\n const handler = (e: MediaQueryListEvent) => callback(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n },\n }\n}\n\n/** Apply resolved theme to the document */\nexport function applyThemeToDOM(\n resolved: ResolvedTheme,\n attribute: 'class' | 'data-theme' = 'class',\n): void {\n if (typeof document === 'undefined') return\n\n const root = document.documentElement\n if (attribute === 'class') {\n root.classList.remove('light', 'dark')\n root.classList.add(resolved)\n } else {\n root.setAttribute(attribute, resolved)\n }\n root.style.colorScheme = resolved\n}\n","import * as React from 'react'\nimport {\n createTheme,\n createLocalStorageAdapter,\n createMediaQueryAdapter,\n applyThemeToDOM,\n type ThemeMode,\n type ResolvedTheme,\n type ThemeConfig,\n type ThemeAPI,\n} from '@refraction-ui/theme'\nimport { devWarn } from '@refraction-ui/shared'\n\nexport interface ThemeContextValue {\n mode: ThemeMode\n resolved: ResolvedTheme\n setMode: (mode: ThemeMode) => void\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null)\n\nexport interface ThemeProviderProps extends ThemeConfig {\n children: React.ReactNode\n}\n\nexport function ThemeProvider({\n children,\n defaultMode = 'system',\n storageKey = 'rfr-theme',\n attribute = 'class',\n}: ThemeProviderProps) {\n const themeRef = React.useRef<ThemeAPI | null>(null)\n\n // Initialize theme machine once (client-side only for adapters)\n if (!themeRef.current) {\n const isBrowser = typeof window !== 'undefined'\n themeRef.current = createTheme(\n { defaultMode, storageKey, attribute },\n isBrowser ? createLocalStorageAdapter() : undefined,\n isBrowser ? createMediaQueryAdapter() : undefined,\n )\n }\n\n const [state, setState] = React.useState(() => themeRef.current!.getState())\n\n React.useEffect(() => {\n const theme = themeRef.current!\n // Apply initial theme to DOM\n applyThemeToDOM(theme.getState().resolved, attribute)\n\n // Subscribe to changes\n const unsub = theme.subscribe((newState) => {\n setState(newState)\n applyThemeToDOM(newState.resolved, attribute)\n })\n\n return () => {\n unsub()\n theme.destroy()\n }\n }, [attribute])\n\n const contextValue = React.useMemo<ThemeContextValue>(\n () => ({\n mode: state.mode,\n resolved: state.resolved,\n setMode: (mode: ThemeMode) => themeRef.current?.setMode(mode),\n }),\n [state.mode, state.resolved],\n )\n\n return React.createElement(ThemeContext.Provider, { value: contextValue }, children)\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = React.useContext(ThemeContext)\n if (!context) {\n devWarn(\n 'react-theme/use-theme-outside-provider',\n 'useTheme() was called outside a <ThemeProvider>. Wrap your app (or the consuming subtree) in <ThemeProvider> so the theme context is available.',\n )\n throw new Error('useTheme must be used within a <ThemeProvider>')\n }\n return context\n}\n","import * as React from 'react'\nimport { useTheme } from './theme-provider.js'\nimport type { ThemeMode } from '@refraction-ui/theme'\n\nconst modes: { value: ThemeMode; label: string; icon: string }[] = [\n { value: 'light', label: 'Light', icon: 'sun' },\n { value: 'dark', label: 'Dark', icon: 'moon' },\n { value: 'system', label: 'System', icon: 'monitor' },\n]\n\n// Inline SVG icons — no external icon library dependency\nconst icons: Record<string, React.ReactNode> = {\n sun: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('circle', { cx: 12, cy: 12, r: 5 }),\n React.createElement('path', { d: 'M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42' }),\n ),\n moon: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('path', { d: 'M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z' }),\n ),\n monitor: React.createElement('svg', {\n xmlns: 'http://www.w3.org/2000/svg', width: 16, height: 16, viewBox: '0 0 24 24',\n fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',\n },\n React.createElement('rect', { x: 2, y: 3, width: 20, height: 14, rx: 2, ry: 2 }),\n React.createElement('line', { x1: 8, y1: 21, x2: 16, y2: 21 }),\n React.createElement('line', { x1: 12, y1: 17, x2: 12, y2: 21 }),\n ),\n}\n\nexport interface ThemeToggleProps {\n className?: string\n /** 'dropdown' shows a menu, 'segmented' shows inline buttons */\n variant?: 'dropdown' | 'segmented'\n}\n\nexport function ThemeToggle({ className, variant = 'segmented' }: ThemeToggleProps) {\n const { mode, setMode } = useTheme()\n\n if (variant === 'segmented') {\n return React.createElement('div', {\n className: `inline-flex items-center gap-1 rounded-lg border p-1 ${className ?? ''}`,\n role: 'radiogroup',\n 'aria-label': 'Theme',\n },\n modes.map(({ value, label, icon }) =>\n React.createElement('button', {\n key: value,\n type: 'button',\n role: 'radio',\n 'aria-checked': mode === value,\n 'aria-label': label,\n className: `inline-flex items-center justify-center rounded-md p-1.5 text-sm transition-colors ${\n mode === value\n ? 'bg-accent text-accent-foreground'\n : 'text-muted-foreground hover:bg-muted'\n }`,\n onClick: () => setMode(value),\n }, icons[icon]),\n ),\n )\n }\n\n // Dropdown variant — simplified, no external dropdown dependency\n const [open, setOpen] = React.useState(false)\n const ref = React.useRef<HTMLDivElement>(null)\n\n React.useEffect(() => {\n if (!open) return\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)\n }\n document.addEventListener('mousedown', handler)\n return () => document.removeEventListener('mousedown', handler)\n }, [open])\n\n const currentIcon = modes.find((m) => m.value === mode)?.icon ?? 'monitor'\n\n return React.createElement('div', { ref, className: `relative ${className ?? ''}` },\n React.createElement('button', {\n type: 'button',\n 'aria-label': 'Toggle theme',\n 'aria-expanded': open,\n className: 'inline-flex items-center justify-center rounded-md p-2 text-sm transition-colors hover:bg-muted',\n onClick: () => setOpen(!open),\n }, icons[currentIcon]),\n open && React.createElement('div', {\n className: 'absolute right-0 top-full mt-1 z-50 min-w-[8rem] rounded-md border bg-popover p-1 shadow-md',\n role: 'menu',\n },\n modes.map(({ value, label, icon }) =>\n React.createElement('button', {\n key: value,\n type: 'button',\n role: 'menuitem',\n className: `flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors hover:bg-accent ${\n mode === value ? 'bg-accent' : ''\n }`,\n onClick: () => { setMode(value); setOpen(false) },\n }, icons[icon], label),\n ),\n ),\n )\n}\n","import * as React from 'react'\nimport { getThemeScript } from '@refraction-ui/theme'\n\nexport interface ThemeScriptProps {\n storageKey?: string\n attribute?: 'class' | 'data-theme'\n}\n\n/**\n * Renders an inline <script> that prevents theme flash on SSR pages.\n * Place this in the <head> of your document (in Next.js layout.tsx, Remix root, etc.)\n */\nexport function ThemeScript({\n storageKey = 'rfr-theme',\n attribute = 'class',\n}: ThemeScriptProps) {\n return React.createElement('script', {\n dangerouslySetInnerHTML: {\n __html: getThemeScript(storageKey, attribute),\n },\n })\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@refraction-ui/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "All Refraction UI React components in one package — headless, accessible, zero-dependency",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,13 +17,18 @@
|
|
|
17
17
|
"import": "./dist/theme.js",
|
|
18
18
|
"require": "./dist/theme.cjs"
|
|
19
19
|
},
|
|
20
|
+
"./form": {
|
|
21
|
+
"types": "./dist/form.d.ts",
|
|
22
|
+
"import": "./dist/form.js",
|
|
23
|
+
"require": "./dist/form.cjs"
|
|
24
|
+
},
|
|
20
25
|
"./styles.css": "./dist/styles.css"
|
|
21
26
|
},
|
|
22
27
|
"files": [
|
|
23
28
|
"dist"
|
|
24
29
|
],
|
|
25
30
|
"scripts": {
|
|
26
|
-
"build": "tsup",
|
|
31
|
+
"build": "tsup && cp src/styles.css dist/styles.css && node ./scripts/embed-internal-types.mjs",
|
|
27
32
|
"dev": "tsup --watch",
|
|
28
33
|
"test": "vitest run --passWithNoTests",
|
|
29
34
|
"test:watch": "vitest",
|
|
@@ -33,15 +38,11 @@
|
|
|
33
38
|
"clean": "rm -rf dist"
|
|
34
39
|
},
|
|
35
40
|
"peerDependencies": {
|
|
36
|
-
"@monaco-editor/react": ">=4",
|
|
37
41
|
"react": ">=18",
|
|
38
42
|
"react-dom": ">=18",
|
|
39
43
|
"react-hook-form": ">=7.43.0"
|
|
40
44
|
},
|
|
41
45
|
"peerDependenciesMeta": {
|
|
42
|
-
"@monaco-editor/react": {
|
|
43
|
-
"optional": true
|
|
44
|
-
},
|
|
45
46
|
"react-hook-form": {
|
|
46
47
|
"optional": true
|
|
47
48
|
}
|
|
@@ -132,7 +133,9 @@
|
|
|
132
133
|
"@refraction-ui/react-card-grid": "workspace:*",
|
|
133
134
|
"@refraction-ui/react-payment": "workspace:*",
|
|
134
135
|
"@refraction-ui/react-command-input": "workspace:*",
|
|
135
|
-
"@refraction-ui/react-sheet": "workspace:*"
|
|
136
|
+
"@refraction-ui/react-sheet": "workspace:*",
|
|
137
|
+
"@refraction-ui/react-logger": "workspace:*",
|
|
138
|
+
"@refraction-ui/react-analytics": "workspace:*"
|
|
136
139
|
},
|
|
137
140
|
"publishConfig": {
|
|
138
141
|
"access": "public"
|