From b1034610916274493251032e262bc127075e5009 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:08 +0800 Subject: [PATCH 01/12] feat: add PWA icons --- icons/icon-72x72.png | Bin 0 -> 164 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/icon-72x72.png diff --git a/icons/icon-72x72.png b/icons/icon-72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..58fccc86c147717a6641b897e28da2e1b9552d47 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBiH}4^I!5APy!V3j}WUs|Wz8Y)==*kcv5P&pYxmDDWIO@N@2e z^AifH(?cBc&%K*|_A|4A!G@HCtp~CiuQIJ>4dY%T7NNT#iYx)W3s0nTEZt%rgRJ*- L^>bP0l+XkK^SL@R literal 0 HcmV?d00001 From 7f4fb0f7029860420f1731b980ba77e79ead373a Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:09 +0800 Subject: [PATCH 02/12] feat: add PWA icons --- icons/icon-96x96.png | Bin 0 -> 221 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/icon-96x96.png diff --git a/icons/icon-96x96.png b/icons/icon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..377f9c5294f824c220a3b9cbdb976fc82f283b7a GIT binary patch literal 221 zcmeAS@N?(olHy`uVBiJf1Wyl_APy!V3j}h1J;(=AOFUg1Ln`LHyDlRknxa#k%@&Pzyc`3$Rxr?d#M-c;{S@` U-yL4KL Date: Fri, 27 Mar 2026 12:31:10 +0800 Subject: [PATCH 03/12] feat: add PWA icons --- icons/icon-128x128.png | Bin 0 -> 304 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/icon-128x128.png diff --git a/icons/icon-128x128.png b/icons/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..77de64ddecd2fd656f66b1782bd26ffdabe27387 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBiJf22T%{APy!V3j};(zRdwr-#uL%Ln`LHy=chGV8C-=!zbPU z)y)@`r!gr^dj9s+221-6b_NG)28LUV3>Wwq8p;?LwlFg+kY!+a%fOH^bfxSxRo(9J Sr{4Mv3JOnGKbLh*2~7Y=Jxq`Q literal 0 HcmV?d00001 From 61b636beb009f3cd448f7549d9bc40d435284c04 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:11 +0800 Subject: [PATCH 04/12] feat: add PWA icons --- icons/icon-144x144.png | Bin 0 -> 414 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/icon-144x144.png diff --git a/icons/icon-144x144.png b/icons/icon-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..e18d43f380c2f3f8b86d2dd4d14c71a1faa8115c GIT binary patch literal 414 zcmeAS@N?(olHy`uVBiJf37#Gm0~=;v zSowF?WaA%`oD`TvtX}6hJ*#H)=jk@oIkrLKaMCD)oIrTS#JOkst=z)Hb3g&_>FVdQ I&MBb@05TV!S^xk5 literal 0 HcmV?d00001 From 204e0e9170237c64af18bfbf5ac3361510c794d3 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:12 +0800 Subject: [PATCH 05/12] feat: add PWA icons --- icons/icon-152x152.png | Bin 0 -> 430 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/icon-152x152.png diff --git a/icons/icon-152x152.png b/icons/icon-152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..9025ac976cae1a321011ebcfc595b63e4192dc43 GIT binary patch literal 430 zcmeAS@N?(olHy`uVBiJf8J-?4K^#m#76?pVu)U0dfw9!n#WAE}&fANQoD2p$2M+vN z`~Q1dk^u+1y21p@-7IImGtDzFI&^|VS-GcYltG<9=-~GGp76%$*w=lai12jvb6Mw< G&;$S?#+ttX literal 0 HcmV?d00001 From 61a258035ca3b52215721862f8736cfe5db85626 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:13 +0800 Subject: [PATCH 06/12] feat: add PWA icons --- icons/icon-192x192.png | Bin 0 -> 546 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/icon-192x192.png diff --git a/icons/icon-192x192.png b/icons/icon-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..d46f62da8de920c99a648ff61e1d9159e91326a5 GIT binary patch literal 546 zcmeAS@N?(olHy`uVBiJf1D+l(K^#m#76{zk_d9@rf$^oMi(^Q|oVS-Yaxy3|95B%T zk`-S6iQT$G-~^+r)w8&QTpRrd*=$ON0-cQ>JT6HJLd+8-I*vG~j0z2{kdP6wt3NVN Rl2sfO`JS$RF6*2UngE&ek&yrZ literal 0 HcmV?d00001 From 7224a1024700690614c2260c7078461297e52d52 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:14 +0800 Subject: [PATCH 07/12] feat: add PWA icons --- icons/icon-384x384.png | Bin 0 -> 1286 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/icon-384x384.png diff --git a/icons/icon-384x384.png b/icons/icon-384x384.png new file mode 100644 index 0000000000000000000000000000000000000000..a1f04125dbd2a3571e8ae91b4e3c1ee9e942f12e GIT binary patch literal 1286 zcmeAS@N?(olHy`uVBlq7U~KU8a0%jI0`fpWd#Tb31_qY1o-U3d6?5KRG2~@1;5o42 zlkWfO)``i}cof2)y=78S(`VS1>fms!p`nM7QIeU-h>azMhvS5Vzyw1Dh2&9*(I6R3 g45Qg#u#^lkRa0s&)&IMe0V?1;UHx3vIVCg!05mr`8UO$Q literal 0 HcmV?d00001 From 7fc78c2e64a2aa01278121a5e1c8c63755ac2707 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:15 +0800 Subject: [PATCH 08/12] feat: add PWA icons --- icons/icon-512x512.png | Bin 0 -> 1880 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/icon-512x512.png diff --git a/icons/icon-512x512.png b/icons/icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..feb54369259a838854521134c162886dba1e7481 GIT binary patch literal 1880 zcmeAS@N?(olHy`uVBlq7U}Es}a0%jI0`fqhTFQAX0|UFfr;B4q#hkZS4S5+1cn)k( zl6kfFv5ACj(n*F-spobxu$--8WYFeeP&mcFuxOMT4T8~BFq#oY%YxD3aAed7cB{`8 UxUzp{^aE9yp00i_>zopr074P4X8-^I literal 0 HcmV?d00001 From 90226a104e95340c61a508a90e2bc3ce3eeab090 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:38 +0800 Subject: [PATCH 09/12] feat: add sound toggle to landing page --- index.html | 419 +++++++++++++++++++++++++++-------------------------- 1 file changed, 213 insertions(+), 206 deletions(-) diff --git a/index.html b/index.html index 1edf5d8..e45e392 100644 --- a/index.html +++ b/index.html @@ -1,206 +1,213 @@ - - - - - - FunWebGames - Play & Learn! - - - - - - - - - - - - - - - - - -
- - -
🎨
-
🔵
-

Color Match

-
- - - -
🧩
-
🐱
-

Animal Puzzle

-
- - - -
🫧
-
🔢
-

Bubble Pop

-
- - - -
🔷
-
🏠
-

Shape Builder

-
- - - -
🌸
-
🦋
-

Counting Garden

-
- - - -
🔤
-
🅰️
-

Letter Explorer

-
- - - -
🎵
-
🎹
-

Music Maker

-
- - - -
🗺️
-
🐰
-

Maze Runner

-
- - - -
-
🌙
-

Star Catcher

-
- - - -
👗
-
🎀
-

Dress Up

-
-
- - -
- -
- - - - - + + + + + + FunWebGames - Play & Learn! + + + + + + + + + + + + + + + + + +
+ + +
🎨
+
🔵
+

Color Match

+
+ + + +
🧩
+
🐱
+

Animal Puzzle

+
+ + + +
🫧
+
🔢
+

Bubble Pop

+
+ + + +
🔷
+
🏠
+

Shape Builder

+
+ + + +
🌸
+
🦋
+

Counting Garden

+
+ + + +
🔤
+
🅰️
+

Letter Explorer

+
+ + + +
🎵
+
🎹
+

Music Maker

+
+ + + +
🗺️
+
🐰
+

Maze Runner

+
+ + + +
+
🌙
+

Star Catcher

+
+ + + +
👗
+
🎀
+

Dress Up

+
+
+ + +
+ +
+ + + + + + From da591fe083dc0b1e4020a2cf9187f2966cb02558 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 12:31:51 +0800 Subject: [PATCH 10/12] feat: support landing page header in sound toggle --- js/sound-toggle.js | 343 +++++++++++++++++++++++---------------------- 1 file changed, 172 insertions(+), 171 deletions(-) diff --git a/js/sound-toggle.js b/js/sound-toggle.js index fedcf72..db66429 100644 --- a/js/sound-toggle.js +++ b/js/sound-toggle.js @@ -1,171 +1,172 @@ -/** - * Sound Toggle System for FunWebGames - * Global mute/unmute control that persists across all games - * - * Usage: - * SoundToggle.init(); // Call once on page load - * SoundToggle.isMuted(); // Check if muted - * SoundToggle.toggle(); // Toggle mute state - * SoundToggle.setMuted(true); // Set mute state - * - * In game sound functions: - * if (SoundToggle.isMuted()) return; - * // ... play sound - */ - -const SoundToggle = (function() { - const STORAGE_KEY = 'funwebgames-sound-muted'; - let isMutedState = false; - let toggleButton = null; - - // Initialize sound toggle - function init() { - // Load saved preference - try { - isMutedState = localStorage.getItem(STORAGE_KEY) === 'true'; - } catch (e) { - isMutedState = false; - } - - // Check for user preference for reduced motion/sound - const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; - if (prefersReducedMotion) { - isMutedState = true; - } - - // Create toggle button - createToggleButton(); - - // Update all audio contexts - updateAllAudio(); - - return isMutedState; - } - - // Create toggle button in DOM - function createToggleButton() { - toggleButton = document.createElement('button'); - toggleButton.className = 'sound-toggle-btn'; - toggleButton.setAttribute('aria-label', isMutedState ? 'Unmute sound' : 'Mute sound'); - toggleButton.setAttribute('title', isMutedState ? 'Unmute' : 'Mute'); - toggleButton.innerHTML = isMutedState ? '🔇' : '🔊'; - - toggleButton.addEventListener('click', toggle); - - // Add to page (typically in header) - const header = document.querySelector('.game-header'); - if (header) { - const existingBtn = header.querySelector('.sound-toggle-btn'); - if (existingBtn) { - existingBtn.remove(); - } - // Insert after help button if exists, otherwise at end - const helpBtn = header.querySelector('.help-btn'); - if (helpBtn && helpBtn.nextSibling) { - header.insertBefore(toggleButton, helpBtn.nextSibling); - } else { - header.appendChild(toggleButton); - } - } - } - - // Toggle mute state - function toggle() { - isMutedState = !isMutedState; - savePreference(); - updateButton(); - updateAllAudio(); - - // Play feedback sound if unmuting - if (!isMutedState) { - playFeedbackSound(); - } - } - - // Set mute state - function setMuted(muted) { - isMutedState = muted; - savePreference(); - updateButton(); - updateAllAudio(); - } - - // Check if muted - function isMuted() { - return isMutedState; - } - - // Save preference to localStorage - function savePreference() { - try { - localStorage.setItem(STORAGE_KEY, isMutedState.toString()); - } catch (e) { - // localStorage not available, continue silently - } - } - - // Update button appearance - function updateButton() { - if (!toggleButton) return; - - toggleButton.innerHTML = isMutedState ? '🔇' : '🔊'; - toggleButton.setAttribute('aria-label', isMutedState ? 'Unmute sound' : 'Mute sound'); - toggleButton.setAttribute('title', isMutedState ? 'Unmute' : 'Mute'); - - if (isMutedState) { - toggleButton.classList.add('muted'); - } else { - toggleButton.classList.remove('muted'); - } - } - - // Update all audio contexts (suspend/resume) - function updateAllAudio() { - // This will be called by individual games to check mute state - // before playing sounds - dispatchChangeEvent(); - } - - // Dispatch custom event for games to listen to - function dispatchChangeEvent() { - const event = new CustomEvent('soundtoggle-change', { - detail: { muted: isMutedState } - }); - document.dispatchEvent(event); - } - - // Play feedback sound (always plays, even when muted) - function playFeedbackSound() { - try { - const audioContext = new (window.AudioContext || window.webkitAudioContext)(); - const oscillator = audioContext.createOscillator(); - const gainNode = audioContext.createGain(); - - oscillator.connect(gainNode); - gainNode.connect(audioContext.destination); - - oscillator.frequency.value = 800; - oscillator.type = 'sine'; - gainNode.gain.setValueAtTime(0.1, audioContext.currentTime); - gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1); - - oscillator.start(audioContext.currentTime); - oscillator.stop(audioContext.currentTime + 0.1); - } catch (e) { - // Audio not supported - } - } - - // Public API - return { - init, - toggle, - setMuted, - isMuted - }; -})(); - -// Export for use in other files -if (typeof module !== 'undefined' && module.exports) { - module.exports = SoundToggle; -} +/** + * Sound Toggle System for FunWebGames + * Global mute/unmute control that persists across all games + * + * Usage: + * SoundToggle.init(); // Call once on page load + * SoundToggle.isMuted(); // Check if muted + * SoundToggle.toggle(); // Toggle mute state + * SoundToggle.setMuted(true); // Set mute state + * + * In game sound functions: + * if (SoundToggle.isMuted()) return; + * // ... play sound + */ + +const SoundToggle = (function() { + const STORAGE_KEY = 'funwebgames-sound-muted'; + let isMutedState = false; + let toggleButton = null; + + // Initialize sound toggle + function init() { + // Load saved preference + try { + isMutedState = localStorage.getItem(STORAGE_KEY) === 'true'; + } catch (e) { + isMutedState = false; + } + + // Check for user preference for reduced motion/sound + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + if (prefersReducedMotion) { + isMutedState = true; + } + + // Create toggle button + createToggleButton(); + + // Update all audio contexts + updateAllAudio(); + + return isMutedState; + } + + // Create toggle button in DOM + function createToggleButton() { + toggleButton = document.createElement('button'); + toggleButton.className = 'sound-toggle-btn'; + toggleButton.setAttribute('aria-label', isMutedState ? 'Unmute sound' : 'Mute sound'); + toggleButton.setAttribute('title', isMutedState ? 'Unmute' : 'Mute'); + toggleButton.innerHTML = isMutedState ? '🔇' : '🔊'; + + toggleButton.addEventListener('click', toggle); + + // Add to page (typically in header) + // Check for game-header first (individual games), then page-header (landing page) + const header = document.querySelector('.game-header') || document.querySelector('.page-header'); + if (header) { + const existingBtn = header.querySelector('.sound-toggle-btn'); + if (existingBtn) { + existingBtn.remove(); + } + // Insert after help button if exists, otherwise at end + const helpBtn = header.querySelector('.help-btn'); + if (helpBtn && helpBtn.nextSibling) { + header.insertBefore(toggleButton, helpBtn.nextSibling); + } else { + header.appendChild(toggleButton); + } + } + } + + // Toggle mute state + function toggle() { + isMutedState = !isMutedState; + savePreference(); + updateButton(); + updateAllAudio(); + + // Play feedback sound if unmuting + if (!isMutedState) { + playFeedbackSound(); + } + } + + // Set mute state + function setMuted(muted) { + isMutedState = muted; + savePreference(); + updateButton(); + updateAllAudio(); + } + + // Check if muted + function isMuted() { + return isMutedState; + } + + // Save preference to localStorage + function savePreference() { + try { + localStorage.setItem(STORAGE_KEY, isMutedState.toString()); + } catch (e) { + // localStorage not available, continue silently + } + } + + // Update button appearance + function updateButton() { + if (!toggleButton) return; + + toggleButton.innerHTML = isMutedState ? '🔇' : '🔊'; + toggleButton.setAttribute('aria-label', isMutedState ? 'Unmute sound' : 'Mute sound'); + toggleButton.setAttribute('title', isMutedState ? 'Unmute' : 'Mute'); + + if (isMutedState) { + toggleButton.classList.add('muted'); + } else { + toggleButton.classList.remove('muted'); + } + } + + // Update all audio contexts (suspend/resume) + function updateAllAudio() { + // This will be called by individual games to check mute state + // before playing sounds + dispatchChangeEvent(); + } + + // Dispatch custom event for games to listen to + function dispatchChangeEvent() { + const event = new CustomEvent('soundtoggle-change', { + detail: { muted: isMutedState } + }); + document.dispatchEvent(event); + } + + // Play feedback sound (always plays, even when muted) + function playFeedbackSound() { + try { + const audioContext = new (window.AudioContext || window.webkitAudioContext)(); + const oscillator = audioContext.createOscillator(); + const gainNode = audioContext.createGain(); + + oscillator.connect(gainNode); + gainNode.connect(audioContext.destination); + + oscillator.frequency.value = 800; + oscillator.type = 'sine'; + gainNode.gain.setValueAtTime(0.1, audioContext.currentTime); + gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1); + + oscillator.start(audioContext.currentTime); + oscillator.stop(audioContext.currentTime + 0.1); + } catch (e) { + // Audio not supported + } + } + + // Public API + return { + init, + toggle, + setMuted, + isMuted + }; +})(); + +// Export for use in other files +if (typeof module !== 'undefined' && module.exports) { + module.exports = SoundToggle; +} From 8ca0ee20ac04ffe9e894752abe2d72f9387e5499 Mon Sep 17 00:00:00 2001 From: D2758695161 <13510221939@163.com> Date: Fri, 27 Mar 2026 20:28:48 +0800 Subject: [PATCH 11/12] feat: add help-modal.js to index.html (#21) Loads help-modal.js so HelpModal.show() works in games. Addresses issue TechGuyTest/FunWebGames#21 --- index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index e45e392..3759652 100644 --- a/index.html +++ b/index.html @@ -106,7 +106,8 @@

Dress Up

- + +