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>