Add Neon and Meridian glass themes, bundled fonts, configurable recovery backup location, autosave and UI polish
This commit is contained in:
parent
aaa491424d
commit
b74cfe98ab
59 changed files with 1848 additions and 185 deletions
|
|
@ -214,6 +214,27 @@ setEditorDoc(editor5, normalized.doc);
|
|||
ok(editor5.getText().includes("alpha") && editor5.getText().includes("beta"),
|
||||
"legacy camelCase hardBreak chapter loads (not empty)");
|
||||
|
||||
// --- text alignment via toolbar command ---
|
||||
const alignEd = makeEditor();
|
||||
setEditorDoc(alignEd, { type: "doc", content: [{ type: "paragraph", content: [{ type: "text", text: "align me" }] }] });
|
||||
alignEd.commands.focus("end");
|
||||
runCommand(alignEd, "align-center");
|
||||
ok(
|
||||
(alignEd.getJSON().content[0].attrs?.textAlign || "") === "center",
|
||||
"align-center sets the paragraph textAlign"
|
||||
);
|
||||
|
||||
// --- standalone Highlight button (no color) must apply a highlight, not be a no-op ---
|
||||
const hlEd = makeEditor();
|
||||
setEditorDoc(hlEd, { type: "doc", content: [{ type: "paragraph", content: [{ type: "text", text: "mark me" }] }] });
|
||||
hlEd.commands.selectAll();
|
||||
runCommand(hlEd, "highlight");
|
||||
const hlMarks = hlEd.getJSON().content[0].content[0].marks || [];
|
||||
ok(
|
||||
hlMarks.some((m) => m.type === "highlight"),
|
||||
"standalone Highlight button applies a default highlight to the selection"
|
||||
);
|
||||
|
||||
fs.rmSync(out, { force: true });
|
||||
fs.rmSync(book, { recursive: true, force: true });
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export { createBook, loadBook, saveMeta } from "./src/main/project.ts";
|
|||
export { setChapterContent, getChapterContent } from "./src/main/chapters.ts";
|
||||
export { markdownToNative } from "./src/main/markdown-to-native.ts";
|
||||
export { parseChapter, serializeChapter, migrateDoc, getChapterDoc, nativeChapterFile, CURRENT_DOC_SCHEMA_VERSION, isValidDoc } from "./src/main/native-doc.ts";
|
||||
export { RECOVERY_ROOT, _resetSessionBackupGuard } from "./src/main/backup.ts";
|
||||
export { getRecoveryRoot, _resetSessionBackupGuard } from "./src/main/backup.ts";
|
||||
`
|
||||
);
|
||||
await esbuild.build({
|
||||
|
|
@ -54,7 +54,7 @@ const {
|
|||
serializeChapter,
|
||||
migrateDoc,
|
||||
CURRENT_DOC_SCHEMA_VERSION,
|
||||
RECOVERY_ROOT,
|
||||
getRecoveryRoot,
|
||||
_resetSessionBackupGuard,
|
||||
} = mod;
|
||||
|
||||
|
|
@ -244,10 +244,10 @@ ok(
|
|||
// Recovery backup exists under ~/Folio-Recovery and contains folio.json
|
||||
const bookName = path.basename(book);
|
||||
const backups = fs
|
||||
.readdirSync(RECOVERY_ROOT)
|
||||
.filter((d) => d.startsWith(bookName) && fs.statSync(path.join(RECOVERY_ROOT, d)).isDirectory());
|
||||
.readdirSync(getRecoveryRoot())
|
||||
.filter((d) => d.startsWith(bookName) && fs.statSync(path.join(getRecoveryRoot(), d)).isDirectory());
|
||||
ok(backups.length >= 1, "recovery backup created under ~/Folio-Recovery");
|
||||
const backupDir = path.join(RECOVERY_ROOT, backups[0]);
|
||||
const backupDir = path.join(getRecoveryRoot(), backups[0]);
|
||||
ok(fs.existsSync(path.join(backupDir, "folio.json")), "backup contains folio.json");
|
||||
ok(
|
||||
fs.existsSync(path.join(backupDir, "chapters", `${c1}.md`)),
|
||||
|
|
@ -294,7 +294,7 @@ ok(
|
|||
fs.rmSync(out, { force: true });
|
||||
fs.rmSync(book, { recursive: true, force: true });
|
||||
for (const d of backups) {
|
||||
fs.rmSync(path.join(RECOVERY_ROOT, d), { recursive: true, force: true });
|
||||
fs.rmSync(path.join(getRecoveryRoot(), d), { recursive: true, force: true });
|
||||
}
|
||||
|
||||
console.log(
|
||||
|
|
|
|||
|
|
@ -90,8 +90,9 @@
|
|||
!!document.querySelector("#chapterList li.chapter-row.active")
|
||||
);
|
||||
|
||||
// Manual save: typing marks the chapter Unsaved; the Save button (or
|
||||
// Ctrl/Cmd+S) is required to persist. No auto-save should occur.
|
||||
// Auto-save: typing marks the chapter Unsaved, then a debounced
|
||||
// background save persists it automatically (no prompt, no manual
|
||||
// Save required). Verify the indicator ends in "Saved".
|
||||
const edSave = window.__folioEditor;
|
||||
if (edSave) {
|
||||
edSave.commands.focus("end");
|
||||
|
|
@ -99,11 +100,8 @@
|
|||
await sleep(150);
|
||||
const unsaved = document.getElementById("saveState");
|
||||
step("afterTypeUnsaved", unsaved ? unsaved.textContent : "n/a");
|
||||
const sb = document.getElementById("saveBtn");
|
||||
if (sb) {
|
||||
sb.click();
|
||||
await sleep(400);
|
||||
}
|
||||
// Wait past the autosave debounce for the chapter to self-persist.
|
||||
await sleep(1600);
|
||||
const after = document.getElementById("saveState");
|
||||
step("afterSaveState", after ? after.textContent : "n/a");
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue