Skip to content

fix(#537): placeholder images respond to dynamic themes - #9

Closed
Owie6789 wants to merge 4 commits into
release/4.0.0-alpha.5from
fix/537-placeholder-theme-colors-v2
Closed

fix(#537): placeholder images respond to dynamic themes#9
Owie6789 wants to merge 4 commits into
release/4.0.0-alpha.5from
fix/537-placeholder-theme-colors-v2

Conversation

@Owie6789

@Owie6789 Owie6789 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Fixes Sandakan#537. Adds SVGR plugin for inline SVG rendering, --illustration-accent-color CSS variable, ThemeableIllustration wrapper component, and converts all placeholder images to use inline SVGs with accent color.

Phase 2: Add illustration accent to useDynamicTheme.tsx - set
--illustration-accent-color from palette Vibrant swatch, add to
resetStyles so it reverts when dynamic themes are off.

Phase 3: Add themeable illustrations to history, favorites, and
playlistId empty states using Empty Inbox Monochromatic SVG.
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@Owie6789

Owie6789 commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

@CodeRabbit full review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 59 minutes.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@Owie6789, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 59 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 110fee1f-03a7-4682-a8d8-dde9ffbda524

📥 Commits

Reviewing files that changed from the base of the PR and between 3ebea51 and be3a448.

📒 Files selected for processing (4)
  • src/renderer/src/hooks/useDynamicTheme.tsx
  • src/renderer/src/routes/main-player/playlists/$playlistId.tsx
  • src/renderer/src/routes/main-player/playlists/favorites.tsx
  • src/renderer/src/routes/main-player/playlists/history.tsx

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

❤️ Share

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

@Owie6789

Owie6789 commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

@CodeRabbit Act as a senior code reviewer and QA engineer. Perform a comprehensive, deep-dive analysis of the entire PR diff against release/4.0.0-alpha.5.

Self-review before posting

For each finding, double-review:

  • Cause: Is the root cause in THIS diff or pre-existing base code?
  • Action: Does the fix compile and match existing types?
  • Reaction: What breaks if applied? Does it introduce a new issue?
  • Necessity: Is this a real bug or style preference? What is the regression risk?
    Only post findings that survive all four checks.

UI review

Evaluate visual hierarchy, spacing, WCAG AA contrast, typography, focus/keyboard nav, responsive layout, loading/empty/error states, micro-interactions, icon consistency, scoped transitions (not transition-all), ARIA attributes, and dark/light theme consistency.

Coverage checklist

  • Merge state and branch staleness (is the branch based on the latest target tip?)
  • IPC boundary validation (safe integers, URL validation, discriminated results)
  • State machine / lifecycle cleanup (listeners removed, no player side-effects)
  • Persistence and async paths (generation counters, per-entity locks, finally cleanup)
  • Edge cases and error propagation
  • Security (path traversal, input validation)
  • Test coverage for new branches
  • Lint/CI hygiene (void prefix, log() not console.log, no any at boundary)

Verdict format

End with exactly one of:

  • ## Verdict: **Ready** — confidence: **high/medium** (no remaining issues)
  • ## Verdict: **Not Ready** — confidence: **high/medium** (with remaining findings listed)

If a prior finding is already fixed in the current diff, say so explicitly rather than re-reporting it.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='release/4.0.0-alpha.5'

echo '== Repository and merge state =='
git status --short
git branch --show-current
git show-ref --verify --quiet "refs/remotes/origin/${base}" || true
git merge-base HEAD "origin/${base}" || true
git rev-parse HEAD
git rev-parse "origin/${base}"

echo '== Changed files against merge base =='
merge_base="$(git merge-base HEAD "origin/${base}")"
git diff --name-status "${merge_base}..HEAD"
git diff --stat "${merge_base}..HEAD"

echo '== Full PR diff =='
git diff --find-renames --find-copies --no-ext-diff "${merge_base}..HEAD"

echo '== Project manifests and automation files =='
fd -HI '^(package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json|vite\.config\..*|vitest\.config\..*|eslint\.config\..*|\.eslintrc.*|tsconfig.*\.json)$' . \
  -E node_modules -E dist -E build \
  -x sh -c 'echo "--- $1"; sed -n "1,260p" "$1"' sh {}

echo '== Affected source outlines =='
git diff --name-only "${merge_base}..HEAD" \
  | grep -E '\.(ts|tsx|js|jsx)$' \
  | while IFS= read -r f; do
      echo "--- $f"
      ast-grep outline "$f" --items all 2>/dev/null || true
    done

Length of output: 1818


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Available Git refs =='
git branch -a
git show-ref --heads --tags | sed -n '1,240p'

echo '== Remotes =='
git remote -v

echo '== Pull request metadata =='
gh pr view 9 --repo Owie6789/Nora --json number,url,state,isDraft,baseRefName,baseRefOid,headRefName,headRefOid,mergeable,mergeStateStatus,commits,statusCheckRollup

echo '== Pull request files and patches =='
gh api --paginate 'repos/Owie6789/Nora/pulls/9/files?per_page=100' \
  --jq '.[] | "=== \(.status) \(.filename) ===\n\(.patch // "[No textual patch available]")\n"'

echo '== Local commit parents and changed paths =='
git show -s --format='commit=%H%nparents=%P%nsubject=%s' HEAD
git diff-tree --no-commit-id --name-status -r HEAD

Length of output: 9150


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Changed hook and route context =='
sed -n '1,230p' src/renderer/src/hooks/useDynamicTheme.tsx
for f in \
  'src/renderer/src/routes/main-player/playlists/$playlistId.tsx' \
  'src/renderer/src/routes/main-player/playlists/favorites.tsx' \
  'src/renderer/src/routes/main-player/playlists/history.tsx'
do
  echo "--- $f"
  grep -n -E 'ThemeableIllustration|EmptyStateImage|no-songs-container|function (PlaylistInfoPage|FavoritesPlaylistInfoPage|HistoryPlaylistInfoPage)|return \(' "$f" || true
  sed -n '240,300p' "$f"
done

echo '== ThemeableIllustration candidates =='
fd -HI -i 'ThemeableIllustration*' . -E node_modules -E dist -E build \
  -x sh -c 'echo "--- $1"; ast-grep outline "$1" --items all 2>/dev/null || true; cat -n "$1"' sh {}

echo '== SVG asset candidates =='
fd -HI -i '*Empty*Inbox*Monochromatic*.svg' . -E node_modules -E dist -E build \
  -x sh -c 'echo "--- $1"; cat -n "$1"' sh {}

echo '== CSS variable declarations and SVG loader/type support =='
rg -n -i -C 4 --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' \
  '(illustration-accent-color|ThemeableIllustration|svgr|\.svg\?react|declare module .*\*\.svg|vite-plugin-svgr)' .

echo '== Related tests and test configuration =='
fd -HI -i '*(playlist|dynamicTheme|themeableIllustration|illustration)*' . -E node_modules -E dist -E build \
  | grep -E '(\.test\.|\.spec\.|__tests__|vitest|test)' \
  | while IFS= read -r f; do
      echo "--- $f"
      ast-grep outline "$f" --items all 2>/dev/null || true
      sed -n '1,260p' "$f"
    done

echo '== Manifest and Vite configuration =='
fd -HI '^(package\.json|vite\.config\..*|tsconfig.*\.json)$' . -E node_modules -E dist -E build \
  -x sh -c 'echo "--- $1"; cat -n "$1"' sh {}

Length of output: 18075


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head='be3a448bdd1bc8733027603f6fcdd4da031ca430'
base='3ebea514dc1d4b3e59ba5c31b33324349c345418'

echo '== Matching paths in PR and target Git trees =='
for rev in "$base" "$head"; do
  echo "--- $rev"
  git ls-tree -r --name-only "$rev" | grep -Ei \
    '(ThemeableIllustration|Empty Inbox.*Monochromatic.*\.svg|vite\.config|package\.json|env\.d\.ts|vite-env\.d\.ts|\.d\.ts$)' \
    || true
done

echo '== Component directory listing =='
find src/renderer/src/components -maxdepth 2 -type f 2>/dev/null | sort | sed -n '1,260p'

echo '== Corrected asset and source searches =='
fd -HI -i 'ThemeableIllustration' . -E node_modules -E dist -E build || true
fd -HI -i 'Empty Inbox _Monochromatic.svg' . -E node_modules -E dist -E build || true
rg -n -i -C 3 --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' \
  '(ThemeableIllustration|vite-plugin-svgr|svgr|\.svg\?react|illustration-accent-color)' . || true

echo '== Resolved component, asset, and configuration contents =='
for f in \
  $(fd -HI -i 'ThemeableIllustration' . -E node_modules -E dist -E build || true) \
  "$(fd -HI -i 'Empty Inbox _Monochromatic.svg' . -E node_modules -E dist -E build | head -n1 || true)" \
  "$(fd -HI '^vite\.config\..*$' . -E node_modules -E dist -E build | head -n1 || true)" \
  "$(fd -HI '^package\.json$' . -E node_modules -E dist -E build | head -n1 || true)"
do
  if [ -n "$f" ] && [ -f "$f" ]; then
    echo "--- $f"
    cat -n "$f"
  fi
done

echo '== Relevant test files =='
find src -type f \( -name '*.test.ts' -o -name '*.test.tsx' -o -name '*.spec.ts' -o -name '*.spec.tsx' \) \
  | grep -Ei '(playlist|theme|illustration|route)' \
  | sort \
  | while IFS= read -r f; do
      echo "--- $f"
      ast-grep outline "$f" --items all 2>/dev/null || true
    done

Length of output: 35561


@Owie6789

Findings

  1. Blocker — the changed playlist routes cannot resolve their new imports.

    ThemeableIllustration does not exist in either the PR tree or release/4.0.0-alpha.5. The repository also has no vite-plugin-svgr dependency, Vite plugin configuration, or TypeScript declaration for *.svg?react.

    The following imports are introduced by this PR:

    • src/renderer/src/routes/main-player/playlists/$playlistId.tsx, Lines 21-22
    • src/renderer/src/routes/main-player/playlists/favorites.tsx, Lines 22-23
    • src/renderer/src/routes/main-player/playlists/history.tsx, Lines 21-22

    A renderer typecheck or production build will fail to resolve @renderer/components/ThemeableIllustration and the ?react SVG module. The new CSS properties also have no consumer in the current tree. The SVG still uses fixed fill="#68E1FD" values.

    Add the wrapper component, SVGR dependency and Vite configuration, the SVG module declaration, and the SVG color conversion in this PR. Add a renderer typecheck or build check to CI. The current lint and test checks do not validate these module imports.

Review coverage

  • The branch is current with release/4.0.0-alpha.5. The PR commit directly follows the current target SHA.
  • The PR has no IPC, persistence, lifecycle, player, or path-handling changes.
  • The existing checks report success, but they do not prove renderer compilation.
  • No tests cover dynamic illustration colors, dynamic-theme cleanup, or the three empty playlist states.
  • I did not identify a separate accessibility or responsive-layout regression. The compile blocker prevents meaningful UI validation.

Verdict: Not Ready — confidence: high

You are interacting with an AI system.

…S vars

Phase 1: Add vite-plugin-svgr to renderer build with replaceAttrValues
for #68e1fd -> currentColor. Declare *.svg?react module typing.

Phase 2: Add --illustration-accent-color CSS variable with Tailwind
token. Wire into useDynamicTheme.tsx palette and resetStyles.

Phase 3: Create ThemeableIllustration wrapper. Convert all empty-state
placeholders (search, albums, artists, genres, songs, queue, playlists,
history, favorites, , ErrorBoundary) to use inline SVGs.
Move ?react inside the import string to satisfy oxlint parser.
All SVG imports now use correct syntax: from 'path.svg?react'
…mponents

Replace Img with ThemeableIllustration in NoSearchResultsContainer and
SearchStartPlaceholder to resolve unused import warnings (S1128).
@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

@Owie6789

Owie6789 commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

@CodeRabbit Act as a senior code reviewer and QA engineer. Perform a comprehensive, deep-dive analysis of the entire PR diff against release/4.0.0-alpha.5.

Self-review before posting

For each finding, double-review:

  • Cause: Is the root cause in THIS diff or pre-existing base code?
  • Action: Does the fix compile and match existing types?
  • Reaction: What breaks if applied? Does it introduce a new issue?
  • Necessity: Is this a real bug or style preference? What is the regression risk?
    Only post findings that survive all four checks.

UI review

Evaluate visual hierarchy, spacing, WCAG AA contrast, typography, focus/keyboard nav, responsive layout, loading/empty/error states, micro-interactions, icon consistency, scoped transitions (not transition-all), ARIA attributes, and dark/light theme consistency.

Coverage checklist

  • Merge state and branch staleness (is the branch based on the latest target tip?)
  • IPC boundary validation (safe integers, URL validation, discriminated results)
  • State machine / lifecycle cleanup (listeners removed, no player side-effects)
  • Persistence and async paths (generation counters, per-entity locks, finally cleanup)
  • Edge cases and error propagation
  • Security (path traversal, input validation)
  • Test coverage for new branches
  • Lint/CI hygiene (void prefix, log() not console.log, no any at boundary)

Verdict format

End with exactly one of:

  • ## Verdict: **Ready** — confidence: **high/medium** (no remaining issues)
  • ## Verdict: **Not Ready** — confidence: **high/medium** (with remaining findings listed)

If a prior finding is already fixed in the current diff, say so explicitly rather than re-reporting it.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='Owie6789/Nora'
base='release/4.0.0-alpha.5'

echo '== Pull request metadata =='
gh pr view 9 --repo "$repo" --json number,url,state,isDraft,baseRefName,baseRefOid,headRefName,headRefOid,mergeable,mergeStateStatus,commits,statusCheckRollup

echo '== Branch and merge-base state =='
git fetch origin "$base" --quiet
merge_base="$(git merge-base HEAD "origin/$base")"
printf 'head=%s\nbase_tip=%s\nmerge_base=%s\n' \
  "$(git rev-parse HEAD)" "$(git rev-parse "origin/$base")" "$merge_base"
git merge-base --is-ancestor "origin/$base" HEAD && echo 'base_is_ancestor_of_head=yes' || echo 'base_is_ancestor_of_head=no'

echo '== Changed files =='
git diff --name-status "$merge_base..HEAD"
git diff --stat "$merge_base..HEAD"

echo '== Diff =='
git diff --find-renames --find-copies --no-ext-diff "$merge_base..HEAD"

echo '== Changed TypeScript/TSX outlines =='
git diff --name-only "$merge_base..HEAD" \
  | grep -E '\.(ts|tsx)$' \
  | while IFS= read -r file; do
      echo "--- $file"
      ast-grep outline "$file" --items all 2>/dev/null || true
    done

Length of output: 6038


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='Owie6789/Nora'
base_sha='3ebea514dc1d4b3e59ba5c31b33324349c345418'
head_sha='7fdfd309f35ce8a5a9593280ff4c6d76032f1d9f'

echo '== GitHub comparison metadata =='
gh api "repos/${repo}/compare/${base_sha}...${head_sha}" \
  --jq '{status,ahead_by,behind_by,total_commits,merge_base_commit:.merge_base_commit.sha}'

echo '== Changed paths and patches =='
gh api --paginate "repos/${repo}/pulls/9/files?per_page=100" \
  | jq -r '.[] | "=== \(.status) \(.filename) ===\n\(.patch // "[No textual patch]")\n"'

echo '== Checked-out head =='
git rev-parse HEAD
git status --short

echo '== Changed local source/configuration outlines =='
gh api --paginate "repos/${repo}/pulls/9/files?per_page=100" --jq '.[].filename' \
  | while IFS= read -r file; do
      if [ -f "$file" ]; then
        case "$file" in
          *.ts|*.tsx|*.js|*.jsx)
            echo "--- OUTLINE $file"
            ast-grep outline "$file" --items all 2>/dev/null || true
            ;;
        esac
      else
        echo "--- MISSING FROM LOCAL CHECKOUT $file"
      fi
    done

echo '== Renderer SVG and style integration =='
rg -n -C 3 --glob '!node_modules/**' \
  '(vite-plugin-svgr|svgr\(|replaceAttrValues|svg\?react|ThemeableIllustration|illustration-accent-color)' \
  package.json src vite.config.* 2>/dev/null || true

Length of output: 67382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== SVGR dependency and package-manager state =='
for file in package.json pnpm-lock.yaml package-lock.json yarn.lock bun.lockb; do
  if [ -f "$file" ]; then
    echo "--- $file"
    rg -n -i -C 2 'vite-plugin-svgr|`@svgr`' "$file" || true
  fi
done

echo '== Theme token and dark-selector definitions =='
rg -n -C 6 --glob '!node_modules/**' \
  'dark-illustration-accent|illustration-accent|--dark-text-color-highlight|--dark-illustration|darkMode|\.dark\b|\[data-theme' \
  tailwind.config.js src/renderer/src/assets/styles src/renderer/src 2>/dev/null || true

echo '== ThemeableIllustration and changed placeholder context =='
cat -n src/renderer/src/components/ThemeableIllustration/index.tsx
for file in \
  src/renderer/src/components/SearchPage/NoSearchResultsContainer.tsx \
  src/renderer/src/components/SearchPage/SearchStartPlaceholder.tsx \
  src/renderer/src/components/ErrorBoundary.tsx \
  src/renderer/src/routes/main-player/albums/index.tsx \
  src/renderer/src/routes/main-player/playlists/\$playlistId.tsx
do
  echo "--- $file"
  sed -n '1,110p' "$file"
  grep -n -A12 -B5 'ThemeableIllustration' "$file" || true
done

echo '== SVG dimensions and accent-color source forms =='
for file in \
  'src/renderer/src/assets/images/svg/Bug Fixed_Monochromatic.svg' \
  'src/renderer/src/assets/images/svg/Sad face_Monochromatic.svg' \
  'src/renderer/src/assets/images/svg/Flying kite_Monochromatic.svg' \
  'src/renderer/src/assets/images/svg/Easter bunny_Monochromatic.svg' \
  'src/renderer/src/assets/images/svg/Sun_Monochromatic.svg' \
  'src/renderer/src/assets/images/svg/Summer landscape_Monochromatic.svg' \
  'src/renderer/src/assets/images/svg/Empty Inbox _Monochromatic.svg'
do
  echo "--- $file"
  sed -n '1,5p' "$file"
  rg -o -i '#[0-9a-f]{3,8}|fill="[^"]+"|stroke="[^"]+"' "$file" | sort | uniq -c
done

echo '== Remaining changed-file imports of Img =='
git diff --name-only HEAD~4..HEAD -- '*.ts' '*.tsx' \
  | while IFS= read -r file; do
      echo "--- $file"
      rg -n '^\s*import\s+.*\bImg\b.*from ' "$file" || true
    done

echo '== Available typecheck/build scripts and CI commands =='
node -e "const p=require('./package.json'); console.log(JSON.stringify({scripts:p.scripts,packageManager:p.packageManager},null,2))"
fd -HI '.*' .github 2>/dev/null | while IFS= read -r file; do
  echo "--- $file"
  sed -n '1,220p' "$file"
done

Length of output: 148978


@Owie6789

The prior unresolved-import finding is fixed. This PR now includes ThemeableIllustration, the *.svg?react declaration, and SVGR configuration.

Findings

  1. Blocker — vite-plugin-svgr is not installed.

    electron.vite.config.ts imports vite-plugin-svgr, but neither package.json nor package-lock.json contains vite-plugin-svgr or its @svgr dependencies.

    A clean npm ci followed by npm run build cannot load the Vite configuration. Existing local node_modules can hide this failure.

    Add vite-plugin-svgr to devDependencies and regenerate package-lock.json. Add npm run typecheck and npm run build to CI so that configuration imports and SVG transforms are verified.

  2. High — dark mode does not use the dark illustration accent.

    src/renderer/src/assets/styles/styles.css defines --dark-illustration-accent-color, and useDynamicTheme updates it. However, the dark-theme selector does not assign:

    --illustration-accent-color: var(--dark-illustration-accent-color);

    ThemeableIllustration always sets its inline color from --illustration-accent-color. Therefore, dark mode uses the light-mode value, including the dynamic darkVibrant palette value. This can make the illustration too dark against the dark background.

    Add the variable mapping to the existing .App.dark, .full-screen-player.dark, and .mini-player.dark rule. Remove dark:text-dark-illustration-accent, or add the missing Tailwind token. The inline style currently takes precedence, so the Tailwind dark class cannot correct the color.

Review coverage

  • The branch is current with release/4.0.0-alpha.5. It is four commits ahead and zero commits behind the target.
  • I checked the changed SVG integration, type declaration, dynamic-theme reset path, and all converted empty-state call sites.
  • The PR does not change IPC boundaries, persistence, lifecycle listeners, player behavior, path handling, or security-sensitive code.
  • I found no additional verified keyboard, ARIA, responsive-layout, transition, or empty/error-state regression.
  • The existing checks pass, but they do not run the renderer typecheck or production build.

Verdict: Not Ready — confidence: high

You are interacting with an AI system.

@Owie6789 Owie6789 closed this Aug 5, 2026
@Owie6789
Owie6789 deleted the fix/537-placeholder-theme-colors-v2 branch August 5, 2026 19:05
@Owie6789
Owie6789 restored the fix/537-placeholder-theme-colors-v2 branch August 5, 2026 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant