generated from nhcarrigan/template
4c46d4c8fd
## Summary This PR adds a collection of productivity features and UI enhancements to improve the Hikari Desktop experience: ### New Features - **Clipboard History** (#25) - Track and manage copied code snippets with language detection, search, filtering, and pinning - **Quick Actions Panel** (#15) - Buttons for common quick actions like "Review PR", "Run tests", "Explain file", with customizable actions - **Git Integration Panel** (#24) - View current branch, changed/staged files, quick git actions (commit, push, pull), and branch management - **Session Import/Export** (#8) - Export conversations to JSON and import previously saved sessions - **Snippet Library** (#22) - Save and reuse common prompts with categories and quick insert - **Session History** (#14) - Auto-save conversations with browsable history and search - **High Contrast Mode** (#20) - Accessibility theme with improved visibility - **Minimize to System Tray** (#11) - System tray support with right-click menu ### UI Enhancements - Trans-pride gradient theme applied across UI elements - Copy button added to code blocks - Linter formatting and eslint-disable comments for cleaner code ## Closes Closes #8 Closes #11 Closes #14 Closes #15 Closes #20 Closes #22 Closes #24 Closes #25 Closes #34 Closes #35 Closes #36 Closes #37 Closes #69 Closes #70 ## Test Plan - [ ] Verify clipboard history captures code from code block copy buttons - [ ] Verify clipboard history captures manually selected text from terminal - [ ] Test snippet library CRUD operations and insertion - [ ] Test quick actions panel with default and custom actions - [ ] Test git panel shows correct status, branch, and performs git operations - [ ] Test session history auto-save and restore - [ ] Test session import/export roundtrip - [ ] Verify high contrast mode provides adequate contrast - [ ] Test minimize to tray functionality and tray menu - [ ] Verify trans-pride gradient theme displays correctly in all themes --- *✨ This PR was created with help from Hikari~ 🌸* Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Reviewed-on: #68 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
188 lines
3.9 KiB
Svelte
188 lines
3.9 KiB
Svelte
<script lang="ts">
|
|
import { characterState, characterInfo } from "$lib/stores/character";
|
|
import type { CharacterState, CharacterStateInfo } from "$lib/types/states";
|
|
|
|
let currentState: CharacterState = $state("idle");
|
|
let info: CharacterStateInfo = $state({
|
|
state: "idle",
|
|
label: "Ready",
|
|
description: "Waiting for your command~",
|
|
spriteFile: "idle.png",
|
|
});
|
|
|
|
characterState.subscribe((state) => {
|
|
currentState = state;
|
|
});
|
|
|
|
characterInfo.subscribe((i) => {
|
|
info = i;
|
|
});
|
|
|
|
function getAnimationClass(): string {
|
|
switch (currentState) {
|
|
case "thinking":
|
|
return "animate-thinking";
|
|
case "typing":
|
|
return "animate-typing";
|
|
case "searching":
|
|
return "animate-searching";
|
|
case "success":
|
|
return "animate-celebrate";
|
|
case "error":
|
|
return "animate-shake";
|
|
default:
|
|
return "animate-idle";
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div
|
|
class="anime-girl-container flex flex-col items-center justify-between h-full p-4 overflow-hidden"
|
|
>
|
|
<div class="character-frame relative flex-1 flex items-center justify-center min-h-0">
|
|
<div class="sprite-container {getAnimationClass()} h-full flex items-center justify-center">
|
|
<img
|
|
src="/sprites/{info.spriteFile}"
|
|
alt="Hikari - {info.label}"
|
|
class="character-sprite h-full w-auto max-w-full object-contain"
|
|
onerror={(e) => {
|
|
const target = e.currentTarget as HTMLImageElement;
|
|
target.src = "/sprites/placeholder.svg";
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="state-indicator mt-2">
|
|
<div
|
|
class="px-3 py-1 rounded-full text-xs font-medium bg-[var(--bg-secondary)] border border-[var(--border-color)] text-[var(--accent-primary)]"
|
|
>
|
|
{info.label}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="speech-bubble mt-2 max-w-xs flex-shrink-0">
|
|
<div
|
|
class="relative bg-[var(--bg-secondary)] rounded-lg px-4 py-2 border border-[var(--border-color)]"
|
|
>
|
|
<div
|
|
class="absolute -top-2 left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-8 border-r-8 border-b-8 border-transparent border-b-[var(--bg-secondary)]"
|
|
></div>
|
|
<p class="text-sm text-gray-300 text-center italic">{info.description}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.anime-girl-container {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.character-frame {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
@keyframes idle-bob {
|
|
0%,
|
|
100% {
|
|
transform: translateY(0);
|
|
}
|
|
50% {
|
|
transform: translateY(-5px);
|
|
}
|
|
}
|
|
|
|
@keyframes thinking-sway {
|
|
0%,
|
|
100% {
|
|
transform: rotate(-2deg);
|
|
}
|
|
50% {
|
|
transform: rotate(2deg);
|
|
}
|
|
}
|
|
|
|
@keyframes typing-bounce {
|
|
0%,
|
|
100% {
|
|
transform: translateY(0) scale(1);
|
|
}
|
|
50% {
|
|
transform: translateY(-3px) scale(1.02);
|
|
}
|
|
}
|
|
|
|
@keyframes searching-look {
|
|
0%,
|
|
100% {
|
|
transform: translateX(0);
|
|
}
|
|
25% {
|
|
transform: translateX(-5px);
|
|
}
|
|
75% {
|
|
transform: translateX(5px);
|
|
}
|
|
}
|
|
|
|
@keyframes celebrate {
|
|
0%,
|
|
100% {
|
|
transform: scale(1) rotate(0deg);
|
|
}
|
|
25% {
|
|
transform: scale(1.1) rotate(-5deg);
|
|
}
|
|
50% {
|
|
transform: scale(1.1) rotate(5deg);
|
|
}
|
|
75% {
|
|
transform: scale(1.05) rotate(-3deg);
|
|
}
|
|
}
|
|
|
|
@keyframes shake {
|
|
0%,
|
|
100% {
|
|
transform: translateX(0);
|
|
}
|
|
10%,
|
|
30%,
|
|
50%,
|
|
70%,
|
|
90% {
|
|
transform: translateX(-5px);
|
|
}
|
|
20%,
|
|
40%,
|
|
60%,
|
|
80% {
|
|
transform: translateX(5px);
|
|
}
|
|
}
|
|
|
|
.animate-idle {
|
|
animation: idle-bob 3s ease-in-out infinite;
|
|
}
|
|
|
|
.animate-thinking {
|
|
animation: thinking-sway 2s ease-in-out infinite;
|
|
}
|
|
|
|
.animate-typing {
|
|
animation: typing-bounce 0.5s ease-in-out infinite;
|
|
}
|
|
|
|
.animate-searching {
|
|
animation: searching-look 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
.animate-celebrate {
|
|
animation: celebrate 0.8s ease-in-out;
|
|
}
|
|
|
|
.animate-shake {
|
|
animation: shake 0.5s ease-in-out;
|
|
}
|
|
</style>
|