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
@@ -148,7 +148,6 @@ export const ALL_DOCK_ITEMS: DockItem[] = [
{ label: 'Jobs', to: '/jobs', icon: Workflow, color: '#14b8a6' },
{ label: 'Logs', to: '/task-logs', icon: ScrollText, color: '#94a3b8' },
{ label: 'Terminal', to: '/terminal', icon: Monitor, color: '#f97316' },
{ label: 'Projects', to: '/projects', icon: FolderKanban, color: '#10b981' },
{ label: 'Browser', to: '/browser', icon: Globe, color: '#06b6d4' },
{ label: 'Desktop', to: '/desktop', icon: MonitorSmartphone, color: '#ec4899' },
{ label: 'Monitor', to: '/system-monitor', icon: Activity, color: '#0ea5e9' },
@@ -156,4 +155,4 @@ export const ALL_DOCK_ITEMS: DockItem[] = [
{ label: 'Dashboards', to: '/dashboards', icon: LayoutGrid, color: '#8b5cf6' },
];
export const DEFAULT_DOCK_PATHS = ['/', '/files', '/music', '/projects', '/dashboards', '/chat'];
export const DEFAULT_DOCK_PATHS = ['/', '/files', '/music', '/dashboards', '/chat'];
@@ -1,77 +0,0 @@
import { useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router';
import { useGlobal } from 'hooks/useGlobal';
import { useIsMobile } from 'hooks/useIsMobile';
import { useDashboardState } from 'state/useDashboardState';
import type { LayoutNode, ProjectType } from 'officerdev';
import {
WorkspaceView,
CREATING_PROJECT,
EDITING_PROJECT,
NEW_PROJ_NAME,
NEW_PROJ_DESC,
NEW_PROJ_TEMPLATE,
NEW_PROJ_TYPE,
NEW_PROJ_HAS_BACKEND,
NEW_PROJ_HAS_AUTH,
} from 'officerdev';
import { generateSlug } from 'helpers/slug';
import { defaultLayout } from './defaultLayout';
export const ProjectListScreen = () => {
const workspace = useDashboardState<LayoutNode>('screens/projects', defaultLayout);
const isMobile = useIsMobile();
// Selection lives in the URL (?selected=<id>); on mobile it (or an active create) drives the panel shown.
const [searchParams, setSearchParams] = useSearchParams();
const selected = searchParams.get('selected');
const [creating] = useGlobal<boolean>(CREATING_PROJECT, false);
const mobilePanelId = isMobile && (selected || creating) ? 'proj-home-right' : undefined;
return (
<div className="h-full w-full">
<WorkspaceView
workspace={workspace}
locked
mobilePanelId={mobilePanelId}
onMobilePanelChange={(id) => {
if (!id)
setSearchParams(
(p) => {
p.delete('selected');
return p;
},
{ replace: true },
);
}}
/>
</div>
);
};
export const NewProjectRedirect = () => {
const navigate = useNavigate();
const [params] = useSearchParams();
const [, setName] = useGlobal<string>(NEW_PROJ_NAME, '');
const [, setDescription] = useGlobal<string>(NEW_PROJ_DESC, '');
const [, setTemplateIdx] = useGlobal<number>(NEW_PROJ_TEMPLATE, 0);
const [, setCreating] = useGlobal<boolean>(CREATING_PROJECT, false);
const [, setEditing] = useGlobal<string | null>(EDITING_PROJECT, null);
const [, setProjectType] = useGlobal<ProjectType>(NEW_PROJ_TYPE, 'app');
const [, setHasBackend] = useGlobal<boolean>(NEW_PROJ_HAS_BACKEND, false);
const [, setHasAuth] = useGlobal<boolean>(NEW_PROJ_HAS_AUTH, false);
useEffect(() => {
// Redirect lands on a clean /projects (no ?selected), so selection is naturally cleared.
setEditing(null);
setName(params.get('name') || generateSlug());
setDescription('');
setTemplateIdx(0);
setProjectType('app');
setHasBackend(false);
setHasAuth(false);
setCreating(true);
navigate('/projects', { replace: true });
}, []);
return null;
};
@@ -1,25 +0,0 @@
import { useParams, Navigate } from 'react-router';
import { useDashboardState } from 'state/useDashboardState';
import type { LayoutNode, ProjectDefinition } from 'officerdev';
import { WorkspaceView, createDefaultLayout } from 'officerdev';
export const ProjectScreen = () => {
const { id } = useParams<{ id: string }>();
const { value: projects, isLoaded } = useDashboardState<ProjectDefinition[]>('projects', []);
const project = projects.find((p) => p.id === id);
if (!isLoaded) return null;
if (!project) return <Navigate to="/projects" replace />;
return <ProjectScreenInner project={project} />;
};
const ProjectScreenInner = ({ project }: { project: ProjectDefinition }) => {
const workspace = useDashboardState<LayoutNode>(`proj-layout-${project.id}`, createDefaultLayout());
return (
<div className="h-full w-full">
<WorkspaceView workspace={workspace} cwd={project.cwd} />
</div>
);
};
@@ -1,11 +0,0 @@
import type { LayoutNode } from 'officerdev';
export const defaultLayout: LayoutNode = {
type: 'group',
id: 'proj-home-root',
direction: 'horizontal',
children: [
{ node: { type: 'panel', id: 'proj-home-list', appType: 'project-list' }, size: 25 },
{ node: { type: 'panel', id: 'proj-home-right', appType: 'project-preview' }, size: 75 },
],
};
@@ -1,2 +0,0 @@
export { ProjectListScreen, NewProjectRedirect } from './ProjectListScreen';
export { ProjectScreen } from './ProjectScreen';
@@ -17,7 +17,6 @@ export * from './Activity';
export * from './CodeEditor';
export * from './ChatHistory';
export * from './Dashboards';
export * from './Projects';
export * from './Terminal';
export * from './Email';
export * from './Browser';