Merge pull request #14959 from itk-dev/feature/accessibility-edit-connection-modal

Feature/accessibility edit connection modal
This commit is contained in:
Tim Jaeryang Baek
2025-06-14 17:58:38 +04:00
committed by GitHub
7 changed files with 81 additions and 22 deletions
+67 -18
View File
@@ -3,7 +3,7 @@
import { getContext, onMount } from 'svelte'; import { getContext, onMount } from 'svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
import { models } from '$lib/stores'; import { settings } from '$lib/stores';
import { verifyOpenAIConnection } from '$lib/apis/openai'; import { verifyOpenAIConnection } from '$lib/apis/openai';
import { verifyOllamaConnection } from '$lib/apis/ollama'; import { verifyOllamaConnection } from '$lib/apis/ollama';
@@ -258,11 +258,17 @@
<div class="flex gap-2 mt-1.5"> <div class="flex gap-2 mt-1.5">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('URL')}</div> <label
for="url-input"
class={`mb-0.5 text-xs text-gray-500
${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : ''}`}
>{$i18n.t('URL')}</label
>
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden" id="url-input"
class={`w-full text-sm bg-transparent ${($settings?.highContrastMode ?? false) ? 'placeholder:text-gray-700 dark:placeholder:text-gray-100' : 'outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700'}`}
type="text" type="text"
bind:value={url} bind:value={url}
placeholder={$i18n.t('API Base URL')} placeholder={$i18n.t('API Base URL')}
@@ -279,11 +285,13 @@
verifyHandler(); verifyHandler();
}} }}
type="button" type="button"
aria-label={$i18n.t('Verify Connection')}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" viewBox="0 0 20 20"
fill="currentColor" fill="currentColor"
aria-hidden="true"
class="w-4 h-4" class="w-4 h-4"
> >
<path <path
@@ -296,19 +304,27 @@
</Tooltip> </Tooltip>
<div class="flex flex-col shrink-0 self-end"> <div class="flex flex-col shrink-0 self-end">
<label class="sr-only" for="toggle-connection"
>{$i18n.t('Toggle whether current connection is active.')}</label
>
<Tooltip content={enable ? $i18n.t('Enabled') : $i18n.t('Disabled')}> <Tooltip content={enable ? $i18n.t('Enabled') : $i18n.t('Disabled')}>
<Switch bind:state={enable} /> <Switch id="toggle-connection" bind:state={enable} />
</Tooltip> </Tooltip>
</div> </div>
</div> </div>
<div class="flex gap-2 mt-2"> <div class="flex gap-2 mt-2">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('Key')}</div> <div
class={`mb-0.5 text-xs text-gray-500
${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : ''}`}
>
{$i18n.t('Key')}
</div>
<div class="flex-1"> <div class="flex-1">
<SensitiveInput <SensitiveInput
className="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden" inputClassName={`w-full text-sm bg-transparent ${($settings?.highContrastMode ?? false) ? 'placeholder:text-gray-700 dark:placeholder:text-gray-100' : 'outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700'}`}
bind:value={key} bind:value={key}
placeholder={$i18n.t('API Key')} placeholder={$i18n.t('API Key')}
required={false} required={false}
@@ -317,7 +333,12 @@
</div> </div>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Prefix ID')}</div> <label
for="prefix-id-input"
class={`mb-0.5 text-xs text-gray-500
${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : ''}`}
>{$i18n.t('Prefix ID')}</label
>
<div class="flex-1"> <div class="flex-1">
<Tooltip <Tooltip
@@ -326,8 +347,9 @@
)} )}
> >
<input <input
class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden" class={`w-full text-sm bg-transparent ${($settings?.highContrastMode ?? false) ? 'placeholder:text-gray-700 dark:placeholder:text-gray-100' : 'outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700'}`}
type="text" type="text"
id="prefix-id-input"
bind:value={prefixId} bind:value={prefixId}
placeholder={$i18n.t('Prefix ID')} placeholder={$i18n.t('Prefix ID')}
autocomplete="off" autocomplete="off"
@@ -340,11 +362,17 @@
{#if azure} {#if azure}
<div class="flex gap-2 mt-2"> <div class="flex gap-2 mt-2">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('API Version')}</div> <label
for="api-version-input"
class={`mb-0.5 text-xs text-gray-500
${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : ''}`}
>{$i18n.t('API Version')}</label
>
<div class="flex-1"> <div class="flex-1">
<input <input
class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden" id="api-version-input"
class={`w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 ${($settings?.highContrastMode ?? false) ? 'placeholder:text-gray-700 dark:placeholder:text-gray-100' : 'outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700'}`}
type="text" type="text"
bind:value={apiVersion} bind:value={apiVersion}
placeholder={$i18n.t('API Version')} placeholder={$i18n.t('API Version')}
@@ -358,7 +386,12 @@
<div class="flex gap-2 mt-2"> <div class="flex gap-2 mt-2">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1.5 text-xs text-gray-500">{$i18n.t('Tags')}</div> <div
class={`mb-0.5 text-xs text-gray-500
${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : ''}`}
>
{$i18n.t('Tags')}
</div>
<div class="flex-1"> <div class="flex-1">
<Tags <Tags
@@ -383,18 +416,26 @@
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class="mb-1 flex justify-between"> <div class="mb-1 flex justify-between">
<div class="text-xs text-gray-500">{$i18n.t('Model IDs')}</div> <div
class={`mb-0.5 text-xs text-gray-500
${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : ''}`}
>
{$i18n.t('Model IDs')}
</div>
</div> </div>
{#if modelIds.length > 0} {#if modelIds.length > 0}
<div class="flex flex-col"> <ul class="flex flex-col">
{#each modelIds as modelId, modelIdx} {#each modelIds as modelId, modelIdx}
<div class=" flex gap-2 w-full justify-between items-center"> <li class=" flex gap-2 w-full justify-between items-center">
<div class=" text-sm flex-1 py-1 rounded-lg"> <div class=" text-sm flex-1 py-1 rounded-lg">
{modelId} {modelId}
</div> </div>
<div class="shrink-0"> <div class="shrink-0">
<button <button
aria-label={$i18n.t(`Remove {{MODELID}} from list.`, {
MODELID: modelId
})}
type="button" type="button"
on:click={() => { on:click={() => {
modelIds = modelIds.filter((_, idx) => idx !== modelIdx); modelIds = modelIds.filter((_, idx) => idx !== modelIdx);
@@ -403,11 +444,14 @@
<Minus strokeWidth="2" className="size-3.5" /> <Minus strokeWidth="2" className="size-3.5" />
</button> </button>
</div> </div>
</div> </li>
{/each} {/each}
</div> </ul>
{:else} {:else}
<div class="text-gray-500 text-xs text-center py-2 px-10"> <div
class={`text-gray-500 text-xs text-center py-2 px-10
${($settings?.highContrastMode ?? false) ? 'text-gray-800 dark:text-gray-100' : ''}`}
>
{#if ollama} {#if ollama}
{$i18n.t('Leave empty to include all models from "{{url}}/api/tags" endpoint', { {$i18n.t('Leave empty to include all models from "{{url}}/api/tags" endpoint', {
url: url url: url
@@ -429,17 +473,22 @@
<hr class=" border-gray-100 dark:border-gray-700/10 my-1.5 w-full" /> <hr class=" border-gray-100 dark:border-gray-700/10 my-1.5 w-full" />
<div class="flex items-center"> <div class="flex items-center">
<label class="sr-only" for="add-model-id-input">{$i18n.t('Add a model ID')}</label>
<input <input
class="w-full py-1 text-sm rounded-lg bg-transparent {modelId class="w-full py-1 text-sm rounded-lg bg-transparent {modelId
? '' ? ''
: 'text-gray-500'} placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden" : 'text-gray-500'} {($settings?.highContrastMode ?? false)
? 'dark:placeholder:text-gray-100 placeholder:text-gray-700'
: 'placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-hidden'}"
bind:value={modelId} bind:value={modelId}
id="add-model-id-input"
placeholder={$i18n.t('Add a model ID')} placeholder={$i18n.t('Add a model ID')}
/> />
<div> <div>
<button <button
type="button" type="button"
aria-label={$i18n.t('Add')}
on:click={() => { on:click={() => {
addModelHandler(); addModelHandler();
}} }}
+2
View File
@@ -2,6 +2,7 @@
import { createEventDispatcher, tick } from 'svelte'; import { createEventDispatcher, tick } from 'svelte';
import { Switch } from 'bits-ui'; import { Switch } from 'bits-ui';
export let state = true; export let state = true;
export let id = '';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@@ -10,6 +11,7 @@
<Switch.Root <Switch.Root
bind:checked={state} bind:checked={state}
{id}
class="flex h-5 min-h-5 w-9 shrink-0 cursor-pointer items-center rounded-full px-[3px] mx-[1px] transition {state class="flex h-5 min-h-5 w-9 shrink-0 cursor-pointer items-center rounded-full px-[3px] mx-[1px] transition {state
? ' bg-emerald-600' ? ' bg-emerald-600'
: 'bg-gray-200 dark:bg-transparent'} outline outline-1 outline-gray-100 dark:outline-gray-800" : 'bg-gray-200 dark:bg-transparent'} outline outline-1 outline-gray-100 dark:outline-gray-800"
+2 -2
View File
@@ -9,7 +9,7 @@
export let tags = []; export let tags = [];
</script> </script>
<div class="flex flex-row flex-wrap gap-1 line-clamp-1"> <ul class="flex flex-row flex-wrap gap-1 line-clamp-1">
<TagList <TagList
{tags} {tags}
on:delete={(e) => { on:delete={(e) => {
@@ -23,4 +23,4 @@
dispatch('add', e.detail); dispatch('add', e.detail);
}} }}
/> />
</div> </ul>
@@ -29,6 +29,7 @@
bind:value={tagName} bind:value={tagName}
class=" px-2 cursor-pointer self-center text-xs h-fit bg-transparent outline-hidden line-clamp-1 w-[6.5rem]" class=" px-2 cursor-pointer self-center text-xs h-fit bg-transparent outline-hidden line-clamp-1 w-[6.5rem]"
placeholder={$i18n.t('Add a tag')} placeholder={$i18n.t('Add a tag')}
aria-label={$i18n.t('Add a tag')}
list="tagOptions" list="tagOptions"
on:keydown={(event) => { on:keydown={(event) => {
if (event.key === 'Enter') { if (event.key === 'Enter') {
@@ -48,6 +49,7 @@
viewBox="0 0 16 16" viewBox="0 0 16 16"
fill="currentColor" fill="currentColor"
stroke-width="2" stroke-width="2"
aria-hidden="true"
class="w-3 h-3" class="w-3 h-3"
> >
<path <path
@@ -72,6 +74,7 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16" viewBox="0 0 16 16"
aria-hidden="true"
fill="currentColor" fill="currentColor"
class="w-3 h-3 {showTagInput ? 'rotate-45' : ''} transition-all transform" class="w-3 h-3 {showTagInput ? 'rotate-45' : ''} transition-all transform"
> >
@@ -1,5 +1,7 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { getContext } from 'svelte';
const i18n = getContext('i18n');
import Tooltip from '../Tooltip.svelte'; import Tooltip from '../Tooltip.svelte';
import XMark from '$lib/components/icons/XMark.svelte'; import XMark from '$lib/components/icons/XMark.svelte';
import Badge from '../Badge.svelte'; import Badge from '../Badge.svelte';
@@ -10,7 +12,7 @@
{#each tags as tag} {#each tags as tag}
<Tooltip content={tag.name}> <Tooltip content={tag.name}>
<div <li
class="relative group/tags px-1.5 py-[0.2px] gap-0.5 flex justify-between h-fit max-h-fit w-fit items-center rounded-full bg-gray-500/20 text-gray-700 dark:text-gray-200 transition cursor-pointer" class="relative group/tags px-1.5 py-[0.2px] gap-0.5 flex justify-between h-fit max-h-fit w-fit items-center rounded-full bg-gray-500/20 text-gray-700 dark:text-gray-200 transition cursor-pointer"
> >
<div class=" text-[0.7rem] font-medium self-center line-clamp-1 w-fit"> <div class=" text-[0.7rem] font-medium self-center line-clamp-1 w-fit">
@@ -23,10 +25,11 @@
dispatch('delete', tag.name); dispatch('delete', tag.name);
}} }}
type="button" type="button"
aria-label={$i18n.t('Remove this tag from list')}
> >
<XMark className="size-3" strokeWidth="2.5" /> <XMark className="size-3" strokeWidth="2.5" />
</button> </button>
</div> </div>
</div> </li>
</Tooltip> </Tooltip>
{/each} {/each}
+1
View File
@@ -6,6 +6,7 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
aria-hidden="true"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width={strokeWidth} stroke-width={strokeWidth}
stroke="currentColor" stroke="currentColor"
+1
View File
@@ -10,6 +10,7 @@
stroke-width={strokeWidth} stroke-width={strokeWidth}
stroke="currentColor" stroke="currentColor"
class={className} class={className}
aria-hidden="true"
> >
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /> <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg> </svg>