generated from nhcarrigan/template
feat: multiple UI improvements, font settings, and memory file display names (#175)
## Summary - **fix**: `show_thinking_blocks` setting now persists across sessions — it was defined on the TypeScript side but missing from the Rust `HikariConfig` struct, so serde silently dropped it on every save/load - **feat**: Tool calls are now rendered as collapsible blocks matching the Extended Thinking block aesthetic, replacing the old inline dropdown approach - **feat**: Add configurable max output tokens setting - **feat**: Use random creative names for conversation tabs - **test**: Significantly expanded frontend unit test coverage - **docs**: Require tests for all changes in CLAUDE.md - **feat**: Allow users to specify a custom terminal font (Closes #176) - **feat**: Display friendly names for memory files derived from the first heading (Closes #177) - **feat**: Add custom UI font support for the app chrome (buttons, labels, tabs) - **fix**: Apply custom UI font to the full app interface — `.app-container` was hardcoded, blocking inheritance from `body`; also renamed "Custom Font" to "Custom Terminal Font" for clarity ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #175 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #175.
This commit is contained in:
@@ -232,6 +232,53 @@ describe("notifications", () => {
|
||||
// Should not throw
|
||||
await expect(soundPlayer.play(NotificationType.SUCCESS)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("play warns when audio type is not in the cache", async () => {
|
||||
const { soundPlayer } = await import("./soundPlayer");
|
||||
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
|
||||
soundPlayer.setEnabled(true);
|
||||
await soundPlayer.play("nonexistent" as NotificationType);
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith("No audio found for notification type: nonexistent");
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("play catches errors from audio playback", async () => {
|
||||
vi.resetModules();
|
||||
|
||||
class FailingAudio {
|
||||
volume = 1;
|
||||
preload = "auto";
|
||||
|
||||
cloneNode() {
|
||||
const clone = new FailingAudio();
|
||||
clone.volume = this.volume;
|
||||
return clone;
|
||||
}
|
||||
|
||||
async play(): Promise<void> {
|
||||
throw new Error("Playback blocked by browser");
|
||||
}
|
||||
}
|
||||
|
||||
const originalAudio = globalThis.Audio;
|
||||
globalThis.Audio = FailingAudio as unknown as typeof Audio;
|
||||
|
||||
const { soundPlayer: freshPlayer } = await import("./soundPlayer");
|
||||
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||
|
||||
freshPlayer.setEnabled(true);
|
||||
await freshPlayer.play(NotificationType.SUCCESS);
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
"Failed to play notification sound:",
|
||||
expect.any(Error)
|
||||
);
|
||||
|
||||
errorSpy.mockRestore();
|
||||
globalThis.Audio = originalAudio;
|
||||
});
|
||||
});
|
||||
|
||||
describe("NotificationManager class", () => {
|
||||
|
||||
Reference in New Issue
Block a user