@fayz-ai/ui 0.22.0 → 0.24.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 (75) hide show
  1. package/dist/analytics/AnalyticsCard.d.ts.map +1 -1
  2. package/dist/analytics/AnalyticsControls.d.ts.map +1 -1
  3. package/dist/analytics/DateRangeControl.d.ts +14 -0
  4. package/dist/analytics/DateRangeControl.d.ts.map +1 -0
  5. package/dist/analytics/StatementTable.d.ts +22 -0
  6. package/dist/analytics/StatementTable.d.ts.map +1 -0
  7. package/dist/analytics/context.d.ts +8 -0
  8. package/dist/analytics/context.d.ts.map +1 -1
  9. package/dist/analytics/derive.d.ts +13 -0
  10. package/dist/analytics/derive.d.ts.map +1 -1
  11. package/dist/analytics/index.d.ts +7 -1
  12. package/dist/analytics/index.d.ts.map +1 -1
  13. package/dist/analytics/statement.d.ts +58 -0
  14. package/dist/analytics/statement.d.ts.map +1 -0
  15. package/dist/{chunk-E44WOPCR.js → chunk-HO2P2XB4.js} +428 -58
  16. package/dist/chunk-HO2P2XB4.js.map +1 -0
  17. package/dist/{chunk-7G3O54CC.js → chunk-L4TXDKMN.js} +5 -5
  18. package/dist/chunk-L4TXDKMN.js.map +1 -0
  19. package/dist/{chunk-ZSOT7KTE.js → chunk-NXFVYZY7.js} +467 -405
  20. package/dist/chunk-NXFVYZY7.js.map +1 -0
  21. package/dist/{chunk-DHNUZF2X.js → chunk-OWN3GFDG.js} +4 -3
  22. package/dist/chunk-OWN3GFDG.js.map +1 -0
  23. package/dist/{chunk-PQUS22JE.js → chunk-RRNMNFLF.js} +483 -794
  24. package/dist/chunk-RRNMNFLF.js.map +1 -0
  25. package/dist/chunk-UP7WVKSU.js +631 -0
  26. package/dist/chunk-UP7WVKSU.js.map +1 -0
  27. package/dist/{chunk-SUSW4LSW.js → chunk-XKFYX3DG.js} +5 -5
  28. package/dist/{chunk-SUSW4LSW.js.map → chunk-XKFYX3DG.js.map} +1 -1
  29. package/dist/dashboard/index.js +4 -4
  30. package/dist/icons.js +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +672 -87
  34. package/dist/index.js.map +1 -1
  35. package/dist/layout/AppShell.d.ts +7 -1
  36. package/dist/layout/AppShell.d.ts.map +1 -1
  37. package/dist/layout/ShellSearch.d.ts +53 -0
  38. package/dist/layout/ShellSearch.d.ts.map +1 -0
  39. package/dist/layout/Sidebar.d.ts +10 -6
  40. package/dist/layout/Sidebar.d.ts.map +1 -1
  41. package/dist/layout/Takeover.d.ts.map +1 -1
  42. package/dist/layout/index.d.ts +1 -0
  43. package/dist/layout/index.d.ts.map +1 -1
  44. package/dist/layout/index.js +4 -4
  45. package/dist/primitives/data-table.d.ts.map +1 -1
  46. package/dist/primitives/date-range-chip.d.ts +32 -0
  47. package/dist/primitives/date-range-chip.d.ts.map +1 -0
  48. package/dist/primitives/header-tabs.d.ts.map +1 -1
  49. package/dist/primitives/index.d.ts +2 -1
  50. package/dist/primitives/index.d.ts.map +1 -1
  51. package/dist/primitives/index.js +5 -5
  52. package/dist/primitives/list-chrome.d.ts +22 -1
  53. package/dist/primitives/list-chrome.d.ts.map +1 -1
  54. package/dist/primitives/list-view.d.ts +56 -1
  55. package/dist/primitives/list-view.d.ts.map +1 -1
  56. package/dist/primitives/modal.d.ts +2 -0
  57. package/dist/primitives/modal.d.ts.map +1 -1
  58. package/dist/primitives/multi-select.d.ts +18 -1
  59. package/dist/primitives/multi-select.d.ts.map +1 -1
  60. package/dist/primitives/page-actions.d.ts +14 -0
  61. package/dist/primitives/page-actions.d.ts.map +1 -1
  62. package/dist/primitives/page-toolbar.d.ts +16 -1
  63. package/dist/primitives/page-toolbar.d.ts.map +1 -1
  64. package/dist/primitives/segmented-control.d.ts.map +1 -1
  65. package/dist/stores/layout.store.d.ts +20 -1
  66. package/dist/stores/layout.store.d.ts.map +1 -1
  67. package/dist/styles.css +58 -0
  68. package/package.json +2 -2
  69. package/dist/chunk-7G3O54CC.js.map +0 -1
  70. package/dist/chunk-DHNUZF2X.js.map +0 -1
  71. package/dist/chunk-E44WOPCR.js.map +0 -1
  72. package/dist/chunk-HKGTF2YA.js +0 -117
  73. package/dist/chunk-HKGTF2YA.js.map +0 -1
  74. package/dist/chunk-PQUS22JE.js.map +0 -1
  75. package/dist/chunk-ZSOT7KTE.js.map +0 -1
@@ -1,134 +1,15 @@
1
- import { useHasPageHeaderHost, usePageHeader, PageTitlePortal, useLayoutStore, useHasPageHeaderBack, useRegisterPageActions, usePageHeaderClaimed, usePageTitleSlotHost, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-HKGTF2YA.js';
2
1
  import { cn } from './chunk-OKZ6WQB3.js';
3
- import { ICON_MAP } from './chunk-DHNUZF2X.js';
4
- import * as React2 from 'react';
5
- import * as DialogPrimitive from '@radix-ui/react-dialog';
6
- import { X, Loader2, ArrowLeft, Search, Bell, PanelLeft, PanelLeftClose, Menu, Crown, ChevronDown, ChevronRight, ChevronLeft, ExternalLink, CreditCard, Sparkles, Home, Plus, UserCog, Settings, LogOut } from 'lucide-react';
7
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { ICON_MAP } from './chunk-OWN3GFDG.js';
3
+ import * as React from 'react';
4
+ import { Loader2, ArrowLeft, Search, CornerDownLeft, X, Bell, PanelLeft, PanelLeftClose, Menu, Crown, ExternalLink, CreditCard, Sparkles, ChevronDown, Home, ChevronRight, Plus, UserCog, Settings, LogOut } from 'lucide-react';
8
5
  import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
9
- import * as Popover from '@radix-ui/react-popover';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
7
  import { createPortal } from 'react-dom';
8
+ import { create } from 'zustand';
9
+ import * as Popover from '@radix-ui/react-popover';
11
10
 
12
- var STYLE_ID = "saas-sheet-styles";
13
- if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
14
- const style = document.createElement("style");
15
- style.id = STYLE_ID;
16
- style.textContent = `
17
- @keyframes saas-sheet-fade-in { from { opacity: 0 } to { opacity: 1 } }
18
- @keyframes saas-sheet-fade-out { from { opacity: 1 } to { opacity: 0 } }
19
- @keyframes saas-sheet-slide-in { from { transform: translateX(100%) } to { transform: translateX(0) } }
20
- @keyframes saas-sheet-slide-out { from { transform: translateX(0) } to { transform: translateX(100%) } }
21
- .saas-sheet-overlay[data-state="open"] { animation: saas-sheet-fade-in 200ms ease-out }
22
- .saas-sheet-overlay[data-state="closed"] { animation: saas-sheet-fade-out 150ms ease-in }
23
- .saas-sheet-content[data-state="open"] { animation: saas-sheet-slide-in 280ms cubic-bezier(0.16, 1, 0.3, 1) }
24
- .saas-sheet-content[data-state="closed"] { animation: saas-sheet-slide-out 200ms ease-in }
25
- `;
26
- document.head.appendChild(style);
27
- }
28
- var Sheet = DialogPrimitive.Root;
29
- var SheetTrigger = DialogPrimitive.Trigger;
30
- var SheetClose = DialogPrimitive.Close;
31
- var SheetPortal = DialogPrimitive.Portal;
32
- var SheetOverlay = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
33
- DialogPrimitive.Overlay,
34
- {
35
- ref,
36
- className: cn(
37
- "saas-sheet-overlay fixed inset-0 z-[100] bg-black/40 backdrop-blur-[2px]",
38
- className
39
- ),
40
- ...props
41
- }
42
- ));
43
- SheetOverlay.displayName = "SheetOverlay";
44
- var SheetContent = React2.forwardRef(({ className, children, width, hideClose, overlay = "blur", onPointerDownOutside, onInteractOutside, ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
45
- /* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
46
- /* @__PURE__ */ jsxs(
47
- DialogPrimitive.Content,
48
- {
49
- ref,
50
- onPointerDownOutside: (e) => {
51
- const target = e.target;
52
- if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
53
- e.preventDefault();
54
- }
55
- onPointerDownOutside?.(e);
56
- },
57
- onInteractOutside: (e) => {
58
- const target = e.target;
59
- if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
60
- e.preventDefault();
61
- }
62
- onInteractOutside?.(e);
63
- },
64
- className: cn(
65
- "fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-[100] flex flex-col border-l bg-card shadow-2xl outline-none",
66
- "w-full",
67
- width ?? "max-w-md",
68
- className
69
- ),
70
- ...props,
71
- children: [
72
- children,
73
- !hideClose && /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 z-10 flex h-7 w-7 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring", children: [
74
- /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
75
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
76
- ] })
77
- ]
78
- }
79
- )
80
- ] }));
81
- SheetContent.displayName = "SheetContent";
82
- function SheetHeader({ className, ...props }) {
83
- return /* @__PURE__ */ jsx(
84
- "div",
85
- {
86
- className: cn("flex flex-col gap-1 px-5 py-4 border-b shrink-0", className),
87
- ...props
88
- }
89
- );
90
- }
91
- SheetHeader.displayName = "SheetHeader";
92
- function SheetFooter({ className, ...props }) {
93
- return /* @__PURE__ */ jsx(
94
- "div",
95
- {
96
- className: cn("flex items-center gap-2 px-5 py-3 border-t shrink-0", className),
97
- ...props
98
- }
99
- );
100
- }
101
- SheetFooter.displayName = "SheetFooter";
102
- var SheetTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
103
- DialogPrimitive.Title,
104
- {
105
- ref,
106
- className: cn("text-sm font-semibold leading-none tracking-tight", className),
107
- ...props
108
- }
109
- ));
110
- SheetTitle.displayName = "SheetTitle";
111
- var SheetDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
112
- DialogPrimitive.Description,
113
- {
114
- ref,
115
- className: cn("text-xs text-muted-foreground", className),
116
- ...props
117
- }
118
- ));
119
- SheetDescription.displayName = "SheetDescription";
120
- function SheetBody({ className, ...props }) {
121
- return /* @__PURE__ */ jsx(
122
- "div",
123
- {
124
- className: cn("flex-1 overflow-y-auto px-5 py-5", className),
125
- ...props
126
- }
127
- );
128
- }
129
- SheetBody.displayName = "SheetBody";
130
- var SaveBarApiContext = React2.createContext(null);
131
- var SaveBarStateContext = React2.createContext({ visible: false, saving: false });
11
+ var SaveBarApiContext = React.createContext(null);
12
+ var SaveBarStateContext = React.createContext({ visible: false, saving: false });
132
13
  var _saveBarVisible = false;
133
14
  var _saveBarSubs = /* @__PURE__ */ new Set();
134
15
  function setSaveBarVisibleSignal(v) {
@@ -137,7 +18,7 @@ function setSaveBarVisibleSignal(v) {
137
18
  _saveBarSubs.forEach((fn) => fn());
138
19
  }
139
20
  function useSaveBarVisible() {
140
- return React2.useSyncExternalStore(
21
+ return React.useSyncExternalStore(
141
22
  (cb) => {
142
23
  _saveBarSubs.add(cb);
143
24
  return () => {
@@ -149,15 +30,15 @@ function useSaveBarVisible() {
149
30
  );
150
31
  }
151
32
  function SaveBarProvider({ children }) {
152
- const [state, setState] = React2.useState({ visible: false, saving: false });
153
- const handlers = React2.useRef({});
154
- const api = React2.useMemo(() => ({ setState, handlers }), []);
155
- const backStack = React2.useRef([]);
156
- const backApi = React2.useMemo(() => ({ stack: backStack }), []);
157
- React2.useEffect(() => {
33
+ const [state, setState] = React.useState({ visible: false, saving: false });
34
+ const handlers = React.useRef({});
35
+ const api = React.useMemo(() => ({ setState, handlers }), []);
36
+ const backStack = React.useRef([]);
37
+ const backApi = React.useMemo(() => ({ stack: backStack }), []);
38
+ React.useEffect(() => {
158
39
  setSaveBarVisibleSignal(state.visible);
159
40
  }, [state.visible]);
160
- React2.useEffect(() => {
41
+ React.useEffect(() => {
161
42
  function onKey(e) {
162
43
  if (state.visible && !state.saving) {
163
44
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
@@ -185,7 +66,7 @@ function SaveBarProvider({ children }) {
185
66
  }, [state.visible, state.saving]);
186
67
  return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
187
68
  }
188
- var BackNavApiContext = React2.createContext(null);
69
+ var BackNavApiContext = React.createContext(null);
189
70
  function isEditableTarget(target) {
190
71
  const el = target;
191
72
  if (!el || !el.tagName) return false;
@@ -193,12 +74,12 @@ function isEditableTarget(target) {
193
74
  return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
194
75
  }
195
76
  function useBackHandler(onBack) {
196
- const api = React2.useContext(BackNavApiContext);
197
- const ref = React2.useRef(onBack);
198
- React2.useEffect(() => {
77
+ const api = React.useContext(BackNavApiContext);
78
+ const ref = React.useRef(onBack);
79
+ React.useEffect(() => {
199
80
  ref.current = onBack;
200
81
  });
201
- React2.useEffect(() => {
82
+ React.useEffect(() => {
202
83
  if (!api) return;
203
84
  const stack = api.stack.current;
204
85
  stack.push(ref);
@@ -209,23 +90,23 @@ function useBackHandler(onBack) {
209
90
  }, [api]);
210
91
  }
211
92
  function useSaveBar({ dirty, alwaysVisible, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
212
- const api = React2.useContext(SaveBarApiContext);
213
- React2.useEffect(() => {
93
+ const api = React.useContext(SaveBarApiContext);
94
+ React.useEffect(() => {
214
95
  if (api) api.handlers.current = { onSave, onDiscard };
215
96
  });
216
- React2.useEffect(() => {
97
+ React.useEffect(() => {
217
98
  if (!api) return;
218
99
  api.setState({ visible: dirty || !!alwaysVisible, dirty, saving, message, saveLabel, discardLabel });
219
100
  return () => api.setState((s) => ({ ...s, visible: false }));
220
101
  }, [api, dirty, alwaysVisible, saving, message, saveLabel, discardLabel]);
221
102
  }
222
103
  function SaveBar({ className }) {
223
- const state = React2.useContext(SaveBarStateContext);
224
- const api = React2.useContext(SaveBarApiContext);
104
+ const state = React.useContext(SaveBarStateContext);
105
+ const api = React.useContext(SaveBarApiContext);
225
106
  const t = useTranslation();
226
- const [rendered, setRendered] = React2.useState(state.visible);
227
- const [closing, setClosing] = React2.useState(false);
228
- React2.useEffect(() => {
107
+ const [rendered, setRendered] = React.useState(state.visible);
108
+ const [closing, setClosing] = React.useState(false);
109
+ React.useEffect(() => {
229
110
  if (state.visible) {
230
111
  setRendered(true);
231
112
  setClosing(false);
@@ -290,6 +171,98 @@ function SaveBar({ className }) {
290
171
  }
291
172
  );
292
173
  }
174
+ var usePageHeaderStore = create((set, get) => ({
175
+ shellSlot: null,
176
+ setShellSlot: (element) => set({ shellSlot: element }),
177
+ outletSlot: null,
178
+ setOutletSlot: (element) => set({ outletSlot: element }),
179
+ claimed: false,
180
+ owner: null,
181
+ hasBack: false,
182
+ hosts: 0,
183
+ outlets: 0,
184
+ actions: 0,
185
+ claim: (owner, hasBack = false) => set({ owner, claimed: true, hasBack }),
186
+ release: (owner) => {
187
+ if (get().owner === owner) set({ owner: null, claimed: false, hasBack: false });
188
+ },
189
+ addHost: () => set((s) => ({ hosts: s.hosts + 1 })),
190
+ removeHost: () => set((s) => ({ hosts: Math.max(0, s.hosts - 1) })),
191
+ addOutlet: () => set((s) => ({ outlets: s.outlets + 1 })),
192
+ removeOutlet: () => set((s) => ({ outlets: Math.max(0, s.outlets - 1) })),
193
+ addActions: () => set((s) => ({ actions: s.actions + 1 })),
194
+ removeActions: () => set((s) => ({ actions: Math.max(0, s.actions - 1) }))
195
+ }));
196
+ function useRegisterPageActions() {
197
+ const add = usePageHeaderStore((s) => s.addActions);
198
+ const remove = usePageHeaderStore((s) => s.removeActions);
199
+ React.useEffect(() => {
200
+ add();
201
+ return remove;
202
+ }, [add, remove]);
203
+ }
204
+ function useHasPageActions() {
205
+ return usePageHeaderStore((s) => s.actions > 0);
206
+ }
207
+ function usePageHeaderClaimed() {
208
+ return usePageHeaderStore((s) => s.claimed);
209
+ }
210
+ function usePageTitleSlot() {
211
+ return usePageHeaderStore((s) => s.outletSlot ?? s.shellSlot);
212
+ }
213
+ function usePageTitleSlotHost() {
214
+ return usePageHeaderStore((s) => s.setShellSlot);
215
+ }
216
+ function usePageHeaderHost() {
217
+ const addHost = usePageHeaderStore((s) => s.addHost);
218
+ const removeHost = usePageHeaderStore((s) => s.removeHost);
219
+ React.useEffect(() => {
220
+ addHost();
221
+ return removeHost;
222
+ }, [addHost, removeHost]);
223
+ }
224
+ function useHasPageHeaderHost() {
225
+ return usePageHeaderStore((s) => s.hosts > 0 || s.outlets > 0);
226
+ }
227
+ function useHasPageHeaderOutlet() {
228
+ return usePageHeaderStore((s) => s.outlets > 0);
229
+ }
230
+ function PageHeaderOutlet({ className }) {
231
+ const setOutletSlot = usePageHeaderStore((s) => s.setOutletSlot);
232
+ const claimed = usePageHeaderStore((s) => s.claimed);
233
+ const addOutlet = usePageHeaderStore((s) => s.addOutlet);
234
+ const removeOutlet = usePageHeaderStore((s) => s.removeOutlet);
235
+ React.useEffect(() => {
236
+ addOutlet();
237
+ return removeOutlet;
238
+ }, [addOutlet, removeOutlet]);
239
+ return React.createElement("div", {
240
+ ref: setOutletSlot,
241
+ className: `${className ?? "mb-4"}${claimed ? "" : " hidden"}`
242
+ });
243
+ }
244
+ function usePageHeader(active, options) {
245
+ const id = React.useId();
246
+ const claim = usePageHeaderStore((s) => s.claim);
247
+ const release = usePageHeaderStore((s) => s.release);
248
+ const back = options?.back ?? false;
249
+ React.useEffect(() => {
250
+ if (!active) {
251
+ release(id);
252
+ return;
253
+ }
254
+ claim(id, back);
255
+ return () => release(id);
256
+ }, [id, active, back, claim, release]);
257
+ }
258
+ function useHasPageHeaderBack() {
259
+ return usePageHeaderStore((s) => s.hasBack);
260
+ }
261
+ function PageTitlePortal({ children }) {
262
+ const slot = usePageTitleSlot();
263
+ if (!slot) return React.createElement(React.Fragment, null, children);
264
+ return createPortal(children, slot);
265
+ }
293
266
  function useCrossModuleBack(onBack, parentLabel) {
294
267
  const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
295
268
  const referrer = useNavReferrer(currentPath);
@@ -344,7 +317,7 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
344
317
  const hasHost = useHasPageHeaderHost();
345
318
  const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
346
319
  useBackHandler(effectiveOnBack);
347
- const node = React2.useMemo(() => /* @__PURE__ */ jsx(
320
+ const node = React.useMemo(() => /* @__PURE__ */ jsx(
348
321
  PageHeaderCrumb,
349
322
  {
350
323
  title,
@@ -356,17 +329,154 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
356
329
  if (hasHost) return /* @__PURE__ */ jsx(PageTitlePortal, { children: node });
357
330
  return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
358
331
  }
332
+ var useLayoutStore = create((set) => ({
333
+ sidebarOpen: true,
334
+ sidebarCollapsed: false,
335
+ mobileDrawerOpen: false,
336
+ drawerKeepOpenOnce: false,
337
+ commandPaletteOpen: false,
338
+ commandPaletteQuery: "",
339
+ toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
340
+ setSidebarCollapsed: (collapsed) => set({ sidebarCollapsed: collapsed }),
341
+ toggleMobileDrawer: () => set((state) => ({ mobileDrawerOpen: !state.mobileDrawerOpen })),
342
+ setMobileDrawerOpen: (open) => set({ mobileDrawerOpen: open }),
343
+ keepDrawerOpenOnce: () => set({ drawerKeepOpenOnce: true }),
344
+ // Opening search closes the mobile drawer. The palette is a full-screen
345
+ // surface on a phone, and leaving the drawer mounted behind it means the user
346
+ // dismisses the palette onto a menu they had already navigated away from.
347
+ // Handled here so every entry point behaves the same — the rail's search
348
+ // field, the topbar button and the ⌘K shortcut.
349
+ setCommandPaletteOpen: (open, query) => set(open ? { commandPaletteOpen: true, mobileDrawerOpen: false, commandPaletteQuery: query ?? "" } : { commandPaletteOpen: false, commandPaletteQuery: "" })
350
+ }));
351
+ function shortcutKey() {
352
+ if (typeof navigator === "undefined") return "\u2318";
353
+ return /Mac|iPhone|iPad/u.test(navigator.platform || navigator.userAgent) ? "\u2318" : "Ctrl ";
354
+ }
355
+ function ShellSearch({
356
+ label = "Search",
357
+ tone = "bar",
358
+ collapsed = false,
359
+ value,
360
+ onValueChange,
361
+ searchAllLabel = "Search everywhere",
362
+ className
363
+ }) {
364
+ const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
365
+ const rail = tone === "rail";
366
+ const contextual = !!onValueChange && !collapsed;
367
+ const query = value ?? "";
368
+ const inputRef = React.useRef(null);
369
+ const escalate = () => setCommandPaletteOpen(true, query);
370
+ const surface = rail ? "bg-sidebar-accent/40 text-sidebar-muted hover:bg-sidebar-accent/70 hover:text-sidebar-foreground" : "bg-accent/40 text-muted-foreground hover:bg-accent/70 hover:text-foreground";
371
+ const ring = rail ? "focus-within:bg-sidebar-accent/70 focus-within:ring-1 focus-within:ring-sidebar-accent-foreground/25" : "focus-within:bg-accent/70 focus-within:ring-1 focus-within:ring-ring/40";
372
+ if (!contextual) {
373
+ return /* @__PURE__ */ jsxs(
374
+ "button",
375
+ {
376
+ onClick: escalate,
377
+ "aria-keyshortcuts": "Meta+K Control+K",
378
+ className: cn(
379
+ // Tonal surface, no outline. A 1px border here is a hard edge
380
+ // competing with every other hard edge; a lighter fill of the same
381
+ // hue reads as "a field" without drawing a box — and leaves the
382
+ // outline treatment free to mean focus, which it must.
383
+ "fz-tap group flex items-center rounded-lg transition-colors",
384
+ collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5",
385
+ surface,
386
+ "focus-visible:outline-none focus-visible:ring-2",
387
+ rail ? "focus-visible:ring-sidebar-accent-foreground/50" : "focus-visible:ring-ring/50",
388
+ className
389
+ ),
390
+ "aria-label": label,
391
+ title: collapsed ? label : void 0,
392
+ children: [
393
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
394
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
395
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: label }),
396
+ /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] font-medium tabular-nums opacity-70 transition-opacity group-hover:opacity-100", children: [
397
+ shortcutKey(),
398
+ "K"
399
+ ] })
400
+ ] })
401
+ ]
402
+ }
403
+ );
404
+ }
405
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1", className), children: [
406
+ /* @__PURE__ */ jsxs(
407
+ "div",
408
+ {
409
+ className: cn(
410
+ "group flex h-9 w-full items-center gap-2.5 rounded-lg px-2.5 transition-colors",
411
+ surface,
412
+ "focus-within:outline-none",
413
+ ring
414
+ ),
415
+ children: [
416
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
417
+ /* @__PURE__ */ jsx(
418
+ "input",
419
+ {
420
+ ref: inputRef,
421
+ type: "search",
422
+ value: query,
423
+ onChange: (e) => onValueChange(e.target.value),
424
+ onKeyDown: (e) => {
425
+ if (e.key === "Enter") {
426
+ e.preventDefault();
427
+ escalate();
428
+ }
429
+ if (e.key === "Escape" && query) {
430
+ e.preventDefault();
431
+ onValueChange("");
432
+ }
433
+ },
434
+ placeholder: label,
435
+ "aria-label": label,
436
+ "aria-keyshortcuts": "Meta+K Control+K",
437
+ className: cn(
438
+ "min-w-0 flex-1 border-none bg-transparent p-0 text-[13px] outline-none",
439
+ // `search` inputs paint their own clear button in WebKit, on top of
440
+ // ours and in the browser's grey.
441
+ "[&::-webkit-search-cancel-button]:appearance-none",
442
+ rail ? "text-sidebar-foreground placeholder:text-sidebar-muted" : "text-foreground placeholder:text-muted-foreground"
443
+ )
444
+ }
445
+ ),
446
+ !query && /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] font-medium tabular-nums opacity-70 transition-opacity group-hover:opacity-100", children: [
447
+ shortcutKey(),
448
+ "K"
449
+ ] })
450
+ ]
451
+ }
452
+ ),
453
+ query && /* @__PURE__ */ jsxs(
454
+ "button",
455
+ {
456
+ onClick: escalate,
457
+ className: cn(
458
+ "fz-tap flex w-full items-center gap-2 rounded-lg py-1.5 pl-9 pr-2.5 text-left text-[12px] transition-colors",
459
+ rail ? "text-sidebar-muted hover:bg-sidebar-accent/50 hover:text-sidebar-foreground" : "text-muted-foreground hover:bg-accent/60 hover:text-foreground"
460
+ ),
461
+ children: [
462
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: searchAllLabel }),
463
+ /* @__PURE__ */ jsx(CornerDownLeft, { className: "h-3.5 w-3.5 shrink-0 opacity-60", "aria-hidden": true })
464
+ ]
465
+ }
466
+ )
467
+ ] });
468
+ }
359
469
  var GUTTER_PX = 8;
360
470
  var CLOSE_DELAY_MS = 140;
361
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
471
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
362
472
  function iconOf(name) {
363
473
  if (!name) return null;
364
474
  return ICON_MAP[name] ?? null;
365
475
  }
366
- var RailFlyoutContext = React2.createContext(null);
476
+ var RailFlyoutContext = React.createContext(null);
367
477
  var RailFlyoutProvider = RailFlyoutContext.Provider;
368
478
  function useRailRow(content) {
369
- const flyout = React2.useContext(RailFlyoutContext);
479
+ const flyout = React.useContext(RailFlyoutContext);
370
480
  if (!flyout || !content) return {};
371
481
  return flyout.rowProps(content);
372
482
  }
@@ -376,19 +486,19 @@ function useRailFlyout({
376
486
  activeRoute,
377
487
  onNavigate
378
488
  }) {
379
- const [state, setState] = React2.useState(null);
380
- const [pos, setPos] = React2.useState({ top: 0, arrow: 20 });
381
- const [animate, setAnimate] = React2.useState(false);
382
- const panelRef = React2.useRef(null);
383
- const closeTimer = React2.useRef(null);
384
- const lastContent = React2.useRef(null);
385
- const cancelClose = React2.useCallback(() => {
489
+ const [state, setState] = React.useState(null);
490
+ const [pos, setPos] = React.useState({ top: 0, arrow: 20 });
491
+ const [animate, setAnimate] = React.useState(false);
492
+ const panelRef = React.useRef(null);
493
+ const closeTimer = React.useRef(null);
494
+ const lastContent = React.useRef(null);
495
+ const cancelClose = React.useCallback(() => {
386
496
  if (closeTimer.current) {
387
497
  clearTimeout(closeTimer.current);
388
498
  closeTimer.current = null;
389
499
  }
390
500
  }, []);
391
- const show = React2.useCallback(
501
+ const show = React.useCallback(
392
502
  (anchor, content2) => {
393
503
  if (!enabled) return;
394
504
  cancelClose();
@@ -396,19 +506,19 @@ function useRailFlyout({
396
506
  },
397
507
  [enabled, cancelClose]
398
508
  );
399
- const hide = React2.useCallback(() => {
509
+ const hide = React.useCallback(() => {
400
510
  cancelClose();
401
511
  closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
402
512
  }, [cancelClose]);
403
- const dismiss = React2.useCallback(() => {
513
+ const dismiss = React.useCallback(() => {
404
514
  cancelClose();
405
515
  setState(null);
406
516
  }, [cancelClose]);
407
- React2.useEffect(() => () => cancelClose(), [cancelClose]);
408
- React2.useEffect(() => {
517
+ React.useEffect(() => () => cancelClose(), [cancelClose]);
518
+ React.useEffect(() => {
409
519
  if (!enabled) dismiss();
410
520
  }, [enabled, dismiss]);
411
- const reposition = React2.useCallback(() => {
521
+ const reposition = React.useCallback(() => {
412
522
  const rail = railRef.current;
413
523
  const anchor = state?.anchor;
414
524
  if (!rail || !anchor) return;
@@ -423,7 +533,7 @@ function useRailFlyout({
423
533
  setPos({ top, arrow });
424
534
  }, [railRef, state]);
425
535
  useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
426
- React2.useEffect(() => {
536
+ React.useEffect(() => {
427
537
  if (!state) return;
428
538
  const rail = railRef.current;
429
539
  const onScroll = () => reposition();
@@ -435,7 +545,7 @@ function useRailFlyout({
435
545
  };
436
546
  }, [state, railRef, reposition]);
437
547
  const visible = !!state;
438
- React2.useEffect(() => {
548
+ React.useEffect(() => {
439
549
  if (!visible) {
440
550
  setAnimate(false);
441
551
  return;
@@ -443,7 +553,7 @@ function useRailFlyout({
443
553
  const id = requestAnimationFrame(() => setAnimate(true));
444
554
  return () => cancelAnimationFrame(id);
445
555
  }, [visible]);
446
- React2.useEffect(() => {
556
+ React.useEffect(() => {
447
557
  if (!visible) return;
448
558
  const onKey = (event) => {
449
559
  if (event.key === "Escape") dismiss();
@@ -451,7 +561,7 @@ function useRailFlyout({
451
561
  window.addEventListener("keydown", onKey);
452
562
  return () => window.removeEventListener("keydown", onKey);
453
563
  }, [visible, dismiss]);
454
- const rowProps = React2.useCallback(
564
+ const rowProps = React.useCallback(
455
565
  (content2) => ({
456
566
  onMouseEnter: (event) => show(event.currentTarget, content2),
457
567
  onFocus: (event) => show(event.currentTarget, content2),
@@ -568,14 +678,10 @@ function FlyoutBody({
568
678
  ] });
569
679
  }
570
680
  var RAIL_PUSH_MS = 260;
571
- var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
681
+ var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
572
682
  function getIcon(name) {
573
683
  return ICON_MAP[name] ?? Home;
574
684
  }
575
- function shortcutKey() {
576
- if (typeof navigator === "undefined") return "\u2318";
577
- return /Mac|iPhone|iPad/u.test(navigator.platform || navigator.userAgent) ? "\u2318" : "Ctrl ";
578
- }
579
685
  function flyoutContentOf(item) {
580
686
  return {
581
687
  key: item.id,
@@ -670,7 +776,7 @@ function NavGroup({
670
776
  flyout
671
777
  }) {
672
778
  const isChildActive = item.children?.some((c) => c.route === activeRoute);
673
- const [open, setOpen] = React2.useState(() => isChildActive ?? false);
779
+ const [open, setOpen] = React.useState(() => isChildActive ?? false);
674
780
  const Icon = getIcon(item.icon);
675
781
  if (collapsed) {
676
782
  const hoverProps = flyout?.rowProps(flyoutContentOf(item));
@@ -751,7 +857,7 @@ function NavGroup({
751
857
  // case in an app that groups its rail).
752
858
  /* @__PURE__ */ jsx("button", { "data-tour": `nav.${child.route}`, onClick: () => onNavigate(child.route), className: childClass, children: childInner })
753
859
  );
754
- if (!childConnector) return /* @__PURE__ */ jsx(React2.Fragment, { children: row }, child.id);
860
+ if (!childConnector) return /* @__PURE__ */ jsx(React.Fragment, { children: row }, child.id);
755
861
  const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
756
862
  const onPath = activeIndex >= 0 && childIndex < activeIndex;
757
863
  return (
@@ -813,12 +919,24 @@ function NavGroup({
813
919
  ] });
814
920
  }
815
921
  function withCollapsed(slot, collapsed) {
816
- if (!React2.isValidElement(slot)) return slot;
922
+ if (!React.isValidElement(slot)) return slot;
817
923
  if (typeof slot.type === "string") return slot;
818
- return React2.cloneElement(slot, { collapsed });
819
- }
924
+ return React.cloneElement(slot, { collapsed });
925
+ }
926
+ var SKELETON_ROWS = [
927
+ { width: 56, group: false },
928
+ { width: 68, group: true },
929
+ { width: 48, group: false },
930
+ { width: 60, group: true },
931
+ { width: 52, group: false }
932
+ ];
820
933
  function Sidebar({
821
934
  navigation,
935
+ navLoading = false,
936
+ navError,
937
+ navErrorLabel,
938
+ navRetryLabel,
939
+ onNavRetry,
822
940
  logo,
823
941
  collapsed,
824
942
  onToggle,
@@ -843,9 +961,8 @@ function Sidebar({
843
961
  drawer
844
962
  }) {
845
963
  const currentPath = currentPathProp;
846
- const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
847
964
  const activeRoute = resolveActiveRoute(navigation, currentPath);
848
- const railRef = React2.useRef(null);
965
+ const railRef = React.useRef(null);
849
966
  const flyout = useRailFlyout({
850
967
  enabled: collapsed,
851
968
  railRef,
@@ -894,10 +1011,10 @@ function Sidebar({
894
1011
  mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
895
1012
  secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
896
1013
  ] });
897
- const [outgoing, setOutgoing] = React2.useState(null);
898
- const lastBodyRef = React2.useRef(null);
899
- const lastKeyRef = React2.useRef(navKey);
900
- const timerRef = React2.useRef(null);
1014
+ const [outgoing, setOutgoing] = React.useState(null);
1015
+ const lastBodyRef = React.useRef(null);
1016
+ const lastKeyRef = React.useRef(navKey);
1017
+ const timerRef = React.useRef(null);
901
1018
  useIsomorphicLayoutEffect2(() => {
902
1019
  if (lastKeyRef.current !== navKey) {
903
1020
  const previousBody = lastBodyRef.current;
@@ -910,7 +1027,7 @@ function Sidebar({
910
1027
  }
911
1028
  lastBodyRef.current = navBody;
912
1029
  });
913
- React2.useEffect(() => () => {
1030
+ React.useEffect(() => () => {
914
1031
  if (timerRef.current) clearTimeout(timerRef.current);
915
1032
  }, []);
916
1033
  return (
@@ -935,8 +1052,10 @@ function Sidebar({
935
1052
  // paid for by the content column on every screen in every app. The
936
1053
  // labels still truncate, and a long one now truncates 32px earlier —
937
1054
  // which is the trade, taken deliberately.
938
- collapsed ? "w-16" : "w-56"
1055
+ // A largura da GAVETA vai em `style`, logo abaixo — ver o porquê lá.
1056
+ collapsed ? "w-16" : !drawer && "w-56"
939
1057
  ),
1058
+ style: drawer && !collapsed ? { width: "min(18rem, 85vw)" } : void 0,
940
1059
  children: [
941
1060
  /* @__PURE__ */ jsxs("div", { className: cn("shrink-0", !borderless && "border-b border-sidebar-border"), children: [
942
1061
  /* @__PURE__ */ jsxs(
@@ -948,7 +1067,13 @@ function Sidebar({
948
1067
  // h-14 quando a marca é a única coisa aqui e a linha tem de bater
949
1068
  // com o cabeçalho da página.
950
1069
  identity ? "h-12" : "h-14",
951
- collapsed && "justify-center"
1070
+ collapsed && "justify-center",
1071
+ // `logo === null` é uma opinião, não a falta de uma: quer dizer
1072
+ // "esta casa não é marcada pelo produto". A linha da marca então
1073
+ // não existe, e o rail abre direto no seletor de conta — o ícone da
1074
+ // casa, o nome e o chevron, numa linha só. `undefined` continua
1075
+ // caindo no rótulo padrão, que é o que um app sem logo sempre teve.
1076
+ logo === null && !topContent && "hidden"
952
1077
  ),
953
1078
  children: [
954
1079
  topContent ? /* @__PURE__ */ jsx("div", { className: cn("min-w-0", collapsed ? "flex justify-center" : "flex-1"), children: withCollapsed(topContent, collapsed) }) : !collapsed && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 px-1.5", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
@@ -956,60 +1081,68 @@ function Sidebar({
956
1081
  "button",
957
1082
  {
958
1083
  onClick: onToggle,
959
- className: "fz-tap shrink-0 rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
1084
+ className: "fz-tap flex h-9 w-9 shrink-0 items-center justify-center rounded-button text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
960
1085
  "aria-label": "Close menu",
961
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1086
+ children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
962
1087
  }
963
1088
  )
964
1089
  ]
965
1090
  }
966
1091
  ),
967
- identity && /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-2", collapsed && "flex justify-center"), children: withCollapsed(identity, collapsed) })
968
- ] }),
969
- navTop ? /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: withCollapsed(navTop, collapsed) }) : (
970
- /* Search above the first destination. It used to be a 16px magnifier
971
- tucked beside the avatar at the very bottom, which is where you put
972
- something nobody is meant to find: the fastest way through an admin
973
- with thirty screens was the one control shaped like an afterthought.
974
- Here it reads as what it is — a field — and says which key opens it.
975
- Clicking it opens the same palette ⌘K does; it is a button, not an
976
- input, because typing happens there. */
977
- /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-2 pt-2", collapsed && "flex justify-center"), children: /* @__PURE__ */ jsxs(
978
- "button",
1092
+ identity && // Sem a linha da marca acima, a conta encosta no topo da janela. O ar
1093
+ // que a marca ocupava continua sendo necessário: é o que separa o
1094
+ // conteúdo do rail da borda do navegador.
1095
+ /* @__PURE__ */ jsx(
1096
+ "div",
979
1097
  {
980
- onClick: () => setCommandPaletteOpen(true),
981
- "aria-keyshortcuts": "Meta+K Control+K",
982
1098
  className: cn(
983
- // Tonal surface, no outline. On a dark rail a 1px border is a hard
984
- // edge competing with every other hard edge; a lighter fill of the
985
- // same hue reads as "a field" without drawing a box around it — and
986
- // leaves the outline treatment free to mean focus, which it must.
987
- "fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
988
- "hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
989
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
990
- collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
1099
+ "px-2 pb-2",
1100
+ logo === null && !topContent && "pt-3",
1101
+ collapsed && "flex justify-center"
991
1102
  ),
992
- "aria-label": searchLabel,
993
- title: collapsed ? searchLabel : void 0,
994
- children: [
995
- /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
996
- !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
997
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
998
- /* @__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: [
999
- shortcutKey(),
1000
- "K"
1001
- ] })
1002
- ] })
1003
- ]
1103
+ children: withCollapsed(identity, collapsed)
1004
1104
  }
1005
- ) })
1006
- ),
1105
+ )
1106
+ ] }),
1107
+ navTop ? /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-2 pt-2", collapsed && "flex justify-center"), children: withCollapsed(navTop, collapsed) }) : /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-2 pt-2", collapsed && "flex justify-center"), children: /* @__PURE__ */ jsx(ShellSearch, { tone: "rail", collapsed, label: searchLabel }) }),
1007
1108
  beforeNav && /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-1 empty:hidden", collapsed && "flex justify-center"), children: withCollapsed(beforeNav, collapsed) }),
1109
+ navError && !navLoading && /* @__PURE__ */ jsxs("div", { className: "flex-1 px-3 py-4", children: [
1110
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-sidebar-foreground", children: navErrorLabel ?? "N\xE3o foi poss\xEDvel carregar o menu" }),
1111
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs leading-relaxed text-sidebar-muted", children: navError }),
1112
+ onNavRetry && /* @__PURE__ */ jsx(
1113
+ "button",
1114
+ {
1115
+ type: "button",
1116
+ onClick: onNavRetry,
1117
+ className: "fz-tap mt-3 rounded-button bg-sidebar-accent px-2.5 py-1.5 text-xs font-medium text-sidebar-accent-foreground transition-colors hover:brightness-110",
1118
+ children: navRetryLabel ?? "Tentar de novo"
1119
+ }
1120
+ )
1121
+ ] }),
1122
+ navLoading && /* @__PURE__ */ jsx(
1123
+ "nav",
1124
+ {
1125
+ "aria-busy": true,
1126
+ "aria-label": "Carregando menu",
1127
+ className: "fz-rail-skeleton flex-1 space-y-1 p-2",
1128
+ children: SKELETON_ROWS.map((row, i) => /* @__PURE__ */ jsxs("div", { className: "flex h-9 items-center gap-3 rounded-button px-2.5", children: [
1129
+ /* @__PURE__ */ jsx("span", { className: "h-4 w-4 shrink-0 rounded bg-sidebar-foreground/10" }),
1130
+ !collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
1131
+ /* @__PURE__ */ jsx("span", { className: "h-3 rounded bg-sidebar-foreground/10", style: { width: `${row.width}%` } }),
1132
+ row.group && /* @__PURE__ */ jsx("span", { className: "ml-auto h-3 w-3 shrink-0 rounded bg-sidebar-foreground/[0.07]" })
1133
+ ] })
1134
+ ] }, i))
1135
+ }
1136
+ ),
1008
1137
  /* @__PURE__ */ jsxs(
1009
1138
  "nav",
1010
1139
  {
1140
+ hidden: navLoading || !!navError,
1011
1141
  className: cn(
1012
1142
  "relative flex-1 p-2",
1143
+ // Entra em fade: o rail é a primeira coisa que alguém lê depois de
1144
+ // escolher a conta, e aparecer de uma vez lê como sobressalto.
1145
+ "duration-300 animate-in fade-in",
1013
1146
  // scrollbar-none: the rail still scrolls, but without the native
1014
1147
  // scrollbar gutter eating into the nav items.
1015
1148
  outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
@@ -1112,7 +1245,7 @@ function TopbarUserMenu({
1112
1245
  onBilling,
1113
1246
  userMenuExtras
1114
1247
  }) {
1115
- const [open, setOpen] = React2.useState(false);
1248
+ const [open, setOpen] = React.useState(false);
1116
1249
  if (!user) return null;
1117
1250
  const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
1118
1251
  const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
@@ -1196,7 +1329,7 @@ function getIcon2(name) {
1196
1329
  return ICON_MAP[name] ?? Home;
1197
1330
  }
1198
1331
  function NavDropdown({ item, currentPath, onNavigate }) {
1199
- const [open, setOpen] = React2.useState(false);
1332
+ const [open, setOpen] = React.useState(false);
1200
1333
  const Icon = getIcon2(item.icon);
1201
1334
  const isChildActive = item.children?.some(
1202
1335
  (child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
@@ -1363,17 +1496,17 @@ function Topbar({
1363
1496
  }) }) }) })
1364
1497
  ] });
1365
1498
  }
1366
- var ModuleHeaderSlotContext = React2.createContext(null);
1499
+ var ModuleHeaderSlotContext = React.createContext(null);
1367
1500
  function useModuleHeaderSlot() {
1368
- return React2.useContext(ModuleHeaderSlotContext);
1501
+ return React.useContext(ModuleHeaderSlotContext);
1369
1502
  }
1370
- var ModuleHeaderActionsSlotContext = React2.createContext(null);
1503
+ var ModuleHeaderActionsSlotContext = React.createContext(null);
1371
1504
  function useModuleHeaderActionsSlot() {
1372
- return React2.useContext(ModuleHeaderActionsSlotContext);
1505
+ return React.useContext(ModuleHeaderActionsSlotContext);
1373
1506
  }
1374
- var PageRailSlotContext = React2.createContext(null);
1507
+ var PageRailSlotContext = React.createContext(null);
1375
1508
  function usePageRailSlot() {
1376
- return React2.useContext(PageRailSlotContext);
1509
+ return React.useContext(PageRailSlotContext);
1377
1510
  }
1378
1511
  function bottomNavReachesAll(items) {
1379
1512
  return !!items?.some((i) => "kind" in i && i.kind === "more");
@@ -1387,6 +1520,11 @@ function ShellPageTitle({
1387
1520
  }
1388
1521
  function SidebarLayout({
1389
1522
  navigation = [],
1523
+ navLoading = false,
1524
+ navError,
1525
+ navErrorLabel,
1526
+ navRetryLabel,
1527
+ onNavRetry,
1390
1528
  logo,
1391
1529
  user,
1392
1530
  children,
@@ -1429,9 +1567,9 @@ function SidebarLayout({
1429
1567
  const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
1430
1568
  const mobileMenuOpen = useLayoutStore((st) => st.mobileDrawerOpen);
1431
1569
  const setMobileMenuOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
1432
- const [headerSlot, setHeaderSlot] = React2.useState(null);
1433
- const [actionsSlot, setActionsSlot] = React2.useState(null);
1434
- const [pageRailSlot, setPageRailSlot] = React2.useState(null);
1570
+ const [headerSlot, setHeaderSlot] = React.useState(null);
1571
+ const [actionsSlot, setActionsSlot] = React.useState(null);
1572
+ const [pageRailSlot, setPageRailSlot] = React.useState(null);
1435
1573
  const hasPageActions = useHasPageActions();
1436
1574
  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(
1437
1575
  "shell-enter fz-viewport-h flex overflow-hidden bg-background",
@@ -1445,6 +1583,11 @@ function SidebarLayout({
1445
1583
  Sidebar,
1446
1584
  {
1447
1585
  navigation,
1586
+ navLoading,
1587
+ navError,
1588
+ navErrorLabel,
1589
+ navRetryLabel,
1590
+ onNavRetry,
1448
1591
  logo,
1449
1592
  collapsed: sidebarCollapsed,
1450
1593
  onToggle: () => setSidebarCollapsed(!sidebarCollapsed),
@@ -1472,55 +1615,73 @@ function SidebarLayout({
1472
1615
  navChildConnector: sidebarNavChildConnector
1473
1616
  }
1474
1617
  ) }),
1475
- /* @__PURE__ */ jsxs("div", { className: cn(
1476
- "relative flex flex-1 overflow-hidden",
1477
- // `content`, not `card`: this is the page's own surface, and cards have
1478
- // to sit ON it. Painted card, the frame was the same tone as everything
1479
- // inside it and the page read as one flat sheet.
1480
- frame && "md:my-2 md:ml-2 md:rounded-xl md:border md:border-border md:bg-content",
1481
- // With a companion rail open, the page and the rail are ONE card: the
1482
- // right edge opens and the rail closes it. Two cards with a strip of
1483
- // chrome between them made the assistant read as a separate window
1484
- // parked next to the app rather than as a column of it — the rail's own
1485
- // left border is then the single seam between the two.
1486
- frame && (railOpen ? "md:rounded-r-none md:border-r-0" : "md:mr-2"),
1487
- immersive && "md:overflow-hidden"
1488
- ), children: [
1489
- /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1490
- /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
1491
- /* @__PURE__ */ jsxs("div", { className: cn(
1492
- "flex shrink-0 items-center justify-between gap-3 px-4 py-2",
1493
- "min-h-14 border-b border-border",
1494
- // …unless the page put its actions in it. Hiding the bar on mobile
1495
- // used to hide the only "Novo" the screen had.
1496
- mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex",
1497
- // Immersive pages draw their own window bar. Keeping ours would be two
1498
- // bars stating one thing — but only on desktop: on a phone this band
1499
- // is where the hamburger lives, and the page's bar sits under it.
1500
- immersive && "md:hidden"
1501
- ), children: [
1502
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1503
- !barOwnsNav && /* @__PURE__ */ jsx(
1504
- "button",
1505
- {
1506
- onClick: () => setMobileMenuOpen(true),
1507
- className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
1508
- "aria-label": "Open menu",
1509
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1510
- }
1511
- ),
1512
- !pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
1513
- /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
1514
- topbarStart,
1515
- /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1516
- ] }),
1517
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
1518
- /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
1519
- topbarEnd
1520
- ] })
1618
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
1619
+ /* @__PURE__ */ jsxs("div", { className: cn(
1620
+ "flex shrink-0 items-center justify-between gap-3 px-4 py-2",
1621
+ "min-h-14 border-b border-border",
1622
+ // Sobre a moldura, os tokens do menu. O que a barra mostra (título,
1623
+ // abas do módulo, botões) é escrito com `foreground`/`border`/`muted`
1624
+ // comuns: num app de menu escuro isso seria texto escuro sobre preto.
1625
+ // Remapear as variáveis aqui converte a barra inteira de uma vez, sem
1626
+ // cada página saber em que superfície caiu. A linha de baixo sai: a
1627
+ // borda de cima do cartão é a costura.
1628
+ // Altura TRAVADA em 56px na moldura, não mínima: esta faixa e a do
1629
+ // assistente são desenhadas em subárvores diferentes, e o que alinha
1630
+ // o topo dos dois cartões é as duas terem exatamente a mesma altura.
1631
+ // Com `min-h` bastava um controle mais alto de um lado para o cartão
1632
+ // daquele lado descer sozinho.
1633
+ frame && "fz-on-band md:h-14 md:border-b-0 md:text-sidebar-foreground md:[--accent-foreground:var(--sidebar-accent-foreground)] md:[--accent:var(--sidebar-accent)] md:[--background:var(--sidebar)] md:[--border:var(--sidebar-border)] md:[--card:var(--sidebar)] md:[--foreground:var(--sidebar-foreground)] md:[--muted-foreground:var(--sidebar-muted)] md:[--muted:var(--sidebar-accent)]",
1634
+ // …unless the page put its actions in it. Hiding the bar on mobile
1635
+ // used to hide the only "Novo" the screen had.
1636
+ mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex",
1637
+ // Immersive pages draw their own window bar. Keeping ours would be two
1638
+ // bars stating one thing — but only on desktop: on a phone this band
1639
+ // is where the hamburger lives, and the page's bar sits under it.
1640
+ immersive && "md:hidden"
1641
+ ), children: [
1642
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
1643
+ !barOwnsNav && /* @__PURE__ */ jsx(
1644
+ "button",
1645
+ {
1646
+ onClick: () => setMobileMenuOpen(true),
1647
+ className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
1648
+ "aria-label": "Open menu",
1649
+ children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
1650
+ }
1651
+ ),
1652
+ !pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
1653
+ /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
1654
+ topbarStart,
1655
+ /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1521
1656
  ] }),
1522
- /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
1523
- /* @__PURE__ */ jsx(SaveBar, {})
1657
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 items-center justify-end gap-2 md:flex-1", children: [
1658
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2", style: { "--primary": "var(--primary-on-rail)", "--primary-foreground": "var(--primary-on-rail-foreground)" } }),
1659
+ topbarEnd
1660
+ ] })
1661
+ ] }),
1662
+ /* @__PURE__ */ jsxs("div", { "data-frame": frame || void 0, className: cn(
1663
+ "relative flex min-w-0 flex-1 overflow-hidden",
1664
+ // `content`, not `card`: this is the page's own surface, and cards have
1665
+ // to sit ON it. Painted card, the frame was the same tone as everything
1666
+ // inside it and the page read as one flat sheet.
1667
+ "fz-page-surface",
1668
+ frame && "md:mb-2 md:ml-2 md:rounded-xl md:border md:border-border md:bg-content",
1669
+ // Sem barra no desktop (páginas imersivas desenham a sua) o cartão
1670
+ // ficaria colado no alto da janela: a folga de cima volta.
1671
+ frame && immersive && "md:mt-2",
1672
+ // With a companion rail open, the page and the rail are ONE card: the
1673
+ // right edge opens and the rail closes it. Two cards with a strip of
1674
+ // chrome between them made the assistant read as a separate window
1675
+ // parked next to the app rather than as a column of it — the rail's own
1676
+ // left border is then the single seam between the two.
1677
+ frame && (railOpen ? "md:rounded-r-none md:border-r-0" : "md:mr-2"),
1678
+ immersive && "md:overflow-hidden"
1679
+ ), children: [
1680
+ /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1681
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
1682
+ /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
1683
+ /* @__PURE__ */ jsx(SaveBar, {})
1684
+ ] })
1524
1685
  ] })
1525
1686
  ] }),
1526
1687
  mobileMenuOpen && /* @__PURE__ */ jsx(
@@ -1579,9 +1740,9 @@ function TopbarLayout({
1579
1740
  }) {
1580
1741
  const mobileMenuOpen = useLayoutStore((st) => st.mobileDrawerOpen);
1581
1742
  const setMobileMenuOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
1582
- const [headerSlot, setHeaderSlot] = React2.useState(null);
1583
- const [actionsSlot, setActionsSlot] = React2.useState(null);
1584
- const [pageRailSlot, setPageRailSlot] = React2.useState(null);
1743
+ const [headerSlot, setHeaderSlot] = React.useState(null);
1744
+ const [actionsSlot, setActionsSlot] = React.useState(null);
1745
+ const [pageRailSlot, setPageRailSlot] = React.useState(null);
1585
1746
  const rawClaimed = usePageHeaderClaimed();
1586
1747
  const setTitleSlot = usePageTitleSlotHost();
1587
1748
  const hasOutlet = useHasPageHeaderOutlet();
@@ -1616,7 +1777,7 @@ function TopbarLayout({
1616
1777
  hideMobileChrome: barOwnsNav
1617
1778
  }
1618
1779
  ),
1619
- /* @__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: [
1780
+ /* @__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", { "data-frame": frame || void 0, className: cn("fz-page-surface flex min-w-0 min-h-0 flex-1 overflow-hidden", frame && "md:rounded-xl md:border md:border-border md:bg-content"), children: [
1620
1781
  /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
1621
1782
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
1622
1783
  (pageHeaderClaimed || pageBack || hasPageActions || barOwnsNav) && /* @__PURE__ */ jsxs(
@@ -1632,7 +1793,7 @@ function TopbarLayout({
1632
1793
  /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
1633
1794
  /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
1634
1795
  ] }),
1635
- /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
1796
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2", style: { "--primary": "var(--primary-on-rail)", "--primary-foreground": "var(--primary-on-rail-foreground)" } })
1636
1797
  ]
1637
1798
  }
1638
1799
  ),
@@ -1677,30 +1838,30 @@ function MinimalLayout({
1677
1838
  }) {
1678
1839
  return /* @__PURE__ */ jsxs("div", { className: cn("shell-enter fz-viewport-h flex flex-col", hasBottomNav && "fz-has-bottom-bar"), children: [
1679
1840
  /* @__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: [
1680
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
1841
+ logo === null ? null : /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
1681
1842
  headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
1682
1843
  ] }),
1683
1844
  /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
1684
1845
  ] });
1685
1846
  }
1686
1847
  function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1687
- const [visible, setVisible] = React2.useState(false);
1688
- React2.useEffect(() => {
1848
+ const [visible, setVisible] = React.useState(false);
1849
+ React.useEffect(() => {
1689
1850
  const id = requestAnimationFrame(() => setVisible(true));
1690
1851
  return () => cancelAnimationFrame(id);
1691
1852
  }, []);
1692
- const close = React2.useCallback(() => {
1853
+ const close = React.useCallback(() => {
1693
1854
  setVisible(false);
1694
1855
  window.setTimeout(onClose, 250);
1695
1856
  }, [onClose]);
1696
- React2.useEffect(() => {
1857
+ React.useEffect(() => {
1697
1858
  const previous = document.body.style.overflow;
1698
1859
  document.body.style.overflow = "hidden";
1699
1860
  return () => {
1700
1861
  document.body.style.overflow = previous;
1701
1862
  };
1702
1863
  }, []);
1703
- React2.useEffect(() => {
1864
+ React.useEffect(() => {
1704
1865
  const onKey = (e) => {
1705
1866
  if (e.key === "Escape") close();
1706
1867
  };
@@ -1729,6 +1890,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
1729
1890
  Sidebar,
1730
1891
  {
1731
1892
  ...sidebarProps,
1893
+ navTop: sidebarProps.navTop ? /* @__PURE__ */ jsx("div", { onClickCapture: () => useLayoutStore.getState().keepDrawerOpenOnce(), children: sidebarProps.navTop }) : void 0,
1732
1894
  collapsed: false,
1733
1895
  onToggle: close,
1734
1896
  drawer: true,
@@ -1848,6 +2010,13 @@ function AppShell({
1848
2010
  variant,
1849
2011
  contentFrame,
1850
2012
  navigation = [],
2013
+ // Declarados em AppShellProps desde sempre e nunca desestruturados aqui: o
2014
+ // esqueleto do rail so existia no caminho que nao e o que o app usa.
2015
+ navLoading = false,
2016
+ navError = null,
2017
+ navErrorLabel,
2018
+ navRetryLabel,
2019
+ onNavRetry,
1851
2020
  logo,
1852
2021
  user,
1853
2022
  children,
@@ -1899,7 +2068,11 @@ function AppShell({
1899
2068
  const barRetracted = !!pageBack || pageClaimedBack;
1900
2069
  const mobileDrawerOpen = useLayoutStore((st) => st.mobileDrawerOpen);
1901
2070
  const setMobileDrawerOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
1902
- React2.useEffect(() => {
2071
+ React.useEffect(() => {
2072
+ if (useLayoutStore.getState().drawerKeepOpenOnce) {
2073
+ useLayoutStore.setState({ drawerKeepOpenOnce: false });
2074
+ return;
2075
+ }
1903
2076
  setMobileDrawerOpen(false);
1904
2077
  }, [currentPath]);
1905
2078
  const bottomBar = hasBottomNav ? /* @__PURE__ */ jsx(
@@ -1924,6 +2097,11 @@ function AppShell({
1924
2097
  SidebarLayout,
1925
2098
  {
1926
2099
  navigation,
2100
+ navLoading,
2101
+ navError,
2102
+ navErrorLabel,
2103
+ navRetryLabel,
2104
+ onNavRetry,
1927
2105
  logo,
1928
2106
  user,
1929
2107
  onNavigate,
@@ -2029,13 +2207,13 @@ function SplitWithRightRail({
2029
2207
  maxWidth = 720,
2030
2208
  resizeLabel = "Resize panel"
2031
2209
  }) {
2032
- const [width, setWidth] = React2.useState(defaultWidth);
2033
- const [dragging, setDragging] = React2.useState(false);
2034
- const clamp = React2.useCallback(
2210
+ const [width, setWidth] = React.useState(defaultWidth);
2211
+ const [dragging, setDragging] = React.useState(false);
2212
+ const clamp = React.useCallback(
2035
2213
  (value) => Math.min(maxWidth, Math.max(minWidth, value)),
2036
2214
  [minWidth, maxWidth]
2037
2215
  );
2038
- React2.useEffect(() => {
2216
+ React.useEffect(() => {
2039
2217
  if (!dragging) return;
2040
2218
  const onMove = (event) => {
2041
2219
  event.preventDefault();
@@ -2057,7 +2235,7 @@ function SplitWithRightRail({
2057
2235
  document.body.style.cursor = previousCursor;
2058
2236
  };
2059
2237
  }, [dragging, clamp]);
2060
- React2.useEffect(() => {
2238
+ React.useEffect(() => {
2061
2239
  if (dragging) return;
2062
2240
  onWidthChange?.(width);
2063
2241
  }, [dragging]);
@@ -2140,496 +2318,7 @@ function SplitWithRightRail({
2140
2318
  ] })
2141
2319
  );
2142
2320
  }
2143
- function HeaderTabs({
2144
- tabs,
2145
- value,
2146
- onChange,
2147
- className,
2148
- fill,
2149
- anchorPrefix,
2150
- ...rest
2151
- }) {
2152
- return /* @__PURE__ */ jsx(
2153
- "div",
2154
- {
2155
- role: "tablist",
2156
- "aria-label": rest["aria-label"],
2157
- className: cn(
2158
- "inline-flex max-w-full gap-5 overflow-x-auto scrollbar-none",
2159
- fill ? "items-stretch" : "items-center",
2160
- className
2161
- ),
2162
- children: tabs.map((tab) => {
2163
- const active = tab.value === value;
2164
- const Icon = tab.icon;
2165
- const box = cn(
2166
- "relative flex shrink-0 items-center gap-1.5 whitespace-nowrap text-sm transition-colors",
2167
- fill ? "h-full" : "pb-1.5",
2168
- active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground",
2169
- tab.dim && "opacity-50 hover:opacity-100"
2170
- );
2171
- const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
2172
- Icon ? /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }) : null,
2173
- /* @__PURE__ */ jsx("span", { children: tab.label }),
2174
- tab.count != null && /* @__PURE__ */ jsx("span", { className: cn(
2175
- "ml-0.5 rounded-full px-1.5 text-[11px] tabular-nums",
2176
- active ? "bg-muted text-foreground" : "bg-muted/70 text-muted-foreground"
2177
- ), children: tab.count }),
2178
- tab.badge
2179
- ] });
2180
- const underline = active ? /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 rounded-full bg-foreground" }) : null;
2181
- if (!tab.trailing) {
2182
- return /* @__PURE__ */ jsxs(
2183
- "button",
2184
- {
2185
- type: "button",
2186
- role: "tab",
2187
- "aria-selected": active,
2188
- "data-tour": anchorPrefix ? `${anchorPrefix}.${tab.value}` : void 0,
2189
- onClick: () => onChange(tab.value),
2190
- className: box,
2191
- children: [
2192
- inner,
2193
- underline
2194
- ]
2195
- },
2196
- tab.value
2197
- );
2198
- }
2199
- return /* @__PURE__ */ jsxs("div", { className: box, children: [
2200
- /* @__PURE__ */ jsx(
2201
- "button",
2202
- {
2203
- type: "button",
2204
- role: "tab",
2205
- "aria-selected": active,
2206
- "data-tour": anchorPrefix ? `${anchorPrefix}.${tab.value}` : void 0,
2207
- onClick: () => onChange(tab.value),
2208
- className: "flex items-center gap-1.5 whitespace-nowrap",
2209
- children: inner
2210
- }
2211
- ),
2212
- tab.trailing,
2213
- underline
2214
- ] }, tab.value);
2215
- })
2216
- }
2217
- );
2218
- }
2219
- function PageTabs({ actions, className, ...props }) {
2220
- return /* @__PURE__ */ jsxs(
2221
- "div",
2222
- {
2223
- className: cn(
2224
- "-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",
2225
- className
2226
- ),
2227
- children: [
2228
- /* @__PURE__ */ jsx(HeaderTabs, { ...props, className: "-mb-px" }),
2229
- actions && /* @__PURE__ */ jsx("div", { className: "ml-auto flex shrink-0 items-center gap-2 pb-1.5", children: actions })
2230
- ]
2231
- }
2232
- );
2233
- }
2234
- var SIZE = {
2235
- sm: { container: "h-[26px]", segment: "px-2.5 text-xs" },
2236
- md: { container: "h-[30px]", segment: "px-3 text-sm" }
2237
- };
2238
- function SegmentedControl({
2239
- options,
2240
- value,
2241
- onChange,
2242
- size = "sm",
2243
- className,
2244
- ...rest
2245
- }) {
2246
- const items = options.map((o) => typeof o === "string" ? { value: o, label: o } : o);
2247
- return /* @__PURE__ */ jsx(
2248
- "div",
2249
- {
2250
- role: "tablist",
2251
- "aria-label": rest["aria-label"],
2252
- className: cn(
2253
- "inline-flex items-center rounded-button border border-border bg-card p-0.5 shadow-button",
2254
- SIZE[size].container,
2255
- className
2256
- ),
2257
- children: items.map((item) => {
2258
- const active = item.value === value;
2259
- return /* @__PURE__ */ jsx(
2260
- "button",
2261
- {
2262
- type: "button",
2263
- role: "tab",
2264
- "aria-selected": active,
2265
- onClick: () => onChange(item.value),
2266
- className: cn(
2267
- "flex h-full items-center rounded-button font-semibold transition-colors",
2268
- SIZE[size].segment,
2269
- active ? "bg-foreground text-background" : "text-muted-foreground hover:bg-muted/60"
2270
- ),
2271
- children: item.label
2272
- },
2273
- item.value
2274
- );
2275
- })
2276
- }
2277
- );
2278
- }
2279
- var NavTransitionContext = React2.createContext("slide");
2280
- function NavTransitionProvider({ value, children }) {
2281
- return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
2282
- }
2283
- function useNavTransition() {
2284
- return React2.useContext(NavTransitionContext);
2285
- }
2286
- function resolveTransitionClass(transition, direction) {
2287
- if (transition === "none") return "";
2288
- if (transition === "fade") return "saas-page-enter";
2289
- if (direction === "back") return "saas-nav-back";
2290
- if (direction === "forward") return "saas-nav-forward";
2291
- return "saas-page-enter";
2292
- }
2293
- function PageTransition({
2294
- transitionKey,
2295
- direction,
2296
- className,
2297
- children
2298
- }) {
2299
- const transition = useNavTransition();
2300
- const animClass = resolveTransitionClass(transition, direction);
2301
- return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), children }, transitionKey);
2302
- }
2303
- function PageRail({ children }) {
2304
- const slot = usePageRailSlot();
2305
- if (slot) return createPortal(children, slot);
2306
- return /* @__PURE__ */ jsx(Fragment, { children });
2307
- }
2308
- function PageHeaderActions({ children }) {
2309
- const slot = useModuleHeaderActionsSlot();
2310
- useRegisterPageActions();
2311
- if (slot) return createPortal(children, slot);
2312
- return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
2313
- }
2314
- function PremiumBadge({ className }) {
2315
- const t = useTranslation();
2316
- const tip = t("upgrade.badgeTooltip");
2317
- 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 }) });
2318
- }
2319
- var ModuleLayoutContext = React2.createContext("rail");
2320
- function ModuleLayoutProvider({ variant, children }) {
2321
- return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
2322
- }
2323
- function useModuleLayout() {
2324
- return React2.useContext(ModuleLayoutContext);
2325
- }
2326
- function RailNavItem({ item }) {
2327
- const [expanded, setExpanded] = React2.useState(true);
2328
- const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
2329
- const hasChildren = item.children && item.children.length > 0;
2330
- const hasActiveChild = hasChildren && item.children.some((c) => c.active);
2331
- const isActive = item.active || hasActiveChild;
2332
- return /* @__PURE__ */ jsxs("div", { children: [
2333
- /* @__PURE__ */ jsxs(
2334
- "button",
2335
- {
2336
- type: "button",
2337
- "data-tour": `modnav.${item.id}`,
2338
- onClick: () => hasChildren ? setExpanded(!expanded) : item.onClick?.(),
2339
- className: cn(
2340
- "flex items-center gap-2 w-full rounded-md px-2.5 py-1.5 text-sm transition-colors",
2341
- isActive ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
2342
- ),
2343
- children: [
2344
- Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }),
2345
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-left truncate", children: item.label }),
2346
- item.premium && /* @__PURE__ */ jsx(PremiumBadge, { className: "ml-1" }),
2347
- hasChildren && (expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5 shrink-0 opacity-50" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 shrink-0 opacity-50" }))
2348
- ]
2349
- }
2350
- ),
2351
- hasChildren && expanded && /* @__PURE__ */ jsx("div", { className: "ml-4 mt-0.5 space-y-0.5 border-l pl-2", children: item.children.map((child) => /* @__PURE__ */ jsxs(
2352
- "button",
2353
- {
2354
- type: "button",
2355
- "data-tour": `modnav.${child.id}`,
2356
- onClick: child.onClick,
2357
- className: cn(
2358
- "flex items-center w-full rounded-md px-2.5 py-1 text-xs transition-colors",
2359
- child.active ? "text-primary font-medium bg-primary/5" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
2360
- ),
2361
- children: [
2362
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-left truncate", children: child.label }),
2363
- child.premium && /* @__PURE__ */ jsx(PremiumBadge, { className: "ml-1" })
2364
- ]
2365
- },
2366
- child.id
2367
- )) })
2368
- ] });
2369
- }
2370
- function defaultChildOf(item) {
2371
- const kids = (item.children ?? []).filter((c) => c.kind !== "action");
2372
- return kids.find((c) => c.defaultChild) ?? kids.find((c) => /(^|-)list$/i.test(c.id)) ?? kids.find((c) => !/(^|-)(new|novo|nova|create)$/i.test(c.id)) ?? kids[0];
2373
- }
2374
- function MobileTabs({ nav }) {
2375
- const scrollRef = React2.useRef(null);
2376
- const [expandedId, setExpandedId] = React2.useState(null);
2377
- React2.useEffect(() => {
2378
- const container = scrollRef.current;
2379
- if (!container) return;
2380
- const activeEl = container.querySelector('[data-active="true"]');
2381
- if (activeEl) {
2382
- const left = activeEl.offsetLeft - container.offsetWidth / 2 + activeEl.offsetWidth / 2;
2383
- container.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
2384
- }
2385
- }, [nav]);
2386
- return /* @__PURE__ */ jsxs("div", { className: "md:hidden -mx-1 mb-4 no-print", children: [
2387
- /* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex gap-1 px-1 pb-2 overflow-x-auto scrollbar-none", children: nav.map((item) => {
2388
- const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
2389
- const hasChildren = item.children && item.children.length > 0;
2390
- const isActive = item.active || item.children?.some((c) => c.active);
2391
- const isExpanded = expandedId === item.id;
2392
- return /* @__PURE__ */ jsxs(
2393
- "button",
2394
- {
2395
- type: "button",
2396
- "data-active": isActive || void 0,
2397
- onClick: () => {
2398
- if (hasChildren) {
2399
- if (!isExpanded) defaultChildOf(item)?.onClick?.();
2400
- setExpandedId(isExpanded ? null : item.id);
2401
- } else {
2402
- item.onClick?.();
2403
- setExpandedId(null);
2404
- }
2405
- },
2406
- className: cn(
2407
- // This strip is the most-tapped control in most modules and it
2408
- // was 28px tall — a third under the comfortable minimum. Height
2409
- // and text step up on touch; the desktop rail is unaffected.
2410
- "flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-4 text-sm font-medium transition-colors",
2411
- "min-h-[2.75rem] active:scale-[0.98]",
2412
- isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"
2413
- ),
2414
- children: [
2415
- Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
2416
- item.label,
2417
- item.premium && /* @__PURE__ */ jsx(PremiumBadge, {}),
2418
- hasChildren && /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-2.5 w-2.5 transition-transform", isExpanded && "rotate-180") })
2419
- ]
2420
- },
2421
- item.id
2422
- );
2423
- }) }),
2424
- expandedId && (() => {
2425
- const item = nav.find((n) => n.id === expandedId);
2426
- const shown = (item?.children ?? []).filter((c) => c.kind !== "action");
2427
- if (shown.length < 2) return null;
2428
- return /* @__PURE__ */ jsx("div", { className: "flex gap-1 px-2 pb-2 animate-in fade-in-0 slide-in-from-top-1", children: shown.map((child) => /* @__PURE__ */ jsxs(
2429
- "button",
2430
- {
2431
- type: "button",
2432
- onClick: () => {
2433
- child.onClick?.();
2434
- setExpandedId(null);
2435
- },
2436
- className: cn(
2437
- "whitespace-nowrap rounded-full px-2.5 py-1 text-[11px] font-medium transition-colors shrink-0",
2438
- child.active ? "bg-foreground/10 text-foreground" : "text-muted-foreground hover:text-foreground"
2439
- ),
2440
- children: [
2441
- child.label,
2442
- child.premium && /* @__PURE__ */ jsx(PremiumBadge, {})
2443
- ]
2444
- },
2445
- child.id
2446
- )) });
2447
- })()
2448
- ] });
2449
- }
2450
- var BackStyleContext = React2.createContext("link");
2451
- function BackStyleProvider({ style, children }) {
2452
- return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
2453
- }
2454
- function useBackStyle() {
2455
- return React2.useContext(BackStyleContext);
2456
- }
2457
- function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
2458
- const t = useTranslation();
2459
- const appStyle = useBackStyle();
2460
- const hasShellBar = useHasPageHeaderHost() && !inline;
2461
- const cross = useCrossModuleBack(onBack, parentLabel);
2462
- const effectiveOnBack = inline ? onBack : cross.onBack;
2463
- const effectiveParentLabel = inline ? parentLabel : cross.parentLabel;
2464
- useBackHandler(effectiveOnBack);
2465
- const Icon = icon ? ICON_MAP[icon] ?? null : null;
2466
- if (inline && effectiveOnBack) {
2467
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2468
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
2469
- /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel: effectiveParentLabel, onBack: effectiveOnBack }),
2470
- actions && /* @__PURE__ */ jsx("div", { className: "no-print", children: actions })
2471
- ] }),
2472
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
2473
- ] });
2474
- }
2475
- if (hasShellBar) {
2476
- return /* @__PURE__ */ jsx(
2477
- ShellSubpageHeader,
2478
- {
2479
- title,
2480
- subtitle,
2481
- icon: Icon,
2482
- onBack: effectiveOnBack,
2483
- parentLabel: effectiveParentLabel,
2484
- actions
2485
- }
2486
- );
2487
- }
2488
- const style = !effectiveOnBack ? "icon" : backStyle ?? appStyle;
2489
- const effectiveStyle = style !== "icon" && !effectiveParentLabel ? "icon" : style;
2490
- const titleBlock = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
2491
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
2492
- effectiveStyle === "icon" && effectiveOnBack && /* @__PURE__ */ jsx(
2493
- "button",
2494
- {
2495
- onClick: effectiveOnBack,
2496
- className: "flex h-8 w-8 items-center justify-center rounded-lg border hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors",
2497
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
2498
- }
2499
- ),
2500
- Icon && /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-muted", children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-muted-foreground" }) }),
2501
- /* @__PURE__ */ jsxs("div", { children: [
2502
- /* @__PURE__ */ jsx("h2", { className: "text-xl font-bold", children: title }),
2503
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
2504
- ] })
2505
- ] }),
2506
- actions && /* @__PURE__ */ jsx("div", { className: "no-print", children: actions })
2507
- ] });
2508
- if (effectiveStyle === "breadcrumb") {
2509
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4 mb-6", children: [
2510
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
2511
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
2512
- /* @__PURE__ */ jsx("button", { onClick: effectiveOnBack, className: "hover:text-foreground transition-colors", children: effectiveParentLabel }),
2513
- /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" }),
2514
- /* @__PURE__ */ jsx("span", { className: "text-foreground font-medium", children: title })
2515
- ] }),
2516
- actions && /* @__PURE__ */ jsx("div", { className: "no-print", children: actions })
2517
- ] }),
2518
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground -mt-2", children: subtitle })
2519
- ] });
2520
- }
2521
- if (effectiveStyle === "link") {
2522
- return /* @__PURE__ */ jsxs("div", { className: "space-y-3 mb-6", children: [
2523
- /* @__PURE__ */ jsxs(
2524
- "button",
2525
- {
2526
- onClick: effectiveOnBack,
2527
- className: "inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors",
2528
- children: [
2529
- /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }),
2530
- t("crud.detail.backTo", { entities: effectiveParentLabel })
2531
- ]
2532
- }
2533
- ),
2534
- titleBlock
2535
- ] });
2536
- }
2537
- return /* @__PURE__ */ jsx("div", { className: "space-y-4 mb-6", children: titleBlock });
2538
- }
2539
- function ShellSubpageHeader({
2540
- title,
2541
- subtitle,
2542
- icon: Icon,
2543
- onBack,
2544
- parentLabel,
2545
- actions
2546
- }) {
2547
- usePageHeader(true, { back: !!onBack });
2548
- const crumb = /* @__PURE__ */ jsx(PageTitlePortal, { children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
2549
- Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
2550
- /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
2551
- ] }) });
2552
- if (!actions) return crumb;
2553
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2554
- crumb,
2555
- /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) })
2556
- ] });
2557
- }
2558
- function ModuleHeader({ nav }) {
2559
- function defaultChild(item) {
2560
- const kids2 = (item.children ?? []).filter((c) => c.kind !== "action");
2561
- return kids2.find((c) => /list/i.test(c.id) || /^list$/i.test(c.label)) ?? kids2[kids2.length - 1];
2562
- }
2563
- const activeItem = nav.find((item) => item.active || item.children?.some((c) => c.active));
2564
- const tabs = nav.map((item) => ({
2565
- value: item.id,
2566
- label: item.label,
2567
- icon: item.icon ? ICON_MAP[item.icon] ?? void 0 : void 0,
2568
- // Plan-gated views keep their upgrade marker — dropping it would hide the
2569
- // only signal that the tab costs an upgrade.
2570
- badge: item.premium ? /* @__PURE__ */ jsx(PremiumBadge, {}) : void 0
2571
- }));
2572
- const onChange = (id) => {
2573
- const item = nav.find((n) => n.id === id);
2574
- if (!item) return;
2575
- const hasChildren = item.children && item.children.length > 0;
2576
- if (hasChildren) defaultChild(item)?.onClick?.();
2577
- else item.onClick?.();
2578
- };
2579
- const kids = (activeItem?.children ?? []).filter((c) => c.kind !== "action");
2580
- const activeKid = kids.find((c) => c.active)?.id ?? kids[0]?.id;
2581
- const childControl = kids.length > 1 ? /* @__PURE__ */ jsx(
2582
- SegmentedControl,
2583
- {
2584
- size: "sm",
2585
- "aria-label": activeItem?.label,
2586
- options: kids.map((c) => ({ value: c.id, label: c.label })),
2587
- value: activeKid ?? "",
2588
- onChange: (id) => kids.find((c) => c.id === id)?.onClick?.()
2589
- }
2590
- ) : void 0;
2591
- return /* @__PURE__ */ jsx(
2592
- PageTabs,
2593
- {
2594
- tabs,
2595
- value: activeItem?.id ?? nav[0]?.id ?? "",
2596
- onChange,
2597
- actions: childControl,
2598
- anchorPrefix: "modnav"
2599
- }
2600
- );
2601
- }
2602
- function ModulePage({ title: _title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
2603
- const ctxVariant = useModuleLayout();
2604
- const variant = navVariant ?? ctxVariant;
2605
- const transitionKey = viewKey ?? "view";
2606
- const onSubpage = useHasPageHeaderBack();
2607
- if (variant === "sidebar") {
2608
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2609
- showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2610
- /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
2611
- ] });
2612
- }
2613
- if (variant === "tabs") {
2614
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2615
- !onSubpage && /* @__PURE__ */ jsx(ModuleHeader, { nav }),
2616
- showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2617
- /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
2618
- ] });
2619
- }
2620
- return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0", className), children: [
2621
- /* @__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: [
2622
- /* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(RailNavItem, { item }, item.id)) }),
2623
- sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto pt-3", children: sidebarFooter })
2624
- ] }) }),
2625
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2626
- showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
2627
- !onSubpage && /* @__PURE__ */ jsx(MobileTabs, { nav }),
2628
- /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
2629
- ] })
2630
- ] });
2631
- }
2632
2321
 
2633
- export { AppShell, BackStyleProvider, HeaderTabs, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeader, PageHeaderActions, PageHeaderCrumb, PageRail, PageTabs, PageTransition, RailNavItem, SaveBar, SaveBarProvider, SegmentedControl, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SubpageHeader, Topbar, bottomNavReachesAll, useBackHandler, useBackStyle, useCrossModuleBack, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, useRailRow, useSaveBar, useSaveBarVisible };
2634
- //# sourceMappingURL=chunk-PQUS22JE.js.map
2635
- //# sourceMappingURL=chunk-PQUS22JE.js.map
2322
+ export { AppShell, PageHeader, PageHeaderCrumb, PageHeaderOutlet, PageTitlePortal, SaveBar, SaveBarProvider, ShellSearch, Sidebar, Topbar, bottomNavReachesAll, useBackHandler, useCrossModuleBack, useHasPageActions, useHasPageHeaderBack, useHasPageHeaderHost, useHasPageHeaderOutlet, useLayoutStore, useModuleHeaderActionsSlot, useModuleHeaderSlot, usePageHeader, usePageHeaderClaimed, usePageHeaderHost, usePageRailSlot, useRailRow, useRegisterPageActions, useSaveBar, useSaveBarVisible };
2323
+ //# sourceMappingURL=chunk-RRNMNFLF.js.map
2324
+ //# sourceMappingURL=chunk-RRNMNFLF.js.map