Первая версия

This commit is contained in:
2026-02-23 09:08:15 +03:00
commit 75fbb53390
23 changed files with 2498 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
import { PathUtils } from "./PathUtils.js";
export class FileSaveService {
constructor() {
this.fallbackHandles = new Map();
}
async saveFile(projectStore, path, content) {
const normalizedPath = PathUtils.normalizeRelative(path);
if (projectStore.rootHandle) {
await this.#writeWithRootHandle(projectStore.rootHandle, normalizedPath, content);
return { mode: "inplace", path: normalizedPath };
}
const knownHandle = projectStore.fileHandles.get(normalizedPath) || this.fallbackHandles.get(normalizedPath);
if (knownHandle && typeof knownHandle.createWritable === "function") {
await this.#writeWithFileHandle(knownHandle, content);
return { mode: "inplace", path: normalizedPath };
}
if (typeof window.showSaveFilePicker === "function") {
const pickerOptions = {
suggestedName: PathUtils.basename(normalizedPath),
id: this.#buildProjectSaveId(projectStore)
};
const startInHandle = projectStore.rootHandle || knownHandle || this.fallbackHandles.get(normalizedPath);
if (startInHandle) pickerOptions.startIn = startInHandle;
const handle = await window.showSaveFilePicker(pickerOptions);
await this.#writeWithFileHandle(handle, content);
this.fallbackHandles.set(normalizedPath, handle);
return { mode: "save_as", path: normalizedPath };
}
this.#downloadFile(normalizedPath, content);
return { mode: "download", path: normalizedPath };
}
async deleteFile(projectStore, path) {
const normalizedPath = PathUtils.normalizeRelative(path);
if (!projectStore.rootHandle) return false;
const parts = normalizedPath.split("/");
const fileName = parts.pop();
let dir = projectStore.rootHandle;
for (const part of parts) {
dir = await dir.getDirectoryHandle(part);
}
await dir.removeEntry(fileName);
return true;
}
async #writeWithRootHandle(rootHandle, path, content) {
const parts = path.split("/");
const fileName = parts.pop();
let dir = rootHandle;
for (const part of parts) {
dir = await dir.getDirectoryHandle(part, { create: true });
}
const handle = await dir.getFileHandle(fileName, { create: true });
await this.#writeWithFileHandle(handle, content);
}
async #writeWithFileHandle(handle, content) {
const writable = await handle.createWritable();
await writable.write(content);
await writable.close();
}
#downloadFile(path, content) {
const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = PathUtils.basename(path);
a.style.display = "none";
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
}
#buildProjectSaveId(projectStore) {
const projectName = projectStore?.rootNode?.name || "project";
const normalized = projectName.toLowerCase().replaceAll(/[^a-z0-9_-]/g, "-").replaceAll(/-+/g, "-");
return `save-${normalized || "project"}`;
}
}