diff --git a/server/index.ts b/server/index.ts index ca673fb..c618af0 100644 --- a/server/index.ts +++ b/server/index.ts @@ -2029,7 +2029,7 @@ app.post("/api/trips/ai-prompt", async (c) => { const geminiModel = genAI.getGenerativeModel({ model: GEMINI_MODELS[model], - tools: [{ functionDeclarations: allDeclarations }], + tools: [{ functionDeclarations: allDeclarations as any }], systemInstruction: systemPrompt || "You are a travel planning assistant.", }); @@ -3284,7 +3284,8 @@ const server = Bun.serve({ } // ── Bare-domain routing: rspace.online/{...} ── - if (!subdomain && hostClean.includes("rspace.online")) { + // Only match canonical bare domain, not stacked subdomains like rspace.rspace.online + if (!subdomain && (hostClean === "rspace.online" || hostClean === "www.rspace.online")) { // Top-level routes that must bypass module rewriting if (url.pathname.startsWith("/rtasks/check/")) { return app.fetch(req); @@ -3349,9 +3350,8 @@ const server = Bun.serve({ // Page navigation: redirect to canonical subdomain URL const space = firstSegment; const rest = "/" + pathSegments.slice(1).join("/"); - const baseDomain = hostClean.replace(/^www\./, ""); return Response.redirect( - `${proto}//${space}.${baseDomain}${rest}${url.search}`, 301 + `${proto}//${space}.rspace.online${rest}${url.search}`, 301 ); } } diff --git a/server/shell.ts b/server/shell.ts index 76843b7..47b1d81 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -173,7 +173,6 @@ export function renderShell(opts: ShellOptions): string { ...m, enabled: !enabledModules || enabledModules.includes(m.id) || m.id === "rspace", }))); - const shellDemoUrl = `https://demo.rspace.online/${escapeAttr(moduleId)}`; return versionAssetUrls(` @@ -241,7 +240,6 @@ export function renderShell(opts: ShellOptions): string {
- Try Demo @@ -439,23 +437,6 @@ export function renderShell(opts: ShellOptions): string { _switcher?.setModules(window.__rspaceModuleList); _switcher?.setAllModules(window.__rspaceAllModules); - // ── "Try Demo" button visibility ── - // Hidden when logged in. When logged out, shown everywhere except demo.rspace.online - // (bare rspace.online rewrites to demo internally but still shows the button). - (function() { - var btn = document.querySelector('.rstack-header__demo-btn'); - if (!btn) return; - function update() { - var loggedIn = false; - try { loggedIn = !!localStorage.getItem('encryptid_session'); } catch(e) {} - if (loggedIn) { btn.setAttribute('data-hide', ''); return; } - var host = window.location.host.split(':')[0]; - if (host === 'demo.rspace.online') { btn.setAttribute('data-hide', ''); } - else { btn.removeAttribute('data-hide'); } - } - update(); - document.addEventListener('auth-change', update); - })(); // ── Welcome tour (guided feature walkthrough for first-time visitors) ── (function() { @@ -2065,7 +2046,6 @@ export function renderModuleLanding(opts: ModuleLandingOptions): string { - Try Demo
@@ -2082,17 +2062,6 @@ export function renderModuleLanding(opts: ModuleLandingOptions): string { navigator.serviceWorker.register("/sw.js").catch(() => {}); } document.querySelector('rstack-app-switcher')?.setModules(${moduleListJSON}); - function _updateDemoBtn() { - var btn = document.querySelector('.rstack-header__demo-btn'); - if (!btn) return; - try { - var raw = localStorage.getItem('encryptid_session'); - if (raw && JSON.parse(raw)?.accessToken) { btn.setAttribute('data-hide', ''); } - else { btn.removeAttribute('data-hide'); } - } catch(e) {} - } - _updateDemoBtn(); - document.addEventListener('auth-change', _updateDemoBtn); try { var raw = localStorage.getItem('encryptid_session'); if (raw) { @@ -2412,7 +2381,6 @@ export function renderSubPageInfo(opts: SubPageInfoOptions): string { - Try Demo
@@ -2429,17 +2397,6 @@ export function renderSubPageInfo(opts: SubPageInfoOptions): string { navigator.serviceWorker.register("/sw.js").catch(() => {}); } document.querySelector('rstack-app-switcher')?.setModules(${moduleListJSON}); - function _updateDemoBtn() { - var btn = document.querySelector('.rstack-header__demo-btn'); - if (!btn) return; - try { - var raw = localStorage.getItem('encryptid_session'); - if (raw && JSON.parse(raw)?.accessToken) { btn.setAttribute('data-hide', ''); } - else { btn.removeAttribute('data-hide'); } - } catch(e) {} - } - _updateDemoBtn(); - document.addEventListener('auth-change', _updateDemoBtn); try { var raw = localStorage.getItem('encryptid_session'); if (raw) { diff --git a/shared/components/rstack-identity.ts b/shared/components/rstack-identity.ts index 600b245..6953bb7 100644 --- a/shared/components/rstack-identity.ts +++ b/shared/components/rstack-identity.ts @@ -333,13 +333,18 @@ function _getCurrentModule(): string { } function _navUrl(space: string, moduleId: string): string { const h = window.location.host.split(":")[0].split("."); - const onSub = h.length >= 3 && h.slice(-2).join(".") === "rspace.online" && !_RESERVED.includes(h[0]); + const proto = window.location.protocol; + const BASE = "rspace.online"; + const onSub = h.length >= 3 && h.slice(-2).join(".") === BASE && !_RESERVED.includes(h[0]); if (onSub) { if (h[0] === space) return "/" + moduleId; - return window.location.protocol + "//" + space + "." + h.slice(-2).join(".") + "/" + moduleId; + if (_RESERVED.includes(space)) return proto + "//" + BASE + "/" + moduleId; + return proto + "//" + space + "." + BASE + "/" + moduleId; } - if (window.location.host.includes("rspace.online") && !window.location.host.startsWith("www")) { - return window.location.protocol + "//" + space + ".rspace.online/" + moduleId; + const host = window.location.host.split(":")[0]; + if (host === BASE || host === "www." + BASE || host.endsWith("." + BASE)) { + if (space === "demo" || _RESERVED.includes(space)) return "/" + moduleId; + return proto + "//" + space + "." + BASE + "/" + moduleId; } return "/" + space + "/" + moduleId; } diff --git a/shared/url-helpers.ts b/shared/url-helpers.ts index 8247b9b..66e2366 100644 --- a/shared/url-helpers.ts +++ b/shared/url-helpers.ts @@ -68,18 +68,24 @@ export function getCurrentModule(): string { * On localhost: uses /{space}/{moduleId}. */ export function rspaceNavUrl(space: string, moduleId: string): string { - const hostParts = window.location.host.split(":")[0].split("."); + const host = window.location.host.split(":")[0]; + const hostParts = host.split("."); + const proto = window.location.protocol; + + // Always use canonical base domain to prevent stacking (e.g. rspace.rspace.online) + const BASE = "rspace.online"; + const onSubdomain = hostParts.length >= 3 && - hostParts.slice(-2).join(".") === "rspace.online" && + hostParts.slice(-2).join(".") === BASE && !RESERVED_SUBDOMAINS.includes(hostParts[0]); // Standalone r*.online domains → redirect to rspace.online for navigation if (isStandaloneDomain()) { if (space === "demo") { - return `${window.location.protocol}//demo.rspace.online/${moduleId}`; + return `${proto}//demo.${BASE}/${moduleId}`; } - return `${window.location.protocol}//${space}.rspace.online/${moduleId}`; + return `${proto}//${space}.${BASE}/${moduleId}`; } if (onSubdomain) { @@ -87,19 +93,26 @@ export function rspaceNavUrl(space: string, moduleId: string): string { if (hostParts[0] === space) { return `/${moduleId}`; } - // Different space → switch subdomain - const baseDomain = hostParts.slice(-2).join("."); - return `${window.location.protocol}//${space}.${baseDomain}/${moduleId}`; + // Guard: reserved words can't be subdomains — treat as demo + if (RESERVED_SUBDOMAINS.includes(space)) { + return `${proto}//${BASE}/${moduleId}`; + } + // Different space → switch subdomain (always use canonical base) + return `${proto}//${space}.${BASE}/${moduleId}`; } - // Bare domain (rspace.online) - if (isBareDomain()) { + // Bare domain (rspace.online) or any non-standard rspace.online host + if (isBareDomain() || host.endsWith(`.${BASE}`)) { // Default space → stay on bare domain: /{moduleId} if (space === "demo") { return `/${moduleId}`; } + // Guard: reserved words can't be subdomains + if (RESERVED_SUBDOMAINS.includes(space)) { + return `/${moduleId}`; + } // Explicit space → switch to subdomain - return `${window.location.protocol}//${space}.rspace.online/${moduleId}`; + return `${proto}//${space}.${BASE}/${moduleId}`; } // Localhost/dev diff --git a/website/public/shell.css b/website/public/shell.css index b9d6251..08ee348 100644 --- a/website/public/shell.css +++ b/website/public/shell.css @@ -62,25 +62,6 @@ body { z-index: 2; } -.rstack-header__demo-btn { - display: inline-flex; - align-items: center; - padding: 5px 14px; - border-radius: 6px; - font-size: 0.78rem; - font-weight: 600; - text-decoration: none; - white-space: nowrap; - transition: background 0.15s, opacity 0.15s; - background: var(--rs-gradient-cta); - color: #fff; - box-shadow: 0 1px 4px rgba(20, 184, 166, 0.25); -} -.rstack-header__demo-btn:hover { - opacity: 0.88; -} -/* Hide the demo button when already on demo space */ -.rstack-header__demo-btn[data-hide] { display: none; } .rstack-header__logo { width: 28px; @@ -403,10 +384,6 @@ body.rstack-sidebar-open #toolbar { gap: 6px; margin-left: auto; } - .rstack-header__demo-btn { - padding: 4px 10px; - font-size: 0.72rem; - } .rstack-header__brand { font-size: 1rem; gap: 6px;