✨ context menu for file tree
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { readdir, readFile, writeFile, mkdir, unlink } from 'fs/promises'
|
||||
import { join } from 'path'
|
||||
import { readdir, readFile, writeFile, mkdir, unlink, rename as fsRename, rm } from 'fs/promises'
|
||||
import { join, dirname } from 'path'
|
||||
import type { FileNode, RevisionMeta } from '../renderer/types/editor'
|
||||
|
||||
const DRAFT_ROOT =
|
||||
@@ -215,3 +215,37 @@ export async function deleteRevision(filePath: string, revisionId: string): Prom
|
||||
const revPath = join(REVISIONS_DIR, slug, `${revisionId}.json`)
|
||||
await unlink(revPath)
|
||||
}
|
||||
|
||||
// ─── File tree mutations ──────────────────────────────────────────────────────
|
||||
|
||||
export async function renameFileOrDir(oldPath: string, newName: string): Promise<string> {
|
||||
assertInDraftRoot(oldPath)
|
||||
const isFile = oldPath.endsWith('.md')
|
||||
const newPath = join(dirname(oldPath), isFile ? `${newName}.md` : newName)
|
||||
assertInDraftRoot(newPath)
|
||||
await fsRename(oldPath, newPath)
|
||||
return newPath
|
||||
}
|
||||
|
||||
export async function deleteFileOrDir(targetPath: string): Promise<void> {
|
||||
assertInDraftRoot(targetPath)
|
||||
await rm(targetPath, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
export async function createMarkdownFile(parentPath: string, name: string): Promise<string> {
|
||||
const dir = parentPath === '__root__' ? DRAFT_ROOT : parentPath
|
||||
assertInDraftRoot(dir)
|
||||
const filePath = join(dir, `${name}.md`)
|
||||
assertInDraftRoot(filePath)
|
||||
await writeFile(filePath, '', 'utf-8')
|
||||
return filePath
|
||||
}
|
||||
|
||||
export async function createSubdirectory(parentPath: string, name: string): Promise<string> {
|
||||
const parent = parentPath === '__root__' ? DRAFT_ROOT : parentPath
|
||||
assertInDraftRoot(parent)
|
||||
const newDir = join(parent, name)
|
||||
assertInDraftRoot(newDir)
|
||||
await mkdir(newDir, { recursive: true })
|
||||
return newDir
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ipcMain, dialog, BrowserWindow } from 'electron'
|
||||
import { readFileSync } from 'fs'
|
||||
import { extname, basename } from 'path'
|
||||
import { listDraftFiles, readMarkdownFile, writeMarkdownFile, getProjectWordCount, saveOrderFile, readSession, writeSession, saveRevision, listRevisions, loadRevision, deleteRevision } from './fileSystem'
|
||||
import { listDraftFiles, readMarkdownFile, writeMarkdownFile, getProjectWordCount, saveOrderFile, readSession, writeSession, saveRevision, listRevisions, loadRevision, deleteRevision, renameFileOrDir, deleteFileOrDir, createMarkdownFile, createSubdirectory } from './fileSystem'
|
||||
import { streamMessage } from './aiService'
|
||||
import type { AIPayload, Attachment } from '../renderer/types/editor'
|
||||
|
||||
@@ -50,6 +50,22 @@ export function registerIpcHandlers(): void {
|
||||
await deleteRevision(filePath, revisionId)
|
||||
})
|
||||
|
||||
ipcMain.handle('fs:rename', async (_event, oldPath: string, newName: string) => {
|
||||
return await renameFileOrDir(oldPath, newName)
|
||||
})
|
||||
|
||||
ipcMain.handle('fs:delete', async (_event, targetPath: string) => {
|
||||
await deleteFileOrDir(targetPath)
|
||||
})
|
||||
|
||||
ipcMain.handle('fs:createFile', async (_event, parentPath: string, name: string) => {
|
||||
return await createMarkdownFile(parentPath, name)
|
||||
})
|
||||
|
||||
ipcMain.handle('fs:createDir', async (_event, parentPath: string, name: string) => {
|
||||
return await createSubdirectory(parentPath, name)
|
||||
})
|
||||
|
||||
ipcMain.handle('fs:pickAttachments', async (event): Promise<Attachment[]> => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
const result = await dialog.showOpenDialog(win!, {
|
||||
|
||||
Reference in New Issue
Block a user