Dev #6

Merged
sandrodach merged 6 commits from dev into main 2026-10-26 18:54:23 +01:00
Showing only changes of commit 80bb74b5b2 - Show all commits
@@ -2190,23 +2190,20 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
if (el instanceof HTMLTextAreaElement) { if (el instanceof HTMLTextAreaElement) {
autoResizeTextarea(el); autoResizeTextarea(el);
} }
if ('setSelectionRange' in el && typeof el.value === 'string') { // Only programmatically adjust selection if the element is NOT already the actively focused element!
if (document.activeElement !== el && 'setSelectionRange' in el && typeof el.value === 'string') {
const segStartAttr = el.getAttribute('data-seg-start'); const segStartAttr = el.getAttribute('data-seg-start');
const segStart = segStartAttr !== null ? parseInt(segStartAttr, 10) || 0 : 0; const segStart = segStartAttr !== null ? parseInt(segStartAttr, 10) || 0 : 0;
const desiredOffset = activeCursorOffsetRef.current; const desiredOffset = activeCursorOffsetRef.current;
const localPos = Math.max(0, Math.min(el.value.length, desiredOffset - segStart)); const localPos = Math.max(0, Math.min(el.value.length, desiredOffset - segStart));
if (document.activeElement !== el) { el.focus();
el.focus();
}
try { try {
if (el.selectionStart !== localPos || el.selectionEnd !== localPos) { el.setSelectionRange(localPos, localPos);
el.setSelectionRange(localPos, localPos);
}
} catch { } catch {
// Ignore selection range errors on unsupported inputs // Ignore selection range errors on unsupported inputs
} }
} }
}, [activeEditingBlockId, activeCursorOffset, selectedDoc]); }, [activeEditingBlockId]);
// Keyboard Navigation: Enter, Shift+Enter, Tab, Shift+Tab, Backspace, / Slash Commands // Keyboard Navigation: Enter, Shift+Enter, Tab, Shift+Tab, Backspace, / Slash Commands
const handleKeyDown = ( const handleKeyDown = (
@@ -3073,7 +3070,29 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
onClick={(e) => { onClick={(e) => {
if (isReadOnly) return; if (isReadOnly) return;
if ((e.target as HTMLElement).closest('button, a')) return; if ((e.target as HTMLElement).closest('button, a')) return;
focusBlock(block.id); let clickOffset: number | undefined = undefined;
try {
if (document.caretRangeFromPoint) {
const range = document.caretRangeFromPoint(e.clientX, e.clientY);
if (range && e.currentTarget.contains(range.startContainer)) {
const preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(e.currentTarget);
preCaretRange.setEnd(range.endContainer, range.endOffset);
clickOffset = preCaretRange.toString().length;
}
} else if ((document as any).caretPositionFromPoint) {
const pos = (document as any).caretPositionFromPoint(e.clientX, e.clientY);
if (pos && pos.offsetNode && e.currentTarget.contains(pos.offsetNode)) {
const range = document.createRange();
range.selectNodeContents(e.currentTarget);
range.setEnd(pos.offsetNode, pos.offset);
clickOffset = range.toString().length;
}
}
} catch {
// fallback to end of text
}
focusBlock(block.id, clickOffset);
}} }}
className={`w-full text-sm leading-relaxed break-words whitespace-pre-wrap min-h-[22px] cursor-text ${extraTextClass}`} className={`w-full text-sm leading-relaxed break-words whitespace-pre-wrap min-h-[22px] cursor-text ${extraTextClass}`}
> >
@@ -3099,7 +3118,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
value={block.content || ''} value={block.content || ''}
placeholder="Notiz eingeben… (/ für Befehle)" placeholder="Notiz eingeben… (/ für Befehle)"
onFocus={() => { onFocus={() => {
handleBlockFocus(); handleBlockFocus(block.id);
}} }}
onBlur={() => { onBlur={() => {
setActiveEditingBlockId((prev) => (prev === block.id ? null : prev)); setActiveEditingBlockId((prev) => (prev === block.id ? null : prev));
@@ -3107,8 +3126,24 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
}} }}
onChange={(e) => { onChange={(e) => {
autoResizeTextarea(e.target); autoResizeTextarea(e.target);
const pos = e.target.selectionStart;
if (typeof pos === 'number') {
activeCursorOffsetRef.current = pos;
}
handleContentChange(block, e.target.value); handleContentChange(block, e.target.value);
}} }}
onSelect={(e) => {
const target = e.target as HTMLTextAreaElement;
if (typeof target.selectionStart === 'number') {
activeCursorOffsetRef.current = target.selectionStart;
}
}}
onClick={(e) => {
const target = e.target as HTMLTextAreaElement;
if (typeof target.selectionStart === 'number') {
activeCursorOffsetRef.current = target.selectionStart;
}
}}
onKeyDown={(e) => handleKeyDown(e, block, originalIndex)} onKeyDown={(e) => handleKeyDown(e, block, originalIndex)}
className={`w-full bg-transparent text-sm leading-relaxed focus:outline-none resize-none overflow-hidden break-words whitespace-pre-wrap placeholder:text-faint p-0 m-0 border-0 ${extraTextClass}`} className={`w-full bg-transparent text-sm leading-relaxed focus:outline-none resize-none overflow-hidden break-words whitespace-pre-wrap placeholder:text-faint p-0 m-0 border-0 ${extraTextClass}`}
/> />
@@ -4642,12 +4677,18 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
rows={1} rows={1}
readOnly={isReadOnly} readOnly={isReadOnly}
value={block.content} value={block.content}
onFocus={handleBlockFocus} onFocus={() => handleBlockFocus(block.id)}
onBlur={handleBlockBlur} onBlur={handleBlockBlur}
onChange={(e) => { onChange={(e) => {
autoResizeTextarea(e.target); autoResizeTextarea(e.target);
const pos = e.target.selectionStart;
if (typeof pos === 'number') activeCursorOffsetRef.current = pos;
handleContentChange(block, e.target.value); handleContentChange(block, e.target.value);
}} }}
onSelect={(e) => {
const target = e.target as HTMLTextAreaElement;
if (typeof target.selectionStart === 'number') activeCursorOffsetRef.current = target.selectionStart;
}}
onKeyDown={(e) => handleKeyDown(e, block, originalIndex)} onKeyDown={(e) => handleKeyDown(e, block, originalIndex)}
className={`w-full bg-transparent text-lg font-bold text-main focus:outline-none resize-none overflow-hidden break-words whitespace-pre-wrap p-0 m-0 border-0 ${ className={`w-full bg-transparent text-lg font-bold text-main focus:outline-none resize-none overflow-hidden break-words whitespace-pre-wrap p-0 m-0 border-0 ${
isReadOnly ? 'cursor-default' : '' isReadOnly ? 'cursor-default' : ''
@@ -4665,12 +4706,18 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
rows={1} rows={1}
readOnly={isReadOnly} readOnly={isReadOnly}
value={block.content} value={block.content}
onFocus={handleBlockFocus} onFocus={() => handleBlockFocus(block.id)}
onBlur={handleBlockBlur} onBlur={handleBlockBlur}
onChange={(e) => { onChange={(e) => {
autoResizeTextarea(e.target); autoResizeTextarea(e.target);
const pos = e.target.selectionStart;
if (typeof pos === 'number') activeCursorOffsetRef.current = pos;
handleContentChange(block, e.target.value); handleContentChange(block, e.target.value);
}} }}
onSelect={(e) => {
const target = e.target as HTMLTextAreaElement;
if (typeof target.selectionStart === 'number') activeCursorOffsetRef.current = target.selectionStart;
}}
onKeyDown={(e) => handleKeyDown(e, block, originalIndex)} onKeyDown={(e) => handleKeyDown(e, block, originalIndex)}
className={`w-full bg-transparent text-base font-semibold text-accent focus:outline-none resize-none overflow-hidden break-words whitespace-pre-wrap p-0 m-0 border-0 ${ className={`w-full bg-transparent text-base font-semibold text-accent focus:outline-none resize-none overflow-hidden break-words whitespace-pre-wrap p-0 m-0 border-0 ${
isReadOnly ? 'cursor-default' : '' isReadOnly ? 'cursor-default' : ''
@@ -4688,12 +4735,18 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
rows={1} rows={1}
readOnly={isReadOnly} readOnly={isReadOnly}
value={block.content} value={block.content}
onFocus={handleBlockFocus} onFocus={() => handleBlockFocus(block.id)}
onBlur={handleBlockBlur} onBlur={handleBlockBlur}
onChange={(e) => { onChange={(e) => {
autoResizeTextarea(e.target); autoResizeTextarea(e.target);
const pos = e.target.selectionStart;
if (typeof pos === 'number') activeCursorOffsetRef.current = pos;
handleContentChange(block, e.target.value); handleContentChange(block, e.target.value);
}} }}
onSelect={(e) => {
const target = e.target as HTMLTextAreaElement;
if (typeof target.selectionStart === 'number') activeCursorOffsetRef.current = target.selectionStart;
}}
onKeyDown={(e) => handleKeyDown(e, block, originalIndex)} onKeyDown={(e) => handleKeyDown(e, block, originalIndex)}
className={`w-full bg-transparent text-sm font-semibold text-main focus:outline-none resize-none overflow-hidden break-words whitespace-pre-wrap p-0 m-0 border-0 ${ className={`w-full bg-transparent text-sm font-semibold text-main focus:outline-none resize-none overflow-hidden break-words whitespace-pre-wrap p-0 m-0 border-0 ${
isReadOnly ? 'cursor-default' : '' isReadOnly ? 'cursor-default' : ''