feat: add automation control center UI

Preserve monotonic source-cleanup requirements while excluding ambiguous job identities from Main payloads, and reconcile mixed add responses only across globally unique jobs that were actually sent.

Render the automation status card, localized fixed-column metrics, read-only test overlay, queue-limit and reconciliation controls, and persistent finish-and-pause or resume action from immutable automation snapshots.

Add hidden Electron coverage for cleanup siblings, Main fingerprints, German and English states, keyboard focus, Escape and inert behavior, responsive 760-pixel layouts, and snapshot-gated upload controls.
This commit is contained in:
Sucukdeluxe
2026-08-26 13:20:40 +02:00
parent 28a475401e
commit 325a20d3fb
5 changed files with 1372 additions and 111 deletions
+125 -1
View File
@@ -1258,6 +1258,114 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
border-bottom: 1px solid var(--border);
}
.settings-page-header + .settings-section-label { margin-top: 0; }
.automation-status-card {
min-width: 0;
margin: 0 0 22px;
padding: 14px;
border: 1px solid var(--border);
border-radius: 10px;
background: linear-gradient(145deg, color-mix(in srgb, var(--bg-raised) 94%, var(--accent) 6%), var(--bg-secondary));
box-shadow: 0 14px 32px rgba(0, 0, 0, .14);
}
.automation-status-card * { min-width: 0; }
.automation-status-header {
display: grid;
grid-template-columns: auto minmax(240px, 1fr);
align-items: center;
gap: 14px;
padding-bottom: 12px;
border-bottom: 1px solid var(--border);
}
.automation-state-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 26px;
padding: 4px 10px;
border: 1px solid transparent;
border-radius: 999px;
font-size: 11px;
font-weight: 800;
line-height: 1.2;
white-space: nowrap;
}
.automation-state-badge.state-inactive { border-color: var(--border-hover); background: rgba(255, 255, 255, .05); color: var(--text-muted); }
.automation-state-badge.state-active { border-color: rgba(16, 185, 129, .42); background: rgba(16, 185, 129, .14); color: #50e3a4; }
.automation-state-badge.state-paused { border-color: rgba(59, 130, 246, .46); background: rgba(59, 130, 246, .16); color: #7db5ff; }
.automation-state-badge.state-queue-limited { border-color: rgba(245, 158, 11, .5); background: rgba(245, 158, 11, .15); color: #fbbf24; }
.automation-state-badge.state-disconnected { border-color: rgba(249, 115, 22, .5); background: rgba(249, 115, 22, .15); color: #fb923c; }
.automation-state-badge.state-error { border-color: rgba(239, 68, 68, .5); background: rgba(239, 68, 68, .15); color: #f87171; }
.automation-queue-summary { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 5px 12px; }
.automation-queue-label { color: var(--text-muted); font-size: 11px; font-weight: 600; }
.automation-queue-value { text-align: right; white-space: nowrap; }
.automation-queue-track {
grid-column: 1 / -1;
height: 7px;
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
border-radius: 999px;
background: rgba(0, 0, 0, .28);
}
.automation-queue-bar { display: block; width: 0; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--success), var(--accent)); transition: width .2s ease; }
.automation-status-metrics {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1px;
margin-top: 12px;
overflow: hidden;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--border);
}
.automation-status-metric {
display: grid;
grid-template-rows: minmax(30px, auto) minmax(22px, auto);
align-content: start;
gap: 4px;
padding: 9px 10px;
background: var(--bg-secondary);
}
.automation-status-metric > span { color: var(--text-dim); font-size: 10px; line-height: 1.35; overflow-wrap: anywhere; }
.automation-status-value { color: var(--text); font-size: 12px; font-variant-numeric: tabular-nums; line-height: 1.35; overflow-wrap: anywhere; }
.automation-status-error {
display: grid;
grid-template-columns: minmax(120px, auto) minmax(0, 1fr);
gap: 12px;
margin-top: 10px;
padding: 9px 10px;
border: 1px solid rgba(239, 68, 68, .38);
border-radius: 8px;
background: rgba(239, 68, 68, .1);
color: #fca5a5;
font-size: 11px;
}
.automation-status-error[hidden] { display: none; }
.automation-test-action-row { display: grid !important; grid-template-columns: minmax(0, 1fr) auto; }
.automation-test-action-copy { display: grid; gap: 4px; }
.automation-test-action-copy > strong { color: var(--text); font-size: 12px; }
.automation-capacity-row > .hs-input { width: 120px; }
.automation-interval-row > .hs-input { min-width: 150px; }
.toolbar-btn.automation-pause-resume-btn { width: auto; min-width: 150px; gap: 7px; padding: 3px 9px; color: var(--text); }
.automation-pause-resume-label { font-size: 11px; font-weight: 700; white-space: nowrap; }
.toolbar-btn.automation-pause-resume-btn.automation-resume { border-color: rgba(16, 185, 129, .48); background: rgba(16, 185, 129, .17); color: #5ee6ac; }
.toolbar-btn.automation-pause-resume-btn.automation-resume:hover { border-color: rgba(16, 185, 129, .7); background: rgba(16, 185, 129, .26); }
.ctx-item-disabled { opacity: .42; pointer-events: none; }
.automation-test-overlay[aria-hidden="true"] { display: none; }
.automation-test-modal { width: min(720px, 100%); }
.automation-test-header p { margin: 4px 0 0; color: var(--text-muted); font-size: 11px; line-height: 1.45; }
.automation-test-body { overflow-y: auto; }
.automation-test-loading { min-height: 160px; display: grid; place-items: center; align-content: center; gap: 12px; color: var(--text-muted); font-size: 12px; }
.automation-test-loading[hidden] { display: none; }
.automation-test-spinner { width: 28px; height: 28px; border: 3px solid rgba(255, 255, 255, .14); border-top-color: var(--accent); border-radius: 50%; animation: automation-test-spin .8s linear infinite; }
.automation-test-metrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.automation-test-metrics[hidden] { display: none; }
.automation-test-metric { display: grid; grid-template-columns: minmax(0, 1fr) minmax(64px, auto); align-items: center; gap: 12px; min-height: 44px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-secondary); }
.automation-test-label { color: var(--text-muted); font-size: 11px; line-height: 1.35; overflow-wrap: anywhere; }
.automation-test-value { color: var(--text); font-size: 13px; font-variant-numeric: tabular-nums; text-align: right; }
.automation-test-error { margin: 12px 0 0; padding: 10px; border: 1px solid rgba(239, 68, 68, .4); border-radius: 7px; background: rgba(239, 68, 68, .1); color: #fca5a5; font-size: 11px; }
.automation-test-error[hidden] { display: none; }
.automation-test-footer { justify-content: flex-end; }
@keyframes automation-test-spin { to { transform: rotate(360deg); } }
.settings-option {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
@@ -4649,9 +4757,25 @@ input[type="checkbox"] {
}
@media (max-width: 900px) {
.automation-status-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 820px) {
.automation-status-header,
.automation-test-action-row { grid-template-columns: minmax(0, 1fr); }
.automation-state-badge { justify-self: start; white-space: normal; }
.automation-status-metrics,
.automation-test-metrics { grid-template-columns: minmax(0, 1fr); }
.automation-test-action-row .btn { width: 100%; }
.toolbar-btn.automation-pause-resume-btn { min-width: 0; }
}
@media (prefers-reduced-motion: reduce) {
.header-update-button.is-checking .header-action-icon,
.header-update-button.is-downloading .header-action-icon {
.header-update-button.is-downloading .header-action-icon,
.automation-test-spinner {
animation: none;
}
.automation-queue-bar { transition: none; }
}