feat: enhance full-room glow with configurable controls and prevent text wrapping

Add white-space: nowrap to header, footer, and code-part elements to
prevent line breaks in narrow viewports. Replace static CSS keyframes
with dynamic generation so glow color, intensity, opacity, outline
thickness, and pulse duration are all configurable from the controls
panel. Double the default outline thickness from 4px to 8px.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-05-03 01:24:47 -04:00
parent 18d66c2dba
commit 3bd5752cb5
2 changed files with 99 additions and 9 deletions

View File

@@ -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 = '';
}
}
}