Add reduced-styles toggle, decrement, and kiosk improvements
- Add reduced_styles config to switch between Tailwind CDN and inline CSS - Restore liquid wave animations in full-styles mode - Add score decrement endpoint and staged hold-to-reset (decrement/arm/reset) - Improve start.sh: prefer .venv, cleanup stale processes, X display fallback - Scope inline utility classes to body.reduced-styles
This commit is contained in:
parent
a70f739ef7
commit
d8ebfcda13
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.venv/
|
||||||
|
|||||||
10
app.py
10
app.py
@ -123,6 +123,7 @@ def index():
|
|||||||
return render_template(
|
return render_template(
|
||||||
"index.html",
|
"index.html",
|
||||||
display_id=config["display_id"],
|
display_id=config["display_id"],
|
||||||
|
reduced_styles=config.get("reduced_styles", True),
|
||||||
header=config.get("header", ""),
|
header=config.get("header", ""),
|
||||||
header_font_size=config.get("header_font_size", 0),
|
header_font_size=config.get("header_font_size", 0),
|
||||||
content_padding_top=config.get("content_padding_top", 0),
|
content_padding_top=config.get("content_padding_top", 0),
|
||||||
@ -171,6 +172,7 @@ def api_status():
|
|||||||
"header_font_size": config.get("header_font_size", 0),
|
"header_font_size": config.get("header_font_size", 0),
|
||||||
"content_padding_top": config.get("content_padding_top", 0),
|
"content_padding_top": config.get("content_padding_top", 0),
|
||||||
"content_gap": config.get("content_gap", 1),
|
"content_gap": config.get("content_gap", 1),
|
||||||
|
"reduced_styles": config.get("reduced_styles", True),
|
||||||
"ip": get_local_ip(),
|
"ip": get_local_ip(),
|
||||||
"port": PORT,
|
"port": PORT,
|
||||||
"scores": dict(scores),
|
"scores": dict(scores),
|
||||||
@ -288,6 +290,14 @@ def increment(side):
|
|||||||
return jsonify(scores)
|
return jsonify(scores)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/decrement/<side>", methods=["GET", "POST"])
|
||||||
|
def decrement(side):
|
||||||
|
if side in scores and scores[side] > 0:
|
||||||
|
scores[side] -= 1
|
||||||
|
broadcast({"type": "score", "scores": dict(scores), "side": side})
|
||||||
|
return jsonify(scores)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/reset", methods=["GET", "POST"])
|
@app.route("/reset", methods=["GET", "POST"])
|
||||||
def reset():
|
def reset():
|
||||||
scores["left"] = 0
|
scores["left"] = 0
|
||||||
|
|||||||
17
config.toml
17
config.toml
@ -1,22 +1,23 @@
|
|||||||
display_id = 1
|
display_id = 6
|
||||||
display_name = "Test"
|
display_name = "Table 6"
|
||||||
header = "Grand Finals"
|
header = "carom sports"
|
||||||
header_font_size = 4
|
header_font_size = 4
|
||||||
content_padding_top = 4
|
content_padding_top = 4
|
||||||
content_gap = 1
|
content_gap = 1
|
||||||
|
reduced_styles = false
|
||||||
|
|
||||||
[left]
|
[left]
|
||||||
name = "Tim"
|
name = " Phil"
|
||||||
subtitle = "Test 456"
|
subtitle = ""
|
||||||
color = "#dc2626"
|
color = "#2c41dd"
|
||||||
name_font_size = 3.75
|
name_font_size = 3.75
|
||||||
subtitle_font_size = 1.25
|
subtitle_font_size = 1.25
|
||||||
score_font_size = 30
|
score_font_size = 30
|
||||||
|
|
||||||
[right]
|
[right]
|
||||||
name = " Nick"
|
name = " Nick"
|
||||||
subtitle = "Test 123"
|
subtitle = ""
|
||||||
color = "#1a5fb4"
|
color = "#e708ac"
|
||||||
name_font_size = 3.75
|
name_font_size = 3.75
|
||||||
subtitle_font_size = 1.25
|
subtitle_font_size = 1.25
|
||||||
score_font_size = 30
|
score_font_size = 30
|
||||||
|
|||||||
30
start.sh
30
start.sh
@ -3,11 +3,37 @@ set -e
|
|||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
python3 app.py &
|
# Clean up previous instances so restarts are safe
|
||||||
|
fuser -k 50743/tcp 2>/dev/null || true
|
||||||
|
pkill -x chromium 2>/dev/null || true
|
||||||
|
pkill -x firefox 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
if [ -x ".venv/bin/python3" ]; then
|
||||||
|
PYTHON=".venv/bin/python3"
|
||||||
|
else
|
||||||
|
PYTHON="python3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$PYTHON" app.py &
|
||||||
APP_PID=$!
|
APP_PID=$!
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
chromium-browser --kiosk http://localhost:50743 &
|
BROWSER_FLAGS="--kiosk http://localhost:50743 \
|
||||||
|
--enable-gpu-rasterization \
|
||||||
|
--ignore-gpu-blocklist \
|
||||||
|
--no-first-run \
|
||||||
|
--no-default-browser-check \
|
||||||
|
--disable-dev-shm-usage \
|
||||||
|
--disable-infobars"
|
||||||
|
|
||||||
|
if command -v chromium >/dev/null 2>&1; then
|
||||||
|
export DISPLAY="${DISPLAY:-:0}"
|
||||||
|
export XAUTHORITY="${XAUTHORITY:-$HOME/.Xauthority}"
|
||||||
|
chromium $BROWSER_FLAGS &
|
||||||
|
else
|
||||||
|
firefox --kiosk http://localhost:50743 &
|
||||||
|
fi
|
||||||
|
|
||||||
wait $APP_PID
|
wait $APP_PID
|
||||||
|
|||||||
@ -11,18 +11,41 @@ html, body {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #0a0a0f;
|
background: #0a0a0f;
|
||||||
|
cursor: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============ TAILWIND UTILITY REPLACEMENTS ============ */
|
||||||
|
/* Only used when reduced_styles = true (no Tailwind CDN). */
|
||||||
|
.reduced-styles .flex { display: flex; }
|
||||||
|
.reduced-styles .flex-col { flex-direction: column; }
|
||||||
|
.reduced-styles .flex-1 { flex: 1 1 0%; }
|
||||||
|
.reduced-styles .h-screen { height: 100vh; }
|
||||||
|
.reduced-styles .w-screen { width: 100vw; }
|
||||||
|
.reduced-styles .text-center { text-align: center; }
|
||||||
|
.reduced-styles .pt-6 { padding-top: 1.5rem; }
|
||||||
|
.reduced-styles .pointer-events-none { pointer-events: none; }
|
||||||
|
.reduced-styles .items-center { align-items: center; }
|
||||||
|
.reduced-styles .justify-center { justify-content: center; }
|
||||||
|
.reduced-styles .relative { position: relative; }
|
||||||
|
.reduced-styles .tracking-widest { letter-spacing: 0.1em; }
|
||||||
|
.reduced-styles .uppercase { text-transform: uppercase; }
|
||||||
|
.reduced-styles .whitespace-nowrap { white-space: nowrap; }
|
||||||
|
.reduced-styles .leading-none { line-height: 1; }
|
||||||
|
.reduced-styles .font-black { font-weight: 900; }
|
||||||
|
.reduced-styles .font-bold { font-weight: 700; }
|
||||||
|
.reduced-styles .italic { font-style: italic; }
|
||||||
|
.reduced-styles .font-cinzel { font-family: 'Cinzel', serif; }
|
||||||
|
.reduced-styles .font-orbitron { font-family: 'Orbitron', sans-serif; }
|
||||||
|
.reduced-styles .font-playfair { font-family: 'Playfair Display', serif; }
|
||||||
|
|
||||||
/* ============ SIDES ============ */
|
/* ============ SIDES ============ */
|
||||||
.side {
|
.side {
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
transition: filter 0.15s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.side:active {
|
.side:active .side-glow {
|
||||||
filter: brightness(1.15);
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hold to reset progress bar */
|
/* Hold to reset progress bar */
|
||||||
@ -36,31 +59,34 @@ html, body {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background: var(--team-color);
|
background: var(--team-color);
|
||||||
box-shadow: 0 0 12px var(--team-color);
|
box-shadow: 0 0 12px var(--team-color);
|
||||||
width: 0;
|
width: 100%;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: left center;
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side.holding::after {
|
.side.holding::after {
|
||||||
animation: hold-progress 1.5s linear forwards;
|
animation: hold-progress 4s linear forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side.hold-reset-arm::after {
|
||||||
|
background: #ef4444;
|
||||||
|
box-shadow: 0 0 12px #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side.hold-cancel::after {
|
.side.hold-cancel::after {
|
||||||
animation: hold-fade 0.2s ease-out forwards;
|
animation: hold-fade 0.2s ease-out forwards;
|
||||||
width: 100%;
|
transform: scaleX(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes hold-progress {
|
@keyframes hold-progress {
|
||||||
0% { width: 0; }
|
0% { transform: scaleX(0); }
|
||||||
100% { width: 100%; }
|
100% { transform: scaleX(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes hold-fade {
|
@keyframes hold-fade {
|
||||||
0% { opacity: 1; }
|
0% { opacity: 1; }
|
||||||
100% { opacity: 0; width: 100%; }
|
100% { opacity: 0; }
|
||||||
}
|
|
||||||
|
|
||||||
.side.holding {
|
|
||||||
filter: brightness(0.7) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-glow {
|
.side-glow {
|
||||||
@ -71,6 +97,9 @@ html, body {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
will-change: opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
.side-content {
|
.side-content {
|
||||||
@ -84,8 +113,7 @@ html, body {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: #c9a84c;
|
color: #c9a84c;
|
||||||
text-shadow:
|
text-shadow:
|
||||||
0 0 20px rgba(201, 168, 76, 0.3),
|
0 0 10px rgba(201, 168, 76, 0.35);
|
||||||
0 0 40px rgba(201, 168, 76, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============ TEAM SUBTITLE ============ */
|
/* ============ TEAM SUBTITLE ============ */
|
||||||
@ -103,9 +131,8 @@ html, body {
|
|||||||
position: relative;
|
position: relative;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-shadow:
|
text-shadow:
|
||||||
0 0 0.04em currentColor,
|
0 0 0.05em currentColor,
|
||||||
0 0 0.1em currentColor,
|
0 0 0.12em currentColor;
|
||||||
0 0 0.2em color-mix(in srgb, currentColor 40%, transparent);
|
|
||||||
transition: text-shadow 0.3s ease;
|
transition: text-shadow 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,9 +207,7 @@ html, body {
|
|||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
90deg,
|
90deg,
|
||||||
var(--team-color) 0%,
|
var(--team-color) 0%,
|
||||||
var(--team-color) 40%,
|
|
||||||
var(--team-color) 60%,
|
var(--team-color) 60%,
|
||||||
color-mix(in srgb, var(--team-color) 40%, transparent) 85%,
|
|
||||||
transparent 100%
|
transparent 100%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -191,20 +216,18 @@ html, body {
|
|||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
90deg,
|
90deg,
|
||||||
transparent 0%,
|
transparent 0%,
|
||||||
color-mix(in srgb, var(--team-color) 40%, transparent) 15%,
|
|
||||||
var(--team-color) 40%,
|
var(--team-color) 40%,
|
||||||
var(--team-color) 60%,
|
|
||||||
var(--team-color) 100%
|
var(--team-color) 100%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============ LIQUID WAVES ============ */
|
/* ============ LIQUID WAVES (full styles only) ============ */
|
||||||
.wave-container {
|
body:not(.reduced-styles) .wave-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wave {
|
body:not(.reduced-styles) .wave {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -25%;
|
left: -25%;
|
||||||
width: 150%;
|
width: 150%;
|
||||||
@ -213,7 +236,7 @@ html, body {
|
|||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wave-front {
|
body:not(.reduced-styles) .wave-front {
|
||||||
height: 160%;
|
height: 160%;
|
||||||
top: -15%;
|
top: -15%;
|
||||||
background: var(--team-color);
|
background: var(--team-color);
|
||||||
@ -221,7 +244,7 @@ html, body {
|
|||||||
animation-duration: 3s;
|
animation-duration: 3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wave-back {
|
body:not(.reduced-styles) .wave-back {
|
||||||
height: 200%;
|
height: 200%;
|
||||||
top: -40%;
|
top: -40%;
|
||||||
background: var(--team-color);
|
background: var(--team-color);
|
||||||
@ -238,18 +261,28 @@ html, body {
|
|||||||
/* ============ HAND WAVE COUNTDOWN ============ */
|
/* ============ HAND WAVE COUNTDOWN ============ */
|
||||||
.side.counting {
|
.side.counting {
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 0 60px rgba(59, 130, 246, 0.9),
|
0 0 40px rgba(59, 130, 246, 0.4),
|
||||||
0 0 120px rgba(59, 130, 246, 0.5),
|
inset 0 0 60px rgba(59, 130, 246, 0.15);
|
||||||
0 0 200px rgba(59, 130, 246, 0.25),
|
|
||||||
inset 0 0 80px rgba(59, 130, 246, 0.2);
|
|
||||||
animation: counting-pulse 0.6s ease-in-out infinite alternate;
|
|
||||||
outline: 4px solid rgba(59, 130, 246, 0.7);
|
outline: 4px solid rgba(59, 130, 246, 0.7);
|
||||||
outline-offset: -4px;
|
outline-offset: -4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.side.counting::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow:
|
||||||
|
0 0 50px rgba(59, 130, 246, 0.55),
|
||||||
|
inset 0 0 90px rgba(59, 130, 246, 0.2);
|
||||||
|
animation: counting-pulse 0.6s ease-in-out infinite alternate;
|
||||||
|
will-change: opacity;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes counting-pulse {
|
@keyframes counting-pulse {
|
||||||
0% { box-shadow: 0 0 50px rgba(59,130,246,0.7), 0 0 100px rgba(59,130,246,0.3), 0 0 160px rgba(59,130,246,0.15), inset 0 0 60px rgba(59,130,246,0.12); outline-color: rgba(59,130,246,0.5); }
|
0% { opacity: 0.4; }
|
||||||
100% { box-shadow: 0 0 80px rgba(59,130,246,1.0), 0 0 160px rgba(59,130,246,0.6), 0 0 260px rgba(59,130,246,0.35), inset 0 0 120px rgba(59,130,246,0.3); outline-color: rgba(59,130,246,0.9); }
|
100% { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.hw-bar {
|
.hw-bar {
|
||||||
@ -261,7 +294,9 @@ html, body {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background: #60a5fa;
|
background: #60a5fa;
|
||||||
box-shadow: 0 0 20px #3b82f6, 0 0 40px #3b82f6;
|
box-shadow: 0 0 20px #3b82f6, 0 0 40px #3b82f6;
|
||||||
width: 0;
|
width: 100%;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: left center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hw-bar.active {
|
.hw-bar.active {
|
||||||
@ -269,8 +304,8 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes hw-progress {
|
@keyframes hw-progress {
|
||||||
0% { width: 0; }
|
0% { transform: scaleX(0); }
|
||||||
100% { width: 100%; }
|
100% { transform: scaleX(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============ RESET CONFIRM OVERLAY ============ */
|
/* ============ RESET CONFIRM OVERLAY ============ */
|
||||||
@ -312,7 +347,9 @@ html, body {
|
|||||||
height: 8px;
|
height: 8px;
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: 0 0 20px white, 0 0 40px white;
|
box-shadow: 0 0 20px white, 0 0 40px white;
|
||||||
width: 0;
|
width: 100%;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: left center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#reset-confirm.show .confirm-bar {
|
#reset-confirm.show .confirm-bar {
|
||||||
@ -320,6 +357,6 @@ html, body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes confirm-progress {
|
@keyframes confirm-progress {
|
||||||
0% { width: 0; }
|
0% { transform: scaleX(0); }
|
||||||
100% { width: 100%; }
|
100% { transform: scaleX(1); }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,6 +44,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fitAll() {
|
function fitAll() {
|
||||||
|
scoreFontCache = {};
|
||||||
var headerRow = document.querySelector('.header-row');
|
var headerRow = document.querySelector('.header-row');
|
||||||
if (headerRow) {
|
if (headerRow) {
|
||||||
var span = headerRow.querySelector('span');
|
var span = headerRow.querySelector('span');
|
||||||
@ -104,33 +105,52 @@
|
|||||||
// ---- Tap / Hold handlers ----
|
// ---- Tap / Hold handlers ----
|
||||||
document.querySelectorAll('.side').forEach(function (side) {
|
document.querySelectorAll('.side').forEach(function (side) {
|
||||||
var sideName = side.dataset.side;
|
var sideName = side.dataset.side;
|
||||||
var holdTimer = null;
|
|
||||||
var holdFadeTimer = null;
|
var holdFadeTimer = null;
|
||||||
|
var holdDecTimer = null;
|
||||||
|
var holdResetArmTimer = null;
|
||||||
|
var holdResetTimer = null;
|
||||||
var holdFired = false;
|
var holdFired = false;
|
||||||
|
|
||||||
|
function clearHoldTimers() {
|
||||||
|
if (holdFadeTimer) { clearTimeout(holdFadeTimer); holdFadeTimer = null; }
|
||||||
|
if (holdDecTimer) { clearTimeout(holdDecTimer); holdDecTimer = null; }
|
||||||
|
if (holdResetArmTimer) { clearTimeout(holdResetArmTimer); holdResetArmTimer = null; }
|
||||||
|
if (holdResetTimer) { clearTimeout(holdResetTimer); holdResetTimer = null; }
|
||||||
|
}
|
||||||
|
|
||||||
function onStart(e) {
|
function onStart(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (debounce) return;
|
if (debounce) return;
|
||||||
holdFired = false;
|
holdFired = false;
|
||||||
side.classList.remove('hold-cancel');
|
side.classList.remove('hold-cancel');
|
||||||
|
side.classList.remove('hold-reset-arm');
|
||||||
|
|
||||||
holdFadeTimer = setTimeout(function () {
|
holdFadeTimer = setTimeout(function () {
|
||||||
void side.offsetWidth;
|
void side.offsetWidth;
|
||||||
side.classList.add('holding');
|
side.classList.add('holding');
|
||||||
}, 500);
|
}, 300);
|
||||||
|
|
||||||
holdTimer = setTimeout(function () {
|
holdDecTimer = setTimeout(function () {
|
||||||
|
holdFired = true;
|
||||||
|
decrement(sideName);
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
holdResetArmTimer = setTimeout(function () {
|
||||||
|
side.classList.add('hold-reset-arm');
|
||||||
|
}, 2500);
|
||||||
|
|
||||||
|
holdResetTimer = setTimeout(function () {
|
||||||
holdFired = true;
|
holdFired = true;
|
||||||
side.classList.remove('holding');
|
side.classList.remove('holding');
|
||||||
side.classList.remove('hold-cancel');
|
side.classList.remove('hold-reset-arm');
|
||||||
resetSide(sideName);
|
resetSide(sideName);
|
||||||
}, 2000);
|
}, 4000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnd(e) {
|
function onEnd(e) {
|
||||||
if (holdFadeTimer) { clearTimeout(holdFadeTimer); holdFadeTimer = null; }
|
clearHoldTimers();
|
||||||
if (holdTimer) { clearTimeout(holdTimer); holdTimer = null; }
|
|
||||||
side.classList.remove('holding');
|
side.classList.remove('holding');
|
||||||
|
side.classList.remove('hold-reset-arm');
|
||||||
if (holdFired) {
|
if (holdFired) {
|
||||||
side.classList.remove('hold-cancel');
|
side.classList.remove('hold-cancel');
|
||||||
return;
|
return;
|
||||||
@ -144,10 +164,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onCancel() {
|
function onCancel() {
|
||||||
if (holdFadeTimer) { clearTimeout(holdFadeTimer); holdFadeTimer = null; }
|
clearHoldTimers();
|
||||||
if (holdTimer) { clearTimeout(holdTimer); holdTimer = null; }
|
|
||||||
side.classList.remove('holding');
|
side.classList.remove('holding');
|
||||||
side.classList.remove('hold-cancel');
|
side.classList.remove('hold-cancel');
|
||||||
|
side.classList.remove('hold-reset-arm');
|
||||||
}
|
}
|
||||||
|
|
||||||
side.addEventListener('pointerdown', onStart);
|
side.addEventListener('pointerdown', onStart);
|
||||||
@ -172,7 +192,7 @@
|
|||||||
var bar = s.querySelector('.hw-bar');
|
var bar = s.querySelector('.hw-bar');
|
||||||
if (bar) {
|
if (bar) {
|
||||||
bar.classList.remove('active');
|
bar.classList.remove('active');
|
||||||
bar.style.width = '0';
|
bar.style.transform = 'scaleX(0)';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -185,7 +205,7 @@
|
|||||||
if (el) el.classList.add('counting');
|
if (el) el.classList.add('counting');
|
||||||
var bar = document.querySelector('.side-' + side + ' .hw-bar');
|
var bar = document.querySelector('.side-' + side + ' .hw-bar');
|
||||||
if (bar) {
|
if (bar) {
|
||||||
bar.style.width = '0';
|
bar.style.transform = 'scaleX(0)';
|
||||||
void bar.offsetWidth;
|
void bar.offsetWidth;
|
||||||
bar.classList.add('active');
|
bar.classList.add('active');
|
||||||
}
|
}
|
||||||
@ -290,7 +310,13 @@
|
|||||||
fetch('/increment/' + side, { method: 'POST' }).catch(function () {});
|
fetch('/increment/' + side, { method: 'POST' }).catch(function () {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Decrement ----
|
||||||
|
function decrement(side) {
|
||||||
|
fetch('/decrement/' + side, { method: 'POST' }).catch(function () {});
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Update display ----
|
// ---- Update display ----
|
||||||
|
var scoreFontCache = {};
|
||||||
function updateDisplay(side, value) {
|
function updateDisplay(side, value) {
|
||||||
var el = document.getElementById('score-' + side);
|
var el = document.getElementById('score-' + side);
|
||||||
el.textContent = value;
|
el.textContent = value;
|
||||||
@ -298,10 +324,16 @@
|
|||||||
var sfs = parseFloat(el.dataset.scoreFontSize);
|
var sfs = parseFloat(el.dataset.scoreFontSize);
|
||||||
if (sfs > 0) {
|
if (sfs > 0) {
|
||||||
el.style.fontSize = sfs + 'rem';
|
el.style.fontSize = sfs + 'rem';
|
||||||
|
} else {
|
||||||
|
var key = side + '-' + String(value).length;
|
||||||
|
if (scoreFontCache[key]) {
|
||||||
|
el.style.fontSize = scoreFontCache[key] + 'px';
|
||||||
} else {
|
} else {
|
||||||
var sideEl = document.querySelector('.side-' + side);
|
var sideEl = document.querySelector('.side-' + side);
|
||||||
var rect = sideEl.getBoundingClientRect();
|
var rect = sideEl.getBoundingClientRect();
|
||||||
fitTextHeight(el, rect.height * 0.55);
|
fitTextHeight(el, rect.height * 0.55);
|
||||||
|
scoreFontCache[key] = parseFloat(el.style.fontSize) || 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
el.classList.remove('score-pop');
|
el.classList.remove('score-pop');
|
||||||
@ -310,6 +342,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---- Trigger sweep animation ----
|
// ---- Trigger sweep animation ----
|
||||||
|
var sweepTimers = {};
|
||||||
function triggerSweep(side) {
|
function triggerSweep(side) {
|
||||||
var overlay = document.getElementById('sweep-' + side);
|
var overlay = document.getElementById('sweep-' + side);
|
||||||
if (!overlay) return;
|
if (!overlay) return;
|
||||||
@ -319,6 +352,12 @@
|
|||||||
void overlay.offsetHeight;
|
void overlay.offsetHeight;
|
||||||
overlay.style.animation = '';
|
overlay.style.animation = '';
|
||||||
overlay.classList.add('sweeping');
|
overlay.classList.add('sweeping');
|
||||||
|
|
||||||
|
if (sweepTimers[side]) clearTimeout(sweepTimers[side]);
|
||||||
|
sweepTimers[side] = setTimeout(function () {
|
||||||
|
overlay.classList.remove('sweeping');
|
||||||
|
sweepTimers[side] = null;
|
||||||
|
}, 1100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Reset single side ----
|
// ---- Reset single side ----
|
||||||
|
|||||||
@ -2,11 +2,12 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=1920, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<title>CuteBoard — Display {{ display_id }}</title>
|
<title>CuteBoard — Display {{ display_id }}</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/fonts.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/fonts.css') }}">
|
||||||
|
|
||||||
|
{% if not reduced_styles %}
|
||||||
<script src="{{ url_for('static', filename='js/tailwind.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/tailwind.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
tailwind.config = {
|
tailwind.config = {
|
||||||
@ -21,10 +22,11 @@
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body{% if reduced_styles %} class="reduced-styles"{% endif %}>
|
||||||
|
|
||||||
<div class="scoreboard flex flex-col h-screen w-screen" data-content-padding-top="{{ content_padding_top }}">
|
<div class="scoreboard flex flex-col h-screen w-screen" data-content-padding-top="{{ content_padding_top }}">
|
||||||
|
|
||||||
@ -67,10 +69,12 @@
|
|||||||
|
|
||||||
<div class="sweep-overlay" id="sweep-left">
|
<div class="sweep-overlay" id="sweep-left">
|
||||||
<div class="sweep-gradient"></div>
|
<div class="sweep-gradient"></div>
|
||||||
|
{% if not reduced_styles %}
|
||||||
<div class="wave-container">
|
<div class="wave-container">
|
||||||
<div class="wave wave-front"></div>
|
<div class="wave wave-front"></div>
|
||||||
<div class="wave wave-back"></div>
|
<div class="wave wave-back"></div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -105,10 +109,12 @@
|
|||||||
|
|
||||||
<div class="sweep-overlay" id="sweep-right">
|
<div class="sweep-overlay" id="sweep-right">
|
||||||
<div class="sweep-gradient"></div>
|
<div class="sweep-gradient"></div>
|
||||||
|
{% if not reduced_styles %}
|
||||||
<div class="wave-container">
|
<div class="wave-container">
|
||||||
<div class="wave wave-front"></div>
|
<div class="wave wave-front"></div>
|
||||||
<div class="wave wave-back"></div>
|
<div class="wave wave-back"></div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- /sides-row -->
|
</div> <!-- /sides-row -->
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user