feat(site): add Kyleosophy alternative completion chimes (#23891)

- Add "Enable Kyleosophy" toggle to Settings > Behavior
- When enabled, replaces standard completion chime with random Kyle
sound clips
- Ships 8 alternative `.mp3` files as static assets (~82KB total)
- localStorage preference (`agents.kyleosophy`), defaults to off
- Pauses orphaned Audio elements on sound URL change to prevent overlap

<details><summary>Review findings addressed</summary>

- **P2** Stale JSDoc on `playChimeAudio` — updated to reflect
parameterized behavior
- **P3** Overlapping audio on rapid completions — added
`chimeAudio?.pause()` before replacement
- **P3** Test ordering dependency — pinned `Math.random` for
determinism, documented cache behavior
- **Nit** Setter naming — `setLocalKyleosophy` → `setKylesophyLocal`
- Toggle moved to bottom of Behavior page per product request
- Description changed to "IYKYK" per product request

</details>

> 🤖 Written by a Coder Agent. Reviewed by a human.
This commit is contained in:
Cian Johnston
2026-04-01 10:06:01 +00:00
committed by GitHub
parent d6df78c9b9
commit bec426b24f
11 changed files with 151 additions and 12 deletions
@@ -13,6 +13,7 @@ import { DurationField } from "./components/DurationField/DurationField";
import { SectionHeader } from "./components/SectionHeader";
import { TextPreviewDialog } from "./components/TextPreviewDialog";
import { UserCompactionThresholdSettings } from "./components/UserCompactionThresholdSettings";
import { getKylesophyEnabled, setKylesophyEnabled } from "./utils/chime";
const textareaMaxHeight = 240;
const textareaBaseClassName =
@@ -124,6 +125,7 @@ export const AgentSettingsBehaviorPageView: FC<
const [isUserPromptOverflowing, setIsUserPromptOverflowing] = useState(false);
const [isSystemPromptOverflowing, setIsSystemPromptOverflowing] =
useState(false);
const [kylesophyEnabled, setKylesophyLocal] = useState(getKylesophyEnabled);
// ── Derived state ──
const hasLoadedSystemPrompt = systemPromptData !== undefined;
@@ -510,6 +512,26 @@ export const AgentSettingsBehaviorPageView: FC<
</form>
</>
)}
<hr className="my-5 border-0 border-t border-solid border-border" />
{/* ── Kyleosophy toggle (always visible) ── */}
<div className="space-y-2">
<h3 className="m-0 text-[13px] font-semibold text-content-primary">
Kyleosophy
</h3>
<div className="flex items-center justify-between gap-4">
<p className="!mt-0.5 m-0 flex-1 text-xs text-content-secondary">
Replace the standard completion chime. IYKYK.
</p>
<Switch
checked={kylesophyEnabled}
onCheckedChange={(checked) => {
setKylesophyEnabled(checked);
setKylesophyLocal(checked);
}}
aria-label="Enable Kyleosophy"
/>
</div>
</div>
{showDefaultPromptPreview && (
<TextPreviewDialog
content={defaultSystemPrompt}
@@ -1,9 +1,12 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
getChimeEnabled,
getKylesophyEnabled,
KYLEOSOPHY_SOUNDS,
LOCK_HOLD_MS,
maybePlayChime,
setChimeEnabled,
setKylesophyEnabled,
} from "./chime";
// ---------------------------------------------------------------------------
@@ -69,6 +72,40 @@ describe("getChimeEnabled / setChimeEnabled", () => {
});
});
// ---------------------------------------------------------------------------
// Kyleosophy preference helpers
// ---------------------------------------------------------------------------
describe("getKylesophyEnabled / setKylesophyEnabled", () => {
beforeEach(() => {
localStorage.clear();
});
it("defaults to false when nothing is stored", () => {
expect(getKylesophyEnabled()).toBe(false);
});
it("returns true when stored as 'true'", () => {
localStorage.setItem("agents.kyleosophy", "true");
expect(getKylesophyEnabled()).toBe(true);
});
it("returns false when stored as 'false'", () => {
localStorage.setItem("agents.kyleosophy", "false");
expect(getKylesophyEnabled()).toBe(false);
});
it("setKylesophyEnabled persists the value", () => {
setKylesophyEnabled(false);
expect(localStorage.getItem("agents.kyleosophy")).toBe("false");
expect(getKylesophyEnabled()).toBe(false);
setKylesophyEnabled(true);
expect(localStorage.getItem("agents.kyleosophy")).toBe("true");
expect(getKylesophyEnabled()).toBe(true);
});
});
// ---------------------------------------------------------------------------
// maybePlayChime
// ---------------------------------------------------------------------------
@@ -235,4 +272,44 @@ describe("maybePlayChime", () => {
// Should play immediately without needing to advance timers.
expect(playSpy).toHaveBeenCalledTimes(1);
});
// -- Kyleosophy sound selection --
it("uses a kyleosophy sound when kyleosophy is enabled", async () => {
setKylesophyEnabled(true);
vi.spyOn(document, "hidden", "get").mockReturnValue(true);
// Pin the random selection so the test is deterministic.
vi.spyOn(Math, "random").mockReturnValue(0.5);
const audioSpy = vi.spyOn(globalThis, "Audio" as never);
await triggerAndSettle("running", "waiting", "chat-1", "chat-2");
expect(playSpy).toHaveBeenCalledTimes(1);
expect(audioSpy).toHaveBeenCalledTimes(1);
const url = (audioSpy as unknown as ReturnType<typeof vi.fn>).mock
.calls[0][0] as string;
// Math.floor(0.5 * 8) = 4 → "/chime_5.mp3"
expect(url).toBe("/chime_5.mp3");
expect(KYLEOSOPHY_SOUNDS).toContain(url);
});
it("uses default chime.mp3 when kyleosophy is disabled", async () => {
setKylesophyEnabled(false);
vi.spyOn(document, "hidden", "get").mockReturnValue(true);
// Force a fresh Audio element by spying on the constructor
// before any call in this test. The previous test left
// lastSoundUrl pointing at a kyleosophy URL, so switching
// back to /chime.mp3 will always trigger a new Audio().
const audioSpy = vi.spyOn(globalThis, "Audio" as never);
await triggerAndSettle("running", "waiting", "chat-1", "chat-2");
expect(playSpy).toHaveBeenCalledTimes(1);
expect(audioSpy).toHaveBeenCalledTimes(1);
const url = (audioSpy as unknown as ReturnType<typeof vi.fn>).mock
.calls[0][0] as string;
expect(url).toBe("/chime.mp3");
});
});
+52 -12
View File
@@ -1,4 +1,5 @@
const CHIME_PREFERENCE_KEY = "agents.chime-on-completion";
const KYLEOSOPHY_PREFERENCE_KEY = "agents.kyleosophy";
export function getChimeEnabled(): boolean {
try {
@@ -19,20 +20,55 @@ export function setChimeEnabled(enabled: boolean): void {
}
}
export function getKylesophyEnabled(): boolean {
try {
const stored = localStorage.getItem(KYLEOSOPHY_PREFERENCE_KEY);
return stored === null ? false : stored === "true";
} catch {
return false;
}
}
export function setKylesophyEnabled(enabled: boolean): void {
try {
localStorage.setItem(KYLEOSOPHY_PREFERENCE_KEY, String(enabled));
} catch {
// Silently ignore storage errors (e.g. private browsing
// quota exceeded).
}
}
/**
* Play the completion chime audio file. The file is a short,
* warm two-tone bell sound shipped as a static asset.
*
* A single Audio element is reused across calls so the browser
* only fetches the file once.
* Alternative completion sounds for Kyleosophy mode. All are
* shipped as static assets alongside chime.mp3.
*/
export const KYLEOSOPHY_SOUNDS: readonly string[] = [
"/chime_1.mp3", // absolutely massive
"/chime_2.mp3", // dope
"/chime_3.mp3", // great
"/chime_4.mp3", // oh god
"/chime_5.mp3", // okay
"/chime_6.mp3", // open up a pr
"/chime_7.mp3", // sweet
"/chime_8.mp3", // yep
];
/**
* Play a completion sound. When Kyleosophy is enabled a random
* voice clip is selected; otherwise the default bell chime is
* used. The Audio element is cached and reused when the sound
* URL hasn't changed between calls.
*/
let chimeAudio: HTMLAudioElement | null = null;
let lastSoundUrl: string | null = null;
function playChimeAudio(): void {
function playChimeAudio(soundUrl = "/chime.mp3"): void {
try {
if (!chimeAudio) {
chimeAudio = new Audio("/chime.mp3");
if (!chimeAudio || soundUrl !== lastSoundUrl) {
chimeAudio?.pause();
chimeAudio = new Audio(soundUrl);
chimeAudio.volume = 0.5;
lastSoundUrl = soundUrl;
}
// Reset to the start in case a previous play hasn't
// finished yet.
@@ -75,9 +111,9 @@ export const LOCK_HOLD_MS = 2000;
* Falls back to playing immediately when the Web Locks API is
* not available (preserving the original single-tab behavior).
*/
function playChime(chatID: string): void {
function playChime(chatID: string, soundUrl?: string): void {
if (typeof navigator === "undefined" || !navigator.locks) {
playChimeAudio();
playChimeAudio(soundUrl);
return;
}
@@ -93,7 +129,7 @@ function playChime(chatID: string): void {
return;
}
playChimeAudio();
playChimeAudio(soundUrl);
// Hold the lock briefly so that tabs receiving the
// WebSocket event a bit later will see the lock as
@@ -149,5 +185,9 @@ export function maybePlayChime(
return;
}
playChime(chatID);
const soundUrl = getKylesophyEnabled()
? KYLEOSOPHY_SOUNDS[Math.floor(Math.random() * KYLEOSOPHY_SOUNDS.length)]
: undefined;
playChime(chatID, soundUrl);
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.