diff --git a/.eslintrc.cjs b/.eslintrc.cjs
deleted file mode 100644
index 728f09f1..00000000
--- a/.eslintrc.cjs
+++ /dev/null
@@ -1,35 +0,0 @@
-/** @type {import("eslint").Linter.Config} */
-const config = {
- parser: "@typescript-eslint/parser",
- plugins: ["@typescript-eslint", "eslint-plugin-next-on-pages"],
- extends: [
- "next/core-web-vitals",
- "plugin:@typescript-eslint/recommended-type-checked",
- "plugin:@typescript-eslint/stylistic-type-checked",
- "plugin:eslint-plugin-next-on-pages/recommended",
- ],
- rules: {
- // These opinionated rules are enabled in stylistic-type-checked above.
- // Feel free to reconfigure them to your own preference.
- "@typescript-eslint/array-type": "off",
- "@typescript-eslint/consistent-type-definitions": "off",
-
- "@typescript-eslint/consistent-type-imports": [
- "warn",
- {
- prefer: "type-imports",
- fixStyle: "inline-type-imports",
- },
- ],
- "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
- "@typescript-eslint/require-await": "off",
- "@typescript-eslint/no-misused-promises": [
- "error",
- {
- checksVoidReturn: { attributes: false },
- },
- ],
- },
-};
-
-module.exports = config;
diff --git a/.gitignore b/.gitignore
index f6c4a3eb..d8480f00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,52 +1,43 @@
-.next/
-.turbo
-*.sqlite
-*.lockb
-.next
+bun.lockb
+.npmrc
+.vars
+.*.vars
.wrangler
-drizzle/
-dist/
-pnpm-lock.yaml
+.million
+# Dependencies
node_modules
-
-# dependencies
-/node_modules
-/.pnp
+.pnp
.pnp.js
-# testing
-/coverage
+# Local env files
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
-# database
-/prisma/db.sqlite
-/prisma/db.sqlite-journal
+# Testing
+coverage
-# next.js
-/.next/
-/out/
-next-env.d.ts
+# Turbo
+.turbo
-# production
-/build
+# Vercel
+.vercel
-# misc
-.DS_Store
-*.pem
+# Build Outputs
+.next/
+out/
+build
+dist
-# debug
+
+# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
-.pnpm-debug.log*
-# local env files
-# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
-.env
-.env*.local
-
-# vercel
-.vercel
-
-# typescript
-*.tsbuildinfo
+# Misc
+.DS_Store
+*.pem
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index e8be54ff..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,6 +0,0 @@
-[submodule "apps/rowser-rendering"]
- path = apps/rowser-rendering
- url = https://github.com/dhravya/markdowner
-[submodule "apps/browser-rendering"]
- path = apps/browser-rendering
- url = https://github.com/dhravya/markdowner
diff --git a/.prettierignore b/.prettierignore
deleted file mode 100644
index f3c04e81..00000000
--- a/.prettierignore
+++ /dev/null
@@ -1 +0,0 @@
-**/.gitignore
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index c50f32c7..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2024 Dhravya Shah
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/README.md b/README.md
deleted file mode 100644
index 0fbe3e85..00000000
--- a/README.md
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-# SuperMemory
-
-Interested in helping build the best second brain for everyone? Join the discord https://discord.gg/2X2XsKz5AU. Contributions welcome.
-
-## 👀 What is this?
-
-Build your own second brain with supermemory. It's a ChatGPT for your bookmarks. Import tweets or save websites and content using the [chrome extension](https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc?hl=en-GB&authuser=0) (the extension on webstore is not updated, please use the one in the repo)
-
-Well, here's the thing - me and @yxshv save a _lot_ of content on the internet.
-
-Twitter bookmarks, websites, snippets, etc.
-
-But we never look back to it - to us, it's like throwing information in the void.
-
-Supermemory fixes this.
-
-## How do I use this?
-
-Just go to [supermemory.dhr.wtf](https://supermemory.dhr.wtf) and sign in with your google account.
-
-To use the chrome extension,
-
-1. Get the chrome ext (click on the button)
-2. Click on the "Extension Auth" button so the extension knows who you are :)
- 
-
-## 👨💻 The Stack
-
-
-
-Supermemory has three main modules, managed by [turborepo](https://turbo.build):
-
-#### `apps/web`: The main web UI.
-
-The database, auth etc logic is here
-
-
-
-Built with:
-
-- Nextjs 14
-- [Next Auth](https://next-auth.js.org/)
-- [Drizzle ORM](https://drizzle.team/)
-- [Cloudflare D1 database](https://developers.cloudflare.com/d1/get-started/)
-- Cloudflare ratelimiter
-- [TailwindCSS](https://tailwindcss.com)
-- [shadcn-ui](https://ui.shadcn.com)
-- And some other amazing open source projects like [Novel](https://novel.sh) and [vaul](https://vaul.emilkowal.ski/)
-- Hosted on [Cloudflare Pages](https://pages.cloudflare.com/)
-
-#### `apps/extension`: Chrome extension
-
-The chrome extension is one of the most important part of the setup, but is not required.This is to easily add pages to your memory.
-
-
-
-You can also use it to import all your twitter bookmarks!
-
-
-Built with:
-
-- [CRXJS](https://crxjs.dev/vite-plugin/getting-started/react/create-project)
-- Vite
-- [TailwindCSS](https://tailwindcss.com)
-- [shadcn-ui](https://ui.shadcn.com)
-- React
-
-#### `apps/cf-ai-backend`: This module handles the vector store and AI response generation
-
-This is where the magic happens!
-Built with:
-
-- Cloudflare Workers
-- [Cloudflare AI](https://ai.cloudflare.com)
-- [Cloudflare Vectorize](https://developers.cloudflare.com/vectorize/)
-- [Cloudflare Queues](https://developers.cloudflare.com/queues/)
-- [Cloudflare Browser Rendering](https://developers.cloudflare.com/browser-rendering/)
-- [Cloudflare KV](https://developers.cloudflare.com/kv)
-
-## Contribute or self host
-
-Supermemory is design to be set up easily locally and super duper easy to set up 💫
-
-Please see the [SETUP-GUIDE.md](SETUP-GUIDE.md) for setup instructions.
-
-### Contributing
-
-Contributions are very welcome! A contribution can be as small as a ⭐ or even finding and creating issues.
diff --git a/SETUP-GUIDE.md b/SETUP-GUIDE.md
deleted file mode 100644
index 7d69b545..00000000
--- a/SETUP-GUIDE.md
+++ /dev/null
@@ -1,79 +0,0 @@
-# Setup guide
-
-## Prerequisites
-
-- [bun](https://bun.sh/)
-- [turbo](https://turbo.build/repo/docs/installing)
-- [wrangler](https://developers.cloudflare.com/workers/cli-wrangler/install-update)
-
-## Steps
-
-1. Clone the repo
-2. Run `bun install` in the root directory
-3. Create a `.dev.vars` file in `apps/web` with the following content:
-
-```bash
-GOOGLE_CLIENT_ID="-"
-GOOGLE_CLIENT_SECRET="-"
-NEXTAUTH_SECRET='nextauthsecret'
-DATABASE_URL='database.sqlite'
-NEXTAUTH_URL='http://localhost:3000'
-BACKEND_SECURITY_KEY='veryrandomsecuritykey'
-```
-
-4. Setup the database:
-
-First, edit the `wrangler.toml` file in `apps/web` to point the d1 database to your account.
-
-You can create a d1 database by running this command
-
-```
-wrangler d1 create DATABASE_NAME
-```
-
-And then replace these values
-
-```
-[[d1_databases]]
-binding = "DATABASE"
-database_name = "YOUR_DATABASE_NAME"
-database_id = "YOUR_DB_ID"
-```
-
-Simply run this command in `apps/web`
-
-```
-wrangler d1 execute dev-d1-anycontext --local --file=db/prepare.sql
-```
-
-If it runs, you can set up the cloud database as well by removing the `--local` flag.
-
-5. You need to host your own worker for the `apps/cf-ai-backend` module.
-
-To do this, first edit the `.dev.vars` file in `apps/cf-ai-backend` with the following content:
-
-```bash
-SECURITY_KEY="veryrandomsecuritykey"
-// Why? to generate embeddings with 4000+ tokens
-OPENAI_API_KEY="sk-"
-```
-
-6. Run this command to initialise vector database
- > Note: You need to use the workers paid plan to use vectorize for now.
-
-```
-wrangler vectorize create --dimensions=1536 supermem-vector-1 --metric=cosine
-```
-
-7. Change the `wrangler.toml` file in `apps/cf-ai-backend` to point to your KV namespace
-
-8. Run `bun dev` in the root directory and Voila! You have your own supermemory instance running!
-
-> Note: You need to replace the url `https://cf-ai-backend.dhr.wtf` everywhere with your own url for the cf-ai-backend module.
-
-## Deploying
-
-To deploy the web app, run `bun deploy` in the `apps/web` directory.
-To deploy the cf-ai-backend module, run `wrangler publish` in the `apps/cf-ai-backend` directory.
-
-To get the extension running, you need to build it first. Run `bun build` in the `apps/extension` directory and then load the extension in chrome.
diff --git a/apps/cf-ai-backend/jest.config.js b/apps/cf-ai-backend/jest.config.js
deleted file mode 100644
index 85c5da79..00000000
--- a/apps/cf-ai-backend/jest.config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-module.exports = {
- testEnvironment: "miniflare",
- testMatch: ["**/test/**/*.+(ts|tsx)", "**/src/**/(*.)+(spec|test).+(ts|tsx)"],
- transform: {
- "^.+\\.(ts|tsx)$": "esbuild-jest",
- },
-};
diff --git a/apps/cf-ai-backend/tsconfig.json b/apps/cf-ai-backend/tsconfig.json
index 9f6f8a73..2b75d5a0 100644
--- a/apps/cf-ai-backend/tsconfig.json
+++ b/apps/cf-ai-backend/tsconfig.json
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"lib": ["ES2020"],
- "types": ["jest", "@cloudflare/workers-types"]
+ "types": ["@cloudflare/workers-types"]
}
}
diff --git a/apps/cf-ai-backend/wrangler.toml b/apps/cf-ai-backend/wrangler.toml
index 83e2d41a..db0ae945 100644
--- a/apps/cf-ai-backend/wrangler.toml
+++ b/apps/cf-ai-backend/wrangler.toml
@@ -1,5 +1,5 @@
name = "new-cf-ai-backend"
-main = "src/server.ts"
+main = "src/index.ts"
compatibility_date = "2024-02-23"
node_compat = true
diff --git a/apps/extension/.eslintrc.cjs b/apps/extension/.eslintrc.cjs
deleted file mode 100644
index 6e8698b7..00000000
--- a/apps/extension/.eslintrc.cjs
+++ /dev/null
@@ -1,18 +0,0 @@
-module.exports = {
- root: true,
- env: { browser: true, es2020: true },
- extends: [
- "eslint:recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:react-hooks/recommended",
- ],
- ignorePatterns: ["dist", ".eslintrc.cjs"],
- parser: "@typescript-eslint/parser",
- plugins: ["react-refresh"],
- rules: {
- "react-refresh/only-export-components": [
- "warn",
- { allowConstantExport: true },
- ],
- },
-};
diff --git a/apps/extension/.gitignore b/apps/extension/.gitignore
deleted file mode 100644
index 97d5fffe..00000000
--- a/apps/extension/.gitignore
+++ /dev/null
@@ -1,26 +0,0 @@
-# Logs
-*.zip
-
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-*.local
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
diff --git a/apps/extension/README.md b/apps/extension/README.md
deleted file mode 100644
index bb156850..00000000
--- a/apps/extension/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# React + TypeScript + Vite
-
-This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
-
-Currently, two official plugins are available:
-
-- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
-- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
-
-## Expanding the ESLint configuration
-
-If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
-
-- Configure the top-level `parserOptions` property like this:
-
-```js
-export default {
- // other rules...
- parserOptions: {
- ecmaVersion: "latest",
- sourceType: "module",
- project: ["./tsconfig.json", "./tsconfig.node.json"],
- tsconfigRootDir: __dirname,
- },
-};
-```
-
-- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
-- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
-- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
diff --git a/apps/extension/components.json b/apps/extension/components.json
deleted file mode 100644
index 6ceb01a1..00000000
--- a/apps/extension/components.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "$schema": "https://ui.shadcn.com/schema.json",
- "style": "default",
- "rsc": false,
- "tsx": true,
- "tailwind": {
- "config": "tailwind.config.js",
- "css": "src/global.css",
- "baseColor": "stone",
- "cssVariables": false,
- "prefix": "anycontext-"
- },
- "aliases": {
- "components": "src/components",
- "utils": "src/lib/utils"
- }
-}
diff --git a/apps/extension/icons/icon128.png b/apps/extension/icons/icon128.png
deleted file mode 100644
index 9ae12886..00000000
Binary files a/apps/extension/icons/icon128.png and /dev/null differ
diff --git a/apps/extension/icons/icon16.png b/apps/extension/icons/icon16.png
deleted file mode 100644
index dedf07d2..00000000
Binary files a/apps/extension/icons/icon16.png and /dev/null differ
diff --git a/apps/extension/icons/icon32.png b/apps/extension/icons/icon32.png
deleted file mode 100644
index 4ceed7ba..00000000
Binary files a/apps/extension/icons/icon32.png and /dev/null differ
diff --git a/apps/extension/icons/icon48.png b/apps/extension/icons/icon48.png
deleted file mode 100644
index e8beaf4f..00000000
Binary files a/apps/extension/icons/icon48.png and /dev/null differ
diff --git a/apps/extension/index.html b/apps/extension/index.html
deleted file mode 100644
index e4b78eae..00000000
--- a/apps/extension/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
- Vite + React + TS
-
-
-
-
-
-
diff --git a/apps/extension/manifest.json b/apps/extension/manifest.json
deleted file mode 100644
index 5cf05298..00000000
--- a/apps/extension/manifest.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "manifest_version": 3,
- "name": "SuperMemory",
- "version": "2.0.0",
- "action": {
- "default_popup": "index.html"
- },
- "icons": {
- "16": "icons/icon16.png",
- "32": "icons/icon32.png",
- "48": "icons/icon48.png",
- "128": "icons/icon128.png"
- },
- "content_scripts": [
- {
- "js": ["src/content.tsx"],
- "matches": [
- "http://localhost:3000/*",
- "https://opulent-funicular-94rx4v9w775f96q-3000.app.github.dev/*",
- "https://anycontext.dhr.wtf/*",
- ""
- ]
- }
- ],
- "permissions": [
- "activeTab",
- "storage",
- "http://localhost:3000/*",
- "https://opulent-funicular-94rx4v9w775f96q-3000.app.github.dev/*",
- "https://anycontext.dhr.wtf/*"
- ],
- "background": {
- "service_worker": "src/background.ts"
- }
-}
diff --git a/apps/extension/package.json b/apps/extension/package.json
deleted file mode 100644
index a7f34ea6..00000000
--- a/apps/extension/package.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "name": "extension",
- "private": true,
- "version": "0.0.0",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "tsc && vite build",
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
- "preview": "vite preview",
- "package": "zip -r extension.zip dist/"
- },
- "dependencies": {
- "@radix-ui/react-dialog": "^1.0.5",
- "@radix-ui/react-dropdown-menu": "^2.0.6",
- "@radix-ui/react-popover": "^1.0.7",
- "@radix-ui/react-tooltip": "^1.0.7",
- "cmdk": "^1.0.0",
- "react": "^18.2.0",
- "react-dom": "^18.2.0"
- },
- "devDependencies": {
- "@types/node": "^20.11.22",
- "@types/react": "^18.2.56",
- "@types/react-dom": "^18.2.19",
- "@typescript-eslint/eslint-plugin": "^7.0.2",
- "@typescript-eslint/parser": "^7.0.2",
- "@vitejs/plugin-react": "^4.2.1",
- "eslint": "^8.56.0",
- "eslint-plugin-react-hooks": "^4.6.0",
- "eslint-plugin-react-refresh": "^0.4.5",
- "typescript": "^5.2.2",
- "vite": "^5.1.4"
- }
-}
diff --git a/apps/extension/postcss.config.js b/apps/extension/postcss.config.js
deleted file mode 100644
index 2aa7205d..00000000
--- a/apps/extension/postcss.config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export default {
- plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
-};
diff --git a/apps/extension/public/vite.svg b/apps/extension/public/vite.svg
deleted file mode 100644
index e7b8dfb1..00000000
--- a/apps/extension/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/apps/extension/src/App.tsx b/apps/extension/src/App.tsx
deleted file mode 100644
index c29d98a2..00000000
--- a/apps/extension/src/App.tsx
+++ /dev/null
@@ -1,308 +0,0 @@
-import { useEffect, useState } from "react";
-import { z } from "zod";
-import { userObj } from "./types/zods";
-import { getEnv } from "./util";
-
-const backendUrl =
- getEnv() === "development"
- ? "http://localhost:3000"
- : "https://supermemory.dhr.wtf";
-
-function App() {
- const [userData, setUserData] = useState | null>(
- null,
- );
-
- const getUserData = () => {
- chrome.runtime.sendMessage({ type: "getJwt" }, (response) => {
- const jwt = response.jwt;
- const loginButton = document.getElementById("login");
-
- if (loginButton) {
- if (jwt) {
- fetch(`${backendUrl}/api/me`, {
- headers: {
- Authorization: `Bearer ${jwt}`,
- },
- })
- .then((res) => res.json())
- .then((data) => {
- const d = userObj.safeParse(data);
- if (d.success) {
- setUserData(d.data);
- } else {
- console.error(d.error);
- }
- });
- loginButton.style.display = "none";
- }
- }
- });
- };
-
- useEffect(() => {
- getUserData();
- }, []);
-
- // TODO: Implement getting bookmarks from Twitter API directly
- // const [status, setStatus] = useState('');
- // const [bookmarks, setBookmarks] = useState([]);
-
- // const fetchBookmarks = (e: React.MouseEvent) => {
- // e.preventDefault();
-
- // chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
- // chrome.tabs.sendMessage(tabs[0].id!, { action: 'showProgressIndicator' });
- // });
-
- // chrome.tabs.create(
- // { url: 'https://twitter.com/i/bookmarks/all' },
- // function (tab) {
- // chrome.tabs.onUpdated.addListener(function listener(tabId, info) {
- // if (tabId === tab.id && info.status === 'complete') {
- // chrome.tabs.onUpdated.removeListener(listener);
-
- // chrome.runtime.sendMessage(
- // { action: 'getAuthData' },
- // function (response) {
- // const authorizationHeader = response.authorizationHeader;
- // const csrfToken = response.csrfToken;
- // const cookies = response.cookies;
-
- // if (authorizationHeader && csrfToken && cookies) {
- // fetchAllBookmarks(authorizationHeader, csrfToken, cookies)
- // .then((bookmarks) => {
- // console.log('Bookmarks data:', bookmarks);
- // setBookmarks(bookmarks);
- // chrome.tabs.sendMessage(tabId, {
- // action: 'hideProgressIndicator',
- // });
- // setStatus(
- // `Fetched ${bookmarks.length} bookmarked tweets.`,
- // );
- // })
- // .catch((error) => {
- // console.error('Error:', error);
- // chrome.tabs.sendMessage(tabId, {
- // action: 'hideProgressIndicator',
- // });
- // setStatus(
- // 'Error fetching bookmarks. Please check the console for details.',
- // );
- // });
- // } else {
- // chrome.tabs.sendMessage(tabId, {
- // action: 'hideProgressIndicator',
- // });
- // setStatus('Missing authentication data');
- // }
- // },
- // );
- // }
- // });
- // },
- // );
- // };
-
- return (
-
-
- chrome.tabs.create({
- url: `${backendUrl}/api/auth/signin`,
- })
- }
- id="login"
- >
- Log in
-
-
- {userData && (
-
-
-
-
{userData.data.user.name}
-
{userData.data.user.email}
-
- {/* TODO: Implement getting bookmarks from API directly */}
- {/*
fetchBookmarks(e)}>Fetch Bookmarks
-
{status}
-
-
- {bookmarks.map((bookmark) => (
-
-
{bookmark.author}
-
{bookmark.date}
-
{bookmark.full_text}
-
- ))}
-
*/}
-
- )}
-
-
- );
-}
-
-// TODO: Implement getting bookmarks from Twitter API directly
-// async function fetchAllBookmarks(
-// authorizationHeader: string,
-// csrfToken: string,
-// cookies: string,
-// ): Promise {
-// const baseUrl =
-// 'https://twitter.com/i/api/graphql/uJEL6XARgGmo2EAsO2Pfkg/Bookmarks';
-// const params = new URLSearchParams({
-// variables: JSON.stringify({
-// count: 100,
-// includePromotedContent: true,
-// }),
-// features: JSON.stringify({
-// graphql_timeline_v2_bookmark_timeline: true,
-// rweb_tipjar_consumption_enabled: false,
-// responsive_web_graphql_exclude_directive_enabled: true,
-// verified_phone_label_enabled: true,
-// creator_subscriptions_tweet_preview_api_enabled: true,
-// responsive_web_graphql_timeline_navigation_enabled: true,
-// responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,
-// communities_web_enable_tweet_community_results_fetch: true,
-// c9s_tweet_anatomy_moderator_badge_enabled: true,
-// tweetypie_unmention_optimization_enabled: true,
-// responsive_web_edit_tweet_api_enabled: true,
-// graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,
-// view_counts_everywhere_api_enabled: true,
-// longform_notetweets_consumption_enabled: true,
-// responsive_web_twitter_article_tweet_consumption_enabled: true,
-// tweet_awards_web_tipping_enabled: false,
-// creator_subscriptions_quote_tweet_preview_enabled: false,
-// freedom_of_speech_not_reach_fetch_enabled: true,
-// standardized_nudges_misinfo: true,
-// tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:
-// true,
-// tweet_with_visibility_results_prefer_gql_media_interstitial_enabled:
-// false,
-// rweb_video_timestamps_enabled: true,
-// longform_notetweets_rich_text_read_enabled: true,
-// longform_notetweets_inline_media_enabled: true,
-// responsive_web_enhance_cards_enabled: false,
-// }),
-// });
-
-// const requestUrl = `${baseUrl}?${params}`;
-
-// const headers = {
-// Authorization: authorizationHeader,
-// 'X-Csrf-Token': csrfToken,
-// Cookie: cookies,
-// };
-
-// const bookmarks: TweetData[] = [];
-// let nextCursor = null;
-// let requestCount = 0;
-// const maxRequestsPerWindow = 450;
-// const windowDuration = 15 * 60 * 1000; // 15 minutes in milliseconds
-// let windowStartTime = Date.now();
-
-// do {
-// if (nextCursor) {
-// params.set(
-// 'variables',
-// JSON.stringify({
-// count: 100,
-// cursor: nextCursor,
-// includePromotedContent: true,
-// }),
-// );
-// }
-
-// // Check if the rate limit is exceeded
-// if (requestCount >= maxRequestsPerWindow) {
-// const elapsedTime = Date.now() - windowStartTime;
-// if (elapsedTime < windowDuration) {
-// const waitTime = windowDuration - elapsedTime;
-// await new Promise((resolve) => setTimeout(resolve, waitTime));
-// }
-// requestCount = 0;
-// windowStartTime = Date.now();
-// }
-
-// try {
-// const response = await fetch(requestUrl, {
-// method: 'GET',
-// headers: headers,
-// });
-
-// requestCount++;
-
-// if (!response.ok) {
-// throw new Error(`HTTP error! status: ${response.status}`);
-// }
-
-// const data = await response.json();
-// const timeline = data.data.bookmark_timeline_v2.timeline;
-
-// timeline.instructions.forEach(
-// (instruction: {
-// type: string;
-// entries: {
-// content: {
-// entryType: string;
-// itemContent: {
-// tweet_results: {
-// result: {
-// legacy: {
-// full_text: string;
-// created_at: string;
-// };
-// core: {
-// user_results: {
-// result: {
-// legacy: {
-// screen_name: string;
-// };
-// };
-// };
-// };
-// rest_id: string;
-// };
-// };
-// };
-// };
-// }[];
-// }) => {
-// if (instruction.type === 'TimelineAddEntries') {
-// instruction.entries.forEach((entry) => {
-// if (entry.content.entryType === 'TimelineTimelineItem') {
-// const tweet = entry.content.itemContent.tweet_results.result;
-// const tweetData = {
-// full_text: tweet.legacy.full_text,
-// url: `https://twitter.com/${tweet.core.user_results.result.legacy.screen_name}/status/${tweet.rest_id}`,
-// author: tweet.core.user_results.result.legacy.screen_name,
-// date: tweet.legacy.created_at,
-// tweet_id: tweet.rest_id,
-// };
-// bookmarks.push(tweetData);
-// }
-// });
-// }
-// },
-// );
-
-// nextCursor = timeline.instructions.find(
-// (instruction: { type: string }) =>
-// instruction.type === 'TimelineTerminateTimeline',
-// )?.direction?.cursor;
-// } catch (error) {
-// console.error('Error fetching bookmarks:', error);
-// throw error;
-// }
-// } while (nextCursor);
-
-// return bookmarks;
-// }
-export default App;
diff --git a/apps/extension/src/SideBar.tsx b/apps/extension/src/SideBar.tsx
deleted file mode 100644
index 385c0f22..00000000
--- a/apps/extension/src/SideBar.tsx
+++ /dev/null
@@ -1,361 +0,0 @@
-import { useState } from "react";
-
-import "./ext.css";
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from "./components/ui/tooltip";
-import { FilterSpaces } from "./components/FilterCombobox";
-import {
- Dialog,
- DialogContent,
- DialogHeader,
- DialogTitle,
- DialogDescription,
- DialogTrigger,
- DialogFooter,
- DialogClose,
-} from "./components/ui/dialog";
-import { Space } from "./types/memory";
-
-function sendUrlToAPI(spaces: number[]) {
- // get the current URL
- const url = window.location.href;
-
- const blacklist = ["localhost:3000", "anycontext.dhr.wtf"];
- // check if the URL is blacklisted
- if (blacklist.some((blacklisted) => url.includes(blacklisted))) {
- console.log("URL is blacklisted");
- return;
- } else {
- // const content = Entire page content, but cleaned up for the LLM. No ads, no scripts, no styles, just the text. if article, just the importnat info abou tit.
- const content = document.documentElement.innerText;
- chrome.runtime.sendMessage({ type: "urlChange", content, url, spaces });
- }
-}
-
-function SideBar({ jwt }: { jwt: string }) {
- // TODO: Implement getting bookmarks from Twitter API directly
- // chrome.runtime.onMessage.addListener(function (request) {
- // if (request.action === 'showProgressIndicator') {
- // // TODO: SHOW PROGRESS INDICATOR
- // // showProgressIndicator();
- // } else if (request.action === 'hideProgressIndicator') {
- // // hideProgressIndicator();
- // }
- // });
-
- const [savedWebsites, setSavedWebsites] = useState([]);
-
- const [isSendingData, setIsSendingData] = useState(false);
-
- const [loading, setLoading] = useState(false);
- const [spaces, setSpaces] = useState();
- const [selectedSpaces, setSelectedSpaces] = useState([]);
-
- const [isImportingTweets, setIsImportingTweets] = useState(false);
-
- const [log, setLog] = useState([]);
-
- interface TweetData {
- tweetText: string;
- postUrl: string;
- authorName: string;
- handle: string;
- time: string;
- saveToUser: string;
- }
-
- function sendBookmarkedTweetsToAPI(tweets: TweetData[], token: string) {
- chrome.runtime.sendMessage({
- type: "sendBookmarkedTweets",
- jwt: token,
- tweets,
- });
- }
-
- const fetchSpaces = async () => {
- setLoading(true);
- chrome.runtime.sendMessage({ type: "fetchSpaces" }, (resp) => {
- console.log("response", resp);
- setSpaces(resp);
- setLoading(false);
- });
- };
-
- const fetchBookmarks = () => {
- const tweets: TweetData[] = []; // Initialize an empty array to hold all tweet elements
-
- setIsImportingTweets(true);
- console.log("Importing tweets");
-
- const scrollInterval = 1000;
- const scrollStep = 5000; // Pixels to scroll on each step
-
- let previousTweetCount = 0;
- let unchangedCount = 0;
-
- const scrollToEndIntervalID = setInterval(() => {
- window.scrollBy(0, scrollStep);
- const currentTweetCount = tweets.length;
- if (currentTweetCount === previousTweetCount) {
- unchangedCount++;
- if (unchangedCount >= 2) {
- setLog([
- ...log,
- "Scraping complete",
- `Total tweets scraped: ${tweets.length}`,
- "Downloading tweets as JSON...",
- ]);
- clearInterval(scrollToEndIntervalID); // Stop scrolling
- observer.disconnect(); // Stop observing DOM changes
- downloadTweetsAsJson(tweets); // Download the tweets list as a JSON file
- }
- } else {
- unchangedCount = 0; // Reset counter if new tweets were added
- }
- previousTweetCount = currentTweetCount; // Update previous count for the next check
- }, scrollInterval);
-
- function updateTweets() {
- document
- .querySelectorAll('article[data-testid="tweet"]')
- .forEach((tweetElement) => {
- const authorName = (
- tweetElement.querySelector(
- '[data-testid="User-Name"]',
- ) as HTMLElement
- )?.innerText;
-
- const handle = (
- tweetElement.querySelector('[role="link"]') as HTMLLinkElement
- ).href
- .split("/")
- .pop();
-
- const tweetText = (
- tweetElement.querySelector(
- '[data-testid="tweetText"]',
- ) as HTMLElement
- )?.innerText;
- const time = (
- tweetElement.querySelector("time") as HTMLTimeElement
- ).getAttribute("datetime");
- const postUrl = (
- tweetElement.querySelector(
- ".css-175oi2r.r-18u37iz.r-1q142lx a",
- ) as HTMLLinkElement
- )?.href;
-
- const isTweetNew = !tweets.some((tweet) => tweet.postUrl === postUrl);
- if (isTweetNew) {
- tweets.push({
- authorName,
- handle: handle ?? "",
- tweetText,
- time: time ?? "",
- postUrl,
- saveToUser: jwt,
- });
-
- setLog([...log, `Scraped tweet: ${tweets.length}`]);
- }
- });
- }
-
- // Initially populate the tweets array
- updateTweets();
-
- // Create a MutationObserver to observe changes in the DOM
- const observer = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.addedNodes.length) {
- updateTweets(); // Call updateTweets whenever new nodes are added to the DOM
- }
- });
- });
-
- // Start observing the document body for child list changes
- observer.observe(document.body, { childList: true, subtree: true });
-
- function downloadTweetsAsJson(tweetsArray: TweetData[]) {
- setLog([...log, "Saving the tweets to our database..."]);
- sendBookmarkedTweetsToAPI(tweetsArray, jwt);
- setIsImportingTweets(false);
- }
- };
-
- return (
- <>
- {isImportingTweets && (
-
-
-
-
-
-
-
Importing your tweets...
-
- {log.map((message, index) => (
-
{message}
- ))}
-
-
-
-
- )}
-
-
-
- {window.location.href.includes("twitter.com") ||
- window.location.href.includes("x.com") ? (
-
-
- {
- if (window.location.href.endsWith("/i/bookmarks/all")) {
- fetchBookmarks();
- } else {
- window.location.href =
- "https://twitter.com/i/bookmarks/all";
-
- setTimeout(() => {
- fetchBookmarks();
- }, 2500);
- }
- }}
- className="anycontext-open-button disabled:anycontext-opacity-30 anycontext-bg-transparent
- anycontext-border-none anycontext-m-0 anycontext-p-0"
- >
-
-
-
-
-
-
-
- Import twitter bookmarks
-
-
-
- ) : (
- <>>
- )}
-
open === true && fetchSpaces()}>
-
-
-
-
- {savedWebsites.includes(window.location.href) ? (
-
-
-
-
-
- ) : (
-
-
-
- )}
-
-
-
-
-
- {savedWebsites.includes(window.location.href)
- ? "Added to memory"
- : "Add to memory"}
-
-
-
-
-
- Add to Memory
-
- Add the current page to memory
-
-
-
-
-
- {
- sendUrlToAPI(selectedSpaces);
- setIsSendingData(true);
- setTimeout(() => {
- setIsSendingData(false);
- setSavedWebsites([
- ...savedWebsites,
- window.location.href,
- ]);
- }, 1000);
- }}
- >
- Add
-
- Cancel
-
-
-
-
-
- >
- );
-}
-
-export default SideBar;
diff --git a/apps/extension/src/assets/Memories.tsx b/apps/extension/src/assets/Memories.tsx
deleted file mode 100644
index 0c138b1e..00000000
--- a/apps/extension/src/assets/Memories.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-export const MemoryIcon: React.FC> = (
- props,
-) => (
-
-
-
-
-
-);
-
-export const SpaceIcon: React.FC> = (props) => (
-
-
-
-
-);
diff --git a/apps/extension/src/assets/react.svg b/apps/extension/src/assets/react.svg
deleted file mode 100644
index 6c87de9b..00000000
--- a/apps/extension/src/assets/react.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/apps/extension/src/background.ts b/apps/extension/src/background.ts
deleted file mode 100644
index d2f8759e..00000000
--- a/apps/extension/src/background.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-import { getEnv } from "./util";
-import { Space } from "./types/memory";
-
-const backendUrl =
- getEnv() === "development"
- ? "http://localhost:3000"
- : "https://supermemory.dhr.wtf";
-
-interface TweetData {
- tweetText: string;
- postUrl: string;
- authorName: string;
- handle: string;
- time: string;
- saveToUser: string;
-}
-
-// TODO: Implement getting bookmarks from Twitter API directly
-// let authorizationHeader: string | null = null;
-// let csrfToken: string | null = null;
-// let cookies: string | null = null;
-
-// chrome.webRequest.onBeforeSendHeaders.addListener(
-// (details) => {
-// for (let i = 0; i < details.requestHeaders!.length; ++i) {
-// const header = details.requestHeaders![i];
-// if (header.name.toLowerCase() === 'authorization') {
-// authorizationHeader = header.value || null;
-// } else if (header.name.toLowerCase() === 'x-csrf-token') {
-// csrfToken = header.value || null;
-// } else if (header.name.toLowerCase() === 'cookie') {
-// cookies = header.value || null;
-// }
-
-// console.log(header, authorizationHeader, csrfToken, cookies)
-// }
-// },
-// { urls: ['https://twitter.com/*', 'https://x.com/*'] },
-// ['requestHeaders']
-// );
-
-chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
- if (request.type === "getJwt") {
- chrome.storage.local.get(["jwt"], ({ jwt }) => {
- sendResponse({ jwt });
- });
-
- return true;
- } else if (request.type === "urlChange") {
- const content = request.content;
- const url = request.url;
- const spaces = request.spaces(
- // eslint-disable-next-line no-unexpected-multiline
- async () => {
- chrome.storage.local.get(["jwt"], ({ jwt }) => {
- if (!jwt) {
- console.error("No JWT found");
- return;
- }
- fetch(`${backendUrl}/api/store`, {
- method: "POST",
- headers: {
- Authorization: `Bearer ${jwt}`,
- },
- body: JSON.stringify({ pageContent: content, url, spaces }),
- }).then((ers) => console.log(ers.status));
- });
- },
- )();
- } else if (request.type === "fetchSpaces") {
- chrome.storage.local.get(["jwt"], async ({ jwt }) => {
- if (!jwt) {
- console.error("No JWT found");
- return;
- }
- const resp = await fetch(`${backendUrl}/api/spaces`, {
- headers: {
- Authorization: `Bearer ${jwt}`,
- },
- });
-
- const data: {
- message: "OK" | string;
- data: Space[] | undefined;
- } = await resp.json();
-
- if (data.message === "OK" && data.data) {
- sendResponse(data.data);
- }
- });
-
- return true;
- } else if (request.type === "queryApi") {
- const input = request.input;
- const jwt = request.jwt;
-
- (async () => {
- await fetch(`${backendUrl}/api/ask`, {
- method: "POST",
- headers: {
- Authorization: `Bearer ${jwt}`,
- },
- body: JSON.stringify({
- query: input,
- }),
- }).then(async (response) => {
- if (!response.body) {
- throw new Error("No response body");
- }
- if (!sender.tab?.id) {
- throw new Error("No tab ID");
- }
- const reader = response.body.getReader();
- // eslint-disable-next-line no-constant-condition
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
- // For simplicity, we're sending chunks as they come.
- // This might need to be adapted based on your data and needs.
- const chunkAsString = new TextDecoder("utf-8")
- .decode(value)
- .replace("data: ", "");
- chrome.tabs.sendMessage(sender.tab.id, {
- action: "streamData",
- data: chunkAsString,
- });
- }
- // Notify the content script that the stream is complete.
- chrome.tabs.sendMessage(sender.tab.id, { action: "streamEnd" });
- });
- // Indicate that sendResponse will be called asynchronously.
- return true;
- })();
- }
- // TODO: Implement getting bookmarks from Twitter API directly
- // else if (request.action === 'getAuthData') {
- // sendResponse({
- // authorizationHeader: authorizationHeader,
- // csrfToken: csrfToken,
- // cookies: cookies
- // });
- // }
- else if (request.type === "sendBookmarkedTweets") {
- const jwt = request.jwt;
- const tweets = request.tweets as TweetData[];
-
- (async () => {
- await fetch(`${backendUrl}/api/vectorizeTweets`, {
- method: "POST",
- headers: {
- Authorization: `Bearer ${jwt}`,
- },
- body: JSON.stringify(tweets),
- }).then(async (response) => {
- return response.json();
- });
- })();
-
- return true;
- }
-});
diff --git a/apps/extension/src/components/FilterCombobox.tsx b/apps/extension/src/components/FilterCombobox.tsx
deleted file mode 100644
index 3c8779b6..00000000
--- a/apps/extension/src/components/FilterCombobox.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import * as React from "react";
-import { PlusCircleIcon, X } from "lucide-react";
-import { Space } from "../types/memory";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
-} from "./ui/dropdown-menu";
-import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
-
-export interface Props extends React.ButtonHTMLAttributes {
- selectedSpaces: number[];
- setSelectedSpaces: (
- spaces: number[] | ((prev: number[]) => number[]),
- ) => void;
- name: string;
- spaces: Space[];
- loading: boolean;
-}
-
-export function FilterSpaces({
- loading,
- selectedSpaces,
- setSelectedSpaces,
- spaces,
-}: Props) {
- console.log(selectedSpaces, spaces);
-
- const filteredSpaces = spaces.filter((space) =>
- selectedSpaces.includes(space.id),
- );
- const leftSpaces = spaces.filter(
- (space) => !selectedSpaces.includes(space.id),
- );
-
- if (loading) {
- return "Loading...";
- }
-
- return (
-
- {filteredSpaces.length < 1 && "Add to a space"}
- {filteredSpaces.map((space) => (
-
- setSelectedSpaces((prev) => prev.filter((s) => s !== space.id))
- }
- />
- ))}
- {leftSpaces.length > 0 && (
-
-
-
-
-
- {leftSpaces.map((space) => (
- <>
- {loading && "Loading..."}
-
- setSelectedSpaces((prev) => [...prev, space.id])
- }
- >
- {space.name}
-
- >
- ))}
-
-
- )}
-
- );
-}
-
-function SpaceItem({ name, onRemove }: Space & { onRemove: () => void }) {
- return (
-
-
-
-
- {name}
-
- );
-}
diff --git a/apps/extension/src/components/ui/button.tsx b/apps/extension/src/components/ui/button.tsx
deleted file mode 100644
index 6ca7d07a..00000000
--- a/apps/extension/src/components/ui/button.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-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";
-
-const buttonVariants = cva(
- "anycontext-inline-flex anycontext-items-center anycontext-justify-center anycontext-whitespace-nowrap anycontext-rounded-md anycontext-text-sm anycontext-font-medium anycontext-ring-offset-white anycontext-transition-colors focus-visible:anycontext-outline-none focus-visible:anycontext-ring-2 focus-visible:anycontext-ring-stone-950 focus-visible:anycontext-ring-offset-2 disabled:anycontext-pointer-events-none disabled:anycontext-opacity-50 dark:anycontext-ring-offset-stone-950 dark:focus-visible:anycontext-ring-stone-300",
- {
- variants: {
- variant: {
- default:
- "anycontext-bg-stone-900 anycontext-text-stone-50 hover:anycontext-bg-stone-900/90 dark:anycontext-bg-stone-50 dark:anycontext-text-stone-900 dark:hover:anycontext-bg-stone-50/90",
- destructive:
- "anycontext-bg-red-500 anycontext-text-stone-50 hover:anycontext-bg-red-500/90 dark:anycontext-bg-red-900 dark:anycontext-text-stone-50 dark:hover:anycontext-bg-red-900/90",
- outline:
- "anycontext-border anycontext-border-stone-200 anycontext-bg-white hover:anycontext-bg-stone-100 hover:anycontext-text-stone-900 dark:anycontext-border-stone-800 dark:anycontext-bg-stone-950 dark:hover:anycontext-bg-stone-800 dark:hover:anycontext-text-stone-50",
- secondary:
- "anycontext-bg-stone-100 anycontext-text-stone-900 hover:anycontext-bg-stone-100/80 dark:anycontext-bg-stone-800 dark:anycontext-text-stone-50 dark:hover:anycontext-bg-stone-800/80",
- ghost:
- "hover:anycontext-bg-stone-100 hover:anycontext-text-stone-900 dark:hover:anycontext-bg-stone-800 dark:hover:anycontext-text-stone-50",
- link: "anycontext-text-stone-900 anycontext-underline-offset-4 hover:anycontext-underline dark:anycontext-text-stone-50",
- },
- size: {
- default: "anycontext-h-10 anycontext-px-4 anycontext-py-2",
- sm: "anycontext-h-9 anycontext-rounded-md anycontext-px-3",
- lg: "anycontext-h-11 anycontext-rounded-md anycontext-px-8",
- icon: "anycontext-h-10 anycontext-w-10",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- },
-);
-
-export interface ButtonProps
- extends React.ButtonHTMLAttributes,
- VariantProps {
- asChild?: boolean;
-}
-
-const Button = React.forwardRef(
- ({ className, variant, size, asChild = false, ...props }, ref) => {
- const Comp = asChild ? Slot : "button";
- return (
-
- );
- },
-);
-Button.displayName = "Button";
-
-export { Button };
diff --git a/apps/extension/src/components/ui/command.tsx b/apps/extension/src/components/ui/command.tsx
deleted file mode 100644
index 858b67f4..00000000
--- a/apps/extension/src/components/ui/command.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import * as React from "react";
-import { type DialogProps } from "@radix-ui/react-dialog";
-import { Command as CommandPrimitive } from "cmdk";
-import { Search } from "lucide-react";
-
-import { cn } from "../../lib/utils";
-import { Dialog, DialogContent } from "../../components/ui/dialog";
-
-const Command = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-Command.displayName = CommandPrimitive.displayName;
-
-interface CommandDialogProps extends DialogProps {}
-
-const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
- return (
-
-
-
- {children}
-
-
-
- );
-};
-
-const CommandInput = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-
-
-
-));
-
-CommandInput.displayName = CommandPrimitive.Input.displayName;
-
-const CommandList = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandList.displayName = CommandPrimitive.List.displayName;
-
-const CommandEmpty = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->((props, ref) => (
-
-));
-
-CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
-
-const CommandGroup = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandGroup.displayName = CommandPrimitive.Group.displayName;
-
-const CommandSeparator = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
-
-const CommandItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandItem.displayName = CommandPrimitive.Item.displayName;
-
-const CommandShortcut = ({
- className,
- ...props
-}: React.HTMLAttributes) => {
- return (
-
- );
-};
-CommandShortcut.displayName = "CommandShortcut";
-
-export {
- Command,
- CommandDialog,
- CommandInput,
- CommandList,
- CommandEmpty,
- CommandGroup,
- CommandItem,
- CommandShortcut,
- CommandSeparator,
-};
diff --git a/apps/extension/src/components/ui/dialog.tsx b/apps/extension/src/components/ui/dialog.tsx
deleted file mode 100644
index 583c335d..00000000
--- a/apps/extension/src/components/ui/dialog.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import * as React from "react";
-import * as DialogPrimitive from "@radix-ui/react-dialog";
-import { X } from "lucide-react";
-
-import { cn } from "../../lib/utils";
-
-const Dialog = DialogPrimitive.Root;
-
-const DialogTrigger = DialogPrimitive.Trigger;
-
-const DialogPortal = DialogPrimitive.Portal;
-
-const DialogClose = DialogPrimitive.Close;
-
-const DialogOverlay = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
-
-const DialogContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
-
-
-
- {children}
-
-
- Close
-
-
-
-));
-DialogContent.displayName = DialogPrimitive.Content.displayName;
-
-const DialogHeader = ({
- className,
- ...props
-}: React.HTMLAttributes) => (
-
-);
-DialogHeader.displayName = "DialogHeader";
-
-const DialogFooter = ({
- className,
- ...props
-}: React.HTMLAttributes) => (
-
-);
-DialogFooter.displayName = "DialogFooter";
-
-const DialogTitle = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DialogTitle.displayName = DialogPrimitive.Title.displayName;
-
-const DialogDescription = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DialogDescription.displayName = DialogPrimitive.Description.displayName;
-
-export {
- Dialog,
- DialogPortal,
- DialogOverlay,
- DialogClose,
- DialogTrigger,
- DialogContent,
- DialogHeader,
- DialogFooter,
- DialogTitle,
- DialogDescription,
-};
diff --git a/apps/extension/src/components/ui/dropdown-menu.tsx b/apps/extension/src/components/ui/dropdown-menu.tsx
deleted file mode 100644
index fcc1edb2..00000000
--- a/apps/extension/src/components/ui/dropdown-menu.tsx
+++ /dev/null
@@ -1,204 +0,0 @@
-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";
-
-const DropdownMenu = DropdownMenuPrimitive.Root;
-
-const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
-
-const DropdownMenuGroup = DropdownMenuPrimitive.Group;
-
-const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
-
-const DropdownMenuSub = DropdownMenuPrimitive.Sub;
-
-const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
-
-const DropdownMenuSubTrigger = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, children, ...props }, ref) => (
-
- {children}
-
-
-));
-DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName;
-
-const DropdownMenuSubContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName;
-
-const DropdownMenuContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, sideOffset = 4, ...props }, ref) => (
-
-
-
-));
-DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
-
-const DropdownMenuItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, ...props }, ref) => (
-
-));
-DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
-
-const DropdownMenuCheckboxItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, checked, ...props }, ref) => (
-
-
-
-
-
-
- {children}
-
-));
-DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName;
-
-const DropdownMenuRadioItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
-
-
-
-
-
-
- {children}
-
-));
-DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
-
-const DropdownMenuLabel = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, ...props }, ref) => (
-
-));
-DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
-
-const DropdownMenuSeparator = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
-
-const DropdownMenuShortcut = ({
- className,
- ...props
-}: React.HTMLAttributes) => {
- return (
-
- );
-};
-DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
-
-export {
- DropdownMenu,
- DropdownMenuTrigger,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuCheckboxItem,
- DropdownMenuRadioItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuGroup,
- DropdownMenuPortal,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuRadioGroup,
-};
diff --git a/apps/extension/src/components/ui/input.tsx b/apps/extension/src/components/ui/input.tsx
deleted file mode 100644
index 4771795a..00000000
--- a/apps/extension/src/components/ui/input.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as React from "react";
-
-import { cn } from "../../lib/utils";
-
-export interface InputProps
- extends React.InputHTMLAttributes {}
-
-const Input = React.forwardRef(
- ({ className, type, ...props }, ref) => {
- return (
-
- );
- },
-);
-Input.displayName = "Input";
-
-export { Input };
diff --git a/apps/extension/src/components/ui/popover.tsx b/apps/extension/src/components/ui/popover.tsx
deleted file mode 100644
index e1b9282d..00000000
--- a/apps/extension/src/components/ui/popover.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import * as React from "react";
-import * as PopoverPrimitive from "@radix-ui/react-popover";
-
-import { cn } from "../../lib/utils";
-
-const Popover = PopoverPrimitive.Root;
-
-const PopoverTrigger = PopoverPrimitive.Trigger;
-
-const PopoverContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
-
-
-
-));
-PopoverContent.displayName = PopoverPrimitive.Content.displayName;
-
-export { Popover, PopoverTrigger, PopoverContent };
diff --git a/apps/extension/src/components/ui/tooltip.tsx b/apps/extension/src/components/ui/tooltip.tsx
deleted file mode 100644
index 12185db5..00000000
--- a/apps/extension/src/components/ui/tooltip.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import * as React from "react";
-import * as TooltipPrimitive from "@radix-ui/react-tooltip";
-
-import { cn } from "../../lib/utils";
-
-const TooltipProvider = TooltipPrimitive.Provider;
-
-const Tooltip = TooltipPrimitive.Root;
-
-const TooltipTrigger = TooltipPrimitive.Trigger;
-
-const TooltipContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, sideOffset = 4, ...props }, ref) => (
-
-));
-TooltipContent.displayName = TooltipPrimitive.Content.displayName;
-
-export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
diff --git a/apps/extension/src/content.tsx b/apps/extension/src/content.tsx
deleted file mode 100644
index 83d976cd..00000000
--- a/apps/extension/src/content.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-window.addEventListener("message", (event) => {
- if (event.source !== window) {
- return;
- }
- const { jwt } = event.data;
-
- if (jwt) {
- if (
- !(
- window.location.hostname === "localhost" ||
- window.location.hostname === "anycontext.dhr.wtf" ||
- window.location.hostname === "supermemory.dhr.wtf"
- )
- ) {
- console.log(
- "JWT is only allowed to be used on localhost or anycontext.dhr.wtf",
- );
- return;
- }
-
- chrome.storage.local.set({ jwt }, () => {});
- }
-});
-
-const appContainer = document.createElement("div");
-appContainer.id = "anycontext-app-container";
-
-// First in the body, above the content
-document.body.insertBefore(appContainer, document.body.firstChild);
-
-appContainer.style.zIndex = "9999";
-
-import ReactDOM from "react-dom/client";
-import SideBar from "./SideBar";
-
-// get JWT from local storage
-const jwt = chrome.storage.local.get("jwt").then((data) => {
- return data.jwt;
-}) as Promise;
-
-jwt.then((token) => {
- ReactDOM.createRoot(
- document.getElementById("anycontext-app-container")!,
- ).render( );
-});
diff --git a/apps/extension/src/ext.css b/apps/extension/src/ext.css
deleted file mode 100644
index bf7a4156..00000000
--- a/apps/extension/src/ext.css
+++ /dev/null
@@ -1,85 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-.anycontext-combobox-button {
- padding: 0.5rem 1rem;
- display: flex;
- flex-direction: row;
- justify-items: center;
- align-items: center;
- gap: 0.5rem;
- @apply anycontext-rounded-md dark:anycontext-bg-white/5 anycontext-bg-black/5;
-}
-
-.anycontext-overlay {
- position: fixed;
- top: 0;
- left: 0;
- min-height: 100vh;
- width: 100%;
- height: 100%;
- background-color: rgba(0, 0, 0, 0.5);
- z-index: 99998;
-}
-
-.anycontext-sidebar {
- position: fixed;
- top: 0;
- right: 0;
- min-height: 100vh;
- width: 100%;
- max-width: 31%; /* Responsive width */
- z-index: 99999;
- padding: 8px 16px; /* px-4 py-2 */
-}
-
-.anycontext-sidebar-content {
- position: relative;
- display: flex;
- flex-direction: column;
- height: 95vh;
- background-color: white;
- border-radius: 8px; /* rounded-lg */
- padding: 8px; /* px-2 */
- box-shadow:
- 0 4px 6px -1px rgba(0, 0, 0, 0.1),
- 0 2px 4px -1px rgba(0, 0, 0, 0.06); /* shadow-md */
-}
-
-.anycontext-close-button {
- position: absolute;
- right: 0;
- padding: 8px; /* p-2 */
- border-radius: 4px; /* rounded-md */
- margin: 8px; /* m-2 */
-}
-
-.anycontext-close-button:hover {
- background-color: rgba(114, 87, 255, 0.5); /* hover:bg-[#7257ff]/50 */
- color: white; /* hover:text-white */
-}
-
-.anycontext-open-button {
- color: white;
- background-color: #7257ff50; /* bg-indigo-600 */
- background-opacity: 75%;
- cursor: pointer;
- padding: 8px; /* px-4 py-2 */
- border-radius: 4px 0 0 4px; /* rounded-l-md */
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.anycontext-header {
- margin: 16px; /* m-4 */
- font-weight: 600; /* font-semibold */
- font-size: 1.25rem; /* text-xl */
- color: black;
-}
-
-.anycontext-icon {
- height: 24px; /* h-6 */
- width: 24px; /* w-6 */
-}
diff --git a/apps/extension/src/lib/utils.ts b/apps/extension/src/lib/utils.ts
deleted file mode 100644
index 365058ce..00000000
--- a/apps/extension/src/lib/utils.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { type ClassValue, clsx } from "clsx";
-import { twMerge } from "tailwind-merge";
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
-}
diff --git a/apps/extension/src/main.tsx b/apps/extension/src/main.tsx
deleted file mode 100644
index b5c00920..00000000
--- a/apps/extension/src/main.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from "react";
-import ReactDOM from "react-dom/client";
-import App from "./App.tsx";
-
-ReactDOM.createRoot(document.getElementById("root")!).render(
-
-
- ,
-);
diff --git a/apps/extension/src/types/memory.ts b/apps/extension/src/types/memory.ts
deleted file mode 100644
index 03ffb848..00000000
--- a/apps/extension/src/types/memory.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export type Space = {
- id: number;
- name: string;
-};
diff --git a/apps/extension/src/types/zods.ts b/apps/extension/src/types/zods.ts
deleted file mode 100644
index 3316aa16..00000000
--- a/apps/extension/src/types/zods.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { z } from "zod";
-
-export const userObj = z.object({
- message: z.string(),
- data: z.object({
- session: z.object({
- sessionToken: z.string(),
- userId: z.string(),
- expires: z.string(),
- }),
- user: z.object({
- id: z.string(),
- name: z.string(),
- email: z.string().nullable().optional(),
- emailVerified: z.string().nullable(),
- image: z.string().nullable().optional(),
- }),
- }),
-});
diff --git a/apps/extension/src/util.ts b/apps/extension/src/util.ts
deleted file mode 100644
index d2ea35d3..00000000
--- a/apps/extension/src/util.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export const getEnv = () => {
- // chrome.management.getSelf((self) => {
- // if (self.installType === 'development') {
- // return "development"
- // }
- // else {
- // return "production"
- // }
- // })
-
- // return null
- return "production";
-};
diff --git a/apps/extension/src/vite-env.d.ts b/apps/extension/src/vite-env.d.ts
deleted file mode 100644
index 11f02fe2..00000000
--- a/apps/extension/src/vite-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/apps/extension/tailwind.config.js b/apps/extension/tailwind.config.js
deleted file mode 100644
index ed971842..00000000
--- a/apps/extension/tailwind.config.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import tailwindcssAnimate from "tailwindcss-animate";
-/** @type {import('tailwindcss').Config} */
-export default {
- //darkMode: "prefe",
- content: [
- "./pages/**/*.{ts,tsx}",
- "./components/**/*.{ts,tsx}",
- "./app/**/*.{ts,tsx}",
- "./src/**/*.{ts,tsx}",
- "index.html",
- ],
- prefix: "anycontext-",
- theme: {
- container: {
- center: true,
- padding: "2rem",
- screens: {
- "2xl": "1400px",
- },
- },
- extend: {
- keyframes: {
- "accordion-down": {
- from: { height: "0" },
- to: { height: "var(--radix-accordion-content-height)" },
- },
- "accordion-up": {
- from: { height: "var(--radix-accordion-content-height)" },
- to: { height: "0" },
- },
- },
- animation: {
- "accordion-down": "accordion-down 0.2s ease-out",
- "accordion-up": "accordion-up 0.2s ease-out",
- },
- },
- },
- plugins: [tailwindcssAnimate],
- corePlugins: {
- preflight: false,
- },
-};
diff --git a/apps/extension/tsconfig.json b/apps/extension/tsconfig.json
deleted file mode 100644
index 2fefaeb1..00000000
--- a/apps/extension/tsconfig.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2020",
- "useDefineForClassFields": true,
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "react-jsx",
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true
- },
- "include": ["src"],
- "references": [{ "path": "./tsconfig.node.json" }],
- "types": ["chrome"]
-}
diff --git a/apps/extension/tsconfig.node.json b/apps/extension/tsconfig.node.json
deleted file mode 100644
index 97ede7ee..00000000
--- a/apps/extension/tsconfig.node.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "compilerOptions": {
- "composite": true,
- "skipLibCheck": true,
- "module": "ESNext",
- "moduleResolution": "bundler",
- "allowSyntheticDefaultImports": true,
- "strict": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/apps/extension/vite.config.ts b/apps/extension/vite.config.ts
deleted file mode 100644
index c2b53f80..00000000
--- a/apps/extension/vite.config.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Plugin, defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
-import { crx } from "@crxjs/vite-plugin";
-import manifest from "./manifest.json";
-import path from "path";
-
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const viteManifestHackIssue846: Plugin & {
- renderCrxManifest: (manifest: unknown, bundle: any) => void;
-} = {
- // Workaround from https://github.com/crxjs/chrome-extension-tools/issues/846#issuecomment-1861880919.
- name: "manifestHackIssue846",
- renderCrxManifest(_manifest, bundle) {
- bundle["manifest.json"] = bundle[".vite/manifest.json"];
- bundle["manifest.json"].fileName = "manifest.json";
- delete bundle[".vite/manifest.json"];
- },
-};
-
-export default defineConfig({
- plugins: [react(), crx({ manifest }), viteManifestHackIssue846],
- resolve: {
- alias: {
- "@": path.resolve(__dirname, "./src"),
- },
- },
-});
diff --git a/apps/web-v2/.eslintrc.json b/apps/web-v2/.eslintrc.json
deleted file mode 100644
index abd7bea7..00000000
--- a/apps/web-v2/.eslintrc.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extends": [
- "next/core-web-vitals",
- "plugin:eslint-plugin-next-on-pages/recommended"
- ],
- "plugins": ["eslint-plugin-next-on-pages"]
-}
diff --git a/apps/web-v2/.gitignore b/apps/web-v2/.gitignore
deleted file mode 100644
index c213988d..00000000
--- a/apps/web-v2/.gitignore
+++ /dev/null
@@ -1,40 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-.yarn/install-state.gz
-
-# testing
-/coverage
-
-# next.js
-/.next/
-/out/
-
-# production
-/build
-
-# misc
-.DS_Store
-*.pem
-
-# debug
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-# local env files
-.env*.local
-
-# vercel
-.vercel
-
-# typescript
-*.tsbuildinfo
-next-env.d.ts
-
-# wrangler files
-.wrangler
-.dev.vars
diff --git a/apps/web-v2/README.md b/apps/web-v2/README.md
deleted file mode 100644
index 64c8f3bc..00000000
--- a/apps/web-v2/README.md
+++ /dev/null
@@ -1,70 +0,0 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`c3`](https://developers.cloudflare.com/pages/get-started/c3).
-
-## Getting Started
-
-First, run the development server:
-
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
-
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-
-## 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))
-
-### Bindings
-
-Cloudflare [Bindings](https://developers.cloudflare.com/pages/functions/bindings/) are what allows you to interact with resources available in the Cloudflare Platform.
-
-You can use bindings during development, when previewing locally your application and of course in the deployed application:
-
-- To use bindings in dev mode you need to define them in the `next.config.js` file under `setupDevBindings`, this mode uses the `next-dev` `@cloudflare/next-on-pages` submodule. For more details see its [documentation](https://github.com/cloudflare/next-on-pages/blob/05b6256/internal-packages/next-dev/README.md).
-
-- 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/).
-
-#### 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:
- ```
- and uncomment the commented lines below it.
-- Do the same in the `wrangler.toml` file, where
- the comment is:
- ```
- # KV Example:
- ```
-- If you're using TypeScript run the `cf-typegen` script to update the `env.d.ts` file:
- ```bash
- npm run cf-typegen
- # or
- yarn cf-typegen
- # or
- pnpm cf-typegen
- # or
- bun cf-typegen
- ```
-
-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.
diff --git a/apps/web-v2/components.json b/apps/web-v2/components.json
deleted file mode 100644
index 268ae38d..00000000
--- a/apps/web-v2/components.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "$schema": "https://ui.shadcn.com/schema.json",
- "style": "default",
- "rsc": true,
- "tsx": true,
- "tailwind": {
- "config": "tailwind.config.ts",
- "css": "src/app/globals.css",
- "baseColor": "zinc",
- "cssVariables": true,
- "prefix": ""
- },
- "aliases": {
- "components": "@/components",
- "utils": "@/lib/utils"
- }
-}
diff --git a/apps/web-v2/env.d.ts b/apps/web-v2/env.d.ts
deleted file mode 100644
index ac17de2c..00000000
--- a/apps/web-v2/env.d.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// Generated by Wrangler
-// by running `wrangler types --env-interface CloudflareEnv env.d.ts`
-
-interface CloudflareEnv {
- RATELIMITER: RateLimitBinding;
-}
-
-export interface RateLimitBinding {
- limit: LimitFunc;
-}
-
-export interface LimitFunc {
- (options: LimitOptions): Promise;
-}
-
-interface RateLimitResult {
- success: boolean;
-}
-
-export interface LimitOptions {
- key: string;
-}
-
-export interface RateLimitResponse {
- key: string;
- success: boolean;
-}
-
-export interface RateLimitOptions {
- continueOnRateLimit: boolean;
-}
-
-export type RateLimitKeyFunc = {
- (c: Context): string;
-};
-
-declare global {
- namespace NodeJS {
- interface ProcessEnv {
- // [key: string]: string | undefined;
- RATELIMITER: RateLimitBinding;
- CLOUDFLARE_TURNSTILE_TOKEN: string;
- }
- }
-}
-
-export {};
diff --git a/apps/web-v2/next.config.mjs b/apps/web-v2/next.config.mjs
deleted file mode 100644
index b0c1476c..00000000
--- a/apps/web-v2/next.config.mjs
+++ /dev/null
@@ -1,13 +0,0 @@
-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") {
- await setupDevPlatform();
-}
-
-/** @type {import('next').NextConfig} */
-const nextConfig = {};
-
-export default nextConfig;
diff --git a/apps/web-v2/package.json b/apps/web-v2/package.json
deleted file mode 100644
index 69a39888..00000000
--- a/apps/web-v2/package.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "name": "web-v2",
- "version": "0.1.0",
- "private": true,
- "scripts": {
- "dev": "next dev",
- "build": "next build",
- "start": "next start",
- "lint": "next lint",
- "pages:build": "bunx @cloudflare/next-on-pages",
- "preview": "bun pages:build && wrangler pages dev",
- "deploy": "bun pages:build && wrangler pages deploy",
- "cf-typegen": "wrangler types --env-interface CloudflareEnv env.d.ts"
- },
- "dependencies": {
- "@radix-ui/react-toast": "^1.1.5",
- "class-variance-authority": "^0.7.0",
- "clsx": "^2.1.1",
- "lucide-react": "^0.378.0",
- "next": "14.1.0",
- "react": "^18",
- "react-dom": "^18",
- "tailwind-merge": "^2.3.0",
- "tailwindcss-animate": "^1.0.7"
- },
- "devDependencies": {
- "@cloudflare/next-on-pages": "1",
- "@cloudflare/workers-types": "^4.20240512.0",
- "@types/node": "^20",
- "@types/react": "^18",
- "@types/react-dom": "^18",
- "autoprefixer": "^10.0.1",
- "eslint": "^8",
- "eslint-config-next": "14.1.0",
- "eslint-plugin-next-on-pages": "^1.11.3",
- "postcss": "^8",
- "tailwindcss": "^3.3.0",
- "typescript": "^5",
- "vercel": "^34.2.0",
- "wrangler": "^3.57.0"
- }
-}
diff --git a/apps/web-v2/postcss.config.js b/apps/web-v2/postcss.config.js
deleted file mode 100644
index 12a703d9..00000000
--- a/apps/web-v2/postcss.config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
- plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
-};
diff --git a/apps/web-v2/public/favicon.ico b/apps/web-v2/public/favicon.ico
deleted file mode 100644
index dd86c6dd..00000000
Binary files a/apps/web-v2/public/favicon.ico and /dev/null differ
diff --git a/apps/web-v2/public/og-image.png b/apps/web-v2/public/og-image.png
deleted file mode 100644
index c5a61b17..00000000
Binary files a/apps/web-v2/public/og-image.png and /dev/null differ
diff --git a/apps/web-v2/public/site.webmanifest b/apps/web-v2/public/site.webmanifest
deleted file mode 100644
index c903e516..00000000
--- a/apps/web-v2/public/site.webmanifest
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "name": "Supermemory - your second brain.",
- "short_name": "Supermemory",
- "icons": [
- {
- "src": "/icons/android-chrome-192x192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/icons/android-chrome-512x512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
- ],
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
-}
diff --git a/apps/web-v2/src/app/(landing)/page.tsx b/apps/web-v2/src/app/(landing)/page.tsx
deleted file mode 100644
index 5351c51b..00000000
--- a/apps/web-v2/src/app/(landing)/page.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import RotatingIcons from "./RotatingIcons";
-import Hero from "./Hero";
-import Navbar from "./Navbar";
-import Cta from "./Cta";
-import { Toaster } from "@/components/ui/toaster";
-import Features from "./Features";
-import Footer from "./footer";
-import { Metadata } from "next";
-
-export const runtime = "edge";
-
-export const metadata: Metadata = {
- title: "Supermemory - Your personal second brain.",
- description:
- "Bring saved information from all over the internet into one place where you can connect it, and ask AI about it",
- openGraph: {
- images: [
- {
- url: "https://supermemory.ai/og-image.png",
- width: 1200,
- height: 627,
- alt: "Supermemory - Your personal second brain.",
- },
- ],
- },
- metadataBase: {
- host: "https://supermemory.ai",
- href: "/",
- origin: "https://supermemory.ai",
- password: "supermemory",
- hash: "supermemory",
- pathname: "/",
- search: "",
- username: "supermemoryai",
- hostname: "supermemory.ai",
- port: "",
- protocol: "https:",
- searchParams: new URLSearchParams(""),
- toString: () => "https://supermemory.ai/",
- toJSON: () => "https://supermemory.ai/",
- },
- twitter: {
- card: "summary_large_image",
- site: "https://supermemory.ai",
- creator: "https://supermemory.ai",
- title: "Supermemory - Your personal second brain.",
- description:
- "Bring saved information from all over the internet into one place where you can connect it, and ask AI about it",
- images: [
- {
- url: "https://supermemory.ai/og-image.png",
- width: 1200,
- height: 627,
- alt: "Supermemory - Your personal second brain.",
- },
- ],
- },
-};
-
-export default function Home() {
- return (
-
-
-
- {/* Background gradients */}
-
-
-
- {/* a blue gradient line that's slightly tilted with blur (a lotof blur)*/}
-
-
-
-
-
- {/* Hero section */}
-
-
- {/* Features section */}
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/web-v2/src/app/globals.css b/apps/web-v2/src/app/globals.css
deleted file mode 100644
index 67115e30..00000000
--- a/apps/web-v2/src/app/globals.css
+++ /dev/null
@@ -1,132 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
- --black-bg: #0f1114;
- --soft-foreground: #ffffff;
- --soft-foreground-text: #b2bcca;
-
- --background: 216, 14%, 7%;
- --foreground: 240 10% 3.9%;
-
- --card: 0 0% 100%;
- --card-foreground: 240 10% 3.9%;
-
- --popover: 0 0% 100%;
- --popover-foreground: 240 10% 3.9%;
-
- --primary: 240 5.9% 10%;
- --primary-foreground: 0 0% 98%;
-
- --secondary: 240 4.8% 95.9%;
- --secondary-foreground: 240 5.9% 10%;
-
- --muted: 240 4.8% 95.9%;
- --muted-foreground: 240 3.8% 46.1%;
-
- --accent: 240 4.8% 95.9%;
- --accent-foreground: 240 5.9% 10%;
-
- --destructive: 0 84.2% 60.2%;
- --destructive-foreground: 0 0% 98%;
-
- --border: 240 5.9% 90%;
- --input: 240 5.9% 90%;
- --ring: 240 10% 3.9%;
-
- --radius: 0.5rem;
- }
-
- .dark {
- --background: 216, 14%, 7%;
- --foreground: 0 0% 98%;
-
- --card: 240 10% 3.9%;
- --card-foreground: 0 0% 98%;
-
- --popover: 240 10% 3.9%;
- --popover-foreground: 0 0% 98%;
-
- --primary: 0 0% 98%;
- --primary-foreground: 240 5.9% 10%;
-
- --secondary: 240 3.7% 15.9%;
- --secondary-foreground: 0 0% 98%;
-
- --muted: 240 3.7% 15.9%;
- --muted-foreground: 240 5% 64.9%;
-
- --accent: 240 3.7% 15.9%;
- --accent-foreground: 0 0% 98%;
-
- --destructive: 0 62.8% 30.6%;
- --destructive-foreground: 0 0% 98%;
-
- --border: 240 3.7% 15.9%;
- --input: 240 3.7% 15.9%;
- --ring: 240 4.9% 83.9%;
- }
-}
-
-@layer base {
- * {
- @apply border-border;
- }
- body {
- @apply bg-background text-foreground;
- }
-}
-
-html {
- scroll-behavior: smooth;
-}
-
-/* width */
-::-webkit-scrollbar {
- width: 8px;
-}
-
-/* Track */
-::-webkit-scrollbar-track {
- background: transparent;
-}
-
-/* Handle */
-::-webkit-scrollbar-thumb {
- background: #131f2c;
-}
-
-/* Handle on hover */
-::-webkit-scrollbar-thumb:hover {
- background: #22303d;
-}
-
-body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(to bottom, transparent, var(--black-bg))
- var(--black-bg);
-}
-
-@layer utilities {
- .text-balance {
- text-wrap: balance;
- }
-}
-
-@keyframes rotate {
- 0% {
- transform: rotate(0deg) translateX(130px); /* Adjust radius */
- }
- 100% {
- transform: rotate(360deg) translateX(130px); /* Adjust radius */
- }
-}
-
-.icon-container {
- animation: rotate 10s linear infinite;
-}
diff --git a/apps/web-v2/src/app/layout.tsx b/apps/web-v2/src/app/layout.tsx
deleted file mode 100644
index 4af34eee..00000000
--- a/apps/web-v2/src/app/layout.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import type { Metadata } from "next";
-import { Inter } from "next/font/google";
-import "./globals.css";
-import Script from "next/script";
-
-const inter = Inter({ subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Supermemory - Your personal second brain.",
- description:
- "Bring saved information from all over the internet into one place where you can connect it, and ask AI about it",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
-
-
- {children}
-
- );
-}
diff --git a/apps/web-v2/src/app/not-found.tsx b/apps/web-v2/src/app/not-found.tsx
deleted file mode 100644
index 3409889a..00000000
--- a/apps/web-v2/src/app/not-found.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-export const runtime = "edge";
-
-export default function NotFound() {
- return (
- <>
- 404: This page could not be found.
-
-
-
-
- 404
-
-
-
This page could not be found.
-
-
-
- >
- );
-}
-
-const styles = {
- error: {
- fontFamily:
- 'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',
- height: "100vh",
- textAlign: "center",
- display: "flex",
- flexDirection: "column",
- alignItems: "center",
- justifyContent: "center",
- },
-
- desc: {
- display: "inline-block",
- },
-
- h1: {
- display: "inline-block",
- margin: "0 20px 0 0",
- padding: "0 23px 0 0",
- fontSize: 24,
- fontWeight: 500,
- verticalAlign: "top",
- lineHeight: "49px",
- },
-
- h2: {
- fontSize: 14,
- fontWeight: 400,
- lineHeight: "49px",
- margin: 0,
- },
-} as const;
diff --git a/apps/web-v2/src/components/ui/cardClick.tsx b/apps/web-v2/src/components/ui/cardClick.tsx
deleted file mode 100644
index 10fb4a07..00000000
--- a/apps/web-v2/src/components/ui/cardClick.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-"use client";
-
-import { cn } from "@/utils/cn";
-import { AnimatePresence, motion } from "framer-motion";
-import React from "react";
-
-export const CardClick = ({
- tab,
- handleClickIndex,
- items,
-}: {
- tab: number;
- handleClickIndex: (tab: number) => void;
- items: {
- title: string;
- description: string;
- svg: React.ReactNode;
- }[];
-}) => {
- return (
-
- {items.map((item, idx) => (
-
handleClickIndex(idx)}
- >
-
- {tab === idx && (
-
- )}
-
-
-
- ))}
-
- );
-};
-
-export const Card = ({
- title,
- description,
- svg,
-}: {
- title: string;
- description: string;
- svg: React.ReactNode;
-}) => {
- return (
-
- {svg}
-
-
- {title}
-
-
{description}
-
-
- );
-};
diff --git a/apps/web-v2/src/components/ui/toast.tsx b/apps/web-v2/src/components/ui/toast.tsx
deleted file mode 100644
index 56f4ebfd..00000000
--- a/apps/web-v2/src/components/ui/toast.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as ToastPrimitives from "@radix-ui/react-toast";
-import { cva, type VariantProps } from "class-variance-authority";
-import { X } from "lucide-react";
-
-import { cn } from "@/lib/utils";
-
-const ToastProvider = ToastPrimitives.Provider;
-
-const ToastViewport = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
-
-const toastVariants = cva(
- "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
- {
- variants: {
- variant: {
- default: "border bg-background text-foreground",
- destructive:
- "destructive group border-destructive bg-destructive text-destructive-foreground",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- },
-);
-
-const Toast = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef &
- VariantProps
->(({ className, variant, ...props }, ref) => {
- return (
-
- );
-});
-Toast.displayName = ToastPrimitives.Root.displayName;
-
-const ToastAction = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-ToastAction.displayName = ToastPrimitives.Action.displayName;
-
-const ToastClose = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-
-
-));
-ToastClose.displayName = ToastPrimitives.Close.displayName;
-
-const ToastTitle = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-ToastTitle.displayName = ToastPrimitives.Title.displayName;
-
-const ToastDescription = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-ToastDescription.displayName = ToastPrimitives.Description.displayName;
-
-type ToastProps = React.ComponentPropsWithoutRef;
-
-type ToastActionElement = React.ReactElement;
-
-export {
- type ToastProps,
- type ToastActionElement,
- ToastProvider,
- ToastViewport,
- Toast,
- ToastTitle,
- ToastDescription,
- ToastClose,
- ToastAction,
-};
diff --git a/apps/web-v2/src/components/ui/toaster.tsx b/apps/web-v2/src/components/ui/toaster.tsx
deleted file mode 100644
index 7d82ed55..00000000
--- a/apps/web-v2/src/components/ui/toaster.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-"use client";
-
-import {
- Toast,
- ToastClose,
- ToastDescription,
- ToastProvider,
- ToastTitle,
- ToastViewport,
-} from "@/components/ui/toast";
-import { useToast } from "@/components/ui/use-toast";
-
-export function Toaster() {
- const { toasts } = useToast();
-
- return (
-
- {toasts.map(function ({ id, title, description, action, ...props }) {
- return (
-
-
- {title && {title} }
- {description && (
- {description}
- )}
-
- {action}
-
-
- );
- })}
-
-
- );
-}
diff --git a/apps/web-v2/src/components/ui/use-toast.ts b/apps/web-v2/src/components/ui/use-toast.ts
deleted file mode 100644
index 6555e795..00000000
--- a/apps/web-v2/src/components/ui/use-toast.ts
+++ /dev/null
@@ -1,191 +0,0 @@
-"use client";
-
-// Inspired by react-hot-toast library
-import * as React from "react";
-
-import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
-
-const TOAST_LIMIT = 1;
-const TOAST_REMOVE_DELAY = 1000000;
-
-type ToasterToast = ToastProps & {
- id: string;
- title?: React.ReactNode;
- description?: React.ReactNode;
- action?: ToastActionElement;
-};
-
-const actionTypes = {
- ADD_TOAST: "ADD_TOAST",
- UPDATE_TOAST: "UPDATE_TOAST",
- DISMISS_TOAST: "DISMISS_TOAST",
- REMOVE_TOAST: "REMOVE_TOAST",
-} as const;
-
-let count = 0;
-
-function genId() {
- count = (count + 1) % Number.MAX_SAFE_INTEGER;
- return count.toString();
-}
-
-type ActionType = typeof actionTypes;
-
-type Action =
- | {
- type: ActionType["ADD_TOAST"];
- toast: ToasterToast;
- }
- | {
- type: ActionType["UPDATE_TOAST"];
- toast: Partial;
- }
- | {
- type: ActionType["DISMISS_TOAST"];
- toastId?: ToasterToast["id"];
- }
- | {
- type: ActionType["REMOVE_TOAST"];
- toastId?: ToasterToast["id"];
- };
-
-interface State {
- toasts: ToasterToast[];
-}
-
-const toastTimeouts = new Map>();
-
-const addToRemoveQueue = (toastId: string) => {
- if (toastTimeouts.has(toastId)) {
- return;
- }
-
- const timeout = setTimeout(() => {
- toastTimeouts.delete(toastId);
- dispatch({
- type: "REMOVE_TOAST",
- toastId: toastId,
- });
- }, TOAST_REMOVE_DELAY);
-
- toastTimeouts.set(toastId, timeout);
-};
-
-export const reducer = (state: State, action: Action): State => {
- switch (action.type) {
- case "ADD_TOAST":
- return {
- ...state,
- toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
- };
-
- case "UPDATE_TOAST":
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === action.toast.id ? { ...t, ...action.toast } : t,
- ),
- };
-
- case "DISMISS_TOAST": {
- const { toastId } = action;
-
- // ! Side effects ! - This could be extracted into a dismissToast() action,
- // but I'll keep it here for simplicity
- if (toastId) {
- addToRemoveQueue(toastId);
- } else {
- state.toasts.forEach((toast) => {
- addToRemoveQueue(toast.id);
- });
- }
-
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === toastId || toastId === undefined
- ? {
- ...t,
- open: false,
- }
- : t,
- ),
- };
- }
- case "REMOVE_TOAST":
- if (action.toastId === undefined) {
- return {
- ...state,
- toasts: [],
- };
- }
- return {
- ...state,
- toasts: state.toasts.filter((t) => t.id !== action.toastId),
- };
- }
-};
-
-const listeners: Array<(state: State) => void> = [];
-
-let memoryState: State = { toasts: [] };
-
-function dispatch(action: Action) {
- memoryState = reducer(memoryState, action);
- listeners.forEach((listener) => {
- listener(memoryState);
- });
-}
-
-type Toast = Omit;
-
-function toast({ ...props }: Toast) {
- const id = genId();
-
- const update = (props: ToasterToast) =>
- dispatch({
- type: "UPDATE_TOAST",
- toast: { ...props, id },
- });
- const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
-
- dispatch({
- type: "ADD_TOAST",
- toast: {
- ...props,
- id,
- open: true,
- onOpenChange: (open) => {
- if (!open) dismiss();
- },
- },
- });
-
- return {
- id: id,
- dismiss,
- update,
- };
-}
-
-function useToast() {
- const [state, setState] = React.useState(memoryState);
-
- React.useEffect(() => {
- listeners.push(setState);
- return () => {
- const index = listeners.indexOf(setState);
- if (index > -1) {
- listeners.splice(index, 1);
- }
- };
- }, [state]);
-
- return {
- ...state,
- toast,
- dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
- };
-}
-
-export { useToast, toast };
diff --git a/apps/web-v2/src/lib/utils.ts b/apps/web-v2/src/lib/utils.ts
deleted file mode 100644
index 365058ce..00000000
--- a/apps/web-v2/src/lib/utils.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { type ClassValue, clsx } from "clsx";
-import { twMerge } from "tailwind-merge";
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
-}
diff --git a/apps/web-v2/src/utils/cn.ts b/apps/web-v2/src/utils/cn.ts
deleted file mode 100644
index cec6ac9e..00000000
--- a/apps/web-v2/src/utils/cn.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { ClassValue, clsx } from "clsx";
-import { twMerge } from "tailwind-merge";
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
-}
diff --git a/apps/web-v2/src/utils/icons.tsx b/apps/web-v2/src/utils/icons.tsx
deleted file mode 100644
index da05d716..00000000
--- a/apps/web-v2/src/utils/icons.tsx
+++ /dev/null
@@ -1,277 +0,0 @@
-import * as React from "react";
-import type { SVGProps } from "react";
-export const Github = (props: SVGProps) => (
-
-
-
-);
-
-export const Twitter = (props: SVGProps) => (
-
-
-
-);
-
-export const Medium = (props: SVGProps) => (
-
-
-
-);
-
-export const Reddit = (props: SVGProps) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-);
-
-export const Notion = (props: SVGProps) => (
-
-
-
-
-);
-
-export const X = (props: SVGProps) => (
-
-
-
-);
diff --git a/apps/web-v2/tailwind.config.ts b/apps/web-v2/tailwind.config.ts
deleted file mode 100644
index 139251ce..00000000
--- a/apps/web-v2/tailwind.config.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import type { Config } from "tailwindcss";
-
-const config = {
- darkMode: ["class"],
- content: [
- "./pages/**/*.{ts,tsx}",
- "./components/**/*.{ts,tsx}",
- "./app/**/*.{ts,tsx}",
- "./src/**/*.{ts,tsx}",
- ],
- prefix: "",
- theme: {
- container: {
- center: true,
- padding: "2rem",
- screens: {
- "2xl": "1400px",
- },
- },
- extend: {
- backgroundImage: {
- "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
- "gradient-conic":
- "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
- },
- colors: {
- "black-bg": "var(--black-bg)",
- "soft-foreground": "var(--foreground-bg)",
- "soft-foreground-text": "var(--soft-foreground-text)",
- border: "hsl(var(--border))",
- input: "hsl(var(--input))",
- ring: "hsl(var(--ring))",
- background: "hsl(var(--background))",
- foreground: "hsl(var(--foreground))",
- primary: {
- DEFAULT: "hsl(var(--primary))",
- foreground: "hsl(var(--primary-foreground))",
- },
- secondary: {
- DEFAULT: "hsl(var(--secondary))",
- foreground: "hsl(var(--secondary-foreground))",
- },
- destructive: {
- DEFAULT: "hsl(var(--destructive))",
- foreground: "hsl(var(--destructive-foreground))",
- },
- muted: {
- DEFAULT: "hsl(var(--muted))",
- foreground: "hsl(var(--muted-foreground))",
- },
- accent: {
- DEFAULT: "hsl(var(--accent))",
- foreground: "hsl(var(--accent-foreground))",
- },
- popover: {
- DEFAULT: "hsl(var(--popover))",
- foreground: "hsl(var(--popover-foreground))",
- },
- card: {
- DEFAULT: "hsl(var(--card))",
- foreground: "hsl(var(--card-foreground))",
- },
- },
- borderRadius: {
- lg: "var(--radius)",
- md: "calc(var(--radius) - 2px)",
- sm: "calc(var(--radius) - 4px)",
- },
- keyframes: {
- "accordion-down": {
- from: { height: "0" },
- to: { height: "var(--radix-accordion-content-height)" },
- },
- "accordion-up": {
- from: { height: "var(--radix-accordion-content-height)" },
- to: { height: "0" },
- },
- },
- animation: {
- "accordion-down": "accordion-down 0.2s ease-out",
- "accordion-up": "accordion-up 0.2s ease-out",
- },
- },
- },
- plugins: [require("tailwindcss-animate")],
-} satisfies Config;
-
-export default config;
diff --git a/apps/web-v2/tsconfig.json b/apps/web-v2/tsconfig.json
deleted file mode 100644
index 7b285893..00000000
--- a/apps/web-v2/tsconfig.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "compilerOptions": {
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "preserve",
- "incremental": true,
- "plugins": [
- {
- "name": "next"
- }
- ],
- "paths": {
- "@/*": ["./src/*"]
- }
- },
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
-}
diff --git a/apps/web-v2/wrangler.toml b/apps/web-v2/wrangler.toml
deleted file mode 100644
index 5518fdd7..00000000
--- a/apps/web-v2/wrangler.toml
+++ /dev/null
@@ -1,94 +0,0 @@
-#:schema node_modules/wrangler/config-schema.json
-name = "web-v2"
-compatibility_date = "2024-05-12"
-compatibility_flags = ["nodejs_compat"]
-pages_build_output_dir = ".vercel/output/static"
-
-# [[unsafe.bindings]]
-# name = "RATELIMITER"
-# type = "ratelimit"
-# namespace_id = "1011"
-
-# simple = { limit = 25, period = 10 }
-
-# Automatically place your workloads in an optimal location to minimize latency.
-# If you are running back-end logic in a Pages Function, running it closer to your back-end infrastructure
-# rather than the end user may result in better performance.
-# Docs: https://developers.cloudflare.com/pages/functions/smart-placement/#smart-placement
-# [placement]
-# mode = "smart"
-
-# Variable bindings. These are arbitrary, plaintext strings (similar to environment variables)
-# Docs:
-# - https://developers.cloudflare.com/pages/functions/bindings/#environment-variables
-# Note: Use secrets to store sensitive data.
-# - https://developers.cloudflare.com/pages/functions/bindings/#secrets
-# [vars]
-# MY_VARIABLE = "production_value"
-
-# Bind the Workers AI model catalog. Run machine learning models, powered by serverless GPUs, on Cloudflare’s global network
-# Docs: https://developers.cloudflare.com/pages/functions/bindings/#workers-ai
-# [ai]
-# binding = "AI"
-
-# Bind a D1 database. D1 is Cloudflare’s native serverless SQL database.
-# Docs: https://developers.cloudflare.com/pages/functions/bindings/#d1-databases
-# [[d1_databases]]
-# binding = "MY_DB"
-# database_name = "my-database"
-# database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
-
-# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model.
-# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps.
-# Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects
-# [[durable_objects.bindings]]
-# name = "MY_DURABLE_OBJECT"
-# class_name = "MyDurableObject"
-# script_name = 'my-durable-object'
-
-# Bind a KV Namespace. Use KV as persistent storage for small key-value pairs.
-# Docs: https://developers.cloudflare.com/pages/functions/bindings/#kv-namespaces
-# KV Example:
-# [[kv_namespaces]]
-# binding = "MY_KV_NAMESPACE"
-# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-
-# Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer.
-# Docs: https://developers.cloudflare.com/pages/functions/bindings/#queue-producers
-# [[queues.producers]]
-# binding = "MY_QUEUE"
-# queue = "my-queue"
-
-# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files.
-# Docs: https://developers.cloudflare.com/pages/functions/bindings/#r2-buckets
-# [[r2_buckets]]
-# binding = "MY_BUCKET"
-# bucket_name = "my-bucket"
-
-# Bind another Worker service. Use this binding to call another Worker without network overhead.
-# Docs: https://developers.cloudflare.com/pages/functions/bindings/#service-bindings
-# [[services]]
-# binding = "MY_SERVICE"
-# service = "my-service"
-
-# To use different bindings for preview and production environments, follow the examples below.
-# When using environment-specific overrides for bindings, ALL bindings must be specified on a per-environment basis.
-# Docs: https://developers.cloudflare.com/pages/functions/wrangler-configuration#environment-specific-overrides
-
-######## PREVIEW environment config ########
-
-# [env.preview.vars]
-# API_KEY = "xyz789"
-
-# [[env.preview.kv_namespaces]]
-# binding = "MY_KV_NAMESPACE"
-# id = ""
-
-######## PRODUCTION environment config ########
-
-# [env.production.vars]
-# API_KEY = "abc123"
-
-# [[env.production.kv_namespaces]]
-# binding = "MY_KV_NAMESPACE"
-# id = ""
diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js
new file mode 100644
index 00000000..7d644a4c
--- /dev/null
+++ b/apps/web/.eslintrc.js
@@ -0,0 +1,9 @@
+/** @type {import("eslint").Linter.Config} */
+module.exports = {
+ root: true,
+ extends: ["@repo/eslint-config/next.js"],
+ parser: "@typescript-eslint/parser",
+ parserOptions: {
+ project: true,
+ },
+};
diff --git a/apps/web/.eslintrc.json b/apps/web/.eslintrc.json
deleted file mode 100644
index abd7bea7..00000000
--- a/apps/web/.eslintrc.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extends": [
- "next/core-web-vitals",
- "plugin:eslint-plugin-next-on-pages/recommended"
- ],
- "plugins": ["eslint-plugin-next-on-pages"]
-}
diff --git a/apps/web/.gitignore b/apps/web/.gitignore
deleted file mode 100644
index b7862632..00000000
--- a/apps/web/.gitignore
+++ /dev/null
@@ -1,41 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-.next/
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-.yarn/install-state.gz
-
-# testing
-/coverage
-
-# next.js
-/.next/
-/out/
-
-# production
-/build
-
-# misc
-.DS_Store
-*.pem
-
-# debug
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-# local env files
-.env*.local
-
-# vercel
-.vercel
-
-# typescript
-*.tsbuildinfo
-next-env.d.ts
-
-# wrangler files
-.wrangler
-.dev.vars
diff --git a/apps/web/README.md b/apps/web/README.md
index 5164b159..3d7b63af 100644
--- a/apps/web/README.md
+++ b/apps/web/README.md
@@ -1,70 +1,28 @@
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`c3`](https://developers.cloudflare.com/pages/get-started/c3).
-
## Getting Started
First, run the development server:
```bash
-npm run dev
-# or
yarn dev
-# or
-pnpm dev
-# or
-bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-## Cloudflare integration
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
-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:
+To create [API routes](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) add an `api/` directory to the `app/` directory with a `route.ts` file. For individual endpoints, create a subfolder in the `api` directory, like `api/hello/route.ts` would map to [http://localhost:3000/api/hello](http://localhost:3000/api/hello).
-- `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
+## Learn More
-> **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))
+To learn more about Next.js, take a look at the following resources:
-### Bindings
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial.
-Cloudflare [Bindings](https://developers.cloudflare.com/pages/functions/bindings/) are what allows you to interact with resources available in the Cloudflare Platform.
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
-You can use bindings during development, when previewing locally your application and of course in the deployed application:
+## Deploy on Vercel
-- To use bindings in dev mode you need to define them in the `next.config.js` file under `setupDevBindings`, this mode uses the `next-dev` `@cloudflare/next-on-pages` submodule. For more details see its [documentation](https://github.com/cloudflare/next-on-pages/blob/05b6256/internal-packages/next-dev/README.md).
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js.
-- 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/).
-
-#### 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:
- ```
- and uncomment the commented lines below it.
-- Do the same in the `wrangler.toml` file, where
- the comment is:
- ```
- # KV Example:
- ```
-- If you're using TypeScript run the `build-cf-types` script to update the `env.d.ts` file:
- ```bash
- npm run build-cf-types
- # or
- yarn build-cf-types
- # or
- pnpm build-cf-types
- # or
- bun build-cf-types
- ```
-
-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.
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/apps/web/app/(auth)/auth-buttons.tsx b/apps/web/app/(auth)/auth-buttons.tsx
new file mode 100644
index 00000000..9da1f5a5
--- /dev/null
+++ b/apps/web/app/(auth)/auth-buttons.tsx
@@ -0,0 +1,15 @@
+"use client";
+
+import { Button } from "@repo/ui/src/button";
+import React from "react";
+import { signIn } from "../helpers/server/auth";
+
+function SignIn() {
+ return (
+ await signIn("google")}>
+ Login with Google
+
+ );
+}
+
+export default SignIn;
diff --git a/apps/web/app/(auth)/signin/page.tsx b/apps/web/app/(auth)/signin/page.tsx
new file mode 100644
index 00000000..44d2b4f4
--- /dev/null
+++ b/apps/web/app/(auth)/signin/page.tsx
@@ -0,0 +1,8 @@
+import { getThemeToggler } from "../../helpers/lib/get-theme-button";
+
+async function Signin() {
+ const SetThemeButton = getThemeToggler();
+ return ;
+}
+
+export default Signin;
diff --git a/apps/web-v2/src/app/(landing)/Cta.tsx b/apps/web/app/(landing)/Cta.tsx
similarity index 100%
rename from apps/web-v2/src/app/(landing)/Cta.tsx
rename to apps/web/app/(landing)/Cta.tsx
diff --git a/apps/web-v2/src/app/(landing)/EmailInput.tsx b/apps/web/app/(landing)/EmailInput.tsx
similarity index 97%
rename from apps/web-v2/src/app/(landing)/EmailInput.tsx
rename to apps/web/app/(landing)/EmailInput.tsx
index 9fd175b7..7e7ee5e7 100644
--- a/apps/web-v2/src/app/(landing)/EmailInput.tsx
+++ b/apps/web/app/(landing)/EmailInput.tsx
@@ -2,7 +2,7 @@
import { FormEvent, useState } from "react";
import formSubmitAction from "./formSubmitAction";
-import { useToast } from "@/components/ui/use-toast";
+import { useToast } from "@repo/ui/src/shadcn/use-toast";
function EmailInput() {
const [email, setEmail] = useState("");
diff --git a/apps/web-v2/src/app/(landing)/FeatureContent.tsx b/apps/web/app/(landing)/FeatureContent.tsx
similarity index 100%
rename from apps/web-v2/src/app/(landing)/FeatureContent.tsx
rename to apps/web/app/(landing)/FeatureContent.tsx
diff --git a/apps/web-v2/src/app/(landing)/Features.tsx b/apps/web/app/(landing)/Features.tsx
similarity index 81%
rename from apps/web-v2/src/app/(landing)/Features.tsx
rename to apps/web/app/(landing)/Features.tsx
index f52f7523..dd60ce8f 100644
--- a/apps/web-v2/src/app/(landing)/Features.tsx
+++ b/apps/web/app/(landing)/Features.tsx
@@ -1,13 +1,11 @@
"use client";
import { useState, useRef, useEffect } from "react";
-import { Transition } from "@headlessui/react";
import Image from "next/image";
-import CarouselIllustration from "@/../public/images/carousel-illustration-01.png";
-import { X } from "@/utils/icons";
+import { X } from "@repo/ui/src/components/icons";
import { features } from "./FeatureContent";
-import { CardClick } from "@/components/ui/cardClick";
+import { CardClick } from "@repo/ui/src/components/cardClick";
export default function Features() {
const [tab, setTab] = useState(0);
@@ -65,71 +63,62 @@ export default function Features() {
{/* Item 1 */}
-
heightFix()}
- unmount={false}
+
{/* Item 2 */}
- heightFix()}
- unmount={false}
+
{/* Item 3 */}
- heightFix()}
- unmount={false}
+
diff --git a/apps/web-v2/src/app/(landing)/Hero.tsx b/apps/web/app/(landing)/Hero.tsx
similarity index 95%
rename from apps/web-v2/src/app/(landing)/Hero.tsx
rename to apps/web/app/(landing)/Hero.tsx
index 6867355b..d571cc26 100644
--- a/apps/web-v2/src/app/(landing)/Hero.tsx
+++ b/apps/web/app/(landing)/Hero.tsx
@@ -1,7 +1,7 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
-import { Twitter } from "@/utils/icons";
+import { Twitter } from "@repo/ui/src/components/icons";
import EmailInput from "./EmailInput";
import LinkArrow from "./linkArrow";
@@ -39,7 +39,7 @@ function Hero() {
...slap,
transition: { ...slap.transition, delay: 0.2 },
}}
- className="text-center text-4xl font-semibold tracking-normal text-white/95 md:text-5xl"
+ className="text-center text-4xl font-semibold tracking-tight text-white/95 md:text-5xl"
>
Build your own second brain with Supermemory
diff --git a/apps/web-v2/src/app/(landing)/Navbar.tsx b/apps/web/app/(landing)/Navbar.tsx
similarity index 93%
rename from apps/web-v2/src/app/(landing)/Navbar.tsx
rename to apps/web/app/(landing)/Navbar.tsx
index c7bc80d4..7d0e3225 100644
--- a/apps/web-v2/src/app/(landing)/Navbar.tsx
+++ b/apps/web/app/(landing)/Navbar.tsx
@@ -1,5 +1,5 @@
-import Logo from "@/../public/logo.svg";
-import { Github } from "@/utils/icons";
+import Logo from "../../public/logo.svg";
+import { Github } from "@repo/ui/src/components/icons";
import Image from "next/image";
import Link from "next/link";
import React from "react";
diff --git a/apps/web-v2/src/app/(landing)/RotatingIcons.tsx b/apps/web/app/(landing)/RotatingIcons.tsx
similarity index 96%
rename from apps/web-v2/src/app/(landing)/RotatingIcons.tsx
rename to apps/web/app/(landing)/RotatingIcons.tsx
index 27d4eaed..fafe9f0b 100644
--- a/apps/web-v2/src/app/(landing)/RotatingIcons.tsx
+++ b/apps/web/app/(landing)/RotatingIcons.tsx
@@ -1,7 +1,13 @@
"use client";
import { motion } from "framer-motion";
-import { Github, Medium, Notion, Reddit, Twitter } from "@/utils/icons";
+import {
+ Github,
+ Medium,
+ Notion,
+ Reddit,
+ Twitter,
+} from "@repo/ui/src/components/icons";
import Image from "next/image";
const icons = [
diff --git a/apps/web-v2/src/app/(landing)/footer.tsx b/apps/web/app/(landing)/footer.tsx
similarity index 100%
rename from apps/web-v2/src/app/(landing)/footer.tsx
rename to apps/web/app/(landing)/footer.tsx
diff --git a/apps/web-v2/src/app/(landing)/formSubmitAction.ts b/apps/web/app/(landing)/formSubmitAction.ts
similarity index 98%
rename from apps/web-v2/src/app/(landing)/formSubmitAction.ts
rename to apps/web/app/(landing)/formSubmitAction.ts
index fa96b943..9c2eefff 100644
--- a/apps/web-v2/src/app/(landing)/formSubmitAction.ts
+++ b/apps/web/app/(landing)/formSubmitAction.ts
@@ -9,6 +9,7 @@ const formSubmitAction = async (email: string, token: string) => {
if (ip) {
if (process.env.RATELIMITER) {
+ // @ts-ignore
const { success } = await process.env.RATELIMITER.limit({
key: `waitlist-${ip}`,
});
diff --git a/apps/web-v2/src/app/(landing)/linkArrow.tsx b/apps/web/app/(landing)/linkArrow.tsx
similarity index 100%
rename from apps/web-v2/src/app/(landing)/linkArrow.tsx
rename to apps/web/app/(landing)/linkArrow.tsx
diff --git a/apps/web/app/(landing)/page.tsx b/apps/web/app/(landing)/page.tsx
new file mode 100644
index 00000000..36f0ce8d
--- /dev/null
+++ b/apps/web/app/(landing)/page.tsx
@@ -0,0 +1,58 @@
+import RotatingIcons from "./RotatingIcons";
+import Hero from "./Hero";
+import Navbar from "./Navbar";
+import Cta from "./Cta";
+import { Toaster } from "@repo/ui/src/shadcn/toaster";
+import Features from "./Features";
+import Footer from "./footer";
+import { auth } from "../helpers/server/auth";
+
+export const runtime = "edge";
+
+export default async function Home() {
+ const user = await auth();
+
+ console.log(user);
+
+ return (
+
+
+
+ {/* Background gradients */}
+
+
+
+ {/* a blue gradient line that's slightly tilted with blur (a lotof blur)*/}
+
+
+
+
+
+ {/* Hero section */}
+
+
+ {/* Features section */}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/app/api/[...nextauth]/route.ts b/apps/web/app/api/[...nextauth]/route.ts
new file mode 100644
index 00000000..50807ab1
--- /dev/null
+++ b/apps/web/app/api/[...nextauth]/route.ts
@@ -0,0 +1,2 @@
+export { GET, POST } from "../../helpers/server/auth";
+export const runtime = "edge";
diff --git a/apps/web/app/api/ensureAuth.ts b/apps/web/app/api/ensureAuth.ts
new file mode 100644
index 00000000..a1401a07
--- /dev/null
+++ b/apps/web/app/api/ensureAuth.ts
@@ -0,0 +1,33 @@
+import { NextRequest } from "next/server";
+import { db } from "../helpers/server/db";
+import { sessions, users } from "../helpers/server/db/schema";
+import { eq } from "drizzle-orm";
+
+export async function ensureAuth(req: NextRequest) {
+ // A helper function to protect routes
+
+ 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 undefined;
+ }
+
+ const sessionData = await db
+ .select()
+ .from(sessions)
+ .innerJoin(users, eq(users.id, sessions.userId))
+ .where(eq(sessions.sessionToken, token!));
+
+ if (!sessionData || sessionData.length < 0) {
+ return undefined;
+ }
+
+ return {
+ user: sessionData[0]!.user,
+ session: sessionData[0]!,
+ };
+}
diff --git a/apps/web-v2/src/app/api/hello/route.ts b/apps/web/app/api/hello/route.ts
similarity index 100%
rename from apps/web-v2/src/app/api/hello/route.ts
rename to apps/web/app/api/hello/route.ts
diff --git a/apps/web/app/api/upload_image/route.ts b/apps/web/app/api/upload_image/route.ts
new file mode 100644
index 00000000..0d93c5b0
--- /dev/null
+++ b/apps/web/app/api/upload_image/route.ts
@@ -0,0 +1,56 @@
+import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
+import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
+
+import type { NextRequest } from "next/server";
+import { ensureAuth } from "../ensureAuth";
+
+export const runtime = "edge";
+
+export async function PUT(request: NextRequest) {
+ const d = await ensureAuth(request);
+
+ if (!d) {
+ return new Response("Unauthorized", { status: 401 });
+ }
+
+ const reqUrl = new URL(request.url);
+ const filename = reqUrl.searchParams.get("filename");
+
+ if (!filename) {
+ return new Response("Missing filename", { status: 400 });
+ }
+
+ if (
+ !process.env.R2_ENDPOINT ||
+ !process.env.R2_ACCESS_ID ||
+ !process.env.R2_SECRET_KEY ||
+ !process.env.R2_BUCKET_NAME
+ ) {
+ return new Response(
+ "Missing one or more R2 env variables: R2_ENDPOINT, R2_ACCESS_ID, R2_SECRET_KEY, R2_BUCKET_NAME. To get them, go to the R2 console, create and paste keys in a `.dev.vars` file in the root of this project.",
+ { status: 500 },
+ );
+ }
+
+ const s3 = new S3Client({
+ region: "auto",
+ endpoint: process.env.R2_ENDPOINT,
+ credentials: {
+ accessKeyId: process.env.R2_ACCESS_ID,
+ secretAccessKey: process.env.R2_SECRET_KEY,
+ },
+ });
+
+ const url = await getSignedUrl(
+ s3,
+ new PutObjectCommand({ Bucket: process.env.R2_BUCKET_NAME, Key: filename }),
+ { expiresIn: 3600 },
+ );
+
+ return new Response(JSON.stringify({ url }), {
+ status: 200,
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+}
diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css
new file mode 100644
index 00000000..8eee6cbd
--- /dev/null
+++ b/apps/web/app/globals.css
@@ -0,0 +1,50 @@
+:root {
+ --max-width: 1100px;
+ --border-radius: 12px;
+ --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
+ "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
+ "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
+
+ --foreground-rgb: 255, 255, 255;
+ --background-start-rgb: 0, 0, 0;
+ --background-end-rgb: 0, 0, 0;
+
+ --callout-rgb: 20, 20, 20;
+ --callout-border-rgb: 108, 108, 108;
+ --card-rgb: 100, 100, 100;
+ --card-border-rgb: 200, 200, 200;
+
+ --glow-conic: conic-gradient(
+ from 180deg at 50% 50%,
+ #2a8af6 0deg,
+ #a853ba 180deg,
+ #e92a67 360deg
+ );
+}
+
+* {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+
+html,
+body {
+ max-width: 100vw;
+ overflow-x: hidden;
+}
+
+body {
+ color: rgb(var(--foreground-rgb));
+ background: linear-gradient(
+ to bottom,
+ transparent,
+ rgb(var(--background-end-rgb))
+ )
+ rgb(var(--background-start-rgb));
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
diff --git a/apps/web/app/helpers/lib/get-theme-button.tsx b/apps/web/app/helpers/lib/get-theme-button.tsx
new file mode 100644
index 00000000..41720927
--- /dev/null
+++ b/apps/web/app/helpers/lib/get-theme-button.tsx
@@ -0,0 +1,11 @@
+// Theming that works perfectly with app router (no flicker, jumps etc!)
+
+import dynamic from "next/dynamic";
+
+// Don't SSR the toggle since the value on the server will be different than the client
+export const getThemeToggler = () =>
+ dynamic(() => import("@repo/ui/src/shadcn/theme-toggle"), {
+ ssr: false,
+ // Make sure to code a placeholder so the UI doesn't jump when the component loads
+ loading: () =>
,
+ });
diff --git a/apps/web/app/helpers/lib/handle-errors.ts b/apps/web/app/helpers/lib/handle-errors.ts
new file mode 100644
index 00000000..42cae589
--- /dev/null
+++ b/apps/web/app/helpers/lib/handle-errors.ts
@@ -0,0 +1,25 @@
+import { isRedirectError } from "next/dist/client/components/redirect";
+import { toast } from "sonner";
+import { z } from "zod";
+
+export function getErrorMessage(err: unknown) {
+ const unknownError = "Something went wrong, please try again later.";
+
+ if (err instanceof z.ZodError) {
+ const errors = err.issues.map((issue) => {
+ return issue.message;
+ });
+ return errors.join("\n");
+ } else if (err instanceof Error) {
+ return err.message;
+ } else if (isRedirectError(err)) {
+ throw err;
+ } else {
+ return unknownError;
+ }
+}
+
+export function showErrorToast(err: unknown) {
+ const errorMessage = getErrorMessage(err);
+ return toast.error(errorMessage);
+}
diff --git a/apps/web/src/server/auth.ts b/apps/web/app/helpers/server/auth.ts
similarity index 64%
rename from apps/web/src/server/auth.ts
rename to apps/web/app/helpers/server/auth.ts
index 95edcf35..e2817cf0 100644
--- a/apps/web/src/server/auth.ts
+++ b/apps/web/app/helpers/server/auth.ts
@@ -1,15 +1,15 @@
-import { env } from "@/env";
-import NextAuth from "next-auth";
+import NextAuth, { NextAuthResult } from "next-auth";
import Google from "next-auth/providers/google";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "./db";
export const {
handlers: { GET, POST },
+ signIn,
+ signOut,
auth,
} = NextAuth({
- secret: env.NEXTAUTH_SECRET,
- trustHost: true,
+ secret: process.env.NEXTAUTH_SECRET ?? process.env.AUTH_SECRET,
callbacks: {
session: ({ session, token, user }) => ({
...session,
@@ -22,8 +22,8 @@ export const {
adapter: DrizzleAdapter(db),
providers: [
Google({
- clientId: env.GOOGLE_CLIENT_ID,
- clientSecret: env.GOOGLE_CLIENT_SECRET,
+ clientId: process.env.GOOGLE_CLIENT_ID,
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
});
diff --git a/apps/web/src/server/db/index.ts b/apps/web/app/helpers/server/db/index.ts
similarity index 100%
rename from apps/web/src/server/db/index.ts
rename to apps/web/app/helpers/server/db/index.ts
diff --git a/apps/web/src/server/db/schema.ts b/apps/web/app/helpers/server/db/schema.ts
similarity index 98%
rename from apps/web/src/server/db/schema.ts
rename to apps/web/app/helpers/server/db/schema.ts
index cd2756f1..c4616eb2 100644
--- a/apps/web/src/server/db/schema.ts
+++ b/apps/web/app/helpers/server/db/schema.ts
@@ -6,7 +6,6 @@ import {
sqliteTableCreator,
text,
integer,
- unique,
} from "drizzle-orm/sqlite-core";
export const createTable = sqliteTableCreator((name) => `${name}`);
@@ -90,6 +89,7 @@ export const storedContent = createTable(
url: text("url").notNull(),
savedAt: int("savedAt", { mode: "timestamp" }).notNull(),
baseUrl: text("baseUrl", { length: 255 }),
+ ogImage: text("ogImage", { length: 255 }),
type: text("type", { enum: ["note", "page", "twitter-bookmark"] }).default(
"page",
),
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
new file mode 100644
index 00000000..035b5827
--- /dev/null
+++ b/apps/web/app/layout.tsx
@@ -0,0 +1,71 @@
+import "@repo/tailwind-config/globals.css";
+
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import { ThemeScript } from "next-app-theme/theme-script";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: "Supermemory - Your personal second brain.",
+ description:
+ "Bring saved information from all over the internet into one place where you can connect it, and ask AI about it",
+ openGraph: {
+ images: [
+ {
+ url: "https://supermemory.ai/og-image.png",
+ width: 1200,
+ height: 627,
+ alt: "Supermemory - Your personal second brain.",
+ },
+ ],
+ },
+ metadataBase: {
+ host: "https://supermemory.ai",
+ href: "/",
+ origin: "https://supermemory.ai",
+ password: "supermemory",
+ hash: "supermemory",
+ pathname: "/",
+ search: "",
+ username: "supermemoryai",
+ hostname: "supermemory.ai",
+ port: "",
+ protocol: "https:",
+ searchParams: new URLSearchParams(""),
+ toString: () => "https://supermemory.ai/",
+ toJSON: () => "https://supermemory.ai/",
+ },
+ twitter: {
+ card: "summary_large_image",
+ site: "https://supermemory.ai",
+ creator: "https://supermemory.ai",
+ title: "Supermemory - Your personal second brain.",
+ description:
+ "Bring saved information from all over the internet into one place where you can connect it, and ask AI about it",
+ images: [
+ {
+ url: "https://supermemory.ai/og-image.png",
+ width: 1200,
+ height: 627,
+ alt: "Supermemory - Your personal second brain.",
+ },
+ ],
+ },
+};
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}): JSX.Element {
+ return (
+
+
+
+
+ {/* TODO: when lightmode support is added, remove the 'dark' class from the body tag */}
+ {children}
+
+ );
+}
diff --git a/apps/web/app/page-ref.tsx b/apps/web/app/page-ref.tsx
new file mode 100644
index 00000000..2d4c9cc3
--- /dev/null
+++ b/apps/web/app/page-ref.tsx
@@ -0,0 +1,116 @@
+import { Button } from "@repo/ui/src/shadcn/button";
+import { auth, signIn, signOut } from "./helpers/server/auth";
+import { db } from "./helpers/server/db";
+import { sql } from "drizzle-orm";
+import { users } from "./helpers/server/db/schema";
+import { getThemeToggler } from "./helpers/lib/get-theme-button";
+
+export const runtime = "edge";
+
+export default async function Page() {
+ const usr = await auth();
+
+ const userCount = await db
+ .select({
+ count: sql`count(*)`.mapWith(Number),
+ })
+ .from(users);
+
+ const SetThemeButton = getThemeToggler();
+
+ return (
+
+
+
+
+
+ {" "}
+
+
+
+
+
+
Cloudflare Next Saas Starter
+
+
+
+ Start by editing apps/web/page.tsx
+
+
+
+
+ Welcome to Cloudflare Next Saas Starter.
Built a full stack app
+ using production-ready tools and frameworks, host on Cloudflare
+ instantly.
+
+ An opinionated, batteries-included framework with{" "}
+
+ Turborepo
+ {" "}
+ and Nextjs. Fully Typesafe. Best practices followed by default.
+
+ Here's what the stack includes:
+
+
+ Authentication with next-auth
+
+ Database using Cloudflare's D1 serverless databases
+ Drizzle ORM, already connected to your database and auth ⚡
+ Light/darkmode theming that works with server components (!)
+ Styling using TailwindCSS and ShadcnUI
+ Turborepo with a landing page and shared components
+ Cloudflare wrangler for quick functions on the edge
+
+ ... best part: everything's already set up for you. Just code!
+
+
+
+ Number of users in database: {userCount[0]!.count}
+
+ {usr?.user?.email ? (
+ <>
+
+ Hello {usr.user.name} 👋
+ {usr.user.email}
+
+
+ >
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/apps/web/app/upload/file-uploader.tsx b/apps/web/app/upload/file-uploader.tsx
new file mode 100644
index 00000000..2404ce7c
--- /dev/null
+++ b/apps/web/app/upload/file-uploader.tsx
@@ -0,0 +1,315 @@
+"use client";
+
+import * as React from "react";
+import Image from "next/image";
+import { Cross2Icon, UploadIcon } from "@radix-ui/react-icons";
+import Dropzone, {
+ type DropzoneProps,
+ type FileRejection,
+} from "react-dropzone";
+import { toast } from "sonner";
+
+import { cn, formatBytes } from "@repo/ui/lib/utils";
+import { useControllableState } from "@repo/ui/hooks/use-controllable-state";
+import { Button } from "@repo/ui/src/button";
+import { Progress } from "@repo/ui/src/progress";
+import { ScrollArea } from "@repo/ui/src/scroll-area";
+
+interface FileUploaderProps extends React.HTMLAttributes {
+ /**
+ * Value of the uploader.
+ * @type File[]
+ * @default undefined
+ * @example value={files}
+ */
+ value?: File[];
+
+ /**
+ * Function to be called when the value changes.
+ * @type React.Dispatch>
+ * @default undefined
+ * @example onValueChange={(files) => setFiles(files)}
+ */
+ onValueChange?: React.Dispatch>;
+
+ /**
+ * Function to be called when files are uploaded.
+ * @type (files: File[]) => Promise
+ * @default undefined
+ * @example onUpload={(files) => uploadFiles(files)}
+ */
+ onUpload?: (files: File[]) => Promise;
+
+ /**
+ * Progress of the uploaded files.
+ * @type Record | undefined
+ * @default undefined
+ * @example progresses={{ "file1.png": 50 }}
+ */
+ progresses?: Record;
+
+ /**
+ * Accepted file types for the uploader.
+ * @type { [key: string]: string[]}
+ * @default
+ * ```ts
+ * { "image/*": [] }
+ * ```
+ * @example accept={["image/png", "image/jpeg"]}
+ */
+ accept?: DropzoneProps["accept"];
+
+ /**
+ * Maximum file size for the uploader.
+ * @type number | undefined
+ * @default 1024 * 1024 * 2 // 2MB
+ * @example maxSize={1024 * 1024 * 2} // 2MB
+ */
+ maxSize?: DropzoneProps["maxSize"];
+
+ /**
+ * Maximum number of files for the uploader.
+ * @type number | undefined
+ * @default 1
+ * @example maxFiles={5}
+ */
+ maxFiles?: DropzoneProps["maxFiles"];
+
+ /**
+ * Whether the uploader should accept multiple files.
+ * @type boolean
+ * @default false
+ * @example multiple
+ */
+ multiple?: boolean;
+
+ /**
+ * Whether the uploader is disabled.
+ * @type boolean
+ * @default false
+ * @example disabled
+ */
+ disabled?: boolean;
+}
+
+export function FileUploader(props: FileUploaderProps) {
+ const {
+ value: valueProp,
+ onValueChange,
+ onUpload,
+ progresses,
+ accept = { "image/*": [] },
+ maxSize = 1024 * 1024 * 2,
+ maxFiles = 1,
+ multiple = false,
+ disabled = false,
+ className,
+ ...dropzoneProps
+ } = props;
+
+ const [files, setFiles] = useControllableState({
+ prop: valueProp,
+ onChange: onValueChange,
+ });
+
+ const onDrop = React.useCallback(
+ (acceptedFiles: File[], rejectedFiles: FileRejection[]) => {
+ if (!multiple && maxFiles === 1 && acceptedFiles.length > 1) {
+ toast.error("Cannot upload more than 1 file at a time");
+ return;
+ }
+
+ if ((files?.length ?? 0) + acceptedFiles.length > maxFiles) {
+ toast.error(`Cannot upload more than ${maxFiles} files`);
+ return;
+ }
+
+ const newFiles = acceptedFiles.map((file) =>
+ Object.assign(file, {
+ preview: URL.createObjectURL(file),
+ }),
+ );
+
+ const updatedFiles = files ? [...files, ...newFiles] : newFiles;
+
+ setFiles(updatedFiles);
+
+ if (rejectedFiles.length > 0) {
+ rejectedFiles.forEach(({ file }) => {
+ toast.error(`File ${file.name} was rejected`);
+ });
+ }
+
+ if (
+ onUpload &&
+ updatedFiles.length > 0 &&
+ updatedFiles.length <= maxFiles
+ ) {
+ const target =
+ updatedFiles.length > 0 ? `${updatedFiles.length} files` : `file`;
+
+ toast.promise(onUpload(updatedFiles), {
+ loading: `Uploading ${target}...`,
+ success: () => {
+ setFiles([]);
+ return `${target} uploaded`;
+ },
+ error: `Failed to upload ${target}`,
+ });
+ }
+ },
+
+ [files, maxFiles, multiple, onUpload, setFiles],
+ );
+
+ function onRemove(index: number) {
+ if (!files) return;
+ const newFiles = files.filter((_, i) => i !== index);
+ setFiles(newFiles);
+ onValueChange?.(newFiles);
+ }
+
+ // Revoke preview url when component unmounts
+ React.useEffect(() => {
+ return () => {
+ if (!files) return;
+ files.forEach((file) => {
+ if (isFileWithPreview(file)) {
+ URL.revokeObjectURL(file.preview);
+ }
+ });
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ const isDisabled = disabled || (files?.length ?? 0) >= maxFiles;
+
+ return (
+
+
1 || multiple}
+ disabled={isDisabled}
+ >
+ {({ getRootProps, getInputProps, isDragActive }) => (
+
+
+ {isDragActive ? (
+
+
+
+
+
+ Drop the files here
+
+
+ ) : (
+
+
+
+
+
+
+ Drag {`'n'`} drop files here, or click to select files
+
+
+ You can upload
+ {maxFiles > 1
+ ? ` ${maxFiles === Infinity ? "multiple" : maxFiles}
+ files (up to ${formatBytes(maxSize)} each)`
+ : ` a file with ${formatBytes(maxSize)}`}
+
+
+
+ )}
+
+ )}
+
+ {files?.length ? (
+
+
+ {files?.map((file, index) => (
+ onRemove(index)}
+ progress={progresses?.[file.name]}
+ />
+ ))}
+
+
+ ) : null}
+
+ );
+}
+
+interface FileCardProps {
+ file: File;
+ onRemove: () => void;
+ progress?: number;
+}
+
+function FileCard({ file, progress, onRemove }: FileCardProps) {
+ return (
+
+
+ {isFileWithPreview(file) ? (
+
+ ) : null}
+
+
+
+ {file.name}
+
+
+ {formatBytes(file.size)}
+
+
+ {progress ?
: null}
+
+
+
+
+
+ Remove file
+
+
+
+ );
+}
+
+function isFileWithPreview(file: File): file is File & { preview: string } {
+ return "preview" in file && typeof file.preview === "string";
+}
diff --git a/apps/web/app/upload/page.tsx b/apps/web/app/upload/page.tsx
new file mode 100644
index 00000000..4899d695
--- /dev/null
+++ b/apps/web/app/upload/page.tsx
@@ -0,0 +1,99 @@
+"use client";
+
+import * as React from "react";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { useForm } from "react-hook-form";
+import { toast } from "sonner";
+import { z } from "zod";
+
+import { getErrorMessage } from "../helpers/lib/handle-errors";
+import { Button } from "@repo/ui/src/button";
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@repo/ui/src/form";
+import { FileUploader } from "./file-uploader";
+
+import { UploadedFilesCard } from "./uploaded-files-card";
+import { useUploadFile } from "@repo/ui/hooks/use-upload-file";
+
+const schema = z.object({
+ images: z.array(z.instanceof(File)),
+});
+
+type Schema = z.infer;
+
+export default function ReactHookFormDemo() {
+ const [loading, setLoading] = React.useState(false);
+ const { uploadFiles, uploadedFiles, isUploading } = useUploadFile(
+ "imageUploader",
+ { defaultUploadedFiles: [] },
+ );
+ const form = useForm({
+ resolver: zodResolver(schema),
+ defaultValues: {
+ images: [],
+ },
+ });
+
+ function onSubmit(input: Schema) {
+ setLoading(true);
+
+ toast.promise(uploadFiles(input.images), {
+ loading: "Uploading images...",
+ success: () => {
+ form.reset();
+ setLoading(false);
+ return "Images uploaded";
+ },
+ error: (err) => {
+ setLoading(false);
+ return getErrorMessage(err);
+ },
+ });
+ }
+
+ return (
+
+ );
+}
diff --git a/apps/web/app/upload/uploaded-files-card.tsx b/apps/web/app/upload/uploaded-files-card.tsx
new file mode 100644
index 00000000..fc9b7d5b
--- /dev/null
+++ b/apps/web/app/upload/uploaded-files-card.tsx
@@ -0,0 +1,93 @@
+import Image from "next/image";
+import type { UploadedFile } from "@repo/shared-types";
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@repo/ui/src/card";
+import { ScrollArea, ScrollBar } from "@repo/ui/src/scroll-area";
+
+interface UploadedFilesCardProps {
+ uploadedFiles: UploadedFile[];
+}
+
+import { ImageIcon } from "@radix-ui/react-icons";
+
+import { cn } from "@repo/ui/lib/utils";
+
+interface EmptyCardProps extends React.ComponentPropsWithoutRef {
+ title: string;
+ description?: string;
+ action?: React.ReactNode;
+ icon?: React.ComponentType<{ className?: string }>;
+ className?: string;
+}
+
+function EmptyCard({
+ title,
+ description,
+ icon: Icon = ImageIcon,
+ action,
+ className,
+ ...props
+}: EmptyCardProps) {
+ return (
+
+
+
+
+
+ {title}
+ {description ? {description} : null}
+
+ {action ? action : null}
+
+ );
+}
+
+export function UploadedFilesCard({ uploadedFiles }: UploadedFilesCardProps) {
+ return (
+
+
+ Uploaded files
+ View the uploaded files here
+
+
+ {uploadedFiles.length > 0 ? (
+
+
+ {uploadedFiles.map((file) => (
+
+
+
+ ))}
+
+
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/apps/web/cf-env.d.ts b/apps/web/cf-env.d.ts
new file mode 100644
index 00000000..98303f35
--- /dev/null
+++ b/apps/web/cf-env.d.ts
@@ -0,0 +1,7 @@
+declare global {
+ namespace NodeJS {
+ interface ProcessEnv extends CloudflareEnv {}
+ }
+}
+
+export {};
diff --git a/apps/web/components.json b/apps/web/components.json
deleted file mode 100644
index c9b7b380..00000000
--- a/apps/web/components.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "$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/db/wipe.sql b/apps/web/db/wipe.sql
deleted file mode 100644
index 456a48ad..00000000
--- a/apps/web/db/wipe.sql
+++ /dev/null
@@ -1,7 +0,0 @@
-DELETE FROM `account`;
-DELETE FROM `contentToSpace`;
-DELETE FROM `session`;
-DELETE FROM `space`;
-DELETE FROM `storedContent`;
-DELETE FROM `user`;
-DELETE FROM `verificationToken`;
\ No newline at end of file
diff --git a/apps/web/drizzle.config.ts b/apps/web/drizzle.config.ts
index c4f5d5fe..ab071121 100644
--- a/apps/web/drizzle.config.ts
+++ b/apps/web/drizzle.config.ts
@@ -1,17 +1,12 @@
import { type Config } from "drizzle-kit";
-const localDb = {
- url: process.env.LOCAL_DB_URL!,
-};
-
export default {
- schema: "./src/server/db/schema.ts",
- driver: process.env.LOCAL_DB_URL ? "better-sqlite" : "d1",
- dbCredentials: process.env.LOCAL_DB_URL
- ? localDb
- : {
- wranglerConfigPath: "./wrangler.toml",
- dbName: "dev-d1-anycontext",
- },
- out: "drizzle",
+ schema: "./app/helpers/server/db/schema.ts",
+ dialect: "sqlite",
+ driver: "d1",
+ dbCredentials: {
+ wranglerConfigPath: "./wrangler.toml",
+ dbName: "",
+ },
+ out: "migrations",
} satisfies Config;
diff --git a/apps/web/env.d.ts b/apps/web/env.d.ts
index 8868e855..d74dcdf3 100644
--- a/apps/web/env.d.ts
+++ b/apps/web/env.d.ts
@@ -1,13 +1,14 @@
-declare global {
- namespace NodeJS {
- interface ProcessEnv {
- [key: string]: string | undefined;
- DATABASE: D1Database;
- VECTORIZE_INDEX: VectorizeIndex;
- AI: any;
- RATELIMITER: any;
- }
- }
-}
+// Generated by Wrangler on Sat May 25 2024 17:07:13 GMT-0500 (Central Daylight Time)
+// by running `wrangler types --env-interface CloudflareEnv env.d.ts`
-export {};
+interface CloudflareEnv {
+ GOOGLE_CLIENT_ID: string;
+ GOOGLE_CLIENT_SECRET: string;
+ NEXTAUTH_SECRET: string;
+ R2_ENDPOINT: string;
+ R2_ACCESS_ID: string;
+ R2_SECRET_KEY: string;
+ R2_BUCKET_NAME: string;
+ STORAGE: R2Bucket;
+ DATABASE: D1Database;
+}
diff --git a/apps/web/db/prepare.sql b/apps/web/migrations/000_setup.sql
similarity index 99%
rename from apps/web/db/prepare.sql
rename to apps/web/migrations/000_setup.sql
index 4c8c2af6..db7f9444 100644
--- a/apps/web/db/prepare.sql
+++ b/apps/web/migrations/000_setup.sql
@@ -47,6 +47,7 @@ CREATE TABLE `storedContent` (
`url` text NOT NULL,
`savedAt` integer NOT NULL,
`baseUrl` text(255),
+ `ogImage` text(255),
`type` text DEFAULT 'page',
`image` text(255),
`user` text(255),
diff --git a/apps/web/migrations/meta/0000_snapshot.json b/apps/web/migrations/meta/0000_snapshot.json
new file mode 100644
index 00000000..29cc4323
--- /dev/null
+++ b/apps/web/migrations/meta/0000_snapshot.json
@@ -0,0 +1,492 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "409cec60-0c4b-4cda-8751-3e70768bbb6c",
+ "prevId": "00000000-0000-0000-0000-000000000000",
+ "tables": {
+ "account": {
+ "name": "account",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "providerAccountId": {
+ "name": "providerAccountId",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "token_type": {
+ "name": "token_type",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_state": {
+ "name": "session_state",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "oauth_token_secret": {
+ "name": "oauth_token_secret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "oauth_token": {
+ "name": "oauth_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "account_userId_idx": {
+ "name": "account_userId_idx",
+ "columns": ["userId"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "account_userId_user_id_fk": {
+ "name": "account_userId_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "contentToSpace": {
+ "name": "contentToSpace",
+ "columns": {
+ "contentId": {
+ "name": "contentId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "spaceId": {
+ "name": "spaceId",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "contentToSpace_contentId_storedContent_id_fk": {
+ "name": "contentToSpace_contentId_storedContent_id_fk",
+ "tableFrom": "contentToSpace",
+ "tableTo": "storedContent",
+ "columnsFrom": ["contentId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "contentToSpace_spaceId_space_id_fk": {
+ "name": "contentToSpace_spaceId_space_id_fk",
+ "tableFrom": "contentToSpace",
+ "tableTo": "space",
+ "columnsFrom": ["spaceId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "contentToSpace_contentId_spaceId_pk": {
+ "columns": ["contentId", "spaceId"],
+ "name": "contentToSpace_contentId_spaceId_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "session": {
+ "name": "session",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "sessionToken": {
+ "name": "sessionToken",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "session_userId_idx": {
+ "name": "session_userId_idx",
+ "columns": ["userId"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "session_userId_user_id_fk": {
+ "name": "session_userId_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "space": {
+ "name": "space",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'none'"
+ },
+ "user": {
+ "name": "user",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "space_name_unique": {
+ "name": "space_name_unique",
+ "columns": ["name"],
+ "isUnique": true
+ },
+ "spaces_name_idx": {
+ "name": "spaces_name_idx",
+ "columns": ["name"],
+ "isUnique": false
+ },
+ "spaces_user_idx": {
+ "name": "spaces_user_idx",
+ "columns": ["user"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "space_user_user_id_fk": {
+ "name": "space_user_user_id_fk",
+ "tableFrom": "space",
+ "tableTo": "user",
+ "columnsFrom": ["user"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "storedContent": {
+ "name": "storedContent",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "savedAt": {
+ "name": "savedAt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "baseUrl": {
+ "name": "baseUrl",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "ogImage": {
+ "name": "ogImage",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "'page'"
+ },
+ "image": {
+ "name": "image",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "user": {
+ "name": "user",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "storedContent_url_idx": {
+ "name": "storedContent_url_idx",
+ "columns": ["url"],
+ "isUnique": false
+ },
+ "storedContent_savedAt_idx": {
+ "name": "storedContent_savedAt_idx",
+ "columns": ["savedAt"],
+ "isUnique": false
+ },
+ "storedContent_title_idx": {
+ "name": "storedContent_title_idx",
+ "columns": ["title"],
+ "isUnique": false
+ },
+ "storedContent_user_idx": {
+ "name": "storedContent_user_idx",
+ "columns": ["user"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "storedContent_user_user_id_fk": {
+ "name": "storedContent_user_user_id_fk",
+ "tableFrom": "storedContent",
+ "tableTo": "user",
+ "columnsFrom": ["user"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "user": {
+ "name": "user",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text(255)",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "image": {
+ "name": "image",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "verificationToken": {
+ "name": "verificationToken",
+ "columns": {
+ "identifier": {
+ "name": "identifier",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "text(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "verificationToken_identifier_token_pk": {
+ "columns": ["identifier", "token"],
+ "name": "verificationToken_identifier_token_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ }
+}
diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json
new file mode 100644
index 00000000..a77d9616
--- /dev/null
+++ b/apps/web/migrations/meta/_journal.json
@@ -0,0 +1,13 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "entries": [
+ {
+ "idx": 0,
+ "version": "6",
+ "when": 1716677954608,
+ "tag": "0000_calm_monster_badoon",
+ "breakpoints": true
+ }
+ ]
+}
diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts
new file mode 100644
index 00000000..4f11a03d
--- /dev/null
+++ b/apps/web/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs
index 31faf8ff..51c492f4 100644
--- a/apps/web/next.config.mjs
+++ b/apps/web/next.config.mjs
@@ -1,20 +1,19 @@
+import MillionLint from "@million/lint";
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") {
- await setupDevPlatform();
-}
-
/** @type {import('next').NextConfig} */
const nextConfig = {
- eslint: {
- ignoreBuildErrors: true,
- },
- typescript: {
- ignoreBuildErrors: true,
- },
+ transpilePackages: ["@repo/ui"],
};
+export default MillionLint.next({
+ rsc: true,
+})(nextConfig);
-export default nextConfig;
+// we only need to use the utility during development so we can check NODE_ENV
+// (note: this check is recommended but completely optional)
+if (process.env.NODE_ENV === "development") {
+ // `await`ing the call is not necessary but it helps making sure that the setup has succeeded.
+ // If you cannot use top level awaits you could use the following to avoid an unhandled rejection:
+ // `setupDevPlatform().catch(e => console.error(e));`
+ await setupDevPlatform();
+}
diff --git a/apps/web/package.json b/apps/web/package.json
index 8dccd4d6..70ad3b46 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,56 +1,36 @@
{
"name": "web",
- "version": "0.1.0",
+ "version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint",
+ "lint": "eslint . --max-warnings 0",
+ "cf-typegen": "wrangler types --env-interface CloudflareEnv env.d.ts",
"pages:build": "bunx @cloudflare/next-on-pages",
- "preview": "bun pages:build && wrangler pages dev .vercel/output/static",
- "deploy": "bun pages:build && wrangler pages deploy .vercel/output/static",
- "build-cf-types": "wrangler types --env-interface CloudflareEnv env.d.ts",
- "prepare-local-db": "drizzle-kit generate:sqlite --out db/prepare.sql && wrangler d1 execute dev-d1-anycontext --local --file=db/prepare.sql",
- "schema-change": "drizzle-kit generate:sqlite --out db/schema-change.sql && wrangler d1 execute dev-d1-anycontext --local --file=db/schema-change.sql"
+ "preview": "bun pages:build && wrangler pages dev",
+ "deploy": "bun pages:build && wrangler pages deploy"
},
"dependencies": {
- "@formkit/auto-animate": "^0.8.1",
- "@radix-ui/react-avatar": "^1.0.4",
- "@radix-ui/react-dialog": "^1.0.5",
- "@radix-ui/react-icons": "^1.3.0",
- "@radix-ui/react-popover": "^1.0.7",
- "@radix-ui/react-slot": "^1.0.2",
- "cheerio": "^1.0.0-rc.12",
- "class-variance-authority": "^0.7.0",
- "clsx": "^2.1.0",
- "cmdk": "latest",
- "framer-motion": "^11.0.24",
- "lucide-react": "^0.338.0",
- "next": "14.2.0",
- "novel": "0.1.22",
- "react": "^18",
- "react-dom": "^18",
- "tailwind-merge": "^2.2.1",
- "tailwindcss": "^3.4.3",
- "tailwindcss-animate": "^1.0.7",
- "tiptap-markdown": "^0.8.10",
- "vaul": "^0.9.0"
+ "@million/lint": "^1.0.0-rc.11",
+ "@repo/ui": "*",
+ "million": "^3.1.6",
+ "next": "^14.1.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
},
"devDependencies": {
- "@cloudflare/next-on-pages": "1",
- "@cloudflare/workers-types": "^4.20240329.0",
- "@types/node": "^20",
- "@types/react": "^18",
- "@types/react-dom": "^18",
- "autoprefixer": "^10.0.1",
- "eslint": "^8",
- "eslint-config-next": "14.1.0",
- "eslint-plugin-next-on-pages": "^1.11.0",
- "postcss": "^8",
- "tailwind-scrollbar": "^3.1.0",
- "typescript": "^5",
- "vercel": "^33.6.2",
- "wrangler": "^3.41.0"
+ "@next/eslint-plugin-next": "^14.1.1",
+ "@repo/eslint-config": "*",
+ "@repo/typescript-config": "*",
+ "@repo/tailwind-config": "*",
+ "@repo/shared-types": "*",
+ "@types/eslint": "^8.56.5",
+ "@types/node": "^20.11.24",
+ "@types/react": "^18.2.61",
+ "@types/react-dom": "^18.2.19",
+ "eslint": "^8.57.0",
+ "typescript": "^5.3.3"
}
}
diff --git a/apps/web/postcss.config.js b/apps/web/postcss.config.js
index 12a703d9..2e342dc3 100644
--- a/apps/web/postcss.config.js
+++ b/apps/web/postcss.config.js
@@ -1,6 +1 @@
-module.exports = {
- plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
-};
+module.exports = require("@repo/tailwind-config/postcss.config");
diff --git a/apps/web/public/android-chrome-192x192.png b/apps/web/public/android-chrome-192x192.png
deleted file mode 100644
index 5e57b282..00000000
Binary files a/apps/web/public/android-chrome-192x192.png and /dev/null differ
diff --git a/apps/web/public/android-chrome-512x512.png b/apps/web/public/android-chrome-512x512.png
deleted file mode 100644
index 2553425e..00000000
Binary files a/apps/web/public/android-chrome-512x512.png and /dev/null differ
diff --git a/apps/web/public/apple-touch-icon.png b/apps/web/public/apple-touch-icon.png
deleted file mode 100644
index d55c6339..00000000
Binary files a/apps/web/public/apple-touch-icon.png and /dev/null differ
diff --git a/apps/web/public/favicon-16x16.png b/apps/web/public/favicon-16x16.png
deleted file mode 100644
index 67fbac93..00000000
Binary files a/apps/web/public/favicon-16x16.png and /dev/null differ
diff --git a/apps/web/public/favicon-32x32.png b/apps/web/public/favicon-32x32.png
deleted file mode 100644
index 2fc8f0b3..00000000
Binary files a/apps/web/public/favicon-32x32.png and /dev/null differ
diff --git a/apps/web/public/favicon.ico b/apps/web/public/favicon.ico
index 163c3627..dd86c6dd 100644
Binary files a/apps/web/public/favicon.ico and b/apps/web/public/favicon.ico differ
diff --git a/apps/web-v2/public/icons/android-chrome-192x192.png b/apps/web/public/icons/android-chrome-192x192.png
similarity index 100%
rename from apps/web-v2/public/icons/android-chrome-192x192.png
rename to apps/web/public/icons/android-chrome-192x192.png
diff --git a/apps/web-v2/public/icons/android-chrome-512x512.png b/apps/web/public/icons/android-chrome-512x512.png
similarity index 100%
rename from apps/web-v2/public/icons/android-chrome-512x512.png
rename to apps/web/public/icons/android-chrome-512x512.png
diff --git a/apps/web-v2/public/icons/apple-touch-icon.png b/apps/web/public/icons/apple-touch-icon.png
similarity index 100%
rename from apps/web-v2/public/icons/apple-touch-icon.png
rename to apps/web/public/icons/apple-touch-icon.png
diff --git a/apps/web/public/icons/black_without_bg.png b/apps/web/public/icons/black_without_bg.png
deleted file mode 100644
index 1bd7582d..00000000
Binary files a/apps/web/public/icons/black_without_bg.png and /dev/null differ
diff --git a/apps/web/public/icons/brain-icon.png b/apps/web/public/icons/brain-icon.png
deleted file mode 100644
index cebe4095..00000000
Binary files a/apps/web/public/icons/brain-icon.png and /dev/null differ
diff --git a/apps/web-v2/public/icons/favicon-16x16.png b/apps/web/public/icons/favicon-16x16.png
similarity index 100%
rename from apps/web-v2/public/icons/favicon-16x16.png
rename to apps/web/public/icons/favicon-16x16.png
diff --git a/apps/web-v2/public/icons/favicon-32x32.png b/apps/web/public/icons/favicon-32x32.png
similarity index 100%
rename from apps/web-v2/public/icons/favicon-32x32.png
rename to apps/web/public/icons/favicon-32x32.png
diff --git a/apps/web/public/icons/logo_bw_without_bg.png b/apps/web/public/icons/logo_bw_without_bg.png
deleted file mode 100644
index 167b9aea..00000000
Binary files a/apps/web/public/icons/logo_bw_without_bg.png and /dev/null differ
diff --git a/apps/web/public/icons/logo_without_bg.png b/apps/web/public/icons/logo_without_bg.png
deleted file mode 100644
index 54313f60..00000000
Binary files a/apps/web/public/icons/logo_without_bg.png and /dev/null differ
diff --git a/apps/web/public/icons/white_without_bg.png b/apps/web/public/icons/white_without_bg.png
deleted file mode 100644
index 4f7d8d42..00000000
Binary files a/apps/web/public/icons/white_without_bg.png and /dev/null differ
diff --git a/apps/web/public/icons/wordmark.png b/apps/web/public/icons/wordmark.png
deleted file mode 100644
index 6fb4ee7b..00000000
Binary files a/apps/web/public/icons/wordmark.png and /dev/null differ
diff --git a/apps/web-v2/public/images/carousel-illustration-01.png b/apps/web/public/images/carousel-illustration-01.png
similarity index 100%
rename from apps/web-v2/public/images/carousel-illustration-01.png
rename to apps/web/public/images/carousel-illustration-01.png
diff --git a/apps/web-v2/public/images/feature-illustration.png b/apps/web/public/images/feature-illustration.png
similarity index 100%
rename from apps/web-v2/public/images/feature-illustration.png
rename to apps/web/public/images/feature-illustration.png
diff --git a/apps/web-v2/public/landing-bg-1.svg b/apps/web/public/landing-bg-1.svg
similarity index 100%
rename from apps/web-v2/public/landing-bg-1.svg
rename to apps/web/public/landing-bg-1.svg
diff --git a/apps/web-v2/public/landing-bg-2.svg b/apps/web/public/landing-bg-2.svg
similarity index 100%
rename from apps/web-v2/public/landing-bg-2.svg
rename to apps/web/public/landing-bg-2.svg
diff --git a/apps/web-v2/public/landing-ui-2.png b/apps/web/public/landing-ui-2.png
similarity index 100%
rename from apps/web-v2/public/landing-ui-2.png
rename to apps/web/public/landing-ui-2.png
diff --git a/apps/web-v2/public/landing-ui.svg b/apps/web/public/landing-ui.svg
similarity index 100%
rename from apps/web-v2/public/landing-ui.svg
rename to apps/web/public/landing-ui.svg
diff --git a/apps/web/public/logo.png b/apps/web/public/logo.png
deleted file mode 100644
index 3fad437e..00000000
Binary files a/apps/web/public/logo.png and /dev/null differ
diff --git a/apps/web-v2/public/logo.svg b/apps/web/public/logo.svg
similarity index 100%
rename from apps/web-v2/public/logo.svg
rename to apps/web/public/logo.svg
diff --git a/apps/web/public/note.svg b/apps/web/public/note.svg
deleted file mode 100644
index ffb4c795..00000000
--- a/apps/web/public/note.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/apps/web/public/og-image.png b/apps/web/public/og-image.png
index 1050ff19..c5a61b17 100644
Binary files a/apps/web/public/og-image.png and b/apps/web/public/og-image.png differ
diff --git a/apps/web/public/site.webmanifest b/apps/web/public/site.webmanifest
index 8d302080..c903e516 100644
--- a/apps/web/public/site.webmanifest
+++ b/apps/web/public/site.webmanifest
@@ -1,14 +1,14 @@
{
- "name": "Supermemory - Your second brain",
- "short_name": "Save your memories forever, build your own second brain.",
+ "name": "Supermemory - your second brain.",
+ "short_name": "Supermemory",
"icons": [
{
- "src": "/android-chrome-192x192.png",
+ "src": "/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
- "src": "/android-chrome-512x512.png",
+ "src": "/icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
diff --git a/apps/web/public/twitter.svg b/apps/web/public/twitter.svg
deleted file mode 100644
index 0819971e..00000000
--- a/apps/web/public/twitter.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/apps/web/public/web.svg b/apps/web/public/web.svg
deleted file mode 100644
index c59f016d..00000000
--- a/apps/web/public/web.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/apps/web/src/actions/db.ts b/apps/web/src/actions/db.ts
deleted file mode 100644
index 59eb8976..00000000
--- a/apps/web/src/actions/db.ts
+++ /dev/null
@@ -1,676 +0,0 @@
-"use server";
-import { cookies, headers } from "next/headers";
-import { db } from "@/server/db";
-import {
- contentToSpace,
- sessions,
- storedContent,
- users,
- space,
- StoredContent,
-} from "@/server/db/schema";
-import { SearchResult } from "@/contexts/MemoryContext";
-import {
- like,
- eq,
- and,
- sql,
- exists,
- asc,
- notExists,
- inArray,
- notInArray,
-} from "drizzle-orm";
-import { union } from "drizzle-orm/sqlite-core";
-import { env } from "@/env";
-
-// @todo: (future) pagination not yet needed
-export async function searchMemoriesAndSpaces(
- query: string,
- opts?: {
- filter?: { memories?: boolean; spaces?: boolean };
- range?: { offset: number; limit: number };
- memoriesRelativeToSpace?: {
- fromSpaces?: number[];
- notInSpaces?: number[];
- };
- },
-): Promise {
- const user = await getUser();
-
- if (!user) {
- return [];
- }
-
- const defaultWhere = and(
- eq(storedContent.user, user.id),
- like(storedContent.title, `%${query}%`),
- );
- const extraWheres = [];
-
- if (opts?.memoriesRelativeToSpace) {
- if (opts.memoriesRelativeToSpace.fromSpaces) {
- extraWheres.push(
- exists(
- db
- .select()
- .from(contentToSpace)
- .where(
- and(
- eq(contentToSpace.contentId, storedContent.id),
- inArray(
- contentToSpace.spaceId,
- opts.memoriesRelativeToSpace.fromSpaces,
- ),
- ),
- ),
- ),
- );
- }
- if (opts.memoriesRelativeToSpace.notInSpaces) {
- extraWheres.push(
- notExists(
- db
- .select()
- .from(contentToSpace)
- .where(
- and(
- eq(contentToSpace.contentId, storedContent.id),
- inArray(
- contentToSpace.spaceId,
- opts.memoriesRelativeToSpace.notInSpaces,
- ),
- ),
- ),
- ),
- );
- }
- }
-
- try {
- let searchMemoriesQuery = db
- .select({
- type: sql`'memory'`,
- space: sql`NULL`,
- memory: storedContent as any,
- })
- .from(storedContent)
- .where(
- extraWheres.length == 2
- ? and(and(...extraWheres), defaultWhere)
- : extraWheres.length == 1
- ? and(...extraWheres, defaultWhere)
- : defaultWhere,
- )
- .orderBy(asc(storedContent.savedAt));
-
- let searchSpacesQuery = db
- .select({
- type: sql`'space'`,
- space: space as any,
- memory: sql`NULL`,
- })
- .from(space)
- .where(and(eq(space.user, user.id), like(space.name, `%${query}%`)))
- .orderBy(asc(space.name));
-
- let queries = [];
-
- console.log("adding");
-
- [undefined, true].includes(opts?.filter?.memories) &&
- queries.push(searchMemoriesQuery);
- [undefined, true].includes(opts?.filter?.spaces) &&
- queries.push(searchSpacesQuery);
-
- if (opts?.range) {
- queries = queries.map((q) =>
- q.offset(opts.range!.offset).limit(opts.range!.limit),
- );
- } else {
- queries = queries.map((q) => q.all());
- }
-
- const data = await Promise.all(queries);
-
- console.log("resp", data);
-
- return data.reduce((acc, i) => [...acc, ...i]) as SearchResult[];
- } catch {
- return [];
- }
-}
-
-export async function getMemoriesFromUrl(urls: string[]) {
- const user = await getUser();
-
- if (!user) {
- return [];
- }
-
- return urls.length > 0
- ? await db
- .select()
- .from(storedContent)
- .where(
- and(
- inArray(storedContent.url, urls),
- eq(storedContent.user, user.id),
- ),
- )
- .all()
- : [];
-}
-
-async function getUser() {
- const token =
- cookies().get("next-auth.session-token")?.value ??
- cookies().get("__Secure-authjs.session-token")?.value ??
- cookies().get("authjs.session-token")?.value ??
- headers().get("Authorization")?.replace("Bearer ", "");
-
- if (!token) {
- return null;
- }
-
- const session = await db
- .select()
- .from(sessions)
- .where(eq(sessions.sessionToken, token!));
-
- if (!session || session.length === 0) {
- return null;
- }
-
- const [userData] = await db
- .select()
- .from(users)
- .where(eq(users.id, session[0].userId))
- .limit(1);
-
- if (!userData) {
- return null;
- }
-
- return userData;
-}
-
-export async function getSpace(id: number) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- return (
- await db
- .select()
- .from(space)
- .where(and(eq(space.id, id), eq(space.user, user.id)))
- )[0];
-}
-
-export async function addSpace(name: string, memories: number[]) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- const [addedSpace] = await db
- .insert(space)
- .values({
- name: name,
- user: user.id,
- })
- .returning();
-
- const addedMemories =
- memories.length > 0
- ? await db
- .insert(contentToSpace)
- .values(
- memories.map((m) => ({
- contentId: m,
- spaceId: addedSpace.id,
- })),
- )
- .returning()
- : [];
-
- return {
- space: addedSpace,
- addedMemories,
- };
-}
-
-export async function fetchContent(id: number) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- const fetchedMemory = await db
- .select()
- .from(storedContent)
- .where(and(eq(storedContent.id, id), eq(storedContent.user, user.id)));
-
- const memory = fetchedMemory.length > 0 ? fetchedMemory[0] : null;
-
- const spaces = memory
- ? await db
- .select()
- .from(contentToSpace)
- .where(eq(contentToSpace.contentId, memory.id))
- : [];
-
- return {
- memory,
- spaces: spaces.map((s) => s.spaceId),
- };
-}
-
-export async function fetchContentForSpace(
- spaceId: number,
- range?: {
- offset: number;
- limit: number;
- },
-) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- const query = db
- .select()
- .from(storedContent)
- .where(
- exists(
- db
- .select()
- .from(contentToSpace)
- .where(
- and(
- and(
- eq(contentToSpace.spaceId, spaceId),
- eq(contentToSpace.contentId, storedContent.id),
- ),
- exists(
- db
- .select()
- .from(space)
- .where(
- and(
- eq(space.user, user.id),
- eq(space.id, contentToSpace.spaceId),
- ),
- ),
- ),
- ),
- ),
- ),
- )
- .orderBy(asc(storedContent.savedAt));
-
- return range
- ? await query.limit(range.limit).offset(range.offset)
- : await query.all();
-}
-
-export async function fetchFreeMemories(range?: {
- offset: number;
- limit: number;
-}) {
- const user = await getUser();
-
- if (!user) {
- return [];
- }
-
- try {
- const query = db
- .select()
- .from(storedContent)
- .where(
- and(
- notExists(
- db
- .select()
- .from(contentToSpace)
- .where(eq(contentToSpace.contentId, storedContent.id)),
- ),
- eq(storedContent.user, user.id),
- ),
- )
- .orderBy(asc(storedContent.savedAt));
-
- return range
- ? await query.limit(range.limit).offset(range.offset)
- : await query.all();
- } catch {
- return [];
- }
-}
-
-export async function updateSpaceTitle(id: number, title: string) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- return (
- await db
- .update(space)
- .set({ name: title })
- .where(and(eq(space.id, id), eq(space.user, user.id)))
- .returning()
- )[0];
-}
-
-export async function addMemory(
- content: typeof storedContent.$inferInsert,
- spaces: number[],
-) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- if (!content.content || content.content.trim() === "") {
- const resp = await fetch(
- `https://cf-ai-backend.dhravya.workers.dev/getPageContent?url=${content.url}`,
- {
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- },
- },
- );
-
- const data = await resp.text();
-
- console.log(data);
-
- content.content = data;
- }
-
- if (!content.content || content.content == "") {
- return null;
- }
-
- let [addedMemory] = await db
- .insert(storedContent)
- .values({
- user: user.id,
- ...content,
- })
- .returning();
-
- const addedToSpaces =
- spaces.length > 0
- ? await db
- .insert(contentToSpace)
- .values(
- spaces.map((s) => ({
- contentId: addedMemory.id,
- spaceId: s,
- })),
- )
- .returning()
- : [];
-
- if (content.type === "note") {
- addedMemory = (
- await db
- .update(storedContent)
- .set({
- url: addedMemory.url + addedMemory.id,
- })
- .where(eq(storedContent.id, addedMemory.id))
- .returning()
- )[0];
- }
-
- console.log("adding with:", `${addedMemory.url}-${user.email}`);
- // Add to vectorDB
- 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({
- pageContent: addedMemory.content,
- title: addedMemory.title,
- url: addedMemory.url,
- user: user.email,
- }),
- }),
- new Promise((_, reject) =>
- setTimeout(() => reject(new Error("Request timed out")), 40000),
- ),
- ])) as Response;
-
- return {
- memory: addedMemory,
- addedToSpaces,
- };
-}
-
-export async function addContentInSpaces(id: number, contents: number[]) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- const data =
- contents.length > 0
- ? await db
- .insert(contentToSpace)
- .values(
- contents.map((i) => ({
- spaceId: id,
- contentId: i,
- })),
- )
- .returning()
- : [];
-
- return data;
-}
-
-export async function updateMemory(
- id: number,
- {
- title,
- content,
- spaces,
- removedFromSpaces: removeSpaces,
- }: {
- title?: string;
- content?: string;
- spaces?: number[];
- removedFromSpaces?: number[];
- },
-) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- let updatedMemory: StoredContent | null = null;
-
- if (title && content) {
- const [prev] = await db
- .select()
- .from(storedContent)
- .where(and(eq(storedContent.user, user.id), eq(storedContent.id, id)));
-
- if (!prev) {
- return null;
- }
-
- const newContent = {
- ...(title ? { title } : {}),
- ...(content ? { content } : {}),
- };
-
- const updated = {
- ...newContent,
- ...prev,
- };
-
- console.log("adding with:", `${updated.url}-${user.email}`);
- // Add to vectorDB
- const res = (await Promise.race([
- fetch("https://cf-ai-backend.dhravya.workers.dev/edit", {
- method: "POST",
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- },
- body: JSON.stringify({
- pageContent: updated.content,
- title: updated.title,
- url: updated.url,
- user: user.email,
- uniqueUrl: updated.url,
- }),
- }),
- new Promise((_, reject) =>
- setTimeout(() => reject(new Error("Request timed out")), 40000),
- ),
- ])) as Response;
-
- [updatedMemory] = await db
- .update(storedContent)
- .set(newContent)
- .where(and(eq(storedContent.id, id), eq(storedContent.user, user.id)))
- .returning();
-
- console.log(updatedMemory, newContent);
- }
-
- if (!updatedMemory) {
- [updatedMemory] = await db
- .select()
- .from(storedContent)
- .where(and(eq(storedContent.id, id), eq(storedContent.user, user.id)));
- }
-
- const removedFromSpaces = removeSpaces
- ? removeSpaces.length > 0
- ? await db
- .delete(contentToSpace)
- .where(
- and(
- inArray(contentToSpace.spaceId, removeSpaces),
- eq(contentToSpace.contentId, id),
- ),
- )
- .returning()
- : []
- : spaces
- ? spaces.length > 0
- ? await db
- .delete(contentToSpace)
- .where(
- and(
- notInArray(contentToSpace.spaceId, spaces),
- eq(contentToSpace.contentId, id),
- ),
- )
- .returning()
- : await db
- .delete(contentToSpace)
- .where(eq(contentToSpace.contentId, id))
- : [];
-
- const addedToSpaces =
- spaces && spaces.length > 0
- ? await db
- .insert(contentToSpace)
- .values(
- spaces.map((s) => ({
- contentId: id,
- spaceId: s,
- })),
- )
- .onConflictDoNothing()
- .returning()
- : [];
-
- const resultedSpaces =
- (
- await db
- .select()
- .from(contentToSpace)
- .where(eq(contentToSpace.contentId, id))
- .all()
- ).map((i) => i.spaceId) ?? [];
-
- return {
- memory: updatedMemory,
- addedToSpaces,
- removedFromSpaces,
- resultedSpaces,
- };
-}
-
-export async function deleteSpace(id: number) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- await db.delete(contentToSpace).where(eq(contentToSpace.spaceId, id));
-
- const [deleted] = await db
- .delete(space)
- .where(and(eq(space.user, user.id), eq(space.id, id)))
- .returning();
-
- return deleted;
-}
-
-export async function deleteMemory(id: number) {
- const user = await getUser();
-
- if (!user) {
- return null;
- }
-
- await db.delete(contentToSpace).where(eq(contentToSpace.contentId, id));
-
- const [deleted] = await db
- .delete(storedContent)
- .where(and(eq(storedContent.user, user.id), eq(storedContent.id, id)))
- .returning();
-
- if (deleted) {
- console.log("adding with:", `${deleted.url}-${user.email}`);
- const res = (await Promise.race([
- fetch(`https://cf-ai-backend.dhravya.workers.dev/delete`, {
- method: "DELETE",
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- },
- body: JSON.stringify({
- websiteUrl: deleted.url,
- user: user.email,
- }),
- }),
- new Promise((_, reject) =>
- setTimeout(() => reject(new Error("Request timed out")), 40000),
- ),
- ])) as Response;
- }
-
- return deleted;
-}
diff --git a/apps/web/src/app/MessagePoster.tsx b/apps/web/src/app/MessagePoster.tsx
deleted file mode 100644
index 1abad1be..00000000
--- a/apps/web/src/app/MessagePoster.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-"use client";
-
-import { useEffect } from "react";
-
-function MessagePoster({ jwt }: { jwt: string }) {
- useEffect(() => {
- if (typeof window === "undefined") return;
- window.postMessage({ jwt }, "*");
- }, [jwt]);
-
- return (
- {
- if (typeof window === "undefined") return;
- window.postMessage({ jwt }, "*");
- }}
- >
- Extension Auth
-
- );
-}
-
-export default MessagePoster;
diff --git a/apps/web/src/app/api/[...nextauth]/route.ts b/apps/web/src/app/api/[...nextauth]/route.ts
deleted file mode 100644
index db7d1fb8..00000000
--- a/apps/web/src/app/api/[...nextauth]/route.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { GET, POST } from "@/server/auth";
-export const runtime = "edge";
diff --git a/apps/web/src/app/api/addTweetsToDb/route.ts b/apps/web/src/app/api/addTweetsToDb/route.ts
deleted file mode 100644
index 7fe2edba..00000000
--- a/apps/web/src/app/api/addTweetsToDb/route.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import { db } from "@/server/db";
-import { eq } from "drizzle-orm";
-import { sessions, storedContent, users } from "@/server/db/schema";
-import { type NextRequest, NextResponse } from "next/server";
-
-export const runtime = "edge";
-
-interface TweetData {
- tweetText: string;
- postUrl: string;
- authorName: string;
- handle: string;
- time: string;
- saveToUser: string;
-}
-
-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, TOKEN 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 TweetData[];
-
- for (const tweet of data) {
- const { id } = (
- await db
- .insert(storedContent)
- .values({
- content: tweet.tweetText,
- title: "Twitter Bookmark",
- description: "",
- url: tweet.postUrl,
- baseUrl: "https://twitter.com",
- image: "https://supermemory.dhr.wtf/twitter.svg",
- savedAt: new Date(),
- user: session.user.id,
- type: "twitter-bookmark",
- })
- .returning({ id: storedContent.id })
- )[0];
-
- if (!id) {
- return NextResponse.json(
- {
- message: "Error",
- error:
- "Something went wrong when inserting the tweet to storedContent",
- },
- { status: 500 },
- );
- }
- }
-
- return NextResponse.json({ message: "OK", data: "Success" }, { status: 200 });
-}
diff --git a/apps/web/src/app/api/ask/route.ts b/apps/web/src/app/api/ask/route.ts
deleted file mode 100644
index 17b24b3e..00000000
--- a/apps/web/src/app/api/ask/route.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { db } from "@/server/db";
-import { eq } from "drizzle-orm";
-import { sessions, users } from "@/server/db/schema";
-import { type NextRequest, NextResponse } from "next/server";
-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 ", "");
-
- if (process.env.RATELIMITER) {
- const { success } = await process.env.RATELIMITER.limit({ key: token });
-
- if (!success) {
- return new Response(JSON.stringify({ message: "Rate limit exceeded" }), {
- status: 429,
- });
- }
- }
-
- 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 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,
- }),
- });
-
- 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);
-
- 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
deleted file mode 100644
index c815070b..00000000
--- a/apps/web/src/app/api/chat/route.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { db } from "@/server/db";
-import { eq } from "drizzle-orm";
-import { sessions, users } from "@/server/db/schema";
-import { type NextRequest, NextResponse } from "next/server";
-import { env } from "@/env";
-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 ", "");
-
- if (process.env.RATELIMITER) {
- const { success } = await process.env.RATELIMITER.limit({ key: token });
-
- if (!success) {
- return new Response(JSON.stringify({ message: "Rate limit exceeded" }), {
- status: 429,
- });
- }
- }
-
- 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,
- });
- }
-
- try {
- 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("sourcesOnly", sourcesOnly);
-
- if (sourcesOnly == "true") {
- const data = await resp.json();
- console.log("data", data);
- return new Response(JSON.stringify(data), { status: 200 });
- }
-
- 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 });
- } catch {}
-}
diff --git a/apps/web/src/app/api/getCount/route.ts b/apps/web/src/app/api/getCount/route.ts
deleted file mode 100644
index 9fe54f78..00000000
--- a/apps/web/src/app/api/getCount/route.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { db } from "@/server/db";
-import { and, eq, ne, sql } from "drizzle-orm";
-import { sessions, storedContent, users } from "@/server/db/schema";
-import { type NextRequest, NextResponse } from "next/server";
-
-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 ", "");
-
- 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 tweetsCount = await db
- .select({
- count: sql`count(*)`.mapWith(Number),
- })
- .from(storedContent)
- .where(
- and(
- eq(storedContent.user, session.user.id),
- eq(storedContent.type, "twitter-bookmark"),
- ),
- );
-
- const pageCount = await db
- .select({
- count: sql`count(*)`.mapWith(Number),
- })
- .from(storedContent)
- .where(
- and(
- eq(storedContent.user, session.user.id),
- ne(storedContent.type, "twitter-bookmark"),
- ),
- );
-
- return NextResponse.json({
- tweetsCount: tweetsCount[0].count,
- tweetsLimit: 1000,
- pageCount: pageCount[0].count,
- pageLimit: 100,
- user: session.user.email,
- });
-}
diff --git a/apps/web/src/app/api/me/route.ts b/apps/web/src/app/api/me/route.ts
deleted file mode 100644
index 6d269872..00000000
--- a/apps/web/src/app/api/me/route.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { db } from "@/server/db";
-import { eq } from "drizzle-orm";
-import { sessions, users } from "@/server/db/schema";
-import { type NextRequest, NextResponse } from "next/server";
-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 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 },
- );
- }
-
- 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 },
- );
- }
-
- 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
deleted file mode 100644
index 5806841e..00000000
--- a/apps/web/src/app/api/query/route.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import { db } from "@/server/db";
-import { eq } from "drizzle-orm";
-import { sessions, users } from "@/server/db/schema";
-import { type NextRequest, NextResponse } from "next/server";
-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 ", "");
-
- if (process.env.RATELIMITER) {
- const { success } = await process.env.RATELIMITER.limit({ key: token });
-
- if (!success) {
- return new Response(JSON.stringify({ message: "Rate limit exceeded" }), {
- status: 429,
- });
- }
- }
-
- 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 sourcesOnly =
- new URL(req.url).searchParams.get("sourcesOnly") ?? "false";
-
- 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,
- },
- },
- );
-
- 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 },
- );
- }
-
- // 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/spaces/route.ts b/apps/web/src/app/api/spaces/route.ts
deleted file mode 100644
index d2685e9f..00000000
--- a/apps/web/src/app/api/spaces/route.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { db } from "@/server/db";
-import { sessions, space, users } from "@/server/db/schema";
-import { eq } from "drizzle-orm";
-import { NextRequest, NextResponse } from "next/server";
-
-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 ", "");
-
- if (!token) {
- return new Response(
- JSON.stringify({ message: "Invalid Key, session not found." }),
- { status: 404 },
- );
- }
-
- if (process.env.RATELIMITER) {
- const { success } = await process.env.RATELIMITER.limit({ key: token });
-
- if (!success) {
- return new Response(JSON.stringify({ message: "Rate limit exceeded" }), {
- status: 429,
- });
- }
- }
-
- 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 userData = await db
- .select()
- .from(users)
- .where(eq(users.id, sessionData[0].userId))
- .limit(1);
-
- if (!userData || userData.length === 0) {
- return NextResponse.json(
- { message: "Invalid Key, session not found." },
- { status: 404 },
- );
- }
-
- const user = userData[0];
-
- const spaces = await db
- .select()
- .from(space)
- .where(eq(space.user, user.id))
- .all();
-
- return NextResponse.json(
- {
- message: "OK",
- data: spaces,
- },
- { status: 200 },
- );
-}
diff --git a/apps/web/src/app/api/store/route.ts b/apps/web/src/app/api/store/route.ts
deleted file mode 100644
index 457eae2e..00000000
--- a/apps/web/src/app/api/store/route.ts
+++ /dev/null
@@ -1,165 +0,0 @@
-import { db } from "@/server/db";
-import { and, eq, sql, inArray } from "drizzle-orm";
-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";
-
-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 },
- );
- }
-
- if (process.env.RATELIMITER) {
- const { success } = await process.env.RATELIMITER.limit({ key: token });
-
- if (!success) {
- return new Response(JSON.stringify({ message: "Rate limit exceeded" }), {
- status: 429,
- });
- }
- }
-
- 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;
- spaces?: string[];
- };
-
- const metadata = await getMetaData(data.url);
- let storeToSpaces = data.spaces;
-
- if (!storeToSpaces) {
- storeToSpaces = [];
- }
-
- const count = await db
- .select({
- count: sql`count(*)`.mapWith(Number),
- })
- .from(storedContent)
- .where(
- and(
- eq(storedContent.user, session.user.id),
- eq(storedContent.type, "page"),
- ),
- );
-
- if (count[0].count > 100) {
- return NextResponse.json(
- { message: "Error", error: "Limit exceeded" },
- { status: 499 },
- );
- }
-
- const { id } = (
- 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,
- })
- .returning({ id: storedContent.id })
- )[0];
-
- if (!id) {
- return NextResponse.json(
- { message: "Error", error: "Error in CF function" },
- { status: 500 },
- );
- }
-
- if (storeToSpaces.length > 0) {
- const spaceData = await db
- .select()
- .from(space)
- .where(
- and(
- inArray(space.name, storeToSpaces ?? []),
- eq(space.user, session.user.id),
- ),
- )
- .all();
-
- await Promise.all([
- spaceData.forEach(async (space) => {
- await db
- .insert(contentToSpace)
- .values({ contentId: id, spaceId: space.id });
- }),
- ]);
- }
-
- 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) {
- console.log(res.status, res.statusText);
- 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/api/vectorizeTweets/route.ts b/apps/web/src/app/api/vectorizeTweets/route.ts
deleted file mode 100644
index 63aa38f0..00000000
--- a/apps/web/src/app/api/vectorizeTweets/route.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { db } from "@/server/db";
-import { eq } from "drizzle-orm";
-import { sessions, storedContent, users } from "@/server/db/schema";
-import { type NextRequest, NextResponse } from "next/server";
-import { env } from "@/env";
-
-export const runtime = "edge";
-
-interface TweetData {
- tweetText: string;
- postUrl: string;
- authorName: string;
- handle: string;
- time: string;
- saveToUser: string;
-}
-
-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 body = (await req.json()) as TweetData[];
-
- console.log(body);
-
- const resp = await fetch(
- `https://cf-ai-backend.dhravya.workers.dev/batchUploadTweets`,
- {
- headers: {
- "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY,
- },
- method: "POST",
- body: JSON.stringify(body),
- },
- );
-
- return new Response(await resp.text(), {
- status: resp.status,
- headers: resp.headers,
- });
-}
diff --git a/apps/web/src/app/content.tsx b/apps/web/src/app/content.tsx
deleted file mode 100644
index 5a68d902..00000000
--- a/apps/web/src/app/content.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-"use client";
-import SessionProviderWrapper from "@/components/dev/SessionProviderWrapper";
-import Main from "@/components/Main";
-import Sidebar from "@/components/Sidebar/index";
-import { useState } from "react";
-
-export default function Content({ jwt }: { jwt: string }) {
- const [selectedItem, setSelectedItem] = useState(null);
-
- return (
-
-
-
-
-
-
- );
-}
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css
deleted file mode 100644
index bed9278b..00000000
--- a/apps/web/src/app/globals.css
+++ /dev/null
@@ -1,140 +0,0 @@
-@import "@radix-ui/colors/gray";
-@import "@radix-ui/colors/gray-dark";
-
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-:root {
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
- }
-}
-
-body {
- @apply text-rgray-11 max-h-screen overflow-y-hidden bg-white;
- /* color: rgb(var(--foreground-rgb));
- background: linear-gradient(
- to bottom,
- transparent,
- rgb(var(--background-end-rgb))
- )
- rgb(var(--background-start-rgb)); */
-}
-
-[vaul-drawer-wrapper] {
- @apply bg-rgray-2;
-}
-
-@layer utilities {
- .text-balance {
- text-wrap: balance;
- }
-}
-
-.sidebar {
- height: 100vh;
- height: 100dvh;
- max-height: 100vh;
- max-height: 100dvh;
-}
-
-.DrawerContent {
- padding-top: 5vh;
- padding-top: 5dvh;
-}
-
-.main-hidden {
- padding-bottom: 20vh;
- padding-bottom: 15dvh;
-}
-
-.bottom-padding {
- bottom: 20vh;
- bottom: 20dvh;
-}
-
-@media (min-width: 768px) {
- .bottom-padding {
- bottom: 0;
- }
-}
-
-.chat-answer code {
- @apply bg-rgray-3 border-rgray-5 text-rgray-11 text-wrap rounded-md border p-1 text-sm;
-}
-
-.novel-editor pre {
- @apply bg-rgray-3 border-rgray-5 text-rgray-11 my-5 rounded-md border p-4 text-sm;
-}
-
-.chat-answer h1 {
- @apply text-rgray-11 my-5 text-xl font-medium;
-}
-
-.chat-answer a {
- @apply underline underline-offset-1 opacity-90 hover:opacity-100;
-}
-
-.chat-answer img {
- @apply my-5 rounded-md font-medium;
-}
-
-.tippy-box {
- @apply bg-rgray-3 text-rgray-11 border-rgray-5 rounded-md border py-0;
-}
-
-.tippy-content #slash-command {
- @apply text-rgray-11 border-none bg-transparent;
-}
-
-#slash-command button {
- @apply text-rgray-11 py-2;
-}
-
-#slash-command button div:first-child {
- @apply text-rgray-11 bg-rgray-4 border-rgray-5;
-}
-
-#slash-command button.novel-bg-stone-100 {
- @apply bg-rgray-1;
-}
-
-.novel-editor [data-type="taskList"] > li {
- @apply my-0;
-}
-
-.novel-editor input[type="checkbox"] {
- @apply accent-rgray-4 rounded-md;
-
- background: var(--gray-4) !important;
- border: 1px solid var(--gray-10) !important;
-}
-
-.novel-editor .is-empty::before {
- content: "Press '/' for commands" !important;
-}
-
-.novel-editor h1 {
- @apply text-2xl;
-}
-
-.novel-editor h2 {
- @apply text-xl;
-}
-
-.novel-editor h3 {
- @apply text-lg;
-}
-
-.novel-editor .drag-handle {
- @apply hidden;
-}
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
deleted file mode 100644
index e96df271..00000000
--- a/apps/web/src/app/layout.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import type { Metadata } from "next";
-import { Roboto, Inter } from "next/font/google";
-import "./globals.css";
-
-const inter = Inter({ weight: ["300", "400", "500"], subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Supermemory - Your second brain",
- description: "Save your memories forever, build your own second brain.",
- openGraph: {
- images: [
- {
- url: "https://supermemory.dhr.wtf/og-image.png",
- width: 1200,
- height: 630,
- },
- ],
- siteName: "Supermemory",
- title: "Supermemory - Your second brain",
- description: "Save your memories forever, build your own second brain.",
- },
- twitter: {
- card: "summary_large_image",
- site: "https://supermemory.dhr.wtf",
- creator: "@dhravyashah",
- description: "Save your memories forever, build your own second brain.",
- images: [
- {
- url: "https://supermemory.dhr.wtf/og-image.png",
- width: 1200,
- height: 630,
- },
- ],
- },
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
-
-
-
-
-
-
-
- {children}
-
-
-
- );
-}
diff --git a/apps/web/src/app/not-found.tsx b/apps/web/src/app/not-found.tsx
deleted file mode 100644
index 3409889a..00000000
--- a/apps/web/src/app/not-found.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-export const runtime = "edge";
-
-export default function NotFound() {
- return (
- <>
- 404: This page could not be found.
-
-
-
-
- 404
-
-
-
This page could not be found.
-
-
-
- >
- );
-}
-
-const styles = {
- error: {
- fontFamily:
- 'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',
- height: "100vh",
- textAlign: "center",
- display: "flex",
- flexDirection: "column",
- alignItems: "center",
- justifyContent: "center",
- },
-
- desc: {
- display: "inline-block",
- },
-
- h1: {
- display: "inline-block",
- margin: "0 20px 0 0",
- padding: "0 23px 0 0",
- fontSize: 24,
- fontWeight: 500,
- verticalAlign: "top",
- lineHeight: "49px",
- },
-
- h2: {
- fontSize: 14,
- fontWeight: 400,
- lineHeight: "49px",
- margin: 0,
- },
-} as const;
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx
deleted file mode 100644
index 05cd1ab8..00000000
--- a/apps/web/src/app/page.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { db } from "@/server/db";
-import {
- ChachedSpaceContent,
- sessions,
- space,
- storedContent,
- users,
-} from "@/server/db/schema";
-import { and, eq, inArray, not } from "drizzle-orm";
-import { cookies, headers } from "next/headers";
-import { redirect } from "next/navigation";
-import { fetchContentForSpace, fetchFreeMemories } from "@/actions/db";
-import { MemoryProvider } from "@/contexts/MemoryContext";
-import Content from "./content";
-import Main from "@/components/Main";
-import { TailwindIndicator } from "@/components/dev/tailwindindicator";
-
-export const runtime = "edge";
-
-export default async function Home() {
- const token =
- cookies().get("next-auth.session-token")?.value ??
- cookies().get("__Secure-authjs.session-token")?.value ??
- cookies().get("authjs.session-token")?.value ??
- headers().get("Authorization")?.replace("Bearer ", "");
-
- if (!token) {
- return redirect("/api/auth/signin");
- }
-
- const session = await db
- .select()
- .from(sessions)
- .where(eq(sessions.sessionToken, token!));
-
- if (!session || session.length === 0) {
- return redirect("/api/auth/signin");
- }
-
- const [userData] = await db
- .select()
- .from(users)
- .where(eq(users.id, session[0].userId))
- .limit(1);
-
- if (!userData) {
- return redirect("/api/auth/signin");
- }
-
- console.log(storedContent.user.name);
-
- const collectedSpaces = await db
- .select()
- .from(space)
- .where(eq(space.user, userData.id))
- .all();
-
- console.log(collectedSpaces);
-
- // Fetch only first 3 content of each spaces
- let contents: ChachedSpaceContent[] = [];
-
- await Promise.all([
- collectedSpaces.forEach(async (space) => {
- console.log("fetching ");
- const data = (
- (await fetchContentForSpace(space.id, {
- offset: 0,
- limit: 3,
- })) ?? []
- ).map((data) => ({
- ...data,
- space: space.id,
- }));
- contents = [...contents, ...data];
- }),
- ]);
-
- console.log("contents", contents);
-
- // freeMemories
- const freeMemories = await fetchFreeMemories();
- console.log("free", freeMemories);
-
- return (
-
-
-
- {/* */}
-
- );
-}
diff --git a/apps/web/src/app/privacy/page.tsx b/apps/web/src/app/privacy/page.tsx
deleted file mode 100644
index 5e40cbe9..00000000
--- a/apps/web/src/app/privacy/page.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from "react";
-import Markdown from "react-markdown";
-import { policy } from "./privacy";
-
-export const runtime = "edge";
-
-function Page() {
- return (
-
- {policy}
-
- );
-}
-
-export default Page;
diff --git a/apps/web/src/app/privacy/privacy.ts b/apps/web/src/app/privacy/privacy.ts
deleted file mode 100644
index 2034f191..00000000
--- a/apps/web/src/app/privacy/privacy.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-export const policy = `
-# Privacy Policy for AnyContext
-
-## Introduction
-
-This Privacy Policy provides detailed information on the handling, storage, and protection of your personal information by AnyContext, a browser extension developed and owned by Dhravya Shah in 2024. The extension is designed to enhance your browsing experience by providing contextual information based on the content of the web pages you visit. This policy outlines the types of data collected by AnyContext, how it is used, and the measures we take to protect your privacy.
-
-## Information Collection
-
-AnyContext collects the following types of information:
-
-1. **Web Browsing Data**: The extension has the capability to see all websites that users visit. However, AnyContext only stores data when the user actively clicks on the extension button while browsing. The browser history is not recorded, ensuring that your browsing activities remain private.
-
-2. **Current Page Data**: Upon activation (click) by the user, AnyContext stores data from the current HTML page. This data is used to provide relevant contextual information based on the content of the page you are viewing.
-
-3. **Personal Information**: When you interact with AnyContext, we may collect personal information including but not limited to your email address, session data, name, and profile picture. This information is collected to improve your user experience and to provide personalized services.
-
-## Data Storage and Security
-
-All collected data is securely stored in a SQLite database hosted on Cloudflare D1. We employ industry-standard security measures to protect your information from unauthorized access, alteration, disclosure, or destruction. Despite our efforts, no method of transmission over the Internet or method of electronic storage is 100% secure. Therefore, while we strive to use commercially acceptable means to protect your personal information, we cannot guarantee its absolute security.
-
-## Use of Information
-
-AnyContext uses the collected information for the following purposes:
-
-- To provide and improve the functionality of the extension.
-- To offer personalized user experiences.
-- To communicate with users regarding updates, support, and promotional offers, if consented.
-- To ensure the security of our services and to detect, prevent, or address technical issues.
-
-## Sharing of Information
-
-AnyContext does not sell, trade, or rent users' personal identification information to others. We may share generic aggregated demographic information not linked to any personal identification information regarding visitors and users with our business partners, trusted affiliates, and advertisers for the purposes outlined above.
-
-## Your Privacy Rights
-
-You have the right to access, update, or delete your personal information that we hold. If you wish to exercise these rights, please contact us at the details provided below.
-
-## Changes to This Privacy Policy
-
-AnyContext reserves the right to update this privacy policy at any time. When we do, we will post a notification on our website and update the date at the top of this page. We encourage users to frequently check this page for any changes to stay informed about how we are protecting the personal information we collect. Your continued use of the service after the posting of changes to this policy will be deemed your acceptance of those changes.
-
-## Contact Us
-
-If you have any questions about this Privacy Policy, the practices of this site, or your dealings with this site, please contact us at:
-
-- Email: dhravyashah@gmail.com
-
-This document was last updated on March 2, 2024.`;
diff --git a/apps/web/src/assets/Bin.tsx b/apps/web/src/assets/Bin.tsx
deleted file mode 100644
index d0793cef..00000000
--- a/apps/web/src/assets/Bin.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import { cn } from "@/lib/utils";
-import { useEffect, useRef } from "react";
-
-export const Bin: React.FC & {}> = ({
- className,
- ...props
-}) => {
- const icon = useRef(null);
-
- useEffect(() => {
- let timeout: ReturnType | undefined;
-
- const observer = new MutationObserver(function (mutations) {
- mutations.forEach(function (mutation) {
- if (
- mutation.type === "attributes" &&
- mutation.attributeName === "data-open" &&
- (mutation.oldValue === "false" || mutation.oldValue === null) &&
- icon.current?.dataset["open"] === "true"
- ) {
- if (timeout) clearTimeout(timeout);
- timeout = setTimeout(() => {
- icon.current!.dataset["open"] = "false";
- }, 2000);
- }
- });
- });
-
- observer.observe(icon.current!, {
- attributes: true, //configure it to listen to attribute changes
- });
-
- return () => {
- observer.disconnect();
- };
- }, []);
-
- return (
- [data-lid]]:rotate-[150deg] [&[data-open='true']>[data-lid]]:delay-0",
- className,
- )}
- {...props}
- >
-
-
-
-
-
-
-
-
-
-
-
- );
-};
diff --git a/apps/web/src/assets/Memories.tsx b/apps/web/src/assets/Memories.tsx
deleted file mode 100644
index cafcd54f..00000000
--- a/apps/web/src/assets/Memories.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-export const MemoryIcon: React.FC> = (
- props,
-) => (
-
-
-
-
-
-);
-
-export const SpaceIcon: React.FC> = (props) => (
-
-
-
-
-);
diff --git a/apps/web/src/assets/MemoryWithImages.tsx b/apps/web/src/assets/MemoryWithImages.tsx
deleted file mode 100644
index 6f7ba90a..00000000
--- a/apps/web/src/assets/MemoryWithImages.tsx
+++ /dev/null
@@ -1,531 +0,0 @@
-import { svgId } from "@/lib/utils";
-
-export const MemoryWithImage: React.FC<
- { image: string; id: string } & React.SVGAttributes
-> = ({ image, id: _id, ...props }) => {
- const id = "space-1-" + _id;
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export const MemoryWithImages2: React.FC<
- { images: string[]; id: string } & React.SVGAttributes
-> = ({ images, id: _id, ...props }) => {
- const id = "space-2-" + _id;
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export const MemoryWithImages3: React.FC<
- { images: string[]; id: string } & React.SVGAttributes
-> = ({ images, id: _id, ...props }) => {
- const id = "space-3-" + _id;
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
diff --git a/apps/web/src/assets/Note.tsx b/apps/web/src/assets/Note.tsx
deleted file mode 100644
index e69de29b..00000000
diff --git a/apps/web/src/components/ChatMessage.tsx b/apps/web/src/components/ChatMessage.tsx
deleted file mode 100644
index 58ef9870..00000000
--- a/apps/web/src/components/ChatMessage.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import React, { useEffect } from "react";
-import { motion } from "framer-motion";
-import { ArrowUpRight, Globe, Text } from "lucide-react";
-import { convertRemToPixels } from "@/lib/utils";
-import { SpaceIcon } from "@/assets/Memories";
-import Markdown from "react-markdown";
-import { ChatHistory } from "../../types/memory";
-
-export function ChatAnswer({
- children: message,
- sources,
- loading = false,
-}: {
- children: string;
- sources?: ChatHistory["answer"]["sources"];
- loading?: boolean;
-}) {
- return (
-
- {loading ? (
-
- ) : (
-
- {message}
-
- )}
- {!loading && sources && sources?.length > 0 && (
- <>
-
-
- Related Memories
-
-
- >
- )}
-
- );
-}
-
-export function ChatQuestion({ children }: { children: string }) {
- return (
- 200 ? "text-xl" : "text-2xl"}`}
- >
- {children}
-
- );
-}
-
-export function ChatMessage({
- children,
- isLast = false,
- index,
-}: {
- children: React.ReactNode | React.ReactNode[];
- isLast?: boolean;
- index: number;
-}) {
- const messageRef = React.useRef(null);
-
- useEffect(() => {
- if (!isLast) return;
- messageRef.current?.parentElement?.scrollTo({
- top: messageRef.current?.offsetTop,
- behavior: "smooth",
- });
- }, []);
-
- return (
-
- {children}
-
- );
-}
-
-function MessageSkeleton() {
- return (
-
- );
-}
-
-function cleanUrl(url: string) {
- if (url.startsWith("https://")) {
- url = url.slice(8);
- } else if (url.startsWith("http://")) {
- url = url.slice(7);
- }
-
- if (url.endsWith("/")) {
- url = url.slice(0, -1);
- }
-
- return url;
-}
diff --git a/apps/web/src/components/Main-2.tsx b/apps/web/src/components/Main-2.tsx
deleted file mode 100644
index 1b602712..00000000
--- a/apps/web/src/components/Main-2.tsx
+++ /dev/null
@@ -1,709 +0,0 @@
-"use client";
-import { MemoryDrawer } from "./MemoryDrawer";
-import useViewport from "@/hooks/useViewport";
-import { AnimatePresence } from "framer-motion";
-import { cn } from "@/lib/utils";
-
-import { Editor } from "novel";
-import { useAutoAnimate } from "@formkit/auto-animate/react";
-import {
- MemoryWithImage,
- MemoryWithImages3,
- MemoryWithImages2,
-} from "@/assets/MemoryWithImages";
-import { Input, InputWithIcon } from "./ui/input";
-import {
- ArrowUpRight,
- Edit3,
- Loader,
- Minus,
- MoreHorizontal,
- Plus,
- Search,
- Sparkles,
- Text,
- Trash2,
-} from "lucide-react";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "./ui/dropdown-menu";
-import { useEffect, useMemo, useRef, useState } from "react";
-import { Variant, useAnimate, motion } from "framer-motion";
-import { SearchResult, useMemory } from "@/contexts/MemoryContext";
-import { SpaceIcon } from "@/assets/Memories";
-import { Dialog, DialogContent } from "./ui/dialog";
-import useTouchHold from "@/hooks/useTouchHold";
-import { DialogTrigger } from "@radix-ui/react-dialog";
-import {
- AddExistingMemoryToSpace,
- AddMemoryPage,
- NoteAddPage,
- SpaceAddPage,
-} from "./Sidebar/AddMemoryDialog";
-import { ExpandedSpace } from "./Sidebar/ExpandedSpace";
-import { StoredContent, StoredSpace } from "@/server/db/schema";
-import { useDebounce } from "@/hooks/useDebounce";
-import { NoteEdit } from "./Sidebar/EditNoteDialog";
-import DeleteConfirmation from "./Sidebar/DeleteConfirmation";
-
-import { ProfileDrawer } from "./ProfileDrawer";
-
-function supportsDVH() {
- try {
- return CSS.supports("height: 100dvh");
- } catch {
- return false;
- }
-}
-
-function pseudoRandomizeColorWithName(name: string) {
- const colorsAvailable = [
- "99e9f2",
- "a5d8ff",
- "d0bfff",
- "eebefa",
- "fcc2d7",
- "b2f2bb",
- "96f2d7",
- "ffec99",
- "ffd8a8",
- "ffc9c9",
- ];
-
- const colorIndex =
- name
- .split("")
- .map((char) => char.charCodeAt(0))
- .reduce((acc, charCode) => acc + charCode, 0) % colorsAvailable.length;
-
- return colorsAvailable[colorIndex];
-}
-
-export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) {
- const { width } = useViewport();
-
- const [parent, enableAnimations] = useAutoAnimate();
- const { spaces, deleteSpace, freeMemories, search } = useMemory();
-
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
- const [addMemoryState, setAddMemoryState] = useState<
- "page" | "note" | "space" | "existing-memory" | null
- >(null);
-
- const [expandedSpace, setExpandedSpace] = useState(null);
-
- const [searchQuery, setSearcyQuery] = useState("");
- const [searchLoading, setSearchLoading] = useState(false);
- const query = useDebounce(searchQuery, 500);
-
- const [searchResults, setSearchResults] = useState([]);
-
- useEffect(() => {
- const q = query.trim();
- if (q.length < 1) {
- setSearchResults([]);
- return;
- }
-
- setSearchLoading(true);
-
- (async () => {
- setSearchResults(await search(q));
- setSearchLoading(false);
- })();
- }, [query]);
-
- // useEffect(() => {
- // if (!isOpen) {
- // setExpandedSpace(null);
- // }
- // }, [isOpen]);
-
- if (expandedSpace) {
- return (
- setExpandedSpace(null)}
- // close={() => setExpandedSpace(null)}
- />
- );
- }
-
- return (
- <>
-
-
-
-
-
- Your Memories
-
-
-
-
-
-
-
- Add
-
-
- e.preventDefault()}
- >
-
- {
- setAddMemoryState("page");
- }}
- >
-
- Page to Memory
-
-
-
- {
- setAddMemoryState("note");
- }}
- >
-
- Note
-
-
-
- {
- setAddMemoryState("space");
- }}
- >
-
- Space
-
-
-
-
-
-
-
-
- ) : (
-
- )
- }
- className="mt-4 w-full text-black"
- value={searchQuery}
- onChange={(e) => setSearcyQuery(e.target.value)}
- />
-
-
- {typeof window !== "undefined" ? (
- query.trim().length > 0 ? (
- <>
- {searchResults.map(({ type, space, memory }, i) => (
- <>
- {type === "memory" && (
-
{
- setSearchResults((prev) =>
- prev.filter((i) => i.memory?.id !== memory.id),
- );
- }}
- />
- )}
- {type === "space" && (
- {
- setSearchResults((prev) =>
- prev.filter((i) => i.space?.id !== space.id),
- );
- deleteSpace(space.id);
- }}
- />
- )}
- >
- ))}
- >
- ) : (
- <>
- {spaces.map((space) => (
- deleteSpace(space.id)}
- key={space.id}
- onClick={() => setExpandedSpace(space.id)}
- {...space}
- />
- ))}
- {freeMemories.map((m) => (
-
- ))}
- >
- )
- ) : (
- <>
- {Array.from({
- length: spaces.length + freeMemories.length,
- }).map((_, i) => (
-
- ))}
- >
- )}
-
-
-
- {width <= 768 && }
-
- >
- );
-}
-
-export function MemoryItem(
- props: StoredContent & {
- onDelete?: () => void;
- removeFromSpace?: () => Promise;
- },
-) {
- const { id, title, image, type, url, onDelete, removeFromSpace } = props;
-
- const { deleteMemory } = useMemory();
-
- const name = title
- ? title.length > 20
- ? title.slice(0, 20) + "..."
- : title
- : "Untitled Memory";
-
- const [isDialogOpen, setIsDialogOpen] = useState(false);
-
- const [moreDropdownOpen, setMoreDropdownOpen] = useState(false);
-
- const touchEventProps = useTouchHold({
- onHold() {
- setMoreDropdownOpen(true);
- },
- });
- return (
-
-
- (type === "page" ? window.open(url) : null)}
- data-space-text
- className="relative flex h-min select-none flex-col items-center justify-center gap-2 text-center font-normal focus-visible:outline-none"
- >
-
- {type === "page" ? (
-
{
- deleteMemory(id);
- onDelete?.();
- }}
- url={url}
- />
- ) : type === "note" ? (
- setIsDialogOpen(true)}
- onDelete={() => {
- deleteMemory(id);
- onDelete?.();
- }}
- />
- ) : null}
-
-
- {type === "page" ? (
-
{
- (e.target as HTMLImageElement).src =
- "/icons/white_without_bg.png";
- }}
- />
- ) : type === "note" ? (
-
setIsDialogOpen(true)}
- className="h-16 w-16"
- />
- ) : (
- <>>
- )}
-
-
- {name}
-
-
-
- setIsDialogOpen(false)}
- memory={props}
- />
-
-
- );
-}
-
-export function SpaceItem({
- name,
- id,
- onDelete,
- onClick,
-}: StoredSpace & { onDelete: () => void; onClick?: () => void }) {
- const { cachedMemories } = useMemory();
-
- const [itemRef, animateItem] = useAnimate();
- const { width } = useViewport();
-
- const [moreDropdownOpen, setMoreDropdownOpen] = useState(false);
-
- const touchEventProps = useTouchHold({
- onHold() {
- setMoreDropdownOpen(true);
- },
- });
-
- const spaceMemories = useMemo(() => {
- return cachedMemories.filter((m) => m.space === id);
- }, [cachedMemories]);
-
- const _name = name.length > 20 ? name.slice(0, 20) + "..." : name;
-
- return (
-
-
-
- {spaceMemories.length > 2 ? (
- (c.type === "note" ? "/note.svg" : c.image))
- .reverse() as string[]
- }
- />
- ) : spaceMemories.length > 1 ? (
- (c.type === "note" ? "/note.svg" : c.image))
- .reverse() as string[]
- }
- />
- ) : spaceMemories.length === 1 ? (
-
- ) : (
-
- )}
-
-
- {_name}
-
- );
-}
-
-export function SpaceMoreButton({
- onDelete,
- isOpen,
- setIsOpen,
- onEdit,
-}: {
- onDelete?: () => void;
- isOpen?: boolean;
- onEdit?: () => void;
- setIsOpen?: (open: boolean) => void;
-}) {
- return (
-
-
-
-
-
-
-
-
-
-
- Edit
-
-
-
-
- Delete
-
-
-
-
-
- );
-}
-
-export function PageMoreButton({
- onDelete,
- isOpen,
- setIsOpen,
- url,
- removeFromSpace,
-}: {
- onDelete?: () => void;
- isOpen?: boolean;
- url: string;
- setIsOpen?: (open: boolean) => void;
- removeFromSpace?: () => Promise;
-}) {
- return (
-
-
-
-
-
-
-
-
- window.open(url)}>
-
- Open
-
- {removeFromSpace && (
-
-
- Remove from space
-
- )}
-
-
-
- Delete
-
-
-
-
-
- );
-}
-
-export function NoteMoreButton({
- onDelete,
- isOpen,
- setIsOpen,
- onEdit,
- removeFromSpace,
-}: {
- onDelete?: () => void;
- isOpen?: boolean;
- onEdit?: () => void;
- setIsOpen?: (open: boolean) => void;
- removeFromSpace?: () => Promise;
-}) {
- return (
-
-
-
-
-
-
-
-
-
-
- Edit
-
- {removeFromSpace && (
-
-
- Remove from space
-
- )}
-
-
-
- Delete
-
-
-
-
-
- );
-}
-
-export function AddMemoryModal({
- type,
- children,
- defaultSpaces,
- onAdd,
- data,
-}: {
- type: "page" | "note" | "space" | "existing-memory" | null;
- children?: React.ReactNode | React.ReactNode[];
- defaultSpaces?: number[];
- data?: {
- space?: {
- title: string;
- id: number;
- };
- fromSpaces?: number[];
- notInSpaces?: number[];
- };
- onAdd?: (data?: StoredSpace | StoredContent | StoredContent[]) => void;
-}) {
- const [isDialogOpen, setIsDialogOpen] = useState(false);
-
- return (
-
- {children}
- {
- e.preventDefault();
- const novel = document.querySelector('[contenteditable="true"]') as
- | HTMLDivElement
- | undefined;
- if (novel) {
- novel.autofocus = false;
- novel.onfocus = () => {
- (
- document.querySelector("[data-modal-autofocus]") as
- | HTMLInputElement
- | undefined
- )?.focus();
- novel.onfocus = null;
- };
- }
- (
- document.querySelector("[data-modal-autofocus]") as
- | HTMLInputElement
- | undefined
- )?.focus();
- }}
- className="w-max max-w-[auto]"
- >
- {type === "page" ? (
- setIsDialogOpen(false)}
- />
- ) : type === "note" ? (
- setIsDialogOpen(false)}
- />
- ) : type === "space" ? (
- setIsDialogOpen(false)}
- />
- ) : type === "existing-memory" ? (
- setIsDialogOpen(false)}
- />
- ) : (
- <>>
- )}
-
-
- );
-}
diff --git a/apps/web/src/components/Main.tsx b/apps/web/src/components/Main.tsx
deleted file mode 100644
index 8ac52569..00000000
--- a/apps/web/src/components/Main.tsx
+++ /dev/null
@@ -1,533 +0,0 @@
-"use client";
-import { useCallback, useEffect, useRef, useState } from "react";
-import { FilterSpaces } from "./Sidebar/FilterCombobox";
-import { Textarea2 } from "./ui/textarea";
-import { ArrowRight, ArrowUp } from "lucide-react";
-import { MemoryDrawer } from "./MemoryDrawer";
-import useViewport from "@/hooks/useViewport";
-import { AnimatePresence, motion } from "framer-motion";
-import { cn, countLines, getIdsFromSource } from "@/lib/utils";
-import { ChatHistory } from "../../types/memory";
-import { ChatAnswer, ChatMessage, ChatQuestion } from "./ChatMessage";
-import { useRouter, useSearchParams } from "next/navigation";
-import { useMemory } from "@/contexts/MemoryContext";
-
-import Image from "next/image";
-import { getMemoriesFromUrl } from "@/actions/db";
-import { ProfileDrawer } from "./ProfileDrawer";
-
-function supportsDVH() {
- try {
- return CSS.supports("height: 100dvh");
- } catch {
- return false;
- }
-}
-
-export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) {
- const searchParams = useSearchParams();
-
- const [hide, setHide] = useState(false);
- const [layout, setLayout] = useState<"chat" | "initial">("initial");
- const [value, setValue] = useState("");
- const { width } = useViewport();
- const [isAiLoading, setIsAiLoading] = useState(false);
-
- const { spaces } = useMemory();
-
- // Variable to keep track of the chat history in this session
- const [chatHistory, setChatHistory] = useState([]);
-
- const [toBeParsed, setToBeParsed] = useState("");
-
- const textArea = useRef(null);
- const main = useRef(null);
-
- const [selectedSpaces, setSelectedSpaces] = useState([]);
-
- const [isStreaming, setIsStreaming] = useState(false);
-
- useEffect(() => {
- const search = searchParams.get("q");
- if (search && search.trim().length > 0) {
- setValue(search);
- onSend();
- //router.push("/");
- }
- }, []);
-
- useEffect(() => {
- // function onResize() {
- // if (!main.current || !window.visualViewport) return;
- // if (
- // window.visualViewport.height < window.innerHeight + 20 &&
- // window.visualViewport.height > window.innerHeight - 20
- // ) {
- // setHide(false);
- // window.scrollTo(0, 0);
- // } else {
- // setHide(true);
- // window.scrollTo(0, document.body.scrollHeight);
- // }
- // }
- // window.visualViewport?.addEventListener("resize", onResize);
- // return () => {
- // window.visualViewport?.removeEventListener("resize", onResize);
- // };
- }, []);
-
- 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) {
- // Append to chat history in this way:
- // If the last message was from the model, append to that message
- // Otherwise, Start a new message from the model and append to that
- if (chatHistory.length > 0) {
- setChatHistory((prev: ChatHistory[]) => {
- const lastMessage = prev[prev.length - 1];
- const newParts = [
- ...lastMessage.answer.parts,
- { text: parsedPart.response },
- ];
- return [
- ...prev.slice(0, prev.length - 1),
- {
- ...lastMessage,
- answer: {
- parts: newParts,
- sources: lastMessage.answer.sources,
- },
- },
- ];
- });
- } else {
- }
- }
- } 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 modifyChatHistory = useCallback((old: ChatHistory[]) => {
- const final: { role: "user" | "model"; parts: { text: string }[] }[] = [];
- old.forEach((chat) => {
- final.push({
- role: "user",
- parts: [{ text: chat.question }],
- });
- final.push({
- role: "model",
- parts: chat.answer.parts.map((part) => ({ text: part.text })),
- });
- });
-
- return final;
- }, []);
-
- const getSearchResults = async () => {
- setIsAiLoading(true);
-
- const _value = value.trim();
- setValue("");
-
- setChatHistory((prev) => [
- ...prev,
- {
- question: _value,
- answer: {
- parts: [],
- sources: [],
- },
- },
- ]);
-
- const sourcesResponse = await fetch(
- `/api/chat?sourcesOnly=true&q=${_value}`,
- {
- method: "POST",
- body: JSON.stringify({
- chatHistory: modifyChatHistory(chatHistory),
- }),
- },
- );
-
- console.log("sources", sourcesResponse);
-
- const sourcesInJson =
- getIdsFromSource(
- (
- (await sourcesResponse.json()) as {
- ids: string[];
- }
- ).ids,
- ) ?? [];
-
- const notesInSources = sourcesInJson.filter((urls) =>
- urls.startsWith("https://notes.supermemory.dhr.wtf/"),
- );
- const nonNotes = sourcesInJson.filter((i) => !notesInSources.includes(i));
-
- const fetchedTitles = await getMemoriesFromUrl(notesInSources);
-
- const sources = [
- ...nonNotes.map((n) => ({ isNote: false, source: n ?? "" })),
- ...fetchedTitles.map((n) => ({
- isNote: true,
- source: n.title ?? "",
- })),
- ];
-
- setIsAiLoading(false);
- setChatHistory((prev) => {
- const lastMessage = prev[prev.length - 1];
- return [
- ...prev.slice(0, prev.length - 1),
- {
- ...lastMessage,
- answer: {
- parts: lastMessage.answer.parts,
- sources,
- },
- },
- ];
- });
-
- const actualSelectedSpaces = selectedSpaces.map(
- (space) => spaces.find((s) => s.id === space)?.name ?? "",
- );
-
- const response = await fetch(
- `/api/chat?q=${_value}&spaces=${actualSelectedSpaces.join(",")}`,
- {
- method: "POST",
- body: JSON.stringify({
- chatHistory: modifyChatHistory(chatHistory),
- }),
- },
- );
-
- if (response.status !== 200) {
- setIsAiLoading(false);
- return;
- }
-
- setIsStreaming(true);
-
- 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) {
- setIsAiLoading(false);
- setToBeParsed("");
-
- return;
- }
- setToBeParsed((prev) => prev + decoder.decode(value));
-
- return reader?.read().then(processText);
- });
- }
- };
-
- const onSend = () => {
- if (value.trim().length < 1) return;
- setLayout("chat");
- getSearchResults();
- };
-
- function onValueChange(e: React.ChangeEvent) {
- const value = e.target.value;
- setValue(value);
- const lines = countLines(e.target);
- e.target.rows = Math.min(5, lines);
- }
-
- return (
- <>
-
- {layout === "chat" ? (
-
- ) : (
-
-
-
-
- Ask your second brain
-
-
- {
- textArea.current?.querySelector("textarea")?.focus();
- }}
- side="top"
- align="start"
- className="mr-auto bg-[#252525] md:hidden"
- selectedSpaces={selectedSpaces}
- setSelectedSpaces={setSelectedSpaces}
- />
- {
- if (e.key === "Enter" && !e.shiftKey) {
- e.preventDefault();
- onSend();
- }
- },
- }}
- >
-
-
{
- textArea.current?.querySelector("textarea")?.focus();
- }}
- className="hidden md:flex"
- selectedSpaces={selectedSpaces}
- setSelectedSpaces={setSelectedSpaces}
- />
-
-
-
-
-
-
- setValue(e.target.value),
- onKeyDown: (e) => {
- if (e.key === "Enter" && !e.shiftKey) {
- e.preventDefault();
- onSend();
- }
- },
- }}
- className="hidden md:flex"
- >
-
-
{
- textArea.current?.querySelector("textarea")?.focus();
- }}
- className="hidden md:flex"
- selectedSpaces={selectedSpaces}
- setSelectedSpaces={setSelectedSpaces}
- />
-
-
-
-
-
-
- )}
- {width <= 768 && }
-
- >
- );
-}
-
-export function Chat({
- sidebarOpen,
- chatHistory,
- isLoading = false,
- askQuestion,
- setValue,
- value,
- selectedSpaces,
- setSelectedSpaces,
-}: {
- sidebarOpen: boolean;
- isLoading?: boolean;
- chatHistory: ChatHistory[];
- askQuestion: () => void;
- setValue: (value: string) => void;
- value: string;
- selectedSpaces: number[];
- setSelectedSpaces: React.Dispatch>;
-}) {
- const textArea = useRef(null);
-
- function onValueChange(e: React.ChangeEvent) {
- const value = e.target.value;
- setValue(value);
- const lines = countLines(e.target);
- e.target.rows = Math.min(5, lines);
- }
-
- const { width } = useViewport();
-
- return (
-
-
-
- {chatHistory.map((msg, i) => (
-
- {msg.question}
-
- {msg.answer.parts
- .map((part) => part.text)
- .join("")
- .replace("", "")}
-
-
- ))}
-
-
-
-
-
{
- textArea.current?.querySelector("textarea")?.focus();
- }}
- side="top"
- align="start"
- className="mr-auto bg-[#252525]"
- selectedSpaces={selectedSpaces}
- setSelectedSpaces={setSelectedSpaces}
- />
- {
- if (e.key === "Enter" && !e.shiftKey) {
- e.preventDefault();
- askQuestion();
- }
- },
- }}
- >
-
-
-
-
-
- );
-}
diff --git a/apps/web/src/components/MemoryDrawer.tsx b/apps/web/src/components/MemoryDrawer.tsx
deleted file mode 100644
index 14283281..00000000
--- a/apps/web/src/components/MemoryDrawer.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { useRef, useState } from "react";
-import { Drawer, DrawerContent, DrawerOverlay } from "./ui/drawer";
-import { MemoryIcon } from "@/assets/Memories";
-import { cn } from "@/lib/utils";
-import { MemoriesBar } from "./Sidebar/MemoriesBar";
-
-export interface Props extends React.ButtonHTMLAttributes {
- hide?: boolean;
-}
-
-export function MemoryDrawer({ className, hide = false, ...props }: Props) {
- const [activeSnapPoint, setActiveSnapPoint] = useState<
- number | null | string
- >(0.1);
-
- return (
-
-
-
- setActiveSnapPoint((prev) => (prev === 0.9 ? 0.1 : 0.9))
- }
- className="bg-rgray-4 border-rgray-6 text-rgray-11 absolute left-1/2 top-0 flex w-fit -translate-x-1/2 -translate-y-1/2 items-center justify-center gap-2 rounded-md border px-3 py-2"
- >
-
- Memories
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/web/src/components/ProfileDrawer.tsx b/apps/web/src/components/ProfileDrawer.tsx
deleted file mode 100644
index bdb32e03..00000000
--- a/apps/web/src/components/ProfileDrawer.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { useRef, useState } from "react";
-import {
- Drawer,
- DrawerContent,
- DrawerOverlay,
- DrawerTrigger,
-} from "./ui/drawer";
-import { cn } from "@/lib/utils";
-import { SettingsTab } from "./Sidebar/SettingsTab";
-import { useSession } from "next-auth/react";
-
-export interface Props extends React.ButtonHTMLAttributes {
- hide?: boolean;
-}
-
-export function ProfileDrawer({ className, hide = false, ...props }: Props) {
- const { data: session } = useSession();
-
- return (
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/web/src/components/SearchResults.tsx b/apps/web/src/components/SearchResults.tsx
deleted file mode 100644
index d348814e..00000000
--- a/apps/web/src/components/SearchResults.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-"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 (
-
-
-
-
- {aiResponse.replace("", "")}
-
-
-
-
- {sources.map((value, index) => (
-
- {value}
-
- ))}
-
-
- );
-}
-
-export default SearchResults;
diff --git a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx b/apps/web/src/components/Sidebar/AddMemoryDialog.tsx
deleted file mode 100644
index 64147b1e..00000000
--- a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx
+++ /dev/null
@@ -1,480 +0,0 @@
-import { Editor } from "novel";
-import {
- DialogClose,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
-} from "../ui/dialog";
-import { Input } from "../ui/input";
-import { Label } from "../ui/label";
-import { Markdown } from "tiptap-markdown";
-import { useEffect, useRef, useState } from "react";
-import { FilterMemories, FilterSpaces } from "./FilterCombobox";
-import { useMemory } from "@/contexts/MemoryContext";
-import { Loader, Plus, X } from "lucide-react";
-import { StoredContent, StoredSpace } from "@/server/db/schema";
-import { cleanUrl } from "@/lib/utils";
-import { motion } from "framer-motion";
-import { getMetaData } from "@/server/helpers";
-
-export function AddMemoryPage({
- closeDialog,
- defaultSpaces,
- onAdd,
-}: {
- closeDialog: () => void;
- defaultSpaces?: number[];
- onAdd?: (addedData: StoredContent) => void;
-}) {
- const { addMemory } = useMemory();
-
- const [loading, setLoading] = useState(false);
- const [url, setUrl] = useState("");
- const [selectedSpacesId, setSelectedSpacesId] = useState(
- defaultSpaces ?? [],
- );
-
- return (
-
-
- Add a web page to memory
-
- This will fetch the content of the web page and add it to the memory
-
-
- URL
- setUrl(e.target.value)}
- disabled={loading}
- />
-
-
- {
- setLoading(true);
- const metadata = await getMetaData(url);
- const data = await addMemory(
- {
- title: metadata.title,
- description: metadata.description,
- content: "",
- type: "page",
- url: url,
- image: metadata.image,
- savedAt: new Date(),
- },
- selectedSpacesId,
- );
- if (data) onAdd?.(data.memory);
- closeDialog();
- }}
- className="bg-rgray-4 focus-visible:ring-rgray-7 relative rounded-md px-4 py-2 ring-transparent transition hover:bg-slate-100 focus-visible:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70"
- >
-
-
-
-
- Add
-
-
-
- Cancel
-
-
-
- );
-}
-
-export function NoteAddPage({
- closeDialog,
- defaultSpaces,
- onAdd,
-}: {
- closeDialog: () => void;
- defaultSpaces?: number[];
- onAdd?: (addedData: StoredContent) => void;
-}) {
- const { addMemory } = useMemory();
-
- const [selectedSpacesId, setSelectedSpacesId] = useState(
- defaultSpaces ?? [],
- );
-
- const inputRef = useRef(null);
- const [name, setName] = useState("");
- const [content, setContent] = useState("");
- const [loading, setLoading] = useState(false);
-
- function check(): boolean {
- const data = {
- name: name.trim(),
- content,
- };
- if (!data.name || data.name.length < 1) {
- if (!inputRef.current) {
- alert("Please enter a name for the note");
- return false;
- }
- inputRef.current.value = "";
- inputRef.current.placeholder = "Please enter a title for the note";
- inputRef.current.dataset["error"] = "true";
- setTimeout(() => {
- inputRef.current!.placeholder = "Title of the note";
- inputRef.current!.dataset["error"] = "false";
- }, 500);
- inputRef.current.focus();
- return false;
- }
- return true;
- }
-
- return (
-
- setName(e.target.value)}
- />
- {
- if (!editor) return;
- setContent(editor.storage.markdown.getMarkdown());
- }}
- extensions={[Markdown]}
- className="novel-editor border-rgray-7 dark mt-5 max-h-[60vh] min-h-[40vh] w-full overflow-y-auto rounded-lg border bg-white md:w-[50vw] [&>div>div]:p-5"
- />
-
-
- {
- if (check()) {
- setLoading(true);
- addMemory(
- {
- content,
- title: name,
- type: "note",
- url: `https://notes.supermemory.dhr.wtf/`,
- image: "",
- savedAt: new Date(),
- },
- selectedSpacesId,
- ).then((data) => {
- if (data?.memory) onAdd?.(data.memory);
- closeDialog();
- });
- }
- }}
- disabled={loading}
- className="hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 relative rounded-md bg-[#F4F3F2] px-4 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70"
- >
-
-
-
-
- Add
-
-
-
- Cancel
-
-
-
- );
-}
-
-export function SpaceAddPage({
- closeDialog,
- onAdd,
-}: {
- closeDialog: () => void;
- onAdd?: (addedData: StoredSpace) => void;
-}) {
- const { addSpace } = useMemory();
-
- const inputRef = useRef(null);
- const [name, setName] = useState("");
-
- const [loading, setLoading] = useState(false);
-
- const [selected, setSelected] = useState([]);
-
- function check(): boolean {
- const data = {
- name: name.trim(),
- };
- if (!data.name || data.name.length < 1) {
- if (!inputRef.current) {
- alert("Please enter a name for the note");
- return false;
- }
- inputRef.current.value = "";
- inputRef.current.placeholder = "Please enter a title for the space";
- inputRef.current.dataset["error"] = "true";
- setTimeout(() => {
- inputRef.current!.placeholder = "Enter the name of the space";
- inputRef.current!.dataset["error"] = "false";
- }, 500);
- inputRef.current.focus();
- return false;
- }
- return true;
- }
-
- return (
-
-
- Add a space
-
-
Name
-
setName(e.target.value)}
- className="mt-2 w-full placeholder:transition placeholder:duration-500 data-[error=true]:placeholder:text-red-400 focus-visible:data-[error=true]:ring-red-500/10"
- />
- {selected.length > 0 && (
- <>
-
Add Memories
-
- {selected.map((i) => (
-
- setSelected((prev) => prev.filter((p) => p.id !== i.id))
- }
- {...i}
- />
- ))}
-
- >
- )}
-
-
-
- Memory
-
- {
- if (check()) {
- setLoading(true);
- addSpace(
- name,
- selected.map((s) => s.id),
- ).then((data) => {
- if (data) onAdd?.(data.space);
- closeDialog();
- });
- }
- }}
- disabled={loading}
- className="bg-rgray-4 focus-visible:ring-rgray-7 relative rounded-md px-4 py-2 ring-transparent transition hover:hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:hover:bg-slate-100 disabled:cursor-not-allowed disabled:opacity-70"
- >
-
-
-
-
- Add
-
-
-
- Cancel
-
-
-
- );
-}
-
-export function MemorySelectedItem({
- id,
- title,
- url,
- type,
- image,
- onRemove,
-}: StoredContent & { onRemove: () => void }) {
- return (
-
-
-
-
-
-
{title}
-
- {type === "note" ? "Note" : cleanUrl(url)}
-
-
- );
-}
-
-export function AddExistingMemoryToSpace({
- space,
- closeDialog,
- fromSpaces,
- notInSpaces,
- onAdd,
-}: {
- space: { title: string; id: number };
- closeDialog: () => void;
- fromSpaces?: number[];
- notInSpaces?: number[];
- onAdd?: () => void;
-}) {
- const { addMemoriesToSpace } = useMemory();
-
- const [loading, setLoading] = useState(false);
-
- const [selected, setSelected] = useState([]);
-
- return (
-
-
- Add an existing memory to {space.title}
-
- Pick the memories you want to add to this space
-
-
- {selected.length > 0 && (
- <>
-
Add Memories
-
- {selected.map((i) => (
-
- setSelected((prev) => prev.filter((p) => p.id !== i.id))
- }
- {...i}
- />
- ))}
-
- >
- )}
-
-
-
- Memory
-
- {
- setLoading(true);
- addMemoriesToSpace(
- space.id,
- selected.map((i) => i.id),
- ).then(() => {
- onAdd?.();
- closeDialog();
- });
- }}
- disabled={loading}
- className="bg-rgray-4 hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 relative rounded-md px-4 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70"
- >
-
-
-
-
- Add
-
-
-
- Cancel
-
-
-
- );
-}
diff --git a/apps/web/src/components/Sidebar/DeleteConfirmation.tsx b/apps/web/src/components/Sidebar/DeleteConfirmation.tsx
deleted file mode 100644
index 7955df0d..00000000
--- a/apps/web/src/components/Sidebar/DeleteConfirmation.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import {
- Dialog,
- DialogContent,
- DialogTrigger,
- DialogTitle,
- DialogDescription,
- DialogClose,
- DialogFooter,
-} from "../ui/dialog";
-
-export default function DeleteConfirmation({
- onDelete,
- trigger = true,
- children,
-}: {
- trigger?: boolean;
- onDelete?: () => void;
- children: React.ReactNode;
-}) {
- return (
-
- {trigger ? (
- {children}
- ) : (
- <>{children}>
- )}
-
- Are you sure?
-
- You will not be able to recover this it.
-
-
-
- Delete
-
-
- Cancel
-
-
-
-
- );
-}
diff --git a/apps/web/src/components/Sidebar/EditNoteDialog.tsx b/apps/web/src/components/Sidebar/EditNoteDialog.tsx
deleted file mode 100644
index c0ad716d..00000000
--- a/apps/web/src/components/Sidebar/EditNoteDialog.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import { Editor } from "novel";
-import { DialogClose, DialogFooter } from "../ui/dialog";
-import { Input } from "../ui/input";
-import { Markdown } from "tiptap-markdown";
-import { useEffect, useRef, useState } from "react";
-import { FilterSpaces } from "./FilterCombobox";
-import { useMemory } from "@/contexts/MemoryContext";
-import { Loader, Plus, Trash, X } from "lucide-react";
-import { motion } from "framer-motion";
-import { StoredContent } from "@/server/db/schema";
-import { fetchContent } from "@/actions/db";
-import { isArraysEqual } from "@/lib/utils";
-import DeleteConfirmation from "./DeleteConfirmation";
-
-export function NoteEdit({
- memory,
- closeDialog,
- onDelete,
-}: {
- memory: StoredContent;
- closeDialog: () => any;
- onDelete?: () => void;
-}) {
- const { updateMemory, deleteMemory } = useMemory();
-
- const [initialSpaces, setInitialSpaces] = useState([]);
- const [selectedSpacesId, setSelectedSpacesId] = useState([]);
-
- const inputRef = useRef(null);
- const [name, setName] = useState(memory.title ?? "");
- const [content, setContent] = useState(memory.content);
- const [loading, setLoading] = useState(false);
-
- function check(): boolean {
- const data = {
- name: name.trim(),
- content,
- };
- if (!data.name || data.name.length < 1) {
- if (!inputRef.current) {
- alert("Please enter a name for the note");
- return false;
- }
- inputRef.current.value = "";
- inputRef.current.placeholder = "Please enter a title for the note";
- inputRef.current.dataset["error"] = "true";
- setTimeout(() => {
- inputRef.current!.placeholder = "Title of the note";
- inputRef.current!.dataset["error"] = "false";
- }, 500);
- inputRef.current.focus();
- return false;
- }
- return true;
- }
-
- useEffect(() => {
- fetchContent(memory.id).then((data) => {
- if (data?.spaces) {
- setInitialSpaces(data.spaces);
- setSelectedSpacesId(data.spaces);
- }
- });
- }, []);
-
- return (
-
- setName(e.target.value)}
- />
- {
- if (!editor) return;
- setContent(editor.storage.markdown.getMarkdown());
- }}
- extensions={[Markdown]}
- className="novel-editor border-rgray-7 dark mt-5 max-h-[60vh] min-h-[40vh] w-[50vw] overflow-y-auto rounded-lg border bg-white [&>div>div]:p-5"
- />
-
-
- {
- deleteMemory(memory.id);
- onDelete?.();
- }}
- >
-
-
-
-
- {
- if (check()) {
- setLoading(true);
- console.log({
- title: name === memory.title ? undefined : name,
- content: content === memory.content ? undefined : content,
- spaces: isArraysEqual(initialSpaces, selectedSpacesId)
- ? undefined
- : selectedSpacesId,
- });
- updateMemory(memory.id, {
- title: name === memory.title ? undefined : name,
- content: content === memory.content ? undefined : content,
- spaces: isArraysEqual(initialSpaces, selectedSpacesId)
- ? undefined
- : selectedSpacesId,
- }).then(closeDialog);
- }
- }}
- disabled={loading}
- className="focus-visible:ring-rgray-7 relative rounded-md bg-white px-4 py-2 ring-transparent transition hover:bg-slate-100 focus-visible:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70"
- >
-
-
-
-
- Save
-
-
-
- Cancel
-
-
-
- );
-}
diff --git a/apps/web/src/components/Sidebar/ExpandedSpace.tsx b/apps/web/src/components/Sidebar/ExpandedSpace.tsx
deleted file mode 100644
index 55d3f3f8..00000000
--- a/apps/web/src/components/Sidebar/ExpandedSpace.tsx
+++ /dev/null
@@ -1,287 +0,0 @@
-import { fetchContentForSpace, getSpace } from "@/actions/db";
-import { useMemory } from "@/contexts/MemoryContext";
-import { StoredContent, StoredSpace } from "@/server/db/schema";
-import {
- Edit3,
- Loader,
- Plus,
- Search,
- Sparkles,
- StickyNote,
- Text,
- Undo2,
-} from "lucide-react";
-import { useEffect, useRef, useState } from "react";
-import { Input, InputWithIcon } from "../ui/input";
-import { useDebounce } from "@/hooks/useDebounce";
-import { useAutoAnimate } from "@formkit/auto-animate/react";
-import { AddMemoryModal, MemoryItem } from "./MemoriesBar";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "../ui/dropdown-menu";
-import { DialogTrigger } from "../ui/dialog";
-
-export function ExpandedSpace({
- spaceId,
- back,
-}: {
- spaceId: number;
- back: () => void;
-}) {
- const { updateMemory, updateSpace, search } = useMemory();
-
- const [parent, enableAnimations] = useAutoAnimate();
-
- const inputRef = useRef(null);
-
- const [contentForSpace, setContentForSpace] = useState([]);
-
- const [lastUpdatedTitle, setLastUpdatedTitle] = useState(null);
-
- const [title, setTitle] = useState("");
- const debouncedTitle = useDebounce(title, 500);
-
- const [loading, setLoading] = useState(true);
-
- const [saveLoading, setSaveLoading] = useState(false);
-
- const [searchQuery, setSearcyQuery] = useState("");
- const [searchLoading, setSearchLoading] = useState(false);
- const query = useDebounce(searchQuery, 500);
-
- const [searchResults, setSearchResults] = useState([]);
-
- const [addMemoryState, setAddMemoryState] = useState<
- "page" | "note" | "existing-memory" | "space" | null
- >(null);
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
-
- useEffect(() => {
- (async () => {
- const title = (await getSpace(spaceId))?.name ?? "";
- setTitle(title);
- setLastUpdatedTitle(title);
- setContentForSpace((await fetchContentForSpace(spaceId)) ?? []);
- setLoading(false);
- })();
- }, []);
-
- useEffect(() => {
- if (
- debouncedTitle.trim().length < 1 ||
- debouncedTitle.trim() === lastUpdatedTitle?.trim()
- )
- return;
- (async () => {
- setSaveLoading(true);
- await updateSpace(spaceId, debouncedTitle.trim());
- setLastUpdatedTitle(debouncedTitle);
- setSaveLoading(false);
- })();
- }, [debouncedTitle]);
-
- useEffect(() => {
- const q = query.trim();
- if (q.length < 1) {
- setSearchResults([]);
- return;
- }
-
- setSearchLoading(true);
-
- (async () => {
- setSearchResults(
- (
- await search(q, {
- filter: { spaces: false },
- memoriesRelativeToSpace: {
- fromSpaces: [spaceId],
- },
- })
- ).map((i) => i.memory!),
- );
- setSearchLoading(false);
- })();
- }, [query]);
-
- if (loading) {
- return (
-
-
-
- );
- }
-
- return (
-
-
-
-
-
- setTitle(e.target.value)}
- />
- {
- inputRef.current?.focus();
- inputRef.current?.animate(
- {
- opacity: [1, 0.2, 1],
- },
- {
- duration: 100,
- },
- );
- }}
- className="focus-visible:ring-offset-rgray-3 focus-visible:ring-rgray-7 rounded-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
- >
- {saveLoading ? (
-
- ) : (
-
- )}
-
-
-
-
- ) : (
-
- )
- }
- className="bg-rgray-4 mt-2 w-full"
- value={searchQuery}
- onChange={(e) => setSearcyQuery(e.target.value)}
- />
-
-
-
{
- if (!data) {
- setLoading(true);
- (async () => {
- const title = (await getSpace(spaceId))?.name ?? "";
- setTitle(title);
- setLastUpdatedTitle(title);
- setContentForSpace((await fetchContentForSpace(spaceId)) ?? []);
- setLoading(false);
- })();
- } else if (Object.hasOwn(data, "url")) {
- const _data = data as StoredContent;
- setContentForSpace((prev) => [...prev, _data]);
- }
- }}
- data={{ space: { title, id: spaceId }, notInSpaces: [spaceId] }}
- defaultSpaces={[spaceId]}
- type={addMemoryState}
- >
-
-
-
-
- Add
-
-
- e.preventDefault()}>
-
- {
- setAddMemoryState("existing-memory");
- }}
- >
-
- Existing Memory
-
-
-
- {
- setAddMemoryState("page");
- }}
- >
-
- Page
-
-
-
- {
- setAddMemoryState("note");
- }}
- >
-
- Note
-
-
-
-
-
-
-
- {query.trim().length > 0 ? (
- <>
- {searchResults.map((memory, i) => (
- {
- await updateMemory(memory.id, {
- removedFromSpaces: [spaceId],
- });
- setContentForSpace((prev) =>
- prev.filter((s) => s.id !== memory.id),
- );
- setSearchResults((prev) =>
- prev.filter((i) => i.id !== memory.id),
- );
- }}
- {...memory!}
- key={i}
- onDelete={() => {
- setContentForSpace((prev) =>
- prev.filter((s) => s.id !== memory.id),
- );
- setSearchResults((prev) =>
- prev.filter((i) => i.id !== memory.id),
- );
- }}
- />
- ))}
- >
- ) : (
- contentForSpace.map((m) => (
-
- setContentForSpace((prev) => prev.filter((s) => s.id !== m.id))
- }
- removeFromSpace={async () => {
- await updateMemory(m.id, {
- removedFromSpaces: [spaceId],
- });
- setContentForSpace((prev) => prev.filter((s) => s.id !== m.id));
- }}
- />
- ))
- )}
-
-
- );
-}
diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx
deleted file mode 100644
index 634a09e3..00000000
--- a/apps/web/src/components/Sidebar/FilterCombobox.tsx
+++ /dev/null
@@ -1,303 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { Check, ChevronsUpDown } from "lucide-react";
-
-import { cn } from "@/lib/utils";
-import { Button } from "@/components/ui/button";
-import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} 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 { SearchResult, useMemory } from "@/contexts/MemoryContext";
-import { useDebounce } from "@/hooks/useDebounce";
-import { StoredContent } from "@/server/db/schema";
-
-export interface FilterSpacesProps
- extends React.ButtonHTMLAttributes {
- side?: "top" | "bottom";
- align?: "end" | "start" | "center";
- onClose?: () => void;
- selectedSpaces: number[];
- setSelectedSpaces: (
- spaces: number[] | ((prev: number[]) => number[]),
- ) => void;
- name: string;
-}
-
-export function FilterSpaces({
- className,
- side = "bottom",
- align = "center",
- onClose,
- selectedSpaces,
- setSelectedSpaces,
- name,
- ...props
-}: FilterSpacesProps) {
- const { spaces } = useMemory();
- const [open, setOpen] = React.useState(false);
-
- const sortedSpaces = spaces.sort(({ id: a }, { id: b }) =>
- selectedSpaces.includes(a) && !selectedSpaces.includes(b)
- ? -1
- : selectedSpaces.includes(b) && !selectedSpaces.includes(a)
- ? 1
- : 0,
- );
-
- React.useEffect(() => {
- if (!open) {
- onClose?.();
- }
- }, [open]);
-
- return (
-
-
-
-
- {name}
-
- 0}
- className="on:flex text-rgray-11 border-rgray-6 bg-rgray-2 absolute left-0 top-0 hidden aspect-[1] h-4 w-4 -translate-x-1/3 -translate-y-1/3 items-center justify-center rounded-full border text-center text-[9px]"
- >
- {selectedSpaces.length}
-
-
-
- e.preventDefault()}
- >
-
- spaces
- .find((s) => s.id.toString() === val)
- ?.name.toLowerCase()
- .includes(search.toLowerCase().trim())
- ? 1
- : 0
- }
- >
-
-
-
- Nothing found
-
- {sortedSpaces.map((space) => (
- {
- setSelectedSpaces((prev: number[]) =>
- prev.includes(parseInt(val))
- ? prev.filter((v) => v !== parseInt(val))
- : [...prev, parseInt(val)],
- );
- }}
- asChild
- >
-
-
- {space.name.length > 10
- ? space.name.slice(0, 10) + "..."
- : space.name}
- {selectedSpaces.includes(space.id)}
-
-
-
- ))}
-
-
-
-
-
-
- );
-}
-
-export type FilterMemoriesProps = {
- side?: "top" | "bottom";
- align?: "end" | "start" | "center";
- onClose?: () => void;
- selected: StoredContent[];
- setSelected: React.Dispatch>;
- fromSpaces?: number[];
- notInSpaces?: number[];
-} & React.ButtonHTMLAttributes;
-
-export function FilterMemories({
- className,
- side = "bottom",
- align = "center",
- onClose,
- selected,
- setSelected,
- fromSpaces,
- notInSpaces,
- ...props
-}: FilterMemoriesProps) {
- const { search } = useMemory();
-
- const [open, setOpen] = React.useState(false);
- const [searchQuery, setSearchQuery] = React.useState("");
- const query = useDebounce(searchQuery, 500);
-
- const [searchResults, setSearchResults] = React.useState([]);
- const [isSearching, setIsSearching] = React.useState(false);
-
- const results = React.useMemo(() => {
- return searchResults.map((r) => r.memory);
- }, [searchResults]);
-
- console.log("memoized", results);
-
- React.useEffect(() => {
- const q = query.trim();
- if (q.length > 0) {
- setIsSearching(true);
- (async () => {
- const results = await search(q, {
- filter: {
- memories: true,
- spaces: false,
- },
- memoriesRelativeToSpace: {
- fromSpaces,
- notInSpaces,
- },
- });
- setSearchResults(results);
- setIsSearching(false);
- })();
- } else {
- setSearchResults([]);
- }
- }, [query]);
-
- React.useEffect(() => {
- if (!open) {
- onClose?.();
- }
- }, [open]);
-
- console.log(searchResults);
- return (
-
-
-
-
-
- {props.children}
-
-
- e.preventDefault()}
- align={align}
- side={side}
- className="w-[200px] p-0"
- >
-
-
-
-
-
- {isSearching
- ? "Searching..."
- : query.trim().length > 0
- ? "Nothing Found"
- : "Search something"}
-
- {results.map((m) => (
- {
- setSelected((prev) =>
- prev.find((p) => p.id === parseInt(val))
- ? prev.filter((v) => v.id !== parseInt(val))
- : [...prev, m],
- );
- }}
- asChild
- >
-
-
- {m.title && m.title?.length > 14
- ? m.title?.slice(0, 14) + "..."
- : m.title}
-
i.id === m.id) !== undefined
- }
- className={cn(
- "on:opacity-100 ml-auto h-4 w-4 opacity-0",
- )}
- />
-
-
- ))}
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/web/src/components/Sidebar/MemoriesBar.tsx b/apps/web/src/components/Sidebar/MemoriesBar.tsx
deleted file mode 100644
index a81d00c0..00000000
--- a/apps/web/src/components/Sidebar/MemoriesBar.tsx
+++ /dev/null
@@ -1,709 +0,0 @@
-import { Editor } from "novel";
-import { useAutoAnimate } from "@formkit/auto-animate/react";
-import {
- MemoryWithImage,
- MemoryWithImages3,
- MemoryWithImages2,
-} from "@/assets/MemoryWithImages";
-import { Input, InputWithIcon } from "../ui/input";
-import {
- ArrowUpRight,
- Edit3,
- Loader,
- Minus,
- MoreHorizontal,
- Plus,
- Search,
- Sparkles,
- Text,
- Trash2,
-} from "lucide-react";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "../ui/dropdown-menu";
-import { useEffect, useMemo, useRef, useState } from "react";
-import { Variant, useAnimate, motion } from "framer-motion";
-import { SearchResult, useMemory } from "@/contexts/MemoryContext";
-import { SpaceIcon } from "@/assets/Memories";
-import { Dialog, DialogContent } from "../ui/dialog";
-import useViewport from "@/hooks/useViewport";
-import useTouchHold from "@/hooks/useTouchHold";
-import { DialogTrigger } from "@radix-ui/react-dialog";
-import {
- AddExistingMemoryToSpace,
- AddMemoryPage,
- NoteAddPage,
- SpaceAddPage,
-} from "./AddMemoryDialog";
-import { ExpandedSpace } from "./ExpandedSpace";
-import { StoredContent, StoredSpace } from "@/server/db/schema";
-import { useDebounce } from "@/hooks/useDebounce";
-import { NoteEdit } from "./EditNoteDialog";
-import DeleteConfirmation from "./DeleteConfirmation";
-
-export function MemoriesBar({ isOpen }: { isOpen: boolean }) {
- const [parent, enableAnimations] = useAutoAnimate();
- const { spaces, deleteSpace, freeMemories, search } = useMemory();
-
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
- const [addMemoryState, setAddMemoryState] = useState<
- "page" | "note" | "space" | "existing-memory" | null
- >(null);
-
- const [expandedSpace, setExpandedSpace] = useState(null);
-
- const [searchQuery, setSearcyQuery] = useState("");
- const [searchLoading, setSearchLoading] = useState(false);
- const query = useDebounce(searchQuery, 500);
-
- const [searchResults, setSearchResults] = useState([]);
-
- useEffect(() => {
- const q = query.trim();
- if (q.length < 1) {
- setSearchResults([]);
- return;
- }
-
- setSearchLoading(true);
-
- (async () => {
- setSearchResults(await search(q));
- setSearchLoading(false);
- })();
- }, [query]);
-
- useEffect(() => {
- if (!isOpen) {
- setExpandedSpace(null);
- }
- }, [isOpen]);
-
- if (expandedSpace) {
- return (
- setExpandedSpace(null)}
- // close={() => setExpandedSpace(null)}
- />
- );
- }
-
- return (
-
-
-
Your Memories
-
- ) : (
-
- )
- }
- className="bg-rgray-4 mt-2 w-full"
- value={searchQuery}
- onChange={(e) => setSearcyQuery(e.target.value)}
- />
-
-
-
-
-
-
-
- Add
-
-
- e.preventDefault()}>
-
- {
- setAddMemoryState("page");
- }}
- >
-
- Page to Memory
-
-
-
- {
- setAddMemoryState("note");
- }}
- >
-
- Note
-
-
-
- {
- setAddMemoryState("space");
- }}
- >
-
- Space
-
-
-
-
-
-
-
- {query.trim().length > 0 ? (
- <>
- {searchResults.map(({ type, space, memory }, i) => (
- <>
- {type === "memory" && (
- {
- setSearchResults((prev) =>
- prev.filter((i) => i.memory?.id !== memory.id),
- );
- }}
- />
- )}
- {type === "space" && (
- {
- setSearchResults((prev) =>
- prev.filter((i) => i.space?.id !== space.id),
- );
- deleteSpace(space.id);
- }}
- />
- )}
- >
- ))}
- >
- ) : (
- <>
- {spaces.map((space) => (
- deleteSpace(space.id)}
- key={space.id}
- onClick={() => setExpandedSpace(space.id)}
- {...space}
- />
- ))}
- {freeMemories.map((m) => (
-
- ))}
- >
- )}
-
-
- );
-}
-
-const SpaceExitVariant: Variant = {
- opacity: 0,
- scale: 0,
- borderRadius: "50%",
- background: "var(--gray-1)",
- transition: {
- duration: 0.2,
- },
-};
-
-export function MemoryItem(
- props: StoredContent & {
- onDelete?: () => void;
- removeFromSpace?: () => Promise;
- },
-) {
- const { id, title, image, type, url, onDelete, removeFromSpace } = props;
-
- const { deleteMemory } = useMemory();
-
- const name = title
- ? title.length > 10
- ? title.slice(0, 10) + "..."
- : title
- : "";
-
- const [isDialogOpen, setIsDialogOpen] = useState(false);
-
- const [moreDropdownOpen, setMoreDropdownOpen] = useState(false);
-
- const touchEventProps = useTouchHold({
- onHold() {
- setMoreDropdownOpen(true);
- },
- });
- return (
-
-
- {type === "note" ? (
-
-
- {name}
-
-
- ) : (
-
window.open(url)}
- data-space-text
- className="focus-visible:outline-none"
- >
- {name}
-
- )}
-
- {type === "page" ? (
-
{
- deleteMemory(id);
- onDelete?.();
- }}
- url={url}
- />
- ) : type === "note" ? (
- setIsDialogOpen(true)}
- onDelete={() => {
- deleteMemory(id);
- onDelete?.();
- }}
- />
- ) : null}
-
-
- {type === "page" ? (
-
window.open(url)}
- className="h-16 w-16"
- id={id.toString()}
- src={image!}
- onError={(e) => {
- (e.target as HTMLImageElement).src =
- "/icons/white_without_bg.png";
- }}
- />
- ) : type === "note" ? (
-
setIsDialogOpen(true)} className="h-16 w-16" />
- ) : (
- <>>
- )}
-
-
-
- setIsDialogOpen(false)}
- memory={props}
- />
-
-
- );
-}
-
-export function SpaceItem({
- name,
- id,
- onDelete,
- onClick,
-}: StoredSpace & { onDelete: () => void; onClick?: () => void }) {
- const { cachedMemories } = useMemory();
-
- const [itemRef, animateItem] = useAnimate();
- const { width } = useViewport();
-
- const [moreDropdownOpen, setMoreDropdownOpen] = useState(false);
-
- const touchEventProps = useTouchHold({
- onHold() {
- setMoreDropdownOpen(true);
- },
- });
-
- const spaceMemories = useMemo(() => {
- return cachedMemories.filter((m) => m.space === id);
- }, [cachedMemories]);
-
- const _name = name.length > 10 ? name.slice(0, 10) + "..." : name;
-
- return (
-
-
- {_name}
-
- {
- onDelete();
- return;
- if (!itemRef.current || width < 768) {
- onDelete();
- return;
- }
- // const trash = document.querySelector("#trash")! as HTMLDivElement;
- // const trashBin = document.querySelector("#trash-button")!;
- // const trashRect = trashBin.getBoundingClientRect();
- // const scopeRect = itemRef.current.getBoundingClientRect();
- // const el = document.createElement("div");
- // el.style.position = "fixed";
- // el.style.top = "0";
- // el.style.left = "0";
- // el.style.width = "15px";
- // el.style.height = "15px";
- // el.style.backgroundColor = "var(--gray-7)";
- // el.style.zIndex = "60";
- // el.style.borderRadius = "50%";
- // el.style.transform = "scale(5)";
- // el.style.opacity = "0";
- // trash.dataset["open"] = "true";
- // const initial = {
- // x: scopeRect.left + scopeRect.width / 2,
- // y: scopeRect.top + scopeRect.height / 2,
- // };
- // const delta = {
- // x:
- // trashRect.left +
- // trashRect.width / 2 -
- // scopeRect.left +
- // scopeRect.width / 2,
- // y:
- // trashRect.top +
- // trashRect.height / 4 -
- // scopeRect.top +
- // scopeRect.height / 2,
- // };
- // const end = {
- // x: trashRect.left + trashRect.width / 2,
- // y: trashRect.top + trashRect.height / 4,
- // };
- // el.style.offsetPath = `path('M ${initial.x} ${initial.y} Q ${delta.x * 0.01} ${delta.y * 0.01} ${end.x} ${end.y}`;
- // animateItem(itemRef.current, SpaceExitVariant, {
- // duration: 0.2,
- // }).then(() => {
- // itemRef.current.style.scale = "0";
- // onDelete();
- // });
- // document.body.appendChild(el);
- // el.animate(
- // {
- // transform: ["scale(5)", "scale(1)"],
- // opacity: [0, 0.3, 1],
- // },
- // {
- // duration: 200,
- // easing: "cubic-bezier(0.64, 0.57, 0.67, 1.53)",
- // fill: "forwards",
- // },
- // );
- // el.animate(
- // {
- // offsetDistance: ["0%", "100%"],
- // },
- // {
- // duration: 2000,
- // easing: "cubic-bezier(0.64, 0.57, 0.67, 1.53)",
- // fill: "forwards",
- // delay: 200,
- // },
- // ).onfinish = () => {
- // el.animate(
- // { transform: "scale(0)", opacity: 0 },
- // { duration: 200, fill: "forwards" },
- // ).onfinish = () => {
- // el.remove();
- // };
- // };
- }}
- />
- {spaceMemories.length > 2 ? (
- (c.type === "note" ? "/note.svg" : c.image))
- .reverse() as string[]
- }
- />
- ) : spaceMemories.length > 1 ? (
- (c.type === "note" ? "/note.svg" : c.image))
- .reverse() as string[]
- }
- />
- ) : spaceMemories.length === 1 ? (
-
- ) : (
-
- )}
-
- );
-}
-
-export function SpaceMoreButton({
- onDelete,
- isOpen,
- setIsOpen,
- onEdit,
-}: {
- onDelete?: () => void;
- isOpen?: boolean;
- onEdit?: () => void;
- setIsOpen?: (open: boolean) => void;
-}) {
- return (
-
-
-
-
-
-
-
-
-
-
- Edit
-
-
-
-
- Delete
-
-
-
-
-
- );
-}
-
-export function PageMoreButton({
- onDelete,
- isOpen,
- setIsOpen,
- url,
- removeFromSpace,
-}: {
- onDelete?: () => void;
- isOpen?: boolean;
- url: string;
- setIsOpen?: (open: boolean) => void;
- removeFromSpace?: () => Promise;
-}) {
- return (
-
-
-
-
-
-
-
-
- window.open(url)}>
-
- Open
-
- {removeFromSpace && (
-
-
- Remove from space
-
- )}
-
-
-
- Delete
-
-
-
-
-
- );
-}
-
-export function NoteMoreButton({
- onDelete,
- isOpen,
- setIsOpen,
- onEdit,
- removeFromSpace,
-}: {
- onDelete?: () => void;
- isOpen?: boolean;
- onEdit?: () => void;
- setIsOpen?: (open: boolean) => void;
- removeFromSpace?: () => Promise;
-}) {
- return (
-
-
-
-
-
-
-
-
-
-
- Edit
-
- {removeFromSpace && (
-
-
- Remove from space
-
- )}
-
-
-
- Delete
-
-
-
-
-
- );
-}
-
-export function AddMemoryModal({
- type,
- children,
- defaultSpaces,
- onAdd,
- data,
-}: {
- type: "page" | "note" | "space" | "existing-memory" | null;
- children?: React.ReactNode | React.ReactNode[];
- defaultSpaces?: number[];
- data?: {
- space?: {
- title: string;
- id: number;
- };
- fromSpaces?: number[];
- notInSpaces?: number[];
- };
- onAdd?: (data?: StoredSpace | StoredContent | StoredContent[]) => void;
-}) {
- const [isDialogOpen, setIsDialogOpen] = useState(false);
-
- return (
-
- {children}
- {
- e.preventDefault();
- const novel = document.querySelector('[contenteditable="true"]') as
- | HTMLDivElement
- | undefined;
- if (novel) {
- novel.autofocus = false;
- novel.onfocus = () => {
- (
- document.querySelector("[data-modal-autofocus]") as
- | HTMLInputElement
- | undefined
- )?.focus();
- novel.onfocus = null;
- };
- }
- (
- document.querySelector("[data-modal-autofocus]") as
- | HTMLInputElement
- | undefined
- )?.focus();
- }}
- className="w-max max-w-[auto]"
- >
- {type === "page" ? (
- setIsDialogOpen(false)}
- />
- ) : type === "note" ? (
- setIsDialogOpen(false)}
- />
- ) : type === "space" ? (
- setIsDialogOpen(false)}
- />
- ) : type === "existing-memory" ? (
- setIsDialogOpen(false)}
- />
- ) : (
- <>>
- )}
-
-
- );
-}
diff --git a/apps/web/src/components/Sidebar/SettingsTab.tsx b/apps/web/src/components/Sidebar/SettingsTab.tsx
deleted file mode 100644
index 31b8380d..00000000
--- a/apps/web/src/components/Sidebar/SettingsTab.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import { Box, LogOut } from "lucide-react";
-import { signOut, useSession } from "next-auth/react";
-import { useEffect, useState } from "react";
-
-export function SettingsTab({ open }: { open: boolean }) {
- const { data: session } = useSession();
-
- const [tweetStat, setTweetStat] = useState<[number, number] | null>();
- const [memoryStat, setMemoryStat] = useState<[number, number] | null>();
-
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- fetch("/api/getCount").then(async (resp) => {
- const data = (await resp.json()) as any;
- setTweetStat([data.tweetsCount, data.tweetsLimit]);
- setMemoryStat([data.pageCount, data.pageLimit]);
- setLoading(false);
- });
- }, [open]);
-
- return (
-
-
-
Settings
-
-
{
- (e.target as HTMLImageElement).src =
- "/icons/white_without_bg.png";
- }}
- />
-
-
{session?.user?.name}
- {session?.user?.email}
- signOut()}
- className="bg-rgray-4 hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 relative mt-auto flex items-center justify-center gap-2 rounded-md px-4 py-2 text-white ring-transparent transition focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70"
- >
-
- Logout
-
-
-
-
-
-
-
- Storage
-
- {loading ? (
-
- ) : (
- <>
-
-
- Memories
-
- {memoryStat?.join("/")}
-
-
-
-
0 ? "5%" : "0%",
- }}
- className="bg-rgray-5 h-full rounded-full"
- />
-
-
-
-
- Tweets
-
- {tweetStat?.join("/")}
-
-
-
-
0 ? "5%" : "0%",
- }}
- className="h-full rounded-full bg-white"
- />
-
-
- >
- )}
-
-
- );
-}
diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx
deleted file mode 100644
index ae757afe..00000000
--- a/apps/web/src/components/Sidebar/index.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-"use client";
-import { MemoryIcon } from "../../assets/Memories";
-import React, { useEffect, useState } from "react";
-import { AnimatePresence, motion } from "framer-motion";
-import { signOut, useSession } from "next-auth/react";
-import MessagePoster from "@/app/MessagePoster";
-import Link from "next/link";
-import { SettingsTab } from "./SettingsTab";
-import { Avatar, AvatarImage } from "@radix-ui/react-avatar";
-import { AvatarFallback } from "../ui/avatar";
-
-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 [selectedItem, setSelectedItem] = useState
(null);
-
- const menuItemsTop: Array = [];
-
- const menuItemsBottom: Array = [
- {
- label: "Settings",
- content: ,
- icon: <>>,
- },
- ];
-
- const menuItems = [...menuItemsTop, ...menuItemsBottom];
-
- const Subbar = menuItems.find((i) => i.label === selectedItem)?.content ?? (
- <>>
- );
-
- useEffect(() => {
- void selectChange?.(selectedItem);
- }, [selectedItem]);
-
- return (
-
-
-
setSelectedItem(null)}
- className="focus-visible:ring-rgray-7 relative z-[100] flex w-full flex-col items-center justify-center rounded-md px-3 py-3 opacity-80 ring-2 ring-transparent transition hover:bg-stone-300 hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none"
- >
-
-
Memories
-
-
-
-
-
-
-
-
- ),
- content: ,
- }}
- selectedItem={selectedItem}
- setSelectedItem={setSelectedItem}
- />
- {/* */}
-
-
-
-
- {session?.user?.name?.split(" ").map((n) => n[0])}{" "}
-
-
-
{session?.user?.name?.split(" ")[0]}
-
-
-
- {selectedItem && {Subbar} }
-
-
- );
-}
-
-const MenuItem = ({
- item: { icon, label, labelDisplay },
- selectedItem,
- setSelectedItem,
- ...props
-}: {
- item: MenuItem;
- selectedItem: string | null;
- setSelectedItem: React.Dispatch>;
-}) => {
- const handleClick = () =>
- setSelectedItem((prev) => (prev === label ? null : label));
-
- return (
-
- {icon}
- {labelDisplay ?? label}
-
- );
-};
-
-export function SubSidebar({ children }: { children?: React.ReactNode }) {
- return (
-
-
- {children}
-
-
- );
-}
diff --git a/apps/web/src/components/WordMark.tsx b/apps/web/src/components/WordMark.tsx
deleted file mode 100644
index eb55647c..00000000
--- a/apps/web/src/components/WordMark.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { cn } from "@/lib/utils";
-import React from "react";
-
-function WordMark({ className }: { className?: string }) {
- return (
-
- smort.
-
- );
-}
-
-export default WordMark;
diff --git a/apps/web/src/components/dev/SessionProviderWrapper.tsx b/apps/web/src/components/dev/SessionProviderWrapper.tsx
deleted file mode 100644
index 71f77886..00000000
--- a/apps/web/src/components/dev/SessionProviderWrapper.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { SessionProvider } from "next-auth/react";
-import React from "react";
-
-function SessionProviderWrapper({ children }: { children: React.ReactNode }) {
- if (typeof window === "undefined") {
- return <>{children}>;
- } else {
- return {children} ;
- }
-}
-
-export default SessionProviderWrapper;
diff --git a/apps/web/src/components/dev/tailwindindicator.tsx b/apps/web/src/components/dev/tailwindindicator.tsx
deleted file mode 100644
index fd70276d..00000000
--- a/apps/web/src/components/dev/tailwindindicator.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-export function TailwindIndicator() {
- if (process.env.NODE_ENV === "production") return null;
-
- return (
-
-
-
xs
-
sm
-
md
-
lg
-
xl
-
2xl
-
-
- );
-}
diff --git a/apps/web/src/components/ui/avatar.tsx b/apps/web/src/components/ui/avatar.tsx
deleted file mode 100644
index b36abf28..00000000
--- a/apps/web/src/components/ui/avatar.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as AvatarPrimitive from "@radix-ui/react-avatar";
-
-import { cn } from "@/lib/utils";
-
-const Avatar = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-Avatar.displayName = AvatarPrimitive.Root.displayName;
-
-const AvatarImage = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-AvatarImage.displayName = AvatarPrimitive.Image.displayName;
-
-const AvatarFallback = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
-
-export { Avatar, AvatarImage, AvatarFallback };
diff --git a/apps/web/src/components/ui/badge.tsx b/apps/web/src/components/ui/badge.tsx
deleted file mode 100644
index fa390bec..00000000
--- a/apps/web/src/components/ui/badge.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as React from "react";
-import { cva, type VariantProps } from "class-variance-authority";
-
-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",
- {
- variants: {
- variant: {
- default:
- "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
- secondary:
- "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
- destructive:
- "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
- outline: "text-foreground",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- },
-);
-
-export interface BadgeProps
- extends React.HTMLAttributes,
- VariantProps {}
-
-function Badge({ className, variant, ...props }: BadgeProps) {
- return (
-
- );
-}
-
-export { Badge, badgeVariants };
diff --git a/apps/web/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx
deleted file mode 100644
index 24fa903e..00000000
--- a/apps/web/src/components/ui/button.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-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";
-
-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",
- {
- variants: {
- variant: {
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
- destructive:
- "bg-destructive text-destructive-foreground hover:bg-destructive/90",
- outline:
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
- secondary:
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
- ghost: "hover:bg-accent hover:text-accent-foreground",
- link: "text-primary underline-offset-4 hover:underline",
- },
- size: {
- default: "h-10 px-4 py-2",
- sm: "h-9 rounded-md px-3",
- lg: "h-11 rounded-md px-8",
- icon: "h-10 w-10",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- },
-);
-
-export interface ButtonProps
- extends React.ButtonHTMLAttributes,
- VariantProps {
- asChild?: boolean;
-}
-
-const Button = React.forwardRef(
- ({ className, variant, size, asChild = false, ...props }, ref) => {
- const Comp = asChild ? Slot : "button";
- return (
-
- );
- },
-);
-Button.displayName = "Button";
-
-export { Button, buttonVariants };
diff --git a/apps/web/src/components/ui/card.tsx b/apps/web/src/components/ui/card.tsx
deleted file mode 100644
index e98d500c..00000000
--- a/apps/web/src/components/ui/card.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-const Card = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-Card.displayName = "Card";
-
-const CardHeader = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardHeader.displayName = "CardHeader";
-
-const CardTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardTitle.displayName = "CardTitle";
-
-const CardDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardDescription.displayName = "CardDescription";
-
-const CardContent = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardContent.displayName = "CardContent";
-
-const CardFooter = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardFooter.displayName = "CardFooter";
-
-export {
- Card,
- CardHeader,
- CardFooter,
- CardTitle,
- CardDescription,
- CardContent,
-};
diff --git a/apps/web/src/components/ui/command.tsx b/apps/web/src/components/ui/command.tsx
deleted file mode 100644
index afc2cf46..00000000
--- a/apps/web/src/components/ui/command.tsx
+++ /dev/null
@@ -1,161 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { type DialogProps } from "@radix-ui/react-dialog";
-import { Command as CommandPrimitive } from "cmdk";
-import { Loader, Search } from "lucide-react";
-
-import { cn } from "@/lib/utils";
-import { Dialog, DialogContent } from "@/components/ui/dialog";
-
-const Command = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
- [cmdk-list-sizer]]:max-h-[250px] [&>[cmdk-list-sizer]]:overflow-y-scroll",
- className,
- )}
- {...props}
- />
-));
-Command.displayName = CommandPrimitive.displayName;
-
-interface CommandDialogProps extends DialogProps {}
-
-const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
- return (
-
-
-
- {children}
-
-
-
- );
-};
-
-const CommandInput = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- isSearching?: boolean;
- }
->(({ className, isSearching = false, ...props }, ref) => (
-
- {isSearching ? (
-
- ) : (
-
- )}
-
-
-));
-
-CommandInput.displayName = CommandPrimitive.Input.displayName;
-
-const CommandList = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandList.displayName = CommandPrimitive.List.displayName;
-
-const CommandEmpty = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->((props, ref) => (
-
-));
-
-CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
-
-const CommandGroup = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandGroup.displayName = CommandPrimitive.Group.displayName;
-
-const CommandSeparator = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
-
-const CommandItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandItem.displayName = CommandPrimitive.Item.displayName;
-
-const CommandShortcut = ({
- className,
- ...props
-}: React.HTMLAttributes) => {
- return (
-
- );
-};
-CommandShortcut.displayName = "CommandShortcut";
-
-export {
- Command,
- CommandDialog,
- CommandInput,
- CommandList,
- CommandEmpty,
- CommandGroup,
- CommandItem,
- CommandShortcut,
- CommandSeparator,
-};
diff --git a/apps/web/src/components/ui/dialog.tsx b/apps/web/src/components/ui/dialog.tsx
deleted file mode 100644
index 0da54769..00000000
--- a/apps/web/src/components/ui/dialog.tsx
+++ /dev/null
@@ -1,119 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as DialogPrimitive from "@radix-ui/react-dialog";
-import { X } from "lucide-react";
-
-import { cn } from "@/lib/utils";
-
-const Dialog = DialogPrimitive.Root;
-
-const DialogTrigger = DialogPrimitive.Trigger;
-
-const DialogPortal = DialogPrimitive.Portal;
-
-const DialogClose = DialogPrimitive.Close;
-
-const DialogOverlay = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
-
-const DialogContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
-
-
-
- {children}
-
-
- Close
-
-
-
-));
-DialogContent.displayName = DialogPrimitive.Content.displayName;
-
-const DialogHeader = ({
- className,
- ...props
-}: React.HTMLAttributes) => (
-
-);
-DialogHeader.displayName = "DialogHeader";
-
-const DialogFooter = ({
- className,
- ...props
-}: React.HTMLAttributes) => (
-
-);
-DialogFooter.displayName = "DialogFooter";
-
-const DialogTitle = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DialogTitle.displayName = DialogPrimitive.Title.displayName;
-
-const DialogDescription = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DialogDescription.displayName = DialogPrimitive.Description.displayName;
-
-export {
- Dialog,
- DialogPortal,
- DialogOverlay,
- DialogClose,
- DialogTrigger,
- DialogContent,
- DialogHeader,
- DialogFooter,
- DialogTitle,
- DialogDescription,
-};
diff --git a/apps/web/src/components/ui/drawer.tsx b/apps/web/src/components/ui/drawer.tsx
deleted file mode 100644
index 8ba01253..00000000
--- a/apps/web/src/components/ui/drawer.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { Drawer as DrawerPrimitive } from "vaul";
-
-import { cn } from "@/lib/utils";
-
-const Drawer = ({
- shouldScaleBackground = true,
- ...props
-}: React.ComponentProps) => (
-
-);
-Drawer.displayName = "Drawer";
-
-const DrawerTrigger = DrawerPrimitive.Trigger;
-
-const DrawerPortal = DrawerPrimitive.Portal;
-
-const DrawerClose = DrawerPrimitive.Close;
-
-const DrawerOverlay = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
-
-const DrawerContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- overlay?: boolean;
- handle?: boolean;
- }
->(({ className, children, overlay = true, handle = true, ...props }, ref) => (
-
- {overlay && }
-
- {handle && (
-
- )}
- {children}
-
-
-));
-DrawerContent.displayName = "DrawerContent";
-
-const DrawerHeader = ({
- className,
- ...props
-}: React.HTMLAttributes) => (
-
-);
-DrawerHeader.displayName = "DrawerHeader";
-
-const DrawerFooter = ({
- className,
- ...props
-}: React.HTMLAttributes) => (
-
-);
-DrawerFooter.displayName = "DrawerFooter";
-
-const DrawerTitle = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
-
-const DrawerDescription = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
-
-export {
- Drawer,
- DrawerPortal,
- DrawerOverlay,
- DrawerTrigger,
- DrawerClose,
- DrawerContent,
- DrawerHeader,
- DrawerFooter,
- DrawerTitle,
- DrawerDescription,
-};
diff --git a/apps/web/src/components/ui/dropdown-menu.tsx b/apps/web/src/components/ui/dropdown-menu.tsx
deleted file mode 100644
index fbe2d99c..00000000
--- a/apps/web/src/components/ui/dropdown-menu.tsx
+++ /dev/null
@@ -1,200 +0,0 @@
-"use client";
-
-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";
-
-const DropdownMenu = DropdownMenuPrimitive.Root;
-
-const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
-
-const DropdownMenuGroup = DropdownMenuPrimitive.Group;
-
-const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
-
-const DropdownMenuSub = DropdownMenuPrimitive.Sub;
-
-const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
-
-const DropdownMenuSubTrigger = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, children, ...props }, ref) => (
-
- {children}
-
-
-));
-DropdownMenuSubTrigger.displayName =
- DropdownMenuPrimitive.SubTrigger.displayName;
-
-const DropdownMenuSubContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DropdownMenuSubContent.displayName =
- DropdownMenuPrimitive.SubContent.displayName;
-
-const DropdownMenuContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, sideOffset = 4, ...props }, ref) => (
-
-
-
-));
-DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
-
-const DropdownMenuItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, ...props }, ref) => (
-
-));
-DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
-
-const DropdownMenuCheckboxItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, checked, ...props }, ref) => (
-
-
-
-
-
-
- {children}
-
-));
-DropdownMenuCheckboxItem.displayName =
- DropdownMenuPrimitive.CheckboxItem.displayName;
-
-const DropdownMenuRadioItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
-
-
-
-
-
-
- {children}
-
-));
-DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
-
-const DropdownMenuLabel = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- inset?: boolean;
- }
->(({ className, inset, ...props }, ref) => (
-
-));
-DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
-
-const DropdownMenuSeparator = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
-
-const DropdownMenuShortcut = ({
- className,
- ...props
-}: React.HTMLAttributes) => {
- return (
-
- );
-};
-DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
-
-export {
- DropdownMenu,
- DropdownMenuTrigger,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuCheckboxItem,
- DropdownMenuRadioItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuShortcut,
- DropdownMenuGroup,
- DropdownMenuPortal,
- DropdownMenuSub,
- DropdownMenuSubContent,
- DropdownMenuSubTrigger,
- DropdownMenuRadioGroup,
-};
diff --git a/apps/web/src/components/ui/input.tsx b/apps/web/src/components/ui/input.tsx
deleted file mode 100644
index 9d925512..00000000
--- a/apps/web/src/components/ui/input.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-export interface InputProps
- extends React.InputHTMLAttributes {}
-
-const Input = React.forwardRef(
- ({ className, type, ...props }, ref) => {
- return (
-
- );
- },
-);
-
-export interface InputWithIconProps
- extends React.InputHTMLAttributes {
- icon: React.ReactNode;
-}
-
-const InputWithIcon = React.forwardRef(
- ({ className, type, icon, ...props }, ref) => {
- return (
-
- {icon}
-
-
- );
- },
-);
-InputWithIcon.displayName = "Input";
-
-export { Input, InputWithIcon };
diff --git a/apps/web/src/components/ui/label.tsx b/apps/web/src/components/ui/label.tsx
deleted file mode 100644
index 84f8b0c7..00000000
--- a/apps/web/src/components/ui/label.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-"use client";
-
-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";
-
-const labelVariants = cva(
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
-);
-
-const Label = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef &
- VariantProps
->(({ className, ...props }, ref) => (
-
-));
-Label.displayName = LabelPrimitive.Root.displayName;
-
-export { Label };
diff --git a/apps/web/src/components/ui/popover.tsx b/apps/web/src/components/ui/popover.tsx
deleted file mode 100644
index cabe76a9..00000000
--- a/apps/web/src/components/ui/popover.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as PopoverPrimitive from "@radix-ui/react-popover";
-
-import { cn } from "@/lib/utils";
-
-const Popover = PopoverPrimitive.Root;
-
-const PopoverTrigger = PopoverPrimitive.Trigger;
-
-const PopoverContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef & {
- animate?: boolean;
- }
->(
- (
- { className, align = "center", animate = true, sideOffset = 4, ...props },
- ref,
- ) => (
-
-
-
- ),
-);
-PopoverContent.displayName = PopoverPrimitive.Content.displayName;
-
-export { Popover, PopoverTrigger, PopoverContent };
diff --git a/apps/web/src/components/ui/textarea.tsx b/apps/web/src/components/ui/textarea.tsx
deleted file mode 100644
index 3b2c9ddd..00000000
--- a/apps/web/src/components/ui/textarea.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import * as React from "react";
-
-import { cn } from "@/lib/utils";
-import { HTMLMotionProps, motion } from "framer-motion";
-
-export interface TextareaProps
- extends React.TextareaHTMLAttributes {}
-
-const Textarea = React.forwardRef(
- ({ className, ...props }, ref) => {
- return (
-
- );
- },
-);
-Textarea.displayName = "Textarea";
-
-export interface Textarea2Props extends HTMLMotionProps<"div"> {
- textAreaProps?: TextareaProps;
- children: React.ReactNode | React.ReactNode[];
-}
-
-const Textarea2 = React.forwardRef(
- ({ className, children, textAreaProps: _textAreaProps, ...props }, ref) => {
- const { className: textAreaClassName, ...textAreaProps } =
- _textAreaProps || {};
- return (
-
-
- {children}
-
- );
- },
-);
-Textarea2.displayName = "Textarea2";
-
-export { Textarea, Textarea2 };
diff --git a/apps/web/src/contexts/MemoryContext.tsx b/apps/web/src/contexts/MemoryContext.tsx
deleted file mode 100644
index 09412465..00000000
--- a/apps/web/src/contexts/MemoryContext.tsx
+++ /dev/null
@@ -1,241 +0,0 @@
-"use client";
-import React, { useCallback } from "react";
-import {
- ChachedSpaceContent,
- StoredContent,
- storedContent,
- StoredSpace,
-} from "@/server/db/schema";
-import {
- addMemory,
- searchMemoriesAndSpaces,
- addSpace,
- fetchContentForSpace,
- deleteSpace,
- deleteMemory,
- fetchFreeMemories,
- updateMemory,
- updateSpaceTitle,
- addContentInSpaces,
-} from "@/actions/db";
-import { User } from "next-auth";
-
-export type SearchResult = {
- type: "memory" | "space";
- space: StoredSpace;
- memory: StoredContent;
-};
-
-// temperory (will change)
-export const MemoryContext = React.createContext<{
- spaces: StoredSpace[];
- freeMemories: StoredContent[];
- addSpace: typeof addSpace;
- addMemory: typeof addMemory;
- cachedMemories: ChachedSpaceContent[];
- search: typeof searchMemoriesAndSpaces;
- deleteSpace: typeof deleteSpace;
- deleteMemory: typeof deleteMemory;
- updateMemory: typeof updateMemory;
- updateSpace: typeof updateSpaceTitle;
- addMemoriesToSpace: typeof addContentInSpaces;
-}>({
- spaces: [],
- freeMemories: [],
- addMemory: (() => {}) as unknown as typeof addMemory,
- addSpace: (async () => {}) as unknown as typeof addSpace,
- cachedMemories: [],
- search: async () => [],
- deleteMemory: (() => {}) as unknown as typeof deleteMemory,
- deleteSpace: (() => {}) as unknown as typeof deleteSpace,
- updateMemory: (() => {}) as unknown as typeof updateMemory,
- updateSpace: (() => {}) as unknown as typeof updateSpaceTitle,
- addMemoriesToSpace: (() => {}) as unknown as typeof addContentInSpaces,
-});
-
-export const MemoryProvider: React.FC<
- {
- spaces: StoredSpace[];
- freeMemories: StoredContent[];
- cachedMemories: ChachedSpaceContent[];
- user: User;
- } & React.PropsWithChildren
-> = ({
- children,
- user,
- spaces: initalSpaces,
- freeMemories: initialFreeMemories,
- cachedMemories: initialCachedMemories,
-}) => {
- const [spaces, setSpaces] = React.useState(initalSpaces);
- const [freeMemories, setFreeMemories] =
- React.useState