Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion frontend/prefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ function applyPrefs(): void {
Array.from(lineSel.options).forEach((opt) => { opt.textContent = `${opt.value} lines`; });
lineSel.dataset.labeled = '1';
}
q('#authState').textContent = 'dashboard signed in';
q('#shells').classList.toggle('focus-mode', viewMode === 'focus');
applySidebar();
}
Expand Down
8 changes: 8 additions & 0 deletions public/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -720,3 +720,11 @@ button.rm-temp:hover,button.rm-temp:focus-visible{background:rgba(139,246,255,.1
.session-actions{display:none!important}
.terminal-panel .panel-header{margin-bottom:8px}
.terminal-panel .panel-header p{display:none}
/* Tickers ride inside the top bar next to the wordmark now (reclaims their own row). The bar
scrolls horizontally so a long ticker list never grows the header's height. */
.topbar .ticker-strip{flex:1 1 auto;min-width:0;margin:0;align-items:center}
.topbar .ticker-bar{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;min-height:30px;padding:5px 9px}
.topbar .ticker-bar::-webkit-scrollbar{display:none}
.topbar .ticker-strip button{min-height:30px}
/* "shells unlocked" is the redundant happy path — only surface the access pill when LOCKED. */
.access-pill.on{display:none}
1 change: 0 additions & 1 deletion public/prefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ function applyPrefs() {
Array.from(lineSel.options).forEach((opt) => { opt.textContent = `${opt.value} lines`; });
lineSel.dataset.labeled = '1';
}
q('#authState').textContent = 'dashboard signed in';
q('#shells').classList.toggle('focus-mode', viewMode === 'focus');
applySidebar();
}
Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ pub struct Config {
// Gather CPU/RAM/load/temps for remote hosts over SSH (one extra /proc read per poll).
// Default on; set DASHBOARD_REMOTE_METRICS=0 to keep remote cards to ping + containers only.
pub remote_metrics: bool,
// "Safe shot" generates a shareable, redacted screenshot. Off by default; set
// DASHBOARD_ENABLE_SAFE_SHOT=1 to show the top-bar button.
pub enable_safe_shot: bool,
Comment on lines +63 to +65
pub allowed_origins: Vec<String>,
pub show_unknown_sessions: bool,
pub known_sessions: Vec<KnownSession>,
Expand Down Expand Up @@ -206,6 +209,7 @@ impl Config {
remote_metrics: env::var("DASHBOARD_REMOTE_METRICS")
.map(|v| parse_flag(&v))
.unwrap_or(true),
enable_safe_shot: env_flag("DASHBOARD_ENABLE_SAFE_SHOT"),
// Extra browser Origins allowed to open the /api/term WebSocket (beyond same-host).
allowed_origins: split_env("DASHBOARD_ALLOWED_ORIGINS"),
show_unknown_sessions: env_flag("DASHBOARD_SHOW_UNKNOWN_SESSIONS"),
Expand Down
8 changes: 7 additions & 1 deletion src/pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ pub fn dashboard(model: &SessionModel, config: &Config) -> String {
let data = serde_json::to_string(model)
.unwrap_or_else(|_| "{}".to_string())
.replace('<', "\\u003c");
let safe_shot_btn = if config.enable_safe_shot {
r##"<button class="ghost" id="safeShotBtn" type="button"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.5 4 13 2H7L5.5 4H3a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-6.5Z"/><circle cx="12" cy="12" r="3.5"/><path d="M20 8h.01"/></svg><span>Safe shot</span></button>"##
} else {
""
};
let v = asset_ver(config);
let html = format!(
r##"<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="theme-color" content="#071017"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-title" content="ShellDeck"><link rel="manifest" href="/manifest.webmanifest"><link rel="icon" href="/icon.svg" type="image/svg+xml"><link rel="stylesheet" href="/assets/xterm.css"><link rel="stylesheet" href="/assets/app.css"><title>ShellDeck</title></head><body><script type="application/json" id="initial-model">{}</script><main class="shell"><header class="topbar"><div class="brand"><img class="brand-logo" src="/assets/shelldeck-logo.png" alt="" width="50" height="50"><div><h1>ShellDeck</h1><div class="status-line"><span class="pill">{}</span><span class="pill" id="authState">dashboard signed in</span><span class="pill" id="updated">syncing</span><span class="pill access-pill" id="accessState">shells locked</span></div></div></div><div class="top-actions"><button class="ghost" id="safeShotBtn" type="button"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.5 4 13 2H7L5.5 4H3a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-6.5Z"/><circle cx="12" cy="12" r="3.5"/><path d="M20 8h.01"/></svg><span>Safe shot</span></button><button class="primary" id="refreshBtn" type="button"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg><span>Refresh</span></button><a class="button ghost" href="/logout"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg><span>Sign out</span></a></div></header><div class="ticker-strip" id="tickerStrip"><div class="ticker-bar" id="tickerBar"><span class="ticker-empty">No tickers configured</span></div><button type="button" id="editTickersBtn"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20V10"/><path d="M18 20V4"/><path d="M6 20v-4"/></svg><span>Edit tickers</span></button></div>{}<div class="toast" id="toast"></div><div id="bootSplash" aria-hidden="true"><div class="boot-card"><img class="brand-logo" src="/assets/shelldeck-logo.png" alt="" width="54" height="54"><div class="boot-spinner"></div><div class="boot-title">ShellDeck</div></div></div><script src="/assets/xterm.js"></script><script src="/assets/addon-fit.js"></script><script src="/assets/core.js"></script><script src="/assets/prefs.js"></script><script src="/assets/render.js"></script><script src="/assets/actions.js"></script><script src="/assets/terminal.js"></script><script src="/assets/metrics.js"></script><script src="/assets/events.js"></script><script>setTimeout(()=>document.body.classList.add('booted'),4000);</script></body></html>"##,
r##"<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="theme-color" content="#071017"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-title" content="ShellDeck"><link rel="manifest" href="/manifest.webmanifest"><link rel="icon" href="/icon.svg" type="image/svg+xml"><link rel="stylesheet" href="/assets/xterm.css"><link rel="stylesheet" href="/assets/app.css"><title>ShellDeck</title></head><body><script type="application/json" id="initial-model">{}</script><main class="shell"><header class="topbar"><div class="brand"><img class="brand-logo" src="/assets/shelldeck-logo.png" alt="" width="50" height="50"><div><h1>ShellDeck</h1><div class="status-line"><span class="pill">{}</span><span class="pill" id="updated">syncing</span><span class="pill access-pill" id="accessState">shells locked</span></div></div></div><div class="ticker-strip" id="tickerStrip"><div class="ticker-bar" id="tickerBar"><span class="ticker-empty">No tickers configured</span></div><button type="button" id="editTickersBtn"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20V10"/><path d="M18 20V4"/><path d="M6 20v-4"/></svg><span>Edit tickers</span></button></div><div class="top-actions">{}<button class="primary" id="refreshBtn" type="button"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg><span>Refresh</span></button><a class="button ghost" href="/logout"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg><span>Sign out</span></a></div></header>{}<div class="toast" id="toast"></div><div id="bootSplash" aria-hidden="true"><div class="boot-card"><img class="brand-logo" src="/assets/shelldeck-logo.png" alt="" width="54" height="54"><div class="boot-spinner"></div><div class="boot-title">ShellDeck</div></div></div><script src="/assets/xterm.js"></script><script src="/assets/addon-fit.js"></script><script src="/assets/core.js"></script><script src="/assets/prefs.js"></script><script src="/assets/render.js"></script><script src="/assets/actions.js"></script><script src="/assets/terminal.js"></script><script src="/assets/metrics.js"></script><script src="/assets/events.js"></script><script>setTimeout(()=>document.body.classList.add('booted'),4000);</script></body></html>"##,
data,
html_escape(&model.hostname),
safe_shot_btn,
workspace(&links_panel_html(config))
);
bust_assets(&html, &v)
Expand Down