2026-03-16 19:19:22 +01:00
|
|
|
/* =========================================================
|
|
|
|
|
diora — dark radio player theme
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
2026-09-12 15:05:08 +02:00
|
|
|
/* Self-hosted (no external request, works offline via the service worker's
|
|
|
|
|
app-shell cache) — variable font covering weight 200-900 in one file per
|
|
|
|
|
style/subset. Latin + Latin Extended only (covers German/English text). */
|
|
|
|
|
@font-face {
|
|
|
|
|
font-family: 'Source Serif 4';
|
|
|
|
|
font-style: normal;
|
|
|
|
|
font-weight: 200 900;
|
|
|
|
|
font-display: swap;
|
|
|
|
|
src: url('/static/fonts/SourceSerif4-normal-latin.woff2') format('woff2');
|
|
|
|
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
|
|
|
}
|
|
|
|
|
@font-face {
|
|
|
|
|
font-family: 'Source Serif 4';
|
|
|
|
|
font-style: normal;
|
|
|
|
|
font-weight: 200 900;
|
|
|
|
|
font-display: swap;
|
|
|
|
|
src: url('/static/fonts/SourceSerif4-normal-latin-ext.woff2') format('woff2');
|
|
|
|
|
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
|
|
|
}
|
|
|
|
|
@font-face {
|
|
|
|
|
font-family: 'Source Serif 4';
|
|
|
|
|
font-style: italic;
|
|
|
|
|
font-weight: 200 900;
|
|
|
|
|
font-display: swap;
|
|
|
|
|
src: url('/static/fonts/SourceSerif4-italic-latin.woff2') format('woff2');
|
|
|
|
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
|
|
|
}
|
|
|
|
|
@font-face {
|
|
|
|
|
font-family: 'Source Serif 4';
|
|
|
|
|
font-style: italic;
|
|
|
|
|
font-weight: 200 900;
|
|
|
|
|
font-display: swap;
|
|
|
|
|
src: url('/static/fonts/SourceSerif4-italic-latin-ext.woff2') format('woff2');
|
|
|
|
|
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 19:19:22 +01:00
|
|
|
*, *::before, *::after {
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 18:01:23 +02:00
|
|
|
html, body {
|
|
|
|
|
height: 100%;
|
|
|
|
|
overflow: hidden;
|
2026-10-14 18:02:33 +02:00
|
|
|
scrollbar-color: var(--raised) var(--bg);
|
2026-04-05 18:01:23 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-05 18:06:20 +02:00
|
|
|
::-webkit-scrollbar { width: 8px; height: 8px; }
|
2026-10-14 18:02:33 +02:00
|
|
|
::-webkit-scrollbar-track { background: var(--bg); }
|
|
|
|
|
::-webkit-scrollbar-thumb { background: var(--raised); border-radius: 4px; }
|
|
|
|
|
::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
|
2026-04-05 18:06:20 +02:00
|
|
|
|
2026-03-16 19:19:22 +01:00
|
|
|
:root {
|
|
|
|
|
--bg: #000;
|
|
|
|
|
--bg-card: transparent;
|
|
|
|
|
--bg-alt: transparent;
|
|
|
|
|
--bg-row: transparent;
|
|
|
|
|
--bg-row-alt: transparent;
|
|
|
|
|
--border: #2a2a2a;
|
|
|
|
|
--accent: #e63946;
|
|
|
|
|
--accent-hover: #ff4d58;
|
|
|
|
|
--green: #2ecc71;
|
|
|
|
|
--yellow: #f1c40f;
|
|
|
|
|
--font: 'Courier New', Courier, monospace;
|
|
|
|
|
--radius: 3px;
|
|
|
|
|
--nav-h: 48px;
|
|
|
|
|
--bar-h: 72px;
|
|
|
|
|
|
|
|
|
|
/* contrast scheme — default: white on dark */
|
|
|
|
|
--fg: #fff;
|
|
|
|
|
--fg-muted: #fff;
|
|
|
|
|
--outline: #000;
|
|
|
|
|
--text: var(--fg);
|
|
|
|
|
--text-muted: var(--fg);
|
2026-10-14 18:02:33 +02:00
|
|
|
|
|
|
|
|
/* The colour scheme (docs/theme.md): dark here, light below. Use only these. */
|
|
|
|
|
--muted: #9a9a9a;
|
|
|
|
|
--card: #0b0b0b;
|
|
|
|
|
--panel: #111;
|
|
|
|
|
--hover: #141414;
|
|
|
|
|
--raised: #1a1a1a;
|
|
|
|
|
--border-strong: #444;
|
|
|
|
|
--on-accent: #fff;
|
|
|
|
|
--scrim: rgba(0, 0, 0, 0.6);
|
|
|
|
|
--shadow: none;
|
|
|
|
|
--surface: var(--panel);
|
|
|
|
|
color-scheme: dark;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
html[data-theme="light"] {
|
|
|
|
|
--bg: #f6f5f1;
|
|
|
|
|
--fg: #17171a;
|
|
|
|
|
--fg-muted: #17171a;
|
|
|
|
|
--muted: #66666b;
|
|
|
|
|
--card: #fff;
|
|
|
|
|
--panel: #fff;
|
|
|
|
|
--hover: #ecebe6;
|
|
|
|
|
--raised: #e4e2dc;
|
|
|
|
|
--border: #d9d6ce;
|
|
|
|
|
--border-strong: #b4b0a6;
|
|
|
|
|
--accent: #d0263a;
|
|
|
|
|
--accent-hover: #b51f31;
|
|
|
|
|
--on-accent: #fff;
|
|
|
|
|
--scrim: rgba(20, 20, 20, 0.35);
|
|
|
|
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
|
|
|
--bg-card: var(--card);
|
|
|
|
|
--bg-alt: var(--hover);
|
|
|
|
|
/* Outlined text is for wallpapers (body.bright-bg / a dark picture); on plain light, none. */
|
|
|
|
|
--outline: transparent;
|
|
|
|
|
color-scheme: light;
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* inverted: black on white */
|
2026-03-16 19:19:22 +01:00
|
|
|
body.bright-bg {
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
--bg: #fff;
|
|
|
|
|
--bg-card: #f5f5f5;
|
|
|
|
|
--bg-alt: #f0f0f0;
|
|
|
|
|
--bg-row: #fafafa;
|
|
|
|
|
--bg-row-alt: #f5f5f5;
|
2026-03-16 19:19:22 +01:00
|
|
|
--fg: #000;
|
|
|
|
|
--fg-muted: #000;
|
|
|
|
|
--outline: #fff;
|
|
|
|
|
--border: #bbb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
html, body {
|
|
|
|
|
background: var(--bg);
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
text-shadow:
|
|
|
|
|
-1px -1px 0 var(--outline),
|
|
|
|
|
1px -1px 0 var(--outline),
|
|
|
|
|
-1px 1px 0 var(--outline),
|
|
|
|
|
1px 1px 0 var(--outline);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Elements with their own coloured backgrounds — no outline, no override */
|
|
|
|
|
.btn-play,
|
|
|
|
|
.btn-amazon,
|
|
|
|
|
.btn-lastfm,
|
|
|
|
|
.btn-danger,
|
|
|
|
|
.btn-primary,
|
2026-08-15 00:02:05 +02:00
|
|
|
.navbar-brand,
|
|
|
|
|
.reader-toast {
|
2026-03-16 19:19:22 +01:00
|
|
|
text-shadow: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Accent colour stays red regardless of scheme */
|
|
|
|
|
.navbar-brand { color: var(--accent); }
|
|
|
|
|
.tab-btn.active { color: var(--accent); }
|
|
|
|
|
|
|
|
|
|
a {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
a:hover {
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
NAVBAR
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.navbar {
|
|
|
|
|
position: sticky;
|
|
|
|
|
top: 0;
|
|
|
|
|
z-index: 100;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
height: var(--nav-h);
|
|
|
|
|
padding: 0 1.5rem;
|
2026-04-01 15:24:35 +02:00
|
|
|
background: var(--bg);
|
2026-03-16 19:19:22 +01:00
|
|
|
border-bottom: 1px solid var(--border);
|
2026-04-05 17:54:00 +02:00
|
|
|
/* Window Controls Overlay: extend navbar into the titlebar area */
|
|
|
|
|
padding-left: max(1.5rem, env(titlebar-area-x, 1.5rem));
|
|
|
|
|
padding-top: env(titlebar-area-y, 0);
|
|
|
|
|
height: calc(var(--nav-h) + env(titlebar-area-y, 0px));
|
|
|
|
|
-webkit-app-region: drag;
|
|
|
|
|
}
|
|
|
|
|
.navbar a, .navbar button, .navbar input, .navbar form {
|
|
|
|
|
-webkit-app-region: no-drag;
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.navbar-brand {
|
|
|
|
|
font-size: 1.4rem;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
.navbar-brand:hover {
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.navbar-links {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.navbar-user {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
MAIN CONTENT
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.main-content {
|
|
|
|
|
max-width: 1100px;
|
|
|
|
|
margin: 0 auto;
|
2026-05-30 00:34:53 +02:00
|
|
|
padding: calc(var(--nav-h) + 3rem) 1.5rem calc(var(--bar-h) + 2rem);
|
2026-05-30 00:19:00 +02:00
|
|
|
height: 100%;
|
2026-04-05 18:01:23 +02:00
|
|
|
overflow-y: auto;
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
MESSAGES
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.messages {
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message {
|
|
|
|
|
padding: 0.6rem 1rem;
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
margin-bottom: 0.4rem;
|
|
|
|
|
background: var(--bg-alt);
|
|
|
|
|
border-left: 3px solid var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
.message-error { border-color: var(--accent); }
|
|
|
|
|
.message-success { border-color: var(--green); }
|
|
|
|
|
.message-warning { border-color: var(--yellow); }
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
NOW PLAYING BAR (fixed at bottom)
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.now-playing-bar {
|
|
|
|
|
position: fixed;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
height: var(--bar-h);
|
2026-04-01 15:24:35 +02:00
|
|
|
background: var(--bg);
|
2026-03-16 19:19:22 +01:00
|
|
|
border-top: 1px solid var(--border);
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
padding: 0 1.5rem;
|
|
|
|
|
z-index: 200;
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.now-playing-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.now-playing-station {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.now-playing-track {
|
|
|
|
|
font-size: 0.95rem;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
color: var(--text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.now-playing-controls {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.volume-label {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.4rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.volume-slider {
|
|
|
|
|
width: 80px;
|
|
|
|
|
accent-color: var(--accent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
AFFILIATE SECTION
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.affiliate-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 1.25rem;
|
|
|
|
|
background: var(--bg-card);
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 1rem;
|
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.affiliate-artwork {
|
|
|
|
|
width: 80px;
|
|
|
|
|
height: 80px;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background: var(--bg-alt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.affiliate-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.2rem;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.affiliate-track {
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.affiliate-artist {
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.affiliate-album {
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-style: italic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-amazon {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
margin-top: 0.5rem;
|
|
|
|
|
background: #ff9900;
|
|
|
|
|
color: #000;
|
|
|
|
|
padding: 0.3rem 0.8rem;
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
}
|
|
|
|
|
.btn-amazon:hover {
|
|
|
|
|
background: #ffad33;
|
|
|
|
|
color: #000;
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
TABS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
gap: 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-30 00:34:53 +02:00
|
|
|
/* Haupttab-Leiste: immer sichtbar unterhalb der Navbar */
|
|
|
|
|
#tabs {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: var(--nav-h);
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
z-index: 150;
|
|
|
|
|
background: var(--bg);
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
padding: 0 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 19:19:22 +01:00
|
|
|
.tab-btn {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
border-bottom: 2px solid transparent;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
padding: 0.5rem 1.25rem;
|
|
|
|
|
transition: color 0.15s, border-color 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-btn:hover {
|
|
|
|
|
color: var(--text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-btn.active {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
border-bottom-color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-panel {
|
|
|
|
|
min-height: 200px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-03 23:08:57 +02:00
|
|
|
/* Ambient: the synthesizer's frame fills the room down to the player bar (app.js _ambientSize). */
|
|
|
|
|
.ambient-frame {
|
|
|
|
|
display: block;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 70vh;
|
|
|
|
|
border: 0;
|
2026-10-14 18:02:33 +02:00
|
|
|
background: var(--bg);
|
2026-10-03 23:08:57 +02:00
|
|
|
}
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
.sub-tabs {
|
|
|
|
|
border-bottom-color: transparent;
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sub-tabs .tab-btn {
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
padding: 0.3rem 1rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sub-tabs .tab-btn.active {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
border-bottom-color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 19:19:22 +01:00
|
|
|
/* =========================================================
|
|
|
|
|
SEARCH
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.search-bar {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-input {
|
|
|
|
|
flex: 1;
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
color: var(--text);
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
padding: 0.4rem 0.7rem;
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
.search-input:focus {
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-msg {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
min-height: 1.4em;
|
|
|
|
|
margin-bottom: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
TABLES
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.data-table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-table th {
|
|
|
|
|
background: var(--bg-alt);
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-weight: normal;
|
|
|
|
|
padding: 0.45rem 0.7rem;
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-table td {
|
|
|
|
|
padding: 0.4rem 0.7rem;
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
max-width: 220px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-table tbody tr:nth-child(odd) { background: transparent; }
|
|
|
|
|
.data-table tbody tr:nth-child(even) { background: transparent; }
|
2026-10-14 18:02:33 +02:00
|
|
|
.data-table tbody tr:hover { background: var(--hover); }
|
2026-03-16 19:19:22 +01:00
|
|
|
|
|
|
|
|
.empty-msg {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 1.5rem !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.history-time {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
BUTTONS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.btn {
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
color: var(--text);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
padding: 0.35rem 0.75rem;
|
|
|
|
|
transition: background 0.1s, border-color 0.1s;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.btn:hover {
|
2026-10-14 18:02:33 +02:00
|
|
|
background: var(--hover);
|
|
|
|
|
border-color: var(--border-strong);
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-play {
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
border-color: var(--accent);
|
2026-10-14 18:02:33 +02:00
|
|
|
color: var(--on-accent);
|
2026-03-16 19:19:22 +01:00
|
|
|
font-size: 0.9rem;
|
|
|
|
|
padding: 0.4rem 1rem;
|
|
|
|
|
}
|
|
|
|
|
.btn-play:hover {
|
|
|
|
|
background: var(--accent-hover);
|
|
|
|
|
border-color: var(--accent-hover);
|
|
|
|
|
}
|
|
|
|
|
.btn-play.playing {
|
2026-10-14 18:02:33 +02:00
|
|
|
background: var(--raised);
|
|
|
|
|
border-color: var(--border-strong);
|
2026-03-16 19:19:22 +01:00
|
|
|
color: var(--text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-save {
|
|
|
|
|
color: var(--yellow);
|
2026-10-14 18:02:33 +02:00
|
|
|
border-color: var(--border-strong);
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
.btn-save:hover {
|
|
|
|
|
background: #2a2a00;
|
|
|
|
|
border-color: var(--yellow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-sm {
|
|
|
|
|
font-size: 0.78rem;
|
|
|
|
|
padding: 0.25rem 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-primary {
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
border-color: var(--accent);
|
2026-10-14 18:02:33 +02:00
|
|
|
color: var(--on-accent);
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
.btn-primary:hover {
|
|
|
|
|
background: var(--accent-hover);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-danger {
|
|
|
|
|
background: #300;
|
|
|
|
|
border-color: #700;
|
|
|
|
|
color: #faa;
|
|
|
|
|
}
|
|
|
|
|
.btn-danger:hover {
|
|
|
|
|
background: #500;
|
|
|
|
|
border-color: #a00;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
2026-10-14 18:02:33 +02:00
|
|
|
html[data-theme="light"] .btn-danger { background: #fdecee; border-color: #e7a3aa; color: #a3172a; }
|
|
|
|
|
html[data-theme="light"] .btn-danger:hover { background: #a3172a; border-color: #a3172a; color: #fff; }
|
|
|
|
|
html[data-theme="light"] .btn-save:hover { background: #fbf3d3; }
|
2026-03-16 19:19:22 +01:00
|
|
|
|
|
|
|
|
.btn-lastfm {
|
|
|
|
|
background: #d51007;
|
|
|
|
|
border-color: #d51007;
|
|
|
|
|
color: #fff;
|
|
|
|
|
display: inline-block;
|
|
|
|
|
padding: 0.4rem 1rem;
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: 1px solid transparent;
|
|
|
|
|
}
|
|
|
|
|
.btn-lastfm:hover {
|
|
|
|
|
background: #ff1a0e;
|
|
|
|
|
color: #fff;
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-full {
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 0.55rem;
|
|
|
|
|
font-size: 0.95rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-link {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
padding: 0;
|
|
|
|
|
}
|
|
|
|
|
.btn-link:hover {
|
|
|
|
|
color: var(--accent-hover);
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-icon {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
padding: 0;
|
|
|
|
|
line-height: 1;
|
|
|
|
|
}
|
|
|
|
|
.btn-icon.active {
|
|
|
|
|
color: var(--yellow);
|
|
|
|
|
}
|
|
|
|
|
.btn-icon:hover {
|
|
|
|
|
color: var(--yellow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.inline-form {
|
|
|
|
|
display: inline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
AUTH FORMS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.auth-container {
|
|
|
|
|
max-width: 400px;
|
|
|
|
|
margin: 3rem auto;
|
|
|
|
|
background: var(--bg-card);
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.auth-title {
|
|
|
|
|
font-size: 1.4rem;
|
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
|
color: var(--text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.auth-form {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-label {
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.auth-form input[type="text"],
|
|
|
|
|
.auth-form input[type="password"],
|
|
|
|
|
.auth-form input[type="email"] {
|
|
|
|
|
background: var(--bg-alt);
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
color: var(--text);
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
padding: 0.45rem 0.7rem;
|
|
|
|
|
outline: none;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
.auth-form input:focus {
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.field-errors {
|
|
|
|
|
list-style: none;
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.form-errors {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
background: #1a0000;
|
|
|
|
|
border: 1px solid #500;
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 0.5rem 0.75rem;
|
|
|
|
|
}
|
2026-10-14 18:02:33 +02:00
|
|
|
html[data-theme="light"] .form-errors { background: #fdecee; border-color: #e7a3aa; }
|
2026-03-16 19:19:22 +01:00
|
|
|
|
|
|
|
|
.field-help {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-01 15:01:25 +01:00
|
|
|
.auth-alt { margin-top: 1rem; }
|
|
|
|
|
.auth-or {
|
|
|
|
|
display: flex; align-items: center; gap: 0.6rem;
|
|
|
|
|
margin: 0 0 1rem; font-size: 0.8rem; color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
.auth-or::before, .auth-or::after { content: ''; flex: 1; border-top: 1px solid var(--border, #333); }
|
|
|
|
|
.auth-alt .btn-full { display: block; text-align: center; text-decoration: none; }
|
|
|
|
|
|
|
|
|
|
/* Unlocking the books on a device without the key (js/app/33-unlock.js) */
|
|
|
|
|
.unlock-code { font: 600 1.6rem/1.2 ui-monospace, monospace; letter-spacing: 0.15em; text-align: center; margin: 4px 0 14px; }
|
|
|
|
|
.unlock-or { font-size: 0.85rem; color: var(--text-muted); margin: 0 0 8px; }
|
|
|
|
|
.unlock-status { font-size: 0.85rem; color: var(--text-muted); min-height: 1.2em; margin: 0 0 10px; }
|
|
|
|
|
|
2026-03-16 19:19:22 +01:00
|
|
|
.auth-switch {
|
|
|
|
|
margin-top: 1.25rem;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
SETTINGS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.settings-container {
|
|
|
|
|
max-width: 600px;
|
|
|
|
|
margin: 2rem auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.settings-title {
|
|
|
|
|
font-size: 1.4rem;
|
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.settings-section {
|
|
|
|
|
background: var(--bg-card);
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 1.25rem;
|
|
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.settings-section h2 {
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.lastfm-description {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
margin-bottom: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.connected-status {
|
|
|
|
|
margin-bottom: 0.75rem;
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.checkbox-label {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.auth-prompt {
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
padding: 1.5rem 0;
|
|
|
|
|
}
|
|
|
|
|
|
Bücher: Cloud-Import aus WebDAV/Nextcloud (SW v41)
Verbindungen werden pro Nutzer in den Einstellungen angelegt (Nextcloud,
ownCloud, Synology oder generisches WebDAV) — der Server ist bewusst nicht
auf eine feste Instanz verdrahtet. Im Bücher-Tab lässt sich der entfernte
Ordner durchblättern und eine .epub/.pdf direkt in die Bibliothek ziehen.
Der Download läuft über den Server, weil der WebDAV-Host cross-origin ist
und keine CORS-Header schickt. Verschlüsselt wird trotzdem erst im Browser:
uploadEbook ist in _importEbookBuffer aufgeteilt, das sich lokaler Upload
und Cloud-Import teilen. Gespeichert wird wie bisher nur Geheimtext.
Weil jeder registrierte Nutzer die Ziel-URL bestimmt und diora im Docker-Netz
neben anderen Diensten läuft, ist der Import eine SSRF-Fläche. Dagegen:
- assert_safe_url weist Hosts ab, die auf nicht-öffentliche Adressen
auflösen (inkl. NAT64 und IPv4-kompatibler v6-Adressen, die is_global
durchlässt)
- _assert_peer_is_safe prüft die tatsächliche Peer-Adresse nach dem
Verbinden — requests löst den Namen ein zweites Mal auf, sonst wäre der
Guard per DNS-Rebinding umgehbar
- Redirects werden abgelehnt statt verfolgt
- identische Fehlermeldung für "nicht auflösbar" und "privat", ohne die
IP zu nennen, damit der Endpunkt kein Scanner für interne Dienste wird
Antwort-Bodies laufen durch _read_capped, und DTDs werden vor dem Parsen
abgewiesen: ElementTree expandiert interne Entities, und seit Python 3.12
gibt es XMLParser.parser nicht mehr, um einen Handler zu setzen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 08:26:22 +00:00
|
|
|
/* --- WebDAV cloud connections (settings) --- */
|
|
|
|
|
|
|
|
|
|
.webdav-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
margin: 0 0 1rem;
|
|
|
|
|
padding: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.webdav-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 0.6rem 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.webdav-item-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.15rem;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.webdav-item-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.webdav-form {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.6rem;
|
|
|
|
|
border-top: 1px solid var(--border);
|
|
|
|
|
padding-top: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.webdav-field {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.webdav-field input {
|
|
|
|
|
background: var(--bg-alt);
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
color: var(--text);
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
padding: 0.45rem 0.7rem;
|
|
|
|
|
outline: none;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.webdav-field input:focus {
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.webdav-form .btn {
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-08 19:22:04 +02:00
|
|
|
/* Editing a saved connection: a full row under its name and buttons */
|
|
|
|
|
.webdav-edit {
|
|
|
|
|
flex-basis: 100%;
|
|
|
|
|
padding-top: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
.webdav-edit[hidden] {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
Bücher: Cloud-Import aus WebDAV/Nextcloud (SW v41)
Verbindungen werden pro Nutzer in den Einstellungen angelegt (Nextcloud,
ownCloud, Synology oder generisches WebDAV) — der Server ist bewusst nicht
auf eine feste Instanz verdrahtet. Im Bücher-Tab lässt sich der entfernte
Ordner durchblättern und eine .epub/.pdf direkt in die Bibliothek ziehen.
Der Download läuft über den Server, weil der WebDAV-Host cross-origin ist
und keine CORS-Header schickt. Verschlüsselt wird trotzdem erst im Browser:
uploadEbook ist in _importEbookBuffer aufgeteilt, das sich lokaler Upload
und Cloud-Import teilen. Gespeichert wird wie bisher nur Geheimtext.
Weil jeder registrierte Nutzer die Ziel-URL bestimmt und diora im Docker-Netz
neben anderen Diensten läuft, ist der Import eine SSRF-Fläche. Dagegen:
- assert_safe_url weist Hosts ab, die auf nicht-öffentliche Adressen
auflösen (inkl. NAT64 und IPv4-kompatibler v6-Adressen, die is_global
durchlässt)
- _assert_peer_is_safe prüft die tatsächliche Peer-Adresse nach dem
Verbinden — requests löst den Namen ein zweites Mal auf, sonst wäre der
Guard per DNS-Rebinding umgehbar
- Redirects werden abgelehnt statt verfolgt
- identische Fehlermeldung für "nicht auflösbar" und "privat", ohne die
IP zu nennen, damit der Endpunkt kein Scanner für interne Dienste wird
Antwort-Bodies laufen durch _read_capped, und DTDs werden vor dem Parsen
abgewiesen: ElementTree expandiert interne Entities, und seit Python 3.12
gibt es XMLParser.parser nicht mehr, um einen Handler zu setzen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 08:26:22 +00:00
|
|
|
/* --- Cloud import browser (books tab) --- */
|
|
|
|
|
|
|
|
|
|
.cloud-browser {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 0.75rem;
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-browser-head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-browser-head select {
|
|
|
|
|
background: var(--bg-alt);
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
color: var(--text);
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
padding: 0.3rem 0.5rem;
|
|
|
|
|
outline: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-path {
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-entries {
|
|
|
|
|
list-style: none;
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
max-height: 320px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-entry {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
padding: 0.4rem 0.25rem;
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-entry:last-child {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-entry-name {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: inherit;
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
text-align: left;
|
|
|
|
|
padding: 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-entry-name:hover {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cloud-entry-size {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 19:19:22 +01:00
|
|
|
/* =========================================================
|
|
|
|
|
RESPONSIVE
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
RECOMMENDATIONS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.recommendations-section {
|
|
|
|
|
padding: 12px 16px 4px;
|
2026-10-14 18:02:33 +02:00
|
|
|
border-bottom: 1px solid var(--border);
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
.recommendations-context {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
margin: 0 0 8px;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
}
|
|
|
|
|
.recommendations-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
.recommendations-list li {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
.muted {
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 600px) {
|
|
|
|
|
.main-content {
|
2026-05-30 00:34:53 +02:00
|
|
|
padding: calc(var(--nav-h) + 3rem) 0.75rem calc(var(--bar-h) + 1.5rem);
|
|
|
|
|
}
|
|
|
|
|
#tabs {
|
|
|
|
|
padding: 0 0.25rem;
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.now-playing-bar {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
height: auto;
|
|
|
|
|
padding: 0.5rem 0.75rem;
|
|
|
|
|
gap: 0.4rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.now-playing-info {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.now-playing-controls {
|
|
|
|
|
width: 100%;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 12:22:04 +02:00
|
|
|
.volume-label,
|
|
|
|
|
.volume-slider,
|
|
|
|
|
.volume-num {
|
|
|
|
|
display: none;
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.affiliate-section {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-table th:nth-child(3),
|
|
|
|
|
.data-table td:nth-child(3),
|
|
|
|
|
.data-table th:nth-child(4),
|
|
|
|
|
.data-table td:nth-child(4) {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.auth-container {
|
|
|
|
|
margin: 1rem;
|
|
|
|
|
padding: 1.25rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.import-bar {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
2026-10-14 18:02:33 +02:00
|
|
|
border-bottom: 1px solid var(--border);
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
TIMER WIDGET
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.timer-widget {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
margin-left: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.timer-phase {
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
width: 36px;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.timer-display {
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
min-width: 48px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
DO NOT DISTURB / FOCUS MODE
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
body.dnd-mode .navbar,
|
|
|
|
|
body.dnd-mode .tabs,
|
|
|
|
|
body.dnd-mode .tab-panel,
|
|
|
|
|
body.dnd-mode .affiliate-section {
|
|
|
|
|
display: none !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body.dnd-mode .now-playing-bar {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
|
|
|
|
height: 100vh;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 24px;
|
|
|
|
|
background: transparent;
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 20:38:08 +01:00
|
|
|
body.dnd-mode.dnd-dark {
|
|
|
|
|
background: #000 !important;
|
|
|
|
|
background-image: none !important;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 19:19:22 +01:00
|
|
|
body.dnd-mode.dnd-dark .now-playing-bar {
|
|
|
|
|
background: #000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dnd-only {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body.dnd-mode .dnd-only {
|
|
|
|
|
display: inline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body.dnd-mode .now-playing-info {
|
|
|
|
|
text-align: center;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body.dnd-mode .now-playing-station {
|
|
|
|
|
font-size: 1.6rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body.dnd-mode .now-playing-track {
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body.dnd-mode .timer-display {
|
|
|
|
|
font-size: 3.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
MOOD CHIPS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
.mood-chips {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 8px 16px;
|
2026-10-14 18:02:33 +02:00
|
|
|
border-bottom: 1px solid var(--border);
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mood-chip {
|
|
|
|
|
background: transparent;
|
2026-10-14 18:02:33 +02:00
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
color: var(--fg);
|
2026-03-16 19:19:22 +01:00
|
|
|
border-radius: 999px;
|
|
|
|
|
padding: 3px 12px;
|
|
|
|
|
font-size: 0.78rem;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background 0.15s, border-color 0.15s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mood-chip:hover {
|
2026-10-14 18:02:33 +02:00
|
|
|
background: var(--hover);
|
|
|
|
|
border-color: var(--border-strong);
|
2026-03-16 19:19:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.focus-today {
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
margin-left: 8px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.curated-lists-container {
|
|
|
|
|
padding: 12px 16px 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.curated-section {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.curated-label {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.curated-stations {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.curated-stations li {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.curated-name {
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
}
|
2026-03-16 20:16:30 +01:00
|
|
|
|
|
|
|
|
.btn-delete-history {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--muted, #888);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
.btn-delete-history:hover { opacity: 1; color: #e55; }
|
2026-03-16 20:57:07 +01:00
|
|
|
|
|
|
|
|
#donation-hint {
|
|
|
|
|
position: fixed;
|
|
|
|
|
bottom: 24px;
|
|
|
|
|
right: 24px;
|
|
|
|
|
background: var(--surface, #1e1e2e);
|
|
|
|
|
border: 1px solid var(--border, #333);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
|
|
|
|
z-index: 999;
|
|
|
|
|
animation: hint-in 0.3s ease;
|
|
|
|
|
max-width: 320px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#donation-hint.hiding {
|
|
|
|
|
animation: hint-out 0.4s ease forwards;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#donation-hint button {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--muted, #888);
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
padding: 0;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#donation-hint button:hover { color: var(--fg, #fff); }
|
|
|
|
|
|
|
|
|
|
@keyframes hint-in {
|
|
|
|
|
from { opacity: 0; transform: translateY(12px); }
|
|
|
|
|
to { opacity: 1; transform: translateY(0); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes hint-out {
|
|
|
|
|
from { opacity: 1; transform: translateY(0); }
|
|
|
|
|
to { opacity: 0; transform: translateY(12px); }
|
|
|
|
|
}
|
2026-03-16 21:01:51 +01:00
|
|
|
|
|
|
|
|
.featured-section {
|
|
|
|
|
padding: 8px 0 12px;
|
|
|
|
|
border-bottom: 1px solid var(--border, #333);
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.featured-label {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
color: var(--muted, #888);
|
|
|
|
|
margin: 0 0 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.featured-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
padding: 0;
|
|
|
|
|
margin: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.featured-list li {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
2026-03-16 21:07:12 +01:00
|
|
|
|
|
|
|
|
.volume-num {
|
|
|
|
|
width: 44px;
|
|
|
|
|
background: var(--surface, #1e1e2e);
|
|
|
|
|
border: 1px solid var(--border, #333);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
color: var(--fg, #fff);
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
padding: 2px 4px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.volume-num::-webkit-inner-spin-button,
|
2026-03-16 21:20:31 +01:00
|
|
|
.volume-num::-webkit-outer-spin-button { display: none; }
|
|
|
|
|
.volume-num { -moz-appearance: textfield; }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
|
|
|
|
|
/* ===== SIDEBAR ===== */
|
|
|
|
|
|
|
|
|
|
.sidebar-overlay {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
2026-10-14 18:02:33 +02:00
|
|
|
background: var(--scrim);
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
z-index: 299;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
width: 400px;
|
|
|
|
|
max-width: 100vw;
|
|
|
|
|
/* stop above the now-playing bar */
|
|
|
|
|
bottom: 72px;
|
|
|
|
|
background: var(--surface, #111);
|
|
|
|
|
border-left: 1px solid var(--border, #333);
|
|
|
|
|
z-index: 300;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
transform: translateX(100%);
|
|
|
|
|
transition: transform 0.22s ease;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar.open {
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
padding: 14px 16px 10px;
|
|
|
|
|
border-bottom: 1px solid var(--border, #333);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-title {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
font-size: 0.95rem;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-close {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
margin-top: -2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sidebar-body {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
font-size: 0.88rem;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-04 09:01:52 +02:00
|
|
|
/* ===== MODAL DIALOG ===== */
|
|
|
|
|
|
|
|
|
|
.modal-overlay {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
2026-10-14 18:02:33 +02:00
|
|
|
background: var(--scrim);
|
2026-08-04 09:01:52 +02:00
|
|
|
z-index: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal-dialog {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 50%;
|
|
|
|
|
left: 50%;
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
background: var(--surface, #111);
|
|
|
|
|
border: 1px solid var(--border, #333);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 20px;
|
|
|
|
|
width: 360px;
|
|
|
|
|
max-width: calc(100vw - 32px);
|
|
|
|
|
z-index: 501;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal-message {
|
|
|
|
|
margin: 0 0 16px;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.modal-input { width: 100%; margin-bottom: 16px; }
|
|
|
|
|
|
|
|
|
|
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; }
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* Style links and basic HTML inside shownotes */
|
|
|
|
|
.sidebar-body a { color: var(--accent, #e63946); }
|
|
|
|
|
.sidebar-body p { margin: 0 0 10px; }
|
|
|
|
|
.sidebar-body ul, .sidebar-body ol { margin: 0 0 10px; padding-left: 20px; }
|
|
|
|
|
.sidebar-body h1, .sidebar-body h2, .sidebar-body h3 {
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
margin: 12px 0 4px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-01 07:04:27 +02:00
|
|
|
.tts-lang-select {
|
|
|
|
|
background: var(--surface, #1e1e2e); color: var(--fg, #fff);
|
|
|
|
|
border: 1px solid var(--border, #444); border-radius: 4px;
|
|
|
|
|
padding: 2px 4px; font-size: 0.8rem; cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
Reader: Notizen auf Mobile schnell setzbar statt über Randspalte (SW v32)
Auf schmalen Screens saß der Marker-Modus-Button bisher nur in der 150px-
Randspalte — man musste sie erst öffnen (und damit den ohnehin knappen
Lesebereich verkleinern), um überhaupt markieren zu können. Jetzt zusätzlich
ein Marker-Button direkt im Haupt-Header (nur <=600px sichtbar, Desktop
bleibt bei der bisherigen Anordnung in der Notes-Leiste).
Im Marker-Modus legt ein einfacher Tap auf reinen Lesetext (ohne Auswahl)
jetzt direkt eine freie Notiz an der Wortposition an — man muss nicht mehr
den exakten Klick-Punkt in der Randspalte treffen. Das Bearbeiten der
Notiz läuft auf schmalen Viewports über ein neues Bottom-Sheet (volle
Breite) statt über das Inline-Textfeld in der 150px-Spalte, das auf dem
Handy-Keyboard unangenehm zu bedienen ist.
Notiz-Objekte werden jetzt erst bei tatsächlich eingegebenem Text angelegt
und gespeichert (vorher: sofort beim Setzen des Ankers) — verhindert, dass
ein abgebrochener/leerer Tap eine Ghost-Notiz in currentHighlights
hinterlässt. Gilt für alle Eingabewege (Bottom-Sheet UND die bestehende
Inline-Randspalten-Bearbeitung), per Playwright verifiziert inkl. Escape-
Cancel auf Desktop.
Priorität laut Nutzer: Notizen-Erfassung muss auf Mobile gut funktionieren;
dass bestehende Notizen dort weniger prominent sichtbar sind, ist explizit
in Ordnung — die vertiefte Auseinandersetzung mit Notizen bleibt eine
Desktop-Aufgabe.
Verifiziert per Playwright (390×844 mobil + 1280×900 Desktop-Regression):
Marker-Button-Sichtbarkeit, Bottom-Sheet-Flow (Erstellen/Abbrechen/Speichern,
kein Ghost-Eintrag), reale Text-Selektion + Highlight+Notiz weiterhin
funktionsfähig auf Mobile, Desktop-Workflow (Randspalte, Inline-Editor,
Escape-Cancel, Blur-Commit) unverändert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 20:13:55 +02:00
|
|
|
.reader-marker-btn-mobile { display: none; }
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
@media (max-width: 600px) {
|
|
|
|
|
.sidebar { width: 100vw; }
|
2026-05-29 21:14:40 +02:00
|
|
|
|
|
|
|
|
/* Reader auf Mobile: Header kompakter, Fortschrittsfeld versteckt */
|
|
|
|
|
.reader-header { padding: 6px 8px; gap: 4px; }
|
|
|
|
|
.reader-title { font-size: 13px; }
|
|
|
|
|
.reader-header-actions { gap: 4px; }
|
|
|
|
|
.reader-progress-wrap { display: none; }
|
|
|
|
|
.reader-content { padding: 16px 10px; }
|
Reader: Notizen auf Mobile schnell setzbar statt über Randspalte (SW v32)
Auf schmalen Screens saß der Marker-Modus-Button bisher nur in der 150px-
Randspalte — man musste sie erst öffnen (und damit den ohnehin knappen
Lesebereich verkleinern), um überhaupt markieren zu können. Jetzt zusätzlich
ein Marker-Button direkt im Haupt-Header (nur <=600px sichtbar, Desktop
bleibt bei der bisherigen Anordnung in der Notes-Leiste).
Im Marker-Modus legt ein einfacher Tap auf reinen Lesetext (ohne Auswahl)
jetzt direkt eine freie Notiz an der Wortposition an — man muss nicht mehr
den exakten Klick-Punkt in der Randspalte treffen. Das Bearbeiten der
Notiz läuft auf schmalen Viewports über ein neues Bottom-Sheet (volle
Breite) statt über das Inline-Textfeld in der 150px-Spalte, das auf dem
Handy-Keyboard unangenehm zu bedienen ist.
Notiz-Objekte werden jetzt erst bei tatsächlich eingegebenem Text angelegt
und gespeichert (vorher: sofort beim Setzen des Ankers) — verhindert, dass
ein abgebrochener/leerer Tap eine Ghost-Notiz in currentHighlights
hinterlässt. Gilt für alle Eingabewege (Bottom-Sheet UND die bestehende
Inline-Randspalten-Bearbeitung), per Playwright verifiziert inkl. Escape-
Cancel auf Desktop.
Priorität laut Nutzer: Notizen-Erfassung muss auf Mobile gut funktionieren;
dass bestehende Notizen dort weniger prominent sichtbar sind, ist explizit
in Ordnung — die vertiefte Auseinandersetzung mit Notizen bleibt eine
Desktop-Aufgabe.
Verifiziert per Playwright (390×844 mobil + 1280×900 Desktop-Regression):
Marker-Button-Sichtbarkeit, Bottom-Sheet-Flow (Erstellen/Abbrechen/Speichern,
kein Ghost-Eintrag), reale Text-Selektion + Highlight+Notiz weiterhin
funktionsfähig auf Mobile, Desktop-Workflow (Randspalte, Inline-Editor,
Escape-Cancel, Blur-Commit) unverändert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 20:13:55 +02:00
|
|
|
|
|
|
|
|
/* On a phone, opening the 150px margin just to reach the marker-mode toggle
|
|
|
|
|
eats too much of an already-narrow reading column — surface it directly
|
|
|
|
|
in the main header instead (desktop keeps it only in the margin header,
|
|
|
|
|
grouped with "Notes", per earlier feedback against back-and-forth). */
|
|
|
|
|
.reader-marker-btn-mobile { display: inline-block; }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
Ebook reader + book list + focus station
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
/* --- Drop zone --- */
|
|
|
|
|
.book-drop-zone {
|
|
|
|
|
border: 2px dashed var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 24px 16px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
margin: 12px 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: border-color 0.15s, background 0.15s;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
.book-drop-zone:hover,
|
|
|
|
|
.book-drop-zone.drag-over {
|
|
|
|
|
border-color: var(--accent);
|
2026-10-14 18:02:33 +02:00
|
|
|
background: var(--hover);
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
}
|
|
|
|
|
.book-drop-zone label {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 21:57:41 +01:00
|
|
|
.enc-key-prompt {
|
|
|
|
|
padding: 24px 0 16px;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-29 21:14:40 +02:00
|
|
|
.offline-notice {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
padding: 4px 0 8px;
|
|
|
|
|
}
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
.book-key-bar {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-top: 1rem;
|
|
|
|
|
padding-top: 0.75rem;
|
|
|
|
|
border-top: 1px solid var(--border);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Book list --- */
|
|
|
|
|
.book-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
}
|
|
|
|
|
.book-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
.book-item-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
.book-title {
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
.book-author,
|
|
|
|
|
.book-progress {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
2026-08-17 11:27:16 +02:00
|
|
|
.book-item-meta-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
.book-item-actions {
|
|
|
|
|
display: flex;
|
2026-08-17 11:27:16 +02:00
|
|
|
align-items: center;
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
gap: 6px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
2026-08-04 18:10:33 +02:00
|
|
|
.book-read-badge {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
font-weight: normal;
|
|
|
|
|
}
|
2026-08-17 11:27:16 +02:00
|
|
|
.book-shelf-badge {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
padding: 1px 7px;
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
color: var(--muted, #888);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Per-book "⋮" menu (everything except Open) --- */
|
|
|
|
|
.book-item-menu {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
.book-item-menu-list {
|
|
|
|
|
display: none;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 0;
|
|
|
|
|
top: calc(100% + 4px);
|
|
|
|
|
z-index: 20;
|
|
|
|
|
min-width: 220px;
|
|
|
|
|
background: var(--surface, #111);
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
padding: 4px;
|
|
|
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
|
|
|
|
|
}
|
|
|
|
|
.book-item-menu-list.open {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
.book-menu-item {
|
|
|
|
|
background: none;
|
|
|
|
|
border: none;
|
|
|
|
|
color: var(--fg);
|
|
|
|
|
text-align: left;
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font: inherit;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
.book-menu-item:hover {
|
|
|
|
|
background: var(--bg-row, rgba(255, 255, 255, 0.08));
|
|
|
|
|
}
|
|
|
|
|
.book-menu-item--danger {
|
|
|
|
|
color: var(--accent, #e63946);
|
|
|
|
|
}
|
2026-08-04 18:10:33 +02:00
|
|
|
.book-list-filter {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: var(--muted, #888);
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
|
2026-08-17 15:37:09 +02:00
|
|
|
/* --- Recently-read section (always pinned atop the books view, plain list style) --- */
|
|
|
|
|
.book-recent-section {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 6px;
|
2026-08-17 10:58:53 +02:00
|
|
|
margin-bottom: 14px;
|
2026-08-17 15:37:09 +02:00
|
|
|
padding-bottom: 14px;
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
2026-08-17 10:58:53 +02:00
|
|
|
}
|
|
|
|
|
.book-recent-title {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: 600;
|
2026-08-17 15:37:09 +02:00
|
|
|
margin: 0;
|
2026-08-17 10:58:53 +02:00
|
|
|
color: var(--muted, #888);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Book folders (single level) --- */
|
|
|
|
|
.book-folder-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
.book-folder-tile {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 12px 8px;
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
background: none;
|
2026-08-17 12:12:17 +02:00
|
|
|
color: var(--fg);
|
2026-08-17 10:58:53 +02:00
|
|
|
cursor: pointer;
|
|
|
|
|
text-align: center;
|
2026-08-17 12:12:17 +02:00
|
|
|
font: inherit;
|
2026-08-17 10:58:53 +02:00
|
|
|
}
|
|
|
|
|
.book-folder-tile-icon {
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
}
|
|
|
|
|
.book-folder-tile-name {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
}
|
|
|
|
|
.book-folder-tile-count {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: var(--muted, #888);
|
|
|
|
|
}
|
|
|
|
|
.book-folder-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* --- PDF pages --- */
|
|
|
|
|
.pdf-page-wrapper {
|
|
|
|
|
margin: 0 auto 1rem;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
2026-03-20 20:02:29 +01:00
|
|
|
max-width: none;
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pdf-page {
|
|
|
|
|
display: block;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Reader overlay --- */
|
|
|
|
|
.reader-overlay {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: var(--nav-h);
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: var(--bar-h);
|
|
|
|
|
background: var(--bg);
|
|
|
|
|
z-index: 200;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
2026-08-04 13:28:19 +02:00
|
|
|
/* The global white text-outline (for readability over a custom background
|
|
|
|
|
image elsewhere in the app) is never needed here — the reader always
|
|
|
|
|
sits on its own opaque background — and it muddies highlighted text
|
|
|
|
|
badly when combined with a colored highlight background. */
|
|
|
|
|
text-shadow: none;
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
}
|
|
|
|
|
.reader-header {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
.reader-title {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
.reader-header-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.reader-progress-wrap {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
.reader-content {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: scroll;
|
2026-04-05 16:14:24 +02:00
|
|
|
overflow-x: auto;
|
2026-04-05 18:08:44 +02:00
|
|
|
position: relative;
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
padding: 24px 16px;
|
|
|
|
|
line-height: 1.8;
|
2026-09-12 15:05:08 +02:00
|
|
|
font-family: 'Source Serif 4', Georgia, 'Times New Roman', serif;
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
font-size: 16px;
|
2026-09-12 15:11:50 +02:00
|
|
|
font-weight: 380;
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
}
|
|
|
|
|
.reader-content > * {
|
|
|
|
|
max-width: 65ch;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
margin-right: auto;
|
|
|
|
|
}
|
|
|
|
|
.reader-content p {
|
|
|
|
|
margin-bottom: 1em;
|
|
|
|
|
}
|
|
|
|
|
.reader-content h1, .reader-content h2, .reader-content h3 {
|
|
|
|
|
margin: 1.4em 0 0.6em;
|
|
|
|
|
font-family: var(--font);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* --- Focus station sidebar --- */
|
2026-04-05 14:22:07 +02:00
|
|
|
/* --- Radio sidebar --- */
|
|
|
|
|
.rsb-nowplaying { margin-bottom: 12px; }
|
|
|
|
|
.rsb-station-name { font-weight: 600; }
|
|
|
|
|
.rsb-track { font-size: 0.85rem; margin-top: 2px; }
|
|
|
|
|
.rsb-controls { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; }
|
|
|
|
|
.rsb-vol { display: flex; align-items: center; gap: 6px; font-size: 0.85rem; color: var(--muted, #888); }
|
|
|
|
|
.rsb-station-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
|
|
|
|
|
/* --- Table of contents sidebar --- */
|
|
|
|
|
.toc-list {
|
|
|
|
|
list-style: none;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
}
|
|
|
|
|
.toc-entry {
|
|
|
|
|
display: block;
|
|
|
|
|
width: 100%;
|
|
|
|
|
text-align: left;
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
white-space: normal;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
.toc-entry:hover {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Focus station pending (playing interrupted) */
|
|
|
|
|
#focus-station-btn.focus-pending {
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
animation: focus-pulse 1.4s ease-in-out infinite;
|
|
|
|
|
}
|
|
|
|
|
@keyframes focus-pulse {
|
|
|
|
|
0%, 100% { opacity: 1; }
|
|
|
|
|
50% { opacity: 0.4; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
Reader feature additions
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
/* Reader themes */
|
|
|
|
|
.reader-theme-sepia .reader-content { background:#f5e6c8; color:#3b2a1a; }
|
|
|
|
|
.reader-theme-bright .reader-content { background:#fff; color:#111; }
|
|
|
|
|
.reader-content > * { max-width:var(--reader-max-width,65ch); margin-left:auto; margin-right:auto; }
|
2026-03-20 20:02:29 +01:00
|
|
|
.reader-content > .pdf-page-wrapper { max-width: none; margin: 0 auto 1rem; }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
|
|
|
|
|
/* Inline panels */
|
|
|
|
|
.reader-settings-panel, .reader-search-bar {
|
|
|
|
|
display:flex; align-items:center; gap:12px; padding:8px 16px;
|
|
|
|
|
border-bottom:1px solid var(--border); flex-shrink:0; flex-wrap:wrap; background:var(--bg);
|
|
|
|
|
}
|
|
|
|
|
.reader-settings-panel input[type="range"] { width:80px; }
|
|
|
|
|
|
2026-03-20 20:22:11 +01:00
|
|
|
/* PDF viewport wrapper — CSS zoom applied here for smooth zoom without re-render */
|
|
|
|
|
#pdf-viewport { transform-origin: top left; }
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* PDF inner container — shares origin with canvas so text layer aligns exactly */
|
|
|
|
|
.pdf-page-inner { position:relative; display:inline-block; line-height:0; }
|
|
|
|
|
|
|
|
|
|
/* PDF text layer — sits flush over the canvas inside pdf-page-inner */
|
2026-11-03 18:16:27 +01:00
|
|
|
.pdf-text-layer { position:absolute; top:0; left:0; width:100%; height:100%; overflow:hidden; pointer-events:none; line-height:1; -webkit-text-size-adjust:none; text-size-adjust:none; z-index:1; }
|
|
|
|
|
.pdf-text-layer span, .pdf-text-layer br { position:absolute; color:transparent !important; background:transparent; white-space:pre; cursor:text; pointer-events:auto; user-select:text; -webkit-user-select:text; line-height:1; transform-origin:0 0; }
|
|
|
|
|
.pdf-text-layer span::selection, .pdf-text-layer mark::selection { background:rgba(0,120,255,0.3); }
|
|
|
|
|
.pdf-text-layer br::selection { background:transparent; }
|
|
|
|
|
.pdf-text-layer mark.reader-search-match { color:transparent; background:rgba(241,196,15,.5); padding:0; border-radius:2px; }
|
|
|
|
|
.pdf-text-layer mark.reader-search-match.active { background:rgba(230,57,70,.6); }
|
|
|
|
|
/* PDF links — over the text, so a tap on one follows it */
|
|
|
|
|
.pdf-link-layer { position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:2; }
|
|
|
|
|
.pdf-link { position:absolute; display:block; pointer-events:auto; border-radius:2px; }
|
|
|
|
|
.pdf-link:hover, .pdf-link:focus-visible { background:rgba(0,120,255,0.15); outline:none; }
|
|
|
|
|
.pdf-back-btn { position:absolute; left:50%; bottom:calc(var(--bar-h, 0px) + 24px); transform:translateX(-50%); z-index:20; box-shadow:0 2px 8px rgba(0,0,0,.25); }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
|
|
|
|
|
/* PDF invert */
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
/* Dark pages: inverted, and the hues turned back, so photos and coloured figures aren't negatives. */
|
|
|
|
|
#reader-overlay.pdf-inverted .pdf-page { filter:invert(1) hue-rotate(180deg); }
|
PDF: öffnet an der gespeicherten Seite, Leserfarben, Seitenzahlen wie im Buch (sw CACHE v166)
Der Lesestand wird gleich beim Öffnen erfragt (parallel zur Datei), ein PDF zeichnet zuerst
die Seite, an der es weitergeht, statt Seite 1; Titel, Inhaltsverzeichnis und Seitenzahlen
kommen nebenher und halten das Öffnen nicht mehr auf. Eine Zeichnung, die schon läuft, wird
abgewartet statt neu begonnen.
„Leserfarben“ (neben Originalfarben und Invertieren): die Seite in den Farben des Lesers
(Dunkel, Sepia) per SVG-Filter, Bilder darüber wie gedruckt (ihre Lage aus den
Zeichenbefehlen); ein Bild, das die halbe Seite füllt, ist ein Scan und wird mit umgefärbt.
pdf.js' eigene pageColors färben die Bilder mit um. Ein anderes Design ändert nur die Farben
des Filters.
Seitenzahlen des Buchs (PageLabels: „iv“, „A-3“) im Kopf mit der Seite der Datei daneben,
eingeben lässt sich beides; Lesezeichen und der Zurück-Knopf nennen sie. Das Test-PDF hat
dafür Seitenzahlen und ein farbiges Bild bekommen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 18:42:26 +01:00
|
|
|
/* Leserfarben (58-pdf.js _pdfColorFilter): the page recoloured, its pictures over it as printed */
|
|
|
|
|
.pdf-page.pdf-recolored { filter:url(#pdf-reader-colors); }
|
|
|
|
|
.pdf-image-layer { position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; }
|
|
|
|
|
.pdf-image-layer canvas { position:absolute; display:block; }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
|
2026-04-01 15:24:00 +02:00
|
|
|
/* ---- Immersive reader mode ---- */
|
|
|
|
|
.navbar { transition: transform 0.35s ease; }
|
|
|
|
|
.now-playing-bar{ transition: transform 0.35s ease; }
|
|
|
|
|
.reader-overlay { transition: top 0.35s ease, bottom 0.35s ease; }
|
|
|
|
|
.reader-header { overflow: hidden; max-height: 60px;
|
|
|
|
|
transition: max-height 0.35s ease, padding-top 0.35s ease, padding-bottom 0.35s ease; }
|
|
|
|
|
|
|
|
|
|
body.reader-immersive .navbar { transform: translateY(-100%); }
|
|
|
|
|
body.reader-immersive .now-playing-bar { transform: translateY(100%); }
|
|
|
|
|
body.reader-immersive .reader-overlay { top: 0; bottom: 0; }
|
|
|
|
|
body.reader-immersive .reader-header { max-height: 0; padding-top: 0; padding-bottom: 0; }
|
|
|
|
|
|
|
|
|
|
body.reader-immersive.reader-show-top .navbar { transform: none; }
|
|
|
|
|
body.reader-immersive.reader-show-top .reader-overlay { top: var(--nav-h); }
|
|
|
|
|
body.reader-immersive.reader-show-top .reader-header { max-height: 60px; padding-top: 8px; padding-bottom: 8px; }
|
|
|
|
|
|
|
|
|
|
body.reader-immersive.reader-show-bottom .now-playing-bar { transform: none; }
|
|
|
|
|
body.reader-immersive.reader-show-bottom .reader-overlay { bottom: var(--bar-h); }
|
2026-10-14 18:26:40 +02:00
|
|
|
/* Reading aloud: the player bar is its remote control and stays, as in the apps. */
|
|
|
|
|
html.nui-tts-on body.reader-immersive .now-playing-bar { transform: none; }
|
|
|
|
|
html.nui-tts-on body.reader-immersive .reader-overlay { bottom: var(--bar-h); }
|
2026-03-21 18:04:11 +01:00
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* PDF paginated */
|
2026-11-03 18:07:18 +01:00
|
|
|
/* Page by page the page fills the whole reading area, without its padding: 66-pdf-paginated.js
|
|
|
|
|
measures the area and places the page by its text box itself. */
|
|
|
|
|
.reader-content.pdf-paginated { overflow:hidden !important; display:block; padding:0 !important; }
|
|
|
|
|
.reader-content.pdf-paginated > #pdf-viewport { width:100%; height:100%; max-width:none; margin:0; }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
.pdf-paginated .pdf-page-wrapper { margin:0; }
|
2026-11-03 18:07:18 +01:00
|
|
|
.pdf-paginated .pdf-page-wrapper { position:relative; }
|
|
|
|
|
.pdf-paginated .pdf-page-inner { position:absolute; left:0; top:0; }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
|
2026-04-05 18:08:44 +02:00
|
|
|
/* PDF loading overlay */
|
|
|
|
|
.pdf-loading-overlay {
|
2026-04-05 18:29:46 +02:00
|
|
|
position: fixed;
|
2026-04-05 18:08:44 +02:00
|
|
|
inset: 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-10-14 18:02:33 +02:00
|
|
|
background: var(--scrim);
|
2026-04-05 18:29:46 +02:00
|
|
|
z-index: 200;
|
2026-04-05 18:08:44 +02:00
|
|
|
}
|
|
|
|
|
.pdf-loading-spinner {
|
|
|
|
|
width: 36px;
|
|
|
|
|
height: 36px;
|
2026-10-14 18:02:33 +02:00
|
|
|
border: 3px solid var(--raised);
|
2026-04-05 18:08:44 +02:00
|
|
|
border-top-color: var(--accent, #e63946);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
animation: pdf-spin 0.7s linear infinite;
|
|
|
|
|
}
|
|
|
|
|
@keyframes pdf-spin { to { transform: rotate(360deg); } }
|
2026-10-19 14:57:03 +02:00
|
|
|
.reader-opening { display: flex; flex-direction: column; align-items: center; gap: 16px; }
|
2026-04-05 18:08:44 +02:00
|
|
|
|
2026-04-05 13:18:25 +02:00
|
|
|
/* PDF two-page spread */
|
|
|
|
|
.pdf-spread-wrapper { display:flex; flex-direction:row; gap:8px; justify-content:center; margin-bottom:1rem; }
|
|
|
|
|
.pdf-spread-wrapper .pdf-page-wrapper { margin:0; }
|
|
|
|
|
.pdf-spread-cover { margin-bottom:1rem; }
|
|
|
|
|
/* Disable text selection during pinch */
|
|
|
|
|
#reader-content.pinch-active { user-select:none; -webkit-user-select:none; }
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* Highlight popover */
|
|
|
|
|
.highlight-popover { position:fixed; z-index:500; display:flex; gap:6px; background:var(--bg-card,#1a1a1a); border:1px solid var(--border); border-radius:var(--radius); padding:6px 8px; box-shadow:0 4px 16px rgba(0,0,0,.5); }
|
|
|
|
|
.hl-color-btn { width:24px; height:24px; border-radius:50%; border:2px solid transparent; cursor:pointer; font-weight:700; font-size:12px; color:#000; line-height:1; }
|
2026-10-14 18:02:33 +02:00
|
|
|
.hl-color-btn:hover { border-color:var(--fg); }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
.hl-note-btn { background:none; border:1px solid var(--border); color:var(--fg); padding:2px 6px; border-radius:var(--radius); cursor:pointer; }
|
|
|
|
|
|
2026-05-29 21:04:47 +02:00
|
|
|
/* Footnote popover */
|
2026-05-29 21:21:43 +02:00
|
|
|
.footnote-popover { position:fixed; z-index:500; max-width:min(400px,90vw); max-height:220px; overflow-y:auto; background:#1a1a1a; color:#e8e8e8; border:1px solid #444; border-radius:var(--radius); padding:10px 28px 10px 12px; box-shadow:0 4px 20px rgba(0,0,0,.6); font-size:0.88em; line-height:1.55; pointer-events:auto; }
|
|
|
|
|
.footnote-popover.reader-theme-sepia { background:#ede0c4; color:#3b2a1a; border-color:#c8b89a; }
|
|
|
|
|
.footnote-popover.reader-theme-bright { background:#f0f0f0; color:#111; border-color:#bbb; }
|
|
|
|
|
.footnote-popover-close { position:absolute; top:4px; right:6px; background:none; border:none; color:inherit; opacity:0.5; cursor:pointer; font-size:14px; padding:2px 4px; line-height:1; }
|
Lesen in Seiten auch im Browser und auf dem Desktop, mit Fußnoten unten auf der Seite und Doppelseite
63-pages.js (DioraPages): die Seitenansicht des E-Reader-Modus der Android-Apps, jetzt für alle –
Seitenumbrüche vor angeschnittenen Zeilen, ein Kapitel auf einmal, Fußnoten am Seitenfuß wie im
gedruckten Buch, Blättern per Tippen/Klick am Rand, Wischen, Pfeil-/Bild-/Leertaste und Mausrad.
Neu: Doppelseite (zwei Seiten nebeneinander ab 900 px Breite). Einstellung „Scrollen | Seiten“
(+ „Fußnoten unten“, „Doppelseite“) im Schrift-&-Layout-Panel, je Gerät.
Fußnoten: Erkennung und Auflösung aus diora-android native-ui.js übernommen (40-epub.js,
78-footnotes.js) – die Notiz wird im richtigen Kapitel gesucht (IDs wiederholen sich je Datei),
auch das Popover zeigt jetzt genau die Notiz. Vorbereitete Bücher werden einmal neu aufbereitet.
Behoben gegenüber der App-Fassung: ein Verweis in der ersten Zeile einer Seite (hochgestellt)
zählte zur Vorseite, und ein Verweis, der samt Notiz nicht mehr passte, blieb ohne Notiz stehen –
jetzt wandert er mit ihr auf die nächste Seite. Schnelles Blättern löst „Weit gescrollt“ nicht mehr aus.
sw CACHE v157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:41:27 +01:00
|
|
|
.footnote-popover > :first-child { margin-top:0; }
|
|
|
|
|
.footnote-popover > :last-child { margin-bottom:0; }
|
|
|
|
|
|
|
|
|
|
/* ---- Reading in pages (63-pages.js) ----
|
|
|
|
|
Only the current chapter is laid out; the others stay in the DOM (block
|
|
|
|
|
indices, search, notes, anchors). No free scrolling: the pager moves. */
|
|
|
|
|
#reader-content.nui-window > [data-epub-src]:not(.nui-ch-current) { display:none; }
|
|
|
|
|
/* Room below the chapter, so its last page can show just the remaining text
|
|
|
|
|
(then the page's colour) instead of scrolling back over lines already read. */
|
|
|
|
|
#reader-content.nui-window::after { content:""; display:block; height:100vh; }
|
|
|
|
|
#reader-content.nui-window { overflow-y:hidden; -ms-overflow-style:none; scrollbar-width:none; }
|
|
|
|
|
#reader-content.nui-window::-webkit-scrollbar { display:none; }
|
|
|
|
|
/* Masks: the line cut at the page's bottom and whatever sits in the top gap. */
|
|
|
|
|
.nui-page-mask { position:fixed; z-index:250; pointer-events:none; display:none; }
|
|
|
|
|
/* Footnotes at the foot of the page, like a printed book. */
|
|
|
|
|
.nui-page-notes { position:fixed; z-index:255; overflow:hidden; padding:0 0 4px; box-sizing:border-box;
|
|
|
|
|
line-height:1.4; font-weight:400; cursor:pointer; }
|
|
|
|
|
.nui-pn-rule { width:30%; border-top:1px solid currentColor; margin:2px 0 6px; }
|
|
|
|
|
.nui-pn-item { margin:0 0 4px; padding-left:1.6em; text-indent:-1.6em; }
|
|
|
|
|
.nui-pn-item p, .nui-pn-item div, .nui-pn-item aside, .nui-pn-item li { display:inline; margin:0; padding:0; text-indent:0; }
|
|
|
|
|
.nui-pn-item p + p::before { content:" "; }
|
|
|
|
|
.nui-pn-label { display:inline-block; min-width:1.6em; text-indent:0; font-weight:700; }
|
|
|
|
|
.nui-pn-item a { color:inherit; text-decoration:none; }
|
|
|
|
|
.nui-pn-item img { display:none; }
|
|
|
|
|
/* Cut off: say so, a tap shows the whole note. */
|
|
|
|
|
.nui-page-notes.nui-pn-cut::after { content:"… mehr"; position:absolute; right:0; bottom:2px; padding-left:8px;
|
|
|
|
|
background:inherit; font-weight:700; }
|
|
|
|
|
/* Two pages side by side (a spread, 63-pages.js): the right one is a copy of
|
|
|
|
|
the chapter, scrolled to the page after the left one. */
|
|
|
|
|
#reader-spread-page { display:none; }
|
|
|
|
|
#reader-overlay.reader-spread #reader-content,
|
|
|
|
|
#reader-overlay.reader-spread #reader-spread-page { flex:1 1 0; min-width:0; }
|
|
|
|
|
#reader-overlay.reader-spread #reader-spread-page { display:block; overflow:hidden; box-shadow:inset 1px 0 0 var(--border);
|
|
|
|
|
user-select:none; -webkit-user-select:none; cursor:default; }
|
|
|
|
|
#reader-spread-page::after { content:""; display:block; height:100vh; }
|
Nachrichten und Blogs im Buch-Reader, Doppelklick für Vollbild, Hörbücher wie die Bücher-Bibliothek
65-article.js (DioraArticle): Artikel öffnen im selben Reader wie Bücher – Schrift, Farben,
Seiten/Doppelseite mit Fußnoten am Seitenfuß, Suche, Vorlesen, Vollbild. Lesezeichen,
Markierungen und Notizspalte bleiben Büchern vorbehalten. Merken, Projekt und Original
stehen in der Kopfzeile (Apps: im Lesemenü). feeds.js gibt sein eigenes Overlay auf;
Fortschritt wie bisher (bis zum unteren Bildschirmrand gelesen, ab 90 % gelesen).
Einstellungen der Artikel gelten für alle Artikel zusammen.
62-reader.js: Doppelklick (Maus) auf den Text schaltet Vollbild an/aus, die Leisten gehen mit.
audiobooks.js: die Hörbücher sind gebaut wie die Bücher – Kopf mit Suche, Sortierung und +,
Chips Angefangen/Ungehört/Gehört und Ordner, „Weiterhören“ wie „Weiterlesen“, Kacheln aus
denselben Bausteinen (53-library.js gibt seine Styles auch an #audiobook-shelf), Cover quadratisch.
63-pages.js: bei nur einem Kapitel zeigt die Fußzeile nur die Seiten.
sw CACHE v158
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 20:15:00 +01:00
|
|
|
/* An article in the reader (65-article.js): what only books have stays out. */
|
|
|
|
|
#reader-overlay.reader-article-mode .reader-progress-wrap,
|
|
|
|
|
#reader-overlay.reader-article-mode #reader-bookmark-btn,
|
|
|
|
|
#reader-overlay.reader-article-mode #reader-bm-list-btn,
|
|
|
|
|
#reader-overlay.reader-article-mode #reader-toc-btn,
|
|
|
|
|
#reader-overlay.reader-article-mode #reader-margin-btn,
|
|
|
|
|
#reader-overlay.reader-article-mode #reader-reset-pos-btn,
|
|
|
|
|
#reader-overlay.reader-article-mode .marker-mode-btn,
|
|
|
|
|
#reader-overlay.reader-article-mode #reader-margin,
|
|
|
|
|
#reader-overlay.reader-article-mode .nui-toc { display:none !important; }
|
|
|
|
|
.reader-article-actions { display:inline-flex; gap:10px; align-items:center; }
|
Lesen in Seiten auch im Browser und auf dem Desktop, mit Fußnoten unten auf der Seite und Doppelseite
63-pages.js (DioraPages): die Seitenansicht des E-Reader-Modus der Android-Apps, jetzt für alle –
Seitenumbrüche vor angeschnittenen Zeilen, ein Kapitel auf einmal, Fußnoten am Seitenfuß wie im
gedruckten Buch, Blättern per Tippen/Klick am Rand, Wischen, Pfeil-/Bild-/Leertaste und Mausrad.
Neu: Doppelseite (zwei Seiten nebeneinander ab 900 px Breite). Einstellung „Scrollen | Seiten“
(+ „Fußnoten unten“, „Doppelseite“) im Schrift-&-Layout-Panel, je Gerät.
Fußnoten: Erkennung und Auflösung aus diora-android native-ui.js übernommen (40-epub.js,
78-footnotes.js) – die Notiz wird im richtigen Kapitel gesucht (IDs wiederholen sich je Datei),
auch das Popover zeigt jetzt genau die Notiz. Vorbereitete Bücher werden einmal neu aufbereitet.
Behoben gegenüber der App-Fassung: ein Verweis in der ersten Zeile einer Seite (hochgestellt)
zählte zur Vorseite, und ein Verweis, der samt Notiz nicht mehr passte, blieb ohne Notiz stehen –
jetzt wandert er mit ihr auf die nächste Seite. Schnelles Blättern löst „Weit gescrollt“ nicht mehr aus.
sw CACHE v157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:41:27 +01:00
|
|
|
/* The browser's line under the page (the apps have their own footer). */
|
|
|
|
|
#reader-page-footer { display:flex; justify-content:space-between; gap:12px; flex-shrink:0; padding:4px 16px 6px;
|
|
|
|
|
font-size:12px; color:var(--muted,#888); font-variant-numeric:tabular-nums; }
|
|
|
|
|
.reader-theme-sepia #reader-page-footer { background:#f5e6c8; color:#7a6650; }
|
|
|
|
|
.reader-theme-bright #reader-page-footer { background:#fff; color:#777; }
|
2026-05-29 21:04:47 +02:00
|
|
|
|
2026-10-08 18:51:05 +02:00
|
|
|
/* Page numbers of the printed edition (epub:type="pagebreak") */
|
|
|
|
|
.reader-content .epub-pagenum { display:inline-block; font-size:0.62em; line-height:1.3; font-weight:400; font-style:normal; letter-spacing:0; text-transform:none; vertical-align:0.35em; padding:0 0.35em; margin-right:0.4em; border:1px solid currentColor; border-radius:3px; opacity:0.45; user-select:none; }
|
|
|
|
|
.reader-content .epub-pagenum:empty { display:none; }
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* Highlight marks */
|
2026-09-28 20:25:59 +02:00
|
|
|
.reader-no-bold * { font-weight: inherit !important; }
|
2026-04-29 14:12:06 +02:00
|
|
|
.reader-content img { max-width: 100%; height: auto; display: block; }
|
2026-08-04 18:44:09 +02:00
|
|
|
/* color:inherit overrides the browser's UA-default `mark { color: black }` —
|
|
|
|
|
without it, highlighted text abruptly flips to black regardless of the
|
|
|
|
|
reader theme's own text color (jarring on the default dark theme, whose
|
|
|
|
|
body text is white). Inheriting keeps highlighted text the same color as
|
|
|
|
|
the text around it in every theme; the background tones below are chosen
|
|
|
|
|
dark/saturated enough to stay readable specifically against white text. */
|
|
|
|
|
.epub-highlight { border-radius:2px; cursor:pointer; color:inherit; }
|
|
|
|
|
.epub-highlight[data-color="yellow"] { background:rgba(184,134,11,.55); }
|
|
|
|
|
.epub-highlight[data-color="green"] { background:rgba(30,140,90,.55); }
|
|
|
|
|
.epub-highlight[data-color="blue"] { background:rgba(41,110,180,.55); }
|
|
|
|
|
.epub-highlight[data-color="red"] { background:rgba(190,50,60,.55); }
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
|
2026-08-04 10:49:30 +02:00
|
|
|
/* Margin ("page margin") panel — left of the reader content. Highlights without
|
|
|
|
|
a note show as a small color dot; anything with note text (freeform notes or
|
|
|
|
|
a highlight+note) shows as a readable little "post-it" card, positioned next
|
|
|
|
|
to the text height it belongs to. Click empty space to place a new freeform
|
|
|
|
|
note anchored to that line. Freeform notes have NO in-text mark — the anchor
|
|
|
|
|
is purely positional, only ever visible here in the margin. */
|
2026-08-04 10:17:01 +02:00
|
|
|
.reader-body-row { display:flex; flex:1; min-height:0; }
|
|
|
|
|
.reader-margin {
|
2026-10-14 19:43:03 +02:00
|
|
|
--reader-margin-w:220px; /* read by app.js _centerReaderColumn */
|
2026-08-04 10:17:01 +02:00
|
|
|
width:0; flex-shrink:0; overflow:hidden;
|
|
|
|
|
display:flex; flex-direction:column;
|
|
|
|
|
border-right:1px solid var(--border); background:var(--bg);
|
|
|
|
|
transition:width 0.25s ease;
|
|
|
|
|
}
|
2026-10-14 19:43:03 +02:00
|
|
|
.reader-margin.open { width:var(--reader-margin-w); }
|
|
|
|
|
/* The text column keeps its place while the margin opens (app.js _centerReaderColumn). */
|
|
|
|
|
.reader-content { transition: padding-right 0.25s ease; }
|
2026-08-04 10:17:01 +02:00
|
|
|
.reader-margin-header {
|
|
|
|
|
display:none; align-items:center; justify-content:space-between;
|
|
|
|
|
padding:6px 8px; border-bottom:1px solid var(--border); white-space:nowrap; flex-shrink:0;
|
|
|
|
|
}
|
|
|
|
|
.reader-margin.open .reader-margin-header { display:flex; }
|
|
|
|
|
.reader-margin-title { font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted,#888); }
|
2026-08-04 11:23:16 +02:00
|
|
|
.reader-margin-header-actions { display:flex; align-items:center; gap:6px; }
|
2026-10-14 18:15:58 +02:00
|
|
|
.reader-margin-markers { position:relative; flex:1; overflow:hidden; cursor:crosshair; contain:layout paint; }
|
|
|
|
|
/* The cards in book coordinates; scrolling moves this layer (app.js _repositionMarginMarkers). */
|
|
|
|
|
.margin-layer { position:absolute; top:0; left:0; right:0; height:0; will-change:transform; }
|
2026-08-04 10:49:30 +02:00
|
|
|
|
|
|
|
|
.margin-note { position:absolute; left:8px; right:8px; cursor:pointer; }
|
|
|
|
|
|
|
|
|
|
.margin-note-dot { left:8px; right:auto; width:12px; height:12px; border-radius:50%; }
|
|
|
|
|
.margin-note-dot[data-color="yellow"] { background:rgba(241,196,15,.85); }
|
|
|
|
|
.margin-note-dot[data-color="green"] { background:rgba(46,204,113,.85); }
|
|
|
|
|
.margin-note-dot[data-color="blue"] { background:rgba(52,152,219,.85); }
|
|
|
|
|
.margin-note-dot[data-color="red"] { background:rgba(230,57,70,.85); }
|
|
|
|
|
|
|
|
|
|
.margin-note-text {
|
|
|
|
|
font-size:11px; line-height:1.4; padding:5px 7px; border-radius:3px;
|
|
|
|
|
background:var(--bg-card,#1a1a1a); border-left:3px solid var(--muted,#888);
|
|
|
|
|
box-shadow:1px 2px 5px rgba(0,0,0,.3);
|
|
|
|
|
white-space:pre-wrap; word-break:break-word;
|
|
|
|
|
}
|
|
|
|
|
.margin-note-text[data-color="yellow"] { border-left-color:#f1c40f; }
|
|
|
|
|
.margin-note-text[data-color="green"] { border-left-color:#2ecc71; }
|
|
|
|
|
.margin-note-text[data-color="blue"] { border-left-color:#3498db; }
|
|
|
|
|
.margin-note-text[data-color="red"] { border-left-color:#e63946; }
|
|
|
|
|
/* Freeform notes get a paper-like tint, like a post-it note pinned to the text */
|
|
|
|
|
.margin-note-text.margin-note-freeform {
|
|
|
|
|
border-left-color:#e6c229; background:rgba(241,196,15,.13);
|
|
|
|
|
}
|
2026-08-04 10:17:01 +02:00
|
|
|
|
2026-08-04 11:23:16 +02:00
|
|
|
/* Inline note editor — appears right where the note lives in the margin, so
|
|
|
|
|
setting/editing a note never means jumping to a separate panel */
|
|
|
|
|
.margin-note-editor { position:absolute; left:8px; right:8px; z-index:1; }
|
|
|
|
|
.margin-note-textarea {
|
|
|
|
|
width:100%; min-height:52px; font-size:11px; line-height:1.4; padding:5px 7px;
|
|
|
|
|
border-radius:3px; border:1px solid var(--accent,#e63946);
|
|
|
|
|
background:var(--bg-card,#1a1a1a); color:var(--fg,#eee);
|
|
|
|
|
box-shadow:1px 2px 5px rgba(0,0,0,.4); resize:vertical;
|
|
|
|
|
}
|
|
|
|
|
|
Reader: Notizen auf Mobile schnell setzbar statt über Randspalte (SW v32)
Auf schmalen Screens saß der Marker-Modus-Button bisher nur in der 150px-
Randspalte — man musste sie erst öffnen (und damit den ohnehin knappen
Lesebereich verkleinern), um überhaupt markieren zu können. Jetzt zusätzlich
ein Marker-Button direkt im Haupt-Header (nur <=600px sichtbar, Desktop
bleibt bei der bisherigen Anordnung in der Notes-Leiste).
Im Marker-Modus legt ein einfacher Tap auf reinen Lesetext (ohne Auswahl)
jetzt direkt eine freie Notiz an der Wortposition an — man muss nicht mehr
den exakten Klick-Punkt in der Randspalte treffen. Das Bearbeiten der
Notiz läuft auf schmalen Viewports über ein neues Bottom-Sheet (volle
Breite) statt über das Inline-Textfeld in der 150px-Spalte, das auf dem
Handy-Keyboard unangenehm zu bedienen ist.
Notiz-Objekte werden jetzt erst bei tatsächlich eingegebenem Text angelegt
und gespeichert (vorher: sofort beim Setzen des Ankers) — verhindert, dass
ein abgebrochener/leerer Tap eine Ghost-Notiz in currentHighlights
hinterlässt. Gilt für alle Eingabewege (Bottom-Sheet UND die bestehende
Inline-Randspalten-Bearbeitung), per Playwright verifiziert inkl. Escape-
Cancel auf Desktop.
Priorität laut Nutzer: Notizen-Erfassung muss auf Mobile gut funktionieren;
dass bestehende Notizen dort weniger prominent sichtbar sind, ist explizit
in Ordnung — die vertiefte Auseinandersetzung mit Notizen bleibt eine
Desktop-Aufgabe.
Verifiziert per Playwright (390×844 mobil + 1280×900 Desktop-Regression):
Marker-Button-Sichtbarkeit, Bottom-Sheet-Flow (Erstellen/Abbrechen/Speichern,
kein Ghost-Eintrag), reale Text-Selektion + Highlight+Notiz weiterhin
funktionsfähig auf Mobile, Desktop-Workflow (Randspalte, Inline-Editor,
Escape-Cancel, Blur-Commit) unverändert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 20:13:55 +02:00
|
|
|
/* Note bottom sheet — the mobile alternative to the inline margin editor.
|
|
|
|
|
Typing a note into a 150px-wide margin column is unpleasant on a phone
|
|
|
|
|
keyboard, and getting there first requires opening that column at all, so
|
|
|
|
|
on narrow viewports note editing uses this full-width sheet instead (see
|
|
|
|
|
_isMobileReader() / _openNoteEditor() in app.js). Existing notes staying
|
|
|
|
|
less discoverable on mobile is an accepted tradeoff — reviewing them in
|
|
|
|
|
depth is expected to happen on desktop; setting them fast is what matters
|
|
|
|
|
here. */
|
|
|
|
|
.note-bottom-sheet { position:fixed; inset:0; z-index:650; }
|
|
|
|
|
.note-bottom-sheet-backdrop {
|
2026-10-14 18:02:33 +02:00
|
|
|
position:absolute; inset:0; background:var(--scrim);
|
Reader: Notizen auf Mobile schnell setzbar statt über Randspalte (SW v32)
Auf schmalen Screens saß der Marker-Modus-Button bisher nur in der 150px-
Randspalte — man musste sie erst öffnen (und damit den ohnehin knappen
Lesebereich verkleinern), um überhaupt markieren zu können. Jetzt zusätzlich
ein Marker-Button direkt im Haupt-Header (nur <=600px sichtbar, Desktop
bleibt bei der bisherigen Anordnung in der Notes-Leiste).
Im Marker-Modus legt ein einfacher Tap auf reinen Lesetext (ohne Auswahl)
jetzt direkt eine freie Notiz an der Wortposition an — man muss nicht mehr
den exakten Klick-Punkt in der Randspalte treffen. Das Bearbeiten der
Notiz läuft auf schmalen Viewports über ein neues Bottom-Sheet (volle
Breite) statt über das Inline-Textfeld in der 150px-Spalte, das auf dem
Handy-Keyboard unangenehm zu bedienen ist.
Notiz-Objekte werden jetzt erst bei tatsächlich eingegebenem Text angelegt
und gespeichert (vorher: sofort beim Setzen des Ankers) — verhindert, dass
ein abgebrochener/leerer Tap eine Ghost-Notiz in currentHighlights
hinterlässt. Gilt für alle Eingabewege (Bottom-Sheet UND die bestehende
Inline-Randspalten-Bearbeitung), per Playwright verifiziert inkl. Escape-
Cancel auf Desktop.
Priorität laut Nutzer: Notizen-Erfassung muss auf Mobile gut funktionieren;
dass bestehende Notizen dort weniger prominent sichtbar sind, ist explizit
in Ordnung — die vertiefte Auseinandersetzung mit Notizen bleibt eine
Desktop-Aufgabe.
Verifiziert per Playwright (390×844 mobil + 1280×900 Desktop-Regression):
Marker-Button-Sichtbarkeit, Bottom-Sheet-Flow (Erstellen/Abbrechen/Speichern,
kein Ghost-Eintrag), reale Text-Selektion + Highlight+Notiz weiterhin
funktionsfähig auf Mobile, Desktop-Workflow (Randspalte, Inline-Editor,
Escape-Cancel, Blur-Commit) unverändert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 20:13:55 +02:00
|
|
|
opacity:0; transition:opacity .2s ease; pointer-events:none;
|
|
|
|
|
}
|
|
|
|
|
.note-bottom-sheet.open .note-bottom-sheet-backdrop { opacity:1; pointer-events:auto; }
|
|
|
|
|
.note-bottom-sheet-panel {
|
|
|
|
|
position:absolute; left:0; right:0; bottom:0;
|
|
|
|
|
background:var(--surface,#111); border-top:1px solid var(--border,#333);
|
|
|
|
|
border-radius:12px 12px 0 0; padding:12px 14px calc(14px + env(safe-area-inset-bottom));
|
|
|
|
|
transform:translateY(100%); transition:transform .25s ease;
|
|
|
|
|
display:flex; flex-direction:column; gap:10px;
|
|
|
|
|
}
|
|
|
|
|
.note-bottom-sheet.open .note-bottom-sheet-panel { transform:translateY(0); }
|
|
|
|
|
.note-bottom-sheet-textarea {
|
|
|
|
|
width:100%; min-height:110px; font-size:15px; line-height:1.4; padding:8px 10px;
|
|
|
|
|
border-radius:var(--radius); border:1px solid var(--border,#333);
|
|
|
|
|
background:var(--bg-card,#1a1a1a); color:var(--fg,#eee); resize:vertical;
|
|
|
|
|
}
|
|
|
|
|
.note-bottom-sheet-actions { display:flex; justify-content:flex-end; gap:8px; }
|
|
|
|
|
|
2026-08-04 10:17:01 +02:00
|
|
|
@media (max-width: 600px) {
|
2026-10-14 19:43:03 +02:00
|
|
|
.reader-margin { --reader-margin-w:150px; }
|
2026-08-04 10:17:01 +02:00
|
|
|
.reader-margin-title { display:none; }
|
|
|
|
|
}
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* Search matches */
|
|
|
|
|
mark.reader-search-match { background:rgba(241,196,15,.6); color:inherit; border-radius:2px; }
|
|
|
|
|
mark.reader-search-match.active { background:rgba(230,57,70,.7); }
|
|
|
|
|
#rs-search-count { font-size:12px; min-width:50px; }
|
|
|
|
|
|
2026-08-31 22:29:13 +02:00
|
|
|
/* Read-aloud (TTS) */
|
|
|
|
|
mark.tts-current { background:rgba(230,57,70,.55); color:inherit; border-radius:2px; }
|
|
|
|
|
.tts-bar { position:fixed; bottom:calc(var(--bar-h) + 16px); left:50%; transform:translateX(-50%); display:flex; align-items:center; gap:14px; background:var(--bg-card,#1a1a1a); border:1px solid var(--border); border-radius:var(--radius); padding:8px 16px; box-shadow:0 4px 16px rgba(0,0,0,.5); z-index:600; }
|
|
|
|
|
.tts-bar button { background:none; border:none; color:inherit; font-size:16px; cursor:pointer; padding:2px 4px; line-height:1; }
|
|
|
|
|
.tts-bar button:hover { opacity:0.7; }
|
|
|
|
|
|
Add ebook reader features: highlights, bookmarks, search, settings, PDF paginated mode
- Backend: EBookHighlights and EBookBookmarks models with encrypted blob storage;
GET/POST views with size guards (700 KB / 100 KB); migration applied
- Reader header: search, settings, bookmark add/list buttons
- Font & layout settings panel (font size, line height, max width, themes for EPUB;
zoom, invert, paginated for PDF); persisted in localStorage
- Bookmarks: encrypted per-book blob, toast on add, sidebar with jump/delete
- Full-text search: EPUB TreeWalker mark injection, PDF span search; Ctrl+F / F3;
arrow key cycling; highlights re-applied on search clear
- PDF paginated mode: single-page view, tap-zone / swipe / arrow key navigation,
smart zoom (text bounding box → scale+translate canvas), auto-enable on mobile
- Progress tracking fixes: save before hiding overlay (was always writing 100%),
wait for EPUB images to load before restoring scroll, PDF paginated uses page
fraction, sendBeacon cache for unload/visibilitychange reliability
- PDF text layer disabled pending overlay rendering fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 13:08:42 +01:00
|
|
|
/* Bookmarks sidebar */
|
|
|
|
|
.bookmark-entry { display:flex; width:100%; padding:6px 0; font-size:13px; justify-content:space-between; border-bottom:1px solid var(--border); }
|
|
|
|
|
|
|
|
|
|
/* Toast */
|
|
|
|
|
.reader-toast { position:fixed; bottom:calc(var(--bar-h) + 16px); left:50%; transform:translateX(-50%); background:var(--fg); color:var(--bg); padding:6px 14px; border-radius:var(--radius); font-size:13px; z-index:600; animation:toast-fade 2s ease forwards; pointer-events:none; }
|
|
|
|
|
@keyframes toast-fade { 0%,70%{opacity:1} 100%{opacity:0} }
|
2026-03-19 21:35:41 +01:00
|
|
|
|
2026-08-04 09:01:52 +02:00
|
|
|
.reader-toast-action { display:flex; align-items:center; gap:10px; padding:8px 8px 8px 14px; animation:toast-slide-up 0.2s ease forwards; pointer-events:auto; }
|
2026-10-14 18:02:33 +02:00
|
|
|
/* The apps: the player bar sits in the reader, above its footer; notices go above both. */
|
|
|
|
|
html.nui-reading .reader-toast { bottom:calc(var(--nui-dock-h, 0px) + 40px); }
|
|
|
|
|
.reader-toast-btn { background:var(--accent,#e63946); color:var(--on-accent); border:none; border-radius:calc(var(--radius) - 2px); padding:5px 12px; font-family:var(--font); font-size:13px; font-weight:600; cursor:pointer; white-space:nowrap; }
|
2026-08-04 09:01:52 +02:00
|
|
|
.reader-toast-btn:hover { filter:brightness(1.1); }
|
|
|
|
|
@keyframes toast-slide-up { 0%{opacity:0; transform:translate(-50%, 8px);} 100%{opacity:1; transform:translate(-50%, 0);} }
|
|
|
|
|
|
2026-03-19 21:35:41 +01:00
|
|
|
.build-time {
|
|
|
|
|
position: fixed;
|
|
|
|
|
bottom: 4px;
|
|
|
|
|
right: 6px;
|
|
|
|
|
font-size: 0.65rem;
|
2026-10-14 18:02:33 +02:00
|
|
|
color: var(--border-strong);
|
2026-03-19 21:35:41 +01:00
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}
|