This commit is contained in:
2026-04-30 20:10:14 +02:00
commit d531bc663d
31 changed files with 2046 additions and 0 deletions

99
src/fs.ts Normal file
View File

@@ -0,0 +1,99 @@
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 areaPath(project: string, area: string): string {
return normalizePath(`${PROJECTS_ROOT}/${project}/${area}`);
}
export function featurePath(project: string, area: string, feature: string): string {
const file = feature.endsWith(".md") ? feature : `${feature}.md`;
return normalizePath(`${PROJECTS_ROOT}/${project}/${area}/${file}`);
}
export async function ensureFolder(app: App, path: string): Promise<void> {
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<TFile> {
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 p = normalizePath(path);
const folder = app.vault.getAbstractFileByPath(p);
if (!(folder instanceof TFolder)) return [];
return folder.children
.filter((c): c is TFolder => c instanceof TFolder)
.sort((a, b) => a.name.localeCompare(b.name));
}
export function listMarkdownFiles(app: App, path: string, exclude: string[] = []): TFile[] {
const p = normalizePath(path);
const folder = app.vault.getAbstractFileByPath(p);
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))
.sort((a, b) => a.basename.localeCompare(b.basename));
}
export async function readFile(app: App, path: string): Promise<string> {
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<void> {
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<void> {
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<void> {
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 createArea(app: App, project: string, area: string): Promise<void> {
await ensureFolder(app, areaPath(project, area));
}
export async function createFeature(
app: App,
project: string,
area: string,
feature: string,
): Promise<TFile> {
return await ensureFile(app, featurePath(project, area, feature), "");
}
export function isProjectRootFile(name: string): boolean {
return (PROJECT_FILES as readonly string[]).includes(name);
}