I've been keeping a place for your words.
+ Come when you're ready.
+
+{% endblock %}
+
+{% block footnote %}
+ This link expires in 24 hours.
+ I'm patient, but not endlessly so.
+{% endblock %}
+
+{% block footer %}
+ Didn't write to me? Then someone else did.
+ Ignore this. I'll forget you were ever here.
+{% endblock %}
\ No newline at end of file
diff --git a/backend/templates/email/activation.txt b/backend/templates/email/activation.txt
new file mode 100644
index 0000000..d115ffd
--- /dev/null
+++ b/backend/templates/email/activation.txt
@@ -0,0 +1,21 @@
+pi. ku.
+-------------------------------------------
+
+{{pen_name}},
+
+Your destination is one train away.
+
+I've been keeping a place for your words.
+Come when you're ready.
+
+{{ cta.title }} -> {{ cta.link }}
+
+-------------------------------------------
+
+This link expires in 24 hours.
+I'm patient, but not endlessly so.
+
+-------------------------------------------
+
+Didn't write to me? Then someone else did.
+Ignore this. I'll forget you were ever here.
\ No newline at end of file
diff --git a/backend/templates/email/base.html b/backend/templates/email/base.html
new file mode 100644
index 0000000..09460b9
--- /dev/null
+++ b/backend/templates/email/base.html
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+ pi. ku.
+
+
+
+
- When you're ready,
-
- you can{" "}
- read it,{" "}
- send it to
- someone, or{" "}
- burn it to
- release
-
+ {type === "KEPT" ? (
+
+ When you're ready,
+
+ you can{" "}
+ read{" "}
+ it, send{" "}
+ it to someone, or{" "}
+ burn it
+ to release
+
+ ) : (
+
+ Be assured that the letter will find you when the time is right.
+
+ Till then,{" "}
+
+ take a deep breath
+
+ ,{" "}
+ manifest
+ , and{" "}
+
+ let it rest
+
+ .
+
- Vaulting locks the letter permanently and will be{" "}
- mailed to you
- automatically on the unlock date.
+ By vaulting this letter, you ask me to hold on to this.
-
- You cannot edit or view the contents of the letter until then.
+ I'll remember to mail you this on the unlock date.
+
+
+ {" "}
+ But I won't let you read or rewrite this letter until then.
+
+ Welcome to
Your identity is now verified and ready for timeless letters.
-
-
+
Activation Failed
-
+
The link might be expired or already used. Please try registering
again.
+
)}
diff --git a/frontend/src/pages/Drawer.tsx b/frontend/src/pages/Drawer.tsx
index d49b509..2692568 100644
--- a/frontend/src/pages/Drawer.tsx
+++ b/frontend/src/pages/Drawer.tsx
@@ -5,6 +5,7 @@ import { DrawerSection } from "../components/drawer/DrawerSection.tsx";
import { LetterItem } from "../components/drawer/LetterItem.tsx";
import { PasskeyModal } from "../components/drawer/PasskeyModal.tsx";
import Logo from "../components/Logo";
+import Saajan from "../components/ui/Saajan.tsx";
import { PATHS } from "../config/routes";
import { useAuth } from "../hooks/useAuth";
import { useLetters } from "../hooks/useLetters";
@@ -165,6 +166,12 @@ export default function Drawer() {
+
+
+
);
}
diff --git a/frontend/src/pages/Editor.test.tsx b/frontend/src/pages/Editor.test.tsx
index f1dc66e..d1e63c4 100644
--- a/frontend/src/pages/Editor.test.tsx
+++ b/frontend/src/pages/Editor.test.tsx
@@ -83,11 +83,9 @@ describe("Editor Page", () => {
expect(screen.queryByText(/Opening your draft/i)).not.toBeInTheDocument();
});
- // Initial state: DRAFT (not read-only)
const canvas = screen.getByTestId("canvas");
expect(canvas.getAttribute("data-readonly")).toBe("false");
- // Click Seal in the main toolbar (it's in the div with id="writer-toolbar")
const toolbar = container.querySelector("#writer-toolbar");
const sealBtn = toolbar?.querySelector(".btn-primary");
if (!sealBtn) throw new Error("Seal button not found");
diff --git a/frontend/src/pages/Editor.tsx b/frontend/src/pages/Editor.tsx
index 4d81ea6..c481516 100644
--- a/frontend/src/pages/Editor.tsx
+++ b/frontend/src/pages/Editor.tsx
@@ -293,7 +293,7 @@ export default function Editor() {
setLetterStatus(status);
setLastSavedPulseTick((prev) => prev + 1);
- if (status === "SEALED") {
+ if (status === "SEALED" || status === "VAULT") {
setSealedTargetId(targetId);
}
setSaveOverlay("saved");
@@ -419,7 +419,11 @@ export default function Editor() {
/>
)}
{sealedTargetId && (
-
+
)}
diff --git a/frontend/src/pages/Login.test.tsx b/frontend/src/pages/Login.test.tsx
index 7ff13a9..c6f4af3 100644
--- a/frontend/src/pages/Login.test.tsx
+++ b/frontend/src/pages/Login.test.tsx
@@ -14,16 +14,6 @@ describe("Login Page", () => {
server.resetHandlers();
});
- it("should render the sign-in form correctly", () => {
- render(
-
-
- ,
- );
-
- expect(screen.getByText("Sign in to")).toBeInTheDocument();
- });
-
it("should display a technical issues message when the server is down", async () => {
server.use(
http.post(`${API_URL}${endpoints.LOGIN}`, () =>
diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx
index 7574f75..b5b0240 100644
--- a/frontend/src/pages/Login.tsx
+++ b/frontend/src/pages/Login.tsx
@@ -1,5 +1,9 @@
import { zodResolver } from "@hookform/resolvers/zod";
-import { ShieldCheckIcon, WarningIcon } from "@phosphor-icons/react";
+import {
+ HandPalmIcon,
+ ShieldCheckIcon,
+ WarningIcon,
+} from "@phosphor-icons/react";
import axios from "axios";
import { useState } from "react";
import { useForm } from "react-hook-form";
@@ -8,6 +12,7 @@ import { z } from "zod";
import { api, publicApi } from "../api/apiClient";
import Logo from "../components/Logo";
import FormField from "../components/ui/FormField";
+import Saajan from "../components/ui/Saajan";
import { endpoints } from "../config/endpoints";
import { ROUTES } from "../config/routes";
import { useAuth } from "../hooks/useAuth";
@@ -20,10 +25,19 @@ const loginSchema = z.object({
type LoginInputs = z.infer;
-function WelcomeModal({ setShowWelcome }) {
+function WelcomeModal({
+ setShowWelcome,
+}: {
+ setShowWelcome: (show: boolean) => void;
+}) {
return (
-
+
+
+
+
- Welcome to !
+ Welcome to
+ !
- To ensure complete privacy, all
- your letters are{" "}
-
- sealed with your password
-
- , which only you have access to.
+ Before we begin, let me make a small promise.
+
+
-
- The server never sees it, and it's a solemn promise!
-
+ Everything you write here is sealed with your password,{" "}
+ cryptographically
+ , before it leaves your hands.
+ A fancy way of saying, I couldn't if I tried.
@@ -53,6 +70,19 @@ function WelcomeModal({ setShowWelcome }) {
If you ever happen to forget your password, your letters are lost
to time, forever.
+
+
+ I highly, highly recommend storing this password in your{" "}
+
+ password manager
+ {" "}
+ or somewhere safe to remember it.
+