feat: auto-pause chat on scroll, resume at bottom
This commit is contained in:
+214
-216
@@ -1,216 +1,214 @@
|
||||
import Store from 'electron-store';
|
||||
|
||||
export interface Keybind {
|
||||
key: string;
|
||||
ctrl: boolean;
|
||||
shift: boolean;
|
||||
alt: boolean;
|
||||
}
|
||||
|
||||
export interface SavedAccount {
|
||||
label: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface AppConfig {
|
||||
window: {
|
||||
width: number;
|
||||
height: number;
|
||||
x: number | undefined;
|
||||
y: number | undefined;
|
||||
maximized: boolean;
|
||||
fullscreen: boolean;
|
||||
};
|
||||
performance: {
|
||||
fpsUnlocked: boolean;
|
||||
hardwareAccel: boolean;
|
||||
gpuPreference: 'high-performance' | 'low-power' | 'default';
|
||||
cpuThrottleGame: number;
|
||||
cpuThrottleMenu: number;
|
||||
processPriority: string;
|
||||
};
|
||||
game: {
|
||||
lastServer: string;
|
||||
socialTabBehaviour: 'New Window' | 'Same Window';
|
||||
joinAsSpectator: boolean;
|
||||
rawInput: boolean;
|
||||
betterChat: boolean;
|
||||
chatHistorySize: number;
|
||||
showPing: boolean;
|
||||
hpEnemyCounter: boolean;
|
||||
};
|
||||
swapper: {
|
||||
enabled: boolean;
|
||||
path: string;
|
||||
};
|
||||
matchmaker: {
|
||||
enabled: boolean;
|
||||
regions: string[];
|
||||
gamemodes: string[];
|
||||
maps: string[];
|
||||
minPlayers: number;
|
||||
maxPlayers: number;
|
||||
minRemainingTime: number;
|
||||
openServerBrowser: boolean;
|
||||
autoJoin: boolean;
|
||||
};
|
||||
keybinds: {
|
||||
reload: Keybind;
|
||||
newMatch: Keybind;
|
||||
copyGameLink: Keybind;
|
||||
joinFromClipboard: Keybind;
|
||||
devTools: Keybind;
|
||||
matchmaker: Keybind;
|
||||
matchmakerAccept: Keybind;
|
||||
matchmakerCancel: Keybind;
|
||||
pauseChat: Keybind;
|
||||
fullscreenToggle: Keybind;
|
||||
};
|
||||
userscripts: {
|
||||
enabled: boolean;
|
||||
path: string;
|
||||
};
|
||||
ui: {
|
||||
showExitButton: boolean;
|
||||
deathscreenAnimation: boolean;
|
||||
hideMenuPopups: boolean;
|
||||
menuTimer: boolean;
|
||||
doublePing: boolean;
|
||||
cssTheme: string;
|
||||
loadingTheme: string;
|
||||
backgroundUrl: string;
|
||||
showChangelog: boolean;
|
||||
lastSeenVersion: string;
|
||||
};
|
||||
discord: {
|
||||
enabled: boolean;
|
||||
};
|
||||
translator: {
|
||||
enabled: boolean;
|
||||
targetLanguage: string;
|
||||
showLanguageTag: boolean;
|
||||
};
|
||||
advanced: {
|
||||
removeUselessFeatures: boolean;
|
||||
gpuRasterizing: boolean;
|
||||
helpfulFlags: boolean;
|
||||
increaseLimits: boolean;
|
||||
lowLatency: boolean;
|
||||
experimentalFlags: boolean;
|
||||
angleBackend: string;
|
||||
verboseLogging: boolean;
|
||||
};
|
||||
accounts: SavedAccount[];
|
||||
tabWindow: {
|
||||
width: number;
|
||||
height: number;
|
||||
x: number | undefined;
|
||||
y: number | undefined;
|
||||
maximized: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const DEFAULT_KEYBINDS: AppConfig['keybinds'] = {
|
||||
reload: { key: 'F5', ctrl: false, shift: false, alt: false },
|
||||
newMatch: { key: 'F4', ctrl: false, shift: false, alt: false },
|
||||
copyGameLink: { key: 'l', ctrl: true, shift: false, alt: false },
|
||||
joinFromClipboard: { key: 'j', ctrl: true, shift: false, alt: false },
|
||||
devTools: { key: 'F12', ctrl: false, shift: false, alt: false },
|
||||
matchmaker: { key: 'F6', ctrl: false, shift: false, alt: false },
|
||||
matchmakerAccept: { key: 'Enter', ctrl: false, shift: false, alt: false },
|
||||
matchmakerCancel: { key: 'Escape', ctrl: false, shift: false, alt: false },
|
||||
pauseChat: { key: 'F10', ctrl: false, shift: false, alt: false },
|
||||
fullscreenToggle: { key: 'F11', ctrl: false, shift: false, alt: false },
|
||||
};
|
||||
|
||||
|
||||
export const config = new Store<AppConfig>({
|
||||
name: 'krunker-civilian-config',
|
||||
defaults: {
|
||||
window: {
|
||||
width: 1600,
|
||||
height: 900,
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
maximized: false,
|
||||
fullscreen: false,
|
||||
},
|
||||
performance: {
|
||||
fpsUnlocked: true,
|
||||
hardwareAccel: true,
|
||||
gpuPreference: 'high-performance',
|
||||
cpuThrottleGame: 1,
|
||||
cpuThrottleMenu: 1.5,
|
||||
processPriority: 'Normal',
|
||||
},
|
||||
game: {
|
||||
lastServer: '',
|
||||
socialTabBehaviour: 'New Window',
|
||||
joinAsSpectator: false,
|
||||
rawInput: true,
|
||||
betterChat: true,
|
||||
chatHistorySize: 200,
|
||||
showPing: true,
|
||||
hpEnemyCounter: true,
|
||||
},
|
||||
swapper: {
|
||||
enabled: true,
|
||||
path: '',
|
||||
},
|
||||
matchmaker: {
|
||||
enabled: true,
|
||||
regions: [],
|
||||
gamemodes: [],
|
||||
maps: [],
|
||||
minPlayers: 1,
|
||||
maxPlayers: 6,
|
||||
minRemainingTime: 120,
|
||||
openServerBrowser: true,
|
||||
autoJoin: false,
|
||||
},
|
||||
keybinds: DEFAULT_KEYBINDS,
|
||||
userscripts: {
|
||||
enabled: true,
|
||||
path: '',
|
||||
},
|
||||
ui: {
|
||||
showExitButton: true,
|
||||
deathscreenAnimation: true,
|
||||
hideMenuPopups: false,
|
||||
menuTimer: true,
|
||||
doublePing: true,
|
||||
cssTheme: 'disabled',
|
||||
loadingTheme: 'disabled',
|
||||
backgroundUrl: '',
|
||||
showChangelog: true,
|
||||
lastSeenVersion: '',
|
||||
},
|
||||
discord: {
|
||||
enabled: false,
|
||||
},
|
||||
translator: {
|
||||
enabled: true,
|
||||
targetLanguage: 'en',
|
||||
showLanguageTag: true,
|
||||
},
|
||||
advanced: {
|
||||
removeUselessFeatures: true,
|
||||
gpuRasterizing: false,
|
||||
helpfulFlags: true,
|
||||
increaseLimits: false,
|
||||
lowLatency: false,
|
||||
experimentalFlags: false,
|
||||
angleBackend: 'default',
|
||||
verboseLogging: false,
|
||||
},
|
||||
accounts: [],
|
||||
tabWindow: {
|
||||
width: 1280,
|
||||
height: 720,
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
maximized: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
import Store from 'electron-store';
|
||||
|
||||
export interface Keybind {
|
||||
key: string;
|
||||
ctrl: boolean;
|
||||
shift: boolean;
|
||||
alt: boolean;
|
||||
}
|
||||
|
||||
export interface SavedAccount {
|
||||
label: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface AppConfig {
|
||||
window: {
|
||||
width: number;
|
||||
height: number;
|
||||
x: number | undefined;
|
||||
y: number | undefined;
|
||||
maximized: boolean;
|
||||
fullscreen: boolean;
|
||||
};
|
||||
performance: {
|
||||
fpsUnlocked: boolean;
|
||||
hardwareAccel: boolean;
|
||||
gpuPreference: 'high-performance' | 'low-power' | 'default';
|
||||
cpuThrottleGame: number;
|
||||
cpuThrottleMenu: number;
|
||||
processPriority: string;
|
||||
};
|
||||
game: {
|
||||
lastServer: string;
|
||||
socialTabBehaviour: 'New Window' | 'Same Window';
|
||||
joinAsSpectator: boolean;
|
||||
rawInput: boolean;
|
||||
betterChat: boolean;
|
||||
chatHistorySize: number;
|
||||
showPing: boolean;
|
||||
hpEnemyCounter: boolean;
|
||||
};
|
||||
swapper: {
|
||||
enabled: boolean;
|
||||
path: string;
|
||||
};
|
||||
matchmaker: {
|
||||
enabled: boolean;
|
||||
regions: string[];
|
||||
gamemodes: string[];
|
||||
maps: string[];
|
||||
minPlayers: number;
|
||||
maxPlayers: number;
|
||||
minRemainingTime: number;
|
||||
openServerBrowser: boolean;
|
||||
autoJoin: boolean;
|
||||
};
|
||||
keybinds: {
|
||||
reload: Keybind;
|
||||
newMatch: Keybind;
|
||||
copyGameLink: Keybind;
|
||||
joinFromClipboard: Keybind;
|
||||
devTools: Keybind;
|
||||
matchmaker: Keybind;
|
||||
matchmakerAccept: Keybind;
|
||||
matchmakerCancel: Keybind;
|
||||
fullscreenToggle: Keybind;
|
||||
};
|
||||
userscripts: {
|
||||
enabled: boolean;
|
||||
path: string;
|
||||
};
|
||||
ui: {
|
||||
showExitButton: boolean;
|
||||
deathscreenAnimation: boolean;
|
||||
hideMenuPopups: boolean;
|
||||
menuTimer: boolean;
|
||||
doublePing: boolean;
|
||||
cssTheme: string;
|
||||
loadingTheme: string;
|
||||
backgroundUrl: string;
|
||||
showChangelog: boolean;
|
||||
lastSeenVersion: string;
|
||||
};
|
||||
discord: {
|
||||
enabled: boolean;
|
||||
};
|
||||
translator: {
|
||||
enabled: boolean;
|
||||
targetLanguage: string;
|
||||
showLanguageTag: boolean;
|
||||
};
|
||||
advanced: {
|
||||
removeUselessFeatures: boolean;
|
||||
gpuRasterizing: boolean;
|
||||
helpfulFlags: boolean;
|
||||
increaseLimits: boolean;
|
||||
lowLatency: boolean;
|
||||
experimentalFlags: boolean;
|
||||
angleBackend: string;
|
||||
verboseLogging: boolean;
|
||||
};
|
||||
accounts: SavedAccount[];
|
||||
tabWindow: {
|
||||
width: number;
|
||||
height: number;
|
||||
x: number | undefined;
|
||||
y: number | undefined;
|
||||
maximized: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const DEFAULT_KEYBINDS: AppConfig['keybinds'] = {
|
||||
reload: { key: 'F5', ctrl: false, shift: false, alt: false },
|
||||
newMatch: { key: 'F4', ctrl: false, shift: false, alt: false },
|
||||
copyGameLink: { key: 'l', ctrl: true, shift: false, alt: false },
|
||||
joinFromClipboard: { key: 'j', ctrl: true, shift: false, alt: false },
|
||||
devTools: { key: 'F12', ctrl: false, shift: false, alt: false },
|
||||
matchmaker: { key: 'F6', ctrl: false, shift: false, alt: false },
|
||||
matchmakerAccept: { key: 'Enter', ctrl: false, shift: false, alt: false },
|
||||
matchmakerCancel: { key: 'Escape', ctrl: false, shift: false, alt: false },
|
||||
fullscreenToggle: { key: 'F11', ctrl: false, shift: false, alt: false },
|
||||
};
|
||||
|
||||
|
||||
export const config = new Store<AppConfig>({
|
||||
name: 'krunker-civilian-config',
|
||||
defaults: {
|
||||
window: {
|
||||
width: 1600,
|
||||
height: 900,
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
maximized: false,
|
||||
fullscreen: false,
|
||||
},
|
||||
performance: {
|
||||
fpsUnlocked: true,
|
||||
hardwareAccel: true,
|
||||
gpuPreference: 'high-performance',
|
||||
cpuThrottleGame: 1,
|
||||
cpuThrottleMenu: 1.5,
|
||||
processPriority: 'Normal',
|
||||
},
|
||||
game: {
|
||||
lastServer: '',
|
||||
socialTabBehaviour: 'New Window',
|
||||
joinAsSpectator: false,
|
||||
rawInput: true,
|
||||
betterChat: true,
|
||||
chatHistorySize: 200,
|
||||
showPing: true,
|
||||
hpEnemyCounter: true,
|
||||
},
|
||||
swapper: {
|
||||
enabled: true,
|
||||
path: '',
|
||||
},
|
||||
matchmaker: {
|
||||
enabled: true,
|
||||
regions: [],
|
||||
gamemodes: [],
|
||||
maps: [],
|
||||
minPlayers: 1,
|
||||
maxPlayers: 6,
|
||||
minRemainingTime: 120,
|
||||
openServerBrowser: true,
|
||||
autoJoin: false,
|
||||
},
|
||||
keybinds: DEFAULT_KEYBINDS,
|
||||
userscripts: {
|
||||
enabled: true,
|
||||
path: '',
|
||||
},
|
||||
ui: {
|
||||
showExitButton: true,
|
||||
deathscreenAnimation: true,
|
||||
hideMenuPopups: false,
|
||||
menuTimer: true,
|
||||
doublePing: true,
|
||||
cssTheme: 'disabled',
|
||||
loadingTheme: 'disabled',
|
||||
backgroundUrl: '',
|
||||
showChangelog: true,
|
||||
lastSeenVersion: '',
|
||||
},
|
||||
discord: {
|
||||
enabled: false,
|
||||
},
|
||||
translator: {
|
||||
enabled: true,
|
||||
targetLanguage: 'en',
|
||||
showLanguageTag: true,
|
||||
},
|
||||
advanced: {
|
||||
removeUselessFeatures: true,
|
||||
gpuRasterizing: false,
|
||||
helpfulFlags: true,
|
||||
increaseLimits: false,
|
||||
lowLatency: false,
|
||||
experimentalFlags: false,
|
||||
angleBackend: 'default',
|
||||
verboseLogging: false,
|
||||
},
|
||||
accounts: [],
|
||||
tabWindow: {
|
||||
width: 1280,
|
||||
height: 720,
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
maximized: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
+24
-2
@@ -16,8 +16,11 @@ let observer: MutationObserver | null = null;
|
||||
let historyMax = 0;
|
||||
let betterChatEnabled = false;
|
||||
let reInsertGuard = false;
|
||||
let scrollPaused = false;
|
||||
let _con: SavedConsole | null = null;
|
||||
|
||||
const SCROLL_BOTTOM_THRESHOLD = 30; // px from bottom to consider "at bottom"
|
||||
|
||||
function isChatMessage(node: Node): node is HTMLElement {
|
||||
return node.nodeType === 1 && (node as HTMLElement).id?.startsWith('chatMsg_');
|
||||
}
|
||||
@@ -83,18 +86,37 @@ function handleMutations(mutations: MutationRecord[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-scroll unless paused
|
||||
if (chatList && !chatList.classList.contains('kpc-chat-paused')) {
|
||||
// Auto-scroll to bottom unless the user has scrolled up
|
||||
if (chatList && !scrollPaused) {
|
||||
chatList.scrollTop = chatList.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function isNearBottom(el: HTMLElement): boolean {
|
||||
return el.scrollHeight - el.scrollTop - el.clientHeight <= SCROLL_BOTTOM_THRESHOLD;
|
||||
}
|
||||
|
||||
function updatePauseState(): void {
|
||||
if (!chatList) return;
|
||||
const atBottom = isNearBottom(chatList);
|
||||
if (scrollPaused && atBottom) {
|
||||
scrollPaused = false;
|
||||
chatList.classList.remove('kpc-chat-paused');
|
||||
} else if (!scrollPaused && !atBottom) {
|
||||
scrollPaused = true;
|
||||
chatList.classList.add('kpc-chat-paused');
|
||||
}
|
||||
}
|
||||
|
||||
function tryAttach(): boolean {
|
||||
chatList = document.getElementById('chatList');
|
||||
if (!chatList) return false;
|
||||
|
||||
observer = new MutationObserver(handleMutations);
|
||||
observer.observe(chatList, { childList: true });
|
||||
|
||||
chatList.addEventListener('scroll', updatePauseState, { passive: true });
|
||||
|
||||
_con?.log('[KCC-Chat] Observer attached to #chatList');
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user