Code-Schnipsel: History-Button Fix, Tag-Split nur Komma, Tag-Vorschläge
This commit is contained in:
@@ -258,7 +258,9 @@ function SnippetCard({ snippet, onEdit, onDelete, onShare, onHistory, toast }) {
|
|||||||
</div>
|
</div>
|
||||||
<div style={{display:'flex',gap:5,flexWrap:'wrap'}}>
|
<div style={{display:'flex',gap:5,flexWrap:'wrap'}}>
|
||||||
<button onClick={()=>onHistory(snippet)}
|
<button onClick={()=>onHistory(snippet)}
|
||||||
style={{...S.btn('rgba(255,255,255,0.3)',true),fontSize:10,padding:'4px 8px'}}>📜</button>
|
style={{background:'rgba(255,255,255,0.07)',border:'1px solid rgba(255,255,255,0.12)',
|
||||||
|
borderRadius:6,color:'rgba(255,255,255,0.5)',cursor:'pointer',
|
||||||
|
fontSize:10,padding:'4px 8px'}}>📜</button>
|
||||||
{!snippet.is_shared && (
|
{!snippet.is_shared && (
|
||||||
<>
|
<>
|
||||||
<button onClick={()=>onShare(snippet)}
|
<button onClick={()=>onShare(snippet)}
|
||||||
@@ -278,29 +280,39 @@ function SnippetCard({ snippet, onEdit, onDelete, onShare, onHistory, toast }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Snippet Form ──────────────────────────────────────────────────────────────
|
// ── Snippet Form ──────────────────────────────────────────────────────────────
|
||||||
function SnippetForm({ initial, onSave, onCancel, toast }) {
|
function SnippetForm({ initial, onSave, onCancel, toast, existingTags=[] }) {
|
||||||
const ALL_LANGS = ['text',...LANG_PATTERNS.map(l=>l.lang)];
|
const ALL_LANGS = ['text',...LANG_PATTERNS.map(l=>l.lang)];
|
||||||
const [title, setTitle] = useState(initial?.title || '');
|
const [title, setTitle] = useState(initial?.title || '');
|
||||||
const [code, setCode] = useState(initial?.code || '');
|
const [code, setCode] = useState(initial?.code || '');
|
||||||
const [lang, setLang] = useState(initial?.language || 'text');
|
const [lang, setLang] = useState(initial?.language || 'text');
|
||||||
const [desc, setDesc] = useState(initial?.description || '');
|
const [desc, setDesc] = useState(initial?.description || '');
|
||||||
const [tags, setTags] = useState(initial?.tags?.join(', ') || '');
|
const [tags, setTags] = useState(initial?.tags?.filter(t=>t!==initial?.language).join(', ') || '');
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
const [autoLang,setAutoLang]= useState(true);
|
const [autoLang,setAutoLang]= useState(!initial?.id);
|
||||||
|
|
||||||
// Auto-detect language as user types code
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
if (!autoLang) return;
|
if (!autoLang) return;
|
||||||
const detected = detectLanguage(code);
|
const detected = detectLanguage(code);
|
||||||
setLang(detected);
|
setLang(detected);
|
||||||
},[code, autoLang]);
|
},[code, autoLang]);
|
||||||
|
|
||||||
|
// Nur Komma als Trennzeichen
|
||||||
const parseTags = () => {
|
const parseTags = () => {
|
||||||
const langTag = lang !== 'text' ? [lang] : [];
|
const langTag = lang !== 'text' ? [lang] : [];
|
||||||
const manual = tags.split(/[,\s]+/).map(t=>t.trim().toLowerCase()).filter(Boolean);
|
const manual = tags.split(',').map(t=>t.trim().toLowerCase()).filter(Boolean);
|
||||||
return [...new Set([...langTag, ...manual])];
|
return [...new Set([...langTag, ...manual])];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addSuggestedTag = tag => {
|
||||||
|
const cur = tags.split(',').map(t=>t.trim()).filter(Boolean);
|
||||||
|
if (cur.includes(tag)) return;
|
||||||
|
setTags(cur.length ? cur.join(', ') + ', ' + tag : tag);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tags die noch nicht eingetragen sind als Vorschläge
|
||||||
|
const currentParsed = parseTags();
|
||||||
|
const suggestions = existingTags.filter(t => t !== lang && !currentParsed.includes(t));
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
if (!title.trim()) { toast('Titel erforderlich','error'); return; }
|
if (!title.trim()) { toast('Titel erforderlich','error'); return; }
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
@@ -348,6 +360,21 @@ function SnippetForm({ initial, onSave, onCancel, toast }) {
|
|||||||
<label style={{...S.head,display:'block',marginBottom:4,fontSize:9}}>WEITERE TAGS (kommagetrennt)</label>
|
<label style={{...S.head,display:'block',marginBottom:4,fontSize:9}}>WEITERE TAGS (kommagetrennt)</label>
|
||||||
<input value={tags} onChange={e=>setTags(e.target.value)} placeholder="z.B. api, auth, utils"
|
<input value={tags} onChange={e=>setTags(e.target.value)} placeholder="z.B. api, auth, utils"
|
||||||
autoCapitalize="none" style={{...S.inp,fontSize:12}}/>
|
autoCapitalize="none" style={{...S.inp,fontSize:12}}/>
|
||||||
|
{/* Vorhandene Tags als Vorschläge */}
|
||||||
|
{suggestions.length>0 && (
|
||||||
|
<div style={{marginTop:6}}>
|
||||||
|
<div style={{color:'rgba(255,255,255,0.25)',fontFamily:'monospace',fontSize:8,marginBottom:4}}>VORHANDENE TAGS HINZUFÜGEN:</div>
|
||||||
|
<div style={{display:'flex',gap:4,flexWrap:'wrap'}}>
|
||||||
|
{suggestions.map(t=>(
|
||||||
|
<button key={t} onClick={()=>addSuggestedTag(t)} style={{
|
||||||
|
fontSize:9,fontFamily:'monospace',padding:'2px 8px',borderRadius:12,cursor:'pointer',
|
||||||
|
background:'rgba(255,255,255,0.04)',border:'1px solid rgba(255,255,255,0.1)',
|
||||||
|
color:'rgba(255,255,255,0.4)',
|
||||||
|
}}>+ {t}</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{parseTags().length>0&&(
|
{parseTags().length>0&&(
|
||||||
<div style={{display:'flex',gap:4,flexWrap:'wrap',marginTop:6}}>
|
<div style={{display:'flex',gap:4,flexWrap:'wrap',marginTop:6}}>
|
||||||
{parseTags().map(t=>(
|
{parseTags().map(t=>(
|
||||||
@@ -463,6 +490,7 @@ export default function CodeSchnipsel({ toast, mobile }) {
|
|||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
onCancel={()=>{ setShowForm(false); setEditItem(null); }}
|
onCancel={()=>{ setShowForm(false); setEditItem(null); }}
|
||||||
toast={toast}
|
toast={toast}
|
||||||
|
existingTags={allTags}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user