fix: the document's format survives a reload

A .docx document came back as .odt in the title and the save picker after
a reload, since only the session knew its format.
This commit is contained in:
Steffen Becker committed 2026-09-04 19:30:12 +02:00
1 parent b2cb216af0
commit 9335f392ce
3 files changed
+19 -2

No files matched your search

+3 -2
View File
@@ -39,7 +39,7 @@
import { DEFAULT_NOTE_SETTINGS } from './lib/storage/noteSettings';
import { builtinStyleSheet, type StyleFamily } from './lib/styles/styleSheet';
import { loadHfDoc, saveHfDoc, loadHfDistances, saveHfDistances, loadDifferentFirstPage, saveDifferentFirstPage, loadDifferentOddEven, saveDifferentOddEven, hfIsEmpty, DEFAULT_HF_DISTANCES, loadExtraHfSections, saveExtraHfSections, type HfDoc, type HfZone, type HfDistances, type HfSet } from './lib/storage/headerFooter';
import { loadDocName, saveDocName, stripOdtExtension, sanitizeNameForFile } from './lib/storage/documentName';
import { loadDocName, saveDocName, loadDocFormat, saveDocFormat, stripOdtExtension, sanitizeNameForFile, type DocumentFormat } from './lib/storage/documentName';
import { loadDocProperties, saveDocProperties, EMPTY_DOC_PROPERTIES, type DocProperties } from './lib/storage/docProperties';
import { loadHyphenation, saveHyphenation } from './lib/storage/hyphenation';
import { loadPageNumbering, savePageNumbering, DEFAULT_PAGE_NUMBERING, type PageNumbering } from './lib/storage/pageNumbering';
@@ -184,7 +184,7 @@
let documentName: string = $state(loadDocName());
// The loaded file's format; drives the title's extension label and which
// exporter "Save" round-trips through (odt-opened saves odt, docx-opened saves docx).
let documentFormat: 'odt' | 'docx' = $state('odt');
let documentFormat: DocumentFormat = $state(loadDocFormat());
let docProps: DocProperties = $state(loadDocProperties());
let docPropsOpen = $state(false);
let hyphenate = $state(loadHyphenation());
@@ -331,6 +331,7 @@
$effect(() => {
saveDocName(documentName);
saveDocFormat(documentFormat);
});
function setZoom(value: number) {
+1
View File
@@ -40,6 +40,7 @@ All keys share the `edentext-` prefix — a namespace, not a description: on Git
- **Document:** `edentext-doc` — TipTap JSON, debounced 1 s on every `onUpdate` (`autosave.ts`); hiding the page (`visibilitychange`/`pagehide`) writes what is pending at once, and writes queue behind each other so a slow picture round trip never stores an older document over a newer one. Loading it raises `edentext-doc-loading`, which only a completed startup clears (`markDocumentLoaded`, two frames after `Editor.svelte`'s mount). Still set at the next load = that document hung or crashed the editor: it moves to `edentext-doc-broken` and the app starts empty, so a reload escapes the freeze instead of repeating it. **Pictures do not live in it:** `imageStore.ts` swaps every data-URI past 4 KB for an `idb:<key>` before the write and keeps the bytes in the `edentext-images` IndexedDB store (a handful of photos is the whole localStorage quota). The key is a content hash of the data-URI's head, tail and length — sampled, because this runs on every autosave — so a picture used twice is stored once, and each save sweeps the keys the document no longer names. Where IndexedDB is unavailable the data-URIs stay inline, exactly as before; a key the store has lost keeps its `idb:` src and is reported, rather than quietly dropping the reader's content. `loadDocument` is therefore **async**, and `Editor.svelte` awaits it before building the editor.
- **Recent files:** `edentext-recent-files` — `{id, name, at}` per entry, newest first, eight kept (`recentFiles.ts`). Only the label lives here: the `FileSystemFileHandle` that reopens one is structured-cloneable but not JSON, so it sits in the `edentext-recent` IndexedDB store under the same id, and each write sweeps the handles no entry names. Two handles compare only through `isSameEntry`, which is async — hence the walk when recording a file already in the list. A file without a handle is never recorded (nothing could reopen it — no File System Access API, or a template), so the list simply stays empty in Firefox/Safari/Brave; a startup prune drops entries whose handle is gone. Reopening asks for `read` permission again (the API drops it on reload) — a declined prompt keeps the entry for the next try, only a moved or deleted file drops it (with a message). Save re-prompts for write on its own (Save-As fallback in `App.svelte`).
- **Document format:** `edentext-doc-format` — `'docx'` while the open document round-trips as .docx; absent = .odt (`documentName.ts`).
- **Theme:** `edentext-theme` — `'light' | 'dark' | 'allBlack' | 'auto'`.
- **Toolbar expanded:** `edentext-toolbar-expanded` — boolean string.
- **Chrome:** `edentext-chrome` — `'modern' | 'ribbon'` ('classic' read as the modern island's legacy stored name); absent = ribbon.
+15
View File
@@ -22,3 +22,18 @@ export function sanitizeNameForFile(name: string): string {
// eslint-disable-next-line no-control-regex
return name.replace(/[<>:"/\\|?*\x00-\x1f]/g, '').trim();
}
export type DocumentFormat = 'odt' | 'docx';
// The format the open document round-trips in. Absent at the .odt default, so only a
// document that came in as .docx writes anything.
const FORMAT_KEY = 'edentext-doc-format';
export function loadDocFormat(): DocumentFormat {
return localStorage.getItem(FORMAT_KEY) === 'docx' ? 'docx' : 'odt';
}
export function saveDocFormat(format: DocumentFormat): void {
if (format === 'docx') localStorage.setItem(FORMAT_KEY, format);
else localStorage.removeItem(FORMAT_KEY);
}