Rip out Theia, ship P5.1 attach E2E + Justine UI work-in-progress

Theia rip-out:
- Delete app/api/theia-auth/route.ts (Traefik ForwardAuth shim)
- Delete app/api/projects/[projectId]/workspace/route.ts and
  app/api/projects/prewarm/route.ts (Cloud Run Theia provisioning)
- Delete lib/cloud-run-workspace.ts and lib/coolify-workspace.ts
- Strip provisionTheiaWorkspace + theiaWorkspaceUrl/theiaAppUuid/
  theiaError from app/api/projects/create/route.ts response
- Remove Theia callbackUrl branch in app/auth/page.tsx
- Drop "Open in Theia" button + xterm/Theia PTY copy in build/page.tsx
- Drop theiaWorkspaceUrl from deployment/page.tsx Project type
- Strip Theia IDE line + theia-code-os from advisor + agent-chat
  context strings
- Scrub Theia mention from lib/auth/workspace-auth.ts comment

P5.1 (custom apex domains + DNS):
- lib/coolify.ts + lib/opensrs.ts: nameserver normalization, OpenSRS
  XML auth, Cloud DNS plumbing
- scripts/smoke-attach-e2e.ts: full prod GCP + sandbox OpenSRS +
  prod Coolify smoke covering register/zone/A/NS/PATCH/cleanup

In-progress (Justine onboarding/build, MVP setup, agent telemetry):
- New (justine)/stories, project (home) layouts, mvp-setup, run, tasks
  routes + supporting components
- Project shell + sidebar + nav refactor for the Stackless palette
- Agent session API hardening (sessions, events, stream, approve,
  retry, stop) + atlas-chat, advisor, design-surfaces refresh
- New scripts/sync-db-url-from-coolify.mjs +
  scripts/prisma-db-push.mjs + docker-compose.local-db.yml for
  local Prisma workflows
- lib/dev-bypass.ts, lib/chat-context-refs.ts, lib/prd-sections.ts
- Misc: stories CSS, debug/prisma route, modal-theme, BuildLivePlanPanel

Made-with: Cursor
This commit is contained in:
2026-04-22 18:05:01 -07:00
parent d6c87a052e
commit 651ddf1e11
105 changed files with 7509 additions and 2319 deletions

View File

@@ -0,0 +1,129 @@
"use client";
import { Suspense, useCallback, useEffect, useState } from "react";
import { JM, JV } from "@/components/project-creation/modal-theme";
import { AtlasChat } from "@/components/AtlasChat";
import {
BuildLivePlanPanel,
addSectionContextRef,
} from "@/components/project-main/BuildLivePlanPanel";
import {
type ChatContextRef,
contextRefKey,
} from "@/lib/chat-context-refs";
export function MvpSetupDescribeView({ projectId, workspace }: { projectId: string; workspace: string }) {
const [chatContextRefs, setChatContextRefs] = useState<ChatContextRef[]>([]);
const [tab, setTab] = useState<"chat" | "plan">("chat");
const [narrow, setNarrow] = useState(false);
useEffect(() => {
const mq = window.matchMedia("(max-width: 900px)");
const apply = () => setNarrow(mq.matches);
apply();
mq.addEventListener("change", apply);
return () => mq.removeEventListener("change", apply);
}, []);
const removeChatContextRef = useCallback((key: string) => {
setChatContextRefs(prev => prev.filter(r => contextRefKey(r) !== key));
}, []);
const addPlanSectionToChat = useCallback((label: string, phaseId: string | null) => {
setChatContextRefs(prev => addSectionContextRef(prev, label, phaseId));
}, []);
return (
<div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0, background: JV.chatColumnBg }}>
<div
style={{
padding: "18px 28px 14px",
background: "#fff",
borderBottom: `1px solid ${JM.border}`,
flexShrink: 0,
}}
>
<div style={{ fontSize: 17, fontWeight: 700, color: JM.ink, marginBottom: 3, fontFamily: JM.fontDisplay }}>
Describe
</div>
<div style={{ fontSize: 12.5, color: JM.muted }}>
Tell Vibn about your idea your plan fills in on the right as you go.
</div>
</div>
{narrow && (
<div
style={{
display: "flex",
borderBottom: `1px solid ${JM.border}`,
background: "#EEF0FF",
flexShrink: 0,
}}
>
{(["chat", "plan"] as const).map(id => (
<button
key={id}
type="button"
onClick={() => setTab(id)}
style={{
flex: 1,
padding: "11px 8px",
border: "none",
background: "transparent",
fontSize: 13,
fontWeight: tab === id ? 600 : 500,
color: tab === id ? JM.indigo : JM.muted,
borderBottom: tab === id ? `2px solid ${JM.indigo}` : "2px solid transparent",
cursor: "pointer",
fontFamily: JM.fontSans,
}}
>
{id === "chat" ? "Chat" : "Your plan"}
</button>
))}
</div>
)}
<div style={{ flex: 1, display: "flex", minHeight: 0, overflow: "hidden" }}>
<div
style={{
flex: 1,
minWidth: 0,
display: narrow && tab !== "chat" ? "none" : "flex",
flexDirection: "column",
}}
>
<AtlasChat
projectId={projectId}
conversationScope="overview"
contextEmptyLabel="Plan"
emptyStateHint="Answer Vibns questions — each phase you complete updates your plan."
chatContextRefs={chatContextRefs}
onRemoveChatContextRef={removeChatContextRef}
/>
</div>
<div
style={{
width: narrow ? undefined : 308,
flex: narrow && tab === "plan" ? 1 : undefined,
flexShrink: 0,
minWidth: 0,
display: narrow && tab !== "plan" ? "none" : "flex",
flexDirection: "column",
overflow: "hidden",
}}
>
<Suspense fallback={<div style={{ flex: 1, background: JV.prdPanelBg }} />}>
<BuildLivePlanPanel
projectId={projectId}
workspace={workspace}
chatContextRefs={chatContextRefs}
onAddSectionRef={addPlanSectionToChat}
compactHeader={narrow}
/>
</Suspense>
</div>
</div>
</div>
);
}