generated from nhcarrigan/template
feat: naomi did too much at once (#53)
- 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:
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { configStore, type HikariConfig, type Theme } from "$lib/stores/config";
|
||||
import { claudeStore } from "$lib/stores/claude";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
|
||||
let config: HikariConfig = $state({
|
||||
model: null,
|
||||
@@ -13,6 +14,7 @@
|
||||
greeting_custom_prompt: null,
|
||||
notifications_enabled: true,
|
||||
notification_volume: 0.7,
|
||||
always_on_top: false,
|
||||
});
|
||||
|
||||
let isOpen = $state(false);
|
||||
@@ -61,6 +63,7 @@
|
||||
saveError = null;
|
||||
try {
|
||||
await configStore.saveConfig(config);
|
||||
configStore.closeSidebar();
|
||||
} catch {
|
||||
// Error is handled by the store
|
||||
} finally {
|
||||
@@ -95,6 +98,13 @@
|
||||
function importFromSession() {
|
||||
config.auto_granted_tools = [...new Set([...config.auto_granted_tools, ...grantedTools])];
|
||||
}
|
||||
|
||||
async function handleAlwaysOnTopChange(enabled: boolean) {
|
||||
config.always_on_top = enabled;
|
||||
const window = getCurrentWindow();
|
||||
await window.setAlwaysOnTop(enabled);
|
||||
await configStore.updateConfig({ always_on_top: enabled });
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Backdrop -->
|
||||
@@ -121,7 +131,7 @@
|
||||
<h2 class="text-lg font-semibold text-[var(--text-primary)]">Settings</h2>
|
||||
<button
|
||||
onclick={configStore.closeSidebar}
|
||||
class="p-1 text-gray-400 hover:text-white transition-colors"
|
||||
class="p-1 text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors"
|
||||
aria-label="Close settings"
|
||||
>
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -149,7 +159,7 @@
|
||||
|
||||
<!-- Model Selection -->
|
||||
<div class="mb-4">
|
||||
<label for="model" class="block text-sm text-gray-400 mb-1">Model</label>
|
||||
<label for="model" class="block text-sm text-[var(--text-secondary)] mb-1">Model</label>
|
||||
<select
|
||||
id="model"
|
||||
bind:value={config.model}
|
||||
@@ -163,8 +173,8 @@
|
||||
|
||||
<!-- API Key -->
|
||||
<div class="mb-4">
|
||||
<label for="api-key" class="block text-sm text-gray-400 mb-1">
|
||||
API Key <span class="text-gray-600">(optional override)</span>
|
||||
<label for="api-key" class="block text-sm text-[var(--text-secondary)] mb-1">
|
||||
API Key <span class="text-[var(--text-tertiary)]">(optional override)</span>
|
||||
</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
@@ -177,7 +187,7 @@
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (showApiKey = !showApiKey)}
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-300"
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 text-[var(--text-tertiary)] hover:text-[var(--text-primary)]"
|
||||
aria-label={showApiKey ? "Hide API key" : "Show API key"}
|
||||
>
|
||||
{#if showApiKey}
|
||||
@@ -211,7 +221,7 @@
|
||||
|
||||
<!-- Custom Instructions -->
|
||||
<div class="mb-4">
|
||||
<label for="instructions" class="block text-sm text-gray-400 mb-1"
|
||||
<label for="instructions" class="block text-sm text-[var(--text-secondary)] mb-1"
|
||||
>Custom Instructions</label
|
||||
>
|
||||
<textarea
|
||||
@@ -238,9 +248,9 @@
|
||||
bind:checked={config.greeting_enabled}
|
||||
class="w-4 h-4 rounded border-[var(--border-color)] bg-[var(--bg-primary)] text-[var(--accent-primary)] focus:ring-[var(--accent-primary)]"
|
||||
/>
|
||||
<span class="text-sm text-gray-300">Send greeting on connect</span>
|
||||
<span class="text-sm text-[var(--text-primary)]">Send greeting on connect</span>
|
||||
</label>
|
||||
<p class="text-xs text-gray-500 mt-1 ml-7">
|
||||
<p class="text-xs text-[var(--text-tertiary)] mt-1 ml-7">
|
||||
Automatically greet you when a session starts with time-based messages
|
||||
</p>
|
||||
</div>
|
||||
@@ -248,8 +258,8 @@
|
||||
<!-- Custom Greeting Prompt -->
|
||||
{#if config.greeting_enabled}
|
||||
<div class="mb-4">
|
||||
<label for="greeting-prompt" class="block text-sm text-gray-400 mb-1">
|
||||
Custom Greeting Prompt <span class="text-gray-600">(optional)</span>
|
||||
<label for="greeting-prompt" class="block text-sm text-[var(--text-secondary)] mb-1">
|
||||
Custom Greeting Prompt <span class="text-[var(--text-tertiary)]">(optional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="greeting-prompt"
|
||||
@@ -268,8 +278,8 @@
|
||||
MCP Servers
|
||||
</h3>
|
||||
<div class="mb-2">
|
||||
<label for="mcp-config" class="block text-sm text-gray-400 mb-1">
|
||||
Server Configuration <span class="text-gray-600">(JSON)</span>
|
||||
<label for="mcp-config" class="block text-sm text-[var(--text-secondary)] mb-1">
|
||||
Server Configuration <span class="text-[var(--text-tertiary)]">(JSON)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="mcp-config"
|
||||
@@ -286,14 +296,14 @@
|
||||
<h3 class="text-sm font-medium text-[var(--accent-primary)] uppercase tracking-wider mb-3">
|
||||
Auto-Granted Tools
|
||||
</h3>
|
||||
<p class="text-xs text-gray-500 mb-3">
|
||||
<p class="text-xs text-[var(--text-tertiary)] mb-3">
|
||||
These tools will be pre-approved for every session (no permission prompts).
|
||||
</p>
|
||||
|
||||
<!-- Common tools checkboxes -->
|
||||
<div class="grid grid-cols-2 gap-2 mb-3">
|
||||
{#each commonTools as tool (tool)}
|
||||
<label class="flex items-center gap-2 text-sm text-gray-300 cursor-pointer">
|
||||
<label class="flex items-center gap-2 text-sm text-[var(--text-primary)] cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.auto_granted_tools.includes(tool)}
|
||||
@@ -309,7 +319,7 @@
|
||||
{#if grantedTools.length > 0}
|
||||
<div class="mb-3">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-xs text-gray-500">Session-granted tools:</span>
|
||||
<span class="text-xs text-[var(--text-tertiary)]">Session-granted tools:</span>
|
||||
<button
|
||||
onclick={importFromSession}
|
||||
class="text-xs text-[var(--accent-primary)] hover:text-[var(--accent-secondary)] transition-colors"
|
||||
@@ -332,7 +342,7 @@
|
||||
<!-- Custom tools list -->
|
||||
{#if config.auto_granted_tools.filter((t) => !commonTools.includes(t)).length > 0}
|
||||
<div class="mb-3">
|
||||
<span class="text-xs text-gray-500 block mb-2">Custom tools:</span>
|
||||
<span class="text-xs text-[var(--text-tertiary)] block mb-2">Custom tools:</span>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#each config.auto_granted_tools.filter((t) => !commonTools.includes(t)) as tool (tool)}
|
||||
<span
|
||||
@@ -341,7 +351,7 @@
|
||||
{tool}
|
||||
<button
|
||||
onclick={() => removeTool(tool)}
|
||||
class="text-gray-500 hover:text-red-400"
|
||||
class="text-[var(--text-tertiary)] hover:text-red-400"
|
||||
aria-label="Remove {tool}"
|
||||
>
|
||||
×
|
||||
@@ -381,7 +391,7 @@
|
||||
onclick={() => handleThemeChange("dark")}
|
||||
class="flex-1 px-4 py-2 rounded-lg border transition-colors {config.theme === 'dark'
|
||||
? 'bg-[var(--accent-primary)] border-[var(--accent-primary)] text-white'
|
||||
: 'bg-[var(--bg-primary)] border-[var(--border-color)] text-gray-400 hover:border-[var(--accent-primary)]'}"
|
||||
: 'bg-[var(--bg-primary)] border-[var(--border-color)] text-[var(--text-secondary)] hover:border-[var(--accent-primary)]'}"
|
||||
>
|
||||
Dark
|
||||
</button>
|
||||
@@ -389,13 +399,36 @@
|
||||
onclick={() => handleThemeChange("light")}
|
||||
class="flex-1 px-4 py-2 rounded-lg border transition-colors {config.theme === 'light'
|
||||
? 'bg-[var(--accent-primary)] border-[var(--accent-primary)] text-white'
|
||||
: 'bg-[var(--bg-primary)] border-[var(--border-color)] text-gray-400 hover:border-[var(--accent-primary)]'}"
|
||||
: 'bg-[var(--bg-primary)] border-[var(--border-color)] text-[var(--text-secondary)] hover:border-[var(--accent-primary)]'}"
|
||||
>
|
||||
Light
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Window Section -->
|
||||
<section class="mb-6">
|
||||
<h3 class="text-sm font-medium text-[var(--accent-primary)] uppercase tracking-wider mb-3">
|
||||
Window
|
||||
</h3>
|
||||
|
||||
<!-- Always on Top Toggle -->
|
||||
<div class="mb-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.always_on_top}
|
||||
onchange={(e) => handleAlwaysOnTopChange(e.currentTarget.checked)}
|
||||
class="w-4 h-4 text-[var(--accent-primary)] bg-[var(--bg-primary)] border-[var(--border-color)] rounded focus:ring-[var(--accent-primary)] focus:ring-2"
|
||||
/>
|
||||
<span class="text-sm text-[var(--text-primary)]">Always on top</span>
|
||||
</label>
|
||||
<p class="text-xs text-[var(--text-tertiary)] mt-1 ml-7">
|
||||
Keep the window above other windows
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Notifications Section -->
|
||||
<section class="mb-6">
|
||||
<h3 class="text-sm font-medium text-[var(--accent-primary)] uppercase tracking-wider mb-3">
|
||||
@@ -410,13 +443,13 @@
|
||||
bind:checked={config.notifications_enabled}
|
||||
class="w-4 h-4 text-[var(--accent-primary)] bg-[var(--bg-primary)] border-[var(--border-color)] rounded focus:ring-[var(--accent-primary)] focus:ring-2"
|
||||
/>
|
||||
<span class="text-sm text-gray-300">Enable sound notifications</span>
|
||||
<span class="text-sm text-[var(--text-primary)]">Enable sound notifications</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Volume Control -->
|
||||
<div class="mb-4">
|
||||
<label for="notification-volume" class="block text-sm text-gray-400 mb-2">
|
||||
<label for="notification-volume" class="block text-sm text-[var(--text-secondary)] mb-2">
|
||||
Notification Volume
|
||||
</label>
|
||||
<div class="flex items-center gap-3">
|
||||
@@ -436,7 +469,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-gray-500">
|
||||
<div class="text-xs text-[var(--text-tertiary)]">
|
||||
Sound notifications will play when I complete tasks, encounter errors, or need permissions.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user