Added dark mode cuz your app is extremely nice (I use it for myself) …#5
Added dark mode cuz your app is extremely nice (I use it for myself) …#5msjabata25 wants to merge 3 commits into
Conversation
…but the lack of a dark mode (and my allergy to light mode) is an ick i needed to fix. Fix cta buttons, quiz/flashcards feedback colors, added chat markdown support (small change with md suppport but honestly very nice to have). Also added detection for if the user already has dark mode in general or not - Add --button-primary-bg CSS var so CTA buttons stay dark in both themes - Add --feedback-correct/wrong CSS vars for adaptive green/rose indicators - Fix all CTA buttons (Generate/Reveal/Next/Send/Start) to use --button-primary-bg - Fix quiz option feedback, VerdictBadge, DeckComplete, FeedbackCard colors - Darken flashcard rating tones (50→200, 200→400) for dark mode contrast - Fix chat user bubble background in dark mode - Add react-markdown rendering to chat Bubble component
There was a problem hiding this comment.
Code Review
This pull request implements a dark mode theme across the application by introducing a ThemeProvider, updating global CSS variables, and replacing hardcoded colors with theme-aware design tokens. It also adds a dark mode toggle in the settings panel and integrates markdown rendering in the chat view. The feedback highlights a critical bug in the settings API where updating other preferences wipes out the saved theme, and suggests loading the theme server-side to prevent a Flash of Unstyled Content (FOUC). Additionally, recommendations are made to use Tailwind's dark: modifier for flashcard rating colors and to replace hardcoded code block backgrounds with design system tokens to ensure proper contrast in dark mode.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
8 issues found across 18 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
|
@msjabata25 thanks for your submission. |
|
@greptile |
…rk mode gaps - lib/viz-runtime.ts: block sandbox escape via Function constructor chain - lib/work-context.ts, lib/kg.ts, lib/store.ts: atomic write (tmp+rename) - lib/kg-runner.ts: fix TOCTOU race, catch unhandled promise rejection - lib/paths.ts: add docId validation regex to prevent path traversal - electron/main.js: fix dark-mode white flash on startup - app/api/settings/route.ts: preserve theme on partial settings POST - app/layout.tsx + ThemeProvider.tsx: SSR dark class via server-side settings read - components/SettingsButton.tsx: remove hydration guard that dropped toggles - components/RightPane/ChatView.tsx: block img, hardcoded colors → CSS vars, href sanitize - components/RightPane/FeynmanView.tsx: gradient/rose → surface/feedback tokens - components/RightPane/KnowledgeGraphView.tsx: deduplicate API fetch - components/RightPane/VoiceBlob.tsx: add maxWidth:100% for responsive sizing - components/Visualizer/GraphView.tsx: canvas/axis colors adapt to html.dark - components/Visualizer/ThreeDView.tsx: clearColor adapts to html.dark - components/PdfViewer.tsx: bg-white → surface tokens - components/UploadCard.tsx: unmount race guard, feedback tokens, keyboard handler - components/CodexHealthBanner.tsx: rose/amber → feedback/tag tokens - app/globals.css: refine dark palette surface/ink/border/accents
msjabata25
left a comment
There was a problem hiding this comment.
Fixed the issues that were in the ai reviews.
All details in the commit message .
And don't worry I did update the voice blob and made it match the theme
|
@7Mik I have started the AI code review. It will take a few minutes to complete. |
|
The review has been posted above. Here's a quick summary of the key findings: High priority:
Lower priority:
The PR is solid overall — the token design in Tip: You can customize Greptile's behavior for this repo with |
There was a problem hiding this comment.
11 issues found across 29 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
- lib/viz-runtime.ts: null AsyncFunction/GeneratorFunction constructors - lib/work-context.ts, kg.ts, store.ts: use unique tmp suffix per write - lib/paths.ts: widen docId regex to accept non-hex IDs (sample-anatomy) - components/Visualizer/GraphView.tsx: add MutationObserver for theme changes; use ink/inkMuted for grid, axes, legend, axis labels - components/ThemeProvider.tsx: always reapply() on system pref change - components/SettingsButton.tsx: guard stale fetch from overwriting user theme toggle (userToggledRef) - components/RightPane/ChatView.tsx: unconditional whitespace-pre-wrap so non-pulsing bubbles preserve newlines - components/RightPane/FlashcardsView.tsx: hardcoded Tailwind shades → --feedback-wrong/tag-amber/--feedback-correct/tag-sky tokens - components/RightPane/VoiceBlob.tsx: add aspectRatio:1 to prevent canvas distortion when maxWidth clamps width - components/AccountButton.tsx: logout hover uses feedback-wrong tokens - components/WelcomePopup.tsx + globals.css: add --shadow-modal token - components/UploadCard.tsx: add e.preventDefault() on Space/Enter
|
Fixed the remaining issues (the ai reviewer is pointing out already solved issues) so if it comes out with another similar review report then i suggest you review the codebase with something else to avoid fixing the same issue over and over again |
There was a problem hiding this comment.
4 issues found across 29 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="components/Visualizer/GraphView.tsx">
<violation number="1" location="components/Visualizer/GraphView.tsx:266">
P2: Theme-triggered redraw can violate the "once per spec" error-callback contract. Toggling theme may repeatedly re-emit runtime errors and retrigger downstream repair/error flows for unchanged graph specs.</violation>
</file>
<file name="components/ThemeProvider.tsx">
<violation number="1" location="components/ThemeProvider.tsx:31">
P2: Fetch-failure fallback overrides explicit theme with system preference. On `/api/settings` error, keep current class instead of calling system fallback.</violation>
</file>
<file name="components/RightPane/ChatView.tsx">
<violation number="1" location="components/RightPane/ChatView.tsx:360">
P3: `code` markdown renderer forwards react-markdown internal props to DOM. Strip `node` before spreading props to `<code>` to avoid React unknown-prop warnings.</violation>
</file>
<file name="components/SettingsButton.tsx">
<violation number="1" location="components/SettingsButton.tsx:90">
P2: Dark-mode switch state is memoized against `theme` only, so follow-system UI can go stale after OS theme changes. This can invert toggle behavior and show incorrect status text until another state change happens.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| // every parent rerender that produces a new function reference. | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [spec]); | ||
| }, [spec, themeVersion]); |
There was a problem hiding this comment.
P2: Theme-triggered redraw can violate the "once per spec" error-callback contract. Toggling theme may repeatedly re-emit runtime errors and retrigger downstream repair/error flows for unchanged graph specs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/Visualizer/GraphView.tsx, line 266:
<comment>Theme-triggered redraw can violate the "once per spec" error-callback contract. Toggling theme may repeatedly re-emit runtime errors and retrigger downstream repair/error flows for unchanged graph specs.</comment>
<file context>
@@ -251,7 +263,7 @@ export default function GraphView({ spec, onRuntimeError }: Props) {
// every parent rerender that produces a new function reference.
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [spec]);
+ }, [spec, themeVersion]);
return (
</file context>
| fetch("/api/settings", { cache: "no-store" }) | ||
| .then((r) => r.json()) | ||
| .then((s: { theme?: string }) => applyTheme(s.theme)) | ||
| .catch(() => applyTheme(undefined)); |
There was a problem hiding this comment.
P2: Fetch-failure fallback overrides explicit theme with system preference. On /api/settings error, keep current class instead of calling system fallback.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/ThemeProvider.tsx, line 31:
<comment>Fetch-failure fallback overrides explicit theme with system preference. On `/api/settings` error, keep current class instead of calling system fallback.</comment>
<file context>
@@ -0,0 +1,56 @@
+ fetch("/api/settings", { cache: "no-store" })
+ .then((r) => r.json())
+ .then((s: { theme?: string }) => applyTheme(s.theme))
+ .catch(() => applyTheme(undefined));
+ }, []);
+
</file context>
| const userToggledRef = useRef(false); | ||
|
|
||
| // Effective theme — if no explicit preference is stored, follow system. | ||
| const effectiveTheme = useMemo<"light" | "dark">(() => { |
There was a problem hiding this comment.
P2: Dark-mode switch state is memoized against theme only, so follow-system UI can go stale after OS theme changes. This can invert toggle behavior and show incorrect status text until another state change happens.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/SettingsButton.tsx, line 90:
<comment>Dark-mode switch state is memoized against `theme` only, so follow-system UI can go stale after OS theme changes. This can invert toggle behavior and show incorrect status text until another state change happens.</comment>
<file context>
@@ -81,20 +82,33 @@ export default function SettingsButton() {
+ const userToggledRef = useRef(false);
+
+ // Effective theme — if no explicit preference is stored, follow system.
+ const effectiveTheme = useMemo<"light" | "dark">(() => {
+ if (theme === "light" || theme === "dark") return theme;
+ if (typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches) {
</file context>
| {children} | ||
| </pre> | ||
| ), | ||
| code: ({ className, children, ...props }) => { |
There was a problem hiding this comment.
P3: code markdown renderer forwards react-markdown internal props to DOM. Strip node before spreading props to <code> to avoid React unknown-prop warnings.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/RightPane/ChatView.tsx, line 360:
<comment>`code` markdown renderer forwards react-markdown internal props to DOM. Strip `node` before spreading props to `<code>` to avoid React unknown-prop warnings.</comment>
<file context>
@@ -335,13 +336,54 @@ function Bubble({
+ {children}
+ </pre>
+ ),
+ code: ({ className, children, ...props }) => {
+ const text = String(children);
+ const isInline = !className && !text.includes("\n");
</file context>
| code: ({ className, children, ...props }) => { | |
| code: ({ node: _node, className, children, ...props }) => { |
Added dark mode cuz your app is extremely nice (I use it for myself) but the lack of a dark mode (and my allergy to light mode) is an ick i needed to fix.
Fix cta buttons, quiz/flashcards feedback colors, added chat markdown support (small change with md suppport but honestly very nice to have). Also added detection for if the user already has dark mode in general or not
Added --button-primary-bg CSS var so CTA buttons stay dark in both themes
Added --feedback-correct/wrong CSS vars for adaptive green/rose indicators
Fixed all CTA buttons (Generate/Reveal/Next/Send/Start) to use --button-primary-bg
Fixed quiz option feedback, VerdictBadge, DeckComplete, FeedbackCard colors
Darkened flashcard rating tones (50→200, 200→400) for dark mode contrast
Fixed chat user bubble background in dark mode
Added react-markdown rendering to chat Bubble component
Note: The Feynman session where the bubble thing is is i think still light mode but i'll try and fix it in a later contribution
Summary by cubic
Adds a system-aware dark mode with SSR application and a Settings toggle, with better live theme responses across the app. Updates tokens so panels, PDFs, buttons, and feedback look right in both themes, and adds chat Markdown with preserved line breaks.
New Features
html.darktokens andThemeProvider; follows saved preference or system and re-applies on changes viaSETTINGS_EVENTand media queries.react-markdown, preserve newlines, block images, and sanitizehttps?://links.--surface-*and--shadow-modal.Bug Fixes
evaland constructor-chain escapes by nullingFunction/AsyncFunction/GeneratorFunctionbefore running viz code.docIdregex widened (e.g.sample-anatomy) while still preventing traversal; VoiceBlob keeps a 1:1 aspect ratio.Written for commit 34f8341. Summary will update on new commits.
Greptile Summary
This PR adds a full system-aware dark mode with SSR class injection, a blocking inline script for FOUC prevention, a
ThemeProviderthat reacts to live settings events and OS preference changes, and a Settings toggle — along with Markdown rendering in chat bubbles, atomic file writes, KG build dedup, and sandbox hardening in the viz runtime.app/globals.cssgains a completehtml.darkblock with surface, ink, feedback, shadow, and button tokens; all hardcodedbg-white/ rose / emerald Tailwind classes across the right-pane views, library, PDF viewer, and overlays are migrated to CSS variables.app/layout.tsxreadsloadSettings()at SSR time and injects both a server-rendereddarkclass on<html>and a tiny blocking<script>that applies or removes it based on stored preference ormatchMedia, so dark-mode users no longer see a white flash on navigation.ChatViewBubblerenders viareact-markdownwith sanitized links and blocked images;UploadCard,lib/kg.ts,lib/store.ts, andlib/work-context.tsall switch to atomic UUID-suffixed tmp-file writes;lib/paths.tsadds path-traversal validation on everydocId.Confidence Score: 4/5
Safe to merge for most users, but light-mode Electron users will see a dark flash on startup and a mismatched Windows title bar, and the 3D visualiser background won't update after a live theme toggle.
Two issues affect current users on well-travelled paths: the Electron window chrome (
backgroundColorandtitleBarOverlay) is now hardcoded dark regardless of the saved preference, so anyone running the app in light mode gets a visual regression at every launch; andThreeDViewreadsisDarkonly at mount time with no observer, so switching theme while a 3D scene is open leaves a stale background color. Both are straightforward to fix and do not affect data correctness.electron/main.js(hardcoded dark window chrome) andcomponents/Visualizer/ThreeDView.tsx(theme not reactive after mount) need attention before the Electron build ships to light-mode users.Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Browser participant SSR as app/layout.tsx (SSR) participant Script as Inline blocking script participant TP as ThemeProvider participant SB as SettingsButton participant API as /api/settings SSR->>SSR: loadSettings() → theme SSR->>Browser: html.dark class + inline script Browser->>Script: executes before paint Script->>Browser: classList.add/remove(dark) from matchMedia fallback Browser->>TP: mount (useEffect) TP->>Browser: addEventListener(SETTINGS_EVENT) TP->>Browser: matchMedia.addEventListener(change) Note over TP,Browser: Live system pref changes → fetch + reapply SB->>API: GET /api/settings (on panel open) API-->>SB: theme, autoGenerate, maxRetries SB->>SB: setTheme(stored value) SB->>SB: user clicks toggle SB->>Browser: classList.toggle(dark) SB->>API: POST /api/settings with theme SB->>Browser: dispatchEvent(SETTINGS_EVENT) Browser->>TP: SETTINGS_EVENT listener fires TP->>Browser: applyTheme(detail.theme)%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Browser participant SSR as app/layout.tsx (SSR) participant Script as Inline blocking script participant TP as ThemeProvider participant SB as SettingsButton participant API as /api/settings SSR->>SSR: loadSettings() → theme SSR->>Browser: html.dark class + inline script Browser->>Script: executes before paint Script->>Browser: classList.add/remove(dark) from matchMedia fallback Browser->>TP: mount (useEffect) TP->>Browser: addEventListener(SETTINGS_EVENT) TP->>Browser: matchMedia.addEventListener(change) Note over TP,Browser: Live system pref changes → fetch + reapply SB->>API: GET /api/settings (on panel open) API-->>SB: theme, autoGenerate, maxRetries SB->>SB: setTheme(stored value) SB->>SB: user clicks toggle SB->>Browser: classList.toggle(dark) SB->>API: POST /api/settings with theme SB->>Browser: dispatchEvent(SETTINGS_EVENT) Browser->>TP: SETTINGS_EVENT listener fires TP->>Browser: applyTheme(detail.theme)Comments Outside Diff (1)
components/RightPane/ChatView.tsx, line 439-451 (link)isInlineis derived from!className. This works for labelled fences (```ts) because react-markdown injectslanguage-ts. For an unlabelled fence (```with no language), react-markdown passes noclassName, soisInlineistrueand the content is wrapped in the small inline<code>span instead of the styled<pre>block. LLM responses frequently omit language labels, so multi-line code will collapse into a single inline chip. A safer guard is to additionally check that the content contains a newline before treating it as inline.Reviews (3): Last reviewed commit: "review fixes: sandbox, write races, them..." | Re-trigger Greptile