word-waltz / commit
waiting notifier
.vscode/settings.json | 9 ++++++++- src/main.ts | 29 ++++++++++++++++++++--------- src/styles/main.css | 22 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9e26dfe..9a49189 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "chat.tools.terminal.autoApprove": { + "/^npx tsc --noEmit 2>&1 && echo \"TS OK\"$/": { + "approve": true, + "matchCommandLine": true + } + } +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 88d1055..90d6af8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,6 +18,23 @@ import { mountShareDialog } from './components/ShareDialog.js'; import { notifyMyTurn, clearNotification } from './utils/notifications.js'; import type { GameState, PlayerId, Round } from './types.js'; +// --------------------------------------------------------------------------- +// Waiting status helper +// --------------------------------------------------------------------------- + +function createWaitingEl(text: string): HTMLElement { + const wrap = document.createElement('div'); + wrap.className = 'status status--waiting'; + const dot = document.createElement('span'); + dot.className = 'status__live-dot'; + dot.setAttribute('aria-hidden', 'true'); + const msg = document.createElement('span'); + msg.textContent = text; + wrap.appendChild(dot); + wrap.appendChild(msg); + return wrap; +} + // --------------------------------------------------------------------------- // Player identity — stored in localStorage, keyed to the gameId. // @@ -123,10 +140,7 @@ function render( const shareEl = document.createElement('div'); mainEl.appendChild(shareEl); cleanupMain = mountShareDialog(shareEl, { shareUrl }); - const waiting = document.createElement('p'); - waiting.className = 'status'; - waiting.textContent = 'Share the link above with your opponent, then wait for them to join…'; - mainEl.appendChild(waiting); + mainEl.appendChild(createWaitingEl('Share the link above with your opponent, then wait for them to join…')); } else if (state.turn === 'pick') { const challenger = nextChallenger(state); @@ -150,7 +164,7 @@ function render( }, }); } else { - mainEl.innerHTML = '<p class="status">Waiting for your opponent to pick a word…</p>'; + mainEl.appendChild(createWaitingEl('Waiting for your opponent to pick a word…')); } } else { // turn === 'solve' @@ -188,10 +202,7 @@ function render( }); } else { // Challenger waits while their opponent solves - const waiting = document.createElement('p'); - waiting.className = 'status'; - waiting.textContent = 'Waiting for your opponent to solve the word…'; - mainEl.appendChild(waiting); + mainEl.appendChild(createWaitingEl('Waiting for your opponent to solve the word…')); } } diff --git a/src/styles/main.css b/src/styles/main.css index f9e5a66..d39fed4 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -132,6 +132,28 @@ header { padding: 12px 0; } +.status--waiting { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.status__live-dot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--color-correct); + flex-shrink: 0; + animation: live-pulse 1.8s ease-in-out infinite; +} + +@keyframes live-pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.3; transform: scale(0.6); } +} + .error { color: var(--color-error); text-align: center;