Timothy J. Baek преди 5 месеца
родител
ревизия
9ea9e8478a

+ 1 - 1
src/lib/components/workspace/Functions.svelte

@@ -184,7 +184,7 @@
 	</title>
 </svelte:head>
 
-<div class="flex flex-col gap-0.5 mt-1 mb-2">
+<div class="flex flex-col gap-1 mt-1.5 mb-2">
 	<div class="flex justify-between items-center">
 		<div class="flex md:self-center text-xl items-center font-medium px-0.5">
 			{$i18n.t('Functions')}

+ 1 - 1
src/lib/components/workspace/Knowledge.svelte

@@ -75,7 +75,7 @@
 	}}
 />
 
-<div class="flex flex-col gap-0.5 mt-1 mb-2">
+<div class="flex flex-col gap-1 mt-1.5 mb-2">
 	<div class="flex justify-between items-center">
 		<div class="flex md:self-center text-xl font-medium px-0.5 items-center">
 			{$i18n.t('Knowledge')}

+ 1 - 1
src/lib/components/workspace/Models.svelte

@@ -306,7 +306,7 @@
 	}}
 />
 
-<div class="flex flex-col gap-0.5 mt-1 mb-2">
+<div class="flex flex-col gap-1 mt-1.5 mb-2">
 	<div class="flex justify-between items-center">
 		<div class="flex items-center md:self-center text-xl font-medium px-0.5">
 			{$i18n.t('Models')}

+ 2 - 2
src/lib/components/workspace/Models/Capabilities.svelte

@@ -23,9 +23,9 @@
 	<div class="flex w-full justify-between mb-1">
 		<div class=" self-center text-sm font-semibold">{$i18n.t('Capabilities')}</div>
 	</div>
-	<div class="flex flex-col">
+	<div class="flex">
 		{#each Object.keys(capabilities) as capability}
-			<div class=" flex items-center gap-2">
+			<div class=" flex items-center gap-2 mr-3">
 				<Checkbox
 					state={capabilities[capability] ? 'checked' : 'unchecked'}
 					on:change={(e) => {

+ 329 - 360
src/lib/components/workspace/Models/ModelEditor.svelte

@@ -149,19 +149,6 @@
 			await tick();
 
 			id = model.id;
-			info = {
-				...info,
-				...JSON.parse(
-					JSON.stringify(
-						model?.info
-							? model?.info
-							: {
-									id: model.id,
-									name: model.name
-								}
-					)
-				)
-			};
 
 			if (model.info.base_model_id) {
 				const base_model = $models
@@ -214,7 +201,16 @@
 
 			info = {
 				...info,
-				...model.info
+				...JSON.parse(
+					JSON.stringify(
+						model?.info
+							? model?.info
+							: {
+									id: model.id,
+									name: model.name
+								}
+					)
+				)
 			};
 
 			console.log(model);
@@ -290,42 +286,17 @@
 		}}
 	/>
 
-	<button
-		class="flex space-x-1"
-		on:click={() => {
-			goto('/workspace/models');
-		}}
-	>
-		<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>
-
 	{#if !edit || model}
 		<form
-			class="flex flex-col max-w-2xl mx-auto mt-4 mb-10"
+			class="flex flex-col md:flex-row mx-auto gap-3 md:gap-6"
 			on:submit|preventDefault={() => {
 				submitHandler();
 			}}
 		>
-			<div class="flex justify-center my-4">
+			<div class="self-center md:self-start flex justify-center my-2 flex-shrink-0">
 				<div class="self-center">
 					<button
-						class=" {info.meta.profile_image_url
-							? ''
-							: 'p-4'} rounded-full border border-dashed border-gray-200 flex items-center"
+						class="rounded-2xl flex flex-shrink-0 items-center bg-white"
 						type="button"
 						on:click={() => {
 							filesInputElement.click();
@@ -334,187 +305,87 @@
 						{#if info.meta.profile_image_url}
 							<img
 								src={info.meta.profile_image_url}
-								alt="modelfile profile"
-								class=" rounded-full size-16 object-cover"
+								alt="model profile"
+								class="rounded-lg size-64 object-cover shrink-0"
 							/>
 						{:else}
-							<svg
-								xmlns="http://www.w3.org/2000/svg"
-								viewBox="0 0 24 24"
-								fill="currentColor"
-								class="size-8"
-							>
-								<path
-									fill-rule="evenodd"
-									d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z"
-									clip-rule="evenodd"
-								/>
-							</svg>
-						{/if}
-					</button>
-				</div>
-			</div>
-
-			<div class="mt-2 my-1 flex space-x-2">
-				<div class="flex-1">
-					<div class=" text-sm font-semibold mb-1">{$i18n.t('Name')}*</div>
-
-					<div>
-						<input
-							class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
-							placeholder={$i18n.t('Name your model')}
-							bind:value={name}
-							required
-						/>
-					</div>
-				</div>
-
-				<div class="flex-1">
-					<div class=" text-sm font-semibold mb-1">{$i18n.t('Model ID')}*</div>
-
-					<div>
-						<input
-							class="px-3 py-1.5 text-sm w-full bg-transparent disabled:text-gray-500 border dark:border-gray-600 outline-none rounded-lg"
-							placeholder={$i18n.t('Add a model id')}
-							value={id}
-							disabled={edit}
-							required
-						/>
-					</div>
-				</div>
-			</div>
-
-			{#if !edit || model.preset}
-				<div class="my-1">
-					<div class=" text-sm font-semibold mb-1">{$i18n.t('Base Model (From)')}</div>
-
-					<div>
-						<select
-							class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
-							placeholder="Select a base model (e.g. llama3, gpt-4o)"
-							bind:value={info.base_model_id}
-							on:change={(e) => {
-								addUsage(e.target.value);
-							}}
-							required
-						>
-							<option value={null} class=" text-gray-900">{$i18n.t('Select a base model')}</option>
-							{#each $models.filter((m) => (model ? m.id !== model.id : true) && !m?.preset && m?.owned_by !== 'arena') as model}
-								<option value={model.id} class=" text-gray-900">{model.name}</option>
-							{/each}
-						</select>
-					</div>
-				</div>
-			{/if}
-
-			<div class="my-1">
-				<div class="flex w-full justify-between items-center">
-					<div class=" self-center text-sm font-semibold">{$i18n.t('Description')}</div>
-
-					<button
-						class="p-1 text-xs flex rounded transition"
-						type="button"
-						on:click={() => {
-							if (info.meta.description === null) {
-								info.meta.description = '';
-							} else {
-								info.meta.description = null;
-							}
-						}}
-					>
-						{#if info.meta.description === null}
-							<span class="ml-2 self-center">{$i18n.t('Default')}</span>
-						{:else}
-							<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+							<img
+								src="/static/favicon.png"
+								alt="model profile"
+								class=" rounded-lg size-64 object-cover shrink-0"
+							/>
 						{/if}
 					</button>
 				</div>
-
-				{#if info.meta.description !== null}
-					<textarea
-						class="mt-1 px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
-						placeholder={$i18n.t('Add a short description about what this model does')}
-						bind:value={info.meta.description}
-						row="3"
-					/>
-				{/if}
 			</div>
 
-			<hr class=" dark:border-gray-850 my-1" />
-
-			<div class="my-2">
-				<div class="flex w-full justify-between">
-					<div class=" self-center text-sm font-semibold">{$i18n.t('Model Params')}</div>
-				</div>
-
-				<!-- <div class=" text-sm font-semibold mb-2"></div> -->
-
-				<div class="mt-2">
-					<div class="my-1">
-						<div class=" text-xs font-semibold mb-2">{$i18n.t('System Prompt')}</div>
+			<div>
+				<div class="mt-2 my-2 flex flex-col">
+					<div class="flex-1">
 						<div>
-							<Textarea
-								className="px-3 py-2 text-sm w-full bg-transparent border dark:border-gray-600 outline-none resize-none overflow-y-hidden rounded-lg "
-								placeholder={`Write your model system prompt content here\ne.g.) You are Mario from Super Mario Bros, acting as an assistant.`}
-								rows={4}
-								bind:value={info.params.system}
+							<input
+								class="text-3xl font-semibold w-full bg-transparent outline-none"
+								placeholder={$i18n.t('Model Name')}
+								bind:value={name}
+								required
 							/>
 						</div>
 					</div>
 
-					<div class="flex w-full justify-between">
-						<div class=" self-center text-xs font-semibold">
-							{$i18n.t('Advanced Params')}
+					<div class="flex-1">
+						<!-- <div class=" text-sm font-semibold">{$i18n.t('Model ID')}*</div> -->
+						<div>
+							<input
+								class="text-xs w-full bg-transparent text-gray-500 outline-none rounded-lg"
+								placeholder={$i18n.t('Model ID')}
+								value={id}
+								disabled={edit}
+								required
+							/>
 						</div>
-
-						<button
-							class="p-1 px-3 text-xs flex rounded transition"
-							type="button"
-							on:click={() => {
-								showAdvanced = !showAdvanced;
-							}}
-						>
-							{#if showAdvanced}
-								<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
-							{:else}
-								<span class="ml-2 self-center">{$i18n.t('Show')}</span>
-							{/if}
-						</button>
 					</div>
+				</div>
+
+				{#if !edit || model.preset}
+					<div class="my-1">
+						<div class=" text-sm font-semibold mb-1">{$i18n.t('Base Model (From)')}</div>
 
-					{#if showAdvanced}
-						<div class="my-2">
-							<AdvancedParams
-								admin={true}
-								bind:params
+						<div>
+							<select
+								class="text-sm w-full bg-transparent outline-none rounded-lg"
+								placeholder="Select a base model (e.g. llama3, gpt-4o)"
+								bind:value={info.base_model_id}
 								on:change={(e) => {
-									info.params = { ...info.params, ...params };
+									addUsage(e.target.value);
 								}}
-							/>
+								required
+							>
+								<option value={null} class=" text-gray-900">{$i18n.t('Select a base model')}</option
+								>
+								{#each $models.filter((m) => (model ? m.id !== model.id : true) && !m?.preset && m?.owned_by !== 'arena') as model}
+									<option value={model.id} class=" text-gray-900">{model.name}</option>
+								{/each}
+							</select>
 						</div>
-					{/if}
-				</div>
-			</div>
-
-			<hr class=" dark:border-gray-850 my-1" />
+					</div>
+				{/if}
 
-			<div class="my-2">
-				<div class="flex w-full justify-between items-center">
-					<div class="flex w-full justify-between items-center">
-						<div class=" self-center text-sm font-semibold">{$i18n.t('Prompt suggestions')}</div>
+				<div class="my-1">
+					<div class="mb-1 flex w-full justify-between items-center">
+						<div class=" self-center text-sm font-semibold">{$i18n.t('Description')}</div>
 
 						<button
 							class="p-1 text-xs flex rounded transition"
 							type="button"
 							on:click={() => {
-								if ((info?.meta?.suggestion_prompts ?? null) === null) {
-									info.meta.suggestion_prompts = [{ content: '' }];
+								if (info.meta.description === null) {
+									info.meta.description = '';
 								} else {
-									info.meta.suggestion_prompts = null;
+									info.meta.description = null;
 								}
 							}}
 						>
-							{#if (info?.meta?.suggestion_prompts ?? null) === null}
+							{#if info.meta.description === null}
 								<span class="ml-2 self-center">{$i18n.t('Default')}</span>
 							{:else}
 								<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
@@ -522,198 +393,296 @@
 						</button>
 					</div>
 
-					{#if (info?.meta?.suggestion_prompts ?? null) !== null}
-						<button
-							class="p-1 px-2 text-xs flex rounded transition"
-							type="button"
-							on:click={() => {
-								if (
-									info.meta.suggestion_prompts.length === 0 ||
-									info.meta.suggestion_prompts.at(-1).content !== ''
-								) {
-									info.meta.suggestion_prompts = [...info.meta.suggestion_prompts, { content: '' }];
-								}
-							}}
-						>
-							<svg
-								xmlns="http://www.w3.org/2000/svg"
-								viewBox="0 0 20 20"
-								fill="currentColor"
-								class="w-4 h-4"
-							>
-								<path
-									d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
-								/>
-							</svg>
-						</button>
+					{#if info.meta.description !== null}
+						<Textarea
+							className=" text-sm w-full bg-transparent outline-none resize-none overflow-y-hidden "
+							placeholder={$i18n.t('Add a short description about what this model does')}
+							rows={3}
+							bind:value={info.meta.description}
+						/>
 					{/if}
 				</div>
 
-				{#if info?.meta?.suggestion_prompts}
-					<div class="flex flex-col space-y-1 mt-2">
-						{#if info.meta.suggestion_prompts.length > 0}
-							{#each info.meta.suggestion_prompts as prompt, promptIdx}
-								<div class=" flex border dark:border-gray-600 rounded-lg">
-									<input
-										class="px-3 py-1.5 text-sm w-full bg-transparent outline-none border-r dark:border-gray-600"
-										placeholder={$i18n.t('Write a prompt suggestion (e.g. Who are you?)')}
-										bind:value={prompt.content}
-									/>
+				<hr class=" dark:border-gray-850 my-1.5" />
 
-									<button
-										class="px-2"
-										type="button"
-										on:click={() => {
-											info.meta.suggestion_prompts.splice(promptIdx, 1);
-											info.meta.suggestion_prompts = info.meta.suggestion_prompts;
-										}}
-									>
-										<svg
-											xmlns="http://www.w3.org/2000/svg"
-											viewBox="0 0 20 20"
-											fill="currentColor"
-											class="w-4 h-4"
-										>
-											<path
-												d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
-											/>
-										</svg>
-									</button>
-								</div>
-							{/each}
-						{:else}
-							<div class="text-xs text-center">No suggestion prompts</div>
+				<div class="my-2">
+					<div class="flex w-full justify-between">
+						<div class=" self-center text-sm font-semibold">{$i18n.t('Model Params')}</div>
+					</div>
+
+					<!-- <div class=" text-sm font-semibold mb-2"></div> -->
+
+					<div class="mt-2">
+						<div class="my-1">
+							<div class=" text-xs font-semibold mb-2">{$i18n.t('System Prompt')}</div>
+							<div>
+								<Textarea
+									className=" text-sm w-full bg-transparent outline-none resize-none overflow-y-hidden "
+									placeholder={`Write your model system prompt content here\ne.g.) You are Mario from Super Mario Bros, acting as an assistant.`}
+									rows={4}
+									bind:value={info.params.system}
+								/>
+							</div>
+						</div>
+
+						<div class="flex w-full justify-between">
+							<div class=" self-center text-xs font-semibold">
+								{$i18n.t('Advanced Params')}
+							</div>
+
+							<button
+								class="p-1 px-3 text-xs flex rounded transition"
+								type="button"
+								on:click={() => {
+									showAdvanced = !showAdvanced;
+								}}
+							>
+								{#if showAdvanced}
+									<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
+								{:else}
+									<span class="ml-2 self-center">{$i18n.t('Show')}</span>
+								{/if}
+							</button>
+						</div>
+
+						{#if showAdvanced}
+							<div class="my-2">
+								<AdvancedParams
+									admin={true}
+									bind:params
+									on:change={(e) => {
+										info.params = { ...info.params, ...params };
+									}}
+								/>
+							</div>
 						{/if}
 					</div>
-				{/if}
-			</div>
+				</div>
 
-			<div class="my-2">
-				<Knowledge bind:selectedKnowledge={knowledge} collections={$knowledgeCollections} />
-			</div>
+				<hr class=" dark:border-gray-850 my-1" />
 
-			<div class="my-2">
-				<ToolsSelector bind:selectedToolIds={toolIds} tools={$tools} />
-			</div>
+				<div class="my-2">
+					<div class="flex w-full justify-between items-center">
+						<div class="flex w-full justify-between items-center">
+							<div class=" self-center text-sm font-semibold">{$i18n.t('Prompt suggestions')}</div>
+
+							<button
+								class="p-1 text-xs flex rounded transition"
+								type="button"
+								on:click={() => {
+									if ((info?.meta?.suggestion_prompts ?? null) === null) {
+										info.meta.suggestion_prompts = [{ content: '' }];
+									} else {
+										info.meta.suggestion_prompts = null;
+									}
+								}}
+							>
+								{#if (info?.meta?.suggestion_prompts ?? null) === null}
+									<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+								{:else}
+									<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+								{/if}
+							</button>
+						</div>
 
-			<div class="my-2">
-				<FiltersSelector
-					bind:selectedFilterIds={filterIds}
-					filters={$functions.filter((func) => func.type === 'filter')}
-				/>
-			</div>
+						{#if (info?.meta?.suggestion_prompts ?? null) !== null}
+							<button
+								class="p-1 px-2 text-xs flex rounded transition"
+								type="button"
+								on:click={() => {
+									if (
+										info.meta.suggestion_prompts.length === 0 ||
+										info.meta.suggestion_prompts.at(-1).content !== ''
+									) {
+										info.meta.suggestion_prompts = [
+											...info.meta.suggestion_prompts,
+											{ content: '' }
+										];
+									}
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 20 20"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
+									/>
+								</svg>
+							</button>
+						{/if}
+					</div>
 
-			<div class="my-2">
-				<ActionsSelector
-					bind:selectedActionIds={actionIds}
-					actions={$functions.filter((func) => func.type === 'action')}
-				/>
-			</div>
+					{#if info?.meta?.suggestion_prompts}
+						<div class="flex flex-col space-y-1 mt-2">
+							{#if info.meta.suggestion_prompts.length > 0}
+								{#each info.meta.suggestion_prompts as prompt, promptIdx}
+									<div class=" flex border dark:border-gray-600 rounded-lg">
+										<input
+											class="px-3 py-1.5 text-sm w-full bg-transparent outline-none border-r dark:border-gray-600"
+											placeholder={$i18n.t('Write a prompt suggestion (e.g. Who are you?)')}
+											bind:value={prompt.content}
+										/>
+
+										<button
+											class="px-2"
+											type="button"
+											on:click={() => {
+												info.meta.suggestion_prompts.splice(promptIdx, 1);
+												info.meta.suggestion_prompts = info.meta.suggestion_prompts;
+											}}
+										>
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												viewBox="0 0 20 20"
+												fill="currentColor"
+												class="w-4 h-4"
+											>
+												<path
+													d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+												/>
+											</svg>
+										</button>
+									</div>
+								{/each}
+							{:else}
+								<div class="text-xs text-center">No suggestion prompts</div>
+							{/if}
+						</div>
+					{/if}
+				</div>
 
-			<div class="my-2">
-				<Capabilities bind:capabilities />
-			</div>
+				<hr class=" dark:border-gray-850 my-1.5" />
 
-			<div class="my-1">
-				<div class="flex w-full justify-between items-center">
-					<div class=" self-center text-sm font-semibold">{$i18n.t('Tags')}</div>
+				<div class="my-2">
+					<Knowledge bind:selectedKnowledge={knowledge} collections={$knowledgeCollections} />
 				</div>
 
-				<div class="mt-2">
-					<Tags
-						tags={info?.meta?.tags ?? []}
-						on:delete={(e) => {
-							const tagName = e.detail;
-							info.meta.tags = info.meta.tags.filter((tag) => tag.name !== tagName);
-						}}
-						on:add={(e) => {
-							const tagName = e.detail;
-							if (!(info?.meta?.tags ?? null)) {
-								info.meta.tags = [{ name: tagName }];
-							} else {
-								info.meta.tags = [...info.meta.tags, { name: tagName }];
-							}
-						}}
+				<div class="my-2">
+					<ToolsSelector bind:selectedToolIds={toolIds} tools={$tools} />
+				</div>
+
+				<div class="my-2">
+					<FiltersSelector
+						bind:selectedFilterIds={filterIds}
+						filters={$functions.filter((func) => func.type === 'filter')}
 					/>
 				</div>
-			</div>
 
-			<div class="my-2 text-gray-300 dark:text-gray-700">
-				<div class="flex w-full justify-between mb-2">
-					<div class=" self-center text-sm font-semibold">{$i18n.t('JSON Preview')}</div>
+				<div class="my-2">
+					<ActionsSelector
+						bind:selectedActionIds={actionIds}
+						actions={$functions.filter((func) => func.type === 'action')}
+					/>
+				</div>
 
-					<button
-						class="p-1 px-3 text-xs flex rounded transition"
-						type="button"
-						on:click={() => {
-							showPreview = !showPreview;
-						}}
-					>
-						{#if showPreview}
-							<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
-						{:else}
-							<span class="ml-2 self-center">{$i18n.t('Show')}</span>
-						{/if}
-					</button>
+				<div class="my-2">
+					<Capabilities bind:capabilities />
 				</div>
 
-				{#if showPreview}
-					<div>
-						<textarea
-							class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg"
-							rows="10"
-							value={JSON.stringify(info, null, 2)}
-							disabled
-							readonly
+				<div class="my-1">
+					<div class="flex w-full justify-between items-center">
+						<div class=" self-center text-sm font-semibold">{$i18n.t('Tags')}</div>
+					</div>
+
+					<div class="mt-2">
+						<Tags
+							tags={info?.meta?.tags ?? []}
+							on:delete={(e) => {
+								const tagName = e.detail;
+								info.meta.tags = info.meta.tags.filter((tag) => tag.name !== tagName);
+							}}
+							on:add={(e) => {
+								const tagName = e.detail;
+								if (!(info?.meta?.tags ?? null)) {
+									info.meta.tags = [{ name: tagName }];
+								} else {
+									info.meta.tags = [...info.meta.tags, { name: tagName }];
+								}
+							}}
 						/>
 					</div>
-				{/if}
-			</div>
+				</div>
 
-			<div class="my-2 flex justify-end mb-20">
-				<button
-					class=" text-sm px-3 py-2 transition rounded-xl {loading
-						? ' cursor-not-allowed bg-gray-100 dark:bg-gray-800'
-						: ' bg-gray-50 hover:bg-gray-100 dark:bg-gray-700 dark:hover:bg-gray-800'} flex"
-					type="submit"
-					disabled={loading}
-				>
-					<div class=" self-center font-medium">
-						{#if edit}
-							{$i18n.t('Save & Update')}
-						{:else}
-							{$i18n.t('Save & Create')}
-						{/if}
+				<div class="my-2 text-gray-300 dark:text-gray-700">
+					<div class="flex w-full justify-between mb-2">
+						<div class=" self-center text-sm font-semibold">{$i18n.t('JSON Preview')}</div>
+
+						<button
+							class="p-1 px-3 text-xs flex rounded transition"
+							type="button"
+							on:click={() => {
+								showPreview = !showPreview;
+							}}
+						>
+							{#if showPreview}
+								<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
+							{:else}
+								<span class="ml-2 self-center">{$i18n.t('Show')}</span>
+							{/if}
+						</button>
 					</div>
 
-					{#if loading}
-						<div class="ml-1.5 self-center">
-							<svg
-								class=" w-4 h-4"
-								viewBox="0 0 24 24"
-								fill="currentColor"
-								xmlns="http://www.w3.org/2000/svg"
-								><style>
-									.spinner_ajPY {
-										transform-origin: center;
-										animation: spinner_AtaB 0.75s infinite linear;
-									}
-									@keyframes spinner_AtaB {
-										100% {
-											transform: rotate(360deg);
-										}
-									}
-								</style><path
-									d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
-									opacity=".25"
-								/><path
-									d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
-									class="spinner_ajPY"
-								/></svg
-							>
+					{#if showPreview}
+						<div>
+							<textarea
+								class="text-sm w-full bg-transparent outline-none resize-none"
+								rows="10"
+								value={JSON.stringify(info, null, 2)}
+								disabled
+								readonly
+							/>
 						</div>
 					{/if}
-				</button>
+				</div>
+
+				<div class="my-2 flex justify-end mb-20">
+					<button
+						class=" text-sm px-3 py-2 transition rounded-xl {loading
+							? ' cursor-not-allowed bg-gray-100 dark:bg-gray-800'
+							: ' bg-gray-50 hover:bg-gray-100 dark:bg-white dark:hover:bg-gray-100 dark:text-black'} flex w-full justify-center"
+						type="submit"
+						disabled={loading}
+					>
+						<div class=" self-center font-medium">
+							{#if edit}
+								{$i18n.t('Save & Update')}
+							{:else}
+								{$i18n.t('Save & Create')}
+							{/if}
+						</div>
+
+						{#if loading}
+							<div class="ml-1.5 self-center">
+								<svg
+									class=" w-4 h-4"
+									viewBox="0 0 24 24"
+									fill="currentColor"
+									xmlns="http://www.w3.org/2000/svg"
+									><style>
+										.spinner_ajPY {
+											transform-origin: center;
+											animation: spinner_AtaB 0.75s infinite linear;
+										}
+										@keyframes spinner_AtaB {
+											100% {
+												transform: rotate(360deg);
+											}
+										}
+									</style><path
+										d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+										opacity=".25"
+									/><path
+										d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+										class="spinner_ajPY"
+									/></svg
+								>
+							</div>
+						{/if}
+					</button>
+				</div>
 			</div>
 		</form>
 	{/if}

+ 1 - 1
src/lib/components/workspace/Prompts.svelte

@@ -81,7 +81,7 @@
 	</div>
 </DeleteConfirmDialog>
 
-<div class="flex flex-col gap-0.5 mt-1 mb-2">
+<div class="flex flex-col gap-1 mt-1.5 mb-2">
 	<div class="flex justify-between items-center">
 		<div class="flex md:self-center text-xl font-medium px-0.5 items-center">
 			{$i18n.t('Prompts')}

+ 1 - 1
src/lib/components/workspace/Tools.svelte

@@ -156,7 +156,7 @@
 	</title>
 </svelte:head>
 
-<div class="flex flex-col gap-0.5 mt-1 mb-2">
+<div class="flex flex-col gap-1 mt-1.5 mb-2">
 	<div class="flex justify-between items-center">
 		<div class="flex md:self-center text-xl font-medium px-0.5 items-center">
 			{$i18n.t('Tools')}