Merge branch 'dev'

This commit is contained in:
Jeff Emmett 2026-03-04 18:02:38 -08:00
commit 627277303e
3 changed files with 24 additions and 27 deletions

View File

@ -240,8 +240,12 @@ spaces.get("/", async (c) => {
const isPermissioned = vis === "permissioned"; const isPermissioned = vis === "permissioned";
const accessible = isPublicSpace || isOwner || isMember || (isPermissioned && !!claims); const accessible = isPublicSpace || isOwner || isMember || (isPermissioned && !!claims);
// For unauthenticated: only show public spaces // For unauthenticated: only show demo
if (!claims && !isPublicSpace) continue; if (!claims && slug !== "demo") continue;
// For authenticated: skip public spaces the user has no role in
// (demo is shown separately, other public spaces are noise)
if (claims && isPublicSpace && !isOwner && !isMember && slug !== "demo") continue;
// Determine relationship // Determine relationship
const relationship = isOwner const relationship = isOwner

View File

@ -223,6 +223,18 @@ function autoResolveSpace(token: string, username: string): void {
.catch(() => {}); .catch(() => {});
} }
// ── Silent provisioning (no redirect) — ensures user's space exists ──
function autoProvisionSpace(token: string): void {
fetch("/api/spaces/auto-provision", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
}).catch(() => {});
}
// ── Inline URL helpers (avoid import cycle with url-helpers) ── // ── Inline URL helpers (avoid import cycle with url-helpers) ──
const _RESERVED = ["www", "rspace", "create", "new", "start", "auth"]; const _RESERVED = ["www", "rspace", "create", "new", "start", "auth"];
function _isSubdomain(): boolean { function _isSubdomain(): boolean {
@ -264,12 +276,12 @@ export class RStackIdentity extends HTMLElement {
this.#refreshIfNeeded(); this.#refreshIfNeeded();
this.#render(); this.#render();
// Belt-and-suspenders: if a session already exists on page load, // If a session already exists on page load, provision the
// ensure the user's personal space is provisioned (catches edge // user's personal space in the background (but do NOT redirect —
// cases like iframe embedding or direct navigation). // the user intentionally navigated to this space).
const session = getSession(); const session = getSession();
if (session?.accessToken && session.claims.username) { if (session?.accessToken && session.claims.username) {
autoResolveSpace(session.accessToken, session.claims.username); autoProvisionSpace(session.accessToken);
} }
} }

View File

@ -171,9 +171,8 @@ export class RStackSpaceSwitcher extends HTMLElement {
return; return;
} }
// 3-section split — exclude demo from public since we show it separately // Split: user's own spaces vs permissioned spaces they can discover
const mySpaces = this.#spaces.filter((s) => s.role); const mySpaces = this.#spaces.filter((s) => s.role);
const publicSpaces = this.#spaces.filter((s) => s.accessible !== false && !s.role && s.slug !== "demo");
const discoverSpaces = this.#spaces.filter((s) => s.accessible === false); const discoverSpaces = this.#spaces.filter((s) => s.accessible === false);
const hasOwnedSpace = mySpaces.some((s) => s.relationship === "owner"); const hasOwnedSpace = mySpaces.some((s) => s.relationship === "owner");
@ -214,25 +213,7 @@ export class RStackSpaceSwitcher extends HTMLElement {
.join(""); .join("");
} }
// ── Public spaces ── // ── Discover (permissioned spaces the user can request access to) ──
if (publicSpaces.length > 0) {
if (mySpaces.length > 0) html += `<div class="divider"></div>`;
html += `<div class="section-label">Public spaces</div>`;
html += publicSpaces
.map((s) => {
const vis = this.#visibilityInfo(s);
return `
<a class="item ${vis.cls} ${s.slug === current ? "active" : ""}"
href="${rspaceNavUrl(s.slug, moduleId)}">
<span class="item-icon">${s.icon || "🌐"}</span>
<span class="item-name">${this.#displayName(s)}</span>
<span class="item-vis ${vis.cls}">${vis.label}</span>
</a>`;
})
.join("");
}
// ── Discover (inaccessible spaces) ──
if (auth && discoverSpaces.length > 0) { if (auth && discoverSpaces.length > 0) {
html += `<div class="divider"></div>`; html += `<div class="divider"></div>`;
html += `<div class="section-label">Discover</div>`; html += `<div class="section-label">Discover</div>`;