From 7825c7749abc200a3cf93b9c42323a0b70c7a9f0 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 28 Mar 2026 11:44:51 +0100 Subject: [PATCH] fix(#73): open annotation side panel when deep-linking via ?annotationId= - NotificationBell now includes annotationId in the deep-link URL when available - +page.svelte reads ?annotationId= param and sets activeAnnotationId on mount, opening the side panel instead of the bottom discussion drawer - AnnotationSidePanel accepts and forwards targetCommentId to CommentThread so the specific comment is highlighted when navigating via a notification Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/components/AnnotationSidePanel.svelte | 3 +++ frontend/src/lib/components/NotificationBell.svelte | 5 ++++- frontend/src/routes/documents/[id]/+page.svelte | 9 +++++++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/components/AnnotationSidePanel.svelte b/frontend/src/lib/components/AnnotationSidePanel.svelte index 58424115..28d292e4 100644 --- a/frontend/src/lib/components/AnnotationSidePanel.svelte +++ b/frontend/src/lib/components/AnnotationSidePanel.svelte @@ -9,6 +9,7 @@ type Props = { canComment: boolean; currentUserId: string | null; canAdmin: boolean; + targetCommentId?: string | null; onClose: () => void; }; @@ -19,6 +20,7 @@ let { canComment, currentUserId, canAdmin, + targetCommentId = null, onClose }: Props = $props(); @@ -57,6 +59,7 @@ const visible = $derived(activeAnnotationId !== null); canComment={canComment} currentUserId={currentUserId} canAdmin={canAdmin} + targetCommentId={targetCommentId} loadOnMount={true} /> {/key} diff --git a/frontend/src/lib/components/NotificationBell.svelte b/frontend/src/lib/components/NotificationBell.svelte index 639d0ba6..8f344b10 100644 --- a/frontend/src/lib/components/NotificationBell.svelte +++ b/frontend/src/lib/components/NotificationBell.svelte @@ -9,6 +9,7 @@ type NotificationItem = { type: 'REPLY' | 'MENTION'; documentId: string; referenceId: string; + annotationId: string | null; read: boolean; createdAt: string; actorName: string; @@ -62,7 +63,9 @@ async function markRead(notification: NotificationItem) { console.error('Failed to mark notification as read', e); } } - const url = `/documents/${notification.documentId}?commentId=${notification.referenceId}`; + const url = notification.annotationId + ? `/documents/${notification.documentId}?commentId=${notification.referenceId}&annotationId=${notification.annotationId}` + : `/documents/${notification.documentId}?commentId=${notification.referenceId}`; closeDropdown(); goto(url); } diff --git a/frontend/src/routes/documents/[id]/+page.svelte b/frontend/src/routes/documents/[id]/+page.svelte index 05558bc8..2d9f30e1 100644 --- a/frontend/src/routes/documents/[id]/+page.svelte +++ b/frontend/src/routes/documents/[id]/+page.svelte @@ -10,6 +10,7 @@ import type { DocumentPanelTab } from '$lib/types'; let { data } = $props(); const targetCommentId = $derived(page.url.searchParams.get('commentId')); +const targetAnnotationId = $derived(page.url.searchParams.get('annotationId')); const doc = $derived(data.document); const canComment = $derived((data.canAnnotate || data.canWrite) ?? false); @@ -95,8 +96,11 @@ onMount(() => { if (!isNaN(h) && h >= 80) panelHeight = h; } - if (targetCommentId) { - // Deep-link: always open discussion tab regardless of saved state + if (targetAnnotationId) { + // Deep-link into an annotation comment: open the side panel + activeAnnotationId = targetAnnotationId; + } else if (targetCommentId) { + // Deep-link into a document-level comment: open discussion tab panelOpen = true; activeTab = 'discussion'; } else if (savedOpen === 'true') { @@ -169,6 +173,7 @@ $effect(() => { canComment={canComment} currentUserId={currentUserId} canAdmin={canAdmin} + targetCommentId={targetAnnotationId ? targetCommentId : null} onClose={() => { activeAnnotationId = null; activeAnnotationPage = null;