@petrarca/sonnet-shell 0.2.0 → 0.3.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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/AppShell.tsx
2
2
  import {
3
- useState,
3
+ useState as useState2,
4
4
  useEffect as useEffect3,
5
5
  useCallback as useCallback2,
6
6
  useRef as useRef2,
@@ -68,58 +68,40 @@ function TopBar({ children }) {
68
68
  return /* @__PURE__ */ jsx2("header", { className: "h-12 shrink-0 border-b border-border flex items-center justify-between px-3 bg-background z-20", children });
69
69
  }
70
70
 
71
- // src/IconRail.tsx
72
- import { cn } from "@petrarca/sonnet-core";
73
- import { Button as Button2 } from "@petrarca/sonnet-ui";
74
- import { Separator } from "@petrarca/sonnet-ui";
75
- import { Tooltip, TooltipContent, TooltipTrigger } from "@petrarca/sonnet-ui";
76
-
77
- // src/shellModules.ts
78
- import { createContext, useContext } from "react";
79
- var ShellModulesContext = createContext(null);
80
- function useShellModules() {
81
- const registry = useContext(ShellModulesContext);
82
- if (!registry) {
83
- throw new Error("useShellModules must be used within an AppShell");
84
- }
85
- return registry;
71
+ // src/ShellFooter.tsx
72
+ import { jsx as jsx3 } from "react/jsx-runtime";
73
+ function ShellFooter({ children }) {
74
+ return /* @__PURE__ */ jsx3("footer", { className: "h-8 shrink-0 border-t border-border flex items-center justify-between px-3 bg-background text-[11px] text-muted-foreground z-20", children });
86
75
  }
87
76
 
88
77
  // src/IconRail.tsx
89
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
78
+ import { cn } from "@petrarca/sonnet-core";
79
+ import { Button as Button2, Separator } from "@petrarca/sonnet-ui";
80
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@petrarca/sonnet-ui";
81
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
90
82
  function IconRail({
91
- activeServiceId,
92
- activeSidePaneModuleId,
93
- onServiceSelect
83
+ children,
84
+ className
94
85
  }) {
95
- const { mainModules, bottomModules } = useShellModules();
96
- const isActive = (id) => activeServiceId === id || activeSidePaneModuleId === id;
97
- return /* @__PURE__ */ jsxs2("nav", { className: "w-14 shrink-0 border-r bg-muted flex flex-col items-center py-2 gap-1", children: [
98
- /* @__PURE__ */ jsx3("div", { className: "flex-1 flex flex-col items-center gap-1 overflow-y-auto", children: mainModules.map((service) => /* @__PURE__ */ jsx3(
99
- RailIcon,
100
- {
101
- service,
102
- isActive: isActive(service.id),
103
- onClick: () => onServiceSelect(service.id)
104
- },
105
- service.id
106
- )) }),
107
- /* @__PURE__ */ jsx3(Separator, { className: "w-6 my-1" }),
108
- /* @__PURE__ */ jsx3("div", { className: "flex flex-col items-center gap-1", children: bottomModules.map((service) => /* @__PURE__ */ jsx3(
109
- RailIcon,
110
- {
111
- service,
112
- isActive: isActive(service.id),
113
- onClick: () => onServiceSelect(service.id)
114
- },
115
- service.id
116
- )) })
117
- ] });
86
+ return /* @__PURE__ */ jsx4(
87
+ "nav",
88
+ {
89
+ className: cn(
90
+ "w-14 shrink-0 border-r bg-muted flex flex-col items-center py-2 gap-1",
91
+ className
92
+ ),
93
+ children
94
+ }
95
+ );
118
96
  }
119
- function RailIcon({ service, isActive, onClick }) {
120
- const Icon = service.icon;
97
+ function RailIcon({
98
+ icon: Icon,
99
+ label,
100
+ active = false,
101
+ onClick
102
+ }) {
121
103
  return /* @__PURE__ */ jsxs2(Tooltip, { delayDuration: 0, children: [
122
- /* @__PURE__ */ jsx3(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
104
+ /* @__PURE__ */ jsx4(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
123
105
  Button2,
124
106
  {
125
107
  variant: "ghost",
@@ -127,23 +109,24 @@ function RailIcon({ service, isActive, onClick }) {
127
109
  onClick,
128
110
  className: cn(
129
111
  "h-10 w-10 rounded-lg transition-colors",
130
- isActive ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:text-foreground"
112
+ active ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground hover:bg-muted-foreground/10"
131
113
  ),
132
114
  children: [
133
- /* @__PURE__ */ jsx3(Icon, { className: "h-5 w-5" }),
134
- /* @__PURE__ */ jsx3("span", { className: "sr-only", children: service.label })
115
+ /* @__PURE__ */ jsx4(Icon, { className: "h-5 w-5" }),
116
+ /* @__PURE__ */ jsx4("span", { className: "sr-only", children: label })
135
117
  ]
136
118
  }
137
119
  ) }),
138
- /* @__PURE__ */ jsx3(TooltipContent, { side: "right", sideOffset: 8, children: service.label })
120
+ /* @__PURE__ */ jsx4(TooltipContent, { side: "right", sideOffset: 8, children: label })
139
121
  ] });
140
122
  }
123
+ function RailSeparator() {
124
+ return /* @__PURE__ */ jsx4(Separator, { className: "w-6 my-1" });
125
+ }
141
126
 
142
127
  // src/SubNavPanel.tsx
143
128
  import { useMemo as useMemo2 } from "react";
144
- import { useLocation, Link } from "react-router-dom";
145
129
  import { ChevronsLeft, ChevronsRight } from "lucide-react";
146
- import { cn as cn2 } from "@petrarca/sonnet-core";
147
130
  import { ScrollArea } from "@petrarca/sonnet-ui";
148
131
  import { Tooltip as Tooltip2, TooltipContent as TooltipContent2, TooltipTrigger as TooltipTrigger2 } from "@petrarca/sonnet-ui";
149
132
 
@@ -413,6 +396,17 @@ var events = {
413
396
  }
414
397
  };
415
398
 
399
+ // src/shellModules.ts
400
+ import { createContext, useContext } from "react";
401
+ var ShellModulesContext = createContext(null);
402
+ function useShellModules() {
403
+ const registry = useContext(ShellModulesContext);
404
+ if (!registry) {
405
+ throw new Error("useShellModules must be used within an AppShell");
406
+ }
407
+ return registry;
408
+ }
409
+
416
410
  // src/hooks.ts
417
411
  function useExtensionPoint(name) {
418
412
  const { getExtensionPoint } = useShellModules();
@@ -430,8 +424,145 @@ function useShellEvent(key, handler) {
430
424
  }, [key]);
431
425
  }
432
426
 
427
+ // src/SidebarGroup.tsx
428
+ import { useState } from "react";
429
+ import { ChevronDown } from "lucide-react";
430
+ import { cn as cn2 } from "@petrarca/sonnet-core";
431
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
432
+ function GroupHeading({
433
+ heading,
434
+ canCollapse,
435
+ open,
436
+ onToggle
437
+ }) {
438
+ return /* @__PURE__ */ jsxs3(
439
+ "button",
440
+ {
441
+ type: "button",
442
+ onClick: canCollapse ? onToggle : void 0,
443
+ className: cn2(
444
+ "flex w-full items-center gap-1 px-2 py-1.5",
445
+ "text-[11px] font-semibold uppercase tracking-widest text-muted-foreground/70",
446
+ canCollapse ? "hover:text-foreground cursor-pointer" : "cursor-default"
447
+ ),
448
+ children: [
449
+ canCollapse && /* @__PURE__ */ jsx5(
450
+ ChevronDown,
451
+ {
452
+ className: cn2("h-3 w-3 transition-transform", !open && "-rotate-90")
453
+ }
454
+ ),
455
+ /* @__PURE__ */ jsx5("span", { children: heading })
456
+ ]
457
+ }
458
+ );
459
+ }
460
+ function SidebarGroup({
461
+ heading,
462
+ collapsible = false,
463
+ defaultOpen = true,
464
+ separator = false,
465
+ children
466
+ }) {
467
+ const [open, setOpen] = useState(defaultOpen);
468
+ const canCollapse = collapsible && !!heading;
469
+ return /* @__PURE__ */ jsxs3("div", { className: cn2(separator && "border-b border-border pb-2"), children: [
470
+ heading && /* @__PURE__ */ jsx5(
471
+ GroupHeading,
472
+ {
473
+ heading,
474
+ canCollapse,
475
+ open,
476
+ onToggle: () => setOpen((v) => !v)
477
+ }
478
+ ),
479
+ (!canCollapse || open) && /* @__PURE__ */ jsx5("ul", { className: "space-y-0.5", children })
480
+ ] });
481
+ }
482
+
483
+ // src/SidebarItem.tsx
484
+ import { Link, useLocation } from "react-router-dom";
485
+ import { ChevronRight } from "lucide-react";
486
+ import { cn as cn3 } from "@petrarca/sonnet-core";
487
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
488
+ function ItemTrailing({
489
+ badge,
490
+ expandable,
491
+ expanded,
492
+ onToggle
493
+ }) {
494
+ if (badge != null) {
495
+ return /* @__PURE__ */ jsx6("span", { className: "inline-flex items-center rounded-full bg-muted px-1.5 h-[18px] text-[10px] tabular-nums text-muted-foreground", children: badge });
496
+ }
497
+ if (expandable) {
498
+ const handleClick = (e) => {
499
+ e.preventDefault();
500
+ e.stopPropagation();
501
+ onToggle?.();
502
+ };
503
+ return /* @__PURE__ */ jsx6(
504
+ "button",
505
+ {
506
+ type: "button",
507
+ onClick: handleClick,
508
+ className: "p-0.5 text-muted-foreground hover:text-foreground",
509
+ children: /* @__PURE__ */ jsx6(
510
+ ChevronRight,
511
+ {
512
+ className: cn3(
513
+ "h-3 w-3 transition-transform",
514
+ expanded && "rotate-90"
515
+ )
516
+ }
517
+ )
518
+ }
519
+ );
520
+ }
521
+ return null;
522
+ }
523
+ function SidebarItem({
524
+ icon: Icon,
525
+ label,
526
+ path,
527
+ badge,
528
+ indent = 0,
529
+ expandable = false,
530
+ expanded = false,
531
+ onToggle,
532
+ onClick
533
+ }) {
534
+ const { pathname } = useLocation();
535
+ const isActive = pathname === path;
536
+ const style = indent > 0 ? { paddingLeft: `${indent * 16 + 8}px` } : void 0;
537
+ return /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsxs4(
538
+ Link,
539
+ {
540
+ to: path,
541
+ onClick,
542
+ style,
543
+ className: cn3(
544
+ "flex items-center gap-2 rounded px-2 py-1.5 text-[13px] font-medium transition-colors select-none",
545
+ isActive ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent/50"
546
+ ),
547
+ children: [
548
+ Icon && /* @__PURE__ */ jsx6(Icon, { className: "h-4 w-4 shrink-0" }),
549
+ /* @__PURE__ */ jsx6("span", { className: "min-w-0 flex-1 truncate", children: label }),
550
+ /* @__PURE__ */ jsx6(
551
+ ItemTrailing,
552
+ {
553
+ badge,
554
+ expandable,
555
+ expanded,
556
+ onToggle
557
+ }
558
+ )
559
+ ]
560
+ }
561
+ ) });
562
+ }
563
+
433
564
  // src/SubNavPanel.tsx
434
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
565
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
435
566
  function useMergedNavigation(service) {
436
567
  const contributions = useExtensionPoint(`${service.id}.nav`);
437
568
  return useMemo2(() => {
@@ -452,66 +583,125 @@ function SubNavPanel({
452
583
  collapsed,
453
584
  onToggleCollapse
454
585
  }) {
455
- const { pathname } = useLocation();
456
586
  const mergedNavigation = useMergedNavigation(service);
457
587
  if (collapsed) {
458
- return /* @__PURE__ */ jsx4("aside", { className: "shrink-0 border-r bg-background flex flex-col items-center pt-2.5", children: /* @__PURE__ */ jsxs3(Tooltip2, { children: [
459
- /* @__PURE__ */ jsx4(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx4(
588
+ return /* @__PURE__ */ jsx7("aside", { className: "shrink-0 border-r bg-background flex flex-col items-center pt-2.5", children: /* @__PURE__ */ jsxs5(Tooltip2, { children: [
589
+ /* @__PURE__ */ jsx7(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx7(
460
590
  "button",
461
591
  {
592
+ type: "button",
462
593
  className: "text-muted-foreground/50 hover:text-muted-foreground transition-colors p-1",
463
594
  onClick: onToggleCollapse,
464
- children: /* @__PURE__ */ jsx4(ChevronsRight, { className: "h-4 w-4" })
595
+ children: /* @__PURE__ */ jsx7(ChevronsRight, { className: "h-4 w-4" })
465
596
  }
466
597
  ) }),
467
- /* @__PURE__ */ jsx4(TooltipContent2, { side: "right", children: "Expand navigation" })
598
+ /* @__PURE__ */ jsx7(TooltipContent2, { side: "right", children: "Expand navigation" })
468
599
  ] }) });
469
600
  }
470
- return /* @__PURE__ */ jsxs3("aside", { className: "w-52 shrink-0 border-r bg-background flex flex-col", children: [
471
- /* @__PURE__ */ jsxs3("div", { className: "h-10 shrink-0 flex items-center justify-between px-4", children: [
472
- /* @__PURE__ */ jsx4("span", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: service.label }),
473
- /* @__PURE__ */ jsxs3(Tooltip2, { children: [
474
- /* @__PURE__ */ jsx4(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx4(
601
+ return /* @__PURE__ */ jsxs5("aside", { className: "w-52 shrink-0 border-r bg-background flex flex-col", children: [
602
+ /* @__PURE__ */ jsxs5("div", { className: "h-10 shrink-0 flex items-center justify-between px-4", children: [
603
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-semibold text-foreground", children: service.label }),
604
+ /* @__PURE__ */ jsxs5(Tooltip2, { children: [
605
+ /* @__PURE__ */ jsx7(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx7(
475
606
  "button",
476
607
  {
608
+ type: "button",
477
609
  className: "text-muted-foreground/40 hover:text-muted-foreground transition-colors p-0.5",
478
610
  onClick: onToggleCollapse,
479
- children: /* @__PURE__ */ jsx4(ChevronsLeft, { className: "h-3.5 w-3.5" })
611
+ children: /* @__PURE__ */ jsx7(ChevronsLeft, { className: "h-3.5 w-3.5" })
480
612
  }
481
613
  ) }),
482
- /* @__PURE__ */ jsx4(TooltipContent2, { side: "right", children: "Collapse navigation" })
614
+ /* @__PURE__ */ jsx7(TooltipContent2, { side: "right", children: "Collapse navigation" })
483
615
  ] })
484
616
  ] }),
485
- /* @__PURE__ */ jsx4(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx4("nav", { className: "px-2 pb-4", children: mergedNavigation.map((group, gi) => /* @__PURE__ */ jsxs3("div", { className: cn2(gi > 0 && "mt-4"), children: [
486
- group.heading && /* @__PURE__ */ jsx4("h6", { className: "text-xs font-medium font-mono uppercase tracking-wider text-muted-foreground px-2 mb-1", children: group.heading }),
487
- /* @__PURE__ */ jsx4("ul", { className: "space-y-0.5", children: group.links.map((link) => {
488
- const isActive = pathname === link.path;
489
- return /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
490
- Link,
617
+ /* @__PURE__ */ jsx7(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx7("nav", { className: "px-1.5 pb-4 space-y-2", children: mergedNavigation.map((group) => /* @__PURE__ */ jsx7(
618
+ SidebarGroup,
619
+ {
620
+ heading: group.heading,
621
+ collapsible: group.collapsible,
622
+ children: group.links.map((link) => /* @__PURE__ */ jsx7(
623
+ SidebarItem,
491
624
  {
492
- to: link.path,
493
- className: cn2(
494
- "flex items-center rounded-md px-2 py-1.5 text-sm transition-colors",
495
- isActive ? "bg-accent text-accent-foreground font-medium" : "text-muted-foreground hover:text-foreground hover:bg-accent/50"
496
- ),
497
- children: link.label
498
- }
499
- ) }, link.path);
500
- }) })
501
- ] }, group.id)) }) })
625
+ label: link.label,
626
+ path: link.path,
627
+ badge: link.badge
628
+ },
629
+ link.path
630
+ ))
631
+ },
632
+ group.id
633
+ )) }) })
634
+ ] });
635
+ }
636
+
637
+ // src/shellNavigation.ts
638
+ import { createContext as createContext2, useContext as useContext2 } from "react";
639
+ var ShellNavigationContext = createContext2(null);
640
+ function useShellNavigation() {
641
+ const ctx = useContext2(ShellNavigationContext);
642
+ if (!ctx) {
643
+ throw new Error("useShellNavigation must be used within AppShell");
644
+ }
645
+ return ctx;
646
+ }
647
+
648
+ // src/ShellRail.tsx
649
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
650
+ function ShellRail() {
651
+ const { mainModules, bottomModules } = useShellModules();
652
+ const {
653
+ activeService,
654
+ sidePaneModuleId,
655
+ subNavCollapsed,
656
+ onToggleSubNav,
657
+ onServiceSelect
658
+ } = useShellNavigation();
659
+ const isActive = (id) => activeService?.id === id || sidePaneModuleId === id;
660
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
661
+ /* @__PURE__ */ jsxs6(IconRail, { children: [
662
+ /* @__PURE__ */ jsx8("div", { className: "flex-1 flex flex-col items-center gap-1 overflow-y-auto", children: mainModules.map((service) => /* @__PURE__ */ jsx8(
663
+ RailIcon,
664
+ {
665
+ icon: service.icon,
666
+ label: service.label,
667
+ active: isActive(service.id),
668
+ onClick: () => onServiceSelect(service.id)
669
+ },
670
+ service.id
671
+ )) }),
672
+ /* @__PURE__ */ jsx8(RailSeparator, {}),
673
+ /* @__PURE__ */ jsx8("div", { className: "flex flex-col items-center gap-1", children: bottomModules.map((service) => /* @__PURE__ */ jsx8(
674
+ RailIcon,
675
+ {
676
+ icon: service.icon,
677
+ label: service.label,
678
+ active: isActive(service.id),
679
+ onClick: () => onServiceSelect(service.id)
680
+ },
681
+ service.id
682
+ )) })
683
+ ] }),
684
+ activeService && activeService.navigation.length > 0 && /* @__PURE__ */ jsx8(
685
+ SubNavPanel,
686
+ {
687
+ service: activeService,
688
+ collapsed: subNavCollapsed,
689
+ onToggleCollapse: onToggleSubNav
690
+ }
691
+ )
502
692
  ] });
503
693
  }
504
694
 
505
695
  // src/SidePane.tsx
506
696
  import { X } from "lucide-react";
507
- import { cn as cn3 } from "@petrarca/sonnet-core";
697
+ import { cn as cn4 } from "@petrarca/sonnet-core";
508
698
  import { useResizablePanel } from "@petrarca/sonnet-core/hooks";
509
699
 
510
700
  // src/sidePaneState.ts
511
- import { createContext as createContext2, useContext as useContext2 } from "react";
512
- var SidePaneContext = createContext2(null);
701
+ import { createContext as createContext3, useContext as useContext3 } from "react";
702
+ var SidePaneContext = createContext3(null);
513
703
  function useSidePaneState() {
514
- const ctx = useContext2(SidePaneContext);
704
+ const ctx = useContext3(SidePaneContext);
515
705
  if (!ctx) {
516
706
  throw new Error("useSidePaneState must be used within an AppShell");
517
707
  }
@@ -519,19 +709,19 @@ function useSidePaneState() {
519
709
  }
520
710
 
521
711
  // src/SidePane.tsx
522
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
712
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
523
713
  function DragHandle({
524
714
  separatorProps,
525
715
  onPointerDown,
526
716
  onDoubleClick
527
717
  }) {
528
- return /* @__PURE__ */ jsx5(
718
+ return /* @__PURE__ */ jsx9(
529
719
  "div",
530
720
  {
531
721
  ...separatorProps,
532
722
  onPointerDown,
533
723
  onDoubleClick,
534
- className: cn3(
724
+ className: cn4(
535
725
  "absolute right-0 top-0 h-full w-1 cursor-col-resize",
536
726
  "hover:bg-primary/40 active:bg-primary/60 transition-colors"
537
727
  )
@@ -544,7 +734,7 @@ var DEFAULT_MAX = 800;
544
734
  function SidePane() {
545
735
  const { pane, close } = useSidePaneState();
546
736
  if (!pane) return null;
547
- return /* @__PURE__ */ jsx5(SidePaneOpen, { pane, onClose: close });
737
+ return /* @__PURE__ */ jsx9(SidePaneOpen, { pane, onClose: close });
548
738
  }
549
739
  function SidePaneOpen({
550
740
  pane,
@@ -557,30 +747,30 @@ function SidePaneOpen({
557
747
  direction: "right-edge"
558
748
  });
559
749
  const isFullWidth = pane.fullWidth === true;
560
- return /* @__PURE__ */ jsxs4(
750
+ return /* @__PURE__ */ jsxs7(
561
751
  "div",
562
752
  {
563
- className: cn3(
753
+ className: cn4(
564
754
  "relative flex flex-col border-r bg-background",
565
755
  isFullWidth ? "flex-1" : "shrink-0"
566
756
  ),
567
757
  style: isFullWidth ? void 0 : { width: panelWidth },
568
758
  children: [
569
- /* @__PURE__ */ jsx5("div", { className: "flex h-10 shrink-0 items-center justify-end border-b px-2", children: /* @__PURE__ */ jsx5(
759
+ /* @__PURE__ */ jsx9("div", { className: "flex h-10 shrink-0 items-center justify-end border-b px-2", children: /* @__PURE__ */ jsx9(
570
760
  "button",
571
761
  {
572
762
  onClick: onClose,
573
- className: cn3(
763
+ className: cn4(
574
764
  "flex h-6 w-6 items-center justify-center rounded text-muted-foreground",
575
765
  "hover:bg-accent hover:text-foreground transition-colors",
576
766
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
577
767
  ),
578
768
  "aria-label": "Close side pane",
579
- children: /* @__PURE__ */ jsx5(X, { className: "h-3.5 w-3.5" })
769
+ children: /* @__PURE__ */ jsx9(X, { className: "h-3.5 w-3.5" })
580
770
  }
581
771
  ) }),
582
- /* @__PURE__ */ jsx5("div", { className: "flex-1 overflow-auto", children: pane.content }),
583
- !isFullWidth && /* @__PURE__ */ jsx5(
772
+ /* @__PURE__ */ jsx9("div", { className: "flex-1 overflow-auto", children: pane.content }),
773
+ !isFullWidth && /* @__PURE__ */ jsx9(
584
774
  DragHandle,
585
775
  {
586
776
  separatorProps,
@@ -605,7 +795,7 @@ import {
605
795
  CommandList,
606
796
  CommandSeparator
607
797
  } from "@petrarca/sonnet-ui";
608
- import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
798
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
609
799
  function CommandMenu({ open, onOpenChange }) {
610
800
  const navigate = useNavigate();
611
801
  const { mainModules, bottomModules } = useShellModules();
@@ -629,16 +819,16 @@ function CommandMenu({ open, onOpenChange }) {
629
819
  const renderNavGroup = (service) => {
630
820
  if (service.navigation.length === 0) return null;
631
821
  const Icon = service.icon;
632
- return /* @__PURE__ */ jsx6(CommandGroup, { heading: service.label, children: service.navigation.flatMap(
633
- (group) => group.links.map((link) => /* @__PURE__ */ jsxs5(
822
+ return /* @__PURE__ */ jsx10(CommandGroup, { heading: service.label, children: service.navigation.flatMap(
823
+ (group) => group.links.map((link) => /* @__PURE__ */ jsxs8(
634
824
  CommandItem,
635
825
  {
636
826
  value: `${service.label} ${group.heading ?? ""} ${link.label}`,
637
827
  onSelect: () => handleSelect(link.path),
638
828
  children: [
639
- /* @__PURE__ */ jsx6(Icon, { className: "mr-2 h-4 w-4 text-muted-foreground" }),
640
- /* @__PURE__ */ jsx6("span", { children: link.label }),
641
- group.heading && /* @__PURE__ */ jsx6("span", { className: "ml-auto text-xs text-muted-foreground", children: group.heading })
829
+ /* @__PURE__ */ jsx10(Icon, { className: "mr-2 h-4 w-4 text-muted-foreground" }),
830
+ /* @__PURE__ */ jsx10("span", { children: link.label }),
831
+ group.heading && /* @__PURE__ */ jsx10("span", { className: "ml-auto text-xs text-muted-foreground", children: group.heading })
642
832
  ]
643
833
  },
644
834
  link.path
@@ -648,13 +838,13 @@ function CommandMenu({ open, onOpenChange }) {
648
838
  const renderCommandGroup = (service) => {
649
839
  if (!service.commands?.length) return null;
650
840
  const Icon = service.icon;
651
- return /* @__PURE__ */ jsx6(
841
+ return /* @__PURE__ */ jsx10(
652
842
  CommandGroup,
653
843
  {
654
844
  heading: service.commands[0].group ?? service.label,
655
845
  children: service.commands.map((cmd) => {
656
846
  const CmdIcon = cmd.icon ?? Icon;
657
- return /* @__PURE__ */ jsxs5(
847
+ return /* @__PURE__ */ jsxs8(
658
848
  CommandItem,
659
849
  {
660
850
  value: `${service.label} ${cmd.label}`,
@@ -663,8 +853,8 @@ function CommandMenu({ open, onOpenChange }) {
663
853
  onOpenChange(false);
664
854
  },
665
855
  children: [
666
- /* @__PURE__ */ jsx6(CmdIcon, { className: "mr-2 h-4 w-4 text-muted-foreground" }),
667
- /* @__PURE__ */ jsx6("span", { children: cmd.label })
856
+ /* @__PURE__ */ jsx10(CmdIcon, { className: "mr-2 h-4 w-4 text-muted-foreground" }),
857
+ /* @__PURE__ */ jsx10("span", { children: cmd.label })
668
858
  ]
669
859
  },
670
860
  cmd.id
@@ -675,15 +865,15 @@ function CommandMenu({ open, onOpenChange }) {
675
865
  );
676
866
  };
677
867
  const allModules = [...mainModules, ...bottomModules];
678
- return /* @__PURE__ */ jsxs5(CommandDialog, { open, onOpenChange, children: [
679
- /* @__PURE__ */ jsx6(CommandInput, { placeholder: "Search pages, services, actions..." }),
680
- /* @__PURE__ */ jsxs5(CommandList, { children: [
681
- /* @__PURE__ */ jsx6(CommandEmpty, { children: "No results found." }),
868
+ return /* @__PURE__ */ jsxs8(CommandDialog, { open, onOpenChange, children: [
869
+ /* @__PURE__ */ jsx10(CommandInput, { placeholder: "Search pages, services, actions..." }),
870
+ /* @__PURE__ */ jsxs8(CommandList, { children: [
871
+ /* @__PURE__ */ jsx10(CommandEmpty, { children: "No results found." }),
682
872
  mainModules.map(renderNavGroup),
683
- /* @__PURE__ */ jsx6(CommandSeparator, {}),
873
+ /* @__PURE__ */ jsx10(CommandSeparator, {}),
684
874
  bottomModules.map(renderNavGroup),
685
- allModules.some((m) => m.commands?.length) && /* @__PURE__ */ jsxs5(Fragment, { children: [
686
- /* @__PURE__ */ jsx6(CommandSeparator, {}),
875
+ allModules.some((m) => m.commands?.length) && /* @__PURE__ */ jsxs8(Fragment2, { children: [
876
+ /* @__PURE__ */ jsx10(CommandSeparator, {}),
687
877
  allModules.map(renderCommandGroup)
688
878
  ] })
689
879
  ] })
@@ -691,10 +881,10 @@ function CommandMenu({ open, onOpenChange }) {
691
881
  }
692
882
 
693
883
  // src/shellConfig.ts
694
- import { createContext as createContext3, useContext as useContext3 } from "react";
695
- var ShellConfigContext = createContext3(null);
884
+ import { createContext as createContext4, useContext as useContext4 } from "react";
885
+ var ShellConfigContext = createContext4(null);
696
886
  function useShellConfig() {
697
- const config = useContext3(ShellConfigContext);
887
+ const config = useContext4(ShellConfigContext);
698
888
  if (!config) {
699
889
  throw new Error("useShellConfig must be used within a ShellConfigProvider");
700
890
  }
@@ -702,7 +892,7 @@ function useShellConfig() {
702
892
  }
703
893
 
704
894
  // src/AppShell.tsx
705
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
895
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
706
896
  var PANEL_WIDTH_CLASS = {
707
897
  narrow: "w-[480px]",
708
898
  default: "w-[640px]",
@@ -776,7 +966,7 @@ function useActiveService(modules) {
776
966
  },
777
967
  [serviceByBasePath]
778
968
  );
779
- const [selectedServiceId, setSelectedServiceId] = useState(
969
+ const [selectedServiceId, setSelectedServiceId] = useState2(
780
970
  () => resolveServiceFromPath(location.pathname)
781
971
  );
782
972
  useEffect3(
@@ -787,7 +977,7 @@ function useActiveService(modules) {
787
977
  return { activeService, selectedServiceId, setSelectedServiceId };
788
978
  }
789
979
  function useSidePaneState2() {
790
- const [sidePaneState, setSidePaneState] = useState(
980
+ const [sidePaneState, setSidePaneState] = useState2(
791
981
  null
792
982
  );
793
983
  const handleOpen = useCallback2(
@@ -817,7 +1007,7 @@ function useSidePaneState2() {
817
1007
  };
818
1008
  }
819
1009
  function usePanelState() {
820
- const [panelState, setPanelState] = useState(null);
1010
+ const [panelState, setPanelState] = useState2(null);
821
1011
  const panelOnCloseRef = useRef2(void 0);
822
1012
  const handleOpen = useCallback2(
823
1013
  (opts) => setPanelState(opts),
@@ -839,7 +1029,7 @@ function usePanelState() {
839
1029
  };
840
1030
  }
841
1031
  function useDialogState() {
842
- const [dialogState, setDialogState] = useState(null);
1032
+ const [dialogState, setDialogState] = useState2(null);
843
1033
  const handleConfirm = useCallback2(() => {
844
1034
  dialogState?.resolve(true);
845
1035
  setDialogState(null);
@@ -863,15 +1053,15 @@ function useFeatureLookup(modules) {
863
1053
  return (featureId) => map.get(featureId) ?? null;
864
1054
  }, [modules]);
865
1055
  }
866
- function AppShell({ registry }) {
1056
+ function AppShell({ registry, sidebar }) {
867
1057
  const navigate = useNavigate2();
868
1058
  const config = useShellConfig();
869
1059
  const { modules } = registry;
870
1060
  const { activeService, setSelectedServiceId } = useActiveService(modules);
871
- const [subNavCollapsed, setSubNavCollapsed] = useState(false);
872
- const [commandMenuOpen, setCommandMenuOpen] = useState(false);
1061
+ const [subNavCollapsed, setSubNavCollapsed] = useState2(false);
1062
+ const [commandMenuOpen, setCommandMenuOpen] = useState2(false);
873
1063
  const openCommandMenu = useCallback2(() => setCommandMenuOpen(true), []);
874
- const [fullscreenState, setFullscreenState] = useState(null);
1064
+ const [fullscreenState, setFullscreenState] = useState2(null);
875
1065
  const handleFullscreenEnter = useCallback2(
876
1066
  (opts) => setFullscreenState(opts),
877
1067
  []
@@ -946,27 +1136,32 @@ function AppShell({ registry }) {
946
1136
  }),
947
1137
  [sidePane2]
948
1138
  );
949
- return /* @__PURE__ */ jsx7(ShellModulesContext.Provider, { value: registry, children: /* @__PURE__ */ jsx7(SidePaneContext.Provider, { value: sidePaneContextValue, children: /* @__PURE__ */ jsx7(TooltipProvider, { children: /* @__PURE__ */ jsxs6("div", { className: "flex h-screen w-screen flex-col overflow-hidden", children: [
950
- /* @__PURE__ */ jsx7(TopBar, { children: config.topBar }),
951
- /* @__PURE__ */ jsx7(
1139
+ const navigationValue = {
1140
+ activeService,
1141
+ sidePaneModuleId: sidePane2.sidePaneState?.moduleId ?? null,
1142
+ subNavCollapsed,
1143
+ onToggleSubNav: () => setSubNavCollapsed((c) => !c),
1144
+ onServiceSelect: handleServiceSelect
1145
+ };
1146
+ return /* @__PURE__ */ jsx11(ShellModulesContext.Provider, { value: registry, children: /* @__PURE__ */ jsx11(SidePaneContext.Provider, { value: sidePaneContextValue, children: /* @__PURE__ */ jsx11(ShellNavigationContext.Provider, { value: navigationValue, children: /* @__PURE__ */ jsx11(TooltipProvider, { children: /* @__PURE__ */ jsxs9("div", { className: "flex h-screen w-screen flex-col overflow-hidden", children: [
1147
+ /* @__PURE__ */ jsx11(TopBar, { children: config.topBar }),
1148
+ /* @__PURE__ */ jsx11(
952
1149
  CommandMenu,
953
1150
  {
954
1151
  open: commandMenuOpen,
955
1152
  onOpenChange: setCommandMenuOpen
956
1153
  }
957
1154
  ),
958
- /* @__PURE__ */ jsx7(
1155
+ /* @__PURE__ */ jsx11(
959
1156
  ShellBody,
960
1157
  {
961
1158
  activeService,
962
1159
  sidePaneFullWidth: sidePane2.sidePaneState?.fullWidth,
963
- sidePaneModuleId: sidePane2.sidePaneState?.moduleId ?? null,
964
- subNavCollapsed,
965
- onToggleSubNav: () => setSubNavCollapsed((c) => !c),
966
- onServiceSelect: handleServiceSelect
1160
+ sidebar
967
1161
  }
968
1162
  ),
969
- dialog2.dialogState && /* @__PURE__ */ jsx7(
1163
+ config.footer && /* @__PURE__ */ jsx11(ShellFooter, { children: config.footer }),
1164
+ dialog2.dialogState && /* @__PURE__ */ jsx11(
970
1165
  ConfirmDialog,
971
1166
  {
972
1167
  open: true,
@@ -979,8 +1174,8 @@ function AppShell({ registry }) {
979
1174
  onCancel: dialog2.handleCancel
980
1175
  }
981
1176
  ),
982
- fullscreenState && /* @__PURE__ */ jsx7("div", { className: "fixed inset-0 z-50 bg-background overflow-auto", children: fullscreenState.content }),
983
- /* @__PURE__ */ jsx7(
1177
+ fullscreenState && /* @__PURE__ */ jsx11("div", { className: "fixed inset-0 z-50 bg-background overflow-auto", children: fullscreenState.content }),
1178
+ /* @__PURE__ */ jsx11(
984
1179
  SlideOverPanel,
985
1180
  {
986
1181
  panelState: panel2.panelState,
@@ -990,7 +1185,7 @@ function AppShell({ registry }) {
990
1185
  onClose: panel2.handleClose
991
1186
  }
992
1187
  )
993
- ] }) }) }) });
1188
+ ] }) }) }) }) });
994
1189
  }
995
1190
  function SlideOverPanel({
996
1191
  panelState,
@@ -999,14 +1194,14 @@ function SlideOverPanel({
999
1194
  panelOnCloseRef,
1000
1195
  onClose
1001
1196
  }) {
1002
- return /* @__PURE__ */ jsx7(
1197
+ return /* @__PURE__ */ jsx11(
1003
1198
  Sheet,
1004
1199
  {
1005
1200
  open: panelState !== null,
1006
1201
  onOpenChange: (open) => {
1007
1202
  if (!open) onClose();
1008
1203
  },
1009
- children: /* @__PURE__ */ jsx7(
1204
+ children: /* @__PURE__ */ jsx11(
1010
1205
  SheetContent,
1011
1206
  {
1012
1207
  side: "right",
@@ -1021,7 +1216,7 @@ function SlideOverPanel({
1021
1216
  ...!panelState?.description && {
1022
1217
  "aria-describedby": void 0
1023
1218
  },
1024
- children: panelState && /* @__PURE__ */ jsx7(PanelContent, { state: panelState })
1219
+ children: panelState && /* @__PURE__ */ jsx11(PanelContent, { state: panelState })
1025
1220
  }
1026
1221
  )
1027
1222
  }
@@ -1030,45 +1225,27 @@ function SlideOverPanel({
1030
1225
  function ShellBody({
1031
1226
  activeService,
1032
1227
  sidePaneFullWidth,
1033
- sidePaneModuleId,
1034
- subNavCollapsed,
1035
- onToggleSubNav,
1036
- onServiceSelect
1228
+ sidebar
1037
1229
  }) {
1038
- return /* @__PURE__ */ jsxs6("div", { className: "flex flex-1 overflow-hidden", children: [
1039
- /* @__PURE__ */ jsx7(
1040
- IconRail,
1041
- {
1042
- activeServiceId: activeService?.id ?? null,
1043
- activeSidePaneModuleId: sidePaneModuleId,
1044
- onServiceSelect
1045
- }
1046
- ),
1047
- activeService && activeService.navigation.length > 0 && /* @__PURE__ */ jsx7(
1048
- SubNavPanel,
1049
- {
1050
- service: activeService,
1051
- collapsed: subNavCollapsed,
1052
- onToggleCollapse: onToggleSubNav
1053
- }
1054
- ),
1055
- /* @__PURE__ */ jsx7(SidePane, {}),
1056
- sidePaneFullWidth !== true && /* @__PURE__ */ jsx7("div", { className: "flex-1 min-w-0 h-full overflow-hidden", children: /* @__PURE__ */ jsx7(ContentArea, { layout: activeService?.layout }) })
1230
+ return /* @__PURE__ */ jsxs9("div", { className: "flex flex-1 overflow-hidden", children: [
1231
+ sidebar ?? /* @__PURE__ */ jsx11(ShellRail, {}),
1232
+ /* @__PURE__ */ jsx11(SidePane, {}),
1233
+ sidePaneFullWidth !== true && /* @__PURE__ */ jsx11("div", { className: "flex-1 min-w-0 h-full overflow-hidden", children: /* @__PURE__ */ jsx11(ContentArea, { layout: activeService?.layout }) })
1057
1234
  ] });
1058
1235
  }
1059
1236
  function ContentArea({ layout }) {
1060
1237
  if (layout === "full") {
1061
- return /* @__PURE__ */ jsx7("div", { className: "h-full w-full overflow-hidden", children: /* @__PURE__ */ jsx7(Outlet, {}) });
1238
+ return /* @__PURE__ */ jsx11("div", { className: "h-full w-full overflow-hidden", children: /* @__PURE__ */ jsx11(Outlet, {}) });
1062
1239
  }
1063
- return /* @__PURE__ */ jsx7(ScrollArea2, { className: "h-full", children: /* @__PURE__ */ jsx7("main", { className: "p-6", children: /* @__PURE__ */ jsx7(Outlet, {}) }) });
1240
+ return /* @__PURE__ */ jsx11(ScrollArea2, { className: "h-full", children: /* @__PURE__ */ jsx11("main", { className: "p-6", children: /* @__PURE__ */ jsx11(Outlet, {}) }) });
1064
1241
  }
1065
1242
  function PanelContent({ state }) {
1066
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1067
- state.title ? /* @__PURE__ */ jsxs6(SheetHeader, { children: [
1068
- /* @__PURE__ */ jsx7(SheetTitle, { children: state.title }),
1069
- state.description && /* @__PURE__ */ jsx7(SheetDescription, { children: state.description })
1070
- ] }) : /* @__PURE__ */ jsx7(SheetTitle, { className: "sr-only", children: "Panel" }),
1071
- /* @__PURE__ */ jsx7(SheetBody, { children: state.content })
1243
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
1244
+ state.title ? /* @__PURE__ */ jsxs9(SheetHeader, { children: [
1245
+ /* @__PURE__ */ jsx11(SheetTitle, { children: state.title }),
1246
+ state.description && /* @__PURE__ */ jsx11(SheetDescription, { children: state.description })
1247
+ ] }) : /* @__PURE__ */ jsx11(SheetTitle, { className: "sr-only", children: "Panel" }),
1248
+ /* @__PURE__ */ jsx11(SheetBody, { children: state.content })
1072
1249
  ] });
1073
1250
  }
1074
1251
 
@@ -1076,32 +1253,36 @@ function PanelContent({ state }) {
1076
1253
  import { Toaster } from "sonner";
1077
1254
 
1078
1255
  // src/ShellConfigProvider.tsx
1079
- import { jsx as jsx8 } from "react/jsx-runtime";
1256
+ import { jsx as jsx12 } from "react/jsx-runtime";
1080
1257
  function ShellConfigProvider({
1081
1258
  config,
1082
1259
  children
1083
1260
  }) {
1084
- return /* @__PURE__ */ jsx8(ShellConfigContext.Provider, { value: config, children });
1261
+ return /* @__PURE__ */ jsx12(ShellConfigContext.Provider, { value: config, children });
1085
1262
  }
1086
1263
 
1087
1264
  // src/RootLayout.tsx
1088
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1089
- function RootLayout({ config, registry }) {
1090
- return /* @__PURE__ */ jsxs7(ShellConfigProvider, { config, children: [
1091
- /* @__PURE__ */ jsx9(Toaster, { position: "top-right", richColors: true, closeButton: true }),
1092
- /* @__PURE__ */ jsx9(AppShell, { registry })
1265
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1266
+ function RootLayout({
1267
+ config,
1268
+ registry,
1269
+ sidebar
1270
+ }) {
1271
+ return /* @__PURE__ */ jsxs10(ShellConfigProvider, { config, children: [
1272
+ /* @__PURE__ */ jsx13(Toaster, { position: "top-right", richColors: true, closeButton: true }),
1273
+ /* @__PURE__ */ jsx13(AppShell, { registry, sidebar })
1093
1274
  ] });
1094
1275
  }
1095
1276
 
1096
1277
  // src/PlaceholderPage.tsx
1097
1278
  import { useLocation as useLocation3 } from "react-router-dom";
1098
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1279
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1099
1280
  function PlaceholderPage() {
1100
1281
  const { pathname } = useLocation3();
1101
- return /* @__PURE__ */ jsxs8("div", { children: [
1102
- /* @__PURE__ */ jsx10("h1", { className: "mb-1", children: pathToTitle(pathname) }),
1103
- /* @__PURE__ */ jsx10("p", { className: "text-sm text-muted-foreground mb-6", children: pathname }),
1104
- /* @__PURE__ */ jsx10("div", { className: "rounded-lg border-2 border-dashed border-border p-12 flex items-center justify-center", children: /* @__PURE__ */ jsx10("span", { className: "text-sm text-muted-foreground", children: "Content area \u2014 not yet implemented" }) })
1282
+ return /* @__PURE__ */ jsxs11("div", { children: [
1283
+ /* @__PURE__ */ jsx14("h1", { className: "mb-1", children: pathToTitle(pathname) }),
1284
+ /* @__PURE__ */ jsx14("p", { className: "text-sm text-muted-foreground mb-6", children: pathname }),
1285
+ /* @__PURE__ */ jsx14("div", { className: "rounded-lg border-2 border-dashed border-border p-12 flex items-center justify-center", children: /* @__PURE__ */ jsx14("span", { className: "text-sm text-muted-foreground", children: "Content area \u2014 not yet implemented" }) })
1105
1286
  ] });
1106
1287
  }
1107
1288
  function pathToTitle(path) {
@@ -1110,12 +1291,131 @@ function pathToTitle(path) {
1110
1291
  return last.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
1111
1292
  }
1112
1293
 
1294
+ // src/Sidebar.tsx
1295
+ import { cn as cn5 } from "@petrarca/sonnet-core";
1296
+ import { ScrollArea as ScrollArea3 } from "@petrarca/sonnet-ui";
1297
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1298
+ function Sidebar({
1299
+ children,
1300
+ header,
1301
+ width = 260,
1302
+ className
1303
+ }) {
1304
+ return /* @__PURE__ */ jsxs12(
1305
+ "aside",
1306
+ {
1307
+ style: { width },
1308
+ className: cn5(
1309
+ "shrink-0 border-r bg-muted/30 flex flex-col h-full",
1310
+ className
1311
+ ),
1312
+ children: [
1313
+ header,
1314
+ /* @__PURE__ */ jsx15(ScrollArea3, { className: "flex-1", children: /* @__PURE__ */ jsx15("nav", { className: "px-1.5 py-2 space-y-3", children }) })
1315
+ ]
1316
+ }
1317
+ );
1318
+ }
1319
+
1320
+ // src/ShellSidebar.tsx
1321
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1322
+ function ShellSidebar({
1323
+ bottomGroupLabel = "Tools",
1324
+ ...sidebarProps
1325
+ }) {
1326
+ const { mainModules, bottomModules } = useShellModules();
1327
+ const topNavItems = mainModules.flatMap(
1328
+ (mod) => (mod.topNav ?? []).map((link) => ({ link, icon: mod.icon }))
1329
+ );
1330
+ return /* @__PURE__ */ jsxs13(Sidebar, { ...sidebarProps, children: [
1331
+ topNavItems.length > 0 && /* @__PURE__ */ jsx16(SidebarGroup, { separator: true, children: topNavItems.map(({ link, icon }) => /* @__PURE__ */ jsx16(
1332
+ SidebarItem,
1333
+ {
1334
+ icon,
1335
+ label: link.label,
1336
+ path: link.path,
1337
+ badge: link.badge
1338
+ },
1339
+ link.path
1340
+ )) }),
1341
+ mainModules.map((mod) => {
1342
+ if (mod.navigation.length === 0) {
1343
+ return /* @__PURE__ */ jsx16(
1344
+ SidebarItem,
1345
+ {
1346
+ icon: mod.icon,
1347
+ label: mod.label,
1348
+ path: mod.basePath
1349
+ },
1350
+ mod.id
1351
+ );
1352
+ }
1353
+ return /* @__PURE__ */ jsx16(SidebarGroup, { heading: mod.label, collapsible: true, children: mod.navigation.flatMap((group) => [
1354
+ // Render NavGroup heading if it has one
1355
+ ...group.heading ? [
1356
+ /* @__PURE__ */ jsx16(
1357
+ SidebarGroup,
1358
+ {
1359
+ heading: group.heading,
1360
+ collapsible: group.collapsible,
1361
+ children: group.links.map((link) => /* @__PURE__ */ jsx16(
1362
+ SidebarItem,
1363
+ {
1364
+ icon: mod.icon,
1365
+ label: link.label,
1366
+ path: link.path,
1367
+ badge: link.badge
1368
+ },
1369
+ link.path
1370
+ ))
1371
+ },
1372
+ group.id
1373
+ )
1374
+ ] : group.links.map((link) => /* @__PURE__ */ jsx16(
1375
+ SidebarItem,
1376
+ {
1377
+ icon: mod.icon,
1378
+ label: link.label,
1379
+ path: link.path,
1380
+ badge: link.badge
1381
+ },
1382
+ link.path
1383
+ ))
1384
+ ]) }, mod.id);
1385
+ }),
1386
+ bottomModules.length > 0 && /* @__PURE__ */ jsx16(SidebarGroup, { heading: bottomGroupLabel, collapsible: true, children: bottomModules.map((mod) => /* @__PURE__ */ jsx16(
1387
+ SidebarItem,
1388
+ {
1389
+ icon: mod.icon,
1390
+ label: mod.label,
1391
+ path: mod.basePath
1392
+ },
1393
+ mod.id
1394
+ )) })
1395
+ ] });
1396
+ }
1397
+
1398
+ // src/ShellVersion.tsx
1399
+ import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1400
+ function ShellVersion({
1401
+ name,
1402
+ version
1403
+ }) {
1404
+ return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
1405
+ /* @__PURE__ */ jsx17("span", { children: name }),
1406
+ version && /* @__PURE__ */ jsxs14("span", { className: "text-muted-foreground/50", children: [
1407
+ "v",
1408
+ version
1409
+ ] })
1410
+ ] });
1411
+ }
1412
+
1113
1413
  // src/SearchTrigger.tsx
1114
1414
  import { Search } from "lucide-react";
1115
1415
  import { Button as Button3 } from "@petrarca/sonnet-ui";
1116
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1416
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1117
1417
  function SearchTrigger() {
1118
- return /* @__PURE__ */ jsxs9(
1418
+ return /* @__PURE__ */ jsxs15(
1119
1419
  Button3,
1120
1420
  {
1121
1421
  variant: "outline",
@@ -1123,10 +1423,10 @@ function SearchTrigger() {
1123
1423
  className: "h-8 gap-2 text-sm text-muted-foreground w-56 justify-start",
1124
1424
  onClick: () => navigation.openCommandMenu(),
1125
1425
  children: [
1126
- /* @__PURE__ */ jsx11(Search, { className: "h-4 w-4" }),
1127
- /* @__PURE__ */ jsx11("span", { children: "Search..." }),
1128
- /* @__PURE__ */ jsxs9("kbd", { className: "ml-auto pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground", children: [
1129
- /* @__PURE__ */ jsx11("span", { className: "text-xs", children: "\u2318" }),
1426
+ /* @__PURE__ */ jsx18(Search, { className: "h-4 w-4" }),
1427
+ /* @__PURE__ */ jsx18("span", { children: "Search..." }),
1428
+ /* @__PURE__ */ jsxs15("kbd", { className: "ml-auto pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground", children: [
1429
+ /* @__PURE__ */ jsx18("span", { className: "text-xs", children: "\u2318" }),
1130
1430
  "K"
1131
1431
  ] })
1132
1432
  ]
@@ -1146,23 +1446,23 @@ import {
1146
1446
  DropdownMenuSeparator,
1147
1447
  DropdownMenuTrigger
1148
1448
  } from "@petrarca/sonnet-ui";
1149
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1449
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
1150
1450
  function UserMenu({ user, onSignOut }) {
1151
- return /* @__PURE__ */ jsxs10(DropdownMenu, { children: [
1152
- /* @__PURE__ */ jsx12(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx12(Button4, { variant: "ghost", size: "compact", className: "rounded-full", children: /* @__PURE__ */ jsx12(Avatar, { className: "h-7 w-7", children: /* @__PURE__ */ jsx12(AvatarFallback, { className: "text-xs", children: user.initials }) }) }) }),
1153
- /* @__PURE__ */ jsxs10(DropdownMenuContent, { align: "end", className: "w-48", children: [
1154
- /* @__PURE__ */ jsx12(DropdownMenuLabel, { className: "font-normal", children: /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-1", children: [
1155
- /* @__PURE__ */ jsx12("p", { className: "text-sm font-medium", children: user.name }),
1156
- /* @__PURE__ */ jsx12("p", { className: "text-xs text-muted-foreground", children: user.email })
1451
+ return /* @__PURE__ */ jsxs16(DropdownMenu, { children: [
1452
+ /* @__PURE__ */ jsx19(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx19(Button4, { variant: "ghost", size: "compact", className: "rounded-full", children: /* @__PURE__ */ jsx19(Avatar, { className: "h-7 w-7", children: /* @__PURE__ */ jsx19(AvatarFallback, { className: "text-xs", children: user.initials }) }) }) }),
1453
+ /* @__PURE__ */ jsxs16(DropdownMenuContent, { align: "end", className: "w-48", children: [
1454
+ /* @__PURE__ */ jsx19(DropdownMenuLabel, { className: "font-normal", children: /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-1", children: [
1455
+ /* @__PURE__ */ jsx19("p", { className: "text-sm font-medium", children: user.name }),
1456
+ /* @__PURE__ */ jsx19("p", { className: "text-xs text-muted-foreground", children: user.email })
1157
1457
  ] }) }),
1158
- /* @__PURE__ */ jsx12(DropdownMenuSeparator, {}),
1159
- /* @__PURE__ */ jsxs10(DropdownMenuItem, { children: [
1160
- /* @__PURE__ */ jsx12(User, { className: "mr-2 h-4 w-4" }),
1458
+ /* @__PURE__ */ jsx19(DropdownMenuSeparator, {}),
1459
+ /* @__PURE__ */ jsxs16(DropdownMenuItem, { children: [
1460
+ /* @__PURE__ */ jsx19(User, { className: "mr-2 h-4 w-4" }),
1161
1461
  "Profile"
1162
1462
  ] }),
1163
- /* @__PURE__ */ jsx12(DropdownMenuSeparator, {}),
1164
- /* @__PURE__ */ jsxs10(DropdownMenuItem, { onSelect: onSignOut, children: [
1165
- /* @__PURE__ */ jsx12(LogOut, { className: "mr-2 h-4 w-4" }),
1463
+ /* @__PURE__ */ jsx19(DropdownMenuSeparator, {}),
1464
+ /* @__PURE__ */ jsxs16(DropdownMenuItem, { onSelect: onSignOut, children: [
1465
+ /* @__PURE__ */ jsx19(LogOut, { className: "mr-2 h-4 w-4" }),
1166
1466
  "Sign out"
1167
1467
  ] })
1168
1468
  ] })
@@ -1193,13 +1493,13 @@ function createModuleRegistry(modules) {
1193
1493
  }
1194
1494
 
1195
1495
  // src/OverviewCard.tsx
1196
- import { cn as cn4 } from "@petrarca/sonnet-core";
1197
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1496
+ import { cn as cn6 } from "@petrarca/sonnet-core";
1497
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
1198
1498
  function FeatureLink({
1199
1499
  feature,
1200
1500
  children
1201
1501
  }) {
1202
- return /* @__PURE__ */ jsx13(
1502
+ return /* @__PURE__ */ jsx20(
1203
1503
  "span",
1204
1504
  {
1205
1505
  role: "link",
@@ -1227,22 +1527,22 @@ function OverviewCard({
1227
1527
  feature,
1228
1528
  className
1229
1529
  }) {
1230
- return /* @__PURE__ */ jsxs11(
1530
+ return /* @__PURE__ */ jsxs17(
1231
1531
  "button",
1232
1532
  {
1233
1533
  onClick: feature ? () => navigation.goToFeature(feature) : void 0,
1234
1534
  disabled: !feature,
1235
- className: cn4(
1535
+ className: cn6(
1236
1536
  "rounded-lg border bg-card p-5 text-left flex flex-col gap-3",
1237
1537
  "disabled:cursor-default",
1238
1538
  feature && "hover:bg-accent/50 transition-colors",
1239
1539
  className
1240
1540
  ),
1241
1541
  children: [
1242
- /* @__PURE__ */ jsx13("div", { className: "h-9 w-9 rounded-md bg-blue-50 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx13(Icon, { className: "h-5 w-5 text-blue-600" }) }),
1243
- /* @__PURE__ */ jsxs11("div", { children: [
1244
- /* @__PURE__ */ jsx13("p", { className: "text-sm font-medium mb-1", children: title }),
1245
- /* @__PURE__ */ jsx13("p", { className: "text-xs text-muted-foreground leading-relaxed", children: description })
1542
+ /* @__PURE__ */ jsx20("div", { className: "h-9 w-9 rounded-md bg-blue-50 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx20(Icon, { className: "h-5 w-5 text-blue-600" }) }),
1543
+ /* @__PURE__ */ jsxs17("div", { children: [
1544
+ /* @__PURE__ */ jsx20("p", { className: "text-sm font-medium mb-1", children: title }),
1545
+ /* @__PURE__ */ jsx20("p", { className: "text-xs text-muted-foreground leading-relaxed", children: description })
1246
1546
  ] })
1247
1547
  ]
1248
1548
  }
@@ -1256,11 +1556,20 @@ export {
1256
1556
  IconRail,
1257
1557
  OverviewCard,
1258
1558
  PlaceholderPage,
1559
+ RailIcon,
1560
+ RailSeparator,
1259
1561
  RootLayout,
1260
1562
  SearchTrigger,
1261
1563
  ShellConfigProvider,
1564
+ ShellFooter,
1565
+ ShellRail,
1566
+ ShellSidebar,
1567
+ ShellVersion,
1262
1568
  SidePane,
1263
1569
  SidePaneContext,
1570
+ Sidebar,
1571
+ SidebarGroup,
1572
+ SidebarItem,
1264
1573
  SubNavPanel,
1265
1574
  TopBar,
1266
1575
  UserMenu,
@@ -1269,6 +1578,8 @@ export {
1269
1578
  events,
1270
1579
  fullscreen,
1271
1580
  initDialog,
1581
+ initFeatureNav,
1582
+ initFullscreen,
1272
1583
  initNavigation,
1273
1584
  initPanel,
1274
1585
  initSidePane,
@@ -1281,6 +1592,7 @@ export {
1281
1592
  useShellConfig,
1282
1593
  useShellEvent,
1283
1594
  useShellModules,
1595
+ useShellNavigation,
1284
1596
  useSidePaneState
1285
1597
  };
1286
1598
  //# sourceMappingURL=index.js.map