aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/web/package.json1
-rw-r--r--packages/web/src/app/_components/post.tsx19
-rw-r--r--packages/web/src/app/auth/sign-in/page.tsx50
-rw-r--r--packages/web/src/app/auth/sign-up/page.tsx65
-rw-r--r--packages/web/src/app/dashboard/dashboard-content.tsx122
-rw-r--r--packages/web/src/app/dashboard/page.tsx13
-rw-r--r--packages/web/src/app/layout.tsx14
-rw-r--r--packages/web/src/app/page.tsx88
-rw-r--r--packages/web/src/server/api/root.ts2
-rw-r--r--packages/web/src/server/api/routers/memory.ts80
-rw-r--r--packages/web/src/styles/globals.css35
-rw-r--r--pnpm-lock.yaml3
12 files changed, 371 insertions, 121 deletions
diff --git a/packages/web/package.json b/packages/web/package.json
index b140111..293ee85 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -18,6 +18,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
+ "@imemio/sdk": "workspace:*",
"@supabase/ssr": "^0.8.0",
"@supabase/supabase-js": "^2.94.0",
"@t3-oss/env-nextjs": "^0.12.0",
diff --git a/packages/web/src/app/_components/post.tsx b/packages/web/src/app/_components/post.tsx
index 9a1251f..5fb8ba8 100644
--- a/packages/web/src/app/_components/post.tsx
+++ b/packages/web/src/app/_components/post.tsx
@@ -15,34 +15,37 @@ export function LatestPost() {
});
return (
- <div className="w-full max-w-xs">
+ <div className="w-full max-w-sm">
{latestPost ? (
- <p className="truncate">Your most recent post: {latestPost.name}</p>
+ <p className="truncate text-[#999999]">
+ your most recent post:{" "}
+ <span className="text-white">{latestPost.name}</span>
+ </p>
) : (
- <p>You have no posts yet.</p>
+ <p className="text-[#666666]">you have no posts yet.</p>
)}
<form
- className="flex flex-col gap-2"
+ className="mt-3 flex flex-col gap-3"
onSubmit={(formSubmitEvent) => {
formSubmitEvent.preventDefault();
createPost.mutate({ name });
}}
>
<input
- className="w-full rounded-full bg-white/10 px-4 py-2 text-white"
+ className="w-full border border-[#2a2a2a] bg-[#0f0f0f] px-3 py-2 text-white placeholder:text-[#666666] focus:border-[#666666]"
onChange={(inputChangeEvent) =>
setName(inputChangeEvent.target.value)
}
- placeholder="Title"
+ placeholder="title"
type="text"
value={name}
/>
<button
- className="rounded-full bg-white/10 px-10 py-3 font-semibold transition hover:bg-white/20"
+ className="w-full border border-[#2a2a2a] bg-[#0f0f0f] px-4 py-2 text-white transition-colors duration-100 hover:border-[#666666] disabled:text-[#666666] disabled:hover:border-[#2a2a2a]"
disabled={createPost.isPending}
type="submit"
>
- {createPost.isPending ? "Submitting..." : "Submit"}
+ {createPost.isPending ? "submitting ..." : "submit"}
</button>
</form>
</div>
diff --git a/packages/web/src/app/auth/sign-in/page.tsx b/packages/web/src/app/auth/sign-in/page.tsx
index 3c4c10a..2bf6767 100644
--- a/packages/web/src/app/auth/sign-in/page.tsx
+++ b/packages/web/src/app/auth/sign-in/page.tsx
@@ -1,5 +1,6 @@
"use client";
+import Link from "next/link";
import { useState } from "react";
import { createClient } from "~/lib/supabase/client";
@@ -32,57 +33,64 @@ export default function SignInPage() {
}
return (
- <div className="flex min-h-screen items-center justify-center">
- <div className="w-full max-w-md space-y-8 p-8">
- <h1 className="text-center text-2xl font-bold">Sign In</h1>
+ <main className="flex min-h-screen flex-col items-center justify-center bg-[#070707]">
+ <div className="w-full max-w-sm px-4">
+ <h1 className="mb-8 text-center text-2xl tracking-tight text-white">
+ <span className="text-[#999999]">&gt;</span> sign in
+ </h1>
- <form className="space-y-6" onSubmit={handleSubmit}>
- <div>
- <label className="block text-sm font-medium" htmlFor="email">
- Email
+ <form className="flex flex-col gap-4" onSubmit={handleSubmit}>
+ <div className="flex flex-col gap-2">
+ <label className="text-sm text-[#666666]" htmlFor="email">
+ email
</label>
<input
- className="mt-1 block w-full rounded-md border px-3 py-2"
+ className="w-full border border-[#2a2a2a] bg-[#0f0f0f] px-3 py-2 text-white placeholder:text-[#666666] focus:border-[#666666]"
id="email"
onChange={(event) => setEmail(event.target.value)}
+ placeholder="[email protected]"
required
type="email"
value={email}
/>
</div>
- <div>
- <label className="block text-sm font-medium" htmlFor="password">
- Password
+ <div className="flex flex-col gap-2">
+ <label className="text-sm text-[#666666]" htmlFor="password">
+ password
</label>
<input
- className="mt-1 block w-full rounded-md border px-3 py-2"
+ className="w-full border border-[#2a2a2a] bg-[#0f0f0f] px-3 py-2 text-white placeholder:text-[#666666] focus:border-[#666666]"
id="password"
onChange={(event) => setPassword(event.target.value)}
+ placeholder="••••••••"
required
type="password"
value={password}
/>
</div>
- {error && <p className="text-sm text-red-500">{error}</p>}
+ {error && <p className="text-sm text-[#999999]">{error}</p>}
<button
- className="w-full rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
+ className="mt-2 w-full border border-[#2a2a2a] bg-[#0f0f0f] px-4 py-2 text-white transition-colors duration-100 hover:border-[#666666] disabled:text-[#666666] disabled:hover:border-[#2a2a2a]"
disabled={loading}
type="submit"
>
- {loading ? "Signing in ..." : "Sign In"}
+ {loading ? "signing in ..." : "sign in"}
</button>
</form>
- <p className="text-center text-sm">
- Don&apos;t have an account?{" "}
- <a className="text-blue-600 hover:underline" href="/auth/sign-up">
- Sign Up
- </a>
+ <p className="mt-6 text-center text-sm text-[#666666]">
+ don&apos;t have an account?{" "}
+ <Link
+ className="text-[#999999] underline underline-offset-2 transition-colors duration-100 hover:text-white"
+ href="/auth/sign-up"
+ >
+ sign up
+ </Link>
</p>
</div>
- </div>
+ </main>
);
}
diff --git a/packages/web/src/app/auth/sign-up/page.tsx b/packages/web/src/app/auth/sign-up/page.tsx
index 94a501c..981692e 100644
--- a/packages/web/src/app/auth/sign-up/page.tsx
+++ b/packages/web/src/app/auth/sign-up/page.tsx
@@ -1,5 +1,6 @@
"use client";
+import Link from "next/link";
import { useState } from "react";
import { createClient } from "~/lib/supabase/client";
@@ -37,68 +38,80 @@ export default function SignUpPage() {
if (success) {
return (
- <div className="flex min-h-screen items-center justify-center">
- <div className="w-full max-w-md space-y-8 p-8 text-center">
- <h1 className="text-2xl font-bold">Check your email</h1>
- <p>We&apos;ve sent you a confirmation link to {email}</p>
+ <main className="flex min-h-screen flex-col items-center justify-center bg-[#070707]">
+ <div className="w-full max-w-sm px-4 text-center">
+ <h1 className="mb-4 text-2xl tracking-tight text-white">
+ <span className="text-[#999999]">&gt;</span> check your email
+ </h1>
+ <p className="text-[#666666]">
+ we&apos;ve sent a confirmation link to{" "}
+ <span className="text-[#999999]">{email}</span>
+ </p>
</div>
- </div>
+ </main>
);
}
return (
- <div className="flex min-h-screen items-center justify-center">
- <div className="w-full max-w-md space-y-8 p-8">
- <h1 className="text-center text-2xl font-bold">Sign Up</h1>
+ <main className="flex min-h-screen flex-col items-center justify-center bg-[#070707]">
+ <div className="w-full max-w-sm px-4">
+ <h1 className="mb-8 text-center text-2xl tracking-tight text-white">
+ <span className="text-[#999999]">&gt;</span> sign up
+ </h1>
- <form className="space-y-6" onSubmit={handleSubmit}>
- <div>
- <label className="block text-sm font-medium" htmlFor="email">
- Email
+ <form className="flex flex-col gap-4" onSubmit={handleSubmit}>
+ <div className="flex flex-col gap-2">
+ <label className="text-sm text-[#666666]" htmlFor="email">
+ email
</label>
<input
- className="mt-1 block w-full rounded-md border px-3 py-2"
+ className="w-full border border-[#2a2a2a] bg-[#0f0f0f] px-3 py-2 text-white placeholder:text-[#666666] focus:border-[#666666]"
id="email"
onChange={(event) => setEmail(event.target.value)}
+ placeholder="[email protected]"
required
type="email"
value={email}
/>
</div>
- <div>
- <label className="block text-sm font-medium" htmlFor="password">
- Password
+ <div className="flex flex-col gap-2">
+ <label className="text-sm text-[#666666]" htmlFor="password">
+ password
</label>
<input
- className="mt-1 block w-full rounded-md border px-3 py-2"
+ className="w-full border border-[#2a2a2a] bg-[#0f0f0f] px-3 py-2 text-white placeholder:text-[#666666] focus:border-[#666666]"
id="password"
minLength={6}
onChange={(event) => setPassword(event.target.value)}
+ placeholder="••••••••"
required
type="password"
value={password}
/>
</div>
- {error && <p className="text-sm text-red-500">{error}</p>}
+ {error && <p className="text-sm text-[#999999]">{error}</p>}
<button
- className="w-full rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
+ className="mt-2 w-full border border-[#2a2a2a] bg-[#0f0f0f] px-4 py-2 text-white transition-colors duration-100 hover:border-[#666666] disabled:text-[#666666] disabled:hover:border-[#2a2a2a]"
disabled={loading}
type="submit"
>
- {loading ? "Creating account ..." : "Sign Up"}
+ {loading ? "creating account ..." : "sign up"}
</button>
</form>
- <p className="text-center text-sm">
- Already have an account?{" "}
- <a className="text-blue-600 hover:underline" href="/auth/sign-in">
- Sign In
- </a>
+ <p className="mt-6 text-center text-sm text-[#666666]">
+ already have an account?{" "}
+ <Link
+ className="text-[#999999] underline underline-offset-2 transition-colors duration-100 hover:text-white"
+ href="/auth/sign-in"
+ >
+ sign in
+ </Link>
</p>
</div>
- </div>
+ </main>
);
}
diff --git a/packages/web/src/app/dashboard/dashboard-content.tsx b/packages/web/src/app/dashboard/dashboard-content.tsx
new file mode 100644
index 0000000..da681b8
--- /dev/null
+++ b/packages/web/src/app/dashboard/dashboard-content.tsx
@@ -0,0 +1,122 @@
+"use client";
+
+import Link from "next/link";
+import { useState } from "react";
+import { api } from "~/trpc/react";
+
+function MemoryList() {
+ const [memories] = api.memory.list.useSuspenseQuery();
+ const trpcUtilities = api.useUtils();
+ const deleteMemory = api.memory.delete.useMutation({
+ onSuccess: async () => {
+ await trpcUtilities.memory.invalidate();
+ },
+ });
+
+ if (memories.length === 0) {
+ return (
+ <div className="border border-[#2a2a2a] bg-[#0f0f0f] p-4 text-center">
+ <p className="text-[#666666]">
+ no memories yet. create your first one below.
+ </p>
+ </div>
+ );
+ }
+
+ return (
+ <div className="flex w-full flex-col gap-2">
+ {memories.map((memory) => (
+ <div
+ className="flex items-start justify-between gap-4 border border-[#2a2a2a] bg-[#0f0f0f] p-3"
+ key={memory.id}
+ >
+ <div className="flex-1">
+ <p className="text-white">{memory.content}</p>
+ <p className="mt-1 text-xs text-[#666666]">
+ {new Date(memory.createdAt).toLocaleString()}
+ </p>
+ </div>
+ <button
+ className="border border-[#2a2a2a] bg-[#0f0f0f] px-3 py-1 text-sm text-[#999999] transition hover:border-[#666666] hover:text-white"
+ disabled={deleteMemory.isPending}
+ onClick={() => deleteMemory.mutate({ id: memory.id })}
+ type="button"
+ >
+ {deleteMemory.isPending ? "deleting ..." : "delete"}
+ </button>
+ </div>
+ ))}
+ </div>
+ );
+}
+
+function CreateMemoryForm() {
+ const [content, setContent] = useState("");
+ const trpcUtilities = api.useUtils();
+ const createMemory = api.memory.create.useMutation({
+ onSuccess: async () => {
+ await trpcUtilities.memory.invalidate();
+ setContent("");
+ },
+ });
+
+ return (
+ <form
+ className="flex w-full flex-col gap-3"
+ onSubmit={(formSubmitEvent) => {
+ formSubmitEvent.preventDefault();
+
+ if (content.trim()) {
+ createMemory.mutate({ content });
+ }
+ }}
+ >
+ <textarea
+ className="w-full border border-[#2a2a2a] bg-[#0f0f0f] px-3 py-2 text-white placeholder:text-[#666666] focus:border-[#666666] focus:outline-none"
+ onChange={(textareaChangeEvent) =>
+ setContent(textareaChangeEvent.target.value)
+ }
+ placeholder="enter your memory content ..."
+ rows={3}
+ value={content}
+ />
+ <button
+ className="border border-[#2a2a2a] bg-[#0f0f0f] px-6 py-2 text-white transition hover:border-[#666666] disabled:text-[#666666] disabled:hover:border-[#2a2a2a]"
+ disabled={createMemory.isPending || !content.trim()}
+ type="submit"
+ >
+ {createMemory.isPending ? "creating ..." : "create memory"}
+ </button>
+ </form>
+ );
+}
+
+export function DashboardContent() {
+ return (
+ <main className="flex min-h-screen flex-col items-center bg-[#070707]">
+ <div className="container flex max-w-2xl flex-col items-center gap-6 px-4 py-12">
+ <div className="flex w-full items-center justify-between">
+ <h1 className="text-2xl tracking-tight text-white">
+ <span className="text-[#999999]">&gt;</span> memory dashboard
+ </h1>
+ <Link
+ className="border border-[#2a2a2a] bg-[#0f0f0f] px-4 py-1 text-[#999999] transition hover:border-[#666666] hover:text-white"
+ href="/"
+ >
+ home
+ </Link>
+ </div>
+
+ <section className="w-full">
+ <h2 className="mb-3 text-sm text-[#666666]">your memories</h2>
+ <MemoryList />
+ </section>
+
+ <section className="w-full">
+ <h2 className="mb-3 text-sm text-[#666666]">create new memory</h2>
+ <CreateMemoryForm />
+ </section>
+ </div>
+ </main>
+ );
+}
diff --git a/packages/web/src/app/dashboard/page.tsx b/packages/web/src/app/dashboard/page.tsx
new file mode 100644
index 0000000..f1e0f5d
--- /dev/null
+++ b/packages/web/src/app/dashboard/page.tsx
@@ -0,0 +1,13 @@
+import { redirect } from "next/navigation";
+import { getUser } from "~/server/auth";
+import { DashboardContent } from "./dashboard-content";
+
+export default async function DashboardPage() {
+ const user = await getUser();
+
+ if (!user) {
+ redirect("/auth/sign-in");
+ }
+
+ return <DashboardContent />;
+}
diff --git a/packages/web/src/app/layout.tsx b/packages/web/src/app/layout.tsx
index 163dbef..bb542b8 100644
--- a/packages/web/src/app/layout.tsx
+++ b/packages/web/src/app/layout.tsx
@@ -1,24 +1,24 @@
import "~/styles/globals.css";
import type { Metadata } from "next";
-import { Geist } from "next/font/google";
+import { JetBrains_Mono } from "next/font/google";
import { TRPCReactProvider } from "~/trpc/react";
export const metadata: Metadata = {
- title: "Create T3 App",
- description: "Generated by create-t3-app",
+ title: "imemio",
+ description: "Memory management dashboard",
icons: [{ rel: "icon", url: "/favicon.ico" }],
};
-const geist = Geist({
+const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
- variable: "--font-geist-sans",
+ variable: "--font-mono",
});
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
- <html className={`${geist.variable}`} lang="en">
- <body>
+ <html className={`${jetbrainsMono.variable}`} lang="en">
+ <body className="bg-[#070707] font-mono text-white antialiased">
<TRPCReactProvider>{children}</TRPCReactProvider>
</body>
</html>
diff --git a/packages/web/src/app/page.tsx b/packages/web/src/app/page.tsx
index 2539f3a..efb6912 100644
--- a/packages/web/src/app/page.tsx
+++ b/packages/web/src/app/page.tsx
@@ -1,68 +1,38 @@
import Link from "next/link";
-import { LatestPost } from "~/app/_components/post";
-import { auth } from "~/server/auth";
-import { api, HydrateClient } from "~/trpc/server";
+import { getSession } from "~/server/auth";
export default async function Home() {
- const hello = await api.post.hello({ text: "from tRPC" });
- const session = await auth();
-
- if (session?.user) {
- void api.post.getLatest.prefetch();
- }
+ const session = await getSession();
return (
- <HydrateClient>
- <main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white">
- <div className="container flex flex-col items-center justify-center gap-12 px-4 py-16">
- <h1 className="font-extrabold text-5xl tracking-tight sm:text-[5rem]">
- Create <span className="text-[hsl(280,100%,70%)]">T3</span> App
- </h1>
- <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-8">
- <Link
- className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20"
- href="https://create.t3.gg/en/usage/first-steps"
- target="_blank"
- >
- <h3 className="font-bold text-2xl">First Steps →</h3>
- <div className="text-lg">
- Just the basics - Everything you need to know to set up your
- database and authentication.
- </div>
- </Link>
- <Link
- className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20"
- href="https://create.t3.gg/en/introduction"
- target="_blank"
- >
- <h3 className="font-bold text-2xl">Documentation →</h3>
- <div className="text-lg">
- Learn more about Create T3 App, the libraries it uses, and how
- to deploy it.
- </div>
- </Link>
- </div>
- <div className="flex flex-col items-center gap-2">
- <p className="text-2xl text-white">
- {hello ? hello.greeting : "Loading tRPC query..."}
- </p>
-
- <div className="flex flex-col items-center justify-center gap-4">
- <p className="text-center text-2xl text-white">
- {session && <span>Logged in as {session.user?.name}</span>}
- </p>
- <Link
- className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20"
- href={session ? "/api/auth/signout" : "/api/auth/signin"}
- >
- {session ? "Sign out" : "Sign in"}
- </Link>
- </div>
+ <main className="flex min-h-screen flex-col items-center justify-center bg-[#070707]">
+ <div className="container flex flex-col items-center justify-center gap-8 px-4 py-12">
+ <h1 className="text-4xl tracking-tight text-white sm:text-5xl">
+ <span className="text-[#999999]">&gt;</span> imemio
+ </h1>
+ <p className="text-[#666666]">memory management system</p>
+ <Link
+ className="flex max-w-xs flex-col gap-2 border border-[#2a2a2a] bg-[#0f0f0f] p-4 transition hover:border-[#666666]"
+ href="/dashboard"
+ >
+ <h3 className="text-lg text-white">dashboard</h3>
+ <div className="text-sm text-[#999999]">
+ View and manage your memories. Create new memories and organise your
+ thoughts.
</div>
-
- {session?.user && <LatestPost />}
+ </Link>
+ <div className="flex flex-col items-center gap-3">
+ <p className="text-center text-[#666666]">
+ {session && <span>logged in as {session.user?.email}</span>}
+ </p>
+ <Link
+ className="border border-[#2a2a2a] bg-[#0f0f0f] px-6 py-2 text-white transition hover:border-[#666666]"
+ href={session ? "/api/auth/signout" : "/auth/sign-in"}
+ >
+ {session ? "sign out" : "sign in"}
+ </Link>
</div>
- </main>
- </HydrateClient>
+ </div>
+ </main>
);
}
diff --git a/packages/web/src/server/api/root.ts b/packages/web/src/server/api/root.ts
index 374285c..b0fd7ba 100644
--- a/packages/web/src/server/api/root.ts
+++ b/packages/web/src/server/api/root.ts
@@ -1,3 +1,4 @@
+import { memoryRouter } from "~/server/api/routers/memory";
import { postRouter } from "~/server/api/routers/post";
import { createCallerFactory, createTRPCRouter } from "~/server/api/trpc";
@@ -7,6 +8,7 @@ import { createCallerFactory, createTRPCRouter } from "~/server/api/trpc";
* All routers added in /api/routers should be manually added here.
*/
export const appRouter = createTRPCRouter({
+ memory: memoryRouter,
post: postRouter,
});
diff --git a/packages/web/src/server/api/routers/memory.ts b/packages/web/src/server/api/routers/memory.ts
new file mode 100644
index 0000000..d1bb849
--- /dev/null
+++ b/packages/web/src/server/api/routers/memory.ts
@@ -0,0 +1,80 @@
+import { SupabaseProjectStore, SupabaseStore } from "@imemio/sdk";
+import type { SupabaseClient } from "@supabase/supabase-js";
+import { z } from "zod";
+import { createClient } from "~/lib/supabase/server";
+import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
+
+const defaultProjectName = "default";
+
+async function getOrCreateDefaultProject(
+ supabaseClient: SupabaseClient,
+ userId: string,
+): Promise<string> {
+ const projectStore = new SupabaseProjectStore(supabaseClient, userId);
+ const projectsResult = await projectStore.list();
+
+ if (projectsResult.success && projectsResult.value.length > 0) {
+ const existingDefaultProject = projectsResult.value.find(
+ (project) => project.name === defaultProjectName,
+ );
+
+ if (existingDefaultProject) {
+ return existingDefaultProject.id;
+ }
+
+ const firstProject = projectsResult.value[0];
+
+ if (firstProject) {
+ return firstProject.id;
+ }
+ }
+
+ const createResult = await projectStore.create({ name: defaultProjectName });
+
+ if (!createResult.success) {
+ throw new Error("Failed to create default project");
+ }
+
+ return createResult.value.id;
+}
+
+export const memoryRouter = createTRPCRouter({
+ list: protectedProcedure.query(async ({ ctx }) => {
+ const supabaseClient = await createClient();
+ const memoryStore = new SupabaseStore(supabaseClient, ctx.user.id);
+ const memories = await memoryStore.list();
+
+ return memories;
+ }),
+
+ create: protectedProcedure
+ .input(z.object({ content: z.string().min(1) }))
+ .mutation(async ({ ctx, input }) => {
+ const supabaseClient = await createClient();
+ const projectId = await getOrCreateDefaultProject(
+ supabaseClient,
+ ctx.user.id,
+ );
+ const memoryStore = new SupabaseStore(supabaseClient, ctx.user.id);
+ const memory = await memoryStore.create({
+ content: input.content,
+ projectId,
+ });
+
+ return memory;
+ }),
+
+ delete: protectedProcedure
+ .input(z.object({ id: z.string() }))
+ .mutation(async ({ ctx, input }) => {
+ const supabaseClient = await createClient();
+ const memoryStore = new SupabaseStore(supabaseClient, ctx.user.id);
+ const deleteResult = await memoryStore.delete(input.id);
+
+ if (!deleteResult.success) {
+ throw new Error(`Memory not found: ${input.id}`);
+ }
+
+ return { success: true };
+ }),
+});
diff --git a/packages/web/src/styles/globals.css b/packages/web/src/styles/globals.css
index 7fdd803..38f76a5 100644
--- a/packages/web/src/styles/globals.css
+++ b/packages/web/src/styles/globals.css
@@ -4,4 +4,39 @@
--font-sans:
var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+
+ --color-bg-primary: #070707;
+ --color-bg-secondary: #0f0f0f;
+ --color-bg-tertiary: #1a1a1a;
+ --color-border: #2a2a2a;
+ --color-text-primary: #ffffff;
+ --color-text-secondary: #999999;
+ --color-text-tertiary: #666666;
+ --color-text-dim: #444444;
+}
+
+* {
+ border-radius: 0 !important;
+}
+
+::selection {
+ background-color: #2a2a2a;
+ color: #ffffff;
+}
+
+input,
+textarea,
+button {
+ font-family: inherit;
+}
+
+input::placeholder,
+textarea::placeholder {
+ color: var(--color-text-tertiary);
+}
+
+input:focus,
+textarea:focus {
+ outline: none;
+ border-color: var(--color-text-tertiary);
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 883a7e8..546aab1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -91,6 +91,9 @@ importers:
packages/web:
dependencies:
+ '@imemio/sdk':
+ specifier: workspace:*
+ version: link:../sdk
'@supabase/ssr':
specifier: ^0.8.0
version: 0.8.0(@supabase/[email protected])