Files
FreeCADWasmDev/web/src/web-commanddoc-files.ts
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

53 lines
2.8 KiB
TypeScript

// Project utility files retain their directory structure in OPFS.
export async function writeProjectUtilityFiles(directory: string,
files: Array<{ filename: string; contentsBase64: string }>): Promise<void> {
if (!directory.trim() || /[\\/]/.test(directory) || [".", ".."].includes(directory))
throw new Error("Choose a project folder name");
const root = await navigator.storage.getDirectory();
const projects = await root.getDirectoryHandle("freecad-project-utility", { create: true });
const destination = await projects.getDirectoryHandle(directory, { create: true });
for (const file of files) {
const parts = file.filename.split("/");
if (parts.some(part => !part || part === "." || part === ".." || part.includes("\\")))
throw new Error("Invalid project file path");
let parent = destination;
for (const part of parts.slice(0, -1)) parent = await parent.getDirectoryHandle(part, { create: true });
const handle = await parent.getFileHandle(parts[parts.length - 1], { create: true });
const writer = await handle.createWritable();
try {
await writer.write(Uint8Array.from(atob(file.contentsBase64), char => char.charCodeAt(0)));
await writer.close();
} catch (error) { await writer.abort().catch(() => undefined); throw error; }
}
}
export async function readProjectUtilityFiles(directory: string): Promise<Array<{ filename: string; contentsBase64: string }>> {
if (!directory.trim() || /[\\/]/.test(directory) || [".", ".."].includes(directory))
throw new Error("Choose a project folder name");
const root = await navigator.storage.getDirectory();
const projects = await root.getDirectoryHandle("freecad-project-utility");
const source = await projects.getDirectoryHandle(directory);
const files: Array<{ filename: string; contentsBase64: string }> = [];
async function visit(handle: FileSystemDirectoryHandle, prefix: string): Promise<void> {
// The iterable DOM declaration is not available in every TypeScript DOM lib.
const iterable = handle as FileSystemDirectoryHandle & {
entries(): AsyncIterableIterator<[string, FileSystemHandle]>;
};
for await (const [name, entry] of iterable.entries()) {
if (entry.kind === "directory") await visit(entry as FileSystemDirectoryHandle, prefix + name + "/");
else files.push({ filename: prefix + name,
contentsBase64: await encodeProjectFile(await (entry as FileSystemFileHandle).getFile()) });
}
}
await visit(source, "");
return files;
}
export async function encodeProjectFile(file: Blob): Promise<string> {
const bytes = new Uint8Array(await file.arrayBuffer());
let binary = "";
for (let offset = 0; offset < bytes.length; offset += 8192)
binary += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
return btoa(binary);
}