Merge branch 'main' into dev

This commit is contained in:
Jeff Emmett 2026-04-01 12:33:19 -07:00
commit adb1c7cb87
6 changed files with 47 additions and 24 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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 <noreply@rmail.online>",
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 <noreply@rmail.online>",
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: `

View File

@ -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 += `
<div class="catalog-divider">
<button class="catalog-toggle" id="catalog-toggle">
${this.#catalogOpen ? '▾' : '▸'} Manage rApps
${disabledModules.length > 0 ? `<span class="catalog-count">${disabledModules.length} available</span>` : ''}
${disabledModules.length > 0 ? `<span class="catalog-count">${disabledModules.length} more</span>` : ''}
</button>
</div>
`;
if (this.#catalogOpen) {
html += `<div class="catalog-panel" id="catalog-panel">`;
// Show enabled modules with toggle-off option
const enabledNonCore = this.#allModules.filter(
m => m.enabled !== false && m.id !== 'rspace'
);
if (enabledNonCore.length > 0) {
html += `<div class="catalog-section-label">Enabled</div>`;
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 += `<div class="catalog-section-label">Available to Add</div>`;
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 += `<div class="catalog-section-label">Remove from Space</div>`;
for (const m of enabledNonCore) {
html += this.#renderCatalogItem(m, true);
}
}
html += `</div>`;
}
}

View File

@ -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: `

View File

@ -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) */