frontend supports dark theme
This commit is contained in:
parent
2bbdc70187
commit
4e56130a40
1 changed files with 157 additions and 25 deletions
|
|
@ -6,10 +6,85 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>WhisperLiveKit</title>
|
<title>WhisperLiveKit</title>
|
||||||
<style>
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #ffffff;
|
||||||
|
--text: #111111;
|
||||||
|
--muted: #666666;
|
||||||
|
--border: #e5e5e5;
|
||||||
|
--chip-bg: rgba(0, 0, 0, 0.04);
|
||||||
|
--chip-text: #000000;
|
||||||
|
--spinner-border: #8d8d8d5c;
|
||||||
|
--spinner-top: #b0b0b0;
|
||||||
|
--silence-bg: #f3f3f3;
|
||||||
|
--loading-bg: rgba(255, 77, 77, 0.06);
|
||||||
|
--button-bg: #ffffff;
|
||||||
|
--button-border: #e9e9e9;
|
||||||
|
--wave-stroke: #000000;
|
||||||
|
--label-dia-text: #868686;
|
||||||
|
--label-trans-text: #111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root:not([data-theme="light"]) {
|
||||||
|
--bg: #0b0b0b;
|
||||||
|
--text: #e6e6e6;
|
||||||
|
--muted: #9aa0a6;
|
||||||
|
--border: #333333;
|
||||||
|
--chip-bg: rgba(255, 255, 255, 0.08);
|
||||||
|
--chip-text: #e6e6e6;
|
||||||
|
--spinner-border: #555555;
|
||||||
|
--spinner-top: #dddddd;
|
||||||
|
--silence-bg: #1a1a1a;
|
||||||
|
--loading-bg: rgba(255, 77, 77, 0.12);
|
||||||
|
--button-bg: #111111;
|
||||||
|
--button-border: #333333;
|
||||||
|
--wave-stroke: #e6e6e6;
|
||||||
|
--label-dia-text: #b3b3b3;
|
||||||
|
--label-trans-text: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] {
|
||||||
|
--bg: #0b0b0b;
|
||||||
|
--text: #e6e6e6;
|
||||||
|
--muted: #9aa0a6;
|
||||||
|
--border: #333333;
|
||||||
|
--chip-bg: rgba(255, 255, 255, 0.08);
|
||||||
|
--chip-text: #e6e6e6;
|
||||||
|
--spinner-border: #555555;
|
||||||
|
--spinner-top: #dddddd;
|
||||||
|
--silence-bg: #1a1a1a;
|
||||||
|
--loading-bg: rgba(255, 77, 77, 0.12);
|
||||||
|
--button-bg: #111111;
|
||||||
|
--button-border: #333333;
|
||||||
|
--wave-stroke: #e6e6e6;
|
||||||
|
--label-dia-text: #b3b3b3;
|
||||||
|
--label-trans-text: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="light"] {
|
||||||
|
--bg: #ffffff;
|
||||||
|
--text: #111111;
|
||||||
|
--muted: #666666;
|
||||||
|
--border: #e5e5e5;
|
||||||
|
--chip-bg: rgba(0, 0, 0, 0.04);
|
||||||
|
--chip-text: #000000;
|
||||||
|
--spinner-border: #8d8d8d5c;
|
||||||
|
--spinner-top: #b0b0b0;
|
||||||
|
--silence-bg: #f3f3f3;
|
||||||
|
--loading-bg: rgba(255, 77, 77, 0.06);
|
||||||
|
--button-bg: #ffffff;
|
||||||
|
--button-border: #e9e9e9;
|
||||||
|
--wave-stroke: #000000;
|
||||||
|
--label-dia-text: #868686;
|
||||||
|
--label-trans-text: #111111;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
font-family: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
font-family: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
#recordButton {
|
#recordButton {
|
||||||
|
|
@ -17,10 +92,10 @@
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: white;
|
background-color: var(--button-bg);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
border: 1px solid rgb(233, 233, 233);
|
border: 1px solid var(--button-border);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
@ -94,14 +169,14 @@
|
||||||
.timer {
|
.timer {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: var(--text);
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#status {
|
#status {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #333;
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-container {
|
.settings-container {
|
||||||
|
|
@ -120,12 +195,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#chunkSelector,
|
#chunkSelector,
|
||||||
#websocketInput {
|
#websocketInput,
|
||||||
|
#themeSelector {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid var(--border);
|
||||||
background-color: #ffffff;
|
background-color: var(--button-bg);
|
||||||
|
color: var(--text);
|
||||||
max-height: 30px;
|
max-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,7 +211,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#chunkSelector:focus,
|
#chunkSelector:focus,
|
||||||
#websocketInput:focus {
|
#websocketInput:focus,
|
||||||
|
#themeSelector:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #007bff;
|
border-color: #007bff;
|
||||||
}
|
}
|
||||||
|
|
@ -156,18 +234,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#linesTranscript strong {
|
#linesTranscript strong {
|
||||||
color: #333;
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
#speaker {
|
#speaker {
|
||||||
border: 1px solid rgb(229, 229, 229);
|
border: 1px solid var(--border);
|
||||||
border-radius: 100px;
|
border-radius: 100px;
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
.label_diarization {
|
.label_diarization {
|
||||||
background-color: #ffffff66;
|
background-color: var(--chip-bg);
|
||||||
border-radius: 8px 8px 8px 8px;
|
border-radius: 8px 8px 8px 8px;
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
|
@ -175,11 +253,11 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
color: rgb(134, 134, 134)
|
color: var(--label-dia-text)
|
||||||
}
|
}
|
||||||
|
|
||||||
.label_transcription {
|
.label_transcription {
|
||||||
background-color: #ffffff66;
|
background-color: var(--chip-bg);
|
||||||
border-radius: 8px 8px 8px 8px;
|
border-radius: 8px 8px 8px 8px;
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
@ -187,11 +265,11 @@
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
color: #000000
|
color: var(--label-trans-text)
|
||||||
}
|
}
|
||||||
|
|
||||||
#timeInfo {
|
#timeInfo {
|
||||||
color: #666;
|
color: var(--muted);
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,7 +284,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.buffer_diarization {
|
.buffer_diarization {
|
||||||
color: rgb(134, 134, 134);
|
color: var(--label-dia-text);
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,8 +298,8 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
border: 2px solid #8d8d8d5c;
|
border: 2px solid var(--spinner-border);
|
||||||
border-top: 2px solid #6c6c6c7d;
|
border-top: 2px solid var(--spinner-top);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: spin 0.7s linear infinite;
|
animation: spin 0.7s linear infinite;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
@ -236,16 +314,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.silence {
|
.silence {
|
||||||
color: #666;
|
color: var(--muted);
|
||||||
background-color: #f3f3f3;
|
background-color: var(--silence-bg);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
color: #666;
|
color: var(--muted);
|
||||||
background-color: #ff4d4d0f;
|
background-color: var(--loading-bg);
|
||||||
border-radius: 8px 8px 8px 0px;
|
border-radius: 8px 8px 8px 0px;
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
@ -284,6 +362,14 @@
|
||||||
<label for="websocketInput">WebSocket URL:</label>
|
<label for="websocketInput">WebSocket URL:</label>
|
||||||
<input id="websocketInput" type="text" />
|
<input id="websocketInput" type="text" />
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="themeSelector">Theme:</label>
|
||||||
|
<select id="themeSelector">
|
||||||
|
<option value="system" selected>System</option>
|
||||||
|
<option value="light">Light</option>
|
||||||
|
<option value="dark">Dark</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -320,6 +406,52 @@
|
||||||
const websocketInput = document.getElementById("websocketInput");
|
const websocketInput = document.getElementById("websocketInput");
|
||||||
const linesTranscriptDiv = document.getElementById("linesTranscript");
|
const linesTranscriptDiv = document.getElementById("linesTranscript");
|
||||||
const timerElement = document.querySelector(".timer");
|
const timerElement = document.querySelector(".timer");
|
||||||
|
const themeSelector = document.getElementById("themeSelector");
|
||||||
|
|
||||||
|
function getWaveStroke() {
|
||||||
|
const styles = getComputedStyle(document.documentElement);
|
||||||
|
const v = styles.getPropertyValue("--wave-stroke").trim();
|
||||||
|
return v || "#000";
|
||||||
|
}
|
||||||
|
|
||||||
|
let waveStroke = getWaveStroke();
|
||||||
|
|
||||||
|
function updateWaveStroke() {
|
||||||
|
waveStroke = getWaveStroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTheme(pref) {
|
||||||
|
if (pref === "light") {
|
||||||
|
document.documentElement.setAttribute("data-theme", "light");
|
||||||
|
} else if (pref === "dark") {
|
||||||
|
document.documentElement.setAttribute("data-theme", "dark");
|
||||||
|
} else {
|
||||||
|
document.documentElement.removeAttribute("data-theme");
|
||||||
|
}
|
||||||
|
updateWaveStroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedThemePref = localStorage.getItem("themePreference") || "system";
|
||||||
|
applyTheme(savedThemePref);
|
||||||
|
if (themeSelector) {
|
||||||
|
themeSelector.value = savedThemePref;
|
||||||
|
themeSelector.addEventListener("change", () => {
|
||||||
|
const val = themeSelector.value;
|
||||||
|
localStorage.setItem("themePreference", val);
|
||||||
|
applyTheme(val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const darkMq = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
const handleOsThemeChange = () => {
|
||||||
|
const pref = localStorage.getItem("themePreference") || "system";
|
||||||
|
if (pref === "system") updateWaveStroke();
|
||||||
|
};
|
||||||
|
if (darkMq && darkMq.addEventListener) {
|
||||||
|
darkMq.addEventListener("change", handleOsThemeChange);
|
||||||
|
} else if (darkMq && darkMq.addListener) {
|
||||||
|
darkMq.addListener(handleOsThemeChange);
|
||||||
|
}
|
||||||
|
|
||||||
function fmt1(x) {
|
function fmt1(x) {
|
||||||
const n = Number(x);
|
const n = Number(x);
|
||||||
|
|
@ -452,7 +584,7 @@
|
||||||
|
|
||||||
function renderLinesWithBuffer(lines, buffer_diarization, buffer_transcription, remaining_time_diarization, remaining_time_transcription, isFinalizing = false, current_status = "active_transcription") {
|
function renderLinesWithBuffer(lines, buffer_diarization, buffer_transcription, remaining_time_diarization, remaining_time_transcription, isFinalizing = false, current_status = "active_transcription") {
|
||||||
if (current_status === "no_audio_detected") {
|
if (current_status === "no_audio_detected") {
|
||||||
linesTranscriptDiv.innerHTML = "<p style='text-align: center; color: #666; margin-top: 20px;'><em>No audio detected...</em></p>";
|
linesTranscriptDiv.innerHTML = "<p style='text-align: center; color: var(--muted); margin-top: 20px;'><em>No audio detected...</em></p>";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -502,7 +634,7 @@
|
||||||
let currentLineText = item.text || "";
|
let currentLineText = item.text || "";
|
||||||
|
|
||||||
if (idx === lines.length - 1) {
|
if (idx === lines.length - 1) {
|
||||||
if (!isFinalizing) {
|
if (!isFinalizing && item.speaker !== -2) {
|
||||||
if (remaining_time_transcription > 0) {
|
if (remaining_time_transcription > 0) {
|
||||||
speakerLabel += `<span class="label_transcription"><span class="spinner"></span>Transcription lag <span id='timeInfo'><span class="lag-transcription-value">${fmt1(remaining_time_transcription)}</span>s</span></span>`;
|
speakerLabel += `<span class="label_transcription"><span class="spinner"></span>Transcription lag <span id='timeInfo'><span class="lag-transcription-value">${fmt1(remaining_time_transcription)}</span>s</span></span>`;
|
||||||
}
|
}
|
||||||
|
|
@ -553,7 +685,7 @@
|
||||||
|
|
||||||
waveCtx.clearRect(0, 0, waveCanvas.width / (window.devicePixelRatio || 1), waveCanvas.height / (window.devicePixelRatio || 1));
|
waveCtx.clearRect(0, 0, waveCanvas.width / (window.devicePixelRatio || 1), waveCanvas.height / (window.devicePixelRatio || 1));
|
||||||
waveCtx.lineWidth = 1;
|
waveCtx.lineWidth = 1;
|
||||||
waveCtx.strokeStyle = 'rgb(0, 0, 0)';
|
waveCtx.strokeStyle = waveStroke;
|
||||||
waveCtx.beginPath();
|
waveCtx.beginPath();
|
||||||
|
|
||||||
const sliceWidth = (waveCanvas.width / (window.devicePixelRatio || 1)) / bufferLength;
|
const sliceWidth = (waveCanvas.width / (window.devicePixelRatio || 1)) / bufferLength;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue