File size: 2,885 Bytes
d3ab1f5
822d5b9
2961a5b
d3ab1f5
2961a5b
 
 
 
 
 
 
 
 
 
b9ff581
822d5b9
d3ab1f5
822d5b9
 
 
 
 
d3ab1f5
2961a5b
 
 
 
 
 
 
 
 
 
 
822d5b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d3ab1f5
 
1302517
822d5b9
1302517
2961a5b
822d5b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2961a5b
b9ff581
 
8cc084b
b9ff581
 
 
2961a5b
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<script lang="ts">
	import { Check, PenLine, X } from '@lucide/svelte';
	import SvelteMarkdown from 'svelte-markdown';

	import type { ChatMessage } from '$lib/helpers/types';
	import Paragraph from './markdown/Paragraph.svelte';
	import Heading from './markdown/Heading.svelte';
	import Code from './markdown/Code.svelte';
	import Codespan from './markdown/Codespan.svelte';
	import Blockquote from './markdown/Blockquote.svelte';
	import List from './markdown/List.svelte';
	import ListItem from './markdown/ListItem.svelte';
	import Link from './markdown/Link.svelte';
	import Hr from './markdown/Hr.svelte';
	import Think from './markdown/Think.svelte';
	import Button from '../ui/button/button.svelte';

	let {
		message,
		nodeId,
		onEdit
	}: { message: ChatMessage; nodeId: string; onEdit?: (newContent: string) => void } = $props();

	const renderers = {
		paragraph: Paragraph,
		heading: Heading,
		code: Code,
		codespan: Codespan,
		blockquote: Blockquote,
		list: List,
		listitem: ListItem,
		link: Link,
		hr: Hr
	};

	let isEditing = $state(false);
	let editContent = $state('');

	function handleEdit() {
		editContent = message.content as string;
		isEditing = true;
	}

	function handleSubmitEdit() {
		const trimmed = editContent.trim();
		if (trimmed) {
			onEdit?.(trimmed);
		}
		isEditing = false;
	}

	function handleCancelEdit() {
		isEditing = false;
	}

	function handleKeydown(e: KeyboardEvent) {
		if (e.key === 'Enter' && !e.shiftKey) {
			e.preventDefault();
			handleSubmitEdit();
		} else if (e.key === 'Escape') {
			handleCancelEdit();
		}
	}
</script>

<main
	class="group/message pointer-events-auto cursor-auto p-1 text-lg leading-relaxed text-accent-foreground select-text"
>
	{#if message?.role === 'user'}
		{#if isEditing}
			<div class="w-full">
				<p
					contenteditable="true"
					bind:textContent={editContent}
					class="w-full rounded-lg bg-accent px-2 py-1 outline-none"
					onkeydown={handleKeydown}
				></p>
				<div class="mt-1.5 flex items-center justify-end gap-1">
					<Button variant="transparent" size="icon-2xs" onclick={handleCancelEdit}>
						<X class="size-3" />
					</Button>
					<Button variant="transparent" size="icon-2xs" onclick={handleSubmitEdit}>
						<Check class="size-3 text-primary" />
					</Button>
				</div>
			</div>
		{:else}
			<p class="relative max-w-max pr-8">
				{message.content}
				<Button
					variant="transparent"
					size="icon-2xs"
					class="absolute top-0.5 right-0 opacity-0 group-hover/message:opacity-100"
					onclick={handleEdit}
				>
					<PenLine class="size-3" />
				</Button>
			</p>
		{/if}
	{:else}
		{#if message.reasoning}
			<Think
				isThinking={!!message.content && (message.content as string)?.trim() !== ''}
				content={message.reasoning as string}
			/>
		{/if}
		<SvelteMarkdown source={message.content} renderers={renderers as any} />
	{/if}
</main>