wired PICK SKY server-side polarity countdown via threading.Timer (tasks.py); fixed polarity_done overlay gating on refresh; cleared sig-select floats on overlay dismiss; filtered Redact events from Most Recent applet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-04-13 00:34:05 -04:00
parent df421fb6c0
commit 32d8d97360
22 changed files with 1028 additions and 88 deletions

View File

@@ -83,13 +83,15 @@
#id_wallet_applet_menu { @extend %applet-menu; }
#id_room_menu { @extend %applet-menu; }
#id_billboard_applet_menu { @extend %applet-menu; }
#id_billscroll_menu { @extend %applet-menu; }
// Page-level gear buttons — fixed to viewport bottom-right
.gameboard-page,
.dashboard-page,
.wallet-page,
.room-page,
.billboard-page {
.billboard-page,
.billscroll-page {
> .gear-btn {
position: fixed;
bottom: 4.2rem;
@@ -102,7 +104,8 @@
#id_game_applet_menu,
#id_game_kit_menu,
#id_wallet_applet_menu,
#id_billboard_applet_menu {
#id_billboard_applet_menu,
#id_billscroll_menu {
position: fixed;
bottom: 6.6rem;
right: 1rem;
@@ -118,7 +121,8 @@
.dashboard-page,
.wallet-page,
.room-page,
.billboard-page {
.billboard-page,
.billscroll-page {
> .gear-btn {
right: 1rem;
bottom: 3.95rem; // same gap above kit btn as portrait; no page-specific overrides needed
@@ -131,7 +135,8 @@
#id_game_kit_menu,
#id_wallet_applet_menu,
#id_room_menu,
#id_billboard_applet_menu {
#id_billboard_applet_menu,
#id_billscroll_menu {
right: 1rem;
bottom: 6.6rem;
top: auto;
@@ -144,7 +149,8 @@
.dashboard-page,
.wallet-page,
.room-page,
.billboard-page {
.billboard-page,
.billscroll-page {
> .gear-btn { right: 2.5rem; }
}
@@ -153,7 +159,8 @@
#id_game_kit_menu,
#id_wallet_applet_menu,
#id_room_menu,
#id_billboard_applet_menu { right: 2.5rem; }
#id_billboard_applet_menu,
#id_billscroll_menu { right: 2.5rem; }
}
// ── Applet box visual shell (reusable outside the grid) ────

View File

@@ -139,6 +139,11 @@ body.page-billscroll {
.drama-event-body {
flex: 0 0 80%;
&.struck {
text-decoration: line-through;
opacity: 0.5;
}
}
.drama-event-time {

View File

@@ -546,10 +546,28 @@ html:has(.gate-backdrop) .position-strip .gate-slot button { pointer-events: aut
.table-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
// "Gravity settling . . ." / "Levity appraising . . ." shown after a polarity
// group confirms their sigs while the other group is still selecting.
// Pulsing opacity signals active waiting without being jarring.
#id_hex_waiting_msg {
font-size: 0.7rem;
letter-spacing: 0.06em;
color: rgba(var(--terUser), 0.8);
text-align: center;
margin: 0.4rem 0 0;
animation: hex-wait-pulse 2.4s ease-in-out infinite;
}
@keyframes hex-wait-pulse {
0%, 100% { opacity: 0.75; }
50% { opacity: 0.3; }
}
.table-seat {
position: absolute;
display: grid;

View File

@@ -8,6 +8,8 @@ describe("SigSelect", () => {
data-polarity="${polarity}"
data-user-role="${userRole}"
data-reserve-url="/epic/room/test/sig-reserve"
data-ready-url="/epic/room/test/sig-ready"
data-reservations="${reservations.replace(/"/g, '&quot;')}">
<div class="sig-modal">
<div class="sig-stage">
@@ -605,4 +607,79 @@ describe("SigSelect", () => {
expect(testDiv.querySelector(".fan-card-correspondence").textContent).toBe("");
});
});
// ── WAIT NVM glow pulse ────────────────────────────────────────────────────── //
//
// After clicking TAKE SIG (POST ok → isReady=true) a setInterval pulses the
// button at 600ms: odd ticks add .btn-cancel + a --terOr outer box-shadow;
// even ticks remove both. Uses jasmine.clock() to advance the fake timer.
describe("WAIT NVM glow pulse", () => {
let takeSigBtn;
beforeEach(() => {
jasmine.clock().install();
// Pre-reserve card 42 as PC so _showTakeSigBtn() fires during init
makeFixture({ reservations: '{"42":"PC"}' });
takeSigBtn = document.getElementById("id_take_sig_btn");
});
afterEach(() => {
jasmine.clock().uninstall();
});
async function clickTakeSig() {
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
// Flush the fetch .then() so _startWaitNoGlow() is called
await Promise.resolve();
}
it("adds .btn-cancel after the first pulse tick (600 ms)", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(true);
});
it("sets a non-empty box-shadow after the first pulse tick", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
expect(takeSigBtn.style.boxShadow).not.toBe("");
});
it("removes .btn-cancel on the second tick (even / trough)", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // peak
jasmine.clock().tick(600); // trough
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
});
it("clears box-shadow on the trough tick", async () => {
await clickTakeSig();
jasmine.clock().tick(601);
jasmine.clock().tick(600);
expect(takeSigBtn.style.boxShadow).toBe("");
});
it("stops glow and removes .btn-cancel when WAIT NVM is clicked (unready)", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // glow is on
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(true);
// Click again → WAIT NVM → fetch unready → _stopWaitNoGlow()
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve();
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
expect(takeSigBtn.style.boxShadow).toBe("");
});
it("glow does not advance after being stopped", async () => {
await clickTakeSig();
jasmine.clock().tick(601); // peak
takeSigBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await Promise.resolve(); // stop
jasmine.clock().tick(600); // would be another tick if running
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
});
});
});