mirror of
https://github.com/open-webui/open-webui.git
synced 2025-04-02 08:59:16 +02:00
commit
506dc0149c
25
.github/workflows/codespell.yml
vendored
Normal file
25
.github/workflows/codespell.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Codespell configuration is within pyproject.toml
|
||||
---
|
||||
name: Codespell
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
name: Check for spelling errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Annotate locations with typos
|
||||
uses: codespell-project/codespell-problem-matcher@v1
|
||||
- name: Codespell
|
||||
uses: codespell-project/actions-codespell@v2
|
27
CHANGELOG.md
27
CHANGELOG.md
@ -5,6 +5,33 @@ 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.5.4] - 2024-01-05
|
||||
|
||||
### Added
|
||||
|
||||
- **🔄 Clone Shared Chats**: Effortlessly clone shared chats to save time and streamline collaboration, perfect for reusing insightful discussions or custom setups.
|
||||
- **📣 Native Notifications for Channel Messages**: Stay informed with integrated desktop notifications for channel messages, ensuring you never miss important updates while multitasking.
|
||||
- **🔥 Torch MPS Support**: MPS support for Mac users when Open WebUI is installed directly, offering better performance and compatibility for AI workloads.
|
||||
- **🌍 Enhanced Translations**: Small improvements to various translations, ensuring a smoother global user experience.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **🖼️ Image-Only Messages in Channels**: You can now send images without accompanying text or content in channels.
|
||||
- **❌ Proper Exception Handling**: Enhanced error feedback by ensuring exceptions are raised clearly, reducing confusion and promoting smoother debugging.
|
||||
- **🔍 RAG Query Generation Restored**: Fixed query generation issues for Retrieval-Augmented Generation, improving retrieval accuracy and ensuring seamless functionality.
|
||||
- **📩 MOA Response Functionality Fixed**: Addressed an error with the MOA response generation feature.
|
||||
- **💬 Channel Thread Loading with 50+ Messages**: Resolved an issue where channel threads stalled when exceeding 50 messages, ensuring smooth navigation in active discussions.
|
||||
- **🔑 API Endpoint Restrictions Resolution**: Fixed a critical bug where the 'API_KEY_ALLOWED_ENDPOINTS' setting was not functioning as intended, ensuring API access is limited to specified endpoints for enhanced security.
|
||||
- **🛠️ Action Functions Restored**: Corrected an issue preventing action functions from working, restoring their utility for customized automations and workflows.
|
||||
- **📂 Temporary Chat JSON Export Fix**: Resolved a bug blocking temporary chats from being exported in JSON format, ensuring seamless data portability.
|
||||
|
||||
### Changed
|
||||
|
||||
- **🎛️ Sidebar UI Tweaks**: Chat folders, including pinned folders, now display below the Chats section for better organization; the "New Folder" button has been relocated to the Chats section for a more intuitive workflow.
|
||||
- **🏗️ Real-Time Save Disabled by Default**: The 'ENABLE_REALTIME_CHAT_SAVE' setting is now off by default, boosting response speed for users who prioritize performance in high-paced workflows or less critical scenarios.
|
||||
- **🎤 Audio Input Echo Cancellation**: Audio input now features echo cancellation enabled by default, reducing audio feedback for improved clarity during conversations or voice-based interactions.
|
||||
- **🔧 General Reliability Improvements**: Numerous under-the-hood enhancements have been made to improve platform stability, boost overall performance, and ensure a more seamless, dependable experience across workflows.
|
||||
|
||||
## [0.5.3] - 2024-12-31
|
||||
|
||||
### Added
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## Our Pledge
|
||||
|
||||
As members, contributors, and leaders of this community, we pledge to make participation in our open-source project a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
As members, contributors, and leaders of this community, we pledge to make participation in our open-source project a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
We are committed to creating and maintaining an open, respectful, and professional environment where positive contributions and meaningful discussions can flourish. By participating in this project, you agree to uphold these values and align your behavior to the standards outlined in this Code of Conduct.
|
||||
|
||||
|
@ -11,7 +11,9 @@
|
||||
[](https://discord.gg/5rJgQTnV4s)
|
||||
[](https://github.com/sponsors/tjbck)
|
||||
|
||||
Open WebUI is an [extensible](https://github.com/open-webui/pipelines), feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. For more information, be sure to check out our [Open WebUI Documentation](https://docs.openwebui.com/).
|
||||
**Open WebUI is an [extensible](https://docs.openwebui.com/features/plugin/), feature-rich, and user-friendly self-hosted AI platform designed to operate entirely offline.** It supports various LLM runners like **Ollama** and **OpenAI-compatible APIs**, with **built-in inference engine** for RAG, making it a **powerful AI deployment solution**.
|
||||
|
||||
For more information, be sure to check out our [Open WebUI Documentation](https://docs.openwebui.com/).
|
||||
|
||||

|
||||
|
||||
|
@ -1211,6 +1211,9 @@ if VECTOR_DB == "pgvector" and not PGVECTOR_DB_URL.startswith("postgres"):
|
||||
raise ValueError(
|
||||
"Pgvector requires setting PGVECTOR_DB_URL or using Postgres with vector extension as the primary database."
|
||||
)
|
||||
PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH = int(
|
||||
os.environ.get("PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH", "1536")
|
||||
)
|
||||
|
||||
####################################
|
||||
# Information Retrieval (RAG)
|
||||
|
@ -53,6 +53,11 @@ if USE_CUDA.lower() == "true":
|
||||
else:
|
||||
DEVICE_TYPE = "cpu"
|
||||
|
||||
try:
|
||||
if torch.backends.mps.is_available() and torch.backends.mps.is_built():
|
||||
DEVICE_TYPE = "mps"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
####################################
|
||||
# LOGGING
|
||||
@ -313,7 +318,7 @@ RESET_CONFIG_ON_START = (
|
||||
|
||||
|
||||
ENABLE_REALTIME_CHAT_SAVE = (
|
||||
os.environ.get("ENABLE_REALTIME_CHAT_SAVE", "True").lower() == "true"
|
||||
os.environ.get("ENABLE_REALTIME_CHAT_SAVE", "False").lower() == "true"
|
||||
)
|
||||
|
||||
####################################
|
||||
|
@ -469,6 +469,8 @@ class ChatTable:
|
||||
def get_chat_by_share_id(self, id: str) -> Optional[ChatModel]:
|
||||
try:
|
||||
with get_db() as db:
|
||||
# it is possible that the shared link was deleted. hence,
|
||||
# we check if the chat is still shared by checkng if a chat with the share_id exists
|
||||
chat = db.query(Chat).filter_by(share_id=id).first()
|
||||
|
||||
if chat:
|
||||
|
@ -189,9 +189,11 @@ class MessageTable:
|
||||
.all()
|
||||
)
|
||||
|
||||
return [
|
||||
MessageModel.model_validate(message) for message in all_messages
|
||||
] + [MessageModel.model_validate(message)]
|
||||
# If length of all_messages is less than limit, then add the parent message
|
||||
if len(all_messages) < limit:
|
||||
all_messages.append(message)
|
||||
|
||||
return [MessageModel.model_validate(message) for message in all_messages]
|
||||
|
||||
def update_message_by_id(
|
||||
self, id: str, form_data: MessageForm
|
||||
|
@ -5,6 +5,7 @@ from sqlalchemy import (
|
||||
create_engine,
|
||||
Column,
|
||||
Integer,
|
||||
MetaData,
|
||||
select,
|
||||
text,
|
||||
Text,
|
||||
@ -19,9 +20,9 @@ from pgvector.sqlalchemy import Vector
|
||||
from sqlalchemy.ext.mutable import MutableDict
|
||||
|
||||
from open_webui.retrieval.vector.main import VectorItem, SearchResult, GetResult
|
||||
from open_webui.config import PGVECTOR_DB_URL
|
||||
from open_webui.config import PGVECTOR_DB_URL, PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH
|
||||
|
||||
VECTOR_LENGTH = 1536
|
||||
VECTOR_LENGTH = PGVECTOR_INITIALIZE_MAX_VECTOR_LENGTH
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
@ -56,6 +57,9 @@ class PgvectorClient:
|
||||
# Ensure the pgvector extension is available
|
||||
self.session.execute(text("CREATE EXTENSION IF NOT EXISTS vector;"))
|
||||
|
||||
# Check vector length consistency
|
||||
self.check_vector_length()
|
||||
|
||||
# Create the tables if they do not exist
|
||||
# Base.metadata.create_all requires a bind (engine or connection)
|
||||
# Get the connection from the session
|
||||
@ -82,6 +86,38 @@ class PgvectorClient:
|
||||
print(f"Error during initialization: {e}")
|
||||
raise
|
||||
|
||||
def check_vector_length(self) -> None:
|
||||
"""
|
||||
Check if the VECTOR_LENGTH matches the existing vector column dimension in the database.
|
||||
Raises an exception if there is a mismatch.
|
||||
"""
|
||||
metadata = MetaData()
|
||||
metadata.reflect(bind=self.session.bind, only=["document_chunk"])
|
||||
|
||||
if "document_chunk" in metadata.tables:
|
||||
document_chunk_table = metadata.tables["document_chunk"]
|
||||
if "vector" in document_chunk_table.columns:
|
||||
vector_column = document_chunk_table.columns["vector"]
|
||||
vector_type = vector_column.type
|
||||
if isinstance(vector_type, Vector):
|
||||
db_vector_length = vector_type.dim
|
||||
if db_vector_length != VECTOR_LENGTH:
|
||||
raise Exception(
|
||||
f"VECTOR_LENGTH {VECTOR_LENGTH} does not match existing vector column dimension {db_vector_length}. "
|
||||
"Cannot change vector size after initialization without migrating the data."
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
"The 'vector' column exists but is not of type 'Vector'."
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
"The 'vector' column does not exist in the 'document_chunk' table."
|
||||
)
|
||||
else:
|
||||
# Table does not exist yet; no action needed
|
||||
pass
|
||||
|
||||
def adjust_vector_length(self, vector: List[float]) -> List[float]:
|
||||
# Adjust vector to have length VECTOR_LENGTH
|
||||
current_length = len(vector)
|
||||
|
@ -683,7 +683,7 @@
|
||||
"age": "October 29, 2022",
|
||||
"extra_snippets": [
|
||||
"You can pass many options to the configure script; run ./configure --help to find out more. On macOS case-insensitive file systems and on Cygwin, the executable is called python.exe; elsewhere it's just python.",
|
||||
"Building a complete Python installation requires the use of various additional third-party libraries, depending on your build platform and configure options. Not all standard library modules are buildable or useable on all platforms. Refer to the Install dependencies section of the Developer Guide for current detailed information on dependencies for various Linux distributions and macOS.",
|
||||
"Building a complete Python installation requires the use of various additional third-party libraries, depending on your build platform and configure options. Not all standard library modules are buildable or usable on all platforms. Refer to the Install dependencies section of the Developer Guide for current detailed information on dependencies for various Linux distributions and macOS.",
|
||||
"To get an optimized build of Python, configure --enable-optimizations before you run make. This sets the default make targets up to enable Profile Guided Optimization (PGO) and may be used to auto-enable Link Time Optimization (LTO) on some platforms. For more details, see the sections below.",
|
||||
"Copyright © 2001-2024 Python Software Foundation. All rights reserved."
|
||||
]
|
||||
|
@ -463,6 +463,30 @@ async def clone_chat_by_id(id: str, user=Depends(get_verified_user)):
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# CloneSharedChatById
|
||||
############################
|
||||
|
||||
|
||||
@router.post("/{id}/clone/shared", response_model=Optional[ChatResponse])
|
||||
async def clone_shared_chat_by_id(id: str, user=Depends(get_verified_user)):
|
||||
chat = Chats.get_chat_by_share_id(id)
|
||||
if chat:
|
||||
updated_chat = {
|
||||
**chat.chat,
|
||||
"originalChatId": chat.id,
|
||||
"branchPointMessageId": chat.chat["history"]["currentId"],
|
||||
"title": f"Clone of {chat.title}",
|
||||
}
|
||||
|
||||
chat = Chats.insert_new_chat(user.id, ChatForm(**{"chat": updated_chat}))
|
||||
return ChatResponse(**chat.model_dump())
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# ArchiveChat
|
||||
############################
|
||||
|
@ -499,8 +499,8 @@ async def generate_moa_response(
|
||||
"model": task_model_id,
|
||||
"messages": [{"role": "user", "content": content}],
|
||||
"stream": form_data.get("stream", False),
|
||||
"chat_id": form_data.get("chat_id", None),
|
||||
"metadata": {
|
||||
"chat_id": form_data.get("chat_id", None),
|
||||
"task": str(TASKS.MOA_RESPONSE_GENERATION),
|
||||
"task_body": form_data,
|
||||
},
|
||||
|
@ -99,9 +99,9 @@ def get_current_user(
|
||||
if request.app.state.config.ENABLE_API_KEY_ENDPOINT_RESTRICTIONS:
|
||||
allowed_paths = [
|
||||
path.strip()
|
||||
for path in str(request.app.state.config.API_KEY_ALLOWED_PATHS).split(
|
||||
","
|
||||
)
|
||||
for path in str(
|
||||
request.app.state.config.API_KEY_ALLOWED_ENDPOINTS
|
||||
).split(",")
|
||||
]
|
||||
|
||||
if request.url.path not in allowed_paths:
|
||||
|
@ -315,6 +315,7 @@ async def chat_action(request: Request, action_id: str, form_data: dict, user: A
|
||||
"chat_id": data["chat_id"],
|
||||
"message_id": data["id"],
|
||||
"session_id": data["session_id"],
|
||||
"user_id": user.id,
|
||||
}
|
||||
)
|
||||
__event_call__ = get_event_call(
|
||||
@ -322,6 +323,7 @@ async def chat_action(request: Request, action_id: str, form_data: dict, user: A
|
||||
"chat_id": data["chat_id"],
|
||||
"message_id": data["id"],
|
||||
"session_id": data["session_id"],
|
||||
"user_id": user.id,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -494,6 +494,7 @@ async def chat_completion_files_handler(
|
||||
if files := body.get("metadata", {}).get("files", None):
|
||||
try:
|
||||
queries_response = await generate_queries(
|
||||
request,
|
||||
{
|
||||
"model": body["model"],
|
||||
"messages": body["messages"],
|
||||
@ -644,7 +645,7 @@ async def process_chat_payload(request, form_data, metadata, user, model):
|
||||
request, form_data, model, extra_params
|
||||
)
|
||||
except Exception as e:
|
||||
return Exception(f"Error: {e}")
|
||||
raise Exception(f"Error: {e}")
|
||||
|
||||
tool_ids = form_data.pop("tool_ids", None)
|
||||
files = form_data.pop("files", None)
|
||||
|
@ -3,7 +3,7 @@ uvicorn[standard]==0.30.6
|
||||
pydantic==2.9.2
|
||||
python-multipart==0.0.18
|
||||
|
||||
Flask==3.0.3
|
||||
Flask==3.1.0
|
||||
Flask-Cors==5.0.0
|
||||
|
||||
python-socketio==5.11.3
|
||||
@ -18,7 +18,7 @@ aiofiles
|
||||
|
||||
sqlalchemy==2.0.32
|
||||
alembic==1.14.0
|
||||
peewee==3.17.6
|
||||
peewee==3.17.8
|
||||
peewee-migrate==1.12.2
|
||||
psycopg2-binary==2.9.9
|
||||
pgvector==0.3.5
|
||||
@ -55,7 +55,7 @@ einops==0.8.0
|
||||
|
||||
ftfy==6.2.3
|
||||
pypdf==4.3.1
|
||||
fpdf2==2.7.9
|
||||
fpdf2==2.8.2
|
||||
pymdown-extensions==10.11.2
|
||||
docx2txt==0.8
|
||||
python-pptx==1.0.0
|
||||
@ -67,7 +67,7 @@ pandas==2.2.3
|
||||
openpyxl==3.1.5
|
||||
pyxlsb==1.0.10
|
||||
xlrd==2.0.1
|
||||
validators==0.33.0
|
||||
validators==0.34.0
|
||||
psutil
|
||||
sentencepiece
|
||||
soundfile==0.12.1
|
||||
@ -78,7 +78,7 @@ rank-bm25==0.2.2
|
||||
|
||||
faster-whisper==1.0.3
|
||||
|
||||
PyJWT[crypto]==2.9.0
|
||||
PyJWT[crypto]==2.10.1
|
||||
authlib==1.3.2
|
||||
|
||||
black==24.8.0
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "open-webui",
|
||||
"version": "0.5.3",
|
||||
"version": "0.5.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "open-webui",
|
||||
"version": "0.5.3",
|
||||
"version": "0.5.4",
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "open-webui",
|
||||
"version": "0.5.3",
|
||||
"version": "0.5.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run pyodide:fetch && vite dev --host",
|
||||
|
@ -11,7 +11,7 @@ dependencies = [
|
||||
"pydantic==2.9.2",
|
||||
"python-multipart==0.0.18",
|
||||
|
||||
"Flask==3.0.3",
|
||||
"Flask==3.1.0",
|
||||
"Flask-Cors==5.0.0",
|
||||
|
||||
"python-socketio==5.11.3",
|
||||
@ -26,7 +26,7 @@ dependencies = [
|
||||
|
||||
"sqlalchemy==2.0.32",
|
||||
"alembic==1.14.0",
|
||||
"peewee==3.17.6",
|
||||
"peewee==3.17.8",
|
||||
"peewee-migrate==1.12.2",
|
||||
"psycopg2-binary==2.9.9",
|
||||
"pgvector==0.3.5",
|
||||
@ -61,7 +61,7 @@ dependencies = [
|
||||
|
||||
"ftfy==6.2.3",
|
||||
"pypdf==4.3.1",
|
||||
"fpdf2==2.7.9",
|
||||
"fpdf2==2.8.2",
|
||||
"pymdown-extensions==10.11.2",
|
||||
"docx2txt==0.8",
|
||||
"python-pptx==1.0.0",
|
||||
@ -73,7 +73,7 @@ dependencies = [
|
||||
"openpyxl==3.1.5",
|
||||
"pyxlsb==1.0.10",
|
||||
"xlrd==2.0.1",
|
||||
"validators==0.33.0",
|
||||
"validators==0.34.0",
|
||||
"psutil",
|
||||
"sentencepiece",
|
||||
"soundfile==0.12.1",
|
||||
@ -84,7 +84,7 @@ dependencies = [
|
||||
|
||||
"faster-whisper==1.0.3",
|
||||
|
||||
"PyJWT[crypto]==2.9.0",
|
||||
"PyJWT[crypto]==2.10.1",
|
||||
"authlib==1.3.2",
|
||||
|
||||
"black==24.8.0",
|
||||
@ -151,3 +151,10 @@ exclude = [
|
||||
"chroma.sqlite3",
|
||||
]
|
||||
force-include = { "CHANGELOG.md" = "open_webui/CHANGELOG.md", build = "open_webui/frontend" }
|
||||
|
||||
[tool.codespell]
|
||||
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
|
||||
skip = '.git*,*.svg,package-lock.json,i18n,*.lock,*.css,*-bundle.js,locales,example-doc.txt,emoji-shortcodes.json'
|
||||
check-hidden = true
|
||||
# ignore-regex = ''
|
||||
ignore-words-list = 'ans'
|
||||
|
@ -618,6 +618,44 @@ export const cloneChatById = async (token: string, id: string) => {
|
||||
return res;
|
||||
};
|
||||
|
||||
export const cloneSharedChatById = async (token: string, id: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/clone/shared`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.then((json) => {
|
||||
return json;
|
||||
})
|
||||
.catch((err) => {
|
||||
error = err;
|
||||
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = err;
|
||||
}
|
||||
|
||||
console.log(err);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const shareChatById = async (token: string, id: string) => {
|
||||
let error = null;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { settings } from '$lib/stores';
|
||||
import { settings, playingNotificationSound, isLastActiveTab } from '$lib/stores';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
import { marked } from 'marked';
|
||||
@ -12,9 +12,20 @@
|
||||
export let content: string;
|
||||
|
||||
onMount(() => {
|
||||
if (!navigator.userActivation.hasBeenActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($settings?.notificationSound ?? true) {
|
||||
const audio = new Audio(`/audio/notification.mp3`);
|
||||
audio.play();
|
||||
if (!$playingNotificationSound && $isLastActiveTab) {
|
||||
playingNotificationSound.set(true);
|
||||
|
||||
const audio = new Audio(`/audio/notification.mp3`);
|
||||
audio.play().finally(() => {
|
||||
// Ensure the global state is reset after the sound finishes
|
||||
playingNotificationSound.set(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -128,7 +128,7 @@
|
||||
await toggleModelById(localStorage.token, model.id);
|
||||
}
|
||||
|
||||
await init();
|
||||
// await init();
|
||||
_models.set(await getModels(localStorage.token));
|
||||
};
|
||||
|
||||
|
@ -136,7 +136,7 @@
|
||||
};
|
||||
|
||||
const submitHandler = async ({ content, data }) => {
|
||||
if (!content) {
|
||||
if (!content && (data?.files ?? []).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -243,7 +243,7 @@
|
||||
};
|
||||
|
||||
const submitHandler = async () => {
|
||||
if (content === '') {
|
||||
if (content === '' && files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -581,11 +581,11 @@
|
||||
<Tooltip content={$i18n.t('Send message')}>
|
||||
<button
|
||||
id="send-message-button"
|
||||
class="{content !== ''
|
||||
class="{content !== '' || files.length !== 0
|
||||
? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
|
||||
: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 self-center"
|
||||
type="submit"
|
||||
disabled={content === ''}
|
||||
disabled={content === '' && files.length === 0}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
@ -178,7 +178,7 @@
|
||||
<div class="flex-auto w-0 pl-1">
|
||||
{#if showUserProfile}
|
||||
<Name>
|
||||
<div class=" self-end text-base font-medium">
|
||||
<div class=" self-end text-base shrink-0 font-medium truncate">
|
||||
{message?.user?.name}
|
||||
</div>
|
||||
|
||||
@ -189,7 +189,7 @@
|
||||
<Tooltip
|
||||
content={dayjs(message.created_at / 1000000).format('dddd, DD MMMM YYYY HH:mm')}
|
||||
>
|
||||
{formatDate(message.created_at / 1000000)}
|
||||
<span class="line-clamp-1">{formatDate(message.created_at / 1000000)}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -61,13 +61,13 @@
|
||||
);
|
||||
}
|
||||
});
|
||||
// Group emojis into rows of 6
|
||||
// Group emojis into rows of 8
|
||||
emojiRows = [];
|
||||
let currentRow = [];
|
||||
flattenedEmojis.forEach((item) => {
|
||||
if (item.type === 'emoji') {
|
||||
currentRow.push(item);
|
||||
if (currentRow.length === 7) {
|
||||
if (currentRow.length === 8) {
|
||||
emojiRows.push(currentRow);
|
||||
currentRow = [];
|
||||
}
|
||||
@ -126,7 +126,7 @@
|
||||
{#if emojiRows.length === 0}
|
||||
<div class="text-center text-xs text-gray-500 dark:text-gray-400">No results</div>
|
||||
{:else}
|
||||
<div class="w-full flex ml-2">
|
||||
<div class="w-full flex ml-0.5">
|
||||
<VirtualList rowHeight={ROW_HEIGHT} items={emojiRows} height={384} let:item>
|
||||
<div class="w-full">
|
||||
{#if item.length === 1 && item[0].type === 'group'}
|
||||
@ -136,7 +136,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<!-- Render emojis in a row -->
|
||||
<div class="flex items-center gap-2 w-full">
|
||||
<div class="flex items-center gap-1.5 w-full">
|
||||
{#each item as emojiItem}
|
||||
<Tooltip
|
||||
content={emojiItem.shortCodes.map((code) => `:${code}:`).join(', ')}
|
||||
|
@ -1151,13 +1151,6 @@
|
||||
if (done) {
|
||||
message.done = true;
|
||||
|
||||
if ($settings.notificationEnabled && !document.hasFocus()) {
|
||||
new Notification(`${message.model}`, {
|
||||
body: message.content,
|
||||
icon: `${WEBUI_BASE_URL}/static/favicon.png`
|
||||
});
|
||||
}
|
||||
|
||||
if ($settings.responseAutoCopy) {
|
||||
copyToClipboard(message.content);
|
||||
}
|
||||
|
@ -217,7 +217,13 @@
|
||||
const startRecording = async () => {
|
||||
if ($showCallOverlay) {
|
||||
if (!audioStream) {
|
||||
audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
audioStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
echoCancellation: true,
|
||||
noiseSuppression: true,
|
||||
autoGainControl: true
|
||||
}
|
||||
});
|
||||
}
|
||||
mediaRecorder = new MediaRecorder(audioStream);
|
||||
|
||||
|
@ -161,7 +161,13 @@
|
||||
const startRecording = async () => {
|
||||
startDurationCounter();
|
||||
|
||||
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
echoCancellation: true,
|
||||
noiseSuppression: true,
|
||||
autoGainControl: true
|
||||
}
|
||||
});
|
||||
mediaRecorder = new MediaRecorder(stream);
|
||||
mediaRecorder.onstart = () => {
|
||||
console.log('Recording started');
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let className = 'h-full flex pt-8';
|
||||
|
||||
export let chatId = '';
|
||||
export let user = $_user;
|
||||
|
||||
@ -333,7 +335,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="h-full flex pt-8">
|
||||
<div class={className}>
|
||||
{#if Object.keys(history?.messages ?? {}).length == 0}
|
||||
<ChatPlaceholder
|
||||
modelIds={selectedModels}
|
||||
|
@ -489,11 +489,15 @@
|
||||
|
||||
<div class="flex-auto w-0 pl-1">
|
||||
<Name>
|
||||
{model?.name ?? message.model}
|
||||
<Tooltip content={model?.name ?? message.model} placement="top-start">
|
||||
<span class="line-clamp-1">
|
||||
{model?.name ?? message.model}
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
{#if message.timestamp}
|
||||
<span
|
||||
class=" self-center invisible group-hover:visible text-gray-400 text-xs font-medium uppercase ml-0.5 -mt-0.5"
|
||||
class=" self-center shrink-0 translate-y-0.5 invisible group-hover:visible text-gray-400 text-xs font-medium uppercase ml-0.5 -mt-0.5"
|
||||
>
|
||||
{dayjs(message.timestamp * 1000).format($i18n.t('h:mm a'))}
|
||||
</span>
|
||||
|
@ -132,11 +132,11 @@
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<div class="flex flex-col items-end space-x-1 mt-1.5">
|
||||
<div class="flex flex-col items-end space-x-1 mt-3">
|
||||
<div class="flex gap-1">
|
||||
{#if $config?.features.enable_community_sharing}
|
||||
<button
|
||||
class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white"
|
||||
class="self-center flex items-center gap-1 px-3.5 py-2 text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:text-white dark:hover:bg-gray-800 transition rounded-full"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
shareChat();
|
||||
@ -148,7 +148,7 @@
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class=" self-center flex items-center gap-1 px-3.5 py-2 rounded-xl text-sm font-medium bg-emerald-600 hover:bg-emerald-500 text-white"
|
||||
class="self-center flex items-center gap-1 px-3.5 py-2 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
|
||||
type="button"
|
||||
id="copy-and-share-chat-button"
|
||||
on:click={async () => {
|
||||
|
@ -56,7 +56,7 @@
|
||||
|
||||
<style>
|
||||
.placeholder::before {
|
||||
/* abolute */
|
||||
/* absolute */
|
||||
position: absolute;
|
||||
content: attr(data-placeholder);
|
||||
color: #adb5bd;
|
||||
|
@ -83,12 +83,19 @@
|
||||
|
||||
const downloadJSONExport = async () => {
|
||||
if (chat.id) {
|
||||
chat = await getChatById(localStorage.token, chat.id);
|
||||
let chatObj = null;
|
||||
|
||||
if (chat.id === 'local' || $temporaryChatEnabled) {
|
||||
chatObj = chat;
|
||||
} else {
|
||||
chatObj = await getChatById(localStorage.token, chat.id);
|
||||
}
|
||||
|
||||
let blob = new Blob([JSON.stringify([chatObj])], {
|
||||
type: 'application/json'
|
||||
});
|
||||
saveAs(blob, `chat-export-${Date.now()}.json`);
|
||||
}
|
||||
let blob = new Blob([JSON.stringify([chat])], {
|
||||
type: 'application/json'
|
||||
});
|
||||
saveAs(blob, `chat-export-${Date.now()}.json`);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -558,19 +558,6 @@
|
||||
on:input={searchDebounceHandler}
|
||||
placeholder={$i18n.t('Search')}
|
||||
/>
|
||||
|
||||
<div class="absolute z-40 right-3.5 top-1">
|
||||
<Tooltip content={$i18n.t('New folder')}>
|
||||
<button
|
||||
class="p-1 rounded-lg bg-gray-50 hover:bg-gray-100 dark:bg-gray-950 dark:hover:bg-gray-900 text-gray-500 dark:text-gray-500 transition"
|
||||
on:click={() => {
|
||||
createFolder();
|
||||
}}
|
||||
>
|
||||
<Plus className=" size-3" strokeWidth="2.5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -578,82 +565,6 @@
|
||||
? 'opacity-20'
|
||||
: ''}"
|
||||
>
|
||||
{#if !search && $pinnedChats.length > 0}
|
||||
<div class="flex flex-col space-y-1 rounded-xl">
|
||||
<Folder
|
||||
className="px-2"
|
||||
bind:open={showPinnedChat}
|
||||
on:change={(e) => {
|
||||
localStorage.setItem('showPinnedChat', e.detail);
|
||||
console.log(e.detail);
|
||||
}}
|
||||
on:import={(e) => {
|
||||
importChatHandler(e.detail, true);
|
||||
}}
|
||||
on:drop={async (e) => {
|
||||
const { type, id, item } = e.detail;
|
||||
|
||||
if (type === 'chat') {
|
||||
let chat = await getChatById(localStorage.token, id).catch((error) => {
|
||||
return null;
|
||||
});
|
||||
if (!chat && item) {
|
||||
chat = await importChat(localStorage.token, item.chat, item?.meta ?? {});
|
||||
}
|
||||
|
||||
if (chat) {
|
||||
console.log(chat);
|
||||
if (chat.folder_id) {
|
||||
const res = await updateChatFolderIdById(
|
||||
localStorage.token,
|
||||
chat.id,
|
||||
null
|
||||
).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
if (!chat.pinned) {
|
||||
const res = await toggleChatPinnedStatusById(localStorage.token, chat.id);
|
||||
}
|
||||
|
||||
initChatList();
|
||||
}
|
||||
}
|
||||
}}
|
||||
name={$i18n.t('Pinned')}
|
||||
>
|
||||
<div
|
||||
class="ml-3 pl-1 mt-[1px] flex flex-col overflow-y-auto scrollbar-hidden border-s border-gray-100 dark:border-gray-900"
|
||||
>
|
||||
{#each $pinnedChats as chat, idx}
|
||||
<ChatItem
|
||||
className=""
|
||||
id={chat.id}
|
||||
title={chat.title}
|
||||
{shiftKey}
|
||||
selected={selectedChatId === chat.id}
|
||||
on:select={() => {
|
||||
selectedChatId = chat.id;
|
||||
}}
|
||||
on:unselect={() => {
|
||||
selectedChatId = null;
|
||||
}}
|
||||
on:change={async () => {
|
||||
initChatList();
|
||||
}}
|
||||
on:tag={(e) => {
|
||||
const { type, name } = e.detail;
|
||||
tagEventHandler(type, name, chat.id);
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</Folder>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if $config?.features?.enable_channels && ($user.role === 'admin' || $channels.length > 0) && !search}
|
||||
<Folder
|
||||
className="px-2 mt-0.5"
|
||||
@ -677,26 +588,14 @@
|
||||
</Folder>
|
||||
{/if}
|
||||
|
||||
{#if !search && folders}
|
||||
<Folders
|
||||
{folders}
|
||||
on:import={(e) => {
|
||||
const { folderId, items } = e.detail;
|
||||
importChatHandler(items, false, folderId);
|
||||
}}
|
||||
on:update={async (e) => {
|
||||
initChatList();
|
||||
}}
|
||||
on:change={async () => {
|
||||
initChatList();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<Folder
|
||||
collapsible={!search}
|
||||
className="px-2 mt-0.5"
|
||||
name={$i18n.t('Chats')}
|
||||
onAdd={() => {
|
||||
createFolder();
|
||||
}}
|
||||
onAddLabel={$i18n.t('New Folder')}
|
||||
on:import={(e) => {
|
||||
importChatHandler(e.detail);
|
||||
}}
|
||||
@ -723,7 +622,7 @@
|
||||
}
|
||||
|
||||
if (chat.pinned) {
|
||||
const res = await toggleChatPinnedStatusById(localStorage.token, chat, id);
|
||||
const res = await toggleChatPinnedStatusById(localStorage.token, chat.id);
|
||||
}
|
||||
|
||||
initChatList();
|
||||
@ -750,6 +649,98 @@
|
||||
<div class="absolute z-40 w-full h-full flex justify-center"></div>
|
||||
{/if}
|
||||
|
||||
{#if !search && $pinnedChats.length > 0}
|
||||
<div class="flex flex-col space-y-1 rounded-xl">
|
||||
<Folder
|
||||
className=""
|
||||
bind:open={showPinnedChat}
|
||||
on:change={(e) => {
|
||||
localStorage.setItem('showPinnedChat', e.detail);
|
||||
console.log(e.detail);
|
||||
}}
|
||||
on:import={(e) => {
|
||||
importChatHandler(e.detail, true);
|
||||
}}
|
||||
on:drop={async (e) => {
|
||||
const { type, id, item } = e.detail;
|
||||
|
||||
if (type === 'chat') {
|
||||
let chat = await getChatById(localStorage.token, id).catch((error) => {
|
||||
return null;
|
||||
});
|
||||
if (!chat && item) {
|
||||
chat = await importChat(localStorage.token, item.chat, item?.meta ?? {});
|
||||
}
|
||||
|
||||
if (chat) {
|
||||
console.log(chat);
|
||||
if (chat.folder_id) {
|
||||
const res = await updateChatFolderIdById(
|
||||
localStorage.token,
|
||||
chat.id,
|
||||
null
|
||||
).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
if (!chat.pinned) {
|
||||
const res = await toggleChatPinnedStatusById(localStorage.token, chat.id);
|
||||
}
|
||||
|
||||
initChatList();
|
||||
}
|
||||
}
|
||||
}}
|
||||
name={$i18n.t('Pinned')}
|
||||
>
|
||||
<div
|
||||
class="ml-3 pl-1 mt-[1px] flex flex-col overflow-y-auto scrollbar-hidden border-s border-gray-100 dark:border-gray-900"
|
||||
>
|
||||
{#each $pinnedChats as chat, idx}
|
||||
<ChatItem
|
||||
className=""
|
||||
id={chat.id}
|
||||
title={chat.title}
|
||||
{shiftKey}
|
||||
selected={selectedChatId === chat.id}
|
||||
on:select={() => {
|
||||
selectedChatId = chat.id;
|
||||
}}
|
||||
on:unselect={() => {
|
||||
selectedChatId = null;
|
||||
}}
|
||||
on:change={async () => {
|
||||
initChatList();
|
||||
}}
|
||||
on:tag={(e) => {
|
||||
const { type, name } = e.detail;
|
||||
tagEventHandler(type, name, chat.id);
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</Folder>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !search && folders}
|
||||
<Folders
|
||||
{folders}
|
||||
on:import={(e) => {
|
||||
const { folderId, items } = e.detail;
|
||||
importChatHandler(items, false, folderId);
|
||||
}}
|
||||
on:update={async (e) => {
|
||||
initChatList();
|
||||
}}
|
||||
on:change={async () => {
|
||||
initChatList();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class=" flex-1 flex flex-col overflow-y-auto scrollbar-hidden">
|
||||
<div class="pt-1.5">
|
||||
{#if $chats}
|
||||
|
@ -81,9 +81,8 @@
|
||||
{#if $user?.role === 'admin'}
|
||||
<button
|
||||
class="absolute z-10 right-2 invisible group-hover:visible self-center flex items-center dark:text-gray-300"
|
||||
on:pointerup={(e) => {
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
showEditChannelModal = true;
|
||||
}}
|
||||
>
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
{#each folderList as folderId (folderId)}
|
||||
<RecursiveFolder
|
||||
className="px-2"
|
||||
className=""
|
||||
{folders}
|
||||
{folderId}
|
||||
on:import={(e) => {
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "أضغط على أسم الصلاحيات لتغيرها للمستخدم",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "استنساخ",
|
||||
"Clone Chat": "",
|
||||
"Close": "أغلق",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "الأسم",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "دردشة جديدة",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "كلمة المرور الجديدة",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Натиснете върху бутона за промяна на ролята на потребителя.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "Клонинг",
|
||||
"Clone Chat": "",
|
||||
"Close": "Затвори",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Име",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Нов чат",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Нова парола",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "ইউজারের পদবি পরিবর্তন করার জন্য ইউজারের পদবি বাটনে ক্লিক করুন",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "ক্লোন",
|
||||
"Clone Chat": "",
|
||||
"Close": "বন্ধ",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "নাম",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "নতুন চ্যাট",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "নতুন পাসওয়ার্ড",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Clica sobre el botó de rol d'usuari per canviar el rol d'un usuari.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permís d'escriptura al porta-retalls denegat. Comprova els ajustos de navegador per donar l'accés necessari.",
|
||||
"Clone": "Clonar",
|
||||
"Clone Chat": "",
|
||||
"Close": "Tancar",
|
||||
"Code execution": "Execució de codi",
|
||||
"Code formatted successfully": "Codi formatat correctament",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nom",
|
||||
"Name your knowledge base": "Anomena la teva base de coneixement",
|
||||
"New Chat": "Nou xat",
|
||||
"New folder": "Nova carpeta",
|
||||
"New Folder": "",
|
||||
"New Password": "Nova contrasenya",
|
||||
"new-channel": "nou-canal",
|
||||
"No content found": "No s'ha trobat contingut",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "I-klik ang User Role button aron usbon ang role sa user.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "",
|
||||
"Clone Chat": "",
|
||||
"Close": "Suod nga",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Ngalan",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Bag-ong diskusyon",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Bag-ong Password",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Klikněte na tlačítko role uživatele, abyste změnili roli uživatele.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Přístup k zápisu do schránky byl zamítnut. Prosím, zkontrolujte nastavení svého prohlížeče a udělte potřebný přístup.",
|
||||
"Clone": "Klonovat",
|
||||
"Clone Chat": "",
|
||||
"Close": "Zavřít",
|
||||
"Code execution": "Provádění kódu",
|
||||
"Code formatted successfully": "Kód byl úspěšně naformátován.",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Jméno",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Nový chat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nové heslo",
|
||||
"new-channel": "",
|
||||
"No content found": "Nebyly nalezeny žádné obsahové informace.",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Klik på bruger ikonet for at ændre brugerens rolle.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Skriveadgang til udklipsholderen ikke tilladt. Tjek venligst indstillingerne i din browser for at give adgang.",
|
||||
"Clone": "Klon",
|
||||
"Clone Chat": "",
|
||||
"Close": "Luk",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "Kode formateret korrekt",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Navn",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Ny chat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Ny adgangskode",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Klicken Sie auf die Benutzerrolle, um sie zu ändern.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Schreibberechtigung für die Zwischenablage verweigert. Bitte überprüfen Sie Ihre Browsereinstellungen, um den erforderlichen Zugriff zu erlauben.",
|
||||
"Clone": "Klonen",
|
||||
"Clone Chat": "",
|
||||
"Close": "Schließen",
|
||||
"Code execution": "Codeausführung",
|
||||
"Code formatted successfully": "Code erfolgreich formatiert",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Name",
|
||||
"Name your knowledge base": "Benennen Sie Ihren Wissensspeicher",
|
||||
"New Chat": "Neue Unterhaltung",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Neues Passwort",
|
||||
"new-channel": "",
|
||||
"No content found": "Kein Inhalt gefunden",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Click user role button to change role.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "",
|
||||
"Clone Chat": "",
|
||||
"Close": "Close",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Name",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "New Bark",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "New Barkword",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Κάντε κλικ στο κουμπί ρόλου χρήστη για να αλλάξετε το ρόλο ενός χρήστη.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Άρνηση δικαιώματος εγγραφής στο πρόχειρο. Παρακαλώ ελέγξτε τις ρυθμίσεις του περιηγητή σας για να δώσετε την απαραίτητη πρόσβαση.",
|
||||
"Clone": "Κλώνος",
|
||||
"Clone Chat": "",
|
||||
"Close": "Κλείσιμο",
|
||||
"Code execution": "Εκτέλεση κώδικα",
|
||||
"Code formatted successfully": "Ο κώδικας μορφοποιήθηκε επιτυχώς",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Όνομα",
|
||||
"Name your knowledge base": "Ονομάστε τη βάση γνώσης σας",
|
||||
"New Chat": "Νέα Συνομιλία",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Νέος Κωδικός",
|
||||
"new-channel": "",
|
||||
"No content found": "Δεν βρέθηκε περιεχόμενο",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "",
|
||||
"Clone Chat": "",
|
||||
"Close": "",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "",
|
||||
"Clone Chat": "",
|
||||
"Close": "",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"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.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permisos de escritura del portapapeles denegados. Por favor, comprueba las configuraciones de tu navegador para otorgar el acceso necesario.",
|
||||
"Clone": "Clonar",
|
||||
"Clone Chat": "",
|
||||
"Close": "Cerrar",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "Se ha formateado correctamente el código.",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nombre",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Nuevo Chat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nueva Contraseña",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Klikatu erabiltzaile rolaren botoian erabiltzaile baten rola aldatzeko.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Arbelerako idazteko baimena ukatua. Mesedez, egiaztatu zure nabigatzailearen ezarpenak beharrezko sarbidea emateko.",
|
||||
"Clone": "Klonatu",
|
||||
"Clone Chat": "",
|
||||
"Close": "Itxi",
|
||||
"Code execution": "Kodearen exekuzioa",
|
||||
"Code formatted successfully": "Kodea ongi formateatu da",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Izena",
|
||||
"Name your knowledge base": "Izendatu zure ezagutza-basea",
|
||||
"New Chat": "Txat berria",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Pasahitz berria",
|
||||
"new-channel": "",
|
||||
"No content found": "Ez da edukirik aurkitu",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "کلون",
|
||||
"Clone Chat": "",
|
||||
"Close": "بسته",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "نام",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "گپ جدید",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "رمز عبور جدید",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Klikkaa käyttäjän roolipainiketta vaihtaaksesi käyttäjän roolia.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Leikepöydälle kirjoitusoikeus evätty. Tarkista selaimesi asetukset ja myönnä tarvittavat käyttöoikeudet.",
|
||||
"Clone": "Kloonaa",
|
||||
"Clone Chat": "",
|
||||
"Close": "Sulje",
|
||||
"Code execution": "Koodin suorittaminen",
|
||||
"Code formatted successfully": "Koodin muotoilu onnistui",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nimi",
|
||||
"Name your knowledge base": "Anna tietokannalle nimi",
|
||||
"New Chat": "Uusi keskustelu",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Uusi salasana",
|
||||
"new-channel": "",
|
||||
"No content found": "Sisältöä ei löytynyt",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Cliquez sur le bouton de rôle d'utilisateur pour modifier le rôle d'un utilisateur.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "L'autorisation d'écriture du presse-papier a été refusée. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
|
||||
"Clone": "Copie conforme",
|
||||
"Clone Chat": "",
|
||||
"Close": "Fermer",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "Le code a été formaté avec succès",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nom",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Nouvelle conversation",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nouveau mot de passe",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Cliquez sur le bouton de rôle d'utilisateur pour modifier son rôle.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "L'autorisation d'écriture du presse-papier a été refusée. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
|
||||
"Clone": "Cloner",
|
||||
"Clone Chat": "",
|
||||
"Close": "Fermer",
|
||||
"Code execution": "Exécution de code",
|
||||
"Code formatted successfully": "Le code a été formaté avec succès",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nom d'utilisateur",
|
||||
"Name your knowledge base": "Nommez votre base de connaissances",
|
||||
"New Chat": "Nouvelle conversation",
|
||||
"New folder": "Nouveau dossier",
|
||||
"New Folder": "",
|
||||
"New Password": "Nouveau mot de passe",
|
||||
"new-channel": "nouveau-canal",
|
||||
"No content found": "Aucun contenu trouvé",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "לחץ על כפתור תפקיד המשתמש כדי לשנות את תפקיד המשתמש.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "שיבוט",
|
||||
"Clone Chat": "",
|
||||
"Close": "סגור",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "שם",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "צ'אט חדש",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "סיסמה חדשה",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "उपयोगकर्ता की भूमिका बदलने के लिए उपयोगकर्ता भूमिका बटन पर क्लिक करें।",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "क्लोन",
|
||||
"Clone Chat": "",
|
||||
"Close": "बंद करना",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "नाम",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "नई चैट",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "नया पासवर्ड",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Kliknite na gumb uloge korisnika za promjenu uloge korisnika.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "Kloniraj",
|
||||
"Clone Chat": "",
|
||||
"Close": "Zatvori",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Ime",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Novi razgovor",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nova lozinka",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Kattints a felhasználói szerep gombra a felhasználó szerepének módosításához.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Vágólap írási engedély megtagadva. Kérjük, ellenőrizd a böngésző beállításait a szükséges hozzáférés megadásához.",
|
||||
"Clone": "Klónozás",
|
||||
"Clone Chat": "",
|
||||
"Close": "Bezárás",
|
||||
"Code execution": "Kód végrehajtás",
|
||||
"Code formatted successfully": "Kód sikeresen formázva",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Név",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Új beszélgetés",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Új jelszó",
|
||||
"new-channel": "",
|
||||
"No content found": "Nem található tartalom",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Klik tombol peran pengguna untuk mengubah peran pengguna.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Izin menulis papan klip ditolak. Periksa pengaturan peramban Anda untuk memberikan akses yang diperlukan.",
|
||||
"Clone": "Kloning",
|
||||
"Clone Chat": "",
|
||||
"Close": "Tutup",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "Kode berhasil diformat",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nama",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Obrolan Baru",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Kata Sandi Baru",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Cliceáil ar an gcnaipe ról úsáideora chun ról úsáideora a athrú.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Diúltaíodh cead scríofa an ghearrthaisce. Seiceáil socruithe do bhrabhsálaí chun an rochtain riachtanach a dheonú.",
|
||||
"Clone": "Clón",
|
||||
"Clone Chat": "",
|
||||
"Close": "Dún",
|
||||
"Code execution": "Cód a fhorghníomhú",
|
||||
"Code formatted successfully": "Cód formáidithe go rathúil",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Ainm",
|
||||
"Name your knowledge base": "Cuir ainm ar do bhunachar eolais",
|
||||
"New Chat": "Comhrá Nua",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Pasfhocal Nua",
|
||||
"new-channel": "",
|
||||
"No content found": "Níor aimsíodh aon ábhar",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"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.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "Clone",
|
||||
"Clone Chat": "",
|
||||
"Close": "Chiudi",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nome",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Nuova chat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nuova password",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "ユーザーの役割を変更するには、ユーザー役割ボタンをクリックしてください。",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "クリップボードへの書き込み許可がありません。ブラウザ設定を確認し許可してください。",
|
||||
"Clone": "クローン",
|
||||
"Clone Chat": "",
|
||||
"Close": "閉じる",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "コードフォーマットに成功しました",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "名前",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "新しいチャット",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "新しいパスワード",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "დააკლიკეთ მომხმარებლის როლის ღილაკს რომ შეცვალოთ მომხმარების როლი",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "კლონი",
|
||||
"Clone Chat": "",
|
||||
"Close": "დახურვა",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "სახელი",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "ახალი მიმოწერა",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "ახალი პაროლი",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "사용자 역할 버튼을 클릭하여 사용자의 역할을 변경하세요.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "클립보드 사용 권한이 거절되었습니다. 필요한 접근을 사용하기 위해 브라우져 설정을 확인 부탁드립니다.",
|
||||
"Clone": "복제",
|
||||
"Clone Chat": "",
|
||||
"Close": "닫기",
|
||||
"Code execution": "코드 실행",
|
||||
"Code formatted successfully": "성공적으로 코드가 생성되었습니다",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "이름",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "새 채팅",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "새 비밀번호",
|
||||
"new-channel": "",
|
||||
"No content found": "내용을 찾을 수 없음",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Paspauskite ant naudotojo rolės mygtuko tam, kad pakeisti naudotojo rolę.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Iškarpinės naudojimas neleidžiamas naršyklės.",
|
||||
"Clone": "Klonuoti",
|
||||
"Clone Chat": "",
|
||||
"Close": "Uždaryti",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "Kodas suformatuotas sėkmingai",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Pavadinimas",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Naujas pokalbis",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Naujas slaptažodis",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Klik pada butang peranan pengguna untuk menukar peranan pengguna",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Kebenaran untuk menulis di papan klip ditolak. Sila semak tetapan pelayan web anda untuk memberikan akses yang diperlukan",
|
||||
"Clone": "Klon",
|
||||
"Clone Chat": "",
|
||||
"Close": "Tutup",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "Kod berjaya diformatkan",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nama",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Perbualan Baru",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Kata Laluan Baru",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Klikk på knappen Brukerrolle for å endre en brukers rolle.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Skrivetilgang til utklippstavlen avslått. Kontroller nettleserinnstillingene for å gi den nødvendige tilgangen.",
|
||||
"Clone": "Klon",
|
||||
"Clone Chat": "",
|
||||
"Close": "Lukk",
|
||||
"Code execution": "Kodekjøring",
|
||||
"Code formatted successfully": "Koden er formatert",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Navn",
|
||||
"Name your knowledge base": "Gi kunnskapsbasen et navn",
|
||||
"New Chat": "Ny chat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nytt passord",
|
||||
"new-channel": "",
|
||||
"No content found": "Finner ikke noe innhold",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"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.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Klembord schrijftoestemming geweigerd. Kijk je browserinstellingen na om de benodigde toestemming te geven.",
|
||||
"Clone": "Kloon",
|
||||
"Clone Chat": "",
|
||||
"Close": "Sluiten",
|
||||
"Code execution": "Code uitvoeren",
|
||||
"Code formatted successfully": "Code succesvol geformateerd",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Naam",
|
||||
"Name your knowledge base": "Geef je kennisbasis een naam",
|
||||
"New Chat": "Nieuwe Chat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nieuw Wachtwoord",
|
||||
"new-channel": "",
|
||||
"No content found": "Geen content gevonden",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "ਉਪਭੋਗਤਾ ਦੀ ਭੂਮਿਕਾ ਬਦਲਣ ਲਈ ਉਪਭੋਗਤਾ ਭੂਮਿਕਾ ਬਟਨ 'ਤੇ ਕਲਿੱਕ ਕਰੋ।",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "ਕਲੋਨ",
|
||||
"Clone Chat": "",
|
||||
"Close": "ਬੰਦ ਕਰੋ",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "ਨਾਮ",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "ਨਵੀਂ ਗੱਲਬਾਤ",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "ਨਵਾਂ ਪਾਸਵਰਡ",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Kliknij przycisk roli użytkownika, aby zmienić rolę użytkownika.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "Klon",
|
||||
"Clone Chat": "",
|
||||
"Close": "Zamknij",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nazwa",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Nowy czat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nowe hasło",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"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.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permissão de escrita na área de transferência negada. Verifique as configurações do seu navegador para conceder o acesso necessário.",
|
||||
"Clone": "Clonar",
|
||||
"Clone Chat": "",
|
||||
"Close": "Fechar",
|
||||
"Code execution": "Execução de código",
|
||||
"Code formatted successfully": "Código formatado com sucesso",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nome",
|
||||
"Name your knowledge base": "Nome da sua base de conhecimento",
|
||||
"New Chat": "Novo Chat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nova Senha",
|
||||
"new-channel": "",
|
||||
"No content found": "Nenhum conteúdo encontrado",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Clique no botão de função do utilizador para alterar a função de um utilizador.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "Clonar",
|
||||
"Clone Chat": "",
|
||||
"Close": "Fechar",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nome",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Nova Conversa",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nova Senha",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Apasă pe butonul rolului utilizatorului pentru a schimba rolul unui utilizator.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permisiunea de scriere în clipboard a fost refuzată. Vă rugăm să verificați setările browserului pentru a acorda accesul necesar.",
|
||||
"Clone": "Clonează",
|
||||
"Clone Chat": "",
|
||||
"Close": "Închide",
|
||||
"Code execution": "Executarea codului",
|
||||
"Code formatted successfully": "Cod formatat cu succes",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Nume",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Conversație Nouă",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Parolă Nouă",
|
||||
"new-channel": "",
|
||||
"No content found": "Nu a fost găsit niciun conținut",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Нажмите кнопку роли пользователя, чтобы изменить роль пользователя.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "В разрешении на запись в буфер обмена отказано. Пожалуйста, проверьте настройки своего браузера, чтобы предоставить необходимый доступ.",
|
||||
"Clone": "Клонировать",
|
||||
"Clone Chat": "",
|
||||
"Close": "Закрыть",
|
||||
"Code execution": "Выполнение кода",
|
||||
"Code formatted successfully": "Код успешно отформатирован",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Имя",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Новый чат",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Новый пароль",
|
||||
"new-channel": "",
|
||||
"No content found": "Контент не найден",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Kliknite na tlačidlo role užívateľa, aby ste zmenili rolu užívateľa.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Prístup na zápis do schránky bol zamietnutý. Skontrolujte nastavenia prehliadača a udeľte potrebný prístup.",
|
||||
"Clone": "Klonovať",
|
||||
"Clone Chat": "",
|
||||
"Close": "Zavrieť",
|
||||
"Code execution": "Vykonávanie kódu",
|
||||
"Code formatted successfully": "Kód bol úspešne naformátovaný.",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Meno",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Nový chat",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nové heslo",
|
||||
"new-channel": "",
|
||||
"No content found": "Nebol nájdený žiadny obsah.",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Кликните на дугме за улогу корисника да промените улогу корисника.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "Клонирај",
|
||||
"Clone Chat": "",
|
||||
"Close": "Затвори",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Име",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Ново ћаскање",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Нова лозинка",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"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.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "Klon",
|
||||
"Clone Chat": "",
|
||||
"Close": "Stäng",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Namn",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Ny chatt",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Nytt lösenord",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "คลิกที่ปุ่มบทบาทผู้ใช้เพื่อเปลี่ยนบทบาทของผู้ใช้",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "การอนุญาตเขียนคลิปบอร์ดถูกปฏิเสธ โปรดตรวจสอบการตั้งค่าเบราว์เซอร์ของคุณเพื่อให้สิทธิ์ที่จำเป็น",
|
||||
"Clone": "โคลน",
|
||||
"Clone Chat": "",
|
||||
"Close": "ปิด",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "จัดรูปแบบโค้ดสำเร็จแล้ว",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "ชื่อ",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "แชทใหม่",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "รหัสผ่านใหม่",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
|
||||
"Clone": "",
|
||||
"Clone Chat": "",
|
||||
"Close": "",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"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.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Panoya yazma izni reddedildi. Tarayıcı ayarlarını kontrol ederek gerekli izinleri sağlayabilirsiniz.",
|
||||
"Clone": "Klon",
|
||||
"Clone Chat": "",
|
||||
"Close": "Kapat",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "Kod başarıyla biçimlendirildi",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Ad",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Yeni Sohbet",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Yeni Parola",
|
||||
"new-channel": "",
|
||||
"No content found": "İçerik bulunamadı",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "Натисніть кнопку ролі користувача, щоб змінити роль користувача.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Відмовлено в дозволі на запис до буфера обміну. Будь ласка, перевірте налаштування вашого браузера, щоб надати необхідний доступ.",
|
||||
"Clone": "Клонувати",
|
||||
"Clone Chat": "",
|
||||
"Close": "Закрити",
|
||||
"Code execution": "Виконання коду",
|
||||
"Code formatted successfully": "Код успішно відформатовано",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Ім'я",
|
||||
"Name your knowledge base": "Назвіть вашу базу знань",
|
||||
"New Chat": "Новий чат",
|
||||
"New folder": "Нова папка",
|
||||
"New Folder": "",
|
||||
"New Password": "Новий пароль",
|
||||
"new-channel": "новий-канал",
|
||||
"No content found": "Контент не знайдено.",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "صارف کا کردار تبدیل کرنے کے لیے صارف کردار بٹن پر کلک کریں",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "کلپ بورڈ لکھنے کی اجازت نہیں دی گئی براہ کرم ضروری رسائی کی اجازت دینے کے لیے اپنے براؤزر کی سیٹنگز چیک کریں",
|
||||
"Clone": "نقل کریں",
|
||||
"Clone Chat": "",
|
||||
"Close": "بند کریں",
|
||||
"Code execution": "کوڈ کا نفاذ",
|
||||
"Code formatted successfully": "کوڈ کامیابی سے فارمیٹ ہو گیا",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "نام",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "نئی بات چیت",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "نیا پاس ورڈ",
|
||||
"new-channel": "",
|
||||
"No content found": "کوئی مواد نہیں ملا",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"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.",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Quyền ghi vào clipboard bị từ chối. Vui lòng kiểm tra cài đặt trên trình duyệt của bạn để được cấp quyền truy cập cần thiết.",
|
||||
"Clone": "Nhân bản",
|
||||
"Clone Chat": "",
|
||||
"Close": "Đóng",
|
||||
"Code execution": "",
|
||||
"Code formatted successfully": "Mã được định dạng thành công",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "Tên",
|
||||
"Name your knowledge base": "",
|
||||
"New Chat": "Tạo chat mới",
|
||||
"New folder": "",
|
||||
"New Folder": "",
|
||||
"New Password": "Mật khẩu mới",
|
||||
"new-channel": "",
|
||||
"No content found": "",
|
||||
|
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "点击角色前方的组别按钮以更改用户所属权限组。",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "写入剪贴板时被拒绝。请检查浏览器设置,授予必要权限。",
|
||||
"Clone": "复制",
|
||||
"Clone Chat": "",
|
||||
"Close": "关闭",
|
||||
"Code execution": "代码执行",
|
||||
"Code formatted successfully": "代码格式化成功",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "名称",
|
||||
"Name your knowledge base": "为您的知识库命名",
|
||||
"New Chat": "新对话",
|
||||
"New folder": "新文件夹",
|
||||
"New Folder": "",
|
||||
"New Password": "新密码",
|
||||
"new-channel": "新频道",
|
||||
"No content found": "未发现内容",
|
||||
|
@ -5,7 +5,7 @@
|
||||
"(e.g. `sh webui.sh --api`)": "(例如 `sh webui.sh --api`)",
|
||||
"(latest)": "(最新版)",
|
||||
"{{ models }}": "{{ models }}",
|
||||
"{{COUNT}} Replies": "",
|
||||
"{{COUNT}} Replies": "{{COUNT}} 回覆",
|
||||
"{{user}}'s Chats": "{{user}} 的對話",
|
||||
"{{webUIName}} Backend Required": "需要 {{webUIName}} 後端",
|
||||
"*Prompt node ID(s) are required for image generation": "* 圖片生成需要提示詞節點 ID",
|
||||
@ -166,6 +166,7 @@
|
||||
"Click on the user role button to change a user's role.": "點選使用者角色按鈕變更使用者的角色。",
|
||||
"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "剪貼簿寫入權限遭拒。請檢查您的瀏覽器設定,授予必要的存取權限。",
|
||||
"Clone": "複製",
|
||||
"Clone Chat": "",
|
||||
"Close": "關閉",
|
||||
"Code execution": "程式碼執行",
|
||||
"Code formatted successfully": "程式碼格式化成功",
|
||||
@ -536,7 +537,7 @@
|
||||
"Language": "語言",
|
||||
"Last Active": "上次活動時間",
|
||||
"Last Modified": "上次修改時間",
|
||||
"Last reply": "",
|
||||
"Last reply": "上次回覆",
|
||||
"LDAP": "LDAP",
|
||||
"LDAP server updated": "LDAP 伺服器已更新",
|
||||
"Leaderboard": "排行榜",
|
||||
@ -611,7 +612,7 @@
|
||||
"Name": "名稱",
|
||||
"Name your knowledge base": "命名您的知識庫",
|
||||
"New Chat": "新增對話",
|
||||
"New folder": "新增資料夾",
|
||||
"New Folder": "",
|
||||
"New Password": "新密碼",
|
||||
"new-channel": "new-channel",
|
||||
"No content found": "找不到內容",
|
||||
@ -742,7 +743,7 @@
|
||||
"Rename": "重新命名",
|
||||
"Reorder Models": "重新排序模型",
|
||||
"Repeat Last N": "重複最後 N 個",
|
||||
"Reply in Thread": "",
|
||||
"Reply in Thread": "在討論串中回覆",
|
||||
"Request Mode": "請求模式",
|
||||
"Reranking Model": "重新排序模型",
|
||||
"Reranking model disabled": "已停用重新排序模型",
|
||||
@ -1009,7 +1010,7 @@
|
||||
"variable to have them replaced with clipboard content.": "變數,以便將其替換為剪貼簿內容。",
|
||||
"Version": "版本",
|
||||
"Version {{selectedVersion}} of {{totalVersions}}": "第 {{selectedVersion}} 版,共 {{totalVersions}} 版",
|
||||
"View Replies": "",
|
||||
"View Replies": "檢視回覆",
|
||||
"Visibility": "可見性",
|
||||
"Voice": "語音",
|
||||
"Voice Input": "語音輸入",
|
||||
|
@ -69,6 +69,9 @@ export const temporaryChatEnabled = writable(false);
|
||||
export const scrollPaginationEnabled = writable(false);
|
||||
export const currentChatPage = writable(1);
|
||||
|
||||
export const isLastActiveTab = writable(true);
|
||||
export const playingNotificationSound = writable(false);
|
||||
|
||||
export type Model = OpenAIModel | OllamaModel;
|
||||
|
||||
type BaseModel = {
|
||||
|
@ -10,7 +10,7 @@
|
||||
export const ssr = false;
|
||||
|
||||
// How to manage the trailing slashes in the URLs
|
||||
// the URL for about page witll be /about with 'ignore' (default)
|
||||
// the URL for about page witll be /about/ with 'always'
|
||||
// the URL for about page will be /about with 'ignore' (default)
|
||||
// the URL for about page will be /about/ with 'always'
|
||||
// https://kit.svelte.dev/docs/page-options#trailingslash
|
||||
export const trailingSlash = 'ignore';
|
||||
|
@ -10,6 +10,7 @@
|
||||
import {
|
||||
config,
|
||||
user,
|
||||
settings,
|
||||
theme,
|
||||
WEBUI_NAME,
|
||||
mobile,
|
||||
@ -20,7 +21,8 @@
|
||||
chats,
|
||||
currentChatPage,
|
||||
tags,
|
||||
temporaryChatEnabled
|
||||
temporaryChatEnabled,
|
||||
isLastActiveTab
|
||||
} from '$lib/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
@ -42,7 +44,10 @@
|
||||
|
||||
setContext('i18n', i18n);
|
||||
|
||||
const bc = new BroadcastChannel('active-tab-channel');
|
||||
|
||||
let loaded = false;
|
||||
|
||||
const BREAKPOINT = 768;
|
||||
|
||||
const setupSocket = async (enableWebsocket) => {
|
||||
@ -107,6 +112,15 @@
|
||||
const { done, content, title } = data;
|
||||
|
||||
if (done) {
|
||||
if ($isLastActiveTab) {
|
||||
if ($settings?.notificationEnabled ?? false) {
|
||||
new Notification(`${title} | Open WebUI`, {
|
||||
body: content,
|
||||
icon: `${WEBUI_BASE_URL}/static/favicon.png`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
toast.custom(NotificationToast, {
|
||||
componentProps: {
|
||||
onClick: () => {
|
||||
@ -138,6 +152,15 @@
|
||||
const data = event?.data?.data ?? null;
|
||||
|
||||
if (type === 'message') {
|
||||
if ($isLastActiveTab) {
|
||||
if ($settings?.notificationEnabled ?? false) {
|
||||
new Notification(`${data?.user?.name} (#${event?.channel?.name}) | Open WebUI`, {
|
||||
body: data?.content,
|
||||
icon: data?.user?.profile_image_url ?? `${WEBUI_BASE_URL}/static/favicon.png`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
toast.custom(NotificationToast, {
|
||||
componentProps: {
|
||||
onClick: () => {
|
||||
@ -154,6 +177,27 @@
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
// Listen for messages on the BroadcastChannel
|
||||
bc.onmessage = (event) => {
|
||||
if (event.data === 'active') {
|
||||
isLastActiveTab.set(false); // Another tab became active
|
||||
}
|
||||
};
|
||||
|
||||
// Set yourself as the last active tab when this tab is focused
|
||||
const handleVisibilityChange = () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
isLastActiveTab.set(true); // This tab is now the active tab
|
||||
bc.postMessage('active'); // Notify other tabs that this tab is active
|
||||
}
|
||||
};
|
||||
|
||||
// Add event listener for visibility state changes
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
// Call visibility change handler initially to set state on load
|
||||
handleVisibilityChange();
|
||||
|
||||
theme.set(localStorage.theme);
|
||||
|
||||
mobile.set(window.innerWidth < BREAKPOINT);
|
||||
|
@ -8,13 +8,14 @@
|
||||
import { settings, chatId, WEBUI_NAME, models } from '$lib/stores';
|
||||
import { convertMessagesToHistory, createMessagesList } from '$lib/utils';
|
||||
|
||||
import { getChatByShareId } from '$lib/apis/chats';
|
||||
import { getChatByShareId, cloneSharedChatById } from '$lib/apis/chats';
|
||||
|
||||
import Messages from '$lib/components/chat/Messages.svelte';
|
||||
import Navbar from '$lib/components/layout/Navbar.svelte';
|
||||
|
||||
import { getUserById } from '$lib/apis/users';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { getModels } from '$lib/apis';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
@ -100,6 +101,19 @@
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const cloneSharedChat = async () => {
|
||||
if (!chat) return;
|
||||
|
||||
const res = await cloneSharedChatById(localStorage.token, chat.id).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
goto(`/c/${res.id}`);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@ -114,25 +128,26 @@
|
||||
<div
|
||||
class="min-h-screen max-h-screen w-full flex flex-col text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-900"
|
||||
>
|
||||
<div class="flex flex-col flex-auto justify-center py-8">
|
||||
<div class="px-3 w-full max-w-5xl mx-auto">
|
||||
<div>
|
||||
<div class=" text-3xl font-semibold line-clamp-1">
|
||||
{title}
|
||||
</div>
|
||||
<div class="flex flex-col flex-auto justify-center relative">
|
||||
<div class=" flex flex-col w-full flex-auto overflow-auto h-0" id="messages-container">
|
||||
<div class="pt-5 px-2 w-full max-w-5xl mx-auto">
|
||||
<div class="px-3">
|
||||
<div class=" text-2xl font-semibold line-clamp-1">
|
||||
{title}
|
||||
</div>
|
||||
|
||||
<div class=" mt-1 text-gray-400">
|
||||
{dayjs(chat.chat.timestamp).format($i18n.t('MMMM DD, YYYY'))}
|
||||
<div class="flex text-sm justify-between items-center mt-1">
|
||||
<div class="text-gray-400">
|
||||
{dayjs(chat.chat.timestamp).format($i18n.t('MMMM DD, YYYY'))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="border-gray-50 dark:border-gray-850 mt-6 mb-2" />
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<div class=" h-full w-full flex flex-col py-2">
|
||||
<div class="">
|
||||
<Messages
|
||||
className="h-full flex pt-4 pb-8"
|
||||
{user}
|
||||
chatId={$chatId}
|
||||
readOnly={true}
|
||||
@ -149,6 +164,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute bottom-0 right-0 left-0 flex justify-center w-full bg-gradient-to-b from-transparent to-gray-900"
|
||||
>
|
||||
<div class="pb-5">
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
|
||||
on:click={cloneSharedChat}
|
||||
>
|
||||
{$i18n.t('Clone Chat')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
Loading…
x
Reference in New Issue
Block a user