react-os-shell 0.1.5 → 0.1.6

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 (30) hide show
  1. package/dist/{Calculator-MBZQ67JD.js → Calculator-LIRYXAO6.js} +4 -4
  2. package/dist/{Calculator-MBZQ67JD.js.map → Calculator-LIRYXAO6.js.map} +1 -1
  3. package/dist/{Calendar-T3ICM2WO.js → Calendar-EPLEHSOJ.js} +3 -3
  4. package/dist/{Calendar-T3ICM2WO.js.map → Calendar-EPLEHSOJ.js.map} +1 -1
  5. package/dist/{CurrencyConverter-MBMRX5RJ.js → CurrencyConverter-DZWLRMAA.js} +4 -4
  6. package/dist/{CurrencyConverter-MBMRX5RJ.js.map → CurrencyConverter-DZWLRMAA.js.map} +1 -1
  7. package/dist/{Email-TDD2OOEQ.js → Email-4PYD3QBK.js} +3 -3
  8. package/dist/{Email-TDD2OOEQ.js.map → Email-4PYD3QBK.js.map} +1 -1
  9. package/dist/{Minesweeper-JOEK7UEM.js → Minesweeper-WD6O5YOD.js} +3 -3
  10. package/dist/{Minesweeper-JOEK7UEM.js.map → Minesweeper-WD6O5YOD.js.map} +1 -1
  11. package/dist/{Notepad-B7X2A6LE.js → Notepad-F3QYVJ36.js} +3 -3
  12. package/dist/{Notepad-B7X2A6LE.js.map → Notepad-F3QYVJ36.js.map} +1 -1
  13. package/dist/{PomodoroTimer-6GSD3YKY.js → PomodoroTimer-TADWMJCU.js} +4 -4
  14. package/dist/{PomodoroTimer-6GSD3YKY.js.map → PomodoroTimer-TADWMJCU.js.map} +1 -1
  15. package/dist/{Spreadsheet-SHJLUKS5.js → Spreadsheet-PZGPOSWV.js} +3 -3
  16. package/dist/{Spreadsheet-SHJLUKS5.js.map → Spreadsheet-PZGPOSWV.js.map} +1 -1
  17. package/dist/{Weather-MBZS6ZJ5.js → Weather-TOIFJCST.js} +4 -4
  18. package/dist/{Weather-MBZS6ZJ5.js.map → Weather-TOIFJCST.js.map} +1 -1
  19. package/dist/apps/index.d.ts +1 -1
  20. package/dist/apps/index.js +10 -10
  21. package/dist/apps/index.js.map +1 -1
  22. package/dist/{chunk-OVUT6VRS.js → chunk-2EBFERVD.js} +128 -42
  23. package/dist/chunk-2EBFERVD.js.map +1 -0
  24. package/dist/{chunk-2MNCN6VM.js → chunk-7JGV4RFC.js} +3 -3
  25. package/dist/{chunk-2MNCN6VM.js.map → chunk-7JGV4RFC.js.map} +1 -1
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.js +2 -2
  28. package/dist/{types-CFIZ1_xt.d.ts → types-BKoa7nhP.d.ts} +4 -0
  29. package/package.json +1 -1
  30. package/dist/chunk-OVUT6VRS.js.map +0 -1
@@ -1277,26 +1277,24 @@ function findPanelByLabel(label) {
1277
1277
  }
1278
1278
  return null;
1279
1279
  }
1280
- function TaskbarTabPreview({ label, anchorEl, onMouseEnter, onMouseLeave }) {
1280
+ function ThumbCard({ label, width, height, onClick, onClose }) {
1281
1281
  const previewRef = useRef(null);
1282
- const PREVIEW_W = 240;
1283
- const PREVIEW_H = 150;
1284
1282
  useEffect(() => {
1285
1283
  const inner = previewRef.current;
1286
1284
  if (!inner) return;
1287
1285
  const target = findPanelByLabel(label);
1288
1286
  if (!target) return;
1289
- const rect2 = target.getBoundingClientRect();
1290
- if (rect2.width === 0 || rect2.height === 0) return;
1291
- const scale = Math.min(PREVIEW_W / rect2.width, PREVIEW_H / rect2.height);
1287
+ const rect = target.getBoundingClientRect();
1288
+ if (rect.width === 0 || rect.height === 0) return;
1289
+ const scale = Math.min(width / rect.width, height / rect.height);
1292
1290
  const clone = target.cloneNode(true);
1293
1291
  clone.style.position = "absolute";
1294
1292
  clone.style.top = "0";
1295
1293
  clone.style.left = "0";
1296
1294
  clone.style.right = "auto";
1297
1295
  clone.style.bottom = "auto";
1298
- clone.style.width = rect2.width + "px";
1299
- clone.style.height = rect2.height + "px";
1296
+ clone.style.width = rect.width + "px";
1297
+ clone.style.height = rect.height + "px";
1300
1298
  clone.style.transform = `scale(${scale})`;
1301
1299
  clone.style.transformOrigin = "top left";
1302
1300
  clone.style.pointerEvents = "none";
@@ -1311,24 +1309,62 @@ function TaskbarTabPreview({ label, anchorEl, onMouseEnter, onMouseLeave }) {
1311
1309
  return () => {
1312
1310
  inner.innerHTML = "";
1313
1311
  };
1314
- }, [label]);
1312
+ }, [label, width, height]);
1313
+ return /* @__PURE__ */ jsxs(
1314
+ "div",
1315
+ {
1316
+ style: { width, height },
1317
+ className: "relative rounded-md overflow-hidden bg-white/95 border border-gray-300 shadow-md cursor-pointer hover:ring-2 hover:ring-blue-400 transition",
1318
+ onClick,
1319
+ children: [
1320
+ /* @__PURE__ */ jsx("div", { ref: previewRef, className: "absolute inset-0 overflow-hidden" }),
1321
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 px-2 py-1 text-[10px] font-medium text-white bg-gradient-to-t from-black/80 to-transparent truncate pointer-events-none", children: label }),
1322
+ onClose && /* @__PURE__ */ jsx(
1323
+ "button",
1324
+ {
1325
+ onClick: (e) => {
1326
+ e.stopPropagation();
1327
+ onClose();
1328
+ },
1329
+ className: "absolute top-1 right-1 h-4 w-4 rounded-full bg-black/40 hover:bg-red-500/90 text-white flex items-center justify-center",
1330
+ title: "Close window",
1331
+ children: /* @__PURE__ */ jsx("svg", { className: "h-2.5 w-2.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 3, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) })
1332
+ }
1333
+ )
1334
+ ]
1335
+ }
1336
+ );
1337
+ }
1338
+ function TaskbarTabPreview({ items, anchorEl, onActivate, onClose, onMouseEnter, onMouseLeave }) {
1339
+ const PREVIEW_W = 240;
1340
+ const PREVIEW_H = 150;
1341
+ const isGroup = items.length > 1;
1342
+ const totalWidth = isGroup ? Math.min(items.length * (PREVIEW_W + 8) + 8, window.innerWidth - 16) : PREVIEW_W;
1343
+ const totalHeight = isGroup ? PREVIEW_H + 16 : PREVIEW_H;
1315
1344
  const rect = anchorEl.getBoundingClientRect();
1316
1345
  const taskbarPos = getComputedStyle(document.documentElement).getPropertyValue("--taskbar-position")?.trim() || "bottom";
1317
- const left = Math.max(8, Math.min(rect.left + rect.width / 2 - PREVIEW_W / 2, window.innerWidth - PREVIEW_W - 8));
1318
- const top = taskbarPos === "top" ? rect.bottom + 8 : taskbarPos === "bottom" ? rect.top - PREVIEW_H - 8 : rect.top + rect.height / 2 - PREVIEW_H / 2;
1319
- const adjustedLeft = taskbarPos === "left" ? rect.right + 8 : taskbarPos === "right" ? rect.left - PREVIEW_W - 8 : left;
1346
+ const left = Math.max(8, Math.min(rect.left + rect.width / 2 - totalWidth / 2, window.innerWidth - totalWidth - 8));
1347
+ const top = taskbarPos === "top" ? rect.bottom + 8 : taskbarPos === "bottom" ? rect.top - totalHeight - 8 : rect.top + rect.height / 2 - totalHeight / 2;
1348
+ const adjustedLeft = taskbarPos === "left" ? rect.right + 8 : taskbarPos === "right" ? rect.left - totalWidth - 8 : left;
1320
1349
  return createPortal(
1321
- /* @__PURE__ */ jsxs(
1350
+ /* @__PURE__ */ jsx(
1322
1351
  "div",
1323
1352
  {
1324
- style: { position: "fixed", left: adjustedLeft, top, width: PREVIEW_W, height: PREVIEW_H, zIndex: 9999 },
1325
- className: "rounded-lg overflow-hidden bg-white/95 backdrop-blur-sm border border-gray-300 shadow-2xl",
1353
+ style: { position: "fixed", left: adjustedLeft, top, zIndex: 9999 },
1354
+ className: isGroup ? "flex gap-2 p-2 rounded-lg bg-white/40 backdrop-blur-sm border border-white/30 shadow-2xl" : "",
1326
1355
  onMouseEnter,
1327
1356
  onMouseLeave,
1328
- children: [
1329
- /* @__PURE__ */ jsx("div", { ref: previewRef, className: "absolute inset-0 overflow-hidden" }),
1330
- /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 px-2 py-1 text-[11px] font-medium text-white bg-gradient-to-t from-black/80 to-transparent truncate pointer-events-none", children: label })
1331
- ]
1357
+ children: items.map((it) => /* @__PURE__ */ jsx(
1358
+ ThumbCard,
1359
+ {
1360
+ label: it.label,
1361
+ width: PREVIEW_W,
1362
+ height: PREVIEW_H,
1363
+ onClick: () => onActivate(it.label),
1364
+ onClose: () => onClose(it.id)
1365
+ },
1366
+ it.id
1367
+ ))
1332
1368
  }
1333
1369
  ),
1334
1370
  document.body
@@ -1351,16 +1387,27 @@ function TaskbarWindows({ openWindows, onRemove, onCloseAll, onSplitView, onActi
1351
1387
  }
1352
1388
  }, []);
1353
1389
  const activeModalId = useSyncExternalStore(subscribeActive, getActiveModalId);
1354
- const [hoveredLabel, setHoveredLabel] = useState(null);
1390
+ const [, forceTick] = useState(0);
1391
+ useEffect(() => {
1392
+ const onTitle = () => forceTick((t) => t + 1);
1393
+ window.addEventListener("window-title-update", onTitle);
1394
+ return () => window.removeEventListener("window-title-update", onTitle);
1395
+ }, []);
1396
+ const liveTitle = (label) => {
1397
+ const panel = findPanelByLabel(label);
1398
+ const titleEl = panel?.querySelector(".text-lg, .text-sm.font-medium");
1399
+ return titleEl?.textContent?.trim() || label;
1400
+ };
1401
+ const [hoveredItems, setHoveredItems] = useState(null);
1355
1402
  const [hoveredAnchor, setHoveredAnchor] = useState(null);
1356
1403
  const hoverTimerRef = useRef(null);
1357
- const handleEnter = (label, el) => {
1404
+ const handleEnter = (items, el) => {
1358
1405
  if (hoverTimerRef.current) {
1359
1406
  clearTimeout(hoverTimerRef.current);
1360
1407
  hoverTimerRef.current = null;
1361
1408
  }
1362
1409
  hoverTimerRef.current = setTimeout(() => {
1363
- setHoveredLabel(label);
1410
+ setHoveredItems(items);
1364
1411
  setHoveredAnchor(el);
1365
1412
  }, 350);
1366
1413
  };
@@ -1370,7 +1417,7 @@ function TaskbarWindows({ openWindows, onRemove, onCloseAll, onSplitView, onActi
1370
1417
  hoverTimerRef.current = null;
1371
1418
  }
1372
1419
  hoverTimerRef.current = setTimeout(() => {
1373
- setHoveredLabel(null);
1420
+ setHoveredItems(null);
1374
1421
  setHoveredAnchor(null);
1375
1422
  }, 150);
1376
1423
  };
@@ -1382,45 +1429,63 @@ function TaskbarWindows({ openWindows, onRemove, onCloseAll, onSplitView, onActi
1382
1429
  };
1383
1430
  const tabWindows = openWindows.filter((item) => !item.route || !WINDOW_REGISTRY[item.route]?.utility);
1384
1431
  if (!target || tabWindows.length === 0) return null;
1432
+ const groups = [];
1433
+ const idx = /* @__PURE__ */ new Map();
1434
+ for (const item of tabWindows) {
1435
+ const key = item.route ?? `entity:${item.id}`;
1436
+ const i = idx.get(key);
1437
+ if (i !== void 0) {
1438
+ groups[i].items.push(item);
1439
+ } else {
1440
+ idx.set(key, groups.length);
1441
+ const registryLabel = item.route ? WINDOW_REGISTRY[item.route]?.label : void 0;
1442
+ groups.push({ key, route: item.route, label: registryLabel ?? item.label, items: [item] });
1443
+ }
1444
+ }
1385
1445
  return createPortal(
1386
1446
  /* @__PURE__ */ jsxs(Fragment, { children: [
1387
- tabWindows.map((item) => {
1388
- const icon = item.route ? navIcons[item.route] : null;
1447
+ groups.map((group) => {
1448
+ const icon = group.route ? navIcons[group.route] : null;
1449
+ const primary = group.items[group.items.length - 1];
1389
1450
  let isActive = false;
1390
1451
  if (activeModalId) {
1391
1452
  const panel = document.querySelector(`[data-modal-id="${activeModalId}"]`);
1392
1453
  if (panel) {
1393
1454
  const titleEl = panel.querySelector(".text-lg, .text-sm.font-medium");
1394
- if (titleEl?.textContent?.includes(item.label)) isActive = true;
1455
+ const titleText = titleEl?.textContent ?? "";
1456
+ isActive = group.items.some((it) => titleText.includes(it.label));
1395
1457
  }
1396
1458
  }
1459
+ const isGrouped = group.items.length > 1;
1397
1460
  return /* @__PURE__ */ jsxs(
1398
1461
  "button",
1399
1462
  {
1400
- onClick: () => onActivate(item.label),
1401
- onMouseEnter: (e) => handleEnter(item.label, e.currentTarget),
1463
+ onClick: () => onActivate(primary.label),
1464
+ onMouseEnter: (e) => handleEnter(group.items, e.currentTarget),
1402
1465
  onMouseLeave: handleLeave,
1403
1466
  onDoubleClick: (e) => {
1404
1467
  e.stopPropagation();
1405
- window.dispatchEvent(new CustomEvent("modal-center", { detail: { label: item.label } }));
1468
+ window.dispatchEvent(new CustomEvent("modal-center", { detail: { label: primary.label } }));
1406
1469
  },
1407
1470
  onContextMenu: (e) => {
1408
1471
  e.preventDefault();
1409
1472
  e.stopPropagation();
1410
- window.dispatchEvent(new CustomEvent("modal-context-menu", { detail: { label: item.label, x: e.clientX, y: e.clientY } }));
1473
+ window.dispatchEvent(new CustomEvent("modal-context-menu", { detail: { label: primary.label, x: e.clientX, y: e.clientY } }));
1411
1474
  },
1412
1475
  style: { width: "var(--window-tab-width, 200px)", fontSize: "var(--window-tab-font-size, 12px)" },
1413
- className: `group flex items-center gap-1.5 rounded-lg px-3 py-2 font-medium transition-all min-w-0 shrink ${isActive ? "bg-blue-100/60 border border-blue-400/60 text-blue-700" : "bg-gray-50/40 border border-gray-200/40 text-gray-700 hover:bg-gray-200/40"}`,
1476
+ "data-tab-group": group.key,
1477
+ className: `group relative flex items-center gap-1.5 rounded-lg px-3 py-2 font-medium transition-all min-w-0 shrink ${isActive ? "bg-blue-100/60 border border-blue-400/60 text-blue-700" : "bg-gray-50/40 border border-gray-200/40 text-gray-700 hover:bg-gray-200/40"}`,
1414
1478
  children: [
1415
1479
  icon && isValidElement(icon) ? cloneElement(icon, { className: `h-3.5 w-3.5 shrink-0 ${isActive ? "text-blue-600" : "text-gray-400"}` }) : /* @__PURE__ */ jsx("svg", { className: `h-3.5 w-3.5 shrink-0 ${isActive ? "text-blue-600" : "text-gray-400"}`, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" }) }),
1416
- /* @__PURE__ */ jsx("span", { className: "truncate flex-1", children: item.label }),
1417
- /* @__PURE__ */ jsx("span", { role: "button", onClick: (e) => {
1480
+ /* @__PURE__ */ jsx("span", { className: "truncate flex-1", children: isGrouped ? group.label : liveTitle(primary.label) }),
1481
+ isGrouped && /* @__PURE__ */ jsx("span", { className: "shrink-0 px-1.5 py-0.5 rounded-full bg-blue-500/80 text-white text-[10px] font-bold leading-none", children: group.items.length }),
1482
+ !isGrouped && /* @__PURE__ */ jsx("span", { role: "button", onClick: (e) => {
1418
1483
  e.stopPropagation();
1419
- onRemove(item.id);
1484
+ onRemove(primary.id);
1420
1485
  }, className: "ml-auto text-gray-400 hover:text-red-500 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) })
1421
1486
  ]
1422
1487
  },
1423
- item.id
1488
+ group.key
1424
1489
  );
1425
1490
  }),
1426
1491
  /* @__PURE__ */ jsx("div", { className: "flex-1" }),
@@ -1435,11 +1500,21 @@ function TaskbarWindows({ openWindows, onRemove, onCloseAll, onSplitView, onActi
1435
1500
  ]
1436
1501
  }
1437
1502
  ),
1438
- hoveredLabel && hoveredAnchor && /* @__PURE__ */ jsx(
1503
+ hoveredItems && hoveredAnchor && /* @__PURE__ */ jsx(
1439
1504
  TaskbarTabPreview,
1440
1505
  {
1441
- label: hoveredLabel,
1506
+ items: hoveredItems,
1442
1507
  anchorEl: hoveredAnchor,
1508
+ onActivate: (label) => {
1509
+ onActivate(label);
1510
+ setHoveredItems(null);
1511
+ setHoveredAnchor(null);
1512
+ },
1513
+ onClose: (id) => {
1514
+ onRemove(id);
1515
+ setHoveredItems(null);
1516
+ setHoveredAnchor(null);
1517
+ },
1443
1518
  onMouseEnter: cancelLeave,
1444
1519
  onMouseLeave: handleLeave
1445
1520
  }
@@ -1492,7 +1567,7 @@ function WindowManagerProvider({ children }) {
1492
1567
  setTimeout(() => {
1493
1568
  const panels = document.querySelectorAll("[data-modal-panel]");
1494
1569
  panels.forEach((p) => {
1495
- const titleEl = p.querySelector(".text-lg");
1570
+ const titleEl = p.querySelector(".text-lg, .text-sm.font-medium");
1496
1571
  if (titleEl?.textContent?.includes(existing.label)) {
1497
1572
  const mid = p.getAttribute("data-modal-id");
1498
1573
  if (mid) activateModal(mid);
@@ -1516,6 +1591,17 @@ function WindowManagerProvider({ children }) {
1516
1591
  if (!WINDOW_REGISTRY[path] || !isPageEntry(WINDOW_REGISTRY[path])) return;
1517
1592
  const entry = WINDOW_REGISTRY[path];
1518
1593
  setOpenWindows((prev) => {
1594
+ if (entry.multiInstance) {
1595
+ const instanceCount = prev.filter((m) => m.type === "page" && m.route === path).length;
1596
+ const nextNum = instanceCount + 1;
1597
+ const id = `page:${path}:${Math.random().toString(36).slice(2, 8)}`;
1598
+ return [...prev, {
1599
+ id,
1600
+ type: "page",
1601
+ label: instanceCount === 0 ? entry.label : `${entry.label} ${nextNum}`,
1602
+ route: path
1603
+ }];
1604
+ }
1519
1605
  const existing = prev.find((m) => m.type === "page" && m.route === path);
1520
1606
  if (existing) {
1521
1607
  if (entry.widget) {
@@ -1524,7 +1610,7 @@ function WindowManagerProvider({ children }) {
1524
1610
  setTimeout(() => {
1525
1611
  const panels = document.querySelectorAll("[data-modal-panel]");
1526
1612
  panels.forEach((p) => {
1527
- const titleEl = p.querySelector(".text-lg");
1613
+ const titleEl = p.querySelector(".text-lg, .text-sm.font-medium");
1528
1614
  if (titleEl?.textContent?.includes(existing.label)) {
1529
1615
  const mid = p.getAttribute("data-modal-id");
1530
1616
  if (mid) activateModal(mid);
@@ -1559,7 +1645,7 @@ function WindowManagerProvider({ children }) {
1559
1645
  onActivate: (label) => {
1560
1646
  const panels = document.querySelectorAll("[data-modal-panel]");
1561
1647
  panels.forEach((p) => {
1562
- const titleEl = p.querySelector(".text-lg");
1648
+ const titleEl = p.querySelector(".text-lg, .text-sm.font-medium");
1563
1649
  if (titleEl?.textContent?.includes(label)) {
1564
1650
  const mid = p.getAttribute("data-modal-id");
1565
1651
  if (mid) activateModal(mid);
@@ -1582,5 +1668,5 @@ function WindowManagerProvider({ children }) {
1582
1668
  }
1583
1669
 
1584
1670
  export { CancelButton, CopyButton, DocFavStar, GLASS_DIVIDER, GLASS_INPUT_BG, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, WindowManagerProvider, WindowTitle, client_default, glassStyle, isEntityEntry, isPageEntry, isSection, navIcons, navSections, sectionIcons, setShellApiClient, setShellNavIcons, setShellWindowRegistry, startMenuCategories, useModalActive, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle };
1585
- //# sourceMappingURL=chunk-OVUT6VRS.js.map
1586
- //# sourceMappingURL=chunk-OVUT6VRS.js.map
1671
+ //# sourceMappingURL=chunk-2EBFERVD.js.map
1672
+ //# sourceMappingURL=chunk-2EBFERVD.js.map