@aphrody/koharu-ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +37 -2
  2. package/package.json +70 -4
  3. package/src/assets/NotoSansCJK-VF.otf +0 -0
  4. package/src/assets/OFL.txt +92 -0
  5. package/src/components/ClientOnly.tsx +21 -0
  6. package/src/components/ShenronExportPanel.tsx +213 -0
  7. package/src/components/app/AboutDialog.tsx +64 -0
  8. package/src/components/app/KoharuApp.tsx +58 -0
  9. package/src/components/app/StartupView.tsx +67 -0
  10. package/src/components/app/TitleBar.tsx +322 -0
  11. package/src/components/app/Updater.tsx +143 -0
  12. package/src/components/app/WindowChrome.tsx +137 -0
  13. package/src/components/controls/ColorSampling.tsx +70 -0
  14. package/src/components/controls/ColorWell.tsx +139 -0
  15. package/src/components/controls/CommitTextarea.tsx +63 -0
  16. package/src/components/controls/FontPicker.tsx +543 -0
  17. package/src/components/controls/ModelPicker.tsx +178 -0
  18. package/src/components/controls/OutputPicker.tsx +170 -0
  19. package/src/components/editor/ActivityCenter.tsx +234 -0
  20. package/src/components/editor/AgentPanel.tsx +352 -0
  21. package/src/components/editor/CanvasCommandBar.tsx +38 -0
  22. package/src/components/editor/CanvasOverlay.tsx +194 -0
  23. package/src/components/editor/CanvasWorkspace.tsx +947 -0
  24. package/src/components/editor/Editor.tsx +56 -0
  25. package/src/components/editor/InferenceControl.tsx +457 -0
  26. package/src/components/editor/Inspector.tsx +1073 -0
  27. package/src/components/editor/PageRail.tsx +581 -0
  28. package/src/components/editor/ResourceMonitor.tsx +151 -0
  29. package/src/components/editor/RightSidebar.tsx +50 -0
  30. package/src/components/editor/SelectionControls.tsx +184 -0
  31. package/src/components/editor/StatusBar.tsx +41 -0
  32. package/src/components/editor/ToolBar.tsx +182 -0
  33. package/src/components/editor/useCanvas.ts +194 -0
  34. package/src/components/preferences/GenerationPreferences.tsx +118 -0
  35. package/src/components/preferences/KoharuSettings.tsx +229 -0
  36. package/src/components/preferences/PipelinePreferences.tsx +173 -0
  37. package/src/components/preferences/PreferenceFields.tsx +149 -0
  38. package/src/components/preferences/ProviderPreferences.tsx +173 -0
  39. package/src/components/preferences/SettingsPage.tsx +336 -0
  40. package/src/components/preferences/TranslationPreferences.tsx +187 -0
  41. package/src/components/preferences/TypesettingPreferences.tsx +142 -0
  42. package/src/components/preferences/models.ts +103 -0
  43. package/src/components/start/StartView.tsx +277 -0
  44. package/src/index.ts +59 -0
  45. package/src/internal/components/accordion.tsx +72 -0
  46. package/src/internal/components/alert-dialog.tsx +189 -0
  47. package/src/internal/components/alert.tsx +76 -0
  48. package/src/internal/components/aspect-ratio.tsx +22 -0
  49. package/src/internal/components/attachment.tsx +207 -0
  50. package/src/internal/components/avatar.tsx +109 -0
  51. package/src/internal/components/badge.tsx +52 -0
  52. package/src/internal/components/breadcrumb.tsx +125 -0
  53. package/src/internal/components/bubble.tsx +128 -0
  54. package/src/internal/components/button-group.tsx +87 -0
  55. package/src/internal/components/button.tsx +58 -0
  56. package/src/internal/components/card.tsx +103 -0
  57. package/src/internal/components/checkbox.tsx +29 -0
  58. package/src/internal/components/collapsible.tsx +21 -0
  59. package/src/internal/components/combobox.tsx +298 -0
  60. package/src/internal/components/context-menu.tsx +272 -0
  61. package/src/internal/components/dialog.tsx +162 -0
  62. package/src/internal/components/direction.tsx +6 -0
  63. package/src/internal/components/drawer.tsx +230 -0
  64. package/src/internal/components/dropdown-menu.tsx +278 -0
  65. package/src/internal/components/empty.tsx +104 -0
  66. package/src/internal/components/field.tsx +238 -0
  67. package/src/internal/components/hover-card.tsx +52 -0
  68. package/src/internal/components/input-group.tsx +158 -0
  69. package/src/internal/components/input.tsx +20 -0
  70. package/src/internal/components/item.tsx +201 -0
  71. package/src/internal/components/kbd.tsx +26 -0
  72. package/src/internal/components/label.tsx +20 -0
  73. package/src/internal/components/marker.tsx +71 -0
  74. package/src/internal/components/menubar.tsx +280 -0
  75. package/src/internal/components/message.tsx +92 -0
  76. package/src/internal/components/native-select.tsx +61 -0
  77. package/src/internal/components/navigation-menu.tsx +168 -0
  78. package/src/internal/components/number-field.tsx +68 -0
  79. package/src/internal/components/pagination.tsx +130 -0
  80. package/src/internal/components/popover.tsx +91 -0
  81. package/src/internal/components/progress.tsx +83 -0
  82. package/src/internal/components/radio-group.tsx +38 -0
  83. package/src/internal/components/resizable.tsx +50 -0
  84. package/src/internal/components/scroll-area.tsx +88 -0
  85. package/src/internal/components/select.tsx +191 -0
  86. package/src/internal/components/separator.tsx +25 -0
  87. package/src/internal/components/sheet.tsx +140 -0
  88. package/src/internal/components/sidebar.tsx +723 -0
  89. package/src/internal/components/skeleton.tsx +13 -0
  90. package/src/internal/components/slider.tsx +56 -0
  91. package/src/internal/components/spinner.tsx +10 -0
  92. package/src/internal/components/switch.tsx +32 -0
  93. package/src/internal/components/table.tsx +116 -0
  94. package/src/internal/components/tabs.tsx +82 -0
  95. package/src/internal/components/textarea.tsx +18 -0
  96. package/src/internal/components/toast.tsx +235 -0
  97. package/src/internal/components/toggle-group.tsx +89 -0
  98. package/src/internal/components/toggle.tsx +45 -0
  99. package/src/internal/components/tooltip.tsx +67 -0
  100. package/src/internal/hooks/use-mobile.ts +19 -0
  101. package/src/internal/lib/utils.ts +6 -0
  102. package/src/internal/styles/globals.css +73 -0
  103. package/src/lib/backend.ts +85 -0
  104. package/src/lib/document.ts +46 -0
  105. package/src/lib/geometry.ts +272 -0
  106. package/src/lib/i18n.ts +54 -0
  107. package/src/lib/queries.ts +110 -0
  108. package/src/lib/store.ts +191 -0
  109. package/src/lib/translation.ts +35 -0
  110. package/src/locales/en-US/translation.json +536 -0
  111. package/src/locales/es-ES/translation.json +536 -0
  112. package/src/locales/ja-JP/translation.json +536 -0
  113. package/src/locales/ko-KR/translation.json +536 -0
  114. package/src/locales/pt-BR/translation.json +536 -0
  115. package/src/locales/ru-RU/translation.json +536 -0
  116. package/src/locales/tr-TR/translation.json +536 -0
  117. package/src/locales/zh-CN/translation.json +536 -0
  118. package/src/locales/zh-TW/translation.json +536 -0
  119. package/src/navigation.ts +28 -0
  120. package/src/providers.tsx +132 -0
  121. package/src/shenron-pipeline-env.d.ts +14 -0
  122. package/src/styles.css +246 -0
  123. package/tsconfig.json +21 -0
@@ -0,0 +1,162 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
5
+
6
+ import { cn } from "../lib/utils"
7
+ import { Button } from "./button"
8
+ import { XIcon } from "lucide-react"
9
+
10
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
11
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
12
+ }
13
+
14
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
15
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
16
+ }
17
+
18
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
19
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
20
+ }
21
+
22
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
23
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
24
+ }
25
+
26
+ function DialogOverlay({
27
+ className,
28
+ ...props
29
+ }: DialogPrimitive.Backdrop.Props) {
30
+ return (
31
+ <DialogPrimitive.Backdrop
32
+ data-koharu-ui-layer=""
33
+ data-slot="dialog-overlay"
34
+ className={cn(
35
+ "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
36
+ className
37
+ )}
38
+ {...props}
39
+ />
40
+ )
41
+ }
42
+
43
+ function DialogContent({
44
+ className,
45
+ children,
46
+ showCloseButton = true,
47
+ ...props
48
+ }: DialogPrimitive.Popup.Props & {
49
+ showCloseButton?: boolean
50
+ }) {
51
+ return (
52
+ <DialogPortal>
53
+ <DialogOverlay />
54
+ <DialogPrimitive.Popup
55
+ data-koharu-ui-layer=""
56
+ data-slot="dialog-content"
57
+ className={cn(
58
+ "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm 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",
59
+ className
60
+ )}
61
+ {...props}
62
+ >
63
+ {children}
64
+ {showCloseButton && (
65
+ <DialogPrimitive.Close
66
+ data-slot="dialog-close"
67
+ render={
68
+ <Button
69
+ variant="ghost"
70
+ className="absolute top-2 right-2"
71
+ size="icon-sm"
72
+ />
73
+ }
74
+ >
75
+ <XIcon
76
+ />
77
+ <span className="sr-only">Close</span>
78
+ </DialogPrimitive.Close>
79
+ )}
80
+ </DialogPrimitive.Popup>
81
+ </DialogPortal>
82
+ )
83
+ }
84
+
85
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
86
+ return (
87
+ <div
88
+ data-slot="dialog-header"
89
+ className={cn("flex flex-col gap-2", className)}
90
+ {...props}
91
+ />
92
+ )
93
+ }
94
+
95
+ function DialogFooter({
96
+ className,
97
+ showCloseButton = false,
98
+ children,
99
+ ...props
100
+ }: React.ComponentProps<"div"> & {
101
+ showCloseButton?: boolean
102
+ }) {
103
+ return (
104
+ <div
105
+ data-slot="dialog-footer"
106
+ className={cn(
107
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
108
+ className
109
+ )}
110
+ {...props}
111
+ >
112
+ {children}
113
+ {showCloseButton && (
114
+ <DialogPrimitive.Close render={<Button variant="outline" />}>
115
+ Close
116
+ </DialogPrimitive.Close>
117
+ )}
118
+ </div>
119
+ )
120
+ }
121
+
122
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
123
+ return (
124
+ <DialogPrimitive.Title
125
+ data-slot="dialog-title"
126
+ className={cn(
127
+ "text-base leading-none font-medium",
128
+ className
129
+ )}
130
+ {...props}
131
+ />
132
+ )
133
+ }
134
+
135
+ function DialogDescription({
136
+ className,
137
+ ...props
138
+ }: DialogPrimitive.Description.Props) {
139
+ return (
140
+ <DialogPrimitive.Description
141
+ data-slot="dialog-description"
142
+ className={cn(
143
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
144
+ className
145
+ )}
146
+ {...props}
147
+ />
148
+ )
149
+ }
150
+
151
+ export {
152
+ Dialog,
153
+ DialogClose,
154
+ DialogContent,
155
+ DialogDescription,
156
+ DialogFooter,
157
+ DialogHeader,
158
+ DialogOverlay,
159
+ DialogPortal,
160
+ DialogTitle,
161
+ DialogTrigger,
162
+ }
@@ -0,0 +1,6 @@
1
+ "use client"
2
+
3
+ export {
4
+ DirectionProvider,
5
+ useDirection,
6
+ } from "@base-ui/react/direction-provider"
@@ -0,0 +1,230 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Drawer as DrawerPrimitive } from "@base-ui/react/drawer"
5
+
6
+ import { cn } from "../lib/utils"
7
+
8
+ type DrawerContextProps = {
9
+ hasSnapPoints: boolean
10
+ modal: DrawerPrimitive.Root.Props["modal"]
11
+ showSwipeHandle: boolean
12
+ swipeDirection: NonNullable<DrawerPrimitive.Root.Props["swipeDirection"]>
13
+ }
14
+
15
+ const DrawerContext = React.createContext<DrawerContextProps | null>(null)
16
+
17
+ function useDrawer() {
18
+ const context = React.useContext(DrawerContext)
19
+
20
+ if (!context) {
21
+ throw new Error("useDrawer must be used within a Drawer.")
22
+ }
23
+
24
+ return context
25
+ }
26
+
27
+ function Drawer({
28
+ modal = true,
29
+ showSwipeHandle = false,
30
+ snapPoints,
31
+ swipeDirection = "down",
32
+ ...props
33
+ }: DrawerPrimitive.Root.Props & {
34
+ showSwipeHandle?: boolean
35
+ }) {
36
+ const hasSnapPoints = snapPoints != null && snapPoints.length > 0
37
+ const contextValue = React.useMemo(
38
+ () => ({ hasSnapPoints, modal, showSwipeHandle, swipeDirection }),
39
+ [hasSnapPoints, modal, showSwipeHandle, swipeDirection]
40
+ )
41
+
42
+ return (
43
+ <DrawerContext.Provider value={contextValue}>
44
+ <DrawerPrimitive.Root
45
+ data-slot="drawer"
46
+ modal={modal}
47
+ snapPoints={snapPoints}
48
+ swipeDirection={swipeDirection}
49
+ {...props}
50
+ />
51
+ </DrawerContext.Provider>
52
+ )
53
+ }
54
+
55
+ function DrawerTrigger({ ...props }: DrawerPrimitive.Trigger.Props) {
56
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
57
+ }
58
+
59
+ function DrawerPortal({ ...props }: DrawerPrimitive.Portal.Props) {
60
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
61
+ }
62
+
63
+ function DrawerClose({ ...props }: DrawerPrimitive.Close.Props) {
64
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
65
+ }
66
+
67
+ function DrawerOverlay({
68
+ className,
69
+ ...props
70
+ }: DrawerPrimitive.Backdrop.Props) {
71
+ return (
72
+ <DrawerPrimitive.Backdrop
73
+ data-koharu-ui-layer=""
74
+ data-slot="drawer-overlay"
75
+ className={cn(
76
+ "fixed inset-0 z-50 min-h-dvh bg-black/10 opacity-[max(var(--drawer-overlay-min-opacity,0),calc(1-var(--drawer-swipe-progress)))] transition-opacity duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] select-none data-ending-style:pointer-events-none data-ending-style:opacity-0 data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-snap-points:[--drawer-overlay-min-opacity:0.5] data-starting-style:opacity-0 data-swiping:duration-0 supports-backdrop-filter:backdrop-blur-xs supports-[-webkit-touch-callout:none]:absolute",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ function DrawerSwipeHandle({
85
+ className,
86
+ ...props
87
+ }: React.ComponentProps<"div">) {
88
+ return (
89
+ <div
90
+ data-slot="drawer-swipe-handle"
91
+ aria-hidden="true"
92
+ className={cn(
93
+ "relative z-10 flex shrink-0 cursor-grab transition-opacity duration-200 group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100 group-data-[swipe-axis=x]/drawer-popup:h-full group-data-[swipe-axis=x]/drawer-popup:w-3 group-data-[swipe-axis=x]/drawer-popup:items-center group-data-[swipe-axis=y]/drawer-popup:h-3 group-data-[swipe-axis=y]/drawer-popup:w-full group-data-[swipe-axis=y]/drawer-popup:justify-center group-data-[swipe-direction=down]/drawer-popup:items-end group-data-[swipe-direction=left]/drawer-popup:order-last group-data-[swipe-direction=left]/drawer-popup:justify-start group-data-[swipe-direction=right]/drawer-popup:justify-end group-data-[swipe-direction=up]/drawer-popup:order-last group-data-[swipe-direction=up]/drawer-popup:items-start after:block after:shrink-0 after:rounded-full after:bg-muted group-data-[swipe-axis=x]/drawer-popup:after:h-24 group-data-[swipe-axis=x]/drawer-popup:after:w-1 group-data-[swipe-axis=y]/drawer-popup:after:h-1 group-data-[swipe-axis=y]/drawer-popup:after:w-24 active:cursor-grabbing",
94
+ className
95
+ )}
96
+ {...props}
97
+ />
98
+ )
99
+ }
100
+
101
+ function DrawerContent({
102
+ className,
103
+ children,
104
+ ...props
105
+ }: DrawerPrimitive.Popup.Props) {
106
+ const { hasSnapPoints, modal, showSwipeHandle, swipeDirection } = useDrawer()
107
+ const swipeAxis =
108
+ swipeDirection === "down" || swipeDirection === "up" ? "y" : "x"
109
+
110
+ return (
111
+ <DrawerPortal data-slot="drawer-portal">
112
+ {modal === true && (
113
+ <DrawerOverlay data-snap-points={hasSnapPoints ? "" : undefined} />
114
+ )}
115
+ <DrawerPrimitive.Viewport
116
+ data-slot="drawer-viewport"
117
+ data-modal={modal}
118
+ className="pointer-events-none fixed inset-0 z-50 select-none data-[modal=true]:pointer-events-auto"
119
+ >
120
+ <DrawerPrimitive.Popup
121
+ data-koharu-ui-layer=""
122
+ data-slot="drawer-popup"
123
+ data-swipe-axis={swipeAxis}
124
+ data-snap-points={hasSnapPoints ? "" : undefined}
125
+ className={cn(
126
+ // Base.
127
+ "group/drawer-popup pointer-events-auto fixed z-50 m-(--drawer-inset,0px) flex h-(--drawer-content-height) max-h-(--drawer-content-max-height,none) min-h-0 w-(--drawer-content-width,auto) transform-[translate3d(var(--translate-x,0px),var(--translate-y,0px),0)_scale(var(--stack-scale))] flex-col bg-popover text-sm text-popover-foreground transition-[transform,height,opacity,filter] duration-450 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform outline-none select-none [interpolate-size:allow-keywords] data-[swipe-direction=down]:rounded-t-xl data-[swipe-direction=down]:border-t data-[swipe-direction=left]:rounded-r-xl data-[swipe-direction=left]:border-r data-[swipe-direction=right]:rounded-l-xl data-[swipe-direction=right]:border-l data-[swipe-direction=up]:rounded-b-xl data-[swipe-direction=up]:border-b",
128
+ // Nested.
129
+ "data-nested-drawer-open:overflow-hidden data-nested-drawer-open:brightness-95",
130
+ // Bleed.
131
+ "after:pointer-events-none after:absolute after:bg-(--drawer-bleed-background,var(--color-popover)) data-[swipe-axis=x]:after:inset-y-0 data-[swipe-axis=x]:after:w-(--bleed) data-[swipe-axis=y]:after:inset-x-0 data-[swipe-axis=y]:after:h-(--bleed) data-[swipe-direction=down]:after:top-full data-[swipe-direction=left]:after:right-full data-[swipe-direction=right]:after:left-full data-[swipe-direction=up]:after:bottom-full",
132
+ // Sizing.
133
+ "[--drawer-content-height:var(--drawer-height,auto)] data-[swipe-axis=x]:[--drawer-content-width:75%] data-[swipe-axis=y]:[--drawer-content-max-height:calc(100dvh-6rem)] data-[swipe-axis=y]:data-snap-points:[--drawer-content-height:100dvh] data-[swipe-axis=x]:sm:[--drawer-content-width:24rem]",
134
+ // Stack.
135
+ "[--bleed:3rem] [--peek:1rem] [--stack-height:var(--drawer-frontmost-height,var(--drawer-height,0px))] [--stack-peek-offset:max(0px,calc((var(--nested-drawers)-var(--stack-progress))*var(--peek)))] [--stack-progress:clamp(0,var(--drawer-swipe-progress),1)] [--stack-scale-base:max(0,calc(1-(var(--nested-drawers)*var(--stack-step))))] [--stack-scale:clamp(0,calc(var(--stack-scale-base)+(var(--stack-step)*var(--stack-progress))),1)] [--stack-shrink:calc(1-var(--stack-scale))] [--stack-step:0.05]",
136
+ // Transitions.
137
+ "data-ending-style:transform-(--closed-transform) data-ending-style:opacity-[0.9999] data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-nested-drawer-swiping:duration-0 data-ending-style:data-nested-drawer-swiping:duration-[calc(var(--drawer-swipe-strength)*400ms)] data-starting-style:transform-(--closed-transform) data-swiping:duration-0 data-ending-style:data-swiping:duration-[calc(var(--drawer-swipe-strength)*400ms)]",
138
+ // Axis: y.
139
+ "data-[swipe-axis=y]:inset-x-0 data-[swipe-axis=y]:data-nested-drawer-open:h-(--stack-height)",
140
+ // Axis: x.
141
+ "data-[swipe-axis=x]:inset-y-0 data-[swipe-axis=x]:flex-row",
142
+ // Direction: down.
143
+ "data-[swipe-direction=down]:bottom-0 data-[swipe-direction=down]:origin-bottom data-[swipe-direction=down]:[--closed-transform:translate3d(0,calc(100%+var(--drawer-inset,0px)+2px),0)] data-[swipe-direction=down]:[--translate-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y)-var(--stack-peek-offset)-(var(--stack-shrink)*var(--stack-height)))]",
144
+ // Direction: up.
145
+ "data-[swipe-direction=up]:top-0 data-[swipe-direction=up]:origin-top data-[swipe-direction=up]:[--closed-transform:translate3d(0,calc(-100%-var(--drawer-inset,0px)-2px),0)] data-[swipe-direction=up]:[--translate-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y)+var(--stack-peek-offset)+(var(--stack-shrink)*var(--stack-height)))]",
146
+ // Direction: left.
147
+ "data-[swipe-direction=left]:left-0 data-[swipe-direction=left]:origin-left data-[swipe-direction=left]:[--closed-transform:translate3d(calc(-100%-var(--drawer-inset,0px)-2px),0,0)] data-[swipe-direction=left]:[--translate-x:calc(var(--drawer-swipe-movement-x)+var(--stack-peek-offset)+(var(--stack-shrink)*100%))]",
148
+ // Direction: right.
149
+ "data-[swipe-direction=right]:right-0 data-[swipe-direction=right]:origin-right data-[swipe-direction=right]:[--closed-transform:translate3d(calc(100%+var(--drawer-inset,0px)+2px),0,0)] data-[swipe-direction=right]:[--translate-x:calc(var(--drawer-swipe-movement-x)-var(--stack-peek-offset)-(var(--stack-shrink)*100%))]",
150
+ className
151
+ )}
152
+ {...props}
153
+ >
154
+ {showSwipeHandle && <DrawerSwipeHandle />}
155
+ <DrawerPrimitive.Content
156
+ data-slot="drawer-content"
157
+ className={cn(
158
+ "flex min-h-0 flex-1 flex-col overflow-hidden overscroll-contain rounded-[inherit] transition-opacity duration-300 ease-[cubic-bezier(0.45,1.005,0,1.005)] select-text group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100 group-data-swiping/drawer-popup:select-none"
159
+ )}
160
+ >
161
+ {children}
162
+ </DrawerPrimitive.Content>
163
+ </DrawerPrimitive.Popup>
164
+ </DrawerPrimitive.Viewport>
165
+ </DrawerPortal>
166
+ )
167
+ }
168
+
169
+ function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
170
+ return (
171
+ <div
172
+ data-slot="drawer-header"
173
+ className={cn(
174
+ "flex shrink-0 flex-col gap-0.5 p-4 pb-0 group-data-[swipe-axis=y]/drawer-popup:text-center md:gap-0.5 md:text-left",
175
+ className
176
+ )}
177
+ {...props}
178
+ />
179
+ )
180
+ }
181
+
182
+ function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
183
+ return (
184
+ <div
185
+ data-slot="drawer-footer"
186
+ className={cn("mt-auto flex shrink-0 flex-col gap-2 p-4 pt-0", className)}
187
+ {...props}
188
+ />
189
+ )
190
+ }
191
+
192
+ function DrawerTitle({ className, ...props }: DrawerPrimitive.Title.Props) {
193
+ return (
194
+ <DrawerPrimitive.Title
195
+ data-slot="drawer-title"
196
+ className={cn(
197
+ "text-base font-medium text-foreground",
198
+ className
199
+ )}
200
+ {...props}
201
+ />
202
+ )
203
+ }
204
+
205
+ function DrawerDescription({
206
+ className,
207
+ ...props
208
+ }: DrawerPrimitive.Description.Props) {
209
+ return (
210
+ <DrawerPrimitive.Description
211
+ data-slot="drawer-description"
212
+ className={cn("text-sm text-balance text-muted-foreground", className)}
213
+ {...props}
214
+ />
215
+ )
216
+ }
217
+
218
+ export {
219
+ Drawer,
220
+ DrawerPortal,
221
+ DrawerOverlay,
222
+ DrawerSwipeHandle,
223
+ DrawerTrigger,
224
+ DrawerClose,
225
+ DrawerContent,
226
+ DrawerHeader,
227
+ DrawerFooter,
228
+ DrawerTitle,
229
+ DrawerDescription,
230
+ }
@@ -0,0 +1,278 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu"
5
+
6
+ import { cn } from "../lib/utils"
7
+ import { ScrollArea } from "./scroll-area"
8
+ import { ChevronRightIcon, CheckIcon } from "lucide-react"
9
+
10
+ function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
11
+ return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
12
+ }
13
+
14
+ function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
15
+ return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
16
+ }
17
+
18
+ function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
19
+ return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
20
+ }
21
+
22
+ function DropdownMenuContent({
23
+ align = "start",
24
+ alignOffset = 0,
25
+ side = "bottom",
26
+ sideOffset = 4,
27
+ className,
28
+ children,
29
+ ...props
30
+ }: MenuPrimitive.Popup.Props &
31
+ Pick<
32
+ MenuPrimitive.Positioner.Props,
33
+ "align" | "alignOffset" | "side" | "sideOffset"
34
+ >) {
35
+ return (
36
+ <MenuPrimitive.Portal>
37
+ <MenuPrimitive.Positioner
38
+ className="isolate z-50 outline-none"
39
+ align={align}
40
+ alignOffset={alignOffset}
41
+ side={side}
42
+ sideOffset={sideOffset}
43
+ >
44
+ <MenuPrimitive.Popup
45
+ data-koharu-ui-layer=""
46
+ data-slot="dropdown-menu-content"
47
+ className={cn("z-50 w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className )}
48
+ {...props}
49
+ >
50
+ <ScrollArea
51
+ className="max-h-(--available-height) w-full"
52
+ viewportClassName="max-h-(--available-height)"
53
+ >
54
+ {children}
55
+ </ScrollArea>
56
+ </MenuPrimitive.Popup>
57
+ </MenuPrimitive.Positioner>
58
+ </MenuPrimitive.Portal>
59
+ )
60
+ }
61
+
62
+ function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
63
+ return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
64
+ }
65
+
66
+ function DropdownMenuLabel({
67
+ className,
68
+ inset,
69
+ ...props
70
+ }: MenuPrimitive.GroupLabel.Props & {
71
+ inset?: boolean
72
+ }) {
73
+ return (
74
+ <MenuPrimitive.GroupLabel
75
+ data-slot="dropdown-menu-label"
76
+ data-inset={inset}
77
+ className={cn(
78
+ "px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
79
+ className
80
+ )}
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ function DropdownMenuItem({
87
+ className,
88
+ inset,
89
+ variant = "default",
90
+ ...props
91
+ }: MenuPrimitive.Item.Props & {
92
+ inset?: boolean
93
+ variant?: "default" | "destructive"
94
+ }) {
95
+ return (
96
+ <MenuPrimitive.Item
97
+ data-slot="dropdown-menu-item"
98
+ data-inset={inset}
99
+ data-variant={variant}
100
+ className={cn(
101
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 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 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
102
+ className
103
+ )}
104
+ {...props}
105
+ />
106
+ )
107
+ }
108
+
109
+ function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
110
+ return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
111
+ }
112
+
113
+ function DropdownMenuSubTrigger({
114
+ className,
115
+ inset,
116
+ children,
117
+ ...props
118
+ }: MenuPrimitive.SubmenuTrigger.Props & {
119
+ inset?: boolean
120
+ }) {
121
+ return (
122
+ <MenuPrimitive.SubmenuTrigger
123
+ data-slot="dropdown-menu-sub-trigger"
124
+ data-inset={inset}
125
+ className={cn(
126
+ "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
127
+ className
128
+ )}
129
+ {...props}
130
+ >
131
+ {children}
132
+ <ChevronRightIcon className="ml-auto" />
133
+ </MenuPrimitive.SubmenuTrigger>
134
+ )
135
+ }
136
+
137
+ function DropdownMenuSubContent({
138
+ align = "start",
139
+ alignOffset = -3,
140
+ side = "right",
141
+ sideOffset = 0,
142
+ className,
143
+ ...props
144
+ }: React.ComponentProps<typeof DropdownMenuContent>) {
145
+ return (
146
+ <DropdownMenuContent
147
+ data-slot="dropdown-menu-sub-content"
148
+ className={cn("w-auto min-w-[96px] rounded-lg bg-popover text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 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-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", className )}
149
+ align={align}
150
+ alignOffset={alignOffset}
151
+ side={side}
152
+ sideOffset={sideOffset}
153
+ {...props}
154
+ />
155
+ )
156
+ }
157
+
158
+ function DropdownMenuCheckboxItem({
159
+ className,
160
+ children,
161
+ checked,
162
+ inset,
163
+ ...props
164
+ }: MenuPrimitive.CheckboxItem.Props & {
165
+ inset?: boolean
166
+ }) {
167
+ return (
168
+ <MenuPrimitive.CheckboxItem
169
+ data-slot="dropdown-menu-checkbox-item"
170
+ data-inset={inset}
171
+ className={cn(
172
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 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
+ checked={checked}
176
+ {...props}
177
+ >
178
+ <span
179
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
180
+ data-slot="dropdown-menu-checkbox-item-indicator"
181
+ >
182
+ <MenuPrimitive.CheckboxItemIndicator>
183
+ <CheckIcon
184
+ />
185
+ </MenuPrimitive.CheckboxItemIndicator>
186
+ </span>
187
+ {children}
188
+ </MenuPrimitive.CheckboxItem>
189
+ )
190
+ }
191
+
192
+ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
193
+ return (
194
+ <MenuPrimitive.RadioGroup
195
+ data-slot="dropdown-menu-radio-group"
196
+ {...props}
197
+ />
198
+ )
199
+ }
200
+
201
+ function DropdownMenuRadioItem({
202
+ className,
203
+ children,
204
+ inset,
205
+ ...props
206
+ }: MenuPrimitive.RadioItem.Props & {
207
+ inset?: boolean
208
+ }) {
209
+ return (
210
+ <MenuPrimitive.RadioItem
211
+ data-slot="dropdown-menu-radio-item"
212
+ data-inset={inset}
213
+ className={cn(
214
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
215
+ className
216
+ )}
217
+ {...props}
218
+ >
219
+ <span
220
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
221
+ data-slot="dropdown-menu-radio-item-indicator"
222
+ >
223
+ <MenuPrimitive.RadioItemIndicator>
224
+ <CheckIcon
225
+ />
226
+ </MenuPrimitive.RadioItemIndicator>
227
+ </span>
228
+ {children}
229
+ </MenuPrimitive.RadioItem>
230
+ )
231
+ }
232
+
233
+ function DropdownMenuSeparator({
234
+ className,
235
+ ...props
236
+ }: MenuPrimitive.Separator.Props) {
237
+ return (
238
+ <MenuPrimitive.Separator
239
+ data-slot="dropdown-menu-separator"
240
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
241
+ {...props}
242
+ />
243
+ )
244
+ }
245
+
246
+ function DropdownMenuShortcut({
247
+ className,
248
+ ...props
249
+ }: React.ComponentProps<"span">) {
250
+ return (
251
+ <span
252
+ data-slot="dropdown-menu-shortcut"
253
+ className={cn(
254
+ "ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
255
+ className
256
+ )}
257
+ {...props}
258
+ />
259
+ )
260
+ }
261
+
262
+ export {
263
+ DropdownMenu,
264
+ DropdownMenuPortal,
265
+ DropdownMenuTrigger,
266
+ DropdownMenuContent,
267
+ DropdownMenuGroup,
268
+ DropdownMenuLabel,
269
+ DropdownMenuItem,
270
+ DropdownMenuCheckboxItem,
271
+ DropdownMenuRadioGroup,
272
+ DropdownMenuRadioItem,
273
+ DropdownMenuSeparator,
274
+ DropdownMenuShortcut,
275
+ DropdownMenuSub,
276
+ DropdownMenuSubTrigger,
277
+ DropdownMenuSubContent,
278
+ }