mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-12 03:48:33 +00:00
refac
This commit is contained in:
parent
42dc6f3d09
commit
352e1a12cc
2 changed files with 104 additions and 79 deletions
|
|
@ -512,6 +512,7 @@ class Pipe:
|
|||
{:else}
|
||||
<LocalizedField
|
||||
placeholder={$i18n.t('Function Name')}
|
||||
showControls={false}
|
||||
bind:value={name}
|
||||
bind:translations={meta.i18n}
|
||||
{locale}
|
||||
|
|
@ -562,6 +563,7 @@ class Pipe:
|
|||
{:else}
|
||||
<LocalizedField
|
||||
placeholder={$i18n.t('Function Description')}
|
||||
showControls={false}
|
||||
bind:value={meta.description}
|
||||
bind:translations={meta.i18n}
|
||||
{locale}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
||||
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
|
||||
import Plus from '$lib/components/icons/Plus.svelte';
|
||||
import Search from '$lib/components/icons/Search.svelte';
|
||||
const i18n = getContext<any>('i18n');
|
||||
export let value: Record<string, string> = {};
|
||||
export let source: Record<string, string> = {};
|
||||
|
|
@ -86,32 +87,44 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<div class="flex min-h-0 flex-1 flex-col gap-2">
|
||||
<div class="flex flex-wrap items-center gap-3 text-xs">
|
||||
<slot name="language" />
|
||||
<input
|
||||
bind:this={fileInput}
|
||||
type="file"
|
||||
accept="application/json,.json"
|
||||
hidden
|
||||
on:change={importFile}
|
||||
/>
|
||||
<button type="button" disabled={readOnly} on:click={() => fileInput.click()}
|
||||
>{$i18n.t('Import')}</button
|
||||
>
|
||||
<button type="button" on:click={exportFile}>{$i18n.t('Export')}</button>
|
||||
{#if allowNewKeys && !readOnly}<Tooltip content={$i18n.t('Add translation')}
|
||||
><button
|
||||
class="flex size-6 items-center justify-center"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Add translation')}
|
||||
on:click={() => (adding = !adding)}><Plus className="size-3.5" /></button
|
||||
></Tooltip
|
||||
>{/if}
|
||||
<div class="flex min-h-0 flex-1 flex-col gap-1.5">
|
||||
<div class="flex min-h-7 flex-wrap items-center justify-between gap-x-2 gap-y-1">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">{$i18n.t('Translations')}</div>
|
||||
<div class="ms-auto flex shrink-0 items-center gap-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
<slot name="language" />
|
||||
<input
|
||||
bind:this={fileInput}
|
||||
type="file"
|
||||
accept="application/json,.json"
|
||||
hidden
|
||||
on:change={importFile}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="hover:text-gray-900 disabled:opacity-40 dark:hover:text-gray-100"
|
||||
disabled={readOnly}
|
||||
on:click={() => fileInput.click()}>{$i18n.t('Import')}</button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="hover:text-gray-900 disabled:opacity-40 dark:hover:text-gray-100"
|
||||
on:click={exportFile}>{$i18n.t('Export')}</button
|
||||
>
|
||||
{#if allowNewKeys && !readOnly}
|
||||
<Tooltip content={$i18n.t('Add translation')}>
|
||||
<button
|
||||
class="flex size-6 items-center justify-center text-gray-400 dark:text-gray-600"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Add translation')}
|
||||
on:click={() => (adding = !adding)}><Plus className="size-3.5" /></button
|
||||
>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if adding && !readOnly}<div class="flex items-center gap-2 text-xs">
|
||||
<input
|
||||
class="min-w-0 flex-1 rounded-lg border border-gray-200 bg-transparent px-2 py-1.5 dark:border-gray-800"
|
||||
class="min-w-0 flex-1 bg-transparent px-2 py-1 text-[0.8125rem] outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700"
|
||||
aria-label={$i18n.t('Translation key')}
|
||||
placeholder={$i18n.t('Translation key')}
|
||||
bind:value={newKey}
|
||||
|
|
@ -128,9 +141,10 @@
|
|||
on:click={addKey}><Plus className="size-4" /></button
|
||||
>
|
||||
</div>{/if}
|
||||
<div class="flex items-center gap-3 text-xs">
|
||||
<div class="flex items-center gap-2 px-2 py-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
<Search className="size-3.5 shrink-0 text-gray-500" />
|
||||
<input
|
||||
class="min-w-0 flex-1 rounded-lg border border-gray-200 bg-transparent px-2 py-1.5 outline-hidden dark:border-gray-800"
|
||||
class="min-w-0 w-full bg-transparent py-0.5 text-xs placeholder:text-gray-500 dark:placeholder:text-gray-400 outline-hidden"
|
||||
bind:value={query}
|
||||
placeholder={$i18n.t('Search')}
|
||||
aria-label={$i18n.t('Search translations')}
|
||||
|
|
@ -140,60 +154,69 @@
|
|||
>
|
||||
</div>
|
||||
<div class="min-h-0 flex-1 overflow-y-auto">
|
||||
<div
|
||||
class="sticky top-0 z-10 hidden grid-cols-[minmax(0,1fr)_minmax(0,1fr)_1.5rem] gap-3 border-b border-gray-100 bg-white py-2 text-xs text-gray-500 dark:border-gray-800 dark:bg-gray-900 sm:grid"
|
||||
>
|
||||
<span>{$i18n.t('Original')}</span><span>{$i18n.t('Translation')}</span>
|
||||
</div>
|
||||
{#each keys.slice(page * 50, (page + 1) * 50) as key (key)}
|
||||
<div
|
||||
class="grid grid-cols-[minmax(0,1fr)_1.5rem] items-start gap-2 border-b border-gray-100 py-2 dark:border-gray-800 sm:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_1.5rem] sm:gap-3"
|
||||
>
|
||||
<div class="col-span-2 min-w-0 text-xs sm:col-span-1">
|
||||
<div class="break-words">{key}</div>
|
||||
{#if source[key] && source[key] !== key}<div class="mt-1 break-words text-gray-500">
|
||||
{source[key]}
|
||||
</div>{/if}
|
||||
<div class="flex flex-col gap-1.5">
|
||||
{#each keys.slice(page * 50, (page + 1) * 50) as key (key)}
|
||||
<div class="flex items-end gap-2 px-2">
|
||||
<label class="min-w-0 flex-1 space-y-1">
|
||||
<Tooltip
|
||||
as="span"
|
||||
className="block min-w-0"
|
||||
content={key}
|
||||
allowHTML={false}
|
||||
placement="top-start"
|
||||
>
|
||||
<span
|
||||
class="block whitespace-pre-wrap break-words text-xs text-gray-600 dark:text-gray-400"
|
||||
>{source[key] || key}</span
|
||||
>
|
||||
</Tooltip>
|
||||
<textarea
|
||||
rows="1"
|
||||
readonly={readOnly}
|
||||
class="block min-h-7 max-h-40 min-w-0 w-full resize-none rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-xs text-gray-700 outline-hidden transition-colors placeholder:text-gray-500 focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:placeholder:text-gray-400 dark:focus:border-blue-500 [field-sizing:content]"
|
||||
aria-label={key}
|
||||
placeholder={$i18n.t('Translation')}
|
||||
value={value?.[key] ?? ''}
|
||||
on:input={(event) => update(key, event.currentTarget.value)}
|
||||
></textarea>
|
||||
</label>
|
||||
<div class="mb-0.5 w-6 shrink-0">
|
||||
{#if !readOnly && Object.hasOwn(value ?? {}, key)}
|
||||
<Tooltip content={$i18n.t('Use default')}>
|
||||
<button
|
||||
type="button"
|
||||
class="flex size-6 items-center justify-center text-gray-400 hover:text-gray-700 dark:text-gray-500 dark:hover:text-gray-300"
|
||||
aria-label={`${$i18n.t('Use default')}: ${key}`}
|
||||
on:click={() => reset(key)}><ArrowUturnLeft className="size-3.5" /></button
|
||||
>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<textarea
|
||||
rows="2"
|
||||
readonly={readOnly}
|
||||
class="w-full min-w-0 resize-y rounded-md border border-gray-100 bg-transparent px-2 py-1 text-xs outline-hidden focus:border-gray-400 dark:border-gray-800"
|
||||
aria-label={key}
|
||||
placeholder={source[key] || key}
|
||||
value={value?.[key] ?? ''}
|
||||
on:input={(event) => update(key, event.currentTarget.value)}
|
||||
></textarea>
|
||||
<Tooltip content={$i18n.t('Use default')}
|
||||
><button
|
||||
type="button"
|
||||
class="flex size-6 items-center justify-center disabled:opacity-30"
|
||||
aria-label={`${$i18n.t('Use default')}: ${key}`}
|
||||
disabled={readOnly || !Object.hasOwn(value ?? {}, key)}
|
||||
on:click={() => reset(key)}><ArrowUturnLeft className="size-3.5" /></button
|
||||
></Tooltip
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
{#if !keys.length}<div class="py-6 text-center text-xs text-gray-500">
|
||||
{$i18n.t('No results found')}
|
||||
</div>{/if}
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-3 text-xs text-gray-500">
|
||||
<span>{page + 1} / {pages}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="flex size-7 items-center justify-center disabled:opacity-30"
|
||||
aria-label={$i18n.t('Previous')}
|
||||
disabled={page === 0}
|
||||
on:click={() => page--}><ChevronLeft className="size-4" /></button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="flex size-7 items-center justify-center disabled:opacity-30"
|
||||
aria-label={$i18n.t('Next')}
|
||||
disabled={page + 1 >= pages}
|
||||
on:click={() => page++}><ChevronRight className="size-4" /></button
|
||||
>
|
||||
{:else}
|
||||
<div class="px-2 py-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
{$i18n.t('No results found')}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{#if pages > 1}
|
||||
<div class="flex items-center justify-end gap-3 text-xs text-gray-500">
|
||||
<span>{page + 1} / {pages}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="flex size-7 items-center justify-center disabled:opacity-30"
|
||||
aria-label={$i18n.t('Previous')}
|
||||
disabled={page === 0}
|
||||
on:click={() => page--}><ChevronLeft className="size-4" /></button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="flex size-7 items-center justify-center disabled:opacity-30"
|
||||
aria-label={$i18n.t('Next')}
|
||||
disabled={page + 1 >= pages}
|
||||
on:click={() => page++}><ChevronRight className="size-4" /></button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue