{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glass-provider",
  "title": "Glass Provider",
  "description": "Follows the device's appearance and accessibility settings and lets your app override them.",
  "files": [
    {
      "path": "registry/glassyui-native/glass-provider.tsx",
      "content": "import {\n  createContext,\n  useCallback,\n  useContext,\n  useMemo,\n  useState,\n  useSyncExternalStore,\n  type ReactNode,\n} from 'react';\nimport { useColorScheme } from 'react-native';\nimport {\n  readSystemAccessibility,\n  subscribeToSystemAccessibility,\n  type GlassyUiPreferences,\n  type GlassyUiTheme,\n} from './lib/preferences';\nimport { GLASS_COLORS } from './lib/tokens';\n\ntype PreferenceState = Required<GlassyUiPreferences>;\n\ntype PreferenceContextValue = {\n  preferences: PreferenceState;\n  setPreferences: (prefs: Partial<GlassyUiPreferences>) => void;\n};\n\nconst PreferencesContext = createContext<PreferenceContextValue | null>(null);\n\nfunction ignorePreferences() {}\n\n/** The device's appearance and accessibility settings, as glass preferences. */\nfunction useSystemPreferences(): PreferenceState {\n  const scheme = useColorScheme();\n\n  const accessibility = useSyncExternalStore(\n    subscribeToSystemAccessibility,\n    readSystemAccessibility,\n    readSystemAccessibility,\n  );\n\n  return useMemo(\n    () => ({\n      theme: scheme === 'light' ? 'light' : 'dark',\n      mode: 'clear',\n      reduceMotion: accessibility.reduceMotion,\n      reduceTransparency: accessibility.reduceTransparency,\n    }),\n    [accessibility, scheme],\n  );\n}\n\n/**\n * Holds the glass preferences every component follows. It starts from the device's appearance,\n * Reduce Motion and Reduce Transparency settings; `defaultTheme` overrides the appearance until\n * the app sets a theme. Choices made with `setPreferences` last while the provider is mounted;\n * persist them in your app's own storage if they should survive a restart.\n */\nexport function GlassProvider({\n  children,\n  defaultTheme,\n}: {\n  children: ReactNode;\n  defaultTheme?: GlassyUiTheme;\n}) {\n  const system = useSystemPreferences();\n  const [explicit, setExplicit] = useState<GlassyUiPreferences>({});\n\n  const preferences = useMemo(() => {\n    const merged: PreferenceState = { ...system, ...explicit };\n\n    if (defaultTheme && explicit.theme === undefined) merged.theme = defaultTheme;\n\n    return merged;\n  }, [defaultTheme, explicit, system]);\n\n  const setPreferences = useCallback((next: Partial<GlassyUiPreferences>) => {\n    setExplicit((previous) => ({ ...previous, ...next }));\n  }, []);\n\n  const value = useMemo(() => ({ preferences, setPreferences }), [preferences, setPreferences]);\n\n  return <PreferencesContext.Provider value={value}>{children}</PreferencesContext.Provider>;\n}\n\nexport const PreferencesProvider = GlassProvider;\n\n/** The provider's preferences, or the device's own settings when no provider is mounted. */\nexport function useOptionalPreferences(): PreferenceContextValue {\n  const context = useContext(PreferencesContext);\n  const system = useSystemPreferences();\n\n  return context ?? { preferences: system, setPreferences: ignorePreferences };\n}\n\nexport function usePreferences() {\n  const context = useContext(PreferencesContext);\n\n  if (!context) {\n    throw new Error('usePreferences must be used within GlassProvider');\n  }\n\n  return context;\n}\n\n/** The colours for the current theme, with the preferences they came from. */\nexport function useGlassTheme() {\n  const { preferences } = useOptionalPreferences();\n\n  return { colors: GLASS_COLORS[preferences.theme], preferences };\n}\n",
      "type": "registry:ui",
      "target": "components/glassyui/glass-provider.tsx"
    },
    {
      "path": "registry/glassyui-native/lib/preferences.ts",
      "content": "import { AccessibilityInfo, Platform } from 'react-native';\n\nexport type GlassyUiMode = 'clear' | 'tinted';\n\nexport type GlassyUiTheme = 'light' | 'dark';\n\nexport interface GlassyUiPreferences {\n  theme?: GlassyUiTheme;\n  /** Clear glass shows more of what lies behind it; tinted glass holds a denser frost. */\n  mode?: GlassyUiMode;\n  reduceTransparency?: boolean;\n  reduceMotion?: boolean;\n}\n\nexport type SystemAccessibility = {\n  reduceMotion: boolean;\n  reduceTransparency: boolean;\n};\n\ntype Subscription = { remove: () => void };\n\n// The system's accessibility settings, read once and shared by every glass component.\nlet snapshot: SystemAccessibility = { reduceMotion: false, reduceTransparency: false };\n\nconst listeners = new Set<() => void>();\n\nlet subscriptions: Subscription[] = [];\n\nfunction update(next: Partial<SystemAccessibility>) {\n  const merged = { ...snapshot, ...next };\n\n  if (\n    merged.reduceMotion === snapshot.reduceMotion &&\n    merged.reduceTransparency === snapshot.reduceTransparency\n  ) {\n    return;\n  }\n\n  snapshot = merged;\n  listeners.forEach((listener) => listener());\n}\n\nfunction warnReadFailure(cause: Error) {\n  console.warn('Glassy UI could not read an accessibility setting', cause);\n}\n\nfunction startListening() {\n  AccessibilityInfo.isReduceMotionEnabled()\n    .then((reduceMotion) => update({ reduceMotion }))\n    .catch(warnReadFailure);\n\n  subscriptions.push(\n    AccessibilityInfo.addEventListener('reduceMotionChanged', (reduceMotion) =>\n      update({ reduceMotion }),\n    ),\n  );\n\n  if (Platform.OS === 'ios') {\n    AccessibilityInfo.isReduceTransparencyEnabled()\n      .then((reduceTransparency) => update({ reduceTransparency }))\n      .catch(warnReadFailure);\n\n    subscriptions.push(\n      AccessibilityInfo.addEventListener('reduceTransparencyChanged', (reduceTransparency) =>\n        update({ reduceTransparency }),\n      ),\n    );\n  }\n\n  // React Native for Web has no reduce-transparency event; the browser reports it directly.\n  if (Platform.OS === 'web' && window.matchMedia) {\n    const query = window.matchMedia('(prefers-reduced-transparency: reduce)');\n    const handleChange = () => update({ reduceTransparency: query.matches });\n\n    update({ reduceTransparency: query.matches });\n    query.addEventListener('change', handleChange);\n    subscriptions.push({ remove: () => query.removeEventListener('change', handleChange) });\n  }\n}\n\nexport function subscribeToSystemAccessibility(listener: () => void) {\n  listeners.add(listener);\n\n  if (listeners.size === 1) startListening();\n\n  return () => {\n    listeners.delete(listener);\n\n    if (listeners.size > 0) return;\n\n    subscriptions.forEach((subscription) => subscription.remove());\n    subscriptions = [];\n  };\n}\n\nexport function readSystemAccessibility() {\n  return snapshot;\n}\n",
      "type": "registry:lib",
      "target": "components/glassyui/lib/preferences.ts"
    },
    {
      "path": "registry/glassyui-native/lib/tokens.ts",
      "content": "import { Platform } from 'react-native';\nimport type { GlassyUiTheme } from './preferences';\n\nexport type GlassTone = 'neutral' | 'blue' | 'green' | 'orange' | 'red' | 'purple';\n\nexport type GlassColors = {\n  /** Selection, active controls and the prominent button. */\n  accent: string;\n  accentForeground: string;\n  border: string;\n  /** The resting fill of fields and unselected controls. */\n  controlSurface: string;\n  focusRing: string;\n  foreground: string;\n  /** Extra frost over blurred glass, for clear and tinted glass. */\n  glassFill: Record<'clear' | 'tinted', string>;\n  invalid: string;\n  knob: string;\n  mutedForeground: string;\n  /** Where glass cannot be drawn: Reduce Transparency. */\n  opaqueSurface: string;\n  /** Floating layers that must hold 4.5:1 text over any backdrop. */\n  overlaySurface: string;\n  /** The hairline round a glass edge, and the light it catches along the top. */\n  rim: string;\n  rimHighlight: string;\n  scrim: string;\n  shadow: string;\n  shadowRaised: string;\n  tones: Record<GlassTone, string>;\n  track: string;\n};\n\n/** Glass stays neutral. Blue marks actions and selection; the tones mark status. */\nexport const GLASS_COLORS = {\n  dark: {\n    accent: '#0067d9',\n    accentForeground: '#ffffff',\n    border: 'rgba(245, 245, 247, 0.22)',\n    controlSurface: 'rgba(245, 245, 247, 0.08)',\n    focusRing: '#b8b8be',\n    foreground: '#f5f5f7',\n    glassFill: { clear: 'rgba(255, 255, 255, 0.04)', tinted: 'rgba(28, 28, 30, 0.45)' },\n    invalid: '#ff7a8e',\n    knob: '#ffffff',\n    mutedForeground: '#d1d1d6',\n    opaqueSurface: '#2c2c2e',\n    overlaySurface: 'rgba(44, 44, 46, 0.78)',\n    rim: 'rgba(255, 255, 255, 0.16)',\n    rimHighlight: 'rgba(255, 255, 255, 0.34)',\n    scrim: 'rgba(0, 0, 0, 0.45)',\n    shadow: '0px 1px 2px rgba(0, 0, 0, 0.18), 0px 8px 24px -4px rgba(0, 0, 0, 0.28)',\n    shadowRaised: '0px 2px 6px rgba(0, 0, 0, 0.2), 0px 30px 60px -18px rgba(0, 0, 0, 0.55)',\n    tones: {\n      neutral: '#8e8e93',\n      blue: '#78b7ff',\n      green: '#33d69f',\n      orange: '#ffbd38',\n      red: '#ff5c7a',\n      purple: '#9b80ff',\n    },\n    track: 'rgba(118, 118, 128, 0.36)',\n  },\n  light: {\n    accent: '#0067d9',\n    accentForeground: '#ffffff',\n    border: 'rgba(0, 0, 0, 0.14)',\n    controlSurface: 'rgba(255, 255, 255, 0.72)',\n    focusRing: '#66666a',\n    foreground: '#222224',\n    glassFill: { clear: 'rgba(255, 255, 255, 0.18)', tinted: 'rgba(255, 255, 255, 0.55)' },\n    invalid: '#c4153f',\n    knob: '#ffffff',\n    mutedForeground: '#505055',\n    opaqueSurface: '#ffffff',\n    overlaySurface: 'rgba(255, 255, 255, 0.82)',\n    rim: 'rgba(255, 255, 255, 0.55)',\n    rimHighlight: 'rgba(255, 255, 255, 0.9)',\n    scrim: 'rgba(0, 0, 0, 0.16)',\n    shadow: '0px 1px 2px rgba(0, 0, 0, 0.06), 0px 8px 24px -6px rgba(0, 0, 0, 0.14)',\n    shadowRaised: '0px 2px 6px rgba(0, 0, 0, 0.06), 0px 28px 56px -18px rgba(0, 0, 0, 0.26)',\n    tones: {\n      neutral: '#8e8e93',\n      blue: '#005ec6',\n      green: '#14a874',\n      orange: '#e8960c',\n      red: '#e8345a',\n      purple: '#6a4cf0',\n    },\n    track: 'rgba(118, 118, 128, 0.2)',\n  },\n} as const satisfies Record<GlassyUiTheme, GlassColors>;\n\nexport const GLASS_RADIUS = { md: 16, lg: 24, capsule: 999 } as const;\n\n/** Arrivals ease out; physical responses spring and settle. Exits run shorter than arrivals. */\nexport const GLASS_MOTION = {\n  duration: 200,\n  /** React Native for Web has no native driver and warns when asked for one. */\n  nativeDriver: Platform.OS !== 'web',\n  spring: { damping: 18, mass: 1, stiffness: 260 },\n} as const;\n",
      "type": "registry:lib",
      "target": "components/glassyui/lib/tokens.ts"
    },
    {
      "path": "registry/glassyui-native/lib/use-controllable-state.ts",
      "content": "import { useCallback, useState } from 'react';\n\n/**\n * State a component owns until its parent passes `value`; then the parent owns it. Either way\n * `onChange` hears every change the component makes.\n */\nexport function useControllableState<T>(\n  value: T | undefined,\n  defaultValue: T,\n  onChange?: (next: T) => void,\n) {\n  const [uncontrolled, setUncontrolled] = useState(defaultValue);\n  const controlled = value !== undefined;\n  const current = controlled ? value : uncontrolled;\n\n  const setValue = useCallback(\n    (next: T) => {\n      if (!controlled) setUncontrolled(next);\n\n      onChange?.(next);\n    },\n    [controlled, onChange],\n  );\n\n  return [current, setValue] as const;\n}\n",
      "type": "registry:lib",
      "target": "components/glassyui/lib/use-controllable-state.ts"
    }
  ],
  "type": "registry:ui"
}