aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDhravya Shah <[email protected]>2024-03-31 23:07:45 -0700
committerGitHub <[email protected]>2024-03-31 23:07:45 -0700
commitdeab0568dfdfdd61092d4ab47bc9a5ea50de39c2 (patch)
treeada7257bd36f6443eb66d80e09e071bdd70da576
parentupdate .github (diff)
parentremove unnecessasry comment (diff)
downloadsupermemory-deab0568dfdfdd61092d4ab47bc9a5ea50de39c2.tar.xz
supermemory-deab0568dfdfdd61092d4ab47bc9a5ea50de39c2.zip
Merge pull request #1 from Dhravya/gemini-api
Gemini api
-rw-r--r--apps/cf-ai-backend/src/index.ts77
-rw-r--r--apps/web/public/brain.pngbin0 -> 3131 bytes
-rw-r--r--apps/web/src/app/globals.css8
-rw-r--r--apps/web/src/app/layout.tsx6
-rw-r--r--apps/web/src/app/ui/page.tsx9
-rw-r--r--apps/web/src/components/QueryAI.tsx5
-rw-r--r--apps/web/src/components/Sidebar.tsx145
-rw-r--r--apps/web/src/components/ui/dropdown-menu.tsx88
-rw-r--r--apps/web/src/server/db/schema.ts46
-rw-r--r--apps/web/tailwind.config.ts16
-rw-r--r--package.json2
11 files changed, 309 insertions, 93 deletions
diff --git a/apps/cf-ai-backend/src/index.ts b/apps/cf-ai-backend/src/index.ts
index fc7241a0..9b8eec8f 100644
--- a/apps/cf-ai-backend/src/index.ts
+++ b/apps/cf-ai-backend/src/index.ts
@@ -7,15 +7,15 @@ import type {
import {
CloudflareVectorizeStore,
} from "@langchain/cloudflare";
-import { Ai } from '@cloudflare/ai';
import { OpenAIEmbeddings } from "./OpenAIEmbedder";
-import { AiTextGenerationOutput } from "@cloudflare/ai/dist/ai/tasks/text-generation";
+import { GoogleGenerativeAI } from "@google/generative-ai";
export interface Env {
VECTORIZE_INDEX: VectorizeIndex;
AI: Fetcher;
SECURITY_KEY: string;
OPENAI_API_KEY: string;
+ GOOGLE_AI_API_KEY: string;
}
@@ -38,8 +38,11 @@ export default {
const store = new CloudflareVectorizeStore(embeddings, {
index: env.VECTORIZE_INDEX,
});
- const ai = new Ai(env.AI)
+ const genAI = new GoogleGenerativeAI(env.GOOGLE_AI_API_KEY);
+ const model = genAI.getGenerativeModel({ model: "gemini-pro" });
+
+ // TODO: Add /chat endpoint to chat with the AI in a conversational manner
if (pathname === "/add" && request.method === "POST") {
const body = await request.json() as {
@@ -119,22 +122,27 @@ export default {
return new Response(JSON.stringify({ message: "No Results Found" }), { status: 400 });
}
- const metadatas = vec.map(({ metadata }) => metadata)
-
- console.log(metadatas)
-
- // TODO: TAKE ALL THE HIGH SCORED IDS INTO CONSIDERATION
- const output: AiTextGenerationOutput = await ai.run('@hf/thebloke/mistral-7b-instruct-v0.1-awq', {
- prompt: `You are an agent that summarizes a page based on the query. Be direct and concise, don't say 'based on the context'.\n\n Context:\n${vec[0].metadata!.text} \nAnswer this question based on the context. Question: ${query}\nAnswer:`,
- stream: true
- }) as ReadableStream
-
-
- return new Response(output, {
- headers: {
- "content-type": "text/event-stream",
- },
- });
+ const preparedContext = vec.slice(0, 3).map(({ metadata }) => `Website title: ${metadata!.title}\nDescription: ${metadata!.description}\nURL: ${metadata!.url}\nContent: ${metadata!.text}`).join("\n\n");
+
+ const prompt = `You are an agent that summarizes a page based on the query. Be direct and concise, don't say 'based on the context'.\n\n Context:\n${preparedContext} \nAnswer this question based on the context. Question: ${query}\nAnswer:`
+ const output = await model.generateContentStream(prompt);
+
+ const response = new Response(
+ new ReadableStream({
+ async start(controller) {
+ const converter = new TextEncoder();
+ for await (const chunk of output.stream) {
+ const chunkText = await chunk.text();
+ const encodedChunk = converter.encode("data: " + JSON.stringify({ "response": chunkText }) + "\n\n");
+ controller.enqueue(encodedChunk);
+ }
+ const doneChunk = converter.encode("data: [DONE]");
+ controller.enqueue(doneChunk);
+ controller.close();
+ }
+ })
+ );
+ return response;
}
else if (pathname === "/ask" && request.method === "POST") {
@@ -146,17 +154,26 @@ export default {
return new Response(JSON.stringify({ message: "Invalid Page Content" }), { status: 400 });
}
- const output: AiTextGenerationOutput = await ai.run('@hf/thebloke/mistral-7b-instruct-v0.1-awq', {
- prompt: `You are an agent that answers a question based on the query. Be direct and concise, don't say 'based on the context'.\n\n Context:\n${body.query} \nAnswer this question based on the context. Question: ${body.query}\nAnswer:`,
- stream: true
- }) as ReadableStream
-
-
- return new Response(output, {
- headers: {
- "content-type": "text/event-stream",
- },
- });
+ const prompt = `You are an agent that answers a question based on the query. Be direct and concise, don't say 'based on the context'.\n\n Context:\n${body.query} \nAnswer this question based on the context. Question: ${body.query}\nAnswer:`
+ const output = await model.generateContentStream(prompt);
+
+ const response = new Response(
+ new ReadableStream({
+ async start(controller) {
+ const converter = new TextEncoder();
+ for await (const chunk of output.stream) {
+ const chunkText = await chunk.text();
+ console.log(chunkText);
+ const encodedChunk = converter.encode("data: " + JSON.stringify({ "response": chunkText }) + "\n\n");
+ controller.enqueue(encodedChunk);
+ }
+ const doneChunk = converter.encode("data: [DONE] \n\n");
+ controller.enqueue(doneChunk);
+ controller.close();
+ }
+ })
+ );
+ return response;
}
return new Response(JSON.stringify({ message: "Invalid Request" }), { status: 400 });
diff --git a/apps/web/public/brain.png b/apps/web/public/brain.png
new file mode 100644
index 00000000..cebe4095
--- /dev/null
+++ b/apps/web/public/brain.png
Binary files differ
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css
index 875c01e8..394bda43 100644
--- a/apps/web/src/app/globals.css
+++ b/apps/web/src/app/globals.css
@@ -1,3 +1,6 @@
+@import "@radix-ui/colors/gray";
+@import "@radix-ui/colors/gray-dark";
+
@tailwind base;
@tailwind components;
@tailwind utilities;
@@ -17,13 +20,14 @@
}
body {
- color: rgb(var(--foreground-rgb));
+ @apply bg-rgray-1 text-rgray-11;
+ /* color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
- rgb(var(--background-start-rgb));
+ rgb(var(--background-start-rgb)); */
}
@layer utilities {
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 3314e478..5464c1d3 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -1,8 +1,8 @@
import type { Metadata } from "next";
-import { Inter } from "next/font/google";
+import { Roboto } from "next/font/google";
import "./globals.css";
-const inter = Inter({ subsets: ["latin"] });
+const roboto = Roboto({ weight: ["300", "400", "500"], subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
@@ -16,7 +16,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
- <body className={inter.className}>{children}</body>
+ <body className={roboto.className}>{children}</body>
</html>
);
}
diff --git a/apps/web/src/app/ui/page.tsx b/apps/web/src/app/ui/page.tsx
new file mode 100644
index 00000000..2f7c6a4b
--- /dev/null
+++ b/apps/web/src/app/ui/page.tsx
@@ -0,0 +1,9 @@
+import Sidebar from "@/components/Sidebar";
+
+export default async function Home() {
+ return (
+ <>
+ <Sidebar />
+ </>
+ );
+}
diff --git a/apps/web/src/components/QueryAI.tsx b/apps/web/src/components/QueryAI.tsx
index 811dd899..3cb14178 100644
--- a/apps/web/src/components/QueryAI.tsx
+++ b/apps/web/src/components/QueryAI.tsx
@@ -82,6 +82,11 @@ function QueryAI() {
const response = await fetch(`/api/query?q=${input}`);
+ if (response.status !== 200) {
+ setIsAiLoading(false);
+ return;
+ }
+
if (response.body) {
let reader = response.body.getReader();
let decoder = new TextDecoder('utf-8');
diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx
new file mode 100644
index 00000000..0644d779
--- /dev/null
+++ b/apps/web/src/components/Sidebar.tsx
@@ -0,0 +1,145 @@
+"use client";
+import { StoredContent } from "@/server/db/schema";
+import {
+ Plus,
+ MoreHorizontal,
+ ArrowUpRight,
+ Edit3,
+ Trash2,
+} from "lucide-react";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "./ui/dropdown-menu";
+
+import { useState, useEffect, useRef } from "react";
+
+export default function Sidebar() {
+ const websties: StoredContent[] = [
+ {
+ id: 1,
+ content: "",
+ title: "Visual Studio Code",
+ url: "https://code.visualstudio.com",
+ description: "",
+ image: "https://code.visualstudio.com/favicon.ico",
+ baseUrl: "https://code.visualstudio.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 1,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ ];
+
+ return (
+ <aside className="bg-rgray-3 flex h-screen w-[25%] flex-col items-start justify-between py-5 pb-[50vh] font-light">
+ <div className="flex items-center justify-center gap-1 px-5 text-xl font-normal">
+ <img src="/brain.png" alt="logo" className="h-10 w-10" />
+ SuperMemory
+ </div>
+ <div className="flex w-full flex-col items-start justify-center p-2">
+ <h1 className="mb-1 flex w-full items-center justify-center px-3 font-normal">
+ Websites
+ <button className="ml-auto ">
+ <Plus className="h-4 w-4 min-w-4" />
+ </button>
+ </h1>
+ {websties.map((item) => (
+ <ListItem key={item.id} item={item} />
+ ))}
+ </div>
+ </aside>
+ );
+}
+
+export const ListItem: React.FC<{ item: StoredContent }> = ({ item }) => {
+ const [isEditing, setIsEditing] = useState(false);
+ const editInputRef = useRef<HTMLInputElement>(null);
+
+ useEffect(() => {
+ if (isEditing) {
+ setTimeout(() => {
+ editInputRef.current?.focus();
+ }, 500);
+ }
+ }, [isEditing]);
+
+ return (
+ <div className="hover:bg-rgray-5 focus-within:bg-rgray-5 flex w-full items-center rounded-full py-1 pl-3 pr-2 transition [&:hover>a>[data-upright-icon]]:block [&:hover>a>img]:hidden [&:hover>button]:opacity-100">
+ <a
+ href={item.url}
+ target="_blank"
+ onClick={(e) => isEditing && e.preventDefault()}
+ className="flex w-[90%] items-center gap-2 focus:outline-none"
+ >
+ {isEditing ? (
+ <Edit3 className="h-4 w-4" strokeWidth={1.5} />
+ ) : (
+ <>
+ <img
+ src={item.image ?? "/brain.png"}
+ alt={item.title ?? "Untitiled website"}
+ className="h-4 w-4"
+ />
+ <ArrowUpRight
+ data-upright-icon
+ className="hidden h-4 w-4 min-w-4 scale-125"
+ strokeWidth={1.5}
+ />
+ </>
+ )}
+ {isEditing ? (
+ <input
+ ref={editInputRef}
+ autoFocus
+ className="text-rgray-12 w-full bg-transparent focus:outline-none"
+ placeholder={item.title ?? "Untitled website"}
+ onBlur={(e) => setIsEditing(false)}
+ onKeyDown={(e) => e.key === "Escape" && setIsEditing(false)}
+ />
+ ) : (
+ <span className="w-full truncate text-nowrap">
+ {item.title ?? "Untitled website"}
+ </span>
+ )}
+ </a>
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <button className="ml-auto w-4 min-w-4 rounded-[0.15rem] opacity-0 focus:opacity-100 focus:outline-none">
+ <MoreHorizontal className="h-4 w-4 min-w-4" />
+ </button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent className="w-5">
+ <DropdownMenuItem onClick={() => window.open(item.url)}>
+ <ArrowUpRight
+ className="mr-2 h-4 w-4 scale-125"
+ strokeWidth={1.5}
+ />
+ Open
+ </DropdownMenuItem>
+ <DropdownMenuItem
+ onClick={(e) => {
+ setIsEditing(true);
+ }}
+ >
+ <Edit3 className="mr-2 h-4 w-4 " strokeWidth={1.5} />
+ Edit
+ </DropdownMenuItem>
+ <DropdownMenuItem className="focus:bg-red-100 focus:text-red-400 dark:focus:bg-red-100/10">
+ <Trash2 className="mr-2 h-4 w-4 " strokeWidth={1.5} />
+ Delete
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ </div>
+ );
+};
diff --git a/apps/web/src/components/ui/dropdown-menu.tsx b/apps/web/src/components/ui/dropdown-menu.tsx
index 4243e7f2..375662bb 100644
--- a/apps/web/src/components/ui/dropdown-menu.tsx
+++ b/apps/web/src/components/ui/dropdown-menu.tsx
@@ -1,27 +1,27 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
-import { Check, ChevronRight, Circle } from "lucide-react"
+import * as React from "react";
+import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
+import { Check, ChevronRight, Circle } from "lucide-react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
-const DropdownMenu = DropdownMenuPrimitive.Root
+const DropdownMenu = DropdownMenuPrimitive.Root;
-const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
+const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
-const DropdownMenuGroup = DropdownMenuPrimitive.Group
+const DropdownMenuGroup = DropdownMenuPrimitive.Group;
-const DropdownMenuPortal = DropdownMenuPrimitive.Portal
+const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
-const DropdownMenuSub = DropdownMenuPrimitive.Sub
+const DropdownMenuSub = DropdownMenuPrimitive.Sub;
-const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
+const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
- inset?: boolean
+ inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
@@ -29,16 +29,16 @@ const DropdownMenuSubTrigger = React.forwardRef<
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-gray-100 data-[state=open]:bg-gray-100 dark:focus:bg-gray-800 dark:data-[state=open]:bg-gray-800",
inset && "pl-8",
- className
+ className,
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
-))
+));
DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName
+ DropdownMenuPrimitive.SubTrigger.displayName;
const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
@@ -47,14 +47,14 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-gray-200 bg-white p-1 text-gray-950 shadow-lg 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 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50",
- className
+ "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 z-50 min-w-[8rem] overflow-hidden rounded-md border border-gray-200 bg-white p-1 text-gray-950 shadow-lg dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50",
+ className,
)}
{...props}
/>
-))
+));
DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName
+ DropdownMenuPrimitive.SubContent.displayName;
const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
@@ -65,32 +65,32 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-gray-200 bg-white p-1 text-gray-950 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 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50",
- className
+ "data-[state=open]:animate-in bg-rgray-3 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 border-rgray-6 text-rgray-11 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md",
+ className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
-))
-DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
+));
+DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
- inset?: boolean
+ inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-gray-100 focus:text-gray-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-gray-800 dark:focus:text-gray-50",
+ "focus:bg-rgray-4 focus:text-rgray-12 relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 ",
inset && "pl-8",
- className
+ className,
)}
{...props}
/>
-))
-DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
+));
+DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
@@ -100,7 +100,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-gray-100 focus:text-gray-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-gray-800 dark:focus:text-gray-50",
- className
+ className,
)}
checked={checked}
{...props}
@@ -112,9 +112,9 @@ const DropdownMenuCheckboxItem = React.forwardRef<
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
-))
+));
DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName
+ DropdownMenuPrimitive.CheckboxItem.displayName;
const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
@@ -124,7 +124,7 @@ const DropdownMenuRadioItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-gray-100 focus:text-gray-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-gray-800 dark:focus:text-gray-50",
- className
+ className,
)}
{...props}
>
@@ -135,13 +135,13 @@ const DropdownMenuRadioItem = React.forwardRef<
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
-))
-DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
+));
+DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
- inset?: boolean
+ inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
@@ -149,12 +149,12 @@ const DropdownMenuLabel = React.forwardRef<
className={cn(
"px-2 py-1.5 text-sm font-semibold",
inset && "pl-8",
- className
+ className,
)}
{...props}
/>
-))
-DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
+));
+DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
@@ -165,8 +165,8 @@ const DropdownMenuSeparator = React.forwardRef<
className={cn("-mx-1 my-1 h-px bg-gray-100 dark:bg-gray-800", className)}
{...props}
/>
-))
-DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
+));
+DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
const DropdownMenuShortcut = ({
className,
@@ -177,9 +177,9 @@ const DropdownMenuShortcut = ({
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
{...props}
/>
- )
-}
-DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
+ );
+};
+DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export {
DropdownMenu,
@@ -197,4 +197,4 @@ export {
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
-}
+};
diff --git a/apps/web/src/server/db/schema.ts b/apps/web/src/server/db/schema.ts
index d473ef2d..55a2ea1e 100644
--- a/apps/web/src/server/db/schema.ts
+++ b/apps/web/src/server/db/schema.ts
@@ -6,7 +6,7 @@ import {
sqliteTableCreator,
text,
integer,
- unique
+ unique,
} from "drizzle-orm/sqlite-core";
/**
@@ -21,7 +21,9 @@ export const users = createTable("user", {
id: text("id", { length: 255 }).notNull().primaryKey(),
name: text("name", { length: 255 }),
email: text("email", { length: 255 }).notNull(),
- emailVerified: int("emailVerified", { mode: "timestamp" }).default(sql`CURRENT_TIMESTAMP`),
+ emailVerified: int("emailVerified", { mode: "timestamp" }).default(
+ sql`CURRENT_TIMESTAMP`,
+ ),
image: text("image", { length: 255 }),
});
@@ -34,7 +36,9 @@ export const accounts = createTable(
"account",
{
id: integer("id").notNull().primaryKey({ autoIncrement: true }),
- userId: text("userId", { length: 255 }).notNull().references(() => users.id),
+ userId: text("userId", { length: 255 })
+ .notNull()
+ .references(() => users.id),
type: text("type", { length: 255 }).notNull(),
provider: text("provider", { length: 255 }).notNull(),
providerAccountId: text("providerAccountId", { length: 255 }).notNull(),
@@ -50,7 +54,7 @@ export const accounts = createTable(
},
(account) => ({
userIdIdx: index("account_userId_idx").on(account.userId),
- })
+ }),
);
export const sessions = createTable(
@@ -58,12 +62,14 @@ export const sessions = createTable(
{
id: integer("id").notNull().primaryKey({ autoIncrement: true }),
sessionToken: text("sessionToken", { length: 255 }).notNull(),
- userId: text("userId", { length: 255 }).notNull().references(() => users.id),
+ userId: text("userId", { length: 255 })
+ .notNull()
+ .references(() => users.id),
expires: int("expires", { mode: "timestamp" }).notNull(),
},
(session) => ({
userIdIdx: index("session_userId_idx").on(session.userId),
- })
+ }),
);
export const verificationTokens = createTable(
@@ -75,19 +81,29 @@ export const verificationTokens = createTable(
},
(vt) => ({
compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
- })
+ }),
);
export const userStoredContent = createTable(
"userStoredContent",
{
- userId: text("userId").notNull().references(() => users.id),
- contentId: integer("contentId").notNull().references(() => storedContent.id),
+ userId: text("userId")
+ .notNull()
+ .references(() => users.id),
+ contentId: integer("contentId")
+ .notNull()
+ .references(() => storedContent.id),
},
(usc) => ({
- userContentIdx: index("userStoredContent_idx").on(usc.userId, usc.contentId),
- uniqueUserContent: unique("unique_user_content").on(usc.userId, usc.contentId),
- })
+ userContentIdx: index("userStoredContent_idx").on(
+ usc.userId,
+ usc.contentId,
+ ),
+ uniqueUserContent: unique("unique_user_content").on(
+ usc.userId,
+ usc.contentId,
+ ),
+ }),
);
export const storedContent = createTable(
@@ -106,5 +122,7 @@ export const storedContent = createTable(
urlIdx: index("storedContent_url_idx").on(sc.url),
savedAtIdx: index("storedContent_savedAt_idx").on(sc.savedAt),
titleInx: index("storedContent_title_idx").on(sc.title),
- })
-); \ No newline at end of file
+ }),
+);
+
+export type StoredContent = typeof storedContent.$inferSelect;
diff --git a/apps/web/tailwind.config.ts b/apps/web/tailwind.config.ts
index 37157fdc..e7d6dd55 100644
--- a/apps/web/tailwind.config.ts
+++ b/apps/web/tailwind.config.ts
@@ -13,6 +13,22 @@ const config: Config = {
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
+ colors: {
+ rgray: {
+ 1: "var(--gray-1)",
+ 2: "var(--gray-2)",
+ 3: "var(--gray-3)",
+ 4: "var(--gray-4)",
+ 5: "var(--gray-5)",
+ 6: "var(--gray-6)",
+ 7: "var(--gray-7)",
+ 8: "var(--gray-8)",
+ 9: "var(--gray-9)",
+ 10: "var(--gray-10)",
+ 11: "var(--gray-11)",
+ 12: "var(--gray-12)",
+ },
+ },
},
},
plugins: [require("tailwindcss-animate")],
diff --git a/package.json b/package.json
index 0cff2728..145a2384 100644
--- a/package.json
+++ b/package.json
@@ -39,8 +39,10 @@
"@cloudflare/ai": "^1.0.52",
"@cloudflare/next-on-pages-next-dev": "^0.0.1",
"@crxjs/vite-plugin": "^1.0.14",
+ "@google/generative-ai": "^0.3.1",
"@heroicons/react": "^2.1.1",
"@langchain/cloudflare": "^0.0.3",
+ "@radix-ui/colors": "^3.0.0",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
"@t3-oss/env-nextjs": "^0.9.2",