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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user