From 29992fe40703cf567177d70f152811bfedb0caad Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:11:00 +0200 Subject: Rework the corner layout and move mode selection out of the button stack - Settings and Store move onto the wordmark's own line. Stacked beneath it they pushed into the space the passage needs and read as a second, unrelated column. - The live player count moves to the bottom-left beside a Multiplayer icon, as readable text ("5 racing") rather than a corner badge. As a badge it was a bare digit with nothing saying what it counted, sharing a corner with two other buttons. - Bottom-right is now just Stats and Leaderboard. - Mode selection becomes a row of chips below both start buttons. It has been three things before this: a chevron notched into the Single Player button, a caption wedged between the two buttons, and a pill sitting on top of them. As its own row it reads as a setting for the run rather than part of the button stack, and every common mode is one click instead of a dropdown away. The eleven topic packs stay behind a "Text" chip. - "Multiplayer" becomes "Multi-Player", so the two buttons read as a pair. --- web/src/screens/mainMenu.js | 72 +++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 28 deletions(-) (limited to 'web/src/screens') diff --git a/web/src/screens/mainMenu.js b/web/src/screens/mainMenu.js index 909740d..a5e9df1 100644 --- a/web/src/screens/mainMenu.js +++ b/web/src/screens/mainMenu.js @@ -7,6 +7,7 @@ import { attachTooltips } from '../tooltip.js'; import { wordListTiers } from '../wordGenerator.js'; import { renderTopRail } from '../topRail.js'; import { onCountsChange, getCounts } from '../counts.js'; +import { MULTIPLAYER_ICON } from './icons.js'; function label(m) { if (m === 'random') return 'Random'; @@ -78,6 +79,7 @@ export function renderMainMenu(root, props) { const gameModes = [...GAME_MODES.filter(m => modes.includes(m)), 'custom']; const textCategories = modes.filter(m => !gameModes.includes(m)); const modeItem = m => ``; + const modeChip = m => ``; const currentIndex = Math.max(0, modes.indexOf(selectedCategory || 'random')); const currentMode = modes[currentIndex] || 'random'; const isCustom = currentMode === 'custom'; @@ -113,28 +115,29 @@ export function renderMainMenu(root, props) { +
+ + +
+
${profile.testsCompleted > 0 ? `
avg ${profile.averageWpm}
@@ -216,13 +224,19 @@ export function renderMainMenu(root, props) { attachTooltips(root); const cleanupTheme = renderTopRail(root, { onShowAccount, onShowFriends }); - // Live count under the Multiplayer button. The Friends badge is the top - // rail's, painted there. - const mpCaptionEl = root.querySelector('.mp-caption'); + // The Friends badge belongs to the top rail. The player count is this + // screen's own, since the menu builds its corner clusters inline. + const onlineEl = root.querySelector('.corner-rail-bottom-left [data-badge="online"]'); + const mpIconBtn = root.querySelector('.corner-rail-bottom-left [data-action="mode-multiplayer"]'); function paintCounts({ online }) { - if (mpCaptionEl) { - mpCaptionEl.hidden = !online; - mpCaptionEl.textContent = online === 1 ? '1 racing now' : `${online} racing now`; + if (onlineEl) { + onlineEl.hidden = !online; + onlineEl.textContent = online === 1 ? '1 racing' : `${online} racing`; + } + if (mpIconBtn) { + mpIconBtn.dataset.tooltip = !online + ? 'Multiplayer' + : (online === 1 ? 'Multiplayer - 1 player racing now' : `Multiplayer - ${online} players racing now`); } } paintCounts(getCounts()); @@ -272,12 +286,14 @@ export function renderMainMenu(root, props) { root.querySelector('.sp-popover'), null, ); - root.querySelectorAll('.sp-popover .mode-popover-item[data-mode]').forEach(item => { + root.querySelectorAll('.sp-popover .mode-popover-item[data-mode], .mode-bar .mode-chip[data-mode]').forEach(item => { item.addEventListener('click', () => onSelectCategory(item.dataset.mode)); }); root.querySelector('[data-action="mode-lyrics"]').addEventListener('click', onShowLyrics); - root.querySelector('[data-action="mode-multiplayer"]').addEventListener('click', onShowMultiplayer); + // Two of these now: the big button and the bottom-left icon beside the + // live player count. + root.querySelectorAll('[data-action="mode-multiplayer"]').forEach(el => el.addEventListener('click', onShowMultiplayer)); const startBtn = root.querySelector('.sp-group .start-main'); const panel = root.querySelector('.custom-panel'); @@ -342,15 +358,15 @@ export function renderMainMenu(root, props) { // them change the selected mode, and a full re-render was resetting // .settings-panel back to hidden on every single click, forcing you to // reopen Settings after each change. - // The mode pill above the buttons carries the mode name and its rules. - const startSubEl = root.querySelector('.mode-pill-label'); + // The rules line under the mode bar, for the modes that have rules. + const startSubEl = root.querySelector('.sp-caption'); function refreshRulesSummary() { if (!startSubEl) return; const s = getSettings(); const parts = [isWords ? `${liveWordCount} words` : `${liveTimeDuration}s`]; if (s.wordsPunctuation) parts.push('punctuation'); if (s.wordsNumbers) parts.push('numbers'); - startSubEl.textContent = `${startLabel} · ${parts.join(' · ')}`; + startSubEl.textContent = parts.join(' · '); } let liveWordCount = wordCount; -- cgit v1.2.3