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
130 lines
4.2 KiB
TypeScript
130 lines
4.2 KiB
TypeScript
"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 Vibn’s 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>
|
||
);
|
||
}
|