первй коммит

This commit is contained in:
2026-02-27 21:26:26 +03:00
parent e400b44732
commit ca00e6bbc8
19 changed files with 2701 additions and 257 deletions

View File

@@ -7,47 +7,49 @@ export class FileSaveService {
async saveFile(projectStore, path, content) {
const normalizedPath = PathUtils.normalizeRelative(path);
const resolvedPath = projectStore.resolveFilePath(normalizedPath) || normalizedPath;
if (projectStore.rootHandle) {
await this.#writeWithRootHandle(projectStore.rootHandle, normalizedPath, content);
return { mode: "inplace", path: normalizedPath };
await this.#writeWithRootHandle(projectStore.rootHandle, resolvedPath, content);
return { mode: "inplace", path: resolvedPath };
}
const knownHandle = projectStore.fileHandles.get(normalizedPath) || this.fallbackHandles.get(normalizedPath);
const knownHandle = projectStore.getFileHandle(resolvedPath) || this.fallbackHandles.get(resolvedPath);
if (knownHandle && typeof knownHandle.createWritable === "function") {
await this.#writeWithFileHandle(knownHandle, content);
return { mode: "inplace", path: normalizedPath };
return { mode: "inplace", path: resolvedPath };
}
if (typeof window.showSaveFilePicker === "function") {
const pickerOptions = {
suggestedName: PathUtils.basename(normalizedPath),
suggestedName: PathUtils.basename(resolvedPath),
id: this.#buildProjectSaveId(projectStore)
};
const startInHandle = projectStore.rootHandle || knownHandle || this.fallbackHandles.get(normalizedPath);
const startInHandle = projectStore.rootHandle || knownHandle || this.fallbackHandles.get(resolvedPath);
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.fallbackHandles.set(resolvedPath, handle);
return { mode: "save_as", path: resolvedPath };
}
this.#downloadFile(normalizedPath, content);
return { mode: "download", path: normalizedPath };
this.#downloadFile(resolvedPath, content);
return { mode: "download", path: resolvedPath };
}
async saveExistingFile(projectStore, path, content) {
const normalizedPath = PathUtils.normalizeRelative(path);
const resolvedPath = projectStore.resolveFilePath(normalizedPath) || normalizedPath;
if (projectStore.rootHandle) {
await this.#writeWithRootHandle(projectStore.rootHandle, normalizedPath, content);
return { mode: "inplace", path: normalizedPath };
await this.#writeWithRootHandle(projectStore.rootHandle, resolvedPath, content);
return { mode: "inplace", path: resolvedPath };
}
const knownHandle = projectStore.fileHandles.get(normalizedPath) || this.fallbackHandles.get(normalizedPath);
const knownHandle = projectStore.getFileHandle(resolvedPath) || this.fallbackHandles.get(resolvedPath);
if (knownHandle && typeof knownHandle.createWritable === "function") {
await this.#writeWithFileHandle(knownHandle, content);
return { mode: "inplace", path: normalizedPath };
return { mode: "inplace", path: resolvedPath };
}
throw new Error("Нет доступа к существующему файлу для записи без выбора новой директории.");
@@ -78,8 +80,9 @@ export class FileSaveService {
async deleteFile(projectStore, path) {
const normalizedPath = PathUtils.normalizeRelative(path);
const resolvedPath = projectStore.resolveFilePath(normalizedPath) || normalizedPath;
if (!projectStore.rootHandle) return false;
const parts = normalizedPath.split("/");
const parts = resolvedPath.split("/");
const fileName = parts.pop();
let dir = projectStore.rootHandle;
@@ -91,6 +94,51 @@ export class FileSaveService {
return true;
}
async createDirectory(projectStore, path) {
const normalizedPath = PathUtils.normalizeRelative(path);
if (!projectStore.rootHandle) throw new Error("Нет доступа к директории проекта.");
const parts = normalizedPath.split("/");
let dir = projectStore.rootHandle;
for (const part of parts) {
dir = await dir.getDirectoryHandle(part, { create: true });
}
return true;
}
async deleteDirectory(projectStore, path) {
const normalizedPath = PathUtils.normalizeRelative(path);
if (!projectStore.rootHandle) throw new Error("Нет доступа к директории проекта.");
const parts = normalizedPath.split("/");
const dirName = parts.pop();
let parent = projectStore.rootHandle;
for (const part of parts) {
parent = await parent.getDirectoryHandle(part);
}
await parent.removeEntry(dirName, { recursive: true });
return true;
}
async renameDirectory(projectStore, oldPath, newPath) {
const normalizedOld = PathUtils.normalizeRelative(oldPath);
const normalizedNew = PathUtils.normalizeRelative(newPath);
if (!projectStore.rootHandle) throw new Error("Нет доступа к директории проекта.");
if (normalizedOld === normalizedNew) return true;
if (normalizedNew.startsWith(`${normalizedOld}/`)) {
throw new Error("Нельзя переместить папку внутрь самой себя.");
}
const sourceDir = await this.#getDirectoryHandleByPath(projectStore.rootHandle, normalizedOld, false);
const newParts = normalizedNew.split("/");
const newName = newParts.pop();
const newParentPath = newParts.join("/");
const targetParent = await this.#getDirectoryHandleByPath(projectStore.rootHandle, newParentPath, true);
const targetDir = await targetParent.getDirectoryHandle(newName, { create: true });
await this.#copyDirectoryRecursive(sourceDir, targetDir);
await this.deleteDirectory(projectStore, normalizedOld);
return true;
}
async #writeWithRootHandle(rootHandle, path, content) {
const parts = path.split("/");
const fileName = parts.pop();
@@ -104,6 +152,32 @@ export class FileSaveService {
await this.#writeWithFileHandle(handle, content);
}
async #getDirectoryHandleByPath(rootHandle, path, createMissing) {
if (!path) return rootHandle;
let dir = rootHandle;
const parts = path.split("/").filter(Boolean);
for (const part of parts) {
dir = await dir.getDirectoryHandle(part, { create: createMissing });
}
return dir;
}
async #copyDirectoryRecursive(sourceDir, targetDir) {
for await (const [entryName, entryHandle] of sourceDir.entries()) {
if (entryHandle.kind === "directory") {
const childTarget = await targetDir.getDirectoryHandle(entryName, { create: true });
await this.#copyDirectoryRecursive(entryHandle, childTarget);
continue;
}
const sourceFile = await entryHandle.getFile();
const targetFile = await targetDir.getFileHandle(entryName, { create: true });
const writable = await targetFile.createWritable();
await writable.write(sourceFile);
await writable.close();
}
}
async #writeWithFileHandle(handle, content) {
const writable = await handle.createWritable();
await writable.write(content);