Merge claude/1
This commit is contained in:
commit
1a1813f1ea
@ -242,6 +242,24 @@
|
|||||||
}
|
}
|
||||||
body:not(.lp-ui--advanced) .service-rich { display: none; }
|
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.
|
Beginner / Advanced toggle in the Services tab title row.
|
||||||
Project-wide visual; same component will be reused wherever
|
Project-wide visual; same component will be reused wherever
|
||||||
|
|||||||
@ -942,10 +942,13 @@ class AppTabbedManager {
|
|||||||
if (button.classList.contains('tab-button')) {
|
if (button.classList.contains('tab-button')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Logs toggle buttons stay clickable while a task runs — viewing
|
// Details + log-stream toggles stay clickable while a task runs —
|
||||||
// log output is read-only and the whole point during a long task.
|
// viewing service details and tailing logs is read-only and the
|
||||||
if (button.dataset.action === 'toggle-logs' ||
|
// whole point during a long task.
|
||||||
button.classList.contains('service-logs') ||
|
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')) {
|
button.classList.contains('toggle-details')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -428,7 +428,7 @@ class ServicesManager {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="task-item service-item" data-service="${escapeHtml(svc.serviceName)}" data-state="${state}">
|
<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">
|
<div class="task-info">
|
||||||
${iconHtml}
|
${iconHtml}
|
||||||
<span class="task-title">${escapeHtml(svc.serviceName)}</span>
|
<span class="task-title">${escapeHtml(svc.serviceName)}</span>
|
||||||
@ -446,11 +446,11 @@ class ServicesManager {
|
|||||||
</svg>
|
</svg>
|
||||||
<span class="task-btn-label">Restart</span>
|
<span class="task-btn-label">Restart</span>
|
||||||
</button>
|
</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">
|
<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>
|
<polyline points="6,9 12,15 18,9"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="task-btn-label">Logs</span>
|
<span class="task-btn-label">Details</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -462,7 +462,14 @@ 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>
|
||||||
<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="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;">
|
||||||
<div class="service-log-overlay-msg" style="color: #ddd; font-size: 13px;"></div>
|
<div class="service-log-overlay-msg" style="color: #ddd; font-size: 13px;"></div>
|
||||||
@ -472,7 +479,6 @@ class ServicesManager {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this._renderRichDetail(info)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
@ -494,8 +500,10 @@ class ServicesManager {
|
|||||||
|
|
||||||
if (action === 'restart') {
|
if (action === 'restart') {
|
||||||
await this._restartService(serviceName, btn);
|
await this._restartService(serviceName, btn);
|
||||||
} else if (action === 'toggle-logs') {
|
} else if (action === 'toggle-details') {
|
||||||
this._toggleLogs(item, serviceName);
|
this._toggleDetails(item, serviceName);
|
||||||
|
} else if (action === 'toggle-log-stream') {
|
||||||
|
this._toggleLogStream(item, serviceName);
|
||||||
} else if (action === 'resume-logs') {
|
} else if (action === 'resume-logs') {
|
||||||
this._resumeLogs(item, serviceName);
|
this._resumeLogs(item, serviceName);
|
||||||
}
|
}
|
||||||
@ -593,26 +601,61 @@ class ServicesManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_toggleLogs(item, serviceName) {
|
// Toggle the .task-details panel (meta + rich detail + log toggle).
|
||||||
// The task-list uses a .task-details-open class (not the `hidden`
|
// Logs are NOT auto-opened here — the user has to click "Show logs"
|
||||||
// attribute) because .task-details has `display: none` baked in.
|
// 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 details = item.querySelector('.task-details');
|
||||||
const output = item.querySelector('.service-log-output');
|
if (!details) return;
|
||||||
if (!details || !output) return;
|
|
||||||
|
|
||||||
const isOpen = details.classList.contains('task-details-open');
|
const isOpen = details.classList.contains('task-details-open');
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
details.classList.remove('task-details-open');
|
details.classList.remove('task-details-open');
|
||||||
this._closeLogStream(serviceName);
|
this._resetLogBlock(item, serviceName);
|
||||||
this._hideLogOverlay(output);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
details.classList.add('task-details-open');
|
logsBlock.style.display = '';
|
||||||
output.textContent = '';
|
output.textContent = '';
|
||||||
this._hideLogOverlay(output);
|
this._hideLogOverlay(output);
|
||||||
output.dataset.stream = 'connecting';
|
output.dataset.stream = 'connecting';
|
||||||
this._openLogStream(serviceName, output);
|
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) {
|
_openLogStream(serviceName, outputEl) {
|
||||||
|
|||||||
@ -247,7 +247,8 @@
|
|||||||
[data-theme="nebula"] .uninstall-btn,
|
[data-theme="nebula"] .uninstall-btn,
|
||||||
[data-theme="nebula"] .btn-uninstall,
|
[data-theme="nebula"] .btn-uninstall,
|
||||||
[data-theme="nebula"] .btn-danger,
|
[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;
|
background: rgba(var(--status-danger-rgb), 0.35) !important;
|
||||||
color: var(--text-primary) !important;
|
color: var(--text-primary) !important;
|
||||||
border: 1px solid rgba(var(--status-danger-rgb), 0.65) !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"] .uninstall-btn:hover:not(:disabled),
|
||||||
[data-theme="nebula"] .btn-uninstall:hover:not(:disabled),
|
[data-theme="nebula"] .btn-uninstall:hover:not(:disabled),
|
||||||
[data-theme="nebula"] .btn-danger: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;
|
background: rgba(var(--status-danger-rgb), 0.50) !important;
|
||||||
border-color: rgba(var(--status-danger-rgb), 0.85) !important;
|
border-color: rgba(var(--status-danger-rgb), 0.85) !important;
|
||||||
transform: translateY(-1px);
|
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"] .manage-btn,
|
||||||
[data-theme="nebula"] .btn-manage,
|
[data-theme="nebula"] .btn-manage,
|
||||||
[data-theme="nebula"] .btn-primary,
|
[data-theme="nebula"] .btn-primary,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user