diff --git a/modules/rsplat/components/folk-splat-viewer.ts b/modules/rsplat/components/folk-splat-viewer.ts index acad001..977cb2e 100644 --- a/modules/rsplat/components/folk-splat-viewer.ts +++ b/modules/rsplat/components/folk-splat-viewer.ts @@ -698,8 +698,8 @@ export class FolkSplatViewer extends HTMLElement { private async initGlbViewer(container: HTMLElement, loading: HTMLElement | null) { const THREE = await import("three"); - const { OrbitControls } = await import("three/examples/jsm/controls/OrbitControls.js"); - const { GLTFLoader } = await import("three/examples/jsm/loaders/GLTFLoader.js"); + const { OrbitControls } = await import("three/addons/controls/OrbitControls.js"); + const { GLTFLoader } = await import("three/addons/loaders/GLTFLoader.js"); const w = container.clientWidth || 800; const h = container.clientHeight || 600; diff --git a/modules/rsplat/mod.ts b/modules/rsplat/mod.ts index f000726..0f65da7 100644 --- a/modules/rsplat/mod.ts +++ b/modules/rsplat/mod.ts @@ -605,7 +605,7 @@ routes.get("/", async (c) => { `, scripts: ` `, }); diff --git a/server/shell.ts b/server/shell.ts index 2599983..b79ded6 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -102,6 +102,11 @@ export function renderShell(opts: ShellOptions): string { ? modules.filter(m => m.id === "rspace" || enabledModules.includes(m.id)) : modules; const moduleListJSON = JSON.stringify(visibleModules); + // Full catalog with enabled flags for "Manage rApps" panel + const allModulesJSON = JSON.stringify(modules.map(m => ({ + ...m, + enabled: !enabledModules || enabledModules.includes(m.id) || m.id === "rspace", + }))); const shellDemoUrl = `https://demo.rspace.online/${escapeAttr(moduleId)}`; return versionAssetUrls(` @@ -352,7 +357,10 @@ export function renderShell(opts: ShellOptions): string { (function(){try{var t=localStorage.getItem('canvas-theme');if(t)document.querySelectorAll('[data-theme]').forEach(function(el){el.setAttribute('data-theme',t)})}catch(e){}})(); // Provide module list to app switcher and offline runtime window.__rspaceModuleList = ${moduleListJSON}; - document.querySelector('rstack-app-switcher')?.setModules(window.__rspaceModuleList); + window.__rspaceAllModules = ${allModulesJSON}; + const _switcher = document.querySelector('rstack-app-switcher'); + _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 diff --git a/shared/components/rstack-app-switcher.ts b/shared/components/rstack-app-switcher.ts index db2f2a1..0b30cd4 100644 --- a/shared/components/rstack-app-switcher.ts +++ b/shared/components/rstack-app-switcher.ts @@ -15,6 +15,7 @@ export interface AppSwitcherModule { description: string; standaloneDomain?: string; scoping?: { defaultScope: 'space' | 'global'; userConfigurable: boolean }; + enabled?: boolean; } // Pastel badge abbreviations & colors for each module @@ -112,7 +113,10 @@ import { rspaceNavUrl, getCurrentSpace, isStandaloneDomain } from "../url-helper export class RStackAppSwitcher extends HTMLElement { #shadow: ShadowRoot; #modules: AppSwitcherModule[] = []; + #allModules: AppSwitcherModule[] = []; // Full catalog including disabled #isOpen = false; + #catalogOpen = false; + #catalogBusy = false; #outsideClickHandler: ((e: MouseEvent) => void) | null = null; constructor() { @@ -168,6 +172,11 @@ export class RStackAppSwitcher extends HTMLElement { this.#render(); } + /** Provide the full module catalog (enabled + disabled) for the "Manage rApps" panel */ + setAllModules(modules: AppSwitcherModule[]) { + this.#allModules = modules; + } + #renderGroupedModules(current: string): string { // Group modules by category const groups = new Map(); @@ -215,6 +224,42 @@ export class RStackAppSwitcher extends HTMLElement { html += uncategorized.map((m) => this.#renderItem(m, current)).join(""); } + // "Manage rApps" catalog section + const disabledModules = this.#allModules.filter( + m => m.enabled === false && m.id !== 'rspace' + ); + if (disabledModules.length > 0 || this.#allModules.length > 0) { + html += ` +
+ +
+ `; + if (this.#catalogOpen) { + html += `
`; + // Show enabled modules with toggle-off option + const enabledNonCore = this.#allModules.filter( + m => m.enabled !== false && m.id !== 'rspace' + ); + if (enabledNonCore.length > 0) { + html += ``; + for (const m of enabledNonCore) { + html += this.#renderCatalogItem(m, true); + } + } + // Show disabled modules with toggle-on option + if (disabledModules.length > 0) { + html += ``; + for (const m of disabledModules) { + html += this.#renderCatalogItem(m, false); + } + } + html += `
`; + } + } + // Footer html += `