diff options
| author | Yash <[email protected]> | 2024-04-11 04:52:44 +0000 |
|---|---|---|
| committer | Yash <[email protected]> | 2024-04-11 04:52:44 +0000 |
| commit | 6dcc7d18c9be5e3a5e0a3ff60668424ee0158b4e (patch) | |
| tree | 179aa936536510cc707368fc7c330c4c7fbdc3f8 | |
| parent | novel editor (diff) | |
| parent | save user ID with url to ensure that same website can be saved by users (diff) | |
| download | supermemory-new-ui.tar.xz supermemory-new-ui.zip | |
Merge branch 'main' of https://github.com/Dhravya/supermemory into new-uinew-ui
88 files changed, 2119 insertions, 2151 deletions
diff --git a/.eslintrc.cjs b/.eslintrc.cjs index d20681f0..728f09f1 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -6,7 +6,7 @@ const config = { "next/core-web-vitals", "plugin:@typescript-eslint/recommended-type-checked", "plugin:@typescript-eslint/stylistic-type-checked", - "plugin:eslint-plugin-next-on-pages/recommended" + "plugin:eslint-plugin-next-on-pages/recommended", ], rules: { // These opinionated rules are enabled in stylistic-type-checked above. diff --git a/.github/actions/buildextension/action.yml b/.github/actions/buildextension/action.yml index b65953d7..ed0d7fa0 100644 --- a/.github/actions/buildextension/action.yml +++ b/.github/actions/buildextension/action.yml @@ -1,7 +1,7 @@ -name: 'Build Chrome extension' -description: 'Builds the chrome extension into a ZIP' +name: "Build Chrome extension" +description: "Builds the chrome extension into a ZIP" runs: - using: 'composite' + using: "composite" steps: - name: Setup Bun uses: oven-sh/setup-bun@v1 @@ -26,4 +26,4 @@ runs: uses: actions/upload-artifact@v3 with: name: extension-${{ github.sha }} - path: apps/extension/dist/extension-${{ github.sha }}.zip
\ No newline at end of file + path: apps/extension/dist/extension-${{ github.sha }}.zip diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c99080ec..9c1f459d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,15 +4,15 @@ on: push: branches: [main] paths: - - 'apps/web/**' - - 'apps/extension/**' - - 'apps/cf-ai-backend/**' + - "apps/web/**" + - "apps/extension/**" + - "apps/cf-ai-backend/**" pull_request: branches: [main] paths: - - 'apps/web/**' - - 'apps/extension/**' - - 'apps/cf-ai-backend/**' + - "apps/web/**" + - "apps/extension/**" + - "apps/cf-ai-backend/**" jobs: build-extension: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bdc9d474..73b074dc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Upload to Web Store on: push: tags: - - '*' + - "*" jobs: build-extension: runs-on: ubuntu-latest @@ -5,6 +5,7 @@ .next .wrangler drizzle/ +dist/ node_modules diff --git a/.husky/_/husky.sh b/.husky/_/husky.sh new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/.husky/_/husky.sh diff --git a/.husky/_/pre-commit b/.husky/_/pre-commit new file mode 100755 index 00000000..921078d7 --- /dev/null +++ b/.husky/_/pre-commit @@ -0,0 +1,3 @@ +#!/bin/sh +. "$(dirname "$0")/husky.sh" +bun run lint-staged
\ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..f3c04e81 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +**/.gitignore
\ No newline at end of file diff --git a/apps/cf-ai-backend/package.json b/apps/cf-ai-backend/package.json index b60ea18b..342fb7ff 100644 --- a/apps/cf-ai-backend/package.json +++ b/apps/cf-ai-backend/package.json @@ -1,15 +1,15 @@ { - "name": "cf-ai-backend", - "version": "0.0.0", - "private": true, - "scripts": { - "deploy": "wrangler deploy", - "dev": "wrangler dev", - "start": "wrangler dev" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20240222.0", - "typescript": "^5.0.4", - "wrangler": "^3.0.0" - } + "name": "cf-ai-backend", + "version": "0.0.0", + "private": true, + "scripts": { + "deploy": "wrangler deploy", + "dev": "wrangler dev", + "start": "wrangler dev" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20240222.0", + "typescript": "^5.0.4", + "wrangler": "^3.0.0" + } } diff --git a/apps/cf-ai-backend/pnpm-lock.yaml b/apps/cf-ai-backend/pnpm-lock.yaml new file mode 100644 index 00000000..5c85a415 --- /dev/null +++ b/apps/cf-ai-backend/pnpm-lock.yaml @@ -0,0 +1,789 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@cloudflare/workers-types': + specifier: ^4.20240222.0 + version: 4.20240222.0 + typescript: + specifier: ^5.0.4 + version: 5.3.3 + wrangler: + specifier: ^3.0.0 + version: 3.29.0(@cloudflare/[email protected]) + +packages: + /@cloudflare/[email protected]: + resolution: { integrity: sha512-lKN2XCfKCmpKb86a1tl4GIwsJYDy9TGuwjhDELLmpKygQhw8X2xR4dusgpC5Tg7q1pB96Eb0rBo81kxSILQMwA== } + dependencies: + mime: 3.0.0 + dev: true + + /@cloudflare/[email protected]: + resolution: { integrity: sha512-64qjsCUz6VtjXnUex5D6dWoJDuUBRw1ps2TEVH9wGJ4ubiLVUxKhj3bzkVy0RoJ8FhaCKzJWWRyTo4yc192UTA== } + engines: { node: '>=16' } + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/[email protected]: + resolution: { integrity: sha512-eVQrAV200LhwLY6JZLx3l2lDrjsTC86lqnvH+RSeM43bAcdneC6lVfykHnTaOTgYFvYQbqRkn9ICWxXj1V9L5g== } + engines: { node: '>=16' } + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/[email protected]: + resolution: { integrity: sha512-ivZ2UuCvi44j8JZ++XlQzSYajt5ptvAdwlh3WPpCcygtHXEh6SVo8QXEUOXhPbv861C0HZMYxLCaLqlpQDWB8g== } + engines: { node: '>=16' } + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/[email protected]: + resolution: { integrity: sha512-aLfvl9kXQKbM7aLvfL0HbOt5VEgv15mEZGyFKyDldJ8+nOXH6nYPma1ccwF8BHmu8otHc420eyPr2xPKhLSJnw== } + engines: { node: '>=16' } + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/[email protected]: + resolution: { integrity: sha512-Y6KMukWnorsSmPx6d82IuJ4SU8sX1+2y+w1uFJ76sucSgXqUAN1fmjG+EyzRVbcbsxRGBCD9c1Pn8T1amMLEYA== } + engines: { node: '>=16' } + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@cloudflare/[email protected]: + resolution: { integrity: sha512-luO0BdK3rLlCv3B240+cTrfqm+XSbHtpk+88aJtGwzyVK9QF/Xz8lBgE/oZZLN8nCTmOvxAZnszyxUuZ8GP8Cg== } + dev: true + + /@cspotcode/[email protected]: + resolution: { integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== } + engines: { node: '>=12' } + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@esbuild-plugins/[email protected]([email protected]): + resolution: { integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw== } + peerDependencies: + esbuild: '*' + dependencies: + esbuild: 0.17.19 + dev: true + + /@esbuild-plugins/[email protected]([email protected]): + resolution: { integrity: sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA== } + peerDependencies: + esbuild: '*' + dependencies: + esbuild: 0.17.19 + escape-string-regexp: 4.0.0 + rollup-plugin-node-polyfills: 0.2.1 + dev: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== } + engines: { node: '>=12' } + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== } + engines: { node: '>=12' } + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== } + engines: { node: '>=12' } + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== } + engines: { node: '>=12' } + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== } + engines: { node: '>=12' } + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== } + engines: { node: '>=12' } + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== } + engines: { node: '>=12' } + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== } + engines: { node: '>=12' } + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== } + engines: { node: '>=12' } + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== } + engines: { node: '>=12' } + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== } + engines: { node: '>=12' } + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== } + engines: { node: '>=12' } + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== } + engines: { node: '>=12' } + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== } + engines: { node: '>=12' } + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== } + engines: { node: '>=12' } + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== } + engines: { node: '>=12' } + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== } + engines: { node: '>=12' } + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== } + engines: { node: '>=12' } + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== } + engines: { node: '>=12' } + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== } + engines: { node: '>=12' } + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== } + engines: { node: '>=12' } + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/[email protected]: + resolution: { integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== } + engines: { node: '>=12' } + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@fastify/[email protected]: + resolution: { integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== } + engines: { node: '>=14' } + dev: true + + /@jridgewell/[email protected]: + resolution: { integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== } + engines: { node: '>=6.0.0' } + dev: true + + /@jridgewell/[email protected]: + resolution: { integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== } + dev: true + + /@jridgewell/[email protected]: + resolution: { integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== } + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@types/[email protected]: + resolution: { integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== } + dependencies: + '@types/node': 20.11.20 + dev: true + + /@types/[email protected]: + resolution: { integrity: sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg== } + dependencies: + undici-types: 5.26.5 + dev: true + + resolution: { integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== } + engines: { node: '>=0.4.0' } + dev: true + + resolution: { integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== } + engines: { node: '>=0.4.0' } + hasBin: true + dev: true + + resolution: { integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== } + engines: { node: '>= 8' } + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + resolution: { integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ== } + dependencies: + printable-characters: 1.0.42 + dev: true + + resolution: { integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== } + engines: { node: '>=8' } + dev: true + + resolution: { integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g== } + dev: true + + resolution: { integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== } + engines: { node: '>=8' } + dependencies: + fill-range: 7.0.1 + dev: true + + resolution: { integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g== } + dependencies: + debug: 4.3.4 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: true + + resolution: { integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== } + engines: { node: '>= 8.10.0' } + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + resolution: { integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== } + engines: { node: '>= 0.6' } + dev: true + + resolution: { integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA== } + dev: true + + resolution: { integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== } + engines: { node: '>=6.0' } + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + resolution: { integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== } + engines: { node: '>=12' } + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + dev: true + + resolution: { integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== } + engines: { node: '>=10' } + dev: true + + resolution: { integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== } + dev: true + + resolution: { integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw== } + engines: { node: '>=6' } + dev: true + + resolution: { integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== } + engines: { node: '>=8' } + dependencies: + to-regex-range: 5.0.1 + dev: true + + resolution: { integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + requiresBuild: true + dev: true + optional: true + + resolution: { integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== } + dev: true + + resolution: { integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w== } + dependencies: + data-uri-to-buffer: 2.0.2 + source-map: 0.6.1 + dev: true + + resolution: { integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== } + engines: { node: '>= 6' } + dependencies: + is-glob: 4.0.3 + dev: true + + resolution: { integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== } + dev: true + + resolution: { integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== } + engines: { node: '>= 0.4' } + dependencies: + function-bind: 1.1.2 + dev: true + + resolution: { integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== } + engines: { node: '>=8' } + dependencies: + binary-extensions: 2.2.0 + dev: true + + resolution: { integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== } + dependencies: + hasown: 2.0.1 + dev: true + + resolution: { integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== } + engines: { node: '>=0.10.0' } + dev: true + + resolution: { integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== } + engines: { node: '>=0.10.0' } + dependencies: + is-extglob: 2.1.1 + dev: true + + resolution: { integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== } + engines: { node: '>=0.12.0' } + dev: true + + resolution: { integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== } + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + resolution: { integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== } + engines: { node: '>=10.0.0' } + hasBin: true + dev: true + + resolution: { integrity: sha512-NnP3MQFh2pV7iETNmJzSlMBF/KhRA+XT4A7JLCfxunadQSPbTMMgbsZo9EfLloMwHMUhZGNVot3Pvh+VnT2joQ== } + engines: { node: '>=16.13' } + hasBin: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + acorn: 8.11.3 + acorn-walk: 8.3.2 + capnp-ts: 0.7.0 + exit-hook: 2.2.1 + glob-to-regexp: 0.4.1 + stoppable: 1.1.0 + undici: 5.28.3 + workerd: 1.20240208.0 + ws: 8.16.0 + youch: 3.3.3 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + resolution: { integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== } + dev: true + + resolution: { integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== } + hasBin: true + dev: true + + resolution: { integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true + dev: true + + resolution: { integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== } + engines: { node: '>= 6.13.0' } + dev: true + + resolution: { integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== } + engines: { node: '>=0.10.0' } + dev: true + + resolution: { integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== } + dev: true + + resolution: { integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== } + dev: true + + resolution: { integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== } + engines: { node: '>=8.6' } + dev: true + + resolution: { integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ== } + dev: true + + resolution: { integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== } + engines: { node: '>=8.10.0' } + dependencies: + picomatch: 2.3.1 + dev: true + + resolution: { integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== } + engines: { node: '>=10' } + dev: true + + resolution: { integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== } + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + resolution: { integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w== } + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject. + dependencies: + estree-walker: 0.6.1 + magic-string: 0.25.9 + rollup-pluginutils: 2.8.2 + dev: true + + resolution: { integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA== } + dependencies: + rollup-plugin-inject: 3.0.2 + dev: true + + resolution: { integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== } + dependencies: + estree-walker: 0.6.1 + dev: true + + resolution: { integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== } + engines: { node: '>=10' } + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 + dev: true + + resolution: { integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== } + engines: { node: '>=0.10.0' } + dev: true + + resolution: { integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== } + deprecated: Please use @jridgewell/sourcemap-codec instead + dev: true + + resolution: { integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw== } + dependencies: + as-table: 1.0.55 + get-source: 2.0.12 + dev: true + + resolution: { integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== } + engines: { node: '>=4', npm: '>=6' } + dev: true + + resolution: { integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== } + engines: { node: '>= 0.4' } + dev: true + + resolution: { integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== } + engines: { node: '>=8.0' } + dependencies: + is-number: 7.0.0 + dev: true + + resolution: { integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== } + dev: true + + resolution: { integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== } + engines: { node: '>=14.17' } + hasBin: true + dev: true + + resolution: { integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== } + dev: true + + resolution: { integrity: sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA== } + engines: { node: '>=14.0' } + dependencies: + '@fastify/busboy': 2.1.0 + dev: true + + resolution: { integrity: sha512-edFdwHU95Ww2SmjBvBJhbc7hhVXMEo6Y7qqSWCl6W9lGScTlCMCXd4AU3f/EGJ3P++FC+CWqu+XuAywebbKF2Q== } + engines: { node: '>=16' } + hasBin: true + requiresBuild: true + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20240208.0 + '@cloudflare/workerd-darwin-arm64': 1.20240208.0 + '@cloudflare/workerd-linux-64': 1.20240208.0 + '@cloudflare/workerd-linux-arm64': 1.20240208.0 + '@cloudflare/workerd-windows-64': 1.20240208.0 + dev: true + + /[email protected](@cloudflare/[email protected]): + resolution: { integrity: sha512-VXUUltM0/fxCF20Z3tH39zpnykDJNPH2lMWI5wA0VmRpuKG0Gffjj5lU2vJaI/PfUCo3q4JErxWcgLezBGnFyA== } + engines: { node: '>=16.17.0' } + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20230914.0 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + dependencies: + '@cloudflare/kv-asset-handler': 0.3.1 + '@cloudflare/workers-types': 4.20240222.0 + '@esbuild-plugins/node-globals-polyfill': 0.2.3([email protected]) + '@esbuild-plugins/node-modules-polyfill': 0.2.2([email protected]) + blake3-wasm: 2.1.5 + chokidar: 3.6.0 + esbuild: 0.17.19 + miniflare: 3.20240208.0 + nanoid: 3.3.7 + path-to-regexp: 6.2.1 + resolve: 1.22.8 + resolve.exports: 2.0.2 + selfsigned: 2.4.1 + source-map: 0.6.1 + xxhash-wasm: 1.0.2 + optionalDependencies: + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + resolution: { integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== } + engines: { node: '>=10.0.0' } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + + resolution: { integrity: sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A== } + dev: true + + resolution: { integrity: sha512-qSFXUk3UZBLfggAW3dJKg0BMblG5biqSF8M34E06o5CSsZtH92u9Hqmj2RzGiHDi64fhe83+4tENFP2DB6t6ZA== } + dependencies: + cookie: 0.5.0 + mustache: 4.2.0 + stacktracey: 2.1.8 + dev: true + + resolution: { integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== } + dev: true diff --git a/apps/cf-ai-backend/src/OpenAIEmbedder.ts b/apps/cf-ai-backend/src/OpenAIEmbedder.ts index 35c36c74..e227d1e3 100644 --- a/apps/cf-ai-backend/src/OpenAIEmbedder.ts +++ b/apps/cf-ai-backend/src/OpenAIEmbedder.ts @@ -1,4 +1,4 @@ -import { AiTextGenerationOutput } from "@cloudflare/ai/dist/ai/tasks/text-generation"; +import { AiTextGenerationOutput } from '@cloudflare/ai/dist/ai/tasks/text-generation'; interface OpenAIEmbeddingsParams { apiKey: string; @@ -15,7 +15,7 @@ export class OpenAIEmbeddings { } async embedDocuments(texts: string[]): Promise<number[][]> { - const responses = await Promise.all(texts.map(text => this.embedQuery(text))); + const responses = await Promise.all(texts.map((text) => this.embedQuery(text))); return responses; } @@ -24,18 +24,18 @@ export class OpenAIEmbeddings { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.apiKey}` + Authorization: `Bearer ${this.apiKey}`, }, body: JSON.stringify({ input: text, - model: this.modelName - }) + model: this.modelName, + }), }); - const data = await response.json() as { + const data = (await response.json()) as { data: { - embedding: number[] - }[] + embedding: number[]; + }[]; }; return data.data[0].embedding; diff --git a/apps/cf-ai-backend/src/index.ts b/apps/cf-ai-backend/src/index.ts index f55c465b..ccaf06bd 100644 --- a/apps/cf-ai-backend/src/index.ts +++ b/apps/cf-ai-backend/src/index.ts @@ -1,15 +1,9 @@ -import type { - VectorizeIndex, - Fetcher, - Request, -} from "@cloudflare/workers-types"; - -import { - CloudflareVectorizeStore, -} from "@langchain/cloudflare"; -import { OpenAIEmbeddings } from "./OpenAIEmbedder"; -import { GoogleGenerativeAI } from "@google/generative-ai"; -import routeMap from "./routes"; +import type { VectorizeIndex, Fetcher, Request } from '@cloudflare/workers-types'; + +import { CloudflareVectorizeStore } from '@langchain/cloudflare'; +import { OpenAIEmbeddings } from './OpenAIEmbedder'; +import { GoogleGenerativeAI } from '@google/generative-ai'; +import routeMap from './routes'; function isAuthorized(request: Request, env: Env): boolean { return request.headers.get('X-Custom-Auth-Key') === env.SECURITY_KEY; @@ -32,7 +26,7 @@ export default { const genAI = new GoogleGenerativeAI(env.GOOGLE_AI_API_KEY); - const model = genAI.getGenerativeModel({ model: "gemini-pro" }); + const model = genAI.getGenerativeModel({ model: 'gemini-pro' }); const url = new URL(request.url); const path = url.pathname; diff --git a/apps/cf-ai-backend/src/routes.ts b/apps/cf-ai-backend/src/routes.ts index 841f107a..a8a3249a 100644 --- a/apps/cf-ai-backend/src/routes.ts +++ b/apps/cf-ai-backend/src/routes.ts @@ -1,14 +1,20 @@ import { CloudflareVectorizeStore } from '@langchain/cloudflare'; import * as apiAdd from './routes/add'; -import * as apiQuery from "./routes/query" -import * as apiAsk from "./routes/ask" -import * as apiChat from "./routes/chat" +import * as apiQuery from './routes/query'; +import * as apiAsk from './routes/ask'; +import * as apiChat from './routes/chat'; import { OpenAIEmbeddings } from './OpenAIEmbedder'; import { GenerativeModel } from '@google/generative-ai'; import { Request } from '@cloudflare/workers-types'; - -type RouteHandler = (request: Request, store: CloudflareVectorizeStore, embeddings: OpenAIEmbeddings, model: GenerativeModel, env: Env, ctx?: ExecutionContext) => Promise<Response>; +type RouteHandler = ( + request: Request, + store: CloudflareVectorizeStore, + embeddings: OpenAIEmbeddings, + model: GenerativeModel, + env: Env, + ctx?: ExecutionContext, +) => Promise<Response>; const routeMap = new Map<string, Record<string, RouteHandler>>(); diff --git a/apps/cf-ai-backend/src/routes/add.ts b/apps/cf-ai-backend/src/routes/add.ts index 9b05e9f0..b9a6da8f 100644 --- a/apps/cf-ai-backend/src/routes/add.ts +++ b/apps/cf-ai-backend/src/routes/add.ts @@ -1,36 +1,38 @@ -import { Request } from "@cloudflare/workers-types"; -import { type CloudflareVectorizeStore } from "@langchain/cloudflare"; +import { Request } from '@cloudflare/workers-types'; +import { type CloudflareVectorizeStore } from '@langchain/cloudflare'; export async function POST(request: Request, store: CloudflareVectorizeStore) { - const body = await request.json() as { - pageContent: string, - title?: string, - description?: string, - space?: string, - url: string, - user: string + const body = (await request.json()) as { + pageContent: string; + title?: string; + description?: string; + space?: string; + url: string; + user: string; }; if (!body.pageContent || !body.url) { - return new Response(JSON.stringify({ message: "Invalid Page Content" }), { status: 400 }); + return new Response(JSON.stringify({ message: 'Invalid Page Content' }), { status: 400 }); } - const newPageContent = `Title: ${body.title}\nDescription: ${body.description}\nURL: ${body.url}\nContent: ${body.pageContent}` + const newPageContent = `Title: ${body.title}\nDescription: ${body.description}\nURL: ${body.url}\nContent: ${body.pageContent}`; - - await store.addDocuments([ - { - pageContent: newPageContent, - metadata: { - title: body.title ?? "", - description: body.description ?? "", - space: body.space ?? "", - url: body.url, - user: body.user, + await store.addDocuments( + [ + { + pageContent: newPageContent, + metadata: { + title: body.title ?? '', + description: body.description ?? '', + space: body.space ?? '', + url: body.url, + user: body.user, + }, }, + ], + { + ids: [`${body.url}-${body.user}`], }, - ], { - ids: [`${body.url}`] - }) + ); - return new Response(JSON.stringify({ message: "Document Added" }), { status: 200 }); + return new Response(JSON.stringify({ message: 'Document Added' }), { status: 200 }); } diff --git a/apps/cf-ai-backend/src/routes/ask.ts b/apps/cf-ai-backend/src/routes/ask.ts index 1c48dde8..267c1513 100644 --- a/apps/cf-ai-backend/src/routes/ask.ts +++ b/apps/cf-ai-backend/src/routes/ask.ts @@ -1,18 +1,18 @@ -import { GenerativeModel } from "@google/generative-ai"; -import { OpenAIEmbeddings } from "../OpenAIEmbedder"; -import { CloudflareVectorizeStore } from "@langchain/cloudflare"; -import { Request } from "@cloudflare/workers-types"; +import { GenerativeModel } from '@google/generative-ai'; +import { OpenAIEmbeddings } from '../OpenAIEmbedder'; +import { CloudflareVectorizeStore } from '@langchain/cloudflare'; +import { Request } from '@cloudflare/workers-types'; export async function POST(request: Request, _: CloudflareVectorizeStore, embeddings: OpenAIEmbeddings, model: GenerativeModel, env?: Env) { - const body = await request.json() as { - query: string + const body = (await request.json()) as { + query: string; }; if (!body.query) { - return new Response(JSON.stringify({ message: "Invalid Page Content" }), { status: 400 }); + return new Response(JSON.stringify({ message: 'Invalid Page Content' }), { status: 400 }); } - const prompt = `You are an agent that answers a question based on the query. don't say 'based on the context'.\n\n Context:\n${body.query} \nAnswer this question based on the context. Question: ${body.query}\nAnswer:` + const prompt = `You are an agent that answers a question based on the query. don't say 'based on the context'.\n\n Context:\n${body.query} \nAnswer this question based on the context. Question: ${body.query}\nAnswer:`; const output = await model.generateContentStream(prompt); const response = new Response( @@ -22,14 +22,14 @@ export async function POST(request: Request, _: CloudflareVectorizeStore, embedd for await (const chunk of output.stream) { const chunkText = await chunk.text(); console.log(chunkText); - const encodedChunk = converter.encode("data: " + JSON.stringify({ "response": chunkText }) + "\n\n"); + const encodedChunk = converter.encode('data: ' + JSON.stringify({ response: chunkText }) + '\n\n'); controller.enqueue(encodedChunk); } - const doneChunk = converter.encode("data: [DONE]"); + const doneChunk = converter.encode('data: [DONE]'); controller.enqueue(doneChunk); controller.close(); - } - }) + }, + }), ); return response; } diff --git a/apps/cf-ai-backend/src/routes/chat.ts b/apps/cf-ai-backend/src/routes/chat.ts index 95788d03..75e298b8 100644 --- a/apps/cf-ai-backend/src/routes/chat.ts +++ b/apps/cf-ai-backend/src/routes/chat.ts @@ -1,28 +1,28 @@ -import { Content, GenerativeModel } from "@google/generative-ai"; -import { OpenAIEmbeddings } from "../OpenAIEmbedder"; -import { CloudflareVectorizeStore } from "@langchain/cloudflare"; -import { Request } from "@cloudflare/workers-types"; +import { Content, GenerativeModel } from '@google/generative-ai'; +import { OpenAIEmbeddings } from '../OpenAIEmbedder'; +import { CloudflareVectorizeStore } from '@langchain/cloudflare'; +import { Request } from '@cloudflare/workers-types'; export async function POST(request: Request, _: CloudflareVectorizeStore, embeddings: OpenAIEmbeddings, model: GenerativeModel, env?: Env) { const queryparams = new URL(request.url).searchParams; - const query = queryparams.get("q"); - const topK = parseInt(queryparams.get("topK") ?? "5"); - const user = queryparams.get("user") - const spaces = queryparams.get("spaces") - const spacesArray = spaces ? spaces.split(",") : undefined + const query = queryparams.get('q'); + const topK = parseInt(queryparams.get('topK') ?? '5'); + const user = queryparams.get('user'); + const spaces = queryparams.get('spaces'); + const spacesArray = spaces ? spaces.split(',') : undefined; - const sourcesOnly = (queryparams.get("sourcesOnly") ?? "false") + const sourcesOnly = queryparams.get('sourcesOnly') ?? 'false'; if (!user) { - return new Response(JSON.stringify({ message: "Invalid User" }), { status: 400 }); + return new Response(JSON.stringify({ message: 'Invalid User' }), { status: 400 }); } if (!query) { - return new Response(JSON.stringify({ message: "Invalid Query" }), { status: 400 }); + return new Response(JSON.stringify({ message: 'Invalid Query' }), { status: 400 }); } const filter: VectorizeVectorMetadataFilter = { - user - } + user, + }; const responses: VectorizeMatches = { matches: [], count: 0 }; @@ -34,12 +34,12 @@ export async function POST(request: Request, _: CloudflareVectorizeStore, embedd const resp = await env!.VECTORIZE_INDEX.query(queryAsVector, { topK, - filter + filter, }); if (resp.count > 0) { - responses.matches.push(...resp.matches) - responses.count += resp.count + responses.matches.push(...resp.matches); + responses.count += resp.count; } } } else { @@ -47,13 +47,13 @@ export async function POST(request: Request, _: CloudflareVectorizeStore, embedd const resp = await env!.VECTORIZE_INDEX.query(queryAsVector, { topK, filter: { - user - } + user, + }, }); if (resp.count > 0) { - responses.matches.push(...resp.matches) - responses.count += resp.count + responses.matches.push(...resp.matches); + responses.count += resp.count; } } @@ -61,27 +61,36 @@ export async function POST(request: Request, _: CloudflareVectorizeStore, embedd // return new Response(JSON.stringify({ message: "No Results Found" }), { status: 404 }); // } - const highScoreIds = responses.matches.filter(({ score }) => score > 0.35).map(({ id }) => id) + const highScoreIds = responses.matches.filter(({ score }) => score > 0.35).map(({ id }) => id); - if (sourcesOnly === "true") { + if (sourcesOnly === 'true') { return new Response(JSON.stringify({ ids: highScoreIds }), { status: 200 }); } - const vec = await env!.VECTORIZE_INDEX.getByIds(highScoreIds) + const vec = await env!.VECTORIZE_INDEX.getByIds(highScoreIds); - const preparedContext = vec.map(({ metadata }) => `Website title: ${metadata!.title}\nDescription: ${metadata!.description}\nURL: ${metadata!.url}\nContent: ${metadata!.text}`).join("\n\n"); + const preparedContext = vec + .map( + ({ metadata }) => + `Website title: ${metadata!.title}\nDescription: ${metadata!.description}\nURL: ${metadata!.url}\nContent: ${metadata!.text}`, + ) + .join('\n\n'); - const body = await request.json() as { - chatHistory?: Content[] + const body = (await request.json()) as { + chatHistory?: Content[]; }; const defaultHistory = [ { - role: "user", - parts: [{ text: `You are an agent that summarizes a page based on the query. don't say 'based on the context'. I expect you to be like a 'Second Brain'. you will be provided with the context (old saved posts) and questions. Answer accordingly. Answer in markdown format` }], + role: 'user', + parts: [ + { + text: `You are an agent that summarizes a page based on the query. don't say 'based on the context'. I expect you to be like a 'Second Brain'. you will be provided with the context (old saved posts) and questions. Answer accordingly. Answer in markdown format`, + }, + ], }, { - role: "model", + role: 'model', parts: [{ text: "Ok, I am a personal assistant, and will act as a second brain to help with user's queries." }], }, ] as Content[]; @@ -100,14 +109,14 @@ export async function POST(request: Request, _: CloudflareVectorizeStore, embedd const converter = new TextEncoder(); for await (const chunk of output.stream) { const chunkText = await chunk.text(); - const encodedChunk = converter.encode("data: " + JSON.stringify({ "response": chunkText }) + "\n\n"); + const encodedChunk = converter.encode('data: ' + JSON.stringify({ response: chunkText }) + '\n\n'); controller.enqueue(encodedChunk); } - const doneChunk = converter.encode("data: [DONE]"); + const doneChunk = converter.encode('data: [DONE]'); controller.enqueue(doneChunk); controller.close(); - } - }) + }, + }), ); return response; } diff --git a/apps/cf-ai-backend/src/routes/query.ts b/apps/cf-ai-backend/src/routes/query.ts index be237d7d..cd5295c5 100644 --- a/apps/cf-ai-backend/src/routes/query.ts +++ b/apps/cf-ai-backend/src/routes/query.ts @@ -1,59 +1,65 @@ -import { GenerativeModel } from "@google/generative-ai"; -import { OpenAIEmbeddings } from "../OpenAIEmbedder"; -import { CloudflareVectorizeStore } from "@langchain/cloudflare"; -import { Request } from "@cloudflare/workers-types"; +import { GenerativeModel } from '@google/generative-ai'; +import { OpenAIEmbeddings } from '../OpenAIEmbedder'; +import { CloudflareVectorizeStore } from '@langchain/cloudflare'; +import { Request } from '@cloudflare/workers-types'; export async function GET(request: Request, _: CloudflareVectorizeStore, embeddings: OpenAIEmbeddings, model: GenerativeModel, env?: Env) { const queryparams = new URL(request.url).searchParams; - const query = queryparams.get("q"); - const topK = parseInt(queryparams.get("topK") ?? "5"); - const user = queryparams.get("user") - const space = queryparams.get("space") + const query = queryparams.get('q'); + const topK = parseInt(queryparams.get('topK') ?? '5'); + const user = queryparams.get('user'); + const space = queryparams.get('space'); - const sourcesOnly = (queryparams.get("sourcesOnly") ?? "false") + const sourcesOnly = queryparams.get('sourcesOnly') ?? 'false'; if (!user) { - return new Response(JSON.stringify({ message: "Invalid User" }), { status: 400 }); + return new Response(JSON.stringify({ message: 'Invalid User' }), { status: 400 }); } if (!query) { - return new Response(JSON.stringify({ message: "Invalid Query" }), { status: 400 }); + return new Response(JSON.stringify({ message: 'Invalid Query' }), { status: 400 }); } const filter: VectorizeVectorMetadataFilter = { - user - } + user, + }; if (space) { - filter.space + filter.space; } const queryAsVector = await embeddings.embedQuery(query); const resp = await env!.VECTORIZE_INDEX.query(queryAsVector, { topK, - filter + filter, }); if (resp.count === 0) { - return new Response(JSON.stringify({ message: "No Results Found" }), { status: 404 }); + return new Response(JSON.stringify({ message: 'No Results Found' }), { status: 404 }); } - const highScoreIds = resp.matches.filter(({ score }) => score > 0.3).map(({ id }) => id) + const highScoreIds = resp.matches.filter(({ score }) => score > 0.3).map(({ id }) => id); - if (sourcesOnly === "true") { + if (sourcesOnly === 'true') { return new Response(JSON.stringify({ ids: highScoreIds }), { status: 200 }); } - const vec = await env!.VECTORIZE_INDEX.getByIds(highScoreIds) + const vec = await env!.VECTORIZE_INDEX.getByIds(highScoreIds); if (vec.length === 0 || !vec[0].metadata) { - return new Response(JSON.stringify({ message: "No Results Found" }), { status: 400 }); + return new Response(JSON.stringify({ message: 'No Results Found' }), { status: 400 }); } - const preparedContext = vec.slice(0, 3).map(({ metadata }) => `Website title: ${metadata!.title}\nDescription: ${metadata!.description}\nURL: ${metadata!.url}\nContent: ${metadata!.text}`).join("\n\n"); + const preparedContext = vec + .slice(0, 3) + .map( + ({ metadata }) => + `Website title: ${metadata!.title}\nDescription: ${metadata!.description}\nURL: ${metadata!.url}\nContent: ${metadata!.text}`, + ) + .join('\n\n'); - const prompt = `You are an agent that summarizes a page based on the query. Be direct and concise, don't say 'based on the context'.\n\n Context:\n${preparedContext} \nAnswer this question based on the context. Question: ${query}\nAnswer:` + const prompt = `You are an agent that summarizes a page based on the query. Be direct and concise, don't say 'based on the context'.\n\n Context:\n${preparedContext} \nAnswer this question based on the context. Question: ${query}\nAnswer:`; const output = await model.generateContentStream(prompt); const response = new Response( @@ -62,14 +68,14 @@ export async function GET(request: Request, _: CloudflareVectorizeStore, embeddi const converter = new TextEncoder(); for await (const chunk of output.stream) { const chunkText = await chunk.text(); - const encodedChunk = converter.encode("data: " + JSON.stringify({ "response": chunkText }) + "\n\n"); + const encodedChunk = converter.encode('data: ' + JSON.stringify({ response: chunkText }) + '\n\n'); controller.enqueue(encodedChunk); } - const doneChunk = converter.encode("data: [DONE]"); + const doneChunk = converter.encode('data: [DONE]'); controller.enqueue(doneChunk); controller.close(); - } - }) + }, + }), ); return response; } diff --git a/apps/extension/.eslintrc.cjs b/apps/extension/.eslintrc.cjs index d6c95379..6e8698b7 100644 --- a/apps/extension/.eslintrc.cjs +++ b/apps/extension/.eslintrc.cjs @@ -2,17 +2,17 @@ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, -} +}; diff --git a/apps/extension/README.md b/apps/extension/README.md index 7619225f..bb156850 100644 --- a/apps/extension/README.md +++ b/apps/extension/README.md @@ -17,15 +17,14 @@ If you are developing a production application, we recommend updating the config export default { // other rules... parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - project: ['./tsconfig.json', './tsconfig.node.json'], + 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 -
\ No newline at end of file diff --git a/apps/extension/components.json b/apps/extension/components.json index 5c081ce9..6ceb01a1 100644 --- a/apps/extension/components.json +++ b/apps/extension/components.json @@ -14,4 +14,4 @@ "components": "src/components", "utils": "src/lib/utils" } -}
\ No newline at end of file +} diff --git a/apps/extension/manifest.json b/apps/extension/manifest.json index 7b7bf460..561ca8fe 100644 --- a/apps/extension/manifest.json +++ b/apps/extension/manifest.json @@ -1,37 +1,35 @@ { - "manifest_version": 3, - "name": "SuperMemory", - "version": "1.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/*", - "<all_urls>" - ] - } - ], - "permissions": [ - "activeTab", - "storage", + "manifest_version": 3, + "name": "SuperMemory", + "version": "1.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/*" - ], - "background": { - "service_worker": "src/background.ts" + "https://anycontext.dhr.wtf/*", + "<all_urls>" + ] } -}
\ No newline at end of file + ], + "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 index 1543535f..241470a8 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -28,4 +28,4 @@ "typescript": "^5.2.2", "vite": "^5.1.4" } -}
\ No newline at end of file +} diff --git a/apps/extension/postcss.config.js b/apps/extension/postcss.config.js index 2e7af2b7..2aa7205d 100644 --- a/apps/extension/postcss.config.js +++ b/apps/extension/postcss.config.js @@ -3,4 +3,4 @@ export default { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/apps/extension/src/App.tsx b/apps/extension/src/App.tsx index 551fb0d0..f563664f 100644 --- a/apps/extension/src/App.tsx +++ b/apps/extension/src/App.tsx @@ -1,9 +1,12 @@ -import { useEffect, useState } from 'react'; -import { z } from 'zod'; -import { userObj } from './types/zods'; -import { getEnv } from './util'; +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"; +const backendUrl = + getEnv() === "development" + ? "http://localhost:3000" + : "https://supermemory.dhr.wtf"; function App() { const [userData, setUserData] = useState<z.infer<typeof userObj> | null>( @@ -11,9 +14,9 @@ function App() { ); const doStuff = () => { - chrome.runtime.sendMessage({ type: 'getJwt' }, (response) => { + chrome.runtime.sendMessage({ type: "getJwt" }, (response) => { const jwt = response.jwt; - const loginButton = document.getElementById('login'); + const loginButton = document.getElementById("login"); if (loginButton) { if (jwt) { @@ -31,7 +34,7 @@ function App() { console.error(d.error); } }); - loginButton.style.display = 'none'; + loginButton.style.display = "none"; } } }); diff --git a/apps/extension/src/SideBar.tsx b/apps/extension/src/SideBar.tsx index 96b1dd4c..07d9b9f5 100644 --- a/apps/extension/src/SideBar.tsx +++ b/apps/extension/src/SideBar.tsx @@ -1,215 +1,64 @@ -import { useEffect, useState } from 'react'; -import { motion } from 'framer-motion'; -import './dist.css'; -import { Input } from './components/ui/input'; -import { Button } from './components/ui/button'; +import { useState } from "react"; + +import "./ext.css"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, -} from './components/ui/tooltip'; +} from "./components/ui/tooltip"; function sendUrlToAPI() { // get the current URL const url = window.location.href; - const blacklist = ['localhost:3000', 'anycontext.dhr.wtf']; + 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'); + 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 }); + chrome.runtime.sendMessage({ type: "urlChange", content, url }); } } -function SideBar({ jwt }: { jwt: string }) { - const [isOpen, setIsOpen] = useState(false); - const [input, setInput] = useState(''); +function SideBar() { const [savedWebsites, setSavedWebsites] = useState<string[]>([]); const [isSendingData, setIsSendingData] = useState(false); - const [toBeParsed, setToBeParsed] = useState(''); - const [aiResponse, setAIResponse] = useState<string>(''); - const [lastQuestion, setLastQuestion] = useState<string>(''); - - const handleStreamData = (newChunk: string) => { - // Append the new chunk to the existing data to be parsed - setToBeParsed((prev) => prev + newChunk); - }; - - const queryApi = async () => { - const content = document.documentElement.innerText; - - setAIResponse(''); - - const query = `Answer this question based on this query: ${input}\n\n${content}`; - - chrome.runtime.sendMessage({ type: 'queryApi', input: query, jwt }); - }; - - useEffect(() => { - if (typeof window === 'undefined') return; - - chrome.runtime.onMessage.addListener((message) => { - if (message.action === 'streamData') { - console.log(message.data); - handleStreamData(message.data); - } else if (message.action === 'streamEnd') { - setLastQuestion(input); - setInput(''); - setToBeParsed(''); - } - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - 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) => { - if (part.startsWith('data: [DONE]data: ')) { - part = part.replace('data: [DONE]data: ', 'data: '); - } - try { - const parsedPart = JSON.parse(part.replace('data: ', '')); // Try to parse the part as JSON - - // If the part is the last one and couldn't be parsed, keep it to accumulate more data - if (index === parts.length - 1 && !parsedPart) { - remainingData = part; - } else if (parsedPart && parsedPart.response) { - // If the part is parsable and has the "response" field, update the AI response state - setAIResponse((prev) => prev + parsedPart.response); - } - } catch (error) { - // If parsing fails and it's not the last part, it's a malformed JSON - if (index !== parts.length - 1) { - console.error('Malformed JSON part: ', part); - } else { - // If it's the last part, it may be incomplete, so keep it - remainingData = part; - } - } - }); - - // Update the toBeParsed state to only contain the unparsed remainder - if (remainingData !== toBeParsed) { - setToBeParsed(remainingData); - } - } catch (error) { - console.error('Error parsing accumulated data: ', error); - } - }; - - // Call the parsing function if there's data to be parsed - if (toBeParsed) { - tryParseAccumulatedData(); - } - }, [toBeParsed]); - return ( <> <TooltipProvider> - {isOpen && ( - <div - onClick={() => setIsOpen(false)} - className="anycontext-overlay" - ></div> - )} - - {isOpen ? ( - <motion.div - initial={{ x: '100%' }} - animate={{ x: 0 }} - exit={{ x: '100%' }} - transition={{ duration: 0.3 }} - className="anycontext-sidebar anycontext-relative" - > - <div className="anycontext-sidebar-content anycontext-text-black anycontext-px-4 anycontext-relative"> - <h1 className="anycontext-header anycontext-font-sans"> - Ask a question to this page - </h1> - - {/* Three buttons as options to choose from */} - {!input && ( - <div className="anycontext-flex anycontext-flex-col anycontext-gap-4 anycontext-mt-8 anycontext-px-4"> - <Button - onClick={async () => { - setInput('Summarise the content of this web page'); - await queryApi(); - }} - className="anycontext-w-full" - > - Summarize content - </Button> - <Button - onClick={async () => { - setInput( - 'Whats the most important points of this page? Answer in points.', - ); - await queryApi(); - }} - className="anycontext-w-full" - > - What's the most important? - </Button> - <Button - onClick={() => { - setIsOpen(false); - sendUrlToAPI(); - setIsSendingData(true); - setTimeout(() => { - setIsSendingData(false); - setSavedWebsites([ - ...savedWebsites, - window.location.href, - ]); - }, 1000); - }} - className="anycontext-w-full anycontext-bg-gradient-to-tr anycontext-from-purple-400 anycontext-to-purple-600" - > - Save to memory - </Button> - </div> - )} - - {lastQuestion && ( - <div className="anycontext-mb-4"> - <h2 className="anycontext-text-lg anycontext-font-semibold"> - {lastQuestion} - </h2> - </div> - )} - <div className="anycontext-px-4 anycontext-max-h-[75vh] anycontext-overflow-y-auto anycontext-mt-8"> - {aiResponse.replace('</s>', '')} - </div> - - <form - onSubmit={async (e) => { - e.preventDefault(); - await queryApi(); - setInput(''); + <div className="anycontext-flex anycontext-flex-col anycontext-gap-2 anycontext-fixed anycontext-bottom-12 anycontext-right-0 anycontext-z-[99999] anycontext-font-sans"> + {/* <Tooltip delayDuration={300}> + <TooltipContent side="left"> + <p>Open Sidebar</p> + </TooltipContent> + </Tooltip> */} + + <Tooltip delayDuration={300}> + <TooltipTrigger + className="anycontext-bg-transparent + anycontext-border-none anycontext-m-0 anycontext-p-0 + " + > + <button + onClick={() => { + sendUrlToAPI(); + setIsSendingData(true); + setTimeout(() => { + setIsSendingData(false); + setSavedWebsites([...savedWebsites, window.location.href]); + }, 1000); }} - className="anycontext-flex anycontext-absolute anycontext-bottom-0 anycontext-w-full anycontext-gap-4 anycontext-justify-between anycontext-px-4" + disabled={savedWebsites.includes(window.location.href)} + className="anycontext-open-button disabled:anycontext-opacity-30 anycontext-bg-transparent + anycontext-border-none anycontext-m-0 anycontext-p-0" > - <Input - value={input} - onChange={(e) => setInput(e.target.value)} - placeholder="Ask anything about this website" - className="anycontext-mb-4 anycontext-text-black anycontext-outline" - /> - <Button onClick={async () => queryApi()}> + {savedWebsites.includes(window.location.href) ? ( <svg xmlns="http://www.w3.org/2000/svg" width="24" @@ -220,102 +69,33 @@ function SideBar({ jwt }: { jwt: string }) { strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" - className="lucide lucide-send-horizontal" + className="lucide lucide-file-check-2" > - <path d="m3 3 3 9-3 9 19-9Z" /> - <path d="M6 12h16" /> + <path d="M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4" /> + <path d="M14 2v4a2 2 0 0 0 2 2h4" /> + <path d="m3 15 2 2 4-4" /> </svg> - </Button> - </form> - </div> - </motion.div> - ) : ( - <div className="anycontext-flex anycontext-flex-col anycontext-gap-2 anycontext-fixed anycontext-bottom-12 anycontext-right-0 anycontext-z-[99999]"> - <Tooltip delayDuration={300}> - <TooltipTrigger> - <button - onClick={() => setIsOpen(true)} - className="anycontext-open-button" - > + ) : ( <svg xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth="2" - strokeLinecap="round" - strokeLinejoin="round" - className="lucide lucide-panel-right-open" + viewBox="0 0 20 20" + fill="currentColor" + className={`anycontext-w-5 anycontext-h-5 ${isSendingData ? "anycontext-animate-spin" : ""}`} > - <rect width="18" height="18" x="3" y="3" rx="2" /> - <path d="M15 3v18" /> - <path d="m10 15-3-3 3-3" /> + <path d="M15.98 1.804a1 1 0 0 0-1.96 0l-.24 1.192a1 1 0 0 1-.784.785l-1.192.238a1 1 0 0 0 0 1.962l1.192.238a1 1 0 0 1 .785.785l.238 1.192a1 1 0 0 0 1.962 0l.238-1.192a1 1 0 0 1 .785-.785l1.192-.238a1 1 0 0 0 0-1.962l-1.192-.238a1 1 0 0 1-.785-.785l-.238-1.192ZM6.949 5.684a1 1 0 0 0-1.898 0l-.683 2.051a1 1 0 0 1-.633.633l-2.051.683a1 1 0 0 0 0 1.898l2.051.684a1 1 0 0 1 .633.632l.683 2.051a1 1 0 0 0 1.898 0l.683-2.051a1 1 0 0 1 .633-.633l2.051-.683a1 1 0 0 0 0-1.898l-2.051-.683a1 1 0 0 1-.633-.633L6.95 5.684ZM13.949 13.684a1 1 0 0 0-1.898 0l-.184.551a1 1 0 0 1-.632.633l-.551.183a1 1 0 0 0 0 1.898l.551.183a1 1 0 0 1 .633.633l.183.551a1 1 0 0 0 1.898 0l.184-.551a1 1 0 0 1 .632-.633l.551-.183a1 1 0 0 0 0-1.898l-.551-.184a1 1 0 0 1-.633-.632l-.183-.551Z" /> </svg> - </button> - </TooltipTrigger> - <TooltipContent side="left"> - <p>Open Sidebar</p> - </TooltipContent> - </Tooltip> - - <Tooltip delayDuration={300}> - <TooltipTrigger> - <button - onClick={() => { - sendUrlToAPI(); - setIsSendingData(true); - setTimeout(() => { - setIsSendingData(false); - setSavedWebsites([ - ...savedWebsites, - window.location.href, - ]); - }, 1000); - }} - disabled={savedWebsites.includes(window.location.href)} - className="anycontext-open-button disabled:anycontext-opacity-30" - > - {savedWebsites.includes(window.location.href) ? ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth="2" - strokeLinecap="round" - strokeLinejoin="round" - className="lucide lucide-file-check-2" - > - <path d="M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4" /> - <path d="M14 2v4a2 2 0 0 0 2 2h4" /> - <path d="m3 15 2 2 4-4" /> - </svg> - ) : ( - <svg - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 20 20" - fill="currentColor" - className={`anycontext-w-5 anycontext-h-5 ${isSendingData ? 'anycontext-animate-spin' : ''}`} - > - <path d="M15.98 1.804a1 1 0 0 0-1.96 0l-.24 1.192a1 1 0 0 1-.784.785l-1.192.238a1 1 0 0 0 0 1.962l1.192.238a1 1 0 0 1 .785.785l.238 1.192a1 1 0 0 0 1.962 0l.238-1.192a1 1 0 0 1 .785-.785l1.192-.238a1 1 0 0 0 0-1.962l-1.192-.238a1 1 0 0 1-.785-.785l-.238-1.192ZM6.949 5.684a1 1 0 0 0-1.898 0l-.683 2.051a1 1 0 0 1-.633.633l-2.051.683a1 1 0 0 0 0 1.898l2.051.684a1 1 0 0 1 .633.632l.683 2.051a1 1 0 0 0 1.898 0l.683-2.051a1 1 0 0 1 .633-.633l2.051-.683a1 1 0 0 0 0-1.898l-2.051-.683a1 1 0 0 1-.633-.633L6.95 5.684ZM13.949 13.684a1 1 0 0 0-1.898 0l-.184.551a1 1 0 0 1-.632.633l-.551.183a1 1 0 0 0 0 1.898l.551.183a1 1 0 0 1 .633.633l.183.551a1 1 0 0 0 1.898 0l.184-.551a1 1 0 0 1 .632-.633l.551-.183a1 1 0 0 0 0-1.898l-.551-.184a1 1 0 0 1-.633-.632l-.183-.551Z" /> - </svg> - )} - </button> - </TooltipTrigger> - <TooltipContent side="left"> - <p> - {savedWebsites.includes(window.location.href) - ? 'Added to memory' - : 'Add to memory'} - </p> - </TooltipContent> - </Tooltip> - </div> - )} + )} + </button> + </TooltipTrigger> + <TooltipContent className="anycontext-p-0" side="left"> + <p className="anycontext-p-0 anycontext-m-0"> + {savedWebsites.includes(window.location.href) + ? "Added to memory" + : "Add to memory"} + </p> + </TooltipContent> + </Tooltip> + </div> </TooltipProvider> </> ); diff --git a/apps/extension/src/background.ts b/apps/extension/src/background.ts index 137849b8..2cf29f40 100644 --- a/apps/extension/src/background.ts +++ b/apps/extension/src/background.ts @@ -1,6 +1,9 @@ import { getEnv } from "./util"; -const backendUrl = getEnv() === "development" ? "http://localhost:3000" : "https://supermemory.dhr.wtf"; +const backendUrl = + getEnv() === "development" + ? "http://localhost:3000" + : "https://supermemory.dhr.wtf"; chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === "getJwt") { @@ -9,9 +12,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { }); return true; - } - - else if (request.type === "urlChange") { + } else if (request.type === "urlChange") { const content = request.content; const url = request.url; @@ -24,15 +25,13 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { fetch(`${backendUrl}/api/store`, { method: "POST", headers: { - "Authorization": `Bearer ${jwt}`, + Authorization: `Bearer ${jwt}`, }, body: JSON.stringify({ pageContent: content, url }), - }).then(ers => console.log(ers.status)) + }).then((ers) => console.log(ers.status)); }); })(); - } - - else if (request.type === "queryApi") { + } else if (request.type === "queryApi") { const input = request.input; const jwt = request.jwt; @@ -40,12 +39,12 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { await fetch(`${backendUrl}/api/ask`, { method: "POST", headers: { - "Authorization": `Bearer ${jwt}`, + Authorization: `Bearer ${jwt}`, }, body: JSON.stringify({ query: input, }), - }).then(async response => { + }).then(async (response) => { if (!response.body) { throw new Error("No response body"); } @@ -59,8 +58,13 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 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 }); + 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" }); diff --git a/apps/extension/src/components/ui/button.tsx b/apps/extension/src/components/ui/button.tsx index cf7b4315..6ca7d07a 100644 --- a/apps/extension/src/components/ui/button.tsx +++ b/apps/extension/src/components/ui/button.tsx @@ -1,22 +1,24 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "../../lib/utils" +import { cn } from "../../lib/utils"; const buttonVariants = cva( "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", + 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", + 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: { @@ -30,27 +32,27 @@ const buttonVariants = cva( variant: "default", size: "default", }, - } -) + }, +); export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> { - asChild?: boolean + asChild?: boolean; } const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" + const Comp = asChild ? Slot : "button"; return ( <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} /> - ) - } -) -Button.displayName = "Button" + ); + }, +); +Button.displayName = "Button"; -export { Button, buttonVariants } +export { Button }; diff --git a/apps/extension/src/components/ui/input.tsx b/apps/extension/src/components/ui/input.tsx index e91bb0ca..4771795a 100644 --- a/apps/extension/src/components/ui/input.tsx +++ b/apps/extension/src/components/ui/input.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from "react"; -import { cn } from "../../lib/utils" +import { cn } from "../../lib/utils"; export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {} @@ -12,14 +12,14 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>( type={type} className={cn( "anycontext-flex anycontext-h-10 anycontext-w-full anycontext-rounded-md anycontext-border anycontext-border-stone-200 anycontext-bg-white anycontext-px-3 anycontext-py-2 anycontext-text-sm anycontext-ring-offset-white file:anycontext-border-0 file:anycontext-bg-transparent file:anycontext-text-sm file:anycontext-font-medium placeholder:anycontext-text-stone-500 focus-visible:anycontext-outline-none focus-visible:anycontext-ring-2 focus-visible:anycontext-ring-stone-950 focus-visible:anycontext-ring-offset-2 disabled:anycontext-cursor-not-allowed disabled:anycontext-opacity-50 dark:anycontext-border-stone-800 dark:anycontext-bg-stone-950 dark:anycontext-ring-offset-stone-950 dark:placeholder:anycontext-text-stone-400 dark:focus-visible:anycontext-ring-stone-300", - className + className, )} ref={ref} {...props} /> - ) - } -) -Input.displayName = "Input" + ); + }, +); +Input.displayName = "Input"; -export { Input } +export { Input }; diff --git a/apps/extension/src/components/ui/tooltip.tsx b/apps/extension/src/components/ui/tooltip.tsx index 364c7ada..12185db5 100644 --- a/apps/extension/src/components/ui/tooltip.tsx +++ b/apps/extension/src/components/ui/tooltip.tsx @@ -1,13 +1,13 @@ -import * as React from "react" -import * as TooltipPrimitive from "@radix-ui/react-tooltip" +import * as React from "react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -import { cn } from "../../lib/utils" +import { cn } from "../../lib/utils"; -const TooltipProvider = TooltipPrimitive.Provider +const TooltipProvider = TooltipPrimitive.Provider; -const Tooltip = TooltipPrimitive.Root +const Tooltip = TooltipPrimitive.Root; -const TooltipTrigger = TooltipPrimitive.Trigger +const TooltipTrigger = TooltipPrimitive.Trigger; const TooltipContent = React.forwardRef< React.ElementRef<typeof TooltipPrimitive.Content>, @@ -18,11 +18,11 @@ const TooltipContent = React.forwardRef< sideOffset={sideOffset} className={cn( "anycontext-z-50 anycontext-overflow-hidden anycontext-rounded-md anycontext-border anycontext-border-stone-200 anycontext-bg-white anycontext-px-3 anycontext-py-1.5 anycontext-text-sm anycontext-text-stone-950 anycontext-shadow-md anycontext-animate-in anycontext-fade-in-0 anycontext-zoom-in-95 data-[state=closed]:anycontext-animate-out data-[state=closed]:anycontext-fade-out-0 data-[state=closed]:anycontext-zoom-out-95 data-[side=bottom]:anycontext-slide-in-from-top-2 data-[side=left]:anycontext-slide-in-from-right-2 data-[side=right]:anycontext-slide-in-from-left-2 data-[side=top]:anycontext-slide-in-from-bottom-2 dark:anycontext-border-stone-800 dark:anycontext-bg-stone-950 dark:anycontext-text-stone-50", - className + className, )} {...props} /> -)) -TooltipContent.displayName = TooltipPrimitive.Content.displayName +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/apps/extension/src/content.tsx b/apps/extension/src/content.tsx index 1e92bf04..d9994dca 100644 --- a/apps/extension/src/content.tsx +++ b/apps/extension/src/content.tsx @@ -1,4 +1,4 @@ -window.addEventListener('message', (event) => { +window.addEventListener("message", (event) => { if (event.source !== window) { return; } @@ -7,13 +7,13 @@ window.addEventListener('message', (event) => { if (jwt) { if ( !( - window.location.hostname === 'localhost' || - window.location.hostname === 'anycontext.dhr.wtf' || - window.location.hostname === 'supermemory.dhr.wtf' + 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', + "JWT is only allowed to be used on localhost or anycontext.dhr.wtf", ); return; } @@ -22,24 +22,24 @@ window.addEventListener('message', (event) => { } }); -const appContainer = document.createElement('div'); -appContainer.id = 'anycontext-app-container'; +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'; +appContainer.style.zIndex = "9999"; -import ReactDOM from 'react-dom/client'; -import SideBar from './SideBar'; +import ReactDOM from "react-dom/client"; +import SideBar from "./SideBar"; // get JWT from local storage -const jwt = chrome.storage.local.get('jwt').then((data) => { +const jwt = chrome.storage.local.get("jwt").then((data) => { return data.jwt; }) as Promise<string>; -jwt.then((jwt) => { +jwt.then(() => { ReactDOM.createRoot( - document.getElementById('anycontext-app-container')!, - ).render(<SideBar jwt={jwt} />); + document.getElementById("anycontext-app-container")!, + ).render(<SideBar />); }); diff --git a/apps/extension/src/dist.css b/apps/extension/src/dist.css deleted file mode 100644 index 5fda8915..00000000 --- a/apps/extension/src/dist.css +++ /dev/null @@ -1,951 +0,0 @@ -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - - border-width: 0; - /* 2 */ - - border-style: solid; - /* 2 */ - - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS -*/ - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, -::before, -::after { - --tw-border-spacing-x: 0; - - --tw-border-spacing-y: 0; - - --tw-translate-x: 0; - - --tw-translate-y: 0; - - --tw-rotate: 0; - - --tw-skew-x: 0; - - --tw-skew-y: 0; - - --tw-scale-x: 1; - - --tw-scale-y: 1; - - --tw-pan-x: ; - - --tw-pan-y: ; - - --tw-pinch-zoom: ; - - --tw-scroll-snap-strictness: proximity; - - --tw-gradient-from-position: ; - - --tw-gradient-via-position: ; - - --tw-gradient-to-position: ; - - --tw-ordinal: ; - - --tw-slashed-zero: ; - - --tw-numeric-figure: ; - - --tw-numeric-spacing: ; - - --tw-numeric-fraction: ; - - --tw-ring-inset: ; - - --tw-ring-offset-width: 0px; - - --tw-ring-offset-color: #fff; - - --tw-ring-color: rgb(59 130 246 / 0.5); - - --tw-ring-offset-shadow: 0 0 #0000; - - --tw-ring-shadow: 0 0 #0000; - - --tw-shadow: 0 0 #0000; - - --tw-shadow-colored: 0 0 #0000; - - --tw-blur: ; - - --tw-brightness: ; - - --tw-contrast: ; - - --tw-grayscale: ; - - --tw-hue-rotate: ; - - --tw-invert: ; - - --tw-saturate: ; - - --tw-sepia: ; - - --tw-drop-shadow: ; - - --tw-backdrop-blur: ; - - --tw-backdrop-brightness: ; - - --tw-backdrop-contrast: ; - - --tw-backdrop-grayscale: ; - - --tw-backdrop-hue-rotate: ; - - --tw-backdrop-invert: ; - - --tw-backdrop-opacity: ; - - --tw-backdrop-saturate: ; - - --tw-backdrop-sepia: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - - --tw-border-spacing-y: 0; - - --tw-translate-x: 0; - - --tw-translate-y: 0; - - --tw-rotate: 0; - - --tw-skew-x: 0; - - --tw-skew-y: 0; - - --tw-scale-x: 1; - - --tw-scale-y: 1; - - --tw-pan-x: ; - - --tw-pan-y: ; - - --tw-pinch-zoom: ; - - --tw-scroll-snap-strictness: proximity; - - --tw-gradient-from-position: ; - - --tw-gradient-via-position: ; - - --tw-gradient-to-position: ; - - --tw-ordinal: ; - - --tw-slashed-zero: ; - - --tw-numeric-figure: ; - - --tw-numeric-spacing: ; - - --tw-numeric-fraction: ; - - --tw-ring-inset: ; - - --tw-ring-offset-width: 0px; - - --tw-ring-offset-color: #fff; - - --tw-ring-color: rgb(59 130 246 / 0.5); - - --tw-ring-offset-shadow: 0 0 #0000; - - --tw-ring-shadow: 0 0 #0000; - - --tw-shadow: 0 0 #0000; - - --tw-shadow-colored: 0 0 #0000; - - --tw-blur: ; - - --tw-brightness: ; - - --tw-contrast: ; - - --tw-grayscale: ; - - --tw-hue-rotate: ; - - --tw-invert: ; - - --tw-saturate: ; - - --tw-sepia: ; - - --tw-drop-shadow: ; - - --tw-backdrop-blur: ; - - --tw-backdrop-brightness: ; - - --tw-backdrop-contrast: ; - - --tw-backdrop-grayscale: ; - - --tw-backdrop-hue-rotate: ; - - --tw-backdrop-invert: ; - - --tw-backdrop-opacity: ; - - --tw-backdrop-saturate: ; - - --tw-backdrop-sepia: ; -} - -.anycontext-fixed { - position: fixed; -} - -.anycontext-absolute { - position: absolute; -} - -.anycontext-relative { - position: relative; -} - -.anycontext-bottom-0 { - bottom: 0px; -} - -.anycontext-bottom-12 { - bottom: 3rem; -} - -.anycontext-right-0 { - right: 0px; -} - -.anycontext-z-50 { - z-index: 50; -} - -.anycontext-z-\[99999\] { - z-index: 99999; -} - -.anycontext-mb-4 { - margin-bottom: 1rem; -} - -.anycontext-mt-8 { - margin-top: 2rem; -} - -.anycontext-flex { - display: flex; -} - -.anycontext-inline-flex { - display: inline-flex; -} - -.anycontext-h-10 { - height: 2.5rem; -} - -.anycontext-h-11 { - height: 2.75rem; -} - -.anycontext-h-5 { - height: 1.25rem; -} - -.anycontext-h-9 { - height: 2.25rem; -} - -.anycontext-max-h-\[75vh\] { - max-height: 75vh; -} - -.anycontext-w-10 { - width: 2.5rem; -} - -.anycontext-w-5 { - width: 1.25rem; -} - -.anycontext-w-full { - width: 100%; -} - -@keyframes anycontext-spin { - to { - transform: rotate(360deg); - } -} - -.anycontext-animate-spin { - animation: anycontext-spin 1s linear infinite; -} - -.anycontext-flex-col { - flex-direction: column; -} - -.anycontext-items-center { - align-items: center; -} - -.anycontext-justify-center { - justify-content: center; -} - -.anycontext-justify-between { - justify-content: space-between; -} - -.anycontext-gap-2 { - gap: 0.5rem; -} - -.anycontext-gap-4 { - gap: 1rem; -} - -.anycontext-overflow-hidden { - overflow: hidden; -} - -.anycontext-overflow-y-auto { - overflow-y: auto; -} - -.anycontext-whitespace-nowrap { - white-space: nowrap; -} - -.anycontext-rounded-md { - border-radius: 0.375rem; -} - -.anycontext-border { - border-width: 1px; -} - -.anycontext-border-stone-200 { - --tw-border-opacity: 1; - - border-color: rgb(231 229 228 / var(--tw-border-opacity)); -} - -.anycontext-bg-red-500 { - --tw-bg-opacity: 1; - - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); -} - -.anycontext-bg-stone-100 { - --tw-bg-opacity: 1; - - background-color: rgb(245 245 244 / var(--tw-bg-opacity)); -} - -.anycontext-bg-stone-900 { - --tw-bg-opacity: 1; - - background-color: rgb(28 25 23 / var(--tw-bg-opacity)); -} - -.anycontext-bg-white { - --tw-bg-opacity: 1; - - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - -.anycontext-bg-gradient-to-tr { - background-image: linear-gradient(to top right, var(--tw-gradient-stops)); -} - -.anycontext-from-purple-400 { - --tw-gradient-from: #c084fc var(--tw-gradient-from-position); - - --tw-gradient-to: rgb(192 132 252 / 0) var(--tw-gradient-to-position); - - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - -.anycontext-to-purple-600 { - --tw-gradient-to: #9333ea var(--tw-gradient-to-position); -} - -.anycontext-px-3 { - padding-left: 0.75rem; - - padding-right: 0.75rem; -} - -.anycontext-px-4 { - padding-left: 1rem; - - padding-right: 1rem; -} - -.anycontext-px-8 { - padding-left: 2rem; - - padding-right: 2rem; -} - -.anycontext-py-1 { - padding-top: 0.25rem; - - padding-bottom: 0.25rem; -} - -.anycontext-py-1\.5 { - padding-top: 0.375rem; - - padding-bottom: 0.375rem; -} - -.anycontext-py-2 { - padding-top: 0.5rem; - - padding-bottom: 0.5rem; -} - -.anycontext-font-sans { - font-family: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', - 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; -} - -.anycontext-text-sm { - font-size: 0.875rem; - - line-height: 1.25rem; -} - -.anycontext-text-lg { - font-size: 1.125rem; - - line-height: 1.75rem; -} - -.anycontext-font-medium { - font-weight: 500; -} - -.anycontext-font-semibold { - font-weight: 600; -} - -.anycontext-text-black { - --tw-text-opacity: 1; - - color: rgb(0 0 0 / var(--tw-text-opacity)); -} - -.anycontext-text-stone-50 { - --tw-text-opacity: 1; - - color: rgb(250 250 249 / var(--tw-text-opacity)); -} - -.anycontext-text-stone-900 { - --tw-text-opacity: 1; - - color: rgb(28 25 23 / var(--tw-text-opacity)); -} - -.anycontext-text-stone-950 { - --tw-text-opacity: 1; - - color: rgb(12 10 9 / var(--tw-text-opacity)); -} - -.anycontext-underline-offset-4 { - text-underline-offset: 4px; -} - -.anycontext-shadow-md { - --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - - --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), - 0 2px 4px -2px var(--tw-shadow-color); - - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), - var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} - -.anycontext-outline { - outline-style: solid; -} - -.anycontext-ring-offset-white { - --tw-ring-offset-color: #fff; -} - -.anycontext-transition-colors { - transition-property: color, background-color, border-color, - text-decoration-color, fill, stroke; - - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - - transition-duration: 150ms; -} - -@keyframes enter { - from { - opacity: var(--tw-enter-opacity, 1); - - transform: translate3d( - var(--tw-enter-translate-x, 0), - var(--tw-enter-translate-y, 0), - 0 - ) - scale3d( - var(--tw-enter-scale, 1), - var(--tw-enter-scale, 1), - var(--tw-enter-scale, 1) - ) - rotate(var(--tw-enter-rotate, 0)); - } -} - -@keyframes exit { - to { - opacity: var(--tw-exit-opacity, 1); - - transform: translate3d( - var(--tw-exit-translate-x, 0), - var(--tw-exit-translate-y, 0), - 0 - ) - scale3d( - var(--tw-exit-scale, 1), - var(--tw-exit-scale, 1), - var(--tw-exit-scale, 1) - ) - rotate(var(--tw-exit-rotate, 0)); - } -} - -.anycontext-animate-in { - animation-name: enter; - - animation-duration: 150ms; - - --tw-enter-opacity: initial; - - --tw-enter-scale: initial; - - --tw-enter-rotate: initial; - - --tw-enter-translate-x: initial; - - --tw-enter-translate-y: initial; -} - -.anycontext-fade-in-0 { - --tw-enter-opacity: 0; -} - -.anycontext-zoom-in-95 { - --tw-enter-scale: 0.95; -} - -/* ext.css - Assuming this is your CSS file */ - -.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: #7257ff; - - 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 */ -} - -.file\:anycontext-border-0::file-selector-button { - border-width: 0px; -} - -.file\:anycontext-bg-transparent::file-selector-button { - background-color: transparent; -} - -.file\:anycontext-text-sm::file-selector-button { - font-size: 0.875rem; - - line-height: 1.25rem; -} - -.file\:anycontext-font-medium::file-selector-button { - font-weight: 500; -} - -.placeholder\:anycontext-text-stone-500::-moz-placeholder { - --tw-text-opacity: 1; - - color: rgb(120 113 108 / var(--tw-text-opacity)); -} - -.placeholder\:anycontext-text-stone-500::placeholder { - --tw-text-opacity: 1; - - color: rgb(120 113 108 / var(--tw-text-opacity)); -} - -.hover\:anycontext-bg-red-500\/90:hover { - background-color: rgb(239 68 68 / 0.9); -} - -.hover\:anycontext-bg-stone-100:hover { - --tw-bg-opacity: 1; - - background-color: rgb(245 245 244 / var(--tw-bg-opacity)); -} - -.hover\:anycontext-bg-stone-100\/80:hover { - background-color: rgb(245 245 244 / 0.8); -} - -.hover\:anycontext-bg-stone-900\/90:hover { - background-color: rgb(28 25 23 / 0.9); -} - -.hover\:anycontext-text-stone-900:hover { - --tw-text-opacity: 1; - - color: rgb(28 25 23 / var(--tw-text-opacity)); -} - -.hover\:anycontext-underline:hover { - text-decoration-line: underline; -} - -.focus-visible\:anycontext-outline-none:focus-visible { - outline: 2px solid transparent; - - outline-offset: 2px; -} - -.focus-visible\:anycontext-ring-2:focus-visible { - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 - var(--tw-ring-offset-width) var(--tw-ring-offset-color); - - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 - calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), - var(--tw-shadow, 0 0 #0000); -} - -.focus-visible\:anycontext-ring-stone-950:focus-visible { - --tw-ring-opacity: 1; - - --tw-ring-color: rgb(12 10 9 / var(--tw-ring-opacity)); -} - -.focus-visible\:anycontext-ring-offset-2:focus-visible { - --tw-ring-offset-width: 2px; -} - -.disabled\:anycontext-pointer-events-none:disabled { - pointer-events: none; -} - -.disabled\:anycontext-cursor-not-allowed:disabled { - cursor: not-allowed; -} - -.disabled\:anycontext-opacity-30:disabled { - opacity: 0.3; -} - -.disabled\:anycontext-opacity-50:disabled { - opacity: 0.5; -} - -.data-\[state\=closed\]\:anycontext-animate-out[data-state='closed'] { - animation-name: exit; - - animation-duration: 150ms; - - --tw-exit-opacity: initial; - - --tw-exit-scale: initial; - - --tw-exit-rotate: initial; - - --tw-exit-translate-x: initial; - - --tw-exit-translate-y: initial; -} - -.data-\[state\=closed\]\:anycontext-fade-out-0[data-state='closed'] { - --tw-exit-opacity: 0; -} - -.data-\[state\=closed\]\:anycontext-zoom-out-95[data-state='closed'] { - --tw-exit-scale: 0.95; -} - -.data-\[side\=bottom\]\:anycontext-slide-in-from-top-2[data-side='bottom'] { - --tw-enter-translate-y: -0.5rem; -} - -.data-\[side\=left\]\:anycontext-slide-in-from-right-2[data-side='left'] { - --tw-enter-translate-x: 0.5rem; -} - -.data-\[side\=right\]\:anycontext-slide-in-from-left-2[data-side='right'] { - --tw-enter-translate-x: -0.5rem; -} - -.data-\[side\=top\]\:anycontext-slide-in-from-bottom-2[data-side='top'] { - --tw-enter-translate-y: 0.5rem; -} - -:is(.anycontext-dark .dark\:anycontext-border-stone-800) { - --tw-border-opacity: 1; - - border-color: rgb(41 37 36 / var(--tw-border-opacity)); -} - -:is(.anycontext-dark .dark\:anycontext-bg-red-900) { - --tw-bg-opacity: 1; - - background-color: rgb(127 29 29 / var(--tw-bg-opacity)); -} - -:is(.anycontext-dark .dark\:anycontext-bg-stone-50) { - --tw-bg-opacity: 1; - - background-color: rgb(250 250 249 / var(--tw-bg-opacity)); -} - -:is(.anycontext-dark .dark\:anycontext-bg-stone-800) { - --tw-bg-opacity: 1; - - background-color: rgb(41 37 36 / var(--tw-bg-opacity)); -} - -:is(.anycontext-dark .dark\:anycontext-bg-stone-950) { - --tw-bg-opacity: 1; - - background-color: rgb(12 10 9 / var(--tw-bg-opacity)); -} - -:is(.anycontext-dark .dark\:anycontext-text-stone-50) { - --tw-text-opacity: 1; - - color: rgb(250 250 249 / var(--tw-text-opacity)); -} - -:is(.anycontext-dark .dark\:anycontext-text-stone-900) { - --tw-text-opacity: 1; - - color: rgb(28 25 23 / var(--tw-text-opacity)); -} - -:is(.anycontext-dark .dark\:anycontext-ring-offset-stone-950) { - --tw-ring-offset-color: #0c0a09; -} - -:is( - .anycontext-dark .dark\:placeholder\:anycontext-text-stone-400 - )::-moz-placeholder { - --tw-text-opacity: 1; - - color: rgb(168 162 158 / var(--tw-text-opacity)); -} - -:is( - .anycontext-dark .dark\:placeholder\:anycontext-text-stone-400 - )::placeholder { - --tw-text-opacity: 1; - - color: rgb(168 162 158 / var(--tw-text-opacity)); -} - -:is(.anycontext-dark .dark\:hover\:anycontext-bg-red-900\/90:hover) { - background-color: rgb(127 29 29 / 0.9); -} - -:is(.anycontext-dark .dark\:hover\:anycontext-bg-stone-50\/90:hover) { - background-color: rgb(250 250 249 / 0.9); -} - -:is(.anycontext-dark .dark\:hover\:anycontext-bg-stone-800:hover) { - --tw-bg-opacity: 1; - - background-color: rgb(41 37 36 / var(--tw-bg-opacity)); -} - -:is(.anycontext-dark .dark\:hover\:anycontext-bg-stone-800\/80:hover) { - background-color: rgb(41 37 36 / 0.8); -} - -:is(.anycontext-dark .dark\:hover\:anycontext-text-stone-50:hover) { - --tw-text-opacity: 1; - - color: rgb(250 250 249 / var(--tw-text-opacity)); -} - -:is( - .anycontext-dark - .dark\:focus-visible\:anycontext-ring-stone-300:focus-visible - ) { - --tw-ring-opacity: 1; - - --tw-ring-color: rgb(214 211 209 / var(--tw-ring-opacity)); -} diff --git a/apps/extension/src/ext.css b/apps/extension/src/ext.css index f5f73fa6..0583faad 100644 --- a/apps/extension/src/ext.css +++ b/apps/extension/src/ext.css @@ -2,7 +2,6 @@ @tailwind components; @tailwind utilities; -/* ext.css - Assuming this is your CSS file */ .anycontext-overlay { position: fixed; top: 0; @@ -53,7 +52,7 @@ .anycontext-open-button { color: white; - background-color: #7257ff; + background-color: #7257ff50; /* bg-indigo-600 */ background-opacity: 75%; cursor: pointer; padding: 8px; /* px-4 py-2 */ @@ -68,7 +67,6 @@ font-weight: 600; /* font-semibold */ font-size: 1.25rem; /* text-xl */ color: black; - } .anycontext-icon { diff --git a/apps/extension/src/lib/utils.ts b/apps/extension/src/lib/utils.ts index d084ccad..365058ce 100644 --- a/apps/extension/src/lib/utils.ts +++ b/apps/extension/src/lib/utils.ts @@ -1,6 +1,6 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } diff --git a/apps/extension/src/main.tsx b/apps/extension/src/main.tsx index e63eef4a..b5c00920 100644 --- a/apps/extension/src/main.tsx +++ b/apps/extension/src/main.tsx @@ -1,9 +1,9 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.tsx' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.tsx"; -ReactDOM.createRoot(document.getElementById('root')!).render( +ReactDOM.createRoot(document.getElementById("root")!).render( <React.StrictMode> <App /> </React.StrictMode>, -) +); diff --git a/apps/extension/src/types/zods.ts b/apps/extension/src/types/zods.ts index 35f9ef87..3316aa16 100644 --- a/apps/extension/src/types/zods.ts +++ b/apps/extension/src/types/zods.ts @@ -1,4 +1,4 @@ -import { z } from "zod" +import { z } from "zod"; export const userObj = z.object({ message: z.string(), @@ -6,14 +6,14 @@ export const userObj = z.object({ session: z.object({ sessionToken: z.string(), userId: z.string(), - expires: 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() - }) - }) -})
\ No newline at end of file + image: z.string().nullable().optional(), + }), + }), +}); diff --git a/apps/extension/src/util.ts b/apps/extension/src/util.ts index b1085edd..d2ea35d3 100644 --- a/apps/extension/src/util.ts +++ b/apps/extension/src/util.ts @@ -1,13 +1,13 @@ export const getEnv = () => { - // chrome.management.getSelf((self) => { - // if (self.installType === 'development') { - // return "development" - // } - // else { - // return "production" - // } - // }) + // chrome.management.getSelf((self) => { + // if (self.installType === 'development') { + // return "development" + // } + // else { + // return "production" + // } + // }) - // return null - return 'development' -}
\ No newline at end of file + // return null + return "production"; +}; diff --git a/apps/extension/tailwind.config.js b/apps/extension/tailwind.config.js index fa014b43..84c01958 100644 --- a/apps/extension/tailwind.config.js +++ b/apps/extension/tailwind.config.js @@ -1,13 +1,13 @@ -import tailwindcssAnimate from 'tailwindcss-animate'; +import tailwindcssAnimate from "tailwindcss-animate"; /** @type {import('tailwindcss').Config} */ export default { darkMode: ["class"], content: [ - './pages/**/*.{ts,tsx}', - './components/**/*.{ts,tsx}', - './app/**/*.{ts,tsx}', - './src/**/*.{ts,tsx}', - 'index.html' + "./pages/**/*.{ts,tsx}", + "./components/**/*.{ts,tsx}", + "./app/**/*.{ts,tsx}", + "./src/**/*.{ts,tsx}", + "index.html", ], prefix: "anycontext-", theme: { @@ -36,4 +36,7 @@ export default { }, }, plugins: [tailwindcssAnimate], -}
\ No newline at end of file + corePlugins: { + preflight: false, + }, +}; diff --git a/apps/extension/vite.config.ts b/apps/extension/vite.config.ts index 6a2e7d5e..c2b53f80 100644 --- a/apps/extension/vite.config.ts +++ b/apps/extension/vite.config.ts @@ -1,29 +1,27 @@ -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' +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 } = { +const viteManifestHackIssue846: Plugin & { + renderCrxManifest: (manifest: unknown, bundle: any) => void; +} = { // Workaround from https://github.com/crxjs/chrome-extension-tools/issues/846#issuecomment-1861880919. - name: 'manifestHackIssue846', + name: "manifestHackIssue846", renderCrxManifest(_manifest, bundle) { - bundle['manifest.json'] = bundle['.vite/manifest.json'] - bundle['manifest.json'].fileName = 'manifest.json' - delete bundle['.vite/manifest.json'] + 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 - ], + plugins: [react(), crx({ manifest }), viteManifestHackIssue846], resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, -})
\ No newline at end of file +}); diff --git a/apps/web/.eslintrc.json b/apps/web/.eslintrc.json index d8649b49..abd7bea7 100644 --- a/apps/web/.eslintrc.json +++ b/apps/web/.eslintrc.json @@ -3,7 +3,5 @@ "next/core-web-vitals", "plugin:eslint-plugin-next-on-pages/recommended" ], - "plugins": [ - "eslint-plugin-next-on-pages" - ] -}
\ No newline at end of file + "plugins": ["eslint-plugin-next-on-pages"] +} diff --git a/apps/web/README.md b/apps/web/README.md index c81448e4..5164b159 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -19,11 +19,12 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the ## Cloudflare integration Besides the `dev` script mentioned above `c3` has added a few extra scripts that allow you to integrate the application with the [Cloudflare Pages](https://pages.cloudflare.com/) environment, these are: - - `pages:build` to build the application for Pages using the [`@cloudflare/next-on-pages`](https://github.com/cloudflare/next-on-pages) CLI - - `preview` to locally preview your Pages application using the [Wrangler](https://developers.cloudflare.com/workers/wrangler/) CLI - - `deploy` to deploy your Pages application using the [Wrangler](https://developers.cloudflare.com/workers/wrangler/) CLI -> __Note:__ while the `dev` script is optimal for local development you should preview your Pages application as well (periodically or before deployments) in order to make sure that it can properly work in the Pages environment (for more details see the [`@cloudflare/next-on-pages` recommended workflow](https://github.com/cloudflare/next-on-pages/blob/05b6256/internal-packages/next-dev/README.md#recommended-workflow)) +- `pages:build` to build the application for Pages using the [`@cloudflare/next-on-pages`](https://github.com/cloudflare/next-on-pages) CLI +- `preview` to locally preview your Pages application using the [Wrangler](https://developers.cloudflare.com/workers/wrangler/) CLI +- `deploy` to deploy your Pages application using the [Wrangler](https://developers.cloudflare.com/workers/wrangler/) CLI + +> **Note:** while the `dev` script is optimal for local development you should preview your Pages application as well (periodically or before deployments) in order to make sure that it can properly work in the Pages environment (for more details see the [`@cloudflare/next-on-pages` recommended workflow](https://github.com/cloudflare/next-on-pages/blob/05b6256/internal-packages/next-dev/README.md#recommended-workflow)) ### Bindings @@ -35,13 +36,14 @@ You can use bindings during development, when previewing locally your applicatio - To use bindings in the preview mode you need to add them to the `pages:preview` script accordingly to the `wrangler pages dev` command. For more details see its [documentation](https://developers.cloudflare.com/workers/wrangler/commands/#dev-1) or the [Pages Bindings documentation](https://developers.cloudflare.com/pages/functions/bindings/). -- To use bindings in the deployed application you will need to configure them in the Cloudflare [dashboard](https://dash.cloudflare.com/). For more details see the [Pages Bindings documentation](https://developers.cloudflare.com/pages/functions/bindings/). +- To use bindings in the deployed application you will need to configure them in the Cloudflare [dashboard](https://dash.cloudflare.com/). For more details see the [Pages Bindings documentation](https://developers.cloudflare.com/pages/functions/bindings/). #### KV Example `c3` has added for you an example showing how you can use a KV binding. In order to enable the example: + - Search for javascript/typescript lines containing the following comment: ```ts // KV Example: @@ -65,4 +67,4 @@ In order to enable the example: After doing this you can run the `dev` or `preview` script and visit the `/api/hello` route to see the example in action. -Finally, if you also want to see the example work in the deployed application make sure to add a `MY_KV_NAMESPACE` binding to your Pages application in its [dashboard kv bindings settings section](https://dash.cloudflare.com/?to=/:account/pages/view/:pages-project/settings/functions#kv_namespace_bindings_section). After having configured it make sure to re-deploy your application.
\ No newline at end of file +Finally, if you also want to see the example work in the deployed application make sure to add a `MY_KV_NAMESPACE` binding to your Pages application in its [dashboard kv bindings settings section](https://dash.cloudflare.com/?to=/:account/pages/view/:pages-project/settings/functions#kv_namespace_bindings_section). After having configured it make sure to re-deploy your application. diff --git a/apps/web/components.json b/apps/web/components.json index 483d0a6e..c9b7b380 100644 --- a/apps/web/components.json +++ b/apps/web/components.json @@ -1,16 +1,16 @@ { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "tailwind.config.ts", - "css": "src/app/globals.css", - "baseColor": "gray", - "cssVariables": false - }, - "aliases": { - "utils": "@/lib/utils", - "components": "@/components" - } - }
\ No newline at end of file + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app/globals.css", + "baseColor": "gray", + "cssVariables": false + }, + "aliases": { + "utils": "@/lib/utils", + "components": "@/components" + } +} diff --git a/apps/web/drizzle.config.ts b/apps/web/drizzle.config.ts index fd2800e4..bacbf9b6 100644 --- a/apps/web/drizzle.config.ts +++ b/apps/web/drizzle.config.ts @@ -7,5 +7,5 @@ export default { wranglerConfigPath: "./wrangler.toml", dbName: "dev-d1-anycontext", }, - out: 'drizzle' + out: "drizzle", } satisfies Config; diff --git a/apps/web/env.d.ts b/apps/web/env.d.ts index 1d23e2bc..ece89e76 100644 --- a/apps/web/env.d.ts +++ b/apps/web/env.d.ts @@ -1,12 +1,12 @@ declare global { - namespace NodeJS { - interface ProcessEnv { - [key: string]: string | undefined; - DATABASE: D1Database; - VECTORIZE_INDEX: VectorizeIndex; - AI: any - } + namespace NodeJS { + interface ProcessEnv { + [key: string]: string | undefined; + DATABASE: D1Database; + VECTORIZE_INDEX: VectorizeIndex; + AI: any; } } - - export { };
\ No newline at end of file +} + +export {}; diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 3713aeab..b0c1476c 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -1,9 +1,9 @@ -import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev'; +import { setupDevPlatform } from "@cloudflare/next-on-pages/next-dev"; // Here we use the @cloudflare/next-on-pages next-dev module to allow us to use bindings during local development // (when running the application with `next dev`), for more information see: // https://github.com/cloudflare/next-on-pages/blob/5712c57ea7/internal-packages/next-dev/README.md -if (process.env.NODE_ENV === 'development') { +if (process.env.NODE_ENV === "development") { await setupDevPlatform(); } diff --git a/apps/web/package.json b/apps/web/package.json index 7ca13deb..be20fba7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -51,4 +51,4 @@ "vercel": "^33.6.2", "wrangler": "^3.41.0" } -}
\ No newline at end of file +} diff --git a/apps/web/public/icons/black_without_bg.png b/apps/web/public/icons/black_without_bg.png Binary files differnew file mode 100644 index 00000000..1bd7582d --- /dev/null +++ b/apps/web/public/icons/black_without_bg.png diff --git a/apps/web/public/brain.png b/apps/web/public/icons/brain-icon.png Binary files differindex cebe4095..cebe4095 100644 --- a/apps/web/public/brain.png +++ b/apps/web/public/icons/brain-icon.png diff --git a/apps/web/public/icons/logo_bw_without_bg.png b/apps/web/public/icons/logo_bw_without_bg.png Binary files differnew file mode 100644 index 00000000..167b9aea --- /dev/null +++ b/apps/web/public/icons/logo_bw_without_bg.png diff --git a/apps/web/public/icons/logo_without_bg.png b/apps/web/public/icons/logo_without_bg.png Binary files differnew file mode 100644 index 00000000..54313f60 --- /dev/null +++ b/apps/web/public/icons/logo_without_bg.png diff --git a/apps/web/public/icons/white_without_bg.png b/apps/web/public/icons/white_without_bg.png Binary files differnew file mode 100644 index 00000000..4f7d8d42 --- /dev/null +++ b/apps/web/public/icons/white_without_bg.png diff --git a/apps/web/public/icons/wordmark.png b/apps/web/public/icons/wordmark.png Binary files differnew file mode 100644 index 00000000..6fb4ee7b --- /dev/null +++ b/apps/web/public/icons/wordmark.png diff --git a/apps/web/public/next.svg b/apps/web/public/next.svg deleted file mode 100644 index 5174b28c..00000000 --- a/apps/web/public/next.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
\ No newline at end of file diff --git a/apps/web/public/vercel.svg b/apps/web/public/vercel.svg deleted file mode 100644 index d2f84222..00000000 --- a/apps/web/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
\ No newline at end of file diff --git a/apps/web/src/app/MessagePoster.tsx b/apps/web/src/app/MessagePoster.tsx index 64dc89fd..9b7011a8 100644 --- a/apps/web/src/app/MessagePoster.tsx +++ b/apps/web/src/app/MessagePoster.tsx @@ -1,21 +1,21 @@ -'use client'; +"use client"; -import { useEffect } from 'react'; +import { useEffect } from "react"; function MessagePoster({ jwt }: { jwt: string }) { useEffect(() => { - if (typeof window === 'undefined') return; - window.postMessage({ jwt }, '*'); + if (typeof window === "undefined") return; + window.postMessage({ jwt }, "*"); }, [jwt]); return ( <button onClick={() => { - if (typeof window === 'undefined') return; - window.postMessage({ jwt }, '*'); + if (typeof window === "undefined") return; + window.postMessage({ jwt }, "*"); }} > - Send message + Extension Auth </button> ); } diff --git a/apps/web/src/app/api/ask/route.ts b/apps/web/src/app/api/ask/route.ts index cad7a671..89123ac9 100644 --- a/apps/web/src/app/api/ask/route.ts +++ b/apps/web/src/app/api/ask/route.ts @@ -7,42 +7,62 @@ import { env } from "@/env"; export const runtime = "edge"; export async function POST(req: NextRequest) { - const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", ""); + const token = + req.cookies.get("next-auth.session-token")?.value ?? + req.cookies.get("__Secure-authjs.session-token")?.value ?? + req.cookies.get("authjs.session-token")?.value ?? + req.headers.get("Authorization")?.replace("Bearer ", ""); - const sessionData = await db.select().from(sessions).where(eq(sessions.sessionToken, token!)) + const sessionData = await db + .select() + .from(sessions) + .where(eq(sessions.sessionToken, token!)); - if (!sessionData || sessionData.length === 0) { - return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 }); - } + if (!sessionData || sessionData.length === 0) { + return new Response( + JSON.stringify({ message: "Invalid Key, session not found." }), + { status: 404 }, + ); + } - const user = await db.select().from(users).where(eq(users.id, sessionData[0].userId)).limit(1) + const user = await db + .select() + .from(users) + .where(eq(users.id, sessionData[0].userId)) + .limit(1); - if (!user || user.length === 0) { - return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 }); - } + if (!user || user.length === 0) { + return NextResponse.json( + { message: "Invalid Key, session not found." }, + { status: 404 }, + ); + } - const body = await req.json() as { - query: string; - } + const body = (await req.json()) as { + query: string; + }; - const resp = await fetch(`https://cf-ai-backend.dhravya.workers.dev/ask`, { - headers: { - "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY, - }, - method: "POST", - body: JSON.stringify({ - query: body.query, - }), - }) + const resp = await fetch(`https://cf-ai-backend.dhravya.workers.dev/ask`, { + headers: { + "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY, + }, + method: "POST", + body: JSON.stringify({ + query: body.query, + }), + }); - if (resp.status !== 200 || !resp.ok) { - const errorData = await resp.json(); - return new Response(JSON.stringify({ message: "Error in CF function", error: errorData }), { status: resp.status }); - } + if (resp.status !== 200 || !resp.ok) { + const errorData = await resp.json(); + return new Response( + JSON.stringify({ message: "Error in CF function", error: errorData }), + { status: resp.status }, + ); + } - // Stream the response back to the client - const { readable, writable } = new TransformStream(); - resp && resp.body!.pipeTo(writable); + // Stream the response back to the client + const { readable, writable } = new TransformStream(); + resp && resp.body!.pipeTo(writable); - return new Response(readable, { status: 200 }); -}
\ No newline at end of file + return new Response(readable, { status: 200 }); +} diff --git a/apps/web/src/app/api/chat/route.ts b/apps/web/src/app/api/chat/route.ts index aec5b0ea..bc7a4ee4 100644 --- a/apps/web/src/app/api/chat/route.ts +++ b/apps/web/src/app/api/chat/route.ts @@ -8,60 +8,85 @@ import { ChatHistory } from "../../../../types/memory"; export const runtime = "edge"; export async function POST(req: NextRequest) { - const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", ""); - - const sessionData = await db.select().from(sessions).where(eq(sessions.sessionToken, token!)) - - if (!sessionData || sessionData.length === 0) { - return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 }); - } - - const user = await db.select().from(users).where(eq(users.id, sessionData[0].userId)).limit(1) - - if (!user || user.length === 0) { - return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 }); - } - - const session = { session: sessionData[0], user: user[0] } - - const query = new URL(req.url).searchParams.get("q"); - const spaces = new URL(req.url).searchParams.get("spaces"); - - const sourcesOnly = new URL(req.url).searchParams.get("sourcesOnly") ?? "false"; - - const chatHistory = await req.json() as { - chatHistory: ChatHistory[] - }; - - console.log("CHathistory", chatHistory) - - if (!query) { - return new Response(JSON.stringify({ message: "Invalid query" }), { status: 400 }); - } - - - const resp = await fetch(`https://cf-ai-backend.dhravya.workers.dev/chat?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}&spaces=${spaces}`, { - headers: { - "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY, - }, - method: "POST", - body: JSON.stringify({ - chatHistory: chatHistory.chatHistory ?? [] - }) - }) - - console.log(resp.status) - console.log(resp.statusText) - - if (resp.status !== 200 || !resp.ok) { - const errorData = await resp.json(); - console.log(errorData) - return new Response(JSON.stringify({ message: "Error in CF function", error: errorData }), { status: resp.status }); - } - - // Stream the response back to the client - const { readable, writable } = new TransformStream(); - resp && resp.body!.pipeTo(writable); - - return new Response(readable, { status: 200 }); -}
\ No newline at end of file + const token = + req.cookies.get("next-auth.session-token")?.value ?? + req.cookies.get("__Secure-authjs.session-token")?.value ?? + req.cookies.get("authjs.session-token")?.value ?? + req.headers.get("Authorization")?.replace("Bearer ", ""); + + const sessionData = await db + .select() + .from(sessions) + .where(eq(sessions.sessionToken, token!)); + + if (!sessionData || sessionData.length === 0) { + return new Response( + JSON.stringify({ message: "Invalid Key, session not found." }), + { status: 404 }, + ); + } + + const user = await db + .select() + .from(users) + .where(eq(users.id, sessionData[0].userId)) + .limit(1); + + if (!user || user.length === 0) { + return NextResponse.json( + { message: "Invalid Key, session not found." }, + { status: 404 }, + ); + } + + const session = { session: sessionData[0], user: user[0] }; + + const query = new URL(req.url).searchParams.get("q"); + const spaces = new URL(req.url).searchParams.get("spaces"); + + const sourcesOnly = + new URL(req.url).searchParams.get("sourcesOnly") ?? "false"; + + const chatHistory = (await req.json()) as { + chatHistory: ChatHistory[]; + }; + + console.log("CHathistory", chatHistory); + + if (!query) { + return new Response(JSON.stringify({ message: "Invalid query" }), { + status: 400, + }); + } + + const resp = await fetch( + `https://cf-ai-backend.dhravya.workers.dev/chat?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}&spaces=${spaces}`, + { + headers: { + "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY, + }, + method: "POST", + body: JSON.stringify({ + chatHistory: chatHistory.chatHistory ?? [], + }), + }, + ); + + console.log(resp.status); + console.log(resp.statusText); + + if (resp.status !== 200 || !resp.ok) { + const errorData = await resp.json(); + console.log(errorData); + return new Response( + JSON.stringify({ message: "Error in CF function", error: errorData }), + { status: resp.status }, + ); + } + + // Stream the response back to the client + const { readable, writable } = new TransformStream(); + resp && resp.body!.pipeTo(writable); + + return new Response(readable, { status: 200 }); +} diff --git a/apps/web/src/app/api/me/route.ts b/apps/web/src/app/api/me/route.ts index a2e713e1..6d269872 100644 --- a/apps/web/src/app/api/me/route.ts +++ b/apps/web/src/app/api/me/route.ts @@ -7,19 +7,42 @@ import { env } from "@/env"; export const runtime = "edge"; export async function GET(req: NextRequest) { - const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", ""); + const token = + req.cookies.get("next-auth.session-token")?.value ?? + req.cookies.get("__Secure-authjs.session-token")?.value ?? + req.cookies.get("authjs.session-token")?.value ?? + req.headers.get("Authorization")?.replace("Bearer ", ""); - const session = await db.select().from(sessions).where(eq(sessions.sessionToken, token!)) + const session = await db + .select() + .from(sessions) + .where(eq(sessions.sessionToken, token!)); - if (!session || session.length === 0) { - return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 }); - } + if (!session || session.length === 0) { + return new Response( + JSON.stringify({ message: "Invalid Key, session not found." }), + { status: 404 }, + ); + } - const user = await db.select().from(users).where(eq(users.id, session[0].userId)).limit(1) + const user = await db + .select() + .from(users) + .where(eq(users.id, session[0].userId)) + .limit(1); - if (!user || user.length === 0) { - return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 }); - } + if (!user || user.length === 0) { + return NextResponse.json( + { message: "Invalid Key, session not found." }, + { status: 404 }, + ); + } - return new Response(JSON.stringify({ message: "OK", data: { session: session[0], user: user[0] } }), { status: 200 }); -}
\ No newline at end of file + return new Response( + JSON.stringify({ + message: "OK", + data: { session: session[0], user: user[0] }, + }), + { status: 200 }, + ); +} diff --git a/apps/web/src/app/api/query/route.ts b/apps/web/src/app/api/query/route.ts index 28f441bc..02bb79da 100644 --- a/apps/web/src/app/api/query/route.ts +++ b/apps/web/src/app/api/query/route.ts @@ -7,46 +7,72 @@ import { env } from "@/env"; export const runtime = "edge"; export async function GET(req: NextRequest) { - const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", ""); + const token = + req.cookies.get("next-auth.session-token")?.value ?? + req.cookies.get("__Secure-authjs.session-token")?.value ?? + req.cookies.get("authjs.session-token")?.value ?? + req.headers.get("Authorization")?.replace("Bearer ", ""); - const sessionData = await db.select().from(sessions).where(eq(sessions.sessionToken, token!)) + const sessionData = await db + .select() + .from(sessions) + .where(eq(sessions.sessionToken, token!)); - if (!sessionData || sessionData.length === 0) { - return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 }); - } + if (!sessionData || sessionData.length === 0) { + return new Response( + JSON.stringify({ message: "Invalid Key, session not found." }), + { status: 404 }, + ); + } - const user = await db.select().from(users).where(eq(users.id, sessionData[0].userId)).limit(1) + const user = await db + .select() + .from(users) + .where(eq(users.id, sessionData[0].userId)) + .limit(1); - if (!user || user.length === 0) { - return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 }); - } + if (!user || user.length === 0) { + return NextResponse.json( + { message: "Invalid Key, session not found." }, + { status: 404 }, + ); + } - const session = { session: sessionData[0], user: user[0] } + const session = { session: sessionData[0], user: user[0] }; - const query = new URL(req.url).searchParams.get("q"); - const sourcesOnly = new URL(req.url).searchParams.get("sourcesOnly") ?? "false"; + const query = new URL(req.url).searchParams.get("q"); + const sourcesOnly = + new URL(req.url).searchParams.get("sourcesOnly") ?? "false"; - if (!query) { - return new Response(JSON.stringify({ message: "Invalid query" }), { status: 400 }); - } + if (!query) { + return new Response(JSON.stringify({ message: "Invalid query" }), { + status: 400, + }); + } - const resp = await fetch(`https://cf-ai-backend.dhravya.workers.dev/query?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}`, { - headers: { - "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY, - } - }) + const resp = await fetch( + `https://cf-ai-backend.dhravya.workers.dev/query?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}`, + { + headers: { + "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY, + }, + }, + ); - console.log(resp.status) + console.log(resp.status); - if (resp.status !== 200 || !resp.ok) { - const errorData = await resp.json(); - console.log(errorData) - return new Response(JSON.stringify({ message: "Error in CF function", error: errorData }), { status: resp.status }); - } + if (resp.status !== 200 || !resp.ok) { + const errorData = await resp.json(); + console.log(errorData); + return new Response( + JSON.stringify({ message: "Error in CF function", error: errorData }), + { status: resp.status }, + ); + } - // Stream the response back to the client - const { readable, writable } = new TransformStream(); - resp && resp.body!.pipeTo(writable); + // Stream the response back to the client + const { readable, writable } = new TransformStream(); + resp && resp.body!.pipeTo(writable); - return new Response(readable, { status: 200 }); -}
\ No newline at end of file + return new Response(readable, { status: 200 }); +} diff --git a/apps/web/src/app/api/store/route.ts b/apps/web/src/app/api/store/route.ts index 06db08b9..ebe23077 100644 --- a/apps/web/src/app/api/store/route.ts +++ b/apps/web/src/app/api/store/route.ts @@ -1,6 +1,12 @@ import { db } from "@/server/db"; import { and, eq } from "drizzle-orm"; -import { contentToSpace, sessions, storedContent, users, space } from "@/server/db/schema"; +import { + contentToSpace, + sessions, + storedContent, + users, + space, +} from "@/server/db/schema"; import { type NextRequest, NextResponse } from "next/server"; import { env } from "@/env"; import { getMetaData } from "@/server/helpers"; @@ -8,94 +14,123 @@ import { getMetaData } from "@/server/helpers"; export const runtime = "edge"; export async function POST(req: NextRequest) { - const token = req.cookies.get("next-auth.session-token")?.value ?? req.cookies.get("__Secure-authjs.session-token")?.value ?? req.cookies.get("authjs.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", ""); - - if (!token) { - return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 }); - } - - const sessionData = await db.select().from(sessions).where(eq(sessions.sessionToken, token!)) - - if (!sessionData || sessionData.length === 0) { - return new Response(JSON.stringify({ message: "Invalid Key, session not found." }), { status: 404 }); - } - - const user = await db.select().from(users).where(eq(users.id, sessionData[0].userId)).limit(1) - - if (!user || user.length === 0) { - return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 }); - } - - const session = { session: sessionData[0], user: user[0] } - - const data = await req.json() as { - pageContent: string, - url: string, - space?: string - }; - - const metadata = await getMetaData(data.url); - - - let id: number | undefined = undefined; - - let storeToSpace = data.space - - if (!storeToSpace) { - storeToSpace = 'all' - } - - const storedContentId = await db.insert(storedContent).values({ - content: data.pageContent, - title: metadata.title, - description: metadata.description, - url: data.url, - baseUrl: metadata.baseUrl, - image: metadata.image, - savedAt: new Date(), - user: session.user.id - }) - - id = storedContentId.meta.last_row_id; - - if (!id) { - return NextResponse.json({ message: "Error", error: "Error in CF function" }, { status: 500 }); - } - - let spaceID = 0; - - const spaceData = await db.select().from(space).where(and(eq(space.name, storeToSpace), eq(space.user, session.user.id))).limit(1) - spaceID = spaceData[0]?.id - - if (!spaceData || spaceData.length === 0) { - const spaceId = await db.insert(space).values({ - name: storeToSpace, - user: session.user.id - }) - spaceID = spaceId.meta.last_row_id; - } - - await db.insert(contentToSpace).values({ - contentId: id as number, - spaceId: spaceID - }) - - const res = await Promise.race([ - fetch("https://cf-ai-backend.dhravya.workers.dev/add", { - method: "POST", - headers: { - "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY, - }, - body: JSON.stringify({ ...data, user: session.user.email }), - }), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Request timed out')), 40000) - ) - ]) as Response - - if (res.status !== 200) { - return NextResponse.json({ message: "Error", error: "Error in CF function" }, { status: 500 }); - } - - return NextResponse.json({ message: "OK", data: "Success" }, { status: 200 }); -}
\ No newline at end of file + const token = + req.cookies.get("next-auth.session-token")?.value ?? + req.cookies.get("__Secure-authjs.session-token")?.value ?? + req.cookies.get("authjs.session-token")?.value ?? + req.headers.get("Authorization")?.replace("Bearer ", ""); + + if (!token) { + return new Response( + JSON.stringify({ message: "Invalid Key, session not found." }), + { status: 404 }, + ); + } + + const sessionData = await db + .select() + .from(sessions) + .where(eq(sessions.sessionToken, token!)); + + if (!sessionData || sessionData.length === 0) { + return new Response( + JSON.stringify({ message: "Invalid Key, session not found." }), + { status: 404 }, + ); + } + + const user = await db + .select() + .from(users) + .where(eq(users.id, sessionData[0].userId)) + .limit(1); + + if (!user || user.length === 0) { + return NextResponse.json( + { message: "Invalid Key, session not found." }, + { status: 404 }, + ); + } + + const session = { session: sessionData[0], user: user[0] }; + + const data = (await req.json()) as { + pageContent: string; + url: string; + space?: string; + }; + + const metadata = await getMetaData(data.url); + + let id: number | undefined = undefined; + + let storeToSpace = data.space; + + if (!storeToSpace) { + storeToSpace = "all"; + } + + const storedContentId = await db.insert(storedContent).values({ + content: data.pageContent, + title: metadata.title, + description: metadata.description, + url: data.url, + baseUrl: metadata.baseUrl, + image: metadata.image, + savedAt: new Date(), + user: session.user.id, + }); + + id = storedContentId.meta.last_row_id; + + if (!id) { + return NextResponse.json( + { message: "Error", error: "Error in CF function" }, + { status: 500 }, + ); + } + + let spaceID = 0; + + const spaceData = await db + .select() + .from(space) + .where(and(eq(space.name, storeToSpace), eq(space.user, session.user.id))) + .limit(1); + spaceID = spaceData[0]?.id; + + if (!spaceData || spaceData.length === 0) { + const spaceId = await db.insert(space).values({ + name: storeToSpace, + user: session.user.id, + }); + spaceID = spaceId.meta.last_row_id; + } + + await db.insert(contentToSpace).values({ + contentId: id as number, + spaceId: spaceID, + }); + + const res = (await Promise.race([ + fetch("https://cf-ai-backend.dhravya.workers.dev/add", { + method: "POST", + headers: { + "X-Custom-Auth-Key": env.BACKEND_SECURITY_KEY, + }, + body: JSON.stringify({ ...data, user: session.user.email }), + }), + new Promise((_, reject) => + setTimeout(() => reject(new Error("Request timed out")), 40000), + ), + ])) as Response; + + if (res.status !== 200) { + return NextResponse.json( + { message: "Error", error: "Error in CF function" }, + { status: 500 }, + ); + } + + return NextResponse.json({ message: "OK", data: "Success" }, { status: 200 }); +} diff --git a/apps/web/src/app/content.tsx b/apps/web/src/app/content.tsx index 39f2948d..50e0617c 100644 --- a/apps/web/src/app/content.tsx +++ b/apps/web/src/app/content.tsx @@ -1,16 +1,16 @@ -'use client'; -import Main from '@/components/Main'; -import Sidebar from '@/components/Sidebar/index'; -import { SessionProvider } from 'next-auth/react'; -import { useState } from 'react'; +"use client"; +import Main from "@/components/Main"; +import Sidebar from "@/components/Sidebar/index"; +import { SessionProvider } from "next-auth/react"; +import { useState } from "react"; -export default function Content() { +export default function Content({ jwt }: { jwt: string }) { const [selectedItem, setSelectedItem] = useState<string | null>(null); return ( <SessionProvider> <div className="flex w-screen"> - <Sidebar selectChange={setSelectedItem} /> + <Sidebar jwt={jwt} selectChange={setSelectedItem} /> <Main sidebarOpen={selectedItem !== null} /> </div> </SessionProvider> diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index cedb03dc..23caee5b 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -20,7 +20,7 @@ } body { - @apply bg-rgray-2 text-rgray-11 max-h-screen overflow-y-hidden; + @apply text-rgray-11 max-h-screen overflow-y-hidden bg-white; /* color: rgb(var(--foreground-rgb)); background: linear-gradient( to bottom, @@ -66,7 +66,7 @@ body { } .chat-answer h1 { - @apply text-rgray-11 text-xl font-medium my-5; + @apply text-rgray-11 my-5 text-xl font-medium; } .chat-answer img { diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 1b204dec..42485461 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,8 +1,9 @@ import type { Metadata } from "next"; -import { Roboto } from "next/font/google"; +import { Roboto, Inter } from "next/font/google"; import "./globals.css"; const roboto = Roboto({ weight: ["300", "400", "500"], subsets: ["latin"] }); +const inter = Inter({ weight: ["300", "400", "500"], subsets: ["latin"] }); export const metadata: Metadata = { title: "Create Next App", @@ -16,7 +17,7 @@ export default function RootLayout({ }>) { return ( <html lang="en" className="dark"> - <body className={roboto.className}> + <body className={inter.className}> <div vaul-drawer-wrapper="" className="min-w-screen overflow-x-hidden"> {children} </div> diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 95e18e83..6f9ca753 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -59,26 +59,26 @@ export default async function Home() { const collectedSpaces = contents.length > 0 ? await transformContent(contents) : []; - collectedSpaces.push({ - id: 2, - title: "Test", - content: [ - { - id: 1, - content: "Test", - title: "Vscode", - description: "Test", - url: "https://vscode-remake.vercel.app/", - savedAt: new Date(), - baseUrl: "https://vscode-remake.vercel.app/", - image: "https://vscode-remake.vercel.app/favicon.svg", - }, - ], - }); + // collectedSpaces.push({ + // id: 2, + // title: "Test", + // content: [ + // { + // id: 1, + // content: "Test", + // title: "Vscode", + // description: "Test", + // url: "https://vscode-remake.vercel.app/", + // savedAt: new Date(), + // baseUrl: "https://vscode-remake.vercel.app/", + // image: "https://vscode-remake.vercel.app/favicon.svg", + // }, + // ], + // }); return ( <MemoryProvider spaces={collectedSpaces}> - <Content /> + <Content jwt={token} /> {/* <MessagePoster jwt={token} /> */} </MemoryProvider> ); diff --git a/apps/web/src/app/privacy/page.tsx b/apps/web/src/app/privacy/page.tsx index 5d0ae2b8..8d126dff 100644 --- a/apps/web/src/app/privacy/page.tsx +++ b/apps/web/src/app/privacy/page.tsx @@ -1,6 +1,6 @@ -import React from 'react'; -import Markdown from 'react-markdown'; -import { policy } from './privacy'; +import React from "react"; +import Markdown from "react-markdown"; +import { policy } from "./privacy"; function Page() { return ( diff --git a/apps/web/src/app/privacy/privacy.ts b/apps/web/src/app/privacy/privacy.ts index 3e3df4fb..2034f191 100644 --- a/apps/web/src/app/privacy/privacy.ts +++ b/apps/web/src/app/privacy/privacy.ts @@ -46,4 +46,4 @@ If you have any questions about this Privacy Policy, the practices of this site, - Email: [email protected] -This document was last updated on March 2, 2024.`
\ No newline at end of file +This document was last updated on March 2, 2024.`; diff --git a/apps/web/src/components/Main.tsx b/apps/web/src/components/Main.tsx index c621c68f..b34755f9 100644 --- a/apps/web/src/components/Main.tsx +++ b/apps/web/src/components/Main.tsx @@ -11,6 +11,7 @@ import { ChatHistory } from "../../types/memory"; import { ChatAnswer, ChatMessage, ChatQuestion } from "./ChatMessage"; import { useRouter, useSearchParams } from "next/navigation"; import { useMemory } from "@/contexts/MemoryContext"; +import WordMark from "./WordMark"; function supportsDVH() { try { @@ -293,8 +294,8 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) { hide ? "" : "main-hidden", )} > - <h1 className="text-rgray-11 mt-auto w-full text-center text-3xl md:mt-0"> - Ask your Second brain + <h1 className="text-rgray-11 mt-auto w-full text-center text-3xl font-bold tracking-tight md:mt-0"> + Ask your second brain </h1> <Textarea2 @@ -308,14 +309,13 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) { duration: 0.2, }} textAreaProps={{ - placeholder: "Ask your SuperMemory...", + placeholder: "Ask your second brain...", className: "h-auto overflow-auto md:h-full md:resize-none text-lg py-0 px-2 pt-2 md:py-0 md:p-5 resize-y text-rgray-11 w-full min-h-[1em]", value, autoFocus: true, onChange: (e) => setValue(e.target.value), onKeyDown: (e) => { - console.log(e.key, e.ctrlKey, e.metaKey); if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { onSend(); } diff --git a/apps/web/src/components/QueryAI.tsx b/apps/web/src/components/QueryAI.tsx new file mode 100644 index 00000000..894b5d2d --- /dev/null +++ b/apps/web/src/components/QueryAI.tsx @@ -0,0 +1,139 @@ +"use client"; + +import { Label } from "./ui/label"; +import React, { useEffect, useState } from "react"; +import { Input } from "./ui/input"; +import { Button } from "./ui/button"; +import SearchResults from "./SearchResults"; + +function QueryAI() { + const [searchResults, setSearchResults] = useState<string[]>([]); + const [isAiLoading, setIsAiLoading] = useState(false); + + const [aiResponse, setAIResponse] = useState(""); + const [input, setInput] = useState(""); + const [toBeParsed, setToBeParsed] = useState(""); + + const handleStreamData = (newChunk: string) => { + // Append the new chunk to the existing data to be parsed + setToBeParsed((prev) => prev + newChunk); + }; + + useEffect(() => { + // Define a function to try parsing the accumulated data + const tryParseAccumulatedData = () => { + // Attempt to parse the "toBeParsed" state as JSON + try { + // Split the accumulated data by the known delimiter "\n\n" + const parts = toBeParsed.split("\n\n"); + let remainingData = ""; + + // Process each part to extract JSON objects + parts.forEach((part, index) => { + try { + const parsedPart = JSON.parse(part.replace("data: ", "")); // Try to parse the part as JSON + + // If the part is the last one and couldn't be parsed, keep it to accumulate more data + if (index === parts.length - 1 && !parsedPart) { + remainingData = part; + } else if (parsedPart && parsedPart.response) { + // If the part is parsable and has the "response" field, update the AI response state + setAIResponse((prev) => prev + parsedPart.response); + } + } catch (error) { + // If parsing fails and it's not the last part, it's a malformed JSON + if (index !== parts.length - 1) { + console.error("Malformed JSON part: ", part); + } else { + // If it's the last part, it may be incomplete, so keep it + remainingData = part; + } + } + }); + + // Update the toBeParsed state to only contain the unparsed remainder + if (remainingData !== toBeParsed) { + setToBeParsed(remainingData); + } + } catch (error) { + console.error("Error parsing accumulated data: ", error); + } + }; + + // Call the parsing function if there's data to be parsed + if (toBeParsed) { + tryParseAccumulatedData(); + } + }, [toBeParsed]); + + const getSearchResults = async (e: React.FormEvent<HTMLFormElement>) => { + e.preventDefault(); + setIsAiLoading(true); + + const sourcesResponse = await fetch( + `/api/query?sourcesOnly=true&q=${input}`, + ); + + const sourcesInJson = (await sourcesResponse.json()) as { + ids: string[]; + }; + + setSearchResults(sourcesInJson.ids); + + const response = await fetch(`/api/query?q=${input}`); + + if (response.status !== 200) { + setIsAiLoading(false); + return; + } + + if (response.body) { + let reader = response.body.getReader(); + let decoder = new TextDecoder("utf-8"); + let result = ""; + + // @ts-ignore + reader.read().then(function processText({ done, value }) { + if (done) { + // setSearchResults(JSON.parse(result.replace('data: ', ''))); + // setIsAiLoading(false); + return; + } + + handleStreamData(decoder.decode(value)); + + return reader.read().then(processText); + }); + } + }; + + return ( + <div className="mx-auto w-full max-w-2xl"> + <form onSubmit={async (e) => await getSearchResults(e)} className="mt-8"> + <Label htmlFor="searchInput">Ask your SuperMemory</Label> + <div className="flex flex-col space-y-2 md:w-full md:flex-row md:items-center md:space-x-2 md:space-y-0"> + <Input + value={input} + onChange={(e) => setInput(e.target.value)} + placeholder="Search using AI... ✨" + id="searchInput" + /> + <Button + disabled={isAiLoading} + className="max-w-min md:w-full" + type="submit" + variant="default" + > + Ask AI + </Button> + </div> + </form> + + {searchResults && ( + <SearchResults aiResponse={aiResponse} sources={searchResults} /> + )} + </div> + ); +} + +export default QueryAI; diff --git a/apps/web/src/components/SearchResults.tsx b/apps/web/src/components/SearchResults.tsx new file mode 100644 index 00000000..d348814e --- /dev/null +++ b/apps/web/src/components/SearchResults.tsx @@ -0,0 +1,40 @@ +"use client"; + +import React from "react"; +import { Card, CardContent } from "./ui/card"; +import Markdown from "react-markdown"; +import remarkGfm from "remark-gfm"; + +function SearchResults({ + aiResponse, + sources, +}: { + aiResponse: string; + sources: string[]; +}) { + return ( + <div + style={{ + backgroundImage: `linear-gradient(to right, #E5D9F2, #CDC1FF)`, + }} + className="mx-auto mt-4 w-full max-w-2xl space-y-6 rounded-xl border px-4 py-6" + > + <div className="text-start"> + <div className="text-xl text-black"> + <Markdown remarkPlugins={[remarkGfm]}> + {aiResponse.replace("</s>", "")} + </Markdown> + </div> + </div> + <div className="grid gap-6"> + {sources.map((value, index) => ( + <Card key={index}> + <CardContent className="space-y-2">{value}</CardContent> + </Card> + ))} + </div> + </div> + ); +} + +export default SearchResults; diff --git a/apps/web/src/components/Sidebar/CategoryItem.tsx b/apps/web/src/components/Sidebar/CategoryItem.tsx index 0cf8a70c..7fb571b5 100644 --- a/apps/web/src/components/Sidebar/CategoryItem.tsx +++ b/apps/web/src/components/Sidebar/CategoryItem.tsx @@ -1,13 +1,13 @@ -'use client'; -import { cleanUrl } from '@/lib/utils'; -import { StoredContent } from '@/server/db/schema'; +"use client"; +import { cleanUrl } from "@/lib/utils"; +import { StoredContent } from "@/server/db/schema"; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, -} from '../ui/dropdown-menu'; -import { Label } from '../ui/label'; +} from "../ui/dropdown-menu"; +import { Label } from "../ui/label"; import { ArrowUpRight, MoreHorizontal, @@ -19,8 +19,8 @@ import { ChevronRight, Plus, Minus, -} from 'lucide-react'; -import { useState } from 'react'; +} from "lucide-react"; +import { useState } from "react"; import { Drawer, DrawerContent, @@ -29,106 +29,106 @@ import { DrawerDescription, DrawerFooter, DrawerClose, -} from '../ui/drawer'; -import { Input } from '../ui/input'; -import { Textarea } from '../ui/textarea'; -import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover'; +} from "../ui/drawer"; +import { Input } from "../ui/input"; +import { Textarea } from "../ui/textarea"; +import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; import { AnimatePresence, motion, Reorder, useMotionValue, -} from 'framer-motion'; +} from "framer-motion"; const pages: StoredContent[] = [ { id: 1, - content: '', - title: 'Visual Studio Code', - url: 'https://code.visualstudio.com', - description: '', - image: 'https://code.visualstudio.com/favicon.ico', - baseUrl: 'https://code.visualstudio.com', + content: "", + title: "Visual Studio Code", + url: "https://code.visualstudio.com", + description: "", + image: "https://code.visualstudio.com/favicon.ico", + baseUrl: "https://code.visualstudio.com", savedAt: new Date(), }, { id: 2, - content: '', + content: "", title: "yxshv/vscode: An unofficial remake of vscode's landing page", - url: 'https://github.com/yxshv/vscode', - description: '', - image: 'https://github.com/favicon.ico', - baseUrl: 'https://github.com', + url: "https://github.com/yxshv/vscode", + description: "", + image: "https://github.com/favicon.ico", + baseUrl: "https://github.com", savedAt: new Date(), }, { id: 3, - content: '', + content: "", title: "yxshv/vscode: An unofficial remake of vscode's landing page", - url: 'https://github.com/yxshv/vscode', - description: '', - image: 'https://github.com/favicon.ico', - baseUrl: 'https://github.com', + url: "https://github.com/yxshv/vscode", + description: "", + image: "https://github.com/favicon.ico", + baseUrl: "https://github.com", savedAt: new Date(), }, { id: 4, - content: '', + content: "", title: "yxshv/vscode: An unofficial remake of vscode's landing page", - url: 'https://github.com/yxshv/vscode', - description: '', - image: 'https://github.com/favicon.ico', - baseUrl: 'https://github.com', + url: "https://github.com/yxshv/vscode", + description: "", + image: "https://github.com/favicon.ico", + baseUrl: "https://github.com", savedAt: new Date(), }, { id: 5, - content: '', + content: "", title: "yxshv/vscode: An unofficial remake of vscode's landing page", - url: 'https://github.com/yxshv/vscode', - description: '', - image: 'https://github.com/favicon.ico', - baseUrl: 'https://github.com', + url: "https://github.com/yxshv/vscode", + description: "", + image: "https://github.com/favicon.ico", + baseUrl: "https://github.com", savedAt: new Date(), }, { id: 6, - content: '', + content: "", title: "yxshv/vscode: An unofficial remake of vscode's landing page", - url: 'https://github.com/yxshv/vscode', - description: '', - image: 'https://github.com/favicon.ico', - baseUrl: 'https://github.com', + url: "https://github.com/yxshv/vscode", + description: "", + image: "https://github.com/favicon.ico", + baseUrl: "https://github.com", savedAt: new Date(), }, { id: 7, - content: '', + content: "", title: "yxshv/vscode: An unofficial remake of vscode's landing page", - url: 'https://github.com/yxshv/vscode', - description: '', - image: 'https://github.com/favicon.ico', - baseUrl: 'https://github.com', + url: "https://github.com/yxshv/vscode", + description: "", + image: "https://github.com/favicon.ico", + baseUrl: "https://github.com", savedAt: new Date(), }, { id: 8, - content: '', + content: "", title: "yxshv/vscode: An unofficial remake of vscode's landing page", - url: 'https://github.com/yxshv/vscode', - description: '', - image: 'https://github.com/favicon.ico', - baseUrl: 'https://github.com', + url: "https://github.com/yxshv/vscode", + description: "", + image: "https://github.com/favicon.ico", + baseUrl: "https://github.com", savedAt: new Date(), }, { id: 9, - content: '', + content: "", title: "yxshv/vscode: An unofficial remake of vscode's landing page", - url: 'https://github.com/yxshv/vscode', - description: '', - image: 'https://github.com/favicon.ico', - baseUrl: 'https://github.com', + url: "https://github.com/yxshv/vscode", + description: "", + image: "https://github.com/favicon.ico", + baseUrl: "https://github.com", savedAt: new Date(), }, ]; @@ -153,13 +153,13 @@ export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => { /> <ChevronDown data-down-icon - className={`absolute left-1/2 top-1/2 z-[2] h-4 w-4 min-w-4 -translate-x-1/2 -translate-y-1/2 scale-75 opacity-0 transition-[transform,opacity] duration-150 ${isExpanded ? 'rotate-180' : 'rotate-0'}`} + className={`absolute left-1/2 top-1/2 z-[2] h-4 w-4 min-w-4 -translate-x-1/2 -translate-y-1/2 scale-75 opacity-0 transition-[transform,opacity] duration-150 ${isExpanded ? "rotate-180" : "rotate-0"}`} strokeWidth={1.5} /> </div> <span className="w-full truncate text-nowrap text-left"> - {item.title ?? 'Untitled website'} + {item.title ?? "Untitled website"} </span> </button> <Drawer @@ -178,7 +178,7 @@ export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => { href={item.url} className="text-rgray-11/90 bg-rgray-3 text-md absolute right-0 top-0 flex w-min translate-y-1/2 items-center justify-center gap-1 rounded-full px-5 py-1" > - <img src={item.image ?? '/brain.png'} className="h-4 w-4" /> + <img src={item.image ?? "/brain.png"} className="h-4 w-4" /> {cleanUrl(item.url)} </a> </DrawerHeader> @@ -188,16 +188,16 @@ export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => { <Input className="" required - value={item.title ?? ''} - placeholder={item.title ?? 'Enter the title for the page'} + value={item.title ?? ""} + placeholder={item.title ?? "Enter the title for the page"} /> </div> <div className="mt-5"> <Label>Additional Context</Label> <Textarea className="" - value={item.content ?? ''} - placeholder={'Enter additional context for this page'} + value={item.content ?? ""} + placeholder={"Enter additional context for this page"} /> </div> <DrawerFooter className="flex flex-row-reverse items-center justify-end px-0 pt-5"> @@ -224,7 +224,7 @@ export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => { onReorder={setItems} as="div" initial={{ height: 0 }} - animate={{ height: 'auto' }} + animate={{ height: "auto" }} exit={{ height: 0, transition: {}, @@ -272,8 +272,8 @@ export const CategoryPage: React.FC<{ > <div className="relative h-4 min-w-4"> <img - src={item.image ?? '/brain.png'} - alt={item.title ?? 'Untitiled website'} + src={item.image ?? "/brain.png"} + alt={item.title ?? "Untitiled website"} className="z-1 h-4 w-4 transition-[transform,opacity] delay-150 duration-150" /> <ArrowUpRight @@ -284,7 +284,7 @@ export const CategoryPage: React.FC<{ </div> <span className="w-full truncate text-nowrap"> - {item.title ?? 'Untitled website'} + {item.title ?? "Untitled website"} </span> </a> <button diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx index 76b66db9..a8e3a1e5 100644 --- a/apps/web/src/components/Sidebar/FilterCombobox.tsx +++ b/apps/web/src/components/Sidebar/FilterCombobox.tsx @@ -1,10 +1,10 @@ -'use client'; +"use client"; -import * as React from 'react'; -import { Check, ChevronsUpDown } from 'lucide-react'; +import * as React from "react"; +import { Check, ChevronsUpDown } from "lucide-react"; -import { cn } from '@/lib/utils'; -import { Button } from '@/components/ui/button'; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, @@ -12,28 +12,30 @@ import { CommandInput, CommandItem, CommandList, -} from '@/components/ui/command'; +} from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger, -} from '@/components/ui/popover'; -import { SpaceIcon } from '@/assets/Memories'; -import { AnimatePresence, LayoutGroup, motion } from 'framer-motion'; -import { useMemory } from '@/contexts/MemoryContext'; +} from "@/components/ui/popover"; +import { SpaceIcon } from "@/assets/Memories"; +import { AnimatePresence, LayoutGroup, motion } from "framer-motion"; +import { useMemory } from "@/contexts/MemoryContext"; export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> { - side?: 'top' | 'bottom'; - align?: 'end' | 'start' | 'center'; + side?: "top" | "bottom"; + align?: "end" | "start" | "center"; onClose?: () => void; selectedSpaces: number[]; - setSelectedSpaces: (spaces: number[] | ((prev: number[]) => number[])) => void; + setSelectedSpaces: ( + spaces: number[] | ((prev: number[]) => number[]), + ) => void; } export function FilterCombobox({ className, - side = 'bottom', - align = 'center', + side = "bottom", + align = "center", onClose, selectedSpaces, setSelectedSpaces, @@ -65,7 +67,7 @@ export function FilterCombobox({ <button data-state-on={open} className={cn( - 'text-rgray-11/70 on:bg-rgray-3 focus-visible:ring-rgray-8 hover:bg-rgray-3 relative flex items-center justify-center gap-1 rounded-md px-3 py-1.5 ring-2 ring-transparent focus-visible:outline-none', + "text-rgray-11/70 on:bg-rgray-3 focus-visible:ring-rgray-8 hover:bg-rgray-3 relative flex items-center justify-center gap-1 rounded-md px-3 py-1.5 ring-2 ring-transparent focus-visible:outline-none", className, )} {...props} @@ -129,7 +131,7 @@ export function FilterCombobox({ <Check data-state-on={selectedSpaces.includes(space.id)} className={cn( - 'on:opacity-100 ml-auto h-4 w-4 opacity-0', + "on:opacity-100 ml-auto h-4 w-4 opacity-0", )} /> </motion.div> diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx index 830b0f05..568aa3dd 100644 --- a/apps/web/src/components/Sidebar/index.tsx +++ b/apps/web/src/components/Sidebar/index.tsx @@ -1,29 +1,35 @@ -'use client'; -import { MemoryIcon } from '../../assets/Memories'; -import { Trash2, User2 } from 'lucide-react'; -import React, { useEffect, useState } from 'react'; -import { MemoriesBar } from './MemoriesBar'; -import { AnimatePresence, motion } from 'framer-motion'; -import { Bin } from '@/assets/Bin'; -import { Avatar, AvatarFallback, AvatarImage } from '@radix-ui/react-avatar'; -import { useSession } from 'next-auth/react'; +"use client"; +import { MemoryIcon } from "../../assets/Memories"; +import { Trash2, User2 } from "lucide-react"; +import React, { useEffect, useState } from "react"; +import { MemoriesBar } from "./MemoriesBar"; +import { AnimatePresence, motion } from "framer-motion"; +import { Bin } from "@/assets/Bin"; +import { Avatar, AvatarFallback, AvatarImage } from "@radix-ui/react-avatar"; +import { useSession } from "next-auth/react"; +import MessagePoster from "@/app/MessagePoster"; +import Image from "next/image"; +import WordMark from "../WordMark"; export type MenuItem = { icon: React.ReactNode | React.ReactNode[]; label: string; content?: React.ReactNode; + labelDisplay?: React.ReactNode; }; export default function Sidebar({ selectChange, + jwt, }: { selectChange?: (selectedItem: string | null) => void; + jwt: string; }) { const { data: session } = useSession(); const menuItemsTop: Array<MenuItem> = [ { icon: <MemoryIcon className="h-10 w-10" />, - label: 'Memories', + label: "Memories", content: <MemoriesBar />, }, ]; @@ -31,7 +37,7 @@ export default function Sidebar({ const menuItemsBottom: Array<MenuItem> = [ { icon: <Trash2 strokeWidth={1.3} className="h-6 w-6" />, - label: 'Trash', + label: "Trash", }, { icon: ( @@ -47,12 +53,12 @@ export default function Sidebar({ <User2 strokeWidth={1.3} className="h-6 w-6" /> )} <AvatarFallback> - {session?.user?.name?.split(' ').map((n) => n[0])}{' '} + {session?.user?.name?.split(" ").map((n) => n[0])}{" "} </AvatarFallback> </Avatar> </div> ), - label: 'Profile', + label: "Profile", }, ]; @@ -70,22 +76,30 @@ export default function Sidebar({ return ( <> <div className="relative hidden h-screen max-h-screen w-max flex-col items-center text-sm font-light md:flex"> - <div className="bg-rgray-2 border-r-rgray-6 relative z-[50] flex h-full w-full flex-col items-center justify-center border-r px-2 py-5 "> + <div className="bg-rgray-3 border-r-rgray-6 relative z-[50] flex h-full w-full flex-col items-center justify-center border-r px-2 py-5 "> + <Image + className="mb-4 rounded-md" + src="/icons/logo_bw_without_bg.png" + alt="Smort logo" + width={50} + height={50} + /> + + <div className="bg-rgray-6 mb-8 h-[1px] w-full" /> + <MenuItem item={{ - label: 'Memories', + label: "Memories", icon: <MemoryIcon className="h-10 w-10" />, content: <MemoriesBar />, }} selectedItem={selectedItem} setSelectedItem={setSelectedItem} /> - <div className="mt-auto" /> - <MenuItem item={{ - label: 'Trash', + label: "Trash", icon: <Bin id="trash" className="z-[300] h-7 w-7" />, }} selectedItem={selectedItem} @@ -94,7 +108,7 @@ export default function Sidebar({ /> <MenuItem item={{ - label: 'Profile', + label: "Profile", icon: ( <div className="mb-2"> <Avatar> @@ -108,7 +122,7 @@ export default function Sidebar({ <User2 strokeWidth={1.3} className="h-6 w-6" /> )} <AvatarFallback> - {session?.user?.name?.split(' ').map((n) => n[0])}{' '} + {session?.user?.name?.split(" ").map((n) => n[0])}{" "} </AvatarFallback> </Avatar> </div> @@ -117,6 +131,7 @@ export default function Sidebar({ selectedItem={selectedItem} setSelectedItem={setSelectedItem} /> + <MessagePoster jwt={jwt} /> </div> <AnimatePresence> {selectedItem && <SubSidebar>{Subbar}</SubSidebar>} @@ -127,7 +142,7 @@ export default function Sidebar({ } const MenuItem = ({ - item: { icon, label }, + item: { icon, label, labelDisplay }, selectedItem, setSelectedItem, ...props @@ -143,18 +158,18 @@ const MenuItem = ({ {...props} > {icon} - <span className="">{label}</span> + <span className="">{labelDisplay ?? label}</span> </button> ); export function SubSidebar({ children }: { children?: React.ReactNode }) { return ( <motion.div - initial={{ opacity: 0, x: '-100%' }} + initial={{ opacity: 0, x: "-100%" }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, - x: '-100%', + x: "-100%", transition: { delay: 0.2 }, }} transition={{ diff --git a/apps/web/src/components/WordMark.tsx b/apps/web/src/components/WordMark.tsx new file mode 100644 index 00000000..eb55647c --- /dev/null +++ b/apps/web/src/components/WordMark.tsx @@ -0,0 +1,12 @@ +import { cn } from "@/lib/utils"; +import React from "react"; + +function WordMark({ className }: { className?: string }) { + return ( + <span className={cn(`text-xl font-bold tracking-tight ${className}`)}> + smort. + </span> + ); +} + +export default WordMark; diff --git a/apps/web/src/components/ui/avatar.tsx b/apps/web/src/components/ui/avatar.tsx index fb190df3..47795451 100644 --- a/apps/web/src/components/ui/avatar.tsx +++ b/apps/web/src/components/ui/avatar.tsx @@ -1,9 +1,9 @@ -"use client" +"use client"; -import * as React from "react" -import * as AvatarPrimitive from "@radix-ui/react-avatar" +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Avatar = React.forwardRef< React.ElementRef<typeof AvatarPrimitive.Root>, @@ -13,12 +13,12 @@ const Avatar = React.forwardRef< ref={ref} className={cn( "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", - className + className, )} {...props} /> -)) -Avatar.displayName = AvatarPrimitive.Root.displayName +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; const AvatarImage = React.forwardRef< React.ElementRef<typeof AvatarPrimitive.Image>, @@ -29,8 +29,8 @@ const AvatarImage = React.forwardRef< className={cn("aspect-square h-full w-full", className)} {...props} /> -)) -AvatarImage.displayName = AvatarPrimitive.Image.displayName +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; const AvatarFallback = React.forwardRef< React.ElementRef<typeof AvatarPrimitive.Fallback>, @@ -40,11 +40,11 @@ const AvatarFallback = React.forwardRef< ref={ref} className={cn( "flex h-full w-full items-center justify-center rounded-full bg-gray-100 dark:bg-gray-800", - className + className, )} {...props} /> -)) -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; -export { Avatar, AvatarImage, AvatarFallback } +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/apps/web/src/components/ui/badge.tsx b/apps/web/src/components/ui/badge.tsx index 1e21383f..40b15b91 100644 --- a/apps/web/src/components/ui/badge.tsx +++ b/apps/web/src/components/ui/badge.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const badgeVariants = cva( "inline-flex items-center rounded-full border border-gray-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 dark:border-gray-800 dark:focus:ring-gray-300", @@ -20,8 +20,8 @@ const badgeVariants = cva( defaultVariants: { variant: "default", }, - } -) + }, +); export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, @@ -30,7 +30,7 @@ export interface BadgeProps function Badge({ className, variant, ...props }: BadgeProps) { return ( <div className={cn(badgeVariants({ variant }), className)} {...props} /> - ) + ); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/apps/web/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx index b67d2657..24fa903e 100644 --- a/apps/web/src/components/ui/button.tsx +++ b/apps/web/src/components/ui/button.tsx @@ -1,8 +1,8 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const buttonVariants = cva( "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-gray-950 dark:focus-visible:ring-gray-300", @@ -30,27 +30,27 @@ const buttonVariants = cva( variant: "default", size: "default", }, - } -) + }, +); export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> { - asChild?: boolean + asChild?: boolean; } const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" + const Comp = asChild ? Slot : "button"; return ( <button className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} /> - ) - } -) -Button.displayName = "Button" + ); + }, +); +Button.displayName = "Button"; -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/apps/web/src/components/ui/card.tsx b/apps/web/src/components/ui/card.tsx index 65119a16..e98d500c 100644 --- a/apps/web/src/components/ui/card.tsx +++ b/apps/web/src/components/ui/card.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Card = React.forwardRef< HTMLDivElement, @@ -10,12 +10,12 @@ const Card = React.forwardRef< ref={ref} className={cn( "rounded-lg border border-gray-200 bg-white text-gray-950 shadow-sm dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50", - className + className, )} {...props} /> -)) -Card.displayName = "Card" +)); +Card.displayName = "Card"; const CardHeader = React.forwardRef< HTMLDivElement, @@ -26,8 +26,8 @@ const CardHeader = React.forwardRef< className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} /> -)) -CardHeader.displayName = "CardHeader" +)); +CardHeader.displayName = "CardHeader"; const CardTitle = React.forwardRef< HTMLParagraphElement, @@ -37,12 +37,12 @@ const CardTitle = React.forwardRef< ref={ref} className={cn( "text-2xl font-semibold leading-none tracking-tight", - className + className, )} {...props} /> -)) -CardTitle.displayName = "CardTitle" +)); +CardTitle.displayName = "CardTitle"; const CardDescription = React.forwardRef< HTMLParagraphElement, @@ -53,16 +53,16 @@ const CardDescription = React.forwardRef< className={cn("text-sm text-gray-500 dark:text-gray-400", className)} {...props} /> -)) -CardDescription.displayName = "CardDescription" +)); +CardDescription.displayName = "CardDescription"; const CardContent = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => ( <div ref={ref} className={cn("p-6 pt-0", className)} {...props} /> -)) -CardContent.displayName = "CardContent" +)); +CardContent.displayName = "CardContent"; const CardFooter = React.forwardRef< HTMLDivElement, @@ -73,7 +73,14 @@ const CardFooter = React.forwardRef< className={cn("flex items-center p-6 pt-0", className)} {...props} /> -)) -CardFooter.displayName = "CardFooter" +)); +CardFooter.displayName = "CardFooter"; -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/apps/web/src/components/ui/label.tsx b/apps/web/src/components/ui/label.tsx index 53418217..84f8b0c7 100644 --- a/apps/web/src/components/ui/label.tsx +++ b/apps/web/src/components/ui/label.tsx @@ -1,14 +1,14 @@ -"use client" +"use client"; -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const labelVariants = cva( - "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" -) + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", +); const Label = React.forwardRef< React.ElementRef<typeof LabelPrimitive.Root>, @@ -20,7 +20,7 @@ const Label = React.forwardRef< className={cn(labelVariants(), className)} {...props} /> -)) -Label.displayName = LabelPrimitive.Root.displayName +)); +Label.displayName = LabelPrimitive.Root.displayName; -export { Label } +export { Label }; diff --git a/apps/web/src/contexts/MemoryContext.tsx b/apps/web/src/contexts/MemoryContext.tsx index 3727c464..eab1e4fe 100644 --- a/apps/web/src/contexts/MemoryContext.tsx +++ b/apps/web/src/contexts/MemoryContext.tsx @@ -1,6 +1,6 @@ -'use client'; -import React, { useCallback } from 'react'; -import { CollectedSpaces } from '../../types/memory'; +"use client"; +import React, { useCallback } from "react"; +import { CollectedSpaces } from "../../types/memory"; // temperory (will change) export const MemoryContext = React.createContext<{ @@ -41,7 +41,7 @@ export const MemoryProvider: React.FC< export const useMemory = () => { const context = React.useContext(MemoryContext); if (context === undefined) { - throw new Error('useMemory must be used within a MemoryProvider'); + throw new Error("useMemory must be used within a MemoryProvider"); } return context; }; diff --git a/apps/web/src/env.js b/apps/web/src/env.js index 3d3085fa..2495d75b 100644 --- a/apps/web/src/env.js +++ b/apps/web/src/env.js @@ -1,7 +1,6 @@ import { createEnv } from "@t3-oss/env-nextjs"; import { z } from "zod"; - export const env = createEnv({ /** * Specify your server-side environment variables schema here. This way you can ensure the app @@ -12,15 +11,13 @@ export const env = createEnv({ .enum(["development", "test", "production"]) .default("development"), NEXTAUTH_SECRET: - process.env.NODE_ENV === "production" - ? z.string() - : z.string(), + process.env.NODE_ENV === "production" ? z.string() : z.string(), NEXTAUTH_URL: z.preprocess( // This makes Vercel deployments not fail if you don't set NEXTAUTH_URL // Since NextAuth.js automatically uses the VERCEL_URL if present. (str) => process.env.VERCEL_URL ?? str, // VERCEL_URL doesn't include `https` so it cant be validated as a URL - process.env.VERCEL ? z.string() : z.string().url() + process.env.VERCEL ? z.string() : z.string().url(), ), GOOGLE_CLIENT_ID: z.string(), GOOGLE_CLIENT_SECRET: z.string(), diff --git a/apps/web/src/lib/searchParams.ts b/apps/web/src/lib/searchParams.ts index b435295d..aae3f4c7 100644 --- a/apps/web/src/lib/searchParams.ts +++ b/apps/web/src/lib/searchParams.ts @@ -1,12 +1,12 @@ import { - createSearchParamsCache, - parseAsInteger, - parseAsString - } from 'nuqs/server' - // Note: import from 'nuqs/server' to avoid the "use client" directive - - export const searchParamsCache = createSearchParamsCache({ - // List your search param keys and associated parsers here: - q: parseAsString.withDefault(''), - maxResults: parseAsInteger.withDefault(10) - })
\ No newline at end of file + createSearchParamsCache, + parseAsInteger, + parseAsString, +} from "nuqs/server"; +// Note: import from 'nuqs/server' to avoid the "use client" directive + +export const searchParamsCache = createSearchParamsCache({ + // List your search param keys and associated parsers here: + q: parseAsString.withDefault(""), + maxResults: parseAsInteger.withDefault(10), +}); diff --git a/apps/web/src/server/auth.ts b/apps/web/src/server/auth.ts index c32efe55..95edcf35 100644 --- a/apps/web/src/server/auth.ts +++ b/apps/web/src/server/auth.ts @@ -1,7 +1,7 @@ import { env } from "@/env"; import NextAuth from "next-auth"; import Google from "next-auth/providers/google"; -import { DrizzleAdapter } from "@auth/drizzle-adapter" +import { DrizzleAdapter } from "@auth/drizzle-adapter"; import { db } from "./db"; export const { @@ -15,9 +15,9 @@ export const { ...session, user: { ...session.user, - id: user.id + id: user.id, }, - }) + }), }, adapter: DrizzleAdapter(db), providers: [ diff --git a/apps/web/src/server/db/index.ts b/apps/web/src/server/db/index.ts index 5aa87fc1..4d671bea 100644 --- a/apps/web/src/server/db/index.ts +++ b/apps/web/src/server/db/index.ts @@ -1,8 +1,5 @@ -import { drizzle } from 'drizzle-orm/d1'; +import { drizzle } from "drizzle-orm/d1"; import * as schema from "./schema"; -export const db = drizzle( - process.env.DATABASE, - { schema, logger: true } -); +export const db = drizzle(process.env.DATABASE, { schema, logger: true }); diff --git a/apps/web/src/server/db/schema.ts b/apps/web/src/server/db/schema.ts index a80eb7cf..e0ddbdbc 100644 --- a/apps/web/src/server/db/schema.ts +++ b/apps/web/src/server/db/schema.ts @@ -6,7 +6,7 @@ import { sqliteTableCreator, text, integer, - unique + unique, } from "drizzle-orm/sqlite-core"; export const createTable = sqliteTableCreator((name) => `${name}`); @@ -78,7 +78,6 @@ export const verificationTokens = createTable( }), ); - export const storedContent = createTable( "storedContent", { @@ -103,8 +102,12 @@ export const storedContent = createTable( export const contentToSpace = createTable( "contentToSpace", { - contentId: integer("contentId").notNull().references(() => storedContent.id), - spaceId: integer("spaceId").notNull().references(() => space.id), + contentId: integer("contentId") + .notNull() + .references(() => storedContent.id), + spaceId: integer("spaceId") + .notNull() + .references(() => space.id), }, (cts) => ({ compoundKey: primaryKey({ columns: [cts.contentId, cts.spaceId] }), @@ -115,7 +118,7 @@ export const space = createTable( "space", { id: integer("id").notNull().primaryKey({ autoIncrement: true }), - name: text('name').notNull().default('all'), + name: text("name").notNull().default("all"), user: text("user", { length: 255 }).references(() => users.id), }, (space) => ({ @@ -124,4 +127,4 @@ export const space = createTable( }), ); -export type StoredContent = Omit<typeof storedContent.$inferSelect, 'user'>
\ No newline at end of file +export type StoredContent = Omit<typeof storedContent.$inferSelect, "user">; diff --git a/apps/web/src/server/helpers.ts b/apps/web/src/server/helpers.ts index 1f6cf977..519e4b17 100644 --- a/apps/web/src/server/helpers.ts +++ b/apps/web/src/server/helpers.ts @@ -1,34 +1,34 @@ export async function getMetaData(url: string) { - const response = await fetch(url); - const html = await response.text(); - - // Extract the base URL - const baseUrl = new URL(url).origin; - - // Extract title - const titleMatch = html.match(/<title>(.*?)<\/title>/); - const title = titleMatch ? titleMatch[1] : 'Title not found'; - - // Extract meta description - const descriptionMatch = html.match( - /<meta name="description" content="(.*?)"\s*\/?>/, - ); - const description = descriptionMatch - ? descriptionMatch[1] - : 'Description not found'; - - // Extract Open Graph image - const imageMatch = html.match( - /<meta property="og:image" content="(.*?)"\s*\/?>/, - ); - const image = imageMatch ? imageMatch[1] : 'Image not found'; - - // Prepare the metadata object - const metadata = { - title, - description, - image, - baseUrl, - }; - return metadata; - }
\ No newline at end of file + const response = await fetch(url); + const html = await response.text(); + + // Extract the base URL + const baseUrl = new URL(url).origin; + + // Extract title + const titleMatch = html.match(/<title>(.*?)<\/title>/); + const title = titleMatch ? titleMatch[1] : "Title not found"; + + // Extract meta description + const descriptionMatch = html.match( + /<meta name="description" content="(.*?)"\s*\/?>/, + ); + const description = descriptionMatch + ? descriptionMatch[1] + : "Description not found"; + + // Extract Open Graph image + const imageMatch = html.match( + /<meta property="og:image" content="(.*?)"\s*\/?>/, + ); + const image = imageMatch ? imageMatch[1] : "Image not found"; + + // Prepare the metadata object + const metadata = { + title, + description, + image, + baseUrl, + }; + return metadata; +} diff --git a/package.json b/package.json index 25710c6b..1e1b4e93 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,9 @@ "lint": "dotenv -- turbo run lint", "start": "dotenv -- turbo run start" }, + "lint-staged": { + "**/*": "prettier --write --ignore-unknown" + }, "devDependencies": { "@types/better-sqlite3": "^7.6.6", "@types/eslint": "^8.44.7", @@ -56,6 +59,8 @@ "framer-motion": "^11.0.6", "html-metadata": "^1.7.1", "html-metadata-parser": "^2.0.4", + "husky": "^9.0.11", + "lint-staged": "^15.2.2", "lucide-react": "^0.343.0", "next-auth": "beta", "nuqs": "^1.17.1", @@ -64,4 +69,4 @@ "tailwind-scrollbar": "^3.1.0", "tailwindcss-animate": "^1.0.7" } -}
\ No newline at end of file +} diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json index 16cf46e4..3ef2dcf7 100644 --- a/packages/tsconfig/base.json +++ b/packages/tsconfig/base.json @@ -1,44 +1,38 @@ { - "compilerOptions": { - "esModuleInterop": true, - "skipLibCheck": true, - "target": "es2022", - "allowJs": true, - "resolveJsonModule": true, - "moduleDetection": "force", - "isolatedModules": true, - "strict": true, - "noUncheckedIndexedAccess": true, - "checkJs": true, - "lib": [ - "dom", - "dom.iterable", - "ES2022" - ], - "noEmit": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "jsx": "preserve", - "plugins": [ - { - "name": "next" - } - ], - "incremental": true, - "baseUrl": "." - }, - "include": [ - "../.eslintrc.cjs", - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - "**/*.cjs", - "**/*.js", - ".next/types/**/*.ts", - "../tailwind.config.ts", - "../prettier.config.js" + "compilerOptions": { + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "checkJs": true, + "lib": ["dom", "dom.iterable", "ES2022"], + "noEmit": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } ], - "exclude": [ - "node_modules" - ] -}
\ No newline at end of file + "incremental": true, + "baseUrl": "." + }, + "include": [ + "../.eslintrc.cjs", + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.cjs", + "**/*.js", + ".next/types/**/*.ts", + "../tailwind.config.ts", + "../prettier.config.js" + ], + "exclude": ["node_modules"] +} diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 3c8eb887..f4735add 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,3 +1,3 @@ { - "name": "tsconfig" -}
\ No newline at end of file + "name": "tsconfig" +} @@ -2,39 +2,23 @@ "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { - "outputs": [ - ".next/**", - "!.next/cache/**" - ] + "outputs": [".next/**", "!.next/cache/**"] }, "deploy": { - "dependsOn": [ - "build", - "test", - "lint" - ] + "dependsOn": ["build", "test", "lint"] }, "db:push": {}, "db:studio": {}, "test": { - "dependsOn": [ - "build" - ], - "inputs": [ - "src/**/*.tsx", - "src/**/*.ts", - "test/**/*.ts", - "test/**/*.tsx" - ] + "dependsOn": ["build"], + "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"] }, "lint": {}, "type-check": {}, "dev": { - "env": [ - "NODE_ENV=development" - ], + "env": ["NODE_ENV=development"], "cache": false, "persistent": true } } -}
\ No newline at end of file +} |