Disco DeDisco 668105aeeb my-sea voice: persist mute across in-sea nav/refresh + 3-min muted auto-disconnect; fix first-connect glow/mute race — TDD
MUTE PERSISTENCE (user-spec 2026-05-30) — a voice mute used to vanish on any
in-sea navigation/refresh (the mesh tears down + auto-rejoins unmuted). Now the
mute is stamped server-side + re-applied on rejoin, with a 3-min muted →
auto-disconnect window:
- `User.voice_muted_at` (timestamp, not a bare bool, so the 3-min window anchors
  here) + migration. Per-user, not per-seat: the owner has no seat row, and a
  user is in ≤1 voice room at a time, so this uniformly covers owner + visitor.
- POST `/voice/mute` {muted} sets/clears it (new voice app views.py + urls.py,
  mounted at `voice/` in core/urls). my_sea + my_sea_visit pass the timestamp to
  `#id_voice_btn` as `data-voice-muted-at`.
- voice-mesh.js gains `setMuted(m)` (set vs. toggleMute's flip), honoured by
  join's post-getUserMedia `_applyMute`. burger-btn.js: a mute toggle POSTs the
  state + arms a client timer; the auto-rejoin re-applies the persisted mute +
  re-arms the timer from the stored timestamp (so the 3-min spans navigations,
  not resets); an elapsed window on rejoin auto-disconnects instead of rejoining;
  a fresh manual join clears any stale mute. On timeout: leave voice + clear.

FIRST-CONNECT GLOW/MUTE RACE (user-reported) — `setOnStateChange` pushes the
current state immediately on subscribe, and voice-glow.js often subscribes
MID-JOIN (getUserMedia pending → inCall=false). Its `setVoiceState` only ever
DELETED `voice.dataset.inCall` (never re-set it) — wiping the join-vs-mute flag
burger-btn.js had just set, so the next click re-joined instead of muting (which
also dropped the peer + killed the equalizer). Two fixes:
- voice-glow keeps `dataset.inCall` SYMMETRIC (set on true, delete on false), so
  the mid-join false is restored once the stream resolves → mute works on first
  connect.
- voice-glow subscribes reliably on AUTO-REJOIN too (no click to trigger its
  poll): voice-mesh.js dispatches `voiceroom:ready` on singleton creation +
  voice-glow listens, so the glow is mesh-driven (peer-count equalizer) after a
  refresh, not just the in-call-class fallback.

Coverage:
- ITs: VoiceMuteViewTest (login/405/invalid-json guards, stamp on true, clear on
  false, re-mute restamps, missing-key=false). voice+lyric 164 green.
- Jasmine: BurgerSpec mute persistence (muteRemainingMs window, rejoin re-mute,
  expired-window auto-disconnect, toggle-persists + 3-min fires, manual-join
  clears); VoiceGlowSpec dataset.inCall sync (sets on in-call, clears on not,
  restores after a mid-join false→true). All green.
- Live multi-party voice (mic/2-device) left to manual verification.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 01:41:30 -04:00
feat: import Minchiate Fiorentine 1860-1890 deck assets (98 PNGs, alpha-channel transparent bg) + naming convention v2 + pngquant optimization tooling. Public-domain 1860-1890 lithograph scans sourced from Wikimedia (single download series + trump 11 Il Gobbo individually-sourced from same era series); user removed white backgrounds in Photoshop to leave irregular card-shape with transparent canvas. Filenames v2-conformant per [[reference-card-image-naming-convention]] (revised from v1 of 2026-05-24): deck slug carries -1860-1890 publication-year suffix so future Minchiate Fiorentine variants from different publishers/eras coexist cleanly; courts use rank-number-prefix (batons-11-page not batons-page) for linear sort key; trumps carry both numeric rank AND italian-name suffix (trumps-01-papa-uno, trumps-11-il-gobbo) for forensic identification across variant decks the user plans to sell. Il Matto (unnumbered Fool in Minchiate tradition) assigned rank 00 to give it a sortable position. Five trump filenames had elided-apostrophe slugs restored from the download source (-lacqua-l-acqua etc.). Card-back at <deck-slug>-back.png sorts alphabetically before all suit categories — no separate card-back/ subdir needed. pngquant 2.17.0 installed at C:\Users\adamc\AppData\Local\Programs\pngquant\, added to user PATH (effective next session) for future deck imports; ran with --quality=65-85 --speed=1 --strip --skip-if-larger for 57.6% size reduction (86.6 MB → 36.7 MB total, 935 KB → 383 KB avg). Second pass at --quality=40-65 hit pngquant's floor (only 0.5% further reduction — re-quantizing an already-quantized image has little headroom). Il Gobbo from Wikimedia was a dimensional outlier (1426x2366 vs siblings ~620x1024) — resized via System.Drawing HighQualityBicubic to 620x1029 before optimization. Format32bppArgb alpha channel verified intact across samples after optimization pass. Visually validated by user: cards must fill entire screen before any pixelization visible. Sprint A precursor — DeckVariant.has_card_images toggle + image-rendering template branch per [[project-image-based-deck-face-rendering]] follows in subsequent commits, will consume these assets in 6 surfaces (my_sign, my_sea, both billboard applets, room, game_kit). Asset set also unblocks downstream Sprint C+B [[project-deck-segment-model]] (admin form will require image upload + enforce naming convention) and Sprint D [[project-card-deck-icon]] (uses -back.png as the deck-stack icon's repeating card-face). Future: when Sprint B's admin form ships, wire pngquant into an optimize_card_images management command so admin uploads auto-optimize on save. Gitignore line src/apps/epic/static/apps/epic/images/cards-faces/minchiate-fiorentine/ dropped — v1 staging dir deleted (was only ever the rename-staging set; superseded by v2-named optimized set). Total disk delta: +36.7 MB binary content. No code changes — pure asset + convention import
2026-05-24 22:39:21 -04:00
2026-01-13 20:58:25 -05:00
Description
No description provided
155 MiB
Languages
Python 45%
JavaScript 37.6%
HTML 9%
SCSS 8.2%
Jinja 0.1%