@fayz-ai/ui 0.12.0 → 0.15.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.
Files changed (105) hide show
  1. package/dist/{ChartWidgetInner-GDE5PBTR.js → ChartWidgetInner-IOLIUYTB.js} +21 -8
  2. package/dist/ChartWidgetInner-IOLIUYTB.js.map +1 -0
  3. package/dist/analytics/AnalyticsCard.d.ts.map +1 -1
  4. package/dist/analytics/AnalyticsControls.d.ts +5 -1
  5. package/dist/analytics/AnalyticsControls.d.ts.map +1 -1
  6. package/dist/chunk-3OVZ5JJQ.js +540 -0
  7. package/dist/chunk-3OVZ5JJQ.js.map +1 -0
  8. package/dist/{chunk-22EVFMS3.js → chunk-4GK6IHOI.js} +17 -3
  9. package/dist/chunk-4GK6IHOI.js.map +1 -0
  10. package/dist/{chunk-7BMXN7MP.js → chunk-5KMAEQA3.js} +25 -24
  11. package/dist/chunk-5KMAEQA3.js.map +1 -0
  12. package/dist/chunk-AASANYXN.js +601 -0
  13. package/dist/chunk-AASANYXN.js.map +1 -0
  14. package/dist/chunk-FFJ2C5YH.js +92 -0
  15. package/dist/chunk-FFJ2C5YH.js.map +1 -0
  16. package/dist/chunk-J7F3Z52K.js +152 -0
  17. package/dist/chunk-J7F3Z52K.js.map +1 -0
  18. package/dist/{chunk-JGJNRNQW.js → chunk-KD2V3TAX.js} +290 -67
  19. package/dist/chunk-KD2V3TAX.js.map +1 -0
  20. package/dist/{chunk-ZDRHCKRK.js → chunk-NA3GE4H3.js} +746 -533
  21. package/dist/chunk-NA3GE4H3.js.map +1 -0
  22. package/dist/{chunk-7BHG3XGJ.js → chunk-SLWTKHNY.js} +296 -269
  23. package/dist/chunk-SLWTKHNY.js.map +1 -0
  24. package/dist/dashboard/ChartWidgetInner.d.ts.map +1 -1
  25. package/dist/dashboard/DashboardCanvas.d.ts.map +1 -1
  26. package/dist/dashboard/DashboardCustomizeMenu.d.ts.map +1 -1
  27. package/dist/dashboard/DashboardRange.d.ts +8 -2
  28. package/dist/dashboard/DashboardRange.d.ts.map +1 -1
  29. package/dist/dashboard/index.js +4 -4
  30. package/dist/icons.d.ts.map +1 -1
  31. package/dist/icons.js +1 -1
  32. package/dist/index.d.ts +4 -3
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +86 -99
  35. package/dist/index.js.map +1 -1
  36. package/dist/layout/AppShell.d.ts +8 -2
  37. package/dist/layout/AppShell.d.ts.map +1 -1
  38. package/dist/layout/ModulePage.d.ts +24 -1
  39. package/dist/layout/ModulePage.d.ts.map +1 -1
  40. package/dist/layout/Sidebar.d.ts +27 -4
  41. package/dist/layout/Sidebar.d.ts.map +1 -1
  42. package/dist/layout/Topbar.d.ts +10 -3
  43. package/dist/layout/Topbar.d.ts.map +1 -1
  44. package/dist/layout/index.d.ts +2 -1
  45. package/dist/layout/index.d.ts.map +1 -1
  46. package/dist/layout/index.js +4 -4
  47. package/dist/layout/rail-flyout.d.ts +10 -0
  48. package/dist/layout/rail-flyout.d.ts.map +1 -1
  49. package/dist/primitives/SearchCombobox.d.ts +9 -1
  50. package/dist/primitives/SearchCombobox.d.ts.map +1 -1
  51. package/dist/primitives/bulk-action-bar.d.ts +3 -1
  52. package/dist/primitives/bulk-action-bar.d.ts.map +1 -1
  53. package/dist/primitives/color-palette.d.ts +24 -0
  54. package/dist/primitives/color-palette.d.ts.map +1 -0
  55. package/dist/primitives/header-tabs.d.ts +47 -7
  56. package/dist/primitives/header-tabs.d.ts.map +1 -1
  57. package/dist/primitives/image-drop.d.ts +28 -0
  58. package/dist/primitives/image-drop.d.ts.map +1 -0
  59. package/dist/primitives/index.d.ts +9 -3
  60. package/dist/primitives/index.d.ts.map +1 -1
  61. package/dist/primitives/index.js +6 -3
  62. package/dist/primitives/list-view.d.ts +9 -1
  63. package/dist/primitives/list-view.d.ts.map +1 -1
  64. package/dist/primitives/option-select.d.ts +46 -0
  65. package/dist/primitives/option-select.d.ts.map +1 -0
  66. package/dist/primitives/page-toolbar.d.ts +80 -0
  67. package/dist/primitives/page-toolbar.d.ts.map +1 -0
  68. package/dist/primitives/search-select.d.ts +3 -1
  69. package/dist/primitives/search-select.d.ts.map +1 -1
  70. package/dist/primitives/sheet.d.ts.map +1 -1
  71. package/dist/primitives/step-progress.d.ts +20 -0
  72. package/dist/primitives/step-progress.d.ts.map +1 -0
  73. package/dist/primitives/tabs.d.ts.map +1 -1
  74. package/dist/primitives/use-row-selection.d.ts +50 -0
  75. package/dist/primitives/use-row-selection.d.ts.map +1 -0
  76. package/dist/stores/index.d.ts +1 -1
  77. package/dist/stores/index.d.ts.map +1 -1
  78. package/dist/stores/page-header.store.d.ts +26 -2
  79. package/dist/stores/page-header.store.d.ts.map +1 -1
  80. package/dist/styles.css +74 -17
  81. package/dist/wizard/Wizard.d.ts +38 -0
  82. package/dist/wizard/Wizard.d.ts.map +1 -0
  83. package/dist/wizard/WizardField.d.ts +22 -0
  84. package/dist/wizard/WizardField.d.ts.map +1 -0
  85. package/dist/wizard/index.d.ts +6 -0
  86. package/dist/wizard/index.d.ts.map +1 -0
  87. package/dist/wizard/index.js +5 -0
  88. package/dist/wizard/index.js.map +1 -0
  89. package/dist/wizard/logic.d.ts +39 -0
  90. package/dist/wizard/logic.d.ts.map +1 -0
  91. package/dist/wizard/types.d.ts +95 -0
  92. package/dist/wizard/types.d.ts.map +1 -0
  93. package/dist/wizard/useWizard.d.ts +47 -0
  94. package/dist/wizard/useWizard.d.ts.map +1 -0
  95. package/package.json +14 -8
  96. package/dist/ChartWidgetInner-GDE5PBTR.js.map +0 -1
  97. package/dist/chunk-22EVFMS3.js.map +0 -1
  98. package/dist/chunk-7BHG3XGJ.js.map +0 -1
  99. package/dist/chunk-7BMXN7MP.js.map +0 -1
  100. package/dist/chunk-JGJNRNQW.js.map +0 -1
  101. package/dist/chunk-VAKWUUVC.js +0 -425
  102. package/dist/chunk-VAKWUUVC.js.map +0 -1
  103. package/dist/chunk-ZBQ772DU.js +0 -17
  104. package/dist/chunk-ZBQ772DU.js.map +0 -1
  105. package/dist/chunk-ZDRHCKRK.js.map +0 -1
@@ -1,40 +1,371 @@
1
- import { useHasPageHeaderHost, useCrossModuleBack, useBackHandler, PageHeaderCrumb, usePageHeader, PageHeaderOutlet, Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, HeaderTabs, usePageHeaderSlot, useHasPageHeaderOutlet, usePageHeaderHost, SaveBarProvider, SaveBar } from './chunk-VAKWUUVC.js';
2
- import { useLayoutStore } from './chunk-ZBQ772DU.js';
3
- import { cn, Button } from './chunk-OKZ6WQB3.js';
4
- import { ICON_MAP } from './chunk-22EVFMS3.js';
5
- import * as React from 'react';
6
- import { PanelLeftClose, PanelLeft, Search, Bell, Menu, Crown, Repeat, GraduationCap, Newspaper, Clapperboard, Zap, Star, UserCheck, Inbox, Clock3, Ban, Map, ListPlus, CalendarX, CalendarCheck2, Mic, UsersRound, Disc3, ListMusic, Eye, Music, Layers, Banknote, BadgeDollarSign, CalendarDays, Radio, PartyPopper, FileCheck2, Box, Boxes, TreePalm, UserX, UserPlus, ArrowDownRight, ArrowUpRight, Ruler, Warehouse, Wallet, Sparkles, AlertTriangle, Clock, CircleDollarSign, TrendingUp, Receipt, Landmark, TableProperties, SlidersHorizontal, Plug, ArrowLeftRight, ArrowUpCircle, ArrowDownCircle, Wheat, Egg, Apple, LeafyGreen, LayoutTemplate, Heart, PawPrint, Cat, Dog, FolderOpen, ListChecks, List, Shield, Plus, Filter, ChevronDown, Building2, Contact, Handshake, MapPin, UtensilsCrossed, Camera, Tags, Tag, Percent, BookOpenCheck, Globe, MessageCircle, BookOpen, UserCog, Briefcase, ClipboardCheck, ClipboardList, Wrench, Target, ShoppingBag, ShoppingCart, Megaphone, DollarSign, Mail, FileText, BarChart3, Activity, Package, CalendarClock, Calendar, CreditCard, Settings, Users, Home, ChevronRight, ChevronLeft, PanelRightOpen, PanelRightClose, PanelRight, ExternalLink, PiggyBank, LogOut } from 'lucide-react';
7
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
1
+ import { useHasPageHeaderHost, usePageHeader, useLayoutStore, useRegisterPageActions, useHasPageHeaderBack, usePageHeaderSlot, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-FFJ2C5YH.js';
2
+ import { cn } from './chunk-OKZ6WQB3.js';
3
+ import { ICON_MAP } from './chunk-4GK6IHOI.js';
4
+ import * as React7 from 'react';
5
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
6
+ import { X, Loader2, ArrowLeft, Search, Bell, PanelLeft, PanelLeftClose, Menu, Crown, ChevronDown, ChevronRight, ChevronLeft, ExternalLink, CreditCard, Sparkles, Home, Plus, PiggyBank, Wallet, MessageCircle, Settings, Calendar, ArrowLeftRight, Receipt, UserCog, LogOut } from 'lucide-react';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
+ import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
8
9
  import * as Popover from '@radix-ui/react-popover';
9
10
  import { createPortal } from 'react-dom';
10
- import { useTranslation } from '@fayz-ai/core';
11
11
 
12
+ var STYLE_ID = "saas-sheet-styles";
13
+ if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
14
+ const style = document.createElement("style");
15
+ style.id = STYLE_ID;
16
+ style.textContent = `
17
+ @keyframes saas-sheet-fade-in { from { opacity: 0 } to { opacity: 1 } }
18
+ @keyframes saas-sheet-fade-out { from { opacity: 1 } to { opacity: 0 } }
19
+ @keyframes saas-sheet-slide-in { from { transform: translateX(100%) } to { transform: translateX(0) } }
20
+ @keyframes saas-sheet-slide-out { from { transform: translateX(0) } to { transform: translateX(100%) } }
21
+ .saas-sheet-overlay[data-state="open"] { animation: saas-sheet-fade-in 200ms ease-out }
22
+ .saas-sheet-overlay[data-state="closed"] { animation: saas-sheet-fade-out 150ms ease-in }
23
+ .saas-sheet-content[data-state="open"] { animation: saas-sheet-slide-in 280ms cubic-bezier(0.16, 1, 0.3, 1) }
24
+ .saas-sheet-content[data-state="closed"] { animation: saas-sheet-slide-out 200ms ease-in }
25
+ `;
26
+ document.head.appendChild(style);
27
+ }
28
+ var Sheet = DialogPrimitive.Root;
29
+ var SheetTrigger = DialogPrimitive.Trigger;
30
+ var SheetClose = DialogPrimitive.Close;
31
+ var SheetPortal = DialogPrimitive.Portal;
32
+ var SheetOverlay = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
33
+ DialogPrimitive.Overlay,
34
+ {
35
+ ref,
36
+ className: cn(
37
+ "saas-sheet-overlay fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px]",
38
+ className
39
+ ),
40
+ ...props
41
+ }
42
+ ));
43
+ SheetOverlay.displayName = "SheetOverlay";
44
+ var SheetContent = React7.forwardRef(({ className, children, width, hideClose, overlay = "blur", onPointerDownOutside, onInteractOutside, ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
45
+ /* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
46
+ /* @__PURE__ */ jsxs(
47
+ DialogPrimitive.Content,
48
+ {
49
+ ref,
50
+ onPointerDownOutside: (e) => {
51
+ const target = e.target;
52
+ if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
53
+ e.preventDefault();
54
+ }
55
+ onPointerDownOutside?.(e);
56
+ },
57
+ onInteractOutside: (e) => {
58
+ const target = e.target;
59
+ if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
60
+ e.preventDefault();
61
+ }
62
+ onInteractOutside?.(e);
63
+ },
64
+ className: cn(
65
+ "fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-50 flex flex-col border-l bg-card shadow-2xl outline-none",
66
+ "w-full",
67
+ width ?? "max-w-md",
68
+ className
69
+ ),
70
+ ...props,
71
+ children: [
72
+ children,
73
+ !hideClose && /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 z-10 flex h-7 w-7 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring", children: [
74
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
75
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
76
+ ] })
77
+ ]
78
+ }
79
+ )
80
+ ] }));
81
+ SheetContent.displayName = "SheetContent";
82
+ function SheetHeader({ className, ...props }) {
83
+ return /* @__PURE__ */ jsx(
84
+ "div",
85
+ {
86
+ className: cn("flex flex-col gap-1 px-5 py-4 border-b shrink-0", className),
87
+ ...props
88
+ }
89
+ );
90
+ }
91
+ SheetHeader.displayName = "SheetHeader";
92
+ function SheetFooter({ className, ...props }) {
93
+ return /* @__PURE__ */ jsx(
94
+ "div",
95
+ {
96
+ className: cn("flex items-center gap-2 px-5 py-3 border-t shrink-0", className),
97
+ ...props
98
+ }
99
+ );
100
+ }
101
+ SheetFooter.displayName = "SheetFooter";
102
+ var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
103
+ DialogPrimitive.Title,
104
+ {
105
+ ref,
106
+ className: cn("text-sm font-semibold leading-none tracking-tight", className),
107
+ ...props
108
+ }
109
+ ));
110
+ SheetTitle.displayName = "SheetTitle";
111
+ var SheetDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
112
+ DialogPrimitive.Description,
113
+ {
114
+ ref,
115
+ className: cn("text-xs text-muted-foreground", className),
116
+ ...props
117
+ }
118
+ ));
119
+ SheetDescription.displayName = "SheetDescription";
120
+ function SheetBody({ className, ...props }) {
121
+ return /* @__PURE__ */ jsx(
122
+ "div",
123
+ {
124
+ className: cn("flex-1 overflow-y-auto px-5 py-5", className),
125
+ ...props
126
+ }
127
+ );
128
+ }
129
+ SheetBody.displayName = "SheetBody";
130
+ var SaveBarApiContext = React7.createContext(null);
131
+ var SaveBarStateContext = React7.createContext({ visible: false, saving: false });
132
+ var _saveBarVisible = false;
133
+ var _saveBarSubs = /* @__PURE__ */ new Set();
134
+ function setSaveBarVisibleSignal(v) {
135
+ if (v === _saveBarVisible) return;
136
+ _saveBarVisible = v;
137
+ _saveBarSubs.forEach((fn) => fn());
138
+ }
139
+ function useSaveBarVisible() {
140
+ return React7.useSyncExternalStore(
141
+ (cb) => {
142
+ _saveBarSubs.add(cb);
143
+ return () => {
144
+ _saveBarSubs.delete(cb);
145
+ };
146
+ },
147
+ () => _saveBarVisible,
148
+ () => false
149
+ );
150
+ }
151
+ function SaveBarProvider({ children }) {
152
+ const [state, setState] = React7.useState({ visible: false, saving: false });
153
+ const handlers = React7.useRef({});
154
+ const api = React7.useMemo(() => ({ setState, handlers }), []);
155
+ const backStack = React7.useRef([]);
156
+ const backApi = React7.useMemo(() => ({ stack: backStack }), []);
157
+ React7.useEffect(() => {
158
+ setSaveBarVisibleSignal(state.visible);
159
+ }, [state.visible]);
160
+ React7.useEffect(() => {
161
+ function onKey(e) {
162
+ if (state.visible && !state.saving) {
163
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
164
+ e.preventDefault();
165
+ handlers.current.onSave?.();
166
+ } else if (e.key === "Escape") {
167
+ e.preventDefault();
168
+ handlers.current.onDiscard?.();
169
+ }
170
+ return;
171
+ }
172
+ if (e.key === "Escape" && !e.defaultPrevented && !isEditableTarget(e.target)) {
173
+ for (let i = backStack.current.length - 1; i >= 0; i--) {
174
+ const back = backStack.current[i].current;
175
+ if (back) {
176
+ e.preventDefault();
177
+ back();
178
+ return;
179
+ }
180
+ }
181
+ }
182
+ }
183
+ window.addEventListener("keydown", onKey);
184
+ return () => window.removeEventListener("keydown", onKey);
185
+ }, [state.visible, state.saving]);
186
+ return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
187
+ }
188
+ var BackNavApiContext = React7.createContext(null);
189
+ function isEditableTarget(target) {
190
+ const el = target;
191
+ if (!el || !el.tagName) return false;
192
+ const tag = el.tagName.toLowerCase();
193
+ return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
194
+ }
195
+ function useBackHandler(onBack) {
196
+ const api = React7.useContext(BackNavApiContext);
197
+ const ref = React7.useRef(onBack);
198
+ React7.useEffect(() => {
199
+ ref.current = onBack;
200
+ });
201
+ React7.useEffect(() => {
202
+ if (!api) return;
203
+ const stack = api.stack.current;
204
+ stack.push(ref);
205
+ return () => {
206
+ const i = stack.indexOf(ref);
207
+ if (i !== -1) stack.splice(i, 1);
208
+ };
209
+ }, [api]);
210
+ }
211
+ function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
212
+ const api = React7.useContext(SaveBarApiContext);
213
+ React7.useEffect(() => {
214
+ if (api) api.handlers.current = { onSave, onDiscard };
215
+ });
216
+ React7.useEffect(() => {
217
+ if (!api) return;
218
+ api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
219
+ return () => api.setState((s) => ({ ...s, visible: false }));
220
+ }, [api, dirty, saving, message, saveLabel, discardLabel]);
221
+ }
222
+ function SaveBar({ className }) {
223
+ const state = React7.useContext(SaveBarStateContext);
224
+ const api = React7.useContext(SaveBarApiContext);
225
+ const t = useTranslation();
226
+ const [rendered, setRendered] = React7.useState(state.visible);
227
+ const [closing, setClosing] = React7.useState(false);
228
+ React7.useEffect(() => {
229
+ if (state.visible) {
230
+ setRendered(true);
231
+ setClosing(false);
232
+ return;
233
+ }
234
+ if (!rendered) return;
235
+ setClosing(true);
236
+ const id = setTimeout(() => {
237
+ setRendered(false);
238
+ setClosing(false);
239
+ }, 200);
240
+ return () => clearTimeout(id);
241
+ }, [state.visible, rendered]);
242
+ if (!api || !rendered) return null;
243
+ const { saving, message, saveLabel, discardLabel } = state;
244
+ return /* @__PURE__ */ jsx("div", { className: cn("pointer-events-none fixed inset-x-0 bottom-5 z-40 flex justify-center px-4", className), children: /* @__PURE__ */ jsxs("div", { className: cn(
245
+ "pointer-events-auto flex items-center gap-5 rounded-full bg-foreground py-2 pl-5 pr-2 text-background shadow-xl ring-1 ring-black/5 duration-200",
246
+ closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
247
+ ), children: [
248
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
249
+ /* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
250
+ message ?? t("saveBar.unsaved")
251
+ ] }),
252
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
253
+ /* @__PURE__ */ jsx(
254
+ "button",
255
+ {
256
+ type: "button",
257
+ disabled: saving,
258
+ onClick: () => api.handlers.current.onDiscard?.(),
259
+ className: "rounded-full px-3 py-1.5 text-sm font-medium text-background/70 transition-colors hover:bg-background/10 hover:text-background disabled:opacity-50",
260
+ children: discardLabel ?? t("common.discard")
261
+ }
262
+ ),
263
+ /* @__PURE__ */ jsxs(
264
+ "button",
265
+ {
266
+ type: "button",
267
+ disabled: saving,
268
+ onClick: () => api.handlers.current.onSave?.(),
269
+ className: "inline-flex items-center rounded-full bg-background px-4 py-1.5 text-sm font-semibold text-foreground transition-colors hover:bg-background/90 disabled:opacity-60",
270
+ children: [
271
+ saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
272
+ saveLabel ?? t("common.save")
273
+ ]
274
+ }
275
+ )
276
+ ] })
277
+ ] }) });
278
+ }
279
+ function useCrossModuleBack(onBack, parentLabel) {
280
+ const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
281
+ const referrer = useNavReferrer(currentPath);
282
+ const cross = referrer && referrer.label && routeModule(referrer.path) !== routeModule(currentPath) ? referrer : null;
283
+ return {
284
+ onBack: cross ? () => navHistoryBack(onBack) : onBack,
285
+ parentLabel: cross ? cross.label : parentLabel,
286
+ isCrossModule: !!cross
287
+ };
288
+ }
289
+ function PageHeaderCrumb({
290
+ title,
291
+ parentLabel,
292
+ onBack
293
+ }) {
294
+ if (!onBack) {
295
+ return title ? /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-lg font-semibold", children: title }) : null;
296
+ }
297
+ return /* @__PURE__ */ jsxs("nav", { className: "flex min-w-0 items-center gap-1.5 text-sm text-muted-foreground", children: [
298
+ /* @__PURE__ */ jsxs(
299
+ "button",
300
+ {
301
+ type: "button",
302
+ onClick: onBack,
303
+ className: "inline-flex shrink-0 items-center gap-1 transition-colors hover:text-foreground",
304
+ children: [
305
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "h-3.5 w-3.5" }),
306
+ parentLabel ?? "Voltar"
307
+ ]
308
+ }
309
+ ),
310
+ title != null && /* @__PURE__ */ jsxs(Fragment, { children: [
311
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", children: "/" }),
312
+ title === "" ? (
313
+ // Title still waiting on data: a pulsing bar, otherwise the line
314
+ // appears from nowhere and shoves the rest when the record lands.
315
+ /* @__PURE__ */ jsx("span", { className: "inline-block h-4 w-24 animate-pulse rounded bg-muted align-middle" })
316
+ ) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-foreground", children: title })
317
+ ] })
318
+ ] });
319
+ }
320
+ function PageHeader({ title, onBack, parentLabel, backLabel }) {
321
+ const hasHost = useHasPageHeaderHost();
322
+ const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
323
+ useBackHandler(effectiveOnBack);
324
+ const node = React7.useMemo(() => /* @__PURE__ */ jsx(
325
+ PageHeaderCrumb,
326
+ {
327
+ title,
328
+ parentLabel: effectiveParent ?? backLabel,
329
+ onBack: effectiveOnBack
330
+ }
331
+ ), [effectiveOnBack, effectiveParent, backLabel, title]);
332
+ usePageHeader(hasHost ? node : null, { back: !!effectiveOnBack });
333
+ if (hasHost) return null;
334
+ return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
335
+ }
12
336
  var GUTTER_PX = 8;
13
337
  var CLOSE_DELAY_MS = 140;
14
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
338
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
15
339
  function iconOf(name) {
16
340
  if (!name) return null;
17
341
  return ICON_MAP[name] ?? null;
18
342
  }
343
+ var RailFlyoutContext = React7.createContext(null);
344
+ var RailFlyoutProvider = RailFlyoutContext.Provider;
345
+ function useRailRow(content) {
346
+ const flyout = React7.useContext(RailFlyoutContext);
347
+ if (!flyout || !content) return {};
348
+ return flyout.rowProps(content);
349
+ }
19
350
  function useRailFlyout({
20
351
  enabled,
21
352
  railRef,
22
353
  activeRoute,
23
354
  onNavigate
24
355
  }) {
25
- const [state, setState] = React.useState(null);
26
- const [pos, setPos] = React.useState({ top: 0, arrow: 20 });
27
- const [animate, setAnimate] = React.useState(false);
28
- const panelRef = React.useRef(null);
29
- const closeTimer = React.useRef(null);
30
- const lastContent = React.useRef(null);
31
- const cancelClose = React.useCallback(() => {
356
+ const [state, setState] = React7.useState(null);
357
+ const [pos, setPos] = React7.useState({ top: 0, arrow: 20 });
358
+ const [animate, setAnimate] = React7.useState(false);
359
+ const panelRef = React7.useRef(null);
360
+ const closeTimer = React7.useRef(null);
361
+ const lastContent = React7.useRef(null);
362
+ const cancelClose = React7.useCallback(() => {
32
363
  if (closeTimer.current) {
33
364
  clearTimeout(closeTimer.current);
34
365
  closeTimer.current = null;
35
366
  }
36
367
  }, []);
37
- const show = React.useCallback(
368
+ const show = React7.useCallback(
38
369
  (anchor, content2) => {
39
370
  if (!enabled) return;
40
371
  cancelClose();
@@ -42,19 +373,19 @@ function useRailFlyout({
42
373
  },
43
374
  [enabled, cancelClose]
44
375
  );
45
- const hide = React.useCallback(() => {
376
+ const hide = React7.useCallback(() => {
46
377
  cancelClose();
47
378
  closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
48
379
  }, [cancelClose]);
49
- const dismiss = React.useCallback(() => {
380
+ const dismiss = React7.useCallback(() => {
50
381
  cancelClose();
51
382
  setState(null);
52
383
  }, [cancelClose]);
53
- React.useEffect(() => () => cancelClose(), [cancelClose]);
54
- React.useEffect(() => {
384
+ React7.useEffect(() => () => cancelClose(), [cancelClose]);
385
+ React7.useEffect(() => {
55
386
  if (!enabled) dismiss();
56
387
  }, [enabled, dismiss]);
57
- const reposition = React.useCallback(() => {
388
+ const reposition = React7.useCallback(() => {
58
389
  const rail = railRef.current;
59
390
  const anchor = state?.anchor;
60
391
  if (!rail || !anchor) return;
@@ -69,7 +400,7 @@ function useRailFlyout({
69
400
  setPos({ top, arrow });
70
401
  }, [railRef, state]);
71
402
  useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
72
- React.useEffect(() => {
403
+ React7.useEffect(() => {
73
404
  if (!state) return;
74
405
  const rail = railRef.current;
75
406
  const onScroll = () => reposition();
@@ -81,7 +412,7 @@ function useRailFlyout({
81
412
  };
82
413
  }, [state, railRef, reposition]);
83
414
  const visible = !!state;
84
- React.useEffect(() => {
415
+ React7.useEffect(() => {
85
416
  if (!visible) {
86
417
  setAnimate(false);
87
418
  return;
@@ -89,7 +420,7 @@ function useRailFlyout({
89
420
  const id = requestAnimationFrame(() => setAnimate(true));
90
421
  return () => cancelAnimationFrame(id);
91
422
  }, [visible]);
92
- React.useEffect(() => {
423
+ React7.useEffect(() => {
93
424
  if (!visible) return;
94
425
  const onKey = (event) => {
95
426
  if (event.key === "Escape") dismiss();
@@ -97,7 +428,7 @@ function useRailFlyout({
97
428
  window.addEventListener("keydown", onKey);
98
429
  return () => window.removeEventListener("keydown", onKey);
99
430
  }, [visible, dismiss]);
100
- const rowProps = React.useCallback(
431
+ const rowProps = React7.useCallback(
101
432
  (content2) => ({
102
433
  onMouseEnter: (event) => show(event.currentTarget, content2),
103
434
  onFocus: (event) => show(event.currentTarget, content2),
@@ -214,7 +545,7 @@ function FlyoutBody({
214
545
  ] });
215
546
  }
216
547
  var RAIL_PUSH_MS = 260;
217
- var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
548
+ var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
218
549
  function getIcon(name) {
219
550
  return ICON_MAP[name] ?? Home;
220
551
  }
@@ -258,7 +589,7 @@ function NavItem({
258
589
  // as cramped rather than as dense.
259
590
  "flex w-full items-center gap-3 rounded-button px-2.5 py-2 text-[13px] font-medium transition-colors",
260
591
  "hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
261
- isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground shadow-sm" : "text-sidebar-foreground/80",
592
+ isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground" : "text-sidebar-foreground/80",
262
593
  collapsed && "justify-center px-2"
263
594
  );
264
595
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -314,7 +645,7 @@ function NavGroup({
314
645
  flyout
315
646
  }) {
316
647
  const isChildActive = item.children?.some((c) => c.route === activeRoute);
317
- const [open, setOpen] = React.useState(() => isChildActive ?? false);
648
+ const [open, setOpen] = React7.useState(() => isChildActive ?? false);
318
649
  const Icon = getIcon(item.icon);
319
650
  if (collapsed) {
320
651
  const hoverProps = flyout?.rowProps(flyoutContentOf(item));
@@ -388,7 +719,7 @@ function NavGroup({
388
719
  child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
389
720
  ] });
390
721
  const row = childExternal ? /* @__PURE__ */ jsx("a", { href: child.href, target: "_blank", rel: "noopener noreferrer", className: childClass, children: childInner }) : /* @__PURE__ */ jsx("button", { onClick: () => onNavigate(child.route), className: childClass, children: childInner });
391
- if (!childConnector) return /* @__PURE__ */ jsx(React.Fragment, { children: row }, child.id);
722
+ if (!childConnector) return /* @__PURE__ */ jsx(React7.Fragment, { children: row }, child.id);
392
723
  const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
393
724
  const onPath = activeIndex >= 0 && childIndex < activeIndex;
394
725
  return (
@@ -450,9 +781,9 @@ function NavGroup({
450
781
  ] });
451
782
  }
452
783
  function withCollapsed(slot, collapsed) {
453
- if (!React.isValidElement(slot)) return slot;
784
+ if (!React7.isValidElement(slot)) return slot;
454
785
  if (typeof slot.type === "string") return slot;
455
- return React.cloneElement(slot, { collapsed });
786
+ return React7.cloneElement(slot, { collapsed });
456
787
  }
457
788
  function Sidebar({
458
789
  navigation,
@@ -464,6 +795,7 @@ function Sidebar({
464
795
  user: _user,
465
796
  orgSwitcher,
466
797
  topContent,
798
+ identity,
467
799
  navTop,
468
800
  footerContent,
469
801
  borderless,
@@ -474,12 +806,13 @@ function Sidebar({
474
806
  sectionLabels,
475
807
  navKey,
476
808
  navDirection = "forward",
477
- navChildConnector
809
+ navChildConnector,
810
+ drawer
478
811
  }) {
479
812
  const currentPath = currentPathProp;
480
813
  const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
481
814
  const activeRoute = resolveActiveRoute(navigation, currentPath);
482
- const railRef = React.useRef(null);
815
+ const railRef = React7.useRef(null);
483
816
  const flyout = useRailFlyout({
484
817
  enabled: collapsed,
485
818
  railRef,
@@ -525,14 +858,13 @@ function Sidebar({
525
858
  item.id
526
859
  );
527
860
  const navBody = /* @__PURE__ */ jsxs(Fragment, { children: [
528
- navTop && /* @__PURE__ */ jsx("div", { className: "mb-2", children: withCollapsed(navTop, collapsed) }),
529
861
  mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
530
862
  secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
531
863
  ] });
532
- const [outgoing, setOutgoing] = React.useState(null);
533
- const lastBodyRef = React.useRef(null);
534
- const lastKeyRef = React.useRef(navKey);
535
- const timerRef = React.useRef(null);
864
+ const [outgoing, setOutgoing] = React7.useState(null);
865
+ const lastBodyRef = React7.useRef(null);
866
+ const lastKeyRef = React7.useRef(navKey);
867
+ const timerRef = React7.useRef(null);
536
868
  useIsomorphicLayoutEffect2(() => {
537
869
  if (lastKeyRef.current !== navKey) {
538
870
  const previousBody = lastBodyRef.current;
@@ -545,166 +877,183 @@ function Sidebar({
545
877
  }
546
878
  lastBodyRef.current = navBody;
547
879
  });
548
- React.useEffect(() => () => {
880
+ React7.useEffect(() => () => {
549
881
  if (timerRef.current) clearTimeout(timerRef.current);
550
882
  }, []);
551
- return /* @__PURE__ */ jsxs(
552
- "aside",
553
- {
554
- ref: railRef,
555
- "data-print": "hide",
556
- onBlur: (event) => {
557
- if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
558
- },
559
- className: cn(
560
- // `relative` anchors the flyout, which hangs off the rail's right edge
561
- // and has to escape the nav's own scroll box to do it.
562
- "relative flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
563
- !borderless && "border-r border-sidebar-border",
564
- // 14rem expanded. It was 16rem, which is the width a rail needs when
565
- // its entries are sentences ("GlossGenius Financing", "Configure your
566
- // payment methods"); these are one or two words, so the extra 2rem was
567
- // paid for by the content column on every screen in every app. The
568
- // labels still truncate, and a long one now truncates 32px earlier —
569
- // which is the trade, taken deliberately.
570
- collapsed ? "w-16" : "w-56"
571
- ),
572
- children: [
573
- /* @__PURE__ */ jsxs(
574
- "div",
575
- {
576
- className: cn(
577
- "flex h-14 items-center justify-between px-4",
578
- !borderless && "border-b border-sidebar-border",
579
- collapsed && "justify-center px-2"
580
- ),
581
- children: [
582
- !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
583
- logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
584
- /* @__PURE__ */ jsx(
585
- "button",
586
- {
587
- onClick: onToggle,
588
- className: "ml-auto rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
589
- "aria-label": "Collapse sidebar",
590
- children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
591
- }
592
- )
593
- ] }),
594
- collapsed && /* @__PURE__ */ jsx(
595
- "button",
596
- {
597
- onClick: onToggle,
598
- className: "rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
599
- "aria-label": "Expand sidebar",
600
- children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
601
- }
602
- )
603
- ]
604
- }
883
+ return (
884
+ // O handle do flyout desce por contexto para QUALQUER linha da trilha —
885
+ // inclusive os widgets que os plugins montam no pé dela. Ver useRailRow.
886
+ /* @__PURE__ */ jsx(RailFlyoutProvider, { value: flyout, children: /* @__PURE__ */ jsxs(
887
+ "aside",
888
+ {
889
+ ref: railRef,
890
+ "data-print": "hide",
891
+ onBlur: (event) => {
892
+ if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
893
+ },
894
+ className: cn(
895
+ // `relative` anchors the flyout, which hangs off the rail's right edge
896
+ // and has to escape the nav's own scroll box to do it.
897
+ "relative flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
898
+ !borderless && "border-r border-sidebar-border",
899
+ // 14rem expanded. It was 16rem, which is the width a rail needs when
900
+ // its entries are sentences ("GlossGenius Financing", "Configure your
901
+ // payment methods"); these are one or two words, so the extra 2rem was
902
+ // paid for by the content column on every screen in every app. The
903
+ // labels still truncate, and a long one now truncates 32px earlier —
904
+ // which is the trade, taken deliberately.
905
+ collapsed ? "w-16" : "w-56"
605
906
  ),
606
- topContent && /* @__PURE__ */ jsx("div", { className: "px-2 pt-2", children: withCollapsed(topContent, collapsed) }),
607
- /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
608
- "button",
609
- {
610
- onClick: () => setCommandPaletteOpen(true),
611
- "aria-keyshortcuts": "Meta+K Control+K",
612
- className: cn(
613
- // Tonal surface, no outline. On a dark rail a 1px border is a hard
614
- // edge competing with every other hard edge; a lighter fill of the
615
- // same hue reads as "a field" without drawing a box around it — and
616
- // leaves the outline treatment free to mean focus, which it must.
617
- "fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
618
- "hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
619
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
620
- collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
621
- ),
622
- "aria-label": searchLabel,
623
- title: collapsed ? searchLabel : void 0,
624
- children: [
625
- /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
626
- !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
627
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
628
- /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] font-medium tabular-nums text-sidebar-muted/60 transition-opacity group-hover:opacity-100 md:opacity-70", children: [
629
- shortcutKey(),
630
- "K"
631
- ] })
632
- ] })
633
- ]
634
- }
635
- ) }),
636
- /* @__PURE__ */ jsxs(
637
- "nav",
638
- {
639
- className: cn(
640
- "relative flex-1 p-2",
641
- // scrollbar-none: the rail still scrolls, but without the native
642
- // scrollbar gutter eating into the nav items.
643
- outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
907
+ children: [
908
+ /* @__PURE__ */ jsxs("div", { className: cn("shrink-0", !borderless && "border-b border-sidebar-border"), children: [
909
+ /* @__PURE__ */ jsxs(
910
+ "div",
911
+ {
912
+ className: cn(
913
+ "flex items-center gap-1 px-2",
914
+ // h-12 com a conta embaixo (48 + 44 + 8 = 100px, na régua de 4/8),
915
+ // h-14 quando a marca é a única coisa aqui e a linha tem de bater
916
+ // com o cabeçalho da página.
917
+ identity ? "h-12" : "h-14",
918
+ collapsed && "justify-center"
919
+ ),
920
+ children: [
921
+ topContent ? /* @__PURE__ */ jsx("div", { className: cn("min-w-0", collapsed ? "flex justify-center" : "flex-1"), children: withCollapsed(topContent, collapsed) }) : !collapsed && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 px-1.5", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
922
+ drawer && /* @__PURE__ */ jsx(
923
+ "button",
924
+ {
925
+ onClick: onToggle,
926
+ className: "fz-tap shrink-0 rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
927
+ "aria-label": "Close menu",
928
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
929
+ }
930
+ )
931
+ ]
932
+ }
644
933
  ),
645
- children: [
646
- outgoing && /* @__PURE__ */ jsx(
647
- "div",
648
- {
649
- "aria-hidden": true,
650
- className: cn(
651
- "pointer-events-none absolute inset-x-2 top-2 space-y-1",
652
- outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
653
- ),
654
- children: outgoing.body
655
- }
656
- ),
657
- /* @__PURE__ */ jsx(
658
- "div",
659
- {
660
- className: cn(
661
- "space-y-1",
662
- outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
663
- ),
664
- children: navBody
665
- },
666
- navKey
667
- )
668
- ]
669
- }
670
- ),
671
- settingsItems.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("p-2 space-y-1", !borderless && "border-t border-sidebar-border"), children: settingsItems.map((item) => /* @__PURE__ */ jsx(
672
- NavItem,
673
- {
674
- item,
675
- collapsed,
676
- isActive: item.route === activeRoute,
677
- onNavigate: handleNavigate,
678
- flyout
679
- },
680
- item.id
681
- )) }),
682
- footerContent && // `empty:hidden` — slot children may all render null (e.g. the
683
- // SeuSiteWidget self-hides when the tenant has no project); without
684
- // it the padded/bordered wrapper shows as a blank block.
685
- /* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
686
- withCollapsed(orgSwitcher, collapsed),
687
- /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
688
- "flex items-center rounded-button",
689
- collapsed ? "flex-col gap-1" : "gap-1 px-1"
690
- ), children: [
691
- userMenuSlot,
692
- !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
693
- notificationSlot ?? /* @__PURE__ */ jsxs(
694
- "button",
934
+ identity && /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-2", collapsed && "flex justify-center"), children: withCollapsed(identity, collapsed) })
935
+ ] }),
936
+ navTop ? /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: withCollapsed(navTop, collapsed) }) : (
937
+ /* Search — above the first destination. It used to be a 16px magnifier
938
+ tucked beside the avatar at the very bottom, which is where you put
939
+ something nobody is meant to find: the fastest way through an admin
940
+ with thirty screens was the one control shaped like an afterthought.
941
+ Here it reads as what it is — a field — and says which key opens it.
942
+ Clicking it opens the same palette ⌘K does; it is a button, not an
943
+ input, because typing happens there. */
944
+ /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
945
+ "button",
946
+ {
947
+ onClick: () => setCommandPaletteOpen(true),
948
+ "aria-keyshortcuts": "Meta+K Control+K",
949
+ className: cn(
950
+ // Tonal surface, no outline. On a dark rail a 1px border is a hard
951
+ // edge competing with every other hard edge; a lighter fill of the
952
+ // same hue reads as "a field" without drawing a box around it — and
953
+ // leaves the outline treatment free to mean focus, which it must.
954
+ "fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
955
+ "hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
956
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
957
+ collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
958
+ ),
959
+ "aria-label": searchLabel,
960
+ title: collapsed ? searchLabel : void 0,
961
+ children: [
962
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
963
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
964
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
965
+ /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] font-medium tabular-nums text-sidebar-muted/60 transition-opacity group-hover:opacity-100 md:opacity-70", children: [
966
+ shortcutKey(),
967
+ "K"
968
+ ] })
969
+ ] })
970
+ ]
971
+ }
972
+ ) })
973
+ ),
974
+ /* @__PURE__ */ jsxs(
975
+ "nav",
695
976
  {
696
- className: "fz-tap relative inline-flex shrink-0 items-center justify-center rounded-button p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
697
- "aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
977
+ className: cn(
978
+ "relative flex-1 p-2",
979
+ // scrollbar-none: the rail still scrolls, but without the native
980
+ // scrollbar gutter eating into the nav items.
981
+ outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
982
+ ),
698
983
  children: [
699
- /* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
700
- unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
984
+ outgoing && /* @__PURE__ */ jsx(
985
+ "div",
986
+ {
987
+ "aria-hidden": true,
988
+ className: cn(
989
+ "pointer-events-none absolute inset-x-2 top-2 space-y-1",
990
+ outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
991
+ ),
992
+ children: outgoing.body
993
+ }
994
+ ),
995
+ /* @__PURE__ */ jsx(
996
+ "div",
997
+ {
998
+ className: cn(
999
+ "space-y-1",
1000
+ outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
1001
+ ),
1002
+ children: navBody
1003
+ },
1004
+ navKey
1005
+ )
701
1006
  ]
702
1007
  }
703
- )
704
- ] }) }),
705
- flyout.panel
706
- ]
707
- }
1008
+ ),
1009
+ settingsItems.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("p-2 space-y-1", !borderless && "border-t border-sidebar-border"), children: settingsItems.map((item) => /* @__PURE__ */ jsx(
1010
+ NavItem,
1011
+ {
1012
+ item,
1013
+ collapsed,
1014
+ isActive: item.route === activeRoute,
1015
+ onNavigate: handleNavigate,
1016
+ flyout
1017
+ },
1018
+ item.id
1019
+ )) }),
1020
+ footerContent && // `empty:hidden` — slot children may all render null (e.g. the
1021
+ // SeuSiteWidget self-hides when the tenant has no project); without
1022
+ // it the padded/bordered wrapper shows as a blank block.
1023
+ /* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
1024
+ withCollapsed(orgSwitcher, collapsed),
1025
+ /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
1026
+ "flex items-center rounded-button",
1027
+ collapsed ? "flex-col gap-1" : "gap-1 px-1"
1028
+ ), children: [
1029
+ userMenuSlot,
1030
+ !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1031
+ notificationSlot ?? /* @__PURE__ */ jsxs(
1032
+ "button",
1033
+ {
1034
+ className: "fz-tap relative inline-flex shrink-0 items-center justify-center rounded-button p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
1035
+ "aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
1036
+ children: [
1037
+ /* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
1038
+ unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
1039
+ ]
1040
+ }
1041
+ ),
1042
+ !drawer && /* @__PURE__ */ jsx(
1043
+ "button",
1044
+ {
1045
+ onClick: onToggle,
1046
+ className: "fz-tap inline-flex shrink-0 items-center justify-center rounded-button p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
1047
+ "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
1048
+ title: collapsed ? "Expand sidebar" : "Collapse sidebar",
1049
+ children: collapsed ? /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
1050
+ }
1051
+ )
1052
+ ] }) }),
1053
+ flyout.panel
1054
+ ]
1055
+ }
1056
+ ) })
708
1057
  );
709
1058
  }
710
1059
  function TopbarUserMenu({
@@ -717,7 +1066,7 @@ function TopbarUserMenu({
717
1066
  onBilling,
718
1067
  userMenuExtras
719
1068
  }) {
720
- const [open, setOpen] = React.useState(false);
1069
+ const [open, setOpen] = React7.useState(false);
721
1070
  if (!user) return null;
722
1071
  const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
723
1072
  const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
@@ -797,115 +1146,11 @@ function TopbarUserMenu({
797
1146
  ) })
798
1147
  ] });
799
1148
  }
800
- var ICON_MAP2 = {
801
- Home: Home,
802
- Users,
803
- Settings: Settings,
804
- CreditCard,
805
- Bell: Bell,
806
- Calendar,
807
- CalendarClock,
808
- Package,
809
- Activity,
810
- BarChart3,
811
- FileText,
812
- Mail,
813
- DollarSign,
814
- Megaphone,
815
- ShoppingCart,
816
- ShoppingBag,
817
- Target,
818
- Wrench,
819
- ClipboardList,
820
- ClipboardCheck,
821
- Briefcase,
822
- UserCog,
823
- BookOpen,
824
- MessageCircle,
825
- Globe,
826
- BookOpenCheck,
827
- Percent,
828
- Tag,
829
- Tags,
830
- Camera,
831
- UtensilsCrossed,
832
- Search: Search,
833
- MapPin,
834
- Handshake,
835
- Contact,
836
- Building2,
837
- ChevronDown: ChevronDown,
838
- Filter,
839
- Plus,
840
- Shield,
841
- List,
842
- ListChecks,
843
- FolderOpen,
844
- Dog,
845
- Cat,
846
- PawPrint,
847
- Heart,
848
- LayoutTemplate,
849
- LeafyGreen,
850
- Apple,
851
- Egg,
852
- Wheat,
853
- ArrowDownCircle,
854
- ArrowUpCircle,
855
- ArrowLeftRight,
856
- Plug,
857
- SlidersHorizontal,
858
- TableProperties,
859
- Landmark,
860
- Receipt,
861
- TrendingUp,
862
- CircleDollarSign,
863
- Clock,
864
- AlertTriangle,
865
- Sparkles,
866
- Wallet,
867
- Warehouse,
868
- Ruler,
869
- ArrowUpRight,
870
- ArrowDownRight,
871
- UserPlus,
872
- UserX,
873
- TreePalm,
874
- Boxes,
875
- Box,
876
- FileCheck2,
877
- PartyPopper,
878
- Radio,
879
- CalendarDays,
880
- BadgeDollarSign,
881
- Banknote,
882
- Layers,
883
- Music,
884
- Eye,
885
- ListMusic,
886
- Disc3,
887
- UsersRound,
888
- Mic,
889
- CalendarCheck2,
890
- CalendarX,
891
- ListPlus,
892
- Map,
893
- Ban,
894
- Clock3,
895
- Inbox,
896
- UserCheck,
897
- Star,
898
- Zap,
899
- Clapperboard,
900
- Newspaper,
901
- GraduationCap,
902
- Repeat
903
- };
904
1149
  function getIcon2(name) {
905
- return ICON_MAP2[name] ?? Home;
1150
+ return ICON_MAP[name] ?? Home;
906
1151
  }
907
1152
  function NavDropdown({ item, currentPath, onNavigate }) {
908
- const [open, setOpen] = React.useState(false);
1153
+ const [open, setOpen] = React7.useState(false);
909
1154
  const Icon = getIcon2(item.icon);
910
1155
  const isChildActive = item.children?.some(
911
1156
  (child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
@@ -975,7 +1220,8 @@ function Topbar({
975
1220
  onBilling,
976
1221
  userPlan,
977
1222
  billingLabel,
978
- userMenuExtras
1223
+ userMenuExtras,
1224
+ userMenuSlot
979
1225
  }) {
980
1226
  const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
981
1227
  const mainNav = navigation.filter((item) => item.section === "main");
@@ -986,7 +1232,7 @@ function Topbar({
986
1232
  };
987
1233
  return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
988
1234
  /* @__PURE__ */ jsx("div", { className: "border-b border-sidebar-border bg-sidebar text-sidebar-foreground", children: /* @__PURE__ */ jsxs("div", { className: "flex h-14 w-full items-center justify-between px-4 md:px-6", children: [
989
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1235
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
990
1236
  onMenuClick && /* @__PURE__ */ jsx(
991
1237
  "button",
992
1238
  {
@@ -996,7 +1242,7 @@ function Topbar({
996
1242
  children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
997
1243
  }
998
1244
  ),
999
- /* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
1245
+ /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 shrink-0 items-center md:flex", children: orgSwitcher ?? logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
1000
1246
  ] }),
1001
1247
  /* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
1002
1248
  "button",
@@ -1011,10 +1257,9 @@ function Topbar({
1011
1257
  }
1012
1258
  ) }),
1013
1259
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
1014
- orgSwitcher && /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 items-center md:flex", children: orgSwitcher }),
1015
1260
  leftContent,
1016
1261
  rightContent,
1017
- /* @__PURE__ */ jsx(
1262
+ userMenuSlot ?? /* @__PURE__ */ jsx(
1018
1263
  TopbarUserMenu,
1019
1264
  {
1020
1265
  user,
@@ -1071,13 +1316,17 @@ function Topbar({
1071
1316
  }) }) }) })
1072
1317
  ] });
1073
1318
  }
1074
- var ModuleHeaderSlotContext = React.createContext(null);
1319
+ var ModuleHeaderSlotContext = React7.createContext(null);
1075
1320
  function useModuleHeaderSlot() {
1076
- return React.useContext(ModuleHeaderSlotContext);
1321
+ return React7.useContext(ModuleHeaderSlotContext);
1077
1322
  }
1078
- var ModuleHeaderActionsSlotContext = React.createContext(null);
1323
+ var ModuleHeaderActionsSlotContext = React7.createContext(null);
1079
1324
  function useModuleHeaderActionsSlot() {
1080
- return React.useContext(ModuleHeaderActionsSlotContext);
1325
+ return React7.useContext(ModuleHeaderActionsSlotContext);
1326
+ }
1327
+ var PageRailSlotContext = React7.createContext(null);
1328
+ function usePageRailSlot() {
1329
+ return React7.useContext(PageRailSlotContext);
1081
1330
  }
1082
1331
  function ShellPageTitle({
1083
1332
  title,
@@ -1099,6 +1348,7 @@ function SidebarLayout({
1099
1348
  userMenuExtras: _userMenuExtras,
1100
1349
  orgSwitcher,
1101
1350
  sidebarTopContent,
1351
+ sidebarIdentity,
1102
1352
  sidebarNavTop,
1103
1353
  sidebarFooterContent,
1104
1354
  frame,
@@ -1116,16 +1366,19 @@ function SidebarLayout({
1116
1366
  sidebarSectionLabels,
1117
1367
  sidebarNavKey,
1118
1368
  sidebarNavDirection,
1119
- sidebarNavChildConnector
1369
+ sidebarNavChildConnector,
1370
+ railOpen
1120
1371
  }) {
1121
1372
  const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
1122
1373
  const pageHeaderSlot = usePageHeaderSlot();
1123
1374
  usePageHeaderHost();
1124
1375
  const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
1125
- const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false);
1126
- const [headerSlot, setHeaderSlot] = React.useState(null);
1127
- const [actionsSlot, setActionsSlot] = React.useState(null);
1128
- return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsxs("div", { className: cn(
1376
+ const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
1377
+ const [headerSlot, setHeaderSlot] = React7.useState(null);
1378
+ const [actionsSlot, setActionsSlot] = React7.useState(null);
1379
+ const [pageRailSlot, setPageRailSlot] = React7.useState(null);
1380
+ const hasPageActions = useHasPageActions();
1381
+ return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: cn(
1129
1382
  "shell-enter flex h-screen overflow-hidden bg-background",
1130
1383
  // Frame mode: the inset gap around the framed card takes the sidebar (menu)
1131
1384
  // color, so the white card reads as a panel floating on the same tone as the
@@ -1148,6 +1401,7 @@ function SidebarLayout({
1148
1401
  onBilling,
1149
1402
  orgSwitcher,
1150
1403
  topContent: sidebarTopContent,
1404
+ identity: sidebarIdentity,
1151
1405
  navTop: sidebarNavTop,
1152
1406
  footerContent: sidebarFooterContent,
1153
1407
  borderless: frame,
@@ -1162,34 +1416,48 @@ function SidebarLayout({
1162
1416
  }
1163
1417
  ) }),
1164
1418
  /* @__PURE__ */ jsxs("div", { className: cn(
1165
- "relative flex flex-1 flex-col overflow-hidden",
1166
- frame && "md:m-2 md:rounded-xl md:border md:border-border md:bg-card"
1419
+ "relative flex flex-1 overflow-hidden",
1420
+ // `content`, not `card`: this is the page's own surface, and cards have
1421
+ // to sit ON it. Painted card, the frame was the same tone as everything
1422
+ // inside it and the page read as one flat sheet.
1423
+ frame && "md:my-2 md:ml-2 md:rounded-xl md:border md:border-border md:bg-content",
1424
+ // With a companion rail open, the page and the rail are ONE card: the
1425
+ // right edge opens and the rail closes it. Two cards with a strip of
1426
+ // chrome between them made the assistant read as a separate window
1427
+ // parked next to the app rather than as a column of it — the rail's own
1428
+ // left border is then the single seam between the two.
1429
+ frame && (railOpen ? "md:rounded-r-none md:border-r-0" : "md:mr-2")
1167
1430
  ), children: [
1168
- /* @__PURE__ */ jsxs("div", { className: cn(
1169
- "flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-border bg-card px-4 py-2",
1170
- mobileHeader !== "minimal" && "hidden md:flex"
1171
- ), children: [
1172
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1173
- !hasBottomNav && /* @__PURE__ */ jsx(
1174
- "button",
1175
- {
1176
- onClick: () => setMobileMenuOpen(true),
1177
- className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
1178
- "aria-label": "Open menu",
1179
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1180
- }
1181
- ),
1182
- pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
1183
- topbarStart,
1184
- /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1431
+ /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1432
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
1433
+ /* @__PURE__ */ jsxs("div", { className: cn(
1434
+ "flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2",
1435
+ // …unless the page put its actions in it. Hiding the bar on mobile
1436
+ // used to hide the only "Novo" the screen had.
1437
+ mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex"
1438
+ ), children: [
1439
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1440
+ !hasBottomNav && /* @__PURE__ */ jsx(
1441
+ "button",
1442
+ {
1443
+ onClick: () => setMobileMenuOpen(true),
1444
+ className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
1445
+ "aria-label": "Open menu",
1446
+ children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1447
+ }
1448
+ ),
1449
+ pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
1450
+ topbarStart,
1451
+ /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1452
+ ] }),
1453
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
1454
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
1455
+ topbarEnd
1456
+ ] })
1185
1457
  ] }),
1186
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
1187
- /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
1188
- topbarEnd
1189
- ] })
1190
- ] }),
1191
- /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
1192
- /* @__PURE__ */ jsx(SaveBar, {})
1458
+ /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
1459
+ /* @__PURE__ */ jsx(SaveBar, {})
1460
+ ] })
1193
1461
  ] }),
1194
1462
  mobileMenuOpen && /* @__PURE__ */ jsx(
1195
1463
  MobileOverlay,
@@ -1205,6 +1473,7 @@ function SidebarLayout({
1205
1473
  onBilling,
1206
1474
  orgSwitcher,
1207
1475
  topContent: sidebarTopContent,
1476
+ identity: sidebarIdentity,
1208
1477
  navTop: sidebarNavTop,
1209
1478
  footerContent: sidebarFooterContent,
1210
1479
  userMenuSlot,
@@ -1214,7 +1483,7 @@ function SidebarLayout({
1214
1483
  onClose: () => setMobileMenuOpen(false)
1215
1484
  }
1216
1485
  )
1217
- ] }) }) }) });
1486
+ ] }) }) }) }) });
1218
1487
  }
1219
1488
  function TopbarLayout({
1220
1489
  navigation = [],
@@ -1229,6 +1498,7 @@ function TopbarLayout({
1229
1498
  userPlan,
1230
1499
  billingLabel,
1231
1500
  userMenuExtras,
1501
+ userMenuSlot,
1232
1502
  orgSwitcher,
1233
1503
  frame,
1234
1504
  currentPath,
@@ -1239,13 +1509,17 @@ function TopbarLayout({
1239
1509
  notificationSlot,
1240
1510
  hasBottomNav = false
1241
1511
  }) {
1242
- const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false);
1512
+ const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
1513
+ const [headerSlot, setHeaderSlot] = React7.useState(null);
1514
+ const [actionsSlot, setActionsSlot] = React7.useState(null);
1515
+ const [pageRailSlot, setPageRailSlot] = React7.useState(null);
1243
1516
  const rawSlot = usePageHeaderSlot();
1244
1517
  const hasOutlet = useHasPageHeaderOutlet();
1518
+ const hasPageActions = useHasPageActions();
1245
1519
  usePageHeaderHost();
1246
1520
  const pageHeaderSlot = hasOutlet ? null : rawSlot;
1247
1521
  const showTitle = !hasOutlet && !!pageTitle;
1248
- return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsxs("div", { className: "shell-enter relative flex h-screen flex-col overflow-hidden bg-background", children: [
1522
+ return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: "shell-enter relative flex h-screen flex-col overflow-hidden bg-background", children: [
1249
1523
  /* @__PURE__ */ jsx(
1250
1524
  Topbar,
1251
1525
  {
@@ -1261,6 +1535,7 @@ function TopbarLayout({
1261
1535
  userPlan,
1262
1536
  billingLabel,
1263
1537
  userMenuExtras,
1538
+ userMenuSlot,
1264
1539
  orgSwitcher,
1265
1540
  leftContent: topbarStart,
1266
1541
  rightContent: topbarEnd || notificationSlot ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -1270,9 +1545,18 @@ function TopbarLayout({
1270
1545
  onMenuClick: hasBottomNav ? void 0 : () => setMobileMenuOpen(true)
1271
1546
  }
1272
1547
  ),
1273
- /* @__PURE__ */ jsx("main", { className: cn("flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", frame && "md:bg-sidebar md:p-2"), children: /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", frame && "md:rounded-xl md:border md:border-border md:bg-card"), children: [
1274
- (pageHeaderSlot || pageBack || showTitle) && /* @__PURE__ */ jsx("div", { className: "flex min-h-12 shrink-0 items-center gap-3 border-b border-border px-4 py-2", children: pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }) }),
1275
- /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
1548
+ /* @__PURE__ */ jsx("main", { className: cn("flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", frame && "md:bg-sidebar md:p-2"), children: /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 min-h-0 flex-1 overflow-hidden", frame && "md:rounded-xl md:border md:border-border md:bg-content"), children: [
1549
+ /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1550
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
1551
+ (pageHeaderSlot || pageBack || hasPageActions) && /* @__PURE__ */ jsxs("div", { className: "flex min-h-12 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2", children: [
1552
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1553
+ pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }),
1554
+ /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1555
+ ] }),
1556
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
1557
+ ] }),
1558
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
1559
+ ] })
1276
1560
  ] }) }),
1277
1561
  /* @__PURE__ */ jsx(SaveBar, {}),
1278
1562
  mobileMenuOpen && /* @__PURE__ */ jsx(
@@ -1292,7 +1576,7 @@ function TopbarLayout({
1292
1576
  onClose: () => setMobileMenuOpen(false)
1293
1577
  }
1294
1578
  )
1295
- ] }) });
1579
+ ] }) }) }) }) });
1296
1580
  }
1297
1581
  function DrawerUserRow({ user }) {
1298
1582
  const label = user.fullName || user.email;
@@ -1316,23 +1600,23 @@ function MinimalLayout({
1316
1600
  ] });
1317
1601
  }
1318
1602
  function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1319
- const [visible, setVisible] = React.useState(false);
1320
- React.useEffect(() => {
1603
+ const [visible, setVisible] = React7.useState(false);
1604
+ React7.useEffect(() => {
1321
1605
  const id = requestAnimationFrame(() => setVisible(true));
1322
1606
  return () => cancelAnimationFrame(id);
1323
1607
  }, []);
1324
- const close = React.useCallback(() => {
1608
+ const close = React7.useCallback(() => {
1325
1609
  setVisible(false);
1326
1610
  window.setTimeout(onClose, 250);
1327
1611
  }, [onClose]);
1328
- React.useEffect(() => {
1612
+ React7.useEffect(() => {
1329
1613
  const previous = document.body.style.overflow;
1330
1614
  document.body.style.overflow = "hidden";
1331
1615
  return () => {
1332
1616
  document.body.style.overflow = previous;
1333
1617
  };
1334
1618
  }, []);
1335
- React.useEffect(() => {
1619
+ React7.useEffect(() => {
1336
1620
  const onKey = (e) => {
1337
1621
  if (e.key === "Escape") close();
1338
1622
  };
@@ -1363,6 +1647,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1363
1647
  ...sidebarProps,
1364
1648
  collapsed: false,
1365
1649
  onToggle: close,
1650
+ drawer: true,
1366
1651
  borderless: true,
1367
1652
  onNavigate: (route) => {
1368
1653
  onNavigate?.(route);
@@ -1376,16 +1661,16 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1376
1661
  }
1377
1662
  var BOTTOM_NAV_ICONS = {
1378
1663
  Home: Home,
1379
- Receipt: Receipt,
1380
- ArrowLeftRight: ArrowLeftRight,
1381
- Calendar: Calendar,
1664
+ Receipt,
1665
+ ArrowLeftRight,
1666
+ Calendar,
1382
1667
  CreditCard: CreditCard,
1383
1668
  Settings: Settings,
1384
1669
  Menu: Menu,
1385
- MessageCircle: MessageCircle,
1386
- Wallet: Wallet,
1670
+ MessageCircle,
1671
+ Wallet,
1387
1672
  PiggyBank,
1388
- Plus: Plus
1673
+ Plus
1389
1674
  };
1390
1675
  function FloatingProfileAvatar({ user, onClick }) {
1391
1676
  if (!user) return null;
@@ -1472,6 +1757,7 @@ function AppShell({
1472
1757
  topbarStart,
1473
1758
  topbarEnd,
1474
1759
  sidebarTopContent,
1760
+ sidebarIdentity,
1475
1761
  sidebarNavTop,
1476
1762
  sidebarFooterContent,
1477
1763
  bottomNav,
@@ -1521,6 +1807,7 @@ function AppShell({
1521
1807
  userMenuExtras,
1522
1808
  orgSwitcher,
1523
1809
  sidebarTopContent,
1810
+ sidebarIdentity,
1524
1811
  sidebarNavTop,
1525
1812
  sidebarFooterContent,
1526
1813
  frame: contentFrame,
@@ -1539,6 +1826,7 @@ function AppShell({
1539
1826
  sidebarNavKey,
1540
1827
  sidebarNavDirection,
1541
1828
  sidebarNavChildConnector,
1829
+ railOpen: !!rightRail && rightRailOpen,
1542
1830
  children
1543
1831
  }
1544
1832
  );
@@ -1558,6 +1846,7 @@ function AppShell({
1558
1846
  userPlan,
1559
1847
  billingLabel,
1560
1848
  userMenuExtras,
1849
+ userMenuSlot,
1561
1850
  orgSwitcher,
1562
1851
  frame: contentFrame,
1563
1852
  currentPath,
@@ -1608,13 +1897,13 @@ function SplitWithRightRail({
1608
1897
  maxWidth = 720,
1609
1898
  resizeLabel = "Resize panel"
1610
1899
  }) {
1611
- const [width, setWidth] = React.useState(defaultWidth);
1612
- const [dragging, setDragging] = React.useState(false);
1613
- const clamp = React.useCallback(
1900
+ const [width, setWidth] = React7.useState(defaultWidth);
1901
+ const [dragging, setDragging] = React7.useState(false);
1902
+ const clamp = React7.useCallback(
1614
1903
  (value) => Math.min(maxWidth, Math.max(minWidth, value)),
1615
1904
  [minWidth, maxWidth]
1616
1905
  );
1617
- React.useEffect(() => {
1906
+ React7.useEffect(() => {
1618
1907
  if (!dragging) return;
1619
1908
  const onMove = (event) => {
1620
1909
  event.preventDefault();
@@ -1636,7 +1925,7 @@ function SplitWithRightRail({
1636
1925
  document.body.style.cursor = previousCursor;
1637
1926
  };
1638
1927
  }, [dragging, clamp]);
1639
- React.useEffect(() => {
1928
+ React7.useEffect(() => {
1640
1929
  if (dragging) return;
1641
1930
  onWidthChange?.(width);
1642
1931
  }, [dragging]);
@@ -1679,7 +1968,11 @@ function SplitWithRightRail({
1679
1968
  }
1680
1969
  },
1681
1970
  className: cn(
1682
- "group relative hidden w-1 shrink-0 cursor-col-resize focus-visible:outline-none",
1971
+ // Zero width: the divider USED to be a 4px gutter, which meant the
1972
+ // page and the rail could never touch — there was always a strip of
1973
+ // chrome between them. It now sits exactly on the seam the rail's own
1974
+ // left border draws, and lights that border up when you reach for it.
1975
+ "group relative hidden w-0 shrink-0 cursor-col-resize focus-visible:outline-none",
1683
1976
  open && "md:block"
1684
1977
  ),
1685
1978
  children: [
@@ -1687,7 +1980,7 @@ function SplitWithRightRail({
1687
1980
  "span",
1688
1981
  {
1689
1982
  className: cn(
1690
- "absolute inset-y-6 left-1/2 w-px -translate-x-1/2 rounded-full bg-transparent transition-colors",
1983
+ "absolute inset-y-6 -left-px w-0.5 rounded-full bg-transparent transition-colors",
1691
1984
  "group-hover:bg-primary/60 group-focus-visible:bg-primary",
1692
1985
  dragging && "bg-primary"
1693
1986
  )
@@ -1715,40 +2008,79 @@ function SplitWithRightRail({
1715
2008
  ] })
1716
2009
  );
1717
2010
  }
1718
- function BootSpinner({ className }) {
2011
+ function HeaderTabs({
2012
+ tabs,
2013
+ value,
2014
+ onChange,
2015
+ className,
2016
+ fill,
2017
+ ...rest
2018
+ }) {
1719
2019
  return /* @__PURE__ */ jsx(
1720
- "span",
2020
+ "div",
1721
2021
  {
1722
- "aria-hidden": true,
2022
+ role: "tablist",
2023
+ "aria-label": rest["aria-label"],
1723
2024
  className: cn(
1724
- "block h-7 w-7 animate-spin rounded-full border-2 border-foreground/20 border-t-foreground/70",
1725
- "motion-reduce:animate-none motion-reduce:border-t-foreground/20 motion-reduce:animate-pulse",
2025
+ "inline-flex max-w-full gap-5 overflow-x-auto scrollbar-none",
2026
+ fill ? "items-stretch" : "items-center",
1726
2027
  className
1727
- )
2028
+ ),
2029
+ children: tabs.map((tab) => {
2030
+ const active = tab.value === value;
2031
+ const Icon = tab.icon;
2032
+ return /* @__PURE__ */ jsxs(
2033
+ "button",
2034
+ {
2035
+ type: "button",
2036
+ role: "tab",
2037
+ "aria-selected": active,
2038
+ onClick: () => onChange(tab.value),
2039
+ className: cn(
2040
+ "relative flex shrink-0 items-center gap-1.5 whitespace-nowrap text-sm transition-colors",
2041
+ // Filled: the row owns the band's height and the underline sits on
2042
+ // its bottom edge. Otherwise the underline trails the label by 6px.
2043
+ fill ? "h-full" : "pb-1.5",
2044
+ active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
2045
+ ),
2046
+ children: [
2047
+ Icon ? /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }) : null,
2048
+ /* @__PURE__ */ jsx("span", { children: tab.label }),
2049
+ tab.count != null && /* @__PURE__ */ jsx("span", { className: cn(
2050
+ "ml-0.5 rounded-full px-1.5 text-[11px] tabular-nums",
2051
+ active ? "bg-muted text-foreground" : "bg-muted/70 text-muted-foreground"
2052
+ ), children: tab.count }),
2053
+ tab.badge,
2054
+ active && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 rounded-full bg-foreground" })
2055
+ ]
2056
+ },
2057
+ tab.value
2058
+ );
2059
+ })
1728
2060
  }
1729
2061
  );
1730
2062
  }
1731
- function AppShellSkeleton({ label, className }) {
2063
+ function PageTabs({ actions, className, ...props }) {
1732
2064
  return /* @__PURE__ */ jsxs(
1733
2065
  "div",
1734
2066
  {
1735
- role: "status",
1736
- "aria-busy": "true",
1737
- className: cn("flex h-screen items-center justify-center overflow-hidden bg-background", className),
2067
+ className: cn(
2068
+ "-mx-3 -mt-3 mb-4 flex flex-wrap items-end justify-between gap-3 border-b border-border px-3 pt-2 md:-mx-6 md:-mt-4 md:px-6",
2069
+ className
2070
+ ),
1738
2071
  children: [
1739
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
1740
- /* @__PURE__ */ jsx(BootSpinner, {})
2072
+ /* @__PURE__ */ jsx(HeaderTabs, { ...props, className: "-mb-px" }),
2073
+ actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2 pb-1.5", children: actions })
1741
2074
  ]
1742
2075
  }
1743
2076
  );
1744
2077
  }
1745
- AppShellSkeleton.displayName = "AppShellSkeleton";
1746
- var NavTransitionContext = React.createContext("slide");
2078
+ var NavTransitionContext = React7.createContext("slide");
1747
2079
  function NavTransitionProvider({ value, children }) {
1748
2080
  return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
1749
2081
  }
1750
2082
  function useNavTransition() {
1751
- return React.useContext(NavTransitionContext);
2083
+ return React7.useContext(NavTransitionContext);
1752
2084
  }
1753
2085
  function resolveTransitionClass(transition, direction) {
1754
2086
  if (transition === "none") return "";
@@ -1767,8 +2099,14 @@ function PageTransition({
1767
2099
  const animClass = resolveTransitionClass(transition, direction);
1768
2100
  return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), children }, transitionKey);
1769
2101
  }
2102
+ function PageRail({ children }) {
2103
+ const slot = usePageRailSlot();
2104
+ if (slot) return createPortal(children, slot);
2105
+ return /* @__PURE__ */ jsx(Fragment, { children });
2106
+ }
1770
2107
  function PageHeaderActions({ children }) {
1771
2108
  const slot = useModuleHeaderActionsSlot();
2109
+ useRegisterPageActions();
1772
2110
  if (slot) return createPortal(children, slot);
1773
2111
  return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
1774
2112
  }
@@ -1777,16 +2115,16 @@ function PremiumBadge({ className }) {
1777
2115
  const tip = t("upgrade.badgeTooltip");
1778
2116
  return /* @__PURE__ */ jsx("span", { title: tip, "aria-label": tip, className: cn("inline-flex shrink-0", className), children: /* @__PURE__ */ jsx(Crown, { className: "h-3.5 w-3.5 text-amber-500", "aria-hidden": true }) });
1779
2117
  }
1780
- var ModuleLayoutContext = React.createContext("rail");
2118
+ var ModuleLayoutContext = React7.createContext("rail");
1781
2119
  function ModuleLayoutProvider({ variant, children }) {
1782
2120
  return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
1783
2121
  }
1784
2122
  function useModuleLayout() {
1785
- return React.useContext(ModuleLayoutContext);
2123
+ return React7.useContext(ModuleLayoutContext);
1786
2124
  }
1787
- function NavItem2({ item }) {
1788
- const [expanded, setExpanded] = React.useState(true);
1789
- const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
2125
+ function RailNavItem({ item }) {
2126
+ const [expanded, setExpanded] = React7.useState(true);
2127
+ const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
1790
2128
  const hasChildren = item.children && item.children.length > 0;
1791
2129
  const hasActiveChild = hasChildren && item.children.some((c) => c.active);
1792
2130
  const isActive = item.active || hasActiveChild;
@@ -1827,9 +2165,9 @@ function NavItem2({ item }) {
1827
2165
  ] });
1828
2166
  }
1829
2167
  function MobileTabs({ nav }) {
1830
- const scrollRef = React.useRef(null);
1831
- const [expandedId, setExpandedId] = React.useState(null);
1832
- React.useEffect(() => {
2168
+ const scrollRef = React7.useRef(null);
2169
+ const [expandedId, setExpandedId] = React7.useState(null);
2170
+ React7.useEffect(() => {
1833
2171
  const container = scrollRef.current;
1834
2172
  if (!container) return;
1835
2173
  const activeEl = container.querySelector('[data-active="true"]');
@@ -1840,7 +2178,7 @@ function MobileTabs({ nav }) {
1840
2178
  }, [nav]);
1841
2179
  return /* @__PURE__ */ jsxs("div", { className: "md:hidden -mx-1 mb-4 no-print", children: [
1842
2180
  /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex gap-1 px-1 pb-2 overflow-x-auto scrollbar-none", children: nav.map((item) => {
1843
- const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
2181
+ const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
1844
2182
  const hasChildren = item.children && item.children.length > 0;
1845
2183
  const isActive = item.active || item.children?.some((c) => c.active);
1846
2184
  const isExpanded = expandedId === item.id;
@@ -1896,12 +2234,12 @@ function MobileTabs({ nav }) {
1896
2234
  })()
1897
2235
  ] });
1898
2236
  }
1899
- var BackStyleContext = React.createContext("link");
2237
+ var BackStyleContext = React7.createContext("link");
1900
2238
  function BackStyleProvider({ style, children }) {
1901
2239
  return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
1902
2240
  }
1903
2241
  function useBackStyle() {
1904
- return React.useContext(BackStyleContext);
2242
+ return React7.useContext(BackStyleContext);
1905
2243
  }
1906
2244
  function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
1907
2245
  const t = useTranslation();
@@ -1911,7 +2249,7 @@ function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, ba
1911
2249
  const effectiveOnBack = inline ? onBack : cross.onBack;
1912
2250
  const effectiveParentLabel = inline ? parentLabel : cross.parentLabel;
1913
2251
  useBackHandler(effectiveOnBack);
1914
- const Icon = icon ? ICON_MAP2[icon] ?? null : null;
2252
+ const Icon = icon ? ICON_MAP[icon] ?? null : null;
1915
2253
  if (inline && effectiveOnBack) {
1916
2254
  return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1917
2255
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
@@ -1997,13 +2335,11 @@ function ShellSubpageHeader({
1997
2335
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
1998
2336
  Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1999
2337
  /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
2000
- ] })
2338
+ ] }),
2339
+ { back: !!onBack }
2001
2340
  );
2002
- if (!subtitle && !actions) return null;
2003
- return /* @__PURE__ */ jsxs("div", { className: "mb-5 flex flex-wrap items-start justify-between gap-3", children: [
2004
- subtitle ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle }) : /* @__PURE__ */ jsx("span", {}),
2005
- actions && /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions })
2006
- ] });
2341
+ if (!actions) return null;
2342
+ return /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) });
2007
2343
  }
2008
2344
  function ModuleHeader({ nav }) {
2009
2345
  function defaultChild(item) {
@@ -2014,7 +2350,7 @@ function ModuleHeader({ nav }) {
2014
2350
  const tabs = nav.map((item) => ({
2015
2351
  value: item.id,
2016
2352
  label: item.label,
2017
- icon: item.icon ? ICON_MAP2[item.icon] ?? void 0 : void 0,
2353
+ icon: item.icon ? ICON_MAP[item.icon] ?? void 0 : void 0,
2018
2354
  // Plan-gated views keep their upgrade marker — dropping it would hide the
2019
2355
  // only signal that the tab costs an upgrade.
2020
2356
  badge: item.premium ? /* @__PURE__ */ jsx(PremiumBadge, {}) : void 0
@@ -2026,12 +2362,13 @@ function ModuleHeader({ nav }) {
2026
2362
  if (hasChildren) defaultChild(item)?.onClick?.();
2027
2363
  else item.onClick?.();
2028
2364
  };
2029
- return /* @__PURE__ */ jsx("div", { className: "mb-6 flex flex-wrap items-center justify-between gap-3", children: /* @__PURE__ */ jsx(HeaderTabs, { tabs, value: activeItem?.id ?? nav[0]?.id ?? "", onChange }) });
2365
+ return /* @__PURE__ */ jsx(PageTabs, { tabs, value: activeItem?.id ?? nav[0]?.id ?? "", onChange });
2030
2366
  }
2031
- function ModulePage({ title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
2367
+ function ModulePage({ title: _title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
2032
2368
  const ctxVariant = useModuleLayout();
2033
2369
  const variant = navVariant ?? ctxVariant;
2034
2370
  const transitionKey = viewKey ?? "view";
2371
+ const onSubpage = useHasPageHeaderBack();
2035
2372
  if (variant === "sidebar") {
2036
2373
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2037
2374
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
@@ -2040,148 +2377,24 @@ function ModulePage({ title, subtitle, nav, children, className, headerAction, s
2040
2377
  }
2041
2378
  if (variant === "tabs") {
2042
2379
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2043
- /* @__PURE__ */ jsx(ModuleHeader, { nav }),
2380
+ !onSubpage && /* @__PURE__ */ jsx(ModuleHeader, { nav }),
2044
2381
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2045
2382
  /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
2046
2383
  ] });
2047
2384
  }
2048
- return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-6 -mt-2", className), children: [
2049
- /* @__PURE__ */ jsx("div", { className: "w-48 shrink-0 hidden md:block no-print", children: /* @__PURE__ */ jsxs("div", { className: cn(sidebarFooter && "sticky top-4 flex flex-col"), style: sidebarFooter ? { height: "calc(100vh - 8rem)" } : void 0, children: [
2050
- /* @__PURE__ */ jsxs("div", { children: [
2051
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider px-2.5 mb-2", children: title }),
2052
- /* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(NavItem2, { item }, item.id)) })
2053
- ] }),
2054
- sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto", children: sidebarFooter })
2385
+ return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0", className), children: [
2386
+ /* @__PURE__ */ jsx(PageRail, { children: /* @__PURE__ */ jsxs("div", { className: "hidden w-48 shrink-0 flex-col overflow-y-auto border-r border-border p-3 no-print md:flex", children: [
2387
+ /* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(RailNavItem, { item }, item.id)) }),
2388
+ sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto pt-3", children: sidebarFooter })
2055
2389
  ] }) }),
2056
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2057
- /* @__PURE__ */ jsx(PageHeaderOutlet, {}),
2058
- showHeader && /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-3", children: [
2059
- /* @__PURE__ */ jsxs("div", { children: [
2060
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: title }),
2061
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
2062
- ] }),
2063
- headerAction
2064
- ] }),
2390
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2391
+ showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2065
2392
  /* @__PURE__ */ jsx(MobileTabs, { nav }),
2066
2393
  /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
2067
2394
  ] })
2068
2395
  ] });
2069
2396
  }
2070
- var EMPTY = [];
2071
- var PageTrailContext = React.createContext(EMPTY);
2072
- function PageTrail({
2073
- label,
2074
- href,
2075
- onNavigate,
2076
- children
2077
- }) {
2078
- const parent = React.useContext(PageTrailContext);
2079
- const trail = React.useMemo(
2080
- () => [...parent, { label, href, onNavigate }],
2081
- [parent, label, href, onNavigate]
2082
- );
2083
- return /* @__PURE__ */ jsx(PageTrailContext.Provider, { value: trail, children });
2084
- }
2085
- function usePageTrail() {
2086
- return React.useContext(PageTrailContext);
2087
- }
2088
- function usePageTrailParent(href) {
2089
- const trail = React.useContext(PageTrailContext);
2090
- return React.useMemo(() => {
2091
- const above = href ? trail.filter((c) => c.href !== href) : trail;
2092
- return above[above.length - 1];
2093
- }, [trail, href]);
2094
- }
2095
- function navigateToCrumb(crumb) {
2096
- if (crumb.onNavigate) crumb.onNavigate();
2097
- else window.location.hash = crumb.href;
2098
- }
2099
- var ContentSplitContext = React.createContext(null);
2100
- function ContentSplit({
2101
- children,
2102
- aside,
2103
- asideTitle,
2104
- asideDescription,
2105
- asideWidth = "20rem",
2106
- asideOpen,
2107
- onAsideOpenChange,
2108
- collapsible = false,
2109
- defaultCollapsed = false,
2110
- className
2111
- }) {
2112
- const [internalOpen, setInternalOpen] = React.useState(false);
2113
- const open = asideOpen ?? internalOpen;
2114
- const setOpen = onAsideOpenChange ?? setInternalOpen;
2115
- const [collapsed, setCollapsed] = React.useState(collapsible && defaultCollapsed);
2116
- return /* @__PURE__ */ jsxs(ContentSplitContext.Provider, { value: { open, setOpen }, children: [
2117
- /* @__PURE__ */ jsxs(
2118
- "div",
2119
- {
2120
- className: cn("lg:grid lg:items-start lg:gap-6", className),
2121
- style: { gridTemplateColumns: `minmax(0, 1fr) ${collapsed ? "auto" : asideWidth}` },
2122
- children: [
2123
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children }),
2124
- /* @__PURE__ */ jsx("aside", { className: "hidden lg:block lg:sticky lg:top-4", children: collapsed ? /* @__PURE__ */ jsx(
2125
- Button,
2126
- {
2127
- variant: "outline",
2128
- size: "sm",
2129
- onClick: () => setCollapsed(false),
2130
- className: "h-9 w-9 p-0",
2131
- "aria-label": typeof asideTitle === "string" ? asideTitle : void 0,
2132
- title: typeof asideTitle === "string" ? asideTitle : void 0,
2133
- children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4" })
2134
- }
2135
- ) : /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card", children: [
2136
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 border-b border-border px-4 py-3", children: [
2137
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2138
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: asideTitle }),
2139
- asideDescription && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: asideDescription })
2140
- ] }),
2141
- collapsible && /* @__PURE__ */ jsx(
2142
- "button",
2143
- {
2144
- type: "button",
2145
- onClick: () => setCollapsed(true),
2146
- className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground",
2147
- "aria-label": "collapse",
2148
- children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
2149
- }
2150
- )
2151
- ] }),
2152
- /* @__PURE__ */ jsx("div", { className: "p-4", children: aside })
2153
- ] }) })
2154
- ]
2155
- }
2156
- ),
2157
- /* @__PURE__ */ jsx("div", { className: "lg:hidden", children: /* @__PURE__ */ jsx(Sheet, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(SheetContent, { width: "max-w-sm", children: [
2158
- /* @__PURE__ */ jsxs(SheetHeader, { children: [
2159
- /* @__PURE__ */ jsx(SheetTitle, { children: asideTitle }),
2160
- asideDescription && /* @__PURE__ */ jsx(SheetDescription, { children: asideDescription })
2161
- ] }),
2162
- /* @__PURE__ */ jsx(SheetBody, { children: aside })
2163
- ] }) }) })
2164
- ] });
2165
- }
2166
- function ContentSplitTrigger({ label, iconOnly, className }) {
2167
- const ctx = React.useContext(ContentSplitContext);
2168
- if (!ctx) return null;
2169
- return /* @__PURE__ */ jsxs(
2170
- Button,
2171
- {
2172
- variant: "outline",
2173
- size: "sm",
2174
- onClick: () => ctx.setOpen(true),
2175
- className: cn("lg:hidden", className),
2176
- "aria-label": typeof label === "string" ? label : void 0,
2177
- children: [
2178
- /* @__PURE__ */ jsx(PanelRight, { className: cn("h-4 w-4", !iconOnly && "mr-1.5") }),
2179
- !iconOnly && label
2180
- ]
2181
- }
2182
- );
2183
- }
2184
2397
 
2185
- export { AppShell, AppShellSkeleton, BackStyleProvider, ContentSplit, ContentSplitTrigger, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeaderActions, PageTrail, PageTransition, Sidebar, SubpageHeader, Topbar, navigateToCrumb, useBackStyle, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, usePageTrail, usePageTrailParent };
2186
- //# sourceMappingURL=chunk-ZDRHCKRK.js.map
2187
- //# sourceMappingURL=chunk-ZDRHCKRK.js.map
2398
+ export { AppShell, BackStyleProvider, HeaderTabs, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeader, PageHeaderActions, PageHeaderCrumb, PageRail, PageTabs, PageTransition, RailNavItem, SaveBar, SaveBarProvider, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SubpageHeader, Topbar, useBackHandler, useBackStyle, useCrossModuleBack, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, useRailRow, useSaveBar, useSaveBarVisible };
2399
+ //# sourceMappingURL=chunk-NA3GE4H3.js.map
2400
+ //# sourceMappingURL=chunk-NA3GE4H3.js.map