react-os-shell 4.67.0 → 4.74.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{Browser-NBYPPENQ.js → Browser-EEHVHZNX.js} +6 -6
- package/dist/{Browser-NBYPPENQ.js.map → Browser-EEHVHZNX.js.map} +1 -1
- package/dist/{Calculator-UNAVT7IE.js → Calculator-5YCRHOVF.js} +5 -5
- package/dist/{Calculator-UNAVT7IE.js.map → Calculator-5YCRHOVF.js.map} +1 -1
- package/dist/{CurrencyConverter-KURXJHLK.js → CurrencyConverter-6G22EWEM.js} +5 -5
- package/dist/{CurrencyConverter-KURXJHLK.js.map → CurrencyConverter-6G22EWEM.js.map} +1 -1
- package/dist/{Documents-7CUBCETC.js → Documents-HUSQFS22.js} +7 -7
- package/dist/{Documents-7CUBCETC.js.map → Documents-HUSQFS22.js.map} +1 -1
- package/dist/{Files-UXEDRQF4.js → Files-FPUXAOCU.js} +8 -8
- package/dist/{Files-UXEDRQF4.js.map → Files-FPUXAOCU.js.map} +1 -1
- package/dist/{Notepad-ON4T45UP.js → Notepad-UDM4SUTH.js} +7 -7
- package/dist/{Notepad-ON4T45UP.js.map → Notepad-UDM4SUTH.js.map} +1 -1
- package/dist/{PomodoroTimer-J7V5VXR4.js → PomodoroTimer-CLTR5VHX.js} +7 -7
- package/dist/{PomodoroTimer-J7V5VXR4.js.map → PomodoroTimer-CLTR5VHX.js.map} +1 -1
- package/dist/{Preview-QINOPDT2.js → Preview-LGSP4FIP.js} +8 -8
- package/dist/{Preview-QINOPDT2.js.map → Preview-LGSP4FIP.js.map} +1 -1
- package/dist/{Spreadsheet-OFF3J4HN.js → Spreadsheet-BPG6FQEZ.js} +6 -6
- package/dist/{Spreadsheet-OFF3J4HN.js.map → Spreadsheet-BPG6FQEZ.js.map} +1 -1
- package/dist/{Stock-BSU7S7MW.js → Stock-CGE7KOVY.js} +5 -5
- package/dist/{Stock-BSU7S7MW.js.map → Stock-CGE7KOVY.js.map} +1 -1
- package/dist/{Weather-BKNRVMG6.js → Weather-MJGURKMH.js} +5 -5
- package/dist/{Weather-BKNRVMG6.js.map → Weather-MJGURKMH.js.map} +1 -1
- package/dist/{WorldClock-VMTD2QWD.js → WorldClock-35LKCPTU.js} +5 -5
- package/dist/{WorldClock-VMTD2QWD.js.map → WorldClock-35LKCPTU.js.map} +1 -1
- package/dist/apps/index.js +16 -16
- package/dist/{chunk-HAFM6ZQD.js → chunk-2ISCAT7X.js} +4 -4
- package/dist/{chunk-HAFM6ZQD.js.map → chunk-2ISCAT7X.js.map} +1 -1
- package/dist/{chunk-TK6HI2U4.js → chunk-2VM2FRZJ.js} +42 -11
- package/dist/chunk-2VM2FRZJ.js.map +1 -0
- package/dist/{chunk-ECE3B3FZ.js → chunk-AXWC3332.js} +3 -3
- package/dist/{chunk-ECE3B3FZ.js.map → chunk-AXWC3332.js.map} +1 -1
- package/dist/{chunk-ZCDFNZQS.js → chunk-BLLINYDD.js} +97 -5
- package/dist/chunk-BLLINYDD.js.map +1 -0
- package/dist/{chunk-GRSVL6WX.js → chunk-EZDTBK2J.js} +139 -40
- package/dist/chunk-EZDTBK2J.js.map +1 -0
- package/dist/chunk-RCWM2YNY.js +7 -0
- package/dist/{chunk-K35QFOJF.js.map → chunk-RCWM2YNY.js.map} +1 -1
- package/dist/{chunk-B3IYV7JN.js → chunk-S6K37RLY.js} +196 -73
- package/dist/chunk-S6K37RLY.js.map +1 -0
- package/dist/{chunk-IOCOH2QQ.js → chunk-TT2WNTDS.js} +4 -4
- package/dist/{chunk-IOCOH2QQ.js.map → chunk-TT2WNTDS.js.map} +1 -1
- package/dist/{chunk-3OCL3DB4.js → chunk-TT56U4GY.js} +3 -3
- package/dist/{chunk-3OCL3DB4.js.map → chunk-TT56U4GY.js.map} +1 -1
- package/dist/{chunk-E2N5WEHI.js → chunk-ZASBNWYL.js} +3 -3
- package/dist/{chunk-E2N5WEHI.js.map → chunk-ZASBNWYL.js.map} +1 -1
- package/dist/{index-DM-d12A7.d.ts → index-DVekeidu.d.ts} +171 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +119 -33
- package/dist/index.js.map +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-B3IYV7JN.js.map +0 -1
- package/dist/chunk-GRSVL6WX.js.map +0 -1
- package/dist/chunk-K35QFOJF.js +0 -7
- package/dist/chunk-TK6HI2U4.js.map +0 -1
- package/dist/chunk-ZCDFNZQS.js.map +0 -1
package/dist/chunk-K35QFOJF.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/toast.ts"],"names":[],"mappings":"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","file":"chunk-TK6HI2U4.js","sourcesContent":["/**\n * Two presentations:\n *\n * 1. toast.success / error / info — brief operation feedback, top-center,\n * auto-dismiss (success/error ~3s, info ~4.5s). The everyday \"what just\n * happened\" feedback — most messages want this.\n * 2. toast.notify — system notification, top-right card, stays 10s, dismissible.\n * For an alert worth lingering on; reach for it deliberately, not by default.\n *\n * (Historically `toast.info` rendered the top-right notification card; it now\n * renders a brief toast — that persistent card moved to `toast.notify`.)\n */\n\nconst TOAST_CONTAINER_ID = 'toast-container';\nconst TOAST_BOTTOM_CONTAINER_ID = 'toast-container-bottom';\nconst NOTIF_CONTAINER_ID = 'notif-container';\nconst FADE_MS = 300;\n\nexport type ToastPlacement = 'top' | 'bottom';\n\nexport interface ToastOptions {\n /** Milliseconds before auto-dismiss. Ignored when `sticky`. */\n duration?: number;\n /**\n * Stay until dismissed. For a message the user must actually act on — the\n * kind that must not scroll past while they are looking at a customer.\n * Implies tap-to-dismiss is the only way out, which it already is.\n */\n sticky?: boolean;\n /**\n * `bottom` centres toasts along the bottom edge. On a till the top of the\n * screen is the cart total and the bottom is nearest the hand.\n */\n placement?: ToastPlacement;\n /**\n * Drop a message identical to one already showing, and RESTART its timer\n * rather than stacking a second copy. A failing poll otherwise writes the\n * same sentence a dozen times. The restart matters: the user asked twice, so\n * the message should persist, not expire on the first one's schedule.\n */\n dedupe?: boolean;\n}\n\n/**\n * Defaults for every subsequent toast, set once at app startup — the same\n * \"consumer wires this once\" shape as `setShellApiClient`/`setShellNavIcons`.\n * A till calls `toast.configure({ placement: 'bottom', duration: 6000,\n * dedupe: true })` instead of repeating those options at 40 call sites.\n * Per-call options still win.\n */\nlet defaults: ToastOptions = {};\n\n/** Live toasts by dedupe key, so a repeat can find and refresh its own. */\nconst live = new Map<string, { el: HTMLElement; restart: () => void }>();\n\nfunction getOrCreate(id: string, className: string): HTMLElement {\n let el = document.getElementById(id);\n if (!el) {\n el = document.createElement('div');\n el.id = id;\n el.className = className;\n document.body.appendChild(el);\n }\n return el;\n}\n\nfunction getMenuOpacity(): number {\n try {\n const val = getComputedStyle(document.documentElement).getPropertyValue('--menu-opacity')?.trim();\n if (val) return parseFloat(val);\n } catch {}\n return 0.95;\n}\n\nfunction glassBackground(o: number): string {\n return `linear-gradient(135deg, rgba(255,255,255,${o * 0.85}) 0%, rgba(255,255,255,${o * 0.65}) 50%, rgba(255,255,255,${o * 0.75}) 100%)`;\n}\n\nconst GLASS_COMMON = `\n backdrop-filter: blur(40px) saturate(1.8); -webkit-backdrop-filter: blur(40px) saturate(1.8);\n border: 1px solid rgba(255,255,255,0.35);\n box-shadow: inset 0 1px 0 rgba(255,255,255,0.4), 0 8px 32px rgba(0,0,0,0.15), 0 2px 8px rgba(0,0,0,0.08);\n`;\n\n// ── Toast (operation feedback) — top-center, brief ──\n\nfunction showToast(variant: 'success' | 'error' | 'warning' | 'info', message: string,\n opts?: ToastOptions) {\n const opts_ = { ...defaults, ...opts };\n const placement: ToastPlacement = opts_.placement ?? 'top';\n const sticky = opts_.sticky ?? false;\n\n // A repeat of a message already on screen refreshes it instead of stacking.\n const key = `${placement}|${variant}|${message}`;\n if (opts_.dedupe) {\n const existing = live.get(key);\n if (existing) { existing.restart(); return; }\n }\n\n import('../utils/sounds').then(s => {\n if (variant === 'success') s.playSuccess();\n else if (variant === 'error') s.playError();\n else s.playNotification();\n }).catch(() => {});\n\n const container = placement === 'bottom'\n ? getOrCreate(TOAST_BOTTOM_CONTAINER_ID, 'fixed bottom-4 left-1/2 -translate-x-1/2 z-[9999] flex flex-col-reverse gap-2 items-center pointer-events-none')\n : getOrCreate(TOAST_CONTAINER_ID, 'fixed top-4 left-1/2 -translate-x-1/2 z-[9999] flex flex-col gap-2 items-center pointer-events-none');\n const o = getMenuOpacity();\n const color =\n variant === 'success' ? '#22c55e' :\n variant === 'error' ? '#ef4444' :\n // Amber, and deliberately not the error red: a warning is something the\n // user should read before continuing, not something that failed. Painting\n // both red teaches people to ignore the colour.\n variant === 'warning' ? '#f59e0b' :\n '#3b82f6';\n const icons = {\n success: '<svg class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"' + color + '\" stroke-width=\"2\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 13l4 4L19 7\"/></svg>',\n error: '<svg class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"' + color + '\" stroke-width=\"2\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\"/></svg>',\n warning: '<svg class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"' + color + '\" stroke-width=\"2\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z\"/></svg>',\n info: '<svg class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"' + color + '\" stroke-width=\"2\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg>',\n };\n const icon = icons[variant];\n\n // Enter from the edge the toast is anchored to, so it reads as arriving from\n // off-screen rather than drifting in from the wrong direction.\n const offscreen = placement === 'bottom' ? 'translateY(10px)' : 'translateY(-10px)';\n\n const el = document.createElement('div');\n el.className = 'pointer-events-auto cursor-pointer';\n el.style.cssText = `\n padding: 8px 20px; border-radius: 12px;\n background: ${glassBackground(o)}; ${GLASS_COMMON}\n opacity: 0; transform: ${offscreen} scale(0.95);\n transition: opacity ${FADE_MS}ms ease, transform ${FADE_MS}ms ease;\n display: flex; align-items: center; gap: 8px;\n font-size: 13px; font-weight: 500; color: rgb(55,65,81);\n max-width: min(90vw, 460px);\n `;\n // A toast IS a status message, and it appears without the user moving focus\n // — so without a live region it is announced to nobody. `alert` is\n // assertive and interrupts whatever is being read, which is right for a\n // failure and wrong for a confirmation the user already expected.\n el.setAttribute('role', variant === 'error' ? 'alert' : 'status');\n // Read the whole toast, not the word that changed. Without it a repeat that\n // only alters a number announces the number alone.\n el.setAttribute('aria-atomic', 'true');\n\n el.innerHTML = icon;\n const span = document.createElement('span');\n span.textContent = message;\n el.appendChild(span);\n\n container.appendChild(el);\n requestAnimationFrame(() => { el.style.opacity = '1'; el.style.transform = 'translateY(0) scale(1)'; });\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n let gone = false;\n const dismiss = () => {\n if (gone) return;\n gone = true;\n if (timer) clearTimeout(timer);\n live.delete(key);\n el.style.opacity = '0';\n el.style.transform = `${offscreen} scale(0.95)`;\n setTimeout(() => el.remove(), FADE_MS);\n };\n\n // Info messages are usually a sentence (\"no matches — check X\"); give them a\n // beat longer to read than the terse success/error confirmations.\n const duration = opts_.duration ?? (variant === 'info' ? 4500 : 3000);\n const restart = () => {\n if (timer) clearTimeout(timer);\n if (!sticky) timer = setTimeout(dismiss, duration);\n };\n restart();\n\n // Hold the timer while the pointer rests on it. Three seconds is enough to\n // read a confirmation and not enough to read an address someone leaned in\n // for — and a toast that vanishes as you reach for it cannot be re-read at\n // all, since there is no history to open.\n el.addEventListener('mouseenter', () => { if (timer) clearTimeout(timer); });\n el.addEventListener('mouseleave', () => { if (!gone) restart(); });\n\n // Tap anywhere on the toast to dismiss it. A toast has no other click\n // affordance, and a sticky one has no other exit at all — `notify` has\n // behaved this way since it existed.\n el.addEventListener('click', dismiss);\n\n live.set(key, { el, restart });\n}\n\n// ── Notification (system alert) — top-right, stays longer ──\n\nfunction showNotification(message: string, opts?: { duration?: number }) {\n import('../utils/sounds').then(s => s.playNotification()).catch(() => {});\n\n const container = getOrCreate(NOTIF_CONTAINER_ID, 'fixed top-4 right-4 z-[9999] flex flex-col gap-3 items-end pointer-events-none');\n const o = getMenuOpacity();\n\n const el = document.createElement('div');\n el.className = 'pointer-events-auto cursor-pointer';\n el.style.cssText = `\n min-width: 280px; max-width: 380px; padding: 12px 16px; border-radius: 16px;\n background: ${glassBackground(o)}; ${GLASS_COMMON}\n opacity: 0; transform: translateX(30px) scale(0.95);\n transition: opacity ${FADE_MS}ms cubic-bezier(0.4,0,0.2,1), transform ${FADE_MS}ms cubic-bezier(0.4,0,0.2,1);\n display: flex; align-items: flex-start; gap: 12px;\n `;\n\n el.innerHTML = `\n <div style=\"width: 36px; height: 36px; border-radius: 10px; background: rgba(59,130,246,0.15); display: flex; align-items: center; justify-content: center; flex-shrink: 0;\">\n <svg class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"#3b82f6\" stroke-width=\"2\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0\"/></svg>\n </div>\n <div style=\"flex: 1; min-width: 0;\">\n <div style=\"font-size: 11px; font-weight: 600; color: #3b82f6; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 2px;\">Notification</div>\n <div class=\"notif-msg\" style=\"font-size: 13px; font-weight: 500; color: rgb(55,65,81); line-height: 1.4;\"></div>\n </div>\n <button style=\"flex-shrink: 0; padding: 4px; color: rgb(156,163,175); font-size: 18px; line-height: 1; transition: color 0.2s;\" onmouseenter=\"this.style.color='rgb(75,85,99)'\" onmouseleave=\"this.style.color='rgb(156,163,175)'\">×</button>\n `;\n\n const msgEl = el.querySelector('.notif-msg');\n if (msgEl) msgEl.textContent = message;\n\n const dismiss = () => {\n el.style.opacity = '0';\n el.style.transform = 'translateX(30px) scale(0.95)';\n setTimeout(() => el.remove(), FADE_MS);\n };\n\n el.querySelector('button')?.addEventListener('click', (e) => { e.stopPropagation(); dismiss(); });\n el.addEventListener('click', dismiss);\n\n container.appendChild(el);\n requestAnimationFrame(() => { el.style.opacity = '1'; el.style.transform = 'translateX(0) scale(1)'; });\n\n setTimeout(dismiss, opts?.duration ?? 10000);\n}\n\nconst toast = {\n success: (message: string, opts?: ToastOptions) => showToast('success', message, opts),\n error: (message: string, opts?: ToastOptions) => showToast('error', message, opts),\n /**\n * Something the user should read before continuing — a partial success, a\n * setting that will not apply until they do something else. Distinct from\n * `error`, which is \"this did not happen\".\n */\n warning: (message: string, opts?: ToastOptions) => showToast('warning', message, opts),\n info: (message: string, opts?: ToastOptions) => showToast('info', message, opts),\n // Persistent top-right notification card (the old toast.info presentation).\n notify: (message: string, opts?: { duration?: number }) => showNotification(message, opts),\n /**\n * Set defaults for every subsequent toast. Call once at app startup; later\n * calls replace the whole object rather than merging, so a consumer sets its\n * policy in one place. Per-call options still take precedence.\n */\n configure: (next: ToastOptions) => { defaults = { ...next }; },\n};\n\nexport default toast;\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/useIsMobile.ts","../src/shell/PopupMenu.tsx","../src/shell/ShellPrefs.tsx"],"names":["PopupMenuItem","jsx"],"mappings":"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","file":"chunk-ZCDFNZQS.js","sourcesContent":["/**\n * Mobile breakpoint hook — single source of truth for \"is this a phone/tablet\n * portrait viewport?\" Drives whether <Layout> renders the desktop chrome\n * (windows / taskbar / start menu sidebar) or the <MobileAppLanding> screen\n * that points the user at the consumer-wired mobile app. A handful of\n * primitives (Modal, ResizableTable) also read it to adapt touch behaviour.\n *\n * Locked at 768 px so iPad portrait counts as mobile. Also OR's with\n * `pointer: coarse` so touch-only devices that happen to be wider (e.g. an\n * Android tablet in landscape) still get touch-appropriate UI.\n */\nimport { useSyncExternalStore } from 'react';\n\nconst QUERY = '(max-width: 767px), (pointer: coarse)';\n\nfunction getSnapshot(): boolean {\n if (typeof window === 'undefined' || !window.matchMedia) return false;\n return window.matchMedia(QUERY).matches;\n}\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\nfunction subscribe(cb: () => void): () => void {\n if (typeof window === 'undefined' || !window.matchMedia) return () => {};\n const mql = window.matchMedia(QUERY);\n // Older Safari uses addListener; modern browsers use addEventListener.\n if (mql.addEventListener) {\n mql.addEventListener('change', cb);\n return () => mql.removeEventListener('change', cb);\n }\n mql.addListener(cb);\n return () => mql.removeListener(cb);\n}\n\nexport function useIsMobile(): boolean {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","import { forwardRef, useEffect, useRef, type ReactNode, type CSSProperties } from 'react';\nimport { createPortal } from 'react-dom';\nimport { glassStyle, GLASS_DIVIDER } from '../utils/glass';\n\n/**\n * Unified popup menu component — used for all context menus, dropdowns, and flyouts.\n * Reads --menu-density CSS variable: 'tight', 'normal' (default), or 'large'.\n */\n\nexport interface PopupMenuProps {\n children: ReactNode;\n style?: CSSProperties;\n className?: string;\n onClose?: () => void;\n minWidth?: number;\n /** Render into document.body instead of in place. Required when the menu\n * is opened from INSIDE a window: the window panel is a transformed /\n * backdrop-filtered `overflow-hidden` container, which both re-anchors\n * `position: fixed` descendants to itself and clips them — a menu\n * positioned at viewport coordinates ends up offset or invisible.\n * Portaling restores true viewport positioning. */\n portal?: boolean;\n}\n\nfunction getDensity(): 'tight' | 'normal' | 'large' {\n return (getComputedStyle(document.documentElement).getPropertyValue('--menu-density')?.trim() as any) || 'normal';\n}\n\n/** Container for a popup menu — auto-clamps to stay within viewport */\nexport function PopupMenu({ children, style, className = '', onClose, minWidth = 180, portal = false }: PopupMenuProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!onClose) return;\n const handleClick = (e: PointerEvent | MouseEvent) => {\n if ((e.target as HTMLElement).closest('[data-menu-toggle]')) return;\n if (ref.current && !ref.current.contains(e.target as Node)) onClose();\n };\n const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n window.addEventListener('pointerdown', handleClick);\n window.addEventListener('keydown', handleKey);\n return () => { window.removeEventListener('pointerdown', handleClick); window.removeEventListener('keydown', handleKey); };\n }, [onClose]);\n\n // After render, clamp position to viewport boundaries\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n requestAnimationFrame(() => {\n const rect = el.getBoundingClientRect();\n const margin = 8;\n // Clamp right edge\n if (rect.right > window.innerWidth - margin) {\n const overflow = rect.right - window.innerWidth + margin;\n if (el.style.left) {\n el.style.left = `${parseFloat(el.style.left) - overflow}px`;\n } else if (!el.style.right) {\n el.style.left = `${rect.left - overflow}px`;\n }\n }\n // Clamp left edge\n if (rect.left < margin) {\n if (el.style.left) {\n el.style.left = `${margin}px`;\n } else if (el.style.right) {\n el.style.right = `${window.innerWidth - rect.width - margin}px`;\n }\n }\n // Clamp bottom edge\n if (rect.bottom > window.innerHeight - margin) {\n const overflow = rect.bottom - window.innerHeight + margin;\n if (el.style.top) {\n el.style.top = `${parseFloat(el.style.top) - overflow}px`;\n } else if (el.style.bottom) {\n // Already anchored to bottom, just clamp\n el.style.bottom = `${margin}px`;\n } else {\n el.style.top = `${rect.top - overflow}px`;\n }\n }\n // Clamp top edge\n if (rect.top < margin) {\n if (el.style.top) {\n el.style.top = `${margin}px`;\n } else if (el.style.bottom) {\n el.style.bottom = `${window.innerHeight - rect.height - margin}px`;\n }\n }\n });\n });\n\n const density = getDensity();\n\n const menu = (\n <div ref={ref}\n className={`fixed z-[400] rounded-2xl ${density === 'tight' ? 'py-1' : density === 'large' ? 'py-2' : 'py-1.5'} ${className}`}\n style={{ minWidth, animation: 'popup-in 0.12s ease-out', ...glassStyle(), ...style }}>\n {children}\n <style>{`@keyframes popup-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }`}</style>\n </div>\n );\n\n return portal ? createPortal(menu, document.body) : menu;\n}\n\n/** A clickable menu item */\nexport interface PopupMenuItemProps {\n onClick?: () => void;\n children: ReactNode;\n className?: string;\n danger?: boolean;\n disabled?: boolean;\n /**\n * The menu semantics, when this item is part of one.\n *\n * A context menu on the desktop is a list of buttons and needs none of this;\n * a dropdown attached to a trigger is a `menu` whose items are `menuitem`s\n * with one tab stop between them. Both draw the same item, so the styling\n * lives here rather than being copied into whichever one is newer.\n */\n role?: 'menuitem';\n tabIndex?: number;\n id?: string;\n onMouseEnter?: () => void;\n}\n\n/**\n * `forwardRef`, not a `ref` prop.\n *\n * Taking `ref` as an ordinary prop is React 19 only. This package's peer range\n * is `react: \">=18\"`, and React 18 strips `ref` from a function component's\n * props and warns — so on an 18 consumer the ref would silently never attach,\n * `DropdownMenu`'s `itemRefs` would all be null, and its arrow keys would move\n * `tabIndex` while DOM focus stayed put. A keyboard-dead menu with no error.\n */\nexport const PopupMenuItem = forwardRef<HTMLButtonElement, PopupMenuItemProps>(function PopupMenuItem({\n onClick, children, className = '', danger, disabled,\n role, tabIndex, id, onMouseEnter,\n}, ref) {\n const density = getDensity();\n // Vertical gap between items. `normal` sits a little tighter than the raw\n // size padding; `large` adds a bit more room. Floored at the tight value so\n // the reduced `normal` never drops below `tight` at the smallest menu size.\n const itemPadY = density === 'tight' ? '0.25rem'\n : density === 'large' ? '0.6rem'\n : 'max(0.25rem, calc(var(--menu-padding-y, 0.5rem) - 0.1rem))';\n return (\n <button\n ref={ref}\n id={id}\n role={role}\n tabIndex={tabIndex}\n onClick={onClick}\n onMouseEnter={onMouseEnter}\n disabled={disabled}\n className={`w-full flex items-center gap-2 text-left transition-colors rounded-lg mx-auto\n ${danger ? 'text-red-600 hover:bg-red-50' : 'text-gray-700 hover:bg-blue-50 hover:text-blue-700'}\n ${disabled ? 'opacity-40 cursor-not-allowed' : ''}\n ${className}`}\n style={{\n width: 'calc(100% - 8px)',\n marginLeft: 4,\n marginRight: 4,\n fontSize: 'var(--menu-font-size, 14px)',\n paddingLeft: 'var(--menu-padding-x, 1rem)',\n paddingRight: 'var(--menu-padding-x, 1rem)',\n paddingTop: itemPadY,\n paddingBottom: itemPadY,\n }}\n >\n {children}\n </button>\n );\n});\n\n/** A divider line between menu items */\nexport function PopupMenuDivider() {\n const density = getDensity();\n return <div className={`border-t ${GLASS_DIVIDER} ${density === 'tight' ? 'my-0.5' : density === 'large' ? 'my-1.5' : 'my-1'} mx-3`} />;\n}\n\n/** A section header label */\nexport function PopupMenuLabel({ children }: { children: ReactNode }) {\n const density = getDensity();\n const labelPadY = density === 'tight' ? '0.125rem' : density === 'large' ? '0.375rem' : '0.25rem';\n return (\n <div\n className=\"text-[10px] font-medium text-gray-400 uppercase tracking-wider\"\n style={{\n paddingLeft: 'var(--menu-padding-x, 1rem)',\n paddingRight: 'var(--menu-padding-x, 1rem)',\n paddingTop: labelPadY,\n paddingBottom: labelPadY,\n }}\n >\n {children}\n </div>\n );\n}\n\n/** An icon helper */\nexport function MenuIcon({ d, className = 'h-4 w-4 text-gray-400' }: { d: string; className?: string }) {\n return (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d={d} />\n </svg>\n );\n}\n","/**\n * Shell-side user-preferences surface — the package never owns where prefs\n * live. The consumer supplies a hook returning the current prefs object plus\n * a save callback; the shell reads keys it knows about (theme,\n * taskbar_position, taskbar_size, world_clocks, favorite_documents,\n * desktop_folders, desktop_snap, notepad_notes, …) and patches them.\n *\n * When no provider is mounted, the shell falls back to localStorage scoped\n * by `storageKey` — so the package works out of the box for a backend-less\n * consumer.\n */\nimport { createContext, useContext, useState, useCallback, type ReactNode } from 'react';\n\nexport interface ShellPrefsAdapter {\n /** The current prefs object. The shell reads its known keys directly off\n * this; consumer-private keys are ignored. */\n prefs: Record<string, any>;\n /** Patch a subset of prefs. The shell calls this with shallow diffs. */\n save: (patch: Record<string, any>) => Promise<void> | void;\n}\n\nconst ShellPrefsContext = createContext<ShellPrefsAdapter | null>(null);\n\nexport function ShellPrefsProvider({\n value,\n children,\n}: { value: ShellPrefsAdapter; children: ReactNode }) {\n return <ShellPrefsContext.Provider value={value}>{children}</ShellPrefsContext.Provider>;\n}\n\n/** Default localStorage-backed adapter — useful when the consumer doesn't\n * ship a backend. Pass the result into <ShellPrefsProvider value={…}>.\n *\n * `defaults` are merged behind whatever's already stored, so they only\n * apply for keys the user hasn't set yet. Useful for opting out of bundled\n * features (e.g. `{ show_desktop_version: false }`). */\nexport function useLocalStoragePrefs(\n storageKey = 'react-os-shell:prefs',\n defaults?: Record<string, any>,\n): ShellPrefsAdapter {\n const [prefs, setPrefs] = useState<Record<string, any>>(() => {\n try {\n const stored = JSON.parse(localStorage.getItem(storageKey) || '{}');\n return { ...(defaults ?? {}), ...stored };\n } catch { return { ...(defaults ?? {}) }; }\n });\n const save = useCallback((patch: Record<string, any>) => {\n setPrefs(prev => {\n const next = { ...prev, ...patch };\n try { localStorage.setItem(storageKey, JSON.stringify(next)); } catch {}\n return next;\n });\n }, [storageKey]);\n return { prefs, save };\n}\n\n/** Returns the active prefs adapter. When no provider is mounted, returns a\n * no-op adapter that reads as empty and silently drops saves — components\n * still render, but persistence is a no-op. */\nexport function useShellPrefs(): ShellPrefsAdapter {\n const ctx = useContext(ShellPrefsContext);\n if (ctx) return ctx;\n return { prefs: {}, save: () => {} };\n}\n"]}
|