refactor(TranscriptionBlock): migrate quote selection to Tiptap selectionUpdate (AC-7)

Replaces captureTextarea + handleTextareaMouseUp (which read selection
bounds off a real <textarea>) with an onSelectionChange callback prop
on PersonMentionEditor, wired to Tiptap's selectionUpdate event. The
editor emits the selected text directly so the parent no longer needs
DOM access.

Tests are updated to drive the contenteditable via the Selection API
instead of the now-deleted textarea.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-29 15:53:54 +02:00
parent d87ad36278
commit 7a25feb04e
2 changed files with 27 additions and 48 deletions

View File

@@ -79,17 +79,6 @@ let leftBorderClass = $derived(
saveState === 'error' ? 'border-l-2 border-error' : active ? 'border-l-2 border-turquoise' : ''
);
// Single source of truth for the editor's textarea — stored on attach so
// we can read selection bounds for quote selection without re-querying the DOM.
let textareaEl: HTMLTextAreaElement | null = null;
function captureTextarea(node: HTMLTextAreaElement) {
textareaEl = node;
return () => {
textareaEl = null;
};
}
function emitChange() {
onTextChange(localText, localMentions);
}
@@ -101,17 +90,6 @@ async function handleDelete() {
});
if (confirmed) onDeleteClick();
}
function handleTextareaMouseUp() {
if (!textareaEl) return;
const start = textareaEl.selectionStart;
const end = textareaEl.selectionEnd;
if (start !== end) {
selectedQuote = localText.substring(start, end);
} else {
selectedQuote = null;
}
}
</script>
<div
@@ -176,24 +154,22 @@ function handleTextareaMouseUp() {
{/if}
</div>
<!-- Textarea (now powered by PersonMentionEditor for @-mention typeahead) -->
<div onmouseup={handleTextareaMouseUp} role="presentation">
<PersonMentionEditor
bind:value={() => localText,
(v) => {
localText = v;
emitChange();
}}
bind:mentionedPersons={() => localMentions,
(next) => {
localMentions = next;
emitChange();
}}
placeholder={m.transcription_block_placeholder()}
onfocus={onFocus}
captureTextarea={captureTextarea}
/>
</div>
<!-- Editor powered by PersonMentionEditor (Tiptap) for @-mention typeahead -->
<PersonMentionEditor
bind:value={() => localText,
(v) => {
localText = v;
emitChange();
}}
bind:mentionedPersons={() => localMentions,
(next) => {
localMentions = next;
emitChange();
}}
placeholder={m.transcription_block_placeholder()}
onfocus={onFocus}
onSelectionChange={(text) => (selectedQuote = text)}
/>
{#if selectedQuote}
<p class="mt-1 text-xs text-ink-3">{m.transcription_block_quote_hint()}</p>