From 70b06e51cecb050e7d48883f9d42cbe4e296dc98 Mon Sep 17 00:00:00 2001 From: ISenseAura Date: Mon, 3 Nov 2025 22:22:25 +0530 Subject: [PATCH 1/5] add getNotificationsCount method to PS class --- play.pokemonshowdown.com/src/client-main.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/play.pokemonshowdown.com/src/client-main.ts b/play.pokemonshowdown.com/src/client-main.ts index b2fadeb0f..8f4baeb3a 100644 --- a/play.pokemonshowdown.com/src/client-main.ts +++ b/play.pokemonshowdown.com/src/client-main.ts @@ -2744,6 +2744,15 @@ export const PS = new class extends PSModel { this.prefs.set('autojoin', autojoin); } } + getNotificationsCount() { + let count = 0; + const notificationRooms = [...PS.leftRoomList, ...PS.rightRoomList, ...PS.miniRoomList]; + for (const roomid of notificationRooms) { + const miniNotifications = PS.rooms[roomid]?.notifications; + if (miniNotifications?.length) count++; + } + return count; + } requestNotifications() { try { if (window.webkitNotifications?.requestPermission) { From 78bcc1f6bb9816bf387458b8e4ff5692bcd1aa52 Mon Sep 17 00:00:00 2001 From: ISenseAura Date: Mon, 3 Nov 2025 22:23:06 +0530 Subject: [PATCH 2/5] update dynamic favicon when notified --- play.pokemonshowdown.com/src/client-main.ts | 9 +++++++++ play.pokemonshowdown.com/src/panel-topbar.tsx | 15 ++++++++------- play.pokemonshowdown.com/src/panels.tsx | 5 +++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/play.pokemonshowdown.com/src/client-main.ts b/play.pokemonshowdown.com/src/client-main.ts index 8f4baeb3a..b56a60449 100644 --- a/play.pokemonshowdown.com/src/client-main.ts +++ b/play.pokemonshowdown.com/src/client-main.ts @@ -19,6 +19,7 @@ import { BattleTextParser, type Args } from './battle-text-parser'; import type { BattleRoom } from './panel-battle'; import { Teams } from './battle-teams'; import type preact from '../js/lib/preact'; +import { PSHeader } from './panel-topbar'; declare const BattleTextAFD: any; declare const BattleTextNotAFD: any; @@ -1041,6 +1042,11 @@ export class PSRoom extends PSStreamModel implements RoomOptions { } } } catch {} + // extra check because the roomIsFocused is not accurate for some reason + if (document.visibilityState !== 'visible') { + PS.isNotifying = true; + PSHeader.updateFavicon(); + } } if (options.noAutoDismiss && !options.id) { throw new Error(`Must specify id for manual dismissing`); @@ -1071,6 +1077,7 @@ export class PSRoom extends PSStreamModel implements RoomOptions { this.notifications[i].notification?.close(); } catch {} this.notifications.splice(i, 1); + PSHeader.updateFavicon(); } dismissNotification(id: string) { const index = this.notifications.findIndex(n => n.id === id); @@ -1903,6 +1910,8 @@ export const PS = new class extends PSModel { /** Tracks whether or not to display the "Use arrow keys" hint */ arrowKeysUsed = false; + isNotifying = false; + newsHTML = document.querySelector('#room-news .readable-bg')?.innerHTML || ''; libsLoaded = makeLoadTracker(); diff --git a/play.pokemonshowdown.com/src/panel-topbar.tsx b/play.pokemonshowdown.com/src/panel-topbar.tsx index 8334b65dc..83a13874e 100644 --- a/play.pokemonshowdown.com/src/panel-topbar.tsx +++ b/play.pokemonshowdown.com/src/panel-topbar.tsx @@ -152,6 +152,13 @@ export class PSHeader extends preact.Component { {closeButton} ; } + static updateFavicon() { + const favicon = document.querySelector('#dynamic-favicon'); + if (favicon instanceof HTMLLinkElement) { + favicon.href = `${Dex.resourcePrefix}/${PS.isNotifying ? 'favicon-notify.ico' : 'favicon.ico'}`; + favicon.dataset.on = PS.isNotifying ? '1' : ''; + } + } handleResize = () => { if (!this.base) return; @@ -307,13 +314,7 @@ export class PSMiniHeader extends preact.Component { }; override render() { if (PS.leftPanelWidth !== null) return null; - - let notificationsCount = 0; - const notificationRooms = [...PS.leftRoomList, ...PS.rightRoomList]; - for (const roomid of notificationRooms) { - const miniNotifications = PS.rooms[roomid]?.notifications; - if (miniNotifications?.length) notificationsCount++; - } + const notificationsCount = PS.getNotificationsCount(); const { icon, title } = PSHeader.roomInfo(PS.panel); const userColor = window.BattleLog && `color:${BattleLog.usernameColor(PS.user.userid)}`; const showMenuButton = PSView.narrowMode; diff --git a/play.pokemonshowdown.com/src/panels.tsx b/play.pokemonshowdown.com/src/panels.tsx index 233fc3e6e..00081c3a0 100644 --- a/play.pokemonshowdown.com/src/panels.tsx +++ b/play.pokemonshowdown.com/src/panels.tsx @@ -623,6 +623,11 @@ export class PSView extends preact.Component { PS.dragging = null; }); + window.addEventListener('focus', () => { + PS.isNotifying = false; + PSHeader.updateFavicon(); + }); + const colorSchemeQuery = window.matchMedia?.('(prefers-color-scheme: dark)'); if (colorSchemeQuery?.media !== 'not all') { colorSchemeQuery.addEventListener('change', cs => { From d8f9f7e22eefbdf5221738d36934c2f4fe9f2e44 Mon Sep 17 00:00:00 2001 From: ISenseAura Date: Mon, 3 Nov 2025 22:28:53 +0530 Subject: [PATCH 3/5] fix eslint --- play.pokemonshowdown.com/src/panel-topbar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/play.pokemonshowdown.com/src/panel-topbar.tsx b/play.pokemonshowdown.com/src/panel-topbar.tsx index 83a13874e..754a7ef3d 100644 --- a/play.pokemonshowdown.com/src/panel-topbar.tsx +++ b/play.pokemonshowdown.com/src/panel-topbar.tsx @@ -155,7 +155,7 @@ export class PSHeader extends preact.Component { static updateFavicon() { const favicon = document.querySelector('#dynamic-favicon'); if (favicon instanceof HTMLLinkElement) { - favicon.href = `${Dex.resourcePrefix}/${PS.isNotifying ? 'favicon-notify.ico' : 'favicon.ico'}`; + favicon.href = `${window.Dex.resourcePrefix}/${PS.isNotifying ? 'favicon-notify.ico' : 'favicon.ico'}`; favicon.dataset.on = PS.isNotifying ? '1' : ''; } } From 975d2e6d7c9b75ed6f709c5bfe4f96055b1586e8 Mon Sep 17 00:00:00 2001 From: ISenseAura Date: Tue, 4 Nov 2025 19:18:55 +0530 Subject: [PATCH 4/5] sneak in e-egg --- play.pokemonshowdown.com/src/client-main.ts | 6 ++ play.pokemonshowdown.com/src/panel-popups.tsx | 99 ++++++++++++++++++- play.pokemonshowdown.com/src/panel-topbar.tsx | 25 +++++ 3 files changed, 129 insertions(+), 1 deletion(-) diff --git a/play.pokemonshowdown.com/src/client-main.ts b/play.pokemonshowdown.com/src/client-main.ts index b56a60449..3f95b7c60 100644 --- a/play.pokemonshowdown.com/src/client-main.ts +++ b/play.pokemonshowdown.com/src/client-main.ts @@ -1506,6 +1506,12 @@ export class PSRoom extends PSStreamModel implements RoomOptions { PS.prefs.set('nounlink', false); this.add('||Locked/banned users\' chat messages: HIDDEN'); }, + 'whatsnew'() { + const success = PSHeader.clickCounter.click(); + if (success) { + PS.join('patchnotes' as RoomID); + } + }, 'hl,highlight'(target) { let highlights = PS.prefs.highlights || {}; if (target.includes(' ')) { diff --git a/play.pokemonshowdown.com/src/panel-popups.tsx b/play.pokemonshowdown.com/src/panel-popups.tsx index b6f171675..36752140e 100644 --- a/play.pokemonshowdown.com/src/panel-popups.tsx +++ b/play.pokemonshowdown.com/src/panel-popups.tsx @@ -1916,6 +1916,102 @@ class RulesPanel extends PSRoomPanel { } } +class PatchNotesPanel extends PSRoomPanel { + static readonly id = 'patchnotes'; + static readonly routes = ['patchnotes']; + static readonly location = 'semimodal-popup'; + static readonly noURL = true; + static readonly Model = PopupRoom; + + getRandomPatchNotes() { + const allTitles = [ + "Patch Notes (because chaos needs documentation)", + "Patch Notes: Now with 30% fewer bugs (No promises)", + "Patch Notes — We Swear It’s Better This Time", + "Patch Notes: Definitely Didn’t Break Anything Else", + "Patch Notes: Technically “Improvements”", + "Patch Notes — Version: “It Works on My Machine”", + "Patch Notes (We Fixed Things You Didn’t Know Were Broken)", + "Patch Notes: Stability? Never Heard of It.", + "Patch Notes — Slightly More Functional Than Yesterday", + "Patch Notes: The Bugs Strike Back", + "Patch Notes (A Love Letter to Debugging)", + "Patch Notes: Powered by Caffeine and Regret", + "Patch Notes — “Trust Me Bro” Edition", + ]; + const allNotes = [ + "Fixed a bug where clicking really hard didn’t make things load faster.", + "Improved stability by removing one line of code we didn’t understand.", + "Added a 'quantum uncertainty' mode — the UI may or may not respond.", + "Performance improved by at least 0.0001% (scientifically proven).", + "Added new AI: 'Emotionally Unstable Bot' — it forfeits if you’re mean.", + "Pokémon now occasionally question the meaning of their existence mid-battle.", + "Critical hits are now determined by your karma.", + "Fixed a bug where RNG was too fair.", + "Improved matchmaking: you’ll now face someone just as tilted as you.", + "Added experimental 'Therapist Mode': the game listens, you vent.", + "The app now looks 30% faster, even if it isn’t.", + "Chat filters now detect sarcasm with 4% accuracy.", + "Fixed a bug where players were sometimes right on the internet.", + "Improved mod tools: moderators can now sense chaos before it happens.", + "Backend now powered by pure friendship and caffeine.", + "Optimized the code to run slightly worse, but with more confidence.", + "Fixed an issue where the bug fix introduced more bugs.", + "Added 'Are you sure?' dialog. It doesn’t do anything, but feels safe.", + "Reduced load times by moving progress bar faster.", + "Replaced all semicolons with good vibes.", + "New setting: 'Developer Tears Mode' — enhances realism.", + "You can now press any key to feel like you’re helping.", + "Made UI 15% shinier for improved morale.", + "Bug reports are now automatically forwarded to the void.", + "Buffed RNG. It’s now personal.", + "Lucario’s aura now glows brighter when you lie.", + "Fixed crash where Trainer couldn’t accept defeat gracefully.", + "Buffed memes. Nerfed logic.", + "New mechanic: 'Disconnect Immunity' — doesn’t exist, but sounds nice.", + "Fixed an issue where players believed in fair matchups.", + "Nerfed stall strategies. (You’re welcome.)", + "Fixed an exploit where players had hope during battles.", + "Blissey now charges for therapy sessions.", + "Togekiss’s Air Slash now comes with a free rage quit.", + "Increased Confusion duration — the devs thought it was funny.", + "Garchomp’s Sand Veil now works indoors, somehow.", + "Rebalanced RNG: it now hates everyone equally.", + "Pikachu’s Thunderbolt now powered by real electricity bills.", + "Machamp now flexes every 3 turns automatically.", + "Buffed Magikarp’s Splash — it now causes mild emotional distress.", + "Nerfed Toxic stall teams. We’re doing this for humanity.", + "Alakazam now needs a PhD to understand its own Special Attack stat.", + "Zoroark’s Illusion now also fools itself sometimes.", + "Fixed a bug where Ditto copied your emotional damage too.", + "Gengar now giggles 20% more menacingly.", + "Nerfed RNG. It was getting too self-aware.", + "Charizard finally acknowledged that it’s not a Dragon-type. Therapy helped.", + ]; + + const title = allTitles.sort(() => 0.5 - Math.random())[0]; + const notes = allNotes.sort(() => 0.5 - Math.random()).slice(0, 12); + return { title, notes }; + } + + override render() { + const room = this.props.room; + const patchNotesData = this.getRandomPatchNotes(); + return +
+

{patchNotesData.title}

+ + {patchNotesData.notes + .map((note, i) =>

{i + 1}. {note}

)} +

+
+
; + } +} + PS.addRoomType( UserPanel, UserOptionsPanel, @@ -1935,5 +2031,6 @@ PS.addRoomType( RoomTabListPanel, BattleOptionsPanel, BattleTimerPanel, - RulesPanel + RulesPanel, + PatchNotesPanel ); diff --git a/play.pokemonshowdown.com/src/panel-topbar.tsx b/play.pokemonshowdown.com/src/panel-topbar.tsx index 754a7ef3d..e79f776ee 100644 --- a/play.pokemonshowdown.com/src/panel-topbar.tsx +++ b/play.pokemonshowdown.com/src/panel-topbar.tsx @@ -21,6 +21,29 @@ window.addEventListener('dragover', e => { }); export class PSHeader extends preact.Component { + static clickCounter = { + count: 0, + lastClick: 0, + maxGap: 500, + click() { + const now = Date.now(); + + if (now - this.lastClick > this.maxGap) { + this.count = 1; + } else { + this.count++; + } + + this.lastClick = now; + + if (this.count === 10) { + console.log('🎉 10 consecutive clicks detected!'); + this.count = 0; + return true; + } + return false; + }, + }; static toggleMute = (e: Event) => { PS.prefs.set('mute', !PS.prefs.mute); PS.update(); @@ -234,6 +257,7 @@ export class PSHeader extends preact.Component { src={`https://${Config.routes.client}/favicon-256.png`} alt="Pokémon Showdown! (beta)" width="50" height="50" + data-cmd="/whatsnew" />
    @@ -275,6 +299,7 @@ export class PSHeader extends preact.Component { src={`https://${Config.routes.client}/favicon-256.png`} alt="Pokémon Showdown! (beta)" width="48" height="48" + data-cmd="/whatsnew" /> {PSHeader.renderRoomTab(PS.leftRoomList[0])} From af7339b03155af17059b6fea2f505763f50cd927 Mon Sep 17 00:00:00 2001 From: ISenseAura Date: Mon, 10 Nov 2025 21:23:39 +0530 Subject: [PATCH 5/5] remove unnecessary console.log --- play.pokemonshowdown.com/src/panel-topbar.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/play.pokemonshowdown.com/src/panel-topbar.tsx b/play.pokemonshowdown.com/src/panel-topbar.tsx index e79f776ee..935857824 100644 --- a/play.pokemonshowdown.com/src/panel-topbar.tsx +++ b/play.pokemonshowdown.com/src/panel-topbar.tsx @@ -37,7 +37,6 @@ export class PSHeader extends preact.Component { this.lastClick = now; if (this.count === 10) { - console.log('🎉 10 consecutive clicks detected!'); this.count = 0; return true; }