novela/containers/novela/templates/editor.html
Ivo Oskamp 91f8380a1f Release v0.2.9
Reader: monotonic reading progress across devices — saved position only
advances, never rewinds (explicit Mark as read/unread still resets).

Plus the previously uncommitted v0.2.5–v0.2.8 work (FlareSolverr scraping,
Book Info pages, deferred chapter add/delete, scanned/uploaded backup
counters, Dropbox upload tuning, four inline editor formatting buttons,
migration logging, "New view" needs_review fix, consecutive break-image
collapsing, and the related TECHNICAL.md updates).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 11:50:49 +02:00

118 lines
5.6 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Novela{% if develop_mode() %} Develop{% endif %} — Edit {{ title or filename }}</title>
<link rel="icon" href="/static/favicon.ico" sizes="16x16"/>
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32.png"/>
<link rel="icon" type="image/png" sizes="256x256" href="/static/favicon-256.png"/>
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png"/>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&display=swap" rel="stylesheet"/>
<link rel="stylesheet" href="/static/theme.css"/>
<link rel="stylesheet" href="/static/editor.css"/>
</head>
<body>
<!-- Header -->
<div class="editor-header">
<a class="header-back" href="/library/book/{{ filename | urlencode }}">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
<polyline points="15 18 9 12 15 6"/>
</svg>
{{ (title or filename) | truncate(30, True) }}
</a>
<div class="header-chapter" id="header-chapter"></div>
<input class="chapter-title-input" id="chapter-title-input" type="text" placeholder="Chapter title…" style="display:none"/>
<div class="header-actions">
<button class="btn-add-page" id="btn-add-page" onclick="addChapter()" title="Add new chapter after current">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 5v14"/><path d="M5 12h14"/>
</svg>
Add page
</button>
<button class="btn-del-page" id="btn-del-page" onclick="deleteChapter()" title="Delete current chapter" disabled>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 6h18"/><path d="M8 6V4h8v2"/><path d="M7 6l1 14h8l1-14"/>
</svg>
Delete page
</button>
<button class="btn-break" id="btn-break" onclick="insertBreak()" title="Insert scene break at cursor" disabled>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="3" y1="12" x2="9" y2="12"/>
<circle cx="12" cy="12" r="2" fill="currentColor" stroke="none"/>
<line x1="15" y1="12" x2="21" y2="12"/>
</svg>
Break
</button>
<button class="btn-subheading" id="btn-subheading" onclick="wrapSpan('subheading')" title="Wrap selection as subheading" disabled>S</button>
<button class="btn-chat" id="btn-chat" onclick="wrapSpan('chat')" title="Wrap selection as chat" disabled>C</button>
<button class="btn-indent" id="btn-indent" onclick="insertIndent()" title="Wrap selection as indented paragraph" disabled>→|</button>
<button class="btn-comment" id="btn-comment" onclick="insertComment()" title="Wrap selection as author comment block" disabled>[ ]</button>
<button class="btn-info-page" onclick="generateIntroPage()" title="Generate a Book Info page as the first chapter">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="9"/>
<line x1="12" y1="8" x2="12" y2="8.01"/>
<polyline points="11 12 12 12 12 16 13 16"/>
</svg>
Info page
</button>
<button class="btn-replace" onclick="openReplaceModal()" title="Find &amp; replace across all chapters">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
Replace
</button>
<span class="save-status" id="save-status"></span>
<button class="btn-save-all" id="btn-save-all" onclick="saveAllChapters()" style="display:none"></button>
<button class="btn-save" id="btn-save" onclick="saveChapter()" disabled>Save</button>
</div>
</div>
<!-- Two-panel body -->
<div class="editor-body">
<nav class="chapter-panel">
<div class="chapter-panel-title">Chapters</div>
<div class="chapter-list" id="chapter-list"></div>
</nav>
<div class="editor-pane" id="editor-pane"></div>
</div>
<!-- Find & Replace modal -->
<div class="modal-backdrop" id="replace-modal">
<div class="modal">
<div class="modal-title">Find &amp; Replace — all chapters</div>
<div class="modal-field">
<label class="modal-label">Search</label>
<input class="modal-input" id="rp-search" type="text" placeholder="Search…" autocomplete="off"/>
</div>
<div class="modal-field">
<label class="modal-label">Replace with</label>
<input class="modal-input" id="rp-replace" type="text" placeholder="Replace with…" autocomplete="off"/>
</div>
<div class="modal-options">
<label class="modal-opt"><input type="checkbox" id="rp-regex"/> Regex</label>
<label class="modal-opt"><input type="checkbox" id="rp-case"/> Case sensitive</label>
</div>
<div class="modal-progress" id="rp-progress"></div>
<div class="modal-actions">
<button class="btn-secondary" onclick="closeReplaceModal()">Cancel</button>
<button class="btn-primary" id="rp-run" onclick="replaceInAllChapters()">Replace all</button>
</div>
</div>
</div>
<script>
const EDITOR = {
filename: {{ filename | tojson }},
title: {{ (title or filename) | tojson }},
is_db: {{ is_db | tojson }},
};
</script>
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs/loader.js"></script>
<script src="/static/books.js"></script>
<script src="/static/editor.js"></script>
</body>
</html>