diff --git a/frontend/src/lib/components/CommentThread.svelte b/frontend/src/lib/components/CommentThread.svelte
index 00ca5ab3..8725bd09 100644
--- a/frontend/src/lib/components/CommentThread.svelte
+++ b/frontend/src/lib/components/CommentThread.svelte
@@ -13,6 +13,7 @@ type Props = {
initialComments?: Comment[];
loadOnMount?: boolean;
canComment: boolean;
+ currentUserId: string | null;
quotedText?: string | null;
showCompose?: boolean;
onCountChange?: (count: number) => void;
@@ -25,6 +26,7 @@ let {
initialComments = [],
loadOnMount = false,
canComment,
+ currentUserId = null,
quotedText = null,
showCompose = true,
onCountChange
@@ -44,6 +46,8 @@ let comments: Comment[] = $state(untrack(() => [...initialComments]));
let newText: string = $state('');
let posting: boolean = $state(false);
let newMentionCandidates: MentionDTO[] = $state([]);
+let editingId: string | null = $state(null);
+let editText: string = $state('');
const commentsBase = $derived(
blockId
@@ -78,6 +82,10 @@ function wasEdited(c: { createdAt: string; updatedAt: string }): boolean {
return c.updatedAt > c.createdAt;
}
+function isOwn(c: { authorId: string | null }): boolean {
+ return currentUserId !== null && c.authorId === currentUserId;
+}
+
function getInitials(name: string): string {
return name
.split(/\s+/)
@@ -126,6 +134,60 @@ async function postComment() {
}
}
+function startEdit(msg: FlatMessage) {
+ editingId = msg.id;
+ editText = msg.content;
+}
+
+async function saveEdit(commentId: string) {
+ const text = editText.trim();
+ if (!text || posting) return;
+ posting = true;
+ try {
+ const res = await fetch(`/api/documents/${documentId}/comments/${commentId}`, {
+ method: 'PATCH',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ content: text })
+ });
+ if (res.ok) {
+ editingId = null;
+ editText = '';
+ await reload();
+ }
+ } finally {
+ posting = false;
+ }
+}
+
+function cancelEdit() {
+ editingId = null;
+ editText = '';
+}
+
+function handleEditKeydown(e: KeyboardEvent, commentId: string) {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ saveEdit(commentId);
+ } else if (e.key === 'Escape') {
+ cancelEdit();
+ }
+}
+
+async function deleteComment(commentId: string) {
+ if (posting) return;
+ posting = true;
+ try {
+ const res = await fetch(`/api/documents/${documentId}/comments/${commentId}`, {
+ method: 'DELETE'
+ });
+ if (res.ok) {
+ await reload();
+ }
+ } finally {
+ posting = false;
+ }
+}
+
onMount(() => {
if (loadOnMount) {
reload();
@@ -181,10 +243,49 @@ onMount(() => {
“{parsed.quote}”
{/if}
-
-
- {@html renderBody(parsed.body, msg.mentionDTOs ?? [])}
-
+
+ {#if editingId === msg.id}
+
+ Enter speichern · Esc abbrechen
+ {:else}
+
+
+ { if (isOwn(msg)) startEdit(msg); }}>
+
+
+ {@html renderBody(parsed.body, msg.mentionDTOs ?? [])}
+
+ {#if isOwn(msg)}
+
+ {/if}
+
+ {/if}
{/each}
diff --git a/frontend/src/lib/components/TranscriptionBlock.svelte b/frontend/src/lib/components/TranscriptionBlock.svelte
index 55b068ad..582486ef 100644
--- a/frontend/src/lib/components/TranscriptionBlock.svelte
+++ b/frontend/src/lib/components/TranscriptionBlock.svelte
@@ -13,6 +13,7 @@ type Props = {
active: boolean;
saveState: SaveState;
canComment: boolean;
+ currentUserId: string | null;
onTextChange: (text: string) => void;
onFocus: () => void;
onDeleteClick: () => void;
@@ -28,6 +29,7 @@ let {
active,
saveState,
canComment,
+ currentUserId,
onTextChange,
onFocus,
onDeleteClick,
@@ -214,6 +216,7 @@ function handleTextareaMouseUp() {
blockId={blockId}
loadOnMount={true}
canComment={canComment}
+ currentUserId={currentUserId}
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 ddae063f..5e2af323 100644
--- a/frontend/src/lib/components/TranscriptionBlock.svelte.spec.ts
+++ b/frontend/src/lib/components/TranscriptionBlock.svelte.spec.ts
@@ -15,6 +15,7 @@ function renderBlock(overrides: Record = {}) {
active: false,
saveState: 'idle' as const,
canComment: true,
+ currentUserId: 'user-1',
onTextChange: vi.fn(),
onFocus: vi.fn(),
onDeleteClick: vi.fn(),
diff --git a/frontend/src/lib/components/TranscriptionEditView.svelte b/frontend/src/lib/components/TranscriptionEditView.svelte
index cdcc4b67..4bb02688 100644
--- a/frontend/src/lib/components/TranscriptionEditView.svelte
+++ b/frontend/src/lib/components/TranscriptionEditView.svelte
@@ -10,12 +10,21 @@ type Props = {
documentId: string;
blocks: TranscriptionBlockData[];
canComment: boolean;
+ currentUserId: string | null;
onBlockFocus: (blockId: string) => void;
onSaveBlock: (blockId: string, text: string) => Promise;
onDeleteBlock: (blockId: string) => Promise;
};
-let { documentId, blocks, canComment, onBlockFocus, onSaveBlock, onDeleteBlock }: Props = $props();
+let {
+ documentId,
+ blocks,
+ canComment,
+ currentUserId,
+ onBlockFocus,
+ onSaveBlock,
+ onDeleteBlock
+}: Props = $props();
let activeBlockId: string | null = $state(null);
let saveStates = new SvelteMap();
@@ -156,6 +165,7 @@ $effect(() => {
active={activeBlockId === block.id}
saveState={getSaveState(block.id)}
canComment={canComment}
+ currentUserId={currentUserId}
onTextChange={(text) => handleTextChange(block.id, text)}
onFocus={() => handleFocus(block.id)}
onDeleteClick={() => handleDelete(block.id)}
diff --git a/frontend/src/routes/documents/[id]/+page.svelte b/frontend/src/routes/documents/[id]/+page.svelte
index 27dd403c..7864ce00 100644
--- a/frontend/src/routes/documents/[id]/+page.svelte
+++ b/frontend/src/routes/documents/[id]/+page.svelte
@@ -9,6 +9,7 @@ let { data } = $props();
const doc = $derived(data.document);
const canWrite = $derived(data.canWrite ?? false);
+const currentUserId = $derived((data.user?.id as string | undefined) ?? null);
// ── File loading ──────────────────────────────────────────────────────────────
@@ -217,6 +218,7 @@ onMount(() => {
documentId={doc.id}
blocks={transcriptionBlocks}
canComment={canWrite}
+ currentUserId={currentUserId}
onBlockFocus={handleBlockFocus}
onSaveBlock={saveBlock}
onDeleteBlock={deleteBlock}