From 92037610dbc3b1c60acd0dec501a42cfe7d29eb1 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Fri, 27 Feb 2026 12:49:59 -0800 Subject: [PATCH] feat: landing pages for rspace.online/{moduleId} with Try Demo CTA Exact module paths (rspace.online/rtube) now show a generated landing page with icon, name, description, and CTAs instead of immediately loading the demo app. Sub-paths still rewrite to /demo/... as before. Removes the iframe embed branch which caused CORS issues. Co-Authored-By: Claude Opus 4.6 --- server/index.ts | 19 ++++++++++++----- server/shell.ts | 55 ------------------------------------------------- 2 files changed, 14 insertions(+), 60 deletions(-) diff --git a/server/index.ts b/server/index.ts index 8c7c0a1..724ed89 100644 --- a/server/index.ts +++ b/server/index.ts @@ -63,7 +63,7 @@ import { dataModule } from "../modules/data/mod"; import { splatModule } from "../modules/splat/mod"; import { photosModule } from "../modules/photos/mod"; import { spaces } from "./spaces"; -import { renderShell } from "./shell"; +import { renderShell, renderModuleLanding } from "./shell"; import { syncServer } from "./sync-instance"; import { loadAllDocs } from "./local-first/doc-persistence"; @@ -779,14 +779,23 @@ const server = Bun.serve({ } // ── Bare-domain module routes: rspace.online/{moduleId}[/...] ── - // Rewrite to /demo/{moduleId}/... so the normal shell renders identically - // to how standalone domains (e.g. rtube.online) serve their content. + // Exact module path → landing page; sub-paths rewrite to /demo/... if (!subdomain && hostClean.includes("rspace.online")) { const pathSegments = url.pathname.split("/").filter(Boolean); if (pathSegments.length >= 1) { const firstSegment = pathSegments[0]; - const knownModuleIds = new Set(getAllModules().map((m) => m.id)); - if (knownModuleIds.has(firstSegment)) { + const allModules = getAllModules(); + const mod = allModules.find((m) => m.id === firstSegment); + if (mod) { + if (pathSegments.length === 1) { + // Exact module path → show landing page + const html = renderModuleLanding({ + module: mod, + modules: getModuleInfoList(), + }); + return new Response(html, { headers: { "Content-Type": "text/html" } }); + } + // Sub-paths → rewrite to /demo/{moduleId}/... const rewrittenPath = `/demo${url.pathname}`; const rewrittenUrl = new URL(rewrittenPath + url.search, `http://localhost:${PORT}`); const rewrittenReq = new Request(rewrittenUrl, req); diff --git a/server/shell.ts b/server/shell.ts index 3682a28..18fd33f 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -425,61 +425,6 @@ export interface ModuleLandingOptions { export function renderModuleLanding(opts: ModuleLandingOptions): string { const { module: mod, modules, theme = "dark" } = opts; const moduleListJSON = JSON.stringify(modules); - - // Modules with a standalone domain: embed it in a full-page iframe - if (mod.standaloneDomain) { - const embedUrl = `https://${escapeAttr(mod.standaloneDomain)}`; - return ` - - - - - - ${escapeHtml(mod.name)} — rSpace - - - - - - -
-
- -
-
- -
-
- -
- - -`; - } - - // Modules without a standalone domain: simple generated landing page const demoUrl = `https://demo.rspace.online/${mod.id}`; return `