feat(viewer): show delete icon on annotation for direct block deletion (#339)
Adds a trash icon button (44×44 px touch target) directly on each annotation shape in transcription mode so users can delete a block without navigating through the sidebar. Includes keyboard support (Delete key), confirm dialog via ConfirmService, prop-chain wiring through DocumentViewer → PdfViewer → AnnotationLayer → AnnotationShape, and orphaned-annotation fallback (calls DELETE /annotations/{id} when no block is linked). Backend security regression test added for deleteBlock 403 on READ_ALL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,9 +13,12 @@ import { getErrorMessage } from '$lib/errors';
|
||||
import { translateOcrProgress } from '$lib/ocr/translateOcrProgress';
|
||||
import { createFileLoader } from '$lib/hooks/useFileLoader.svelte';
|
||||
import { scrollToCommentFromQuery } from '$lib/utils/deepLinkScroll';
|
||||
import { getConfirmService } from '$lib/services/confirm.svelte.js';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
const { confirm } = getConfirmService();
|
||||
|
||||
const doc = $derived(data.document);
|
||||
const canWrite = $derived(data.canWrite ?? false);
|
||||
const currentUserId = $derived((data.user?.id as string | undefined) ?? null);
|
||||
@@ -105,6 +108,23 @@ async function deleteBlock(blockId: string) {
|
||||
annotationReloadKey++;
|
||||
}
|
||||
|
||||
async function handleAnnotationDeleteRequest(annotationId: string) {
|
||||
const confirmed = await confirm({
|
||||
title: m.transcription_block_delete_confirm(),
|
||||
destructive: true
|
||||
});
|
||||
if (!confirmed) return;
|
||||
|
||||
const block = transcriptionBlocks.find((b) => b.annotationId === annotationId);
|
||||
if (block) {
|
||||
await deleteBlock(block.id);
|
||||
} else {
|
||||
// Annotation has no linked block — delete the annotation directly
|
||||
await fetch(`/api/documents/${doc.id}/annotations/${annotationId}`, { method: 'DELETE' });
|
||||
annotationReloadKey++;
|
||||
}
|
||||
}
|
||||
|
||||
async function reviewToggle(blockId: string) {
|
||||
const res = await fetch(`/api/documents/${doc.id}/transcription-blocks/${blockId}/review`, {
|
||||
method: 'PUT'
|
||||
@@ -381,6 +401,7 @@ onMount(() => {
|
||||
bind:activeAnnotationId={activeAnnotationId}
|
||||
onAnnotationClick={handleAnnotationClick}
|
||||
onTranscriptionDraw={createBlockFromDraw}
|
||||
onDeleteAnnotationRequest={handleAnnotationDeleteRequest}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user