@petrarca/sonnet-shell 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { RouteObject } from 'react-router-dom';
3
2
  import * as React from 'react';
4
3
  import React__default, { ReactNode } from 'react';
4
+ import { RouteObject } from 'react-router-dom';
5
5
  import { LucideIcon } from 'lucide-react';
6
6
 
7
7
  interface SidePaneOptions {
@@ -50,7 +50,7 @@ declare function useSidePaneState(): SidePaneState;
50
50
  * Shell type definitions.
51
51
  *
52
52
  * These types define the contract between the shell and modules.
53
- * Modules implement ServiceModule; the shell consumes it.
53
+ * Modules implement ShellModule; the shell consumes it.
54
54
  */
55
55
 
56
56
  interface NavLink {
@@ -62,17 +62,21 @@ interface NavLink {
62
62
  id?: string;
63
63
  label: string;
64
64
  path: string;
65
+ /** Optional trailing badge (count or status text) for sidebar display. */
66
+ badge?: string | number;
65
67
  }
66
68
  interface NavGroup {
67
69
  /** Stable identifier used as React key. Defaults to heading if omitted. */
68
70
  id: string;
69
71
  heading?: string;
70
72
  links: NavLink[];
73
+ /** Whether this group can be collapsed in sidebar mode. */
74
+ collapsible?: boolean;
71
75
  }
72
76
  /**
73
77
  * An action contributed by a module to the CommandMenu.
74
78
  *
75
- * Modules declare commands in ServiceModule.commands[]. The CommandMenu
79
+ * Modules declare commands in ShellModule.commands[]. The CommandMenu
76
80
  * renders them in a dedicated group so users can trigger module actions
77
81
  * (open side pane, trigger a workflow, etc.) directly from Cmd+K without
78
82
  * navigating to a route first.
@@ -123,7 +127,7 @@ interface Contribution {
123
127
  interface ShellEventMap {
124
128
  [custom: string]: unknown;
125
129
  }
126
- interface ServiceModule {
130
+ interface ShellModule {
127
131
  /** Unique identifier for this module */
128
132
  id: string;
129
133
  /** Display name (icon rail tooltip, sub-nav header, command menu) */
@@ -134,7 +138,14 @@ interface ServiceModule {
134
138
  icon: LucideIcon;
135
139
  /** URL prefix used to match this module to the current route */
136
140
  basePath: string;
137
- /** Navigation groups for the sub-nav panel */
141
+ /**
142
+ * Fixed top-level navigation links shown in the sidebar's top zone
143
+ * (no group heading, separator below). Not shown in the icon-rail
144
+ * sub-nav panel — they are sidebar-specific shortcuts.
145
+ * Optional — omit if all navigation belongs in grouped sections.
146
+ */
147
+ topNav?: NavLink[];
148
+ /** Navigation groups for the sub-nav panel and sidebar grouped sections */
138
149
  navigation: NavGroup[];
139
150
  /** React Router route definitions owned by this module */
140
151
  routes: RouteObject[];
@@ -188,12 +199,55 @@ interface ServiceModule {
188
199
  * canvases, or any route that manages its own scroll and height.
189
200
  */
190
201
  layout?: "default" | "full";
202
+ /**
203
+ * Optional module-owned effect hook, invoked once by the shell inside the
204
+ * shell tree (so it has Router / QueryClient / provider context available).
205
+ *
206
+ * Use this for app-state reactions that belong to the module rather than to
207
+ * central app code — e.g. subscribing to a shell event and invalidating the
208
+ * module's own caches. The shell calls it unconditionally on every render, so
209
+ * it MUST obey the Rules of Hooks (call hooks unconditionally, no early
210
+ * returns) and the set of modules MUST be stable for the app's lifetime.
211
+ *
212
+ * It renders no UI; return nothing.
213
+ */
214
+ useEffects?: () => void;
215
+ /**
216
+ * Capabilities this module requires to be available, as "domain:name" strings
217
+ * (e.g. "state:graph.selected", "auth:templates.read"). Combined with implicit
218
+ * AND. Absent ⇒ the module is always available.
219
+ *
220
+ * The shell does not interpret these — the app's `resolveCapability` evaluates
221
+ * each one. See docs/design/module-capabilities.md.
222
+ */
223
+ requires?: string[];
224
+ }
225
+ /** Semantic reason a capability is unmet. Drives the presentation matrix. */
226
+ type UnmetKind = "missing-state" | "not-authorized" | "not-authorized-soft" | "feature-off" | "error";
227
+ /** Result of evaluating a single capability. Decision only — never presentation. */
228
+ interface CapabilityResult {
229
+ met: boolean;
230
+ /** Why the capability is unmet (only when met === false). */
231
+ kind?: UnmetKind;
232
+ /** Optional human-readable hint, surfaced in tooltips / adjacent text. */
233
+ reason?: string;
191
234
  }
235
+ /** App-provided function that evaluates a single capability string. */
236
+ type CapabilityResolver = (capability: string) => CapabilityResult;
237
+ /** A shell surface that gates modules. */
238
+ type Surface = "rail" | "command" | "route";
239
+ /** How an unavailable module renders on a surface. */
240
+ type Effect = "visible" | "disabled" | "hidden" | "redirect";
241
+ /**
242
+ * Maps (unmet reason kind, surface) → effect. Sparse overrides are merged over
243
+ * the shell defaults (`DEFAULT_CAPABILITY_POLICY`).
244
+ */
245
+ type CapabilityPolicy = Partial<Record<UnmetKind, Partial<Record<Surface, Effect>>>>;
192
246
 
193
247
  /**
194
248
  * Module registry factory.
195
249
  *
196
- * Accepts a list of ServiceModules and returns derived data structures
250
+ * Accepts a list of ShellModules and returns derived data structures
197
251
  * consumed by the shell (icon rail groups, flattened routes, extension
198
252
  * point lookup). This is the reusable core; the app-specific module
199
253
  * list lives in the consumer (e.g. modules/registry.ts).
@@ -201,22 +255,29 @@ interface ServiceModule {
201
255
 
202
256
  interface ModuleRegistry {
203
257
  /** All modules in registration order. */
204
- modules: ServiceModule[];
258
+ modules: ShellModule[];
205
259
  /** Visible modules shown in the main area of the icon rail. */
206
- mainModules: ServiceModule[];
260
+ mainModules: ShellModule[];
207
261
  /** Modules pinned to the bottom of the icon rail. */
208
- bottomModules: ServiceModule[];
262
+ bottomModules: ShellModule[];
209
263
  /** All routes from all modules, flattened. */
210
264
  allRoutes: RouteObject[];
211
265
  /** Get contributions for a named extension point, sorted by order. */
212
266
  getExtensionPoint: (name: string) => Contribution[];
213
267
  }
214
- declare function createModuleRegistry(modules: ServiceModule[]): ModuleRegistry;
268
+ declare function createModuleRegistry(modules: ShellModule[]): ModuleRegistry;
215
269
 
216
270
  interface AppShellProps {
217
271
  registry: ModuleRegistry;
272
+ /**
273
+ * Optional custom sidebar replacing the default IconRail + SubNavPanel.
274
+ * When provided, the shell renders this element in place of the two-column
275
+ * navigation chrome. Use the Sidebar / SidebarGroup / SidebarItem components
276
+ * from this package to build a single-column navigation layout.
277
+ */
278
+ sidebar?: React__default.ReactNode;
218
279
  }
219
- declare function AppShell({ registry }: AppShellProps): react_jsx_runtime.JSX.Element;
280
+ declare function AppShell({ registry, sidebar }: AppShellProps): react_jsx_runtime.JSX.Element;
220
281
 
221
282
  interface ShellConfig {
222
283
  /**
@@ -229,6 +290,27 @@ interface ShellConfig {
229
290
  * components.
230
291
  */
231
292
  topBar?: ReactNode;
293
+ /**
294
+ * Content rendered inside the ShellFooter. The shell provides the
295
+ * `<footer>` element with its sizing and border; the consumer owns
296
+ * everything inside it.
297
+ *
298
+ * Common uses: status bar, sync indicators, version info, quick actions.
299
+ * Omit to render no footer.
300
+ */
301
+ footer?: ReactNode;
302
+ /**
303
+ * Evaluates a module capability (from `ShellModule.requires`) for gating the
304
+ * icon rail / command menu / routes. Absent ⇒ no gating (all modules
305
+ * available). See docs/design/module-capabilities.md.
306
+ */
307
+ resolveCapability?: CapabilityResolver;
308
+ /**
309
+ * Sparse overrides for the (unmet reason kind, surface) → effect matrix,
310
+ * merged over the shell defaults. Lets the app tune hide-vs-disable per
311
+ * reason kind and surface without touching modules.
312
+ */
313
+ capabilityPolicy?: CapabilityPolicy;
232
314
  }
233
315
  /**
234
316
  * Read the shell configuration from context.
@@ -243,8 +325,10 @@ declare function useShellConfig(): ShellConfig;
243
325
  interface RootLayoutProps {
244
326
  config: ShellConfig;
245
327
  registry: ModuleRegistry;
328
+ /** Optional custom sidebar replacing the default IconRail + SubNavPanel. */
329
+ sidebar?: React__default.ReactNode;
246
330
  }
247
- declare function RootLayout({ config, registry }: RootLayoutProps): react_jsx_runtime.JSX.Element;
331
+ declare function RootLayout({ config, registry, sidebar, }: RootLayoutProps): react_jsx_runtime.JSX.Element;
248
332
 
249
333
  /**
250
334
  * TopBar -- persistent header across the entire application.
@@ -258,32 +342,43 @@ interface TopBarProps {
258
342
  }
259
343
  declare function TopBar({ children }: TopBarProps): react_jsx_runtime.JSX.Element;
260
344
 
261
- interface IconRailProps {
262
- activeServiceId: string | null;
263
- /** moduleId of the module whose side pane is currently open, or null. */
264
- activeSidePaneModuleId: string | null;
265
- onServiceSelect: (serviceId: string) => void;
266
- }
267
345
  /**
268
- * IconRail -- narrow vertical bar with service domain icons.
346
+ * IconRail primitives composable building blocks for the narrow
347
+ * vertical icon strip.
348
+ *
349
+ * - IconRail: the container (<nav> with flex column, border, bg)
350
+ * - RailIcon: a single icon button with tooltip
351
+ * - RailSeparator: divider between groups
269
352
  *
270
- * Main services scroll in the center, settings/playground pinned at bottom.
271
- * Tooltips show service labels on hover.
353
+ * For the auto-wired layout, use ShellRail instead.
272
354
  */
273
- declare function IconRail({ activeServiceId, activeSidePaneModuleId, onServiceSelect, }: IconRailProps): react_jsx_runtime.JSX.Element;
355
+
356
+ interface IconRailProps {
357
+ children: React__default.ReactNode;
358
+ className?: string;
359
+ }
360
+ /** Narrow vertical nav container for RailIcon items. */
361
+ declare function IconRail({ children, className, }: IconRailProps): React__default.ReactElement;
362
+ interface RailIconProps {
363
+ icon: LucideIcon;
364
+ label: string;
365
+ active?: boolean;
366
+ onClick?: () => void;
367
+ /** Render greyed and non-interactive (e.g. a capability requirement is unmet). */
368
+ disabled?: boolean;
369
+ /** Optional tooltip override (e.g. why the item is disabled). Defaults to `label`. */
370
+ tooltip?: string;
371
+ }
372
+ /** Single icon button with tooltip inside an IconRail. */
373
+ declare function RailIcon({ icon: Icon, label, active, onClick, disabled, tooltip, }: RailIconProps): React__default.ReactElement;
374
+ /** Visual divider between icon groups inside an IconRail. */
375
+ declare function RailSeparator(): React__default.ReactElement;
274
376
 
275
377
  interface SubNavPanelProps {
276
- service: ServiceModule;
378
+ service: ShellModule;
277
379
  collapsed: boolean;
278
380
  onToggleCollapse: () => void;
279
381
  }
280
- /**
281
- * SubNavPanel -- contextual secondary navigation for the selected service domain.
282
- *
283
- * Shows the service label at top, then grouped nav links. If other modules
284
- * contribute links via the "<moduleId>.nav" extension point, they are
285
- * appended as an additional group.
286
- */
287
382
  declare function SubNavPanel({ service, collapsed, onToggleCollapse, }: SubNavPanelProps): react_jsx_runtime.JSX.Element;
288
383
 
289
384
  /**
@@ -351,6 +446,97 @@ interface ConfirmDialogProps {
351
446
  */
352
447
  declare function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, variant, onConfirm, onCancel, }: ConfirmDialogProps): React__default.ReactElement;
353
448
 
449
+ /**
450
+ * Sidebar — single-column navigation layout.
451
+ *
452
+ * Alternative to the IconRail + SubNavPanel two-column pattern.
453
+ * Renders an optional header, a scrollable body with SidebarGroup /
454
+ * SidebarItem children, and takes its full height from the shell.
455
+ */
456
+
457
+ interface SidebarProps {
458
+ children: React__default.ReactNode;
459
+ /** Optional header rendered above the scrollable nav (e.g. app name, collapse button). */
460
+ header?: React__default.ReactNode;
461
+ width?: number;
462
+ className?: string;
463
+ }
464
+ declare function Sidebar({ children, header, width, className, }: SidebarProps): React__default.ReactElement;
465
+
466
+ /** Labeled, optionally collapsible section within a Sidebar. */
467
+
468
+ interface SidebarGroupProps {
469
+ heading?: string;
470
+ collapsible?: boolean;
471
+ defaultOpen?: boolean;
472
+ /** Render a border separator below this group. */
473
+ separator?: boolean;
474
+ children: React__default.ReactNode;
475
+ }
476
+ declare function SidebarGroup({ heading, collapsible, defaultOpen, separator, children, }: SidebarGroupProps): React__default.ReactElement;
477
+
478
+ /** Single navigation row inside a Sidebar — icon + label + optional trailing slot. */
479
+
480
+ interface SidebarItemProps {
481
+ icon?: LucideIcon;
482
+ label: string;
483
+ path: string;
484
+ badge?: string | number;
485
+ indent?: number;
486
+ expandable?: boolean;
487
+ expanded?: boolean;
488
+ onToggle?: () => void;
489
+ onClick?: () => void;
490
+ }
491
+ declare function SidebarItem({ icon: Icon, label, path, badge, indent, expandable, expanded, onToggle, onClick, }: SidebarItemProps): React__default.ReactElement;
492
+
493
+ /**
494
+ * ShellRail — icon-rail + sub-nav-panel layout, auto-wired from the
495
+ * module registry and shell navigation context.
496
+ *
497
+ * Reads active service state from ShellNavigationContext (provided by
498
+ * AppShell) and module list from ShellModulesContext. No props required.
499
+ *
500
+ * For custom layouts, compose IconRail, RailIcon, RailSeparator, and
501
+ * SubNavPanel directly.
502
+ */
503
+
504
+ declare function ShellRail(): React__default.ReactElement;
505
+
506
+ /**
507
+ * ShellSidebar — single-column sidebar layout, auto-wired from the
508
+ * module registry.
509
+ *
510
+ * Reads module list from ShellModulesContext (provided by AppShell).
511
+ * For custom layouts, compose Sidebar / SidebarGroup / SidebarItem directly.
512
+ */
513
+
514
+ type ShellSidebarProps = Omit<SidebarProps, "children"> & {
515
+ /** Label for the bottom-pinned modules group. Default: "Tools". */
516
+ bottomGroupLabel?: string;
517
+ };
518
+ declare function ShellSidebar({ bottomGroupLabel, ...sidebarProps }: ShellSidebarProps): React__default.ReactElement;
519
+
520
+ interface ShellFooterProps {
521
+ children?: ReactNode;
522
+ }
523
+ declare function ShellFooter({ children }: ShellFooterProps): react_jsx_runtime.JSX.Element;
524
+
525
+ /**
526
+ * ShellVersion — displays the app name and optional version in the footer.
527
+ *
528
+ * Typically used as ShellConfig.footer content alongside ShellFooter.
529
+ * The consumer provides name and version from their own package.json.
530
+ */
531
+
532
+ interface ShellVersionProps {
533
+ /** Application name. */
534
+ name: string;
535
+ /** Version string (e.g. from package.json). Omit to show name only. */
536
+ version?: string;
537
+ }
538
+ declare function ShellVersion({ name, version, }: ShellVersionProps): React__default.ReactElement;
539
+
354
540
  interface ShellConfigProviderProps {
355
541
  config: ShellConfig;
356
542
  children: ReactNode;
@@ -383,6 +569,25 @@ interface UserMenuProps {
383
569
  }
384
570
  declare function UserMenu({ user, onSignOut }: UserMenuProps): react_jsx_runtime.JSX.Element;
385
571
 
572
+ /** Default (kind, surface) → effect matrix. App overrides are merged over this. */
573
+ declare const DEFAULT_CAPABILITY_POLICY: Record<UnmetKind, Record<Surface, Effect>>;
574
+ /** Merge a sparse app override over the default matrix. */
575
+ declare function resolveCapabilityPolicy(override?: CapabilityPolicy): Record<UnmetKind, Record<Surface, Effect>>;
576
+ interface ModuleEffect {
577
+ effect: Effect;
578
+ /** Hint from the first most-restrictive unmet capability (for tooltips). */
579
+ reason?: string;
580
+ }
581
+ /**
582
+ * Evaluate a module's requirements for a given surface.
583
+ *
584
+ * Returns `visible` when there are no requirements or all are met. Otherwise
585
+ * each unmet capability maps (kind, surface) → effect and the most restrictive
586
+ * wins (hidden > redirect > disabled > visible). When capabilities share the
587
+ * same effect rank, the first one with a non-empty reason provides the tooltip.
588
+ */
589
+ declare function evaluateModule(module: ShellModule, surface: Surface, resolve: CapabilityResolver | undefined, policy: Record<UnmetKind, Record<Surface, Effect>>): ModuleEffect;
590
+
386
591
  /**
387
592
  * Shell API -- imperative domain capabilities for modules.
388
593
  *
@@ -431,11 +636,18 @@ declare const dialog: {
431
636
  };
432
637
  type NavigateFn = (path: string) => void;
433
638
  type CommandMenuFn = () => void;
639
+ type FeatureLookupFn = (featureId: string) => string | null;
434
640
  /**
435
641
  * Called by the shell during initialization to inject router + command menu.
436
642
  * Not part of the public module API -- only the shell calls this.
437
643
  */
438
644
  declare function initNavigation(navigateFn: NavigateFn, openCommandMenuFn: CommandMenuFn): void;
645
+ /**
646
+ * Called by the shell during initialization to inject the feature lookup.
647
+ * Resolves a stable feature id (e.g. "auditing.audit-events") to a path.
648
+ * Not part of the public module API -- only the shell calls this.
649
+ */
650
+ declare function initFeatureNav(lookupFn: FeatureLookupFn): void;
439
651
  declare const navigation: {
440
652
  /** Navigate to a path within the shell. */
441
653
  goTo(path: string): void;
@@ -480,6 +692,22 @@ interface PanelOptions {
480
692
  * - "full": panel covers the full viewport height including the TopBar.
481
693
  */
482
694
  coverage?: "below-header" | "full";
695
+ /**
696
+ * Allow the user to drag the left edge of the panel to resize it.
697
+ * Defaults to false. When true, the fixed `width` token is used as the
698
+ * initial width and the panel width is controlled dynamically.
699
+ */
700
+ resizable?: boolean;
701
+ /**
702
+ * Minimum width in pixels when resizable. Defaults to 240.
703
+ * Ignored when resizable is false.
704
+ */
705
+ minWidth?: number;
706
+ /**
707
+ * Maximum width in pixels when resizable. Defaults to 90% of viewport width.
708
+ * Ignored when resizable is false.
709
+ */
710
+ maxWidth?: number;
483
711
  /** Called after the panel finishes closing. Use to restore focus to the triggering element. */
484
712
  onClose?: () => void;
485
713
  }
@@ -537,6 +765,13 @@ interface FullscreenOptions {
537
765
  /** React content to render inside the fullscreen overlay. */
538
766
  content: ReactNode;
539
767
  }
768
+ type FullscreenEnterFn = (opts: FullscreenOptions) => void;
769
+ type FullscreenExitFn = () => void;
770
+ /**
771
+ * Called by the shell during initialization to inject fullscreen controls.
772
+ * Not part of the public module API -- only the shell calls this.
773
+ */
774
+ declare function initFullscreen(enterFn: FullscreenEnterFn, exitFn: FullscreenExitFn): void;
540
775
  declare const fullscreen: {
541
776
  /** Enter fullscreen mode, rendering content over all shell chrome. */
542
777
  enter(opts: FullscreenOptions): void;
@@ -559,6 +794,16 @@ declare const events: {
559
794
  */
560
795
  declare function useShellModules(): ModuleRegistry;
561
796
 
797
+ interface ShellNavigationState {
798
+ activeService: ShellModule | null;
799
+ sidePaneModuleId: string | null;
800
+ subNavCollapsed: boolean;
801
+ onToggleSubNav: () => void;
802
+ onServiceSelect: (id: string) => void;
803
+ }
804
+ /** Read shell navigation state. Must be used within AppShell. */
805
+ declare function useShellNavigation(): ShellNavigationState;
806
+
562
807
  /**
563
808
  * OverviewCard and FeatureLink -- building blocks for module overview pages.
564
809
  *
@@ -622,7 +867,7 @@ declare function useExtensionPoint(name: string): Contribution[];
622
867
  *
623
868
  * Useful for building overviews like the home page.
624
869
  */
625
- declare function useMainModules(): ServiceModule[];
870
+ declare function useMainModules(): ShellModule[];
626
871
  /**
627
872
  * Subscribe to a shell event, auto-unsubscribing on unmount.
628
873
  *
@@ -638,4 +883,4 @@ declare function useMainModules(): ServiceModule[];
638
883
  */
639
884
  declare function useShellEvent<K extends keyof ShellEventMap>(key: K, handler: (payload: ShellEventMap[K]) => void): void;
640
885
 
641
- export { AppShell, CommandMenu, ConfirmDialog, type ConfirmDialogProps, type ConfirmOptions, type Contribution, FeatureLink, type FullscreenOptions, IconRail, type ModuleCommand, type ModuleRegistry, type NavGroup, type NavLink, OverviewCard, type PanelOptions, PlaceholderPage, RootLayout, SearchTrigger, type ServiceModule, type ShellConfig, ShellConfigProvider, type ShellEventMap, SidePane, type SidePaneOptions as SidePaneApiOptions, SidePaneContext, type SidePaneOptions, type SidePaneState, SubNavPanel, TopBar, UserMenu, type UserMenuUser, createModuleRegistry, dialog, events, fullscreen, initDialog, initNavigation, initPanel, initSidePane, navigation, notification, panel, sidePane, useExtensionPoint, useMainModules, useShellConfig, useShellEvent, useShellModules, useSidePaneState };
886
+ export { AppShell, type CapabilityPolicy, type CapabilityResolver, type CapabilityResult, CommandMenu, ConfirmDialog, type ConfirmDialogProps, type ConfirmOptions, type Contribution, DEFAULT_CAPABILITY_POLICY, type Effect, FeatureLink, type FullscreenOptions, IconRail, type IconRailProps, type ModuleCommand, type ModuleEffect, type ModuleRegistry, type NavGroup, type NavLink, OverviewCard, type PanelOptions, PlaceholderPage, RailIcon, type RailIconProps, RailSeparator, RootLayout, SearchTrigger, type ShellConfig, ShellConfigProvider, type ShellEventMap, ShellFooter, type ShellFooterProps, type ShellModule, type ShellNavigationState, ShellRail, ShellSidebar, type ShellSidebarProps, ShellVersion, type ShellVersionProps, SidePane, type SidePaneOptions as SidePaneApiOptions, SidePaneContext, type SidePaneOptions, type SidePaneState, Sidebar, SidebarGroup, type SidebarGroupProps, SidebarItem, type SidebarItemProps, type SidebarProps, SubNavPanel, type SubNavPanelProps, type Surface, TopBar, type UnmetKind, UserMenu, type UserMenuUser, createModuleRegistry, dialog, evaluateModule, events, fullscreen, initDialog, initFeatureNav, initFullscreen, initNavigation, initPanel, initSidePane, navigation, notification, panel, resolveCapabilityPolicy, sidePane, useExtensionPoint, useMainModules, useShellConfig, useShellEvent, useShellModules, useShellNavigation, useSidePaneState };