feat: add file explorer with tree view and IDE layout

Backend: editor service for directory tree reading and file CRUD,
editor routes at /api/editor with path traversal protection.

Frontend: FileExplorer tree component with expand/collapse directories,
IDELayout with sidebar + header + outlet, wired into App routing.
Editor now receives state as props from App for cross-component file loading.
This commit is contained in:
plenarius
2026-04-20 19:09:19 -04:00
parent eaca2d8a6c
commit 02ca134743
8 changed files with 473 additions and 7 deletions
+51
View File
@@ -0,0 +1,51 @@
import { Router } from "express";
import {
getDirectoryTree,
readFile,
writeFile,
deleteFile,
} from "../services/editor.service.js";
const router = Router();
router.get("/tree/:repo", async (req, res) => {
try {
const tree = await getDirectoryTree(req.params.repo);
res.json(tree);
} catch (err: unknown) {
const message = err instanceof Error ? err.message : "Unknown error";
res.status(500).json({ error: message });
}
});
router.get("/file/:repo/*path", async (req, res) => {
try {
const content = await readFile(req.params.repo, req.params.path);
res.json({ content });
} catch (err: unknown) {
const message = err instanceof Error ? err.message : "Unknown error";
res.status(404).json({ error: message });
}
});
router.put("/file/:repo/*path", async (req, res) => {
try {
await writeFile(req.params.repo, req.params.path, req.body.content);
res.json({ ok: true });
} catch (err: unknown) {
const message = err instanceof Error ? err.message : "Unknown error";
res.status(500).json({ error: message });
}
});
router.delete("/file/:repo/*path", async (req, res) => {
try {
await deleteFile(req.params.repo, req.params.path);
res.json({ ok: true });
} catch (err: unknown) {
const message = err instanceof Error ? err.message : "Unknown error";
res.status(500).json({ error: message });
}
});
export default router;