From 13c96a973a599c09f61708fbd961cc36cec361a3 Mon Sep 17 00:00:00 2001 From: Hikari Date: Fri, 23 Jan 2026 18:05:55 -0800 Subject: [PATCH] feat: resizable character panel and full-height sprite - Change sprite to use full height instead of full width for better scaling - Add draggable divider between character panel and terminal - Persist panel width preference in config (min: 200px, max: 600px) Closes #10 --- src-tauri/src/config.rs | 6 +++ src/lib/components/AnimeGirl.svelte | 26 +++++++----- src/lib/components/ConfigSidebar.svelte | 1 + src/lib/components/StatusBar.svelte | 1 + src/lib/stores/config.ts | 2 + src/routes/+page.svelte | 53 +++++++++++++++++++++++-- 6 files changed, 75 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index baac7d5..dd2714c 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -64,6 +64,9 @@ pub struct HikariConfig { #[serde(default = "default_update_checks_enabled")] pub update_checks_enabled: bool, + + #[serde(default)] + pub character_panel_width: Option, } impl Default for HikariConfig { @@ -81,6 +84,7 @@ impl Default for HikariConfig { notification_volume: 0.7, always_on_top: false, update_checks_enabled: true, + character_panel_width: None, } } } @@ -126,6 +130,7 @@ mod tests { assert!(config.greeting_custom_prompt.is_none()); assert!(!config.always_on_top); assert!(config.update_checks_enabled); + assert!(config.character_panel_width.is_none()); } #[test] @@ -143,6 +148,7 @@ mod tests { notification_volume: 0.7, always_on_top: true, update_checks_enabled: true, + character_panel_width: Some(400), }; let json = serde_json::to_string(&config).unwrap(); diff --git a/src/lib/components/AnimeGirl.svelte b/src/lib/components/AnimeGirl.svelte index 129b03d..58c0c50 100644 --- a/src/lib/components/AnimeGirl.svelte +++ b/src/lib/components/AnimeGirl.svelte @@ -57,30 +57,34 @@ } -
-
-
+
+
+
Hikari - {info.label} { const target = e.currentTarget as HTMLImageElement; target.src = "/sprites/placeholder.svg"; }} />
+
-
-
- {info.label} -
+
+
+ {info.label}
-
+
diff --git a/src/lib/components/ConfigSidebar.svelte b/src/lib/components/ConfigSidebar.svelte index e831a9d..7c242b9 100644 --- a/src/lib/components/ConfigSidebar.svelte +++ b/src/lib/components/ConfigSidebar.svelte @@ -16,6 +16,7 @@ notification_volume: 0.7, always_on_top: false, update_checks_enabled: true, + character_panel_width: null, }); let isOpen = $state(false); diff --git a/src/lib/components/StatusBar.svelte b/src/lib/components/StatusBar.svelte index ab20ff0..76d7b31 100644 --- a/src/lib/components/StatusBar.svelte +++ b/src/lib/components/StatusBar.svelte @@ -47,6 +47,7 @@ notification_volume: 0.5, always_on_top: false, update_checks_enabled: true, + character_panel_width: null, }); onMount(async () => { diff --git a/src/lib/stores/config.ts b/src/lib/stores/config.ts index 446c8ee..f6ec468 100644 --- a/src/lib/stores/config.ts +++ b/src/lib/stores/config.ts @@ -16,6 +16,7 @@ export interface HikariConfig { notification_volume: number; always_on_top: boolean; update_checks_enabled: boolean; + character_panel_width: number | null; } const defaultConfig: HikariConfig = { @@ -31,6 +32,7 @@ const defaultConfig: HikariConfig = { notification_volume: 0.7, always_on_top: false, update_checks_enabled: true, + character_panel_width: null, }; function createConfigStore() { diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e0ba4ef..bb7ca25 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -24,6 +24,35 @@ let updateNotification: UpdateNotification; let achievementPanelOpen = $state(false); + // Resizable panel state + let panelWidth = $state(320); // Default width in pixels + let isResizing = $state(false); + const MIN_PANEL_WIDTH = 200; + const MAX_PANEL_WIDTH = 600; + + function startResize(event: MouseEvent) { + isResizing = true; + event.preventDefault(); + document.addEventListener("mousemove", handleResize); + document.addEventListener("mouseup", stopResize); + } + + function handleResize(event: MouseEvent) { + if (!isResizing) return; + const newWidth = event.clientX; + panelWidth = Math.max(MIN_PANEL_WIDTH, Math.min(MAX_PANEL_WIDTH, newWidth)); + } + + function stopResize() { + if (isResizing) { + isResizing = false; + document.removeEventListener("mousemove", handleResize); + document.removeEventListener("mouseup", stopResize); + // Save the panel width to config + configStore.updateConfig({ character_panel_width: panelWidth }); + } + } + // Global keyboard shortcuts function handleGlobalKeydown(event: KeyboardEvent) { // Don't trigger shortcuts when typing in inputs (except for specific ones) @@ -105,6 +134,11 @@ await window.setAlwaysOnTop(true); } + // Load saved panel width + if (config.character_panel_width) { + panelWidth = config.character_panel_width; + } + // Initialize notification settings sync initNotificationSync(); @@ -134,13 +168,22 @@
+ + +
+ -
+
@@ -169,7 +212,11 @@ } .character-panel { - min-width: 320px; background: linear-gradient(180deg, var(--bg-secondary) 0%, var(--bg-primary) 100%); } + + .resize-handle:hover, + .resize-handle:active { + width: 4px; + }