From 3808b51a64d0be375d83faa399998c5c4eff09b7 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Sun, 1 Mar 2026 15:03:46 -0800 Subject: [PATCH] feat: strip demo pages to show just the interactive component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Demo pages now render the same clean shell as regular spaces — just the component full-page, no marketing wrapper (hero, feature cards, CTA). Descriptions belong on landing pages, not demos. - Remove demo branch from 7 module route handlers (rcal, rcart, rfunds, rnotes, rtrips, rtube, rvote) - Delete 7 demo.ts files (~1200 lines of dead markup) - Remove renderDemoShell() and DEMO_PAGE_CSS from server/shell.ts - Remove demoPage field from RSpaceModule interface - Rename top rApp dropdown item from "rSpace" to "rStack" Co-Authored-By: Claude Opus 4.6 --- modules/rcal/demo.ts | 165 ---------------- modules/rcal/mod.ts | 16 +- modules/rcart/demo.ts | 116 ----------- modules/rcart/mod.ts | 18 +- modules/rfunds/demo.ts | 116 ----------- modules/rfunds/mod.ts | 18 +- modules/rnotes/demo.ts | 116 ----------- modules/rnotes/mod.ts | 16 +- modules/rtrips/demo.ts | 116 ----------- modules/rtrips/mod.ts | 16 +- modules/rtube/demo.ts | 116 ----------- modules/rtube/mod.ts | 16 +- modules/rvote/demo.ts | 116 ----------- modules/rvote/mod.ts | 16 +- server/shell.ts | 233 ----------------------- shared/components/rstack-app-switcher.ts | 2 +- shared/module.ts | 2 - 17 files changed, 10 insertions(+), 1204 deletions(-) delete mode 100644 modules/rcal/demo.ts delete mode 100644 modules/rcart/demo.ts delete mode 100644 modules/rfunds/demo.ts delete mode 100644 modules/rnotes/demo.ts delete mode 100644 modules/rtrips/demo.ts delete mode 100644 modules/rtube/demo.ts delete mode 100644 modules/rvote/demo.ts diff --git a/modules/rcal/demo.ts b/modules/rcal/demo.ts deleted file mode 100644 index 263dbef..0000000 --- a/modules/rcal/demo.ts +++ /dev/null @@ -1,165 +0,0 @@ -/** - * rCal demo page — server-rendered HTML body. - * - * Embeds the full component for - * real interactivity (month/week/day views, navigation, lunar overlay, - * source filtering, event modals, keyboard shortcuts) plus showcase - * sections explaining the rCal vision. - */ - -const FEATURES = [ - { - icon: "\u{1F50D}", - title: "Temporal Zoom", - desc: "Navigate seamlessly from geological eras down to individual minutes. The calendar adapts its grid density and label fidelity at every level.", - }, - { - icon: "\u{1F30D}", - title: "Spatial Context", - desc: "Events are location-aware. Zoom the map and the calendar filters to show only events within the visible region.", - }, - { - icon: "\u{1F319}", - title: "Lunar Cycles", - desc: "Overlay moon phases, tidal patterns, and seasonal markers. Useful for agriculture, ceremony, and natural rhythm tracking.", - }, - { - icon: "\u{1F4C5}", - title: "Multi-Calendar", - desc: "Layer Gregorian, Islamic, Hebrew, Chinese, and custom community calendars. Cross-reference events across time systems.", - }, -]; - -const INTEGRATIONS = [ - { icon: "\u{1F5FA}", name: "rTrips", desc: "Travel itineraries surface as calendar events with departure/arrival times and locations." }, - { icon: "\u{1F30D}", name: "rMaps", desc: "Events appear on the map. Zoom the map and the calendar filters to show only visible events." }, - { icon: "\u{1F465}", name: "rNetwork", desc: "See availability across your network. Coordinate meetings without back-and-forth." }, - { icon: "\u{1F4DD}", name: "rNotes", desc: "Link notes to calendar events. Meeting agendas, daily journals, and retrospective logs." }, - { icon: "\u{1F4B0}", name: "rFunds", desc: "Budget reviews, treasury flows, and governance votes appear on the calendar timeline." }, - { icon: "\u2615\uFE0F", name: "rSpace", desc: "Each space has its own calendar layer. Nest calendars across spaces for cross-community coordination." }, -]; - -const ZOOM_LEVELS = [ - "Era", "Century", "Decade", "Year", "Quarter", - "Month", "Week", "Day", "Hour", "Minute", -]; - -export function renderDemo(): string { - return ` -
- - -
-
- Multi-Dimensional Calendar -
-

rCal Demo

-

Temporal coordination with lunar cycles, spatial context, and multi-scale zoom

-
- \u{1F50D} Temporal Zoom - | - \u{1F30D} Spatial Context - | - \u{1F319} Lunar Cycles - | - \u{1F4C5} Multi-Calendar -
-
- - -
-
- -
-
- 1 - 2 - 3 - Day / Week / Month  \u00B7  - \u2190 - \u2192 - Navigate  \u00B7  - L - Lunar  \u00B7  - T - Today -
-
- - -
-
-

- \u{1F50D} Temporal Zoom -

-

- Navigate across 10 temporal granularities. The calendar adapts its grid at each level — - from geological eras to individual minutes. -

-
- ${ZOOM_LEVELS.map( - (level) => { - const isActive = level === "Month"; - return `
${level}${isActive ? " \u25C0" : ""}
`; - }, - ).join("\n ")} -
-
-
- - -
-
- ${FEATURES.map( - (f) => ` -
-
${f.icon}
-

${f.title}

-

${f.desc}

-
`, - ).join("")} -
-
- - -
-

- r* Ecosystem Integrations -

-
- ${INTEGRATIONS.map( - (i) => ` -
-
${i.icon}
-

${i.name}

-

${i.desc}

-
`, - ).join("")} -
-
- - -
-
-

Coordinate in Time & Space

-

- rCal layers temporal zoom, spatial context, and lunar cycles into a single calendar. - Plan events that respect natural rhythms and local conditions. -

- - Create Your Space - -
-
- -
`; -} diff --git a/modules/rcal/mod.ts b/modules/rcal/mod.ts index e733302..1f207fe 100644 --- a/modules/rcal/mod.ts +++ b/modules/rcal/mod.ts @@ -9,12 +9,11 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderDemoShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; import { renderLanding } from "./landing"; -import { renderDemo } from "./demo"; const routes = new Hono(); @@ -377,18 +376,6 @@ routes.get("/api/context/:tool", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - if (space === "demo") { - return c.html(renderDemoShell({ - title: "rCal Demo — rSpace", - moduleId: "rcal", - spaceSlug: space, - modules: getModuleInfoList(), - theme: "dark", - body: renderDemo(), - scripts: ``, - styles: ``, - })); - } return c.html(renderShell({ title: `${space} — Calendar | rSpace`, moduleId: "rcal", @@ -409,7 +396,6 @@ export const calModule: RSpaceModule = { routes, standaloneDomain: "rcal.online", landingPage: renderLanding, - demoPage: renderDemo, feeds: [ { id: "events", diff --git a/modules/rcart/demo.ts b/modules/rcart/demo.ts deleted file mode 100644 index b0092a7..0000000 --- a/modules/rcart/demo.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * rCart demo page — server-rendered HTML body. - * - * Embeds the full component for - * real interactivity (catalog browsing, order tracking, filtering) - * plus showcase sections explaining the rCart vision. - */ - -const FEATURES = [ - { - icon: "\u{1F30D}", - title: "Cosmolocal Fulfillment", - desc: "Orders are matched to the nearest capable print shop or makerspace. Design global, manufacture local.", - }, - { - icon: "\u{1F6D2}", - title: "Group Shopping", - desc: "Communities pool resources and split costs. Transparent funding progress for every item in the cart.", - }, - { - icon: "\u{1F4B0}", - title: "Revenue Splits", - desc: "Every order automatically splits revenue between provider, creator, and community fund via rFunds flows.", - }, - { - icon: "\u{1F4E6}", - title: "Order Tracking", - desc: "Follow orders from pending through production to delivery. Real-time status updates across the community.", - }, -]; - -const INTEGRATIONS = [ - { icon: "\u{1F30A}", name: "rFunds", desc: "Revenue from orders flows through TBFF budget funnels with enoughness thresholds." }, - { icon: "\u{1F3A8}", name: "rDesign", desc: "Design artifacts become print-ready catalog entries with one click." }, - { icon: "\u{1F465}", name: "rNetwork", desc: "Provider registry matches orders to the closest maker in your network." }, - { icon: "\u{1F5FA}", name: "rMaps", desc: "See provider locations and delivery zones on the map." }, - { icon: "\u{1F4DD}", name: "rNotes", desc: "Link product specs, sizing guides, and design notes to catalog items." }, - { icon: "\u2615\uFE0F", name: "rSpace", desc: "Each space has its own shop. Nest catalogs across spaces for cross-community commerce." }, -]; - -export function renderDemo(): string { - return ` -
- - -
-
- Cosmolocal Print-on-Demand -
-

rCart Demo

-

Group shopping with cosmolocal fulfillment, revenue splits, and transparent funding

-
- \u{1F6D2} Catalog & Orders - | - \u{1F30D} Local Fulfillment - | - \u{1F4B0} Revenue Splits - | - \u{1F4E6} Order Tracking -
-
- - -
-
- -
-
- - -
-
- ${FEATURES.map( - (f) => ` -
-
${f.icon}
-

${f.title}

-

${f.desc}

-
`, - ).join("")} -
-
- - -
-

- r* Ecosystem Integrations -

-
- ${INTEGRATIONS.map( - (i) => ` -
-
${i.icon}
-

${i.name}

-

${i.desc}

-
`, - ).join("")} -
-
- - -
-
-

Ready to shop together?

-

- rCart gives your community a shared catalog with cosmolocal fulfillment, - transparent funding, and automatic revenue splits. -

- - Create Your First Cart - -
-
- -
`; -} diff --git a/modules/rcart/mod.ts b/modules/rcart/mod.ts index 915ca15..822850d 100644 --- a/modules/rcart/mod.ts +++ b/modules/rcart/mod.ts @@ -10,13 +10,12 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderDemoShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import { depositOrderRevenue } from "./flow"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; import { renderLanding } from "./landing"; -import { renderDemo } from "./demo"; const routes = new Hono(); @@ -443,20 +442,8 @@ routes.post("/api/fulfill/resolve", async (c) => { // ── Page route: shop ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - if (space === "demo") { - return c.html(renderDemoShell({ - title: "rCart Demo — rSpace", - moduleId: "rcart", - spaceSlug: space, - modules: getModuleInfoList(), - theme: "dark", - body: renderDemo(), - scripts: ``, - styles: ``, - })); - } return c.html(renderShell({ - title: `Shop | rSpace`, + title: `${space} — Shop | rSpace`, moduleId: "rcart", spaceSlug: space, modules: getModuleInfoList(), @@ -475,7 +462,6 @@ export const cartModule: RSpaceModule = { routes, standaloneDomain: "rcart.online", landingPage: renderLanding, - demoPage: renderDemo, feeds: [ { id: "orders", diff --git a/modules/rfunds/demo.ts b/modules/rfunds/demo.ts deleted file mode 100644 index 6b52950..0000000 --- a/modules/rfunds/demo.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * rFunds demo page — server-rendered HTML body. - * - * Embeds the full component - * for real interactivity (flow listing, river visualization, TBFF diagrams) - * plus showcase sections explaining the rFunds vision. - */ - -const FEATURES = [ - { - icon: "\u{1F30A}", - title: "River Visualization", - desc: "Watch resources flow through animated Sankey rivers. Sources feed into funnels, funnels feed outcomes, and surplus overflows to where it's needed most.", - }, - { - icon: "\u{1F4CA}", - title: "TBFF Flows", - desc: "Threshold-Based Funding Flows distribute resources based on enoughness. When a funnel is sufficient, surplus flows to the next highest-need area.", - }, - { - icon: "\u{1F4B8}", - title: "Treasury Management", - desc: "Track deposits, withdrawals, and allocations across all community funnels. Transparent financial governance in real time.", - }, - { - icon: "\u2696\uFE0F", - title: "Enoughness Layer", - desc: "Each funnel has a sufficiency threshold. Golden glow indicates a funded funnel. Resources keep flowing until everyone has enough.", - }, -]; - -const INTEGRATIONS = [ - { icon: "\u{1F6D2}", name: "rCart", desc: "Order revenue flows through TBFF funnels with automatic creator/provider/community splits." }, - { icon: "\u{1F5F3}", name: "rVote", desc: "Governance votes determine funding priorities and threshold adjustments." }, - { icon: "\u{2708}\uFE0F", name: "rTrips", desc: "Group expenses feed into shared budget flows with per-person tracking." }, - { icon: "\u{1F4DD}", name: "rNotes", desc: "Attach budget rationales, meeting minutes, and audit logs to flows." }, - { icon: "\u{1F4C5}", name: "rCal", desc: "Budget reviews, treasury snapshots, and governance votes on the calendar timeline." }, - { icon: "\u2615\uFE0F", name: "rSpace", desc: "Each space has its own treasury. Nest flows across spaces for multi-community coordination." }, -]; - -export function renderDemo(): string { - return ` -
- - -
-
- Threshold-Based Funding Flows -
-

rFunds Demo

-

Budget flows, river visualization, and treasury management with enoughness thresholds

-
- \u{1F30A} River Visualization - | - \u{1F4CA} TBFF Flows - | - \u{1F4B8} Treasury - | - \u2696\uFE0F Enoughness -
-
- - -
-
- -
-
- - -
-
- ${FEATURES.map( - (f) => ` -
-
${f.icon}
-

${f.title}

-

${f.desc}

-
`, - ).join("")} -
-
- - -
-

- r* Ecosystem Integrations -

-
- ${INTEGRATIONS.map( - (i) => ` -
-
${i.icon}
-

${i.name}

-

${i.desc}

-
`, - ).join("")} -
-
- - -
-
-

Design Your Funding Flows

-

- rFunds lets your community design transparent budget flows with threshold-based - mechanisms, enoughness scoring, and animated river visualizations. -

- - Create Your Space - -
-
- -
`; -} diff --git a/modules/rfunds/mod.ts b/modules/rfunds/mod.ts index 4676c27..dd17a78 100644 --- a/modules/rfunds/mod.ts +++ b/modules/rfunds/mod.ts @@ -8,12 +8,11 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderDemoShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import type { RSpaceModule } from "../../shared/module"; import { getModuleInfoList } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; import { renderLanding } from "./landing"; -import { renderDemo } from "./demo"; const FLOW_SERVICE_URL = process.env.FLOW_SERVICE_URL || "http://payment-flow:3010"; @@ -198,20 +197,8 @@ const fundsStyles = ``; // Landing page routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "demo"; - if (spaceSlug === "demo") { - return c.html(renderDemoShell({ - title: "rFunds Demo — rSpace", - moduleId: "rfunds", - spaceSlug, - modules: getModuleInfoList(), - theme: "dark", - body: renderDemo(), - scripts: ``, - styles: ``, - })); - } return c.html(renderShell({ - title: `rFunds — TBFF Flow Funding | rSpace`, + title: `${spaceSlug} — Funds | rSpace`, moduleId: "rfunds", spaceSlug, modules: getModuleInfoList(), @@ -260,7 +247,6 @@ export const fundsModule: RSpaceModule = { description: "Budget flows, river visualization, and treasury management", routes, landingPage: renderLanding, - demoPage: renderDemo, standaloneDomain: "rfunds.online", feeds: [ { diff --git a/modules/rnotes/demo.ts b/modules/rnotes/demo.ts deleted file mode 100644 index 1cd7ebf..0000000 --- a/modules/rnotes/demo.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * rNotes demo page — server-rendered HTML body. - * - * Embeds the full component for - * real interactivity (notebook browsing, note editing, search, tags) - * plus showcase sections explaining the rNotes vision. - */ - -const FEATURES = [ - { - icon: "\u{1F3A4}", - title: "Live Transcription", - desc: "Record and transcribe in real time. Stream audio via WebSocket or transcribe offline with Parakeet.js.", - }, - { - icon: "\u270F\uFE0F", - title: "Rich Editing", - desc: "Headings, lists, code blocks, highlights, images, and file attachments in every note.", - }, - { - icon: "\u{1F4D3}", - title: "Notebooks", - desc: "Organize notes into notebooks with sections. Nest as deep as you need for any project structure.", - }, - { - icon: "\u{1F3F7}\uFE0F", - title: "Flexible Tags", - desc: "Cross-cutting tags let you find notes across all notebooks instantly. Filter and search by any combination.", - }, -]; - -const INTEGRATIONS = [ - { icon: "\u{1F4C5}", name: "rCal", desc: "Link notes to calendar events. Meeting agendas, daily journals, and retrospective logs." }, - { icon: "\u{1F5FA}", name: "rMaps", desc: "Pin location-aware notes to places on the map. Field notes, venue reviews, site reports." }, - { icon: "\u{1F465}", name: "rNetwork", desc: "Collaborate on notes across your network with real-time Automerge sync." }, - { icon: "\u{1F3AC}", name: "rTube", desc: "Attach meeting notes, transcripts, and timestamps to video recordings." }, - { icon: "\u{1F5F3}", name: "rVote", desc: "Link governance proposals to supporting research notes and discussion threads." }, - { icon: "\u2615\uFE0F", name: "rSpace", desc: "Pin any note to the collaborative canvas. Each space has its own knowledge base." }, -]; - -export function renderDemo(): string { - return ` -
- - -
-
- Collaborative Knowledge Base -
-

rNotes Demo

-

Notebooks with rich-text notes, voice transcription, and real-time collaboration

-
- \u{1F3A4} Transcription - | - \u270F\uFE0F Rich Editing - | - \u{1F4D3} Notebooks - | - \u{1F3F7}\uFE0F Tags & Search -
-
- - -
-
- -
-
- - -
-
- ${FEATURES.map( - (f) => ` -
-
${f.icon}
-

${f.title}

-

${f.desc}

-
`, - ).join("")} -
-
- - -
-

- r* Ecosystem Integrations -

-
- ${INTEGRATIONS.map( - (i) => ` -
-
${i.icon}
-

${i.name}

-

${i.desc}

-
`, - ).join("")} -
-
- - -
-
-

Ready to capture everything?

-

- rNotes gives your team a shared knowledge base with rich editing, flexible organization, - and deep integration with the r* ecosystem — all on a collaborative canvas. -

- - Start Taking Notes - -
-
- -
`; -} diff --git a/modules/rnotes/mod.ts b/modules/rnotes/mod.ts index 774bbb1..908c9c2 100644 --- a/modules/rnotes/mod.ts +++ b/modules/rnotes/mod.ts @@ -9,12 +9,11 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderDemoShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; import { renderLanding } from "./landing"; -import { renderDemo } from "./demo"; const routes = new Hono(); @@ -363,18 +362,6 @@ routes.delete("/api/notes/:id", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - if (space === "demo") { - return c.html(renderDemoShell({ - title: "rNotes Demo — rSpace", - moduleId: "rnotes", - spaceSlug: space, - modules: getModuleInfoList(), - theme: "dark", - body: renderDemo(), - scripts: ``, - styles: ``, - })); - } return c.html(renderShell({ title: `${space} — Notes | rSpace`, moduleId: "rnotes", @@ -394,7 +381,6 @@ export const notesModule: RSpaceModule = { description: "Notebooks with rich-text notes, voice transcription, and collaboration", routes, landingPage: renderLanding, - demoPage: renderDemo, standaloneDomain: "rnotes.online", feeds: [ { diff --git a/modules/rtrips/demo.ts b/modules/rtrips/demo.ts deleted file mode 100644 index 1b8a941..0000000 --- a/modules/rtrips/demo.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * rTrips demo page — server-rendered HTML body. - * - * Embeds the full component for - * real interactivity (trip list, destinations, itinerary, bookings, - * expenses, packing lists) plus showcase sections explaining the rTrips vision. - */ - -const FEATURES = [ - { - icon: "\u{1F5FA}", - title: "Destinations", - desc: "Pin destinations on the map with arrival/departure dates, country info, and notes. Reorder your route with drag and drop.", - }, - { - icon: "\u{1F4C5}", - title: "Itinerary", - desc: "Plan day-by-day activities grouped by date. Categories include hiking, dining, sightseeing, transit, and more.", - }, - { - icon: "\u{1F4B0}", - title: "Expense Splitting", - desc: "Track group expenses with automatic per-person splits. See who paid what and who owes whom.", - }, - { - icon: "\u{1F392}", - title: "Packing Lists", - desc: "Collaborative packing checklists organized by category. Check items off as you pack — synced in real time.", - }, -]; - -const INTEGRATIONS = [ - { icon: "\u{1F5FA}", name: "rMaps", desc: "Destinations and routes appear on the interactive map with pins and driving directions." }, - { icon: "\u{1F4C5}", name: "rCal", desc: "Trip dates, activities, and bookings sync to the community calendar." }, - { icon: "\u{1F30A}", name: "rFunds", desc: "Group expenses feed into shared budget flows with threshold-based splits." }, - { icon: "\u{1F5F3}", name: "rVote", desc: "Vote on daily activities, restaurants, and route decisions as a group." }, - { icon: "\u{1F4DD}", name: "rNotes", desc: "Attach travel journals, packing tips, and logistics notes to the trip." }, - { icon: "\u2615\uFE0F", name: "rSpace", desc: "Each trip lives on its own canvas with maps, notes, polls, and expenses connected." }, -]; - -export function renderDemo(): string { - return ` -
- - -
-
- Collaborative Trip Planner -
-

rTrips Demo

-

Plan trips together with destinations, itinerary, bookings, expenses, and packing lists

-
- \u{1F5FA} Destinations - | - \u{1F4C5} Itinerary - | - \u{1F4B0} Expenses - | - \u{1F392} Packing -
-
- - -
-
- -
-
- - -
-
- ${FEATURES.map( - (f) => ` -
-
${f.icon}
-

${f.title}

-

${f.desc}

-
`, - ).join("")} -
-
- - -
-

- r* Ecosystem Integrations -

-
- ${INTEGRATIONS.map( - (i) => ` -
-
${i.icon}
-

${i.name}

-

${i.desc}

-
`, - ).join("")} -
-
- - -
-
-

Plan Your Next Adventure

-

- rTrips gives your group everything you need — routes, schedules, polls, - shared expenses, and packing lists — all connected in one trip canvas. -

- - Start Planning - -
-
- -
`; -} diff --git a/modules/rtrips/mod.ts b/modules/rtrips/mod.ts index cd1b9bc..60aa538 100644 --- a/modules/rtrips/mod.ts +++ b/modules/rtrips/mod.ts @@ -9,12 +9,11 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderDemoShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; import { renderLanding } from "./landing"; -import { renderDemo } from "./demo"; const OSRM_URL = process.env.OSRM_URL || "http://osrm-backend:5000"; @@ -255,18 +254,6 @@ routes.get("/routes", (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - if (space === "demo") { - return c.html(renderDemoShell({ - title: "rTrips Demo — rSpace", - moduleId: "rtrips", - spaceSlug: space, - modules: getModuleInfoList(), - theme: "dark", - body: renderDemo(), - scripts: ``, - styles: ``, - })); - } return c.html(renderShell({ title: `${space} — Trips | rSpace`, moduleId: "rtrips", @@ -286,7 +273,6 @@ export const tripsModule: RSpaceModule = { description: "Collaborative trip planner with itinerary, bookings, and expense splitting", routes, landingPage: renderLanding, - demoPage: renderDemo, standaloneDomain: "rtrips.online", feeds: [ { diff --git a/modules/rtube/demo.ts b/modules/rtube/demo.ts deleted file mode 100644 index 18160d5..0000000 --- a/modules/rtube/demo.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * rTube demo page — server-rendered HTML body. - * - * Embeds the full component for - * real interactivity (video library, search, playback, live streaming) - * plus showcase sections explaining the rTube vision. - */ - -const FEATURES = [ - { - icon: "\u{1F3AC}", - title: "Video Library", - desc: "Browse, search, and play videos from your community's R2-backed storage. Supports MP4, WebM, MOV, and more.", - }, - { - icon: "\u{1F4E1}", - title: "Live Streaming", - desc: "Broadcast live via RTMP from OBS Studio or any streaming software. Viewers watch in real-time with HLS playback.", - }, - { - icon: "\u{1F4E4}", - title: "Easy Uploads", - desc: "Authenticated members upload videos directly. Files stream to Cloudflare R2 with automatic format detection.", - }, - { - icon: "\u{1F517}", - title: "Direct Links", - desc: "Copy shareable links to any video. HTTP range requests enable efficient streaming and seeking.", - }, -]; - -const INTEGRATIONS = [ - { icon: "\u{1F4DD}", name: "rNotes", desc: "Attach meeting notes, transcripts, and timestamps to video recordings." }, - { icon: "\u{1F4C5}", name: "rCal", desc: "Scheduled recordings and live streams appear on the community calendar." }, - { icon: "\u{1F465}", name: "rNetwork", desc: "Share videos across your network. Collaborative viewing and commenting." }, - { icon: "\u{1F4DA}", name: "rBooks", desc: "Embed video content in publications and educational materials." }, - { icon: "\u{1F5FA}", name: "rMaps", desc: "Geotagged videos appear on the map at their recording location." }, - { icon: "\u2615\uFE0F", name: "rSpace", desc: "Each space has its own video library. Pin videos to the collaborative canvas." }, -]; - -export function renderDemo(): string { - return ` -
- - -
-
- Community Video Hosting -
-

rTube Demo

-

Video library, live streaming, and uploads powered by Cloudflare R2

-
- \u{1F3AC} Video Library - | - \u{1F4E1} Live Streaming - | - \u{1F4E4} Uploads - | - \u{1F517} Direct Links -
-
- - -
-
- -
-
- - -
-
- ${FEATURES.map( - (f) => ` -
-
${f.icon}
-

${f.title}

-

${f.desc}

-
`, - ).join("")} -
-
- - -
-

- r* Ecosystem Integrations -

-
- ${INTEGRATIONS.map( - (i) => ` -
-
${i.icon}
-

${i.name}

-

${i.desc}

-
`, - ).join("")} -
-
- - -
-
-

Host Your Video Library

-

- rTube gives your community private video hosting with streaming, - uploads, and live broadcasting — all powered by Cloudflare R2. -

- - Create Your Space - -
-
- -
`; -} diff --git a/modules/rtube/mod.ts b/modules/rtube/mod.ts index f3fce31..557de0f 100644 --- a/modules/rtube/mod.ts +++ b/modules/rtube/mod.ts @@ -6,12 +6,11 @@ */ import { Hono } from "hono"; -import { renderShell, renderDemoShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; import { renderLanding } from "./landing"; -import { renderDemo } from "./demo"; import { S3Client, ListObjectsV2Command, GetObjectCommand, HeadObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3"; const routes = new Hono(); @@ -193,18 +192,6 @@ routes.get("/api/health", (c) => c.json({ ok: true })); // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - if (space === "demo") { - return c.html(renderDemoShell({ - title: "rTube Demo — rSpace", - moduleId: "rtube", - spaceSlug: space, - modules: getModuleInfoList(), - theme: "dark", - body: renderDemo(), - scripts: ``, - styles: ``, - })); - } return c.html(renderShell({ title: `${space} — Tube | rSpace`, moduleId: "rtube", @@ -224,7 +211,6 @@ export const tubeModule: RSpaceModule = { description: "Community video hosting & live streaming", routes, landingPage: renderLanding, - demoPage: renderDemo, standaloneDomain: "rtube.online", feeds: [ { diff --git a/modules/rvote/demo.ts b/modules/rvote/demo.ts deleted file mode 100644 index 27fa35a..0000000 --- a/modules/rvote/demo.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * rVote demo page — server-rendered HTML body. - * - * Embeds the full component for - * real interactivity (browse spaces, create proposals, cast conviction - * votes, binary final votes) plus showcase sections explaining the rVote vision. - */ - -const FEATURES = [ - { - icon: "\u{1F4CA}", - title: "Conviction Voting", - desc: "Stake credits on proposals you support. Weight costs credits quadratically (weight\u00B2), preventing plutocratic capture while rewarding conviction.", - }, - { - icon: "\u{1F3AF}", - title: "Promotion Threshold", - desc: "Proposals accumulate conviction score. When they hit the threshold, they auto-promote to a binary YES/NO/ABSTAIN final vote.", - }, - { - icon: "\u23F3", - title: "Vote Decay", - desc: "Conviction decays after 30 days. This ensures ongoing relevance — stale votes fade, keeping governance dynamic and current.", - }, - { - icon: "\u{1F3DB}\uFE0F", - title: "Governance Spaces", - desc: "Each community gets its own voting space with configurable thresholds, credit budgets, and voting periods.", - }, -]; - -const INTEGRATIONS = [ - { icon: "\u{1F30A}", name: "rFunds", desc: "Passed proposals trigger funding flows. Vote on budget allocations and threshold adjustments." }, - { icon: "\u{1F4DD}", name: "rNotes", desc: "Link supporting research, discussion threads, and rationale documents to proposals." }, - { icon: "\u{1F4C5}", name: "rCal", desc: "Voting deadlines, governance meetings, and proposal review periods on the calendar." }, - { icon: "\u{1F465}", name: "rNetwork", desc: "Delegate voting power to trusted network members. Liquid democracy across communities." }, - { icon: "\u{1F6D2}", name: "rCart", desc: "Vote on merchandise decisions, provider selection, and catalog curation." }, - { icon: "\u2615\uFE0F", name: "rSpace", desc: "Each space has its own governance layer. Nest voting across spaces for multi-community decisions." }, -]; - -export function renderDemo(): string { - return ` -
- - -
-
- Conviction Voting Engine -
-

rVote Demo

-

Credit-weighted conviction voting for collaborative governance decisions

-
- \u{1F4CA} Conviction Voting - | - \u{1F3AF} Thresholds - | - \u23F3 Vote Decay - | - \u{1F3DB}\uFE0F Governance -
-
- - -
-
- -
-
- - -
-
- ${FEATURES.map( - (f) => ` -
-
${f.icon}
-

${f.title}

-

${f.desc}

-
`, - ).join("")} -
-
- - -
-

- r* Ecosystem Integrations -

-
- ${INTEGRATIONS.map( - (i) => ` -
-
${i.icon}
-

${i.name}

-

${i.desc}

-
`, - ).join("")} -
-
- - -
-
-

Govern Together

-

- rVote brings conviction-weighted governance to your community. - Proposals rise through collective conviction, not majority rule. -

- - Create Your Space - -
-
- -
`; -} diff --git a/modules/rvote/mod.ts b/modules/rvote/mod.ts index c0dfd53..44d1caf 100644 --- a/modules/rvote/mod.ts +++ b/modules/rvote/mod.ts @@ -9,9 +9,8 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderDemoShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; -import { renderDemo } from "./demo"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; import { renderLanding } from "./landing"; @@ -329,18 +328,6 @@ routes.post("/api/proposals/:id/final-vote", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - if (space === "demo") { - return c.html(renderDemoShell({ - title: "rVote Demo — rSpace", - moduleId: "rvote", - spaceSlug: space, - modules: getModuleInfoList(), - theme: "dark", - body: renderDemo(), - scripts: ``, - styles: ``, - })); - } return c.html(renderShell({ title: `${space} — Vote | rSpace`, moduleId: "rvote", @@ -361,7 +348,6 @@ export const voteModule: RSpaceModule = { routes, standaloneDomain: "rvote.online", landingPage: renderLanding, - demoPage: renderDemo, feeds: [ { id: "proposals", diff --git a/server/shell.ts b/server/shell.ts index 235bc0d..cc8da49 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -645,18 +645,6 @@ const WELCOME_CSS = ` `; -/** - * Render a demo page shell: standard shell + DEMO_PAGE_CSS + demo scripts. - * Used by modules that have a demoPage() renderer. - */ -export function renderDemoShell(opts: ShellOptions & { demoScripts?: string }): string { - return renderShell({ - ...opts, - styles: `${opts.styles || ""}\n`, - scripts: `${opts.scripts || ""}\n${opts.demoScripts || ""}`, - }); -} - // ── Module landing page (bare-domain rspace.online/{moduleId}) ── export interface ModuleLandingOptions { @@ -982,227 +970,6 @@ export const RICH_LANDING_CSS = ` // ── Demo page CSS utilities (rd-* prefix, parallel to rl-* landing pages) ── -export const DEMO_PAGE_CSS = ` -/* ── Demo page base ── */ -.rd-root { - min-height: 100vh; - background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%); - color: white; padding-bottom: 2rem; -} -.rd-hero { - max-width: 48rem; margin: 0 auto; text-align: center; - padding: 3rem 1.5rem 2rem; -} -.rd-hero h1 { - font-size: 2.25rem; font-weight: 700; margin-bottom: 1rem; - background: linear-gradient(135deg, var(--rd-accent-from, #14b8a6), var(--rd-accent-to, #22d3ee)); - -webkit-background-clip: text; -webkit-text-fill-color: transparent; - background-clip: text; -} -@media (min-width: 640px) { .rd-hero h1 { font-size: 3rem; } } -.rd-hero .rd-subtitle { font-size: 1.1rem; color: #cbd5e1; margin-bottom: 0.5rem; } -.rd-hero .rd-meta { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 1rem; font-size: 0.875rem; color: #94a3b8; margin-bottom: 1.5rem; } -.rd-hero .rd-meta span:not(:last-child)::after { content: ""; } - -/* Avatars */ -.rd-avatars { display: flex; align-items: center; justify-content: center; gap: 0.5rem; } -.rd-avatar { - width: 2.5rem; height: 2.5rem; border-radius: 9999px; - display: flex; align-items: center; justify-content: center; - font-size: 0.875rem; font-weight: 700; color: white; - box-shadow: 0 0 0 2px #1e293b; -} -.rd-avatars .rd-count { font-size: 0.875rem; color: #94a3b8; margin-left: 0.5rem; } - -/* Cards */ -.rd-card { - background: rgba(30,41,59,0.5); border-radius: 1rem; - border: 1px solid rgba(51,65,85,0.5); overflow: hidden; -} -.rd-card-header { - display: flex; align-items: center; justify-content: space-between; - padding: 0.75rem 1.25rem; border-bottom: 1px solid rgba(51,65,85,0.5); -} -.rd-card-header .rd-card-title { display: flex; align-items: center; gap: 0.5rem; font-weight: 600; font-size: 0.875rem; } -.rd-card-header .rd-card-title .rd-icon { font-size: 1.25rem; } -.rd-card-header .rd-open-link { - font-size: 0.75rem; padding: 0.375rem 0.75rem; - background: rgba(51,65,85,0.6); border-radius: 0.5rem; - color: #cbd5e1; text-decoration: none; transition: all 0.15s; -} -.rd-card-header .rd-open-link:hover { background: rgba(71,85,105,0.6); color: white; } -.rd-card-body { padding: 1.25rem; } - -/* Live badge */ -.rd-live { - display: inline-flex; align-items: center; gap: 0.375rem; - font-size: 0.75rem; color: #34d399; -} -.rd-live::before { - content: ""; width: 0.375rem; height: 0.375rem; border-radius: 9999px; - background: #34d399; animation: rd-pulse 2s ease-in-out infinite; -} -@keyframes rd-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } - -/* Status badge */ -.rd-status { - display: inline-flex; align-items: center; gap: 0.375rem; - font-size: 0.75rem; padding: 0.25rem 0.625rem; border-radius: 9999px; -} -.rd-status--connected { color: #34d399; background: rgba(52,211,153,0.1); border: 1px solid rgba(52,211,153,0.2); } -.rd-status--disconnected { color: #f87171; background: rgba(248,113,113,0.1); border: 1px solid rgba(248,113,113,0.2); } -.rd-status::before { - content: ""; width: 0.5rem; height: 0.5rem; border-radius: 9999px; -} -.rd-status--connected::before { background: #34d399; } -.rd-status--disconnected::before { background: #f87171; } - -/* Progress bar */ -.rd-progress { height: 0.75rem; border-radius: 9999px; background: rgba(51,65,85,1); overflow: hidden; } -.rd-progress--sm { height: 0.375rem; } -.rd-progress--xs { height: 0.25rem; } -.rd-progress__fill { - height: 100%; border-radius: 9999px; transition: width 0.3s ease-out; - background: linear-gradient(90deg, var(--rd-accent-from, #14b8a6), var(--rd-accent-to, #2dd4bf)); -} -.rd-progress__fill--emerald { background: #10b981; } -.rd-progress__fill--sky { background: #0ea5e9; } -.rd-progress__fill--amber { background: #f59e0b; } -.rd-progress__fill--rose { background: #f43f5e; } -.rd-progress__fill--orange { background: linear-gradient(90deg, #fb923c, #f97316); } -.rd-progress__fill--teal { background: linear-gradient(90deg, #14b8a6, #2dd4bf); } -.rd-progress__fill--cyan { background: #06b6d4; } -.rd-progress__fill--violet { background: #8b5cf6; } - -/* Grid */ -.rd-grid { display: grid; gap: 1rem; } -.rd-grid--2 { grid-template-columns: 1fr; } -.rd-grid--3 { grid-template-columns: 1fr; } -.rd-grid--4 { grid-template-columns: 1fr 1fr; } -@media (min-width: 640px) { - .rd-grid--2 { grid-template-columns: repeat(2, 1fr); } - .rd-grid--3 { grid-template-columns: repeat(3, 1fr); } -} -@media (min-width: 768px) { - .rd-grid--3 { grid-template-columns: repeat(3, 1fr); } - .rd-grid--4 { grid-template-columns: repeat(4, 1fr); } -} - -/* Section */ -.rd-section { max-width: 72rem; margin: 0 auto; padding: 0 1.5rem 1.5rem; } -.rd-section--narrow { max-width: 64rem; } - -/* Badge */ -.rd-badge { - display: inline-block; font-size: 0.75rem; font-weight: 500; - padding: 0.125rem 0.625rem; border-radius: 9999px; -} -.rd-badge--emerald { background: rgba(16,185,129,0.2); color: #6ee7b7; } -.rd-badge--sky { background: rgba(14,165,233,0.2); color: #7dd3fc; } -.rd-badge--amber { background: rgba(245,158,11,0.2); color: #fcd34d; } -.rd-badge--rose { background: rgba(244,63,94,0.2); color: #fda4af; } -.rd-badge--orange { background: rgba(249,115,22,0.2); color: #fdba74; } -.rd-badge--teal { background: rgba(20,184,166,0.2); color: #5eead4; } -.rd-badge--slate { background: rgba(100,116,139,0.2); color: #94a3b8; } - -/* Stat box */ -.rd-stat { background: rgba(51,65,85,0.3); border-radius: 0.75rem; padding: 1rem; text-align: center; } -.rd-stat__value { font-size: 1.5rem; font-weight: 700; color: white; } -.rd-stat__label { font-size: 0.75rem; color: #94a3b8; margin-top: 0.25rem; } -.rd-stat__sub { font-size: 0.75rem; color: #64748b; margin-top: 0.125rem; } - -/* Button */ -.rd-btn { - display: inline-flex; align-items: center; gap: 0.5rem; - padding: 0.5rem 1rem; border-radius: 0.5rem; font-size: 0.875rem; - font-weight: 500; cursor: pointer; border: none; transition: all 0.15s; -} -.rd-btn--ghost { background: rgba(51,65,85,0.6); color: #cbd5e1; } -.rd-btn--ghost:hover { background: rgba(71,85,105,0.6); color: white; } -.rd-btn--ghost:disabled { opacity: 0.5; cursor: not-allowed; } -.rd-btn--primary { color: white; } - -/* Divider row */ -.rd-row { - display: flex; align-items: center; justify-content: space-between; - padding: 0.75rem 1.25rem; border-top: 1px solid rgba(51,65,85,0.5); -} - -/* Item row (expense/cart list item) */ -.rd-item { - display: flex; align-items: center; gap: 1rem; - padding: 0.75rem 1.25rem; transition: background 0.1s; -} -.rd-item:hover { background: rgba(51,65,85,0.2); } -.rd-item + .rd-item { border-top: 1px solid rgba(51,65,85,0.3); } - -/* Checkbox */ -.rd-checkbox { - width: 1.25rem; height: 1.25rem; border-radius: 0.25rem; flex-shrink: 0; - border: 2px solid #475569; display: flex; align-items: center; justify-content: center; - cursor: pointer; transition: all 0.15s; -} -.rd-checkbox--checked { background: var(--rd-accent-from, #14b8a6); border-color: var(--rd-accent-from, #14b8a6); } -.rd-checkbox svg { width: 0.75rem; height: 0.75rem; color: white; } -.rd-checkbox:hover { border-color: #64748b; } - -/* CTA section */ -.rd-cta { - background: rgba(30,41,59,0.5); border-radius: 1rem; - border: 1px solid rgba(51,65,85,0.5); padding: 2.5rem; - text-align: center; margin-top: 1rem; -} -.rd-cta h2 { font-size: 1.875rem; font-weight: 700; margin-bottom: 0.75rem; } -.rd-cta p { color: #94a3b8; max-width: 32rem; margin: 0 auto 1.5rem; font-size: 0.875rem; } -.rd-cta a { - display: inline-block; padding: 0.875rem 2rem; border-radius: 0.75rem; - font-size: 1.1rem; font-weight: 500; color: white; text-decoration: none; - transition: transform 0.2s, box-shadow 0.2s; -} -.rd-cta a:hover { transform: translateY(-2px); } - -/* Text helpers */ -.rd-text-muted { color: #94a3b8; } -.rd-text-dim { color: #64748b; } -.rd-text-sm { font-size: 0.875rem; } -.rd-text-xs { font-size: 0.75rem; } -.rd-text-center { text-align: center; } -.rd-font-bold { font-weight: 700; } -.rd-font-semibold { font-weight: 600; } -.rd-font-medium { font-weight: 500; } -.rd-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.rd-line-through { text-decoration: line-through; } -.rd-hidden { display: none !important; } - -/* Color helpers */ -.rd-emerald { color: #34d399; } -.rd-rose { color: #fb7185; } -.rd-amber { color: #fbbf24; } -.rd-cyan { color: #22d3ee; } -.rd-teal { color: #2dd4bf; } -.rd-orange { color: #fb923c; } -.rd-sky { color: #38bdf8; } -.rd-violet { color: #a78bfa; } - -/* BG helpers */ -.rd-bg-emerald { background: #10b981; } -.rd-bg-cyan { background: #06b6d4; } -.rd-bg-violet { background: #8b5cf6; } -.rd-bg-amber { background: #f59e0b; } -.rd-bg-rose { background: #f43f5e; } -.rd-bg-teal { background: #14b8a6; } -.rd-bg-sky { background: #0ea5e9; } -.rd-bg-orange { background: #f97316; } -.rd-bg-slate { background: #64748b; } - -/* Responsive */ -@media (max-width: 640px) { - .rd-hero { padding: 2rem 1rem 1.5rem; } - .rd-hero h1 { font-size: 2rem; } - .rd-section { padding: 0 1rem 1rem; } -} -`; - export function escapeHtml(s: string): string { return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } diff --git a/shared/components/rstack-app-switcher.ts b/shared/components/rstack-app-switcher.ts index 88bd231..ba98177 100644 --- a/shared/components/rstack-app-switcher.ts +++ b/shared/components/rstack-app-switcher.ts @@ -153,7 +153,7 @@ export class RStackAppSwitcher extends HTMLElement { r*
- rSpace + rStack Self-hosted community app suite
diff --git a/shared/module.ts b/shared/module.ts index bd3b882..b74e976 100644 --- a/shared/module.ts +++ b/shared/module.ts @@ -54,8 +54,6 @@ export interface RSpaceModule { url: string; name: string; }; - /** Optional: render rich demo page body HTML (served when space === "demo") */ - demoPage?: () => string; } /** Registry of all loaded modules */