From aeb0b2fada200872a13da49565d08d5b65973e1b Mon Sep 17 00:00:00 2001
From: itsnobii <57056220+itsnobii@users.noreply.github.com>
Date: Sun, 5 Apr 2026 17:16:26 -0400
Subject: [PATCH 1/2] Add hotkey for agent select and send activation state to
server
---
angular/src/app/hotkeys/hotkeys.component.css | 43 +++++++++++++++++++
.../src/app/hotkeys/hotkeys.component.html | 41 ++++++++++++++++++
angular/src/app/hotkeys/hotkeys.component.ts | 41 ++++++++++++++++--
.../src/app/observer/observer.component.ts | 5 +++
src/main.ts | 1 +
src/services/connectorService.ts | 2 +
src/services/formattingService.ts | 1 +
7 files changed, 131 insertions(+), 3 deletions(-)
diff --git a/angular/src/app/hotkeys/hotkeys.component.css b/angular/src/app/hotkeys/hotkeys.component.css
index e69de29..81b0b60 100644
--- a/angular/src/app/hotkeys/hotkeys.component.css
+++ b/angular/src/app/hotkeys/hotkeys.component.css
@@ -0,0 +1,43 @@
+.agent-select-warning {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ justify-content: center;
+ padding-bottom: 1rem;
+ pointer-events: none;
+ z-index: 9999;
+ animation: toast-lifecycle 5s ease-in-out forwards;
+}
+
+.agent-select-warning-inner {
+ background-color: var(--p-surface-800);
+ color: var(--p-surface-0);
+ font-size: 0.875rem;
+ padding: 0.625rem 1rem;
+ border-radius: 0.5rem;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ border: 1px solid var(--p-surface-600);
+ max-width: 28rem;
+ text-align: center;
+}
+
+@keyframes toast-lifecycle {
+ 0% {
+ transform: translateY(150%);
+ opacity: 0;
+ }
+ 7% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+ 88% {
+ transform: translateY(0);
+ opacity: 1;
+ }
+ 100% {
+ transform: translateY(150%);
+ opacity: 0;
+ }
+}
diff --git a/angular/src/app/hotkeys/hotkeys.component.html b/angular/src/app/hotkeys/hotkeys.component.html
index 0cc63d4..18c61f6 100644
--- a/angular/src/app/hotkeys/hotkeys.component.html
+++ b/angular/src/app/hotkeys/hotkeys.component.html
@@ -202,4 +202,45 @@
Hotkeys
+
+
+
+
+
+
+
+
+
+
+
+
+@if (showAgentSelectWarning) {
+
+
+ Enabling the hotkey for agent select will stop it from displaying automatically.
+
+
+}
diff --git a/angular/src/app/hotkeys/hotkeys.component.ts b/angular/src/app/hotkeys/hotkeys.component.ts
index 1350e2b..8fcee1d 100644
--- a/angular/src/app/hotkeys/hotkeys.component.ts
+++ b/angular/src/app/hotkeys/hotkeys.component.ts
@@ -4,6 +4,7 @@ import {
EventEmitter,
HostListener,
Input,
+ OnDestroy,
Output,
ViewChild,
} from "@angular/core";
@@ -37,7 +38,7 @@ import { ToggleSwitchModule } from "primeng/toggleswitch";
templateUrl: "./hotkeys.component.html",
styleUrl: "./hotkeys.component.css",
})
-export class HotkeysComponent implements Validatable, AfterContentInit {
+export class HotkeysComponent implements Validatable, AfterContentInit, OnDestroy {
@Input({ required: true })
data!: Hotkeys;
@@ -48,11 +49,29 @@ export class HotkeysComponent implements Validatable, AfterContentInit {
rightTimeout: false,
switchKdaCredits: false,
showToast: false,
+ toggleAgentSelect: false,
};
@ViewChild("change_popover")
changePopover!: Popover;
+ protected showAgentSelectWarning = false;
+ private warningTimer: ReturnType | null = null;
+
+ onToggleAgentSelectChange(enabled: boolean) {
+ if (!enabled) return;
+ if (this.warningTimer !== null) clearTimeout(this.warningTimer);
+ this.showAgentSelectWarning = true;
+ this.warningTimer = setTimeout(() => {
+ this.showAgentSelectWarning = false;
+ this.warningTimer = null;
+ }, 5000);
+ }
+
+ ngOnDestroy() {
+ if (this.warningTimer !== null) clearTimeout(this.warningTimer);
+ }
+
ngAfterContentInit(): void {
this.runValidation();
}
@@ -65,6 +84,7 @@ export class HotkeysComponent implements Validatable, AfterContentInit {
| "spike"
| "switchKdaCredits"
| "showToast"
+ | "toggleAgentSelect"
| "" = "";
@HostListener("window:keyup", ["$event"])
@@ -120,12 +140,23 @@ export class HotkeysComponent implements Validatable, AfterContentInit {
break;
case "showToast":
this.data.showToast = keyString;
+ break;
+ case "toggleAgentSelect":
+ this.data.toggleAgentSelect = keyString;
+ break;
}
}
changeKeybind(
event: any,
- hotkey: "timeLeft" | "timeRight" | "techPause" | "spike" | "switchKdaCredits" | "showToast",
+ hotkey:
+ | "timeLeft"
+ | "timeRight"
+ | "techPause"
+ | "spike"
+ | "switchKdaCredits"
+ | "showToast"
+ | "toggleAgentSelect",
) {
this.isCapturing = true;
this.capturingHotkey = hotkey;
@@ -150,6 +181,9 @@ export class HotkeysComponent implements Validatable, AfterContentInit {
!this.data.enabled.switchKdaCredits || this.data.switchKdaCredits.match(hotkeyRegex) != null;
this.valid.showToast =
!this.data.enabled.showToast || this.data.showToast.match(hotkeyRegex) != null;
+ this.valid.toggleAgentSelect =
+ !this.data.enabled.toggleAgentSelect ||
+ this.data.toggleAgentSelect.match(hotkeyRegex) != null;
valid =
this.valid.spikePlanted &&
@@ -157,7 +191,8 @@ export class HotkeysComponent implements Validatable, AfterContentInit {
this.valid.leftTimeout &&
this.valid.rightTimeout &&
this.valid.switchKdaCredits &&
- this.valid.showToast;
+ this.valid.showToast &&
+ this.valid.toggleAgentSelect;
this.validationChanged.emit(valid ? ValidationState.VALID : ValidationState.INVALID);
}
}
diff --git a/angular/src/app/observer/observer.component.ts b/angular/src/app/observer/observer.component.ts
index 0e2a965..2b16cca 100644
--- a/angular/src/app/observer/observer.component.ts
+++ b/angular/src/app/observer/observer.component.ts
@@ -259,6 +259,7 @@ export class ObserverComponent implements OnInit {
rightTimeout: "P",
switchKdaCredits: "I",
showToast: "L",
+ toggleAgentSelect: "U",
enabled: {
spikePlanted: false,
techPause: true,
@@ -266,6 +267,7 @@ export class ObserverComponent implements OnInit {
rightTimeout: true,
switchKdaCredits: true,
showToast: false,
+ toggleAgentSelect: false,
},
};
@@ -347,6 +349,7 @@ export class ObserverComponent implements OnInit {
rightTimeout: true,
switchKdaCredits: true,
showToast: true,
+ toggleAgentSelect: false,
};
}
// Hotkey migration for switch KDA/Credits
@@ -959,6 +962,7 @@ export type Hotkeys = {
rightTimeout: string;
switchKdaCredits: string;
showToast: string;
+ toggleAgentSelect: string;
enabled: {
spikePlanted: boolean;
techPause: boolean;
@@ -966,6 +970,7 @@ export type Hotkeys = {
rightTimeout: boolean;
switchKdaCredits: boolean;
showToast: boolean;
+ toggleAgentSelect: boolean;
};
};
diff --git a/src/main.ts b/src/main.ts
index 96eac4c..1e06aa3 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -557,6 +557,7 @@ function processInputs(
playercamsInfo,
timeoutInfo,
roundWinBox,
+ hotkeys.enabled.toggleAgentSelect,
win,
);
}
diff --git a/src/services/connectorService.ts b/src/services/connectorService.ts
index aafcf36..1fc09a8 100644
--- a/src/services/connectorService.ts
+++ b/src/services/connectorService.ts
@@ -91,6 +91,7 @@ export class ConnectorService {
playercamsInfo: PlayercamsInfo,
timeoutInfo: ITimeoutInfo,
roundWinBox: IRoundWinBox,
+ agentSelectHotkeyEnabled: boolean,
win: Electron.Main.BrowserWindow,
) {
if (RegExp("(http|https)://[^/]+:[0-9]+").test(ingestIp)) {
@@ -204,6 +205,7 @@ export class ConnectorService {
watermarkInfo: watermarkInfo,
playercamsInfo: playercamsInfo,
roundWinBox: roundWinBox,
+ agentSelectHotkeyEnabled: agentSelectHotkeyEnabled,
},
};
this.ws.emit(SocketChannels.OBSERVER_LOGON, JSON.stringify(logonData));
diff --git a/src/services/formattingService.ts b/src/services/formattingService.ts
index c0245e8..c5e170e 100644
--- a/src/services/formattingService.ts
+++ b/src/services/formattingService.ts
@@ -299,6 +299,7 @@ export interface IToolsData {
watermarkInfo: WatermarkInfo;
playercamsInfo: PlayercamsInfo;
roundWinBox: IRoundWinBox;
+ agentSelectHotkeyEnabled?: boolean;
}
export type ISeriesInfo = {
From c3dd6ade2ac1a403c402ebf7a54f426d17c58e8a Mon Sep 17 00:00:00 2001
From: itsnobii <57056220+itsnobii@users.noreply.github.com>
Date: Sun, 5 Apr 2026 17:28:49 -0400
Subject: [PATCH 2/2] Add agent select hotkey to appropriate services
---
src/main.ts | 5 +++++
src/services/formattingService.ts | 1 +
src/services/hotkeyService.ts | 12 ++++++++++++
3 files changed, 18 insertions(+)
diff --git a/src/main.ts b/src/main.ts
index 1e06aa3..5db7a51 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -526,6 +526,11 @@ function processInputs(
hotkeys.showToast,
hotkeys.enabled.showToast,
);
+ hotkeyService.setKeyForHotkey(
+ HotkeyType.TOGGLE_AGENT_SELECT,
+ hotkeys.toggleAgentSelect,
+ hotkeys.enabled.toggleAgentSelect,
+ );
} catch (error: any) {
messageBox("Spectra Client - Error", error.message, messageBoxType.ERROR);
return;
diff --git a/src/services/formattingService.ts b/src/services/formattingService.ts
index c5e170e..10f3d99 100644
--- a/src/services/formattingService.ts
+++ b/src/services/formattingService.ts
@@ -269,6 +269,7 @@ export enum DataTypes {
LEFT_TIMEOUT = "left_timeout",
RIGHT_TIMEOUT = "right_timeout",
SWITCH_KDA_CREDITS = "switch_kda_credits",
+ TOGGLE_AGENT_SELECT = "toggle_agent_select",
// Mid-match update type
TOAST = "toast",
SWAP_L_R = "swap_left_right",
diff --git a/src/services/hotkeyService.ts b/src/services/hotkeyService.ts
index 8c36469..316d224 100644
--- a/src/services/hotkeyService.ts
+++ b/src/services/hotkeyService.ts
@@ -49,6 +49,12 @@ export default class HotkeyService {
type: HotkeyType.SHOW_TOAST,
enabled: true,
};
+ this.hotkeys[HotkeyType.TOGGLE_AGENT_SELECT] = {
+ key: "",
+ action: this._toggleAgentSelectHotkeyAction.bind(this),
+ type: HotkeyType.TOGGLE_AGENT_SELECT,
+ enabled: false,
+ };
}
public static getInstance(): HotkeyService {
@@ -133,6 +139,11 @@ export default class HotkeyService {
private _showToastHotkeyAction() {
fireSendToast();
}
+
+ private _toggleAgentSelectHotkeyAction() {
+ const toSend = { type: DataTypes.TOGGLE_AGENT_SELECT, data: true };
+ ConnectorService.getInstance().sendToIngest(toSend);
+ }
}
export enum HotkeyType {
SPIKE_PLANTED,
@@ -141,6 +152,7 @@ export enum HotkeyType {
RIGHT_TIMEOUT,
SWITCH_KDA_CREDITS,
SHOW_TOAST,
+ TOGGLE_AGENT_SELECT,
}
type Hotkey = {
key: string;