From d53d0f6b77622aa751ebedbb8211da97a452cbc0 Mon Sep 17 00:00:00 2001 From: Dicken Date: Thu, 25 Jun 2026 21:54:24 +0200 Subject: [PATCH] Whiteboard: Text Live-Vorschau beim Tippen auf Canvas; Linkliste: doppelter Kanban-Eintrag entfernt --- frontend/src/tools/linkliste.jsx | 1 - frontend/src/tools/whiteboard.jsx | 50 ++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/frontend/src/tools/linkliste.jsx b/frontend/src/tools/linkliste.jsx index 3614c80..1a70c94 100644 --- a/frontend/src/tools/linkliste.jsx +++ b/frontend/src/tools/linkliste.jsx @@ -10,7 +10,6 @@ const TOOL_ENTRIES = [ { url:'tool://bestellungen', label:'๐Ÿ“ฆ Bestellungen', group:'3D-Druck' }, { url:'tool://kalkulator3d', label:'๐Ÿงฎ Kostenrechner', group:'3D-Druck' }, { url:'tool://kanban', label:'๐Ÿ—‚ Kanban', group:'Werkzeuge' }, - { url:'tool://kanban', label:'๐Ÿ—‚ Kanban', group:'Werkzeuge' }, { url:'tool://whiteboard', label:'๐Ÿ–Š Whiteboard', group:'Werkzeuge' }, { url:'tool://dateien', label:'๐Ÿ“ Dateien', group:'Werkzeuge' }, { url:'tool://nachrichten', label:'๐Ÿ’ฌ Nachrichten', group:'Werkzeuge' }, diff --git a/frontend/src/tools/whiteboard.jsx b/frontend/src/tools/whiteboard.jsx index c5c52c1..a399361 100644 --- a/frontend/src/tools/whiteboard.jsx +++ b/frontend/src/tools/whiteboard.jsx @@ -283,6 +283,7 @@ export default function Whiteboard({ toast, mobile }) { const [saving, setSaving] = useState(false); const [shareModal,setShareModal]= useState(false); const [textInput, setTextInput] = useState(null); + const [textPreview, setTextPreview] = useState(null); // Live-Vorschau wรคhrend Tippen const [selectedId,setSelectedId]= useState(null); // Kollaboration @@ -303,6 +304,7 @@ export default function Whiteboard({ toast, mobile }) { const vpRef = useRef({x:0,y:0,zoom:1}); const elementsRef = useRef([]); const selectedRef = useRef(null); + const textPreviewRef = useRef(null); // โ”€โ”€ Hilfsfunktionen โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ const syncVp = (vp) => { vpRef.current=vp; setViewport({...vp}); }; @@ -463,7 +465,25 @@ export default function Whiteboard({ toast, mobile }) { ctx.save(); ctx.fillStyle=c.color; ctx.font='10px monospace'; ctx.fillText('โ–ถ '+c.username, sx+6, sy-4); ctx.restore(); } - }, [collab]); + // Text-Vorschau wรคhrend Tippen + if (textPreviewRef.current) { + const { text, x, y, fontSize, color } = textPreviewRef.current; + const vp = vpRef.current; + const sx = x * vp.zoom + vp.x; + const sy = y * vp.zoom + vp.y; + ctx.save(); + ctx.font = `${fontSize * vp.zoom}px monospace`; + ctx.fillStyle = color; + ctx.globalAlpha = 0.85; + ctx.fillText(text || '|', sx, sy); + // Cursor-Blinken simulieren: kleiner Strich hinter dem Text + if (text) { + const tw = ctx.measureText(text).width; + ctx.fillRect(sx + tw + 1, sy - fontSize * vp.zoom * 0.8, 2, fontSize * vp.zoom); + } + ctx.restore(); + } + }, [collab, textPreview]); useEffect(()=>{ render(); }, [elements, viewport, selectedId, collab, render]); @@ -507,6 +527,8 @@ export default function Whiteboard({ toast, mobile }) { // Text if (tool==='text') { + textPreviewRef.current = { text:'', x, y, fontSize:FONT_SIZES[fontSize], color }; + render(); setTextInput({ sx, sy, x, y }); return; } @@ -717,7 +739,6 @@ export default function Whiteboard({ toast, mobile }) { {textInput&&( mobile ? ( - // Mobile: festes Panel unten, kein Keyboard-Verschiebe-Problem
@@ -726,37 +747,44 @@ export default function Whiteboard({ toast, mobile }) {
{ + textPreviewRef.current={text:e.target.value,x:textInput.x,y:textInput.y,fontSize:FONT_SIZES[fontSize],color}; + render(); + }} onKeyDown={e=>{ if(e.key==='Enter'){ - const v=e.target.value; setTextInput(null); + const v=e.target.value; textPreviewRef.current=null; setTextInput(null); if(v?.trim()) pushElement({id:getId(),type:'text',color,fontSize:FONT_SIZES[fontSize],text:v,x:textInput.x,y:textInput.y}); } - if(e.key==='Escape') setTextInput(null); + if(e.key==='Escape'){ textPreviewRef.current=null; setTextInput(null); render(); } }} placeholder="Text eingebenโ€ฆ" style={{...S.inp,flex:1,fontSize:14,color}}/> - +
) : ( - // Desktop: direkt am Klick-Punkt, Canvas-relative Position
{ + textPreviewRef.current={text:e.target.value,x:textInput.x,y:textInput.y,fontSize:FONT_SIZES[fontSize],color}; + render(); + }} onKeyDown={e=>{ if(e.key==='Enter'){ - const v=e.target.value; setTextInput(null); + const v=e.target.value; textPreviewRef.current=null; setTextInput(null); if(v?.trim()) pushElement({id:getId(),type:'text',color,fontSize:FONT_SIZES[fontSize],text:v,x:textInput.x,y:textInput.y}); } - if(e.key==='Escape') setTextInput(null); + if(e.key==='Escape'){ textPreviewRef.current=null; setTextInput(null); render(); } }} onBlur={e=>{ - const v=e.target.value; setTextInput(null); + const v=e.target.value; textPreviewRef.current=null; setTextInput(null); if(v?.trim()) pushElement({id:getId(),type:'text',color,fontSize:FONT_SIZES[fontSize],text:v,x:textInput.x,y:textInput.y}); }} style={{background:'rgba(0,0,0,0.7)',border:'none',