File size: 5,359 Bytes
6426ece
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
<script lang="ts">
	import { StreamLanguage } from '@codemirror/language';
	import CodeMirror from '$lib/CodeMirror/CodeMirror.svelte';
	import { jinja2 } from '@codemirror/legacy-modes/mode/jinja2';
	import { EditorView, lineNumbers } from '@codemirror/view';
	import CopyButton from '$lib/CopyButton/CopyButton.svelte';
	import IconCodeGeneration from '$lib/Icons/IconCodeGeneration.svelte';
	import { tooltip } from '$lib/utils/tooltip';
	import type { FormattedChatTemplate } from './types';
	import LineWrapButton from '$lib/LineWrapButton/LineWrapButton.svelte';
	import { createEventDispatcher } from 'svelte';
	import IconRestart from '$lib/Icons/IconRestart.svelte';

	export let modelId: string;
	export let formattedTemplates: FormattedChatTemplate[] = [];
	export let selectedTemplate: FormattedChatTemplate | undefined = undefined;
	export let showFormattedTemplate = true;

	let wrapLines = true;

	const dispatch = createEventDispatcher<{ modelIdChange: string; templateChange: string }>();

	async function handleUpdateEditor(e: CustomEvent<string>) {
		const currentCode = e.detail;
		if (selectedTemplate) {
			showFormattedTemplate
				? (selectedTemplate.formattedTemplate = currentCode)
				: (selectedTemplate.template = currentCode);
		}
	}
</script>

<div class="h-full overflow-scroll bg-white dark:bg-gray-900">
	<div class="sticky top-0 z-10 bg-white dark:bg-gray-900">
		<div
			class="text-semibold flex items-center gap-x-2 border-b border-gray-500 bg-linear-to-r from-green-200 to-white px-3 py-1.5 text-lg dark:from-green-700 dark:to-green-900 dark:text-gray-200"
		>
			Chat template{formattedTemplates.length > 1 ? 's' : ''} for
			<a class="font-mono underline" href="https://huggingface.co/{modelId}" target="_blank"
				>{modelId}</a
			>
			<button
				class="btn ml-auto text-sm"
				on:click={() => {
					const newModelId = prompt('Enter model ID (ex: deepseek-ai/DeepSeek-R1)')?.trim();
					if (newModelId) {
						dispatch('modelIdChange', newModelId);
					}
				}}>change model</button
			>
		</div>
		<div class="flex items-center border-b px-3 py-2">
			{#if formattedTemplates.length > 1}
				<div class="my-1.5 flex flex-wrap items-center gap-x-1 gap-y-0.5">
					{#each formattedTemplates as template (template.name)}
						<button
							class="text-md flex items-center rounded-lg border px-1.5 py-1 leading-none select-none
								 {selectedTemplate?.name === template.name
								? 'border-gray-800 bg-black text-white dark:bg-gray-700'
								: 'cursor-pointer text-gray-500 opacity-90 hover:text-gray-700 hover:shadow-xs dark:hover:text-gray-200'}"
							type="button"
							on:click={() => {
								selectedTemplate = template;
								dispatch('templateChange', template.name);
							}}
						>
							{template.name}
						</button>
					{/each}
				</div>
			{/if}
			<div class="ml-auto flex items-center gap-x-2">
				<!-- reset button -->
				{#if showFormattedTemplate ? selectedTemplate?.formattedTemplate !== selectedTemplate?.formattedTemplateUnedited : selectedTemplate?.template !== selectedTemplate?.templateUnedited}
					<button
						class="relative inline-flex h-6! cursor-pointer items-center justify-center rounded-md border border-gray-500 bg-white p-0! px-1.5! text-sm shadow-xs focus:outline-hidden dark:bg-gray-900 dark:text-white [&_svg]:translate-x-px! [&_svg]:translate-y-px! [&_svg]:text-base!"
						type="button"
						on:click={() => {
							if (selectedTemplate) {
								showFormattedTemplate
									? (selectedTemplate.formattedTemplate =
											selectedTemplate.formattedTemplateUnedited)
									: (selectedTemplate.template = selectedTemplate.templateUnedited);
							}
						}}
						use:tooltip={'Reset template to original'}
						><IconRestart classNames="dark:text-gray-200!" />
						<span class="ml-1 text-sm select-none dark:text-gray-200!"> Reset </span>
					</button>
				{/if}

				<CopyButton
					label="Copy"
					value={showFormattedTemplate
						? (selectedTemplate?.formattedTemplate ?? '')
						: (selectedTemplate?.template ?? '')}
					style="button-clear"
					classNames="h-6! [&_svg]:text-[0.7rem]! px-1.5! text-black! dark:text-gray-200!"
				/>

				<!-- format button -->
				<button
					class="relative inline-flex h-6! cursor-pointer items-center justify-center rounded-md border border-gray-500 bg-white p-0! px-1.5! text-sm shadow-xs focus:outline-hidden dark:bg-gray-900 dark:text-white [&_svg]:translate-x-px! [&_svg]:translate-y-px! [&_svg]:text-base!"
					type="button"
					on:click={() => {
						showFormattedTemplate = !showFormattedTemplate;
					}}
					use:tooltip={'Format with @huggingface/jinja'}
					><IconCodeGeneration classNames={showFormattedTemplate ? 'opacity-100' : 'opacity-40'} />
					<span
						class="ml-1 text-sm select-none {showFormattedTemplate ? 'opacity-100' : 'opacity-40'}"
					>
						Formatted
					</span>
				</button>

				<LineWrapButton
					style="button-clear"
					bind:wrapLines
					classNames="[&_svg]:text-xs! size-6! p-0!"
				/>
			</div>
		</div>
	</div>

	<CodeMirror
		value={showFormattedTemplate
			? (selectedTemplate?.formattedTemplate ?? '')
			: (selectedTemplate?.template ?? '')}
		on:change={handleUpdateEditor}
		extensions={[
			lineNumbers(),
			StreamLanguage.define(jinja2),
			...[wrapLines ? [EditorView.lineWrapping] : []]
		]}
	/>
</div>