feat(ui): sidebar project tree, status bar and a tabbed workbench for workspaces

The Workspace page's lane strip moves into the sidebar as a project tree:
workspaces grouped by git repository (new GET /api/projects, cached in
memory, POST /api/projects/refresh), untracked worktrees with Adopt, inline
rename, drag/Alt+arrow ordering and a right-click row menu. The selected
workspace is the URL's ?lane=, one shared lane list feeds the tree, the new
bottom status bar and the page. The Changes panel gains an activity bar
(Files / Changes / Sessions, with resume), and split panes get a header.
This commit is contained in:
2026-10-01 16:33:25 +07:00
parent 5b3884b309
commit ba2c4e1914
39 changed files with 2431 additions and 1439 deletions
+25
View File
@@ -0,0 +1,25 @@
/**
* @file Express router for /api/projects: every lane grouped by the git repo
* it belongs to, with that repo's worktrees (see server/lib/projects.js). Read
* on demand by the sidebar's project tree, never polled; `POST /refresh` drops
* the cached roots and worktree lists first.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { Router } = require("express");
const lanesLib = require("../lib/lanes");
const { listProjects, clearProjectCaches } = require("../lib/projects");
const { sameOriginGuard } = require("../lib/same-origin");
const router = Router();
router.get("/", async (_req, res) => {
res.json({ projects: await listProjects(lanesLib.listLanes()) });
});
router.post("/refresh", sameOriginGuard, async (_req, res) => {
clearProjectCaches();
res.json({ projects: await listProjects(lanesLib.listLanes()) });
});
module.exports = router;