aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorDhravya <[email protected]>2024-04-01 15:29:51 -0700
committerDhravya <[email protected]>2024-04-01 15:29:51 -0700
commitf5887dbbf91dfb2f5ab25975f8fdf0442bd0716c (patch)
treecc249fc4cee270dc259c8c255ae32a727786b6cb /apps
parentMerge branch 'main' of https://github.com/Dhravya/supermemory into new-ui (diff)
parentrename to PageItem (diff)
downloadsupermemory-f5887dbbf91dfb2f5ab25975f8fdf0442bd0716c.tar.xz
supermemory-f5887dbbf91dfb2f5ab25975f8fdf0442bd0716c.zip
Merge branch 'new-ui' of https://github.com/Dhravya/supermemory into new-ui
Diffstat (limited to 'apps')
-rw-r--r--apps/web/package.json11
-rw-r--r--apps/web/pnpm-lock.yaml468
-rw-r--r--apps/web/src/app/globals.css6
-rw-r--r--apps/web/src/app/layout.tsx6
-rw-r--r--apps/web/src/app/ui/page.tsx2
-rw-r--r--apps/web/src/components/Sidebar.tsx145
-rw-r--r--apps/web/src/components/Sidebar/PagesItem.tsx189
-rw-r--r--apps/web/src/components/Sidebar/index.tsx46
-rw-r--r--apps/web/src/components/ui/drawer.tsx118
-rw-r--r--apps/web/src/components/ui/input.tsx18
-rw-r--r--apps/web/src/components/ui/popover.tsx31
-rw-r--r--apps/web/src/components/ui/textarea.tsx24
-rw-r--r--apps/web/src/lib/utils.ts19
-rw-r--r--apps/web/tailwind.config.ts27
14 files changed, 946 insertions, 164 deletions
diff --git a/apps/web/package.json b/apps/web/package.json
index 297de1a7..d80e9de5 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -14,17 +14,20 @@
"prepare-local-db": "drizzle-kit generate:sqlite --out db/prepare.sql && wrangler d1 execute dev-d1-anycontext --local --file=db/prepare.sql"
},
"dependencies": {
- "react": "^18",
- "react-dom": "^18",
- "next": "14.1.0",
"@radix-ui/react-avatar": "^1.0.4",
+ "@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
+ "@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"lucide-react": "^0.338.0",
+ "next": "14.1.0",
+ "react": "^18",
+ "react-dom": "^18",
"tailwind-merge": "^2.2.1",
- "tailwindcss-animate": "^1.0.7"
+ "tailwindcss-animate": "^1.0.7",
+ "vaul": "^0.9.0"
},
"devDependencies": {
"@cloudflare/next-on-pages": "1",
diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml
index a66884c0..583b6b2a 100644
--- a/apps/web/pnpm-lock.yaml
+++ b/apps/web/pnpm-lock.yaml
@@ -8,9 +8,15 @@ dependencies:
'@radix-ui/react-avatar':
specifier: ^1.0.4
+ '@radix-ui/react-dialog':
+ specifier: ^1.0.5
'@radix-ui/react-icons':
specifier: ^1.3.0
version: 1.3.0([email protected])
+ '@radix-ui/react-popover':
+ specifier: ^1.0.7
'@radix-ui/react-slot':
specifier: ^1.0.2
version: 1.0.2(@types/[email protected])([email protected])
@@ -38,6 +44,9 @@ dependencies:
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7([email protected])
+ vaul:
+ specifier: ^0.9.0
devDependencies:
'@cloudflare/next-on-pages':
@@ -497,6 +506,34 @@ packages:
engines: {node: '>=14'}
dev: true
+ /@floating-ui/[email protected]:
+ resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
+ dependencies:
+ '@floating-ui/utils': 0.2.1
+ dev: false
+
+ /@floating-ui/[email protected]:
+ resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==}
+ dependencies:
+ '@floating-ui/core': 1.6.0
+ '@floating-ui/utils': 0.2.1
+ dev: false
+
+ resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+ dependencies:
+ '@floating-ui/dom': 1.6.3
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ dev: false
+
+ /@floating-ui/[email protected]:
+ resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
+ dev: false
+
/@humanwhocodes/[email protected]:
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
engines: {node: '>=10.10.0'}
@@ -693,6 +730,33 @@ packages:
requiresBuild: true
optional: true
+ /@radix-ui/[email protected]:
+ resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
+ dependencies:
+ '@babel/runtime': 7.24.1
+ dev: false
+
+ resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/react-primitive': 1.0.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@types/react': 18.2.73
+ '@types/react-dom': 18.2.23
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ dev: false
+
resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==}
peerDependencies:
@@ -745,6 +809,102 @@ packages:
react: 18.2.0
dev: false
+ resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-context': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-dismissable-layer': 1.0.5(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-focus-guards': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-focus-scope': 1.0.4(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-id': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-portal': 1.0.4(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-presence': 1.0.1(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-primitive': 1.0.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-slot': 1.0.2(@types/[email protected])([email protected])
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ '@types/react-dom': 18.2.23
+ aria-hidden: 1.2.4
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ react-remove-scroll: 2.5.5(@types/[email protected])([email protected])
+ dev: false
+
+ resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-primitive': 1.0.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-use-escape-keydown': 1.0.3(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ '@types/react-dom': 18.2.23
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ dev: false
+
+ resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@types/react': 18.2.73
+ react: 18.2.0
+ dev: false
+
+ resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-primitive': 1.0.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ '@types/react-dom': 18.2.23
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ dev: false
+
resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==}
peerDependencies:
@@ -753,6 +913,129 @@ packages:
react: 18.2.0
dev: false
+ resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ react: 18.2.0
+ dev: false
+
+ resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-context': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-dismissable-layer': 1.0.5(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-focus-guards': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-focus-scope': 1.0.4(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-id': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-popper': 1.1.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-portal': 1.0.4(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-presence': 1.0.1(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-primitive': 1.0.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-slot': 1.0.2(@types/[email protected])([email protected])
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ '@types/react-dom': 18.2.23
+ aria-hidden: 1.2.4
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ react-remove-scroll: 2.5.5(@types/[email protected])([email protected])
+ dev: false
+
+ resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@floating-ui/react-dom': 2.0.8([email protected])([email protected])
+ '@radix-ui/react-arrow': 1.0.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-compose-refs': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-context': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-primitive': 1.0.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-use-rect': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-use-size': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/rect': 1.0.1
+ '@types/react': 18.2.73
+ '@types/react-dom': 18.2.23
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ dev: false
+
+ resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/react-primitive': 1.0.3(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ '@types/react': 18.2.73
+ '@types/react-dom': 18.2.23
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ dev: false
+
+ resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/[email protected])([email protected])
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ '@types/react-dom': 18.2.23
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ dev: false
+
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
@@ -803,6 +1086,36 @@ packages:
react: 18.2.0
dev: false
+ resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ react: 18.2.0
+ dev: false
+
+ resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ react: 18.2.0
+ dev: false
+
resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==}
peerDependencies:
@@ -817,6 +1130,42 @@ packages:
react: 18.2.0
dev: false
+ resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/rect': 1.0.1
+ '@types/react': 18.2.73
+ react: 18.2.0
+ dev: false
+
+ resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.1
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/[email protected])([email protected])
+ '@types/react': 18.2.73
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/[email protected]:
+ resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
+ dependencies:
+ '@babel/runtime': 7.24.1
+ dev: false
+
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
engines: {node: '>= 8.0.0'}
@@ -1280,6 +1629,13 @@ packages:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
dev: true
+ resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
+ engines: {node: '>=10'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
dependencies:
@@ -1812,6 +2168,10 @@ packages:
engines: {node: '>=8'}
dev: true
+ resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+ dev: false
+
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
@@ -2982,6 +3342,11 @@ packages:
hasown: 2.0.2
dev: true
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+ dev: false
+
resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==}
dependencies:
@@ -3225,6 +3590,12 @@ packages:
side-channel: 1.0.6
dev: true
+ resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
+ dependencies:
+ loose-envify: 1.4.0
+ dev: false
+
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
engines: {node: '>= 0.4'}
@@ -4248,6 +4619,58 @@ packages:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: true
+ resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.73
+ react: 18.2.0
+ react-style-singleton: 2.2.1(@types/[email protected])([email protected])
+ tslib: 2.6.2
+ dev: false
+
+ resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.73
+ react: 18.2.0
+ react-remove-scroll-bar: 2.3.6(@types/[email protected])([email protected])
+ react-style-singleton: 2.2.1(@types/[email protected])([email protected])
+ tslib: 2.6.2
+ use-callback-ref: 1.3.2(@types/[email protected])([email protected])
+ use-sidecar: 1.1.2(@types/[email protected])([email protected])
+ dev: false
+
+ resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.73
+ get-nonce: 1.0.1
+ invariant: 2.2.4
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
@@ -5028,6 +5451,37 @@ packages:
punycode: 2.3.1
dev: true
+ resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.73
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
+ resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.73
+ detect-node-es: 1.1.0
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -5041,6 +5495,20 @@ packages:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
dev: true
+ resolution: {integrity: sha512-bZSySGbAHiTXmZychprnX/dE0EsSige88xtyyL3/MCRbrFotRPQZo7UdydGXZWw+CKbNOw5Ow8gwAo93/nB/Cg==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@radix-ui/react-dialog': 1.0.5(@types/[email protected])(@types/[email protected])([email protected])([email protected])
+ react: 18.2.0
+ react-dom: 18.2.0([email protected])
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+ dev: false
+
resolution: {integrity: sha512-9tpC+wAtGSKiZf1TPjXZK6ntQDK0IZPd5xl99Ka91bcNQS9Z/8jJ/IKmTbgKw80z7i769ozMPtKj3/jTRnWuhw==}
engines: {node: '>= 16'}
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css
index 394bda43..2bb3159a 100644
--- a/apps/web/src/app/globals.css
+++ b/apps/web/src/app/globals.css
@@ -20,7 +20,7 @@
}
body {
- @apply bg-rgray-1 text-rgray-11;
+ @apply bg-rgray-2 text-rgray-11;
/* color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
@@ -30,6 +30,10 @@ body {
rgb(var(--background-start-rgb)); */
}
+[vaul-drawer-wrapper] {
+ @apply bg-rgray-1;
+}
+
@layer utilities {
.text-balance {
text-wrap: balance;
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx
index 5464c1d3..5b2ced94 100644
--- a/apps/web/src/app/layout.tsx
+++ b/apps/web/src/app/layout.tsx
@@ -16,7 +16,11 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
- <body className={roboto.className}>{children}</body>
+ <body className={roboto.className}>
+ <div vaul-drawer-wrapper="" className="min-w-screen overflow-x-hidden">
+ {children}
+ </div>
+ </body>
</html>
);
}
diff --git a/apps/web/src/app/ui/page.tsx b/apps/web/src/app/ui/page.tsx
index 2f7c6a4b..43cff341 100644
--- a/apps/web/src/app/ui/page.tsx
+++ b/apps/web/src/app/ui/page.tsx
@@ -1,4 +1,4 @@
-import Sidebar from "@/components/Sidebar";
+import Sidebar from "@/components/Sidebar/index";
export default async function Home() {
return (
diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx
deleted file mode 100644
index 0644d779..00000000
--- a/apps/web/src/components/Sidebar.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-"use client";
-import { StoredContent } from "@/server/db/schema";
-import {
- Plus,
- MoreHorizontal,
- ArrowUpRight,
- Edit3,
- Trash2,
-} from "lucide-react";
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from "./ui/dropdown-menu";
-
-import { useState, useEffect, useRef } from "react";
-
-export default function Sidebar() {
- const websties: StoredContent[] = [
- {
- id: 1,
- content: "",
- title: "Visual Studio Code",
- url: "https://code.visualstudio.com",
- description: "",
- image: "https://code.visualstudio.com/favicon.ico",
- baseUrl: "https://code.visualstudio.com",
- savedAt: new Date(),
- },
- {
- id: 1,
- content: "",
- title: "yxshv/vscode: An unofficial remake of vscode's landing page",
- url: "https://github.com/yxshv/vscode",
- description: "",
- image: "https://github.com/favicon.ico",
- baseUrl: "https://github.com",
- savedAt: new Date(),
- },
- ];
-
- return (
- <aside className="bg-rgray-3 flex h-screen w-[25%] flex-col items-start justify-between py-5 pb-[50vh] font-light">
- <div className="flex items-center justify-center gap-1 px-5 text-xl font-normal">
- <img src="/brain.png" alt="logo" className="h-10 w-10" />
- SuperMemory
- </div>
- <div className="flex w-full flex-col items-start justify-center p-2">
- <h1 className="mb-1 flex w-full items-center justify-center px-3 font-normal">
- Websites
- <button className="ml-auto ">
- <Plus className="h-4 w-4 min-w-4" />
- </button>
- </h1>
- {websties.map((item) => (
- <ListItem key={item.id} item={item} />
- ))}
- </div>
- </aside>
- );
-}
-
-export const ListItem: React.FC<{ item: StoredContent }> = ({ item }) => {
- const [isEditing, setIsEditing] = useState(false);
- const editInputRef = useRef<HTMLInputElement>(null);
-
- useEffect(() => {
- if (isEditing) {
- setTimeout(() => {
- editInputRef.current?.focus();
- }, 500);
- }
- }, [isEditing]);
-
- return (
- <div className="hover:bg-rgray-5 focus-within:bg-rgray-5 flex w-full items-center rounded-full py-1 pl-3 pr-2 transition [&:hover>a>[data-upright-icon]]:block [&:hover>a>img]:hidden [&:hover>button]:opacity-100">
- <a
- href={item.url}
- target="_blank"
- onClick={(e) => isEditing && e.preventDefault()}
- className="flex w-[90%] items-center gap-2 focus:outline-none"
- >
- {isEditing ? (
- <Edit3 className="h-4 w-4" strokeWidth={1.5} />
- ) : (
- <>
- <img
- src={item.image ?? "/brain.png"}
- alt={item.title ?? "Untitiled website"}
- className="h-4 w-4"
- />
- <ArrowUpRight
- data-upright-icon
- className="hidden h-4 w-4 min-w-4 scale-125"
- strokeWidth={1.5}
- />
- </>
- )}
- {isEditing ? (
- <input
- ref={editInputRef}
- autoFocus
- className="text-rgray-12 w-full bg-transparent focus:outline-none"
- placeholder={item.title ?? "Untitled website"}
- onBlur={(e) => setIsEditing(false)}
- onKeyDown={(e) => e.key === "Escape" && setIsEditing(false)}
- />
- ) : (
- <span className="w-full truncate text-nowrap">
- {item.title ?? "Untitled website"}
- </span>
- )}
- </a>
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <button className="ml-auto w-4 min-w-4 rounded-[0.15rem] opacity-0 focus:opacity-100 focus:outline-none">
- <MoreHorizontal className="h-4 w-4 min-w-4" />
- </button>
- </DropdownMenuTrigger>
- <DropdownMenuContent className="w-5">
- <DropdownMenuItem onClick={() => window.open(item.url)}>
- <ArrowUpRight
- className="mr-2 h-4 w-4 scale-125"
- strokeWidth={1.5}
- />
- Open
- </DropdownMenuItem>
- <DropdownMenuItem
- onClick={(e) => {
- setIsEditing(true);
- }}
- >
- <Edit3 className="mr-2 h-4 w-4 " strokeWidth={1.5} />
- Edit
- </DropdownMenuItem>
- <DropdownMenuItem className="focus:bg-red-100 focus:text-red-400 dark:focus:bg-red-100/10">
- <Trash2 className="mr-2 h-4 w-4 " strokeWidth={1.5} />
- Delete
- </DropdownMenuItem>
- </DropdownMenuContent>
- </DropdownMenu>
- </div>
- );
-};
diff --git a/apps/web/src/components/Sidebar/PagesItem.tsx b/apps/web/src/components/Sidebar/PagesItem.tsx
new file mode 100644
index 00000000..ce762ae5
--- /dev/null
+++ b/apps/web/src/components/Sidebar/PagesItem.tsx
@@ -0,0 +1,189 @@
+"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";
+import {
+ ArrowUpRight,
+ MoreHorizontal,
+ Edit3,
+ Trash2,
+ Save,
+ ChevronRight,
+ Plus,
+} from "lucide-react";
+import { useState } from "react";
+import {
+ Drawer,
+ DrawerContent,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerClose,
+} from "../ui/drawer";
+import { Input } from "../ui/input";
+import { Textarea } from "../ui/textarea";
+import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
+
+export const PageItem: React.FC<{ item: StoredContent }> = ({ item }) => {
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+ const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false);
+
+ return (
+ <div className="hover:bg-rgray-5 focus-within:bg-rgray-5 flex w-full items-center rounded-full py-1 pl-3 pr-2 transition [&:hover>a>div>[data-upright-icon]]:scale-125 [&:hover>a>div>[data-upright-icon]]:opacity-100 [&:hover>a>div>[data-upright-icon]]:delay-150 [&:hover>a>div>img]:scale-75 [&:hover>a>div>img]:opacity-0 [&:hover>a>div>img]:delay-0 [&:hover>button]:opacity-100">
+ <a
+ href={item.url}
+ target="_blank"
+ className="flex w-[90%] items-center gap-2 focus-visible:outline-none"
+ >
+ <div className="relative h-4 min-w-4">
+ <img
+ 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
+ data-upright-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"
+ strokeWidth={1.5}
+ />
+ </div>
+
+ <span className="w-full truncate text-nowrap">
+ {item.title ?? "Untitled website"}
+ </span>
+ </a>
+ <DropdownMenu open={isDropdownOpen} onOpenChange={setIsDropdownOpen}>
+ <DropdownMenuTrigger asChild>
+ <button className="ml-auto w-4 min-w-4 rounded-[0.15rem] opacity-0 focus-visible:opacity-100 focus-visible:outline-none">
+ <MoreHorizontal className="h-4 w-4 min-w-4" />
+ </button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent className="w-5">
+ <DropdownMenuItem onClick={() => window.open(item.url)}>
+ <ArrowUpRight
+ className="mr-2 h-4 w-4 scale-125"
+ strokeWidth={1.5}
+ />
+ Open
+ </DropdownMenuItem>
+ <DropdownMenuItem
+ onClick={() => {
+ setIsDropdownOpen(false);
+ setIsEditDrawerOpen(true);
+ }}
+ >
+ <Edit3 className="mr-2 h-4 w-4" strokeWidth={1.5} />
+ Edit
+ </DropdownMenuItem>
+ <DropdownMenuItem className="focus-visible:bg-red-100 focus-visible:text-red-400 dark:focus-visible:bg-red-100/10">
+ <Trash2 className="mr-2 h-4 w-4" strokeWidth={1.5} />
+ Delete
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ <Drawer
+ shouldScaleBackground
+ open={isEditDrawerOpen}
+ onOpenChange={setIsEditDrawerOpen}
+ >
+ <DrawerContent className="pb-10 lg:px-[25vw]">
+ <DrawerHeader className="relative mt-10 px-0">
+ <DrawerTitle className=" flex w-full justify-between">
+ Edit Page Details
+ </DrawerTitle>
+ <DrawerDescription>Change the page details</DrawerDescription>
+ <a
+ target="_blank"
+ 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" />
+ {cleanUrl(item.url)}
+ </a>
+ </DrawerHeader>
+
+ <div className="mt-5">
+ <Label>Title</Label>
+ <Input
+ className=""
+ required
+ 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"}
+ />
+ </div>
+ <DrawerFooter className="flex flex-row-reverse items-center justify-end px-0 pt-5">
+ <DrawerClose className="flex items-center justify-center rounded-md px-3 py-2 ring-2 ring-transparent transition hover:bg-blue-100 hover:text-blue-400 focus-visible:bg-blue-100 focus-visible:text-blue-400 focus-visible:outline-none focus-visible:ring-blue-200 dark:hover:bg-blue-100/10 dark:focus-visible:bg-blue-100/10 dark:focus-visible:ring-blue-200/30">
+ <Save className="mr-2 h-4 w-4 " strokeWidth={1.5} />
+ Save
+ </DrawerClose>
+ <DrawerClose className="hover:bg-rgray-3 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 flex items-center justify-center rounded-md px-3 py-2 ring-2 ring-transparent transition focus-visible:outline-none">
+ Cancel
+ </DrawerClose>
+ <DrawerClose className="mr-auto flex items-center justify-center rounded-md bg-red-100 px-3 py-2 text-red-400 ring-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-red-200 dark:bg-red-100/10 dark:focus-visible:ring-red-200/30">
+ <Trash2 className="mr-2 h-4 w-4 " strokeWidth={1.5} />
+ Delete
+ </DrawerClose>
+ </DrawerFooter>
+ </DrawerContent>
+ </Drawer>
+ </div>
+ );
+};
+
+export const AddNewPagePopover: React.FC<{
+ addNewUrl?: (url: string) => Promise<void>;
+}> = ({ addNewUrl }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [url, setUrl] = useState("");
+
+ return (
+ <Popover open={isOpen} onOpenChange={setIsOpen}>
+ <PopoverTrigger asChild>
+ <button className="focus-visible:ring-rgray-7 ring-offset-rgray-3 ml-auto rounded-sm ring-2 ring-transparent ring-offset-2 focus-visible:outline-none">
+ <Plus className="h-4 w-4 min-w-4" />
+ </button>
+ </PopoverTrigger>
+ <PopoverContent align="start" side="top">
+ <h1 className="mb-2 flex items-center justify-between ">
+ Add a new page
+ <button
+ onClick={() => {
+ setIsOpen(false);
+ addNewUrl?.(url);
+ }}
+ className="hover:bg-rgray-3 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 ring-offset-rgray-3 rounded-sm ring-2 ring-transparent ring-offset-2 transition focus-visible:outline-none"
+ >
+ <ChevronRight className="h-4 w-4" />
+ </button>
+ </h1>
+ <Input
+ className="w-full"
+ autoFocus
+ onChange={(e) => setUrl(e.target.value)}
+ onKeyDown={(e) => {
+ if (e.key === "Enter") {
+ setIsOpen(false);
+ addNewUrl?.(url);
+ }
+ }}
+ placeholder="Enter the URL of the page"
+ />
+ </PopoverContent>
+ </Popover>
+ );
+};
diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx
new file mode 100644
index 00000000..80d4beb5
--- /dev/null
+++ b/apps/web/src/components/Sidebar/index.tsx
@@ -0,0 +1,46 @@
+"use server";
+import { StoredContent } from "@/server/db/schema";
+import { AddNewPagePopover, PageItem } from "./PagesItem";
+
+export default async function Sidebar() {
+ 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",
+ savedAt: new Date(),
+ },
+ {
+ id: 2,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ ];
+
+ return (
+ <aside className="bg-rgray-3 flex h-screen w-[25%] flex-col items-start justify-between py-5 pb-[50vh] font-light">
+ <div className="flex items-center justify-center gap-1 px-5 text-xl font-normal">
+ <img src="/brain.png" alt="logo" className="h-10 w-10" />
+ SuperMemory
+ </div>
+ <div className="flex w-full flex-col items-start justify-center p-2">
+ <h1 className="mb-1 flex w-full items-center justify-center px-3 font-normal">
+ Pages
+ <AddNewPagePopover />
+ </h1>
+ {pages.map((item) => (
+ <PageItem key={item.id} item={item} />
+ ))}
+ </div>
+ </aside>
+ );
+}
diff --git a/apps/web/src/components/ui/drawer.tsx b/apps/web/src/components/ui/drawer.tsx
new file mode 100644
index 00000000..705ca01c
--- /dev/null
+++ b/apps/web/src/components/ui/drawer.tsx
@@ -0,0 +1,118 @@
+"use client";
+
+import * as React from "react";
+import { Drawer as DrawerPrimitive } from "vaul";
+
+import { cn } from "@/lib/utils";
+
+const Drawer = ({
+ shouldScaleBackground = true,
+ ...props
+}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
+ <DrawerPrimitive.Root
+ shouldScaleBackground={shouldScaleBackground}
+ {...props}
+ />
+);
+Drawer.displayName = "Drawer";
+
+const DrawerTrigger = DrawerPrimitive.Trigger;
+
+const DrawerPortal = DrawerPrimitive.Portal;
+
+const DrawerClose = DrawerPrimitive.Close;
+
+const DrawerOverlay = React.forwardRef<
+ React.ElementRef<typeof DrawerPrimitive.Overlay>,
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
+>(({ className, ...props }, ref) => (
+ <DrawerPrimitive.Overlay
+ ref={ref}
+ className={cn("fixed inset-0 z-50 bg-black/80", className)}
+ {...props}
+ />
+));
+DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
+
+const DrawerContent = React.forwardRef<
+ React.ElementRef<typeof DrawerPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
+>(({ className, children, ...props }, ref) => (
+ <DrawerPortal>
+ <DrawerOverlay />
+ <DrawerPrimitive.Content
+ ref={ref}
+ className={cn(
+ "border-rgray-6 bg-rgray-2 fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border",
+ className,
+ )}
+ {...props}
+ >
+ <div className="bg-rgray-4 mx-auto mt-4 h-2 w-[100px] rounded-full " />
+ {children}
+ </DrawerPrimitive.Content>
+ </DrawerPortal>
+));
+DrawerContent.displayName = "DrawerContent";
+
+const DrawerHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes<HTMLDivElement>) => (
+ <div
+ className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
+ {...props}
+ />
+);
+DrawerHeader.displayName = "DrawerHeader";
+
+const DrawerFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes<HTMLDivElement>) => (
+ <div
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
+ {...props}
+ />
+);
+DrawerFooter.displayName = "DrawerFooter";
+
+const DrawerTitle = React.forwardRef<
+ React.ElementRef<typeof DrawerPrimitive.Title>,
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
+>(({ className, ...props }, ref) => (
+ <DrawerPrimitive.Title
+ ref={ref}
+ className={cn(
+ "text-rgray-12 text-xl font-medium leading-none tracking-tight",
+ className,
+ )}
+ {...props}
+ />
+));
+DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
+
+const DrawerDescription = React.forwardRef<
+ React.ElementRef<typeof DrawerPrimitive.Description>,
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
+>(({ className, ...props }, ref) => (
+ <DrawerPrimitive.Description
+ ref={ref}
+ className={cn("text-rgray-11 text-md", className)}
+ {...props}
+ />
+));
+DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
+
+export {
+ Drawer,
+ DrawerPortal,
+ DrawerOverlay,
+ DrawerTrigger,
+ DrawerClose,
+ DrawerContent,
+ DrawerHeader,
+ DrawerFooter,
+ DrawerTitle,
+ DrawerDescription,
+};
diff --git a/apps/web/src/components/ui/input.tsx b/apps/web/src/components/ui/input.tsx
index aae15c80..8a7a9340 100644
--- a/apps/web/src/components/ui/input.tsx
+++ b/apps/web/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> {}
@@ -11,15 +11,15 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
className={cn(
- "flex h-10 w-full rounded-md border border-gray-200 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-800 dark:bg-gray-950 dark:ring-offset-gray-950 dark:placeholder:text-gray-400 dark:focus-visible:ring-gray-300",
- className
+ "border-rgray-6 text-rgray-12 ring-offset-rgray-2 placeholder:text-rgray-11 focus-visible:ring-rgray-7 flex h-10 w-full rounded-md border bg-transparent px-3 py-2 text-sm transition file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ",
+ className,
)}
ref={ref}
{...props}
/>
- )
- }
-)
-Input.displayName = "Input"
+ );
+ },
+);
+Input.displayName = "Input";
-export { Input }
+export { Input };
diff --git a/apps/web/src/components/ui/popover.tsx b/apps/web/src/components/ui/popover.tsx
new file mode 100644
index 00000000..0c4563a8
--- /dev/null
+++ b/apps/web/src/components/ui/popover.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import * as React from "react";
+import * as PopoverPrimitive from "@radix-ui/react-popover";
+
+import { cn } from "@/lib/utils";
+
+const Popover = PopoverPrimitive.Root;
+
+const PopoverTrigger = PopoverPrimitive.Trigger;
+
+const PopoverContent = React.forwardRef<
+ React.ElementRef<typeof PopoverPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+ <PopoverPrimitive.Portal>
+ <PopoverPrimitive.Content
+ ref={ref}
+ align={align}
+ sideOffset={sideOffset}
+ className={cn(
+ "border-rgray-6 bg-rgray-3 text-rgray-11 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-none",
+ className,
+ )}
+ {...props}
+ />
+ </PopoverPrimitive.Portal>
+));
+PopoverContent.displayName = PopoverPrimitive.Content.displayName;
+
+export { Popover, PopoverTrigger, PopoverContent };
diff --git a/apps/web/src/components/ui/textarea.tsx b/apps/web/src/components/ui/textarea.tsx
new file mode 100644
index 00000000..68d8e79e
--- /dev/null
+++ b/apps/web/src/components/ui/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
+
+const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
+ ({ className, ...props }, ref) => {
+ return (
+ <textarea
+ className={cn(
+ "border-rgray-6 ring-offset-rgray-2 placeholder:text-rgray-11 focus-visible:ring-rgray-7 flex min-h-[80px] w-full rounded-md border bg-transparent px-3 py-2 text-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
+ className,
+ )}
+ ref={ref}
+ {...props}
+ />
+ );
+ },
+);
+Textarea.displayName = "Textarea";
+
+export { Textarea };
diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts
index d084ccad..6b8f3fd9 100644
--- a/apps/web/src/lib/utils.ts
+++ b/apps/web/src/lib/utils.ts
@@ -1,6 +1,19 @@
-import { type ClassValue, clsx } from "clsx"
-import { twMerge } from "tailwind-merge"
+"use client";
+import { type ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs))
+ return twMerge(clsx(inputs));
+}
+
+// removes http(s?):// and / from the url
+export function cleanUrl(url: string) {
+ if (url.endsWith("/")) {
+ url = url.slice(0, -1);
+ }
+ return url.startsWith("https://")
+ ? url.slice(8)
+ : url.startsWith("http://")
+ ? url.slice(7)
+ : url;
}
diff --git a/apps/web/tailwind.config.ts b/apps/web/tailwind.config.ts
index e7d6dd55..19cb25be 100644
--- a/apps/web/tailwind.config.ts
+++ b/apps/web/tailwind.config.ts
@@ -8,6 +8,32 @@ const config: Config = {
],
theme: {
extend: {
+ keyframes: {
+ "scale-in": {
+ "0%": {
+ transform: "scale(0)",
+ transformOrigin: "var(--radix-popover-content-transform-origin)",
+ },
+ "100%": {
+ transform: "scale(1)",
+ transformOrigin: "var(--radix-popover-content-transform-origin)",
+ },
+ },
+ "scale-out": {
+ "0%": {
+ transform: "scale(1)",
+ transformOrigin: "var(--radix-popover-content-transform-origin)",
+ },
+ "100%": {
+ transform: "scale(0)",
+ transformOrigin: "var(--radix-popover-content-transform-origin)",
+ },
+ },
+ },
+ animation: {
+ "scale-in": "scale-in 0.2s cubic-bezier(0.16, 1, 0.3, 1)",
+ "scale-out": "scale-out 0.2s cubic-bezier(0.16, 1, 0.3, 1)",
+ },
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
@@ -31,6 +57,7 @@ const config: Config = {
},
},
},
+ darkMode: "class",
plugins: [require("tailwindcss-animate")],
};
export default config;