diff --git a/src/lib/components/ui/avatar/avatar-fallback.svelte b/src/lib/components/ui/avatar/avatar-fallback.svelte
new file mode 100644
index 0000000..3b9b24c
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar-fallback.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/avatar/avatar-image.svelte b/src/lib/components/ui/avatar/avatar-image.svelte
new file mode 100644
index 0000000..27b0e7d
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar-image.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/src/lib/components/ui/avatar/avatar.svelte b/src/lib/components/ui/avatar/avatar.svelte
new file mode 100644
index 0000000..b8a6ee1
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/avatar/index.ts b/src/lib/components/ui/avatar/index.ts
new file mode 100644
index 0000000..b08c780
--- /dev/null
+++ b/src/lib/components/ui/avatar/index.ts
@@ -0,0 +1,13 @@
+import Root from "./avatar.svelte";
+import Image from "./avatar-image.svelte";
+import Fallback from "./avatar-fallback.svelte";
+
+export {
+ Root,
+ Image,
+ Fallback,
+ //
+ Root as Avatar,
+ Image as AvatarImage,
+ Fallback as AvatarFallback
+};
diff --git a/src/lib/components/ui/button/button.svelte b/src/lib/components/ui/button/button.svelte
new file mode 100644
index 0000000..a128f14
--- /dev/null
+++ b/src/lib/components/ui/button/button.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/button/index.ts b/src/lib/components/ui/button/index.ts
new file mode 100644
index 0000000..9787e3e
--- /dev/null
+++ b/src/lib/components/ui/button/index.ts
@@ -0,0 +1,52 @@
+import type { Button as ButtonPrimitive } from "bits-ui";
+import { tv, type VariantProps } from "tailwind-variants";
+import Root from "./button.svelte";
+
+const buttonVariants = tv({
+ base: "inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline"
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9"
+ }
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default"
+ }
+});
+
+type Variant = VariantProps["variant"];
+type Size = VariantProps["size"];
+
+type Props = ButtonPrimitive.Props & {
+ variant?: Variant;
+ size?: Size;
+};
+
+type Events = ButtonPrimitive.Events;
+
+export {
+ Root,
+ type Props,
+ type Events,
+ //
+ Root as Button,
+ type Props as ButtonProps,
+ type Events as ButtonEvents,
+ buttonVariants
+};
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
new file mode 100644
index 0000000..727dbd5
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
new file mode 100644
index 0000000..3d09fe9
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
new file mode 100644
index 0000000..aaf9d71
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
new file mode 100644
index 0000000..a7fb3c4
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
new file mode 100644
index 0000000..1c74ae1
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
new file mode 100644
index 0000000..e518364
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
new file mode 100644
index 0000000..775f8fe
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte
@@ -0,0 +1,14 @@
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
new file mode 100644
index 0000000..caf24ad
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
new file mode 100644
index 0000000..eec9bfa
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
new file mode 100644
index 0000000..d3a5ad3
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
+
diff --git a/src/lib/components/ui/dropdown-menu/index.ts b/src/lib/components/ui/dropdown-menu/index.ts
new file mode 100644
index 0000000..313213a
--- /dev/null
+++ b/src/lib/components/ui/dropdown-menu/index.ts
@@ -0,0 +1,48 @@
+import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
+import Item from "./dropdown-menu-item.svelte";
+import Label from "./dropdown-menu-label.svelte";
+import Content from "./dropdown-menu-content.svelte";
+import Shortcut from "./dropdown-menu-shortcut.svelte";
+import RadioItem from "./dropdown-menu-radio-item.svelte";
+import Separator from "./dropdown-menu-separator.svelte";
+import RadioGroup from "./dropdown-menu-radio-group.svelte";
+import SubContent from "./dropdown-menu-sub-content.svelte";
+import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
+import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
+
+const Sub = DropdownMenuPrimitive.Sub;
+const Root = DropdownMenuPrimitive.Root;
+const Trigger = DropdownMenuPrimitive.Trigger;
+const Group = DropdownMenuPrimitive.Group;
+
+export {
+ Sub,
+ Root,
+ Item,
+ Label,
+ Group,
+ Trigger,
+ Content,
+ Shortcut,
+ Separator,
+ RadioItem,
+ SubContent,
+ SubTrigger,
+ RadioGroup,
+ CheckboxItem,
+ //
+ Root as DropdownMenu,
+ Sub as DropdownMenuSub,
+ Item as DropdownMenuItem,
+ Label as DropdownMenuLabel,
+ Group as DropdownMenuGroup,
+ Content as DropdownMenuContent,
+ Trigger as DropdownMenuTrigger,
+ Shortcut as DropdownMenuShortcut,
+ RadioItem as DropdownMenuRadioItem,
+ Separator as DropdownMenuSeparator,
+ RadioGroup as DropdownMenuRadioGroup,
+ SubContent as DropdownMenuSubContent,
+ SubTrigger as DropdownMenuSubTrigger,
+ CheckboxItem as DropdownMenuCheckboxItem
+};
diff --git a/src/lib/components/ui/menubar/index.ts b/src/lib/components/ui/menubar/index.ts
new file mode 100644
index 0000000..733ab08
--- /dev/null
+++ b/src/lib/components/ui/menubar/index.ts
@@ -0,0 +1,52 @@
+import { Menubar as MenubarPrimitive } from "bits-ui";
+
+import Root from "./menubar.svelte";
+import CheckboxItem from "./menubar-checkbox-item.svelte";
+import Content from "./menubar-content.svelte";
+import Item from "./menubar-item.svelte";
+import Label from "./menubar-label.svelte";
+import RadioItem from "./menubar-radio-item.svelte";
+import Separator from "./menubar-separator.svelte";
+import Shortcut from "./menubar-shortcut.svelte";
+import SubContent from "./menubar-sub-content.svelte";
+import SubTrigger from "./menubar-sub-trigger.svelte";
+import Trigger from "./menubar-trigger.svelte";
+
+const Menu = MenubarPrimitive.Menu;
+const Group = MenubarPrimitive.Group;
+const Sub = MenubarPrimitive.Sub;
+const RadioGroup = MenubarPrimitive.RadioGroup;
+
+export {
+ Root,
+ CheckboxItem,
+ Content,
+ Item,
+ Label,
+ RadioItem,
+ Separator,
+ Shortcut,
+ SubContent,
+ SubTrigger,
+ Trigger,
+ Menu,
+ Group,
+ Sub,
+ RadioGroup,
+ //
+ Root as Menubar,
+ CheckboxItem as MenubarCheckboxItem,
+ Content as MenubarContent,
+ Item as MenubarItem,
+ Label as MenubarLabel,
+ RadioItem as MenubarRadioItem,
+ Separator as MenubarSeparator,
+ Shortcut as MenubarShortcut,
+ SubContent as MenubarSubContent,
+ SubTrigger as MenubarSubTrigger,
+ Trigger as MenubarTrigger,
+ Menu as MenubarMenu,
+ Group as MenubarGroup,
+ Sub as MenubarSub,
+ RadioGroup as MenubarRadioGroup
+};
diff --git a/src/lib/components/ui/menubar/menubar-checkbox-item.svelte b/src/lib/components/ui/menubar/menubar-checkbox-item.svelte
new file mode 100644
index 0000000..a98439a
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-checkbox-item.svelte
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-content.svelte b/src/lib/components/ui/menubar/menubar-content.svelte
new file mode 100644
index 0000000..20f61c7
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-content.svelte
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-item.svelte b/src/lib/components/ui/menubar/menubar-item.svelte
new file mode 100644
index 0000000..53af5c1
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-item.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-label.svelte b/src/lib/components/ui/menubar/menubar-label.svelte
new file mode 100644
index 0000000..710619f
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-label.svelte
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-radio-item.svelte b/src/lib/components/ui/menubar/menubar-radio-item.svelte
new file mode 100644
index 0000000..bf1e2d3
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-radio-item.svelte
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-separator.svelte b/src/lib/components/ui/menubar/menubar-separator.svelte
new file mode 100644
index 0000000..90851d6
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-separator.svelte
@@ -0,0 +1,13 @@
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-shortcut.svelte b/src/lib/components/ui/menubar/menubar-shortcut.svelte
new file mode 100644
index 0000000..82309a0
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-shortcut.svelte
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-sub-content.svelte b/src/lib/components/ui/menubar/menubar-sub-content.svelte
new file mode 100644
index 0000000..287b756
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-sub-content.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-sub-trigger.svelte b/src/lib/components/ui/menubar/menubar-sub-trigger.svelte
new file mode 100644
index 0000000..329ee24
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-sub-trigger.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar-trigger.svelte b/src/lib/components/ui/menubar/menubar-trigger.svelte
new file mode 100644
index 0000000..c6dfe97
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar-trigger.svelte
@@ -0,0 +1,23 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/menubar/menubar.svelte b/src/lib/components/ui/menubar/menubar.svelte
new file mode 100644
index 0000000..4ed491a
--- /dev/null
+++ b/src/lib/components/ui/menubar/menubar.svelte
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/separator/index.ts b/src/lib/components/ui/separator/index.ts
new file mode 100644
index 0000000..dbfb139
--- /dev/null
+++ b/src/lib/components/ui/separator/index.ts
@@ -0,0 +1,7 @@
+import Root from "./separator.svelte";
+
+export {
+ Root,
+ //
+ Root as Separator
+};
diff --git a/src/lib/components/ui/separator/separator.svelte b/src/lib/components/ui/separator/separator.svelte
new file mode 100644
index 0000000..bf9127a
--- /dev/null
+++ b/src/lib/components/ui/separator/separator.svelte
@@ -0,0 +1,22 @@
+
+
+
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..230a1fb
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,62 @@
+import { type ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
+import { cubicOut } from "svelte/easing";
+import type { TransitionConfig } from "svelte/transition";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
+
+type FlyAndScaleParams = {
+ y?: number;
+ x?: number;
+ start?: number;
+ duration?: number;
+};
+
+export const flyAndScale = (
+ node: Element,
+ params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
+): TransitionConfig => {
+ const style = getComputedStyle(node);
+ const transform = style.transform === "none" ? "" : style.transform;
+
+ const scaleConversion = (
+ valueA: number,
+ scaleA: [number, number],
+ scaleB: [number, number]
+ ) => {
+ const [minA, maxA] = scaleA;
+ const [minB, maxB] = scaleB;
+
+ const percentage = (valueA - minA) / (maxA - minA);
+ const valueB = percentage * (maxB - minB) + minB;
+
+ return valueB;
+ };
+
+ const styleToString = (
+ style: Record
+ ): string => {
+ return Object.keys(style).reduce((str, key) => {
+ if (style[key] === undefined) return str;
+ return str + `${key}:${style[key]};`;
+ }, "");
+ };
+
+ return {
+ duration: params.duration ?? 200,
+ delay: 0,
+ css: (t) => {
+ const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
+ const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
+ const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
+
+ return styleToString({
+ transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
+ opacity: t
+ });
+ },
+ easing: cubicOut
+ };
+};
\ No newline at end of file