CommentEditor.svelte
1<script lang="ts">
2 import { commentingLine } from '$lib/stores/ui';
3 import { addComment } from '$lib/stores/review';
4
5 interface Props {
6 filePath: string;
7 lineNumber: number;
8 side: 'old' | 'new';
9 threadId?: string;
10 originalLineContent?: string;
11 onclose?: () => void;
12 }
13
14 let { filePath, lineNumber, side, threadId, originalLineContent, onclose }: Props = $props();
15
16 let text = $state('');
17 let commentType = $state<'comment' | 'suggestion' | 'question'>('comment');
18 let submitting = $state(false);
19 let textarea: HTMLTextAreaElement | undefined = $state();
20
21 $effect(() => {
22 textarea?.focus();
23 });
24
25 async function submit() {
26 if (!text.trim()) return;
27 submitting = true;
28 try {
29 await addComment(filePath, lineNumber, side, text.trim(), commentType, threadId, originalLineContent);
30 text = '';
31 $commentingLine = null;
32 onclose?.();
33 } finally {
34 submitting = false;
35 }
36 }
37
38 function cancel() {
39 $commentingLine = null;
40 onclose?.();
41 }
42
43 function handleKeydown(e: KeyboardEvent) {
44 if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
45 e.preventDefault();
46 submit();
47 }
48 if (e.key === 'Escape') {
49 cancel();
50 }
51 }
52</script>
53
54<div class="bg-panel border border-border-strong rounded-lg p-3 my-1 mx-2">
55 <div class="flex gap-1 mb-2">
56 {#each ['comment', 'suggestion', 'question'] as type}
57 {@const active = commentType === type}
58 <button
59 class="text-xs px-2 py-1 rounded transition-colors {active ? 'bg-active text-primary' : 'text-tertiary hover:text-secondary hover:bg-hover'}"
60 onclick={() => (commentType = type as typeof commentType)}
61 >
62 {type === 'comment' ? '💬' : type === 'suggestion' ? '💡' : '❓'}
63 {type}
64 </button>
65 {/each}
66 </div>
67 <textarea
68 bind:this={textarea}
69 bind:value={text}
70 onkeydown={handleKeydown}
71 placeholder="Leave a {commentType}..."
72 class="w-full bg-surface text-primary border border-border-strong rounded px-3 py-2 text-sm resize-y min-h-[80px] focus:outline-none focus:border-blue-500 placeholder:text-muted"
73 rows="3"
74 ></textarea>
75 <div class="flex items-center justify-between mt-2">
76 <span class="text-xs text-muted">
77 {navigator?.platform?.includes('Mac') ? '⌘' : 'Ctrl'}+Enter to submit
78 </span>
79 <div class="flex gap-2">
80 <button
81 class="text-xs px-3 py-1.5 rounded text-tertiary hover:text-primary hover:bg-hover transition-colors"
82 onclick={cancel}
83 >
84 Cancel
85 </button>
86 <button
87 class="text-xs px-3 py-1.5 rounded bg-blue-600 text-white hover:bg-blue-500 transition-colors disabled:opacity-50"
88 disabled={!text.trim() || submitting}
89 onclick={submit}
90 >
91 {submitting ? 'Adding...' : 'Add ' + commentType}
92 </button>
93 </div>
94 </div>
95</div>