aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorKinfe Michael Tariku <[email protected]>2024-06-25 19:56:54 +0300
committerGitHub <[email protected]>2024-06-25 19:56:54 +0300
commitf46e42c2dfd1b223d4ad701a86d05fc0bb380e45 (patch)
treef17fdfadf3bec08eee7f02da33af952796657254 /packages
parentfix: import using absolute path (diff)
parentdev and prod databases (diff)
downloadsupermemory-f46e42c2dfd1b223d4ad701a86d05fc0bb380e45.tar.xz
supermemory-f46e42c2dfd1b223d4ad701a86d05fc0bb380e45.zip
Merge branch 'main' into feat/landing_revamp
Diffstat (limited to 'packages')
-rw-r--r--packages/eslint-config/package.json4
-rw-r--r--packages/shared-types/index.ts76
-rw-r--r--packages/shared-types/package.json4
-rw-r--r--packages/tailwind-config/globals.css185
-rw-r--r--packages/tailwind-config/tailwind.config.ts6
-rw-r--r--packages/ui/components/QueryInput.tsx60
-rw-r--r--packages/ui/components/canvas/components/canvas.tsx94
-rw-r--r--packages/ui/components/canvas/components/draggableComponent.tsx71
-rw-r--r--packages/ui/components/canvas/components/dropComponent.tsx203
-rw-r--r--packages/ui/components/canvas/components/enabledComp copy.tsx22
-rw-r--r--packages/ui/components/canvas/components/enabledComp.tsx22
-rw-r--r--packages/ui/components/canvas/components/savesnap.tsx43
-rw-r--r--packages/ui/components/canvas/components/textCard.tsx45
-rw-r--r--packages/ui/components/canvas/components/twitterCard.tsx84
-rw-r--r--packages/ui/components/canvas/lib/context.ts18
-rw-r--r--packages/ui/components/canvas/lib/createAssetUrl.ts94
-rw-r--r--packages/ui/components/canvas/lib/createEmbeds.ts236
-rw-r--r--packages/ui/components/canvas/lib/loadSnap.ts14
-rw-r--r--packages/ui/icons/add.svg2
-rw-r--r--packages/ui/icons/autocomplete.svg15
-rw-r--r--packages/ui/icons/block.svg15
-rw-r--r--packages/ui/icons/canvas.svg3
-rw-r--r--packages/ui/icons/chat.svg2
-rw-r--r--packages/ui/icons/drag.svg14
-rw-r--r--packages/ui/icons/explore.svg2
-rw-r--r--packages/ui/icons/history.svg2
-rw-r--r--packages/ui/icons/index.ts20
-rw-r--r--packages/ui/icons/link.svg15
-rw-r--r--packages/ui/icons/memories.svg2
-rw-r--r--packages/ui/icons/nextarrow.svg3
-rw-r--r--packages/ui/icons/search.svg3
-rw-r--r--packages/ui/icons/settings.svg14
-rw-r--r--packages/ui/icons/url.svg4
-rw-r--r--packages/ui/shadcn/accordion.tsx55
-rw-r--r--packages/ui/shadcn/combobox.tsx2
-rw-r--r--packages/ui/shadcn/select.tsx160
-rw-r--r--packages/ui/shadcn/separator.tsx21
-rw-r--r--packages/ui/shadcn/sonner.tsx31
-rw-r--r--packages/ui/shadcn/switch.tsx29
-rw-r--r--packages/ui/shadcn/textarea.tsx24
-rw-r--r--packages/ui/shadcn/use-toast.ts2
41 files changed, 1679 insertions, 42 deletions
diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json
index 5c51c03d..cca76c17 100644
--- a/packages/eslint-config/package.json
+++ b/packages/eslint-config/package.json
@@ -12,8 +12,8 @@
"eslint-config-turbo": "^1.12.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-only-warn": "^1.1.0",
- "@typescript-eslint/parser": "^7.1.0",
- "@typescript-eslint/eslint-plugin": "^7.1.0",
+ "@typescript-eslint/parser": "^v6.21.0",
+ "@typescript-eslint/eslint-plugin": "^v6.21.0",
"typescript": "^5.3.3"
}
}
diff --git a/packages/shared-types/index.ts b/packages/shared-types/index.ts
index bf4a56da..9d5caa40 100644
--- a/packages/shared-types/index.ts
+++ b/packages/shared-types/index.ts
@@ -1,7 +1,69 @@
-export type ChatHistory = {
- question: string;
- answer: {
- parts: { text: string }[];
- sources: { isNote: boolean; source: string }[];
- };
-};
+import { z } from "zod";
+
+export const SourceZod = z.object({
+ type: z.string(),
+ source: z.string(),
+ title: z.string(),
+ content: z.string(),
+ numChunks: z.number().optional().default(1),
+});
+
+export type Source = z.infer<typeof SourceZod>;
+
+export const ChatHistoryZod = z.object({
+ question: z.string(),
+ answer: z.object({
+ parts: z.array(z.object({ text: z.string().optional() })),
+ sources: z.array(SourceZod),
+ justification: z.string().optional(),
+ }),
+});
+
+export type ChatHistory = z.infer<typeof ChatHistoryZod>;
+
+export const ModelCompatibleChatHistoryZod = z.array(
+ z.object({
+ role: z.union([
+ z.literal("user"),
+ z.literal("assistant"),
+ z.literal("system"),
+ ]),
+ content: z.string(),
+ }),
+);
+
+export type ModelCompatibleChatHistory = z.infer<
+ typeof ModelCompatibleChatHistoryZod
+>;
+
+export function convertChatHistoryList(
+ chatHistoryList: ChatHistory[],
+): ModelCompatibleChatHistory {
+ let convertedChats: ModelCompatibleChatHistory = [];
+
+ chatHistoryList.forEach((chat) => {
+ convertedChats.push(
+ {
+ role: "user",
+ content: chat.question,
+ },
+ {
+ role: "assistant",
+ content: chat.answer.parts.map((part) => part.text).join(" "),
+ },
+ );
+ });
+
+ // THE LAST ASSISTANT CONTENT WILL ALWAYS BE EMPTY, so we remove it
+ convertedChats.pop();
+
+ return convertedChats;
+}
+
+export const sourcesZod = z.object({
+ ids: z.array(z.string()),
+ metadata: z.array(z.any()),
+ normalizedData: z.array(z.any()).optional(),
+});
+
+export type SourcesFromApi = z.infer<typeof sourcesZod>;
diff --git a/packages/shared-types/package.json b/packages/shared-types/package.json
index 18e6049e..8b9e0110 100644
--- a/packages/shared-types/package.json
+++ b/packages/shared-types/package.json
@@ -1,3 +1,5 @@
{
- "name": "@repo/shared-types"
+ "name": "@repo/shared-types",
+ "version": "0.0.0",
+ "private": true
}
diff --git a/packages/tailwind-config/globals.css b/packages/tailwind-config/globals.css
index 18017f73..d09b120b 100644
--- a/packages/tailwind-config/globals.css
+++ b/packages/tailwind-config/globals.css
@@ -2,39 +2,37 @@
@tailwind components;
@tailwind utilities;
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground: rgba(179, 188, 197, 1);
- --foreground-menu: rgba(106, 115, 125, 1);
- --background: rgba(23, 27, 31, 1);
- --secondary: rgba(31, 36, 40, 1);
- --primary: rgba(54, 157, 253, 1);
- --border: rgba(51, 57, 67, 1);
+:root {
+ --foreground: rgba(179, 188, 197, 1);
+ --foreground-menu: rgba(106, 115, 125, 1);
+ --background: rgba(23, 27, 31, 1);
+ --secondary: rgba(31, 36, 40, 1);
+ --primary: rgba(54, 157, 253, 1);
+ --border: rgba(51, 57, 67, 1);
- --card: 0 0% 100%;
- --card-foreground: 0 0% 3.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 0 0% 3.9%;
- --popover: 0 0% 100%;
- --popover-foreground: 0 0% 3.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 0 0% 3.9%;
- --primary-foreground: 0 0% 98%;
+ --primary-foreground: 0 0% 98%;
- --secondary-foreground: 0 0% 9%;
+ --secondary-foreground: 0 0% 9%;
- --muted: 0 0% 96.1%;
- --muted-foreground: 0 0% 45.1%;
+ --muted: 0 0% 96.1%;
+ --muted-foreground: 0 0% 45.1%;
- --accent: 0 0% 96.1%;
- --accent-foreground: 0 0% 9%;
+ --accent: 0 0% 96.1%;
+ --accent-foreground: 0 0% 9%;
- --destructive: 0 84.2% 60.2%;
- --destructive-foreground: 0 0% 98%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
- --input: 0 0% 89.8%;
- --ring: 0 0% 3.9%;
+ --input: 0 0% 89.8%;
+ --ring: 0 0% 3.9%;
- --radius: 0.5rem;
- }
+ --radius: 0.5rem;
}
body {
@@ -49,6 +47,42 @@ body {
align-items: center;
justify-content: center;
}
+
+ .markdown table {
+ --tw-border-spacing-x: 0px;
+ --tw-border-spacing-y: 0px;
+ border-collapse: separate;
+ border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y);
+ width: 100%;
+ }
+ .markdown th {
+ background-color: rgba(236, 236, 241, 0.2);
+ border-bottom-width: 1px;
+ border-left-width: 1px;
+ border-top-width: 1px;
+ padding: 0.25rem 0.75rem;
+ }
+ .markdown th:first-child {
+ border-top-left-radius: 0.375rem;
+ }
+ .markdown th:last-child {
+ border-right-width: 1px;
+ border-top-right-radius: 0.375rem;
+ }
+ .markdown td {
+ border-bottom-width: 1px;
+ border-left-width: 1px;
+ padding: 0.25rem 0.75rem;
+ }
+ .markdown td:last-child {
+ border-right-width: 1px;
+ }
+ .markdown tbody tr:last-child td:first-child {
+ border-bottom-left-radius: 0.375rem;
+ }
+ .markdown tbody tr:last-child td:last-child {
+ border-bottom-right-radius: 0.375rem;
+ }
}
@layer utilities {
@@ -57,6 +91,34 @@ body {
}
}
+@layer components {
+ .markdown ol,
+ .markdown ul {
+ display: flex;
+ flex-direction: column;
+ padding-left: 1rem;
+ }
+
+ .markdown ol li,
+ .markdown ol li > p,
+ .markdown ol ol,
+ .markdown ol ul,
+ .markdown ul li,
+ .markdown ul li > p,
+ .markdown ul ol,
+ .markdown ul ul {
+ margin: 0;
+ }
+
+ .markdown ul li:before {
+ content: "•";
+ font-size: 0.875rem;
+ line-height: 1.25rem;
+ margin-left: -1rem;
+ position: absolute;
+ }
+}
+
.gradient-background {
background: linear-gradient(
150deg,
@@ -84,3 +146,78 @@ body {
::-webkit-scrollbar-thumb:hover {
background: #22303d;
}
+
+.no-scrollbar {
+ /* For WebKit (Safari, Chrome, etc.) */
+ &::-webkit-scrollbar {
+ display: none;
+ }
+
+ /* For Firefox */
+ scrollbar-width: none;
+
+ /* For IE and Edge */
+ -ms-overflow-style: none;
+}
+
+:not(pre) > code.hljs,
+:not(pre) > code[class*="language-"] {
+ border-radius: 0.3em;
+ white-space: normal;
+}
+.hljs-comment {
+ color: hsla(0, 0%, 100%, 0.5);
+}
+.hljs-meta {
+ color: hsla(0, 0%, 100%, 0.6);
+}
+.hljs-built_in,
+.hljs-class .hljs-title {
+ color: #e9950c;
+}
+.hljs-doctag,
+.hljs-formula,
+.hljs-keyword,
+.hljs-literal {
+ color: #2e95d3;
+}
+.hljs-addition,
+.hljs-attribute,
+.hljs-meta-string,
+.hljs-regexp,
+.hljs-string {
+ color: #00a67d;
+}
+.hljs-attr,
+.hljs-number,
+.hljs-selector-attr,
+.hljs-selector-class,
+.hljs-selector-pseudo,
+.hljs-template-variable,
+.hljs-type,
+.hljs-variable {
+ color: #df3079;
+}
+.hljs-bullet,
+.hljs-link,
+.hljs-selector-id,
+.hljs-symbol,
+.hljs-title {
+ color: #f22c3d;
+}
+
+/* no scrollbar visibility */
+.no-scrollbar::-webkit-scrollbar {
+ width: 0px;
+}
+
+::-moz-selection {
+ /* Code for Firefox */
+ color: #369dfd;
+ background: #21303d;
+}
+
+::selection {
+ color: #369dfd;
+ background: #21303d;
+}
diff --git a/packages/tailwind-config/tailwind.config.ts b/packages/tailwind-config/tailwind.config.ts
index c321559b..bd3f26b1 100644
--- a/packages/tailwind-config/tailwind.config.ts
+++ b/packages/tailwind-config/tailwind.config.ts
@@ -111,7 +111,11 @@ const config = {
},
},
},
- plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
+ plugins: [
+ require("tailwindcss-animate"),
+ require("@tailwindcss/typography"),
+ require("tailwind-scrollbar"),
+ ],
} satisfies Config;
export default config;
diff --git a/packages/ui/components/QueryInput.tsx b/packages/ui/components/QueryInput.tsx
new file mode 100644
index 00000000..ba476dda
--- /dev/null
+++ b/packages/ui/components/QueryInput.tsx
@@ -0,0 +1,60 @@
+import React from "react";
+import Divider from "../shadcn/divider";
+import { ArrowRightIcon } from "../icons";
+import Image from "next/image";
+
+function QueryInput() {
+ return (
+ <div>
+ <div className="bg-secondary rounded-[20px] h-[68 px]">
+ {/* input and action button */}
+ <form className="flex gap-4 p-2.5">
+ <textarea
+ name="q"
+ cols={30}
+ rows={4}
+ className="bg-transparent h-12 focus:h-[128px] no-scrollbar pt-3 px-2 text-base placeholder:text-[#5D6165] text-[#9DA0A4] focus:text-white duration-200 tracking-[3%] outline-none resize-none w-full"
+ placeholder="Ask your second brain..."
+ // onKeyDown={(e) => {
+ // if (e.key === "Enter") {
+ // e.preventDefault();
+ // if (!e.shiftKey) push(parseQ());
+ // }
+ // }}
+ // onChange={(e) => setQ(e.target.value)}
+ // value={q}
+ // disabled={disabled}
+ />
+
+ <button
+ // type="submit"
+ // onClick={e => e.preventDefault()}
+ // disabled={disabled}
+ className="h-12 w-12 rounded-[14px] bg-[#21303D] all-center shrink-0 hover:brightness-125 duration-200 outline-none focus:outline focus:outline-primary active:scale-90"
+ >
+ <Image src={ArrowRightIcon} alt="Right arrow icon" />
+ </button>
+ </form>
+
+ {/* <Divider /> */}
+ </div>
+ {/* selected sources */}
+ {/* <div className="flex items-center gap-6 p-2 h-auto bg-secondary"> */}
+ {/* <MultipleSelector
+ key={options.length}
+ disabled={disabled}
+ defaultOptions={options}
+ onChange={(e) => setSelectedSpaces(e.map((x) => parseInt(x.value)))}
+ placeholder="Focus on specific spaces..."
+ emptyIndicator={
+ <p className="text-center text-lg leading-10 text-gray-600 dark:text-gray-400">
+ no results found.
+ </p>
+ }
+ /> */}
+ {/* </div> */}
+ </div>
+ );
+}
+
+export default QueryInput;
diff --git a/packages/ui/components/canvas/components/canvas.tsx b/packages/ui/components/canvas/components/canvas.tsx
new file mode 100644
index 00000000..57b63d49
--- /dev/null
+++ b/packages/ui/components/canvas/components/canvas.tsx
@@ -0,0 +1,94 @@
+import { useCallback, useEffect, useRef, useState } from "react";
+import { Editor, Tldraw, setUserPreferences, TLStoreWithStatus } from "tldraw";
+import { createAssetFromUrl } from "../lib/createAssetUrl";
+import "tldraw/tldraw.css";
+import { components } from "./enabledComp";
+import { twitterCardUtil } from "./twitterCard";
+import { textCardUtil } from "./textCard";
+import createEmbedsFromUrl from "../lib/createEmbeds";
+import { loadRemoteSnapshot } from "../lib/loadSnap";
+import { SaveStatus } from "./savesnap";
+import { getAssetUrls } from "@tldraw/assets/selfHosted";
+import { memo } from "react";
+import DragContext from "../lib/context";
+import DropZone from "./dropComponent";
+// import "./canvas.css";
+
+export const Canvas = memo(() => {
+ const [isDraggingOver, setIsDraggingOver] = useState<boolean>(false);
+ const Dragref = useRef<HTMLDivElement | null>(null);
+
+ const handleDragOver = (event: any) => {
+ event.preventDefault();
+ setIsDraggingOver(true);
+ console.log("entere");
+ };
+
+ useEffect(() => {
+ const divElement = Dragref.current;
+ if (divElement) {
+ divElement.addEventListener("dragover", handleDragOver);
+ }
+ return () => {
+ if (divElement) {
+ divElement.removeEventListener("dragover", handleDragOver);
+ }
+ };
+ }, []);
+
+ return (
+ <DragContext.Provider value={{ isDraggingOver, setIsDraggingOver }}>
+ <div ref={Dragref} className="w-full h-full">
+ <TldrawComponent />
+ </div>
+ </DragContext.Provider>
+ );
+});
+
+const TldrawComponent = memo(() => {
+ const [storeWithStatus, setStoreWithStatus] = useState<TLStoreWithStatus>({
+ status: "loading",
+ });
+ useEffect(() => {
+ const fetchStore = async () => {
+ const store = await loadRemoteSnapshot();
+
+ setStoreWithStatus({
+ store: store,
+ status: "not-synced",
+ });
+ };
+
+ fetchStore();
+ }, []);
+
+ const handleMount = useCallback((editor: Editor) => {
+ (window as any).app = editor;
+ (window as any).editor = editor;
+ editor.registerExternalAssetHandler("url", createAssetFromUrl);
+ editor.registerExternalContentHandler("url", ({ url, point, sources }) => {
+ createEmbedsFromUrl({ url, point, sources, editor });
+ });
+ }, []);
+
+ setUserPreferences({ id: "supermemory", isDarkMode: true });
+
+ const assetUrls = getAssetUrls();
+ return (
+ <div className="w-full h-full">
+ <Tldraw
+ className="relative"
+ assetUrls={assetUrls}
+ components={components}
+ store={storeWithStatus}
+ shapeUtils={[twitterCardUtil, textCardUtil]}
+ onMount={handleMount}
+ >
+ <div className="absolute left-1/2 top-0 z-[1000000] flex -translate-x-1/2 gap-2 bg-[#2C3439] text-[#B3BCC5]">
+ <SaveStatus />
+ </div>
+ <DropZone />
+ </Tldraw>
+ </div>
+ );
+});
diff --git a/packages/ui/components/canvas/components/draggableComponent.tsx b/packages/ui/components/canvas/components/draggableComponent.tsx
new file mode 100644
index 00000000..d0832e81
--- /dev/null
+++ b/packages/ui/components/canvas/components/draggableComponent.tsx
@@ -0,0 +1,71 @@
+import Image from "next/image";
+import { useRef, useState } from "react";
+
+interface DraggableComponentsProps {
+ content: string;
+ extraInfo?: string;
+ icon: string;
+ iconAlt: string;
+}
+
+export default function DraggableComponentsContainer({
+ content,
+}: {
+ content: DraggableComponentsProps[];
+}) {
+ return (
+ <div className="flex flex-col gap-10">
+ {content.map((i) => {
+ return (
+ <DraggableComponents
+ content={i.content}
+ icon={i.icon}
+ iconAlt={i.iconAlt}
+ extraInfo={i.extraInfo}
+ />
+ );
+ })}
+ </div>
+ );
+}
+
+function DraggableComponents({
+ content,
+ extraInfo,
+ icon,
+ iconAlt,
+}: DraggableComponentsProps) {
+ const [isDragging, setIsDragging] = useState(false);
+ const containerRef = useRef<HTMLDivElement>(null);
+
+ const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
+ setIsDragging(true);
+ if (containerRef.current) {
+ // Serialize the children as a string for dataTransfer
+ const childrenHtml = containerRef.current.innerHTML;
+ event.dataTransfer.setData("text/html", childrenHtml);
+ }
+ };
+
+ const handleDragEnd = () => {
+ setIsDragging(false);
+ };
+
+ return (
+ <div
+ ref={containerRef}
+ onDragEnd={handleDragEnd}
+ onDragStart={handleDragStart}
+ draggable
+ className={`flex gap-4 px-1 rounded-md text-[#989EA4] border-2 transition ${isDragging ? "border-blue-600" : "border-[#1F2428]"}`}
+ >
+ <Image className="select-none" src={icon} alt={iconAlt} />
+ <div className="flex flex-col gap-2">
+ <div>
+ <h1 className="line-clamp-3">{content}</h1>
+ </div>
+ <p className="line-clamp-1 text-[#369DFD]">{extraInfo}</p>
+ </div>
+ </div>
+ );
+}
diff --git a/packages/ui/components/canvas/components/dropComponent.tsx b/packages/ui/components/canvas/components/dropComponent.tsx
new file mode 100644
index 00000000..0374f367
--- /dev/null
+++ b/packages/ui/components/canvas/components/dropComponent.tsx
@@ -0,0 +1,203 @@
+import React, { useRef, useCallback, useEffect, useContext } from "react";
+import { useEditor } from "tldraw";
+import DragContext, { DragContextType, useDragContext } from "../lib/context";
+import { handleExternalDroppedContent } from "../lib/createEmbeds";
+
+const stripHtmlTags = (html: string): string => {
+ const div = document.createElement("div");
+ div.innerHTML = html;
+ return div.textContent || div.innerText || "";
+};
+
+function formatTextToRatio(text: string) {
+ const totalWidth = text.length;
+ const maxLineWidth = Math.floor(totalWidth / 4);
+
+ const words = text.split(" ");
+ let lines = [];
+ let currentLine = "";
+
+ words.forEach((word) => {
+ // Check if adding the next word exceeds the maximum line width
+ if ((currentLine + word).length <= maxLineWidth) {
+ currentLine += (currentLine ? " " : "") + word;
+ } else {
+ // If the current line is full, push it to new line
+ lines.push(currentLine);
+ currentLine = word;
+ }
+ });
+ if (currentLine) {
+ lines.push(currentLine);
+ }
+ return lines.join("\n");
+}
+
+function DropZone() {
+ const dropRef = useRef<HTMLDivElement | null>(null);
+ const { isDraggingOver, setIsDraggingOver } = useDragContext();
+
+ const editor = useEditor();
+
+ const handleDragLeave = () => {
+ setIsDraggingOver(false);
+ console.log("leaver");
+ };
+
+ useEffect(() => {
+ setInterval(() => {
+ editor.selectAll();
+ const shapes = editor.getSelectedShapes();
+ const text = shapes.filter((s) => s.type === "text");
+ console.log("hrhh", text);
+ }, 5000);
+ }, []);
+
+ const handleDrop = useCallback((event: DragEvent) => {
+ event.preventDefault();
+ setIsDraggingOver(false);
+ const dt = event.dataTransfer;
+ if (!dt) {
+ return;
+ }
+ const items = dt.items;
+
+ for (let i = 0; i < items.length; i++) {
+ if (items[i]!.kind === "file" && items[i]!.type.startsWith("image/")) {
+ const file = items[i]!.getAsFile();
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ if (e.target) {
+ // setDroppedImage(e.target.result as string);
+ }
+ };
+ reader.readAsDataURL(file);
+ }
+ } else if (items[i]!.kind === "string") {
+ items[i]!.getAsString((data) => {
+ const cleanText = stripHtmlTags(data);
+ const onethree = formatTextToRatio(cleanText);
+ handleExternalDroppedContent({ editor, text: onethree });
+ });
+ }
+ }
+ }, []);
+
+ useEffect(() => {
+ const divElement = dropRef.current;
+ if (divElement) {
+ divElement.addEventListener("drop", handleDrop);
+ divElement.addEventListener("dragleave", handleDragLeave);
+ }
+ return () => {
+ if (divElement) {
+ divElement.removeEventListener("drop", handleDrop);
+ divElement.addEventListener("dragleave", handleDragLeave);
+ }
+ };
+ }, []);
+
+ return (
+ <div
+ className={`h-full flex justify-center items-center w-full absolute top-0 left-0 z-[100000] pointer-events-none ${isDraggingOver && "bg-[#2c3439ad] pointer-events-auto"}`}
+ ref={dropRef}
+ >
+ {isDraggingOver && (
+ <>
+ <div className="absolute top-4 left-8">
+ <TopRight />
+ </div>
+ <div className="absolute top-4 right-8">
+ <TopLeft />
+ </div>
+ <div className="absolute bottom-4 left-8">
+ <BottomLeft />
+ </div>
+ <div className="absolute bottom-4 right-8">
+ <BottomRight />
+ </div>
+ <h2 className="text-2xl">Drop here to add Content on Canvas</h2>
+ </>
+ )}
+ </div>
+ );
+}
+
+function TopRight() {
+ return (
+ <svg
+ width="48"
+ height="48"
+ viewBox="0 0 48 48"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M44 4H12C7.58172 4 4 7.58172 4 12V44"
+ stroke="white"
+ stroke-width="8"
+ stroke-linecap="round"
+ />
+ </svg>
+ );
+}
+
+function TopLeft() {
+ return (
+ <svg
+ width="48"
+ height="48"
+ viewBox="0 0 48 48"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M4 4H36C40.4183 4 44 7.58172 44 12V44"
+ stroke="white"
+ stroke-width="8"
+ stroke-linecap="round"
+ />
+ </svg>
+ );
+}
+
+function BottomLeft() {
+ return (
+ <svg
+ width="48"
+ height="48"
+ viewBox="0 0 48 48"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M44 44H12C7.58172 44 4 40.4183 4 36V4"
+ stroke="white"
+ stroke-width="8"
+ stroke-linecap="round"
+ />
+ </svg>
+ );
+}
+
+function BottomRight() {
+ return (
+ <svg
+ width="48"
+ height="48"
+ viewBox="0 0 48 48"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M4 44H36C40.4183 44 44 40.4183 44 36V4"
+ stroke="white"
+ stroke-width="8"
+ stroke-linecap="round"
+ />
+ </svg>
+ );
+}
+
+export default DropZone;
diff --git a/packages/ui/components/canvas/components/enabledComp copy.tsx b/packages/ui/components/canvas/components/enabledComp copy.tsx
new file mode 100644
index 00000000..85811b82
--- /dev/null
+++ b/packages/ui/components/canvas/components/enabledComp copy.tsx
@@ -0,0 +1,22 @@
+import { TLUiComponents } from "tldraw";
+
+export const components: Partial<TLUiComponents> = {
+ ActionsMenu: null,
+ MainMenu: null,
+ QuickActions: null,
+ TopPanel: null,
+ DebugPanel: null,
+ DebugMenu: null,
+ PageMenu: null,
+ // Minimap: null,
+ // ContextMenu: null,
+ // HelpMenu: null,
+ // ZoomMenu: null,
+ // StylePanel: null,
+ // NavigationPanel: null,
+ // Toolbar: null,
+ // KeyboardShortcutsDialog: null,
+ // HelperButtons: null,
+ // SharePanel: null,
+ // MenuPanel: null,
+}; \ No newline at end of file
diff --git a/packages/ui/components/canvas/components/enabledComp.tsx b/packages/ui/components/canvas/components/enabledComp.tsx
new file mode 100644
index 00000000..85811b82
--- /dev/null
+++ b/packages/ui/components/canvas/components/enabledComp.tsx
@@ -0,0 +1,22 @@
+import { TLUiComponents } from "tldraw";
+
+export const components: Partial<TLUiComponents> = {
+ ActionsMenu: null,
+ MainMenu: null,
+ QuickActions: null,
+ TopPanel: null,
+ DebugPanel: null,
+ DebugMenu: null,
+ PageMenu: null,
+ // Minimap: null,
+ // ContextMenu: null,
+ // HelpMenu: null,
+ // ZoomMenu: null,
+ // StylePanel: null,
+ // NavigationPanel: null,
+ // Toolbar: null,
+ // KeyboardShortcutsDialog: null,
+ // HelperButtons: null,
+ // SharePanel: null,
+ // MenuPanel: null,
+}; \ No newline at end of file
diff --git a/packages/ui/components/canvas/components/savesnap.tsx b/packages/ui/components/canvas/components/savesnap.tsx
new file mode 100644
index 00000000..f82e97e3
--- /dev/null
+++ b/packages/ui/components/canvas/components/savesnap.tsx
@@ -0,0 +1,43 @@
+import { useCallback, useEffect, useState } from "react";
+import { debounce, useEditor } from "tldraw";
+
+export function SaveStatus() {
+ const [save, setSave] = useState("saved!");
+ const editor = useEditor();
+
+ const debouncedSave = useCallback(
+ debounce(async () => {
+ const snapshot = editor.store.getSnapshot();
+ localStorage.setItem("saved", JSON.stringify(snapshot));
+
+ const res = await fetch(
+ "https://learning-cf.pruthvirajthinks.workers.dev/post/page3",
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ data: snapshot,
+ }),
+ },
+ );
+
+ console.log(await res.json());
+ setSave("saved!");
+ }, 3000),
+ [editor], // Dependency array ensures the function is not recreated on every render
+ );
+
+ useEffect(() => {
+ const unsubscribe = editor.store.listen(
+ () => {
+ setSave("saving...");
+ debouncedSave();
+ },
+ { scope: "document", source: "user" },
+ );
+
+ return () => unsubscribe(); // Cleanup on unmount
+ }, [editor, debouncedSave]);
+
+ return <button>{save}</button>;
+} \ No newline at end of file
diff --git a/packages/ui/components/canvas/components/textCard.tsx b/packages/ui/components/canvas/components/textCard.tsx
new file mode 100644
index 00000000..b24dae52
--- /dev/null
+++ b/packages/ui/components/canvas/components/textCard.tsx
@@ -0,0 +1,45 @@
+import { BaseBoxShapeUtil, HTMLContainer, TLBaseShape } from "tldraw";
+
+type ITextCardShape = TLBaseShape<
+ "Textcard",
+ { w: number; h: number; content: string; extrainfo: string }
+>;
+
+export class textCardUtil extends BaseBoxShapeUtil<ITextCardShape> {
+ static override type = "Textcard" as const;
+
+ getDefaultProps(): ITextCardShape["props"] {
+ return {
+ w: 100,
+ h: 50,
+ content: "",
+ extrainfo: "",
+ };
+ }
+
+ component(s: ITextCardShape) {
+ return (
+ <HTMLContainer className="flex h-full w-full items-center justify-center">
+ <div
+ style={{
+ height: s.props.h,
+ width: s.props.w,
+ pointerEvents: "all",
+ background: "#2C3439",
+ borderRadius: "16px",
+ padding: "8px 14px",
+ }}
+ >
+ <h1 style={{ fontSize: "15px" }}>{s.props.content}</h1>
+ <p style={{ fontSize: "14px", color: "#369DFD" }}>
+ {s.props.extrainfo}
+ </p>
+ </div>
+ </HTMLContainer>
+ );
+ }
+
+ indicator(shape: ITextCardShape) {
+ return <rect width={shape.props.w} height={shape.props.h} />;
+ }
+}
diff --git a/packages/ui/components/canvas/components/twitterCard.tsx b/packages/ui/components/canvas/components/twitterCard.tsx
new file mode 100644
index 00000000..c5582a98
--- /dev/null
+++ b/packages/ui/components/canvas/components/twitterCard.tsx
@@ -0,0 +1,84 @@
+import { BaseBoxShapeUtil, HTMLContainer, TLBaseShape, toDomPrecision } from "tldraw";
+
+type ITwitterCardShape = TLBaseShape<
+ "Twittercard",
+ { w: number; h: number; url: string }
+>;
+
+export class twitterCardUtil extends BaseBoxShapeUtil<ITwitterCardShape> {
+ static override type = "Twittercard" as const;
+
+ getDefaultProps(): ITwitterCardShape["props"] {
+ return {
+ w: 500,
+ h: 550,
+ url: "",
+ };
+ }
+
+ component(s: ITwitterCardShape) {
+ return (
+ <HTMLContainer className="flex h-full w-full items-center justify-center">
+ <TwitterPost
+ url={s.props.url}
+ width={s.props.w}
+ isInteractive={false}
+ height={s.props.h}
+ />
+ </HTMLContainer>
+ );
+ }
+
+ indicator(shape: ITwitterCardShape) {
+ return <rect width={shape.props.w} height={shape.props.h} />;
+ }
+}
+
+function TwitterPost({
+ isInteractive,
+ width,
+ height,
+ url,
+}: {
+ isInteractive: boolean;
+ width: number;
+ height: number;
+ url: string;
+}) {
+ const link = (() => {
+ try {
+ const urlObj = new URL(url);
+ const path = urlObj.pathname;
+ return path;
+ } catch (error) {
+ console.error("Invalid URL", error);
+ return null;
+ }
+ })();
+
+ return (
+ <iframe
+ className="tl-embed"
+ draggable={false}
+ width={toDomPrecision(width)}
+ height={toDomPrecision(height)}
+ seamless
+ referrerPolicy="no-referrer-when-downgrade"
+ style={{
+ pointerEvents: isInteractive ? "all" : "none",
+ zIndex: isInteractive ? "" : "-1",
+ }}
+ srcDoc={`
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Document</title>
+ </head>
+ <body>
+ <blockquote data-theme="dark" class="twitter-tweet"><p lang="en" dir="ltr"><a href="https://twitter.com${link}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
+ </body>
+ </html>`}
+ />
+ );
+}
diff --git a/packages/ui/components/canvas/lib/context.ts b/packages/ui/components/canvas/lib/context.ts
new file mode 100644
index 00000000..4e6ecd1c
--- /dev/null
+++ b/packages/ui/components/canvas/lib/context.ts
@@ -0,0 +1,18 @@
+import { createContext, useContext } from 'react';
+
+export interface DragContextType {
+ isDraggingOver: boolean;
+ setIsDraggingOver: React.Dispatch<React.SetStateAction<boolean>>;
+}
+
+const DragContext = createContext<DragContextType | undefined>(undefined);
+
+export const useDragContext = () => {
+ const context = useContext(DragContext);
+ if (context === undefined) {
+ throw new Error('useAppContext must be used within an AppProvider');
+ }
+ return context;
+};
+
+export default DragContext; \ No newline at end of file
diff --git a/packages/ui/components/canvas/lib/createAssetUrl.ts b/packages/ui/components/canvas/lib/createAssetUrl.ts
new file mode 100644
index 00000000..05c2baea
--- /dev/null
+++ b/packages/ui/components/canvas/lib/createAssetUrl.ts
@@ -0,0 +1,94 @@
+import {
+ AssetRecordType,
+ TLAsset,
+ getHashForString,
+ truncateStringWithEllipsis,
+} from "tldraw";
+// import { BOOKMARK_ENDPOINT } from './config'
+
+interface ResponseBody {
+ title?: string;
+ description?: string;
+ image?: string;
+}
+
+export async function createAssetFromUrl({
+ url,
+}: {
+ type: "url";
+ url: string;
+}): Promise<TLAsset> {
+ // try {
+ // // First, try to get the meta data from our endpoint
+ // const meta = (await (
+ // await fetch(BOOKMARK_ENDPOINT, {
+ // method: 'POST',
+ // headers: {
+ // 'Content-Type': 'application/json',
+ // },
+ // body: JSON.stringify({
+ // url,
+ // }),
+ // })
+ // ).json()) as ResponseBody
+
+ // return {
+ // id: AssetRecordType.createId(getHashForString(url)),
+ // typeName: 'asset',
+ // type: 'bookmark',
+ // props: {
+ // src: url,
+ // description: meta.description ?? '',
+ // image: meta.image ?? '',
+ // title: meta.title ?? truncateStringWithEllipsis(url, 32),
+ // },
+ // meta: {},
+ // }
+ // } catch (error) {
+ // Otherwise, fallback to fetching data from the url
+
+ let meta: { image: string; title: string; description: string };
+
+ try {
+ const resp = await fetch(url, { method: "GET", mode: "no-cors" });
+ const html = await resp.text();
+ const doc = new DOMParser().parseFromString(html, "text/html");
+ meta = {
+ image:
+ doc.head
+ .querySelector('meta[property="og:image"]')
+ ?.getAttribute("content") ?? "",
+ title:
+ doc.head
+ .querySelector('meta[property="og:title"]')
+ ?.getAttribute("content") ?? truncateStringWithEllipsis(url, 32),
+ description:
+ doc.head
+ .querySelector('meta[property="og:description"]')
+ ?.getAttribute("content") ?? "",
+ };
+ } catch (error) {
+ console.error(error);
+ meta = {
+ image: "",
+ title: truncateStringWithEllipsis(url, 32),
+ description: "",
+ };
+ }
+
+ // Create the bookmark asset from the meta
+ return {
+ id: AssetRecordType.createId(getHashForString(url)),
+ typeName: "asset",
+ type: "bookmark",
+ props: {
+ src: url,
+ image: meta.image,
+ title: meta.title,
+ description: meta.description,
+ favicon: meta.image,
+ },
+ meta: {},
+ };
+ // }
+}
diff --git a/packages/ui/components/canvas/lib/createEmbeds.ts b/packages/ui/components/canvas/lib/createEmbeds.ts
new file mode 100644
index 00000000..b3a7fb52
--- /dev/null
+++ b/packages/ui/components/canvas/lib/createEmbeds.ts
@@ -0,0 +1,236 @@
+// @ts-nocheck TODO: A LOT OF TS ERRORS HERE
+
+import {
+ AssetRecordType,
+ Editor,
+ TLAsset,
+ TLAssetId,
+ TLBookmarkShape,
+ TLExternalContentSource,
+ TLShapePartial,
+ Vec,
+ VecLike,
+ createShapeId,
+ getEmbedInfo,
+ getHashForString,
+} from "tldraw";
+
+export default async function createEmbedsFromUrl({
+ url,
+ point,
+ sources,
+ editor,
+}: {
+ url: string;
+ point?: VecLike | undefined;
+ sources?: TLExternalContentSource[] | undefined;
+ editor: Editor;
+}) {
+ const position =
+ point ??
+ (editor.inputs.shiftKey
+ ? editor.inputs.currentPagePoint
+ : editor.getViewportPageBounds().center);
+
+ if (url?.includes("x.com") || url?.includes("twitter.com")) {
+ return editor.createShape({
+ type: "Twittercard",
+ x: position.x - 250,
+ y: position.y - 150,
+ props: { url: url },
+ });
+ }
+
+ // try to paste as an embed first
+ const embedInfo = getEmbedInfo(url);
+
+ if (embedInfo) {
+ return editor.putExternalContent({
+ type: "embed",
+ url: embedInfo.url,
+ point,
+ embed: embedInfo.definition,
+ });
+ }
+
+ const assetId: TLAssetId = AssetRecordType.createId(getHashForString(url));
+ const shape = createEmptyBookmarkShape(editor, url, position);
+
+ // Use an existing asset if we have one, or else else create a new one
+ let asset = editor.getAsset(assetId) as TLAsset;
+ let shouldAlsoCreateAsset = false;
+ if (!asset) {
+ shouldAlsoCreateAsset = true;
+ try {
+ const bookmarkAsset = await editor.getAssetForExternalContent({
+ type: "url",
+ url,
+ });
+ const fetchWebsite: {
+ title?: string;
+ image?: string;
+ description?: string;
+ } = await (
+ await fetch(`/api/unfirlsite?website=${url}`, {
+ method: "POST",
+ })
+ ).json();
+ if (bookmarkAsset) {
+ if (fetchWebsite.title) bookmarkAsset.props.title = fetchWebsite.title;
+ if (fetchWebsite.image) bookmarkAsset.props.image = fetchWebsite.image;
+ if (fetchWebsite.description)
+ bookmarkAsset.props.description = fetchWebsite.description;
+ }
+ if (!bookmarkAsset) throw Error("Could not create an asset");
+ asset = bookmarkAsset;
+ } catch (e) {
+ console.log(e);
+ return;
+ }
+ }
+
+ editor.batch(() => {
+ if (shouldAlsoCreateAsset) {
+ editor.createAssets([asset]);
+ }
+
+ editor.updateShapes([
+ {
+ id: shape.id,
+ type: shape.type,
+ props: {
+ assetId: asset.id,
+ },
+ },
+ ]);
+ });
+}
+
+function isURL(str: string) {
+ try {
+ new URL(str);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+function formatTextToRatio(text: string) {
+ const totalWidth = text.length;
+ const maxLineWidth = Math.floor(totalWidth / 10);
+
+ const words = text.split(" ");
+ let lines = [];
+ let currentLine = "";
+
+ words.forEach((word) => {
+ if ((currentLine + word).length <= maxLineWidth) {
+ currentLine += (currentLine ? " " : "") + word;
+ } else {
+ lines.push(currentLine);
+ currentLine = word;
+ }
+ });
+ if (currentLine) {
+ lines.push(currentLine);
+ }
+ return { height: (lines.length + 1) * 18, width: maxLineWidth * 10 };
+}
+
+export function handleExternalDroppedContent({
+ text,
+ editor,
+}: {
+ text: string;
+ editor: Editor;
+}) {
+ const position = editor.inputs.shiftKey
+ ? editor.inputs.currentPagePoint
+ : editor.getViewportPageBounds().center;
+
+ if (isURL(text)) {
+ createEmbedsFromUrl({ editor, url: text });
+ } else {
+ // editor.createShape({
+ // type: "text",
+ // x: position.x - 75,
+ // y: position.y - 75,
+ // props: {
+ // text: text,
+ // size: "s",
+ // textAlign: "start",
+ // },
+ // });
+ const { height, width } = formatTextToRatio(text);
+ editor.createShape({
+ type: "Textcard",
+ x: position.x - width / 2,
+ y: position.y - height / 2,
+ props: {
+ content: text,
+ extrainfo: "https://chatgpt.com/c/762cd44e-1752-495b-967a-aa3c23c6024a",
+ w: width,
+ h: height,
+ },
+ });
+ }
+}
+
+function centerSelectionAroundPoint(editor: Editor, position: VecLike) {
+ // Re-position shapes so that the center of the group is at the provided point
+ const viewportPageBounds = editor.getViewportPageBounds();
+ let selectionPageBounds = editor.getSelectionPageBounds();
+
+ if (selectionPageBounds) {
+ const offset = selectionPageBounds!.center.sub(position);
+
+ editor.updateShapes(
+ editor.getSelectedShapes().map((shape) => {
+ const localRotation = editor
+ .getShapeParentTransform(shape)
+ .decompose().rotation;
+ const localDelta = Vec.Rot(offset, -localRotation);
+ return {
+ id: shape.id,
+ type: shape.type,
+ x: shape.x! - localDelta.x,
+ y: shape.y! - localDelta.y,
+ };
+ }),
+ );
+ }
+
+ // Zoom out to fit the shapes, if necessary
+ selectionPageBounds = editor.getSelectionPageBounds();
+ if (
+ selectionPageBounds &&
+ !viewportPageBounds.contains(selectionPageBounds)
+ ) {
+ editor.zoomToSelection();
+ }
+}
+
+export function createEmptyBookmarkShape(
+ editor: Editor,
+ url: string,
+ position: VecLike,
+): TLBookmarkShape {
+ const partial: TLShapePartial = {
+ id: createShapeId(),
+ type: "bookmark",
+ x: position.x - 150,
+ y: position.y - 160,
+ opacity: 1,
+ props: {
+ assetId: null,
+ url,
+ },
+ };
+
+ editor.batch(() => {
+ editor.createShapes([partial]).select(partial.id);
+ centerSelectionAroundPoint(editor, position);
+ });
+
+ return editor.getShape(partial.id) as TLBookmarkShape;
+}
diff --git a/packages/ui/components/canvas/lib/loadSnap.ts b/packages/ui/components/canvas/lib/loadSnap.ts
new file mode 100644
index 00000000..846b1967
--- /dev/null
+++ b/packages/ui/components/canvas/lib/loadSnap.ts
@@ -0,0 +1,14 @@
+import { createTLStore, defaultShapeUtils, loadSnapshot } from "tldraw";
+import { twitterCardUtil } from "../components/twitterCard";
+import { textCardUtil } from "../components/textCard";
+export async function loadRemoteSnapshot() {
+ const res = await fetch(
+ "https://learning-cf.pruthvirajthinks.workers.dev/get/page3",
+ );
+ const snapshot = JSON.parse(await res.json());
+ const newStore = createTLStore({
+ shapeUtils: [...defaultShapeUtils, twitterCardUtil, textCardUtil],
+ });
+ loadSnapshot(newStore, snapshot);
+ return newStore;
+}
diff --git a/packages/ui/icons/add.svg b/packages/ui/icons/add.svg
index 1c6d87f6..970a7c95 100644
--- a/packages/ui/icons/add.svg
+++ b/packages/ui/icons/add.svg
@@ -1,3 +1,3 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M10 4.25V16.75M16.25 10.5H3.75" stroke="#C2C7CB" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10 4.25V16.75M16.25 10.5H3.75" stroke="#989EA4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/icons/autocomplete.svg b/packages/ui/icons/autocomplete.svg
new file mode 100644
index 00000000..a4452242
--- /dev/null
+++ b/packages/ui/icons/autocomplete.svg
@@ -0,0 +1,15 @@
+ <svg
+ width="34"
+ height="24"
+ viewBox="0 0 14 10"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M0.8125 1.0625H13.1875M0.8125 5H13.1875M0.8125 8.9375H7"
+ stroke="#989EA4"
+ stroke-width="1.5"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg> \ No newline at end of file
diff --git a/packages/ui/icons/block.svg b/packages/ui/icons/block.svg
new file mode 100644
index 00000000..ca4cc20e
--- /dev/null
+++ b/packages/ui/icons/block.svg
@@ -0,0 +1,15 @@
+ <svg
+ width="32"
+ height="36"
+ viewBox="0 0 16 18"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M14.75 5.625L8 1.6875L1.25 5.625M14.75 5.625L8 9.5625M14.75 5.625V12.375L8 16.3125M1.25 5.625L8 9.5625M1.25 5.625V12.375L8 16.3125M8 9.5625V16.3125"
+ stroke="#989EA4"
+ stroke-width="1.5"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg> \ No newline at end of file
diff --git a/packages/ui/icons/canvas.svg b/packages/ui/icons/canvas.svg
new file mode 100644
index 00000000..529c75de
--- /dev/null
+++ b/packages/ui/icons/canvas.svg
@@ -0,0 +1,3 @@
+<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.25 0.25C1.05109 0.25 0.860322 0.329018 0.71967 0.46967C0.579018 0.610322 0.5 0.801088 0.5 1C0.5 1.19891 0.579018 1.38968 0.71967 1.53033C0.860322 1.67098 1.05109 1.75 1.25 1.75H2V12.25C2 13.0456 2.31607 13.8087 2.87868 14.3713C3.44129 14.9339 4.20435 15.25 5 15.25H6.21L5.038 18.763C4.97514 18.9518 4.98988 19.1579 5.07896 19.3359C5.16804 19.5138 5.32417 19.6491 5.513 19.712C5.70183 19.7749 5.9079 19.7601 6.08588 19.671C6.26385 19.582 6.39914 19.4258 6.462 19.237L6.791 18.25H15.209L15.539 19.237C15.6073 19.4186 15.7433 19.5666 15.9184 19.6501C16.0935 19.7335 16.2941 19.7459 16.4782 19.6845C16.6622 19.6232 16.8153 19.4929 16.9053 19.3211C16.9954 19.1493 17.0153 18.9492 16.961 18.763L15.791 15.25H17C17.7956 15.25 18.5587 14.9339 19.1213 14.3713C19.6839 13.8087 20 13.0456 20 12.25V1.75H20.75C20.9489 1.75 21.1397 1.67098 21.2803 1.53033C21.421 1.38968 21.5 1.19891 21.5 1C21.5 0.801088 21.421 0.610322 21.2803 0.46967C21.1397 0.329018 20.9489 0.25 20.75 0.25H1.25ZM7.79 15.25H14.21L14.71 16.75H7.29L7.79 15.25ZM15.875 6.255C15.961 6.20611 16.0364 6.1407 16.097 6.06253C16.1577 5.98436 16.2022 5.89497 16.2281 5.79952C16.254 5.70406 16.2608 5.60443 16.2481 5.50634C16.2353 5.40826 16.2033 5.31366 16.1538 5.228C16.1044 5.14235 16.0385 5.06732 15.9599 5.00724C15.8813 4.94715 15.7916 4.90321 15.696 4.87793C15.6004 4.85264 15.5007 4.84653 15.4027 4.85993C15.3047 4.87333 15.2103 4.90598 15.125 4.956C13.7615 5.74523 12.5554 6.7792 11.567 8.006L10.03 6.47C9.88937 6.32955 9.69875 6.25066 9.5 6.25066C9.30125 6.25066 9.11063 6.32955 8.97 6.47L5.97 9.47C5.89631 9.53866 5.83721 9.62146 5.79622 9.71346C5.75523 9.80546 5.73319 9.90477 5.73141 10.0055C5.72963 10.1062 5.74816 10.2062 5.78588 10.2996C5.8236 10.393 5.87974 10.4778 5.95096 10.549C6.02218 10.6203 6.10701 10.6764 6.2004 10.7141C6.29379 10.7518 6.39382 10.7704 6.49452 10.7686C6.59523 10.7668 6.69454 10.7448 6.78654 10.7038C6.87854 10.6628 6.96134 10.6037 7.03 10.53L9.5 8.06L11.117 9.678C11.1946 9.75558 11.2882 9.81519 11.3913 9.85264C11.4945 9.89008 11.6046 9.90445 11.7138 9.89472C11.8231 9.885 11.9289 9.85142 12.0238 9.79635C12.1187 9.74128 12.2003 9.66606 12.263 9.576C13.2095 8.21806 14.4425 7.0844 15.875 6.255Z" fill="#6A737D"/>
+</svg>
diff --git a/packages/ui/icons/chat.svg b/packages/ui/icons/chat.svg
index 7fe96c16..fd33dcd7 100644
--- a/packages/ui/icons/chat.svg
+++ b/packages/ui/icons/chat.svg
@@ -1,3 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M7.1875 10C7.1875 10.0829 7.15458 10.1624 7.09597 10.221C7.03737 10.2796 6.95788 10.3125 6.875 10.3125C6.79212 10.3125 6.71263 10.2796 6.65403 10.221C6.59542 10.1624 6.5625 10.0829 6.5625 10C6.5625 9.91712 6.59542 9.83763 6.65403 9.77903C6.71263 9.72042 6.79212 9.6875 6.875 9.6875C6.95788 9.6875 7.03737 9.72042 7.09597 9.77903C7.15458 9.83763 7.1875 9.91712 7.1875 10ZM7.1875 10H6.875M10.3125 10C10.3125 10.0829 10.2796 10.1624 10.221 10.221C10.1624 10.2796 10.0829 10.3125 10 10.3125C9.91712 10.3125 9.83763 10.2796 9.77903 10.221C9.72042 10.1624 9.6875 10.0829 9.6875 10C9.6875 9.91712 9.72042 9.83763 9.77903 9.77903C9.83763 9.72042 9.91712 9.6875 10 9.6875C10.0829 9.6875 10.1624 9.72042 10.221 9.77903C10.2796 9.83763 10.3125 9.91712 10.3125 10ZM10.3125 10H10M13.4375 10C13.4375 10.0829 13.4046 10.1624 13.346 10.221C13.2874 10.2796 13.2079 10.3125 13.125 10.3125C13.0421 10.3125 12.9626 10.2796 12.904 10.221C12.8454 10.1624 12.8125 10.0829 12.8125 10C12.8125 9.91712 12.8454 9.83763 12.904 9.77903C12.9626 9.72042 13.0421 9.6875 13.125 9.6875C13.2079 9.6875 13.2874 9.72042 13.346 9.77903C13.4046 9.83763 13.4375 9.91712 13.4375 10ZM13.4375 10H13.125M17.5 10C17.5 13.7967 14.1417 16.875 10 16.875C9.28099 16.8759 8.56503 16.7814 7.87083 16.5942C6.8923 17.2824 5.6986 17.5951 4.50833 17.475C4.376 17.4622 4.24422 17.4442 4.11333 17.4208C4.52406 16.9368 4.80456 16.356 4.92833 15.7333C5.00333 15.3525 4.8175 14.9825 4.53917 14.7117C3.275 13.4817 2.5 11.8242 2.5 10C2.5 6.20333 5.85833 3.125 10 3.125C14.1417 3.125 17.5 6.20333 17.5 10Z" stroke="#989EA4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M7.1875 10C7.1875 10.0829 7.15458 10.1624 7.09597 10.221C7.03737 10.2796 6.95788 10.3125 6.875 10.3125C6.79212 10.3125 6.71263 10.2796 6.65403 10.221C6.59542 10.1624 6.5625 10.0829 6.5625 10C6.5625 9.91712 6.59542 9.83763 6.65403 9.77903C6.71263 9.72042 6.79212 9.6875 6.875 9.6875C6.95788 9.6875 7.03737 9.72042 7.09597 9.77903C7.15458 9.83763 7.1875 9.91712 7.1875 10ZM7.1875 10H6.875M10.3125 10C10.3125 10.0829 10.2796 10.1624 10.221 10.221C10.1624 10.2796 10.0829 10.3125 10 10.3125C9.91712 10.3125 9.83763 10.2796 9.77903 10.221C9.72042 10.1624 9.6875 10.0829 9.6875 10C9.6875 9.91712 9.72042 9.83763 9.77903 9.77903C9.83763 9.72042 9.91712 9.6875 10 9.6875C10.0829 9.6875 10.1624 9.72042 10.221 9.77903C10.2796 9.83763 10.3125 9.91712 10.3125 10ZM10.3125 10H10M13.4375 10C13.4375 10.0829 13.4046 10.1624 13.346 10.221C13.2874 10.2796 13.2079 10.3125 13.125 10.3125C13.0421 10.3125 12.9626 10.2796 12.904 10.221C12.8454 10.1624 12.8125 10.0829 12.8125 10C12.8125 9.91712 12.8454 9.83763 12.904 9.77903C12.9626 9.72042 13.0421 9.6875 13.125 9.6875C13.2079 9.6875 13.2874 9.72042 13.346 9.77903C13.4046 9.83763 13.4375 9.91712 13.4375 10ZM13.4375 10H13.125M17.5 10C17.5 13.7967 14.1417 16.875 10 16.875C9.28099 16.8759 8.56503 16.7814 7.87083 16.5942C6.8923 17.2824 5.6986 17.5951 4.50833 17.475C4.376 17.4622 4.24422 17.4442 4.11333 17.4208C4.52406 16.9368 4.80456 16.356 4.92833 15.7333C5.00333 15.3525 4.8175 14.9825 4.53917 14.7117C3.275 13.4817 2.5 11.8242 2.5 10C2.5 6.20333 5.85833 3.125 10 3.125C14.1417 3.125 17.5 6.20333 17.5 10Z" stroke="#7D8994" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/icons/drag.svg b/packages/ui/icons/drag.svg
new file mode 100644
index 00000000..366a4cb0
--- /dev/null
+++ b/packages/ui/icons/drag.svg
@@ -0,0 +1,14 @@
+ <svg
+ width="6"
+ height="9"
+ viewBox="0 0 6 9"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M4.78829 0.916134C4.78348 0.920945 4.77696 0.923648 4.77015 0.923648C4.76335 0.923648 4.75682 0.920945 4.75201 0.916134C4.7472 0.911322 4.74449 0.904797 4.74449 0.897991C4.74449 0.891186 4.7472 0.884661 4.75201 0.879849C4.75682 0.875038 4.76335 0.872335 4.77015 0.872335C4.77696 0.872335 4.78348 0.875038 4.78829 0.879849C4.79311 0.884661 4.79581 0.891186 4.79581 0.897991C4.79581 0.904797 4.79311 0.911322 4.78829 0.916134ZM4.77015 0C4.53199 0 4.30358 0.0946096 4.13518 0.263016C3.96677 0.431421 3.87216 0.659829 3.87216 0.897991C3.87216 1.13615 3.96677 1.36456 4.13518 1.53297C4.30358 1.70137 4.53199 1.79598 4.77015 1.79598C5.00831 1.79598 5.23672 1.70137 5.40513 1.53297C5.57353 1.36456 5.66814 1.13615 5.66814 0.897991C5.66814 0.659829 5.57353 0.431421 5.40513 0.263016C5.23672 0.0946096 5.00831 0 4.77015 0ZM4.78829 4.40547C4.78348 4.41028 4.77696 4.41299 4.77015 4.41299C4.76335 4.41299 4.75682 4.41028 4.75201 4.40547C4.7472 4.40066 4.74449 4.39414 4.74449 4.38733C4.74449 4.38052 4.7472 4.374 4.75201 4.36919C4.75682 4.36438 4.76335 4.36167 4.77015 4.36167C4.77696 4.36167 4.78348 4.36438 4.78829 4.36919C4.79311 4.374 4.79581 4.38052 4.79581 4.38733C4.79581 4.39414 4.79311 4.40066 4.78829 4.40547ZM4.77015 3.48934C4.53199 3.48934 4.30358 3.58395 4.13518 3.75235C3.96677 3.92076 3.87216 4.14917 3.87216 4.38733C3.87216 4.62549 3.96677 4.8539 4.13518 5.02231C4.30358 5.19071 4.53199 5.28532 4.77015 5.28532C5.00831 5.28532 5.23672 5.19071 5.40513 5.02231C5.57353 4.8539 5.66814 4.62549 5.66814 4.38733C5.66814 4.14917 5.57353 3.92076 5.40513 3.75235C5.23672 3.58395 5.00831 3.48934 4.77015 3.48934ZM4.78829 7.89481C4.78348 7.89962 4.77696 7.90232 4.77015 7.90232C4.76335 7.90232 4.75682 7.89962 4.75201 7.89481C4.7472 7.89 4.74449 7.88347 4.74449 7.87667C4.74449 7.86986 4.7472 7.86334 4.75201 7.85853C4.75682 7.85371 4.76335 7.85101 4.77015 7.85101C4.77696 7.85101 4.78348 7.85371 4.78829 7.85853C4.79311 7.86334 4.79581 7.86986 4.79581 7.87667C4.79581 7.88347 4.79311 7.89 4.78829 7.89481ZM4.77015 6.97868C4.53199 6.97868 4.30358 7.07329 4.13518 7.24169C3.96677 7.4101 3.87216 7.63851 3.87216 7.87667C3.87216 8.11483 3.96677 8.34324 4.13518 8.51164C4.30358 8.68005 4.53199 8.77466 4.77015 8.77466C5.00831 8.77466 5.23672 8.68005 5.40513 8.51164C5.57353 8.34324 5.66814 8.11483 5.66814 7.87667C5.66814 7.63851 5.57353 7.4101 5.40513 7.24169C5.23672 7.07329 5.00831 6.97868 4.77015 6.97868ZM0.916134 0.91702C0.911322 0.921832 0.904796 0.924535 0.897991 0.924535C0.891187 0.924535 0.884661 0.921832 0.879849 0.91702C0.875038 0.912209 0.872335 0.905683 0.872335 0.898878C0.872335 0.892073 0.875038 0.885547 0.879849 0.880736C0.884661 0.875924 0.891187 0.873221 0.897991 0.873221C0.904796 0.873221 0.911322 0.875924 0.916134 0.880736C0.920945 0.885547 0.923648 0.892073 0.923648 0.898878C0.923648 0.905683 0.920945 0.912209 0.916134 0.91702ZM0.897991 0.000886679C0.659829 0.000886679 0.431422 0.0954962 0.263016 0.263902C0.0946102 0.432308 0 0.660715 0 0.898878C0 1.13704 0.0946102 1.36545 0.263016 1.53385C0.431422 1.70226 0.659829 1.79687 0.897991 1.79687C1.13615 1.79687 1.36456 1.70226 1.53297 1.53385C1.70137 1.36545 1.79598 1.13704 1.79598 0.898878C1.79598 0.660715 1.70137 0.432308 1.53297 0.263902C1.36456 0.0954962 1.13615 0.000886679 0.897991 0.000886679ZM0.916134 4.40636C0.911323 4.41117 0.904797 4.41387 0.897991 4.41387C0.891186 4.41387 0.88466 4.41117 0.879849 4.40636C0.875038 4.40155 0.872335 4.39502 0.872335 4.38822C0.872335 4.38141 0.875038 4.37489 0.879849 4.37007C0.88466 4.36526 0.891186 4.36256 0.897991 4.36256C0.904797 4.36256 0.911323 4.36526 0.916134 4.37007C0.920945 4.37489 0.923648 4.38141 0.923648 4.38822C0.923648 4.39502 0.920945 4.40155 0.916134 4.40636ZM0.897991 3.49022C0.659828 3.49022 0.431421 3.58484 0.263016 3.75324C0.0946104 3.92165 0 4.15005 0 4.38822C0 4.62638 0.0946104 4.85479 0.263016 5.02319C0.431421 5.1916 0.659828 5.28621 0.897991 5.28621C1.13615 5.28621 1.36456 5.1916 1.53297 5.02319C1.70137 4.85479 1.79598 4.62638 1.79598 4.38822C1.79598 4.15005 1.70137 3.92165 1.53297 3.75324C1.36456 3.58484 1.13615 3.49022 0.897991 3.49022ZM0.916134 7.8957C0.911322 7.90051 0.904795 7.90321 0.897991 7.90321C0.891187 7.90321 0.884661 7.90051 0.879849 7.8957C0.875038 7.89089 0.872335 7.88436 0.872335 7.87755C0.872335 7.87075 0.875038 7.86422 0.879849 7.85941C0.884661 7.8546 0.891187 7.8519 0.897991 7.8519C0.904795 7.8519 0.911322 7.8546 0.916134 7.85941C0.920945 7.86422 0.923648 7.87075 0.923648 7.87755C0.923648 7.88436 0.920945 7.89089 0.916134 7.8957ZM0.897991 6.97956C0.65983 6.97956 0.431422 7.07417 0.263016 7.24258C0.0946099 7.41098 0 7.63939 0 7.87755C0 8.11572 0.0946099 8.34412 0.263016 8.51253C0.431422 8.68094 0.65983 8.77555 0.897991 8.77555C1.13615 8.77555 1.36456 8.68094 1.53297 8.51253C1.70137 8.34412 1.79598 8.11572 1.79598 7.87755C1.79598 7.63939 1.70137 7.41098 1.53297 7.24258C1.36456 7.07417 1.13615 6.97956 0.897991 6.97956Z"
+ fill="#989EA4"
+ />
+ </svg> \ No newline at end of file
diff --git a/packages/ui/icons/explore.svg b/packages/ui/icons/explore.svg
index d6332612..98785c14 100644
--- a/packages/ui/icons/explore.svg
+++ b/packages/ui/icons/explore.svg
@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M6.5 17.5L14 14L17.5 6.5L10 10L6.5 17.5ZM12 13C11.7167 13 11.4793 12.904 11.288 12.712C11.0967 12.52 11.0007 12.2827 11 12C10.9993 11.7173 11.0953 11.48 11.288 11.288C11.4807 11.096 11.718 11 12 11C12.282 11 12.5197 11.096 12.713 11.288C12.9063 11.48 13.002 11.7173 13 12C12.998 12.2827 12.902 12.5203 12.712 12.713C12.522 12.9057 12.2847 13.0013 12 13ZM12 22C10.6167 22 9.31667 21.7373 8.1 21.212C6.88334 20.6867 5.825 19.9743 4.925 19.075C4.025 18.1757 3.31267 17.1173 2.788 15.9C2.26333 14.6827 2.00067 13.3827 2 12C1.99933 10.6173 2.262 9.31733 2.788 8.1C3.314 6.88267 4.02633 5.82433 4.925 4.925C5.82367 4.02567 6.882 3.31333 8.1 2.788C9.318 2.26267 10.618 2 12 2C13.382 2 14.682 2.26267 15.9 2.788C17.118 3.31333 18.1763 4.02567 19.075 4.925C19.9737 5.82433 20.6863 6.88267 21.213 8.1C21.7397 9.31733 22.002 10.6173 22 12C21.998 13.3827 21.7353 14.6827 21.212 15.9C20.6887 17.1173 19.9763 18.1757 19.075 19.075C18.1737 19.9743 17.1153 20.687 15.9 21.213C14.6847 21.739 13.3847 22.0013 12 22Z" fill="#6A737D"/>
+<path d="M6.5 17.5L14 14L17.5 6.5L10 10L6.5 17.5ZM12 13C11.7167 13 11.4793 12.904 11.288 12.712C11.0967 12.52 11.0007 12.2827 11 12C10.9993 11.7173 11.0953 11.48 11.288 11.288C11.4807 11.096 11.718 11 12 11C12.282 11 12.5197 11.096 12.713 11.288C12.9063 11.48 13.002 11.7173 13 12C12.998 12.2827 12.902 12.5203 12.712 12.713C12.522 12.9057 12.2847 13.0013 12 13ZM12 22C10.6167 22 9.31667 21.7373 8.1 21.212C6.88334 20.6867 5.825 19.9743 4.925 19.075C4.025 18.1757 3.31267 17.1173 2.788 15.9C2.26333 14.6827 2.00067 13.3827 2 12C1.99933 10.6173 2.262 9.31733 2.788 8.1C3.314 6.88267 4.02633 5.82433 4.925 4.925C5.82367 4.02567 6.882 3.31333 8.1 2.788C9.318 2.26267 10.618 2 12 2C13.382 2 14.682 2.26267 15.9 2.788C17.118 3.31333 18.1763 4.02567 19.075 4.925C19.9737 5.82433 20.6863 6.88267 21.213 8.1C21.7397 9.31733 22.002 10.6173 22 12C21.998 13.3827 21.7353 14.6827 21.212 15.9C20.6887 17.1173 19.9763 18.1757 19.075 19.075C18.1737 19.9743 17.1153 20.687 15.9 21.213C14.6847 21.739 13.3847 22.0013 12 22Z" fill="#777E87"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/icons/history.svg b/packages/ui/icons/history.svg
index 7766c75d..9d735d5a 100644
--- a/packages/ui/icons/history.svg
+++ b/packages/ui/icons/history.svg
@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.25C6.615 2.25 2.25 6.615 2.25 12C2.25 17.385 6.615 21.75 12 21.75C17.385 21.75 21.75 17.385 21.75 12C21.75 6.615 17.385 2.25 12 2.25ZM12.75 6C12.75 5.80109 12.671 5.61032 12.5303 5.46967C12.3897 5.32902 12.1989 5.25 12 5.25C11.8011 5.25 11.6103 5.32902 11.4697 5.46967C11.329 5.61032 11.25 5.80109 11.25 6V12C11.25 12.414 11.586 12.75 12 12.75H16.5C16.6989 12.75 16.8897 12.671 17.0303 12.5303C17.171 12.3897 17.25 12.1989 17.25 12C17.25 11.8011 17.171 11.6103 17.0303 11.4697C16.8897 11.329 16.6989 11.25 16.5 11.25H12.75V6Z" fill="#6A737D"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.25C6.615 2.25 2.25 6.615 2.25 12C2.25 17.385 6.615 21.75 12 21.75C17.385 21.75 21.75 17.385 21.75 12C21.75 6.615 17.385 2.25 12 2.25ZM12.75 6C12.75 5.80109 12.671 5.61032 12.5303 5.46967C12.3897 5.32902 12.1989 5.25 12 5.25C11.8011 5.25 11.6103 5.32902 11.4697 5.46967C11.329 5.61032 11.25 5.80109 11.25 6V12C11.25 12.414 11.586 12.75 12 12.75H16.5C16.6989 12.75 16.8897 12.671 17.0303 12.5303C17.171 12.3897 17.25 12.1989 17.25 12C17.25 11.8011 17.171 11.6103 17.0303 11.4697C16.8897 11.329 16.6989 11.25 16.5 11.25H12.75V6Z" fill="#777E87"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/icons/index.ts b/packages/ui/icons/index.ts
index e2331ee6..cf2f0aaa 100644
--- a/packages/ui/icons/index.ts
+++ b/packages/ui/icons/index.ts
@@ -5,6 +5,16 @@ import ExploreIcon from "./explore.svg";
import MemoriesIcon from "./memories.svg";
import ArrowRightIcon from "./arrowright.svg";
import SelectIcon from "./select.svg";
+import SearchIcon from "./search.svg";
+import NextIcon from "./nextarrow.svg";
+import UrlIcon from "./url.svg";
+import CanvasIcon from "./canvas.svg";
+import blockIcon from "./block.svg";
+import LinkIcon from "./link.svg";
+import AutocompleteIcon from "./autocomplete.svg";
+import BlockIcon from "./block.svg";
+import DragIcon from "./drag.svg";
+import SettingsIcon from "./settings.svg";
export {
AddIcon,
@@ -14,4 +24,14 @@ export {
MemoriesIcon,
ArrowRightIcon,
SelectIcon,
+ SearchIcon,
+ NextIcon,
+ UrlIcon,
+ CanvasIcon,
+ blockIcon,
+ LinkIcon,
+ AutocompleteIcon,
+ BlockIcon,
+ DragIcon,
+ SettingsIcon,
};
diff --git a/packages/ui/icons/link.svg b/packages/ui/icons/link.svg
new file mode 100644
index 00000000..25e2d04e
--- /dev/null
+++ b/packages/ui/icons/link.svg
@@ -0,0 +1,15 @@
+ <svg
+ width="36"
+ height="36"
+ viewBox="0 0 18 18"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M9.89252 6.51602C10.3799 6.74871 10.8043 7.09497 11.1301 7.5257C11.4559 7.95643 11.6736 8.45906 11.7648 8.99136C11.8561 9.52366 11.8183 10.0701 11.6546 10.5848C11.4909 11.0994 11.206 11.5673 10.824 11.949L7.44902 15.324C6.81608 15.957 5.95763 16.3125 5.06252 16.3125C4.16741 16.3125 3.30896 15.957 2.67602 15.324C2.04308 14.6911 1.6875 13.8326 1.6875 12.9375C1.6875 12.0424 2.04308 11.184 2.67602 10.551L3.99377 9.23327M14.0063 8.76677L15.324 7.44902C15.957 6.81608 16.3125 5.95763 16.3125 5.06252C16.3125 4.16741 15.957 3.30896 15.324 2.67602C14.6911 2.04308 13.8326 1.6875 12.9375 1.6875C12.0424 1.6875 11.184 2.04308 10.551 2.67602L7.17602 6.05102C6.794 6.43277 6.50917 6.90063 6.34546 7.41529C6.18175 7.92995 6.14393 8.47638 6.2352 9.00868C6.32646 9.54098 6.54414 10.0436 6.86994 10.4743C7.19574 10.9051 7.62015 11.2513 8.10752 11.484"
+ stroke="#989EA4"
+ stroke-width="1.5"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg> \ No newline at end of file
diff --git a/packages/ui/icons/memories.svg b/packages/ui/icons/memories.svg
index 27784aac..069b4db8 100644
--- a/packages/ui/icons/memories.svg
+++ b/packages/ui/icons/memories.svg
@@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M5.566 4.657C5.95195 4.55235 6.35011 4.49955 6.75 4.5H17.25C17.66 4.5 18.056 4.555 18.433 4.657C18.1837 4.15898 17.8006 3.7402 17.3268 3.44754C16.8529 3.15489 16.3069 2.99993 15.75 3H8.25C7.69288 2.99974 7.1467 3.15462 6.67265 3.44728C6.1986 3.73994 5.8154 4.15883 5.566 4.657ZM2.25 12C2.25 11.2044 2.56607 10.4413 3.12868 9.87868C3.69129 9.31607 4.45435 9 5.25 9H18.75C19.5456 9 20.3087 9.31607 20.8713 9.87868C21.4339 10.4413 21.75 11.2044 21.75 12V18C21.75 18.7956 21.4339 19.5587 20.8713 20.1213C20.3087 20.6839 19.5456 21 18.75 21H5.25C4.45435 21 3.69129 20.6839 3.12868 20.1213C2.56607 19.5587 2.25 18.7956 2.25 18V12ZM5.25 7.5C4.84 7.5 4.444 7.555 4.066 7.657C4.3154 7.15883 4.6986 6.73994 5.17265 6.44728C5.6467 6.15462 6.19288 5.99974 6.75 6H17.25C17.8069 5.99993 18.3529 6.15489 18.8268 6.44754C19.3006 6.7402 19.6837 7.15898 19.933 7.657C19.5474 7.55244 19.1496 7.49964 18.75 7.5H5.25Z" fill="#6A737D"/>
+<path d="M5.566 4.657C5.95195 4.55235 6.35011 4.49955 6.75 4.5H17.25C17.66 4.5 18.056 4.555 18.433 4.657C18.1837 4.15898 17.8006 3.7402 17.3268 3.44754C16.8529 3.15489 16.3069 2.99993 15.75 3H8.25C7.69288 2.99974 7.1467 3.15462 6.67265 3.44728C6.1986 3.73994 5.8154 4.15883 5.566 4.657ZM2.25 12C2.25 11.2044 2.56607 10.4413 3.12868 9.87868C3.69129 9.31607 4.45435 9 5.25 9H18.75C19.5456 9 20.3087 9.31607 20.8713 9.87868C21.4339 10.4413 21.75 11.2044 21.75 12V18C21.75 18.7956 21.4339 19.5587 20.8713 20.1213C20.3087 20.6839 19.5456 21 18.75 21H5.25C4.45435 21 3.69129 20.6839 3.12868 20.1213C2.56607 19.5587 2.25 18.7956 2.25 18V12ZM5.25 7.5C4.84 7.5 4.444 7.555 4.066 7.657C4.3154 7.15883 4.6986 6.73994 5.17265 6.44728C5.6467 6.15462 6.19288 5.99974 6.75 6H17.25C17.8069 5.99993 18.3529 6.15489 18.8268 6.44754C19.3006 6.7402 19.6837 7.15898 19.933 7.657C19.5474 7.55244 19.1496 7.49964 18.75 7.5H5.25Z" fill="#777E87"/>
</svg> \ No newline at end of file
diff --git a/packages/ui/icons/nextarrow.svg b/packages/ui/icons/nextarrow.svg
new file mode 100644
index 00000000..b0f4a5fe
--- /dev/null
+++ b/packages/ui/icons/nextarrow.svg
@@ -0,0 +1,3 @@
+<svg width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.1875 1.375L6.8125 7L1.1875 12.625" stroke="#B3BCC5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/packages/ui/icons/search.svg b/packages/ui/icons/search.svg
new file mode 100644
index 00000000..af8b6664
--- /dev/null
+++ b/packages/ui/icons/search.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.75 14.7501L10.8523 10.8523M10.8523 10.8523C11.9072 9.79742 12.4998 8.36662 12.4998 6.87472C12.4998 5.38282 11.9072 3.95203 10.8523 2.8971C9.79732 1.84217 8.36653 1.24951 6.87463 1.24951C5.38273 1.24951 3.95194 1.84217 2.89701 2.8971C1.84207 3.95203 1.24942 5.38282 1.24942 6.87472C1.24942 8.36662 1.84207 9.79742 2.89701 10.8523C3.95194 11.9073 5.38273 12.4999 6.87463 12.4999C8.36653 12.4999 9.79732 11.9073 10.8523 10.8523Z" stroke="#687077" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/packages/ui/icons/settings.svg b/packages/ui/icons/settings.svg
new file mode 100644
index 00000000..2059b976
--- /dev/null
+++ b/packages/ui/icons/settings.svg
@@ -0,0 +1,14 @@
+ <svg
+ width="16"
+ height="18"
+ viewBox="0 0 16 18"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M7.2321 0.875C6.46793 0.875 5.81627 1.4275 5.69043 2.18083L5.5421 3.07417C5.52543 3.17417 5.44627 3.29083 5.2946 3.36417C5.00906 3.50143 4.73438 3.66022 4.47293 3.83917C4.3346 3.935 4.1946 3.94417 4.09793 3.90833L3.25043 3.59C2.90397 3.4602 2.52268 3.45755 2.17445 3.58253C1.82621 3.70751 1.53362 3.95201 1.34877 4.2725L0.580434 5.60333C0.395508 5.92363 0.330196 6.29915 0.396115 6.66307C0.462034 7.027 0.65491 7.35575 0.940434 7.59083L1.64043 8.1675C1.7196 8.2325 1.7821 8.35833 1.76877 8.52583C1.74502 8.84178 1.74502 9.15906 1.76877 9.475C1.78127 9.64167 1.7196 9.76833 1.64127 9.83333L0.940434 10.41C0.65491 10.6451 0.462034 10.9738 0.396115 11.3378C0.330196 11.7017 0.395508 12.0772 0.580434 12.3975L1.34877 13.7283C1.53376 14.0487 1.8264 14.293 2.17462 14.4178C2.52285 14.5426 2.90406 14.5399 3.25043 14.41L4.0996 14.0917C4.19543 14.0558 4.33543 14.0658 4.4746 14.16C4.7346 14.3383 5.00877 14.4975 5.29543 14.635C5.4471 14.7083 5.52627 14.825 5.54293 14.9267L5.69127 15.8192C5.8171 16.5725 6.46877 17.125 7.23293 17.125H8.7696C9.53293 17.125 10.1854 16.5725 10.3113 15.8192L10.4596 14.9258C10.4763 14.8258 10.5546 14.7092 10.7071 14.635C10.9938 14.4975 11.2679 14.3383 11.5279 14.16C11.6671 14.065 11.8071 14.0558 11.9029 14.0917L12.7529 14.41C13.0992 14.5394 13.4801 14.5418 13.828 14.4168C14.1758 14.2919 14.4681 14.0476 14.6529 13.7275L15.4221 12.3967C15.607 12.0764 15.6723 11.7009 15.6064 11.3369C15.5405 10.973 15.3476 10.6443 15.0621 10.4092L14.3621 9.8325C14.2829 9.7675 14.2204 9.64167 14.2338 9.47417C14.2575 9.15822 14.2575 8.84095 14.2338 8.525C14.2204 8.35833 14.2829 8.23167 14.3613 8.16667L15.0613 7.59C15.6513 7.105 15.8038 6.265 15.4221 5.6025L14.6538 4.27167C14.4688 3.95132 14.1761 3.707 13.8279 3.58218C13.4797 3.45735 13.0985 3.46013 12.7521 3.59L11.9021 3.90833C11.8071 3.94417 11.6671 3.93417 11.5279 3.83917C11.2668 3.66025 10.9924 3.50145 10.7071 3.36417C10.5546 3.29167 10.4763 3.175 10.4596 3.07417L10.3104 2.18083C10.2497 1.81589 10.0614 1.48435 9.77905 1.24522C9.49674 1.0061 9.13874 0.874907 8.76877 0.875H7.23293H7.2321ZM8.00043 12.125C8.82923 12.125 9.62409 11.7958 10.2101 11.2097C10.7962 10.6237 11.1254 9.8288 11.1254 9C11.1254 8.1712 10.7962 7.37634 10.2101 6.79029C9.62409 6.20424 8.82923 5.875 8.00043 5.875C7.17163 5.875 6.37678 6.20424 5.79072 6.79029C5.20467 7.37634 4.87543 8.1712 4.87543 9C4.87543 9.8288 5.20467 10.6237 5.79072 11.2097C6.37678 11.7958 7.17163 12.125 8.00043 12.125Z"
+ fill="#989EA4"
+ />
+ </svg> \ No newline at end of file
diff --git a/packages/ui/icons/url.svg b/packages/ui/icons/url.svg
new file mode 100644
index 00000000..65084459
--- /dev/null
+++ b/packages/ui/icons/url.svg
@@ -0,0 +1,4 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9.89249 6.51602C10.3799 6.74871 10.8043 7.09497 11.1301 7.5257C11.4559 7.95643 11.6735 8.45906 11.7648 8.99136C11.8561 9.52367 11.8183 10.0701 11.6546 10.5848C11.4908 11.0994 11.206 11.5673 10.824 11.949L7.44899 15.324C6.81605 15.957 5.9576 16.3125 5.06249 16.3125C4.16738 16.3125 3.30893 15.957 2.67599 15.324C2.04305 14.6911 1.68747 13.8326 1.68747 12.9375C1.68747 12.0424 2.04305 11.184 2.67599 10.551L3.99374 9.23327M14.0062 8.76677L15.324 7.44902C15.9569 6.81608 16.3125 5.95763 16.3125 5.06252C16.3125 4.16741 15.9569 3.30896 15.324 2.67602C14.6911 2.04308 13.8326 1.6875 12.9375 1.6875C12.0424 1.6875 11.1839 2.04308 10.551 2.67602L7.17599 6.05102C6.79397 6.43277 6.50914 6.90063 6.34543 7.41529C6.18172 7.92995 6.1439 8.47638 6.23517 9.00868C6.32643 9.54098 6.54411 10.0436 6.86991 10.4743C7.19571 10.9051 7.62012 11.2513 8.10749 11.484" stroke="#B3BCC5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+
diff --git a/packages/ui/shadcn/accordion.tsx b/packages/ui/shadcn/accordion.tsx
new file mode 100644
index 00000000..b24ecde4
--- /dev/null
+++ b/packages/ui/shadcn/accordion.tsx
@@ -0,0 +1,55 @@
+"use client";
+
+import * as React from "react";
+import * as AccordionPrimitive from "@radix-ui/react-accordion";
+import { ChevronDownIcon } from "@heroicons/react/24/outline";
+
+import { cn } from "@repo/ui/lib/utils";
+
+const Accordion = AccordionPrimitive.Root;
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef<typeof AccordionPrimitive.Item>,
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
+>(({ className, ...props }, ref) => (
+ <AccordionPrimitive.Item ref={ref} className={cn(className)} {...props} />
+));
+AccordionItem.displayName = "AccordionItem";
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef<typeof AccordionPrimitive.Trigger>,
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
+>(({ className, children, ...props }, ref) => (
+ <AccordionPrimitive.Header className="flex">
+ <AccordionPrimitive.Trigger
+ ref={ref}
+ className={cn(
+ "flex flex-1 items-center gap-2 py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+ <ChevronDownIcon className="size-4 stroke-2 transition-transform duration-200" />
+ </AccordionPrimitive.Trigger>
+ </AccordionPrimitive.Header>
+));
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef<typeof AccordionPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
+>(({ className, children, ...props }, ref) => (
+ <AccordionPrimitive.Content
+ ref={ref}
+ className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
+ {...props}
+ >
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
+ </AccordionPrimitive.Content>
+));
+
+AccordionContent.displayName = AccordionPrimitive.Content.displayName;
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
diff --git a/packages/ui/shadcn/combobox.tsx b/packages/ui/shadcn/combobox.tsx
index 1a2674ff..0d0fd714 100644
--- a/packages/ui/shadcn/combobox.tsx
+++ b/packages/ui/shadcn/combobox.tsx
@@ -106,7 +106,7 @@ function transToGroupOption(options: Option[], groupBy?: string) {
if (!groupOption[key]) {
groupOption[key] = [];
}
- groupOption[key].push(option);
+ groupOption[key]?.push(option);
});
return groupOption;
}
diff --git a/packages/ui/shadcn/select.tsx b/packages/ui/shadcn/select.tsx
new file mode 100644
index 00000000..8abe27c1
--- /dev/null
+++ b/packages/ui/shadcn/select.tsx
@@ -0,0 +1,160 @@
+"use client";
+
+import * as React from "react";
+import * as SelectPrimitive from "@radix-ui/react-select";
+import { Check, ChevronDown, ChevronUp } from "lucide-react";
+
+import { cn } from "@repo/ui/lib/utils";
+
+const Select = SelectPrimitive.Root;
+
+const SelectGroup = SelectPrimitive.Group;
+
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
+>(({ className, children, ...props }, ref) => (
+ <SelectPrimitive.Trigger
+ ref={ref}
+ className={cn(
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+ <SelectPrimitive.Icon asChild>
+ <ChevronDown className="h-4 w-4 opacity-50" />
+ </SelectPrimitive.Icon>
+ </SelectPrimitive.Trigger>
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.ScrollUpButton
+ ref={ref}
+ className={cn(
+ "flex cursor-default items-center justify-center py-1",
+ className,
+ )}
+ {...props}
+ >
+ <ChevronUp className="h-4 w-4" />
+ </SelectPrimitive.ScrollUpButton>
+));
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.ScrollDownButton
+ ref={ref}
+ className={cn(
+ "flex cursor-default items-center justify-center py-1",
+ className,
+ )}
+ {...props}
+ >
+ <ChevronDown className="h-4 w-4" />
+ </SelectPrimitive.ScrollDownButton>
+));
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName;
+
+const SelectContent = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
+>(({ className, children, position = "popper", ...props }, ref) => (
+ <SelectPrimitive.Portal>
+ <SelectPrimitive.Content
+ ref={ref}
+ className={cn(
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md text-popover-foreground shadow-md 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-[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",
+ position === "popper" &&
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
+ className,
+ )}
+ position={position}
+ {...props}
+ >
+ <SelectScrollUpButton />
+ <SelectPrimitive.Viewport
+ className={cn(
+ "p-1",
+ position === "popper" &&
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
+ )}
+ >
+ {children}
+ </SelectPrimitive.Viewport>
+ <SelectScrollDownButton />
+ </SelectPrimitive.Content>
+ </SelectPrimitive.Portal>
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Label>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.Label
+ ref={ref}
+ className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
+ {...props}
+ />
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Item>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
+>(({ className, children, ...props }, ref) => (
+ <SelectPrimitive.Item
+ ref={ref}
+ className={cn(
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-foreground-menu data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
+ className,
+ )}
+ {...props}
+ >
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
+ <SelectPrimitive.ItemIndicator>
+ <Check className="h-4 w-4" />
+ </SelectPrimitive.ItemIndicator>
+ </span>
+
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
+ </SelectPrimitive.Item>
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Separator>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.Separator
+ ref={ref}
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
+ {...props}
+ />
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+};
diff --git a/packages/ui/shadcn/separator.tsx b/packages/ui/shadcn/separator.tsx
new file mode 100644
index 00000000..d956f2f0
--- /dev/null
+++ b/packages/ui/shadcn/separator.tsx
@@ -0,0 +1,21 @@
+"use client";
+
+import * as React from "react";
+import * as SeparatorPrimitive from "@radix-ui/react-separator";
+import { cn } from "@repo/ui/lib/utils";
+
+const Separator = React.forwardRef<
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
+>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
+ <SeparatorPrimitive.Root
+ ref={ref}
+ decorative={decorative}
+ orientation={orientation}
+ className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : " w-[1px]", className)}
+ {...props}
+ />
+));
+Separator.displayName = SeparatorPrimitive.Root.displayName;
+
+export { Separator };
diff --git a/packages/ui/shadcn/sonner.tsx b/packages/ui/shadcn/sonner.tsx
new file mode 100644
index 00000000..549cf841
--- /dev/null
+++ b/packages/ui/shadcn/sonner.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import { useTheme } from "next-themes";
+import { Toaster as Sonner } from "sonner";
+
+type ToasterProps = React.ComponentProps<typeof Sonner>;
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme();
+
+ return (
+ <Sonner
+ theme={theme as ToasterProps["theme"]}
+ className="toaster group"
+ toastOptions={{
+ classNames: {
+ toast:
+ "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
+ description: "group-[.toast]:text-muted-foreground",
+ actionButton:
+ "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
+ cancelButton:
+ "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
+ },
+ }}
+ {...props}
+ />
+ );
+};
+
+export { Toaster };
diff --git a/packages/ui/shadcn/switch.tsx b/packages/ui/shadcn/switch.tsx
new file mode 100644
index 00000000..3c246594
--- /dev/null
+++ b/packages/ui/shadcn/switch.tsx
@@ -0,0 +1,29 @@
+"use client";
+
+import * as React from "react";
+import * as SwitchPrimitives from "@radix-ui/react-switch";
+
+import { cn } from "@repo/ui/lib/utils";
+
+const Switch = React.forwardRef<
+ React.ElementRef<typeof SwitchPrimitives.Root>,
+ React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
+>(({ className, ...props }, ref) => (
+ <SwitchPrimitives.Root
+ className={cn(
+ "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
+ className,
+ )}
+ {...props}
+ ref={ref}
+ >
+ <SwitchPrimitives.Thumb
+ className={cn(
+ "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
+ )}
+ />
+ </SwitchPrimitives.Root>
+));
+Switch.displayName = SwitchPrimitives.Root.displayName;
+
+export { Switch };
diff --git a/packages/ui/shadcn/textarea.tsx b/packages/ui/shadcn/textarea.tsx
new file mode 100644
index 00000000..30ce2800
--- /dev/null
+++ b/packages/ui/shadcn/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+
+import { cn } from "@repo/ui/lib/utils";
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
+
+const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
+ ({ className, ...props }, ref) => {
+ return (
+ <textarea
+ className={cn(
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
+ className
+ )}
+ ref={ref}
+ {...props}
+ />
+ )
+ }
+)
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/packages/ui/shadcn/use-toast.ts b/packages/ui/shadcn/use-toast.ts
index 7dd2304d..24ab9941 100644
--- a/packages/ui/shadcn/use-toast.ts
+++ b/packages/ui/shadcn/use-toast.ts
@@ -2,7 +2,7 @@
// Inspired by react-hot-toast library
import * as React from "react";
-import { ToastActionElement, ToastProps } from "@repo/ui/shadcn/toast";
+import type { ToastActionElement, ToastProps } from "@repo/ui/shadcn/toast";
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;