feat(frontend): move annotation comments to right-side panel

Annotation threads now open in a slide-in side panel (320 px, right
edge of the PDF viewer) instead of expanding the bottom drawer.
The PDF stays visible while the user reads and writes annotation
comments.

- Add AnnotationSidePanel component (absolute-positioned, CSS slide
  transition, keyed CommentThread, close via X or Escape)
- Remove the $effect that opened the bottom drawer on annotation click
- Simplify PanelDiscussion back to document-level thread only (no
  annotation sub-tabs)
- Remove annotation-related props from DocumentBottomPanel and
  PanelDiscussion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-25 07:23:20 +01:00
parent 10783fdb55
commit f71712ab4b
4 changed files with 88 additions and 116 deletions

View File

@@ -1,5 +1,4 @@
<script lang="ts">
import { m } from '$lib/paraglide/messages.js';
import CommentThread from './CommentThread.svelte';
type CommentReply = {
@@ -23,107 +22,21 @@ type Comment = {
type Props = {
documentId: string;
activeAnnotationId: string | null;
activeAnnotationPage: number | null;
initialComments: Comment[];
canComment: boolean;
currentUserId: string | null;
canAdmin: boolean;
onAnnotationCommentCountChange?: (annotationId: string, count: number) => void;
};
let {
documentId,
activeAnnotationId,
activeAnnotationPage,
initialComments,
canComment,
currentUserId,
canAdmin,
onAnnotationCommentCountChange
}: Props = $props();
// Sub-tab within the discussion panel: 'document' or 'annotation'
type DiscussionTab = 'document' | 'annotation';
let activeSubTab = $state<DiscussionTab>('document');
// Track document-level comment count for badge.
// CommentThread calls onCountChange immediately on mount with the accurate total.
let docCommentCount = $state(0);
// When an annotation becomes active, switch to the annotation sub-tab automatically.
$effect(() => {
if (activeAnnotationId) {
activeSubTab = 'annotation';
} else {
activeSubTab = 'document';
}
});
function selectDocumentTab() {
activeSubTab = 'document';
}
function selectAnnotationTab() {
activeSubTab = 'annotation';
}
let { documentId, initialComments, canComment, currentUserId, canAdmin }: Props = $props();
</script>
<div class="flex h-full flex-col">
<!-- Sub-tab bar (only shown when annotation is active) -->
{#if activeAnnotationId}
<div class="flex shrink-0 border-b border-brand-sand/70 bg-gray-50 px-4">
<button
onclick={selectDocumentTab}
class="mr-1 px-3 py-2 font-sans text-xs font-medium transition-colors {activeSubTab === 'document'
? 'border-b-2 border-brand-navy text-brand-navy'
: 'text-gray-400 hover:text-brand-navy'}"
>
{m.doc_panel_tab_discussion()}
{#if docCommentCount > 0}
<span
class="ml-1 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-brand-mint px-1 font-mono text-[10px] text-brand-navy"
>
{docCommentCount}
</span>
{/if}
</button>
<button
onclick={selectAnnotationTab}
class="px-3 py-2 font-sans text-xs font-medium transition-colors {activeSubTab === 'annotation'
? 'border-b-2 border-brand-navy text-brand-navy'
: 'text-gray-400 hover:text-brand-navy'}"
>
{m.doc_panel_discussion_annotation_tab({ page: String(activeAnnotationPage ?? '?') })}
</button>
</div>
{/if}
<!-- Content area -->
<div class="flex-1 overflow-y-auto p-6">
{#if !activeAnnotationId || activeSubTab === 'document'}
<!-- Document-level thread -->
<CommentThread
documentId={documentId}
initialComments={initialComments}
canComment={canComment}
currentUserId={currentUserId}
canAdmin={canAdmin}
onCountChange={(count) => (docCommentCount = count)}
/>
{:else}
<!-- Annotation-level thread -->
{#key activeAnnotationId}
<CommentThread
documentId={documentId}
annotationId={activeAnnotationId}
canComment={canComment}
currentUserId={currentUserId}
canAdmin={canAdmin}
loadOnMount={true}
onCountChange={(count) => onAnnotationCommentCountChange?.(activeAnnotationId, count)}
/>
{/key}
{/if}
</div>
<div class="flex-1 overflow-y-auto p-6">
<CommentThread
documentId={documentId}
initialComments={initialComments}
canComment={canComment}
currentUserId={currentUserId}
canAdmin={canAdmin}
/>
</div>