react-os-shell 2.7.0 → 2.8.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.
Files changed (34) hide show
  1. package/dist/{Browser-YEOBTPST.js → Browser-RNO4KYSE.js} +4 -4
  2. package/dist/{Browser-YEOBTPST.js.map → Browser-RNO4KYSE.js.map} +1 -1
  3. package/dist/{Documents-D7UN7W6P.js → Documents-L7CTOPIN.js} +3 -3
  4. package/dist/{Documents-D7UN7W6P.js.map → Documents-L7CTOPIN.js.map} +1 -1
  5. package/dist/{Files-VXHZY7NY.js → Files-FJL2BZDI.js} +7 -7
  6. package/dist/{Files-VXHZY7NY.js.map → Files-FJL2BZDI.js.map} +1 -1
  7. package/dist/{Notepad-THWCG35G.js → Notepad-2OJ53Q7M.js} +3 -3
  8. package/dist/{Notepad-THWCG35G.js.map → Notepad-2OJ53Q7M.js.map} +1 -1
  9. package/dist/Preview-65VPPGWD.js +9 -0
  10. package/dist/{Preview-5SOLJFFK.js.map → Preview-65VPPGWD.js.map} +1 -1
  11. package/dist/{Sidebar-BW7SYNBA.js → Sidebar-PY762ANK.js} +3 -3
  12. package/dist/Sidebar-PY762ANK.js.map +1 -0
  13. package/dist/{Spreadsheet-UVBEPLQB.js → Spreadsheet-VIYNZ6KJ.js} +4 -4
  14. package/dist/{Spreadsheet-UVBEPLQB.js.map → Spreadsheet-VIYNZ6KJ.js.map} +1 -1
  15. package/dist/apps/index.js +12 -12
  16. package/dist/{chunk-FX77XLQZ.js → chunk-5HXHD62G.js} +4 -4
  17. package/dist/{chunk-FX77XLQZ.js.map → chunk-5HXHD62G.js.map} +1 -1
  18. package/dist/{chunk-MJIMKMSJ.js → chunk-IXW6775F.js} +3 -3
  19. package/dist/{chunk-MJIMKMSJ.js.map → chunk-IXW6775F.js.map} +1 -1
  20. package/dist/{chunk-AAKIF7SW.js → chunk-V6N2NXHQ.js} +3 -3
  21. package/dist/{chunk-AAKIF7SW.js.map → chunk-V6N2NXHQ.js.map} +1 -1
  22. package/dist/{chunk-UATWDGLV.js → chunk-WG3PMYDQ.js} +3 -3
  23. package/dist/{chunk-UATWDGLV.js.map → chunk-WG3PMYDQ.js.map} +1 -1
  24. package/dist/{chunk-LD2JBHD3.js → chunk-YQWFKXWH.js} +3 -3
  25. package/dist/{chunk-LD2JBHD3.js.map → chunk-YQWFKXWH.js.map} +1 -1
  26. package/dist/{chunk-YZEQWMO5.js → chunk-ZEMXT6BR.js} +4 -4
  27. package/dist/{chunk-YZEQWMO5.js.map → chunk-ZEMXT6BR.js.map} +1 -1
  28. package/dist/index.js +99 -64
  29. package/dist/index.js.map +1 -1
  30. package/dist/styles.css +187 -2
  31. package/dist/themes.css +244 -0
  32. package/package.json +4 -3
  33. package/dist/Preview-5SOLJFFK.js +0 -9
  34. package/dist/Sidebar-BW7SYNBA.js.map +0 -1
package/dist/styles.css CHANGED
@@ -18,11 +18,15 @@
18
18
  * - Status-badge tone-downs (matches the 8 semantic groups in StatusBadge)
19
19
  * - Scrollbar styling for editable grids and dark-mode containers
20
20
  *
21
- * Per-theme variants (pink / green / grey / blue) ship as extension
22
- * stylesheets the consumer can opt into separately not included here.
21
+ * Per-theme accent variants (pink / green / grey / blue + custom accent)
22
+ * live in themes.css, imported below so every consumer gets the full set —
23
+ * useTheme() offers all of them in every portal, so the remaps must exist
24
+ * wherever styles.css is loaded. themes.css is also exported standalone
25
+ * ("react-os-shell/themes.css") for consumers that import granularly.
23
26
  */
24
27
 
25
28
  @import "tailwindcss";
29
+ @import "./themes.css";
26
30
 
27
31
  /* Tailwind v4 doesn't scan node_modules by default — tell it to scan this
28
32
  package's compiled JS so utility classes used in shell components get
@@ -212,6 +216,7 @@
212
216
  [data-theme="dark"] [data-sticky-id] .text-black\/70 { color: rgba(230, 233, 245, 0.92) !important; }
213
217
  [data-theme="dark"] [data-sticky-id] .text-black\/30 { color: rgba(230, 233, 245, 0.45) !important; }
214
218
  [data-theme="dark"] [data-sticky-id] .text-black\/20 { color: rgba(230, 233, 245, 0.42) !important; }
219
+ [data-theme="dark"] [data-sticky-id] .text-black\/15 { color: rgba(230, 233, 245, 0.35) !important; }
215
220
  [data-theme="dark"] [data-sticky-id] .hover\:text-black\/50:hover { color: rgba(230, 233, 245, 0.70) !important; }
216
221
  [data-theme="dark"] [data-sticky-id] .placeholder\:text-black\/30::placeholder { color: rgba(230, 233, 245, 0.45) !important; }
217
222
  [data-theme="dark"] [data-sticky-id] .bg-black\/10 { background-color: rgba(230, 233, 245, 0.16) !important; }
@@ -262,6 +267,186 @@
262
267
  [data-theme="dark"] .bg-orange-100.text-orange-800 { background-color: rgba(249, 115, 22, 0.18) !important; color: #fdba74 !important; }
263
268
  [data-theme="dark"] .bg-red-100.text-red-800 { background-color: rgba(239, 68, 68, 0.18) !important; color: #fca5a5 !important; }
264
269
 
270
+ /* ── Extended dark-mode tint families ────────────────────────────────
271
+ Upstreamed 2026-06-13 from the admin portal's 2026-06-10 audit so all
272
+ consumers share one source of truth. Every hue used by chips/badges
273
+ (tag colors, group colors, status accents, deal stages), notice
274
+ banners, verdict panels, row tints and danger buttons gets the same
275
+ scale as the families above: -50 → 10% tint, -100 → 18%, -200 → ~30%,
276
+ borders -200 → 32% / -300 → 45%, and dark inks flip to the
277
+ -400/-300/-200 light end of their scale.
278
+ Deliberately NOT remapped: game surfaces that read as intentionally
279
+ light (Calculator LCD bg-slate-200/text-slate-600/800, 2048/Chess
280
+ tiles, weather gradients), the Customization theme-preview swatches
281
+ (bg-pink-50/90, bg-green-50/90, bg-blue-50/90, bg-gray-200/90,
282
+ bg-pink-200, bg-green-200 — they must show each theme's real colors),
283
+ and low white/black alphas used as glass highlights on wallpaper. */
284
+
285
+ /* Red */
286
+ [data-theme="dark"] .bg-red-50 { background-color: rgba(239, 68, 68, 0.10) !important; }
287
+ [data-theme="dark"] .hover\:bg-red-50:hover { background-color: rgba(239, 68, 68, 0.16) !important; }
288
+ [data-theme="dark"] .active\:bg-red-50:active { background-color: rgba(239, 68, 68, 0.16) !important; }
289
+ [data-theme="dark"] .bg-red-50\/30 { background-color: rgba(239, 68, 68, 0.06) !important; }
290
+ [data-theme="dark"] .bg-red-50\/50 { background-color: rgba(239, 68, 68, 0.08) !important; }
291
+ [data-theme="dark"] .hover\:bg-red-100:hover { background-color: rgba(239, 68, 68, 0.22) !important; }
292
+ [data-theme="dark"] .border-red-200 { border-color: rgba(239, 68, 68, 0.35) !important; }
293
+ [data-theme="dark"] .border-red-300,
294
+ [data-theme="dark"] .hover\:border-red-300:hover { border-color: rgba(239, 68, 68, 0.45) !important; }
295
+ [data-theme="dark"] .text-red-600,
296
+ [data-theme="dark"] .hover\:text-red-600:hover { color: #f87171 !important; }
297
+ [data-theme="dark"] .hover\:text-red-700:hover { color: #fca5a5 !important; }
298
+ [data-theme="dark"] .text-red-800,
299
+ [data-theme="dark"] .hover\:text-red-800:hover { color: #fecaca !important; }
300
+ [data-theme="dark"] .text-red-900 { color: #fecaca !important; }
301
+
302
+ /* Amber */
303
+ [data-theme="dark"] .bg-amber-50 { background-color: rgba(245, 158, 11, 0.10) !important; }
304
+ [data-theme="dark"] .hover\:bg-amber-50:hover { background-color: rgba(245, 158, 11, 0.14) !important; }
305
+ [data-theme="dark"] .bg-amber-50\/30 { background-color: rgba(245, 158, 11, 0.06) !important; }
306
+ [data-theme="dark"] .bg-amber-50\/40 { background-color: rgba(245, 158, 11, 0.07) !important; }
307
+ [data-theme="dark"] .bg-amber-50\/50 { background-color: rgba(245, 158, 11, 0.08) !important; }
308
+ [data-theme="dark"] .hover\:bg-amber-100:hover { background-color: rgba(245, 158, 11, 0.24) !important; }
309
+ [data-theme="dark"] .bg-amber-200 { background-color: rgba(245, 158, 11, 0.28) !important; }
310
+ [data-theme="dark"] .border-amber-200 { border-color: rgba(245, 158, 11, 0.35) !important; }
311
+ [data-theme="dark"] .border-amber-300,
312
+ [data-theme="dark"] .hover\:border-amber-300:hover { border-color: rgba(245, 158, 11, 0.45) !important; }
313
+ [data-theme="dark"] .text-amber-600,
314
+ [data-theme="dark"] .hover\:text-amber-600:hover { color: #fbbf24 !important; }
315
+ [data-theme="dark"] .text-amber-800 { color: #fde68a !important; }
316
+ [data-theme="dark"] .text-amber-900 { color: #fde68a !important; }
317
+
318
+ /* Yellow */
319
+ [data-theme="dark"] .bg-yellow-50 { background-color: rgba(234, 179, 8, 0.10) !important; }
320
+ [data-theme="dark"] .bg-yellow-50\/50 { background-color: rgba(234, 179, 8, 0.08) !important; }
321
+ [data-theme="dark"] .border-yellow-200 { border-color: rgba(234, 179, 8, 0.35) !important; }
322
+ [data-theme="dark"] .border-yellow-300 { border-color: rgba(234, 179, 8, 0.45) !important; }
323
+ [data-theme="dark"] .text-yellow-600,
324
+ [data-theme="dark"] .hover\:text-yellow-600:hover { color: #facc15 !important; }
325
+ [data-theme="dark"] .text-yellow-900 { color: #fef08a !important; }
326
+
327
+ /* Green / emerald */
328
+ [data-theme="dark"] .bg-green-50 { background-color: rgba(34, 197, 94, 0.10) !important; }
329
+ [data-theme="dark"] .hover\:bg-green-50:hover { background-color: rgba(34, 197, 94, 0.14) !important; }
330
+ [data-theme="dark"] .bg-green-50\/40 { background-color: rgba(34, 197, 94, 0.06) !important; }
331
+ [data-theme="dark"] .border-green-200 { border-color: rgba(34, 197, 94, 0.32) !important; }
332
+ [data-theme="dark"] .border-green-300,
333
+ [data-theme="dark"] .hover\:border-green-300:hover { border-color: rgba(34, 197, 94, 0.45) !important; }
334
+ [data-theme="dark"] .text-green-600,
335
+ [data-theme="dark"] .hover\:text-green-600:hover { color: #4ade80 !important; }
336
+ [data-theme="dark"] .text-green-900,
337
+ [data-theme="dark"] .hover\:text-green-800:hover { color: #bbf7d0 !important; }
338
+ [data-theme="dark"] .bg-emerald-50 { background-color: rgba(16, 185, 129, 0.10) !important; }
339
+ [data-theme="dark"] .hover\:bg-emerald-100:hover { background-color: rgba(16, 185, 129, 0.24) !important; }
340
+ [data-theme="dark"] .border-emerald-200 { border-color: rgba(16, 185, 129, 0.32) !important; }
341
+ [data-theme="dark"] .border-emerald-300 { border-color: rgba(16, 185, 129, 0.45) !important; }
342
+ [data-theme="dark"] .text-emerald-600 { color: #34d399 !important; }
343
+ [data-theme="dark"] .text-emerald-800 { color: #a7f3d0 !important; }
344
+
345
+ /* Orange */
346
+ [data-theme="dark"] .bg-orange-50 { background-color: rgba(249, 115, 22, 0.10) !important; }
347
+ [data-theme="dark"] .hover\:bg-orange-50:hover { background-color: rgba(249, 115, 22, 0.14) !important; }
348
+ [data-theme="dark"] .hover\:bg-orange-200:hover { background-color: rgba(249, 115, 22, 0.30) !important; }
349
+ [data-theme="dark"] .border-orange-200 { border-color: rgba(249, 115, 22, 0.32) !important; }
350
+ [data-theme="dark"] .border-orange-300,
351
+ [data-theme="dark"] .hover\:border-orange-300:hover { border-color: rgba(249, 115, 22, 0.45) !important; }
352
+ [data-theme="dark"] .text-orange-600 { color: #fb923c !important; }
353
+ [data-theme="dark"] .text-orange-900 { color: #fed7aa !important; }
354
+
355
+ /* Sky / teal / cyan */
356
+ [data-theme="dark"] .bg-sky-50 { background-color: rgba(14, 165, 233, 0.10) !important; }
357
+ [data-theme="dark"] .border-sky-300 { border-color: rgba(14, 165, 233, 0.45) !important; }
358
+ [data-theme="dark"] .bg-teal-50 { background-color: rgba(20, 184, 166, 0.10) !important; }
359
+ [data-theme="dark"] .bg-teal-100 { background-color: rgba(20, 184, 166, 0.18) !important; }
360
+ [data-theme="dark"] .text-teal-600 { color: #2dd4bf !important; }
361
+ [data-theme="dark"] .text-teal-700 { color: #5eead4 !important; }
362
+ [data-theme="dark"] .text-teal-800 { color: #99f6e4 !important; }
363
+ [data-theme="dark"] .text-cyan-600 { color: #22d3ee !important; }
364
+
365
+ /* Purple / violet */
366
+ [data-theme="dark"] .bg-purple-50 { background-color: rgba(168, 85, 247, 0.10) !important; }
367
+ [data-theme="dark"] .hover\:bg-purple-50:hover { background-color: rgba(168, 85, 247, 0.14) !important; }
368
+ [data-theme="dark"] .border-purple-200 { border-color: rgba(168, 85, 247, 0.32) !important; }
369
+ [data-theme="dark"] .border-purple-300,
370
+ [data-theme="dark"] .hover\:border-purple-300:hover { border-color: rgba(168, 85, 247, 0.45) !important; }
371
+ [data-theme="dark"] .text-purple-600 { color: #c084fc !important; }
372
+ [data-theme="dark"] .text-purple-800,
373
+ [data-theme="dark"] .text-purple-900 { color: #e9d5ff !important; }
374
+ [data-theme="dark"] .bg-violet-100 { background-color: rgba(139, 92, 246, 0.18) !important; }
375
+ [data-theme="dark"] .border-violet-300 { border-color: rgba(139, 92, 246, 0.45) !important; }
376
+ [data-theme="dark"] .text-violet-800 { color: #ddd6fe !important; }
377
+
378
+ /* Pink / rose */
379
+ [data-theme="dark"] .bg-pink-100 { background-color: rgba(236, 72, 153, 0.18) !important; }
380
+ [data-theme="dark"] .border-pink-200 { border-color: rgba(236, 72, 153, 0.32) !important; }
381
+ [data-theme="dark"] .border-pink-300 { border-color: rgba(236, 72, 153, 0.45) !important; }
382
+ [data-theme="dark"] .text-pink-600 { color: #f472b6 !important; }
383
+ [data-theme="dark"] .text-pink-800,
384
+ [data-theme="dark"] .text-pink-900 { color: #fbcfe8 !important; }
385
+ [data-theme="dark"] .bg-rose-100 { background-color: rgba(244, 63, 94, 0.18) !important; }
386
+ [data-theme="dark"] .text-rose-700 { color: #fda4af !important; }
387
+ [data-theme="dark"] .text-rose-800 { color: #fecdd3 !important; }
388
+
389
+ /* Indigo / slate */
390
+ [data-theme="dark"] .bg-indigo-50 { background-color: rgba(99, 102, 241, 0.15) !important; }
391
+ [data-theme="dark"] .hover\:bg-indigo-100:hover { background-color: rgba(99, 102, 241, 0.25) !important; }
392
+ [data-theme="dark"] .text-indigo-600 { color: #a5b4fc !important; }
393
+ [data-theme="dark"] .border-indigo-300 { border-color: rgba(99, 102, 241, 0.45) !important; }
394
+ [data-theme="dark"] .text-slate-700 { color: #cbd5e1 !important; } /* slate-600/800 left for the Calculator LCD */
395
+ [data-theme="dark"] .border-slate-300 { border-color: rgba(148, 163, 184, 0.35) !important; }
396
+
397
+ /* Blue — gaps beyond the accent remaps above. Hover/active steps sit one
398
+ ladder step above their resting tint (base .26 → active .30; the /30../50
399
+ alpha ladder steps 0.10/0.12/0.15 → hovers 0.12/0.15/0.18). */
400
+ [data-theme="dark"] .bg-blue-200 { background-color: rgba(59, 130, 246, 0.35) !important; }
401
+ [data-theme="dark"] .active\:bg-blue-200:active { background-color: rgba(59, 130, 246, 0.40) !important; }
402
+ [data-theme="dark"] .bg-blue-200\/60 { background-color: rgba(59, 130, 246, 0.20) !important; }
403
+ [data-theme="dark"] .hover\:bg-blue-100:hover { background-color: rgba(59, 130, 246, 0.26) !important; }
404
+ [data-theme="dark"] .active\:bg-blue-50:active { background-color: rgba(59, 130, 246, 0.30) !important; }
405
+ [data-theme="dark"] .hover\:bg-blue-50\/30:hover { background-color: rgba(59, 130, 246, 0.12) !important; }
406
+ [data-theme="dark"] .hover\:bg-blue-50\/40:hover { background-color: rgba(59, 130, 246, 0.15) !important; }
407
+ [data-theme="dark"] .hover\:bg-blue-50\/50:hover { background-color: rgba(59, 130, 246, 0.18) !important; }
408
+ [data-theme="dark"] .border-blue-100 { border-color: rgba(59, 130, 246, 0.22) !important; }
409
+ [data-theme="dark"] .border-blue-200,
410
+ [data-theme="dark"] .hover\:border-blue-200:hover { border-color: rgba(59, 130, 246, 0.32) !important; }
411
+ [data-theme="dark"] .border-blue-300,
412
+ [data-theme="dark"] .hover\:border-blue-300:hover { border-color: rgba(59, 130, 246, 0.45) !important; }
413
+ [data-theme="dark"] .border-blue-300\/50 { border-color: rgba(59, 130, 246, 0.25) !important; }
414
+ [data-theme="dark"] .text-blue-900 { color: #bfdbfe !important; }
415
+ [data-theme="dark"] .hover\:text-blue-600:hover { color: #93c5fd !important; }
416
+ [data-theme="dark"] .hover\:text-blue-800:hover { color: #bfdbfe !important; }
417
+ /* File-input buttons (file: compiles to ::file-selector-button) */
418
+ [data-theme="dark"] .file\:bg-blue-50::file-selector-button { background-color: rgba(59, 130, 246, 0.26) !important; }
419
+ [data-theme="dark"] .file\:text-blue-700::file-selector-button { color: #bfdbfe !important; }
420
+ [data-theme="dark"] .hover\:file\:bg-blue-100:hover::file-selector-button { background-color: rgba(59, 130, 246, 0.26) !important; }
421
+
422
+ /* Gray interaction-state variants (separate class names from the bare remaps) */
423
+ [data-theme="dark"] .hover\:text-gray-600:hover,
424
+ [data-theme="dark"] .hover\:text-gray-700:hover { color: #a6adc8 !important; }
425
+ [data-theme="dark"] .hover\:text-gray-800:hover { color: #bac2de !important; }
426
+ [data-theme="dark"] .hover\:text-gray-900:hover,
427
+ [data-theme="dark"] .active\:text-gray-900:active { color: #cdd6f4 !important; }
428
+ [data-theme="dark"] .active\:bg-gray-100:active { background-color: #313244 !important; }
429
+ [data-theme="dark"] .active\:bg-gray-200:active { background-color: #45475a !important; }
430
+ [data-theme="dark"] .active\:bg-gray-300:active,
431
+ [data-theme="dark"] .hover\:bg-gray-300:hover { background-color: #585b70 !important; }
432
+ [data-theme="dark"] .disabled\:bg-gray-100:disabled { background-color: #1e1e2e !important; }
433
+ [data-theme="dark"] .disabled\:bg-gray-200:disabled { background-color: #313244 !important; }
434
+ [data-theme="dark"] .hover\:bg-gray-200\/50:hover { background-color: rgba(69, 71, 90, 0.5) !important; }
435
+ [data-theme="dark"] .even\:bg-gray-50\/50:nth-child(even) { background-color: rgba(24, 24, 37, 0.5) !important; }
436
+ [data-theme="dark"] .border-gray-50,
437
+ [data-theme="dark"] .divide-gray-50 > :not(:last-child) { border-color: #2a2a3c !important; }
438
+ [data-theme="dark"] .border-gray-200\/50 { border-color: rgba(49, 50, 68, 0.6) !important; }
439
+ [data-theme="dark"] .hover\:border-gray-200:hover { border-color: #313244 !important; }
440
+ [data-theme="dark"] .hover\:border-gray-300:hover { border-color: #45475a !important; }
441
+
442
+ /* White panel surfaces / solid black ink. hover-to-white means "emphasize",
443
+ which on dark glass means a solid panel tint; hover-to-black ink flips to
444
+ brighter, not darker (e.g. the taskbar bell). */
445
+ [data-theme="dark"] .bg-white\/85 { background-color: rgba(30, 30, 46, 0.85) !important; }
446
+ [data-theme="dark"] .hover\:bg-white:hover { background-color: #1e1e2e !important; }
447
+ [data-theme="dark"] .text-black { color: #cdd6f4 !important; }
448
+ [data-theme="dark"] .hover\:text-black:hover { color: #ffffff !important; }
449
+
265
450
  /* Scrollbar */
266
451
  /* Force visible scrollbars on editable grids (macOS hides them by default) */
267
452
  .grid-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
@@ -0,0 +1,244 @@
1
+ /**
2
+ * react-os-shell theme variants — accent + surface-tint remaps for the
3
+ * pink / green / grey / blue themes and the user-picked custom accent.
4
+ *
5
+ * useTheme() stamps `data-theme="<name>"` on <html> and, when the user
6
+ * picks a custom accent color, `data-custom-accent` plus the --accent-*
7
+ * shade scale. This file turns those attributes into looks by remapping
8
+ * the blue accent utilities (bg-blue-600 buttons, text-blue-700 links,
9
+ * focus rings, checkbox accents) and tinting the neutral gray surfaces.
10
+ *
11
+ * styles.css imports this file, so plain `import 'react-os-shell/styles.css'`
12
+ * already includes it. It is exported standalone ("react-os-shell/themes.css")
13
+ * only for consumers that build their CSS granularly.
14
+ *
15
+ * Dark-theme remaps are NOT here — they live in styles.css, because dark
16
+ * is a readability baseline rather than an accent variant.
17
+ *
18
+ * Upstreamed 2026-06-13 from the admin portal's index.css so all three
19
+ * EFFICIENT portals + the demo share one source of truth.
20
+ */
21
+
22
+ /* ═══════════════════════════════════════════════════
23
+ Theme: Pink
24
+ Swaps the blue accent to pink/rose throughout
25
+ ═══════════════════════════════════════════════════ */
26
+
27
+ /* Accent backgrounds */
28
+ [data-theme="pink"] .bg-blue-600 { background-color: #db2777 !important; }
29
+ [data-theme="pink"] .bg-blue-700 { background-color: #be185d !important; }
30
+ [data-theme="pink"] .bg-blue-500 { background-color: #ec4899 !important; }
31
+ [data-theme="pink"] .bg-blue-100 { background-color: #fce7f3 !important; }
32
+ [data-theme="pink"] .bg-blue-50 { background-color: #fdf2f8 !important; }
33
+
34
+ /* Accent text */
35
+ [data-theme="pink"] .text-blue-600 { color: #db2777 !important; }
36
+ [data-theme="pink"] .text-blue-700 { color: #be185d !important; }
37
+ [data-theme="pink"] .text-blue-800 { color: #9d174d !important; }
38
+ [data-theme="pink"] .text-blue-500 { color: #ec4899 !important; }
39
+
40
+ /* Accent borders */
41
+ [data-theme="pink"] .border-blue-600 { border-color: #db2777 !important; }
42
+ [data-theme="pink"] .border-blue-500 { border-color: #ec4899 !important; }
43
+ [data-theme="pink"] .border-blue-400 { border-color: #f472b6 !important; }
44
+ [data-theme="pink"] .border-blue-300 { border-color: #f9a8d4 !important; }
45
+ [data-theme="pink"] .border-blue-200 { border-color: #fbcfe8 !important; }
46
+
47
+ /* Accent hover */
48
+ [data-theme="pink"] .hover\:bg-blue-700:hover { background-color: #be185d !important; }
49
+ [data-theme="pink"] .hover\:bg-blue-100:hover { background-color: #fce7f3 !important; }
50
+ [data-theme="pink"] .hover\:bg-blue-50:hover { background-color: #fdf2f8 !important; }
51
+ [data-theme="pink"] .hover\:text-blue-800:hover { color: #9d174d !important; }
52
+ [data-theme="pink"] .hover\:border-blue-300:hover { border-color: #f9a8d4 !important; }
53
+
54
+ /* Focus rings */
55
+ [data-theme="pink"] .focus\:border-blue-500:focus { border-color: #ec4899 !important; }
56
+ [data-theme="pink"] .focus\:ring-blue-500:focus { --tw-ring-color: #ec4899 !important; }
57
+
58
+ /* Checkbox accent */
59
+ [data-theme="pink"] input[type="checkbox"].text-blue-600 { accent-color: #db2777; }
60
+ [data-theme="pink"] input[type="range"].accent-blue-600 { accent-color: #db2777; }
61
+
62
+ /* Ring colors */
63
+ [data-theme="pink"] .ring-blue-300 { --tw-ring-color: #f9a8d4 !important; }
64
+
65
+ /* Pink tint on backgrounds */
66
+ [data-theme="pink"] .bg-white { background-color: #fff5f9 !important; }
67
+ [data-theme="pink"] .bg-gray-100 { background-color: #fce7f3 !important; }
68
+ [data-theme="pink"] .bg-gray-50 { background-color: #fdf2f8 !important; }
69
+ [data-theme="pink"] .bg-gray-200 { background-color: #fbcfe8 !important; }
70
+
71
+ /* Pink tint on borders — kept light to distinguish from accent */
72
+ [data-theme="pink"] .border-gray-200 { border-color: #fce7f3 !important; }
73
+ [data-theme="pink"] .border-gray-300 { border-color: #fbcfe8 !important; }
74
+ [data-theme="pink"] .border-gray-100 { border-color: #fdf2f8 !important; }
75
+ [data-theme="pink"] .divide-gray-200 > :not(:last-child) { border-color: #fce7f3 !important; }
76
+ [data-theme="pink"] .divide-gray-100 > :not(:last-child) { border-color: #fdf2f8 !important; }
77
+
78
+ /* Pink hover states */
79
+ [data-theme="pink"] .hover\:bg-gray-50:hover { background-color: #fdf2f8 !important; }
80
+ [data-theme="pink"] .hover\:bg-gray-100:hover { background-color: #fce7f3 !important; }
81
+ [data-theme="pink"] .hover\:bg-gray-200:hover { background-color: #fbcfe8 !important; }
82
+
83
+
84
+ /* ═══════════════════════════════════════════════════
85
+ Theme: Green
86
+ Swaps the blue accent to green/emerald throughout
87
+ ═══════════════════════════════════════════════════ */
88
+
89
+ /* Accent backgrounds */
90
+ [data-theme="green"] .bg-blue-600 { background-color: #059669 !important; }
91
+ [data-theme="green"] .bg-blue-700 { background-color: #047857 !important; }
92
+ [data-theme="green"] .bg-blue-500 { background-color: #10b981 !important; }
93
+ [data-theme="green"] .bg-blue-100 { background-color: #d1fae5 !important; }
94
+ [data-theme="green"] .bg-blue-50 { background-color: #ecfdf5 !important; }
95
+
96
+ /* Accent text */
97
+ [data-theme="green"] .text-blue-600 { color: #059669 !important; }
98
+ [data-theme="green"] .text-blue-700 { color: #047857 !important; }
99
+ [data-theme="green"] .text-blue-800 { color: #065f46 !important; }
100
+ [data-theme="green"] .text-blue-500 { color: #10b981 !important; }
101
+
102
+ /* Accent borders */
103
+ [data-theme="green"] .border-blue-600 { border-color: #059669 !important; }
104
+ [data-theme="green"] .border-blue-500 { border-color: #10b981 !important; }
105
+ [data-theme="green"] .border-blue-400 { border-color: #34d399 !important; }
106
+ [data-theme="green"] .border-blue-300 { border-color: #6ee7b7 !important; }
107
+ [data-theme="green"] .border-blue-200 { border-color: #a7f3d0 !important; }
108
+
109
+ /* Accent hover */
110
+ [data-theme="green"] .hover\:bg-blue-700:hover { background-color: #047857 !important; }
111
+ [data-theme="green"] .hover\:bg-blue-100:hover { background-color: #d1fae5 !important; }
112
+ [data-theme="green"] .hover\:bg-blue-50:hover { background-color: #ecfdf5 !important; }
113
+ [data-theme="green"] .hover\:text-blue-800:hover { color: #065f46 !important; }
114
+ [data-theme="green"] .hover\:border-blue-300:hover { border-color: #6ee7b7 !important; }
115
+
116
+ /* Focus rings */
117
+ [data-theme="green"] .focus\:border-blue-500:focus { border-color: #10b981 !important; }
118
+ [data-theme="green"] .focus\:ring-blue-500:focus { --tw-ring-color: #10b981 !important; }
119
+
120
+ /* Checkbox accent */
121
+ [data-theme="green"] input[type="checkbox"].text-blue-600 { accent-color: #059669; }
122
+ [data-theme="green"] input[type="range"].accent-blue-600 { accent-color: #059669; }
123
+
124
+ /* Ring colors */
125
+ [data-theme="green"] .ring-blue-300 { --tw-ring-color: #6ee7b7 !important; }
126
+
127
+ /* Green tint on backgrounds */
128
+ [data-theme="green"] .bg-white { background-color: #f5fef7 !important; }
129
+ [data-theme="green"] .bg-gray-100 { background-color: #dcfce7 !important; }
130
+ [data-theme="green"] .bg-gray-50 { background-color: #ecfdf5 !important; }
131
+ [data-theme="green"] .bg-gray-200 { background-color: #bbf7d0 !important; }
132
+
133
+ /* Green tint on borders — kept light to distinguish from accent */
134
+ [data-theme="green"] .border-gray-200 { border-color: #dcfce7 !important; }
135
+ [data-theme="green"] .border-gray-300 { border-color: #bbf7d0 !important; }
136
+ [data-theme="green"] .border-gray-100 { border-color: #ecfdf5 !important; }
137
+ [data-theme="green"] .divide-gray-200 > :not(:last-child) { border-color: #dcfce7 !important; }
138
+ [data-theme="green"] .divide-gray-100 > :not(:last-child) { border-color: #ecfdf5 !important; }
139
+
140
+ /* Green hover states */
141
+ [data-theme="green"] .hover\:bg-gray-50:hover { background-color: #ecfdf5 !important; }
142
+ [data-theme="green"] .hover\:bg-gray-100:hover { background-color: #dcfce7 !important; }
143
+ [data-theme="green"] .hover\:bg-gray-200:hover { background-color: #bbf7d0 !important; }
144
+
145
+
146
+ /* ═══════════════════════════════════════════════════
147
+ Theme: Grey
148
+ A darker, more contrasty neutral theme
149
+ ═══════════════════════════════════════════════════ */
150
+
151
+ /* Grey tint on backgrounds */
152
+ [data-theme="grey"] .bg-white { background-color: #f3f4f6 !important; }
153
+ [data-theme="grey"] .bg-gray-100 { background-color: #e5e7eb !important; }
154
+ [data-theme="grey"] .bg-gray-50 { background-color: #f3f4f6 !important; }
155
+ [data-theme="grey"] .bg-gray-200 { background-color: #d1d5db !important; }
156
+
157
+ /* Grey tint on borders */
158
+ [data-theme="grey"] .border-gray-200 { border-color: #d1d5db !important; }
159
+ [data-theme="grey"] .border-gray-300 { border-color: #9ca3af !important; }
160
+ [data-theme="grey"] .border-gray-100 { border-color: #e5e7eb !important; }
161
+ [data-theme="grey"] .divide-gray-200 > :not(:last-child) { border-color: #d1d5db !important; }
162
+ [data-theme="grey"] .divide-gray-100 > :not(:last-child) { border-color: #e5e7eb !important; }
163
+
164
+ /* Grey hover states */
165
+ [data-theme="grey"] .hover\:bg-gray-50:hover { background-color: #e5e7eb !important; }
166
+ [data-theme="grey"] .hover\:bg-gray-100:hover { background-color: #d1d5db !important; }
167
+ [data-theme="grey"] .hover\:bg-gray-200:hover { background-color: #9ca3af !important; }
168
+
169
+ /* Grey accent — dark gray buttons */
170
+ [data-theme="grey"] .bg-blue-600 { background-color: #374151 !important; }
171
+ [data-theme="grey"] .bg-blue-700 { background-color: #1f2937 !important; }
172
+ [data-theme="grey"] .bg-blue-500 { background-color: #4b5563 !important; }
173
+ [data-theme="grey"] .bg-blue-100 { background-color: #e5e7eb !important; }
174
+ [data-theme="grey"] .bg-blue-50 { background-color: #f3f4f6 !important; }
175
+ [data-theme="grey"] .text-blue-600 { color: #374151 !important; }
176
+ [data-theme="grey"] .text-blue-700 { color: #1f2937 !important; }
177
+ [data-theme="grey"] .text-blue-800 { color: #111827 !important; }
178
+ [data-theme="grey"] .text-blue-500 { color: #4b5563 !important; }
179
+ [data-theme="grey"] .border-blue-600 { border-color: #374151 !important; }
180
+ [data-theme="grey"] .border-blue-500 { border-color: #4b5563 !important; }
181
+ [data-theme="grey"] .border-blue-400 { border-color: #6b7280 !important; }
182
+ [data-theme="grey"] .border-blue-300 { border-color: #9ca3af !important; }
183
+ [data-theme="grey"] .border-blue-200 { border-color: #d1d5db !important; }
184
+ [data-theme="grey"] .hover\:bg-blue-700:hover { background-color: #1f2937 !important; }
185
+ [data-theme="grey"] .hover\:bg-blue-100:hover { background-color: #e5e7eb !important; }
186
+ [data-theme="grey"] .hover\:bg-blue-50:hover { background-color: #f3f4f6 !important; }
187
+ [data-theme="grey"] .focus\:border-blue-500:focus { border-color: #4b5563 !important; }
188
+ [data-theme="grey"] .focus\:ring-blue-500:focus { --tw-ring-color: #4b5563 !important; }
189
+ [data-theme="grey"] input[type="checkbox"].text-blue-600 { accent-color: #374151; }
190
+ [data-theme="grey"] input[type="range"].accent-blue-600 { accent-color: #374151; }
191
+ [data-theme="grey"] .ring-blue-300 { --tw-ring-color: #9ca3af !important; }
192
+
193
+
194
+ /* ═══════════════════════════════════════════════════
195
+ Theme: Blue
196
+ Swaps the blue accent to a deeper blue, tints UI blue
197
+ ═══════════════════════════════════════════════════ */
198
+
199
+ /* Blue tint on backgrounds */
200
+ [data-theme="blue"] .bg-white { background-color: #f0f6ff !important; }
201
+ [data-theme="blue"] .bg-gray-100 { background-color: #dbeafe !important; }
202
+ [data-theme="blue"] .bg-gray-50 { background-color: #eff6ff !important; }
203
+ [data-theme="blue"] .bg-gray-200 { background-color: #bfdbfe !important; }
204
+
205
+ /* Blue tint on borders — kept light to distinguish from accent */
206
+ [data-theme="blue"] .border-gray-200 { border-color: #dbeafe !important; }
207
+ [data-theme="blue"] .border-gray-300 { border-color: #bfdbfe !important; }
208
+ [data-theme="blue"] .border-gray-100 { border-color: #eff6ff !important; }
209
+ [data-theme="blue"] .divide-gray-200 > :not(:last-child) { border-color: #dbeafe !important; }
210
+ [data-theme="blue"] .divide-gray-100 > :not(:last-child) { border-color: #eff6ff !important; }
211
+
212
+ /* Blue hover states */
213
+ [data-theme="blue"] .hover\:bg-gray-50:hover { background-color: #eff6ff !important; }
214
+ [data-theme="blue"] .hover\:bg-gray-100:hover { background-color: #dbeafe !important; }
215
+ [data-theme="blue"] .hover\:bg-gray-200:hover { background-color: #bfdbfe !important; }
216
+
217
+
218
+ /* ═══════════════════════════════════════════════════
219
+ Custom Accent Color
220
+ Overrides the blue accent when user picks a custom color.
221
+ useTheme() sets data-custom-accent + the --accent-* scale.
222
+ ═══════════════════════════════════════════════════ */
223
+ [data-custom-accent] .bg-blue-600 { background-color: var(--accent-600) !important; }
224
+ [data-custom-accent] .bg-blue-700 { background-color: var(--accent-700) !important; }
225
+ [data-custom-accent] .bg-blue-500 { background-color: var(--accent-500) !important; }
226
+ [data-custom-accent] .bg-blue-100 { background-color: var(--accent-100) !important; }
227
+ [data-custom-accent] .bg-blue-50 { background-color: var(--accent-50) !important; }
228
+ [data-custom-accent] .text-blue-600 { color: var(--accent-600) !important; }
229
+ [data-custom-accent] .text-blue-700 { color: var(--accent-700) !important; }
230
+ [data-custom-accent] .text-blue-800 { color: var(--accent-700) !important; }
231
+ [data-custom-accent] .text-blue-500 { color: var(--accent-500) !important; }
232
+ [data-custom-accent] .border-blue-600 { border-color: var(--accent-600) !important; }
233
+ [data-custom-accent] .border-blue-500 { border-color: var(--accent-500) !important; }
234
+ [data-custom-accent] .border-blue-400 { border-color: var(--accent-400) !important; }
235
+ [data-custom-accent] .border-blue-300 { border-color: var(--accent-300) !important; }
236
+ [data-custom-accent] .border-blue-200 { border-color: var(--accent-200) !important; }
237
+ [data-custom-accent] .hover\:bg-blue-700:hover { background-color: var(--accent-700) !important; }
238
+ [data-custom-accent] .hover\:bg-blue-100:hover { background-color: var(--accent-100) !important; }
239
+ [data-custom-accent] .hover\:bg-blue-50:hover { background-color: var(--accent-50) !important; }
240
+ [data-custom-accent] .focus\:border-blue-500:focus { border-color: var(--accent-500) !important; }
241
+ [data-custom-accent] .focus\:ring-blue-500:focus { --tw-ring-color: var(--accent-500) !important; }
242
+ [data-custom-accent] input[type="checkbox"].text-blue-600 { accent-color: var(--accent-600); }
243
+ [data-custom-accent] input[type="range"].accent-blue-600 { accent-color: var(--accent-600); }
244
+ [data-custom-accent] .ring-blue-300 { --tw-ring-color: var(--accent-300) !important; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-os-shell",
3
- "version": "2.7.0",
3
+ "version": "2.8.1",
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",
@@ -28,7 +28,8 @@
28
28
  "types": "./dist/apps/index.d.ts",
29
29
  "import": "./dist/apps/index.js"
30
30
  },
31
- "./styles.css": "./dist/styles.css"
31
+ "./styles.css": "./dist/styles.css",
32
+ "./themes.css": "./dist/themes.css"
32
33
  },
33
34
  "files": [
34
35
  "dist"
@@ -65,7 +66,7 @@
65
66
  "xlsx": "^0.18.5"
66
67
  },
67
68
  "scripts": {
68
- "build": "tsup && cp src/styles.css dist/styles.css",
69
+ "build": "tsup && cp src/styles.css src/themes.css dist/",
69
70
  "dev": "tsup --watch",
70
71
  "typecheck": "tsc --noEmit",
71
72
  "screenshot": "node scripts/screenshot.mjs",
@@ -1,9 +0,0 @@
1
- export { Preview as default, setPdfPreview } from './chunk-UATWDGLV.js';
2
- import './chunk-KUIPWCTJ.js';
3
- import './chunk-WIJ45SYD.js';
4
- import './chunk-MJIMKMSJ.js';
5
- import './chunk-JNF5VRPB.js';
6
- import './chunk-UBN4IUDE.js';
7
- import './chunk-ZF6AYO4G.js';
8
- //# sourceMappingURL=Preview-5SOLJFFK.js.map
9
- //# sourceMappingURL=Preview-5SOLJFFK.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shell/Sidebar.tsx"],"names":["navSections","navIcons","sectionIcons"],"mappings":"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","file":"Sidebar-BW7SYNBA.js","sourcesContent":["/**\n * Sidebar — persistent left strip used when `prefs.layout_mode === 'sidebar'`.\n *\n * Same configuration surface as <StartMenu> (navSections, navIcons,\n * sectionIcons, categories) but rendered inline:\n * - No flyouts. Sections expand/collapse accordion-style below their\n * header, indented one step.\n * - No taskbar-anchored positioning — fixed full-height left strip,\n * width pulled from `--sidebar-width`.\n * - Right edge `rounded-r-2xl` so it matches windowed cards on the\n * right (which use `rounded-2xl`).\n *\n * Designed for small-screen layouts where flyouts would clip and where\n * keeping the menu always-visible saves a tap to switch apps.\n */\n\nimport { useEffect, useMemo, useRef, useState, isValidElement, cloneElement, type ReactElement, type ReactNode } from 'react';\nimport {\n navSections as defaultNavSections,\n navIcons as defaultNavIcons,\n sectionIcons as defaultSectionIcons,\n startMenuCategories as defaultCategories,\n isSection,\n type NavSection,\n type NavItem,\n type StartMenuCategories,\n type VirtualSection,\n} from '../shell-config/nav';\nimport { useAuth } from '../contexts/AuthContext';\nimport { glassStyle, GLASS_INPUT_BG } from '../utils/glass';\n\ninterface SidebarProps {\n width: number;\n openPage: (path: string) => void;\n profile: any;\n user: any;\n onLogout: () => void;\n onNavigate: (path: string) => void;\n navSections?: (NavSection | NavItem)[];\n navIcons?: Record<string, ReactNode>;\n sectionIcons?: Record<string, ReactNode>;\n categories?: StartMenuCategories;\n productName?: string;\n productIcon?: string;\n}\n\nexport default function Sidebar({\n width,\n openPage,\n profile,\n user,\n onLogout,\n onNavigate,\n navSections = defaultNavSections,\n navIcons = defaultNavIcons,\n sectionIcons = defaultSectionIcons,\n categories = defaultCategories,\n productName,\n productIcon,\n}: SidebarProps) {\n const { hasAnyPerm } = useAuth();\n const erpLabels = new Set(categories.erp);\n const systemLabels = new Set(categories.system);\n const footerLabels = new Set(categories.footer ?? []);\n // Flat rows pinned to the footer (next to the profile) — rendered inline,\n // not as an accordion section. Mirrors StartMenu's `footerItems`.\n const footerItems = (categories.footerItems ?? []).filter(it => !it.perms || hasAnyPerm(it.perms));\n const virtualSections = categories.virtual ?? [];\n\n const [search, setSearch] = useState('');\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n const searchRef = useRef<HTMLInputElement>(null);\n\n const toggleExpanded = (label: string) => {\n setExpanded(prev => {\n const next = new Set(prev);\n if (next.has(label)) next.delete(label);\n else next.add(label);\n return next;\n });\n };\n\n // Top-level items vs sections, mirroring StartMenu's split.\n const topItems = navSections.filter(item => !isSection(item)) as NavItem[];\n const erpSections = navSections.filter(item => isSection(item) && erpLabels.has((item as NavSection).label)) as NavSection[];\n const systemSections = navSections.filter(item => isSection(item) && systemLabels.has((item as NavSection).label)) as NavSection[];\n const footerSections = navSections.filter(item => isSection(item) && footerLabels.has((item as NavSection).label)) as NavSection[];\n\n const getVisibleItems = (section: { items: NavItem[]; perms?: string[] }) => {\n if (section.perms && !hasAnyPerm(section.perms)) return [];\n return section.items.filter(it => !it.perms || hasAnyPerm(it.perms));\n };\n\n // Search across all items + sections (same flat list StartMenu uses).\n // Walks 3rd-level children too so nested entries are still discoverable.\n const matchTree = (it: NavItem, q: string): NavItem[] => {\n if (it.perms && !hasAnyPerm(it.perms)) return [];\n const hits: NavItem[] = [];\n if (it.label.toLowerCase().includes(q)) hits.push(it);\n if (it.children) {\n for (const c of it.children) hits.push(...matchTree(c, q));\n }\n return hits;\n };\n const searchResults = useMemo(() => {\n if (search.length < 2) return [] as NavItem[];\n const q = search.toLowerCase();\n return [\n ...navSections.flatMap((entry) => {\n if (isSection(entry)) {\n return getVisibleItems(entry).flatMap(it => matchTree(it, q));\n }\n return matchTree(entry as NavItem, q);\n }),\n ...footerItems.flatMap(it => matchTree(it, q)),\n ];\n }, [search, navSections, footerItems, hasAnyPerm]);\n\n // Esc collapses any expanded section + clears search; '/' focuses search.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setExpanded(new Set());\n setSearch('');\n } else if (e.key === '/' && document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {\n e.preventDefault();\n searchRef.current?.focus();\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, []);\n\n const handleClick = (path: string) => {\n onNavigate(path);\n onPageOpenedReset();\n };\n\n // Reset search after navigating so the next visit starts clean.\n const onPageOpenedReset = () => {\n setSearch('');\n };\n\n const itemCls = 'w-full flex items-center gap-2 rounded-lg px-3 py-2 text-sm';\n const menuGlass = glassStyle();\n\n // Helper that returns the (possibly recolored) per-route icon.\n const iconEl = (to: string) => {\n const icon = navIcons[to];\n if (icon && isValidElement(icon)) {\n return cloneElement(icon as ReactElement<{ className?: string }>, {\n className: 'h-4 w-4 shrink-0 text-gray-500',\n });\n }\n return <span className=\"h-4 w-4 shrink-0\" />;\n };\n\n const secIcon = (label: string) => {\n const icon = sectionIcons[label];\n if (icon && isValidElement(icon)) {\n return cloneElement(icon as ReactElement<{ className?: string }>, {\n className: 'h-4 w-4 shrink-0 text-gray-500',\n });\n }\n return <span className=\"h-4 w-4 shrink-0\" />;\n };\n\n const renderItem = (item: NavItem) => (\n <div key={item.to}>\n <button\n onClick={() => handleClick(item.to)}\n className={`${itemCls} text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors`}\n >\n {iconEl(item.to)}\n <span className=\"truncate\">{item.label}</span>\n </button>\n {item.dividerAfter && <div className=\"border-t border-white/20 my-1.5 mx-2\" />}\n </div>\n );\n\n // 3rd-level: when a NavItem inside an accordion has children, render the\n // parent as its own mini-accordion (further indented). Expansion key is\n // `child:<to>` so it doesn't clash with the section labels in `expanded`.\n const renderNestedItem = (item: NavItem) => {\n const kids = (item.children ?? []).filter(c => !c.perms || hasAnyPerm(c.perms));\n if (kids.length === 0) return renderItem(item);\n const key = `child:${item.to}`;\n const isOpen = expanded.has(key);\n return (\n <div key={item.to}>\n <button\n onClick={() => toggleExpanded(key)}\n aria-expanded={isOpen}\n className={`${itemCls} text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors`}\n >\n {iconEl(item.to)}\n <span className=\"truncate\">{item.label}</span>\n <svg\n className={`h-3.5 w-3.5 ml-auto text-gray-500 transition-transform ${isOpen ? 'rotate-90' : ''}`}\n fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n </button>\n {isOpen && (\n <div className=\"pl-4 mt-0.5 mb-1 space-y-0.5\">\n {kids.map(c => (\n <button\n key={c.to}\n onClick={() => handleClick(c.to)}\n className={`${itemCls} text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors`}\n >\n {iconEl(c.to)}\n <span className=\"truncate\">{c.label}</span>\n </button>\n ))}\n </div>\n )}\n {item.dividerAfter && <div className=\"border-t border-white/20 my-1.5 mx-2\" />}\n </div>\n );\n };\n\n const renderSectionAccordion = (section: NavSection | VirtualSection, isErp: boolean) => {\n const items = 'perms' in section\n ? getVisibleItems(section as NavSection)\n : (section as VirtualSection).items;\n if (items.length === 0) return null;\n const isOpen = expanded.has(section.label);\n return (\n <div key={section.label}>\n <button\n onClick={() => toggleExpanded(section.label)}\n aria-expanded={isOpen}\n className={`${itemCls} text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors`}\n >\n {'icon' in section && section.icon\n ? section.icon\n : secIcon(section.label)}\n <span className={`truncate ${isErp ? 'font-medium' : ''}`}>{section.label}</span>\n <svg\n className={`h-3.5 w-3.5 ml-auto text-gray-500 transition-transform ${isOpen ? 'rotate-90' : ''}`}\n fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n </button>\n {isOpen && (\n <div className=\"pl-4 mt-0.5 mb-1 space-y-0.5\">\n {items.map(it => renderNestedItem(it))}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div\n className=\"fixed top-0 left-0 bottom-0 z-[260] flex flex-col rounded-r-2xl overflow-hidden\"\n style={{ width, ...menuGlass }}\n >\n {/* Brand */}\n <div className=\"flex items-center gap-2 px-4 py-3 border-b border-white/15 shrink-0\">\n {productIcon && <img src={productIcon} alt=\"\" className=\"h-5 w-5 shrink-0 opacity-80\" />}\n <span className=\"text-sm font-semibold text-gray-800 truncate\">{productName ?? 'Apps'}</span>\n </div>\n\n {/* Search */}\n <div className=\"px-3 pt-3 pb-2 shrink-0\">\n <div className={`flex items-center gap-2 ${GLASS_INPUT_BG} rounded-lg px-2.5 py-1.5`}>\n <svg className=\"h-3.5 w-3.5 text-gray-400 shrink-0\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z\" />\n </svg>\n <input\n ref={searchRef}\n value={search}\n onChange={e => setSearch(e.target.value)}\n placeholder=\"Search...\"\n className=\"flex-1 bg-transparent text-xs outline-none placeholder-gray-400\"\n />\n {search && (\n <button onClick={() => setSearch('')} className=\"text-gray-400 hover:text-gray-600 text-xs\">×</button>\n )}\n </div>\n </div>\n\n {/* Body */}\n <div className=\"flex-1 overflow-y-auto px-1 pb-1\">\n {search.length >= 2 ? (\n // Search results take over the body.\n <div>\n {searchResults.length === 0 ? (\n <div className=\"px-3 py-6 text-center text-xs text-gray-400\">No matches</div>\n ) : (\n searchResults.map(r => (\n <button\n key={r.to}\n onClick={() => handleClick(r.to)}\n className={`${itemCls} text-gray-700 hover:bg-blue-50 hover:text-blue-700 transition-colors`}\n >\n {iconEl(r.to)}\n <span className=\"truncate\">{r.label}</span>\n </button>\n ))\n )}\n </div>\n ) : (\n <>\n {/* Top-level apps */}\n {topItems.map(renderItem)}\n\n <div className=\"border-t border-white/15 my-1.5 mx-2\" />\n\n {/* ERP sections then system sections — same order as StartMenu's vertical layout. */}\n {erpSections.map(s => renderSectionAccordion(s, true))}\n {systemSections.map(s => renderSectionAccordion(s, false))}\n {virtualSections.map(v => renderSectionAccordion(v, false))}\n {/* Footer items + sections: pinned just above the profile, divided from the rest. */}\n {(footerSections.length > 0 || footerItems.length > 0) && <div className=\"border-t border-white/15 my-1.5 mx-2\" />}\n {footerSections.map(s => renderSectionAccordion(s, false))}\n {footerItems.map(renderItem)}\n </>\n )}\n </div>\n\n {/* Profile + Sign out at the bottom — mirrors StartMenu's user row. */}\n <div className=\"border-t border-white/15 p-1 shrink-0\">\n <div\n onClick={() => handleClick('/profile')}\n className=\"rounded-lg px-2 py-1.5 flex items-center gap-2.5 hover:bg-blue-50 hover:text-blue-700 transition-colors cursor-pointer\"\n >\n {profile?.avatar_url ? (\n <img src={profile.avatar_url} alt=\"\" className=\"h-8 w-8 rounded-full object-cover border border-white/20 shrink-0\" />\n ) : (\n <div className=\"h-8 w-8 rounded-full bg-blue-100 flex items-center justify-center text-sm font-bold text-blue-700 shrink-0\">\n {(profile?.first_name?.charAt(0) || user?.email?.charAt(0) || '?').toUpperCase()}\n </div>\n )}\n <p className=\"flex-1 min-w-0 text-sm font-medium text-gray-900 truncate\">\n {profile?.first_name ? `${profile.first_name} ${profile.last_name || ''}`.trim() : user?.email}\n </p>\n <button\n onClick={e => { e.stopPropagation(); onLogout(); }}\n title=\"Sign Out\"\n className=\"shrink-0 p-1.5 rounded-md text-gray-500 hover:text-red-600 hover:bg-red-50 transition-colors\"\n >\n <svg className=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9\" />\n </svg>\n </button>\n </div>\n </div>\n </div>\n );\n}\n"]}