@petrarca/sonnet-shell 0.2.0 → 0.4.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,11 +1,12 @@
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,
7
- useMemo as useMemo3
7
+ useMemo as useMemo5
8
8
  } from "react";
9
+ import { useResizablePanel as useResizablePanel2 } from "@petrarca/sonnet-core/hooks";
9
10
  import { Outlet, useLocation as useLocation2, useNavigate as useNavigate2 } from "react-router-dom";
10
11
  import { TooltipProvider } from "@petrarca/sonnet-ui";
11
12
  import { ScrollArea as ScrollArea2 } from "@petrarca/sonnet-ui";
@@ -68,82 +69,71 @@ function TopBar({ children }) {
68
69
  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
70
  }
70
71
 
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;
72
+ // src/ShellFooter.tsx
73
+ import { jsx as jsx3 } from "react/jsx-runtime";
74
+ function ShellFooter({ children }) {
75
+ 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
76
  }
87
77
 
78
+ // src/ShellRail.tsx
79
+ import { useMemo as useMemo3 } from "react";
80
+
88
81
  // src/IconRail.tsx
89
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
82
+ import { cn } from "@petrarca/sonnet-core";
83
+ import { Button as Button2, Separator } from "@petrarca/sonnet-ui";
84
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@petrarca/sonnet-ui";
85
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
90
86
  function IconRail({
91
- activeServiceId,
92
- activeSidePaneModuleId,
93
- onServiceSelect
87
+ children,
88
+ className
94
89
  }) {
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
- ] });
90
+ return /* @__PURE__ */ jsx4(
91
+ "nav",
92
+ {
93
+ className: cn(
94
+ "w-14 shrink-0 border-r bg-muted flex flex-col items-center py-2 gap-1",
95
+ className
96
+ ),
97
+ children
98
+ }
99
+ );
118
100
  }
119
- function RailIcon({ service, isActive, onClick }) {
120
- const Icon = service.icon;
101
+ function RailIcon({
102
+ icon: Icon,
103
+ label,
104
+ active = false,
105
+ onClick,
106
+ disabled = false,
107
+ tooltip
108
+ }) {
121
109
  return /* @__PURE__ */ jsxs2(Tooltip, { delayDuration: 0, children: [
122
- /* @__PURE__ */ jsx3(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
110
+ /* @__PURE__ */ jsx4(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
123
111
  Button2,
124
112
  {
125
113
  variant: "ghost",
126
114
  size: "compact",
127
- onClick,
115
+ "aria-disabled": disabled || void 0,
116
+ onClick: disabled ? void 0 : onClick,
128
117
  className: cn(
129
118
  "h-10 w-10 rounded-lg transition-colors",
130
- isActive ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:text-foreground"
119
+ disabled ? "text-muted-foreground/40 cursor-default hover:bg-transparent" : active ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground hover:bg-muted-foreground/10"
131
120
  ),
132
121
  children: [
133
- /* @__PURE__ */ jsx3(Icon, { className: "h-5 w-5" }),
134
- /* @__PURE__ */ jsx3("span", { className: "sr-only", children: service.label })
122
+ /* @__PURE__ */ jsx4(Icon, { className: "h-5 w-5" }),
123
+ /* @__PURE__ */ jsx4("span", { className: "sr-only", children: label })
135
124
  ]
136
125
  }
137
126
  ) }),
138
- /* @__PURE__ */ jsx3(TooltipContent, { side: "right", sideOffset: 8, children: service.label })
127
+ /* @__PURE__ */ jsx4(TooltipContent, { side: "right", sideOffset: 8, children: tooltip ?? label })
139
128
  ] });
140
129
  }
130
+ function RailSeparator() {
131
+ return /* @__PURE__ */ jsx4(Separator, { className: "w-6 my-1" });
132
+ }
141
133
 
142
134
  // src/SubNavPanel.tsx
143
135
  import { useMemo as useMemo2 } from "react";
144
- import { useLocation, Link } from "react-router-dom";
145
136
  import { ChevronsLeft, ChevronsRight } from "lucide-react";
146
- import { cn as cn2 } from "@petrarca/sonnet-core";
147
137
  import { ScrollArea } from "@petrarca/sonnet-ui";
148
138
  import { Tooltip as Tooltip2, TooltipContent as TooltipContent2, TooltipTrigger as TooltipTrigger2 } from "@petrarca/sonnet-ui";
149
139
 
@@ -413,6 +403,17 @@ var events = {
413
403
  }
414
404
  };
415
405
 
406
+ // src/shellModules.ts
407
+ import { createContext, useContext } from "react";
408
+ var ShellModulesContext = createContext(null);
409
+ function useShellModules() {
410
+ const registry = useContext(ShellModulesContext);
411
+ if (!registry) {
412
+ throw new Error("useShellModules must be used within an AppShell");
413
+ }
414
+ return registry;
415
+ }
416
+
416
417
  // src/hooks.ts
417
418
  function useExtensionPoint(name) {
418
419
  const { getExtensionPoint } = useShellModules();
@@ -430,8 +431,145 @@ function useShellEvent(key, handler) {
430
431
  }, [key]);
431
432
  }
432
433
 
434
+ // src/SidebarGroup.tsx
435
+ import { useState } from "react";
436
+ import { ChevronDown } from "lucide-react";
437
+ import { cn as cn2 } from "@petrarca/sonnet-core";
438
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
439
+ function GroupHeading({
440
+ heading,
441
+ canCollapse,
442
+ open,
443
+ onToggle
444
+ }) {
445
+ return /* @__PURE__ */ jsxs3(
446
+ "button",
447
+ {
448
+ type: "button",
449
+ onClick: canCollapse ? onToggle : void 0,
450
+ className: cn2(
451
+ "flex w-full items-center gap-1 px-2 py-1.5",
452
+ "text-[11px] font-semibold uppercase tracking-widest text-muted-foreground/70",
453
+ canCollapse ? "hover:text-foreground cursor-pointer" : "cursor-default"
454
+ ),
455
+ children: [
456
+ canCollapse && /* @__PURE__ */ jsx5(
457
+ ChevronDown,
458
+ {
459
+ className: cn2("h-3 w-3 transition-transform", !open && "-rotate-90")
460
+ }
461
+ ),
462
+ /* @__PURE__ */ jsx5("span", { children: heading })
463
+ ]
464
+ }
465
+ );
466
+ }
467
+ function SidebarGroup({
468
+ heading,
469
+ collapsible = false,
470
+ defaultOpen = true,
471
+ separator = false,
472
+ children
473
+ }) {
474
+ const [open, setOpen] = useState(defaultOpen);
475
+ const canCollapse = collapsible && !!heading;
476
+ return /* @__PURE__ */ jsxs3("div", { className: cn2(separator && "border-b border-border pb-2"), children: [
477
+ heading && /* @__PURE__ */ jsx5(
478
+ GroupHeading,
479
+ {
480
+ heading,
481
+ canCollapse,
482
+ open,
483
+ onToggle: () => setOpen((v) => !v)
484
+ }
485
+ ),
486
+ (!canCollapse || open) && /* @__PURE__ */ jsx5("ul", { className: "space-y-0.5", children })
487
+ ] });
488
+ }
489
+
490
+ // src/SidebarItem.tsx
491
+ import { Link, useLocation } from "react-router-dom";
492
+ import { ChevronRight } from "lucide-react";
493
+ import { cn as cn3 } from "@petrarca/sonnet-core";
494
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
495
+ function ItemTrailing({
496
+ badge,
497
+ expandable,
498
+ expanded,
499
+ onToggle
500
+ }) {
501
+ if (badge != null) {
502
+ 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 });
503
+ }
504
+ if (expandable) {
505
+ const handleClick = (e) => {
506
+ e.preventDefault();
507
+ e.stopPropagation();
508
+ onToggle?.();
509
+ };
510
+ return /* @__PURE__ */ jsx6(
511
+ "button",
512
+ {
513
+ type: "button",
514
+ onClick: handleClick,
515
+ className: "p-0.5 text-muted-foreground hover:text-foreground",
516
+ children: /* @__PURE__ */ jsx6(
517
+ ChevronRight,
518
+ {
519
+ className: cn3(
520
+ "h-3 w-3 transition-transform",
521
+ expanded && "rotate-90"
522
+ )
523
+ }
524
+ )
525
+ }
526
+ );
527
+ }
528
+ return null;
529
+ }
530
+ function SidebarItem({
531
+ icon: Icon,
532
+ label,
533
+ path,
534
+ badge,
535
+ indent = 0,
536
+ expandable = false,
537
+ expanded = false,
538
+ onToggle,
539
+ onClick
540
+ }) {
541
+ const { pathname } = useLocation();
542
+ const isActive = pathname === path;
543
+ const style = indent > 0 ? { paddingLeft: `${indent * 16 + 8}px` } : void 0;
544
+ return /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsxs4(
545
+ Link,
546
+ {
547
+ to: path,
548
+ onClick,
549
+ style,
550
+ className: cn3(
551
+ "flex items-center gap-2 rounded px-2 py-1.5 text-[13px] font-medium transition-colors select-none",
552
+ isActive ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent/50"
553
+ ),
554
+ children: [
555
+ Icon && /* @__PURE__ */ jsx6(Icon, { className: "h-4 w-4 shrink-0" }),
556
+ /* @__PURE__ */ jsx6("span", { className: "min-w-0 flex-1 truncate", children: label }),
557
+ /* @__PURE__ */ jsx6(
558
+ ItemTrailing,
559
+ {
560
+ badge,
561
+ expandable,
562
+ expanded,
563
+ onToggle
564
+ }
565
+ )
566
+ ]
567
+ }
568
+ ) });
569
+ }
570
+
433
571
  // src/SubNavPanel.tsx
434
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
572
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
435
573
  function useMergedNavigation(service) {
436
574
  const contributions = useExtensionPoint(`${service.id}.nav`);
437
575
  return useMemo2(() => {
@@ -452,99 +590,249 @@ function SubNavPanel({
452
590
  collapsed,
453
591
  onToggleCollapse
454
592
  }) {
455
- const { pathname } = useLocation();
456
593
  const mergedNavigation = useMergedNavigation(service);
457
594
  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(
595
+ return /* @__PURE__ */ jsx7("aside", { className: "shrink-0 border-r bg-background flex flex-col items-center pt-2.5", children: /* @__PURE__ */ jsxs5(Tooltip2, { children: [
596
+ /* @__PURE__ */ jsx7(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx7(
460
597
  "button",
461
598
  {
599
+ type: "button",
462
600
  className: "text-muted-foreground/50 hover:text-muted-foreground transition-colors p-1",
463
601
  onClick: onToggleCollapse,
464
- children: /* @__PURE__ */ jsx4(ChevronsRight, { className: "h-4 w-4" })
602
+ children: /* @__PURE__ */ jsx7(ChevronsRight, { className: "h-4 w-4" })
465
603
  }
466
604
  ) }),
467
- /* @__PURE__ */ jsx4(TooltipContent2, { side: "right", children: "Expand navigation" })
605
+ /* @__PURE__ */ jsx7(TooltipContent2, { side: "right", children: "Expand navigation" })
468
606
  ] }) });
469
607
  }
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(
608
+ return /* @__PURE__ */ jsxs5("aside", { className: "w-52 shrink-0 border-r bg-background flex flex-col", children: [
609
+ /* @__PURE__ */ jsxs5("div", { className: "h-10 shrink-0 flex items-center justify-between px-4", children: [
610
+ /* @__PURE__ */ jsx7("span", { className: "text-sm font-semibold text-foreground", children: service.label }),
611
+ /* @__PURE__ */ jsxs5(Tooltip2, { children: [
612
+ /* @__PURE__ */ jsx7(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx7(
475
613
  "button",
476
614
  {
615
+ type: "button",
477
616
  className: "text-muted-foreground/40 hover:text-muted-foreground transition-colors p-0.5",
478
617
  onClick: onToggleCollapse,
479
- children: /* @__PURE__ */ jsx4(ChevronsLeft, { className: "h-3.5 w-3.5" })
618
+ children: /* @__PURE__ */ jsx7(ChevronsLeft, { className: "h-3.5 w-3.5" })
480
619
  }
481
620
  ) }),
482
- /* @__PURE__ */ jsx4(TooltipContent2, { side: "right", children: "Collapse navigation" })
621
+ /* @__PURE__ */ jsx7(TooltipContent2, { side: "right", children: "Collapse navigation" })
483
622
  ] })
484
623
  ] }),
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,
624
+ /* @__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(
625
+ SidebarGroup,
626
+ {
627
+ heading: group.heading,
628
+ collapsible: group.collapsible,
629
+ children: group.links.map((link) => /* @__PURE__ */ jsx7(
630
+ SidebarItem,
491
631
  {
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)) }) })
632
+ label: link.label,
633
+ path: link.path,
634
+ badge: link.badge
635
+ },
636
+ link.path
637
+ ))
638
+ },
639
+ group.id
640
+ )) }) })
641
+ ] });
642
+ }
643
+
644
+ // src/shellNavigation.ts
645
+ import { createContext as createContext2, useContext as useContext2 } from "react";
646
+ var ShellNavigationContext = createContext2(null);
647
+ function useShellNavigation() {
648
+ const ctx = useContext2(ShellNavigationContext);
649
+ if (!ctx) {
650
+ throw new Error("useShellNavigation must be used within AppShell");
651
+ }
652
+ return ctx;
653
+ }
654
+
655
+ // src/shellConfig.ts
656
+ import { createContext as createContext3, useContext as useContext3 } from "react";
657
+ var ShellConfigContext = createContext3(null);
658
+ function useShellConfig() {
659
+ const config = useContext3(ShellConfigContext);
660
+ if (!config) {
661
+ throw new Error("useShellConfig must be used within a ShellConfigProvider");
662
+ }
663
+ return config;
664
+ }
665
+
666
+ // src/capabilities.ts
667
+ import { warnLog as warnLog3 } from "@petrarca/sonnet-core";
668
+ var DEFAULT_CAPABILITY_POLICY = {
669
+ "missing-state": { rail: "disabled", command: "disabled", route: "redirect" },
670
+ "not-authorized": { rail: "hidden", command: "hidden", route: "redirect" },
671
+ "not-authorized-soft": {
672
+ rail: "disabled",
673
+ command: "disabled",
674
+ route: "redirect"
675
+ },
676
+ "feature-off": { rail: "hidden", command: "disabled", route: "redirect" },
677
+ error: { rail: "hidden", command: "hidden", route: "redirect" }
678
+ };
679
+ var EFFECT_RANK = {
680
+ hidden: 3,
681
+ redirect: 2,
682
+ disabled: 1,
683
+ visible: 0
684
+ };
685
+ function resolveCapabilityPolicy(override) {
686
+ if (!override) return DEFAULT_CAPABILITY_POLICY;
687
+ const merged = {};
688
+ for (const kind of Object.keys(DEFAULT_CAPABILITY_POLICY)) {
689
+ merged[kind] = {
690
+ ...DEFAULT_CAPABILITY_POLICY[kind],
691
+ ...override[kind] ?? {}
692
+ };
693
+ }
694
+ return merged;
695
+ }
696
+ function foldUnmet(effect, resultReason, acc) {
697
+ const rank = EFFECT_RANK[effect];
698
+ const worstRank = EFFECT_RANK[acc.effect];
699
+ if (rank > worstRank) {
700
+ return { effect, reason: resultReason };
701
+ }
702
+ if (rank === worstRank && !acc.reason && resultReason) {
703
+ return { effect: acc.effect, reason: resultReason };
704
+ }
705
+ return acc;
706
+ }
707
+ function evaluateModule(module, surface, resolve, policy) {
708
+ const capabilities = module.requires;
709
+ if (!capabilities || capabilities.length === 0) return { effect: "visible" };
710
+ if (!resolve) return { effect: "visible" };
711
+ let acc = { effect: "visible" };
712
+ for (const capability of capabilities) {
713
+ const result = resolve(capability);
714
+ if (result.met) continue;
715
+ const kind = result.kind ?? "error";
716
+ if (kind === "error") {
717
+ warnLog3(
718
+ `[capabilities] unmet capability "${capability}" resolved to kind "error" (fail-closed)`
719
+ );
720
+ }
721
+ acc = foldUnmet(policy[kind][surface], result.reason, acc);
722
+ }
723
+ return acc;
724
+ }
725
+
726
+ // src/ShellRail.tsx
727
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
728
+ function ShellRail() {
729
+ const { mainModules, bottomModules } = useShellModules();
730
+ const { resolveCapability, capabilityPolicy } = useShellConfig();
731
+ const {
732
+ activeService,
733
+ sidePaneModuleId,
734
+ subNavCollapsed,
735
+ onToggleSubNav,
736
+ onServiceSelect
737
+ } = useShellNavigation();
738
+ const isActive = (id) => activeService?.id === id || sidePaneModuleId === id;
739
+ const policy = useMemo3(
740
+ () => resolveCapabilityPolicy(capabilityPolicy),
741
+ [capabilityPolicy]
742
+ );
743
+ const renderModule = (service) => {
744
+ const { effect, reason } = evaluateModule(
745
+ service,
746
+ "rail",
747
+ resolveCapability,
748
+ policy
749
+ );
750
+ if (effect === "hidden" || effect === "redirect") return null;
751
+ const disabled = effect === "disabled";
752
+ return /* @__PURE__ */ jsx8(
753
+ RailIcon,
754
+ {
755
+ icon: service.icon,
756
+ label: service.label,
757
+ active: isActive(service.id),
758
+ onClick: () => onServiceSelect(service.id),
759
+ disabled,
760
+ tooltip: disabled && reason ? `${service.label} \u2014 ${reason}` : void 0
761
+ },
762
+ service.id
763
+ );
764
+ };
765
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
766
+ /* @__PURE__ */ jsxs6(IconRail, { children: [
767
+ /* @__PURE__ */ jsx8("div", { className: "flex-1 flex flex-col items-center gap-1 overflow-y-auto", children: mainModules.map(renderModule) }),
768
+ /* @__PURE__ */ jsx8(RailSeparator, {}),
769
+ /* @__PURE__ */ jsx8("div", { className: "flex flex-col items-center gap-1", children: bottomModules.map(renderModule) })
770
+ ] }),
771
+ activeService && activeService.navigation.length > 0 && /* @__PURE__ */ jsx8(
772
+ SubNavPanel,
773
+ {
774
+ service: activeService,
775
+ collapsed: subNavCollapsed,
776
+ onToggleCollapse: onToggleSubNav
777
+ }
778
+ )
502
779
  ] });
503
780
  }
504
781
 
505
782
  // src/SidePane.tsx
506
783
  import { X } from "lucide-react";
507
- import { cn as cn3 } from "@petrarca/sonnet-core";
784
+ import { cn as cn5 } from "@petrarca/sonnet-core";
508
785
  import { useResizablePanel } from "@petrarca/sonnet-core/hooks";
509
786
 
510
787
  // src/sidePaneState.ts
511
- import { createContext as createContext2, useContext as useContext2 } from "react";
512
- var SidePaneContext = createContext2(null);
788
+ import { createContext as createContext4, useContext as useContext4 } from "react";
789
+ var SidePaneContext = createContext4(null);
513
790
  function useSidePaneState() {
514
- const ctx = useContext2(SidePaneContext);
791
+ const ctx = useContext4(SidePaneContext);
515
792
  if (!ctx) {
516
793
  throw new Error("useSidePaneState must be used within an AppShell");
517
794
  }
518
795
  return ctx;
519
796
  }
520
797
 
521
- // src/SidePane.tsx
522
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
798
+ // src/DragHandle.tsx
799
+ import { cn as cn4 } from "@petrarca/sonnet-core";
800
+ import { jsx as jsx9 } from "react/jsx-runtime";
523
801
  function DragHandle({
802
+ edge,
524
803
  separatorProps,
525
804
  onPointerDown,
526
805
  onDoubleClick
527
806
  }) {
528
- return /* @__PURE__ */ jsx5(
807
+ return /* @__PURE__ */ jsx9(
529
808
  "div",
530
809
  {
531
810
  ...separatorProps,
532
811
  onPointerDown,
533
812
  onDoubleClick,
534
- className: cn3(
535
- "absolute right-0 top-0 h-full w-1 cursor-col-resize",
536
- "hover:bg-primary/40 active:bg-primary/60 transition-colors"
813
+ className: cn4(
814
+ // 8px invisible hit area; visual feedback only via the before pseudo-element.
815
+ "absolute top-0 h-full w-2 cursor-col-resize",
816
+ edge === "right" ? "right-0" : "left-0",
817
+ // Centered line: invisible at rest, 2px on hover, 4px while dragging.
818
+ "before:absolute before:inset-y-0 before:left-1/2 before:-translate-x-1/2",
819
+ "before:w-0 before:transition-all before:duration-150",
820
+ "hover:before:w-0.5 hover:before:bg-primary/60",
821
+ "active:before:w-1 active:before:bg-primary/80"
537
822
  )
538
823
  }
539
824
  );
540
825
  }
826
+
827
+ // src/SidePane.tsx
828
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
541
829
  var DEFAULT_WIDTH = 320;
542
830
  var DEFAULT_MIN = 240;
543
831
  var DEFAULT_MAX = 800;
544
832
  function SidePane() {
545
833
  const { pane, close } = useSidePaneState();
546
834
  if (!pane) return null;
547
- return /* @__PURE__ */ jsx5(SidePaneOpen, { pane, onClose: close });
835
+ return /* @__PURE__ */ jsx10(SidePaneOpen, { pane, onClose: close });
548
836
  }
549
837
  function SidePaneOpen({
550
838
  pane,
@@ -557,32 +845,33 @@ function SidePaneOpen({
557
845
  direction: "right-edge"
558
846
  });
559
847
  const isFullWidth = pane.fullWidth === true;
560
- return /* @__PURE__ */ jsxs4(
848
+ return /* @__PURE__ */ jsxs7(
561
849
  "div",
562
850
  {
563
- className: cn3(
851
+ className: cn5(
564
852
  "relative flex flex-col border-r bg-background",
565
853
  isFullWidth ? "flex-1" : "shrink-0"
566
854
  ),
567
855
  style: isFullWidth ? void 0 : { width: panelWidth },
568
856
  children: [
569
- /* @__PURE__ */ jsx5("div", { className: "flex h-10 shrink-0 items-center justify-end border-b px-2", children: /* @__PURE__ */ jsx5(
857
+ /* @__PURE__ */ jsx10("div", { className: "flex h-10 shrink-0 items-center justify-end border-b px-2", children: /* @__PURE__ */ jsx10(
570
858
  "button",
571
859
  {
572
860
  onClick: onClose,
573
- className: cn3(
861
+ className: cn5(
574
862
  "flex h-6 w-6 items-center justify-center rounded text-muted-foreground",
575
863
  "hover:bg-accent hover:text-foreground transition-colors",
576
864
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
577
865
  ),
578
866
  "aria-label": "Close side pane",
579
- children: /* @__PURE__ */ jsx5(X, { className: "h-3.5 w-3.5" })
867
+ children: /* @__PURE__ */ jsx10(X, { className: "h-3.5 w-3.5" })
580
868
  }
581
869
  ) }),
582
- /* @__PURE__ */ jsx5("div", { className: "flex-1 overflow-auto", children: pane.content }),
583
- !isFullWidth && /* @__PURE__ */ jsx5(
870
+ /* @__PURE__ */ jsx10("div", { className: "flex-1 overflow-auto", children: pane.content }),
871
+ !isFullWidth && /* @__PURE__ */ jsx10(
584
872
  DragHandle,
585
873
  {
874
+ edge: "right",
586
875
  separatorProps,
587
876
  onPointerDown: handlePointerDown,
588
877
  onDoubleClick: handleDoubleClick
@@ -594,7 +883,7 @@ function SidePaneOpen({
594
883
  }
595
884
 
596
885
  // src/CommandMenu.tsx
597
- import { useEffect as useEffect2, useCallback } from "react";
886
+ import { useEffect as useEffect2, useCallback, useMemo as useMemo4 } from "react";
598
887
  import { useNavigate } from "react-router-dom";
599
888
  import {
600
889
  CommandDialog,
@@ -605,10 +894,11 @@ import {
605
894
  CommandList,
606
895
  CommandSeparator
607
896
  } from "@petrarca/sonnet-ui";
608
- import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
897
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
609
898
  function CommandMenu({ open, onOpenChange }) {
610
899
  const navigate = useNavigate();
611
900
  const { mainModules, bottomModules } = useShellModules();
901
+ const { resolveCapability, capabilityPolicy } = useShellConfig();
612
902
  useEffect2(() => {
613
903
  const handleKeyDown = (e) => {
614
904
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
@@ -619,6 +909,11 @@ function CommandMenu({ open, onOpenChange }) {
619
909
  document.addEventListener("keydown", handleKeyDown);
620
910
  return () => document.removeEventListener("keydown", handleKeyDown);
621
911
  }, [open, onOpenChange]);
912
+ const policy = useMemo4(
913
+ () => resolveCapabilityPolicy(capabilityPolicy),
914
+ [capabilityPolicy]
915
+ );
916
+ const moduleEffect = (service) => evaluateModule(service, "command", resolveCapability, policy);
622
917
  const handleSelect = useCallback(
623
918
  (path) => {
624
919
  navigate(path);
@@ -628,17 +923,21 @@ function CommandMenu({ open, onOpenChange }) {
628
923
  );
629
924
  const renderNavGroup = (service) => {
630
925
  if (service.navigation.length === 0) return null;
926
+ const { effect, reason } = moduleEffect(service);
927
+ if (effect === "hidden" || effect === "redirect") return null;
928
+ const disabled = effect === "disabled";
631
929
  const Icon = service.icon;
632
- return /* @__PURE__ */ jsx6(CommandGroup, { heading: service.label, children: service.navigation.flatMap(
633
- (group) => group.links.map((link) => /* @__PURE__ */ jsxs5(
930
+ return /* @__PURE__ */ jsx11(CommandGroup, { heading: service.label, children: service.navigation.flatMap(
931
+ (group) => group.links.map((link) => /* @__PURE__ */ jsxs8(
634
932
  CommandItem,
635
933
  {
636
934
  value: `${service.label} ${group.heading ?? ""} ${link.label}`,
637
- onSelect: () => handleSelect(link.path),
935
+ disabled,
936
+ onSelect: disabled ? void 0 : () => handleSelect(link.path),
638
937
  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 })
938
+ /* @__PURE__ */ jsx11(Icon, { className: "mr-2 h-4 w-4 text-muted-foreground" }),
939
+ /* @__PURE__ */ jsx11("span", { children: link.label }),
940
+ disabled && reason ? /* @__PURE__ */ jsx11("span", { className: "ml-auto text-xs text-muted-foreground", children: reason }) : group.heading && /* @__PURE__ */ jsx11("span", { className: "ml-auto text-xs text-muted-foreground", children: group.heading })
642
941
  ]
643
942
  },
644
943
  link.path
@@ -647,24 +946,29 @@ function CommandMenu({ open, onOpenChange }) {
647
946
  };
648
947
  const renderCommandGroup = (service) => {
649
948
  if (!service.commands?.length) return null;
949
+ const { effect, reason } = moduleEffect(service);
950
+ if (effect === "hidden" || effect === "redirect") return null;
951
+ const disabled = effect === "disabled";
650
952
  const Icon = service.icon;
651
- return /* @__PURE__ */ jsx6(
953
+ return /* @__PURE__ */ jsx11(
652
954
  CommandGroup,
653
955
  {
654
956
  heading: service.commands[0].group ?? service.label,
655
957
  children: service.commands.map((cmd) => {
656
958
  const CmdIcon = cmd.icon ?? Icon;
657
- return /* @__PURE__ */ jsxs5(
959
+ return /* @__PURE__ */ jsxs8(
658
960
  CommandItem,
659
961
  {
660
962
  value: `${service.label} ${cmd.label}`,
661
- onSelect: () => {
963
+ disabled,
964
+ onSelect: disabled ? void 0 : () => {
662
965
  cmd.action();
663
966
  onOpenChange(false);
664
967
  },
665
968
  children: [
666
- /* @__PURE__ */ jsx6(CmdIcon, { className: "mr-2 h-4 w-4 text-muted-foreground" }),
667
- /* @__PURE__ */ jsx6("span", { children: cmd.label })
969
+ /* @__PURE__ */ jsx11(CmdIcon, { className: "mr-2 h-4 w-4 text-muted-foreground" }),
970
+ /* @__PURE__ */ jsx11("span", { children: cmd.label }),
971
+ disabled && reason && /* @__PURE__ */ jsx11("span", { className: "ml-auto text-xs text-muted-foreground", children: reason })
668
972
  ]
669
973
  },
670
974
  cmd.id
@@ -675,45 +979,69 @@ function CommandMenu({ open, onOpenChange }) {
675
979
  );
676
980
  };
677
981
  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." }),
982
+ const hasVisibleNav = (modules) => modules.some(
983
+ (m) => m.navigation.length > 0 && !["hidden", "redirect"].includes(moduleEffect(m).effect)
984
+ );
985
+ const hasVisibleCmds = allModules.some(
986
+ (m) => (m.commands?.length ?? 0) > 0 && !["hidden", "redirect"].includes(moduleEffect(m).effect)
987
+ );
988
+ return /* @__PURE__ */ jsxs8(CommandDialog, { open, onOpenChange, children: [
989
+ /* @__PURE__ */ jsx11(CommandInput, { placeholder: "Search pages, services, actions..." }),
990
+ /* @__PURE__ */ jsxs8(CommandList, { children: [
991
+ /* @__PURE__ */ jsx11(CommandEmpty, { children: "No results found." }),
682
992
  mainModules.map(renderNavGroup),
683
- /* @__PURE__ */ jsx6(CommandSeparator, {}),
993
+ hasVisibleNav(mainModules) && hasVisibleNav(bottomModules) && /* @__PURE__ */ jsx11(CommandSeparator, {}),
684
994
  bottomModules.map(renderNavGroup),
685
- allModules.some((m) => m.commands?.length) && /* @__PURE__ */ jsxs5(Fragment, { children: [
686
- /* @__PURE__ */ jsx6(CommandSeparator, {}),
995
+ hasVisibleCmds && /* @__PURE__ */ jsxs8(Fragment2, { children: [
996
+ /* @__PURE__ */ jsx11(CommandSeparator, {}),
687
997
  allModules.map(renderCommandGroup)
688
998
  ] })
689
999
  ] })
690
1000
  ] });
691
1001
  }
692
1002
 
693
- // src/shellConfig.ts
694
- import { createContext as createContext3, useContext as useContext3 } from "react";
695
- var ShellConfigContext = createContext3(null);
696
- function useShellConfig() {
697
- const config = useContext3(ShellConfigContext);
698
- if (!config) {
699
- throw new Error("useShellConfig must be used within a ShellConfigProvider");
1003
+ // src/ModuleEffects.tsx
1004
+ import React3 from "react";
1005
+ import { warnLog as warnLog4 } from "@petrarca/sonnet-core";
1006
+ import { Fragment as Fragment3, jsx as jsx12 } from "react/jsx-runtime";
1007
+ var ModuleEffectBoundary = class extends React3.Component {
1008
+ state = { caught: false };
1009
+ static getDerivedStateFromError() {
1010
+ return { caught: true };
700
1011
  }
701
- return config;
1012
+ componentDidCatch(error) {
1013
+ warnLog4(
1014
+ "[ModuleEffects] module '{}' useEffects threw \u2014 effects disabled for this module: {}",
1015
+ this.props.moduleId,
1016
+ error
1017
+ );
1018
+ }
1019
+ render() {
1020
+ return this.state.caught ? null : this.props.children;
1021
+ }
1022
+ };
1023
+ function ModuleEffectHost({ module }) {
1024
+ module.useEffects();
1025
+ return null;
1026
+ }
1027
+ function ModuleEffects({ modules }) {
1028
+ return /* @__PURE__ */ jsx12(Fragment3, { children: modules.filter((m) => typeof m.useEffects === "function").map((m) => /* @__PURE__ */ jsx12(ModuleEffectBoundary, { moduleId: m.id, children: /* @__PURE__ */ jsx12(ModuleEffectHost, { module: m }) }, m.id)) });
702
1029
  }
703
1030
 
704
1031
  // src/AppShell.tsx
705
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
706
- var PANEL_WIDTH_CLASS = {
707
- narrow: "w-[480px]",
708
- default: "w-[640px]",
709
- wide: "w-[800px]",
710
- half: "w-[50vw]",
711
- full: "w-screen",
1032
+ import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1033
+ var PANEL_WIDTHS = {
1034
+ narrow: { css: "w-[480px]", px: 480 },
1035
+ default: { css: "w-[640px]", px: 640 },
1036
+ wide: { css: "w-[800px]", px: 800 },
1037
+ half: { css: "w-[50vw]", px: 640 },
1038
+ full: { css: "w-screen", px: 640 },
712
1039
  // legacy aliases
713
- lg: "w-[640px]",
714
- "1/3": "w-[33vw]",
715
- "1/2": "w-[50vw]"
1040
+ lg: { css: "w-[640px]", px: 640 },
1041
+ "1/3": { css: "w-[33vw]", px: 640 },
1042
+ "1/2": { css: "w-[50vw]", px: 640 }
716
1043
  };
1044
+ var DEFAULT_PANEL = PANEL_WIDTHS.default;
717
1045
  function isSidePaneModule(service) {
718
1046
  return !!service.sidePane && service.navigation.length === 0;
719
1047
  }
@@ -761,7 +1089,7 @@ function useShellApiInit(deps) {
761
1089
  }
762
1090
  function useActiveService(modules) {
763
1091
  const location = useLocation2();
764
- const serviceByBasePath = useMemo3(
1092
+ const serviceByBasePath = useMemo5(
765
1093
  () => new Map(
766
1094
  modules.filter((m) => !m.hidden && m.basePath !== "/").map((m) => [m.basePath, m.id])
767
1095
  ),
@@ -776,7 +1104,7 @@ function useActiveService(modules) {
776
1104
  },
777
1105
  [serviceByBasePath]
778
1106
  );
779
- const [selectedServiceId, setSelectedServiceId] = useState(
1107
+ const [selectedServiceId, setSelectedServiceId] = useState2(
780
1108
  () => resolveServiceFromPath(location.pathname)
781
1109
  );
782
1110
  useEffect3(
@@ -787,7 +1115,7 @@ function useActiveService(modules) {
787
1115
  return { activeService, selectedServiceId, setSelectedServiceId };
788
1116
  }
789
1117
  function useSidePaneState2() {
790
- const [sidePaneState, setSidePaneState] = useState(
1118
+ const [sidePaneState, setSidePaneState] = useState2(
791
1119
  null
792
1120
  );
793
1121
  const handleOpen = useCallback2(
@@ -816,8 +1144,27 @@ function useSidePaneState2() {
816
1144
  handleSetFullWidth
817
1145
  };
818
1146
  }
1147
+ var DEFAULT_RESIZE_MIN = 240;
1148
+ var DEFAULT_RESIZE_MAX = 1200;
1149
+ function resizeOptionsFromPanel(state, widthEntry) {
1150
+ if (!state?.resizable) {
1151
+ return {
1152
+ enabled: false,
1153
+ defaultWidth: widthEntry.px,
1154
+ minWidth: DEFAULT_RESIZE_MIN,
1155
+ maxWidth: DEFAULT_RESIZE_MAX
1156
+ };
1157
+ }
1158
+ const viewportMax = typeof window !== "undefined" ? Math.round(window.innerWidth * 0.9) : DEFAULT_RESIZE_MAX;
1159
+ return {
1160
+ enabled: true,
1161
+ defaultWidth: widthEntry.px,
1162
+ minWidth: state.minWidth ?? DEFAULT_RESIZE_MIN,
1163
+ maxWidth: state.maxWidth ?? viewportMax
1164
+ };
1165
+ }
819
1166
  function usePanelState() {
820
- const [panelState, setPanelState] = useState(null);
1167
+ const [panelState, setPanelState] = useState2(null);
821
1168
  const panelOnCloseRef = useRef2(void 0);
822
1169
  const handleOpen = useCallback2(
823
1170
  (opts) => setPanelState(opts),
@@ -827,7 +1174,8 @@ function usePanelState() {
827
1174
  panelOnCloseRef.current = panelState?.onClose;
828
1175
  setPanelState(null);
829
1176
  }, [panelState]);
830
- const panelWidth = PANEL_WIDTH_CLASS[panelState?.width ?? "default"] ?? PANEL_WIDTH_CLASS.default;
1177
+ const widthEntry = PANEL_WIDTHS[panelState?.width ?? "default"] ?? DEFAULT_PANEL;
1178
+ const panelWidth = widthEntry.css;
831
1179
  const panelOffsetTop = panelState?.coverage === "full" ? "0px" : "3rem";
832
1180
  return {
833
1181
  panelState,
@@ -835,11 +1183,12 @@ function usePanelState() {
835
1183
  handleOpen,
836
1184
  handleClose,
837
1185
  panelWidth,
838
- panelOffsetTop
1186
+ panelOffsetTop,
1187
+ resize: resizeOptionsFromPanel(panelState, widthEntry)
839
1188
  };
840
1189
  }
841
1190
  function useDialogState() {
842
- const [dialogState, setDialogState] = useState(null);
1191
+ const [dialogState, setDialogState] = useState2(null);
843
1192
  const handleConfirm = useCallback2(() => {
844
1193
  dialogState?.resolve(true);
845
1194
  setDialogState(null);
@@ -851,7 +1200,7 @@ function useDialogState() {
851
1200
  return { dialogState, setDialogState, handleConfirm, handleCancel };
852
1201
  }
853
1202
  function useFeatureLookup(modules) {
854
- return useMemo3(() => {
1203
+ return useMemo5(() => {
855
1204
  const map = /* @__PURE__ */ new Map();
856
1205
  for (const m of modules) {
857
1206
  for (const group of m.navigation) {
@@ -863,15 +1212,15 @@ function useFeatureLookup(modules) {
863
1212
  return (featureId) => map.get(featureId) ?? null;
864
1213
  }, [modules]);
865
1214
  }
866
- function AppShell({ registry }) {
1215
+ function AppShell({ registry, sidebar }) {
867
1216
  const navigate = useNavigate2();
868
1217
  const config = useShellConfig();
869
1218
  const { modules } = registry;
870
1219
  const { activeService, setSelectedServiceId } = useActiveService(modules);
871
- const [subNavCollapsed, setSubNavCollapsed] = useState(false);
872
- const [commandMenuOpen, setCommandMenuOpen] = useState(false);
1220
+ const [subNavCollapsed, setSubNavCollapsed] = useState2(false);
1221
+ const [commandMenuOpen, setCommandMenuOpen] = useState2(false);
873
1222
  const openCommandMenu = useCallback2(() => setCommandMenuOpen(true), []);
874
- const [fullscreenState, setFullscreenState] = useState(null);
1223
+ const [fullscreenState, setFullscreenState] = useState2(null);
875
1224
  const handleFullscreenEnter = useCallback2(
876
1225
  (opts) => setFullscreenState(opts),
877
1226
  []
@@ -930,7 +1279,7 @@ function AppShell({ registry }) {
930
1279
  setSubNavCollapsed
931
1280
  ]
932
1281
  );
933
- const sidePaneContextValue = useMemo3(
1282
+ const sidePaneContextValue = useMemo5(
934
1283
  () => ({
935
1284
  pane: sidePane2.sidePaneState,
936
1285
  open: sidePane2.handleOpen,
@@ -946,50 +1295,59 @@ function AppShell({ registry }) {
946
1295
  }),
947
1296
  [sidePane2]
948
1297
  );
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(
952
- CommandMenu,
953
- {
954
- open: commandMenuOpen,
955
- onOpenChange: setCommandMenuOpen
956
- }
957
- ),
958
- /* @__PURE__ */ jsx7(
959
- ShellBody,
960
- {
961
- activeService,
962
- sidePaneFullWidth: sidePane2.sidePaneState?.fullWidth,
963
- sidePaneModuleId: sidePane2.sidePaneState?.moduleId ?? null,
964
- subNavCollapsed,
965
- onToggleSubNav: () => setSubNavCollapsed((c) => !c),
966
- onServiceSelect: handleServiceSelect
967
- }
968
- ),
969
- dialog2.dialogState && /* @__PURE__ */ jsx7(
970
- ConfirmDialog,
971
- {
972
- open: true,
973
- title: dialog2.dialogState.opts.title,
974
- description: dialog2.dialogState.opts.description,
975
- confirmLabel: dialog2.dialogState.opts.confirmLabel,
976
- cancelLabel: dialog2.dialogState.opts.cancelLabel,
977
- variant: dialog2.dialogState.opts.variant,
978
- onConfirm: dialog2.handleConfirm,
979
- onCancel: dialog2.handleCancel
980
- }
981
- ),
982
- fullscreenState && /* @__PURE__ */ jsx7("div", { className: "fixed inset-0 z-50 bg-background overflow-auto", children: fullscreenState.content }),
983
- /* @__PURE__ */ jsx7(
984
- SlideOverPanel,
985
- {
986
- panelState: panel2.panelState,
987
- panelWidth: panel2.panelWidth,
988
- panelOffsetTop: panel2.panelOffsetTop,
989
- panelOnCloseRef: panel2.panelOnCloseRef,
990
- onClose: panel2.handleClose
991
- }
992
- )
1298
+ const navigationValue = {
1299
+ activeService,
1300
+ sidePaneModuleId: sidePane2.sidePaneState?.moduleId ?? null,
1301
+ subNavCollapsed,
1302
+ onToggleSubNav: () => setSubNavCollapsed((c) => !c),
1303
+ onServiceSelect: handleServiceSelect
1304
+ };
1305
+ return /* @__PURE__ */ jsx13(ShellModulesContext.Provider, { value: registry, children: /* @__PURE__ */ jsx13(SidePaneContext.Provider, { value: sidePaneContextValue, children: /* @__PURE__ */ jsx13(ShellNavigationContext.Provider, { value: navigationValue, children: /* @__PURE__ */ jsxs9(TooltipProvider, { children: [
1306
+ /* @__PURE__ */ jsx13(ModuleEffects, { modules }),
1307
+ /* @__PURE__ */ jsxs9("div", { className: "flex h-screen w-screen flex-col overflow-hidden", children: [
1308
+ /* @__PURE__ */ jsx13(TopBar, { children: config.topBar }),
1309
+ /* @__PURE__ */ jsx13(
1310
+ CommandMenu,
1311
+ {
1312
+ open: commandMenuOpen,
1313
+ onOpenChange: setCommandMenuOpen
1314
+ }
1315
+ ),
1316
+ /* @__PURE__ */ jsx13(
1317
+ ShellBody,
1318
+ {
1319
+ activeService,
1320
+ sidePaneFullWidth: sidePane2.sidePaneState?.fullWidth,
1321
+ sidebar
1322
+ }
1323
+ ),
1324
+ config.footer && /* @__PURE__ */ jsx13(ShellFooter, { children: config.footer }),
1325
+ dialog2.dialogState && /* @__PURE__ */ jsx13(
1326
+ ConfirmDialog,
1327
+ {
1328
+ open: true,
1329
+ title: dialog2.dialogState.opts.title,
1330
+ description: dialog2.dialogState.opts.description,
1331
+ confirmLabel: dialog2.dialogState.opts.confirmLabel,
1332
+ cancelLabel: dialog2.dialogState.opts.cancelLabel,
1333
+ variant: dialog2.dialogState.opts.variant,
1334
+ onConfirm: dialog2.handleConfirm,
1335
+ onCancel: dialog2.handleCancel
1336
+ }
1337
+ ),
1338
+ fullscreenState && /* @__PURE__ */ jsx13("div", { className: "fixed inset-0 z-50 bg-background overflow-auto", children: fullscreenState.content }),
1339
+ /* @__PURE__ */ jsx13(
1340
+ SlideOverPanel,
1341
+ {
1342
+ panelState: panel2.panelState,
1343
+ panelWidth: panel2.panelWidth,
1344
+ panelOffsetTop: panel2.panelOffsetTop,
1345
+ panelOnCloseRef: panel2.panelOnCloseRef,
1346
+ onClose: panel2.handleClose,
1347
+ resize: panel2.resize
1348
+ }
1349
+ )
1350
+ ] })
993
1351
  ] }) }) }) });
994
1352
  }
995
1353
  function SlideOverPanel({
@@ -997,78 +1355,92 @@ function SlideOverPanel({
997
1355
  panelWidth,
998
1356
  panelOffsetTop,
999
1357
  panelOnCloseRef,
1000
- onClose
1358
+ onClose,
1359
+ resize
1001
1360
  }) {
1002
- return /* @__PURE__ */ jsx7(
1361
+ const sheetProps = {
1362
+ side: "right",
1363
+ offsetTop: panelOffsetTop,
1364
+ onCloseAutoFocus: (e) => {
1365
+ if (panelOnCloseRef.current) {
1366
+ e.preventDefault();
1367
+ panelOnCloseRef.current();
1368
+ }
1369
+ },
1370
+ ...!panelState?.description && { "aria-describedby": void 0 }
1371
+ };
1372
+ return /* @__PURE__ */ jsx13(
1003
1373
  Sheet,
1004
1374
  {
1005
1375
  open: panelState !== null,
1006
1376
  onOpenChange: (open) => {
1007
1377
  if (!open) onClose();
1008
1378
  },
1009
- children: /* @__PURE__ */ jsx7(
1010
- SheetContent,
1011
- {
1012
- side: "right",
1013
- className: `${panelWidth} max-w-[90vw]`,
1014
- offsetTop: panelOffsetTop,
1015
- onCloseAutoFocus: (e) => {
1016
- if (panelOnCloseRef.current) {
1017
- e.preventDefault();
1018
- panelOnCloseRef.current();
1019
- }
1020
- },
1021
- ...!panelState?.description && {
1022
- "aria-describedby": void 0
1023
- },
1024
- children: panelState && /* @__PURE__ */ jsx7(PanelContent, { state: panelState })
1025
- }
1026
- )
1379
+ children: resize.enabled ? /* @__PURE__ */ jsx13(ResizableSheetContent, { ...sheetProps, resize, children: panelState && /* @__PURE__ */ jsx13(PanelContent, { state: panelState }) }) : /* @__PURE__ */ jsx13(SheetContent, { ...sheetProps, className: `${panelWidth} max-w-[90vw]`, children: panelState && /* @__PURE__ */ jsx13(PanelContent, { state: panelState }) })
1380
+ }
1381
+ );
1382
+ }
1383
+ function ResizableSheetContent({
1384
+ resize,
1385
+ children,
1386
+ ...sheetProps
1387
+ }) {
1388
+ const {
1389
+ panelWidth: dynWidth,
1390
+ handlePointerDown,
1391
+ handleDoubleClick,
1392
+ separatorProps
1393
+ } = useResizablePanel2({
1394
+ defaultWidth: resize.defaultWidth,
1395
+ minWidth: resize.minWidth,
1396
+ maxWidth: resize.maxWidth,
1397
+ direction: "left-edge"
1398
+ });
1399
+ return /* @__PURE__ */ jsxs9(
1400
+ SheetContent,
1401
+ {
1402
+ ...sheetProps,
1403
+ className: "max-w-[90vw]",
1404
+ style: { width: dynWidth },
1405
+ children: [
1406
+ /* @__PURE__ */ jsx13(
1407
+ DragHandle,
1408
+ {
1409
+ edge: "left",
1410
+ separatorProps,
1411
+ onPointerDown: handlePointerDown,
1412
+ onDoubleClick: handleDoubleClick
1413
+ }
1414
+ ),
1415
+ children
1416
+ ]
1027
1417
  }
1028
1418
  );
1029
1419
  }
1030
1420
  function ShellBody({
1031
1421
  activeService,
1032
1422
  sidePaneFullWidth,
1033
- sidePaneModuleId,
1034
- subNavCollapsed,
1035
- onToggleSubNav,
1036
- onServiceSelect
1423
+ sidebar
1037
1424
  }) {
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 }) })
1425
+ return /* @__PURE__ */ jsxs9("div", { className: "flex flex-1 overflow-hidden", children: [
1426
+ sidebar ?? /* @__PURE__ */ jsx13(ShellRail, {}),
1427
+ /* @__PURE__ */ jsx13(SidePane, {}),
1428
+ sidePaneFullWidth !== true && /* @__PURE__ */ jsx13("div", { className: "flex-1 min-w-0 h-full overflow-hidden", children: /* @__PURE__ */ jsx13(ContentArea, { layout: activeService?.layout }) })
1057
1429
  ] });
1058
1430
  }
1059
1431
  function ContentArea({ layout }) {
1060
1432
  if (layout === "full") {
1061
- return /* @__PURE__ */ jsx7("div", { className: "h-full w-full overflow-hidden", children: /* @__PURE__ */ jsx7(Outlet, {}) });
1433
+ return /* @__PURE__ */ jsx13("div", { className: "h-full w-full overflow-hidden", children: /* @__PURE__ */ jsx13(Outlet, {}) });
1062
1434
  }
1063
- return /* @__PURE__ */ jsx7(ScrollArea2, { className: "h-full", children: /* @__PURE__ */ jsx7("main", { className: "p-6", children: /* @__PURE__ */ jsx7(Outlet, {}) }) });
1435
+ return /* @__PURE__ */ jsx13(ScrollArea2, { className: "h-full", children: /* @__PURE__ */ jsx13("main", { className: "p-6", children: /* @__PURE__ */ jsx13(Outlet, {}) }) });
1064
1436
  }
1065
1437
  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 })
1438
+ return /* @__PURE__ */ jsxs9(Fragment4, { children: [
1439
+ state.title ? /* @__PURE__ */ jsxs9(SheetHeader, { children: [
1440
+ /* @__PURE__ */ jsx13(SheetTitle, { children: state.title }),
1441
+ state.description && /* @__PURE__ */ jsx13(SheetDescription, { children: state.description })
1442
+ ] }) : /* @__PURE__ */ jsx13(SheetTitle, { className: "sr-only", children: "Panel" }),
1443
+ /* @__PURE__ */ jsx13(SheetBody, { children: state.content })
1072
1444
  ] });
1073
1445
  }
1074
1446
 
@@ -1076,32 +1448,36 @@ function PanelContent({ state }) {
1076
1448
  import { Toaster } from "sonner";
1077
1449
 
1078
1450
  // src/ShellConfigProvider.tsx
1079
- import { jsx as jsx8 } from "react/jsx-runtime";
1451
+ import { jsx as jsx14 } from "react/jsx-runtime";
1080
1452
  function ShellConfigProvider({
1081
1453
  config,
1082
1454
  children
1083
1455
  }) {
1084
- return /* @__PURE__ */ jsx8(ShellConfigContext.Provider, { value: config, children });
1456
+ return /* @__PURE__ */ jsx14(ShellConfigContext.Provider, { value: config, children });
1085
1457
  }
1086
1458
 
1087
1459
  // 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 })
1460
+ import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
1461
+ function RootLayout({
1462
+ config,
1463
+ registry,
1464
+ sidebar
1465
+ }) {
1466
+ return /* @__PURE__ */ jsxs10(ShellConfigProvider, { config, children: [
1467
+ /* @__PURE__ */ jsx15(Toaster, { position: "top-right", richColors: true, closeButton: true }),
1468
+ /* @__PURE__ */ jsx15(AppShell, { registry, sidebar })
1093
1469
  ] });
1094
1470
  }
1095
1471
 
1096
1472
  // src/PlaceholderPage.tsx
1097
1473
  import { useLocation as useLocation3 } from "react-router-dom";
1098
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1474
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
1099
1475
  function PlaceholderPage() {
1100
1476
  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" }) })
1477
+ return /* @__PURE__ */ jsxs11("div", { children: [
1478
+ /* @__PURE__ */ jsx16("h1", { className: "mb-1", children: pathToTitle(pathname) }),
1479
+ /* @__PURE__ */ jsx16("p", { className: "text-sm text-muted-foreground mb-6", children: pathname }),
1480
+ /* @__PURE__ */ jsx16("div", { className: "rounded-lg border-2 border-dashed border-border p-12 flex items-center justify-center", children: /* @__PURE__ */ jsx16("span", { className: "text-sm text-muted-foreground", children: "Content area \u2014 not yet implemented" }) })
1105
1481
  ] });
1106
1482
  }
1107
1483
  function pathToTitle(path) {
@@ -1110,12 +1486,131 @@ function pathToTitle(path) {
1110
1486
  return last.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
1111
1487
  }
1112
1488
 
1489
+ // src/Sidebar.tsx
1490
+ import { cn as cn6 } from "@petrarca/sonnet-core";
1491
+ import { ScrollArea as ScrollArea3 } from "@petrarca/sonnet-ui";
1492
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
1493
+ function Sidebar({
1494
+ children,
1495
+ header,
1496
+ width = 260,
1497
+ className
1498
+ }) {
1499
+ return /* @__PURE__ */ jsxs12(
1500
+ "aside",
1501
+ {
1502
+ style: { width },
1503
+ className: cn6(
1504
+ "shrink-0 border-r bg-muted/30 flex flex-col h-full",
1505
+ className
1506
+ ),
1507
+ children: [
1508
+ header,
1509
+ /* @__PURE__ */ jsx17(ScrollArea3, { className: "flex-1", children: /* @__PURE__ */ jsx17("nav", { className: "px-1.5 py-2 space-y-3", children }) })
1510
+ ]
1511
+ }
1512
+ );
1513
+ }
1514
+
1515
+ // src/ShellSidebar.tsx
1516
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1517
+ function ShellSidebar({
1518
+ bottomGroupLabel = "Tools",
1519
+ ...sidebarProps
1520
+ }) {
1521
+ const { mainModules, bottomModules } = useShellModules();
1522
+ const topNavItems = mainModules.flatMap(
1523
+ (mod) => (mod.topNav ?? []).map((link) => ({ link, icon: mod.icon }))
1524
+ );
1525
+ return /* @__PURE__ */ jsxs13(Sidebar, { ...sidebarProps, children: [
1526
+ topNavItems.length > 0 && /* @__PURE__ */ jsx18(SidebarGroup, { separator: true, children: topNavItems.map(({ link, icon }) => /* @__PURE__ */ jsx18(
1527
+ SidebarItem,
1528
+ {
1529
+ icon,
1530
+ label: link.label,
1531
+ path: link.path,
1532
+ badge: link.badge
1533
+ },
1534
+ link.path
1535
+ )) }),
1536
+ mainModules.map((mod) => {
1537
+ if (mod.navigation.length === 0) {
1538
+ return /* @__PURE__ */ jsx18(
1539
+ SidebarItem,
1540
+ {
1541
+ icon: mod.icon,
1542
+ label: mod.label,
1543
+ path: mod.basePath
1544
+ },
1545
+ mod.id
1546
+ );
1547
+ }
1548
+ return /* @__PURE__ */ jsx18(SidebarGroup, { heading: mod.label, collapsible: true, children: mod.navigation.flatMap((group) => [
1549
+ // Render NavGroup heading if it has one
1550
+ ...group.heading ? [
1551
+ /* @__PURE__ */ jsx18(
1552
+ SidebarGroup,
1553
+ {
1554
+ heading: group.heading,
1555
+ collapsible: group.collapsible,
1556
+ children: group.links.map((link) => /* @__PURE__ */ jsx18(
1557
+ SidebarItem,
1558
+ {
1559
+ icon: mod.icon,
1560
+ label: link.label,
1561
+ path: link.path,
1562
+ badge: link.badge
1563
+ },
1564
+ link.path
1565
+ ))
1566
+ },
1567
+ group.id
1568
+ )
1569
+ ] : group.links.map((link) => /* @__PURE__ */ jsx18(
1570
+ SidebarItem,
1571
+ {
1572
+ icon: mod.icon,
1573
+ label: link.label,
1574
+ path: link.path,
1575
+ badge: link.badge
1576
+ },
1577
+ link.path
1578
+ ))
1579
+ ]) }, mod.id);
1580
+ }),
1581
+ bottomModules.length > 0 && /* @__PURE__ */ jsx18(SidebarGroup, { heading: bottomGroupLabel, collapsible: true, children: bottomModules.map((mod) => /* @__PURE__ */ jsx18(
1582
+ SidebarItem,
1583
+ {
1584
+ icon: mod.icon,
1585
+ label: mod.label,
1586
+ path: mod.basePath
1587
+ },
1588
+ mod.id
1589
+ )) })
1590
+ ] });
1591
+ }
1592
+
1593
+ // src/ShellVersion.tsx
1594
+ import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1595
+ function ShellVersion({
1596
+ name,
1597
+ version
1598
+ }) {
1599
+ return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
1600
+ /* @__PURE__ */ jsx19("span", { children: name }),
1601
+ version && /* @__PURE__ */ jsxs14("span", { className: "text-muted-foreground/50", children: [
1602
+ "v",
1603
+ version
1604
+ ] })
1605
+ ] });
1606
+ }
1607
+
1113
1608
  // src/SearchTrigger.tsx
1114
1609
  import { Search } from "lucide-react";
1115
1610
  import { Button as Button3 } from "@petrarca/sonnet-ui";
1116
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1611
+ import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
1117
1612
  function SearchTrigger() {
1118
- return /* @__PURE__ */ jsxs9(
1613
+ return /* @__PURE__ */ jsxs15(
1119
1614
  Button3,
1120
1615
  {
1121
1616
  variant: "outline",
@@ -1123,10 +1618,10 @@ function SearchTrigger() {
1123
1618
  className: "h-8 gap-2 text-sm text-muted-foreground w-56 justify-start",
1124
1619
  onClick: () => navigation.openCommandMenu(),
1125
1620
  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" }),
1621
+ /* @__PURE__ */ jsx20(Search, { className: "h-4 w-4" }),
1622
+ /* @__PURE__ */ jsx20("span", { children: "Search..." }),
1623
+ /* @__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: [
1624
+ /* @__PURE__ */ jsx20("span", { className: "text-xs", children: "\u2318" }),
1130
1625
  "K"
1131
1626
  ] })
1132
1627
  ]
@@ -1146,23 +1641,23 @@ import {
1146
1641
  DropdownMenuSeparator,
1147
1642
  DropdownMenuTrigger
1148
1643
  } from "@petrarca/sonnet-ui";
1149
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1644
+ import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
1150
1645
  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 })
1646
+ return /* @__PURE__ */ jsxs16(DropdownMenu, { children: [
1647
+ /* @__PURE__ */ jsx21(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx21(Button4, { variant: "ghost", size: "compact", className: "rounded-full", children: /* @__PURE__ */ jsx21(Avatar, { className: "h-7 w-7", children: /* @__PURE__ */ jsx21(AvatarFallback, { className: "text-xs", children: user.initials }) }) }) }),
1648
+ /* @__PURE__ */ jsxs16(DropdownMenuContent, { align: "end", className: "w-48", children: [
1649
+ /* @__PURE__ */ jsx21(DropdownMenuLabel, { className: "font-normal", children: /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-1", children: [
1650
+ /* @__PURE__ */ jsx21("p", { className: "text-sm font-medium", children: user.name }),
1651
+ /* @__PURE__ */ jsx21("p", { className: "text-xs text-muted-foreground", children: user.email })
1157
1652
  ] }) }),
1158
- /* @__PURE__ */ jsx12(DropdownMenuSeparator, {}),
1159
- /* @__PURE__ */ jsxs10(DropdownMenuItem, { children: [
1160
- /* @__PURE__ */ jsx12(User, { className: "mr-2 h-4 w-4" }),
1653
+ /* @__PURE__ */ jsx21(DropdownMenuSeparator, {}),
1654
+ /* @__PURE__ */ jsxs16(DropdownMenuItem, { children: [
1655
+ /* @__PURE__ */ jsx21(User, { className: "mr-2 h-4 w-4" }),
1161
1656
  "Profile"
1162
1657
  ] }),
1163
- /* @__PURE__ */ jsx12(DropdownMenuSeparator, {}),
1164
- /* @__PURE__ */ jsxs10(DropdownMenuItem, { onSelect: onSignOut, children: [
1165
- /* @__PURE__ */ jsx12(LogOut, { className: "mr-2 h-4 w-4" }),
1658
+ /* @__PURE__ */ jsx21(DropdownMenuSeparator, {}),
1659
+ /* @__PURE__ */ jsxs16(DropdownMenuItem, { onSelect: onSignOut, children: [
1660
+ /* @__PURE__ */ jsx21(LogOut, { className: "mr-2 h-4 w-4" }),
1166
1661
  "Sign out"
1167
1662
  ] })
1168
1663
  ] })
@@ -1193,13 +1688,13 @@ function createModuleRegistry(modules) {
1193
1688
  }
1194
1689
 
1195
1690
  // src/OverviewCard.tsx
1196
- import { cn as cn4 } from "@petrarca/sonnet-core";
1197
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1691
+ import { cn as cn7 } from "@petrarca/sonnet-core";
1692
+ import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
1198
1693
  function FeatureLink({
1199
1694
  feature,
1200
1695
  children
1201
1696
  }) {
1202
- return /* @__PURE__ */ jsx13(
1697
+ return /* @__PURE__ */ jsx22(
1203
1698
  "span",
1204
1699
  {
1205
1700
  role: "link",
@@ -1227,22 +1722,22 @@ function OverviewCard({
1227
1722
  feature,
1228
1723
  className
1229
1724
  }) {
1230
- return /* @__PURE__ */ jsxs11(
1725
+ return /* @__PURE__ */ jsxs17(
1231
1726
  "button",
1232
1727
  {
1233
1728
  onClick: feature ? () => navigation.goToFeature(feature) : void 0,
1234
1729
  disabled: !feature,
1235
- className: cn4(
1730
+ className: cn7(
1236
1731
  "rounded-lg border bg-card p-5 text-left flex flex-col gap-3",
1237
1732
  "disabled:cursor-default",
1238
1733
  feature && "hover:bg-accent/50 transition-colors",
1239
1734
  className
1240
1735
  ),
1241
1736
  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 })
1737
+ /* @__PURE__ */ jsx22("div", { className: "h-9 w-9 rounded-md bg-blue-50 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx22(Icon, { className: "h-5 w-5 text-blue-600" }) }),
1738
+ /* @__PURE__ */ jsxs17("div", { children: [
1739
+ /* @__PURE__ */ jsx22("p", { className: "text-sm font-medium mb-1", children: title }),
1740
+ /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground leading-relaxed", children: description })
1246
1741
  ] })
1247
1742
  ]
1248
1743
  }
@@ -1252,35 +1747,50 @@ export {
1252
1747
  AppShell,
1253
1748
  CommandMenu,
1254
1749
  ConfirmDialog,
1750
+ DEFAULT_CAPABILITY_POLICY,
1255
1751
  FeatureLink,
1256
1752
  IconRail,
1257
1753
  OverviewCard,
1258
1754
  PlaceholderPage,
1755
+ RailIcon,
1756
+ RailSeparator,
1259
1757
  RootLayout,
1260
1758
  SearchTrigger,
1261
1759
  ShellConfigProvider,
1760
+ ShellFooter,
1761
+ ShellRail,
1762
+ ShellSidebar,
1763
+ ShellVersion,
1262
1764
  SidePane,
1263
1765
  SidePaneContext,
1766
+ Sidebar,
1767
+ SidebarGroup,
1768
+ SidebarItem,
1264
1769
  SubNavPanel,
1265
1770
  TopBar,
1266
1771
  UserMenu,
1267
1772
  createModuleRegistry,
1268
1773
  dialog,
1774
+ evaluateModule,
1269
1775
  events,
1270
1776
  fullscreen,
1271
1777
  initDialog,
1778
+ initFeatureNav,
1779
+ initFullscreen,
1272
1780
  initNavigation,
1273
1781
  initPanel,
1274
1782
  initSidePane,
1275
1783
  navigation,
1276
1784
  notification,
1277
1785
  panel,
1786
+ resolveCapabilityPolicy,
1278
1787
  sidePane,
1279
1788
  useExtensionPoint,
1280
1789
  useMainModules,
1281
1790
  useShellConfig,
1282
1791
  useShellEvent,
1283
1792
  useShellModules,
1793
+ useShellNavigation,
1284
1794
  useSidePaneState
1285
1795
  };
1286
1796
  //# sourceMappingURL=index.js.map