react-os-shell 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -455,13 +455,14 @@ interface EditableGridProps {
455
455
  declare function EditableGrid({ columns, data, onChange, onColumnsChange, fixedRows, minRows, maxHeight, cellStyles, onFocusChange, onSelectionChange }: EditableGridProps): react_jsx_runtime.JSX.Element;
456
456
 
457
457
  /**
458
- * Two-pane layout with a drag-to-resize left sidebar.
458
+ * Two-pane layout with a drag-to-resize sidebar (left or right).
459
459
  *
460
460
  * Renders flush (`h-full w-full`) — pair it with a `flushBody` window so the
461
461
  * sidebar runs from just under the title bar to the very bottom with no
462
- * surrounding padding. The user can drag the right edge of the sidebar to
463
- * resize it; pass `storageKey` to persist that width across reopens, or
464
- * double-click the handle to reset to `defaultWidth`.
462
+ * surrounding padding. The user can drag the sidebar's inner edge to resize
463
+ * it; pass `storageKey` to persist that width across reopens, or double-click
464
+ * the handle to reset to `defaultWidth`. Set `side="right"` to put the sidebar
465
+ * on the right (e.g. a detail-panel layout).
465
466
  *
466
467
  * @example
467
468
  * <SidebarLayout sidebar={<MyNav />} storageKey="todo.sidebarWidth">
@@ -471,8 +472,11 @@ declare function EditableGrid({ columns, data, onChange, onColumnsChange, fixedR
471
472
  interface SidebarLayoutProps {
472
473
  /** Content of the left sidebar pane. */
473
474
  sidebar: ReactNode;
474
- /** Content of the main pane (right of the sidebar). */
475
+ /** Content of the main pane (opposite the sidebar). */
475
476
  children: ReactNode;
477
+ /** Which side the sidebar sits on. Default `'left'`. When `'right'`, the
478
+ * sidebar renders on the right with the resize handle on its left edge. */
479
+ side?: 'left' | 'right';
476
480
  /** localStorage key to persist the sidebar width across reopens. When set,
477
481
  * the last dragged width is restored on mount. Omit for session-only width. */
478
482
  storageKey?: string;
@@ -491,7 +495,7 @@ interface SidebarLayoutProps {
491
495
  /** Classes for the main content pane. Defaults to a white background. */
492
496
  contentClassName?: string;
493
497
  }
494
- declare function SidebarLayout({ sidebar, children, storageKey, defaultWidth, minWidth, maxWidth, className, sidebarClassName, contentClassName, }: SidebarLayoutProps): react_jsx_runtime.JSX.Element;
498
+ declare function SidebarLayout({ sidebar, children, side, storageKey, defaultWidth, minWidth, maxWidth, className, sidebarClassName, contentClassName, }: SidebarLayoutProps): react_jsx_runtime.JSX.Element;
495
499
 
496
500
  /** Generic notification shape consumed by the shell. Consumer-specific
497
501
  * fields live on `extra` (or just on additional properties — TS structural
package/dist/index.js CHANGED
@@ -564,14 +564,16 @@ var clamp = (n, lo, hi) => Math.min(Math.max(n, lo), hi);
564
564
  function SidebarLayout({
565
565
  sidebar,
566
566
  children,
567
+ side = "left",
567
568
  storageKey,
568
569
  defaultWidth = 256,
569
570
  minWidth = 180,
570
571
  maxWidth = 480,
571
572
  className = "",
572
- sidebarClassName = "border-r border-gray-200 bg-gray-50",
573
+ sidebarClassName = side === "right" ? "border-l border-gray-200 bg-gray-50" : "border-r border-gray-200 bg-gray-50",
573
574
  contentClassName = "bg-white"
574
575
  }) {
576
+ const sideRight = side === "right";
575
577
  const [width, setWidth] = useState(() => {
576
578
  if (storageKey && typeof window !== "undefined") {
577
579
  const saved = window.localStorage.getItem(storageKey);
@@ -584,8 +586,9 @@ function SidebarLayout({
584
586
  const onMove = useCallback((e) => {
585
587
  const d = dragRef.current;
586
588
  if (!d) return;
587
- setWidth(clamp(d.startWidth + (e.clientX - d.startX), minWidth, maxWidth));
588
- }, [minWidth, maxWidth]);
589
+ const delta = (e.clientX - d.startX) * (sideRight ? -1 : 1);
590
+ setWidth(clamp(d.startWidth + delta, minWidth, maxWidth));
591
+ }, [minWidth, maxWidth, sideRight]);
589
592
  const onUp = useCallback(() => {
590
593
  dragRef.current = null;
591
594
  window.removeEventListener("pointermove", onMove);
@@ -615,22 +618,28 @@ function SidebarLayout({
615
618
  window.removeEventListener("pointermove", onMove);
616
619
  window.removeEventListener("pointerup", onUp);
617
620
  }, [onMove, onUp]);
618
- return /* @__PURE__ */ jsxs("div", { className: `flex h-full w-full overflow-hidden ${className}`, children: [
619
- /* @__PURE__ */ jsxs("div", { className: "relative flex h-full shrink-0 flex-col", style: { width }, children: [
620
- /* @__PURE__ */ jsx("div", { className: `flex h-full flex-col overflow-y-auto ${sidebarClassName}`, children: sidebar }),
621
- /* @__PURE__ */ jsx(
622
- "div",
623
- {
624
- onPointerDown: startDrag,
625
- onDoubleClick: () => setWidth(clamp(defaultWidth, minWidth, maxWidth)),
626
- title: "Drag to resize \xB7 double-click to reset",
627
- className: "group absolute inset-y-0 right-0 z-10 w-2 cursor-col-resize",
628
- children: /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 w-px bg-transparent transition-colors group-hover:bg-blue-400" })
629
- }
630
- )
631
- ] }),
632
- /* @__PURE__ */ jsx("div", { className: `flex min-w-0 flex-1 flex-col ${contentClassName}`, children })
621
+ const edge = sideRight ? "left-0" : "right-0";
622
+ const sidebarPane = /* @__PURE__ */ jsxs("div", { className: "relative flex h-full shrink-0 flex-col", style: { width }, children: [
623
+ /* @__PURE__ */ jsx("div", { className: `flex h-full flex-col overflow-y-auto ${sidebarClassName}`, children: sidebar }),
624
+ /* @__PURE__ */ jsx(
625
+ "div",
626
+ {
627
+ onPointerDown: startDrag,
628
+ onDoubleClick: () => setWidth(clamp(defaultWidth, minWidth, maxWidth)),
629
+ title: "Drag to resize \xB7 double-click to reset",
630
+ className: `group absolute inset-y-0 ${edge} z-10 w-2 cursor-col-resize`,
631
+ children: /* @__PURE__ */ jsx("div", { className: `absolute inset-y-0 ${edge} w-px bg-transparent transition-colors group-hover:bg-blue-400` })
632
+ }
633
+ )
633
634
  ] });
635
+ const contentPane = /* @__PURE__ */ jsx("div", { className: `flex min-w-0 flex-1 flex-col ${contentClassName}`, children });
636
+ return /* @__PURE__ */ jsx("div", { className: `flex h-full w-full overflow-hidden ${className}`, children: sideRight ? /* @__PURE__ */ jsxs(Fragment, { children: [
637
+ contentPane,
638
+ sidebarPane
639
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
640
+ sidebarPane,
641
+ contentPane
642
+ ] }) });
634
643
  }
635
644
  function timeAgo(dateStr) {
636
645
  const diff = Date.now() - new Date(dateStr).getTime();
@@ -1472,7 +1481,7 @@ function WidgetManager({ open, onClose }) {
1472
1481
  }
1473
1482
 
1474
1483
  // src/version.ts
1475
- var VERSION = "0.11.0" ;
1484
+ var VERSION = "0.12.0" ;
1476
1485
  var APP_VERSION = VERSION;
1477
1486
 
1478
1487
  // src/changelog.ts
@@ -5701,34 +5710,43 @@ function SystemPreferences({
5701
5710
  const initial = defaultSelected && sections2.some((s) => s.key === defaultSelected) ? defaultSelected : sections2[0]?.key ?? "";
5702
5711
  const [selected, setSelected] = useState(initial);
5703
5712
  const active = sections2.find((s) => s.key === selected) ?? sections2[0];
5704
- return /* @__PURE__ */ jsxs("div", { className: `flex h-full gap-4 px-4 py-3 min-h-0 ${className ?? ""}`.trim(), children: [
5705
- /* @__PURE__ */ jsx("aside", { className: "w-60 shrink-0 flex flex-col bg-white rounded-lg shadow overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto py-1", children: sections2.map((item) => {
5706
- const isActive = item.key === selected;
5707
- return /* @__PURE__ */ jsxs(
5708
- "button",
5709
- {
5710
- type: "button",
5711
- onClick: () => setSelected(item.key),
5712
- className: `w-full text-left px-3 py-2.5 text-sm transition-colors flex items-start gap-2.5 ${isActive ? "bg-blue-50 text-blue-700" : "hover:bg-gray-50 text-gray-700"}`,
5713
- children: [
5714
- item.icon && /* @__PURE__ */ jsx("span", { className: `mt-0.5 ${isActive ? "text-blue-600" : "text-gray-400"}`, children: item.icon }),
5715
- /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
5716
- /* @__PURE__ */ jsx(
5717
- "span",
5718
- {
5719
- className: `block ${isActive ? "font-semibold text-blue-700" : "font-medium text-gray-800"}`,
5720
- children: item.label
5721
- }
5722
- ),
5723
- item.description && /* @__PURE__ */ jsx("span", { className: "block text-[11px] text-gray-500 mt-0.5", children: item.description })
5724
- ] })
5725
- ]
5726
- },
5727
- item.key
5728
- );
5729
- }) }) }),
5730
- /* @__PURE__ */ jsx("main", { className: "flex-1 min-w-0 flex flex-col min-h-0 overflow-auto pr-2", children: active?.render() })
5731
- ] });
5713
+ return /* @__PURE__ */ jsx(
5714
+ SidebarLayout,
5715
+ {
5716
+ storageKey: "shell.systemPreferences.sidebarWidth",
5717
+ defaultWidth: 240,
5718
+ minWidth: 200,
5719
+ maxWidth: 360,
5720
+ className,
5721
+ sidebarClassName: "border-r border-gray-200 bg-gray-50",
5722
+ sidebar: /* @__PURE__ */ jsx("nav", { className: "py-1.5", children: sections2.map((item) => {
5723
+ const isActive = item.key === selected;
5724
+ return /* @__PURE__ */ jsxs(
5725
+ "button",
5726
+ {
5727
+ type: "button",
5728
+ onClick: () => setSelected(item.key),
5729
+ className: `w-full text-left px-3 py-2.5 text-sm transition-colors flex items-start gap-2.5 ${isActive ? "bg-blue-50 text-blue-700" : "hover:bg-gray-100 text-gray-700"}`,
5730
+ children: [
5731
+ item.icon && /* @__PURE__ */ jsx("span", { className: `mt-0.5 ${isActive ? "text-blue-600" : "text-gray-400"}`, children: item.icon }),
5732
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
5733
+ /* @__PURE__ */ jsx(
5734
+ "span",
5735
+ {
5736
+ className: `block ${isActive ? "font-semibold text-blue-700" : "font-medium text-gray-800"}`,
5737
+ children: item.label
5738
+ }
5739
+ ),
5740
+ item.description && /* @__PURE__ */ jsx("span", { className: "block text-[11px] text-gray-500 mt-0.5", children: item.description })
5741
+ ] })
5742
+ ]
5743
+ },
5744
+ item.key
5745
+ );
5746
+ }) }),
5747
+ children: /* @__PURE__ */ jsx("main", { className: "flex-1 min-w-0 overflow-auto p-4", children: active?.render() })
5748
+ }
5749
+ );
5732
5750
  }
5733
5751
  function useTableNav(items, onSelect, onToggle, onSelectAll, onSelectRange) {
5734
5752
  const [focusIdx, setFocusIdx] = useState(-1);