Refactor UI components, enhance markdown support, and improve accessibility#59
Refactor UI components, enhance markdown support, and improve accessibility#59aditya-2k23 wants to merge 18 commits intomainfrom
Conversation
…les across various components Co-authored-by: Copilot <copilot@github.com>
…urnal and JournalModal with new features
…increase page size
…onment variables and improve SSR compatibility
…estrictions in AGENT.md
…s.css, CustomToaster, Journal, and MessageBubble
…bal styles to utilize them Co-authored-by: Copilot <copilot@github.com>
…nd journal modal view.
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.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Deploy Preview for moody-adi ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
📝 WalkthroughWalkthroughThis 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. ChangesRich Text Editing & Design System Overhaul
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (7)
components/Input.js (1)
4-4: ⚡ Quick winUse
focus-visible+ scoped transitions to keep interactions calmer and cheaper
transition-all+ always-onfocus:glow can feel busy and animates more properties than needed. Preferfocus-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 valueWhitespace-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 valueVoice input may not display while editor is focused.
The
isFocusedguard 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 valueHardcoded 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 valueMarkdown 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 valueConsider using design tokens for transition values.
The
.smooth-transitionutility hardcodes220msand the cubic-bezier easing, but these values are already defined as--duration-baseand--ease-defaultin 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 winKill in-flight GSAP tweens before starting a new one.
If
t.visibleflips tofalsewhile the 0.5 s entry animation is still running, a secondgsap.tostarts on the same element and both tweens fight each other — the exit animation loses. Callinggsap.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
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (32)
.gitignoreAGENT.mdapp/api/delete-account/route.jsapp/design-tokens.cssapp/globals.cssapp/layout.jsapp/page.jscomponents/AIInsightsSection.jscomponents/ConditionalFooter.jscomponents/CustomToaster.jscomponents/Dashboard.jscomponents/GlowBackground.jscomponents/Input.jscomponents/Journal.jscomponents/JournalModal.jscomponents/Login.jscomponents/RichTextEditor.jscomponents/Splashscreen.jscomponents/StyleTools.jscomponents/ThemeToggle.jscomponents/chat/ChatHeader.jscomponents/chat/MessageBubble.jscomponents/chat/MessageList.jscomponents/landing/HeroSection.jscomponents/landing/LandingFooter.jscomponents/landing/LumiDemoSection.jscontext/authContext.jsfirebase.jslib/release.jsnext.config.mjspackage.jsontailwind.config.js
💤 Files with no reviewable changes (1)
- AGENT.md
| <Sparkles size={25} /> | ||
| {insights ? "Lumi's Thoughts" : "Let's talk about your day"} |
There was a problem hiding this comment.
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.
| <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.
| <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> |
There was a problem hiding this comment.
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
hiddenclass 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.
| <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 |
There was a problem hiding this comment.
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.
| <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.
| <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> |
There was a problem hiding this comment.
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.
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
app/design-tokens.cssfile containing all design tokens (colors, fonts, spacing, shadows, etc.) for consistent theming, and refactoredapp/globals.cssto import and use these tokens. Removed legacy CSS variables fromglobals.cssand 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].purpleShadow,.glow,.blob-btn, and markdown content. Added.smooth-transitionutility and improved focus and contrast for.journal-textarea. [1] [2] [3] [4] [5] [6] [7] [8]Firestore Deletion Optimization
deleteCollectionTreefunction inapp/api/delete-account/route.jsto use Firestore batch deletes (500 per batch) for improved performance and reliability, passing thedbinstance explicitly. [1] [2]UI/UX & Accessibility Improvements
Component & Dependency Updates
react-hot-toastToasterwith a customCustomToastercomponent for more controlled notifications. [1] [2]Documentation Updates
AGENT.md, removing redundant or outdated restrictions and clarifying best practices. [1] [2]Summary by CodeRabbit
New Features
Improvements
Bug Fixes