resolved conflicts #2

This commit is contained in:
Jannik Streidl 2024-05-02 21:46:24 +02:00
commit e57c0c301c
81 changed files with 2590 additions and 1074 deletions

View File

@ -10,7 +10,8 @@ OPENAI_API_KEY=''
# DO NOT TRACK
SCARF_NO_ANALYTICS=true
DO_NOT_TRACK=true
ANONYMIZED_TELEMETRY=false
# Use locally bundled version of the LiteLLM cost map json
# to avoid repetitive startup connections
LITELLM_LOCAL_MODEL_COST_MAP="True"
LITELLM_LOCAL_MODEL_COST_MAP="True"

11
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: pip
directory: "/backend"
schedule:
interval: daily
time: "13:00"
groups:
python-packages:
patterns:
- "*"

View File

@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.123] - 2024-05-02
### Added
- **🎨 New Landing Page Design**: Refreshed design for a more modern look and optimized use of screen space.
- **📹 Youtube RAG Pipeline**: Introduces dedicated RAG pipeline for Youtube videos, enabling interaction with video transcriptions directly.
- **🔧 Enhanced Admin Panel**: Streamlined user management with options to add users directly or in bulk via CSV import.
- **👥 '@' Model Integration**: Easily switch to specific models during conversations; old collaborative chat feature phased out.
- **🌐 Language Enhancements**: Swedish translation added, plus improvements to German, Spanish, and the addition of Doge translation.
### Fixed
- **🗑️ Delete Chat Shortcut**: Addressed issue where shortcut wasn't functioning.
- **🖼️ Modal Closing Bug**: Resolved unexpected closure of modal when dragging from within.
- **✏️ Edit Button Styling**: Fixed styling inconsistency with edit buttons.
- **🌐 Image Generation Compatibility Issue**: Rectified image generation compatibility issue with third-party APIs.
- **📱 iOS PWA Icon Fix**: Corrected iOS PWA home screen icon shape.
- **🔍 Scroll Gesture Bug**: Adjusted gesture sensitivity to prevent accidental activation when scrolling through code on mobile; now requires scrolling from the leftmost side to open the sidebar.
### Changed
- **🔄 Unlimited Context Length**: Advanced settings now allow unlimited max context length (previously limited to 16000).
- **👑 Super Admin Assignment**: The first signup is automatically assigned a super admin role, unchangeable by other admins.
- **🛡️ Admin User Restrictions**: User action buttons from the admin panel are now disabled for users with admin roles.
- **🔝 Default Model Selector**: Set as default model option now exclusively available on the landing page.
## [0.1.122] - 2024-04-27
### Added

View File

@ -51,7 +51,8 @@ ENV OLLAMA_BASE_URL="/ollama" \
ENV OPENAI_API_KEY="" \
WEBUI_SECRET_KEY="" \
SCARF_NO_ANALYTICS=true \
DO_NOT_TRACK=true
DO_NOT_TRACK=true \
ANONYMIZED_TELEMETRY=false
# Use locally bundled version of the LiteLLM cost map json
# to avoid repetitive startup connections
@ -74,6 +75,10 @@ ENV HF_HOME="/app/backend/data/cache/embedding/models"
WORKDIR /app/backend
ENV HOME /root
RUN mkdir -p $HOME/.cache/chroma
RUN echo -n 00000000-0000-0000-0000-000000000000 > $HOME/.cache/chroma/telemetry_user_id
RUN if [ "$USE_OLLAMA" = "true" ]; then \
apt-get update && \
# Install pandoc and netcat
@ -129,4 +134,4 @@ COPY ./backend .
EXPOSE 8080
CMD [ "bash", "start.sh"]
CMD [ "bash", "start.sh"]

View File

@ -24,6 +24,7 @@ from utils.misc import calculate_sha256
from typing import Optional
from pydantic import BaseModel
from pathlib import Path
import mimetypes
import uuid
import base64
import json
@ -315,38 +316,50 @@ class GenerateImageForm(BaseModel):
def save_b64_image(b64_str):
image_id = str(uuid.uuid4())
file_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}.png")
try:
# Split the base64 string to get the actual image data
img_data = base64.b64decode(b64_str)
header, encoded = b64_str.split(",", 1)
mime_type = header.split(";")[0]
# Write the image data to a file
img_data = base64.b64decode(encoded)
image_id = str(uuid.uuid4())
image_format = mimetypes.guess_extension(mime_type)
image_filename = f"{image_id}{image_format}"
file_path = IMAGE_CACHE_DIR / f"{image_filename}"
with open(file_path, "wb") as f:
f.write(img_data)
return image_id
return image_filename
except Exception as e:
log.error(f"Error saving image: {e}")
log.exception(f"Error saving image: {e}")
return None
def save_url_image(url):
image_id = str(uuid.uuid4())
file_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}.png")
try:
r = requests.get(url)
r.raise_for_status()
if r.headers["content-type"].split("/")[0] == "image":
with open(file_path, "wb") as image_file:
image_file.write(r.content)
mime_type = r.headers["content-type"]
image_format = mimetypes.guess_extension(mime_type)
if not image_format:
raise ValueError("Could not determine image type from MIME type")
file_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}{image_format}")
with open(file_path, "wb") as image_file:
for chunk in r.iter_content(chunk_size=8192):
image_file.write(chunk)
return image_id, image_format
else:
log.error(f"Url does not point to an image.")
return None, None
return image_id
except Exception as e:
log.exception(f"Error saving image: {e}")
return None
return None, None
@app.post("/generations")
@ -385,8 +398,8 @@ def generate_image(
images = []
for image in res["data"]:
image_id = save_b64_image(image["b64_json"])
images.append({"url": f"/cache/image/generations/{image_id}.png"})
image_filename = save_b64_image(image["b64_json"])
images.append({"url": f"/cache/image/generations/{image_filename}"})
file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}.json")
with open(file_body_path, "w") as f:
@ -422,8 +435,10 @@ def generate_image(
images = []
for image in res["data"]:
image_id = save_url_image(image["url"])
images.append({"url": f"/cache/image/generations/{image_id}.png"})
image_id, image_format = save_url_image(image["url"])
images.append(
{"url": f"/cache/image/generations/{image_id}{image_format}"}
)
file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}.json")
with open(file_body_path, "w") as f:
@ -460,8 +475,8 @@ def generate_image(
images = []
for image in res["images"]:
image_id = save_b64_image(image)
images.append({"url": f"/cache/image/generations/{image_id}.png"})
image_filename = save_b64_image(image)
images.append({"url": f"/cache/image/generations/{image_filename}"})
file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_id}.json")
with open(file_body_path, "w") as f:

View File

@ -28,6 +28,7 @@ from langchain_community.document_loaders import (
UnstructuredXMLLoader,
UnstructuredRSTLoader,
UnstructuredExcelLoader,
YoutubeLoader,
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
@ -181,7 +182,7 @@ class CollectionNameForm(BaseModel):
collection_name: Optional[str] = "test"
class StoreWebForm(CollectionNameForm):
class UrlForm(CollectionNameForm):
url: str
@ -456,8 +457,32 @@ def query_collection_handler(
)
@app.post("/youtube")
def store_youtube_video(form_data: UrlForm, user=Depends(get_current_user)):
try:
loader = YoutubeLoader.from_youtube_url(form_data.url, add_video_info=False)
data = loader.load()
collection_name = form_data.collection_name
if collection_name == "":
collection_name = calculate_sha256_string(form_data.url)[:63]
store_data_in_vector_db(data, collection_name, overwrite=True)
return {
"status": True,
"collection_name": collection_name,
"filename": form_data.url,
}
except Exception as e:
log.exception(e)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.DEFAULT(e),
)
@app.post("/web")
def store_web(form_data: StoreWebForm, user=Depends(get_current_user)):
def store_web(form_data: UrlForm, user=Depends(get_current_user)):
# "https://www.gutenberg.org/files/1727/1727-h/1727-h.htm"
try:
loader = get_web_loader(form_data.url)

View File

@ -53,7 +53,7 @@ def query_doc_with_hybrid_search(
embedding_function,
k: int,
reranking_function,
r: int,
r: float,
):
try:
collection = CHROMA_CLIENT.get_collection(name=collection_name)
@ -321,8 +321,12 @@ def rag_messages(
context_string = ""
for context in relevant_contexts:
items = context["documents"][0]
context_string += "\n\n".join(items)
try:
if "documents" in context:
items = [item for item in context["documents"][0] if item is not None]
context_string += "\n\n".join(items)
except Exception as e:
log.exception(e)
context_string = context_string.strip()
ra_content = rag_template(

View File

@ -89,6 +89,10 @@ class SignupForm(BaseModel):
profile_image_url: Optional[str] = "/user.png"
class AddUserForm(SignupForm):
role: Optional[str] = "pending"
class AuthsTable:
def __init__(self, db):
self.db = db

View File

@ -123,6 +123,13 @@ class UsersTable:
def get_num_users(self) -> Optional[int]:
return User.select().count()
def get_first_user(self) -> UserModel:
try:
user = User.select().order_by(User.created_at).first()
return UserModel(**model_to_dict(user))
except:
return None
def update_user_role_by_id(self, id: str, role: str) -> Optional[UserModel]:
try:
query = User.update(role=role).where(User.id == id)

View File

@ -1,16 +1,19 @@
import logging
from fastapi import Request
from fastapi import Request, UploadFile, File
from fastapi import Depends, HTTPException, status
from fastapi import APIRouter
from pydantic import BaseModel
import re
import uuid
import csv
from apps.web.models.auths import (
SigninForm,
SignupForm,
AddUserForm,
UpdateProfileForm,
UpdatePasswordForm,
UserResponse,
@ -205,6 +208,51 @@ async def signup(request: Request, form_data: SignupForm):
raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
############################
# AddUser
############################
@router.post("/add", response_model=SigninResponse)
async def add_user(form_data: AddUserForm, user=Depends(get_admin_user)):
if not validate_email_format(form_data.email.lower()):
raise HTTPException(
status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT
)
if Users.get_user_by_email(form_data.email.lower()):
raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
try:
print(form_data)
hashed = get_password_hash(form_data.password)
user = Auths.insert_new_auth(
form_data.email.lower(),
hashed,
form_data.name,
form_data.profile_image_url,
form_data.role,
)
if user:
token = create_token(data={"id": user.id})
return {
"token": token,
"token_type": "Bearer",
"id": user.id,
"email": user.email,
"name": user.name,
"role": user.role,
"profile_image_url": user.profile_image_url,
}
else:
raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
except Exception as err:
raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
############################
# ToggleSignUp
############################

View File

@ -58,7 +58,7 @@ async def update_user_permissions(
@router.post("/update/role", response_model=Optional[UserModel])
async def update_user_role(form_data: UserRoleUpdateForm, user=Depends(get_admin_user)):
if user.id != form_data.id:
if user.id != form_data.id and form_data.id != Users.get_first_user().id:
return Users.update_user_role_by_id(form_data.id, form_data.role)
raise HTTPException(

View File

@ -367,6 +367,17 @@ DEFAULT_PROMPT_SUGGESTIONS = (
"title": ["Show me a code snippet", "of a website's sticky header"],
"content": "Show me a code snippet of a website's sticky header in CSS and JavaScript.",
},
{
"title": [
"Explain options trading",
"if I'm familiar with buying and selling stocks",
],
"content": "Explain options trading in simple terms if I'm familiar with buying and selling stocks.",
},
{
"title": ["Overcome procrastination", "give me tips"],
"content": "Could you start by asking me about instances when I procrastinate the most and then give me some suggestions to overcome it?",
},
]
)

View File

@ -18,6 +18,18 @@
{
"title": ["Show me a code snippet", "of a website's sticky header"],
"content": "Show me a code snippet of a website's sticky header in CSS and JavaScript."
},
{
"title": ["Explain options trading", "if I'm familiar with buying and selling stocks"],
"content": "Explain options trading in simple terms if I'm familiar with buying and selling stocks."
},
{
"title": ["Overcome procrastination", "give me tips"],
"content": "Could you start by asking me about instances when I procrastinate the most and then give me some suggestions to overcome it?"
},
{
"title": ["Grammar check", "rewrite it for better readability "],
"content": "Check the following sentence for grammar and clarity: \"[sentence]\". Rewrite it for better readability while maintaining its original meaning."
}
]
}

View File

@ -311,7 +311,7 @@ async def get_manifest_json():
"background_color": "#343541",
"theme_color": "#343541",
"orientation": "portrait-primary",
"icons": [{"src": "/favicon.png", "type": "image/png", "sizes": "844x884"}],
"icons": [{"src": "/static/logo.png", "type": "image/png", "sizes": "500x500"}],
}

View File

@ -1,60 +1,62 @@
fastapi
uvicorn[standard]
pydantic
python-multipart
fastapi==0.109.2
uvicorn[standard]==0.22.0
pydantic==2.7.1
python-multipart==0.0.9
flask
flask_cors
Flask==3.0.3
Flask-Cors==4.0.0
python-socketio
python-jose
passlib[bcrypt]
uuid
python-socketio==5.11.2
python-jose==3.3.0
passlib[bcrypt]==1.7.4
uuid==1.30
requests
aiohttp
peewee
peewee-migrate
psycopg2-binary
pymysql
bcrypt
requests==2.31.0
aiohttp==3.9.5
peewee==3.17.3
peewee-migrate==1.12.2
psycopg2-binary==2.9.9
PyMySQL==1.1.0
bcrypt==4.1.2
litellm==1.35.28
litellm[proxy]==1.35.28
boto3
boto3==1.34.95
argon2-cffi
apscheduler
google-generativeai
argon2-cffi==23.1.0
APScheduler==3.10.4
google-generativeai==0.5.2
langchain
langchain-chroma
langchain-community
fake_useragent
chromadb
sentence_transformers
pypdf
docx2txt
unstructured
markdown
pypandoc
pandas
openpyxl
pyxlsb
xlrd
validators
langchain==0.1.16
langchain-community==0.0.34
langchain-chroma==0.1.0
opencv-python-headless
rapidocr-onnxruntime
fake-useragent==1.5.1
chromadb==0.4.24
sentence-transformers==2.7.0
pypdf==4.2.0
docx2txt==0.8
unstructured==0.11.8
Markdown==3.6
pypandoc==1.13
pandas==2.2.2
openpyxl==3.1.2
pyxlsb==1.0.10
xlrd==2.0.1
validators==0.28.1
fpdf2
rank_bm25
opencv-python-headless==4.9.0.80
rapidocr-onnxruntime==1.2.3
faster-whisper
fpdf2==2.7.8
rank-bm25==0.2.2
PyJWT
pyjwt[crypto]
faster-whisper==1.0.1
black
langfuse
PyJWT==2.8.0
PyJWT[crypto]==2.8.0
black==24.4.2
langfuse==2.27.3
youtube-transcript-api

BIN
backend/static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1 @@
Name,Email,Password,Role
1 Name Email Password Role

BIN
backend/utils/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

13
package-lock.json generated
View File

@ -1,17 +1,18 @@
{
"name": "open-webui",
"version": "0.1.122",
"version": "0.1.123",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-webui",
"version": "0.1.122",
"version": "0.1.123",
"dependencies": {
"@sveltejs/adapter-node": "^1.3.1",
"async": "^3.2.5",
"bits-ui": "^0.19.7",
"dayjs": "^1.11.10",
"eventsource-parser": "^1.1.2",
"file-saver": "^2.0.5",
"highlight.js": "^11.9.0",
"i18next": "^23.10.0",
@ -3167,6 +3168,14 @@
"integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==",
"dev": true
},
"node_modules/eventsource-parser": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz",
"integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==",
"engines": {
"node": ">=14.18"
}
},
"node_modules/execa": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "open-webui",
"version": "0.1.122",
"version": "0.1.123",
"private": true,
"scripts": {
"dev": "vite dev --host",
@ -49,6 +49,7 @@
"async": "^3.2.5",
"bits-ui": "^0.19.7",
"dayjs": "^1.11.10",
"eventsource-parser": "^1.1.2",
"file-saver": "^2.0.5",
"highlight.js": "^11.9.0",
"i18next": "^23.10.0",

View File

@ -95,6 +95,45 @@ export const userSignUp = async (
return res;
};
export const addUser = async (
token: string,
name: string,
email: string,
password: string,
role: string = 'pending'
) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/auths/add`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token && { authorization: `Bearer ${token}` })
},
body: JSON.stringify({
name: name,
email: email,
password: password,
role: role
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
export const updateUserProfile = async (token: string, name: string, profileImageUrl: string) => {
let error = null;

View File

@ -221,6 +221,37 @@ export const uploadWebToVectorDB = async (token: string, collection_name: string
return res;
};
export const uploadYoutubeTranscriptionToVectorDB = async (token: string, url: string) => {
let error = null;
const res = await fetch(`${RAG_API_BASE_URL}/youtube`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
},
body: JSON.stringify({
url: url
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
error = err.detail;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const queryDoc = async (
token: string,
collection_name: string,

View File

@ -1,15 +1,22 @@
import { EventSourceParserStream } from 'eventsource-parser/stream';
import type { ParsedEvent } from 'eventsource-parser';
type TextStreamUpdate = {
done: boolean;
value: string;
};
// createOpenAITextStream takes a ReadableStreamDefaultReader from an SSE response,
// createOpenAITextStream takes a responseBody with a SSE response,
// and returns an async generator that emits delta updates with large deltas chunked into random sized chunks
export async function createOpenAITextStream(
messageStream: ReadableStreamDefaultReader,
responseBody: ReadableStream<Uint8Array>,
splitLargeDeltas: boolean
): Promise<AsyncGenerator<TextStreamUpdate>> {
let iterator = openAIStreamToIterator(messageStream);
const eventStream = responseBody
.pipeThrough(new TextDecoderStream())
.pipeThrough(new EventSourceParserStream())
.getReader();
let iterator = openAIStreamToIterator(eventStream);
if (splitLargeDeltas) {
iterator = streamLargeDeltasAsRandomChunks(iterator);
}
@ -17,7 +24,7 @@ export async function createOpenAITextStream(
}
async function* openAIStreamToIterator(
reader: ReadableStreamDefaultReader
reader: ReadableStreamDefaultReader<ParsedEvent>
): AsyncGenerator<TextStreamUpdate> {
while (true) {
const { value, done } = await reader.read();
@ -25,31 +32,22 @@ async function* openAIStreamToIterator(
yield { done: true, value: '' };
break;
}
const lines = value.split('\n');
for (let line of lines) {
if (line.endsWith('\r')) {
// Remove trailing \r
line = line.slice(0, -1);
}
if (line !== '') {
console.log(line);
if (line === 'data: [DONE]') {
yield { done: true, value: '' };
} else if (line.startsWith(':')) {
// Events starting with : are comments https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format
// OpenRouter sends heartbeats like ": OPENROUTER PROCESSING"
continue;
} else {
try {
const data = JSON.parse(line.replace(/^data: /, ''));
console.log(data);
if (!value) {
continue;
}
const data = value.data;
if (data.startsWith('[DONE]')) {
yield { done: true, value: '' };
break;
}
yield { done: false, value: data.choices?.[0]?.delta?.content ?? '' };
} catch (e) {
console.error('Error extracting delta from SSE event:', e);
}
}
}
try {
const parsedData = JSON.parse(data);
console.log(parsedData);
yield { done: false, value: parsedData.choices?.[0]?.delta?.content ?? '' };
} catch (e) {
console.error('Error extracting delta from SSE event:', e);
}
}
}

View File

@ -22,7 +22,7 @@
</script>
<Modal bind:show>
<div class="px-5 py-4 dark:text-gray-300">
<div class="px-5 py-4 dark:text-gray-300 text-gray-700">
<div class="flex justify-between items-start">
<div class="text-xl font-bold">
{$i18n.t('Whats New in')}
@ -59,7 +59,7 @@
<hr class=" dark:border-gray-800" />
<div class=" w-full p-4 px-5">
<div class=" w-full p-4 px-5 text-gray-700 dark:text-gray-100">
<div class=" overflow-y-scroll max-h-80">
<div class="mb-3">
{#if changelog}

View File

@ -0,0 +1,334 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import { createEventDispatcher } from 'svelte';
import { onMount, getContext } from 'svelte';
import { addUser } from '$lib/apis/auths';
import Modal from '../common/Modal.svelte';
import { WEBUI_BASE_URL } from '$lib/constants';
const i18n = getContext('i18n');
const dispatch = createEventDispatcher();
export let show = false;
let loading = false;
let tab = '';
let inputFiles;
let _user = {
name: '',
email: '',
password: '',
role: 'user'
};
$: if (show) {
_user = {
name: '',
email: '',
password: '',
role: 'user'
};
}
const submitHandler = async () => {
const stopLoading = () => {
dispatch('save');
loading = false;
};
if (tab === '') {
loading = true;
const res = await addUser(
localStorage.token,
_user.name,
_user.email,
_user.password,
_user.role
).catch((error) => {
toast.error(error);
});
if (res) {
stopLoading();
show = false;
}
} else {
if (inputFiles) {
loading = true;
const file = inputFiles[0];
const reader = new FileReader();
reader.onload = async (e) => {
const csv = e.target.result;
const rows = csv.split('\n');
let userCount = 0;
for (const [idx, row] of rows.entries()) {
const columns = row.split(',').map((col) => col.trim());
console.log(idx, columns);
if (idx > 0) {
if (columns.length === 4 && ['admin', 'user', 'pending'].includes(columns[3])) {
const res = await addUser(
localStorage.token,
columns[0],
columns[1],
columns[2],
columns[3]
).catch((error) => {
toast.error(`Row ${idx + 1}: ${error}`);
return null;
});
if (res) {
userCount = userCount + 1;
}
} else {
toast.error(`Row ${idx + 1}: invalid format.`);
}
}
}
toast.success(`Successfully imported ${userCount} users.`);
inputFiles = null;
const uploadInputElement = document.getElementById('upload-user-csv-input');
if (uploadInputElement) {
uploadInputElement.value = null;
}
stopLoading();
};
reader.readAsText(file);
} else {
toast.error(`File not found.`);
}
}
};
</script>
<Modal size="sm" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center">{$i18n.t('Add User')}</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<form
class="flex flex-col w-full"
on:submit|preventDefault={() => {
submitHandler();
}}
>
<div class="flex text-center text-sm font-medium rounded-xl bg-transparent/10 p-1 mb-2">
<button
class="w-full rounded-lg p-1.5 {tab === '' ? 'bg-gray-50 dark:bg-gray-850' : ''}"
type="button"
on:click={() => {
tab = '';
}}>Form</button
>
<button
class="w-full rounded-lg p-1 {tab === 'import' ? 'bg-gray-50 dark:bg-gray-850' : ''}"
type="button"
on:click={() => {
tab = 'import';
}}>CSV Import</button
>
</div>
<div class="px-1">
{#if tab === ''}
<div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Role')}</div>
<div class="flex-1">
<select
class="w-full capitalize rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
bind:value={_user.role}
placeholder={$i18n.t('Enter Your Role')}
required
>
<option value="pending"> pending </option>
<option value="user"> user </option>
<option value="admin"> admin </option>
</select>
</div>
</div>
<div class="flex flex-col w-full mt-2">
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name')}</div>
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
type="text"
bind:value={_user.name}
placeholder={$i18n.t('Enter Your Full Name')}
autocomplete="off"
required
/>
</div>
</div>
<hr class=" dark:border-gray-800 my-3 w-full" />
<div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Email')}</div>
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
type="email"
bind:value={_user.email}
placeholder={$i18n.t('Enter Your Email')}
autocomplete="off"
required
/>
</div>
</div>
<div class="flex flex-col w-full mt-2">
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Password')}</div>
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
type="password"
bind:value={_user.password}
placeholder={$i18n.t('Enter Your Password')}
autocomplete="off"
/>
</div>
</div>
{:else if tab === 'import'}
<div>
<div class="mb-3 w-full">
<input
id="upload-user-csv-input"
hidden
bind:files={inputFiles}
type="file"
accept=".csv"
/>
<button
class="w-full text-sm font-medium py-3 bg-transparent hover:bg-gray-100 border border-dashed dark:border-gray-800 dark:hover:bg-gray-850 text-center rounded-xl"
type="button"
on:click={() => {
document.getElementById('upload-user-csv-input')?.click();
}}
>
{#if inputFiles}
{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected.
{:else}
{$i18n.t('Click here to select a csv file.')}
{/if}
</button>
</div>
<div class=" text-xs text-gray-500">
{$i18n.t(
'Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.'
)}
<a
class="underline dark:text-gray-200"
href="{WEBUI_BASE_URL}/static/user-import.csv"
>
Click here to download user import template file.
</a>
</div>
</div>
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-medium">
<button
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg flex flex-row space-x-1 items-center {loading
? ' cursor-not-allowed'
: ''}"
type="submit"
disabled={loading}
>
{$i18n.t('Submit')}
{#if loading}
<div class="ml-2 self-center">
<svg
class=" w-4 h-4"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/><path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/></svg
>
</div>
{/if}
</button>
</div>
</form>
</div>
</div>
</div>
</Modal>
<style>
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
/* display: none; <- Crashes Chrome on hover */
-webkit-appearance: none;
margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
}
.tabs::-webkit-scrollbar {
display: none; /* for Chrome, Safari and Opera */
}
.tabs {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
input[type='number'] {
-moz-appearance: textfield; /* Firefox */
}
</style>

View File

@ -15,7 +15,7 @@
<Modal bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center">{$i18n.t('Admin Settings')}</div>
<button
class="self-center"
@ -35,7 +35,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
<div

File diff suppressed because it is too large Load Diff

View File

@ -87,6 +87,17 @@
chatInputElement?.focus();
await tick();
};
const confirmSelectYoutube = async (url) => {
dispatch('youtube', url);
prompt = removeFirstHashWord(prompt);
const chatInputElement = document.getElementById('chat-textarea');
await tick();
chatInputElement?.focus();
await tick();
};
</script>
{#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
@ -132,7 +143,30 @@
</button>
{/each}
{#if prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
{#if prompt.split(' ')?.at(0)?.substring(1).startsWith('https://www.youtube.com')}
<button
class="px-3 py-1.5 rounded-xl w-full text-left bg-gray-100 selected-command-option-button"
type="button"
on:click={() => {
const url = prompt.split(' ')?.at(0)?.substring(1);
if (isValidHttpUrl(url)) {
confirmSelectYoutube(url);
} else {
toast.error(
$i18n.t(
'Oops! Looks like the URL is invalid. Please double-check and try again.'
)
);
}
}}
>
<div class=" font-medium text-black line-clamp-1">
{prompt.split(' ')?.at(0)?.substring(1)}
</div>
<div class=" text-xs text-gray-600 line-clamp-1">{$i18n.t('Youtube')}</div>
</button>
{:else if prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
<button
class="px-3 py-1.5 rounded-xl w-full text-left bg-gray-100 selected-command-option-button"
type="button"

View File

@ -1,4 +1,6 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { generatePrompt } from '$lib/apis/ollama';
import { models } from '$lib/stores';
import { splitStream } from '$lib/utils';
@ -7,6 +9,8 @@
const i18n = getContext('i18n');
const dispatch = createEventDispatcher();
export let prompt = '';
export let user = null;
@ -17,12 +21,7 @@
let filteredModels = [];
$: filteredModels = $models
.filter(
(p) =>
p.name !== 'hr' &&
!p.external &&
p.name.includes(prompt.split(' ')?.at(0)?.substring(1) ?? '')
)
.filter((p) => p.name.includes(prompt.split(' ')?.at(0)?.substring(1) ?? ''))
.sort((a, b) => a.name.localeCompare(b.name));
$: if (prompt) {
@ -38,6 +37,11 @@
};
const confirmSelect = async (model) => {
prompt = '';
dispatch('select', model);
};
const confirmSelectCollaborativeChat = async (model) => {
// dispatch('select', model);
prompt = '';
user = JSON.parse(JSON.stringify(model.name));
@ -127,40 +131,42 @@
};
</script>
{#if filteredModels.length > 0}
<div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
<div class="flex w-full px-2">
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-xl text-center">
<div class=" text-lg font-semibold mt-2">@</div>
</div>
{#if prompt.charAt(0) === '@'}
{#if filteredModels.length > 0}
<div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
<div class="flex w-full px-2">
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-xl text-center">
<div class=" text-lg font-semibold mt-2">@</div>
</div>
<div class="max-h-60 flex flex-col w-full rounded-r-xl bg-white">
<div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5">
{#each filteredModels as model, modelIdx}
<button
class=" px-3 py-1.5 rounded-xl w-full text-left {modelIdx === selectedIdx
? ' bg-gray-100 selected-command-option-button'
: ''}"
type="button"
on:click={() => {
confirmSelect(model);
}}
on:mousemove={() => {
selectedIdx = modelIdx;
}}
on:focus={() => {}}
>
<div class=" font-medium text-black line-clamp-1">
{model.name}
</div>
<div class="max-h-60 flex flex-col w-full rounded-r-xl bg-white">
<div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5">
{#each filteredModels as model, modelIdx}
<button
class=" px-3 py-1.5 rounded-xl w-full text-left {modelIdx === selectedIdx
? ' bg-gray-100 selected-command-option-button'
: ''}"
type="button"
on:click={() => {
confirmSelect(model);
}}
on:mousemove={() => {
selectedIdx = modelIdx;
}}
on:focus={() => {}}
>
<div class=" font-medium text-black line-clamp-1">
{model.name}
</div>
<!-- <div class=" text-xs text-gray-600 line-clamp-1">
<!-- <div class=" text-xs text-gray-600 line-clamp-1">
{doc.title}
</div> -->
</button>
{/each}
</button>
{/each}
</div>
</div>
</div>
</div>
</div>
{/if}
{/if}

View File

@ -1,56 +1,116 @@
<script lang="ts">
import Bolt from '$lib/components/icons/Bolt.svelte';
import { onMount } from 'svelte';
export let submitPrompt: Function;
export let suggestionPrompts = [];
let prompts = [];
$: prompts =
suggestionPrompts.length <= 4
? suggestionPrompts
: suggestionPrompts.sort(() => Math.random() - 0.5).slice(0, 4);
$: prompts = suggestionPrompts
.reduce((acc, current) => [...acc, ...[current]], [])
.sort(() => Math.random() - 0.5);
// suggestionPrompts.length <= 4
// ? suggestionPrompts
// : suggestionPrompts.sort(() => Math.random() - 0.5).slice(0, 4);
onMount(() => {
const containerElement = document.getElementById('suggestions-container');
if (containerElement) {
containerElement.addEventListener('wheel', function (event) {
if (event.deltaY !== 0) {
// If scrolling vertically, prevent default behavior
event.preventDefault();
// Adjust horizontal scroll position based on vertical scroll
containerElement.scrollLeft += event.deltaY;
}
});
}
});
</script>
<div class=" mb-3 md:p-1 text-left w-full">
<div class=" flex flex-wrap-reverse px-2 text-left">
{#if prompts.length > 0}
<div class="mb-2 flex gap-1 text-sm font-medium items-center text-gray-400 dark:text-gray-600">
<Bolt />
Suggested
</div>
{/if}
<div class="w-full">
<div
class="relative w-full flex gap-2 snap-x snap-mandatory md:snap-none overflow-x-auto tabs"
id="suggestions-container"
>
{#each prompts as prompt, promptIdx}
<div
class="{promptIdx > 1 ? 'hidden sm:inline-flex' : ''} basis-full sm:basis-1/2 p-[5px] px-1"
>
<div class="snap-center shrink-0">
<button
class=" flex-1 flex justify-between w-full h-full px-4 py-2.5 bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 rounded-2xl transition group"
class="flex flex-col flex-1 shrink-0 w-64 justify-between h-36 p-5 px-6 bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 rounded-3xl transition group"
on:click={() => {
submitPrompt(prompt.content);
}}
>
<div class="flex flex-col text-left self-center">
<div class="flex flex-col text-left">
{#if prompt.title && prompt.title[0] !== ''}
<div class="text-sm font-medium dark:text-gray-300">{prompt.title[0]}</div>
<div class="text-sm text-gray-500 line-clamp-1">{prompt.title[1]}</div>
<div
class=" font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition"
>
{prompt.title[0]}
</div>
<div class="text-sm text-gray-600 font-normal line-clamp-2">{prompt.title[1]}</div>
{:else}
<div class=" self-center text-sm font-medium dark:text-gray-300 line-clamp-2">
<div
class=" self-center text-sm font-medium dark:text-gray-300 dark:group-hover:text-gray-100 transition line-clamp-2"
>
{prompt.content}
</div>
{/if}
</div>
<div
class="self-center p-1 rounded-lg text-gray-50 group-hover:text-gray-800 dark:text-gray-850 dark:group-hover:text-gray-100 transition"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
<div class="w-full flex justify-between">
<div
class="text-xs text-gray-400 group-hover:text-gray-500 dark:text-gray-600 dark:group-hover:text-gray-500 transition self-center"
>
<path
fill-rule="evenodd"
d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
clip-rule="evenodd"
/>
</svg>
Prompt
</div>
<div
class="self-end p-1 rounded-lg text-gray-300 group-hover:text-gray-800 dark:text-gray-700 dark:group-hover:text-gray-100 transition"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="size-4"
>
<path
fill-rule="evenodd"
d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
clip-rule="evenodd"
/>
</svg>
</div>
</div>
</button>
</div>
{/each}
<!-- <div class="snap-center shrink-0">
<img
class="shrink-0 w-80 h-40 rounded-lg shadow-xl bg-white"
src="https://images.unsplash.com/photo-1604999565976-8913ad2ddb7c?ixlib=rb-1.2.1&amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;auto=format&amp;fit=crop&amp;w=320&amp;h=160&amp;q=80"
/>
</div> -->
</div>
</div>
<style>
.tabs::-webkit-scrollbar {
display: none; /* for Chrome, Safari and Opera */
}
.tabs {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
</style>

View File

@ -12,7 +12,7 @@
import Placeholder from './Messages/Placeholder.svelte';
import Spinner from '../common/Spinner.svelte';
import { imageGenerations } from '$lib/apis/images';
import { copyToClipboard } from '$lib/utils';
import { copyToClipboard, findWordIndices } from '$lib/utils';
const i18n = getContext('i18n');
@ -22,6 +22,8 @@
export let continueGeneration: Function;
export let regenerateResponse: Function;
export let prompt;
export let suggestionPrompts;
export let processing = '';
export let bottomPadding = false;
export let autoScroll;
@ -236,108 +238,111 @@
history: history
});
};
// const messageDeleteHandler = async (messageId) => {
// const message = history.messages[messageId];
// const parentId = message.parentId;
// const childrenIds = message.childrenIds ?? [];
// const grandchildrenIds = [];
// // Iterate through childrenIds to find grandchildrenIds
// for (const childId of childrenIds) {
// const childMessage = history.messages[childId];
// const grandChildrenIds = childMessage.childrenIds ?? [];
// for (const grandchildId of grandchildrenIds) {
// const childMessage = history.messages[grandchildId];
// childMessage.parentId = parentId;
// }
// grandchildrenIds.push(...grandChildrenIds);
// }
// history.messages[parentId].childrenIds.push(...grandchildrenIds);
// history.messages[parentId].childrenIds = history.messages[parentId].childrenIds.filter(
// (id) => id !== messageId
// );
// // Select latest message
// let currentMessageId = grandchildrenIds.at(-1);
// if (currentMessageId) {
// let messageChildrenIds = history.messages[currentMessageId].childrenIds;
// while (messageChildrenIds.length !== 0) {
// currentMessageId = messageChildrenIds.at(-1);
// messageChildrenIds = history.messages[currentMessageId].childrenIds;
// }
// history.currentId = currentMessageId;
// }
// await updateChatById(localStorage.token, chatId, { messages, history });
// };
</script>
{#if messages.length == 0}
<Placeholder models={selectedModels} modelfiles={selectedModelfiles} />
{:else}
<div class=" pb-10">
{#key chatId}
{#each messages as message, messageIdx}
<div class=" w-full">
<div
class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
? 'max-w-full'
: 'max-w-3xl'} mx-auto rounded-lg group"
>
{#if message.role === 'user'}
<UserMessage
on:delete={() => messageDeleteHandler(message.id)}
user={$user}
{readOnly}
{message}
isFirstMessage={messageIdx === 0}
siblings={message.parentId !== null
? history.messages[message.parentId]?.childrenIds ?? []
: Object.values(history.messages)
.filter((message) => message.parentId === null)
.map((message) => message.id) ?? []}
{confirmEditMessage}
{showPreviousMessage}
{showNextMessage}
copyToClipboard={copyToClipboardWithToast}
/>
{:else}
<ResponseMessage
{message}
modelfiles={selectedModelfiles}
siblings={history.messages[message.parentId]?.childrenIds ?? []}
isLastMessage={messageIdx + 1 === messages.length}
{readOnly}
{updateChatMessages}
{confirmEditResponseMessage}
{showPreviousMessage}
{showNextMessage}
{rateMessage}
copyToClipboard={copyToClipboardWithToast}
{continueGeneration}
{regenerateResponse}
on:save={async (e) => {
console.log('save', e);
<div class="h-full flex mb-16">
{#if messages.length == 0}
<Placeholder
models={selectedModels}
modelfiles={selectedModelfiles}
{suggestionPrompts}
submitPrompt={async (p) => {
let text = p;
const message = e.detail;
history.messages[message.id] = message;
await updateChatById(localStorage.token, chatId, {
messages: messages,
history: history
});
}}
/>
{/if}
if (p.includes('{{CLIPBOARD}}')) {
const clipboardText = await navigator.clipboard.readText().catch((err) => {
toast.error($i18n.t('Failed to read clipboard contents'));
return '{{CLIPBOARD}}';
});
text = p.replaceAll('{{CLIPBOARD}}', clipboardText);
}
prompt = text;
await tick();
const chatInputElement = document.getElementById('chat-textarea');
if (chatInputElement) {
prompt = p;
chatInputElement.style.height = '';
chatInputElement.style.height = Math.min(chatInputElement.scrollHeight, 200) + 'px';
chatInputElement.focus();
const words = findWordIndices(prompt);
if (words.length > 0) {
const word = words.at(0);
chatInputElement.setSelectionRange(word?.startIndex, word.endIndex + 1);
}
}
await tick();
}}
/>
{:else}
<div class="w-full pt-2">
{#key chatId}
{#each messages as message, messageIdx}
<div class=" w-full {messageIdx === messages.length - 1 ? 'pb-28' : ''}">
<div
class="flex flex-col justify-between px-5 mb-3 {$settings?.fullScreenMode ?? null
? 'max-w-full'
: 'max-w-5xl'} mx-auto rounded-lg group"
>
{#if message.role === 'user'}
<UserMessage
on:delete={() => messageDeleteHandler(message.id)}
user={$user}
{readOnly}
{message}
isFirstMessage={messageIdx === 0}
siblings={message.parentId !== null
? history.messages[message.parentId]?.childrenIds ?? []
: Object.values(history.messages)
.filter((message) => message.parentId === null)
.map((message) => message.id) ?? []}
{confirmEditMessage}
{showPreviousMessage}
{showNextMessage}
copyToClipboard={copyToClipboardWithToast}
/>
{:else}
<ResponseMessage
{message}
modelfiles={selectedModelfiles}
siblings={history.messages[message.parentId]?.childrenIds ?? []}
isLastMessage={messageIdx + 1 === messages.length}
{readOnly}
{updateChatMessages}
{confirmEditResponseMessage}
{showPreviousMessage}
{showNextMessage}
{rateMessage}
copyToClipboard={copyToClipboardWithToast}
{continueGeneration}
{regenerateResponse}
on:save={async (e) => {
console.log('save', e);
const message = e.detail;
history.messages[message.id] = message;
await updateChatById(localStorage.token, chatId, {
messages: messages,
history: history
});
}}
/>
{/if}
</div>
</div>
</div>
{/each}
{/each}
{#if bottomPadding}
<div class=" mb-10" />
{/if}
{/key}
</div>
{/if}
{#if bottomPadding}
<div class=" pb-20" />
{/if}
{/key}
</div>
{/if}
</div>

View File

@ -31,7 +31,9 @@
>
</div>
<pre class=" rounded-b-lg hljs p-4 px-5 overflow-x-auto rounded-t-none"><code
<pre
class=" hljs p-4 px-5 overflow-x-auto"
style="border-top-left-radius: 0px; border-top-right-radius: 0px;"><code
class="language-{lang} rounded-t-none whitespace-pre">{@html highlightedCode || code}</code
></pre>
</div>

View File

@ -3,11 +3,19 @@
import { user } from '$lib/stores';
import { onMount, getContext } from 'svelte';
import { blur, fade } from 'svelte/transition';
import Suggestions from '../MessageInput/Suggestions.svelte';
const i18n = getContext('i18n');
export let models = [];
export let modelfiles = [];
export let submitPrompt;
export let suggestionPrompts;
let mounted = false;
let modelfile = null;
let selectedModelIdx = 0;
@ -17,12 +25,16 @@
$: if (models.length > 0) {
selectedModelIdx = models.length - 1;
}
onMount(() => {
mounted = true;
});
</script>
{#if models.length > 0}
<div class="m-auto text-center max-w-md px-2">
<div class="flex justify-center mt-8">
<div class="flex -space-x-4 mb-1">
{#key mounted}
<div class="m-auto w-full max-w-6xl px-8 lg:px-24 pb-16">
<div class="flex justify-start">
<div class="flex -space-x-4 mb-1" in:fade={{ duration: 200 }}>
{#each models as model, modelIdx}
<button
on:click={() => {
@ -33,7 +45,7 @@
<img
src={modelfiles[model]?.imageUrl ?? `${WEBUI_BASE_URL}/static/favicon.png`}
alt="modelfile"
class=" size-12 rounded-full border-[1px] border-gray-200 dark:border-none"
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
draggable="false"
/>
{:else}
@ -41,7 +53,7 @@
src={$i18n.language === 'dg-DG'
? `/doge.png`
: `${WEBUI_BASE_URL}/static/favicon.png`}
class=" size-12 rounded-full border-[1px] border-gray-200 dark:border-none"
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
alt="logo"
draggable="false"
/>
@ -50,26 +62,42 @@
{/each}
</div>
</div>
<div class=" mt-2 mb-5 text-2xl text-gray-800 dark:text-gray-100 font-semibold">
{#if modelfile}
<span class=" capitalize">
{modelfile.title}
</span>
<div class="mt-0.5 text-base font-normal text-gray-600 dark:text-gray-400">
{modelfile.desc}
</div>
{#if modelfile.user}
<div class="mt-0.5 text-sm font-normal text-gray-500 dark:text-gray-500">
By <a href="https://openwebui.com/m/{modelfile.user.username}"
>{modelfile.user.name ? modelfile.user.name : `@${modelfile.user.username}`}</a
>
</div>
{/if}
{:else}
<div class=" line-clamp-1">{$i18n.t('Hello, {{name}}', { name: $user.name })}</div>
<div>{$i18n.t('How can I help you today?')}</div>
{/if}
<div
class=" mt-2 mb-4 text-3xl text-gray-800 dark:text-gray-100 font-semibold text-left flex items-center gap-4"
>
<div>
<div class=" capitalize line-clamp-1" in:fade={{ duration: 200 }}>
{#if modelfile}
{modelfile.title}
{:else}
{$i18n.t('Hello, {{name}}', { name: $user.name })}
{/if}
</div>
<div in:fade={{ duration: 200, delay: 200 }}>
{#if modelfile}
<div class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400">
{modelfile.desc}
</div>
{#if modelfile.user}
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
By <a href="https://openwebui.com/m/{modelfile.user.username}"
>{modelfile.user.name ? modelfile.user.name : `@${modelfile.user.username}`}</a
>
</div>
{/if}
{:else}
<div class=" font-medium text-gray-400 dark:text-gray-500">
{$i18n.t('How can I help you today?')}
</div>
{/if}
</div>
</div>
</div>
<div class=" w-full" in:fade={{ duration: 200, delay: 300 }}>
<Suggestions {suggestionPrompts} {submitPrompt} />
</div>
</div>
{/if}
{/key}

View File

@ -137,20 +137,22 @@
.getElementById(`message-${message.id}`)
?.getElementsByClassName('chat-assistant');
for (const element of chatMessageElements) {
auto_render(element, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{ left: '$$', right: '$$', display: false },
{ left: '$ ', right: ' $', display: false },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: false },
{ left: '[ ', right: ' ]', display: false }
],
// • rendering keys, e.g.:
throwOnError: false
});
if (chatMessageElements) {
for (const element of chatMessageElements) {
auto_render(element, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{ left: '$$', right: '$$', display: false },
{ left: '$ ', right: ' $', display: false },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: false },
{ left: '[ ', right: ' ]', display: false }
],
// • rendering keys, e.g.:
throwOnError: false
});
}
}
};
@ -325,9 +327,8 @@
{#key message.id}
<div class=" flex w-full message-{message.id}" id="message-{message.id}">
<ProfileImage
src={modelfiles[message.model]?.imageUrl ?? $i18n.language === 'dg-DG'
? `/doge.png`
: `${WEBUI_BASE_URL}/static/favicon.png`}
src={modelfiles[message.model]?.imageUrl ??
($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
/>
<div class="w-full overflow-hidden">

View File

@ -13,6 +13,8 @@
export let selectedModels = [''];
export let disabled = false;
export let showSetDefault = true;
const saveDefaultModel = async () => {
const hasEmptyModel = selectedModels.filter((it) => it === '');
if (hasEmptyModel.length) {
@ -38,9 +40,9 @@
<div class="flex flex-col mt-0.5 w-full">
{#each selectedModels as selectedModel, selectedModelIdx}
<div class="flex w-full">
<div class="flex w-full max-w-fit">
<div class="overflow-hidden w-full">
<div class="mr-0.5 max-w-full">
<div class="mr-1 max-w-full">
<Selector
placeholder={$i18n.t('Select a model')}
items={$models
@ -69,9 +71,9 @@
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
class="size-3.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
</svg>
@ -92,9 +94,9 @@
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke-width="2"
stroke="currentColor"
class="w-4 h-4"
class="size-3.5"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15" />
</svg>
@ -106,6 +108,8 @@
{/each}
</div>
<div class="text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500">
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
</div>
{#if showSetDefault}
<div class="text-left mt-0.5 ml-1 text-[0.7rem] text-gray-500">
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
</div>
{/if}

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { Select } from 'bits-ui';
import { DropdownMenu } from 'bits-ui';
import { flyAndScale } from '$lib/utils/transitions';
import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
@ -25,6 +25,11 @@
export let items = [{ value: 'mango', label: 'Mango' }];
export let className = 'max-w-lg';
let selectedModel = '';
$: selectedModel = items.find((item) => item.value === value) ?? '';
let searchValue = '';
let ollamaVersion = null;
@ -175,27 +180,28 @@
};
</script>
<Select.Root
{items}
<DropdownMenu.Root
onOpenChange={async () => {
searchValue = '';
window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0);
}}
selected={items.find((item) => item.value === value) ?? ''}
onSelectedChange={(selectedItem) => {
value = selectedItem.value;
}}
>
<Select.Trigger class="relative w-full" aria-label={placeholder}>
<Select.Value
class="flex text-left px-0.5 outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400 focus:outline-none"
{placeholder}
/>
<ChevronDown className="absolute end-2 top-1/2 -translate-y-[45%] size-3.5" strokeWidth="2.5" />
</Select.Trigger>
<Select.Content
class=" z-40 w-full rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none"
<DropdownMenu.Trigger class="relative w-full" aria-label={placeholder}>
<div
class="flex w-full text-left px-0.5 outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400 focus:outline-none"
>
{#if selectedModel}
{selectedModel.label}
{:else}
{placeholder}
{/if}
<ChevronDown className=" self-center ml-2 size-3" strokeWidth="2.5" />
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Content
class=" z-40 w-full {className} justify-start rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none "
transition={flyAndScale}
side={'bottom-start'}
sideOffset={4}
>
<slot>
@ -214,12 +220,13 @@
<hr class="border-gray-100 dark:border-gray-800" />
{/if}
<div class="px-3 my-2 max-h-72 overflow-y-auto">
<div class="px-3 my-2 max-h-72 overflow-y-auto scrollbar-none">
{#each filteredItems as item}
<Select.Item
<DropdownMenu.Item
class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
value={item.value}
label={item.label}
on:click={() => {
value = item.value;
}}
>
<div class="flex items-center gap-2">
<div class="line-clamp-1">
@ -287,7 +294,7 @@
<Check />
</div>
{/if}
</Select.Item>
</DropdownMenu.Item>
{:else}
<div>
<div class="block px-3 py-2 text-sm text-gray-700 dark:text-gray-100">
@ -386,5 +393,16 @@
{/each}
</div>
</slot>
</Select.Content>
</Select.Root>
</DropdownMenu.Content>
</DropdownMenu.Root>
<style>
.scrollbar-none:active::-webkit-scrollbar-thumb,
.scrollbar-none:focus::-webkit-scrollbar-thumb,
.scrollbar-none:hover::-webkit-scrollbar-thumb {
visibility: visible;
}
.scrollbar-none::-webkit-scrollbar-thumb {
visibility: hidden;
}
</style>

View File

@ -35,8 +35,8 @@
</script>
<Modal bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class="text-gray-700 dark:text-gray-100">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
<div class=" text-lg font-medium self-center">{$i18n.t('Settings')}</div>
<button
class="self-center"
@ -56,7 +56,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
<div

View File

@ -71,7 +71,7 @@
<Modal bind:show size="sm">
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-0.5">
<div class=" text-lg font-medium self-center">{$i18n.t('Share Chat')}</div>
<button
class="self-center"
@ -91,10 +91,9 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
{#if chat}
<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
<div class="px-5 pt-4 pb-5 w-full flex flex-col justify-center">
<div class=" text-sm dark:text-gray-300 mb-1">
{#if chat.share_id}
<a href="/s/{chat.share_id}" target="_blank"

View File

@ -8,8 +8,8 @@
</script>
<Modal bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class="text-gray-700 dark:text-gray-100">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Keyboard shortcuts')}</div>
<button
class="self-center"
@ -29,7 +29,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full p-5 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">

View File

@ -96,7 +96,7 @@
<Modal size="sm" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Add Docs')}</div>
<button
class="self-center"
@ -116,8 +116,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<form

View File

@ -75,7 +75,7 @@
<Modal size="sm" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Edit Doc')}</div>
<button
class="self-center"
@ -95,8 +95,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<form
@ -111,28 +109,18 @@
<div class="flex flex-1">
<div
class="bg-gray-200 dark:bg-gray-600 font-bold px-3 py-1 border border-r-0 dark:border-gray-600 rounded-l-lg flex items-center"
class="bg-gray-200 dark:bg-gray-800 font-bold px-3 py-0.5 border border-r-0 dark:border-gray-800 rounded-l-xl flex items-center"
>
#
</div>
<input
class="w-full rounded-r-lg py-2.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
class="w-full rounded-r-xl py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
type="text"
bind:value={doc.name}
autocomplete="off"
required
/>
</div>
<!-- <div class="flex-1">
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
type="text"
bind:value={doc.name}
autocomplete="off"
required
/>
</div> -->
</div>
<div class="flex flex-col w-full">
@ -140,7 +128,7 @@
<div class="flex-1">
<input
class="w-full rounded-lg py-2.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
class="w-full rounded-xl py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
bind:value={doc.title}
autocomplete="off"
@ -150,7 +138,7 @@
</div>
<div class="flex flex-col w-full">
<div class=" mb-1.5 text-xs text-gray-500">{$i18n.t('Tags')}</div>
<div class=" mb-2 text-xs text-gray-500">{$i18n.t('Tags')}</div>
<Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} />
</div>

View File

@ -12,7 +12,7 @@
<Modal bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
<div class=" text-lg font-medium self-center">{$i18n.t('Document Settings')}</div>
<button
class="self-center"
@ -32,7 +32,6 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-800" />
<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
<div

View File

@ -0,0 +1,19 @@
<script lang="ts">
export let className = 'size-3';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"
/>
</svg>

View File

@ -0,0 +1,15 @@
<script lang="ts">
export let className = 'size-3.5';
export let strokeWidth = '2';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>

View File

@ -9,6 +9,7 @@
import ModelSelector from '../chat/ModelSelector.svelte';
import Tooltip from '../common/Tooltip.svelte';
import Menu from './Navbar/Menu.svelte';
import { page } from '$app/stores';
const i18n = getContext('i18n');
@ -27,19 +28,16 @@
<ShareChatModal bind:show={showShareChatModal} chatId={$chatId} />
<nav id="nav" class=" sticky py-2.5 top-0 flex flex-row justify-center z-30">
<div
class=" flex {$settings?.fullScreenMode ?? null ? 'max-w-full' : 'max-w-3xl'}
w-full mx-auto px-3"
>
<div class=" flex max-w-full w-full mx-auto px-5 pt-0.5 md:px-[1.3rem]">
<div class="flex items-center w-full max-w-full">
<div class="flex-1 overflow-hidden max-w-full">
{#if showModelSelector}
<ModelSelector bind:selectedModels />
<ModelSelector bind:selectedModels showSetDefault={!shareEnabled} />
{/if}
</div>
<div class="self-start flex flex-none items-center">
<div class="flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" />
<div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" />
{#if !shareEnabled}
<Tooltip content={$i18n.t('Settings')}>

View File

@ -1,12 +1,6 @@
<script lang="ts">
import { v4 as uuidv4 } from 'uuid';
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
import { goto, invalidateAll } from '$app/navigation';
import { page } from '$app/stores';
import { user, chats, settings, showSettings, chatId, tags } from '$lib/stores';
import { goto } from '$app/navigation';
import { user, chats, settings, showSettings, chatId, tags, showSidebar } from '$lib/stores';
import { onMount, getContext } from 'svelte';
const i18n = getContext('i18n');
@ -30,6 +24,7 @@
import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte';
const BREAKPOINT = 1024;
let show = false;
let navElement;
@ -50,48 +45,49 @@
let isEditing = false;
onMount(async () => {
show = window.innerWidth > BREAKPOINT;
showSidebar.set(window.innerWidth > BREAKPOINT);
await chats.set(await getChatList(localStorage.token));
let touchstartX = 0;
let touchendX = 0;
let touchstart;
let touchend;
function checkDirection() {
const screenWidth = window.innerWidth;
const swipeDistance = Math.abs(touchendX - touchstartX);
if (swipeDistance >= screenWidth / 4) {
if (touchendX < touchstartX) {
show = false;
const swipeDistance = Math.abs(touchend.screenX - touchstart.screenX);
if (touchstart.clientX < 40 && swipeDistance >= screenWidth / 8) {
if (touchend.screenX < touchstart.screenX) {
showSidebar.set(false);
}
if (touchendX > touchstartX) {
show = true;
if (touchend.screenX > touchstart.screenX) {
showSidebar.set(true);
}
}
}
const onTouchStart = (e) => {
touchstartX = e.changedTouches[0].screenX;
touchstart = e.changedTouches[0];
console.log(touchstart.clientX);
};
const onTouchEnd = (e) => {
touchendX = e.changedTouches[0].screenX;
touchend = e.changedTouches[0];
checkDirection();
};
const onResize = () => {
if (show && window.innerWidth < BREAKPOINT) {
show = false;
if ($showSidebar && window.innerWidth < BREAKPOINT) {
showSidebar.set(false);
}
};
document.addEventListener('touchstart', onTouchStart);
document.addEventListener('touchend', onTouchEnd);
window.addEventListener('touchstart', onTouchStart);
window.addEventListener('touchend', onTouchEnd);
window.addEventListener('resize', onResize);
return () => {
document.removeEventListener('touchstart', onTouchStart);
document.removeEventListener('touchend', onTouchEnd);
document.removeEventListener('resize', onResize);
window.removeEventListener('touchstart', onTouchStart);
window.removeEventListener('touchend', onTouchEnd);
window.removeEventListener('resize', onResize);
};
});
@ -166,13 +162,15 @@
<div
bind:this={navElement}
class="h-screen max-h-[100dvh] min-h-screen {show
id="sidebar"
class="h-screen max-h-[100dvh] min-h-screen {$showSidebar
? 'lg:relative w-[260px]'
: '-translate-x-[260px] w-[0px]'} bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200 text-sm transition fixed z-50 top-0 left-0
: '-translate-x-[260px] w-[0px]'} bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200 text-sm transition fixed z-50 top-0 left-0 rounded-r-2xl
"
data-state={$showSidebar}
>
<div
class="py-2.5 my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] {show
class="py-2.5 my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] {$showSidebar
? ''
: 'invisible'}"
>
@ -419,7 +417,7 @@
</div>
{/if}
<div class="pl-2 my-2 flex-1 flex flex-col space-y-1 overflow-y-auto">
<div class="pl-2 my-2 flex-1 flex flex-col space-y-1 overflow-y-auto scrollbar-none">
{#each $chats.filter((chat) => {
if (search === '') {
return true;
@ -465,7 +463,7 @@
on:click={() => {
selectedChatId = chat.id;
if (window.innerWidth < 1024) {
show = false;
showSidebar.set(false);
}
}}
draggable="false"
@ -621,6 +619,27 @@
<ArchiveBox />
</button>
</Tooltip>
{#if chat.id === $chatId}
<button
id="delete-chat-button"
class="hidden"
on:click={() => {
chatDeleteId = chat.id;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
/>
</svg>
</button>
{/if}
</div>
{/if}
</div>
@ -802,14 +821,14 @@
>
<Tooltip
placement="right"
content={`${show ? $i18n.t('Close') : $i18n.t('Open')} ${$i18n.t('sidebar')}`}
content={`${$showSidebar ? $i18n.t('Close') : $i18n.t('Open')} ${$i18n.t('sidebar')}`}
touch={false}
>
<button
id="sidebar-toggle-button"
class=" group"
on:click={() => {
show = !show;
showSidebar.set(!$showSidebar);
}}
><span class="" data-state="closed"
><div
@ -833,3 +852,14 @@
</Tooltip>
</div>
</div>
<style>
.scrollbar-none:active::-webkit-scrollbar-thumb,
.scrollbar-none:focus::-webkit-scrollbar-thumb,
.scrollbar-none:hover::-webkit-scrollbar-thumb {
visibility: visible;
}
.scrollbar-none::-webkit-scrollbar-thumb {
visibility: hidden;
}
</style>

View File

@ -42,7 +42,7 @@
<Modal size="lg" bind:show>
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
<div class=" text-lg font-medium self-center">{$i18n.t('Archived Chats')}</div>
<button
class="self-center"
@ -62,9 +62,8 @@
</svg>
</button>
</div>
<hr class=" dark:border-gray-850" />
<div class="flex flex-col md:flex-row w-full px-5 py-4 md:space-x-4 dark:text-gray-200">
<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
{#if chats.length > 0}
<div class="text-left text-sm w-full mb-4 max-h-[22rem] overflow-y-scroll">

View File

@ -20,6 +20,7 @@
"Add message": "Добавяне на съобщение",
"Add Model": "",
"Add Tags": "добавяне на тагове",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "При промяна на тези настройки промените се прилагат за всички потребители.",
"admin": "админ",
"Admin Panel": "Панел на Администратор",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Натиснете тук за проверка на други моделфайлове.",
"Click here to select": "Натиснете тук, за да изберете",
"Click here to select a csv file.": "",
"Click here to select documents.": "Натиснете тук, за да изберете документи.",
"click here.": "натиснете тук.",
"Click on the user role button to change a user's role.": "Натиснете върху бутона за промяна на ролята на потребителя.",
@ -154,6 +156,7 @@
"Enable Chat History": "Вклюване на Чат История",
"Enable New Sign Ups": "Вклюване на Нови Потребители",
"Enabled": "Включено",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Въведете съобщение за {{role}} тук",
"Enter Chunk Overlap": "Въведете Chunk Overlap",
"Enter Chunk Size": "Въведете Chunk Size",
@ -173,6 +176,7 @@
"Enter Your Email": "Въведете имейл",
"Enter Your Full Name": "Въведете вашето пълно име",
"Enter Your Password": "Въведете вашата парола",
"Enter Your Role": "",
"Experimental": "Експериментално",
"Export All Chats (All Users)": "Експортване на всички чатове (За всички потребители)",
"Export Chats": "Експортване на чатове",
@ -229,7 +233,6 @@
"Manage Ollama Models": "Управление на Ollama Моделите",
"Max Tokens": "Max Tokens",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимум 3 модели могат да бъдат сваляни едновременно. Моля, опитайте отново по-късно.",
"Messages you send after creating your link won't be shared. Users with the URL will beable to view the shared chat.": "",
"Minimum Score": "",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
@ -450,5 +453,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Вие сте полезен асистент.",
"You're now logged in.": "Сега, вие влязохте в системата."
"You're now logged in.": "Сега, вие влязохте в системата.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "মেসেজ যোগ করুন",
"Add Model": "",
"Add Tags": "ট্যাগ যোগ করুন",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "এই সেটিংগুলো পরিবর্তন করলে তা সব ইউজারের উপরেই প্রয়োগ করা হবে",
"admin": "এডমিন",
"Admin Panel": "এডমিন প্যানেল",
@ -40,7 +41,7 @@
"API keys": "",
"API RPM": "এপিআই আরপিএম",
"Archive": "",
"Archived Chats": "",
"Archived Chats": "চ্যাট ইতিহাস সংরক্ষণাগার",
"are allowed - Activate this command by typing": "অনুমোদিত - কমান্ডটি চালু করার জন্য লিখুন",
"Are you sure?": "আপনি নিশ্চিত?",
"Attention to detail": "",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "অন্যান্য মডেলফাইল চেক করার জন্য এখানে ক্লিক করুন",
"Click here to select": "নির্বাচন করার জন্য এখানে ক্লিক করুন",
"Click here to select a csv file.": "",
"Click here to select documents.": "ডকুমেন্টগুলো নির্বাচন করার জন্য এখানে ক্লিক করুন",
"click here.": "এখানে ক্লিক করুন",
"Click on the user role button to change a user's role.": "ইউজারের পদবি পরিবর্তন করার জন্য ইউজারের পদবি বাটনে ক্লিক করুন",
@ -154,6 +156,7 @@
"Enable Chat History": "চ্যাট হিস্টোরি চালু করুন",
"Enable New Sign Ups": "নতুন সাইনআপ চালু করুন",
"Enabled": "চালু করা হয়েছে",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "{{role}} মেসেজ এখানে লিখুন",
"Enter Chunk Overlap": "চাঙ্ক ওভারল্যাপ লিখুন",
"Enter Chunk Size": "চাংক সাইজ লিখুন",
@ -173,6 +176,7 @@
"Enter Your Email": "আপনার ইমেইল লিখুন",
"Enter Your Full Name": "আপনার পূর্ণ নাম লিখুন",
"Enter Your Password": "আপনার পাসওয়ার্ড লিখুন",
"Enter Your Role": "",
"Experimental": "পরিক্ষামূলক",
"Export All Chats (All Users)": "সব চ্যাট এক্সপোর্ট করুন (সব ইউজারের)",
"Export Chats": "চ্যাটগুলো এক্সপোর্ট করুন",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "আপনি একজন উপকারী এসিস্ট্যান্ট",
"You're now logged in.": "আপনি এখন লগইন করা অবস্থায় আছেন"
"You're now logged in.": "আপনি এখন লগইন করা অবস্থায় আছেন",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Afegeix missatge",
"Add Model": "",
"Add Tags": "afegeix etiquetes",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Ajustar aquests paràmetres aplicarà canvis de manera universal a tots els usuaris.",
"admin": "administrador",
"Admin Panel": "Panell d'Administració",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Fes clic aquí per comprovar altres fitxers de model.",
"Click here to select": "Fes clic aquí per seleccionar",
"Click here to select a csv file.": "",
"Click here to select documents.": "Fes clic aquí per seleccionar documents.",
"click here.": "fes clic aquí.",
"Click on the user role button to change a user's role.": "Fes clic al botó de rol d'usuari per canviar el rol d'un usuari.",
@ -154,6 +156,7 @@
"Enable Chat History": "Activa Historial de Xat",
"Enable New Sign Ups": "Permet Noves Inscripcions",
"Enabled": "Activat",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
"Enter Chunk Overlap": "Introdueix el Solapament de Blocs",
"Enter Chunk Size": "Introdueix la Mida del Bloc",
@ -173,6 +176,7 @@
"Enter Your Email": "Introdueix el Teu Correu Electrònic",
"Enter Your Full Name": "Introdueix el Teu Nom Complet",
"Enter Your Password": "Introdueix la Teva Contrasenya",
"Enter Your Role": "",
"Experimental": "Experimental",
"Export All Chats (All Users)": "Exporta Tots els Xats (Tots els Usuaris)",
"Export Chats": "Exporta Xats",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Ets un assistent útil.",
"You're now logged in.": "Ara estàs connectat."
"You're now logged in.": "Ara estàs connectat.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Nachricht eingeben",
"Add Model": "Modell hinzufügen",
"Add Tags": "Tags hinzufügen",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wirkt sich universell auf alle Benutzer aus.",
"admin": "Administrator",
"Admin Panel": "Admin Panel",
@ -73,6 +74,7 @@
"Click here to": "Klicke hier, um",
"Click here to check other modelfiles.": "Klicke hier, um andere Modelfiles zu überprüfen.",
"Click here to select": "Klicke hier um auszuwählen",
"Click here to select a csv file.": "",
"Click here to select documents.": "Klicke hier um Dokumente auszuwählen",
"click here.": "hier klicken.",
"Click on the user role button to change a user's role.": "Klicke auf die Benutzerrollenschaltfläche, um die Rolle eines Benutzers zu ändern.",
@ -154,6 +156,7 @@
"Enable Chat History": "Chat-Verlauf aktivieren",
"Enable New Sign Ups": "Neue Anmeldungen aktivieren",
"Enabled": "Aktiviert",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Gib die {{role}} Nachricht hier ein",
"Enter Chunk Overlap": "Gib den Chunk Overlap ein",
"Enter Chunk Size": "Gib die Chunk Size ein",
@ -173,6 +176,7 @@
"Enter Your Email": "Gib deine E-Mail-Adresse ein",
"Enter Your Full Name": "Gib deinen vollständigen Namen ein",
"Enter Your Password": "Gib dein Passwort ein",
"Enter Your Role": "",
"Experimental": "Experimentell",
"Export All Chats (All Users)": "Alle Chats exportieren (alle Benutzer)",
"Export Chats": "Chats exportieren",
@ -184,11 +188,11 @@
"Feel free to add specific details": "Ergänze Details.",
"File Mode": "File Modus",
"File not found.": "Datei nicht gefunden.",
"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingerabdruck-Spoofing erkannt: Initialen können nicht als Avatar verwendet werden. Standardprofilbild wird verwendet.",
"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingerprint spoofing erkannt: Initialen können nicht als Avatar verwendet werden. Es wird auf das Standardprofilbild zurückgegriffen.",
"Fluidly stream large external response chunks": "Streamen Sie große externe Antwortblöcke flüssig",
"Focus chat input": "Chat-Eingabe fokussieren",
"Followed instructions perfectly": "Hat die Anweisungen perfekt befolgt",
"Format your variables using square brackets like this:": "Formatiere Deine Variablen mit eckigen Klammern wie folgt:",
"Followed instructions perfectly": "Anweisungen perfekt befolgt",
"Format your variables using square brackets like this:": "Formatiere deine Variablen mit eckigen Klammern wie folgt:",
"From (Base Model)": "Von (Basismodell)",
"Full Screen Mode": "Vollbildmodus",
"General": "Allgemein",
@ -297,7 +301,7 @@
"pending": "ausstehend",
"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}",
"Plain text (.txt)": "Nur Text (.txt)",
"Playground": "Playground",
"Playground": "Spielplatz",
"Positive attitude": "Positive Einstellung",
"Profile Image": "Profilbild",
"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (z.B. Erzähle mir eine interessante Tatsache über das Römische Reich.",
@ -450,5 +454,6 @@
"You have no archived conversations.": "Du hast keine archivierten Unterhaltungen.",
"You have shared this chat": "Du hast diesen Chat",
"You're a helpful assistant.": "Du bist ein hilfreicher Assistent.",
"You're now logged in.": "Du bist nun eingeloggt."
"You're now logged in.": "Du bist nun eingeloggt.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Add Prompt",
"Add Model": "",
"Add Tags": "",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Adjusting these settings will apply changes to all users. Such universal, very wow.",
"admin": "admin",
"Admin Panel": "Admin Panel",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Click to check other modelfiles.",
"Click here to select": "Click to select",
"Click here to select a csv file.": "",
"Click here to select documents.": "Click to select documents",
"click here.": "click here. Such click.",
"Click on the user role button to change a user's role.": "Click user role button to change role.",
@ -154,6 +156,7 @@
"Enable Chat History": "Activate Chat Story",
"Enable New Sign Ups": "Enable New Bark Ups",
"Enabled": "So Activated",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Enter {{role}} bork here",
"Enter Chunk Overlap": "Enter Overlap of Chunks",
"Enter Chunk Size": "Enter Size of Chunk",
@ -173,6 +176,7 @@
"Enter Your Email": "Enter Your Dogemail",
"Enter Your Full Name": "Enter Your Full Wow",
"Enter Your Password": "Enter Your Barkword",
"Enter Your Role": "",
"Experimental": "Much Experiment",
"Export All Chats (All Users)": "Export All Chats (All Doggos)",
"Export Chats": "Export Barks",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "You're a helpful assistant. Much helpful.",
"You're now logged in.": "You're now logged in. Much logged."
"You're now logged in.": "You're now logged in. Much logged.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "",
"Add Model": "",
"Add Tags": "",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "",
"admin": "",
"Admin Panel": "",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "",
"Click here to select": "",
"Click here to select a csv file.": "",
"Click here to select documents.": "",
"click here.": "",
"Click on the user role button to change a user's role.": "",
@ -154,6 +156,7 @@
"Enable Chat History": "",
"Enable New Sign Ups": "",
"Enabled": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "",
"Enter Chunk Overlap": "",
"Enter Chunk Size": "",
@ -173,6 +176,7 @@
"Enter Your Email": "",
"Enter Your Full Name": "",
"Enter Your Password": "",
"Enter Your Role": "",
"Experimental": "",
"Export All Chats (All Users)": "",
"Export Chats": "",
@ -229,7 +233,10 @@
"Manage Ollama Models": "",
"Max Tokens": "",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "",
<<<<<<< HEAD
"Messages you send after creating your link won't be shared. Users with the URL will beable to view the shared chat.": "",
=======
>>>>>>> origin-dev
"Minimum Score": "",
"Mirostat": "",
"Mirostat Eta": "",
@ -450,5 +457,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "",
"You're now logged in.": ""
"You're now logged in.": "",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "",
"Add Model": "",
"Add Tags": "",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "",
"admin": "",
"Admin Panel": "",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "",
"Click here to select": "",
"Click here to select a csv file.": "",
"Click here to select documents.": "",
"click here.": "",
"Click on the user role button to change a user's role.": "",
@ -154,6 +156,7 @@
"Enable Chat History": "",
"Enable New Sign Ups": "",
"Enabled": "",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "",
"Enter Chunk Overlap": "",
"Enter Chunk Size": "",
@ -173,6 +176,7 @@
"Enter Your Email": "",
"Enter Your Full Name": "",
"Enter Your Password": "",
"Enter Your Role": "",
"Experimental": "",
"Export All Chats (All Users)": "",
"Export Chats": "",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "",
"You're now logged in.": ""
"You're now logged in.": "",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Agregar Prompt",
"Add Model": "",
"Add Tags": "agregar etiquetas",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Ajustar estas opciones aplicará los cambios universalmente a todos los usuarios.",
"admin": "admin",
"Admin Panel": "Panel de Administración",
@ -42,7 +43,7 @@
"Archive": "",
"Archived Chats": "Chats archivados",
"are allowed - Activate this command by typing": "están permitidos - Active este comando escribiendo",
"Are you sure?": "Esta usted seguro?",
"Are you sure?": "¿Está seguro?",
"Attention to detail": "",
"Audio": "Audio",
"Auto-playback response": "Respuesta de reproducción automática",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Presiona aquí para otros modelfiles.",
"Click here to select": "Presiona aquí para seleccionar",
"Click here to select a csv file.": "",
"Click here to select documents.": "Presiona aquí para seleccionar documentos",
"click here.": "Presiona aquí.",
"Click on the user role button to change a user's role.": "Presiona en el botón de roles del usuario para cambiar su rol.",
@ -139,7 +141,7 @@
"Documents": "Documentos",
"does not make any external connections, and your data stays securely on your locally hosted server.": "no realiza ninguna conexión externa y sus datos permanecen seguros en su servidor alojado localmente.",
"Don't Allow": "No Permitir",
"Don't have an account?": "No tienes una cuenta?",
"Don't have an account?": "¿No tienes una cuenta?",
"Don't like the style": "",
"Download": "",
"Download Database": "Descarga la Base de Datos",
@ -154,6 +156,7 @@
"Enable Chat History": "Activa el Historial de Chat",
"Enable New Sign Ups": "Habilitar Nuevos Registros",
"Enabled": "Activado",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Ingrese el mensaje {{role}} aquí",
"Enter Chunk Overlap": "Ingresar superposición de fragmentos",
"Enter Chunk Size": "Ingrese el tamaño del fragmento",
@ -166,13 +169,14 @@
"Enter model tag (e.g. {{modelTag}})": "Ingrese la etiqueta del modelo (p.ej. {{modelTag}})",
"Enter Number of Steps (e.g. 50)": "Ingrese el número de pasos (p.ej., 50)",
"Enter Score": "",
"Enter stop sequence": "Introduzca la secuencia de parada",
"Enter Top K": "Introduzca el Top K",
"Enter stop sequence": "Ingrese la secuencia de parada",
"Enter Top K": "Ingrese el Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Ingrese la URL (p.ej., http://127.0.0.1:7860/)",
"Enter URL (e.g. http://localhost:11434)": "",
"Enter Your Email": "Ingrese su correo electrónico",
"Enter Your Full Name": "Ingrese su nombre completo",
"Enter Your Password": "Ingrese su contraseña",
"Enter Your Role": "",
"Experimental": "Experimental",
"Export All Chats (All Users)": "Exportar todos los chats (Todos los usuarios)",
"Export Chats": "Exportar Chats",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Eres un asistente útil.",
"You're now logged in.": "Has iniciado sesión."
"You're now logged in.": "Has iniciado sesión.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "اضافه کردن پیغام",
"Add Model": "",
"Add Tags": "اضافه کردن تگ\u200cها",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "با تنظیم این تنظیمات، تغییرات به طور کلی برای همه کاربران اعمال می شود.",
"admin": "مدیر",
"Admin Panel": "پنل مدیریت",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "برای بررسی سایر فایل\u200cهای مدل اینجا را کلیک کنید.",
"Click here to select": "برای انتخاب اینجا کلیک کنید",
"Click here to select a csv file.": "",
"Click here to select documents.": "برای انتخاب اسناد اینجا را کلیک کنید.",
"click here.": "اینجا کلیک کنید.",
"Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.",
@ -154,6 +156,7 @@
"Enable Chat History": "تاریخچه چت را فعال کنید",
"Enable New Sign Ups": "فعال کردن ثبت نام\u200cهای جدید",
"Enabled": "فعال",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "پیام {{role}} را اینجا وارد کنید",
"Enter Chunk Overlap": "مقدار Chunk Overlap را وارد کنید",
"Enter Chunk Size": "مقدار Chunk Size را وارد کنید",
@ -173,6 +176,7 @@
"Enter Your Email": "ایمیل خود را وارد کنید",
"Enter Your Full Name": "نام کامل خود را وارد کنید",
"Enter Your Password": "رمز عبور خود را وارد کنید",
"Enter Your Role": "",
"Experimental": "آزمایشی",
"Export All Chats (All Users)": "اکسپورت از همه گپ\u200cها(همه کاربران)",
"Export Chats": "اکسپورت از گپ\u200cها",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "تو یک دستیار سودمند هستی.",
"You're now logged in.": "شما اکنون وارد شده\u200cاید."
"You're now logged in.": "شما اکنون وارد شده\u200cاید.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Ajouter un message",
"Add Model": "",
"Add Tags": "ajouter des tags",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "L'ajustement de ces paramètres appliquera les changements à tous les utilisateurs.",
"admin": "Administrateur",
"Admin Panel": "Panneau d'administration",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Cliquez ici pour vérifier d'autres fichiers de modèle.",
"Click here to select": "Cliquez ici pour sélectionner",
"Click here to select a csv file.": "",
"Click here to select documents.": "Cliquez ici pour sélectionner des documents.",
"click here.": "cliquez ici.",
"Click on the user role button to change a user's role.": "Cliquez sur le bouton de rôle d'utilisateur pour changer le rôle d'un utilisateur.",
@ -154,6 +156,7 @@
"Enable Chat History": "Activer l'historique des discussions",
"Enable New Sign Ups": "Activer les nouvelles inscriptions",
"Enabled": "Activé",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Entrez le message {{role}} ici",
"Enter Chunk Overlap": "Entrez le chevauchement de bloc",
"Enter Chunk Size": "Entrez la taille du bloc",
@ -173,6 +176,7 @@
"Enter Your Email": "Entrez votre adresse email",
"Enter Your Full Name": "Entrez votre nom complet",
"Enter Your Password": "Entrez votre mot de passe",
"Enter Your Role": "",
"Experimental": "Expérimental",
"Export All Chats (All Users)": "Exporter toutes les discussions (Tous les utilisateurs)",
"Export Chats": "Exporter les discussions",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Vous êtes un assistant utile",
"You're now logged in.": "Vous êtes maintenant connecté."
"You're now logged in.": "Vous êtes maintenant connecté.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Ajouter un message",
"Add Model": "",
"Add Tags": "ajouter des tags",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "L'ajustement de ces paramètres appliquera les changements à tous les utilisateurs.",
"admin": "Administrateur",
"Admin Panel": "Panneau d'administration",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Cliquez ici pour vérifier d'autres fichiers de modèle.",
"Click here to select": "Cliquez ici pour sélectionner",
"Click here to select a csv file.": "",
"Click here to select documents.": "Cliquez ici pour sélectionner des documents.",
"click here.": "cliquez ici.",
"Click on the user role button to change a user's role.": "Cliquez sur le bouton de rôle d'utilisateur pour changer le rôle d'un utilisateur.",
@ -154,6 +156,7 @@
"Enable Chat History": "Activer l'historique du chat",
"Enable New Sign Ups": "Activer les nouvelles inscriptions",
"Enabled": "Activé",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Entrez le message {{role}} ici",
"Enter Chunk Overlap": "Entrez le chevauchement de bloc",
"Enter Chunk Size": "Entrez la taille du bloc",
@ -173,6 +176,7 @@
"Enter Your Email": "Entrez votre email",
"Enter Your Full Name": "Entrez votre nom complet",
"Enter Your Password": "Entrez votre mot de passe",
"Enter Your Role": "",
"Experimental": "Expérimental",
"Export All Chats (All Users)": "Exporter tous les chats (tous les utilisateurs)",
"Export Chats": "Exporter les chats",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Vous êtes un assistant utile",
"You're now logged in.": "Vous êtes maintenant connecté."
"You're now logged in.": "Vous êtes maintenant connecté.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Aggiungi messaggio",
"Add Model": "",
"Add Tags": "aggiungi tag",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "La modifica di queste impostazioni applicherà le modifiche universalmente a tutti gli utenti.",
"admin": "amministratore",
"Admin Panel": "Pannello di amministrazione",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Clicca qui per controllare altri file modello.",
"Click here to select": "Clicca qui per selezionare",
"Click here to select a csv file.": "",
"Click here to select documents.": "Clicca qui per selezionare i documenti.",
"click here.": "clicca qui.",
"Click on the user role button to change a user's role.": "Clicca sul pulsante del ruolo utente per modificare il ruolo di un utente.",
@ -154,6 +156,7 @@
"Enable Chat History": "Abilita cronologia chat",
"Enable New Sign Ups": "Abilita nuove iscrizioni",
"Enabled": "Abilitato",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Inserisci il messaggio per {{role}} qui",
"Enter Chunk Overlap": "Inserisci la sovrapposizione chunk",
"Enter Chunk Size": "Inserisci la dimensione chunk",
@ -173,6 +176,7 @@
"Enter Your Email": "Inserisci la tua email",
"Enter Your Full Name": "Inserisci il tuo nome completo",
"Enter Your Password": "Inserisci la tua password",
"Enter Your Role": "",
"Experimental": "Sperimentale",
"Export All Chats (All Users)": "Esporta tutte le chat (tutti gli utenti)",
"Export Chats": "Esporta chat",
@ -229,7 +233,10 @@
"Manage Ollama Models": "Gestisci modelli Ollama",
"Max Tokens": "Max token",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "È possibile scaricare un massimo di 3 modelli contemporaneamente. Riprova più tardi.",
<<<<<<< HEAD
"Messages you send after creating your link won't be shared. Users with the URL will beable to view the shared chat.": "",
=======
>>>>>>> origin-dev
"Minimum Score": "",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
@ -450,5 +457,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Sei un assistente utile.",
"You're now logged in.": "Ora hai effettuato l'accesso."
"You're now logged in.": "Ora hai effettuato l'accesso.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "メッセージを追加",
"Add Model": "",
"Add Tags": "タグを追加",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "これらの設定を調整すると、すべてのユーザーに普遍的に変更が適用されます。",
"admin": "管理者",
"Admin Panel": "管理者パネル",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "他のモデルファイルを確認するにはここをクリックしてください。",
"Click here to select": "選択するにはここをクリックしてください",
"Click here to select a csv file.": "",
"Click here to select documents.": "ドキュメントを選択するにはここをクリックしてください。",
"click here.": "ここをクリックしてください。",
"Click on the user role button to change a user's role.": "ユーザーの役割を変更するには、ユーザー役割ボタンをクリックしてください。",
@ -154,6 +156,7 @@
"Enable Chat History": "チャット履歴を有効化",
"Enable New Sign Ups": "新規登録を有効化",
"Enabled": "有効",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "{{role}} メッセージをここに入力してください",
"Enter Chunk Overlap": "チャンクオーバーラップを入力してください",
"Enter Chunk Size": "チャンクサイズを入力してください",
@ -173,6 +176,7 @@
"Enter Your Email": "メールアドレスを入力してください",
"Enter Your Full Name": "フルネームを入力してください",
"Enter Your Password": "パスワードを入力してください",
"Enter Your Role": "",
"Experimental": "実験的",
"Export All Chats (All Users)": "すべてのチャットをエクスポート (すべてのユーザー)",
"Export Chats": "チャットをエクスポート",
@ -229,7 +233,10 @@
"Manage Ollama Models": "Ollama モデルを管理",
"Max Tokens": "最大トークン数",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "同時にダウンロードできるモデルは最大 3 つです。後でもう一度お試しください。",
<<<<<<< HEAD
"Messages you send after creating your link won't be shared. Users with the URL will beable to view the shared chat.": "",
=======
>>>>>>> origin-dev
"Minimum Score": "",
"Mirostat": "ミロスタット",
"Mirostat Eta": "ミロスタット Eta",
@ -450,5 +457,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "あなたは役に立つアシスタントです。",
"You're now logged in.": "ログインしました。"
"You're now logged in.": "ログインしました。",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "შეტყობინების დამატება",
"Add Model": "",
"Add Tags": "ტეგების დამატება",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "ამ პარამეტრების რეგულირება ცვლილებებს უნივერსალურად გამოიყენებს ყველა მომხმარებლისთვის",
"admin": "ადმინისტრატორი",
"Admin Panel": "ადმინ პანელი",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "სხვა მოდელური ფაილების სანახავად, დააკლიკე აქ",
"Click here to select": "ასარჩევად, დააკლიკე აქ",
"Click here to select a csv file.": "",
"Click here to select documents.": "დოკუმენტების ასარჩევად, დააკლიკე აქ",
"click here.": "დააკლიკე აქ",
"Click on the user role button to change a user's role.": "დააკლიკეთ მომხმარებლის როლის ღილაკს რომ შეცვალოთ მომხმარების როლი",
@ -154,6 +156,7 @@
"Enable Chat History": "მიმოწერის ისტორიის ჩართვა",
"Enable New Sign Ups": "ახალი რეგისტრაციების ჩართვა",
"Enabled": "ჩართულია",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "შეიყვანე {{role}} შეტყობინება აქ",
"Enter Chunk Overlap": "შეიყვანეთ ნაწილის გადახურვა",
"Enter Chunk Size": "შეიყვანე ბლოკის ზომა",
@ -173,6 +176,7 @@
"Enter Your Email": "შეიყვანეთ თქვენი ელ-ფოსტა",
"Enter Your Full Name": "შეიყვანეთ თქვენი სრული სახელი",
"Enter Your Password": "შეიყვანეთ თქვენი პაროლი",
"Enter Your Role": "",
"Experimental": "ექსპერიმენტალური",
"Export All Chats (All Users)": "",
"Export Chats": "მიმოწერის ექსპორტირება",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "თქვენ სასარგებლო ასისტენტი ხართ.",
"You're now logged in.": "თქვენ შესული ხართ."
"You're now logged in.": "თქვენ შესული ხართ.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "메시지 추가",
"Add Model": "",
"Add Tags": "태그들 추가",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "이 설정을 조정하면 모든 사용자에게 적용됩니다.",
"admin": "관리자",
"Admin Panel": "관리자 패널",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "다른 모델파일을 확인하려면 여기를 클릭하세요.",
"Click here to select": "선택하려면 여기를 클릭하세요.",
"Click here to select a csv file.": "",
"Click here to select documents.": "문서를 선택하려면 여기를 클릭하세요.",
"click here.": "여기를 클릭하세요.",
"Click on the user role button to change a user's role.": "사용자 역할 버튼을 클릭하여 사용자의 역할을 변경하세요.",
@ -154,6 +156,7 @@
"Enable Chat History": "채팅 기록 활성화",
"Enable New Sign Ups": "새 회원가입 활성화",
"Enabled": "활성화",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "여기에 {{role}} 메시지 입력",
"Enter Chunk Overlap": "청크 오버랩 입력",
"Enter Chunk Size": "청크 크기 입력",
@ -173,6 +176,7 @@
"Enter Your Email": "이메일 입력",
"Enter Your Full Name": "전체 이름 입력",
"Enter Your Password": "비밀번호 입력",
"Enter Your Role": "",
"Experimental": "실험적",
"Export All Chats (All Users)": "모든 채팅 내보내기 (모든 사용자)",
"Export Chats": "채팅 내보내기",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "당신은 유용한 어시스턴트입니다.",
"You're now logged in.": "로그인되었습니다."
"You're now logged in.": "로그인되었습니다.",
"Youtube": ""
}

View File

@ -75,6 +75,10 @@
"code": "ru-RU",
"title": "Russian (Russia)"
},
{
"code": "sv-SE",
"title": "Swedish"
},
{
"code": "tr-TR",
"title": "Turkish"

View File

@ -20,6 +20,7 @@
"Add message": "Voeg bericht toe",
"Add Model": "",
"Add Tags": "voeg tags toe",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Het aanpassen van deze instellingen zal universeel worden toegepast op alle gebruikers.",
"admin": "admin",
"Admin Panel": "Administratieve Paneel",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Klik hier om andere modelfiles te controleren.",
"Click here to select": "Klik hier om te selecteren",
"Click here to select a csv file.": "",
"Click here to select documents.": "Klik hier om documenten te selecteren",
"click here.": "klik hier.",
"Click on the user role button to change a user's role.": "Klik op de gebruikersrol knop om de rol van een gebruiker te wijzigen.",
@ -154,6 +156,7 @@
"Enable Chat History": "Schakel Chat Geschiedenis in",
"Enable New Sign Ups": "Schakel Nieuwe Registraties in",
"Enabled": "Ingeschakeld",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Voeg {{role}} bericht hier toe",
"Enter Chunk Overlap": "Voeg Chunk Overlap toe",
"Enter Chunk Size": "Voeg Chunk Size toe",
@ -173,6 +176,7 @@
"Enter Your Email": "Voer je Email in",
"Enter Your Full Name": "Voer je Volledige Naam in",
"Enter Your Password": "Voer je Wachtwoord in",
"Enter Your Role": "",
"Experimental": "Experimenteel",
"Export All Chats (All Users)": "Exporteer Alle Chats (Alle Gebruikers)",
"Export Chats": "Exporteer Chats",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Jij bent een behulpzame assistent.",
"You're now logged in.": "Je bent nu ingelogd."
"You're now logged in.": "Je bent nu ingelogd.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Dodaj wiadomość",
"Add Model": "",
"Add Tags": "dodaj tagi",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Dostosowanie tych ustawień spowoduje zastosowanie zmian uniwersalnie do wszystkich użytkowników.",
"admin": "admin",
"Admin Panel": "Panel administracyjny",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Kliknij tutaj, aby sprawdzić inne pliki modelowe.",
"Click here to select": "Kliknij tutaj, aby wybrać",
"Click here to select a csv file.": "",
"Click here to select documents.": "Kliknij tutaj, aby wybrać dokumenty.",
"click here.": "kliknij tutaj.",
"Click on the user role button to change a user's role.": "Kliknij przycisk roli użytkownika, aby zmienić rolę użytkownika.",
@ -154,6 +156,7 @@
"Enable Chat History": "Włącz historię czatu",
"Enable New Sign Ups": "Włącz nowe rejestracje",
"Enabled": "Włączone",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Wprowadź wiadomość {{role}} tutaj",
"Enter Chunk Overlap": "Wprowadź zakchodzenie bloku",
"Enter Chunk Size": "Wprowadź rozmiar bloku",
@ -173,6 +176,7 @@
"Enter Your Email": "Wprowadź swój adres email",
"Enter Your Full Name": "Wprowadź swoje imię i nazwisko",
"Enter Your Password": "Wprowadź swoje hasło",
"Enter Your Role": "",
"Experimental": "Eksperymentalne",
"Export All Chats (All Users)": "Eksportuj wszystkie czaty (wszyscy użytkownicy)",
"Export Chats": "Eksportuj czaty",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Jesteś pomocnym asystentem.",
"You're now logged in.": "Jesteś teraz zalogowany."
"You're now logged in.": "Jesteś teraz zalogowany.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Adicionar mensagem",
"Add Model": "",
"Add Tags": "adicionar tags",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Ajustar essas configurações aplicará alterações universalmente a todos os usuários.",
"admin": "administrador",
"Admin Panel": "Painel do Administrador",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Clique aqui para verificar outros arquivos de modelo.",
"Click here to select": "Clique aqui para selecionar",
"Click here to select a csv file.": "",
"Click here to select documents.": "Clique aqui para selecionar documentos.",
"click here.": "clique aqui.",
"Click on the user role button to change a user's role.": "Clique no botão de função do usuário para alterar a função de um usuário.",
@ -154,6 +156,7 @@
"Enable Chat History": "Ativar Histórico de Bate-papo",
"Enable New Sign Ups": "Ativar Novas Inscrições",
"Enabled": "Ativado",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Digite a mensagem de {{role}} aqui",
"Enter Chunk Overlap": "Digite a Sobreposição de Fragmento",
"Enter Chunk Size": "Digite o Tamanho do Fragmento",
@ -173,6 +176,7 @@
"Enter Your Email": "Digite seu E-mail",
"Enter Your Full Name": "Digite seu Nome Completo",
"Enter Your Password": "Digite sua Senha",
"Enter Your Role": "",
"Experimental": "Experimental",
"Export All Chats (All Users)": "Exportar Todos os Bate-papos (Todos os Usuários)",
"Export Chats": "Exportar Bate-papos",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Você é um assistente útil.",
"You're now logged in.": "Você está conectado agora."
"You're now logged in.": "Você está conectado agora.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Adicionar mensagem",
"Add Model": "",
"Add Tags": "adicionar tags",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Ajustar essas configurações aplicará alterações universalmente a todos os usuários.",
"admin": "administrador",
"Admin Panel": "Painel do Administrador",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Clique aqui para verificar outros arquivos de modelo.",
"Click here to select": "Clique aqui para selecionar",
"Click here to select a csv file.": "",
"Click here to select documents.": "Clique aqui para selecionar documentos.",
"click here.": "clique aqui.",
"Click on the user role button to change a user's role.": "Clique no botão de função do usuário para alterar a função de um usuário.",
@ -154,6 +156,7 @@
"Enable Chat History": "Ativar Histórico de Bate-papo",
"Enable New Sign Ups": "Ativar Novas Inscrições",
"Enabled": "Ativado",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Digite a mensagem de {{role}} aqui",
"Enter Chunk Overlap": "Digite a Sobreposição de Fragmento",
"Enter Chunk Size": "Digite o Tamanho do Fragmento",
@ -173,6 +176,7 @@
"Enter Your Email": "Digite seu E-mail",
"Enter Your Full Name": "Digite seu Nome Completo",
"Enter Your Password": "Digite sua Senha",
"Enter Your Role": "",
"Experimental": "Experimental",
"Export All Chats (All Users)": "Exportar Todos os Bate-papos (Todos os Usuários)",
"Export Chats": "Exportar Bate-papos",
@ -229,7 +233,10 @@
"Manage Ollama Models": "Gerenciar Modelos Ollama",
"Max Tokens": "Máximo de Tokens",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Máximo de 3 modelos podem ser baixados simultaneamente. Tente novamente mais tarde.",
<<<<<<< HEAD
"Messages you send after creating your link won't be shared. Users with the URL will beable to view the shared chat.": "",
=======
>>>>>>> origin-dev
"Minimum Score": "",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
@ -450,5 +457,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Você é um assistente útil.",
"You're now logged in.": "Você está conectado agora."
"You're now logged in.": "Você está conectado agora.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Добавьте сообщение",
"Add Model": "",
"Add Tags": "Добавьте тэгы",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Регулирующий этих настроек приведет к изменениям для все пользователей.",
"admin": "админ",
"Admin Panel": "Панель админ",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Нажмите тут чтобы проверить другие файлы моделей.",
"Click here to select": "Нажмите тут чтобы выберите",
"Click here to select a csv file.": "",
"Click here to select documents.": "Нажмите здесь чтобы выберите документы.",
"click here.": "нажмите здесь.",
"Click on the user role button to change a user's role.": "Нажмите кнопку роли пользователя чтобы изменить роль пользователя.",
@ -154,6 +156,7 @@
"Enable Chat History": "Включить историю чата",
"Enable New Sign Ups": "Разрешить новые регистрации",
"Enabled": "Включено",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Введите сообщение {{role}} здесь",
"Enter Chunk Overlap": "Введите перекрытие фрагмента",
"Enter Chunk Size": "Введите размер фрагмента",
@ -173,6 +176,7 @@
"Enter Your Email": "Введите вашу электронную почту",
"Enter Your Full Name": "Введите ваше полное имя",
"Enter Your Password": "Введите ваш пароль",
"Enter Your Role": "",
"Experimental": "Экспериментальное",
"Export All Chats (All Users)": "Экспортировать все чаты (все пользователи)",
"Export Chats": "Экспортировать чаты",
@ -229,7 +233,10 @@
"Manage Ollama Models": "Управление моделями Ollama",
"Max Tokens": "Максимальное количество токенов",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимальное количество моделей для загрузки одновременно - 3. Пожалуйста, попробуйте позже.",
<<<<<<< HEAD
"Messages you send after creating your link won't be shared. Users with the URL will beable to view the shared chat.": "",
=======
>>>>>>> origin-dev
"Minimum Score": "",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
@ -450,5 +457,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Вы полезный ассистент.",
"You're now logged in.": "Вы вошли в систему."
"You're now logged in.": "Вы вошли в систему.",
"Youtube": ""
}

View File

@ -0,0 +1,443 @@
{
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' eller '-1' för ingen utgång.",
"(Beta)": "(Beta)",
"(e.g. `sh webui.sh --api`)": "(t.ex. `sh webui.sh --api`)",
"(latest)": "(senaste)",
"{{modelName}} is thinking...": "{{modelName}} tänker...",
"{{user}}'s Chats": "",
"{{webUIName}} Backend Required": "{{webUIName}} Backend krävs",
"a user": "en användare",
"About": "Om",
"Account": "Konto",
"Accurate information": "",
"Add a model": "Lägg till en modell",
"Add a model tag name": "Lägg till ett modellnamn",
"Add a short description about what this modelfile does": "Lägg till en kort beskrivning av vad den här modelfilen gör",
"Add a short title for this prompt": "Lägg till en kort titel för denna prompt",
"Add a tag": "Lägg till en tagg",
"Add Docs": "Lägg till dokument",
"Add Files": "Lägg till filer",
"Add message": "Lägg till meddelande",
"Add Model": "",
"Add Tags": "",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Justering av dessa inställningar kommer att tillämpa ändringar universellt för alla användare.",
"admin": "administratör",
"Admin Panel": "Administrationspanel",
"Admin Settings": "Administratörsinställningar",
"Advanced Parameters": "Avancerade parametrar",
"all": "alla",
"All Users": "Alla användare",
"Allow": "Tillåt",
"Allow Chat Deletion": "Tillåt chattborttagning",
"alphanumeric characters and hyphens": "alfanumeriska tecken och bindestreck",
"Already have an account?": "Har du redan ett konto?",
"an assistant": "en assistent",
"and": "och",
"API Base URL": "API-bas-URL",
"API Key": "API-nyckel",
"API Key created.": "",
"API keys": "",
"API RPM": "API RPM",
"Archive": "",
"Archived Chats": "",
"are allowed - Activate this command by typing": "är tillåtna - Aktivera detta kommando genom att skriva",
"Are you sure?": "Är du säker?",
"Attention to detail": "",
"Audio": "Ljud",
"Auto-playback response": "Automatisk uppspelning",
"Auto-send input after 3 sec.": "Skicka automatiskt indata efter 3 sek.",
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 bas-URL",
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 bas-URL krävs.",
"available!": "tillgänglig!",
"Back": "Tillbaka",
"Bad Response": "",
"Being lazy": "",
"Builder Mode": "Byggarläge",
"Cancel": "Avbryt",
"Categories": "Kategorier",
"Change Password": "Ändra lösenord",
"Chat": "Chatt",
"Chat History": "Chatthistorik",
"Chat History is off for this browser.": "Chatthistoriken är avstängd för denna webbläsare.",
"Chats": "Chattar",
"Check Again": "Kontrollera igen",
"Check for updates": "Sök efter uppdateringar",
"Checking for updates...": "Söker efter uppdateringar...",
"Choose a model before saving...": "Välj en modell innan du sparar...",
"Chunk Overlap": "Överlappning",
"Chunk Params": "Chunk-parametrar",
"Chunk Size": "Chunk-storlek",
"Click here for help.": "Klicka här för hjälp.",
"Click here to check other modelfiles.": "Klicka här för att kontrollera andra modelfiler.",
"Click here to select": "Klicka här för att välja",
"Click here to select a csv file.": "",
"Click here to select documents.": "Klicka här för att välja dokument.",
"click here.": "klicka här.",
"Click on the user role button to change a user's role.": "Klicka på knappen för användarroll för att ändra en användares roll.",
"Close": "Stäng",
"Collection": "Samling",
"ComfyUI": "",
"ComfyUI Base URL": "",
"ComfyUI Base URL is required.": "",
"Command": "Kommando",
"Confirm Password": "Bekräfta lösenord",
"Connections": "Anslutningar",
"Content": "Innehåll",
"Context Length": "Kontextlängd",
"Continue Response": "",
"Conversation Mode": "Samtalsläge",
"Copied shared chat URL to clipboard!": "",
"Copy": "",
"Copy last code block": "Kopiera sista kodblock",
"Copy last response": "Kopiera sista svar",
"Copy Link": "",
"Copying to clipboard was successful!": "Kopiering till urklipp lyckades!",
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Skapa en kort, 3-5 ords fras som rubrik för följande fråga, strikt följa 3-5 ordsgränsen och undvika användning av ordet 'titel':",
"Create a modelfile": "Skapa en modelfil",
"Create Account": "Skapa konto",
"Created at": "Skapad",
"Created At": "",
"Current Model": "Aktuell modell",
"Current Password": "Nuvarande lösenord",
"Custom": "Anpassad",
"Customize Ollama models for a specific purpose": "Anpassa Ollama-modeller för ett specifikt ändamål",
"Dark": "Mörk",
"Database": "Databas",
"DD/MM/YYYY HH:mm": "DD/MM/ÅÅÅÅ TT:mm",
"Default": "Standard",
"Default (Automatic1111)": "Standard (Automatic1111)",
"Default (SentenceTransformers)": "",
"Default (Web API)": "Standard (Web API)",
"Default model updated": "Standardmodell uppdaterad",
"Default Prompt Suggestions": "Standardpromptförslag",
"Default User Role": "Standardanvändarroll",
"delete": "radera",
"Delete": "",
"Delete a model": "Ta bort en modell",
"Delete chat": "Radera chatt",
"Delete Chat": "",
"Delete Chats": "Radera chattar",
"Delete User": "",
"Deleted {{deleteModelTag}}": "Raderad {{deleteModelTag}}",
"Deleted {{tagName}}": "",
"Description": "Beskrivning",
"Didn't fully follow instructions": "",
"Disabled": "Inaktiverad",
"Discover a modelfile": "Upptäck en modelfil",
"Discover a prompt": "Upptäck en prompt",
"Discover, download, and explore custom prompts": "Upptäck, ladda ner och utforska anpassade prompts",
"Discover, download, and explore model presets": "Upptäck, ladda ner och utforska modellförinställningar",
"Display the username instead of You in the Chat": "Visa användarnamnet istället för du i chatten",
"Document": "Dokument",
"Document Settings": "Dokumentinställningar",
"Documents": "Dokument",
"does not make any external connections, and your data stays securely on your locally hosted server.": "gör inga externa anslutningar, och dina data förblir säkra på din lokalt värdade server.",
"Don't Allow": "Tillåt inte",
"Don't have an account?": "Har du inte ett konto?",
"Don't like the style": "",
"Download": "",
"Download Database": "Ladda ner databas",
"Drop any files here to add to the conversation": "Släpp filer här för att lägga till i konversationen",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "t.ex. '30s', '10m'. Giltiga tidsenheter är 's', 'm', 'h'.",
"Edit": "",
"Edit Doc": "Redigera dokument",
"Edit User": "Redigera användare",
"Email": "E-post",
"Embedding Model Engine": "",
"Embedding model set to \"{{embedding_model}}\"": "",
"Enable Chat History": "Aktivera chatthistorik",
"Enable New Sign Ups": "Aktivera nya registreringar",
"Enabled": "Aktiverad",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Skriv {{role}} meddelande här",
"Enter Chunk Overlap": "Ange Chunk-överlappning",
"Enter Chunk Size": "Ange Chunk-storlek",
"Enter Image Size (e.g. 512x512)": "Ange bildstorlek (t.ex. 512x512)",
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Ange LiteLLM API-bas-URL (litellm_params.api_base)",
"Enter LiteLLM API Key (litellm_params.api_key)": "Ange LiteLLM API-nyckel (litellm_params.api_key)",
"Enter LiteLLM API RPM (litellm_params.rpm)": "Ange LiteLLM API RPM (litellm_params.rpm)",
"Enter LiteLLM Model (litellm_params.model)": "Ange LiteLLM-modell (litellm_params.model)",
"Enter Max Tokens (litellm_params.max_tokens)": "Ange max antal tokens (litellm_params.max_tokens)",
"Enter model tag (e.g. {{modelTag}})": "Ange modelltagg (t.ex. {{modelTag}})",
"Enter Number of Steps (e.g. 50)": "Ange antal steg (t.ex. 50)",
"Enter Score": "",
"Enter stop sequence": "Ange stoppsekvens",
"Enter Top K": "Ange Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "Ange URL (t.ex. http://127.0.0.1:7860/)",
"Enter Your Email": "Ange din e-post",
"Enter Your Full Name": "Ange ditt fullständiga namn",
"Enter Your Password": "Ange ditt lösenord",
"Enter Your Role": "",
"Experimental": "Experimentell",
"Export All Chats (All Users)": "Exportera alla chattar (alla användare)",
"Export Chats": "Exportera chattar",
"Export Documents Mapping": "Exportera dokumentmappning",
"Export Modelfiles": "Exportera modelfiler",
"Export Prompts": "Exportera prompts",
"Failed to create API Key.": "",
"Failed to read clipboard contents": "Misslyckades med att läsa urklippsinnehåll",
"Feel free to add specific details": "",
"File Mode": "Fil-läge",
"File not found.": "Fil hittades inte.",
"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingeravtrycksmanipulering upptäckt: Kan inte använda initialer som avatar. Återställning till standardprofilbild.",
"Fluidly stream large external response chunks": "Flytande ström stora externa svarsblock",
"Focus chat input": "Fokusera chattindata",
"Followed instructions perfectly": "",
"Format your variables using square brackets like this:": "Formatera dina variabler med hakparenteser så här:",
"From (Base Model)": "Från (basmodell)",
"Full Screen Mode": "Helskärmsläge",
"General": "Allmän",
"General Settings": "Allmänna inställningar",
"Generation Info": "",
"Good Response": "",
"has no conversations.": "",
"Hello, {{name}}": "Hej, {{name}}",
"Hide": "Dölj",
"Hide Additional Params": "Dölj ytterligare parametrar",
"How can I help you today?": "Hur kan jag hjälpa dig idag?",
"Hybrid Search": "",
"Image Generation (Experimental)": "Bildgenerering (experimentell)",
"Image Generation Engine": "Bildgenereringsmotor",
"Image Settings": "Bildinställningar",
"Images": "Bilder",
"Import Chats": "Importera chattar",
"Import Documents Mapping": "Importera dokumentmappning",
"Import Modelfiles": "Importera modelfiler",
"Import Prompts": "Importera prompts",
"Include `--api` flag when running stable-diffusion-webui": "Inkludera `--api`-flagga när du kör stabil-diffusion-webui",
"Interface": "Gränssnitt",
"join our Discord for help.": "gå med i vår Discord för hjälp.",
"JSON": "JSON",
"JWT Expiration": "JWT-utgång",
"JWT Token": "JWT-token",
"Keep Alive": "Håll vid liv",
"Keyboard shortcuts": "Tangentbordsgenvägar",
"Language": "Språk",
"Last Active": "",
"Light": "Ljus",
"Listening...": "Lyssnar...",
"LLMs can make mistakes. Verify important information.": "LLM:er kan göra misstag. Verifiera viktig information.",
"Made by OpenWebUI Community": "Skapad av OpenWebUI Community",
"Make sure to enclose them with": "Se till att bifoga dem med",
"Manage LiteLLM Models": "Hantera LiteLLM-modeller",
"Manage Models": "Hantera modeller",
"Manage Ollama Models": "Hantera Ollama-modeller",
"Max Tokens": "Max antal tokens",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Högst 3 modeller kan laddas ner samtidigt. Vänligen försök igen senare.",
"Minimum Score": "",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
"MMMM DD, YYYY": "MMMM DD, ÅÅÅÅ",
"MMMM DD, YYYY HH:mm": "",
"Model '{{modelName}}' has been successfully downloaded.": "Modellen '{{modelName}}' har laddats ner framgångsrikt.",
"Model '{{modelTag}}' is already in queue for downloading.": "Modellen '{{modelTag}}' är redan i kö för nedladdning.",
"Model {{modelId}} not found": "Modell {{modelId}} hittades inte",
"Model {{modelName}} already exists.": "Modellen {{modelName}} finns redan.",
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modellens filsystemväg upptäckt. Modellens kortnamn krävs för uppdatering, kan inte fortsätta.",
"Model Name": "Modellnamn",
"Model not selected": "Modell inte vald",
"Model Tag Name": "Modelltaggnamn",
"Model Whitelisting": "Modellens vitlista",
"Model(s) Whitelisted": "Modell(er) vitlistade",
"Modelfile": "Modelfil",
"Modelfile Advanced Settings": "Modelfilens avancerade inställningar",
"Modelfile Content": "Modelfilens innehåll",
"Modelfiles": "Modelfiler",
"Models": "Modeller",
"More": "",
"My Documents": "Mina dokument",
"My Modelfiles": "Mina modelfiler",
"My Prompts": "Mina promptar",
"Name": "Namn",
"Name Tag": "Namntag",
"Name your modelfile": "Namnge din modelfil",
"New Chat": "Ny chatt",
"New Password": "Nytt lösenord",
"Not factually correct": "",
"Not sure what to add?": "Inte säker på vad du ska lägga till?",
"Not sure what to write? Switch to": "Inte säker på vad du ska skriva? Växla till",
"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "",
"Notifications": "Notifikationer",
"Off": "Av",
"Okay, Let's Go!": "Okej, nu kör vi!",
"OLED Dark": "",
"Ollama": "",
"Ollama Base URL": "Ollama bas-URL",
"Ollama Version": "Ollama-version",
"On": "På",
"Only": "Endast",
"Only alphanumeric characters and hyphens are allowed in the command string.": "Endast alfanumeriska tecken och bindestreck är tillåtna i kommandosträngen.",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "Hoppsan! Håll i dig! Dina filer är fortfarande i bearbetningsugnen. Vi lagar dem till perfektion. Var tålmodig så meddelar vi dig när de är redo.",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Hoppsan! Det ser ut som om URL:en är ogiltig. Dubbelkolla gärna och försök igen.",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Hoppsan! Du använder en ej stödd metod (endast frontend). Vänligen servera WebUI från backend.",
"Open": "Öppna",
"Open AI": "Öppna AI",
"Open AI (Dall-E)": "Öppna AI (Dall-E)",
"Open new chat": "Öppna ny chatt",
"OpenAI": "",
"OpenAI API": "OpenAI API",
"OpenAI API Config": "",
"OpenAI API Key is required.": "OpenAI API-nyckel krävs.",
"OpenAI URL/Key required.": "",
"or": "eller",
"Other": "",
"Parameters": "Parametrar",
"Password": "Lösenord",
"PDF document (.pdf)": "",
"PDF Extract Images (OCR)": "PDF Extrahera bilder (OCR)",
"pending": "väntande",
"Permission denied when accessing microphone: {{error}}": "Tillstånd nekades vid åtkomst till mikrofon: {{error}}",
"Plain text (.txt)": "",
"Playground": "Lekplats",
"Positive attitude": "",
"Profile Image": "",
"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "",
"Prompt Content": "Promptinnehåll",
"Prompt suggestions": "Förslag",
"Prompts": "Prompts",
"Pull a model from Ollama.com": "Dra en modell från Ollama.com",
"Pull Progress": "Dra framsteg",
"Query Params": "Frågeparametrar",
"RAG Template": "RAG-mall",
"Raw Format": "Råformat",
"Read Aloud": "",
"Record voice": "Spela in röst",
"Redirecting you to OpenWebUI Community": "Omdirigerar dig till OpenWebUI Community",
"Refused when it shouldn't have": "",
"Regenerate": "",
"Release Notes": "Versionsinformation",
"Remove": "",
"Repeat Last N": "Upprepa senaste N",
"Repeat Penalty": "Upprepa straff",
"Request Mode": "Begär läge",
"Reranking model disabled": "",
"Reranking model set to \"{{reranking_model}}\"": "",
"Reset Vector Storage": "Återställ vektorlager",
"Response AutoCopy to Clipboard": "Svara AutoCopy till urklipp",
"Role": "Roll",
"Rosé Pine": "Rosé Pine",
"Rosé Pine Dawn": "Rosé Pine Dawn",
"Save": "Spara",
"Save & Create": "Spara och skapa",
"Save & Submit": "Spara och skicka",
"Save & Update": "Spara och uppdatera",
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Att spara chatloggar direkt till din webbläsares lagring stöds inte längre. Ta en stund och ladda ner och radera dina chattloggar genom att klicka på knappen nedan. Oroa dig inte, du kan enkelt importera dina chattloggar till backend genom",
"Scan": "Skanna",
"Scan complete!": "Skanning klar!",
"Scan for documents from {{path}}": "Skanna efter dokument från {{path}}",
"Search": "Sök",
"Search a model": "",
"Search Documents": "Sök dokument",
"Search Prompts": "Sök promptar",
"See readme.md for instructions": "Se readme.md för instruktioner",
"See what's new": "Se vad som är nytt",
"Seed": "Seed",
"Select a mode": "Välj ett läge",
"Select a model": "Välj en modell",
"Select an Ollama instance": "Välj en Ollama-instans",
"Send a Message": "Skicka ett meddelande",
"Send message": "Skicka meddelande",
"Server connection verified": "Serveranslutning verifierad",
"Set as default": "Ange som standard",
"Set Default Model": "Ange standardmodell",
"Set Image Size": "Ange bildstorlek",
"Set Steps": "Ange steg",
"Set Title Auto-Generation Model": "Ange modell för automatisk generering av titel",
"Set Voice": "Ange röst",
"Settings": "Inställningar",
"Settings saved successfully!": "Inställningar sparades framgångsrikt!",
"Share": "",
"Share Chat": "",
"Share to OpenWebUI Community": "Dela till OpenWebUI Community",
"short-summary": "kort sammanfattning",
"Show": "Visa",
"Show Additional Params": "Visa ytterligare parametrar",
"Show shortcuts": "Visa genvägar",
"Showcased creativity": "",
"sidebar": "sidofält",
"Sign in": "Logga in",
"Sign Out": "Logga ut",
"Sign up": "Registrera dig",
"Signing in": "",
"Speech recognition error: {{error}}": "Fel vid taligenkänning: {{error}}",
"Speech-to-Text Engine": "Tal-till-text-motor",
"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API stöds inte i denna webbläsare.",
"Stop Sequence": "Stoppsekvens",
"STT Settings": "STT-inställningar",
"Submit": "Skicka in",
"Subtitle (e.g. about the Roman Empire)": "",
"Success": "Framgång",
"Successfully updated.": "Uppdaterades framgångsrikt.",
"Sync All": "Synkronisera allt",
"System": "System",
"System Prompt": "Systemprompt",
"Tags": "Taggar",
"Tell us more:": "",
"Temperature": "Temperatur",
"Template": "Mall",
"Text Completion": "Textslutförande",
"Text-to-Speech Engine": "Text-till-tal-motor",
"Tfs Z": "Tfs Z",
"Thanks for your feedback!": "",
"The score should be a value between 0.0 (0%) and 1.0 (100%).": "",
"Theme": "Tema",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Detta säkerställer att dina värdefulla konversationer sparas säkert till din backend-databas. Tack!",
"This setting does not sync across browsers or devices.": "Denna inställning synkroniseras inte mellan webbläsare eller enheter.",
"Thorough explanation": "",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tips: Uppdatera flera variabelplatser efter varandra genom att trycka på tabb-tangenten i chattinmatningen efter varje ersättning.",
"Title": "Titel",
"Title (e.g. Tell me a fun fact)": "",
"Title Auto-Generation": "Automatisk generering av titel",
"Title Generation Prompt": "Titelgenereringsprompt",
"to": "till",
"To access the available model names for downloading,": "För att komma åt de tillgängliga modellnamnen för nedladdning,",
"To access the GGUF models available for downloading,": "För att komma åt de GGUF-modeller som finns tillgängliga för nedladdning,",
"to chat input.": "till chattinmatning.",
"Toggle settings": "Växla inställningar",
"Toggle sidebar": "Växla sidofält",
"Top K": "Topp K",
"Top P": "Topp P",
"Trouble accessing Ollama?": "Problem med att komma åt Ollama?",
"TTS Settings": "TTS-inställningar",
"Type Hugging Face Resolve (Download) URL": "Skriv Hugging Face Resolve (nedladdning) URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "Oj då! Det uppstod ett problem med att ansluta till {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Okänd filtyp '{{file_type}}', men accepterar och behandlar som vanlig text",
"Update and Copy Link": "",
"Update Embedding Model": "",
"Update embedding model (e.g. {{model}})": "",
"Update password": "Uppdatera lösenord",
"Update Reranking Model": "",
"Update reranking model (e.g. {{model}})": "",
"Upload a GGUF model": "Ladda upp en GGUF-modell",
"Upload files": "Ladda upp filer",
"Upload Progress": "Uppladdningsförlopp",
"URL Mode": "URL-läge",
"Use '#' in the prompt input to load and select your documents.": "Använd '#' i promptinmatningen för att ladda och välja dina dokument.",
"Use Gravatar": "Använd Gravatar",
"Use Initials": "Använd initialer",
"user": "användare",
"User Permissions": "Användarbehörigheter",
"Users": "Användare",
"Utilize": "Använd",
"Valid time units:": "Giltiga tidsenheter:",
"variable": "variabel",
"variable to have them replaced with clipboard content.": "variabel för att få dem ersatta med urklippsinnehåll.",
"Version": "Version",
"Warning: If you update or change your embedding model, you will need to re-import all documents.": "",
"Web": "Webb",
"Webhook URL": "",
"WebUI Add-ons": "WebUI-tillägg",
"WebUI Settings": "WebUI-inställningar",
"WebUI will make requests to": "WebUI kommer att skicka förfrågningar till",
"Whats New in": "Vad är nytt i",
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "När historiken är avstängd visas inte nya chattar i denna webbläsare i din historik på någon av dina enheter.",
"Whisper (Local)": "Whisper (lokal)",
"Write a prompt suggestion (e.g. Who are you?)": "Skriv ett förslag (t.ex. Vem är du?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "Skriv en sammanfattning på 50 ord som sammanfattar [ämne eller nyckelord].",
"You": "Du",
"You're a helpful assistant.": "Du är en hjälpsam assistent.",
"You're now logged in.": "Du är nu inloggad.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Mesaj ekle",
"Add Model": "",
"Add Tags": "etiketler ekle",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Bu ayarları ayarlamak değişiklikleri tüm kullanıcılara evrensel olarak uygular.",
"admin": "yönetici",
"Admin Panel": "Yönetici Paneli",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Diğer model dosyalarını kontrol etmek için buraya tıklayın.",
"Click here to select": "Seçmek için buraya tıklayın",
"Click here to select a csv file.": "",
"Click here to select documents.": "Belgeleri seçmek için buraya tıklayın.",
"click here.": "buraya tıklayın.",
"Click on the user role button to change a user's role.": "Bir kullanıcının rolünü değiştirmek için kullanıcı rolü düğmesine tıklayın.",
@ -154,6 +156,7 @@
"Enable Chat History": "Sohbet Geçmişini Etkinleştir",
"Enable New Sign Ups": "Yeni Kayıtları Etkinleştir",
"Enabled": "Etkin",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Buraya {{role}} mesajını girin",
"Enter Chunk Overlap": "Chunk Örtüşmesini Girin",
"Enter Chunk Size": "Chunk Boyutunu Girin",
@ -173,6 +176,7 @@
"Enter Your Email": "E-postanızı Girin",
"Enter Your Full Name": "Tam Adınızı Girin",
"Enter Your Password": "Parolanızı Girin",
"Enter Your Role": "",
"Experimental": "Deneysel",
"Export All Chats (All Users)": "Tüm Sohbetleri Dışa Aktar (Tüm Kullanıcılar)",
"Export Chats": "Sohbetleri Dışa Aktar",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Sen yardımcı bir asistansın.",
"You're now logged in.": "Şimdi oturum açtınız."
"You're now logged in.": "Şimdi oturum açtınız.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Додати повідомлення",
"Add Model": "",
"Add Tags": "додати теги",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Зміни в цих налаштуваннях будуть застосовані для всіх користувачів.",
"admin": "адмін",
"Admin Panel": "Панель адміністратора",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Клацніть тут, щоб перевірити інші файли моделей.",
"Click here to select": "Натисніть тут, щоб вибрати",
"Click here to select a csv file.": "",
"Click here to select documents.": "Натисніть тут, щоб вибрати документи.",
"click here.": "клацніть тут.",
"Click on the user role button to change a user's role.": "Натисніть кнопку ролі користувача, щоб змінити роль користувача.",
@ -154,6 +156,7 @@
"Enable Chat History": "Увімкнути історію чату",
"Enable New Sign Ups": "Дозволити нові реєстрації",
"Enabled": "Увімкнено",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Введіть повідомлення {{role}} тут",
"Enter Chunk Overlap": "Введіть перекриття фрагменту",
"Enter Chunk Size": "Введіть розмір фрагменту",
@ -173,6 +176,7 @@
"Enter Your Email": "Введіть вашу електронну пошту",
"Enter Your Full Name": "Введіть ваше ім'я",
"Enter Your Password": "Введіть ваш пароль",
"Enter Your Role": "",
"Experimental": "Експериментальне",
"Export All Chats (All Users)": "Експортувати всі чати (всі користувачі)",
"Export Chats": "Експортувати чати",
@ -229,7 +233,10 @@
"Manage Ollama Models": "Керування моделями Ollama",
"Max Tokens": "Максимальна кількість токенів",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимум 3 моделі можна завантажити одночасно. Будь ласка, спробуйте пізніше.",
<<<<<<< HEAD
"Messages you send after creating your link won't be shared. Users with the URL will beable to view the shared chat.": "",
=======
>>>>>>> origin-dev
"Minimum Score": "",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
@ -450,5 +457,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Ви корисний асистент.",
"You're now logged in.": "Ви увійшли в систему."
"You're now logged in.": "Ви увійшли в систему.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "Thêm tin nhắn",
"Add Model": "",
"Add Tags": "thêm thẻ",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "Các thay đổi cài đặt này sẽ áp dụng cho tất cả người sử dụng.",
"admin": "quản trị viên",
"Admin Panel": "Trang Quản trị",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "Bấm vào đây để kiểm tra các tệp mô tả mô hình (modelfiles) khác.",
"Click here to select": "Bấm vào đây để chọn",
"Click here to select a csv file.": "",
"Click here to select documents.": "Bấm vào đây để chọn tài liệu.",
"click here.": "bấm vào đây.",
"Click on the user role button to change a user's role.": "Bấm vào nút trong cột VAI TRÒ để thay đổi quyền của người sử dụng.",
@ -154,6 +156,7 @@
"Enable Chat History": "Bật Lịch sử chat",
"Enable New Sign Ups": "Cho phép đăng ký mới",
"Enabled": "Đã bật",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "Nhập yêu cầu của {{role}} ở đây",
"Enter Chunk Overlap": "Nhập Chunk chồng lấn (overlap)",
"Enter Chunk Size": "Nhập Kích thước Chunk",
@ -173,6 +176,7 @@
"Enter Your Email": "Nhập Email của bạn",
"Enter Your Full Name": "Nhập Họ và Tên của bạn",
"Enter Your Password": "Nhập Mật khẩu của bạn",
"Enter Your Role": "",
"Experimental": "Thử nghiệm",
"Export All Chats (All Users)": "Tải về tất cả nội dung chat (tất cả mọi người)",
"Export Chats": "Tải nội dung chat về máy",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "Bạn là một trợ lý hữu ích.",
"You're now logged in.": "Bạn đã đăng nhập."
"You're now logged in.": "Bạn đã đăng nhập.",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "添加消息",
"Add Model": "",
"Add Tags": "添加标签",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "调整这些设置将会对所有用户应用更改。",
"admin": "管理员",
"Admin Panel": "管理员面板",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "点击这里检查其他模型文件。",
"Click here to select": "点击这里选择",
"Click here to select a csv file.": "",
"Click here to select documents.": "点击这里选择文档。",
"click here.": "点击这里。",
"Click on the user role button to change a user's role.": "点击用户角色按钮以更改用户的角色。",
@ -154,6 +156,7 @@
"Enable Chat History": "启用聊天历史",
"Enable New Sign Ups": "启用新注册",
"Enabled": "启用",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "在此处输入 {{role}} 信息",
"Enter Chunk Overlap": "输入块重叠(Chunk Overlap)",
"Enter Chunk Size": "输入块大小(Chunk Size)",
@ -173,6 +176,7 @@
"Enter Your Email": "输入您的电子邮件",
"Enter Your Full Name": "输入您的全名",
"Enter Your Password": "输入您的密码",
"Enter Your Role": "",
"Experimental": "实验性",
"Export All Chats (All Users)": "导出所有聊天(所有用户)",
"Export Chats": "导出聊天",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "你是一个有帮助的助手。",
"You're now logged in.": "已登录。"
"You're now logged in.": "已登录。",
"Youtube": ""
}

View File

@ -20,6 +20,7 @@
"Add message": "新增訊息",
"Add Model": "",
"Add Tags": "新增標籤",
"Add User": "",
"Adjusting these settings will apply changes universally to all users.": "調整這些設定將對所有使用者進行更改。",
"admin": "管理員",
"Admin Panel": "管理員控制台",
@ -73,6 +74,7 @@
"Click here to": "",
"Click here to check other modelfiles.": "點擊這裡檢查其他 Modelfiles。",
"Click here to select": "點擊這裡選擇",
"Click here to select a csv file.": "",
"Click here to select documents.": "點擊這裡選擇文件。",
"click here.": "點擊這裡。",
"Click on the user role button to change a user's role.": "點擊使用者 Role 按鈕以更改使用者的 Role。",
@ -154,6 +156,7 @@
"Enable Chat History": "啟用聊天歷史",
"Enable New Sign Ups": "允許註冊新帳號",
"Enabled": "已啟用",
"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
"Enter {{role}} message here": "在這裡輸入 {{role}} 訊息",
"Enter Chunk Overlap": "輸入 Chunk Overlap",
"Enter Chunk Size": "輸入 Chunk 大小",
@ -173,6 +176,7 @@
"Enter Your Email": "輸入你的電子郵件",
"Enter Your Full Name": "輸入你的全名",
"Enter Your Password": "輸入你的密碼",
"Enter Your Role": "",
"Experimental": "實驗功能",
"Export All Chats (All Users)": "匯出所有聊天紀錄(所有使用者)",
"Export Chats": "匯出聊天紀錄",
@ -450,5 +454,6 @@
"You have no archived conversations.": "",
"You have shared this chat": "",
"You're a helpful assistant.": "你是一位善於協助他人的助手。",
"You're now logged in.": "已登入。"
"You're now logged in.": "已登入。",
"Youtube": ""
}

View File

@ -34,6 +34,8 @@ export const documents = writable([
]);
export const settings: Writable<Settings> = writable({});
export const showSidebar = writable(false);
export const showSettings = writable(false);
export const showChangelog = writable(false);
@ -78,6 +80,7 @@ type Settings = {
saveChatHistory?: boolean;
notificationEnabled?: boolean;
title?: TitleSettings;
splitLargeDeltas?: boolean;
system?: string;
requestFormat?: string;

View File

@ -154,7 +154,7 @@
if (isCtrlPressed && event.key === '.') {
event.preventDefault();
console.log('openSettings');
document.getElementById('open-settings-button')?.click();
showSettings.set(!$showSettings);
}
// Check if Ctrl + / is pressed
@ -192,6 +192,8 @@
</div>
<ShortcutsModal bind:show={showShortcuts} />
<SettingsModal bind:show={$showSettings} />
<ChangelogModal bind:show={$showChangelog} />
<div class="app relative">
<div
@ -292,8 +294,6 @@
{/if}
<Sidebar />
<SettingsModal bind:show={$showSettings} />
<ChangelogModal bind:show={$showChangelog} />
<slot />
{/if}
</div>

View File

@ -15,7 +15,8 @@
chatId,
config,
WEBUI_NAME,
tags as _tags
tags as _tags,
showSidebar
} from '$lib/stores';
import { copyToClipboard, splitStream } from '$lib/utils';
@ -50,7 +51,9 @@
let currentRequestId = null;
let showModelSelector = true;
let selectedModels = [''];
let atSelectedModel = '';
let selectedModelfile = null;
$: selectedModelfile =
@ -144,7 +147,8 @@
setTimeout(() => chatInput?.focus(), 0);
};
const scrollToBottom = () => {
const scrollToBottom = async () => {
await tick();
if (messagesContainerElement) {
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
}
@ -242,7 +246,8 @@
const _chatId = JSON.parse(JSON.stringify($chatId));
await Promise.all(
selectedModels.map(async (modelId) => {
(atSelectedModel !== '' ? [atSelectedModel.id] : selectedModels).map(async (modelId) => {
console.log('modelId', modelId);
const model = $models.filter((m) => m.id === modelId).at(0);
if (model) {
@ -536,7 +541,7 @@
console.log(docs);
console.log(model);
scrollToBottom();
const [res, controller] = await generateOpenAIChatCompletion(
localStorage.token,
@ -605,14 +610,8 @@
scrollToBottom();
if (res && res.ok) {
const reader = res.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(splitStream('\n'))
.getReader();
const textStream = await createOpenAITextStream(reader, $settings.splitLargeChunks);
console.log(textStream);
if (res && res.ok && res.body) {
const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks);
for await (const update of textStream) {
const { value, done } = update;
@ -844,7 +843,11 @@
</title>
</svelte:head>
<div class="h-screen max-h-[100dvh] w-full flex flex-col">
<div
class="min-h-screen max-h-screen {$showSidebar
? 'lg:max-w-[calc(100%-260px)]'
: ''} w-full max-w-full flex flex-col"
>
<Navbar
{title}
bind:selectedModels
@ -855,7 +858,7 @@
/>
<div class="flex flex-col flex-auto">
<div
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full"
id="messages-container"
bind:this={messagesContainerElement}
on:scroll={(e) => {
@ -873,22 +876,25 @@
bind:history
bind:messages
bind:autoScroll
bind:prompt
bottomPadding={files.length > 0}
suggestionPrompts={selectedModelfile?.suggestionPrompts ??
$config.default_prompt_suggestions}
{sendPrompt}
{continueGeneration}
{regenerateResponse}
/>
</div>
</div>
<MessageInput
bind:files
bind:prompt
bind:autoScroll
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
{messages}
{submitPrompt}
{stopResponse}
/>
</div>
</div>
<MessageInput
bind:files
bind:prompt
bind:autoScroll
bind:selectedModel={atSelectedModel}
{messages}
{submitPrompt}
{stopResponse}
/>

View File

@ -18,6 +18,7 @@
import ChatBubbles from '$lib/components/icons/ChatBubbles.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import UserChatsModal from '$lib/components/admin/UserChatsModal.svelte';
import AddUserModal from '$lib/components/admin/AddUserModal.svelte';
const i18n = getContext('i18n');
@ -30,6 +31,7 @@
let page = 1;
let showSettingsModal = false;
let showAddUserModal = false;
let showUserChatsModal = false;
let showEditUserModal = false;
@ -91,6 +93,12 @@
/>
{/key}
<AddUserModal
bind:show={showAddUserModal}
on:save={async () => {
users = await getUsers(localStorage.token);
}}
/>
<UserChatsModal bind:show={showUserChatsModal} user={selectedUser} />
<SettingsModal bind:show={showSettingsModal} />
@ -100,32 +108,34 @@
<div class=" mx-auto w-full">
<div class="w-full">
<div class=" flex flex-col justify-center">
<div class=" px-5 pt-3">
<div class=" px-6 pt-4">
<div class=" flex justify-between items-center">
<div class="flex items-center text-2xl font-semibold">{$i18n.t('Dashboard')}</div>
<div>
<button
class="flex items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 transition"
type="button"
on:click={() => {
showSettingsModal = !showSettingsModal;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
<Tooltip content={$i18n.t('Admin Settings')}>
<button
class="flex items-center space-x-1 p-2 md:px-3 md:py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 transition"
type="button"
on:click={() => {
showSettingsModal = !showSettingsModal;
}}
>
<path
fill-rule="evenodd"
d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
clip-rule="evenodd"
/>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
clip-rule="evenodd"
/>
</svg>
<div class=" text-xs">{$i18n.t('Admin Settings')}</div>
</button>
<div class="hidden md:inline text-xs">{$i18n.t('Admin Settings')}</div>
</button>
</Tooltip>
</div>
</div>
</div>
@ -137,8 +147,8 @@
<hr class=" mb-3 dark:border-gray-800" />
<div class="px-5">
<div class="mt-0.5 mb-3 flex justify-between">
<div class="px-6">
<div class="mt-0.5 mb-3 gap-1 flex flex-col md:flex-row justify-between">
<div class="flex text-lg font-medium px-0.5">
{$i18n.t('All Users')}
<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-200 dark:bg-gray-700" />
@ -147,12 +157,34 @@
>
</div>
<div class="">
<div class="flex gap-1">
<input
class=" w-60 rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full md:w-60 rounded-xl py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Search')}
bind:value={search}
/>
<div>
<Tooltip content="Add User">
<button
class=" px-2 py-2 rounded-xl border border-gray-200 dark:border-gray-600 dark:border-0 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition font-medium text-sm flex items-center space-x-1"
on:click={() => {
showAddUserModal = !showAddUserModal;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
</svg>
</button>
</Tooltip>
</div>
</div>
</div>
@ -235,66 +267,68 @@
<td class="px-3 py-2 text-right">
<div class="flex justify-end w-full">
<Tooltip content={$i18n.t('Chats')}>
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
showUserChatsModal = !showUserChatsModal;
selectedUser = user;
}}
>
<ChatBubbles />
</button>
</Tooltip>
<Tooltip content={$i18n.t('Edit User')}>
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
showEditUserModal = !showEditUserModal;
selectedUser = user;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
{#if user.role !== 'admin'}
<Tooltip content={$i18n.t('Chats')}>
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
showUserChatsModal = !showUserChatsModal;
selectedUser = user;
}}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
<ChatBubbles />
</button>
</Tooltip>
<Tooltip content={$i18n.t('Delete User')}>
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
deleteUserHandler(user.id);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
<Tooltip content={$i18n.t('Edit User')}>
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
showEditUserModal = !showEditUserModal;
selectedUser = user;
}}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
/>
</svg>
</button>
</Tooltip>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
/>
</svg>
</button>
</Tooltip>
<Tooltip content={$i18n.t('Delete User')}>
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
deleteUserHandler(user.id);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
/>
</svg>
</button>
</Tooltip>
{/if}
</div>
</td>
</tr>

View File

@ -5,7 +5,6 @@
import { onMount, tick, getContext } from 'svelte';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import {
models,
modelfiles,
@ -15,7 +14,8 @@
chatId,
config,
WEBUI_NAME,
tags as _tags
tags as _tags,
showSidebar
} from '$lib/stores';
import { copyToClipboard, splitStream, convertMessagesToHistory } from '$lib/utils';
@ -57,6 +57,8 @@
// let chatId = $page.params.id;
let showModelSelector = true;
let selectedModels = [''];
let atSelectedModel = '';
let selectedModelfile = null;
$: selectedModelfile =
@ -167,7 +169,8 @@
}
};
const scrollToBottom = () => {
const scrollToBottom = async () => {
await tick();
if (messagesContainerElement) {
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
}
@ -256,7 +259,7 @@
const _chatId = JSON.parse(JSON.stringify($chatId));
await Promise.all(
selectedModels.map(async (modelId) => {
(atSelectedModel !== '' ? [atSelectedModel.id] : selectedModels).map(async (modelId) => {
const model = $models.filter((m) => m.id === modelId).at(0);
if (model) {
@ -550,6 +553,8 @@
console.log(docs);
scrollToBottom();
const [res, controller] = await generateOpenAIChatCompletion(
localStorage.token,
{
@ -617,14 +622,8 @@
scrollToBottom();
if (res && res.ok) {
const reader = res.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(splitStream('\n'))
.getReader();
const textStream = await createOpenAITextStream(reader, $settings.splitLargeChunks);
console.log(textStream);
if (res && res.ok && res.body) {
const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks);
for await (const update of textStream) {
const { value, done } = update;
@ -863,7 +862,11 @@
</svelte:head>
{#if loaded}
<div class="min-h-screen max-h-screen w-full flex flex-col">
<div
class="min-h-screen max-h-screen {$showSidebar
? 'lg:max-w-[calc(100%-260px)]'
: ''} w-full max-w-full flex flex-col"
>
<Navbar
{title}
{chat}
@ -881,7 +884,7 @@
/>
<div class="flex flex-col flex-auto">
<div
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full"
id="messages-container"
bind:this={messagesContainerElement}
on:scroll={(e) => {
@ -906,17 +909,17 @@
/>
</div>
</div>
<MessageInput
bind:files
bind:prompt
bind:autoScroll
suggestionPrompts={selectedModelfile?.suggestionPrompts ??
$config.default_prompt_suggestions}
{messages}
{submitPrompt}
{stopResponse}
/>
</div>
</div>
<MessageInput
bind:files
bind:prompt
bind:autoScroll
bind:selectedModel={atSelectedModel}
suggestionPrompts={selectedModelfile?.suggestionPrompts ?? $config.default_prompt_suggestions}
{messages}
{submitPrompt}
{stopResponse}
/>
{/if}

View File

@ -316,7 +316,7 @@
</div>
</div>
<div class="flex flex-col gap-1 px-1 w-full">
<div class="flex flex-col gap-1 w-full">
<div class="flex w-full">
<div class="overflow-hidden w-full">
<div class="max-w-full">
@ -330,6 +330,7 @@
info: model
}))}
bind:value={selectedModelId}
className="max-w-2xl"
/>
</div>
</div>

View File

@ -152,10 +152,7 @@
<hr class=" dark:border-gray-800 mt-6 mb-2" />
</div>
<div
class=" flex flex-col justify-center w-full flex-auto overflow-auto h-0"
id="messages-container"
>
<div class=" flex flex-col w-full flex-auto overflow-auto h-0" id="messages-container">
<div class=" h-full w-full flex flex-col py-4">
<div class="py-2">
<Messages