remove the projects feature

Being retired, so it is deleted rather than fixed — it had both defects dashboards just
had (edit dropping ?selected=, an abandoned edit following you onto the next item) and
repairing them was work for something on its way out.

Gone: the two screens and the panel apps, the three routes, the dock item and its entry
in the default dock paths (client and the three server-side copies), the page-title rule,
the app-registry entries, the barrel exports, the `projects` table with its queries and
row types, and the proj-meta/proj-layout/proj-terminals/proj-host-terminals branches in
the dashboards endpoint. The chat context and terminal state-key special cases for
`proj-layout-` went with them.

Deliberately kept: `getUserProjectsDir` in data-path.ts — the apps and dev-server routers
resolve user apps under the same on-disk Projects/ directory and are unrelated to this
feature. Nothing on disk is touched.

Needs `bun db:push` to drop the table; the schema change is the only thing standing
between the code and the database. One row was in it.

tsgo clean, 56/56 tests, no references left in src. Not yet exercised at runtime — the
restart is what will prove it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 08:16:27 +00:00
co-authored by Claude Opus 5
parent 4970e7e70f
commit 5a1e3d7e0b
27 changed files with 95 additions and 1411 deletions
-72
View File
@@ -1,21 +1,13 @@
import { mkdir, rm, cp } from 'node:fs/promises';
import { join, resolve } from 'node:path';
import { readdirSync } from 'node:fs';
import { createRouter } from '@@/create-router';
import { getUserProjectsDir } from '@@/data-path';
import {
getAllDashboardState,
upsertDashboard,
deleteDashboard,
upsertScreen,
deleteScreen,
upsertProject,
deleteProject,
upsertDefaults,
} from 'officerdb';
const TEMPLATES_DIR = resolve(import.meta.dir, '../../../../seed/project-templates');
export const dashboardsRouter = createRouter();
// GET /dashboards
@@ -93,70 +85,6 @@ dashboardsRouter.patch('/', async (ctx) => {
continue;
}
// proj-meta-{slug} — create/update/delete project
const projMetaMatch = key.match(/^proj-meta-(.+)$/);
if (projMetaMatch) {
const slug = projMetaMatch[1]!;
const projectDir = join(getUserProjectsDir(user.email), slug);
if (value === null) {
await deleteProject(userId, slug);
await rm(projectDir, { recursive: true, force: true });
continue;
}
const meta = value as Record<string, unknown>;
const isNew = !(await Bun.file(join(projectDir, '.officerdev', 'meta.json')).exists());
await upsertProject(userId, slug, { meta: value });
if (isNew) {
if (meta.projectType === 'app') {
const templateDir = join(TEMPLATES_DIR, 'simple-app-template');
const entries = readdirSync(templateDir);
for (const entry of entries) {
if (entry === '.git' || entry === '.officerdev') continue;
await cp(join(templateDir, entry), join(projectDir, entry), { recursive: true });
}
const pkgPath = join(projectDir, 'package.json');
const pkg = await Bun.file(pkgPath)
.json()
.catch(() => null);
if (pkg) {
pkg.name = slug;
await Bun.write(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
}
const install = Bun.spawn(['bun', 'install'], { cwd: projectDir, stdout: 'ignore', stderr: 'ignore' });
await install.exited;
}
const gitInit = Bun.spawn(['git', 'init', projectDir], { stdout: 'ignore', stderr: 'ignore' });
await gitInit.exited;
}
continue;
}
// proj-layout-{slug}
const projLayoutMatch = key.match(/^proj-layout-(.+)$/);
if (projLayoutMatch) {
const slug = projLayoutMatch[1]!;
await upsertProject(userId, slug, { layout: value });
continue;
}
// proj-terminals-{slug}
const projTerminalsMatch = key.match(/^proj-terminals-(.+)$/);
if (projTerminalsMatch) {
const slug = projTerminalsMatch[1]!;
await upsertProject(userId, slug, { terminals: value });
continue;
}
// proj-host-terminals-{slug}
const projHostTerminalsMatch = key.match(/^proj-host-terminals-(.+)$/);
if (projHostTerminalsMatch) {
const slug = projHostTerminalsMatch[1]!;
await upsertProject(userId, slug, { hostTerminals: value });
continue;
}
}
const state = await getAllDashboardState(userId);
+1 -1
View File
@@ -308,7 +308,7 @@ export const googleCallbackHandler = async (ctx: any) => {
// Auto-add /email to dock
try {
const existing = await getDockPaths(dbUser.id);
const paths = existing ?? ['/', '/files', '/automation', '/projects', '/dashboards', '/chat'];
const paths = existing ?? ['/', '/files', '/automation', '/dashboards', '/chat'];
if (!paths.includes('/email')) {
paths.push('/email');