admin08077/Githubgemini
0
1{"version":3,"file":"SnippetVault-Bod85v6Z.js","sources":["../../components/features/SnippetVault.tsx"],"sourcesContent":["import React, { useState, useEffect, useMemo } from 'react';\nimport { LockClosedIcon, SparklesIcon, TrashIcon, ClipboardDocumentIcon, ArrowDownTrayIcon } from '../icons.tsx';\nimport { useLocalStorage } from '../../hooks/useLocalStorage.ts';\nimport { enhanceSnippetStream } from '../../services/geminiService.ts';\nimport { LoadingSpinner } from '../shared/LoadingSpinner.tsx';\nimport { downloadFile } from '../../services/fileUtils.ts';\n\ninterface Snippet {\n id: number; name: string; code: string; language: string; tags: string[];\n}\n\nconst langToExt: Record<string, string> = {\n javascript: 'js',\n typescript: 'ts',\n python: 'py',\n css: 'css',\n html: 'html',\n json: 'json',\n markdown: 'md',\n plaintext: 'txt',\n};\n\nexport const SnippetVault: React.FC = () => {\n const [snippets, setSnippets] = useLocalStorage<Snippet[]>('devcore_snippets', [{ id: 1, name: 'React Hook Boilerplate', language: 'javascript', code: `import { useState } from 'react';\\n\\nconst useCustomHook = () => {\\n const [value, setValue] = useState(null);\\n return { value, setValue };\\n};`, tags: ['react', 'hook'] }]);\n const [activeSnippet, setActiveSnippet] = useState<Snippet | null>(null);\n const [isEnhancing, setIsEnhancing] = useState(false);\n const [searchTerm, setSearchTerm] = useState('');\n const [isEditingName, setIsEditingName] = useState(false);\n\n const filteredSnippets = useMemo(() => {\n if (!searchTerm) return snippets;\n const lowerSearch = searchTerm.toLowerCase();\n return snippets.filter((s: Snippet) => \n s.name.toLowerCase().includes(lowerSearch) || \n s.code.toLowerCase().includes(lowerSearch) ||\n (s.tags && s.tags.some(t => t.toLowerCase().includes(lowerSearch)))\n );\n }, [snippets, searchTerm]);\n\n useEffect(() => {\n if (!activeSnippet && filteredSnippets.length > 0) setActiveSnippet(filteredSnippets[0]);\n if (activeSnippet) setActiveSnippet(snippets.find((s: Snippet) => s.id === activeSnippet.id) || null);\n }, [snippets, activeSnippet, filteredSnippets]);\n\n const updateSnippet = (snippet: Snippet) => {\n setSnippets(snippets.map((s: Snippet) => s.id === snippet.id ? snippet : s));\n setActiveSnippet(snippet);\n };\n\n const handleEnhance = async () => {\n if (!activeSnippet) return;\n setIsEnhancing(true);\n try {\n const stream = enhanceSnippetStream(activeSnippet.code);\n let fullResponse = '';\n for await (const chunk of stream) {\n fullResponse += chunk;\n updateSnippet({ ...activeSnippet, code: fullResponse.replace(/^```(?:\\w+\\n)?/, '').replace(/```$/, '') });\n }\n } finally { setIsEnhancing(false); }\n };\n\n const handleAddNew = () => {\n const newSnippet: Snippet = { id: Date.now(), name: 'New Snippet', language: 'plaintext', code: '', tags: [] };\n setSnippets([...snippets, newSnippet]);\n setActiveSnippet(newSnippet);\n };\n \n const handleDelete = (id: number) => {\n setSnippets(snippets.filter((s: Snippet) => s.id !== id));\n if(activeSnippet?.id === id) setActiveSnippet(filteredSnippets.length > 1 ? filteredSnippets[0] : null);\n };\n \n const handleDownload = () => {\n if(!activeSnippet) return;\n const extension = langToExt[activeSnippet.language] || 'txt';\n const filename = `${activeSnippet.name.replace(/\\s/g, '_')}.${extension}`;\n downloadFile(activeSnippet.code, filename);\n }\n\n const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (activeSnippet) updateSnippet({...activeSnippet, name: e.target.value});\n };\n \n const handleTagsChange = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter' && activeSnippet) {\n const newTag = e.currentTarget.value.trim();\n if (newTag && !activeSnippet.tags.includes(newTag)) {\n updateSnippet({...activeSnippet, tags: [...(activeSnippet.tags ?? []), newTag]});\n }\n e.currentTarget.value = '';\n }\n };\n\n return (\n <div className=\"h-full flex flex-col p-4 sm:p-6 lg:p-8 text-text-primary\">\n <header className=\"mb-6\"><h1 className=\"text-3xl font-bold flex items-center\"><LockClosedIcon /><span className=\"ml-3\">Snippet Vault</span></h1><p className=\"text-text-secondary mt-1\">Store, search, tag, and enhance your reusable code snippets with AI.</p></header>\n <div className=\"flex-grow flex gap-6 min-h-0\">\n <aside className=\"w-1/3 bg-surface border border-border p-4 rounded-lg flex flex-col\">\n <input type=\"text\" placeholder=\"Search snippets...\" value={searchTerm} onChange={e => setSearchTerm(e.target.value)} className=\"w-full px-3 py-1.5 mb-3 rounded-md bg-background border border-border text-sm\"/>\n <ul className=\"space-y-2 flex-grow overflow-y-auto pr-2\">{filteredSnippets.map((s: Snippet) => (<li key={s.id} className=\"group flex items-center justify-between\"><button onClick={() => setActiveSnippet(s)} className={`w-full text-left px-3 py-2 rounded-md ${activeSnippet?.id === s.id ? 'bg-primary/10 text-primary' : 'hover:bg-gray-100'}`}>{s.name}</button><div className=\"flex opacity-0 group-hover:opacity-100 transition-opacity\"><button onClick={() => navigator.clipboard.writeText(s.code)} className=\"ml-2 p-1 text-text-secondary hover:text-primary\" title=\"Copy\"><ClipboardDocumentIcon /></button><button onClick={() => handleDelete(s.id)} className=\"ml-2 p-1 text-text-secondary hover:text-red-500\" title=\"Delete\"><TrashIcon/></button></div></li>))}</ul>\n <div className=\"mt-4 pt-4 border-t border-border\"><button onClick={handleAddNew} className=\"btn-primary w-full text-sm py-2\">Add New Snippet</button></div>\n </aside>\n <main className=\"w-2/3 flex flex-col\">\n {activeSnippet ? (<>\n <div className=\"flex justify-between items-center mb-2\">\n {isEditingName ? <input type=\"text\" value={activeSnippet.name} onChange={handleNameChange} onBlur={() => setIsEditingName(false)} autoFocus className=\"text-lg font-bold bg-gray-100 rounded px-2\"/> : <h3 onDoubleClick={() => setIsEditingName(true)} className=\"text-lg font-bold cursor-pointer\">{activeSnippet.name}</h3>}\n <div className=\"flex gap-2\">\n <button onClick={handleEnhance} disabled={isEnhancing} className=\"flex items-center gap-2 px-3 py-1 bg-purple-500 text-white font-bold text-xs rounded-md disabled:bg-gray-400\"><SparklesIcon /> AI Enhance</button>\n <button onClick={() => navigator.clipboard.writeText(activeSnippet.code)} className=\"px-3 py-1 bg-gray-100 text-xs rounded-md\">Copy</button>\n <button onClick={handleDownload} className=\"flex items-center gap-1 px-3 py-1 bg-gray-100 text-xs rounded-md\"><ArrowDownTrayIcon className=\"w-4 h-4\"/> Download</button>\n </div>\n </div>\n <textarea value={activeSnippet.code} onChange={e => updateSnippet({...activeSnippet, code: e.target.value})} className=\"flex-grow p-4 bg-surface border border-border rounded-md resize-none font-mono text-sm focus:ring-2 focus:ring-primary focus:outline-none\"/>\n <div className=\"mt-2 text-xs text-text-secondary\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-bold\">Tags:</span> {(activeSnippet.tags ?? []).map(t => <span key={t} className=\"bg-gray-200 px-2 py-0.5 rounded-full\">{t}</span>)}\n <input type=\"text\" placeholder=\"+ Add tag\" onKeyDown={handleTagsChange} className=\"bg-transparent border-b border-border focus:outline-none focus:border-primary w-24 text-xs px-1\"/>\n </div>\n </div>\n </>) : (<div className=\"flex-grow flex items-center justify-center bg-background border border-border rounded-lg text-text-secondary\">Select a snippet or create a new one.</div>)}\n </main>\n </div>\n </div>\n );\n};"],"names":["langToExt","SnippetVault","snippets","setSnippets","useLocalStorage","activeSnippet","setActiveSnippet","useState","isEnhancing","setIsEnhancing","searchTerm","setSearchTerm","isEditingName","setIsEditingName","filteredSnippets","useMemo","lowerSearch","s","t","useEffect","updateSnippet","snippet","handleEnhance","stream","enhanceSnippetStream","fullResponse","chunk","handleAddNew","newSnippet","handleDelete","id","handleDownload","extension","filename","downloadFile","handleNameChange","handleTagsChange","newTag","jsxs","jsx","LockClosedIcon","ClipboardDocumentIcon","TrashIcon","Fragment","SparklesIcon","ArrowDownTrayIcon"],"mappings":"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"}