Enable Gemini thinking stream

This commit is contained in:
2026-06-15 11:37:29 -07:00
parent b3ec779058
commit e58972d594
5 changed files with 72 additions and 17 deletions

View File

@@ -161,7 +161,12 @@ export default function PreviewTab() {
if (loading || !anatomy) return;
ensureCalledRef.current = true;
fetch(`/api/projects/${projectId}/dev-server/ensure`, {
let url = `/api/projects/${projectId}/dev-server/ensure`;
if (selectedPort) {
url += `?port=${selectedPort}`;
}
fetch(url, {
method: "POST",
credentials: "include",
})
@@ -238,7 +243,9 @@ export default function PreviewTab() {
onStart={() => {
setIsForceStarting(true);
fetch(
`/api/projects/${projectId}/dev-server/ensure?forceStart=true`,
`/api/projects/${projectId}/dev-server/ensure?forceStart=true${
selectedPort ? `&port=${selectedPort}` : ""
}`,
{
method: "POST",
},

View File

@@ -56,7 +56,18 @@ export async function POST(
const projectSlug = (project.data?.slug as string) || project.id;
const projectName = (project.data?.name as string) || "Project";
// 1. Is a dev server already active on the primary port?
const url = new URL(request.url);
const requestedPortStr = url.searchParams.get("port");
const forceStart = url.searchParams.get("forceStart") === "true";
// 1. Is a dev server already active on the requested port (or most recent port)?
let portFilterSql = "";
const queryParams: any[] = [projectId];
if (requestedPortStr) {
portFilterSql = "AND port = $2";
queryParams.push(parseInt(requestedPortStr, 10));
}
const active = await queryOne<{
id: string;
state: string;
@@ -67,13 +78,13 @@ export async function POST(
`SELECT id, state, preview_url, command, port
FROM fs_dev_servers
WHERE project_id = $1
AND port = 3000
${portFilterSql}
AND (
state = 'running' OR
(state = 'starting' AND started_at > NOW() - INTERVAL '15 minutes')
)
ORDER BY started_at DESC LIMIT 1`,
[projectId],
queryParams,
);
// A `starting` row is mid cold-boot; the readiness probe will promote it to
@@ -124,7 +135,17 @@ export async function POST(
}
// 2. Do we have a previous config to restart from?
// (Limit to port 3000 since that's what the preview pane embeds)
// (Limit to requested port or most recent)
let lastPortFilterSql = "";
const lastQueryParams: any[] = [projectId];
if (requestedPortStr) {
lastPortFilterSql = "AND port = $2";
lastQueryParams.push(parseInt(requestedPortStr, 10));
} else if (active?.port) {
lastPortFilterSql = "AND port = $2";
lastQueryParams.push(active.port);
}
const last = await queryOne<{
command: string;
port: number;
@@ -132,13 +153,12 @@ export async function POST(
}>(
`SELECT command, port, preview_url
FROM fs_dev_servers
WHERE project_id = $1 AND port = 3000
WHERE project_id = $1 ${lastPortFilterSql}
ORDER BY started_at DESC LIMIT 1`,
[projectId],
lastQueryParams,
);
const forceStart =
new URL(request.url).searchParams.get("forceStart") === "true";
const forceStartQuery = forceStart; // we already parsed this above
// If there's no history, we STILL want to auto-start! We just assume it's a standard
// Next.js app on port 3000. Forcing the user to hit "Start Preview" on a new project

View File

@@ -635,9 +635,23 @@ function Timeline({ entries }: { entries: TimelineEntry[] }) {
<div style={{ marginBottom: 6 }}>
{items.map((item, i) => {
if (item.kind === "thought") {
// Reasoning/thought bubbles are intentionally not rendered — they're
// internal and add noise to the chat.
return null;
return (
<div
key={i}
style={{
fontSize: "0.85rem",
color: "#6b7280",
fontStyle: "italic",
padding: "8px 12px",
background: "#f9fafb",
borderLeft: "2px solid #d1d5db",
marginBottom: 8,
whiteSpace: "pre-wrap",
}}
>
Thinking: {item.text}
</div>
);
}
if (item.kind === "text") {
return <TimelineText key={i} text={item.text} />;

View File

@@ -413,9 +413,9 @@ export async function suspendDevContainer(projectId: string): Promise<void> {
[projectId],
);
// Also mark the fixed port-3000 app as stopped so the UI knows
// Also mark ALL preview servers as stopped so the UI knows
await query(
`UPDATE fs_dev_servers SET state = 'stopped' WHERE project_id = $1 AND port = 3000`,
`UPDATE fs_dev_servers SET state = 'stopped' WHERE project_id = $1 AND state != 'stopped'`,
[projectId],
).catch(() => {});
}
@@ -435,9 +435,9 @@ export async function resumeDevContainer(projectId: string): Promise<void> {
[projectId],
);
// Mark the fixed port-3000 app as running again since the container boots it
// Mark the last run preview server as starting again since we may need to boot it
await query(
`UPDATE fs_dev_servers SET state = 'running' WHERE project_id = $1 AND port = 3000`,
`UPDATE fs_dev_servers SET state = 'starting' WHERE project_id = $1 AND state = 'stopped' AND id = (SELECT id FROM fs_dev_servers WHERE project_id = $1 ORDER BY started_at DESC LIMIT 1)`,
[projectId],
).catch(() => {});
}
@@ -1065,10 +1065,12 @@ export async function startDevServer(
const logFile = `/var/log/vibn-dev/${id}.log`;
const listenSafeCommand = ensurePreviewListenAllInterfaces(opts.command);
const secret = devAuthSecret(opts.projectId);
const launch =
`mkdir -p /var/log/vibn-dev && ` +
`cd /workspace && ` +
`nohup env PORT=${opts.port} VIBN_DEV_SERVER_ID=${id} ` +
`AUTH_SECRET=${secret} NEXTAUTH_SECRET=${secret} AUTH_TRUST_HOST=true ` +
`bash -lc ${shellEscape(listenSafeCommand)} > ${logFile} 2>&1 & ` +
`echo $!`;