Merge claude/1
This commit is contained in:
commit
1a1813f1ea
@ -242,6 +242,24 @@
|
||||
}
|
||||
body:not(.lp-ui--advanced) .service-rich { display: none; }
|
||||
|
||||
/* "Show logs" / "Hide logs" toggle sitting at the very bottom of the
|
||||
open .task-details panel. Logs are off by default — the user opts in
|
||||
per service so a row of expanded services doesn't open one SSE stream
|
||||
each. Centred and given a little top breathing room so it reads as
|
||||
a footer action rather than another inline button. */
|
||||
.service-logs-toggle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.service-logs-toggle .service-show-logs {
|
||||
padding: 6px 14px;
|
||||
}
|
||||
.service-item.logs-shown .service-logs-toggle {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Beginner / Advanced toggle in the Services tab title row.
|
||||
Project-wide visual; same component will be reused wherever
|
||||
|
||||
@ -942,10 +942,13 @@ class AppTabbedManager {
|
||||
if (button.classList.contains('tab-button')) {
|
||||
return;
|
||||
}
|
||||
// Logs toggle buttons stay clickable while a task runs — viewing
|
||||
// log output is read-only and the whole point during a long task.
|
||||
if (button.dataset.action === 'toggle-logs' ||
|
||||
button.classList.contains('service-logs') ||
|
||||
// Details + log-stream toggles stay clickable while a task runs —
|
||||
// viewing service details and tailing logs is read-only and the
|
||||
// whole point during a long task.
|
||||
if (button.dataset.action === 'toggle-details' ||
|
||||
button.dataset.action === 'toggle-log-stream' ||
|
||||
button.classList.contains('service-details') ||
|
||||
button.classList.contains('service-show-logs') ||
|
||||
button.classList.contains('toggle-details')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -428,7 +428,7 @@ class ServicesManager {
|
||||
|
||||
return `
|
||||
<div class="task-item service-item" data-service="${escapeHtml(svc.serviceName)}" data-state="${state}">
|
||||
<div class="task-header" data-action="toggle-logs">
|
||||
<div class="task-header" data-action="toggle-details">
|
||||
<div class="task-info">
|
||||
${iconHtml}
|
||||
<span class="task-title">${escapeHtml(svc.serviceName)}</span>
|
||||
@ -446,11 +446,11 @@ class ServicesManager {
|
||||
</svg>
|
||||
<span class="task-btn-label">Restart</span>
|
||||
</button>
|
||||
<button class="task-btn toggle-details service-logs" data-action="toggle-logs" title="Toggle live logs">
|
||||
<button class="task-btn toggle-details service-details" data-action="toggle-details" title="Show service details">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="6,9 12,15 18,9"></polyline>
|
||||
</svg>
|
||||
<span class="task-btn-label">Logs</span>
|
||||
<span class="task-btn-label">Details</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -462,7 +462,14 @@ class ServicesManager {
|
||||
<div class="meta-item"><strong>State:</strong> ${escapeHtml(state)}</div>
|
||||
<div class="meta-item"><strong>Status:</strong> ${escapeHtml(svc.statusText)}</div>
|
||||
</div>
|
||||
<div class="task-logs" style="position: relative;">
|
||||
${this._renderRichDetail(info)}
|
||||
<div class="service-logs-toggle">
|
||||
<button type="button" class="task-btn service-show-logs" data-action="toggle-log-stream" title="Tail this container's logs">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
|
||||
<span class="task-btn-label">Show logs</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="task-logs" style="position: relative; display: none;">
|
||||
<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-msg" style="color: #ddd; font-size: 13px;"></div>
|
||||
@ -472,7 +479,6 @@ class ServicesManager {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
${this._renderRichDetail(info)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
@ -494,8 +500,10 @@ class ServicesManager {
|
||||
|
||||
if (action === 'restart') {
|
||||
await this._restartService(serviceName, btn);
|
||||
} else if (action === 'toggle-logs') {
|
||||
this._toggleLogs(item, serviceName);
|
||||
} else if (action === 'toggle-details') {
|
||||
this._toggleDetails(item, serviceName);
|
||||
} else if (action === 'toggle-log-stream') {
|
||||
this._toggleLogStream(item, serviceName);
|
||||
} else if (action === 'resume-logs') {
|
||||
this._resumeLogs(item, serviceName);
|
||||
}
|
||||
@ -593,26 +601,61 @@ class ServicesManager {
|
||||
}
|
||||
}
|
||||
|
||||
_toggleLogs(item, serviceName) {
|
||||
// The task-list uses a .task-details-open class (not the `hidden`
|
||||
// attribute) because .task-details has `display: none` baked in.
|
||||
// Toggle the .task-details panel (meta + rich detail + log toggle).
|
||||
// Logs are NOT auto-opened here — the user has to click "Show logs"
|
||||
// explicitly. Closing the panel also tears down any open log stream
|
||||
// and resets the inline log block back to its hidden state.
|
||||
_toggleDetails(item, serviceName) {
|
||||
const details = item.querySelector('.task-details');
|
||||
const output = item.querySelector('.service-log-output');
|
||||
if (!details || !output) return;
|
||||
if (!details) return;
|
||||
|
||||
const isOpen = details.classList.contains('task-details-open');
|
||||
if (isOpen) {
|
||||
details.classList.remove('task-details-open');
|
||||
this._closeLogStream(serviceName);
|
||||
this._hideLogOverlay(output);
|
||||
this._resetLogBlock(item, serviceName);
|
||||
return;
|
||||
}
|
||||
details.classList.add('task-details-open');
|
||||
}
|
||||
|
||||
// Show / hide the log block inside the open details panel. Opening
|
||||
// starts the SSE stream so logs auto-update; closing tears it down.
|
||||
_toggleLogStream(item, serviceName) {
|
||||
const logsBlock = item.querySelector('.task-logs');
|
||||
const output = item.querySelector('.service-log-output');
|
||||
if (!logsBlock || !output) return;
|
||||
|
||||
const showing = item.classList.contains('logs-shown');
|
||||
if (showing) {
|
||||
this._resetLogBlock(item, serviceName);
|
||||
return;
|
||||
}
|
||||
|
||||
details.classList.add('task-details-open');
|
||||
logsBlock.style.display = '';
|
||||
output.textContent = '';
|
||||
this._hideLogOverlay(output);
|
||||
output.dataset.stream = 'connecting';
|
||||
this._openLogStream(serviceName, output);
|
||||
item.classList.add('logs-shown');
|
||||
this._setLogToggleLabel(item, 'Hide logs');
|
||||
}
|
||||
|
||||
// Tear down the log stream and put the block back to its closed state.
|
||||
// Used both when the user clicks "Hide logs" and when the parent
|
||||
// details panel collapses (so reopening starts in the clean state).
|
||||
_resetLogBlock(item, serviceName) {
|
||||
const logsBlock = item.querySelector('.task-logs');
|
||||
const output = item.querySelector('.service-log-output');
|
||||
if (output) this._hideLogOverlay(output);
|
||||
if (logsBlock) logsBlock.style.display = 'none';
|
||||
this._closeLogStream(serviceName);
|
||||
item.classList.remove('logs-shown');
|
||||
this._setLogToggleLabel(item, 'Show logs');
|
||||
}
|
||||
|
||||
_setLogToggleLabel(item, text) {
|
||||
const lbl = item.querySelector('.service-show-logs .task-btn-label');
|
||||
if (lbl) lbl.textContent = text;
|
||||
}
|
||||
|
||||
_openLogStream(serviceName, outputEl) {
|
||||
|
||||
@ -247,7 +247,8 @@
|
||||
[data-theme="nebula"] .uninstall-btn,
|
||||
[data-theme="nebula"] .btn-uninstall,
|
||||
[data-theme="nebula"] .btn-danger,
|
||||
[data-theme="nebula"] .backup-danger-btn {
|
||||
[data-theme="nebula"] .backup-danger-btn,
|
||||
[data-theme="nebula"] .tool-run-btn.destructive {
|
||||
background: rgba(var(--status-danger-rgb), 0.35) !important;
|
||||
color: var(--text-primary) !important;
|
||||
border: 1px solid rgba(var(--status-danger-rgb), 0.65) !important;
|
||||
@ -259,12 +260,34 @@
|
||||
[data-theme="nebula"] .uninstall-btn:hover:not(:disabled),
|
||||
[data-theme="nebula"] .btn-uninstall:hover:not(:disabled),
|
||||
[data-theme="nebula"] .btn-danger:hover:not(:disabled),
|
||||
[data-theme="nebula"] .backup-danger-btn:hover:not(:disabled) {
|
||||
[data-theme="nebula"] .backup-danger-btn:hover:not(:disabled),
|
||||
[data-theme="nebula"] .tool-run-btn.destructive:hover:not(:disabled) {
|
||||
background: rgba(var(--status-danger-rgb), 0.50) !important;
|
||||
border-color: rgba(var(--status-danger-rgb), 0.85) !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Tools-page Run buttons — same translucent recipe as service-trigger
|
||||
(the small "Open" pill on installed app cards). The 0.12/0.30 alphas
|
||||
from tools.css read as muddy green/red against Nebula's cosmic
|
||||
gradient, especially with --status-success/danger text — bump to
|
||||
0.35/0.65 with neutral text so they pop. .destructive picks up the
|
||||
danger recipe above via the chained selector. */
|
||||
[data-theme="nebula"] .tool-run-btn {
|
||||
background: rgba(var(--status-success-rgb), 0.35) !important;
|
||||
color: var(--text-primary) !important;
|
||||
border: 1px solid rgba(var(--status-success-rgb), 0.65) !important;
|
||||
text-shadow: none;
|
||||
font-weight: 600 !important;
|
||||
transition: background 0.18s ease, border-color 0.18s ease, transform 0.15s ease !important;
|
||||
}
|
||||
|
||||
[data-theme="nebula"] .tool-run-btn:hover:not(:disabled):not(.destructive) {
|
||||
background: rgba(var(--status-success-rgb), 0.50) !important;
|
||||
border-color: rgba(var(--status-success-rgb), 0.85) !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
[data-theme="nebula"] .manage-btn,
|
||||
[data-theme="nebula"] .btn-manage,
|
||||
[data-theme="nebula"] .btn-primary,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user