ToolkitEditor.svelte 4.69 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
<script>
Timothy J. Baek's avatar
Timothy J. Baek committed
2
	import { getContext, createEventDispatcher, onMount } from 'svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
3
4
5
6
7

	const i18n = getContext('i18n');

	import CodeEditor from './CodeEditor.svelte';
	import { goto } from '$app/navigation';
Timothy J. Baek's avatar
Timothy J. Baek committed
8
	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
9

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
10
11
	const dispatch = createEventDispatcher();

Timothy J. Baek's avatar
Timothy J. Baek committed
12
	let formElement = null;
Timothy J. Baek's avatar
Timothy J. Baek committed
13
	let loading = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
14
	let showConfirm = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
15

Timothy J. Baek's avatar
Timothy J. Baek committed
16
	export let edit = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
17
	export let clone = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
18
19
20
21

	export let id = '';
	export let name = '';
	export let meta = {
Timothy J. Baek's avatar
Timothy J. Baek committed
22
23
		description: ''
	};
Timothy J. Baek's avatar
Timothy J. Baek committed
24
	export let content = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
25

Timothy J. Baek's avatar
Timothy J. Baek committed
26
	$: if (name && !edit && !clone) {
Timothy J. Baek's avatar
Timothy J. Baek committed
27
28
29
30
31
32
33
		id = name.replace(/\s+/g, '_').toLowerCase();
	}

	let codeEditor;

	const saveHandler = async () => {
		loading = true;
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
34
35
36
37
38
39
		dispatch('save', {
			id,
			name,
			meta,
			content
		});
Timothy J. Baek's avatar
Timothy J. Baek committed
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
	};

	const submitHandler = async () => {
		if (codeEditor) {
			const res = await codeEditor.formatHandler();

			if (res) {
				console.log('Code formatted successfully');
				saveHandler();
			}
		}
	};
</script>

<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
	<div class="mx-auto w-full md:px-0 h-full">
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
56
		<form
Timothy J. Baek's avatar
Timothy J. Baek committed
57
			bind:this={formElement}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
58
59
			class=" flex flex-col max-h-[100dvh] h-full"
			on:submit|preventDefault={() => {
60
61
62
63
64
				if (edit) {
					submitHandler();
				} else {
					showConfirm = true;
				}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
65
66
			}}
		>
Timothy J. Baek's avatar
Timothy J. Baek committed
67
68
69
70
71
72
			<div class="mb-2.5">
				<button
					class="flex space-x-1"
					on:click={() => {
						goto('/workspace/tools');
					}}
Timothy J. Baek's avatar
Timothy J. Baek committed
73
					type="button"
Timothy J. Baek's avatar
Timothy J. Baek committed
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
				>
					<div class=" self-center">
						<svg
							xmlns="http://www.w3.org/2000/svg"
							viewBox="0 0 20 20"
							fill="currentColor"
							class="w-4 h-4"
						>
							<path
								fill-rule="evenodd"
								d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
								clip-rule="evenodd"
							/>
						</svg>
					</div>
					<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
				</button>
			</div>

			<div class="flex flex-col flex-1 overflow-auto h-0 rounded-lg">
				<div class="w-full mb-2 flex flex-col gap-1.5">
					<div class="flex gap-2 w-full">
						<input
							class="w-full px-3 py-2 text-sm font-medium bg-gray-50 dark:bg-gray-850 dark:text-gray-200 rounded-lg outline-none"
							type="text"
							placeholder="Toolkit Name (e.g. My ToolKit)"
							bind:value={name}
							required
						/>

						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
105
							class="w-full px-3 py-2 text-sm font-medium disabled:text-gray-300 dark:disabled:text-gray-700 bg-gray-50 dark:bg-gray-850 dark:text-gray-200 rounded-lg outline-none"
Timothy J. Baek's avatar
Timothy J. Baek committed
106
107
108
109
							type="text"
							placeholder="Toolkit ID (e.g. my_toolkit)"
							bind:value={id}
							required
Timothy J. Baek's avatar
Timothy J. Baek committed
110
							disabled={edit}
Timothy J. Baek's avatar
Timothy J. Baek committed
111
112
113
114
115
116
117
118
119
120
121
122
						/>
					</div>
					<input
						class="w-full px-3 py-2 text-sm font-medium bg-gray-50 dark:bg-gray-850 dark:text-gray-200 rounded-lg outline-none"
						type="text"
						placeholder="Toolkit Description (e.g. A toolkit for performing various operations)"
						bind:value={meta.description}
						required
					/>
				</div>

				<div class="mb-2 flex-1 overflow-auto h-0 rounded-lg">
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
123
124
125
126
					<CodeEditor
						bind:value={content}
						bind:this={codeEditor}
						on:save={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
127
128
129
							if (formElement) {
								formElement.requestSubmit();
							}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
130
131
						}}
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
132
133
				</div>

Timothy J. Baek's avatar
Timothy J. Baek committed
134
135
136
137
138
139
140
141
142
143
				<div class="pb-3 flex justify-between">
					<div class="flex-1 pr-3">
						<div class="text-xs text-gray-500 line-clamp-2">
							<span class=" font-semibold dark:text-gray-200">Warning:</span> Tools are a function
							calling system with arbitrary code execution <br />—
							<span class=" font-medium dark:text-gray-400"
								>don't install random tools from sources you don't trust.</span
							>
						</div>
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
144

Timothy J. Baek's avatar
Timothy J. Baek committed
145
146
					<button
						class="px-3 py-1.5 text-sm font-medium bg-emerald-600 hover:bg-emerald-700 text-gray-50 transition rounded-lg"
Timothy J. Baek's avatar
Timothy J. Baek committed
147
						type="submit"
Timothy J. Baek's avatar
Timothy J. Baek committed
148
149
150
151
152
					>
						{$i18n.t('Save')}
					</button>
				</div>
			</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
153
		</form>
Timothy J. Baek's avatar
Timothy J. Baek committed
154
155
	</div>
</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

<ConfirmDialog
	bind:show={showConfirm}
	on:confirm={() => {
		submitHandler();
	}}
>
	<div class="text-sm text-gray-500">
		<div class=" bg-yellow-500/20 text-yellow-700 dark:text-yellow-200 rounded-lg px-4 py-3">
			<div>Please carefully review the following warnings:</div>

			<ul class=" mt-1 list-disc pl-4 text-xs">
				<li>Tools have a function calling system that allows arbitrary code execution.</li>
				<li>Do not install tools from sources you do not fully trust.</li>
			</ul>
		</div>

		<div class="my-3">
			I acknowledge that I have read and I understand the implications of my action. I am aware of
			the risks associated with executing arbitrary code and I have verified the trustworthiness of
			the source.
		</div>
	</div>
</ConfirmDialog>