Compare commits
2 Commits
18d66c2dba
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7a834a992
|
||
|
|
3bd5752cb5
|
49
js/controls.js
vendored
49
js/controls.js
vendored
@@ -194,6 +194,42 @@ function initControls(manager, wsClient, components) {
|
||||
localStorage.setItem(STORAGE_API_KEY, apiKeyInput.value.trim());
|
||||
});
|
||||
|
||||
const importKeyBtn = document.getElementById('import-key-btn');
|
||||
const importKeyFile = /** @type {HTMLInputElement | null} */ (
|
||||
document.getElementById('import-key-file')
|
||||
);
|
||||
const promptKeyBtn = document.getElementById('prompt-key-btn');
|
||||
|
||||
function applyImportedKey(key) {
|
||||
if (!apiKeyInput) return;
|
||||
apiKeyInput.value = key;
|
||||
localStorage.setItem(STORAGE_API_KEY, key);
|
||||
}
|
||||
|
||||
if (importKeyBtn && importKeyFile) {
|
||||
importKeyBtn.addEventListener('click', () => {
|
||||
importKeyFile.value = '';
|
||||
importKeyFile.click();
|
||||
});
|
||||
importKeyFile.addEventListener('change', () => {
|
||||
const file = importKeyFile.files && importKeyFile.files[0];
|
||||
if (!file) return;
|
||||
var reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
var text = typeof reader.result === 'string' ? reader.result.trim() : '';
|
||||
if (text) applyImportedKey(text);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
if (promptKeyBtn) {
|
||||
promptKeyBtn.addEventListener('click', () => {
|
||||
var key = window.prompt('Paste or type your API key:');
|
||||
if (key && key.trim()) applyImportedKey(key.trim());
|
||||
});
|
||||
}
|
||||
|
||||
const volumeSlider = document.getElementById('volume-slider');
|
||||
const volumeValue = document.getElementById('volume-value');
|
||||
const themeSound = /** @type {HTMLAudioElement | null} */ (
|
||||
@@ -340,22 +376,25 @@ function initConnectionStatusHandler() {
|
||||
const wsDisconnectRow = document.getElementById('ws-disconnect-row');
|
||||
const apiUrlInput = document.getElementById('api-url-input');
|
||||
const apiKeyInput = document.getElementById('api-key-input');
|
||||
const importKeyBtn = document.getElementById('import-key-btn');
|
||||
const promptKeyBtn = document.getElementById('prompt-key-btn');
|
||||
|
||||
return (state, message) => {
|
||||
if (wsStatusDot) wsStatusDot.className = `status-dot ${state}`;
|
||||
if (wsStatusText) wsStatusText.textContent = message ?? String(state);
|
||||
|
||||
if (state === 'connected') {
|
||||
var isConnected = state === 'connected';
|
||||
if (isConnected) {
|
||||
if (wsConnectBtn) wsConnectBtn.style.display = 'none';
|
||||
if (wsDisconnectRow) wsDisconnectRow.style.display = 'flex';
|
||||
if (apiUrlInput) apiUrlInput.disabled = true;
|
||||
if (apiKeyInput) apiKeyInput.disabled = true;
|
||||
} else {
|
||||
if (wsConnectBtn) wsConnectBtn.style.display = 'block';
|
||||
if (wsDisconnectRow) wsDisconnectRow.style.display = 'none';
|
||||
if (apiUrlInput) apiUrlInput.disabled = false;
|
||||
if (apiKeyInput) apiKeyInput.disabled = false;
|
||||
}
|
||||
if (apiUrlInput) apiUrlInput.disabled = isConnected;
|
||||
if (apiKeyInput) apiKeyInput.disabled = isConnected;
|
||||
if (importKeyBtn) importKeyBtn.disabled = isConnected;
|
||||
if (promptKeyBtn) promptKeyBtn.disabled = isConnected;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@
|
||||
* @property {HTMLInputElement} line2AppearDuration
|
||||
* @property {HTMLInputElement} line2HideTime
|
||||
* @property {HTMLInputElement} line2HideDuration
|
||||
* @property {HTMLInputElement} glowColor
|
||||
* @property {HTMLInputElement} glowIntensity
|
||||
* @property {HTMLInputElement} glowOpacity
|
||||
* @property {HTMLInputElement} glowWhiteIntensity
|
||||
* @property {HTMLInputElement} glowOutline
|
||||
* @property {HTMLInputElement} glowPulseDuration
|
||||
*/
|
||||
|
||||
class RoomCodeDisplay {
|
||||
@@ -65,6 +71,8 @@ class RoomCodeDisplay {
|
||||
/** @type {number | null} */
|
||||
#meterRafId = null;
|
||||
|
||||
/** @type {HTMLStyleElement | null} */
|
||||
#glowStyleEl = null;
|
||||
|
||||
/**
|
||||
* @param {RoomCodeDisplayElements} elements
|
||||
@@ -73,6 +81,12 @@ class RoomCodeDisplay {
|
||||
init(elements, inputs) {
|
||||
this.#elements = elements;
|
||||
this.#inputs = inputs;
|
||||
|
||||
if (!this.#glowStyleEl) {
|
||||
this.#glowStyleEl = document.createElement('style');
|
||||
this.#glowStyleEl.id = 'glow-keyframes';
|
||||
document.head.appendChild(this.#glowStyleEl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,6 +231,9 @@ class RoomCodeDisplay {
|
||||
footer.style.color = inputs.footerColor.value;
|
||||
footer.style.fontSize = `${inputs.footerSize.value}px`;
|
||||
footer.style.transform = `translateY(${inputs.footerOffset.value}px)`;
|
||||
|
||||
this.#rebuildGlowKeyframes();
|
||||
this.#checkFullPulse();
|
||||
}
|
||||
|
||||
#startAnimation() {
|
||||
@@ -370,14 +387,52 @@ class RoomCodeDisplay {
|
||||
this.#meterRafId = requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
#rebuildGlowKeyframes() {
|
||||
const inputs = this.#inputs;
|
||||
const style = this.#glowStyleEl;
|
||||
if (!inputs || !style) return;
|
||||
|
||||
const outline = Number(inputs.glowOutline?.value ?? 8);
|
||||
const whiteGlow = Number(inputs.glowWhiteIntensity?.value ?? 40);
|
||||
const colorGlow = Number(inputs.glowIntensity?.value ?? 80);
|
||||
const opacity = Number(inputs.glowOpacity?.value ?? 0.6);
|
||||
const duration = Number(inputs.glowPulseDuration?.value ?? 1.2);
|
||||
|
||||
const glowHex = inputs.glowColor?.value ?? '#f35dcb';
|
||||
const r = parseInt(glowHex.slice(1, 3), 16);
|
||||
const g = parseInt(glowHex.slice(3, 5), 16);
|
||||
const b = parseInt(glowHex.slice(5, 7), 16);
|
||||
|
||||
const outlineShadow =
|
||||
`drop-shadow(0 0 ${outline}px black) drop-shadow(0 0 ${outline}px black)`;
|
||||
|
||||
style.textContent = `
|
||||
@keyframes meter-full-pulse {
|
||||
0%, 100% {
|
||||
filter: ${outlineShadow} drop-shadow(0 0 0 rgba(255,255,255,0));
|
||||
}
|
||||
50% {
|
||||
filter: ${outlineShadow}
|
||||
drop-shadow(0 0 ${whiteGlow}px rgba(255,255,255,0.95))
|
||||
drop-shadow(0 0 ${colorGlow}px rgba(${r},${g},${b},${opacity}));
|
||||
}
|
||||
}
|
||||
#header.meter-full-pulse {
|
||||
animation: meter-full-pulse ${duration}s ease-in-out infinite;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
#checkFullPulse() {
|
||||
const header = this.#elements?.header;
|
||||
if (!header) return;
|
||||
|
||||
if (this.#meterFill >= 1) {
|
||||
this.#rebuildGlowKeyframes();
|
||||
header.classList.add('meter-full-pulse');
|
||||
} else {
|
||||
header.classList.remove('meter-full-pulse');
|
||||
header.style.animation = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#header {
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-220px);
|
||||
color: #f35dcb;
|
||||
@@ -45,19 +46,12 @@
|
||||
filter: drop-shadow(3px 3px 8px rgba(0, 0, 0, 0.8));
|
||||
}
|
||||
|
||||
@keyframes meter-full-pulse {
|
||||
0% { filter: drop-shadow(3px 3px 8px rgba(0,0,0,0.8)); }
|
||||
50% { filter: drop-shadow(0 0 20px rgba(255,255,255,0.6)) drop-shadow(3px 3px 8px rgba(0,0,0,0.8)); }
|
||||
100% { filter: drop-shadow(3px 3px 8px rgba(0,0,0,0.8)); }
|
||||
}
|
||||
|
||||
#header.meter-full-pulse {
|
||||
animation: meter-full-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
/* meter-full-pulse keyframes are generated dynamically by RoomCodeDisplay */
|
||||
|
||||
#footer {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
transform: translateY(160px);
|
||||
color: #f35dcb;
|
||||
@@ -77,6 +71,7 @@
|
||||
letter-spacing: 8px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease;
|
||||
@@ -542,6 +537,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Glow Effect Settings Section -->
|
||||
<div class="control-section">
|
||||
<div class="section-header">
|
||||
<span>Glow Effect Settings</span>
|
||||
<span class="toggle-indicator"></span>
|
||||
</div>
|
||||
<div class="section-content">
|
||||
<div class="control-row">
|
||||
<label>Glow Color:</label>
|
||||
<input type="color" id="glow-color-input" value="#f35dcb">
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<label>Glow Intensity (px):</label>
|
||||
<input type="number" id="glow-intensity-input" value="80" min="10" max="200" step="5">
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<label>Glow Opacity:</label>
|
||||
<input type="number" id="glow-opacity-input" value="0.6" min="0" max="1" step="0.05">
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<label>White Glow (px):</label>
|
||||
<input type="number" id="glow-white-intensity-input" value="40" min="0" max="200" step="5">
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<label>Outline Thickness (px):</label>
|
||||
<input type="number" id="glow-outline-input" value="8" min="0" max="30" step="1">
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<label>Pulse Duration (sec):</label>
|
||||
<input type="number" id="glow-pulse-duration-input" value="1.2" min="0.2" max="5" step="0.1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer Settings Section -->
|
||||
<div class="control-section">
|
||||
<div class="section-header">
|
||||
@@ -735,6 +764,11 @@
|
||||
<div class="control-row">
|
||||
<input type="password" id="api-key-input" placeholder="Enter API key">
|
||||
</div>
|
||||
<div class="control-row" style="gap: 6px;">
|
||||
<button id="import-key-btn" title="Import API key from a .txt file">Import from File</button>
|
||||
<button id="prompt-key-btn" title="Enter API key via dialog (paste works)">Paste Key</button>
|
||||
<input type="file" id="import-key-file" accept=".txt,.key" style="display: none;">
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<button id="ws-connect-btn">Connect</button>
|
||||
</div>
|
||||
@@ -855,6 +889,12 @@
|
||||
line2AppearDuration: document.getElementById('line2-appear-duration'),
|
||||
line2HideTime: document.getElementById('line2-hide-time'),
|
||||
line2HideDuration: document.getElementById('line2-hide-duration'),
|
||||
glowColor: document.getElementById('glow-color-input'),
|
||||
glowIntensity: document.getElementById('glow-intensity-input'),
|
||||
glowOpacity: document.getElementById('glow-opacity-input'),
|
||||
glowWhiteIntensity: document.getElementById('glow-white-intensity-input'),
|
||||
glowOutline: document.getElementById('glow-outline-input'),
|
||||
glowPulseDuration: document.getElementById('glow-pulse-duration-input'),
|
||||
}
|
||||
);
|
||||
manager.registerComponent('roomCode', roomCodeDisplay);
|
||||
|
||||
Reference in New Issue
Block a user