docs(launcher): add Call to Play specification and design reference

Extend the launcher design specification and prototype reference with the
"Call to Play" multiplayer coordination feature and clean up top-bar chrome.

Key additions and changes:
- Spec & Roadmap: Document Call to Play mechanics in SPEC.md, including Play
  Now / Scheduled call flavors, 15-minute check-in windows, top-bar button
  with active count badge, quick-bar ticker stack above grid, overlay modal,
  and per-call group chat. Update design/README.md.
- Components & Logic: Add calltoplay.jsx and ctp-chat.jsx components for call
  creation, status progression, and chat. Extend data.jsx with a mock peer
  roster (PEERS) and helper functions for peer install count tracking.
- Top-bar Cleanup: Move game-folder configuration from top bar into Settings ->
  Library, freeing top-bar space for the Call to Play action button.
- Styling & Layout: Add CTP quick-bar, ticker, badge, card, and chat styles to
  styles.css, and integrate CTP components into launcher.jsx, components.jsx,
  and SoftLAN Launcher.html.

Test Plan:
- `git diff --cached --check` -- passed (no trailing whitespace or conflict markers)
- `cargo check --workspace` -- passed cleanly
- Manual review of staged diff across 9 design/launcher files -- confirmed clean staging
This commit is contained in:
2026-07-21 20:00:07 +02:00
parent 8d54e6e954
commit 8f151e38b4
9 changed files with 1831 additions and 10 deletions
+30
View File
@@ -203,7 +203,37 @@ const STORAGE = {
total: 512,
};
// ────────────────────────────────────────────────────────────────────
// LAN roster (mock) — used by the peer-install indicator and by the
// Call to Play feature to simulate other people in the room.
// ────────────────────────────────────────────────────────────────────
const PEERS = [
'shadowfox', 'pixelqueen', 'kbmaster', 'nullptr', 'wavedash',
'crit_happens', 'afk_andy', 'lowping_larry', 'gg_val', 'ctrlaltdave',
];
const hashStr = (s) => [...String(s)].reduce((a, c) => a + c.charCodeAt(0), 0);
// Deterministic-but-varied subset of PEERS who "have this game installed" —
// stable per game id so the same game always shows the same roster/count.
function installedPeersFor(game) {
const h = hashStr(game.id);
const count = 1 + (h % PEERS.length); // 1..PEERS.length
const scored = PEERS.map((p, i) => ({ p, s: (h * 7 + i * 13 + hashStr(p)) % 97 }));
scored.sort((a, b) => a.s - b.s);
return scored.slice(0, count).map(x => x.p);
}
// "216" / "1-8" → 16 / 8. Falls back to 8 if unparsable.
function parseMaxPlayers(playersStr) {
const m = String(playersStr).match(/(\d+)\s*$/);
return m ? parseInt(m[1], 10) : 8;
}
window.GAMES = GAMES;
window.PEERS = PEERS;
window.installedPeersFor = installedPeersFor;
window.parseMaxPlayers = parseMaxPlayers;
window.STATE_META = STATE_META;
window.ACTION_FOR_STATE = ACTION_FOR_STATE;
window.countByFilter = countByFilter;