fix: couleurs icônes, boutons carrés, sidebar collapsée, langue, SW scope, LXC arrêté
Icônes : - Sidebar navItems : couleur distincte par section (iconStyle via data-binding) - Sidebar footer user : couleur primary - Navbar : logout → danger, soleil → amber, lune → blue - Panel widgets : œil visible → success, caché → muted Boutons : - `.neu-btn--sm:has(> i:only-child)` → carré 2rem×2rem automatiquement (theme, logout, mode édition) sans modifier le HTML Sidebar : - --sidebar-width-collapsed : 64px → 52px - Sidebar réduite : icônes centrées (justify-content center) Langue : - Setter vide sur `lang` dans navbar() pour corriger x-model avec getter (le @change gère la vraie mise à jour du store) Service Worker : - Enregistrement depuis /ws.sw.js (scope /) au lieu de /js/ws.sw.js (scope /js/) - build.mjs : copie ws.sw.js vers dist/ root en plus de dist/js/ LXC arrêté : - checkTarget() : skip si target.status !== 'running' → évite les 502 SSH Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7c57b0ff84
commit
b851dc61af
11 changed files with 53 additions and 15 deletions
|
|
@ -71,6 +71,10 @@ for (const f of fs.readdirSync('js')) {
|
||||||
fs.copyFileSync(`js/${f}`, `${dist}/js/${f}`)
|
fs.copyFileSync(`js/${f}`, `${dist}/js/${f}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Service Worker doit être servi depuis la racine pour avoir le bon scope
|
||||||
|
if (fs.existsSync('js/ws.sw.js')) {
|
||||||
|
fs.copyFileSync('js/ws.sw.js', `${dist}/ws.sw.js`)
|
||||||
|
}
|
||||||
|
|
||||||
// 5. Copy CSS
|
// 5. Copy CSS
|
||||||
for (const f of fs.readdirSync('css')) {
|
for (const f of fs.readdirSync('css')) {
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ input, select, textarea { font-family: inherit; font-size: inherit; }
|
||||||
|
|
||||||
/* Sidebar */
|
/* Sidebar */
|
||||||
--sidebar-width: 240px;
|
--sidebar-width: 240px;
|
||||||
--sidebar-width-collapsed: 64px;
|
--sidebar-width-collapsed: 52px;
|
||||||
|
|
||||||
/* Z-index */
|
/* Z-index */
|
||||||
--z-sidebar: 100;
|
--z-sidebar: 100;
|
||||||
|
|
@ -215,6 +215,14 @@ input, select, textarea { font-family: inherit; font-size: inherit; }
|
||||||
border-radius: var(--neu-radius-md);
|
border-radius: var(--neu-radius-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bouton icône carré taille sm — se déclenche automatiquement si le bouton
|
||||||
|
ne contient qu'un <i> (ex: theme, logout, edit mode) */
|
||||||
|
.neu-btn--sm:has(> i:only-child) {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.neu-btn--ghost {
|
.neu-btn--ghost {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
@ -423,6 +431,12 @@ input, select, textarea { font-family: inherit; font-size: inherit; }
|
||||||
width: var(--sidebar-width-collapsed);
|
width: var(--sidebar-width-collapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar.collapsed .sidebar-link {
|
||||||
|
justify-content: center;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -619,3 +633,13 @@ html.is-animating .transition-fade {
|
||||||
margin-right: var(--sidebar-width-collapsed);
|
margin-right: var(--sidebar-width-collapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Couleurs icônes contextuelles ─────────────────────────────────────────── */
|
||||||
|
|
||||||
|
/* Navbar : logout, thème, édition */
|
||||||
|
.navbar .lnid-power-button { color: var(--neu-danger); }
|
||||||
|
.navbar .lnid-sun-1 { color: #f59e0b; }
|
||||||
|
.navbar .lnid-moon-half-left-1 { color: #60a5fa; }
|
||||||
|
|
||||||
|
/* Sidebar footer : utilisateur */
|
||||||
|
.sidebar-footer .sidebar-icon { color: var(--neu-primary); }
|
||||||
|
|
|
||||||
|
|
@ -621,6 +621,7 @@
|
||||||
transition: color .15s;
|
transition: color .15s;
|
||||||
}
|
}
|
||||||
.widget-panel-toggle:hover { color: var(--neu-primary); }
|
.widget-panel-toggle:hover { color: var(--neu-primary); }
|
||||||
|
.widget-panel-item.is-visible .widget-panel-toggle { color: var(--neu-success); }
|
||||||
|
|
||||||
/* Resize handle (coin bas-droit de la tuile, edit mode) */
|
/* Resize handle (coin bas-droit de la tuile, edit mode) */
|
||||||
.widget-resize-handle {
|
.widget-resize-handle {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<template x-for="item in navItems" :key="item.id">
|
<template x-for="item in navItems" :key="item.id">
|
||||||
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
||||||
<i class="sidebar-icon" :class="item.iconClass"></i>
|
<i class="sidebar-icon" :class="item.iconClass" :style="iconStyle(item)"></i>
|
||||||
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const WsProxy = {
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
if (!('serviceWorker' in navigator)) return
|
if (!('serviceWorker' in navigator)) return
|
||||||
navigator.serviceWorker.register('/js/ws.sw.js')
|
navigator.serviceWorker.register('/ws.sw.js')
|
||||||
.catch(e => console.warn('[WsProxy] SW registration failed:', e))
|
.catch(e => console.warn('[WsProxy] SW registration failed:', e))
|
||||||
navigator.serviceWorker.addEventListener('message', event => {
|
navigator.serviceWorker.addEventListener('message', event => {
|
||||||
const { channel, type, data, status } = event.data || {}
|
const { channel, type, data, status } = event.data || {}
|
||||||
|
|
@ -253,14 +253,18 @@ document.addEventListener('alpine:init', () => {
|
||||||
get currentPage() { return Alpine.store('ui').currentPage },
|
get currentPage() { return Alpine.store('ui').currentPage },
|
||||||
|
|
||||||
navItems: [
|
navItems: [
|
||||||
{ id: 'dashboard', iconClass: 'lnid-dashboard-square-1', labelKey: 'nav.dashboard', href: '/dashboard.html' },
|
{ id: 'dashboard', iconClass: 'lnid-dashboard-square-1', iconColor: '#6c8ef4', labelKey: 'nav.dashboard', href: '/dashboard.html' },
|
||||||
{ id: 'proxmox', iconClass: 'lnid-server-1', labelKey: 'nav.proxmox', href: '/proxmox.html' },
|
{ id: 'proxmox', iconClass: 'lnid-server-1', iconColor: '#22c55e', labelKey: 'nav.proxmox', href: '/proxmox.html' },
|
||||||
{ id: 'updates', iconClass: 'lnid-arrow-upward', labelKey: 'nav.updates', href: '/updates.html' },
|
{ id: 'updates', iconClass: 'lnid-arrow-upward', iconColor: '#f59e0b', labelKey: 'nav.updates', href: '/updates.html' },
|
||||||
{ id: 'terminal', iconClass: 'lnid-terminal', labelKey: 'nav.terminal', href: '/terminal.html' },
|
{ id: 'terminal', iconClass: 'lnid-terminal', iconColor: '#a78bfa', labelKey: 'nav.terminal', href: '/terminal.html' },
|
||||||
{ id: 'settings', iconClass: 'lnid-gear-1', labelKey: 'nav.settings', href: '/settings.html' },
|
{ id: 'settings', iconClass: 'lnid-gear-1', iconColor: '#94a3b8', labelKey: 'nav.settings', href: '/settings.html' },
|
||||||
{ id: 'modules', iconClass: 'lnid-puzzle', labelKey: 'nav.modules', href: '/modules.html' },
|
{ id: 'modules', iconClass: 'lnid-puzzle', iconColor: '#f472b6', labelKey: 'nav.modules', href: '/modules.html' },
|
||||||
],
|
],
|
||||||
|
|
||||||
|
iconStyle(item) {
|
||||||
|
return this.isActive(item.id) ? '' : `color: ${item.iconColor}`
|
||||||
|
},
|
||||||
|
|
||||||
isActive(id) {
|
isActive(id) {
|
||||||
return this.currentPage === id
|
return this.currentPage === id
|
||||||
},
|
},
|
||||||
|
|
@ -282,6 +286,7 @@ document.addEventListener('alpine:init', () => {
|
||||||
get theme() { return Alpine.store('ui').theme },
|
get theme() { return Alpine.store('ui').theme },
|
||||||
get user() { return Alpine.store('auth').user },
|
get user() { return Alpine.store('auth').user },
|
||||||
get lang() { return Alpine.store('i18n').lang },
|
get lang() { return Alpine.store('i18n').lang },
|
||||||
|
set lang(v) { /* x-model a besoin d'un setter ; @change gère la vraie MAJ */ },
|
||||||
|
|
||||||
toggleTheme() { Alpine.store('ui').toggleTheme() },
|
toggleTheme() { Alpine.store('ui').toggleTheme() },
|
||||||
logout() { Alpine.store('auth').logout() },
|
logout() { Alpine.store('auth').logout() },
|
||||||
|
|
@ -657,15 +662,19 @@ document.addEventListener('alpine:init', () => {
|
||||||
},
|
},
|
||||||
|
|
||||||
async checkTarget(target) {
|
async checkTarget(target) {
|
||||||
|
if (target.status !== 'running') return // container arrêté → pas de SSH possible
|
||||||
target.checking = true
|
target.checking = true
|
||||||
target.packages = null
|
target.packages = null
|
||||||
try {
|
try {
|
||||||
const res = await apiFetch(`/api/updates/packages?target=${encodeURIComponent(target.id)}`)
|
const res = await apiFetch(`/api/updates/packages?target=${encodeURIComponent(target.id)}`)
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
target.packages = await res.json()
|
target.packages = await res.json()
|
||||||
|
} else {
|
||||||
|
target.packages = []
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error('checkTarget', e)
|
console.error('checkTarget', e)
|
||||||
|
target.packages = []
|
||||||
} finally {
|
} finally {
|
||||||
target.checking = false
|
target.checking = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<template x-for="item in navItems" :key="item.id">
|
<template x-for="item in navItems" :key="item.id">
|
||||||
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
||||||
<i class="sidebar-icon" :class="item.iconClass"></i>
|
<i class="sidebar-icon" :class="item.iconClass" :style="iconStyle(item)"></i>
|
||||||
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<template x-for="item in navItems" :key="item.id">
|
<template x-for="item in navItems" :key="item.id">
|
||||||
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
||||||
<i class="sidebar-icon" :class="item.iconClass"></i>
|
<i class="sidebar-icon" :class="item.iconClass" :style="iconStyle(item)"></i>
|
||||||
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<template x-for="item in navItems" :key="item.id">
|
<template x-for="item in navItems" :key="item.id">
|
||||||
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
||||||
<i class="sidebar-icon" :class="item.iconClass"></i>
|
<i class="sidebar-icon" :class="item.iconClass" :style="iconStyle(item)"></i>
|
||||||
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<template x-for="item in navItems" :key="item.id">
|
<template x-for="item in navItems" :key="item.id">
|
||||||
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
||||||
<i class="sidebar-icon" :class="item.iconClass"></i>
|
<i class="sidebar-icon" :class="item.iconClass" :style="iconStyle(item)"></i>
|
||||||
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<template x-for="item in navItems" :key="item.id">
|
<template x-for="item in navItems" :key="item.id">
|
||||||
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
||||||
<i class="sidebar-icon" :class="item.iconClass"></i>
|
<i class="sidebar-icon" :class="item.iconClass" :style="iconStyle(item)"></i>
|
||||||
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
<nav class="sidebar-nav">
|
<nav class="sidebar-nav">
|
||||||
<template x-for="item in navItems" :key="item.id">
|
<template x-for="item in navItems" :key="item.id">
|
||||||
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
<a class="sidebar-link" :class="{ active: isActive(item.id) }" :href="item.href" @click.prevent="navigate(item.href)">
|
||||||
<i class="sidebar-icon" :class="item.iconClass"></i>
|
<i class="sidebar-icon" :class="item.iconClass" :style="iconStyle(item)"></i>
|
||||||
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
<span class="sidebar-label" x-show="!collapsed" x-text="t(item.labelKey)"></span>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue