generated from nhcarrigan/template
feat: another wave of features (#61)
## Explanation This PR bundles several user-facing improvements and feature additions for the v0.3.0 release, including quality-of-life improvements to the UI, new slash commands, better state persistence, and auto-update checking. ## Included Changes - **Resizable chat input** with drag handle (#58 partial) - **Arrow key navigation fix** - cursor keys now navigate text when user has typed input (#58) - **Scroll position persistence** per conversation tab - **/skill command** for invoking Claude Code skills (#57) - **Stats persistence fix** - stats now persist across session changes, only reset on disconnect (#59) - **Auto-update checker** on startup (#17) - **Resizable character panel** with full-height sprites (#10) - **Font size and zoom settings** with keyboard shortcuts (Ctrl++/Ctrl+-/Ctrl+0) (#19) ## Closes Closes #10, #17, #19, #57, #58, #59 ## Attestations - [x] I have read and agree to the Code of Conduct - [x] I have read and agree to the Community Guidelines - [x] My contribution complies with the Contributor Covenant - [x] I have run the linter and resolved any errors - [x] My pull request uses an appropriate title, matching the conventional commit standards - [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request - [x] All new and existing tests pass locally with my changes - [x] Code coverage remains at or above the configured threshold ## Documentation N/A - Internal app features ## Versioning Minor - My pull request introduces new non-breaking features. --- ✨ This PR was created with help from Hikari~ 🌸 Co-authored-by: Hikari <hikari@nhcarrigan.com> Reviewed-on: #61 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #61.
This commit is contained in:
+81
-4
@@ -3,7 +3,7 @@
|
||||
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 { configStore, applyTheme, applyFontSize } from "$lib/stores/config";
|
||||
import { initNotificationSync, cleanupNotificationSync } from "$lib/stores/notifications";
|
||||
import { conversationsStore } from "$lib/stores/conversations";
|
||||
import { claudeStore, isClaudeProcessing } from "$lib/stores/claude";
|
||||
@@ -18,10 +18,41 @@
|
||||
import ConfigSidebar from "$lib/components/ConfigSidebar.svelte";
|
||||
import AchievementNotification from "$lib/components/AchievementNotification.svelte";
|
||||
import AchievementsPanel from "$lib/components/AchievementsPanel.svelte";
|
||||
import UpdateNotification from "$lib/components/UpdateNotification.svelte";
|
||||
|
||||
let initialized = false;
|
||||
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)
|
||||
@@ -69,6 +100,27 @@
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl++ or Ctrl+= - Increase font size
|
||||
if (event.ctrlKey && (event.key === "+" || event.key === "=")) {
|
||||
event.preventDefault();
|
||||
configStore.increaseFontSize();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+- - Decrease font size
|
||||
if (event.ctrlKey && event.key === "-") {
|
||||
event.preventDefault();
|
||||
configStore.decreaseFontSize();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl+0 - Reset font size
|
||||
if (event.ctrlKey && event.key === "0") {
|
||||
event.preventDefault();
|
||||
configStore.resetFontSize();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleInterrupt() {
|
||||
@@ -96,6 +148,7 @@
|
||||
// Apply saved settings on startup
|
||||
const config = configStore.getConfig();
|
||||
applyTheme(config.theme);
|
||||
applyFontSize(config.font_size);
|
||||
|
||||
// Apply always-on-top setting
|
||||
if (config.always_on_top) {
|
||||
@@ -103,11 +156,21 @@
|
||||
await window.setAlwaysOnTop(true);
|
||||
}
|
||||
|
||||
// Load saved panel width
|
||||
if (config.character_panel_width) {
|
||||
panelWidth = config.character_panel_width;
|
||||
}
|
||||
|
||||
// Initialize notification settings sync
|
||||
initNotificationSync();
|
||||
|
||||
// Add global keyboard shortcut listener
|
||||
window.addEventListener("keydown", handleGlobalKeydown);
|
||||
|
||||
// Check for updates on startup
|
||||
if (config.update_checks_enabled) {
|
||||
updateNotification?.checkForUpdates();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -127,13 +190,22 @@
|
||||
<main class="flex-1 flex overflow-hidden">
|
||||
<!-- Left panel: Character display -->
|
||||
<div
|
||||
class="character-panel w-1/3 flex flex-col items-center justify-center border-r border-[var(--border-color)] bg-[var(--bg-secondary)]/50"
|
||||
class="character-panel flex flex-col items-center justify-center bg-[var(--bg-secondary)]/50"
|
||||
style="width: {panelWidth}px; min-width: {MIN_PANEL_WIDTH}px; max-width: {MAX_PANEL_WIDTH}px;"
|
||||
>
|
||||
<AnimeGirl />
|
||||
</div>
|
||||
|
||||
<!-- Resize handle -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="resize-handle w-1 cursor-col-resize bg-[var(--border-color)] hover:bg-[var(--accent-primary)] transition-colors flex-shrink-0"
|
||||
class:bg-[var(--accent-primary)]={isResizing}
|
||||
onmousedown={startResize}
|
||||
></div>
|
||||
|
||||
<!-- Right panel: Terminal and input -->
|
||||
<div class="terminal-panel flex-1 flex flex-col">
|
||||
<div class="terminal-panel flex-1 flex flex-col min-w-0">
|
||||
<Terminal />
|
||||
<InputBar />
|
||||
</div>
|
||||
@@ -147,6 +219,7 @@
|
||||
bind:isOpen={achievementPanelOpen}
|
||||
onClose={() => (achievementPanelOpen = false)}
|
||||
/>
|
||||
<UpdateNotification bind:this={updateNotification} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -161,7 +234,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;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user