aboutsummaryrefslogtreecommitdiff
path: root/apps/web
diff options
context:
space:
mode:
authorYash <[email protected]>2024-04-11 04:52:44 +0000
committerYash <[email protected]>2024-04-11 04:52:44 +0000
commit6dcc7d18c9be5e3a5e0a3ff60668424ee0158b4e (patch)
tree179aa936536510cc707368fc7c330c4c7fbdc3f8 /apps/web
parentnovel editor (diff)
parentsave user ID with url to ensure that same website can be saved by users (diff)
downloadsupermemory-new-ui.tar.xz
supermemory-new-ui.zip
Merge branch 'main' of https://github.com/Dhravya/supermemory into new-uinew-ui
Diffstat (limited to 'apps/web')
-rw-r--r--apps/web/.eslintrc.json6
-rw-r--r--apps/web/README.md14
-rw-r--r--apps/web/components.json30
-rw-r--r--apps/web/drizzle.config.ts2
-rw-r--r--apps/web/env.d.ts18
-rw-r--r--apps/web/next.config.mjs4
-rw-r--r--apps/web/package.json2
-rw-r--r--apps/web/public/icons/black_without_bg.pngbin0 -> 89265 bytes
-rw-r--r--apps/web/public/icons/brain-icon.png (renamed from apps/web/public/brain.png)bin3131 -> 3131 bytes
-rw-r--r--apps/web/public/icons/logo_bw_without_bg.pngbin0 -> 41595 bytes
-rw-r--r--apps/web/public/icons/logo_without_bg.pngbin0 -> 218280 bytes
-rw-r--r--apps/web/public/icons/white_without_bg.pngbin0 -> 89822 bytes
-rw-r--r--apps/web/public/icons/wordmark.pngbin0 -> 144083 bytes
-rw-r--r--apps/web/public/next.svg1
-rw-r--r--apps/web/public/vercel.svg1
-rw-r--r--apps/web/src/app/MessagePoster.tsx14
-rw-r--r--apps/web/src/app/api/ask/route.ts80
-rw-r--r--apps/web/src/app/api/chat/route.ts139
-rw-r--r--apps/web/src/app/api/me/route.ts45
-rw-r--r--apps/web/src/app/api/query/route.ts88
-rw-r--r--apps/web/src/app/api/store/route.ts219
-rw-r--r--apps/web/src/app/content.tsx14
-rw-r--r--apps/web/src/app/globals.css4
-rw-r--r--apps/web/src/app/layout.tsx5
-rw-r--r--apps/web/src/app/page.tsx34
-rw-r--r--apps/web/src/app/privacy/page.tsx6
-rw-r--r--apps/web/src/app/privacy/privacy.ts2
-rw-r--r--apps/web/src/components/Main.tsx8
-rw-r--r--apps/web/src/components/QueryAI.tsx139
-rw-r--r--apps/web/src/components/SearchResults.tsx40
-rw-r--r--apps/web/src/components/Sidebar/CategoryItem.tsx138
-rw-r--r--apps/web/src/components/Sidebar/FilterCombobox.tsx36
-rw-r--r--apps/web/src/components/Sidebar/index.tsx63
-rw-r--r--apps/web/src/components/WordMark.tsx12
-rw-r--r--apps/web/src/components/ui/avatar.tsx26
-rw-r--r--apps/web/src/components/ui/badge.tsx14
-rw-r--r--apps/web/src/components/ui/button.tsx26
-rw-r--r--apps/web/src/components/ui/card.tsx41
-rw-r--r--apps/web/src/components/ui/label.tsx20
-rw-r--r--apps/web/src/contexts/MemoryContext.tsx8
-rw-r--r--apps/web/src/env.js7
-rw-r--r--apps/web/src/lib/searchParams.ts22
-rw-r--r--apps/web/src/server/auth.ts6
-rw-r--r--apps/web/src/server/db/index.ts7
-rw-r--r--apps/web/src/server/db/schema.ts15
-rw-r--r--apps/web/src/server/helpers.ts66
46 files changed, 881 insertions, 541 deletions
diff --git a/apps/web/.eslintrc.json b/apps/web/.eslintrc.json
index d8649b49..abd7bea7 100644
--- a/apps/web/.eslintrc.json
+++ b/apps/web/.eslintrc.json
@@ -3,7 +3,5 @@
"next/core-web-vitals",
"plugin:eslint-plugin-next-on-pages/recommended"
],
- "plugins": [
- "eslint-plugin-next-on-pages"
- ]
-} \ No newline at end of file
+ "plugins": ["eslint-plugin-next-on-pages"]
+}
diff --git a/apps/web/README.md b/apps/web/README.md
index c81448e4..5164b159 100644
--- a/apps/web/README.md
+++ b/apps/web/README.md
@@ -19,11 +19,12 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
## Cloudflare integration
Besides the `dev` script mentioned above `c3` has added a few extra scripts that allow you to integrate the application with the [Cloudflare Pages](https://pages.cloudflare.com/) environment, these are:
- - `pages:build` to build the application for Pages using the [`@cloudflare/next-on-pages`](https://github.com/cloudflare/next-on-pages) CLI
- - `preview` to locally preview your Pages application using the [Wrangler](https://developers.cloudflare.com/workers/wrangler/) CLI
- - `deploy` to deploy your Pages application using the [Wrangler](https://developers.cloudflare.com/workers/wrangler/) CLI
-> __Note:__ while the `dev` script is optimal for local development you should preview your Pages application as well (periodically or before deployments) in order to make sure that it can properly work in the Pages environment (for more details see the [`@cloudflare/next-on-pages` recommended workflow](https://github.com/cloudflare/next-on-pages/blob/05b6256/internal-packages/next-dev/README.md#recommended-workflow))
+- `pages:build` to build the application for Pages using the [`@cloudflare/next-on-pages`](https://github.com/cloudflare/next-on-pages) CLI
+- `preview` to locally preview your Pages application using the [Wrangler](https://developers.cloudflare.com/workers/wrangler/) CLI
+- `deploy` to deploy your Pages application using the [Wrangler](https://developers.cloudflare.com/workers/wrangler/) CLI
+
+> **Note:** while the `dev` script is optimal for local development you should preview your Pages application as well (periodically or before deployments) in order to make sure that it can properly work in the Pages environment (for more details see the [`@cloudflare/next-on-pages` recommended workflow](https://github.com/cloudflare/next-on-pages/blob/05b6256/internal-packages/next-dev/README.md#recommended-workflow))
### Bindings
@@ -35,13 +36,14 @@ You can use bindings during development, when previewing locally your applicatio
- To use bindings in the preview mode you need to add them to the `pages:preview` script accordingly to the `wrangler pages dev` command. For more details see its [documentation](https://developers.cloudflare.com/workers/wrangler/commands/#dev-1) or the [Pages Bindings documentation](https://developers.cloudflare.com/pages/functions/bindings/).
-- To use bindings in the deployed application you will need to configure them in the Cloudflare [dashboard](https://dash.cloudflare.com/). For more details see the [Pages Bindings documentation](https://developers.cloudflare.com/pages/functions/bindings/).
+- To use bindings in the deployed application you will need to configure them in the Cloudflare [dashboard](https://dash.cloudflare.com/). For more details see the [Pages Bindings documentation](https://developers.cloudflare.com/pages/functions/bindings/).
#### KV Example
`c3` has added for you an example showing how you can use a KV binding.
In order to enable the example:
+
- Search for javascript/typescript lines containing the following comment:
```ts
// KV Example:
@@ -65,4 +67,4 @@ In order to enable the example:
After doing this you can run the `dev` or `preview` script and visit the `/api/hello` route to see the example in action.
-Finally, if you also want to see the example work in the deployed application make sure to add a `MY_KV_NAMESPACE` binding to your Pages application in its [dashboard kv bindings settings section](https://dash.cloudflare.com/?to=/:account/pages/view/:pages-project/settings/functions#kv_namespace_bindings_section). After having configured it make sure to re-deploy your application. \ No newline at end of file
+Finally, if you also want to see the example work in the deployed application make sure to add a `MY_KV_NAMESPACE` binding to your Pages application in its [dashboard kv bindings settings section](https://dash.cloudflare.com/?to=/:account/pages/view/:pages-project/settings/functions#kv_namespace_bindings_section). After having configured it make sure to re-deploy your application.
diff --git a/apps/web/components.json b/apps/web/components.json
index 483d0a6e..c9b7b380 100644
--- a/apps/web/components.json
+++ b/apps/web/components.json
@@ -1,16 +1,16 @@
{
- "$schema": "https://ui.shadcn.com/schema.json",
- "style": "default",
- "rsc": true,
- "tsx": true,
- "tailwind": {
- "config": "tailwind.config.ts",
- "css": "src/app/globals.css",
- "baseColor": "gray",
- "cssVariables": false
- },
- "aliases": {
- "utils": "@/lib/utils",
- "components": "@/components"
- }
- } \ No newline at end of file
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "src/app/globals.css",
+ "baseColor": "gray",
+ "cssVariables": false
+ },
+ "aliases": {
+ "utils": "@/lib/utils",
+ "components": "@/components"
+ }
+}
diff --git a/apps/web/drizzle.config.ts b/apps/web/drizzle.config.ts
index fd2800e4..bacbf9b6 100644
--- a/apps/web/drizzle.config.ts
+++ b/apps/web/drizzle.config.ts
@@ -7,5 +7,5 @@ export default {
wranglerConfigPath: "./wrangler.toml",
dbName: "dev-d1-anycontext",
},
- out: 'drizzle'
+ out: "drizzle",
} satisfies Config;
diff --git a/apps/web/env.d.ts b/apps/web/env.d.ts
index 1d23e2bc..ece89e76 100644
--- a/apps/web/env.d.ts
+++ b/apps/web/env.d.ts
@@ -1,12 +1,12 @@
declare global {
- namespace NodeJS {
- interface ProcessEnv {
- [key: string]: string | undefined;
- DATABASE: D1Database;
- VECTORIZE_INDEX: VectorizeIndex;
- AI: any
- }
+ namespace NodeJS {
+ interface ProcessEnv {
+ [key: string]: string | undefined;
+ DATABASE: D1Database;
+ VECTORIZE_INDEX: VectorizeIndex;
+ AI: any;
}
}
-
- export { }; \ No newline at end of file
+}
+
+export {};
diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs
index 3713aeab..b0c1476c 100644
--- a/apps/web/next.config.mjs
+++ b/apps/web/next.config.mjs
@@ -1,9 +1,9 @@
-import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev';
+import { setupDevPlatform } from "@cloudflare/next-on-pages/next-dev";
// Here we use the @cloudflare/next-on-pages next-dev module to allow us to use bindings during local development
// (when running the application with `next dev`), for more information see:
// https://github.com/cloudflare/next-on-pages/blob/5712c57ea7/internal-packages/next-dev/README.md
-if (process.env.NODE_ENV === 'development') {
+if (process.env.NODE_ENV === "development") {
await setupDevPlatform();
}
diff --git a/apps/web/package.json b/apps/web/package.json
index 7ca13deb..be20fba7 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -51,4 +51,4 @@
"vercel": "^33.6.2",
"wrangler": "^3.41.0"
}
-} \ No newline at end of file
+}
diff --git a/apps/web/public/icons/black_without_bg.png b/apps/web/public/icons/black_without_bg.png
new file mode 100644
index 00000000..1bd7582d
--- /dev/null
+++ b/apps/web/public/icons/black_without_bg.png
Binary files differ
diff --git a/apps/web/public/brain.png b/apps/web/public/icons/brain-icon.png
index cebe4095..cebe4095 100644
--- a/apps/web/public/brain.png
+++ b/apps/web/public/icons/brain-icon.png
Binary files differ
diff --git a/apps/web/public/icons/logo_bw_without_bg.png b/apps/web/public/icons/logo_bw_without_bg.png
new file mode 100644
index 00000000..167b9aea
--- /dev/null
+++ b/apps/web/public/icons/logo_bw_without_bg.png
Binary files differ
diff --git a/apps/web/public/icons/logo_without_bg.png b/apps/web/public/icons/logo_without_bg.png
new file mode 100644
index 00000000..54313f60
--- /dev/null
+++ b/apps/web/public/icons/logo_without_bg.png
Binary files differ
diff --git a/apps/web/public/icons/white_without_bg.png b/apps/web/public/icons/white_without_bg.png
new file mode 100644
index 00000000..4f7d8d42
--- /dev/null
+++ b/apps/web/public/icons/white_without_bg.png
Binary files differ
diff --git a/apps/web/public/icons/wordmark.png b/apps/web/public/icons/wordmark.png
new file mode 100644
index 00000000..6fb4ee7b
--- /dev/null
+++ b/apps/web/public/icons/wordmark.png
Binary files differ
diff --git a/apps/web/public/next.svg b/apps/web/public/next.svg
deleted file mode 100644
index 5174b28c..00000000
--- a/apps/web/public/next.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg> \ No newline at end of file
diff --git a/apps/web/public/vercel.svg b/apps/web/public/vercel.svg
deleted file mode 100644
index d2f84222..00000000
--- a/apps/web/public/vercel.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg> \ No newline at end of file
diff --git a/apps/web/src/app/MessagePoster.tsx b/apps/web/src/app/MessagePoster.tsx
index 64dc89fd..9b7011a8 100644
--- a/apps/web/src/app/MessagePoster.tsx
+++ b/apps/web/src/app/MessagePoster.tsx
@@ -1,21 +1,21 @@
-'use client';
+"use client";
-import { useEffect } from 'react';
+import { useEffect } from "react";
function MessagePoster({ jwt }: { jwt: string }) {
useEffect(() => {
- if (typeof window === 'undefined') return;
- window.postMessage({ jwt }, '*');
+ if (typeof window === "undefined") return;
+ window.postMessage({ jwt }, "*");
}, [jwt]);
return (
<button
onClick={() => {
- if (typeof window === 'undefined') return;
- window.postMessage({ jwt }, '*');
+ if (typeof window === "undefined") return;
+ window.postMessage({ jwt }, "*");
}}
>
- Send message
+ Extension Auth
</button>
);
}
diff --git a/apps/web/src/app/api/ask/route.ts b/apps/web/src/app/api/ask/route.ts
index cad7a671..89123ac9 100644
--- a/apps/web/src/app/api/ask/route.ts
+++ b/apps/web/src/app/api/ask/route.ts
@@ -7,42 +7,62 @@ import { env } from "@/env";
export const runtime = "edge";
export async function POST(req: NextRequest) {
- const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", "");
+ const token =
+ req.cookies.get("next-auth.session-token")?.value ??
+ req.cookies.get("__Secure-authjs.session-token")?.value ??
+ req.cookies.get("authjs.session-token")?.value ??
+ req.headers.get("Authorization")?.replace("Bearer ", "");
- const sessionData = await db.select().from(sessions).where(eq(sessions.sessionToken, token!))
+ const sessionData = await db
+ .select()
+ .from(sessions)
+ .where(eq(sessions.sessionToken, token!));
- if (!sessionData || sessionData.length === 0) {
- return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 });
- }
+ if (!sessionData || sessionData.length === 0) {
+ return new Response(
+ JSON.stringify({ message: "Invalid Key, session not found." }),
+ { status: 404 },
+ );
+ }
- const user = await db.select().from(users).where(eq(users.id, sessionData[0].userId)).limit(1)
+ const user = await db
+ .select()
+ .from(users)
+ .where(eq(users.id, sessionData[0].userId))
+ .limit(1);
- if (!user || user.length === 0) {
- return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 });
- }
+ if (!user || user.length === 0) {
+ return NextResponse.json(
+ { message: "Invalid Key, session not found." },
+ { status: 404 },
+ );
+ }
- const body = await req.json() as {
- query: string;
- }
+ const body = (await req.json()) as {
+ query: string;
+ };
- const resp = await fetch(`https://cf-ai-backend.dhravya.workers.dev/ask`, {
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- },
- method: "POST",
- body: JSON.stringify({
- query: body.query,
- }),
- })
+ const resp = await fetch(`https://cf-ai-backend.dhravya.workers.dev/ask`, {
+ headers: {
+ "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
+ },
+ method: "POST",
+ body: JSON.stringify({
+ query: body.query,
+ }),
+ });
- if (resp.status !== 200 || !resp.ok) {
- const errorData = await resp.json();
- return new Response(JSON.stringify({ message: "Error in CF function", error: errorData }), { status: resp.status });
- }
+ if (resp.status !== 200 || !resp.ok) {
+ const errorData = await resp.json();
+ return new Response(
+ JSON.stringify({ message: "Error in CF function", error: errorData }),
+ { status: resp.status },
+ );
+ }
- // Stream the response back to the client
- const { readable, writable } = new TransformStream();
- resp && resp.body!.pipeTo(writable);
+ // Stream the response back to the client
+ const { readable, writable } = new TransformStream();
+ resp && resp.body!.pipeTo(writable);
- return new Response(readable, { status: 200 });
-} \ No newline at end of file
+ return new Response(readable, { status: 200 });
+}
diff --git a/apps/web/src/app/api/chat/route.ts b/apps/web/src/app/api/chat/route.ts
index aec5b0ea..bc7a4ee4 100644
--- a/apps/web/src/app/api/chat/route.ts
+++ b/apps/web/src/app/api/chat/route.ts
@@ -8,60 +8,85 @@ import { ChatHistory } from "../../../../types/memory";
export const runtime = "edge";
export async function POST(req: NextRequest) {
- const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", "");
-
- const sessionData = await db.select().from(sessions).where(eq(sessions.sessionToken, token!))
-
- if (!sessionData || sessionData.length === 0) {
- return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 });
- }
-
- const user = await db.select().from(users).where(eq(users.id, sessionData[0].userId)).limit(1)
-
- if (!user || user.length === 0) {
- return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 });
- }
-
- const session = { session: sessionData[0], user: user[0] }
-
- const query = new URL(req.url).searchParams.get("q");
- const spaces = new URL(req.url).searchParams.get("spaces");
-
- const sourcesOnly = new URL(req.url).searchParams.get("sourcesOnly") ?? "false";
-
- const chatHistory = await req.json() as {
- chatHistory: ChatHistory[]
- };
-
- console.log("CHathistory", chatHistory)
-
- if (!query) {
- return new Response(JSON.stringify({ message: "Invalid query" }), { status: 400 });
- }
-
-
- const resp = await fetch(`https://cf-ai-backend.dhravya.workers.dev/chat?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}&spaces=${spaces}`, {
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- },
- method: "POST",
- body: JSON.stringify({
- chatHistory: chatHistory.chatHistory ?? []
- })
- })
-
- console.log(resp.status)
- console.log(resp.statusText)
-
- if (resp.status !== 200 || !resp.ok) {
- const errorData = await resp.json();
- console.log(errorData)
- return new Response(JSON.stringify({ message: "Error in CF function", error: errorData }), { status: resp.status });
- }
-
- // Stream the response back to the client
- const { readable, writable } = new TransformStream();
- resp && resp.body!.pipeTo(writable);
-
- return new Response(readable, { status: 200 });
-} \ No newline at end of file
+ const token =
+ req.cookies.get("next-auth.session-token")?.value ??
+ req.cookies.get("__Secure-authjs.session-token")?.value ??
+ req.cookies.get("authjs.session-token")?.value ??
+ req.headers.get("Authorization")?.replace("Bearer ", "");
+
+ const sessionData = await db
+ .select()
+ .from(sessions)
+ .where(eq(sessions.sessionToken, token!));
+
+ if (!sessionData || sessionData.length === 0) {
+ return new Response(
+ JSON.stringify({ message: "Invalid Key, session not found." }),
+ { status: 404 },
+ );
+ }
+
+ const user = await db
+ .select()
+ .from(users)
+ .where(eq(users.id, sessionData[0].userId))
+ .limit(1);
+
+ if (!user || user.length === 0) {
+ return NextResponse.json(
+ { message: "Invalid Key, session not found." },
+ { status: 404 },
+ );
+ }
+
+ const session = { session: sessionData[0], user: user[0] };
+
+ const query = new URL(req.url).searchParams.get("q");
+ const spaces = new URL(req.url).searchParams.get("spaces");
+
+ const sourcesOnly =
+ new URL(req.url).searchParams.get("sourcesOnly") ?? "false";
+
+ const chatHistory = (await req.json()) as {
+ chatHistory: ChatHistory[];
+ };
+
+ console.log("CHathistory", chatHistory);
+
+ if (!query) {
+ return new Response(JSON.stringify({ message: "Invalid query" }), {
+ status: 400,
+ });
+ }
+
+ const resp = await fetch(
+ `https://cf-ai-backend.dhravya.workers.dev/chat?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}&spaces=${spaces}`,
+ {
+ headers: {
+ "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
+ },
+ method: "POST",
+ body: JSON.stringify({
+ chatHistory: chatHistory.chatHistory ?? [],
+ }),
+ },
+ );
+
+ console.log(resp.status);
+ console.log(resp.statusText);
+
+ if (resp.status !== 200 || !resp.ok) {
+ const errorData = await resp.json();
+ console.log(errorData);
+ return new Response(
+ JSON.stringify({ message: "Error in CF function", error: errorData }),
+ { status: resp.status },
+ );
+ }
+
+ // Stream the response back to the client
+ const { readable, writable } = new TransformStream();
+ resp && resp.body!.pipeTo(writable);
+
+ return new Response(readable, { status: 200 });
+}
diff --git a/apps/web/src/app/api/me/route.ts b/apps/web/src/app/api/me/route.ts
index a2e713e1..6d269872 100644
--- a/apps/web/src/app/api/me/route.ts
+++ b/apps/web/src/app/api/me/route.ts
@@ -7,19 +7,42 @@ import { env } from "@/env";
export const runtime = "edge";
export async function GET(req: NextRequest) {
- const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", "");
+ const token =
+ req.cookies.get("next-auth.session-token")?.value ??
+ req.cookies.get("__Secure-authjs.session-token")?.value ??
+ req.cookies.get("authjs.session-token")?.value ??
+ req.headers.get("Authorization")?.replace("Bearer ", "");
- const session = await db.select().from(sessions).where(eq(sessions.sessionToken, token!))
+ const session = await db
+ .select()
+ .from(sessions)
+ .where(eq(sessions.sessionToken, token!));
- if (!session || session.length === 0) {
- return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 });
- }
+ if (!session || session.length === 0) {
+ return new Response(
+ JSON.stringify({ message: "Invalid Key, session not found." }),
+ { status: 404 },
+ );
+ }
- const user = await db.select().from(users).where(eq(users.id, session[0].userId)).limit(1)
+ const user = await db
+ .select()
+ .from(users)
+ .where(eq(users.id, session[0].userId))
+ .limit(1);
- if (!user || user.length === 0) {
- return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 });
- }
+ if (!user || user.length === 0) {
+ return NextResponse.json(
+ { message: "Invalid Key, session not found." },
+ { status: 404 },
+ );
+ }
- return new Response(JSON.stringify({ message: "OK", data: { session: session[0], user: user[0] } }), { status: 200 });
-} \ No newline at end of file
+ return new Response(
+ JSON.stringify({
+ message: "OK",
+ data: { session: session[0], user: user[0] },
+ }),
+ { status: 200 },
+ );
+}
diff --git a/apps/web/src/app/api/query/route.ts b/apps/web/src/app/api/query/route.ts
index 28f441bc..02bb79da 100644
--- a/apps/web/src/app/api/query/route.ts
+++ b/apps/web/src/app/api/query/route.ts
@@ -7,46 +7,72 @@ import { env } from "@/env";
export const runtime = "edge";
export async function GET(req: NextRequest) {
- const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", "");
+ const token =
+ req.cookies.get("next-auth.session-token")?.value ??
+ req.cookies.get("__Secure-authjs.session-token")?.value ??
+ req.cookies.get("authjs.session-token")?.value ??
+ req.headers.get("Authorization")?.replace("Bearer ", "");
- const sessionData = await db.select().from(sessions).where(eq(sessions.sessionToken, token!))
+ const sessionData = await db
+ .select()
+ .from(sessions)
+ .where(eq(sessions.sessionToken, token!));
- if (!sessionData || sessionData.length === 0) {
- return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 });
- }
+ if (!sessionData || sessionData.length === 0) {
+ return new Response(
+ JSON.stringify({ message: "Invalid Key, session not found." }),
+ { status: 404 },
+ );
+ }
- const user = await db.select().from(users).where(eq(users.id, sessionData[0].userId)).limit(1)
+ const user = await db
+ .select()
+ .from(users)
+ .where(eq(users.id, sessionData[0].userId))
+ .limit(1);
- if (!user || user.length === 0) {
- return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 });
- }
+ if (!user || user.length === 0) {
+ return NextResponse.json(
+ { message: "Invalid Key, session not found." },
+ { status: 404 },
+ );
+ }
- const session = { session: sessionData[0], user: user[0] }
+ const session = { session: sessionData[0], user: user[0] };
- const query = new URL(req.url).searchParams.get("q");
- const sourcesOnly = new URL(req.url).searchParams.get("sourcesOnly") ?? "false";
+ const query = new URL(req.url).searchParams.get("q");
+ const sourcesOnly =
+ new URL(req.url).searchParams.get("sourcesOnly") ?? "false";
- if (!query) {
- return new Response(JSON.stringify({ message: "Invalid query" }), { status: 400 });
- }
+ if (!query) {
+ return new Response(JSON.stringify({ message: "Invalid query" }), {
+ status: 400,
+ });
+ }
- const resp = await fetch(`https://cf-ai-backend.dhravya.workers.dev/query?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}`, {
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- }
- })
+ const resp = await fetch(
+ `https://cf-ai-backend.dhravya.workers.dev/query?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}`,
+ {
+ headers: {
+ "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
+ },
+ },
+ );
- console.log(resp.status)
+ console.log(resp.status);
- if (resp.status !== 200 || !resp.ok) {
- const errorData = await resp.json();
- console.log(errorData)
- return new Response(JSON.stringify({ message: "Error in CF function", error: errorData }), { status: resp.status });
- }
+ if (resp.status !== 200 || !resp.ok) {
+ const errorData = await resp.json();
+ console.log(errorData);
+ return new Response(
+ JSON.stringify({ message: "Error in CF function", error: errorData }),
+ { status: resp.status },
+ );
+ }
- // Stream the response back to the client
- const { readable, writable } = new TransformStream();
- resp && resp.body!.pipeTo(writable);
+ // Stream the response back to the client
+ const { readable, writable } = new TransformStream();
+ resp && resp.body!.pipeTo(writable);
- return new Response(readable, { status: 200 });
-} \ No newline at end of file
+ return new Response(readable, { status: 200 });
+}
diff --git a/apps/web/src/app/api/store/route.ts b/apps/web/src/app/api/store/route.ts
index 06db08b9..ebe23077 100644
--- a/apps/web/src/app/api/store/route.ts
+++ b/apps/web/src/app/api/store/route.ts
@@ -1,6 +1,12 @@
import { db } from "@/server/db";
import { and, eq } from "drizzle-orm";
-import { contentToSpace, sessions, storedContent, users, space } from "@/server/db/schema";
+import {
+ contentToSpace,
+ sessions,
+ storedContent,
+ users,
+ space,
+} from "@/server/db/schema";
import { type NextRequest, NextResponse } from "next/server";
import { env } from "@/env";
import { getMetaData } from "@/server/helpers";
@@ -8,94 +14,123 @@ import { getMetaData } from "@/server/helpers";
export const runtime = "edge";
export async function POST(req: NextRequest) {
- const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", "");
-
- if (!token) {
- return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 });
- }
-
- const sessionData = await db.select().from(sessions).where(eq(sessions.sessionToken, token!))
-
- if (!sessionData || sessionData.length === 0) {
- return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 });
- }
-
- const user = await db.select().from(users).where(eq(users.id, sessionData[0].userId)).limit(1)
-
- if (!user || user.length === 0) {
- return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 });
- }
-
- const session = { session: sessionData[0], user: user[0] }
-
- const data = await req.json() as {
- pageContent: string,
- url: string,
- space?: string
- };
-
- const metadata = await getMetaData(data.url);
-
-
- let id: number | undefined = undefined;
-
- let storeToSpace = data.space
-
- if (!storeToSpace) {
- storeToSpace = 'all'
- }
-
- const storedContentId = await db.insert(storedContent).values({
- content: data.pageContent,
- title: metadata.title,
- description: metadata.description,
- url: data.url,
- baseUrl: metadata.baseUrl,
- image: metadata.image,
- savedAt: new Date(),
- user: session.user.id
- })
-
- id = storedContentId.meta.last_row_id;
-
- if (!id) {
- return NextResponse.json({ message: "Error", error: "Error in CF function" }, { status: 500 });
- }
-
- let spaceID = 0;
-
- const spaceData = await db.select().from(space).where(and(eq(space.name, storeToSpace), eq(space.user, session.user.id))).limit(1)
- spaceID = spaceData[0]?.id
-
- if (!spaceData || spaceData.length === 0) {
- const spaceId = await db.insert(space).values({
- name: storeToSpace,
- user: session.user.id
- })
- spaceID = spaceId.meta.last_row_id;
- }
-
- await db.insert(contentToSpace).values({
- contentId: id as number,
- spaceId: spaceID
- })
-
- const res = await Promise.race([
- fetch("https://cf-ai-backend.dhravya.workers.dev/add", {
- method: "POST",
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- },
- body: JSON.stringify({ ...data, user: session.user.email }),
- }),
- new Promise((_, reject) =>
- setTimeout(() => reject(new Error('Request timed out')), 40000)
- )
- ]) as Response
-
- if (res.status !== 200) {
- return NextResponse.json({ message: "Error", error: "Error in CF function" }, { status: 500 });
- }
-
- return NextResponse.json({ message: "OK", data: "Success" }, { status: 200 });
-} \ No newline at end of file
+ const token =
+ req.cookies.get("next-auth.session-token")?.value ??
+ req.cookies.get("__Secure-authjs.session-token")?.value ??
+ req.cookies.get("authjs.session-token")?.value ??
+ req.headers.get("Authorization")?.replace("Bearer ", "");
+
+ if (!token) {
+ return new Response(
+ JSON.stringify({ message: "Invalid Key, session not found." }),
+ { status: 404 },
+ );
+ }
+
+ const sessionData = await db
+ .select()
+ .from(sessions)
+ .where(eq(sessions.sessionToken, token!));
+
+ if (!sessionData || sessionData.length === 0) {
+ return new Response(
+ JSON.stringify({ message: "Invalid Key, session not found." }),
+ { status: 404 },
+ );
+ }
+
+ const user = await db
+ .select()
+ .from(users)
+ .where(eq(users.id, sessionData[0].userId))
+ .limit(1);
+
+ if (!user || user.length === 0) {
+ return NextResponse.json(
+ { message: "Invalid Key, session not found." },
+ { status: 404 },
+ );
+ }
+
+ const session = { session: sessionData[0], user: user[0] };
+
+ const data = (await req.json()) as {
+ pageContent: string;
+ url: string;
+ space?: string;
+ };
+
+ const metadata = await getMetaData(data.url);
+
+ let id: number | undefined = undefined;
+
+ let storeToSpace = data.space;
+
+ if (!storeToSpace) {
+ storeToSpace = "all";
+ }
+
+ const storedContentId = await db.insert(storedContent).values({
+ content: data.pageContent,
+ title: metadata.title,
+ description: metadata.description,
+ url: data.url,
+ baseUrl: metadata.baseUrl,
+ image: metadata.image,
+ savedAt: new Date(),
+ user: session.user.id,
+ });
+
+ id = storedContentId.meta.last_row_id;
+
+ if (!id) {
+ return NextResponse.json(
+ { message: "Error", error: "Error in CF function" },
+ { status: 500 },
+ );
+ }
+
+ let spaceID = 0;
+
+ const spaceData = await db
+ .select()
+ .from(space)
+ .where(and(eq(space.name, storeToSpace), eq(space.user, session.user.id)))
+ .limit(1);
+ spaceID = spaceData[0]?.id;
+
+ if (!spaceData || spaceData.length === 0) {
+ const spaceId = await db.insert(space).values({
+ name: storeToSpace,
+ user: session.user.id,
+ });
+ spaceID = spaceId.meta.last_row_id;
+ }
+
+ await db.insert(contentToSpace).values({
+ contentId: id as number,
+ spaceId: spaceID,
+ });
+
+ const res = (await Promise.race([
+ fetch("https://cf-ai-backend.dhravya.workers.dev/add", {
+ method: "POST",
+ headers: {
+ "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
+ },
+ body: JSON.stringify({ ...data, user: session.user.email }),
+ }),
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error("Request timed out")), 40000),
+ ),
+ ])) as Response;
+
+ if (res.status !== 200) {
+ return NextResponse.json(
+ { message: "Error", error: "Error in CF function" },
+ { status: 500 },
+ );
+ }
+
+ return NextResponse.json({ message: "OK", data: "Success" }, { status: 200 });
+}
diff --git a/apps/web/src/app/content.tsx b/apps/web/src/app/content.tsx
index 39f2948d..50e0617c 100644
--- a/apps/web/src/app/content.tsx
+++ b/apps/web/src/app/content.tsx
@@ -1,16 +1,16 @@
-'use client';
-import Main from '@/components/Main';
-import Sidebar from '@/components/Sidebar/index';
-import { SessionProvider } from 'next-auth/react';
-import { useState } from 'react';
+"use client";
+import Main from "@/components/Main";
+import Sidebar from "@/components/Sidebar/index";
+import { SessionProvider } from "next-auth/react";
+import { useState } from "react";
-export default function Content() {
+export default function Content({ jwt }: { jwt: string }) {
const [selectedItem, setSelectedItem] = useState<string | null>(null);
return (
<SessionProvider>
<div className="flex w-screen">
- <Sidebar selectChange={setSelectedItem} />
+ <Sidebar jwt={jwt} selectChange={setSelectedItem} />
<Main sidebarOpen={selectedItem !== null} />
</div>
</SessionProvider>
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css
index cedb03dc..23caee5b 100644
--- a/apps/web/src/app/globals.css
+++ b/apps/web/src/app/globals.css
@@ -20,7 +20,7 @@
}
body {
- @apply bg-rgray-2 text-rgray-11 max-h-screen overflow-y-hidden;
+ @apply text-rgray-11 max-h-screen overflow-y-hidden bg-white;
/* color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
@@ -66,7 +66,7 @@ body {
}
.chat-answer h1 {
- @apply text-rgray-11 text-xl font-medium my-5;
+ @apply text-rgray-11 my-5 text-xl font-medium;
}
.chat-answer img {
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 1b204dec..42485461 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -1,8 +1,9 @@
import type { Metadata } from "next";
-import { Roboto } from "next/font/google";
+import { Roboto, Inter } from "next/font/google";
import "./globals.css";
const roboto = Roboto({ weight: ["300", "400", "500"], subsets: ["latin"] });
+const inter = Inter({ weight: ["300", "400", "500"], subsets: ["latin"] });
export const metadata: Metadata = {
title: "Create Next App",
@@ -16,7 +17,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en" className="dark">
- <body className={roboto.className}>
+ <body className={inter.className}>
<div vaul-drawer-wrapper="" className="min-w-screen overflow-x-hidden">
{children}
</div>
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
index 95e18e83..6f9ca753 100644
--- a/apps/web/src/app/page.tsx
+++ b/apps/web/src/app/page.tsx
@@ -59,26 +59,26 @@ export default async function Home() {
const collectedSpaces =
contents.length > 0 ? await transformContent(contents) : [];
- collectedSpaces.push({
- id: 2,
- title: "Test",
- content: [
- {
- id: 1,
- content: "Test",
- title: "Vscode",
- description: "Test",
- url: "https://vscode-remake.vercel.app/",
- savedAt: new Date(),
- baseUrl: "https://vscode-remake.vercel.app/",
- image: "https://vscode-remake.vercel.app/favicon.svg",
- },
- ],
- });
+ // collectedSpaces.push({
+ // id: 2,
+ // title: "Test",
+ // content: [
+ // {
+ // id: 1,
+ // content: "Test",
+ // title: "Vscode",
+ // description: "Test",
+ // url: "https://vscode-remake.vercel.app/",
+ // savedAt: new Date(),
+ // baseUrl: "https://vscode-remake.vercel.app/",
+ // image: "https://vscode-remake.vercel.app/favicon.svg",
+ // },
+ // ],
+ // });
return (
<MemoryProvider spaces={collectedSpaces}>
- <Content />
+ <Content jwt={token} />
{/* <MessagePoster jwt={token} /> */}
</MemoryProvider>
);
diff --git a/apps/web/src/app/privacy/page.tsx b/apps/web/src/app/privacy/page.tsx
index 5d0ae2b8..8d126dff 100644
--- a/apps/web/src/app/privacy/page.tsx
+++ b/apps/web/src/app/privacy/page.tsx
@@ -1,6 +1,6 @@
-import React from 'react';
-import Markdown from 'react-markdown';
-import { policy } from './privacy';
+import React from "react";
+import Markdown from "react-markdown";
+import { policy } from "./privacy";
function Page() {
return (
diff --git a/apps/web/src/app/privacy/privacy.ts b/apps/web/src/app/privacy/privacy.ts
index 3e3df4fb..2034f191 100644
--- a/apps/web/src/app/privacy/privacy.ts
+++ b/apps/web/src/app/privacy/privacy.ts
@@ -46,4 +46,4 @@ If you have any questions about this Privacy Policy, the practices of this site,
-This document was last updated on March 2, 2024.` \ No newline at end of file
+This document was last updated on March 2, 2024.`;
diff --git a/apps/web/src/components/Main.tsx b/apps/web/src/components/Main.tsx
index c621c68f..b34755f9 100644
--- a/apps/web/src/components/Main.tsx
+++ b/apps/web/src/components/Main.tsx
@@ -11,6 +11,7 @@ import { ChatHistory } from "../../types/memory";
import { ChatAnswer, ChatMessage, ChatQuestion } from "./ChatMessage";
import { useRouter, useSearchParams } from "next/navigation";
import { useMemory } from "@/contexts/MemoryContext";
+import WordMark from "./WordMark";
function supportsDVH() {
try {
@@ -293,8 +294,8 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) {
hide ? "" : "main-hidden",
)}
>
- <h1 className="text-rgray-11 mt-auto w-full text-center text-3xl md:mt-0">
- Ask your Second brain
+ <h1 className="text-rgray-11 mt-auto w-full text-center text-3xl font-bold tracking-tight md:mt-0">
+ Ask your second brain
</h1>
<Textarea2
@@ -308,14 +309,13 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) {
duration: 0.2,
}}
textAreaProps={{
- placeholder: "Ask your SuperMemory...",
+ placeholder: "Ask your second brain...",
className:
"h-auto overflow-auto md:h-full md:resize-none text-lg py-0 px-2 pt-2 md:py-0 md:p-5 resize-y text-rgray-11 w-full min-h-[1em]",
value,
autoFocus: true,
onChange: (e) => setValue(e.target.value),
onKeyDown: (e) => {
- console.log(e.key, e.ctrlKey, e.metaKey);
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
onSend();
}
diff --git a/apps/web/src/components/QueryAI.tsx b/apps/web/src/components/QueryAI.tsx
new file mode 100644
index 00000000..894b5d2d
--- /dev/null
+++ b/apps/web/src/components/QueryAI.tsx
@@ -0,0 +1,139 @@
+"use client";
+
+import { Label } from "./ui/label";
+import React, { useEffect, useState } from "react";
+import { Input } from "./ui/input";
+import { Button } from "./ui/button";
+import SearchResults from "./SearchResults";
+
+function QueryAI() {
+ const [searchResults, setSearchResults] = useState<string[]>([]);
+ const [isAiLoading, setIsAiLoading] = useState(false);
+
+ const [aiResponse, setAIResponse] = useState("");
+ const [input, setInput] = useState("");
+ const [toBeParsed, setToBeParsed] = useState("");
+
+ const handleStreamData = (newChunk: string) => {
+ // Append the new chunk to the existing data to be parsed
+ setToBeParsed((prev) => prev + newChunk);
+ };
+
+ useEffect(() => {
+ // Define a function to try parsing the accumulated data
+ const tryParseAccumulatedData = () => {
+ // Attempt to parse the "toBeParsed" state as JSON
+ try {
+ // Split the accumulated data by the known delimiter "\n\n"
+ const parts = toBeParsed.split("\n\n");
+ let remainingData = "";
+
+ // Process each part to extract JSON objects
+ parts.forEach((part, index) => {
+ try {
+ const parsedPart = JSON.parse(part.replace("data: ", "")); // Try to parse the part as JSON
+
+ // If the part is the last one and couldn't be parsed, keep it to accumulate more data
+ if (index === parts.length - 1 && !parsedPart) {
+ remainingData = part;
+ } else if (parsedPart && parsedPart.response) {
+ // If the part is parsable and has the "response" field, update the AI response state
+ setAIResponse((prev) => prev + parsedPart.response);
+ }
+ } catch (error) {
+ // If parsing fails and it's not the last part, it's a malformed JSON
+ if (index !== parts.length - 1) {
+ console.error("Malformed JSON part: ", part);
+ } else {
+ // If it's the last part, it may be incomplete, so keep it
+ remainingData = part;
+ }
+ }
+ });
+
+ // Update the toBeParsed state to only contain the unparsed remainder
+ if (remainingData !== toBeParsed) {
+ setToBeParsed(remainingData);
+ }
+ } catch (error) {
+ console.error("Error parsing accumulated data: ", error);
+ }
+ };
+
+ // Call the parsing function if there's data to be parsed
+ if (toBeParsed) {
+ tryParseAccumulatedData();
+ }
+ }, [toBeParsed]);
+
+ const getSearchResults = async (e: React.FormEvent<HTMLFormElement>) => {
+ e.preventDefault();
+ setIsAiLoading(true);
+
+ const sourcesResponse = await fetch(
+ `/api/query?sourcesOnly=true&q=${input}`,
+ );
+
+ const sourcesInJson = (await sourcesResponse.json()) as {
+ ids: string[];
+ };
+
+ setSearchResults(sourcesInJson.ids);
+
+ 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");
+ let result = "";
+
+ // @ts-ignore
+ reader.read().then(function processText({ done, value }) {
+ if (done) {
+ // setSearchResults(JSON.parse(result.replace('data: ', '')));
+ // setIsAiLoading(false);
+ return;
+ }
+
+ handleStreamData(decoder.decode(value));
+
+ return reader.read().then(processText);
+ });
+ }
+ };
+
+ return (
+ <div className="mx-auto w-full max-w-2xl">
+ <form onSubmit={async (e) => await getSearchResults(e)} className="mt-8">
+ <Label htmlFor="searchInput">Ask your SuperMemory</Label>
+ <div className="flex flex-col space-y-2 md:w-full md:flex-row md:items-center md:space-x-2 md:space-y-0">
+ <Input
+ value={input}
+ onChange={(e) => setInput(e.target.value)}
+ placeholder="Search using AI... ✨"
+ id="searchInput"
+ />
+ <Button
+ disabled={isAiLoading}
+ className="max-w-min md:w-full"
+ type="submit"
+ variant="default"
+ >
+ Ask AI
+ </Button>
+ </div>
+ </form>
+
+ {searchResults && (
+ <SearchResults aiResponse={aiResponse} sources={searchResults} />
+ )}
+ </div>
+ );
+}
+
+export default QueryAI;
diff --git a/apps/web/src/components/SearchResults.tsx b/apps/web/src/components/SearchResults.tsx
new file mode 100644
index 00000000..d348814e
--- /dev/null
+++ b/apps/web/src/components/SearchResults.tsx
@@ -0,0 +1,40 @@
+"use client";
+
+import React from "react";
+import { Card, CardContent } from "./ui/card";
+import Markdown from "react-markdown";
+import remarkGfm from "remark-gfm";
+
+function SearchResults({
+ aiResponse,
+ sources,
+}: {
+ aiResponse: string;
+ sources: string[];
+}) {
+ return (
+ <div
+ style={{
+ backgroundImage: `linear-gradient(to right, #E5D9F2, #CDC1FF)`,
+ }}
+ className="mx-auto mt-4 w-full max-w-2xl space-y-6 rounded-xl border px-4 py-6"
+ >
+ <div className="text-start">
+ <div className="text-xl text-black">
+ <Markdown remarkPlugins={[remarkGfm]}>
+ {aiResponse.replace("</s>", "")}
+ </Markdown>
+ </div>
+ </div>
+ <div className="grid gap-6">
+ {sources.map((value, index) => (
+ <Card key={index}>
+ <CardContent className="space-y-2">{value}</CardContent>
+ </Card>
+ ))}
+ </div>
+ </div>
+ );
+}
+
+export default SearchResults;
diff --git a/apps/web/src/components/Sidebar/CategoryItem.tsx b/apps/web/src/components/Sidebar/CategoryItem.tsx
index 0cf8a70c..7fb571b5 100644
--- a/apps/web/src/components/Sidebar/CategoryItem.tsx
+++ b/apps/web/src/components/Sidebar/CategoryItem.tsx
@@ -1,13 +1,13 @@
-'use client';
-import { cleanUrl } from '@/lib/utils';
-import { StoredContent } from '@/server/db/schema';
+"use client";
+import { cleanUrl } from "@/lib/utils";
+import { StoredContent } from "@/server/db/schema";
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
-} from '../ui/dropdown-menu';
-import { Label } from '../ui/label';
+} from "../ui/dropdown-menu";
+import { Label } from "../ui/label";
import {
ArrowUpRight,
MoreHorizontal,
@@ -19,8 +19,8 @@ import {
ChevronRight,
Plus,
Minus,
-} from 'lucide-react';
-import { useState } from 'react';
+} from "lucide-react";
+import { useState } from "react";
import {
Drawer,
DrawerContent,
@@ -29,106 +29,106 @@ import {
DrawerDescription,
DrawerFooter,
DrawerClose,
-} from '../ui/drawer';
-import { Input } from '../ui/input';
-import { Textarea } from '../ui/textarea';
-import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
+} from "../ui/drawer";
+import { Input } from "../ui/input";
+import { Textarea } from "../ui/textarea";
+import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import {
AnimatePresence,
motion,
Reorder,
useMotionValue,
-} from 'framer-motion';
+} from "framer-motion";
const pages: 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',
+ 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: 2,
- content: '',
+ 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',
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
savedAt: new Date(),
},
{
id: 3,
- content: '',
+ 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',
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
savedAt: new Date(),
},
{
id: 4,
- content: '',
+ 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',
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
savedAt: new Date(),
},
{
id: 5,
- content: '',
+ 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',
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
savedAt: new Date(),
},
{
id: 6,
- content: '',
+ 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',
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
savedAt: new Date(),
},
{
id: 7,
- content: '',
+ 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',
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
savedAt: new Date(),
},
{
id: 8,
- content: '',
+ 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',
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
savedAt: new Date(),
},
{
id: 9,
- content: '',
+ 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',
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
savedAt: new Date(),
},
];
@@ -153,13 +153,13 @@ export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => {
/>
<ChevronDown
data-down-icon
- className={`absolute left-1/2 top-1/2 z-[2] h-4 w-4 min-w-4 -translate-x-1/2 -translate-y-1/2 scale-75 opacity-0 transition-[transform,opacity] duration-150 ${isExpanded ? 'rotate-180' : 'rotate-0'}`}
+ className={`absolute left-1/2 top-1/2 z-[2] h-4 w-4 min-w-4 -translate-x-1/2 -translate-y-1/2 scale-75 opacity-0 transition-[transform,opacity] duration-150 ${isExpanded ? "rotate-180" : "rotate-0"}`}
strokeWidth={1.5}
/>
</div>
<span className="w-full truncate text-nowrap text-left">
- {item.title ?? 'Untitled website'}
+ {item.title ?? "Untitled website"}
</span>
</button>
<Drawer
@@ -178,7 +178,7 @@ export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => {
href={item.url}
className="text-rgray-11/90 bg-rgray-3 text-md absolute right-0 top-0 flex w-min translate-y-1/2 items-center justify-center gap-1 rounded-full px-5 py-1"
>
- <img src={item.image ?? '/brain.png'} className="h-4 w-4" />
+ <img src={item.image ?? "/brain.png"} className="h-4 w-4" />
{cleanUrl(item.url)}
</a>
</DrawerHeader>
@@ -188,16 +188,16 @@ export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => {
<Input
className=""
required
- value={item.title ?? ''}
- placeholder={item.title ?? 'Enter the title for the page'}
+ value={item.title ?? ""}
+ placeholder={item.title ?? "Enter the title for the page"}
/>
</div>
<div className="mt-5">
<Label>Additional Context</Label>
<Textarea
className=""
- value={item.content ?? ''}
- placeholder={'Enter additional context for this page'}
+ value={item.content ?? ""}
+ placeholder={"Enter additional context for this page"}
/>
</div>
<DrawerFooter className="flex flex-row-reverse items-center justify-end px-0 pt-5">
@@ -224,7 +224,7 @@ export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => {
onReorder={setItems}
as="div"
initial={{ height: 0 }}
- animate={{ height: 'auto' }}
+ animate={{ height: "auto" }}
exit={{
height: 0,
transition: {},
@@ -272,8 +272,8 @@ export const CategoryPage: React.FC<{
>
<div className="relative h-4 min-w-4">
<img
- src={item.image ?? '/brain.png'}
- alt={item.title ?? 'Untitiled website'}
+ src={item.image ?? "/brain.png"}
+ alt={item.title ?? "Untitiled website"}
className="z-1 h-4 w-4 transition-[transform,opacity] delay-150 duration-150"
/>
<ArrowUpRight
@@ -284,7 +284,7 @@ export const CategoryPage: React.FC<{
</div>
<span className="w-full truncate text-nowrap">
- {item.title ?? 'Untitled website'}
+ {item.title ?? "Untitled website"}
</span>
</a>
<button
diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx
index 76b66db9..a8e3a1e5 100644
--- a/apps/web/src/components/Sidebar/FilterCombobox.tsx
+++ b/apps/web/src/components/Sidebar/FilterCombobox.tsx
@@ -1,10 +1,10 @@
-'use client';
+"use client";
-import * as React from 'react';
-import { Check, ChevronsUpDown } from 'lucide-react';
+import * as React from "react";
+import { Check, ChevronsUpDown } from "lucide-react";
-import { cn } from '@/lib/utils';
-import { Button } from '@/components/ui/button';
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
@@ -12,28 +12,30 @@ import {
CommandInput,
CommandItem,
CommandList,
-} from '@/components/ui/command';
+} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
-} from '@/components/ui/popover';
-import { SpaceIcon } from '@/assets/Memories';
-import { AnimatePresence, LayoutGroup, motion } from 'framer-motion';
-import { useMemory } from '@/contexts/MemoryContext';
+} from "@/components/ui/popover";
+import { SpaceIcon } from "@/assets/Memories";
+import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
+import { useMemory } from "@/contexts/MemoryContext";
export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
- side?: 'top' | 'bottom';
- align?: 'end' | 'start' | 'center';
+ side?: "top" | "bottom";
+ align?: "end" | "start" | "center";
onClose?: () => void;
selectedSpaces: number[];
- setSelectedSpaces: (spaces: number[] | ((prev: number[]) => number[])) => void;
+ setSelectedSpaces: (
+ spaces: number[] | ((prev: number[]) => number[]),
+ ) => void;
}
export function FilterCombobox({
className,
- side = 'bottom',
- align = 'center',
+ side = "bottom",
+ align = "center",
onClose,
selectedSpaces,
setSelectedSpaces,
@@ -65,7 +67,7 @@ export function FilterCombobox({
<button
data-state-on={open}
className={cn(
- 'text-rgray-11/70 on:bg-rgray-3 focus-visible:ring-rgray-8 hover:bg-rgray-3 relative flex items-center justify-center gap-1 rounded-md px-3 py-1.5 ring-2 ring-transparent focus-visible:outline-none',
+ "text-rgray-11/70 on:bg-rgray-3 focus-visible:ring-rgray-8 hover:bg-rgray-3 relative flex items-center justify-center gap-1 rounded-md px-3 py-1.5 ring-2 ring-transparent focus-visible:outline-none",
className,
)}
{...props}
@@ -129,7 +131,7 @@ export function FilterCombobox({
<Check
data-state-on={selectedSpaces.includes(space.id)}
className={cn(
- 'on:opacity-100 ml-auto h-4 w-4 opacity-0',
+ "on:opacity-100 ml-auto h-4 w-4 opacity-0",
)}
/>
</motion.div>
diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx
index 830b0f05..568aa3dd 100644
--- a/apps/web/src/components/Sidebar/index.tsx
+++ b/apps/web/src/components/Sidebar/index.tsx
@@ -1,29 +1,35 @@
-'use client';
-import { MemoryIcon } from '../../assets/Memories';
-import { Trash2, User2 } from 'lucide-react';
-import React, { useEffect, useState } from 'react';
-import { MemoriesBar } from './MemoriesBar';
-import { AnimatePresence, motion } from 'framer-motion';
-import { Bin } from '@/assets/Bin';
-import { Avatar, AvatarFallback, AvatarImage } from '@radix-ui/react-avatar';
-import { useSession } from 'next-auth/react';
+"use client";
+import { MemoryIcon } from "../../assets/Memories";
+import { Trash2, User2 } from "lucide-react";
+import React, { useEffect, useState } from "react";
+import { MemoriesBar } from "./MemoriesBar";
+import { AnimatePresence, motion } from "framer-motion";
+import { Bin } from "@/assets/Bin";
+import { Avatar, AvatarFallback, AvatarImage } from "@radix-ui/react-avatar";
+import { useSession } from "next-auth/react";
+import MessagePoster from "@/app/MessagePoster";
+import Image from "next/image";
+import WordMark from "../WordMark";
export type MenuItem = {
icon: React.ReactNode | React.ReactNode[];
label: string;
content?: React.ReactNode;
+ labelDisplay?: React.ReactNode;
};
export default function Sidebar({
selectChange,
+ jwt,
}: {
selectChange?: (selectedItem: string | null) => void;
+ jwt: string;
}) {
const { data: session } = useSession();
const menuItemsTop: Array<MenuItem> = [
{
icon: <MemoryIcon className="h-10 w-10" />,
- label: 'Memories',
+ label: "Memories",
content: <MemoriesBar />,
},
];
@@ -31,7 +37,7 @@ export default function Sidebar({
const menuItemsBottom: Array<MenuItem> = [
{
icon: <Trash2 strokeWidth={1.3} className="h-6 w-6" />,
- label: 'Trash',
+ label: "Trash",
},
{
icon: (
@@ -47,12 +53,12 @@ export default function Sidebar({
<User2 strokeWidth={1.3} className="h-6 w-6" />
)}
<AvatarFallback>
- {session?.user?.name?.split(' ').map((n) => n[0])}{' '}
+ {session?.user?.name?.split(" ").map((n) => n[0])}{" "}
</AvatarFallback>
</Avatar>
</div>
),
- label: 'Profile',
+ label: "Profile",
},
];
@@ -70,22 +76,30 @@ export default function Sidebar({
return (
<>
<div className="relative hidden h-screen max-h-screen w-max flex-col items-center text-sm font-light md:flex">
- <div className="bg-rgray-2 border-r-rgray-6 relative z-[50] flex h-full w-full flex-col items-center justify-center border-r px-2 py-5 ">
+ <div className="bg-rgray-3 border-r-rgray-6 relative z-[50] flex h-full w-full flex-col items-center justify-center border-r px-2 py-5 ">
+ <Image
+ className="mb-4 rounded-md"
+ src="/icons/logo_bw_without_bg.png"
+ alt="Smort logo"
+ width={50}
+ height={50}
+ />
+
+ <div className="bg-rgray-6 mb-8 h-[1px] w-full" />
+
<MenuItem
item={{
- label: 'Memories',
+ label: "Memories",
icon: <MemoryIcon className="h-10 w-10" />,
content: <MemoriesBar />,
}}
selectedItem={selectedItem}
setSelectedItem={setSelectedItem}
/>
-
<div className="mt-auto" />
-
<MenuItem
item={{
- label: 'Trash',
+ label: "Trash",
icon: <Bin id="trash" className="z-[300] h-7 w-7" />,
}}
selectedItem={selectedItem}
@@ -94,7 +108,7 @@ export default function Sidebar({
/>
<MenuItem
item={{
- label: 'Profile',
+ label: "Profile",
icon: (
<div className="mb-2">
<Avatar>
@@ -108,7 +122,7 @@ export default function Sidebar({
<User2 strokeWidth={1.3} className="h-6 w-6" />
)}
<AvatarFallback>
- {session?.user?.name?.split(' ').map((n) => n[0])}{' '}
+ {session?.user?.name?.split(" ").map((n) => n[0])}{" "}
</AvatarFallback>
</Avatar>
</div>
@@ -117,6 +131,7 @@ export default function Sidebar({
selectedItem={selectedItem}
setSelectedItem={setSelectedItem}
/>
+ <MessagePoster jwt={jwt} />
</div>
<AnimatePresence>
{selectedItem && <SubSidebar>{Subbar}</SubSidebar>}
@@ -127,7 +142,7 @@ export default function Sidebar({
}
const MenuItem = ({
- item: { icon, label },
+ item: { icon, label, labelDisplay },
selectedItem,
setSelectedItem,
...props
@@ -143,18 +158,18 @@ const MenuItem = ({
{...props}
>
{icon}
- <span className="">{label}</span>
+ <span className="">{labelDisplay ?? label}</span>
</button>
);
export function SubSidebar({ children }: { children?: React.ReactNode }) {
return (
<motion.div
- initial={{ opacity: 0, x: '-100%' }}
+ initial={{ opacity: 0, x: "-100%" }}
animate={{ opacity: 1, x: 0 }}
exit={{
opacity: 0,
- x: '-100%',
+ x: "-100%",
transition: { delay: 0.2 },
}}
transition={{
diff --git a/apps/web/src/components/WordMark.tsx b/apps/web/src/components/WordMark.tsx
new file mode 100644
index 00000000..eb55647c
--- /dev/null
+++ b/apps/web/src/components/WordMark.tsx
@@ -0,0 +1,12 @@
+import { cn } from "@/lib/utils";
+import React from "react";
+
+function WordMark({ className }: { className?: string }) {
+ return (
+ <span className={cn(`text-xl font-bold tracking-tight ${className}`)}>
+ smort.
+ </span>
+ );
+}
+
+export default WordMark;
diff --git a/apps/web/src/components/ui/avatar.tsx b/apps/web/src/components/ui/avatar.tsx
index fb190df3..47795451 100644
--- a/apps/web/src/components/ui/avatar.tsx
+++ b/apps/web/src/components/ui/avatar.tsx
@@ -1,9 +1,9 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as AvatarPrimitive from "@radix-ui/react-avatar"
+import * as React from "react";
+import * as AvatarPrimitive from "@radix-ui/react-avatar";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
@@ -13,12 +13,12 @@ const Avatar = React.forwardRef<
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
- className
+ className,
)}
{...props}
/>
-))
-Avatar.displayName = AvatarPrimitive.Root.displayName
+));
+Avatar.displayName = AvatarPrimitive.Root.displayName;
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
@@ -29,8 +29,8 @@ const AvatarImage = React.forwardRef<
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
-))
-AvatarImage.displayName = AvatarPrimitive.Image.displayName
+));
+AvatarImage.displayName = AvatarPrimitive.Image.displayName;
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
@@ -40,11 +40,11 @@ const AvatarFallback = React.forwardRef<
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-gray-100 dark:bg-gray-800",
- className
+ className,
)}
{...props}
/>
-))
-AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
+));
+AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
-export { Avatar, AvatarImage, AvatarFallback }
+export { Avatar, AvatarImage, AvatarFallback };
diff --git a/apps/web/src/components/ui/badge.tsx b/apps/web/src/components/ui/badge.tsx
index 1e21383f..40b15b91 100644
--- a/apps/web/src/components/ui/badge.tsx
+++ b/apps/web/src/components/ui/badge.tsx
@@ -1,7 +1,7 @@
-import * as React from "react"
-import { cva, type VariantProps } from "class-variance-authority"
+import * as React from "react";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-full border border-gray-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 dark:border-gray-800 dark:focus:ring-gray-300",
@@ -20,8 +20,8 @@ const badgeVariants = cva(
defaultVariants: {
variant: "default",
},
- }
-)
+ },
+);
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
@@ -30,7 +30,7 @@ export interface BadgeProps
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
- )
+ );
}
-export { Badge, badgeVariants }
+export { Badge, badgeVariants };
diff --git a/apps/web/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx
index b67d2657..24fa903e 100644
--- a/apps/web/src/components/ui/button.tsx
+++ b/apps/web/src/components/ui/button.tsx
@@ -1,8 +1,8 @@
-import * as React from "react"
-import { Slot } from "@radix-ui/react-slot"
-import { cva, type VariantProps } from "class-variance-authority"
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-gray-950 dark:focus-visible:ring-gray-300",
@@ -30,27 +30,27 @@ const buttonVariants = cva(
variant: "default",
size: "default",
},
- }
-)
+ },
+);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
- asChild?: boolean
+ asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
- const Comp = asChild ? Slot : "button"
+ const Comp = asChild ? Slot : "button";
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
- )
- }
-)
-Button.displayName = "Button"
+ );
+ },
+);
+Button.displayName = "Button";
-export { Button, buttonVariants }
+export { Button, buttonVariants };
diff --git a/apps/web/src/components/ui/card.tsx b/apps/web/src/components/ui/card.tsx
index 65119a16..e98d500c 100644
--- a/apps/web/src/components/ui/card.tsx
+++ b/apps/web/src/components/ui/card.tsx
@@ -1,6 +1,6 @@
-import * as React from "react"
+import * as React from "react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const Card = React.forwardRef<
HTMLDivElement,
@@ -10,12 +10,12 @@ const Card = React.forwardRef<
ref={ref}
className={cn(
"rounded-lg border border-gray-200 bg-white text-gray-950 shadow-sm dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50",
- className
+ className,
)}
{...props}
/>
-))
-Card.displayName = "Card"
+));
+Card.displayName = "Card";
const CardHeader = React.forwardRef<
HTMLDivElement,
@@ -26,8 +26,8 @@ const CardHeader = React.forwardRef<
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
-))
-CardHeader.displayName = "CardHeader"
+));
+CardHeader.displayName = "CardHeader";
const CardTitle = React.forwardRef<
HTMLParagraphElement,
@@ -37,12 +37,12 @@ const CardTitle = React.forwardRef<
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
- className
+ className,
)}
{...props}
/>
-))
-CardTitle.displayName = "CardTitle"
+));
+CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef<
HTMLParagraphElement,
@@ -53,16 +53,16 @@ const CardDescription = React.forwardRef<
className={cn("text-sm text-gray-500 dark:text-gray-400", className)}
{...props}
/>
-))
-CardDescription.displayName = "CardDescription"
+));
+CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
-))
-CardContent.displayName = "CardContent"
+));
+CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef<
HTMLDivElement,
@@ -73,7 +73,14 @@ const CardFooter = React.forwardRef<
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
-))
-CardFooter.displayName = "CardFooter"
+));
+CardFooter.displayName = "CardFooter";
-export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardDescription,
+ CardContent,
+};
diff --git a/apps/web/src/components/ui/label.tsx b/apps/web/src/components/ui/label.tsx
index 53418217..84f8b0c7 100644
--- a/apps/web/src/components/ui/label.tsx
+++ b/apps/web/src/components/ui/label.tsx
@@ -1,14 +1,14 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as LabelPrimitive from "@radix-ui/react-label"
-import { cva, type VariantProps } from "class-variance-authority"
+import * as React from "react";
+import * as LabelPrimitive from "@radix-ui/react-label";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const labelVariants = cva(
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
-)
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
+);
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
@@ -20,7 +20,7 @@ const Label = React.forwardRef<
className={cn(labelVariants(), className)}
{...props}
/>
-))
-Label.displayName = LabelPrimitive.Root.displayName
+));
+Label.displayName = LabelPrimitive.Root.displayName;
-export { Label }
+export { Label };
diff --git a/apps/web/src/contexts/MemoryContext.tsx b/apps/web/src/contexts/MemoryContext.tsx
index 3727c464..eab1e4fe 100644
--- a/apps/web/src/contexts/MemoryContext.tsx
+++ b/apps/web/src/contexts/MemoryContext.tsx
@@ -1,6 +1,6 @@
-'use client';
-import React, { useCallback } from 'react';
-import { CollectedSpaces } from '../../types/memory';
+"use client";
+import React, { useCallback } from "react";
+import { CollectedSpaces } from "../../types/memory";
// temperory (will change)
export const MemoryContext = React.createContext<{
@@ -41,7 +41,7 @@ export const MemoryProvider: React.FC<
export const useMemory = () => {
const context = React.useContext(MemoryContext);
if (context === undefined) {
- throw new Error('useMemory must be used within a MemoryProvider');
+ throw new Error("useMemory must be used within a MemoryProvider");
}
return context;
};
diff --git a/apps/web/src/env.js b/apps/web/src/env.js
index 3d3085fa..2495d75b 100644
--- a/apps/web/src/env.js
+++ b/apps/web/src/env.js
@@ -1,7 +1,6 @@
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
-
export const env = createEnv({
/**
* Specify your server-side environment variables schema here. This way you can ensure the app
@@ -12,15 +11,13 @@ export const env = createEnv({
.enum(["development", "test", "production"])
.default("development"),
NEXTAUTH_SECRET:
- process.env.NODE_ENV === "production"
- ? z.string()
- : z.string(),
+ process.env.NODE_ENV === "production" ? z.string() : z.string(),
NEXTAUTH_URL: z.preprocess(
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
// Since NextAuth.js automatically uses the VERCEL_URL if present.
(str) => process.env.VERCEL_URL ?? str,
// VERCEL_URL doesn't include `https` so it cant be validated as a URL
- process.env.VERCEL ? z.string() : z.string().url()
+ process.env.VERCEL ? z.string() : z.string().url(),
),
GOOGLE_CLIENT_ID: z.string(),
GOOGLE_CLIENT_SECRET: z.string(),
diff --git a/apps/web/src/lib/searchParams.ts b/apps/web/src/lib/searchParams.ts
index b435295d..aae3f4c7 100644
--- a/apps/web/src/lib/searchParams.ts
+++ b/apps/web/src/lib/searchParams.ts
@@ -1,12 +1,12 @@
import {
- createSearchParamsCache,
- parseAsInteger,
- parseAsString
- } from 'nuqs/server'
- // Note: import from 'nuqs/server' to avoid the "use client" directive
-
- export const searchParamsCache = createSearchParamsCache({
- // List your search param keys and associated parsers here:
- q: parseAsString.withDefault(''),
- maxResults: parseAsInteger.withDefault(10)
- }) \ No newline at end of file
+ createSearchParamsCache,
+ parseAsInteger,
+ parseAsString,
+} from "nuqs/server";
+// Note: import from 'nuqs/server' to avoid the "use client" directive
+
+export const searchParamsCache = createSearchParamsCache({
+ // List your search param keys and associated parsers here:
+ q: parseAsString.withDefault(""),
+ maxResults: parseAsInteger.withDefault(10),
+});
diff --git a/apps/web/src/server/auth.ts b/apps/web/src/server/auth.ts
index c32efe55..95edcf35 100644
--- a/apps/web/src/server/auth.ts
+++ b/apps/web/src/server/auth.ts
@@ -1,7 +1,7 @@
import { env } from "@/env";
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
-import { DrizzleAdapter } from "@auth/drizzle-adapter"
+import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "./db";
export const {
@@ -15,9 +15,9 @@ export const {
...session,
user: {
...session.user,
- id: user.id
+ id: user.id,
},
- })
+ }),
},
adapter: DrizzleAdapter(db),
providers: [
diff --git a/apps/web/src/server/db/index.ts b/apps/web/src/server/db/index.ts
index 5aa87fc1..4d671bea 100644
--- a/apps/web/src/server/db/index.ts
+++ b/apps/web/src/server/db/index.ts
@@ -1,8 +1,5 @@
-import { drizzle } from 'drizzle-orm/d1';
+import { drizzle } from "drizzle-orm/d1";
import * as schema from "./schema";
-export const db = drizzle(
- process.env.DATABASE,
- { schema, logger: true }
-);
+export const db = drizzle(process.env.DATABASE, { schema, logger: true });
diff --git a/apps/web/src/server/db/schema.ts b/apps/web/src/server/db/schema.ts
index a80eb7cf..e0ddbdbc 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";
export const createTable = sqliteTableCreator((name) => `${name}`);
@@ -78,7 +78,6 @@ export const verificationTokens = createTable(
}),
);
-
export const storedContent = createTable(
"storedContent",
{
@@ -103,8 +102,12 @@ export const storedContent = createTable(
export const contentToSpace = createTable(
"contentToSpace",
{
- contentId: integer("contentId").notNull().references(() => storedContent.id),
- spaceId: integer("spaceId").notNull().references(() => space.id),
+ contentId: integer("contentId")
+ .notNull()
+ .references(() => storedContent.id),
+ spaceId: integer("spaceId")
+ .notNull()
+ .references(() => space.id),
},
(cts) => ({
compoundKey: primaryKey({ columns: [cts.contentId, cts.spaceId] }),
@@ -115,7 +118,7 @@ export const space = createTable(
"space",
{
id: integer("id").notNull().primaryKey({ autoIncrement: true }),
- name: text('name').notNull().default('all'),
+ name: text("name").notNull().default("all"),
user: text("user", { length: 255 }).references(() => users.id),
},
(space) => ({
@@ -124,4 +127,4 @@ export const space = createTable(
}),
);
-export type StoredContent = Omit<typeof storedContent.$inferSelect, 'user'> \ No newline at end of file
+export type StoredContent = Omit<typeof storedContent.$inferSelect, "user">;
diff --git a/apps/web/src/server/helpers.ts b/apps/web/src/server/helpers.ts
index 1f6cf977..519e4b17 100644
--- a/apps/web/src/server/helpers.ts
+++ b/apps/web/src/server/helpers.ts
@@ -1,34 +1,34 @@
export async function getMetaData(url: string) {
- const response = await fetch(url);
- const html = await response.text();
-
- // Extract the base URL
- const baseUrl = new URL(url).origin;
-
- // Extract title
- const titleMatch = html.match(/<title>(.*?)<\/title>/);
- const title = titleMatch ? titleMatch[1] : 'Title not found';
-
- // Extract meta description
- const descriptionMatch = html.match(
- /<meta name="description" content="(.*?)"\s*\/?>/,
- );
- const description = descriptionMatch
- ? descriptionMatch[1]
- : 'Description not found';
-
- // Extract Open Graph image
- const imageMatch = html.match(
- /<meta property="og:image" content="(.*?)"\s*\/?>/,
- );
- const image = imageMatch ? imageMatch[1] : 'Image not found';
-
- // Prepare the metadata object
- const metadata = {
- title,
- description,
- image,
- baseUrl,
- };
- return metadata;
- } \ No newline at end of file
+ const response = await fetch(url);
+ const html = await response.text();
+
+ // Extract the base URL
+ const baseUrl = new URL(url).origin;
+
+ // Extract title
+ const titleMatch = html.match(/<title>(.*?)<\/title>/);
+ const title = titleMatch ? titleMatch[1] : "Title not found";
+
+ // Extract meta description
+ const descriptionMatch = html.match(
+ /<meta name="description" content="(.*?)"\s*\/?>/,
+ );
+ const description = descriptionMatch
+ ? descriptionMatch[1]
+ : "Description not found";
+
+ // Extract Open Graph image
+ const imageMatch = html.match(
+ /<meta property="og:image" content="(.*?)"\s*\/?>/,
+ );
+ const image = imageMatch ? imageMatch[1] : "Image not found";
+
+ // Prepare the metadata object
+ const metadata = {
+ title,
+ description,
+ image,
+ baseUrl,
+ };
+ return metadata;
+}