@petrarca/sonnet-shell 0.7.0 → 0.8.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
@@ -6,7 +6,7 @@ import {
6
6
  useRef as useRef2,
7
7
  useMemo as useMemo5
8
8
  } from "react";
9
- import { useResizablePanel as useResizablePanel2 } from "@petrarca/sonnet-core/hooks";
9
+ import { useResizablePanel as useResizablePanel2, useMediaQuery } from "@petrarca/sonnet-core/hooks";
10
10
  import { Outlet, useLocation as useLocation2, useNavigate as useNavigate2 } from "react-router-dom";
11
11
  import { TooltipProvider } from "@petrarca/sonnet-ui";
12
12
  import { ScrollArea as ScrollArea2 } from "@petrarca/sonnet-ui";
@@ -448,9 +448,10 @@ function GroupHeading({
448
448
  type: "button",
449
449
  onClick: canCollapse ? onToggle : void 0,
450
450
  className: cn2(
451
- "flex w-full items-center gap-1 px-2 py-1.5",
451
+ "flex w-full items-center gap-1 rounded px-2 py-1.5",
452
452
  "text-[11px] font-semibold uppercase tracking-widest text-muted-foreground/70",
453
- canCollapse ? "hover:text-foreground cursor-pointer" : "cursor-default"
453
+ canCollapse ? "hover:text-foreground cursor-pointer" : "cursor-default",
454
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
454
455
  ),
455
456
  children: [
456
457
  canCollapse && /* @__PURE__ */ jsx5(
@@ -511,8 +512,9 @@ function ItemTrailing({
511
512
  "button",
512
513
  {
513
514
  type: "button",
515
+ "aria-label": expanded ? "Collapse" : "Expand",
514
516
  onClick: handleClick,
515
- className: "p-0.5 text-muted-foreground hover:text-foreground",
517
+ className: "rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
516
518
  children: /* @__PURE__ */ jsx6(
517
519
  ChevronRight,
518
520
  {
@@ -562,6 +564,7 @@ function SidebarItem({
562
564
  style,
563
565
  className: cn3(
564
566
  "flex items-center gap-2 rounded px-2 py-1.5 text-[13px] font-medium transition-colors select-none",
567
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
565
568
  itemStateClass(disabled, isActive)
566
569
  ),
567
570
  children: [
@@ -610,7 +613,8 @@ function SubNavPanel({
610
613
  "button",
611
614
  {
612
615
  type: "button",
613
- className: "text-muted-foreground/50 hover:text-muted-foreground transition-colors p-1",
616
+ "aria-label": "Expand navigation",
617
+ className: "rounded text-muted-foreground/50 hover:text-muted-foreground transition-colors p-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
614
618
  onClick: onToggleCollapse,
615
619
  children: /* @__PURE__ */ jsx7(ChevronsRight, { className: "h-4 w-4" })
616
620
  }
@@ -626,7 +630,8 @@ function SubNavPanel({
626
630
  "button",
627
631
  {
628
632
  type: "button",
629
- className: "text-muted-foreground/40 hover:text-muted-foreground transition-colors p-0.5",
633
+ "aria-label": "Collapse navigation",
634
+ className: "rounded text-muted-foreground/40 hover:text-muted-foreground transition-colors p-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
630
635
  onClick: onToggleCollapse,
631
636
  children: /* @__PURE__ */ jsx7(ChevronsLeft, { className: "h-3.5 w-3.5" })
632
637
  }
@@ -1256,6 +1261,7 @@ function AppShell({ registry, sidebar }) {
1256
1261
  const { modules } = registry;
1257
1262
  const { activeService, setSelectedServiceId } = useActiveService(modules);
1258
1263
  const [subNavCollapsed, setSubNavCollapsed] = useState2(false);
1264
+ const isBelowTablet = useMediaQuery("(max-width: 1023px)");
1259
1265
  const [commandMenuOpen, setCommandMenuOpen] = useState2(false);
1260
1266
  const openCommandMenu = useCallback2(() => setCommandMenuOpen(true), []);
1261
1267
  const [fullscreenState, setFullscreenState] = useState2(null);
@@ -1336,7 +1342,7 @@ function AppShell({ registry, sidebar }) {
1336
1342
  const navigationValue = {
1337
1343
  activeService,
1338
1344
  sidePaneModuleId: sidePane2.sidePaneState?.moduleId ?? null,
1339
- subNavCollapsed,
1345
+ subNavCollapsed: subNavCollapsed || isBelowTablet,
1340
1346
  onToggleSubNav: () => setSubNavCollapsed((c) => !c),
1341
1347
  onServiceSelect: handleServiceSelect
1342
1348
  };
@@ -1679,7 +1685,7 @@ function ShellVersion({
1679
1685
  }) {
1680
1686
  return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
1681
1687
  /* @__PURE__ */ jsx19("span", { children: name }),
1682
- version && /* @__PURE__ */ jsxs14("span", { className: "text-muted-foreground/50", children: [
1688
+ version && /* @__PURE__ */ jsxs14("span", { className: "text-tertiary-foreground", children: [
1683
1689
  "v",
1684
1690
  version
1685
1691
  ] })
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/AppShell.tsx","../src/ConfirmDialog.tsx","../src/TopBar.tsx","../src/ShellFooter.tsx","../src/ShellRail.tsx","../src/IconRail.tsx","../src/SubNavPanel.tsx","../src/hooks.ts","../src/api.ts","../src/eventBus.ts","../src/shellModules.ts","../src/SidebarGroup.tsx","../src/SidebarItem.tsx","../src/shellNavigation.ts","../src/shellConfig.ts","../src/capabilities.ts","../src/SidePane.tsx","../src/sidePaneState.ts","../src/DragHandle.tsx","../src/CommandMenu.tsx","../src/ModuleEffects.tsx","../src/RootLayout.tsx","../src/ShellConfigProvider.tsx","../src/PlaceholderPage.tsx","../src/Sidebar.tsx","../src/ShellSidebar.tsx","../src/ShellVersion.tsx","../src/SearchTrigger.tsx","../src/UserMenu.tsx","../src/createModuleRegistry.ts","../src/OverviewCard.tsx"],"sourcesContent":["import React, {\n useState,\n useEffect,\n useCallback,\n useRef,\n useMemo,\n} from \"react\";\nimport { useResizablePanel } from \"@petrarca/sonnet-core/hooks\";\nimport { Outlet, useLocation, useNavigate } from \"react-router-dom\";\nimport { TooltipProvider } from \"@petrarca/sonnet-ui\";\nimport { ScrollArea } from \"@petrarca/sonnet-ui\";\nimport {\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n SheetBody,\n} from \"@petrarca/sonnet-ui\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\nimport TopBar from \"./TopBar\";\nimport ShellFooter from \"./ShellFooter\";\nimport { ShellRail } from \"./ShellRail\";\nimport SidePane from \"./SidePane\";\nimport { DragHandle } from \"./DragHandle\";\nimport CommandMenu from \"./CommandMenu\";\nimport { ModuleEffects } from \"./ModuleEffects\";\nimport {\n initNavigation,\n initFeatureNav,\n initPanel,\n initDialog,\n initFullscreen,\n initSidePane,\n type PanelOptions,\n type ConfirmOptions,\n type FullscreenOptions,\n} from \"./api\";\nimport type { ModuleRegistry } from \"./createModuleRegistry\";\nimport { ShellModulesContext } from \"./shellModules\";\nimport { useShellConfig } from \"./shellConfig\";\nimport { SidePaneContext, type SidePaneOptions } from \"./sidePaneState\";\nimport { ShellNavigationContext } from \"./shellNavigation\";\nimport type { ShellModule } from \"./types\";\n\n// Single source of truth for panel width tokens.\n// px: pixel value used as defaultWidth for resizable mode (viewport-relative\n// tokens like \"half\" / \"full\" are not resizable -- resizable falls back to 640).\nconst PANEL_WIDTHS: Record<string, { css: string; px: number }> = {\n narrow: { css: \"w-[480px]\", px: 480 },\n default: { css: \"w-[640px]\", px: 640 },\n wide: { css: \"w-[800px]\", px: 800 },\n half: { css: \"w-[50vw]\", px: 640 },\n full: { css: \"w-screen\", px: 640 },\n // legacy aliases\n lg: { css: \"w-[640px]\", px: 640 },\n \"1/3\": { css: \"w-[33vw]\", px: 640 },\n \"1/2\": { css: \"w-[50vw]\", px: 640 },\n};\n\nconst DEFAULT_PANEL = PANEL_WIDTHS.default;\n\n// -- Helpers extracted to reduce AppShell complexity --\n\nfunction isSidePaneModule(service: {\n sidePane?: unknown;\n navigation: unknown[];\n}): boolean {\n return !!service.sidePane && service.navigation.length === 0;\n}\n\nfunction selectSidePaneModule(\n service: {\n id: string;\n basePath: string;\n routes: unknown[];\n sidePane?: {\n defaultWidth?: number;\n minWidth?: number;\n maxWidth?: number;\n contentFactory?: () => React.ReactNode;\n };\n },\n current: SidePaneOptions | null,\n setSidePaneState: (s: SidePaneOptions | null) => void,\n navigate: (path: string) => void,\n): void {\n if (current?.moduleId === service.id) {\n setSidePaneState(null);\n } else {\n setSidePaneState({\n moduleId: service.id,\n content: service.sidePane?.contentFactory?.() ?? null,\n defaultWidth: service.sidePane?.defaultWidth,\n minWidth: service.sidePane?.minWidth,\n maxWidth: service.sidePane?.maxWidth,\n });\n // Only navigate if the module has routes — pane-only modules\n // (routes: []) have no registered path, navigating would 404.\n if (service.routes.length > 0) {\n navigate(service.basePath);\n }\n }\n}\n\nfunction selectNavModule(\n service: {\n basePath: string;\n navigation: Array<{ links: Array<{ path: string }> }>;\n },\n navigate: (path: string) => void,\n setSubNavCollapsed: (v: boolean) => void,\n closeSidePane: () => void,\n): void {\n closeSidePane();\n setSubNavCollapsed(false);\n const target = service.navigation[0]?.links[0]?.path ?? service.basePath;\n navigate(target);\n}\n\n// Wire all imperative shell APIs once on mount.\nfunction useShellApiInit(deps: {\n navigate: ReturnType<typeof useNavigate>;\n openCommandMenu: () => void;\n handlePanelOpen: (opts: PanelOptions) => void;\n handlePanelClose: () => void;\n handleFullscreenEnter: (opts: FullscreenOptions) => void;\n handleFullscreenExit: () => void;\n handleSidePaneOpen: (opts: SidePaneOptions) => void;\n handleSidePaneClose: () => void;\n handleSidePaneIsOpen: () => boolean;\n handleSidePaneActiveModuleId: () => string | null;\n handleSidePaneSetFullWidth: (fullWidth: boolean) => void;\n setDialogState: React.Dispatch<React.SetStateAction<DialogState | null>>;\n featureLookup: (featureId: string) => string | null;\n}) {\n // The imperative API is registered once, but several of these callbacks\n // change identity as state changes: handleSidePaneIsOpen and\n // handleSidePaneActiveModuleId track sidePaneState, and featureLookup tracks\n // the module registry. Registering them directly would freeze the mount-time\n // closures -- isOpen() would answer for an empty pane forever -- so the API\n // calls through this ref, which is refreshed after every render.\n const latest = useRef(deps);\n useEffect(() => {\n latest.current = deps;\n });\n\n useEffect(() => {\n initNavigation(\n (path) => latest.current.navigate(path),\n () => latest.current.openCommandMenu(),\n );\n initFeatureNav((featureId) => latest.current.featureLookup(featureId));\n initPanel(\n (opts) => latest.current.handlePanelOpen(opts),\n () => latest.current.handlePanelClose(),\n );\n initFullscreen(\n (opts) => latest.current.handleFullscreenEnter(opts),\n () => latest.current.handleFullscreenExit(),\n );\n initSidePane(\n (opts) => latest.current.handleSidePaneOpen(opts),\n () => latest.current.handleSidePaneClose(),\n () => latest.current.handleSidePaneIsOpen(),\n () => latest.current.handleSidePaneActiveModuleId(),\n (fullWidth) => latest.current.handleSidePaneSetFullWidth(fullWidth),\n );\n initDialog(\n (opts: ConfirmOptions) =>\n new Promise<boolean>((resolve) =>\n latest.current.setDialogState({ opts, resolve }),\n ),\n );\n }, []);\n}\n\ninterface AppShellProps {\n registry: ModuleRegistry;\n /**\n * Optional custom sidebar replacing the default IconRail + SubNavPanel.\n * When provided, the shell renders this element in place of the two-column\n * navigation chrome. Use the Sidebar / SidebarGroup / SidebarItem components\n * from this package to build a single-column navigation layout.\n */\n sidebar?: React.ReactNode;\n}\n\ninterface DialogState {\n opts: ConfirmOptions;\n resolve: (confirmed: boolean) => void;\n}\n\n/**\n * AppShell -- the main layout skeleton.\n *\n * Owns the panel (slide-over) -- modules request it via panel.open(),\n * the shell renders the provided content inside a Sheet.\n *\n * Receives its module registry as a prop so it has no dependency on\n * app-specific module lists.\n */\n// Hook: track which service is active based on URL.\nfunction useActiveService(modules: ModuleRegistry[\"modules\"]) {\n const location = useLocation();\n // Build [prefix, moduleId] pairs from each module's basePath plus any\n // matchPaths. Sorted by descending prefix length so a more specific prefix\n // (e.g. \"/org-units\") wins over a shorter one (e.g. \"/org\") regardless of\n // module registration order.\n const prefixToService = useMemo<Array<[string, string]>>(() => {\n const pairs: Array<[string, string]> = [];\n for (const m of modules) {\n if (m.hidden) continue;\n const prefixes = [m.basePath, ...(m.matchPaths ?? [])];\n for (const prefix of prefixes) {\n if (prefix !== \"/\") pairs.push([prefix, m.id]);\n }\n }\n pairs.sort((a, b) => b[0].length - a[0].length);\n return pairs;\n }, [modules]);\n const resolveServiceFromPath = useCallback(\n (pathname: string): string | null => {\n for (const [prefix, id] of prefixToService) {\n if (pathname.startsWith(prefix)) return id;\n }\n return null;\n },\n [prefixToService],\n );\n const [selectedServiceId, setSelectedServiceId] = useState<string | null>(\n () => resolveServiceFromPath(location.pathname),\n );\n useEffect(\n () => setSelectedServiceId(resolveServiceFromPath(location.pathname)),\n [location.pathname, resolveServiceFromPath],\n );\n const activeService = selectedServiceId\n ? (modules.find((s) => s.id === selectedServiceId) ?? null)\n : null;\n return { activeService, selectedServiceId, setSelectedServiceId };\n}\n\n// Hook: side pane state and handlers.\nfunction useSidePaneState() {\n const [sidePaneState, setSidePaneState] = useState<SidePaneOptions | null>(\n null,\n );\n const handleOpen = useCallback(\n (opts: SidePaneOptions) => setSidePaneState(opts),\n [],\n );\n const handleClose = useCallback(() => setSidePaneState(null), []);\n const handleIsOpen = useCallback(\n () => sidePaneState !== null,\n [sidePaneState],\n );\n const handleActiveModuleId = useCallback(\n () => sidePaneState?.moduleId ?? null,\n [sidePaneState],\n );\n const handleSetFullWidth = useCallback((fullWidth: boolean) => {\n setSidePaneState((prev) => (prev ? { ...prev, fullWidth } : prev));\n }, []);\n // Functional update so the callback stays stable and never closes over a\n // stale sidePaneState.\n const handleToggle = useCallback((opts: SidePaneOptions) => {\n setSidePaneState((prev) =>\n prev?.moduleId === opts.moduleId ? null : opts,\n );\n }, []);\n return {\n sidePaneState,\n setSidePaneState,\n handleOpen,\n handleClose,\n handleIsOpen,\n handleActiveModuleId,\n handleSetFullWidth,\n handleToggle,\n };\n}\n\n/** Resolved resize options passed as a single object to SlideOverPanel. */\ninterface PanelResizeOptions {\n enabled: boolean;\n defaultWidth: number;\n minWidth: number;\n maxWidth: number;\n}\n\nconst DEFAULT_RESIZE_MIN = 240;\nconst DEFAULT_RESIZE_MAX = 1200;\n\n// Derive resize options from panel state. Extracted to keep usePanelState lean.\nfunction resizeOptionsFromPanel(\n state: PanelOptions | null,\n widthEntry: { px: number },\n): PanelResizeOptions {\n if (!state?.resizable) {\n return {\n enabled: false,\n defaultWidth: widthEntry.px,\n minWidth: DEFAULT_RESIZE_MIN,\n maxWidth: DEFAULT_RESIZE_MAX,\n };\n }\n const viewportMax =\n typeof window !== \"undefined\"\n ? Math.round(window.innerWidth * 0.9)\n : DEFAULT_RESIZE_MAX;\n return {\n enabled: true,\n defaultWidth: widthEntry.px,\n minWidth: state.minWidth ?? DEFAULT_RESIZE_MIN,\n maxWidth: state.maxWidth ?? viewportMax,\n };\n}\n\n// Hook: slide-over panel state and handlers.\nfunction usePanelState() {\n const [panelState, setPanelState] = useState<PanelOptions | null>(null);\n const panelOnCloseRef = useRef<(() => void) | undefined>(undefined);\n const handleOpen = useCallback(\n (opts: PanelOptions) => setPanelState(opts),\n [],\n );\n const handleClose = useCallback(() => {\n panelOnCloseRef.current = panelState?.onClose;\n setPanelState(null);\n }, [panelState]);\n\n const widthEntry =\n PANEL_WIDTHS[panelState?.width ?? \"default\"] ?? DEFAULT_PANEL;\n const panelWidth = widthEntry.css;\n const panelOffsetTop = panelState?.coverage === \"full\" ? \"0px\" : \"3rem\";\n\n return {\n panelState,\n panelOnCloseRef,\n handleOpen,\n handleClose,\n panelWidth,\n panelOffsetTop,\n resize: resizeOptionsFromPanel(panelState, widthEntry),\n };\n}\n\n// Hook: confirm dialog state and handlers.\nfunction useDialogState() {\n const [dialogState, setDialogState] = useState<DialogState | null>(null);\n const handleConfirm = useCallback(() => {\n dialogState?.resolve(true);\n setDialogState(null);\n }, [dialogState]);\n const handleCancel = useCallback(() => {\n dialogState?.resolve(false);\n setDialogState(null);\n }, [dialogState]);\n return { dialogState, setDialogState, handleConfirm, handleCancel };\n}\n\n// Build a stable feature id -> path lookup from the module registry.\nfunction useFeatureLookup(modules: ModuleRegistry[\"modules\"]) {\n return useMemo(() => {\n const map = new Map<string, string>();\n for (const m of modules) {\n for (const group of m.navigation) {\n for (const link of group.links) {\n if (link.id) map.set(link.id, link.path);\n }\n }\n }\n return (featureId: string) => map.get(featureId) ?? null;\n }, [modules]);\n}\n\nexport default function AppShell({ registry, sidebar }: AppShellProps) {\n const navigate = useNavigate();\n const config = useShellConfig();\n const { modules } = registry;\n\n const { activeService, setSelectedServiceId } = useActiveService(modules);\n const [subNavCollapsed, setSubNavCollapsed] = useState(false);\n const [commandMenuOpen, setCommandMenuOpen] = useState(false);\n const openCommandMenu = useCallback(() => setCommandMenuOpen(true), []);\n const [fullscreenState, setFullscreenState] =\n useState<FullscreenOptions | null>(null);\n const handleFullscreenEnter = useCallback(\n (opts: FullscreenOptions) => setFullscreenState(opts),\n [],\n );\n const handleFullscreenExit = useCallback(() => setFullscreenState(null), []);\n\n const sidePane = useSidePaneState();\n const panel = usePanelState();\n const dialog = useDialogState();\n const featureLookup = useFeatureLookup(modules);\n\n useShellApiInit({\n navigate,\n openCommandMenu,\n handlePanelOpen: panel.handleOpen,\n handlePanelClose: panel.handleClose,\n handleFullscreenEnter,\n handleFullscreenExit,\n handleSidePaneOpen: sidePane.handleOpen,\n handleSidePaneClose: sidePane.handleClose,\n handleSidePaneIsOpen: sidePane.handleIsOpen,\n handleSidePaneActiveModuleId: sidePane.handleActiveModuleId,\n handleSidePaneSetFullWidth: sidePane.handleSetFullWidth,\n setDialogState: dialog.setDialogState,\n featureLookup,\n });\n\n const handleServiceSelect = useCallback(\n (serviceId: string) => {\n const service = modules.find((s) => s.id === serviceId);\n if (!service) return;\n if (isSidePaneModule(service)) {\n selectSidePaneModule(\n service,\n sidePane.sidePaneState,\n sidePane.setSidePaneState,\n navigate,\n );\n // Don't change activeService for pane-only modules — the content\n // area still shows the previous route and needs its layout mode.\n // Collapse SubNavPanel by default so it doesn't linger from a nav module.\n // Consumers can opt out via sidePane.collapseSubNav: false.\n if (service.sidePane?.collapseSubNav !== false) {\n setSubNavCollapsed(true);\n }\n } else {\n selectNavModule(\n service,\n navigate,\n setSubNavCollapsed,\n sidePane.handleClose,\n );\n setSelectedServiceId(serviceId);\n }\n },\n [\n modules,\n sidePane.sidePaneState,\n sidePane.setSidePaneState,\n sidePane.handleClose,\n navigate,\n setSelectedServiceId,\n setSubNavCollapsed,\n ],\n );\n\n // Depends on the individual members, not the hook's return object -- that is\n // a fresh literal every render and would defeat the memo entirely.\n const sidePaneContextValue = useMemo(\n () => ({\n pane: sidePane.sidePaneState,\n open: sidePane.handleOpen,\n close: sidePane.handleClose,\n toggle: sidePane.handleToggle,\n setFullWidth: sidePane.handleSetFullWidth,\n }),\n [\n sidePane.sidePaneState,\n sidePane.handleOpen,\n sidePane.handleClose,\n sidePane.handleToggle,\n sidePane.handleSetFullWidth,\n ],\n );\n\n const navigationValue = {\n activeService,\n sidePaneModuleId: sidePane.sidePaneState?.moduleId ?? null,\n subNavCollapsed,\n onToggleSubNav: () => setSubNavCollapsed((c) => !c),\n onServiceSelect: handleServiceSelect,\n };\n\n return (\n <ShellModulesContext.Provider value={registry}>\n <SidePaneContext.Provider value={sidePaneContextValue}>\n <ShellNavigationContext.Provider value={navigationValue}>\n <TooltipProvider>\n {/* Module-owned effects (event subscriptions, cache invalidation) */}\n <ModuleEffects modules={modules} />\n <div className=\"flex h-screen w-screen flex-col overflow-hidden\">\n {/* TopBar */}\n <TopBar>{config.topBar}</TopBar>\n\n {/* CommandMenu (Cmd+K) */}\n <CommandMenu\n open={commandMenuOpen}\n onOpenChange={setCommandMenuOpen}\n />\n\n {/* Body: sidebar (or ShellRail) + SidePane + Content Area */}\n <ShellBody\n activeService={activeService}\n sidePaneFullWidth={sidePane.sidePaneState?.fullWidth}\n sidebar={sidebar}\n />\n\n {/* Footer */}\n {config.footer && <ShellFooter>{config.footer}</ShellFooter>}\n\n {/* Confirm Dialog */}\n {dialog.dialogState && (\n <ConfirmDialog\n open={true}\n title={dialog.dialogState.opts.title}\n description={dialog.dialogState.opts.description}\n confirmLabel={dialog.dialogState.opts.confirmLabel}\n cancelLabel={dialog.dialogState.opts.cancelLabel}\n variant={dialog.dialogState.opts.variant}\n onConfirm={dialog.handleConfirm}\n onCancel={dialog.handleCancel}\n />\n )}\n\n {/* Fullscreen overlay -- covers all shell chrome */}\n {fullscreenState && (\n <div className=\"fixed inset-0 z-50 bg-background overflow-auto\">\n {fullscreenState.content}\n </div>\n )}\n\n {/* Slide-over Panel */}\n <SlideOverPanel\n panelState={panel.panelState}\n panelWidth={panel.panelWidth}\n panelOffsetTop={panel.panelOffsetTop}\n panelOnCloseRef={panel.panelOnCloseRef}\n onClose={panel.handleClose}\n resize={panel.resize}\n />\n </div>\n </TooltipProvider>\n </ShellNavigationContext.Provider>\n </SidePaneContext.Provider>\n </ShellModulesContext.Provider>\n );\n}\n\n// -- Sub-components extracted to reduce AppShell complexity --\n\ninterface SlideOverPanelProps {\n panelState: PanelOptions | null;\n panelWidth: string;\n panelOffsetTop: string;\n panelOnCloseRef: React.MutableRefObject<(() => void) | undefined>;\n onClose: () => void;\n resize: PanelResizeOptions;\n}\n\nfunction SlideOverPanel({\n panelState,\n panelWidth,\n panelOffsetTop,\n panelOnCloseRef,\n onClose,\n resize,\n}: SlideOverPanelProps) {\n const sheetProps = {\n side: \"right\" as const,\n offsetTop: panelOffsetTop,\n onCloseAutoFocus: (e: Event) => {\n if (panelOnCloseRef.current) {\n e.preventDefault();\n panelOnCloseRef.current();\n }\n },\n ...(!panelState?.description && { \"aria-describedby\": undefined }),\n };\n\n return (\n <Sheet\n open={panelState !== null}\n onOpenChange={(open) => {\n if (!open) onClose();\n }}\n >\n {resize.enabled ? (\n <ResizableSheetContent {...sheetProps} resize={resize}>\n {panelState && <PanelContent state={panelState} />}\n </ResizableSheetContent>\n ) : (\n <SheetContent {...sheetProps} className={`${panelWidth} max-w-[90vw]`}>\n {panelState && <PanelContent state={panelState} />}\n </SheetContent>\n )}\n </Sheet>\n );\n}\n\n/**\n * Wrapper that mounts useResizablePanel only when resize is enabled.\n * Hooks must be called unconditionally — splitting into a separate component\n * is the idiomatic way to avoid calling hooks behind a conditional.\n */\nfunction ResizableSheetContent({\n resize,\n children,\n ...sheetProps\n}: {\n resize: PanelResizeOptions;\n children: React.ReactNode;\n} & React.ComponentPropsWithoutRef<typeof SheetContent>) {\n const {\n panelWidth: dynWidth,\n handlePointerDown,\n handleDoubleClick,\n separatorProps,\n } = useResizablePanel({\n defaultWidth: resize.defaultWidth,\n minWidth: resize.minWidth,\n maxWidth: resize.maxWidth,\n direction: \"left-edge\",\n });\n\n return (\n <SheetContent\n {...sheetProps}\n className=\"max-w-[90vw]\"\n style={{ width: dynWidth }}\n >\n <DragHandle\n edge=\"left\"\n separatorProps={separatorProps}\n onPointerDown={handlePointerDown}\n onDoubleClick={handleDoubleClick}\n />\n {children}\n </SheetContent>\n );\n}\n\ninterface ShellBodyProps {\n activeService: ShellModule | null;\n sidePaneFullWidth?: boolean;\n sidebar?: React.ReactNode;\n}\n\nfunction ShellBody({\n activeService,\n sidePaneFullWidth,\n sidebar,\n}: ShellBodyProps) {\n return (\n <div className=\"flex flex-1 overflow-hidden\">\n {sidebar ?? <ShellRail />}\n\n <SidePane />\n\n {sidePaneFullWidth !== true && (\n <div className=\"flex-1 min-w-0 h-full overflow-hidden\">\n <ContentArea layout={activeService?.layout} />\n </div>\n )}\n </div>\n );\n}\n\nfunction ContentArea({ layout }: { layout?: \"default\" | \"full\" }) {\n if (layout === \"full\") {\n return (\n <div className=\"h-full w-full overflow-hidden\">\n <Outlet />\n </div>\n );\n }\n return (\n <ScrollArea className=\"h-full\">\n <main className=\"p-6\">\n <Outlet />\n </main>\n </ScrollArea>\n );\n}\n\nfunction PanelContent({ state }: { state: PanelOptions }) {\n return (\n <>\n {state.title ? (\n <SheetHeader>\n <SheetTitle>{state.title}</SheetTitle>\n {state.description && (\n <SheetDescription>{state.description}</SheetDescription>\n )}\n </SheetHeader>\n ) : (\n <SheetTitle className=\"sr-only\">Panel</SheetTitle>\n )}\n <SheetBody>{state.content}</SheetBody>\n </>\n );\n}\n","/**\n * Confirm Dialog\n *\n * Reusable confirmation dialog for actions that require user confirmation.\n * Used for discard changes, delete actions, etc.\n *\n * Wired into the shell via `initDialog` in `shell/api.ts` so modules can\n * trigger it imperatively with `dialog.confirm(opts)`.\n *\n * @module components/Common/ConfirmDialog\n */\n\nimport React from \"react\";\nimport { AlertTriangle } from \"lucide-react\";\nimport { Button } from \"@petrarca/sonnet-ui\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@petrarca/sonnet-ui\";\n\nexport interface ConfirmDialogProps {\n /** Whether the dialog is open */\n open: boolean;\n\n /** Dialog title */\n title: string;\n\n /** Description shown below the title */\n description?: string;\n\n /** Confirm button label (default: \"Confirm\") */\n confirmLabel?: string;\n\n /** Cancel button label (default: \"Cancel\") */\n cancelLabel?: string;\n\n /**\n * Controls confirm button appearance.\n * \"destructive\" for irreversible actions (delete, revoke, etc.)\n */\n variant?: \"default\" | \"destructive\";\n\n /** Callback when user confirms */\n onConfirm: () => void;\n\n /** Callback when user cancels or closes dialog */\n onCancel: () => void;\n}\n\n/**\n * ConfirmDialog Component\n *\n * A reusable confirmation dialog backed by shadcn Dialog primitives.\n */\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n variant = \"default\",\n onConfirm,\n onCancel,\n}: ConfirmDialogProps): React.ReactElement {\n return (\n <Dialog open={open} onOpenChange={(isOpen) => !isOpen && onCancel()}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n {description && <DialogDescription>{description}</DialogDescription>}\n </DialogHeader>\n\n {variant === \"destructive\" && (\n <div className=\"flex gap-2 items-start\">\n <AlertTriangle\n size={20}\n className=\"text-destructive flex-shrink-0 mt-0.5\"\n />\n <p className=\"flex-1 text-sm text-muted-foreground\">\n This action cannot be undone.\n </p>\n </div>\n )}\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={onCancel}>\n {cancelLabel}\n </Button>\n <Button variant={variant} onClick={onConfirm}>\n {confirmLabel}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import type { ReactNode } from \"react\";\n\n/**\n * TopBar -- persistent header across the entire application.\n *\n * A pure layout container. The shell provides the `<header>` element with\n * its sizing, background, and border. The consumer owns all content via\n * the `children` prop (composed through `ShellConfig.topBar`).\n */\ninterface TopBarProps {\n children?: ReactNode;\n}\n\nexport default function TopBar({ children }: TopBarProps) {\n return (\n <header className=\"h-12 shrink-0 border-b border-border flex items-center justify-between px-3 bg-background z-20\">\n {children}\n </header>\n );\n}\n","/**\n * ShellFooter — persistent footer bar across the entire application.\n *\n * A pure layout container, mirroring TopBar. The shell provides the\n * `<footer>` element with its sizing, background, and border. The\n * consumer owns all content via the `children` prop (composed through\n * `ShellConfig.footer`).\n */\nimport type { ReactNode } from \"react\";\n\nexport interface ShellFooterProps {\n children?: ReactNode;\n}\n\nexport default function ShellFooter({ children }: ShellFooterProps) {\n return (\n <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\">\n {children}\n </footer>\n );\n}\n","/**\n * ShellRail — icon-rail + sub-nav-panel layout, auto-wired from the\n * module registry and shell navigation context.\n *\n * Reads active service state from ShellNavigationContext (provided by\n * AppShell) and module list from ShellModulesContext. No props required.\n *\n * For custom layouts, compose IconRail, RailIcon, RailSeparator, and\n * SubNavPanel directly.\n */\nimport React, { useMemo } from \"react\";\nimport { IconRail, RailIcon, RailSeparator } from \"./IconRail\";\nimport { SubNavPanel } from \"./SubNavPanel\";\nimport { useShellModules } from \"./shellModules\";\nimport { useShellNavigation } from \"./shellNavigation\";\nimport { useShellConfig } from \"./shellConfig\";\nimport { evaluateModule, resolveCapabilityPolicy } from \"./capabilities\";\nimport type { ShellModule } from \"./types\";\n\nexport function ShellRail(): React.ReactElement {\n const { mainModules, bottomModules } = useShellModules();\n const { resolveCapability, capabilityPolicy } = useShellConfig();\n const {\n activeService,\n sidePaneModuleId,\n subNavCollapsed,\n onToggleSubNav,\n onServiceSelect,\n } = useShellNavigation();\n\n const isActive = (id: string) =>\n activeService?.id === id || sidePaneModuleId === id;\n\n const policy = useMemo(\n () => resolveCapabilityPolicy(capabilityPolicy),\n [capabilityPolicy],\n );\n\n const renderModule = (service: ShellModule): React.ReactElement | null => {\n const { effect, reason } = evaluateModule(\n service,\n \"rail\",\n resolveCapability,\n policy,\n );\n // \"redirect\" has no rail rendering — treat as hidden.\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n return (\n <RailIcon\n key={service.id}\n icon={service.icon}\n label={service.label}\n active={isActive(service.id)}\n onClick={() => onServiceSelect(service.id)}\n disabled={disabled}\n tooltip={\n disabled && reason ? `${service.label} -- ${reason}` : undefined\n }\n />\n );\n };\n\n return (\n <>\n <IconRail>\n <div className=\"flex-1 flex flex-col items-center gap-1 overflow-y-auto\">\n {mainModules.map(renderModule)}\n </div>\n\n <RailSeparator />\n <div className=\"flex flex-col items-center gap-1\">\n {bottomModules.map(renderModule)}\n </div>\n </IconRail>\n\n {activeService && activeService.navigation.length > 0 && (\n <SubNavPanel\n service={activeService}\n collapsed={subNavCollapsed}\n onToggleCollapse={onToggleSubNav}\n />\n )}\n </>\n );\n}\n","/**\n * IconRail primitives — composable building blocks for the narrow\n * vertical icon strip.\n *\n * - IconRail: the container (<nav> with flex column, border, bg)\n * - RailIcon: a single icon button with tooltip\n * - RailSeparator: divider between groups\n *\n * For the auto-wired layout, use ShellRail instead.\n */\nimport React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { Button, Separator } from \"@petrarca/sonnet-ui\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@petrarca/sonnet-ui\";\nimport type { LucideIcon } from \"lucide-react\";\n\n// IconRail container\n\nexport interface IconRailProps {\n children: React.ReactNode;\n className?: string;\n}\n\n/** Narrow vertical nav container for RailIcon items. */\nexport function IconRail({\n children,\n className,\n}: IconRailProps): React.ReactElement {\n return (\n <nav\n className={cn(\n \"w-14 shrink-0 border-r bg-muted flex flex-col items-center py-2 gap-1\",\n className,\n )}\n >\n {children}\n </nav>\n );\n}\n\n// RailIcon\n\nexport interface RailIconProps {\n icon: LucideIcon;\n label: string;\n active?: boolean;\n onClick?: () => void;\n /** Render greyed and non-interactive (e.g. a capability requirement is unmet). */\n disabled?: boolean;\n /** Optional tooltip override (e.g. why the item is disabled). Defaults to `label`. */\n tooltip?: string;\n}\n\n/** Single icon button with tooltip inside an IconRail. */\nexport function RailIcon({\n icon: Icon,\n label,\n active = false,\n onClick,\n disabled = false,\n tooltip,\n}: RailIconProps): React.ReactElement {\n return (\n <Tooltip delayDuration={0}>\n <TooltipTrigger asChild>\n {/* aria-disabled (not native `disabled`) keeps the trigger focusable so\n the tooltip explaining *why* remains discoverable. */}\n <Button\n variant=\"ghost\"\n size=\"compact\"\n aria-disabled={disabled || undefined}\n onClick={disabled ? undefined : onClick}\n className={cn(\n \"h-10 w-10 rounded-lg transition-colors\",\n disabled\n ? \"text-muted-foreground/40 cursor-default hover:bg-transparent\"\n : active\n ? \"bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:text-foreground hover:bg-muted-foreground/10\",\n )}\n >\n <Icon className=\"h-5 w-5\" />\n <span className=\"sr-only\">{label}</span>\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"right\" sideOffset={8}>\n {tooltip ?? label}\n </TooltipContent>\n </Tooltip>\n );\n}\n\n// RailSeparator\n\n/** Visual divider between icon groups inside an IconRail. */\nexport function RailSeparator(): React.ReactElement {\n return <Separator className=\"w-6 my-1\" />;\n}\n","/**\n * SubNavPanel — contextual secondary navigation for the selected service.\n *\n * Shows the service label at top, then grouped nav links using the shared\n * SidebarGroup / SidebarItem primitives. If other modules contribute links\n * via the \"<moduleId>.nav\" extension point, they are appended.\n *\n * Supports a collapsed state (thin strip with expand chevron).\n */\nimport { useMemo } from \"react\";\nimport { ChevronsLeft, ChevronsRight } from \"lucide-react\";\nimport { ScrollArea } from \"@petrarca/sonnet-ui\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@petrarca/sonnet-ui\";\nimport type { ShellModule, NavGroup } from \"./types\";\nimport { useExtensionPoint } from \"./hooks\";\nimport { SidebarGroup } from \"./SidebarGroup\";\nimport { SidebarItem } from \"./SidebarItem\";\n\nexport interface SubNavPanelProps {\n service: ShellModule;\n collapsed: boolean;\n onToggleCollapse: () => void;\n}\n\n// Merge contributed nav links from other modules.\nfunction useMergedNavigation(service: ShellModule): NavGroup[] {\n const contributions = useExtensionPoint(`${service.id}.nav`);\n return useMemo(() => {\n if (contributions.length === 0) return service.navigation;\n\n const contributed: NavGroup = {\n id: `${service.id}-contributed`,\n links: contributions.map((c) => ({\n id: c.extensionPoint + \".\" + c.label.toLowerCase().replace(/\\s+/g, \"-\"),\n label: c.label,\n path: c.path,\n })),\n };\n\n return [...service.navigation, contributed];\n }, [service, contributions]);\n}\n\nexport function SubNavPanel({\n service,\n collapsed,\n onToggleCollapse,\n}: SubNavPanelProps) {\n const mergedNavigation = useMergedNavigation(service);\n\n if (collapsed) {\n return (\n <aside className=\"shrink-0 border-r bg-background flex flex-col items-center pt-2.5\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n className=\"text-muted-foreground/50 hover:text-muted-foreground transition-colors p-1\"\n onClick={onToggleCollapse}\n >\n <ChevronsRight className=\"h-4 w-4\" />\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"right\">Expand navigation</TooltipContent>\n </Tooltip>\n </aside>\n );\n }\n\n return (\n <aside className=\"w-52 shrink-0 border-r bg-background flex flex-col\">\n {/* Service header with collapse toggle */}\n <div className=\"h-10 shrink-0 flex items-center justify-between px-4\">\n <span className=\"text-sm font-semibold text-foreground\">\n {service.label}\n </span>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n className=\"text-muted-foreground/40 hover:text-muted-foreground transition-colors p-0.5\"\n onClick={onToggleCollapse}\n >\n <ChevronsLeft className=\"h-3.5 w-3.5\" />\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"right\">Collapse navigation</TooltipContent>\n </Tooltip>\n </div>\n\n {/* Nav groups — reuses SidebarGroup / SidebarItem primitives */}\n <ScrollArea className=\"flex-1\">\n <nav className=\"px-1.5 pb-4 space-y-2\">\n {mergedNavigation.map((group) => (\n <SidebarGroup\n key={group.id}\n heading={group.heading}\n collapsible={group.collapsible}\n >\n {group.links.map((link) => (\n <SidebarItem\n key={link.path}\n label={link.label}\n path={link.path}\n badge={link.badge}\n />\n ))}\n </SidebarGroup>\n ))}\n </nav>\n </ScrollArea>\n </aside>\n );\n}\n","/**\n * Shell hooks -- reactive APIs for modules.\n *\n * Import path: @/shell/hooks\n */\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { events } from \"./api\";\nimport { useShellModules } from \"./shellModules\";\nimport type { Contribution, ShellModule, ShellEventMap } from \"./types\";\n\n/**\n * Get sorted contributions for a named extension point.\n *\n * Returns all contributions registered by any module for the given\n * extension point, sorted by `order` (ascending).\n *\n * @example\n * ```tsx\n * const tabs = useExtensionPoint(\"organization-detail\");\n * ```\n */\nexport function useExtensionPoint(name: string): Contribution[] {\n const { getExtensionPoint } = useShellModules();\n return useMemo(() => getExtensionPoint(name), [getExtensionPoint, name]);\n}\n\n/**\n * Get the list of visible service modules (excludes hidden and bottom-pinned).\n *\n * Useful for building overviews like the home page.\n */\nexport function useMainModules(): ShellModule[] {\n const { mainModules } = useShellModules();\n return mainModules;\n}\n\n/**\n * Subscribe to a shell event, auto-unsubscribing on unmount.\n *\n * The handler reference is stabilized via useRef so that the\n * subscription is not torn down and recreated on every render.\n *\n * @example\n * ```tsx\n * useShellEvent(\"schema-definition:changed\", (payload) => {\n * queryClient.invalidateQueries({ queryKey: [\"my-key\"] });\n * });\n * ```\n */\nexport function useShellEvent<K extends keyof ShellEventMap>(\n key: K,\n handler: (payload: ShellEventMap[K]) => void,\n): void {\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n return events.on(key, (payload) => handlerRef.current(payload));\n }, [key]);\n}\n","/**\n * Shell API -- imperative domain capabilities for modules.\n *\n * Import path: @/shell/api\n *\n * Grouped by domain: notification, dialog, navigation, panel, events.\n */\nimport type { ReactNode } from \"react\";\nimport { toast } from \"sonner\";\nimport { warnLog } from \"@petrarca/sonnet-core\";\nimport type { ShellEventMap } from \"./types\";\nimport * as eventBus from \"./eventBus\";\n\n// notification\n\ninterface PromiseOptions<T> {\n loading: string;\n success: string | ((data: T) => string);\n error: string | ((err: unknown) => string);\n}\n\nexport const notification = {\n success(message: string) {\n toast.success(message);\n },\n error(message: string) {\n toast.error(message);\n },\n warning(message: string) {\n toast.warning(message);\n },\n info(message: string) {\n toast.info(message);\n },\n promise<T>(promise: Promise<T>, opts: PromiseOptions<T>) {\n return toast.promise(promise, opts);\n },\n dismiss(id?: string | number) {\n toast.dismiss(id);\n },\n};\n\n// dialog\n\n// The shell mounts a single ConfirmDialog instance in AppShell and injects\n// its open function here via initDialog(). Modules call dialog.confirm()\n// without knowing where the implementation lives -- federation-safe, same\n// pattern as navigation and panel.\n\nexport interface ConfirmOptions {\n title: string;\n description?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: \"default\" | \"destructive\";\n}\n\ntype DialogConfirmFn = (opts: ConfirmOptions) => Promise<boolean>;\n\nlet _dialogConfirm: DialogConfirmFn | null = null;\n\n/**\n * Called by the shell during initialization to inject the confirm dialog.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initDialog(confirmFn: DialogConfirmFn) {\n _dialogConfirm = confirmFn;\n}\n\nexport const dialog = {\n /**\n * Show a confirmation dialog. Returns true if confirmed, false if cancelled.\n */\n confirm(opts: ConfirmOptions): Promise<boolean> {\n if (_dialogConfirm) {\n return _dialogConfirm(opts);\n }\n warnLog(\"dialog.confirm: shell not initialized yet.\");\n return Promise.resolve(false);\n },\n};\n\n// navigation\n\n// The shell injects the navigate function and command menu opener at startup\n// via initNavigation(). Modules call navigation.goTo() etc. without knowing\n// where the implementation comes from -- works with both monorepo and\n// Module Federation (host provides the injector, remotes consume the API).\n\ntype NavigateFn = (path: string) => void;\ntype CommandMenuFn = () => void;\ntype FeatureLookupFn = (featureId: string) => string | null;\n\nlet _navigate: NavigateFn | null = null;\nlet _openCommandMenu: CommandMenuFn | null = null;\nlet _lookupFeature: FeatureLookupFn | null = null;\n\n/**\n * Called by the shell during initialization to inject router + command menu.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initNavigation(\n navigateFn: NavigateFn,\n openCommandMenuFn: CommandMenuFn,\n) {\n _navigate = navigateFn;\n _openCommandMenu = openCommandMenuFn;\n}\n\n/**\n * Called by the shell during initialization to inject the feature lookup.\n * Resolves a stable feature id (e.g. \"auditing.audit-events\") to a path.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initFeatureNav(lookupFn: FeatureLookupFn) {\n _lookupFeature = lookupFn;\n}\n\nexport const navigation = {\n /** Navigate to a path within the shell. */\n goTo(path: string) {\n if (_navigate) {\n _navigate(path);\n } else {\n warnLog(\"navigation.goTo: shell not initialized yet.\");\n }\n },\n /**\n * Navigate to a feature by its stable identifier (e.g. \"auditing.audit-events\").\n * The path is resolved from the module registry at call time, so it stays\n * correct even if routes are reorganised.\n */\n goToFeature(featureId: string) {\n if (!_navigate || !_lookupFeature) {\n warnLog(\"navigation.goToFeature: shell not initialized yet.\");\n return;\n }\n const path = _lookupFeature(featureId);\n if (path) {\n _navigate(path);\n } else {\n warnLog(\"navigation.goToFeature: unknown feature id '{}'.\", featureId);\n }\n },\n /** Open the Cmd+K command menu. */\n openCommandMenu() {\n if (_openCommandMenu) {\n _openCommandMenu();\n } else {\n warnLog(\"navigation.openCommandMenu: shell not initialized yet.\");\n }\n },\n /** Go back in browser history. */\n back() {\n window.history.back();\n },\n};\n\n// panel\n\n// The shell owns the slide-over panel (Sheet). Modules request it via\n// panel.open() with content to render. The shell renders that content inside\n// the panel. Same injection pattern as navigation -- federation-safe.\n\nexport interface PanelOptions {\n /**\n * Title shown in the panel header. When omitted the header is visually\n * hidden (the content owns its own header) but a screen-reader-only\n * title is still rendered for accessibility.\n */\n title?: string;\n /** React content to render inside the panel */\n content: ReactNode;\n /** Optional description below the title */\n description?: string;\n /**\n * Panel width token.\n *\n * - \"narrow\" -- 480px -- quick actions, simple single-field forms\n * - \"default\" -- 640px -- standard entity forms (default when omitted)\n * - \"wide\" -- 800px -- forms with embedded tables or complex layouts\n * - \"half\" -- 50vw -- split-screen inspection, side-by-side context\n * - \"full\" -- 100vw -- immersive editing, full-width canvases\n *\n * Legacy values \"lg\", \"1/3\", \"1/2\" are kept for backward compatibility\n * and map to \"default\" (640px), 33vw, and \"half\" (50vw) respectively.\n */\n width?:\n | \"narrow\"\n | \"default\"\n | \"wide\"\n | \"half\"\n | \"full\"\n | \"lg\"\n | \"1/3\"\n | \"1/2\";\n /**\n * Vertical coverage of the panel.\n * - \"below-header\" (default): panel starts below the TopBar, keeping it visible.\n * - \"full\": panel covers the full viewport height including the TopBar.\n */\n coverage?: \"below-header\" | \"full\";\n /**\n * Allow the user to drag the left edge of the panel to resize it.\n * Defaults to false. When true, the fixed `width` token is used as the\n * initial width and the panel width is controlled dynamically.\n */\n resizable?: boolean;\n /**\n * Minimum width in pixels when resizable. Defaults to 240.\n * Ignored when resizable is false.\n */\n minWidth?: number;\n /**\n * Maximum width in pixels when resizable. Defaults to 90% of viewport width.\n * Ignored when resizable is false.\n */\n maxWidth?: number;\n /** Called after the panel finishes closing. Use to restore focus to the triggering element. */\n onClose?: () => void;\n}\n\ntype PanelOpenFn = (opts: PanelOptions) => void;\ntype PanelCloseFn = () => void;\n\nlet _panelOpen: PanelOpenFn | null = null;\nlet _panelClose: PanelCloseFn | null = null;\n\n/**\n * Called by the shell during initialization to inject panel controls.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initPanel(openFn: PanelOpenFn, closeFn: PanelCloseFn) {\n _panelOpen = openFn;\n _panelClose = closeFn;\n}\n\nexport const panel = {\n /** Open the shell panel with content rendered inside. */\n open(opts: PanelOptions) {\n if (_panelOpen) {\n _panelOpen(opts);\n } else {\n warnLog(\"panel.open: shell not initialized yet.\");\n }\n },\n /** Close the panel. */\n close() {\n if (_panelClose) {\n _panelClose();\n } else {\n warnLog(\"panel.close: shell not initialized yet.\");\n }\n },\n};\n\n// sidePane\n\n// The shell owns the side pane (SidePane component in AppShell). Modules\n// request it via sidePane.open() with content and sizing options. The pane\n// sits inline beside the content area -- the main view shrinks rather than\n// being overlaid. Same injection pattern as panel -- federation-safe.\n\nimport type { SidePaneOptions } from \"./sidePaneState\";\nexport type { SidePaneOptions };\n\ntype SidePaneOpenFn = (opts: SidePaneOptions) => void;\ntype SidePaneCloseFn = () => void;\ntype SidePaneIsOpenFn = () => boolean;\ntype SidePaneActiveModuleIdFn = () => string | null;\ntype SidePaneSetFullWidthFn = (fullWidth: boolean) => void;\n\nlet _sidePaneOpen: SidePaneOpenFn | null = null;\nlet _sidePaneClose: SidePaneCloseFn | null = null;\nlet _sidePaneIsOpen: SidePaneIsOpenFn | null = null;\nlet _sidePaneActiveModuleId: SidePaneActiveModuleIdFn | null = null;\nlet _sidePaneSetFullWidth: SidePaneSetFullWidthFn | null = null;\n\n/**\n * Called by the shell during initialization to inject side pane controls.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initSidePane(\n openFn: SidePaneOpenFn,\n closeFn: SidePaneCloseFn,\n isOpenFn: SidePaneIsOpenFn,\n activeModuleIdFn: SidePaneActiveModuleIdFn,\n setFullWidthFn?: SidePaneSetFullWidthFn,\n) {\n _sidePaneOpen = openFn;\n _sidePaneClose = closeFn;\n _sidePaneIsOpen = isOpenFn;\n _sidePaneActiveModuleId = activeModuleIdFn;\n _sidePaneSetFullWidth = setFullWidthFn ?? null;\n}\n\nexport const sidePane = {\n /** Open the side pane with the given content and sizing options. */\n open(opts: SidePaneOptions) {\n if (_sidePaneOpen) {\n _sidePaneOpen(opts);\n } else {\n warnLog(\"sidePane.open: shell not initialized yet.\");\n }\n },\n /** Close the side pane. */\n close() {\n if (_sidePaneClose) {\n _sidePaneClose();\n } else {\n warnLog(\"sidePane.close: shell not initialized yet.\");\n }\n },\n /**\n * Toggle the side pane.\n *\n * If the pane is closed, opens it with opts.\n * If the pane is open with the same moduleId, closes it.\n * If the pane is open with a different moduleId, replaces it with opts.\n */\n toggle(opts: SidePaneOptions) {\n if (\n !_sidePaneIsOpen ||\n !_sidePaneActiveModuleId ||\n !_sidePaneOpen ||\n !_sidePaneClose\n ) {\n warnLog(\"sidePane.toggle: shell not initialized yet.\");\n return;\n }\n if (_sidePaneIsOpen() && _sidePaneActiveModuleId() === opts.moduleId) {\n _sidePaneClose();\n } else {\n _sidePaneOpen(opts);\n }\n },\n /** Returns true when the pane is currently open. */\n isOpen(): boolean {\n return _sidePaneIsOpen ? _sidePaneIsOpen() : false;\n },\n /** Returns the moduleId of the currently open pane, or null. */\n activeModuleId(): string | null {\n return _sidePaneActiveModuleId ? _sidePaneActiveModuleId() : null;\n },\n /**\n * Expand or collapse the pane to fill the full content area.\n *\n * When true the pane takes flex-1 and hides the main route content.\n * Use for full-screen tool views (schema viewer, form editors).\n * Call setFullWidth(false) to restore normal inline sizing.\n */\n setFullWidth(fullWidth: boolean) {\n if (_sidePaneSetFullWidth) {\n _sidePaneSetFullWidth(fullWidth);\n } else {\n warnLog(\"sidePane.setFullWidth: shell not initialized yet.\");\n }\n },\n};\n\n// fullscreen\n\n// The shell owns the fullscreen overlay. Modules request it via\n// fullscreen.enter(content) and exit via fullscreen.exit(). The shell renders\n// the content in a fixed inset-0 z-50 overlay, covering all shell chrome.\n// Same injection pattern as panel -- federation-safe.\n\nexport interface FullscreenOptions {\n /** React content to render inside the fullscreen overlay. */\n content: ReactNode;\n}\n\ntype FullscreenEnterFn = (opts: FullscreenOptions) => void;\ntype FullscreenExitFn = () => void;\n\nlet _fullscreenEnter: FullscreenEnterFn | null = null;\nlet _fullscreenExit: FullscreenExitFn | null = null;\n\n/**\n * Called by the shell during initialization to inject fullscreen controls.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initFullscreen(\n enterFn: FullscreenEnterFn,\n exitFn: FullscreenExitFn,\n) {\n _fullscreenEnter = enterFn;\n _fullscreenExit = exitFn;\n}\n\nexport const fullscreen = {\n /** Enter fullscreen mode, rendering content over all shell chrome. */\n enter(opts: FullscreenOptions) {\n if (_fullscreenEnter) {\n _fullscreenEnter(opts);\n } else {\n warnLog(\"fullscreen.enter: shell not initialized yet.\");\n }\n },\n /** Exit fullscreen mode, restoring the normal shell layout. */\n exit() {\n if (_fullscreenExit) {\n _fullscreenExit();\n } else {\n warnLog(\"fullscreen.exit: shell not initialized yet.\");\n }\n },\n};\n\n// events\n\ntype Unsubscribe = () => void;\n\nexport const events = {\n /** Subscribe to an event. Returns an unsubscribe function. */\n on<K extends keyof ShellEventMap>(\n key: K,\n handler: (payload: ShellEventMap[K]) => void,\n ): Unsubscribe {\n return eventBus.on(key as string, handler as (payload: unknown) => void);\n },\n\n /** Subscribe, auto-unsubscribing after the first emission. */\n once<K extends keyof ShellEventMap>(\n key: K,\n handler: (payload: ShellEventMap[K]) => void,\n ): Unsubscribe {\n return eventBus.once(key as string, handler as (payload: unknown) => void);\n },\n\n /** Emit an event. Handlers run asynchronously (microtask). */\n emit<K extends keyof ShellEventMap>(key: K, payload: ShellEventMap[K]): void {\n eventBus.emit(key as string, payload);\n },\n};\n","/**\n * Shell event bus -- in-memory pub/sub for cross-module communication.\n *\n * The bus is untyped internally (operates on strings and unknown payloads).\n * Type safety is provided by the public API layer in shell/api.ts, which\n * constrains keys and payloads via the ShellEventMap interface.\n *\n * Mirrors the server-side EventBus pattern (fire-and-forget, error isolation)\n * adapted for the browser's single-threaded model.\n */\nimport { devLog, warnLog } from \"@petrarca/sonnet-core\";\n\ntype Handler = (payload: unknown) => void;\ntype Unsubscribe = () => void;\n\nconst listeners = new Map<string, Set<Handler>>();\n\n/** Subscribe to an event key. Returns an unsubscribe function. */\nexport function on(key: string, handler: Handler): Unsubscribe {\n let set = listeners.get(key);\n if (!set) {\n set = new Set();\n listeners.set(key, set);\n }\n set.add(handler);\n devLog(`[shell:event] on \"${key}\" (${set.size} handler(s))`);\n return () => {\n set!.delete(handler);\n if (set!.size === 0) listeners.delete(key);\n devLog(`[shell:event] off \"${key}\" (${set!.size} handler(s))`);\n };\n}\n\n/** Subscribe to an event key, auto-unsubscribing after the first emission. */\nexport function once(key: string, handler: Handler): Unsubscribe {\n const unsubscribe = on(key, (payload) => {\n unsubscribe();\n handler(payload);\n });\n return unsubscribe;\n}\n\n/** Emit an event. Handlers run in microtasks (non-blocking, error-isolated). */\nexport function emit(key: string, payload: unknown): void {\n const set = listeners.get(key);\n const count = set?.size ?? 0;\n devLog(`[shell:event] emit \"${key}\"`, payload, `(${count} handler(s))`);\n if (!set || count === 0) return;\n for (const handler of set) {\n Promise.resolve().then(() => {\n try {\n handler(payload);\n } catch (err) {\n warnLog(`[shell:event] handler error \"${key}\":`, err);\n }\n });\n }\n}\n","/**\n * Shell modules context.\n *\n * Provides the module registry to shell components (IconRail,\n * CommandMenu, hooks) without requiring them to import from the\n * app-specific modules/registry.ts. AppShell injects the registry;\n * children consume it via useShellModules().\n */\nimport { createContext, useContext } from \"react\";\nimport type { ModuleRegistry } from \"./createModuleRegistry\";\n\nexport const ShellModulesContext = createContext<ModuleRegistry | null>(null);\n\n/**\n * Read the module registry from context.\n * Must be called within an AppShell (which provides the context).\n */\nexport function useShellModules(): ModuleRegistry {\n const registry = useContext(ShellModulesContext);\n if (!registry) {\n throw new Error(\"useShellModules must be used within an AppShell\");\n }\n return registry;\n}\n","/** Labeled, optionally collapsible section within a Sidebar. */\nimport React, { useState } from \"react\";\nimport { ChevronDown } from \"lucide-react\";\nimport { cn } from \"@petrarca/sonnet-core\";\n\nexport interface SidebarGroupProps {\n heading?: string;\n collapsible?: boolean;\n defaultOpen?: boolean;\n /** Render a border separator below this group. */\n separator?: boolean;\n children: React.ReactNode;\n}\n\n// Section heading with optional collapse toggle.\nfunction GroupHeading({\n heading,\n canCollapse,\n open,\n onToggle,\n}: {\n heading: string;\n canCollapse: boolean;\n open: boolean;\n onToggle: () => void;\n}) {\n return (\n <button\n type=\"button\"\n onClick={canCollapse ? onToggle : undefined}\n className={cn(\n \"flex w-full items-center gap-1 px-2 py-1.5\",\n \"text-[11px] font-semibold uppercase tracking-widest text-muted-foreground/70\",\n canCollapse ? \"hover:text-foreground cursor-pointer\" : \"cursor-default\",\n )}\n >\n {canCollapse && (\n <ChevronDown\n className={cn(\"h-3 w-3 transition-transform\", !open && \"-rotate-90\")}\n />\n )}\n <span>{heading}</span>\n </button>\n );\n}\n\nexport function SidebarGroup({\n heading,\n collapsible = false,\n defaultOpen = true,\n separator = false,\n children,\n}: SidebarGroupProps): React.ReactElement {\n const [open, setOpen] = useState(defaultOpen);\n const canCollapse = collapsible && !!heading;\n\n return (\n <div className={cn(separator && \"border-b border-border pb-2\")}>\n {heading && (\n <GroupHeading\n heading={heading}\n canCollapse={canCollapse}\n open={open}\n onToggle={() => setOpen((v) => !v)}\n />\n )}\n {(!canCollapse || open) && <ul className=\"space-y-0.5\">{children}</ul>}\n </div>\n );\n}\n","/** Single navigation row inside a Sidebar — icon + label + optional trailing slot. */\nimport React from \"react\";\nimport { Link, useLocation } from \"react-router-dom\";\nimport { ChevronRight } from \"lucide-react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport type { LucideIcon } from \"lucide-react\";\n\nexport interface SidebarItemProps {\n icon?: LucideIcon;\n label: string;\n path: string;\n badge?: string | number;\n indent?: number;\n expandable?: boolean;\n expanded?: boolean;\n onToggle?: () => void;\n onClick?: () => void;\n /**\n * Render greyed and non-navigable (e.g. a capability requirement is unmet).\n * Uses aria-disabled, not native disable, so the row stays focusable and its\n * title explaining why remains discoverable -- matching RailIcon.\n */\n disabled?: boolean;\n /** Tooltip / title shown when disabled (e.g. why it is unavailable). */\n title?: string;\n}\n\n// Trailing badge or expand chevron rendered after the label.\nfunction ItemTrailing({\n badge,\n expandable,\n expanded,\n onToggle,\n}: Pick<SidebarItemProps, \"badge\" | \"expandable\" | \"expanded\" | \"onToggle\">) {\n if (badge != null) {\n return (\n <span className=\"inline-flex items-center rounded-full bg-muted px-1.5 h-[18px] text-[10px] tabular-nums text-muted-foreground\">\n {badge}\n </span>\n );\n }\n if (expandable) {\n const handleClick = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onToggle?.();\n };\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n className=\"p-0.5 text-muted-foreground hover:text-foreground\"\n >\n <ChevronRight\n className={cn(\n \"h-3 w-3 transition-transform\",\n expanded && \"rotate-90\",\n )}\n />\n </button>\n );\n }\n return null;\n}\n\n// Row appearance by state, kept out of the component to hold it under the\n// complexity limit.\nfunction itemStateClass(disabled: boolean, isActive: boolean): string {\n if (disabled)\n return \"text-muted-foreground/40 cursor-default hover:bg-transparent\";\n if (isActive) return \"bg-accent text-accent-foreground\";\n return \"text-muted-foreground hover:text-foreground hover:bg-accent/50\";\n}\n\nexport function SidebarItem({\n icon: Icon,\n label,\n path,\n badge,\n indent = 0,\n expandable = false,\n expanded = false,\n onToggle,\n onClick,\n disabled = false,\n title,\n}: SidebarItemProps): React.ReactElement {\n const { pathname } = useLocation();\n const isActive = !disabled && pathname === path;\n const style =\n indent > 0 ? { paddingLeft: `${indent * 16 + 8}px` } : undefined;\n // aria-disabled (not native) keeps the row focusable so its title explaining\n // why it is unavailable stays discoverable -- see RailIcon.\n const disabledProps = disabled\n ? {\n \"aria-disabled\": true as const,\n title,\n onClick: (e: React.MouseEvent) => e.preventDefault(),\n }\n : { onClick };\n\n return (\n <li>\n <Link\n to={path}\n {...disabledProps}\n style={style}\n className={cn(\n \"flex items-center gap-2 rounded px-2 py-1.5 text-[13px] font-medium transition-colors select-none\",\n itemStateClass(disabled, isActive),\n )}\n >\n {Icon && <Icon className=\"h-4 w-4 shrink-0\" />}\n <span className=\"min-w-0 flex-1 truncate\">{label}</span>\n <ItemTrailing\n badge={badge}\n expandable={expandable}\n expanded={expanded}\n onToggle={onToggle}\n />\n </Link>\n </li>\n );\n}\n","/**\n * Shell navigation context — provides active service state and interaction\n * handlers to ShellRail, SubNavPanel, and any custom navigation components.\n *\n * Provided by AppShell. Consumed by ShellRail (and available to consumers\n * who build custom navigation layouts).\n */\nimport { createContext, useContext } from \"react\";\nimport type { ShellModule } from \"./types\";\n\nexport interface ShellNavigationState {\n activeService: ShellModule | null;\n sidePaneModuleId: string | null;\n subNavCollapsed: boolean;\n onToggleSubNav: () => void;\n onServiceSelect: (id: string) => void;\n}\n\nexport const ShellNavigationContext =\n createContext<ShellNavigationState | null>(null);\n\n/** Read shell navigation state. Must be used within AppShell. */\nexport function useShellNavigation(): ShellNavigationState {\n const ctx = useContext(ShellNavigationContext);\n if (!ctx) {\n throw new Error(\"useShellNavigation must be used within AppShell\");\n }\n return ctx;\n}\n","/**\n * Shell configuration types and context.\n *\n * ShellConfig is intentionally minimal. The shell provides layout chrome\n * (TopBar header, IconRail, SubNavPanel, CommandMenu) but does not own\n * any app-specific content. The consumer composes the TopBar content --\n * logo, environment selector, search trigger, user menu -- via the\n * `topBar` ReactNode slot using building blocks exported from the shell.\n */\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type { CapabilityPolicy, CapabilityResolver } from \"./types\";\n\n// Top-level shell configuration.\n\nexport interface ShellConfig {\n /**\n * Content rendered inside the TopBar header. The shell provides the\n * `<header>` element with its sizing and border; the consumer owns\n * everything inside it.\n *\n * Compose this using building blocks like `SearchTrigger` and\n * `UserMenu` (exported from the shell) alongside any app-specific\n * components.\n */\n topBar?: ReactNode;\n\n /**\n * Content rendered inside the ShellFooter. The shell provides the\n * `<footer>` element with its sizing and border; the consumer owns\n * everything inside it.\n *\n * Common uses: status bar, sync indicators, version info, quick actions.\n * Omit to render no footer.\n */\n footer?: ReactNode;\n\n /**\n * Evaluates a module capability (from `ShellModule.requires`) for gating the\n * icon rail / command menu / routes. Absent ⇒ no gating (all modules\n * available). See docs/design/module-capabilities.md in this package.\n */\n resolveCapability?: CapabilityResolver;\n\n /**\n * Sparse overrides for the (unmet reason kind, surface) → effect matrix,\n * merged over the shell defaults. Lets the app tune hide-vs-disable per\n * reason kind and surface without touching modules.\n */\n capabilityPolicy?: CapabilityPolicy;\n}\n\n// Context\n\nexport const ShellConfigContext = createContext<ShellConfig | null>(null);\n\n/**\n * Read the shell configuration from context.\n * Must be called within a ShellConfigProvider.\n */\nexport function useShellConfig(): ShellConfig {\n const config = useContext(ShellConfigContext);\n if (!config) {\n throw new Error(\"useShellConfig must be used within a ShellConfigProvider\");\n }\n return config;\n}\n","/**\n * Capability gating — evaluate a module's requirements into a per-surface\n * presentation effect.\n *\n * See docs/design/module-capabilities.md in this package. The shell is\n * domain-agnostic: a module declares `requires` capabilities, the app provides a\n * `CapabilityResolver`, and the shell maps the resolver's semantic reason kind\n * to an `Effect` via a surface-aware policy matrix.\n */\nimport { warnLog } from \"@petrarca/sonnet-core\";\nimport type {\n CapabilityPolicy,\n CapabilityResolver,\n Effect,\n ShellModule,\n Surface,\n UnmetKind,\n} from \"./types\";\n\n/**\n * Default (kind, surface) → effect matrix. App overrides are merged over this.\n *\n * The `route` column is planned, not yet wired: no caller passes the `\"route\"`\n * surface to `evaluateModule` (only `\"rail\"` and `\"command\"` are evaluated\n * today), so its `redirect` effects do not fire. The column is kept because\n * route gating is a designed capability -- a gated module's routes still render\n * if reached directly -- and removing it would be a type break for the\n * capability we intend to build. See docs/design/module-capabilities.md.\n */\nexport const DEFAULT_CAPABILITY_POLICY: Record<\n UnmetKind,\n Record<Surface, Effect>\n> = {\n \"missing-state\": { rail: \"disabled\", command: \"disabled\", route: \"redirect\" },\n \"not-authorized\": { rail: \"hidden\", command: \"hidden\", route: \"redirect\" },\n \"not-authorized-soft\": {\n rail: \"disabled\",\n command: \"disabled\",\n route: \"redirect\",\n },\n \"feature-off\": { rail: \"hidden\", command: \"disabled\", route: \"redirect\" },\n error: { rail: \"hidden\", command: \"hidden\", route: \"redirect\" },\n};\n\n/** Strictness order — the most restrictive effect wins when folding requirements. */\nconst EFFECT_RANK: Record<Effect, number> = {\n hidden: 3,\n redirect: 2,\n disabled: 1,\n visible: 0,\n};\n\n/** Merge a sparse app override over the default matrix. */\nexport function resolveCapabilityPolicy(\n override?: CapabilityPolicy,\n): Record<UnmetKind, Record<Surface, Effect>> {\n if (!override) return DEFAULT_CAPABILITY_POLICY;\n const merged = {} as Record<UnmetKind, Record<Surface, Effect>>;\n for (const kind of Object.keys(DEFAULT_CAPABILITY_POLICY) as UnmetKind[]) {\n merged[kind] = {\n ...DEFAULT_CAPABILITY_POLICY[kind],\n ...(override[kind] ?? {}),\n };\n }\n return merged;\n}\n\nexport interface ModuleEffect {\n effect: Effect;\n /** Hint from the first most-restrictive unmet capability (for tooltips). */\n reason?: string;\n}\n\n/**\n * Fold a single unmet-capability result into the running accumulator.\n * Extracted to keep evaluateModule below the cyclomatic-complexity limit.\n */\nfunction foldUnmet(\n effect: Effect,\n resultReason: string | undefined,\n acc: ModuleEffect,\n): ModuleEffect {\n const rank = EFFECT_RANK[effect];\n const worstRank = EFFECT_RANK[acc.effect];\n if (rank > worstRank) {\n // Strictly more restrictive — take this effect and its reason.\n return { effect, reason: resultReason };\n }\n if (rank === worstRank && !acc.reason && resultReason) {\n // Same restrictiveness; take the first available reason at this rank.\n return { effect: acc.effect, reason: resultReason };\n }\n return acc;\n}\n\n/**\n * Evaluate a module's requirements for a given surface.\n *\n * Returns `visible` when there are no requirements or all are met. Otherwise\n * each unmet capability maps (kind, surface) → effect and the most restrictive\n * wins (hidden > redirect > disabled > visible). When capabilities share the\n * same effect rank, the first one with a non-empty reason provides the tooltip.\n */\nexport function evaluateModule(\n module: ShellModule,\n surface: Surface,\n resolve: CapabilityResolver | undefined,\n policy: Record<UnmetKind, Record<Surface, Effect>>,\n): ModuleEffect {\n const capabilities = module.requires;\n if (!capabilities || capabilities.length === 0) return { effect: \"visible\" };\n // No resolver ⇒ nothing to evaluate against ⇒ available (gating disabled).\n if (!resolve) return { effect: \"visible\" };\n\n let acc: ModuleEffect = { effect: \"visible\" };\n for (const capability of capabilities) {\n const result = resolve(capability);\n if (result.met) continue;\n const kind = result.kind ?? \"error\";\n if (kind === \"error\") {\n warnLog(\n `[capabilities] unmet capability \"${capability}\" resolved to kind \"error\" (fail-closed)`,\n );\n }\n acc = foldUnmet(policy[kind][surface], result.reason, acc);\n }\n return acc;\n}\n","/**\n * SidePane -- persistent resizable inline panel.\n *\n * Sits between SubNavPanel and the main content area in the AppShell flex row.\n * When open it takes a fixed pixel width; the content area (flex-1) shrinks to\n * fill the remainder. The main view remains fully visible and interactive.\n *\n * Opened/closed imperatively via sidePane.open() / sidePane.close() / sidePane.toggle()\n * from api.ts. State is provided by SidePaneContext (owned by AppShell).\n */\nimport { X } from \"lucide-react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { useResizablePanel } from \"@petrarca/sonnet-core/hooks\";\nimport { useSidePaneState } from \"./sidePaneState\";\nimport { DragHandle } from \"./DragHandle\";\n\nconst DEFAULT_WIDTH = 320;\nconst DEFAULT_MIN = 240;\nconst DEFAULT_MAX = 800;\n\n/**\n * SidePane renders nothing when no pane is open.\n * When open it renders a bordered panel with a drag handle on its right edge.\n * Double-clicking the handle resets to defaultWidth.\n */\nexport default function SidePane() {\n const { pane, close } = useSidePaneState();\n if (!pane) return null;\n return <SidePaneOpen pane={pane} onClose={close} />;\n}\n\nfunction SidePaneOpen({\n pane,\n onClose,\n}: {\n pane: NonNullable<ReturnType<typeof useSidePaneState>[\"pane\"]>;\n onClose: () => void;\n}) {\n const { panelWidth, handlePointerDown, handleDoubleClick, separatorProps } =\n useResizablePanel({\n defaultWidth: pane.defaultWidth ?? DEFAULT_WIDTH,\n minWidth: pane.minWidth ?? DEFAULT_MIN,\n maxWidth: pane.maxWidth ?? DEFAULT_MAX,\n direction: \"right-edge\",\n });\n\n const isFullWidth = pane.fullWidth === true;\n\n return (\n <div\n className={cn(\n \"relative flex flex-col border-r bg-background\",\n isFullWidth ? \"flex-1\" : \"shrink-0\",\n )}\n style={isFullWidth ? undefined : { width: panelWidth }}\n >\n {/* Header */}\n <div className=\"flex h-10 shrink-0 items-center justify-end border-b px-2\">\n <button\n onClick={onClose}\n className={cn(\n \"flex h-6 w-6 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground transition-colors\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n aria-label=\"Close side pane\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-auto\">{pane.content}</div>\n\n {!isFullWidth && (\n <DragHandle\n edge=\"right\"\n separatorProps={separatorProps}\n onPointerDown={handlePointerDown}\n onDoubleClick={handleDoubleClick}\n />\n )}\n </div>\n );\n}\n","/**\n * Side pane state types and context.\n *\n * The side pane is a persistent, resizable inline panel that sits between\n * the SubNavPanel and the main content area. It shrinks the content area\n * rather than overlaying it -- the main view stays visible and interactive.\n *\n * Modules open/close/toggle it via the imperative sidePane API in api.ts.\n * AppShell owns the state and injects it here via SidePaneContext so that\n * SidePane.tsx can read it without coupling to AppShell directly.\n */\nimport { createContext, useContext, type ReactNode } from \"react\";\n\nexport interface SidePaneOptions {\n /** Content to render inside the side pane. */\n content: ReactNode;\n /**\n * Stable key identifying which module/tool owns this pane instance.\n * Used by IconRail to highlight the active pane module and by toggle()\n * to detect whether the same content is already open.\n */\n moduleId: string;\n /** Initial width in pixels. Defaults to 320. */\n defaultWidth?: number;\n /** Minimum draggable width in pixels. Defaults to 240. */\n minWidth?: number;\n /**\n * Maximum draggable width in pixels.\n * Omit or pass Infinity for unlimited (fills remaining space up to viewport).\n */\n maxWidth?: number;\n /**\n * When true, the pane expands to fill the entire content area (flex-1),\n * collapsing the main route content to zero width. Use for full-screen\n * tool views (schema viewer, form editors). The drag handle is hidden.\n * Toggle via sidePane.setFullWidth(boolean) from inside the pane content.\n */\n fullWidth?: boolean;\n}\n\nexport interface SidePaneState {\n /** Currently open pane options, or null when closed. */\n pane: SidePaneOptions | null;\n open: (opts: SidePaneOptions) => void;\n close: () => void;\n toggle: (opts: SidePaneOptions) => void;\n /** Expand or collapse the pane to fill the full content area. */\n setFullWidth: (fullWidth: boolean) => void;\n}\n\nexport const SidePaneContext = createContext<SidePaneState | null>(null);\n\n/**\n * Read the side pane state from context.\n * Must be called within an AppShell (which provides the context).\n */\nexport function useSidePaneState(): SidePaneState {\n const ctx = useContext(SidePaneContext);\n if (!ctx) {\n throw new Error(\"useSidePaneState must be used within an AppShell\");\n }\n return ctx;\n}\n","/**\n * DragHandle — shared resize handle used by SidePane (right-edge) and\n * SlideOverPanel (left-edge).\n *\n * Invisible at rest. On hover shows a 2px primary line; while actively\n * dragging the line thickens to 4px. The hit area is always 8px wide\n * regardless of visual state.\n */\nimport type React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\n\ninterface DragHandleProps {\n /** Which edge of the parent the handle sits on. */\n edge: \"left\" | \"right\";\n separatorProps: React.HTMLAttributes<HTMLDivElement>;\n onPointerDown: React.PointerEventHandler<HTMLDivElement>;\n onDoubleClick: React.MouseEventHandler<HTMLDivElement>;\n}\n\nexport function DragHandle({\n edge,\n separatorProps,\n onPointerDown,\n onDoubleClick,\n}: DragHandleProps) {\n return (\n <div\n {...separatorProps}\n onPointerDown={onPointerDown}\n onDoubleClick={onDoubleClick}\n className={cn(\n // 8px invisible hit area; visual feedback only via the before pseudo-element.\n \"absolute top-0 h-full w-2 cursor-col-resize\",\n edge === \"right\" ? \"right-0\" : \"left-0\",\n // Centered line: invisible at rest, 2px on hover, 4px while dragging.\n \"before:absolute before:inset-y-0 before:left-1/2 before:-translate-x-1/2\",\n \"before:w-0 before:transition-all before:duration-150\",\n \"hover:before:w-0.5 hover:before:bg-primary/60\",\n \"active:before:w-1 active:before:bg-primary/80\",\n )}\n />\n );\n}\n","import { useEffect, useCallback, useMemo } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport {\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n} from \"@petrarca/sonnet-ui\";\nimport { useShellModules } from \"./shellModules\";\nimport { useShellConfig } from \"./shellConfig\";\nimport { evaluateModule, resolveCapabilityPolicy } from \"./capabilities\";\nimport type { ShellModule } from \"./types\";\n\ninterface CommandMenuProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\n/**\n * CommandMenu -- Cmd+K command menu.\n *\n * Indexes all navigation items from the service domains so users can\n * quickly jump to any page by typing.\n */\nexport default function CommandMenu({ open, onOpenChange }: CommandMenuProps) {\n const navigate = useNavigate();\n const { mainModules, bottomModules } = useShellModules();\n const { resolveCapability, capabilityPolicy } = useShellConfig();\n\n // Global Cmd+K / Ctrl+K shortcut\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n onOpenChange(!open);\n }\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, onOpenChange]);\n\n const policy = useMemo(\n () => resolveCapabilityPolicy(capabilityPolicy),\n [capabilityPolicy],\n );\n\n // Evaluate a module's requirements for the \"command\" surface.\n const moduleEffect = (service: ShellModule) =>\n evaluateModule(service, \"command\", resolveCapability, policy);\n\n const handleSelect = useCallback(\n (path: string) => {\n navigate(path);\n onOpenChange(false);\n },\n [navigate, onOpenChange],\n );\n\n const renderNavGroup = (service: ShellModule) => {\n if (service.navigation.length === 0) return null;\n const { effect, reason } = moduleEffect(service);\n // hidden / redirect ⇒ omit from the palette; disabled ⇒ show greyed.\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n const Icon = service.icon;\n return (\n <CommandGroup key={`nav-${service.id}`} heading={service.label}>\n {service.navigation.flatMap((group) =>\n group.links.map((link) => (\n <CommandItem\n key={link.path}\n value={`${service.label} ${group.heading ?? \"\"} ${link.label}`}\n disabled={disabled}\n onSelect={disabled ? undefined : () => handleSelect(link.path)}\n >\n <Icon className=\"mr-2 h-4 w-4 text-muted-foreground\" />\n <span>{link.label}</span>\n {disabled && reason ? (\n <span className=\"ml-auto text-xs text-muted-foreground\">\n {reason}\n </span>\n ) : (\n group.heading && (\n <span className=\"ml-auto text-xs text-muted-foreground\">\n {group.heading}\n </span>\n )\n )}\n </CommandItem>\n )),\n )}\n </CommandGroup>\n );\n };\n\n const renderCommandGroup = (service: ShellModule) => {\n if (!service.commands?.length) return null;\n const { effect, reason } = moduleEffect(service);\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n const Icon = service.icon;\n return (\n <CommandGroup\n key={`cmd-${service.id}`}\n heading={service.commands[0].group ?? service.label}\n >\n {service.commands.map((cmd) => {\n const CmdIcon = cmd.icon ?? Icon;\n return (\n <CommandItem\n key={cmd.id}\n value={`${service.label} ${cmd.label}`}\n disabled={disabled}\n onSelect={\n disabled\n ? undefined\n : () => {\n cmd.action();\n onOpenChange(false);\n }\n }\n >\n <CmdIcon className=\"mr-2 h-4 w-4 text-muted-foreground\" />\n <span>{cmd.label}</span>\n {disabled && reason && (\n <span className=\"ml-auto text-xs text-muted-foreground\">\n {reason}\n </span>\n )}\n </CommandItem>\n );\n })}\n </CommandGroup>\n );\n };\n\n const allModules = [...mainModules, ...bottomModules];\n\n // A module group has visible nav items when: it has navigation links AND its\n // capability effect is not hidden/redirect. Pre-compute to guard separators.\n const hasVisibleNav = (modules: ShellModule[]) =>\n modules.some(\n (m) =>\n m.navigation.length > 0 &&\n ![\"hidden\", \"redirect\"].includes(moduleEffect(m).effect),\n );\n const hasVisibleCmds = allModules.some(\n (m) =>\n (m.commands?.length ?? 0) > 0 &&\n ![\"hidden\", \"redirect\"].includes(moduleEffect(m).effect),\n );\n\n return (\n <CommandDialog open={open} onOpenChange={onOpenChange}>\n <CommandInput placeholder=\"Search pages, services, actions...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n\n {mainModules.map(renderNavGroup)}\n\n {/* Only show the separator when both groups contribute visible items,\n to avoid a dangling separator when all modules in one group are\n capability-gated to hidden. */}\n {hasVisibleNav(mainModules) && hasVisibleNav(bottomModules) && (\n <CommandSeparator />\n )}\n\n {bottomModules.map(renderNavGroup)}\n\n {hasVisibleCmds && (\n <>\n <CommandSeparator />\n {allModules.map(renderCommandGroup)}\n </>\n )}\n </CommandList>\n </CommandDialog>\n );\n}\n","/**\n * ModuleEffects -- runs each module's optional `useEffects` hook.\n *\n * Mounted once by AppShell inside the shell tree so module effects have access\n * to Router / QueryClient / provider context. Lets a module own its reactions\n * to app state (e.g. subscribe to a shell event and invalidate its own caches)\n * instead of centralizing that knowledge in app-level code.\n *\n * Each module gets its own host component so its `useEffects` hook is called\n * unconditionally from a stable component (Rules of Hooks). The module set must\n * be stable for the app's lifetime (it is — modules are declared at load time).\n *\n * A per-host `ModuleEffectBoundary` isolates errors: if one module's hook\n * throws, only that module's effects are lost — the rest of the shell is\n * unaffected.\n */\nimport React from \"react\";\nimport { warnLog } from \"@petrarca/sonnet-core\";\nimport type { ShellModule } from \"./types\";\n\ninterface BoundaryState {\n caught: boolean;\n}\n\n/** Minimal error boundary isolating a single module's useEffects hook. */\nclass ModuleEffectBoundary extends React.Component<\n { moduleId: string; children: React.ReactNode },\n BoundaryState\n> {\n state: BoundaryState = { caught: false };\n\n static getDerivedStateFromError(): BoundaryState {\n return { caught: true };\n }\n\n componentDidCatch(error: unknown) {\n warnLog(\n \"[ModuleEffects] module '{}' useEffects threw -- effects disabled for this module: {}\",\n this.props.moduleId,\n error,\n );\n }\n\n render() {\n return this.state.caught ? null : this.props.children;\n }\n}\n\nfunction ModuleEffectHost({ module }: { module: ShellModule }) {\n // Safe: render only mounts a host for modules that declare useEffects (see\n // ModuleEffects), and the module identity is stable, so this hook is called\n // unconditionally for the component's lifetime.\n module.useEffects!();\n return null;\n}\n\nexport function ModuleEffects({ modules }: { modules: ShellModule[] }) {\n return (\n <>\n {modules\n .filter((m) => typeof m.useEffects === \"function\")\n .map((m) => (\n <ModuleEffectBoundary key={m.id} moduleId={m.id}>\n <ModuleEffectHost module={m} />\n </ModuleEffectBoundary>\n ))}\n </>\n );\n}\n","import React from \"react\";\nimport { Toaster } from \"sonner\";\nimport AppShell from \"./AppShell\";\nimport { ShellConfigProvider } from \"./ShellConfigProvider\";\nimport type { ShellConfig } from \"./shellConfig\";\nimport type { ModuleRegistry } from \"./createModuleRegistry\";\n\n/**\n * RootLayout -- top-level layout that wraps the entire authenticated app.\n * Renders the Toaster for notifications and the AppShell (icon rail + sub-nav + content).\n */\n\ninterface RootLayoutProps {\n config: ShellConfig;\n registry: ModuleRegistry;\n /** Optional custom sidebar replacing the default IconRail + SubNavPanel. */\n sidebar?: React.ReactNode;\n}\n\nexport default function RootLayout({\n config,\n registry,\n sidebar,\n}: RootLayoutProps) {\n return (\n <ShellConfigProvider config={config}>\n <Toaster position=\"top-right\" richColors closeButton />\n <AppShell registry={registry} sidebar={sidebar} />\n </ShellConfigProvider>\n );\n}\n","/**\n * ShellConfigProvider -- wraps the shell with configuration context.\n */\nimport type { ReactNode } from \"react\";\nimport { ShellConfigContext, type ShellConfig } from \"./shellConfig\";\n\ninterface ShellConfigProviderProps {\n config: ShellConfig;\n children: ReactNode;\n}\n\nexport function ShellConfigProvider({\n config,\n children,\n}: ShellConfigProviderProps) {\n return (\n <ShellConfigContext.Provider value={config}>\n {children}\n </ShellConfigContext.Provider>\n );\n}\n","import { useLocation } from \"react-router-dom\";\n\n/**\n * PlaceholderPage — generic wireframe page for routes not yet implemented.\n * Shows the current path and a placeholder content area.\n */\nexport default function PlaceholderPage() {\n const { pathname } = useLocation();\n\n return (\n <div>\n <h1 className=\"mb-1\">{pathToTitle(pathname)}</h1>\n <p className=\"text-sm text-muted-foreground mb-6\">{pathname}</p>\n\n {/* Wireframe content placeholder */}\n <div className=\"rounded-lg border-2 border-dashed border-border p-12 flex items-center justify-center\">\n <span className=\"text-sm text-muted-foreground\">\n Content area — not yet implemented\n </span>\n </div>\n </div>\n );\n}\n\n/** Convert a path like \"/healthcare/patients\" to \"Patients\" */\nfunction pathToTitle(path: string): string {\n const last = path.split(\"/\").filter(Boolean).pop();\n if (!last) return \"Dashboard\";\n return last\n .split(\"-\")\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n}\n","/**\n * Sidebar — single-column navigation layout.\n *\n * Alternative to the IconRail + SubNavPanel two-column pattern.\n * Renders an optional header, a scrollable body with SidebarGroup /\n * SidebarItem children, and takes its full height from the shell.\n */\nimport React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { ScrollArea } from \"@petrarca/sonnet-ui\";\n\nexport interface SidebarProps {\n children: React.ReactNode;\n /** Optional header rendered above the scrollable nav (e.g. app name, collapse button). */\n header?: React.ReactNode;\n width?: number;\n className?: string;\n}\n\nexport function Sidebar({\n children,\n header,\n width = 260,\n className,\n}: SidebarProps): React.ReactElement {\n return (\n <aside\n style={{ width }}\n className={cn(\n \"shrink-0 border-r bg-muted/30 flex flex-col h-full\",\n className,\n )}\n >\n {header}\n <ScrollArea className=\"flex-1\">\n <nav className=\"px-1.5 py-2 space-y-3\">{children}</nav>\n </ScrollArea>\n </aside>\n );\n}\n","/**\n * ShellSidebar — single-column sidebar layout, auto-wired from the\n * module registry.\n *\n * Reads module list from ShellModulesContext (provided by AppShell).\n * For custom layouts, compose Sidebar / SidebarGroup / SidebarItem directly.\n */\nimport React, { useMemo } from \"react\";\nimport { useShellModules } from \"./shellModules\";\nimport { useShellConfig } from \"./shellConfig\";\nimport { evaluateModule, resolveCapabilityPolicy } from \"./capabilities\";\nimport { Sidebar } from \"./Sidebar\";\nimport type { SidebarProps } from \"./Sidebar\";\nimport { SidebarGroup } from \"./SidebarGroup\";\nimport { SidebarItem } from \"./SidebarItem\";\nimport type { ModuleEffect } from \"./capabilities\";\n\nexport type ShellSidebarProps = Omit<SidebarProps, \"children\"> & {\n /** Label for the bottom-pinned modules group. Default: \"Tools\". */\n bottomGroupLabel?: string;\n};\n\nexport function ShellSidebar({\n bottomGroupLabel = \"Tools\",\n ...sidebarProps\n}: ShellSidebarProps): React.ReactElement {\n const { mainModules, bottomModules } = useShellModules();\n const { resolveCapability, capabilityPolicy } = useShellConfig();\n\n const policy = useMemo(\n () => resolveCapabilityPolicy(capabilityPolicy),\n [capabilityPolicy],\n );\n\n // Capability gating uses the shared \"rail\" surface: the sidebar and the icon\n // rail are two renderings of the same primary navigation, so one policy\n // governs both. hidden/redirect drop the module; disabled greys its rows.\n const effectOf = React.useCallback(\n (moduleId: string): ModuleEffect => {\n const module = [...mainModules, ...bottomModules].find(\n (m) => m.id === moduleId,\n );\n if (!module) return { effect: \"visible\" };\n return evaluateModule(module, \"rail\", resolveCapability, policy);\n },\n [mainModules, bottomModules, resolveCapability, policy],\n );\n\n // Collect topNav links from modules that are not hidden, tagging each with\n // its owning module's effect so a disabled module's links render disabled.\n const topNavItems = mainModules.flatMap((mod) => {\n const { effect, reason } = effectOf(mod.id);\n if (effect === \"hidden\" || effect === \"redirect\") return [];\n const disabled = effect === \"disabled\";\n return (mod.topNav ?? []).map((link) => ({\n link,\n icon: mod.icon,\n disabled,\n reason,\n }));\n });\n\n return (\n <Sidebar {...sidebarProps}>\n {/* Top fixed zone — no heading, separator below */}\n {topNavItems.length > 0 && (\n <SidebarGroup separator>\n {topNavItems.map(({ link, icon, disabled, reason }) => (\n <SidebarItem\n key={link.path}\n icon={icon}\n label={link.label}\n path={link.path}\n badge={link.badge}\n disabled={disabled}\n title={disabled ? reason : undefined}\n />\n ))}\n </SidebarGroup>\n )}\n\n {/* Grouped sections */}\n {mainModules.map((mod) => {\n const { effect, reason } = effectOf(mod.id);\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n const title = disabled ? reason : undefined;\n\n if (mod.navigation.length === 0) {\n return (\n <SidebarItem\n key={mod.id}\n icon={mod.icon}\n label={mod.label}\n path={mod.basePath}\n disabled={disabled}\n title={title}\n />\n );\n }\n return (\n <SidebarGroup key={mod.id} heading={mod.label} collapsible>\n {mod.navigation.flatMap((group) => [\n // Render NavGroup heading if it has one\n ...(group.heading\n ? [\n <SidebarGroup\n key={group.id}\n heading={group.heading}\n collapsible={group.collapsible}\n >\n {group.links.map((link) => (\n <SidebarItem\n key={link.path}\n icon={mod.icon}\n label={link.label}\n path={link.path}\n badge={link.badge}\n disabled={disabled}\n title={disabled ? reason : undefined}\n />\n ))}\n </SidebarGroup>,\n ]\n : group.links.map((link) => (\n <SidebarItem\n key={link.path}\n icon={mod.icon}\n label={link.label}\n path={link.path}\n badge={link.badge}\n disabled={disabled}\n title={disabled ? reason : undefined}\n />\n ))),\n ])}\n </SidebarGroup>\n );\n })}\n\n {bottomModules.length > 0 && (\n <SidebarGroup heading={bottomGroupLabel} collapsible>\n {bottomModules.map((mod) => {\n const { effect, reason } = effectOf(mod.id);\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n return (\n <SidebarItem\n key={mod.id}\n icon={mod.icon}\n label={mod.label}\n path={mod.basePath}\n disabled={disabled}\n title={disabled ? reason : undefined}\n />\n );\n })}\n </SidebarGroup>\n )}\n </Sidebar>\n );\n}\n","/**\n * ShellVersion — displays the app name and optional version in the footer.\n *\n * Typically used as ShellConfig.footer content alongside ShellFooter.\n * The consumer provides name and version from their own package.json.\n */\nimport React from \"react\";\n\nexport interface ShellVersionProps {\n /** Application name. */\n name: string;\n /** Version string (e.g. from package.json). Omit to show name only. */\n version?: string;\n}\n\nexport function ShellVersion({\n name,\n version,\n}: ShellVersionProps): React.ReactElement {\n return (\n <div className=\"flex items-center gap-2\">\n <span>{name}</span>\n {version && <span className=\"text-muted-foreground/50\">v{version}</span>}\n </div>\n );\n}\n","import { Search } from \"lucide-react\";\nimport { Button } from \"@petrarca/sonnet-ui\";\nimport { navigation } from \"./api\";\n\n/**\n * SearchTrigger -- Cmd+K search button for the TopBar.\n *\n * Opens the shell CommandMenu via the imperative navigation API.\n * Generic building block, no app-specific dependencies.\n */\nexport function SearchTrigger() {\n return (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-8 gap-2 text-sm text-muted-foreground w-56 justify-start\"\n onClick={() => navigation.openCommandMenu()}\n >\n <Search className=\"h-4 w-4\" />\n <span>Search...</span>\n <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\">\n <span className=\"text-xs\">&#x2318;</span>K\n </kbd>\n </Button>\n );\n}\n","import { LogOut, User } from \"lucide-react\";\nimport { Button } from \"@petrarca/sonnet-ui\";\nimport { Avatar, AvatarFallback } from \"@petrarca/sonnet-ui\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@petrarca/sonnet-ui\";\n\n/**\n * UserMenu -- avatar dropdown for the TopBar.\n *\n * Shows the user's initials in an avatar. The dropdown displays name,\n * email, a profile link, and a sign-out action. Generic building block,\n * no app-specific dependencies.\n */\n\nexport interface UserMenuUser {\n name: string;\n email: string;\n initials: string;\n}\n\ninterface UserMenuProps {\n user: UserMenuUser;\n onSignOut?: () => void;\n}\n\nexport function UserMenu({ user, onSignOut }: UserMenuProps) {\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"compact\" className=\"rounded-full\">\n <Avatar className=\"h-7 w-7\">\n <AvatarFallback className=\"text-xs\">{user.initials}</AvatarFallback>\n </Avatar>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-48\">\n <DropdownMenuLabel className=\"font-normal\">\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-sm font-medium\">{user.name}</p>\n <p className=\"text-xs text-muted-foreground\">{user.email}</p>\n </div>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem>\n <User className=\"mr-2 h-4 w-4\" />\n Profile\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={onSignOut}>\n <LogOut className=\"mr-2 h-4 w-4\" />\n Sign out\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","/**\n * Module registry factory.\n *\n * Accepts a list of ShellModules and returns derived data structures\n * consumed by the shell (icon rail groups, flattened routes, extension\n * point lookup). This is the reusable core; the app-specific module\n * list lives in the consumer (e.g. modules/registry.ts).\n */\nimport type { RouteObject } from \"react-router-dom\";\nimport type { ShellModule, Contribution } from \"./types\";\n\nexport interface ModuleRegistry {\n /** All modules in registration order. */\n modules: ShellModule[];\n\n /** Visible modules shown in the main area of the icon rail. */\n mainModules: ShellModule[];\n\n /** Modules pinned to the bottom of the icon rail. */\n bottomModules: ShellModule[];\n\n /** All routes from all modules, flattened. */\n allRoutes: RouteObject[];\n\n /** Get contributions for a named extension point, sorted by order. */\n getExtensionPoint: (name: string) => Contribution[];\n}\n\nexport function createModuleRegistry(modules: ShellModule[]): ModuleRegistry {\n const visible = modules.filter((m) => !m.hidden);\n const mainModules = visible.filter((m) => !m.pinBottom);\n const bottomModules = visible.filter((m) => m.pinBottom);\n const allRoutes = modules.flatMap((m) => m.routes);\n\n // Pre-collect all contributions for O(n) lookup per call.\n const contributionsByPoint = new Map<string, Contribution[]>();\n for (const m of modules) {\n for (const c of m.contributions ?? []) {\n const list = contributionsByPoint.get(c.extensionPoint) ?? [];\n list.push(c);\n contributionsByPoint.set(c.extensionPoint, list);\n }\n }\n // Sort each group once.\n for (const list of contributionsByPoint.values()) {\n list.sort((a, b) => a.order - b.order);\n }\n\n function getExtensionPoint(name: string): Contribution[] {\n return contributionsByPoint.get(name) ?? [];\n }\n\n return { modules, mainModules, bottomModules, allRoutes, getExtensionPoint };\n}\n","/**\n * OverviewCard and FeatureLink -- building blocks for module overview pages.\n *\n * OverviewCard presents a feature with icon, title, description, and optional\n * navigation via a stable feature id.\n *\n * FeatureLink is an inline text link for use in descriptions or prose,\n * also navigating via stable feature id.\n *\n * Part of the shell -- extracts to @petrarca/sonnet-shell.\n */\n\nimport React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { navigation } from \"./api\";\n\ninterface OverviewCardProps {\n /** Lucide icon component. */\n icon: React.ComponentType<{ className?: string }>;\n /** Card title. */\n title: string;\n /** Description -- plain string or React nodes (e.g. with FeatureLink). */\n description: React.ReactNode;\n /**\n * Stable feature identifier (e.g. \"auditing.audit-events\").\n * Resolved to a path via the shell registry at click time.\n * Omit for non-navigable cards.\n */\n feature?: string;\n /** Additional CSS class names. */\n className?: string;\n}\n\n/**\n * FeatureLink -- inline text link navigating to a feature by stable id.\n *\n * Use inside OverviewCard descriptions or overview page prose to link\n * to a feature without hardcoding paths.\n *\n * @example\n * <FeatureLink feature=\"auditing.audit-log\">Audit Log</FeatureLink>\n */\nexport function FeatureLink({\n feature,\n children,\n}: {\n feature: string;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <span\n role=\"link\"\n tabIndex={0}\n onClick={(e) => {\n e.stopPropagation();\n navigation.goToFeature(feature);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n e.stopPropagation();\n navigation.goToFeature(feature);\n }\n }}\n className=\"underline underline-offset-2 hover:text-foreground transition-colors cursor-pointer\"\n >\n {children}\n </span>\n );\n}\n\n/** Feature card for module overview pages. Navigates via the shell API. */\nexport function OverviewCard({\n icon: Icon,\n title,\n description,\n feature,\n className,\n}: OverviewCardProps): React.ReactElement {\n return (\n <button\n onClick={feature ? () => navigation.goToFeature(feature) : undefined}\n disabled={!feature}\n className={cn(\n \"rounded-lg border bg-card p-5 text-left flex flex-col gap-3\",\n \"disabled:cursor-default\",\n feature && \"hover:bg-accent/50 transition-colors\",\n className,\n )}\n >\n <div className=\"h-9 w-9 rounded-md bg-blue-50 flex items-center justify-center shrink-0\">\n <Icon className=\"h-5 w-5 text-blue-600\" />\n </div>\n <div>\n <p className=\"text-sm font-medium mb-1\">{title}</p>\n <p className=\"text-xs text-muted-foreground leading-relaxed\">\n {description}\n </p>\n </div>\n </button>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","useRef","useMemo","useResizablePanel","useLocation","useNavigate","ScrollArea","jsx","jsx","useMemo","Button","jsx","jsxs","useMemo","Tooltip","TooltipContent","TooltipTrigger","warnLog","warnLog","cn","jsx","jsxs","cn","jsx","jsxs","jsx","jsxs","useMemo","Tooltip","TooltipTrigger","TooltipContent","createContext","useContext","createContext","useContext","warnLog","jsx","jsxs","useMemo","cn","createContext","useContext","cn","jsx","jsx","jsxs","cn","useEffect","useMemo","Fragment","jsx","jsxs","useEffect","useMemo","React","warnLog","Fragment","jsx","Fragment","jsx","jsxs","useRef","useEffect","useLocation","useMemo","useCallback","useState","useSidePaneState","useNavigate","sidePane","panel","dialog","useResizablePanel","ScrollArea","jsx","jsx","jsxs","useLocation","jsx","jsxs","cn","ScrollArea","jsx","jsxs","React","useMemo","jsx","jsxs","useMemo","React","jsx","jsxs","Button","jsx","jsxs","Button","Button","jsx","jsxs","cn","jsx","jsxs","cn"]}
1
+ {"version":3,"sources":["../src/AppShell.tsx","../src/ConfirmDialog.tsx","../src/TopBar.tsx","../src/ShellFooter.tsx","../src/ShellRail.tsx","../src/IconRail.tsx","../src/SubNavPanel.tsx","../src/hooks.ts","../src/api.ts","../src/eventBus.ts","../src/shellModules.ts","../src/SidebarGroup.tsx","../src/SidebarItem.tsx","../src/shellNavigation.ts","../src/shellConfig.ts","../src/capabilities.ts","../src/SidePane.tsx","../src/sidePaneState.ts","../src/DragHandle.tsx","../src/CommandMenu.tsx","../src/ModuleEffects.tsx","../src/RootLayout.tsx","../src/ShellConfigProvider.tsx","../src/PlaceholderPage.tsx","../src/Sidebar.tsx","../src/ShellSidebar.tsx","../src/ShellVersion.tsx","../src/SearchTrigger.tsx","../src/UserMenu.tsx","../src/createModuleRegistry.ts","../src/OverviewCard.tsx"],"sourcesContent":["import React, {\n useState,\n useEffect,\n useCallback,\n useRef,\n useMemo,\n} from \"react\";\nimport { useResizablePanel, useMediaQuery } from \"@petrarca/sonnet-core/hooks\";\nimport { Outlet, useLocation, useNavigate } from \"react-router-dom\";\nimport { TooltipProvider } from \"@petrarca/sonnet-ui\";\nimport { ScrollArea } from \"@petrarca/sonnet-ui\";\nimport {\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n SheetBody,\n} from \"@petrarca/sonnet-ui\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\nimport TopBar from \"./TopBar\";\nimport ShellFooter from \"./ShellFooter\";\nimport { ShellRail } from \"./ShellRail\";\nimport SidePane from \"./SidePane\";\nimport { DragHandle } from \"./DragHandle\";\nimport CommandMenu from \"./CommandMenu\";\nimport { ModuleEffects } from \"./ModuleEffects\";\nimport {\n initNavigation,\n initFeatureNav,\n initPanel,\n initDialog,\n initFullscreen,\n initSidePane,\n type PanelOptions,\n type ConfirmOptions,\n type FullscreenOptions,\n} from \"./api\";\nimport type { ModuleRegistry } from \"./createModuleRegistry\";\nimport { ShellModulesContext } from \"./shellModules\";\nimport { useShellConfig } from \"./shellConfig\";\nimport { SidePaneContext, type SidePaneOptions } from \"./sidePaneState\";\nimport { ShellNavigationContext } from \"./shellNavigation\";\nimport type { ShellModule } from \"./types\";\n\n// Single source of truth for panel width tokens.\n// px: pixel value used as defaultWidth for resizable mode (viewport-relative\n// tokens like \"half\" / \"full\" are not resizable -- resizable falls back to 640).\nconst PANEL_WIDTHS: Record<string, { css: string; px: number }> = {\n narrow: { css: \"w-[480px]\", px: 480 },\n default: { css: \"w-[640px]\", px: 640 },\n wide: { css: \"w-[800px]\", px: 800 },\n half: { css: \"w-[50vw]\", px: 640 },\n full: { css: \"w-screen\", px: 640 },\n // legacy aliases\n lg: { css: \"w-[640px]\", px: 640 },\n \"1/3\": { css: \"w-[33vw]\", px: 640 },\n \"1/2\": { css: \"w-[50vw]\", px: 640 },\n};\n\nconst DEFAULT_PANEL = PANEL_WIDTHS.default;\n\n// -- Helpers extracted to reduce AppShell complexity --\n\nfunction isSidePaneModule(service: {\n sidePane?: unknown;\n navigation: unknown[];\n}): boolean {\n return !!service.sidePane && service.navigation.length === 0;\n}\n\nfunction selectSidePaneModule(\n service: {\n id: string;\n basePath: string;\n routes: unknown[];\n sidePane?: {\n defaultWidth?: number;\n minWidth?: number;\n maxWidth?: number;\n contentFactory?: () => React.ReactNode;\n };\n },\n current: SidePaneOptions | null,\n setSidePaneState: (s: SidePaneOptions | null) => void,\n navigate: (path: string) => void,\n): void {\n if (current?.moduleId === service.id) {\n setSidePaneState(null);\n } else {\n setSidePaneState({\n moduleId: service.id,\n content: service.sidePane?.contentFactory?.() ?? null,\n defaultWidth: service.sidePane?.defaultWidth,\n minWidth: service.sidePane?.minWidth,\n maxWidth: service.sidePane?.maxWidth,\n });\n // Only navigate if the module has routes — pane-only modules\n // (routes: []) have no registered path, navigating would 404.\n if (service.routes.length > 0) {\n navigate(service.basePath);\n }\n }\n}\n\nfunction selectNavModule(\n service: {\n basePath: string;\n navigation: Array<{ links: Array<{ path: string }> }>;\n },\n navigate: (path: string) => void,\n setSubNavCollapsed: (v: boolean) => void,\n closeSidePane: () => void,\n): void {\n closeSidePane();\n setSubNavCollapsed(false);\n const target = service.navigation[0]?.links[0]?.path ?? service.basePath;\n navigate(target);\n}\n\n// Wire all imperative shell APIs once on mount.\nfunction useShellApiInit(deps: {\n navigate: ReturnType<typeof useNavigate>;\n openCommandMenu: () => void;\n handlePanelOpen: (opts: PanelOptions) => void;\n handlePanelClose: () => void;\n handleFullscreenEnter: (opts: FullscreenOptions) => void;\n handleFullscreenExit: () => void;\n handleSidePaneOpen: (opts: SidePaneOptions) => void;\n handleSidePaneClose: () => void;\n handleSidePaneIsOpen: () => boolean;\n handleSidePaneActiveModuleId: () => string | null;\n handleSidePaneSetFullWidth: (fullWidth: boolean) => void;\n setDialogState: React.Dispatch<React.SetStateAction<DialogState | null>>;\n featureLookup: (featureId: string) => string | null;\n}) {\n // The imperative API is registered once, but several of these callbacks\n // change identity as state changes: handleSidePaneIsOpen and\n // handleSidePaneActiveModuleId track sidePaneState, and featureLookup tracks\n // the module registry. Registering them directly would freeze the mount-time\n // closures -- isOpen() would answer for an empty pane forever -- so the API\n // calls through this ref, which is refreshed after every render.\n const latest = useRef(deps);\n useEffect(() => {\n latest.current = deps;\n });\n\n useEffect(() => {\n initNavigation(\n (path) => latest.current.navigate(path),\n () => latest.current.openCommandMenu(),\n );\n initFeatureNav((featureId) => latest.current.featureLookup(featureId));\n initPanel(\n (opts) => latest.current.handlePanelOpen(opts),\n () => latest.current.handlePanelClose(),\n );\n initFullscreen(\n (opts) => latest.current.handleFullscreenEnter(opts),\n () => latest.current.handleFullscreenExit(),\n );\n initSidePane(\n (opts) => latest.current.handleSidePaneOpen(opts),\n () => latest.current.handleSidePaneClose(),\n () => latest.current.handleSidePaneIsOpen(),\n () => latest.current.handleSidePaneActiveModuleId(),\n (fullWidth) => latest.current.handleSidePaneSetFullWidth(fullWidth),\n );\n initDialog(\n (opts: ConfirmOptions) =>\n new Promise<boolean>((resolve) =>\n latest.current.setDialogState({ opts, resolve }),\n ),\n );\n }, []);\n}\n\ninterface AppShellProps {\n registry: ModuleRegistry;\n /**\n * Optional custom sidebar replacing the default IconRail + SubNavPanel.\n * When provided, the shell renders this element in place of the two-column\n * navigation chrome. Use the Sidebar / SidebarGroup / SidebarItem components\n * from this package to build a single-column navigation layout.\n */\n sidebar?: React.ReactNode;\n}\n\ninterface DialogState {\n opts: ConfirmOptions;\n resolve: (confirmed: boolean) => void;\n}\n\n/**\n * AppShell -- the main layout skeleton.\n *\n * Owns the panel (slide-over) -- modules request it via panel.open(),\n * the shell renders the provided content inside a Sheet.\n *\n * Receives its module registry as a prop so it has no dependency on\n * app-specific module lists.\n */\n// Hook: track which service is active based on URL.\nfunction useActiveService(modules: ModuleRegistry[\"modules\"]) {\n const location = useLocation();\n // Build [prefix, moduleId] pairs from each module's basePath plus any\n // matchPaths. Sorted by descending prefix length so a more specific prefix\n // (e.g. \"/org-units\") wins over a shorter one (e.g. \"/org\") regardless of\n // module registration order.\n const prefixToService = useMemo<Array<[string, string]>>(() => {\n const pairs: Array<[string, string]> = [];\n for (const m of modules) {\n if (m.hidden) continue;\n const prefixes = [m.basePath, ...(m.matchPaths ?? [])];\n for (const prefix of prefixes) {\n if (prefix !== \"/\") pairs.push([prefix, m.id]);\n }\n }\n pairs.sort((a, b) => b[0].length - a[0].length);\n return pairs;\n }, [modules]);\n const resolveServiceFromPath = useCallback(\n (pathname: string): string | null => {\n for (const [prefix, id] of prefixToService) {\n if (pathname.startsWith(prefix)) return id;\n }\n return null;\n },\n [prefixToService],\n );\n const [selectedServiceId, setSelectedServiceId] = useState<string | null>(\n () => resolveServiceFromPath(location.pathname),\n );\n useEffect(\n () => setSelectedServiceId(resolveServiceFromPath(location.pathname)),\n [location.pathname, resolveServiceFromPath],\n );\n const activeService = selectedServiceId\n ? (modules.find((s) => s.id === selectedServiceId) ?? null)\n : null;\n return { activeService, selectedServiceId, setSelectedServiceId };\n}\n\n// Hook: side pane state and handlers.\nfunction useSidePaneState() {\n const [sidePaneState, setSidePaneState] = useState<SidePaneOptions | null>(\n null,\n );\n const handleOpen = useCallback(\n (opts: SidePaneOptions) => setSidePaneState(opts),\n [],\n );\n const handleClose = useCallback(() => setSidePaneState(null), []);\n const handleIsOpen = useCallback(\n () => sidePaneState !== null,\n [sidePaneState],\n );\n const handleActiveModuleId = useCallback(\n () => sidePaneState?.moduleId ?? null,\n [sidePaneState],\n );\n const handleSetFullWidth = useCallback((fullWidth: boolean) => {\n setSidePaneState((prev) => (prev ? { ...prev, fullWidth } : prev));\n }, []);\n // Functional update so the callback stays stable and never closes over a\n // stale sidePaneState.\n const handleToggle = useCallback((opts: SidePaneOptions) => {\n setSidePaneState((prev) =>\n prev?.moduleId === opts.moduleId ? null : opts,\n );\n }, []);\n return {\n sidePaneState,\n setSidePaneState,\n handleOpen,\n handleClose,\n handleIsOpen,\n handleActiveModuleId,\n handleSetFullWidth,\n handleToggle,\n };\n}\n\n/** Resolved resize options passed as a single object to SlideOverPanel. */\ninterface PanelResizeOptions {\n enabled: boolean;\n defaultWidth: number;\n minWidth: number;\n maxWidth: number;\n}\n\nconst DEFAULT_RESIZE_MIN = 240;\nconst DEFAULT_RESIZE_MAX = 1200;\n\n// Derive resize options from panel state. Extracted to keep usePanelState lean.\nfunction resizeOptionsFromPanel(\n state: PanelOptions | null,\n widthEntry: { px: number },\n): PanelResizeOptions {\n if (!state?.resizable) {\n return {\n enabled: false,\n defaultWidth: widthEntry.px,\n minWidth: DEFAULT_RESIZE_MIN,\n maxWidth: DEFAULT_RESIZE_MAX,\n };\n }\n const viewportMax =\n typeof window !== \"undefined\"\n ? Math.round(window.innerWidth * 0.9)\n : DEFAULT_RESIZE_MAX;\n return {\n enabled: true,\n defaultWidth: widthEntry.px,\n minWidth: state.minWidth ?? DEFAULT_RESIZE_MIN,\n maxWidth: state.maxWidth ?? viewportMax,\n };\n}\n\n// Hook: slide-over panel state and handlers.\nfunction usePanelState() {\n const [panelState, setPanelState] = useState<PanelOptions | null>(null);\n const panelOnCloseRef = useRef<(() => void) | undefined>(undefined);\n const handleOpen = useCallback(\n (opts: PanelOptions) => setPanelState(opts),\n [],\n );\n const handleClose = useCallback(() => {\n panelOnCloseRef.current = panelState?.onClose;\n setPanelState(null);\n }, [panelState]);\n\n const widthEntry =\n PANEL_WIDTHS[panelState?.width ?? \"default\"] ?? DEFAULT_PANEL;\n const panelWidth = widthEntry.css;\n const panelOffsetTop = panelState?.coverage === \"full\" ? \"0px\" : \"3rem\";\n\n return {\n panelState,\n panelOnCloseRef,\n handleOpen,\n handleClose,\n panelWidth,\n panelOffsetTop,\n resize: resizeOptionsFromPanel(panelState, widthEntry),\n };\n}\n\n// Hook: confirm dialog state and handlers.\nfunction useDialogState() {\n const [dialogState, setDialogState] = useState<DialogState | null>(null);\n const handleConfirm = useCallback(() => {\n dialogState?.resolve(true);\n setDialogState(null);\n }, [dialogState]);\n const handleCancel = useCallback(() => {\n dialogState?.resolve(false);\n setDialogState(null);\n }, [dialogState]);\n return { dialogState, setDialogState, handleConfirm, handleCancel };\n}\n\n// Build a stable feature id -> path lookup from the module registry.\nfunction useFeatureLookup(modules: ModuleRegistry[\"modules\"]) {\n return useMemo(() => {\n const map = new Map<string, string>();\n for (const m of modules) {\n for (const group of m.navigation) {\n for (const link of group.links) {\n if (link.id) map.set(link.id, link.path);\n }\n }\n }\n return (featureId: string) => map.get(featureId) ?? null;\n }, [modules]);\n}\n\nexport default function AppShell({ registry, sidebar }: AppShellProps) {\n const navigate = useNavigate();\n const config = useShellConfig();\n const { modules } = registry;\n\n const { activeService, setSelectedServiceId } = useActiveService(modules);\n const [subNavCollapsed, setSubNavCollapsed] = useState(false);\n // Below the tablet breakpoint the secondary sidebar auto-collapses to a thin\n // strip so the content area keeps a usable width. The user's manual collapse\n // preference is preserved and re-applied once the viewport widens again.\n const isBelowTablet = useMediaQuery(\"(max-width: 1023px)\");\n const [commandMenuOpen, setCommandMenuOpen] = useState(false);\n const openCommandMenu = useCallback(() => setCommandMenuOpen(true), []);\n const [fullscreenState, setFullscreenState] =\n useState<FullscreenOptions | null>(null);\n const handleFullscreenEnter = useCallback(\n (opts: FullscreenOptions) => setFullscreenState(opts),\n [],\n );\n const handleFullscreenExit = useCallback(() => setFullscreenState(null), []);\n\n const sidePane = useSidePaneState();\n const panel = usePanelState();\n const dialog = useDialogState();\n const featureLookup = useFeatureLookup(modules);\n\n useShellApiInit({\n navigate,\n openCommandMenu,\n handlePanelOpen: panel.handleOpen,\n handlePanelClose: panel.handleClose,\n handleFullscreenEnter,\n handleFullscreenExit,\n handleSidePaneOpen: sidePane.handleOpen,\n handleSidePaneClose: sidePane.handleClose,\n handleSidePaneIsOpen: sidePane.handleIsOpen,\n handleSidePaneActiveModuleId: sidePane.handleActiveModuleId,\n handleSidePaneSetFullWidth: sidePane.handleSetFullWidth,\n setDialogState: dialog.setDialogState,\n featureLookup,\n });\n\n const handleServiceSelect = useCallback(\n (serviceId: string) => {\n const service = modules.find((s) => s.id === serviceId);\n if (!service) return;\n if (isSidePaneModule(service)) {\n selectSidePaneModule(\n service,\n sidePane.sidePaneState,\n sidePane.setSidePaneState,\n navigate,\n );\n // Don't change activeService for pane-only modules — the content\n // area still shows the previous route and needs its layout mode.\n // Collapse SubNavPanel by default so it doesn't linger from a nav module.\n // Consumers can opt out via sidePane.collapseSubNav: false.\n if (service.sidePane?.collapseSubNav !== false) {\n setSubNavCollapsed(true);\n }\n } else {\n selectNavModule(\n service,\n navigate,\n setSubNavCollapsed,\n sidePane.handleClose,\n );\n setSelectedServiceId(serviceId);\n }\n },\n [\n modules,\n sidePane.sidePaneState,\n sidePane.setSidePaneState,\n sidePane.handleClose,\n navigate,\n setSelectedServiceId,\n setSubNavCollapsed,\n ],\n );\n\n // Depends on the individual members, not the hook's return object -- that is\n // a fresh literal every render and would defeat the memo entirely.\n const sidePaneContextValue = useMemo(\n () => ({\n pane: sidePane.sidePaneState,\n open: sidePane.handleOpen,\n close: sidePane.handleClose,\n toggle: sidePane.handleToggle,\n setFullWidth: sidePane.handleSetFullWidth,\n }),\n [\n sidePane.sidePaneState,\n sidePane.handleOpen,\n sidePane.handleClose,\n sidePane.handleToggle,\n sidePane.handleSetFullWidth,\n ],\n );\n\n const navigationValue = {\n activeService,\n sidePaneModuleId: sidePane.sidePaneState?.moduleId ?? null,\n subNavCollapsed: subNavCollapsed || isBelowTablet,\n onToggleSubNav: () => setSubNavCollapsed((c) => !c),\n onServiceSelect: handleServiceSelect,\n };\n\n return (\n <ShellModulesContext.Provider value={registry}>\n <SidePaneContext.Provider value={sidePaneContextValue}>\n <ShellNavigationContext.Provider value={navigationValue}>\n <TooltipProvider>\n {/* Module-owned effects (event subscriptions, cache invalidation) */}\n <ModuleEffects modules={modules} />\n <div className=\"flex h-screen w-screen flex-col overflow-hidden\">\n {/* TopBar */}\n <TopBar>{config.topBar}</TopBar>\n\n {/* CommandMenu (Cmd+K) */}\n <CommandMenu\n open={commandMenuOpen}\n onOpenChange={setCommandMenuOpen}\n />\n\n {/* Body: sidebar (or ShellRail) + SidePane + Content Area */}\n <ShellBody\n activeService={activeService}\n sidePaneFullWidth={sidePane.sidePaneState?.fullWidth}\n sidebar={sidebar}\n />\n\n {/* Footer */}\n {config.footer && <ShellFooter>{config.footer}</ShellFooter>}\n\n {/* Confirm Dialog */}\n {dialog.dialogState && (\n <ConfirmDialog\n open={true}\n title={dialog.dialogState.opts.title}\n description={dialog.dialogState.opts.description}\n confirmLabel={dialog.dialogState.opts.confirmLabel}\n cancelLabel={dialog.dialogState.opts.cancelLabel}\n variant={dialog.dialogState.opts.variant}\n onConfirm={dialog.handleConfirm}\n onCancel={dialog.handleCancel}\n />\n )}\n\n {/* Fullscreen overlay -- covers all shell chrome */}\n {fullscreenState && (\n <div className=\"fixed inset-0 z-50 bg-background overflow-auto\">\n {fullscreenState.content}\n </div>\n )}\n\n {/* Slide-over Panel */}\n <SlideOverPanel\n panelState={panel.panelState}\n panelWidth={panel.panelWidth}\n panelOffsetTop={panel.panelOffsetTop}\n panelOnCloseRef={panel.panelOnCloseRef}\n onClose={panel.handleClose}\n resize={panel.resize}\n />\n </div>\n </TooltipProvider>\n </ShellNavigationContext.Provider>\n </SidePaneContext.Provider>\n </ShellModulesContext.Provider>\n );\n}\n\n// -- Sub-components extracted to reduce AppShell complexity --\n\ninterface SlideOverPanelProps {\n panelState: PanelOptions | null;\n panelWidth: string;\n panelOffsetTop: string;\n panelOnCloseRef: React.MutableRefObject<(() => void) | undefined>;\n onClose: () => void;\n resize: PanelResizeOptions;\n}\n\nfunction SlideOverPanel({\n panelState,\n panelWidth,\n panelOffsetTop,\n panelOnCloseRef,\n onClose,\n resize,\n}: SlideOverPanelProps) {\n const sheetProps = {\n side: \"right\" as const,\n offsetTop: panelOffsetTop,\n onCloseAutoFocus: (e: Event) => {\n if (panelOnCloseRef.current) {\n e.preventDefault();\n panelOnCloseRef.current();\n }\n },\n ...(!panelState?.description && { \"aria-describedby\": undefined }),\n };\n\n return (\n <Sheet\n open={panelState !== null}\n onOpenChange={(open) => {\n if (!open) onClose();\n }}\n >\n {resize.enabled ? (\n <ResizableSheetContent {...sheetProps} resize={resize}>\n {panelState && <PanelContent state={panelState} />}\n </ResizableSheetContent>\n ) : (\n <SheetContent {...sheetProps} className={`${panelWidth} max-w-[90vw]`}>\n {panelState && <PanelContent state={panelState} />}\n </SheetContent>\n )}\n </Sheet>\n );\n}\n\n/**\n * Wrapper that mounts useResizablePanel only when resize is enabled.\n * Hooks must be called unconditionally — splitting into a separate component\n * is the idiomatic way to avoid calling hooks behind a conditional.\n */\nfunction ResizableSheetContent({\n resize,\n children,\n ...sheetProps\n}: {\n resize: PanelResizeOptions;\n children: React.ReactNode;\n} & React.ComponentPropsWithoutRef<typeof SheetContent>) {\n const {\n panelWidth: dynWidth,\n handlePointerDown,\n handleDoubleClick,\n separatorProps,\n } = useResizablePanel({\n defaultWidth: resize.defaultWidth,\n minWidth: resize.minWidth,\n maxWidth: resize.maxWidth,\n direction: \"left-edge\",\n });\n\n return (\n <SheetContent\n {...sheetProps}\n className=\"max-w-[90vw]\"\n style={{ width: dynWidth }}\n >\n <DragHandle\n edge=\"left\"\n separatorProps={separatorProps}\n onPointerDown={handlePointerDown}\n onDoubleClick={handleDoubleClick}\n />\n {children}\n </SheetContent>\n );\n}\n\ninterface ShellBodyProps {\n activeService: ShellModule | null;\n sidePaneFullWidth?: boolean;\n sidebar?: React.ReactNode;\n}\n\nfunction ShellBody({\n activeService,\n sidePaneFullWidth,\n sidebar,\n}: ShellBodyProps) {\n return (\n <div className=\"flex flex-1 overflow-hidden\">\n {sidebar ?? <ShellRail />}\n\n <SidePane />\n\n {sidePaneFullWidth !== true && (\n <div className=\"flex-1 min-w-0 h-full overflow-hidden\">\n <ContentArea layout={activeService?.layout} />\n </div>\n )}\n </div>\n );\n}\n\nfunction ContentArea({ layout }: { layout?: \"default\" | \"full\" }) {\n if (layout === \"full\") {\n return (\n <div className=\"h-full w-full overflow-hidden\">\n <Outlet />\n </div>\n );\n }\n return (\n <ScrollArea className=\"h-full\">\n <main className=\"p-6\">\n <Outlet />\n </main>\n </ScrollArea>\n );\n}\n\nfunction PanelContent({ state }: { state: PanelOptions }) {\n return (\n <>\n {state.title ? (\n <SheetHeader>\n <SheetTitle>{state.title}</SheetTitle>\n {state.description && (\n <SheetDescription>{state.description}</SheetDescription>\n )}\n </SheetHeader>\n ) : (\n <SheetTitle className=\"sr-only\">Panel</SheetTitle>\n )}\n <SheetBody>{state.content}</SheetBody>\n </>\n );\n}\n","/**\n * Confirm Dialog\n *\n * Reusable confirmation dialog for actions that require user confirmation.\n * Used for discard changes, delete actions, etc.\n *\n * Wired into the shell via `initDialog` in `shell/api.ts` so modules can\n * trigger it imperatively with `dialog.confirm(opts)`.\n *\n * @module components/Common/ConfirmDialog\n */\n\nimport React from \"react\";\nimport { AlertTriangle } from \"lucide-react\";\nimport { Button } from \"@petrarca/sonnet-ui\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@petrarca/sonnet-ui\";\n\nexport interface ConfirmDialogProps {\n /** Whether the dialog is open */\n open: boolean;\n\n /** Dialog title */\n title: string;\n\n /** Description shown below the title */\n description?: string;\n\n /** Confirm button label (default: \"Confirm\") */\n confirmLabel?: string;\n\n /** Cancel button label (default: \"Cancel\") */\n cancelLabel?: string;\n\n /**\n * Controls confirm button appearance.\n * \"destructive\" for irreversible actions (delete, revoke, etc.)\n */\n variant?: \"default\" | \"destructive\";\n\n /** Callback when user confirms */\n onConfirm: () => void;\n\n /** Callback when user cancels or closes dialog */\n onCancel: () => void;\n}\n\n/**\n * ConfirmDialog Component\n *\n * A reusable confirmation dialog backed by shadcn Dialog primitives.\n */\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n variant = \"default\",\n onConfirm,\n onCancel,\n}: ConfirmDialogProps): React.ReactElement {\n return (\n <Dialog open={open} onOpenChange={(isOpen) => !isOpen && onCancel()}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n {description && <DialogDescription>{description}</DialogDescription>}\n </DialogHeader>\n\n {variant === \"destructive\" && (\n <div className=\"flex gap-2 items-start\">\n <AlertTriangle\n size={20}\n className=\"text-destructive flex-shrink-0 mt-0.5\"\n />\n <p className=\"flex-1 text-sm text-muted-foreground\">\n This action cannot be undone.\n </p>\n </div>\n )}\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={onCancel}>\n {cancelLabel}\n </Button>\n <Button variant={variant} onClick={onConfirm}>\n {confirmLabel}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import type { ReactNode } from \"react\";\n\n/**\n * TopBar -- persistent header across the entire application.\n *\n * A pure layout container. The shell provides the `<header>` element with\n * its sizing, background, and border. The consumer owns all content via\n * the `children` prop (composed through `ShellConfig.topBar`).\n */\ninterface TopBarProps {\n children?: ReactNode;\n}\n\nexport default function TopBar({ children }: TopBarProps) {\n return (\n <header className=\"h-12 shrink-0 border-b border-border flex items-center justify-between px-3 bg-background z-20\">\n {children}\n </header>\n );\n}\n","/**\n * ShellFooter — persistent footer bar across the entire application.\n *\n * A pure layout container, mirroring TopBar. The shell provides the\n * `<footer>` element with its sizing, background, and border. The\n * consumer owns all content via the `children` prop (composed through\n * `ShellConfig.footer`).\n */\nimport type { ReactNode } from \"react\";\n\nexport interface ShellFooterProps {\n children?: ReactNode;\n}\n\nexport default function ShellFooter({ children }: ShellFooterProps) {\n return (\n <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\">\n {children}\n </footer>\n );\n}\n","/**\n * ShellRail — icon-rail + sub-nav-panel layout, auto-wired from the\n * module registry and shell navigation context.\n *\n * Reads active service state from ShellNavigationContext (provided by\n * AppShell) and module list from ShellModulesContext. No props required.\n *\n * For custom layouts, compose IconRail, RailIcon, RailSeparator, and\n * SubNavPanel directly.\n */\nimport React, { useMemo } from \"react\";\nimport { IconRail, RailIcon, RailSeparator } from \"./IconRail\";\nimport { SubNavPanel } from \"./SubNavPanel\";\nimport { useShellModules } from \"./shellModules\";\nimport { useShellNavigation } from \"./shellNavigation\";\nimport { useShellConfig } from \"./shellConfig\";\nimport { evaluateModule, resolveCapabilityPolicy } from \"./capabilities\";\nimport type { ShellModule } from \"./types\";\n\nexport function ShellRail(): React.ReactElement {\n const { mainModules, bottomModules } = useShellModules();\n const { resolveCapability, capabilityPolicy } = useShellConfig();\n const {\n activeService,\n sidePaneModuleId,\n subNavCollapsed,\n onToggleSubNav,\n onServiceSelect,\n } = useShellNavigation();\n\n const isActive = (id: string) =>\n activeService?.id === id || sidePaneModuleId === id;\n\n const policy = useMemo(\n () => resolveCapabilityPolicy(capabilityPolicy),\n [capabilityPolicy],\n );\n\n const renderModule = (service: ShellModule): React.ReactElement | null => {\n const { effect, reason } = evaluateModule(\n service,\n \"rail\",\n resolveCapability,\n policy,\n );\n // \"redirect\" has no rail rendering — treat as hidden.\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n return (\n <RailIcon\n key={service.id}\n icon={service.icon}\n label={service.label}\n active={isActive(service.id)}\n onClick={() => onServiceSelect(service.id)}\n disabled={disabled}\n tooltip={\n disabled && reason ? `${service.label} -- ${reason}` : undefined\n }\n />\n );\n };\n\n return (\n <>\n <IconRail>\n <div className=\"flex-1 flex flex-col items-center gap-1 overflow-y-auto\">\n {mainModules.map(renderModule)}\n </div>\n\n <RailSeparator />\n <div className=\"flex flex-col items-center gap-1\">\n {bottomModules.map(renderModule)}\n </div>\n </IconRail>\n\n {activeService && activeService.navigation.length > 0 && (\n <SubNavPanel\n service={activeService}\n collapsed={subNavCollapsed}\n onToggleCollapse={onToggleSubNav}\n />\n )}\n </>\n );\n}\n","/**\n * IconRail primitives — composable building blocks for the narrow\n * vertical icon strip.\n *\n * - IconRail: the container (<nav> with flex column, border, bg)\n * - RailIcon: a single icon button with tooltip\n * - RailSeparator: divider between groups\n *\n * For the auto-wired layout, use ShellRail instead.\n */\nimport React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { Button, Separator } from \"@petrarca/sonnet-ui\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@petrarca/sonnet-ui\";\nimport type { LucideIcon } from \"lucide-react\";\n\n// IconRail container\n\nexport interface IconRailProps {\n children: React.ReactNode;\n className?: string;\n}\n\n/** Narrow vertical nav container for RailIcon items. */\nexport function IconRail({\n children,\n className,\n}: IconRailProps): React.ReactElement {\n return (\n <nav\n className={cn(\n \"w-14 shrink-0 border-r bg-muted flex flex-col items-center py-2 gap-1\",\n className,\n )}\n >\n {children}\n </nav>\n );\n}\n\n// RailIcon\n\nexport interface RailIconProps {\n icon: LucideIcon;\n label: string;\n active?: boolean;\n onClick?: () => void;\n /** Render greyed and non-interactive (e.g. a capability requirement is unmet). */\n disabled?: boolean;\n /** Optional tooltip override (e.g. why the item is disabled). Defaults to `label`. */\n tooltip?: string;\n}\n\n/** Single icon button with tooltip inside an IconRail. */\nexport function RailIcon({\n icon: Icon,\n label,\n active = false,\n onClick,\n disabled = false,\n tooltip,\n}: RailIconProps): React.ReactElement {\n return (\n <Tooltip delayDuration={0}>\n <TooltipTrigger asChild>\n {/* aria-disabled (not native `disabled`) keeps the trigger focusable so\n the tooltip explaining *why* remains discoverable. */}\n <Button\n variant=\"ghost\"\n size=\"compact\"\n aria-disabled={disabled || undefined}\n onClick={disabled ? undefined : onClick}\n className={cn(\n \"h-10 w-10 rounded-lg transition-colors\",\n disabled\n ? \"text-muted-foreground/40 cursor-default hover:bg-transparent\"\n : active\n ? \"bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:text-foreground hover:bg-muted-foreground/10\",\n )}\n >\n <Icon className=\"h-5 w-5\" />\n <span className=\"sr-only\">{label}</span>\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"right\" sideOffset={8}>\n {tooltip ?? label}\n </TooltipContent>\n </Tooltip>\n );\n}\n\n// RailSeparator\n\n/** Visual divider between icon groups inside an IconRail. */\nexport function RailSeparator(): React.ReactElement {\n return <Separator className=\"w-6 my-1\" />;\n}\n","/**\n * SubNavPanel — contextual secondary navigation for the selected service.\n *\n * Shows the service label at top, then grouped nav links using the shared\n * SidebarGroup / SidebarItem primitives. If other modules contribute links\n * via the \"<moduleId>.nav\" extension point, they are appended.\n *\n * Supports a collapsed state (thin strip with expand chevron).\n */\nimport { useMemo } from \"react\";\nimport { ChevronsLeft, ChevronsRight } from \"lucide-react\";\nimport { ScrollArea } from \"@petrarca/sonnet-ui\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@petrarca/sonnet-ui\";\nimport type { ShellModule, NavGroup } from \"./types\";\nimport { useExtensionPoint } from \"./hooks\";\nimport { SidebarGroup } from \"./SidebarGroup\";\nimport { SidebarItem } from \"./SidebarItem\";\n\nexport interface SubNavPanelProps {\n service: ShellModule;\n collapsed: boolean;\n onToggleCollapse: () => void;\n}\n\n// Merge contributed nav links from other modules.\nfunction useMergedNavigation(service: ShellModule): NavGroup[] {\n const contributions = useExtensionPoint(`${service.id}.nav`);\n return useMemo(() => {\n if (contributions.length === 0) return service.navigation;\n\n const contributed: NavGroup = {\n id: `${service.id}-contributed`,\n links: contributions.map((c) => ({\n id: c.extensionPoint + \".\" + c.label.toLowerCase().replace(/\\s+/g, \"-\"),\n label: c.label,\n path: c.path,\n })),\n };\n\n return [...service.navigation, contributed];\n }, [service, contributions]);\n}\n\nexport function SubNavPanel({\n service,\n collapsed,\n onToggleCollapse,\n}: SubNavPanelProps) {\n const mergedNavigation = useMergedNavigation(service);\n\n if (collapsed) {\n return (\n <aside className=\"shrink-0 border-r bg-background flex flex-col items-center pt-2.5\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label=\"Expand navigation\"\n className=\"rounded text-muted-foreground/50 hover:text-muted-foreground transition-colors p-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={onToggleCollapse}\n >\n <ChevronsRight className=\"h-4 w-4\" />\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"right\">Expand navigation</TooltipContent>\n </Tooltip>\n </aside>\n );\n }\n\n return (\n <aside className=\"w-52 shrink-0 border-r bg-background flex flex-col\">\n {/* Service header with collapse toggle */}\n <div className=\"h-10 shrink-0 flex items-center justify-between px-4\">\n <span className=\"text-sm font-semibold text-foreground\">\n {service.label}\n </span>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n aria-label=\"Collapse navigation\"\n className=\"rounded text-muted-foreground/40 hover:text-muted-foreground transition-colors p-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={onToggleCollapse}\n >\n <ChevronsLeft className=\"h-3.5 w-3.5\" />\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"right\">Collapse navigation</TooltipContent>\n </Tooltip>\n </div>\n\n {/* Nav groups — reuses SidebarGroup / SidebarItem primitives */}\n <ScrollArea className=\"flex-1\">\n <nav className=\"px-1.5 pb-4 space-y-2\">\n {mergedNavigation.map((group) => (\n <SidebarGroup\n key={group.id}\n heading={group.heading}\n collapsible={group.collapsible}\n >\n {group.links.map((link) => (\n <SidebarItem\n key={link.path}\n label={link.label}\n path={link.path}\n badge={link.badge}\n />\n ))}\n </SidebarGroup>\n ))}\n </nav>\n </ScrollArea>\n </aside>\n );\n}\n","/**\n * Shell hooks -- reactive APIs for modules.\n *\n * Import path: @/shell/hooks\n */\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { events } from \"./api\";\nimport { useShellModules } from \"./shellModules\";\nimport type { Contribution, ShellModule, ShellEventMap } from \"./types\";\n\n/**\n * Get sorted contributions for a named extension point.\n *\n * Returns all contributions registered by any module for the given\n * extension point, sorted by `order` (ascending).\n *\n * @example\n * ```tsx\n * const tabs = useExtensionPoint(\"organization-detail\");\n * ```\n */\nexport function useExtensionPoint(name: string): Contribution[] {\n const { getExtensionPoint } = useShellModules();\n return useMemo(() => getExtensionPoint(name), [getExtensionPoint, name]);\n}\n\n/**\n * Get the list of visible service modules (excludes hidden and bottom-pinned).\n *\n * Useful for building overviews like the home page.\n */\nexport function useMainModules(): ShellModule[] {\n const { mainModules } = useShellModules();\n return mainModules;\n}\n\n/**\n * Subscribe to a shell event, auto-unsubscribing on unmount.\n *\n * The handler reference is stabilized via useRef so that the\n * subscription is not torn down and recreated on every render.\n *\n * @example\n * ```tsx\n * useShellEvent(\"schema-definition:changed\", (payload) => {\n * queryClient.invalidateQueries({ queryKey: [\"my-key\"] });\n * });\n * ```\n */\nexport function useShellEvent<K extends keyof ShellEventMap>(\n key: K,\n handler: (payload: ShellEventMap[K]) => void,\n): void {\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n return events.on(key, (payload) => handlerRef.current(payload));\n }, [key]);\n}\n","/**\n * Shell API -- imperative domain capabilities for modules.\n *\n * Import path: @/shell/api\n *\n * Grouped by domain: notification, dialog, navigation, panel, events.\n */\nimport type { ReactNode } from \"react\";\nimport { toast } from \"sonner\";\nimport { warnLog } from \"@petrarca/sonnet-core\";\nimport type { ShellEventMap } from \"./types\";\nimport * as eventBus from \"./eventBus\";\n\n// notification\n\ninterface PromiseOptions<T> {\n loading: string;\n success: string | ((data: T) => string);\n error: string | ((err: unknown) => string);\n}\n\nexport const notification = {\n success(message: string) {\n toast.success(message);\n },\n error(message: string) {\n toast.error(message);\n },\n warning(message: string) {\n toast.warning(message);\n },\n info(message: string) {\n toast.info(message);\n },\n promise<T>(promise: Promise<T>, opts: PromiseOptions<T>) {\n return toast.promise(promise, opts);\n },\n dismiss(id?: string | number) {\n toast.dismiss(id);\n },\n};\n\n// dialog\n\n// The shell mounts a single ConfirmDialog instance in AppShell and injects\n// its open function here via initDialog(). Modules call dialog.confirm()\n// without knowing where the implementation lives -- federation-safe, same\n// pattern as navigation and panel.\n\nexport interface ConfirmOptions {\n title: string;\n description?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: \"default\" | \"destructive\";\n}\n\ntype DialogConfirmFn = (opts: ConfirmOptions) => Promise<boolean>;\n\nlet _dialogConfirm: DialogConfirmFn | null = null;\n\n/**\n * Called by the shell during initialization to inject the confirm dialog.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initDialog(confirmFn: DialogConfirmFn) {\n _dialogConfirm = confirmFn;\n}\n\nexport const dialog = {\n /**\n * Show a confirmation dialog. Returns true if confirmed, false if cancelled.\n */\n confirm(opts: ConfirmOptions): Promise<boolean> {\n if (_dialogConfirm) {\n return _dialogConfirm(opts);\n }\n warnLog(\"dialog.confirm: shell not initialized yet.\");\n return Promise.resolve(false);\n },\n};\n\n// navigation\n\n// The shell injects the navigate function and command menu opener at startup\n// via initNavigation(). Modules call navigation.goTo() etc. without knowing\n// where the implementation comes from -- works with both monorepo and\n// Module Federation (host provides the injector, remotes consume the API).\n\ntype NavigateFn = (path: string) => void;\ntype CommandMenuFn = () => void;\ntype FeatureLookupFn = (featureId: string) => string | null;\n\nlet _navigate: NavigateFn | null = null;\nlet _openCommandMenu: CommandMenuFn | null = null;\nlet _lookupFeature: FeatureLookupFn | null = null;\n\n/**\n * Called by the shell during initialization to inject router + command menu.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initNavigation(\n navigateFn: NavigateFn,\n openCommandMenuFn: CommandMenuFn,\n) {\n _navigate = navigateFn;\n _openCommandMenu = openCommandMenuFn;\n}\n\n/**\n * Called by the shell during initialization to inject the feature lookup.\n * Resolves a stable feature id (e.g. \"auditing.audit-events\") to a path.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initFeatureNav(lookupFn: FeatureLookupFn) {\n _lookupFeature = lookupFn;\n}\n\nexport const navigation = {\n /** Navigate to a path within the shell. */\n goTo(path: string) {\n if (_navigate) {\n _navigate(path);\n } else {\n warnLog(\"navigation.goTo: shell not initialized yet.\");\n }\n },\n /**\n * Navigate to a feature by its stable identifier (e.g. \"auditing.audit-events\").\n * The path is resolved from the module registry at call time, so it stays\n * correct even if routes are reorganised.\n */\n goToFeature(featureId: string) {\n if (!_navigate || !_lookupFeature) {\n warnLog(\"navigation.goToFeature: shell not initialized yet.\");\n return;\n }\n const path = _lookupFeature(featureId);\n if (path) {\n _navigate(path);\n } else {\n warnLog(\"navigation.goToFeature: unknown feature id '{}'.\", featureId);\n }\n },\n /** Open the Cmd+K command menu. */\n openCommandMenu() {\n if (_openCommandMenu) {\n _openCommandMenu();\n } else {\n warnLog(\"navigation.openCommandMenu: shell not initialized yet.\");\n }\n },\n /** Go back in browser history. */\n back() {\n window.history.back();\n },\n};\n\n// panel\n\n// The shell owns the slide-over panel (Sheet). Modules request it via\n// panel.open() with content to render. The shell renders that content inside\n// the panel. Same injection pattern as navigation -- federation-safe.\n\nexport interface PanelOptions {\n /**\n * Title shown in the panel header. When omitted the header is visually\n * hidden (the content owns its own header) but a screen-reader-only\n * title is still rendered for accessibility.\n */\n title?: string;\n /** React content to render inside the panel */\n content: ReactNode;\n /** Optional description below the title */\n description?: string;\n /**\n * Panel width token.\n *\n * - \"narrow\" -- 480px -- quick actions, simple single-field forms\n * - \"default\" -- 640px -- standard entity forms (default when omitted)\n * - \"wide\" -- 800px -- forms with embedded tables or complex layouts\n * - \"half\" -- 50vw -- split-screen inspection, side-by-side context\n * - \"full\" -- 100vw -- immersive editing, full-width canvases\n *\n * Legacy values \"lg\", \"1/3\", \"1/2\" are kept for backward compatibility\n * and map to \"default\" (640px), 33vw, and \"half\" (50vw) respectively.\n */\n width?:\n | \"narrow\"\n | \"default\"\n | \"wide\"\n | \"half\"\n | \"full\"\n | \"lg\"\n | \"1/3\"\n | \"1/2\";\n /**\n * Vertical coverage of the panel.\n * - \"below-header\" (default): panel starts below the TopBar, keeping it visible.\n * - \"full\": panel covers the full viewport height including the TopBar.\n */\n coverage?: \"below-header\" | \"full\";\n /**\n * Allow the user to drag the left edge of the panel to resize it.\n * Defaults to false. When true, the fixed `width` token is used as the\n * initial width and the panel width is controlled dynamically.\n */\n resizable?: boolean;\n /**\n * Minimum width in pixels when resizable. Defaults to 240.\n * Ignored when resizable is false.\n */\n minWidth?: number;\n /**\n * Maximum width in pixels when resizable. Defaults to 90% of viewport width.\n * Ignored when resizable is false.\n */\n maxWidth?: number;\n /** Called after the panel finishes closing. Use to restore focus to the triggering element. */\n onClose?: () => void;\n}\n\ntype PanelOpenFn = (opts: PanelOptions) => void;\ntype PanelCloseFn = () => void;\n\nlet _panelOpen: PanelOpenFn | null = null;\nlet _panelClose: PanelCloseFn | null = null;\n\n/**\n * Called by the shell during initialization to inject panel controls.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initPanel(openFn: PanelOpenFn, closeFn: PanelCloseFn) {\n _panelOpen = openFn;\n _panelClose = closeFn;\n}\n\nexport const panel = {\n /** Open the shell panel with content rendered inside. */\n open(opts: PanelOptions) {\n if (_panelOpen) {\n _panelOpen(opts);\n } else {\n warnLog(\"panel.open: shell not initialized yet.\");\n }\n },\n /** Close the panel. */\n close() {\n if (_panelClose) {\n _panelClose();\n } else {\n warnLog(\"panel.close: shell not initialized yet.\");\n }\n },\n};\n\n// sidePane\n\n// The shell owns the side pane (SidePane component in AppShell). Modules\n// request it via sidePane.open() with content and sizing options. The pane\n// sits inline beside the content area -- the main view shrinks rather than\n// being overlaid. Same injection pattern as panel -- federation-safe.\n\nimport type { SidePaneOptions } from \"./sidePaneState\";\nexport type { SidePaneOptions };\n\ntype SidePaneOpenFn = (opts: SidePaneOptions) => void;\ntype SidePaneCloseFn = () => void;\ntype SidePaneIsOpenFn = () => boolean;\ntype SidePaneActiveModuleIdFn = () => string | null;\ntype SidePaneSetFullWidthFn = (fullWidth: boolean) => void;\n\nlet _sidePaneOpen: SidePaneOpenFn | null = null;\nlet _sidePaneClose: SidePaneCloseFn | null = null;\nlet _sidePaneIsOpen: SidePaneIsOpenFn | null = null;\nlet _sidePaneActiveModuleId: SidePaneActiveModuleIdFn | null = null;\nlet _sidePaneSetFullWidth: SidePaneSetFullWidthFn | null = null;\n\n/**\n * Called by the shell during initialization to inject side pane controls.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initSidePane(\n openFn: SidePaneOpenFn,\n closeFn: SidePaneCloseFn,\n isOpenFn: SidePaneIsOpenFn,\n activeModuleIdFn: SidePaneActiveModuleIdFn,\n setFullWidthFn?: SidePaneSetFullWidthFn,\n) {\n _sidePaneOpen = openFn;\n _sidePaneClose = closeFn;\n _sidePaneIsOpen = isOpenFn;\n _sidePaneActiveModuleId = activeModuleIdFn;\n _sidePaneSetFullWidth = setFullWidthFn ?? null;\n}\n\nexport const sidePane = {\n /** Open the side pane with the given content and sizing options. */\n open(opts: SidePaneOptions) {\n if (_sidePaneOpen) {\n _sidePaneOpen(opts);\n } else {\n warnLog(\"sidePane.open: shell not initialized yet.\");\n }\n },\n /** Close the side pane. */\n close() {\n if (_sidePaneClose) {\n _sidePaneClose();\n } else {\n warnLog(\"sidePane.close: shell not initialized yet.\");\n }\n },\n /**\n * Toggle the side pane.\n *\n * If the pane is closed, opens it with opts.\n * If the pane is open with the same moduleId, closes it.\n * If the pane is open with a different moduleId, replaces it with opts.\n */\n toggle(opts: SidePaneOptions) {\n if (\n !_sidePaneIsOpen ||\n !_sidePaneActiveModuleId ||\n !_sidePaneOpen ||\n !_sidePaneClose\n ) {\n warnLog(\"sidePane.toggle: shell not initialized yet.\");\n return;\n }\n if (_sidePaneIsOpen() && _sidePaneActiveModuleId() === opts.moduleId) {\n _sidePaneClose();\n } else {\n _sidePaneOpen(opts);\n }\n },\n /** Returns true when the pane is currently open. */\n isOpen(): boolean {\n return _sidePaneIsOpen ? _sidePaneIsOpen() : false;\n },\n /** Returns the moduleId of the currently open pane, or null. */\n activeModuleId(): string | null {\n return _sidePaneActiveModuleId ? _sidePaneActiveModuleId() : null;\n },\n /**\n * Expand or collapse the pane to fill the full content area.\n *\n * When true the pane takes flex-1 and hides the main route content.\n * Use for full-screen tool views (schema viewer, form editors).\n * Call setFullWidth(false) to restore normal inline sizing.\n */\n setFullWidth(fullWidth: boolean) {\n if (_sidePaneSetFullWidth) {\n _sidePaneSetFullWidth(fullWidth);\n } else {\n warnLog(\"sidePane.setFullWidth: shell not initialized yet.\");\n }\n },\n};\n\n// fullscreen\n\n// The shell owns the fullscreen overlay. Modules request it via\n// fullscreen.enter(content) and exit via fullscreen.exit(). The shell renders\n// the content in a fixed inset-0 z-50 overlay, covering all shell chrome.\n// Same injection pattern as panel -- federation-safe.\n\nexport interface FullscreenOptions {\n /** React content to render inside the fullscreen overlay. */\n content: ReactNode;\n}\n\ntype FullscreenEnterFn = (opts: FullscreenOptions) => void;\ntype FullscreenExitFn = () => void;\n\nlet _fullscreenEnter: FullscreenEnterFn | null = null;\nlet _fullscreenExit: FullscreenExitFn | null = null;\n\n/**\n * Called by the shell during initialization to inject fullscreen controls.\n * Not part of the public module API -- only the shell calls this.\n */\nexport function initFullscreen(\n enterFn: FullscreenEnterFn,\n exitFn: FullscreenExitFn,\n) {\n _fullscreenEnter = enterFn;\n _fullscreenExit = exitFn;\n}\n\nexport const fullscreen = {\n /** Enter fullscreen mode, rendering content over all shell chrome. */\n enter(opts: FullscreenOptions) {\n if (_fullscreenEnter) {\n _fullscreenEnter(opts);\n } else {\n warnLog(\"fullscreen.enter: shell not initialized yet.\");\n }\n },\n /** Exit fullscreen mode, restoring the normal shell layout. */\n exit() {\n if (_fullscreenExit) {\n _fullscreenExit();\n } else {\n warnLog(\"fullscreen.exit: shell not initialized yet.\");\n }\n },\n};\n\n// events\n\ntype Unsubscribe = () => void;\n\nexport const events = {\n /** Subscribe to an event. Returns an unsubscribe function. */\n on<K extends keyof ShellEventMap>(\n key: K,\n handler: (payload: ShellEventMap[K]) => void,\n ): Unsubscribe {\n return eventBus.on(key as string, handler as (payload: unknown) => void);\n },\n\n /** Subscribe, auto-unsubscribing after the first emission. */\n once<K extends keyof ShellEventMap>(\n key: K,\n handler: (payload: ShellEventMap[K]) => void,\n ): Unsubscribe {\n return eventBus.once(key as string, handler as (payload: unknown) => void);\n },\n\n /** Emit an event. Handlers run asynchronously (microtask). */\n emit<K extends keyof ShellEventMap>(key: K, payload: ShellEventMap[K]): void {\n eventBus.emit(key as string, payload);\n },\n};\n","/**\n * Shell event bus -- in-memory pub/sub for cross-module communication.\n *\n * The bus is untyped internally (operates on strings and unknown payloads).\n * Type safety is provided by the public API layer in shell/api.ts, which\n * constrains keys and payloads via the ShellEventMap interface.\n *\n * Mirrors the server-side EventBus pattern (fire-and-forget, error isolation)\n * adapted for the browser's single-threaded model.\n */\nimport { devLog, warnLog } from \"@petrarca/sonnet-core\";\n\ntype Handler = (payload: unknown) => void;\ntype Unsubscribe = () => void;\n\nconst listeners = new Map<string, Set<Handler>>();\n\n/** Subscribe to an event key. Returns an unsubscribe function. */\nexport function on(key: string, handler: Handler): Unsubscribe {\n let set = listeners.get(key);\n if (!set) {\n set = new Set();\n listeners.set(key, set);\n }\n set.add(handler);\n devLog(`[shell:event] on \"${key}\" (${set.size} handler(s))`);\n return () => {\n set!.delete(handler);\n if (set!.size === 0) listeners.delete(key);\n devLog(`[shell:event] off \"${key}\" (${set!.size} handler(s))`);\n };\n}\n\n/** Subscribe to an event key, auto-unsubscribing after the first emission. */\nexport function once(key: string, handler: Handler): Unsubscribe {\n const unsubscribe = on(key, (payload) => {\n unsubscribe();\n handler(payload);\n });\n return unsubscribe;\n}\n\n/** Emit an event. Handlers run in microtasks (non-blocking, error-isolated). */\nexport function emit(key: string, payload: unknown): void {\n const set = listeners.get(key);\n const count = set?.size ?? 0;\n devLog(`[shell:event] emit \"${key}\"`, payload, `(${count} handler(s))`);\n if (!set || count === 0) return;\n for (const handler of set) {\n Promise.resolve().then(() => {\n try {\n handler(payload);\n } catch (err) {\n warnLog(`[shell:event] handler error \"${key}\":`, err);\n }\n });\n }\n}\n","/**\n * Shell modules context.\n *\n * Provides the module registry to shell components (IconRail,\n * CommandMenu, hooks) without requiring them to import from the\n * app-specific modules/registry.ts. AppShell injects the registry;\n * children consume it via useShellModules().\n */\nimport { createContext, useContext } from \"react\";\nimport type { ModuleRegistry } from \"./createModuleRegistry\";\n\nexport const ShellModulesContext = createContext<ModuleRegistry | null>(null);\n\n/**\n * Read the module registry from context.\n * Must be called within an AppShell (which provides the context).\n */\nexport function useShellModules(): ModuleRegistry {\n const registry = useContext(ShellModulesContext);\n if (!registry) {\n throw new Error(\"useShellModules must be used within an AppShell\");\n }\n return registry;\n}\n","/** Labeled, optionally collapsible section within a Sidebar. */\nimport React, { useState } from \"react\";\nimport { ChevronDown } from \"lucide-react\";\nimport { cn } from \"@petrarca/sonnet-core\";\n\nexport interface SidebarGroupProps {\n heading?: string;\n collapsible?: boolean;\n defaultOpen?: boolean;\n /** Render a border separator below this group. */\n separator?: boolean;\n children: React.ReactNode;\n}\n\n// Section heading with optional collapse toggle.\nfunction GroupHeading({\n heading,\n canCollapse,\n open,\n onToggle,\n}: {\n heading: string;\n canCollapse: boolean;\n open: boolean;\n onToggle: () => void;\n}) {\n return (\n <button\n type=\"button\"\n onClick={canCollapse ? onToggle : undefined}\n className={cn(\n \"flex w-full items-center gap-1 rounded px-2 py-1.5\",\n \"text-[11px] font-semibold uppercase tracking-widest text-muted-foreground/70\",\n canCollapse ? \"hover:text-foreground cursor-pointer\" : \"cursor-default\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n )}\n >\n {canCollapse && (\n <ChevronDown\n className={cn(\"h-3 w-3 transition-transform\", !open && \"-rotate-90\")}\n />\n )}\n <span>{heading}</span>\n </button>\n );\n}\n\nexport function SidebarGroup({\n heading,\n collapsible = false,\n defaultOpen = true,\n separator = false,\n children,\n}: SidebarGroupProps): React.ReactElement {\n const [open, setOpen] = useState(defaultOpen);\n const canCollapse = collapsible && !!heading;\n\n return (\n <div className={cn(separator && \"border-b border-border pb-2\")}>\n {heading && (\n <GroupHeading\n heading={heading}\n canCollapse={canCollapse}\n open={open}\n onToggle={() => setOpen((v) => !v)}\n />\n )}\n {(!canCollapse || open) && <ul className=\"space-y-0.5\">{children}</ul>}\n </div>\n );\n}\n","/** Single navigation row inside a Sidebar — icon + label + optional trailing slot. */\nimport React from \"react\";\nimport { Link, useLocation } from \"react-router-dom\";\nimport { ChevronRight } from \"lucide-react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport type { LucideIcon } from \"lucide-react\";\n\nexport interface SidebarItemProps {\n icon?: LucideIcon;\n label: string;\n path: string;\n badge?: string | number;\n indent?: number;\n expandable?: boolean;\n expanded?: boolean;\n onToggle?: () => void;\n onClick?: () => void;\n /**\n * Render greyed and non-navigable (e.g. a capability requirement is unmet).\n * Uses aria-disabled, not native disable, so the row stays focusable and its\n * title explaining why remains discoverable -- matching RailIcon.\n */\n disabled?: boolean;\n /** Tooltip / title shown when disabled (e.g. why it is unavailable). */\n title?: string;\n}\n\n// Trailing badge or expand chevron rendered after the label.\nfunction ItemTrailing({\n badge,\n expandable,\n expanded,\n onToggle,\n}: Pick<SidebarItemProps, \"badge\" | \"expandable\" | \"expanded\" | \"onToggle\">) {\n if (badge != null) {\n return (\n <span className=\"inline-flex items-center rounded-full bg-muted px-1.5 h-[18px] text-[10px] tabular-nums text-muted-foreground\">\n {badge}\n </span>\n );\n }\n if (expandable) {\n const handleClick = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n onToggle?.();\n };\n return (\n <button\n type=\"button\"\n aria-label={expanded ? \"Collapse\" : \"Expand\"}\n onClick={handleClick}\n className=\"rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n >\n <ChevronRight\n className={cn(\n \"h-3 w-3 transition-transform\",\n expanded && \"rotate-90\",\n )}\n />\n </button>\n );\n }\n return null;\n}\n\n// Row appearance by state, kept out of the component to hold it under the\n// complexity limit.\nfunction itemStateClass(disabled: boolean, isActive: boolean): string {\n if (disabled)\n return \"text-muted-foreground/40 cursor-default hover:bg-transparent\";\n if (isActive) return \"bg-accent text-accent-foreground\";\n return \"text-muted-foreground hover:text-foreground hover:bg-accent/50\";\n}\n\nexport function SidebarItem({\n icon: Icon,\n label,\n path,\n badge,\n indent = 0,\n expandable = false,\n expanded = false,\n onToggle,\n onClick,\n disabled = false,\n title,\n}: SidebarItemProps): React.ReactElement {\n const { pathname } = useLocation();\n const isActive = !disabled && pathname === path;\n const style =\n indent > 0 ? { paddingLeft: `${indent * 16 + 8}px` } : undefined;\n // aria-disabled (not native) keeps the row focusable so its title explaining\n // why it is unavailable stays discoverable -- see RailIcon.\n const disabledProps = disabled\n ? {\n \"aria-disabled\": true as const,\n title,\n onClick: (e: React.MouseEvent) => e.preventDefault(),\n }\n : { onClick };\n\n return (\n <li>\n <Link\n to={path}\n {...disabledProps}\n style={style}\n className={cn(\n \"flex items-center gap-2 rounded px-2 py-1.5 text-[13px] font-medium transition-colors select-none\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n itemStateClass(disabled, isActive),\n )}\n >\n {Icon && <Icon className=\"h-4 w-4 shrink-0\" />}\n <span className=\"min-w-0 flex-1 truncate\">{label}</span>\n <ItemTrailing\n badge={badge}\n expandable={expandable}\n expanded={expanded}\n onToggle={onToggle}\n />\n </Link>\n </li>\n );\n}\n","/**\n * Shell navigation context — provides active service state and interaction\n * handlers to ShellRail, SubNavPanel, and any custom navigation components.\n *\n * Provided by AppShell. Consumed by ShellRail (and available to consumers\n * who build custom navigation layouts).\n */\nimport { createContext, useContext } from \"react\";\nimport type { ShellModule } from \"./types\";\n\nexport interface ShellNavigationState {\n activeService: ShellModule | null;\n sidePaneModuleId: string | null;\n subNavCollapsed: boolean;\n onToggleSubNav: () => void;\n onServiceSelect: (id: string) => void;\n}\n\nexport const ShellNavigationContext =\n createContext<ShellNavigationState | null>(null);\n\n/** Read shell navigation state. Must be used within AppShell. */\nexport function useShellNavigation(): ShellNavigationState {\n const ctx = useContext(ShellNavigationContext);\n if (!ctx) {\n throw new Error(\"useShellNavigation must be used within AppShell\");\n }\n return ctx;\n}\n","/**\n * Shell configuration types and context.\n *\n * ShellConfig is intentionally minimal. The shell provides layout chrome\n * (TopBar header, IconRail, SubNavPanel, CommandMenu) but does not own\n * any app-specific content. The consumer composes the TopBar content --\n * logo, environment selector, search trigger, user menu -- via the\n * `topBar` ReactNode slot using building blocks exported from the shell.\n */\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type { CapabilityPolicy, CapabilityResolver } from \"./types\";\n\n// Top-level shell configuration.\n\nexport interface ShellConfig {\n /**\n * Content rendered inside the TopBar header. The shell provides the\n * `<header>` element with its sizing and border; the consumer owns\n * everything inside it.\n *\n * Compose this using building blocks like `SearchTrigger` and\n * `UserMenu` (exported from the shell) alongside any app-specific\n * components.\n */\n topBar?: ReactNode;\n\n /**\n * Content rendered inside the ShellFooter. The shell provides the\n * `<footer>` element with its sizing and border; the consumer owns\n * everything inside it.\n *\n * Common uses: status bar, sync indicators, version info, quick actions.\n * Omit to render no footer.\n */\n footer?: ReactNode;\n\n /**\n * Evaluates a module capability (from `ShellModule.requires`) for gating the\n * icon rail / command menu / routes. Absent ⇒ no gating (all modules\n * available). See docs/design/module-capabilities.md in this package.\n */\n resolveCapability?: CapabilityResolver;\n\n /**\n * Sparse overrides for the (unmet reason kind, surface) → effect matrix,\n * merged over the shell defaults. Lets the app tune hide-vs-disable per\n * reason kind and surface without touching modules.\n */\n capabilityPolicy?: CapabilityPolicy;\n}\n\n// Context\n\nexport const ShellConfigContext = createContext<ShellConfig | null>(null);\n\n/**\n * Read the shell configuration from context.\n * Must be called within a ShellConfigProvider.\n */\nexport function useShellConfig(): ShellConfig {\n const config = useContext(ShellConfigContext);\n if (!config) {\n throw new Error(\"useShellConfig must be used within a ShellConfigProvider\");\n }\n return config;\n}\n","/**\n * Capability gating — evaluate a module's requirements into a per-surface\n * presentation effect.\n *\n * See docs/design/module-capabilities.md in this package. The shell is\n * domain-agnostic: a module declares `requires` capabilities, the app provides a\n * `CapabilityResolver`, and the shell maps the resolver's semantic reason kind\n * to an `Effect` via a surface-aware policy matrix.\n */\nimport { warnLog } from \"@petrarca/sonnet-core\";\nimport type {\n CapabilityPolicy,\n CapabilityResolver,\n Effect,\n ShellModule,\n Surface,\n UnmetKind,\n} from \"./types\";\n\n/**\n * Default (kind, surface) → effect matrix. App overrides are merged over this.\n *\n * The `route` column is planned, not yet wired: no caller passes the `\"route\"`\n * surface to `evaluateModule` (only `\"rail\"` and `\"command\"` are evaluated\n * today), so its `redirect` effects do not fire. The column is kept because\n * route gating is a designed capability -- a gated module's routes still render\n * if reached directly -- and removing it would be a type break for the\n * capability we intend to build. See docs/design/module-capabilities.md.\n */\nexport const DEFAULT_CAPABILITY_POLICY: Record<\n UnmetKind,\n Record<Surface, Effect>\n> = {\n \"missing-state\": { rail: \"disabled\", command: \"disabled\", route: \"redirect\" },\n \"not-authorized\": { rail: \"hidden\", command: \"hidden\", route: \"redirect\" },\n \"not-authorized-soft\": {\n rail: \"disabled\",\n command: \"disabled\",\n route: \"redirect\",\n },\n \"feature-off\": { rail: \"hidden\", command: \"disabled\", route: \"redirect\" },\n error: { rail: \"hidden\", command: \"hidden\", route: \"redirect\" },\n};\n\n/** Strictness order — the most restrictive effect wins when folding requirements. */\nconst EFFECT_RANK: Record<Effect, number> = {\n hidden: 3,\n redirect: 2,\n disabled: 1,\n visible: 0,\n};\n\n/** Merge a sparse app override over the default matrix. */\nexport function resolveCapabilityPolicy(\n override?: CapabilityPolicy,\n): Record<UnmetKind, Record<Surface, Effect>> {\n if (!override) return DEFAULT_CAPABILITY_POLICY;\n const merged = {} as Record<UnmetKind, Record<Surface, Effect>>;\n for (const kind of Object.keys(DEFAULT_CAPABILITY_POLICY) as UnmetKind[]) {\n merged[kind] = {\n ...DEFAULT_CAPABILITY_POLICY[kind],\n ...(override[kind] ?? {}),\n };\n }\n return merged;\n}\n\nexport interface ModuleEffect {\n effect: Effect;\n /** Hint from the first most-restrictive unmet capability (for tooltips). */\n reason?: string;\n}\n\n/**\n * Fold a single unmet-capability result into the running accumulator.\n * Extracted to keep evaluateModule below the cyclomatic-complexity limit.\n */\nfunction foldUnmet(\n effect: Effect,\n resultReason: string | undefined,\n acc: ModuleEffect,\n): ModuleEffect {\n const rank = EFFECT_RANK[effect];\n const worstRank = EFFECT_RANK[acc.effect];\n if (rank > worstRank) {\n // Strictly more restrictive — take this effect and its reason.\n return { effect, reason: resultReason };\n }\n if (rank === worstRank && !acc.reason && resultReason) {\n // Same restrictiveness; take the first available reason at this rank.\n return { effect: acc.effect, reason: resultReason };\n }\n return acc;\n}\n\n/**\n * Evaluate a module's requirements for a given surface.\n *\n * Returns `visible` when there are no requirements or all are met. Otherwise\n * each unmet capability maps (kind, surface) → effect and the most restrictive\n * wins (hidden > redirect > disabled > visible). When capabilities share the\n * same effect rank, the first one with a non-empty reason provides the tooltip.\n */\nexport function evaluateModule(\n module: ShellModule,\n surface: Surface,\n resolve: CapabilityResolver | undefined,\n policy: Record<UnmetKind, Record<Surface, Effect>>,\n): ModuleEffect {\n const capabilities = module.requires;\n if (!capabilities || capabilities.length === 0) return { effect: \"visible\" };\n // No resolver ⇒ nothing to evaluate against ⇒ available (gating disabled).\n if (!resolve) return { effect: \"visible\" };\n\n let acc: ModuleEffect = { effect: \"visible\" };\n for (const capability of capabilities) {\n const result = resolve(capability);\n if (result.met) continue;\n const kind = result.kind ?? \"error\";\n if (kind === \"error\") {\n warnLog(\n `[capabilities] unmet capability \"${capability}\" resolved to kind \"error\" (fail-closed)`,\n );\n }\n acc = foldUnmet(policy[kind][surface], result.reason, acc);\n }\n return acc;\n}\n","/**\n * SidePane -- persistent resizable inline panel.\n *\n * Sits between SubNavPanel and the main content area in the AppShell flex row.\n * When open it takes a fixed pixel width; the content area (flex-1) shrinks to\n * fill the remainder. The main view remains fully visible and interactive.\n *\n * Opened/closed imperatively via sidePane.open() / sidePane.close() / sidePane.toggle()\n * from api.ts. State is provided by SidePaneContext (owned by AppShell).\n */\nimport { X } from \"lucide-react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { useResizablePanel } from \"@petrarca/sonnet-core/hooks\";\nimport { useSidePaneState } from \"./sidePaneState\";\nimport { DragHandle } from \"./DragHandle\";\n\nconst DEFAULT_WIDTH = 320;\nconst DEFAULT_MIN = 240;\nconst DEFAULT_MAX = 800;\n\n/**\n * SidePane renders nothing when no pane is open.\n * When open it renders a bordered panel with a drag handle on its right edge.\n * Double-clicking the handle resets to defaultWidth.\n */\nexport default function SidePane() {\n const { pane, close } = useSidePaneState();\n if (!pane) return null;\n return <SidePaneOpen pane={pane} onClose={close} />;\n}\n\nfunction SidePaneOpen({\n pane,\n onClose,\n}: {\n pane: NonNullable<ReturnType<typeof useSidePaneState>[\"pane\"]>;\n onClose: () => void;\n}) {\n const { panelWidth, handlePointerDown, handleDoubleClick, separatorProps } =\n useResizablePanel({\n defaultWidth: pane.defaultWidth ?? DEFAULT_WIDTH,\n minWidth: pane.minWidth ?? DEFAULT_MIN,\n maxWidth: pane.maxWidth ?? DEFAULT_MAX,\n direction: \"right-edge\",\n });\n\n const isFullWidth = pane.fullWidth === true;\n\n return (\n <div\n className={cn(\n \"relative flex flex-col border-r bg-background\",\n isFullWidth ? \"flex-1\" : \"shrink-0\",\n )}\n style={isFullWidth ? undefined : { width: panelWidth }}\n >\n {/* Header */}\n <div className=\"flex h-10 shrink-0 items-center justify-end border-b px-2\">\n <button\n onClick={onClose}\n className={cn(\n \"flex h-6 w-6 items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground transition-colors\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n )}\n aria-label=\"Close side pane\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-auto\">{pane.content}</div>\n\n {!isFullWidth && (\n <DragHandle\n edge=\"right\"\n separatorProps={separatorProps}\n onPointerDown={handlePointerDown}\n onDoubleClick={handleDoubleClick}\n />\n )}\n </div>\n );\n}\n","/**\n * Side pane state types and context.\n *\n * The side pane is a persistent, resizable inline panel that sits between\n * the SubNavPanel and the main content area. It shrinks the content area\n * rather than overlaying it -- the main view stays visible and interactive.\n *\n * Modules open/close/toggle it via the imperative sidePane API in api.ts.\n * AppShell owns the state and injects it here via SidePaneContext so that\n * SidePane.tsx can read it without coupling to AppShell directly.\n */\nimport { createContext, useContext, type ReactNode } from \"react\";\n\nexport interface SidePaneOptions {\n /** Content to render inside the side pane. */\n content: ReactNode;\n /**\n * Stable key identifying which module/tool owns this pane instance.\n * Used by IconRail to highlight the active pane module and by toggle()\n * to detect whether the same content is already open.\n */\n moduleId: string;\n /** Initial width in pixels. Defaults to 320. */\n defaultWidth?: number;\n /** Minimum draggable width in pixels. Defaults to 240. */\n minWidth?: number;\n /**\n * Maximum draggable width in pixels.\n * Omit or pass Infinity for unlimited (fills remaining space up to viewport).\n */\n maxWidth?: number;\n /**\n * When true, the pane expands to fill the entire content area (flex-1),\n * collapsing the main route content to zero width. Use for full-screen\n * tool views (schema viewer, form editors). The drag handle is hidden.\n * Toggle via sidePane.setFullWidth(boolean) from inside the pane content.\n */\n fullWidth?: boolean;\n}\n\nexport interface SidePaneState {\n /** Currently open pane options, or null when closed. */\n pane: SidePaneOptions | null;\n open: (opts: SidePaneOptions) => void;\n close: () => void;\n toggle: (opts: SidePaneOptions) => void;\n /** Expand or collapse the pane to fill the full content area. */\n setFullWidth: (fullWidth: boolean) => void;\n}\n\nexport const SidePaneContext = createContext<SidePaneState | null>(null);\n\n/**\n * Read the side pane state from context.\n * Must be called within an AppShell (which provides the context).\n */\nexport function useSidePaneState(): SidePaneState {\n const ctx = useContext(SidePaneContext);\n if (!ctx) {\n throw new Error(\"useSidePaneState must be used within an AppShell\");\n }\n return ctx;\n}\n","/**\n * DragHandle — shared resize handle used by SidePane (right-edge) and\n * SlideOverPanel (left-edge).\n *\n * Invisible at rest. On hover shows a 2px primary line; while actively\n * dragging the line thickens to 4px. The hit area is always 8px wide\n * regardless of visual state.\n */\nimport type React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\n\ninterface DragHandleProps {\n /** Which edge of the parent the handle sits on. */\n edge: \"left\" | \"right\";\n separatorProps: React.HTMLAttributes<HTMLDivElement>;\n onPointerDown: React.PointerEventHandler<HTMLDivElement>;\n onDoubleClick: React.MouseEventHandler<HTMLDivElement>;\n}\n\nexport function DragHandle({\n edge,\n separatorProps,\n onPointerDown,\n onDoubleClick,\n}: DragHandleProps) {\n return (\n <div\n {...separatorProps}\n onPointerDown={onPointerDown}\n onDoubleClick={onDoubleClick}\n className={cn(\n // 8px invisible hit area; visual feedback only via the before pseudo-element.\n \"absolute top-0 h-full w-2 cursor-col-resize\",\n edge === \"right\" ? \"right-0\" : \"left-0\",\n // Centered line: invisible at rest, 2px on hover, 4px while dragging.\n \"before:absolute before:inset-y-0 before:left-1/2 before:-translate-x-1/2\",\n \"before:w-0 before:transition-all before:duration-150\",\n \"hover:before:w-0.5 hover:before:bg-primary/60\",\n \"active:before:w-1 active:before:bg-primary/80\",\n )}\n />\n );\n}\n","import { useEffect, useCallback, useMemo } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport {\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n} from \"@petrarca/sonnet-ui\";\nimport { useShellModules } from \"./shellModules\";\nimport { useShellConfig } from \"./shellConfig\";\nimport { evaluateModule, resolveCapabilityPolicy } from \"./capabilities\";\nimport type { ShellModule } from \"./types\";\n\ninterface CommandMenuProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\n/**\n * CommandMenu -- Cmd+K command menu.\n *\n * Indexes all navigation items from the service domains so users can\n * quickly jump to any page by typing.\n */\nexport default function CommandMenu({ open, onOpenChange }: CommandMenuProps) {\n const navigate = useNavigate();\n const { mainModules, bottomModules } = useShellModules();\n const { resolveCapability, capabilityPolicy } = useShellConfig();\n\n // Global Cmd+K / Ctrl+K shortcut\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"k\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n onOpenChange(!open);\n }\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, onOpenChange]);\n\n const policy = useMemo(\n () => resolveCapabilityPolicy(capabilityPolicy),\n [capabilityPolicy],\n );\n\n // Evaluate a module's requirements for the \"command\" surface.\n const moduleEffect = (service: ShellModule) =>\n evaluateModule(service, \"command\", resolveCapability, policy);\n\n const handleSelect = useCallback(\n (path: string) => {\n navigate(path);\n onOpenChange(false);\n },\n [navigate, onOpenChange],\n );\n\n const renderNavGroup = (service: ShellModule) => {\n if (service.navigation.length === 0) return null;\n const { effect, reason } = moduleEffect(service);\n // hidden / redirect ⇒ omit from the palette; disabled ⇒ show greyed.\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n const Icon = service.icon;\n return (\n <CommandGroup key={`nav-${service.id}`} heading={service.label}>\n {service.navigation.flatMap((group) =>\n group.links.map((link) => (\n <CommandItem\n key={link.path}\n value={`${service.label} ${group.heading ?? \"\"} ${link.label}`}\n disabled={disabled}\n onSelect={disabled ? undefined : () => handleSelect(link.path)}\n >\n <Icon className=\"mr-2 h-4 w-4 text-muted-foreground\" />\n <span>{link.label}</span>\n {disabled && reason ? (\n <span className=\"ml-auto text-xs text-muted-foreground\">\n {reason}\n </span>\n ) : (\n group.heading && (\n <span className=\"ml-auto text-xs text-muted-foreground\">\n {group.heading}\n </span>\n )\n )}\n </CommandItem>\n )),\n )}\n </CommandGroup>\n );\n };\n\n const renderCommandGroup = (service: ShellModule) => {\n if (!service.commands?.length) return null;\n const { effect, reason } = moduleEffect(service);\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n const Icon = service.icon;\n return (\n <CommandGroup\n key={`cmd-${service.id}`}\n heading={service.commands[0].group ?? service.label}\n >\n {service.commands.map((cmd) => {\n const CmdIcon = cmd.icon ?? Icon;\n return (\n <CommandItem\n key={cmd.id}\n value={`${service.label} ${cmd.label}`}\n disabled={disabled}\n onSelect={\n disabled\n ? undefined\n : () => {\n cmd.action();\n onOpenChange(false);\n }\n }\n >\n <CmdIcon className=\"mr-2 h-4 w-4 text-muted-foreground\" />\n <span>{cmd.label}</span>\n {disabled && reason && (\n <span className=\"ml-auto text-xs text-muted-foreground\">\n {reason}\n </span>\n )}\n </CommandItem>\n );\n })}\n </CommandGroup>\n );\n };\n\n const allModules = [...mainModules, ...bottomModules];\n\n // A module group has visible nav items when: it has navigation links AND its\n // capability effect is not hidden/redirect. Pre-compute to guard separators.\n const hasVisibleNav = (modules: ShellModule[]) =>\n modules.some(\n (m) =>\n m.navigation.length > 0 &&\n ![\"hidden\", \"redirect\"].includes(moduleEffect(m).effect),\n );\n const hasVisibleCmds = allModules.some(\n (m) =>\n (m.commands?.length ?? 0) > 0 &&\n ![\"hidden\", \"redirect\"].includes(moduleEffect(m).effect),\n );\n\n return (\n <CommandDialog open={open} onOpenChange={onOpenChange}>\n <CommandInput placeholder=\"Search pages, services, actions...\" />\n <CommandList>\n <CommandEmpty>No results found.</CommandEmpty>\n\n {mainModules.map(renderNavGroup)}\n\n {/* Only show the separator when both groups contribute visible items,\n to avoid a dangling separator when all modules in one group are\n capability-gated to hidden. */}\n {hasVisibleNav(mainModules) && hasVisibleNav(bottomModules) && (\n <CommandSeparator />\n )}\n\n {bottomModules.map(renderNavGroup)}\n\n {hasVisibleCmds && (\n <>\n <CommandSeparator />\n {allModules.map(renderCommandGroup)}\n </>\n )}\n </CommandList>\n </CommandDialog>\n );\n}\n","/**\n * ModuleEffects -- runs each module's optional `useEffects` hook.\n *\n * Mounted once by AppShell inside the shell tree so module effects have access\n * to Router / QueryClient / provider context. Lets a module own its reactions\n * to app state (e.g. subscribe to a shell event and invalidate its own caches)\n * instead of centralizing that knowledge in app-level code.\n *\n * Each module gets its own host component so its `useEffects` hook is called\n * unconditionally from a stable component (Rules of Hooks). The module set must\n * be stable for the app's lifetime (it is — modules are declared at load time).\n *\n * A per-host `ModuleEffectBoundary` isolates errors: if one module's hook\n * throws, only that module's effects are lost — the rest of the shell is\n * unaffected.\n */\nimport React from \"react\";\nimport { warnLog } from \"@petrarca/sonnet-core\";\nimport type { ShellModule } from \"./types\";\n\ninterface BoundaryState {\n caught: boolean;\n}\n\n/** Minimal error boundary isolating a single module's useEffects hook. */\nclass ModuleEffectBoundary extends React.Component<\n { moduleId: string; children: React.ReactNode },\n BoundaryState\n> {\n state: BoundaryState = { caught: false };\n\n static getDerivedStateFromError(): BoundaryState {\n return { caught: true };\n }\n\n componentDidCatch(error: unknown) {\n warnLog(\n \"[ModuleEffects] module '{}' useEffects threw -- effects disabled for this module: {}\",\n this.props.moduleId,\n error,\n );\n }\n\n render() {\n return this.state.caught ? null : this.props.children;\n }\n}\n\nfunction ModuleEffectHost({ module }: { module: ShellModule }) {\n // Safe: render only mounts a host for modules that declare useEffects (see\n // ModuleEffects), and the module identity is stable, so this hook is called\n // unconditionally for the component's lifetime.\n module.useEffects!();\n return null;\n}\n\nexport function ModuleEffects({ modules }: { modules: ShellModule[] }) {\n return (\n <>\n {modules\n .filter((m) => typeof m.useEffects === \"function\")\n .map((m) => (\n <ModuleEffectBoundary key={m.id} moduleId={m.id}>\n <ModuleEffectHost module={m} />\n </ModuleEffectBoundary>\n ))}\n </>\n );\n}\n","import React from \"react\";\nimport { Toaster } from \"sonner\";\nimport AppShell from \"./AppShell\";\nimport { ShellConfigProvider } from \"./ShellConfigProvider\";\nimport type { ShellConfig } from \"./shellConfig\";\nimport type { ModuleRegistry } from \"./createModuleRegistry\";\n\n/**\n * RootLayout -- top-level layout that wraps the entire authenticated app.\n * Renders the Toaster for notifications and the AppShell (icon rail + sub-nav + content).\n */\n\ninterface RootLayoutProps {\n config: ShellConfig;\n registry: ModuleRegistry;\n /** Optional custom sidebar replacing the default IconRail + SubNavPanel. */\n sidebar?: React.ReactNode;\n}\n\nexport default function RootLayout({\n config,\n registry,\n sidebar,\n}: RootLayoutProps) {\n return (\n <ShellConfigProvider config={config}>\n <Toaster position=\"top-right\" richColors closeButton />\n <AppShell registry={registry} sidebar={sidebar} />\n </ShellConfigProvider>\n );\n}\n","/**\n * ShellConfigProvider -- wraps the shell with configuration context.\n */\nimport type { ReactNode } from \"react\";\nimport { ShellConfigContext, type ShellConfig } from \"./shellConfig\";\n\ninterface ShellConfigProviderProps {\n config: ShellConfig;\n children: ReactNode;\n}\n\nexport function ShellConfigProvider({\n config,\n children,\n}: ShellConfigProviderProps) {\n return (\n <ShellConfigContext.Provider value={config}>\n {children}\n </ShellConfigContext.Provider>\n );\n}\n","import { useLocation } from \"react-router-dom\";\n\n/**\n * PlaceholderPage — generic wireframe page for routes not yet implemented.\n * Shows the current path and a placeholder content area.\n */\nexport default function PlaceholderPage() {\n const { pathname } = useLocation();\n\n return (\n <div>\n <h1 className=\"mb-1\">{pathToTitle(pathname)}</h1>\n <p className=\"text-sm text-muted-foreground mb-6\">{pathname}</p>\n\n {/* Wireframe content placeholder */}\n <div className=\"rounded-lg border-2 border-dashed border-border p-12 flex items-center justify-center\">\n <span className=\"text-sm text-muted-foreground\">\n Content area — not yet implemented\n </span>\n </div>\n </div>\n );\n}\n\n/** Convert a path like \"/healthcare/patients\" to \"Patients\" */\nfunction pathToTitle(path: string): string {\n const last = path.split(\"/\").filter(Boolean).pop();\n if (!last) return \"Dashboard\";\n return last\n .split(\"-\")\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n}\n","/**\n * Sidebar — single-column navigation layout.\n *\n * Alternative to the IconRail + SubNavPanel two-column pattern.\n * Renders an optional header, a scrollable body with SidebarGroup /\n * SidebarItem children, and takes its full height from the shell.\n */\nimport React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { ScrollArea } from \"@petrarca/sonnet-ui\";\n\nexport interface SidebarProps {\n children: React.ReactNode;\n /** Optional header rendered above the scrollable nav (e.g. app name, collapse button). */\n header?: React.ReactNode;\n width?: number;\n className?: string;\n}\n\nexport function Sidebar({\n children,\n header,\n width = 260,\n className,\n}: SidebarProps): React.ReactElement {\n return (\n <aside\n style={{ width }}\n className={cn(\n \"shrink-0 border-r bg-muted/30 flex flex-col h-full\",\n className,\n )}\n >\n {header}\n <ScrollArea className=\"flex-1\">\n <nav className=\"px-1.5 py-2 space-y-3\">{children}</nav>\n </ScrollArea>\n </aside>\n );\n}\n","/**\n * ShellSidebar — single-column sidebar layout, auto-wired from the\n * module registry.\n *\n * Reads module list from ShellModulesContext (provided by AppShell).\n * For custom layouts, compose Sidebar / SidebarGroup / SidebarItem directly.\n */\nimport React, { useMemo } from \"react\";\nimport { useShellModules } from \"./shellModules\";\nimport { useShellConfig } from \"./shellConfig\";\nimport { evaluateModule, resolveCapabilityPolicy } from \"./capabilities\";\nimport { Sidebar } from \"./Sidebar\";\nimport type { SidebarProps } from \"./Sidebar\";\nimport { SidebarGroup } from \"./SidebarGroup\";\nimport { SidebarItem } from \"./SidebarItem\";\nimport type { ModuleEffect } from \"./capabilities\";\n\nexport type ShellSidebarProps = Omit<SidebarProps, \"children\"> & {\n /** Label for the bottom-pinned modules group. Default: \"Tools\". */\n bottomGroupLabel?: string;\n};\n\nexport function ShellSidebar({\n bottomGroupLabel = \"Tools\",\n ...sidebarProps\n}: ShellSidebarProps): React.ReactElement {\n const { mainModules, bottomModules } = useShellModules();\n const { resolveCapability, capabilityPolicy } = useShellConfig();\n\n const policy = useMemo(\n () => resolveCapabilityPolicy(capabilityPolicy),\n [capabilityPolicy],\n );\n\n // Capability gating uses the shared \"rail\" surface: the sidebar and the icon\n // rail are two renderings of the same primary navigation, so one policy\n // governs both. hidden/redirect drop the module; disabled greys its rows.\n const effectOf = React.useCallback(\n (moduleId: string): ModuleEffect => {\n const module = [...mainModules, ...bottomModules].find(\n (m) => m.id === moduleId,\n );\n if (!module) return { effect: \"visible\" };\n return evaluateModule(module, \"rail\", resolveCapability, policy);\n },\n [mainModules, bottomModules, resolveCapability, policy],\n );\n\n // Collect topNav links from modules that are not hidden, tagging each with\n // its owning module's effect so a disabled module's links render disabled.\n const topNavItems = mainModules.flatMap((mod) => {\n const { effect, reason } = effectOf(mod.id);\n if (effect === \"hidden\" || effect === \"redirect\") return [];\n const disabled = effect === \"disabled\";\n return (mod.topNav ?? []).map((link) => ({\n link,\n icon: mod.icon,\n disabled,\n reason,\n }));\n });\n\n return (\n <Sidebar {...sidebarProps}>\n {/* Top fixed zone — no heading, separator below */}\n {topNavItems.length > 0 && (\n <SidebarGroup separator>\n {topNavItems.map(({ link, icon, disabled, reason }) => (\n <SidebarItem\n key={link.path}\n icon={icon}\n label={link.label}\n path={link.path}\n badge={link.badge}\n disabled={disabled}\n title={disabled ? reason : undefined}\n />\n ))}\n </SidebarGroup>\n )}\n\n {/* Grouped sections */}\n {mainModules.map((mod) => {\n const { effect, reason } = effectOf(mod.id);\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n const title = disabled ? reason : undefined;\n\n if (mod.navigation.length === 0) {\n return (\n <SidebarItem\n key={mod.id}\n icon={mod.icon}\n label={mod.label}\n path={mod.basePath}\n disabled={disabled}\n title={title}\n />\n );\n }\n return (\n <SidebarGroup key={mod.id} heading={mod.label} collapsible>\n {mod.navigation.flatMap((group) => [\n // Render NavGroup heading if it has one\n ...(group.heading\n ? [\n <SidebarGroup\n key={group.id}\n heading={group.heading}\n collapsible={group.collapsible}\n >\n {group.links.map((link) => (\n <SidebarItem\n key={link.path}\n icon={mod.icon}\n label={link.label}\n path={link.path}\n badge={link.badge}\n disabled={disabled}\n title={disabled ? reason : undefined}\n />\n ))}\n </SidebarGroup>,\n ]\n : group.links.map((link) => (\n <SidebarItem\n key={link.path}\n icon={mod.icon}\n label={link.label}\n path={link.path}\n badge={link.badge}\n disabled={disabled}\n title={disabled ? reason : undefined}\n />\n ))),\n ])}\n </SidebarGroup>\n );\n })}\n\n {bottomModules.length > 0 && (\n <SidebarGroup heading={bottomGroupLabel} collapsible>\n {bottomModules.map((mod) => {\n const { effect, reason } = effectOf(mod.id);\n if (effect === \"hidden\" || effect === \"redirect\") return null;\n const disabled = effect === \"disabled\";\n return (\n <SidebarItem\n key={mod.id}\n icon={mod.icon}\n label={mod.label}\n path={mod.basePath}\n disabled={disabled}\n title={disabled ? reason : undefined}\n />\n );\n })}\n </SidebarGroup>\n )}\n </Sidebar>\n );\n}\n","/**\n * ShellVersion — displays the app name and optional version in the footer.\n *\n * Typically used as ShellConfig.footer content alongside ShellFooter.\n * The consumer provides name and version from their own package.json.\n */\nimport React from \"react\";\n\nexport interface ShellVersionProps {\n /** Application name. */\n name: string;\n /** Version string (e.g. from package.json). Omit to show name only. */\n version?: string;\n}\n\nexport function ShellVersion({\n name,\n version,\n}: ShellVersionProps): React.ReactElement {\n return (\n <div className=\"flex items-center gap-2\">\n <span>{name}</span>\n {version && <span className=\"text-tertiary-foreground\">v{version}</span>}\n </div>\n );\n}\n","import { Search } from \"lucide-react\";\nimport { Button } from \"@petrarca/sonnet-ui\";\nimport { navigation } from \"./api\";\n\n/**\n * SearchTrigger -- Cmd+K search button for the TopBar.\n *\n * Opens the shell CommandMenu via the imperative navigation API.\n * Generic building block, no app-specific dependencies.\n */\nexport function SearchTrigger() {\n return (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-8 gap-2 text-sm text-muted-foreground w-56 justify-start\"\n onClick={() => navigation.openCommandMenu()}\n >\n <Search className=\"h-4 w-4\" />\n <span>Search...</span>\n <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\">\n <span className=\"text-xs\">&#x2318;</span>K\n </kbd>\n </Button>\n );\n}\n","import { LogOut, User } from \"lucide-react\";\nimport { Button } from \"@petrarca/sonnet-ui\";\nimport { Avatar, AvatarFallback } from \"@petrarca/sonnet-ui\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@petrarca/sonnet-ui\";\n\n/**\n * UserMenu -- avatar dropdown for the TopBar.\n *\n * Shows the user's initials in an avatar. The dropdown displays name,\n * email, a profile link, and a sign-out action. Generic building block,\n * no app-specific dependencies.\n */\n\nexport interface UserMenuUser {\n name: string;\n email: string;\n initials: string;\n}\n\ninterface UserMenuProps {\n user: UserMenuUser;\n onSignOut?: () => void;\n}\n\nexport function UserMenu({ user, onSignOut }: UserMenuProps) {\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"compact\" className=\"rounded-full\">\n <Avatar className=\"h-7 w-7\">\n <AvatarFallback className=\"text-xs\">{user.initials}</AvatarFallback>\n </Avatar>\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-48\">\n <DropdownMenuLabel className=\"font-normal\">\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-sm font-medium\">{user.name}</p>\n <p className=\"text-xs text-muted-foreground\">{user.email}</p>\n </div>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem>\n <User className=\"mr-2 h-4 w-4\" />\n Profile\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem onSelect={onSignOut}>\n <LogOut className=\"mr-2 h-4 w-4\" />\n Sign out\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n","/**\n * Module registry factory.\n *\n * Accepts a list of ShellModules and returns derived data structures\n * consumed by the shell (icon rail groups, flattened routes, extension\n * point lookup). This is the reusable core; the app-specific module\n * list lives in the consumer (e.g. modules/registry.ts).\n */\nimport type { RouteObject } from \"react-router-dom\";\nimport type { ShellModule, Contribution } from \"./types\";\n\nexport interface ModuleRegistry {\n /** All modules in registration order. */\n modules: ShellModule[];\n\n /** Visible modules shown in the main area of the icon rail. */\n mainModules: ShellModule[];\n\n /** Modules pinned to the bottom of the icon rail. */\n bottomModules: ShellModule[];\n\n /** All routes from all modules, flattened. */\n allRoutes: RouteObject[];\n\n /** Get contributions for a named extension point, sorted by order. */\n getExtensionPoint: (name: string) => Contribution[];\n}\n\nexport function createModuleRegistry(modules: ShellModule[]): ModuleRegistry {\n const visible = modules.filter((m) => !m.hidden);\n const mainModules = visible.filter((m) => !m.pinBottom);\n const bottomModules = visible.filter((m) => m.pinBottom);\n const allRoutes = modules.flatMap((m) => m.routes);\n\n // Pre-collect all contributions for O(n) lookup per call.\n const contributionsByPoint = new Map<string, Contribution[]>();\n for (const m of modules) {\n for (const c of m.contributions ?? []) {\n const list = contributionsByPoint.get(c.extensionPoint) ?? [];\n list.push(c);\n contributionsByPoint.set(c.extensionPoint, list);\n }\n }\n // Sort each group once.\n for (const list of contributionsByPoint.values()) {\n list.sort((a, b) => a.order - b.order);\n }\n\n function getExtensionPoint(name: string): Contribution[] {\n return contributionsByPoint.get(name) ?? [];\n }\n\n return { modules, mainModules, bottomModules, allRoutes, getExtensionPoint };\n}\n","/**\n * OverviewCard and FeatureLink -- building blocks for module overview pages.\n *\n * OverviewCard presents a feature with icon, title, description, and optional\n * navigation via a stable feature id.\n *\n * FeatureLink is an inline text link for use in descriptions or prose,\n * also navigating via stable feature id.\n *\n * Part of the shell -- extracts to @petrarca/sonnet-shell.\n */\n\nimport React from \"react\";\nimport { cn } from \"@petrarca/sonnet-core\";\nimport { navigation } from \"./api\";\n\ninterface OverviewCardProps {\n /** Lucide icon component. */\n icon: React.ComponentType<{ className?: string }>;\n /** Card title. */\n title: string;\n /** Description -- plain string or React nodes (e.g. with FeatureLink). */\n description: React.ReactNode;\n /**\n * Stable feature identifier (e.g. \"auditing.audit-events\").\n * Resolved to a path via the shell registry at click time.\n * Omit for non-navigable cards.\n */\n feature?: string;\n /** Additional CSS class names. */\n className?: string;\n}\n\n/**\n * FeatureLink -- inline text link navigating to a feature by stable id.\n *\n * Use inside OverviewCard descriptions or overview page prose to link\n * to a feature without hardcoding paths.\n *\n * @example\n * <FeatureLink feature=\"auditing.audit-log\">Audit Log</FeatureLink>\n */\nexport function FeatureLink({\n feature,\n children,\n}: {\n feature: string;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <span\n role=\"link\"\n tabIndex={0}\n onClick={(e) => {\n e.stopPropagation();\n navigation.goToFeature(feature);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n e.stopPropagation();\n navigation.goToFeature(feature);\n }\n }}\n className=\"underline underline-offset-2 hover:text-foreground transition-colors cursor-pointer\"\n >\n {children}\n </span>\n );\n}\n\n/** Feature card for module overview pages. Navigates via the shell API. */\nexport function OverviewCard({\n icon: Icon,\n title,\n description,\n feature,\n className,\n}: OverviewCardProps): React.ReactElement {\n return (\n <button\n onClick={feature ? () => navigation.goToFeature(feature) : undefined}\n disabled={!feature}\n className={cn(\n \"rounded-lg border bg-card p-5 text-left flex flex-col gap-3\",\n \"disabled:cursor-default\",\n feature && \"hover:bg-accent/50 transition-colors\",\n className,\n )}\n >\n <div className=\"h-9 w-9 rounded-md bg-blue-50 flex items-center justify-center shrink-0\">\n <Icon className=\"h-5 w-5 text-blue-600\" />\n </div>\n <div>\n <p className=\"text-sm font-medium mb-1\">{title}</p>\n <p className=\"text-xs text-muted-foreground leading-relaxed\">\n {description}\n </p>\n </div>\n </button>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","useRef","useMemo","useResizablePanel","useLocation","useNavigate","ScrollArea","jsx","jsx","useMemo","Button","jsx","jsxs","useMemo","Tooltip","TooltipContent","TooltipTrigger","warnLog","warnLog","cn","jsx","jsxs","cn","jsx","jsxs","jsx","jsxs","useMemo","Tooltip","TooltipTrigger","TooltipContent","createContext","useContext","createContext","useContext","warnLog","jsx","jsxs","useMemo","cn","createContext","useContext","cn","jsx","jsx","jsxs","cn","useEffect","useMemo","Fragment","jsx","jsxs","useEffect","useMemo","React","warnLog","Fragment","jsx","Fragment","jsx","jsxs","useRef","useEffect","useLocation","useMemo","useCallback","useState","useSidePaneState","useNavigate","sidePane","panel","dialog","useResizablePanel","ScrollArea","jsx","jsx","jsxs","useLocation","jsx","jsxs","cn","ScrollArea","jsx","jsxs","React","useMemo","jsx","jsxs","useMemo","React","jsx","jsxs","Button","jsx","jsxs","Button","Button","jsx","jsxs","cn","jsx","jsxs","cn"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@petrarca/sonnet-shell",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Application shell, layout, navigation, auth flow, and imperative API for the Petrarca Sonnet component library",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -25,8 +25,8 @@
25
25
  "dependencies": {
26
26
  "lucide-react": "^0.553.0",
27
27
  "sonner": "^2.0.7",
28
- "@petrarca/sonnet-core": "0.7.0",
29
- "@petrarca/sonnet-ui": "0.7.0"
28
+ "@petrarca/sonnet-core": "0.8.0",
29
+ "@petrarca/sonnet-ui": "0.8.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=19",