Compare commits
No commits in common. "647965668015b3bde4019e8137c55f4a2912f182" and "76222fc1b2079ed95976aa193d53f2ebdf5b554f" have entirely different histories.
6479656680
...
76222fc1b2
5 changed files with 59 additions and 124 deletions
|
|
@ -80,34 +80,32 @@ export function buildMenu(
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "Export",
|
label: "Export Markdown…",
|
||||||
enabled: !!getState().bookPath,
|
|
||||||
submenu: [
|
|
||||||
{
|
|
||||||
label: "Markdown…",
|
|
||||||
accelerator: "CmdOrCtrl+Shift+E",
|
accelerator: "CmdOrCtrl+Shift+E",
|
||||||
|
enabled: !!getState().bookPath,
|
||||||
click: () => getWindow()?.webContents.send("folio:export-markdown-requested"),
|
click: () => getWindow()?.webContents.send("folio:export-markdown-requested"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Bundle (ZIP)…",
|
label: "Export Bundle (ZIP)…",
|
||||||
|
enabled: !!getState().bookPath,
|
||||||
click: () => getWindow()?.webContents.send("folio:export-zip-requested"),
|
click: () => getWindow()?.webContents.send("folio:export-zip-requested"),
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
|
||||||
{
|
{
|
||||||
label: "Full Book (PDF)…",
|
label: "Export Full Book (PDF)…",
|
||||||
accelerator: "CmdOrCtrl+Shift+P",
|
accelerator: "CmdOrCtrl+Shift+P",
|
||||||
|
enabled: !!getState().bookPath,
|
||||||
click: () => getWindow()?.webContents.send("folio:export-pdf-requested"),
|
click: () => getWindow()?.webContents.send("folio:export-pdf-requested"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Full Book (DOCX)…",
|
label: "Export Full Book (DOCX)…",
|
||||||
|
enabled: !!getState().bookPath,
|
||||||
click: () => getWindow()?.webContents.send("folio:export-docx-requested"),
|
click: () => getWindow()?.webContents.send("folio:export-docx-requested"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Full Book (ODT)…",
|
label: "Export Full Book (ODT)…",
|
||||||
|
enabled: !!getState().bookPath,
|
||||||
click: () => getWindow()?.webContents.send("folio:export-odt-requested"),
|
click: () => getWindow()?.webContents.send("folio:export-odt-requested"),
|
||||||
},
|
},
|
||||||
],
|
|
||||||
},
|
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "Check for Updates…",
|
label: "Check for Updates…",
|
||||||
|
|
|
||||||
|
|
@ -95,21 +95,17 @@ export function createSearchFeature(ctx: BookCtx, editor: Editor): SearchFeature
|
||||||
void (async () => {
|
void (async () => {
|
||||||
const list = await api.getChapterList();
|
const list = await api.getChapterList();
|
||||||
const chapters = Array.isArray(list) ? list : [];
|
const chapters = Array.isArray(list) ? list : [];
|
||||||
const byChapter: { id: string; title: string; plain: string; positions: number[]; titleHits: number }[] = [];
|
const byChapter: { id: string; title: string; plain: string; positions: number[] }[] = [];
|
||||||
let total = 0;
|
let total = 0;
|
||||||
for (const ch of chapters) {
|
for (const ch of chapters) {
|
||||||
if (total >= 200) break;
|
if (total >= 200) break;
|
||||||
// Chapter titles count as matches too: a title hit links to the
|
|
||||||
// chapter itself (no in-body scroll position).
|
|
||||||
const title = ch.title || ch.id;
|
|
||||||
const titleHits = findMatches(title, term, matchCase).length;
|
|
||||||
const res = await api.getChapterContent(ch.id);
|
const res = await api.getChapterContent(ch.id);
|
||||||
const md = typeof res === "string" ? res : "";
|
const md = typeof res === "string" ? res : "";
|
||||||
const plain = markdownToPlain(md);
|
const plain = markdownToPlain(md);
|
||||||
const positions = findMatches(plain, term, matchCase);
|
const positions = findMatches(plain, term, matchCase);
|
||||||
if (positions.length || titleHits) {
|
if (positions.length) {
|
||||||
byChapter.push({ id: ch.id, title, plain, positions, titleHits });
|
byChapter.push({ id: ch.id, title: ch.title || ch.id, plain, positions });
|
||||||
total += positions.length + titleHits;
|
total += positions.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchMeta.textContent = total
|
searchMeta.textContent = total
|
||||||
|
|
@ -118,16 +114,7 @@ export function createSearchFeature(ctx: BookCtx, editor: Editor): SearchFeature
|
||||||
for (const r of byChapter) {
|
for (const r of byChapter) {
|
||||||
const head = document.createElement("li");
|
const head = document.createElement("li");
|
||||||
head.className = "search-chapter-head";
|
head.className = "search-chapter-head";
|
||||||
const counts = [
|
head.textContent = `${r.title} (${r.positions.length})`;
|
||||||
r.titleHits ? `title: ${r.titleHits}` : "",
|
|
||||||
r.positions.length ? `text: ${r.positions.length}` : "",
|
|
||||||
].filter(Boolean).join(", ");
|
|
||||||
head.textContent = `${r.title} (${counts})`;
|
|
||||||
if (r.titleHits) {
|
|
||||||
head.classList.add("search-result");
|
|
||||||
head.title = "Open chapter";
|
|
||||||
head.addEventListener("click", () => void goToResult(r.id, 0, term, matchCase));
|
|
||||||
}
|
|
||||||
searchResults.appendChild(head);
|
searchResults.appendChild(head);
|
||||||
for (const pos of r.positions) {
|
for (const pos of r.positions) {
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
|
|
|
||||||
|
|
@ -164,8 +164,8 @@ export function createSettingsFeature(ctx: BookCtx): SettingsFeature {
|
||||||
if (e.key === "Escape" && !modal.classList.contains("hidden")) closeSettings();
|
if (e.key === "Escape" && !modal.classList.contains("hidden")) closeSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
// File-menu entry point (the redundant sidebar button was removed Sep 22).
|
// Sidebar + File-menu entry points.
|
||||||
$("settingCheckUpdate")?.addEventListener("click", () => hooks.checkForUpdates());
|
$("settingsBtn")?.addEventListener("click", openSettings);
|
||||||
api.onSettingsRequested(() => openSettings());
|
api.onSettingsRequested(() => openSettings());
|
||||||
|
|
||||||
// Refresh the resolved-path line whenever a book opens (the default root
|
// Refresh the resolved-path line whenever a book opens (the default root
|
||||||
|
|
|
||||||
|
|
@ -188,13 +188,6 @@ html, body {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.update-control {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.backup-label {
|
.backup-label {
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|
@ -288,24 +281,11 @@ html, body {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- sidebar nav row (Search / Full Book, under the Home bar) ---- */
|
|
||||||
.sidebar-nav {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
margin-bottom: var(--gap);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar-nav .nav-btn {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- full book nav ---- */
|
/* ---- full book nav ---- */
|
||||||
.full-book-nav {
|
.full-book-nav {
|
||||||
display: flex;
|
margin-top: var(--gap);
|
||||||
flex: 1;
|
padding-top: var(--gap);
|
||||||
min-width: 0;
|
border-top: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-book-btn {
|
.full-book-btn {
|
||||||
|
|
@ -321,8 +301,7 @@ html, body {
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
|
transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
|
||||||
min-width: 0;
|
min-width: 100px;
|
||||||
flex: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-book-btn:hover {
|
.full-book-btn:hover {
|
||||||
|
|
@ -609,47 +588,6 @@ html, body {
|
||||||
color: var(--color-on-accent);
|
color: var(--color-on-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.recent-list li.recent-item.selected {
|
|
||||||
background: var(--color-accent);
|
|
||||||
color: var(--color-on-accent);
|
|
||||||
outline: 1px solid var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-list li.recent-item:focus-visible {
|
|
||||||
outline: 2px solid var(--color-accent);
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Row actions appear only when the row is selected. */
|
|
||||||
.recent-item .recent-actions {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
display: none;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-item.selected .recent-actions {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-item .recent-action {
|
|
||||||
height: 20px;
|
|
||||||
line-height: 1;
|
|
||||||
font-size: 0.72rem;
|
|
||||||
padding: 0 6px;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
color: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: var(--radius);
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-item .recent-action:hover {
|
|
||||||
opacity: 1;
|
|
||||||
background: rgba(255, 255, 255, 0.18);
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-item .recent-title {
|
.recent-item .recent-title {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
@ -659,9 +597,24 @@ html, body {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recent-item .recent-delete {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
.recent-item .recent-delete:hover {
|
.recent-item .recent-delete:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: rgba(220, 53, 69, 0.35);
|
background: rgba(220, 53, 69, 0.18);
|
||||||
color: #ff6b6b;
|
color: #ff6b6b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,13 @@
|
||||||
<span class="book-title" id="bookTitle"></span>
|
<span class="book-title" id="bookTitle"></span>
|
||||||
<span class="book-path" id="bookPath" title=""></span>
|
<span class="book-path" id="bookPath" title=""></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-nav">
|
|
||||||
<button class="button button-ghost nav-btn" id="searchBtn" title="Search chapter titles and text (Ctrl+F)">Search</button>
|
|
||||||
<div class="full-book-nav hidden" id="fullBookNav">
|
|
||||||
<button class="full-book-btn nav-btn" id="fullBookBtn" title="View the entire book as one continuous manuscript">Full Book</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ul class="chapter-list" id="chapterList"></ul>
|
<ul class="chapter-list" id="chapterList"></ul>
|
||||||
|
<div class="full-book-nav hidden" id="fullBookNav">
|
||||||
|
<button class="full-book-btn" id="fullBookBtn" title="View the entire book as one continuous manuscript">Full Book</button>
|
||||||
|
</div>
|
||||||
<div class="sidebar-footer">
|
<div class="sidebar-footer">
|
||||||
|
<button class="button button-ghost" id="searchBtn" title="Search all chapters (Ctrl+F)">Search</button>
|
||||||
|
<button class="button button-ghost" id="settingsBtn" title="Settings (autosave, backups)">Settings</button>
|
||||||
<div class="theme-control">
|
<div class="theme-control">
|
||||||
<label class="theme-label" for="themeSelect">Theme</label>
|
<label class="theme-label" for="themeSelect">Theme</label>
|
||||||
<select id="themeSelect" class="theme-select" title="Interface theme">
|
<select id="themeSelect" class="theme-select" title="Interface theme">
|
||||||
|
|
@ -58,6 +57,8 @@
|
||||||
<div class="welcome-actions">
|
<div class="welcome-actions">
|
||||||
<button class="button" id="welcomeNewBook">New</button>
|
<button class="button" id="welcomeNewBook">New</button>
|
||||||
<button class="button button-ghost" id="welcomeOpenBook">Open</button>
|
<button class="button button-ghost" id="welcomeOpenBook">Open</button>
|
||||||
|
<button class="button button-ghost" id="welcomeOpenMd" title="Open a Markdown or text file as a new book">Open file</button>
|
||||||
|
<button class="button button-ghost" id="welcomeUpdate" title="Check for a new version of Folio">Update</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="recents-block">
|
<div class="recents-block">
|
||||||
<div class="recents-title">Recent</div>
|
<div class="recents-title">Recent</div>
|
||||||
|
|
@ -350,10 +351,6 @@
|
||||||
</div>
|
</div>
|
||||||
<p class="setting-meta" id="settingResolved"></p>
|
<p class="setting-meta" id="settingResolved"></p>
|
||||||
<p class="setting-meta" id="settingLastSnap"></p>
|
<p class="setting-meta" id="settingLastSnap"></p>
|
||||||
<div class="update-control">
|
|
||||||
<span class="backup-label">Updates</span>
|
|
||||||
<button type="button" class="button button-ghost" id="settingCheckUpdate" title="Check for a new version of Folio">Check now</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button class="button button-ghost" id="settingSnapNow" title="Take a snapshot right now">Snapshot now</button>
|
<button class="button button-ghost" id="settingSnapNow" title="Take a snapshot right now">Snapshot now</button>
|
||||||
<button class="button" id="settingsClose">Close</button>
|
<button class="button" id="settingsClose">Close</button>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue