From 45372c6681ff378581b9893c3d666729e76e10ba Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Wed, 1 Apr 2026 12:32:53 -0700 Subject: [PATCH] Fix sidebar showing all modules: hide Manage panel when none disabled - App switcher: only show "Manage rApps" when there are actually disabled modules or active restrictions. Move "Available to Add" above "Remove" to prioritize adding. Eliminates duplicate module listing when all modules are enabled. - Shell: update app switcher on modules-changed event (was only updating tab bar and folk-rapp, not the sidebar itself). - SMTP: use space-agent@rspace.online as From for invite/approval emails with proper envelope sender for DKIM alignment. - Shell CSS: fix banner z-index, smooth header transition on banner. Co-Authored-By: Claude Opus 4.6 --- modules/rinbox/mod.ts | 2 ++ server/shell.ts | 11 ++++++++- server/spaces.ts | 14 ++++++++---- shared/components/rstack-app-switcher.ts | 29 +++++++++++++----------- src/encryptid/server.ts | 5 +++- website/public/shell.css | 10 ++++---- 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/modules/rinbox/mod.ts b/modules/rinbox/mod.ts index 5579c78..60938ba 100644 --- a/modules/rinbox/mod.ts +++ b/modules/rinbox/mod.ts @@ -458,6 +458,8 @@ async function executeApproval(docId: string, approvalId: string) { replyTo: agentReplyTo, subject: approval.subject, text: approval.bodyText, + // Envelope override: authenticate as SMTP_USER but show mailboxEmail in From header + envelope: { from: SMTP_USER || 'noreply@rmail.online', to: approval.toAddresses }, }; if (approval.ccAddresses.length > 0) { diff --git a/server/shell.ts b/server/shell.ts index 3e12776..edc043a 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -607,10 +607,19 @@ export function renderShell(opts: ShellOptions): string { var enabledModules = detail.enabledModules; window.__rspaceEnabledModules = enabledModules; - // Update tab bar's module list + // Recompute visible + allModules with updated enabled flags var allMods = window.__rspaceAllModules || []; var enabledSet = new Set(enabledModules); var visible = allMods.filter(function(m) { return m.id === 'rspace' || enabledSet.has(m.id); }); + var updatedAll = allMods.map(function(m) { return Object.assign({}, m, { enabled: m.id === 'rspace' || enabledSet.has(m.id) }); }); + window.__rspaceModuleList = visible; + window.__rspaceAllModules = updatedAll; + + // Update app switcher's main navigation + catalog + var switcher = document.querySelector('rstack-app-switcher'); + if (switcher) { switcher.setModules(visible); switcher.setAllModules(updatedAll); } + + // Update tab bar's module list var tb = document.querySelector('rstack-tab-bar'); if (tb) tb.setModules(visible); diff --git a/server/spaces.ts b/server/spaces.ts index 8549923..1970a9e 100644 --- a/server/spaces.ts +++ b/server/spaces.ts @@ -2199,11 +2199,14 @@ spaces.post("/:slug/invite", async (c) => { metadata: { inviteToken: invite.token, role }, }).catch(() => {}); - // Send invite email + // Send invite email from {slug}-agent@rspace.online if (inviteTransport) { try { + const agentAddr = `${slug}-agent@rspace.online`; await inviteTransport.sendMail({ - from: process.env.SMTP_FROM || "rSpace ", + from: `${slug} <${agentAddr}>`, + replyTo: agentAddr, + envelope: { from: process.env.SMTP_USER || "noreply@rmail.online", to: body.email }, to: body.email, subject: `${inviterName} invited you to "${slug}" on rSpace`, html: ` @@ -2326,11 +2329,14 @@ spaces.post("/:slug/members/add", async (c) => { metadata: { inviteToken: invite.token, role }, }).catch(() => {}); - // Send invite email (non-fatal) + // Send invite email from {slug}-agent@rspace.online (non-fatal) if (inviteTransport && targetEmail) { try { + const agentAddr = `${slug}-agent@rspace.online`; await inviteTransport.sendMail({ - from: process.env.SMTP_FROM || "rSpace ", + from: `${slug} <${agentAddr}>`, + replyTo: agentAddr, + envelope: { from: process.env.SMTP_USER || "noreply@rmail.online", to: targetEmail }, to: targetEmail, subject: `${inviterName} invited you to "${slug}" on rSpace`, html: ` diff --git a/shared/components/rstack-app-switcher.ts b/shared/components/rstack-app-switcher.ts index 3da8a95..7a2bfe8 100644 --- a/shared/components/rstack-app-switcher.ts +++ b/shared/components/rstack-app-switcher.ts @@ -243,34 +243,37 @@ export class RStackAppSwitcher extends HTMLElement { const disabledModules = this.#allModules.filter( m => m.enabled === false && m.id !== 'rspace' ); - if (disabledModules.length > 0 || this.#allModules.length > 0) { + // Only show the Manage section when there are disabled modules to add, + // or when enabledModules is actively configured (not null/all-enabled) + const hasRestrictions = disabledModules.length > 0; + if (hasRestrictions || this.#allModules.length > this.#modules.length) { 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 + // Show disabled modules with add option if (disabledModules.length > 0) { html += ``; for (const m of disabledModules) { html += this.#renderCatalogItem(m, false); } } + // Show enabled modules with remove option (compact section below) + 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); + } + } html += `
`; } } diff --git a/src/encryptid/server.ts b/src/encryptid/server.ts index 48c8465..f255407 100644 --- a/src/encryptid/server.ts +++ b/src/encryptid/server.ts @@ -5462,8 +5462,11 @@ app.post('/api/invites/identity', async (c) => { const subjectLine = spaceSlug ? `${payload.username} invited you to join "${spaceSlug}" on rSpace` : `${payload.username} invited you to join rSpace`; + const agentAddr = spaceSlug ? `${spaceSlug}-agent@rspace.online` : null; await smtpTransport.sendMail({ - from: CONFIG.smtp.from, + from: agentAddr ? `${spaceSlug} <${agentAddr}>` : CONFIG.smtp.from, + ...(agentAddr ? { replyTo: agentAddr } : {}), + ...(agentAddr ? { envelope: { from: CONFIG.smtp.user, to: email } } : {}), to: email, subject: subjectLine, html: ` diff --git a/website/public/shell.css b/website/public/shell.css index 7676814..6c0b02f 100644 --- a/website/public/shell.css +++ b/website/public/shell.css @@ -15,7 +15,7 @@ body { .rspace-banner { position: fixed; top: 0; left: 0; right: 0; - z-index: 10002; + z-index: 10000; /* above header (9999) */ display: flex; align-items: center; justify-content: center; @@ -68,7 +68,7 @@ body { .rspace-banner__close:hover { color: #fff; } -/* Push header down when a banner is visible */ +/* Push header + content down when a banner is visible */ body.rspace-banner-visible .rstack-header { top: 36px; } @@ -628,13 +628,13 @@ body.rspace-headers-minimized .rapp-subnav { /* Smooth transitions for header minimize/restore */ .rstack-header { - transition: transform 0.25s ease; + transition: transform 0.25s ease, top 0.3s ease-out; } .rstack-tab-row { - transition: margin-left 0.25s ease, left 0.25s ease, transform 0.25s ease; + transition: margin-left 0.25s ease, left 0.25s ease, transform 0.25s ease, top 0.3s ease-out; } #app { - transition: margin-left 0.25s ease, left 0.25s ease, padding-top 0.25s ease; + transition: margin-left 0.25s ease, left 0.25s ease, padding-top 0.3s ease-out; } /* Mobile: minimized state adjustments (sticky, not fixed) */