generated from nhcarrigan/template
feat: add multiple productivity features and UI enhancements (#68)
## 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>
This commit was merged in pull request #68.
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
onToggleAchievements?: () => void;
|
||||
onToggleCompact?: () => void;
|
||||
}
|
||||
|
||||
const { onToggleAchievements = () => {} }: Props = $props();
|
||||
const { onToggleAchievements = () => {}, onToggleCompact = () => {} }: Props = $props();
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
@@ -11,7 +12,7 @@
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { get } from "svelte/store";
|
||||
import { claudeStore } from "$lib/stores/claude";
|
||||
import { configStore, type HikariConfig } from "$lib/stores/config";
|
||||
import { configStore, type HikariConfig, isStreamerMode } from "$lib/stores/config";
|
||||
import type { ConnectionStatus } from "$lib/types/messages";
|
||||
import { onMount } from "svelte";
|
||||
import StatsDisplay from "./StatsDisplay.svelte";
|
||||
@@ -19,6 +20,9 @@
|
||||
import HelpPanel from "./HelpPanel.svelte";
|
||||
import KeyboardShortcutsModal from "./KeyboardShortcutsModal.svelte";
|
||||
import { achievementProgress } from "$lib/stores/achievements";
|
||||
import SessionHistoryPanel from "./SessionHistoryPanel.svelte";
|
||||
import GitPanel from "./GitPanel.svelte";
|
||||
import ProfilePanel from "./ProfilePanel.svelte";
|
||||
|
||||
const DISCORD_URL = "https://chat.nhcarrigan.com";
|
||||
const DONATE_URL = "https://donate.nhcarrigan.com";
|
||||
@@ -33,6 +37,9 @@
|
||||
let showAbout = $state(false);
|
||||
let showHelp = $state(false);
|
||||
let showKeyboardShortcuts = $state(false);
|
||||
let showSessionHistory = $state(false);
|
||||
let showGitPanel = $state(false);
|
||||
let showProfile = $state(false);
|
||||
const progress = $derived($achievementProgress);
|
||||
let currentConfig: HikariConfig = $state({
|
||||
model: null,
|
||||
@@ -49,6 +56,28 @@
|
||||
update_checks_enabled: true,
|
||||
character_panel_width: null,
|
||||
font_size: 14,
|
||||
minimize_to_tray: false,
|
||||
streamer_mode: false,
|
||||
streamer_hide_paths: false,
|
||||
compact_mode: false,
|
||||
profile_name: null,
|
||||
profile_avatar_path: null,
|
||||
profile_bio: null,
|
||||
custom_theme_colors: {
|
||||
bg_primary: null,
|
||||
bg_secondary: null,
|
||||
bg_terminal: null,
|
||||
accent_primary: null,
|
||||
accent_secondary: null,
|
||||
text_primary: null,
|
||||
text_secondary: null,
|
||||
border_color: null,
|
||||
},
|
||||
});
|
||||
|
||||
let streamerModeActive = $state(false);
|
||||
isStreamerMode.subscribe((value) => {
|
||||
streamerModeActive = value;
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
@@ -202,9 +231,43 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
{#if streamerModeActive}
|
||||
<div
|
||||
class="w-2.5 h-2.5 rounded-full bg-red-500 animate-pulse"
|
||||
title="Streamer mode active (Ctrl+Shift+S to toggle)"
|
||||
></div>
|
||||
{/if}
|
||||
<button
|
||||
onclick={() => (showProfile = true)}
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Profile"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onclick={onToggleCompact}
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Compact Mode (Ctrl+Shift+M)"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onclick={toggleAchievements}
|
||||
class="p-1 text-gray-500 hover:text-[var(--accent-primary)] transition-colors relative"
|
||||
class="p-1 text-gray-500 icon-trans-hover relative"
|
||||
title="Achievements"
|
||||
>
|
||||
<span class="text-lg">🏆</span>
|
||||
@@ -216,11 +279,37 @@
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
<button
|
||||
onclick={() => (showSessionHistory = true)}
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Session History"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onclick={() => (showGitPanel = true)}
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Git Panel"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onclick={() => (showStats = !showStats)}
|
||||
class="p-1 text-gray-500 hover:text-[var(--accent-primary)] transition-colors {showStats
|
||||
? 'text-[var(--accent-primary)]'
|
||||
: ''}"
|
||||
class="p-1 text-gray-500 icon-trans-hover {showStats ? 'text-[var(--trans-pink)]' : ''}"
|
||||
title="Usage Stats"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -234,7 +323,7 @@
|
||||
</button>
|
||||
<button
|
||||
onclick={configStore.openSidebar}
|
||||
class="p-1 text-gray-500 hover:text-[var(--accent-primary)] transition-colors"
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Settings"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -254,7 +343,7 @@
|
||||
</button>
|
||||
<button
|
||||
onclick={() => openUrl(DONATE_URL)}
|
||||
class="p-1 text-gray-500 hover:text-[var(--accent-primary)] transition-colors"
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Support our work"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
@@ -265,7 +354,7 @@
|
||||
</button>
|
||||
<button
|
||||
onclick={() => (showAbout = true)}
|
||||
class="p-1 text-gray-500 hover:text-[var(--accent-primary)] transition-colors"
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="About Hikari Desktop"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -279,7 +368,7 @@
|
||||
</button>
|
||||
<button
|
||||
onclick={() => (showKeyboardShortcuts = true)}
|
||||
class="p-1 text-gray-500 hover:text-[var(--accent-primary)] transition-colors"
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Keyboard Shortcuts"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -299,7 +388,7 @@
|
||||
</button>
|
||||
<button
|
||||
onclick={() => (showHelp = true)}
|
||||
class="p-1 text-gray-500 hover:text-[var(--accent-primary)] transition-colors"
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Help"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -313,7 +402,7 @@
|
||||
</button>
|
||||
<button
|
||||
onclick={() => openUrl(DISCORD_URL)}
|
||||
class="p-1 text-gray-500 hover:text-[var(--accent-primary)] transition-colors"
|
||||
class="p-1 text-gray-500 icon-trans-hover"
|
||||
title="Join our Discord"
|
||||
>
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
|
||||
@@ -370,3 +459,15 @@
|
||||
{#if showKeyboardShortcuts}
|
||||
<KeyboardShortcutsModal onClose={() => (showKeyboardShortcuts = false)} />
|
||||
{/if}
|
||||
|
||||
{#if showSessionHistory}
|
||||
<SessionHistoryPanel onClose={() => (showSessionHistory = false)} />
|
||||
{/if}
|
||||
|
||||
{#if showGitPanel}
|
||||
<GitPanel isOpen={showGitPanel} onClose={() => (showGitPanel = false)} />
|
||||
{/if}
|
||||
|
||||
{#if showProfile}
|
||||
<ProfilePanel onClose={() => (showProfile = false)} />
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user