первй коммит
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user