Add Relays group (chat/file/message) + faded homepage dividers

New collapsible 'Relays' group in the sidebar (chat/file/message relay) that
expands to show its items and persists open state. Homepage gets a matching
Relays card group, and areas are now split by faded separator bars like the
sidebar. Relay pages are placeholder 'coming soon' stubs (/relays/{kind}).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jon
2026-06-05 18:38:27 +01:00
parent 3f9683e07f
commit 5a5134f9b2
4 changed files with 89 additions and 1 deletions

View File

@@ -144,6 +144,20 @@ async def bots_page(request: Request):
}) })
RELAY_KINDS = {"chat": "Chat relay", "file": "File relay", "message": "Message relay"}
@app.get("/relays/{kind}", response_class=HTMLResponse)
async def relay_page(request: Request, kind: str):
if redir := _redirect_if_unauth(request):
return redir
if kind not in RELAY_KINDS:
raise HTTPException(status_code=404, detail="Unknown relay")
return TEMPLATES.TemplateResponse(request, "relay.html", {
"nav_active": "relays", "kind": kind, "title": RELAY_KINDS[kind],
})
@app.get("/settings", response_class=HTMLResponse) @app.get("/settings", response_class=HTMLResponse)
async def settings_page(request: Request): async def settings_page(request: Request):
if redir := _redirect_if_unauth(request): if redir := _redirect_if_unauth(request):

View File

@@ -129,6 +129,23 @@
.side-nav .ico { width: 20px; text-align: center; font-size: 16px; flex-shrink: 0; } .side-nav .ico { width: 20px; text-align: center; font-size: 16px; flex-shrink: 0; }
.side-nav a.nav-sep { margin-top: 10px; padding-top: 17px; border-top: 1px solid var(--border); } .side-nav a.nav-sep { margin-top: 10px; padding-top: 17px; border-top: 1px solid var(--border); }
/* collapsible nav group (e.g. Relays) */
.nav-group-toggle {
display: flex; align-items: center; gap: 12px; width: 100%;
padding: 11px 18px; background: none; border: none; border-left: 3px solid transparent;
color: var(--muted); font-family: inherit; font-size: 14px; font-weight: 600;
white-space: nowrap; overflow: hidden; cursor: pointer; text-align: left;
}
.nav-group-toggle:hover { color: var(--text); background: var(--bg); }
.nav-group-toggle .ico { width: 20px; text-align: center; font-size: 16px; flex-shrink: 0; }
.nav-group-toggle .caret { margin-left: auto; font-size: 10px; transition: transform 0.15s; }
.nav-group.open .nav-group-toggle .caret { transform: rotate(90deg); }
.nav-sub { display: none; }
.nav-group.open .nav-sub { display: block; }
.nav-sub a { padding-left: 44px; font-size: 13px; }
html.collapsed .nav-group-toggle .caret { display: none; }
html.collapsed .nav-sub { display: none !important; }
.side-foot { margin-top: auto; padding: 8px 0; border-top: 1px solid var(--border); } .side-foot { margin-top: auto; padding: 8px 0; border-top: 1px solid var(--border); }
.side-status { padding: 10px 18px 12px; font-size: 12px; color: var(--muted); .side-status { padding: 10px 18px 12px; font-size: 12px; color: var(--muted);
@@ -274,6 +291,16 @@
<a href="/users" {% if nav_active == 'users' %}class="active"{% endif %}><span class="ico">👤</span><span class="lbl">Users</span></a> <a href="/users" {% if nav_active == 'users' %}class="active"{% endif %}><span class="ico">👤</span><span class="lbl">Users</span></a>
<a href="/businesses" {% if nav_active == 'businesses' %}class="active"{% endif %}><span class="ico">💼</span><span class="lbl">Businesses</span></a> <a href="/businesses" {% if nav_active == 'businesses' %}class="active"{% endif %}><span class="ico">💼</span><span class="lbl">Businesses</span></a>
<a href="/bots" {% if nav_active == 'bots' %}class="active"{% endif %}><span class="ico">🤖</span><span class="lbl">Bots</span></a> <a href="/bots" {% if nav_active == 'bots' %}class="active"{% endif %}><span class="ico">🤖</span><span class="lbl">Bots</span></a>
<div class="nav-group {% if nav_active == 'relays' %}open{% endif %}" id="nav-relays">
<button class="nav-group-toggle" onclick="toggleNavGroup('relays')" aria-label="Relays">
<span class="ico">🔀</span><span class="lbl">Relays</span><span class="caret"></span>
</button>
<div class="nav-sub">
<a href="/relays/chat" {% if nav_active == 'relays' and kind == 'chat' %}class="active"{% endif %}><span class="ico">💬</span><span class="lbl">Chat relay</span></a>
<a href="/relays/file" {% if nav_active == 'relays' and kind == 'file' %}class="active"{% endif %}><span class="ico">📁</span><span class="lbl">File relay</span></a>
<a href="/relays/message" {% if nav_active == 'relays' and kind == 'message' %}class="active"{% endif %}><span class="ico">✉️</span><span class="lbl">Message relay</span></a>
</div>
</div>
<a href="https://simplex.chat/file/" target="_blank" rel="noopener"><span class="ico">📁</span><span class="lbl">File upload</span></a> <a href="https://simplex.chat/file/" target="_blank" rel="noopener"><span class="ico">📁</span><span class="lbl">File upload</span></a>
<a href="/notifications" class="nav-sep {% if nav_active == 'notifications' %}active{% endif %}"><span class="ico">🔔</span><span class="lbl">Notifications</span><span class="notif-badge" id="notif-badge" style="display:none;"></span></a> <a href="/notifications" class="nav-sep {% if nav_active == 'notifications' %}active{% endif %}"><span class="ico">🔔</span><span class="lbl">Notifications</span><span class="notif-badge" id="notif-badge" style="display:none;"></span></a>
<a href="/settings" class="nav-sep {% if nav_active == 'settings' %}active{% endif %}"><span class="ico">⚙️</span><span class="lbl">Settings</span></a> <a href="/settings" class="nav-sep {% if nav_active == 'settings' %}active{% endif %}"><span class="ico">⚙️</span><span class="lbl">Settings</span></a>
@@ -324,6 +351,18 @@
if (ico && document.documentElement.classList.contains('collapsed')) ico.textContent = ''; if (ico && document.documentElement.classList.contains('collapsed')) ico.textContent = '';
})(); })();
// Collapsible sidebar groups (e.g. Relays) — persist open state
function toggleNavGroup(id) {
const g = document.getElementById('nav-' + id);
if (!g) return;
const open = g.classList.toggle('open');
localStorage.setItem('navgroup-' + id, open ? '1' : '');
}
(function(){
const g = document.getElementById('nav-relays');
if (g && localStorage.getItem('navgroup-relays')) g.classList.add('open');
})();
// Poll for unread notifications and update the sidebar badge // Poll for unread notifications and update the sidebar badge
async function pollNotifications() { async function pollNotifications() {
try { try {

View File

@@ -8,7 +8,8 @@
.home-head p { color: var(--muted); font-size: 14px; } .home-head p { color: var(--muted); font-size: 14px; }
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; } .tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.tiles + .tiles { margin-top: 32px; } /* vertical buffer between areas */ /* faded bar between areas, like the sidebar separators */
.tiles + .tiles { margin-top: 28px; padding-top: 28px; border-top: 1px solid var(--border); }
.tile { .tile {
display: flex; flex-direction: column; gap: 8px; display: flex; flex-direction: column; gap: 8px;
background: var(--card); border: 1px solid var(--border); border-radius: 12px; background: var(--card); border: 1px solid var(--border); border-radius: 12px;
@@ -58,6 +59,25 @@
</a> </a>
</div> </div>
<!-- Area: relays -->
<div class="tiles">
<a class="tile" href="/relays/chat">
<span class="t-ico">💬</span>
<span class="t-title">Chat relay</span>
<span class="t-desc">Relay chat messages between connections.</span>
</a>
<a class="tile" href="/relays/file">
<span class="t-ico">📁</span>
<span class="t-title">File relay</span>
<span class="t-desc">Relay files between connections.</span>
</a>
<a class="tile" href="/relays/message">
<span class="t-ico">✉️</span>
<span class="t-title">Message relay</span>
<span class="t-desc">Relay messages between connections.</span>
</a>
</div>
<!-- Area 2: manage --> <!-- Area 2: manage -->
<div class="tiles"> <div class="tiles">
<a class="tile" href="/network"> <a class="tile" href="/network">

View File

@@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block title %}{{ title }} — SimpleX Manager{% endblock %}
{% block content %}
<div class="flex-between" style="margin-bottom:24px;">
<h1 style="margin:0;">{{ title }}</h1>
<a href="/" class="muted" style="text-decoration:none;">← Home</a>
</div>
<div class="card" style="text-align:center;padding:48px 24px;">
<div style="font-size:40px;line-height:1;margin-bottom:12px;">🔀</div>
<strong>{{ title }} — coming soon</strong>
<p class="muted" style="margin-top:8px;">This relay isnt implemented yet.</p>
</div>
{% endblock %}