import { App, TFile, TFolder, normalizePath } from "obsidian"; import { PROJECTS_ROOT, PROJECT_FILES } from "./const"; export function projectsPath(): string { return PROJECTS_ROOT; } export function projectPath(name: string): string { return normalizePath(`${PROJECTS_ROOT}/${name}`); } export function nodeMdPath(folderPath: string): string { const p = normalizePath(folderPath); const name = p.split("/").pop() ?? p; return normalizePath(`${p}/${name}.md`); } export function isNode(app: App, folderPath: string): boolean { const md = app.vault.getAbstractFileByPath(nodeMdPath(folderPath)); return md instanceof TFile; } export async function ensureFolder(app: App, path: string): Promise { if (!path) return; const p = normalizePath(path); const exists = app.vault.getAbstractFileByPath(p); if (!exists) { await app.vault.createFolder(p); } } export async function ensureFile(app: App, path: string, content = ""): Promise { const p = normalizePath(path); const existing = app.vault.getAbstractFileByPath(p); if (existing instanceof TFile) return existing; return await app.vault.create(p, content); } export function listFolders(app: App, path: string): TFolder[] { const folder = path ? app.vault.getAbstractFileByPath(normalizePath(path)) : app.vault.getRoot(); if (!(folder instanceof TFolder)) return []; return folder.children .filter((c): c is TFolder => c instanceof TFolder) .filter((c) => !c.name.startsWith("_") || c.name.startsWith("__")) .sort((a, b) => a.name.localeCompare(b.name)); } export function listMarkdownFiles(app: App, path: string, exclude: string[] = []): TFile[] { const folder = path ? app.vault.getAbstractFileByPath(normalizePath(path)) : app.vault.getRoot(); if (!(folder instanceof TFolder)) return []; return folder.children .filter((c): c is TFile => c instanceof TFile && c.extension === "md") .filter((f) => !exclude.includes(f.name)) .filter((f) => !f.basename.startsWith("_") || f.basename.startsWith("__")) .sort((a, b) => a.basename.localeCompare(b.basename)); } export async function readFile(app: App, path: string): Promise { const p = normalizePath(path); const f = app.vault.getAbstractFileByPath(p); if (!(f instanceof TFile)) return ""; return await app.vault.read(f); } export async function deleteRecursive(app: App, path: string): Promise { const p = normalizePath(path); const f = app.vault.getAbstractFileByPath(p); if (!f) return; await app.vault.delete(f, true); } export async function rename(app: App, oldPath: string, newPath: string): Promise { const f = app.vault.getAbstractFileByPath(normalizePath(oldPath)); if (!f) return; await app.fileManager.renameFile(f, normalizePath(newPath)); } export async function createProject(app: App, name: string): Promise { const root = projectPath(name); await ensureFolder(app, projectsPath()); await ensureFolder(app, root); for (const file of PROJECT_FILES) { await ensureFile(app, normalizePath(`${root}/${file}`), ""); } } export async function createNode( app: App, parentFolderPath: string, name: string, ): Promise { const folder = normalizePath(`${parentFolderPath}/${name}`); await ensureFolder(app, folder); return await ensureFile(app, nodeMdPath(folder), ""); } export async function renameNode( app: App, folderPath: string, newName: string, ): Promise { const p = normalizePath(folderPath); const parent = p.substring(0, p.lastIndexOf("/")); const oldName = p.split("/").pop() ?? ""; if (oldName === newName) return; const oldMd = nodeMdPath(p); const newFolder = normalizePath(`${parent}/${newName}`); const mdFile = app.vault.getAbstractFileByPath(oldMd); if (mdFile instanceof TFile) { const tmpMd = normalizePath(`${p}/${newName}.md`); if (tmpMd !== oldMd) await rename(app, oldMd, tmpMd); } await rename(app, p, newFolder); } export async function moveNode( app: App, folderPath: string, newParentFolderPath: string, ): Promise { const p = normalizePath(folderPath); const name = p.split("/").pop() ?? ""; const target = normalizePath(`${newParentFolderPath}/${name}`); if (p === target) return; await rename(app, p, target); } export interface NodeTree { name: string; path: string; children: NodeTree[]; } export function buildNodeTree(app: App, projectName: string): NodeTree { const root = projectPath(projectName); const walk = (folderPath: string, name: string): NodeTree => { const children = listFolders(app, folderPath).map((f) => walk(f.path, f.name)); return { name, path: folderPath, children }; }; return walk(root, projectName); } export interface ProjectFileLocation { project: string; path: string[]; } export function parseProjectFilePath(path: string): ProjectFileLocation | null { if (!path.endsWith(".md")) return null; const parts = normalizePath(path).split("/"); let idx = 0; if (PROJECTS_ROOT) { if (parts[0] !== PROJECTS_ROOT) return null; idx = 1; } if (parts.length < idx + 2) return null; const project = parts[idx]; const rest = parts.slice(idx + 1); return { project, path: rest }; }