react-os-shell 0.2.26 → 0.2.30

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-RJZLTAJQ.js → Browser-5ZCLRIRU.js} +3 -3
  2. package/dist/{Browser-RJZLTAJQ.js.map → Browser-5ZCLRIRU.js.map} +1 -1
  3. package/dist/{Calculator-DMROKOY2.js → Calculator-OHL2AZ52.js} +4 -4
  4. package/dist/{Calculator-DMROKOY2.js.map → Calculator-OHL2AZ52.js.map} +1 -1
  5. package/dist/{Calendar-24TAKCAJ.js → Calendar-6AHL3UJY.js} +3 -3
  6. package/dist/{Calendar-24TAKCAJ.js.map → Calendar-6AHL3UJY.js.map} +1 -1
  7. package/dist/{CurrencyConverter-A6CHXNEQ.js → CurrencyConverter-XZVZ7XOF.js} +4 -4
  8. package/dist/{CurrencyConverter-A6CHXNEQ.js.map → CurrencyConverter-XZVZ7XOF.js.map} +1 -1
  9. package/dist/{Documents-QMP6QN3C.js → Documents-IPVZ47JX.js} +3 -3
  10. package/dist/{Documents-QMP6QN3C.js.map → Documents-IPVZ47JX.js.map} +1 -1
  11. package/dist/{Email-XTFUEIE5.js → Email-GB2JMBSI.js} +3 -3
  12. package/dist/{Email-XTFUEIE5.js.map → Email-GB2JMBSI.js.map} +1 -1
  13. package/dist/Files-NJCMN2VH.js +7 -0
  14. package/dist/{Files-YHUOR7RM.js.map → Files-NJCMN2VH.js.map} +1 -1
  15. package/dist/{Minesweeper-YPAR6SPJ.js → Minesweeper-NGN4O6C4.js} +3 -3
  16. package/dist/{Minesweeper-YPAR6SPJ.js.map → Minesweeper-NGN4O6C4.js.map} +1 -1
  17. package/dist/{Notepad-VEEUZROP.js → Notepad-W3YYZ3GS.js} +3 -3
  18. package/dist/{Notepad-VEEUZROP.js.map → Notepad-W3YYZ3GS.js.map} +1 -1
  19. package/dist/{PomodoroTimer-HWHMQZO6.js → PomodoroTimer-T2J5NDJR.js} +4 -4
  20. package/dist/{PomodoroTimer-HWHMQZO6.js.map → PomodoroTimer-T2J5NDJR.js.map} +1 -1
  21. package/dist/Preview-DRWHK4DK.js +6 -0
  22. package/dist/{Preview-SLX4ZLUQ.js.map → Preview-DRWHK4DK.js.map} +1 -1
  23. package/dist/{Spreadsheet-W76QOD42.js → Spreadsheet-FCFII6DW.js} +3 -3
  24. package/dist/{Spreadsheet-W76QOD42.js.map → Spreadsheet-FCFII6DW.js.map} +1 -1
  25. package/dist/{Weather-DIKN7BOT.js → Weather-JZHYW5XF.js} +4 -4
  26. package/dist/{Weather-DIKN7BOT.js.map → Weather-JZHYW5XF.js.map} +1 -1
  27. package/dist/apps/index.js +16 -16
  28. package/dist/{chunk-23RBDC2Z.js → chunk-472UPWAS.js} +4 -4
  29. package/dist/{chunk-23RBDC2Z.js.map → chunk-472UPWAS.js.map} +1 -1
  30. package/dist/{chunk-QXY6ZHRX.js → chunk-T2NQXP2J.js} +446 -240
  31. package/dist/chunk-T2NQXP2J.js.map +1 -0
  32. package/dist/{chunk-HVZUPS3P.js → chunk-TVOBLSSV.js} +3 -3
  33. package/dist/{chunk-HVZUPS3P.js.map → chunk-TVOBLSSV.js.map} +1 -1
  34. package/dist/{chunk-6IV6OWF3.js → chunk-VOD2AMU4.js} +3 -3
  35. package/dist/{chunk-6IV6OWF3.js.map → chunk-VOD2AMU4.js.map} +1 -1
  36. package/dist/index.d.ts +6 -2
  37. package/dist/index.js +38 -14
  38. package/dist/index.js.map +1 -1
  39. package/package.json +1 -1
  40. package/dist/Files-YHUOR7RM.js +0 -7
  41. package/dist/Preview-SLX4ZLUQ.js +0 -6
  42. package/dist/chunk-QXY6ZHRX.js.map +0 -1
@@ -556,9 +556,128 @@ function useIsActiveModal(modalId) {
556
556
  const activeId = useSyncExternalStore(subscribeActive, getActiveModalId);
557
557
  return activationOrder.length <= 1 || activeId === modalId;
558
558
  }
559
+ var _exposeOn = false;
560
+ var _exposeListeners = /* @__PURE__ */ new Set();
561
+ function _notifyExpose() {
562
+ _exposeListeners.forEach((fn) => fn());
563
+ }
564
+ function subscribeExpose(fn) {
565
+ _exposeListeners.add(fn);
566
+ return () => _exposeListeners.delete(fn);
567
+ }
568
+ function getExposeState() {
569
+ return _exposeOn;
570
+ }
571
+ function setExposeState(v) {
572
+ if (_exposeOn === v) return;
573
+ _exposeOn = v;
574
+ _notifyExpose();
575
+ }
576
+ var _exposeExitFocusId = null;
577
+ var _exposeExitListeners = /* @__PURE__ */ new Set();
578
+ function subscribeExposeExitFocus(fn) {
579
+ _exposeExitListeners.add(fn);
580
+ return () => _exposeExitListeners.delete(fn);
581
+ }
582
+ function getExposeExitFocus() {
583
+ return _exposeExitFocusId;
584
+ }
585
+ function setExposeExitFocus(id) {
586
+ if (_exposeExitFocusId === id) return;
587
+ _exposeExitFocusId = id;
588
+ _exposeExitListeners.forEach((fn) => fn());
589
+ }
559
590
  function triggerSplitView() {
591
+ setExposeState(!_exposeOn);
560
592
  window.dispatchEvent(new CustomEvent("modal-split-view"));
561
593
  }
594
+ window.addEventListener("keydown", (e) => {
595
+ if (_exposeOn && e.key === "Escape") {
596
+ setExposeState(false);
597
+ }
598
+ });
599
+ function computeExposeTile(modalId) {
600
+ if (typeof document === "undefined") return null;
601
+ const allPanels = Array.from(document.querySelectorAll("[data-modal-panel]"));
602
+ const tileable = allPanels.filter((p) => {
603
+ const el = p;
604
+ if (el.hasAttribute("data-utility") || el.hasAttribute("data-widget")) return false;
605
+ if (el.style.display === "none") return false;
606
+ return true;
607
+ });
608
+ tileable.sort((a2, b) => (a2.getAttribute("data-modal-id") || "").localeCompare(b.getAttribute("data-modal-id") || ""));
609
+ const myIdx = tileable.findIndex((p) => p.getAttribute("data-modal-id") === modalId);
610
+ const count = tileable.length;
611
+ if (myIdx < 0 || count < 1) return null;
612
+ const cols = Math.ceil(Math.sqrt(count));
613
+ const rows = Math.ceil(count / cols);
614
+ const myRow = Math.floor(myIdx / cols);
615
+ const myCol = myIdx % cols;
616
+ const lastRowCount = count - cols * (rows - 1);
617
+ const isLastRow = myRow === rows - 1 && lastRowCount < cols;
618
+ const taskbarH = parseInt(getComputedStyle(document.documentElement).getPropertyValue("--taskbar-height")) || 0;
619
+ const taskbarW = parseInt(getComputedStyle(document.documentElement).getPropertyValue("--taskbar-width")) || 0;
620
+ const tbPos = (getComputedStyle(document.documentElement).getPropertyValue("--taskbar-position") || "bottom").trim();
621
+ const xOffset = tbPos === "left" ? taskbarW : 0;
622
+ const xRight = tbPos === "right" ? taskbarW : 0;
623
+ const yOffset = tbPos === "top" ? taskbarH : 0;
624
+ const yBottom = tbPos === "top" || tbPos === "left" || tbPos === "right" ? 0 : taskbarH;
625
+ const a = {
626
+ x: xOffset,
627
+ y: yOffset,
628
+ w: window.innerWidth - xOffset - xRight,
629
+ h: window.innerHeight - yOffset - yBottom
630
+ };
631
+ const gapX = 20;
632
+ const gapY = 14;
633
+ const labelH = 22;
634
+ const cellW = (a.w - gapX * (cols + 1)) / cols;
635
+ const cellH = (a.h - gapY * (rows + 1) - labelH * rows) / rows;
636
+ const lastRowOffsetX = isLastRow ? (cols - lastRowCount) * (cellW + gapX) / 2 : 0;
637
+ const tileX = a.x + gapX + myCol * (cellW + gapX) + lastRowOffsetX;
638
+ const tileY = a.y + gapY + myRow * (cellH + gapY + labelH);
639
+ return { x: tileX, y: tileY, w: cellW, h: cellH };
640
+ }
641
+ function ExposeBackdrop() {
642
+ const on = useSyncExternalStore(subscribeExpose, getExposeState);
643
+ const [mounted, setMounted] = useState(on);
644
+ const [visible, setVisible] = useState(on);
645
+ useEffect(() => {
646
+ if (on) {
647
+ setMounted(true);
648
+ const id = requestAnimationFrame(() => setVisible(true));
649
+ return () => cancelAnimationFrame(id);
650
+ }
651
+ setVisible(false);
652
+ const t = setTimeout(() => setMounted(false), 280);
653
+ return () => clearTimeout(t);
654
+ }, [on]);
655
+ if (!mounted || typeof document === "undefined") return null;
656
+ return createPortal(
657
+ /* @__PURE__ */ jsx(
658
+ "div",
659
+ {
660
+ onMouseDown: (e) => {
661
+ e.stopPropagation();
662
+ setExposeState(false);
663
+ },
664
+ style: {
665
+ position: "fixed",
666
+ inset: 0,
667
+ background: "rgba(0,0,0,0.55)",
668
+ backdropFilter: "blur(2px)",
669
+ WebkitBackdropFilter: "blur(2px)",
670
+ zIndex: 2e3,
671
+ cursor: "pointer",
672
+ opacity: visible ? 1 : 0,
673
+ transition: "opacity 260ms cubic-bezier(0.2, 0.8, 0.2, 1)",
674
+ pointerEvents: visible ? "auto" : "none"
675
+ }
676
+ }
677
+ ),
678
+ document.body
679
+ );
680
+ }
562
681
  function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = false, onNext, onPrev, footer, bodyScroll, onMinimize, initialBox, actions, actionsLeft, allowPinOnTop, initialPosition, widget, compact, appStyle, autoHeight, autoMinHeight, widgetMenu, dimensions, windowKey, openedFromKey, children }) {
563
682
  const isMobile = useIsMobile();
564
683
  const [swipeX, setSwipeX] = useState(0);
@@ -670,6 +789,29 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
670
789
  }, [modalId]);
671
790
  const [zIndex, setZIndex] = useState(50);
672
791
  const isActive = useIsActiveModal(modalId);
792
+ const exposeOn = useSyncExternalStore(subscribeExpose, getExposeState);
793
+ const exposeExitFocusId = useSyncExternalStore(subscribeExposeExitFocus, getExposeExitFocus);
794
+ const isExposeTileable = !allowPinOnTop && !widget;
795
+ const exposeActive = exposeOn && isExposeTileable;
796
+ const [exposeHovered, setExposeHovered] = useState(false);
797
+ const [exposeExiting, setExposeExiting] = useState(false);
798
+ const exposeExitRole = exposeExiting && isExposeTileable && exposeExitFocusId === modalId ? "picked" : null;
799
+ const prevExposeActiveRef = useRef(exposeActive);
800
+ useEffect(() => {
801
+ if (prevExposeActiveRef.current && !exposeActive) {
802
+ setExposeExiting(true);
803
+ const t = setTimeout(() => {
804
+ setExposeExiting(false);
805
+ if (_exposeExitFocusId === modalId) setExposeExitFocus(null);
806
+ }, 700);
807
+ prevExposeActiveRef.current = exposeActive;
808
+ return () => clearTimeout(t);
809
+ }
810
+ prevExposeActiveRef.current = exposeActive;
811
+ }, [exposeActive, modalId]);
812
+ useEffect(() => {
813
+ if (!exposeActive) setExposeHovered(false);
814
+ }, [exposeActive]);
673
815
  useEffect(() => {
674
816
  const r = actionsRef.current;
675
817
  const l = actionsLeftRef.current;
@@ -756,28 +898,6 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
756
898
  setZIndex(getZForModal(modalId));
757
899
  };
758
900
  const onSplitView = () => {
759
- if (allowPinOnTop || widget) return;
760
- const allPanels = document.querySelectorAll("[data-modal-panel]");
761
- let nonUtilityCount = 0;
762
- let myNonUtilIdx = -1;
763
- activationOrder.forEach((id, _i) => {
764
- const panel = Array.from(allPanels).find((p) => p.getAttribute("data-modal-id") === id);
765
- if (panel && !panel.hasAttribute("data-utility") && !panel.hasAttribute("data-widget")) {
766
- if (id === modalId) myNonUtilIdx = nonUtilityCount;
767
- nonUtilityCount++;
768
- }
769
- });
770
- const count = nonUtilityCount;
771
- if (count < 2) return;
772
- const myIdx = myNonUtilIdx;
773
- if (myIdx < 0) return;
774
- const a = workArea();
775
- const baseW = Math.floor(a.w / count);
776
- const remainder = a.w - baseW * count;
777
- const colW = baseW + (myIdx < remainder ? 1 : 0);
778
- const xOffset = myIdx * baseW + Math.min(myIdx, remainder);
779
- setBox({ x: a.x + xOffset, y: a.y, w: colW, h: a.h });
780
- setMaximized(false);
781
901
  };
782
902
  const onCenter = (e) => {
783
903
  const label = e.detail?.label;
@@ -1129,121 +1249,209 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
1129
1249
  return () => window.removeEventListener("keydown", handler);
1130
1250
  }, [open, onNext, onPrev, hasNav, showBoundaryToast, modalId]);
1131
1251
  if (!open || interceptedRef.current) return null;
1132
- const content = /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
1133
- "div",
1134
- {
1135
- ref: panelRef,
1136
- "data-modal-panel": true,
1137
- "data-modal-id": modalId,
1138
- "data-window-key": windowKey || void 0,
1139
- ...allowPinOnTop ? { "data-utility": "" } : {},
1140
- ...widget ? { "data-widget": "" } : {},
1141
- className: `fixed rounded-lg flex flex-col overflow-hidden group ${widget ? isActive ? "shadow-2xl" : "shadow-lg" : `border ${isActive ? "shadow-2xl border-gray-200" : "shadow-lg border-gray-300"}`}`,
1142
- onMouseDown: (e) => {
1143
- setWindowMenu(null);
1144
- const targetPanel = e.target.closest("[data-modal-panel]");
1145
- if (targetPanel && targetPanel !== panelRef.current) return;
1146
- if (!e.target.closest("button, input, a, select, textarea")) {
1147
- activateModal(modalId);
1148
- }
1149
- },
1150
- style: isMobile ? {
1151
- // Mobile fullscreen: ignore stored box, fill viewport down to bottom
1152
- // nav. Widgets stay hidden on mobile. Only the active window is
1153
- // visible all others hidden so swipe-right reveals the home
1154
- // wallpaper backdrop. Exception: when a sibling Modal is being
1155
- // swiped to-back and we're its `openedFrom` parent, un-hide so the
1156
- // user sees their parent list during the slide instead of just the
1157
- // wallpaper.
1158
- zIndex: zIndex + 1,
1159
- top: 0,
1160
- left: 0,
1161
- right: 0,
1162
- bottom: "var(--mobile-bottom-nav, 56px)",
1163
- width: "auto",
1164
- height: "auto",
1165
- transform: `translateX(${swipeX}px)`,
1166
- transition: swipeDragging ? "none" : "transform 180ms ease-out",
1167
- ...widget ? { display: "none" } : {},
1168
- ...zIndex < 0 ? { display: "none" } : {},
1169
- ...!isActive && !pinnedOnTop && !(swipingParentKey && windowKey === swipingParentKey) ? { display: "none" } : {}
1170
- } : {
1171
- zIndex: pinnedOnTop ? 999 : zIndex + 1,
1172
- width: box.w,
1173
- height: autoHeight ? "auto" : box.h,
1174
- top: box.y,
1175
- ...autoHeight ? {
1176
- // Widgets must fit content exactly (Weather, Currency, etc.)
1177
- // the 240 px floor only applies to non-widget app windows where
1178
- // a near-empty body would look broken.
1179
- minHeight: `${autoMinHeight ?? (widget ? 0 : 240)}px`,
1180
- maxHeight: `calc(100vh - var(--taskbar-height, 0px) - 24px)`
1181
- } : {},
1182
- ...widget && widgetAnchor === "right" ? { right: window.innerWidth - box.x - box.w } : { left: box.x },
1183
- ...zIndex < 0 && !pinnedOnTop ? { display: "none" } : {}
1184
- },
1185
- children: [
1186
- isMobile && !widget && /* @__PURE__ */ jsx(
1187
- "div",
1188
- {
1189
- onPointerDown: handleEdgePointerDown,
1190
- className: "absolute top-0 bottom-0 left-0 w-[22px] z-[5]",
1191
- style: { touchAction: "pan-y" },
1192
- "aria-hidden": "true"
1252
+ const exposeTile = exposeActive ? computeExposeTile(modalId) : null;
1253
+ const exposeStyle = (() => {
1254
+ if (exposeActive && exposeTile) {
1255
+ const scale = Math.min(exposeTile.w / box.w, exposeTile.h / box.h);
1256
+ const scaledW = box.w * scale;
1257
+ const scaledH = box.h * scale;
1258
+ const tx = exposeTile.x + (exposeTile.w - scaledW) / 2 - box.x;
1259
+ const ty = exposeTile.y + (exposeTile.h - scaledH) / 2 - box.y;
1260
+ return {
1261
+ transform: `translate(${tx}px, ${ty}px) scale(${scale})`,
1262
+ transformOrigin: "top left",
1263
+ transition: "transform 280ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 280ms",
1264
+ cursor: "pointer",
1265
+ // Hovered tile lifts above its neighbours so the hover ring isn't
1266
+ // clipped by adjacent panels.
1267
+ zIndex: exposeHovered ? 2020 : 2010,
1268
+ boxShadow: exposeHovered ? "0 24px 72px rgba(0,0,0,0.6), 0 0 0 2px rgba(255,255,255,0.55), 0 0 36px 12px rgba(96,165,250,0.85), 0 0 96px 28px rgba(96,165,250,0.55)" : "0 16px 48px rgba(0,0,0,0.55)",
1269
+ pointerEvents: "auto"
1270
+ };
1271
+ }
1272
+ if (exposeExiting && isExposeTileable) {
1273
+ const picked = exposeExitRole === "picked";
1274
+ return {
1275
+ transition: picked ? "transform 640ms cubic-bezier(0.34, 1.42, 0.64, 1), box-shadow 600ms" : "transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 600ms",
1276
+ ...picked ? { zIndex: 2030 } : {}
1277
+ };
1278
+ }
1279
+ return null;
1280
+ })();
1281
+ const content = /* @__PURE__ */ jsxs("div", { children: [
1282
+ /* @__PURE__ */ jsxs(
1283
+ "div",
1284
+ {
1285
+ ref: panelRef,
1286
+ "data-modal-panel": true,
1287
+ "data-modal-id": modalId,
1288
+ "data-window-key": windowKey || void 0,
1289
+ ...allowPinOnTop ? { "data-utility": "" } : {},
1290
+ ...widget ? { "data-widget": "" } : {},
1291
+ className: `fixed rounded-lg flex flex-col overflow-hidden group ${widget ? isActive ? "shadow-2xl" : "shadow-lg" : `border ${isActive ? "shadow-2xl border-gray-200" : "shadow-lg border-gray-300"}`}`,
1292
+ onMouseDownCapture: (e) => {
1293
+ if (exposeActive) {
1294
+ e.preventDefault();
1295
+ e.stopPropagation();
1296
+ setExposeExitFocus(modalId);
1297
+ setExposeState(false);
1298
+ activateModal(modalId);
1299
+ return;
1193
1300
  }
1194
- ),
1195
- widget ? (
1196
- /* Widget: no title bar — drag via body, close via right-click context menu */
1197
- null
1198
- ) : isMobile ? null : compact ? (
1199
- /* Compact: smaller title bar with title + close only */
1200
- /* @__PURE__ */ jsxs(
1301
+ },
1302
+ onMouseDown: (e) => {
1303
+ if (exposeActive) return;
1304
+ setWindowMenu(null);
1305
+ const targetPanel = e.target.closest("[data-modal-panel]");
1306
+ if (targetPanel && targetPanel !== panelRef.current) return;
1307
+ if (!e.target.closest("button, input, a, select, textarea")) {
1308
+ activateModal(modalId);
1309
+ }
1310
+ },
1311
+ style: isMobile ? {
1312
+ // Mobile fullscreen: ignore stored box, fill viewport down to bottom
1313
+ // nav. Widgets stay hidden on mobile. Only the active window is
1314
+ // visible — all others hidden so swipe-right reveals the home
1315
+ // wallpaper backdrop. Exception: when a sibling Modal is being
1316
+ // swiped to-back and we're its `openedFrom` parent, un-hide so the
1317
+ // user sees their parent list during the slide instead of just the
1318
+ // wallpaper.
1319
+ zIndex: zIndex + 1,
1320
+ top: 0,
1321
+ left: 0,
1322
+ right: 0,
1323
+ bottom: "var(--mobile-bottom-nav, 56px)",
1324
+ width: "auto",
1325
+ height: "auto",
1326
+ transform: `translateX(${swipeX}px)`,
1327
+ transition: swipeDragging ? "none" : "transform 180ms ease-out",
1328
+ ...widget ? { display: "none" } : {},
1329
+ ...zIndex < 0 ? { display: "none" } : {},
1330
+ ...!isActive && !pinnedOnTop && !(swipingParentKey && windowKey === swipingParentKey) ? { display: "none" } : {}
1331
+ } : {
1332
+ zIndex: pinnedOnTop ? 999 : zIndex + 1,
1333
+ width: box.w,
1334
+ height: autoHeight ? "auto" : box.h,
1335
+ top: box.y,
1336
+ ...autoHeight ? {
1337
+ // Widgets must fit content exactly (Weather, Currency, etc.) —
1338
+ // the 240 px floor only applies to non-widget app windows where
1339
+ // a near-empty body would look broken.
1340
+ minHeight: `${autoMinHeight ?? (widget ? 0 : 240)}px`,
1341
+ maxHeight: `calc(100vh - var(--taskbar-height, 0px) - 24px)`
1342
+ } : {},
1343
+ ...widget && widgetAnchor === "right" ? { right: window.innerWidth - box.x - box.w } : { left: box.x },
1344
+ ...zIndex < 0 && !pinnedOnTop ? { display: "none" } : {},
1345
+ ...exposeStyle ?? {}
1346
+ },
1347
+ children: [
1348
+ isMobile && !widget && /* @__PURE__ */ jsx(
1201
1349
  "div",
1202
1350
  {
1203
- onPointerDown: startDrag,
1204
- className: `flex items-center justify-between px-3 py-1.5 border-b border-gray-200 shrink-0 cursor-move select-none rounded-t-lg ${isActive ? "backdrop-blur-sm" : ""}`,
1205
- style: { touchAction: "none", backgroundColor: isActive ? `rgb(var(--window-header-rgb) / var(--active-header-opacity, 0.8))` : `rgb(var(--window-header-rgb) / var(--inactive-header-opacity, 0.7))` },
1206
- children: [
1207
- /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium min-w-0 flex-1 truncate flex items-center gap-1.5", style: { color: isActive ? "rgb(17 24 39)" : "rgb(156 163 175)" }, children: [
1208
- renderIconButton(),
1209
- /* @__PURE__ */ jsx("span", { className: "truncate", children: displayTitle })
1210
- ] }),
1211
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0 ml-2", children: [
1212
- allowPinOnTop && /* @__PURE__ */ jsx(
1213
- "button",
1214
- {
1215
- onClick: () => setPinnedOnTop((p) => !p),
1216
- title: pinnedOnTop ? "Unpin from top" : "Pin on top",
1217
- className: `p-0.5 rounded hover:bg-gray-200 ${pinnedOnTop ? "text-blue-600" : "text-gray-400 hover:text-gray-600"}`,
1218
- children: /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: pinnedOnTop ? "currentColor" : "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" }) })
1219
- }
1220
- ),
1221
- /* @__PURE__ */ jsx("button", { type: "button", onClick: guardedClose, className: "rounded p-0.5 text-gray-400 hover:text-gray-600 hover:bg-gray-200", children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-4 w-4" }) })
1222
- ] })
1223
- ]
1351
+ onPointerDown: handleEdgePointerDown,
1352
+ className: "absolute top-0 bottom-0 left-0 w-[22px] z-[5]",
1353
+ style: { touchAction: "pan-y" },
1354
+ "aria-hidden": "true"
1224
1355
  }
1225
- )
1226
- ) : appStyle ? (
1227
- /* App style: small title bar like compact, but keeps minimize/maximize for full window control. */
1228
- /* @__PURE__ */ jsxs(
1356
+ ),
1357
+ widget ? (
1358
+ /* Widget: no title bar drag via body, close via right-click context menu */
1359
+ null
1360
+ ) : isMobile ? null : compact ? (
1361
+ /* Compact: smaller title bar with title + close only */
1362
+ /* @__PURE__ */ jsxs(
1363
+ "div",
1364
+ {
1365
+ onPointerDown: startDrag,
1366
+ className: `flex items-center justify-between px-3 py-1.5 border-b border-gray-200 shrink-0 cursor-move select-none rounded-t-lg ${isActive ? "backdrop-blur-sm" : ""}`,
1367
+ style: { touchAction: "none", backgroundColor: isActive ? `rgb(var(--window-header-rgb) / var(--active-header-opacity, 0.8))` : `rgb(var(--window-header-rgb) / var(--inactive-header-opacity, 0.7))` },
1368
+ children: [
1369
+ /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium min-w-0 flex-1 truncate flex items-center gap-1.5", style: { color: isActive ? "rgb(17 24 39)" : "rgb(156 163 175)" }, children: [
1370
+ renderIconButton(),
1371
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: displayTitle })
1372
+ ] }),
1373
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0 ml-2", children: [
1374
+ allowPinOnTop && /* @__PURE__ */ jsx(
1375
+ "button",
1376
+ {
1377
+ onClick: () => setPinnedOnTop((p) => !p),
1378
+ title: pinnedOnTop ? "Unpin from top" : "Pin on top",
1379
+ className: `p-0.5 rounded hover:bg-gray-200 ${pinnedOnTop ? "text-blue-600" : "text-gray-400 hover:text-gray-600"}`,
1380
+ children: /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: pinnedOnTop ? "currentColor" : "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" }) })
1381
+ }
1382
+ ),
1383
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: guardedClose, className: "rounded p-0.5 text-gray-400 hover:text-gray-600 hover:bg-gray-200", children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-4 w-4" }) })
1384
+ ] })
1385
+ ]
1386
+ }
1387
+ )
1388
+ ) : appStyle ? (
1389
+ /* App style: small title bar like compact, but keeps minimize/maximize for full window control. */
1390
+ /* @__PURE__ */ jsxs(
1391
+ "div",
1392
+ {
1393
+ onPointerDown: startDrag,
1394
+ className: `flex items-center justify-between px-3 py-1.5 border-b border-gray-200 shrink-0 cursor-move select-none rounded-t-lg ${isActive ? "backdrop-blur-sm" : ""}`,
1395
+ style: { touchAction: "none", backgroundColor: isActive ? `rgb(var(--window-header-rgb) / var(--active-header-opacity, 0.8))` : `rgb(var(--window-header-rgb) / var(--inactive-header-opacity, 0.7))` },
1396
+ children: [
1397
+ /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium min-w-0 flex-1 truncate flex items-center gap-1.5", style: { color: isActive ? "rgb(17 24 39)" : "rgb(156 163 175)" }, children: [
1398
+ renderIconButton(),
1399
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: displayTitle })
1400
+ ] }),
1401
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0 ml-2", children: [
1402
+ allowPinOnTop && /* @__PURE__ */ jsx(
1403
+ "button",
1404
+ {
1405
+ onClick: () => setPinnedOnTop((p) => !p),
1406
+ title: pinnedOnTop ? "Unpin from top" : "Pin on top",
1407
+ className: `p-0.5 rounded hover:bg-gray-200 ${pinnedOnTop ? "text-blue-600" : "text-gray-400 hover:text-gray-600"}`,
1408
+ children: /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: pinnedOnTop ? "currentColor" : "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" }) })
1409
+ }
1410
+ ),
1411
+ /* @__PURE__ */ jsx("button", { onClick: () => {
1412
+ const idx = activationOrder.indexOf(modalId);
1413
+ if (idx !== -1) activationOrder.splice(idx, 1);
1414
+ activeListeners.forEach((fn) => fn());
1415
+ window.dispatchEvent(new CustomEvent("modal-reorder"));
1416
+ }, title: "Minimize", className: "text-gray-400 hover:text-gray-600 px-1 py-0.5 rounded hover:bg-gray-200 text-xs leading-none", children: "\u2500" }),
1417
+ /* @__PURE__ */ jsx("button", { onClick: () => {
1418
+ if (maximized) {
1419
+ setMaximized(false);
1420
+ setBox(calcWindowed());
1421
+ } else {
1422
+ reset();
1423
+ }
1424
+ }, title: maximized ? "Windowed" : "Maximize", className: "text-gray-400 hover:text-gray-600 px-1 py-0.5 rounded hover:bg-gray-200 text-xs leading-none", children: maximized ? "\u2750" : "\u2922" }),
1425
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: guardedClose, className: "rounded p-0.5 text-gray-400 hover:text-gray-600 hover:bg-gray-200", children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-4 w-4" }) })
1426
+ ] })
1427
+ ]
1428
+ }
1429
+ )
1430
+ ) : /* @__PURE__ */ jsxs(
1229
1431
  "div",
1230
1432
  {
1231
1433
  onPointerDown: startDrag,
1232
- className: `flex items-center justify-between px-3 py-1.5 border-b border-gray-200 shrink-0 cursor-move select-none rounded-t-lg ${isActive ? "backdrop-blur-sm" : ""}`,
1434
+ className: `flex items-center justify-between px-4 py-2.5 border-b border-gray-200 shrink-0 cursor-move select-none rounded-t-lg ${isActive ? "backdrop-blur-sm" : ""}`,
1233
1435
  style: { touchAction: "none", backgroundColor: isActive ? `rgb(var(--window-header-rgb) / var(--active-header-opacity, 0.8))` : `rgb(var(--window-header-rgb) / var(--inactive-header-opacity, 0.7))` },
1234
1436
  children: [
1235
- /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium min-w-0 flex-1 truncate flex items-center gap-1.5", style: { color: isActive ? "rgb(17 24 39)" : "rgb(156 163 175)" }, children: [
1437
+ /* @__PURE__ */ jsxs("div", { className: "text-lg font-semibold min-w-0 flex-1 truncate flex items-center gap-2", style: { color: isActive ? "var(--window-title-active, rgb(17 24 39))" : "var(--window-title-inactive, rgb(156 163 175))" }, children: [
1236
1438
  renderIconButton(),
1237
1439
  /* @__PURE__ */ jsx("span", { className: "truncate", children: displayTitle })
1238
1440
  ] }),
1239
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0 ml-2", children: [
1441
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0 ml-4", children: [
1442
+ hasNav && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 mr-1 text-[10px] text-gray-400", children: [
1443
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 bg-gray-200 px-1.5 py-0.5 font-medium text-gray-500", children: "K" }),
1444
+ /* @__PURE__ */ jsx("span", { children: "Prev" }),
1445
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 bg-gray-200 px-1.5 py-0.5 font-medium ml-1 text-gray-500", children: "J" }),
1446
+ /* @__PURE__ */ jsx("span", { children: "Next" })
1447
+ ] }),
1240
1448
  allowPinOnTop && /* @__PURE__ */ jsx(
1241
1449
  "button",
1242
1450
  {
1243
1451
  onClick: () => setPinnedOnTop((p) => !p),
1244
1452
  title: pinnedOnTop ? "Unpin from top" : "Pin on top",
1245
- className: `p-0.5 rounded hover:bg-gray-200 ${pinnedOnTop ? "text-blue-600" : "text-gray-400 hover:text-gray-600"}`,
1246
- children: /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", fill: pinnedOnTop ? "currentColor" : "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" }) })
1453
+ className: `text-xs px-2 py-1 rounded hover:bg-gray-200 ${pinnedOnTop ? "text-blue-600" : "text-gray-400 hover:text-gray-600"}`,
1454
+ children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: pinnedOnTop ? "currentColor" : "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" }) })
1247
1455
  }
1248
1456
  ),
1249
1457
  /* @__PURE__ */ jsx("button", { onClick: () => {
@@ -1251,7 +1459,7 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
1251
1459
  if (idx !== -1) activationOrder.splice(idx, 1);
1252
1460
  activeListeners.forEach((fn) => fn());
1253
1461
  window.dispatchEvent(new CustomEvent("modal-reorder"));
1254
- }, title: "Minimize", className: "text-gray-400 hover:text-gray-600 px-1 py-0.5 rounded hover:bg-gray-200 text-xs leading-none", children: "\u2500" }),
1462
+ }, title: "Minimize", className: "text-gray-400 hover:text-gray-600 text-xs px-2 py-1 rounded hover:bg-gray-200", children: "\u2500" }),
1255
1463
  /* @__PURE__ */ jsx("button", { onClick: () => {
1256
1464
  if (maximized) {
1257
1465
  setMaximized(false);
@@ -1259,126 +1467,117 @@ function Modal({ open, onClose, title, icon, copyText, size = "lg", dirty = fals
1259
1467
  } else {
1260
1468
  reset();
1261
1469
  }
1262
- }, title: maximized ? "Windowed" : "Maximize", className: "text-gray-400 hover:text-gray-600 px-1 py-0.5 rounded hover:bg-gray-200 text-xs leading-none", children: maximized ? "\u2750" : "\u2922" }),
1263
- /* @__PURE__ */ jsx("button", { type: "button", onClick: guardedClose, className: "rounded p-0.5 text-gray-400 hover:text-gray-600 hover:bg-gray-200", children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-4 w-4" }) })
1470
+ }, title: maximized ? "Windowed" : "Maximize", className: "text-gray-400 hover:text-gray-600 text-xs px-2 py-1 rounded hover:bg-gray-200", children: maximized ? "\u2750" : "\u2922" }),
1471
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 bg-gray-200 px-1.5 py-0.5 text-[10px] font-medium text-gray-400", children: "ESC" }),
1472
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: guardedClose, className: "rounded-md text-gray-400 hover:text-gray-600", children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-5 w-5" }) })
1264
1473
  ] })
1265
1474
  ]
1266
1475
  }
1267
- )
1268
- ) : /* @__PURE__ */ jsxs(
1269
- "div",
1270
- {
1271
- onPointerDown: startDrag,
1272
- className: `flex items-center justify-between px-4 py-2.5 border-b border-gray-200 shrink-0 cursor-move select-none rounded-t-lg ${isActive ? "backdrop-blur-sm" : ""}`,
1273
- style: { touchAction: "none", backgroundColor: isActive ? `rgb(var(--window-header-rgb) / var(--active-header-opacity, 0.8))` : `rgb(var(--window-header-rgb) / var(--inactive-header-opacity, 0.7))` },
1274
- children: [
1275
- /* @__PURE__ */ jsxs("div", { className: "text-lg font-semibold min-w-0 flex-1 truncate flex items-center gap-2", style: { color: isActive ? "var(--window-title-active, rgb(17 24 39))" : "var(--window-title-inactive, rgb(156 163 175))" }, children: [
1276
- renderIconButton(),
1277
- /* @__PURE__ */ jsx("span", { className: "truncate", children: displayTitle })
1278
- ] }),
1279
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0 ml-4", children: [
1280
- hasNav && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 mr-1 text-[10px] text-gray-400", children: [
1281
- /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 bg-gray-200 px-1.5 py-0.5 font-medium text-gray-500", children: "K" }),
1282
- /* @__PURE__ */ jsx("span", { children: "Prev" }),
1283
- /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 bg-gray-200 px-1.5 py-0.5 font-medium ml-1 text-gray-500", children: "J" }),
1284
- /* @__PURE__ */ jsx("span", { children: "Next" })
1285
- ] }),
1286
- allowPinOnTop && /* @__PURE__ */ jsx(
1287
- "button",
1288
- {
1289
- onClick: () => setPinnedOnTop((p) => !p),
1290
- title: pinnedOnTop ? "Unpin from top" : "Pin on top",
1291
- className: `text-xs px-2 py-1 rounded hover:bg-gray-200 ${pinnedOnTop ? "text-blue-600" : "text-gray-400 hover:text-gray-600"}`,
1292
- children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", fill: pinnedOnTop ? "currentColor" : "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 1.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" }) })
1293
- }
1294
- ),
1295
- /* @__PURE__ */ jsx("button", { onClick: () => {
1296
- const idx = activationOrder.indexOf(modalId);
1297
- if (idx !== -1) activationOrder.splice(idx, 1);
1298
- activeListeners.forEach((fn) => fn());
1299
- window.dispatchEvent(new CustomEvent("modal-reorder"));
1300
- }, title: "Minimize", className: "text-gray-400 hover:text-gray-600 text-xs px-2 py-1 rounded hover:bg-gray-200", children: "\u2500" }),
1301
- /* @__PURE__ */ jsx("button", { onClick: () => {
1302
- if (maximized) {
1303
- setMaximized(false);
1304
- setBox(calcWindowed());
1305
- } else {
1306
- reset();
1307
- }
1308
- }, title: maximized ? "Windowed" : "Maximize", className: "text-gray-400 hover:text-gray-600 text-xs px-2 py-1 rounded hover:bg-gray-200", children: maximized ? "\u2750" : "\u2922" }),
1309
- /* @__PURE__ */ jsx("kbd", { className: "rounded border border-gray-300 bg-gray-200 px-1.5 py-0.5 text-[10px] font-medium text-gray-400", children: "ESC" }),
1310
- /* @__PURE__ */ jsx("button", { type: "button", onClick: guardedClose, className: "rounded-md text-gray-400 hover:text-gray-600", children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-5 w-5" }) })
1311
- ] })
1312
- ]
1313
- }
1314
- ),
1315
- /* @__PURE__ */ jsx(ModalIdContext.Provider, { value: modalId, children: /* @__PURE__ */ jsx(ModalActionsContext.Provider, { value: { rightRef: actionsRef, leftRef: actionsLeftRef, notify: () => setHasActions(true), active: isActive, isDirty }, children: /* @__PURE__ */ jsx(
1316
- "div",
1317
- {
1318
- ...widget ? { onPointerDown: startDrag, onContextMenu: (e) => {
1319
- e.preventDefault();
1320
- setCtxMenu({ x: e.clientX, y: e.clientY });
1321
- } } : {},
1322
- className: `flex-1 min-h-0 flex flex-col ${widget ? "p-0 cursor-move" : appStyle ? "p-0" : compact ? "p-2" : "p-4"} ${widget ? "" : "backdrop-blur-sm"} ${bodyScroll === false || appStyle ? "overflow-hidden" : "overflow-y-auto"} ${widget ? "rounded-lg select-none" : ""}`,
1323
- style: { ...widget ? { touchAction: "none" } : {}, backgroundColor: widget ? "transparent" : isActive ? `rgb(var(--window-content-rgb) / var(--active-content-opacity, 0.9))` : `rgb(var(--window-content-rgb) / var(--inactive-content-opacity, 0.8))` },
1324
- children
1325
- }
1326
- ) }) }),
1327
- widget && ctxMenu && /* @__PURE__ */ jsxs(PopupMenu, { minWidth: 160, style: { left: ctxMenu.x, top: ctxMenu.y }, onClose: () => setCtxMenu(null), children: [
1328
- /* @__PURE__ */ jsx(PopupMenuItem, { onClick: () => {
1329
- setCtxMenu(null);
1330
- window.dispatchEvent(new CustomEvent("widget-open-settings", { detail: modalId }));
1331
- }, children: "Settings" }),
1332
- /* @__PURE__ */ jsx(PopupMenuDivider, {}),
1333
- widgetMenu && /* @__PURE__ */ jsxs(Fragment, { children: [
1334
- widgetMenu,
1335
- /* @__PURE__ */ jsx(PopupMenuDivider, {})
1476
+ ),
1477
+ /* @__PURE__ */ jsx(ModalIdContext.Provider, { value: modalId, children: /* @__PURE__ */ jsx(ModalActionsContext.Provider, { value: { rightRef: actionsRef, leftRef: actionsLeftRef, notify: () => setHasActions(true), active: isActive, isDirty }, children: /* @__PURE__ */ jsx(
1478
+ "div",
1479
+ {
1480
+ ...widget ? { onPointerDown: startDrag, onContextMenu: (e) => {
1481
+ e.preventDefault();
1482
+ setCtxMenu({ x: e.clientX, y: e.clientY });
1483
+ } } : {},
1484
+ className: `flex-1 min-h-0 flex flex-col ${widget ? "p-0 cursor-move" : appStyle ? "p-0" : compact ? "p-2" : "p-4"} ${widget ? "" : "backdrop-blur-sm"} ${bodyScroll === false || appStyle ? "overflow-hidden" : "overflow-y-auto"} ${widget ? "rounded-lg select-none" : ""}`,
1485
+ style: { ...widget ? { touchAction: "none" } : {}, backgroundColor: widget ? "transparent" : isActive ? `rgb(var(--window-content-rgb) / var(--active-content-opacity, 0.9))` : `rgb(var(--window-content-rgb) / var(--inactive-content-opacity, 0.8))` },
1486
+ children
1487
+ }
1488
+ ) }) }),
1489
+ widget && ctxMenu && /* @__PURE__ */ jsxs(PopupMenu, { minWidth: 160, style: { left: ctxMenu.x, top: ctxMenu.y }, onClose: () => setCtxMenu(null), children: [
1490
+ /* @__PURE__ */ jsx(PopupMenuItem, { onClick: () => {
1491
+ setCtxMenu(null);
1492
+ window.dispatchEvent(new CustomEvent("widget-open-settings", { detail: modalId }));
1493
+ }, children: "Settings" }),
1494
+ /* @__PURE__ */ jsx(PopupMenuDivider, {}),
1495
+ widgetMenu && /* @__PURE__ */ jsxs(Fragment, { children: [
1496
+ widgetMenu,
1497
+ /* @__PURE__ */ jsx(PopupMenuDivider, {})
1498
+ ] }),
1499
+ /* @__PURE__ */ jsxs(PopupMenuItem, { onClick: () => {
1500
+ setCtxMenu(null);
1501
+ setPinnedOnTop((p) => !p);
1502
+ }, children: [
1503
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: "Always on Top" }),
1504
+ pinnedOnTop && /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-blue-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) })
1505
+ ] }),
1506
+ /* @__PURE__ */ jsx(PopupMenuDivider, {}),
1507
+ /* @__PURE__ */ jsx(PopupMenuItem, { onClick: () => {
1508
+ setCtxMenu(null);
1509
+ guardedClose();
1510
+ }, children: "Close" })
1336
1511
  ] }),
1337
- /* @__PURE__ */ jsxs(PopupMenuItem, { onClick: () => {
1338
- setCtxMenu(null);
1339
- setPinnedOnTop((p) => !p);
1340
- }, children: [
1341
- /* @__PURE__ */ jsx("span", { className: "flex-1", children: "Always on Top" }),
1342
- pinnedOnTop && /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 text-blue-600 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" }) })
1512
+ /* @__PURE__ */ jsxs(
1513
+ "div",
1514
+ {
1515
+ onPointerDown: startDrag,
1516
+ className: `px-4 py-2 border-t border-gray-200 shrink-0 flex items-center justify-between text-xs select-none cursor-move${isActive ? " backdrop-blur-sm" : ""}${widget || compact || appStyle || isMobile || !footer && !hasActions && !actions && !actionsLeft ? " hidden" : ""}`,
1517
+ style: { touchAction: "none", backgroundColor: isActive ? `rgb(var(--window-footer-rgb) / var(--active-header-opacity, 0.8))` : `rgb(var(--window-footer-rgb) / var(--inactive-header-opacity, 0.7))` },
1518
+ children: [
1519
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
1520
+ actionsLeft,
1521
+ /* @__PURE__ */ jsx("div", { ref: actionsLeftRef, "data-modal-actions-left": true, className: "flex items-center gap-2" }),
1522
+ footer
1523
+ ] }),
1524
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
1525
+ /* @__PURE__ */ jsx("div", { ref: actionsRef, "data-modal-actions": true, className: "flex items-center gap-2" }),
1526
+ actions
1527
+ ] })
1528
+ ]
1529
+ }
1530
+ ),
1531
+ !widget && !isMobile && !exposeActive && isActive && /* @__PURE__ */ jsxs(Fragment, { children: [
1532
+ /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "se"), className: "absolute bottom-0 right-0 w-3 h-3 cursor-nwse-resize z-10" }),
1533
+ /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "sw"), className: "absolute bottom-0 left-0 w-3 h-3 cursor-nesw-resize z-10" }),
1534
+ /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "ne"), className: "absolute top-0 right-0 w-3 h-3 cursor-nesw-resize z-10" }),
1535
+ /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "nw"), className: "absolute top-0 left-0 w-3 h-3 cursor-nwse-resize z-10" }),
1536
+ /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "n"), className: "absolute top-0 left-3 right-3 h-1 cursor-ns-resize" }),
1537
+ /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "s"), className: "absolute bottom-0 left-3 right-3 h-1 cursor-ns-resize" }),
1538
+ /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "e"), className: "absolute top-3 bottom-3 right-0 w-1 cursor-ew-resize" }),
1539
+ /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "w"), className: "absolute top-3 bottom-3 left-0 w-1 cursor-ew-resize" })
1343
1540
  ] }),
1344
- /* @__PURE__ */ jsx(PopupMenuDivider, {}),
1345
- /* @__PURE__ */ jsx(PopupMenuItem, { onClick: () => {
1346
- setCtxMenu(null);
1347
- guardedClose();
1348
- }, children: "Close" })
1349
- ] }),
1350
- /* @__PURE__ */ jsxs(
1351
- "div",
1352
- {
1353
- onPointerDown: startDrag,
1354
- className: `px-4 py-2 border-t border-gray-200 shrink-0 flex items-center justify-between text-xs select-none cursor-move${isActive ? " backdrop-blur-sm" : ""}${widget || compact || appStyle || isMobile || !footer && !hasActions && !actions && !actionsLeft ? " hidden" : ""}`,
1355
- style: { touchAction: "none", backgroundColor: isActive ? `rgb(var(--window-footer-rgb) / var(--active-header-opacity, 0.8))` : `rgb(var(--window-footer-rgb) / var(--inactive-header-opacity, 0.7))` },
1356
- children: [
1357
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
1358
- actionsLeft,
1359
- /* @__PURE__ */ jsx("div", { ref: actionsLeftRef, "data-modal-actions-left": true, className: "flex items-center gap-2" }),
1360
- footer
1361
- ] }),
1362
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
1363
- /* @__PURE__ */ jsx("div", { ref: actionsRef, "data-modal-actions": true, className: "flex items-center gap-2" }),
1364
- actions
1365
- ] })
1366
- ]
1367
- }
1368
- ),
1369
- !widget && !isMobile && isActive && /* @__PURE__ */ jsxs(Fragment, { children: [
1370
- /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "se"), className: "absolute bottom-0 right-0 w-3 h-3 cursor-nwse-resize z-10" }),
1371
- /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "sw"), className: "absolute bottom-0 left-0 w-3 h-3 cursor-nesw-resize z-10" }),
1372
- /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "ne"), className: "absolute top-0 right-0 w-3 h-3 cursor-nesw-resize z-10" }),
1373
- /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "nw"), className: "absolute top-0 left-0 w-3 h-3 cursor-nwse-resize z-10" }),
1374
- /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "n"), className: "absolute top-0 left-3 right-3 h-1 cursor-ns-resize" }),
1375
- /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "s"), className: "absolute bottom-0 left-3 right-3 h-1 cursor-ns-resize" }),
1376
- /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "e"), className: "absolute top-3 bottom-3 right-0 w-1 cursor-ew-resize" }),
1377
- /* @__PURE__ */ jsx("div", { onPointerDown: (e) => startResizeCorner(e, "w"), className: "absolute top-3 bottom-3 left-0 w-1 cursor-ew-resize" })
1378
- ] })
1379
- ]
1380
- }
1381
- ) });
1541
+ exposeActive && /* @__PURE__ */ jsx(
1542
+ "div",
1543
+ {
1544
+ className: "absolute inset-0",
1545
+ style: { zIndex: 9999, cursor: "pointer", background: "transparent" },
1546
+ onMouseEnter: () => setExposeHovered(true),
1547
+ onMouseLeave: () => setExposeHovered(false),
1548
+ onMouseDown: (e) => {
1549
+ e.stopPropagation();
1550
+ e.preventDefault();
1551
+ activateModal(modalId);
1552
+ setExposeState(false);
1553
+ },
1554
+ onClick: (e) => {
1555
+ e.stopPropagation();
1556
+ e.preventDefault();
1557
+ }
1558
+ }
1559
+ )
1560
+ ]
1561
+ }
1562
+ ),
1563
+ exposeActive && exposeTile && /* @__PURE__ */ jsx(
1564
+ "div",
1565
+ {
1566
+ className: "fixed pointer-events-none select-none truncate text-center",
1567
+ style: {
1568
+ left: exposeTile.x,
1569
+ top: exposeTile.y + exposeTile.h + 4,
1570
+ width: exposeTile.w,
1571
+ zIndex: exposeHovered ? 2021 : 2011,
1572
+ color: "white",
1573
+ fontSize: 12,
1574
+ fontWeight: 500,
1575
+ textShadow: "0 1px 2px rgba(0,0,0,0.6)"
1576
+ },
1577
+ children: displayTitle
1578
+ }
1579
+ )
1580
+ ] });
1382
1581
  const windowMenuEl = windowMenu && /* @__PURE__ */ jsxs(PopupMenu, { style: { left: windowMenu.x, top: windowMenu.y }, onClose: () => setWindowMenu(null), minWidth: 160, children: [
1383
1582
  !widget && !compact && /* @__PURE__ */ jsxs(Fragment, { children: [
1384
1583
  /* @__PURE__ */ jsxs(PopupMenuItem, { onClick: () => {
@@ -1863,10 +2062,16 @@ function TaskbarWindows({ openWindows, onRemove, onCloseAll, onSplitView, onActi
1863
2062
  "button",
1864
2063
  {
1865
2064
  onClick: onSplitView,
2065
+ title: "Expos\xE9 \u2014 show all open windows as thumbnails",
1866
2066
  className: "flex items-center gap-1 rounded px-2 py-1 text-[11px] font-medium text-blue-600 border border-blue-300 hover:bg-blue-50 transition-colors shrink-0",
1867
2067
  children: [
1868
- /* @__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: "M9 4H5a1 1 0 00-1 1v14a1 1 0 001 1h4m6-16h4a1 1 0 011 1v14a1 1 0 01-1 1h-4m-6 0V4" }) }),
1869
- "Split"
2068
+ /* @__PURE__ */ jsxs("svg", { className: "h-3 w-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: [
2069
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1" }),
2070
+ /* @__PURE__ */ jsx("rect", { x: "13.5", y: "3.5", width: "7", height: "7", rx: "1" }),
2071
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "13.5", width: "7", height: "7", rx: "1" }),
2072
+ /* @__PURE__ */ jsx("rect", { x: "13.5", y: "13.5", width: "7", height: "7", rx: "1" })
2073
+ ] }),
2074
+ "Expos\xE9"
1870
2075
  ]
1871
2076
  }
1872
2077
  ),
@@ -2034,6 +2239,7 @@ function WindowManagerProvider({ children }) {
2034
2239
  }
2035
2240
  }
2036
2241
  ),
2242
+ /* @__PURE__ */ jsx(ExposeBackdrop, {}),
2037
2243
  !isAuthPage && openWindows.map((item) => item.type === "page" ? /* @__PURE__ */ jsx(PageWindow, { item, onClose: () => closeEntity(item.id) }, item.id) : /* @__PURE__ */ jsx(
2038
2244
  RestoredRegistryModal,
2039
2245
  {
@@ -2048,5 +2254,5 @@ function WindowManagerProvider({ children }) {
2048
2254
  }
2049
2255
 
2050
2256
  export { CancelButton, CopyButton, DocFavStar, GLASS_DIVIDER, GLASS_INPUT_BG, LoadingSpinner, Modal, ModalActions, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ThumbCard, WINDOW_REGISTRY, WindowManagerProvider, WindowTitle, activateModal, client_default, getActiveModalId, glassStyle, isEntityEntry, isPageEntry, isSection, navIcons, navSections, sectionIcons, setShellApiClient, setShellNavIcons, setShellWindowRegistry, startMenuCategories, useIsMobile, useModalActive, useWidgetSettings, useWindowManager, useWindowMenuItem, useWindowTitle };
2051
- //# sourceMappingURL=chunk-QXY6ZHRX.js.map
2052
- //# sourceMappingURL=chunk-QXY6ZHRX.js.map
2257
+ //# sourceMappingURL=chunk-T2NQXP2J.js.map
2258
+ //# sourceMappingURL=chunk-T2NQXP2J.js.map