Add chat actions, channels/groups mgmt, directory + deadmans bots, network status
Profiles/bots (profiles.py, main.py): - Surface real send errors; fix group send and member-count staleness (refresh on view) - Group/channel actions: join, leave, owner delete; consistent member counts - Contacts: always-visible Chat, plus Clear chat and Delete contact - Support bot: OpenAI-compatible LLM backend (Grok/Ollama/OpenAI) per-bot config - Deadmans bot: check-in window, trigger message, recipients, owner - Directory bot: add-to-group registration, super-user /approve /reject /list, search, publishes listing.json in the website schema (directory.py registry module) - Profile edit (name/bio/avatar) + avatars on list pages - Global status + /network page (operators, SMP/XFTP servers) and Settings network info UI (templates): - Chat rooms; collapsible left sidebar with notifications + network widget - Per-directory-bot website generated on creation (name substituted) - Matrix theme; copy/hyperlink addresses; site footer Ignore runtime bot state and generated directory sites. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -107,17 +107,39 @@
|
||||
|
||||
.side-nav { display: flex; flex-direction: column; padding: 8px 0; }
|
||||
.side-nav a {
|
||||
position: relative;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 11px 18px; color: var(--muted); text-decoration: none;
|
||||
font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.notif-badge {
|
||||
margin-left: auto; min-width: 18px; height: 18px; padding: 0 5px;
|
||||
border-radius: 9px; background: var(--red); color: #fff;
|
||||
font-size: 11px; font-weight: 700;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
}
|
||||
html.collapsed .notif-badge {
|
||||
position: absolute; top: 5px; right: 8px; margin: 0;
|
||||
min-width: 16px; height: 16px; font-size: 10px; padding: 0 4px;
|
||||
}
|
||||
.side-nav a:hover { color: var(--text); background: var(--bg); }
|
||||
.side-nav a.active { color: var(--accent); border-left-color: var(--accent); }
|
||||
.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-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);
|
||||
border-bottom: 1px solid var(--border); }
|
||||
.side-status .ss-title { font-weight: 700; text-transform: uppercase; font-size: 10px;
|
||||
letter-spacing: 0.5px; margin-bottom: 7px; opacity: 0.7; }
|
||||
.side-status .ss-row { display: flex; align-items: center; gap: 6px; margin-top: 3px;
|
||||
white-space: nowrap; overflow: hidden; }
|
||||
.ss-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex-shrink: 0; }
|
||||
.ss-dot.online { background: var(--green); box-shadow: 0 0 5px var(--green); }
|
||||
.ss-dot.offline { background: var(--red); }
|
||||
html.collapsed .side-status { display: none; }
|
||||
.collapse-btn {
|
||||
display: flex; align-items: center; gap: 12px; width: 100%;
|
||||
padding: 11px 18px; background: none; border: none; cursor: pointer;
|
||||
@@ -246,9 +268,17 @@
|
||||
<a href="/users" {% if nav_active == 'users' %}class="active"{% endif %}><span class="ico">👤</span><span class="lbl">Users</span></a>
|
||||
<a href="/bots" {% if nav_active == 'bots' %}class="active"{% endif %}><span class="ico">🤖</span><span class="lbl">Bots</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="/settings" class="nav-sep {% if nav_active == 'settings' %}active{% endif %}"><span class="ico">⚙️</span><span class="lbl">Settings</span></a>
|
||||
</nav>
|
||||
<div class="side-foot">
|
||||
<a href="/network" class="side-status" id="side-status" title="View SimpleX network & servers"
|
||||
style="display:block;text-decoration:none;{% if nav_active == 'network' %}background:var(--bg);{% endif %}">
|
||||
<div class="ss-title">Network ›</div>
|
||||
<div class="ss-row"><span class="ss-dot" id="ss-dot"></span><span id="ss-running">–/–</span> running</div>
|
||||
<div class="ss-row" id="ss-servers">📡 –</div>
|
||||
<div class="ss-row" id="ss-ops" style="opacity:0.8;"></div>
|
||||
</a>
|
||||
<button class="collapse-btn" onclick="toggleCollapse()" title="Collapse sidebar" aria-label="Collapse sidebar">
|
||||
<span class="ico" id="collapse-ico">‹</span>
|
||||
</button>
|
||||
@@ -285,6 +315,41 @@
|
||||
const ico = document.getElementById('collapse-ico');
|
||||
if (ico && document.documentElement.classList.contains('collapsed')) ico.textContent = '›';
|
||||
})();
|
||||
|
||||
// Poll for unread notifications and update the sidebar badge
|
||||
async function pollNotifications() {
|
||||
try {
|
||||
const t = document.cookie.match(/(?:^|;\s*)token=([^;]+)/)?.[1] || '';
|
||||
const r = await fetch('/api/notifications', { headers: { 'X-Token': t } });
|
||||
if (!r.ok) return;
|
||||
const d = await r.json();
|
||||
const b = document.getElementById('notif-badge');
|
||||
if (!b) return;
|
||||
if (d.unread > 0) { b.textContent = d.unread > 99 ? '99+' : d.unread; b.style.display = 'inline-flex'; }
|
||||
else { b.style.display = 'none'; }
|
||||
} catch (e) {}
|
||||
}
|
||||
pollNotifications();
|
||||
setInterval(pollNotifications, 5000);
|
||||
|
||||
// Poll global SimpleX/network status for the sidebar widget
|
||||
async function pollStatus() {
|
||||
try {
|
||||
const t = document.cookie.match(/(?:^|;\s*)token=([^;]+)/)?.[1] || '';
|
||||
const r = await fetch('/api/status', { headers: { 'X-Token': t } });
|
||||
if (!r.ok) return;
|
||||
const d = await r.json();
|
||||
document.getElementById('ss-running').textContent = d.profiles_running + '/' + d.profiles_total;
|
||||
const dot = document.getElementById('ss-dot');
|
||||
dot.className = 'ss-dot ' + (d.online ? 'online' : 'offline');
|
||||
document.getElementById('ss-servers').textContent =
|
||||
d.online ? `📡 ${d.smp_servers} SMP · ${d.xftp_servers} XFTP` : 'no profile running';
|
||||
document.getElementById('ss-ops').textContent =
|
||||
(d.operators && d.operators.length) ? d.operators.join(', ') : '';
|
||||
} catch (e) {}
|
||||
}
|
||||
pollStatus();
|
||||
setInterval(pollStatus, 15000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user