From 5b011a55ea378b36902eee162345555e589de03f Mon Sep 17 00:00:00 2001 From: X99 Date: Thu, 1 Oct 2026 21:38:11 +0200 Subject: [PATCH] Firmware actions on one row, as real buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Install…, Staged… and Roll out wrapped onto two lines and looked like plain text, and the table overflowed on a 1366-px screen. They now sit on one row as bordered buttons (Install… in the accent color, deleting stays discreet), notes may wrap and the compressed size goes under the size: the table fits from 1280 px on. Fixes #106 --- server/otter/static/app.js | 14 +++++++------- server/otter/static/style.css | 7 +++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/server/otter/static/app.js b/server/otter/static/app.js index f5b77a7..0fb5d0e 100644 --- a/server/otter/static/app.js +++ b/server/otter/static/app.js @@ -919,17 +919,17 @@ function renderFirmwares() { ${esc(f.version)}${f.signed ? ` 🔏` : ""}${ f.crash_count ? ` 💥 ${f.crash_count}` : ""} ${bytes(f.size)}${ - f.compressed_size ? ` (${bytes(f.compressed_size)} zipped)` : ""} + f.compressed_size ? `
${bytes(f.compressed_size)} zipped
` : ""} ${esc(f.sha256.slice(0, 12))}… ${ago(f.uploaded_at)} - ${esc(f.notes)} - - ${f.has_factory && installFamily(f.hw) ? `` : ""} - - + ${esc(f.notes)} +
+ ${f.has_factory && installFamily(f.hw) ? `` : ""} + + - +
`).join(""); $("#no-firmwares").hidden = state.firmwares.length > 0; $("#firmware-count").textContent = state.firmwares.length || ""; diff --git a/server/otter/static/style.css b/server/otter/static/style.css index dd11044..e7f375a 100644 --- a/server/otter/static/style.css +++ b/server/otter/static/style.css @@ -142,6 +142,8 @@ tr[data-status="off"] .avatar { --tone: var(--muted); } .newer { font-family: system-ui, sans-serif; font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: 99px; color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent); } .ver { font-family: var(--mono); font-size: 12.5px; padding: 2px 8px; border-radius: 6px; background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent); } td.fw-app { font-weight: 600; } +#firmwares td.notes { white-space: normal; min-width: 110px; } /* the column that gives way */ +#firmwares td .sub { font-size: 12px; margin-top: 1px; } tr.offline td:not(.actions):not(.check) { opacity: .55; } th.check, td.check { width: 32px; } .update-col { width: 240px; } @@ -192,6 +194,11 @@ input[type="checkbox"] { accent-color: var(--accent); } @keyframes pulse { 50% { opacity: .5; } } .actions { text-align: right; } +/* A firmware's actions: one row, real buttons; deleting stays discreet. */ +.row-actions { display: flex; justify-content: flex-end; align-items: center; gap: 6px; } +.row-actions button:not(.ghost) { padding: 4px 10px; font-size: 13px; white-space: nowrap; } +.row-actions button:not(.ghost):hover { border-color: var(--accent); color: var(--accent); } +.row-actions .install { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); } .upload { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; margin-bottom: 12px; padding: 16px; background: var(--surface); border: 1px dashed color-mix(in srgb, var(--accent) 40%, var(--border)); border-radius: 12px; } .upload-title { flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; font-size: 13px; }