From f84e45fc5a6b50ac58e7a294cf6d5e76ad220d43 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Tue, 24 Feb 2026 19:36:58 -0800 Subject: [PATCH] feat: rebrand AppSwitcher with pastel badges, rStack header, updated categories - Pastel rainbow badges (rS, rN, rP, rC, rT...) replace plain emoji icons - Emoji moved to right of app name in dropdown items - rStack header with gradient badge at top of dropdown - rStack footer link at bottom - Canvas renamed to rSpace - rMaps moved to Planning category - "Sharing & Media" renamed to "Social & Sharing" with rNetwork at top - Trigger button shows pastel badge + app name Co-Authored-By: Claude Opus 4.6 --- modules/canvas/mod.ts | 4 +- shared/components/rstack-app-switcher.ts | 147 ++++++++++++++++++++--- 2 files changed, 130 insertions(+), 21 deletions(-) diff --git a/modules/canvas/mod.ts b/modules/canvas/mod.ts index b79019f..982137a 100644 --- a/modules/canvas/mod.ts +++ b/modules/canvas/mod.ts @@ -51,8 +51,8 @@ routes.get("/", async (c) => { export const canvasModule: RSpaceModule = { id: "canvas", - name: "Canvas", + name: "rSpace", icon: "🎨", - description: "Collaborative infinite canvas", + description: "Real-time collaborative canvas", routes, }; diff --git a/shared/components/rstack-app-switcher.ts b/shared/components/rstack-app-switcher.ts index 6724e82..0c13d52 100644 --- a/shared/components/rstack-app-switcher.ts +++ b/shared/components/rstack-app-switcher.ts @@ -16,6 +16,33 @@ export interface AppSwitcherModule { standaloneDomain?: string; } +// Pastel badge abbreviations & colors for each module +const MODULE_BADGES: Record = { + canvas: { badge: "rS", color: "#5eead4" }, // teal-300 + notes: { badge: "rN", color: "#fcd34d" }, // amber-300 + pubs: { badge: "rP", color: "#fda4af" }, // rose-300 + swag: { badge: "rSw", color: "#fda4af" }, // rose-300 + splat: { badge: "r3", color: "#d8b4fe" }, // purple-300 + cal: { badge: "rC", color: "#7dd3fc" }, // sky-300 + trips: { badge: "rT", color: "#6ee7b7" }, // emerald-300 + maps: { badge: "rM", color: "#86efac" }, // green-300 + work: { badge: "rWk", color: "#fdba74" }, // orange-300 + forum: { badge: "rFo", color: "#a5b4fc" }, // indigo-300 + inbox: { badge: "rI", color: "#a5b4fc" }, // indigo-300 + choices: { badge: "rCh", color: "#f0abfc" }, // fuchsia-300 + vote: { badge: "rV", color: "#c4b5fd" }, // violet-300 + funds: { badge: "rF", color: "#bef264" }, // lime-300 + wallet: { badge: "rW", color: "#fde047" }, // yellow-300 + cart: { badge: "rCt", color: "#fdba74" }, // orange-300 + providers: { badge: "rPr", color: "#fdba74" }, // orange-300 + books: { badge: "rB", color: "#fda4af" }, // rose-300 + files: { badge: "rFi", color: "#67e8f9" }, // cyan-300 + tube: { badge: "rTu", color: "#f9a8d4" }, // pink-300 + data: { badge: "rD", color: "#d8b4fe" }, // purple-300 + network: { badge: "rNe", color: "#93c5fd" }, // blue-300 + auctions: { badge: "rA", color: "#fca5a5" }, // red-300 +}; + // Category definitions for the rApp dropdown (display-only grouping) const MODULE_CATEGORIES: Record = { canvas: "Creating", @@ -25,6 +52,7 @@ const MODULE_CATEGORIES: Record = { splat: "Creating", cal: "Planning", trips: "Planning", + maps: "Planning", work: "Planning", forum: "Discussing & Deciding", inbox: "Discussing & Deciding", @@ -34,12 +62,12 @@ const MODULE_CATEGORIES: Record = { wallet: "Funding & Commerce", cart: "Funding & Commerce", providers: "Funding & Commerce", - books: "Sharing & Media", - files: "Sharing & Media", - tube: "Sharing & Media", - data: "Sharing & Media", - maps: "Sharing & Media", - network: "Sharing & Media", + auctions: "Funding & Commerce", + network: "Social & Sharing", + books: "Social & Sharing", + files: "Social & Sharing", + tube: "Social & Sharing", + data: "Social & Sharing", }; const CATEGORY_ORDER = [ @@ -47,7 +75,7 @@ const CATEGORY_ORDER = [ "Planning", "Discussing & Deciding", "Funding & Commerce", - "Sharing & Media", + "Social & Sharing", ]; export class RStackAppSwitcher extends HTMLElement { @@ -95,7 +123,17 @@ export class RStackAppSwitcher extends HTMLElement { } } - let html = ""; + // rStack header + let html = ` +
+ r* +
+ rStack + Self-hosted community app suite +
+
+ `; + for (const cat of CATEGORY_ORDER) { const items = groups.get(cat); if (!items || items.length === 0) continue; @@ -106,18 +144,34 @@ export class RStackAppSwitcher extends HTMLElement { html += `
Other
`; html += uncategorized.map((m) => this.#renderItem(m, current)).join(""); } + + // Footer + html += ` + + `; + return html; } #renderItem(m: AppSwitcherModule, current: string): string { + const badgeInfo = MODULE_BADGES[m.id]; + const badgeHtml = badgeInfo + ? `${badgeInfo.badge}` + : `${m.icon}`; + return `
- ${m.icon} + ${badgeHtml}
- ${m.name} + + ${m.name} + ${m.icon} + ${m.description}
@@ -129,12 +183,18 @@ export class RStackAppSwitcher extends HTMLElement { #render() { const current = this.current; const currentMod = this.#modules.find((m) => m.id === current); - const label = currentMod ? `${currentMod.icon} ${currentMod.name}` : "🌌 rSpace"; + const badgeInfo = currentMod ? MODULE_BADGES[currentMod.id] : null; + + const triggerContent = badgeInfo + ? `${badgeInfo.badge} ${currentMod!.name}` + : currentMod + ? `${currentMod.icon} ${currentMod.name}` + : `r* rStack`; this.#shadow.innerHTML = `
- + @@ -192,11 +252,21 @@ const STYLES = ` .trigger:hover { background: rgba(255,255,255,0.12); } :host-context([data-theme="light"]) .trigger:hover { background: rgba(0,0,0,0.08); } +.trigger-badge { + display: inline-flex; align-items: center; justify-content: center; + width: 22px; height: 22px; border-radius: 5px; + font-size: 0.6rem; font-weight: 900; color: #0f172a; + line-height: 1; flex-shrink: 0; +} +.trigger-badge.rstack-gradient { + background: linear-gradient(135deg, #67e8f9, #c4b5fd, #fda4af); +} + .caret { font-size: 0.7em; opacity: 0.6; } .menu { position: absolute; top: 100%; left: 0; margin-top: 6px; - min-width: 260px; border-radius: 12px; overflow: hidden; + min-width: 300px; border-radius: 12px; overflow: hidden; overflow-y: auto; max-height: 70vh; box-shadow: 0 8px 30px rgba(0,0,0,0.25); display: none; z-index: 200; } @@ -208,6 +278,37 @@ const STYLES = ` background: #1e293b; border: 1px solid rgba(255,255,255,0.1); } +/* rStack header */ +.rstack-header { + display: flex; align-items: center; gap: 10px; + padding: 12px 14px; border-bottom: 1px solid rgba(128,128,128,0.15); +} +.rstack-badge { + display: flex; align-items: center; justify-content: center; + width: 28px; height: 28px; border-radius: 8px; + background: linear-gradient(135deg, #67e8f9, #c4b5fd, #fda4af); + font-size: 0.7rem; font-weight: 900; color: #0f172a; line-height: 1; + flex-shrink: 0; +} +.rstack-info { display: flex; flex-direction: column; } +.rstack-title { font-size: 0.875rem; font-weight: 700; } +.rstack-subtitle { font-size: 0.65rem; opacity: 0.5; } +:host-context([data-theme="light"]) .rstack-title { color: #0f172a; } +:host-context([data-theme="dark"]) .rstack-title { color: white; } + +/* Footer */ +.rstack-footer { + padding: 10px 14px; text-align: center; + border-top: 1px solid rgba(128,128,128,0.15); +} +.rstack-footer a { + font-size: 0.7rem; opacity: 0.4; text-decoration: none; color: inherit; + transition: opacity 0.15s; +} +.rstack-footer a:hover { opacity: 0.8; } +:host-context([data-theme="light"]) .rstack-footer a:hover { color: #06b6d4; } +:host-context([data-theme="dark"]) .rstack-footer a:hover { color: #22d3ee; } + .item-row { display: flex; align-items: center; transition: background 0.12s; @@ -220,8 +321,8 @@ const STYLES = ` :host-context([data-theme="dark"]) .item-row.active { background: rgba(6,182,212,0.1); } .item { - display: flex; align-items: center; gap: 12px; - padding: 10px 14px; text-decoration: none; + display: flex; align-items: center; gap: 10px; + padding: 8px 14px; text-decoration: none; cursor: pointer; flex: 1; min-width: 0; color: inherit; } @@ -236,14 +337,22 @@ const STYLES = ` :host-context([data-theme="light"]) .item-ext { color: #06b6d4; } :host-context([data-theme="dark"]) .item-ext { color: #22d3ee; } +.item-badge { + display: flex; align-items: center; justify-content: center; + width: 28px; height: 28px; border-radius: 6px; + font-size: 0.6rem; font-weight: 900; color: #0f172a; + line-height: 1; flex-shrink: 0; +} .item-icon { font-size: 1.3rem; width: 28px; text-align: center; flex-shrink: 0; } -.item-text { display: flex; flex-direction: column; min-width: 0; } +.item-text { display: flex; flex-direction: column; min-width: 0; flex: 1; } +.item-name-row { display: flex; align-items: center; gap: 6px; } .item-name { font-size: 0.875rem; font-weight: 600; } -.item-desc { font-size: 0.75rem; opacity: 0.6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.item-emoji { font-size: 0.875rem; flex-shrink: 0; } +.item-desc { font-size: 0.7rem; opacity: 0.5; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .category-header { - padding: 8px 14px 4px; font-size: 0.7rem; font-weight: 700; - text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.5; + padding: 8px 14px 4px; font-size: 0.6rem; font-weight: 700; + text-transform: uppercase; letter-spacing: 0.08em; opacity: 0.5; user-select: none; } .category-header:not(:first-child) {