Upgrade remaining components to Svelte 5 syntax#962
Conversation
📝 WalkthroughWalkthroughThis PR completes the Svelte 5 migration by updating 100+ components to use Svelte 5 runes ( ChangesSvelte 5 Migration: Runes, Props API, and Asset/Navigation Helpers
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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/FontSelector.svelte`:
- Around line 13-15: The modal opens with a stale selectedFont value; update
selectedFont from the currentFont store/value before showing the dialog. In the
export function showModal() (and the equivalent open/submit path around the same
modal usage), read the latest currentFont (or $currentFont) and assign it to
selectedFont immediately prior to calling modal.showModal(), ensuring the
selection reflects the currentFont when the modal opens.
In `@src/lib/components/HistoryCard.svelte`:
- Line 55: The inline style on the clickable div using "text-decorationnone;" is
invalid and being ignored; update the div with the correct CSS declaration
(e.g., "text-decoration: none;") or remove the style attribute entirely if not
needed, by editing the element that contains onclick={onHistoryClick} to use a
valid style string or class name so the intended no-text-decoration behavior is
applied.
In `@src/lib/components/StackView.svelte`:
- Around line 118-121: In StackView.svelte change the onclick handler so
insideClick receives the actual clicked element instead of the outer container:
keep e.stopPropagation(), then compute const clicked = (e.target as
HTMLElement).closest('a') ?? e.target and call insideClick(clicked); this
ensures insideClick (used to check data-start-ref or class ref-link) sees the
generated <a> even when wrapped by tooltips or other wrappers.
In `@src/routes/plans/`[id]/settings/+page.svelte:
- Around line 21-24: Compute a single guarded planId from data.planConfig (e.g.
const planId = data.planConfig?.id) and use that in backNavigation and the start
action instead of accessing data.planConfig directly; if planId is missing do an
early return (or show a fallback) to avoid routing to `/plans/undefined` or
throwing on click—update the backNavigation function (which calls
gotoRoute(`/plans/${id}`)) and the other handler around lines 52-54 to use this
guarded planId and return immediately when it’s falsy.
🪄 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: 27d0830f-4f46-4c56-9852-9c0442931502
📒 Files selected for processing (93)
eslint.config.jssrc/lib/components/AudioPlaybackSpeed.sveltesrc/lib/components/ContentCarousel.sveltesrc/lib/components/ContentGrid.sveltesrc/lib/components/ContentHeading.sveltesrc/lib/components/ContentSingle.sveltesrc/lib/components/FontList.sveltesrc/lib/components/FontSelector.sveltesrc/lib/components/HistoryCard.sveltesrc/lib/components/HtmlBookView.sveltesrc/lib/components/IconCard.sveltesrc/lib/components/PlayButton.sveltesrc/lib/components/ScrolledContent.sveltesrc/lib/components/Sidebar.sveltesrc/lib/components/Slider.sveltesrc/lib/components/StackView.sveltesrc/lib/components/TextAppearanceSelector.sveltesrc/lib/data/plansData.tssrc/lib/icons/AboutIcon.sveltesrc/lib/icons/AccountIcon.sveltesrc/lib/icons/ArrowBackIcon.sveltesrc/lib/icons/ArrowForwardIcon.sveltesrc/lib/icons/BibleIcon.sveltesrc/lib/icons/BookmarkIcon.sveltesrc/lib/icons/BookmarkOutlineIcon.sveltesrc/lib/icons/CalendarMonthIcon.sveltesrc/lib/icons/CheckIcon.sveltesrc/lib/icons/CheckboxIcon.sveltesrc/lib/icons/CheckboxOutlineIcon.sveltesrc/lib/icons/ChevronIcon.sveltesrc/lib/icons/CopyContentIcon.sveltesrc/lib/icons/DeleteIcon.sveltesrc/lib/icons/DeleteSweepIcon.sveltesrc/lib/icons/DropdownIcon.sveltesrc/lib/icons/EditIcon.sveltesrc/lib/icons/HamburgerIcon.sveltesrc/lib/icons/HighlightIcon.sveltesrc/lib/icons/HistoryIcon.sveltesrc/lib/icons/HomeIcon.sveltesrc/lib/icons/InfoIcon.sveltesrc/lib/icons/LaunchIcon.sveltesrc/lib/icons/LineSpacingIcon.sveltesrc/lib/icons/MoreVertIcon.sveltesrc/lib/icons/NoteIcon.sveltesrc/lib/icons/SearchIcon.sveltesrc/lib/icons/SettingsIcon.sveltesrc/lib/icons/ShareIcon.sveltesrc/lib/icons/SideBySideIcon.sveltesrc/lib/icons/SinglePaneIcon.sveltesrc/lib/icons/SortIcon.sveltesrc/lib/icons/TextAppearanceIcon.sveltesrc/lib/icons/TriangleLeftIcon.sveltesrc/lib/icons/TriangleRightIcon.sveltesrc/lib/icons/VerseByVerseIcon.sveltesrc/lib/icons/audio/PauseIcon.sveltesrc/lib/icons/audio/PauseOutlineCircleIcon.sveltesrc/lib/icons/audio/PlayFillCircleIcon.sveltesrc/lib/icons/audio/PlayIcon.sveltesrc/lib/icons/audio/PlayOutlineCircleIcon.sveltesrc/lib/icons/audio/PlayRepeatIcon.sveltesrc/lib/icons/audio/RepeatIcon.sveltesrc/lib/icons/image/BlurIcon.sveltesrc/lib/icons/image/BrightnessIcon.sveltesrc/lib/icons/image/ContrastIcon.sveltesrc/lib/icons/image/FontChoiceIcon.sveltesrc/lib/icons/image/FormatAlignCenterIcon.sveltesrc/lib/icons/image/FormatAlignLeftIcon.sveltesrc/lib/icons/image/FormatAlignRightIcon.sveltesrc/lib/icons/image/FormatBoldIcon.sveltesrc/lib/icons/image/FormatColorFillIcon.sveltesrc/lib/icons/image/FormatItalicIcon.sveltesrc/lib/icons/image/FormatLineSpacingIcon.sveltesrc/lib/icons/image/ImageSingleIcon.sveltesrc/lib/icons/image/ImagesIcon.sveltesrc/lib/icons/image/LetterSpacingIcon.sveltesrc/lib/icons/image/OpacityIcon.sveltesrc/lib/icons/image/ReferenceIcon.sveltesrc/lib/icons/image/ResizeArrowsIcon.sveltesrc/lib/icons/image/SaturationIcon.sveltesrc/lib/icons/image/TextGlowIcon.sveltesrc/lib/icons/image/TextShadowIcon.sveltesrc/lib/icons/image/TextShadowNoneIcon.sveltesrc/lib/icons/image/TextWidthIcon.sveltesrc/lib/navigate/index.tssrc/lib/video/index.tssrc/routes/+page.sveltesrc/routes/bookmarks/+page.sveltesrc/routes/contents/[id]/+page.sveltesrc/routes/dev/icons/+page.sveltesrc/routes/lexicon/+page.sveltesrc/routes/notes/+page.sveltesrc/routes/plans/[id]/settings/+page.sveltesrc/routes/text/+page.svelte
💤 Files with no reviewable changes (3)
- src/lib/data/plansData.ts
- src/lib/components/ScrolledContent.svelte
- src/routes/lexicon/+page.svelte
| onclick={(e) => { | ||
| e.stopPropagation(); | ||
| insideClick(e.currentTarget); | ||
| }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify mismatch between insideClick expectations and call-site argument.
rg -n -C2 "function insideClick|data-start-ref|classList.contains\\('ref-link'\\)|insideClick\\(e\\.currentTarget\\)" src/lib/components/StackView.svelteRepository: sillsdev/appbuilder-pwa
Length of output: 823
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/lib/components/StackView.svelte"
echo "== insideClick implementation =="
nl -ba "$FILE" | sed -n '40,110p'
echo
echo "== onclick callsite + surrounding markup =="
nl -ba "$FILE" | sed -n '105,150p'Repository: sillsdev/appbuilder-pwa
Length of output: 161
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/lib/components/StackView.svelte"
echo "== insideClick implementation =="
cat -n "$FILE" | sed -n '40,110p'
echo
echo "== onclick callsite + surrounding markup =="
cat -n "$FILE" | sed -n '105,150p'Repository: sillsdev/appbuilder-pwa
Length of output: 4734
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "data-start-ref|data-end-ref|ref-link" src/lib/components/StackView.svelteRepository: sillsdev/appbuilder-pwa
Length of output: 425
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "data-start-ref" src/lib/components src/lib -S || true
rg -n "ref-link" src/lib/components src/lib -S || trueRepository: sillsdev/appbuilder-pwa
Length of output: 2025
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show how data-start-ref and ref-link are applied
echo "== scripture-reference-utils.ts =="
cat -n src/lib/scripts/scripture-reference-utils.ts | sed -n '1,130p'
echo
echo "== milestoneLinks.ts =="
cat -n src/lib/scripts/milestoneLinks.ts | sed -n '130,220p'
echo
echo "== ScriptureViewSofria.svelte click logic uses e.target =="
cat -n src/lib/components/ScriptureViewSofria.svelte | sed -n '620,700p'
echo
echo "== ScriptureViewSofria.svelte later ref-link handling =="
cat -n src/lib/components/ScriptureViewSofria.svelte | sed -n '1400,1460p'Repository: sillsdev/appbuilder-pwa
Length of output: 14638
🏁 Script executed:
#!/bin/bash
# Verify mismatch between insideClick expectations and call-site argument.
rg -n -C2 "function insideClick|data-start-ref|classList.contains\\('ref-link'\\)|insideClick\\(e\\.currentTarget\\)" src/lib/components/StackView.svelteRepository: sillsdev/appbuilder-pwa
Length of output: 823
🏁 Script executed:
#!/bin/bash
# Verify mismatch between insideClick expectations and call-site argument.
rg -n -C2 "function insideClick|data-start-ref|classList.contains\\('ref-link'\\)|insideClick\\(e\\.currentTarget\\)" src/lib/components/StackView.svelteRepository: sillsdev/appbuilder-pwa
Length of output: 823
🏁 Script executed:
#!/bin/bash
rg -n -C2 "function insideClick|data-start-ref|classList.contains\\('ref-link'\\)|insideClick\\(e\\.currentTarget\\)" src/lib/components/StackView.svelteRepository: sillsdev/appbuilder-pwa
Length of output: 823
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find how generated anchors are inserted into the HTML that StackView renders
rg -n "generateAnchor\\(" src/lib/scripts/scripture-reference-utils.ts
rg -n "getReferenceLinkHtml\\(" src/lib/scripts/milestoneLinks.ts
rg -n "generateHTML(Main)?\\(" src/lib/scripts/scripture-reference-utils.ts | headRepository: sillsdev/appbuilder-pwa
Length of output: 731
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/lib/components/StackView.svelte"
rg -n "handleHeaderLinkPressed|footnotes\\.(push|reset)|\\{`@html` item\\}|\\$footnotes" "$FILE"
rg -n "function handleHeaderLinkPressed|async function handleHeaderLinkPressed|handleHeaderLinkPressed\\(" "$FILE"Repository: sillsdev/appbuilder-pwa
Length of output: 114
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/lib/components/StackView.svelte"
rg -n 'handleHeaderLinkPressed|footnotes\.(push|reset)|\{`@html` item\}|\$footnotes' "$FILE" || true
rg -n 'function handleHeaderLinkPressed|async function handleHeaderLinkPressed|handleHeaderLinkPressed\(' "$FILE" || trueRepository: sillsdev/appbuilder-pwa
Length of output: 576
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/lib/components/StackView.svelte"
cat -n "$FILE" | sed -n '1,220p'Repository: sillsdev/appbuilder-pwa
Length of output: 5986
🏁 Script executed:
#!/bin/bash
rg -n -C2 "function insideClick|data-start-ref|classList.contains\\('ref-link'\\)|insideClick\\(e\\.currentTarget\\)" src/lib/components/StackView.svelteRepository: sillsdev/appbuilder-pwa
Length of output: 823
🏁 Script executed:
#!/bin/bash
rg -n -C2 "function insideClick|data-start-ref|classList.contains\\('ref-link'\\)|insideClick\\(e\\.currentTarget\\)" src/lib/components/StackView.svelteRepository: sillsdev/appbuilder-pwa
Length of output: 823
🏁 Script executed:
#!/bin/bash
# Ensure callsite no longer passes e.currentTarget and insideClick still checks expected markers.
rg -n "insideClick\\(e\\.currentTarget\\)" src/lib/components/StackView.svelte
rg -n "insideClick\\(target\\: HTMLElement\\)|data-start-ref|classList\\.contains\\('ref-link'\\)" src/lib/components/StackView.svelteRepository: sillsdev/appbuilder-pwa
Length of output: 305
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "async function insideClick\\(target: HTMLElement\\)|data-start-ref|classList\\.contains\\('ref-link'\\)|insideClick\\(e\\.currentTarget\\)" src/lib/components/StackView.svelte
rg -n "setAttribute\\('data-start-ref'|classList\\.add\\('ref-link'\\)" src/lib/scripts/scripture-reference-utils.ts src/lib/scripts/milestoneLinks.tsRepository: sillsdev/appbuilder-pwa
Length of output: 564
Fix insideClick argument to use the clicked element (not e.currentTarget).
insideClick(target) only works when target has data-start-ref or the ref-link class. data-start-ref is set on the generated <a> by scripture-reference-utils.ts, and ref-link is added to the <a> by milestoneLinks.ts, but StackView.svelte currently calls insideClick(e.currentTarget) (the outer .footnote div), so those branches won’t match.
💡 Proposed fix
onclick={(e) => {
e.stopPropagation();
- insideClick(e.currentTarget);
+ insideClick(e.target as HTMLElement);
}}When the ref-link <a> is wrapped (e.g., tooltip wrapper), prefer passing e.target.closest('a') ?? e.target so clicks on the wrapper still hit the <a>.
📝 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.
| onclick={(e) => { | |
| e.stopPropagation(); | |
| insideClick(e.currentTarget); | |
| }} | |
| onclick={(e) => { | |
| e.stopPropagation(); | |
| insideClick(e.target as HTMLElement); | |
| }} |
🤖 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/StackView.svelte` around lines 118 - 121, In
StackView.svelte change the onclick handler so insideClick receives the actual
clicked element instead of the outer container: keep e.stopPropagation(), then
compute const clicked = (e.target as HTMLElement).closest('a') ?? e.target and
call insideClick(clicked); this ensures insideClick (used to check
data-start-ref or class ref-link) sees the generated <a> even when wrapped by
tooltips or other wrappers.
| function backNavigation() { | ||
| const id = $page.data.planConfig.id; | ||
| const id = data.planConfig?.id; | ||
| gotoRoute(`/plans/${id}`); | ||
| } |
There was a problem hiding this comment.
Guard planConfig before routing and start action.
Line 22 and Line 53 handle planConfig inconsistently. If planConfig is missing, this can route to /plans/undefined or throw on click. Add a single guarded planId path and early-return fallback.
Suggested fix
<script lang="ts">
@@
- function backNavigation() {
- const id = data.planConfig?.id;
- gotoRoute(`/plans/${id}`);
- }
+ const planId = $derived(data.planConfig?.id);
+
+ function backNavigation() {
+ if (!planId) {
+ gotoRoute('/plans');
+ return;
+ }
+ gotoRoute(`/plans/${planId}`);
+ }
@@
- onclick={async function () {
- await startPlan(data.planConfig!.id);
- }}
+ onclick={async function () {
+ if (!planId) return;
+ await startPlan(planId);
+ }}
>Also applies to: 52-54
🤖 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/routes/plans/`[id]/settings/+page.svelte around lines 21 - 24, Compute a
single guarded planId from data.planConfig (e.g. const planId =
data.planConfig?.id) and use that in backNavigation and the start action instead
of accessing data.planConfig directly; if planId is missing do an early return
(or show a fallback) to avoid routing to `/plans/undefined` or throwing on
click—update the backNavigation function (which calls gotoRoute(`/plans/${id}`))
and the other handler around lines 52-54 to use this guarded planId and return
immediately when it’s falsy.
Closes #819, #890
Summary by CodeRabbit
Refactor
Chores