mirror of
https://github.com/open-webui/open-webui.git
synced 2026-09-15 23:32:40 +00:00
refac
This commit is contained in:
parent
ca26632a2a
commit
f677fdbf50
59 changed files with 285 additions and 217 deletions
|
|
@ -4,9 +4,11 @@
|
|||
import DOMPurify from 'dompurify';
|
||||
import { marked } from 'marked';
|
||||
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { createEventDispatcher, getContext, onMount } from 'svelte';
|
||||
import XMark from '$lib/components/icons/XMark.svelte';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let onClick: Function = () => {};
|
||||
|
|
@ -103,7 +105,7 @@
|
|||
bind:this={closeButtonElement}
|
||||
class="absolute -top-0.5 -left-0.5 p-0.5 rounded-full opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-opacity z-10"
|
||||
on:click|stopPropagation={closeHandler}
|
||||
aria-label="Dismiss notification"
|
||||
aria-label={$i18n.t('Dismiss notification')}
|
||||
>
|
||||
<XMark className="size-3" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
export let endDate: number | null = null;
|
||||
export let onClose: () => void = () => {};
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
type Tab = 'overview' | 'chats';
|
||||
type ChatSortKey = 'title' | 'updated_at' | 'user_name';
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
const TIME_RANGES: { key: TimeRange; label: string; days: number }[] = [
|
||||
{ key: '30d', label: '30D', days: 30 },
|
||||
{ key: '1y', label: '1Y', days: 365 },
|
||||
{ key: 'all', label: 'All', days: 0 }
|
||||
{ key: 'all', label: $i18n.t('All'), days: 0 }
|
||||
];
|
||||
let selectedRange: TimeRange = '30d';
|
||||
let history: Array<{ date: string; won: number; lost: number }> = [];
|
||||
|
|
@ -187,7 +187,7 @@
|
|||
<button
|
||||
class="self-center rounded-lg p-1 text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
||||
on:click={close}
|
||||
aria-label="Close"
|
||||
aria-label={$i18n.t('Close')}
|
||||
>
|
||||
<XMark className={'size-4'} />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import Modal from '$lib/components/common/Modal.svelte';
|
||||
import { getContext } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
import XMark from '$lib/components/icons/XMark.svelte';
|
||||
import { getFeedbackById } from '$lib/apis/evaluations';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
<button
|
||||
class="self-center rounded-lg p-1 text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
||||
on:click={close}
|
||||
aria-label="Close"
|
||||
aria-label={$i18n.t('Close')}
|
||||
>
|
||||
<XMark className={'size-4'} />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
|
||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
let rankedModels = [];
|
||||
let query = '';
|
||||
|
|
@ -174,7 +174,7 @@
|
|||
>
|
||||
<thead class="text-xs text-gray-800 uppercase bg-transparent dark:text-gray-200">
|
||||
<tr class="border-b-[1.5px] border-gray-50 dark:border-gray-850/30">
|
||||
{#each [{ key: 'rating', label: 'RK', class: 'w-3' }, { key: 'name', label: 'Model', class: '' }, { key: 'rating', label: 'Rating', class: 'text-right w-fit' }, { key: 'won', label: 'Won', class: 'text-right w-5' }, { key: 'lost', label: 'Lost', class: 'text-right w-5' }] as col}
|
||||
{#each [{ key: 'rating', label: 'RK', class: 'w-3' }, { key: 'name', label: $i18n.t('Model'), class: '' }, { key: 'rating', label: $i18n.t('Rating'), class: 'text-right w-fit' }, { key: 'won', label: $i18n.t('Won'), class: 'text-right w-5' }, { key: 'lost', label: $i18n.t('Lost'), class: 'text-right w-5' }] as col}
|
||||
<th
|
||||
scope="col"
|
||||
class="px-2.5 py-2 font-normal cursor-pointer select-none {col.class}"
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
export let model = null;
|
||||
export let onClose: () => void = () => {};
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
type TimeRange = '30d' | '1y' | 'all';
|
||||
const TIME_RANGES: { key: TimeRange; label: string; days: number }[] = [
|
||||
{ key: '30d', label: '30D', days: 30 },
|
||||
{ key: '1y', label: '1Y', days: 365 },
|
||||
{ key: 'all', label: 'All', days: 0 } // 0 = all time, starts from first feedback
|
||||
{ key: 'all', label: $i18n.t('All'), days: 0 } // 0 = all time, starts from first feedback
|
||||
];
|
||||
|
||||
let selectedRange: TimeRange = '30d';
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
<button
|
||||
class="self-center rounded-lg p-1 text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
||||
on:click={close}
|
||||
aria-label="Close"
|
||||
aria-label={$i18n.t('Close')}
|
||||
>
|
||||
<XMark className={'size-4'} />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { getContext } from 'svelte';
|
||||
import Spinner from '$lib/components/common/Spinner.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let history: Array<{ date: string; won: number; lost: number }> = [];
|
||||
export let loading = false;
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Won',
|
||||
label: $i18n.t('Won'),
|
||||
data: wonData,
|
||||
backgroundColor: '#5ba3c8',
|
||||
borderRadius: 2,
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
categoryPercentage
|
||||
},
|
||||
{
|
||||
label: 'Lost',
|
||||
label: $i18n.t('Lost'),
|
||||
data: lostData,
|
||||
backgroundColor: '#d97c5a',
|
||||
borderRadius: 2,
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@
|
|||
bind:value={adminConfig.DEFAULT_GROUP_ID}
|
||||
placeholder={$i18n.t('Select a group')}
|
||||
>
|
||||
<option value={''}>None</option>
|
||||
<option value={''}>{$i18n.t('None')}</option>
|
||||
{#each groups as group}
|
||||
<option value={group.id}>{group.name}</option>
|
||||
{/each}
|
||||
|
|
@ -707,7 +707,7 @@
|
|||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder="* (all domains)"
|
||||
placeholder={$i18n.t('* (all domains)')}
|
||||
bind:value={oauthConfig.OAUTH_ALLOWED_DOMAINS}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@
|
|||
<span class="capitalize text-black dark:text-white"
|
||||
>{$config?.license_metadata?.type} license</span
|
||||
>
|
||||
registered to
|
||||
{$i18n.t('registered to')}
|
||||
<span class="capitalize text-black dark:text-white"
|
||||
>{$config?.license_metadata?.organization_name}</span
|
||||
>
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@
|
|||
<button
|
||||
class="shrink-0 text-gray-400 transition-colors hover:text-gray-900 dark:text-gray-600 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label="verify connection"
|
||||
aria-label={$i18n.t('verify connection')}
|
||||
on:click={async () => {
|
||||
await updateConfigHandler();
|
||||
const res = await verifyConfigUrl(localStorage.token).catch((error) => {
|
||||
|
|
@ -503,7 +503,7 @@
|
|||
<button
|
||||
class="shrink-0 text-gray-400 transition-colors hover:text-gray-900 dark:text-gray-600 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label="verify connection"
|
||||
aria-label={$i18n.t('verify connection')}
|
||||
on:click={async () => {
|
||||
await updateConfigHandler();
|
||||
const res = await verifyConfigUrl(localStorage.token).catch((error) => {
|
||||
|
|
@ -801,7 +801,7 @@
|
|||
<button
|
||||
class="shrink-0 text-gray-400 transition-colors hover:text-gray-900 dark:text-gray-600 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label="verify connection"
|
||||
aria-label={$i18n.t('verify connection')}
|
||||
on:click={async () => {
|
||||
await updateConfigHandler();
|
||||
const res = await verifyConfigUrl(localStorage.token).catch((error) => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
const { saveAs } = fileSaver;
|
||||
|
||||
import { onMount, onDestroy, getContext, tick } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
import {
|
||||
config,
|
||||
|
|
@ -966,7 +966,7 @@
|
|||
>
|
||||
<img
|
||||
src={`${WEBUI_API_BASE_URL}/models/model/profile/image?id=${model.id}&lang=${$i18n.language}`}
|
||||
alt="modelfile profile"
|
||||
alt={$i18n.t('modelfile profile')}
|
||||
class=" rounded-xl size-7 object-cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { getContext, onMount } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user, config, settings } from '$lib/stores';
|
||||
import { splitStream } from '$lib/utils';
|
||||
|
|
@ -623,7 +623,7 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<Tooltip content="Update All Models" placement="top">
|
||||
<Tooltip content={$i18n.t('Update All Models')} placement="top">
|
||||
<button
|
||||
class="flex gap-2 items-center bg-transparent rounded-lg transition"
|
||||
aria-label={$i18n.t('Update All Models')}
|
||||
|
|
@ -726,7 +726,10 @@
|
|||
|
||||
{#if updateModelId}
|
||||
<div class="text-xs flex justify-between items-center">
|
||||
<div>Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''}</div>
|
||||
<div>
|
||||
{$i18n.t('Updating "{{modelId}}"', { modelId: updateModelId })}
|
||||
{updateProgress ? `(${updateProgress}%)` : ''}
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t('Cancel')}>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@
|
|||
}}
|
||||
>
|
||||
{#if pipelineFiles}
|
||||
{pipelineFiles.length > 0 ? `${pipelineFiles.length}` : ''} pipeline(s) selected
|
||||
{$i18n.t('{{count}} pipeline(s) selected', { count: pipelineFiles.length })}
|
||||
{:else}
|
||||
{$i18n.t('Select a .py file')}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
import ProfilePreview from '$lib/components/channel/Messages/Message/ProfilePreview.svelte';
|
||||
import UserPreviewModal from '$lib/components/admin/UserPreviewModal.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
let page = 1;
|
||||
|
||||
|
|
@ -166,9 +166,10 @@
|
|||
className="mx-0"
|
||||
banner={{
|
||||
type: 'error',
|
||||
title: 'License Error',
|
||||
content:
|
||||
title: $i18n.t('License Error'),
|
||||
content: $i18n.t(
|
||||
'Exceeded the number of seats in your license. Please contact support to increase the number of seats.'
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
import XMark from '$lib/components/icons/XMark.svelte';
|
||||
import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let show = false;
|
||||
|
|
@ -275,7 +275,7 @@
|
|||
}}
|
||||
>
|
||||
{#if inputFiles}
|
||||
{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected.
|
||||
{$i18n.t('{{count}} document(s) selected.', { count: inputFiles.length })}
|
||||
{:else}
|
||||
{$i18n.t('Click here to select a csv file.')}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import Plus from '$lib/components/icons/Plus.svelte';
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
let selected = '';
|
||||
</script>
|
||||
|
||||
<nav
|
||||
aria-label="App navigation"
|
||||
aria-label={$i18n.t('App navigation')}
|
||||
class="min-w-[4.5rem] bg-gray-50 dark:bg-gray-950 flex gap-2.5 flex-col pt-8"
|
||||
>
|
||||
<div class="flex justify-center relative">
|
||||
|
|
@ -17,9 +20,9 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<Tooltip content="Home" placement="right">
|
||||
<Tooltip content={$i18n.t('Home')} placement="right">
|
||||
<button
|
||||
aria-label="Home"
|
||||
aria-label={$i18n.t('Home')}
|
||||
class=" cursor-pointer {selected === 'home' ? 'rounded-2xl' : 'rounded-full'}"
|
||||
on:click={() => {
|
||||
selected = 'home';
|
||||
|
|
@ -51,7 +54,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
<button
|
||||
aria-label="Chat"
|
||||
aria-label={$i18n.t('Chat')}
|
||||
class=" cursor-pointer bg-transparent"
|
||||
on:click={() => {
|
||||
selected = '';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import { tick, getContext, onMount } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
import { config, mobile, settings, socket, user } from '$lib/stores';
|
||||
import {
|
||||
|
|
@ -997,7 +997,7 @@
|
|||
id="input-menu-button"
|
||||
class="bg-transparent hover:bg-gray-100 text-gray-700 dark:text-white dark:hover:bg-gray-800 rounded-full size-[1.875rem] flex justify-center items-center outline-hidden focus:outline-hidden shrink-0"
|
||||
type="button"
|
||||
aria-label="More"
|
||||
aria-label={$i18n.t('More')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -1048,7 +1048,7 @@
|
|||
toast.error($i18n.t('Permission denied when accessing microphone'));
|
||||
}
|
||||
}}
|
||||
aria-label="Voice Input"
|
||||
aria-label={$i18n.t('Voice Input')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
import Pin from '../icons/Pin.svelte';
|
||||
import PinnedMessagesModal from './PinnedMessagesModal.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
let showChannelPinnedMessagesModal = false;
|
||||
let showChannelInfoModal = false;
|
||||
|
|
@ -159,7 +159,7 @@
|
|||
<Tooltip content={$i18n.t('Pinned Messages')}>
|
||||
<button
|
||||
class=" flex cursor-pointer py-1.5 px-1.5 border dark:border-gray-850 border-gray-50 rounded-xl text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-850 transition"
|
||||
aria-label="Pinned Messages"
|
||||
aria-label={$i18n.t('Pinned Messages')}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showChannelPinnedMessagesModal = true;
|
||||
|
|
@ -175,7 +175,7 @@
|
|||
<Tooltip content={$i18n.t('Users')}>
|
||||
<button
|
||||
class=" flex cursor-pointer shrink-0 py-1 px-1.5 border dark:border-gray-850 border-gray-50 rounded-xl text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-850 transition"
|
||||
aria-label="User Count"
|
||||
aria-label={$i18n.t('User Count')}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showChannelInfoModal = true;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { onMount, getContext, createEventDispatcher } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import {
|
||||
|
|
@ -245,7 +245,7 @@
|
|||
{#if contents[selectedContentIdx].type === 'iframe'}
|
||||
<iframe
|
||||
bind:this={iframeElement}
|
||||
title="Content"
|
||||
title={$i18n.t('Content')}
|
||||
srcdoc={injectCsp(
|
||||
contents[selectedContentIdx].content,
|
||||
$config?.ui?.iframe_csp ?? ''
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import { embed, showControls, showEmbeds } from '$lib/stores';
|
||||
|
||||
import FullHeightIframe from '$lib/components/common/FullHeightIframe.svelte';
|
||||
|
|
@ -6,6 +7,8 @@
|
|||
|
||||
export let overlay = false;
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
const getSrcUrl = (url: string, chatId?: string, messageId?: string, sourceId: string) => {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
|
|
@ -57,7 +60,7 @@
|
|||
|
||||
<button
|
||||
class="self-center pointer-events-auto p-1 rounded-full bg-white dark:bg-gray-850"
|
||||
aria-label="Close embed"
|
||||
aria-label={$i18n.t('Close embed')}
|
||||
on:click={() => {
|
||||
showControls.set(false);
|
||||
showEmbeds.set(false);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import EyeSlash from '$lib/components/icons/EyeSlash.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let modelIds = [];
|
||||
export let models = [];
|
||||
|
|
@ -112,7 +112,7 @@
|
|||
</div>
|
||||
{#if models[selectedModelIdx]?.info?.meta?.user}
|
||||
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
|
||||
By
|
||||
{$i18n.t('By')}
|
||||
{#if models[selectedModelIdx]?.info?.meta?.user.community}
|
||||
<a
|
||||
href="https://openwebui.com/m/{models[selectedModelIdx]?.info?.meta?.user
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
import PortPreview from './FileNav/PortPreview.svelte';
|
||||
import XTerminal from './XTerminal.svelte';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let overlay = false;
|
||||
export let chatId: string | null = null;
|
||||
|
|
@ -937,7 +937,9 @@
|
|||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to render Office file:', e);
|
||||
fileContent = `Error previewing file: ${e instanceof Error ? e.message : 'Unknown error'}`;
|
||||
fileContent = $i18n.t('Error previewing file: {{error}}', {
|
||||
error: e instanceof Error ? e.message : $i18n.t('Unknown error')
|
||||
});
|
||||
}
|
||||
} else {
|
||||
fileContent = await readFile(terminal.url, terminal.key, filePath, chatId ?? undefined);
|
||||
|
|
|
|||
|
|
@ -318,7 +318,7 @@
|
|||
</span>
|
||||
{/if}
|
||||
{#if !writable && !renaming}
|
||||
<span class="text-[0.625rem] text-gray-400 shrink-0">Read-only</span>
|
||||
<span class="text-[0.625rem] text-gray-400 shrink-0">{$i18n.t('Read-only')}</span>
|
||||
{/if}
|
||||
{#if entry.type === 'file' && entry.size !== undefined && !renaming}
|
||||
{#if showDate && entry.modified}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,9 @@
|
|||
{/if}
|
||||
</div>
|
||||
{#if !writable}
|
||||
<span class="text-[0.625rem] text-gray-400 dark:text-gray-500 shrink-0"> Read-only </span>
|
||||
<span class="text-[0.625rem] text-gray-400 dark:text-gray-500 shrink-0">
|
||||
{$i18n.t('Read-only')}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
<Tooltip content={$i18n.t('Refresh')}>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
let pdfPagesPreviewRef: PdfPagesPreview;
|
||||
let fileCodeEditorRef: FileCodeEditor;
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let selectedFile: string | null = null;
|
||||
export let fileLoading = false;
|
||||
|
|
@ -242,7 +242,7 @@
|
|||
jsonError = null;
|
||||
} catch (e) {
|
||||
parsedJson = undefined;
|
||||
jsonError = e instanceof Error ? e.message : 'Invalid JSON';
|
||||
jsonError = e instanceof Error ? e.message : $i18n.t('Invalid JSON');
|
||||
}
|
||||
} else {
|
||||
parsedJson = undefined;
|
||||
|
|
@ -393,7 +393,7 @@
|
|||
? ' allow-forms'
|
||||
: ''}{($settings?.iframeSandboxAllowSameOrigin ?? false) ? ' allow-same-origin' : ''}"
|
||||
class="w-full h-full border-none bg-white"
|
||||
title="HTML Preview"
|
||||
title={$i18n.t('HTML Preview')}
|
||||
/>
|
||||
{:else if isHtml && !showRaw}
|
||||
{#if overlay}
|
||||
|
|
@ -409,7 +409,7 @@
|
|||
? ' allow-forms'
|
||||
: ''}{($settings?.iframeSandboxAllowSameOrigin ?? false) ? ' allow-same-origin' : ''}"
|
||||
class="w-full h-full border-none bg-white"
|
||||
title="HTML Preview"
|
||||
title={$i18n.t('HTML Preview')}
|
||||
/>
|
||||
{:else if isHtml && showRaw}
|
||||
<div class="absolute inset-0">
|
||||
|
|
@ -470,7 +470,9 @@
|
|||
</div>
|
||||
{:else if isJson && !showRaw && jsonError}
|
||||
<div class="p-3 text-xs">
|
||||
<div class="text-red-500 mb-2">JSON parse error: {jsonError}</div>
|
||||
<div class="text-red-500 mb-2">
|
||||
{$i18n.t('JSON parse error: {{error}}', { error: jsonError })}
|
||||
</div>
|
||||
<pre
|
||||
class="text-xs font-mono text-gray-800 dark:text-gray-200 whitespace-pre-wrap break-all leading-relaxed">{fileContent}</pre>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
import Tooltip from '../../common/Tooltip.svelte';
|
||||
import CellEditor from './CellEditor.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let notebook: Record<string, unknown>;
|
||||
export let filePath: string = '';
|
||||
|
|
@ -309,7 +309,7 @@
|
|||
class="nb-run-btn"
|
||||
on:click={() => runCell(i)}
|
||||
disabled={runningCell !== null}
|
||||
title="Run cell (⌘+Enter)"
|
||||
title={$i18n.t('Run cell (⌘+Enter)')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { getContext, onDestroy } from 'svelte';
|
||||
import Spinner from '../../common/Spinner.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let data: ArrayBuffer;
|
||||
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
loading = false;
|
||||
} catch (e) {
|
||||
loading = false;
|
||||
error = e instanceof Error ? e.message : 'Failed to open database';
|
||||
error = e instanceof Error ? e.message : $i18n.t('Failed to open database');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -82,7 +82,7 @@
|
|||
rows = [];
|
||||
}
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Query failed';
|
||||
error = e instanceof Error ? e.message : $i18n.t('Query failed');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
queryRows = [];
|
||||
}
|
||||
} catch (e) {
|
||||
queryError = e instanceof Error ? e.message : 'Query failed';
|
||||
queryError = e instanceof Error ? e.message : $i18n.t('Query failed');
|
||||
queryColumns = [];
|
||||
queryRows = [];
|
||||
}
|
||||
|
|
@ -275,7 +275,13 @@
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<span>{page + 1} / {totalPages} ({totalRows.toLocaleString()} rows)</span>
|
||||
<span>
|
||||
{$i18n.t('{{page}} / {{totalPages}} ({{rows}} rows)', {
|
||||
page: page + 1,
|
||||
totalPages,
|
||||
rows: totalRows.toLocaleString()
|
||||
})}
|
||||
</span>
|
||||
<button
|
||||
aria-label={$i18n.t('Next page')}
|
||||
class="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-30"
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@
|
|||
import QueuedMessageItem from './MessageInput/QueuedMessageItem.svelte';
|
||||
import TaskList from './Messages/ResponseMessage/TaskList.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
type AskUserPrompt = {
|
||||
show: boolean;
|
||||
|
|
@ -1780,7 +1780,7 @@
|
|||
<div class="mx-1 rounded-2xl bg-white text-xs dark:bg-gray-900">
|
||||
<div class="flex items-center justify-between px-3 py-1.5">
|
||||
<div class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300">
|
||||
<span>Status</span>
|
||||
<span>{$i18n.t('Status')}</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1790,14 +1790,14 @@
|
|||
showStatusPanel = false;
|
||||
}}
|
||||
>
|
||||
Close
|
||||
{$i18n.t('Close')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-0.5 px-3 pb-2">
|
||||
<div class="rounded-xl py-0.5 text-gray-600 dark:text-gray-400">
|
||||
<div class="flex min-h-4 items-center gap-3">
|
||||
<span class="min-w-0 flex-1 truncate">Context usage</span>
|
||||
<span class="min-w-0 flex-1 truncate">{$i18n.t('Context usage')}</span>
|
||||
<span
|
||||
class="shrink-0 font-mono text-[0.625rem] text-gray-400 dark:text-gray-600"
|
||||
>
|
||||
|
|
@ -1818,7 +1818,7 @@
|
|||
|
||||
{#if messageQueue.length}
|
||||
<div class="flex min-h-5 items-center gap-3 text-gray-600 dark:text-gray-400">
|
||||
<span class="min-w-0 flex-1 truncate">Queued messages</span>
|
||||
<span class="min-w-0 flex-1 truncate">{$i18n.t('Queued messages')}</span>
|
||||
<span class="font-mono text-[0.625rem] text-gray-400 dark:text-gray-600">
|
||||
{messageQueue.length}
|
||||
</span>
|
||||
|
|
@ -1827,7 +1827,7 @@
|
|||
|
||||
{#if chatTasks.length}
|
||||
<div class="flex min-h-5 items-center gap-3 text-gray-600 dark:text-gray-400">
|
||||
<span class="min-w-0 flex-1 truncate">Tasks</span>
|
||||
<span class="min-w-0 flex-1 truncate">{$i18n.t('Tasks')}</span>
|
||||
<span class="font-mono text-[0.625rem] text-gray-400 dark:text-gray-600">
|
||||
{chatTasks.length}
|
||||
</span>
|
||||
|
|
@ -1835,7 +1835,7 @@
|
|||
{/if}
|
||||
|
||||
<div class="flex min-h-5 items-center gap-3 text-gray-600 dark:text-gray-400">
|
||||
<span class="min-w-0 flex-1 truncate">Chat ID</span>
|
||||
<span class="min-w-0 flex-1 truncate">{$i18n.t('Chat ID')}</span>
|
||||
{#if chatId}
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -1869,7 +1869,7 @@
|
|||
<div class="flex items-center justify-between w-full">
|
||||
<div class="pl-[0.0625rem] flex items-center gap-2 text-sm dark:text-gray-500">
|
||||
<img
|
||||
alt="model profile"
|
||||
alt={$i18n.t('model profile')}
|
||||
class="size-3.5 max-w-[1.75rem] object-cover rounded-full"
|
||||
src={`${WEBUI_API_BASE_URL}/models/model/profile/image?id=${$models.find((model) => model.id === atSelectedModel.id).id}&lang=${$i18n.language}`}
|
||||
/>
|
||||
|
|
@ -1991,7 +1991,7 @@
|
|||
<button
|
||||
type="button"
|
||||
class="absolute top-2.5 right-3 z-20 p-1 rounded-lg hover:bg-gray-100/50 dark:hover:bg-gray-800/50"
|
||||
aria-label="Expand input"
|
||||
aria-label={$i18n.t('Expand input')}
|
||||
on:click={() => {
|
||||
showInputModal = true;
|
||||
}}
|
||||
|
|
@ -2330,7 +2330,7 @@
|
|||
>
|
||||
<button
|
||||
class="translate-y-[0.5px] px-1 flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg self-center transition"
|
||||
aria-label="Available Tools"
|
||||
aria-label={$i18n.t('Available Tools')}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showTools = !showTools;
|
||||
|
|
@ -2353,7 +2353,7 @@
|
|||
>
|
||||
<button
|
||||
class="translate-y-[0.5px] px-1 flex gap-1 items-center text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 rounded-lg self-center transition"
|
||||
aria-label="Available Skills"
|
||||
aria-label={$i18n.t('Available Skills')}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showSkills = !showSkills;
|
||||
|
|
@ -2607,7 +2607,7 @@
|
|||
toast.error($i18n.t('Permission denied when accessing microphone'));
|
||||
}
|
||||
}}
|
||||
aria-label="Voice Input"
|
||||
aria-label={$i18n.t('Voice Input')}
|
||||
>
|
||||
<Mic className="size-[1.125rem]" />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
import { KokoroWorker } from '$lib/workers/KokoroWorker';
|
||||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let eventTarget: EventTarget;
|
||||
export let submitPrompt: Function;
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
...videoInputDevices,
|
||||
{
|
||||
deviceId: 'screen',
|
||||
label: 'Screen Share'
|
||||
label: $i18n.t('Screen Share')
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import Knobs from '$lib/components/icons/Knobs.svelte';
|
||||
import Sparkles from '$lib/components/icons/Sparkles.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let query = '';
|
||||
export let onSelect = (e) => {};
|
||||
|
|
@ -142,10 +142,10 @@
|
|||
|
||||
{#each commandItems as item, commandIdx}
|
||||
{#if item.data.id === 'temporary'}
|
||||
<Tooltip content="Toggle temporary chat for this new chat." placement="top">
|
||||
<Tooltip content={$i18n.t('Toggle temporary chat for this new chat.')} placement="top">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Temporary: toggle temporary chat for this new chat."
|
||||
aria-label={$i18n.t('Temporary: toggle temporary chat for this new chat.')}
|
||||
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
|
||||
{commandIdx === selectedIdx ? 'app-interactive-active' : ''}"
|
||||
on:mousedown={(e) => e.preventDefault()}
|
||||
|
|
@ -166,18 +166,21 @@
|
|||
{/if}
|
||||
</span>
|
||||
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
|
||||
<span class="truncate">Temporary</span>
|
||||
<span class="truncate">{$i18n.t('Temporary')}</span>
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">
|
||||
{temporaryEnabled ? 'On' : 'Off'}
|
||||
{temporaryEnabled ? $i18n.t('On') : $i18n.t('Off')}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{:else if item.data.id === 'compact'}
|
||||
<Tooltip content="Shorten older messages so this chat can keep going." placement="top">
|
||||
<Tooltip
|
||||
content={$i18n.t('Shorten older messages so this chat can keep going.')}
|
||||
placement="top"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Compact: shorten older messages so this chat can keep going."
|
||||
aria-label={$i18n.t('Compact: shorten older messages so this chat can keep going.')}
|
||||
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
|
||||
{commandIdx === selectedIdx ? 'app-interactive-active' : ''} disabled:opacity-50"
|
||||
disabled={compactDisabled}
|
||||
|
|
@ -220,20 +223,20 @@
|
|||
{/if}
|
||||
</span>
|
||||
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
|
||||
<span class="truncate">Compact</span>
|
||||
<span class="truncate">{$i18n.t('Compact')}</span>
|
||||
{#if contextHasThreshold}
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">
|
||||
{contextCirclePercent}% full
|
||||
{$i18n.t('{{percent}}% full', { percent: contextCirclePercent })}
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{:else if item.data.id === 'fork'}
|
||||
<Tooltip content="Fork the current chat branch into a new chat." placement="top">
|
||||
<Tooltip content={$i18n.t('Fork the current chat branch into a new chat.')} placement="top">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Fork: fork the current chat branch into a new chat."
|
||||
aria-label={$i18n.t('Fork: fork the current chat branch into a new chat.')}
|
||||
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
|
||||
{commandIdx === selectedIdx ? 'app-interactive-active' : ''} disabled:opacity-50"
|
||||
disabled={forkDisabled}
|
||||
|
|
@ -268,16 +271,18 @@
|
|||
</svg>
|
||||
</span>
|
||||
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
|
||||
<span class="truncate">Fork</span>
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">Current branch</span>
|
||||
<span class="truncate">{$i18n.t('Fork')}</span>
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">
|
||||
{$i18n.t('Current branch')}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{:else if item.data.id === 'status'}
|
||||
<Tooltip content="Check what is running in this chat." placement="top">
|
||||
<Tooltip content={$i18n.t('Check what is running in this chat.')} placement="top">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Status: check what is running in this chat."
|
||||
aria-label={$i18n.t('Status: check what is running in this chat.')}
|
||||
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
|
||||
{commandIdx === selectedIdx ? 'app-interactive-active' : ''}"
|
||||
on:mousedown={(e) => e.preventDefault()}
|
||||
|
|
@ -306,18 +311,18 @@
|
|||
</svg>
|
||||
</span>
|
||||
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
|
||||
<span class="truncate">Status</span>
|
||||
<span class="truncate">{$i18n.t('Status')}</span>
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">
|
||||
Check what is running in this chat.
|
||||
{$i18n.t('Check what is running in this chat.')}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{:else if item.data.id === 'model'}
|
||||
<Tooltip content="Show or switch the current model." placement="top">
|
||||
<Tooltip content={$i18n.t('Show or switch the current model.')} placement="top">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Model: show or switch the current model."
|
||||
aria-label={$i18n.t('Model: show or switch the current model.')}
|
||||
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
|
||||
{commandIdx === selectedIdx ? 'app-interactive-active' : ''}"
|
||||
on:mousedown={(e) => e.preventDefault()}
|
||||
|
|
@ -334,16 +339,16 @@
|
|||
<Sparkles className="size-3.5" />
|
||||
</span>
|
||||
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
|
||||
<span class="truncate">Model</span>
|
||||
<span class="truncate">{$i18n.t('Model')}</span>
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">/model</span>
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{:else if item.data.id === 'settings'}
|
||||
<Tooltip content="Open settings." placement="top">
|
||||
<Tooltip content={$i18n.t('Open settings.')} placement="top">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Settings: open settings."
|
||||
aria-label={$i18n.t('Settings: open settings.')}
|
||||
class="slash-command-row flex items-center gap-2 w-full h-6 px-2 rounded-xl text-xs text-left transition-colors duration-75
|
||||
{commandIdx === selectedIdx ? 'app-interactive-active' : ''}"
|
||||
on:mousedown={(e) => e.preventDefault()}
|
||||
|
|
@ -360,7 +365,7 @@
|
|||
<Knobs className="size-3.5" />
|
||||
</span>
|
||||
<span class="flex-1 min-w-0 flex items-baseline gap-1.5 overflow-hidden">
|
||||
<span class="truncate">Settings</span>
|
||||
<span class="truncate">{$i18n.t('Settings')}</span>
|
||||
<span class="app-muted text-[0.625rem] truncate shrink-0">/settings</span>
|
||||
</span>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import AddFilesPlaceholder from '$lib/components/AddFilesPlaceholder.svelte';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
</script>
|
||||
|
||||
|
|
@ -9,7 +12,7 @@
|
|||
class="absolute inset-0 w-full h-full flex z-[9999] touch-none pointer-events-none"
|
||||
id="dropzone"
|
||||
role="region"
|
||||
aria-label="Drag and Drop Container"
|
||||
aria-label={$i18n.t('Drag and Drop Container')}
|
||||
>
|
||||
<div
|
||||
class="absolute w-full h-full backdrop-blur-sm bg-gray-100/50 dark:bg-gray-900/80 flex justify-center"
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
import AttachWebpageModal from './AttachWebpageModal.svelte';
|
||||
import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let files = [];
|
||||
|
||||
|
|
@ -54,13 +54,13 @@
|
|||
const toolApprovalModes = [
|
||||
{
|
||||
value: 'full',
|
||||
label: 'Full access',
|
||||
description: 'Run tools without asking for approval.'
|
||||
label: $i18n.t('Full access'),
|
||||
description: $i18n.t('Run tools without asking for approval.')
|
||||
},
|
||||
{
|
||||
value: 'ask',
|
||||
label: 'Ask for approval',
|
||||
description: 'Stop before each tool call until you allow or deny it.'
|
||||
label: $i18n.t('Ask for approval'),
|
||||
description: $i18n.t('Stop before each tool call until you allow or deny it.')
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
<script lang="ts">
|
||||
import DOMPurify from 'dompurify';
|
||||
import type { Token } from 'marked';
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import { settings } from '$lib/stores';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let id: string;
|
||||
export let token: Token;
|
||||
|
||||
|
|
@ -23,7 +26,7 @@
|
|||
<video
|
||||
class="w-full my-2"
|
||||
src={videoSrc.replaceAll('&', '&')}
|
||||
title="Video player"
|
||||
title={$i18n.t('Video player')}
|
||||
frameborder="0"
|
||||
referrerpolicy="strict-origin-when-cross-origin"
|
||||
controls
|
||||
|
|
@ -40,7 +43,7 @@
|
|||
<audio
|
||||
class="w-full my-2"
|
||||
src={audioSrc.replaceAll('&', '&')}
|
||||
title="Audio player"
|
||||
title={$i18n.t('Audio player')}
|
||||
controls
|
||||
></audio>
|
||||
{:else}
|
||||
|
|
@ -55,7 +58,7 @@
|
|||
<iframe
|
||||
class="w-full aspect-video my-2"
|
||||
src={`https://www.youtube.com/embed/${ytId}`}
|
||||
title="YouTube video player"
|
||||
title={$i18n.t('YouTube video player')}
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
referrerpolicy="strict-origin-when-cross-origin"
|
||||
|
|
@ -70,7 +73,7 @@
|
|||
<iframe
|
||||
class="w-full my-2"
|
||||
src={iframeSrc}
|
||||
title="Embedded content"
|
||||
title={$i18n.t('Embedded content')}
|
||||
frameborder="0"
|
||||
sandbox
|
||||
on:load={(e) => {
|
||||
|
|
@ -107,7 +110,7 @@
|
|||
<iframe
|
||||
class="w-full my-2"
|
||||
src={`${WEBUI_BASE_URL}/api/v1/files/${fileId}/content/html`}
|
||||
title="Content"
|
||||
title={$i18n.t('Content')}
|
||||
frameborder="0"
|
||||
sandbox="{($settings?.iframeSandboxAllowScripts ?? true)
|
||||
? 'allow-scripts'
|
||||
|
|
|
|||
|
|
@ -1498,9 +1498,9 @@
|
|||
{/each}
|
||||
|
||||
{#if message.done && !readOnly && forkHandler && ($user?.role === 'admin' || ($user?.permissions?.chat?.import ?? true))}
|
||||
<Tooltip content="Fork chat" placement="bottom">
|
||||
<Tooltip content={$i18n.t('Fork chat')} placement="bottom">
|
||||
<button
|
||||
aria-label="Fork chat"
|
||||
aria-label={$i18n.t('Fork chat')}
|
||||
class="{isLastMessage || ($settings?.highContrastMode ?? false)
|
||||
? 'visible'
|
||||
: 'hover-reveal'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
import ModelItem from './ModelItem.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let id = '';
|
||||
|
|
@ -1296,8 +1296,10 @@
|
|||
>
|
||||
<Spinner className="size-3 shrink-0 text-gray-400 dark:text-gray-500" />
|
||||
<div class="min-w-0 flex-1 truncate">
|
||||
Downloading "{downloadName}"{download?.providerLabel
|
||||
? ` from ${download.providerLabel}`
|
||||
{$i18n.t('Downloading "{{name}}"', {
|
||||
name: downloadName
|
||||
})}{download?.providerLabel
|
||||
? ` ${$i18n.t('from {{provider}}', { provider: download.providerLabel })}`
|
||||
: ''}
|
||||
</div>
|
||||
{#if 'pullProgress' in download}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
import Knobs from '../icons/Knobs.svelte';
|
||||
import { isTemporaryChatId } from '$lib/utils/chatId';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let initNewChat: Function;
|
||||
export let readOnly: boolean = false;
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
on:click={() => {
|
||||
initNewChat();
|
||||
}}
|
||||
aria-label="New Chat"
|
||||
aria-label={$i18n.t('New Chat')}
|
||||
/>
|
||||
|
||||
<nav
|
||||
|
|
@ -223,7 +223,7 @@
|
|||
on:click={() => {
|
||||
initNewChat();
|
||||
}}
|
||||
aria-label="New Chat"
|
||||
aria-label={$i18n.t('New Chat')}
|
||||
>
|
||||
<ChatPlus className="size-4.5" strokeWidth="1.5" />
|
||||
</button>
|
||||
|
|
@ -237,7 +237,7 @@
|
|||
on:click={async () => {
|
||||
await showControls.set(!$showControls);
|
||||
}}
|
||||
aria-label="Controls"
|
||||
aria-label={$i18n.t('Controls')}
|
||||
>
|
||||
<Knobs className="size-5" strokeWidth="1" />
|
||||
</button>
|
||||
|
|
@ -264,7 +264,7 @@
|
|||
<Banner
|
||||
banner={{
|
||||
type: 'info',
|
||||
title: 'Trial License',
|
||||
title: $i18n.t('Trial License'),
|
||||
content: $i18n.t(
|
||||
'You are currently using a trial license. Please contact support to upgrade your license.'
|
||||
)
|
||||
|
|
@ -276,7 +276,7 @@
|
|||
<Banner
|
||||
banner={{
|
||||
type: 'error',
|
||||
title: 'License Error',
|
||||
title: $i18n.t('License Error'),
|
||||
content: $i18n.t(
|
||||
'Exceeded the number of seats in your license. Please contact support to increase the number of seats.'
|
||||
)
|
||||
|
|
|
|||
|
|
@ -56,10 +56,16 @@
|
|||
<PinSlash />
|
||||
{/if}
|
||||
</ControlButton>
|
||||
<ControlButton on:click={() => setLayoutDirection('vertical')} title="Vertical Layout">
|
||||
<ControlButton
|
||||
on:click={() => setLayoutDirection('vertical')}
|
||||
title={$i18n.t('Vertical Layout')}
|
||||
>
|
||||
<AlignVertical className="size-4" />
|
||||
</ControlButton>
|
||||
<ControlButton on:click={() => setLayoutDirection('horizontal')} title="Horizontal Layout">
|
||||
<ControlButton
|
||||
on:click={() => setLayoutDirection('horizontal')}
|
||||
title={$i18n.t('Horizontal Layout')}
|
||||
>
|
||||
<AlignHorizontal className="size-4" />
|
||||
</ControlButton>
|
||||
</Controls>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
import FolderPlaceholder from './Placeholder/FolderPlaceholder.svelte';
|
||||
import FolderTitle from './Placeholder/FolderTitle.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let createMessagePair: Function;
|
||||
export let stopResponse: Function;
|
||||
|
|
@ -209,7 +209,7 @@
|
|||
|
||||
{#if models[selectedModelIdx]?.info?.meta?.user}
|
||||
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
|
||||
By
|
||||
{$i18n.t('By')}
|
||||
{#if models[selectedModelIdx]?.info?.meta?.user.community}
|
||||
<a
|
||||
href="https://openwebui.com/m/{models[selectedModelIdx]?.info?.meta?.user
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
import UserSettingRow from './UserSettingRow.svelte';
|
||||
import UserSettingSection from './UserSettingSection.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
let ollamaVersion = '';
|
||||
|
||||
|
|
@ -120,7 +120,8 @@
|
|||
<span>{$WEBUI_NAME}</span> -
|
||||
{/if}
|
||||
|
||||
<span class="capitalize">{$config?.license_metadata?.type}</span> license purchased by
|
||||
<span class="capitalize">{$config?.license_metadata?.type}</span>
|
||||
{$i18n.t('license purchased by')}
|
||||
<span class="capitalize">{$config?.license_metadata?.organization_name}</span>
|
||||
</div>
|
||||
{:else}
|
||||
|
|
@ -144,8 +145,10 @@
|
|||
{/if}
|
||||
|
||||
<div class="text-xs text-gray-400 dark:text-gray-500">
|
||||
Emoji graphics provided by
|
||||
<a href="https://github.com/jdecked/twemoji" target="_blank">Twemoji</a>, licensed under
|
||||
{$i18n.t('Emoji graphics provided by')}
|
||||
<a href="https://github.com/jdecked/twemoji" target="_blank">Twemoji</a>, {$i18n.t(
|
||||
'licensed under'
|
||||
)}
|
||||
<a href="https://creativecommons.org/licenses/by/4.0/" target="_blank">CC-BY 4.0</a>.
|
||||
</div>
|
||||
|
||||
|
|
@ -153,10 +156,11 @@
|
|||
<!-- LICENSE covers this Open WebUI copyright attribution.
|
||||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
Copyright (c) {new Date().getFullYear()}
|
||||
{$i18n.t('Copyright (c)')}
|
||||
{new Date().getFullYear()}
|
||||
<a href="https://openwebui.com" target="_blank" class="underline">Open WebUI Inc.</a>
|
||||
<a href="https://github.com/open-webui/open-webui/blob/main/LICENSE" target="_blank"
|
||||
>All rights reserved.</a
|
||||
>{$i18n.t('All rights reserved.')}</a
|
||||
>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { config, models, settings, theme, user } from '$lib/stores';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
import AdvancedParams from './Advanced/AdvancedParams.svelte';
|
||||
import Textarea from '$lib/components/common/Textarea.svelte';
|
||||
|
|
@ -240,7 +240,7 @@
|
|||
</UserSettingRow>
|
||||
{#if $i18n.language === 'en-US' && !($config?.license_metadata ?? false)}
|
||||
<div class="-mt-1 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
Couldn't find your language?
|
||||
{$i18n.t("Couldn't find your language?")}
|
||||
<a
|
||||
class="font-normal underline text-gray-400 dark:text-gray-600"
|
||||
href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization"
|
||||
|
|
@ -249,7 +249,7 @@
|
|||
<!-- LICENSE covers this Open WebUI wordmark.
|
||||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
Help us translate Open WebUI!
|
||||
{$i18n.t('Help us translate Open WebUI!')}
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,14 @@
|
|||
let events: { event: string; label: string; description?: string }[] = [
|
||||
{
|
||||
event: 'chat.finished',
|
||||
label: 'Chat finished',
|
||||
description: 'A chat run finished successfully.'
|
||||
label: $i18n.t('Chat finished'),
|
||||
description: $i18n.t('A chat run finished successfully.')
|
||||
},
|
||||
{ event: 'chat.failed', label: 'Chat failed', description: 'A chat run failed.' }
|
||||
{
|
||||
event: 'chat.failed',
|
||||
label: $i18n.t('Chat failed'),
|
||||
description: $i18n.t('A chat run failed.')
|
||||
}
|
||||
];
|
||||
let loadingTargets = false;
|
||||
let savingTarget = false;
|
||||
|
|
|
|||
|
|
@ -123,9 +123,9 @@
|
|||
];
|
||||
|
||||
const heatmapModes: Array<{ value: HeatmapMode; label: string }> = [
|
||||
{ value: 'daily', label: 'Daily' },
|
||||
{ value: 'weekly', label: 'Weekly' },
|
||||
{ value: 'cumulative', label: 'Cumulative' }
|
||||
{ value: 'daily', label: $i18n.t('Daily') },
|
||||
{ value: 'weekly', label: $i18n.t('Weekly') },
|
||||
{ value: 'cumulative', label: $i18n.t('Cumulative') }
|
||||
];
|
||||
|
||||
const loadUsage = async () => {
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@
|
|||
const allSettings: SettingsTab[] = [
|
||||
{
|
||||
id: 'general',
|
||||
title: 'General',
|
||||
title: $i18n.t('General'),
|
||||
keywords: [
|
||||
'advancedparams',
|
||||
'advancedparameters',
|
||||
|
|
@ -214,7 +214,7 @@
|
|||
},
|
||||
{
|
||||
id: 'interface',
|
||||
title: 'Interface',
|
||||
title: $i18n.t('Interface'),
|
||||
keywords: [
|
||||
'allow user location',
|
||||
'allow voice interruption in call',
|
||||
|
|
@ -350,7 +350,7 @@
|
|||
},
|
||||
{
|
||||
id: 'notifications',
|
||||
title: 'Notifications',
|
||||
title: $i18n.t('Notifications'),
|
||||
keywords: [
|
||||
'browser notifications',
|
||||
'browsernotifications',
|
||||
|
|
@ -366,7 +366,7 @@
|
|||
},
|
||||
{
|
||||
id: 'shortcuts',
|
||||
title: 'Keyboard',
|
||||
title: $i18n.t('Keyboard'),
|
||||
keywords: [
|
||||
'commands',
|
||||
'hotkeys',
|
||||
|
|
@ -381,7 +381,7 @@
|
|||
},
|
||||
{
|
||||
id: 'connections',
|
||||
title: 'Connections',
|
||||
title: $i18n.t('Connections'),
|
||||
keywords: [
|
||||
'addconnection',
|
||||
'add connection',
|
||||
|
|
@ -394,7 +394,7 @@
|
|||
},
|
||||
{
|
||||
id: 'tools',
|
||||
title: 'Integrations',
|
||||
title: $i18n.t('Integrations'),
|
||||
keywords: [
|
||||
'addconnection',
|
||||
'add connection',
|
||||
|
|
@ -412,7 +412,7 @@
|
|||
|
||||
{
|
||||
id: 'personalization',
|
||||
title: 'Personalization',
|
||||
title: $i18n.t('Personalization'),
|
||||
keywords: [
|
||||
'account preferences',
|
||||
'account settings',
|
||||
|
|
@ -434,7 +434,7 @@
|
|||
},
|
||||
{
|
||||
id: 'audio',
|
||||
title: 'Audio',
|
||||
title: $i18n.t('Audio'),
|
||||
keywords: [
|
||||
'audio config',
|
||||
'audio control',
|
||||
|
|
@ -510,7 +510,7 @@
|
|||
},
|
||||
{
|
||||
id: 'data_controls',
|
||||
title: 'Data Controls',
|
||||
title: $i18n.t('Data Controls'),
|
||||
keywords: [
|
||||
'archive all chats',
|
||||
'archive chats',
|
||||
|
|
@ -545,7 +545,7 @@
|
|||
},
|
||||
{
|
||||
id: 'usage',
|
||||
title: 'Usage',
|
||||
title: $i18n.t('Usage'),
|
||||
keywords: [
|
||||
'activity',
|
||||
'activity heatmap',
|
||||
|
|
@ -563,7 +563,7 @@
|
|||
},
|
||||
{
|
||||
id: 'archived_chats',
|
||||
title: 'Archived Chats',
|
||||
title: $i18n.t('Archived Chats'),
|
||||
keywords: [
|
||||
'archive',
|
||||
'archive chat',
|
||||
|
|
@ -582,7 +582,7 @@
|
|||
},
|
||||
{
|
||||
id: 'account',
|
||||
title: 'Account',
|
||||
title: $i18n.t('Account'),
|
||||
keywords: [
|
||||
'account preferences',
|
||||
'account settings',
|
||||
|
|
@ -632,7 +632,7 @@
|
|||
},
|
||||
{
|
||||
id: 'about',
|
||||
title: 'About',
|
||||
title: $i18n.t('About'),
|
||||
keywords: [
|
||||
'about app',
|
||||
'about me',
|
||||
|
|
@ -683,12 +683,12 @@
|
|||
const adminSettings: SettingsTab[] = [
|
||||
{
|
||||
id: 'admin:general',
|
||||
title: 'General',
|
||||
title: $i18n.t('General'),
|
||||
keywords: ['general', 'admin', 'settings', 'version', 'update', 'community', 'channels']
|
||||
},
|
||||
{
|
||||
id: 'admin:authentication',
|
||||
title: 'Authentication',
|
||||
title: $i18n.t('Authentication'),
|
||||
keywords: [
|
||||
'authentication',
|
||||
'auth',
|
||||
|
|
@ -703,7 +703,7 @@
|
|||
},
|
||||
{
|
||||
id: 'admin:connections',
|
||||
title: 'Connections',
|
||||
title: $i18n.t('Connections'),
|
||||
keywords: [
|
||||
'connections',
|
||||
'ollama',
|
||||
|
|
@ -716,7 +716,7 @@
|
|||
},
|
||||
{
|
||||
id: 'admin:models',
|
||||
title: 'Models',
|
||||
title: $i18n.t('Models'),
|
||||
keywords: [
|
||||
'models',
|
||||
'pull',
|
||||
|
|
@ -731,63 +731,63 @@
|
|||
},
|
||||
{
|
||||
id: 'admin:subagents',
|
||||
title: 'Sub-agents',
|
||||
title: $i18n.t('Sub-agents'),
|
||||
keywords: ['sub-agents', 'subagents', 'delegation', 'background', 'agents']
|
||||
},
|
||||
{
|
||||
id: 'admin:interface',
|
||||
title: 'Interface',
|
||||
title: $i18n.t('Interface'),
|
||||
keywords: ['interface', 'ui', 'appearance', 'banners', 'tasks', 'prompt suggestions', 'tags']
|
||||
},
|
||||
{
|
||||
id: 'admin:audio',
|
||||
title: 'Audio',
|
||||
title: $i18n.t('Audio'),
|
||||
keywords: ['audio', 'voice', 'speech', 'tts', 'stt', 'whisper', 'deepgram', 'azure']
|
||||
},
|
||||
{
|
||||
id: 'admin:images',
|
||||
title: 'Images',
|
||||
title: $i18n.t('Images'),
|
||||
keywords: ['images', 'generation', 'dalle', 'stable diffusion', 'comfyui', 'automatic1111']
|
||||
},
|
||||
{
|
||||
id: 'admin:evaluations',
|
||||
title: 'Evaluations',
|
||||
title: $i18n.t('Evaluations'),
|
||||
keywords: ['evaluations', 'feedback', 'rating', 'arena', 'leaderboard', 'preference']
|
||||
},
|
||||
{
|
||||
id: 'admin:analytics',
|
||||
title: 'Analytics',
|
||||
title: $i18n.t('Analytics'),
|
||||
keywords: ['analytics', 'usage', 'stats', 'dashboard', 'models', 'users', 'messages']
|
||||
},
|
||||
{
|
||||
id: 'admin:integrations',
|
||||
title: 'Integrations',
|
||||
title: $i18n.t('Integrations'),
|
||||
keywords: ['tools', 'integrations', 'plugins', 'extensions', 'functions', 'openapi', 'server']
|
||||
},
|
||||
{
|
||||
id: 'admin:documents',
|
||||
title: 'Documents',
|
||||
title: $i18n.t('Documents'),
|
||||
keywords: ['documents', 'files', 'rag', 'knowledge', 'upload', 'embedding', 'vector db']
|
||||
},
|
||||
{
|
||||
id: 'admin:web',
|
||||
title: 'Web Search',
|
||||
title: $i18n.t('Web Search'),
|
||||
keywords: ['web search', 'google', 'bing', 'duckduckgo', 'serp', 'searxng', 'tavily', 'exa']
|
||||
},
|
||||
{
|
||||
id: 'admin:code-execution',
|
||||
title: 'Code Execution',
|
||||
title: $i18n.t('Code Execution'),
|
||||
keywords: ['code execution', 'python', 'sandbox', 'compiler', 'jupyter', 'interpreter']
|
||||
},
|
||||
{
|
||||
id: 'admin:pipelines',
|
||||
title: 'Pipelines',
|
||||
title: $i18n.t('Pipelines'),
|
||||
keywords: ['pipelines', 'workflows', 'filters', 'valves', 'middleware']
|
||||
},
|
||||
|
||||
{
|
||||
id: 'admin:db',
|
||||
title: 'Database',
|
||||
title: $i18n.t('Database'),
|
||||
keywords: ['database', 'export', 'import', 'backup', 'chats', 'users']
|
||||
}
|
||||
];
|
||||
|
|
@ -1189,7 +1189,7 @@
|
|||
}}
|
||||
>
|
||||
<AdminTabIcon id={adminTabSegment(tab.id)} className="size-3.5" strokeWidth="2" />
|
||||
<span>{$i18n.t(tab.title)}</span>
|
||||
<span>{tab.title}</span>
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import CodeEditor from './CodeEditor.svelte';
|
||||
import Drawer from './Drawer.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
let {
|
||||
show = $bindable(),
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
</div>
|
||||
<button
|
||||
class="self-center"
|
||||
aria-label="Close"
|
||||
aria-label={$i18n.t('Close')}
|
||||
onclick={() => {
|
||||
show = false;
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-7 h-7 inline-flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-500 dark:text-gray-400"
|
||||
on:click={zoomOut}
|
||||
aria-label="Zoom out"
|
||||
aria-label={$i18n.t('Zoom out')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -224,7 +224,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-12 h-7 px-1.5 py-1 text-center text-[0.6875rem] font-normal text-gray-500 dark:text-gray-400 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition tabular-nums"
|
||||
on:click={resetView}
|
||||
aria-label="Reset zoom"
|
||||
aria-label={$i18n.t('Reset zoom')}
|
||||
>
|
||||
{Math.round(zoomLevel * 100)}%
|
||||
</button>
|
||||
|
|
@ -232,7 +232,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-7 h-7 inline-flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-500 dark:text-gray-400"
|
||||
on:click={zoomIn}
|
||||
aria-label="Zoom in"
|
||||
aria-label={$i18n.t('Zoom in')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
import CodeBlock from '$lib/components/chat/Messages/CodeBlock.svelte';
|
||||
import Markdown from '$lib/components/chat/Messages/Markdown.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
const CONTENT_PREVIEW_LIMIT = 10000;
|
||||
let expandedContent = false;
|
||||
|
|
@ -545,7 +545,7 @@
|
|||
{@html excelHtml}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-gray-500 text-sm p-4">No content available</div>
|
||||
<div class="text-gray-500 text-sm p-4">{$i18n.t('No content available')}</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{:else if isCode}
|
||||
|
|
@ -571,7 +571,7 @@
|
|||
{:else if docxData}
|
||||
<DocxPreview data={docxData} className="h-[60vh]" />
|
||||
{:else}
|
||||
<div class="text-gray-500 text-sm p-4">No content available</div>
|
||||
<div class="text-gray-500 text-sm p-4">{$i18n.t('No content available')}</div>
|
||||
{/if}
|
||||
{:else if isPptx}
|
||||
{#if pptxError}
|
||||
|
|
@ -583,7 +583,7 @@
|
|||
className="h-[60vh]"
|
||||
/>
|
||||
{:else}
|
||||
<div class="text-gray-500 text-sm p-4">No content available</div>
|
||||
<div class="text-gray-500 text-sm p-4">{$i18n.t('No content available')}</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="max-h-96 overflow-scroll scrollbar-hidden text-xs whitespace-pre-wrap">
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import Drawer from './Drawer.svelte';
|
||||
import RichTextInput from './RichTextInput.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let id = 'input-modal';
|
||||
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
</div>
|
||||
<button
|
||||
class="self-center"
|
||||
aria-label="Close"
|
||||
aria-label={$i18n.t('Close')}
|
||||
onclick={() => {
|
||||
show = false;
|
||||
onClose();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy, tick } from 'svelte';
|
||||
import { getContext, onMount, onDestroy, tick } from 'svelte';
|
||||
import pdfWorkerUrl from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
import panzoom, { type PanZoom } from 'panzoom';
|
||||
import { clampDocumentTargetPage } from '$lib/utils/documentPreview';
|
||||
import Spinner from './Spinner.svelte';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let url: string | null = null;
|
||||
export let data: ArrayBuffer | Uint8Array | null = null;
|
||||
export let className = 'w-full h-[70vh]';
|
||||
|
|
@ -548,7 +550,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-7 h-7 inline-flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-500 dark:text-gray-400 pointer-coarse:hidden"
|
||||
on:click={zoomOut}
|
||||
aria-label="Zoom out"
|
||||
aria-label={$i18n.t('Zoom out')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -567,7 +569,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-12 h-7 px-1.5 py-1 text-center text-[0.6875rem] font-normal text-gray-500 dark:text-gray-400 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition tabular-nums"
|
||||
on:click={resetView}
|
||||
aria-label="Reset zoom"
|
||||
aria-label={$i18n.t('Reset zoom')}
|
||||
>
|
||||
{Math.round(zoomLevel * 100)}%
|
||||
</button>
|
||||
|
|
@ -575,7 +577,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-7 h-7 inline-flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-500 dark:text-gray-400 pointer-coarse:hidden"
|
||||
on:click={zoomIn}
|
||||
aria-label="Zoom in"
|
||||
aria-label={$i18n.t('Zoom in')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy, onMount, tick } from 'svelte';
|
||||
import { getContext, onDestroy, onMount, tick } from 'svelte';
|
||||
import panzoom, { type PanZoom } from 'panzoom';
|
||||
import { clampDocumentTargetPage } from '$lib/utils/documentPreview';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let slides: string[] = [];
|
||||
export let currentSlide = 0;
|
||||
export let className = '';
|
||||
|
|
@ -356,7 +358,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-7 h-7 inline-flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-500 dark:text-gray-400 pointer-coarse:hidden"
|
||||
on:click={zoomOut}
|
||||
aria-label="Zoom out"
|
||||
aria-label={$i18n.t('Zoom out')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -375,7 +377,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-12 h-7 px-1.5 py-1 text-center text-[0.6875rem] font-normal text-gray-500 dark:text-gray-400 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition tabular-nums"
|
||||
on:click={resetView}
|
||||
aria-label="Reset zoom"
|
||||
aria-label={$i18n.t('Reset zoom')}
|
||||
>
|
||||
{Math.round(zoomLevel * 100)}%
|
||||
</button>
|
||||
|
|
@ -383,7 +385,7 @@
|
|||
type="button"
|
||||
class="shrink-0 min-w-7 h-7 inline-flex items-center justify-center p-1.5 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-500 dark:text-gray-400 pointer-coarse:hidden"
|
||||
on:click={zoomIn}
|
||||
aria-label="Zoom in"
|
||||
aria-label={$i18n.t('Zoom in')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { getContext, onDestroy, onMount } from 'svelte';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let open = false;
|
||||
export let side: 'left' | 'right' = 'right';
|
||||
|
|
@ -145,7 +147,7 @@
|
|||
on:keydown={resizeKeyHandler}
|
||||
role="separator"
|
||||
tabindex="0"
|
||||
aria-label="Resize panel"
|
||||
aria-label={$i18n.t('Resize panel')}
|
||||
aria-orientation="vertical"
|
||||
>
|
||||
<span
|
||||
|
|
@ -168,7 +170,7 @@
|
|||
on:keydown={resizeKeyHandler}
|
||||
role="separator"
|
||||
tabindex="0"
|
||||
aria-label="Resize panel"
|
||||
aria-label={$i18n.t('Resize panel')}
|
||||
aria-orientation="vertical"
|
||||
>
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
{valvesSpec.properties[property].title}
|
||||
|
||||
{#if (valvesSpec?.required ?? []).includes(property)}
|
||||
<span class=" text-gray-500">*required</span>
|
||||
<span class=" text-gray-500">{$i18n.t('*required')}</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { getContext, onDestroy, onMount, tick } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
import Modal from '$lib/components/common/Modal.svelte';
|
||||
import SearchInput from './Sidebar/SearchInput.svelte';
|
||||
|
|
@ -845,7 +845,7 @@
|
|||
}}
|
||||
>
|
||||
<button
|
||||
aria-label="Chat Menu"
|
||||
aria-label={$i18n.t('Chat Menu')}
|
||||
class="self-center dark:hover:text-white transition"
|
||||
>
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
} from '$lib/stores/chatList';
|
||||
import { onMount, getContext, tick, onDestroy } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
$: canImportChats = $user?.role === 'admin' || ($user?.permissions?.chat?.import ?? true);
|
||||
|
||||
|
|
@ -189,11 +189,11 @@
|
|||
|
||||
const getMenuItemMeta = (id) => {
|
||||
const items = {
|
||||
notes: { label: 'Notes', href: '/notes', iconType: 'note' },
|
||||
workspace: { label: 'Workspace', href: '/workspace', iconType: 'workspace' },
|
||||
automations: { label: 'Automations', href: '/automations', iconType: 'automations' },
|
||||
calendar: { label: 'Calendar', href: '/calendar', iconType: 'calendar' },
|
||||
playground: { label: 'Playground', href: '/playground', iconType: 'playground' }
|
||||
notes: { label: $i18n.t('Notes'), href: '/notes', iconType: 'note' },
|
||||
workspace: { label: $i18n.t('Workspace'), href: '/workspace', iconType: 'workspace' },
|
||||
automations: { label: $i18n.t('Automations'), href: '/automations', iconType: 'automations' },
|
||||
calendar: { label: $i18n.t('Calendar'), href: '/calendar', iconType: 'calendar' },
|
||||
playground: { label: $i18n.t('Playground'), href: '/playground', iconType: 'playground' }
|
||||
};
|
||||
return items[id];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -768,7 +768,7 @@
|
|||
>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Chat Menu"
|
||||
aria-label={$i18n.t('Chat Menu')}
|
||||
class="flex size-5 items-center justify-center self-center dark:hover:text-white transition m-0"
|
||||
>
|
||||
<MoreHorizontalIcon className="size-3.5" strokeWidth="2" />
|
||||
|
|
|
|||
|
|
@ -931,7 +931,7 @@
|
|||
{/if}
|
||||
|
||||
{#if chats === null && chatsLoading}
|
||||
<div class="flex gap-1 px-2 py-1.5" aria-label="Loading">
|
||||
<div class="flex gap-1 px-2 py-1.5" aria-label={$i18n.t('Loading')}>
|
||||
<span class="size-1 rounded-full bg-gray-400 animate-pulse dark:bg-gray-600"></span>
|
||||
<span
|
||||
class="size-1 rounded-full bg-gray-400 animate-pulse [animation-delay:150ms] dark:bg-gray-600"
|
||||
|
|
@ -974,7 +974,7 @@
|
|||
on:click={() => setFolderItems(true)}
|
||||
>
|
||||
{#if chatsLoading}
|
||||
<div class="flex gap-1 px-2 py-1.5" aria-label="Loading">
|
||||
<div class="flex gap-1 px-2 py-1.5" aria-label={$i18n.t('Loading')}>
|
||||
<span class="size-1 rounded-full bg-gray-400 animate-pulse dark:bg-gray-600"
|
||||
></span>
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -98,7 +98,10 @@
|
|||
{/if}
|
||||
|
||||
{#if !isWritable}
|
||||
<div class="shrink-0 text-[0.625rem] text-gray-400 dark:text-gray-600" title="Read only">
|
||||
<div
|
||||
class="shrink-0 text-[0.625rem] text-gray-400 dark:text-gray-600"
|
||||
title={$i18n.t('Read only')}
|
||||
>
|
||||
<Eye className="size-3" />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -108,7 +111,7 @@
|
|||
{#if expanded}
|
||||
<div class="pl-3">
|
||||
{#if loading && !loaded}
|
||||
<div class="flex gap-1 px-2 py-1.5" aria-label="Loading">
|
||||
<div class="flex gap-1 px-2 py-1.5" aria-label={$i18n.t('Loading')}>
|
||||
<span class="size-1 rounded-full bg-gray-400 animate-pulse dark:bg-gray-600"></span>
|
||||
<span
|
||||
class="size-1 rounded-full bg-gray-400 animate-pulse [animation-delay:150ms] dark:bg-gray-600"
|
||||
|
|
@ -143,7 +146,7 @@
|
|||
on:click={() => loadChats(true)}
|
||||
>
|
||||
{#if loading}
|
||||
<div class="flex gap-1 px-2 py-1.5" aria-label="Loading">
|
||||
<div class="flex gap-1 px-2 py-1.5" aria-label={$i18n.t('Loading')}>
|
||||
<span class="size-1 rounded-full bg-gray-400 animate-pulse dark:bg-gray-600"></span>
|
||||
<span
|
||||
class="size-1 rounded-full bg-gray-400 animate-pulse [animation-delay:150ms] dark:bg-gray-600"
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
import Download from '../icons/Download.svelte';
|
||||
import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
let loaded = false;
|
||||
|
||||
|
|
@ -261,7 +261,7 @@
|
|||
|
||||
const exportData = {
|
||||
chat: {
|
||||
title: 'Playground Chat',
|
||||
title: $i18n.t('Playground Chat'),
|
||||
models: [selectedModelId],
|
||||
params: system ? { system } : {},
|
||||
history: {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
import AccessButton from '$lib/components/common/AccessButton.svelte';
|
||||
import { extractInputVariables } from '$lib/utils';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
export let onSubmit: Function;
|
||||
export let onBack: null | Function = null;
|
||||
|
|
@ -622,13 +622,13 @@
|
|||
{#if info.meta.profile_image_url}
|
||||
<img
|
||||
src={info.meta.profile_image_url}
|
||||
alt="model profile"
|
||||
alt={$i18n.t('model profile')}
|
||||
class="size-full object-cover"
|
||||
/>
|
||||
{:else}
|
||||
<img
|
||||
src="{WEBUI_BASE_URL}/static/favicon.png"
|
||||
alt="model profile"
|
||||
alt={$i18n.t('model profile')}
|
||||
class="size-full object-cover"
|
||||
/>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, tick } from 'svelte';
|
||||
import { createEventDispatcher, getContext, tick } from 'svelte';
|
||||
|
||||
type Voice = {
|
||||
id: string;
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
export let className = 'w-full';
|
||||
export let selectedIds: string[] | null = null;
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
select: Voice;
|
||||
enableall: Voice[];
|
||||
|
|
@ -153,7 +155,9 @@
|
|||
enableVoices();
|
||||
}}
|
||||
>
|
||||
<span class="truncate">Enable all ({matchedVoices.length})</span>
|
||||
<span class="truncate"
|
||||
>{$i18n.t('Enable all ({{count}})', { count: matchedVoices.length })}</span
|
||||
>
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, tick } from 'svelte';
|
||||
import { createEventDispatcher, getContext, tick } from 'svelte';
|
||||
import Dropdown from '$lib/components/common/Dropdown.svelte';
|
||||
import Search from '$lib/components/icons/Search.svelte';
|
||||
import TTSVoiceInput from './TTSVoiceInput.svelte';
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
export let selectedIds: string[] | null = null;
|
||||
export let variant: 'inline' | 'dropdown' = 'inline';
|
||||
|
||||
const i18n: any = getContext('i18n');
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
select: Item;
|
||||
enableall: Item[];
|
||||
|
|
@ -101,7 +103,9 @@
|
|||
class="h-[1.6875rem] w-full rounded-xl px-2 text-left text-[0.8125rem] text-gray-700 transition-colors hover:bg-gray-50/40 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800/40 dark:hover:text-gray-100"
|
||||
on:click={enableItems}
|
||||
>
|
||||
<span class="truncate">Enable all ({matchedItems.length})</span>
|
||||
<span class="truncate"
|
||||
>{$i18n.t('Enable all ({{count}})', { count: matchedItems.length })}</span
|
||||
>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue