feat(canvas): fullscreen workspace outside the app shell

Move /projects/:id and its service detail route out of PlatformShell —
the project view now owns the entire viewport like Railway's canvas:
no sidebar, no global nav, just the slim project bar over the graph.
Service detail inherits the same chrome-free context with a breadcrumb
back to the canvas, and back/delete navigation preserves ?demo=1.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-23 18:20:05 +02:00
1 parent 1425618c92
commit d9bfd8a2fd
3 files changed
+10 -6

No files matched your search

+4 -2
View File
@@ -116,10 +116,12 @@ export default function App() {
<Route path="/auth/sign-up" element={<SignUpPage />} /> <Route path="/auth/sign-up" element={<SignUpPage />} />
{/* Public browsing — approved projects, templates, docs. */} {/* Public browsing — approved projects, templates, docs. */}
<Route element={<PlatformShell />}> {/* Project workspace renders outside the shell — fullscreen canvas, no nav chrome */}
<Route path="/projects" element={<ProjectsPage />} />
<Route path="/projects/:projectId" element={<ProjectWorkspacePage />} /> <Route path="/projects/:projectId" element={<ProjectWorkspacePage />} />
<Route path="/projects/:projectId/services/:serviceId" element={<ServiceDetailPage />} /> <Route path="/projects/:projectId/services/:serviceId" element={<ServiceDetailPage />} />
<Route element={<PlatformShell />}>
<Route path="/projects" element={<ProjectsPage />} />
<Route path="/templates" element={<TemplatesPage />} /> <Route path="/templates" element={<TemplatesPage />} />
<Route path="/databases" element={<DatabasesPage />} /> <Route path="/databases" element={<DatabasesPage />} />
<Route path="/ha" element={<HighAvailabilityPage />} /> <Route path="/ha" element={<HighAvailabilityPage />} />
@@ -384,7 +384,7 @@ export function ServiceDetailPage() {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['project-services', projectId] }); queryClient.invalidateQueries({ queryKey: ['project-services', projectId] });
queryClient.invalidateQueries({ queryKey: ['projects'] }); queryClient.invalidateQueries({ queryKey: ['projects'] });
navigate(`/projects/${projectId}`); navigate(isDemoMode ? `/projects/${projectId}?demo=1` : `/projects/${projectId}`);
}, },
}); });
@@ -644,7 +644,7 @@ export function ProjectWorkspacePage() {
This project may have been deleted or you don't have access. This project may have been deleted or you don't have access.
</p> </p>
<button <button
onClick={() => navigate('/projects')} onClick={() => navigate(isDemoMode ? '/projects?demo=1' : '/projects')}
className="mt-6 px-4 py-2 rounded-[var(--radius-md)] border border-[var(--border-subtle)] text-sm font-medium hover:border-[var(--border-default)] transition-colors" className="mt-6 px-4 py-2 rounded-[var(--radius-md)] border border-[var(--border-subtle)] text-sm font-medium hover:border-[var(--border-default)] transition-colors"
> >
Back to Projects Back to Projects
@@ -656,12 +656,14 @@ export function ProjectWorkspacePage() {
const canvasView = activeView === 'canvas'; const canvasView = activeView === 'canvas';
const projectsHref = isDemoMode ? '/projects?demo=1' : '/projects';
return ( return (
<div className="flex h-full min-h-0 flex-col"> <div className="flex h-dvh min-h-0 flex-col bg-[var(--bg-void)]">
{/* Slim workspace bar — tabs + project meta, canvas fills everything below */} {/* Slim workspace bar — tabs + project meta, canvas fills everything below */}
<div className="flex h-12 shrink-0 items-center gap-3 border-b border-[var(--border-subtle)] bg-[var(--bg-base)]/80 px-4 backdrop-blur-sm"> <div className="flex h-12 shrink-0 items-center gap-3 border-b border-[var(--border-subtle)] bg-[var(--bg-base)]/80 px-4 backdrop-blur-sm">
<button <button
onClick={() => navigate('/projects')} onClick={() => navigate(projectsHref)}
className="flex items-center gap-1.5 text-xs text-[var(--text-tertiary)] hover:text-[var(--text-primary)] transition-colors shrink-0" className="flex items-center gap-1.5 text-xs text-[var(--text-tertiary)] hover:text-[var(--text-primary)] transition-colors shrink-0"
> >
<ArrowLeft size={14} /> <ArrowLeft size={14} />