From 7946129a75c38b378f438e49a2e622713449807a Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 1 Jun 2026 19:44:44 +0200 Subject: [PATCH] web: start: move css to dedicated file Signed-off-by: GnomeZworc --- web/components/date-display/date-display.css | 38 +++++++ web/components/date-display/date-display.js | 40 +------ web/components/login-gate/login-gate.css | 81 ++++++++++++++ web/components/login-gate/login-gate.js | 103 +----------------- .../logout-button/logout-button.css | 30 +++++ web/components/logout-button/logout-button.js | 46 +------- web/components/side-nav/side-nav.css | 40 +++++++ web/components/side-nav/side-nav.js | 42 +------ web/components/side-panel/side-panel.css | 83 ++++++++++++++ web/components/side-panel/side-panel.js | 94 +--------------- 10 files changed, 288 insertions(+), 309 deletions(-) create mode 100644 web/components/date-display/date-display.css create mode 100644 web/components/login-gate/login-gate.css create mode 100644 web/components/logout-button/logout-button.css create mode 100644 web/components/side-nav/side-nav.css create mode 100644 web/components/side-panel/side-panel.css diff --git a/web/components/date-display/date-display.css b/web/components/date-display/date-display.css new file mode 100644 index 0000000..06c3390 --- /dev/null +++ b/web/components/date-display/date-display.css @@ -0,0 +1,38 @@ +:host { + display: block; + background: #1e1e2e; + border: 1px solid #313244; + border-radius: 8px; + padding: 16px 20px; + font-family: monospace; + color: #cdd6f4; + min-width: 260px; +} + +.label { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.08em; + color: #6c7086; + margin-bottom: 6px; +} + +.time { + font-size: 28px; + font-weight: 600; + color: #89b4fa; +} + +.date { + font-size: 13px; + color: #a6adc8; + margin-top: 4px; +} + +.agent { + margin-top: 12px; + font-size: 11px; + color: #585b70; + border-top: 1px solid #313244; + padding-top: 10px; +} diff --git a/web/components/date-display/date-display.js b/web/components/date-display/date-display.js index dab208e..c008c93 100644 --- a/web/components/date-display/date-display.js +++ b/web/components/date-display/date-display.js @@ -1,53 +1,19 @@ import { config } from '../../core/config.js' +const CSS = new URL('./date-display.css', import.meta.url).href + class DateDisplay extends HTMLElement { #interval = null connectedCallback() { this.attachShadow({ mode: 'open' }) this.shadowRoot.innerHTML = ` - +
Current time
agent →
` - this.shadowRoot.getElementById('agent-url').textContent = config.agent_url this.#tick() this.#interval = setInterval(() => this.#tick(), 1000) diff --git a/web/components/login-gate/login-gate.css b/web/components/login-gate/login-gate.css new file mode 100644 index 0000000..f843d38 --- /dev/null +++ b/web/components/login-gate/login-gate.css @@ -0,0 +1,81 @@ +* { box-sizing: border-box; margin: 0; padding: 0; } + +.overlay { + position: fixed; + inset: 0; + background: #181825; + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; +} + +.card { + background: #1e1e2e; + border: 1px solid #313244; + border-radius: 12px; + padding: 36px 40px; + width: 100%; + max-width: 420px; + display: flex; + flex-direction: column; + gap: 24px; +} + +@media (max-width: 480px) { + .card { padding: 24px 20px; margin: 0 12px; } +} + +.header { display: flex; flex-direction: column; gap: 6px; } + +.header h1 { + font-size: 20px; + font-weight: 600; + color: #89b4fa; + letter-spacing: 0.02em; +} + +.header p { font-size: 13px; color: #6c7086; } + +.fields { display: flex; flex-direction: column; gap: 14px; } +.field { display: flex; flex-direction: column; gap: 6px; } + +label { + font-size: 12px; + font-weight: 500; + color: #a6adc8; + text-transform: uppercase; + letter-spacing: 0.06em; +} + +input { + background: #181825; + border: 1px solid #313244; + border-radius: 6px; + padding: 9px 12px; + color: #cdd6f4; + font-size: 14px; + font-family: monospace; + outline: none; + transition: border-color 0.15s; + width: 100%; +} + +input:focus { border-color: #89b4fa; } +input::placeholder { color: #45475a; } + +button { + background: #89b4fa; + color: #1e1e2e; + border: none; + border-radius: 6px; + padding: 10px 16px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: background 0.15s; + width: 100%; +} + +button:hover { background: #b4d0fa; } diff --git a/web/components/login-gate/login-gate.js b/web/components/login-gate/login-gate.js index 247fa06..1de65de 100644 --- a/web/components/login-gate/login-gate.js +++ b/web/components/login-gate/login-gate.js @@ -18,6 +18,7 @@ // Phase 2: swap for that resolves ready with a JWT — api-client unchanged. const SESSION_KEY = 'two:credentials' +const CSS = new URL('./login-gate.css', import.meta.url).href class LoginGate extends HTMLElement { #resolve = null @@ -57,107 +58,7 @@ class LoginGate extends HTMLElement { const overlay = document.createElement('div') overlay.attachShadow({ mode: 'open' }) overlay.shadowRoot.innerHTML = ` - - +
diff --git a/web/components/logout-button/logout-button.css b/web/components/logout-button/logout-button.css new file mode 100644 index 0000000..2a6aa10 --- /dev/null +++ b/web/components/logout-button/logout-button.css @@ -0,0 +1,30 @@ +:host { + display: inline-flex; +} + +button { + display: inline-flex; + align-items: center; + gap: 7px; + background: transparent; + border: 1px solid #313244; + border-radius: 6px; + padding: 7px 12px; + color: #a6adc8; + font-size: 13px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + cursor: pointer; + transition: background 0.12s, color 0.12s, border-color 0.12s; +} + +button:hover { + background: #313244; + color: #f38ba8; + border-color: #f38ba8; +} + +.icon { display: flex; align-items: center; } + +/* compact attribute — icon only */ +:host([compact]) button { padding: 7px; } +:host([compact]) .label { display: none; } diff --git a/web/components/logout-button/logout-button.js b/web/components/logout-button/logout-button.js index 45aa80f..69b651f 100644 --- a/web/components/logout-button/logout-button.js +++ b/web/components/logout-button/logout-button.js @@ -1,63 +1,21 @@ -// Logout button — drop anywhere in the DOM. -// -// full button with label -// icon only (for tight spaces) -// -// Delegates to .logout(). Works regardless of where it sits -// (header, members panel, dropdown…) since it resolves the gate from the document. -// -// Phase 2: if login-gate is swapped for oidc-gate, this still works as long as -// the auth component exposes a logout() method (see #gate()). +const CSS = new URL('./logout-button.css', import.meta.url).href const ICON = `` class LogoutButton extends HTMLElement { connectedCallback() { this.attachShadow({ mode: 'open' }) - - const compact = this.hasAttribute('compact') - this.shadowRoot.innerHTML = ` - - + ` - this.shadowRoot.querySelector('button') .addEventListener('click', () => this.#logout()) } - // Resolves the auth component. Today: login-gate. Tomorrow: any element - // exposing logout() (oidc-gate, oauth-gate…). #gate() { return document.querySelector('login-gate, [data-auth-gate]') } diff --git a/web/components/side-nav/side-nav.css b/web/components/side-nav/side-nav.css new file mode 100644 index 0000000..6e371c1 --- /dev/null +++ b/web/components/side-nav/side-nav.css @@ -0,0 +1,40 @@ +:host { + display: flex; + flex-direction: column; + width: 220px; + min-width: 220px; + background: #1e1e2e; + border-right: 1px solid #313244; + padding: 16px 12px; + gap: 4px; + height: 100%; + overflow: hidden; + box-sizing: border-box; +} + +.item { + display: flex; + align-items: center; + gap: 10px; + padding: 11px 12px; + border-radius: 6px; + text-decoration: none; + color: #a6adc8; + font-size: 14px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + min-height: 44px; + transition: background 0.1s, color 0.1s; +} + +.item:hover { background: #313244; color: #cdd6f4; } +.item.active { background: #313244; color: #89b4fa; } +.item.active .icon { color: #89b4fa; } + +.icon { + display: flex; + align-items: center; + color: #6c7086; + flex-shrink: 0; +} + +.item:hover .icon { color: #cdd6f4; } diff --git a/web/components/side-nav/side-nav.js b/web/components/side-nav/side-nav.js index b96f236..8eb9e61 100644 --- a/web/components/side-nav/side-nav.js +++ b/web/components/side-nav/side-nav.js @@ -18,7 +18,8 @@ const ICONS = { vm: ``, } -const MQ = window.matchMedia('(max-width: 768px)') +const CSS = new URL('./side-nav.css', import.meta.url).href +const MQ = window.matchMedia('(max-width: 768px)') const NAV_W = 220 // px function resolveIcon(name) { @@ -67,44 +68,7 @@ class SideNav extends HTMLElement { }).join('') this.shadowRoot.innerHTML = ` - + ${links} ` diff --git a/web/components/side-panel/side-panel.css b/web/components/side-panel/side-panel.css new file mode 100644 index 0000000..017585c --- /dev/null +++ b/web/components/side-panel/side-panel.css @@ -0,0 +1,83 @@ +:host { + --width: 480px; /* overridden via this.style.setProperty in JS */ + --offset: 0px; + --dur: 220ms; + --actual-width: min(var(--width), 100vw); + + position: fixed; + top: 0; + right: calc(-1 * var(--actual-width)); + width: var(--actual-width); + height: 100vh; + background: #1e1e2e; + border-left: 1px solid #313244; + box-shadow: -8px 0 32px rgba(0, 0, 0, 0.4); + display: flex; + flex-direction: column; + z-index: calc(1000 + var(--stack, 0)); + transition: right var(--dur) cubic-bezier(0.4, 0, 0.2, 1); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + color: #cdd6f4; +} + +:host([data-open]) { + right: var(--offset); +} + +@media (max-width: 600px) { + :host { + --actual-width: 100vw; + } + :host([data-open]) { + right: 0; + } +} + +.header { + display: flex; + align-items: center; + gap: 12px; + padding: 16px 20px; + border-bottom: 1px solid #313244; + flex-shrink: 0; +} + +.title { + flex: 1; + font-size: 15px; + font-weight: 600; + color: #cdd6f4; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.close { + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px solid transparent; + border-radius: 6px; + padding: 5px; + color: #6c7086; + cursor: pointer; + transition: background 0.12s, color 0.12s, border-color 0.12s; + flex-shrink: 0; +} + +.close:hover { + background: #313244; + border-color: #45475a; + color: #f38ba8; +} + +.body { + flex: 1; + overflow-y: auto; + padding: 20px; +} + +.body::-webkit-scrollbar { width: 6px; } +.body::-webkit-scrollbar-track { background: transparent; } +.body::-webkit-scrollbar-thumb { background: #45475a; border-radius: 3px; } diff --git a/web/components/side-panel/side-panel.js b/web/components/side-panel/side-panel.js index 254160f..63e68c9 100644 --- a/web/components/side-panel/side-panel.js +++ b/web/components/side-panel/side-panel.js @@ -25,6 +25,8 @@ // Clicking the backdrop closes only the topmost panel. // ESC closes the topmost panel. +const CSS = new URL('./side-panel.css', import.meta.url).href + const CLOSE_ICON = ` @@ -103,95 +105,11 @@ export class SidePanel extends HTMLElement { const title = this.getAttribute('title') ?? '' const width = this.getAttribute('width') ?? '480px' + // Passe la largeur comme custom property — référencée dans le CSS via var(--width) + this.style.setProperty('--width', width) + this.shadowRoot.innerHTML = ` - - +
${title}