Files
Krunker-Civilian-Client-Test/src/preload/utils.ts
T
bigjakk 87ddf1499d Initial commit — Krunker Civilian Client
Cross-platform Krunker.io game client forked from Krunker Police Client
with all KPD/moderator features stripped: no KPD auth, OBS recording,
evidence uploads, yt-dlp, bytenode, or code obfuscation.

Retained: unlimited FPS (custom Electron 42), ad blocking, resource
swapper, matchmaker, userscripts, chat translator, Discord RPC, alt
account manager, configurable keybinds, and advanced Chromium flags.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 06:38:15 -08:00

76 lines
2.3 KiB
TypeScript

// ── Shared preload utilities ──
// Common types, helpers, and constants used across preload modules.
// ── Shared interfaces ──
export interface SavedConsole {
log: (...args: unknown[]) => void;
warn: (...args: unknown[]) => void;
error: (...args: unknown[]) => void;
}
export interface KeybindDef {
key: string;
ctrl: boolean;
shift: boolean;
alt: boolean;
}
// ── HTML escaping ──
const HTML_ESCAPE_MAP: Record<string, string> = {
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
};
export function escapeHtml(s: string): string {
return s.replace(/[&<>"']/g, c => HTML_ESCAPE_MAP[c]);
}
// ── Chat message injection ──
// Creates messages in #chatHolder inside a persistent #kpcMessageHolder div.
// timeout=0 means the message is persistent (not auto-removed).
export function genChatMsg(text: string, timeout = 2.25): HTMLElement | null {
const chatHolder = document.getElementById('chatHolder');
if (!chatHolder) return null;
if (!document.getElementById('kpcMessageHolder')) {
chatHolder.insertAdjacentHTML('afterbegin', '<div id="kpcMessageHolder"></div>');
}
const holder = document.getElementById('kpcMessageHolder')!;
holder.insertAdjacentHTML('beforeend',
'<div class="chatHolder_kpc"><div class="chatItem_kpc"><span class="chatMsg_kpc">' +
escapeHtml(text) + '</span></div></div>');
const elem = holder.lastElementChild as HTMLElement;
if (timeout !== 0) {
setTimeout(() => { elem.remove(); }, timeout * 1000);
}
return elem;
}
// ── Filename sanitisation ──
export function sanitizeFilename(name: string): string {
return name.replace(/[^a-zA-Z0-9_-]/g, '_');
}
// ── Shared CSS constants ──
export const DEATH_ANIM_BLOCK_ID = 'kpc-animationBlock';
export const DEATH_ANIM_BLOCK_CSS =
'.death-ui-bottom, .death-ui-bottom-empty { animation: none !important; transition: none !important; }';
/** Inject or remove the death screen animation block style element. */
export function setDeathAnimBlock(enabled: boolean): void {
let el = document.getElementById(DEATH_ANIM_BLOCK_ID);
if (enabled) {
if (!el) {
el = document.createElement('style');
el.id = DEATH_ANIM_BLOCK_ID;
el.textContent = DEATH_ANIM_BLOCK_CSS;
document.head.appendChild(el);
}
} else if (el) {
el.remove();
}
}