react-os-shell 2.8.1 → 2.9.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.
Files changed (42) hide show
  1. package/dist/{Browser-RNO4KYSE.js → Browser-MMVOWX6V.js} +4 -4
  2. package/dist/{Browser-RNO4KYSE.js.map → Browser-MMVOWX6V.js.map} +1 -1
  3. package/dist/{Documents-L7CTOPIN.js → Documents-YOILVZTR.js} +4 -4
  4. package/dist/{Documents-L7CTOPIN.js.map → Documents-YOILVZTR.js.map} +1 -1
  5. package/dist/Files-DQPXOF2C.js +13 -0
  6. package/dist/{Files-FJL2BZDI.js.map → Files-DQPXOF2C.js.map} +1 -1
  7. package/dist/ImageAnnotator-55NPWJ2R.js +4 -0
  8. package/dist/{ImageAnnotator-CTTMAY5Z.js.map → ImageAnnotator-55NPWJ2R.js.map} +1 -1
  9. package/dist/{Notepad-2OJ53Q7M.js → Notepad-VWPY5QBS.js} +4 -4
  10. package/dist/{Notepad-2OJ53Q7M.js.map → Notepad-VWPY5QBS.js.map} +1 -1
  11. package/dist/{PomodoroTimer-M7MDQTVN.js → PomodoroTimer-CDAJC2NX.js} +4 -4
  12. package/dist/{PomodoroTimer-M7MDQTVN.js.map → PomodoroTimer-CDAJC2NX.js.map} +1 -1
  13. package/dist/Preview-OZBOKT2W.js +9 -0
  14. package/dist/{Preview-65VPPGWD.js.map → Preview-OZBOKT2W.js.map} +1 -1
  15. package/dist/{Spreadsheet-VIYNZ6KJ.js → Spreadsheet-SFWYWPGX.js} +4 -4
  16. package/dist/{Spreadsheet-VIYNZ6KJ.js.map → Spreadsheet-SFWYWPGX.js.map} +1 -1
  17. package/dist/apps/index.js +15 -15
  18. package/dist/{chunk-YQWFKXWH.js → chunk-6EUBB5B5.js} +3 -3
  19. package/dist/{chunk-YQWFKXWH.js.map → chunk-6EUBB5B5.js.map} +1 -1
  20. package/dist/{chunk-5HXHD62G.js → chunk-6XRORZDB.js} +5 -5
  21. package/dist/{chunk-5HXHD62G.js.map → chunk-6XRORZDB.js.map} +1 -1
  22. package/dist/{chunk-ZEMXT6BR.js → chunk-EMVVW7CY.js} +5 -5
  23. package/dist/{chunk-ZEMXT6BR.js.map → chunk-EMVVW7CY.js.map} +1 -1
  24. package/dist/{chunk-P75EON66.js → chunk-FIUUGSGJ.js} +3 -3
  25. package/dist/{chunk-P75EON66.js.map → chunk-FIUUGSGJ.js.map} +1 -1
  26. package/dist/{chunk-KUIPWCTJ.js → chunk-NUPYEVU4.js} +3 -3
  27. package/dist/{chunk-KUIPWCTJ.js.map → chunk-NUPYEVU4.js.map} +1 -1
  28. package/dist/{chunk-WG3PMYDQ.js → chunk-UXJKPSLC.js} +5 -5
  29. package/dist/{chunk-WG3PMYDQ.js.map → chunk-UXJKPSLC.js.map} +1 -1
  30. package/dist/{chunk-WIJ45SYD.js → chunk-VENYVK3L.js} +18 -10
  31. package/dist/chunk-VENYVK3L.js.map +1 -0
  32. package/dist/{chunk-IXW6775F.js → chunk-Y4FCCBR7.js} +3 -3
  33. package/dist/{chunk-IXW6775F.js.map → chunk-Y4FCCBR7.js.map} +1 -1
  34. package/dist/{chunk-V6N2NXHQ.js → chunk-YUZRECE6.js} +3 -3
  35. package/dist/{chunk-V6N2NXHQ.js.map → chunk-YUZRECE6.js.map} +1 -1
  36. package/dist/index.d.ts +12 -3
  37. package/dist/index.js +12 -12
  38. package/package.json +1 -1
  39. package/dist/Files-FJL2BZDI.js +0 -13
  40. package/dist/ImageAnnotator-CTTMAY5Z.js +0 -4
  41. package/dist/Preview-65VPPGWD.js +0 -9
  42. package/dist/chunk-WIJ45SYD.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -436,10 +436,16 @@ declare const ALT_SHIFT_E: string;
436
436
  declare const ALT_SHIFT_N: string;
437
437
 
438
438
  /**
439
- * Two notification systems:
439
+ * Two presentations:
440
440
  *
441
- * 1. toast.success/error — operation feedback, top-center, auto-dismiss 3s
442
- * 2. toast.info system notification, top-right, stays 10s, dismissible
441
+ * 1. toast.success / error / info brief operation feedback, top-center,
442
+ * auto-dismiss (success/error ~3s, info ~4.5s). The everyday "what just
443
+ * happened" feedback — most messages want this.
444
+ * 2. toast.notify — system notification, top-right card, stays 10s, dismissible.
445
+ * For an alert worth lingering on; reach for it deliberately, not by default.
446
+ *
447
+ * (Historically `toast.info` rendered the top-right notification card; it now
448
+ * renders a brief toast — that persistent card moved to `toast.notify`.)
443
449
  */
444
450
  declare const toast: {
445
451
  success: (message: string) => void;
@@ -447,6 +453,9 @@ declare const toast: {
447
453
  info: (message: string, opts?: {
448
454
  duration?: number;
449
455
  }) => void;
456
+ notify: (message: string, opts?: {
457
+ duration?: number;
458
+ }) => void;
450
459
  };
451
460
 
452
461
  interface GridColumn {
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
- import { subscribePomo, getPomoSnapshot } from './chunk-P75EON66.js';
2
- export { setShellTodoProvider } from './chunk-P75EON66.js';
3
- import { PREVIEW_OPENED_EVENT, publishDesktopFolders, requestFilesTrashView, FolderGlyph, openPreviewFile, requestFilesDesktopFolderView, FileIconTile, hashGradient } from './chunk-ZEMXT6BR.js';
4
- export { Breadcrumbs } from './chunk-ZEMXT6BR.js';
1
+ import { subscribePomo, getPomoSnapshot } from './chunk-FIUUGSGJ.js';
2
+ export { setShellTodoProvider } from './chunk-FIUUGSGJ.js';
3
+ import { PREVIEW_OPENED_EVENT, publishDesktopFolders, requestFilesTrashView, FolderGlyph, openPreviewFile, requestFilesDesktopFolderView, FileIconTile, hashGradient } from './chunk-EMVVW7CY.js';
4
+ export { Breadcrumbs } from './chunk-EMVVW7CY.js';
5
5
  import { SidebarLayout } from './chunk-VGTEM5RZ.js';
6
6
  export { SidebarLayout } from './chunk-VGTEM5RZ.js';
7
7
  import { playNotification, playStartup, soundsEnabled, getSoundConfig, SOUND_PACK_KEYS, SOUND_PACKS, SOUND_TYPES, SOUND_TYPE_LABELS, setSoundForType, previewSound, setAllSounds, playLogout } from './chunk-D7PYW2QS.js';
8
- import { setPdfPreview } from './chunk-WG3PMYDQ.js';
9
- import './chunk-KUIPWCTJ.js';
10
- import { toast_default } from './chunk-WIJ45SYD.js';
11
- export { toast_default as toast } from './chunk-WIJ45SYD.js';
12
- export { EditableGrid } from './chunk-V6N2NXHQ.js';
13
- import { APP_VERSION } from './chunk-IXW6775F.js';
14
- export { VERSION } from './chunk-IXW6775F.js';
8
+ import { setPdfPreview } from './chunk-UXJKPSLC.js';
9
+ import './chunk-NUPYEVU4.js';
10
+ import { toast_default } from './chunk-VENYVK3L.js';
11
+ export { toast_default as toast } from './chunk-VENYVK3L.js';
12
+ export { EditableGrid } from './chunk-YUZRECE6.js';
13
+ import { APP_VERSION } from './chunk-Y4FCCBR7.js';
14
+ export { VERSION } from './chunk-Y4FCCBR7.js';
15
15
  import { useWindowManager, PopupMenu, PopupMenuLabel, PopupMenuDivider, PopupMenuItem, Modal, WINDOW_REGISTRY, isPageEntry, useShellPrefs, useIsMobile, ModalActions, useModalActive, client_default, LoadingSpinner, setWindowPosition, ThumbCard, activateModal } from './chunk-JNF5VRPB.js';
16
16
  export { CancelButton, CopyButton, DocFavStar, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, WindowCrashedFallback, WindowErrorBoundary, WindowManagerProvider, WindowTitle, commitExposeHighlight, exitExposeMode, getActiveWindowRoute, getExposeHighlight, getWindowPosition, isEntityEntry, isPageEntry, registerModalEscapeInterceptor, setExposeHighlight, setShellApiClient, setShellWindowRegistry, setWindowDefaultPosition, setWindowPosition, subscribeExposeHighlight, toggleExposeMode, useLocalStoragePrefs, useModalActive, useShellPrefs, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle } from './chunk-JNF5VRPB.js';
17
17
  import { confirm } from './chunk-UBN4IUDE.js';
@@ -1221,7 +1221,7 @@ function BugReportProvider({ children }) {
1221
1221
  )
1222
1222
  ] });
1223
1223
  }
1224
- var LazyImageAnnotator = lazy(() => import('./ImageAnnotator-CTTMAY5Z.js'));
1224
+ var LazyImageAnnotator = lazy(() => import('./ImageAnnotator-55NPWJ2R.js'));
1225
1225
  function UploadDropZone({ onSelect }) {
1226
1226
  const inputRef = useRef(null);
1227
1227
  const [hover, setHover] = useState(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-os-shell",
3
- "version": "2.8.1",
3
+ "version": "2.9.0",
4
4
  "description": "Desktop-style React UI shell — windows, taskbar, start menu, sticky notes, frosted glass theming, and bundled apps.",
5
5
  "license": "MIT",
6
6
  "author": "Victor Y. Mau",
@@ -1,13 +0,0 @@
1
- export { Files as default, openFilesInTrashMode, setFilesDemoTree } from './chunk-5HXHD62G.js';
2
- import './chunk-ZEMXT6BR.js';
3
- import './chunk-VGTEM5RZ.js';
4
- import './chunk-WG3PMYDQ.js';
5
- import './chunk-KUIPWCTJ.js';
6
- import './chunk-WIJ45SYD.js';
7
- import './chunk-V6N2NXHQ.js';
8
- import './chunk-IXW6775F.js';
9
- import './chunk-JNF5VRPB.js';
10
- import './chunk-UBN4IUDE.js';
11
- import './chunk-ZF6AYO4G.js';
12
- //# sourceMappingURL=Files-FJL2BZDI.js.map
13
- //# sourceMappingURL=Files-FJL2BZDI.js.map
@@ -1,4 +0,0 @@
1
- export { ImageAnnotator_default as default } from './chunk-KUIPWCTJ.js';
2
- import './chunk-WIJ45SYD.js';
3
- //# sourceMappingURL=ImageAnnotator-CTTMAY5Z.js.map
4
- //# sourceMappingURL=ImageAnnotator-CTTMAY5Z.js.map
@@ -1,9 +0,0 @@
1
- export { Preview as default, setPdfPreview } from './chunk-WG3PMYDQ.js';
2
- import './chunk-KUIPWCTJ.js';
3
- import './chunk-WIJ45SYD.js';
4
- import './chunk-IXW6775F.js';
5
- import './chunk-JNF5VRPB.js';
6
- import './chunk-UBN4IUDE.js';
7
- import './chunk-ZF6AYO4G.js';
8
- //# sourceMappingURL=Preview-65VPPGWD.js.map
9
- //# sourceMappingURL=Preview-65VPPGWD.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shell/toast.ts"],"names":[],"mappings":"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","file":"chunk-WIJ45SYD.js","sourcesContent":["/**\n * Two notification systems:\n *\n * 1. toast.success/error — operation feedback, top-center, auto-dismiss 3s\n * 2. toast.info — system notification, top-right, stays 10s, dismissible\n */\n\nconst TOAST_CONTAINER_ID = 'toast-container';\nconst NOTIF_CONTAINER_ID = 'notif-container';\nconst FADE_MS = 300;\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', message: string) {\n import('../utils/sounds').then(s => {\n if (variant === 'success') s.playSuccess();\n else s.playError();\n }).catch(() => {});\n\n const container = 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 isSuccess = variant === 'success';\n const color = isSuccess ? '#22c55e' : '#ef4444';\n const icon = isSuccess\n ? '<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 : '<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\n const el = document.createElement('div');\n el.className = 'pointer-events-auto';\n el.style.cssText = `\n padding: 8px 20px; border-radius: 12px;\n background: ${glassBackground(o)}; ${GLASS_COMMON}\n opacity: 0; transform: translateY(-10px) 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 white-space: nowrap;\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 setTimeout(() => {\n el.style.opacity = '0';\n el.style.transform = 'translateY(-10px) scale(0.95)';\n setTimeout(() => el.remove(), FADE_MS);\n }, 3000);\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)'\">&times;</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) => showToast('success', message),\n error: (message: string) => showToast('error', message),\n info: (message: string, opts?: { duration?: number }) => showNotification(message, opts),\n};\n\nexport default toast;\n"]}