@braedonsaunders/appkit-ui 0.2.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 (300) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +11 -0
  3. package/account-menu.d.ts +49 -0
  4. package/account-menu.d.ts.map +1 -0
  5. package/account-menu.js +66 -0
  6. package/account-menu.js.map +1 -0
  7. package/alert.d.ts +13 -0
  8. package/alert.d.ts.map +1 -0
  9. package/alert.js +22 -0
  10. package/alert.js.map +1 -0
  11. package/animated-number.d.ts +13 -0
  12. package/animated-number.d.ts.map +1 -0
  13. package/animated-number.js +53 -0
  14. package/animated-number.js.map +1 -0
  15. package/api-reference.d.ts +44 -0
  16. package/api-reference.d.ts.map +1 -0
  17. package/api-reference.js +82 -0
  18. package/api-reference.js.map +1 -0
  19. package/app-shell.d.ts +39 -0
  20. package/app-shell.d.ts.map +1 -0
  21. package/app-shell.js +21 -0
  22. package/app-shell.js.map +1 -0
  23. package/app-sidebar.d.ts +22 -0
  24. package/app-sidebar.d.ts.map +1 -0
  25. package/app-sidebar.js +30 -0
  26. package/app-sidebar.js.map +1 -0
  27. package/avatar.d.ts +9 -0
  28. package/avatar.d.ts.map +1 -0
  29. package/avatar.js +18 -0
  30. package/avatar.js.map +1 -0
  31. package/badge.d.ts +9 -0
  32. package/badge.d.ts.map +1 -0
  33. package/badge.js +23 -0
  34. package/badge.js.map +1 -0
  35. package/button.d.ts +24 -0
  36. package/button.d.ts.map +1 -0
  37. package/button.js +50 -0
  38. package/button.js.map +1 -0
  39. package/card.d.ts +21 -0
  40. package/card.d.ts.map +1 -0
  41. package/card.js +36 -0
  42. package/card.js.map +1 -0
  43. package/checkbox.d.ts +4 -0
  44. package/checkbox.d.ts.map +1 -0
  45. package/checkbox.js +6 -0
  46. package/checkbox.js.map +1 -0
  47. package/confirm-dialog.d.ts +23 -0
  48. package/confirm-dialog.d.ts.map +1 -0
  49. package/confirm-dialog.js +67 -0
  50. package/confirm-dialog.js.map +1 -0
  51. package/context-menu.d.ts +47 -0
  52. package/context-menu.d.ts.map +1 -0
  53. package/context-menu.js +90 -0
  54. package/context-menu.js.map +1 -0
  55. package/dialog.d.ts +25 -0
  56. package/dialog.d.ts.map +1 -0
  57. package/dialog.js +89 -0
  58. package/dialog.js.map +1 -0
  59. package/document-title.d.ts +11 -0
  60. package/document-title.d.ts.map +1 -0
  61. package/document-title.js +17 -0
  62. package/document-title.js.map +1 -0
  63. package/drawer.d.ts +63 -0
  64. package/drawer.d.ts.map +1 -0
  65. package/drawer.js +188 -0
  66. package/drawer.js.map +1 -0
  67. package/empty-state.d.ts +10 -0
  68. package/empty-state.d.ts.map +1 -0
  69. package/empty-state.js +7 -0
  70. package/empty-state.js.map +1 -0
  71. package/file-uploader.d.ts +67 -0
  72. package/file-uploader.d.ts.map +1 -0
  73. package/file-uploader.js +156 -0
  74. package/file-uploader.js.map +1 -0
  75. package/filter-chips.d.ts +47 -0
  76. package/filter-chips.d.ts.map +1 -0
  77. package/filter-chips.js +62 -0
  78. package/filter-chips.js.map +1 -0
  79. package/global-search.d.ts +41 -0
  80. package/global-search.d.ts.map +1 -0
  81. package/global-search.js +116 -0
  82. package/global-search.js.map +1 -0
  83. package/index.d.ts +70 -0
  84. package/index.d.ts.map +1 -0
  85. package/index.js +70 -0
  86. package/index.js.map +1 -0
  87. package/input.d.ts +9 -0
  88. package/input.d.ts.map +1 -0
  89. package/input.js +11 -0
  90. package/input.js.map +1 -0
  91. package/label.d.ts +3 -0
  92. package/label.d.ts.map +1 -0
  93. package/label.js +6 -0
  94. package/label.js.map +1 -0
  95. package/line-grid.d.ts +48 -0
  96. package/line-grid.d.ts.map +1 -0
  97. package/line-grid.js +155 -0
  98. package/line-grid.js.map +1 -0
  99. package/link-context.d.ts +44 -0
  100. package/link-context.d.ts.map +1 -0
  101. package/link-context.js +32 -0
  102. package/link-context.js.map +1 -0
  103. package/list-nav.d.ts +28 -0
  104. package/list-nav.d.ts.map +1 -0
  105. package/list-nav.js +41 -0
  106. package/list-nav.js.map +1 -0
  107. package/list-pagination.d.ts +27 -0
  108. package/list-pagination.d.ts.map +1 -0
  109. package/list-pagination.js +43 -0
  110. package/list-pagination.js.map +1 -0
  111. package/list-params.d.ts +41 -0
  112. package/list-params.d.ts.map +1 -0
  113. package/list-params.js +84 -0
  114. package/list-params.js.map +1 -0
  115. package/list-search-input-state.d.ts +13 -0
  116. package/list-search-input-state.d.ts.map +1 -0
  117. package/list-search-input-state.js +27 -0
  118. package/list-search-input-state.js.map +1 -0
  119. package/list-search-input.d.ts +16 -0
  120. package/list-search-input.d.ts.map +1 -0
  121. package/list-search-input.js +51 -0
  122. package/list-search-input.js.map +1 -0
  123. package/mobile-tab-bar.d.ts +14 -0
  124. package/mobile-tab-bar.d.ts.map +1 -0
  125. package/mobile-tab-bar.js +25 -0
  126. package/mobile-tab-bar.js.map +1 -0
  127. package/multi-select.d.ts +32 -0
  128. package/multi-select.d.ts.map +1 -0
  129. package/multi-select.js +52 -0
  130. package/multi-select.js.map +1 -0
  131. package/nav-overflow.d.ts +12 -0
  132. package/nav-overflow.d.ts.map +1 -0
  133. package/nav-overflow.js +30 -0
  134. package/nav-overflow.js.map +1 -0
  135. package/navigation-config-editor.d.ts +26 -0
  136. package/navigation-config-editor.d.ts.map +1 -0
  137. package/navigation-config-editor.js +57 -0
  138. package/navigation-config-editor.js.map +1 -0
  139. package/navigation-config.d.ts +48 -0
  140. package/navigation-config.d.ts.map +1 -0
  141. package/navigation-config.js +75 -0
  142. package/navigation-config.js.map +1 -0
  143. package/navigation-mode.d.ts +16 -0
  144. package/navigation-mode.d.ts.map +1 -0
  145. package/navigation-mode.js +30 -0
  146. package/navigation-mode.js.map +1 -0
  147. package/notifications-bell.d.ts +22 -0
  148. package/notifications-bell.d.ts.map +1 -0
  149. package/notifications-bell.js +30 -0
  150. package/notifications-bell.js.map +1 -0
  151. package/org-chart.d.ts +85 -0
  152. package/org-chart.d.ts.map +1 -0
  153. package/org-chart.js +193 -0
  154. package/org-chart.js.map +1 -0
  155. package/package.json +61 -0
  156. package/page-header.d.ts +32 -0
  157. package/page-header.d.ts.map +1 -0
  158. package/page-header.js +14 -0
  159. package/page-header.js.map +1 -0
  160. package/page-layout.d.ts +29 -0
  161. package/page-layout.d.ts.map +1 -0
  162. package/page-layout.js +23 -0
  163. package/page-layout.js.map +1 -0
  164. package/page-transition.d.ts +13 -0
  165. package/page-transition.d.ts.map +1 -0
  166. package/page-transition.js +14 -0
  167. package/page-transition.js.map +1 -0
  168. package/paged-table.d.ts +54 -0
  169. package/paged-table.d.ts.map +1 -0
  170. package/paged-table.js +74 -0
  171. package/paged-table.js.map +1 -0
  172. package/popover.d.ts +16 -0
  173. package/popover.d.ts.map +1 -0
  174. package/popover.js +99 -0
  175. package/popover.js.map +1 -0
  176. package/progress.d.ts +7 -0
  177. package/progress.d.ts.map +1 -0
  178. package/progress.js +8 -0
  179. package/progress.js.map +1 -0
  180. package/prompt-dialog.d.ts +21 -0
  181. package/prompt-dialog.d.ts.map +1 -0
  182. package/prompt-dialog.js +109 -0
  183. package/prompt-dialog.js.map +1 -0
  184. package/record-list.d.ts +76 -0
  185. package/record-list.d.ts.map +1 -0
  186. package/record-list.js +51 -0
  187. package/record-list.js.map +1 -0
  188. package/search-select.d.ts +42 -0
  189. package/search-select.d.ts.map +1 -0
  190. package/search-select.js +194 -0
  191. package/search-select.js.map +1 -0
  192. package/select-options.d.ts +16 -0
  193. package/select-options.d.ts.map +1 -0
  194. package/select-options.js +119 -0
  195. package/select-options.js.map +1 -0
  196. package/select.d.ts +24 -0
  197. package/select.d.ts.map +1 -0
  198. package/select.js +65 -0
  199. package/select.js.map +1 -0
  200. package/separator.d.ts +6 -0
  201. package/separator.d.ts.map +1 -0
  202. package/separator.js +7 -0
  203. package/separator.js.map +1 -0
  204. package/settings-layout.d.ts +79 -0
  205. package/settings-layout.d.ts.map +1 -0
  206. package/settings-layout.js +74 -0
  207. package/settings-layout.js.map +1 -0
  208. package/sidebar-nav.d.ts +67 -0
  209. package/sidebar-nav.d.ts.map +1 -0
  210. package/sidebar-nav.js +291 -0
  211. package/sidebar-nav.js.map +1 -0
  212. package/signature-pad.d.ts +16 -0
  213. package/signature-pad.d.ts.map +1 -0
  214. package/signature-pad.js +355 -0
  215. package/signature-pad.js.map +1 -0
  216. package/skeleton.d.ts +9 -0
  217. package/skeleton.d.ts.map +1 -0
  218. package/skeleton.js +12 -0
  219. package/skeleton.js.map +1 -0
  220. package/slider.d.ts +13 -0
  221. package/slider.d.ts.map +1 -0
  222. package/slider.js +15 -0
  223. package/slider.js.map +1 -0
  224. package/sortable-th.d.ts +28 -0
  225. package/sortable-th.d.ts.map +1 -0
  226. package/sortable-th.js +35 -0
  227. package/sortable-th.js.map +1 -0
  228. package/sparkline.d.ts +16 -0
  229. package/sparkline.d.ts.map +1 -0
  230. package/sparkline.js +46 -0
  231. package/sparkline.js.map +1 -0
  232. package/spinner.d.ts +11 -0
  233. package/spinner.d.ts.map +1 -0
  234. package/spinner.js +11 -0
  235. package/spinner.js.map +1 -0
  236. package/styles.css +166 -0
  237. package/subtab-nav.d.ts +17 -0
  238. package/subtab-nav.d.ts.map +1 -0
  239. package/subtab-nav.js +11 -0
  240. package/subtab-nav.js.map +1 -0
  241. package/switch.d.ts +9 -0
  242. package/switch.d.ts.map +1 -0
  243. package/switch.js +11 -0
  244. package/switch.js.map +1 -0
  245. package/tab-content.d.ts +24 -0
  246. package/tab-content.d.ts.map +1 -0
  247. package/tab-content.js +26 -0
  248. package/tab-content.js.map +1 -0
  249. package/table.d.ts +28 -0
  250. package/table.d.ts.map +1 -0
  251. package/table.js +21 -0
  252. package/table.js.map +1 -0
  253. package/tabs.d.ts +18 -0
  254. package/tabs.d.ts.map +1 -0
  255. package/tabs.js +18 -0
  256. package/tabs.js.map +1 -0
  257. package/text-context.d.ts +9 -0
  258. package/text-context.d.ts.map +1 -0
  259. package/text-context.js +12 -0
  260. package/text-context.js.map +1 -0
  261. package/textarea.d.ts +4 -0
  262. package/textarea.d.ts.map +1 -0
  263. package/textarea.js +6 -0
  264. package/textarea.js.map +1 -0
  265. package/theme-script.d.ts +3 -0
  266. package/theme-script.d.ts.map +1 -0
  267. package/theme-script.js +6 -0
  268. package/theme-script.js.map +1 -0
  269. package/theme.d.ts +28 -0
  270. package/theme.d.ts.map +1 -0
  271. package/theme.js +92 -0
  272. package/theme.js.map +1 -0
  273. package/toast.d.ts +52 -0
  274. package/toast.d.ts.map +1 -0
  275. package/toast.js +178 -0
  276. package/toast.js.map +1 -0
  277. package/tooltip.d.ts +12 -0
  278. package/tooltip.d.ts.map +1 -0
  279. package/tooltip.js +14 -0
  280. package/tooltip.js.map +1 -0
  281. package/top-nav.d.ts +18 -0
  282. package/top-nav.d.ts.map +1 -0
  283. package/top-nav.js +160 -0
  284. package/top-nav.js.map +1 -0
  285. package/upload-limits.d.ts +4 -0
  286. package/upload-limits.d.ts.map +1 -0
  287. package/upload-limits.js +21 -0
  288. package/upload-limits.js.map +1 -0
  289. package/use-element-size.d.ts +7 -0
  290. package/use-element-size.d.ts.map +1 -0
  291. package/use-element-size.js +21 -0
  292. package/use-element-size.js.map +1 -0
  293. package/utils.d.ts +4 -0
  294. package/utils.d.ts.map +1 -0
  295. package/utils.js +7 -0
  296. package/utils.js.map +1 -0
  297. package/workspace-launcher.d.ts +50 -0
  298. package/workspace-launcher.d.ts.map +1 -0
  299. package/workspace-launcher.js +127 -0
  300. package/workspace-launcher.js.map +1 -0
@@ -0,0 +1,49 @@
1
+ import * as React from 'react';
2
+ import { type BadgeProps } from './badge.js';
3
+ export type AccountMenuOption = {
4
+ value: string;
5
+ label: string;
6
+ description?: string;
7
+ };
8
+ export type AccountMenuChoice = {
9
+ label: string;
10
+ summary: string;
11
+ value: string;
12
+ options: AccountMenuOption[];
13
+ onChange: (value: string) => void | Promise<void>;
14
+ };
15
+ export type AccountMenuLabels = {
16
+ menu: string;
17
+ account: string;
18
+ theme: string;
19
+ back: string;
20
+ signOut: string;
21
+ signingOut: string;
22
+ };
23
+ export type AccountMenuProps = {
24
+ name: string;
25
+ email: string;
26
+ contextLabel?: string;
27
+ contextTone?: 'default' | 'warning';
28
+ roleLabel?: string;
29
+ status?: {
30
+ label: string;
31
+ variant?: BadgeProps['variant'];
32
+ };
33
+ organization?: AccountMenuChoice;
34
+ language?: AccountMenuChoice;
35
+ navigation?: AccountMenuChoice;
36
+ showTheme?: boolean;
37
+ elevatedAccess?: {
38
+ label: string;
39
+ href: string;
40
+ };
41
+ onSignOut?: () => void | Promise<void>;
42
+ labels?: Partial<AccountMenuLabels>;
43
+ };
44
+ /**
45
+ * Bounded account launcher generalized into controlled choices.
46
+ * Organization, language, layout persistence and sign-out stay app-owned.
47
+ */
48
+ export declare function AccountMenu({ name, email, contextLabel, contextTone, roleLabel, status, organization, language, navigation, showTheme, elevatedAccess, onSignOut, labels: labelOverrides, }: AccountMenuProps): React.JSX.Element;
49
+ //# sourceMappingURL=account-menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-menu.d.ts","sourceRoot":"","sources":["../src/account-menu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAe9B,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AAMhD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClD,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAaD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IACnC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;KAAE,CAAA;IAC3D,YAAY,CAAC,EAAE,iBAAiB,CAAA;IAChC,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;CACpC,CAAA;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,KAAK,EACL,YAAY,EACZ,WAAuB,EACvB,SAAS,EACT,MAAM,EACN,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAgB,EAChB,cAAc,EACd,SAAS,EACT,MAAM,EAAE,cAAc,GACvB,EAAE,gBAAgB,qBAyHlB"}
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Building2, Check, ChevronDown, ChevronLeft, ChevronRight, Languages, LayoutPanelLeft, LogOut, Palette, ShieldAlert, } from 'lucide-react';
5
+ import { Avatar } from './avatar.js';
6
+ import { Badge } from './badge.js';
7
+ import { UiLink } from './link-context.js';
8
+ import { Popover } from './popover.js';
9
+ import { ThemeToggle } from './theme.js';
10
+ import { cn } from './utils.js';
11
+ const DEFAULT_LABELS = {
12
+ menu: 'Open account menu',
13
+ account: 'Account',
14
+ theme: 'Theme',
15
+ back: 'Back',
16
+ signOut: 'Sign out',
17
+ signingOut: 'Signing out…',
18
+ };
19
+ /**
20
+ * Bounded account launcher generalized into controlled choices.
21
+ * Organization, language, layout persistence and sign-out stay app-owned.
22
+ */
23
+ export function AccountMenu({ name, email, contextLabel, contextTone = 'default', roleLabel, status, organization, language, navigation, showTheme = true, elevatedAccess, onSignOut, labels: labelOverrides, }) {
24
+ const labels = { ...DEFAULT_LABELS, ...labelOverrides };
25
+ const [open, setOpen] = React.useState(false);
26
+ const [view, setView] = React.useState('home');
27
+ const [saving, setSaving] = React.useState(false);
28
+ const [signingOut, startSignOut] = React.useTransition();
29
+ const displayName = name || email || labels.account;
30
+ function close() {
31
+ setOpen(false);
32
+ window.setTimeout(() => setView('home'), 150);
33
+ }
34
+ async function choose(choice, value) {
35
+ setSaving(true);
36
+ try {
37
+ await choice.onChange(value);
38
+ close();
39
+ }
40
+ finally {
41
+ setSaving(false);
42
+ }
43
+ }
44
+ const title = view === 'organization'
45
+ ? organization?.label
46
+ : view === 'language'
47
+ ? language?.label
48
+ : view === 'navigation'
49
+ ? navigation?.label
50
+ : labels.theme;
51
+ return (_jsx(Popover, { open: open, onOpenChange: (next) => {
52
+ setOpen(next);
53
+ if (!next)
54
+ window.setTimeout(() => setView('home'), 150);
55
+ }, align: "end", className: "w-72", trigger: _jsxs("button", { type: "button", onClick: () => setOpen((current) => !current), "aria-label": labels.menu, "aria-expanded": open, "aria-haspopup": "menu", className: "flex shrink-0 items-center gap-2 rounded-md border border-transparent px-1.5 py-1 text-sm text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg", children: [_jsx(Avatar, { name: displayName, size: 28 }), _jsxs("span", { className: "hidden min-w-0 max-w-44 flex-col text-left sm:flex", children: [_jsx("span", { className: "truncate text-sm leading-tight text-fg", children: displayName }), contextLabel ? (_jsx("span", { className: cn('truncate text-[11px] leading-tight', contextTone === 'warning' ? 'text-warning' : 'text-fg-subtle'), children: contextLabel })) : null] }), _jsx(ChevronDown, { size: 14, className: "hidden shrink-0 text-fg-subtle sm:inline" })] }), children: view === 'home' ? (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-3 px-3 py-3", children: [_jsx(Avatar, { name: displayName, size: 40 }), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate text-sm font-medium text-fg", children: displayName }), email ? _jsx("span", { className: "truncate text-xs text-fg-muted", children: email }) : null, _jsxs("span", { className: "mt-1 flex flex-wrap gap-1", children: [roleLabel ? _jsx(Badge, { variant: "secondary", className: "text-[10px]", children: roleLabel }) : null, status ? _jsx(Badge, { variant: status.variant ?? 'secondary', className: "text-[10px]", children: status.label }) : null] })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2 px-2 pb-2", children: [organization ? _jsx(LauncherCard, { icon: Building2, tone: "primary", label: organization.label, summary: organization.summary, onClick: () => setView('organization') }) : null, language ? _jsx(LauncherCard, { icon: Languages, tone: "info", label: language.label, summary: language.summary, onClick: () => setView('language') }) : null, showTheme ? _jsx(LauncherCard, { icon: Palette, tone: "primary", label: labels.theme, onClick: () => setView('theme') }) : null, navigation ? _jsx(LauncherCard, { icon: LayoutPanelLeft, tone: "warning", label: navigation.label, summary: navigation.summary, onClick: () => setView('navigation') }) : null, elevatedAccess ? (_jsxs(UiLink, { href: elevatedAccess.href, onClick: close, className: "group relative flex flex-col gap-2 rounded-xl border border-border bg-surface p-3 transition-colors hover:border-border-strong hover:bg-surface-hover", children: [_jsx("span", { className: "grid size-9 place-items-center rounded-lg bg-danger-subtle text-danger", children: _jsx(ShieldAlert, { size: 18 }) }), _jsx("span", { className: "truncate text-sm font-medium text-fg", children: elevatedAccess.label })] })) : null] }), onSignOut ? (_jsx("div", { className: "border-t border-border-subtle p-1", children: _jsxs("button", { type: "button", disabled: signingOut, role: "menuitem", onClick: () => startSignOut(async () => { await onSignOut(); close(); }), className: "flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg disabled:opacity-60", children: [_jsx(LogOut, { size: 15 }), signingOut ? labels.signingOut : labels.signOut] }) })) : null] })) : (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-1.5 border-b border-border-subtle px-2 py-2", children: [_jsx("button", { type: "button", onClick: () => setView('home'), "aria-label": labels.back, className: "grid size-7 place-items-center rounded-md text-fg-muted hover:bg-surface-hover hover:text-fg", children: _jsx(ChevronLeft, { size: 16 }) }), _jsx("span", { className: "text-sm font-semibold text-fg", children: title })] }), view === 'theme' ? _jsx("div", { className: "p-3", children: _jsx(ThemeToggle, {}) }) : null, view === 'organization' && organization ? _jsx(ChoiceList, { choice: organization, saving: saving, onChoose: choose }) : null, view === 'language' && language ? _jsx(ChoiceList, { choice: language, saving: saving, onChoose: choose }) : null, view === 'navigation' && navigation ? _jsx(ChoiceList, { choice: navigation, saving: saving, onChoose: choose }) : null] })) }));
56
+ }
57
+ function LauncherCard({ icon: Icon, tone, label, summary, onClick }) {
58
+ return (_jsxs("button", { type: "button", onClick: onClick, className: "group relative flex flex-col gap-2 rounded-xl border border-border bg-surface p-3 text-left transition-colors hover:border-border-strong hover:bg-surface-hover", children: [_jsx("span", { className: cn('grid size-9 place-items-center rounded-lg', tone === 'info' ? 'bg-info-subtle text-info' : tone === 'warning' ? 'bg-warning-subtle text-warning' : 'bg-primary-subtle text-primary'), children: _jsx(Icon, { size: 18 }) }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block truncate text-sm font-medium text-fg", children: label }), summary ? _jsx("span", { className: "block truncate text-[11px] text-fg-subtle", children: summary }) : null] }), _jsx(ChevronRight, { size: 14, className: "absolute right-2.5 top-3 text-fg-subtle transition-transform group-hover:translate-x-0.5" })] }));
59
+ }
60
+ function ChoiceList({ choice, saving, onChoose }) {
61
+ return (_jsx("div", { className: "max-h-72 overflow-y-auto p-1", children: choice.options.map((option) => {
62
+ const active = choice.value === option.value;
63
+ return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": active, disabled: saving, onClick: () => void onChoose(choice, option.value), className: "flex w-full items-start gap-2 rounded-md px-3 py-2 text-left text-sm text-fg-muted hover:bg-surface-hover hover:text-fg disabled:opacity-60", children: [_jsx(Check, { size: 15, className: cn('mt-0.5 shrink-0', active ? 'text-primary' : 'text-transparent') }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block truncate", children: option.label }), option.description ? _jsx("span", { className: "block truncate text-xs text-fg-subtle", children: option.description }) : null] })] }, option.value));
64
+ }) }));
65
+ }
66
+ //# sourceMappingURL=account-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-menu.js","sourceRoot":"","sources":["../src/account-menu.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport {\n Building2,\n Check,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n Languages,\n LayoutPanelLeft,\n LogOut,\n Palette,\n ShieldAlert,\n type LucideIcon,\n} from 'lucide-react'\nimport { Avatar } from './avatar'\nimport { Badge, type BadgeProps } from './badge'\nimport { UiLink } from './link-context'\nimport { Popover } from './popover'\nimport { ThemeToggle } from './theme'\nimport { cn } from './utils'\n\nexport type AccountMenuOption = {\n value: string\n label: string\n description?: string\n}\n\nexport type AccountMenuChoice = {\n label: string\n summary: string\n value: string\n options: AccountMenuOption[]\n onChange: (value: string) => void | Promise<void>\n}\n\nexport type AccountMenuLabels = {\n menu: string\n account: string\n theme: string\n back: string\n signOut: string\n signingOut: string\n}\n\nconst DEFAULT_LABELS: AccountMenuLabels = {\n menu: 'Open account menu',\n account: 'Account',\n theme: 'Theme',\n back: 'Back',\n signOut: 'Sign out',\n signingOut: 'Signing out…',\n}\n\ntype View = 'home' | 'organization' | 'language' | 'theme' | 'navigation'\n\nexport type AccountMenuProps = {\n name: string\n email: string\n contextLabel?: string\n contextTone?: 'default' | 'warning'\n roleLabel?: string\n status?: { label: string; variant?: BadgeProps['variant'] }\n organization?: AccountMenuChoice\n language?: AccountMenuChoice\n navigation?: AccountMenuChoice\n showTheme?: boolean\n elevatedAccess?: { label: string; href: string }\n onSignOut?: () => void | Promise<void>\n labels?: Partial<AccountMenuLabels>\n}\n\n/**\n * Bounded account launcher generalized into controlled choices.\n * Organization, language, layout persistence and sign-out stay app-owned.\n */\nexport function AccountMenu({\n name,\n email,\n contextLabel,\n contextTone = 'default',\n roleLabel,\n status,\n organization,\n language,\n navigation,\n showTheme = true,\n elevatedAccess,\n onSignOut,\n labels: labelOverrides,\n}: AccountMenuProps) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides }\n const [open, setOpen] = React.useState(false)\n const [view, setView] = React.useState<View>('home')\n const [saving, setSaving] = React.useState(false)\n const [signingOut, startSignOut] = React.useTransition()\n const displayName = name || email || labels.account\n\n function close() {\n setOpen(false)\n window.setTimeout(() => setView('home'), 150)\n }\n\n async function choose(choice: AccountMenuChoice, value: string) {\n setSaving(true)\n try {\n await choice.onChange(value)\n close()\n } finally {\n setSaving(false)\n }\n }\n\n const title =\n view === 'organization'\n ? organization?.label\n : view === 'language'\n ? language?.label\n : view === 'navigation'\n ? navigation?.label\n : labels.theme\n\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next)\n if (!next) window.setTimeout(() => setView('home'), 150)\n }}\n align=\"end\"\n className=\"w-72\"\n trigger={\n <button\n type=\"button\"\n onClick={() => setOpen((current) => !current)}\n aria-label={labels.menu}\n aria-expanded={open}\n aria-haspopup=\"menu\"\n className=\"flex shrink-0 items-center gap-2 rounded-md border border-transparent px-1.5 py-1 text-sm text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg\"\n >\n <Avatar name={displayName} size={28} />\n <span className=\"hidden min-w-0 max-w-44 flex-col text-left sm:flex\">\n <span className=\"truncate text-sm leading-tight text-fg\">{displayName}</span>\n {contextLabel ? (\n <span className={cn('truncate text-[11px] leading-tight', contextTone === 'warning' ? 'text-warning' : 'text-fg-subtle')}>\n {contextLabel}\n </span>\n ) : null}\n </span>\n <ChevronDown size={14} className=\"hidden shrink-0 text-fg-subtle sm:inline\" />\n </button>\n }\n >\n {view === 'home' ? (\n <div>\n <div className=\"flex items-center gap-3 px-3 py-3\">\n <Avatar name={displayName} size={40} />\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate text-sm font-medium text-fg\">{displayName}</span>\n {email ? <span className=\"truncate text-xs text-fg-muted\">{email}</span> : null}\n <span className=\"mt-1 flex flex-wrap gap-1\">\n {roleLabel ? <Badge variant=\"secondary\" className=\"text-[10px]\">{roleLabel}</Badge> : null}\n {status ? <Badge variant={status.variant ?? 'secondary'} className=\"text-[10px]\">{status.label}</Badge> : null}\n </span>\n </span>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2 px-2 pb-2\">\n {organization ? <LauncherCard icon={Building2} tone=\"primary\" label={organization.label} summary={organization.summary} onClick={() => setView('organization')} /> : null}\n {language ? <LauncherCard icon={Languages} tone=\"info\" label={language.label} summary={language.summary} onClick={() => setView('language')} /> : null}\n {showTheme ? <LauncherCard icon={Palette} tone=\"primary\" label={labels.theme} onClick={() => setView('theme')} /> : null}\n {navigation ? <LauncherCard icon={LayoutPanelLeft} tone=\"warning\" label={navigation.label} summary={navigation.summary} onClick={() => setView('navigation')} /> : null}\n {elevatedAccess ? (\n <UiLink href={elevatedAccess.href} onClick={close} className=\"group relative flex flex-col gap-2 rounded-xl border border-border bg-surface p-3 transition-colors hover:border-border-strong hover:bg-surface-hover\">\n <span className=\"grid size-9 place-items-center rounded-lg bg-danger-subtle text-danger\"><ShieldAlert size={18} /></span>\n <span className=\"truncate text-sm font-medium text-fg\">{elevatedAccess.label}</span>\n </UiLink>\n ) : null}\n </div>\n\n {onSignOut ? (\n <div className=\"border-t border-border-subtle p-1\">\n <button\n type=\"button\"\n disabled={signingOut}\n role=\"menuitem\"\n onClick={() => startSignOut(async () => { await onSignOut(); close() })}\n className=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg disabled:opacity-60\"\n >\n <LogOut size={15} />\n {signingOut ? labels.signingOut : labels.signOut}\n </button>\n </div>\n ) : null}\n </div>\n ) : (\n <div>\n <div className=\"flex items-center gap-1.5 border-b border-border-subtle px-2 py-2\">\n <button type=\"button\" onClick={() => setView('home')} aria-label={labels.back} className=\"grid size-7 place-items-center rounded-md text-fg-muted hover:bg-surface-hover hover:text-fg\">\n <ChevronLeft size={16} />\n </button>\n <span className=\"text-sm font-semibold text-fg\">{title}</span>\n </div>\n {view === 'theme' ? <div className=\"p-3\"><ThemeToggle /></div> : null}\n {view === 'organization' && organization ? <ChoiceList choice={organization} saving={saving} onChoose={choose} /> : null}\n {view === 'language' && language ? <ChoiceList choice={language} saving={saving} onChoose={choose} /> : null}\n {view === 'navigation' && navigation ? <ChoiceList choice={navigation} saving={saving} onChoose={choose} /> : null}\n </div>\n )}\n </Popover>\n )\n}\n\nfunction LauncherCard({ icon: Icon, tone, label, summary, onClick }: { icon: LucideIcon; tone: 'primary' | 'info' | 'warning'; label: string; summary?: string; onClick: () => void }) {\n return (\n <button type=\"button\" onClick={onClick} className=\"group relative flex flex-col gap-2 rounded-xl border border-border bg-surface p-3 text-left transition-colors hover:border-border-strong hover:bg-surface-hover\">\n <span className={cn('grid size-9 place-items-center rounded-lg', tone === 'info' ? 'bg-info-subtle text-info' : tone === 'warning' ? 'bg-warning-subtle text-warning' : 'bg-primary-subtle text-primary')}><Icon size={18} /></span>\n <span className=\"min-w-0\">\n <span className=\"block truncate text-sm font-medium text-fg\">{label}</span>\n {summary ? <span className=\"block truncate text-[11px] text-fg-subtle\">{summary}</span> : null}\n </span>\n <ChevronRight size={14} className=\"absolute right-2.5 top-3 text-fg-subtle transition-transform group-hover:translate-x-0.5\" />\n </button>\n )\n}\n\nfunction ChoiceList({ choice, saving, onChoose }: { choice: AccountMenuChoice; saving: boolean; onChoose: (choice: AccountMenuChoice, value: string) => Promise<void> }) {\n return (\n <div className=\"max-h-72 overflow-y-auto p-1\">\n {choice.options.map((option) => {\n const active = choice.value === option.value\n return (\n <button key={option.value} type=\"button\" role=\"menuitemradio\" aria-checked={active} disabled={saving} onClick={() => void onChoose(choice, option.value)} className=\"flex w-full items-start gap-2 rounded-md px-3 py-2 text-left text-sm text-fg-muted hover:bg-surface-hover hover:text-fg disabled:opacity-60\">\n <Check size={15} className={cn('mt-0.5 shrink-0', active ? 'text-primary' : 'text-transparent')} />\n <span className=\"min-w-0\"><span className=\"block truncate\">{option.label}</span>{option.description ? <span className=\"block truncate text-xs text-fg-subtle\">{option.description}</span> : null}</span>\n </button>\n )\n })}\n </div>\n )\n}\n"]}
package/alert.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+ import { type VariantProps } from 'class-variance-authority';
3
+ declare const alertVariants: (props?: ({
4
+ variant?: "default" | "destructive" | "warning" | "success" | "info" | null | undefined;
5
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
+ export type AlertProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>;
7
+ export declare const Alert: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
8
+ variant?: "default" | "destructive" | "warning" | "success" | "info" | null | undefined;
9
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
10
+ export declare const AlertTitle: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => React.JSX.Element;
11
+ export declare const AlertDescription: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => React.JSX.Element;
12
+ export { alertVariants };
13
+ //# sourceMappingURL=alert.d.ts.map
package/alert.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.d.ts","sourceRoot":"","sources":["../src/alert.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGjE,QAAA,MAAM,aAAa;;8EAWjB,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAA;AAElG,eAAO,MAAM,KAAK;;sHAIjB,CAAA;AAGD,eAAO,MAAM,UAAU,GAAI,yBAAyB,KAAK,CAAC,cAAc,CAAC,kBAAkB,CAAC,sBAE3F,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,yBAAyB,KAAK,CAAC,cAAc,CAAC,oBAAoB,CAAC,sBAEnG,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,CAAA"}
package/alert.js ADDED
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { cva } from 'class-variance-authority';
4
+ import { cn } from './utils.js';
5
+ const alertVariants = cva('relative w-full rounded-lg border p-4 text-sm shadow-sm', {
6
+ variants: {
7
+ variant: {
8
+ default: 'border-border bg-surface text-fg',
9
+ destructive: 'border-danger/25 bg-danger-subtle text-danger',
10
+ warning: 'border-warning/25 bg-warning-subtle text-warning',
11
+ success: 'border-success/25 bg-success-subtle text-success',
12
+ info: 'border-info/25 bg-info-subtle text-info',
13
+ },
14
+ },
15
+ defaultVariants: { variant: 'default' },
16
+ });
17
+ export const Alert = React.forwardRef(({ className, variant, ...props }, ref) => (_jsx("div", { ref: ref, role: "alert", className: cn(alertVariants({ variant }), className), ...props })));
18
+ Alert.displayName = 'Alert';
19
+ export const AlertTitle = ({ className, ...props }) => (_jsx("h5", { className: cn('mb-1 font-semibold leading-tight tracking-tight', className), ...props }));
20
+ export const AlertDescription = ({ className, ...props }) => (_jsx("div", { className: cn('text-sm leading-relaxed opacity-90 [&_p]:leading-relaxed', className), ...props }));
21
+ export { alertVariants };
22
+ //# sourceMappingURL=alert.js.map
package/alert.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.js","sourceRoot":"","sources":["../src/alert.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,GAAG,EAAqB,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAE5B,MAAM,aAAa,GAAG,GAAG,CAAC,yDAAyD,EAAE;IACnF,QAAQ,EAAE;QACR,OAAO,EAAE;YACP,OAAO,EAAE,kCAAkC;YAC3C,WAAW,EAAE,+CAA+C;YAC5D,OAAO,EAAE,kDAAkD;YAC3D,OAAO,EAAE,kDAAkD;YAC3D,IAAI,EAAE,yCAAyC;SAChD;KACF;IACD,eAAe,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;CACxC,CAAC,CAAA;AAIF,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CACnC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CACzC,cAAK,GAAG,EAAE,GAAG,EAAE,IAAI,EAAC,OAAO,EAAC,SAAS,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAChG,CACF,CAAA;AACD,KAAK,CAAC,WAAW,GAAG,OAAO,CAAA;AAE3B,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAA4C,EAAE,EAAE,CAAC,CAC/F,aAAI,SAAS,EAAE,EAAE,CAAC,iDAAiD,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CAC/F,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAA8C,EAAE,EAAE,CAAC,CACvG,cAAK,SAAS,EAAE,EAAE,CAAC,0DAA0D,EAAE,SAAS,CAAC,KAAM,KAAK,GAAI,CACzG,CAAA;AAED,OAAO,EAAE,aAAa,EAAE,CAAA","sourcesContent":["import * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from './utils'\n\nconst alertVariants = cva('relative w-full rounded-lg border p-4 text-sm shadow-sm', {\n variants: {\n variant: {\n default: 'border-border bg-surface text-fg',\n destructive: 'border-danger/25 bg-danger-subtle text-danger',\n warning: 'border-warning/25 bg-warning-subtle text-warning',\n success: 'border-success/25 bg-success-subtle text-success',\n info: 'border-info/25 bg-info-subtle text-info',\n },\n },\n defaultVariants: { variant: 'default' },\n})\n\nexport type AlertProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>\n\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n ({ className, variant, ...props }, ref) => (\n <div ref={ref} role=\"alert\" className={cn(alertVariants({ variant }), className)} {...props} />\n ),\n)\nAlert.displayName = 'Alert'\n\nexport const AlertTitle = ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (\n <h5 className={cn('mb-1 font-semibold leading-tight tracking-tight', className)} {...props} />\n)\n\nexport const AlertDescription = ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (\n <div className={cn('text-sm leading-relaxed opacity-90 [&_p]:leading-relaxed', className)} {...props} />\n)\n\nexport { alertVariants }\n"]}
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+ export type AnimatedNumberProps = {
3
+ value: number;
4
+ from?: number;
5
+ duration?: number;
6
+ format?: (value: number) => string;
7
+ className?: string;
8
+ decimals?: number;
9
+ };
10
+ /** Byte-compatible sibling counter, with its default duration read from appkit's motion tokens. */
11
+ export declare function AnimatedNumber({ value, from, duration, format, className, decimals, }: AnimatedNumberProps): React.JSX.Element;
12
+ export declare function formatNumber(value: number, format?: (value: number) => string, decimals?: number): string;
13
+ //# sourceMappingURL=animated-number.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animated-number.d.ts","sourceRoot":"","sources":["../src/animated-number.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,IAAQ,EACR,QAAQ,EACR,MAAM,EACN,SAAS,EACT,QAAQ,GACT,EAAE,mBAAmB,qBAkCrB;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAClC,QAAQ,SAAI,GACX,MAAM,CAMR"}
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { animate, useMotionValue, useReducedMotion } from 'framer-motion';
5
+ function tokenDurationSeconds(token) {
6
+ if (typeof document === 'undefined')
7
+ return 0.32;
8
+ const raw = getComputedStyle(document.documentElement).getPropertyValue(token).trim();
9
+ if (raw.endsWith('ms'))
10
+ return Number.parseFloat(raw) / 1000;
11
+ if (raw.endsWith('s'))
12
+ return Number.parseFloat(raw);
13
+ return 0.32;
14
+ }
15
+ /** Byte-compatible sibling counter, with its default duration read from appkit's motion tokens. */
16
+ export function AnimatedNumber({ value, from = 0, duration, format, className, decimals, }) {
17
+ const reduce = useReducedMotion();
18
+ const motionValue = useMotionValue(reduce ? value : from);
19
+ const [displayValue, setDisplayValue] = React.useState(reduce ? value : from);
20
+ React.useEffect(() => {
21
+ if (reduce || document.visibilityState !== 'visible') {
22
+ setDisplayValue(value);
23
+ return;
24
+ }
25
+ const controls = animate(motionValue, value, {
26
+ duration: duration ?? tokenDurationSeconds('--duration-slow'),
27
+ ease: [0.22, 1, 0.36, 1],
28
+ onUpdate: setDisplayValue,
29
+ });
30
+ const onVisibilityChange = () => {
31
+ if (document.visibilityState === 'hidden') {
32
+ controls.stop();
33
+ motionValue.set(value);
34
+ setDisplayValue(value);
35
+ }
36
+ };
37
+ document.addEventListener('visibilitychange', onVisibilityChange);
38
+ return () => {
39
+ document.removeEventListener('visibilitychange', onVisibilityChange);
40
+ controls.stop();
41
+ };
42
+ }, [duration, motionValue, reduce, value]);
43
+ return (_jsx("span", { className: className, "aria-label": String(value), children: formatNumber(displayValue, format, decimals) }));
44
+ }
45
+ export function formatNumber(value, format, decimals = 0) {
46
+ if (format)
47
+ return format(value);
48
+ const rounded = decimals > 0 ? value.toFixed(decimals) : Math.round(value).toString();
49
+ const [integer, fraction] = rounded.split('.');
50
+ const grouped = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
51
+ return fraction ? `${grouped}.${fraction}` : grouped;
52
+ }
53
+ //# sourceMappingURL=animated-number.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animated-number.js","sourceRoot":"","sources":["../src/animated-number.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { animate, useMotionValue, useReducedMotion } from 'framer-motion'\n\nfunction tokenDurationSeconds(token: '--duration-fast' | '--duration-base' | '--duration-slow'): number {\n if (typeof document === 'undefined') return 0.32\n const raw = getComputedStyle(document.documentElement).getPropertyValue(token).trim()\n if (raw.endsWith('ms')) return Number.parseFloat(raw) / 1000\n if (raw.endsWith('s')) return Number.parseFloat(raw)\n return 0.32\n}\n\nexport type AnimatedNumberProps = {\n value: number\n from?: number\n duration?: number\n format?: (value: number) => string\n className?: string\n decimals?: number\n}\n\n/** Byte-compatible sibling counter, with its default duration read from appkit's motion tokens. */\nexport function AnimatedNumber({\n value,\n from = 0,\n duration,\n format,\n className,\n decimals,\n}: AnimatedNumberProps) {\n const reduce = useReducedMotion()\n const motionValue = useMotionValue(reduce ? value : from)\n const [displayValue, setDisplayValue] = React.useState(reduce ? value : from)\n\n React.useEffect(() => {\n if (reduce || document.visibilityState !== 'visible') {\n setDisplayValue(value)\n return\n }\n const controls = animate(motionValue, value, {\n duration: duration ?? tokenDurationSeconds('--duration-slow'),\n ease: [0.22, 1, 0.36, 1],\n onUpdate: setDisplayValue,\n })\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n controls.stop()\n motionValue.set(value)\n setDisplayValue(value)\n }\n }\n document.addEventListener('visibilitychange', onVisibilityChange)\n return () => {\n document.removeEventListener('visibilitychange', onVisibilityChange)\n controls.stop()\n }\n }, [duration, motionValue, reduce, value])\n\n return (\n <span className={className} aria-label={String(value)}>\n {formatNumber(displayValue, format, decimals)}\n </span>\n )\n}\n\nexport function formatNumber(\n value: number,\n format?: (value: number) => string,\n decimals = 0,\n): string {\n if (format) return format(value)\n const rounded = decimals > 0 ? value.toFixed(decimals) : Math.round(value).toString()\n const [integer, fraction] = rounded.split('.')\n const grouped = integer!.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n return fraction ? `${grouped}.${fraction}` : grouped\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ export type ApiParamDoc = {
3
+ name: string;
4
+ in?: 'query' | 'path' | 'header';
5
+ type?: string;
6
+ required?: boolean;
7
+ description?: string;
8
+ };
9
+ export type ApiEndpointDoc = {
10
+ method: string;
11
+ path: string;
12
+ summary?: string;
13
+ description?: string;
14
+ tag?: string;
15
+ permission?: string;
16
+ params?: ApiParamDoc[];
17
+ requestExample?: unknown;
18
+ responseExample?: unknown;
19
+ };
20
+ export declare function MethodBadge({ method, className }: {
21
+ method: string;
22
+ className?: string;
23
+ }): React.JSX.Element;
24
+ export declare function CodeBlock({ code, className }: {
25
+ code: string;
26
+ className?: string;
27
+ }): React.JSX.Element;
28
+ export declare function ApiEndpoint({ endpoint, baseUrl, apiKey, defaultOpen, }: {
29
+ endpoint: ApiEndpointDoc;
30
+ baseUrl?: string;
31
+ apiKey?: string;
32
+ defaultOpen?: boolean;
33
+ }): React.JSX.Element;
34
+ export declare function ApiReference({ endpoints, baseUrl, title, description, apiKey: initialApiKey, showBearerToken, className, }: {
35
+ endpoints: ApiEndpointDoc[];
36
+ baseUrl?: string;
37
+ title?: string;
38
+ description?: string;
39
+ apiKey?: string;
40
+ /** Show the optional bearer-token input beside the base URL. */
41
+ showBearerToken?: boolean;
42
+ className?: string;
43
+ }): React.JSX.Element;
44
+ //# sourceMappingURL=api-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-reference.d.ts","sourceRoot":"","sources":["../src/api-reference.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAY9B,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,WAAW,EAAE,CAAA;IACtB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAA;AAUD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBAaxF;AAED,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,qBAuBlF;AAID,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAO,EACP,MAAM,EACN,WAAmB,GACpB,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,qBAsIA;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,OAAO,EACP,KAAK,EACL,WAAW,EACX,MAAM,EAAE,aAAa,EACrB,eAAsB,EACtB,SAAS,GACV,EAAE;IACD,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,qBAiDA"}
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { AnimatePresence, motion } from 'framer-motion';
5
+ import { Check, ChevronRight, Copy, Loader2, Play } from 'lucide-react';
6
+ import { Badge } from './badge.js';
7
+ import { Button } from './button.js';
8
+ import { Input } from './input.js';
9
+ import { cn } from './utils.js';
10
+ const METHOD_COLORS = {
11
+ GET: 'bg-success-subtle text-success',
12
+ POST: 'bg-info-subtle text-info',
13
+ PUT: 'bg-warning-subtle text-warning',
14
+ PATCH: 'bg-warning-subtle text-warning',
15
+ DELETE: 'bg-danger-subtle text-danger',
16
+ };
17
+ export function MethodBadge({ method, className }) {
18
+ const m = method.toUpperCase();
19
+ return (_jsx("span", { className: cn('inline-flex min-w-14 justify-center rounded-md px-2 py-0.5 text-[11px] font-bold tracking-wide', METHOD_COLORS[m] ?? 'bg-bg-subtle text-fg-muted', className), children: m }));
20
+ }
21
+ export function CodeBlock({ code, className }) {
22
+ const [copied, setCopied] = React.useState(false);
23
+ const copy = () => {
24
+ navigator.clipboard?.writeText(code).then(() => {
25
+ setCopied(true);
26
+ setTimeout(() => setCopied(false), 1500);
27
+ });
28
+ };
29
+ return (_jsxs("div", { className: cn('group relative overflow-hidden rounded-lg border border-border bg-bg-subtle', className), children: [_jsx("button", { type: "button", onClick: copy, "aria-label": "Copy", className: "absolute right-2 top-2 rounded-md p-1.5 text-fg-subtle opacity-0 transition hover:bg-surface-hover hover:text-fg group-hover:opacity-100", children: copied ? _jsx(Check, { className: "size-4 text-success" }) : _jsx(Copy, { className: "size-4" }) }), _jsx("pre", { className: "app-scroll overflow-x-auto p-3 text-xs leading-relaxed", children: _jsx("code", { className: "font-mono text-fg", children: code }) })] }));
30
+ }
31
+ const json = (v) => JSON.stringify(v, null, 2);
32
+ export function ApiEndpoint({ endpoint, baseUrl, apiKey, defaultOpen = false, }) {
33
+ const [open, setOpen] = React.useState(defaultOpen);
34
+ const [sending, setSending] = React.useState(false);
35
+ const [result, setResult] = React.useState(null);
36
+ async function send() {
37
+ if (!baseUrl)
38
+ return;
39
+ setSending(true);
40
+ setResult(null);
41
+ try {
42
+ const hasBody = endpoint.method !== 'GET' && endpoint.requestExample !== undefined;
43
+ const res = await fetch(baseUrl + endpoint.path, {
44
+ method: endpoint.method,
45
+ headers: {
46
+ ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
47
+ ...(hasBody ? { 'Content-Type': 'application/json' } : {}),
48
+ },
49
+ body: hasBody ? JSON.stringify(endpoint.requestExample) : undefined,
50
+ });
51
+ const text = await res.text();
52
+ let body = text;
53
+ try {
54
+ body = json(JSON.parse(text));
55
+ }
56
+ catch {
57
+ // Preserve non-JSON response bodies verbatim.
58
+ }
59
+ setResult({ status: res.status, body });
60
+ }
61
+ catch (e) {
62
+ setResult({ status: 0, body: String(e) });
63
+ }
64
+ finally {
65
+ setSending(false);
66
+ }
67
+ }
68
+ return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-border bg-surface", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-surface-hover", children: [_jsx(MethodBadge, { method: endpoint.method }), _jsx("code", { className: "min-w-0 flex-1 truncate font-mono text-sm text-fg", children: endpoint.path }), endpoint.summary ? (_jsx("span", { className: "hidden min-w-0 truncate text-sm text-fg-muted md:block", children: endpoint.summary })) : null, _jsx(ChevronRight, { className: cn('size-4 shrink-0 text-fg-subtle transition-transform', open && 'rotate-90') })] }), _jsx(AnimatePresence, { initial: false, children: open ? (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: 'auto', opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] }, className: "overflow-hidden", children: _jsxs("div", { className: "space-y-4 border-t border-border px-4 py-4", children: [endpoint.description ? _jsx("p", { className: "text-sm text-fg-muted", children: endpoint.description }) : null, endpoint.permission ? (_jsxs("div", { className: "flex items-center gap-2 text-xs", children: [_jsx("span", { className: "text-fg-subtle", children: "Requires" }), _jsx(Badge, { variant: "secondary", className: "font-mono", children: endpoint.permission })] })) : null, endpoint.params?.length ? (_jsx("div", { className: "overflow-hidden rounded-lg border border-border", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-bg-subtle text-xs uppercase tracking-wide text-fg-muted", children: _jsxs("tr", { children: [_jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Param" }), _jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Type" }), _jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Description" })] }) }), _jsx("tbody", { className: "divide-y divide-border", children: endpoint.params.map((p) => (_jsxs("tr", { children: [_jsxs("td", { className: "px-3 py-2 font-mono text-fg", children: [p.name, p.required ? _jsx("span", { className: "text-danger", children: " *" }) : null, _jsx("span", { className: "ml-1 text-[11px] text-fg-subtle", children: p.in ?? 'query' })] }), _jsx("td", { className: "px-3 py-2 font-mono text-fg-muted", children: p.type ?? 'string' }), _jsx("td", { className: "px-3 py-2 text-fg-muted", children: p.description })] }, p.name))) })] }) })) : null, endpoint.requestExample !== undefined ? (_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs font-medium text-fg-muted", children: "Request body" }), _jsx(CodeBlock, { code: json(endpoint.requestExample) })] })) : null, endpoint.responseExample !== undefined ? (_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs font-medium text-fg-muted", children: "Response" }), _jsx(CodeBlock, { code: json(endpoint.responseExample) })] })) : null, baseUrl ? (_jsxs("div", { className: "space-y-2", children: [_jsxs(Button, { size: "sm", onClick: send, disabled: sending, children: [sending ? _jsx(Loader2, { className: "size-4 animate-spin" }) : _jsx(Play, { className: "size-4" }), "Send request"] }), result ? (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs", children: [_jsx("span", { className: "text-fg-subtle", children: "Response" }), _jsx(Badge, { variant: result.status >= 200 && result.status < 300 ? 'success' : 'destructive', children: result.status || 'error' })] }), _jsx(CodeBlock, { code: result.body })] })) : null] })) : null] }) })) : null })] }));
69
+ }
70
+ export function ApiReference({ endpoints, baseUrl, title, description, apiKey: initialApiKey, showBearerToken = true, className, }) {
71
+ const [apiKey, setApiKey] = React.useState(initialApiKey ?? '');
72
+ const groups = React.useMemo(() => {
73
+ const map = new Map();
74
+ for (const e of endpoints) {
75
+ const tag = e.tag ?? 'Endpoints';
76
+ (map.get(tag) ?? map.set(tag, []).get(tag)).push(e);
77
+ }
78
+ return [...map.entries()];
79
+ }, [endpoints]);
80
+ return (_jsxs("div", { className: cn('space-y-6', className), children: [title || description ? (_jsxs("div", { className: "space-y-1", children: [title ? _jsx("h2", { className: "text-lg font-semibold tracking-tight text-fg", children: title }) : null, description ? _jsx("p", { className: "text-sm text-fg-muted", children: description }) : null] })) : null, baseUrl ? (_jsxs("div", { className: "flex flex-col gap-2 rounded-xl border border-border bg-surface p-4 sm:flex-row sm:items-center", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs text-fg-subtle", children: "Base URL" }), _jsx("code", { className: "truncate font-mono text-sm text-fg", children: baseUrl })] }), showBearerToken ? (_jsx(Input, { value: apiKey, onChange: (e) => setApiKey(e.target.value), placeholder: "Bearer token (optional)", className: "sm:w-64" })) : null] })) : null, groups.map(([tag, eps]) => (_jsxs("section", { className: "space-y-2", children: [_jsx("h3", { className: "px-0.5 text-xs font-semibold uppercase tracking-wide text-fg-subtle", children: tag }), _jsx("div", { className: "space-y-2", children: eps.map((e, i) => (_jsx(ApiEndpoint, { endpoint: e, baseUrl: baseUrl, apiKey: apiKey || initialApiKey }, `${e.method}-${e.path}-${i}`))) })] }, tag)))] }));
81
+ }
82
+ //# sourceMappingURL=api-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-reference.js","sourceRoot":"","sources":["../src/api-reference.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { Check, ChevronRight, Copy, Loader2, Play } from 'lucide-react'\nimport { Badge } from './badge'\nimport { Button } from './button'\nimport { Input } from './input'\nimport { cn } from './utils'\n\n// Self-documenting API reference. Feed it route descriptions (structurally the\n// same as @braedonsaunders/appkit-api's ApiRouteDoc) and it renders a live, interactive reference\n// — method badges, params, code samples, and a real \"Send\" button.\n\nexport type ApiParamDoc = {\n name: string\n in?: 'query' | 'path' | 'header'\n type?: string\n required?: boolean\n description?: string\n}\n\nexport type ApiEndpointDoc = {\n method: string\n path: string\n summary?: string\n description?: string\n tag?: string\n permission?: string\n params?: ApiParamDoc[]\n requestExample?: unknown\n responseExample?: unknown\n}\n\nconst METHOD_COLORS: Record<string, string> = {\n GET: 'bg-success-subtle text-success',\n POST: 'bg-info-subtle text-info',\n PUT: 'bg-warning-subtle text-warning',\n PATCH: 'bg-warning-subtle text-warning',\n DELETE: 'bg-danger-subtle text-danger',\n}\n\nexport function MethodBadge({ method, className }: { method: string; className?: string }) {\n const m = method.toUpperCase()\n return (\n <span\n className={cn(\n 'inline-flex min-w-14 justify-center rounded-md px-2 py-0.5 text-[11px] font-bold tracking-wide',\n METHOD_COLORS[m] ?? 'bg-bg-subtle text-fg-muted',\n className,\n )}\n >\n {m}\n </span>\n )\n}\n\nexport function CodeBlock({ code, className }: { code: string; className?: string }) {\n const [copied, setCopied] = React.useState(false)\n const copy = () => {\n navigator.clipboard?.writeText(code).then(() => {\n setCopied(true)\n setTimeout(() => setCopied(false), 1500)\n })\n }\n return (\n <div className={cn('group relative overflow-hidden rounded-lg border border-border bg-bg-subtle', className)}>\n <button\n type=\"button\"\n onClick={copy}\n aria-label=\"Copy\"\n className=\"absolute right-2 top-2 rounded-md p-1.5 text-fg-subtle opacity-0 transition hover:bg-surface-hover hover:text-fg group-hover:opacity-100\"\n >\n {copied ? <Check className=\"size-4 text-success\" /> : <Copy className=\"size-4\" />}\n </button>\n <pre className=\"app-scroll overflow-x-auto p-3 text-xs leading-relaxed\">\n <code className=\"font-mono text-fg\">{code}</code>\n </pre>\n </div>\n )\n}\n\nconst json = (v: unknown) => JSON.stringify(v, null, 2)\n\nexport function ApiEndpoint({\n endpoint,\n baseUrl,\n apiKey,\n defaultOpen = false,\n}: {\n endpoint: ApiEndpointDoc\n baseUrl?: string\n apiKey?: string\n defaultOpen?: boolean\n}) {\n const [open, setOpen] = React.useState(defaultOpen)\n const [sending, setSending] = React.useState(false)\n const [result, setResult] = React.useState<{ status: number; body: string } | null>(null)\n\n async function send() {\n if (!baseUrl) return\n setSending(true)\n setResult(null)\n try {\n const hasBody = endpoint.method !== 'GET' && endpoint.requestExample !== undefined\n const res = await fetch(baseUrl + endpoint.path, {\n method: endpoint.method,\n headers: {\n ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n ...(hasBody ? { 'Content-Type': 'application/json' } : {}),\n },\n body: hasBody ? JSON.stringify(endpoint.requestExample) : undefined,\n })\n const text = await res.text()\n let body = text\n try {\n body = json(JSON.parse(text))\n } catch {\n // Preserve non-JSON response bodies verbatim.\n }\n setResult({ status: res.status, body })\n } catch (e) {\n setResult({ status: 0, body: String(e) })\n } finally {\n setSending(false)\n }\n }\n\n return (\n <div className=\"overflow-hidden rounded-xl border border-border bg-surface\">\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-surface-hover\"\n >\n <MethodBadge method={endpoint.method} />\n <code className=\"min-w-0 flex-1 truncate font-mono text-sm text-fg\">{endpoint.path}</code>\n {endpoint.summary ? (\n <span className=\"hidden min-w-0 truncate text-sm text-fg-muted md:block\">{endpoint.summary}</span>\n ) : null}\n <ChevronRight className={cn('size-4 shrink-0 text-fg-subtle transition-transform', open && 'rotate-90')} />\n </button>\n\n <AnimatePresence initial={false}>\n {open ? (\n <motion.div\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={{ duration: 0.2, ease: [0.22, 1, 0.36, 1] }}\n className=\"overflow-hidden\"\n >\n <div className=\"space-y-4 border-t border-border px-4 py-4\">\n {endpoint.description ? <p className=\"text-sm text-fg-muted\">{endpoint.description}</p> : null}\n {endpoint.permission ? (\n <div className=\"flex items-center gap-2 text-xs\">\n <span className=\"text-fg-subtle\">Requires</span>\n <Badge variant=\"secondary\" className=\"font-mono\">\n {endpoint.permission}\n </Badge>\n </div>\n ) : null}\n\n {endpoint.params?.length ? (\n <div className=\"overflow-hidden rounded-lg border border-border\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-subtle text-xs uppercase tracking-wide text-fg-muted\">\n <tr>\n <th className=\"px-3 py-2 text-left font-medium\">Param</th>\n <th className=\"px-3 py-2 text-left font-medium\">Type</th>\n <th className=\"px-3 py-2 text-left font-medium\">Description</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {endpoint.params.map((p) => (\n <tr key={p.name}>\n <td className=\"px-3 py-2 font-mono text-fg\">\n {p.name}\n {p.required ? <span className=\"text-danger\"> *</span> : null}\n <span className=\"ml-1 text-[11px] text-fg-subtle\">{p.in ?? 'query'}</span>\n </td>\n <td className=\"px-3 py-2 font-mono text-fg-muted\">{p.type ?? 'string'}</td>\n <td className=\"px-3 py-2 text-fg-muted\">{p.description}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : null}\n\n {endpoint.requestExample !== undefined ? (\n <div className=\"space-y-1.5\">\n <div className=\"text-xs font-medium text-fg-muted\">Request body</div>\n <CodeBlock code={json(endpoint.requestExample)} />\n </div>\n ) : null}\n {endpoint.responseExample !== undefined ? (\n <div className=\"space-y-1.5\">\n <div className=\"text-xs font-medium text-fg-muted\">Response</div>\n <CodeBlock code={json(endpoint.responseExample)} />\n </div>\n ) : null}\n\n {baseUrl ? (\n <div className=\"space-y-2\">\n <Button size=\"sm\" onClick={send} disabled={sending}>\n {sending ? <Loader2 className=\"size-4 animate-spin\" /> : <Play className=\"size-4\" />}\n Send request\n </Button>\n {result ? (\n <div className=\"space-y-1.5\">\n <div className=\"flex items-center gap-2 text-xs\">\n <span className=\"text-fg-subtle\">Response</span>\n <Badge variant={result.status >= 200 && result.status < 300 ? 'success' : 'destructive'}>\n {result.status || 'error'}\n </Badge>\n </div>\n <CodeBlock code={result.body} />\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n </motion.div>\n ) : null}\n </AnimatePresence>\n </div>\n )\n}\n\nexport function ApiReference({\n endpoints,\n baseUrl,\n title,\n description,\n apiKey: initialApiKey,\n showBearerToken = true,\n className,\n}: {\n endpoints: ApiEndpointDoc[]\n baseUrl?: string\n title?: string\n description?: string\n apiKey?: string\n /** Show the optional bearer-token input beside the base URL. */\n showBearerToken?: boolean\n className?: string\n}) {\n const [apiKey, setApiKey] = React.useState(initialApiKey ?? '')\n const groups = React.useMemo(() => {\n const map = new Map<string, ApiEndpointDoc[]>()\n for (const e of endpoints) {\n const tag = e.tag ?? 'Endpoints'\n ;(map.get(tag) ?? map.set(tag, []).get(tag)!).push(e)\n }\n return [...map.entries()]\n }, [endpoints])\n\n return (\n <div className={cn('space-y-6', className)}>\n {title || description ? (\n <div className=\"space-y-1\">\n {title ? <h2 className=\"text-lg font-semibold tracking-tight text-fg\">{title}</h2> : null}\n {description ? <p className=\"text-sm text-fg-muted\">{description}</p> : null}\n </div>\n ) : null}\n\n {baseUrl ? (\n <div className=\"flex flex-col gap-2 rounded-xl border border-border bg-surface p-4 sm:flex-row sm:items-center\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-xs text-fg-subtle\">Base URL</div>\n <code className=\"truncate font-mono text-sm text-fg\">{baseUrl}</code>\n </div>\n {showBearerToken ? (\n <Input\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n placeholder=\"Bearer token (optional)\"\n className=\"sm:w-64\"\n />\n ) : null}\n </div>\n ) : null}\n\n {groups.map(([tag, eps]) => (\n <section key={tag} className=\"space-y-2\">\n <h3 className=\"px-0.5 text-xs font-semibold uppercase tracking-wide text-fg-subtle\">{tag}</h3>\n <div className=\"space-y-2\">\n {eps.map((e, i) => (\n <ApiEndpoint key={`${e.method}-${e.path}-${i}`} endpoint={e} baseUrl={baseUrl} apiKey={apiKey || initialApiKey} />\n ))}\n </div>\n </section>\n ))}\n </div>\n )\n}\n"]}
package/app-shell.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ import { type SidebarNavGroup } from './sidebar-nav.js';
3
+ import type { LinkRender } from './settings-layout.js';
4
+ export type AppShellNavigationMode = 'topbar' | 'sidebar';
5
+ /**
6
+ * Generalized application shell. One serialized navigation registry drives the
7
+ * dropdown topbar, shared collapsible rail,
8
+ * the mobile drawer, and the native-style mobile tab bar.
9
+ */
10
+ export declare function AppShell({ groups, pathname, brand, sidebarFooter, sidebarCollapsedFooter, mobileFooter, headerMiddle, header, banner, defaultCollapsed, navigationMode, linkRender, moreLabel, openNavigationLabel, menuLabel, primaryNavigationLabel, topNavigationItemClassName, topNavigationActiveItemClassName, topNavigationInactiveItemClassName, mobileTabCount, showMobileTabs, children, }: {
11
+ groups: SidebarNavGroup[];
12
+ pathname: string;
13
+ brand?: React.ReactNode;
14
+ sidebarFooter?: React.ReactNode;
15
+ sidebarCollapsedFooter?: React.ReactNode;
16
+ mobileFooter?: React.ReactNode;
17
+ /** Search, tenant, role, or other application-owned middle controls. */
18
+ headerMiddle?: React.ReactNode;
19
+ /** Right-side utility actions and account menu. */
20
+ header?: React.ReactNode;
21
+ banner?: React.ReactNode;
22
+ defaultCollapsed?: boolean;
23
+ navigationMode?: AppShellNavigationMode;
24
+ linkRender?: LinkRender;
25
+ moreLabel?: string;
26
+ openNavigationLabel?: string;
27
+ menuLabel?: string;
28
+ primaryNavigationLabel?: string;
29
+ /** Shared typography/spacing overrides for topbar navigation items. */
30
+ topNavigationItemClassName?: string;
31
+ /** Overrides applied only to the active topbar navigation item. */
32
+ topNavigationActiveItemClassName?: string;
33
+ /** Overrides applied only to inactive topbar navigation items. */
34
+ topNavigationInactiveItemClassName?: string;
35
+ mobileTabCount?: number;
36
+ showMobileTabs?: boolean;
37
+ children: React.ReactNode;
38
+ }): React.JSX.Element;
39
+ //# sourceMappingURL=app-shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-shell.d.ts","sourceRoot":"","sources":["../src/app-shell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,eAAe,CAAA;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAInD,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEzD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,QAAQ,EACR,KAAK,EACL,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,MAAM,EACN,gBAAwB,EACxB,cAAyB,EACzB,UAAU,EACV,SAAkB,EAClB,mBAAuC,EACvC,SAAkB,EAClB,sBAA6C,EAC7C,0BAA0B,EAC1B,gCAAgC,EAChC,kCAAkC,EAClC,cAAkB,EAClB,cAAqB,EACrB,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B,sBAAsB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxC,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,wEAAwE;IACxE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,mDAAmD;IACnD,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,cAAc,CAAC,EAAE,sBAAsB,CAAA;IACvC,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,uEAAuE;IACvE,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,mEAAmE;IACnE,gCAAgC,CAAC,EAAE,MAAM,CAAA;IACzC,kEAAkE;IAClE,kCAAkC,CAAC,EAAE,MAAM,CAAA;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,qBAmFA"}
package/app-shell.js ADDED
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Menu } from 'lucide-react';
5
+ import { AppSidebar } from './app-sidebar.js';
6
+ import { Drawer } from './drawer.js';
7
+ import { MobileTabBar } from './mobile-tab-bar.js';
8
+ import { SidebarNav } from './sidebar-nav.js';
9
+ import { TopNav } from './top-nav.js';
10
+ import { cn } from './utils.js';
11
+ /**
12
+ * Generalized application shell. One serialized navigation registry drives the
13
+ * dropdown topbar, shared collapsible rail,
14
+ * the mobile drawer, and the native-style mobile tab bar.
15
+ */
16
+ export function AppShell({ groups, pathname, brand, sidebarFooter, sidebarCollapsedFooter, mobileFooter, headerMiddle, header, banner, defaultCollapsed = false, navigationMode = 'topbar', linkRender, moreLabel = 'More', openNavigationLabel = 'Open navigation', menuLabel = 'Menu', primaryNavigationLabel = 'Primary navigation', topNavigationItemClassName, topNavigationActiveItemClassName, topNavigationInactiveItemClassName, mobileTabCount = 4, showMobileTabs = true, children, }) {
17
+ const [mobileOpen, setMobileOpen] = React.useState(false);
18
+ const topbar = navigationMode === 'topbar';
19
+ return (_jsxs("div", { className: "flex h-screen overflow-hidden", children: [topbar ? null : (_jsx(AppSidebar, { groups: groups, pathname: pathname, brand: brand, footer: sidebarFooter, collapsedFooter: sidebarCollapsedFooter, defaultCollapsed: defaultCollapsed, linkRender: linkRender })), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col overflow-hidden [padding-top:env(safe-area-inset-top)]", children: [banner, _jsxs("header", { className: "flex h-14 shrink-0 items-center gap-2 border-b border-border bg-surface px-3 sm:gap-3 sm:px-4", children: [_jsx("button", { type: "button", onClick: () => setMobileOpen(true), "aria-label": openNavigationLabel, className: "grid size-9 shrink-0 place-items-center rounded-md border border-border text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg lg:hidden", children: _jsx(Menu, { size: 18 }) }), topbar ? (_jsxs(_Fragment, { children: [brand ? _jsx("div", { className: "hidden shrink-0 lg:block", children: brand }) : null, _jsx(TopNav, { groups: groups, pathname: pathname, linkRender: linkRender, moreLabel: moreLabel, ariaLabel: primaryNavigationLabel, itemClassName: topNavigationItemClassName, activeItemClassName: topNavigationActiveItemClassName, inactiveItemClassName: topNavigationInactiveItemClassName }), _jsx("div", { className: "flex-1 lg:hidden" }), headerMiddle] })) : (_jsx("div", { className: cn('min-w-0 flex-1', !headerMiddle && 'hidden lg:block'), children: headerMiddle })), header ? _jsx("div", { className: "flex shrink-0 items-center gap-1", children: header }) : null] }), _jsx("main", { className: "flex min-h-0 flex-1 flex-col overflow-hidden bg-bg-subtle", children: children }), showMobileTabs ? (_jsx(MobileTabBar, { groups: groups, pathname: pathname, onOpenMenu: () => setMobileOpen(true), linkRender: linkRender, tabCount: mobileTabCount, menuLabel: menuLabel, ariaLabel: primaryNavigationLabel })) : null] }), _jsx(Drawer, { open: mobileOpen, onClose: () => setMobileOpen(false), side: "left", size: "sm", title: brand ?? menuLabel, disableFullscreen: true, bodyClassName: "min-h-0 flex-1 overflow-hidden p-0", footer: mobileFooter ?? sidebarFooter, children: _jsx("div", { onClick: () => setMobileOpen(false), className: "flex h-full flex-col", children: _jsx(SidebarNav, { groups: groups, pathname: pathname, linkRender: linkRender }) }) })] }));
20
+ }
21
+ //# sourceMappingURL=app-shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-shell.js","sourceRoot":"","sources":["../src/app-shell.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Menu } from 'lucide-react'\nimport { AppSidebar } from './app-sidebar'\nimport { Drawer } from './drawer'\nimport { MobileTabBar } from './mobile-tab-bar'\nimport { SidebarNav, type SidebarNavGroup } from './sidebar-nav'\nimport type { LinkRender } from './settings-layout'\nimport { TopNav } from './top-nav'\nimport { cn } from './utils'\n\nexport type AppShellNavigationMode = 'topbar' | 'sidebar'\n\n/**\n * Generalized application shell. One serialized navigation registry drives the\n * dropdown topbar, shared collapsible rail,\n * the mobile drawer, and the native-style mobile tab bar.\n */\nexport function AppShell({\n groups,\n pathname,\n brand,\n sidebarFooter,\n sidebarCollapsedFooter,\n mobileFooter,\n headerMiddle,\n header,\n banner,\n defaultCollapsed = false,\n navigationMode = 'topbar',\n linkRender,\n moreLabel = 'More',\n openNavigationLabel = 'Open navigation',\n menuLabel = 'Menu',\n primaryNavigationLabel = 'Primary navigation',\n topNavigationItemClassName,\n topNavigationActiveItemClassName,\n topNavigationInactiveItemClassName,\n mobileTabCount = 4,\n showMobileTabs = true,\n children,\n}: {\n groups: SidebarNavGroup[]\n pathname: string\n brand?: React.ReactNode\n sidebarFooter?: React.ReactNode\n sidebarCollapsedFooter?: React.ReactNode\n mobileFooter?: React.ReactNode\n /** Search, tenant, role, or other application-owned middle controls. */\n headerMiddle?: React.ReactNode\n /** Right-side utility actions and account menu. */\n header?: React.ReactNode\n banner?: React.ReactNode\n defaultCollapsed?: boolean\n navigationMode?: AppShellNavigationMode\n linkRender?: LinkRender\n moreLabel?: string\n openNavigationLabel?: string\n menuLabel?: string\n primaryNavigationLabel?: string\n /** Shared typography/spacing overrides for topbar navigation items. */\n topNavigationItemClassName?: string\n /** Overrides applied only to the active topbar navigation item. */\n topNavigationActiveItemClassName?: string\n /** Overrides applied only to inactive topbar navigation items. */\n topNavigationInactiveItemClassName?: string\n mobileTabCount?: number\n showMobileTabs?: boolean\n children: React.ReactNode\n}) {\n const [mobileOpen, setMobileOpen] = React.useState(false)\n const topbar = navigationMode === 'topbar'\n\n return (\n <div className=\"flex h-screen overflow-hidden\">\n {topbar ? null : (\n <AppSidebar\n groups={groups}\n pathname={pathname}\n brand={brand}\n footer={sidebarFooter}\n collapsedFooter={sidebarCollapsedFooter}\n defaultCollapsed={defaultCollapsed}\n linkRender={linkRender}\n />\n )}\n\n <div className=\"flex min-w-0 flex-1 flex-col overflow-hidden [padding-top:env(safe-area-inset-top)]\">\n {banner}\n <header className=\"flex h-14 shrink-0 items-center gap-2 border-b border-border bg-surface px-3 sm:gap-3 sm:px-4\">\n <button\n type=\"button\"\n onClick={() => setMobileOpen(true)}\n aria-label={openNavigationLabel}\n className=\"grid size-9 shrink-0 place-items-center rounded-md border border-border text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg lg:hidden\"\n >\n <Menu size={18} />\n </button>\n\n {topbar ? (\n <>\n {brand ? <div className=\"hidden shrink-0 lg:block\">{brand}</div> : null}\n <TopNav\n groups={groups}\n pathname={pathname}\n linkRender={linkRender}\n moreLabel={moreLabel}\n ariaLabel={primaryNavigationLabel}\n itemClassName={topNavigationItemClassName}\n activeItemClassName={topNavigationActiveItemClassName}\n inactiveItemClassName={topNavigationInactiveItemClassName}\n />\n <div className=\"flex-1 lg:hidden\" />\n {headerMiddle}\n </>\n ) : (\n <div className={cn('min-w-0 flex-1', !headerMiddle && 'hidden lg:block')}>{headerMiddle}</div>\n )}\n {header ? <div className=\"flex shrink-0 items-center gap-1\">{header}</div> : null}\n </header>\n\n <main className=\"flex min-h-0 flex-1 flex-col overflow-hidden bg-bg-subtle\">{children}</main>\n\n {showMobileTabs ? (\n <MobileTabBar\n groups={groups}\n pathname={pathname}\n onOpenMenu={() => setMobileOpen(true)}\n linkRender={linkRender}\n tabCount={mobileTabCount}\n menuLabel={menuLabel}\n ariaLabel={primaryNavigationLabel}\n />\n ) : null}\n </div>\n\n <Drawer\n open={mobileOpen}\n onClose={() => setMobileOpen(false)}\n side=\"left\"\n size=\"sm\"\n title={brand ?? menuLabel}\n disableFullscreen\n bodyClassName=\"min-h-0 flex-1 overflow-hidden p-0\"\n footer={mobileFooter ?? sidebarFooter}\n >\n <div onClick={() => setMobileOpen(false)} className=\"flex h-full flex-col\">\n <SidebarNav groups={groups} pathname={pathname} linkRender={linkRender} />\n </div>\n </Drawer>\n </div>\n )\n}\n"]}