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:
@@ -0,0 +1,138 @@
|
||||
import { writable, derived } from "svelte/store";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export interface Snippet {
|
||||
id: string;
|
||||
name: string;
|
||||
content: string;
|
||||
category: string;
|
||||
is_default: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
function createSnippetsStore() {
|
||||
const snippets = writable<Snippet[]>([]);
|
||||
const categories = writable<string[]>([]);
|
||||
const isLoading = writable(false);
|
||||
const selectedCategory = writable<string | null>(null);
|
||||
|
||||
const filteredSnippets = derived(
|
||||
[snippets, selectedCategory],
|
||||
([$snippets, $selectedCategory]) => {
|
||||
if (!$selectedCategory) {
|
||||
return $snippets;
|
||||
}
|
||||
return $snippets.filter((s) => s.category === $selectedCategory);
|
||||
}
|
||||
);
|
||||
|
||||
async function loadSnippets(): Promise<void> {
|
||||
isLoading.set(true);
|
||||
try {
|
||||
const [snippetList, categoryList] = await Promise.all([
|
||||
invoke<Snippet[]>("list_snippets"),
|
||||
invoke<string[]>("get_snippet_categories"),
|
||||
]);
|
||||
snippets.set(snippetList);
|
||||
categories.set(categoryList);
|
||||
} catch (error) {
|
||||
console.error("Failed to load snippets:", error);
|
||||
} finally {
|
||||
isLoading.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSnippet(snippet: Snippet): Promise<boolean> {
|
||||
try {
|
||||
await invoke("save_snippet", { snippet });
|
||||
await loadSnippets();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Failed to save snippet:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function createSnippet(name: string, content: string, category: string): Promise<boolean> {
|
||||
const now = new Date().toISOString();
|
||||
const snippet: Snippet = {
|
||||
id: `custom-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
|
||||
name,
|
||||
content,
|
||||
category,
|
||||
is_default: false,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
};
|
||||
return saveSnippet(snippet);
|
||||
}
|
||||
|
||||
async function updateSnippet(
|
||||
id: string,
|
||||
name: string,
|
||||
content: string,
|
||||
category: string
|
||||
): Promise<boolean> {
|
||||
const currentSnippets = await invoke<Snippet[]>("list_snippets");
|
||||
const existing = currentSnippets.find((s) => s.id === id);
|
||||
|
||||
if (!existing) {
|
||||
console.error("Snippet not found for update");
|
||||
return false;
|
||||
}
|
||||
|
||||
const updated: Snippet = {
|
||||
...existing,
|
||||
name,
|
||||
content,
|
||||
category,
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
return saveSnippet(updated);
|
||||
}
|
||||
|
||||
async function deleteSnippet(snippetId: string): Promise<boolean> {
|
||||
try {
|
||||
await invoke("delete_snippet", { snippetId });
|
||||
await loadSnippets();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Failed to delete snippet:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function resetDefaults(): Promise<boolean> {
|
||||
try {
|
||||
await invoke("reset_default_snippets");
|
||||
await loadSnippets();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Failed to reset default snippets:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setSelectedCategory(category: string | null): void {
|
||||
selectedCategory.set(category);
|
||||
}
|
||||
|
||||
return {
|
||||
snippets: { subscribe: snippets.subscribe },
|
||||
categories: { subscribe: categories.subscribe },
|
||||
filteredSnippets: { subscribe: filteredSnippets.subscribe },
|
||||
isLoading: { subscribe: isLoading.subscribe },
|
||||
selectedCategory: { subscribe: selectedCategory.subscribe },
|
||||
loadSnippets,
|
||||
saveSnippet,
|
||||
createSnippet,
|
||||
updateSnippet,
|
||||
deleteSnippet,
|
||||
resetDefaults,
|
||||
setSelectedCategory,
|
||||
};
|
||||
}
|
||||
|
||||
export const snippetsStore = createSnippetsStore();
|
||||
Reference in New Issue
Block a user