@fayz-ai/ui 0.11.0 → 0.14.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 (72) hide show
  1. package/dist/analytics/AnalyticsCard.d.ts.map +1 -1
  2. package/dist/analytics/AnalyticsControls.d.ts +5 -1
  3. package/dist/analytics/AnalyticsControls.d.ts.map +1 -1
  4. package/dist/analytics/derive.d.ts.map +1 -1
  5. package/dist/{chunk-JGJNRNQW.js → chunk-2EFRFYGF.js} +117 -41
  6. package/dist/chunk-2EFRFYGF.js.map +1 -0
  7. package/dist/{chunk-7BMXN7MP.js → chunk-46UZAKFJ.js} +24 -24
  8. package/dist/chunk-46UZAKFJ.js.map +1 -0
  9. package/dist/chunk-5KAGNW45.js +152 -0
  10. package/dist/chunk-5KAGNW45.js.map +1 -0
  11. package/dist/{chunk-XRDCYOND.js → chunk-77CUIMOI.js} +289 -166
  12. package/dist/chunk-77CUIMOI.js.map +1 -0
  13. package/dist/chunk-LFZFBN3P.js +142 -0
  14. package/dist/chunk-LFZFBN3P.js.map +1 -0
  15. package/dist/{chunk-QSHA2Q27.js → chunk-MVH64JFG.js} +1049 -771
  16. package/dist/chunk-MVH64JFG.js.map +1 -0
  17. package/dist/{chunk-22EVFMS3.js → chunk-UZYXFEQS.js} +4 -3
  18. package/dist/chunk-UZYXFEQS.js.map +1 -0
  19. package/dist/dashboard/DashboardCanvas.d.ts.map +1 -1
  20. package/dist/dashboard/DashboardCustomizeMenu.d.ts.map +1 -1
  21. package/dist/dashboard/DashboardRange.d.ts +8 -2
  22. package/dist/dashboard/DashboardRange.d.ts.map +1 -1
  23. package/dist/dashboard/index.js +4 -5
  24. package/dist/icons.js +1 -1
  25. package/dist/index.d.ts +3 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +72 -98
  28. package/dist/index.js.map +1 -1
  29. package/dist/layout/AppShell.d.ts +1 -0
  30. package/dist/layout/AppShell.d.ts.map +1 -1
  31. package/dist/layout/AppShellSkeleton.d.ts +1 -1
  32. package/dist/layout/ModulePage.d.ts +24 -1
  33. package/dist/layout/ModulePage.d.ts.map +1 -1
  34. package/dist/layout/Sidebar.d.ts.map +1 -1
  35. package/dist/layout/index.d.ts +2 -1
  36. package/dist/layout/index.d.ts.map +1 -1
  37. package/dist/layout/index.js +4 -5
  38. package/dist/layout/rail-flyout.d.ts +10 -0
  39. package/dist/layout/rail-flyout.d.ts.map +1 -1
  40. package/dist/primitives/bulk-action-bar.d.ts +3 -1
  41. package/dist/primitives/bulk-action-bar.d.ts.map +1 -1
  42. package/dist/primitives/header-tabs.d.ts +36 -6
  43. package/dist/primitives/header-tabs.d.ts.map +1 -1
  44. package/dist/primitives/index.d.ts +3 -1
  45. package/dist/primitives/index.d.ts.map +1 -1
  46. package/dist/primitives/index.js +5 -4
  47. package/dist/primitives/list-view.d.ts +3 -1
  48. package/dist/primitives/list-view.d.ts.map +1 -1
  49. package/dist/primitives/page-toolbar.d.ts +61 -0
  50. package/dist/primitives/page-toolbar.d.ts.map +1 -0
  51. package/dist/primitives/refresh-button.d.ts +18 -0
  52. package/dist/primitives/refresh-button.d.ts.map +1 -0
  53. package/dist/primitives/search-select.d.ts +3 -1
  54. package/dist/primitives/search-select.d.ts.map +1 -1
  55. package/dist/primitives/tabs.d.ts.map +1 -1
  56. package/dist/stores/index.d.ts +1 -1
  57. package/dist/stores/index.d.ts.map +1 -1
  58. package/dist/stores/page-header.store.d.ts +26 -2
  59. package/dist/stores/page-header.store.d.ts.map +1 -1
  60. package/dist/styles.css +224 -0
  61. package/package.json +5 -5
  62. package/dist/chunk-22EVFMS3.js.map +0 -1
  63. package/dist/chunk-7BMXN7MP.js.map +0 -1
  64. package/dist/chunk-JGJNRNQW.js.map +0 -1
  65. package/dist/chunk-OKZ6WQB3.js +0 -57
  66. package/dist/chunk-OKZ6WQB3.js.map +0 -1
  67. package/dist/chunk-QSHA2Q27.js.map +0 -1
  68. package/dist/chunk-VAKWUUVC.js +0 -425
  69. package/dist/chunk-VAKWUUVC.js.map +0 -1
  70. package/dist/chunk-XRDCYOND.js.map +0 -1
  71. package/dist/chunk-ZBQ772DU.js +0 -17
  72. package/dist/chunk-ZBQ772DU.js.map +0 -1
@@ -1,40 +1,720 @@
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 { cn, useHasPageHeaderHost, usePageHeader, useLayoutStore, useRegisterPageActions, useHasPageHeaderBack, usePageHeaderSlot, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-LFZFBN3P.js';
2
+ import { ICON_MAP } from './chunk-UZYXFEQS.js';
3
+ import * as React7 from 'react';
4
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
5
+ import { X, Loader2, ArrowLeft, Menu, Search, Crown, PanelLeftClose, PanelLeft, Bell, 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, ExternalLink, PiggyBank, LogOut } from 'lucide-react';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+ import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
8
8
  import * as Popover from '@radix-ui/react-popover';
9
9
  import { createPortal } from 'react-dom';
10
- import { useTranslation } from '@fayz-ai/core';
11
10
 
11
+ var STYLE_ID = "saas-sheet-styles";
12
+ if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
13
+ const style = document.createElement("style");
14
+ style.id = STYLE_ID;
15
+ style.textContent = `
16
+ @keyframes saas-sheet-fade-in { from { opacity: 0 } to { opacity: 1 } }
17
+ @keyframes saas-sheet-fade-out { from { opacity: 1 } to { opacity: 0 } }
18
+ @keyframes saas-sheet-slide-in { from { transform: translateX(100%) } to { transform: translateX(0) } }
19
+ @keyframes saas-sheet-slide-out { from { transform: translateX(0) } to { transform: translateX(100%) } }
20
+ .saas-sheet-overlay[data-state="open"] { animation: saas-sheet-fade-in 200ms ease-out }
21
+ .saas-sheet-overlay[data-state="closed"] { animation: saas-sheet-fade-out 150ms ease-in }
22
+ .saas-sheet-content[data-state="open"] { animation: saas-sheet-slide-in 280ms cubic-bezier(0.16, 1, 0.3, 1) }
23
+ .saas-sheet-content[data-state="closed"] { animation: saas-sheet-slide-out 200ms ease-in }
24
+ `;
25
+ document.head.appendChild(style);
26
+ }
27
+ var Sheet = DialogPrimitive.Root;
28
+ var SheetTrigger = DialogPrimitive.Trigger;
29
+ var SheetClose = DialogPrimitive.Close;
30
+ var SheetPortal = DialogPrimitive.Portal;
31
+ var SheetOverlay = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
32
+ DialogPrimitive.Overlay,
33
+ {
34
+ ref,
35
+ className: cn(
36
+ "saas-sheet-overlay fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px]",
37
+ className
38
+ ),
39
+ ...props
40
+ }
41
+ ));
42
+ SheetOverlay.displayName = "SheetOverlay";
43
+ var SheetContent = React7.forwardRef(({ className, children, width, hideClose, overlay = "blur", ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
44
+ /* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
45
+ /* @__PURE__ */ jsxs(
46
+ DialogPrimitive.Content,
47
+ {
48
+ ref,
49
+ className: cn(
50
+ "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",
51
+ "w-full",
52
+ width ?? "max-w-md",
53
+ className
54
+ ),
55
+ ...props,
56
+ children: [
57
+ children,
58
+ !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: [
59
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
60
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
61
+ ] })
62
+ ]
63
+ }
64
+ )
65
+ ] }));
66
+ SheetContent.displayName = "SheetContent";
67
+ function SheetHeader({ className, ...props }) {
68
+ return /* @__PURE__ */ jsx(
69
+ "div",
70
+ {
71
+ className: cn("flex flex-col gap-1 px-5 py-4 border-b shrink-0", className),
72
+ ...props
73
+ }
74
+ );
75
+ }
76
+ SheetHeader.displayName = "SheetHeader";
77
+ function SheetFooter({ className, ...props }) {
78
+ return /* @__PURE__ */ jsx(
79
+ "div",
80
+ {
81
+ className: cn("flex items-center gap-2 px-5 py-3 border-t shrink-0", className),
82
+ ...props
83
+ }
84
+ );
85
+ }
86
+ SheetFooter.displayName = "SheetFooter";
87
+ var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
88
+ DialogPrimitive.Title,
89
+ {
90
+ ref,
91
+ className: cn("text-sm font-semibold leading-none tracking-tight", className),
92
+ ...props
93
+ }
94
+ ));
95
+ SheetTitle.displayName = "SheetTitle";
96
+ var SheetDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
97
+ DialogPrimitive.Description,
98
+ {
99
+ ref,
100
+ className: cn("text-xs text-muted-foreground", className),
101
+ ...props
102
+ }
103
+ ));
104
+ SheetDescription.displayName = "SheetDescription";
105
+ function SheetBody({ className, ...props }) {
106
+ return /* @__PURE__ */ jsx(
107
+ "div",
108
+ {
109
+ className: cn("flex-1 overflow-y-auto px-5 py-5", className),
110
+ ...props
111
+ }
112
+ );
113
+ }
114
+ SheetBody.displayName = "SheetBody";
115
+ var SaveBarApiContext = React7.createContext(null);
116
+ var SaveBarStateContext = React7.createContext({ visible: false, saving: false });
117
+ var _saveBarVisible = false;
118
+ var _saveBarSubs = /* @__PURE__ */ new Set();
119
+ function setSaveBarVisibleSignal(v) {
120
+ if (v === _saveBarVisible) return;
121
+ _saveBarVisible = v;
122
+ _saveBarSubs.forEach((fn) => fn());
123
+ }
124
+ function useSaveBarVisible() {
125
+ return React7.useSyncExternalStore(
126
+ (cb) => {
127
+ _saveBarSubs.add(cb);
128
+ return () => {
129
+ _saveBarSubs.delete(cb);
130
+ };
131
+ },
132
+ () => _saveBarVisible,
133
+ () => false
134
+ );
135
+ }
136
+ function SaveBarProvider({ children }) {
137
+ const [state, setState] = React7.useState({ visible: false, saving: false });
138
+ const handlers = React7.useRef({});
139
+ const api = React7.useMemo(() => ({ setState, handlers }), []);
140
+ const backStack = React7.useRef([]);
141
+ const backApi = React7.useMemo(() => ({ stack: backStack }), []);
142
+ React7.useEffect(() => {
143
+ setSaveBarVisibleSignal(state.visible);
144
+ }, [state.visible]);
145
+ React7.useEffect(() => {
146
+ function onKey(e) {
147
+ if (state.visible && !state.saving) {
148
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
149
+ e.preventDefault();
150
+ handlers.current.onSave?.();
151
+ } else if (e.key === "Escape") {
152
+ e.preventDefault();
153
+ handlers.current.onDiscard?.();
154
+ }
155
+ return;
156
+ }
157
+ if (e.key === "Escape" && !e.defaultPrevented && !isEditableTarget(e.target)) {
158
+ for (let i = backStack.current.length - 1; i >= 0; i--) {
159
+ const back = backStack.current[i].current;
160
+ if (back) {
161
+ e.preventDefault();
162
+ back();
163
+ return;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ window.addEventListener("keydown", onKey);
169
+ return () => window.removeEventListener("keydown", onKey);
170
+ }, [state.visible, state.saving]);
171
+ return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
172
+ }
173
+ var BackNavApiContext = React7.createContext(null);
174
+ function isEditableTarget(target) {
175
+ const el = target;
176
+ if (!el || !el.tagName) return false;
177
+ const tag = el.tagName.toLowerCase();
178
+ return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
179
+ }
180
+ function useBackHandler(onBack) {
181
+ const api = React7.useContext(BackNavApiContext);
182
+ const ref = React7.useRef(onBack);
183
+ React7.useEffect(() => {
184
+ ref.current = onBack;
185
+ });
186
+ React7.useEffect(() => {
187
+ if (!api) return;
188
+ const stack = api.stack.current;
189
+ stack.push(ref);
190
+ return () => {
191
+ const i = stack.indexOf(ref);
192
+ if (i !== -1) stack.splice(i, 1);
193
+ };
194
+ }, [api]);
195
+ }
196
+ function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
197
+ const api = React7.useContext(SaveBarApiContext);
198
+ React7.useEffect(() => {
199
+ if (api) api.handlers.current = { onSave, onDiscard };
200
+ });
201
+ React7.useEffect(() => {
202
+ if (!api) return;
203
+ api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
204
+ return () => api.setState((s) => ({ ...s, visible: false }));
205
+ }, [api, dirty, saving, message, saveLabel, discardLabel]);
206
+ }
207
+ function SaveBar({ className }) {
208
+ const state = React7.useContext(SaveBarStateContext);
209
+ const api = React7.useContext(SaveBarApiContext);
210
+ const t = useTranslation();
211
+ const [rendered, setRendered] = React7.useState(state.visible);
212
+ const [closing, setClosing] = React7.useState(false);
213
+ React7.useEffect(() => {
214
+ if (state.visible) {
215
+ setRendered(true);
216
+ setClosing(false);
217
+ return;
218
+ }
219
+ if (!rendered) return;
220
+ setClosing(true);
221
+ const id = setTimeout(() => {
222
+ setRendered(false);
223
+ setClosing(false);
224
+ }, 200);
225
+ return () => clearTimeout(id);
226
+ }, [state.visible, rendered]);
227
+ if (!api || !rendered) return null;
228
+ const { saving, message, saveLabel, discardLabel } = state;
229
+ 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(
230
+ "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",
231
+ closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
232
+ ), children: [
233
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
234
+ /* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
235
+ message ?? t("saveBar.unsaved")
236
+ ] }),
237
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
238
+ /* @__PURE__ */ jsx(
239
+ "button",
240
+ {
241
+ type: "button",
242
+ disabled: saving,
243
+ onClick: () => api.handlers.current.onDiscard?.(),
244
+ 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",
245
+ children: discardLabel ?? t("common.discard")
246
+ }
247
+ ),
248
+ /* @__PURE__ */ jsxs(
249
+ "button",
250
+ {
251
+ type: "button",
252
+ disabled: saving,
253
+ onClick: () => api.handlers.current.onSave?.(),
254
+ 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",
255
+ children: [
256
+ saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
257
+ saveLabel ?? t("common.save")
258
+ ]
259
+ }
260
+ )
261
+ ] })
262
+ ] }) });
263
+ }
264
+ function useCrossModuleBack(onBack, parentLabel) {
265
+ const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
266
+ const referrer = useNavReferrer(currentPath);
267
+ const cross = referrer && referrer.label && routeModule(referrer.path) !== routeModule(currentPath) ? referrer : null;
268
+ return {
269
+ onBack: cross ? () => navHistoryBack(onBack) : onBack,
270
+ parentLabel: cross ? cross.label : parentLabel,
271
+ isCrossModule: !!cross
272
+ };
273
+ }
274
+ function PageHeaderCrumb({
275
+ title,
276
+ parentLabel,
277
+ onBack
278
+ }) {
279
+ if (!onBack) {
280
+ return title ? /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-lg font-semibold", children: title }) : null;
281
+ }
282
+ return /* @__PURE__ */ jsxs("nav", { className: "flex min-w-0 items-center gap-1.5 text-sm text-muted-foreground", children: [
283
+ /* @__PURE__ */ jsxs(
284
+ "button",
285
+ {
286
+ type: "button",
287
+ onClick: onBack,
288
+ className: "inline-flex shrink-0 items-center gap-1 transition-colors hover:text-foreground",
289
+ children: [
290
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "h-3.5 w-3.5" }),
291
+ parentLabel ?? "Voltar"
292
+ ]
293
+ }
294
+ ),
295
+ title != null && /* @__PURE__ */ jsxs(Fragment, { children: [
296
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", children: "/" }),
297
+ title === "" ? (
298
+ // Title still waiting on data: a pulsing bar, otherwise the line
299
+ // appears from nowhere and shoves the rest when the record lands.
300
+ /* @__PURE__ */ jsx("span", { className: "inline-block h-4 w-24 animate-pulse rounded bg-muted align-middle" })
301
+ ) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-foreground", children: title })
302
+ ] })
303
+ ] });
304
+ }
305
+ function PageHeader({ title, onBack, parentLabel, backLabel }) {
306
+ const hasHost = useHasPageHeaderHost();
307
+ const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
308
+ useBackHandler(effectiveOnBack);
309
+ const node = React7.useMemo(() => /* @__PURE__ */ jsx(
310
+ PageHeaderCrumb,
311
+ {
312
+ title,
313
+ parentLabel: effectiveParent ?? backLabel,
314
+ onBack: effectiveOnBack
315
+ }
316
+ ), [effectiveOnBack, effectiveParent, backLabel, title]);
317
+ usePageHeader(hasHost ? node : null, { back: !!effectiveOnBack });
318
+ if (hasHost) return null;
319
+ return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
320
+ }
321
+ function TopbarUserMenu({
322
+ user,
323
+ userPlan,
324
+ billingLabel = "Subscription",
325
+ onSignOut,
326
+ onProfile,
327
+ onSettings,
328
+ onBilling,
329
+ userMenuExtras
330
+ }) {
331
+ const [open, setOpen] = React7.useState(false);
332
+ if (!user) return null;
333
+ const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
334
+ const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
335
+ "button",
336
+ {
337
+ onClick: () => {
338
+ setOpen(false);
339
+ onClick();
340
+ },
341
+ className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
342
+ children: [
343
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
344
+ /* @__PURE__ */ jsx("span", { children: label })
345
+ ]
346
+ },
347
+ label
348
+ ) : null;
349
+ return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
350
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
351
+ "button",
352
+ {
353
+ "aria-label": "User menu",
354
+ className: "ml-1 flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-primary text-xs font-semibold text-primary-foreground ring-2 ring-transparent transition hover:ring-sidebar-border",
355
+ children: user.avatarUrl ? (
356
+ // eslint-disable-next-line @next/next/no-img-element
357
+ /* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
358
+ ) : initial
359
+ }
360
+ ) }),
361
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
362
+ Popover.Content,
363
+ {
364
+ align: "end",
365
+ sideOffset: 8,
366
+ className: "fayz-glass-surface z-50 min-w-[220px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
367
+ children: [
368
+ /* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
369
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
370
+ /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
371
+ ] }),
372
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
373
+ item("Perfil", onProfile, UserCog),
374
+ item("Configura\xE7\xF5es", onSettings, Settings),
375
+ onBilling && /* @__PURE__ */ jsxs(
376
+ "button",
377
+ {
378
+ onClick: () => {
379
+ setOpen(false);
380
+ onBilling();
381
+ },
382
+ className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
383
+ children: [
384
+ /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
385
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: billingLabel }),
386
+ userPlan && /* @__PURE__ */ jsxs(
387
+ "span",
388
+ {
389
+ className: userPlan.paid ? "inline-flex items-center gap-1 rounded-full border border-amber-400/40 bg-amber-400/15 px-2 py-0.5 text-[10px] font-semibold text-amber-600 dark:text-amber-300" : "inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
390
+ children: [
391
+ userPlan.paid ? /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
392
+ userPlan.label
393
+ ]
394
+ }
395
+ )
396
+ ]
397
+ },
398
+ "billing"
399
+ ),
400
+ userMenuExtras?.map((e) => item(e.label, e.onClick)),
401
+ onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
402
+ /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
403
+ item("Sair", onSignOut, LogOut)
404
+ ] })
405
+ ] })
406
+ ]
407
+ }
408
+ ) })
409
+ ] });
410
+ }
411
+ var ICON_MAP2 = {
412
+ Home,
413
+ Users,
414
+ Settings,
415
+ CreditCard,
416
+ Bell,
417
+ Calendar,
418
+ CalendarClock,
419
+ Package,
420
+ Activity,
421
+ BarChart3,
422
+ FileText,
423
+ Mail,
424
+ DollarSign,
425
+ Megaphone,
426
+ ShoppingCart,
427
+ ShoppingBag,
428
+ Target,
429
+ Wrench,
430
+ ClipboardList,
431
+ ClipboardCheck,
432
+ Briefcase,
433
+ UserCog,
434
+ BookOpen,
435
+ MessageCircle,
436
+ Globe,
437
+ BookOpenCheck,
438
+ Percent,
439
+ Tag,
440
+ Tags,
441
+ Camera,
442
+ UtensilsCrossed,
443
+ Search,
444
+ MapPin,
445
+ Handshake,
446
+ Contact,
447
+ Building2,
448
+ ChevronDown,
449
+ Filter,
450
+ Plus,
451
+ Shield,
452
+ List,
453
+ ListChecks,
454
+ FolderOpen,
455
+ Dog,
456
+ Cat,
457
+ PawPrint,
458
+ Heart,
459
+ LayoutTemplate,
460
+ LeafyGreen,
461
+ Apple,
462
+ Egg,
463
+ Wheat,
464
+ ArrowDownCircle,
465
+ ArrowUpCircle,
466
+ ArrowLeftRight,
467
+ Plug,
468
+ SlidersHorizontal,
469
+ TableProperties,
470
+ Landmark,
471
+ Receipt,
472
+ TrendingUp,
473
+ CircleDollarSign,
474
+ Clock,
475
+ AlertTriangle,
476
+ Sparkles,
477
+ Wallet,
478
+ Warehouse,
479
+ Ruler,
480
+ ArrowUpRight,
481
+ ArrowDownRight,
482
+ UserPlus,
483
+ UserX,
484
+ TreePalm,
485
+ Boxes,
486
+ Box,
487
+ FileCheck2,
488
+ PartyPopper,
489
+ Radio,
490
+ CalendarDays,
491
+ BadgeDollarSign,
492
+ Banknote,
493
+ Layers,
494
+ Music,
495
+ Eye,
496
+ ListMusic,
497
+ Disc3,
498
+ UsersRound,
499
+ Mic,
500
+ CalendarCheck2,
501
+ CalendarX,
502
+ ListPlus,
503
+ Map,
504
+ Ban,
505
+ Clock3,
506
+ Inbox,
507
+ UserCheck,
508
+ Star,
509
+ Zap,
510
+ Clapperboard,
511
+ Newspaper,
512
+ GraduationCap,
513
+ Repeat
514
+ };
515
+ function getIcon(name) {
516
+ return ICON_MAP2[name] ?? Home;
517
+ }
518
+ function NavDropdown({ item, currentPath, onNavigate }) {
519
+ const [open, setOpen] = React7.useState(false);
520
+ const Icon = getIcon(item.icon);
521
+ const isChildActive = item.children?.some(
522
+ (child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
523
+ );
524
+ return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
525
+ /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
526
+ "button",
527
+ {
528
+ "data-nav-active": isChildActive || void 0,
529
+ className: cn(
530
+ "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
531
+ isChildActive ? "border-sidebar-accent-foreground font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
532
+ ),
533
+ children: [
534
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
535
+ /* @__PURE__ */ jsx("span", { children: item.label }),
536
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3 w-3 transition-transform", open && "rotate-180") })
537
+ ]
538
+ }
539
+ ) }),
540
+ /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
541
+ Popover.Content,
542
+ {
543
+ align: "start",
544
+ sideOffset: 4,
545
+ className: "fayz-glass-surface z-50 min-w-[200px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
546
+ children: item.children?.map((child) => {
547
+ const ChildIcon = getIcon(child.icon);
548
+ const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
549
+ return /* @__PURE__ */ jsxs(
550
+ "button",
551
+ {
552
+ onClick: () => {
553
+ onNavigate(child.route);
554
+ setOpen(false);
555
+ },
556
+ className: cn(
557
+ "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors",
558
+ isActive ? "bg-muted font-medium text-foreground" : "text-popover-foreground hover:bg-muted/70"
559
+ ),
560
+ children: [
561
+ /* @__PURE__ */ jsx(ChildIcon, { className: "h-4 w-4 text-muted-foreground" }),
562
+ /* @__PURE__ */ jsx("span", { children: child.label })
563
+ ]
564
+ },
565
+ child.id
566
+ );
567
+ })
568
+ }
569
+ ) })
570
+ ] });
571
+ }
572
+ function Topbar({
573
+ navigation,
574
+ logo,
575
+ user,
576
+ onMenuClick,
577
+ orgSwitcher,
578
+ leftContent,
579
+ rightContent,
580
+ currentPath = "/",
581
+ onNavigate,
582
+ searchPlaceholder = "Search...",
583
+ onSignOut,
584
+ onProfile,
585
+ onSettings,
586
+ onBilling,
587
+ userPlan,
588
+ billingLabel,
589
+ userMenuExtras
590
+ }) {
591
+ const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
592
+ const mainNav = navigation.filter((item) => item.section === "main");
593
+ const secondaryNav = navigation.filter((item) => item.section === "secondary");
594
+ const allNav = [...mainNav, ...secondaryNav];
595
+ const handleNavigate = (route) => {
596
+ onNavigate?.(route);
597
+ };
598
+ return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
599
+ /* @__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: [
600
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
601
+ onMenuClick && /* @__PURE__ */ jsx(
602
+ "button",
603
+ {
604
+ onClick: onMenuClick,
605
+ className: "inline-flex items-center justify-center rounded-md p-2 text-sidebar-foreground/70 hover:bg-sidebar/30 hover:text-sidebar-foreground md:hidden",
606
+ "aria-label": "Open menu",
607
+ children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
608
+ }
609
+ ),
610
+ /* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
611
+ ] }),
612
+ /* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
613
+ "button",
614
+ {
615
+ onClick: () => setCommandPaletteOpen(true),
616
+ className: "relative flex h-8 w-full items-center rounded-md border border-sidebar-border bg-sidebar-accent px-3 pl-8 text-sm text-sidebar-muted shadow-sm transition-colors hover:border-sidebar-foreground/25 hover:bg-sidebar-foreground/10 hover:text-sidebar-foreground",
617
+ children: [
618
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
619
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
620
+ /* @__PURE__ */ jsx("kbd", { className: "ml-auto hidden rounded border border-sidebar-border bg-sidebar-accent px-1.5 py-0.5 text-[10px] font-medium text-sidebar-muted sm:inline-block", children: "\u2318K" })
621
+ ]
622
+ }
623
+ ) }),
624
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
625
+ orgSwitcher && /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 items-center md:flex", children: orgSwitcher }),
626
+ leftContent,
627
+ rightContent,
628
+ /* @__PURE__ */ jsx(
629
+ TopbarUserMenu,
630
+ {
631
+ user,
632
+ userPlan,
633
+ billingLabel,
634
+ onSignOut,
635
+ onProfile,
636
+ onSettings,
637
+ onBilling,
638
+ userMenuExtras
639
+ }
640
+ )
641
+ ] })
642
+ ] }) }),
643
+ /* @__PURE__ */ jsx("div", { className: "hidden md:block bg-sidebar", children: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-full items-center px-4 md:px-6", children: /* @__PURE__ */ jsx("nav", { ref: (el) => {
644
+ if (!el) return;
645
+ const active = el.querySelector('[data-nav-active="true"]');
646
+ if (active) {
647
+ const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
648
+ el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
649
+ }
650
+ }, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-none", children: allNav.map((item) => {
651
+ if (item.children && item.children.length > 0) {
652
+ return /* @__PURE__ */ jsx(
653
+ NavDropdown,
654
+ {
655
+ item,
656
+ currentPath,
657
+ onNavigate: handleNavigate
658
+ },
659
+ item.id
660
+ );
661
+ }
662
+ const Icon = getIcon(item.icon);
663
+ const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
664
+ return /* @__PURE__ */ jsxs(
665
+ "button",
666
+ {
667
+ "data-nav-active": isActive || void 0,
668
+ onClick: () => handleNavigate(item.route),
669
+ className: cn(
670
+ "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
671
+ isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
672
+ ),
673
+ children: [
674
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
675
+ /* @__PURE__ */ jsx("span", { children: item.label }),
676
+ item.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 text-amber-500", "aria-hidden": true }),
677
+ item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground", children: item.badge })
678
+ ]
679
+ },
680
+ item.id
681
+ );
682
+ }) }) }) })
683
+ ] });
684
+ }
12
685
  var GUTTER_PX = 8;
13
686
  var CLOSE_DELAY_MS = 140;
14
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
687
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
15
688
  function iconOf(name) {
16
689
  if (!name) return null;
17
690
  return ICON_MAP[name] ?? null;
18
691
  }
692
+ var RailFlyoutContext = React7.createContext(null);
693
+ var RailFlyoutProvider = RailFlyoutContext.Provider;
694
+ function useRailRow(content) {
695
+ const flyout = React7.useContext(RailFlyoutContext);
696
+ if (!flyout || !content) return {};
697
+ return flyout.rowProps(content);
698
+ }
19
699
  function useRailFlyout({
20
700
  enabled,
21
701
  railRef,
22
702
  activeRoute,
23
703
  onNavigate
24
704
  }) {
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(() => {
705
+ const [state, setState] = React7.useState(null);
706
+ const [pos, setPos] = React7.useState({ top: 0, arrow: 20 });
707
+ const [animate, setAnimate] = React7.useState(false);
708
+ const panelRef = React7.useRef(null);
709
+ const closeTimer = React7.useRef(null);
710
+ const lastContent = React7.useRef(null);
711
+ const cancelClose = React7.useCallback(() => {
32
712
  if (closeTimer.current) {
33
713
  clearTimeout(closeTimer.current);
34
714
  closeTimer.current = null;
35
715
  }
36
716
  }, []);
37
- const show = React.useCallback(
717
+ const show = React7.useCallback(
38
718
  (anchor, content2) => {
39
719
  if (!enabled) return;
40
720
  cancelClose();
@@ -42,19 +722,19 @@ function useRailFlyout({
42
722
  },
43
723
  [enabled, cancelClose]
44
724
  );
45
- const hide = React.useCallback(() => {
725
+ const hide = React7.useCallback(() => {
46
726
  cancelClose();
47
727
  closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
48
728
  }, [cancelClose]);
49
- const dismiss = React.useCallback(() => {
729
+ const dismiss = React7.useCallback(() => {
50
730
  cancelClose();
51
731
  setState(null);
52
732
  }, [cancelClose]);
53
- React.useEffect(() => () => cancelClose(), [cancelClose]);
54
- React.useEffect(() => {
733
+ React7.useEffect(() => () => cancelClose(), [cancelClose]);
734
+ React7.useEffect(() => {
55
735
  if (!enabled) dismiss();
56
736
  }, [enabled, dismiss]);
57
- const reposition = React.useCallback(() => {
737
+ const reposition = React7.useCallback(() => {
58
738
  const rail = railRef.current;
59
739
  const anchor = state?.anchor;
60
740
  if (!rail || !anchor) return;
@@ -69,7 +749,7 @@ function useRailFlyout({
69
749
  setPos({ top, arrow });
70
750
  }, [railRef, state]);
71
751
  useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
72
- React.useEffect(() => {
752
+ React7.useEffect(() => {
73
753
  if (!state) return;
74
754
  const rail = railRef.current;
75
755
  const onScroll = () => reposition();
@@ -81,7 +761,7 @@ function useRailFlyout({
81
761
  };
82
762
  }, [state, railRef, reposition]);
83
763
  const visible = !!state;
84
- React.useEffect(() => {
764
+ React7.useEffect(() => {
85
765
  if (!visible) {
86
766
  setAnimate(false);
87
767
  return;
@@ -89,7 +769,7 @@ function useRailFlyout({
89
769
  const id = requestAnimationFrame(() => setAnimate(true));
90
770
  return () => cancelAnimationFrame(id);
91
771
  }, [visible]);
92
- React.useEffect(() => {
772
+ React7.useEffect(() => {
93
773
  if (!visible) return;
94
774
  const onKey = (event) => {
95
775
  if (event.key === "Escape") dismiss();
@@ -97,7 +777,7 @@ function useRailFlyout({
97
777
  window.addEventListener("keydown", onKey);
98
778
  return () => window.removeEventListener("keydown", onKey);
99
779
  }, [visible, dismiss]);
100
- const rowProps = React.useCallback(
780
+ const rowProps = React7.useCallback(
101
781
  (content2) => ({
102
782
  onMouseEnter: (event) => show(event.currentTarget, content2),
103
783
  onFocus: (event) => show(event.currentTarget, content2),
@@ -214,8 +894,8 @@ function FlyoutBody({
214
894
  ] });
215
895
  }
216
896
  var RAIL_PUSH_MS = 260;
217
- var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
218
- function getIcon(name) {
897
+ var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
898
+ function getIcon2(name) {
219
899
  return ICON_MAP[name] ?? Home;
220
900
  }
221
901
  function shortcutKey() {
@@ -248,11 +928,15 @@ function NavItem({
248
928
  onNavigate,
249
929
  flyout
250
930
  }) {
251
- const Icon = getIcon(item.icon);
931
+ const Icon = getIcon2(item.icon);
252
932
  const isExternal = !!item.href;
253
933
  const hoverProps = collapsed && flyout ? flyout.rowProps(flyoutContentOf(item)) : void 0;
254
934
  const className = cn(
255
- "flex w-full items-center gap-3 rounded-button px-2.5 py-1.5 text-[13px] font-medium transition-colors",
935
+ // 36px rows. The rail was condensed to 28px while it listed every
936
+ // destination flat; with the destinations folded into collapsible groups it
937
+ // shows a handful of rows at rest, and at that length the tight rhythm read
938
+ // as cramped rather than as dense.
939
+ "flex w-full items-center gap-3 rounded-button px-2.5 py-2 text-[13px] font-medium transition-colors",
256
940
  "hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
257
941
  isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground shadow-sm" : "text-sidebar-foreground/80",
258
942
  collapsed && "justify-center px-2"
@@ -310,8 +994,8 @@ function NavGroup({
310
994
  flyout
311
995
  }) {
312
996
  const isChildActive = item.children?.some((c) => c.route === activeRoute);
313
- const [open, setOpen] = React.useState(() => isChildActive ?? false);
314
- const Icon = getIcon(item.icon);
997
+ const [open, setOpen] = React7.useState(() => isChildActive ?? false);
998
+ const Icon = getIcon2(item.icon);
315
999
  if (collapsed) {
316
1000
  const hoverProps = flyout?.rowProps(flyoutContentOf(item));
317
1001
  return /* @__PURE__ */ jsx(
@@ -337,7 +1021,9 @@ function NavGroup({
337
1021
  {
338
1022
  onClick: () => setOpen(!open),
339
1023
  className: cn(
340
- "flex w-full items-center gap-3 rounded-button px-2.5 py-1.5 text-[13px] font-medium transition-colors",
1024
+ // Same metrics as NavItem a group header and a plain link are peers
1025
+ // in the rail and must share a baseline.
1026
+ "flex w-full items-center gap-3 rounded-button px-2.5 py-2 text-[13px] font-medium transition-colors",
341
1027
  "hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
342
1028
  isChildActive ? "text-sidebar-accent-foreground" : "text-sidebar-foreground/80"
343
1029
  ),
@@ -364,7 +1050,7 @@ function NavGroup({
364
1050
  // along with the labels and land the arrowhead on top of the text.
365
1051
  childConnector ? "relative" : "ml-4 space-y-0.5 border-l border-sidebar-border/50 pl-3"
366
1052
  ), children: item.children?.map((child, childIndex) => {
367
- const ChildIcon = getIcon(child.icon);
1053
+ const ChildIcon = getIcon2(child.icon);
368
1054
  const isActive = child.route === activeRoute;
369
1055
  const childExternal = !!child.href;
370
1056
  const childClass = cn(
@@ -382,7 +1068,7 @@ function NavGroup({
382
1068
  child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
383
1069
  ] });
384
1070
  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 });
385
- if (!childConnector) return /* @__PURE__ */ jsx(React.Fragment, { children: row }, child.id);
1071
+ if (!childConnector) return /* @__PURE__ */ jsx(React7.Fragment, { children: row }, child.id);
386
1072
  const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
387
1073
  const onPath = activeIndex >= 0 && childIndex < activeIndex;
388
1074
  return (
@@ -444,9 +1130,9 @@ function NavGroup({
444
1130
  ] });
445
1131
  }
446
1132
  function withCollapsed(slot, collapsed) {
447
- if (!React.isValidElement(slot)) return slot;
1133
+ if (!React7.isValidElement(slot)) return slot;
448
1134
  if (typeof slot.type === "string") return slot;
449
- return React.cloneElement(slot, { collapsed });
1135
+ return React7.cloneElement(slot, { collapsed });
450
1136
  }
451
1137
  function Sidebar({
452
1138
  navigation,
@@ -473,7 +1159,7 @@ function Sidebar({
473
1159
  const currentPath = currentPathProp;
474
1160
  const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
475
1161
  const activeRoute = resolveActiveRoute(navigation, currentPath);
476
- const railRef = React.useRef(null);
1162
+ const railRef = React7.useRef(null);
477
1163
  const flyout = useRailFlyout({
478
1164
  enabled: collapsed,
479
1165
  railRef,
@@ -486,7 +1172,7 @@ function Sidebar({
486
1172
  const handleNavigate = (route) => {
487
1173
  onNavigate?.(route);
488
1174
  };
489
- const sectionHeading = (label) => label && !collapsed ? /* @__PURE__ */ jsx("p", { className: "px-2.5 pb-1 pt-1 text-[10px] font-semibold uppercase tracking-wider text-sidebar-muted", children: label }) : null;
1175
+ const sectionHeading = (label) => label && !collapsed ? /* @__PURE__ */ jsx("p", { className: "px-2.5 pb-0.5 pt-1 text-[10px] font-semibold uppercase tracking-wider text-sidebar-muted", children: label }) : null;
490
1176
  const groupRuns = (items) => {
491
1177
  const runs = [];
492
1178
  for (const item of items) {
@@ -498,10 +1184,14 @@ function Sidebar({
498
1184
  };
499
1185
  const renderSection = (items, label) => {
500
1186
  const runs = groupRuns(items);
501
- return runs.map((run, i) => /* @__PURE__ */ jsxs("div", { className: i === 0 ? "space-y-1" : "mt-6 space-y-1", children: [
502
- sectionHeading(run.group ?? label),
503
- run.items.map(renderItem)
504
- ] }, run.group ?? `ungrouped-${i}`));
1187
+ return runs.map((run, i) => (
1188
+ // 4px between rows, 20px between runs (a run being a `group` heading's
1189
+ // worth of entries — see NavigationItem.group).
1190
+ /* @__PURE__ */ jsxs("div", { className: i === 0 ? "space-y-1" : "mt-5 space-y-1", children: [
1191
+ sectionHeading(run.group ?? label),
1192
+ run.items.map(renderItem)
1193
+ ] }, run.group ?? `ungrouped-${i}`)
1194
+ ));
505
1195
  };
506
1196
  const renderItem = (item) => item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(NavGroup, { item, collapsed, activeRoute, onNavigate: handleNavigate, childConnector: navChildConnector, flyout }, item.id) : /* @__PURE__ */ jsx(
507
1197
  NavItem,
@@ -519,10 +1209,10 @@ function Sidebar({
519
1209
  mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
520
1210
  secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
521
1211
  ] });
522
- const [outgoing, setOutgoing] = React.useState(null);
523
- const lastBodyRef = React.useRef(null);
524
- const lastKeyRef = React.useRef(navKey);
525
- const timerRef = React.useRef(null);
1212
+ const [outgoing, setOutgoing] = React7.useState(null);
1213
+ const lastBodyRef = React7.useRef(null);
1214
+ const lastKeyRef = React7.useRef(navKey);
1215
+ const timerRef = React7.useRef(null);
526
1216
  useIsomorphicLayoutEffect2(() => {
527
1217
  if (lastKeyRef.current !== navKey) {
528
1218
  const previousBody = lastBodyRef.current;
@@ -535,533 +1225,183 @@ function Sidebar({
535
1225
  }
536
1226
  lastBodyRef.current = navBody;
537
1227
  });
538
- React.useEffect(() => () => {
1228
+ React7.useEffect(() => () => {
539
1229
  if (timerRef.current) clearTimeout(timerRef.current);
540
1230
  }, []);
541
- return /* @__PURE__ */ jsxs(
542
- "aside",
543
- {
544
- ref: railRef,
545
- "data-print": "hide",
546
- onBlur: (event) => {
547
- if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
548
- },
549
- className: cn(
550
- // `relative` anchors the flyout, which hangs off the rail's right edge
551
- // and has to escape the nav's own scroll box to do it.
552
- "relative flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
553
- !borderless && "border-r border-sidebar-border",
554
- collapsed ? "w-16" : "w-64"
555
- ),
556
- children: [
557
- /* @__PURE__ */ jsxs(
558
- "div",
559
- {
560
- className: cn(
561
- "flex h-14 items-center justify-between px-4",
562
- !borderless && "border-b border-sidebar-border",
563
- collapsed && "justify-center px-2"
564
- ),
565
- children: [
566
- !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
567
- logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
568
- /* @__PURE__ */ jsx(
569
- "button",
570
- {
571
- onClick: onToggle,
572
- className: "ml-auto rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
573
- "aria-label": "Collapse sidebar",
574
- children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
575
- }
576
- )
577
- ] }),
578
- collapsed && /* @__PURE__ */ jsx(
579
- "button",
580
- {
581
- onClick: onToggle,
582
- className: "rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
583
- "aria-label": "Expand sidebar",
584
- children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
585
- }
586
- )
587
- ]
588
- }
589
- ),
590
- topContent && /* @__PURE__ */ jsx("div", { className: "px-2 pt-2", children: withCollapsed(topContent, collapsed) }),
591
- /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
592
- "button",
593
- {
594
- onClick: () => setCommandPaletteOpen(true),
595
- "aria-keyshortcuts": "Meta+K Control+K",
596
- className: cn(
597
- // Tonal surface, no outline. On a dark rail a 1px border is a hard
598
- // edge competing with every other hard edge; a lighter fill of the
599
- // same hue reads as "a field" without drawing a box around it — and
600
- // leaves the outline treatment free to mean focus, which it must.
601
- "fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
602
- "hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
603
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
604
- collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
605
- ),
606
- "aria-label": searchLabel,
607
- title: collapsed ? searchLabel : void 0,
608
- children: [
609
- /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
610
- !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
611
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
612
- /* @__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: [
613
- shortcutKey(),
614
- "K"
615
- ] })
616
- ] })
617
- ]
618
- }
619
- ) }),
620
- /* @__PURE__ */ jsxs(
621
- "nav",
622
- {
623
- className: cn(
624
- "relative flex-1 p-2",
625
- // scrollbar-none: the rail still scrolls, but without the native
626
- // scrollbar gutter eating into the nav items.
627
- outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
628
- ),
629
- children: [
630
- outgoing && /* @__PURE__ */ jsx(
631
- "div",
632
- {
633
- "aria-hidden": true,
634
- className: cn(
635
- "pointer-events-none absolute inset-x-2 top-2 space-y-1",
636
- outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
637
- ),
638
- children: outgoing.body
639
- }
640
- ),
641
- /* @__PURE__ */ jsx(
642
- "div",
643
- {
644
- className: cn(
645
- "space-y-1",
646
- outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
647
- ),
648
- children: navBody
649
- },
650
- navKey
651
- )
652
- ]
653
- }
654
- ),
655
- 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(
656
- NavItem,
657
- {
658
- item,
659
- collapsed,
660
- isActive: item.route === activeRoute,
661
- onNavigate: handleNavigate,
662
- flyout
663
- },
664
- item.id
665
- )) }),
666
- footerContent && // `empty:hidden` — slot children may all render null (e.g. the
667
- // SeuSiteWidget self-hides when the tenant has no project); without
668
- // it the padded/bordered wrapper shows as a blank block.
669
- /* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
670
- withCollapsed(orgSwitcher, collapsed),
671
- /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
672
- "flex items-center rounded-button",
673
- collapsed ? "flex-col gap-1" : "gap-1 px-1"
674
- ), children: [
675
- userMenuSlot,
676
- !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
677
- notificationSlot ?? /* @__PURE__ */ jsxs(
678
- "button",
679
- {
680
- 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",
681
- "aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
682
- children: [
683
- /* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
684
- unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
685
- ]
686
- }
687
- )
688
- ] }) }),
689
- flyout.panel
690
- ]
691
- }
692
- );
693
- }
694
- function TopbarUserMenu({
695
- user,
696
- userPlan,
697
- billingLabel = "Subscription",
698
- onSignOut,
699
- onProfile,
700
- onSettings,
701
- onBilling,
702
- userMenuExtras
703
- }) {
704
- const [open, setOpen] = React.useState(false);
705
- if (!user) return null;
706
- const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
707
- const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
708
- "button",
709
- {
710
- onClick: () => {
711
- setOpen(false);
712
- onClick();
713
- },
714
- className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
715
- children: [
716
- Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
717
- /* @__PURE__ */ jsx("span", { children: label })
718
- ]
719
- },
720
- label
721
- ) : null;
722
- return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
723
- /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
724
- "button",
725
- {
726
- "aria-label": "User menu",
727
- className: "ml-1 flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-primary text-xs font-semibold text-primary-foreground ring-2 ring-transparent transition hover:ring-sidebar-border",
728
- children: user.avatarUrl ? (
729
- // eslint-disable-next-line @next/next/no-img-element
730
- /* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
731
- ) : initial
732
- }
733
- ) }),
734
- /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
735
- Popover.Content,
736
- {
737
- align: "end",
738
- sideOffset: 8,
739
- className: "fayz-glass-surface z-50 min-w-[220px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
740
- children: [
741
- /* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
742
- /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
743
- /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
744
- ] }),
745
- /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
746
- item("Perfil", onProfile, UserCog),
747
- item("Configura\xE7\xF5es", onSettings, Settings),
748
- onBilling && /* @__PURE__ */ jsxs(
749
- "button",
750
- {
751
- onClick: () => {
752
- setOpen(false);
753
- onBilling();
754
- },
755
- className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
756
- children: [
757
- /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
758
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: billingLabel }),
759
- userPlan && /* @__PURE__ */ jsxs(
760
- "span",
761
- {
762
- className: userPlan.paid ? "inline-flex items-center gap-1 rounded-full border border-amber-400/40 bg-amber-400/15 px-2 py-0.5 text-[10px] font-semibold text-amber-600 dark:text-amber-300" : "inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
763
- children: [
764
- userPlan.paid ? /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
765
- userPlan.label
766
- ]
767
- }
768
- )
769
- ]
770
- },
771
- "billing"
772
- ),
773
- userMenuExtras?.map((e) => item(e.label, e.onClick)),
774
- onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
775
- /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
776
- item("Sair", onSignOut, LogOut)
777
- ] })
778
- ] })
779
- ]
780
- }
781
- ) })
782
- ] });
783
- }
784
- var ICON_MAP2 = {
785
- Home: Home,
786
- Users,
787
- Settings: Settings,
788
- CreditCard,
789
- Bell: Bell,
790
- Calendar,
791
- CalendarClock,
792
- Package,
793
- Activity,
794
- BarChart3,
795
- FileText,
796
- Mail,
797
- DollarSign,
798
- Megaphone,
799
- ShoppingCart,
800
- ShoppingBag,
801
- Target,
802
- Wrench,
803
- ClipboardList,
804
- ClipboardCheck,
805
- Briefcase,
806
- UserCog,
807
- BookOpen,
808
- MessageCircle,
809
- Globe,
810
- BookOpenCheck,
811
- Percent,
812
- Tag,
813
- Tags,
814
- Camera,
815
- UtensilsCrossed,
816
- Search: Search,
817
- MapPin,
818
- Handshake,
819
- Contact,
820
- Building2,
821
- ChevronDown: ChevronDown,
822
- Filter,
823
- Plus,
824
- Shield,
825
- List,
826
- ListChecks,
827
- FolderOpen,
828
- Dog,
829
- Cat,
830
- PawPrint,
831
- Heart,
832
- LayoutTemplate,
833
- LeafyGreen,
834
- Apple,
835
- Egg,
836
- Wheat,
837
- ArrowDownCircle,
838
- ArrowUpCircle,
839
- ArrowLeftRight,
840
- Plug,
841
- SlidersHorizontal,
842
- TableProperties,
843
- Landmark,
844
- Receipt,
845
- TrendingUp,
846
- CircleDollarSign,
847
- Clock,
848
- AlertTriangle,
849
- Sparkles,
850
- Wallet,
851
- Warehouse,
852
- Ruler,
853
- ArrowUpRight,
854
- ArrowDownRight,
855
- UserPlus,
856
- UserX,
857
- TreePalm,
858
- Boxes,
859
- Box,
860
- FileCheck2,
861
- PartyPopper,
862
- Radio,
863
- CalendarDays,
864
- BadgeDollarSign,
865
- Banknote,
866
- Layers,
867
- Music,
868
- Eye,
869
- ListMusic,
870
- Disc3,
871
- UsersRound,
872
- Mic,
873
- CalendarCheck2,
874
- CalendarX,
875
- ListPlus,
876
- Map,
877
- Ban,
878
- Clock3,
879
- Inbox,
880
- UserCheck,
881
- Star,
882
- Zap,
883
- Clapperboard,
884
- Newspaper,
885
- GraduationCap,
886
- Repeat
887
- };
888
- function getIcon2(name) {
889
- return ICON_MAP2[name] ?? Home;
890
- }
891
- function NavDropdown({ item, currentPath, onNavigate }) {
892
- const [open, setOpen] = React.useState(false);
893
- const Icon = getIcon2(item.icon);
894
- const isChildActive = item.children?.some(
895
- (child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
896
- );
897
- return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
898
- /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
899
- "button",
1231
+ return (
1232
+ // O handle do flyout desce por contexto para QUALQUER linha da trilha —
1233
+ // inclusive os widgets que os plugins montam no pé dela. Ver useRailRow.
1234
+ /* @__PURE__ */ jsx(RailFlyoutProvider, { value: flyout, children: /* @__PURE__ */ jsxs(
1235
+ "aside",
900
1236
  {
901
- "data-nav-active": isChildActive || void 0,
1237
+ ref: railRef,
1238
+ "data-print": "hide",
1239
+ onBlur: (event) => {
1240
+ if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
1241
+ },
902
1242
  className: cn(
903
- "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
904
- isChildActive ? "border-sidebar-accent-foreground font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
1243
+ // `relative` anchors the flyout, which hangs off the rail's right edge
1244
+ // and has to escape the nav's own scroll box to do it.
1245
+ "relative flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
1246
+ !borderless && "border-r border-sidebar-border",
1247
+ // 14rem expanded. It was 16rem, which is the width a rail needs when
1248
+ // its entries are sentences ("GlossGenius Financing", "Configure your
1249
+ // payment methods"); these are one or two words, so the extra 2rem was
1250
+ // paid for by the content column on every screen in every app. The
1251
+ // labels still truncate, and a long one now truncates 32px earlier —
1252
+ // which is the trade, taken deliberately.
1253
+ collapsed ? "w-16" : "w-56"
905
1254
  ),
906
1255
  children: [
907
- /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
908
- /* @__PURE__ */ jsx("span", { children: item.label }),
909
- /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3 w-3 transition-transform", open && "rotate-180") })
910
- ]
911
- }
912
- ) }),
913
- /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
914
- Popover.Content,
915
- {
916
- align: "start",
917
- sideOffset: 4,
918
- className: "fayz-glass-surface z-50 min-w-[200px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
919
- children: item.children?.map((child) => {
920
- const ChildIcon = getIcon2(child.icon);
921
- const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
922
- return /* @__PURE__ */ jsxs(
1256
+ /* @__PURE__ */ jsxs(
1257
+ "div",
1258
+ {
1259
+ className: cn(
1260
+ "flex h-14 items-center justify-between px-4",
1261
+ !borderless && "border-b border-sidebar-border",
1262
+ collapsed && "justify-center px-2"
1263
+ ),
1264
+ children: [
1265
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
1266
+ logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
1267
+ /* @__PURE__ */ jsx(
1268
+ "button",
1269
+ {
1270
+ onClick: onToggle,
1271
+ className: "ml-auto rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
1272
+ "aria-label": "Collapse sidebar",
1273
+ children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
1274
+ }
1275
+ )
1276
+ ] }),
1277
+ collapsed && /* @__PURE__ */ jsx(
1278
+ "button",
1279
+ {
1280
+ onClick: onToggle,
1281
+ className: "rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
1282
+ "aria-label": "Expand sidebar",
1283
+ children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
1284
+ }
1285
+ )
1286
+ ]
1287
+ }
1288
+ ),
1289
+ topContent && /* @__PURE__ */ jsx("div", { className: "px-2 pt-2", children: withCollapsed(topContent, collapsed) }),
1290
+ /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
923
1291
  "button",
924
1292
  {
925
- onClick: () => {
926
- onNavigate(child.route);
927
- setOpen(false);
928
- },
1293
+ onClick: () => setCommandPaletteOpen(true),
1294
+ "aria-keyshortcuts": "Meta+K Control+K",
929
1295
  className: cn(
930
- "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors",
931
- isActive ? "bg-muted font-medium text-foreground" : "text-popover-foreground hover:bg-muted/70"
1296
+ // Tonal surface, no outline. On a dark rail a 1px border is a hard
1297
+ // edge competing with every other hard edge; a lighter fill of the
1298
+ // same hue reads as "a field" without drawing a box around it — and
1299
+ // leaves the outline treatment free to mean focus, which it must.
1300
+ "fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
1301
+ "hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
1302
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
1303
+ collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
932
1304
  ),
1305
+ "aria-label": searchLabel,
1306
+ title: collapsed ? searchLabel : void 0,
933
1307
  children: [
934
- /* @__PURE__ */ jsx(ChildIcon, { className: "h-4 w-4 text-muted-foreground" }),
935
- /* @__PURE__ */ jsx("span", { children: child.label })
1308
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
1309
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
1310
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
1311
+ /* @__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: [
1312
+ shortcutKey(),
1313
+ "K"
1314
+ ] })
1315
+ ] })
1316
+ ]
1317
+ }
1318
+ ) }),
1319
+ /* @__PURE__ */ jsxs(
1320
+ "nav",
1321
+ {
1322
+ className: cn(
1323
+ "relative flex-1 p-2",
1324
+ // scrollbar-none: the rail still scrolls, but without the native
1325
+ // scrollbar gutter eating into the nav items.
1326
+ outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
1327
+ ),
1328
+ children: [
1329
+ outgoing && /* @__PURE__ */ jsx(
1330
+ "div",
1331
+ {
1332
+ "aria-hidden": true,
1333
+ className: cn(
1334
+ "pointer-events-none absolute inset-x-2 top-2 space-y-1",
1335
+ outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
1336
+ ),
1337
+ children: outgoing.body
1338
+ }
1339
+ ),
1340
+ /* @__PURE__ */ jsx(
1341
+ "div",
1342
+ {
1343
+ className: cn(
1344
+ "space-y-1",
1345
+ outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
1346
+ ),
1347
+ children: navBody
1348
+ },
1349
+ navKey
1350
+ )
936
1351
  ]
1352
+ }
1353
+ ),
1354
+ 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(
1355
+ NavItem,
1356
+ {
1357
+ item,
1358
+ collapsed,
1359
+ isActive: item.route === activeRoute,
1360
+ onNavigate: handleNavigate,
1361
+ flyout
937
1362
  },
938
- child.id
939
- );
940
- })
1363
+ item.id
1364
+ )) }),
1365
+ footerContent && // `empty:hidden` — slot children may all render null (e.g. the
1366
+ // SeuSiteWidget self-hides when the tenant has no project); without
1367
+ // it the padded/bordered wrapper shows as a blank block.
1368
+ /* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
1369
+ withCollapsed(orgSwitcher, collapsed),
1370
+ /* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
1371
+ "flex items-center rounded-button",
1372
+ collapsed ? "flex-col gap-1" : "gap-1 px-1"
1373
+ ), children: [
1374
+ userMenuSlot,
1375
+ !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1376
+ notificationSlot ?? /* @__PURE__ */ jsxs(
1377
+ "button",
1378
+ {
1379
+ 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",
1380
+ "aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
1381
+ children: [
1382
+ /* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
1383
+ unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
1384
+ ]
1385
+ }
1386
+ )
1387
+ ] }) }),
1388
+ flyout.panel
1389
+ ]
941
1390
  }
942
1391
  ) })
943
- ] });
944
- }
945
- function Topbar({
946
- navigation,
947
- logo,
948
- user,
949
- onMenuClick,
950
- orgSwitcher,
951
- leftContent,
952
- rightContent,
953
- currentPath = "/",
954
- onNavigate,
955
- searchPlaceholder = "Search...",
956
- onSignOut,
957
- onProfile,
958
- onSettings,
959
- onBilling,
960
- userPlan,
961
- billingLabel,
962
- userMenuExtras
963
- }) {
964
- const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
965
- const mainNav = navigation.filter((item) => item.section === "main");
966
- const secondaryNav = navigation.filter((item) => item.section === "secondary");
967
- const allNav = [...mainNav, ...secondaryNav];
968
- const handleNavigate = (route) => {
969
- onNavigate?.(route);
970
- };
971
- return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
972
- /* @__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: [
973
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
974
- onMenuClick && /* @__PURE__ */ jsx(
975
- "button",
976
- {
977
- onClick: onMenuClick,
978
- className: "inline-flex items-center justify-center rounded-md p-2 text-sidebar-foreground/70 hover:bg-sidebar/30 hover:text-sidebar-foreground md:hidden",
979
- "aria-label": "Open menu",
980
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
981
- }
982
- ),
983
- /* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
984
- ] }),
985
- /* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
986
- "button",
987
- {
988
- onClick: () => setCommandPaletteOpen(true),
989
- className: "relative flex h-8 w-full items-center rounded-md border border-sidebar-border bg-sidebar-accent px-3 pl-8 text-sm text-sidebar-muted shadow-sm transition-colors hover:border-sidebar-foreground/25 hover:bg-sidebar-foreground/10 hover:text-sidebar-foreground",
990
- children: [
991
- /* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
992
- /* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
993
- /* @__PURE__ */ jsx("kbd", { className: "ml-auto hidden rounded border border-sidebar-border bg-sidebar-accent px-1.5 py-0.5 text-[10px] font-medium text-sidebar-muted sm:inline-block", children: "\u2318K" })
994
- ]
995
- }
996
- ) }),
997
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
998
- orgSwitcher && /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 items-center md:flex", children: orgSwitcher }),
999
- leftContent,
1000
- rightContent,
1001
- /* @__PURE__ */ jsx(
1002
- TopbarUserMenu,
1003
- {
1004
- user,
1005
- userPlan,
1006
- billingLabel,
1007
- onSignOut,
1008
- onProfile,
1009
- onSettings,
1010
- onBilling,
1011
- userMenuExtras
1012
- }
1013
- )
1014
- ] })
1015
- ] }) }),
1016
- /* @__PURE__ */ jsx("div", { className: "hidden md:block bg-sidebar", children: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-full items-center px-4 md:px-6", children: /* @__PURE__ */ jsx("nav", { ref: (el) => {
1017
- if (!el) return;
1018
- const active = el.querySelector('[data-nav-active="true"]');
1019
- if (active) {
1020
- const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
1021
- el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
1022
- }
1023
- }, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-none", children: allNav.map((item) => {
1024
- if (item.children && item.children.length > 0) {
1025
- return /* @__PURE__ */ jsx(
1026
- NavDropdown,
1027
- {
1028
- item,
1029
- currentPath,
1030
- onNavigate: handleNavigate
1031
- },
1032
- item.id
1033
- );
1034
- }
1035
- const Icon = getIcon2(item.icon);
1036
- const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
1037
- return /* @__PURE__ */ jsxs(
1038
- "button",
1039
- {
1040
- "data-nav-active": isActive || void 0,
1041
- onClick: () => handleNavigate(item.route),
1042
- className: cn(
1043
- "flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
1044
- isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
1045
- ),
1046
- children: [
1047
- /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
1048
- /* @__PURE__ */ jsx("span", { children: item.label }),
1049
- item.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 text-amber-500", "aria-hidden": true }),
1050
- item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground", children: item.badge })
1051
- ]
1052
- },
1053
- item.id
1054
- );
1055
- }) }) }) })
1056
- ] });
1392
+ );
1057
1393
  }
1058
- var ModuleHeaderSlotContext = React.createContext(null);
1394
+ var ModuleHeaderSlotContext = React7.createContext(null);
1059
1395
  function useModuleHeaderSlot() {
1060
- return React.useContext(ModuleHeaderSlotContext);
1396
+ return React7.useContext(ModuleHeaderSlotContext);
1061
1397
  }
1062
- var ModuleHeaderActionsSlotContext = React.createContext(null);
1398
+ var ModuleHeaderActionsSlotContext = React7.createContext(null);
1063
1399
  function useModuleHeaderActionsSlot() {
1064
- return React.useContext(ModuleHeaderActionsSlotContext);
1400
+ return React7.useContext(ModuleHeaderActionsSlotContext);
1401
+ }
1402
+ var PageRailSlotContext = React7.createContext(null);
1403
+ function usePageRailSlot() {
1404
+ return React7.useContext(PageRailSlotContext);
1065
1405
  }
1066
1406
  function ShellPageTitle({
1067
1407
  title,
@@ -1106,11 +1446,13 @@ function SidebarLayout({
1106
1446
  const pageHeaderSlot = usePageHeaderSlot();
1107
1447
  usePageHeaderHost();
1108
1448
  const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
1109
- const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false);
1110
- const [headerSlot, setHeaderSlot] = React.useState(null);
1111
- const [actionsSlot, setActionsSlot] = React.useState(null);
1112
- return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsxs("div", { className: cn(
1113
- "flex h-screen overflow-hidden bg-background",
1449
+ const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
1450
+ const [headerSlot, setHeaderSlot] = React7.useState(null);
1451
+ const [actionsSlot, setActionsSlot] = React7.useState(null);
1452
+ const [pageRailSlot, setPageRailSlot] = React7.useState(null);
1453
+ const hasPageActions = useHasPageActions();
1454
+ 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(
1455
+ "shell-enter flex h-screen overflow-hidden bg-background",
1114
1456
  // Frame mode: the inset gap around the framed card takes the sidebar (menu)
1115
1457
  // color, so the white card reads as a panel floating on the same tone as the
1116
1458
  // left menu. The card's own md:m-2 margin reveals this behind it.
@@ -1134,7 +1476,7 @@ function SidebarLayout({
1134
1476
  topContent: sidebarTopContent,
1135
1477
  navTop: sidebarNavTop,
1136
1478
  footerContent: sidebarFooterContent,
1137
- borderless: false,
1479
+ borderless: frame,
1138
1480
  userMenuSlot,
1139
1481
  notificationSlot,
1140
1482
  unreadCount,
@@ -1146,34 +1488,42 @@ function SidebarLayout({
1146
1488
  }
1147
1489
  ) }),
1148
1490
  /* @__PURE__ */ jsxs("div", { className: cn(
1149
- "relative flex flex-1 flex-col overflow-hidden",
1150
- frame && "md:m-2 md:rounded-xl md:border md:border-border md:bg-card"
1491
+ "relative flex flex-1 overflow-hidden",
1492
+ // `content`, not `card`: this is the page's own surface, and cards have
1493
+ // to sit ON it. Painted card, the frame was the same tone as everything
1494
+ // inside it and the page read as one flat sheet.
1495
+ frame && "md:m-2 md:rounded-xl md:border md:border-border md:bg-content"
1151
1496
  ), children: [
1152
- /* @__PURE__ */ jsxs("div", { className: cn(
1153
- "flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-border bg-card px-4 py-2",
1154
- mobileHeader !== "minimal" && "hidden md:flex"
1155
- ), children: [
1156
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1157
- !hasBottomNav && /* @__PURE__ */ jsx(
1158
- "button",
1159
- {
1160
- onClick: () => setMobileMenuOpen(true),
1161
- className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
1162
- "aria-label": "Open menu",
1163
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1164
- }
1165
- ),
1166
- pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
1167
- topbarStart,
1168
- /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1497
+ /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1498
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
1499
+ /* @__PURE__ */ jsxs("div", { className: cn(
1500
+ "flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2",
1501
+ // …unless the page put its actions in it. Hiding the bar on mobile
1502
+ // used to hide the only "Novo" the screen had.
1503
+ mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex"
1504
+ ), children: [
1505
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1506
+ !hasBottomNav && /* @__PURE__ */ jsx(
1507
+ "button",
1508
+ {
1509
+ onClick: () => setMobileMenuOpen(true),
1510
+ className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
1511
+ "aria-label": "Open menu",
1512
+ children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1513
+ }
1514
+ ),
1515
+ pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
1516
+ topbarStart,
1517
+ /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1518
+ ] }),
1519
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
1520
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
1521
+ topbarEnd
1522
+ ] })
1169
1523
  ] }),
1170
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
1171
- /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
1172
- topbarEnd
1173
- ] })
1174
- ] }),
1175
- /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
1176
- /* @__PURE__ */ jsx(SaveBar, {})
1524
+ /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
1525
+ /* @__PURE__ */ jsx(SaveBar, {})
1526
+ ] })
1177
1527
  ] }),
1178
1528
  mobileMenuOpen && /* @__PURE__ */ jsx(
1179
1529
  MobileOverlay,
@@ -1198,7 +1548,7 @@ function SidebarLayout({
1198
1548
  onClose: () => setMobileMenuOpen(false)
1199
1549
  }
1200
1550
  )
1201
- ] }) }) }) });
1551
+ ] }) }) }) }) });
1202
1552
  }
1203
1553
  function TopbarLayout({
1204
1554
  navigation = [],
@@ -1223,13 +1573,17 @@ function TopbarLayout({
1223
1573
  notificationSlot,
1224
1574
  hasBottomNav = false
1225
1575
  }) {
1226
- const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false);
1576
+ const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
1577
+ const [headerSlot, setHeaderSlot] = React7.useState(null);
1578
+ const [actionsSlot, setActionsSlot] = React7.useState(null);
1579
+ const [pageRailSlot, setPageRailSlot] = React7.useState(null);
1227
1580
  const rawSlot = usePageHeaderSlot();
1228
1581
  const hasOutlet = useHasPageHeaderOutlet();
1582
+ const hasPageActions = useHasPageActions();
1229
1583
  usePageHeaderHost();
1230
1584
  const pageHeaderSlot = hasOutlet ? null : rawSlot;
1231
1585
  const showTitle = !hasOutlet && !!pageTitle;
1232
- return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsxs("div", { className: "relative flex h-screen flex-col overflow-hidden bg-background", children: [
1586
+ 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: [
1233
1587
  /* @__PURE__ */ jsx(
1234
1588
  Topbar,
1235
1589
  {
@@ -1254,9 +1608,18 @@ function TopbarLayout({
1254
1608
  onMenuClick: hasBottomNav ? void 0 : () => setMobileMenuOpen(true)
1255
1609
  }
1256
1610
  ),
1257
- /* @__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: [
1258
- (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 }) }),
1259
- /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
1611
+ /* @__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: [
1612
+ /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1613
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
1614
+ (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: [
1615
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1616
+ pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }),
1617
+ /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1618
+ ] }),
1619
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
1620
+ ] }),
1621
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
1622
+ ] })
1260
1623
  ] }) }),
1261
1624
  /* @__PURE__ */ jsx(SaveBar, {}),
1262
1625
  mobileMenuOpen && /* @__PURE__ */ jsx(
@@ -1276,7 +1639,7 @@ function TopbarLayout({
1276
1639
  onClose: () => setMobileMenuOpen(false)
1277
1640
  }
1278
1641
  )
1279
- ] }) });
1642
+ ] }) }) }) }) });
1280
1643
  }
1281
1644
  function DrawerUserRow({ user }) {
1282
1645
  const label = user.fullName || user.email;
@@ -1291,7 +1654,7 @@ function MinimalLayout({
1291
1654
  children,
1292
1655
  headerEnd
1293
1656
  }) {
1294
- return /* @__PURE__ */ jsxs("div", { className: "flex h-screen flex-col", children: [
1657
+ return /* @__PURE__ */ jsxs("div", { className: "shell-enter flex h-screen flex-col", children: [
1295
1658
  /* @__PURE__ */ jsxs("header", { className: "flex h-14 shrink-0 items-center justify-between border-b border-border bg-card px-4 md:px-6", children: [
1296
1659
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
1297
1660
  headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
@@ -1300,23 +1663,23 @@ function MinimalLayout({
1300
1663
  ] });
1301
1664
  }
1302
1665
  function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1303
- const [visible, setVisible] = React.useState(false);
1304
- React.useEffect(() => {
1666
+ const [visible, setVisible] = React7.useState(false);
1667
+ React7.useEffect(() => {
1305
1668
  const id = requestAnimationFrame(() => setVisible(true));
1306
1669
  return () => cancelAnimationFrame(id);
1307
1670
  }, []);
1308
- const close = React.useCallback(() => {
1671
+ const close = React7.useCallback(() => {
1309
1672
  setVisible(false);
1310
1673
  window.setTimeout(onClose, 250);
1311
1674
  }, [onClose]);
1312
- React.useEffect(() => {
1675
+ React7.useEffect(() => {
1313
1676
  const previous = document.body.style.overflow;
1314
1677
  document.body.style.overflow = "hidden";
1315
1678
  return () => {
1316
1679
  document.body.style.overflow = previous;
1317
1680
  };
1318
1681
  }, []);
1319
- React.useEffect(() => {
1682
+ React7.useEffect(() => {
1320
1683
  const onKey = (e) => {
1321
1684
  if (e.key === "Escape") close();
1322
1685
  };
@@ -1592,13 +1955,13 @@ function SplitWithRightRail({
1592
1955
  maxWidth = 720,
1593
1956
  resizeLabel = "Resize panel"
1594
1957
  }) {
1595
- const [width, setWidth] = React.useState(defaultWidth);
1596
- const [dragging, setDragging] = React.useState(false);
1597
- const clamp = React.useCallback(
1958
+ const [width, setWidth] = React7.useState(defaultWidth);
1959
+ const [dragging, setDragging] = React7.useState(false);
1960
+ const clamp = React7.useCallback(
1598
1961
  (value) => Math.min(maxWidth, Math.max(minWidth, value)),
1599
1962
  [minWidth, maxWidth]
1600
1963
  );
1601
- React.useEffect(() => {
1964
+ React7.useEffect(() => {
1602
1965
  if (!dragging) return;
1603
1966
  const onMove = (event) => {
1604
1967
  event.preventDefault();
@@ -1620,7 +1983,7 @@ function SplitWithRightRail({
1620
1983
  document.body.style.cursor = previousCursor;
1621
1984
  };
1622
1985
  }, [dragging, clamp]);
1623
- React.useEffect(() => {
1986
+ React7.useEffect(() => {
1624
1987
  if (dragging) return;
1625
1988
  onWidthChange?.(width);
1626
1989
  }, [dragging]);
@@ -1699,40 +2062,74 @@ function SplitWithRightRail({
1699
2062
  ] })
1700
2063
  );
1701
2064
  }
1702
- function BootSpinner({ className }) {
2065
+ function HeaderTabs({
2066
+ tabs,
2067
+ value,
2068
+ onChange,
2069
+ className,
2070
+ ...rest
2071
+ }) {
1703
2072
  return /* @__PURE__ */ jsx(
1704
- "span",
2073
+ "div",
1705
2074
  {
1706
- "aria-hidden": true,
2075
+ role: "tablist",
2076
+ "aria-label": rest["aria-label"],
1707
2077
  className: cn(
1708
- "block h-7 w-7 animate-spin rounded-full border-2 border-sidebar-foreground/20 border-t-sidebar-foreground/70",
1709
- "motion-reduce:animate-none motion-reduce:border-t-sidebar-foreground/20 motion-reduce:animate-pulse",
2078
+ "inline-flex max-w-full items-center gap-5 overflow-x-auto scrollbar-none",
1710
2079
  className
1711
- )
2080
+ ),
2081
+ children: tabs.map((tab) => {
2082
+ const active = tab.value === value;
2083
+ const Icon = tab.icon;
2084
+ return /* @__PURE__ */ jsxs(
2085
+ "button",
2086
+ {
2087
+ type: "button",
2088
+ role: "tab",
2089
+ "aria-selected": active,
2090
+ onClick: () => onChange(tab.value),
2091
+ className: cn(
2092
+ "relative flex shrink-0 items-center gap-1.5 whitespace-nowrap pb-1.5 text-sm transition-colors",
2093
+ active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
2094
+ ),
2095
+ children: [
2096
+ Icon ? /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }) : null,
2097
+ /* @__PURE__ */ jsx("span", { children: tab.label }),
2098
+ tab.count != null && /* @__PURE__ */ jsx("span", { className: cn(
2099
+ "ml-0.5 rounded-full px-1.5 text-[11px] tabular-nums",
2100
+ active ? "bg-muted text-foreground" : "bg-muted/70 text-muted-foreground"
2101
+ ), children: tab.count }),
2102
+ tab.badge,
2103
+ active && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 rounded-full bg-foreground" })
2104
+ ]
2105
+ },
2106
+ tab.value
2107
+ );
2108
+ })
1712
2109
  }
1713
2110
  );
1714
2111
  }
1715
- function AppShellSkeleton({ label, className }) {
2112
+ function PageTabs({ actions, className, ...props }) {
1716
2113
  return /* @__PURE__ */ jsxs(
1717
2114
  "div",
1718
2115
  {
1719
- role: "status",
1720
- "aria-busy": "true",
1721
- className: cn("flex h-screen items-center justify-center overflow-hidden bg-sidebar", className),
2116
+ className: cn(
2117
+ "-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",
2118
+ className
2119
+ ),
1722
2120
  children: [
1723
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
1724
- /* @__PURE__ */ jsx(BootSpinner, {})
2121
+ /* @__PURE__ */ jsx(HeaderTabs, { ...props, className: "-mb-px" }),
2122
+ actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2 pb-1.5", children: actions })
1725
2123
  ]
1726
2124
  }
1727
2125
  );
1728
2126
  }
1729
- AppShellSkeleton.displayName = "AppShellSkeleton";
1730
- var NavTransitionContext = React.createContext("slide");
2127
+ var NavTransitionContext = React7.createContext("slide");
1731
2128
  function NavTransitionProvider({ value, children }) {
1732
2129
  return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
1733
2130
  }
1734
2131
  function useNavTransition() {
1735
- return React.useContext(NavTransitionContext);
2132
+ return React7.useContext(NavTransitionContext);
1736
2133
  }
1737
2134
  function resolveTransitionClass(transition, direction) {
1738
2135
  if (transition === "none") return "";
@@ -1751,8 +2148,14 @@ function PageTransition({
1751
2148
  const animClass = resolveTransitionClass(transition, direction);
1752
2149
  return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), children }, transitionKey);
1753
2150
  }
2151
+ function PageRail({ children }) {
2152
+ const slot = usePageRailSlot();
2153
+ if (slot) return createPortal(children, slot);
2154
+ return /* @__PURE__ */ jsx(Fragment, { children });
2155
+ }
1754
2156
  function PageHeaderActions({ children }) {
1755
2157
  const slot = useModuleHeaderActionsSlot();
2158
+ useRegisterPageActions();
1756
2159
  if (slot) return createPortal(children, slot);
1757
2160
  return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
1758
2161
  }
@@ -1761,15 +2164,15 @@ function PremiumBadge({ className }) {
1761
2164
  const tip = t("upgrade.badgeTooltip");
1762
2165
  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 }) });
1763
2166
  }
1764
- var ModuleLayoutContext = React.createContext("rail");
2167
+ var ModuleLayoutContext = React7.createContext("rail");
1765
2168
  function ModuleLayoutProvider({ variant, children }) {
1766
2169
  return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
1767
2170
  }
1768
2171
  function useModuleLayout() {
1769
- return React.useContext(ModuleLayoutContext);
2172
+ return React7.useContext(ModuleLayoutContext);
1770
2173
  }
1771
- function NavItem2({ item }) {
1772
- const [expanded, setExpanded] = React.useState(true);
2174
+ function RailNavItem({ item }) {
2175
+ const [expanded, setExpanded] = React7.useState(true);
1773
2176
  const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
1774
2177
  const hasChildren = item.children && item.children.length > 0;
1775
2178
  const hasActiveChild = hasChildren && item.children.some((c) => c.active);
@@ -1811,9 +2214,9 @@ function NavItem2({ item }) {
1811
2214
  ] });
1812
2215
  }
1813
2216
  function MobileTabs({ nav }) {
1814
- const scrollRef = React.useRef(null);
1815
- const [expandedId, setExpandedId] = React.useState(null);
1816
- React.useEffect(() => {
2217
+ const scrollRef = React7.useRef(null);
2218
+ const [expandedId, setExpandedId] = React7.useState(null);
2219
+ React7.useEffect(() => {
1817
2220
  const container = scrollRef.current;
1818
2221
  if (!container) return;
1819
2222
  const activeEl = container.querySelector('[data-active="true"]');
@@ -1880,12 +2283,12 @@ function MobileTabs({ nav }) {
1880
2283
  })()
1881
2284
  ] });
1882
2285
  }
1883
- var BackStyleContext = React.createContext("link");
2286
+ var BackStyleContext = React7.createContext("link");
1884
2287
  function BackStyleProvider({ style, children }) {
1885
2288
  return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
1886
2289
  }
1887
2290
  function useBackStyle() {
1888
- return React.useContext(BackStyleContext);
2291
+ return React7.useContext(BackStyleContext);
1889
2292
  }
1890
2293
  function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
1891
2294
  const t = useTranslation();
@@ -1981,13 +2384,11 @@ function ShellSubpageHeader({
1981
2384
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
1982
2385
  Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1983
2386
  /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
1984
- ] })
2387
+ ] }),
2388
+ { back: !!onBack }
1985
2389
  );
1986
- if (!subtitle && !actions) return null;
1987
- return /* @__PURE__ */ jsxs("div", { className: "mb-5 flex flex-wrap items-start justify-between gap-3", children: [
1988
- subtitle ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle }) : /* @__PURE__ */ jsx("span", {}),
1989
- actions && /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions })
1990
- ] });
2390
+ if (!actions) return null;
2391
+ return /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) });
1991
2392
  }
1992
2393
  function ModuleHeader({ nav }) {
1993
2394
  function defaultChild(item) {
@@ -2010,12 +2411,13 @@ function ModuleHeader({ nav }) {
2010
2411
  if (hasChildren) defaultChild(item)?.onClick?.();
2011
2412
  else item.onClick?.();
2012
2413
  };
2013
- 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 }) });
2414
+ return /* @__PURE__ */ jsx(PageTabs, { tabs, value: activeItem?.id ?? nav[0]?.id ?? "", onChange });
2014
2415
  }
2015
- function ModulePage({ title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
2416
+ function ModulePage({ title: _title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
2016
2417
  const ctxVariant = useModuleLayout();
2017
2418
  const variant = navVariant ?? ctxVariant;
2018
2419
  const transitionKey = viewKey ?? "view";
2420
+ const onSubpage = useHasPageHeaderBack();
2019
2421
  if (variant === "sidebar") {
2020
2422
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2021
2423
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
@@ -2024,148 +2426,24 @@ function ModulePage({ title, subtitle, nav, children, className, headerAction, s
2024
2426
  }
2025
2427
  if (variant === "tabs") {
2026
2428
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2027
- /* @__PURE__ */ jsx(ModuleHeader, { nav }),
2429
+ !onSubpage && /* @__PURE__ */ jsx(ModuleHeader, { nav }),
2028
2430
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2029
2431
  /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
2030
2432
  ] });
2031
2433
  }
2032
- return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-6 -mt-2", className), children: [
2033
- /* @__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: [
2034
- /* @__PURE__ */ jsxs("div", { children: [
2035
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider px-2.5 mb-2", children: title }),
2036
- /* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(NavItem2, { item }, item.id)) })
2037
- ] }),
2038
- sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto", children: sidebarFooter })
2434
+ return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0", className), children: [
2435
+ /* @__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: [
2436
+ /* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(RailNavItem, { item }, item.id)) }),
2437
+ sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto pt-3", children: sidebarFooter })
2039
2438
  ] }) }),
2040
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2041
- /* @__PURE__ */ jsx(PageHeaderOutlet, {}),
2042
- showHeader && /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-3", children: [
2043
- /* @__PURE__ */ jsxs("div", { children: [
2044
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: title }),
2045
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
2046
- ] }),
2047
- headerAction
2048
- ] }),
2439
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2440
+ showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2049
2441
  /* @__PURE__ */ jsx(MobileTabs, { nav }),
2050
2442
  /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
2051
2443
  ] })
2052
2444
  ] });
2053
2445
  }
2054
- var EMPTY = [];
2055
- var PageTrailContext = React.createContext(EMPTY);
2056
- function PageTrail({
2057
- label,
2058
- href,
2059
- onNavigate,
2060
- children
2061
- }) {
2062
- const parent = React.useContext(PageTrailContext);
2063
- const trail = React.useMemo(
2064
- () => [...parent, { label, href, onNavigate }],
2065
- [parent, label, href, onNavigate]
2066
- );
2067
- return /* @__PURE__ */ jsx(PageTrailContext.Provider, { value: trail, children });
2068
- }
2069
- function usePageTrail() {
2070
- return React.useContext(PageTrailContext);
2071
- }
2072
- function usePageTrailParent(href) {
2073
- const trail = React.useContext(PageTrailContext);
2074
- return React.useMemo(() => {
2075
- const above = href ? trail.filter((c) => c.href !== href) : trail;
2076
- return above[above.length - 1];
2077
- }, [trail, href]);
2078
- }
2079
- function navigateToCrumb(crumb) {
2080
- if (crumb.onNavigate) crumb.onNavigate();
2081
- else window.location.hash = crumb.href;
2082
- }
2083
- var ContentSplitContext = React.createContext(null);
2084
- function ContentSplit({
2085
- children,
2086
- aside,
2087
- asideTitle,
2088
- asideDescription,
2089
- asideWidth = "20rem",
2090
- asideOpen,
2091
- onAsideOpenChange,
2092
- collapsible = false,
2093
- defaultCollapsed = false,
2094
- className
2095
- }) {
2096
- const [internalOpen, setInternalOpen] = React.useState(false);
2097
- const open = asideOpen ?? internalOpen;
2098
- const setOpen = onAsideOpenChange ?? setInternalOpen;
2099
- const [collapsed, setCollapsed] = React.useState(collapsible && defaultCollapsed);
2100
- return /* @__PURE__ */ jsxs(ContentSplitContext.Provider, { value: { open, setOpen }, children: [
2101
- /* @__PURE__ */ jsxs(
2102
- "div",
2103
- {
2104
- className: cn("lg:grid lg:items-start lg:gap-6", className),
2105
- style: { gridTemplateColumns: `minmax(0, 1fr) ${collapsed ? "auto" : asideWidth}` },
2106
- children: [
2107
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children }),
2108
- /* @__PURE__ */ jsx("aside", { className: "hidden lg:block lg:sticky lg:top-4", children: collapsed ? /* @__PURE__ */ jsx(
2109
- Button,
2110
- {
2111
- variant: "outline",
2112
- size: "sm",
2113
- onClick: () => setCollapsed(false),
2114
- className: "h-9 w-9 p-0",
2115
- "aria-label": typeof asideTitle === "string" ? asideTitle : void 0,
2116
- title: typeof asideTitle === "string" ? asideTitle : void 0,
2117
- children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4" })
2118
- }
2119
- ) : /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card", children: [
2120
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 border-b border-border px-4 py-3", children: [
2121
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2122
- /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: asideTitle }),
2123
- asideDescription && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: asideDescription })
2124
- ] }),
2125
- collapsible && /* @__PURE__ */ jsx(
2126
- "button",
2127
- {
2128
- type: "button",
2129
- onClick: () => setCollapsed(true),
2130
- className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground",
2131
- "aria-label": "collapse",
2132
- children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
2133
- }
2134
- )
2135
- ] }),
2136
- /* @__PURE__ */ jsx("div", { className: "p-4", children: aside })
2137
- ] }) })
2138
- ]
2139
- }
2140
- ),
2141
- /* @__PURE__ */ jsx("div", { className: "lg:hidden", children: /* @__PURE__ */ jsx(Sheet, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(SheetContent, { width: "max-w-sm", children: [
2142
- /* @__PURE__ */ jsxs(SheetHeader, { children: [
2143
- /* @__PURE__ */ jsx(SheetTitle, { children: asideTitle }),
2144
- asideDescription && /* @__PURE__ */ jsx(SheetDescription, { children: asideDescription })
2145
- ] }),
2146
- /* @__PURE__ */ jsx(SheetBody, { children: aside })
2147
- ] }) }) })
2148
- ] });
2149
- }
2150
- function ContentSplitTrigger({ label, iconOnly, className }) {
2151
- const ctx = React.useContext(ContentSplitContext);
2152
- if (!ctx) return null;
2153
- return /* @__PURE__ */ jsxs(
2154
- Button,
2155
- {
2156
- variant: "outline",
2157
- size: "sm",
2158
- onClick: () => ctx.setOpen(true),
2159
- className: cn("lg:hidden", className),
2160
- "aria-label": typeof label === "string" ? label : void 0,
2161
- children: [
2162
- /* @__PURE__ */ jsx(PanelRight, { className: cn("h-4 w-4", !iconOnly && "mr-1.5") }),
2163
- !iconOnly && label
2164
- ]
2165
- }
2166
- );
2167
- }
2168
2446
 
2169
- export { AppShell, AppShellSkeleton, BackStyleProvider, ContentSplit, ContentSplitTrigger, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeaderActions, PageTrail, PageTransition, Sidebar, SubpageHeader, Topbar, navigateToCrumb, useBackStyle, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, usePageTrail, usePageTrailParent };
2170
- //# sourceMappingURL=chunk-QSHA2Q27.js.map
2171
- //# sourceMappingURL=chunk-QSHA2Q27.js.map
2447
+ 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 };
2448
+ //# sourceMappingURL=chunk-MVH64JFG.js.map
2449
+ //# sourceMappingURL=chunk-MVH64JFG.js.map