Merge claude/2

This commit is contained in:
librelad 2026-05-27 23:09:07 +01:00
commit 51069ae05a
2 changed files with 132 additions and 4 deletions

View File

@ -231,7 +231,8 @@
Rich container detail panel limits, image, healthcheck,
networks, mounts. Rendered inside .task-details above the
log container so it's discoverable from the existing "Logs"
expand action.
expand action. Gated behind the global Advanced UI mode so
a Beginner doesn't see a wall of technical detail.
============================================================ */
.service-rich {
display: flex;
@ -239,6 +240,72 @@
gap: 14px;
margin: 8px 0 14px;
}
body:not(.lp-ui--advanced) .service-rich { display: none; }
/* ============================================================
Beginner / Advanced toggle in the Services tab title row.
Project-wide visual; same component will be reused wherever
else surfaces grow an Advanced-only section.
============================================================ */
.services-title {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
}
.services-title-main { flex: 1; min-width: 0; }
.lp-ui-advanced-toggle {
display: inline-flex;
align-items: center;
gap: 8px;
cursor: pointer;
user-select: none;
flex-shrink: 0;
}
.lp-ui-advanced-toggle input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.lp-ui-advanced-toggle-track {
position: relative;
width: 34px;
height: 18px;
background: rgba(var(--text-rgb), 0.18);
border-radius: 999px;
transition: background .15s ease;
flex-shrink: 0;
}
.lp-ui-advanced-toggle-thumb {
position: absolute;
top: 2px;
left: 2px;
width: 14px;
height: 14px;
background: var(--text-primary);
border-radius: 50%;
transition: transform .15s ease, background .15s ease;
}
.lp-ui-advanced-toggle input:checked + .lp-ui-advanced-toggle-track {
background: var(--accent);
}
.lp-ui-advanced-toggle input:checked + .lp-ui-advanced-toggle-track .lp-ui-advanced-toggle-thumb {
transform: translateX(16px);
background: var(--text-on-accent, #0a1426);
}
.lp-ui-advanced-toggle-label {
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.02em;
color: rgba(var(--text-rgb), 0.7);
transition: color .15s ease;
}
.lp-ui-advanced-toggle:hover .lp-ui-advanced-toggle-label { color: var(--text-primary); }
.lp-ui-advanced-toggle input:focus-visible + .lp-ui-advanced-toggle-track {
outline: 2px solid rgba(var(--accent-rgb), 0.6);
outline-offset: 2px;
}
.service-rich-grid {
display: grid;

View File

@ -1,3 +1,44 @@
// Global "UI mode" — Beginner vs Advanced. First foothold of a
// project-wide pattern: any surface that wants to render extra-technical
// detail (mounts, limits, internals, raw IDs, …) gates it on
// body.lp-ui--advanced. Default is OFF (Beginner) so a newcomer isn't
// overwhelmed; flipping the toggle reveals everything site-wide.
//
// The flag persists per-browser via localStorage. The install will later
// seed the initial value from CFG_INSTALL_LEVEL (Beginner | Advanced)
// chosen on the very first setup screen — once that lands, the body
// class is set server-side at template render time so there's no FOUC.
// Until then, the user picks it from any surface that exposes a toggle
// (currently the Services tab).
(function setupLpUi() {
if (window.LpUi && window.LpUi.advanced) return;
const KEY = 'lp.ui.advanced';
const advanced = {
get() {
try { return localStorage.getItem(KEY) === '1'; } catch { return false; }
},
set(on) {
const v = !!on;
try { localStorage.setItem(KEY, v ? '1' : '0'); } catch { /* private mode */ }
document.body && document.body.classList.toggle('lp-ui--advanced', v);
window.dispatchEvent(new CustomEvent('lp-ui-advanced-changed', { detail: { advanced: v } }));
},
apply() {
if (!document.body) return;
document.body.classList.toggle('lp-ui--advanced', this.get());
},
};
window.LpUi = { ...(window.LpUi || {}), advanced };
// Apply as early as possible — once the body exists, we drop the class
// in so any already-rendered surface (e.g. another tab re-mounted with
// .service-rich present) reflects the saved mode immediately.
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => advanced.apply(), { once: true });
} else {
advanced.apply();
}
})();
// Services tab on the app detail page.
//
// Each row renders a single docker compose service with:
@ -87,10 +128,23 @@ class ServicesManager {
_titleBlock(appName) {
const display = (appName || '').replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
const adv = window.LpUi?.advanced?.get() ? 'checked' : '';
// The toggle is the visible surface for the global "Advanced UI" mode
// ([[window.LpUi.advanced]]). Flipping it here unhides the rich
// container detail (limits, mounts, networks, healthcheck) across
// every service row. The body class drives the CSS show/hide, so
// other surfaces that opt into the same mode get it for free.
return `
<div class="services-title">
<div class="services-title-main">
<h3> Services</h3>
<p>Inspect, restart and tail logs for the docker compose services that make up ${escapeHtml(display)}</p>
</div>
<label class="lp-ui-advanced-toggle" title="Reveal container internals (limits, mounts, networks, healthcheck)">
<input type="checkbox" data-action="toggle-advanced" ${adv}>
<span class="lp-ui-advanced-toggle-track"><span class="lp-ui-advanced-toggle-thumb"></span></span>
<span class="lp-ui-advanced-toggle-label">Advanced</span>
</label>
</div>`;
}
@ -449,7 +503,6 @@ class ServicesManager {
<div class="meta-item"><strong>State:</strong> ${escapeHtml(state)}</div>
<div class="meta-item"><strong>Status:</strong> ${escapeHtml(svc.statusText)}</div>
</div>
${this._renderRichDetail(info)}
<div class="task-logs" style="position: relative;">
<div class="log-container terminal-style service-log-output" data-stream="off" style="height: 200px; overflow-y: auto; border: 1px solid #444; border-radius: 4px; padding: 10px; background-color: #1a1a1a;"></div>
<div class="service-log-overlay" style="position: absolute; inset: 0; display: none; flex-direction: column; align-items: center; justify-content: center; gap: 10px; background: rgba(20, 20, 24, 0.85); backdrop-filter: blur(2px); border-radius: 4px;">
@ -460,6 +513,7 @@ class ServicesManager {
</button>
</div>
</div>
${this._renderRichDetail(info)}
</div>
</div>`;
}
@ -487,6 +541,13 @@ class ServicesManager {
this._resumeLogs(item, serviceName);
}
});
// Advanced toggle lives in the title, not on a row — bind change here
// so it works regardless of which service rows are present.
root.addEventListener('change', (ev) => {
const cb = ev.target.closest('[data-action="toggle-advanced"]');
if (!cb || !window.LpUi?.advanced) return;
window.LpUi.advanced.set(cb.checked);
});
}
async _restartService(serviceName, btn) {