@fayz-ai/ui 0.31.0 → 0.33.1

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 (62) hide show
  1. package/dist/{chunk-VBYFN5ZL.js → chunk-33A7SN2N.js} +693 -173
  2. package/dist/chunk-33A7SN2N.js.map +1 -0
  3. package/dist/{chunk-ABY2HXG4.js → chunk-6GWXDVCO.js} +223 -27
  4. package/dist/chunk-6GWXDVCO.js.map +1 -0
  5. package/dist/{chunk-VDAYIQX4.js → chunk-C6PW5C5Q.js} +3 -3
  6. package/dist/chunk-C6PW5C5Q.js.map +1 -0
  7. package/dist/{chunk-NTPQRDJF.js → chunk-Q2B5RPH5.js} +13 -16
  8. package/dist/chunk-Q2B5RPH5.js.map +1 -0
  9. package/dist/{chunk-UDY4KJRX.js → chunk-UNEEY5XG.js} +7 -5
  10. package/dist/chunk-UNEEY5XG.js.map +1 -0
  11. package/dist/dashboard/DashboardGrid.d.ts +8 -1
  12. package/dist/dashboard/DashboardGrid.d.ts.map +1 -1
  13. package/dist/dashboard/KpiCard.d.ts.map +1 -1
  14. package/dist/dashboard/index.js +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +9 -9
  18. package/dist/layout/AppShell.d.ts.map +1 -1
  19. package/dist/layout/ModulePage.d.ts +55 -2
  20. package/dist/layout/ModulePage.d.ts.map +1 -1
  21. package/dist/layout/Sidebar.d.ts +6 -1
  22. package/dist/layout/Sidebar.d.ts.map +1 -1
  23. package/dist/layout/index.d.ts +1 -1
  24. package/dist/layout/index.d.ts.map +1 -1
  25. package/dist/layout/index.js +1 -1
  26. package/dist/layout/rail-flyout.d.ts.map +1 -1
  27. package/dist/primitives/collapsible-column.d.ts +38 -0
  28. package/dist/primitives/collapsible-column.d.ts.map +1 -0
  29. package/dist/primitives/coming-soon.d.ts +31 -0
  30. package/dist/primitives/coming-soon.d.ts.map +1 -0
  31. package/dist/primitives/data-table.d.ts +10 -1
  32. package/dist/primitives/data-table.d.ts.map +1 -1
  33. package/dist/primitives/date-picker.d.ts +6 -1
  34. package/dist/primitives/date-picker.d.ts.map +1 -1
  35. package/dist/primitives/filter-panel.d.ts +85 -0
  36. package/dist/primitives/filter-panel.d.ts.map +1 -0
  37. package/dist/primitives/filter-rail.d.ts +10 -7
  38. package/dist/primitives/filter-rail.d.ts.map +1 -1
  39. package/dist/primitives/header-tabs.d.ts +9 -35
  40. package/dist/primitives/header-tabs.d.ts.map +1 -1
  41. package/dist/primitives/index.d.ts +2 -0
  42. package/dist/primitives/index.d.ts.map +1 -1
  43. package/dist/primitives/index.js +3 -3
  44. package/dist/primitives/input.d.ts.map +1 -1
  45. package/dist/primitives/list-view.d.ts +3 -1
  46. package/dist/primitives/list-view.d.ts.map +1 -1
  47. package/dist/primitives/nav-connector.d.ts +23 -0
  48. package/dist/primitives/nav-connector.d.ts.map +1 -0
  49. package/dist/primitives/phone-field.d.ts.map +1 -1
  50. package/dist/primitives/search-select.d.ts.map +1 -1
  51. package/dist/stores/index.d.ts +1 -1
  52. package/dist/stores/index.d.ts.map +1 -1
  53. package/dist/stores/page-header.store.d.ts +35 -1
  54. package/dist/stores/page-header.store.d.ts.map +1 -1
  55. package/dist/styles.css +66 -1
  56. package/dist/theme/index.js +1 -1
  57. package/package.json +2 -2
  58. package/dist/chunk-ABY2HXG4.js.map +0 -1
  59. package/dist/chunk-NTPQRDJF.js.map +0 -1
  60. package/dist/chunk-UDY4KJRX.js.map +0 -1
  61. package/dist/chunk-VBYFN5ZL.js.map +0 -1
  62. package/dist/chunk-VDAYIQX4.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { cn } from './chunk-OKZ6WQB3.js';
2
2
  import { ICON_MAP } from './chunk-ATDIWKBD.js';
3
3
  import * as React11 from 'react';
4
- import { Loader2, ChevronLeft, ArrowLeft, X, Bell, PanelLeft, PanelLeftClose, Search, CornerDownLeft, Menu, Crown, ChevronDown, ChevronRight, Info, ExternalLink, CreditCard, Sparkles, Home, ChevronUp, Plus, Settings, UserCog, LogOut } from 'lucide-react';
4
+ import { Loader2, ChevronLeft, ArrowLeft, X, Bell, PanelLeft, PanelLeftClose, Search, CornerDownLeft, Menu, Crown, ChevronsRight, ChevronsLeft, ChevronDown, ChevronRight, Info, ExternalLink, CreditCard, Sparkles, Home, ChevronUp, Plus, Settings, UserCog, LogOut } from 'lucide-react';
5
5
  import { useTranslation, useNavReferrer, routeModule, navHistoryBack, recordNavigation } from '@fayz-ai/core';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
@@ -215,10 +215,14 @@ var TooltipContent = React11.forwardRef(({ className, sideOffset = 4, ...props }
215
215
  ));
216
216
  TooltipContent.displayName = TooltipPrimitive.Content.displayName;
217
217
  var usePageHeaderStore = create((set, get) => ({
218
+ moduleViewLabel: null,
219
+ setModuleViewLabel: (moduleViewLabel) => set((state) => state.moduleViewLabel === moduleViewLabel ? state : { moduleViewLabel }),
218
220
  shellSlot: null,
219
221
  setShellSlot: (element) => set({ shellSlot: element }),
220
222
  outletSlot: null,
221
223
  setOutletSlot: (element) => set({ outletSlot: element }),
224
+ outletActionsSlot: null,
225
+ setOutletActionsSlot: (element) => set({ outletActionsSlot: element }),
222
226
  claimed: false,
223
227
  owner: null,
224
228
  hasBack: false,
@@ -253,6 +257,9 @@ function usePageHeaderClaimed() {
253
257
  function usePageTitleSlot() {
254
258
  return usePageHeaderStore((s) => s.outletSlot ?? s.shellSlot);
255
259
  }
260
+ function usePageActionsOutletSlot() {
261
+ return usePageHeaderStore((s) => s.outletActionsSlot);
262
+ }
256
263
  function usePageTitleSlotHost() {
257
264
  return usePageHeaderStore((s) => s.setShellSlot);
258
265
  }
@@ -270,19 +277,30 @@ function useHasPageHeaderHost() {
270
277
  function useHasPageHeaderOutlet() {
271
278
  return usePageHeaderStore((s) => s.outlets > 0);
272
279
  }
273
- function PageHeaderOutlet({ className }) {
280
+ function PageHeaderOutlet({ className, active = true }) {
274
281
  const setOutletSlot = usePageHeaderStore((s) => s.setOutletSlot);
282
+ const setOutletActionsSlot = usePageHeaderStore((s) => s.setOutletActionsSlot);
275
283
  const claimed = usePageHeaderStore((s) => s.claimed);
284
+ const hasActions = usePageHeaderStore((s) => s.actions > 0);
276
285
  const addOutlet = usePageHeaderStore((s) => s.addOutlet);
277
286
  const removeOutlet = usePageHeaderStore((s) => s.removeOutlet);
278
287
  React11.useEffect(() => {
288
+ if (!active) return;
279
289
  addOutlet();
280
290
  return removeOutlet;
281
- }, [addOutlet, removeOutlet]);
282
- return React11.createElement("div", {
283
- ref: setOutletSlot,
284
- className: `${className ?? "mb-4"}${claimed ? "" : " hidden"}`
285
- });
291
+ }, [active, addOutlet, removeOutlet]);
292
+ React11.useEffect(() => {
293
+ if (active) return;
294
+ setOutletSlot(null);
295
+ setOutletActionsSlot(null);
296
+ }, [active, setOutletSlot, setOutletActionsSlot]);
297
+ if (!active) return null;
298
+ return React11.createElement(
299
+ "div",
300
+ { className: `flex min-h-9 items-center justify-between gap-3${claimed || hasActions ? "" : " hidden"} ${className ?? "mb-3"}` },
301
+ React11.createElement("div", { ref: setOutletSlot, className: "flex min-w-0 flex-1 items-center" }),
302
+ React11.createElement("div", { ref: setOutletActionsSlot, className: "flex shrink-0 items-center gap-2" })
303
+ );
286
304
  }
287
305
  function usePageHeader(active, options) {
288
306
  const id = React11.useId();
@@ -301,6 +319,16 @@ function usePageHeader(active, options) {
301
319
  function useHasPageHeaderBack() {
302
320
  return usePageHeaderStore((s) => s.hasBack);
303
321
  }
322
+ function useModuleViewLabel() {
323
+ return usePageHeaderStore((s) => s.moduleViewLabel);
324
+ }
325
+ function usePublishModuleViewLabel(label) {
326
+ const setLabel = usePageHeaderStore((s) => s.setModuleViewLabel);
327
+ React11.useEffect(() => {
328
+ setLabel(label ?? null);
329
+ return () => setLabel(null);
330
+ }, [label, setLabel]);
331
+ }
304
332
  function PageTitlePortal({ children }) {
305
333
  const slot = usePageTitleSlot();
306
334
  if (!slot) return React11.createElement(React11.Fragment, null, children);
@@ -539,6 +567,7 @@ function useRailFlyout({
539
567
  "div",
540
568
  {
541
569
  ref: panelRef,
570
+ style: { borderTopLeftRadius: 2, borderBottomLeftRadius: 2 },
542
571
  className: cn(
543
572
  // Sidebar tone, sidebar border: the panel is the menu leaning out,
544
573
  // not a popover from the page underneath it.
@@ -619,6 +648,51 @@ function FlyoutBody({
619
648
  ] })
620
649
  ] });
621
650
  }
651
+ function NavChildConnector({ onPath, active }) {
652
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
653
+ onPath && /* @__PURE__ */ jsx(
654
+ "span",
655
+ {
656
+ "aria-hidden": true,
657
+ className: "pointer-events-none absolute bottom-0 left-[13px] top-0 w-[1.5px] bg-current"
658
+ }
659
+ ),
660
+ active && /* @__PURE__ */ jsxs(
661
+ "svg",
662
+ {
663
+ "aria-hidden": true,
664
+ className: "pointer-events-none absolute left-1 top-0",
665
+ width: "21",
666
+ height: "32",
667
+ viewBox: "0 0 21 32",
668
+ fill: "none",
669
+ children: [
670
+ /* @__PURE__ */ jsx(
671
+ "path",
672
+ {
673
+ d: "M9.75 0 V11 Q9.75 16 14.75 16 H20",
674
+ stroke: "currentColor",
675
+ strokeWidth: "1.5",
676
+ strokeLinecap: "round",
677
+ fill: "none"
678
+ }
679
+ ),
680
+ /* @__PURE__ */ jsx(
681
+ "path",
682
+ {
683
+ d: "M17 13 L20 16 L17 19",
684
+ stroke: "currentColor",
685
+ strokeWidth: "1.5",
686
+ strokeLinecap: "round",
687
+ strokeLinejoin: "round",
688
+ fill: "none"
689
+ }
690
+ )
691
+ ]
692
+ }
693
+ )
694
+ ] });
695
+ }
622
696
  var RAIL_PUSH_MS = 260;
623
697
  var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React11.useLayoutEffect : React11.useEffect;
624
698
  function getIcon(name) {
@@ -845,47 +919,7 @@ function NavGroup({
845
919
  // to /70 when inactive, and a line fading along with the label it
846
920
  // points AT was nearly invisible on a dark rail.
847
921
  /* @__PURE__ */ jsxs("div", { className: "relative h-8 text-sidebar-foreground/45", children: [
848
- onPath && /* @__PURE__ */ jsx(
849
- "span",
850
- {
851
- "aria-hidden": true,
852
- className: "pointer-events-none absolute bottom-0 left-[13px] top-0 w-[1.5px] bg-current"
853
- }
854
- ),
855
- isActive && /* @__PURE__ */ jsxs(
856
- "svg",
857
- {
858
- "aria-hidden": true,
859
- className: "pointer-events-none absolute left-1 top-0",
860
- width: "21",
861
- height: "32",
862
- viewBox: "0 0 21 32",
863
- fill: "none",
864
- children: [
865
- /* @__PURE__ */ jsx(
866
- "path",
867
- {
868
- d: "M9.75 0 V11 Q9.75 16 14.75 16 H20",
869
- stroke: "currentColor",
870
- strokeWidth: "1.5",
871
- strokeLinecap: "round",
872
- fill: "none"
873
- }
874
- ),
875
- /* @__PURE__ */ jsx(
876
- "path",
877
- {
878
- d: "M17 13 L20 16 L17 19",
879
- stroke: "currentColor",
880
- strokeWidth: "1.5",
881
- strokeLinecap: "round",
882
- strokeLinejoin: "round",
883
- fill: "none"
884
- }
885
- )
886
- ]
887
- }
888
- ),
922
+ /* @__PURE__ */ jsx(NavChildConnector, { onPath, active: isActive }),
889
923
  row
890
924
  ] }, child.id)
891
925
  );
@@ -926,6 +960,7 @@ function Sidebar({
926
960
  borderless,
927
961
  userMenuSlot,
928
962
  notificationSlot,
963
+ hideNotifications = false,
929
964
  unreadCount = 0,
930
965
  searchLabel: _searchLabel,
931
966
  sectionLabels,
@@ -1197,7 +1232,7 @@ function Sidebar({
1197
1232
  ), children: [
1198
1233
  withCollapsed(userMenuSlot, collapsed),
1199
1234
  !collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1200
- withCollapsed(notificationSlot, collapsed) ?? /* @__PURE__ */ jsxs(
1235
+ !hideNotifications && (withCollapsed(notificationSlot, collapsed) ?? /* @__PURE__ */ jsxs(
1201
1236
  "button",
1202
1237
  {
1203
1238
  ...collapsed ? flyout.rowProps({
@@ -1211,7 +1246,7 @@ function Sidebar({
1211
1246
  unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
1212
1247
  ]
1213
1248
  }
1214
- ),
1249
+ )),
1215
1250
  !drawer && /* @__PURE__ */ jsx(
1216
1251
  "button",
1217
1252
  {
@@ -1914,7 +1949,9 @@ function SidebarLayout({
1914
1949
  railOpen
1915
1950
  }) {
1916
1951
  const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
1917
- const pageHeaderClaimed = usePageHeaderClaimed();
1952
+ const rawClaimed = usePageHeaderClaimed();
1953
+ const hasOutlet = useHasPageHeaderOutlet();
1954
+ const pageHeaderClaimed = hasOutlet ? false : rawClaimed;
1918
1955
  const setTitleSlot = usePageTitleSlotHost();
1919
1956
  usePageHeaderHost();
1920
1957
  const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
@@ -1959,7 +1996,7 @@ function SidebarLayout({
1959
1996
  footerContent: sidebarFooterContent,
1960
1997
  borderless: frame,
1961
1998
  userMenuSlot,
1962
- notificationSlot,
1999
+ hideNotifications: true,
1963
2000
  unreadCount,
1964
2001
  searchLabel,
1965
2002
  sectionLabels: menuNavigation ? void 0 : sidebarSectionLabels,
@@ -2025,19 +2062,22 @@ function SidebarLayout({
2025
2062
  ]
2026
2063
  }
2027
2064
  ),
2028
- !pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack, primary: pagePrimary }),
2029
- /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
2030
2065
  topbarStart,
2031
2066
  /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
2032
2067
  ] }),
2033
2068
  /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 flex-1 justify-center px-2 md:flex", children: /* @__PURE__ */ jsx(ShellSearch, { tone: "bar", label: searchLabel, className: "w-full max-w-[280px]" }) }),
2034
2069
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 shrink-0 items-center justify-end gap-2 md:flex-1", children: [
2035
- /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2", style: { "--primary": "var(--primary-on-rail)", "--primary-foreground": "var(--primary-on-rail-foreground)" } }),
2036
- topbarEnd
2070
+ topbarEnd,
2071
+ notificationSlot
2037
2072
  ] })
2038
2073
  ] }),
2039
2074
  /* @__PURE__ */ jsxs("div", { "data-frame": frame || void 0, className: cn(
2040
- "relative flex min-w-0 flex-1 overflow-hidden",
2075
+ // `flex-col`: a faixa da página é a PRIMEIRA fila do cartão e
2076
+ // atravessa-o inteiro; a trilha da página e o conteúdo dividem a fila
2077
+ // de baixo. Era uma coluna só, com a trilha ao lado da faixa: a
2078
+ // migalha começava depois do menu de Configurações e a régua sob ela
2079
+ // parava no meio do cartão, como se fossem duas telas encostadas.
2080
+ "relative flex min-w-0 flex-1 flex-col overflow-hidden",
2041
2081
  // `content`, not `card`: this is the page's own surface, and cards have
2042
2082
  // to sit ON it. Painted card, the frame was the same tone as everything
2043
2083
  // inside it and the page read as one flat sheet.
@@ -2054,10 +2094,49 @@ function SidebarLayout({
2054
2094
  frame && (railOpen ? "md:rounded-r-none md:border-r-0" : "md:mr-2"),
2055
2095
  immersive && "md:overflow-hidden"
2056
2096
  ), children: [
2057
- /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
2058
- /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
2059
- /* @__PURE__ */ jsx("main", { className: cn("min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", menuOpen && "invisible md:visible"), children }),
2060
- /* @__PURE__ */ jsx(SaveBar, {})
2097
+ /* @__PURE__ */ jsxs(
2098
+ "div",
2099
+ {
2100
+ style: {
2101
+ // A RÉGUA NÃO ATRAVESSA A COLUNA DO MÓDULO.
2102
+ //
2103
+ // Ela fecha o cabeçalho, e o cabeçalho termina onde a coluna começa:
2104
+ // sobre a coluna, a mesma linha separava duas coisas que são uma só —
2105
+ // o nome da tela e o menu que pende dele. Tapar o pedaço com um pixel
2106
+ // da cor do papel funcionava num tema e falhava no outro, porque o
2107
+ // papel do cartão e o `--page-surface` nem sempre são a mesma tinta.
2108
+ //
2109
+ // Então ela NASCE depois: a régua é um degradê de 1px que só começa
2110
+ // em `--page-rail-seam` — a largura da coluna mais o raio da curva,
2111
+ // publicado por quem a desenha (ver RailColumn). Sem coluna, a
2112
+ // variável não existe e a régua começa em zero, como sempre.
2113
+ backgroundImage: "linear-gradient(to right, transparent 0 var(--page-rail-seam, 0px), hsl(var(--border)) var(--page-rail-seam, 0px) 100%)",
2114
+ backgroundSize: "100% 1px",
2115
+ backgroundRepeat: "no-repeat",
2116
+ backgroundPosition: "left bottom"
2117
+ },
2118
+ className: cn(
2119
+ // O MESMO vidro da coluna do módulo logo abaixo — uma classe só,
2120
+ // senão as duas superfícies encostadas saem em tons diferentes e
2121
+ // a emenda que acabamos de apagar volta como degrau de cor.
2122
+ "fz-liquid-glass relative z-20 flex min-h-12 shrink-0 items-center justify-between gap-3 px-3 md:px-6",
2123
+ menuOpen && "invisible md:visible"
2124
+ ),
2125
+ children: [
2126
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
2127
+ !pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack, primary: pagePrimary }),
2128
+ /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" })
2129
+ ] }),
2130
+ /* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
2131
+ ]
2132
+ }
2133
+ ),
2134
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 min-h-0 flex-1 overflow-hidden", children: [
2135
+ /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
2136
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
2137
+ /* @__PURE__ */ jsx("main", { className: cn("min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", menuOpen && "invisible md:visible"), children }),
2138
+ /* @__PURE__ */ jsx(SaveBar, {})
2139
+ ] })
2061
2140
  ] })
2062
2141
  ] })
2063
2142
  ] }),
@@ -2185,43 +2264,49 @@ function TopbarLayout({
2185
2264
  hideMobileChrome: barOwnsNav
2186
2265
  }
2187
2266
  ),
2188
- /* @__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: [
2189
- /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
2190
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
2191
- (pageHeaderClaimed || pageBack || hasPageActions || barOwnsNav) && /* @__PURE__ */ jsxs(
2192
- "div",
2193
- {
2194
- className: cn(
2195
- // `relative` + glass for the same reason as the sidebar
2196
- // layout's band: on a phone this IS the navigation bar.
2197
- "relative flex min-h-[44px] md:min-h-12 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2",
2198
- menuOpen && "invisible md:visible",
2199
- "fayz-glass-topbar",
2200
- !(pageHeaderClaimed || pageBack || hasPageActions) && "md:hidden"
2201
- ),
2202
- children: [
2203
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
2204
- mobileBackToMenu && /* @__PURE__ */ jsxs(
2205
- "button",
2206
- {
2207
- type: "button",
2208
- onClick: mobileBackToMenu,
2209
- className: "fz-ios-tint fz-tap -ml-1 inline-flex shrink-0 items-center gap-0.5 text-[15px] md:hidden",
2210
- children: [
2211
- /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" }),
2212
- /* @__PURE__ */ jsx("span", { children: "Voltar" })
2213
- ]
2214
- }
2215
- ),
2216
- !pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack, primary: pagePrimary }),
2217
- /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
2218
- /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
2219
- ] }),
2220
- /* @__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)" } })
2221
- ]
2222
- }
2223
- ),
2224
- /* @__PURE__ */ jsx("div", { className: cn("min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", menuOpen && "invisible md:visible"), children })
2267
+ /* @__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 flex-col overflow-hidden", frame && "md:rounded-xl md:border md:border-border md:bg-content"), children: [
2268
+ (pageHeaderClaimed || pageBack || hasPageActions || barOwnsNav) && /* @__PURE__ */ jsxs(
2269
+ "div",
2270
+ {
2271
+ style: {
2272
+ backgroundImage: "linear-gradient(to right, transparent 0 var(--page-rail-seam, 0px), hsl(var(--border)) var(--page-rail-seam, 0px) 100%)",
2273
+ backgroundSize: "100% 1px",
2274
+ backgroundRepeat: "no-repeat",
2275
+ backgroundPosition: "left bottom"
2276
+ },
2277
+ className: cn(
2278
+ // `relative` + glass for the same reason as the sidebar
2279
+ // layout's band: on a phone this IS the navigation bar.
2280
+ "relative flex min-h-[44px] md:min-h-12 shrink-0 items-center justify-between gap-3 px-4 py-2",
2281
+ menuOpen && "invisible md:visible",
2282
+ "fayz-glass-topbar",
2283
+ !(pageHeaderClaimed || pageBack || hasPageActions) && "md:hidden"
2284
+ ),
2285
+ children: [
2286
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
2287
+ mobileBackToMenu && /* @__PURE__ */ jsxs(
2288
+ "button",
2289
+ {
2290
+ type: "button",
2291
+ onClick: mobileBackToMenu,
2292
+ className: "fz-ios-tint fz-tap -ml-1 inline-flex shrink-0 items-center gap-0.5 text-[15px] md:hidden",
2293
+ children: [
2294
+ /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" }),
2295
+ /* @__PURE__ */ jsx("span", { children: "Voltar" })
2296
+ ]
2297
+ }
2298
+ ),
2299
+ !pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack, primary: pagePrimary }),
2300
+ /* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
2301
+ /* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
2302
+ ] }),
2303
+ /* @__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)" } })
2304
+ ]
2305
+ }
2306
+ ),
2307
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 overflow-hidden", children: [
2308
+ /* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
2309
+ /* @__PURE__ */ jsx("div", { className: cn("min-w-0 min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", menuOpen && "invisible md:visible"), children })
2225
2310
  ] })
2226
2311
  ] }) }),
2227
2312
  /* @__PURE__ */ jsx(SaveBar, {}),
@@ -3337,13 +3422,26 @@ function HeaderTabs({
3337
3422
  }
3338
3423
  );
3339
3424
  }
3425
+ var PAGE_BAND_CLASS = "sticky top-0 z-20 -mx-3 -mt-3 mb-4 flex flex-nowrap items-end justify-between gap-3 border-b border-border px-3 pt-2 md:-mx-6 md:-mt-4 md:px-6 md:min-h-[2.5rem]";
3426
+ var PAGE_BAND_SURFACE = { background: "var(--page-surface, hsl(var(--background)))" };
3427
+ function PageBand({ children, actions, className }) {
3428
+ return /* @__PURE__ */ jsxs("div", { style: PAGE_BAND_SURFACE, className: cn(PAGE_BAND_CLASS, "items-center", className), children: [
3429
+ /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 items-center gap-3", children }),
3430
+ actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2 empty:hidden", children: actions })
3431
+ ] });
3432
+ }
3340
3433
  function PageTabs({ actions, className, ...props }) {
3341
3434
  return /* @__PURE__ */ jsxs(
3342
3435
  "div",
3343
3436
  {
3344
3437
  style: { background: "var(--page-surface, hsl(var(--background)))" },
3345
3438
  className: cn(
3346
- "sticky top-0 z-20 -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",
3439
+ // `flex-nowrap`: a faixa NÃO quebra mais. Quebrando, o slot da direita
3440
+ // (filtros, modos de ver) descia para uma segunda linha e a tela ganhava
3441
+ // um terceiro andar de chrome antes do conteúdo — e, pior, as abas
3442
+ // comiam a largura dele primeiro. Agora o slot da direita tem lugar
3443
+ // garantido e são as ABAS que rolam na horizontal quando não cabem.
3444
+ "sticky top-0 z-20 -mx-3 -mt-3 mb-4 flex flex-nowrap items-end justify-between gap-3 border-b border-border px-3 pt-2 md:-mx-6 md:-mt-4 md:px-6",
3347
3445
  // 48px no desktop, a mesma altura do subcabeçalho dos painéis do
3348
3446
  // assistente à direita (TasksFilterBar, NotebookPanel). Eram 34 aqui e
3349
3447
  // 48 lá: os dois fios que fecham as duas faixas paravam em alturas
@@ -3354,8 +3452,8 @@ function PageTabs({ actions, className, ...props }) {
3354
3452
  className
3355
3453
  ),
3356
3454
  children: [
3357
- /* @__PURE__ */ jsx(HeaderTabs, { ...props, className: "-mb-px" }),
3358
- actions && /* @__PURE__ */ jsx("div", { className: "ml-auto flex shrink-0 items-center gap-2 pb-1.5", children: actions })
3455
+ /* @__PURE__ */ jsx(HeaderTabs, { ...props, className: "-mb-px min-w-0 flex-1" }),
3456
+ actions && /* @__PURE__ */ jsx("div", { className: "-mt-1 flex shrink-0 items-center gap-2 pb-1 empty:hidden", children: actions })
3359
3457
  ]
3360
3458
  }
3361
3459
  );
@@ -3434,15 +3532,15 @@ function navigateToCrumb(crumb) {
3434
3532
  else window.location.hash = crumb.href;
3435
3533
  }
3436
3534
  function useCurrentRouteHref() {
3437
- const read = () => {
3535
+ const read2 = () => {
3438
3536
  if (typeof window === "undefined") return "";
3439
3537
  const hash = window.location.hash.replace(/^#/, "");
3440
3538
  const clean = hash.split("?")[0];
3441
3539
  return clean.startsWith("/") ? clean : `/${clean}`;
3442
3540
  };
3443
- const [href, setHref] = React11.useState(read);
3541
+ const [href, setHref] = React11.useState(read2);
3444
3542
  React11.useEffect(() => {
3445
- const handler = () => setHref(read());
3543
+ const handler = () => setHref(read2());
3446
3544
  window.addEventListener("hashchange", handler);
3447
3545
  handler();
3448
3546
  return () => window.removeEventListener("hashchange", handler);
@@ -3457,6 +3555,241 @@ function useTrailBack(href) {
3457
3555
  [parent]
3458
3556
  );
3459
3557
  }
3558
+ var COLUMN_WIDTH = 224;
3559
+ var COLUMN_COLLAPSED_WIDTH = 64;
3560
+ function read(key) {
3561
+ try {
3562
+ return localStorage.getItem(key) === "1";
3563
+ } catch {
3564
+ return false;
3565
+ }
3566
+ }
3567
+ function CollapsibleColumn({
3568
+ children,
3569
+ storageKey,
3570
+ footer,
3571
+ squeezed,
3572
+ autoCollapseKey,
3573
+ autoCollapseMs = 1800,
3574
+ onWidth,
3575
+ className,
3576
+ seamTop = 12,
3577
+ "aria-label": ariaLabel
3578
+ }) {
3579
+ const pinnedKey = `${storageKey}.pinned`;
3580
+ const [collapsed, setCollapsed] = React11.useState(() => read(storageKey));
3581
+ const [pinned, setPinned] = React11.useState(() => read(pinnedKey));
3582
+ const [hovering, setHovering] = React11.useState(false);
3583
+ const hoveringRef = React11.useRef(false);
3584
+ const collapsedRef = React11.useRef(collapsed);
3585
+ collapsedRef.current = collapsed;
3586
+ const autoRef = React11.useRef(false);
3587
+ const open = !collapsed || hovering;
3588
+ const width = open ? COLUMN_WIDTH : COLUMN_COLLAPSED_WIDTH;
3589
+ const setHover = (on) => {
3590
+ hoveringRef.current = on;
3591
+ setHovering(on);
3592
+ };
3593
+ React11.useEffect(() => {
3594
+ onWidth?.(width);
3595
+ }, [width, onWidth]);
3596
+ const setOpenState = React11.useCallback((nextCollapsed) => {
3597
+ setCollapsed(nextCollapsed);
3598
+ setPinned(!nextCollapsed);
3599
+ autoRef.current = false;
3600
+ try {
3601
+ localStorage.setItem(storageKey, nextCollapsed ? "1" : "0");
3602
+ localStorage.setItem(pinnedKey, nextCollapsed ? "0" : "1");
3603
+ } catch {
3604
+ }
3605
+ if (nextCollapsed) setHover(false);
3606
+ }, [storageKey, pinnedKey]);
3607
+ React11.useEffect(() => {
3608
+ if (squeezed === void 0) return;
3609
+ if (squeezed) {
3610
+ if (!collapsedRef.current) {
3611
+ autoRef.current = true;
3612
+ setCollapsed(true);
3613
+ }
3614
+ } else if (autoRef.current) {
3615
+ autoRef.current = false;
3616
+ setCollapsed(false);
3617
+ }
3618
+ }, [squeezed]);
3619
+ const seen = React11.useRef(null);
3620
+ React11.useEffect(() => {
3621
+ if (autoCollapseKey === void 0) return;
3622
+ if (seen.current === null) {
3623
+ seen.current = autoCollapseKey;
3624
+ return;
3625
+ }
3626
+ if (seen.current === autoCollapseKey) return;
3627
+ seen.current = autoCollapseKey;
3628
+ if (collapsed || pinned) return;
3629
+ const t = setTimeout(() => {
3630
+ if (!hoveringRef.current) setCollapsed(true);
3631
+ }, autoCollapseMs);
3632
+ return () => clearTimeout(t);
3633
+ }, [autoCollapseKey, collapsed, pinned, autoCollapseMs]);
3634
+ const scrollRef = React11.useRef(null);
3635
+ const [hasMore, setHasMore] = React11.useState(false);
3636
+ const onScroll = React11.useCallback(() => {
3637
+ const el = scrollRef.current;
3638
+ if (!el) return;
3639
+ setHasMore(el.scrollHeight - el.scrollTop - el.clientHeight > 4);
3640
+ }, []);
3641
+ React11.useEffect(() => {
3642
+ onScroll();
3643
+ const el = scrollRef.current;
3644
+ if (!el) return;
3645
+ const ro = new ResizeObserver(onScroll);
3646
+ ro.observe(el);
3647
+ return () => {
3648
+ ro.disconnect();
3649
+ };
3650
+ }, [onScroll, open, children]);
3651
+ const boxRef = React11.useRef(null);
3652
+ const panelRef = React11.useRef(null);
3653
+ const [seamed, setSeamed] = React11.useState(false);
3654
+ const armedRef = React11.useRef(false);
3655
+ const publish = React11.useCallback(() => {
3656
+ if (!seamTop) return;
3657
+ const el = panelRef.current;
3658
+ const card = el?.closest(".fz-page-surface");
3659
+ const band = card?.firstElementChild;
3660
+ if (!el || !card || !band) return;
3661
+ const root = document.documentElement;
3662
+ const flush = Math.abs(el.getBoundingClientRect().top - band.getBoundingClientRect().bottom) <= 2;
3663
+ setSeamed(flush);
3664
+ if (!flush) {
3665
+ root.style.removeProperty("--page-rail-seam");
3666
+ return;
3667
+ }
3668
+ const x = el.getBoundingClientRect().right - card.getBoundingClientRect().left;
3669
+ if (!armedRef.current) root.style.transition = "none";
3670
+ root.style.setProperty("--page-rail-seam", `${x + seamTop}px`);
3671
+ if (!armedRef.current) {
3672
+ armedRef.current = true;
3673
+ requestAnimationFrame(() => {
3674
+ root.style.transition = "--page-rail-seam 200ms";
3675
+ });
3676
+ }
3677
+ }, [seamTop]);
3678
+ React11.useEffect(() => {
3679
+ publish();
3680
+ }, [publish, width]);
3681
+ React11.useEffect(() => {
3682
+ if (!seamTop) return;
3683
+ const el = panelRef.current;
3684
+ const card = el?.closest(".fz-page-surface");
3685
+ if (!el || !card) return;
3686
+ const root = document.documentElement;
3687
+ const ro = new ResizeObserver(publish);
3688
+ ro.observe(el);
3689
+ ro.observe(card);
3690
+ return () => {
3691
+ ro.disconnect();
3692
+ armedRef.current = false;
3693
+ root.style.transition = "none";
3694
+ root.style.removeProperty("--page-rail-seam");
3695
+ requestAnimationFrame(() => {
3696
+ root.style.removeProperty("transition");
3697
+ });
3698
+ };
3699
+ }, [publish, seamTop]);
3700
+ return (
3701
+ // A caixa RESERVA a largura; a coluna mora dentro dela, posicionada. Sem
3702
+ // isso, reabrir no hover empurraria o conteúdo a cada passada do ponteiro.
3703
+ /* @__PURE__ */ jsxs(
3704
+ "div",
3705
+ {
3706
+ ref: boxRef,
3707
+ className: cn("relative hidden shrink-0 no-print transition-[width] duration-200 md:block", className),
3708
+ style: { width: collapsed ? COLUMN_COLLAPSED_WIDTH : COLUMN_WIDTH },
3709
+ children: [
3710
+ /* @__PURE__ */ jsxs(
3711
+ "nav",
3712
+ {
3713
+ ref: panelRef,
3714
+ "aria-label": ariaLabel,
3715
+ onMouseEnter: () => setHover(true),
3716
+ onMouseLeave: () => setHover(false),
3717
+ onDoubleClick: () => {
3718
+ if (collapsed) setOpenState(false);
3719
+ },
3720
+ style: { width },
3721
+ className: cn(
3722
+ // O vidro é uma classe compartilhada com a faixa do cabeçalho.
3723
+ "fz-liquid-glass absolute inset-y-0 left-0 flex flex-col overflow-hidden p-3 transition-[width] duration-200",
3724
+ collapsed && hovering && "z-30 shadow-xl"
3725
+ ),
3726
+ children: [
3727
+ /* @__PURE__ */ jsx(
3728
+ "span",
3729
+ {
3730
+ "aria-hidden": true,
3731
+ className: "pointer-events-none absolute right-0 w-px",
3732
+ style: { top: seamTop, bottom: 0, background: "hsl(var(--border))" }
3733
+ }
3734
+ ),
3735
+ /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1", children: [
3736
+ /* @__PURE__ */ jsx("div", { ref: scrollRef, onScroll, className: "scrollbar-none h-full overflow-y-auto overflow-x-hidden", children: children({ collapsed: !open }) }),
3737
+ /* @__PURE__ */ jsx(
3738
+ "div",
3739
+ {
3740
+ "aria-hidden": true,
3741
+ className: cn(
3742
+ "pointer-events-none absolute inset-x-0 bottom-0 h-8 transition-opacity duration-200",
3743
+ hasMore ? "opacity-100" : "opacity-0"
3744
+ ),
3745
+ style: { backgroundImage: "linear-gradient(to top, var(--page-surface, hsl(var(--background))), transparent)" }
3746
+ }
3747
+ )
3748
+ ] }),
3749
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex shrink-0 items-center gap-2 border-t border-border pt-2", children: [
3750
+ open && footer && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: footer }),
3751
+ /* @__PURE__ */ jsx(
3752
+ "button",
3753
+ {
3754
+ type: "button",
3755
+ onClick: () => setOpenState(!collapsed),
3756
+ "aria-label": collapsed ? "Expandir menu" : "Recolher menu",
3757
+ title: collapsed ? "Expandir menu" : "Recolher menu",
3758
+ "aria-expanded": !collapsed,
3759
+ className: "flex h-8 w-full items-center justify-end rounded-button bg-transparent px-2.5 text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground",
3760
+ children: collapsed ? /* @__PURE__ */ jsx(ChevronsRight, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronsLeft, { className: "h-4 w-4" })
3761
+ }
3762
+ )
3763
+ ] })
3764
+ ]
3765
+ }
3766
+ ),
3767
+ seamed && /* @__PURE__ */ jsx(
3768
+ "svg",
3769
+ {
3770
+ "aria-hidden": true,
3771
+ className: "pointer-events-none absolute top-0",
3772
+ style: { left: `${width - 1}px`, marginTop: "-1px", zIndex: 25, transition: "left 200ms" },
3773
+ width: seamTop,
3774
+ height: seamTop,
3775
+ viewBox: `0 0 ${seamTop} ${seamTop}`,
3776
+ fill: "none",
3777
+ children: /* @__PURE__ */ jsx(
3778
+ "path",
3779
+ {
3780
+ d: `M ${seamTop} 0.5 Q 0.5 0.5 0.5 ${seamTop}`,
3781
+ stroke: "hsl(var(--border))",
3782
+ strokeWidth: "1",
3783
+ fill: "none"
3784
+ }
3785
+ )
3786
+ }
3787
+ )
3788
+ ]
3789
+ }
3790
+ )
3791
+ );
3792
+ }
3460
3793
  function PageRail({ children }) {
3461
3794
  const slot = usePageRailSlot();
3462
3795
  if (slot) return createPortal(children, slot);
@@ -3479,21 +3812,11 @@ function ViewBarHost() {
3479
3812
  return /* @__PURE__ */ jsx(
3480
3813
  "div",
3481
3814
  {
3482
- style: { background: "var(--page-surface, hsl(var(--background)))", marginTop: "-1rem" },
3483
- className: cn(
3484
- "sticky top-[2.4rem] z-10 -mx-3 mb-3 border-b border-border px-3 py-1 md:top-[2.5rem] md:-mx-6 md:px-6",
3485
- !active && "hidden"
3486
- ),
3487
- children: /* @__PURE__ */ jsx(
3488
- "div",
3489
- {
3490
- ref: (el) => {
3491
- viewBarSlot = el;
3492
- viewBarNotify();
3493
- },
3494
- className: "flex min-h-7 items-center justify-between gap-2"
3495
- }
3496
- )
3815
+ ref: (el) => {
3816
+ viewBarSlot = el;
3817
+ viewBarNotify();
3818
+ },
3819
+ className: cn("flex items-center gap-2", !active && "hidden")
3497
3820
  }
3498
3821
  );
3499
3822
  }
@@ -3508,10 +3831,165 @@ function PageViewBar({ children }) {
3508
3831
  };
3509
3832
  }, []);
3510
3833
  if (slot) return createPortal(children, slot);
3511
- return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-between gap-2", children });
3834
+ return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
3835
+ }
3836
+ var SUB_NAV_STYLE = "nested";
3837
+ function RailSections({ sections, search, collapsed }) {
3838
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3839
+ search && !collapsed && /* @__PURE__ */ jsxs("div", { className: "relative mb-2", children: [
3840
+ /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-1 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
3841
+ /* @__PURE__ */ jsx(
3842
+ "input",
3843
+ {
3844
+ type: "search",
3845
+ value: search.value,
3846
+ onChange: (e) => search.onChange(e.target.value),
3847
+ placeholder: search.placeholder,
3848
+ "aria-label": search.placeholder,
3849
+ className: "h-8 w-full border-0 bg-transparent pl-7 pr-2 text-[13px] outline-none placeholder:text-muted-foreground focus:ring-0"
3850
+ }
3851
+ )
3852
+ ] }),
3853
+ sections.map((section, i) => /* @__PURE__ */ jsxs("div", { className: i > 0 ? "mt-3 border-t border-border pt-3" : void 0, children: [
3854
+ section.heading && !collapsed && /* @__PURE__ */ jsx("p", { className: "px-3 pb-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: section.heading }),
3855
+ /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: section.items.map((item) => /* @__PURE__ */ jsx(RailNavItem, { item, collapsed }, item.id)) })
3856
+ ] }, section.key ?? i))
3857
+ ] });
3858
+ }
3859
+ var RAIL_COLLAPSED_KEY = "fayz.subnav.collapsed";
3860
+ function RailColumn({ className, "aria-label": ariaLabel, sections, search, footer }) {
3861
+ const boxRef = React11.useRef(null);
3862
+ const [squeezed, setSqueezed] = React11.useState(void 0);
3863
+ React11.useEffect(() => {
3864
+ if (typeof window === "undefined") return;
3865
+ const row = boxRef.current?.parentElement?.parentElement;
3866
+ if (!row) return;
3867
+ const COLLAPSE_BELOW = 560;
3868
+ const RESTORE_ABOVE = 820;
3869
+ let timer;
3870
+ const decide = () => {
3871
+ const content = row.clientWidth - COLUMN_WIDTH;
3872
+ if (content < COLLAPSE_BELOW) setSqueezed(true);
3873
+ else if (content > RESTORE_ABOVE) setSqueezed(false);
3874
+ };
3875
+ const apply = () => {
3876
+ if (timer) clearTimeout(timer);
3877
+ timer = setTimeout(decide, 180);
3878
+ };
3879
+ apply();
3880
+ const ro = new ResizeObserver(apply);
3881
+ ro.observe(row);
3882
+ window.addEventListener("resize", apply);
3883
+ return () => {
3884
+ if (timer) clearTimeout(timer);
3885
+ ro.disconnect();
3886
+ window.removeEventListener("resize", apply);
3887
+ };
3888
+ }, []);
3889
+ const activeKey = sections.flatMap((sec) => sec.items).filter((i) => i.active).map((i) => i.id).join("|");
3890
+ return /* @__PURE__ */ jsx("div", { ref: boxRef, className: "contents", children: /* @__PURE__ */ jsx(
3891
+ CollapsibleColumn,
3892
+ {
3893
+ "aria-label": ariaLabel,
3894
+ storageKey: RAIL_COLLAPSED_KEY,
3895
+ className,
3896
+ footer,
3897
+ squeezed,
3898
+ autoCollapseKey: activeKey,
3899
+ children: ({ collapsed }) => /* @__PURE__ */ jsx(RailSections, { sections, search, collapsed })
3900
+ }
3901
+ ) });
3902
+ }
3903
+ var published = [];
3904
+ var subNavHosts = 0;
3905
+ var subNavListeners = /* @__PURE__ */ new Set();
3906
+ var subNavNotify = () => subNavListeners.forEach((l) => l());
3907
+ var subNavSubscribe = (l) => {
3908
+ subNavListeners.add(l);
3909
+ return () => {
3910
+ subNavListeners.delete(l);
3911
+ };
3912
+ };
3913
+ function publishSubNav(entry) {
3914
+ const i = published.findIndex((p) => p.id === entry.id);
3915
+ published = i < 0 ? [...published, entry] : published.map((p, n) => n === i ? entry : p);
3916
+ subNavNotify();
3917
+ }
3918
+ function unpublishSubNav(id) {
3919
+ published = published.filter((p) => p.id !== id);
3920
+ subNavNotify();
3921
+ }
3922
+ function useSubNavPublished() {
3923
+ return React11.useSyncExternalStore(subNavSubscribe, () => published, () => published);
3924
+ }
3925
+ function useSubNavHost() {
3926
+ React11.useEffect(() => {
3927
+ subNavHosts++;
3928
+ subNavNotify();
3929
+ return () => {
3930
+ subNavHosts--;
3931
+ subNavNotify();
3932
+ };
3933
+ }, []);
3934
+ }
3935
+ function useHasSubNavHost() {
3936
+ return React11.useSyncExternalStore(subNavSubscribe, () => subNavHosts > 0, () => false);
3937
+ }
3938
+ function SubNavBand({ sections, "aria-label": ariaLabel, className }) {
3939
+ const items = sections.flatMap((s) => s.items);
3940
+ const value = items.find((i) => i.active)?.id ?? items[0]?.id;
3941
+ if (!value) return null;
3942
+ return /* @__PURE__ */ jsx(
3943
+ ViewTabs,
3944
+ {
3945
+ className,
3946
+ "aria-label": ariaLabel,
3947
+ options: items.map((i) => ({ value: i.id, label: i.label })),
3948
+ value,
3949
+ onChange: (id) => items.find((i) => i.id === id)?.onClick?.()
3950
+ }
3951
+ );
3952
+ }
3953
+ function SubNavBandHost({ leading, label }) {
3954
+ useSubNavHost();
3955
+ const pubs = useSubNavPublished();
3956
+ const sections = leading ?? pubs.flatMap((p) => p.sections);
3957
+ if (sections.length === 0) return null;
3958
+ return /* @__PURE__ */ jsx(
3959
+ "div",
3960
+ {
3961
+ style: { marginTop: "0.5rem" },
3962
+ className: "flex min-w-0 items-center gap-3",
3963
+ children: /* @__PURE__ */ jsx(SubNavBand, { sections, "aria-label": leading ? label : pubs[0]?.label })
3964
+ }
3965
+ );
3966
+ }
3967
+ function PageSubNav({ items, sections, search, footer, style, "aria-label": ariaLabel }) {
3968
+ const id = React11.useId();
3969
+ const hasHost = useHasSubNavHost();
3970
+ const resolved = sections ?? (items ? [{ items }] : []);
3971
+ const ref = React11.useRef(resolved);
3972
+ ref.current = resolved;
3973
+ const signature = JSON.stringify(resolved.map((s) => [s.key, s.heading, s.items.map((i) => [i.id, i.label, i.icon, !!i.active, !!i.premium])]));
3974
+ React11.useEffect(() => {
3975
+ publishSubNav({ id, label: ariaLabel, sections: ref.current });
3976
+ return () => unpublishSubNav(id);
3977
+ }, [id, ariaLabel, signature]);
3978
+ if (resolved.every((s) => s.items.length === 0)) return null;
3979
+ if (hasHost) return null;
3980
+ const asRail = (style ?? SUB_NAV_STYLE) === "nested";
3981
+ if (!asRail) {
3982
+ return /* @__PURE__ */ jsx("div", { className: "mb-4 flex min-w-0 items-center gap-3", children: /* @__PURE__ */ jsx(SubNavBand, { sections: resolved, "aria-label": ariaLabel }) });
3983
+ }
3984
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3985
+ /* @__PURE__ */ jsx(PageRail, { children: /* @__PURE__ */ jsx(RailColumn, { sections: resolved, search, footer, "aria-label": ariaLabel }) }),
3986
+ /* @__PURE__ */ jsx("div", { className: "mb-4 flex min-w-0 items-center gap-3 md:hidden", children: /* @__PURE__ */ jsx(SubNavBand, { sections: resolved, "aria-label": ariaLabel }) })
3987
+ ] });
3512
3988
  }
3513
3989
  function PageHeaderActions({ children }) {
3514
- const slot = useModuleHeaderActionsSlot();
3990
+ const outlet = usePageActionsOutletSlot();
3991
+ const shellSlot = useModuleHeaderActionsSlot();
3992
+ const slot = outlet ?? shellSlot;
3515
3993
  useRegisterPageActions();
3516
3994
  if (slot) return createPortal(children, slot);
3517
3995
  return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
@@ -3528,8 +4006,11 @@ function ModuleLayoutProvider({ variant, children }) {
3528
4006
  function useModuleLayout() {
3529
4007
  return React11.useContext(ModuleLayoutContext);
3530
4008
  }
3531
- function RailNavItem({ item }) {
3532
- const [expanded, setExpanded] = React11.useState(true);
4009
+ function RailNavItem({ item, collapsed }) {
4010
+ const [expanded, setExpanded] = React11.useState(false);
4011
+ React11.useEffect(() => {
4012
+ if (collapsed) setExpanded(false);
4013
+ }, [collapsed]);
3533
4014
  const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
3534
4015
  const hasChildren = item.children && item.children.length > 0;
3535
4016
  const hasActiveChild = hasChildren && item.children.some((c) => c.active);
@@ -3540,36 +4021,53 @@ function RailNavItem({ item }) {
3540
4021
  {
3541
4022
  type: "button",
3542
4023
  "data-tour": `modnav.${item.id}`,
3543
- onClick: () => hasChildren ? setExpanded(!expanded) : item.onClick?.(),
4024
+ onClick: () => hasChildren && !collapsed ? setExpanded(!expanded) : item.onClick?.(),
4025
+ title: collapsed ? item.label : void 0,
3544
4026
  className: cn(
3545
- "flex items-center gap-2 w-full rounded-md px-2.5 py-1.5 text-sm transition-colors",
4027
+ // `px-2.5` NOS DOIS estados: centralizado, o ícone da tira ficava 2px
4028
+ // à direita do mesmo ícone com a coluna aberta, e todos eles davam um
4029
+ // pulinho lateral quando o ponteiro chegava. O recuo é o mesmo; o que
4030
+ // muda é só o rótulo aparecer.
4031
+ // ALTURA FIXA. Com `py-1.5` a linha media o que tinha dentro: 32px com
4032
+ // rótulo (a entrelinha do texto) e 28px só com o ícone — então cada
4033
+ // linha da tira caía num y diferente do da coluna aberta, e a lista
4034
+ // inteira escorregava quando o ponteiro chegava. `h-8` nos dois.
4035
+ "flex h-8 w-full items-center gap-2 rounded-md px-2.5 text-sm transition-colors",
3546
4036
  isActive ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
3547
4037
  ),
3548
4038
  children: [
3549
4039
  Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }),
3550
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-left truncate", children: item.label }),
3551
- item.premium && /* @__PURE__ */ jsx(PremiumBadge, { className: "ml-1" }),
3552
- 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" }))
4040
+ !collapsed && /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left", children: item.label }),
4041
+ !collapsed && item.premium && /* @__PURE__ */ jsx(PremiumBadge, { className: "ml-1" }),
4042
+ !collapsed && 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" }))
3553
4043
  ]
3554
4044
  }
3555
4045
  ),
3556
- 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(
3557
- "button",
3558
- {
3559
- type: "button",
3560
- "data-tour": `modnav.${child.id}`,
3561
- onClick: child.onClick,
3562
- className: cn(
3563
- "flex items-center w-full rounded-md px-2.5 py-1 text-xs transition-colors",
3564
- child.active ? "text-primary font-medium bg-primary/5" : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
3565
- ),
3566
- children: [
3567
- /* @__PURE__ */ jsx("span", { className: "flex-1 text-left truncate", children: child.label }),
3568
- child.premium && /* @__PURE__ */ jsx(PremiumBadge, { className: "ml-1" })
3569
- ]
3570
- },
3571
- child.id
3572
- )) })
4046
+ hasChildren && expanded && !collapsed && /* @__PURE__ */ jsx("div", { className: "relative mt-0.5 text-muted-foreground/45", children: item.children.map((child, i) => {
4047
+ const activeIndex = item.children.findIndex((c) => c.active);
4048
+ return /* @__PURE__ */ jsxs("div", { className: "relative h-8", children: [
4049
+ /* @__PURE__ */ jsx(NavChildConnector, { onPath: activeIndex >= 0 && i < activeIndex, active: !!child.active }),
4050
+ /* @__PURE__ */ jsxs(
4051
+ "button",
4052
+ {
4053
+ type: "button",
4054
+ "data-tour": `modnav.${child.id}`,
4055
+ onClick: child.onClick,
4056
+ className: cn(
4057
+ // `pl-11` passa da ponta da seta (~24px) com folga, para as
4058
+ // filhas lerem como UMA coluna indentada em vez de apertarem
4059
+ // a linha que aponta para elas.
4060
+ "flex h-8 w-full items-center rounded-button pl-11 pr-1 text-sm transition-colors",
4061
+ child.active ? "font-medium text-primary" : "text-muted-foreground hover:text-foreground"
4062
+ ),
4063
+ children: [
4064
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left", children: child.label }),
4065
+ child.premium && /* @__PURE__ */ jsx(PremiumBadge, { className: "ml-1" })
4066
+ ]
4067
+ }
4068
+ )
4069
+ ] }, child.id);
4070
+ }) })
3573
4071
  ] });
3574
4072
  }
3575
4073
  function defaultChildOf(item) {
@@ -3765,11 +4263,18 @@ function ShellSubpageHeader({
3765
4263
  /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) })
3766
4264
  ] });
3767
4265
  }
4266
+ function ModuleSubBand({ nav }) {
4267
+ useSubNavHost();
4268
+ const pubs = useSubNavPublished();
4269
+ const activeItem = nav.find((item) => item.active || item.children?.some((c) => c.active));
4270
+ const kids = (activeItem?.children ?? []).filter((c) => c.kind !== "action");
4271
+ kids.find((c) => c.active)?.id ?? kids[0]?.id;
4272
+ const own = [];
4273
+ const sections = own.length ? own : pubs.flatMap((p) => p.sections);
4274
+ if (sections.every((sec) => sec.items.length === 0)) return null;
4275
+ return /* @__PURE__ */ jsx(PageBand, { actions: /* @__PURE__ */ jsx(ViewBarHost, {}), children: /* @__PURE__ */ jsx(SubNavBand, { sections, "aria-label": own.length ? activeItem?.label : pubs[0]?.label }) });
4276
+ }
3768
4277
  function ModuleHeader({ nav }) {
3769
- function defaultChild(item) {
3770
- const kids2 = (item.children ?? []).filter((c) => c.kind !== "action");
3771
- return kids2.find((c) => /list/i.test(c.id) || /^list$/i.test(c.label)) ?? kids2[kids2.length - 1];
3772
- }
3773
4278
  const activeItem = nav.find((item) => item.active || item.children?.some((c) => c.active));
3774
4279
  const tabs = nav.map((item) => ({
3775
4280
  value: item.id,
@@ -3783,23 +4288,12 @@ function ModuleHeader({ nav }) {
3783
4288
  const item = nav.find((n) => n.id === id);
3784
4289
  if (!item) return;
3785
4290
  const hasChildren = item.children && item.children.length > 0;
3786
- if (hasChildren) defaultChild(item)?.onClick?.();
4291
+ if (hasChildren) defaultChildOf(item)?.onClick?.();
3787
4292
  else item.onClick?.();
3788
4293
  };
3789
4294
  const kids = (activeItem?.children ?? []).filter((c) => c.kind !== "action");
3790
4295
  const activeKid = kids.find((c) => c.active)?.id ?? kids[0]?.id;
3791
- const childControl = kids.length > 1 && activeKid ? /* @__PURE__ */ jsxs(PageViewBar, { children: [
3792
- /* @__PURE__ */ jsx(
3793
- ViewTabs,
3794
- {
3795
- "aria-label": activeItem?.label,
3796
- options: kids.map((c) => ({ value: c.id, label: c.label })),
3797
- value: activeKid,
3798
- onChange: (id) => kids.find((c) => c.id === id)?.onClick?.()
3799
- }
3800
- ),
3801
- /* @__PURE__ */ jsx("span", {})
3802
- ] }) : void 0;
4296
+ const childNav = kids.length > 1 && activeKid ? [{ items: kids.map((c) => ({ id: c.id, label: c.label, icon: c.icon, premium: c.premium, active: c.id === activeKid, onClick: c.onClick })) }] : void 0;
3803
4297
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3804
4298
  /* @__PURE__ */ jsx(
3805
4299
  PageTabs,
@@ -3807,17 +4301,39 @@ function ModuleHeader({ nav }) {
3807
4301
  tabs,
3808
4302
  value: activeItem?.id ?? nav[0]?.id ?? "",
3809
4303
  onChange,
3810
- anchorPrefix: "modnav"
4304
+ anchorPrefix: "modnav",
4305
+ actions: /* @__PURE__ */ jsx(ViewBarHost, {})
3811
4306
  }
3812
4307
  ),
3813
- childControl
4308
+ /* @__PURE__ */ jsx(SubNavBandHost, { leading: childNav, label: activeItem?.label })
3814
4309
  ] });
3815
4310
  }
3816
- function ModulePage({ title: _title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
4311
+ function ModulePage({ title: _title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction, navFilterPlaceholder }) {
3817
4312
  const ctxVariant = useModuleLayout();
3818
4313
  const variant = navVariant ?? ctxVariant;
4314
+ const [navQuery, setNavQuery] = React11.useState("");
4315
+ const filteredNav = React11.useMemo(() => {
4316
+ const q = navQuery.trim().toLowerCase();
4317
+ if (!q) return nav;
4318
+ const hit = (label) => label.toLowerCase().includes(q);
4319
+ return nav.flatMap((item) => {
4320
+ if (hit(item.label)) return [item];
4321
+ const kids = (item.children ?? []).filter((c) => hit(c.label));
4322
+ return kids.length ? [{ ...item, children: kids }] : [];
4323
+ });
4324
+ }, [nav, navQuery]);
3819
4325
  const transitionKey = viewKey ?? "view";
3820
4326
  const onSubpage = useHasPageHeaderBack();
4327
+ const activeLabel = React11.useMemo(() => {
4328
+ if (onSubpage) return null;
4329
+ for (const item of nav) {
4330
+ const kid = item.children?.find((c) => c.active);
4331
+ if (kid) return kid.label === item.label ? item.label : `${item.label} \u203A ${kid.label}`;
4332
+ if (item.active) return item.label;
4333
+ }
4334
+ return null;
4335
+ }, [nav, onSubpage]);
4336
+ usePublishModuleViewLabel(activeLabel);
3821
4337
  if (variant === "sidebar") {
3822
4338
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3823
4339
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
@@ -3827,24 +4343,28 @@ function ModulePage({ title: _title, subtitle, nav, children, className, headerA
3827
4343
  if (variant === "tabs") {
3828
4344
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3829
4345
  !onSubpage && /* @__PURE__ */ jsx(ModuleHeader, { nav }),
3830
- !onSubpage && /* @__PURE__ */ jsx(ViewBarHost, {}),
3831
4346
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
3832
4347
  /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
3833
4348
  ] });
3834
4349
  }
3835
4350
  return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0", className), children: [
3836
- /* @__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: [
3837
- /* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(RailNavItem, { item }, item.id)) }),
3838
- sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto pt-3", children: sidebarFooter })
3839
- ] }) }),
4351
+ /* @__PURE__ */ jsx(PageRail, { children: /* @__PURE__ */ jsx(
4352
+ RailColumn,
4353
+ {
4354
+ sections: [{ items: filteredNav }],
4355
+ search: navFilterPlaceholder ? { value: navQuery, onChange: setNavQuery, placeholder: navFilterPlaceholder } : void 0,
4356
+ footer: sidebarFooter
4357
+ }
4358
+ ) }),
3840
4359
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
3841
4360
  showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
3842
4361
  !onSubpage && /* @__PURE__ */ jsx(MobileTabs, { nav }),
4362
+ !onSubpage && /* @__PURE__ */ jsx(ModuleSubBand, { nav }),
3843
4363
  /* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
3844
4364
  ] })
3845
4365
  ] });
3846
4366
  }
3847
4367
 
3848
- export { AppShell, BackStyleProvider, BottomSheet, HeaderTabs, Hint, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeader, PageHeaderActions, PageHeaderCrumb, PageHeaderOutlet, PageRail, PageTabs, PageTitlePortal, PageTrail, PageTransition, PageViewBar, RailNavItem, SaveBar, SaveBarProvider, SheetCard, SheetRow, ShellSearch, Sidebar, SubpageHeader, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Topbar, ViewTabs, bottomNavReachesAll, isCreateRoute, navigateToCrumb, useBackHandler, useBackStyle, useCrossModuleBack, useCurrentRouteHref, useHasPageActions, useHasPageHeaderBack, useHasPageHeaderHost, useHasPageHeaderOutlet, useLayoutStore, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, usePageHeader, usePageHeaderClaimed, usePageHeaderHost, usePageTrail, usePageTrailParent, useRailRow, useRegisterPageActions, useSaveBar, useSaveBarVisible, useSheetClose, useSheetDrag, useTrailBack };
3849
- //# sourceMappingURL=chunk-VBYFN5ZL.js.map
3850
- //# sourceMappingURL=chunk-VBYFN5ZL.js.map
4368
+ export { AppShell, BackStyleProvider, BottomSheet, COLUMN_WIDTH, CollapsibleColumn, HeaderTabs, Hint, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeader, PageHeaderActions, PageHeaderCrumb, PageHeaderOutlet, PageRail, PageSubNav, PageTabs, PageTitlePortal, PageTrail, PageTransition, PageViewBar, RailColumn, RailNavItem, SUB_NAV_STYLE, SaveBar, SaveBarProvider, SheetCard, SheetRow, ShellSearch, Sidebar, SubpageHeader, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Topbar, ViewTabs, bottomNavReachesAll, isCreateRoute, navigateToCrumb, useBackHandler, useBackStyle, useCrossModuleBack, useCurrentRouteHref, useHasPageActions, useHasPageHeaderBack, useHasPageHeaderHost, useHasPageHeaderOutlet, useLayoutStore, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useModuleViewLabel, useNavTransition, usePageHeader, usePageHeaderClaimed, usePageHeaderHost, usePageTrail, usePageTrailParent, usePublishModuleViewLabel, useRailRow, useRegisterPageActions, useSaveBar, useSaveBarVisible, useSheetClose, useSheetDrag, useTrailBack };
4369
+ //# sourceMappingURL=chunk-33A7SN2N.js.map
4370
+ //# sourceMappingURL=chunk-33A7SN2N.js.map