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

View file

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

View file

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Installation</title> <title>ProxmoxPanel — Installation</title>
@ -146,27 +147,27 @@
.auth-layout { min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem; } .auth-layout { min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem; }
.install-card { width:100%;max-width:560px;padding:2rem; } .install-card { width:100%;max-width:560px;padding:2rem; }
.auth-logo { text-align:center;margin-bottom:1.5rem; } .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-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; } .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 { 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-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(--accent-primary);background:var(--accent-primary);color:#fff; } .step.active .step-dot { border-color:var(--neu-primary);background:var(--neu-primary);color:#fff; }
.step.done .step-dot { border-color:var(--color-success,#22c55e);background:var(--color-success,#22c55e);color:#fff; } .step.done .step-dot { border-color:var(--neu-success);background:var(--neu-success);color:#fff; }
.step-label { font-size:.7rem;color:var(--text-secondary);text-align:center; } .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 { display:flex;flex-direction:column;gap:1rem;min-height:200px; }
.step-content h2 { margin:0;font-size:1.125rem; } .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-group { display:flex;flex-direction:column;gap:.4rem; }
.form-label { font-size:.8rem;font-weight:600;color:var(--text-secondary); } .form-label { font-size:.8rem;font-weight:600;color:var(--neu-text-muted); }
.form-hint { font-size:.75rem;color:var(--text-muted);margin:0; } .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; } .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-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(--color-error,#ef4444);color:var(--color-error,#ef4444);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-summary { padding:1rem;display:flex;flex-direction:column;gap:.5rem;border-radius:.5rem; }
.confirm-row { display:flex;gap:1rem;font-size:.875rem; } .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; } .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); } } @keyframes spin { to { transform:rotate(360deg); } }
[x-cloak] { display:none!important; } [x-cloak] { display:none!important; }

View file

@ -314,7 +314,8 @@ document.addEventListener('alpine:init', () => {
connectWS() { connectWS() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws' 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) => { this.ws.onmessage = (e) => {
const msg = JSON.parse(e.data) const msg = JSON.parse(e.data)
if (msg.type === 'proxmox_resources') { if (msg.type === 'proxmox_resources') {
@ -349,7 +350,8 @@ document.addEventListener('alpine:init', () => {
connectWS() { connectWS() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws' 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) => { this.ws.onmessage = (e) => {
const msg = JSON.parse(e.data) const msg = JSON.parse(e.data)
if (msg.type === 'proxmox_resources') { if (msg.type === 'proxmox_resources') {
@ -377,9 +379,9 @@ document.addEventListener('alpine:init', () => {
}, },
cpuColor(pct) { cpuColor(pct) {
if (pct > 80) return 'var(--color-error)' if (pct > 80) return 'var(--neu-danger)'
if (pct > 50) return 'var(--color-warning)' if (pct > 50) return 'var(--neu-warning)'
return 'var(--color-success)' return 'var(--neu-success)'
}, },
formatMem(bytes) { formatMem(bytes) {

View file

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

View file

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Modules</title> <title>ProxmoxPanel — Modules</title>
@ -92,17 +93,17 @@
.module-icon{font-size:1.75rem;flex-shrink:0} .module-icon{font-size:1.75rem;flex-shrink:0}
.module-info{flex:1} .module-info{flex:1}
.module-name{font-weight:700;display:block;margin-bottom:.2rem} .module-name{font-weight:700;display:block;margin-bottom:.2rem}
.module-desc{font-size:.8rem;color:var(--text-secondary);display:block} .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(--accent-primary);font-weight:700;text-transform:uppercase} .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(--text-secondary);font-size:.8rem} .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-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-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(--text-muted);border-radius:50%;transition:transform .2s,background .2s} .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(--accent-primary);border-color:var(--accent-primary)} .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} .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)} .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(--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}
.empty-state{color:var(--text-muted);font-size:.875rem} .empty-state{color:var(--neu-text-muted);font-size:.875rem}
@keyframes spin{to{transform:rotate(360deg)}} @keyframes spin{to{transform:rotate(360deg)}}
</style> </style>
</body> </body>

View file

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

View file

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Paramètres</title> <title>ProxmoxPanel — Paramètres</title>
@ -141,21 +142,21 @@
<style> <style>
[x-cloak]{display:none!important} [x-cloak]{display:none!important}
.main-layout{display:flex;flex-direction:column;flex:1;margin-left:var(--sidebar-width,240px);transition:margin-left .2s} .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} .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(--text-secondary);background:transparent;border:none;cursor:pointer;transition:all .15s} .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(--accent-primary);background:var(--bg-secondary);border-bottom:2px solid var(--accent-primary)} .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(--text-primary)} .tab-btn:hover:not(.active){color:var(--neu-text)}
.tab-panel{animation:fadeIn .15s ease} .tab-panel{animation:fadeIn .15s ease}
@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}} @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-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-group{display:flex;flex-direction:column;gap:.4rem}
.form-label{font-size:.8rem;font-weight:600;color:var(--text-secondary)} .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(--border-color)} .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-feedback{flex:1}
.save-success{color:var(--color-success,#22c55e);font-size:.875rem} .save-success{color:var(--neu-success);font-size:.875rem}
.save-error{color:var(--color-error,#ef4444);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(--text-muted)} .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(--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}
.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-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)}} @keyframes spin{to{transform:rotate(360deg)}}
</style> </style>

View file

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Terminal</title> <title>ProxmoxPanel — Terminal</title>
@ -60,11 +61,11 @@
[x-cloak]{display:none!important} [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} .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-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{font-size:.8rem;font-family:monospace}
.terminal-status.connected{color:var(--color-success,#22c55e)} .terminal-status.connected{color:var(--neu-success)}
.terminal-status.disconnected,.terminal-status.error{color:var(--color-error,#ef4444)} .terminal-status.disconnected,.terminal-status.error{color:var(--neu-danger)}
.terminal-status.connecting{color:var(--text-muted)} .terminal-status.connecting{color:var(--neu-text-muted)}
.terminal-container{flex:1;overflow:hidden;background:#1a1a2e;padding:.5rem} .terminal-container{flex:1;overflow:hidden;background:#1a1a2e;padding:.5rem}
.terminal-container .xterm{height:100%} .terminal-container .xterm{height:100%}
</style> </style>

View file

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
<script>(function(){document.documentElement.setAttribute("data-theme",localStorage.getItem("pxp_theme")||"dark")})()</script>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProxmoxPanel — Mises à jour</title> <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} .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{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} .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} .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-card{padding:1rem;display:flex;flex-direction:column;gap:.75rem}
.target-header{display:flex;justify-content:space-between;align-items:flex-start} .target-header{display:flex;justify-content:space-between;align-items:flex-start}
.target-info{display:flex;flex-direction:column;gap:.2rem} .target-info{display:flex;flex-direction:column;gap:.2rem}
.target-name{font-weight:700;font-size:.95rem} .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{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.running{background:rgba(34,197,94,.15);color:var(--neu-success)}
.badge.stopped{background:rgba(239,68,68,.1);color:var(--color-error,#ef4444)} .badge.stopped{background:rgba(239,68,68,.1);color:var(--neu-danger)}
.package-summary{font-size:.875rem} .package-summary{font-size:.875rem}
.muted{color:var(--text-muted)} .muted{color:var(--neu-text-muted)}
.up-to-date{color:var(--color-success,#22c55e)} .up-to-date{color:var(--neu-success)}
.has-updates{color:var(--color-warning,#f59e0b);font-weight:600} .has-updates{color:var(--neu-warning);font-weight:600}
.checking-text{display:flex;align-items:center;gap:.4rem;color:var(--text-secondary)} .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(--border-color);padding-top:.5rem;display:flex;flex-direction:column;gap:.25rem} .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} .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-name{font-weight:600;font-family:monospace;color:var(--neu-primary)}
.pkg-version{display:flex;align-items:center;gap:.25rem;color:var(--text-secondary)} .pkg-version{display:flex;align-items:center;gap:.25rem;color:var(--neu-text-muted)}
.old-ver{text-decoration:line-through;opacity:.6} .old-ver{text-decoration:line-through;opacity:.6}
.arrow{color:var(--accent-primary)} .arrow{color:var(--neu-primary)}
.new-ver{color:var(--color-success,#22c55e);font-weight:600} .new-ver{color:var(--neu-success);font-weight:600}
.target-actions{display:flex;gap:.5rem;margin-top:auto} .target-actions{display:flex;gap:.5rem;margin-top:auto}
.output-panel{margin-top:1.5rem;border-radius:.75rem;overflow:hidden} .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} .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{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.running{background:rgba(99,102,241,.15);color:var(--neu-primary)}
.job-status.success{background:rgba(34,197,94,.15);color:var(--color-success,#22c55e)} .job-status.success{background:rgba(34,197,94,.15);color:var(--neu-success)}
.job-status.error{background:rgba(239,68,68,.1);color:var(--color-error,#ef4444)} .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(--text-primary)} .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(--text-muted)} .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-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)}} @keyframes spin{to{transform:rotate(360deg)}}
</style> </style>
</body> </body>