Compare commits

..

No commits in common. "51069ae05a223d20303fbb051dace3f445ed980b" and "092c1c215e75e5be347b5fb93af18d55f995dd8d" have entirely different histories.

2 changed files with 4 additions and 132 deletions

View File

@ -231,8 +231,7 @@
Rich container detail panel limits, image, healthcheck, Rich container detail panel limits, image, healthcheck,
networks, mounts. Rendered inside .task-details above the networks, mounts. Rendered inside .task-details above the
log container so it's discoverable from the existing "Logs" log container so it's discoverable from the existing "Logs"
expand action. Gated behind the global Advanced UI mode so expand action.
a Beginner doesn't see a wall of technical detail.
============================================================ */ ============================================================ */
.service-rich { .service-rich {
display: flex; display: flex;
@ -240,72 +239,6 @@
gap: 14px; gap: 14px;
margin: 8px 0 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 { .service-rich-grid {
display: grid; display: grid;

View File

@ -1,44 +1,3 @@
// 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. // Services tab on the app detail page.
// //
// Each row renders a single docker compose service with: // Each row renders a single docker compose service with:
@ -128,23 +87,10 @@ class ServicesManager {
_titleBlock(appName) { _titleBlock(appName) {
const display = (appName || '').replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); 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 ` return `
<div class="services-title"> <div class="services-title">
<div class="services-title-main"> <h3> Services</h3>
<h3> Services</h3> <p>Inspect, restart and tail logs for the docker compose services that make up ${escapeHtml(display)}</p>
<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>`; </div>`;
} }
@ -503,6 +449,7 @@ class ServicesManager {
<div class="meta-item"><strong>State:</strong> ${escapeHtml(state)}</div> <div class="meta-item"><strong>State:</strong> ${escapeHtml(state)}</div>
<div class="meta-item"><strong>Status:</strong> ${escapeHtml(svc.statusText)}</div> <div class="meta-item"><strong>Status:</strong> ${escapeHtml(svc.statusText)}</div>
</div> </div>
${this._renderRichDetail(info)}
<div class="task-logs" style="position: relative;"> <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="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;"> <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;">
@ -513,7 +460,6 @@ class ServicesManager {
</button> </button>
</div> </div>
</div> </div>
${this._renderRichDetail(info)}
</div> </div>
</div>`; </div>`;
} }
@ -541,13 +487,6 @@ class ServicesManager {
this._resumeLogs(item, serviceName); 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) { async _restartService(serviceName, btn) {