Skip to content

Upgrade dependencies and add release workflow - #44

Open
macintushar wants to merge 2 commits into
mainfrom
chore/upgrade-deps-and-releases
Open

Upgrade dependencies and add release workflow#44
macintushar wants to merge 2 commits into
mainfrom
chore/upgrade-deps-and-releases

Conversation

@macintushar

@macintushar macintushar commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • Bump all dependencies to latest versions in package.json and bun.lock
  • Migrate from Tailwind CSS v3 to v4 (new PostCSS plugin, remove tailwindcss-animate, add tw-animate-css)
  • Upgrade React 18 → 19, TypeScript 5 → 6, Vite 5 → 8, ESLint 9 → 10
  • Add .github/workflows/release.yml with cron (weekly Friday) and manual triggers
  • Remove postcss.config.js lingering v3 config and update import paths (src/index.css, tailwind.config.ts)
  • Re-generate routeTree.gen.ts and rebuild tsconfig.app.tsbuildinfo / tsconfig.node.tsbuildinfo

Testing

  • Project builds cleanly (npm run build)
  • Dev server starts without errors (npm run dev)
  • Tailwind v4 utility classes render correctly (checked home page and profile dropdown)
  • React 19 + new JSX transform work (no import React errors)
  • TypeScript 6 compilation succeeds with updated configs
  • Release workflow: Not run (requires merge to main to test)
  • ESLint passes on changed files (npm run lint)

Summary by CodeRabbit

  • New Features
    • Added a new sidebar-based app layout with recent pages, quick navigation, and theme switching.
    • Introduced OAuth “Continue with Google” and new authentication screens (forgot password, verify email, update password) with email confirmation/resend flows.
    • Refreshed the Pages experience with a “Start drawing” flow and page preview thumbnails.
  • Bug Fixes
    • Improved UI loading/disabled behavior across buttons and updated the GitHub profile menu icon.
  • Chores
    • Bumped the app version to 0.1.0 and refreshed dependencies/tooling, styling, and Tailwind configuration.

- Bump React, Tailwind CSS, TypeScript, Vite to latest major versions
- Add GitHub Actions release workflow (.github/workflows/release.yml)
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
draw Ready Ready Preview, Comment Jul 22, 2026 7:26am

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds release automation, upgrades the frontend platform, introduces authentication and email-template workflows, replaces the navbar with a responsive sidebar, redesigns page and profile views, and refreshes the shared UI component system and styling configuration.

Changes

Release and Platform Migration

Layer / File(s) Summary
Release automation
.github/workflows/release.yml, package.json
Adds scheduled/manual semver release creation with changelog generation and tag guards, and bumps the package version to 0.1.0.
Dependency and Tailwind migration
package.json, postcss.config.js, tailwind.config.ts, src/index.css, components.json
Upgrades React and tooling, adopts Tailwind v4 PostCSS configuration, adds theme tokens, updates fonts, and changes UI generator settings.
Environment and routing configuration
src/env.ts, src/db/supabase.ts, src/main.tsx, tsconfig*.json, src/routeTree.gen.ts, src/routes/*
Validates environment variables, removes baseUrl, updates build metadata, and wires new authentication routes into generated TanStack Router definitions.

Authentication and Email Workflows

Layer / File(s) Summary
Authentication helpers and pages
src/db/auth.ts, src/lib/schemas.ts, src/views/ForgotPassword.tsx, src/views/UpdatePassword.tsx, src/views/VerifyEmail.tsx, src/views/Login.tsx, src/views/SignUp.tsx, src/components/OAuthButtons.tsx
Adds password reset, email verification, Google OAuth, redirect handling, and linked identity operations.
Email template rendering
email-templates/**, docs/supabase.md, .gitignore
Adds shared React Email layouts and confirmation, reset-password, and change-email templates with static HTML export support and setup documentation.

Workspace Navigation and Page Experience

Layer / File(s) Summary
Responsive sidebar and application layout
src/components/ui/sidebar.tsx, src/components/ui/sheet.tsx, src/components/AppSidebar.tsx, src/views/Layout.tsx, src/hooks/use-mobile.ts
Adds responsive sidebar state, mobile sheet rendering, navigation, recent pages, profile actions, and replaces the navbar-based layout.
Page previews and workspace views
src/components/PagePreview.tsx, src/lib/utils.ts, src/views/Pages.tsx, src/views/Page.tsx
Adds SVG page thumbnails and relative-time formatting, redesigns the page grid, and moves page controls above Excalidraw.
Profile and linked accounts
src/views/Profile.tsx, src/db/auth.ts
Splits profile settings into profile, appearance, password, and linked-account sections with OAuth link/unlink flows.

UI Component System

Layer / File(s) Summary
Form and input primitives
src/components/ui/button.tsx, card.tsx, dropdown-menu.tsx, form.tsx, input.tsx, label.tsx, textarea.tsx
Refactors shared primitives to function wrappers, updates styling and data attributes, changes ref contracts, and adds button/card variants.
Overlay and feedback primitives
src/components/ui/separator.tsx, sheet.tsx, skeleton.tsx, sonner.tsx, tooltip.tsx
Adds separator, sheet, and skeleton components and updates tooltip and toast rendering for the new theme.

Supporting Styling and Documentation

Layer / File(s) Summary
Supporting styling and documentation
src/components/ProfileDropdown.tsx, src/routes/__root.tsx, src/components/TitleBar.tsx, src/views/HomePage.tsx, src/views/Mermaid.tsx, docs/mockups/sidebar-layout.html, docs/supabase.md
Updates icons, dark-mode colors, font classes, tooltip imports, authentication documentation, and a static sidebar mockup.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • macintushar/draw#4: Both PRs modify src/views/Navbar.tsx and its navigation implementation.
  • macintushar/draw#21: Both PRs update src/views/Page.tsx around Excalidraw WelcomeScreen rendering.

Poem

A bunny finds new routes to hop,
Sends reset mails from every stop,
A sidebar blooms with pages bright,
Tailwind paints the theme just right,
OAuth joins the carrot cart! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the two main changes: dependency upgrades and the new release workflow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/upgrade-deps-and-releases

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
package.json (1)

24-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

bun listed as a runtime dependency is unusual.

bun (and @types/bun) is the runtime/toolchain rather than an app dependency; pinning it under dependencies doesn't install a usable Bun binary and can be confusing. Consider relying on the CI/.tool-versions/engines to pin Bun instead, unless this is intentional for a specific reason.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 24, The package.json dependency list includes bun as an
app runtime dependency, which is unusual and can be misleading. Update the
package metadata around the bun entry so Bun is pinned via the project’s
runtime/tooling configuration instead of dependencies, unless there is a
deliberate reason to keep it there; use the package.json dependency block and
any related Bun/runtime settings as the location to adjust.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 27-30: The checkout step in the release workflow is persisting the
GITHUB_TOKEN in git config unnecessarily. Update the existing
actions/checkout@v4 configuration to disable credential persistence by setting
persist-credentials to false while keeping the full-history/tag fetch settings
unchanged.

In `@package.json`:
- Line 53: Remove the stray install dependency from package.json and update the
lockfile accordingly. The dependency is unused, so delete the install entry from
the dependencies section and regenerate or edit the lockfile so it no longer
references it, keeping the change consistent with the package manifest.

In `@src/index.css`:
- Around line 1-5: Stylelint is flagging Tailwind v4 directives as unknown
at-rules in the stylesheet; update the stylelint configuration to allowlist the
Tailwind-specific directives used in src/index.css. Adjust the rule that powers
scss/at-rule-no-unknown so it ignores `@config` and `@custom-variant` (and any other
Tailwind v4 at-rules already present in the stylesheet), keeping the change in
the stylelint config rather than the CSS itself.

---

Nitpick comments:
In `@package.json`:
- Line 24: The package.json dependency list includes bun as an app runtime
dependency, which is unusual and can be misleading. Update the package metadata
around the bun entry so Bun is pinned via the project’s runtime/tooling
configuration instead of dependencies, unless there is a deliberate reason to
keep it there; use the package.json dependency block and any related Bun/runtime
settings as the location to adjust.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0fcbc1e3-84e2-4be1-b02a-aca94aca1a5d

📥 Commits

Reviewing files that changed from the base of the PR and between 2d6c73c and ad6f268.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • .github/workflows/release.yml
  • package.json
  • postcss.config.js
  • src/components/ProfileDropdown.tsx
  • src/index.css
  • src/routeTree.gen.ts
  • src/views/Page.tsx
  • tailwind.config.ts
  • tsconfig.app.json
  • tsconfig.app.tsbuildinfo
  • tsconfig.json
  • tsconfig.node.tsbuildinfo
💤 Files with no reviewable changes (2)
  • tsconfig.json
  • tsconfig.app.json

Comment on lines +27 to +30
- name: Checkout (full history + tags)
uses: actions/checkout@v4
with:
fetch-depth: 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Set persist-credentials: false on checkout.

actions/checkout persists the GITHUB_TOKEN in .git/config by default. This workflow never pushes via git (the release is created through action-gh-release using the token directly), so the persisted credential is unnecessary and leaves a credential exposed to any later step.

🔒️ Proposed hardening
       - name: Checkout (full history + tags)
         uses: actions/checkout@v4
         with:
           fetch-depth: 0
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout (full history + tags)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Checkout (full history + tags)
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 27-30: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 27 - 30, The checkout step in the
release workflow is persisting the GITHUB_TOKEN in git config unnecessarily.
Update the existing actions/checkout@v4 configuration to disable credential
persistence by setting persist-credentials to false while keeping the
full-history/tag fetch settings unchanged.

Source: Linters/SAST tools

Comment thread package.json
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.3",
"globals": "^17.7.0",
"install": "^0.13.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm nothing references the `install` package.
rg -nP "require\(['\"]install['\"]\)|from ['\"]install['\"]" .

Repository: macintushar/draw

Length of output: 154


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- package.json excerpt ---'
cat -n package.json | sed -n '40,65p'

echo
echo '--- plain-text references to "install" (filtered to likely relevant paths) ---'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/.git/**' --glob '!package-lock.json' --glob '!pnpm-lock.yaml' --glob '!yarn.lock' '\binstall\b' .

echo
echo '--- package.json dependency entries ---'
python3 - <<'PY'
import json
from pathlib import Path
pkg = json.loads(Path('package.json').read_text())
for section in ('dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'):
    deps = pkg.get(section, {})
    if 'install' in deps:
        print(section, deps['install'])
PY

Repository: macintushar/draw

Length of output: 2141


Remove the stray install dependency. install doesn’t appear to be used anywhere; remove it from package.json and the lockfile to avoid unnecessary supply-chain surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 53, Remove the stray install dependency from
package.json and update the lockfile accordingly. The dependency is unused, so
delete the install entry from the dependencies section and regenerate or edit
the lockfile so it no longer references it, keeping the change consistent with
the package manifest.

Comment thread src/index.css
Comment on lines +1 to +5
@import "tailwindcss";
@import "tw-animate-css";
@config "../tailwind.config.ts";

@custom-variant dark (&:is(.dark *));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update stylelint to recognize Tailwind v4 at-rules.

Stylelint flags @config (Line 3) and @custom-variant (Line 5) as unknown at-rules via scss/at-rule-no-unknown. These are valid Tailwind v4 directives, so this will produce persistent CI/editor lint noise (and may fail lint gates) unless the stylelint config allowlists them.

🔧 Suggested stylelint config update
 rules: {
   "scss/at-rule-no-unknown": [
     true,
     {
-      ignoreAtRules: [/* existing entries */],
+      ignoreAtRules: [
+        "tailwind",
+        "apply",
+        "layer",
+        "config",
+        "custom-variant",
+        "theme",
+        "utility",
+        "source",
+        /* existing entries */
+      ],
     },
   ],
 },
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 3-3: Unexpected unknown at-rule "@config" (scss/at-rule-no-unknown)

(scss/at-rule-no-unknown)


[error] 5-5: Unexpected unknown at-rule "@custom-variant" (scss/at-rule-no-unknown)

(scss/at-rule-no-unknown)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.css` around lines 1 - 5, Stylelint is flagging Tailwind v4
directives as unknown at-rules in the stylesheet; update the stylelint
configuration to allowlist the Tailwind-specific directives used in
src/index.css. Adjust the rule that powers scss/at-rule-no-unknown so it ignores
`@config` and `@custom-variant` (and any other Tailwind v4 at-rules already present
in the stylesheet), keeping the change in the stylelint config rather than the
CSS itself.

Source: Linters/SAST tools

- Add forgot-password, update-password, and verify-email pages
- Add React Email templates for auth emails
- Replace Navbar with AppSidebar using Sheet component
- Add OAuth buttons and env validation
- Swap Virgil font for Excalifont
- Upgrade UI components and add Dialog, Separator, Skeleton

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/ui/skeleton.tsx (1)

1-14: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Import React types in these files

src/components/ui/skeleton.tsx and src/components/ui/sonner.tsx both use React.ComponentProps / React.CSSProperties without importing React, and the current tsconfig doesn’t enable allowUmdGlobalAccess. Add import type * as React from "react" (or import the specific types) so these modules compile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/skeleton.tsx` around lines 1 - 14, Add a type-only React
import in the Skeleton component module so React.ComponentProps resolves without
relying on a global UMD namespace. Apply the same import fix in
src/components/ui/skeleton.tsx lines 1-14 and src/components/ui/sonner.tsx lines
26-33, where React.CSSProperties is referenced.
🧹 Nitpick comments (4)
src/views/Pages.tsx (1)

28-47: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Shared React Query key ["pages"] with divergent queryFn. Both files register the same cache key but different fetchers/return shapes, so when both are mounted the effective queryFn (and its toast side effect / null vs {data,error} return) is nondeterministic. Unify the fetcher or use distinct keys.

  • src/views/Pages.tsx#L28-L47: extract the shared pages fetcher (or key it distinctly, e.g. ["pages","list"]) and keep the toast/empty-session handling in one place.
  • src/components/AppSidebar.tsx#L86-L97: consume the same shared fetcher/key so the recents list and the full list stay consistent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/views/Pages.tsx` around lines 28 - 47, Unify the pages query contract
across src/views/Pages.tsx:28-47 and src/components/AppSidebar.tsx:86-97 by
extracting or reusing one shared fetcher and query key, preserving the toast and
empty-session handling in that shared implementation; update both query sites to
consume it so the full pages list and recents use the same return shape and
behavior.
src/index.css (1)

90-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Target the sidebar's own data-slot attributes instead of positional child selectors.

The comment explains why the offcanvas geometry needs a hard override, but > div:first-child / > div:last-child + !important is fragile — it silently breaks if the sidebar's internal markup gains/reorders a wrapper. src/components/ui/sidebar.tsx already exposes data-slot="sidebar-gap" and data-slot="sidebar-container" on exactly these divs, which is a stable, self-documenting target. Also, the override only patches left; if AppSidebar (or any future consumer) ever renders with side="right", this rule won't undo the right positioning used by that variant.

♻️ Suggested fix
-[data-collapsible="offcanvas"] > div:first-child {
+[data-collapsible="offcanvas"] [data-slot="sidebar-gap"] {
   width: 0 !important;
 }
-[data-collapsible="offcanvas"] > div:last-child {
+[data-collapsible="offcanvas"] [data-slot="sidebar-container"] {
   left: calc(var(--sidebar-width) * -1) !important;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.css` around lines 90 - 100, Update the offcanvas overrides to
target the sidebar’s stable data-slot attributes, using sidebar-gap for the
width rule and sidebar-container for positioning instead of
first-child/last-child selectors. Preserve the hard offcanvas geometry while
clearing the side-specific right positioning as well as overriding left, so both
left- and right-sided sidebars collapse correctly.
tsconfig.app.tsbuildinfo (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stop tracking the generated .tsbuildinfo files. tsconfig.app.tsbuildinfo and tsconfig.node.tsbuildinfo are both tracked and not ignored; remove them from git and add a *.tsbuildinfo ignore rule so they don’t keep churning diffs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tsconfig.app.tsbuildinfo` at line 1, Remove the tracked generated
tsconfig.app.tsbuildinfo and tsconfig.node.tsbuildinfo files from version
control, and add a *.tsbuildinfo rule to the repository ignore configuration so
future TypeScript build-info files remain untracked.
src/db/auth.ts (1)

20-31: 🎯 Functional Correctness | 🔵 Trivial

Verify new redirect targets are allow-listed in Supabase.

sendPasswordReset (/update-password), linkIdentity (/profile), signInWithOAuth/signUp/resend (/pages) all pass window.location.origin + a path as redirectTo/emailRedirectTo. Supabase rejects/redirects to the Site URL for any redirect target not present in the project's Auth → URL Configuration allow-list. Please confirm /update-password and /profile (and /pages, if not already present) are added there for every deployed environment origin.

Also applies to: 64-107

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/db/auth.ts` around lines 20 - 31, Verify the Supabase Auth URL
Configuration includes the deployed origins with /update-password, /profile, and
/pages redirect paths used by sendPasswordReset, linkIdentity, signInWithOAuth,
signUp, and resend. Add any missing targets for every deployed environment
without changing the existing redirect construction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/mockups/sidebar-layout.html`:
- Around line 327-336: Replace the personal email text in the profile markup’s
email element with an obviously fake placeholder address, while leaving the
surrounding profile structure unchanged.

In `@email-templates/emails/components/layout.tsx`:
- Line 15: Update the asset URL configuration near baseUrl and the related image
rendering at the additionally affected lines to require a hosted
EMAIL_ASSETS_BASE_URL instead of falling back to an empty string. Ensure
generated HTML always uses an absolute hosted asset URL, and fail clearly when
the environment variable is missing.

In `@email-templates/render.tsx`:
- Line 41: Replace the console.log call in the rendering status message with the
lint-compliant process.stdout.write API, preserving the existing “Rendered
out/${name}.html” output and newline behavior.
- Around line 31-40: Update the output path handling around outDir to convert
both file URLs with fileURLToPath() instead of using URL.pathname, and use
join() when constructing the static directory and template HTML paths. Preserve
the existing mkdir, cp, and writeFile behavior while ensuring paths work with
percent-encoded names and Windows separators.

In `@email-templates/tsconfig.json`:
- Line 16: Update the include list in email-templates/tsconfig.json to include
both the existing emails directory and render.tsx, ensuring the render.tsx
imports and props are type-checked without removing the current emails coverage.

In `@src/components/PagePreview.tsx`:
- Around line 24-63: Remove the synchronous setIsEmpty calls from the useEffect
callback. Derive the empty state during render from the nonDeleted elements
collection, while retaining isEmpty state only for asynchronous export failures
in the catch handler; ensure the rendered empty condition still reflects either
no non-deleted elements or an export failure.

In `@src/components/ui/input.tsx`:
- Around line 32-42: Add an accessible name to the icon-only password toggle
button in the HiddenInput component, using a state-dependent aria-label that
clearly indicates whether it will show or hide the password. Preserve the
existing showPassword toggle behavior and icon rendering.

In `@src/index.css`:
- Line 9: Update the font-family declaration in the stylesheet to use the
unquoted Excalifont name, preserving the existing font choice while satisfying
the font-family-name-quotes lint rule.

In `@src/views/Layout.tsx`:
- Around line 12-49: The LayoutContent mobile flow lacks an external sidebar
trigger when the mobile sheet is closed. Update ExpandButton or add a nearby
trigger using the sidebar context’s mobile open state and toggle action, render
it outside AppSidebar so it remains visible on small screens, and preserve the
existing desktop collapsed-sidebar behavior.

In `@src/views/Login.tsx`:
- Around line 53-58: Update the error check in the Login submit flow to use
data.error.code === "email_not_confirmed" instead of matching data.error.message
text, while preserving the existing navigation to /verify-email with the
submitted email and the early return.

In `@src/views/Pages.tsx`:
- Around line 132-136: Update the delete control in Pages around Trash2 by
wrapping the icon in a focusable button with an accessible label describing page
deletion. Move the click handler to the button, preserve the existing styling
and handlePageDelete(page.page_id) behavior, and ensure the icon remains
decorative within the labeled button.

In `@src/views/VerifyEmail.tsx`:
- Around line 38-40: Initialize the cooldown state in VerifyEmail using the
existing emailParam: when a pre-filled email is present, seed it with the
intended resend cooldown; otherwise retain the zero cooldown for users without
an email parameter. Keep the existing email, loading, and cooldown behavior
unchanged after initialization.

---

Outside diff comments:
In `@src/components/ui/skeleton.tsx`:
- Around line 1-14: Add a type-only React import in the Skeleton component
module so React.ComponentProps resolves without relying on a global UMD
namespace. Apply the same import fix in src/components/ui/skeleton.tsx lines
1-14 and src/components/ui/sonner.tsx lines 26-33, where React.CSSProperties is
referenced.

---

Nitpick comments:
In `@src/db/auth.ts`:
- Around line 20-31: Verify the Supabase Auth URL Configuration includes the
deployed origins with /update-password, /profile, and /pages redirect paths used
by sendPasswordReset, linkIdentity, signInWithOAuth, signUp, and resend. Add any
missing targets for every deployed environment without changing the existing
redirect construction.

In `@src/index.css`:
- Around line 90-100: Update the offcanvas overrides to target the sidebar’s
stable data-slot attributes, using sidebar-gap for the width rule and
sidebar-container for positioning instead of first-child/last-child selectors.
Preserve the hard offcanvas geometry while clearing the side-specific right
positioning as well as overriding left, so both left- and right-sided sidebars
collapse correctly.

In `@src/views/Pages.tsx`:
- Around line 28-47: Unify the pages query contract across
src/views/Pages.tsx:28-47 and src/components/AppSidebar.tsx:86-97 by extracting
or reusing one shared fetcher and query key, preserving the toast and
empty-session handling in that shared implementation; update both query sites to
consume it so the full pages list and recents use the same return shape and
behavior.

In `@tsconfig.app.tsbuildinfo`:
- Line 1: Remove the tracked generated tsconfig.app.tsbuildinfo and
tsconfig.node.tsbuildinfo files from version control, and add a *.tsbuildinfo
rule to the repository ignore configuration so future TypeScript build-info
files remain untracked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3a764067-1f28-4c55-8b6f-20a0cea1fcd2

📥 Commits

Reviewing files that changed from the base of the PR and between ad6f268 and 366167e.

⛔ Files ignored due to path filters (6)
  • bun.lock is excluded by !**/*.lock
  • email-templates/bun.lock is excluded by !**/*.lock
  • email-templates/emails/static/draw-logo.png is excluded by !**/*.png
  • public/static/draw-logo.png is excluded by !**/*.png
  • src/assets/fonts/Excalifont.woff2 is excluded by !**/*.woff2
  • src/assets/fonts/Virgil.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (58)
  • .env.example
  • .gitignore
  • .tanstack/tmp/cfbf5386-d35b5b35ba1538933b305e6abb8fa3ab
  • components.json
  • docs/mockups/sidebar-layout.html
  • docs/supabase.md
  • email-templates/emails/change-email.tsx
  • email-templates/emails/components/layout.tsx
  • email-templates/emails/confirm-signup.tsx
  • email-templates/emails/reset-password.tsx
  • email-templates/package.json
  • email-templates/render.tsx
  • email-templates/tsconfig.json
  • package.json
  • src/components/AppSidebar.tsx
  • src/components/OAuthButtons.tsx
  • src/components/PagePreview.tsx
  • src/components/TitleBar.tsx
  • src/components/ui/button.tsx
  • src/components/ui/card.tsx
  • src/components/ui/dropdown-menu.tsx
  • src/components/ui/form.tsx
  • src/components/ui/input.tsx
  • src/components/ui/label.tsx
  • src/components/ui/separator.tsx
  • src/components/ui/sheet.tsx
  • src/components/ui/sidebar.tsx
  • src/components/ui/skeleton.tsx
  • src/components/ui/sonner.tsx
  • src/components/ui/textarea.tsx
  • src/components/ui/tooltip.tsx
  • src/db/auth.ts
  • src/db/supabase.ts
  • src/env.ts
  • src/hooks/use-mobile.ts
  • src/index.css
  • src/lib/schemas.ts
  • src/lib/utils.ts
  • src/main.tsx
  • src/routeTree.gen.ts
  • src/routes/__root.tsx
  • src/routes/forgot-password.lazy.tsx
  • src/routes/update-password.lazy.tsx
  • src/routes/verify-email.tsx
  • src/views/ForgotPassword.tsx
  • src/views/HomePage.tsx
  • src/views/Layout.tsx
  • src/views/Login.tsx
  • src/views/Mermaid.tsx
  • src/views/Navbar.tsx
  • src/views/Page.tsx
  • src/views/Pages.tsx
  • src/views/Profile.tsx
  • src/views/SignUp.tsx
  • src/views/UpdatePassword.tsx
  • src/views/VerifyEmail.tsx
  • tailwind.config.ts
  • tsconfig.app.tsbuildinfo
💤 Files with no reviewable changes (2)
  • src/views/Navbar.tsx
  • tailwind.config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json

Comment on lines +327 to +336
<div class="profile">
<div class="avatar">T</div>
<div class="who">
<div class="name">Tushar</div>
<div class="email">tusharkumar91111@gmail.com</div>
</div>
<button class="icon-btn" id="themeToggle" title="Toggle theme">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
</button>
</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Replace the real email address with a placeholder.

Line 331 embeds what appears to be a personal email address in a committed mockup. Even in demo markup this exposes it to scraping/spam; use an obviously fake placeholder instead.

🔒 Proposed fix
-        <div class="email">tusharkumar91111@gmail.com</div>
+        <div class="email">tushar@example.com</div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div class="profile">
<div class="avatar">T</div>
<div class="who">
<div class="name">Tushar</div>
<div class="email">tusharkumar91111@gmail.com</div>
</div>
<button class="icon-btn" id="themeToggle" title="Toggle theme">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
</button>
</div>
<div class="profile">
<div class="avatar">T</div>
<div class="who">
<div class="name">Tushar</div>
<div class="email">tushar@example.com</div>
</div>
<button class="icon-btn" id="themeToggle" title="Toggle theme">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
</button>
</div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/mockups/sidebar-layout.html` around lines 327 - 336, Replace the
personal email text in the profile markup’s email element with an obviously fake
placeholder address, while leaving the surrounding profile structure unchanged.

} from "react-email";
import type { ReactNode } from "react";

const baseUrl = process.env.EMAIL_ASSETS_BASE_URL ?? "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Require a hosted asset base URL.

With the current fallback, exported HTML contains src="/static/draw-logo.png". The renderer only copies assets locally; after pasting the template into Supabase, recipients cannot resolve this relative URL and the logo will be broken.

Proposed fix
-const baseUrl = process.env.EMAIL_ASSETS_BASE_URL ?? "";
+const assetBaseUrl = process.env.EMAIL_ASSETS_BASE_URL;
+if (!assetBaseUrl) {
+  throw new Error("EMAIL_ASSETS_BASE_URL must be configured");
+}
+const logoUrl = new URL("/static/draw-logo.png", assetBaseUrl).href;
...
-                  src={`${baseUrl}/static/draw-logo.png`}
+                  src={logoUrl}

Also applies to: 43-48

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@email-templates/emails/components/layout.tsx` at line 15, Update the asset
URL configuration near baseUrl and the related image rendering at the
additionally affected lines to require a hosted EMAIL_ASSETS_BASE_URL instead of
falling back to an empty string. Ensure generated HTML always uses an absolute
hosted asset URL, and fail clearly when the environment variable is missing.

Comment on lines +31 to +40
const outDir = new URL("./out/", import.meta.url).pathname;

await mkdir(outDir, { recursive: true });
await cp(new URL("./emails/static/", import.meta.url).pathname, outDir + "static", {
recursive: true,
});

for (const [name, element] of Object.entries(templates)) {
const html = await render(element, { pretty: true });
await writeFile(`${outDir}${name}.html`, html);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files email-templates/render.tsx
wc -l email-templates/render.tsx
cat -n email-templates/render.tsx | sed -n '1,120p'

Repository: macintushar/draw

Length of output: 1820


🏁 Script executed:

node - <<'JS'
const { fileURLToPath } = require('node:url');

const u1 = new URL('file:///tmp/My%20Folder/out/');
const u2 = new URL('file:///C:/tmp/My%20Folder/out/');

console.log('pathname1:', u1.pathname);
console.log('fileURLToPath1:', fileURLToPath(u1));
console.log('pathname2:', u2.pathname);
console.log('fileURLToPath2:', fileURLToPath(u2));
JS

Repository: macintushar/draw

Length of output: 298


Convert file URLs with fileURLToPath()

URL.pathname leaves percent-encoding intact and produces a bad Windows path here. Use fileURLToPath() for both URLs, then join() when building static and *.html output paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@email-templates/render.tsx` around lines 31 - 40, Update the output path
handling around outDir to convert both file URLs with fileURLToPath() instead of
using URL.pathname, and use join() when constructing the static directory and
template HTML paths. Preserve the existing mkdir, cp, and writeFile behavior
while ensuring paths work with percent-encoded names and Windows separators.

for (const [name, element] of Object.entries(templates)) {
const html = await render(element, { pretty: true });
await writeFile(`${outDir}${name}.html`, html);
console.log(`Rendered out/${name}.html`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove the reported lint violation.

The CI lint job rejects console.log here. Use a lint-compliant status writer, such as process.stdout.write(...), so this known error no longer blocks the check.

Proposed fix
-  console.log(`Rendered out/${name}.html`);
+  process.stdout.write(`Rendered out/${name}.html\n`);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(`Rendered out/${name}.html`);
process.stdout.write(`Rendered out/${name}.html\n`);
🧰 Tools
🪛 GitHub Actions: Lint Code / 0_build.txt

[error] 41-41: ESLint (no-console): Unexpected console statement. Only these console methods are allowed: error

🪛 GitHub Actions: Lint Code / build

[error] 41-41: ESLint (no-console): Unexpected console statement. Only these console methods are allowed: error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@email-templates/render.tsx` at line 41, Replace the console.log call in the
rendering status message with the lint-compliant process.stdout.write API,
preserving the existing “Rendered out/${name}.html” output and newline behavior.

Source: Pipeline failures

"isolatedModules": true,
"noEmit": true
},
"include": ["emails"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
bunx tsc -p email-templates/tsconfig.json --noEmit --listFiles | rg 'email-templates/render\.tsx'

Repository: macintushar/draw

Length of output: 192


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant tsconfig and nearby files.
git ls-files 'email-templates/*' | sed -n '1,120p'
printf '\n--- tsconfig ---\n'
cat -n email-templates/tsconfig.json
printf '\n--- render file ---\n'
cat -n email-templates/render.tsx
printf '\n--- emails dir listing ---\n'
find email-templates/emails -maxdepth 2 -type f | sort | sed -n '1,120p'

Repository: macintushar/draw

Length of output: 2872


Type-check render.tsx too. email-templates/tsconfig.json only includes emails, so email-templates/render.tsx is skipped and its imports/props can drift unnoticed. Add it to the project’s include list.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@email-templates/tsconfig.json` at line 16, Update the include list in
email-templates/tsconfig.json to include both the existing emails directory and
render.tsx, ensuring the render.tsx imports and props are type-checked without
removing the current emails coverage.

Comment thread src/index.css
@layer base {
@font-face {
font-family: "Virgil";
font-family: "Excalifont";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Unquote the font-family name to satisfy stylelint.

Stylelint flags the quoted "Excalifont" value with font-family-name-quotes.

🔧 Suggested fix
-    font-family: "Excalifont";
+    font-family: Excalifont;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
font-family: "Excalifont";
font-family: Excalifont;
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 9-9: Expected no quotes around "Excalifont" (font-family-name-quotes)

(font-family-name-quotes)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.css` at line 9, Update the font-family declaration in the
stylesheet to use the unquoted Excalifont name, preserving the existing font
choice while satisfying the font-family-name-quotes lint rule.

Source: Linters/SAST tools

Comment thread src/views/Layout.tsx
Comment on lines +12 to +49
function ExpandButton() {
const { open, toggleSidebar } = useSidebar();

if (open) return null;

return (
<button
onClick={toggleSidebar}
title="Expand sidebar"
className="group/expand border-sidebar-border fixed top-3 left-3 z-20 flex h-9 w-9 items-center justify-center rounded-lg border bg-white text-lg dark:bg-zinc-900"
>
<span className="group-hover/expand:hidden">📄</span>
<PanelLeft className="hidden h-4 w-4 group-hover/expand:block" />
<span className="sr-only">Expand sidebar</span>
</button>
);
}

function LayoutContent() {
const { open } = useSidebar();

export default function Layout() {
return (
<div className="flex h-full w-full flex-col p-1">
<Navbar />
<div className="h-full w-full p-3 pt-1">
<div className="flex h-full flex-row justify-center gap-8 overflow-clip rounded-xl border-2 border-black bg-gray-200/60 dark:border-white dark:bg-gray-900">
<>
<AppSidebar />
<ExpandButton />
<main className="h-svh min-w-0 flex-1 p-2">
<div
className={cn(
"scrollbar-thumb-accent flex h-full flex-row justify-center gap-8 overflow-y-auto rounded-xl border-2 border-black bg-white dark:border-white dark:bg-zinc-900",
!open && "pl-12",
)}
>
<Outlet />
</div>
</div>
</div>
</main>
</>
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find any sidebar trigger / toggleSidebar usage rendered outside AppSidebar
rg -nP '\btoggleSidebar\b|SidebarTrigger|openMobile|setOpenMobile' src --type=ts --type=tsx -C2

Repository: macintushar/draw

Length of output: 182


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files 'src/**' | rg 'Layout\.tsx|sidebar|Sidebar|sheet|trigger'

Repository: macintushar/draw

Length of output: 263


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## src/components/AppSidebar.tsx\n'
ast-grep outline src/components/AppSidebar.tsx --view expanded || true
printf '\n## src/components/ui/sidebar.tsx\n'
ast-grep outline src/components/ui/sidebar.tsx --view expanded || true
printf '\n## src/views/Layout.tsx\n'
ast-grep outline src/views/Layout.tsx --view expanded || true

printf '\n## Sidebar-trigger related lines in AppSidebar.tsx\n'
rg -n "SidebarTrigger|toggleSidebar|openMobile|setOpenMobile|Sheet|ProfileFooter|header" src/components/AppSidebar.tsx src/components/ui/sidebar.tsx src/views/Layout.tsx -C 3

Repository: macintushar/draw

Length of output: 12922


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## SidebarTrigger usages\n'
rg -n "\bSidebarTrigger\b" src --type-add 'ts:*.ts' --type-add 'tsx:*.tsx' -g '!src/components/ui/sidebar.tsx' -C 3 || true

printf '\n## AppSidebar excerpt\n'
sed -n '227,288p' src/components/AppSidebar.tsx

printf '\n## Layout excerpt\n'
sed -n '1,80p' src/views/Layout.tsx

Repository: macintushar/draw

Length of output: 3659


Add a mobile sidebar trigger
ExpandButton only reflects the desktop open state, and the only toggleSidebar controls live inside AppSidebar, which is hidden behind the mobile sheet when openMobile is false. Add a trigger outside the sheet so the sidebar can be opened on small screens.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/views/Layout.tsx` around lines 12 - 49, The LayoutContent mobile flow
lacks an external sidebar trigger when the mobile sheet is closed. Update
ExpandButton or add a nearby trigger using the sidebar context’s mobile open
state and toggle action, render it outside AppSidebar so it remains visible on
small screens, and preserve the existing desktop collapsed-sidebar behavior.

Comment thread src/views/Login.tsx
Comment on lines +53 to +58

if (data.error.message.toLowerCase().includes("email not confirmed")) {
navigate({ to: "/verify-email", search: { email: values.email } });
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and nearby auth handling
printf '\n== src/views/Login.tsx outline ==\n'
ast-grep outline src/views/Login.tsx --view expanded || true

printf '\n== src/views/Login.tsx lines 1-220 ==\n'
cat -n src/views/Login.tsx | sed -n '1,220p'

printf '\n== Search for email_not_confirmed and error.code usage ==\n'
rg -n --hidden -S 'email_not_confirmed|error\.code|email not confirmed' src . || true

Repository: macintushar/draw

Length of output: 5963


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether the repo references the structured auth error code anywhere else.
rg -n --hidden -S 'email_not_confirmed|EMAIL_NOT_CONFIRMED|not confirmed' .

Repository: macintushar/draw

Length of output: 257


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== auth-related files ==\n'
fd -a 'auth.*' src

printf '\n== src/db/auth.* outline or contents ==\n'
for f in src/db/auth.ts src/db/auth.tsx src/db/auth.js src/db/auth.jsx; do
  if [ -f "$f" ]; then
    echo "--- $f"
    ast-grep outline "$f" --view expanded || true
    echo
    cat -n "$f" | sed -n '1,260p'
  fi
done

printf '\n== package.json relevant deps ==\n'
cat -n package.json | sed -n '1,220p'

printf '\n== search for supabase imports/usages ==\n'
rg -n --hidden -S 'supabase|createClient|signInWithPassword|AuthError|error\.code|email_confirm' src package.json .

Repository: macintushar/draw

Length of output: 16258


🌐 Web query:

Supabase AuthError code email_not_confirmed @supabase/supabase-js 2.110.0 documentation

💡 Result:

In the @supabase/supabase-js SDK (which utilizes @supabase/auth-js), the email_not_confirmed error code is a standard identifier returned by the Supabase Auth API when a user attempts to sign in with an email address that has not yet been verified [1][2]. Key Technical Details: - SDK Representation: Errors originating from the Supabase Auth API are wrapped in the AuthError class [1][3]. You should handle this by checking the error.code property rather than parsing error messages [4][3]. - Meaning: The email_not_confirmed code explicitly indicates that the user's email address is unverified, and as a result, sign-in is blocked [1][5]. - Implementation Guidance: Supabase recommends using error.code and error.name for robust error handling [3]. When using TypeScript, AuthError exposes the code property (typically typed as ErrorCode or string) [6]. - Known Behaviors: There have been reports that this error can persist even when email confirmation settings are disabled, particularly for users created via the Admin API, as the email_confirmed_at timestamp may remain NULL [7]. Example Pattern: const { data, error } = await supabase.auth.signInWithPassword({ email: 'example@email.com', password: 'example-password', }) if (error) { if (error.code === 'email_not_confirmed') { // Handle unconfirmed email (e.g., prompt user to check inbox) } console.error(error) }

Citations:


Prefer error.code here instead of matching the message text.

supabase-js returns email_not_confirmed for this case, so checking data.error.code === "email_not_confirmed" is more robust than relying on message.toLowerCase().includes(...).

♻️ Proposed fix
-      if (data.error.message.toLowerCase().includes("email not confirmed")) {
+      if (data.error.code === "email_not_confirmed") {
         navigate({ to: "/verify-email", search: { email: values.email } });
         return;
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (data.error.message.toLowerCase().includes("email not confirmed")) {
navigate({ to: "/verify-email", search: { email: values.email } });
return;
}
if (data.error.code === "email_not_confirmed") {
navigate({ to: "/verify-email", search: { email: values.email } });
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/views/Login.tsx` around lines 53 - 58, Update the error check in the
Login submit flow to use data.error.code === "email_not_confirmed" instead of
matching data.error.message text, while preserving the existing navigation to
/verify-email with the submitted email and the early return.

Comment thread src/views/Pages.tsx
Comment on lines 132 to 136
<Trash2
className="invisible h-4 w-4 cursor-pointer rounded-lg text-gray-600 transition-all hover:bg-gray-100 hover:text-red-500 group-hover:visible hover:dark:bg-gray-900"
strokeWidth={3}
className="mt-0.5 h-4 w-4 flex-shrink-0 cursor-pointer text-gray-400 opacity-0 transition-all hover:text-red-500 group-hover:opacity-100"
strokeWidth={2.5}
onClick={() => handlePageDelete(page.page_id)}
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Delete control is not keyboard accessible.

The Trash2 icon is a bare SVG with an onClick; it cannot be focused or activated via keyboard and exposes no button role/label. Wrap it in a <button> with an accessible label.

♿ Proposed fix
-                <Trash2
-                  className="mt-0.5 h-4 w-4 flex-shrink-0 cursor-pointer text-gray-400 opacity-0 transition-all hover:text-red-500 group-hover:opacity-100"
-                  strokeWidth={2.5}
-                  onClick={() => handlePageDelete(page.page_id)}
-                />
+                <button
+                  type="button"
+                  aria-label={`Delete ${page.name || "Untitled"}`}
+                  className="mt-0.5 flex-shrink-0 text-gray-400 opacity-0 transition-all hover:text-red-500 focus-visible:opacity-100 group-hover:opacity-100"
+                  onClick={() => handlePageDelete(page.page_id)}
+                >
+                  <Trash2 className="h-4 w-4" strokeWidth={2.5} />
+                </button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Trash2
className="invisible h-4 w-4 cursor-pointer rounded-lg text-gray-600 transition-all hover:bg-gray-100 hover:text-red-500 group-hover:visible hover:dark:bg-gray-900"
strokeWidth={3}
className="mt-0.5 h-4 w-4 flex-shrink-0 cursor-pointer text-gray-400 opacity-0 transition-all hover:text-red-500 group-hover:opacity-100"
strokeWidth={2.5}
onClick={() => handlePageDelete(page.page_id)}
/>
<button
type="button"
aria-label={`Delete ${page.name || "Untitled"}`}
className="mt-0.5 flex-shrink-0 text-gray-400 opacity-0 transition-all hover:text-red-500 focus-visible:opacity-100 group-hover:opacity-100"
onClick={() => handlePageDelete(page.page_id)}
>
<Trash2 className="h-4 w-4" strokeWidth={2.5} />
</button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/views/Pages.tsx` around lines 132 - 136, Update the delete control in
Pages around Trash2 by wrapping the icon in a focusable button with an
accessible label describing page deletion. Move the click handler to the button,
preserve the existing styling and handlePageDelete(page.page_id) behavior, and
ensure the icon remains decorative within the labeled button.

Comment thread src/views/VerifyEmail.tsx
Comment on lines +38 to +40
const [email, setEmail] = useState<string | undefined>(emailParam);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [cooldown, setCooldown] = useState<number>(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Seed cooldown when arriving with a pre-filled email.

cooldown starts at 0 even when emailParam is already set (i.e., right after signup already triggered the initial email), letting the user immediately click "Resend" and likely hit Supabase's resend rate limit.

🐛 Proposed fix
-  const [cooldown, setCooldown] = useState<number>(0);
+  const [cooldown, setCooldown] = useState<number>(
+    emailParam ? RESEND_COOLDOWN_SECONDS : 0,
+  );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [email, setEmail] = useState<string | undefined>(emailParam);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [cooldown, setCooldown] = useState<number>(0);
const [email, setEmail] = useState<string | undefined>(emailParam);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [cooldown, setCooldown] = useState<number>(
emailParam ? RESEND_COOLDOWN_SECONDS : 0,
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/views/VerifyEmail.tsx` around lines 38 - 40, Initialize the cooldown
state in VerifyEmail using the existing emailParam: when a pre-filled email is
present, seed it with the intended resend cooldown; otherwise retain the zero
cooldown for users without an email parameter. Keep the existing email, loading,
and cooldown behavior unchanged after initialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant