fix: Save round-trips a .docx-opened document as .docx

Ctrl+S always built .odt bytes and wrote them into whatever handle was
open — including a handle obtained by opening a .docx, silently
overwriting it with ODF content under its old name. Track the loaded
format and route Save through the matching exporter and handle; the
title's extension label now reflects it instead of a hardcoded .odt.
Save As stays .odt-only, so it switches the format back.
This commit is contained in:
Steffen Becker committed 2026-08-15 22:40:32 +02:00
1 parent 873af0794d
commit d78181fdd9
3 files changed
+38 -10

No files matched your search

+17 -7
View File
@@ -84,16 +84,26 @@ export async function saveAsOdt(
return handle;
}
// Export a .docx: always prompt for a location (no handle is tracked — .docx is an
// export target, like PDF, while DOCX import doesn't exist yet). Falls back to a
// plain download. Returns null. Throws AbortError if the user cancels.
export async function saveAsDocx(bytes: Uint8Array, suggestedName: string): Promise<void> {
// Save to the given handle if we have one; otherwise prompt for a location. Mirrors
// saveOdt, for a document that was opened as .docx and must round-trip as .docx.
export async function saveDocx(
bytes: Uint8Array,
suggestedName: string,
handle: FileSystemFileHandle | null,
): Promise<FileSystemFileHandle | null> {
if (!supportsFsAccess()) {
download(bytes, suggestedName, DOCX_MIME);
return;
return null;
}
const handle = await (window as WinFs).showSaveFilePicker!({ suggestedName, types: DOCX_PICKER_TYPES });
await writeHandle(handle, bytes);
const target = handle ?? (await (window as WinFs).showSaveFilePicker!({ suggestedName, types: DOCX_PICKER_TYPES }));
await writeHandle(target, bytes);
return target;
}
// Export a .docx: always prompt for a location (no handle is tracked — this is the
// explicit "Export" action, like PDF). Returns null. Throws AbortError if cancelled.
export async function saveAsDocx(bytes: Uint8Array, suggestedName: string): Promise<void> {
await saveDocx(bytes, suggestedName, null);
}
// Save a template. The picker offers both formats, so the bytes can only be built