feat: clip-cutter modal themed + global radio button styling
Two interrelated changes shipped together. Clip-cutter modal cleanup. The "VOD zuschneiden" modal was the last big surface still painted in the apps PRE-purple colour palette: hardcoded #2b2b2b modal bg, #E5A00D orange title, #1a1a1a slider tracks (already overridden by the global rule but inline-styles still sat there), #333 input bgs, #444 borders, plain "white" text, #888 labels, #aaa radio labels. All of it inline. The result: opening the clip dialog was visually jumping back two themes. Extracted everything to class-based styles using var() colours: - .clip-modal* family of classes for layout - Title now uses var(--text), no orange - Inputs use var(--bg-elevated) + var(--border-soft) and pick up the global focus ring automatically - The duration display ("Dauer: 00:01:00") now sits in a small green-tinted card to make it visually distinct from the input rows around it - Radio labels go through a unified .clip-radio-row with a hover background tint, and the :has(input:checked) selector swaps the label text colour + weight when a radio is selected Global radio button styling. The clip modal had four radio buttons that were the only non-OS-themed control left in the app. Custom .appearance:none + ::after-driven dot styling matching the new checkbox visual: 16px circle, 1.5px border, hover purple tint, checked fills the inner circle with the accent colour + a soft purple shadow, focus-visible has the same 3px purple ring as every other form control. Cascades globally so any future radio gets the treatment for free. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
32decb4c01
commit
0df8bf357d
103
src/index.html
103
src/index.html
@ -47,90 +47,67 @@
|
|||||||
|
|
||||||
<!-- Clip Dialog Modal -->
|
<!-- Clip Dialog Modal -->
|
||||||
<div class="modal-overlay" id="clipModal" role="dialog" aria-modal="true" aria-labelledby="clipDialogTitle">
|
<div class="modal-overlay" id="clipModal" role="dialog" aria-modal="true" aria-labelledby="clipDialogTitle">
|
||||||
<div class="modal" style="background: #2b2b2b; max-width: 500px;">
|
<div class="modal clip-modal">
|
||||||
<button class="modal-close" aria-label="Close" onclick="closeClipDialog()">x</button>
|
<button class="modal-close" aria-label="Close" onclick="closeClipDialog()">x</button>
|
||||||
<h2 style="color: #E5A00D; text-align: center; margin-bottom: 20px;" id="clipDialogTitle">VOD zuschneiden</h2>
|
<h2 class="clip-modal-title" id="clipDialogTitle">VOD zuschneiden</h2>
|
||||||
|
|
||||||
<!-- Start Zeit mit Slider -->
|
<div class="clip-modal-field">
|
||||||
<div style="margin-bottom: 15px;">
|
<label class="clip-modal-label" id="clipDialogStartLabel">Start:</label>
|
||||||
<label id="clipDialogStartLabel" style="display: block; margin-bottom: 5px;">Start:</label>
|
<input type="range" id="clipStartSlider" min="0" max="100" value="0" oninput="updateFromSlider('start')">
|
||||||
<input type="range" id="clipStartSlider" min="0" max="100" value="0"
|
<div class="clip-modal-time-row">
|
||||||
style="width: 100%; height: 6px; -webkit-appearance: none; background: #1a1a1a; border-radius: 3px; cursor: pointer;"
|
<label class="clip-modal-meta" id="clipDialogStartTimeLabel">Startzeit (HH:MM:SS):</label>
|
||||||
oninput="updateFromSlider('start')">
|
<input type="text" id="clipStartTime" value="00:00:00" class="clip-modal-time-input" onchange="updateFromInput('start')">
|
||||||
<div style="display: flex; align-items: center; gap: 10px; margin-top: 8px;">
|
|
||||||
<label id="clipDialogStartTimeLabel" style="color: #888;">Startzeit (HH:MM:SS):</label>
|
|
||||||
<input type="text" id="clipStartTime" value="00:00:00"
|
|
||||||
style="width: 100px; background: #333; border: 1px solid #444; border-radius: 4px; padding: 6px 10px; color: white; font-family: monospace; text-align: center;"
|
|
||||||
onchange="updateFromInput('start')">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- End Zeit mit Slider -->
|
<div class="clip-modal-field">
|
||||||
<div style="margin-bottom: 15px;">
|
<label class="clip-modal-label" id="clipDialogEndLabel">Ende:</label>
|
||||||
<label id="clipDialogEndLabel" style="display: block; margin-bottom: 5px;">Ende:</label>
|
<input type="range" id="clipEndSlider" min="0" max="100" value="60" oninput="updateFromSlider('end')">
|
||||||
<input type="range" id="clipEndSlider" min="0" max="100" value="60"
|
<div class="clip-modal-time-row">
|
||||||
style="width: 100%; height: 6px; -webkit-appearance: none; background: #1a1a1a; border-radius: 3px; cursor: pointer;"
|
<label class="clip-modal-meta" id="clipDialogEndTimeLabel">Endzeit (HH:MM:SS):</label>
|
||||||
oninput="updateFromSlider('end')">
|
<input type="text" id="clipEndTime" value="00:01:00" class="clip-modal-time-input" onchange="updateFromInput('end')">
|
||||||
<div style="display: flex; align-items: center; gap: 10px; margin-top: 8px;">
|
|
||||||
<label id="clipDialogEndTimeLabel" style="color: #888;">Endzeit (HH:MM:SS):</label>
|
|
||||||
<input type="text" id="clipEndTime" value="00:01:00"
|
|
||||||
style="width: 100px; background: #333; border: 1px solid #444; border-radius: 4px; padding: 6px 10px; color: white; font-family: monospace; text-align: center;"
|
|
||||||
onchange="updateFromInput('end')">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dauer Anzeige -->
|
<div class="clip-modal-duration">
|
||||||
<div style="text-align: center; margin-bottom: 20px;">
|
<span id="clipDialogDurationLabel" class="clip-modal-meta">Dauer: </span>
|
||||||
<span id="clipDialogDurationLabel" style="color: #888;">Dauer: </span>
|
<span id="clipDurationDisplay" class="clip-modal-duration-value">00:01:00</span>
|
||||||
<span id="clipDurationDisplay" style="color: #00c853;">00:01:00</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Teil Nummer -->
|
<div class="clip-modal-field">
|
||||||
<div style="margin-bottom: 15px;">
|
<label class="clip-modal-label" id="clipDialogPartLabel">Start Part-Nummer (optional, fur Fortsetzung):</label>
|
||||||
<label id="clipDialogPartLabel" style="display: block; margin-bottom: 8px;">Start Part-Nummer (optional, fur Fortsetzung):</label>
|
<input type="text" id="clipStartPart" placeholder="z.B. 42" class="clip-modal-part-input" oninput="updateFilenameExamples()">
|
||||||
<input type="text" id="clipStartPart" placeholder="z.B. 42"
|
<div id="clipDialogPartHint" class="clip-modal-hint">Leer lassen = Teil 1</div>
|
||||||
style="width: 100px; background: #333; border: 1px solid #444; border-radius: 4px; padding: 8px 12px; color: white;"
|
|
||||||
oninput="updateFilenameExamples()">
|
|
||||||
<div id="clipDialogPartHint" style="color: #888; font-size: 12px; margin-top: 5px;">Leer lassen = Teil 1</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dateinamen Format -->
|
<div class="clip-modal-field">
|
||||||
<div style="margin-bottom: 20px;">
|
<label class="clip-modal-label" id="clipDialogFormatLabel">Dateinamen-Format:</label>
|
||||||
<label id="clipDialogFormatLabel" style="display: block; margin-bottom: 10px;">Dateinamen-Format:</label>
|
<label class="clip-radio-row">
|
||||||
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 8px;">
|
<input type="radio" name="filenameFormat" value="simple" checked onchange="updateFilenameExamples()">
|
||||||
<input type="radio" name="filenameFormat" value="simple" checked onchange="updateFilenameExamples()"
|
<span id="formatSimple" class="clip-radio-label">01.02.2026_1.mp4 (Standard)</span>
|
||||||
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
|
||||||
<span id="formatSimple" style="color: #aaa;">01.02.2026_1.mp4 (Standard)</span>
|
|
||||||
</label>
|
</label>
|
||||||
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 8px;">
|
<label class="clip-radio-row">
|
||||||
<input type="radio" name="filenameFormat" value="timestamp" onchange="updateFilenameExamples()"
|
<input type="radio" name="filenameFormat" value="timestamp" onchange="updateFilenameExamples()">
|
||||||
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
<span id="formatTimestamp" class="clip-radio-label">01.02.2026_CLIP_00-00-00_1.mp4 (mit Zeitstempel)</span>
|
||||||
<span id="formatTimestamp" style="color: #aaa;">01.02.2026_CLIP_00-00-00_1.mp4 (mit Zeitstempel)</span>
|
|
||||||
</label>
|
</label>
|
||||||
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 8px;">
|
<label class="clip-radio-row">
|
||||||
<input type="radio" name="filenameFormat" value="parts" onchange="updateFilenameExamples()"
|
<input type="radio" name="filenameFormat" value="parts" onchange="updateFilenameExamples()">
|
||||||
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
<span id="formatParts" class="clip-radio-label">01.02.2026_Part01.mp4 (Parts-Format)</span>
|
||||||
<span id="formatParts" style="color: #aaa;">01.02.2026_Part01.mp4 (Parts-Format)</span>
|
|
||||||
</label>
|
</label>
|
||||||
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 10px;">
|
<label class="clip-radio-row">
|
||||||
<input type="radio" name="filenameFormat" value="template" onchange="updateFilenameExamples()"
|
<input type="radio" name="filenameFormat" value="template" onchange="updateFilenameExamples()">
|
||||||
style="width: 18px; height: 18px; accent-color: #9146FF;">
|
<span id="formatTemplate" class="clip-radio-label">{date}_{part}.mp4 (benutzerdefiniert)</span>
|
||||||
<span id="formatTemplate" style="color: #aaa;">{date}_{part}.mp4 (benutzerdefiniert)</span>
|
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div id="clipFilenameTemplateWrap" style="display:none; margin-top: 10px;">
|
<div id="clipFilenameTemplateWrap" class="clip-template-wrap" style="display:none;">
|
||||||
<input type="text" id="clipFilenameTemplate" value="{date}_{part}.mp4"
|
<input type="text" id="clipFilenameTemplate" value="{date}_{part}.mp4" placeholder="{date}_{part}.mp4" class="clip-modal-template-input" oninput="updateFilenameExamples()">
|
||||||
placeholder="{date}_{part}.mp4"
|
<div id="clipTemplateHelp" class="clip-modal-hint">Platzhalter: {title} {id} {channel} {date} {part} {trim_start} {trim_end} {trim_length} {date_custom="yyyy-MM-dd"}</div>
|
||||||
style="width: 100%; background: #333; border: 1px solid #444; border-radius: 4px; padding: 8px 12px; color: white; font-family: monospace;"
|
<div id="clipTemplateLint" class="clip-template-lint">Template-Check: OK</div>
|
||||||
oninput="updateFilenameExamples()">
|
|
||||||
<div id="clipTemplateHelp" style="color: #888; font-size: 12px; margin-top: 6px;">Platzhalter: {title} {id} {channel} {date} {part} {trim_start} {trim_end} {trim_length} {date_custom="yyyy-MM-dd"}</div>
|
|
||||||
<div id="clipTemplateLint" style="color: #8bc34a; font-size: 12px; margin-top: 4px;">Template-Check: OK</div>
|
|
||||||
<button class="btn-secondary" id="clipTemplateGuideBtn" style="margin-top: 8px;" onclick="openTemplateGuide('clip')">Template Guide</button>
|
<button class="btn-secondary" id="clipTemplateGuideBtn" style="margin-top: 8px;" onclick="openTemplateGuide('clip')">Template Guide</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Button -->
|
<div class="clip-modal-actions">
|
||||||
<div style="text-align: center;">
|
|
||||||
<button class="btn-pill success" id="clipDialogConfirmBtn" style="padding: 12px 30px;" onclick="confirmClipDialog()">Zur Queue hinzufugen</button>
|
<button class="btn-pill success" id="clipDialogConfirmBtn" style="padding: 12px 30px;" onclick="confirmClipDialog()">Zur Queue hinzufugen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
181
src/styles.css
181
src/styles.css
@ -188,6 +188,142 @@ body {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
CLIP-CUTTER MODAL — themed to match the rest of the app
|
||||||
|
============================================
|
||||||
|
This modal had a stack of hard-coded #2b2b2b / #E5A00D / #888 /
|
||||||
|
#333 colors from before the Twitch-purple theme was a thing.
|
||||||
|
Extracted to classes and re-themed using CSS vars + accent. */
|
||||||
|
.clip-modal {
|
||||||
|
max-width: 500px;
|
||||||
|
background: var(--bg-card);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-title {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-field {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-meta {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-time-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-time-input {
|
||||||
|
width: 100px;
|
||||||
|
background: var(--bg-elevated);
|
||||||
|
border: 1px solid var(--border-soft);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
color: var(--text);
|
||||||
|
font-family: 'Consolas', 'Segoe UI Mono', monospace;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-part-input {
|
||||||
|
width: 100px;
|
||||||
|
background: var(--bg-elevated);
|
||||||
|
border: 1px solid var(--border-soft);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-template-input {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--bg-elevated);
|
||||||
|
border: 1px solid var(--border-soft);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
color: var(--text);
|
||||||
|
font-family: 'Consolas', 'Segoe UI Mono', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-duration {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
background: rgba(0, 200, 83, 0.06);
|
||||||
|
border: 1px solid rgba(0, 200, 83, 0.18);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-duration-value {
|
||||||
|
color: #00c853;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: 'Consolas', 'Segoe UI Mono', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-hint {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-template-lint {
|
||||||
|
color: #8bc34a;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-template-wrap {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-radio-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-radio-row:hover {
|
||||||
|
background: rgba(145, 70, 255, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-radio-label {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
flex: 1;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-radio-row:has(input[type="radio"]:checked) .clip-radio-label {
|
||||||
|
color: var(--text);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-modal-actions {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.streamer-item .remove {
|
.streamer-item .remove {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@ -427,6 +563,51 @@ input[type="checkbox"]:disabled {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
CUSTOM RADIO — matches the checkbox visual
|
||||||
|
============================================ */
|
||||||
|
input[type="radio"] {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1.5px solid var(--border-soft);
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--bg-card);
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
transition: background 0.18s, border-color 0.18s, box-shadow 0.18s, transform 0.12s;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:hover:not(:disabled) {
|
||||||
|
border-color: rgba(145, 70, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:checked {
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: var(--bg-card);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:checked::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 3px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--accent);
|
||||||
|
box-shadow: 0 0 6px rgba(145, 70, 255, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 3px rgba(145, 70, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:active:not(:disabled) {
|
||||||
|
transform: scale(0.92);
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
CUSTOM SELECT — chevron via inline SVG background
|
CUSTOM SELECT — chevron via inline SVG background
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user