fix: CSS variables neu-*, WebSocket token, thème initial

- CSS: remplace var(--bg-*)/var(--text-*)/var(--accent-*)/var(--color-*)
  par les vraies variables --neu-* (neu-bg, neu-surface, neu-text, neu-primary…)
- CSS: supprime body{overflow:hidden} qui bloquait le scroll
- CSS: .auth-layout déplacé dans neu.css pour login/install
- WS: ajoute ?token= aux connexions /ws/proxmox (dashboardPage + proxmoxPage)
- HTML: script inline pour appliquer data-theme avant Alpine (évite FOUC)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
enzo 2026-03-21 16:39:23 +01:00
parent 2098c80ec1
commit 562eff8863
10 changed files with 142 additions and 133 deletions

View file

@ -374,37 +374,27 @@
/* ── Layout Alpine (sidebar + navbar + page-content) ───────────────────────── */
:root {
--sidebar-width: 240px;
--sidebar-collapsed-width: 64px;
--navbar-height: 56px;
}
body {
display: flex;
min-height: 100vh;
overflow: hidden;
}
/* Sidebar */
/* Sidebar (position:fixed, hors flux) */
.sidebar {
width: var(--sidebar-width);
min-height: 100vh;
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
height: 100vh;
background: var(--neu-surface);
border-right: 1px solid var(--neu-border);
display: flex;
flex-direction: column;
transition: width 0.2s ease;
flex-shrink: 0;
position: fixed;
top: 0;
left: 0;
height: 100vh;
z-index: 100;
z-index: var(--z-sidebar);
overflow: hidden;
}
.sidebar.collapsed {
width: var(--sidebar-collapsed-width);
width: var(--sidebar-width-collapsed);
}
.sidebar-header {
@ -412,13 +402,14 @@ body {
align-items: center;
gap: 0.75rem;
padding: 1rem;
border-bottom: 1px solid var(--border-color);
border-bottom: 1px solid var(--neu-border);
min-height: var(--navbar-height);
flex-shrink: 0;
}
.sidebar-logo {
font-size: 1.5rem;
color: var(--accent-primary);
color: var(--neu-primary);
flex-shrink: 0;
}
@ -427,6 +418,7 @@ body {
font-size: 1rem;
white-space: nowrap;
overflow: hidden;
color: var(--neu-text);
}
.sidebar-toggle {
@ -449,9 +441,9 @@ body {
align-items: center;
gap: 0.75rem;
padding: 0.625rem 0.75rem;
border-radius: 0.5rem;
border-radius: var(--neu-radius-sm);
text-decoration: none;
color: var(--text-secondary);
color: var(--neu-text-muted);
font-size: 0.875rem;
font-weight: 500;
transition: all 0.15s;
@ -460,13 +452,13 @@ body {
}
.sidebar-link:hover {
background: var(--bg-hover, rgba(255,255,255,0.05));
color: var(--text-primary);
background: rgba(108, 142, 244, 0.08);
color: var(--neu-text);
}
.sidebar-link.active {
background: rgba(99, 102, 241, 0.15);
color: var(--accent-primary);
background: rgba(108, 142, 244, 0.15);
color: var(--neu-primary);
}
.sidebar-icon {
@ -483,35 +475,33 @@ body {
.sidebar-footer {
padding: 0.75rem;
border-top: 1px solid var(--border-color);
border-top: 1px solid var(--neu-border);
display: flex;
align-items: center;
gap: 0.5rem;
flex-shrink: 0;
}
.sidebar-user {
flex: 1;
font-size: 0.8rem;
color: var(--text-secondary);
color: var(--neu-text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Main layout */
/* Main layout (marge pour compenser le sidebar fixe) */
.main-layout {
display: flex;
flex-direction: column;
flex: 1;
margin-left: var(--sidebar-width);
min-height: 100vh;
margin-left: var(--sidebar-width);
transition: margin-left 0.2s ease;
overflow: hidden;
}
.sidebar.collapsed ~ .main-layout,
body:has(.sidebar.collapsed) .main-layout {
margin-left: var(--sidebar-collapsed-width);
.sidebar.collapsed ~ .main-layout {
margin-left: var(--sidebar-width-collapsed);
}
/* Navbar */
@ -521,18 +511,19 @@ body:has(.sidebar.collapsed) .main-layout {
align-items: center;
justify-content: space-between;
padding: 0 1.5rem;
border-bottom: 1px solid var(--border-color);
background: var(--bg-primary);
border-bottom: 1px solid var(--neu-border);
background: var(--neu-bg);
flex-shrink: 0;
position: sticky;
top: 0;
z-index: 50;
z-index: var(--z-navbar);
}
.navbar-title {
font-size: 1rem;
font-weight: 700;
margin: 0;
color: var(--neu-text);
}
.navbar-actions {
@ -546,7 +537,19 @@ body:has(.sidebar.collapsed) .main-layout {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
height: calc(100vh - var(--navbar-height));
background: var(--neu-bg);
color: var(--neu-text);
}
/* Auth layout (login, install) */
.auth-layout {
min-height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
background: var(--neu-bg);
}
/* Swup fade transition */
@ -560,12 +563,8 @@ html.is-animating .transition-fade {
/* Responsive */
@media (max-width: 768px) {
.sidebar {
width: var(--sidebar-collapsed-width);
}
.main-layout {
margin-left: var(--sidebar-collapsed-width);
}
.sidebar { width: var(--sidebar-width-collapsed); }
.main-layout { margin-left: var(--sidebar-width-collapsed); }
}
[x-cloak] { display: none !important; }

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Dashboard</title>
@ -127,32 +128,32 @@
<style>
[x-cloak]{display:none!important}
.main-layout{display:flex;flex-direction:column;flex:1;margin-left:var(--sidebar-width,240px);transition:margin-left .2s}
.sidebar.collapsed~.main-layout{margin-left:var(--sidebar-collapsed-width,64px)}
.sidebar.collapsed~.main-layout{margin-left:var(--sidebar-width-collapsed,64px)}
.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:1rem;margin-bottom:1.5rem}
.stat-card{padding:1.25rem;text-align:center}
.stat-icon{font-size:1.5rem;margin-bottom:.5rem;color:var(--accent-primary)}
.stat-icon{font-size:1.5rem;margin-bottom:.5rem;color:var(--neu-primary)}
.stat-value{font-size:2rem;font-weight:700}
.stat-label{font-size:.8rem;color:var(--text-secondary)}
.ws-status{padding:.5rem 1rem;border-radius:.5rem;font-size:.8rem;margin-bottom:1rem;background:var(--bg-secondary)}
.ws-status.ok{color:var(--color-success,#22c55e)}
.ws-status.disconnected,.ws-status.error{color:var(--color-warning,#f59e0b)}
.stat-label{font-size:.8rem;color:var(--neu-text-muted)}
.ws-status{padding:.5rem 1rem;border-radius:.5rem;font-size:.8rem;margin-bottom:1rem;background:var(--neu-surface)}
.ws-status.ok{color:var(--neu-success)}
.ws-status.disconnected,.ws-status.error{color:var(--neu-warning)}
.resource-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:1rem}
.resource-card{padding:1rem}
.resource-header{display:flex;align-items:center;gap:.5rem;margin-bottom:.75rem}
.resource-name{font-weight:600;flex:1}
.resource-id{font-size:.75rem;color:var(--text-secondary)}
.resource-id{font-size:.75rem;color:var(--neu-text-muted)}
.resource-badge{font-size:.7rem;padding:.2rem .5rem;border-radius:.25rem;text-transform:uppercase}
.resource-badge.running{background:rgba(34,197,94,.15);color:var(--color-success,#22c55e)}
.resource-badge.stopped{background:rgba(239,68,68,.1);color:var(--color-error,#ef4444)}
.resource-badge.running{background:rgba(34,197,94,.15);color:var(--neu-success)}
.resource-badge.stopped{background:rgba(239,68,68,.1);color:var(--neu-danger)}
.metric{display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem}
.metric-label{font-size:.7rem;color:var(--text-secondary);min-width:30px}
.metric-bar{flex:1;height:6px;border-radius:3px;background:var(--bg-secondary);overflow:hidden}
.metric-fill{height:100%;border-radius:3px;background:var(--accent-primary);transition:width .5s}
.metric-label{font-size:.7rem;color:var(--neu-text-muted);min-width:30px}
.metric-bar{flex:1;height:6px;border-radius:3px;background:var(--neu-surface);overflow:hidden}
.metric-fill{height:100%;border-radius:3px;background:var(--neu-primary);transition:width .5s}
.metric-val{font-size:.7rem;min-width:36px;text-align:right}
.section{margin-bottom:2rem}
.section-title{font-size:1rem;font-weight:600;margin-bottom:.75rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.05em}
.empty-state{color:var(--text-muted);font-size:.875rem}
.loading{color:var(--text-muted);font-size:.875rem}
.section-title{font-size:1rem;font-weight:600;margin-bottom:.75rem;color:var(--neu-text-muted);text-transform:uppercase;letter-spacing:.05em}
.empty-state{color:var(--neu-text-muted);font-size:.875rem}
.loading{color:var(--neu-text-muted);font-size:.875rem}
</style>
</body>
</html>

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Installation</title>
@ -146,27 +147,27 @@
.auth-layout { min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem; }
.install-card { width:100%;max-width:560px;padding:2rem; }
.auth-logo { text-align:center;margin-bottom:1.5rem; }
.logo-icon { font-size:2.5rem;color:var(--accent-primary); }
.logo-icon { font-size:2.5rem;color:var(--neu-primary); }
.auth-title { font-size:1.5rem;font-weight:700;margin:.25rem 0; }
.auth-subtitle { font-size:.875rem;color:var(--text-secondary);margin:0; }
.auth-subtitle { font-size:.875rem;color:var(--neu-text-muted);margin:0; }
.stepper { display:flex;gap:.5rem;justify-content:center;margin:1.5rem 0; }
.step { display:flex;flex-direction:column;align-items:center;gap:.25rem;flex:1;max-width:100px; }
.step-dot { width:2rem;height:2rem;border-radius:50%;border:2px solid var(--border-color);display:flex;align-items:center;justify-content:center;font-size:.8rem;font-weight:700;transition:all .2s; }
.step.active .step-dot { border-color:var(--accent-primary);background:var(--accent-primary);color:#fff; }
.step.done .step-dot { border-color:var(--color-success,#22c55e);background:var(--color-success,#22c55e);color:#fff; }
.step-label { font-size:.7rem;color:var(--text-secondary);text-align:center; }
.step-dot { width:2rem;height:2rem;border-radius:50%;border:2px solid var(--neu-border);display:flex;align-items:center;justify-content:center;font-size:.8rem;font-weight:700;transition:all .2s; }
.step.active .step-dot { border-color:var(--neu-primary);background:var(--neu-primary);color:#fff; }
.step.done .step-dot { border-color:var(--neu-success);background:var(--neu-success);color:#fff; }
.step-label { font-size:.7rem;color:var(--neu-text-muted);text-align:center; }
.step-content { display:flex;flex-direction:column;gap:1rem;min-height:200px; }
.step-content h2 { margin:0;font-size:1.125rem; }
.step-desc { margin:0;font-size:.875rem;color:var(--text-secondary); }
.step-desc { margin:0;font-size:.875rem;color:var(--neu-text-muted); }
.form-group { display:flex;flex-direction:column;gap:.4rem; }
.form-label { font-size:.8rem;font-weight:600;color:var(--text-secondary); }
.form-hint { font-size:.75rem;color:var(--text-muted);margin:0; }
.form-label { font-size:.8rem;font-weight:600;color:var(--neu-text-muted); }
.form-hint { font-size:.75rem;color:var(--neu-text-muted);margin:0; }
.step-nav { display:flex;justify-content:flex-end;gap:.75rem;margin-top:1.5rem; }
.status-ok { padding:.5rem .75rem;border-radius:.375rem;background:rgba(34,197,94,.1);border:1px solid var(--color-success,#22c55e);color:var(--color-success,#22c55e);font-size:.875rem; }
.status-error { padding:.5rem .75rem;border-radius:.375rem;background:rgba(239,68,68,.1);border:1px solid var(--color-error,#ef4444);color:var(--color-error,#ef4444);font-size:.875rem; }
.status-ok { padding:.5rem .75rem;border-radius:.375rem;background:rgba(34,197,94,.1);border:1px solid var(--neu-success);color:var(--neu-success);font-size:.875rem; }
.status-error { padding:.5rem .75rem;border-radius:.375rem;background:rgba(239,68,68,.1);border:1px solid var(--neu-danger);color:var(--neu-danger);font-size:.875rem; }
.confirm-summary { padding:1rem;display:flex;flex-direction:column;gap:.5rem;border-radius:.5rem; }
.confirm-row { display:flex;gap:1rem;font-size:.875rem; }
.confirm-label { font-weight:600;min-width:80px;color:var(--text-secondary); }
.confirm-label { font-weight:600;min-width:80px;color:var(--neu-text-muted); }
.spinner { display:inline-block;width:1rem;height:1rem;border:2px solid transparent;border-top-color:currentColor;border-radius:50%;animation:spin .6s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }
[x-cloak] { display:none!important; }

View file

@ -314,7 +314,8 @@ document.addEventListener('alpine:init', () => {
connectWS() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws'
this.ws = new WebSocket(`${proto}://${location.host}/ws/proxmox`)
const token = encodeURIComponent(localStorage.getItem('pxp_token') || '')
this.ws = new WebSocket(`${proto}://${location.host}/ws/proxmox?token=${token}`)
this.ws.onmessage = (e) => {
const msg = JSON.parse(e.data)
if (msg.type === 'proxmox_resources') {
@ -349,7 +350,8 @@ document.addEventListener('alpine:init', () => {
connectWS() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws'
this.ws = new WebSocket(`${proto}://${location.host}/ws/proxmox`)
const token = encodeURIComponent(localStorage.getItem('pxp_token') || '')
this.ws = new WebSocket(`${proto}://${location.host}/ws/proxmox?token=${token}`)
this.ws.onmessage = (e) => {
const msg = JSON.parse(e.data)
if (msg.type === 'proxmox_resources') {
@ -377,9 +379,9 @@ document.addEventListener('alpine:init', () => {
},
cpuColor(pct) {
if (pct > 80) return 'var(--color-error)'
if (pct > 50) return 'var(--color-warning)'
return 'var(--color-success)'
if (pct > 80) return 'var(--neu-danger)'
if (pct > 50) return 'var(--neu-warning)'
return 'var(--neu-success)'
},
formatMem(bytes) {

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Connexion</title>
@ -75,7 +76,7 @@
}
.logo-icon {
font-size: 3rem;
color: var(--accent-primary);
color: var(--neu-primary);
}
.auth-title {
font-size: 1.5rem;
@ -84,7 +85,7 @@
}
.auth-subtitle {
font-size: 0.875rem;
color: var(--text-secondary);
color: var(--neu-text-muted);
margin: 0;
}
.auth-form {
@ -100,15 +101,15 @@
.form-label {
font-size: 0.875rem;
font-weight: 600;
color: var(--text-secondary);
color: var(--neu-text-muted);
}
.form-error {
background: rgba(239,68,68,0.1);
border: 1px solid var(--color-error, #ef4444);
border: 1px solid var(--neu-danger);
border-radius: 0.5rem;
padding: 0.75rem;
font-size: 0.875rem;
color: var(--color-error, #ef4444);
color: var(--neu-danger);
}
.auth-submit {
width: 100%;

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Modules</title>
@ -92,17 +93,17 @@
.module-icon{font-size:1.75rem;flex-shrink:0}
.module-info{flex:1}
.module-name{font-weight:700;display:block;margin-bottom:.2rem}
.module-desc{font-size:.8rem;color:var(--text-secondary);display:block}
.core-badge{font-size:.65rem;padding:.15rem .4rem;border-radius:.2rem;background:rgba(99,102,241,.15);color:var(--accent-primary);font-weight:700;text-transform:uppercase}
.toggle-btn{display:flex;align-items:center;gap:.4rem;background:none;border:none;cursor:pointer;color:var(--text-secondary);font-size:.8rem}
.module-desc{font-size:.8rem;color:var(--neu-text-muted);display:block}
.core-badge{font-size:.65rem;padding:.15rem .4rem;border-radius:.2rem;background:rgba(99,102,241,.15);color:var(--neu-primary);font-weight:700;text-transform:uppercase}
.toggle-btn{display:flex;align-items:center;gap:.4rem;background:none;border:none;cursor:pointer;color:var(--neu-text-muted);font-size:.8rem}
.toggle-btn:disabled{cursor:not-allowed;opacity:.5}
.toggle-track{width:2.5rem;height:1.25rem;background:var(--bg-secondary);border-radius:.625rem;position:relative;transition:background .2s;border:1px solid var(--border-color)}
.toggle-thumb{position:absolute;top:.125rem;left:.125rem;width:.875rem;height:.875rem;background:var(--text-muted);border-radius:50%;transition:transform .2s,background .2s}
.toggle-btn.on .toggle-track{background:var(--accent-primary);border-color:var(--accent-primary)}
.toggle-track{width:2.5rem;height:1.25rem;background:var(--neu-surface);border-radius:.625rem;position:relative;transition:background .2s;border:1px solid var(--neu-border)}
.toggle-thumb{position:absolute;top:.125rem;left:.125rem;width:.875rem;height:.875rem;background:var(--neu-text-muted);border-radius:50%;transition:transform .2s,background .2s}
.toggle-btn.on .toggle-track{background:var(--neu-primary);border-color:var(--neu-primary)}
.toggle-btn.on .toggle-thumb{transform:translateX(1.25rem);background:#fff}
.loading-state{display:flex;align-items:center;gap:.75rem;padding:2rem;color:var(--text-muted)}
.spinner-lg{width:2rem;height:2rem;border:3px solid transparent;border-top-color:var(--accent-primary);border-radius:50%;animation:spin .6s linear infinite}
.empty-state{color:var(--text-muted);font-size:.875rem}
.loading-state{display:flex;align-items:center;gap:.75rem;padding:2rem;color:var(--neu-text-muted)}
.spinner-lg{width:2rem;height:2rem;border:3px solid transparent;border-top-color:var(--neu-primary);border-radius:50%;animation:spin .6s linear infinite}
.empty-state{color:var(--neu-text-muted);font-size:.875rem}
@keyframes spin{to{transform:rotate(360deg)}}
</style>
</body>

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Proxmox</title>
@ -49,9 +50,9 @@
<div class="ws-status" :class="wsStatus">
<span x-show="wsStatus==='connecting'">⌛ Connexion WebSocket…</span>
<span x-show="wsStatus==='ok'" style="color:var(--color-success)">● Live</span>
<span x-show="wsStatus==='disconnected'" style="color:var(--color-warning)">⚠ Reconnexion…</span>
<span x-show="wsStatus==='error'" style="color:var(--color-error)">✗ Erreur WebSocket</span>
<span x-show="wsStatus==='ok'" style="color:var(--neu-success)">● Live</span>
<span x-show="wsStatus==='disconnected'" style="color:var(--neu-warning)">⚠ Reconnexion…</span>
<span x-show="wsStatus==='error'" style="color:var(--neu-danger)">✗ Erreur WebSocket</span>
</div>
<!-- LXC -->
@ -140,19 +141,19 @@
.resource-header{display:flex;align-items:center;gap:.5rem;margin-bottom:.75rem}
.resource-name{font-weight:600;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.badge{font-size:.7rem;padding:.2rem .5rem;border-radius:.25rem;text-transform:uppercase;font-weight:600}
.badge.running{background:rgba(34,197,94,.15);color:var(--color-success,#22c55e)}
.badge.stopped{background:rgba(239,68,68,.1);color:var(--color-error,#ef4444)}
.badge.running{background:rgba(34,197,94,.15);color:var(--neu-success)}
.badge.stopped{background:rgba(239,68,68,.1);color:var(--neu-danger)}
.resource-metrics{margin-bottom:.75rem}
.metric{display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem}
.metric-label{font-size:.7rem;color:var(--text-secondary);min-width:30px}
.metric-bar{flex:1;height:6px;border-radius:3px;background:var(--bg-secondary);overflow:hidden}
.metric-fill{height:100%;border-radius:3px;background:var(--accent-primary);transition:width .5s}
.metric-label{font-size:.7rem;color:var(--neu-text-muted);min-width:30px}
.metric-bar{flex:1;height:6px;border-radius:3px;background:var(--neu-surface);overflow:hidden}
.metric-fill{height:100%;border-radius:3px;background:var(--neu-primary);transition:width .5s}
.metric-val{font-size:.7rem;min-width:36px;text-align:right}
.resource-actions{display:flex;gap:.5rem;flex-wrap:wrap}
.section{margin-bottom:2rem}
.section-title{font-size:.875rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);margin-bottom:.75rem}
.section-title{font-size:.875rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--neu-text-muted);margin-bottom:.75rem}
.ws-status{font-size:.8rem;margin-bottom:1rem}
.empty-state{color:var(--text-muted);font-size:.875rem}
.empty-state{color:var(--neu-text-muted);font-size:.875rem}
</style>
</body>
</html>

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Paramètres</title>
@ -141,21 +142,21 @@
<style>
[x-cloak]{display:none!important}
.main-layout{display:flex;flex-direction:column;flex:1;margin-left:var(--sidebar-width,240px);transition:margin-left .2s}
.tabs{display:flex;gap:.5rem;margin-bottom:1.5rem;border-bottom:1px solid var(--border-color);padding-bottom:.5rem}
.tab-btn{padding:.5rem 1rem;border-radius:.375rem .375rem 0 0;font-size:.875rem;font-weight:600;color:var(--text-secondary);background:transparent;border:none;cursor:pointer;transition:all .15s}
.tab-btn.active{color:var(--accent-primary);background:var(--bg-secondary);border-bottom:2px solid var(--accent-primary)}
.tab-btn:hover:not(.active){color:var(--text-primary)}
.tabs{display:flex;gap:.5rem;margin-bottom:1.5rem;border-bottom:1px solid var(--neu-border);padding-bottom:.5rem}
.tab-btn{padding:.5rem 1rem;border-radius:.375rem .375rem 0 0;font-size:.875rem;font-weight:600;color:var(--neu-text-muted);background:transparent;border:none;cursor:pointer;transition:all .15s}
.tab-btn.active{color:var(--neu-primary);background:var(--neu-surface);border-bottom:2px solid var(--neu-primary)}
.tab-btn:hover:not(.active){color:var(--neu-text)}
.tab-panel{animation:fadeIn .15s ease}
@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.form-grid{display:grid;gap:1.25rem;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));margin-bottom:1.5rem}
.form-group{display:flex;flex-direction:column;gap:.4rem}
.form-label{font-size:.8rem;font-weight:600;color:var(--text-secondary)}
.save-bar{display:flex;align-items:center;justify-content:flex-end;gap:1rem;padding-top:1rem;border-top:1px solid var(--border-color)}
.form-label{font-size:.8rem;font-weight:600;color:var(--neu-text-muted)}
.save-bar{display:flex;align-items:center;justify-content:flex-end;gap:1rem;padding-top:1rem;border-top:1px solid var(--neu-border)}
.save-feedback{flex:1}
.save-success{color:var(--color-success,#22c55e);font-size:.875rem}
.save-error{color:var(--color-error,#ef4444);font-size:.875rem}
.loading-state{display:flex;align-items:center;gap:.75rem;padding:2rem;color:var(--text-muted)}
.spinner-lg{width:2rem;height:2rem;border:3px solid transparent;border-top-color:var(--accent-primary);border-radius:50%;animation:spin .6s linear infinite}
.save-success{color:var(--neu-success);font-size:.875rem}
.save-error{color:var(--neu-danger);font-size:.875rem}
.loading-state{display:flex;align-items:center;gap:.75rem;padding:2rem;color:var(--neu-text-muted)}
.spinner-lg{width:2rem;height:2rem;border:3px solid transparent;border-top-color:var(--neu-primary);border-radius:50%;animation:spin .6s linear infinite}
.spinner-sm{display:inline-block;width:.875rem;height:.875rem;border:2px solid transparent;border-top-color:currentColor;border-radius:50%;animation:spin .6s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
</style>

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Terminal</title>
@ -60,11 +61,11 @@
[x-cloak]{display:none!important}
.main-layout{display:flex;flex-direction:column;flex:1;margin-left:var(--sidebar-width,240px);transition:margin-left .2s;height:100vh;overflow:hidden}
.terminal-layout{flex:1;display:flex;flex-direction:column;padding:0!important;overflow:hidden}
.terminal-toolbar{display:flex;align-items:center;justify-content:space-between;padding:.5rem 1rem;border-bottom:1px solid var(--border-color);background:var(--bg-secondary);flex-shrink:0}
.terminal-toolbar{display:flex;align-items:center;justify-content:space-between;padding:.5rem 1rem;border-bottom:1px solid var(--neu-border);background:var(--neu-surface);flex-shrink:0}
.terminal-status{font-size:.8rem;font-family:monospace}
.terminal-status.connected{color:var(--color-success,#22c55e)}
.terminal-status.disconnected,.terminal-status.error{color:var(--color-error,#ef4444)}
.terminal-status.connecting{color:var(--text-muted)}
.terminal-status.connected{color:var(--neu-success)}
.terminal-status.disconnected,.terminal-status.error{color:var(--neu-danger)}
.terminal-status.connecting{color:var(--neu-text-muted)}
.terminal-container{flex:1;overflow:hidden;background:#1a1a2e;padding:.5rem}
.terminal-container .xterm{height:100%}
</style>

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Mises à jour</title>
@ -150,40 +151,40 @@
.main-layout{display:flex;flex-direction:column;flex:1;margin-left:var(--sidebar-width,240px);transition:margin-left .2s}
.page-actions{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.5rem;gap:1rem;flex-wrap:wrap}
.page-actions-left,.page-actions-right{display:flex;align-items:center;gap:.75rem}
.total-badge{font-size:.875rem;font-weight:600;color:var(--accent-primary)}
.total-badge{font-size:.875rem;font-weight:600;color:var(--neu-primary)}
.targets-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1rem}
.target-card{padding:1rem;display:flex;flex-direction:column;gap:.75rem}
.target-header{display:flex;justify-content:space-between;align-items:flex-start}
.target-info{display:flex;flex-direction:column;gap:.2rem}
.target-name{font-weight:700;font-size:.95rem}
.target-id{font-size:.7rem;color:var(--text-muted);font-family:monospace}
.target-id{font-size:.7rem;color:var(--neu-text-muted);font-family:monospace}
.badge{font-size:.7rem;padding:.2rem .5rem;border-radius:.25rem;text-transform:uppercase;font-weight:600}
.badge.running{background:rgba(34,197,94,.15);color:var(--color-success,#22c55e)}
.badge.stopped{background:rgba(239,68,68,.1);color:var(--color-error,#ef4444)}
.badge.running{background:rgba(34,197,94,.15);color:var(--neu-success)}
.badge.stopped{background:rgba(239,68,68,.1);color:var(--neu-danger)}
.package-summary{font-size:.875rem}
.muted{color:var(--text-muted)}
.up-to-date{color:var(--color-success,#22c55e)}
.has-updates{color:var(--color-warning,#f59e0b);font-weight:600}
.checking-text{display:flex;align-items:center;gap:.4rem;color:var(--text-secondary)}
.package-list{max-height:160px;overflow-y:auto;border-top:1px solid var(--border-color);padding-top:.5rem;display:flex;flex-direction:column;gap:.25rem}
.muted{color:var(--neu-text-muted)}
.up-to-date{color:var(--neu-success)}
.has-updates{color:var(--neu-warning);font-weight:600}
.checking-text{display:flex;align-items:center;gap:.4rem;color:var(--neu-text-muted)}
.package-list{max-height:160px;overflow-y:auto;border-top:1px solid var(--neu-border);padding-top:.5rem;display:flex;flex-direction:column;gap:.25rem}
.package-row{display:flex;justify-content:space-between;gap:.5rem;font-size:.75rem}
.pkg-name{font-weight:600;font-family:monospace;color:var(--accent-primary)}
.pkg-version{display:flex;align-items:center;gap:.25rem;color:var(--text-secondary)}
.pkg-name{font-weight:600;font-family:monospace;color:var(--neu-primary)}
.pkg-version{display:flex;align-items:center;gap:.25rem;color:var(--neu-text-muted)}
.old-ver{text-decoration:line-through;opacity:.6}
.arrow{color:var(--accent-primary)}
.new-ver{color:var(--color-success,#22c55e);font-weight:600}
.arrow{color:var(--neu-primary)}
.new-ver{color:var(--neu-success);font-weight:600}
.target-actions{display:flex;gap:.5rem;margin-top:auto}
.output-panel{margin-top:1.5rem;border-radius:.75rem;overflow:hidden}
.output-header{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;border-bottom:1px solid var(--border-color)}
.output-header{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;border-bottom:1px solid var(--neu-border)}
.output-title{font-weight:600;font-size:.875rem;font-family:monospace}
.job-status{font-size:.75rem;padding:.2rem .5rem;border-radius:.25rem;text-transform:uppercase}
.job-status.running{background:rgba(99,102,241,.15);color:var(--accent-primary)}
.job-status.success{background:rgba(34,197,94,.15);color:var(--color-success,#22c55e)}
.job-status.error{background:rgba(239,68,68,.1);color:var(--color-error,#ef4444)}
.output-content{padding:1rem;font-family:monospace;font-size:.75rem;white-space:pre-wrap;word-break:break-all;max-height:400px;overflow-y:auto;margin:0;color:var(--text-primary)}
.loading-state{display:flex;align-items:center;gap:.75rem;padding:2rem;color:var(--text-muted)}
.job-status.running{background:rgba(99,102,241,.15);color:var(--neu-primary)}
.job-status.success{background:rgba(34,197,94,.15);color:var(--neu-success)}
.job-status.error{background:rgba(239,68,68,.1);color:var(--neu-danger)}
.output-content{padding:1rem;font-family:monospace;font-size:.75rem;white-space:pre-wrap;word-break:break-all;max-height:400px;overflow-y:auto;margin:0;color:var(--neu-text)}
.loading-state{display:flex;align-items:center;gap:.75rem;padding:2rem;color:var(--neu-text-muted)}
.spinner-sm{display:inline-block;width:.875rem;height:.875rem;border:2px solid transparent;border-top-color:currentColor;border-radius:50%;animation:spin .6s linear infinite}
.spinner-lg{width:2rem;height:2rem;border:3px solid transparent;border-top-color:var(--accent-primary);border-radius:50%;animation:spin .6s linear infinite}
.spinner-lg{width:2rem;height:2rem;border:3px solid transparent;border-top-color:var(--neu-primary);border-radius:50%;animation:spin .6s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
</style>
</body>