This commit is contained in:
Timothy Jaeryang Baek 2026-07-26 17:51:49 -04:00
parent c882222f68
commit 421834b2de
2 changed files with 114 additions and 445 deletions

View file

@ -3,9 +3,11 @@
import Textarea from '$lib/components/common/Textarea.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import Plus from '$lib/components/icons/Plus.svelte';
import type { i18n as i18nType } from 'i18next';
import { getContext } from 'svelte';
import type { Writable } from 'svelte/store';
const i18n = getContext('i18n');
const i18n = getContext<Writable<i18nType>>('i18n');
export let onChange: (params: any) => void = () => {};
@ -48,13 +50,49 @@
num_thread: null,
num_gpu: null
};
type RangeParamKey = keyof typeof defaultParams;
export let params = defaultParams;
export let params: any = defaultParams;
$: if (params) {
onChange(params);
}
</script>
{#snippet rangeParam(
key: RangeParamKey,
label: string,
min: number,
max: number,
rangeStep: number | string,
numberStep: number | string = 'any',
numberMax: number | undefined = max
)}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
type="range"
aria-label={label}
{min}
{max}
step={rangeStep}
bind:value={params[key]}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params[key]}
type="number"
aria-label={label}
class=" bg-transparent text-center w-14"
{min}
max={numberMax}
step={numberStep}
/>
</div>
</div>
{/snippet}
<div
class={layout === 'grid'
? 'grid grid-cols-1 gap-x-5 gap-y-1 pb-safe-bottom text-xs text-gray-600 dark:text-gray-400 sm:grid-cols-2 lg:grid-cols-3'
@ -127,28 +165,15 @@
</Tooltip>
{#if (params?.stream_delta_chunk_size ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="1"
max="128"
step="1"
bind:value={params.stream_delta_chunk_size}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.stream_delta_chunk_size}
type="number"
class=" bg-transparent text-center w-14"
min="1"
step="any"
/>
</div>
</div>
{@render rangeParam(
'stream_delta_chunk_size',
$i18n.t('Stream Delta Chunk Size'),
1,
128,
1,
'any',
undefined
)}
{/if}
</div>
@ -187,6 +212,7 @@
<input
class="text-sm w-full bg-transparent outline-hidden outline-none"
type="number"
aria-label={$i18n.t('Context Compaction Threshold')}
placeholder={$i18n.t('Enter token threshold')}
bind:value={params.compact_token_threshold}
autocomplete="off"
@ -282,6 +308,7 @@
<input
class="text-sm w-full bg-transparent outline-hidden outline-none"
type="text"
aria-label={$i18n.t('Start Tag')}
placeholder={$i18n.t('Start Tag')}
bind:value={params.reasoning_tags[0]}
autocomplete="off"
@ -292,6 +319,7 @@
<input
class="text-sm w-full bg-transparent outline-hidden outline-none"
type="text"
aria-label={$i18n.t('End Tag')}
placeholder={$i18n.t('End Tag')}
bind:value={params.reasoning_tags[1]}
autocomplete="off"
@ -336,6 +364,7 @@
<input
class="text-sm w-full bg-transparent outline-hidden outline-none"
type="number"
aria-label={$i18n.t('Seed')}
placeholder={$i18n.t('Enter Seed')}
bind:value={params.seed}
autocomplete="off"
@ -381,6 +410,7 @@
<input
class="text-sm w-full bg-transparent outline-hidden outline-none"
type="text"
aria-label={$i18n.t('Stop Sequence')}
placeholder={$i18n.t('Enter stop sequence')}
bind:value={params.stop}
autocomplete="off"
@ -419,29 +449,7 @@
</Tooltip>
{#if (params?.temperature ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="2"
step="0.05"
bind:value={params.temperature}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.temperature}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="2"
step="any"
/>
</div>
</div>
{@render rangeParam('temperature', $i18n.t('Temperature'), 0, 2, 0.05)}
{/if}
</div>
@ -479,6 +487,7 @@
<input
class="text-sm w-full bg-transparent outline-hidden outline-none"
type="text"
aria-label={$i18n.t('Reasoning Effort')}
placeholder={$i18n.t('Enter reasoning effort')}
bind:value={params.reasoning_effort}
autocomplete="off"
@ -522,6 +531,7 @@
<input
class="text-sm w-full bg-transparent outline-hidden outline-none"
type="text"
aria-label="logit_bias"
placeholder={$i18n.t(
'Enter comma-separated "token:bias_value" pairs (example: 5432:100, 413:-100)'
)}
@ -563,28 +573,7 @@
</Tooltip>
{#if (params?.max_tokens ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-2"
max="131072"
step="1"
bind:value={params.max_tokens}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.max_tokens}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
step="1"
/>
</div>
</div>
{@render rangeParam('max_tokens', 'max_tokens', -2, 131072, 1, 1, undefined)}
{/if}
</div>
@ -617,29 +606,7 @@
</Tooltip>
{#if (params?.top_k ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="1000"
step="0.5"
bind:value={params.top_k}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.top_k}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="100"
step="any"
/>
</div>
</div>
{@render rangeParam('top_k', 'top_k', 0, 1000, 0.5, 'any', 100)}
{/if}
</div>
@ -673,29 +640,7 @@
</Tooltip>
{#if (params?.top_p ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="1"
step="0.05"
bind:value={params.top_p}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.top_p}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="1"
step="any"
/>
</div>
</div>
{@render rangeParam('top_p', 'top_p', 0, 1, 0.05)}
{/if}
</div>
@ -728,29 +673,7 @@
</Tooltip>
{#if (params?.min_p ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="1"
step="0.05"
bind:value={params.min_p}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.min_p}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="1"
step="any"
/>
</div>
</div>
{@render rangeParam('min_p', 'min_p', 0, 1, 0.05)}
{/if}
</div>
@ -784,29 +707,7 @@
</Tooltip>
{#if (params?.frequency_penalty ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-2"
max="2"
step="0.05"
bind:value={params.frequency_penalty}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.frequency_penalty}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
max="2"
step="any"
/>
</div>
</div>
{@render rangeParam('frequency_penalty', 'frequency_penalty', -2, 2, 0.05)}
{/if}
</div>
@ -840,29 +741,7 @@
</Tooltip>
{#if (params?.presence_penalty ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-2"
max="2"
step="0.05"
bind:value={params.presence_penalty}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.presence_penalty}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
max="2"
step="any"
/>
</div>
</div>
{@render rangeParam('presence_penalty', 'presence_penalty', -2, 2, 0.05)}
{/if}
</div>
@ -893,29 +772,7 @@
</Tooltip>
{#if (params?.mirostat ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="2"
step="1"
bind:value={params.mirostat}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.mirostat}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="2"
step="1"
/>
</div>
</div>
{@render rangeParam('mirostat', 'mirostat', 0, 2, 1, 1)}
{/if}
</div>
@ -948,29 +805,7 @@
</Tooltip>
{#if (params?.mirostat_eta ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="1"
step="0.05"
bind:value={params.mirostat_eta}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.mirostat_eta}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="1"
step="any"
/>
</div>
</div>
{@render rangeParam('mirostat_eta', 'mirostat_eta', 0, 1, 0.05)}
{/if}
</div>
@ -1004,29 +839,7 @@
</Tooltip>
{#if (params?.mirostat_tau ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="10"
step="0.5"
bind:value={params.mirostat_tau}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.mirostat_tau}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="10"
step="any"
/>
</div>
</div>
{@render rangeParam('mirostat_tau', 'mirostat_tau', 0, 10, 0.5)}
{/if}
</div>
@ -1058,29 +871,7 @@
</Tooltip>
{#if (params?.repeat_last_n ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-1"
max="128"
step="1"
bind:value={params.repeat_last_n}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.repeat_last_n}
type="number"
class=" bg-transparent text-center w-14"
min="-1"
max="128"
step="1"
/>
</div>
</div>
{@render rangeParam('repeat_last_n', 'repeat_last_n', -1, 128, 1, 1)}
{/if}
</div>
@ -1114,29 +905,7 @@
</Tooltip>
{#if (params?.tfs_z ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="2"
step="0.05"
bind:value={params.tfs_z}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.tfs_z}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="2"
step="any"
/>
</div>
</div>
{@render rangeParam('tfs_z', 'tfs_z', 0, 2, 0.05)}
{/if}
</div>
@ -1170,29 +939,7 @@
</Tooltip>
{#if (params?.repeat_penalty ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-2"
max="2"
step="0.05"
bind:value={params.repeat_penalty}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.repeat_penalty}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
max="2"
step="any"
/>
</div>
</div>
{@render rangeParam('repeat_penalty', 'repeat_penalty', -2, 2, 0.05)}
{/if}
</div>
@ -1231,7 +978,7 @@
{params.use_mmap ? $i18n.t('Enabled') : $i18n.t('Disabled')}
</div>
<div class=" pr-2">
<Switch bind:state={params.use_mmap} />
<Switch bind:state={params.use_mmap} ariaLabel="use_mmap" />
</div>
</div>
{/if}
@ -1273,7 +1020,7 @@
</div>
<div class=" pr-2">
<Switch bind:state={params.use_mlock} />
<Switch bind:state={params.use_mlock} ariaLabel="use_mlock" />
</div>
</div>
{/if}
@ -1326,6 +1073,7 @@
<input
class="text-sm w-full bg-transparent outline-hidden outline-none"
type="text"
aria-label={`think (${$i18n.t('Ollama')})`}
placeholder={$i18n.t("e.g. 'low', 'medium', 'high'")}
bind:value={params.think}
autocomplete="off"
@ -1365,6 +1113,7 @@
<div class="flex mt-0.5 space-x-2">
<Textarea
className="w-full text-sm bg-transparent outline-hidden"
ariaLabel={`format (${$i18n.t('Ollama')})`}
placeholder={$i18n.t('e.g. "json" or a JSON schema')}
bind:value={params.format}
/>
@ -1402,28 +1151,15 @@
</Tooltip>
{#if (params?.num_keep ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-1"
max="10240000"
step="1"
bind:value={params.num_keep}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_keep}
type="number"
class=" bg-transparent text-center w-14"
min="-1"
step="1"
/>
</div>
</div>
{@render rangeParam(
'num_keep',
`num_keep (${$i18n.t('Ollama')})`,
-1,
10240000,
1,
1,
undefined
)}
{/if}
</div>
@ -1455,28 +1191,15 @@
</Tooltip>
{#if (params?.num_ctx ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="-1"
max="10240000"
step="1"
bind:value={params.num_ctx}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_ctx}
type="number"
class=" bg-transparent text-center w-14"
min="-1"
step="1"
/>
</div>
</div>
{@render rangeParam(
'num_ctx',
`num_ctx (${$i18n.t('Ollama')})`,
-1,
10240000,
1,
1,
undefined
)}
{/if}
</div>
@ -1510,28 +1233,15 @@
</Tooltip>
{#if (params?.num_batch ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="256"
max="8192"
step="256"
bind:value={params.num_batch}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={params.num_batch}
type="number"
class=" bg-transparent text-center w-14"
min="256"
step="256"
/>
</div>
</div>
{@render rangeParam(
'num_batch',
`num_batch (${$i18n.t('Ollama')})`,
256,
8192,
256,
256,
undefined
)}
{/if}
</div>
@ -1566,29 +1276,7 @@
</Tooltip>
{#if (params?.num_thread ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="1"
max="256"
step="1"
bind:value={params.num_thread}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_thread}
type="number"
class=" bg-transparent text-center w-14"
min="1"
max="256"
step="1"
/>
</div>
</div>
{@render rangeParam('num_thread', `num_thread (${$i18n.t('Ollama')})`, 1, 256, 1, 1)}
{/if}
</div>
@ -1622,29 +1310,7 @@
</Tooltip>
{#if (params?.num_gpu ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="0"
max="256"
step="1"
bind:value={params.num_gpu}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_gpu}
type="number"
class=" bg-transparent text-center w-14"
min="0"
max="256"
step="1"
/>
</div>
</div>
{@render rangeParam('num_gpu', `num_gpu (${$i18n.t('Ollama')})`, 0, 256, 1, 1)}
{/if}
</div>
@ -1681,6 +1347,7 @@
<input
class="w-full text-sm bg-transparent outline-hidden"
type="text"
aria-label={`keep_alive (${$i18n.t('Ollama')})`}
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
bind:value={params.keep_alive}
/>
@ -1697,10 +1364,11 @@
<input
type="text"
class=" text-xs w-full bg-transparent outline-none"
aria-label={$i18n.t('Custom Parameter Name')}
placeholder={$i18n.t('Custom Parameter Name')}
value={key}
on:change={(e) => {
const newKey = e.target.value.trim();
const newKey = e.currentTarget.value.trim();
if (newKey && newKey !== key) {
params.custom_params[newKey] = params.custom_params[key];
delete params.custom_params[key];
@ -1732,6 +1400,7 @@
bind:value={params.custom_params[key]}
type="text"
class="text-sm w-full bg-transparent outline-hidden outline-none"
aria-label={$i18n.t('Custom Parameter Value')}
placeholder={$i18n.t('Custom Parameter Value')}
/>
</div>

View file

@ -4,18 +4,18 @@
export let value = '';
export let placeholder = '';
export let rows = 1;
export let minSize = null;
export let maxSize = null;
export let minSize: number | null = null;
export let maxSize: number | null = null;
export let required = false;
export let readonly = false;
export let className =
'w-full rounded-lg px-3.5 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden h-full';
export let ariaLabel = null;
export let ariaLabel: string | null = null;
export let onInput = () => {};
export let onBlur = () => {};
export let onInput: (event: Event) => void = () => {};
export let onBlur: (event: FocusEvent) => void = () => {};
let textareaElement;
let textareaElement: HTMLTextAreaElement | null = null;
// Adjust height on mount and after setting the element.
onMount(async () => {
@ -38,7 +38,7 @@
// Find all ancestors that currently have an active scroll offset.
// This is extremely fast because reading `scrollTop` on a clean layout doesn't trigger reflow,
// and it makes the fix 100% robust without relying on hardcoded CSS classes.
let activeScrollParents = [];
const activeScrollParents: { el: HTMLElement; top: number }[] = [];
let p = textareaElement.parentNode;
while (p && p !== document.body) {
if (p instanceof HTMLElement && p.scrollTop > 0) {
@ -91,4 +91,4 @@
resize();
}}
on:blur={onBlur}
/>
></textarea>