7 Commits

Author SHA1 Message Date
me fff90902b5 refactor: fix whitespace indents in copy
CI / Generate Certificates (pull_request) Successful in 37s
CI / Frontend CI (pull_request) Successful in 1m8s
CI / Backend CI (pull_request) Successful in 1m8s
CI / E2E Tests (pull_request) Successful in 6m53s
2026-05-08 23:20:27 +05:30
me a3a56d4316 chore: replace empty jsx space braces with nbsp
CI / Generate Certificates (pull_request) Successful in 41s
CI / Frontend CI (pull_request) Successful in 1m7s
CI / Backend CI (pull_request) Successful in 1m8s
CI / E2E Tests (pull_request) Successful in 6m55s
2026-05-08 22:17:50 +05:30
me 2ba5d6964f test: replace role texts by testids
CI / Generate Certificates (pull_request) Successful in 38s
CI / Frontend CI (pull_request) Successful in 1m6s
CI / Backend CI (pull_request) Successful in 1m18s
CI / E2E Tests (pull_request) Successful in 6m49s
2026-05-08 21:21:30 +05:30
me ffe588c3ec fix: add fallback to Modal portal container 2026-05-08 21:19:54 +05:30
me a7cced71ee chore: update copy of register and login 2026-05-08 21:00:18 +05:30
me c6545a11b1 style: improve mobile responsiveness and a11y for WelcomeModal 2026-05-08 20:16:09 +05:30
me 2419b73b15 fix: update modal positioning to the viewport instead of parent container 2026-05-08 20:04:33 +05:30
20 changed files with 207 additions and 170 deletions
@@ -68,7 +68,8 @@ export function DrawerSection({
<div className="font-sans text-xs text-base-content/20 mt-1"> <div className="font-sans text-xs text-base-content/20 mt-1">
<span className="font-mono text-xs md:text-base -mt-1 absolute text-primary/30"> <span className="font-mono text-xs md:text-base -mt-1 absolute text-primary/30">
{count} {count}
</span>{" "} </span>
&nbsp;
<span className="ml-3">{subtext}</span> <span className="ml-3">{subtext}</span>
</div> </div>
<div className="absolute right-5 -translate-y-15 text-base-content/4"> <div className="absolute right-5 -translate-y-15 text-base-content/4">
@@ -41,10 +41,15 @@ export function PasskeyModal() {
required required
type="password" type="password"
placeholder="password" placeholder="password"
data-testid="passkey-input"
className="font-sans validator input input-bordered rounded-r-none" className="font-sans validator input input-bordered rounded-r-none"
/> />
<div className="validator-message text-xs text-error"></div> <div className="validator-message text-xs text-error"></div>
<button type="submit" className="btn btn-primary rounded-l-none"> <button
type="submit"
data-testid="passkey-submit-btn"
className="btn btn-primary rounded-l-none"
>
Unlock Unlock
</button> </button>
</form> </form>
@@ -25,10 +25,11 @@ export function PostSealModal({
<p className="text-base-content/80 text-sm font-sans"> <p className="text-base-content/80 text-sm font-sans">
When you're ready, When you're ready,
<br /> <br />
you can{" "} you can&nbsp;
<span className="text-primary font-bold font-display">read</span> it,{" "} <span className="text-primary font-bold font-display">read</span>
&nbsp; it,&nbsp;
<span className="text-accent font-bold font-display">send</span> it to <span className="text-accent font-bold font-display">send</span> it to
someone, or{" "} someone, or&nbsp;
<span className="text-error font-bold font-display">burn</span> it to <span className="text-error font-bold font-display">burn</span> it to
release release
</p> </p>
@@ -36,12 +37,12 @@ export function PostSealModal({
<p className="text-base-content/80 text-sm font-sans"> <p className="text-base-content/80 text-sm font-sans">
Be assured that the letter will find you when the time is right. Be assured that the letter will find you when the time is right.
<br /> <br />
Till then,{" "} Till then,&nbsp;
<span className="font-bold font-display text-primary"> <span className="font-bold font-display text-primary">
take a deep breath take a deep breath
</span> </span>
, <span className="font-bold font-display text-accent">manifest</span> , <span className="font-bold font-display text-accent">manifest</span>
, and{" "} , and&nbsp;
<span className="font-bold font-display text-success"> <span className="font-bold font-display text-success">
let it rest let it rest
</span> </span>
+4 -2
View File
@@ -194,6 +194,7 @@ export function ToolBar({
</div> </div>
<button <button
type="button" type="button"
data-testid="vault-trigger-btn"
className="btn btn-neutral btn-sm rounded-full px-6 group" className="btn btn-neutral btn-sm rounded-full px-6 group"
onClick={() => setConfirmModal("VAULT")} onClick={() => setConfirmModal("VAULT")}
> >
@@ -269,8 +270,7 @@ export function VaultConfirmModal({
I'll remember to mail you this on the unlock date. I'll remember to mail you this on the unlock date.
<br /> <br />
<span className={"font-bold text-primary"}> <span className={"font-bold text-primary"}>
{" "} &nbsp; But I won't let you read or rewrite this letter until then.
But I won't let you read or rewrite this letter until then.
</span> </span>
<br /> <br />
</p> </p>
@@ -299,6 +299,7 @@ export function VaultConfirmModal({
<div className="w-full flex justify-center gap-8 mt-4"> <div className="w-full flex justify-center gap-8 mt-4">
<button <button
type="button" type="button"
data-testid="vault-cancel-btn"
className="btn btn-ghost btn-sm mt-4" className="btn btn-ghost btn-sm mt-4"
onClick={() => setConfirmModal(null)} onClick={() => setConfirmModal(null)}
> >
@@ -307,6 +308,7 @@ export function VaultConfirmModal({
<button <button
className="btn btn-primary btn-sm mt-4" className="btn btn-primary btn-sm mt-4"
type="submit" type="submit"
data-testid="vault-confirm-btn"
form="vault-form" form="vault-form"
> >
Take it Take it
+20 -18
View File
@@ -15,7 +15,7 @@ export default function WelcomeModal({
return ( return (
<> <>
<Modal isOpen={true}> <Modal isOpen={true}>
<div className="flex flex-col items-center text-center gap-4"> <div className="flex flex-col items-center text-center gap-2 md:gap-4">
<div className="bg-primary/10 p-4 rounded-full animate-pulse"> <div className="bg-primary/10 p-4 rounded-full animate-pulse">
<ShieldCheckIcon <ShieldCheckIcon
size={48} size={48}
@@ -24,40 +24,42 @@ export default function WelcomeModal({
/> />
</div> </div>
<h3 className="font-display text-2xl font-bold text-primary"> <h3 className="font-display text-2xl font-bold text-primary">
Welcome to &nbsp; Welcome to&nbsp;
<Logo /> &nbsp;! <Logo type="inline" />
</h3> </h3>
<p className="text-base-content/80 leading-relaxed"> <p className="inline text-sm md:text-base text-base-content/80">
Before we begin, let me make a small promise. Before we begin, let me make a small promise.
<HandPalmIcon <HandPalmIcon
size={18} size={18}
className="inline text-primary" className="inline text-primary"
weight="fill" weight="fill"
/> />
<span className="divider my-0 block"></span> <span className="divider my-0"></span>
Everything you write here is sealed with your password,{" "} Everything you write here is sealed with your password,&nbsp;
<span className="font-display text-success">cryptographically</span> <span className="font-display text-success">cryptographically</span>
, before it leaves your hands. , before it leaves your hands.
<br />A fancy way of saying, I couldn't if I tried. <br />
<br />A fancy way of saying, no one else can read them without your
key&mdash;not even me.
</p> </p>
<div className="alert alert-warning bg-paper/20 border-paper/20 flex items-start gap-3 text-left py-3"> <div className="alert alert-warning flex items-start gap-3 text-left py-3">
<WarningIcon size={24} weight="fill" className="shrink-0 mt-0.5" /> <WarningIcon size={24} weight="fill" className="shrink-0" />
<div className="text-sm font-medium text-primary-content"> <div className="text-xs md:text-sm font-medium text-primary-content tracking-tight">
If you ever happen to forget your password, your letters are lost If you ever happen to forget your password, your letters are lost
to time, forever. to time, forever.
<br /> <span className="mt-2 block">
<span className="font-bold mt-2 block"> I highly, <span className="font-bold italic">highly</span>&nbsp;
I highly, highly recommend storing this password in your{" "} recommend storing this password in your&nbsp;
<a <a
href="https://www.privacyguides.org/en/passwords/" href="https://www.privacyguides.org/en/passwords/"
target="_blank" target="_blank"
className="link link-primary-content" className="link link-neutral!"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
password manager password manager
</a>{" "} </a>
or somewhere safe to remember it. &nbsp; or somewhere safe to remember it.
</span> </span>
</div> </div>
</div> </div>
@@ -74,9 +76,9 @@ export default function WelcomeModal({
</div> </div>
</div> </div>
</Modal> </Modal>
<div className="absolute bottom-0 right-0 z-1000 font-sans w-full"> <div className="absolute bottom-0 md:right-5/12 z-1000 font-sans w-full flex justify-center">
<Saajan <Saajan
position="top" position="left"
message={"I've lost words before.\nI know what it feels like."} message={"I've lost words before.\nI know what it feels like."}
/> />
</div> </div>
+2 -2
View File
@@ -58,8 +58,8 @@ export function BurnModal({
Let the echoes of your unsaid be finally released. Let the echoes of your unsaid be finally released.
</p> </p>
<div className="mt-4 font-sans text-sm"> <div className="mt-4 font-sans text-sm">
<span className="text-error">Press</span> and{" "} <span className="text-error">Press</span> and&nbsp;
<span className="text-error">hold</span> the{" "} <span className="text-error">hold</span> the&nbsp;
<span className="text-amber-300">flame</span> to proceed. <span className="text-amber-300">flame</span> to proceed.
</div> </div>
<div className="modal-action w-full justify-center gap-3 mt-2"> <div className="modal-action w-full justify-center gap-3 mt-2">
@@ -23,8 +23,8 @@ export function PostActionOverlay({ revealState }: PostActionOverlayProps) {
May your <span className="italic text-primary">soul</span> find May your <span className="italic text-primary">soul</span> find
solace, solace,
<br /> <br />
just like your <span className="text-accent italic">unsaid</span>{" "} just like your <span className="text-accent italic">unsaid</span>
words did. &nbsp; words did.
</p> </p>
<div className="divider mx-auto w-24 text-center"></div> <div className="divider mx-auto w-24 text-center"></div>
<button <button
@@ -30,7 +30,7 @@ export function ShareModal({ shareLink, setShareLink }: ShareModalProps) {
<p className="text-base-content/80 text-sm font-sans mt-4"> <p className="text-base-content/80 text-sm font-sans mt-4">
You've carried these words long enough. You've carried these words long enough.
<br /> <br />
Send your letter now, and let the{" "} Send your letter now, and let the&nbsp;
<span className="text-accent font-display">unsaid</span> finally <span className="text-accent font-display">unsaid</span> finally
find its home. find its home.
</p> </p>
@@ -59,8 +59,8 @@ export function ShareModal({ shareLink, setShareLink }: ShareModalProps) {
</div> </div>
<div className="flex flex-col gap-1 uppercase tracking-widest text-base-content/30 font-sans"> <div className="flex flex-col gap-1 uppercase tracking-widest text-base-content/30 font-sans">
<p className="textarea-xs flex items-center justify-center"> <p className="textarea-xs flex items-center justify-center">
<EyeSlashIcon weight="duotone" size={18} className="mr-2" />{" "} <EyeSlashIcon weight="duotone" size={18} className="mr-2" />
Zero-Knowledge Share: &nbsp; Zero-Knowledge Share:
</p> </p>
<p className="textarea-xs font-mono text-center"> <p className="textarea-xs font-mono text-center">
The key never leaves your or the recipient's browser. The key never leaves your or the recipient's browser.
@@ -68,7 +68,7 @@ export function ShareModal({ shareLink, setShareLink }: ShareModalProps) {
</div> </div>
</div> </div>
</Modal> </Modal>
<div className="absolute bottom-0 z-1000 font-sans w-full"> <div className="absolute bottom-0 md:right-5/11 z-1000 font-sans w-full">
<Saajan <Saajan
position="top" position="top"
message={`Someone once said,\n"To send a letter is a good way to go somewhere without moving anything but your heart."\nThey were not wrong.`} message={`Someone once said,\n"To send a letter is a good way to go somewhere without moving anything but your heart."\nThey were not wrong.`}
+1 -1
View File
@@ -23,7 +23,7 @@ export default function FormField({
<div className="form-control"> <div className="form-control">
<label <label
htmlFor={registration.name} htmlFor={registration.name}
className="field-label font-display text-base-content/90 font-medium" className="field-label font-display text-neutral-content/80 font-medium"
> >
{label} {label}
</label> </label>
+8 -4
View File
@@ -1,5 +1,6 @@
import { XCircleIcon } from "@phosphor-icons/react"; import { XCircleIcon } from "@phosphor-icons/react";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { createPortal } from "react-dom";
interface ModalProps { interface ModalProps {
isOpen: boolean; isOpen: boolean;
@@ -15,13 +16,15 @@ export function Modal({
"data-testid": testId, "data-testid": testId,
}: ModalProps) { }: ModalProps) {
if (!isOpen) return null; if (!isOpen) return null;
// render the modal top of all elements and position them to document viewport (/ the main wrapper).
return ( // NOTE: this is recommended approach for modals as it shouldn't be bound to the parent box.
const mainContainer = document.querySelector("main") || document.body;
return createPortal(
<div <div
data-testid={testId} data-testid={testId}
className="modal modal-open modal-middle backdrop-blur-md before:absolute before:top-0 before:left-0 before:w-full before:h-full before:content-[''] before:opacity-[0.03] before:z-10 before:pointer-events-none before:bg-[url('assets/textures/noise.gif')]" className="modal modal-open modal-middle backdrop-blur-md before:absolute before:top-0 before:left-0 before:w-full before:h-full before:content-[''] before:opacity-[0.03] before:z-10 before:pointer-events-none before:bg-[url('assets/textures/noise.gif')]"
> >
<div className="modal-box relative bg-base-100/60 flex flex-col items-center text-center gap-6"> <div className="modal-box border border-neutral/60 relative bg-base-100/60 flex flex-col items-center text-center gap-6">
{onClose && ( {onClose && (
<button <button
type="button" type="button"
@@ -35,6 +38,7 @@ export function Modal({
)} )}
{children} {children}
</div> </div>
</div> </div>,
mainContainer,
); );
} }
+96 -84
View File
@@ -98,11 +98,11 @@ function PrivacySection() {
</h1> </h1>
<div className="flex flex-col items-center shrink-0 gap-8 max-w-11/12 w-220"> <div className="flex flex-col items-center shrink-0 gap-8 max-w-11/12 w-220">
<p className="text-xxs md:text-sm tracking-widester text-neutral-content/80 font-semibold uppercase mt-6"> <p className="text-xxs md:text-sm tracking-widester text-neutral-content/80 font-semibold uppercase mt-6">
<span className="text-accent">Your letters.</span>{" "} <span className="text-accent">Your letters.</span>&nbsp;
<span className="text-error">Nobody else's.</span> <span className="text-error">Nobody else's.</span>
</p> </p>
<p className="text-sm md:text-lg text-neutral"> <p className="text-sm md:text-lg text-neutral">
When you write or upload anything{" "} When you write or upload anything&nbsp;
<span className="font-hand">(yes, even images)</span> here, it gets <span className="font-hand">(yes, even images)</span> here, it gets
encrypted in your browser before anything leaves your device. What encrypted in your browser before anything leaves your device. What
reaches the server is something unreadable&mdash;and the server has no reaches the server is something unreadable&mdash;and the server has no
@@ -226,29 +226,33 @@ function SpecsSection() {
</h1> </h1>
<div className="flex flex-col items-center shrink-0 gap-6 max-w-11/12 w-220 mt-4 md:mt-12 text-neutral-content/80"> <div className="flex flex-col items-center shrink-0 gap-6 max-w-11/12 w-220 mt-4 md:mt-12 text-neutral-content/80">
<h2 className="text-xl md:text-3xl text-center mx-auto"> <h2 className="text-xl md:text-3xl text-center mx-auto">
<Logo type={"inline"} /> uses{" "} <Logo type={"inline"} /> uses&nbsp;
<span className="text-accent font-mono">Zero Knowledge</span>{" "} <span className="text-accent font-mono">Zero Knowledge</span>&nbsp;
<span className="group ul-wavy font-mono text-success"> <button
type="button"
className="group ul-wavy font-mono text-success"
>
E E
<span className="hidden group-hover:inline group-focus-within:inline"> <span className="hidden group-hover:inline group-focus-within:inline text-neutral">
nd&mdash; nd&mdash;
</span> </span>
2 2
<span className="hidden group-hover:inline group-focus-within:inline"> <span className="hidden group-hover:inline group-focus-within:inline text-neutral">
&mdash; &mdash;
</span> </span>
E E
<span className="hidden group-hover:inline group-focus-within:inline"> <span className="hidden group-hover:inline group-focus-within:inline text-neutral">
nd nd
</span> </span>
<span className="hidden group-hover:inline group-focus-within:inline"> <span className="hidden group-hover:inline group-focus-within:inline">
&nbsp;<span>E</span> &nbsp;<span>E</span>
<span className="hidden group-hover:inline group-focus-within:inline"> <span className="hidden group-hover:inline group-focus-within:inline text-neutral">
ncryption ncryption
</span> </span>
</span> </span>
</span>{" "} </button>
for your <span className="font-hand text-primary">letters</span>, with{" "} &nbsp; for your&nbsp;
<span className="font-hand text-primary">letters</span>, with&nbsp;
<a <a
href="https://hackernoon.com/what-the-heck-is-envelope-encryption-in-cloud-security" href="https://hackernoon.com/what-the-heck-is-envelope-encryption-in-cloud-security"
target="_blank" target="_blank"
@@ -256,30 +260,32 @@ function SpecsSection() {
className="font-mono text-neutral!" className="font-mono text-neutral!"
> >
Envelope Encryption Envelope Encryption
</a>{" "} </a>
for the <span className="font-hand text-primary">keys</span>. &nbsp; for the <span className="font-hand text-primary">keys</span>.
</h2> </h2>
<div className="text-sm md:text-xl leading-relaxed"> <div className="text-sm md:text-xl leading-relaxed">
This means, both the{" "} This means, both the&nbsp;
<span className="font-display text-info">encryption</span> and{" "} <span className="font-display text-info">encryption</span> and&nbsp;
<span className="font-display text-info">decryption</span> runs on <span className="font-display text-info">decryption</span> runs on
your device, in your browser. your device, in your browser.
<ul className="list-decimal ml-6 md:ml-10 list-outside text-neutral marker:text-primary/30 marker:font-mono marker:text-xs marker:md:text-base"> <ul className="list-decimal ml-6 md:ml-10 list-outside text-neutral marker:text-primary/30 marker:font-mono marker:text-xs marker:md:text-base">
<li> <li>
Every letter has a{" "} Every letter has a&nbsp;
<span className="font-mono text-primary/50 font-bold"> <span className="font-mono text-primary/50 font-bold">
unique key unique key
</span>{" "} </span>
which is derived from your original password. &nbsp; which is derived from your original password.
</li> </li>
<li> <li>
Both the letter and the key are encrypted securely and sent to the Both the letter and the key are encrypted securely and sent to the
server. server.
</li> </li>
<li> <li>
Now, the server holds{" "} Now, the server holds&nbsp;
<span className="text-primary/50 font-bold">the envelope</span>,{" "} <span className="text-primary/50 font-bold">the envelope</span>
<span className="text-primary/50 font-bold">the seal</span> and{" "} ,&nbsp;
<span className="text-primary/50 font-bold">the seal</span>&nbsp;
and&nbsp;
<span className="text-primary/50 font-bold"> <span className="text-primary/50 font-bold">
another locked box another locked box
</span> </span>
@@ -288,7 +294,7 @@ function SpecsSection() {
</ul> </ul>
But you&mdash; But you&mdash;
<span className="italic">only you</span>&mdash;hold the very thing <span className="italic">only you</span>&mdash;hold the very thing
that opens that box,{" "} that opens that box,&nbsp;
<span className="font-mono text-accent">your password</span>. <span className="font-mono text-accent">your password</span>.
</div> </div>
<div className="text-xs md:text-lg text-right w-full flex items-center justify-end gap-4 leading-relaxed text-neutral-content/80"> <div className="text-xs md:text-lg text-right w-full flex items-center justify-end gap-4 leading-relaxed text-neutral-content/80">
@@ -296,17 +302,18 @@ function SpecsSection() {
Nothing on the server is readable without your actual password. Nothing on the server is readable without your actual password.
<br /> <br />
Even if someone were to breach in, all they'd find is encrypted Even if someone were to breach in, all they'd find is encrypted
noise and ain't no way they crackin' it.{" "} noise and ain't no way they crackin'
<br />
<a <a
href="https://xkcd.com/538/" href="https://xkcd.com/538/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-xxs md:text-sm text-neutral! font-hand" className="text-xxs md:text-sm text-neutral! font-hand"
> >
(unless this happens) &nbsp;(unless this happens)
</a> </a>
</span> </span>
<div className="w-18 h-18 flex shrink-0 items-center justify-end bg-success/20 rounded-full p-0 "> <div className="flex shrink-0 items-center justify-end bg-success/20 rounded-full p-4 ">
<VaultIcon <VaultIcon
size={36} size={36}
weight="duotone" weight="duotone"
@@ -333,10 +340,10 @@ function SpecsSection() {
</Modal> </Modal>
<p className="text-sm md:text-lg"> <p className="text-sm md:text-lg">
Of course, this level of{" "} Of course, this level of&nbsp;
<span className="text-success font-bold">privacy</span> comes with a <span className="text-success font-bold">privacy</span> comes with a
catch. <span className="text-error font-bold">No password reset</span>{" "} catch. <span className="text-error font-bold">No password reset</span>
for you. &nbsp; for you.
</p> </p>
<p className="text-xs md:text-base alert alert-warning font-medium"> <p className="text-xs md:text-base alert alert-warning font-medium">
<InfoIcon weight="duotone" /> Your original password is never stored <InfoIcon weight="duotone" /> Your original password is never stored
@@ -357,16 +364,17 @@ function OSSSection() {
} }
> >
<Logo type={"inline"} /> <Logo type={"inline"} />
is{" "} is&nbsp;
<span className="line-through decoration-6 text-neutral-content/50 decoration-error"> <span className="line-through decoration-6 text-neutral-content/50 decoration-error">
&nbsp;private &nbsp;private
<span className="absolute -translate-y-2 -translate-x-42 md:-translate-x-72 font-hand text-xs md:text-xl opacity-70 rotate-8 tracking-normal inline-flex items-center not-italic w-48 md:w-100 flex-wrap"> <span className="absolute -translate-y-2 -translate-x-42 md:-translate-x-72 font-hand text-xs md:text-xl opacity-70 rotate-8 tracking-normal inline-flex items-center not-italic w-48 md:w-100 flex-wrap">
only for only for
<span className="text-primary">&nbsp;your letters&nbsp;</span>{" "} <span className="text-primary">&nbsp;your letters&nbsp;</span>&nbsp;
<SmileyIcon weight="duotone" className="text-primary" /> <SmileyIcon weight="duotone" className="text-primary" />
<ArrowArcLeftIcon className="text-accent inline rotate-45 -translate-y" /> <ArrowArcLeftIcon className="text-accent inline rotate-45 -translate-y" />
</span> </span>
</span>{" "} </span>
&nbsp;
<span className="text-success -rotate-3">open source !</span> <span className="text-success -rotate-3">open source !</span>
</h1> </h1>
<div className="flex flex-col items-center shrink-0 max-w-11/12 w-220 gap-4 p-4 md:p-6 text-neutral-content/80"> <div className="flex flex-col items-center shrink-0 max-w-11/12 w-220 gap-4 p-4 md:p-6 text-neutral-content/80">
@@ -378,8 +386,9 @@ function OSSSection() {
don't have to take my word at it. don't have to take my word at it.
</p> </p>
<p className="text-sm md:text-lg"> <p className="text-sm md:text-lg">
You can also{" "} You can also&nbsp;
<span className="uppercase font-mono text-primary">Self-host</span>{" "} <span className="uppercase font-mono text-primary">Self-host</span>
&nbsp;
<Logo type={"inline"} /> in just 4 steps. <Logo type={"inline"} /> in just 4 steps.
</p> </p>
<div className="mockup-code w-110 max-w-11/12 text-xs"> <div className="mockup-code w-110 max-w-11/12 text-xs">
@@ -408,7 +417,7 @@ function OSSSection() {
</a> </a>
. .
<p className="text-xs md:text-base opacity-70"> <p className="text-xs md:text-base opacity-70">
Found something to report or request?{" "} Found something to report or request?&nbsp;
<a <a
href="https://git.ramvignesh.dev/me/pi-ku/issues" href="https://git.ramvignesh.dev/me/pi-ku/issues"
target="_blank" target="_blank"
@@ -449,16 +458,16 @@ function OSSSection() {
rel="noopener noreferrer" rel="noopener noreferrer"
> >
DaisyUI DaisyUI
</a>{" "} </a>
·{" "} &nbsp; ·&nbsp;
<a <a
href="http://fabricjs.com" href="http://fabricjs.com"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
Fabric.js Fabric.js
</a>{" "} </a>
·{" "} &nbsp; ·&nbsp;
<a <a
href="https://phosphoricons.com" href="https://phosphoricons.com"
target="_blank" target="_blank"
@@ -533,7 +542,7 @@ function StorySection() {
postscript; a note written after the letter is signed. postscript; a note written after the letter is signed.
<br /> <br />
<blockquote className="text-primary/50 italic mt-2 ml-2 border-l-primary/20 leading-none border-l"> <blockquote className="text-primary/50 italic mt-2 ml-2 border-l-primary/20 leading-none border-l">
"the most honest thing was always in the{" "} "the most honest thing was always in the&nbsp;
<span className="font-ink">பி. கு.</span>" <span className="font-ink">பி. கு.</span>"
</blockquote> </blockquote>
</li> </li>
@@ -559,7 +568,7 @@ function StorySection() {
<Logo type={"inline"} /> is an abbreviated transliteration of the <Logo type={"inline"} /> is an abbreviated transliteration of the
<span className="font-ink text-accent"> ி </span> <span className="font-ink text-accent"> ி </span>
<span className="italic text-xs md:text-base">(Tamil) </span>word <span className="italic text-xs md:text-base">(Tamil) </span>word
for{" "} for&nbsp;
<button <button
type="button" type="button"
className={ className={
@@ -583,9 +592,10 @@ function StorySection() {
cript cript
</span> </span>
. .
</button>{" "} </button>
&mdash;the thing you add after you've already signed your name, what &nbsp; &mdash;the thing you add after you've already signed your
you write when you thought you were finished, but weren't. name, what you write when you thought you were finished, but
weren't.
</p> </p>
<p> <p>
<span className={"font-medium text-primary"}> <span className={"font-medium text-primary"}>
@@ -594,7 +604,7 @@ function StorySection() {
<br /> <br />
It sits in drafts , in half-written notes, in the pause before we It sits in drafts , in half-written notes, in the pause before we
change the subject. <br /> change the subject. <br />
Those words{" "} Those words&nbsp;
<button <button
type="button" type="button"
className={ className={
@@ -602,8 +612,8 @@ function StorySection() {
} }
> >
don't just disappear. They don't just disappear. They
</button>{" "} </button>
stay{" "} &nbsp; stay&nbsp;
<span className={"text-primary font-hand font-extrabold"}> <span className={"text-primary font-hand font-extrabold"}>
unsaid unsaid
</span> </span>
@@ -640,10 +650,9 @@ function ForWhoSection() {
<Logo type={"mono"} /> wasn't built for one kind of person, but a <Logo type={"mono"} /> wasn't built for one kind of person, but a
particular kind of feeling&mdash; particular kind of feeling&mdash;
<span className="italic font-serif text-stone-900"> <span className="italic font-serif text-stone-900">
{" "} &nbsp; the one that lingers very quietly
the one that lingers very quietly </span>
</span>{" "} &nbsp; &mdash;fragile, yet never breaks.
&mdash;fragile, yet never breaks.
</p> </p>
<div className="pt-8 flex items-center gap-4"> <div className="pt-8 flex items-center gap-4">
@@ -681,8 +690,8 @@ function ArchetypesSection() {
open open
> >
<summary className="collapse-title md:text-xl leading-tight font-hand flex items-center gap-4"> <summary className="collapse-title md:text-xl leading-tight font-hand flex items-center gap-4">
<GhostIcon weight="duotone" className="text-accent" size={32} />{" "} <GhostIcon weight="duotone" className="text-accent" size={32} />
To someone you can't reach anymore. &nbsp; To someone you can't reach anymore.
</summary> </summary>
<div className="collapse-content text-sm md:text-lg flex flex-col gap-4"> <div className="collapse-content text-sm md:text-lg flex flex-col gap-4">
<p> <p>
@@ -712,8 +721,8 @@ function ArchetypesSection() {
weight="duotone" weight="duotone"
className="text-accent" className="text-accent"
size={32} size={32}
/>{" "} />
To someone who's still here. &nbsp; To someone who's still here.
</summary> </summary>
<div className="collapse-content text-sm md:text-lg flex flex-col gap-4"> <div className="collapse-content text-sm md:text-lg flex flex-col gap-4">
<p> <p>
@@ -742,7 +751,8 @@ function ArchetypesSection() {
weight="duotone" weight="duotone"
className="text-accent" className="text-accent"
size={14} size={14}
/>{" "} />
&nbsp;
<PersonArmsSpreadIcon <PersonArmsSpreadIcon
weight="duotone" weight="duotone"
className="text-accent" className="text-accent"
@@ -775,8 +785,8 @@ function ArchetypesSection() {
name="my-accordion-det-1" name="my-accordion-det-1"
> >
<summary className="collapse-title text-lg md:text-xl leading-tight font-hand flex items-center gap-4"> <summary className="collapse-title text-lg md:text-xl leading-tight font-hand flex items-center gap-4">
<SparkleIcon weight="duotone" className="text-accent" size={32} />{" "} <SparkleIcon weight="duotone" className="text-accent" size={32} />
For liberation. &nbsp; For liberation.
</summary> </summary>
<div className="collapse-content text-sm md:text-lg flex flex-col gap-4"> <div className="collapse-content text-sm md:text-lg flex flex-col gap-4">
<p> <p>
@@ -864,16 +874,16 @@ function AttributionSection() {
<p> <p>
<Logo type={"inline"} /> took a while to exist. <Logo type={"inline"} /> took a while to exist.
<br /> <br />
This started as a{" "} This started as a&nbsp;
<a <a
href="https://cs50.harvard.edu/web/" href="https://cs50.harvard.edu/web/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
CS50W CS50W
</a>{" "} </a>
capstone&mdash;one I kept postponing until I ran out of excuses. &nbsp; capstone&mdash;one I kept postponing until I ran out of
When I sat down to build it, it felt heavier than a typical excuses. When I sat down to build it, it felt heavier than a typical
assignment&mdash;not just because things were difficult. It had to assignment&mdash;not just because things were difficult. It had to
be something that outlasted the grade. I wanted to make this one be something that outlasted the grade. I wanted to make this one
count more than anything else I'd ever made. Something as close to count more than anything else I'd ever made. Something as close to
@@ -885,19 +895,19 @@ function AttributionSection() {
Of course, frustrations, id-exisi crises, crept in from time to Of course, frustrations, id-exisi crises, crept in from time to
time. But <Logo type="inline" /> helped me re-kindle the love for time. But <Logo type="inline" /> helped me re-kindle the love for
the odd hours spent obsessing over the tiniest UX decisions and the odd hours spent obsessing over the tiniest UX decisions and
endlessly polishing the UI{" "} endlessly polishing the UI&nbsp;
<span className="font-hand"> <span className="font-hand">
(only if I could've just made my mind up on one design system (only if I could've just made my mind up on one design system
sooner, instead of paddling in a sea of muses, muses everywhere) sooner, instead of paddling in a sea of muses, muses everywhere)
</span> </span>
. I know I've shared the nuts and bolts of <Logo type={"inline"} />{" "} . I know I've shared the nuts and bolts of <Logo type={"inline"} />
here&mdash;the core philosophies, how it all works&mdash;but the &nbsp; here&mdash;the core philosophies, how it all works&mdash;but
heart of it is really something you have to find by exploring it the heart of it is really something you have to find by exploring it
yourself. yourself.
</p> </p>
<p> <p>
The "why" behind all of this didn't just appear out of nowhere. For The "why" behind all of this didn't just appear out of nowhere. For
a while, I kept coming back to{" "} a while, I kept coming back to&nbsp;
<span <span
role="tooltip" role="tooltip"
className="cursor-default ul-wavy text-accent" className="cursor-default ul-wavy text-accent"
@@ -918,27 +928,29 @@ function AttributionSection() {
onMouseLeave={() => setHover((h) => ({ ...h, visible: false }))} onMouseLeave={() => setHover((h) => ({ ...h, visible: false }))}
> >
Saajan Saajan
</span>{" "} </span>
from{" "} &nbsp; from&nbsp;
<a <a
href="https://www.themoviedb.org/movie/191714-the-lunchbox" href="https://www.themoviedb.org/movie/191714-the-lunchbox"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
The Lunchbox The Lunchbox
</a>{" "} </a>
&mdash;brought to life with such subtle brilliance by{" "} &nbsp; &mdash;brought to life with such subtle brilliance by&nbsp;
<a <a
className="text-accent!" className="text-accent!"
href="https://www.themoviedb.org/person/76793-irrfan-khan" href="https://www.themoviedb.org/person/76793-irrfan-khan"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
Irrfan Khan Irrfan Khan
</a>{" "} </a>
&nbsp;
<PeaceIcon weight="duotone" className="inline text-accent" /> <PeaceIcon weight="duotone" className="inline text-accent" />
&mdash;the quiet emotional weight he carries through a lonely and &mdash;the quiet emotional weight he carries through a lonely and
mechanized life, right up until those letters arrive and something mechanized life, right up until those letters arrive and something
inside him finally loosens. The ending feels like a deep sigh of{" "} inside him finally loosens. The ending feels like a deep sigh
of&nbsp;
<span className="font-hand font-bold text-accent"> <span className="font-hand font-bold text-accent">
"it is what it is" "it is what it is"
</span> </span>
@@ -947,15 +959,15 @@ function AttributionSection() {
that a lot. that a lot.
</p> </p>
<p> <p>
There's a lot that goes{" "} There's a lot that goes&nbsp;
<span className={"text-primary font-hand text-lg md:text-xl"}> <span className={"text-primary font-hand text-lg md:text-xl"}>
unsaid unsaid
</span>{" "} </span>
these days. Not for a lack of feeling, not for the lack of time, but &nbsp; these days. Not for a lack of feeling, not for the lack of
because the ways we reach each other have quietly changed. We're time, but because the ways we reach each other have quietly changed.
always reachable <span className="italic">digitally,</span> yet We're always reachable <span className="italic">digitally,</span>
somehow the things that actually matter most end up staying &nbsp; yet somehow the things that actually matter most end up
inside&mdash;a trapped one at that. staying inside&mdash;a trapped one at that.
<br /> <br />
Maybe writing can/will help. Maybe putting words somewhere Maybe writing can/will help. Maybe putting words somewhere
deliberate makes them feel less like a weight you're carrying alone. deliberate makes them feel less like a weight you're carrying alone.
@@ -973,7 +985,7 @@ function AttributionSection() {
</p> </p>
<p className="text-xs md:text-sm opacity-75 font-mono"> <p className="text-xs md:text-sm opacity-75 font-mono">
P.S. And just so we're clear&mdash;I wrote every word of this P.S. And just so we're clear&mdash;I wrote every word of this
myself&mdash;as I continue to back{" "} myself&mdash;as I continue to back&nbsp;
<a <a
href="https://em-dash-appreciation.org/" href="https://em-dash-appreciation.org/"
target="_blank" target="_blank"
@@ -981,7 +993,7 @@ function AttributionSection() {
> >
Em DASH Em DASH
</a> </a>
. Why should AI get to have all the fun with 'em em dashes?{" "} . Why should AI get to have all the fun with 'em em dashes?&nbsp;
<span className="font-hand">(get it?)</span> <span className="font-hand">(get it?)</span>
</p> </p>
</div> </div>
@@ -990,10 +1002,10 @@ function AttributionSection() {
weight="duotone" weight="duotone"
size={48} size={48}
className="rotate-180 text-neutral-content" className="rotate-180 text-neutral-content"
/>{" "} />
I think we forget things if there is nobody to tell them. &nbsp; I think we forget things if there is nobody to tell them.
<span className="block mt-2 text-sm not-italic text-base-200/70 w-full text-right"> <span className="block mt-2 text-sm not-italic text-base-200/70 w-full text-right">
~ Saajan Fernandes,{" "} ~ Saajan Fernandes,&nbsp;
<span className="italic underline decoration-dotted"> <span className="italic underline decoration-dotted">
The Lunchbox The Lunchbox
</span> </span>
+2 -1
View File
@@ -59,7 +59,8 @@ export default function Activate() {
You're in. You're in.
</h2> </h2>
<p className="opacity-70 leading-relaxed"> <p className="opacity-70 leading-relaxed">
Welcome to <Logo scale={1} /> Welcome to&nbsp;
<Logo type="inline" />
<br /> <br />
Just one more step and you can start writing timeless letters. Just one more step and you can start writing timeless letters.
</p> </p>
+1 -1
View File
@@ -94,7 +94,7 @@ describe("Drawer Page", () => {
); );
expect(screen.getByTestId("passkey-modal-title")).toBeInTheDocument(); expect(screen.getByTestId("passkey-modal-title")).toBeInTheDocument();
expect(screen.getByPlaceholderText(/password/i)).toBeInTheDocument(); expect(screen.getByTestId("passkey-input")).toBeInTheDocument();
}); });
it("renders the welcome letter when firstTime state is present", () => { it("renders the welcome letter when firstTime state is present", () => {
+2 -2
View File
@@ -58,7 +58,7 @@ export default function Drawer() {
Personal Archive Personal Archive
</div> </div>
<div className="mt-6 font-sans text-sm text-base-content flex items-center justify-center gap-2 opacity-60 hover:opacity-100 transition-opacity"> <div className="mt-6 font-sans text-sm text-base-content flex items-center justify-center gap-2 opacity-60 hover:opacity-100 transition-opacity">
Welcome Back{" "} Welcome Back&nbsp;
<span className="font-semibold text-primary">{user.full_name}</span> <span className="font-semibold text-primary">{user.full_name}</span>
<button <button
type="button" type="button"
@@ -180,7 +180,7 @@ export default function Drawer() {
weight="duotone" weight="duotone"
className="text-primary/30 transition-all duration-300 group-hover:text-primary" className="text-primary/30 transition-all duration-300 group-hover:text-primary"
/> />
Write something{" "} Write something&nbsp;
<span className="relative inline-flex"> <span className="relative inline-flex">
<span className="transition-opacity duration-500 opacity-80 group-hover:opacity-0"> <span className="transition-opacity duration-500 opacity-80 group-hover:opacity-0">
. . . . . . . . . . . .
+8 -13
View File
@@ -97,25 +97,22 @@ describe("Editor Page", () => {
fireEvent.click(sealBtn); fireEvent.click(sealBtn);
// Click Vault to show confirm modal // Click Vault to show confirm modal
const vaultBtn = screen.getByRole("button", { name: /vault/i }); const vaultBtn = screen.getByTestId("vault-trigger-btn");
fireEvent.click(vaultBtn); fireEvent.click(vaultBtn);
// Set date and submit vault form // Set date and submit vault form
const dateInput = container.querySelector('input[name="vault-date"]'); const dateInput = document.body.querySelector('input[name="vault-date"]');
if (!dateInput) throw new Error("Date input not found"); if (!dateInput) throw new Error("Date input not found");
fireEvent.change(dateInput, { target: { value: "2026-12-31" } }); fireEvent.change(dateInput, { target: { value: "2026-12-31" } });
const confirmVaultBtn = container.querySelector( const confirmVaultBtn = screen.getByTestId("vault-confirm-btn");
'button[form="vault-form"]',
);
if (!confirmVaultBtn) throw new Error("Confirm vault button not found");
fireEvent.click(confirmVaultBtn); fireEvent.click(confirmVaultBtn);
// Wait for save to complete and check readOnly // Wait for save to complete and check readOnly
expect(await screen.findByTestId("save-success-toast")).toBeInTheDocument(); expect(await screen.findByTestId("save-success-toast")).toBeInTheDocument();
expect(canvas.getAttribute("data-readonly")).toBe("true"); expect(canvas.getAttribute("data-readonly")).toBe("true");
expect(screen.getByLabelText(/recipient/i)).toBeDisabled(); expect(screen.getByTestId("recipient-input")).toBeDisabled();
}); });
it("should set canvas to readOnly when status is SEALED", async () => { it("should set canvas to readOnly when status is SEALED", async () => {
@@ -135,7 +132,7 @@ describe("Editor Page", () => {
}), }),
); );
const { container } = render( render(
<MemoryRouter initialEntries={["/write/test-id"]}> <MemoryRouter initialEntries={["/write/test-id"]}>
<Routes> <Routes>
<Route path="/write/:public_id" element={<Editor />} /> <Route path="/write/:public_id" element={<Editor />} />
@@ -149,19 +146,17 @@ describe("Editor Page", () => {
const canvas = screen.getByTestId("canvas"); const canvas = screen.getByTestId("canvas");
const toolbar = container.querySelector("#writer-toolbar"); const sealBtn = screen.getByTestId("seal-trigger-btn");
const sealBtn = toolbar?.querySelector(".btn-primary");
if (!sealBtn) throw new Error("Seal button not found");
fireEvent.click(sealBtn); fireEvent.click(sealBtn);
// The secondary seal button appears (it has btn-accent class) // The secondary seal button appears (it has btn-accent class)
const secondarySealBtn = container.querySelector(".btn-accent"); const secondarySealBtn = screen.getByTestId("seal-confirm-btn");
if (!secondarySealBtn) throw new Error("Secondary seal button not found"); if (!secondarySealBtn) throw new Error("Secondary seal button not found");
fireEvent.click(secondarySealBtn); fireEvent.click(secondarySealBtn);
expect(await screen.findByTestId("save-success-toast")).toBeInTheDocument(); expect(await screen.findByTestId("save-success-toast")).toBeInTheDocument();
expect(canvas.getAttribute("data-readonly")).toBe("true"); expect(canvas.getAttribute("data-readonly")).toBe("true");
expect(screen.getByLabelText(/recipient/i)).toBeDisabled(); expect(screen.getByTestId("recipient-input")).toBeDisabled();
}); });
}); });
+8 -9
View File
@@ -149,7 +149,7 @@ export default function Home() {
}} }}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
> >
pen down your unsaid words into{" "} pen down your unsaid words into&nbsp;
<span className="font-display text-primary font-extralight"> <span className="font-display text-primary font-extralight">
letters letters
</span> </span>
@@ -171,11 +171,11 @@ export default function Home() {
}} }}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
> >
seal it{" "} seal it&nbsp;
<span className="text-success font-mono tracking-tighter font-extrabold"> <span className="text-success font-mono tracking-tighter font-extrabold">
secure secure
</span>{" "} </span>
and{" "} &nbsp; and&nbsp;
<span className="text-info font-mono tracking-tighter italic"> <span className="text-info font-mono tracking-tighter italic">
private private
</span> </span>
@@ -197,7 +197,7 @@ export default function Home() {
}} }}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
> >
send it to{" "} send it to&nbsp;
<motion.span <motion.span
className="font-display text-accent" className="font-display text-accent"
style={{ style={{
@@ -225,8 +225,7 @@ export default function Home() {
), ),
}} }}
> >
{" "} &nbsp; or&nbsp;
or{" "}
</motion.span> </motion.span>
<span className="font-display text-success"> <span className="font-display text-success">
yourself in the future yourself in the future
@@ -250,8 +249,8 @@ export default function Home() {
}} }}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
> >
and even <span className="font-display text-error">burn it</span>{" "} and even <span className="font-display text-error">burn it</span>
to release the burden. &nbsp; to release the burden.
</motion.h2> </motion.h2>
{/* Outro */} {/* Outro */}
<motion.h2 <motion.h2
+6 -6
View File
@@ -27,9 +27,9 @@ describe("Login Page", () => {
</MemoryRouter>, </MemoryRouter>,
); );
await userEvent.type(screen.getByLabelText(/email/i), "test@example.com"); await userEvent.type(screen.getByTestId("email-input"), "test@example.com");
await userEvent.type(screen.getByLabelText(/password/i), "password123"); await userEvent.type(screen.getByTestId("password-input"), "password123");
await userEvent.click(screen.getByRole("button", { name: /sign in/i })); await userEvent.click(screen.getByTestId("login-submit-btn"));
expect(await screen.findByTestId("login-error-message")).toHaveTextContent( expect(await screen.findByTestId("login-error-message")).toHaveTextContent(
/technical issues/i, /technical issues/i,
@@ -87,9 +87,9 @@ describe("Login Page", () => {
</MemoryRouter>, </MemoryRouter>,
); );
await userEvent.type(screen.getByLabelText(/email/i), "test@example.com"); await userEvent.type(screen.getByTestId("email-input"), "test@example.com");
await userEvent.type(screen.getByLabelText(/password/i), "password123"); await userEvent.type(screen.getByTestId("password-input"), "password123");
await userEvent.click(screen.getByRole("button", { name: /sign in/i })); await userEvent.click(screen.getByTestId("login-submit-btn"));
const expectedTestId = const expectedTestId =
nextRoute.toLowerCase() === "drawer" ? "drawer-page" : "reader-page"; nextRoute.toLowerCase() === "drawer" ? "drawer-page" : "reader-page";
+8 -7
View File
@@ -120,28 +120,29 @@ export default function Login() {
type="submit" type="submit"
name="login" name="login"
disabled={isLoading} disabled={isLoading}
aria-label="Sign In"
data-testid="login-submit-btn" data-testid="login-submit-btn"
className="btn btn-primary w-full shadow-lg" className="btn btn-primary w-full shadow-lg"
> >
{isLoading ? ( {isLoading ? (
<span className="loading loading-spinner loading-sm" /> <span className="loading loading-spinner loading-sm" />
) : ( ) : (
"Sign In" "Continue"
)} )}
</button> </button>
</div> </div>
<div className="divider text-neutral my-0">or</div>
<div className="text-center text-sm font-medium text-base-content/70"> <div className="text-center text-sm font-medium text-neutral">
Don't have an account?{" "} New to <Logo type="inline" />
?&nbsp;
<button <button
type="button" type="button"
name="register" name="register"
onClick={() => navigate(ROUTES.ONBOARD)} onClick={() => navigate(ROUTES.ONBOARD)}
className="link link-primary no-underline hover:underline font-bold" className="link link-primary"
> >
Register Start here
</button> </button>
.
</div> </div>
</form> </form>
</div> </div>
+19 -5
View File
@@ -77,7 +77,8 @@ export default function Register() {
<div className="glass-card w-full max-w-sm p-2 transition-all duration-500 hover:shadow-2xl fade-zoom"> <div className="glass-card w-full max-w-sm p-2 transition-all duration-500 hover:shadow-2xl fade-zoom">
<form onSubmit={handleSubmit(onSubmit)} className="card-body gap-4"> <form onSubmit={handleSubmit(onSubmit)} className="card-body gap-4">
<div className="card-title font-display text-2xl justify-center text-primary/80 tracking-tight whitespace-nowrap"> <div className="card-title font-display text-2xl justify-center text-primary/80 tracking-tight whitespace-nowrap">
Create a <Logo type="logo" scale={0.7} /> Account Create a<Logo type="logo" scale={0.7} />
Account
</div> </div>
{apiError && ( {apiError && (
@@ -143,9 +144,9 @@ export default function Register() {
<InfoIcon size={20} weight="duotone" className="mt-0.5 shrink-0" /> <InfoIcon size={20} weight="duotone" className="mt-0.5 shrink-0" />
<p className="text-sm font-semibold"> <p className="text-sm font-semibold">
Choose a password you won't forget. <br /> Choose a password you won't forget. <br />
Just like life,{" "} Just like life,&nbsp;
<span className="underline decoration-2">there is no reset</span>{" "} <span className="underline decoration-2">there is no reset</span>
here. If you lose it, your letters cannot be recovered. &nbsp; here. If you lose it, your letters cannot be recovered.
</p> </p>
</div> </div>
@@ -160,10 +161,23 @@ export default function Register() {
{isLoading ? ( {isLoading ? (
<span className="loading loading-spinner loading-sm" /> <span className="loading loading-spinner loading-sm" />
) : ( ) : (
"Register" "Begin"
)} )}
</button> </button>
</div> </div>
<div className="divider text-neutral my-0">or</div>
<div className="text-center text-sm font-medium text-neutral">
Been here before?&nbsp;
<button
type="button"
name="register"
onClick={() => navigate(ROUTES.LOGIN)}
className="link link-primary"
>
Continue where you left off
</button>
.
</div>
</form> </form>
</div> </div>
</div> </div>
+2 -2
View File
@@ -23,8 +23,8 @@ export default function VerifyEmail() {
Check Your Mailbox Check Your Mailbox
</h2> </h2>
<p className="text-sm opacity-80 leading-relaxed font-sans mt-6"> <p className="text-sm opacity-80 leading-relaxed font-sans mt-6">
You're one train away from starting your <Logo scale={0.8} />{" "} You're one train away from starting your <Logo scale={0.8} />
journey. &nbsp; journey.
</p> </p>
</div> </div>