feat(reader): wire double-page spread setting into web reader

The double_page_spread checkbox in the reader settings panel was inert:
it had no Alpine binding, no apply logic, and no persistence. Default
was also inconsistent (false in settings-manager, absent from server
defaults).

- Add doublePageSpread state to the reader Alpine component, loaded
  from saved settings (default true)
- Add applyDoublePageSpread() which sets the renderer's 'spread'
  attribute to auto/none and persists the setting via saveSettings
- Apply the spread attribute during fixed-layout renderer init
- Bind the settings checkbox with x-model and @change
- Add double_page_spread: true to ReaderService server defaults so
  new users get the same starting value the client expects
- Also improve the PDF pan/select toolbar button: distinct smart-
  select vs pan icons, highlighted state while pan mode is active,
  and dynamic tooltips/aria-labels explaining each mode
This commit is contained in:
john-okeefe committed 2026-08-14 08:17:25 -04:00
1 parent 04e2a069d6
commit f5d9578375
5 files changed
+29 -12

No files matched your search

+8 -8
View File
@@ -189,7 +189,7 @@ func ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</h1><button @click=\"toggleSettings()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Settings\">⚙️</button></div></div><!-- Bottom bar --><div id=\"reader-bottombar\" class=\"fixed bottom-0 left-0 right-0 bg-opacity-95 backdrop-blur border-t z-40 pb-[env(safe-area-inset-bottom)]\" style=\"background-color: var(--bg-primary);\"><div class=\"flex items-center px-1.5 py-1.5 gap-0.5 sm:px-2 sm:py-2 sm:gap-1\"><!-- Left navigation --><button @click=\"goLeft()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Go Left\" aria-label=\"Go left\"><svg class=\"reader-icon\" width=\"24\" height=\"24\" aria-hidden=\"true\"><path d=\"M 15 6 L 9 12 L 15 18\"></path></svg></button><!-- Progress slider --><input id=\"progress-slider\" type=\"range\" min=\"0\" max=\"1\" step=\"any\" list=\"tick-marks\" @input=\"goToFraction($event.target.value)\" class=\"grow\"> <datalist id=\"tick-marks\"></datalist><!-- Right navigation --><button @click=\"goRight()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Go Right\" aria-label=\"Go right\"><svg class=\"reader-icon\" width=\"24\" height=\"24\" aria-hidden=\"true\"><path d=\"M 9 6 L 15 12 L 9 18\"></path></svg></button><!-- Separator --><div class=\"w-px h-6 bg-gray-600 mx-1\"></div><!-- Zoom controls (fixed-layout only) --><template x-if=\"isFixedLayout\"><div class=\"flex items-center gap-1\"><!-- Zoom out --><button @click=\"zoomOut()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Zoom Out\" aria-label=\"Zoom out\"><svg class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M 5 10 L 15 10\"></path></svg></button><!-- Zoom percentage --><button @click=\"resetZoom()\" class=\"text-xs px-1 rounded-lg hover:bg-gray-700 min-w-[3rem]\" title=\"Reset Zoom\" aria-label=\"Reset zoom\"><span x-text=\"zoomPercent + '%'\">100%</span></button><!-- Zoom in --><button @click=\"zoomIn()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Zoom In\" aria-label=\"Zoom in\"><svg class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M 10 5 L 10 15 M 5 10 L 15 10\"></path></svg></button><!-- Magnifier --><button @click=\"toggleMagnifier()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Magnifier\" aria-label=\"Toggle magnifier\"><svg class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><circle cx=\"9\" cy=\"9\" r=\"5\"></circle> <path d=\"M 13 13 L 18 18\"></path></svg></button><!-- Pan/Select mode (PDF only) --><button x-show=\"isPDF\" @click=\"toggleInteractionMode()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Pan/Select Mode\" aria-label=\"Toggle pan/select mode\"><svg class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M 5 3 v 12 M 5 15 l -2 2 M 5 15 l 2 2 M 5 3 l 3 3\"></path></svg></button></div></template><!-- Separator --><div class=\"w-px h-6 bg-gray-600 mx-1\"></div><!-- Progress display --><div id=\"progress-display\" @click=\"cycleProgressMode()\" :title=\"progressTooltip()\" class=\"text-sm min-w-[4rem] max-w-[5rem] sm:max-w-none text-center cursor-pointer truncate whitespace-nowrap overflow-hidden\"><span class=\"hidden sm:inline\" x-text=\"progressLabel\"></span><span x-text=\"progressMain\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</h1><button @click=\"toggleSettings()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Settings\">⚙️</button></div></div><!-- Bottom bar --><div id=\"reader-bottombar\" class=\"fixed bottom-0 left-0 right-0 bg-opacity-95 backdrop-blur border-t z-40 pb-[env(safe-area-inset-bottom)]\" style=\"background-color: var(--bg-primary);\"><div class=\"flex items-center px-1.5 py-1.5 gap-0.5 sm:px-2 sm:py-2 sm:gap-1\"><!-- Left navigation --><button @click=\"goLeft()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Go Left\" aria-label=\"Go left\"><svg class=\"reader-icon\" width=\"24\" height=\"24\" aria-hidden=\"true\"><path d=\"M 15 6 L 9 12 L 15 18\"></path></svg></button><!-- Progress slider --><input id=\"progress-slider\" type=\"range\" min=\"0\" max=\"1\" step=\"any\" list=\"tick-marks\" @input=\"goToFraction($event.target.value)\" class=\"grow\"> <datalist id=\"tick-marks\"></datalist><!-- Right navigation --><button @click=\"goRight()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Go Right\" aria-label=\"Go right\"><svg class=\"reader-icon\" width=\"24\" height=\"24\" aria-hidden=\"true\"><path d=\"M 9 6 L 15 12 L 9 18\"></path></svg></button><!-- Separator --><div class=\"w-px h-6 bg-gray-600 mx-1\"></div><!-- Zoom controls (fixed-layout only) --><template x-if=\"isFixedLayout\"><div class=\"flex items-center gap-1\"><!-- Zoom out --><button @click=\"zoomOut()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Zoom Out\" aria-label=\"Zoom out\"><svg class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M 5 10 L 15 10\"></path></svg></button><!-- Zoom percentage --><button @click=\"resetZoom()\" class=\"text-xs px-1 rounded-lg hover:bg-gray-700 min-w-[3rem]\" title=\"Reset Zoom\" aria-label=\"Reset zoom\"><span x-text=\"zoomPercent + '%'\">100%</span></button><!-- Zoom in --><button @click=\"zoomIn()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Zoom In\" aria-label=\"Zoom in\"><svg class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M 10 5 L 10 15 M 5 10 L 15 10\"></path></svg></button><!-- Magnifier --><button @click=\"toggleMagnifier()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" title=\"Magnifier\" aria-label=\"Toggle magnifier\"><svg class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><circle cx=\"9\" cy=\"9\" r=\"5\"></circle> <path d=\"M 13 13 L 18 18\"></path></svg></button><!-- Pan/Select mode (PDF only) --><button x-show=\"isPDF\" @click=\"toggleInteractionMode()\" class=\"p-1.5 sm:p-2 rounded-lg hover:bg-gray-700\" :class=\"interactionMode === 'pan' ? 'bg-blue-600 hover:bg-blue-700' : ''\" :title=\"interactionMode === 'pan' ? 'Pan mode: drag anywhere to move. Click for Smart select.' : 'Smart select: drag text to select, drag blank to pan (Shift = force pan). Click for Pan mode.'\" :aria-label=\"interactionMode === 'pan' ? 'Pan mode' : 'Smart select mode'\"><!-- Smart select (default): text cursor --><svg x-show=\"interactionMode !== 'pan'\" class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M 10 3 L 10 17 M 7 5 L 10 4 L 13 5 M 7 15 L 10 16 L 13 15\"></path></svg><!-- Force pan: move arrow --><svg x-show=\"interactionMode === 'pan'\" class=\"reader-icon\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M 5 3 v 12 M 5 15 l -2 2 M 5 15 l 2 2 M 5 3 l 3 3\"></path></svg></button></div></template><!-- Separator --><div class=\"w-px h-6 bg-gray-600 mx-1\"></div><!-- Progress display --><div id=\"progress-display\" @click=\"cycleProgressMode()\" :title=\"progressTooltip()\" class=\"text-sm min-w-[4rem] max-w-[5rem] sm:max-w-none text-center cursor-pointer truncate whitespace-nowrap overflow-hidden\"><span class=\"hidden sm:inline\" x-text=\"progressLabel\"></span><span x-text=\"progressMain\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -198,7 +198,7 @@ func ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress)
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.0f%% · Page %d/%d", progress.Percentage, progress.CurrentPage, metadata.EstimatedPages))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 170, Col: 113}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 175, Col: 113}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
@@ -208,7 +208,7 @@ func ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress)
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.0f%%", progress.Percentage))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 172, Col: 52}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 177, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
@@ -219,7 +219,7 @@ func ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress)
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d/%d", progress.CurrentPage, progress.TotalPages))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 175, Col: 72}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 180, Col: 72}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
@@ -255,7 +255,7 @@ func ReaderSettingsPanel() templ.Component {
templ_7745c5c3_Var10 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<div class=\"dockable-panel\" data-panel=\"settings\" data-side=\"left\" x-ref=\"settingsPanel\"><div class=\"panel-header flex items-center justify-between p-3 cursor-pointer\" @click=\"toggleWindowShade($refs.settingsPanel)\"><h3 class=\"panel-title font-semibold\">⚙️ Settings</h3><div class=\"panel-controls flex items-center gap-2\"><button class=\"window-shade-toggle\">─</button></div></div><div class=\"panel-content p-4 overflow-y-auto\"><!-- Display settings --><div class=\"mb-6\"><h3 class=\"font-semibold mb-2\">Display</h3><label class=\"block mb-2\">Chrome Behavior <select name=\"chrome_behavior\" class=\"w-full mt-1 px-3 py-2 rounded border\"><option value=\"auto-hide\">Auto Hide</option> <option value=\"always-visible\">Always Visible</option> <option value=\"hide-on-scroll\">Hide on Scroll</option></select></label> <label class=\"block mb-2\">Progress Mode <select name=\"progress_mode\" x-model=\"progressMode\" @change=\"applyProgressMode()\" class=\"w-full mt-1 px-3 py-2 rounded border\"><option value=\"pages\">Pages</option> <option value=\"chapter\">Chapter</option> <option value=\"percentage\">Percentage</option> <option value=\"time-left\">Time Left</option></select></label></div><!-- Reading Theme (reflowable only) --><div class=\"mb-6\" x-show=\"!isFixedLayout\"><div class=\"flex items-center justify-between mb-2\"><h3 class=\"font-semibold\">Reading Theme</h3><button @click=\"toggleReadingMode()\" class=\"relative w-14 h-7 rounded-full transition-colors duration-200 flex items-center px-1\" :class=\"readingMode === 'dark' ? 'bg-blue-500' : 'bg-gray-400'\" type=\"button\" :aria-label=\"readingMode === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'\"><span class=\"absolute top-0.5 left-0.5 w-6 h-6 bg-white rounded-full shadow transition-transform duration-200 flex items-center justify-center text-xs\" :class=\"readingMode === 'dark' ? 'translate-x-7' : 'translate-x-0'\" x-text=\"readingMode === 'dark' ? '🌙' : '☀️'\">☀️</span></button></div><label class=\"block mb-2\"><select name=\"reading_theme\" x-model=\"readingTheme\" @change=\"applyTheme()\" class=\"w-full mt-1 px-3 py-2 rounded border\"><optgroup label=\"📖 Classic Reading\"><option value=\"light\">Light</option> <option value=\"paper\">Paper</option> <option value=\"sepia\">Sepia</option> <option value=\"parchment\">Parchment</option> <option value=\"warm\">Warm</option> <option value=\"candlelight\">Candlelight</option></optgroup> <optgroup label=\"🌤️ Sky & Atmosphere\"><option value=\"azure\">Azure</option> <option value=\"sky\">Sky</option> <option value=\"arctic\">Arctic</option> <option value=\"frost\">Frost</option></optgroup> <optgroup label=\"🌅 Sunset & Warmth\"><option value=\"dusk\">Dusk</option> <option value=\"sunset\">Sunset</option> <option value=\"twilight\">Twilight</option></optgroup> <optgroup label=\"🌲 Nature & Earth\"><option value=\"forest\">Forest</option> <option value=\"moss\">Moss</option> <option value=\"slate\">Slate</option></optgroup> <optgroup label=\"⚡ High Performance\"><option value=\"oled\">OLED</option> <option value=\"solarized\">Solarized</option></optgroup></select></label></div><!-- Typography (reflowable only) --><div class=\"mb-6\" x-show=\"!isFixedLayout\"><h3 class=\"font-semibold mb-2\">Typography</h3><label class=\"block mb-2\">Reading Font <select name=\"reading_font\" x-model=\"readingFont\" @change=\"applyFont()\" class=\"w-full mt-1 px-3 py-2 rounded border\"><option value=\"literata\">Literata (Default)</option> <option value=\"crimson\">Crimson Text</option> <option value=\"source-serif\">Source Serif 4</option> <option value=\"eb-garamond\">EB Garamond</option> <option value=\"libertinus\">Libertinus Serif</option> <option value=\"noto-serif\">Noto Serif</option> <option value=\"charis-sil\">Charis SIL</option> <option value=\"ibm-plex\">IBM Plex Serif</option></select></label> <label class=\"block mb-2\">Font Size <input type=\"range\" name=\"font_size\" x-model.number=\"fontSize\" @change=\"applyFont()\" min=\"10\" max=\"40\" class=\"w-full\"> <span class=\"text-xs\" x-text=\"fontSize + 'px'\" style=\"color: var(--text-secondary)\">18px</span></label> <label class=\"block mb-2\">Line Height <input type=\"range\" name=\"line_height\" x-model.number=\"lineHeight\" @change=\"applyFont()\" min=\"0.8\" max=\"3.0\" step=\"0.1\" class=\"w-full\"> <span class=\"text-xs\" x-text=\"lineHeight\" style=\"color: var(--text-secondary)\">1.6</span></label></div><!-- Navigation --><div class=\"mb-6\" x-show=\"isFixedLayout\"><h3 class=\"font-semibold mb-2\">Navigation</h3><label class=\"flex items-center mb-2\"><input type=\"checkbox\" name=\"double_page_spread\" class=\"mr-2\"> Double Page Spread</label></div><div class=\"flex gap-2 mt-6\"><button @click=\"restoreDefaults()\" class=\"flex-1 py-2 border rounded hover:opacity-80\" style=\"border-color: var(--border);\">Restore DefLine truncated
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<div class=\"dockable-panel\" data-panel=\"settings\" data-side=\"left\" x-ref=\"settingsPanel\"><div class=\"panel-header flex items-center justify-between p-3 cursor-pointer\" @click=\"toggleWindowShade($refs.settingsPanel)\"><h3 class=\"panel-title font-semibold\">⚙️ Settings</h3><div class=\"panel-controls flex items-center gap-2\"><button class=\"window-shade-toggle\">─</button></div></div><div class=\"panel-content p-4 overflow-y-auto\"><!-- Display settings --><div class=\"mb-6\"><h3 class=\"font-semibold mb-2\">Display</h3><label class=\"block mb-2\">Chrome Behavior <select name=\"chrome_behavior\" class=\"w-full mt-1 px-3 py-2 rounded border\"><option value=\"auto-hide\">Auto Hide</option> <option value=\"always-visible\">Always Visible</option> <option value=\"hide-on-scroll\">Hide on Scroll</option></select></label> <label class=\"block mb-2\">Progress Mode <select name=\"progress_mode\" x-model=\"progressMode\" @change=\"applyProgressMode()\" class=\"w-full mt-1 px-3 py-2 rounded border\"><option value=\"pages\">Pages</option> <option value=\"chapter\">Chapter</option> <option value=\"percentage\">Percentage</option> <option value=\"time-left\">Time Left</option></select></label></div><!-- Reading Theme (reflowable only) --><div class=\"mb-6\" x-show=\"!isFixedLayout\"><div class=\"flex items-center justify-between mb-2\"><h3 class=\"font-semibold\">Reading Theme</h3><button @click=\"toggleReadingMode()\" class=\"relative w-14 h-7 rounded-full transition-colors duration-200 flex items-center px-1\" :class=\"readingMode === 'dark' ? 'bg-blue-500' : 'bg-gray-400'\" type=\"button\" :aria-label=\"readingMode === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'\"><span class=\"absolute top-0.5 left-0.5 w-6 h-6 bg-white rounded-full shadow transition-transform duration-200 flex items-center justify-center text-xs\" :class=\"readingMode === 'dark' ? 'translate-x-7' : 'translate-x-0'\" x-text=\"readingMode === 'dark' ? '🌙' : '☀️'\">☀️</span></button></div><label class=\"block mb-2\"><select name=\"reading_theme\" x-model=\"readingTheme\" @change=\"applyTheme()\" class=\"w-full mt-1 px-3 py-2 rounded border\"><optgroup label=\"📖 Classic Reading\"><option value=\"light\">Light</option> <option value=\"paper\">Paper</option> <option value=\"sepia\">Sepia</option> <option value=\"parchment\">Parchment</option> <option value=\"warm\">Warm</option> <option value=\"candlelight\">Candlelight</option></optgroup> <optgroup label=\"🌤️ Sky & Atmosphere\"><option value=\"azure\">Azure</option> <option value=\"sky\">Sky</option> <option value=\"arctic\">Arctic</option> <option value=\"frost\">Frost</option></optgroup> <optgroup label=\"🌅 Sunset & Warmth\"><option value=\"dusk\">Dusk</option> <option value=\"sunset\">Sunset</option> <option value=\"twilight\">Twilight</option></optgroup> <optgroup label=\"🌲 Nature & Earth\"><option value=\"forest\">Forest</option> <option value=\"moss\">Moss</option> <option value=\"slate\">Slate</option></optgroup> <optgroup label=\"⚡ High Performance\"><option value=\"oled\">OLED</option> <option value=\"solarized\">Solarized</option></optgroup></select></label></div><!-- Typography (reflowable only) --><div class=\"mb-6\" x-show=\"!isFixedLayout\"><h3 class=\"font-semibold mb-2\">Typography</h3><label class=\"block mb-2\">Reading Font <select name=\"reading_font\" x-model=\"readingFont\" @change=\"applyFont()\" class=\"w-full mt-1 px-3 py-2 rounded border\"><option value=\"literata\">Literata (Default)</option> <option value=\"crimson\">Crimson Text</option> <option value=\"source-serif\">Source Serif 4</option> <option value=\"eb-garamond\">EB Garamond</option> <option value=\"libertinus\">Libertinus Serif</option> <option value=\"noto-serif\">Noto Serif</option> <option value=\"charis-sil\">Charis SIL</option> <option value=\"ibm-plex\">IBM Plex Serif</option></select></label> <label class=\"block mb-2\">Font Size <input type=\"range\" name=\"font_size\" x-model.number=\"fontSize\" @change=\"applyFont()\" min=\"10\" max=\"40\" class=\"w-full\"> <span class=\"text-xs\" x-text=\"fontSize + 'px'\" style=\"color: var(--text-secondary)\">18px</span></label> <label class=\"block mb-2\">Line Height <input type=\"range\" name=\"line_height\" x-model.number=\"lineHeight\" @change=\"applyFont()\" min=\"0.8\" max=\"3.0\" step=\"0.1\" class=\"w-full\"> <span class=\"text-xs\" x-text=\"lineHeight\" style=\"color: var(--text-secondary)\">1.6</span></label></div><!-- Navigation --><div class=\"mb-6\" x-show=\"isFixedLayout\"><h3 class=\"font-semibold mb-2\">Navigation</h3><label class=\"flex items-center mb-2\"><input type=\"checkbox\" name=\"double_page_spread\" x-model=\"doublePageSpread\" @change=\"applyDoublePageSpread()\" class=\"mr-2\"> Double Page Spread</label></div><div class=\"flex gap-2 mt-6\"><button @click=\"restoreDefaults()\" class=\"flex-1 py-2 border rounded hoveLine truncated
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -359,7 +359,7 @@ func ReaderBookmarksPanel(bookmarks []Bookmark) templ.Component {
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(bookmark.CfiPosition)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 393, Col: 38}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 398, Col: 38}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
if templ_7745c5c3_Err != nil {
@@ -372,7 +372,7 @@ func ReaderBookmarksPanel(bookmarks []Bookmark) templ.Component {
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(bookmark.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 396, Col: 49}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 401, Col: 49}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
@@ -385,7 +385,7 @@ func ReaderBookmarksPanel(bookmarks []Bookmark) templ.Component {
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(bookmark.Position)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 398, Col: 27}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 403, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {