@akira-io/ui 1.0.0 → 1.1.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 (70) hide show
  1. package/README.md +15 -10
  2. package/dist/blocks.d.ts +159 -6
  3. package/dist/blocks.js +1388 -29
  4. package/dist/blocks.js.map +1 -1
  5. package/dist/button-B6wQRe7p.d.ts +16 -0
  6. package/dist/chunk-5AJFQ6C4.js +35 -0
  7. package/dist/chunk-5AJFQ6C4.js.map +1 -0
  8. package/dist/chunk-6QM5BA7B.js +148 -0
  9. package/dist/chunk-6QM5BA7B.js.map +1 -0
  10. package/dist/chunk-6X4TKSF6.js +21 -0
  11. package/dist/chunk-6X4TKSF6.js.map +1 -0
  12. package/dist/chunk-DSD7MVLL.js +287 -0
  13. package/dist/chunk-DSD7MVLL.js.map +1 -0
  14. package/dist/chunk-IQMRJXEO.js +59 -0
  15. package/dist/chunk-IQMRJXEO.js.map +1 -0
  16. package/dist/chunk-JAHTUAQL.js +82 -0
  17. package/dist/chunk-JAHTUAQL.js.map +1 -0
  18. package/dist/{chunk-A2SIZOEA.js → chunk-KYWA5UYN.js} +125 -45
  19. package/dist/chunk-KYWA5UYN.js.map +1 -0
  20. package/dist/chunk-L2H5GIF7.js +22 -0
  21. package/dist/chunk-L2H5GIF7.js.map +1 -0
  22. package/dist/chunk-LKSM3WLK.js +44 -0
  23. package/dist/chunk-LKSM3WLK.js.map +1 -0
  24. package/dist/{chunk-5MQWJVM2.js → chunk-MNGNBEYN.js} +168 -200
  25. package/dist/chunk-MNGNBEYN.js.map +1 -0
  26. package/dist/chunk-SA5LM2GD.js +102 -0
  27. package/dist/chunk-SA5LM2GD.js.map +1 -0
  28. package/dist/chunk-UUEWFCKT.js +28 -0
  29. package/dist/chunk-UUEWFCKT.js.map +1 -0
  30. package/dist/{chunk-42BJBH56.js → chunk-UUTXPPRN.js} +521 -107
  31. package/dist/chunk-UUTXPPRN.js.map +1 -0
  32. package/dist/{chunk-UXL33CZ4.js → chunk-YOVY3KT6.js} +13 -11
  33. package/dist/chunk-YOVY3KT6.js.map +1 -0
  34. package/dist/code.d.ts +6 -0
  35. package/dist/code.js +651 -0
  36. package/dist/code.js.map +1 -0
  37. package/dist/context-VgHq4U98.d.ts +384 -0
  38. package/dist/editor.d.ts +126 -0
  39. package/dist/editor.js +436 -0
  40. package/dist/editor.js.map +1 -0
  41. package/dist/index.d.ts +93 -17
  42. package/dist/index.js +996 -472
  43. package/dist/index.js.map +1 -1
  44. package/dist/inertia.d.ts +47 -2
  45. package/dist/inertia.js +138 -7
  46. package/dist/inertia.js.map +1 -1
  47. package/dist/input-4uxAHW6y.d.ts +5 -0
  48. package/dist/json-viewer-BTHBACj9.d.ts +50 -0
  49. package/dist/{language-Bft6rBBt.d.ts → language-Dgjr9bm3.d.ts} +1 -1
  50. package/dist/locales/pt.d.ts +26 -4
  51. package/dist/locales/pt.js +127 -1
  52. package/dist/locales/pt.js.map +1 -1
  53. package/dist/password-input-r39y2pCT.d.ts +50 -0
  54. package/dist/shells.d.ts +45 -7
  55. package/dist/shells.js +95 -60
  56. package/dist/shells.js.map +1 -1
  57. package/dist/{sidebar-BDhG9SUU.d.ts → sidebar-BhOIlSjt.d.ts} +4 -12
  58. package/dist/{types-Cno1_Ijx.d.ts → types-B4QS3h5p.d.ts} +9 -1
  59. package/dist/types-CT0rBsFc.d.ts +110 -0
  60. package/package.json +170 -130
  61. package/theme.css +20 -0
  62. package/themes/nosferry.css +4 -0
  63. package/dist/chunk-42BJBH56.js.map +0 -1
  64. package/dist/chunk-5MQWJVM2.js.map +0 -1
  65. package/dist/chunk-A2SIZOEA.js.map +0 -1
  66. package/dist/chunk-POE7OYET.js +0 -96
  67. package/dist/chunk-POE7OYET.js.map +0 -1
  68. package/dist/chunk-UXL33CZ4.js.map +0 -1
  69. package/dist/date-range-filter-QEa8fhUM.d.ts +0 -149
  70. package/dist/types-cOieac8G.d.ts +0 -77
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-appearance.tsx"],"names":[],"mappings":";;;AAIA,IAAM,cAAc,MAAM;AACtB,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,OAAO,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA,CAAE,OAAA;AAC7D,CAAA;AAEA,IAAM,SAAA,GAAY,CAAC,IAAA,EAAc,KAAA,EAAe,OAAO,GAAA,KAAQ;AAC3D,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACjC,IAAA;AAAA,EACJ;AAEA,EAAA,MAAM,MAAA,GAAS,IAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA;AAChC,EAAA,QAAA,CAAS,SAAS,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,KAAK,mBAAmB,MAAM,CAAA,aAAA,CAAA;AAC/D,CAAA;AAEA,IAAM,UAAA,GAAa,CAAC,UAAA,KAA2B;AAC3C,EAAA,MAAM,MAAA,GACF,UAAA,KAAe,MAAA,IAAW,UAAA,KAAe,YAAY,WAAA,EAAY;AAErE,EAAA,QAAA,CAAS,eAAA,CAAgB,SAAA,CAAU,MAAA,CAAO,MAAA,EAAQ,MAAM,CAAA;AACxD,EAAA,QAAA,CAAS,eAAA,CAAgB,KAAA,CAAM,WAAA,GAAc,MAAA,GAAS,MAAA,GAAS,OAAA;AACnE,CAAA;AAEA,IAAM,aAAa,MAAM;AACrB,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,OAAO,IAAA;AAAA,EACX;AAEA,EAAA,OAAO,MAAA,CAAO,WAAW,8BAA8B,CAAA;AAC3D,CAAA;AAEA,IAAM,0BAA0B,MAAM;AAClC,EAAA,MAAM,iBAAA,GAAoB,YAAA,CAAa,OAAA,CAAQ,YAAY,CAAA;AAC3D,EAAA,UAAA,CAAW,qBAAqB,QAAQ,CAAA;AAC5C,CAAA;AAEO,SAAS,eAAA,GAAkB;AAC9B,EAAA,MAAM,eAAA,GACD,YAAA,CAAa,OAAA,CAAQ,YAAY,CAAA,IAAoB,QAAA;AAE1D,EAAA,UAAA,CAAW,eAAe,CAAA;AAE1B,EAAA,UAAA,EAAW,EAAG,gBAAA,CAAiB,QAAA,EAAU,uBAAuB,CAAA;AACpE;AAEO,SAAS,aAAA,GAAgB;AAC5B,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAqB,QAAQ,CAAA;AAEjE,EAAA,MAAM,gBAAA,GAAmB,WAAA,CAAY,CAAC,IAAA,KAAqB;AACvD,IAAA,aAAA,CAAc,IAAI,CAAA;AAElB,IAAA,YAAA,CAAa,OAAA,CAAQ,cAAc,IAAI,CAAA;AACvC,IAAA,SAAA,CAAU,cAAc,IAAI,CAAA;AAC5B,IAAA,UAAA,CAAW,IAAI,CAAA;AAAA,EACnB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,MAAM,kBAAkB,YAAA,CAAa,OAAA;AAAA,MACjC;AAAA,KACJ;AACA,IAAA,gBAAA,CAAiB,mBAAmB,QAAQ,CAAA;AAE5C,IAAA,MAAM,QAAQ,UAAA,EAAW;AAEzB,IAAA,KAAA,EAAO,gBAAA,CAAiB,UAAU,uBAAuB,CAAA;AAEzD,IAAA,OAAO,MACH,KAAA,EAAO,mBAAA,CAAoB,QAAA,EAAU,uBAAuB,CAAA;AAAA,EACpE,CAAA,EAAG,CAAC,gBAAgB,CAAC,CAAA;AAErB,EAAA,OAAO,EAAE,YAAY,gBAAA,EAAiB;AAC1C","file":"chunk-IQMRJXEO.js","sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\nexport type Appearance = 'light' | 'dark' | 'system';\n\nconst prefersDark = () => {\n if (typeof window === 'undefined') {\n return false;\n }\n\n return window.matchMedia('(prefers-color-scheme: dark)').matches;\n};\n\nconst setCookie = (name: string, value: string, days = 365) => {\n if (typeof document === 'undefined') {\n return;\n }\n\n const maxAge = days * 24 * 60 * 60;\n document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;\n};\n\nconst applyTheme = (appearance: Appearance) => {\n const isDark =\n appearance === 'dark' || (appearance === 'system' && prefersDark());\n\n document.documentElement.classList.toggle('dark', isDark);\n document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';\n};\n\nconst mediaQuery = () => {\n if (typeof window === 'undefined') {\n return null;\n }\n\n return window.matchMedia('(prefers-color-scheme: dark)');\n};\n\nconst handleSystemThemeChange = () => {\n const currentAppearance = localStorage.getItem('appearance') as Appearance;\n applyTheme(currentAppearance || 'system');\n};\n\nexport function initializeTheme() {\n const savedAppearance =\n (localStorage.getItem('appearance') as Appearance) || 'system';\n\n applyTheme(savedAppearance);\n\n mediaQuery()?.addEventListener('change', handleSystemThemeChange);\n}\n\nexport function useAppearance() {\n const [appearance, setAppearance] = useState<Appearance>('system');\n\n const updateAppearance = useCallback((mode: Appearance) => {\n setAppearance(mode);\n\n localStorage.setItem('appearance', mode);\n setCookie('appearance', mode);\n applyTheme(mode);\n }, []);\n\n useEffect(() => {\n const savedAppearance = localStorage.getItem(\n 'appearance',\n ) as Appearance | null;\n updateAppearance(savedAppearance || 'system');\n\n const query = mediaQuery();\n\n query?.addEventListener('change', handleSystemThemeChange);\n\n return () =>\n query?.removeEventListener('change', handleSystemThemeChange);\n }, [updateAppearance]);\n\n return { appearance, updateAppearance } as const;\n}\n"]}
@@ -0,0 +1,82 @@
1
+ import { useUiLabels } from './chunk-5AJFQ6C4.js';
2
+ import { Button } from './chunk-DSD7MVLL.js';
3
+ import { Check, Copy } from 'lucide-react';
4
+ import * as React from 'react';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ var copyButtonLabels = {
8
+ copyLabel: "Copy",
9
+ copiedLabel: "Copied"
10
+ };
11
+ async function writeToClipboard(value) {
12
+ const clipboard = globalThis.navigator?.clipboard;
13
+ if (typeof clipboard?.writeText !== "function") {
14
+ throw new Error("The clipboard is not available in this context.");
15
+ }
16
+ await clipboard.writeText(value);
17
+ }
18
+ function CopyButton({
19
+ value,
20
+ copyLabel,
21
+ copiedLabel,
22
+ acknowledgementDuration = 2e3,
23
+ onCopied,
24
+ onCopyFailed,
25
+ onClick,
26
+ variant = "ghost",
27
+ size = "icon-sm",
28
+ className,
29
+ ...props
30
+ }) {
31
+ const labels = useUiLabels("copyButton", copyButtonLabels, {
32
+ copyLabel,
33
+ copiedLabel
34
+ });
35
+ const [copied, setCopied] = React.useState(false);
36
+ const timer = React.useRef(void 0);
37
+ React.useEffect(() => () => clearTimeout(timer.current), []);
38
+ async function copy(event) {
39
+ onClick?.(event);
40
+ try {
41
+ await writeToClipboard(value);
42
+ } catch (reason) {
43
+ onCopyFailed?.(reason);
44
+ return;
45
+ }
46
+ setCopied(true);
47
+ onCopied?.(value);
48
+ clearTimeout(timer.current);
49
+ timer.current = setTimeout(
50
+ () => setCopied(false),
51
+ acknowledgementDuration
52
+ );
53
+ }
54
+ return /* @__PURE__ */ jsx(Button, { asChild: true, variant, size, className, children: /* @__PURE__ */ jsxs(
55
+ "button",
56
+ {
57
+ ...props,
58
+ type: "button",
59
+ "data-slot": "copy-button",
60
+ "data-copied": copied || void 0,
61
+ "aria-label": copied ? labels.copiedLabel : labels.copyLabel,
62
+ onClick: copy,
63
+ children: [
64
+ copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Copy, { "aria-hidden": "true" }),
65
+ /* @__PURE__ */ jsx(
66
+ "span",
67
+ {
68
+ "data-slot": "copy-button-status",
69
+ role: "status",
70
+ "aria-live": "polite",
71
+ className: "sr-only",
72
+ children: copied ? labels.copiedLabel : ""
73
+ }
74
+ )
75
+ ]
76
+ }
77
+ ) });
78
+ }
79
+
80
+ export { CopyButton, copyButtonLabels };
81
+ //# sourceMappingURL=chunk-JAHTUAQL.js.map
82
+ //# sourceMappingURL=chunk-JAHTUAQL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/ui/copy-button.tsx"],"names":[],"mappings":";;;;;;AAYO,IAAM,gBAAA,GAAqC;AAAA,EAC9C,SAAA,EAAW,MAAA;AAAA,EACX,WAAA,EAAa;AACjB;AAcA,eAAe,iBAAiB,KAAA,EAA8B;AAC1D,EAAA,MAAM,SAAA,GAAY,WAAW,SAAA,EAAW,SAAA;AAExC,EAAA,IAAI,OAAO,SAAA,EAAW,SAAA,KAAc,UAAA,EAAY;AAC5C,IAAA,MAAM,IAAI,MAAM,iDAAiD,CAAA;AAAA,EACrE;AAEA,EAAA,MAAM,SAAA,CAAU,UAAU,KAAK,CAAA;AACnC;AAEA,SAAS,UAAA,CAAW;AAAA,EAChB,KAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,uBAAA,GAA0B,GAAA;AAAA,EAC1B,QAAA;AAAA,EACA,YAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA,GAAU,OAAA;AAAA,EACV,IAAA,GAAO,SAAA;AAAA,EACP,SAAA;AAAA,EACA,GAAG;AACP,CAAA,EAAoB;AAChB,EAAA,MAAM,MAAA,GAAS,WAAA,CAAY,YAAA,EAAc,gBAAA,EAAkB;AAAA,IACvD,SAAA;AAAA,IACA;AAAA,GACH,CAAA;AACD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAU,eAAS,KAAK,CAAA;AAChD,EAAA,MAAM,KAAA,GAAc,aAAsC,MAAS,CAAA;AAEnE,EAAM,KAAA,CAAA,SAAA,CAAU,MAAM,MAAM,YAAA,CAAa,MAAM,OAAO,CAAA,EAAG,EAAE,CAAA;AAE3D,EAAA,eAAe,KAAK,KAAA,EAA4C;AAC5D,IAAA,OAAA,GAAU,KAAK,CAAA;AAEf,IAAA,IAAI;AACA,MAAA,MAAM,iBAAiB,KAAK,CAAA;AAAA,IAChC,SAAS,MAAA,EAAQ;AACb,MAAA,YAAA,GAAe,MAAM,CAAA;AAErB,MAAA;AAAA,IACJ;AAEA,IAAA,SAAA,CAAU,IAAI,CAAA;AACd,IAAA,QAAA,GAAW,KAAK,CAAA;AAEhB,IAAA,YAAA,CAAa,MAAM,OAAO,CAAA;AAC1B,IAAA,KAAA,CAAM,OAAA,GAAU,UAAA;AAAA,MACZ,MAAM,UAAU,KAAK,CAAA;AAAA,MACrB;AAAA,KACJ;AAAA,EACJ;AAEA,EAAA,2BACK,MAAA,EAAA,EAAO,OAAA,EAAO,IAAA,EAAC,OAAA,EAAkB,MAAY,SAAA,EAC1C,QAAA,kBAAA,IAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACI,GAAG,KAAA;AAAA,MACJ,IAAA,EAAK,QAAA;AAAA,MACL,WAAA,EAAU,aAAA;AAAA,MACV,eAAa,MAAA,IAAU,MAAA;AAAA,MACvB,YAAA,EAAY,MAAA,GAAS,MAAA,CAAO,WAAA,GAAc,MAAA,CAAO,SAAA;AAAA,MACjD,OAAA,EAAS,IAAA;AAAA,MAER,QAAA,EAAA;AAAA,QAAA,MAAA,mBACG,GAAA,CAAC,SAAM,aAAA,EAAY,MAAA,EAAO,oBAE1B,GAAA,CAAC,IAAA,EAAA,EAAK,eAAY,MAAA,EAAO,CAAA;AAAA,wBAE7B,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACG,WAAA,EAAU,oBAAA;AAAA,YACV,IAAA,EAAK,QAAA;AAAA,YACL,WAAA,EAAU,QAAA;AAAA,YACV,SAAA,EAAU,SAAA;AAAA,YAET,QAAA,EAAA,MAAA,GAAS,OAAO,WAAA,GAAc;AAAA;AAAA;AACnC;AAAA;AAAA,GACJ,EACJ,CAAA;AAER","file":"chunk-JAHTUAQL.js","sourcesContent":["import { type VariantProps } from 'class-variance-authority';\nimport { Check, Copy } from 'lucide-react';\nimport * as React from 'react';\n\nimport { Button, buttonVariants } from '@/components/ui/button';\nimport { useUiLabels } from '@/locales/context';\n\nexport interface CopyButtonLabels {\n copyLabel: string;\n copiedLabel: string;\n}\n\nexport const copyButtonLabels: CopyButtonLabels = {\n copyLabel: 'Copy',\n copiedLabel: 'Copied',\n};\n\ninterface CopyButtonProps\n extends\n Omit<React.ComponentProps<'button'>, 'value' | 'onCopy' | 'onError'>,\n VariantProps<typeof buttonVariants> {\n value: string;\n copyLabel?: CopyButtonLabels['copyLabel'];\n copiedLabel?: CopyButtonLabels['copiedLabel'];\n acknowledgementDuration?: number;\n onCopied?: (value: string) => void;\n onCopyFailed?: (reason: unknown) => void;\n}\n\nasync function writeToClipboard(value: string): Promise<void> {\n const clipboard = globalThis.navigator?.clipboard;\n\n if (typeof clipboard?.writeText !== 'function') {\n throw new Error('The clipboard is not available in this context.');\n }\n\n await clipboard.writeText(value);\n}\n\nfunction CopyButton({\n value,\n copyLabel,\n copiedLabel,\n acknowledgementDuration = 2000,\n onCopied,\n onCopyFailed,\n onClick,\n variant = 'ghost',\n size = 'icon-sm',\n className,\n ...props\n}: CopyButtonProps) {\n const labels = useUiLabels('copyButton', copyButtonLabels, {\n copyLabel,\n copiedLabel,\n });\n const [copied, setCopied] = React.useState(false);\n const timer = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n\n React.useEffect(() => () => clearTimeout(timer.current), []);\n\n async function copy(event: React.MouseEvent<HTMLButtonElement>) {\n onClick?.(event);\n\n try {\n await writeToClipboard(value);\n } catch (reason) {\n onCopyFailed?.(reason);\n\n return;\n }\n\n setCopied(true);\n onCopied?.(value);\n\n clearTimeout(timer.current);\n timer.current = setTimeout(\n () => setCopied(false),\n acknowledgementDuration,\n );\n }\n\n return (\n <Button asChild variant={variant} size={size} className={className}>\n <button\n {...props}\n type=\"button\"\n data-slot=\"copy-button\"\n data-copied={copied || undefined}\n aria-label={copied ? labels.copiedLabel : labels.copyLabel}\n onClick={copy}\n >\n {copied ? (\n <Check aria-hidden=\"true\" />\n ) : (\n <Copy aria-hidden=\"true\" />\n )}\n <span\n data-slot=\"copy-button-status\"\n role=\"status\"\n aria-live=\"polite\"\n className=\"sr-only\"\n >\n {copied ? labels.copiedLabel : ''}\n </span>\n </button>\n </Button>\n );\n}\n\nexport { CopyButton, type CopyButtonProps };\n"]}
@@ -1,23 +1,85 @@
1
- import { SidebarGroup, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton, Icon, SidebarGroupLabel, Collapsible, CollapsibleTrigger, CollapsibleContent, Avatar, AvatarImage, AvatarFallback, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem, useSidebar, useIsMobile, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, Sidebar, SidebarHeader, SidebarContent, SidebarFooter, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbPage, BreadcrumbLink, BreadcrumbSeparator, SidebarTrigger, Separator } from './chunk-5MQWJVM2.js';
2
- import { cn, Button } from './chunk-POE7OYET.js';
3
- import { useCallback, useState, Fragment as Fragment$1, useEffect, createElement } from 'react';
1
+ import { SidebarGroup, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton, Icon, SidebarGroupLabel, Collapsible, CollapsibleTrigger, CollapsibleContent, Avatar, AvatarImage, AvatarFallback, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem, useSidebar, useIsMobile, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, Sidebar, SidebarHeader, SidebarContent, SidebarFooter, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbPage, BreadcrumbLink, BreadcrumbSeparator, SidebarTrigger } from './chunk-MNGNBEYN.js';
2
+ import { hrefToString, resolveLink } from './chunk-L2H5GIF7.js';
3
+ import { Separator } from './chunk-UUEWFCKT.js';
4
+ import { cn, Button } from './chunk-DSD7MVLL.js';
5
+ import { useState, useEffect, useCallback, Fragment as Fragment$1 } from 'react';
4
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
7
  import { ChevronDown, Settings, LogOut, ChevronsUpDown, Search } from 'lucide-react';
6
8
 
7
- function hrefToString(href) {
8
- return typeof href === "string" ? href : href.url;
9
+ var SIDEBAR_COLLAPSED_GROUPS_KEY = "akira-ui:collapsed-nav-groups";
10
+ function readCollapsedGroups() {
11
+ if (typeof window === "undefined") {
12
+ return null;
13
+ }
14
+ try {
15
+ const raw = window.localStorage.getItem(SIDEBAR_COLLAPSED_GROUPS_KEY);
16
+ if (raw === null) {
17
+ return null;
18
+ }
19
+ const parsed = JSON.parse(raw);
20
+ return Array.isArray(parsed) ? parsed.filter(
21
+ (entry) => typeof entry === "string"
22
+ ) : null;
23
+ } catch {
24
+ return null;
25
+ }
9
26
  }
10
- var DefaultLink = ({
11
- href,
12
- children,
13
- prefetch: _prefetch,
14
- as: _as,
15
- ...props
16
- }) => {
17
- return createElement("a", { href: hrefToString(href), ...props }, children);
18
- };
19
- function resolveLink(linkComponent) {
20
- return linkComponent ?? DefaultLink;
27
+ function writeCollapsedGroups(groups) {
28
+ if (typeof window === "undefined") {
29
+ return;
30
+ }
31
+ try {
32
+ window.localStorage.setItem(
33
+ SIDEBAR_COLLAPSED_GROUPS_KEY,
34
+ JSON.stringify(groups)
35
+ );
36
+ } catch {
37
+ return;
38
+ }
39
+ }
40
+ function withGroup(groups, group, collapsed) {
41
+ if (!collapsed) {
42
+ return groups.filter((entry) => entry !== group);
43
+ }
44
+ return groups.includes(group) ? groups : [...groups, group];
45
+ }
46
+ function useCollapsedGroup({
47
+ group,
48
+ defaultOpen = true,
49
+ collapsedGroups,
50
+ onCollapsedChange
51
+ }) {
52
+ const isControlled = collapsedGroups !== void 0 && onCollapsedChange !== void 0;
53
+ const [storedGroups, setStoredGroups] = useState(
54
+ () => defaultOpen ? [] : [group]
55
+ );
56
+ useEffect(() => {
57
+ if (isControlled) {
58
+ return;
59
+ }
60
+ const persisted = readCollapsedGroups();
61
+ if (persisted !== null) {
62
+ setStoredGroups(persisted);
63
+ }
64
+ }, [isControlled]);
65
+ const setOpen = useCallback(
66
+ (open) => {
67
+ if (isControlled) {
68
+ onCollapsedChange(withGroup(collapsedGroups, group, !open));
69
+ return;
70
+ }
71
+ const next = withGroup(
72
+ readCollapsedGroups() ?? storedGroups,
73
+ group,
74
+ !open
75
+ );
76
+ setStoredGroups(next);
77
+ writeCollapsedGroups(next);
78
+ },
79
+ [collapsedGroups, group, isControlled, onCollapsedChange, storedGroups]
80
+ );
81
+ const source = isControlled ? collapsedGroups : storedGroups;
82
+ return { open: !source.includes(group), setOpen };
21
83
  }
22
84
  function useInitials() {
23
85
  return useCallback((fullName) => {
@@ -67,21 +129,41 @@ function NavFooter({
67
129
  }
68
130
  );
69
131
  }
132
+ function isItemActive(item, currentUrl) {
133
+ if (item.isActive !== void 0) {
134
+ return item.isActive;
135
+ }
136
+ if (!currentUrl) {
137
+ return false;
138
+ }
139
+ return currentUrl.startsWith(hrefToString(item.href));
140
+ }
70
141
  function NavMain({
71
142
  items = [],
72
143
  label = "Platform",
73
144
  currentUrl = "",
74
145
  linkComponent,
75
146
  collapsible = false,
76
- defaultOpen = true
147
+ defaultOpen = true,
148
+ collapsedGroups,
149
+ onCollapsedChange
77
150
  }) {
78
151
  const Link = resolveLink(linkComponent);
79
- const [open, setOpen] = useState(defaultOpen);
152
+ const holdsCurrentRoute = items.some(
153
+ (item) => isItemActive(item, currentUrl)
154
+ );
155
+ const { open, setOpen } = useCollapsedGroup({
156
+ group: label,
157
+ defaultOpen,
158
+ collapsedGroups,
159
+ onCollapsedChange
160
+ });
161
+ const expanded = open || holdsCurrentRoute;
80
162
  const menu = /* @__PURE__ */ jsx(SidebarMenu, { children: items.map((item) => /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(
81
163
  SidebarMenuButton,
82
164
  {
83
165
  asChild: true,
84
- isActive: item.isActive ?? (currentUrl ? currentUrl.startsWith(hrefToString(item.href)) : false),
166
+ isActive: isItemActive(item, currentUrl),
85
167
  tooltip: { children: item.title },
86
168
  children: /* @__PURE__ */ jsxs(Link, { href: item.href, prefetch: true, children: [
87
169
  item.icon && /* @__PURE__ */ jsx(item.icon, {}),
@@ -95,29 +177,21 @@ function NavMain({
95
177
  menu
96
178
  ] });
97
179
  }
98
- return /* @__PURE__ */ jsx(SidebarGroup, { className: "px-2 py-0", children: /* @__PURE__ */ jsxs(
99
- Collapsible,
100
- {
101
- open,
102
- onOpenChange: setOpen,
103
- className: "p-0 rounded-none border-0 bg-transparent shadow-none ring-0 backdrop-blur-none",
104
- children: [
105
- /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(SidebarGroupLabel, { className: "w-full cursor-pointer justify-between", children: [
106
- label,
107
- /* @__PURE__ */ jsx(
108
- ChevronDown,
109
- {
110
- className: cn(
111
- "size-4 transition-transform",
112
- !open && "-rotate-90"
113
- )
114
- }
180
+ return /* @__PURE__ */ jsx(SidebarGroup, { className: "px-2 py-0", children: /* @__PURE__ */ jsxs(Collapsible, { open: expanded, onOpenChange: setOpen, children: [
181
+ /* @__PURE__ */ jsx(CollapsibleTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(SidebarGroupLabel, { className: "w-full cursor-pointer justify-between", children: [
182
+ label,
183
+ /* @__PURE__ */ jsx(
184
+ ChevronDown,
185
+ {
186
+ className: cn(
187
+ "size-4 transition-transform",
188
+ !expanded && "-rotate-90"
115
189
  )
116
- ] }) }),
117
- /* @__PURE__ */ jsx(CollapsibleContent, { children: menu })
118
- ]
119
- }
120
- ) });
190
+ }
191
+ )
192
+ ] }) }),
193
+ /* @__PURE__ */ jsx(CollapsibleContent, { children: menu })
194
+ ] }) });
121
195
  }
122
196
  function UserInfo({
123
197
  user,
@@ -247,6 +321,9 @@ function AppSidebar({
247
321
  logoutHref,
248
322
  currentUrl = "",
249
323
  linkComponent,
324
+ collapsibleGroups = false,
325
+ collapsedGroups,
326
+ onCollapsedChange,
250
327
  onSettingsClick,
251
328
  onLogout
252
329
  }) {
@@ -267,7 +344,10 @@ function AppSidebar({
267
344
  items: group.items,
268
345
  label: group.label ?? "",
269
346
  currentUrl,
270
- linkComponent
347
+ linkComponent,
348
+ collapsible: collapsibleGroups,
349
+ collapsedGroups,
350
+ onCollapsedChange
271
351
  },
272
352
  group.label ?? index
273
353
  )) }),
@@ -408,6 +488,6 @@ function SettingsLayout({
408
488
  ] });
409
489
  }
410
490
 
411
- export { AppSidebar, AppSidebarHeader, Breadcrumbs, Heading, NavFooter, NavMain, NavUser, SettingsLayout, UserInfo, UserMenuContent, hrefToString, useInitials };
412
- //# sourceMappingURL=chunk-A2SIZOEA.js.map
413
- //# sourceMappingURL=chunk-A2SIZOEA.js.map
491
+ export { AppSidebar, AppSidebarHeader, Breadcrumbs, Heading, NavFooter, NavMain, NavUser, SIDEBAR_COLLAPSED_GROUPS_KEY, SettingsLayout, UserInfo, UserMenuContent, useCollapsedGroup, useInitials };
492
+ //# sourceMappingURL=chunk-KYWA5UYN.js.map
493
+ //# sourceMappingURL=chunk-KYWA5UYN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-collapsed-groups.tsx","../src/hooks/use-initials.tsx","../src/shells/nav-footer.tsx","../src/shells/nav-main.tsx","../src/shells/user-info.tsx","../src/shells/user-menu-content.tsx","../src/shells/nav-user.tsx","../src/shells/app-sidebar.tsx","../src/shells/breadcrumbs.tsx","../src/shells/app-sidebar-header.tsx","../src/shells/heading.tsx","../src/shells/settings-layout.tsx"],"names":["useCallback","jsx","jsxs","Fragment","useState","useEffect"],"mappings":"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","file":"chunk-KYWA5UYN.js","sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\nexport const SIDEBAR_COLLAPSED_GROUPS_KEY = 'akira-ui:collapsed-nav-groups';\n\nfunction readCollapsedGroups(): string[] | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n try {\n const raw = window.localStorage.getItem(SIDEBAR_COLLAPSED_GROUPS_KEY);\n\n if (raw === null) {\n return null;\n }\n\n const parsed: unknown = JSON.parse(raw);\n\n return Array.isArray(parsed)\n ? parsed.filter(\n (entry): entry is string => typeof entry === 'string',\n )\n : null;\n } catch {\n return null;\n }\n}\n\nfunction writeCollapsedGroups(groups: string[]): void {\n if (typeof window === 'undefined') {\n return;\n }\n\n try {\n window.localStorage.setItem(\n SIDEBAR_COLLAPSED_GROUPS_KEY,\n JSON.stringify(groups),\n );\n } catch {\n return;\n }\n}\n\nfunction withGroup(groups: string[], group: string, collapsed: boolean) {\n if (!collapsed) {\n return groups.filter((entry) => entry !== group);\n }\n\n return groups.includes(group) ? groups : [...groups, group];\n}\n\nexport interface CollapsedGroupsOptions {\n group: string;\n defaultOpen?: boolean;\n collapsedGroups?: string[];\n onCollapsedChange?: (collapsedGroups: string[]) => void;\n}\n\nexport function useCollapsedGroup({\n group,\n defaultOpen = true,\n collapsedGroups,\n onCollapsedChange,\n}: CollapsedGroupsOptions) {\n const isControlled =\n collapsedGroups !== undefined && onCollapsedChange !== undefined;\n\n const [storedGroups, setStoredGroups] = useState<string[]>(() =>\n defaultOpen ? [] : [group],\n );\n\n useEffect(() => {\n if (isControlled) {\n return;\n }\n\n const persisted = readCollapsedGroups();\n\n if (persisted !== null) {\n setStoredGroups(persisted);\n }\n }, [isControlled]);\n\n const setOpen = useCallback(\n (open: boolean) => {\n if (isControlled) {\n onCollapsedChange(withGroup(collapsedGroups, group, !open));\n\n return;\n }\n\n const next = withGroup(\n readCollapsedGroups() ?? storedGroups,\n group,\n !open,\n );\n\n setStoredGroups(next);\n writeCollapsedGroups(next);\n },\n [collapsedGroups, group, isControlled, onCollapsedChange, storedGroups],\n );\n\n const source = isControlled ? collapsedGroups : storedGroups;\n\n return { open: !source.includes(group), setOpen };\n}\n","import { useCallback } from 'react';\n\nexport function useInitials() {\n return useCallback((fullName: string): string => {\n const names = fullName.trim().split(' ');\n\n if (names.length === 0) return '';\n if (names.length === 1) return names[0].charAt(0).toUpperCase();\n\n const firstInitial = names[0].charAt(0);\n const lastInitial = names[names.length - 1].charAt(0);\n\n return `${firstInitial}${lastInitial}`.toUpperCase();\n }, []);\n}\n","import { type ComponentPropsWithoutRef } from 'react';\n\nimport { Icon } from '@/components/ui/icon';\nimport {\n SidebarGroup,\n SidebarGroupContent,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport { hrefToString } from '@/lib/href';\nimport type { NavItem } from '@/types';\n\nexport function NavFooter({\n items,\n className,\n ...props\n}: ComponentPropsWithoutRef<typeof SidebarGroup> & {\n items: NavItem[];\n}) {\n return (\n <SidebarGroup\n {...props}\n className={`group-data-[collapsible=icon]:p-0 ${className || ''}`}\n >\n <SidebarGroupContent>\n <SidebarMenu>\n {items.map((item) => (\n <SidebarMenuItem key={item.title}>\n <SidebarMenuButton\n asChild\n className=\"text-muted-foreground hover:text-foreground\"\n >\n <a\n href={hrefToString(item.href)}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {item.icon && (\n <Icon\n iconNode={item.icon}\n className=\"h-5 w-5\"\n />\n )}\n <span>{item.title}</span>\n </a>\n </SidebarMenuButton>\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n </SidebarGroupContent>\n </SidebarGroup>\n );\n}\n","import { ChevronDown } from 'lucide-react';\n\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from '@/components/ui/collapsible';\nimport {\n SidebarGroup,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport { useCollapsedGroup } from '@/hooks/use-collapsed-groups';\nimport { hrefToString, resolveLink } from '@/lib/href';\nimport { cn } from '@/lib/utils';\nimport type { LinkComponent, NavItem } from '@/types';\n\nfunction isItemActive(item: NavItem, currentUrl: string): boolean {\n if (item.isActive !== undefined) {\n return item.isActive;\n }\n\n if (!currentUrl) {\n return false;\n }\n\n return currentUrl.startsWith(hrefToString(item.href));\n}\n\nexport interface NavMainProps {\n items: NavItem[];\n label?: string;\n currentUrl?: string;\n linkComponent?: LinkComponent;\n collapsible?: boolean;\n defaultOpen?: boolean;\n collapsedGroups?: string[];\n onCollapsedChange?: (collapsedGroups: string[]) => void;\n}\n\nexport function NavMain({\n items = [],\n label = 'Platform',\n currentUrl = '',\n linkComponent,\n collapsible = false,\n defaultOpen = true,\n collapsedGroups,\n onCollapsedChange,\n}: NavMainProps) {\n const Link = resolveLink(linkComponent);\n const holdsCurrentRoute = items.some((item) =>\n isItemActive(item, currentUrl),\n );\n const { open, setOpen } = useCollapsedGroup({\n group: label,\n defaultOpen,\n collapsedGroups,\n onCollapsedChange,\n });\n const expanded = open || holdsCurrentRoute;\n\n const menu = (\n <SidebarMenu>\n {items.map((item) => (\n <SidebarMenuItem key={item.title}>\n <SidebarMenuButton\n asChild\n isActive={isItemActive(item, currentUrl)}\n tooltip={{ children: item.title }}\n >\n <Link href={item.href} prefetch>\n {item.icon && <item.icon />}\n <span>{item.title}</span>\n </Link>\n </SidebarMenuButton>\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n );\n\n if (!collapsible || !label) {\n return (\n <SidebarGroup className=\"px-2 py-0\">\n {label && <SidebarGroupLabel>{label}</SidebarGroupLabel>}\n {menu}\n </SidebarGroup>\n );\n }\n\n return (\n <SidebarGroup className=\"px-2 py-0\">\n <Collapsible open={expanded} onOpenChange={setOpen}>\n <CollapsibleTrigger asChild>\n <SidebarGroupLabel className=\"w-full cursor-pointer justify-between\">\n {label}\n <ChevronDown\n className={cn(\n 'size-4 transition-transform',\n !expanded && '-rotate-90',\n )}\n />\n </SidebarGroupLabel>\n </CollapsibleTrigger>\n <CollapsibleContent>{menu}</CollapsibleContent>\n </Collapsible>\n </SidebarGroup>\n );\n}\n","import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { useInitials } from '@/hooks/use-initials';\nimport type { SharedUser } from '@/types';\n\nexport function UserInfo({\n user,\n showEmail = false,\n}: {\n user: SharedUser;\n showEmail?: boolean;\n}) {\n const getInitials = useInitials();\n\n return (\n <>\n <Avatar className=\"h-8 w-8 overflow-hidden rounded-full\">\n <AvatarImage src={user.avatar} alt={user.name} />\n <AvatarFallback className=\"rounded-xl bg-muted text-foreground\">\n {getInitials(user.name)}\n </AvatarFallback>\n </Avatar>\n <div className=\"text-sm leading-tight grid flex-1 text-left\">\n <span className=\"font-medium truncate\">{user.name}</span>\n {showEmail && (\n <span className=\"text-xs truncate text-muted-foreground\">\n {user.email}\n </span>\n )}\n </div>\n </>\n );\n}\n","import { LogOut, Settings } from 'lucide-react';\n\nimport {\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from '@/components/ui/dropdown-menu';\nimport { resolveLink } from '@/lib/href';\nimport type { LinkComponent, SharedUser, UrlLike } from '@/types';\nimport { UserInfo } from './user-info';\n\ninterface UserMenuContentProps {\n user: SharedUser;\n settingsHref: UrlLike;\n logoutHref: UrlLike;\n linkComponent?: LinkComponent;\n onSettingsClick?: () => void;\n onLogout?: () => void;\n settingsLabel?: string;\n logoutLabel?: string;\n}\n\nexport function UserMenuContent({\n user,\n settingsHref,\n logoutHref,\n linkComponent,\n onSettingsClick,\n onLogout,\n settingsLabel = 'Settings',\n logoutLabel = 'Log out',\n}: UserMenuContentProps) {\n const Link = resolveLink(linkComponent);\n\n return (\n <>\n <DropdownMenuLabel className=\"p-0 font-normal\">\n <div className=\"gap-2 px-1 py-1.5 text-sm flex items-center text-left\">\n <UserInfo user={user} showEmail={true} />\n </div>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuGroup>\n <DropdownMenuItem asChild>\n <Link\n className=\"block w-full\"\n href={settingsHref}\n as=\"button\"\n prefetch\n onClick={onSettingsClick}\n >\n <Settings className=\"mr-2\" />\n {settingsLabel}\n </Link>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n <DropdownMenuSeparator />\n <DropdownMenuItem asChild>\n <Link\n className=\"block w-full\"\n href={logoutHref}\n as=\"button\"\n onClick={onLogout}\n data-test=\"logout-button\"\n >\n <LogOut className=\"mr-2\" />\n {logoutLabel}\n </Link>\n </DropdownMenuItem>\n </>\n );\n}\n","import { ChevronsUpDown } from 'lucide-react';\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport {\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n useSidebar,\n} from '@/components/ui/sidebar';\nimport { useIsMobile } from '@/hooks/use-mobile';\nimport type { LinkComponent, SharedUser, UrlLike } from '@/types';\nimport { UserInfo } from './user-info';\nimport { UserMenuContent } from './user-menu-content';\n\ninterface NavUserProps {\n user: SharedUser;\n settingsHref: UrlLike;\n logoutHref: UrlLike;\n linkComponent?: LinkComponent;\n onSettingsClick?: () => void;\n onLogout?: () => void;\n}\n\nexport function NavUser({\n user,\n settingsHref,\n logoutHref,\n linkComponent,\n onSettingsClick,\n onLogout,\n}: NavUserProps) {\n const { state } = useSidebar();\n const isMobile = useIsMobile();\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <SidebarMenuButton\n size=\"lg\"\n className=\"group text-sidebar-accent-foreground data-[state=open]:bg-sidebar-accent\"\n data-test=\"sidebar-menu-button\"\n >\n <UserInfo user={user} />\n <ChevronsUpDown className=\"size-4 ml-auto\" />\n </SidebarMenuButton>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n className=\"min-w-56 rounded-xl w-(--radix-dropdown-menu-trigger-width)\"\n align=\"end\"\n side={\n isMobile\n ? 'bottom'\n : state === 'collapsed'\n ? 'left'\n : 'bottom'\n }\n >\n <UserMenuContent\n user={user}\n settingsHref={settingsHref}\n logoutHref={logoutHref}\n linkComponent={linkComponent}\n onSettingsClick={onSettingsClick}\n onLogout={onLogout}\n />\n </DropdownMenuContent>\n </DropdownMenu>\n </SidebarMenuItem>\n </SidebarMenu>\n );\n}\n","import { type ReactNode } from 'react';\n\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n} from '@/components/ui/sidebar';\nimport { hrefToString, resolveLink } from '@/lib/href';\nimport type {\n LinkComponent,\n NavGroup,\n NavItem,\n SharedUser,\n UrlLike,\n} from '@/types';\nimport { NavFooter } from './nav-footer';\nimport { NavMain } from './nav-main';\nimport { NavUser } from './nav-user';\n\nfunction mostSpecificActiveHref(\n groups: NavGroup[],\n currentUrl: string,\n): string {\n let best = '';\n for (const group of groups) {\n for (const item of group.items) {\n const href = hrefToString(item.href);\n const matches =\n currentUrl === href || currentUrl.startsWith(`${href}/`);\n if (matches && href.length > best.length) {\n best = href;\n }\n }\n }\n return best;\n}\n\ninterface AppSidebarProps {\n logo: ReactNode;\n logoHref: UrlLike;\n groups: NavGroup[];\n footerItems?: NavItem[];\n user: SharedUser;\n settingsHref: UrlLike;\n logoutHref: UrlLike;\n currentUrl?: string;\n linkComponent?: LinkComponent;\n collapsibleGroups?: boolean;\n collapsedGroups?: string[];\n onCollapsedChange?: (collapsedGroups: string[]) => void;\n onSettingsClick?: () => void;\n onLogout?: () => void;\n}\n\nexport function AppSidebar({\n logo,\n logoHref,\n groups,\n footerItems = [],\n user,\n settingsHref,\n logoutHref,\n currentUrl = '',\n linkComponent,\n collapsibleGroups = false,\n collapsedGroups,\n onCollapsedChange,\n onSettingsClick,\n onLogout,\n}: AppSidebarProps) {\n const Link = resolveLink(linkComponent);\n const activeHref = mostSpecificActiveHref(groups, currentUrl);\n const resolvedGroups = groups.map((group) => ({\n ...group,\n items: group.items.map((item) => ({\n ...item,\n isActive: hrefToString(item.href) === activeHref,\n })),\n }));\n\n return (\n <Sidebar collapsible=\"icon\" variant=\"inset\">\n <SidebarHeader>\n <SidebarMenu>\n <SidebarMenuItem>\n <SidebarMenuButton size=\"lg\" asChild>\n <Link href={logoHref} prefetch>\n {logo}\n </Link>\n </SidebarMenuButton>\n </SidebarMenuItem>\n </SidebarMenu>\n </SidebarHeader>\n\n <SidebarContent>\n {resolvedGroups.map((group, index) => (\n <NavMain\n key={group.label ?? index}\n items={group.items}\n label={group.label ?? ''}\n currentUrl={currentUrl}\n linkComponent={linkComponent}\n collapsible={collapsibleGroups}\n collapsedGroups={collapsedGroups}\n onCollapsedChange={onCollapsedChange}\n />\n ))}\n </SidebarContent>\n\n <SidebarFooter>\n {footerItems.length > 0 && (\n <NavFooter items={footerItems} className=\"mt-auto\" />\n )}\n <NavUser\n user={user}\n settingsHref={settingsHref}\n logoutHref={logoutHref}\n linkComponent={linkComponent}\n onSettingsClick={onSettingsClick}\n onLogout={onLogout}\n />\n </SidebarFooter>\n </Sidebar>\n );\n}\n","import { Fragment } from 'react';\n\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@/components/ui/breadcrumb';\nimport { resolveLink } from '@/lib/href';\nimport type {\n BreadcrumbItem as BreadcrumbItemType,\n LinkComponent,\n} from '@/types';\n\nexport function Breadcrumbs({\n breadcrumbs,\n linkComponent,\n}: {\n breadcrumbs: BreadcrumbItemType[];\n linkComponent?: LinkComponent;\n}) {\n const Link = resolveLink(linkComponent);\n\n if (breadcrumbs.length === 0) {\n return null;\n }\n\n return (\n <Breadcrumb>\n <BreadcrumbList>\n {breadcrumbs.map((item, index) => {\n const isLast = index === breadcrumbs.length - 1;\n return (\n <Fragment key={index}>\n <BreadcrumbItem>\n {isLast ? (\n <BreadcrumbPage>\n {item.title}\n </BreadcrumbPage>\n ) : (\n <BreadcrumbLink asChild>\n <Link href={item.href}>\n {item.title}\n </Link>\n </BreadcrumbLink>\n )}\n </BreadcrumbItem>\n {!isLast && <BreadcrumbSeparator />}\n </Fragment>\n );\n })}\n </BreadcrumbList>\n </Breadcrumb>\n );\n}\n","import { Search } from 'lucide-react';\nimport { useEffect, useState } from 'react';\n\nimport { SidebarTrigger } from '@/components/ui/sidebar';\nimport type {\n BreadcrumbItem as BreadcrumbItemType,\n LinkComponent,\n} from '@/types';\nimport { Breadcrumbs } from './breadcrumbs';\n\ninterface AppSidebarHeaderProps {\n breadcrumbs?: BreadcrumbItemType[];\n linkComponent?: LinkComponent;\n onSearchClick?: () => void;\n searchLabel?: string;\n}\n\nexport function AppSidebarHeader({\n breadcrumbs = [],\n linkComponent,\n onSearchClick,\n searchLabel = 'Search...',\n}: AppSidebarHeaderProps) {\n const [modifier, setModifier] = useState('Ctrl');\n\n useEffect(() => {\n const platform = navigator.userAgent;\n setModifier(/Mac|iPhone|iPad|iPod/.test(platform) ? '⌘' : 'Ctrl');\n }, []);\n\n return (\n <header className=\"h-16 gap-2 px-6 group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 md:px-4 flex shrink-0 items-center border-b border-sidebar-border/50 transition-[width,height] ease-linear\">\n <div className=\"gap-2 flex items-center\">\n <SidebarTrigger className=\"-ml-1\" />\n <Breadcrumbs\n breadcrumbs={breadcrumbs}\n linkComponent={linkComponent}\n />\n </div>\n\n {onSearchClick && (\n <button\n type=\"button\"\n onClick={onSearchClick}\n className=\"h-9 w-56 gap-2 rounded-xl px-3 text-sm ml-auto flex items-center border border-border/60 bg-muted/30 text-muted-foreground transition-colors hover:bg-muted/50\"\n >\n <Search className=\"size-4\" />\n <span>{searchLabel}</span>\n <kbd className=\"gap-0.5 px-1.5 py-0.5 font-semibold rounded-xl ml-auto inline-flex items-center border border-border/60 bg-background font-sans text-[10px] text-muted-foreground\">\n <span>{modifier}</span>\n <span>K</span>\n </kbd>\n </button>\n )}\n </header>\n );\n}\n","export function Heading({\n title,\n description,\n}: {\n title: string;\n description?: string;\n}) {\n return (\n <div className=\"mb-8 space-y-0.5\">\n <h2 className=\"text-xl font-semibold tracking-tight\">{title}</h2>\n {description && (\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n )}\n </div>\n );\n}\n","import { type PropsWithChildren } from 'react';\n\nimport { Button } from '@/components/ui/button';\nimport { Separator } from '@/components/ui/separator';\nimport { hrefToString, resolveLink } from '@/lib/href';\nimport { cn } from '@/lib/utils';\nimport type { LinkComponent, NavItem } from '@/types';\nimport { Heading } from './heading';\n\nexport interface SettingsLayoutProps {\n items: NavItem[];\n linkComponent?: LinkComponent;\n currentPath?: string;\n title?: string;\n description?: string;\n wide?: boolean;\n}\n\nexport function SettingsLayout({\n items,\n linkComponent,\n currentPath,\n title = 'Settings',\n description = 'Manage your profile and account settings',\n wide = false,\n children,\n}: PropsWithChildren<SettingsLayoutProps>) {\n const Link = resolveLink(linkComponent);\n const activePath =\n currentPath ??\n (typeof window === 'undefined' ? '' : window.location.pathname);\n\n return (\n <div className=\"px-4 py-6\">\n <Heading title={title} description={description} />\n\n <div className=\"lg:flex-row lg:space-x-12 flex flex-col\">\n <aside className=\"max-w-xl lg:w-48 w-full\">\n <nav className=\"space-y-1 space-x-0 flex flex-col\">\n {items.map((item, index) => {\n const href = hrefToString(item.href);\n return (\n <Button\n key={`${href}-${index}`}\n size=\"sm\"\n variant=\"ghost\"\n asChild\n className={cn('w-full justify-start', {\n 'bg-muted': activePath === href,\n })}\n >\n <Link href={item.href}>\n {item.icon && (\n <item.icon className=\"h-4 w-4\" />\n )}\n {item.title}\n </Link>\n </Button>\n );\n })}\n </nav>\n </aside>\n\n <Separator className=\"my-6 lg:hidden\" />\n\n <div\n className={cn(\n wide ? 'min-w-0 flex-1' : 'md:max-w-2xl flex-1',\n )}\n >\n <section\n className={cn(\n 'space-y-12',\n wide ? 'max-w-5xl' : 'max-w-xl',\n )}\n >\n {children}\n </section>\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { createElement } from 'react';
2
+
3
+ // src/lib/href.ts
4
+ function hrefToString(href) {
5
+ return typeof href === "string" ? href : href.url;
6
+ }
7
+ var DefaultLink = ({
8
+ href,
9
+ children,
10
+ prefetch: _prefetch,
11
+ as: _as,
12
+ ...props
13
+ }) => {
14
+ return createElement("a", { href: hrefToString(href), ...props }, children);
15
+ };
16
+ function resolveLink(linkComponent) {
17
+ return linkComponent ?? DefaultLink;
18
+ }
19
+
20
+ export { hrefToString, resolveLink };
21
+ //# sourceMappingURL=chunk-L2H5GIF7.js.map
22
+ //# sourceMappingURL=chunk-L2H5GIF7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/href.ts"],"names":[],"mappings":";;;AAKO,SAAS,aAAa,IAAA,EAAuB;AAChD,EAAA,OAAO,OAAO,IAAA,KAAS,QAAA,GAAW,IAAA,GAAO,IAAA,CAAK,GAAA;AAClD;AAEO,IAAM,cAAwC,CAAC;AAAA,EAClD,IAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA,EAAU,SAAA;AAAA,EACV,EAAA,EAAI,GAAA;AAAA,EACJ,GAAG;AACP,CAAA,KAAM;AACF,EAAA,OAAO,aAAA,CAAc,GAAA,EAAK,EAAE,IAAA,EAAM,YAAA,CAAa,IAAI,CAAA,EAAG,GAAG,KAAA,EAAM,EAAG,QAAQ,CAAA;AAC9E,CAAA;AAEO,SAAS,YAAY,aAAA,EAA8C;AACtE,EAAA,OAAO,aAAA,IAAiB,WAAA;AAC5B","file":"chunk-L2H5GIF7.js","sourcesContent":["import type { ComponentType } from 'react';\nimport { createElement } from 'react';\n\nimport type { LinkComponent, LinkProps, UrlLike } from '@/types';\n\nexport function hrefToString(href: UrlLike): string {\n return typeof href === 'string' ? href : href.url;\n}\n\nexport const DefaultLink: ComponentType<LinkProps> = ({\n href,\n children,\n prefetch: _prefetch,\n as: _as,\n ...props\n}) => {\n return createElement('a', { href: hrefToString(href), ...props }, children);\n};\n\nexport function resolveLink(linkComponent?: LinkComponent): LinkComponent {\n return linkComponent ?? DefaultLink;\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { focusRing, glassControl, controlFill, cn } from './chunk-DSD7MVLL.js';
2
+ import * as TogglePrimitive from '@radix-ui/react-toggle';
3
+ import { cva } from 'class-variance-authority';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ var toggleVariants = cva(
7
+ `inline-flex items-center justify-center gap-2 rounded-xl text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:font-semibold data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 transition-[color,box-shadow] aria-invalid:ring-destructive/20 aria-invalid:border-destructive ${focusRing}`,
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-transparent",
12
+ outline: `${glassControl} ${controlFill} hover:bg-accent hover:text-accent-foreground`
13
+ },
14
+ size: {
15
+ default: "h-11 px-4 min-w-11",
16
+ sm: "h-9 px-3 min-w-9",
17
+ lg: "h-12 px-6 min-w-12"
18
+ }
19
+ },
20
+ defaultVariants: {
21
+ variant: "default",
22
+ size: "default"
23
+ }
24
+ }
25
+ );
26
+ function Toggle({
27
+ className,
28
+ variant,
29
+ size,
30
+ ...props
31
+ }) {
32
+ return /* @__PURE__ */ jsx(
33
+ TogglePrimitive.Root,
34
+ {
35
+ "data-slot": "toggle",
36
+ className: cn(toggleVariants({ variant, size, className })),
37
+ ...props
38
+ }
39
+ );
40
+ }
41
+
42
+ export { Toggle, toggleVariants };
43
+ //# sourceMappingURL=chunk-LKSM3WLK.js.map
44
+ //# sourceMappingURL=chunk-LKSM3WLK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/ui/toggle.tsx"],"names":[],"mappings":";;;;;AAOA,IAAM,cAAA,GAAiB,GAAA;AAAA,EACnB,4bAA4b,SAAS,CAAA,CAAA;AAAA,EACrc;AAAA,IACI,QAAA,EAAU;AAAA,MACN,OAAA,EAAS;AAAA,QACL,OAAA,EAAS,gBAAA;AAAA,QACT,OAAA,EAAS,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,WAAW,CAAA,6CAAA;AAAA,OAC3C;AAAA,MACA,IAAA,EAAM;AAAA,QACF,OAAA,EAAS,oBAAA;AAAA,QACT,EAAA,EAAI,kBAAA;AAAA,QACJ,EAAA,EAAI;AAAA;AACR,KACJ;AAAA,IACA,eAAA,EAAiB;AAAA,MACb,OAAA,EAAS,SAAA;AAAA,MACT,IAAA,EAAM;AAAA;AACV;AAER;AAEA,SAAS,MAAA,CAAO;AAAA,EACZ,SAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAG;AACP,CAAA,EACyC;AACrC,EAAA,uBACI,GAAA;AAAA,IAAiB,eAAA,CAAA,IAAA;AAAA,IAAhB;AAAA,MACG,WAAA,EAAU,QAAA;AAAA,MACV,SAAA,EAAW,GAAG,cAAA,CAAe,EAAE,SAAS,IAAA,EAAM,SAAA,EAAW,CAAC,CAAA;AAAA,MACzD,GAAG;AAAA;AAAA,GACR;AAER","file":"chunk-LKSM3WLK.js","sourcesContent":["import * as TogglePrimitive from '@radix-ui/react-toggle';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { controlFill, focusRing, glassControl } from '@/lib/language';\nimport { cn } from '@/lib/utils';\n\nconst toggleVariants = cva(\n `inline-flex items-center justify-center gap-2 rounded-xl text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:font-semibold data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 transition-[color,box-shadow] aria-invalid:ring-destructive/20 aria-invalid:border-destructive ${focusRing}`,\n {\n variants: {\n variant: {\n default: 'bg-transparent',\n outline: `${glassControl} ${controlFill} hover:bg-accent hover:text-accent-foreground`,\n },\n size: {\n default: 'h-11 px-4 min-w-11',\n sm: 'h-9 px-3 min-w-9',\n lg: 'h-12 px-6 min-w-12',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\nfunction Toggle({\n className,\n variant,\n size,\n ...props\n}: React.ComponentProps<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>) {\n return (\n <TogglePrimitive.Root\n data-slot=\"toggle\"\n className={cn(toggleVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Toggle, toggleVariants };\n"]}