From 3d086bd1fb2f7875c5f9e98563758c9d79fc90c6 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 5 Apr 2026 22:30:13 +0200 Subject: [PATCH] fix(transcription): auto-capture quote on text selection, smart comment button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Quote captured automatically on mouseup in textarea (no button needed) Selection is held in state and pre-fills the comment input - "Kommentieren" button only shown when zero comments exist When comments are present, the input is already visible — button is noise - Chat bubble icon added to Kommentieren button for visual consistency Co-Authored-By: Claude Sonnet 4.6 --- .../lib/components/TranscriptionBlock.svelte | 57 ++++++++++++------- .../TranscriptionBlock.svelte.spec.ts | 13 +---- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/frontend/src/lib/components/TranscriptionBlock.svelte b/frontend/src/lib/components/TranscriptionBlock.svelte index 3027c8d6..55b068ad 100644 --- a/frontend/src/lib/components/TranscriptionBlock.svelte +++ b/frontend/src/lib/components/TranscriptionBlock.svelte @@ -36,9 +36,12 @@ let { let localText = $state(text); let commentOpen = $state(false); +let commentCount = $state(0); let selectedQuote = $state(null); let textareaEl = $state(null); +const hasComments = $derived(commentCount > 0); + // Sync from prop only when switching to a different block (not on save responses) let prevBlockId = $state(blockId); $effect(() => { @@ -83,17 +86,15 @@ function handleDelete() { } } -function captureSelectionAndOpenComments() { - if (textareaEl) { - const start = textareaEl.selectionStart; - const end = textareaEl.selectionEnd; - if (start !== end) { - selectedQuote = localText.substring(start, end); - } else { - selectedQuote = null; - } +function handleTextareaMouseUp() { + if (!textareaEl) return; + const start = textareaEl.selectionStart; + const end = textareaEl.selectionEnd; + if (start !== end) { + selectedQuote = localText.substring(start, end); + } else { + selectedQuote = null; } - commentOpen = true; } @@ -126,22 +127,33 @@ function captureSelectionAndOpenComments() { value={localText} oninput={handleInput} onfocus={onFocus} + onmouseup={handleTextareaMouseUp} >
-
- - {#if active} - - {m.transcription_block_quote_hint()} - +
+ {#if !hasComments} + {/if}
@@ -204,6 +216,7 @@ function captureSelectionAndOpenComments() { canComment={canComment} quotedText={selectedQuote} showCompose={commentOpen} + onCountChange={(count) => (commentCount = count)} />
diff --git a/frontend/src/lib/components/TranscriptionBlock.svelte.spec.ts b/frontend/src/lib/components/TranscriptionBlock.svelte.spec.ts index 6a166861..ddae063f 100644 --- a/frontend/src/lib/components/TranscriptionBlock.svelte.spec.ts +++ b/frontend/src/lib/components/TranscriptionBlock.svelte.spec.ts @@ -114,20 +114,9 @@ describe('TranscriptionBlock — interactions', () => { expect(onFocus).toHaveBeenCalled(); }); - it('shows Kommentieren button that opens comment thread', async () => { + it('shows Kommentieren button when no comments exist', async () => { renderBlock(); const btn = page.getByText('Kommentieren'); await expect.element(btn).toBeInTheDocument(); }); - - it('shows quote hint when block is active', async () => { - renderBlock({ active: true }); - await expect.element(page.getByText('Text markieren für Zitat')).toBeInTheDocument(); - }); - - it('hides quote hint when block is not active', async () => { - renderBlock({ active: false }); - const hint = page.getByText('Text markieren für Zitat'); - await expect.element(hint).not.toBeInTheDocument(); - }); });