History

feat: shadcn/ui monorepo setup with all components

- packages/ui (@executor/ui): shared UI package with shadcn components - 56 components installed (button, card, dialog, sidebar, etc.) - lib/utils.ts with cn() helper - globals.css with light/dark theme (oklch colors, auto detection) - DM Sans + Instrument Serif + JetBrains Mono fonts - components.json in both packages/ui and apps/web for CLI support - Tailwind v4 with @source directives for cross-package class scanning - Fixed source namespace detection (use last tag, filter by 'openapi' presence) - apps/web imports globals.css from @executor/ui/globals.css

Rhys Sullivan committed 7 months ago

Commit e2587321 parent cb0197f69 files · +7139 −9Browse files

Unified · Split

69 files
  1. apps/web/components.json +20 −0
  2. apps/web/package.json +1 −0
  3. apps/web/src/main.tsx +1 −1
  4. apps/web/src/pages/sources.tsx +4 −4
  5. apps/web/src/shell.tsx +3 −2
  6. bun.lock +306 −0
  7. package.json +1 −0
  8. packages/ui/components.json +20 −0
  9. packages/ui/package.json +42 −0
  10. packages/ui/src/components/accordion.tsx +64 −0
  11. packages/ui/src/components/alert-dialog.tsx +196 −0
  12. packages/ui/src/components/alert.tsx +66 −0
  13. packages/ui/src/components/aspect-ratio.tsx +11 −0
  14. packages/ui/src/components/avatar.tsx +107 −0
  15. packages/ui/src/components/badge.tsx +48 −0
  16. packages/ui/src/components/breadcrumb.tsx +109 −0
  17. packages/ui/src/components/button-group.tsx +83 −0
  18. packages/ui/src/components/button.tsx +64 −0
  19. packages/ui/src/components/calendar.tsx +218 −0
  20. packages/ui/src/components/card.tsx +92 −0
  21. packages/ui/src/components/carousel.tsx +241 −0
  22. packages/ui/src/components/chart.tsx +372 −0
  23. packages/ui/src/components/checkbox.tsx +32 −0
  24. packages/ui/src/components/collapsible.tsx +31 −0
  25. packages/ui/src/components/combobox.tsx +310 −0
  26. packages/ui/src/components/command.tsx +184 −0
  27. packages/ui/src/components/context-menu.tsx +252 −0
  28. packages/ui/src/components/dialog.tsx +156 −0
  29. packages/ui/src/components/direction.tsx +22 −0
  30. packages/ui/src/components/drawer.tsx +133 −0
  31. packages/ui/src/components/dropdown-menu.tsx +257 −0
  32. packages/ui/src/components/empty.tsx +104 −0
  33. packages/ui/src/components/field.tsx +248 −0
  34. packages/ui/src/components/form.tsx +165 −0
  35. packages/ui/src/components/hover-card.tsx +42 −0
  36. packages/ui/src/components/input-group.tsx +168 −0
  37. packages/ui/src/components/input-otp.tsx +77 −0
  38. packages/ui/src/components/input.tsx +21 −0
  39. packages/ui/src/components/item.tsx +193 −0
  40. packages/ui/src/components/kbd.tsx +28 −0
  41. packages/ui/src/components/label.tsx +22 −0
  42. packages/ui/src/components/menubar.tsx +276 −0
  43. packages/ui/src/components/native-select.tsx +53 −0
  44. packages/ui/src/components/navigation-menu.tsx +168 −0
  45. packages/ui/src/components/pagination.tsx +127 −0
  46. packages/ui/src/components/popover.tsx +87 −0
  47. packages/ui/src/components/progress.tsx +31 −0
  48. packages/ui/src/components/radio-group.tsx +43 −0
  49. packages/ui/src/components/resizable.tsx +53 −0
  50. packages/ui/src/components/scroll-area.tsx +56 −0
  51. packages/ui/src/components/select.tsx +190 −0
  52. packages/ui/src/components/separator.tsx +26 −0
  53. packages/ui/src/components/sheet.tsx +143 −0
  54. packages/ui/src/components/sidebar.tsx +724 −0
  55. packages/ui/src/components/skeleton.tsx +13 −0
  56. packages/ui/src/components/slider.tsx +61 −0
  57. packages/ui/src/components/sonner.tsx +40 −0
  58. packages/ui/src/components/spinner.tsx +16 −0
  59. packages/ui/src/components/switch.tsx +33 −0
  60. packages/ui/src/components/table.tsx +116 −0
  61. packages/ui/src/components/tabs.tsx +89 −0
  62. packages/ui/src/components/textarea.tsx +18 −0
  63. packages/ui/src/components/toggle-group.tsx +83 −0
  64. packages/ui/src/components/toggle.tsx +47 −0
  65. packages/ui/src/components/tooltip.tsx +55 −0
  66. packages/ui/src/hooks/use-mobile.ts +19 −0
  67. packages/ui/src/lib/utils.ts +6 −0
  68. packages/ui/src/styles/globals.css +27 −2
  69. packages/ui/tsconfig.json +25 −0
apps/web/components.json+20−0
@@ -0,0 +1,20 @@
1+{
2+ "$schema": "https://ui.shadcn.com/schema.json",
3+ "style": "new-york",
4+ "rsc": false,
5+ "tsx": true,
6+ "tailwind": {
7+ "config": "",
8+ "css": "../../packages/ui/src/styles/globals.css",
9+ "baseColor": "neutral",
10+ "cssVariables": true
11+ },
12+ "iconLibrary": "lucide",
13+ "aliases": {
14+ "components": "@/components",
15+ "hooks": "@/hooks",
16+ "lib": "@/lib",
17+ "utils": "@executor/ui/lib/utils",
18+ "ui": "@executor/ui/components"
19+ }
20+}
apps/web/package.json+1−0
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 "type": "module",
66 "dependencies": {
77 "@executor/plugin-openapi": "workspace:*",
8+ "@executor/ui": "workspace:*",
89 "tailwindcss": "^4.1.10",
910 "@executor/react": "workspace:*",
1011 "@executor/server": "workspace:*",
apps/web/src/main.tsx+1−1
@@ -1,6 +1,6 @@
11 import React from "react";
22 import { createRoot } from "react-dom/client";
3−import "./styles.css";
3+import "@executor/ui/globals.css";
44 import { App } from "./App";
55
66 const root = createRoot(document.getElementById("root")!);
apps/web/src/pages/sources.tsx+4−4
@@ -22,10 +22,10 @@ export function SourcesPage() { · 21 unmodified lines
2222 if (tools._tag !== "Success") return [];
2323 const namespaces = new Map<string, number>();
2424 for (const tool of tools.value) {
25− const ns = tool.tags.find(
26− (t) => t !== "openapi" && !sourcePlugins.some((p) => p.key === t),
27− );
28− if (ns) {
25+ if (!tool.tags.includes("openapi")) continue;
26+ // Namespace is the last tag (after op tags and "openapi")
27+ const ns = tool.tags[tool.tags.length - 1];
28+ if (ns && ns !== "openapi") {
2929 namespaces.set(ns, (namespaces.get(ns) ?? 0) + 1);
3030 }
3131 }
apps/web/src/shell.tsx+3−2
@@ -34,8 +34,9 @@ function SourceList() { · 33 unmodified lines
3434 if (tools._tag !== "Success") return [];
3535 const namespaces = new Map<string, number>();
3636 for (const tool of tools.value) {
37− const ns = tool.tags.find((t) => t !== "openapi");
38− if (ns && ns !== tool.tags[0]) {
37+ if (!tool.tags.includes("openapi")) continue;
38+ const ns = tool.tags[tool.tags.length - 1];
39+ if (ns && ns !== "openapi") {
3940 namespaces.set(ns, (namespaces.get(ns) ?? 0) + 1);
4041 }
4142 }
bun.lock+306−0
@@ -34,6 +34,7 @@ · 33 unmodified lines
3434 "@executor/plugin-openapi": "workspace:*",
3535 "@executor/react": "workspace:*",
3636 "@executor/server": "workspace:*",
37+ "@executor/ui": "workspace:*",
3738 "@tanstack/react-router": "^1.168.10",
3839 "react": "^19.1.0",
3940 "react-dom": "^19.1.0",
@@ -190,6 +191,37 @@ · 150 unmodified lines
190191 "react",
191192 ],
192193 },
194+ "packages/ui": {
195+ "name": "@executor/ui",
196+ "version": "0.0.0",
197+ "dependencies": {
198+ "@base-ui/react": "^1.3.0",
199+ "@hookform/resolvers": "^5.2.2",
200+ "class-variance-authority": "^0.7.1",
201+ "clsx": "^2.1.1",
202+ "cmdk": "^1.1.1",
203+ "date-fns": "^4.1.0",
204+ "embla-carousel-react": "^8.6.0",
205+ "input-otp": "^1.4.2",
206+ "lucide-react": "^1.7.0",
207+ "next-themes": "^0.4.6",
208+ "radix-ui": "^1.4.3",
209+ "react": "^19.1.0",
210+ "react-day-picker": "^9.14.0",
211+ "react-hook-form": "^7.72.0",
212+ "react-resizable-panels": "^4",
213+ "recharts": "3.8.0",
214+ "sonner": "^2.0.7",
215+ "tailwind-merge": "^3.3.0",
216+ "vaul": "^1.1.2",
217+ "zod": "^4.3.6",
218+ },
219+ "devDependencies": {
220+ "@types/react": "^19.1.0",
221+ "tailwindcss": "^4.1.10",
222+ "typescript": "^5.9.3",
223+ },
224+ },
193225 },
194226 "catalog": {
195227 "@effect/cluster": "^0.58.0",
@@ -243,12 +275,20 @@ · 47 unmodified lines
243275
244276 "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
245277
278+ "@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
279+
246280 "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
247281
248282 "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
249283
250284 "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
251285
286+ "@base-ui/react": ["@base-ui/react@1.3.0", "", { "dependencies": { "@babel/runtime": "^7.28.6", "@base-ui/utils": "0.2.6", "@floating-ui/react-dom": "^2.1.8", "@floating-ui/utils": "^0.2.11", "tabbable": "^6.4.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-FwpKqZbPz14AITp1CVgf4AjhKPe1OeeVKSBMdgD10zbFlj3QSWelmtCMLi2+/PFZZcIm3l87G7rwtCZJwHyXWA=="],
287+
288+ "@base-ui/utils": ["@base-ui/utils@0.2.6", "", { "dependencies": { "@babel/runtime": "^7.28.6", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-yQ+qeuqohwhsNpoYDqqXaLllYAkPCP4vYdDrVo8FQXaAPfHWm1pG/Vm+jmGTA5JFS0BAIjookyapuJFY8F9PIw=="],
289+
290+ "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
291+
252292 "@effect-atom/atom": ["@effect-atom/atom@0.5.3", "", { "peerDependencies": { "@effect/experimental": "^0.58.0", "@effect/platform": "^0.94.2", "@effect/rpc": "^0.73.0", "effect": "^3.19.15" } }, "sha512-TRZv/i+YT3TtnN0oFORJqXdxSs1fc7lrJlH+1xZvDFyjC9hgoVnrcKbeZsDFmr6r0wYRqVo7U3IftxiQNjpNZA=="],
253293
254294 "@effect-atom/atom-react": ["@effect-atom/atom-react@0.5.0", "", { "dependencies": { "@effect-atom/atom": "^0.5.0" }, "peerDependencies": { "effect": "^3.19", "react": ">=18 <20", "scheduler": "*" } }, "sha512-aFfjWi4rEJCqfM12Oi36/EKaDm/W6n4/N6yM5vL0t/QozKhJhK05rQL/GY4XMxlH2eqkQ4ih8jBQa3Yyp0Fiqw=="],
@@ -343,8 +383,20 @@ · 88 unmodified lines
343383
344384 "@executor/server": ["@executor/server@workspace:apps/server"],
345385
386+ "@executor/ui": ["@executor/ui@workspace:packages/ui"],
387+
346388 "@executor/web": ["@executor/web@workspace:apps/web"],
347389
390+ "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="],
391+
392+ "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="],
393+
394+ "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="],
395+
396+ "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
397+
398+ "@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="],
399+
348400 "@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.31.0", "", {}, "sha512-1yrgvXlmXH2oNj3eFTrkwacGJbmM0crwipA3ohCrjv52gBeDaD7PsTvFYinlAnqU8iPME3LGP437yk05a2oejw=="],
349401
350402 "@jitl/quickjs-wasmfile-debug-asyncify": ["@jitl/quickjs-wasmfile-debug-asyncify@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-YkdzQdr1uaftFhgEnTRjTTZHk2SFZdpWO7XhOmRVbi6CEVsH9g5oNF8Ta1q3OuSJHRwwT8YsuR1YzEiEIJEk6w=="],
@@ -469,6 +521,128 @@ · 118 unmodified lines
469521
470522 "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
471523
524+ "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
525+
526+ "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
527+
528+ "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.1.7", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A=="],
529+
530+ "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="],
531+
532+ "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="],
533+
534+ "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="],
535+
536+ "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g=="],
537+
538+ "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.10", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog=="],
539+
540+ "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="],
541+
542+ "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="],
543+
544+ "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="],
545+
546+ "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="],
547+
548+ "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="],
549+
550+ "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww=="],
551+
552+ "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="],
553+
554+ "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="],
555+
556+ "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="],
557+
558+ "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="],
559+
560+ "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="],
561+
562+ "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="],
563+
564+ "@radix-ui/react-form": ["@radix-ui/react-form@0.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ=="],
565+
566+ "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="],
567+
568+ "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="],
569+
570+ "@radix-ui/react-label": ["@radix-ui/react-label@2.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ=="],
571+
572+ "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="],
573+
574+ "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA=="],
575+
576+ "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="],
577+
578+ "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.1.8", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg=="],
579+
580+ "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw=="],
581+
582+ "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="],
583+
584+ "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="],
585+
586+ "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="],
587+
588+ "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="],
589+
590+ "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="],
591+
592+ "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.7", "", { "dependencies": { "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg=="],
593+
594+ "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="],
595+
596+ "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="],
597+
598+ "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="],
599+
600+ "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="],
601+
602+ "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA=="],
603+
604+ "@radix-ui/react-slider": ["@radix-ui/react-slider@1.3.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw=="],
605+
606+ "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
607+
608+ "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="],
609+
610+ "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="],
611+
612+ "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g=="],
613+
614+ "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ=="],
615+
616+ "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q=="],
617+
618+ "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-toggle-group": "1.1.11" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg=="],
619+
620+ "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="],
621+
622+ "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="],
623+
624+ "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="],
625+
626+ "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="],
627+
628+ "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="],
629+
630+ "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.0", "", { "dependencies": { "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA=="],
631+
632+ "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
633+
634+ "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="],
635+
636+ "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="],
637+
638+ "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="],
639+
640+ "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="],
641+
642+ "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
643+
644+ "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="],
645+
472646 "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
473647
474648 "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.1", "", { "os": "android", "cpu": "arm" }, "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA=="],
@@ -523,6 +697,10 @@ · 48 unmodified lines
523697
524698 "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
525699
700+ "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
701+
702+ "@tabby_ai/hijri-converter": ["@tabby_ai/hijri-converter@1.0.5", "", {}, "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ=="],
703+
526704 "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="],
527705
528706 "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="],
@@ -585,6 +763,24 @@ · 56 unmodified lines
585763
586764 "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
587765
766+ "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
767+
768+ "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
769+
770+ "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
771+
772+ "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
773+
774+ "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
775+
776+ "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
777+
778+ "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="],
779+
780+ "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
781+
782+ "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
783+
588784 "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
589785
590786 "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
@@ -597,6 +793,8 @@ · 6 unmodified lines
597793
598794 "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
599795
796+ "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="],
797+
600798 "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
601799
602800 "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
@@ -621,6 +819,8 @@ · 18 unmodified lines
621819
622820 "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
623821
822+ "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
823+
624824 "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
625825
626826 "baseline-browser-mapping": ["baseline-browser-mapping@2.10.13", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw=="],
@@ -639,32 +839,78 @@ · 12 unmodified lines
639839
640840 "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="],
641841
842+ "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
843+
844+ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
845+
846+ "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="],
847+
642848 "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
643849
644850 "cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="],
645851
646852 "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
647853
854+ "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
855+
856+ "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
857+
858+ "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
859+
860+ "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
861+
862+ "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
863+
864+ "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
865+
866+ "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
867+
868+ "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
869+
870+ "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
871+
872+ "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
873+
874+ "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
875+
876+ "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="],
877+
878+ "date-fns-jalali": ["date-fns-jalali@4.1.0-0", "", {}, "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg=="],
879+
648880 "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
649881
882+ "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
883+
650884 "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
651885
652886 "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
653887
888+ "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
889+
654890 "effect": ["effect@3.21.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ=="],
655891
656892 "electron-to-chromium": ["electron-to-chromium@1.5.330", "", {}, "sha512-jFNydB5kFtYUobh4IkWUnXeyDbjf/r9gcUEXe1xcrcUxIGfTdzPXA+ld6zBRbwvgIGVzDll/LTIiDztEtckSnA=="],
657893
894+ "embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="],
895+
896+ "embla-carousel-react": ["embla-carousel-react@8.6.0", "", { "dependencies": { "embla-carousel": "8.6.0", "embla-carousel-reactive-utils": "8.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA=="],
897+
898+ "embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A=="],
899+
658900 "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="],
659901
660902 "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
661903
904+ "es-toolkit": ["es-toolkit@1.45.1", "", {}, "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw=="],
905+
662906 "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
663907
664908 "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
665909
666910 "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
667911
912+ "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="],
913+
668914 "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="],
669915
670916 "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
@@ -681,8 +927,16 @@ · 10 unmodified lines
681927
682928 "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
683929
930+ "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
931+
684932 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
685933
934+ "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="],
935+
936+ "input-otp": ["input-otp@1.4.2", "", { "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA=="],
937+
938+ "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
939+
686940 "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
687941
688942 "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
@@ -731,6 +985,8 @@ · 42 unmodified lines
731985
732986 "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
733987
988+ "lucide-react": ["lucide-react@1.7.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg=="],
989+
734990 "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
735991
736992 "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
@@ -745,6 +1001,8 @@ · 8 unmodified lines
7451001
7461002 "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
7471003
1004+ "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
1005+
7481006 "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
7491007
7501008 "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
@@ -771,14 +1029,40 @@ · 20 unmodified lines
7711029
7721030 "quickjs-emscripten-core": ["quickjs-emscripten-core@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-oQz8p0SiKDBc1TC7ZBK2fr0GoSHZKA0jZIeXxsnCyCs4y32FStzCW4d1h6E1sE0uHDMbGITbk2zhNaytaoJwXQ=="],
7731031
1032+ "radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="],
1033+
7741034 "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
7751035
1036+ "react-day-picker": ["react-day-picker@9.14.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "@tabby_ai/hijri-converter": "1.0.5", "date-fns": "^4.1.0", "date-fns-jalali": "4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-tBaoDWjPwe0M5pGrum4H0SR6Lyk+BO9oHnp9JbKpGKW2mlraNPgP9BMfsg5pWpwrssARmeqk7YBl2oXutZTaHA=="],
1037+
7761038 "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
7771039
1040+ "react-hook-form": ["react-hook-form@7.72.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-V4v6jubaf6JAurEaVnT9aUPKFbNtDgohj5CIgVGyPHvT9wRx5OZHVjz31GsxnPNI278XMu+ruFz+wGOscHaLKw=="],
1041+
1042+ "react-is": ["react-is@19.2.4", "", {}, "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA=="],
1043+
1044+ "react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="],
1045+
7781046 "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
7791047
1048+ "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
1049+
1050+ "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
1051+
1052+ "react-resizable-panels": ["react-resizable-panels@4.8.0", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-2uEABkewb3ky/ZgIlAUxWa1W/LjsK494fdV1QsXxst7CDRHCzo7h22tWWu3NNaBjmiuriOCt3CvhipnaYcpoIw=="],
1053+
1054+ "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
1055+
1056+ "recharts": ["recharts@3.8.0", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ=="],
1057+
1058+ "redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="],
1059+
1060+ "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="],
1061+
7801062 "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
7811063
1064+ "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="],
1065+
7821066 "rollup": ["rollup@4.60.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="],
7831067
7841068 "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
@@ -791,6 +1075,8 @@ · 6 unmodified lines
7911075
7921076 "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
7931077
1078+ "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="],
1079+
7941080 "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
7951081
7961082 "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
@@ -799,10 +1085,16 @@ · 2 unmodified lines
7991085
8001086 "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="],
8011087
1088+ "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
1089+
1090+ "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="],
1091+
8021092 "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="],
8031093
8041094 "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="],
8051095
1096+ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
1097+
8061098 "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
8071099
8081100 "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
@@ -815,6 +1107,8 @@ · 6 unmodified lines
8151107
8161108 "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="],
8171109
1110+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
1111+
8181112 "turbo": ["turbo@2.9.2", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.2", "@turbo/darwin-arm64": "2.9.2", "@turbo/linux-64": "2.9.2", "@turbo/linux-arm64": "2.9.2", "@turbo/windows-64": "2.9.2", "@turbo/windows-arm64": "2.9.2" }, "bin": { "turbo": "bin/turbo" } }, "sha512-DUUWysctg/vax8W05UJcyrHm4LvxtX0umAT4bZB5lab4inNXTbwoYSFpdgqzBxON23j8ls3xGxvx0kwQz6Q8wQ=="],
8191113
8201114 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
@@ -825,10 +1119,18 @@ · 4 unmodified lines
8251119
8261120 "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
8271121
1122+ "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
1123+
1124+ "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
1125+
8281126 "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
8291127
8301128 "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
8311129
1130+ "vaul": ["vaul@1.1.2", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA=="],
1131+
1132+ "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="],
1133+
8321134 "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
8331135
8341136 "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
@@ -843,10 +1145,14 @@ · 8 unmodified lines
8431145
8441146 "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="],
8451147
1148+ "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
1149+
8461150 "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
8471151
8481152 "@executor/sdk/typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
8491153
1154+ "@reduxjs/toolkit/immer": ["immer@11.1.4", "", {}, "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="],
1155+
8501156 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
8511157
8521158 "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA=="],
package.json+1−0
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 "packageManager": "bun@1.2.22",
66 "workspaces": [
77 "packages/*/*",
8+ "packages/ui",
89 "apps/*"
910 ],
1011 "catalog": {
packages/ui/components.json+20−0
@@ -0,0 +1,20 @@
1+{
2+ "$schema": "https://ui.shadcn.com/schema.json",
3+ "style": "new-york",
4+ "rsc": false,
5+ "tsx": true,
6+ "tailwind": {
7+ "config": "",
8+ "css": "src/styles/globals.css",
9+ "baseColor": "neutral",
10+ "cssVariables": true
11+ },
12+ "iconLibrary": "lucide",
13+ "aliases": {
14+ "components": "@executor/ui/components",
15+ "utils": "@executor/ui/lib/utils",
16+ "hooks": "@executor/ui/hooks",
17+ "lib": "@executor/ui/lib",
18+ "ui": "@executor/ui/components"
19+ }
20+}
packages/ui/package.json+42−0
@@ -0,0 +1,42 @@
1+{
2+ "name": "@executor/ui",
3+ "version": "0.0.0",
4+ "private": true,
5+ "type": "module",
6+ "exports": {
7+ "./components/*": "./src/components/*.tsx",
8+ "./hooks/*": "./src/hooks/*.ts",
9+ "./lib/*": "./src/lib/*.ts",
10+ "./globals.css": "./src/styles/globals.css"
11+ },
12+ "dependencies": {
13+ "@base-ui/react": "^1.3.0",
14+ "@hookform/resolvers": "^5.2.2",
15+ "class-variance-authority": "^0.7.1",
16+ "clsx": "^2.1.1",
17+ "cmdk": "^1.1.1",
18+ "date-fns": "^4.1.0",
19+ "embla-carousel-react": "^8.6.0",
20+ "input-otp": "^1.4.2",
21+ "lucide-react": "^1.7.0",
22+ "next-themes": "^0.4.6",
23+ "radix-ui": "^1.4.3",
24+ "react": "^19.1.0",
25+ "react-day-picker": "^9.14.0",
26+ "react-hook-form": "^7.72.0",
27+ "react-resizable-panels": "^4",
28+ "recharts": "3.8.0",
29+ "sonner": "^2.0.7",
30+ "tailwind-merge": "^3.3.0",
31+ "vaul": "^1.1.2",
32+ "zod": "^4.3.6"
33+ },
34+ "devDependencies": {
35+ "@types/react": "^19.1.0",
36+ "typescript": "^5.9.3",
37+ "tailwindcss": "^4.1.10"
38+ },
39+ "scripts": {
40+ "typecheck": "tsc --noEmit"
41+ }
42+}
packages/ui/src/components/accordion.tsx+64−0
@@ -0,0 +1,64 @@
1+import * as React from "react"
2+import { ChevronDownIcon } from "lucide-react"
3+import { Accordion as AccordionPrimitive } from "radix-ui"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+
7+function Accordion({
8+ ...props
9+}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
10+ return <AccordionPrimitive.Root data-slot="accordion" {...props} />
11+}
12+
13+function AccordionItem({
14+ className,
15+ ...props
16+}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
17+ return (
18+ <AccordionPrimitive.Item
19+ data-slot="accordion-item"
20+ className={cn("border-b last:border-b-0", className)}
21+ {...props}
22+ />
23+ )
24+}
25+
26+function AccordionTrigger({
27+ className,
28+ children,
29+ ...props
30+}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
31+ return (
32+ <AccordionPrimitive.Header className="flex">
33+ <AccordionPrimitive.Trigger
34+ data-slot="accordion-trigger"
35+ className={cn(
36+ "flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
37+ className
38+ )}
39+ {...props}
40+ >
41+ {children}
42+ <ChevronDownIcon className="pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200" />
43+ </AccordionPrimitive.Trigger>
44+ </AccordionPrimitive.Header>
45+ )
46+}
47+
48+function AccordionContent({
49+ className,
50+ children,
51+ ...props
52+}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
53+ return (
54+ <AccordionPrimitive.Content
55+ data-slot="accordion-content"
56+ className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
57+ {...props}
58+ >
59+ <div className={cn("pt-0 pb-4", className)}>{children}</div>
60+ </AccordionPrimitive.Content>
61+ )
62+}
63+
64+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
packages/ui/src/components/alert-dialog.tsx+196−0
@@ -0,0 +1,196 @@
1+"use client"
2+
3+import * as React from "react"
4+import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
5+
6+import { cn } from "@executor/ui/lib/utils"
7+import { Button } from "@executor/ui/components/button"
8+
9+function AlertDialog({
10+ ...props
11+}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
12+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
13+}
14+
15+function AlertDialogTrigger({
16+ ...props
17+}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
18+ return (
19+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
20+ )
21+}
22+
23+function AlertDialogPortal({
24+ ...props
25+}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
26+ return (
27+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
28+ )
29+}
30+
31+function AlertDialogOverlay({
32+ className,
33+ ...props
34+}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
35+ return (
36+ <AlertDialogPrimitive.Overlay
37+ data-slot="alert-dialog-overlay"
38+ className={cn(
39+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
40+ className
41+ )}
42+ {...props}
43+ />
44+ )
45+}
46+
47+function AlertDialogContent({
48+ className,
49+ size = "default",
50+ ...props
51+}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
52+ size?: "default" | "sm"
53+}) {
54+ return (
55+ <AlertDialogPortal>
56+ <AlertDialogOverlay />
57+ <AlertDialogPrimitive.Content
58+ data-slot="alert-dialog-content"
59+ data-size={size}
60+ className={cn(
61+ "group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[size=sm]:max-w-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[size=default]:sm:max-w-lg",
62+ className
63+ )}
64+ {...props}
65+ />
66+ </AlertDialogPortal>
67+ )
68+}
69+
70+function AlertDialogHeader({
71+ className,
72+ ...props
73+}: React.ComponentProps<"div">) {
74+ return (
75+ <div
76+ data-slot="alert-dialog-header"
77+ className={cn(
78+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
79+ className
80+ )}
81+ {...props}
82+ />
83+ )
84+}
85+
86+function AlertDialogFooter({
87+ className,
88+ ...props
89+}: React.ComponentProps<"div">) {
90+ return (
91+ <div
92+ data-slot="alert-dialog-footer"
93+ className={cn(
94+ "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
95+ className
96+ )}
97+ {...props}
98+ />
99+ )
100+}
101+
102+function AlertDialogTitle({
103+ className,
104+ ...props
105+}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
106+ return (
107+ <AlertDialogPrimitive.Title
108+ data-slot="alert-dialog-title"
109+ className={cn(
110+ "text-lg font-semibold sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
111+ className
112+ )}
113+ {...props}
114+ />
115+ )
116+}
117+
118+function AlertDialogDescription({
119+ className,
120+ ...props
121+}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
122+ return (
123+ <AlertDialogPrimitive.Description
124+ data-slot="alert-dialog-description"
125+ className={cn("text-sm text-muted-foreground", className)}
126+ {...props}
127+ />
128+ )
129+}
130+
131+function AlertDialogMedia({
132+ className,
133+ ...props
134+}: React.ComponentProps<"div">) {
135+ return (
136+ <div
137+ data-slot="alert-dialog-media"
138+ className={cn(
139+ "mb-2 inline-flex size-16 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
140+ className
141+ )}
142+ {...props}
143+ />
144+ )
145+}
146+
147+function AlertDialogAction({
148+ className,
149+ variant = "default",
150+ size = "default",
151+ ...props
152+}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
153+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
154+ return (
155+ <Button variant={variant} size={size} asChild>
156+ <AlertDialogPrimitive.Action
157+ data-slot="alert-dialog-action"
158+ className={cn(className)}
159+ {...props}
160+ />
161+ </Button>
162+ )
163+}
164+
165+function AlertDialogCancel({
166+ className,
167+ variant = "outline",
168+ size = "default",
169+ ...props
170+}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
171+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
172+ return (
173+ <Button variant={variant} size={size} asChild>
174+ <AlertDialogPrimitive.Cancel
175+ data-slot="alert-dialog-cancel"
176+ className={cn(className)}
177+ {...props}
178+ />
179+ </Button>
180+ )
181+}
182+
183+export {
184+ AlertDialog,
185+ AlertDialogAction,
186+ AlertDialogCancel,
187+ AlertDialogContent,
188+ AlertDialogDescription,
189+ AlertDialogFooter,
190+ AlertDialogHeader,
191+ AlertDialogMedia,
192+ AlertDialogOverlay,
193+ AlertDialogPortal,
194+ AlertDialogTitle,
195+ AlertDialogTrigger,
196+}
packages/ui/src/components/alert.tsx+66−0
@@ -0,0 +1,66 @@
1+import * as React from "react"
2+import { cva, type VariantProps } from "class-variance-authority"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+const alertVariants = cva(
7+ "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
8+ {
9+ variants: {
10+ variant: {
11+ default: "bg-card text-card-foreground",
12+ destructive:
13+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current",
14+ },
15+ },
16+ defaultVariants: {
17+ variant: "default",
18+ },
19+ }
20+)
21+
22+function Alert({
23+ className,
24+ variant,
25+ ...props
26+}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
27+ return (
28+ <div
29+ data-slot="alert"
30+ role="alert"
31+ className={cn(alertVariants({ variant }), className)}
32+ {...props}
33+ />
34+ )
35+}
36+
37+function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
38+ return (
39+ <div
40+ data-slot="alert-title"
41+ className={cn(
42+ "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
43+ className
44+ )}
45+ {...props}
46+ />
47+ )
48+}
49+
50+function AlertDescription({
51+ className,
52+ ...props
53+}: React.ComponentProps<"div">) {
54+ return (
55+ <div
56+ data-slot="alert-description"
57+ className={cn(
58+ "col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed",
59+ className
60+ )}
61+ {...props}
62+ />
63+ )
64+}
65+
66+export { Alert, AlertTitle, AlertDescription }
packages/ui/src/components/aspect-ratio.tsx+11−0
@@ -0,0 +1,11 @@
1+"use client"
2+
3+import { AspectRatio as AspectRatioPrimitive } from "radix-ui"
4+
5+function AspectRatio({
6+ ...props
7+}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
8+ return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} />
9+}
10+
11+export { AspectRatio }
packages/ui/src/components/avatar.tsx+107−0
@@ -0,0 +1,107 @@
1+import * as React from "react"
2+import { Avatar as AvatarPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function Avatar({
7+ className,
8+ size = "default",
9+ ...props
10+}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
11+ size?: "default" | "sm" | "lg"
12+}) {
13+ return (
14+ <AvatarPrimitive.Root
15+ data-slot="avatar"
16+ data-size={size}
17+ className={cn(
18+ "group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
19+ className
20+ )}
21+ {...props}
22+ />
23+ )
24+}
25+
26+function AvatarImage({
27+ className,
28+ ...props
29+}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
30+ return (
31+ <AvatarPrimitive.Image
32+ data-slot="avatar-image"
33+ className={cn("aspect-square size-full", className)}
34+ {...props}
35+ />
36+ )
37+}
38+
39+function AvatarFallback({
40+ className,
41+ ...props
42+}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
43+ return (
44+ <AvatarPrimitive.Fallback
45+ data-slot="avatar-fallback"
46+ className={cn(
47+ "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
48+ className
49+ )}
50+ {...props}
51+ />
52+ )
53+}
54+
55+function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
56+ return (
57+ <span
58+ data-slot="avatar-badge"
59+ className={cn(
60+ "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground ring-2 ring-background select-none",
61+ "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
62+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
63+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
64+ className
65+ )}
66+ {...props}
67+ />
68+ )
69+}
70+
71+function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
72+ return (
73+ <div
74+ data-slot="avatar-group"
75+ className={cn(
76+ "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
77+ className
78+ )}
79+ {...props}
80+ />
81+ )
82+}
83+
84+function AvatarGroupCount({
85+ className,
86+ ...props
87+}: React.ComponentProps<"div">) {
88+ return (
89+ <div
90+ data-slot="avatar-group-count"
91+ className={cn(
92+ "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
93+ className
94+ )}
95+ {...props}
96+ />
97+ )
98+}
99+
100+export {
101+ Avatar,
102+ AvatarImage,
103+ AvatarFallback,
104+ AvatarBadge,
105+ AvatarGroup,
106+ AvatarGroupCount,
107+}
packages/ui/src/components/badge.tsx+48−0
@@ -0,0 +1,48 @@
1+import * as React from "react"
2+import { cva, type VariantProps } from "class-variance-authority"
3+import { Slot } from "radix-ui"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+
7+const badgeVariants = cva(
8+ "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
9+ {
10+ variants: {
11+ variant: {
12+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
13+ secondary:
14+ "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
15+ destructive:
16+ "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
17+ outline:
18+ "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
19+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20+ link: "text-primary underline-offset-4 [a&]:hover:underline",
21+ },
22+ },
23+ defaultVariants: {
24+ variant: "default",
25+ },
26+ }
27+)
28+
29+function Badge({
30+ className,
31+ variant = "default",
32+ asChild = false,
33+ ...props
34+}: React.ComponentProps<"span"> &
35+ VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
36+ const Comp = asChild ? Slot.Root : "span"
37+
38+ return (
39+ <Comp
40+ data-slot="badge"
41+ data-variant={variant}
42+ className={cn(badgeVariants({ variant }), className)}
43+ {...props}
44+ />
45+ )
46+}
47+
48+export { Badge, badgeVariants }
packages/ui/src/components/breadcrumb.tsx+109−0
@@ -0,0 +1,109 @@
1+import * as React from "react"
2+import { ChevronRight, MoreHorizontal } from "lucide-react"
3+import { Slot } from "radix-ui"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+
7+function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
8+ return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
9+}
10+
11+function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
12+ return (
13+ <ol
14+ data-slot="breadcrumb-list"
15+ className={cn(
16+ "flex flex-wrap items-center gap-1.5 text-sm break-words text-muted-foreground sm:gap-2.5",
17+ className
18+ )}
19+ {...props}
20+ />
21+ )
22+}
23+
24+function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
25+ return (
26+ <li
27+ data-slot="breadcrumb-item"
28+ className={cn("inline-flex items-center gap-1.5", className)}
29+ {...props}
30+ />
31+ )
32+}
33+
34+function BreadcrumbLink({
35+ asChild,
36+ className,
37+ ...props
38+}: React.ComponentProps<"a"> & {
39+ asChild?: boolean
40+}) {
41+ const Comp = asChild ? Slot.Root : "a"
42+
43+ return (
44+ <Comp
45+ data-slot="breadcrumb-link"
46+ className={cn("transition-colors hover:text-foreground", className)}
47+ {...props}
48+ />
49+ )
50+}
51+
52+function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
53+ return (
54+ <span
55+ data-slot="breadcrumb-page"
56+ role="link"
57+ aria-disabled="true"
58+ aria-current="page"
59+ className={cn("font-normal text-foreground", className)}
60+ {...props}
61+ />
62+ )
63+}
64+
65+function BreadcrumbSeparator({
66+ children,
67+ className,
68+ ...props
69+}: React.ComponentProps<"li">) {
70+ return (
71+ <li
72+ data-slot="breadcrumb-separator"
73+ role="presentation"
74+ aria-hidden="true"
75+ className={cn("[&>svg]:size-3.5", className)}
76+ {...props}
77+ >
78+ {children ?? <ChevronRight />}
79+ </li>
80+ )
81+}
82+
83+function BreadcrumbEllipsis({
84+ className,
85+ ...props
86+}: React.ComponentProps<"span">) {
87+ return (
88+ <span
89+ data-slot="breadcrumb-ellipsis"
90+ role="presentation"
91+ aria-hidden="true"
92+ className={cn("flex size-9 items-center justify-center", className)}
93+ {...props}
94+ >
95+ <MoreHorizontal className="size-4" />
96+ <span className="sr-only">More</span>
97+ </span>
98+ )
99+}
100+
101+export {
102+ Breadcrumb,
103+ BreadcrumbList,
104+ BreadcrumbItem,
105+ BreadcrumbLink,
106+ BreadcrumbPage,
107+ BreadcrumbSeparator,
108+ BreadcrumbEllipsis,
109+}
packages/ui/src/components/button-group.tsx+83−0
@@ -0,0 +1,83 @@
1+import { cva, type VariantProps } from "class-variance-authority"
2+import { Slot } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+import { Separator } from "@executor/ui/components/separator"
6+
7+const buttonGroupVariants = cva(
8+ "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
9+ {
10+ variants: {
11+ orientation: {
12+ horizontal:
13+ "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
14+ vertical:
15+ "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",
16+ },
17+ },
18+ defaultVariants: {
19+ orientation: "horizontal",
20+ },
21+ }
22+)
23+
24+function ButtonGroup({
25+ className,
26+ orientation,
27+ ...props
28+}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
29+ return (
30+ <div
31+ role="group"
32+ data-slot="button-group"
33+ data-orientation={orientation}
34+ className={cn(buttonGroupVariants({ orientation }), className)}
35+ {...props}
36+ />
37+ )
38+}
39+
40+function ButtonGroupText({
41+ className,
42+ asChild = false,
43+ ...props
44+}: React.ComponentProps<"div"> & {
45+ asChild?: boolean
46+}) {
47+ const Comp = asChild ? Slot.Root : "div"
48+
49+ return (
50+ <Comp
51+ className={cn(
52+ "flex items-center gap-2 rounded-md border bg-muted px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
53+ className
54+ )}
55+ {...props}
56+ />
57+ )
58+}
59+
60+function ButtonGroupSeparator({
61+ className,
62+ orientation = "vertical",
63+ ...props
64+}: React.ComponentProps<typeof Separator>) {
65+ return (
66+ <Separator
67+ data-slot="button-group-separator"
68+ orientation={orientation}
69+ className={cn(
70+ "relative m-0! self-stretch bg-input data-[orientation=vertical]:h-auto",
71+ className
72+ )}
73+ {...props}
74+ />
75+ )
76+}
77+
78+export {
79+ ButtonGroup,
80+ ButtonGroupSeparator,
81+ ButtonGroupText,
82+ buttonGroupVariants,
83+}
packages/ui/src/components/button.tsx+64−0
@@ -0,0 +1,64 @@
1+import * as React from "react"
2+import { cva, type VariantProps } from "class-variance-authority"
3+import { Slot } from "radix-ui"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+
7+const buttonVariants = cva(
8+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
9+ {
10+ variants: {
11+ variant: {
12+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13+ destructive:
14+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
15+ outline:
16+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
17+ secondary:
18+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19+ ghost:
20+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21+ link: "text-primary underline-offset-4 hover:underline",
22+ },
23+ size: {
24+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
25+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
26+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
27+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
28+ icon: "size-9",
29+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
30+ "icon-sm": "size-8",
31+ "icon-lg": "size-10",
32+ },
33+ },
34+ defaultVariants: {
35+ variant: "default",
36+ size: "default",
37+ },
38+ }
39+)
40+
41+function Button({
42+ className,
43+ variant = "default",
44+ size = "default",
45+ asChild = false,
46+ ...props
47+}: React.ComponentProps<"button"> &
48+ VariantProps<typeof buttonVariants> & {
49+ asChild?: boolean
50+ }) {
51+ const Comp = asChild ? Slot.Root : "button"
52+
53+ return (
54+ <Comp
55+ data-slot="button"
56+ data-variant={variant}
57+ data-size={size}
58+ className={cn(buttonVariants({ variant, size, className }))}
59+ {...props}
60+ />
61+ )
62+}
63+
64+export { Button, buttonVariants }
packages/ui/src/components/calendar.tsx+218−0
@@ -0,0 +1,218 @@
1+import * as React from "react"
2+import {
3+ ChevronDownIcon,
4+ ChevronLeftIcon,
5+ ChevronRightIcon,
6+} from "lucide-react"
7+import {
8+ DayPicker,
9+ getDefaultClassNames,
10+ type DayButton,
11+} from "react-day-picker"
12+
13+import { cn } from "@executor/ui/lib/utils"
14+import { Button, buttonVariants } from "@executor/ui/components/button"
15+
16+function Calendar({
17+ className,
18+ classNames,
19+ showOutsideDays = true,
20+ captionLayout = "label",
21+ buttonVariant = "ghost",
22+ formatters,
23+ components,
24+ ...props
25+}: React.ComponentProps<typeof DayPicker> & {
26+ buttonVariant?: React.ComponentProps<typeof Button>["variant"]
27+}) {
28+ const defaultClassNames = getDefaultClassNames()
29+
30+ return (
31+ <DayPicker
32+ showOutsideDays={showOutsideDays}
33+ className={cn(
34+ "group/calendar bg-background p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
35+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
36+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
37+ className
38+ )}
39+ captionLayout={captionLayout}
40+ formatters={{
41+ formatMonthDropdown: (date) =>
42+ date.toLocaleString("default", { month: "short" }),
43+ ...formatters,
44+ }}
45+ classNames={{
46+ root: cn("w-fit", defaultClassNames.root),
47+ months: cn(
48+ "relative flex flex-col gap-4 md:flex-row",
49+ defaultClassNames.months
50+ ),
51+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
52+ nav: cn(
53+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
54+ defaultClassNames.nav
55+ ),
56+ button_previous: cn(
57+ buttonVariants({ variant: buttonVariant }),
58+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
59+ defaultClassNames.button_previous
60+ ),
61+ button_next: cn(
62+ buttonVariants({ variant: buttonVariant }),
63+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
64+ defaultClassNames.button_next
65+ ),
66+ month_caption: cn(
67+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
68+ defaultClassNames.month_caption
69+ ),
70+ dropdowns: cn(
71+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
72+ defaultClassNames.dropdowns
73+ ),
74+ dropdown_root: cn(
75+ "relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50",
76+ defaultClassNames.dropdown_root
77+ ),
78+ dropdown: cn(
79+ "absolute inset-0 bg-popover opacity-0",
80+ defaultClassNames.dropdown
81+ ),
82+ caption_label: cn(
83+ "font-medium select-none",
84+ captionLayout === "label"
85+ ? "text-sm"
86+ : "flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
87+ defaultClassNames.caption_label
88+ ),
89+ table: "w-full border-collapse",
90+ weekdays: cn("flex", defaultClassNames.weekdays),
91+ weekday: cn(
92+ "flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none",
93+ defaultClassNames.weekday
94+ ),
95+ week: cn("mt-2 flex w-full", defaultClassNames.week),
96+ week_number_header: cn(
97+ "w-(--cell-size) select-none",
98+ defaultClassNames.week_number_header
99+ ),
100+ week_number: cn(
101+ "text-[0.8rem] text-muted-foreground select-none",
102+ defaultClassNames.week_number
103+ ),
104+ day: cn(
105+ "group/day relative aspect-square h-full w-full p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md",
106+ props.showWeekNumber
107+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
108+ : "[&:first-child[data-selected=true]_button]:rounded-l-md",
109+ defaultClassNames.day
110+ ),
111+ range_start: cn(
112+ "rounded-l-md bg-accent",
113+ defaultClassNames.range_start
114+ ),
115+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
116+ range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
117+ today: cn(
118+ "rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none",
119+ defaultClassNames.today
120+ ),
121+ outside: cn(
122+ "text-muted-foreground aria-selected:text-muted-foreground",
123+ defaultClassNames.outside
124+ ),
125+ disabled: cn(
126+ "text-muted-foreground opacity-50",
127+ defaultClassNames.disabled
128+ ),
129+ hidden: cn("invisible", defaultClassNames.hidden),
130+ ...classNames,
131+ }}
132+ components={{
133+ Root: ({ className, rootRef, ...props }) => {
134+ return (
135+ <div
136+ data-slot="calendar"
137+ ref={rootRef}
138+ className={cn(className)}
139+ {...props}
140+ />
141+ )
142+ },
143+ Chevron: ({ className, orientation, ...props }) => {
144+ if (orientation === "left") {
145+ return (
146+ <ChevronLeftIcon className={cn("size-4", className)} {...props} />
147+ )
148+ }
149+
150+ if (orientation === "right") {
151+ return (
152+ <ChevronRightIcon
153+ className={cn("size-4", className)}
154+ {...props}
155+ />
156+ )
157+ }
158+
159+ return (
160+ <ChevronDownIcon className={cn("size-4", className)} {...props} />
161+ )
162+ },
163+ DayButton: CalendarDayButton,
164+ WeekNumber: ({ children, ...props }) => {
165+ return (
166+ <td {...props}>
167+ <div className="flex size-(--cell-size) items-center justify-center text-center">
168+ {children}
169+ </div>
170+ </td>
171+ )
172+ },
173+ ...components,
174+ }}
175+ {...props}
176+ />
177+ )
178+}
179+
180+function CalendarDayButton({
181+ className,
182+ day,
183+ modifiers,
184+ ...props
185+}: React.ComponentProps<typeof DayButton>) {
186+ const defaultClassNames = getDefaultClassNames()
187+
188+ const ref = React.useRef<HTMLButtonElement>(null)
189+ React.useEffect(() => {
190+ if (modifiers.focused) ref.current?.focus()
191+ }, [modifiers.focused])
192+
193+ return (
194+ <Button
195+ ref={ref}
196+ variant="ghost"
197+ size="icon"
198+ data-day={day.date.toLocaleDateString()}
199+ data-selected-single={
200+ modifiers.selected &&
201+ !modifiers.range_start &&
202+ !modifiers.range_end &&
203+ !modifiers.range_middle
204+ }
205+ data-range-start={modifiers.range_start}
206+ data-range-end={modifiers.range_end}
207+ data-range-middle={modifiers.range_middle}
208+ className={cn(
209+ "flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-accent-foreground [&>span]:text-xs [&>span]:opacity-70",
210+ defaultClassNames.day,
211+ className
212+ )}
213+ {...props}
214+ />
215+ )
216+}
217+
218+export { Calendar, CalendarDayButton }
packages/ui/src/components/card.tsx+92−0
@@ -0,0 +1,92 @@
1+import * as React from "react"
2+
3+import { cn } from "@executor/ui/lib/utils"
4+
5+function Card({ className, ...props }: React.ComponentProps<"div">) {
6+ return (
7+ <div
8+ data-slot="card"
9+ className={cn(
10+ "flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
11+ className
12+ )}
13+ {...props}
14+ />
15+ )
16+}
17+
18+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19+ return (
20+ <div
21+ data-slot="card-header"
22+ className={cn(
23+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24+ className
25+ )}
26+ {...props}
27+ />
28+ )
29+}
30+
31+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32+ return (
33+ <div
34+ data-slot="card-title"
35+ className={cn("leading-none font-semibold", className)}
36+ {...props}
37+ />
38+ )
39+}
40+
41+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42+ return (
43+ <div
44+ data-slot="card-description"
45+ className={cn("text-sm text-muted-foreground", className)}
46+ {...props}
47+ />
48+ )
49+}
50+
51+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52+ return (
53+ <div
54+ data-slot="card-action"
55+ className={cn(
56+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57+ className
58+ )}
59+ {...props}
60+ />
61+ )
62+}
63+
64+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65+ return (
66+ <div
67+ data-slot="card-content"
68+ className={cn("px-6", className)}
69+ {...props}
70+ />
71+ )
72+}
73+
74+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75+ return (
76+ <div
77+ data-slot="card-footer"
78+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
79+ {...props}
80+ />
81+ )
82+}
83+
84+export {
85+ Card,
86+ CardHeader,
87+ CardFooter,
88+ CardTitle,
89+ CardAction,
90+ CardDescription,
91+ CardContent,
92+}
packages/ui/src/components/carousel.tsx+241−0
@@ -0,0 +1,241 @@
1+"use client"
2+
3+import * as React from "react"
4+import useEmblaCarousel, {
5+ type UseEmblaCarouselType,
6+} from "embla-carousel-react"
7+import { ArrowLeft, ArrowRight } from "lucide-react"
8+
9+import { cn } from "@executor/ui/lib/utils"
10+import { Button } from "@executor/ui/components/button"
11+
12+type CarouselApi = UseEmblaCarouselType[1]
13+type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
14+type CarouselOptions = UseCarouselParameters[0]
15+type CarouselPlugin = UseCarouselParameters[1]
16+
17+type CarouselProps = {
18+ opts?: CarouselOptions
19+ plugins?: CarouselPlugin
20+ orientation?: "horizontal" | "vertical"
21+ setApi?: (api: CarouselApi) => void
22+}
23+
24+type CarouselContextProps = {
25+ carouselRef: ReturnType<typeof useEmblaCarousel>[0]
26+ api: ReturnType<typeof useEmblaCarousel>[1]
27+ scrollPrev: () => void
28+ scrollNext: () => void
29+ canScrollPrev: boolean
30+ canScrollNext: boolean
31+} & CarouselProps
32+
33+const CarouselContext = React.createContext<CarouselContextProps | null>(null)
34+
35+function useCarousel() {
36+ const context = React.useContext(CarouselContext)
37+
38+ if (!context) {
39+ throw new Error("useCarousel must be used within a <Carousel />")
40+ }
41+
42+ return context
43+}
44+
45+function Carousel({
46+ orientation = "horizontal",
47+ opts,
48+ setApi,
49+ plugins,
50+ className,
51+ children,
52+ ...props
53+}: React.ComponentProps<"div"> & CarouselProps) {
54+ const [carouselRef, api] = useEmblaCarousel(
55+ {
56+ ...opts,
57+ axis: orientation === "horizontal" ? "x" : "y",
58+ },
59+ plugins
60+ )
61+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
62+ const [canScrollNext, setCanScrollNext] = React.useState(false)
63+
64+ const onSelect = React.useCallback((api: CarouselApi) => {
65+ if (!api) return
66+ setCanScrollPrev(api.canScrollPrev())
67+ setCanScrollNext(api.canScrollNext())
68+ }, [])
69+
70+ const scrollPrev = React.useCallback(() => {
71+ api?.scrollPrev()
72+ }, [api])
73+
74+ const scrollNext = React.useCallback(() => {
75+ api?.scrollNext()
76+ }, [api])
77+
78+ const handleKeyDown = React.useCallback(
79+ (event: React.KeyboardEvent<HTMLDivElement>) => {
80+ if (event.key === "ArrowLeft") {
81+ event.preventDefault()
82+ scrollPrev()
83+ } else if (event.key === "ArrowRight") {
84+ event.preventDefault()
85+ scrollNext()
86+ }
87+ },
88+ [scrollPrev, scrollNext]
89+ )
90+
91+ React.useEffect(() => {
92+ if (!api || !setApi) return
93+ setApi(api)
94+ }, [api, setApi])
95+
96+ React.useEffect(() => {
97+ if (!api) return
98+ onSelect(api)
99+ api.on("reInit", onSelect)
100+ api.on("select", onSelect)
101+
102+ return () => {
103+ api?.off("select", onSelect)
104+ }
105+ }, [api, onSelect])
106+
107+ return (
108+ <CarouselContext.Provider
109+ value={{
110+ carouselRef,
111+ api: api,
112+ opts,
113+ orientation:
114+ orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
115+ scrollPrev,
116+ scrollNext,
117+ canScrollPrev,
118+ canScrollNext,
119+ }}
120+ >
121+ <div
122+ onKeyDownCapture={handleKeyDown}
123+ className={cn("relative", className)}
124+ role="region"
125+ aria-roledescription="carousel"
126+ data-slot="carousel"
127+ {...props}
128+ >
129+ {children}
130+ </div>
131+ </CarouselContext.Provider>
132+ )
133+}
134+
135+function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
136+ const { carouselRef, orientation } = useCarousel()
137+
138+ return (
139+ <div
140+ ref={carouselRef}
141+ className="overflow-hidden"
142+ data-slot="carousel-content"
143+ >
144+ <div
145+ className={cn(
146+ "flex",
147+ orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
148+ className
149+ )}
150+ {...props}
151+ />
152+ </div>
153+ )
154+}
155+
156+function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
157+ const { orientation } = useCarousel()
158+
159+ return (
160+ <div
161+ role="group"
162+ aria-roledescription="slide"
163+ data-slot="carousel-item"
164+ className={cn(
165+ "min-w-0 shrink-0 grow-0 basis-full",
166+ orientation === "horizontal" ? "pl-4" : "pt-4",
167+ className
168+ )}
169+ {...props}
170+ />
171+ )
172+}
173+
174+function CarouselPrevious({
175+ className,
176+ variant = "outline",
177+ size = "icon",
178+ ...props
179+}: React.ComponentProps<typeof Button>) {
180+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
181+
182+ return (
183+ <Button
184+ data-slot="carousel-previous"
185+ variant={variant}
186+ size={size}
187+ className={cn(
188+ "absolute size-8 rounded-full",
189+ orientation === "horizontal"
190+ ? "top-1/2 -left-12 -translate-y-1/2"
191+ : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
192+ className
193+ )}
194+ disabled={!canScrollPrev}
195+ onClick={scrollPrev}
196+ {...props}
197+ >
198+ <ArrowLeft />
199+ <span className="sr-only">Previous slide</span>
200+ </Button>
201+ )
202+}
203+
204+function CarouselNext({
205+ className,
206+ variant = "outline",
207+ size = "icon",
208+ ...props
209+}: React.ComponentProps<typeof Button>) {
210+ const { orientation, scrollNext, canScrollNext } = useCarousel()
211+
212+ return (
213+ <Button
214+ data-slot="carousel-next"
215+ variant={variant}
216+ size={size}
217+ className={cn(
218+ "absolute size-8 rounded-full",
219+ orientation === "horizontal"
220+ ? "top-1/2 -right-12 -translate-y-1/2"
221+ : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
222+ className
223+ )}
224+ disabled={!canScrollNext}
225+ onClick={scrollNext}
226+ {...props}
227+ >
228+ <ArrowRight />
229+ <span className="sr-only">Next slide</span>
230+ </Button>
231+ )
232+}
233+
234+export {
235+ type CarouselApi,
236+ Carousel,
237+ CarouselContent,
238+ CarouselItem,
239+ CarouselPrevious,
240+ CarouselNext,
241+}
packages/ui/src/components/chart.tsx+372−0
@@ -0,0 +1,372 @@
1+import * as React from "react"
2+import * as RechartsPrimitive from "recharts"
3+import type { TooltipValueType } from "recharts"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+
7+// Format: { THEME_NAME: CSS_SELECTOR }
8+const THEMES = { light: "", dark: ".dark" } as const
9+
10+const INITIAL_DIMENSION = { width: 320, height: 200 } as const
11+type TooltipNameType = number | string
12+
13+export type ChartConfig = Record<
14+ string,
15+ {
16+ label?: React.ReactNode
17+ icon?: React.ComponentType
18+ } & (
19+ | { color?: string; theme?: never }
20+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
21+ )
22+>
23+
24+type ChartContextProps = {
25+ config: ChartConfig
26+}
27+
28+const ChartContext = React.createContext<ChartContextProps | null>(null)
29+
30+function useChart() {
31+ const context = React.useContext(ChartContext)
32+
33+ if (!context) {
34+ throw new Error("useChart must be used within a <ChartContainer />")
35+ }
36+
37+ return context
38+}
39+
40+function ChartContainer({
41+ id,
42+ className,
43+ children,
44+ config,
45+ initialDimension = INITIAL_DIMENSION,
46+ ...props
47+}: React.ComponentProps<"div"> & {
48+ config: ChartConfig
49+ children: React.ComponentProps<
50+ typeof RechartsPrimitive.ResponsiveContainer
51+ >["children"]
52+ initialDimension?: {
53+ width: number
54+ height: number
55+ }
56+}) {
57+ const uniqueId = React.useId()
58+ const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`
59+
60+ return (
61+ <ChartContext.Provider value={{ config }}>
62+ <div
63+ data-slot="chart"
64+ data-chart={chartId}
65+ className={cn(
66+ "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
67+ className
68+ )}
69+ {...props}
70+ >
71+ <ChartStyle id={chartId} config={config} />
72+ <RechartsPrimitive.ResponsiveContainer
73+ initialDimension={initialDimension}
74+ >
75+ {children}
76+ </RechartsPrimitive.ResponsiveContainer>
77+ </div>
78+ </ChartContext.Provider>
79+ )
80+}
81+
82+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
83+ const colorConfig = Object.entries(config).filter(
84+ ([, config]) => config.theme ?? config.color
85+ )
86+
87+ if (!colorConfig.length) {
88+ return null
89+ }
90+
91+ return (
92+ <style
93+ dangerouslySetInnerHTML={{
94+ __html: Object.entries(THEMES)
95+ .map(
96+ ([theme, prefix]) => `
97+${prefix} [data-chart=${id}] {
98+${colorConfig
99+ .map(([key, itemConfig]) => {
100+ const color =
101+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??
102+ itemConfig.color
103+ return color ? ` --color-${key}: ${color};` : null
104+ })
105+ .join("\n")}
106+}
107+`
108+ )
109+ .join("\n"),
110+ }}
111+ />
112+ )
113+}
114+
115+const ChartTooltip = RechartsPrimitive.Tooltip
116+
117+function ChartTooltipContent({
118+ active,
119+ payload,
120+ className,
121+ indicator = "dot",
122+ hideLabel = false,
123+ hideIndicator = false,
124+ label,
125+ labelFormatter,
126+ labelClassName,
127+ formatter,
128+ color,
129+ nameKey,
130+ labelKey,
131+}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
132+ React.ComponentProps<"div"> & {
133+ hideLabel?: boolean
134+ hideIndicator?: boolean
135+ indicator?: "line" | "dot" | "dashed"
136+ nameKey?: string
137+ labelKey?: string
138+ } & Omit<
139+ RechartsPrimitive.DefaultTooltipContentProps<
140+ TooltipValueType,
141+ TooltipNameType
142+ >,
143+ "accessibilityLayer"
144+ >) {
145+ const { config } = useChart()
146+
147+ const tooltipLabel = React.useMemo(() => {
148+ if (hideLabel || !payload?.length) {
149+ return null
150+ }
151+
152+ const [item] = payload
153+ const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`
154+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
155+ const value =
156+ !labelKey && typeof label === "string"
157+ ? (config[label]?.label ?? label)
158+ : itemConfig?.label
159+
160+ if (labelFormatter) {
161+ return (
162+ <div className={cn("font-medium", labelClassName)}>
163+ {labelFormatter(value, payload)}
164+ </div>
165+ )
166+ }
167+
168+ if (!value) {
169+ return null
170+ }
171+
172+ return <div className={cn("font-medium", labelClassName)}>{value}</div>
173+ }, [
174+ label,
175+ labelFormatter,
176+ payload,
177+ hideLabel,
178+ labelClassName,
179+ config,
180+ labelKey,
181+ ])
182+
183+ if (!active || !payload?.length) {
184+ return null
185+ }
186+
187+ const nestLabel = payload.length === 1 && indicator !== "dot"
188+
189+ return (
190+ <div
191+ className={cn(
192+ "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
193+ className
194+ )}
195+ >
196+ {!nestLabel ? tooltipLabel : null}
197+ <div className="grid gap-1.5">
198+ {payload
199+ .filter((item) => item.type !== "none")
200+ .map((item, index) => {
201+ const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`
202+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
203+ const indicatorColor = color ?? item.payload?.fill ?? item.color
204+
205+ return (
206+ <div
207+ key={index}
208+ className={cn(
209+ "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
210+ indicator === "dot" && "items-center"
211+ )}
212+ >
213+ {formatter && item?.value !== undefined && item.name ? (
214+ formatter(item.value, item.name, item, index, item.payload)
215+ ) : (
216+ <>
217+ {itemConfig?.icon ? (
218+ <itemConfig.icon />
219+ ) : (
220+ !hideIndicator && (
221+ <div
222+ className={cn(
223+ "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
224+ {
225+ "h-2.5 w-2.5": indicator === "dot",
226+ "w-1": indicator === "line",
227+ "w-0 border-[1.5px] border-dashed bg-transparent":
228+ indicator === "dashed",
229+ "my-0.5": nestLabel && indicator === "dashed",
230+ }
231+ )}
232+ style={
233+ {
234+ "--color-bg": indicatorColor,
235+ "--color-border": indicatorColor,
236+ } as React.CSSProperties
237+ }
238+ />
239+ )
240+ )}
241+ <div
242+ className={cn(
243+ "flex flex-1 justify-between leading-none",
244+ nestLabel ? "items-end" : "items-center"
245+ )}
246+ >
247+ <div className="grid gap-1.5">
248+ {nestLabel ? tooltipLabel : null}
249+ <span className="text-muted-foreground">
250+ {itemConfig?.label ?? item.name}
251+ </span>
252+ </div>
253+ {item.value != null && (
254+ <span className="font-mono font-medium text-foreground tabular-nums">
255+ {typeof item.value === "number"
256+ ? item.value.toLocaleString()
257+ : String(item.value)}
258+ </span>
259+ )}
260+ </div>
261+ </>
262+ )}
263+ </div>
264+ )
265+ })}
266+ </div>
267+ </div>
268+ )
269+}
270+
271+const ChartLegend = RechartsPrimitive.Legend
272+
273+function ChartLegendContent({
274+ className,
275+ hideIcon = false,
276+ payload,
277+ verticalAlign = "bottom",
278+ nameKey,
279+}: React.ComponentProps<"div"> & {
280+ hideIcon?: boolean
281+ nameKey?: string
282+} & RechartsPrimitive.DefaultLegendContentProps) {
283+ const { config } = useChart()
284+
285+ if (!payload?.length) {
286+ return null
287+ }
288+
289+ return (
290+ <div
291+ className={cn(
292+ "flex items-center justify-center gap-4",
293+ verticalAlign === "top" ? "pb-3" : "pt-3",
294+ className
295+ )}
296+ >
297+ {payload
298+ .filter((item) => item.type !== "none")
299+ .map((item, index) => {
300+ const key = `${nameKey ?? item.dataKey ?? "value"}`
301+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
302+
303+ return (
304+ <div
305+ key={index}
306+ className={cn(
307+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
308+ )}
309+ >
310+ {itemConfig?.icon && !hideIcon ? (
311+ <itemConfig.icon />
312+ ) : (
313+ <div
314+ className="h-2 w-2 shrink-0 rounded-[2px]"
315+ style={{
316+ backgroundColor: item.color,
317+ }}
318+ />
319+ )}
320+ {itemConfig?.label}
321+ </div>
322+ )
323+ })}
324+ </div>
325+ )
326+}
327+
328+// Helper to extract item config from a payload.
329+function getPayloadConfigFromPayload(
330+ config: ChartConfig,
331+ payload: unknown,
332+ key: string
333+) {
334+ if (typeof payload !== "object" || payload === null) {
335+ return undefined
336+ }
337+
338+ const payloadPayload =
339+ "payload" in payload &&
340+ typeof payload.payload === "object" &&
341+ payload.payload !== null
342+ ? payload.payload
343+ : undefined
344+
345+ let configLabelKey: string = key
346+
347+ if (
348+ key in payload &&
349+ typeof payload[key as keyof typeof payload] === "string"
350+ ) {
351+ configLabelKey = payload[key as keyof typeof payload] as string
352+ } else if (
353+ payloadPayload &&
354+ key in payloadPayload &&
355+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
356+ ) {
357+ configLabelKey = payloadPayload[
358+ key as keyof typeof payloadPayload
359+ ] as string
360+ }
361+
362+ return configLabelKey in config ? config[configLabelKey] : config[key]
363+}
364+
365+export {
366+ ChartContainer,
367+ ChartTooltip,
368+ ChartTooltipContent,
369+ ChartLegend,
370+ ChartLegendContent,
371+ ChartStyle,
372+}
packages/ui/src/components/checkbox.tsx+32−0
@@ -0,0 +1,32 @@
1+"use client"
2+
3+import * as React from "react"
4+import { CheckIcon } from "lucide-react"
5+import { Checkbox as CheckboxPrimitive } from "radix-ui"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+
9+function Checkbox({
10+ className,
11+ ...props
12+}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
13+ return (
14+ <CheckboxPrimitive.Root
15+ data-slot="checkbox"
16+ className={cn(
17+ "peer size-4 shrink-0 rounded-[4px] border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary",
18+ className
19+ )}
20+ {...props}
21+ >
22+ <CheckboxPrimitive.Indicator
23+ data-slot="checkbox-indicator"
24+ className="grid place-content-center text-current transition-none"
25+ >
26+ <CheckIcon className="size-3.5" />
27+ </CheckboxPrimitive.Indicator>
28+ </CheckboxPrimitive.Root>
29+ )
30+}
31+
32+export { Checkbox }
packages/ui/src/components/collapsible.tsx+31−0
@@ -0,0 +1,31 @@
1+import { Collapsible as CollapsiblePrimitive } from "radix-ui"
2+
3+function Collapsible({
4+ ...props
5+}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
6+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
7+}
8+
9+function CollapsibleTrigger({
10+ ...props
11+}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
12+ return (
13+ <CollapsiblePrimitive.CollapsibleTrigger
14+ data-slot="collapsible-trigger"
15+ {...props}
16+ />
17+ )
18+}
19+
20+function CollapsibleContent({
21+ ...props
22+}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
23+ return (
24+ <CollapsiblePrimitive.CollapsibleContent
25+ data-slot="collapsible-content"
26+ {...props}
27+ />
28+ )
29+}
30+
31+export { Collapsible, CollapsibleTrigger, CollapsibleContent }
packages/ui/src/components/combobox.tsx+310−0
@@ -0,0 +1,310 @@
1+"use client"
2+
3+import * as React from "react"
4+import { Combobox as ComboboxPrimitive } from "@base-ui/react"
5+import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+import { Button } from "@executor/ui/components/button"
9+import {
10+ InputGroup,
11+ InputGroupAddon,
12+ InputGroupButton,
13+ InputGroupInput,
14+} from "@executor/ui/components/input-group"
15+
16+const Combobox = ComboboxPrimitive.Root
17+
18+function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
19+ return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />
20+}
21+
22+function ComboboxTrigger({
23+ className,
24+ children,
25+ ...props
26+}: ComboboxPrimitive.Trigger.Props) {
27+ return (
28+ <ComboboxPrimitive.Trigger
29+ data-slot="combobox-trigger"
30+ className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
31+ {...props}
32+ >
33+ {children}
34+ <ChevronDownIcon
35+ data-slot="combobox-trigger-icon"
36+ className="pointer-events-none size-4 text-muted-foreground"
37+ />
38+ </ComboboxPrimitive.Trigger>
39+ )
40+}
41+
42+function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
43+ return (
44+ <ComboboxPrimitive.Clear
45+ data-slot="combobox-clear"
46+ render={<InputGroupButton variant="ghost" size="icon-xs" />}
47+ className={cn(className)}
48+ {...props}
49+ >
50+ <XIcon className="pointer-events-none" />
51+ </ComboboxPrimitive.Clear>
52+ )
53+}
54+
55+function ComboboxInput({
56+ className,
57+ children,
58+ disabled = false,
59+ showTrigger = true,
60+ showClear = false,
61+ ...props
62+}: ComboboxPrimitive.Input.Props & {
63+ showTrigger?: boolean
64+ showClear?: boolean
65+}) {
66+ return (
67+ <InputGroup className={cn("w-auto", className)}>
68+ <ComboboxPrimitive.Input
69+ render={<InputGroupInput disabled={disabled} />}
70+ {...props}
71+ />
72+ <InputGroupAddon align="inline-end">
73+ {showTrigger && (
74+ <InputGroupButton
75+ size="icon-xs"
76+ variant="ghost"
77+ asChild
78+ data-slot="input-group-button"
79+ className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
80+ disabled={disabled}
81+ >
82+ <ComboboxTrigger />
83+ </InputGroupButton>
84+ )}
85+ {showClear && <ComboboxClear disabled={disabled} />}
86+ </InputGroupAddon>
87+ {children}
88+ </InputGroup>
89+ )
90+}
91+
92+function ComboboxContent({
93+ className,
94+ side = "bottom",
95+ sideOffset = 6,
96+ align = "start",
97+ alignOffset = 0,
98+ anchor,
99+ ...props
100+}: ComboboxPrimitive.Popup.Props &
101+ Pick<
102+ ComboboxPrimitive.Positioner.Props,
103+ "side" | "align" | "sideOffset" | "alignOffset" | "anchor"
104+ >) {
105+ return (
106+ <ComboboxPrimitive.Portal>
107+ <ComboboxPrimitive.Positioner
108+ side={side}
109+ sideOffset={sideOffset}
110+ align={align}
111+ alignOffset={alignOffset}
112+ anchor={anchor}
113+ className="isolate z-50"
114+ >
115+ <ComboboxPrimitive.Popup
116+ data-slot="combobox-content"
117+ data-chips={!!anchor}
118+ className={cn(
119+ "group/combobox-content relative max-h-96 w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
120+ className
121+ )}
122+ {...props}
123+ />
124+ </ComboboxPrimitive.Positioner>
125+ </ComboboxPrimitive.Portal>
126+ )
127+}
128+
129+function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
130+ return (
131+ <ComboboxPrimitive.List
132+ data-slot="combobox-list"
133+ className={cn(
134+ "max-h-[min(calc(--spacing(96)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0",
135+ className
136+ )}
137+ {...props}
138+ />
139+ )
140+}
141+
142+function ComboboxItem({
143+ className,
144+ children,
145+ ...props
146+}: ComboboxPrimitive.Item.Props) {
147+ return (
148+ <ComboboxPrimitive.Item
149+ data-slot="combobox-item"
150+ className={cn(
151+ "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
152+ className
153+ )}
154+ {...props}
155+ >
156+ {children}
157+ <ComboboxPrimitive.ItemIndicator
158+ data-slot="combobox-item-indicator"
159+ render={
160+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
161+ }
162+ >
163+ <CheckIcon className="pointer-events-none size-4 pointer-coarse:size-5" />
164+ </ComboboxPrimitive.ItemIndicator>
165+ </ComboboxPrimitive.Item>
166+ )
167+}
168+
169+function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
170+ return (
171+ <ComboboxPrimitive.Group
172+ data-slot="combobox-group"
173+ className={cn(className)}
174+ {...props}
175+ />
176+ )
177+}
178+
179+function ComboboxLabel({
180+ className,
181+ ...props
182+}: ComboboxPrimitive.GroupLabel.Props) {
183+ return (
184+ <ComboboxPrimitive.GroupLabel
185+ data-slot="combobox-label"
186+ className={cn(
187+ "px-2 py-1.5 text-xs text-muted-foreground pointer-coarse:px-3 pointer-coarse:py-2 pointer-coarse:text-sm",
188+ className
189+ )}
190+ {...props}
191+ />
192+ )
193+}
194+
195+function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
196+ return (
197+ <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
198+ )
199+}
200+
201+function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
202+ return (
203+ <ComboboxPrimitive.Empty
204+ data-slot="combobox-empty"
205+ className={cn(
206+ "hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex",
207+ className
208+ )}
209+ {...props}
210+ />
211+ )
212+}
213+
214+function ComboboxSeparator({
215+ className,
216+ ...props
217+}: ComboboxPrimitive.Separator.Props) {
218+ return (
219+ <ComboboxPrimitive.Separator
220+ data-slot="combobox-separator"
221+ className={cn("-mx-1 my-1 h-px bg-border", className)}
222+ {...props}
223+ />
224+ )
225+}
226+
227+function ComboboxChips({
228+ className,
229+ ...props
230+}: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> &
231+ ComboboxPrimitive.Chips.Props) {
232+ return (
233+ <ComboboxPrimitive.Chips
234+ data-slot="combobox-chips"
235+ className={cn(
236+ "flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-[3px] has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1.5 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40",
237+ className
238+ )}
239+ {...props}
240+ />
241+ )
242+}
243+
244+function ComboboxChip({
245+ className,
246+ children,
247+ showRemove = true,
248+ ...props
249+}: ComboboxPrimitive.Chip.Props & {
250+ showRemove?: boolean
251+}) {
252+ return (
253+ <ComboboxPrimitive.Chip
254+ data-slot="combobox-chip"
255+ className={cn(
256+ "flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0",
257+ className
258+ )}
259+ {...props}
260+ >
261+ {children}
262+ {showRemove && (
263+ <ComboboxPrimitive.ChipRemove
264+ render={<Button variant="ghost" size="icon-xs" />}
265+ className="-ml-1 opacity-50 hover:opacity-100"
266+ data-slot="combobox-chip-remove"
267+ >
268+ <XIcon className="pointer-events-none" />
269+ </ComboboxPrimitive.ChipRemove>
270+ )}
271+ </ComboboxPrimitive.Chip>
272+ )
273+}
274+
275+function ComboboxChipsInput({
276+ className,
277+ children,
278+ ...props
279+}: ComboboxPrimitive.Input.Props) {
280+ return (
281+ <ComboboxPrimitive.Input
282+ data-slot="combobox-chip-input"
283+ className={cn("min-w-16 flex-1 outline-none", className)}
284+ {...props}
285+ />
286+ )
287+}
288+
289+function useComboboxAnchor() {
290+ return React.useRef<HTMLDivElement | null>(null)
291+}
292+
293+export {
294+ Combobox,
295+ ComboboxInput,
296+ ComboboxContent,
297+ ComboboxList,
298+ ComboboxItem,
299+ ComboboxGroup,
300+ ComboboxLabel,
301+ ComboboxCollection,
302+ ComboboxEmpty,
303+ ComboboxSeparator,
304+ ComboboxChips,
305+ ComboboxChip,
306+ ComboboxChipsInput,
307+ ComboboxTrigger,
308+ ComboboxValue,
309+ useComboboxAnchor,
310+}
packages/ui/src/components/command.tsx+184−0
@@ -0,0 +1,184 @@
1+"use client"
2+
3+import * as React from "react"
4+import { Command as CommandPrimitive } from "cmdk"
5+import { SearchIcon } from "lucide-react"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+import {
9+ Dialog,
10+ DialogContent,
11+ DialogDescription,
12+ DialogHeader,
13+ DialogTitle,
14+} from "@executor/ui/components/dialog"
15+
16+function Command({
17+ className,
18+ ...props
19+}: React.ComponentProps<typeof CommandPrimitive>) {
20+ return (
21+ <CommandPrimitive
22+ data-slot="command"
23+ className={cn(
24+ "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
25+ className
26+ )}
27+ {...props}
28+ />
29+ )
30+}
31+
32+function CommandDialog({
33+ title = "Command Palette",
34+ description = "Search for a command to run...",
35+ children,
36+ className,
37+ showCloseButton = true,
38+ ...props
39+}: React.ComponentProps<typeof Dialog> & {
40+ title?: string
41+ description?: string
42+ className?: string
43+ showCloseButton?: boolean
44+}) {
45+ return (
46+ <Dialog {...props}>
47+ <DialogHeader className="sr-only">
48+ <DialogTitle>{title}</DialogTitle>
49+ <DialogDescription>{description}</DialogDescription>
50+ </DialogHeader>
51+ <DialogContent
52+ className={cn("overflow-hidden p-0", className)}
53+ showCloseButton={showCloseButton}
54+ >
55+ <Command className="**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
56+ {children}
57+ </Command>
58+ </DialogContent>
59+ </Dialog>
60+ )
61+}
62+
63+function CommandInput({
64+ className,
65+ ...props
66+}: React.ComponentProps<typeof CommandPrimitive.Input>) {
67+ return (
68+ <div
69+ data-slot="command-input-wrapper"
70+ className="flex h-9 items-center gap-2 border-b px-3"
71+ >
72+ <SearchIcon className="size-4 shrink-0 opacity-50" />
73+ <CommandPrimitive.Input
74+ data-slot="command-input"
75+ className={cn(
76+ "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
77+ className
78+ )}
79+ {...props}
80+ />
81+ </div>
82+ )
83+}
84+
85+function CommandList({
86+ className,
87+ ...props
88+}: React.ComponentProps<typeof CommandPrimitive.List>) {
89+ return (
90+ <CommandPrimitive.List
91+ data-slot="command-list"
92+ className={cn(
93+ "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
94+ className
95+ )}
96+ {...props}
97+ />
98+ )
99+}
100+
101+function CommandEmpty({
102+ ...props
103+}: React.ComponentProps<typeof CommandPrimitive.Empty>) {
104+ return (
105+ <CommandPrimitive.Empty
106+ data-slot="command-empty"
107+ className="py-6 text-center text-sm"
108+ {...props}
109+ />
110+ )
111+}
112+
113+function CommandGroup({
114+ className,
115+ ...props
116+}: React.ComponentProps<typeof CommandPrimitive.Group>) {
117+ return (
118+ <CommandPrimitive.Group
119+ data-slot="command-group"
120+ className={cn(
121+ "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
122+ className
123+ )}
124+ {...props}
125+ />
126+ )
127+}
128+
129+function CommandSeparator({
130+ className,
131+ ...props
132+}: React.ComponentProps<typeof CommandPrimitive.Separator>) {
133+ return (
134+ <CommandPrimitive.Separator
135+ data-slot="command-separator"
136+ className={cn("-mx-1 h-px bg-border", className)}
137+ {...props}
138+ />
139+ )
140+}
141+
142+function CommandItem({
143+ className,
144+ ...props
145+}: React.ComponentProps<typeof CommandPrimitive.Item>) {
146+ return (
147+ <CommandPrimitive.Item
148+ data-slot="command-item"
149+ className={cn(
150+ "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
151+ className
152+ )}
153+ {...props}
154+ />
155+ )
156+}
157+
158+function CommandShortcut({
159+ className,
160+ ...props
161+}: React.ComponentProps<"span">) {
162+ return (
163+ <span
164+ data-slot="command-shortcut"
165+ className={cn(
166+ "ml-auto text-xs tracking-widest text-muted-foreground",
167+ className
168+ )}
169+ {...props}
170+ />
171+ )
172+}
173+
174+export {
175+ Command,
176+ CommandDialog,
177+ CommandInput,
178+ CommandList,
179+ CommandEmpty,
180+ CommandGroup,
181+ CommandItem,
182+ CommandShortcut,
183+ CommandSeparator,
184+}
packages/ui/src/components/context-menu.tsx+252−0
@@ -0,0 +1,252 @@
1+"use client"
2+
3+import * as React from "react"
4+import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
5+import { ContextMenu as ContextMenuPrimitive } from "radix-ui"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+
9+function ContextMenu({
10+ ...props
11+}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
12+ return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
13+}
14+
15+function ContextMenuTrigger({
16+ ...props
17+}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
18+ return (
19+ <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
20+ )
21+}
22+
23+function ContextMenuGroup({
24+ ...props
25+}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
26+ return (
27+ <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
28+ )
29+}
30+
31+function ContextMenuPortal({
32+ ...props
33+}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
34+ return (
35+ <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
36+ )
37+}
38+
39+function ContextMenuSub({
40+ ...props
41+}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
42+ return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
43+}
44+
45+function ContextMenuRadioGroup({
46+ ...props
47+}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
48+ return (
49+ <ContextMenuPrimitive.RadioGroup
50+ data-slot="context-menu-radio-group"
51+ {...props}
52+ />
53+ )
54+}
55+
56+function ContextMenuSubTrigger({
57+ className,
58+ inset,
59+ children,
60+ ...props
61+}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
62+ inset?: boolean
63+}) {
64+ return (
65+ <ContextMenuPrimitive.SubTrigger
66+ data-slot="context-menu-sub-trigger"
67+ data-inset={inset}
68+ className={cn(
69+ "flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
70+ className
71+ )}
72+ {...props}
73+ >
74+ {children}
75+ <ChevronRightIcon className="ml-auto" />
76+ </ContextMenuPrimitive.SubTrigger>
77+ )
78+}
79+
80+function ContextMenuSubContent({
81+ className,
82+ ...props
83+}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
84+ return (
85+ <ContextMenuPrimitive.SubContent
86+ data-slot="context-menu-sub-content"
87+ className={cn(
88+ "z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
89+ className
90+ )}
91+ {...props}
92+ />
93+ )
94+}
95+
96+function ContextMenuContent({
97+ className,
98+ ...props
99+}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
100+ return (
101+ <ContextMenuPrimitive.Portal>
102+ <ContextMenuPrimitive.Content
103+ data-slot="context-menu-content"
104+ className={cn(
105+ "z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
106+ className
107+ )}
108+ {...props}
109+ />
110+ </ContextMenuPrimitive.Portal>
111+ )
112+}
113+
114+function ContextMenuItem({
115+ className,
116+ inset,
117+ variant = "default",
118+ ...props
119+}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
120+ inset?: boolean
121+ variant?: "default" | "destructive"
122+}) {
123+ return (
124+ <ContextMenuPrimitive.Item
125+ data-slot="context-menu-item"
126+ data-inset={inset}
127+ data-variant={variant}
128+ className={cn(
129+ "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
130+ className
131+ )}
132+ {...props}
133+ />
134+ )
135+}
136+
137+function ContextMenuCheckboxItem({
138+ className,
139+ children,
140+ checked,
141+ ...props
142+}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
143+ return (
144+ <ContextMenuPrimitive.CheckboxItem
145+ data-slot="context-menu-checkbox-item"
146+ className={cn(
147+ "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
148+ className
149+ )}
150+ checked={checked}
151+ {...props}
152+ >
153+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
154+ <ContextMenuPrimitive.ItemIndicator>
155+ <CheckIcon className="size-4" />
156+ </ContextMenuPrimitive.ItemIndicator>
157+ </span>
158+ {children}
159+ </ContextMenuPrimitive.CheckboxItem>
160+ )
161+}
162+
163+function ContextMenuRadioItem({
164+ className,
165+ children,
166+ ...props
167+}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
168+ return (
169+ <ContextMenuPrimitive.RadioItem
170+ data-slot="context-menu-radio-item"
171+ className={cn(
172+ "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
173+ className
174+ )}
175+ {...props}
176+ >
177+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
178+ <ContextMenuPrimitive.ItemIndicator>
179+ <CircleIcon className="size-2 fill-current" />
180+ </ContextMenuPrimitive.ItemIndicator>
181+ </span>
182+ {children}
183+ </ContextMenuPrimitive.RadioItem>
184+ )
185+}
186+
187+function ContextMenuLabel({
188+ className,
189+ inset,
190+ ...props
191+}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
192+ inset?: boolean
193+}) {
194+ return (
195+ <ContextMenuPrimitive.Label
196+ data-slot="context-menu-label"
197+ data-inset={inset}
198+ className={cn(
199+ "px-2 py-1.5 text-sm font-medium text-foreground data-[inset]:pl-8",
200+ className
201+ )}
202+ {...props}
203+ />
204+ )
205+}
206+
207+function ContextMenuSeparator({
208+ className,
209+ ...props
210+}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
211+ return (
212+ <ContextMenuPrimitive.Separator
213+ data-slot="context-menu-separator"
214+ className={cn("-mx-1 my-1 h-px bg-border", className)}
215+ {...props}
216+ />
217+ )
218+}
219+
220+function ContextMenuShortcut({
221+ className,
222+ ...props
223+}: React.ComponentProps<"span">) {
224+ return (
225+ <span
226+ data-slot="context-menu-shortcut"
227+ className={cn(
228+ "ml-auto text-xs tracking-widest text-muted-foreground",
229+ className
230+ )}
231+ {...props}
232+ />
233+ )
234+}
235+
236+export {
237+ ContextMenu,
238+ ContextMenuTrigger,
239+ ContextMenuContent,
240+ ContextMenuItem,
241+ ContextMenuCheckboxItem,
242+ ContextMenuRadioItem,
243+ ContextMenuLabel,
244+ ContextMenuSeparator,
245+ ContextMenuShortcut,
246+ ContextMenuGroup,
247+ ContextMenuPortal,
248+ ContextMenuSub,
249+ ContextMenuSubContent,
250+ ContextMenuSubTrigger,
251+ ContextMenuRadioGroup,
252+}
packages/ui/src/components/dialog.tsx+156−0
@@ -0,0 +1,156 @@
1+import * as React from "react"
2+import { XIcon } from "lucide-react"
3+import { Dialog as DialogPrimitive } from "radix-ui"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+import { Button } from "@executor/ui/components/button"
7+
8+function Dialog({
9+ ...props
10+}: React.ComponentProps<typeof DialogPrimitive.Root>) {
11+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
12+}
13+
14+function DialogTrigger({
15+ ...props
16+}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
17+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
18+}
19+
20+function DialogPortal({
21+ ...props
22+}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
23+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
24+}
25+
26+function DialogClose({
27+ ...props
28+}: React.ComponentProps<typeof DialogPrimitive.Close>) {
29+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
30+}
31+
32+function DialogOverlay({
33+ className,
34+ ...props
35+}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
36+ return (
37+ <DialogPrimitive.Overlay
38+ data-slot="dialog-overlay"
39+ className={cn(
40+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
41+ className
42+ )}
43+ {...props}
44+ />
45+ )
46+}
47+
48+function DialogContent({
49+ className,
50+ children,
51+ showCloseButton = true,
52+ ...props
53+}: React.ComponentProps<typeof DialogPrimitive.Content> & {
54+ showCloseButton?: boolean
55+}) {
56+ return (
57+ <DialogPortal data-slot="dialog-portal">
58+ <DialogOverlay />
59+ <DialogPrimitive.Content
60+ data-slot="dialog-content"
61+ className={cn(
62+ "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
63+ className
64+ )}
65+ {...props}
66+ >
67+ {children}
68+ {showCloseButton && (
69+ <DialogPrimitive.Close
70+ data-slot="dialog-close"
71+ className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
72+ >
73+ <XIcon />
74+ <span className="sr-only">Close</span>
75+ </DialogPrimitive.Close>
76+ )}
77+ </DialogPrimitive.Content>
78+ </DialogPortal>
79+ )
80+}
81+
82+function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
83+ return (
84+ <div
85+ data-slot="dialog-header"
86+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
87+ {...props}
88+ />
89+ )
90+}
91+
92+function DialogFooter({
93+ className,
94+ showCloseButton = false,
95+ children,
96+ ...props
97+}: React.ComponentProps<"div"> & {
98+ showCloseButton?: boolean
99+}) {
100+ return (
101+ <div
102+ data-slot="dialog-footer"
103+ className={cn(
104+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
105+ className
106+ )}
107+ {...props}
108+ >
109+ {children}
110+ {showCloseButton && (
111+ <DialogPrimitive.Close asChild>
112+ <Button variant="outline">Close</Button>
113+ </DialogPrimitive.Close>
114+ )}
115+ </div>
116+ )
117+}
118+
119+function DialogTitle({
120+ className,
121+ ...props
122+}: React.ComponentProps<typeof DialogPrimitive.Title>) {
123+ return (
124+ <DialogPrimitive.Title
125+ data-slot="dialog-title"
126+ className={cn("text-lg leading-none font-semibold", className)}
127+ {...props}
128+ />
129+ )
130+}
131+
132+function DialogDescription({
133+ className,
134+ ...props
135+}: React.ComponentProps<typeof DialogPrimitive.Description>) {
136+ return (
137+ <DialogPrimitive.Description
138+ data-slot="dialog-description"
139+ className={cn("text-sm text-muted-foreground", className)}
140+ {...props}
141+ />
142+ )
143+}
144+
145+export {
146+ Dialog,
147+ DialogClose,
148+ DialogContent,
149+ DialogDescription,
150+ DialogFooter,
151+ DialogHeader,
152+ DialogOverlay,
153+ DialogPortal,
154+ DialogTitle,
155+ DialogTrigger,
156+}
packages/ui/src/components/direction.tsx+22−0
@@ -0,0 +1,22 @@
1+"use client"
2+
3+import * as React from "react"
4+import { Direction } from "radix-ui"
5+
6+function DirectionProvider({
7+ dir,
8+ direction,
9+ children,
10+}: React.ComponentProps<typeof Direction.DirectionProvider> & {
11+ direction?: React.ComponentProps<typeof Direction.DirectionProvider>["dir"]
12+}) {
13+ return (
14+ <Direction.DirectionProvider dir={direction ?? dir}>
15+ {children}
16+ </Direction.DirectionProvider>
17+ )
18+}
19+
20+const useDirection = Direction.useDirection
21+
22+export { DirectionProvider, useDirection }
packages/ui/src/components/drawer.tsx+133−0
@@ -0,0 +1,133 @@
1+import * as React from "react"
2+import { Drawer as DrawerPrimitive } from "vaul"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function Drawer({
7+ ...props
8+}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
9+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />
10+}
11+
12+function DrawerTrigger({
13+ ...props
14+}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
15+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
16+}
17+
18+function DrawerPortal({
19+ ...props
20+}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
21+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
22+}
23+
24+function DrawerClose({
25+ ...props
26+}: React.ComponentProps<typeof DrawerPrimitive.Close>) {
27+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
28+}
29+
30+function DrawerOverlay({
31+ className,
32+ ...props
33+}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
34+ return (
35+ <DrawerPrimitive.Overlay
36+ data-slot="drawer-overlay"
37+ className={cn(
38+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
39+ className
40+ )}
41+ {...props}
42+ />
43+ )
44+}
45+
46+function DrawerContent({
47+ className,
48+ children,
49+ ...props
50+}: React.ComponentProps<typeof DrawerPrimitive.Content>) {
51+ return (
52+ <DrawerPortal data-slot="drawer-portal">
53+ <DrawerOverlay />
54+ <DrawerPrimitive.Content
55+ data-slot="drawer-content"
56+ className={cn(
57+ "group/drawer-content fixed z-50 flex h-auto flex-col bg-background",
58+ "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
59+ "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
60+ "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
61+ "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
62+ className
63+ )}
64+ {...props}
65+ >
66+ <div className="mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
67+ {children}
68+ </DrawerPrimitive.Content>
69+ </DrawerPortal>
70+ )
71+}
72+
73+function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
74+ return (
75+ <div
76+ data-slot="drawer-header"
77+ className={cn(
78+ "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
79+ className
80+ )}
81+ {...props}
82+ />
83+ )
84+}
85+
86+function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
87+ return (
88+ <div
89+ data-slot="drawer-footer"
90+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
91+ {...props}
92+ />
93+ )
94+}
95+
96+function DrawerTitle({
97+ className,
98+ ...props
99+}: React.ComponentProps<typeof DrawerPrimitive.Title>) {
100+ return (
101+ <DrawerPrimitive.Title
102+ data-slot="drawer-title"
103+ className={cn("font-semibold text-foreground", className)}
104+ {...props}
105+ />
106+ )
107+}
108+
109+function DrawerDescription({
110+ className,
111+ ...props
112+}: React.ComponentProps<typeof DrawerPrimitive.Description>) {
113+ return (
114+ <DrawerPrimitive.Description
115+ data-slot="drawer-description"
116+ className={cn("text-sm text-muted-foreground", className)}
117+ {...props}
118+ />
119+ )
120+}
121+
122+export {
123+ Drawer,
124+ DrawerPortal,
125+ DrawerOverlay,
126+ DrawerTrigger,
127+ DrawerClose,
128+ DrawerContent,
129+ DrawerHeader,
130+ DrawerFooter,
131+ DrawerTitle,
132+ DrawerDescription,
133+}
packages/ui/src/components/dropdown-menu.tsx+257−0
@@ -0,0 +1,257 @@
1+"use client"
2+
3+import * as React from "react"
4+import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
5+import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+
9+function DropdownMenu({
10+ ...props
11+}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
12+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
13+}
14+
15+function DropdownMenuPortal({
16+ ...props
17+}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
18+ return (
19+ <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
20+ )
21+}
22+
23+function DropdownMenuTrigger({
24+ ...props
25+}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
26+ return (
27+ <DropdownMenuPrimitive.Trigger
28+ data-slot="dropdown-menu-trigger"
29+ {...props}
30+ />
31+ )
32+}
33+
34+function DropdownMenuContent({
35+ className,
36+ sideOffset = 4,
37+ ...props
38+}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
39+ return (
40+ <DropdownMenuPrimitive.Portal>
41+ <DropdownMenuPrimitive.Content
42+ data-slot="dropdown-menu-content"
43+ sideOffset={sideOffset}
44+ className={cn(
45+ "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
46+ className
47+ )}
48+ {...props}
49+ />
50+ </DropdownMenuPrimitive.Portal>
51+ )
52+}
53+
54+function DropdownMenuGroup({
55+ ...props
56+}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
57+ return (
58+ <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
59+ )
60+}
61+
62+function DropdownMenuItem({
63+ className,
64+ inset,
65+ variant = "default",
66+ ...props
67+}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
68+ inset?: boolean
69+ variant?: "default" | "destructive"
70+}) {
71+ return (
72+ <DropdownMenuPrimitive.Item
73+ data-slot="dropdown-menu-item"
74+ data-inset={inset}
75+ data-variant={variant}
76+ className={cn(
77+ "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
78+ className
79+ )}
80+ {...props}
81+ />
82+ )
83+}
84+
85+function DropdownMenuCheckboxItem({
86+ className,
87+ children,
88+ checked,
89+ ...props
90+}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
91+ return (
92+ <DropdownMenuPrimitive.CheckboxItem
93+ data-slot="dropdown-menu-checkbox-item"
94+ className={cn(
95+ "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
96+ className
97+ )}
98+ checked={checked}
99+ {...props}
100+ >
101+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
102+ <DropdownMenuPrimitive.ItemIndicator>
103+ <CheckIcon className="size-4" />
104+ </DropdownMenuPrimitive.ItemIndicator>
105+ </span>
106+ {children}
107+ </DropdownMenuPrimitive.CheckboxItem>
108+ )
109+}
110+
111+function DropdownMenuRadioGroup({
112+ ...props
113+}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
114+ return (
115+ <DropdownMenuPrimitive.RadioGroup
116+ data-slot="dropdown-menu-radio-group"
117+ {...props}
118+ />
119+ )
120+}
121+
122+function DropdownMenuRadioItem({
123+ className,
124+ children,
125+ ...props
126+}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
127+ return (
128+ <DropdownMenuPrimitive.RadioItem
129+ data-slot="dropdown-menu-radio-item"
130+ className={cn(
131+ "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
132+ className
133+ )}
134+ {...props}
135+ >
136+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
137+ <DropdownMenuPrimitive.ItemIndicator>
138+ <CircleIcon className="size-2 fill-current" />
139+ </DropdownMenuPrimitive.ItemIndicator>
140+ </span>
141+ {children}
142+ </DropdownMenuPrimitive.RadioItem>
143+ )
144+}
145+
146+function DropdownMenuLabel({
147+ className,
148+ inset,
149+ ...props
150+}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
151+ inset?: boolean
152+}) {
153+ return (
154+ <DropdownMenuPrimitive.Label
155+ data-slot="dropdown-menu-label"
156+ data-inset={inset}
157+ className={cn(
158+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
159+ className
160+ )}
161+ {...props}
162+ />
163+ )
164+}
165+
166+function DropdownMenuSeparator({
167+ className,
168+ ...props
169+}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
170+ return (
171+ <DropdownMenuPrimitive.Separator
172+ data-slot="dropdown-menu-separator"
173+ className={cn("-mx-1 my-1 h-px bg-border", className)}
174+ {...props}
175+ />
176+ )
177+}
178+
179+function DropdownMenuShortcut({
180+ className,
181+ ...props
182+}: React.ComponentProps<"span">) {
183+ return (
184+ <span
185+ data-slot="dropdown-menu-shortcut"
186+ className={cn(
187+ "ml-auto text-xs tracking-widest text-muted-foreground",
188+ className
189+ )}
190+ {...props}
191+ />
192+ )
193+}
194+
195+function DropdownMenuSub({
196+ ...props
197+}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
198+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
199+}
200+
201+function DropdownMenuSubTrigger({
202+ className,
203+ inset,
204+ children,
205+ ...props
206+}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
207+ inset?: boolean
208+}) {
209+ return (
210+ <DropdownMenuPrimitive.SubTrigger
211+ data-slot="dropdown-menu-sub-trigger"
212+ data-inset={inset}
213+ className={cn(
214+ "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
215+ className
216+ )}
217+ {...props}
218+ >
219+ {children}
220+ <ChevronRightIcon className="ml-auto size-4" />
221+ </DropdownMenuPrimitive.SubTrigger>
222+ )
223+}
224+
225+function DropdownMenuSubContent({
226+ className,
227+ ...props
228+}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
229+ return (
230+ <DropdownMenuPrimitive.SubContent
231+ data-slot="dropdown-menu-sub-content"
232+ className={cn(
233+ "z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
234+ className
235+ )}
236+ {...props}
237+ />
238+ )
239+}
240+
241+export {
242+ DropdownMenu,
243+ DropdownMenuPortal,
244+ DropdownMenuTrigger,
245+ DropdownMenuContent,
246+ DropdownMenuGroup,
247+ DropdownMenuLabel,
248+ DropdownMenuItem,
249+ DropdownMenuCheckboxItem,
250+ DropdownMenuRadioGroup,
251+ DropdownMenuRadioItem,
252+ DropdownMenuSeparator,
253+ DropdownMenuShortcut,
254+ DropdownMenuSub,
255+ DropdownMenuSubTrigger,
256+ DropdownMenuSubContent,
257+}
packages/ui/src/components/empty.tsx+104−0
@@ -0,0 +1,104 @@
1+import { cva, type VariantProps } from "class-variance-authority"
2+
3+import { cn } from "@executor/ui/lib/utils"
4+
5+function Empty({ className, ...props }: React.ComponentProps<"div">) {
6+ return (
7+ <div
8+ data-slot="empty"
9+ className={cn(
10+ "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12",
11+ className
12+ )}
13+ {...props}
14+ />
15+ )
16+}
17+
18+function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
19+ return (
20+ <div
21+ data-slot="empty-header"
22+ className={cn(
23+ "flex max-w-sm flex-col items-center gap-2 text-center",
24+ className
25+ )}
26+ {...props}
27+ />
28+ )
29+}
30+
31+const emptyMediaVariants = cva(
32+ "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
33+ {
34+ variants: {
35+ variant: {
36+ default: "bg-transparent",
37+ icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6",
38+ },
39+ },
40+ defaultVariants: {
41+ variant: "default",
42+ },
43+ }
44+)
45+
46+function EmptyMedia({
47+ className,
48+ variant = "default",
49+ ...props
50+}: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>) {
51+ return (
52+ <div
53+ data-slot="empty-icon"
54+ data-variant={variant}
55+ className={cn(emptyMediaVariants({ variant, className }))}
56+ {...props}
57+ />
58+ )
59+}
60+
61+function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
62+ return (
63+ <div
64+ data-slot="empty-title"
65+ className={cn("text-lg font-medium tracking-tight", className)}
66+ {...props}
67+ />
68+ )
69+}
70+
71+function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
72+ return (
73+ <div
74+ data-slot="empty-description"
75+ className={cn(
76+ "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
77+ className
78+ )}
79+ {...props}
80+ />
81+ )
82+}
83+
84+function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
85+ return (
86+ <div
87+ data-slot="empty-content"
88+ className={cn(
89+ "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
90+ className
91+ )}
92+ {...props}
93+ />
94+ )
95+}
96+
97+export {
98+ Empty,
99+ EmptyHeader,
100+ EmptyTitle,
101+ EmptyDescription,
102+ EmptyContent,
103+ EmptyMedia,
104+}
packages/ui/src/components/field.tsx+248−0
@@ -0,0 +1,248 @@
1+"use client"
2+
3+import { useMemo } from "react"
4+import { cva, type VariantProps } from "class-variance-authority"
5+
6+import { cn } from "@executor/ui/lib/utils"
7+import { Label } from "@executor/ui/components/label"
8+import { Separator } from "@executor/ui/components/separator"
9+
10+function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
11+ return (
12+ <fieldset
13+ data-slot="field-set"
14+ className={cn(
15+ "flex flex-col gap-6",
16+ "has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
17+ className
18+ )}
19+ {...props}
20+ />
21+ )
22+}
23+
24+function FieldLegend({
25+ className,
26+ variant = "legend",
27+ ...props
28+}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
29+ return (
30+ <legend
31+ data-slot="field-legend"
32+ data-variant={variant}
33+ className={cn(
34+ "mb-3 font-medium",
35+ "data-[variant=legend]:text-base",
36+ "data-[variant=label]:text-sm",
37+ className
38+ )}
39+ {...props}
40+ />
41+ )
42+}
43+
44+function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
45+ return (
46+ <div
47+ data-slot="field-group"
48+ className={cn(
49+ "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
50+ className
51+ )}
52+ {...props}
53+ />
54+ )
55+}
56+
57+const fieldVariants = cva(
58+ "group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
59+ {
60+ variants: {
61+ orientation: {
62+ vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
63+ horizontal: [
64+ "flex-row items-center",
65+ "[&>[data-slot=field-label]]:flex-auto",
66+ "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
67+ ],
68+ responsive: [
69+ "flex-col @md/field-group:flex-row @md/field-group:items-center [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto",
70+ "@md/field-group:[&>[data-slot=field-label]]:flex-auto",
71+ "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
72+ ],
73+ },
74+ },
75+ defaultVariants: {
76+ orientation: "vertical",
77+ },
78+ }
79+)
80+
81+function Field({
82+ className,
83+ orientation = "vertical",
84+ ...props
85+}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
86+ return (
87+ <div
88+ role="group"
89+ data-slot="field"
90+ data-orientation={orientation}
91+ className={cn(fieldVariants({ orientation }), className)}
92+ {...props}
93+ />
94+ )
95+}
96+
97+function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
98+ return (
99+ <div
100+ data-slot="field-content"
101+ className={cn(
102+ "group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
103+ className
104+ )}
105+ {...props}
106+ />
107+ )
108+}
109+
110+function FieldLabel({
111+ className,
112+ ...props
113+}: React.ComponentProps<typeof Label>) {
114+ return (
115+ <Label
116+ data-slot="field-label"
117+ className={cn(
118+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
119+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
120+ "has-data-[state=checked]:border-primary has-data-[state=checked]:bg-primary/5 dark:has-data-[state=checked]:bg-primary/10",
121+ className
122+ )}
123+ {...props}
124+ />
125+ )
126+}
127+
128+function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
129+ return (
130+ <div
131+ data-slot="field-label"
132+ className={cn(
133+ "flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50",
134+ className
135+ )}
136+ {...props}
137+ />
138+ )
139+}
140+
141+function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
142+ return (
143+ <p
144+ data-slot="field-description"
145+ className={cn(
146+ "text-sm leading-normal font-normal text-muted-foreground group-has-[[data-orientation=horizontal]]/field:text-balance",
147+ "last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5",
148+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
149+ className
150+ )}
151+ {...props}
152+ />
153+ )
154+}
155+
156+function FieldSeparator({
157+ children,
158+ className,
159+ ...props
160+}: React.ComponentProps<"div"> & {
161+ children?: React.ReactNode
162+}) {
163+ return (
164+ <div
165+ data-slot="field-separator"
166+ data-content={!!children}
167+ className={cn(
168+ "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
169+ className
170+ )}
171+ {...props}
172+ >
173+ <Separator className="absolute inset-0 top-1/2" />
174+ {children && (
175+ <span
176+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
177+ data-slot="field-separator-content"
178+ >
179+ {children}
180+ </span>
181+ )}
182+ </div>
183+ )
184+}
185+
186+function FieldError({
187+ className,
188+ children,
189+ errors,
190+ ...props
191+}: React.ComponentProps<"div"> & {
192+ errors?: Array<{ message?: string } | undefined>
193+}) {
194+ const content = useMemo(() => {
195+ if (children) {
196+ return children
197+ }
198+
199+ if (!errors?.length) {
200+ return null
201+ }
202+
203+ const uniqueErrors = [
204+ ...new Map(errors.map((error) => [error?.message, error])).values(),
205+ ]
206+
207+ if (uniqueErrors?.length == 1) {
208+ return uniqueErrors[0]?.message
209+ }
210+
211+ return (
212+ <ul className="ml-4 flex list-disc flex-col gap-1">
213+ {uniqueErrors.map(
214+ (error, index) =>
215+ error?.message && <li key={index}>{error.message}</li>
216+ )}
217+ </ul>
218+ )
219+ }, [children, errors])
220+
221+ if (!content) {
222+ return null
223+ }
224+
225+ return (
226+ <div
227+ role="alert"
228+ data-slot="field-error"
229+ className={cn("text-sm font-normal text-destructive", className)}
230+ {...props}
231+ >
232+ {content}
233+ </div>
234+ )
235+}
236+
237+export {
238+ Field,
239+ FieldLabel,
240+ FieldDescription,
241+ FieldError,
242+ FieldGroup,
243+ FieldLegend,
244+ FieldSeparator,
245+ FieldSet,
246+ FieldContent,
247+ FieldTitle,
248+}
packages/ui/src/components/form.tsx+165−0
@@ -0,0 +1,165 @@
1+import * as React from "react"
2+import type { Label as LabelPrimitive } from "radix-ui"
3+import { Slot } from "radix-ui"
4+import {
5+ Controller,
6+ FormProvider,
7+ useFormContext,
8+ useFormState,
9+ type ControllerProps,
10+ type FieldPath,
11+ type FieldValues,
12+} from "react-hook-form"
13+
14+import { cn } from "@executor/ui/lib/utils"
15+import { Label } from "@executor/ui/components/label"
16+
17+const Form = FormProvider
18+
19+type FormFieldContextValue<
20+ TFieldValues extends FieldValues = FieldValues,
21+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
22+> = {
23+ name: TName
24+}
25+
26+const FormFieldContext = React.createContext<FormFieldContextValue>(
27+ {} as FormFieldContextValue
28+)
29+
30+const FormField = <
31+ TFieldValues extends FieldValues = FieldValues,
32+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
33+>({
34+ ...props
35+}: ControllerProps<TFieldValues, TName>) => {
36+ return (
37+ <FormFieldContext.Provider value={{ name: props.name }}>
38+ <Controller {...props} />
39+ </FormFieldContext.Provider>
40+ )
41+}
42+
43+const useFormField = () => {
44+ const fieldContext = React.useContext(FormFieldContext)
45+ const itemContext = React.useContext(FormItemContext)
46+ const { getFieldState } = useFormContext()
47+ const formState = useFormState({ name: fieldContext.name })
48+ const fieldState = getFieldState(fieldContext.name, formState)
49+
50+ if (!fieldContext) {
51+ throw new Error("useFormField should be used within <FormField>")
52+ }
53+
54+ const { id } = itemContext
55+
56+ return {
57+ id,
58+ name: fieldContext.name,
59+ formItemId: `${id}-form-item`,
60+ formDescriptionId: `${id}-form-item-description`,
61+ formMessageId: `${id}-form-item-message`,
62+ ...fieldState,
63+ }
64+}
65+
66+type FormItemContextValue = {
67+ id: string
68+}
69+
70+const FormItemContext = React.createContext<FormItemContextValue>(
71+ {} as FormItemContextValue
72+)
73+
74+function FormItem({ className, ...props }: React.ComponentProps<"div">) {
75+ const id = React.useId()
76+
77+ return (
78+ <FormItemContext.Provider value={{ id }}>
79+ <div
80+ data-slot="form-item"
81+ className={cn("grid gap-2", className)}
82+ {...props}
83+ />
84+ </FormItemContext.Provider>
85+ )
86+}
87+
88+function FormLabel({
89+ className,
90+ ...props
91+}: React.ComponentProps<typeof LabelPrimitive.Root>) {
92+ const { error, formItemId } = useFormField()
93+
94+ return (
95+ <Label
96+ data-slot="form-label"
97+ data-error={!!error}
98+ className={cn("data-[error=true]:text-destructive", className)}
99+ htmlFor={formItemId}
100+ {...props}
101+ />
102+ )
103+}
104+
105+function FormControl({ ...props }: React.ComponentProps<typeof Slot.Root>) {
106+ const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
107+
108+ return (
109+ <Slot.Root
110+ data-slot="form-control"
111+ id={formItemId}
112+ aria-describedby={
113+ !error
114+ ? `${formDescriptionId}`
115+ : `${formDescriptionId} ${formMessageId}`
116+ }
117+ aria-invalid={!!error}
118+ {...props}
119+ />
120+ )
121+}
122+
123+function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
124+ const { formDescriptionId } = useFormField()
125+
126+ return (
127+ <p
128+ data-slot="form-description"
129+ id={formDescriptionId}
130+ className={cn("text-sm text-muted-foreground", className)}
131+ {...props}
132+ />
133+ )
134+}
135+
136+function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
137+ const { error, formMessageId } = useFormField()
138+ const body = error ? String(error?.message ?? "") : props.children
139+
140+ if (!body) {
141+ return null
142+ }
143+
144+ return (
145+ <p
146+ data-slot="form-message"
147+ id={formMessageId}
148+ className={cn("text-sm text-destructive", className)}
149+ {...props}
150+ >
151+ {body}
152+ </p>
153+ )
154+}
155+
156+export {
157+ useFormField,
158+ Form,
159+ FormItem,
160+ FormLabel,
161+ FormControl,
162+ FormDescription,
163+ FormMessage,
164+ FormField,
165+}
packages/ui/src/components/hover-card.tsx+42−0
@@ -0,0 +1,42 @@
1+import * as React from "react"
2+import { HoverCard as HoverCardPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function HoverCard({
7+ ...props
8+}: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
9+ return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
10+}
11+
12+function HoverCardTrigger({
13+ ...props
14+}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
15+ return (
16+ <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
17+ )
18+}
19+
20+function HoverCardContent({
21+ className,
22+ align = "center",
23+ sideOffset = 4,
24+ ...props
25+}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
26+ return (
27+ <HoverCardPrimitive.Portal data-slot="hover-card-portal">
28+ <HoverCardPrimitive.Content
29+ data-slot="hover-card-content"
30+ align={align}
31+ sideOffset={sideOffset}
32+ className={cn(
33+ "z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
34+ className
35+ )}
36+ {...props}
37+ />
38+ </HoverCardPrimitive.Portal>
39+ )
40+}
41+
42+export { HoverCard, HoverCardTrigger, HoverCardContent }
packages/ui/src/components/input-group.tsx+168−0
@@ -0,0 +1,168 @@
1+import * as React from "react"
2+import { cva, type VariantProps } from "class-variance-authority"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+import { Button } from "@executor/ui/components/button"
6+import { Input } from "@executor/ui/components/input"
7+import { Textarea } from "@executor/ui/components/textarea"
8+
9+function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
10+ return (
11+ <div
12+ data-slot="input-group"
13+ role="group"
14+ className={cn(
15+ "group/input-group relative flex w-full items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none dark:bg-input/30",
16+ "h-9 min-w-0 has-[>textarea]:h-auto",
17+
18+ // Variants based on alignment.
19+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
20+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
21+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
22+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
23+
24+ // Focus state.
25+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50",
26+
27+ // Error state.
28+ "has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
29+
30+ className
31+ )}
32+ {...props}
33+ />
34+ )
35+}
36+
37+const inputGroupAddonVariants = cva(
38+ "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
39+ {
40+ variants: {
41+ align: {
42+ "inline-start":
43+ "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
44+ "inline-end":
45+ "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
46+ "block-start":
47+ "order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3",
48+ "block-end":
49+ "order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3",
50+ },
51+ },
52+ defaultVariants: {
53+ align: "inline-start",
54+ },
55+ }
56+)
57+
58+function InputGroupAddon({
59+ className,
60+ align = "inline-start",
61+ ...props
62+}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
63+ return (
64+ <div
65+ role="group"
66+ data-slot="input-group-addon"
67+ data-align={align}
68+ className={cn(inputGroupAddonVariants({ align }), className)}
69+ onClick={(e) => {
70+ if ((e.target as HTMLElement).closest("button")) {
71+ return
72+ }
73+ e.currentTarget.parentElement?.querySelector("input")?.focus()
74+ }}
75+ {...props}
76+ />
77+ )
78+}
79+
80+const inputGroupButtonVariants = cva(
81+ "flex items-center gap-2 text-sm shadow-none",
82+ {
83+ variants: {
84+ size: {
85+ xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
86+ sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
87+ "icon-xs":
88+ "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
89+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
90+ },
91+ },
92+ defaultVariants: {
93+ size: "xs",
94+ },
95+ }
96+)
97+
98+function InputGroupButton({
99+ className,
100+ type = "button",
101+ variant = "ghost",
102+ size = "xs",
103+ ...props
104+}: Omit<React.ComponentProps<typeof Button>, "size"> &
105+ VariantProps<typeof inputGroupButtonVariants>) {
106+ return (
107+ <Button
108+ type={type}
109+ data-size={size}
110+ variant={variant}
111+ className={cn(inputGroupButtonVariants({ size }), className)}
112+ {...props}
113+ />
114+ )
115+}
116+
117+function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
118+ return (
119+ <span
120+ className={cn(
121+ "flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
122+ className
123+ )}
124+ {...props}
125+ />
126+ )
127+}
128+
129+function InputGroupInput({
130+ className,
131+ ...props
132+}: React.ComponentProps<"input">) {
133+ return (
134+ <Input
135+ data-slot="input-group-control"
136+ className={cn(
137+ "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
138+ className
139+ )}
140+ {...props}
141+ />
142+ )
143+}
144+
145+function InputGroupTextarea({
146+ className,
147+ ...props
148+}: React.ComponentProps<"textarea">) {
149+ return (
150+ <Textarea
151+ data-slot="input-group-control"
152+ className={cn(
153+ "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
154+ className
155+ )}
156+ {...props}
157+ />
158+ )
159+}
160+
161+export {
162+ InputGroup,
163+ InputGroupAddon,
164+ InputGroupButton,
165+ InputGroupText,
166+ InputGroupInput,
167+ InputGroupTextarea,
168+}
packages/ui/src/components/input-otp.tsx+77−0
@@ -0,0 +1,77 @@
1+"use client"
2+
3+import * as React from "react"
4+import { OTPInput, OTPInputContext } from "input-otp"
5+import { MinusIcon } from "lucide-react"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+
9+function InputOTP({
10+ className,
11+ containerClassName,
12+ ...props
13+}: React.ComponentProps<typeof OTPInput> & {
14+ containerClassName?: string
15+}) {
16+ return (
17+ <OTPInput
18+ data-slot="input-otp"
19+ containerClassName={cn(
20+ "flex items-center gap-2 has-disabled:opacity-50",
21+ containerClassName
22+ )}
23+ className={cn("disabled:cursor-not-allowed", className)}
24+ {...props}
25+ />
26+ )
27+}
28+
29+function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
30+ return (
31+ <div
32+ data-slot="input-otp-group"
33+ className={cn("flex items-center", className)}
34+ {...props}
35+ />
36+ )
37+}
38+
39+function InputOTPSlot({
40+ index,
41+ className,
42+ ...props
43+}: React.ComponentProps<"div"> & {
44+ index: number
45+}) {
46+ const inputOTPContext = React.useContext(OTPInputContext)
47+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
48+
49+ return (
50+ <div
51+ data-slot="input-otp-slot"
52+ data-active={isActive}
53+ className={cn(
54+ "relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-[3px] data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40",
55+ className
56+ )}
57+ {...props}
58+ >
59+ {char}
60+ {hasFakeCaret && (
61+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
62+ <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
63+ </div>
64+ )}
65+ </div>
66+ )
67+}
68+
69+function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
70+ return (
71+ <div data-slot="input-otp-separator" role="separator" {...props}>
72+ <MinusIcon />
73+ </div>
74+ )
75+}
76+
77+export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
packages/ui/src/components/input.tsx+21−0
@@ -0,0 +1,21 @@
1+import * as React from "react"
2+
3+import { cn } from "@executor/ui/lib/utils"
4+
5+function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6+ return (
7+ <input
8+ type={type}
9+ data-slot="input"
10+ className={cn(
11+ "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
12+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
13+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
14+ className
15+ )}
16+ {...props}
17+ />
18+ )
19+}
20+
21+export { Input }
packages/ui/src/components/item.tsx+193−0
@@ -0,0 +1,193 @@
1+import * as React from "react"
2+import { cva, type VariantProps } from "class-variance-authority"
3+import { Slot } from "radix-ui"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+import { Separator } from "@executor/ui/components/separator"
7+
8+function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
9+ return (
10+ <div
11+ role="list"
12+ data-slot="item-group"
13+ className={cn("group/item-group flex flex-col", className)}
14+ {...props}
15+ />
16+ )
17+}
18+
19+function ItemSeparator({
20+ className,
21+ ...props
22+}: React.ComponentProps<typeof Separator>) {
23+ return (
24+ <Separator
25+ data-slot="item-separator"
26+ orientation="horizontal"
27+ className={cn("my-0", className)}
28+ {...props}
29+ />
30+ )
31+}
32+
33+const itemVariants = cva(
34+ "group/item flex flex-wrap items-center rounded-md border border-transparent text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-accent/50",
35+ {
36+ variants: {
37+ variant: {
38+ default: "bg-transparent",
39+ outline: "border-border",
40+ muted: "bg-muted/50",
41+ },
42+ size: {
43+ default: "gap-4 p-4",
44+ sm: "gap-2.5 px-4 py-3",
45+ },
46+ },
47+ defaultVariants: {
48+ variant: "default",
49+ size: "default",
50+ },
51+ }
52+)
53+
54+function Item({
55+ className,
56+ variant = "default",
57+ size = "default",
58+ asChild = false,
59+ ...props
60+}: React.ComponentProps<"div"> &
61+ VariantProps<typeof itemVariants> & { asChild?: boolean }) {
62+ const Comp = asChild ? Slot.Root : "div"
63+ return (
64+ <Comp
65+ data-slot="item"
66+ data-variant={variant}
67+ data-size={size}
68+ className={cn(itemVariants({ variant, size, className }))}
69+ {...props}
70+ />
71+ )
72+}
73+
74+const itemMediaVariants = cva(
75+ "flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:translate-y-0.5 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none",
76+ {
77+ variants: {
78+ variant: {
79+ default: "bg-transparent",
80+ icon: "size-8 rounded-sm border bg-muted [&_svg:not([class*='size-'])]:size-4",
81+ image:
82+ "size-10 overflow-hidden rounded-sm [&_img]:size-full [&_img]:object-cover",
83+ },
84+ },
85+ defaultVariants: {
86+ variant: "default",
87+ },
88+ }
89+)
90+
91+function ItemMedia({
92+ className,
93+ variant = "default",
94+ ...props
95+}: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>) {
96+ return (
97+ <div
98+ data-slot="item-media"
99+ data-variant={variant}
100+ className={cn(itemMediaVariants({ variant, className }))}
101+ {...props}
102+ />
103+ )
104+}
105+
106+function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
107+ return (
108+ <div
109+ data-slot="item-content"
110+ className={cn(
111+ "flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none",
112+ className
113+ )}
114+ {...props}
115+ />
116+ )
117+}
118+
119+function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
120+ return (
121+ <div
122+ data-slot="item-title"
123+ className={cn(
124+ "flex w-fit items-center gap-2 text-sm leading-snug font-medium",
125+ className
126+ )}
127+ {...props}
128+ />
129+ )
130+}
131+
132+function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
133+ return (
134+ <p
135+ data-slot="item-description"
136+ className={cn(
137+ "line-clamp-2 text-sm leading-normal font-normal text-balance text-muted-foreground",
138+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
139+ className
140+ )}
141+ {...props}
142+ />
143+ )
144+}
145+
146+function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
147+ return (
148+ <div
149+ data-slot="item-actions"
150+ className={cn("flex items-center gap-2", className)}
151+ {...props}
152+ />
153+ )
154+}
155+
156+function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
157+ return (
158+ <div
159+ data-slot="item-header"
160+ className={cn(
161+ "flex basis-full items-center justify-between gap-2",
162+ className
163+ )}
164+ {...props}
165+ />
166+ )
167+}
168+
169+function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
170+ return (
171+ <div
172+ data-slot="item-footer"
173+ className={cn(
174+ "flex basis-full items-center justify-between gap-2",
175+ className
176+ )}
177+ {...props}
178+ />
179+ )
180+}
181+
182+export {
183+ Item,
184+ ItemMedia,
185+ ItemContent,
186+ ItemActions,
187+ ItemGroup,
188+ ItemSeparator,
189+ ItemTitle,
190+ ItemDescription,
191+ ItemHeader,
192+ ItemFooter,
193+}
packages/ui/src/components/kbd.tsx+28−0
@@ -0,0 +1,28 @@
1+import { cn } from "@executor/ui/lib/utils"
2+
3+function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
4+ return (
5+ <kbd
6+ data-slot="kbd"
7+ className={cn(
8+ "pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none",
9+ "[&_svg:not([class*='size-'])]:size-3",
10+ "[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
11+ className
12+ )}
13+ {...props}
14+ />
15+ )
16+}
17+
18+function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
19+ return (
20+ <kbd
21+ data-slot="kbd-group"
22+ className={cn("inline-flex items-center gap-1", className)}
23+ {...props}
24+ />
25+ )
26+}
27+
28+export { Kbd, KbdGroup }
packages/ui/src/components/label.tsx+22−0
@@ -0,0 +1,22 @@
1+import * as React from "react"
2+import { Label as LabelPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function Label({
7+ className,
8+ ...props
9+}: React.ComponentProps<typeof LabelPrimitive.Root>) {
10+ return (
11+ <LabelPrimitive.Root
12+ data-slot="label"
13+ className={cn(
14+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
15+ className
16+ )}
17+ {...props}
18+ />
19+ )
20+}
21+
22+export { Label }
packages/ui/src/components/menubar.tsx+276−0
@@ -0,0 +1,276 @@
1+"use client"
2+
3+import * as React from "react"
4+import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
5+import { Menubar as MenubarPrimitive } from "radix-ui"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+
9+function Menubar({
10+ className,
11+ ...props
12+}: React.ComponentProps<typeof MenubarPrimitive.Root>) {
13+ return (
14+ <MenubarPrimitive.Root
15+ data-slot="menubar"
16+ className={cn(
17+ "flex h-9 items-center gap-1 rounded-md border bg-background p-1 shadow-xs",
18+ className
19+ )}
20+ {...props}
21+ />
22+ )
23+}
24+
25+function MenubarMenu({
26+ ...props
27+}: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
28+ return <MenubarPrimitive.Menu data-slot="menubar-menu" {...props} />
29+}
30+
31+function MenubarGroup({
32+ ...props
33+}: React.ComponentProps<typeof MenubarPrimitive.Group>) {
34+ return <MenubarPrimitive.Group data-slot="menubar-group" {...props} />
35+}
36+
37+function MenubarPortal({
38+ ...props
39+}: React.ComponentProps<typeof MenubarPrimitive.Portal>) {
40+ return <MenubarPrimitive.Portal data-slot="menubar-portal" {...props} />
41+}
42+
43+function MenubarRadioGroup({
44+ ...props
45+}: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
46+ return (
47+ <MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />
48+ )
49+}
50+
51+function MenubarTrigger({
52+ className,
53+ ...props
54+}: React.ComponentProps<typeof MenubarPrimitive.Trigger>) {
55+ return (
56+ <MenubarPrimitive.Trigger
57+ data-slot="menubar-trigger"
58+ className={cn(
59+ "flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
60+ className
61+ )}
62+ {...props}
63+ />
64+ )
65+}
66+
67+function MenubarContent({
68+ className,
69+ align = "start",
70+ alignOffset = -4,
71+ sideOffset = 8,
72+ ...props
73+}: React.ComponentProps<typeof MenubarPrimitive.Content>) {
74+ return (
75+ <MenubarPortal>
76+ <MenubarPrimitive.Content
77+ data-slot="menubar-content"
78+ align={align}
79+ alignOffset={alignOffset}
80+ sideOffset={sideOffset}
81+ className={cn(
82+ "z-50 min-w-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
83+ className
84+ )}
85+ {...props}
86+ />
87+ </MenubarPortal>
88+ )
89+}
90+
91+function MenubarItem({
92+ className,
93+ inset,
94+ variant = "default",
95+ ...props
96+}: React.ComponentProps<typeof MenubarPrimitive.Item> & {
97+ inset?: boolean
98+ variant?: "default" | "destructive"
99+}) {
100+ return (
101+ <MenubarPrimitive.Item
102+ data-slot="menubar-item"
103+ data-inset={inset}
104+ data-variant={variant}
105+ className={cn(
106+ "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
107+ className
108+ )}
109+ {...props}
110+ />
111+ )
112+}
113+
114+function MenubarCheckboxItem({
115+ className,
116+ children,
117+ checked,
118+ ...props
119+}: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>) {
120+ return (
121+ <MenubarPrimitive.CheckboxItem
122+ data-slot="menubar-checkbox-item"
123+ className={cn(
124+ "relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
125+ className
126+ )}
127+ checked={checked}
128+ {...props}
129+ >
130+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
131+ <MenubarPrimitive.ItemIndicator>
132+ <CheckIcon className="size-4" />
133+ </MenubarPrimitive.ItemIndicator>
134+ </span>
135+ {children}
136+ </MenubarPrimitive.CheckboxItem>
137+ )
138+}
139+
140+function MenubarRadioItem({
141+ className,
142+ children,
143+ ...props
144+}: React.ComponentProps<typeof MenubarPrimitive.RadioItem>) {
145+ return (
146+ <MenubarPrimitive.RadioItem
147+ data-slot="menubar-radio-item"
148+ className={cn(
149+ "relative flex cursor-default items-center gap-2 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
150+ className
151+ )}
152+ {...props}
153+ >
154+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
155+ <MenubarPrimitive.ItemIndicator>
156+ <CircleIcon className="size-2 fill-current" />
157+ </MenubarPrimitive.ItemIndicator>
158+ </span>
159+ {children}
160+ </MenubarPrimitive.RadioItem>
161+ )
162+}
163+
164+function MenubarLabel({
165+ className,
166+ inset,
167+ ...props
168+}: React.ComponentProps<typeof MenubarPrimitive.Label> & {
169+ inset?: boolean
170+}) {
171+ return (
172+ <MenubarPrimitive.Label
173+ data-slot="menubar-label"
174+ data-inset={inset}
175+ className={cn(
176+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
177+ className
178+ )}
179+ {...props}
180+ />
181+ )
182+}
183+
184+function MenubarSeparator({
185+ className,
186+ ...props
187+}: React.ComponentProps<typeof MenubarPrimitive.Separator>) {
188+ return (
189+ <MenubarPrimitive.Separator
190+ data-slot="menubar-separator"
191+ className={cn("-mx-1 my-1 h-px bg-border", className)}
192+ {...props}
193+ />
194+ )
195+}
196+
197+function MenubarShortcut({
198+ className,
199+ ...props
200+}: React.ComponentProps<"span">) {
201+ return (
202+ <span
203+ data-slot="menubar-shortcut"
204+ className={cn(
205+ "ml-auto text-xs tracking-widest text-muted-foreground",
206+ className
207+ )}
208+ {...props}
209+ />
210+ )
211+}
212+
213+function MenubarSub({
214+ ...props
215+}: React.ComponentProps<typeof MenubarPrimitive.Sub>) {
216+ return <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} />
217+}
218+
219+function MenubarSubTrigger({
220+ className,
221+ inset,
222+ children,
223+ ...props
224+}: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
225+ inset?: boolean
226+}) {
227+ return (
228+ <MenubarPrimitive.SubTrigger
229+ data-slot="menubar-sub-trigger"
230+ data-inset={inset}
231+ className={cn(
232+ "flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
233+ className
234+ )}
235+ {...props}
236+ >
237+ {children}
238+ <ChevronRightIcon className="ml-auto h-4 w-4" />
239+ </MenubarPrimitive.SubTrigger>
240+ )
241+}
242+
243+function MenubarSubContent({
244+ className,
245+ ...props
246+}: React.ComponentProps<typeof MenubarPrimitive.SubContent>) {
247+ return (
248+ <MenubarPrimitive.SubContent
249+ data-slot="menubar-sub-content"
250+ className={cn(
251+ "z-50 min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
252+ className
253+ )}
254+ {...props}
255+ />
256+ )
257+}
258+
259+export {
260+ Menubar,
261+ MenubarPortal,
262+ MenubarMenu,
263+ MenubarTrigger,
264+ MenubarContent,
265+ MenubarGroup,
266+ MenubarSeparator,
267+ MenubarLabel,
268+ MenubarItem,
269+ MenubarShortcut,
270+ MenubarCheckboxItem,
271+ MenubarRadioGroup,
272+ MenubarRadioItem,
273+ MenubarSub,
274+ MenubarSubTrigger,
275+ MenubarSubContent,
276+}
packages/ui/src/components/native-select.tsx+53−0
@@ -0,0 +1,53 @@
1+import * as React from "react"
2+import { ChevronDownIcon } from "lucide-react"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function NativeSelect({
7+ className,
8+ size = "default",
9+ ...props
10+}: Omit<React.ComponentProps<"select">, "size"> & { size?: "sm" | "default" }) {
11+ return (
12+ <div
13+ className="group/native-select relative w-fit has-[select:disabled]:opacity-50"
14+ data-slot="native-select-wrapper"
15+ >
16+ <select
17+ data-slot="native-select"
18+ data-size={size}
19+ className={cn(
20+ "h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed data-[size=sm]:h-8 data-[size=sm]:py-1 dark:bg-input/30 dark:hover:bg-input/50",
21+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
22+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
23+ className
24+ )}
25+ {...props}
26+ />
27+ <ChevronDownIcon
28+ className="pointer-events-none absolute top-1/2 right-3.5 size-4 -translate-y-1/2 text-muted-foreground opacity-50 select-none"
29+ aria-hidden="true"
30+ data-slot="native-select-icon"
31+ />
32+ </div>
33+ )
34+}
35+
36+function NativeSelectOption({ ...props }: React.ComponentProps<"option">) {
37+ return <option data-slot="native-select-option" {...props} />
38+}
39+
40+function NativeSelectOptGroup({
41+ className,
42+ ...props
43+}: React.ComponentProps<"optgroup">) {
44+ return (
45+ <optgroup
46+ data-slot="native-select-optgroup"
47+ className={cn(className)}
48+ {...props}
49+ />
50+ )
51+}
52+
53+export { NativeSelect, NativeSelectOptGroup, NativeSelectOption }
packages/ui/src/components/navigation-menu.tsx+168−0
@@ -0,0 +1,168 @@
1+import * as React from "react"
2+import { cva } from "class-variance-authority"
3+import { ChevronDownIcon } from "lucide-react"
4+import { NavigationMenu as NavigationMenuPrimitive } from "radix-ui"
5+
6+import { cn } from "@executor/ui/lib/utils"
7+
8+function NavigationMenu({
9+ className,
10+ children,
11+ viewport = true,
12+ ...props
13+}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
14+ viewport?: boolean
15+}) {
16+ return (
17+ <NavigationMenuPrimitive.Root
18+ data-slot="navigation-menu"
19+ data-viewport={viewport}
20+ className={cn(
21+ "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
22+ className
23+ )}
24+ {...props}
25+ >
26+ {children}
27+ {viewport && <NavigationMenuViewport />}
28+ </NavigationMenuPrimitive.Root>
29+ )
30+}
31+
32+function NavigationMenuList({
33+ className,
34+ ...props
35+}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
36+ return (
37+ <NavigationMenuPrimitive.List
38+ data-slot="navigation-menu-list"
39+ className={cn(
40+ "group flex flex-1 list-none items-center justify-center gap-1",
41+ className
42+ )}
43+ {...props}
44+ />
45+ )
46+}
47+
48+function NavigationMenuItem({
49+ className,
50+ ...props
51+}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
52+ return (
53+ <NavigationMenuPrimitive.Item
54+ data-slot="navigation-menu-item"
55+ className={cn("relative", className)}
56+ {...props}
57+ />
58+ )
59+}
60+
61+const navigationMenuTriggerStyle = cva(
62+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-[color,box-shadow] outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent/50 data-[state=open]:text-accent-foreground data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent"
63+)
64+
65+function NavigationMenuTrigger({
66+ className,
67+ children,
68+ ...props
69+}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
70+ return (
71+ <NavigationMenuPrimitive.Trigger
72+ data-slot="navigation-menu-trigger"
73+ className={cn(navigationMenuTriggerStyle(), "group", className)}
74+ {...props}
75+ >
76+ {children}{" "}
77+ <ChevronDownIcon
78+ className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
79+ aria-hidden="true"
80+ />
81+ </NavigationMenuPrimitive.Trigger>
82+ )
83+}
84+
85+function NavigationMenuContent({
86+ className,
87+ ...props
88+}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
89+ return (
90+ <NavigationMenuPrimitive.Content
91+ data-slot="navigation-menu-content"
92+ className={cn(
93+ "top-0 left-0 w-full p-2 pr-2.5 data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out md:absolute md:w-auto",
94+ "group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95",
95+ className
96+ )}
97+ {...props}
98+ />
99+ )
100+}
101+
102+function NavigationMenuViewport({
103+ className,
104+ ...props
105+}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
106+ return (
107+ <div
108+ className={cn(
109+ "absolute top-full left-0 isolate z-50 flex justify-center"
110+ )}
111+ >
112+ <NavigationMenuPrimitive.Viewport
113+ data-slot="navigation-menu-viewport"
114+ className={cn(
115+ "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
116+ className
117+ )}
118+ {...props}
119+ />
120+ </div>
121+ )
122+}
123+
124+function NavigationMenuLink({
125+ className,
126+ ...props
127+}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
128+ return (
129+ <NavigationMenuPrimitive.Link
130+ data-slot="navigation-menu-link"
131+ className={cn(
132+ "flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground data-[active=true]:hover:bg-accent data-[active=true]:focus:bg-accent [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
133+ className
134+ )}
135+ {...props}
136+ />
137+ )
138+}
139+
140+function NavigationMenuIndicator({
141+ className,
142+ ...props
143+}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
144+ return (
145+ <NavigationMenuPrimitive.Indicator
146+ data-slot="navigation-menu-indicator"
147+ className={cn(
148+ "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in",
149+ className
150+ )}
151+ {...props}
152+ >
153+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
154+ </NavigationMenuPrimitive.Indicator>
155+ )
156+}
157+
158+export {
159+ NavigationMenu,
160+ NavigationMenuList,
161+ NavigationMenuItem,
162+ NavigationMenuContent,
163+ NavigationMenuTrigger,
164+ NavigationMenuLink,
165+ NavigationMenuIndicator,
166+ NavigationMenuViewport,
167+ navigationMenuTriggerStyle,
168+}
packages/ui/src/components/pagination.tsx+127−0
@@ -0,0 +1,127 @@
1+import * as React from "react"
2+import {
3+ ChevronLeftIcon,
4+ ChevronRightIcon,
5+ MoreHorizontalIcon,
6+} from "lucide-react"
7+
8+import { cn } from "@executor/ui/lib/utils"
9+import { buttonVariants, type Button } from "@executor/ui/components/button"
10+
11+function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
12+ return (
13+ <nav
14+ role="navigation"
15+ aria-label="pagination"
16+ data-slot="pagination"
17+ className={cn("mx-auto flex w-full justify-center", className)}
18+ {...props}
19+ />
20+ )
21+}
22+
23+function PaginationContent({
24+ className,
25+ ...props
26+}: React.ComponentProps<"ul">) {
27+ return (
28+ <ul
29+ data-slot="pagination-content"
30+ className={cn("flex flex-row items-center gap-1", className)}
31+ {...props}
32+ />
33+ )
34+}
35+
36+function PaginationItem({ ...props }: React.ComponentProps<"li">) {
37+ return <li data-slot="pagination-item" {...props} />
38+}
39+
40+type PaginationLinkProps = {
41+ isActive?: boolean
42+} & Pick<React.ComponentProps<typeof Button>, "size"> &
43+ React.ComponentProps<"a">
44+
45+function PaginationLink({
46+ className,
47+ isActive,
48+ size = "icon",
49+ ...props
50+}: PaginationLinkProps) {
51+ return (
52+ <a
53+ aria-current={isActive ? "page" : undefined}
54+ data-slot="pagination-link"
55+ data-active={isActive}
56+ className={cn(
57+ buttonVariants({
58+ variant: isActive ? "outline" : "ghost",
59+ size,
60+ }),
61+ className
62+ )}
63+ {...props}
64+ />
65+ )
66+}
67+
68+function PaginationPrevious({
69+ className,
70+ ...props
71+}: React.ComponentProps<typeof PaginationLink>) {
72+ return (
73+ <PaginationLink
74+ aria-label="Go to previous page"
75+ size="default"
76+ className={cn("gap-1 px-2.5 sm:pl-2.5", className)}
77+ {...props}
78+ >
79+ <ChevronLeftIcon />
80+ <span className="hidden sm:block">Previous</span>
81+ </PaginationLink>
82+ )
83+}
84+
85+function PaginationNext({
86+ className,
87+ ...props
88+}: React.ComponentProps<typeof PaginationLink>) {
89+ return (
90+ <PaginationLink
91+ aria-label="Go to next page"
92+ size="default"
93+ className={cn("gap-1 px-2.5 sm:pr-2.5", className)}
94+ {...props}
95+ >
96+ <span className="hidden sm:block">Next</span>
97+ <ChevronRightIcon />
98+ </PaginationLink>
99+ )
100+}
101+
102+function PaginationEllipsis({
103+ className,
104+ ...props
105+}: React.ComponentProps<"span">) {
106+ return (
107+ <span
108+ aria-hidden
109+ data-slot="pagination-ellipsis"
110+ className={cn("flex size-9 items-center justify-center", className)}
111+ {...props}
112+ >
113+ <MoreHorizontalIcon className="size-4" />
114+ <span className="sr-only">More pages</span>
115+ </span>
116+ )
117+}
118+
119+export {
120+ Pagination,
121+ PaginationContent,
122+ PaginationLink,
123+ PaginationItem,
124+ PaginationPrevious,
125+ PaginationNext,
126+ PaginationEllipsis,
127+}
packages/ui/src/components/popover.tsx+87−0
@@ -0,0 +1,87 @@
1+import * as React from "react"
2+import { Popover as PopoverPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function Popover({
7+ ...props
8+}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
9+ return <PopoverPrimitive.Root data-slot="popover" {...props} />
10+}
11+
12+function PopoverTrigger({
13+ ...props
14+}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
15+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
16+}
17+
18+function PopoverContent({
19+ className,
20+ align = "center",
21+ sideOffset = 4,
22+ ...props
23+}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
24+ return (
25+ <PopoverPrimitive.Portal>
26+ <PopoverPrimitive.Content
27+ data-slot="popover-content"
28+ align={align}
29+ sideOffset={sideOffset}
30+ className={cn(
31+ "z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
32+ className
33+ )}
34+ {...props}
35+ />
36+ </PopoverPrimitive.Portal>
37+ )
38+}
39+
40+function PopoverAnchor({
41+ ...props
42+}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
43+ return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
44+}
45+
46+function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
47+ return (
48+ <div
49+ data-slot="popover-header"
50+ className={cn("flex flex-col gap-1 text-sm", className)}
51+ {...props}
52+ />
53+ )
54+}
55+
56+function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
57+ return (
58+ <div
59+ data-slot="popover-title"
60+ className={cn("font-medium", className)}
61+ {...props}
62+ />
63+ )
64+}
65+
66+function PopoverDescription({
67+ className,
68+ ...props
69+}: React.ComponentProps<"p">) {
70+ return (
71+ <p
72+ data-slot="popover-description"
73+ className={cn("text-muted-foreground", className)}
74+ {...props}
75+ />
76+ )
77+}
78+
79+export {
80+ Popover,
81+ PopoverTrigger,
82+ PopoverContent,
83+ PopoverAnchor,
84+ PopoverHeader,
85+ PopoverTitle,
86+ PopoverDescription,
87+}
packages/ui/src/components/progress.tsx+31−0
@@ -0,0 +1,31 @@
1+"use client"
2+
3+import * as React from "react"
4+import { Progress as ProgressPrimitive } from "radix-ui"
5+
6+import { cn } from "@executor/ui/lib/utils"
7+
8+function Progress({
9+ className,
10+ value,
11+ ...props
12+}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
13+ return (
14+ <ProgressPrimitive.Root
15+ data-slot="progress"
16+ className={cn(
17+ "relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
18+ className
19+ )}
20+ {...props}
21+ >
22+ <ProgressPrimitive.Indicator
23+ data-slot="progress-indicator"
24+ className="h-full w-full flex-1 bg-primary transition-all"
25+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
26+ />
27+ </ProgressPrimitive.Root>
28+ )
29+}
30+
31+export { Progress }
packages/ui/src/components/radio-group.tsx+43−0
@@ -0,0 +1,43 @@
1+import * as React from "react"
2+import { CircleIcon } from "lucide-react"
3+import { RadioGroup as RadioGroupPrimitive } from "radix-ui"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+
7+function RadioGroup({
8+ className,
9+ ...props
10+}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
11+ return (
12+ <RadioGroupPrimitive.Root
13+ data-slot="radio-group"
14+ className={cn("grid gap-3", className)}
15+ {...props}
16+ />
17+ )
18+}
19+
20+function RadioGroupItem({
21+ className,
22+ ...props
23+}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
24+ return (
25+ <RadioGroupPrimitive.Item
26+ data-slot="radio-group-item"
27+ className={cn(
28+ "aspect-square size-4 shrink-0 rounded-full border border-input text-primary shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
29+ className
30+ )}
31+ {...props}
32+ >
33+ <RadioGroupPrimitive.Indicator
34+ data-slot="radio-group-indicator"
35+ className="relative flex items-center justify-center"
36+ >
37+ <CircleIcon className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 fill-primary" />
38+ </RadioGroupPrimitive.Indicator>
39+ </RadioGroupPrimitive.Item>
40+ )
41+}
42+
43+export { RadioGroup, RadioGroupItem }
packages/ui/src/components/resizable.tsx+53−0
@@ -0,0 +1,53 @@
1+"use client"
2+
3+import { GripVerticalIcon } from "lucide-react"
4+import * as ResizablePrimitive from "react-resizable-panels"
5+
6+import { cn } from "@executor/ui/lib/utils"
7+
8+function ResizablePanelGroup({
9+ className,
10+ ...props
11+}: ResizablePrimitive.GroupProps) {
12+ return (
13+ <ResizablePrimitive.Group
14+ data-slot="resizable-panel-group"
15+ className={cn(
16+ "flex h-full w-full aria-[orientation=vertical]:flex-col",
17+ className
18+ )}
19+ {...props}
20+ />
21+ )
22+}
23+
24+function ResizablePanel({ ...props }: ResizablePrimitive.PanelProps) {
25+ return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />
26+}
27+
28+function ResizableHandle({
29+ withHandle,
30+ className,
31+ ...props
32+}: ResizablePrimitive.SeparatorProps & {
33+ withHandle?: boolean
34+}) {
35+ return (
36+ <ResizablePrimitive.Separator
37+ data-slot="resizable-handle"
38+ className={cn(
39+ "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-hidden aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:translate-x-0 aria-[orientation=horizontal]:after:-translate-y-1/2 [&[aria-orientation=horizontal]>div]:rotate-90",
40+ className
41+ )}
42+ {...props}
43+ >
44+ {withHandle && (
45+ <div className="z-10 flex h-4 w-3 items-center justify-center rounded-xs border bg-border">
46+ <GripVerticalIcon className="size-2.5" />
47+ </div>
48+ )}
49+ </ResizablePrimitive.Separator>
50+ )
51+}
52+
53+export { ResizableHandle, ResizablePanel, ResizablePanelGroup }
packages/ui/src/components/scroll-area.tsx+56−0
@@ -0,0 +1,56 @@
1+import * as React from "react"
2+import { ScrollArea as ScrollAreaPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function ScrollArea({
7+ className,
8+ children,
9+ ...props
10+}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
11+ return (
12+ <ScrollAreaPrimitive.Root
13+ data-slot="scroll-area"
14+ className={cn("relative", className)}
15+ {...props}
16+ >
17+ <ScrollAreaPrimitive.Viewport
18+ data-slot="scroll-area-viewport"
19+ className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
20+ >
21+ {children}
22+ </ScrollAreaPrimitive.Viewport>
23+ <ScrollBar />
24+ <ScrollAreaPrimitive.Corner />
25+ </ScrollAreaPrimitive.Root>
26+ )
27+}
28+
29+function ScrollBar({
30+ className,
31+ orientation = "vertical",
32+ ...props
33+}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
34+ return (
35+ <ScrollAreaPrimitive.ScrollAreaScrollbar
36+ data-slot="scroll-area-scrollbar"
37+ orientation={orientation}
38+ className={cn(
39+ "flex touch-none p-px transition-colors select-none",
40+ orientation === "vertical" &&
41+ "h-full w-2.5 border-l border-l-transparent",
42+ orientation === "horizontal" &&
43+ "h-2.5 flex-col border-t border-t-transparent",
44+ className
45+ )}
46+ {...props}
47+ >
48+ <ScrollAreaPrimitive.ScrollAreaThumb
49+ data-slot="scroll-area-thumb"
50+ className="relative flex-1 rounded-full bg-border"
51+ />
52+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
53+ )
54+}
55+
56+export { ScrollArea, ScrollBar }
packages/ui/src/components/select.tsx+190−0
@@ -0,0 +1,190 @@
1+"use client"
2+
3+import * as React from "react"
4+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
5+import { Select as SelectPrimitive } from "radix-ui"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+
9+function Select({
10+ ...props
11+}: React.ComponentProps<typeof SelectPrimitive.Root>) {
12+ return <SelectPrimitive.Root data-slot="select" {...props} />
13+}
14+
15+function SelectGroup({
16+ ...props
17+}: React.ComponentProps<typeof SelectPrimitive.Group>) {
18+ return <SelectPrimitive.Group data-slot="select-group" {...props} />
19+}
20+
21+function SelectValue({
22+ ...props
23+}: React.ComponentProps<typeof SelectPrimitive.Value>) {
24+ return <SelectPrimitive.Value data-slot="select-value" {...props} />
25+}
26+
27+function SelectTrigger({
28+ className,
29+ size = "default",
30+ children,
31+ ...props
32+}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
33+ size?: "sm" | "default"
34+}) {
35+ return (
36+ <SelectPrimitive.Trigger
37+ data-slot="select-trigger"
38+ data-size={size}
39+ className={cn(
40+ "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
41+ className
42+ )}
43+ {...props}
44+ >
45+ {children}
46+ <SelectPrimitive.Icon asChild>
47+ <ChevronDownIcon className="size-4 opacity-50" />
48+ </SelectPrimitive.Icon>
49+ </SelectPrimitive.Trigger>
50+ )
51+}
52+
53+function SelectContent({
54+ className,
55+ children,
56+ position = "item-aligned",
57+ align = "center",
58+ ...props
59+}: React.ComponentProps<typeof SelectPrimitive.Content>) {
60+ return (
61+ <SelectPrimitive.Portal>
62+ <SelectPrimitive.Content
63+ data-slot="select-content"
64+ className={cn(
65+ "relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
66+ position === "popper" &&
67+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
68+ className
69+ )}
70+ position={position}
71+ align={align}
72+ {...props}
73+ >
74+ <SelectScrollUpButton />
75+ <SelectPrimitive.Viewport
76+ className={cn(
77+ "p-1",
78+ position === "popper" &&
79+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
80+ )}
81+ >
82+ {children}
83+ </SelectPrimitive.Viewport>
84+ <SelectScrollDownButton />
85+ </SelectPrimitive.Content>
86+ </SelectPrimitive.Portal>
87+ )
88+}
89+
90+function SelectLabel({
91+ className,
92+ ...props
93+}: React.ComponentProps<typeof SelectPrimitive.Label>) {
94+ return (
95+ <SelectPrimitive.Label
96+ data-slot="select-label"
97+ className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)}
98+ {...props}
99+ />
100+ )
101+}
102+
103+function SelectItem({
104+ className,
105+ children,
106+ ...props
107+}: React.ComponentProps<typeof SelectPrimitive.Item>) {
108+ return (
109+ <SelectPrimitive.Item
110+ data-slot="select-item"
111+ className={cn(
112+ "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
113+ className
114+ )}
115+ {...props}
116+ >
117+ <span
118+ data-slot="select-item-indicator"
119+ className="absolute right-2 flex size-3.5 items-center justify-center"
120+ >
121+ <SelectPrimitive.ItemIndicator>
122+ <CheckIcon className="size-4" />
123+ </SelectPrimitive.ItemIndicator>
124+ </span>
125+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
126+ </SelectPrimitive.Item>
127+ )
128+}
129+
130+function SelectSeparator({
131+ className,
132+ ...props
133+}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
134+ return (
135+ <SelectPrimitive.Separator
136+ data-slot="select-separator"
137+ className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
138+ {...props}
139+ />
140+ )
141+}
142+
143+function SelectScrollUpButton({
144+ className,
145+ ...props
146+}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
147+ return (
148+ <SelectPrimitive.ScrollUpButton
149+ data-slot="select-scroll-up-button"
150+ className={cn(
151+ "flex cursor-default items-center justify-center py-1",
152+ className
153+ )}
154+ {...props}
155+ >
156+ <ChevronUpIcon className="size-4" />
157+ </SelectPrimitive.ScrollUpButton>
158+ )
159+}
160+
161+function SelectScrollDownButton({
162+ className,
163+ ...props
164+}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
165+ return (
166+ <SelectPrimitive.ScrollDownButton
167+ data-slot="select-scroll-down-button"
168+ className={cn(
169+ "flex cursor-default items-center justify-center py-1",
170+ className
171+ )}
172+ {...props}
173+ >
174+ <ChevronDownIcon className="size-4" />
175+ </SelectPrimitive.ScrollDownButton>
176+ )
177+}
178+
179+export {
180+ Select,
181+ SelectContent,
182+ SelectGroup,
183+ SelectItem,
184+ SelectLabel,
185+ SelectScrollDownButton,
186+ SelectScrollUpButton,
187+ SelectSeparator,
188+ SelectTrigger,
189+ SelectValue,
190+}
packages/ui/src/components/separator.tsx+26−0
@@ -0,0 +1,26 @@
1+import * as React from "react"
2+import { Separator as SeparatorPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function Separator({
7+ className,
8+ orientation = "horizontal",
9+ decorative = true,
10+ ...props
11+}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
12+ return (
13+ <SeparatorPrimitive.Root
14+ data-slot="separator"
15+ decorative={decorative}
16+ orientation={orientation}
17+ className={cn(
18+ "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
19+ className
20+ )}
21+ {...props}
22+ />
23+ )
24+}
25+
26+export { Separator }
packages/ui/src/components/sheet.tsx+143−0
@@ -0,0 +1,143 @@
1+"use client"
2+
3+import * as React from "react"
4+import { XIcon } from "lucide-react"
5+import { Dialog as SheetPrimitive } from "radix-ui"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+
9+function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
10+ return <SheetPrimitive.Root data-slot="sheet" {...props} />
11+}
12+
13+function SheetTrigger({
14+ ...props
15+}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
16+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
17+}
18+
19+function SheetClose({
20+ ...props
21+}: React.ComponentProps<typeof SheetPrimitive.Close>) {
22+ return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
23+}
24+
25+function SheetPortal({
26+ ...props
27+}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
28+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
29+}
30+
31+function SheetOverlay({
32+ className,
33+ ...props
34+}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
35+ return (
36+ <SheetPrimitive.Overlay
37+ data-slot="sheet-overlay"
38+ className={cn(
39+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
40+ className
41+ )}
42+ {...props}
43+ />
44+ )
45+}
46+
47+function SheetContent({
48+ className,
49+ children,
50+ side = "right",
51+ showCloseButton = true,
52+ ...props
53+}: React.ComponentProps<typeof SheetPrimitive.Content> & {
54+ side?: "top" | "right" | "bottom" | "left"
55+ showCloseButton?: boolean
56+}) {
57+ return (
58+ <SheetPortal>
59+ <SheetOverlay />
60+ <SheetPrimitive.Content
61+ data-slot="sheet-content"
62+ className={cn(
63+ "fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500",
64+ side === "right" &&
65+ "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
66+ side === "left" &&
67+ "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
68+ side === "top" &&
69+ "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
70+ side === "bottom" &&
71+ "inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
72+ className
73+ )}
74+ {...props}
75+ >
76+ {children}
77+ {showCloseButton && (
78+ <SheetPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary">
79+ <XIcon className="size-4" />
80+ <span className="sr-only">Close</span>
81+ </SheetPrimitive.Close>
82+ )}
83+ </SheetPrimitive.Content>
84+ </SheetPortal>
85+ )
86+}
87+
88+function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
89+ return (
90+ <div
91+ data-slot="sheet-header"
92+ className={cn("flex flex-col gap-1.5 p-4", className)}
93+ {...props}
94+ />
95+ )
96+}
97+
98+function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
99+ return (
100+ <div
101+ data-slot="sheet-footer"
102+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
103+ {...props}
104+ />
105+ )
106+}
107+
108+function SheetTitle({
109+ className,
110+ ...props
111+}: React.ComponentProps<typeof SheetPrimitive.Title>) {
112+ return (
113+ <SheetPrimitive.Title
114+ data-slot="sheet-title"
115+ className={cn("font-semibold text-foreground", className)}
116+ {...props}
117+ />
118+ )
119+}
120+
121+function SheetDescription({
122+ className,
123+ ...props
124+}: React.ComponentProps<typeof SheetPrimitive.Description>) {
125+ return (
126+ <SheetPrimitive.Description
127+ data-slot="sheet-description"
128+ className={cn("text-sm text-muted-foreground", className)}
129+ {...props}
130+ />
131+ )
132+}
133+
134+export {
135+ Sheet,
136+ SheetTrigger,
137+ SheetClose,
138+ SheetContent,
139+ SheetHeader,
140+ SheetFooter,
141+ SheetTitle,
142+ SheetDescription,
143+}
packages/ui/src/components/sidebar.tsx+724−0
@@ -0,0 +1,724 @@
1+import * as React from "react"
2+import { cva, type VariantProps } from "class-variance-authority"
3+import { PanelLeftIcon } from "lucide-react"
4+import { Slot } from "radix-ui"
5+
6+import { useIsMobile } from "@executor/ui/hooks/use-mobile"
7+import { cn } from "@executor/ui/lib/utils"
8+import { Button } from "@executor/ui/components/button"
9+import { Input } from "@executor/ui/components/input"
10+import { Separator } from "@executor/ui/components/separator"
11+import {
12+ Sheet,
13+ SheetContent,
14+ SheetDescription,
15+ SheetHeader,
16+ SheetTitle,
17+} from "@executor/ui/components/sheet"
18+import { Skeleton } from "@executor/ui/components/skeleton"
19+import {
20+ Tooltip,
21+ TooltipContent,
22+ TooltipProvider,
23+ TooltipTrigger,
24+} from "@executor/ui/components/tooltip"
25+
26+const SIDEBAR_COOKIE_NAME = "sidebar_state"
27+const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
28+const SIDEBAR_WIDTH = "16rem"
29+const SIDEBAR_WIDTH_MOBILE = "18rem"
30+const SIDEBAR_WIDTH_ICON = "3rem"
31+const SIDEBAR_KEYBOARD_SHORTCUT = "b"
32+
33+type SidebarContextProps = {
34+ state: "expanded" | "collapsed"
35+ open: boolean
36+ setOpen: (open: boolean) => void
37+ openMobile: boolean
38+ setOpenMobile: (open: boolean) => void
39+ isMobile: boolean
40+ toggleSidebar: () => void
41+}
42+
43+const SidebarContext = React.createContext<SidebarContextProps | null>(null)
44+
45+function useSidebar() {
46+ const context = React.useContext(SidebarContext)
47+ if (!context) {
48+ throw new Error("useSidebar must be used within a SidebarProvider.")
49+ }
50+
51+ return context
52+}
53+
54+function SidebarProvider({
55+ defaultOpen = true,
56+ open: openProp,
57+ onOpenChange: setOpenProp,
58+ className,
59+ style,
60+ children,
61+ ...props
62+}: React.ComponentProps<"div"> & {
63+ defaultOpen?: boolean
64+ open?: boolean
65+ onOpenChange?: (open: boolean) => void
66+}) {
67+ const isMobile = useIsMobile()
68+ const [openMobile, setOpenMobile] = React.useState(false)
69+
70+ // This is the internal state of the sidebar.
71+ // We use openProp and setOpenProp for control from outside the component.
72+ const [_open, _setOpen] = React.useState(defaultOpen)
73+ const open = openProp ?? _open
74+ const setOpen = React.useCallback(
75+ (value: boolean | ((value: boolean) => boolean)) => {
76+ const openState = typeof value === "function" ? value(open) : value
77+ if (setOpenProp) {
78+ setOpenProp(openState)
79+ } else {
80+ _setOpen(openState)
81+ }
82+
83+ // This sets the cookie to keep the sidebar state.
84+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
85+ },
86+ [setOpenProp, open]
87+ )
88+
89+ // Helper to toggle the sidebar.
90+ const toggleSidebar = React.useCallback(() => {
91+ return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
92+ }, [isMobile, setOpen, setOpenMobile])
93+
94+ // Adds a keyboard shortcut to toggle the sidebar.
95+ React.useEffect(() => {
96+ const handleKeyDown = (event: KeyboardEvent) => {
97+ if (
98+ event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
99+ (event.metaKey || event.ctrlKey)
100+ ) {
101+ event.preventDefault()
102+ toggleSidebar()
103+ }
104+ }
105+
106+ window.addEventListener("keydown", handleKeyDown)
107+ return () => window.removeEventListener("keydown", handleKeyDown)
108+ }, [toggleSidebar])
109+
110+ // We add a state so that we can do data-state="expanded" or "collapsed".
111+ // This makes it easier to style the sidebar with Tailwind classes.
112+ const state = open ? "expanded" : "collapsed"
113+
114+ const contextValue = React.useMemo<SidebarContextProps>(
115+ () => ({
116+ state,
117+ open,
118+ setOpen,
119+ isMobile,
120+ openMobile,
121+ setOpenMobile,
122+ toggleSidebar,
123+ }),
124+ [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
125+ )
126+
127+ return (
128+ <SidebarContext.Provider value={contextValue}>
129+ <TooltipProvider delayDuration={0}>
130+ <div
131+ data-slot="sidebar-wrapper"
132+ style={
133+ {
134+ "--sidebar-width": SIDEBAR_WIDTH,
135+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
136+ ...style,
137+ } as React.CSSProperties
138+ }
139+ className={cn(
140+ "group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar",
141+ className
142+ )}
143+ {...props}
144+ >
145+ {children}
146+ </div>
147+ </TooltipProvider>
148+ </SidebarContext.Provider>
149+ )
150+}
151+
152+function Sidebar({
153+ side = "left",
154+ variant = "sidebar",
155+ collapsible = "offcanvas",
156+ className,
157+ children,
158+ ...props
159+}: React.ComponentProps<"div"> & {
160+ side?: "left" | "right"
161+ variant?: "sidebar" | "floating" | "inset"
162+ collapsible?: "offcanvas" | "icon" | "none"
163+}) {
164+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
165+
166+ if (collapsible === "none") {
167+ return (
168+ <div
169+ data-slot="sidebar"
170+ className={cn(
171+ "flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground",
172+ className
173+ )}
174+ {...props}
175+ >
176+ {children}
177+ </div>
178+ )
179+ }
180+
181+ if (isMobile) {
182+ return (
183+ <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
184+ <SheetContent
185+ data-sidebar="sidebar"
186+ data-slot="sidebar"
187+ data-mobile="true"
188+ className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
189+ style={
190+ {
191+ "--sidebar-width": SIDEBAR_WIDTH_MOBILE,
192+ } as React.CSSProperties
193+ }
194+ side={side}
195+ >
196+ <SheetHeader className="sr-only">
197+ <SheetTitle>Sidebar</SheetTitle>
198+ <SheetDescription>Displays the mobile sidebar.</SheetDescription>
199+ </SheetHeader>
200+ <div className="flex h-full w-full flex-col">{children}</div>
201+ </SheetContent>
202+ </Sheet>
203+ )
204+ }
205+
206+ return (
207+ <div
208+ className="group peer hidden text-sidebar-foreground md:block"
209+ data-state={state}
210+ data-collapsible={state === "collapsed" ? collapsible : ""}
211+ data-variant={variant}
212+ data-side={side}
213+ data-slot="sidebar"
214+ >
215+ {/* This is what handles the sidebar gap on desktop */}
216+ <div
217+ data-slot="sidebar-gap"
218+ className={cn(
219+ "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
220+ "group-data-[collapsible=offcanvas]:w-0",
221+ "group-data-[side=right]:rotate-180",
222+ variant === "floating" || variant === "inset"
223+ ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
224+ : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
225+ )}
226+ />
227+ <div
228+ data-slot="sidebar-container"
229+ className={cn(
230+ "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
231+ side === "left"
232+ ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
233+ : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
234+ // Adjust the padding for floating and inset variants.
235+ variant === "floating" || variant === "inset"
236+ ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
237+ : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
238+ className
239+ )}
240+ {...props}
241+ >
242+ <div
243+ data-sidebar="sidebar"
244+ data-slot="sidebar-inner"
245+ className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-sm"
246+ >
247+ {children}
248+ </div>
249+ </div>
250+ </div>
251+ )
252+}
253+
254+function SidebarTrigger({
255+ className,
256+ onClick,
257+ ...props
258+}: React.ComponentProps<typeof Button>) {
259+ const { toggleSidebar } = useSidebar()
260+
261+ return (
262+ <Button
263+ data-sidebar="trigger"
264+ data-slot="sidebar-trigger"
265+ variant="ghost"
266+ size="icon"
267+ className={cn("size-7", className)}
268+ onClick={(event) => {
269+ onClick?.(event)
270+ toggleSidebar()
271+ }}
272+ {...props}
273+ >
274+ <PanelLeftIcon />
275+ <span className="sr-only">Toggle Sidebar</span>
276+ </Button>
277+ )
278+}
279+
280+function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
281+ const { toggleSidebar } = useSidebar()
282+
283+ return (
284+ <button
285+ data-sidebar="rail"
286+ data-slot="sidebar-rail"
287+ aria-label="Toggle Sidebar"
288+ tabIndex={-1}
289+ onClick={toggleSidebar}
290+ title="Toggle Sidebar"
291+ className={cn(
292+ "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex",
293+ "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
294+ "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
295+ "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar",
296+ "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
297+ "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
298+ className
299+ )}
300+ {...props}
301+ />
302+ )
303+}
304+
305+function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
306+ return (
307+ <main
308+ data-slot="sidebar-inset"
309+ className={cn(
310+ "relative flex w-full flex-1 flex-col bg-background",
311+ "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
312+ className
313+ )}
314+ {...props}
315+ />
316+ )
317+}
318+
319+function SidebarInput({
320+ className,
321+ ...props
322+}: React.ComponentProps<typeof Input>) {
323+ return (
324+ <Input
325+ data-slot="sidebar-input"
326+ data-sidebar="input"
327+ className={cn("h-8 w-full bg-background shadow-none", className)}
328+ {...props}
329+ />
330+ )
331+}
332+
333+function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
334+ return (
335+ <div
336+ data-slot="sidebar-header"
337+ data-sidebar="header"
338+ className={cn("flex flex-col gap-2 p-2", className)}
339+ {...props}
340+ />
341+ )
342+}
343+
344+function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
345+ return (
346+ <div
347+ data-slot="sidebar-footer"
348+ data-sidebar="footer"
349+ className={cn("flex flex-col gap-2 p-2", className)}
350+ {...props}
351+ />
352+ )
353+}
354+
355+function SidebarSeparator({
356+ className,
357+ ...props
358+}: React.ComponentProps<typeof Separator>) {
359+ return (
360+ <Separator
361+ data-slot="sidebar-separator"
362+ data-sidebar="separator"
363+ className={cn("mx-2 w-auto bg-sidebar-border", className)}
364+ {...props}
365+ />
366+ )
367+}
368+
369+function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
370+ return (
371+ <div
372+ data-slot="sidebar-content"
373+ data-sidebar="content"
374+ className={cn(
375+ "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
376+ className
377+ )}
378+ {...props}
379+ />
380+ )
381+}
382+
383+function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
384+ return (
385+ <div
386+ data-slot="sidebar-group"
387+ data-sidebar="group"
388+ className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
389+ {...props}
390+ />
391+ )
392+}
393+
394+function SidebarGroupLabel({
395+ className,
396+ asChild = false,
397+ ...props
398+}: React.ComponentProps<"div"> & { asChild?: boolean }) {
399+ const Comp = asChild ? Slot.Root : "div"
400+
401+ return (
402+ <Comp
403+ data-slot="sidebar-group-label"
404+ data-sidebar="group-label"
405+ className={cn(
406+ "flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
407+ "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
408+ className
409+ )}
410+ {...props}
411+ />
412+ )
413+}
414+
415+function SidebarGroupAction({
416+ className,
417+ asChild = false,
418+ ...props
419+}: React.ComponentProps<"button"> & { asChild?: boolean }) {
420+ const Comp = asChild ? Slot.Root : "button"
421+
422+ return (
423+ <Comp
424+ data-slot="sidebar-group-action"
425+ data-sidebar="group-action"
426+ className={cn(
427+ "absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
428+ // Increases the hit area of the button on mobile.
429+ "after:absolute after:-inset-2 md:after:hidden",
430+ "group-data-[collapsible=icon]:hidden",
431+ className
432+ )}
433+ {...props}
434+ />
435+ )
436+}
437+
438+function SidebarGroupContent({
439+ className,
440+ ...props
441+}: React.ComponentProps<"div">) {
442+ return (
443+ <div
444+ data-slot="sidebar-group-content"
445+ data-sidebar="group-content"
446+ className={cn("w-full text-sm", className)}
447+ {...props}
448+ />
449+ )
450+}
451+
452+function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
453+ return (
454+ <ul
455+ data-slot="sidebar-menu"
456+ data-sidebar="menu"
457+ className={cn("flex w-full min-w-0 flex-col gap-1", className)}
458+ {...props}
459+ />
460+ )
461+}
462+
463+function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
464+ return (
465+ <li
466+ data-slot="sidebar-menu-item"
467+ data-sidebar="menu-item"
468+ className={cn("group/menu-item relative", className)}
469+ {...props}
470+ />
471+ )
472+}
473+
474+const sidebarMenuButtonVariants = cva(
475+ "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
476+ {
477+ variants: {
478+ variant: {
479+ default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
480+ outline:
481+ "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
482+ },
483+ size: {
484+ default: "h-8 text-sm",
485+ sm: "h-7 text-xs",
486+ lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!",
487+ },
488+ },
489+ defaultVariants: {
490+ variant: "default",
491+ size: "default",
492+ },
493+ }
494+)
495+
496+function SidebarMenuButton({
497+ asChild = false,
498+ isActive = false,
499+ variant = "default",
500+ size = "default",
501+ tooltip,
502+ className,
503+ ...props
504+}: React.ComponentProps<"button"> & {
505+ asChild?: boolean
506+ isActive?: boolean
507+ tooltip?: string | React.ComponentProps<typeof TooltipContent>
508+} & VariantProps<typeof sidebarMenuButtonVariants>) {
509+ const Comp = asChild ? Slot.Root : "button"
510+ const { isMobile, state } = useSidebar()
511+
512+ const button = (
513+ <Comp
514+ data-slot="sidebar-menu-button"
515+ data-sidebar="menu-button"
516+ data-size={size}
517+ data-active={isActive}
518+ className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
519+ {...props}
520+ />
521+ )
522+
523+ if (!tooltip) {
524+ return button
525+ }
526+
527+ if (typeof tooltip === "string") {
528+ tooltip = {
529+ children: tooltip,
530+ }
531+ }
532+
533+ return (
534+ <Tooltip>
535+ <TooltipTrigger asChild>{button}</TooltipTrigger>
536+ <TooltipContent
537+ side="right"
538+ align="center"
539+ hidden={state !== "collapsed" || isMobile}
540+ {...tooltip}
541+ />
542+ </Tooltip>
543+ )
544+}
545+
546+function SidebarMenuAction({
547+ className,
548+ asChild = false,
549+ showOnHover = false,
550+ ...props
551+}: React.ComponentProps<"button"> & {
552+ asChild?: boolean
553+ showOnHover?: boolean
554+}) {
555+ const Comp = asChild ? Slot.Root : "button"
556+
557+ return (
558+ <Comp
559+ data-slot="sidebar-menu-action"
560+ data-sidebar="menu-action"
561+ className={cn(
562+ "absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform peer-hover/menu-button:text-sidebar-accent-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
563+ // Increases the hit area of the button on mobile.
564+ "after:absolute after:-inset-2 md:after:hidden",
565+ "peer-data-[size=sm]/menu-button:top-1",
566+ "peer-data-[size=default]/menu-button:top-1.5",
567+ "peer-data-[size=lg]/menu-button:top-2.5",
568+ "group-data-[collapsible=icon]:hidden",
569+ showOnHover &&
570+ "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground data-[state=open]:opacity-100 md:opacity-0",
571+ className
572+ )}
573+ {...props}
574+ />
575+ )
576+}
577+
578+function SidebarMenuBadge({
579+ className,
580+ ...props
581+}: React.ComponentProps<"div">) {
582+ return (
583+ <div
584+ data-slot="sidebar-menu-badge"
585+ data-sidebar="menu-badge"
586+ className={cn(
587+ "pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none",
588+ "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
589+ "peer-data-[size=sm]/menu-button:top-1",
590+ "peer-data-[size=default]/menu-button:top-1.5",
591+ "peer-data-[size=lg]/menu-button:top-2.5",
592+ "group-data-[collapsible=icon]:hidden",
593+ className
594+ )}
595+ {...props}
596+ />
597+ )
598+}
599+
600+function SidebarMenuSkeleton({
601+ className,
602+ showIcon = false,
603+ ...props
604+}: React.ComponentProps<"div"> & {
605+ showIcon?: boolean
606+}) {
607+ // Random width between 50 to 90%.
608+ const width = React.useMemo(() => {
609+ return `${Math.floor(Math.random() * 40) + 50}%`
610+ }, [])
611+
612+ return (
613+ <div
614+ data-slot="sidebar-menu-skeleton"
615+ data-sidebar="menu-skeleton"
616+ className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
617+ {...props}
618+ >
619+ {showIcon && (
620+ <Skeleton
621+ className="size-4 rounded-md"
622+ data-sidebar="menu-skeleton-icon"
623+ />
624+ )}
625+ <Skeleton
626+ className="h-4 max-w-(--skeleton-width) flex-1"
627+ data-sidebar="menu-skeleton-text"
628+ style={
629+ {
630+ "--skeleton-width": width,
631+ } as React.CSSProperties
632+ }
633+ />
634+ </div>
635+ )
636+}
637+
638+function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
639+ return (
640+ <ul
641+ data-slot="sidebar-menu-sub"
642+ data-sidebar="menu-sub"
643+ className={cn(
644+ "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5",
645+ "group-data-[collapsible=icon]:hidden",
646+ className
647+ )}
648+ {...props}
649+ />
650+ )
651+}
652+
653+function SidebarMenuSubItem({
654+ className,
655+ ...props
656+}: React.ComponentProps<"li">) {
657+ return (
658+ <li
659+ data-slot="sidebar-menu-sub-item"
660+ data-sidebar="menu-sub-item"
661+ className={cn("group/menu-sub-item relative", className)}
662+ {...props}
663+ />
664+ )
665+}
666+
667+function SidebarMenuSubButton({
668+ asChild = false,
669+ size = "md",
670+ isActive = false,
671+ className,
672+ ...props
673+}: React.ComponentProps<"a"> & {
674+ asChild?: boolean
675+ size?: "sm" | "md"
676+ isActive?: boolean
677+}) {
678+ const Comp = asChild ? Slot.Root : "a"
679+
680+ return (
681+ <Comp
682+ data-slot="sidebar-menu-sub-button"
683+ data-sidebar="menu-sub-button"
684+ data-size={size}
685+ data-active={isActive}
686+ className={cn(
687+ "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
688+ "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
689+ size === "sm" && "text-xs",
690+ size === "md" && "text-sm",
691+ "group-data-[collapsible=icon]:hidden",
692+ className
693+ )}
694+ {...props}
695+ />
696+ )
697+}
698+
699+export {
700+ Sidebar,
701+ SidebarContent,
702+ SidebarFooter,
703+ SidebarGroup,
704+ SidebarGroupAction,
705+ SidebarGroupContent,
706+ SidebarGroupLabel,
707+ SidebarHeader,
708+ SidebarInput,
709+ SidebarInset,
710+ SidebarMenu,
711+ SidebarMenuAction,
712+ SidebarMenuBadge,
713+ SidebarMenuButton,
714+ SidebarMenuItem,
715+ SidebarMenuSkeleton,
716+ SidebarMenuSub,
717+ SidebarMenuSubButton,
718+ SidebarMenuSubItem,
719+ SidebarProvider,
720+ SidebarRail,
721+ SidebarSeparator,
722+ SidebarTrigger,
723+ useSidebar,
724+}
packages/ui/src/components/skeleton.tsx+13−0
@@ -0,0 +1,13 @@
1+import { cn } from "@executor/ui/lib/utils"
2+
3+function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4+ return (
5+ <div
6+ data-slot="skeleton"
7+ className={cn("animate-pulse rounded-md bg-accent", className)}
8+ {...props}
9+ />
10+ )
11+}
12+
13+export { Skeleton }
packages/ui/src/components/slider.tsx+61−0
@@ -0,0 +1,61 @@
1+import * as React from "react"
2+import { Slider as SliderPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function Slider({
7+ className,
8+ defaultValue,
9+ value,
10+ min = 0,
11+ max = 100,
12+ ...props
13+}: React.ComponentProps<typeof SliderPrimitive.Root>) {
14+ const _values = React.useMemo(
15+ () =>
16+ Array.isArray(value)
17+ ? value
18+ : Array.isArray(defaultValue)
19+ ? defaultValue
20+ : [min, max],
21+ [value, defaultValue, min, max]
22+ )
23+
24+ return (
25+ <SliderPrimitive.Root
26+ data-slot="slider"
27+ defaultValue={defaultValue}
28+ value={value}
29+ min={min}
30+ max={max}
31+ className={cn(
32+ "relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
33+ className
34+ )}
35+ {...props}
36+ >
37+ <SliderPrimitive.Track
38+ data-slot="slider-track"
39+ className={cn(
40+ "relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
41+ )}
42+ >
43+ <SliderPrimitive.Range
44+ data-slot="slider-range"
45+ className={cn(
46+ "absolute bg-primary data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
47+ )}
48+ />
49+ </SliderPrimitive.Track>
50+ {Array.from({ length: _values.length }, (_, index) => (
51+ <SliderPrimitive.Thumb
52+ data-slot="slider-thumb"
53+ key={index}
54+ className="block size-4 shrink-0 rounded-full border border-primary bg-white shadow-sm ring-ring/50 transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
55+ />
56+ ))}
57+ </SliderPrimitive.Root>
58+ )
59+}
60+
61+export { Slider }
packages/ui/src/components/sonner.tsx+40−0
@@ -0,0 +1,40 @@
1+"use client"
2+
3+import {
4+ CircleCheckIcon,
5+ InfoIcon,
6+ Loader2Icon,
7+ OctagonXIcon,
8+ TriangleAlertIcon,
9+} from "lucide-react"
10+import { useTheme } from "next-themes"
11+import { Toaster as Sonner, type ToasterProps } from "sonner"
12+
13+const Toaster = ({ ...props }: ToasterProps) => {
14+ const { theme = "system" } = useTheme()
15+
16+ return (
17+ <Sonner
18+ theme={theme as ToasterProps["theme"]}
19+ className="toaster group"
20+ icons={{
21+ success: <CircleCheckIcon className="size-4" />,
22+ info: <InfoIcon className="size-4" />,
23+ warning: <TriangleAlertIcon className="size-4" />,
24+ error: <OctagonXIcon className="size-4" />,
25+ loading: <Loader2Icon className="size-4 animate-spin" />,
26+ }}
27+ style={
28+ {
29+ "--normal-bg": "var(--popover)",
30+ "--normal-text": "var(--popover-foreground)",
31+ "--normal-border": "var(--border)",
32+ "--border-radius": "var(--radius)",
33+ } as React.CSSProperties
34+ }
35+ {...props}
36+ />
37+ )
38+}
39+
40+export { Toaster }
packages/ui/src/components/spinner.tsx+16−0
@@ -0,0 +1,16 @@
1+import { Loader2Icon } from "lucide-react"
2+
3+import { cn } from "@executor/ui/lib/utils"
4+
5+function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
6+ return (
7+ <Loader2Icon
8+ role="status"
9+ aria-label="Loading"
10+ className={cn("size-4 animate-spin", className)}
11+ {...props}
12+ />
13+ )
14+}
15+
16+export { Spinner }
packages/ui/src/components/switch.tsx+33−0
@@ -0,0 +1,33 @@
1+import * as React from "react"
2+import { Switch as SwitchPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function Switch({
7+ className,
8+ size = "default",
9+ ...props
10+}: React.ComponentProps<typeof SwitchPrimitive.Root> & {
11+ size?: "sm" | "default"
12+}) {
13+ return (
14+ <SwitchPrimitive.Root
15+ data-slot="switch"
16+ data-size={size}
17+ className={cn(
18+ "peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80",
19+ className
20+ )}
21+ {...props}
22+ >
23+ <SwitchPrimitive.Thumb
24+ data-slot="switch-thumb"
25+ className={cn(
26+ "pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground"
27+ )}
28+ />
29+ </SwitchPrimitive.Root>
30+ )
31+}
32+
33+export { Switch }
packages/ui/src/components/table.tsx+116−0
@@ -0,0 +1,116 @@
1+"use client"
2+
3+import * as React from "react"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+
7+function Table({ className, ...props }: React.ComponentProps<"table">) {
8+ return (
9+ <div
10+ data-slot="table-container"
11+ className="relative w-full overflow-x-auto"
12+ >
13+ <table
14+ data-slot="table"
15+ className={cn("w-full caption-bottom text-sm", className)}
16+ {...props}
17+ />
18+ </div>
19+ )
20+}
21+
22+function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
23+ return (
24+ <thead
25+ data-slot="table-header"
26+ className={cn("[&_tr]:border-b", className)}
27+ {...props}
28+ />
29+ )
30+}
31+
32+function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
33+ return (
34+ <tbody
35+ data-slot="table-body"
36+ className={cn("[&_tr:last-child]:border-0", className)}
37+ {...props}
38+ />
39+ )
40+}
41+
42+function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
43+ return (
44+ <tfoot
45+ data-slot="table-footer"
46+ className={cn(
47+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
48+ className
49+ )}
50+ {...props}
51+ />
52+ )
53+}
54+
55+function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
56+ return (
57+ <tr
58+ data-slot="table-row"
59+ className={cn(
60+ "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
61+ className
62+ )}
63+ {...props}
64+ />
65+ )
66+}
67+
68+function TableHead({ className, ...props }: React.ComponentProps<"th">) {
69+ return (
70+ <th
71+ data-slot="table-head"
72+ className={cn(
73+ "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
74+ className
75+ )}
76+ {...props}
77+ />
78+ )
79+}
80+
81+function TableCell({ className, ...props }: React.ComponentProps<"td">) {
82+ return (
83+ <td
84+ data-slot="table-cell"
85+ className={cn(
86+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
87+ className
88+ )}
89+ {...props}
90+ />
91+ )
92+}
93+
94+function TableCaption({
95+ className,
96+ ...props
97+}: React.ComponentProps<"caption">) {
98+ return (
99+ <caption
100+ data-slot="table-caption"
101+ className={cn("mt-4 text-sm text-muted-foreground", className)}
102+ {...props}
103+ />
104+ )
105+}
106+
107+export {
108+ Table,
109+ TableHeader,
110+ TableBody,
111+ TableFooter,
112+ TableHead,
113+ TableRow,
114+ TableCell,
115+ TableCaption,
116+}
packages/ui/src/components/tabs.tsx+89−0
@@ -0,0 +1,89 @@
1+import * as React from "react"
2+import { cva, type VariantProps } from "class-variance-authority"
3+import { Tabs as TabsPrimitive } from "radix-ui"
4+
5+import { cn } from "@executor/ui/lib/utils"
6+
7+function Tabs({
8+ className,
9+ orientation = "horizontal",
10+ ...props
11+}: React.ComponentProps<typeof TabsPrimitive.Root>) {
12+ return (
13+ <TabsPrimitive.Root
14+ data-slot="tabs"
15+ data-orientation={orientation}
16+ orientation={orientation}
17+ className={cn(
18+ "group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
19+ className
20+ )}
21+ {...props}
22+ />
23+ )
24+}
25+
26+const tabsListVariants = cva(
27+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
28+ {
29+ variants: {
30+ variant: {
31+ default: "bg-muted",
32+ line: "gap-1 bg-transparent",
33+ },
34+ },
35+ defaultVariants: {
36+ variant: "default",
37+ },
38+ }
39+)
40+
41+function TabsList({
42+ className,
43+ variant = "default",
44+ ...props
45+}: React.ComponentProps<typeof TabsPrimitive.List> &
46+ VariantProps<typeof tabsListVariants>) {
47+ return (
48+ <TabsPrimitive.List
49+ data-slot="tabs-list"
50+ data-variant={variant}
51+ className={cn(tabsListVariants({ variant }), className)}
52+ {...props}
53+ />
54+ )
55+}
56+
57+function TabsTrigger({
58+ className,
59+ ...props
60+}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
61+ return (
62+ <TabsPrimitive.Trigger
63+ data-slot="tabs-trigger"
64+ className={cn(
65+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
66+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
67+ "data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground",
68+ "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
69+ className
70+ )}
71+ {...props}
72+ />
73+ )
74+}
75+
76+function TabsContent({
77+ className,
78+ ...props
79+}: React.ComponentProps<typeof TabsPrimitive.Content>) {
80+ return (
81+ <TabsPrimitive.Content
82+ data-slot="tabs-content"
83+ className={cn("flex-1 outline-none", className)}
84+ {...props}
85+ />
86+ )
87+}
88+
89+export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
packages/ui/src/components/textarea.tsx+18−0
@@ -0,0 +1,18 @@
1+import * as React from "react"
2+
3+import { cn } from "@executor/ui/lib/utils"
4+
5+function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6+ return (
7+ <textarea
8+ data-slot="textarea"
9+ className={cn(
10+ "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
11+ className
12+ )}
13+ {...props}
14+ />
15+ )
16+}
17+
18+export { Textarea }
packages/ui/src/components/toggle-group.tsx+83−0
@@ -0,0 +1,83 @@
1+"use client"
2+
3+import * as React from "react"
4+import { type VariantProps } from "class-variance-authority"
5+import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+import { toggleVariants } from "@executor/ui/components/toggle"
9+
10+const ToggleGroupContext = React.createContext<
11+ VariantProps<typeof toggleVariants> & {
12+ spacing?: number
13+ }
14+>({
15+ size: "default",
16+ variant: "default",
17+ spacing: 0,
18+})
19+
20+function ToggleGroup({
21+ className,
22+ variant,
23+ size,
24+ spacing = 0,
25+ children,
26+ ...props
27+}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
28+ VariantProps<typeof toggleVariants> & {
29+ spacing?: number
30+ }) {
31+ return (
32+ <ToggleGroupPrimitive.Root
33+ data-slot="toggle-group"
34+ data-variant={variant}
35+ data-size={size}
36+ data-spacing={spacing}
37+ style={{ "--gap": spacing } as React.CSSProperties}
38+ className={cn(
39+ "group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=default]:data-[variant=outline]:shadow-xs",
40+ className
41+ )}
42+ {...props}
43+ >
44+ <ToggleGroupContext.Provider value={{ variant, size, spacing }}>
45+ {children}
46+ </ToggleGroupContext.Provider>
47+ </ToggleGroupPrimitive.Root>
48+ )
49+}
50+
51+function ToggleGroupItem({
52+ className,
53+ children,
54+ variant,
55+ size,
56+ ...props
57+}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
58+ VariantProps<typeof toggleVariants>) {
59+ const context = React.useContext(ToggleGroupContext)
60+
61+ return (
62+ <ToggleGroupPrimitive.Item
63+ data-slot="toggle-group-item"
64+ data-variant={context.variant || variant}
65+ data-size={context.size || size}
66+ data-spacing={context.spacing}
67+ className={cn(
68+ toggleVariants({
69+ variant: context.variant || variant,
70+ size: context.size || size,
71+ }),
72+ "w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10",
73+ "data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",
74+ className
75+ )}
76+ {...props}
77+ >
78+ {children}
79+ </ToggleGroupPrimitive.Item>
80+ )
81+}
82+
83+export { ToggleGroup, ToggleGroupItem }
packages/ui/src/components/toggle.tsx+47−0
@@ -0,0 +1,47 @@
1+"use client"
2+
3+import * as React from "react"
4+import { cva, type VariantProps } from "class-variance-authority"
5+import { Toggle as TogglePrimitive } from "radix-ui"
6+
7+import { cn } from "@executor/ui/lib/utils"
8+
9+const toggleVariants = cva(
10+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
11+ {
12+ variants: {
13+ variant: {
14+ default: "bg-transparent",
15+ outline:
16+ "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
17+ },
18+ size: {
19+ default: "h-9 min-w-9 px-2",
20+ sm: "h-8 min-w-8 px-1.5",
21+ lg: "h-10 min-w-10 px-2.5",
22+ },
23+ },
24+ defaultVariants: {
25+ variant: "default",
26+ size: "default",
27+ },
28+ }
29+)
30+
31+function Toggle({
32+ className,
33+ variant,
34+ size,
35+ ...props
36+}: React.ComponentProps<typeof TogglePrimitive.Root> &
37+ VariantProps<typeof toggleVariants>) {
38+ return (
39+ <TogglePrimitive.Root
40+ data-slot="toggle"
41+ className={cn(toggleVariants({ variant, size, className }))}
42+ {...props}
43+ />
44+ )
45+}
46+
47+export { Toggle, toggleVariants }
packages/ui/src/components/tooltip.tsx+55−0
@@ -0,0 +1,55 @@
1+import * as React from "react"
2+import { Tooltip as TooltipPrimitive } from "radix-ui"
3+
4+import { cn } from "@executor/ui/lib/utils"
5+
6+function TooltipProvider({
7+ delayDuration = 0,
8+ ...props
9+}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
10+ return (
11+ <TooltipPrimitive.Provider
12+ data-slot="tooltip-provider"
13+ delayDuration={delayDuration}
14+ {...props}
15+ />
16+ )
17+}
18+
19+function Tooltip({
20+ ...props
21+}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
22+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
23+}
24+
25+function TooltipTrigger({
26+ ...props
27+}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
28+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
29+}
30+
31+function TooltipContent({
32+ className,
33+ sideOffset = 0,
34+ children,
35+ ...props
36+}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
37+ return (
38+ <TooltipPrimitive.Portal>
39+ <TooltipPrimitive.Content
40+ data-slot="tooltip-content"
41+ sideOffset={sideOffset}
42+ className={cn(
43+ "z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
44+ className
45+ )}
46+ {...props}
47+ >
48+ {children}
49+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
50+ </TooltipPrimitive.Content>
51+ </TooltipPrimitive.Portal>
52+ )
53+}
54+
55+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
packages/ui/src/hooks/use-mobile.ts+19−0
@@ -0,0 +1,19 @@
1+import * as React from "react"
2+
3+const MOBILE_BREAKPOINT = 768
4+
5+export function useIsMobile() {
6+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
7+
8+ React.useEffect(() => {
9+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
10+ const onChange = () => {
11+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
12+ }
13+ mql.addEventListener("change", onChange)
14+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
15+ return () => mql.removeEventListener("change", onChange)
16+ }, [])
17+
18+ return !!isMobile
19+}
packages/ui/src/lib/utils.ts+6−0
@@ -0,0 +1,6 @@
1+import { type ClassValue, clsx } from "clsx";
2+import { twMerge } from "tailwind-merge";
3+
4+export function cn(...inputs: ClassValue[]) {
5+ return twMerge(clsx(inputs));
6+}
packages/ui/src/styles/globals.cssrenamed from apps/web/src/styles.css+27−2
@@ -1,7 +1,11 @@
11 @import "tailwindcss";
22
3−@source "../../../packages/clients/react/src/**/*.{ts,tsx}";
4−@source "../../../packages/plugins/openapi/src/react/**/*.{ts,tsx}";
3+@custom-variant dark (&:is(.dark *));
4+
5+@source "../components/**/*.{ts,tsx}";
6+@source "../../../../apps/web/src/**/*.{ts,tsx}";
7+@source "../../../clients/react/src/**/*.{ts,tsx}";
8+@source "../../../plugins/openapi/src/react/**/*.{ts,tsx}";
59
610 @theme inline {
711 --font-sans: "DM Sans", ui-sans-serif, system-ui, sans-serif;
@@ -34,6 +38,11 @@ · 26 unmodified lines
3438 --radius-md: calc(var(--radius) * 0.8);
3539 --radius-lg: var(--radius);
3640 --radius-xl: calc(var(--radius) * 1.4);
41+ --color-sidebar-ring: var(--sidebar-ring);
42+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
43+ --color-sidebar-accent: var(--sidebar-accent);
44+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
45+ --color-sidebar-primary: var(--sidebar-primary);
3746 }
3847
3948 /* ——— Light theme ——— */
@@ -65,6 +74,11 @@ · 25 unmodified lines
6574
6675 --scrollbar-thumb: oklch(0.78 0.005 260);
6776 --scrollbar-thumb-hover: oklch(0.68 0.005 260);
77+ --sidebar-primary: hsl(240 5.9% 10%);
78+ --sidebar-primary-foreground: hsl(0 0% 98%);
79+ --sidebar-accent: hsl(240 4.8% 95.9%);
80+ --sidebar-accent-foreground: hsl(240 5.9% 10%);
81+ --sidebar-ring: hsl(217.2 91.2% 59.8%);
6882 }
6983
7084 /* ——— Dark theme ——— */
@@ -126,3 +140,14 @@ · 55 unmodified lines
126140 background: var(--scrollbar-thumb-hover);
127141 }
128142 }
143+
144+.dark {
145+ --sidebar: hsl(240 5.9% 10%);
146+ --sidebar-foreground: hsl(240 4.8% 95.9%);
147+ --sidebar-primary: hsl(224.3 76.3% 48%);
148+ --sidebar-primary-foreground: hsl(0 0% 100%);
149+ --sidebar-accent: hsl(240 3.7% 15.9%);
150+ --sidebar-accent-foreground: hsl(240 4.8% 95.9%);
151+ --sidebar-border: hsl(240 3.7% 15.9%);
152+ --sidebar-ring: hsl(217.2 91.2% 59.8%);
153+}
packages/ui/tsconfig.json+25−0
@@ -0,0 +1,25 @@
1+{
2+ "compilerOptions": {
3+ "target": "ESNext",
4+ "module": "ESNext",
5+ "moduleResolution": "bundler",
6+ "strict": true,
7+ "esModuleInterop": true,
8+ "skipLibCheck": true,
9+ "outDir": "dist",
10+ "rootDir": "src",
11+ "declaration": true,
12+ "declarationMap": true,
13+ "sourceMap": true,
14+ "jsx": "react-jsx",
15+ "paths": {
16+ "@executor/ui/*": ["./src/*"]
17+ },
18+ "plugins": [
19+ {
20+ "name": "@effect/language-service"
21+ }
22+ ]
23+ },
24+ "include": ["src"]
25+}