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:
@@ -54,9 +54,6 @@ export function App() {
|
||||
<Route path="/jobs/:id" element={<Dashboard.JobsPage />} />
|
||||
<Route path="/dashboards" element={<Dashboard.DashboardsScreen />} />
|
||||
<Route path="/dashboards/:id" element={<Dashboard.DashboardScreen />} />
|
||||
<Route path="/projects" element={<Dashboard.ProjectListScreen />} />
|
||||
<Route path="/projects/new" element={<Dashboard.NewProjectRedirect />} />
|
||||
<Route path="/projects/:id" element={<Dashboard.ProjectScreen />} />
|
||||
<Route path="/email" element={<Dashboard.EmailScreen />} />
|
||||
<Route path="/email/:emailId" element={<Dashboard.EmailScreen />} />
|
||||
<Route path="/browser" element={<Dashboard.BrowserScreen />} />
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -19,7 +19,6 @@ const RULES: TitleRule[] = [
|
||||
{ match: (p) => p.startsWith('/soulseek'), title: 'Soulseek' },
|
||||
{ match: (p) => p.startsWith('/system-monitor'), title: 'System Monitor' },
|
||||
{ match: (p) => p.startsWith('/code-editor'), title: 'Code Editor' },
|
||||
{ match: (p) => p.startsWith('/projects'), title: 'Projects' },
|
||||
{ match: (p) => p.startsWith('/task-logs'), title: 'Task Logs' },
|
||||
{ match: (p) => p.startsWith('/tasks'), title: 'Tasks' },
|
||||
{ match: (p) => p.startsWith('/jobs'), title: 'Jobs' },
|
||||
|
||||
Reference in New Issue
Block a user