Twitch-VOD-Manager/src/styles.css
xRangerDE 3f04b42b02 feat: auto-resume queue toggle + already-downloaded VOD indicator
Two real UX wins.

1. Auto-resume queue on startup. New checkbox in Settings -> Download
   ("Queue beim Start automatisch fortsetzen"). When enabled and the
   persisted queue has pending items, processQueue() fires ~5 seconds
   after did-finish-load — long enough for the user to see the queue
   and pause if they did not actually want this. Default off so the
   existing behaviour (explicit Start click) is preserved on upgrade.
   The Settings auto-save fingerprint includes the new flag and
   syncSettingsFormFromConfig restores it. Tooltip explains the
   timing on hover.

2. Already-downloaded indicator on VOD cards. Config gains
   downloaded_vod_ids: string[] (bounded to 4096 latest entries).
   Every successful queue-item download appends its parsed VOD ID
   (or every component ID for merge groups). On the VOD grid each
   card whose vod.id is in the set gets a small green checkmark
   badge in the top-right plus a slightly dimmed thumbnail, with a
   localized "Already downloaded" / "Bereits heruntergeladen"
   tooltip. The lookup builds a Set once per render so it stays
   O(1) per card. The renderer refreshes its local config copy on
   every "newly completed" queue update so the badge appears live
   without waiting for a settings save.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:16:21 +02:00

1812 lines
33 KiB
CSS

:root {
--bg-main: #0e0e10;
--bg-sidebar: #18181b;
--bg-card: #1f1f23;
--accent: #9146FF;
--accent-hover: #772ce8;
--text: #efeff1;
--text-secondary: #adadb8;
--success: #00c853;
--error: #ff4444;
--warning: #ffab00;
/* Soft border that adapts to theme — used by post-4.5.x UI additions
(filter input, sort select, bulk bar) so they don't visually break
in light theme. */
--border-soft: rgba(255, 255, 255, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-main);
color: var(--text);
height: 100vh;
overflow: hidden;
}
.app {
display: flex;
height: 100vh;
}
/* Sidebar */
.sidebar {
width: 300px;
background: var(--bg-sidebar);
display: flex;
flex-direction: column;
border-right: 1px solid rgba(255,255,255,0.1);
overflow-y: auto;
overflow-x: hidden;
}
.logo {
padding: 20px;
font-size: 18px;
font-weight: bold;
color: var(--accent);
display: flex;
align-items: center;
gap: 10px;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.logo svg {
width: 28px;
height: 28px;
fill: currentColor;
}
.nav {
padding: 15px;
}
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 15px;
border-radius: 6px;
cursor: pointer;
color: var(--text-secondary);
transition: all 0.2s;
margin-bottom: 4px;
font-size: 14px;
}
.nav-item:hover {
background: rgba(145, 71, 255, 0.15);
color: var(--text);
}
.nav-item.active {
background: var(--accent);
color: white;
}
.nav-item svg {
width: 20px;
height: 20px;
}
.section-title {
font-size: 11px;
text-transform: uppercase;
color: var(--text-secondary);
padding: 15px 15px 8px;
letter-spacing: 0.5px;
}
.streamers {
flex: 1;
overflow-y: auto;
padding: 0 10px;
}
.streamer-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 6px;
cursor: pointer;
color: var(--text-secondary);
transition: all 0.2s;
font-size: 14px;
}
.streamer-item:hover {
background: rgba(255,255,255,0.05);
color: var(--text);
}
.streamer-item.active {
background: rgba(145, 71, 255, 0.2);
color: var(--text);
border-left: 3px solid var(--accent);
}
.streamer-item .remove {
margin-left: auto;
opacity: 0;
color: var(--error);
cursor: pointer;
}
.streamer-item:hover .remove {
opacity: 1;
}
.add-streamer {
padding: 10px;
display: flex;
gap: 8px;
}
.add-streamer input {
flex: 1;
background: var(--bg-card);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 4px;
padding: 8px 12px;
color: var(--text);
font-size: 13px;
}
.add-streamer input::placeholder {
color: var(--text-secondary);
}
.add-streamer button {
background: var(--accent);
border: none;
border-radius: 4px;
color: white;
width: 36px;
cursor: pointer;
font-size: 18px;
}
/* Queue Section */
.queue-section {
border-top: 1px solid rgba(255,255,255,0.1);
padding: 15px;
display: flex;
flex-direction: column;
min-height: 0;
flex: 1;
}
.queue-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
gap: 8px;
}
.queue-title {
font-size: 13px;
font-weight: 600;
}
.queue-count {
background: var(--accent);
color: white;
font-size: 11px;
padding: 2px 8px;
border-radius: 10px;
}
.queue-list {
flex: 1;
overflow-y: auto;
min-height: 60px;
}
.health-badge {
font-size: 10px;
padding: 2px 8px;
border-radius: 999px;
border: 1px solid transparent;
white-space: nowrap;
}
.health-badge.good {
background: rgba(0, 200, 83, 0.2);
border-color: rgba(0, 200, 83, 0.45);
color: #93efb9;
}
.health-badge.warn {
background: rgba(255, 171, 0, 0.2);
border-color: rgba(255, 171, 0, 0.45);
color: #ffd98e;
}
.health-badge.bad,
.health-badge.unknown {
background: rgba(255, 68, 68, 0.2);
border-color: rgba(255, 68, 68, 0.45);
color: #ffaaaa;
}
.queue-item {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px;
background: var(--bg-card);
border-radius: 4px;
margin-bottom: 6px;
font-size: 12px;
}
.queue-item .status {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--text-secondary);
}
.queue-item .status.pending { background: var(--warning); }
.queue-item .status.downloading { background: var(--accent); animation: pulse 1s infinite; }
.queue-item .status.completed { background: var(--success); }
.queue-item .status.error { background: var(--error); }
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.queue-item .title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.queue-main {
flex: 1;
min-width: 0;
}
.queue-title-row {
display: flex;
align-items: center;
gap: 8px;
}
.queue-status-label {
flex-shrink: 0;
font-size: 10px;
color: var(--text-secondary);
}
.queue-meta {
font-size: 10px;
color: var(--text-secondary);
margin-top: 2px;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.queue-progress-wrap {
height: 4px;
border-radius: 999px;
overflow: hidden;
background: rgba(255,255,255,0.12);
}
.queue-progress-bar {
height: 100%;
width: 0;
background: var(--accent);
transition: width 0.2s ease;
}
.queue-progress-bar.indeterminate {
width: 35% !important;
animation: queue-indeterminate 1.3s ease-in-out infinite;
}
.queue-progress-text {
margin-top: 3px;
font-size: 10px;
color: var(--text-secondary);
}
@keyframes queue-indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(280%); }
}
.queue-item .remove {
cursor: pointer;
color: var(--error);
opacity: 0.7;
}
.queue-item .remove:hover {
opacity: 1;
}
.queue-item[draggable="true"] {
cursor: grab;
}
.queue-item[draggable="true"]:active {
cursor: grabbing;
}
.queue-item.dragging {
opacity: 0.4;
}
.queue-details {
font-size: 10px;
color: var(--text-secondary);
padding: 4px 0;
word-break: break-all;
}
.queue-details div {
margin-bottom: 2px;
}
.queue-selector {
min-width: 22px;
height: 22px;
padding: 0 3px;
border: 2px solid var(--text-secondary);
border-radius: 4px;
cursor: pointer;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 700;
color: var(--bg-primary);
user-select: none;
transition: all 0.15s;
}
.queue-selector.selected {
background: var(--accent);
border-color: var(--accent);
}
.queue-selector:hover {
border-color: var(--accent);
}
.queue-item.merge-group {
border-left: 3px solid var(--accent);
}
.merge-group-icon {
vertical-align: middle;
margin-right: 2px;
opacity: 0.8;
}
.btn-merge-group {
background: var(--accent);
color: var(--bg-primary);
}
.btn-merge-group:hover {
opacity: 0.9;
}
.queue-actions {
display: flex;
gap: 8px;
margin-top: 10px;
flex-shrink: 0;
}
.stats-bar {
padding: 6px 15px;
font-size: 10px;
color: var(--text-secondary);
border-top: 1px solid rgba(255,255,255,0.1);
flex-shrink: 0;
}
.btn {
flex: 1;
padding: 5px 8px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
font-size: 12px;
transition: all 0.2s;
}
.btn-retry {
background: #2a3344;
color: #d9e4f7;
}
.btn-retry:hover {
background: #33405a;
}
.btn-start {
background: var(--success);
color: white;
}
.btn-start:hover {
background: #00a844;
}
.btn-start.downloading {
background: var(--error);
}
.btn-clear {
background: var(--bg-card);
color: var(--text-secondary);
}
.btn-clear:hover {
background: #2a2a2e;
}
/* Main Content */
.main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.header {
padding: 20px 30px;
border-bottom: 1px solid rgba(255,255,255,0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
font-size: 22px;
font-weight: 600;
}
.header-actions {
display: flex;
align-items: center;
gap: 15px;
}
.header-search {
display: flex;
gap: 8px;
}
.header-search input {
background: var(--bg-card);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 4px;
padding: 8px 12px;
color: var(--text);
font-size: 13px;
width: 200px;
}
.header-search input:focus {
outline: none;
border-color: var(--accent);
}
.header-search button {
background: var(--accent);
border: none;
border-radius: 4px;
color: white;
padding: 8px 12px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
}
.header-search button:hover {
opacity: 0.9;
}
.btn-icon {
background: var(--bg-card);
border: none;
border-radius: 4px;
color: var(--text);
padding: 8px 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
}
.btn-icon:hover {
background: #2a2a2e;
}
.content {
flex: 1;
overflow-y: auto;
padding: 25px 30px;
}
/* Tabs */
.tab-content {
display: none;
}
.tab-content.active {
display: flex;
flex-direction: column;
min-height: 100%;
}
/* VOD Grid */
.vod-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
flex: 1;
}
.vod-grid:has(.empty-state) {
display: flex;
align-items: center;
justify-content: center;
}
.vod-card {
background: var(--bg-card);
border-radius: 8px;
overflow: hidden;
transition: transform 0.2s, box-shadow 0.2s;
cursor: pointer;
position: relative;
}
.vod-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0,0,0,0.3);
}
.vod-card.selected {
box-shadow: 0 0 0 2px #9146FF, 0 8px 25px rgba(145, 70, 255, 0.25);
}
.vod-downloaded-badge {
position: absolute;
top: 8px;
right: 8px;
background: rgba(0, 200, 83, 0.92);
color: white;
border-radius: 50%;
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 700;
z-index: 2;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.vod-card.already-downloaded .vod-thumbnail {
opacity: 0.6;
}
.streamer-item.dragging {
opacity: 0.4;
}
.vod-thumbnail {
width: 100%;
aspect-ratio: 16/9;
background: #333;
object-fit: cover;
}
.vod-info {
padding: 12px 15px;
}
.vod-title {
font-weight: 600;
font-size: 14px;
margin-bottom: 6px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.4;
}
.vod-meta {
display: flex;
gap: 12px;
font-size: 12px;
color: var(--text-secondary);
}
.vod-actions {
padding: 10px 15px 15px;
display: flex;
gap: 8px;
}
.vod-btn {
flex: 1;
padding: 8px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
font-size: 12px;
transition: all 0.2s;
}
.vod-btn.primary {
background: var(--accent);
color: white;
}
.vod-btn.primary:hover {
background: var(--accent-hover);
}
.vod-btn.secondary {
background: rgba(255,255,255,0.1);
color: var(--text);
}
.vod-btn.secondary:hover {
background: rgba(255,255,255,0.15);
}
/* Settings */
.settings-card {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.settings-card h3 {
font-size: 16px;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 8px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
font-size: 13px;
color: var(--text-secondary);
margin-bottom: 6px;
}
.form-group input:not([type="checkbox"]):not([type="radio"]), .form-group select {
width: 100%;
background: var(--bg-main);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 4px;
padding: 10px 12px;
color: var(--text);
font-size: 14px;
}
.form-group input:not([type="checkbox"]):not([type="radio"]):focus, .form-group select:focus {
outline: none;
border-color: var(--accent);
}
.form-group input:not([type="checkbox"]):not([type="radio"]):disabled,
.form-group select:disabled {
opacity: 0.55;
cursor: not-allowed;
color: rgba(239, 239, 241, 0.7);
}
.input-disabled {
opacity: 0.65;
}
.form-group input[type="checkbox"],
.form-group input[type="radio"] {
width: auto;
}
.language-picker {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
.lang-option {
display: flex;
align-items: center;
gap: 8px;
border: 1px solid rgba(255,255,255,0.14);
border-radius: 6px;
background: var(--bg-main);
color: var(--text);
padding: 9px 10px;
cursor: pointer;
font-size: 13px;
}
.lang-option:hover {
border-color: rgba(255,255,255,0.26);
}
.lang-option.active {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(145, 70, 255, 0.2);
}
.flag-icon {
width: 16px;
height: 12px;
border-radius: 2px;
border: 1px solid rgba(0,0,0,0.35);
flex-shrink: 0;
position: relative;
overflow: hidden;
}
.flag-de {
background: linear-gradient(to bottom, #111 0 33.33%, #dd0000 33.33% 66.66%, #ffce00 66.66% 100%);
}
.flag-en {
background: repeating-linear-gradient(to bottom, #b22234 0 7.7%, #ffffff 7.7% 15.4%);
}
.flag-en::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 45%;
height: 56%;
background: #3c3b6e;
}
.form-row {
display: flex;
gap: 10px;
}
.log-panel {
background: #11151c;
border: 1px solid rgba(255,255,255,0.12);
border-radius: 6px;
padding: 10px;
max-height: 220px;
overflow: auto;
white-space: pre-wrap;
color: #b8c7df;
font-size: 12px;
line-height: 1.35;
}
.form-row input {
flex: 1;
}
.btn-primary {
background: var(--accent);
color: white;
border: none;
border-radius: 4px;
padding: 10px 20px;
cursor: pointer;
font-weight: 600;
}
.btn-primary:hover {
background: var(--accent-hover);
}
.btn-primary:disabled {
background: var(--text-secondary);
cursor: not-allowed;
}
.btn-secondary {
background: var(--bg-card);
color: var(--text);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 4px;
padding: 10px 20px;
cursor: pointer;
}
/* Clips */
.clip-input {
max-width: 600px;
margin: 0 auto;
text-align: center;
padding: 40px 20px;
}
.clip-input h2 {
margin-bottom: 20px;
}
.clip-input input {
width: 100%;
background: var(--bg-card);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 4px;
padding: 12px 15px;
color: var(--text);
font-size: 14px;
margin-bottom: 15px;
}
.clip-status {
margin-top: 15px;
font-size: 14px;
}
.clip-status.success { color: var(--success); }
.clip-status.error { color: var(--error); }
.clip-status.loading { color: var(--warning); }
/* Empty State */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
min-height: 60vh;
padding: 20px;
color: var(--text-secondary);
}
.empty-state svg {
width: 64px;
height: 64px;
margin-bottom: 15px;
opacity: 0.5;
}
.empty-state h3 {
margin-bottom: 8px;
color: var(--text);
}
/* Status Bar */
.status-bar {
padding: 10px 30px;
background: var(--bg-sidebar);
border-top: 1px solid rgba(255,255,255,0.1);
display: flex;
justify-content: space-between;
font-size: 12px;
color: var(--text-secondary);
}
.status-indicator {
display: flex;
align-items: center;
gap: 8px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--text-secondary);
}
.status-dot.connected { background: var(--success); }
.status-dot.error { background: var(--error); }
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.15);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255,255,255,0.25);
}
/* Update Banner */
.update-banner {
background: linear-gradient(90deg, var(--accent), #5a2d82);
padding: 10px 20px;
display: none;
justify-content: center;
align-items: center;
gap: 15px;
font-size: 13px;
}
.update-banner.show {
display: flex;
}
.update-banner button {
background: white;
color: var(--accent);
border: none;
border-radius: 4px;
padding: 6px 15px;
cursor: pointer;
font-weight: 600;
}
.update-banner button:disabled {
opacity: 0.7;
cursor: not-allowed;
}
.update-modal {
max-width: 680px;
border: 1px solid rgba(255, 255, 255, 0.08);
background:
linear-gradient(180deg, rgba(145, 70, 255, 0.18) 0%, rgba(145, 70, 255, 0.05) 24%, rgba(14, 14, 16, 0.98) 100%),
var(--bg-card);
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.48);
}
.update-modal-eyebrow {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 999px;
background: rgba(145, 70, 255, 0.16);
color: #f1e7ff;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
margin-bottom: 14px;
}
.update-modal-message {
color: var(--text);
line-height: 1.6;
margin: -8px 0 12px;
}
.update-modal-meta {
color: var(--text-secondary);
font-size: 12px;
margin-bottom: 16px;
}
.update-modal-actions {
justify-content: flex-end;
}
.update-changelog-card {
margin-top: 10px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 10px;
background: rgba(7, 7, 10, 0.42);
overflow: hidden;
}
.update-changelog-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
padding: 12px 14px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.update-changelog-label {
font-size: 12px;
color: var(--text-secondary);
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.update-changelog-toggle {
background: transparent;
border: none;
color: #f3ecff;
cursor: pointer;
font-size: 13px;
font-weight: 600;
}
.update-changelog-toggle:hover {
color: white;
}
.update-changelog-panel {
max-height: 320px;
overflow: auto;
padding: 14px;
}
.update-changelog-content {
display: grid;
gap: 12px;
}
.update-changelog-heading {
font-size: 17px;
line-height: 1.25;
color: #ffffff;
margin: 0;
}
.update-changelog-paragraph {
margin: 0;
color: var(--text);
line-height: 1.6;
}
.update-changelog-list {
margin: 0;
padding-left: 18px;
color: var(--text);
display: grid;
gap: 8px;
}
.update-changelog-list li {
line-height: 1.5;
}
.update-changelog-content strong {
color: #ffffff;
font-weight: 700;
}
.update-changelog-empty {
margin: 0;
color: var(--text-secondary);
font-size: 13px;
}
#updateProgressBar.downloading {
width: 30% !important;
animation: indeterminate 1.5s ease-in-out infinite;
}
@keyframes indeterminate {
0% { margin-left: 0; width: 30%; }
50% { margin-left: 35%; width: 30%; }
100% { margin-left: 70%; width: 30%; }
}
/* Video Cutter Styles */
.cutter-container {
max-width: 900px;
margin: 0 auto;
}
.video-preview {
background: #000;
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
aspect-ratio: 16/9;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.video-preview img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.video-preview .placeholder {
color: var(--text-secondary);
text-align: center;
}
.timeline-container {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.timeline {
position: relative;
height: 60px;
background: var(--bg-main);
border-radius: 4px;
margin: 15px 0;
cursor: pointer;
}
.timeline-selection {
position: absolute;
top: 0;
height: 100%;
background: rgba(145, 71, 255, 0.3);
border-left: 3px solid var(--accent);
border-right: 3px solid var(--accent);
}
.timeline-handle {
position: absolute;
top: -5px;
width: 12px;
height: 70px;
background: var(--accent);
border-radius: 3px;
cursor: ew-resize;
}
.timeline-handle.start { left: 0; transform: translateX(-50%); }
.timeline-handle.end { right: 0; transform: translateX(50%); }
.timeline-current {
position: absolute;
top: 0;
width: 2px;
height: 100%;
background: var(--success);
pointer-events: none;
}
.time-inputs {
display: flex;
gap: 20px;
align-items: center;
justify-content: center;
margin-top: 15px;
}
.time-input-group {
display: flex;
align-items: center;
gap: 8px;
}
.time-input-group label {
color: var(--text-secondary);
font-size: 13px;
}
.time-input-group input {
width: 100px;
background: var(--bg-main);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 4px;
padding: 8px 10px;
color: var(--text);
text-align: center;
font-family: monospace;
}
.cutter-actions {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 20px;
}
.cutter-info {
background: var(--bg-card);
border-radius: 8px;
padding: 15px 20px;
margin-bottom: 20px;
display: flex;
justify-content: space-around;
text-align: center;
}
.cutter-info-item {
display: flex;
flex-direction: column;
gap: 5px;
}
.cutter-info-label {
font-size: 12px;
color: var(--text-secondary);
}
.cutter-info-value {
font-size: 16px;
font-weight: 600;
font-family: monospace;
}
/* Merge Styles */
.merge-container {
max-width: 800px;
margin: 0 auto;
}
.file-list {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
min-height: 200px;
}
.file-item {
display: flex;
align-items: center;
gap: 15px;
padding: 12px 15px;
background: var(--bg-main);
border-radius: 6px;
margin-bottom: 10px;
}
.file-item .file-order {
width: 30px;
height: 30px;
background: var(--accent);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
}
.file-item .file-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-item .file-actions {
display: flex;
gap: 8px;
}
.file-item .file-btn {
background: transparent;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 5px;
font-size: 16px;
}
.file-item .file-btn:hover {
color: var(--text);
}
.file-item .file-btn.remove:hover {
color: var(--error);
}
.merge-actions {
display: flex;
gap: 10px;
justify-content: center;
}
/* Progress Bar */
.progress-container {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
display: none;
}
.progress-container.show {
display: block;
}
.progress-bar {
height: 8px;
background: var(--bg-main);
border-radius: 4px;
overflow: hidden;
margin-bottom: 10px;
}
.progress-bar-fill {
height: 100%;
background: var(--accent);
transition: width 0.3s;
}
.progress-text {
text-align: center;
color: var(--text-secondary);
font-size: 14px;
}
/* Theme variations */
body.theme-discord {
--bg-main: #36393f;
--bg-sidebar: #202225;
--bg-card: #2f3136;
--accent: #5865F2;
--accent-hover: #4752C4;
}
body.theme-youtube {
--bg-main: #0f0f0f;
--bg-sidebar: #0f0f0f;
--bg-card: #272727;
--accent: #FF0000;
--accent-hover: #cc0000;
}
body.theme-apple {
--bg-main: #1c1c1e;
--bg-sidebar: #2c2c2e;
--bg-card: #3a3a3c;
--accent: #0A84FF;
--accent-hover: #0071e3;
}
body.theme-light {
--bg-main: #f0f2f5;
--bg-sidebar: #ffffff;
--bg-card: #e4e6ea;
--text: #1a1a2e;
--text-secondary: #65676b;
--accent: #9146ff;
--accent-hover: #772ce8;
--success: #00c853;
--error: #e41e3f;
--warning: #e68a00;
--border-soft: rgba(0, 0, 0, 0.12);
}
/* Light theme: swap white-alpha borders/backgrounds to black-alpha */
body.theme-light .sidebar,
body.theme-light .queue-section,
body.theme-light .logo,
body.theme-light .stats-bar,
body.theme-light .header,
body.theme-light .status-bar {
border-color: rgba(0,0,0,0.1);
}
body.theme-light .add-streamer input,
body.theme-light .form-group input:not([type="checkbox"]):not([type="radio"]),
body.theme-light .form-group select,
body.theme-light .clip-input input,
body.theme-light .time-input-group input,
body.theme-light .part-number-group input,
body.theme-light .btn-secondary,
body.theme-light .lang-option,
body.theme-light .log-panel,
body.theme-light .template-guide-table-wrap,
body.theme-light .template-guide-preview-box {
border-color: rgba(0,0,0,0.12);
}
body.theme-light .lang-option:hover {
border-color: rgba(0,0,0,0.26);
}
body.theme-light .streamer-item:hover {
background: rgba(0,0,0,0.05);
}
body.theme-light .vod-btn.secondary {
background: rgba(0,0,0,0.08);
}
body.theme-light .vod-btn.secondary:hover {
background: rgba(0,0,0,0.12);
}
body.theme-light .nav-item:hover {
background: rgba(145, 71, 255, 0.1);
}
body.theme-light ::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.15);
}
body.theme-light ::-webkit-scrollbar-thumb:hover {
background: rgba(0,0,0,0.25);
}
body.theme-light .update-modal {
border-color: rgba(0,0,0,0.1);
background:
linear-gradient(180deg, rgba(145, 70, 255, 0.12) 0%, rgba(145, 70, 255, 0.03) 24%, rgba(240, 242, 245, 0.98) 100%),
var(--bg-card);
}
body.theme-light .update-modal-eyebrow {
color: #4a2a8a;
}
body.theme-light .update-changelog-card {
border-color: rgba(0,0,0,0.08);
background: rgba(255, 255, 255, 0.6);
}
body.theme-light .update-changelog-header {
border-bottom-color: rgba(0,0,0,0.06);
}
body.theme-light .update-changelog-toggle {
color: #4a2a8a;
}
body.theme-light .update-changelog-toggle:hover {
color: #1a1a2e;
}
body.theme-light .update-changelog-heading,
body.theme-light .update-changelog-content strong {
color: #1a1a2e;
}
body.theme-light .template-guide-preview-box {
background: rgba(0, 0, 0, 0.04);
}
body.theme-light .template-guide-output {
background: rgba(0, 0, 0, 0.06);
}
body.theme-light .template-guide-table th,
body.theme-light .template-guide-table td {
border-bottom-color: rgba(0,0,0,0.08);
}
body.theme-light .log-panel {
background: #f8f9fb;
color: #2c3e50;
}
body.theme-light .app-toast {
background: rgba(255, 255, 255, 0.96);
color: #1a1a2e;
border-color: rgba(0,0,0,0.12);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
body.theme-light .btn-retry {
background: #dce4f0;
color: #2a3344;
}
body.theme-light .btn-retry:hover {
background: #c8d4e8;
}
body.theme-light .btn-clear:hover {
background: #d0d2d6;
}
body.theme-light .modal {
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
/* Modal Styles */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-overlay.show {
display: flex;
}
.modal {
background: var(--bg-card);
border-radius: 12px;
padding: 25px;
width: 90%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
}
.modal h2 {
margin-bottom: 20px;
font-size: 18px;
}
.modal-close {
float: right;
background: none;
border: none;
color: var(--text-secondary);
font-size: 24px;
cursor: pointer;
padding: 0;
line-height: 1;
}
.modal-close:hover {
color: var(--text);
}
.slider-group {
margin-bottom: 20px;
}
.slider-group label {
display: block;
margin-bottom: 8px;
color: var(--text-secondary);
font-size: 13px;
}
.slider-group input[type="range"],
.modal input[type="range"] {
width: 100%;
height: 6px;
-webkit-appearance: none;
background: #1a1a1a;
border-radius: 3px;
outline: none;
}
.slider-group input[type="range"]::-webkit-slider-thumb,
.modal input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
background: #E5A00D;
border-radius: 50%;
cursor: pointer;
}
.modal input[type="range"]::-webkit-slider-thumb:hover {
background: #ffb825;
}
.clip-time-display {
display: flex;
justify-content: space-between;
margin-top: 8px;
font-family: monospace;
font-size: 14px;
}
.clip-info-row {
background: var(--bg-main);
padding: 12px 15px;
border-radius: 6px;
margin-bottom: 15px;
text-align: center;
}
.clip-info-row .label {
color: var(--text-secondary);
font-size: 12px;
margin-bottom: 4px;
}
.clip-info-row .value {
font-size: 18px;
font-weight: 600;
color: var(--success);
}
.clip-info-row .value.error {
color: var(--error);
}
.part-number-group {
margin-bottom: 20px;
}
.part-number-group input {
width: 100px;
background: var(--bg-main);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 4px;
padding: 8px 12px;
color: var(--text);
font-size: 14px;
}
.part-number-group small {
display: block;
margin-top: 5px;
color: var(--text-secondary);
font-size: 11px;
}
.modal-actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
.modal-actions button {
flex: 1;
}
.template-guide-modal {
max-width: 860px;
}
.template-guide-intro {
color: var(--text-secondary);
margin-bottom: 14px;
line-height: 1.5;
}
.template-guide-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 12px;
}
.template-guide-actions .btn-secondary {
padding: 8px 12px;
min-width: 140px;
}
.template-guide-actions .btn-secondary.active {
background: var(--accent);
color: #fff;
border-color: transparent;
}
.template-guide-label {
display: block;
margin-bottom: 6px;
font-size: 13px;
color: var(--text-secondary);
}
.template-guide-input {
width: 100%;
font-family: Consolas, "Courier New", monospace;
margin-bottom: 10px;
}
.template-guide-preview-box {
background: rgba(0, 0, 0, 0.22);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
padding: 10px;
margin-bottom: 14px;
}
.template-guide-preview-label {
font-size: 12px;
color: var(--text-secondary);
margin-bottom: 6px;
}
.template-guide-output {
font-family: Consolas, "Courier New", monospace;
color: var(--text);
word-break: break-word;
background: rgba(0, 0, 0, 0.2);
border-radius: 6px;
padding: 8px;
}
.template-guide-context {
margin-top: 6px;
font-size: 12px;
color: var(--text-secondary);
}
.template-guide-vars-title {
margin: 0 0 8px;
font-size: 14px;
}
.template-guide-table-wrap {
max-height: 280px;
overflow: auto;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
margin-bottom: 12px;
}
.template-guide-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.template-guide-table th,
.template-guide-table td {
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
padding: 8px;
vertical-align: top;
}
.template-guide-table tbody tr:last-child td {
border-bottom: 0;
}
.template-guide-table td:first-child,
.template-guide-table td:last-child {
font-family: Consolas, "Courier New", monospace;
}
.template-guide-footer {
display: flex;
justify-content: flex-end;
}
.app-toast {
position: fixed;
right: 18px;
bottom: 16px;
z-index: 2200;
max-width: min(90vw, 520px);
background: rgba(20, 20, 24, 0.96);
color: #e6e6ea;
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 8px;
padding: 10px 12px;
font-size: 13px;
line-height: 1.45;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
opacity: 0;
transform: translateY(10px);
pointer-events: none;
transition: opacity 0.18s ease, transform 0.18s ease;
}
.app-toast.show {
opacity: 1;
transform: translateY(0);
}
.app-toast.warn {
border-color: rgba(255, 167, 38, 0.7);
}