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
-2
View File
@@ -59,8 +59,6 @@ export {
deleteDashboard,
upsertScreen,
deleteScreen,
upsertProject,
deleteProject,
getDefaults,
upsertDefaults,
} from './queries/dashboards';
@@ -1,16 +1,15 @@
import { eq, and } from 'drizzle-orm';
import { db } from '../db';
import { dashboards, screens, projects, dashboardDefaults } from '../schema';
import { dashboards, screens, dashboardDefaults } from '../schema';
// ── Full state read ──
export async function getAllDashboardState(userId: number): Promise<Record<string, unknown>> {
const result: Record<string, unknown> = {};
const [dashRows, screenRows, projectRows, defaultsRow] = await Promise.all([
const [dashRows, screenRows, defaultsRow] = await Promise.all([
db.select().from(dashboards).where(eq(dashboards.userId, userId)),
db.select().from(screens).where(eq(screens.userId, userId)),
db.select().from(projects).where(eq(projects.userId, userId)),
db
.select()
.from(dashboardDefaults)
@@ -41,20 +40,6 @@ export async function getAllDashboardState(userId: number): Promise<Record<strin
result[`screens/${s.name}`] = s.layout;
}
// Projects
const projectList = projectRows.map((p) => ({
...(p.meta as object),
id: p.slug,
cwd: `/Projects/${p.slug}`,
}));
result['projects'] = projectList;
for (const p of projectRows) {
result[`proj-layout-${p.slug}`] = p.layout;
result[`proj-terminals-${p.slug}`] = p.terminals;
result[`proj-host-terminals-${p.slug}`] = p.hostTerminals;
}
return result;
}
@@ -141,51 +126,6 @@ export async function deleteScreen(userId: number, name: string): Promise<void>
await db.delete(screens).where(and(eq(screens.userId, userId), eq(screens.name, name)));
}
// ── Project CRUD ──
type UpsertProjectData = {
meta?: unknown;
layout?: unknown;
terminals?: unknown;
hostTerminals?: unknown;
};
export async function upsertProject(userId: number, slug: string, data: UpsertProjectData): Promise<void> {
const now = new Date();
const existing = await db
.select()
.from(projects)
.where(and(eq(projects.userId, userId), eq(projects.slug, slug)))
.then((rows) => rows[0]);
if (existing) {
const set: Record<string, unknown> = { updatedAt: now };
if (data.meta !== undefined) set.meta = data.meta;
if (data.layout !== undefined) set.layout = data.layout;
if (data.terminals !== undefined) set.terminals = data.terminals;
if (data.hostTerminals !== undefined) set.hostTerminals = data.hostTerminals;
await db
.update(projects)
.set(set)
.where(and(eq(projects.userId, userId), eq(projects.slug, slug)));
} else {
await db.insert(projects).values({
userId,
slug,
meta: (data.meta ?? {}) as never,
layout: (data.layout ?? []) as never,
terminals: (data.terminals ?? []) as never,
hostTerminals: (data.hostTerminals ?? {}) as never,
createdAt: now,
updatedAt: now,
});
}
}
export async function deleteProject(userId: number, slug: string): Promise<void> {
await db.delete(projects).where(and(eq(projects.userId, userId), eq(projects.slug, slug)));
}
// ── Defaults ──
type UpsertDefaultsData = {
@@ -36,24 +36,6 @@ export const screens = pgTable(
(table) => [unique('uq_screens_user_name').on(table.userId, table.name)],
);
export const projects = pgTable(
'projects',
{
id: serial('id').primaryKey(),
userId: integer('user_id')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),
slug: text('slug').notNull(),
meta: jsonb('meta').notNull().default({}),
layout: jsonb('layout').notNull().default([]),
terminals: jsonb('terminals').notNull().default([]),
hostTerminals: jsonb('host_terminals').notNull().default({}),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(table) => [unique('uq_projects_user_slug').on(table.userId, table.slug)],
);
export const dashboardDefaults = pgTable('dashboard_defaults', {
id: serial('id').primaryKey(),
userId: integer('user_id')
-3
View File
@@ -45,9 +45,6 @@ export type DashboardInsert = typeof Schema.dashboards.$inferInsert;
export type ScreenSelect = typeof Schema.screens.$inferSelect;
export type ScreenInsert = typeof Schema.screens.$inferInsert;
export type ProjectSelect = typeof Schema.projects.$inferSelect;
export type ProjectInsert = typeof Schema.projects.$inferInsert;
// ── Operations ──
export type TaskLogSelect = typeof Schema.taskLogs.$inferSelect;