@tscircuit/cli 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/.prettierrc +1 -0
  2. package/README.md +50 -0
  3. package/build-cli.ts +14 -0
  4. package/bun.lockb +0 -0
  5. package/cli.ts +12 -0
  6. package/dev-server-api/README.md +3 -0
  7. package/dev-server-api/bun.lockb +0 -0
  8. package/dev-server-api/edgespec.config.ts +4 -0
  9. package/dev-server-api/index.ts +1 -0
  10. package/dev-server-api/package-lock.json +1201 -0
  11. package/dev-server-api/package.json +24 -0
  12. package/dev-server-api/routes/api/dev_package_examples/create.ts +43 -0
  13. package/dev-server-api/routes/api/dev_package_examples/get.ts +37 -0
  14. package/dev-server-api/routes/api/dev_package_examples/list.ts +29 -0
  15. package/dev-server-api/routes/api/dev_package_examples/update.ts +31 -0
  16. package/dev-server-api/routes/api/health.ts +11 -0
  17. package/dev-server-api/src/db/create-schema.ts +14 -0
  18. package/dev-server-api/src/db/get-db.ts +78 -0
  19. package/dev-server-api/src/middlewares/with-db.ts +14 -0
  20. package/dev-server-api/src/middlewares/with-error-response.ts +24 -0
  21. package/dev-server-api/src/with-edge-spec.ts +8 -0
  22. package/dev-server-api/tsconfig.json +28 -0
  23. package/dev-server-frontend/.eslintrc.cjs +20 -0
  24. package/dev-server-frontend/README.md +3 -0
  25. package/dev-server-frontend/bun.lockb +0 -0
  26. package/dev-server-frontend/components.json +17 -0
  27. package/dev-server-frontend/index.html +13 -0
  28. package/dev-server-frontend/package.json +62 -0
  29. package/dev-server-frontend/postcss.config.js +6 -0
  30. package/dev-server-frontend/src/App.tsx +22 -0
  31. package/dev-server-frontend/src/ExampleContentView.tsx +60 -0
  32. package/dev-server-frontend/src/Header.tsx +53 -0
  33. package/dev-server-frontend/src/HeaderMenu.tsx +188 -0
  34. package/dev-server-frontend/src/components/command-k.tsx +62 -0
  35. package/dev-server-frontend/src/components/global-context-providers.tsx +11 -0
  36. package/dev-server-frontend/src/components/select-example-search.tsx +137 -0
  37. package/dev-server-frontend/src/components/ui/alert-dialog.tsx +139 -0
  38. package/dev-server-frontend/src/components/ui/alert.tsx +59 -0
  39. package/dev-server-frontend/src/components/ui/breadcrumb.tsx +115 -0
  40. package/dev-server-frontend/src/components/ui/button.tsx +57 -0
  41. package/dev-server-frontend/src/components/ui/card.tsx +76 -0
  42. package/dev-server-frontend/src/components/ui/command.tsx +153 -0
  43. package/dev-server-frontend/src/components/ui/context-menu.tsx +202 -0
  44. package/dev-server-frontend/src/components/ui/dialog.tsx +120 -0
  45. package/dev-server-frontend/src/components/ui/menubar.tsx +238 -0
  46. package/dev-server-frontend/src/components/ui/navigation-menu.tsx +128 -0
  47. package/dev-server-frontend/src/components/ui/popover.tsx +31 -0
  48. package/dev-server-frontend/src/components/ui/select.tsx +162 -0
  49. package/dev-server-frontend/src/components/ui/tabs.tsx +53 -0
  50. package/dev-server-frontend/src/components/ui/toggle-group.tsx +59 -0
  51. package/dev-server-frontend/src/components/ui/toggle.tsx +43 -0
  52. package/dev-server-frontend/src/components/ui/tooltip.tsx +28 -0
  53. package/dev-server-frontend/src/hooks/toast-if-api-not-connected.ts +23 -0
  54. package/dev-server-frontend/src/hooks/use-global-store.ts +42 -0
  55. package/dev-server-frontend/src/index.css +76 -0
  56. package/dev-server-frontend/src/lib/utils.ts +6 -0
  57. package/dev-server-frontend/src/main.tsx +13 -0
  58. package/dev-server-frontend/src/vite-env.d.ts +1 -0
  59. package/dev-server-frontend/tailwind.config.js +77 -0
  60. package/dev-server-frontend/tsconfig.json +26 -0
  61. package/dev-server-frontend/tsconfig.node.json +13 -0
  62. package/dev-server-frontend/vite.config.ts +23 -0
  63. package/dist/cli.js +1326 -0
  64. package/lib/cmd-fns/auth-login.ts +59 -0
  65. package/lib/cmd-fns/auth-logout.ts +3 -0
  66. package/lib/cmd-fns/auth-sessions-create.ts +3 -0
  67. package/lib/cmd-fns/auth-sessions-get.ts +3 -0
  68. package/lib/cmd-fns/auth-sessions-list.ts +5 -0
  69. package/lib/cmd-fns/config-print-config.ts +6 -0
  70. package/lib/cmd-fns/config-reveal-location.ts +5 -0
  71. package/lib/cmd-fns/config-set-log-requests.ts +7 -0
  72. package/lib/cmd-fns/config-set-registry.ts +7 -0
  73. package/lib/cmd-fns/config-set-session.ts +7 -0
  74. package/lib/cmd-fns/dev.ts +217 -0
  75. package/lib/cmd-fns/index.ts +27 -0
  76. package/lib/cmd-fns/package-examples-create.ts +33 -0
  77. package/lib/cmd-fns/package-examples-get.ts +20 -0
  78. package/lib/cmd-fns/package-examples-list.ts +18 -0
  79. package/lib/cmd-fns/package-files-create.ts +31 -0
  80. package/lib/cmd-fns/package-files-download.ts +29 -0
  81. package/lib/cmd-fns/package-files-get.ts +3 -0
  82. package/lib/cmd-fns/package-files-list.ts +28 -0
  83. package/lib/cmd-fns/package-files-upload-directory.ts +6 -0
  84. package/lib/cmd-fns/package-releases-create.ts +35 -0
  85. package/lib/cmd-fns/package-releases-get.ts +3 -0
  86. package/lib/cmd-fns/package-releases-list.ts +32 -0
  87. package/lib/cmd-fns/packages-create.ts +16 -0
  88. package/lib/cmd-fns/packages-get.ts +16 -0
  89. package/lib/cmd-fns/packages-list.ts +16 -0
  90. package/lib/cmd-fns/publish.ts +3 -0
  91. package/lib/cmd-fns/soupify.ts +27 -0
  92. package/lib/get-program.ts +191 -0
  93. package/lib/param-handlers/index.ts +16 -0
  94. package/lib/param-handlers/interact-for-local-directory.ts +58 -0
  95. package/lib/param-handlers/interact-for-local-file.ts +59 -0
  96. package/lib/param-handlers/interact-for-package-example-id.ts +25 -0
  97. package/lib/param-handlers/interact-for-package-name-with-version.ts +63 -0
  98. package/lib/param-handlers/interact-for-package-name.ts +45 -0
  99. package/lib/param-handlers/interact-for-package-release-id.ts +16 -0
  100. package/lib/param-handlers/param-handler-type.ts +7 -0
  101. package/lib/soupify.ts +71 -0
  102. package/lib/util/app-context.ts +14 -0
  103. package/lib/util/create-context-and-run-program.ts +116 -0
  104. package/package.json +62 -0
  105. package/tests/assets/example-project/examples/basic-capacitor.tsx +1 -0
  106. package/tests/assets/example-project/examples/basic-resistor.tsx +3 -0
  107. package/tests/assets/example-project/package.json +8 -0
  108. package/tests/assets/example-project/src/MyCircuit.tsx +3 -0
  109. package/tests/soupify.test.ts +9 -0
  110. package/tsconfig.json +109 -0
@@ -0,0 +1,188 @@
1
+ import { useState } from "react"
2
+ import {
3
+ Menubar,
4
+ MenubarContent,
5
+ MenubarItem,
6
+ MenubarMenu,
7
+ MenubarRadioGroup,
8
+ MenubarRadioItem,
9
+ MenubarCheckboxItem,
10
+ MenubarSeparator,
11
+ MenubarSub,
12
+ MenubarSubContent,
13
+ MenubarSubTrigger,
14
+ MenubarTrigger,
15
+ } from "src/components/ui/menubar"
16
+ import toast from "react-hot-toast"
17
+ import { useGlobalStore } from "./hooks/use-global-store"
18
+
19
+ export const HeaderMenu = () => {
20
+ const [viewMode, setViewMode] = useGlobalStore((s) => [
21
+ s.view_mode,
22
+ s.setViewMode,
23
+ ])
24
+ const [splitMode, setSplitMode] = useGlobalStore((s) => [
25
+ s.split_mode,
26
+ s.setSplitMode,
27
+ ])
28
+ const [inDebugMode, setInDebugMode] = useState(false)
29
+
30
+ return (
31
+ <Menubar className="border-none shadow-none">
32
+ <MenubarMenu>
33
+ <MenubarTrigger>File</MenubarTrigger>
34
+ <MenubarContent>
35
+ <MenubarItem disabled>seveibar/arduino@1.0.0</MenubarItem>
36
+ <MenubarSeparator />
37
+ <MenubarItem
38
+ onSelect={() => {
39
+ toast.error("Not yet implemented!")
40
+ }}
41
+ >
42
+ New Circuit
43
+ </MenubarItem>
44
+ <MenubarSeparator />
45
+ <MenubarSub>
46
+ <MenubarSubTrigger>Export</MenubarSubTrigger>
47
+ <MenubarSubContent>
48
+ <MenubarItem
49
+ onSelect={() => {
50
+ toast.error("Not yet implemented!")
51
+ }}
52
+ >
53
+ Gerbers
54
+ </MenubarItem>
55
+ <MenubarItem
56
+ onSelect={() => {
57
+ toast.error("Not yet implemented!")
58
+ }}
59
+ >
60
+ Netlist
61
+ </MenubarItem>
62
+ <MenubarItem
63
+ onSelect={() => {
64
+ toast.error("Not yet implemented!")
65
+ }}
66
+ >
67
+ PNG
68
+ </MenubarItem>
69
+ </MenubarSubContent>
70
+ </MenubarSub>
71
+ </MenubarContent>
72
+ </MenubarMenu>
73
+ <MenubarMenu>
74
+ <MenubarTrigger>View</MenubarTrigger>
75
+ <MenubarContent>
76
+ <MenubarRadioGroup
77
+ value={viewMode}
78
+ onValueChange={(v) => setViewMode(v as any)}
79
+ >
80
+ <MenubarRadioItem value="schematic">Schematic</MenubarRadioItem>
81
+ <MenubarRadioItem value="pcb">PCB</MenubarRadioItem>
82
+ <MenubarRadioItem value="split">Split View</MenubarRadioItem>
83
+ </MenubarRadioGroup>
84
+ <MenubarSeparator />
85
+ <MenubarRadioGroup
86
+ value={viewMode === "split" ? splitMode : "neither"}
87
+ onValueChange={(v) => setSplitMode(v as any)}
88
+ >
89
+ <MenubarRadioItem
90
+ disabled={viewMode !== "split"}
91
+ value="horizontal"
92
+ >
93
+ Horizontal Split
94
+ </MenubarRadioItem>
95
+ <MenubarRadioItem disabled={viewMode !== "split"} value="vertical">
96
+ Vertical Split
97
+ </MenubarRadioItem>
98
+ </MenubarRadioGroup>
99
+ <MenubarSeparator />
100
+ <MenubarCheckboxItem
101
+ checked={inDebugMode}
102
+ onSelect={() => setInDebugMode(!inDebugMode)}
103
+ >
104
+ Debug Mode
105
+ </MenubarCheckboxItem>
106
+ </MenubarContent>
107
+ </MenubarMenu>
108
+ <MenubarMenu>
109
+ <MenubarTrigger>Package</MenubarTrigger>
110
+ <MenubarContent>
111
+ <MenubarItem disabled>seveibar/arduino@1.0.0</MenubarItem>
112
+ <MenubarSeparator />
113
+ <MenubarCheckboxItem
114
+ checked
115
+ onSelect={() => toast.error("Not yet implemented!")}
116
+ >
117
+ Published
118
+ </MenubarCheckboxItem>
119
+ <MenubarCheckboxItem
120
+ onSelect={() => toast.error("Not yet implemented!")}
121
+ >
122
+ Locked
123
+ </MenubarCheckboxItem>
124
+ <MenubarItem onSelect={() => toast.error("Not yet implemented!")}>
125
+ Publish New Version
126
+ </MenubarItem>
127
+ <MenubarSeparator />
128
+ <MenubarItem
129
+ onSelect={() => {
130
+ toast.error("Not yet implemented!")
131
+ // window.open(`https://registry.tscircuit.com/${}`)
132
+ }}
133
+ >
134
+ View on Registry
135
+ </MenubarItem>
136
+ <MenubarItem
137
+ onSelect={() => {
138
+ toast.error("Not yet implemented!")
139
+ // window.open(`https://registry.tscircuit.com/${}`)
140
+ }}
141
+ >
142
+ Edit Registry Settings
143
+ </MenubarItem>
144
+ </MenubarContent>
145
+ </MenubarMenu>
146
+ <MenubarMenu>
147
+ <MenubarTrigger>About</MenubarTrigger>
148
+ <MenubarContent>
149
+ <MenubarItem
150
+ onSelect={() => {
151
+ window.open("https://github.com/tscircuit/tscircuit", "_blank")
152
+ }}
153
+ >
154
+ Github
155
+ </MenubarItem>
156
+ <MenubarItem
157
+ onSelect={() => {
158
+ toast.error("Not yet implemented!")
159
+ }}
160
+ >
161
+ Tutorials
162
+ </MenubarItem>
163
+ <MenubarItem
164
+ onSelect={() => {
165
+ toast.error("Not yet implemented!")
166
+ }}
167
+ >
168
+ tscircuit.com
169
+ </MenubarItem>
170
+ <MenubarItem
171
+ onSelect={() => {
172
+ toast.error("Not yet implemented!")
173
+ }}
174
+ >
175
+ registry.tscircuit.com
176
+ </MenubarItem>
177
+ <MenubarItem
178
+ onSelect={() => {
179
+ window.open("https://tscircuit.com/changelog", "_blank")
180
+ }}
181
+ >
182
+ Changelog
183
+ </MenubarItem>
184
+ </MenubarContent>
185
+ </MenubarMenu>
186
+ </Menubar>
187
+ )
188
+ }
@@ -0,0 +1,62 @@
1
+ import { useEffect, useState } from "react"
2
+ import {
3
+ Command,
4
+ CommandDialog,
5
+ CommandGroup,
6
+ CommandInput,
7
+ CommandItem,
8
+ CommandList,
9
+ CommandShortcut,
10
+ } from "./ui/command"
11
+ import { useGlobalStore } from "src/hooks/use-global-store"
12
+
13
+ export const CommandK = () => {
14
+ const [open, setOpen] = useState(false)
15
+ const store = useGlobalStore()
16
+ useEffect(() => {
17
+ const down = (e: KeyboardEvent) => {
18
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
19
+ e.preventDefault()
20
+ setOpen((open) => !open)
21
+ }
22
+ }
23
+
24
+ document.addEventListener("keydown", down)
25
+ return () => document.removeEventListener("keydown", down)
26
+ }, [])
27
+
28
+ const close = () => {
29
+ setOpen(false)
30
+ return true
31
+ }
32
+
33
+ return (
34
+ <CommandDialog open={open}>
35
+ <Command className="rounded-lg border shadow-md">
36
+ <CommandInput placeholder="Type a command or search..." />
37
+ <CommandGroup>
38
+ <CommandList>
39
+ <CommandItem
40
+ onSelect={() => close() && store.setViewMode("schematic")}
41
+ >
42
+ View Schematic
43
+ <CommandShortcut></CommandShortcut>
44
+ </CommandItem>
45
+ <CommandItem onSelect={() => close() && store.setViewMode("pcb")}>
46
+ View PCB
47
+ <CommandShortcut></CommandShortcut>
48
+ </CommandItem>
49
+ <CommandItem onSelect={() => close() && store.setViewMode("split")}>
50
+ View in Split Mode
51
+ <CommandShortcut></CommandShortcut>
52
+ </CommandItem>
53
+ <CommandItem onSelect={() => close() && store.setViewMode("split")}>
54
+ Vertical Split
55
+ <CommandShortcut></CommandShortcut>
56
+ </CommandItem>
57
+ </CommandList>
58
+ </CommandGroup>
59
+ </Command>
60
+ </CommandDialog>
61
+ )
62
+ }
@@ -0,0 +1,11 @@
1
+ import { Toaster } from "react-hot-toast"
2
+ import { QueryClient, QueryClientProvider } from "react-query"
3
+
4
+ const queryClient = new QueryClient()
5
+
6
+ export const GlobalContextProviders = ({ children }: any) => (
7
+ <>
8
+ <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
9
+ <Toaster />
10
+ </>
11
+ )
@@ -0,0 +1,137 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
5
+ import { useQuery } from "react-query"
6
+ import axios from "axios"
7
+
8
+ import { cn } from "src/lib/utils"
9
+ import { Button } from "src/components/ui/button"
10
+ import {
11
+ Command,
12
+ CommandEmpty,
13
+ CommandGroup,
14
+ CommandInput,
15
+ CommandItem,
16
+ CommandList,
17
+ CommandShortcut,
18
+ } from "src/components/ui/command"
19
+ import {
20
+ Popover,
21
+ PopoverContent,
22
+ PopoverTrigger,
23
+ } from "src/components/ui/popover"
24
+ import { useGlobalStore } from "src/hooks/use-global-store"
25
+
26
+ export const useDevPackageExamples = () => {
27
+ return useQuery("examples", async () => {
28
+ const { data } = await axios.get("/api/dev_package_examples/list")
29
+ return data.dev_package_examples.map(inflatePackageExample) as Array<{
30
+ dev_package_example_id: number
31
+ searchable_id: string
32
+ file_path: string
33
+ expath: string
34
+ export_name: string
35
+ last_updated_at: string
36
+ }>
37
+ })
38
+ }
39
+
40
+ function inflatePackageExample(ex: any) {
41
+ if (!ex) return ex
42
+ return {
43
+ ...ex,
44
+ expath: ex.file_path.replace(/.*examples\//, ""),
45
+ searchable_id: `${ex.file_path.replace(/.*examples\//, "")} | ${
46
+ ex.export_name
47
+ }`,
48
+ }
49
+ }
50
+
51
+ export const SelectExampleSearch = () => {
52
+ const [open, setOpen] = React.useState(false)
53
+ const { data: examples } = useDevPackageExamples()
54
+ const [active_dev_example_package_id, setActiveDevExamplePackageId] =
55
+ useGlobalStore((s) => [
56
+ s.active_dev_example_package_id,
57
+ s.setActiveDevExamplePackageId,
58
+ ])
59
+
60
+ const activeDevExamplePackage = inflatePackageExample(
61
+ examples?.find(
62
+ (ex) =>
63
+ ex.dev_package_example_id.toString() === active_dev_example_package_id
64
+ )
65
+ )
66
+
67
+ React.useEffect(() => {
68
+ const down = (e: KeyboardEvent) => {
69
+ if (e.key === "o" && (e.metaKey || e.ctrlKey)) {
70
+ e.preventDefault()
71
+ setOpen((open) => !open)
72
+ }
73
+ }
74
+
75
+ document.addEventListener("keydown", down)
76
+ return () => document.removeEventListener("keydown", down)
77
+ }, [])
78
+
79
+ return (
80
+ <Popover open={open} onOpenChange={setOpen} modal>
81
+ <PopoverTrigger asChild>
82
+ <Button
83
+ variant="outline"
84
+ role="combobox"
85
+ aria-expanded={open}
86
+ className="w-[200px] justify-between"
87
+ >
88
+ <div>
89
+ {activeDevExamplePackage
90
+ ? activeDevExamplePackage.expath
91
+ : "Select Circuit"}
92
+ </div>
93
+ <CommandShortcut>
94
+ <kbd>⌘O</kbd>
95
+ </CommandShortcut>
96
+ <CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
97
+ </Button>
98
+ </PopoverTrigger>
99
+ <PopoverContent className="w-[400px] p-0">
100
+ <Command value={active_dev_example_package_id ?? "none"}>
101
+ <CommandInput placeholder="Search for circuit..." className="h-9" />
102
+ <CommandList>
103
+ <CommandEmpty>
104
+ <div className="text-gray-600 mt-8">
105
+ No matching circuits in{" "}
106
+ <span className="font-mono bg-gray-100 py-0.5 px-1 rounded-sm">
107
+ ./examples
108
+ </span>
109
+ </div>
110
+ <Button className="mt-12 mb-8">Create Circuit</Button>
111
+ </CommandEmpty>
112
+ <CommandGroup>
113
+ {examples?.map((example) => (
114
+ <CommandItem
115
+ key={example.dev_package_example_id}
116
+ value={example.searchable_id}
117
+ onSelect={() => {
118
+ setActiveDevExamplePackageId(
119
+ example.dev_package_example_id.toString()
120
+ )
121
+ setOpen(false)
122
+ }}
123
+ className="flex justify-between cursor-pointer hover:bg-gray-100"
124
+ >
125
+ <div className="text-gray-800">{example.expath}</div>
126
+ <div className="font-mono text-xs text-gray-600">
127
+ {example.export_name}
128
+ </div>
129
+ </CommandItem>
130
+ ))}
131
+ </CommandGroup>
132
+ </CommandList>
133
+ </Command>
134
+ </PopoverContent>
135
+ </Popover>
136
+ )
137
+ }
@@ -0,0 +1,139 @@
1
+ import * as React from "react"
2
+ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
3
+
4
+ import { cn } from "src/lib/utils"
5
+ import { buttonVariants } from "src/components/ui/button"
6
+
7
+ const AlertDialog = AlertDialogPrimitive.Root
8
+
9
+ const AlertDialogTrigger = AlertDialogPrimitive.Trigger
10
+
11
+ const AlertDialogPortal = AlertDialogPrimitive.Portal
12
+
13
+ const AlertDialogOverlay = React.forwardRef<
14
+ React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
15
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
16
+ >(({ className, ...props }, ref) => (
17
+ <AlertDialogPrimitive.Overlay
18
+ className={cn(
19
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
20
+ className
21
+ )}
22
+ {...props}
23
+ ref={ref}
24
+ />
25
+ ))
26
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
27
+
28
+ const AlertDialogContent = React.forwardRef<
29
+ React.ElementRef<typeof AlertDialogPrimitive.Content>,
30
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
31
+ >(({ className, ...props }, ref) => (
32
+ <AlertDialogPortal>
33
+ <AlertDialogOverlay />
34
+ <AlertDialogPrimitive.Content
35
+ ref={ref}
36
+ className={cn(
37
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
38
+ className
39
+ )}
40
+ {...props}
41
+ />
42
+ </AlertDialogPortal>
43
+ ))
44
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
45
+
46
+ const AlertDialogHeader = ({
47
+ className,
48
+ ...props
49
+ }: React.HTMLAttributes<HTMLDivElement>) => (
50
+ <div
51
+ className={cn(
52
+ "flex flex-col space-y-2 text-center sm:text-left",
53
+ className
54
+ )}
55
+ {...props}
56
+ />
57
+ )
58
+ AlertDialogHeader.displayName = "AlertDialogHeader"
59
+
60
+ const AlertDialogFooter = ({
61
+ className,
62
+ ...props
63
+ }: React.HTMLAttributes<HTMLDivElement>) => (
64
+ <div
65
+ className={cn(
66
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
67
+ className
68
+ )}
69
+ {...props}
70
+ />
71
+ )
72
+ AlertDialogFooter.displayName = "AlertDialogFooter"
73
+
74
+ const AlertDialogTitle = React.forwardRef<
75
+ React.ElementRef<typeof AlertDialogPrimitive.Title>,
76
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
77
+ >(({ className, ...props }, ref) => (
78
+ <AlertDialogPrimitive.Title
79
+ ref={ref}
80
+ className={cn("text-lg font-semibold", className)}
81
+ {...props}
82
+ />
83
+ ))
84
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
85
+
86
+ const AlertDialogDescription = React.forwardRef<
87
+ React.ElementRef<typeof AlertDialogPrimitive.Description>,
88
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
89
+ >(({ className, ...props }, ref) => (
90
+ <AlertDialogPrimitive.Description
91
+ ref={ref}
92
+ className={cn("text-sm text-muted-foreground", className)}
93
+ {...props}
94
+ />
95
+ ))
96
+ AlertDialogDescription.displayName =
97
+ AlertDialogPrimitive.Description.displayName
98
+
99
+ const AlertDialogAction = React.forwardRef<
100
+ React.ElementRef<typeof AlertDialogPrimitive.Action>,
101
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
102
+ >(({ className, ...props }, ref) => (
103
+ <AlertDialogPrimitive.Action
104
+ ref={ref}
105
+ className={cn(buttonVariants(), className)}
106
+ {...props}
107
+ />
108
+ ))
109
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
110
+
111
+ const AlertDialogCancel = React.forwardRef<
112
+ React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
113
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
114
+ >(({ className, ...props }, ref) => (
115
+ <AlertDialogPrimitive.Cancel
116
+ ref={ref}
117
+ className={cn(
118
+ buttonVariants({ variant: "outline" }),
119
+ "mt-2 sm:mt-0",
120
+ className
121
+ )}
122
+ {...props}
123
+ />
124
+ ))
125
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
126
+
127
+ export {
128
+ AlertDialog,
129
+ AlertDialogPortal,
130
+ AlertDialogOverlay,
131
+ AlertDialogTrigger,
132
+ AlertDialogContent,
133
+ AlertDialogHeader,
134
+ AlertDialogFooter,
135
+ AlertDialogTitle,
136
+ AlertDialogDescription,
137
+ AlertDialogAction,
138
+ AlertDialogCancel,
139
+ }
@@ -0,0 +1,59 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "src/lib/utils"
5
+
6
+ const alertVariants = cva(
7
+ "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-background text-foreground",
12
+ destructive:
13
+ "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ }
20
+ )
21
+
22
+ const Alert = React.forwardRef<
23
+ HTMLDivElement,
24
+ React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
25
+ >(({ className, variant, ...props }, ref) => (
26
+ <div
27
+ ref={ref}
28
+ role="alert"
29
+ className={cn(alertVariants({ variant }), className)}
30
+ {...props}
31
+ />
32
+ ))
33
+ Alert.displayName = "Alert"
34
+
35
+ const AlertTitle = React.forwardRef<
36
+ HTMLParagraphElement,
37
+ React.HTMLAttributes<HTMLHeadingElement>
38
+ >(({ className, ...props }, ref) => (
39
+ <h5
40
+ ref={ref}
41
+ className={cn("mb-1 font-medium leading-none tracking-tight", className)}
42
+ {...props}
43
+ />
44
+ ))
45
+ AlertTitle.displayName = "AlertTitle"
46
+
47
+ const AlertDescription = React.forwardRef<
48
+ HTMLParagraphElement,
49
+ React.HTMLAttributes<HTMLParagraphElement>
50
+ >(({ className, ...props }, ref) => (
51
+ <div
52
+ ref={ref}
53
+ className={cn("text-sm [&_p]:leading-relaxed", className)}
54
+ {...props}
55
+ />
56
+ ))
57
+ AlertDescription.displayName = "AlertDescription"
58
+
59
+ export { Alert, AlertTitle, AlertDescription }