From a7a7d803300debce76254c571c55b534a52bd990 Mon Sep 17 00:00:00 2001 From: Harry Phan Date: Sun, 21 Jun 2026 03:09:39 +0700 Subject: [PATCH 1/2] feat(web): redesign landing + overview, restyle memory page, polish hero mock - Landing: saas222-style floating-pill header, centered single-column hero with serif-italic accent words and a wide dashboard mock; split "Open ContextMeM" CTA. - New in-app Overview dashboard at /app/home (fixes Home tab bouncing to landing); real counts from facts/history, namespaces table with per-namespace MCP URL. - Dashboard light restyle: Space Grotesk + JetBrains Mono via a CSS token bridge. - /app/memory facts restyle: display section/topic/entity headings, mono metadata, soft-green count pills + group badges, roomier rounded cards (entity-name link colors preserved, type-driven). - Fix clipped/illegible hero preview mock: remove the destructive bottom mask-fade (was cutting the metrics row) and fix dark-on-dark log text contrast. --- apps/web/index.html | 3 + .../src/components/blocks/navigation-10.tsx | 10 +- apps/web/src/main.tsx | 965 ++++++--------- apps/web/src/styles.css | 1078 +++++++++++++++++ 4 files changed, 1486 insertions(+), 570 deletions(-) diff --git a/apps/web/index.html b/apps/web/index.html index 5f4091c..6d57def 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -14,6 +14,9 @@ + + +
diff --git a/apps/web/src/components/blocks/navigation-10.tsx b/apps/web/src/components/blocks/navigation-10.tsx index 513beab..99581c7 100644 --- a/apps/web/src/components/blocks/navigation-10.tsx +++ b/apps/web/src/components/blocks/navigation-10.tsx @@ -42,9 +42,13 @@ export default function Navigation10({
diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index d95bd8a..44c80c0 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -1392,28 +1392,53 @@ function ContextMemExperience() { let body = (await response.json()) as { job: HostedExtractionJob; demo?: { remainingToday?: number } }; lastJob = body.job; setDemoPreview(demoPreviewStateFromJob(body.job, displayTarget)); - for (let attempt = 0; attempt < 20 && (body.job.status === "queued" || body.job.status === "running"); attempt += 1) { - await delay(800); + // Prod runs the build async on a Worker queue, so a real Walrus Site can + // take a couple of minutes. Poll ~3 min (200 × 900ms) before handing off + // to a graceful "still building" state instead of falsely erroring. + for (let attempt = 0; attempt < 200 && (body.job.status === "queued" || body.job.status === "running"); attempt += 1) { + await delay(900); const status = await fetch(`${API_BASE}/api/demo/extractions/${encodeURIComponent(body.job.id)}`); if (!status.ok) throw new Error(await readResponseError(status)); body = (await status.json()) as { job: HostedExtractionJob }; lastJob = body.job; setDemoPreview(demoPreviewStateFromJob(body.job, displayTarget)); } + if (body.job.status === "failed") throw new Error(body.job.error ?? "Context build failed."); const shareId = (body.job.result as { share?: { id?: string } } | undefined)?.share?.id; - if (!shareId) throw new Error(body.job.error ?? "Demo extraction finished without a share page."); - setDemoPreview({ - phase: "completed", - target: body.job.target || displayTarget, - jobId: body.job.id, - shareId, - message: "Preview ready. Opening share page", - updatedAt: Date.now() - }); - navigate(`/share/${shareId}`); - window.requestAnimationFrame(() => window.scrollTo({ top: 0, behavior: "smooth" })); + if (shareId) { + setDemoPreview({ + phase: "completed", + target: body.job.target || displayTarget, + jobId: body.job.id, + shareId, + message: "Preview ready. Opening share page", + updatedAt: Date.now() + }); + navigate(`/share/${shareId}`); + window.requestAnimationFrame(() => window.scrollTo({ top: 0, behavior: "smooth" })); + return; + } + if (body.job.status === "queued" || body.job.status === "running") { + // Our local poll budget ran out but the Worker is still building — don't + // surface this as a failure. The share page will appear shortly. + setDemoPreview({ + phase: "queued", + target: body.job.target || displayTarget, + jobId: body.job.id, + message: "Still building — larger sites take a couple of minutes. Your share page will be ready shortly; reload or check Runs.", + updatedAt: Date.now() + }); + setAuthHint("Still building on the Worker — larger sites take a couple of minutes. The share page will appear shortly."); + return; + } + throw new Error(body.job.error ?? "Demo extraction finished without a share page."); } catch (err) { - const message = err instanceof Error ? err.message : String(err); + const raw = err instanceof Error ? err.message : String(err); + // Placeholder hosts (example.com/.org/.net) come back tagged from the + // Worker — replace the noisy tagged message with one clean line. + const message = raw.includes("DEMO_PLACEHOLDER_HOST") + ? "That looks like a placeholder URL (example.com). Try a real Walrus Site — e.g. https://fmsprint.wal.app/ — or a public product URL." + : raw; setDemoPreview({ phase: "failed", target: lastJob?.target || displayTarget, @@ -1489,13 +1514,17 @@ function ContextMemExperience() { /> ); - function renderShell(pageTitle: string, pageDescription: string, child: React.ReactNode) { + function renderShell(pageTitle: string, pageDescription: string, child: React.ReactNode, allowAnon = false) { const lockedContent = ( <> {demoPreview ? openApp("/")} /> : null} {lockScreen} ); + // Read-only pages (namespace gallery, knowledge-graph explorer) hit only public + // GET endpoints, so they render for anonymous visitors. Write/build/settings pages + // stay gated on a delegate. + const unlocked = allowAnon || hasMemWalDelegate; return ( - {hasMemWalDelegate ? {child} : lockedContent} + {unlocked ? {child} : lockedContent} ); } @@ -1537,7 +1567,8 @@ function ContextMemExperience() { onHeroMouseMove={handleHeroMouseMove} onHeroMouseLeave={resetHeroMotion} onHeroAction={startRunFromLanding} - onOpenApp={() => openApp("/app")} + onOpenApp={() => openApp("/app/home")} + onImportDelegate={() => openApp("/app/settings")} onInspectArtifacts={() => openApp("/app/artifacts")} onOpenHistory={() => openApp("/app/runs")} /> @@ -1545,6 +1576,7 @@ function ContextMemExperience() { /> } /> } /> + , true)} /> + , + true )} /> )} /> )} /> - )} /> + , true)} /> +