diff --git a/apps/landing/README.md b/apps/landing/README.md
new file mode 100644
index 00000000..c4033664
--- /dev/null
+++ b/apps/landing/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/apps/landing/app/favicon.ico b/apps/landing/app/favicon.ico
new file mode 100644
index 00000000..750e3c04
Binary files /dev/null and b/apps/landing/app/favicon.ico differ
diff --git a/apps/landing/app/globals.css b/apps/landing/app/globals.css
new file mode 100644
index 00000000..8abdb15c
--- /dev/null
+++ b/apps/landing/app/globals.css
@@ -0,0 +1,76 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 222.2 84% 4.9%;
+
+ --radius: 0.5rem;
+ }
+
+ .dark {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 212.7 26.8% 83.9%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
diff --git a/apps/landing/app/layout.tsx b/apps/landing/app/layout.tsx
new file mode 100644
index 00000000..331c8b11
--- /dev/null
+++ b/apps/landing/app/layout.tsx
@@ -0,0 +1,25 @@
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+
+import "./globals.css";
+
+import React from "react";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: "Hoarder",
+ description: "Your AI powered second brain",
+};
+
+export default async function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/apps/web/components/landing/LandingPage.tsx b/apps/landing/app/page.tsx
similarity index 95%
rename from apps/web/components/landing/LandingPage.tsx
rename to apps/landing/app/page.tsx
index cf8e8abd..d87962bb 100644
--- a/apps/web/components/landing/LandingPage.tsx
+++ b/apps/landing/app/page.tsx
@@ -1,11 +1,10 @@
import Image from "next/image";
import Link from "next/link";
+import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
-import screenshot from "@/public/landing/screenshot.png";
+import screenshot from "@/public/screenshot.png";
import { ExternalLink, Github, PackageOpen } from "lucide-react";
-import { Button, buttonVariants } from "../ui/button";
-
const GITHUB_LINK = "https://github.com/MohamedBassem/hoarder-app";
function NavBar() {
diff --git a/apps/landing/components.json b/apps/landing/components.json
new file mode 100644
index 00000000..fa674c93
--- /dev/null
+++ b/apps/landing/components.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "app/globals.css",
+ "baseColor": "slate",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils"
+ }
+}
diff --git a/apps/landing/components/ui/button.tsx b/apps/landing/components/ui/button.tsx
new file mode 100644
index 00000000..5ed8df91
--- /dev/null
+++ b/apps/landing/components/ui/button.tsx
@@ -0,0 +1,56 @@
+import type { VariantProps } from "class-variance-authority";
+import * as React from "react";
+import { cn } from "@/lib/utils";
+import { Slot } from "@radix-ui/react-slot";
+import { cva } from "class-variance-authority";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-md px-3",
+ lg: "h-11 rounded-md px-8",
+ icon: "size-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ },
+);
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean;
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button";
+ return (
+
+ );
+ },
+);
+Button.displayName = "Button";
+
+export { Button, buttonVariants };
diff --git a/apps/landing/lib/utils.ts b/apps/landing/lib/utils.ts
new file mode 100644
index 00000000..88283f01
--- /dev/null
+++ b/apps/landing/lib/utils.ts
@@ -0,0 +1,7 @@
+import type { ClassValue } from "clsx";
+import { clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/apps/landing/next-env.d.ts b/apps/landing/next-env.d.ts
new file mode 100644
index 00000000..4f11a03d
--- /dev/null
+++ b/apps/landing/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/apps/landing/next.config.mjs b/apps/landing/next.config.mjs
new file mode 100644
index 00000000..743e4d69
--- /dev/null
+++ b/apps/landing/next.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ /** We already do linting and typechecking as separate tasks in CI */
+ eslint: { ignoreDuringBuilds: true },
+ typescript: { ignoreBuildErrors: true },
+};
+
+export default nextConfig;
diff --git a/apps/landing/package.json b/apps/landing/package.json
new file mode 100644
index 00000000..565b7a23
--- /dev/null
+++ b/apps/landing/package.json
@@ -0,0 +1,49 @@
+{
+ "$schema": "https://json.schemastore.org/package.json",
+ "name": "@hoarder/landing",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "next dev",
+ "clean": "git clean -xdf .next .turbo node_modules",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint",
+ "typecheck": "tsc --noEmit",
+ "format": "prettier --check . --ignore-path ../../.gitignore"
+ },
+ "dependencies": {
+ "@radix-ui/react-slot": "^1.0.2",
+ "class-variance-authority": "^0.7.0",
+ "clsx": "^2.1.0",
+ "lucide-react": "^0.330.0",
+ "next": "14.1.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-select": "^5.8.0",
+ "tailwind-merge": "^2.2.1",
+ "tailwindcss-animate": "^1.0.7"
+ },
+ "devDependencies": {
+ "@hoarder/eslint-config": "workspace:^0.2.0",
+ "@hoarder/prettier-config": "workspace:^0.1.0",
+ "@hoarder/tailwind-config": "workspace:^0.1.0",
+ "@hoarder/tsconfig": "workspace:^0.1.0",
+ "@tailwindcss/typography": "^0.5.10",
+ "@types/react": "^18.2.55",
+ "@types/react-dom": "^18.2.19",
+ "autoprefixer": "^10.4.17",
+ "postcss": "^8.4.35",
+ "tailwindcss": "^3.4.1"
+ },
+ "eslintConfig": {
+ "root": true,
+ "extends": [
+ "@hoarder/eslint-config/base",
+ "@hoarder/eslint-config/nextjs",
+ "@hoarder/eslint-config/react"
+ ]
+ },
+ "prettier": "@hoarder/prettier-config"
+}
diff --git a/apps/landing/postcss.config.cjs b/apps/landing/postcss.config.cjs
new file mode 100644
index 00000000..12a703d9
--- /dev/null
+++ b/apps/landing/postcss.config.cjs
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/apps/landing/public/app-store-badge.png b/apps/landing/public/app-store-badge.png
new file mode 100644
index 00000000..059e2133
Binary files /dev/null and b/apps/landing/public/app-store-badge.png differ
diff --git a/apps/landing/public/extension-badge.png b/apps/landing/public/extension-badge.png
new file mode 100644
index 00000000..224c12bf
Binary files /dev/null and b/apps/landing/public/extension-badge.png differ
diff --git a/apps/landing/public/screenshot.png b/apps/landing/public/screenshot.png
new file mode 100644
index 00000000..d4403659
Binary files /dev/null and b/apps/landing/public/screenshot.png differ
diff --git a/apps/landing/tailwind.config.ts b/apps/landing/tailwind.config.ts
new file mode 100644
index 00000000..521ba51c
--- /dev/null
+++ b/apps/landing/tailwind.config.ts
@@ -0,0 +1,89 @@
+import type { Config } from "tailwindcss";
+
+const config = {
+ darkMode: ["class"],
+ content: [
+ "./pages/**/*.{ts,tsx}",
+ "./components/**/*.{ts,tsx}",
+ "./app/**/*.{ts,tsx}",
+ "./src/**/*.{ts,tsx}",
+ ],
+ prefix: "",
+ theme: {
+ container: {
+ center: true,
+ padding: "2rem",
+ screens: {
+ "2xl": "1400px",
+ },
+ },
+ extend: {
+ colors: {
+ border: "hsl(var(--border))",
+ input: "hsl(var(--input))",
+ ring: "hsl(var(--ring))",
+ background: "hsl(var(--background))",
+ foreground: "hsl(var(--foreground))",
+ primary: {
+ DEFAULT: "hsl(var(--primary))",
+ foreground: "hsl(var(--primary-foreground))",
+ },
+ secondary: {
+ DEFAULT: "hsl(var(--secondary))",
+ foreground: "hsl(var(--secondary-foreground))",
+ },
+ destructive: {
+ DEFAULT: "hsl(var(--destructive))",
+ foreground: "hsl(var(--destructive-foreground))",
+ },
+ muted: {
+ DEFAULT: "hsl(var(--muted))",
+ foreground: "hsl(var(--muted-foreground))",
+ },
+ accent: {
+ DEFAULT: "hsl(var(--accent))",
+ foreground: "hsl(var(--accent-foreground))",
+ },
+ popover: {
+ DEFAULT: "hsl(var(--popover))",
+ foreground: "hsl(var(--popover-foreground))",
+ },
+ card: {
+ DEFAULT: "hsl(var(--card))",
+ foreground: "hsl(var(--card-foreground))",
+ },
+ },
+ borderRadius: {
+ lg: "var(--radius)",
+ md: "calc(var(--radius) - 2px)",
+ sm: "calc(var(--radius) - 4px)",
+ },
+ keyframes: {
+ "accordion-down": {
+ from: { height: "0" },
+ to: { height: "var(--radix-accordion-content-height)" },
+ },
+ "accordion-up": {
+ from: { height: "var(--radix-accordion-content-height)" },
+ to: { height: "0" },
+ },
+ "pulse-border": {
+ "0%, 100%": {
+ "box-shadow": "0 0 0 0 gray",
+ },
+ "50%": {
+ "box-shadow": "0 0 0 2px gray",
+ },
+ },
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
+ "pulse-border": "pulse-border 1s ease-in-out infinite",
+ },
+ },
+ },
+ plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
+} satisfies Config;
+
+export default config;
diff --git a/apps/landing/tsconfig.json b/apps/landing/tsconfig.json
new file mode 100644
index 00000000..db90cf17
--- /dev/null
+++ b/apps/landing/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@hoarder/tsconfig/base.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx
index d86c91be..c5d57e8a 100644
--- a/apps/web/app/page.tsx
+++ b/apps/web/app/page.tsx
@@ -1,13 +1,11 @@
import { redirect } from "next/navigation";
-import LandingPage from "@/components/landing/LandingPage";
import { getServerAuthSession } from "@/server/auth";
export default async function Home() {
- // TODO: Home currently just redirects between pages until we build a proper landing page
const session = await getServerAuthSession();
if (session) {
redirect("/dashboard/bookmarks");
+ } else {
+ redirect("/signin");
}
-
- return ;
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 024ca436..97aabd3b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -135,6 +135,70 @@ importers:
specifier: ^5.1.0
version: 5.1.4(@types/node@20.11.20)
+ apps/landing:
+ dependencies:
+ '@radix-ui/react-slot':
+ specifier: ^1.0.2
+ version: 1.0.2(@types/react@18.2.58)(react@18.2.0)
+ class-variance-authority:
+ specifier: ^0.7.0
+ version: 0.7.0
+ clsx:
+ specifier: ^2.1.0
+ version: 2.1.0
+ lucide-react:
+ specifier: ^0.330.0
+ version: 0.330.0(react@18.2.0)
+ next:
+ specifier: 14.1.1
+ version: 14.1.1(@babel/core@7.24.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ react:
+ specifier: ^18.2.0
+ version: 18.2.0
+ react-dom:
+ specifier: ^18.2.0
+ version: 18.2.0(react@18.2.0)
+ react-select:
+ specifier: ^5.8.0
+ version: 5.8.0(@types/react@18.2.58)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ tailwind-merge:
+ specifier: ^2.2.1
+ version: 2.2.1
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@3.4.1)
+ devDependencies:
+ '@hoarder/eslint-config':
+ specifier: workspace:^0.2.0
+ version: link:../../tooling/eslint
+ '@hoarder/prettier-config':
+ specifier: workspace:^0.1.0
+ version: link:../../tooling/prettier
+ '@hoarder/tailwind-config':
+ specifier: workspace:^0.1.0
+ version: link:../../tooling/tailwind
+ '@hoarder/tsconfig':
+ specifier: workspace:^0.1.0
+ version: link:../../tooling/typescript
+ '@tailwindcss/typography':
+ specifier: ^0.5.10
+ version: 0.5.10(tailwindcss@3.4.1)
+ '@types/react':
+ specifier: ^18.2.55
+ version: 18.2.58
+ '@types/react-dom':
+ specifier: ^18.2.19
+ version: 18.2.19
+ autoprefixer:
+ specifier: ^10.4.17
+ version: 10.4.17(postcss@8.4.35)
+ postcss:
+ specifier: ^8.4.35
+ version: 8.4.35
+ tailwindcss:
+ specifier: ^3.4.1
+ version: 3.4.1
+
apps/mobile:
dependencies:
'@hoarder/trpc':
@@ -432,10 +496,10 @@ importers:
version: 3.4.1
vite-tsconfig-paths:
specifier: ^4.3.1
- version: 4.3.1
+ version: 4.3.1(typescript@5.3.3)
vitest:
specifier: ^1.3.1
- version: 1.3.1
+ version: 1.3.1(@types/node@20.11.20)
apps/workers:
dependencies:
@@ -26588,9 +26652,6 @@ snapshots:
ts-interface-checker@0.1.13: {}
- tsconfck@3.0.2:
- dev: true
-
tsconfck@3.0.2(typescript@5.3.3):
dependencies:
typescript: 5.3.3
@@ -27033,24 +27094,6 @@ snapshots:
video-extensions@1.2.0:
dev: false
- vite-node@1.3.1:
- dependencies:
- cac: 6.7.14
- debug: 4.3.4
- pathe: 1.1.2
- picocolors: 1.0.0
- vite: 5.1.4
- transitivePeerDependencies:
- - '@types/node'
- - less
- - lightningcss
- - sass
- - stylus
- - sugarss
- - supports-color
- - terser
- dev: true
-
vite-node@1.3.1(@types/node@20.11.20):
dependencies:
cac: 6.7.14
@@ -27069,16 +27112,6 @@ snapshots:
- terser
dev: true
- vite-tsconfig-paths@4.3.1:
- dependencies:
- debug: 4.3.4
- globrex: 0.1.2
- tsconfck: 3.0.2
- transitivePeerDependencies:
- - supports-color
- - typescript
- dev: true
-
vite-tsconfig-paths@4.3.1(typescript@5.3.3):
dependencies:
debug: 4.3.4
@@ -27089,15 +27122,6 @@ snapshots:
- typescript
dev: true
- vite@5.1.4:
- dependencies:
- esbuild: 0.19.12
- postcss: 8.4.35
- rollup: 4.12.0
- optionalDependencies:
- fsevents: 2.3.3
- dev: true
-
vite@5.1.4(@types/node@20.11.20):
dependencies:
'@types/node': 20.11.20
@@ -27108,38 +27132,6 @@ snapshots:
fsevents: 2.3.3
dev: true
- vitest@1.3.1:
- dependencies:
- '@vitest/expect': 1.3.1
- '@vitest/runner': 1.3.1
- '@vitest/snapshot': 1.3.1
- '@vitest/spy': 1.3.1
- '@vitest/utils': 1.3.1
- acorn-walk: 8.3.2
- chai: 4.4.1
- debug: 4.3.4
- execa: 8.0.1
- local-pkg: 0.5.0
- magic-string: 0.30.7
- pathe: 1.1.2
- picocolors: 1.0.0
- std-env: 3.7.0
- strip-literal: 2.0.0
- tinybench: 2.6.0
- tinypool: 0.8.2
- vite: 5.1.4
- vite-node: 1.3.1
- why-is-node-running: 2.2.2
- transitivePeerDependencies:
- - less
- - lightningcss
- - sass
- - stylus
- - sugarss
- - supports-color
- - terser
- dev: true
-
vitest@1.3.1(@types/node@20.11.20):
dependencies:
'@types/node': 20.11.20