add battle pass claim all button and menu timer
Build and Release / build-and-release (push) Failing after 4m1s

This commit is contained in:
2026-04-04 08:47:55 -07:00
parent 021acf67a0
commit 0e75affe0d
4 changed files with 121 additions and 2 deletions
+6 -1
View File
@@ -679,5 +679,10 @@ export const HP_COUNTER_CSS = `
}
`;
// ── Battle Pass Claim All CSS ──
export const BP_CLAIM_ALL_CSS = `
#claimAllBtn.disabled { opacity: 0.4; pointer-events: none; }
`;
/** Pre-concatenated CSS for single-call injection (excludes HIDE_ADS_CSS which is separate) */
export const ALL_CLIENT_CSS = `${CLIENT_SETTINGS_CSS}\n${MATCHMAKER_SETTINGS_CSS}\n${TRANSLATOR_CSS}\n${ALT_MANAGER_CSS}\n${HP_COUNTER_CSS}`;
export const ALL_CLIENT_CSS = `${CLIENT_SETTINGS_CSS}\n${MATCHMAKER_SETTINGS_CSS}\n${TRANSLATOR_CSS}\n${ALT_MANAGER_CSS}\n${HP_COUNTER_CSS}\n${BP_CLAIM_ALL_CSS}`;
+2
View File
@@ -77,6 +77,7 @@ export interface AppConfig {
deathscreenAnimation: boolean;
hideMenuPopups: boolean;
cleanerMenu: boolean;
menuTimer: boolean;
doublePing: boolean;
cssTheme: string;
loadingTheme: string;
@@ -186,6 +187,7 @@ export const config = new Store<AppConfig>({
deathscreenAnimation: true,
hideMenuPopups: false,
cleanerMenu: false,
menuTimer: true,
doublePing: true,
cssTheme: 'disabled',
loadingTheme: 'disabled',
+62 -1
View File
@@ -4,7 +4,7 @@ import type { MatchmakerConfig } from './matchmaker';
import { initUserscripts, getInstances, setScriptEnabled } from './userscripts';
import type { UserscriptInstance } from './userscripts';
import { initTranslator, updateTranslatorConfig } from './translator';
import { setDeathAnimBlock, setCleanerMenu, escapeHtml } from './utils';
import { setDeathAnimBlock, setCleanerMenu, setMenuTimer, escapeHtml } from './utils';
import { initChat, setBetterChat, setChatHistorySize } from './chat';
import { initHPCounter, destroyHPCounter } from './competitive';
import { checkChangelog } from './changelog';
@@ -615,6 +615,13 @@ function buildGeneralSection(
onChange: (v) => { ui.cleanerMenu = v; saveUI(); setCleanerMenu(v); },
}));
body.appendChild(createToggleRow({
label: 'Menu Timer',
desc: 'Show the game/spectate timer on the menu screen',
checked: ui.menuTimer ?? true, instant: true,
onChange: (v) => { ui.menuTimer = v; saveUI(); setMenuTimer(v); },
}));
body.appendChild(createToggleRow({
label: 'Double Ping Display',
desc: 'Show the real ping value (Krunker displays half the actual latency)',
@@ -656,6 +663,7 @@ function buildGeneralSection(
}));
if (ui.deathscreenAnimation) setDeathAnimBlock(true);
if (ui.menuTimer ?? true) setMenuTimer(true);
if (ui.hideMenuPopups) startHidePopups();
body.appendChild(createKeybindRow('Toggle Fullscreen', 'Fullscreen the game window (default F11)', bag.binds.fullscreenToggle, (b) => {
@@ -1635,6 +1643,7 @@ ipcRenderer.on('main_did-finish-load', () => {
if (uiConf?.deathscreenAnimation) setDeathAnimBlock(true);
if (uiConf?.hideMenuPopups) startHidePopups();
if (uiConf?.cleanerMenu) setCleanerMenu(true);
if (uiConf?.menuTimer ?? true) setMenuTimer(true);
// ── Double ping display ──
if (isGamePage && (uiConf?.doublePing ?? true)) {
@@ -1689,6 +1698,58 @@ ipcRenderer.on('main_did-finish-load', () => {
checkChangelog(currentVersion, uiConf?.lastSeenVersion || '');
}
// ── Battle Pass Claim All (game page only) ──
// Poll for .bpBotH element — injects button when BP window is visible
if (isGamePage) {
const getClaimable = () => Array.from(document.querySelectorAll('.bpClaimB')).filter(
(el: any) => el.offsetParent !== null && el.textContent?.trim() === 'Claim'
);
setInterval(() => {
const bar = document.querySelector('.bpBotH') as HTMLElement | null;
if (!bar || bar.offsetParent === null) return;
const existing = document.getElementById('claimAllBtn');
if (existing) {
// Update state on re-check (rewards may have become claimable)
const claimable = getClaimable();
if (claimable.length > 0) {
existing.textContent = 'Claim All';
existing.classList.remove('disabled');
} else {
existing.textContent = 'Nothing to Claim';
existing.classList.add('disabled');
}
return;
}
const claimable = getClaimable();
const btn = document.createElement('div');
btn.className = 'bpBtn skip';
btn.id = 'claimAllBtn';
btn.style.cssText = 'margin-left: 8px; cursor: pointer; background: #4CAF50;';
if (claimable.length > 0) {
btn.textContent = 'Claim All';
} else {
btn.textContent = 'Nothing to Claim';
btn.classList.add('disabled');
}
btn.addEventListener('click', async () => {
if (btn.classList.contains('disabled')) return;
(window as any).playSelect?.(0.1);
const items = getClaimable();
if (items.length === 0) return;
btn.textContent = 'Claiming...';
btn.classList.add('disabled');
for (const item of items) {
(item as HTMLElement).click();
await new Promise(r => setTimeout(r, 200));
}
const remaining = getClaimable();
btn.textContent = remaining.length > 0 ? 'Claim All' : 'Nothing to Claim';
btn.classList.toggle('disabled', remaining.length === 0);
});
bar.appendChild(btn);
}, 500);
}
// ── Initialize userscripts ──
const usEnabled = usConf ? usConf.enabled : true;
if (usEnabled) {
+51
View File
@@ -101,6 +101,57 @@ const CLEANER_MENU_CSS = `
.headerBarR { right: -23px !important; }
`;
// ── Menu Timer ──
// Shows the native spectate/game timer prominently on the menu screen.
// CSS approach from crankshaft/glorp.
const MENU_TIMER_ID = 'kpc-menuTimer';
const MENU_TIMER_CSS = `
#uiBase.onMenu #spectateUI { display: block !important; }
#uiBase.onCompMenu.onMenu #specTimer,
#uiBase.onMenu #specGMessage,
#uiBase.onMenu #spec1,
#uiBase.onMenu #specGameInfo,
#uiBase.onMenu #spec0,
#uiBase.onMenu #specControlHolder,
#uiBase.onMenu #specNames { display: none !important; }
#uiBase.onMenu #spectateHUD {
box-sizing: border-box; display: flex !important; justify-content: center;
height: 0.5rem; white-space: nowrap; width: max-content;
position: fixed; top: calc(50% + 140px);
}
#uiBase.onMenu #spectateHUD #specGMessage { top: 0; }
#uiBase.onMenu #spectateUI > #spectateHUD { z-index: 1; transform: unset; }
#uiBase.onMenu .spectateInfo {
position: fixed; top: calc(50% + 80px); left: 50%; transform: translate(-50%, -50%);
}
#uiBase.onMenu #spectateUI div .spectateInfo #specTimer {
background-color: transparent; padding: 25px; font-size: 42px; border-radius: 0.5em;
}
#uiBase.onMenu #specKPDContr { display: none; }
#uiBase.onMenu #spectateUI div#specStats {
position: absolute; top: calc(50% + 13em); left: 50%; transform: translateX(-50%); z-index: 1;
}
#uiBase.onMenu #spectateUI div#specStats:before {
content: "Spectating"; position: absolute; bottom: 100%; left: 50%;
transform: translateX(-50%); font-size: 1.2em; padding-bottom: 0.5em;
}
`;
export function setMenuTimer(enabled: boolean): void {
let el = document.getElementById(MENU_TIMER_ID);
if (enabled) {
if (!el) {
el = document.createElement('style');
el.id = MENU_TIMER_ID;
el.textContent = MENU_TIMER_CSS;
document.head.appendChild(el);
}
} else if (el) {
el.remove();
}
}
export function setCleanerMenu(enabled: boolean): void {
let el = document.getElementById(CLEANER_MENU_ID);
if (enabled) {