Merge branch 'dev'

This commit is contained in:
Jeff Emmett 2026-03-04 10:31:44 -08:00
commit ceff80a036
10 changed files with 280 additions and 215 deletions

View File

@ -649,32 +649,32 @@ const THREAD_CSS = `
.thread-btn { padding: 0.5rem 1rem; border-radius: 8px; border: none; font-size: 0.85rem; font-weight: 600; cursor: pointer; transition: all 0.15s; }
.thread-btn--primary { background: #6366f1; color: white; }
.thread-btn--primary:hover { background: #818cf8; }
.thread-btn--outline { background: transparent; color: #94a3b8; border: 1px solid #334155; }
.thread-btn--outline { background: transparent; color: var(--rs-text-secondary); border: 1px solid var(--rs-input-border); }
.thread-btn--outline:hover { border-color: #6366f1; color: #c4b5fd; }
.thread-btn--success { background: #10b981; color: white; }
.thread-btn--success:hover { background: #34d399; }
.thread-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.thread-compose { position: sticky; top: 1rem; align-self: start; display: flex; flex-direction: column; gap: 1rem; }
.thread-compose__textarea {
width: 100%; min-height: 320px; background: #1e293b; color: #e2e8f0; border: 1px solid #334155;
width: 100%; min-height: 320px; background: var(--rs-bg-surface); color: var(--rs-text-primary); border: 1px solid var(--rs-input-border);
border-radius: 0.75rem; padding: 1rem; font-family: inherit; font-size: 0.9rem; resize: vertical;
line-height: 1.6; box-sizing: border-box;
}
.thread-compose__textarea:focus { outline: none; border-color: #6366f1; }
.thread-compose__textarea::placeholder { color: #475569; }
.thread-compose__textarea::placeholder { color: var(--rs-text-muted); }
.thread-compose__fields { display: flex; gap: 0.75rem; }
.thread-compose__input {
flex: 1; background: #1e293b; color: #e2e8f0; border: 1px solid #334155;
flex: 1; background: var(--rs-bg-surface); color: var(--rs-text-primary); border: 1px solid var(--rs-input-border);
border-radius: 8px; padding: 0.5rem 0.75rem; font-size: 0.85rem; box-sizing: border-box;
}
.thread-compose__input:focus { outline: none; border-color: #6366f1; }
.thread-compose__input::placeholder { color: #475569; }
.thread-compose__input::placeholder { color: var(--rs-text-muted); }
.thread-compose__title {
width: 100%; background: #1e293b; color: #e2e8f0; border: 1px solid #334155;
width: 100%; background: var(--rs-bg-surface); color: var(--rs-text-primary); border: 1px solid var(--rs-input-border);
border-radius: 8px; padding: 0.5rem 0.75rem; font-size: 0.85rem; box-sizing: border-box;
}
.thread-compose__title:focus { outline: none; border-color: #6366f1; }
.thread-compose__title::placeholder { color: #475569; }
.thread-compose__title::placeholder { color: var(--rs-text-muted); }
.thread-drafts { grid-column: 1 / -1; }
.thread-drafts__toggle { cursor: pointer; user-select: none; }
.thread-drafts__list {
@ -682,24 +682,24 @@ const THREAD_CSS = `
margin-top: 0.75rem;
}
.thread-drafts__list[hidden] { display: none; }
.thread-drafts__empty { color: #475569; font-size: 0.8rem; padding: 0.5rem 0; }
.thread-drafts__empty { color: var(--rs-text-muted); font-size: 0.8rem; padding: 0.5rem 0; }
.thread-draft-item {
display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem;
background: #1e293b; border: 1px solid #334155; border-radius: 8px;
background: var(--rs-bg-surface); border: 1px solid var(--rs-input-border); border-radius: 8px;
transition: border-color 0.15s; cursor: pointer;
}
.thread-draft-item:hover { border-color: #6366f1; }
.thread-draft-item--active { border-color: #6366f1; background: rgba(99,102,241,0.1); }
.thread-draft-item__info { flex: 1; min-width: 0; }
.thread-draft-item__info strong { display: block; font-size: 0.8rem; color: #e2e8f0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.thread-draft-item__info span { font-size: 0.7rem; color: #64748b; }
.thread-draft-item__info strong { display: block; font-size: 0.8rem; color: var(--rs-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.thread-draft-item__info span { font-size: 0.7rem; color: var(--rs-text-muted); }
.thread-draft-item__delete {
background: none; border: none; color: #64748b; font-size: 1.2rem; cursor: pointer;
background: none; border: none; color: var(--rs-text-muted); font-size: 1.2rem; cursor: pointer;
padding: 0 4px; line-height: 1; flex-shrink: 0;
}
.thread-draft-item__delete:hover { color: #ef4444; }
.thread-image-section { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.thread-image-preview { border-radius: 8px; overflow: hidden; border: 1px solid #334155; }
.thread-image-preview { border-radius: 8px; overflow: hidden; border: 1px solid var(--rs-input-border); }
.thread-image-preview[hidden] { display: none; }
.thread-image-preview img { display: block; max-width: 200px; height: auto; }
#share-link-area { grid-column: 1 / -1; }
@ -710,20 +710,20 @@ const THREAD_CSS = `
}
.thread-share-link code { font-size: 0.75rem; color: #7dd3fc; }
.thread-share-link button {
background: none; border: none; color: #94a3b8; cursor: pointer; font-size: 0.75rem; padding: 2px 6px;
background: none; border: none; color: var(--rs-text-secondary); cursor: pointer; font-size: 0.75rem; padding: 2px 6px;
}
.thread-share-link button:hover { color: #e2e8f0; }
.thread-share-link button:hover { color: var(--rs-text-primary); }
.thread-preview { display: flex; flex-direction: column; gap: 0; }
.thread-preview__empty { color: #475569; text-align: center; padding: 3rem 1rem; font-size: 0.9rem; }
.thread-preview__empty { color: var(--rs-text-muted); text-align: center; padding: 3rem 1rem; font-size: 0.9rem; }
.tweet-card {
position: relative; background: #1e293b; border: 1px solid #334155; border-radius: 0.75rem;
position: relative; background: var(--rs-bg-surface); border: 1px solid var(--rs-input-border); border-radius: 0.75rem;
padding: 1rem; margin-bottom: 0;
}
.tweet-card + .tweet-card { border-top-left-radius: 0; border-top-right-radius: 0; margin-top: -1px; }
.tweet-card:has(+ .tweet-card) { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.tweet-card__connector {
position: absolute; left: 29px; top: -1px; width: 2px; height: 1rem;
background: #334155; z-index: 1;
background: var(--rs-input-border); z-index: 1;
}
.tweet-card__header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
.tweet-card__avatar {
@ -731,16 +731,16 @@ const THREAD_CSS = `
display: flex; align-items: center; justify-content: center; color: white;
font-weight: 700; font-size: 1rem; flex-shrink: 0;
}
.tweet-card__name { font-weight: 700; color: #f1f5f9; font-size: 0.9rem; }
.tweet-card__handle { color: #64748b; font-size: 0.85rem; }
.tweet-card__dot { color: #64748b; font-size: 0.85rem; }
.tweet-card__time { color: #64748b; font-size: 0.85rem; }
.tweet-card__content { color: #e2e8f0; font-size: 0.95rem; line-height: 1.6; margin: 0 0 0.75rem; white-space: pre-wrap; word-break: break-word; }
.tweet-card__name { font-weight: 700; color: var(--rs-text-primary); font-size: 0.9rem; }
.tweet-card__handle { color: var(--rs-text-muted); font-size: 0.85rem; }
.tweet-card__dot { color: var(--rs-text-muted); font-size: 0.85rem; }
.tweet-card__time { color: var(--rs-text-muted); font-size: 0.85rem; }
.tweet-card__content { color: var(--rs-text-primary); font-size: 0.95rem; line-height: 1.6; margin: 0 0 0.75rem; white-space: pre-wrap; word-break: break-word; }
.tweet-card__footer { display: flex; align-items: center; justify-content: space-between; }
.tweet-card__actions { display: flex; gap: 1.25rem; }
.tweet-card__action { display: flex; align-items: center; gap: 0.3rem; color: #64748b; font-size: 0.8rem; cursor: default; }
.tweet-card__action { display: flex; align-items: center; gap: 0.3rem; color: var(--rs-text-muted); font-size: 0.8rem; cursor: default; }
.tweet-card__action svg { width: 16px; height: 16px; }
.tweet-card__meta { display: flex; align-items: center; gap: 0.75rem; font-size: 0.75rem; color: #64748b; }
.tweet-card__meta { display: flex; align-items: center; gap: 0.75rem; font-size: 0.75rem; color: var(--rs-text-muted); }
.tweet-card__chars { font-variant-numeric: tabular-nums; }
.tweet-card__chars--over { color: #ef4444; font-weight: 600; }
.tweet-card__thread-num { color: #6366f1; font-weight: 600; }
@ -751,18 +751,18 @@ const THREAD_CSS = `
.thread-export-dropdown { position: relative; }
.thread-export-menu {
position: absolute; top: calc(100% + 4px); right: 0; z-index: 100;
background: #1e293b; border: 1px solid #334155; border-radius: 8px;
background: var(--rs-bg-surface); border: 1px solid var(--rs-input-border); border-radius: 8px;
min-width: 180px; overflow: hidden;
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
box-shadow: 0 8px 24px var(--rs-shadow-lg);
}
.thread-export-menu[hidden] { display: none; }
.thread-export-menu button {
display: block; width: 100%; padding: 0.6rem 0.75rem; border: none;
background: transparent; color: #e2e8f0; font-size: 0.85rem;
background: transparent; color: var(--rs-text-primary); font-size: 0.85rem;
text-align: left; cursor: pointer; transition: background 0.1s;
}
.thread-export-menu button:hover { background: rgba(99,102,241,0.15); }
.thread-export-menu button + button { border-top: 1px solid rgba(255,255,255,0.05); }
.thread-export-menu button + button { border-top: 1px solid var(--rs-bg-hover); }
`;
function renderThreadBuilderPage(space: string, threadData?: ThreadData | null): string {
@ -1404,35 +1404,35 @@ const THREAD_RO_CSS = `
.thread-ro { max-width: 640px; margin: 0 auto; padding: 2rem 1rem; }
.thread-ro__header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem; flex-wrap: wrap; gap: 1rem; }
.thread-ro__author { display: flex; align-items: center; gap: 0.75rem; }
.thread-ro__name { font-weight: 700; color: #f1f5f9; font-size: 1.1rem; }
.thread-ro__handle { color: #64748b; font-size: 0.9rem; }
.thread-ro__meta { display: flex; align-items: center; gap: 0.5rem; color: #64748b; font-size: 0.85rem; }
.thread-ro__title { font-size: 1.4rem; color: #f1f5f9; margin: 0 0 1.5rem; line-height: 1.3; }
.thread-ro__image { margin-bottom: 1.5rem; border-radius: 12px; overflow: hidden; border: 1px solid #334155; }
.thread-ro__name { font-weight: 700; color: var(--rs-text-primary); font-size: 1.1rem; }
.thread-ro__handle { color: var(--rs-text-muted); font-size: 0.9rem; }
.thread-ro__meta { display: flex; align-items: center; gap: 0.5rem; color: var(--rs-text-muted); font-size: 0.85rem; }
.thread-ro__title { font-size: 1.4rem; color: var(--rs-text-primary); margin: 0 0 1.5rem; line-height: 1.3; }
.thread-ro__image { margin-bottom: 1.5rem; border-radius: 12px; overflow: hidden; border: 1px solid var(--rs-input-border); }
.thread-ro__image img { display: block; width: 100%; height: auto; }
.thread-ro__cards { margin-bottom: 1.5rem; }
.thread-ro__actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 2rem; padding-bottom: 2rem; border-bottom: 1px solid #334155; }
.thread-ro__actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 2rem; padding-bottom: 2rem; border-bottom: 1px solid var(--rs-input-border); }
.thread-ro__cta { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; }
.thread-export-dropdown { position: relative; }
.thread-export-menu {
position: absolute; top: calc(100% + 4px); right: 0; z-index: 100;
background: #1e293b; border: 1px solid #334155; border-radius: 8px;
background: var(--rs-bg-surface); border: 1px solid var(--rs-input-border); border-radius: 8px;
min-width: 180px; overflow: hidden;
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
box-shadow: 0 8px 24px var(--rs-shadow-lg);
}
.thread-export-menu[hidden] { display: none; }
.thread-export-menu button {
display: block; width: 100%; padding: 0.6rem 0.75rem; border: none;
background: transparent; color: #e2e8f0; font-size: 0.85rem;
background: transparent; color: var(--rs-text-primary); font-size: 0.85rem;
text-align: left; cursor: pointer; transition: background 0.1s;
}
.thread-export-menu button:hover { background: rgba(99,102,241,0.15); }
.thread-export-menu button + button { border-top: 1px solid rgba(255,255,255,0.05); }
.thread-export-menu button + button { border-top: 1px solid var(--rs-bg-hover); }
.thread-export-toast {
position: fixed; bottom: 1.5rem; left: 50%; transform: translateX(-50%);
background: #1e293b; border: 1px solid #6366f1; color: #c4b5fd;
background: var(--rs-bg-surface); border: 1px solid #6366f1; color: #c4b5fd;
padding: 0.6rem 1.25rem; border-radius: 8px; font-size: 0.85rem;
box-shadow: 0 4px 16px rgba(0,0,0,0.4); z-index: 1000;
box-shadow: 0 4px 16px var(--rs-shadow-lg); z-index: 1000;
transition: opacity 0.2s;
}
.thread-export-toast[hidden] { display: none; }
@ -1523,27 +1523,27 @@ const THREADS_LIST_CSS = `
.threads-gallery__grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem;
}
.threads-gallery__empty { color: #64748b; text-align: center; padding: 3rem 1rem; font-size: 0.9rem; }
.threads-gallery__empty { color: var(--rs-text-muted); text-align: center; padding: 3rem 1rem; font-size: 0.9rem; }
.thread-card {
background: #1e293b; border: 1px solid #334155; border-radius: 0.75rem;
background: var(--rs-bg-surface); border: 1px solid var(--rs-input-border); border-radius: 0.75rem;
padding: 1.25rem; transition: border-color 0.15s, transform 0.15s;
display: flex; flex-direction: column; gap: 0.75rem;
text-decoration: none; color: inherit;
}
.thread-card:hover { border-color: #6366f1; transform: translateY(-2px); }
.thread-card__title { font-size: 1rem; font-weight: 700; color: #f1f5f9; margin: 0; line-height: 1.3; }
.thread-card__title { font-size: 1rem; font-weight: 700; color: var(--rs-text-primary); margin: 0; line-height: 1.3; }
.thread-card__preview {
font-size: 0.85rem; color: #94a3b8; line-height: 1.5;
font-size: 0.85rem; color: var(--rs-text-secondary); line-height: 1.5;
display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.thread-card__meta { display: flex; align-items: center; gap: 0.75rem; font-size: 0.75rem; color: #64748b; margin-top: auto; }
.thread-card__meta { display: flex; align-items: center; gap: 0.75rem; font-size: 0.75rem; color: var(--rs-text-muted); margin-top: auto; }
.thread-card__author { display: flex; align-items: center; gap: 0.4rem; }
.thread-card__avatar-sm {
width: 20px; height: 20px; border-radius: 50%; background: #6366f1;
display: flex; align-items: center; justify-content: center;
color: white; font-weight: 700; font-size: 0.55rem; flex-shrink: 0;
}
.thread-card__image { border-radius: 8px; overflow: hidden; border: 1px solid #334155; margin-bottom: 0.25rem; }
.thread-card__image { border-radius: 8px; overflow: hidden; border: 1px solid var(--rs-input-border); margin-bottom: 0.25rem; }
.thread-card__image img { display: block; width: 100%; height: 120px; object-fit: cover; }
`;

View File

@ -105,20 +105,20 @@ export function renderOutputListPage(
.output-list { max-width: 960px; margin: 0 auto; padding: 2rem 1rem; }
.output-list__header { display: flex; align-items: center; gap: 1rem; margin-bottom: 2rem; }
.output-list__icon { font-size: 2.5rem; }
.output-list__title { margin: 0; font-size: 1.5rem; color: #f1f5f9; }
.output-list__desc { margin: 0.25rem 0 0; color: #94a3b8; font-size: 0.95rem; }
.output-list__title { margin: 0; font-size: 1.5rem; color: var(--rs-text-primary); }
.output-list__desc { margin: 0.25rem 0 0; color: var(--rs-text-secondary); font-size: 0.95rem; }
.output-list__grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
}
.output-list__loading { color: #64748b; text-align: center; padding: 3rem 0; grid-column: 1 / -1; }
.output-list__empty { text-align: center; padding: 4rem 1rem; grid-column: 1 / -1; color: #64748b; }
.output-list__loading { color: var(--rs-text-muted); text-align: center; padding: 3rem 0; grid-column: 1 / -1; }
.output-list__empty { text-align: center; padding: 4rem 1rem; grid-column: 1 / -1; color: var(--rs-text-muted); }
.output-list__empty-icon { font-size: 3rem; margin-bottom: 0.5rem; }
.output-card {
display: block;
background: #1e293b;
border: 1px solid #334155;
background: var(--rs-bg-surface);
border: 1px solid var(--rs-bg-surface-raised);
border-radius: 0.75rem;
padding: 1.25rem;
text-decoration: none;
@ -126,9 +126,9 @@ export function renderOutputListPage(
transition: border-color 0.15s, transform 0.15s;
}
.output-card:hover { border-color: #6366f1; transform: translateY(-2px); }
.output-card__title { font-size: 1.05rem; font-weight: 600; color: #f1f5f9; margin-bottom: 0.5rem; }
.output-card__desc { font-size: 0.875rem; color: #94a3b8; line-height: 1.4; margin-bottom: 0.5rem; }
.output-card__date { font-size: 0.75rem; color: #64748b; }
.output-card__title { font-size: 1.05rem; font-weight: 600; color: var(--rs-text-primary); margin-bottom: 0.5rem; }
.output-card__desc { font-size: 0.875rem; color: var(--rs-text-secondary); line-height: 1.4; margin-bottom: 0.5rem; }
.output-card__date { font-size: 0.75rem; color: var(--rs-text-muted); }
</style>`;
return renderShell({

View File

@ -105,8 +105,7 @@ export function renderShell(opts: ShellOptions): string {
<div class="rstack-header__left">
<a href="/" style="display:flex;align-items:center;margin-right:4px"><img src="/favicon.png" alt="rSpace" class="rstack-header__logo"></a>
<rstack-app-switcher current="${escapeAttr(moduleId)}"></rstack-app-switcher>
<rstack-space-switcher current="${escapeAttr(spaceSlug)}" name="${escapeAttr(spaceName || spaceSlug)}"></rstack-space-switcher>${spaceEncrypted ? '<span class="rstack-header__encrypted" title="End-to-end encrypted space">&#x1F512;</span>' : ''}<button class="rstack-header__settings-btn" id="settings-btn" title="Space Settings" style="background:none;border:none;color:#94a3b8;cursor:pointer;padding:4px;margin-left:4px;display:flex;align-items:center;border-radius:4px;transition:color 0.15s,background 0.15s;" onmouseover="this.style.color='#e2e8f0';this.style.background='rgba(255,255,255,0.08)'" onmouseout="this.style.color='#94a3b8';this.style.background='none'"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
<rstack-space-settings space="${escapeAttr(spaceSlug)}"></rstack-space-settings>
<rstack-space-switcher current="${escapeAttr(spaceSlug)}" name="${escapeAttr(spaceName || spaceSlug)}"></rstack-space-switcher>${spaceEncrypted ? '<span class="rstack-header__encrypted" title="End-to-end encrypted space">&#x1F512;</span>' : ''}<button class="rstack-header__settings-btn" id="settings-btn" title="Space Settings" style="background:none;border:none;color:var(--rs-text-secondary);cursor:pointer;padding:4px;margin-left:4px;display:flex;align-items:center;border-radius:4px;transition:color 0.15s,background 0.15s;" onmouseover="this.style.color='var(--rs-text-primary)';this.style.background='var(--rs-btn-secondary-bg)'" onmouseout="this.style.color='var(--rs-text-secondary)';this.style.background='none'"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></button>
</div>
<div class="rstack-header__center">
<rstack-mi></rstack-mi>
@ -117,6 +116,7 @@ export function renderShell(opts: ShellOptions): string {
<rstack-identity></rstack-identity>
</div>
</header>
<rstack-space-settings space="${escapeAttr(spaceSlug)}"></rstack-space-settings>
<div class="rstack-tab-row">
<rstack-tab-bar space="${escapeAttr(spaceSlug)}" active="" view-mode="flat"></rstack-tab-bar>
</div>
@ -625,7 +625,7 @@ const ACCESS_GATE_CSS = `
#rspace-access-gate {
position: fixed; inset: 0; z-index: 9999;
display: flex; align-items: center; justify-content: center;
background: rgba(15, 23, 42, 0.95); backdrop-filter: blur(8px);
background: var(--rs-bg-overlay); backdrop-filter: blur(8px);
}
.access-gate__card {
text-align: center; color: var(--rs-text-primary); max-width: 400px; padding: 2rem;

View File

@ -1,5 +1,5 @@
/**
* <rstack-app-switcher> Dropdown to switch between rSpace modules.
* <rstack-app-switcher> Collapsible left sidebar to switch between rSpace modules.
*
* Attributes:
* current the active module ID (highlighted)
@ -236,29 +236,35 @@ export class RStackAppSwitcher extends HTMLElement {
<style>${STYLES}</style>
<div class="switcher">
<button class="trigger" id="trigger">${triggerContent} <span class="caret"></span></button>
<div class="menu" id="menu">
<div class="sidebar" id="sidebar">
<button class="collapse-btn" id="collapse" title="Close sidebar"></button>
${this.#renderGroupedModules(current)}
</div>
</div>
`;
const trigger = this.#shadow.getElementById("trigger")!;
const menu = this.#shadow.getElementById("menu")!;
const sidebar = this.#shadow.getElementById("sidebar")!;
const collapse = this.#shadow.getElementById("collapse")!;
const open = () => {
sidebar.classList.add("open");
document.body.classList.add("rstack-sidebar-open");
};
const close = () => {
sidebar.classList.remove("open");
document.body.classList.remove("rstack-sidebar-open");
};
const toggle = () => {
if (sidebar.classList.contains("open")) close(); else open();
};
trigger.addEventListener("click", (e) => {
e.stopPropagation();
const isOpen = menu.classList.toggle("open");
if (isOpen) {
const rect = trigger.getBoundingClientRect();
menu.style.top = `${rect.bottom + 6}px`;
menu.style.left = `${Math.max(4, rect.left)}px`;
}
toggle();
});
// Prevent external links from closing the menu prematurely
this.#shadow.querySelectorAll(".item-ext").forEach((el) => {
el.addEventListener("click", (e) => e.stopPropagation());
});
collapse.addEventListener("click", () => close());
// Intercept same-origin module links → dispatch event for tab system
this.#shadow.querySelectorAll("a.item").forEach((el) => {
@ -272,7 +278,6 @@ export class RStackAppSwitcher extends HTMLElement {
if (url.origin !== window.location.origin) return;
} catch { return; }
e.preventDefault();
menu.classList.remove("open");
this.dispatchEvent(new CustomEvent("module-select", {
detail: { moduleId },
bubbles: true,
@ -280,8 +285,6 @@ export class RStackAppSwitcher extends HTMLElement {
}));
});
});
document.addEventListener("click", () => menu.classList.remove("open"));
}
#getSpaceSlug(): string {
@ -313,7 +316,7 @@ const STYLES = `
.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;
font-size: 0.6rem; font-weight: 900; color: var(--rs-text-inverse);
line-height: 1; flex-shrink: 0;
}
.trigger-badge.rstack-gradient {
@ -322,14 +325,42 @@ const STYLES = `
.caret { font-size: 0.7em; opacity: 0.6; }
.menu {
position: fixed; margin-top: 0;
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: 10001;
background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
/* ── Sidebar panel ── */
.sidebar {
position: fixed;
top: 56px; left: 0; bottom: 0;
width: 280px;
overflow-y: auto;
z-index: 10001;
background: var(--rs-bg-surface);
border-right: 1px solid var(--rs-border);
box-shadow: var(--rs-shadow-lg);
transform: translateX(-100%);
transition: transform 0.25s ease;
}
.sidebar.open {
transform: translateX(0);
}
/* Collapse button */
.collapse-btn {
position: absolute;
top: 8px; right: 8px;
width: 28px; height: 28px;
border-radius: 6px;
border: 1px solid var(--rs-border);
background: var(--rs-bg-surface);
color: var(--rs-text-secondary);
font-size: 1rem; line-height: 1;
cursor: pointer;
display: flex; align-items: center; justify-content: center;
transition: background 0.15s, color 0.15s;
z-index: 1;
}
.collapse-btn:hover {
background: var(--rs-bg-hover);
color: var(--rs-text-primary);
}
.menu.open { display: block; }
/* rStack header */
a.rstack-header {
@ -343,7 +374,7 @@ a.rstack-header:hover { background: var(--rs-bg-hover); }
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;
font-size: 0.7rem; font-weight: 900; color: var(--rs-text-inverse); line-height: 1;
flex-shrink: 0;
}
.rstack-info { display: flex; flex-direction: column; }
@ -388,7 +419,7 @@ a.rstack-header:hover { background: var(--rs-bg-hover); }
.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;
font-size: 0.6rem; font-weight: 900; color: var(--rs-text-inverse);
line-height: 1; flex-shrink: 0;
}
.item-icon { font-size: 1.3rem; width: 28px; text-align: center; flex-shrink: 0; }
@ -413,4 +444,9 @@ a.rstack-header:hover { background: var(--rs-bg-hover); }
border-top: 1px solid var(--rs-border-subtle);
margin-top: 4px; padding-top: 10px;
}
/* Mobile: sidebar overlays instead of pushing */
@media (max-width: 640px) {
.sidebar { box-shadow: 4px 0 20px rgba(0,0,0,0.3); }
}
`;

View File

@ -710,7 +710,7 @@ export class RStackIdentity extends HTMLElement {
if (emailStep === "input") {
body = `
<div class="account-section-body">
<p style="color:#94a3b8;font-size:0.85rem;margin:0 0 12px">Link an email for notifications and account recovery.</p>
<p style="color:var(--rs-text-secondary);font-size:0.85rem;margin:0 0 12px">Link an email for notifications and account recovery.</p>
<input class="input" id="acct-email" type="email" placeholder="you@example.com" />
<div class="actions">
<button class="btn btn--primary" data-action="send-code">Send Verification Code</button>
@ -720,7 +720,7 @@ export class RStackIdentity extends HTMLElement {
} else {
body = `
<div class="account-section-body">
<p style="color:#94a3b8;font-size:0.85rem;margin:0 0 12px">Enter the 6-digit code sent to <strong>${emailAddr.replace(/</g, "&lt;")}</strong></p>
<p style="color:var(--rs-text-secondary);font-size:0.85rem;margin:0 0 12px">Enter the 6-digit code sent to <strong>${emailAddr.replace(/</g, "&lt;")}</strong></p>
<input class="input" id="acct-code" type="text" placeholder="000000" maxlength="6" inputmode="numeric" />
<div class="actions">
<button class="btn btn--secondary" data-action="email-back">Back</button>
@ -745,7 +745,7 @@ export class RStackIdentity extends HTMLElement {
const done = acctStatus ? acctStatus.multiDevice : null;
const body = isOpen ? `
<div class="account-section-body">
<p style="color:#94a3b8;font-size:0.85rem;margin:0 0 12px">Register an additional passkey for backup access.${acctStatus ? ` <span style="color:#64748b">(${acctStatus.credentialCount} passkey${acctStatus.credentialCount !== 1 ? "s" : ""} registered)</span>` : ""}</p>
<p style="color:var(--rs-text-secondary);font-size:0.85rem;margin:0 0 12px">Register an additional passkey for backup access.${acctStatus ? ` <span style="color:var(--rs-text-muted)">(${acctStatus.credentialCount} passkey${acctStatus.credentialCount !== 1 ? "s" : ""} registered)</span>` : ""}</p>
<div class="actions actions--stack">
<button class="btn btn--primary" data-action="register-device">🔑 Register Passkey on This Device</button>
</div>
@ -767,7 +767,7 @@ export class RStackIdentity extends HTMLElement {
let body = "";
if (isOpen) {
if (guardiansLoading) {
body = `<div class="account-section-body"><div style="text-align:center;padding:1rem;color:#94a3b8"><span class="spinner"></span> Loading guardians...</div></div>`;
body = `<div class="account-section-body"><div style="text-align:center;padding:1rem;color:var(--rs-text-secondary)"><span class="spinner"></span> Loading guardians...</div></div>`;
} else {
const guardiansHTML = guardians.length > 0
? `<div class="contact-list">${guardians.map(g => `
@ -789,7 +789,7 @@ export class RStackIdentity extends HTMLElement {
body = `
<div class="account-section-body">
<p style="color:#94a3b8;font-size:0.85rem;margin:0 0 12px">Choose trusted contacts who can help recover your account.</p>
<p style="color:var(--rs-text-secondary);font-size:0.85rem;margin:0 0 12px">Choose trusted contacts who can help recover your account.</p>
${guardians.length < 3 ? `<div class="input-row">
<input class="input input--inline" id="acct-guardian-name" type="text" placeholder="Guardian name" />
<input class="input input--inline" id="acct-guardian-email" type="email" placeholder="Email (optional)" />
@ -817,14 +817,14 @@ export class RStackIdentity extends HTMLElement {
let body = "";
if (isOpen) {
if (addressesLoading) {
body = `<div class="account-section-body"><div style="text-align:center;padding:1rem;color:#94a3b8"><span class="spinner"></span> Loading addresses...</div></div>`;
body = `<div class="account-section-body"><div style="text-align:center;padding:1rem;color:var(--rs-text-secondary)"><span class="spinner"></span> Loading addresses...</div></div>`;
} else {
const listHTML = addresses.length > 0
? `<div class="contact-list">${addresses.map(a => `
<div class="contact-item">
<div style="display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;font-size:0.85rem">
<span>${a.street.replace(/</g, "&lt;")}</span>
<span style="color:#94a3b8">${a.city.replace(/</g, "&lt;")}, ${a.state.replace(/</g, "&lt;")} ${a.zip.replace(/</g, "&lt;")} ${a.country.replace(/</g, "&lt;")}</span>
<span style="color:var(--rs-text-secondary)">${a.city.replace(/</g, "&lt;")}, ${a.state.replace(/</g, "&lt;")} ${a.zip.replace(/</g, "&lt;")} ${a.country.replace(/</g, "&lt;")}</span>
</div>
<button class="contact-remove" data-remove-address="${a.id}">&times;</button>
</div>
@ -1231,7 +1231,7 @@ export class RStackIdentity extends HTMLElement {
: "";
const emptyState = !yourSpaces.length && !publicSpaces.length
? `<p style="color:#94a3b8;text-align:center;padding:1rem 0">No spaces yet. Create one to get started!</p>`
? `<p style="color:var(--rs-text-secondary);text-align:center;padding:1rem 0">No spaces yet. Create one to get started!</p>`
: "";
overlay.innerHTML = `
@ -1397,9 +1397,8 @@ const STYLES = `
min-width: 16px; height: 16px; border-radius: 8px;
background: #ef4444; color: white; font-size: 0.6rem; font-weight: 700;
display: flex; align-items: center; justify-content: center;
padding: 0 4px; border: 2px solid #1e293b; line-height: 1;
padding: 0 4px; border: 2px solid var(--rs-bg-surface); line-height: 1;
}
.notif-badge { border-color: var(--rs-bg-surface); }
/* Notification items in dropdown */
.dropdown-section-label {
@ -1411,7 +1410,7 @@ const STYLES = `
}
.notif-text { font-size: 0.8rem; line-height: 1.4; color: var(--rs-text-primary); }
.notif-msg {
font-size: 0.75rem; color: #94a3b8; font-style: italic;
font-size: 0.75rem; color: var(--rs-text-secondary); font-style: italic;
margin-top: 4px; overflow: hidden; text-overflow: ellipsis;
white-space: nowrap; max-width: 240px;
}
@ -1522,7 +1521,7 @@ const MODAL_STYLES = `
`;
const SETTINGS_STYLES = `
.info-text { margin-top: 1rem; font-size: 0.8rem; color: #475569; line-height: 1.5; }
.info-text { margin-top: 1rem; font-size: 0.8rem; color: var(--rs-text-muted); line-height: 1.5; }
.btn--success { background: #059669 !important; color: white; cursor: default; }
.btn--small { padding: 10px 16px; flex: none; }
.input-row { display: flex; gap: 8px; align-items: stretch; }
@ -1530,24 +1529,24 @@ const SETTINGS_STYLES = `
.contact-list { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; }
.contact-item {
display: flex; align-items: center; justify-content: space-between;
padding: 8px 12px; border-radius: 8px; background: rgba(255,255,255,0.05);
border: 1px solid rgba(255,255,255,0.1); font-size: 0.9rem; color: #e2e8f0;
padding: 8px 12px; border-radius: 8px; background: var(--rs-bg-hover);
border: 1px solid var(--rs-border); font-size: 0.9rem; color: var(--rs-text-primary);
}
.contact-remove {
background: none; border: none; color: #64748b; font-size: 1.2rem;
background: none; border: none; color: var(--rs-text-muted); font-size: 1.2rem;
cursor: pointer; padding: 2px 6px; border-radius: 4px; line-height: 1;
}
.contact-remove:hover { color: #ef4444; background: rgba(239,68,68,0.1); }
.threshold-row {
display: flex; align-items: center; gap: 8px; margin-top: 12px;
font-size: 0.85rem; color: #94a3b8;
font-size: 0.85rem; color: var(--rs-text-secondary);
}
.threshold-row label { white-space: nowrap; }
.threshold-row select {
padding: 6px 10px; border-radius: 6px; background: rgba(255,255,255,0.08);
border: 1px solid rgba(255,255,255,0.15); color: white; font-size: 0.85rem;
padding: 6px 10px; border-radius: 6px; background: var(--rs-btn-secondary-bg);
border: 1px solid var(--rs-border); color: var(--rs-text-primary); font-size: 0.85rem;
}
.threshold-hint { color: #64748b; font-size: 0.8rem; }
.threshold-hint { color: var(--rs-text-muted); font-size: 0.8rem; }
`;
const ACCOUNT_MODAL_STYLES = `
@ -1557,10 +1556,10 @@ const ACCOUNT_MODAL_STYLES = `
justify-content: center; z-index: 10000; animation: fadeIn 0.2s;
}
.account-modal {
background: #1e293b; border: 1px solid rgba(255,255,255,0.1);
background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
border-radius: 16px; padding: 2rem; max-width: 520px; width: 92%;
max-height: 85vh; overflow-y: auto; color: white; position: relative;
box-shadow: 0 20px 60px rgba(0,0,0,0.4); animation: slideUp 0.3s;
max-height: 85vh; overflow-y: auto; color: var(--rs-text-primary); position: relative;
box-shadow: var(--rs-shadow-lg); animation: slideUp 0.3s;
text-align: left;
}
.account-modal h2 {
@ -1569,29 +1568,29 @@ const ACCOUNT_MODAL_STYLES = `
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.account-section {
border: 1px solid rgba(255,255,255,0.08); border-radius: 10px;
border: 1px solid var(--rs-btn-secondary-bg); border-radius: 10px;
margin-bottom: 8px; overflow: hidden; transition: border-color 0.2s;
}
.account-section:hover { border-color: rgba(255,255,255,0.15); }
.account-section:hover { border-color: var(--rs-border); }
.account-section.open { border-color: rgba(6,182,212,0.3); }
.account-section-header {
display: flex; align-items: center; justify-content: space-between;
padding: 12px 16px; cursor: pointer; font-size: 0.9rem; font-weight: 500;
transition: background 0.15s; user-select: none;
}
.account-section-header:hover { background: rgba(255,255,255,0.04); }
.section-arrow { font-size: 0.7rem; color: #64748b; transition: transform 0.2s; }
.account-section-header:hover { background: var(--rs-bg-hover); }
.section-arrow { font-size: 0.7rem; color: var(--rs-text-muted); transition: transform 0.2s; }
.account-section-body {
padding: 0 16px 16px; animation: fadeIn 0.15s;
}
.account-section--inline {
border: 1px solid rgba(255,255,255,0.08); border-radius: 10px;
border: 1px solid var(--rs-btn-secondary-bg); border-radius: 10px;
margin-bottom: 8px; padding: 4px 0;
}
.account-section--inline .account-section-header { cursor: default; }
.account-section--inline .account-section-header:hover { background: none; }
.toggle-hint {
padding: 0 16px 10px; font-size: 0.75rem; color: #64748b; line-height: 1.4;
padding: 0 16px 10px; font-size: 0.75rem; color: var(--rs-text-muted); line-height: 1.4;
}
.guardian-piece { font-size: 1.1rem; flex-shrink: 0; }
.status-dot {
@ -1636,10 +1635,10 @@ const SPACES_STYLES = `
justify-content: center; z-index: 10000; animation: fadeIn 0.2s;
}
.spaces-modal {
background: #1e293b; border: 1px solid rgba(255,255,255,0.1);
background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
border-radius: 16px; padding: 2rem; max-width: 720px; width: 92%;
max-height: 85vh; overflow-y: auto; color: white; position: relative;
box-shadow: 0 20px 60px rgba(0,0,0,0.4); animation: slideUp 0.3s;
max-height: 85vh; overflow-y: auto; color: var(--rs-text-primary); position: relative;
box-shadow: var(--rs-shadow-lg); animation: slideUp 0.3s;
}
.spaces-modal h2 {
font-size: 1.5rem; margin-bottom: 0.5rem;
@ -1648,7 +1647,7 @@ const SPACES_STYLES = `
}
.spaces-section-label {
font-size: 0.75rem; font-weight: 600; text-transform: uppercase;
letter-spacing: 0.05em; color: #64748b; margin: 1rem 0 0.5rem;
letter-spacing: 0.05em; color: var(--rs-text-muted); margin: 1rem 0 0.5rem;
}
.spaces-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
@ -1657,13 +1656,13 @@ const SPACES_STYLES = `
.space-card {
display: flex; flex-direction: column; align-items: center; gap: 8px;
padding: 20px 12px; border-radius: 12px; cursor: pointer;
background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08);
transition: all 0.2s; text-align: center; color: white; font-family: inherit;
background: var(--rs-bg-hover); border: 1px solid var(--rs-btn-secondary-bg);
transition: all 0.2s; text-align: center; color: var(--rs-text-primary); font-family: inherit;
font-size: inherit;
}
.space-card:hover {
background: rgba(255,255,255,0.08); border-color: rgba(6,182,212,0.4);
transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.2);
background: var(--rs-btn-secondary-bg); border-color: rgba(6,182,212,0.4);
transform: translateY(-2px); box-shadow: var(--rs-shadow-md);
}
.space-card-initial {
width: 48px; height: 48px; border-radius: 50%;
@ -1679,7 +1678,7 @@ const SPACES_STYLES = `
display: flex; gap: 6px; flex-wrap: wrap; justify-content: center;
}
.space-vis {
font-size: 0.7rem; color: #94a3b8; background: rgba(255,255,255,0.06);
font-size: 0.7rem; color: var(--rs-text-secondary); background: var(--rs-bg-hover);
padding: 2px 8px; border-radius: 10px;
}
.space-vis.vis-public { background: rgba(52,211,153,0.15); color: #34d399; }
@ -1693,10 +1692,10 @@ const SPACES_STYLES = `
padding: 2px 8px; border-radius: 10px; font-weight: 600;
}
.space-card--create {
border-style: dashed; border-color: rgba(255,255,255,0.15);
border-style: dashed; border-color: var(--rs-border);
}
.space-card--create .space-card-initial {
background: rgba(255,255,255,0.08); font-size: 1.5rem;
background: var(--rs-btn-secondary-bg); font-size: 1.5rem;
}
.space-card--create:hover .space-card-initial {
background: linear-gradient(135deg, #06b6d4, #7c3aed);

View File

@ -359,7 +359,7 @@ const STYLES = `
}
.bell-btn:hover {
color: var(--rs-text-primary, #e2e8f0);
background: var(--rs-bg-hover, rgba(255,255,255,0.08));
background: var(--rs-bg-hover, rgba(255,255,255,0.05));
}
.badge {
@ -392,7 +392,7 @@ const STYLES = `
border-radius: 10px;
background: var(--rs-bg-surface, #1e293b);
border: 1px solid var(--rs-border, rgba(255,255,255,0.1));
box-shadow: 0 8px 30px rgba(0,0,0,0.3);
box-shadow: var(--rs-shadow-lg, 0 8px 30px rgba(0,0,0,0.3));
z-index: 200;
}
@ -421,7 +421,7 @@ const STYLES = `
transition: background 0.15s;
}
.mark-all-btn:hover {
background: var(--rs-bg-hover, rgba(255,255,255,0.08));
background: var(--rs-bg-hover, rgba(255,255,255,0.05));
}
.panel-empty {
@ -438,10 +438,10 @@ const STYLES = `
padding: 10px 16px;
cursor: pointer;
transition: background 0.15s;
border-bottom: 1px solid var(--rs-border, rgba(255,255,255,0.05));
border-bottom: 1px solid var(--rs-border, rgba(255,255,255,0.1));
}
.notif-item:hover {
background: var(--rs-bg-hover, rgba(255,255,255,0.06));
background: var(--rs-bg-hover, rgba(255,255,255,0.05));
}
.notif-item.unread {
background: rgba(6, 182, 212, 0.05);

View File

@ -457,7 +457,7 @@ const PANEL_CSS = `
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 9998;
z-index: 200000;
animation: fadeIn 0.2s ease;
}
@ -467,13 +467,13 @@ const PANEL_CSS = `
right: 0;
bottom: 0;
width: min(380px, 90vw);
background: #1e293b;
border-left: 1px solid rgba(255,255,255,0.1);
z-index: 9999;
background: var(--rs-bg-surface);
border-left: 1px solid var(--rs-border);
z-index: 200001;
display: flex;
flex-direction: column;
animation: slideIn 0.25s ease;
color: #e2e8f0;
color: var(--rs-text-primary);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
@ -491,7 +491,7 @@ const PANEL_CSS = `
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid rgba(255,255,255,0.08);
border-bottom: 1px solid var(--rs-btn-secondary-bg);
}
.panel-header h2 {
@ -507,14 +507,14 @@ const PANEL_CSS = `
.close-btn {
background: none;
border: none;
color: #64748b;
color: var(--rs-text-muted);
font-size: 1.5rem;
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
line-height: 1;
}
.close-btn:hover { color: #e2e8f0; background: rgba(255,255,255,0.08); }
.close-btn:hover { color: var(--rs-text-primary); background: var(--rs-btn-secondary-bg); }
.panel-content {
flex: 1;
@ -529,7 +529,7 @@ const PANEL_CSS = `
.section h3 {
font-size: 0.82rem;
font-weight: 700;
color: #94a3b8;
color: var(--rs-text-secondary);
text-transform: uppercase;
letter-spacing: 0.06em;
margin: 0 0 10px;
@ -537,7 +537,7 @@ const PANEL_CSS = `
.count {
font-weight: 400;
color: #64748b;
color: var(--rs-text-muted);
font-size: 0.75rem;
margin-left: 4px;
}
@ -557,7 +557,7 @@ const PANEL_CSS = `
border-radius: 8px;
transition: background 0.15s;
}
.member-row:hover { background: rgba(255,255,255,0.04); }
.member-row:hover { background: var(--rs-bg-hover); }
.member-avatar {
width: 32px;
@ -581,7 +581,7 @@ const PANEL_CSS = `
.member-name {
font-size: 0.85rem;
font-weight: 500;
color: #e2e8f0;
color: var(--rs-text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -610,9 +610,9 @@ const PANEL_CSS = `
}
.role-select {
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.1);
color: #e2e8f0;
background: var(--rs-border-subtle);
border: 1px solid var(--rs-border);
color: var(--rs-text-primary);
border-radius: 6px;
padding: 3px 6px;
font-size: 0.72rem;
@ -623,7 +623,7 @@ const PANEL_CSS = `
.remove-btn {
background: none;
border: none;
color: #64748b;
color: var(--rs-text-muted);
font-size: 1.1rem;
cursor: pointer;
padding: 2px 6px;
@ -642,9 +642,9 @@ const PANEL_CSS = `
.toggle-btn {
flex: 1;
padding: 6px 10px;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
color: #94a3b8;
background: var(--rs-bg-hover);
border: 1px solid var(--rs-btn-secondary-bg);
color: var(--rs-text-secondary);
border-radius: 6px;
font-size: 0.78rem;
cursor: pointer;
@ -663,9 +663,9 @@ const PANEL_CSS = `
}
.input {
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.1);
color: #e2e8f0;
background: var(--rs-border-subtle);
border: 1px solid var(--rs-border);
color: var(--rs-text-primary);
border-radius: 8px;
padding: 8px 12px;
font-size: 0.85rem;
@ -673,7 +673,7 @@ const PANEL_CSS = `
box-sizing: border-box;
}
.input:focus { outline: none; border-color: #14b8a6; }
.input::placeholder { color: #64748b; }
.input::placeholder { color: var(--rs-text-muted); }
.add-row {
display: flex;
@ -725,7 +725,7 @@ const PANEL_CSS = `
justify-content: space-between;
padding: 8px 10px;
border-radius: 8px;
background: rgba(255,255,255,0.02);
background: var(--rs-bg-hover);
}
.invite-info {
@ -736,7 +736,7 @@ const PANEL_CSS = `
.invite-email {
font-size: 0.82rem;
color: #e2e8f0;
color: var(--rs-text-primary);
}
.invite-role {
@ -746,7 +746,7 @@ const PANEL_CSS = `
.invite-expiry {
font-size: 0.65rem;
color: #64748b;
color: var(--rs-text-muted);
}
.revoke-btn {
@ -763,7 +763,7 @@ const PANEL_CSS = `
.empty-state {
font-size: 0.8rem;
color: #64748b;
color: var(--rs-text-muted);
padding: 12px 0;
text-align: center;
}

View File

@ -946,7 +946,7 @@ const STYLES = `
.menu {
position: fixed; margin-top: 0;
min-width: 260px; max-height: 400px; overflow-y: auto;
border-radius: 12px; box-shadow: 0 8px 30px rgba(0,0,0,0.25);
border-radius: 12px; box-shadow: var(--rs-shadow-lg);
display: none; z-index: 10001;
background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
}
@ -1075,7 +1075,7 @@ const STYLES = `
.divider { height: 1px; margin: 4px 0; background: var(--rs-border-subtle); }
.menu-loading, .menu-empty {
padding: 16px; text-align: center; font-size: 0.85rem; color: #94a3b8;
padding: 16px; text-align: center; font-size: 0.85rem; color: var(--rs-text-secondary);
}
/* Discover section — non-navigable items */
@ -1103,9 +1103,9 @@ const REQUEST_MODAL_STYLES = `
justify-content: center; z-index: 10000; animation: fadeIn 0.2s;
}
.auth-modal {
background: #1e293b; border: 1px solid rgba(255,255,255,0.1);
background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
border-radius: 16px; padding: 2rem; max-width: 420px; width: 90%;
text-align: center; color: white; box-shadow: 0 20px 60px rgba(0,0,0,0.4);
text-align: center; color: var(--rs-text-primary); box-shadow: var(--rs-shadow-lg);
animation: slideUp 0.3s; position: relative;
}
.auth-modal h2 {
@ -1113,15 +1113,15 @@ const REQUEST_MODAL_STYLES = `
background: linear-gradient(135deg, #06b6d4, #7c3aed);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.auth-modal p { color: #94a3b8; font-size: 0.95rem; line-height: 1.6; margin-bottom: 1rem; }
.auth-modal p { color: var(--rs-text-secondary); font-size: 0.95rem; line-height: 1.6; margin-bottom: 1rem; }
.input {
width: 100%; padding: 12px 16px; border-radius: 8px;
border: 1px solid rgba(255,255,255,0.15); background: rgba(255,255,255,0.05);
color: white; font-size: 0.9rem; margin-bottom: 1rem; outline: none;
border: 1px solid var(--rs-input-border); background: var(--rs-bg-hover);
color: var(--rs-text-primary); font-size: 0.9rem; margin-bottom: 1rem; outline: none;
transition: border-color 0.2s; box-sizing: border-box; font-family: inherit;
}
.input:focus { border-color: #06b6d4; }
.input::placeholder { color: #64748b; }
.input::placeholder { color: var(--rs-text-muted); }
.actions { display: flex; gap: 12px; margin-top: 0.5rem; }
.btn {
flex: 1; padding: 12px 20px; border-radius: 8px; border: none;
@ -1130,15 +1130,15 @@ const REQUEST_MODAL_STYLES = `
.btn--primary { background: linear-gradient(135deg, #06b6d4, #7c3aed); color: white; }
.btn--primary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(6,182,212,0.3); }
.btn--primary:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.btn--secondary { background: rgba(255,255,255,0.08); color: #94a3b8; border: 1px solid rgba(255,255,255,0.1); }
.btn--secondary:hover { background: rgba(255,255,255,0.12); color: white; }
.btn--secondary { background: var(--rs-btn-secondary-bg); color: var(--rs-text-secondary); border: 1px solid var(--rs-border); }
.btn--secondary:hover { background: var(--rs-bg-hover); color: var(--rs-text-primary); }
.error { color: #ef4444; font-size: 0.85rem; margin-top: 0.5rem; min-height: 1.2em; }
.close-btn {
position: absolute; top: 12px; right: 16px;
background: none; border: none; color: #64748b; font-size: 1.5rem;
background: none; border: none; color: var(--rs-text-muted); font-size: 1.5rem;
cursor: pointer; line-height: 1; padding: 4px 8px; border-radius: 6px;
}
.close-btn:hover { color: white; background: rgba(255,255,255,0.1); }
.close-btn:hover { color: var(--rs-text-primary); background: var(--rs-border); }
.spinner {
display: inline-block; width: 18px; height: 18px;
border: 2px solid transparent; border-top-color: currentColor;
@ -1157,9 +1157,9 @@ const EDIT_SPACE_MODAL_STYLES = `
justify-content: center; z-index: 10000; animation: fadeIn 0.2s;
}
.edit-modal {
background: #1e293b; border: 1px solid rgba(255,255,255,0.1);
background: var(--rs-bg-surface); border: 1px solid var(--rs-border);
border-radius: 16px; padding: 2rem; max-width: 520px; width: 92%;
color: white; box-shadow: 0 20px 60px rgba(0,0,0,0.4);
color: var(--rs-text-primary); box-shadow: var(--rs-shadow-lg);
animation: slideUp 0.3s; position: relative;
}
.edit-modal h2 {
@ -1169,30 +1169,30 @@ const EDIT_SPACE_MODAL_STYLES = `
}
.close-btn {
position: absolute; top: 12px; right: 16px;
background: none; border: none; color: #64748b; font-size: 1.5rem;
background: none; border: none; color: var(--rs-text-muted); font-size: 1.5rem;
cursor: pointer; line-height: 1; padding: 4px 8px; border-radius: 6px;
}
.close-btn:hover { color: white; background: rgba(255,255,255,0.1); }
.close-btn:hover { color: var(--rs-text-primary); background: var(--rs-border); }
/* Tabs */
.tabs { display: flex; gap: 4px; margin-bottom: 1.2rem; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 0; }
.tabs { display: flex; gap: 4px; margin-bottom: 1.2rem; border-bottom: 1px solid var(--rs-border); padding-bottom: 0; }
.tab {
padding: 8px 16px; border: none; background: none; color: #94a3b8;
padding: 8px 16px; border: none; background: none; color: var(--rs-text-secondary);
font-size: 0.85rem; font-weight: 600; cursor: pointer; border-bottom: 2px solid transparent;
transition: all 0.15s; border-radius: 6px 6px 0 0;
}
.tab:hover { color: #e2e8f0; background: rgba(255,255,255,0.05); }
.tab:hover { color: var(--rs-text-primary); background: var(--rs-bg-hover); }
.tab.active { color: #06b6d4; border-bottom-color: #06b6d4; }
.tab-panel { }
.tab-panel.hidden { display: none; }
/* Form fields */
.field-label { display: block; font-size: 0.75rem; font-weight: 600; color: #94a3b8; margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.05em; }
.field-label { display: block; font-size: 0.75rem; font-weight: 600; color: var(--rs-text-secondary); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.05em; }
.input {
width: 100%; padding: 10px 14px; border-radius: 8px;
border: 1px solid rgba(255,255,255,0.15); background: rgba(255,255,255,0.05);
color: white; font-size: 0.9rem; margin-bottom: 0.75rem; outline: none;
border: 1px solid var(--rs-input-border); background: var(--rs-bg-hover);
color: var(--rs-text-primary); font-size: 0.9rem; margin-bottom: 0.75rem; outline: none;
transition: border-color 0.2s; box-sizing: border-box; font-family: inherit;
}
.input:focus { border-color: #06b6d4; }
@ -1223,7 +1223,7 @@ select.input { appearance: auto; }
.member-list { max-height: 320px; overflow-y: auto; }
.member-item {
display: flex; align-items: center; gap: 10px;
padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.06);
padding: 8px 0; border-bottom: 1px solid var(--rs-border-subtle);
}
.member-item:last-child { border-bottom: none; }
.member-name { flex: 1; font-size: 0.875rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@ -1232,11 +1232,11 @@ select.input { appearance: auto; }
}
.role-owner { background: rgba(251,191,36,0.15); color: #fbbf24; }
.role-select {
padding: 4px 8px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.15);
background: rgba(255,255,255,0.05); color: white; font-size: 0.8rem; outline: none;
padding: 4px 8px; border-radius: 6px; border: 1px solid var(--rs-input-border);
background: var(--rs-bg-hover); color: var(--rs-text-primary); font-size: 0.8rem; outline: none;
}
.member-remove {
background: none; border: none; color: #64748b; font-size: 1.2rem;
background: none; border: none; color: var(--rs-text-muted); font-size: 1.2rem;
cursor: pointer; padding: 2px 6px; border-radius: 4px; line-height: 1;
}
.member-remove:hover { color: #ef4444; background: rgba(239,68,68,0.1); }
@ -1246,12 +1246,12 @@ select.input { appearance: auto; }
.invitation-list { max-height: 320px; overflow-y: auto; }
.invite-item {
display: flex; align-items: center; gap: 10px;
padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.06);
padding: 8px 0; border-bottom: 1px solid var(--rs-border-subtle);
}
.invite-item:last-child { border-bottom: none; }
.invite-info { flex: 1; min-width: 0; }
.invite-name { font-size: 0.875rem; font-weight: 500; display: block; }
.invite-msg { font-size: 0.8rem; color: #64748b; display: block; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.invite-msg { font-size: 0.8rem; color: var(--rs-text-muted); display: block; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.invite-actions { display: flex; gap: 6px; flex-shrink: 0; }
.btn-sm {
padding: 4px 10px; border-radius: 6px; border: none;
@ -1266,27 +1266,27 @@ select.input { appearance: auto; }
.invite-status--approved { background: rgba(52,211,153,0.15); color: #34d399; }
.invite-status--denied { background: rgba(239,68,68,0.15); color: #f87171; }
.loading { padding: 16px; text-align: center; font-size: 0.85rem; color: #94a3b8; }
.loading { padding: 16px; text-align: center; font-size: 0.85rem; color: var(--rs-text-secondary); }
/* Modules config panel */
.modules-list { max-height: 400px; overflow-y: auto; }
.module-encryption { margin-bottom: 0.25rem; }
.module-divider { height: 1px; background: rgba(255,255,255,0.08); margin: 0.75rem 0; }
.module-section-label { font-size: 0.72rem; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 0.5rem; }
.module-divider { height: 1px; background: var(--rs-btn-secondary-bg); margin: 0.75rem 0; }
.module-section-label { font-size: 0.72rem; font-weight: 700; color: var(--rs-text-secondary); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 0.5rem; }
.module-row {
display: flex; align-items: center; justify-content: space-between;
padding: 6px 0; cursor: pointer;
}
.module-label { font-size: 0.875rem; }
.module-hint { font-size: 0.75rem; color: #64748b; margin-top: 2px; padding-left: 2px; }
.module-hint { font-size: 0.75rem; color: var(--rs-text-muted); margin-top: 2px; padding-left: 2px; }
.scope-row {
display: flex; align-items: center; gap: 8px;
padding: 2px 0 6px 24px; font-size: 0.8rem; color: #94a3b8;
padding: 2px 0 6px 24px; font-size: 0.8rem; color: var(--rs-text-secondary);
}
.scope-label { font-size: 0.75rem; }
.scope-select {
padding: 3px 8px; border-radius: 6px; border: 1px solid rgba(255,255,255,0.12);
background: rgba(255,255,255,0.05); color: white; font-size: 0.75rem; outline: none;
padding: 3px 8px; border-radius: 6px; border: 1px solid var(--rs-border);
background: var(--rs-bg-hover); color: var(--rs-text-primary); font-size: 0.75rem; outline: none;
}
.toggle-switch {
position: relative; display: inline-block; width: 36px; height: 20px;
@ -1294,7 +1294,7 @@ select.input { appearance: auto; }
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
position: absolute; cursor: pointer; inset: 0;
background: rgba(255,255,255,0.15); border-radius: 20px; transition: 0.2s;
background: var(--rs-input-border); border-radius: 20px; transition: 0.2s;
}
.toggle-slider:before {
content: ""; position: absolute; height: 14px; width: 14px;

View File

@ -293,8 +293,10 @@ export class RStackTabBar extends HTMLElement {
<div class="tab-bar" data-view="${this.#viewMode}">
<div class="tabs-scroll">
${this.#layers.map(l => this.#renderTab(l, active)).join("")}
<button class="tab-add" id="add-btn" title="Add layer">+</button>
${this.#addMenuOpen ? this.#renderAddMenu() : ""}
<div class="tab-add-wrap">
<button class="tab-add" id="add-btn" title="Add layer">+</button>
${this.#addMenuOpen ? this.#renderAddMenu() : ""}
</div>
</div>
<div class="tab-actions">
<button class="view-toggle ${this.#viewMode === "stack" ? "active" : ""}"
@ -1256,7 +1258,7 @@ const STYLES = `
border-radius: 4px;
font-size: 0.55rem;
font-weight: 900;
color: #0f172a;
color: var(--rs-text-inverse);
line-height: 1;
flex-shrink: 0;
}
@ -1292,6 +1294,13 @@ const STYLES = `
/* ── Add button ── */
.tab-add-wrap {
position: relative;
flex-shrink: 0;
margin-left: 4px;
z-index: 1000;
}
.tab-add {
display: flex;
align-items: center;
@ -1300,19 +1309,16 @@ const STYLES = `
height: 28px;
min-width: 44px;
min-height: 44px;
border: 1px dashed rgba(148,163,184,0.3);
border: 1px dashed var(--rs-border);
border-radius: 5px;
background: transparent;
color: #64748b;
color: var(--rs-text-muted);
font-size: 1rem;
cursor: pointer;
transition: border-color 0.15s, color 0.15s, background 0.15s;
flex-shrink: 0;
margin-left: 4px;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
position: relative;
z-index: 2;
}
.tab-add:hover, .tab-add:active {
border-color: #22d3ee;
@ -1325,16 +1331,16 @@ const STYLES = `
.add-menu {
position: absolute;
top: 100%;
left: auto;
right: 0;
left: 0;
right: auto;
margin-top: 4px;
min-width: 260px;
max-height: 400px;
overflow-y: auto;
border-radius: 10px;
padding: 4px;
z-index: 100;
box-shadow: 0 8px 30px rgba(0,0,0,0.25);
z-index: 1000;
box-shadow: var(--rs-shadow-lg);
scrollbar-width: thin;
background: var(--rs-bg-surface);
border: 1px solid var(--rs-border);
@ -1350,7 +1356,7 @@ const STYLES = `
user-select: none;
}
.add-menu-category:not(:first-child) {
border-top: 1px solid rgba(128,128,128,0.12);
border-top: 1px solid var(--rs-border-subtle);
margin-top: 2px;
padding-top: 8px;
}
@ -1384,7 +1390,7 @@ const STYLES = `
border-radius: 5px;
font-size: 0.55rem;
font-weight: 900;
color: #0f172a;
color: var(--rs-text-inverse);
flex-shrink: 0;
}
@ -1452,7 +1458,7 @@ const STYLES = `
border: none;
border-radius: 6px;
background: transparent;
color: #64748b;
color: var(--rs-text-muted);
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
@ -1550,7 +1556,7 @@ const STYLES = `
border-radius: 5px;
font-size: 0.55rem;
font-weight: 900;
color: #0f172a;
color: var(--rs-text-inverse);
flex-shrink: 0;
}
@ -1606,7 +1612,7 @@ const STYLES = `
.io-chip--in {
background: transparent;
border: 1px dashed color-mix(in srgb, var(--chip-color) 35%, transparent);
color: color-mix(in srgb, var(--chip-color) 70%, #e2e8f0);
color: color-mix(in srgb, var(--chip-color) 70%, var(--rs-text-primary));
}
.io-chip--contained {
@ -1826,7 +1832,7 @@ const STYLES = `
border-radius: 12px;
padding: 16px;
z-index: 50;
box-shadow: 0 12px 40px rgba(0,0,0,0.4);
box-shadow: var(--rs-shadow-xl);
background: var(--rs-bg-surface);
border: 1px solid var(--rs-border);
color: var(--rs-text-primary);
@ -1854,7 +1860,7 @@ const STYLES = `
border-radius: 5px;
font-size: 0.6rem;
font-weight: 900;
color: #0f172a;
color: var(--rs-text-inverse);
}
.flow-dialog-arrow {
@ -1895,7 +1901,7 @@ const STYLES = `
cursor: pointer;
transition: background 0.12s, border-color 0.12s;
}
.flow-kind-btn:hover { background: rgba(255,255,255,0.05); }
.flow-kind-btn:hover { background: var(--rs-bg-hover); }
.flow-kind-btn.selected {
border-color: var(--kind-color);
background: color-mix(in srgb, var(--kind-color) 10%, transparent);
@ -1912,7 +1918,7 @@ const STYLES = `
font-size: 0.55rem;
font-weight: 700;
background: var(--kind-color);
color: #0f172a;
color: var(--rs-text-inverse);
width: 16px;
height: 16px;
border-radius: 50%;
@ -1962,7 +1968,7 @@ const STYLES = `
}
.flow-dialog-cancel {
border: 1px solid rgba(255,255,255,0.1);
border: 1px solid var(--rs-border);
background: transparent;
color: inherit;
opacity: 0.6;
@ -1972,7 +1978,7 @@ const STYLES = `
.flow-dialog-create {
border: none;
background: #22d3ee;
color: #0f172a;
color: var(--rs-text-inverse);
}
.flow-dialog-create:hover { opacity: 0.85; }

View File

@ -289,6 +289,26 @@ body {
opacity: 0.88;
}
/* ── Sidebar push offsets ── */
.rstack-tab-row,
#app,
.rspace-iframe-wrap {
transition: margin-left 0.25s ease, left 0.25s ease;
}
body.rstack-sidebar-open .rstack-tab-row {
left: 280px;
}
body.rstack-sidebar-open #app {
margin-left: 280px;
}
body.rstack-sidebar-open .rspace-iframe-wrap {
left: 280px;
}
/* ── Mobile adjustments ── */
@media (max-width: 640px) {
@ -351,6 +371,10 @@ body {
flex-wrap: wrap;
gap: 6px;
}
/* Sidebar overlays on mobile — no push offsets */
body.rstack-sidebar-open .rstack-tab-row { left: 0; }
body.rstack-sidebar-open #app { margin-left: 0; }
body.rstack-sidebar-open .rspace-iframe-wrap { left: 0; }
}
@media (max-width: 480px) {