From f8cc4b32b0989df1c0c7667396307349f7db69b5 Mon Sep 17 00:00:00 2001 From: mawkone Date: Sun, 14 Jun 2026 14:52:16 -0700 Subject: [PATCH] feat(logs): add Dev Previews to server logs page --- .../project/[projectId]/(home)/logs/page.tsx | 158 ++++++++++++------ 1 file changed, 110 insertions(+), 48 deletions(-) diff --git a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx index 493b1de..386c43d 100644 --- a/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx +++ b/vibn-frontend/app/[workspace]/project/[projectId]/(home)/logs/page.tsx @@ -21,26 +21,40 @@ export default function LogsPage() { const { anatomy, loading } = useAnatomy(projectId, { pollMs: 8000 }); const live = anatomy?.hosting.live ?? []; - const [activeUuid, setActiveUuid] = useState(null); + const previews = anatomy?.hosting.previews ?? []; + + const [activeItem, setActiveItem] = useState<{ + type: "app" | "preview"; + id: string; + } | null>(null); const [logs, setLogs] = useState(null); const [logsLoading, setLogsLoading] = useState(false); - // Auto-select first app if none selected + // Auto-select first available item useEffect(() => { - if (live.length > 0 && !activeUuid) { - setActiveUuid(live[0].uuid); + if (activeItem) return; + if (live.length > 0) { + setActiveItem({ type: "app", id: live[0].uuid }); + } else if (previews.length > 0) { + setActiveItem({ type: "preview", id: previews[0].id }); } - }, [live, activeUuid]); + }, [live, previews, activeItem]); - const fetchLogs = async (uuid: string) => { + const fetchLogs = async (item: { type: "app" | "preview"; id: string }) => { setLogsLoading(true); try { + const action = item.type === "app" ? "apps.logs" : "dev_server.logs"; + const payloadParams = + item.type === "app" + ? { uuid: item.id, lines: 100 } + : { id: item.id, lines: 100, projectId }; + const r = await fetch(`/api/mcp`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - action: "apps.logs", - params: { uuid, lines: 100 }, + action, + params: payloadParams, }), }); const d = await r.json(); @@ -77,10 +91,10 @@ export default function LogsPage() { } }; - // Fetch when active app changes + // Fetch when active item changes useEffect(() => { - if (activeUuid) fetchLogs(activeUuid); - }, [activeUuid]); + if (activeItem) fetchLogs(activeItem); + }, [activeItem]); return (
Loading…
- ) : live.length === 0 ? ( + ) : live.length === 0 && previews.length === 0 ? (
} - title="No apps running" - hint="Once you deploy an app, its runtime logs will appear here." + title="No active servers" + hint="Once you deploy an app or start a dev server, its runtime logs will appear here." />
) : ( @@ -145,59 +159,51 @@ export default function LogsPage() { flexDirection: "column", borderRight: `1px solid ${THEME.borderSoft}`, overflowY: "auto", + paddingBottom: 24, }} > -
-

- Apps -

-
+ {live.length > 0 && ( +
+

+ Live Apps +

+
+ )}
{live.map((app) => ( ))}
+ + {previews.length > 0 && ( +
+

+ Dev Previews +

+
+ )} +
+ {previews.map((preview) => ( + + ))} +
{/* Log Viewer Column */} @@ -236,7 +294,11 @@ export default function LogsPage() { color: THEME.mid, }} > - {live.find((a) => a.uuid === activeUuid)?.name ?? "Logs"} + {activeItem?.type === "app" + ? (live.find((a) => a.uuid === activeItem.id)?.name ?? + "Logs") + : previews.find((p) => p.id === activeItem?.id)?.name || + "Dev Server Logs"} ) } - onClick={() => activeUuid && fetchLogs(activeUuid)} + onClick={() => activeItem && fetchLogs(activeItem)} disabled={logsLoading} style={{ padding: "4px 8px", fontSize: "0.75rem" }} >