From bdf6bac6028af901c82326f63636baf82ba7544b Mon Sep 17 00:00:00 2001 From: xRangerDE Date: Mon, 11 May 2026 05:29:04 +0200 Subject: [PATCH] feat: window title syncs with active tab / streamer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit document.title was stamped once during app boot with the static "Twitch VOD Manager vX.Y.Z" string. After that, the H1 page-title in the header updated as the user navigated tabs and selected streamers, but the OS-level window title — the string shown in the taskbar, Alt+Tab switcher, and OS notifications — never changed. Multitasking suffered: a user with three Electron windows pinned to taskbar all read identical "Twitch VOD Manager v4.6.x", with no clue which window had what tab or streamer loaded. Added a setPageTitle(text) helper in renderer.ts that: - Updates the H1 #pageTitle textContent (the visible header) - Updates document.title with `${text} - ${appName} v${version}` for non-default text, or just `${appName} v${version}` for the default app-name fallback - Exposed on window so the renderer-streamers.ts and renderer-settings.ts modules can reach it without crossing the module-vs-bundle boundary Three call sites updated to use the helper: - showTab → uses for tab-derived titles - selectStreamer → uses for "xrohat" style streamer titles - the renderer-settings language-switch refresh path Co-Authored-By: Claude Opus 4.7 (1M context) --- src/renderer-settings.ts | 9 +++++++-- src/renderer-streamers.ts | 4 +++- src/renderer.ts | 22 +++++++++++++++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/renderer-settings.ts b/src/renderer-settings.ts index 4f9677b..ac11970 100644 --- a/src/renderer-settings.ts +++ b/src/renderer-settings.ts @@ -194,9 +194,14 @@ function changeLanguage(lang: string): void { const activeTabId = document.querySelector('.tab-content.active')?.id || 'vodsTab'; const activeTab = activeTabId.replace('Tab', ''); if (activeTab === 'vods' && currentStreamer) { - byId('pageTitle').textContent = currentStreamer; + const setTitle = (window as unknown as { setPageTitle?: (text: string) => void }).setPageTitle; + if (typeof setTitle === 'function') setTitle(currentStreamer); + else byId('pageTitle').textContent = currentStreamer; } else { - byId('pageTitle').textContent = (UI_TEXT.tabs as Record)[activeTab] || UI_TEXT.appName; + const setTitle = (window as unknown as { setPageTitle?: (text: string) => void }).setPageTitle; + const text = (UI_TEXT.tabs as Record)[activeTab] || UI_TEXT.appName; + if (typeof setTitle === 'function') setTitle(text); + else byId('pageTitle').textContent = text; } void refreshRuntimeMetrics(); diff --git a/src/renderer-streamers.ts b/src/renderer-streamers.ts index 7d41cf7..9833a41 100644 --- a/src/renderer-streamers.ts +++ b/src/renderer-streamers.ts @@ -751,7 +751,9 @@ async function selectStreamer(name: string, forceRefresh = false): Promise const savedY = vodScrollPositions[name]; pendingScrollRestore = (typeof savedY === 'number' && savedY > 0) ? { streamer: name, y: savedY } : null; renderStreamers(); - byId('pageTitle').textContent = name; + const setTitle = (window as unknown as { setPageTitle?: (text: string) => void }).setPageTitle; + if (typeof setTitle === 'function') setTitle(name); + else byId('pageTitle').textContent = name; // Kick off the profile header load in parallel with VOD fetching. // It's a separate request stream and not strictly needed for the VOD diff --git a/src/renderer.ts b/src/renderer.ts index 1121e69..72203d1 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -20,6 +20,7 @@ async function init(): Promise { byId('versionText').textContent = `v${version}`; byId('versionInfo').textContent = `Version: v${version}`; + appVersion = version; document.title = `${UI_TEXT.appName} v${version}`; byId('clientId').value = config.client_id ?? ''; @@ -636,6 +637,24 @@ async function updateStatsBar(): Promise { let toastHideTimer: number | null = null; let queueSyncTimer: number | null = null; +let appVersion = ''; + +// Single source of truth for what the user is looking at — keeps the +// visible H1, the document title (which drives the OS task bar / Alt+Tab +// label), and the app version pill in sync. Previously document.title was +// stamped once at boot, so the OS task bar always read "Twitch VOD +// Manager v4.6.76" no matter what tab or streamer was active. +(window as unknown as { setPageTitle: (text: string) => void }).setPageTitle = setPageTitle; + +function setPageTitle(text: string): void { + const titleEl = document.getElementById('pageTitle'); + if (titleEl) titleEl.textContent = text; + const appName = UI_TEXT.appName; + const versionSuffix = appVersion ? ` v${appVersion}` : ''; + document.title = text && text !== appName + ? `${text} - ${appName}${versionSuffix}` + : `${appName}${versionSuffix}`; +} let queueSyncInFlight = false; let lastQueueActivityAt = Date.now(); @@ -850,9 +869,10 @@ function showTab(tab: string): void { // Only show the streamer name on the VODs tab — otherwise the title would // mismatch the tab content (e.g. "streamer X" while on Settings) - byId('pageTitle').textContent = (tab === 'vods' && currentStreamer) + const pageTitleText = (tab === 'vods' && currentStreamer) ? currentStreamer : (titles[tab] || UI_TEXT.appName); + setPageTitle(pageTitleText); persistActiveTab(tab);