feat: naomi did too much at once (#53)
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 53s
CI / Lint & Test (push) Successful in 14m10s
CI / Build Linux (push) Successful in 16m47s
CI / Build Windows (cross-compile) (push) Successful in 26m36s

- feat: add slash commands
- feat: toggle window always on top
- fix: save settings button closes settings panel
- feat: input history (both text and commands)
- feat: add keyboard shortcuts
- feat: add confirmation modal when closing connected tabs
- fix: better text colours in light mode
- fix: handle multiple tabs requesting permission

Closes #6
Closes #13
Closes #21
Closes #28

Reviewed-on: #53
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #53.
This commit is contained in:
2026-01-21 17:38:36 -08:00
committed by Naomi Carrigan
parent 9fe4e8a48a
commit 947e56ef41
17 changed files with 1040 additions and 137 deletions
+76 -1
View File
@@ -1,9 +1,13 @@
<script lang="ts">
import { onMount, onDestroy } from "svelte";
import { invoke } from "@tauri-apps/api/core";
import { get } from "svelte/store";
import { initializeTauriListeners, cleanupTauriListeners } from "$lib/tauri";
import { configStore, applyTheme } from "$lib/stores/config";
import { initNotificationSync, cleanupNotificationSync } from "$lib/stores/notifications";
import { conversationsStore } from "$lib/stores/conversations";
import { claudeStore, isClaudeProcessing } from "$lib/stores/claude";
import { getCurrentWindow } from "@tauri-apps/api/window";
import "$lib/notifications/testNotifications";
import Terminal from "$lib/components/Terminal.svelte";
import InputBar from "$lib/components/InputBar.svelte";
@@ -17,6 +21,67 @@
let initialized = false;
let achievementPanelOpen = $state(false);
// Global keyboard shortcuts
function handleGlobalKeydown(event: KeyboardEvent) {
// Don't trigger shortcuts when typing in inputs (except for specific ones)
const target = event.target as HTMLElement;
const isInputFocused = target.tagName === "INPUT" || target.tagName === "TEXTAREA";
// Escape closes panels (always works)
if (event.key === "Escape") {
// Check if any panels are open and close them
if (achievementPanelOpen) {
achievementPanelOpen = false;
event.preventDefault();
return;
}
// ConfigSidebar handles its own escape via store
if (get(configStore.isSidebarOpen)) {
configStore.closeSidebar();
event.preventDefault();
return;
}
}
// Skip other shortcuts if user is typing in an input
if (isInputFocused) return;
// Ctrl+L - Clear terminal
if (event.ctrlKey && event.key === "l") {
event.preventDefault();
claudeStore.clearTerminal();
return;
}
// Ctrl+, - Open settings
if (event.ctrlKey && event.key === ",") {
event.preventDefault();
configStore.openSidebar();
return;
}
// Ctrl+C - Interrupt (only when processing)
if (event.ctrlKey && event.key === "c") {
if (get(isClaudeProcessing)) {
event.preventDefault();
handleInterrupt();
return;
}
}
}
async function handleInterrupt() {
try {
const conversationId = get(claudeStore.activeConversationId);
if (!conversationId) return;
await invoke("interrupt_claude", { conversationId });
claudeStore.addLine("system", "Process interrupted");
} catch (error) {
console.error("Failed to interrupt:", error);
}
}
onMount(async () => {
if (!initialized) {
initialized = true;
@@ -27,12 +92,21 @@
await initializeTauriListeners();
await configStore.loadConfig();
// Apply saved theme on startup
// Apply saved settings on startup
const config = configStore.getConfig();
applyTheme(config.theme);
// Apply always-on-top setting
if (config.always_on_top) {
const window = getCurrentWindow();
await window.setAlwaysOnTop(true);
}
// Initialize notification settings sync
initNotificationSync();
// Add global keyboard shortcut listener
window.addEventListener("keydown", handleGlobalKeydown);
}
});
@@ -40,6 +114,7 @@
if (initialized) {
cleanupTauriListeners();
cleanupNotificationSync();
window.removeEventListener("keydown", handleGlobalKeydown);
initialized = false;
}
});