Implement Highlight Words tab in Verse on Image - #1089
Conversation
📝 WalkthroughWalkthroughVerseOnImage now splits verse text into selectable words, supports per-word color and case editing, adds a Word Editor Pane, and synchronizes editor-tab scrolling. New arrow and word-selection icons are added and exported through the icon registries. The download selector styling is also updated. ChangesVerse word editing
Download selector styling
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant VerseOnImage
participant WordEditorPane
participant WordState
VerseOnImage->>WordState: Select rendered word
VerseOnImage->>WordEditorPane: Show selected word controls
WordEditorPane->>WordState: Update color or case
WordState->>VerseOnImage: Apply word styling
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/components/VerseOnImage.svelte`:
- Around line 1143-1160: The uppercase and lowercase controls in the
word-selection UI use fixed gray icon colors, preventing selected-state styling.
Update the ArrowUpIcon and ArrowDownIcon usages in the relevant button handlers
to receive SliderProgressColor when their corresponding wordSelected.uppercase
state is selected, while retaining `#6B7280` otherwise.
- Around line 841-854: Update the interactive span in the words each-block to be
keyboard-operable: prefer replacing it with a semantic button, or add tabindex
and Enter/Space key handling alongside the existing click behavior. Preserve the
active_editor_index check and word selection logic using wordSelected and
word.color.
- Around line 841-849: Update the word selection handler in the words loop so
selecting a word only assigns `wordSelected` and synchronizes the picker state
from that word’s existing color; remove the immediate `word.color` mutation
using the previous `wordColor`. Preserve color updates exclusively in the
picker-change flow.
🪄 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 Plus
Run ID: 6f896b2b-10a3-4529-ab90-a730778d722b
📒 Files selected for processing (6)
src/lib/components/VerseOnImage.sveltesrc/lib/icons/ArrowDownIcon.sveltesrc/lib/icons/ArrowUpIcon.sveltesrc/lib/icons/image/WordSelectIcon.sveltesrc/lib/icons/image/index.tssrc/lib/icons/index.ts
| {#each words as word, i} | ||
| <!-- svelte-ignore a11y_click_events_have_key_events --> | ||
| <span | ||
| onclick={() => { | ||
| if (active_editor_index === wordEditorIndex) { | ||
| word.color = `color:${wordColor};`; | ||
| wordSelected = word; | ||
| } | ||
| }} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not change a word merely by selecting it.
Line 846 applies the previous selection’s wordColor before assigning wordSelected. Selecting a previously customized word therefore destroys its existing color. Selection should sync the picker from the word; only a picker change should update the word.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/components/VerseOnImage.svelte` around lines 841 - 849, Update the
word selection handler in the words loop so selecting a word only assigns
`wordSelected` and synchronizes the picker state from that word’s existing
color; remove the immediate `word.color` mutation using the previous
`wordColor`. Preserve color updates exclusively in the picker-change flow.
FyreByrd
left a comment
There was a problem hiding this comment.
The functionality is all there and works just like the native app as far as I can tell. Well done.
However, there are a few UI issues...
Also, there are tabs that should not need to be vertically scrollable (e.g. the alignment tab) but are vertically scrollable. I think this is because the container for the tabs content has its height set by the tallest element. It looks like this may be a limitation of the DaisyUI classes being used. If you can fix it without much effort, great. If not, it's probably not a big deal.
|
How did you get those issues, particularly the vertical scrolling? I'm having trouble reproducing them. |
How did I get the issues? By running the code... I didn't do anything special to get those issues... |
|
What device/browser were you running it on? I can only get the first issue by making the width of the page really small, and I can't get the second issue. |
Brave Browser. I'll check the screen dimensions rq |
Ok. So the max height of my browser window is 638 pixels. The width of the container depends on the width of the image. I am using SAB Test PWA. It shouldn't matter which verse. I am using the first image, which has a width of 375px. Also, another weird bug, the image seems to disappear when the width transitions across 450px and does not reappear until a new image is selected... |
|
Do you have a screenshot of the vertical scrolling issue? |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/components/VerseOnImage.svelte (1)
326-331: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winRebuild
wordswhen the verse source changes.
wordsis reconstructed only once on mount fromselectedVerses.getCompositeText(). Since this can be reached by navigating in via/image, the selected verses can be swapped while the component remains mounted; add a reactively synced effect/reset path so the renderable word array and any attached editors stay in sync with the current verse.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/components/VerseOnImage.svelte` around lines 326 - 331, Update the VerseOnImage word-initialization flow so words is reactively rebuilt whenever selectedVerses.getCompositeText() changes, not only during mount. Ensure the reset also synchronizes or clears any attached editors so rendering and editing reflect the current verse after navigation or verse swaps.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/lib/components/VerseOnImage.svelte`:
- Around line 326-331: Update the VerseOnImage word-initialization flow so words
is reactively rebuilt whenever selectedVerses.getCompositeText() changes, not
only during mount. Ensure the reset also synchronizes or clears any attached
editors so rendering and editing reflect the current verse after navigation or
verse swaps.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 731cbfc3-53b9-4b9e-a3c7-b2845de8ad4f
📒 Files selected for processing (1)
src/lib/components/VerseOnImage.svelte
FyreByrd
left a comment
There was a problem hiding this comment.
I think it would be better UX to have the case change buttons located above the color picker, but that may be just me...
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/components/VerseOnImage.svelte`:
- Around line 1128-1130: Update the controls wrapper div adjacent to ColorPicker
in VerseOnImage to use Tailwind v4’s grow utility instead of the obsolete
flex-grow class, preserving the existing flex expansion and centered button
layout.
🪄 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 Plus
Run ID: ccc5c94a-d58c-4abf-a6a9-b01db80b5588
📒 Files selected for processing (1)
src/lib/components/VerseOnImage.svelte
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/components/DownloadSelector.svelte`:
- Around line 29-42: Update the download buttons in the DownloadSelector
component to use the theme-provided foreground/text color instead of the fixed
text-gray-700 class, ensuring labels and icons remain readable against
PopupBackgroundColor in dark and other supported themes.
🪄 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 Plus
Run ID: 3c0225f1-b653-45f8-84a0-154e81b87b0e
📒 Files selected for processing (2)
src/lib/components/DownloadSelector.sveltesrc/lib/components/VerseOnImage.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
- src/lib/components/VerseOnImage.svelte
| styling="padding:0; background-color:{$themeColors['PopupBackgroundColor']}; {positioningCSS}" | ||
| > | ||
| <div class="grid gap-2 m-2"> | ||
| <button | ||
| class="dy-btn dy-btn-sm flex items-center justify-center gap-2" | ||
| class="dy-btn dy-btn-sm flex items-center justify-center gap-2 border-none text-gray-700" | ||
| style="background-color:{$themeColors['PopupBackgroundColor']};" | ||
| onclick={() => downloadImage()} | ||
| > | ||
| <ImageIcon.Image /> | ||
| {$t['Text_On_Image_Save_Image']} | ||
| </button> | ||
| <button | ||
| class="dy-btn dy-btn-sm flex items-center justify-center gap-2" | ||
| class="dy-btn dy-btn-sm flex items-center justify-center gap-2 border-none text-gray-700" | ||
| style="background-color:{$themeColors['PopupBackgroundColor']};" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 4 '\bPopupBackgroundColor\b' src
rg -n -C 2 'text-gray-700' src/lib/componentsRepository: sillsdev/appbuilder-pwa
Length of output: 5430
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Candidate files mentioning themeColors or PopupBackgroundColor:"
rg -n -C 3 'themeColors|PopupBackgroundColor|TextAppearance|Text_On_Image_Save_(Image|Video)' src/lib src 2>/dev/null || true
echo
echo "DownloadSelector outline and relevant section:"
if [ -f src/lib/components/DownloadSelector.svelte ]; then
wc -l src/lib/components/DownloadSelector.svelte
sed -n '1,80p' src/lib/components/DownloadSelector.svelte | cat -n
fi
echo
echo "TextAppearanceSelector relevant section:"
if [ -f src/lib/components/TextAppearanceSelector.svelte ]; then
sed -n '1,220p' src/lib/components/TextAppearanceSelector.svelte | cat -n
fiRepository: sillsdev/appbuilder-pwa
Length of output: 50380
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate stores/data files:"
git ls-files 'src/lib/data/*' 'src/lib/data/**/*.svelte' 'src/lib/data/**/*.ts' 'src/lib/data/**/*.js' | sed -n '1,120p'
echo
echo "themeColors definitions/usages near stores:"
rg -n -C 5 "export .*themeColors|let themeColors|const themeColors|PopupBackgroundColor" src/lib/data src 2>/dev/null | sed -n '1,240p'
echo
echo "Theme selector implementation:"
sed -n '1,220p' src/lib/components/TextAppearanceSelector.svelte | cat -n
echo
echo "Data/config theme candidates:"
rg -n -C 3 "PopupBackgroundColor|Theme|theme|DialogBackgroundColor|DialogTextColor|TextColor" src/lib src/routes src/assets --glob '!src/**/node_modules/**' | sed -n '1,240p'Repository: sillsdev/appbuilder-pwa
Length of output: 35568
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "theme.ts:"
cat -n src/lib/data/stores/theme.ts | sed -n '1,120p'
echo
echo "Store index theme exports:"
cat -n src/lib/data/stores/index.ts | sed -n '1,180p'
echo
echo "Config/theme files only:"
git ls-files | rg '(^|/)(configuration|config|themes|style|styles|tailwind|vite-config|app\.config|theme)' | sed -n '1,200p'
echo
echo "TextAppearanceSelector remainder:"
sed -n '220,340p' src/lib/components/TextAppearanceSelector.svelte | cat -n
echo
echo "Style properties mentioning TextColor / Popup / Dialog:"
rg -n -C 2 "name: 'Text|PopupBackgroundColor|DialogTextColor|TextColor|TextAppear|DialogBackgroundColor|Dialog" src/lib/data/stores/theme.ts src 2>/dev/null | sed -n '1,240p'Repository: sillsdev/appbuilder-pwa
Length of output: 22839
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import json, re
# Read only configuration/theme data files as text and extract JSON-like objects where safe.
files = list(Path("test_data/data").glob("config.js")) if Path("test_data/data").exists() else []
print("config.js files:", files)
for p in files:
txt = p.read_text()
matches = re.findall(r'var\s+config\s*=\s*({[\s\S]*?})\s*;', txt)
for i, body in enumerate(matches):
print(f"\n--- {p} match {i+1} ---")
try:
obj = json.loads(body)
print("themes:", len(obj.get("themes", [])))
for t in obj.get("themes", []):
cs = next((c for c in t.get("colorSets", []) if c.get("type") == "main"), {})
bgs = cs.get("colors", {}).get("PopupBackgroundColor", "")
dialog = cs.get("colors", {}).get("DialogBackgroundColor", "")
bg = cs.get("colors", {}).get("BackgroundColor", "")
bg2 = t.get("backgroundColor", "")
print({
"name": t.get("name"),
"enabled": t.get("enabled"),
"PopupBackgroundColor": bgs,
"DialogBackgroundColor": dialog,
"BackgroundColor": bg,
"backgroundColor": bg2
})
except Exception as e:
print("parse error:", e)
print(body[:500])
PY
echo
echo "text-based PopupBackgroundColor assignments in test config:"
rg -n -C 4 "PopupBackgroundColor|PopupBackgroundColor" test_data/data src test 2>/dev/null | sed -n '1,220p'Repository: sillsdev/appbuilder-pwa
Length of output: 6647
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Relevant config.theme definitions:"
rg -n -C 8 '"name": "(Normal|Sage|Rose|Sepia|Charcoal|Dark)"|Popu|Dialog|TextColor' test_data/data/config.js 2>/dev/null | sed -n '1,260p'
echo
echo "Search for TextColor definitions in all tracked files:"
rg -n -C 5 '"TextColor": "text"|TextColor|TextAppearance|colors:' test_data/data/config.js 2>/dev/null | sed -n '1,300p'Repository: sillsdev/appbuilder-pwa
Length of output: 25492
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import json, re
from pathlib import Path
# Extract JSON from test_data/data/config.js using the config.js parser from the repo if available.
# The repository has src/lib/scripts/config-repository.ts; run it only if its parser is already usable.
text = Path("test_data/data/config.js").read_text()
m = re.search(r'var config = ({[\s\S]*?});', text)
if not m:
print("could not find config literal")
raise SystemExit(0)
data = json.loads(m.group(1))
theme_order = ['Normal', 'Sage', 'Rose', 'Sepia', 'Charcoal', 'Dark']
for theme in data.get("themes", []):
if theme.get("name") in theme_order:
cs = next((c for c in theme.get("colorSets", []) if c.get("type") == "main"), {})
colors = cs.get("colors", {})
bg = colors.get("PopupBackgroundColor")
text_color = colors.get("TextColor")
print({
"theme": theme["name"],
"PopupBackgroundColor": bg,
"TextColor": text_color
})
# Check TextAppearanceSelector code for text color override outside button body.
component = Path("src/lib/components/TextAppearanceSelector.svelte").read_text()
print("\nTextAppearanceSelector contains explicit button text-color style:", bool(re.search(r'style:color\s*=', component)))
print("TextAppearanceSelector contains monoIconColor:", "monoIconColor" in component)
PYRepository: sillsdev/appbuilder-pwa
Length of output: 191
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "config.js header/footer:"
wc -l test_data/data/config.js
sed -n '1,120p' test_data/data/config.js | cat -n
sed -n '1820,1920p' test_data/data/config.js | cat -n
echo
echo "Enable fields around all themes:"
rg -n -C 4 '"name": "(Normal|Sage|Rose|Sepia|Charcoal|Dark)"|enabled|PopupBackgroundColor|TextColor' test_data/data/config.js | sed -n '1,260p'Repository: sillsdev/appbuilder-pwa
Length of output: 21118
🌐 Web query:
Tailwind CSS text-gray-700 hex color RGB dark gray
💡 Result:
In Tailwind CSS, the color value for the gray-700 utility class depends on the version you are using, as Tailwind CSS transitioned to OKLCH color values in version 4 [1]. For Tailwind CSS v3 and earlier, the gray-700 color corresponds to the following values: - HEX: #374151 [2][3][4] - RGB: rgb(55, 65, 81) [2][3][4] For Tailwind CSS v4 and later, which uses the OKLCH color space by default for its color palette, the color definition is: - OKLCH: oklch(0.373 0.034 259.733) [5][1] When using Tailwind CSS, these values are typically handled automatically by the utility classes (e.g., text-gray-700), but if you need to reference them directly in your own CSS or when creating arbitrary values (like text-[#374151]), these are the standard identifiers [3][1]. Note that Tailwind offers multiple "gray" scales (such as Slate, Zinc, Neutral, and Stone), each with slightly different undertones, but the "gray" scale is the standard neutral option [4][6].
Citations:
- 1: https://hex2tailwind.com/colors/tailwind-palette
- 2: https://pantonetools.com/colors/tailwind/gray-700
- 3: https://hexcolor.io/tailwind/gray-700
- 4: https://frontend-hero.com/tailwind/gray
- 5: https://github.com/tailwindlabs/tailwindcss/blob/main/packages/tailwindcss/src/compat/colors.ts
- 6: https://whatisthecolor.net/tailwind.html
🌐 Web query:
Dark V8 theme enabled PopupBackgroundColor TextColor config.js test_data/data/config.js
💡 Result:
In technical applications, configuring dark mode themes through a config.js file typically involves defining color variables, often utilizing CSS custom properties (variables) to maintain consistency [1][2]. While specific implementation details vary by codebase, the general pattern for setting popup colors in a configuration object is shown below [1][3][4]. For applications that use a central config.js or theme configuration object, you define the popupBackgroundColor and textColor within your theme export or configuration block [1][3]. Example configuration pattern: // Example configuration in config.js or a theme definition file export const theme = { //... other theme settings popupBackgroundColor: "#121212", // Dark background color textColor: "#FFFFFF", // Light text color for contrast }; // If using CSS variables, it is often structured as::root { --popup-background-color: #121212; --text-color: #ffffff; } When applying these settings, ensure your UI components reference these values. For instance, in React or similar frameworks, you might pass these styles to a component's prop (e.g., overlayInnerStyle) [3][5]. If you are working with a specific framework or library, it is standard practice to: 1. Identify the central configuration file (e.g., config.js, theme.js) [6][7]. 2. Add or override the popupBackgroundColor and textColor properties [1][4]. 3. Ensure the CSS/styling engine is configured to consume these variables (often injected via CSS variables or a ThemeProvider) [1][2][8]. Note that for some advanced dark theme implementations, libraries may use auto-detection or color-mixing algorithms based on your primary colors rather than requiring explicit manual overrides for every component [8]. Always check the documentation specific to your project or library for how it registers theme changes [1].
Citations:
- 1: https://docs.acode.app/docs/helpers/theme-builder
- 2: https://deepwiki.com/portainer/portainer/2.3-frontend-architecture
- 3: https://git.logal.dev/LogalDeveloper/owncast/commit/ad188f2742c1e0b1da4582fc118aa9759b032fd4.diff
- 4: https://popup-js.readthedocs.io/en/latest/popup-styling/
- 5: https://git.logal.dev/LogalDeveloper/owncast/commit/ad188f2742c1e0b1da4582fc118aa9759b032fd4.patch
- 6: http://github.com/GeorgakilasLab/bMINTY
- 7: https://github.com/neurodesk/spinalcordtoolbox-webapp
- 8: https://docs.getpara.com/v3/react/guides/customization/modal-theming
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
path = Path("test_data/data/config.js")
text = path.read_text()
# Find theme definitions by name and capture enabled=true enabled:false blocks until the next theme or the styles array.
def find_theme(name):
start = re.search(r'\{\s*\n\s*"name":\s*"{}"'.format(re.escape(name)), text)
if not start:
return None
search = text[start.start():]
end = re.search(r'\n\s*\{\s*\n\s*"name":\s*"|^\s*"styles":\s*\[\s*\{', search, flags=re.M)
if end:
block = search[:end.start()]
else:
block = search
m = re.search(r'"PopupBackgroundColor":\s*"([^"]+)"\s*,', block)
return {
"enabled": re.search(r'"enabled":\s*(true|false)\s*,', block),
"PopupBackgroundColor": m.group(1) if m else None,
"textContent": bool(re.search(r'\bPopupBackgroundColor\b|TextColor\b', block[:3000]))
}
print("enabled normal text:", find_theme("Normal"))
print("enabled sepia text:", find_theme("Sepia"))
print("enabled dark text:", find_theme("Dark"))
# Extract theme order from theme.ts and compare against enabled text.
tstxt = Path("src/lib/data/stores/theme.ts").read_text()
m = re.search(r"const themeOrder: string\[\] = (\[[^\]]+\])", tstxt)
print("themeOrder source:", m.group(1) if m else None)
print("disabled text includes Dark:", '"Dark"' in text[re.search(r'"name":\s*"Dark"', text).span()[1] if re.search(r'"name":\s*"Dark"', text) else 0:])
PY
echo
echo "All Dark theme header with enabled:"
m=$(rg -n '"name":\s*"Dark"' test_data/data/config.js | head -1)
line=${m%%:*}
sed -n "$((line-3)),$((line+25))p" test_data/data/config.js | cat -nRepository: sillsdev/appbuilder-pwa
Length of output: 316
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
path = Path("test_data/data/config.js")
text = path.read_text()
def extract_enabled_popup(theme_name):
idx = text.find(f'"name": "{theme_name}"')
if idx == -1:
return None
block = text[idx:text.find('"name": "', idx + 1) if text.find('"name": "', idx + 1) != -1 else idx + 4000]
enabled_match = re.search(r'"enabled":\s*(true|false)\s*,', block[:500])
popup_match = re.search(r'"PopupBackgroundColor":\s*"([^"]+)"', block[:5000])
return {
"enabled": enabled_match.group(1) if enabled_match else "missing",
"PopupBackgroundColor": popup_match.group(1) if popup_match else "missing"
}
for theme_name in ["Normal", "Sepia", "Dark", "Charcoal", "Rose", "Sage"]:
print(extract_enabled_popup(theme_name))
tstxt = Path("src/lib/data/stores/theme.ts").read_text()
m = re.search(r"const themeOrder: string\[\] = (\[[^\]]+\])", tstxt)
print("themeOrder source:", m.group(1))
PY
echo
sed -n '412,422p' test_data/data/config.js | cat -nRepository: sillsdev/appbuilder-pwa
Length of output: 688
Check Dark theme contrast before keeping text-gray-700.
The Dark theme sets PopupBackgroundColor to #202020 and has TextColor: white, but these download buttons still inherit Tailwind’s gray text. Use the theme-provided foreground color for the button labels/icons, or document that this contrast is intentional for all supported themes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/components/DownloadSelector.svelte` around lines 29 - 42, Update the
download buttons in the DownloadSelector component to use the theme-provided
foreground/text color instead of the fixed text-gray-700 class, ensuring labels
and icons remain readable against PopupBackgroundColor in dark and other
supported themes.
There was a problem hiding this comment.
The native app seems to just have a hardcoded text color, so it does not have good contrast in the dark theme. I don't know whether this is something we want to change for the PWA.
There was a problem hiding this comment.
@TheNonPirate, understood. The contrast issue is valid in the PWA, but changing it would create behavior that differs from the native app.
I will leave this finding unresolved until the team decides whether the PWA should prioritize theme contrast or native-app visual parity.
You are interacting with an AI system.


Resolves #1011. This PR implements the Highlight Words tab in Verse on Image, which allows individual words to be colored and their case to be set. It also fixes an issue with Verse on Image where the carousel of tabs can be scrolled using the arrow keys, but it does not fully switch tabs.
Summary by CodeRabbit
New Features
Refinements
Bug Fixes