@ngrok/mantle 0.73.5 → 0.75.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/dist/accordion.d.ts +1 -1
- package/dist/agent.json +4 -2
- package/dist/alert-dialog.d.ts +2 -2
- package/dist/alert.d.ts +2 -2
- package/dist/alert.js.map +1 -1
- package/dist/anchor-CcTY5SIz.js.map +1 -1
- package/dist/badge.d.ts +1 -1
- package/dist/button.d.ts +1 -1
- package/dist/calendar.d.ts +1 -1
- package/dist/checkbox.d.ts +1 -1
- package/dist/code-block.d.ts +1 -1
- package/dist/code-block.js.map +1 -1
- package/dist/combobox.d.ts +1 -1
- package/dist/command.d.ts +10 -10
- package/dist/data-table.d.ts +8 -8
- package/dist/data-table.js.map +1 -1
- package/dist/dialog.d.ts +6 -6
- package/dist/{dropdown-menu-BgYk4L8o.d.ts → dropdown-menu-BqdyTFLu.d.ts} +2 -2
- package/dist/dropdown-menu.d.ts +1 -1
- package/dist/empty.d.ts +5 -5
- package/dist/field.d.ts +1 -1
- package/dist/field.js.map +1 -1
- package/dist/flag.d.ts +1 -1
- package/dist/hooks.js.map +1 -1
- package/dist/hover-card.d.ts +1 -1
- package/dist/{icon-button-ntupABbM.d.ts → icon-button-Ct3A7aoj.d.ts} +2 -2
- package/dist/icons.d.ts +6 -6
- package/dist/kbd.d.ts +1 -1
- package/dist/llms.txt +3 -1
- package/dist/main.d.ts +1 -1
- package/dist/multi-select.d.ts +2 -2
- package/dist/multi-select.js.map +1 -1
- package/dist/otp-input.js.map +1 -1
- package/dist/pagination.d.ts +1 -1
- package/dist/{primitive-D_-h74Kt.d.ts → primitive-FoWela9a.d.ts} +2 -2
- package/dist/progress.d.ts +4 -4
- package/dist/qr-code.d.ts +159 -0
- package/dist/qr-code.js +2 -0
- package/dist/qr-code.js.map +1 -0
- package/dist/radio-group.d.ts +3 -3
- package/dist/resolve-pre-rendered-props-C-vrNxH1.js.map +1 -1
- package/dist/separator-Bqjy77rG.js.map +1 -1
- package/dist/separator.d.ts +1 -1
- package/dist/sheet.d.ts +6 -6
- package/dist/skip-to-main-link.d.ts +1 -1
- package/dist/skip-to-main-link.js.map +1 -1
- package/dist/slider.d.ts +1 -1
- package/dist/split-button.d.ts +2 -2
- package/dist/tabs.d.ts +1 -1
- package/dist/theme-provider-MMwxHEfw.js.map +1 -1
- package/dist/theme.d.ts +4 -4
- package/dist/toast.d.ts +1 -1
- package/dist/tooltip.d.ts +3 -3
- package/dist/use-matches-media-query-CMSxHR9n.js.map +1 -1
- package/dist/use-prefers-reduced-motion-CWIoFA6W.js.map +1 -1
- package/dist/well.d.ts +34 -0
- package/dist/well.js +2 -0
- package/dist/well.js.map +1 -0
- package/package.json +35 -24
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme-provider-MMwxHEfw.js","names":[],"sources":["../src/components/theme/themes.ts","../src/components/theme/theme-provider.tsx"],"sourcesContent":["/**\n * resolvedThemes is a tuple of valid themes that have been resolved from \"system\" to a specific theme.\n */\nconst resolvedThemes = [\"light\", \"dark\", \"light-high-contrast\", \"dark-high-contrast\"] as const;\n\n/**\n * ResolvedTheme is a type that represents a theme that has been resolved from \"system\" to a specific theme.\n */\ntype ResolvedTheme = (typeof resolvedThemes)[number];\n\n/**\n * themes is a tuple of valid themes.\n */\nconst themes = [\"system\", ...resolvedThemes] as const;\n\n/**\n * Theme is a string literal type that represents a valid theme.\n */\ntype Theme = (typeof themes)[number];\n\n/**\n * $theme is a helper which translates the Theme type into a string literal type.\n */\nconst $theme = <T extends Theme = Theme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid theme.\n */\nfunction isTheme(value: unknown): value is Theme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn themes.includes(value as Theme);\n}\n\n/**\n * $resolvedTheme is a helper which translates the ResolvedTheme type into a string literal type.\n */\nconst $resolvedTheme = <T extends ResolvedTheme = ResolvedTheme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid resolved theme.\n */\nfunction isResolvedTheme(value: unknown): value is ResolvedTheme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn resolvedThemes.includes(value as ResolvedTheme);\n}\n\nexport {\n\t//,\n\tthemes,\n\tresolvedThemes,\n\t$resolvedTheme,\n\t$theme,\n\tisResolvedTheme,\n\tisTheme,\n};\n\nexport type {\n\t//,\n\tTheme,\n\tResolvedTheme,\n};\n","\"use client\";\n\nimport type { PropsWithChildren } from \"react\";\nimport { createContext, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { useMatchesMediaQuery } from \"../../hooks/use-matches-media-query.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { canUseDOM } from \"../browser-only/browser-only.js\";\nimport {\n\ttype ResolvedTheme,\n\ttype Theme,\n\tisResolvedTheme,\n\tisTheme,\n\tresolvedThemes,\n\tthemes,\n} from \"./themes.js\";\n\n/**\n * prefersDarkModeMediaQuery is the media query used to detect if the user prefers dark mode.\n */\nconst prefersDarkModeMediaQuery = \"(prefers-color-scheme: dark)\";\n\n/**\n * prefersHighContrastMediaQuery is the media query used to detect if the user prefers high contrast mode.\n */\nconst prefersHighContrastMediaQuery = \"(prefers-contrast: more)\";\n\n/**\n * THEME_STORAGE_KEY is the key used to store the theme in cookies.\n */\nconst THEME_STORAGE_KEY = \"mantle-ui-theme\";\n\n/**\n * DEFAULT_THEME is the initial theme to apply if no value is found in storage.\n * {@link themes}\n */\nconst DEFAULT_THEME = \"system\" satisfies Theme;\n\n/**\n * ThemeProviderState is the shape of the state returned by the ThemeProviderContext.\n */\ntype ThemeProviderState = [theme: Theme, setTheme: (theme: Theme) => void];\n\n/**\n * Initial state for the ThemeProviderContext.\n */\nconst initialState: ThemeProviderState = [\"system\", () => null];\n\n/**\n * ThemeProviderContext is a React Context that provides the current theme and a function to set the theme.\n */\nconst ThemeProviderContext = createContext<ThemeProviderState | null>(initialState);\n\ntype ThemeProviderProps = PropsWithChildren;\n\n/**\n * ThemeProvider is a React Context Provider that provides the current theme and a function to set the theme.\n *\n * @see https://mantle.ngrok.com/components/theme-provider#themeprovider\n *\n * @example\n * ```tsx\n * <ThemeProvider defaultTheme=\"system\" storageKey=\"app-theme\">\n * <App />\n * </ThemeProvider>\n * ```\n */\nfunction ThemeProvider({ children }: ThemeProviderProps) {\n\t// Init once from cookie and apply immediately to avoid flashes\n\tconst [theme, setTheme] = useState<Theme>(() => {\n\t\tconst storedTheme = getStoredTheme({\n\t\t\tcookie: canUseDOM() ? document.cookie : null,\n\t\t});\n\t\tapplyThemeToHtml(storedTheme);\n\t\treturn storedTheme;\n\t});\n\n\tconst broadcastChannelRef = useRef<BroadcastChannel | null>(null);\n\n\tuseEffect(() => {\n\t\tfunction syncThemeFromCookie(next?: Theme) {\n\t\t\tconst newTheme = next ?? getStoredTheme({ cookie: document.cookie });\n\t\t\tsetTheme(newTheme);\n\t\t\tapplyThemeToHtml(newTheme);\n\t\t}\n\n\t\t// initial sync in case defaultTheme or storageKey changed\n\t\tsyncThemeFromCookie();\n\n\t\t// add cross-tab listeners (prefer broadcast channel, use localStorage as fallback)\n\t\ttry {\n\t\t\tif (\"BroadcastChannel\" in window) {\n\t\t\t\tbroadcastChannelRef.current = new BroadcastChannel(THEME_STORAGE_KEY);\n\t\t\t\tbroadcastChannelRef.current.addEventListener(\"message\", (event) => {\n\t\t\t\t\tconst value: unknown = event?.data?.theme;\n\t\t\t\t\tif (isTheme(value)) {\n\t\t\t\t\t\tsyncThemeFromCookie(value);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\n\t\tfunction onStorage(event: StorageEvent) {\n\t\t\tif (event.key === `${THEME_STORAGE_KEY}__ping`) {\n\t\t\t\tsyncThemeFromCookie();\n\t\t\t}\n\t\t}\n\t\twindow.addEventListener(\"storage\", onStorage);\n\n\t\t// add media query listeners for system theme changes\n\t\tconst prefersDarkMql = window.matchMedia(prefersDarkModeMediaQuery);\n\t\tconst prefersHighContrastMql = window.matchMedia(prefersHighContrastMediaQuery);\n\n\t\tfunction onChange() {\n\t\t\tsyncThemeFromCookie();\n\t\t}\n\n\t\tfunction onVisibilityChange() {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tsyncThemeFromCookie();\n\t\t\t}\n\t\t}\n\n\t\tprefersDarkMql.addEventListener(\"change\", onChange);\n\t\tprefersHighContrastMql.addEventListener(\"change\", onChange);\n\n\t\t// pageshow fires on bfcache restore (event.persisted === true) and some restore-from-freeze cases.\n\t\twindow.addEventListener(\"pageshow\", onChange);\n\n\t\t// visibilitychange to handle coming back to a tab\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\t// don't forget to clean up your slop!\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"storage\", onStorage);\n\t\t\tprefersDarkMql.removeEventListener(\"change\", onChange);\n\t\t\tprefersHighContrastMql.removeEventListener(\"change\", onChange);\n\t\t\twindow.removeEventListener(\"pageshow\", onChange);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\t\ttry {\n\t\t\t\tbroadcastChannelRef.current?.close();\n\t\t\t} catch {\n\t\t\t\t// silently swallow errors\n\t\t\t}\n\t\t\tbroadcastChannelRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst value: ThemeProviderState = useMemo(\n\t\t() => [\n\t\t\ttheme,\n\t\t\t(next: Theme) => {\n\t\t\t\tsetCookie(next);\n\t\t\t\tsetTheme(next);\n\t\t\t\tapplyThemeToHtml(next);\n\t\t\t\tnotifyOtherTabs(next, {\n\t\t\t\t\tbroadcastChannel: broadcastChannelRef.current,\n\t\t\t\t\tpingKey: `${THEME_STORAGE_KEY}__ping`,\n\t\t\t\t});\n\t\t\t},\n\t\t],\n\t\t[theme],\n\t);\n\n\treturn <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>;\n}\nThemeProvider.displayName = \"ThemeProvider\";\n\n/**\n * useTheme returns the current theme and a function to set the theme.\n *\n * @note This function will throw an error if used outside of a ThemeProvider context tree.\n */\nfunction useTheme() {\n\tconst context = useContext(ThemeProviderContext);\n\n\tinvariant(context, \"useTheme must be used within a ThemeProvider\");\n\n\treturn context;\n}\n\n/**\n * Applies the given theme to the `<html>` element.\n */\nfunction applyThemeToHtml(theme: Theme) {\n\tif (!canUseDOM()) {\n\t\treturn;\n\t}\n\n\tconst html = window.document.documentElement;\n\n\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\n\tconst resolvedTheme = resolveTheme(theme, {\n\t\tprefersDarkMode,\n\t\tprefersHighContrast,\n\t});\n\n\tconst htmlTheme = html.dataset.theme;\n\tconst htmlAppliedTheme = html.dataset.appliedTheme;\n\n\tconst currentTheme = isTheme(htmlTheme) ? htmlTheme : undefined;\n\tconst currentResolvedTheme = isResolvedTheme(htmlAppliedTheme) ? htmlAppliedTheme : undefined;\n\n\tif (currentTheme === theme && currentResolvedTheme === resolvedTheme) {\n\t\t// nothing to do: input theme and resolved class already match\n\t\treturn;\n\t}\n\n\t// Clear any stale theme class, then apply the new one\n\thtml.classList.remove(...resolvedThemes); // ✅ remove all potential theme classes\n\thtml.classList.add(resolvedTheme);\n\thtml.dataset.theme = theme;\n\thtml.dataset.appliedTheme = resolvedTheme;\n}\n\n/**\n * Read the theme and applied theme from the `<html>` element.\n */\nfunction readThemeFromHtmlElement() {\n\tif (!canUseDOM()) {\n\t\treturn {\n\t\t\tappliedTheme: undefined,\n\t\t\ttheme: undefined,\n\t\t};\n\t}\n\n\tconst htmlElement = window.document.documentElement;\n\tconst theme = isTheme(htmlElement.dataset.theme) ? htmlElement.dataset.theme : undefined;\n\tconst appliedTheme = isResolvedTheme(htmlElement.dataset.appliedTheme)\n\t\t? htmlElement.dataset.appliedTheme\n\t\t: undefined;\n\n\treturn {\n\t\tappliedTheme,\n\t\ttheme,\n\t};\n}\n\n/**\n * If the theme is \"system\", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction resolveTheme(\n\ttheme: Theme,\n\t{\n\t\tprefersDarkMode,\n\t\tprefersHighContrast,\n\t}: { prefersDarkMode: boolean; prefersHighContrast: boolean },\n) {\n\tif (theme === \"system\") {\n\t\treturn determineThemeFromMediaQuery({\n\t\t\tprefersDarkMode,\n\t\t\tprefersHighContrast,\n\t\t});\n\t}\n\n\treturn theme;\n}\n\n/**\n * If the theme is \"system\", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction useAppliedTheme() {\n\tconst themeContext = useContext(ThemeProviderContext);\n\tconst theme = themeContext != null ? themeContext[0] : \"system\";\n\n\tconst prefersDarkMode = useMatchesMediaQuery(prefersDarkModeMediaQuery);\n\tconst prefersHighContrast = useMatchesMediaQuery(prefersHighContrastMediaQuery);\n\n\treturn resolveTheme(theme, { prefersDarkMode, prefersHighContrast });\n}\n\n/**\n * determineThemeFromMediaQuery returns the theme that should be used based on the user's media query preferences.\n * @private\n *\n * @example\n * ```tsx\n * const theme = determineThemeFromMediaQuery({\n * prefersDarkMode: true,\n * prefersHighContrast: false\n * });\n * // Returns: \"dark\"\n *\n * const themeWithContrast = determineThemeFromMediaQuery({\n * prefersDarkMode: false,\n * prefersHighContrast: true\n * });\n * // Returns: \"light-high-contrast\"\n * ```\n */\nexport function determineThemeFromMediaQuery({\n\tprefersDarkMode,\n\tprefersHighContrast,\n}: {\n\tprefersDarkMode: boolean;\n\tprefersHighContrast: boolean;\n}): ResolvedTheme {\n\tif (prefersHighContrast) {\n\t\treturn prefersDarkMode ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t}\n\n\treturn prefersDarkMode ? \"dark\" : \"light\";\n}\n\n/**\n * The FOUC-prevention bootstrap. This entire function is stringified and inlined\n * into a blocking `<script>` in the document head; it runs synchronously before\n * React mounts (and before first paint) so the resolved theme class is on\n * `<html>` by the time the browser paints, avoiding a light→dark (or vice-versa)\n * flash.\n *\n * Resolution order:\n * 1. Read the stored preference: cookie first, then `localStorage` (legacy fallback).\n * 2. Validate it against the configured `themes`; fall back to `defaultTheme` otherwise.\n * 3. If the preference is `\"system\"`, resolve against the OS media queries\n * (`prefers-color-scheme`, `prefers-contrast`).\n * 4. Apply the resolved class to `<html>` and refresh the cookie so subsequent\n * SSRs see the same value.\n *\n * Why nested helpers: this function is serialized verbatim into the inlined\n * script, so it must be hermetic — every helper it calls has to travel with it.\n * Hoisting them to module scope would leave dangling references in the inlined\n * source. All catches are intentionally swallowing to keep the script crash-free\n * in environments where cookies / `localStorage` / `matchMedia` throw (sandboxed\n * iframes, privacy modes, SSR-style polyfills).\n *\n * @param args.storageKey Cookie + localStorage key for the persisted theme.\n * @param args.defaultTheme Theme to use when no valid preference is stored.\n * @param args.themes Allowed `Theme` values (used to validate stored input).\n * @param args.resolvedThemes Allowed `ResolvedTheme` class names applied to `<html>`.\n * @param args.prefersDarkModeMediaQuery Media query string for OS dark-mode detection.\n * @param args.prefersHighContrastMediaQuery Media query string for OS high-contrast detection.\n */\nfunction preventThemeFlash(args: {\n\tstorageKey: string;\n\tdefaultTheme: Theme;\n\tthemes: readonly Theme[];\n\tresolvedThemes: readonly ResolvedTheme[];\n\tprefersDarkModeMediaQuery: string;\n\tprefersHighContrastMediaQuery: string;\n}) {\n\tconst {\n\t\tstorageKey,\n\t\tdefaultTheme,\n\t\tthemes,\n\t\tresolvedThemes,\n\t\tprefersDarkModeMediaQuery,\n\t\tprefersHighContrastMediaQuery,\n\t} = args;\n\n\tfunction isTheme(value: unknown): value is Theme {\n\t\treturn typeof value === \"string\" && themes.includes(value as Theme);\n\t}\n\n\t// Nested helpers below must stay inside `preventThemeFlash` so they are\n\t// included when the function is stringified into the inlined FOUC-prevention\n\t// script. Hoisting them would leave dangling references at runtime.\n\t// oxlint-disable-next-line unicorn/consistent-function-scoping\n\tfunction getThemeFromCookie(name: string): string | null {\n\t\tconst cookie = document.cookie;\n\t\tif (!cookie) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cookies = cookie.split(\";\");\n\t\t\tconst themeCookie = cookies.find((c) => c.trim().startsWith(`${name}=`));\n\t\t\tconst cookieValue = themeCookie?.split(\"=\")[1];\n\t\t\tconst storedTheme = cookieValue ? decodeURIComponent(cookieValue) : null;\n\t\t\treturn storedTheme;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// oxlint-disable-next-line unicorn/consistent-function-scoping -- stringified into the inlined FOUC script; see note above.\n\tfunction buildCookie(name: string, val: string): string {\n\t\tconst expires = new Date();\n\t\texpires.setFullYear(expires.getFullYear() + 1);\n\t\tconst hostname = window.location.hostname;\n\t\tconst protocol = window.location.protocol;\n\t\tconst domainAttribute =\n\t\t\thostname === \"ngrok.com\" || hostname.endsWith(\".ngrok.com\") ? \"; domain=.ngrok.com\" : \"\";\n\t\tconst secureAttribute = protocol === \"https:\" ? \"; Secure\" : \"\";\n\t\treturn `${name}=${encodeURIComponent(val)}; expires=${expires.toUTCString()}; path=/${domainAttribute}; SameSite=Lax${secureAttribute}`;\n\t}\n\n\tfunction writeCookie(name: string, val: string): void {\n\t\ttry {\n\t\t\tdocument.cookie = buildCookie(name, val);\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\t}\n\n\t// oxlint-disable-next-line unicorn/consistent-function-scoping -- stringified into the inlined FOUC script; see note above.\n\tfunction resolveThemeValue(\n\t\ttheme: Theme,\n\t\tisDark: boolean,\n\t\tisHighContrast: boolean,\n\t): ResolvedTheme {\n\t\tif (theme === \"system\") {\n\t\t\tif (isHighContrast) {\n\t\t\t\treturn isDark ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t\t\t}\n\t\t\treturn isDark ? \"dark\" : \"light\";\n\t\t}\n\t\treturn theme;\n\t}\n\n\t// 1) Read preference: cookie first, fallback to localStorage (migration support)\n\tlet cookieTheme: string | null = null;\n\tlet lsTheme: string | null = null;\n\tlet storedTheme: Theme | null = null;\n\n\ttry {\n\t\tcookieTheme = getThemeFromCookie(storageKey);\n\t} catch {\n\t\t// silently swallow errors\n\t}\n\n\tif (isTheme(cookieTheme)) {\n\t\tstoredTheme = cookieTheme;\n\t} else {\n\t\ttry {\n\t\t\tlsTheme = window.localStorage?.getItem(storageKey) ?? null;\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\t\tif (isTheme(lsTheme)) {\n\t\t\tstoredTheme = lsTheme;\n\t\t}\n\t}\n\n\tconst preference = isTheme(storedTheme) ? storedTheme : defaultTheme;\n\n\t// 2) Resolve theme based on media queries\n\tconst isDark = matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst isHighContrast = matchMedia(prefersHighContrastMediaQuery).matches;\n\tconst resolvedTheme = resolveThemeValue(preference, isDark, isHighContrast);\n\n\tconst html = document.documentElement;\n\t// 3) Apply theme to DOM (same order as applyThemeToHtml)\n\tif (html.dataset.appliedTheme !== resolvedTheme || html.dataset.theme !== preference) {\n\t\t// Remove all theme classes\n\t\tfor (const themeClass of resolvedThemes as readonly string[]) {\n\t\t\thtml.classList.remove(themeClass);\n\t\t}\n\t\t// Add resolved theme class\n\t\thtml.classList.add(resolvedTheme);\n\t\t// Set data attributes\n\t\thtml.dataset.theme = preference;\n\t\thtml.dataset.appliedTheme = resolvedTheme;\n\t}\n\n\t// 4) Handle persistence/migration synchronously to prevent FOUC\n\tconst hadValidCookie = isTheme(cookieTheme);\n\ttry {\n\t\tif (isTheme(lsTheme) && !hadValidCookie) {\n\t\t\t// Migrate from localStorage to cookie\n\t\t\twriteCookie(storageKey, lsTheme);\n\t\t\ttry {\n\t\t\t\twindow.localStorage.removeItem(storageKey);\n\t\t\t} catch {\n\t\t\t\t// silently swallow errors\n\t\t\t}\n\t\t} else if (!hadValidCookie) {\n\t\t\t// Set default cookie if none existed\n\t\t\twriteCookie(storageKey, preference);\n\t\t}\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n\n/**\n * preventWrongThemeFlashScriptContent generates a script that prevents the wrong theme from flashing on initial page load.\n * It checks cookies for a stored theme, and if none is found, it sets the default theme.\n * It also applies the correct theme to the `<html>` element based on the user's media query preferences.\n */\nfunction preventWrongThemeFlashScriptContent() {\n\tconst args = {\n\t\tstorageKey: THEME_STORAGE_KEY,\n\t\tdefaultTheme: DEFAULT_THEME,\n\t\tthemes,\n\t\tresolvedThemes,\n\t\tprefersDarkModeMediaQuery,\n\t\tprefersHighContrastMediaQuery,\n\t} as const satisfies Parameters<typeof preventThemeFlash>[0];\n\n\treturn `(${preventThemeFlash.toString()})(${JSON.stringify(args)})`;\n}\n\nexport type PreventWrongThemeFlashScriptProps = {\n\t/**\n\t * An optional CSP nonce to allowlist this inline script. Using this can help\n\t * you to avoid using the CSP `unsafe-inline` directive, which disables\n\t * XSS protection and would allowlist all inline scripts or styles.\n\t *\n\t * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce\n\t */\n\tnonce?: string;\n};\n\n/**\n * Renders an inline script that prevents Flash of Unstyled Content (FOUC) or the\n * wrong theme flashing on first paint.\n *\n * This is the preferred building block for SSR apps. Pair it with\n * {@link preloadFontLink} HTTP `Link` headers in your server entry so font fetches\n * begin before HTML is parsed. For client-only apps without header control, pair\n * it with {@link PreloadFont} elements in `<head>` instead.\n *\n * Place this as early as possible in the `<head>`.\n *\n * @example\n * ```tsx\n * // entry.server.tsx — send font preloads as HTTP headers (preferred for SSR)\n * headers.set(\"Link\", [\n * `<${assetsCdnOrigin}>; rel=preconnect; crossorigin`,\n * preloadFontLink(\"roobert\"),\n * preloadFontLink(\"jetbrains-mono\"),\n * ].join(\", \"));\n *\n * // root.tsx — only the FOUC script in <head>\n * <head>\n * <PreventWrongThemeFlashScript nonce={nonce} />\n * </head>\n * ```\n *\n * @param nonce - Optional CSP nonce to allowlist the inline script under a strict CSP.\n * @returns {JSX.Element} A script tag injected before first paint.\n * @see preloadFontLink\n * @see PreloadFont\n * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce\n */\nconst PreventWrongThemeFlashScript = ({ nonce }: PreventWrongThemeFlashScriptProps) => (\n\t<script\n\t\tdangerouslySetInnerHTML={{\n\t\t\t__html: preventWrongThemeFlashScriptContent(),\n\t\t}}\n\t\tnonce={nonce}\n\t\tsuppressHydrationWarning\n\t/>\n);\nPreventWrongThemeFlashScript.displayName = \"PreventWrongThemeFlashScript\";\n\ntype InitialThemeProps = {\n\tclassName: string;\n\t\"data-applied-theme\": ResolvedTheme;\n\t\"data-theme\": Theme;\n};\n\ntype UseInitialHtmlThemePropsOptions = {\n\tclassName?: string;\n\t/**\n\t * Theme cookie string for SSR theme resolution. Pass only the theme cookie\n\t * pair (via {@link extractThemeCookie}) rather than the full raw `Cookie`\n\t * header to avoid leaking sensitive cookies in serialized loader data.\n\t */\n\tssrCookie?: string;\n};\n\n/**\n * useInitialHtmlThemeProps returns the initial props that should be applied to the <html> element to prevent react hydration errors.\n */\nfunction useInitialHtmlThemeProps(props: UseInitialHtmlThemePropsOptions = {}): InitialThemeProps {\n\tconst { className = \"\", ssrCookie } = props ?? {};\n\n\treturn useMemo(() => {\n\t\tlet initialTheme: Theme;\n\t\tlet resolvedTheme: ResolvedTheme;\n\n\t\tif (!canUseDOM()) {\n\t\t\tinitialTheme = getStoredTheme({ cookie: ssrCookie });\n\t\t\tresolvedTheme = resolveTheme(initialTheme, {\n\t\t\t\t// During SSR we can't detect media queries, so assume light/no high contrast.\n\t\t\t\t// The inline script will correct this before paint for \"system\" theme users.\n\t\t\t\tprefersDarkMode: false,\n\t\t\t\tprefersHighContrast: false,\n\t\t\t});\n\t\t} else {\n\t\t\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\t\t\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\t\t\tinitialTheme = getStoredTheme({ cookie: document.cookie });\n\t\t\tresolvedTheme = resolveTheme(initialTheme, {\n\t\t\t\tprefersDarkMode,\n\t\t\t\tprefersHighContrast,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tclassName: cx(className, resolvedTheme),\n\t\t\t\"data-applied-theme\": resolvedTheme,\n\t\t\t\"data-theme\": initialTheme,\n\t\t};\n\t}, [className, ssrCookie]);\n}\n\ntype GetStoredThemeOptions = {\n\t/**\n\t * raw Cookie header (SSR) or document.cookie (client)\n\t */\n\tcookie: string | null | undefined;\n};\n\n/**\n * Returns the persisted UI theme from a Cookie header string.\n *\n * Looks for a cookie named by {@link THEME_STORAGE_KEY} and returns its value **iff**\n * it’s a valid `Theme` per `isTheme`. Otherwise, falls back to\n * {@link DEFAULT_THEME}. This function never throws; malformed encodings or\n * missing cookies quietly return the default.\n *\n * @example\n * getStoredTheme({ cookie: `${THEME_STORAGE_KEY}=dark; session=abc` }) // \"dark\"\n * @example\n * getStoredTheme({ cookie: \"\" }) // DEFAULT_THEME\n */\nfunction getStoredTheme({ cookie }: GetStoredThemeOptions): Theme {\n\tif (!cookie) {\n\t\treturn DEFAULT_THEME;\n\t}\n\n\ttry {\n\t\tconst cookies = cookie.split(\";\");\n\t\tconst themeCookie = cookies.find((cookieStr) =>\n\t\t\tcookieStr.trim().startsWith(`${THEME_STORAGE_KEY}=`),\n\t\t);\n\t\tconst cookieValue = themeCookie?.split(\"=\")[1];\n\t\tconst storedTheme = cookieValue ? globalThis.decodeURIComponent(cookieValue) : null;\n\n\t\treturn isTheme(storedTheme) ? storedTheme : DEFAULT_THEME;\n\t} catch {\n\t\treturn DEFAULT_THEME;\n\t}\n}\n\n/**\n * Extract just the mantle theme cookie from a raw `Cookie` header string.\n *\n * Use this in SSR loaders to safely pass the theme cookie to\n * {@link useInitialHtmlThemeProps} without exposing the full `Cookie` header\n * (which may contain HttpOnly/session cookies) in serialized loader data.\n *\n * @example\n * ```ts\n * // app/root.tsx loader\n * export const loader = async ({ request }: Route.LoaderArgs) => {\n * const themeCookie = extractThemeCookie(request.headers.get(\"Cookie\"));\n * return { themeCookie };\n * };\n * ```\n *\n * @param cookieHeader - The raw `Cookie` header string from the request, or null/undefined.\n * @returns The `mantle-ui-theme=<value>` cookie string, or undefined if not found.\n */\nfunction extractThemeCookie(cookieHeader: string | null | undefined): string | undefined {\n\tif (!cookieHeader) {\n\t\treturn undefined;\n\t}\n\n\treturn cookieHeader\n\t\t.split(\";\")\n\t\t.map((part) => part.trim())\n\t\t.find((part) => part.startsWith(`${THEME_STORAGE_KEY}=`));\n}\n\nexport {\n\tPreventWrongThemeFlashScript,\n\tThemeProvider,\n\t//,\n\textractThemeCookie,\n\tgetStoredTheme,\n\tpreventWrongThemeFlashScriptContent,\n\treadThemeFromHtmlElement,\n\tuseAppliedTheme,\n\tuseInitialHtmlThemeProps,\n\tuseTheme,\n};\n\n/**\n * Notifies other open tabs (same origin) that the theme changed.\n *\n * Prefers a shared {@link BroadcastChannel} for immediate, reliable delivery.\n * Falls back to writing a unique “ping” value to `localStorage`, which triggers\n * the cross-tab `storage` event. Both mechanisms only work across the same origin.\n *\n * Uses a timestamp to ensure the storage value always changes so the event fires.\n *\n * @remarks\n * - Same-origin only: BroadcastChannel and the `storage` event do not cross subdomains\n * or different schemes/ports. For cross-subdomain sync, use a postMessage hub or server push.\n * - This function is fire-and-forget and intentionally swallows errors.\n * - Receivers should re-read the cookie/source of truth and then apply the theme;\n * don’t trust the payload blindly.\n *\n * @example\n * // Sender (inside your setter)\n * notifyOtherTabs(nextTheme, {\n * broadcastChannel: broadcastChannelRef.current,\n * pingKey: `${storageKey}__ping`,\n * });\n *\n * @example\n * // Receiver (setup once per tab)\n * const bc = new BroadcastChannel(storageKey);\n * bc.onmessage = () => syncThemeFromCookie();\n * window.addEventListener('storage', (e) => {\n * if (e.key === `${storageKey}__ping`) syncThemeFromCookie();\n * });\n */\nfunction notifyOtherTabs(\n\ttheme: Theme,\n\toptions: {\n\t\tbroadcastChannel: BroadcastChannel | null;\n\t\tpingKey: `${string}__ping`;\n\t},\n) {\n\tconst { broadcastChannel, pingKey } = options;\n\n\t// first try BroadcastChannel\n\ttry {\n\t\tif (broadcastChannel) {\n\t\t\t// BroadcastChannel.postMessage has no `targetOrigin` parameter (unlike Window.postMessage); the rule can't distinguish the two.\n\t\t\t// oxlint-disable unicorn/require-post-message-target-origin\n\t\t\tbroadcastChannel.postMessage({\n\t\t\t\ttheme,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t});\n\t\t\t// oxlint-enable unicorn/require-post-message-target-origin\n\t\t\treturn;\n\t\t}\n\t} catch {\n\t\t// silently swallow errors\n\t}\n\n\t// fallback to storage event: write a \"ping\" key (not the real storageKey)\n\ttry {\n\t\tlocalStorage.setItem(pingKey, JSON.stringify({ theme, timestamp: Date.now() }));\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n\nfunction buildThemeCookie(value: string) {\n\tconst expires = new Date();\n\texpires.setFullYear(expires.getFullYear() + 1); // 1 year expiration\n\n\t// Only set .ngrok.com domain for ngrok domains, otherwise let it default to current domain\n\tconst { hostname, protocol } = window.location;\n\tconst domainAttribute =\n\t\thostname === \"ngrok.com\" || hostname.endsWith(\".ngrok.com\") ? \"; domain=.ngrok.com\" : \"\";\n\tconst secureAttribute = protocol === \"https:\" ? \"; Secure\" : \"\";\n\n\treturn `${THEME_STORAGE_KEY}=${encodeURIComponent(value)}; expires=${expires.toUTCString()}; path=/${domainAttribute}; SameSite=Lax${secureAttribute}` as const;\n}\n\n/**\n * Sets a cookie with appropriate domain for the current hostname.\n * Uses .ngrok.com for ngrok domains, otherwise no domain (current domain only).\n */\nfunction setCookie(value: string) {\n\tif (!canUseDOM()) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tdocument.cookie = buildThemeCookie(value);\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"theme-provider-MMwxHEfw.js","names":[],"sources":["../src/components/theme/themes.ts","../src/components/theme/theme-provider.tsx"],"sourcesContent":["/**\n * resolvedThemes is a tuple of valid themes that have been resolved from \"system\" to a specific theme.\n */\nconst resolvedThemes = [\"light\", \"dark\", \"light-high-contrast\", \"dark-high-contrast\"] as const;\n\n/**\n * ResolvedTheme is a type that represents a theme that has been resolved from \"system\" to a specific theme.\n */\ntype ResolvedTheme = (typeof resolvedThemes)[number];\n\n/**\n * themes is a tuple of valid themes.\n */\nconst themes = [\"system\", ...resolvedThemes] as const;\n\n/**\n * Theme is a string literal type that represents a valid theme.\n */\ntype Theme = (typeof themes)[number];\n\n/**\n * $theme is a helper which translates the Theme type into a string literal type.\n */\nconst $theme = <T extends Theme = Theme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid theme.\n */\nfunction isTheme(value: unknown): value is Theme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn themes.includes(value as Theme);\n}\n\n/**\n * $resolvedTheme is a helper which translates the ResolvedTheme type into a string literal type.\n */\nconst $resolvedTheme = <T extends ResolvedTheme = ResolvedTheme>(value: T) => value;\n\n/**\n * Type predicate that checks if a value is a valid resolved theme.\n */\nfunction isResolvedTheme(value: unknown): value is ResolvedTheme {\n\tif (typeof value !== \"string\") {\n\t\treturn false;\n\t}\n\n\treturn resolvedThemes.includes(value as ResolvedTheme);\n}\n\nexport {\n\t//,\n\tthemes,\n\tresolvedThemes,\n\t$resolvedTheme,\n\t$theme,\n\tisResolvedTheme,\n\tisTheme,\n};\n\nexport type {\n\t//,\n\tTheme,\n\tResolvedTheme,\n};\n","\"use client\";\n\nimport type { PropsWithChildren } from \"react\";\nimport { createContext, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport invariant from \"tiny-invariant\";\nimport { useMatchesMediaQuery } from \"../../hooks/use-matches-media-query.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { canUseDOM } from \"../browser-only/browser-only.js\";\nimport {\n\ttype ResolvedTheme,\n\ttype Theme,\n\tisResolvedTheme,\n\tisTheme,\n\tresolvedThemes,\n\tthemes,\n} from \"./themes.js\";\n\n/**\n * prefersDarkModeMediaQuery is the media query used to detect if the user prefers dark mode.\n */\nconst prefersDarkModeMediaQuery = \"(prefers-color-scheme: dark)\";\n\n/**\n * prefersHighContrastMediaQuery is the media query used to detect if the user prefers high contrast mode.\n */\nconst prefersHighContrastMediaQuery = \"(prefers-contrast: more)\";\n\n/**\n * THEME_STORAGE_KEY is the key used to store the theme in cookies.\n */\nconst THEME_STORAGE_KEY = \"mantle-ui-theme\";\n\n/**\n * DEFAULT_THEME is the initial theme to apply if no value is found in storage.\n * {@link themes}\n */\nconst DEFAULT_THEME = \"system\" satisfies Theme;\n\n/**\n * ThemeProviderState is the shape of the state returned by the ThemeProviderContext.\n */\ntype ThemeProviderState = [theme: Theme, setTheme: (theme: Theme) => void];\n\n/**\n * Initial state for the ThemeProviderContext.\n */\nconst initialState: ThemeProviderState = [\"system\", () => null];\n\n/**\n * ThemeProviderContext is a React Context that provides the current theme and a function to set the theme.\n */\nconst ThemeProviderContext = createContext<ThemeProviderState | null>(initialState);\n\ntype ThemeProviderProps = PropsWithChildren;\n\n/**\n * ThemeProvider is a React Context Provider that provides the current theme and a function to set the theme.\n *\n * @see https://mantle.ngrok.com/components/theme-provider#themeprovider\n *\n * @example\n * ```tsx\n * <ThemeProvider defaultTheme=\"system\" storageKey=\"app-theme\">\n * <App />\n * </ThemeProvider>\n * ```\n */\nfunction ThemeProvider({ children }: ThemeProviderProps) {\n\t// Init once from cookie and apply immediately to avoid flashes\n\tconst [theme, setTheme] = useState<Theme>(() => {\n\t\tconst storedTheme = getStoredTheme({\n\t\t\tcookie: canUseDOM() ? document.cookie : null,\n\t\t});\n\t\tapplyThemeToHtml(storedTheme);\n\t\treturn storedTheme;\n\t});\n\n\tconst broadcastChannelRef = useRef<BroadcastChannel | null>(null);\n\n\tuseEffect(() => {\n\t\tfunction syncThemeFromCookie(next?: Theme) {\n\t\t\tconst newTheme = next ?? getStoredTheme({ cookie: document.cookie });\n\t\t\tsetTheme(newTheme);\n\t\t\tapplyThemeToHtml(newTheme);\n\t\t}\n\n\t\t// initial sync in case defaultTheme or storageKey changed\n\t\tsyncThemeFromCookie();\n\n\t\t// add cross-tab listeners (prefer broadcast channel, use localStorage as fallback)\n\t\ttry {\n\t\t\tif (\"BroadcastChannel\" in window) {\n\t\t\t\tbroadcastChannelRef.current = new BroadcastChannel(THEME_STORAGE_KEY);\n\t\t\t\tbroadcastChannelRef.current.addEventListener(\"message\", (event) => {\n\t\t\t\t\tconst value: unknown = event?.data?.theme;\n\t\t\t\t\tif (isTheme(value)) {\n\t\t\t\t\t\tsyncThemeFromCookie(value);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\n\t\tfunction onStorage(event: StorageEvent) {\n\t\t\tif (event.key === `${THEME_STORAGE_KEY}__ping`) {\n\t\t\t\tsyncThemeFromCookie();\n\t\t\t}\n\t\t}\n\t\twindow.addEventListener(\"storage\", onStorage);\n\n\t\t// add media query listeners for system theme changes\n\t\tconst prefersDarkMql = window.matchMedia(prefersDarkModeMediaQuery);\n\t\tconst prefersHighContrastMql = window.matchMedia(prefersHighContrastMediaQuery);\n\n\t\tfunction onChange() {\n\t\t\tsyncThemeFromCookie();\n\t\t}\n\n\t\tfunction onVisibilityChange() {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tsyncThemeFromCookie();\n\t\t\t}\n\t\t}\n\n\t\tprefersDarkMql.addEventListener(\"change\", onChange);\n\t\tprefersHighContrastMql.addEventListener(\"change\", onChange);\n\n\t\t// pageshow fires on bfcache restore (event.persisted === true) and some restore-from-freeze cases.\n\t\twindow.addEventListener(\"pageshow\", onChange);\n\n\t\t// visibilitychange to handle coming back to a tab\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\t// don't forget to clean up your slop!\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"storage\", onStorage);\n\t\t\tprefersDarkMql.removeEventListener(\"change\", onChange);\n\t\t\tprefersHighContrastMql.removeEventListener(\"change\", onChange);\n\t\t\twindow.removeEventListener(\"pageshow\", onChange);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\t\ttry {\n\t\t\t\tbroadcastChannelRef.current?.close();\n\t\t\t} catch {\n\t\t\t\t// silently swallow errors\n\t\t\t}\n\t\t\tbroadcastChannelRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst value: ThemeProviderState = useMemo(\n\t\t() => [\n\t\t\ttheme,\n\t\t\t(next: Theme) => {\n\t\t\t\tsetCookie(next);\n\t\t\t\tsetTheme(next);\n\t\t\t\tapplyThemeToHtml(next);\n\t\t\t\tnotifyOtherTabs(next, {\n\t\t\t\t\tbroadcastChannel: broadcastChannelRef.current,\n\t\t\t\t\tpingKey: `${THEME_STORAGE_KEY}__ping`,\n\t\t\t\t});\n\t\t\t},\n\t\t],\n\t\t[theme],\n\t);\n\n\treturn <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>;\n}\nThemeProvider.displayName = \"ThemeProvider\";\n\n/**\n * useTheme returns the current theme and a function to set the theme.\n *\n * @note This function will throw an error if used outside of a ThemeProvider context tree.\n */\nfunction useTheme() {\n\tconst context = useContext(ThemeProviderContext);\n\n\tinvariant(context, \"useTheme must be used within a ThemeProvider\");\n\n\treturn context;\n}\n\n/**\n * Applies the given theme to the `<html>` element.\n */\nfunction applyThemeToHtml(theme: Theme) {\n\tif (!canUseDOM()) {\n\t\treturn;\n\t}\n\n\tconst html = window.document.documentElement;\n\n\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\n\tconst resolvedTheme = resolveTheme(theme, {\n\t\tprefersDarkMode,\n\t\tprefersHighContrast,\n\t});\n\n\tconst htmlTheme = html.dataset.theme;\n\tconst htmlAppliedTheme = html.dataset.appliedTheme;\n\n\tconst currentTheme = isTheme(htmlTheme) ? htmlTheme : undefined;\n\tconst currentResolvedTheme = isResolvedTheme(htmlAppliedTheme) ? htmlAppliedTheme : undefined;\n\n\tif (currentTheme === theme && currentResolvedTheme === resolvedTheme) {\n\t\t// nothing to do: input theme and resolved class already match\n\t\treturn;\n\t}\n\n\t// Clear any stale theme class, then apply the new one\n\thtml.classList.remove(...resolvedThemes); // ✅ remove all potential theme classes\n\thtml.classList.add(resolvedTheme);\n\thtml.dataset.theme = theme;\n\thtml.dataset.appliedTheme = resolvedTheme;\n}\n\n/**\n * Read the theme and applied theme from the `<html>` element.\n */\nfunction readThemeFromHtmlElement() {\n\tif (!canUseDOM()) {\n\t\treturn {\n\t\t\tappliedTheme: undefined,\n\t\t\ttheme: undefined,\n\t\t};\n\t}\n\n\tconst htmlElement = window.document.documentElement;\n\tconst theme = isTheme(htmlElement.dataset.theme) ? htmlElement.dataset.theme : undefined;\n\tconst appliedTheme = isResolvedTheme(htmlElement.dataset.appliedTheme)\n\t\t? htmlElement.dataset.appliedTheme\n\t\t: undefined;\n\n\treturn {\n\t\tappliedTheme,\n\t\ttheme,\n\t};\n}\n\n/**\n * If the theme is \"system\", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction resolveTheme(\n\ttheme: Theme,\n\t{\n\t\tprefersDarkMode,\n\t\tprefersHighContrast,\n\t}: { prefersDarkMode: boolean; prefersHighContrast: boolean },\n) {\n\tif (theme === \"system\") {\n\t\treturn determineThemeFromMediaQuery({\n\t\t\tprefersDarkMode,\n\t\t\tprefersHighContrast,\n\t\t});\n\t}\n\n\treturn theme;\n}\n\n/**\n * If the theme is \"system\", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.\n * This will mirror the result that gets applied to the <html> element.\n */\nfunction useAppliedTheme() {\n\tconst themeContext = useContext(ThemeProviderContext);\n\tconst theme = themeContext != null ? themeContext[0] : \"system\";\n\n\tconst prefersDarkMode = useMatchesMediaQuery(prefersDarkModeMediaQuery);\n\tconst prefersHighContrast = useMatchesMediaQuery(prefersHighContrastMediaQuery);\n\n\treturn resolveTheme(theme, { prefersDarkMode, prefersHighContrast });\n}\n\n/**\n * determineThemeFromMediaQuery returns the theme that should be used based on the user's media query preferences.\n * @private\n *\n * @example\n * ```tsx\n * const theme = determineThemeFromMediaQuery({\n * prefersDarkMode: true,\n * prefersHighContrast: false\n * });\n * // Returns: \"dark\"\n *\n * const themeWithContrast = determineThemeFromMediaQuery({\n * prefersDarkMode: false,\n * prefersHighContrast: true\n * });\n * // Returns: \"light-high-contrast\"\n * ```\n */\nexport function determineThemeFromMediaQuery({\n\tprefersDarkMode,\n\tprefersHighContrast,\n}: {\n\tprefersDarkMode: boolean;\n\tprefersHighContrast: boolean;\n}): ResolvedTheme {\n\tif (prefersHighContrast) {\n\t\treturn prefersDarkMode ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t}\n\n\treturn prefersDarkMode ? \"dark\" : \"light\";\n}\n\n/**\n * The FOUC-prevention bootstrap. This entire function is stringified and inlined\n * into a blocking `<script>` in the document head; it runs synchronously before\n * React mounts (and before first paint) so the resolved theme class is on\n * `<html>` by the time the browser paints, avoiding a light→dark (or vice-versa)\n * flash.\n *\n * Resolution order:\n * 1. Read the stored preference: cookie first, then `localStorage` (legacy fallback).\n * 2. Validate it against the configured `themes`; fall back to `defaultTheme` otherwise.\n * 3. If the preference is `\"system\"`, resolve against the OS media queries\n * (`prefers-color-scheme`, `prefers-contrast`).\n * 4. Apply the resolved class to `<html>` and refresh the cookie so subsequent\n * SSRs see the same value.\n *\n * Why nested helpers: this function is serialized verbatim into the inlined\n * script, so it must be hermetic — every helper it calls has to travel with it.\n * Hoisting them to module scope would leave dangling references in the inlined\n * source. All catches are intentionally swallowing to keep the script crash-free\n * in environments where cookies / `localStorage` / `matchMedia` throw (sandboxed\n * iframes, privacy modes, SSR-style polyfills).\n *\n * @param args.storageKey Cookie + localStorage key for the persisted theme.\n * @param args.defaultTheme Theme to use when no valid preference is stored.\n * @param args.themes Allowed `Theme` values (used to validate stored input).\n * @param args.resolvedThemes Allowed `ResolvedTheme` class names applied to `<html>`.\n * @param args.prefersDarkModeMediaQuery Media query string for OS dark-mode detection.\n * @param args.prefersHighContrastMediaQuery Media query string for OS high-contrast detection.\n */\nfunction preventThemeFlash(args: {\n\tstorageKey: string;\n\tdefaultTheme: Theme;\n\tthemes: readonly Theme[];\n\tresolvedThemes: readonly ResolvedTheme[];\n\tprefersDarkModeMediaQuery: string;\n\tprefersHighContrastMediaQuery: string;\n}) {\n\tconst {\n\t\tstorageKey,\n\t\tdefaultTheme,\n\t\tthemes,\n\t\tresolvedThemes,\n\t\tprefersDarkModeMediaQuery,\n\t\tprefersHighContrastMediaQuery,\n\t} = args;\n\n\tfunction isTheme(value: unknown): value is Theme {\n\t\treturn typeof value === \"string\" && themes.includes(value as Theme);\n\t}\n\n\t// Nested helpers below must stay inside `preventThemeFlash` so they are\n\t// included when the function is stringified into the inlined FOUC-prevention\n\t// script. Hoisting them would leave dangling references at runtime.\n\t// oxlint-disable-next-line unicorn/consistent-function-scoping\n\tfunction getThemeFromCookie(name: string): string | null {\n\t\tconst cookie = document.cookie;\n\t\tif (!cookie) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cookies = cookie.split(\";\");\n\t\t\tconst themeCookie = cookies.find((c) => c.trim().startsWith(`${name}=`));\n\t\t\tconst cookieValue = themeCookie?.split(\"=\")[1];\n\t\t\tconst storedTheme = cookieValue ? decodeURIComponent(cookieValue) : null;\n\t\t\treturn storedTheme;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// oxlint-disable-next-line unicorn/consistent-function-scoping -- stringified into the inlined FOUC script; see note above.\n\tfunction buildCookie(name: string, val: string): string {\n\t\tconst expires = new Date();\n\t\texpires.setFullYear(expires.getFullYear() + 1);\n\t\tconst hostname = window.location.hostname;\n\t\tconst protocol = window.location.protocol;\n\t\tconst domainAttribute =\n\t\t\thostname === \"ngrok.com\" || hostname.endsWith(\".ngrok.com\") ? \"; domain=.ngrok.com\" : \"\";\n\t\tconst secureAttribute = protocol === \"https:\" ? \"; Secure\" : \"\";\n\t\treturn `${name}=${encodeURIComponent(val)}; expires=${expires.toUTCString()}; path=/${domainAttribute}; SameSite=Lax${secureAttribute}`;\n\t}\n\n\tfunction writeCookie(name: string, val: string): void {\n\t\ttry {\n\t\t\tdocument.cookie = buildCookie(name, val);\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\t}\n\n\t// oxlint-disable-next-line unicorn/consistent-function-scoping -- stringified into the inlined FOUC script; see note above.\n\tfunction resolveThemeValue(\n\t\ttheme: Theme,\n\t\tisDark: boolean,\n\t\tisHighContrast: boolean,\n\t): ResolvedTheme {\n\t\tif (theme === \"system\") {\n\t\t\tif (isHighContrast) {\n\t\t\t\treturn isDark ? \"dark-high-contrast\" : \"light-high-contrast\";\n\t\t\t}\n\t\t\treturn isDark ? \"dark\" : \"light\";\n\t\t}\n\t\treturn theme;\n\t}\n\n\t// 1) Read preference: cookie first, fallback to localStorage (migration support)\n\tlet cookieTheme: string | null = null;\n\tlet lsTheme: string | null = null;\n\tlet storedTheme: Theme | null = null;\n\n\ttry {\n\t\tcookieTheme = getThemeFromCookie(storageKey);\n\t} catch {\n\t\t// silently swallow errors\n\t}\n\n\tif (isTheme(cookieTheme)) {\n\t\tstoredTheme = cookieTheme;\n\t} else {\n\t\ttry {\n\t\t\tlsTheme = window.localStorage?.getItem(storageKey) ?? null;\n\t\t} catch {\n\t\t\t// silently swallow errors\n\t\t}\n\t\tif (isTheme(lsTheme)) {\n\t\t\tstoredTheme = lsTheme;\n\t\t}\n\t}\n\n\tconst preference = isTheme(storedTheme) ? storedTheme : defaultTheme;\n\n\t// 2) Resolve theme based on media queries\n\tconst isDark = matchMedia(prefersDarkModeMediaQuery).matches;\n\tconst isHighContrast = matchMedia(prefersHighContrastMediaQuery).matches;\n\tconst resolvedTheme = resolveThemeValue(preference, isDark, isHighContrast);\n\n\tconst html = document.documentElement;\n\t// 3) Apply theme to DOM (same order as applyThemeToHtml)\n\tif (html.dataset.appliedTheme !== resolvedTheme || html.dataset.theme !== preference) {\n\t\t// Remove all theme classes\n\t\tfor (const themeClass of resolvedThemes as readonly string[]) {\n\t\t\thtml.classList.remove(themeClass);\n\t\t}\n\t\t// Add resolved theme class\n\t\thtml.classList.add(resolvedTheme);\n\t\t// Set data attributes\n\t\thtml.dataset.theme = preference;\n\t\thtml.dataset.appliedTheme = resolvedTheme;\n\t}\n\n\t// 4) Handle persistence/migration synchronously to prevent FOUC\n\tconst hadValidCookie = isTheme(cookieTheme);\n\ttry {\n\t\tif (isTheme(lsTheme) && !hadValidCookie) {\n\t\t\t// Migrate from localStorage to cookie\n\t\t\twriteCookie(storageKey, lsTheme);\n\t\t\ttry {\n\t\t\t\twindow.localStorage.removeItem(storageKey);\n\t\t\t} catch {\n\t\t\t\t// silently swallow errors\n\t\t\t}\n\t\t} else if (!hadValidCookie) {\n\t\t\t// Set default cookie if none existed\n\t\t\twriteCookie(storageKey, preference);\n\t\t}\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n\n/**\n * preventWrongThemeFlashScriptContent generates a script that prevents the wrong theme from flashing on initial page load.\n * It checks cookies for a stored theme, and if none is found, it sets the default theme.\n * It also applies the correct theme to the `<html>` element based on the user's media query preferences.\n */\nfunction preventWrongThemeFlashScriptContent() {\n\tconst args = {\n\t\tstorageKey: THEME_STORAGE_KEY,\n\t\tdefaultTheme: DEFAULT_THEME,\n\t\tthemes,\n\t\tresolvedThemes,\n\t\tprefersDarkModeMediaQuery,\n\t\tprefersHighContrastMediaQuery,\n\t} as const satisfies Parameters<typeof preventThemeFlash>[0];\n\n\treturn `(${preventThemeFlash.toString()})(${JSON.stringify(args)})`;\n}\n\nexport type PreventWrongThemeFlashScriptProps = {\n\t/**\n\t * An optional CSP nonce to allowlist this inline script. Using this can help\n\t * you to avoid using the CSP `unsafe-inline` directive, which disables\n\t * XSS protection and would allowlist all inline scripts or styles.\n\t *\n\t * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce\n\t */\n\tnonce?: string;\n};\n\n/**\n * Renders an inline script that prevents Flash of Unstyled Content (FOUC) or the\n * wrong theme flashing on first paint.\n *\n * This is the preferred building block for SSR apps. Pair it with\n * {@link preloadFontLink} HTTP `Link` headers in your server entry so font fetches\n * begin before HTML is parsed. For client-only apps without header control, pair\n * it with {@link PreloadFont} elements in `<head>` instead.\n *\n * Place this as early as possible in the `<head>`.\n *\n * @example\n * ```tsx\n * // entry.server.tsx — send font preloads as HTTP headers (preferred for SSR)\n * headers.set(\"Link\", [\n * `<${assetsCdnOrigin}>; rel=preconnect; crossorigin`,\n * preloadFontLink(\"roobert\"),\n * preloadFontLink(\"jetbrains-mono\"),\n * ].join(\", \"));\n *\n * // root.tsx — only the FOUC script in <head>\n * <head>\n * <PreventWrongThemeFlashScript nonce={nonce} />\n * </head>\n * ```\n *\n * @param nonce - Optional CSP nonce to allowlist the inline script under a strict CSP.\n * @returns {JSX.Element} A script tag injected before first paint.\n * @see preloadFontLink\n * @see PreloadFont\n * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce\n */\nconst PreventWrongThemeFlashScript = ({ nonce }: PreventWrongThemeFlashScriptProps) => (\n\t<script\n\t\tdangerouslySetInnerHTML={{\n\t\t\t__html: preventWrongThemeFlashScriptContent(),\n\t\t}}\n\t\tnonce={nonce}\n\t\tsuppressHydrationWarning\n\t/>\n);\nPreventWrongThemeFlashScript.displayName = \"PreventWrongThemeFlashScript\";\n\ntype InitialThemeProps = {\n\tclassName: string;\n\t\"data-applied-theme\": ResolvedTheme;\n\t\"data-theme\": Theme;\n};\n\ntype UseInitialHtmlThemePropsOptions = {\n\tclassName?: string;\n\t/**\n\t * Theme cookie string for SSR theme resolution. Pass only the theme cookie\n\t * pair (via {@link extractThemeCookie}) rather than the full raw `Cookie`\n\t * header to avoid leaking sensitive cookies in serialized loader data.\n\t */\n\tssrCookie?: string;\n};\n\n/**\n * useInitialHtmlThemeProps returns the initial props that should be applied to the <html> element to prevent react hydration errors.\n */\nfunction useInitialHtmlThemeProps(props: UseInitialHtmlThemePropsOptions = {}): InitialThemeProps {\n\tconst { className = \"\", ssrCookie } = props ?? {};\n\n\treturn useMemo(() => {\n\t\tlet initialTheme: Theme;\n\t\tlet resolvedTheme: ResolvedTheme;\n\n\t\tif (!canUseDOM()) {\n\t\t\tinitialTheme = getStoredTheme({ cookie: ssrCookie });\n\t\t\tresolvedTheme = resolveTheme(initialTheme, {\n\t\t\t\t// During SSR we can't detect media queries, so assume light/no high contrast.\n\t\t\t\t// The inline script will correct this before paint for \"system\" theme users.\n\t\t\t\tprefersDarkMode: false,\n\t\t\t\tprefersHighContrast: false,\n\t\t\t});\n\t\t} else {\n\t\t\tconst prefersDarkMode = window.matchMedia(prefersDarkModeMediaQuery).matches;\n\t\t\tconst prefersHighContrast = window.matchMedia(prefersHighContrastMediaQuery).matches;\n\t\t\tinitialTheme = getStoredTheme({ cookie: document.cookie });\n\t\t\tresolvedTheme = resolveTheme(initialTheme, {\n\t\t\t\tprefersDarkMode,\n\t\t\t\tprefersHighContrast,\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tclassName: cx(className, resolvedTheme),\n\t\t\t\"data-applied-theme\": resolvedTheme,\n\t\t\t\"data-theme\": initialTheme,\n\t\t};\n\t}, [className, ssrCookie]);\n}\n\ntype GetStoredThemeOptions = {\n\t/**\n\t * raw Cookie header (SSR) or document.cookie (client)\n\t */\n\tcookie: string | null | undefined;\n};\n\n/**\n * Returns the persisted UI theme from a Cookie header string.\n *\n * Looks for a cookie named by {@link THEME_STORAGE_KEY} and returns its value **iff**\n * it’s a valid `Theme` per `isTheme`. Otherwise, falls back to\n * {@link DEFAULT_THEME}. This function never throws; malformed encodings or\n * missing cookies quietly return the default.\n *\n * @example\n * getStoredTheme({ cookie: `${THEME_STORAGE_KEY}=dark; session=abc` }) // \"dark\"\n * @example\n * getStoredTheme({ cookie: \"\" }) // DEFAULT_THEME\n */\nfunction getStoredTheme({ cookie }: GetStoredThemeOptions): Theme {\n\tif (!cookie) {\n\t\treturn DEFAULT_THEME;\n\t}\n\n\ttry {\n\t\tconst cookies = cookie.split(\";\");\n\t\tconst themeCookie = cookies.find((cookieStr) =>\n\t\t\tcookieStr.trim().startsWith(`${THEME_STORAGE_KEY}=`),\n\t\t);\n\t\tconst cookieValue = themeCookie?.split(\"=\")[1];\n\t\tconst storedTheme = cookieValue ? globalThis.decodeURIComponent(cookieValue) : null;\n\n\t\treturn isTheme(storedTheme) ? storedTheme : DEFAULT_THEME;\n\t} catch {\n\t\treturn DEFAULT_THEME;\n\t}\n}\n\n/**\n * Extract just the mantle theme cookie from a raw `Cookie` header string.\n *\n * Use this in SSR loaders to safely pass the theme cookie to\n * {@link useInitialHtmlThemeProps} without exposing the full `Cookie` header\n * (which may contain HttpOnly/session cookies) in serialized loader data.\n *\n * @example\n * ```ts\n * // app/root.tsx loader\n * export const loader = async ({ request }: Route.LoaderArgs) => {\n * const themeCookie = extractThemeCookie(request.headers.get(\"Cookie\"));\n * return { themeCookie };\n * };\n * ```\n *\n * @param cookieHeader - The raw `Cookie` header string from the request, or null/undefined.\n * @returns The `mantle-ui-theme=<value>` cookie string, or undefined if not found.\n */\nfunction extractThemeCookie(cookieHeader: string | null | undefined): string | undefined {\n\tif (!cookieHeader) {\n\t\treturn undefined;\n\t}\n\n\treturn cookieHeader\n\t\t.split(\";\")\n\t\t.map((part) => part.trim())\n\t\t.find((part) => part.startsWith(`${THEME_STORAGE_KEY}=`));\n}\n\nexport {\n\tPreventWrongThemeFlashScript,\n\tThemeProvider,\n\t//,\n\textractThemeCookie,\n\tgetStoredTheme,\n\tpreventWrongThemeFlashScriptContent,\n\treadThemeFromHtmlElement,\n\tuseAppliedTheme,\n\tuseInitialHtmlThemeProps,\n\tuseTheme,\n};\n\n/**\n * Notifies other open tabs (same origin) that the theme changed.\n *\n * Prefers a shared {@link BroadcastChannel} for immediate, reliable delivery.\n * Falls back to writing a unique “ping” value to `localStorage`, which triggers\n * the cross-tab `storage` event. Both mechanisms only work across the same origin.\n *\n * Uses a timestamp to ensure the storage value always changes so the event fires.\n *\n * @remarks\n * - Same-origin only: BroadcastChannel and the `storage` event do not cross subdomains\n * or different schemes/ports. For cross-subdomain sync, use a postMessage hub or server push.\n * - This function is fire-and-forget and intentionally swallows errors.\n * - Receivers should re-read the cookie/source of truth and then apply the theme;\n * don’t trust the payload blindly.\n *\n * @example\n * // Sender (inside your setter)\n * notifyOtherTabs(nextTheme, {\n * broadcastChannel: broadcastChannelRef.current,\n * pingKey: `${storageKey}__ping`,\n * });\n *\n * @example\n * // Receiver (setup once per tab)\n * const bc = new BroadcastChannel(storageKey);\n * bc.onmessage = () => syncThemeFromCookie();\n * window.addEventListener('storage', (e) => {\n * if (e.key === `${storageKey}__ping`) syncThemeFromCookie();\n * });\n */\nfunction notifyOtherTabs(\n\ttheme: Theme,\n\toptions: {\n\t\tbroadcastChannel: BroadcastChannel | null;\n\t\tpingKey: `${string}__ping`;\n\t},\n) {\n\tconst { broadcastChannel, pingKey } = options;\n\n\t// first try BroadcastChannel\n\ttry {\n\t\tif (broadcastChannel) {\n\t\t\t// BroadcastChannel.postMessage has no `targetOrigin` parameter (unlike Window.postMessage); the rule can't distinguish the two.\n\t\t\t// oxlint-disable unicorn/require-post-message-target-origin\n\t\t\tbroadcastChannel.postMessage({\n\t\t\t\ttheme,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t});\n\t\t\t// oxlint-enable unicorn/require-post-message-target-origin\n\t\t\treturn;\n\t\t}\n\t} catch {\n\t\t// silently swallow errors\n\t}\n\n\t// fallback to storage event: write a \"ping\" key (not the real storageKey)\n\ttry {\n\t\tlocalStorage.setItem(pingKey, JSON.stringify({ theme, timestamp: Date.now() }));\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n\nfunction buildThemeCookie(value: string) {\n\tconst expires = new Date();\n\texpires.setFullYear(expires.getFullYear() + 1); // 1 year expiration\n\n\t// Only set .ngrok.com domain for ngrok domains, otherwise let it default to current domain\n\tconst { hostname, protocol } = window.location;\n\tconst domainAttribute =\n\t\thostname === \"ngrok.com\" || hostname.endsWith(\".ngrok.com\") ? \"; domain=.ngrok.com\" : \"\";\n\tconst secureAttribute = protocol === \"https:\" ? \"; Secure\" : \"\";\n\n\treturn `${THEME_STORAGE_KEY}=${encodeURIComponent(value)}; expires=${expires.toUTCString()}; path=/${domainAttribute}; SameSite=Lax${secureAttribute}` as const;\n}\n\n/**\n * Sets a cookie with appropriate domain for the current hostname.\n * Uses .ngrok.com for ngrok domains, otherwise no domain (current domain only).\n */\nfunction setCookie(value: string) {\n\tif (!canUseDOM()) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tdocument.cookie = buildThemeCookie(value);\n\t} catch {\n\t\t// silently swallow errors\n\t}\n}\n"],"mappings":"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"}
|
package/dist/theme.d.ts
CHANGED
|
@@ -162,7 +162,7 @@ declare function MantleStyleSheets({
|
|
|
162
162
|
forceTheme,
|
|
163
163
|
nonce,
|
|
164
164
|
ssrCookie
|
|
165
|
-
}: MantleStyleSheetsProps): import("react
|
|
165
|
+
}: MantleStyleSheetsProps): import("react").JSX.Element;
|
|
166
166
|
declare namespace MantleStyleSheets {
|
|
167
167
|
var displayName: string;
|
|
168
168
|
}
|
|
@@ -187,7 +187,7 @@ type ThemeProviderProps = PropsWithChildren;
|
|
|
187
187
|
*/
|
|
188
188
|
declare function ThemeProvider({
|
|
189
189
|
children
|
|
190
|
-
}: ThemeProviderProps): import("react
|
|
190
|
+
}: ThemeProviderProps): import("react").JSX.Element;
|
|
191
191
|
declare namespace ThemeProvider {
|
|
192
192
|
var displayName: string;
|
|
193
193
|
}
|
|
@@ -279,7 +279,7 @@ type PreventWrongThemeFlashScriptProps = {
|
|
|
279
279
|
declare const PreventWrongThemeFlashScript: {
|
|
280
280
|
({
|
|
281
281
|
nonce
|
|
282
|
-
}: PreventWrongThemeFlashScriptProps): import("react
|
|
282
|
+
}: PreventWrongThemeFlashScriptProps): import("react").JSX.Element;
|
|
283
283
|
displayName: string;
|
|
284
284
|
};
|
|
285
285
|
type InitialThemeProps = {
|
|
@@ -436,7 +436,7 @@ declare function preloadFontLink(name: CoreFontName): string;
|
|
|
436
436
|
declare const PreloadFont: {
|
|
437
437
|
({
|
|
438
438
|
name
|
|
439
|
-
}: PreloadFontProps): import("react
|
|
439
|
+
}: PreloadFontProps): import("react").JSX.Element;
|
|
440
440
|
displayName: string;
|
|
441
441
|
};
|
|
442
442
|
//#endregion
|
package/dist/toast.d.ts
CHANGED
package/dist/tooltip.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ declare const TooltipProvider: {
|
|
|
30
30
|
({
|
|
31
31
|
delayDuration,
|
|
32
32
|
...props
|
|
33
|
-
}: ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>): import("react
|
|
33
|
+
}: ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>): import("react").JSX.Element;
|
|
34
34
|
displayName: string;
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
@@ -55,7 +55,7 @@ declare const TooltipProvider: {
|
|
|
55
55
|
* </Tooltip.Root>
|
|
56
56
|
* ```
|
|
57
57
|
*/
|
|
58
|
-
declare function Root(props: ComponentProps<typeof TooltipPrimitive.Root>): import("react
|
|
58
|
+
declare function Root(props: ComponentProps<typeof TooltipPrimitive.Root>): import("react").JSX.Element;
|
|
59
59
|
declare namespace Root {
|
|
60
60
|
var displayName: string;
|
|
61
61
|
}
|
|
@@ -78,7 +78,7 @@ declare namespace Root {
|
|
|
78
78
|
* </Tooltip.Root>
|
|
79
79
|
* ```
|
|
80
80
|
*/
|
|
81
|
-
declare function Trigger(props: ComponentProps<typeof TooltipPrimitive.Trigger>): import("react
|
|
81
|
+
declare function Trigger(props: ComponentProps<typeof TooltipPrimitive.Trigger>): import("react").JSX.Element;
|
|
82
82
|
declare namespace Trigger {
|
|
83
83
|
var displayName: string;
|
|
84
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-matches-media-query-CMSxHR9n.js","names":[],"sources":["../src/hooks/use-matches-media-query.tsx"],"sourcesContent":["import { useCallback, useSyncExternalStore } from \"react\";\n\n/**\n * React hook that subscribes to a CSS media query and returns whether it\n * currently matches, re-rendering whenever the result changes.\n *\n * Uses `window.matchMedia` under the hood and `useSyncExternalStore` for\n * compatibility with React's concurrent rendering model. Returns `false`\n * on the server; during hydration React uses that server snapshot before\n * updating to the client media-query value.\n *\n * For common viewport breakpoint checks, prefer the more specific\n * {@link useBreakpoint} or {@link useIsBelowBreakpoint} hooks.\n *\n * @param query - A valid CSS media query string\n * (e.g. `\"(max-width: 768px)\"`, `\"(prefers-color-scheme: dark)\"`).\n * @returns `true` if the media query currently matches, otherwise `false`.\n *\n * @example\n * // Detect if the user prefers a dark color scheme\n * const prefersDark = useMatchesMediaQuery(\"(prefers-color-scheme: dark)\");\n *\n * return <div className={prefersDark ? \"dark\" : \"light\"}>Hello</div>;\n *\n * @example\n * // Show a different layout on portrait orientation\n * const isPortrait = useMatchesMediaQuery(\"(orientation: portrait)\");\n *\n * return isPortrait ? <PortraitLayout /> : <LandscapeLayout />;\n */\nexport function useMatchesMediaQuery(query: string) {\n\tconst subscribe = useCallback(\n\t\t(callback: () => void) => {\n\t\t\tconst matchMedia = window.matchMedia(query);\n\n\t\t\tmatchMedia.addEventListener(\"change\", callback);\n\t\t\treturn () => {\n\t\t\t\tmatchMedia.removeEventListener(\"change\", callback);\n\t\t\t};\n\t\t},\n\t\t[query],\n\t);\n\n\treturn useSyncExternalStore(\n\t\tsubscribe,\n\t\t() => {\n\t\t\treturn window.matchMedia(query).matches;\n\t\t},\n\t\t() => false,\n\t);\n}\n"],"mappings":"8DA8BA,SAAgB,EAAqB,EAAe,CAanD,OAAO,EAZW,EAChB,GAAyB,CACzB,IAAM,EAAa,OAAO,WAAW,CAAK,EAG1C,OADA,EAAW,iBAAiB,SAAU,CAAQ,MACjC,CACZ,EAAW,oBAAoB,SAAU,CAAQ,CAClD,CACD,EACA,CAAC,CAAK,CAIE,MAEA,OAAO,WAAW,CAAK,
|
|
1
|
+
{"version":3,"file":"use-matches-media-query-CMSxHR9n.js","names":[],"sources":["../src/hooks/use-matches-media-query.tsx"],"sourcesContent":["import { useCallback, useSyncExternalStore } from \"react\";\n\n/**\n * React hook that subscribes to a CSS media query and returns whether it\n * currently matches, re-rendering whenever the result changes.\n *\n * Uses `window.matchMedia` under the hood and `useSyncExternalStore` for\n * compatibility with React's concurrent rendering model. Returns `false`\n * on the server; during hydration React uses that server snapshot before\n * updating to the client media-query value.\n *\n * For common viewport breakpoint checks, prefer the more specific\n * {@link useBreakpoint} or {@link useIsBelowBreakpoint} hooks.\n *\n * @param query - A valid CSS media query string\n * (e.g. `\"(max-width: 768px)\"`, `\"(prefers-color-scheme: dark)\"`).\n * @returns `true` if the media query currently matches, otherwise `false`.\n *\n * @example\n * // Detect if the user prefers a dark color scheme\n * const prefersDark = useMatchesMediaQuery(\"(prefers-color-scheme: dark)\");\n *\n * return <div className={prefersDark ? \"dark\" : \"light\"}>Hello</div>;\n *\n * @example\n * // Show a different layout on portrait orientation\n * const isPortrait = useMatchesMediaQuery(\"(orientation: portrait)\");\n *\n * return isPortrait ? <PortraitLayout /> : <LandscapeLayout />;\n */\nexport function useMatchesMediaQuery(query: string) {\n\tconst subscribe = useCallback(\n\t\t(callback: () => void) => {\n\t\t\tconst matchMedia = window.matchMedia(query);\n\n\t\t\tmatchMedia.addEventListener(\"change\", callback);\n\t\t\treturn () => {\n\t\t\t\tmatchMedia.removeEventListener(\"change\", callback);\n\t\t\t};\n\t\t},\n\t\t[query],\n\t);\n\n\treturn useSyncExternalStore(\n\t\tsubscribe,\n\t\t() => {\n\t\t\treturn window.matchMedia(query).matches;\n\t\t},\n\t\t() => false,\n\t);\n}\n"],"mappings":"8DA8BA,SAAgB,EAAqB,EAAe,CAanD,OAAO,EAZW,EAChB,GAAyB,CACzB,IAAM,EAAa,OAAO,WAAW,CAAK,EAG1C,OADA,EAAW,iBAAiB,SAAU,CAAQ,MACjC,CACZ,EAAW,oBAAoB,SAAU,CAAQ,CAClD,CACD,EACA,CAAC,CAAK,CAIE,MAEA,OAAO,WAAW,CAAK,CAAC,CAAC,YAE3B,EACP,CACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-prefers-reduced-motion-CWIoFA6W.js","names":[],"sources":["../src/hooks/use-prefers-reduced-motion.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { canUseDOM } from \"../components/browser-only/browser-only.js\";\n\n/**\n * no-preference is the default value for the prefers-reduced-motion media query.\n * Users who have never fiddled with their a11y settings will still see animations\n * (no explicit opt-in required from a user's perspective)\n */\nconst query = \"(prefers-reduced-motion: no-preference)\";\n\n/**\n * Imperatively reads the current `prefers-reduced-motion` preference once at\n * the time of the call.\n *\n * Useful in event handlers, animation entrypoints, or plain functions where\n * a React hook cannot be called. Prefer {@link usePrefersReducedMotion}\n * inside components — it subscribes to live changes.\n *\n * @returns `true` when the user has opted out of animations or when called\n * outside a browser environment (SSR), `false` when motion is allowed.\n *\n * @remarks\n * The conservative SSR default of `true` matches\n * {@link usePrefersReducedMotion}: animations stay off until we can verify\n * the user's preference on the client.\n *\n * @example\n * // Skip a one-off entrance animation in a click handler\n * function onOpen() {\n * if (getPrefersReducedMotion()) {\n * element.style.opacity = \"1\";\n * return;\n * }\n * element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 200 });\n * }\n */\nexport function getPrefersReducedMotion(): boolean {\n\tif (!canUseDOM()) {\n\t\treturn true;\n\t}\n\treturn !window.matchMedia(query).matches;\n}\n\n/**\n * React hook that subscribes to the user's `prefers-reduced-motion` media\n * query and re-renders when it changes.\n *\n * Defaults to `true` (reduce motion) on the server and during the first\n * client render to avoid animating before hydration. The initial client\n * effect reads the *real* preference and updates state. The underlying\n * media query used is `(prefers-reduced-motion: no-preference)` inverted —\n * \"if the system hasn't opted out, animations are allowed.\"\n *\n * @returns `true` when the user prefers reduced motion (animations should be\n * shortened or skipped), `false` when full motion is acceptable.\n *\n * @remarks\n * If you need to support very old browsers that lack\n * `MediaQueryList.addEventListener`, consider falling back to\n * `addListener` / `removeListener`.\n *\n * @example\n * // Conditionally shorten or skip transitions\n * const prefersReducedMotion = usePrefersReducedMotion();\n * const duration = prefersReducedMotion ? 0 : 200;\n *\n * return <Modal transitionDuration={duration} />;\n *\n * @example\n * // Disable an autoplaying carousel when motion is reduced\n * const prefersReducedMotion = usePrefersReducedMotion();\n *\n * return <Carousel autoplay={!prefersReducedMotion} />;\n */\nexport function usePrefersReducedMotion(): boolean {\n\t// Default to no animations on SSR/first paint; update on mount with the real value.\n\tconst [prefersReducedMotion, setPrefersReducedMotion] = useState(true);\n\n\tuseEffect(() => {\n\t\tconst mediaQueryList = window.matchMedia(query);\n\n\t\t// set the _real_ initial value now that we're on the client\n\t\tsetPrefersReducedMotion(getPrefersReducedMotion());\n\n\t\t// register for updates\n\t\tfunction listener(event: MediaQueryListEvent) {\n\t\t\tsetPrefersReducedMotion(!event.matches);\n\t\t}\n\n\t\tmediaQueryList.addEventListener(\"change\", listener);\n\n\t\treturn () => {\n\t\t\tmediaQueryList.removeEventListener(\"change\", listener);\n\t\t};\n\t}, []);\n\n\treturn prefersReducedMotion;\n}\n"],"mappings":"+FAQA,MAAM,EAAQ,0CA4Bd,SAAgB,GAAmC,CAIlD,OAHK,EAAU,EAGR,CAAC,OAAO,WAAW,CAAK,
|
|
1
|
+
{"version":3,"file":"use-prefers-reduced-motion-CWIoFA6W.js","names":[],"sources":["../src/hooks/use-prefers-reduced-motion.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { canUseDOM } from \"../components/browser-only/browser-only.js\";\n\n/**\n * no-preference is the default value for the prefers-reduced-motion media query.\n * Users who have never fiddled with their a11y settings will still see animations\n * (no explicit opt-in required from a user's perspective)\n */\nconst query = \"(prefers-reduced-motion: no-preference)\";\n\n/**\n * Imperatively reads the current `prefers-reduced-motion` preference once at\n * the time of the call.\n *\n * Useful in event handlers, animation entrypoints, or plain functions where\n * a React hook cannot be called. Prefer {@link usePrefersReducedMotion}\n * inside components — it subscribes to live changes.\n *\n * @returns `true` when the user has opted out of animations or when called\n * outside a browser environment (SSR), `false` when motion is allowed.\n *\n * @remarks\n * The conservative SSR default of `true` matches\n * {@link usePrefersReducedMotion}: animations stay off until we can verify\n * the user's preference on the client.\n *\n * @example\n * // Skip a one-off entrance animation in a click handler\n * function onOpen() {\n * if (getPrefersReducedMotion()) {\n * element.style.opacity = \"1\";\n * return;\n * }\n * element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 200 });\n * }\n */\nexport function getPrefersReducedMotion(): boolean {\n\tif (!canUseDOM()) {\n\t\treturn true;\n\t}\n\treturn !window.matchMedia(query).matches;\n}\n\n/**\n * React hook that subscribes to the user's `prefers-reduced-motion` media\n * query and re-renders when it changes.\n *\n * Defaults to `true` (reduce motion) on the server and during the first\n * client render to avoid animating before hydration. The initial client\n * effect reads the *real* preference and updates state. The underlying\n * media query used is `(prefers-reduced-motion: no-preference)` inverted —\n * \"if the system hasn't opted out, animations are allowed.\"\n *\n * @returns `true` when the user prefers reduced motion (animations should be\n * shortened or skipped), `false` when full motion is acceptable.\n *\n * @remarks\n * If you need to support very old browsers that lack\n * `MediaQueryList.addEventListener`, consider falling back to\n * `addListener` / `removeListener`.\n *\n * @example\n * // Conditionally shorten or skip transitions\n * const prefersReducedMotion = usePrefersReducedMotion();\n * const duration = prefersReducedMotion ? 0 : 200;\n *\n * return <Modal transitionDuration={duration} />;\n *\n * @example\n * // Disable an autoplaying carousel when motion is reduced\n * const prefersReducedMotion = usePrefersReducedMotion();\n *\n * return <Carousel autoplay={!prefersReducedMotion} />;\n */\nexport function usePrefersReducedMotion(): boolean {\n\t// Default to no animations on SSR/first paint; update on mount with the real value.\n\tconst [prefersReducedMotion, setPrefersReducedMotion] = useState(true);\n\n\tuseEffect(() => {\n\t\tconst mediaQueryList = window.matchMedia(query);\n\n\t\t// set the _real_ initial value now that we're on the client\n\t\tsetPrefersReducedMotion(getPrefersReducedMotion());\n\n\t\t// register for updates\n\t\tfunction listener(event: MediaQueryListEvent) {\n\t\t\tsetPrefersReducedMotion(!event.matches);\n\t\t}\n\n\t\tmediaQueryList.addEventListener(\"change\", listener);\n\n\t\treturn () => {\n\t\t\tmediaQueryList.removeEventListener(\"change\", listener);\n\t\t};\n\t}, []);\n\n\treturn prefersReducedMotion;\n}\n"],"mappings":"+FAQA,MAAM,EAAQ,0CA4Bd,SAAgB,GAAmC,CAIlD,OAHK,EAAU,EAGR,CAAC,OAAO,WAAW,CAAK,CAAC,CAAC,QAFzB,EAGT,CAiCA,SAAgB,GAAmC,CAElD,GAAM,CAAC,EAAsB,GAA2B,EAAS,EAAI,EAoBrE,OAlBA,MAAgB,CACf,IAAM,EAAiB,OAAO,WAAW,CAAK,EAG9C,EAAwB,EAAwB,CAAC,EAGjD,SAAS,EAAS,EAA4B,CAC7C,EAAwB,CAAC,EAAM,OAAO,CACvC,CAIA,OAFA,EAAe,iBAAiB,SAAU,CAAQ,MAErC,CACZ,EAAe,oBAAoB,SAAU,CAAQ,CACtD,CACD,EAAG,CAAC,CAAC,EAEE,CACR"}
|
package/dist/well.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { t as WithAsChild } from "./as-child-uN_018tj.js";
|
|
2
|
+
import { ComponentProps } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/components/well/well.d.ts
|
|
5
|
+
type WellProps = ComponentProps<"div"> & WithAsChild;
|
|
6
|
+
/**
|
|
7
|
+
* A recessed, inset container used to visually group and de-emphasize content
|
|
8
|
+
* relative to the surrounding surface, such as code samples, supplementary
|
|
9
|
+
* notes, or read-only summaries.
|
|
10
|
+
*
|
|
11
|
+
* Renders a `<div>` by default. Pass `asChild` to render as a different element
|
|
12
|
+
* or your own component, forwarding all class names, `data-*` attributes, and
|
|
13
|
+
* the ref onto that child.
|
|
14
|
+
*
|
|
15
|
+
* @see https://mantle.ngrok.com/components/well
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <Well>
|
|
20
|
+
* <p>Eu ad sint laborum nostrud ullamco esse.</p>
|
|
21
|
+
* </Well>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <Well asChild>
|
|
27
|
+
* <section aria-label="Summary">…</section>
|
|
28
|
+
* </Well>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare const Well: import("react").ForwardRefExoticComponent<Omit<WellProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { Well };
|
|
34
|
+
//# sourceMappingURL=well.d.ts.map
|
package/dist/well.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{t as e}from"./cx-CBSnSC36.js";import{t}from"./slot-CV5fmqFr.js";import{forwardRef as n}from"react";import{jsx as r}from"react/jsx-runtime";const i=n(({asChild:n=!1,className:i,...a},o)=>r(n?t:`div`,{ref:o,"data-slot":`well`,className:e(`border-card bg-base relative rounded-md border shadow-inner`,i),...a}));i.displayName=`Well`;export{i as Well};
|
|
2
|
+
//# sourceMappingURL=well.js.map
|
package/dist/well.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"well.js","names":[],"sources":["../src/components/well/well.tsx"],"sourcesContent":["import type { ComponentProps, ComponentRef } from \"react\";\nimport { forwardRef } from \"react\";\nimport type { WithAsChild } from \"../../types/index.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { Slot } from \"../slot/index.js\";\n\ntype WellProps = ComponentProps<\"div\"> & WithAsChild;\n\n/**\n * A recessed, inset container used to visually group and de-emphasize content\n * relative to the surrounding surface, such as code samples, supplementary\n * notes, or read-only summaries.\n *\n * Renders a `<div>` by default. Pass `asChild` to render as a different element\n * or your own component, forwarding all class names, `data-*` attributes, and\n * the ref onto that child.\n *\n * @see https://mantle.ngrok.com/components/well\n *\n * @example\n * ```tsx\n * <Well>\n * <p>Eu ad sint laborum nostrud ullamco esse.</p>\n * </Well>\n * ```\n *\n * @example\n * ```tsx\n * <Well asChild>\n * <section aria-label=\"Summary\">…</section>\n * </Well>\n * ```\n */\nconst Well = forwardRef<ComponentRef<\"div\">, WellProps>(\n\t({ asChild = false, className, ...props }, ref) => {\n\t\tconst Comp = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<Comp\n\t\t\t\tref={ref}\n\t\t\t\tdata-slot=\"well\"\n\t\t\t\tclassName={cx(\"border-card bg-base relative rounded-md border shadow-inner\", className)}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nWell.displayName = \"Well\";\n\nexport {\n\t//,\n\tWell,\n};\n"],"mappings":"kJAiCA,MAAM,EAAO,GACX,CAAE,UAAU,GAAO,YAAW,GAAG,GAAS,IAIzC,EAHY,EAAU,EAAO,MAG7B,CACM,MACL,YAAU,OACV,UAAW,EAAG,8DAA+D,CAAS,EACtF,GAAI,CACJ,CAAA,CAGJ,EACA,EAAK,YAAc"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ngrok/mantle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"description": "mantle is ngrok's UI library and design system.",
|
|
5
5
|
"homepage": "https://mantle.ngrok.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -240,6 +240,11 @@
|
|
|
240
240
|
"types": "./dist/progress.d.ts",
|
|
241
241
|
"import": "./dist/progress.js"
|
|
242
242
|
},
|
|
243
|
+
"./qr-code": {
|
|
244
|
+
"@ngrok/src-live-types": "./src/components/qr-code/index.ts",
|
|
245
|
+
"types": "./dist/qr-code.d.ts",
|
|
246
|
+
"import": "./dist/qr-code.js"
|
|
247
|
+
},
|
|
243
248
|
"./radio-group": {
|
|
244
249
|
"@ngrok/src-live-types": "./src/components/radio-group/index.ts",
|
|
245
250
|
"types": "./dist/radio-group.d.ts",
|
|
@@ -334,23 +339,28 @@
|
|
|
334
339
|
"@ngrok/src-live-types": "./src/utils/index.ts",
|
|
335
340
|
"types": "./dist/utils.d.ts",
|
|
336
341
|
"import": "./dist/utils.js"
|
|
342
|
+
},
|
|
343
|
+
"./well": {
|
|
344
|
+
"@ngrok/src-live-types": "./src/components/well/index.ts",
|
|
345
|
+
"types": "./dist/well.d.ts",
|
|
346
|
+
"import": "./dist/well.js"
|
|
337
347
|
}
|
|
338
348
|
},
|
|
339
349
|
"dependencies": {
|
|
340
|
-
"@ariakit/react": "0.4.
|
|
350
|
+
"@ariakit/react": "0.4.29",
|
|
341
351
|
"@headlessui/react": "2.2.10",
|
|
342
|
-
"@radix-ui/react-accordion": "1.2.
|
|
343
|
-
"@radix-ui/react-dialog": "1.1.
|
|
344
|
-
"@radix-ui/react-dropdown-menu": "2.1.
|
|
345
|
-
"@radix-ui/react-hover-card": "1.1.
|
|
346
|
-
"@radix-ui/react-popover": "1.1.
|
|
347
|
-
"@radix-ui/react-progress": "1.1.
|
|
348
|
-
"@radix-ui/react-select": "2.
|
|
349
|
-
"@radix-ui/react-slider": "1.
|
|
350
|
-
"@radix-ui/react-slot": "1.2.
|
|
351
|
-
"@radix-ui/react-switch": "1.
|
|
352
|
-
"@radix-ui/react-tabs": "1.1.
|
|
353
|
-
"@radix-ui/react-tooltip": "1.2.
|
|
352
|
+
"@radix-ui/react-accordion": "1.2.13",
|
|
353
|
+
"@radix-ui/react-dialog": "1.1.16",
|
|
354
|
+
"@radix-ui/react-dropdown-menu": "2.1.17",
|
|
355
|
+
"@radix-ui/react-hover-card": "1.1.16",
|
|
356
|
+
"@radix-ui/react-popover": "1.1.16",
|
|
357
|
+
"@radix-ui/react-progress": "1.1.9",
|
|
358
|
+
"@radix-ui/react-select": "2.3.0",
|
|
359
|
+
"@radix-ui/react-slider": "1.4.0",
|
|
360
|
+
"@radix-ui/react-slot": "1.2.5",
|
|
361
|
+
"@radix-ui/react-switch": "1.3.0",
|
|
362
|
+
"@radix-ui/react-tabs": "1.1.14",
|
|
363
|
+
"@radix-ui/react-tooltip": "1.2.9",
|
|
354
364
|
"@tanstack/react-table": "8.21.3",
|
|
355
365
|
"@uidotdev/usehooks": "2.4.1",
|
|
356
366
|
"class-variance-authority": "0.7.1",
|
|
@@ -361,7 +371,8 @@
|
|
|
361
371
|
"sonner": "2.0.7",
|
|
362
372
|
"tailwind-merge": "3.6.0",
|
|
363
373
|
"tiny-invariant": "1.3.3",
|
|
364
|
-
"tw-animate-css": "1.4.0"
|
|
374
|
+
"tw-animate-css": "1.4.0",
|
|
375
|
+
"uqr": "0.1.3"
|
|
365
376
|
},
|
|
366
377
|
"devDependencies": {
|
|
367
378
|
"@phosphor-icons/react": "2.1.10",
|
|
@@ -369,24 +380,24 @@
|
|
|
369
380
|
"@testing-library/jest-dom": "6.9.1",
|
|
370
381
|
"@testing-library/react": "16.3.2",
|
|
371
382
|
"@testing-library/user-event": "14.6.1",
|
|
372
|
-
"@tsdown/css": "0.22.
|
|
373
|
-
"@types/react": "19.2.
|
|
383
|
+
"@tsdown/css": "0.22.2",
|
|
384
|
+
"@types/react": "19.2.17",
|
|
374
385
|
"@types/react-dom": "19.2.3",
|
|
375
|
-
"@vitest/browser-playwright": "4.1.
|
|
386
|
+
"@vitest/browser-playwright": "4.1.8",
|
|
376
387
|
"browserslist": "4.28.2",
|
|
377
|
-
"date-fns": "4.
|
|
378
|
-
"happy-dom": "20.
|
|
388
|
+
"date-fns": "4.4.0",
|
|
389
|
+
"happy-dom": "20.10.2",
|
|
379
390
|
"playwright": "1.60.0",
|
|
380
|
-
"react": "19.2.
|
|
381
|
-
"react-dom": "19.2.
|
|
391
|
+
"react": "19.2.7",
|
|
392
|
+
"react-dom": "19.2.7",
|
|
382
393
|
"tailwindcss": "4.3.0",
|
|
383
|
-
"tsdown": "0.22.
|
|
394
|
+
"tsdown": "0.22.2",
|
|
384
395
|
"typescript": "6.0.3",
|
|
385
396
|
"@cfg/tsconfig": "1.0.0"
|
|
386
397
|
},
|
|
387
398
|
"peerDependencies": {
|
|
388
399
|
"@phosphor-icons/react": "^2.1.10",
|
|
389
|
-
"date-fns": "^4.
|
|
400
|
+
"date-fns": "^4.4.0",
|
|
390
401
|
"react": "^18 || ^19",
|
|
391
402
|
"react-dom": "^18 || ^19",
|
|
392
403
|
"tailwindcss": "^4.3.0"
|