Add terminal code snippets feature #341

Merged
ZacharyZcR merged 4 commits from feature/terminal-snippets into dev-1.8.0 2025-10-04 01:35:22 +00:00
3 changed files with 79 additions and 23 deletions
Showing only changes of commit af58e84ae3 - Show all commits
+11
View File
@@ -242,6 +242,17 @@ async function initializeCompleteDatabase(): Promise<void> {
FOREIGN KEY (user_id) REFERENCES users (id) FOREIGN KEY (user_id) REFERENCES users (id)
); );
CREATE TABLE IF NOT EXISTS snippets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
name TEXT NOT NULL,
content TEXT NOT NULL,
description TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
);
`); `);
migrateSchema(); migrateSchema();
@@ -11,10 +11,15 @@ import {
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Plus, Play, Edit, Trash2, X } from "lucide-react"; import { Plus, Play, Edit, Trash2, X } from "lucide-react";
import axios from "axios";
import { toast } from "sonner"; import { toast } from "sonner";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useConfirmation } from "@/hooks/use-confirmation.ts"; import { useConfirmation } from "@/hooks/use-confirmation.ts";
import {
getSnippets,
createSnippet,
updateSnippet,
deleteSnippet,
} from "@/ui/main-axios";
import type { Snippet, SnippetData } from "../../../../types/index.js"; import type { Snippet, SnippetData } from "../../../../types/index.js";
interface SnippetsSidebarProps { interface SnippetsSidebarProps {
@@ -53,9 +58,9 @@ export function SnippetsSidebar({
const fetchSnippets = async () => { const fetchSnippets = async () => {
try { try {
setLoading(true); setLoading(true);
const response = await axios.get("/snippets"); const data = await getSnippets();
// Defensive: ensure response.data is an array // Defensive: ensure data is an array
setSnippets(Array.isArray(response.data) ? response.data : []); setSnippets(Array.isArray(data) ? data : []);
} catch (err) { } catch (err) {
toast.error(t("snippets.failedToFetch")); toast.error(t("snippets.failedToFetch"));
setSnippets([]); setSnippets([]);
@@ -82,23 +87,20 @@ export function SnippetsSidebar({
setShowDialog(true); setShowDialog(true);
}; };
const handleDelete = async (snippet: Snippet) => { const handleDelete = (snippet: Snippet) => {
const confirmed = await confirmWithToast({ confirmWithToast(
title: t("snippets.deleteConfirmTitle"), t("snippets.deleteConfirmDescription", { name: snippet.name }),
description: t("snippets.deleteConfirmDescription", { async () => {
name: snippet.name,
}),
});
if (confirmed) {
try { try {
await axios.delete(`/snippets/${snippet.id}`); await deleteSnippet(snippet.id);
toast.success(t("snippets.deleteSuccess")); toast.success(t("snippets.deleteSuccess"));
fetchSnippets(); fetchSnippets();
} catch (err) { } catch (err) {
toast.error(t("snippets.deleteFailed")); toast.error(t("snippets.deleteFailed"));
} }
} },
"destructive",
);
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
@@ -116,10 +118,10 @@ export function SnippetsSidebar({
try { try {
if (editingSnippet) { if (editingSnippet) {
await axios.put(`/snippets/${editingSnippet.id}`, formData); await updateSnippet(editingSnippet.id, formData);
toast.success(t("snippets.updateSuccess")); toast.success(t("snippets.updateSuccess"));
} else { } else {
await axios.post("/snippets", formData); await createSnippet(formData);
toast.success(t("snippets.createSuccess")); toast.success(t("snippets.createSuccess"));
} }
setShowDialog(false); setShowDialog(false);
+43
View File
@@ -2155,3 +2155,46 @@ export async function deployCredentialToHost(
throw handleApiError(error, "deploy credential to host"); throw handleApiError(error, "deploy credential to host");
} }
} }
// ============================================================================
// SNIPPETS API
// ============================================================================
export async function getSnippets(): Promise<any> {
try {
const response = await authApi.get("/snippets");
return response.data;
} catch (error) {
throw handleApiError(error, "fetch snippets");
}
}
export async function createSnippet(snippetData: any): Promise<any> {
try {
const response = await authApi.post("/snippets", snippetData);
return response.data;
} catch (error) {
throw handleApiError(error, "create snippet");
}
}
export async function updateSnippet(
snippetId: number,
snippetData: any,
): Promise<any> {
try {
const response = await authApi.put(`/snippets/${snippetId}`, snippetData);
return response.data;
} catch (error) {
throw handleApiError(error, "update snippet");
}
}
export async function deleteSnippet(snippetId: number): Promise<any> {
try {
const response = await authApi.delete(`/snippets/${snippetId}`);
return response.data;
} catch (error) {
throw handleApiError(error, "delete snippet");
}
}