Skip to content

Refactor UI components, enhance markdown support, and improve accessibility#59

Open
aditya-2k23 wants to merge 18 commits intomainfrom
refactor/design
Open

Refactor UI components, enhance markdown support, and improve accessibility#59
aditya-2k23 wants to merge 18 commits intomainfrom
refactor/design

Conversation

@aditya-2k23
Copy link
Copy Markdown
Owner

@aditya-2k23 aditya-2k23 commented May 2, 2026

This pull request introduces a centralized design token system for theming, improves dark mode and accessibility, optimizes Firestore deletion, and updates several UI components for consistency and maintainability. The most important changes are summarized below.


Design System & Theming

  • Introduced a new app/design-tokens.css file containing all design tokens (colors, fonts, spacing, shadows, etc.) for consistent theming, and refactored app/globals.css to import and use these tokens. Removed legacy CSS variables from globals.css and replaced them with references to the new design tokens. Also added dark mode overrides and semantic tokens for better maintainability and theme switching. [1] [2] [3]
  • Updated component styles to use the new design tokens, including improved shadows, glows, and color handling for elements like .purpleShadow, .glow, .blob-btn, and markdown content. Added .smooth-transition utility and improved focus and contrast for .journal-textarea. [1] [2] [3] [4] [5] [6] [7] [8]

Firestore Deletion Optimization

  • Refactored the recursive deleteCollectionTree function in app/api/delete-account/route.js to use Firestore batch deletes (500 per batch) for improved performance and reliability, passing the db instance explicitly. [1] [2]

UI/UX & Accessibility Improvements

  • Improved cursor styles and accessibility for interactive elements, ensuring correct pointer, text, and disabled cursors across more input types and roles. [1] [2]
  • Enhanced contrast and color handling for markdown previews and message bubbles, especially in dark mode.
  • Updated the homepage background overlay for better opacity handling in light/dark modes.
  • Improved the footer and section headers for better theme consistency and clarity. [1] [2]

Component & Dependency Updates

  • Replaced the default react-hot-toast Toaster with a custom CustomToaster component for more controlled notifications. [1] [2]

Documentation Updates

  • Cleaned up project guidelines in AGENT.md, removing redundant or outdated restrictions and clarifying best practices. [1] [2]

Summary by CodeRabbit

  • New Features

    • Rich text editing support in journal entries with formatting toolbar
    • Markdown rendering in chat messages with improved link handling
    • Animated toast notifications with success/error states
  • Improvements

    • Enhanced dark mode theming with new design token system
    • Improved login UI with better password visibility toggle
    • Better input field styling with enhanced focus states
    • Refined splash screen and loading experience
    • Updated journal modal UI with improved mood selection layout
  • Bug Fixes

    • Account deletion now uses optimized batch processing for reliability

aditya-2k23 and others added 18 commits April 30, 2026 19:23
…les across various components

Co-authored-by: Copilot <copilot@github.com>
…onment variables and improve SSR compatibility
…s.css, CustomToaster, Journal, and MessageBubble
…bal styles to utilize them

Co-authored-by: Copilot <copilot@github.com>
Updated background opacity settings in the main page and added a decorative grid background with mask effects to the Login component. These changes include specific dark mode adjustments to maintain visual consistency.
Updated the Content Security Policy in next.config.mjs to include loom.com in connect-src and frame-src directives to support Loom video integrations.
Refine custom cursor logic in globals.css for better UX feedback. Update Input component focus shadow intensity and color. Migrate Login component from FontAwesome to Lucide icons and adjust styling.
Add ARIA labels and focus-visible styles to improve accessibility. Update save validation in JournalModal to correctly handle clearing existing entries. Implement GSAP SplitText cleanup in the splash screen to prevent element duplication. Refine the cloud sync status icon and chat header release tag logic.
@aditya-2k23 aditya-2k23 requested a review from Copilot May 2, 2026 13:12
@vercel
Copy link
Copy Markdown

vercel Bot commented May 2, 2026

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

Project Deployment Actions Updated (UTC)
moody Ready Ready Preview, Comment, Open in v0 May 2, 2026 1:12pm

@netlify
Copy link
Copy Markdown

netlify Bot commented May 2, 2026

Deploy Preview for moody-adi ready!

Name Link
🔨 Latest commit 60d6b87
🔍 Latest deploy log https://app.netlify.com/projects/moody-adi/deploys/69f5f8561b21af00082e291d
😎 Deploy Preview https://deploy-preview-59--moody-adi.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 2, 2026

📝 Walkthrough

Walkthrough

This PR introduces a rich text editing system (TipTap-based RichTextEditor with styling toolbar), centralizes design tokens in CSS custom properties with dark mode support, refactors Firebase initialization for SSR builds, optimizes Firestore account deletion with batching, adds a custom animated toast component, and refines UI styling across the app using the new token system.

Changes

Rich Text Editing & Design System Overhaul

Layer / File(s) Summary
Dependencies & Configuration
package.json, tailwind.config.js, next.config.mjs, lib/release.js
Added TipTap, typography, react-markdown, and markdown packages. Enabled Tailwind typography plugin. Updated CSP to allow Loom frames. Refined version label to conditionally include release tag. Bumped version to 3.1.0.
Design Tokens Foundation
app/design-tokens.css
Introduced centralized CSS custom properties for typography, spacing, radii, motion, light/dark theme colors, shadows, glows, and backwards-compatible aliases. Dark mode overrides provided via .dark class selector.
Rich Text Editor Components
components/RichTextEditor.js, components/StyleTools.js
Implemented TipTap-based editor with markdown support, auto-sync of parent value, and disabled-state handling. Created formatting toolbar with bold, italic, heading, list, and quote toggles.
Custom Toast Component
components/CustomToaster.js
Built animated toast renderer using GSAP with entry/exit animations, conditional success/error styling, and dismiss functionality.
Journal & Modal Integration
components/Journal.js, components/JournalModal.js
Replaced textarea with RichTextEditor in both components. Updated auto-save logic to handle whitespace-only entries differently. Integrated StyleTools for format control. Adjusted JournalModal save validation to allow clearing existing entries.
Global Layout & Toast Wiring
app/layout.js
Replaced default Toaster with custom CustomToaster in root layout.
Core UI Styling Refinements
app/globals.css
Imported design tokens, added .smooth-transition utility. Updated .purpleShadow, .glow, .theme-reveal-* to use token variables. Rewrote .journal-textarea styling with explicit light/dark backgrounds and focus states. Updated .blob-btn hierarchy and dark mode overrides. Expanded cursor rules with URL-based sizing. Updated .circular-gallery and markdown content styling.
Component Styling Updates
components/Input.js, components/GlowBackground.js, components/ThemeToggle.js, components/AIInsightsSection.js, components/ConditionalFooter.js
Input now uses ring-based styling. GlowBackground changed to opacity-65 and uses color-mix with token variables. ThemeToggle uses CSS variables for icon colors. AIInsightsSection title toggles based on insights presence. ConditionalFooter updated footer link colors.
Dashboard & Splash Screen
components/Dashboard.js, components/Splashscreen.js
Dashboard enforces 2.5s minimum splash duration via minTimeMet gate. Splashscreen refactored from emoji to useGSAP with SplitText logo animation, responsive icon layout, and dynamic glow orbs from glowVariants.
Firebase Infrastructure
firebase.js, app/api/delete-account/route.js
Added getFirebaseApp() helper to handle SSR builds and app reuse. Refactored account deletion to use batched (500-per-batch) recursive subcollection deletion instead of immediate per-document deletes.
Landing & Authentication Pages
app/page.js, components/Login.js, components/landing/HeroSection.js, components/landing/LandingFooter.js, components/landing/LumiDemoSection.js
Updated background overlay opacity. Login refactored to use Lucide eye icons and masked gradient overlay. Version badge text changed from "chatbot" to "public release". Dashboard CTA adds dark prop. Footer badge styling refined. Lumi demo CTA now includes ArrowRight icon.
Chat & Miscellaneous Updates
components/chat/MessageBubble.js, components/chat/MessageList.js, components/chat/ChatHeader.js, context/authContext.js
MessageBubble renders markdown instead of plain text with external link handling. MessageList spacing reduced from space-y-4 to space-y-3. ChatHeader hides empty release tag and shows dot instead. Auth context logging changed from console.log to console.error.
Configuration & Maintenance
.gitignore, AGENT.md
Added .gitgenie/** to gitignore. Removed two restrictive AGENT.md bullets (Redux/Zustand and large-file rewrite prohibitions).

Sequence Diagram

sequenceDiagram
    participant User
    participant Journal
    participant RichTextEditor
    participant StyleTools
    participant Dashboard
    participant Firebase as Firebase<br/>(getFirebaseApp)
    participant Firestore
    
    User->>Journal: Opens journal
    Journal->>RichTextEditor: Initialize editor with value
    RichTextEditor->>RichTextEditor: useEditor with TipTap config
    Note over RichTextEditor: Sync parent value via useEffect<br/>Subscribe to onUpdate for markdown
    
    User->>RichTextEditor: Types/edits content
    RichTextEditor->>Journal: onChange emits markdown
    Journal->>Journal: Update entry state, mark unsaved
    Journal->>Dashboard: Trigger auto-save debounce
    
    User->>StyleTools: Click format button (bold/italic/heading)
    StyleTools->>RichTextEditor: Execute editor.chain command
    RichTextEditor->>RichTextEditor: Apply formatting, emit updated markdown
    RichTextEditor->>Journal: onChange with formatted content
    
    Journal->>Dashboard: Auto-save timer fires
    Dashboard->>Firebase: Call saveJournalText(currentUser, entry)
    Firebase->>Firestore: Write document
    Firestore-->>Firebase: Success
    Firebase-->>Dashboard: Return
    Dashboard->>Journal: Update cloudSaved state
    Journal->>User: Show CloudCheck icon
    
    User->>Dashboard: Delete account
    Dashboard->>Firebase: Trigger deleteUserFirestoreData
    Firebase->>getFirebaseApp: Get app instance (reuse or initialize)
    getFirebaseApp-->>Firebase: Return app
    Firebase->>Firestore: deleteCollectionTree(collectionRef, db)
    loop For each batch (500 docs)
        Firestore->>Firestore: Fetch page of documents
        Firestore->>Firestore: batch.delete() each doc
        Firestore->>Firestore: Recursively delete nested collections
        Firestore->>Firestore: batch.commit()
    end
    Firestore-->>Firebase: All collections deleted
    Firebase-->>Dashboard: Account cleanup complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

feature, ui, editor, design-system, firebase

Poem

🐰 With TipTap's brush and tokens bright,
The journal flows in prose and light,
Design tokens reign, dark mode gleams,
While Firebase batches fulfill dreams.
From splash to toast, the UI sings—
A rabbit celebrates all these things! 🎨✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% 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
Title check ✅ Passed The title accurately summarizes the main changes: refactored UI components, enhanced markdown support, and improved accessibility are all prominent themes in this PR.
Description check ✅ Passed The description covers all required template sections: motivation/context provided, type of change clearly stated (refactoring/UI improvements/features), key changes listed, and version bump confirmed in package.json.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 refactor/design

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (7)
components/Input.js (1)

4-4: ⚡ Quick win

Use focus-visible + scoped transitions to keep interactions calmer and cheaper

transition-all + always-on focus: glow can feel busy and animates more properties than needed. Prefer focus-visible: and a scoped transition (transition-shadow / transition-colors) for better accessibility/perf balance.

Suggested minimal tweak
-      className="dark:bg-slate-900/50 dark:text-slate-200 w-full max-w-[400px] mx-auto px-4 py-2 sm:py-3 rounded-full outline-none duration-200 transition-all ring-1 ring-indigo-500/70 dark:ring-indigo-500/30 focus:ring-indigo-500 focus:shadow-[0_0_20px_rgba(99,102,241,0.4)] dark:focus:shadow-[0_0_24px_rgba(99,102,241,0.4)] placeholder:font-sans"
+      className="dark:bg-slate-900/50 dark:text-slate-200 w-full max-w-[400px] mx-auto px-4 py-2 sm:py-3 rounded-full outline-none duration-200 transition-shadow transition-colors ring-1 ring-indigo-500/70 dark:ring-indigo-500/30 focus-visible:ring-indigo-500 focus-visible:shadow-[0_0_16px_rgba(99,102,241,0.35)] dark:focus-visible:shadow-[0_0_20px_rgba(99,102,241,0.35)] placeholder:font-sans"

As per coding guidelines: “Maintain a calm, minimal, non-distracting UI design philosophy; avoid loud colors, aggressive animations” and “Ensure all UI components prioritize mobile usability, accessibility, and performance.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Input.js` at line 4, Update the input element's className in the
Input component to replace the broad "transition-all" and always-on "focus:"
variants with scoped transitions and focus-visible selectors: remove
"transition-all" and use "transition-shadow" and/or "transition-colors", and
change "focus:" prefixed utilities (e.g., "focus:ring-indigo-500"
"focus:shadow-[...]") to "focus-visible:" equivalents so only keyboard focus
triggers the glow; keep existing dark-mode classes and ring/shadow values but
switch their prefixes to "focus-visible" and ensure the placeholder and sizing
classes remain unchanged.
components/Journal.js (1)

229-232: 💤 Low value

Whitespace-only entries can now be saved.

The condition changed from !entry.trim() to !entry, which means whitespace-only entries (e.g., " ") are now truthy and will trigger auto-save. This appears intentional per the summary, but verify this is the desired behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Journal.js` around lines 229 - 232, The current check uses if
(!entry && !lastSavedEntryRef.current) which treats whitespace-only strings as
non-empty and allows auto-save; change the condition to explicitly reject
whitespace-only content by using entry.trim() (e.g., if (!entry ||
!entry.trim()) or if (!entry.trim() && !lastSavedEntryRef.current) depending on
intended logic) so whitespace-only entries are not saved—update the condition
that references entry, lastSavedEntryRef.current and setCloudStatus accordingly
(or add an explicit trim-based guard earlier) to prevent saving
blank/whitespace-only notes.
components/RichTextEditor.js (1)

66-76: 💤 Low value

Voice input may not display while editor is focused.

The isFocused guard prevents content sync when the user is actively in the editor. If a user speaks while their cursor is in the editor, the voice transcript won't appear until they click away. This is likely an acceptable tradeoff to prevent cursor jumping during typing, but worth documenting or testing the voice-while-focused flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/RichTextEditor.js` around lines 66 - 76, The current focus guard
(checking editor.isFocused before calling editor.commands.setContent) blocks
voice-transcript updates when the cursor is focused; add a clear bypass for
external updates by introducing and checking an explicit flag (e.g.,
isVoiceInput or externalUpdate) alongside value/currentMarkdown: when
isVoiceInput is true, call editor.commands.setContent(value, false, {
preserveWhitespace: "full" }) regardless of editor.isFocused so voice input
appears immediately; update the component prop/state that supplies this flag and
ensure the logic around value/currentMarkdown, editor.isFocused, and
editor.commands.setContent references that flag to allow external updates while
preserving the existing focus-protection for manual typing.
app/globals.css (3)

592-603: 💤 Low value

Hardcoded colors could reference design tokens.

The journal-textarea uses hardcoded hex values (#0f1724, #071025, #e6eef8) that match the design tokens (--color-text, --color-bg). For consistency with the token-based theming approach, consider referencing the tokens directly.

♻️ Suggested refactor
 .journal-textarea {
-  background: var(--color-white);
-  color: `#0f1724`;
+  background: var(--color-bg);
+  color: var(--color-text);
   box-shadow: 0 6px 18px rgba(99, 102, 241, 0.06);
 }

 .dark .journal-textarea {
-  background: `#071025`;
-  /* richer dark background */
-  color: `#e6eef8`;
+  background: var(--color-bg);
+  color: var(--color-text);
   box-shadow: 0 6px 22px rgba(2, 6, 23, 0.6);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/globals.css` around lines 592 - 603, Replace the hardcoded hex colors in
the .journal-textarea and .dark .journal-textarea rules with the existing CSS
design tokens (e.g., --color-text, --color-bg, --color-white or whichever tokens
map to those values) so the styles use token references instead of literal
values; update the color, background and box-shadow color stops to reference
tokens (and, if necessary, use rgba() with var(--token) or CSS color-mix/opacity
tokens) inside the .journal-textarea and .dark .journal-textarea selectors to
ensure theming consistency.

681-696: 💤 Low value

Markdown content colors could also use design tokens.

Similar to the journal-textarea, these hardcoded values duplicate what's defined in design-tokens.css.

♻️ Suggested refactor
 .markdown-content p {
-  color: `#0f1724`;
+  color: var(--color-text);
 }

 .dark .markdown-content p {
-  color: `#e6eef8`;
+  color: var(--color-text);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/globals.css` around lines 681 - 696, Replace the hardcoded color values
in the selectors .markdown-content p, .dark .markdown-content p,
.markdown-content a, and .dark .markdown-content a with the corresponding design
token CSS custom properties from design-tokens.css (e.g., text and link color
tokens) so the rules reuse the central tokens instead of duplicating hex values;
update the four selectors to reference the appropriate --token names used
elsewhere (matching the journal-textarea usage) and remove the literal hex
codes.

59-65: 💤 Low value

Consider using design tokens for transition values.

The .smooth-transition utility hardcodes 220ms and the cubic-bezier easing, but these values are already defined as --duration-base and --ease-default in design-tokens.css.

♻️ Suggested refactor
 .smooth-transition {
   transition:
-    background-color 220ms cubic-bezier(0.2, 0.9, 0.2, 1),
-    color 220ms cubic-bezier(0.2, 0.9, 0.2, 1),
-    box-shadow 220ms cubic-bezier(0.2, 0.9, 0.2, 1),
-    transform 220ms cubic-bezier(0.2, 0.9, 0.2, 1);
+    background-color var(--duration-base) var(--ease-default),
+    color var(--duration-base) var(--ease-default),
+    box-shadow var(--duration-base) var(--ease-default),
+    transform var(--duration-base) var(--ease-default);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/globals.css` around lines 59 - 65, Replace the hardcoded timing and
easing in the .smooth-transition rule with the design token variables: use
var(--duration-base) in place of 220ms and var(--ease-default) in place of the
cubic-bezier; update each property declaration (background-color, color,
box-shadow, transform) to use var(--duration-base) and var(--ease-default) and
optionally include fallbacks like var(--duration-base, 220ms) and
var(--ease-default, cubic-bezier(0.2,0.9,0.2,1)) to preserve behavior if tokens
are missing.
components/CustomToaster.js (1)

13-24: ⚡ Quick win

Kill in-flight GSAP tweens before starting a new one.

If t.visible flips to false while the 0.5 s entry animation is still running, a second gsap.to starts on the same element and both tweens fight each other — the exit animation loses. Calling gsap.killTweensOf(elRef.current) at the top of the effect (and returning it as cleanup) prevents this.

♻️ Proposed fix
  useEffect(() => {
+   if (!elRef.current) return;
+   gsap.killTweensOf(elRef.current);
    if (t.visible) {
      gsap.fromTo(elRef.current,
        { y: -30, opacity: 0, scale: 0.9 },
        { y: 0, opacity: 1, scale: 1, duration: 0.5, ease: "back.out(1.7)" }
      );
    } else {
      gsap.to(elRef.current, {
        y: -20, opacity: 0, scale: 0.9, duration: 0.3, ease: "power2.in"
      });
    }
+   return () => { gsap.killTweensOf(elRef.current); };
  }, [t.visible]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/CustomToaster.js` around lines 13 - 24, The useEffect that
animates elRef based on t.visible should kill any in-flight GSAP tweens before
starting a new tween and also clean them up on unmount: at the top of the
useEffect call gsap.killTweensOf(elRef.current) before invoking gsap.fromTo or
gsap.to, and return a cleanup function that calls
gsap.killTweensOf(elRef.current) to ensure no competing animations remain;
update the effect that references useEffect, elRef, t.visible, and the
gsap.fromTo/gsap.to calls accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/AIInsightsSection.js`:
- Around line 126-127: Update the header conditional so it shows the generation
state when insights are being created: use the existing isLoading flag together
with insights to decide the text (e.g., if isLoading OR insights is non-empty
show the "Lumi's Thoughts"/generation heading, otherwise show "Let's talk about
your day"). Modify the JSX that renders Sparkles and the header (the line
currently using insights ? "Lumi's Thoughts" : "Let's talk about your day") to
check isLoading as well, so when InsightSkeletonLeft is active during first-time
generation the header matches the loading state.

In `@components/chat/ChatHeader.js`:
- Around line 59-61: The outer span in ChatHeader.js currently applies "hidden"
when APP_RELEASE_TAG is empty, which prevents the inner dot from ever showing;
make the behavior consistent by removing the conditional "hidden" from that
span's className and instead conditionally render the inner content based on
APP_RELEASE_TAG: keep the outer <span> (the badge span in the ChatHeader
component) always visible (no "hidden" class), and change the inner ternary so
it renders the APP_RELEASE_TAG when present and a visible dot element when
APP_RELEASE_TAG.length === 0; alternatively, if you prefer to hide the entire
badge when no tag exists, simplify the inner ternary to only render
APP_RELEASE_TAG and keep the outer className conditional — pick one of these two
consistent approaches and apply it to the badge span in ChatHeader.js.

In `@components/ConditionalFooter.js`:
- Line 20: The decorative star span (<span className="text-indigo-500
dark:text-indigo-400">✦</span>) is announced by screen readers; update that span
in ConditionalFooter.js to include aria-hidden="true" so the symbol is ignored
by assistive technologies while preserving visual appearance.

In `@components/CustomToaster.js`:
- Around line 62-70: The divider between the toast message and dismiss button is
missing because the wrapper div with className "flex border-slate-200
dark:border-slate-700 h-full" only sets border colors but not a border side;
update the wrapper div in CustomToaster.js (the div that contains the dismiss
<button> and X icon) to include a border side utility such as "border-l" (or
"border" if you want all sides) so the color classes take effect and the
separator renders in both light and dark themes.

---

Nitpick comments:
In `@app/globals.css`:
- Around line 592-603: Replace the hardcoded hex colors in the .journal-textarea
and .dark .journal-textarea rules with the existing CSS design tokens (e.g.,
--color-text, --color-bg, --color-white or whichever tokens map to those values)
so the styles use token references instead of literal values; update the color,
background and box-shadow color stops to reference tokens (and, if necessary,
use rgba() with var(--token) or CSS color-mix/opacity tokens) inside the
.journal-textarea and .dark .journal-textarea selectors to ensure theming
consistency.
- Around line 681-696: Replace the hardcoded color values in the selectors
.markdown-content p, .dark .markdown-content p, .markdown-content a, and .dark
.markdown-content a with the corresponding design token CSS custom properties
from design-tokens.css (e.g., text and link color tokens) so the rules reuse the
central tokens instead of duplicating hex values; update the four selectors to
reference the appropriate --token names used elsewhere (matching the
journal-textarea usage) and remove the literal hex codes.
- Around line 59-65: Replace the hardcoded timing and easing in the
.smooth-transition rule with the design token variables: use
var(--duration-base) in place of 220ms and var(--ease-default) in place of the
cubic-bezier; update each property declaration (background-color, color,
box-shadow, transform) to use var(--duration-base) and var(--ease-default) and
optionally include fallbacks like var(--duration-base, 220ms) and
var(--ease-default, cubic-bezier(0.2,0.9,0.2,1)) to preserve behavior if tokens
are missing.

In `@components/CustomToaster.js`:
- Around line 13-24: The useEffect that animates elRef based on t.visible should
kill any in-flight GSAP tweens before starting a new tween and also clean them
up on unmount: at the top of the useEffect call gsap.killTweensOf(elRef.current)
before invoking gsap.fromTo or gsap.to, and return a cleanup function that calls
gsap.killTweensOf(elRef.current) to ensure no competing animations remain;
update the effect that references useEffect, elRef, t.visible, and the
gsap.fromTo/gsap.to calls accordingly.

In `@components/Input.js`:
- Line 4: Update the input element's className in the Input component to replace
the broad "transition-all" and always-on "focus:" variants with scoped
transitions and focus-visible selectors: remove "transition-all" and use
"transition-shadow" and/or "transition-colors", and change "focus:" prefixed
utilities (e.g., "focus:ring-indigo-500" "focus:shadow-[...]") to
"focus-visible:" equivalents so only keyboard focus triggers the glow; keep
existing dark-mode classes and ring/shadow values but switch their prefixes to
"focus-visible" and ensure the placeholder and sizing classes remain unchanged.

In `@components/Journal.js`:
- Around line 229-232: The current check uses if (!entry &&
!lastSavedEntryRef.current) which treats whitespace-only strings as non-empty
and allows auto-save; change the condition to explicitly reject whitespace-only
content by using entry.trim() (e.g., if (!entry || !entry.trim()) or if
(!entry.trim() && !lastSavedEntryRef.current) depending on intended logic) so
whitespace-only entries are not saved—update the condition that references
entry, lastSavedEntryRef.current and setCloudStatus accordingly (or add an
explicit trim-based guard earlier) to prevent saving blank/whitespace-only
notes.

In `@components/RichTextEditor.js`:
- Around line 66-76: The current focus guard (checking editor.isFocused before
calling editor.commands.setContent) blocks voice-transcript updates when the
cursor is focused; add a clear bypass for external updates by introducing and
checking an explicit flag (e.g., isVoiceInput or externalUpdate) alongside
value/currentMarkdown: when isVoiceInput is true, call
editor.commands.setContent(value, false, { preserveWhitespace: "full" })
regardless of editor.isFocused so voice input appears immediately; update the
component prop/state that supplies this flag and ensure the logic around
value/currentMarkdown, editor.isFocused, and editor.commands.setContent
references that flag to allow external updates while preserving the existing
focus-protection for manual typing.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: c14a9520-bbd8-47ae-a694-62207ad15af5

📥 Commits

Reviewing files that changed from the base of the PR and between 809ffac and 60d6b87.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (32)
  • .gitignore
  • AGENT.md
  • app/api/delete-account/route.js
  • app/design-tokens.css
  • app/globals.css
  • app/layout.js
  • app/page.js
  • components/AIInsightsSection.js
  • components/ConditionalFooter.js
  • components/CustomToaster.js
  • components/Dashboard.js
  • components/GlowBackground.js
  • components/Input.js
  • components/Journal.js
  • components/JournalModal.js
  • components/Login.js
  • components/RichTextEditor.js
  • components/Splashscreen.js
  • components/StyleTools.js
  • components/ThemeToggle.js
  • components/chat/ChatHeader.js
  • components/chat/MessageBubble.js
  • components/chat/MessageList.js
  • components/landing/HeroSection.js
  • components/landing/LandingFooter.js
  • components/landing/LumiDemoSection.js
  • context/authContext.js
  • firebase.js
  • lib/release.js
  • next.config.mjs
  • package.json
  • tailwind.config.js
💤 Files with no reviewable changes (1)
  • AGENT.md

Comment on lines +126 to +127
<Sparkles size={25} />
{insights ? "Lumi's Thoughts" : "Let's talk about your day"}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Header text is misleading during initial insight generation.

When isLoading is true but insights is still "" (the first-time generation case — see Journal.js line 44 where insights initialises as ""), the header renders "Let's talk about your day" while InsightSkeletonLeft is visually active below it. The skeleton implies generation-in-progress, but the heading implies the idle chat-only state, creating a brief semantic mismatch.

🐛 Proposed fix
-        {insights ? "Lumi's Thoughts" : "Let's talk about your day"}
+        {insights || isLoading ? "Lumi's Thoughts" : "Let's talk about your day"}

This makes the heading consistent with the loading skeleton on first generation while preserving the existing behaviour for every other state.

📝 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
<Sparkles size={25} />
{insights ? "Lumi's Thoughts" : "Let's talk about your day"}
<Sparkles size={25} />
{insights || isLoading ? "Lumi's Thoughts" : "Let's talk about your day"}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/AIInsightsSection.js` around lines 126 - 127, Update the header
conditional so it shows the generation state when insights are being created:
use the existing isLoading flag together with insights to decide the text (e.g.,
if isLoading OR insights is non-empty show the "Lumi's Thoughts"/generation
heading, otherwise show "Let's talk about your day"). Modify the JSX that
renders Sparkles and the header (the line currently using insights ? "Lumi's
Thoughts" : "Let's talk about your day") to check isLoading as well, so when
InsightSkeletonLeft is active during first-time generation the header matches
the loading state.

Comment on lines +59 to 61
<span className={`text-[9px] uppercase tracking-wide font-bold px-1.5 py-0.5 rounded-md ${APP_RELEASE_TAG.length > 0 ? "" : "hidden"} bg-indigo-100 text-indigo-700 dark:bg-indigo-900/40 dark:text-indigo-300`}>
{APP_RELEASE_TAG.length === 0 ? <span className="w-1 h-1 rounded-full bg-indigo-500 dark:bg-slate-600" /> : APP_RELEASE_TAG}
</span>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Dot indicator is unreachable — the parent span is hidden when APP_RELEASE_TAG is empty.

The inner ternary on line 60 renders a small dot when APP_RELEASE_TAG.length === 0, but the parent <span> on line 59 already carries the hidden Tailwind class under that same condition — so the dot is never displayed. The code has two conflicting intents:

  • If the intent is to hide the badge entirely when no tag is set (which is what the code actually does), the ternary inside can be simplified:
🛠 Proposed simplification
-<span className={`text-[9px] uppercase tracking-wide font-bold px-1.5 py-0.5 rounded-md ${APP_RELEASE_TAG.length > 0 ? "" : "hidden"} bg-indigo-100 text-indigo-700 dark:bg-indigo-900/40 dark:text-indigo-300`}>
-  {APP_RELEASE_TAG.length === 0 ? <span className="w-1 h-1 rounded-full bg-indigo-500 dark:bg-slate-600" /> : APP_RELEASE_TAG}
-</span>
+{APP_RELEASE_TAG && (
+  <span className="text-[9px] uppercase tracking-wide font-bold px-1.5 py-0.5 rounded-md bg-indigo-100 text-indigo-700 dark:bg-indigo-900/40 dark:text-indigo-300">
+    {APP_RELEASE_TAG}
+  </span>
+)}
  • If the intent is to show a visible dot indicator when no tag is set, the hidden class on the outer span should be removed so the dot is actually rendered.
📝 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
<span className={`text-[9px] uppercase tracking-wide font-bold px-1.5 py-0.5 rounded-md ${APP_RELEASE_TAG.length > 0 ? "" : "hidden"} bg-indigo-100 text-indigo-700 dark:bg-indigo-900/40 dark:text-indigo-300`}>
{APP_RELEASE_TAG.length === 0 ? <span className="w-1 h-1 rounded-full bg-indigo-500 dark:bg-slate-600" /> : APP_RELEASE_TAG}
</span>
{APP_RELEASE_TAG && (
<span className="text-[9px] uppercase tracking-wide font-bold px-1.5 py-0.5 rounded-md bg-indigo-100 text-indigo-700 dark:bg-indigo-900/40 dark:text-indigo-300">
{APP_RELEASE_TAG}
</span>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/chat/ChatHeader.js` around lines 59 - 61, The outer span in
ChatHeader.js currently applies "hidden" when APP_RELEASE_TAG is empty, which
prevents the inner dot from ever showing; make the behavior consistent by
removing the conditional "hidden" from that span's className and instead
conditionally render the inner content based on APP_RELEASE_TAG: keep the outer
<span> (the badge span in the ChatHeader component) always visible (no "hidden"
class), and change the inner ternary so it renders the APP_RELEASE_TAG when
present and a visible dot element when APP_RELEASE_TAG.length === 0;
alternatively, if you prefer to hide the entire badge when no tag exists,
simplify the inner ternary to only render APP_RELEASE_TAG and keep the outer
className conditional — pick one of these two consistent approaches and apply it
to the badge span in ChatHeader.js.

<Link href="/?ref=internal" className="fugaz text-sm sm:text-base md:text-lg text-gray-300">
<span className="text-indigo-400">✦</span> Moody
<Link href="/?ref=internal" className="fugaz text-sm sm:text-base md:text-lg text-slate-600 dark:text-gray-300">
<span className="text-indigo-500 dark:text-indigo-400">✦</span> Moody
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add aria-hidden="true" to the decorative symbol span.

The character is purely decorative. Without aria-hidden, screen readers will announce it (typically as "heavy four balloon-spoked asterisk" or similar), which is noisy and confusing.

As per coding guidelines, all UI components should prioritize accessibility.

♿ Proposed fix
-          <span className="text-indigo-500 dark:text-indigo-400">✦</span> Moody
+          <span className="text-indigo-500 dark:text-indigo-400" aria-hidden="true">✦</span> Moody
📝 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
<span className="text-indigo-500 dark:text-indigo-400"></span> Moody
<span className="text-indigo-500 dark:text-indigo-400" aria-hidden="true"></span> Moody
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ConditionalFooter.js` at line 20, The decorative star span (<span
className="text-indigo-500 dark:text-indigo-400">✦</span>) is announced by
screen readers; update that span in ConditionalFooter.js to include
aria-hidden="true" so the symbol is ignored by assistive technologies while
preserving visual appearance.

Comment on lines +62 to +70
<div className="flex border-slate-200 dark:border-slate-700 h-full">
<button
onClick={() => toast.dismiss(t.id)}
aria-label="Dismiss notification"
className="w-full h-full p-3 lg:p-4 text-slate-500 hover:text-slate-600 dark:text-indigo-200/80 dark:hover:text-indigo-300"
>
<X className="w-4 h-4 lg:w-5 lg:h-5" />
</button>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing border direction — separator between message and dismiss button never renders.

border-slate-200 dark:border-slate-700 sets color only. Without a border-l (or border) utility alongside it, Tailwind emits no border at all, so the visual divider is invisible.

🐛 Proposed fix
-      <div className="flex border-slate-200 dark:border-slate-700 h-full">
+      <div className="flex border-l border-slate-200 dark:border-slate-700 h-full">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/CustomToaster.js` around lines 62 - 70, The divider between the
toast message and dismiss button is missing because the wrapper div with
className "flex border-slate-200 dark:border-slate-700 h-full" only sets border
colors but not a border side; update the wrapper div in CustomToaster.js (the
div that contains the dismiss <button> and X icon) to include a border side
utility such as "border-l" (or "border" if you want all sides) so the color
classes take effect and the separator renders in both light and dark themes.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

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.

2 participants