diff --git a/app/sovran_systemsos_web/static/app.js b/app/sovran_systemsos_web/static/app.js
index 47e2980..a2ab79b 100644
--- a/app/sovran_systemsos_web/static/app.js
+++ b/app/sovran_systemsos_web/static/app.js
@@ -15,8 +15,6 @@ const CATEGORY_ORDER = [
"communication",
"apps",
"nostr",
- "support",
- "feature-manager",
];
const FEATURE_SUBCATEGORY_LABELS = {
@@ -59,6 +57,8 @@ let _rebuildIsEnabling = true;
// ── DOM refs ──────────────────────────────────────────────────────
const $tilesArea = document.getElementById("tiles-area");
+const $sidebarSupport = document.getElementById("sidebar-support");
+const $sidebarFeatures = document.getElementById("sidebar-features");
const $updateBtn = document.getElementById("btn-update");
const $updateBadge = document.getElementById("update-badge");
const $refreshBtn = document.getElementById("btn-refresh");
@@ -172,11 +172,19 @@ async function apiFetch(path, options) {
function buildTiles(services, categoryLabels) {
_servicesCache = services;
var grouped = {};
+ var supportServices = [];
for (var i = 0; i < services.length; i++) {
- var cat = services[i].category || "other";
+ var svc = services[i];
+ // Support tiles go to the sidebar, not the main grid
+ if (svc.category === "support" || svc.type === "support") {
+ supportServices.push(svc);
+ continue;
+ }
+ var cat = svc.category || "other";
if (!grouped[cat]) grouped[cat] = [];
- grouped[cat].push(services[i]);
+ grouped[cat].push(svc);
}
+ renderSidebarSupport(supportServices);
$tilesArea.innerHTML = "";
var orderedKeys = CATEGORY_ORDER.filter(function(k) { return grouped[k]; });
Object.keys(grouped).forEach(function(k) {
@@ -202,6 +210,28 @@ function buildTiles(services, categoryLabels) {
}
}
+function renderSidebarSupport(supportServices) {
+ $sidebarSupport.innerHTML = "";
+ for (var i = 0; i < supportServices.length; i++) {
+ var svc = supportServices[i];
+ var btn = document.createElement("button");
+ btn.className = "sidebar-support-btn";
+ btn.innerHTML =
+ '' +
+ '';
+ btn.addEventListener("click", function() { openSupportModal(); });
+ $sidebarSupport.appendChild(btn);
+ }
+ if (supportServices.length > 0) {
+ var hr = document.createElement("hr");
+ hr.className = "sidebar-divider";
+ $sidebarSupport.appendChild(hr);
+ }
+}
+
function buildTile(svc) {
var isSupport = svc.type === "support";
var sc = statusClass(svc.status);
@@ -1194,7 +1224,7 @@ async function loadFeatureManager() {
function renderFeatureManager(data) {
// Remove old feature manager section if it exists
- var old = $tilesArea.querySelector(".feature-manager-section");
+ var old = $sidebarFeatures.querySelector(".feature-manager-section");
if (old) old.parentNode.removeChild(old);
var section = document.createElement("div");
@@ -1236,7 +1266,7 @@ function renderFeatureManager(data) {
section.appendChild(subcat);
}
- $tilesArea.appendChild(section);
+ $sidebarFeatures.appendChild(section);
}
function buildFeatureCard(feat) {
diff --git a/app/sovran_systemsos_web/static/style.css b/app/sovran_systemsos_web/static/style.css
index 259fdad..be58928 100644
--- a/app/sovran_systemsos_web/static/style.css
+++ b/app/sovran_systemsos_web/static/style.css
@@ -37,6 +37,9 @@ body {
color: var(--text-primary);
line-height: 1.5;
min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
}
/* ── Header bar ─────────────────────────────────────────────────── */
@@ -189,9 +192,87 @@ button:disabled {
/* ── Main content ───────────────────────────────────────────────── */
.main-content {
- max-width: 980px;
- margin: 0 auto;
- padding: 24px 16px 48px;
+ display: flex;
+ align-items: flex-start;
+ flex: 1;
+ overflow: hidden;
+}
+
+/* ── Sidebar ────────────────────────────────────────────────────── */
+
+.sidebar {
+ width: 270px;
+ flex-shrink: 0;
+ height: 100%;
+ overflow-y: auto;
+ border-right: 1px solid var(--border-color);
+ background-color: var(--surface-color);
+ padding: 20px 14px;
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+/* ── Sidebar: Tech Support button ───────────────────────────────── */
+
+.sidebar-support-btn {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ width: 100%;
+ background-color: var(--card-color);
+ border: 2px dashed var(--accent-color);
+ border-radius: 12px;
+ padding: 12px 14px;
+ color: var(--text-primary);
+ cursor: pointer;
+ transition: border-style 0.15s, border-color 0.15s, background-color 0.15s;
+ text-align: left;
+}
+
+.sidebar-support-btn:hover {
+ border-style: solid;
+ border-color: #a8c8ff;
+ background-color: #35354a;
+}
+
+.sidebar-support-icon {
+ font-size: 1.5rem;
+ flex-shrink: 0;
+}
+
+.sidebar-support-text {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.sidebar-support-title {
+ font-size: 0.88rem;
+ font-weight: 700;
+ color: var(--text-primary);
+}
+
+.sidebar-support-hint {
+ font-size: 0.72rem;
+ color: var(--accent-color);
+ font-weight: 600;
+}
+
+.sidebar-divider {
+ border: none;
+ border-top: 1px solid var(--border-color);
+ margin: 16px 0;
+}
+
+/* ── Tiles area ─────────────────────────────────────────────────── */
+
+#tiles-area {
+ flex: 1;
+ height: 100%;
+ overflow-y: auto;
+ padding: 24px 20px 48px;
+ min-width: 0;
}
/* ── Category sections ──────────────────────────────────────────── */
@@ -859,6 +940,28 @@ button.btn-reboot:hover:not(:disabled) {
/* ── Responsive ─────────────────────────────────────────────────── */
+@media (max-width: 768px) {
+ body {
+ overflow: auto;
+ }
+ .main-content {
+ flex-direction: column;
+ overflow: visible;
+ }
+ .sidebar {
+ width: 100%;
+ height: auto;
+ border-right: none;
+ border-bottom: 1px solid var(--border-color);
+ padding: 14px 12px;
+ }
+ #tiles-area {
+ height: auto;
+ overflow-y: visible;
+ padding: 16px 12px 40px;
+ }
+}
+
@media (max-width: 600px) {
.header-bar {
padding: 10px 14px;
@@ -872,9 +975,6 @@ button.btn-reboot:hover:not(:disabled) {
flex-wrap: wrap;
padding: 8px 14px;
}
- .main-content {
- padding: 16px 12px 40px;
- }
.tiles-grid {
justify-content: center;
}
@@ -1444,3 +1544,29 @@ button.btn-reboot:hover:not(:disabled) {
.tile-ports-label--loading {
color: var(--text-dim);
}
+
+/* ── Sidebar: compact feature card overrides ─────────────────────── */
+
+.sidebar .feature-manager-section {
+ margin-bottom: 0;
+}
+
+.sidebar .feature-subcategory {
+ margin-bottom: 16px;
+}
+
+.sidebar .feature-card {
+ padding: 10px 12px;
+}
+
+.sidebar .feature-card-name {
+ font-size: 0.88rem;
+}
+
+.sidebar .feature-card-desc {
+ font-size: 0.78rem;
+}
+
+.sidebar .section-header {
+ font-size: 0.75rem;
+}
diff --git a/app/sovran_systemsos_web/templates/index.html b/app/sovran_systemsos_web/templates/index.html
index 618528e..8fe916f 100644
--- a/app/sovran_systemsos_web/templates/index.html
+++ b/app/sovran_systemsos_web/templates/index.html
@@ -34,6 +34,10 @@
+