feat(reader): wire up TOC, bookmarks, and navigator panels with Alpine.js bindings
Replace dead data-action attributes with Alpine.js @click handlers and x-ref references across all reader panels: - TOC panel: replaced static <nav> with x-for loop over tocItems array, added goToTOCItem() click handler, window-shade toggle via .tocPanel - Bookmarks panel: replaced data-action with @click.prevent handlers, added goToBookmarkTarget() using data-cfi attributes for navigation, window-shade toggle via .bookmarksPanel - Navigator panel: replaced data-action with @click window-shade toggle via .navigatorPanel - Added goToBookmarkTarget() and toggleWindowShade() methods to reader.ts - Removed unused panel-lock buttons (lock feature not yet implemented) - Regenerated reader_templ.go, rebuilt CSS and JS bundles
This commit is contained in:
+62
-70
@@ -15,16 +15,6 @@ templ Reader(user User, metadata ReaderMetadata, progress ReadingProgress, bookm
|
||||
<script src="/static/htmx.min.js"></script>
|
||||
<script type="module" src="/static/reader.js"></script>
|
||||
<link href="/static/style.css" rel="stylesheet"/>
|
||||
<style>
|
||||
.reader-icon {
|
||||
display: block;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2px;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
x-data="readerShell"
|
||||
@@ -36,19 +26,19 @@ templ Reader(user User, metadata ReaderMetadata, progress ReadingProgress, bookm
|
||||
<div id="reader-panels" class="fixed inset-0 pointer-events-none z-30">
|
||||
<!-- Left Sidebar (TOC, Settings) -->
|
||||
<div id="left-sidebar" class="absolute left-0 top-0 bottom-0 pointer-events-auto flex flex-col">
|
||||
<div id="toc-panel" class="panel-container pointer-events-auto" data-panel="toc">
|
||||
<div id="toc-panel" x-show="tocOpen" x-transition class="panel-container pointer-events-auto" data-panel="toc">
|
||||
@ReaderTOCPanel(metadata)
|
||||
</div>
|
||||
<div id="settings-panel" class="panel-container pointer-events-auto" data-panel="settings">
|
||||
<div id="settings-panel" x-show="settingsOpen" x-transition class="panel-container pointer-events-auto" data-panel="settings">
|
||||
@ReaderSettingsPanel()
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right Sidebar (Navigator, Bookmarks) -->
|
||||
<div id="right-sidebar" class="absolute right-0 top-0 bottom-0 pointer-events-auto flex flex-col">
|
||||
<div id="navigator-panel" class="panel-container pointer-events-auto" data-panel="navigator">
|
||||
<div id="navigator-panel" x-show="navigatorOpen" x-transition class="panel-container pointer-events-auto" data-panel="navigator">
|
||||
@ReaderNavigatorPanel()
|
||||
</div>
|
||||
<div id="bookmarks-panel" class="panel-container pointer-events-auto" data-panel="bookmarks">
|
||||
<div id="bookmarks-panel" x-show="bookmarksOpen" x-transition class="panel-container pointer-events-auto" data-panel="bookmarks">
|
||||
@ReaderBookmarksPanel(bookmarks)
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,7 +59,7 @@ templ ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress)
|
||||
</a>
|
||||
<h1 class="text-lg font-semibold">{ metadata.Title }</h1>
|
||||
<button
|
||||
data-action="open-settings"
|
||||
@click="toggleSettings()"
|
||||
class="p-2 rounded-lg hover:bg-gray-700"
|
||||
title="Settings"
|
||||
>
|
||||
@@ -150,9 +140,9 @@ templ ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress)
|
||||
<div class="w-px h-6 bg-gray-600 mx-1"></div>
|
||||
<!-- Action buttons -->
|
||||
<div class="flex items-center gap-1">
|
||||
<button data-action="toggle-toc" class="p-2 rounded-lg hover:bg-gray-700" title="Table of Contents">📖</button>
|
||||
<button data-action="add-bookmark" class="p-2 rounded-lg hover:bg-gray-700" title="Bookmark">🏷️</button>
|
||||
<button data-action="add-note" class="p-2 rounded-lg hover:bg-gray-700" title="Note">📝</button>
|
||||
<button @click="toggleTOC()" class="p-2 rounded-lg hover:bg-gray-700" title="Table of Contents">📖</button>
|
||||
<button @click="addBookmark()" class="p-2 rounded-lg hover:bg-gray-700" title="Bookmark">🏷️</button>
|
||||
<button @click="toggleBookmarks()" class="p-2 rounded-lg hover:bg-gray-700" title="Notes">📝</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -161,16 +151,15 @@ templ ReaderChrome(user User, metadata ReaderMetadata, progress ReadingProgress)
|
||||
|
||||
templ ReaderSettingsPanel() {
|
||||
<div
|
||||
id="settings-panel"
|
||||
class="dockable-panel panel-collapsed"
|
||||
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" data-action="toggle-panel">
|
||||
<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="panel-lock" data-action="lock-panel" title="Lock position">🔓</button>
|
||||
<button class="window-shade-toggle" data-action="window-shade">─</button>
|
||||
<button class="window-shade-toggle">─</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content p-4 overflow-y-auto">
|
||||
@@ -195,12 +184,12 @@ templ ReaderSettingsPanel() {
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<!-- Typography (ebooks only) -->
|
||||
<div class="mb-6" data-visible-for="ebook">
|
||||
<!-- Reading Theme (reflowable only) -->
|
||||
<div class="mb-6" x-show="!isFixedLayout">
|
||||
<h3 class="font-semibold mb-2">Reading Theme</h3>
|
||||
<label class="block mb-2">
|
||||
<select name="reading_theme" class="w-full mt-1 px-3 py-2 rounded border">
|
||||
<optgroup label="📖 Classic Reading">
|
||||
<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>
|
||||
@@ -208,89 +197,95 @@ templ ReaderSettingsPanel() {
|
||||
<option value="warm">Warm</option>
|
||||
<option value="candlelight">Candlelight</option>
|
||||
</optgroup>
|
||||
<optgroup label="🌤️ Sky & Atmosphere">
|
||||
<optgroup label="Sky and 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">
|
||||
<optgroup label="Sunset and Warmth">
|
||||
<option value="dusk">Dusk</option>
|
||||
<option value="sunset">Sunset</option>
|
||||
<option value="twilight">Twilight</option>
|
||||
</optgroup>
|
||||
<optgroup label="🌲 Nature & Earth">
|
||||
<optgroup label="Nature and Earth">
|
||||
<option value="forest">Forest</option>
|
||||
<option value="moss">Moss</option>
|
||||
<option value="slate">Slate</option>
|
||||
</optgroup>
|
||||
<optgroup label="⚡ High Performance">
|
||||
<optgroup label="High Performance">
|
||||
<option value="oled">OLED</option>
|
||||
<option value="solarized">Solarized</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<p class="text-xs mt-1" style="color: var(--text-secondary)">18 themes organized by category</p>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-6" data-visible-for="ebook">
|
||||
<!-- 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" class="w-full mt-1 px-3 py-2 rounded border">
|
||||
<option value="literata">Literata (Default - Designed for ebooks)</option>
|
||||
<option value="crimson">Crimson Text (Screen-optimized)</option>
|
||||
<option value="source-serif">Source Serif 4 (Adobe quality)</option>
|
||||
<option value="eb-garamond">EB Garamond (Classic)</option>
|
||||
<option value="libertinus">Libertinus Serif (Technical)</option>
|
||||
<option value="noto-serif">Noto Serif (All languages)</option>
|
||||
<option value="charis-sil">Charis SIL (Multilingual)</option>
|
||||
<option value="ibm-plex">IBM Plex Serif (Modern)</option>
|
||||
<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>
|
||||
<p class="text-xs ml-2" style="color: var(--text-secondary)">8 libre fonts bundled with Bookhoard</p>
|
||||
</label>
|
||||
<label class="block mb-2">
|
||||
Font Size
|
||||
<input type="range" name="font_size" min="12" max="24" value="16" class="w-full"/>
|
||||
<span class="text-xs ml-2" style="color: var(--text-secondary)">12-24px</span>
|
||||
<input type="range" name="font_size" x-model.number="fontSize" @change="applyFont()" min="12" max="24" class="w-full"/>
|
||||
<span class="text-xs" x-text="fontSize + 'px'" style="color: var(--text-secondary)">16px</span>
|
||||
</label>
|
||||
<label class="block mb-2">
|
||||
Line Height
|
||||
<input type="range" name="line_height" min="1.0" max="2.5" step="0.1" value="1.6" class="w-full"/>
|
||||
<span class="text-xs ml-2" style="color: var(--text-secondary)">1.0-2.5</span>
|
||||
<input type="range" name="line_height" x-model.number="lineHeight" @change="applyFont()" min="1.0" max="2.5" 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">
|
||||
<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 (Comics/Manga)
|
||||
Double Page Spread
|
||||
</label>
|
||||
</div>
|
||||
<button data-action="close-settings" class="w-full py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
<button @click="toggleSettings()" class="w-full py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ ReaderTOCPanel(metadata ReaderMetadata) {
|
||||
templ ReaderTOCPanel(_ ReaderMetadata) {
|
||||
<div
|
||||
id="toc-panel"
|
||||
class="dockable-panel"
|
||||
data-panel="toc"
|
||||
data-side="left"
|
||||
x-ref="tocPanel"
|
||||
>
|
||||
<div class="panel-header flex items-center justify-between p-3 cursor-pointer" data-action="toggle-panel">
|
||||
<div class="panel-header flex items-center justify-between p-3 cursor-pointer" @click="toggleWindowShade($refs.tocPanel)">
|
||||
<h3 class="panel-title font-semibold">📖 Table of Contents</h3>
|
||||
<div class="panel-controls flex items-center gap-2">
|
||||
<button class="panel-lock" data-action="lock-panel" title="Lock position">🔓</button>
|
||||
<button class="window-shade-toggle" data-action="window-shade">─</button>
|
||||
<button class="window-shade-toggle">─</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content p-4 overflow-y-auto">
|
||||
<nav id="toc-list" class="space-y-2">
|
||||
<!-- TOC items populated by JavaScript -->
|
||||
<nav id="toc-list" class="space-y-1">
|
||||
<template x-for="item in tocItems" :key="item.href">
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="goToTOCItem(item)"
|
||||
class="block py-1 px-2 rounded hover:bg-gray-700 text-sm"
|
||||
:style="'padding-left: ' + ((item.depth || 0) * 1 + 0.5) + 'rem'"
|
||||
x-text="item.label"
|
||||
></a>
|
||||
</template>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
@@ -298,22 +293,19 @@ templ ReaderTOCPanel(metadata ReaderMetadata) {
|
||||
|
||||
templ ReaderNavigatorPanel() {
|
||||
<div
|
||||
id="navigator-panel"
|
||||
class="dockable-panel"
|
||||
data-panel="navigator"
|
||||
data-side="right"
|
||||
x-ref="navigatorPanel"
|
||||
>
|
||||
<div class="panel-header flex items-center justify-between p-3 cursor-pointer" data-action="toggle-panel">
|
||||
<div class="panel-header flex items-center justify-between p-3 cursor-pointer" @click="toggleWindowShade($refs.navigatorPanel)">
|
||||
<h3 class="panel-title font-semibold">🗺️ Navigator</h3>
|
||||
<div class="panel-controls flex items-center gap-2">
|
||||
<button class="panel-lock" data-action="lock-panel" title="Lock position">🔓</button>
|
||||
<button class="window-shade-toggle" data-action="window-shade">─</button>
|
||||
<button class="window-shade-toggle">─</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content p-2 overflow-hidden">
|
||||
<div id="navigator-viewport" class="relative w-full h-full">
|
||||
<!-- Full page preview with draggable viewport box -->
|
||||
<!-- JavaScript renders current page as scaled thumbnail with draggable viewport -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -321,16 +313,15 @@ templ ReaderNavigatorPanel() {
|
||||
|
||||
templ ReaderBookmarksPanel(bookmarks []Bookmark) {
|
||||
<div
|
||||
id="bookmarks-panel"
|
||||
class="dockable-panel panel-collapsed"
|
||||
class="dockable-panel"
|
||||
data-panel="bookmarks"
|
||||
data-side="right"
|
||||
x-ref="bookmarksPanel"
|
||||
>
|
||||
<div class="panel-header flex items-center justify-between p-3 cursor-pointer" data-action="toggle-panel">
|
||||
<div class="panel-header flex items-center justify-between p-3 cursor-pointer" @click="toggleWindowShade($refs.bookmarksPanel)">
|
||||
<h3 class="panel-title font-semibold">🔖 Bookmarks</h3>
|
||||
<div class="panel-controls flex items-center gap-2">
|
||||
<button class="panel-lock" data-action="lock-panel" title="Lock position">🔓</button>
|
||||
<button class="window-shade-toggle" data-action="window-shade">─</button>
|
||||
<button class="window-shade-toggle">─</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content p-4 overflow-y-auto">
|
||||
@@ -339,7 +330,8 @@ templ ReaderBookmarksPanel(bookmarks []Bookmark) {
|
||||
for _, bookmark := range bookmarks {
|
||||
<a
|
||||
href="#"
|
||||
data-bookmark-id={ bookmark.ID }
|
||||
@click.prevent="goToBookmarkTarget($el.dataset.cfi)"
|
||||
data-cfi={ bookmark.CfiPosition }
|
||||
class="block py-2 hover:bg-gray-700 rounded px-2"
|
||||
>
|
||||
<span class="font-medium">{ bookmark.Title }</span>
|
||||
@@ -352,7 +344,7 @@ templ ReaderBookmarksPanel(bookmarks []Bookmark) {
|
||||
} else {
|
||||
<p class="text-sm" style="color: var(--text-secondary)">No bookmarks yet</p>
|
||||
}
|
||||
<button data-action="add-bookmark" class="w-full py-2 mt-4 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
<button @click="addBookmark()" class="w-full py-2 mt-4 bg-blue-600 text-white rounded hover:bg-blue-700">
|
||||
+ Add Bookmark
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user