forked from open-webui/open-webui
Merge branch 'main' into feat/cancel-model-download
# Conflicts: # src/lib/components/chat/Settings/Models.svelte
This commit is contained in:
commit
45311bfa15
170 changed files with 19354 additions and 3729 deletions
|
|
@ -261,3 +261,60 @@ export const toggleSignUpEnabledStatus = async (token: string) => {
|
|||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getJWTExpiresDuration = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/auths/token/expires`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.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 updateJWTExpiresDuration = async (token: string, duration: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/auths/token/expires/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
duration: duration
|
||||
})
|
||||
})
|
||||
.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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ export const deleteAllChats = async (token: string) => {
|
|||
return json;
|
||||
})
|
||||
.catch((err) => {
|
||||
error = err;
|
||||
error = err.detail;
|
||||
|
||||
console.log(err);
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ export const createNewDoc = async (
|
|||
collection_name: string,
|
||||
filename: string,
|
||||
name: string,
|
||||
title: string
|
||||
title: string,
|
||||
content: object | null = null
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
|
|
@ -20,7 +21,8 @@ export const createNewDoc = async (
|
|||
collection_name: collection_name,
|
||||
filename: filename,
|
||||
name: name,
|
||||
title: title
|
||||
title: title,
|
||||
...(content ? { content: JSON.stringify(content) } : {})
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
|
|
|
|||
473
src/lib/apis/images/index.ts
Normal file
473
src/lib/apis/images/index.ts
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
import { IMAGES_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
export const getImageGenerationConfig = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/config`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const updateImageGenerationConfig = async (
|
||||
token: string = '',
|
||||
engine: string,
|
||||
enabled: boolean
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/config/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
engine,
|
||||
enabled
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getOpenAIKey = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/key`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.OPENAI_API_KEY;
|
||||
};
|
||||
|
||||
export const updateOpenAIKey = async (token: string = '', key: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/key/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
key: key
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.OPENAI_API_KEY;
|
||||
};
|
||||
|
||||
export const getAUTOMATIC1111Url = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/url`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.AUTOMATIC1111_BASE_URL;
|
||||
};
|
||||
|
||||
export const updateAUTOMATIC1111Url = async (token: string = '', url: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/url/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
url: url
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.AUTOMATIC1111_BASE_URL;
|
||||
};
|
||||
|
||||
export const getImageSize = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/size`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.IMAGE_SIZE;
|
||||
};
|
||||
|
||||
export const updateImageSize = async (token: string = '', size: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/size/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
size: size
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.IMAGE_SIZE;
|
||||
};
|
||||
|
||||
export const getImageSteps = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/steps`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.IMAGE_STEPS;
|
||||
};
|
||||
|
||||
export const updateImageSteps = async (token: string = '', steps: number) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/steps/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({ steps })
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.IMAGE_STEPS;
|
||||
};
|
||||
|
||||
export const getImageGenerationModels = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/models`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getDefaultImageGenerationModel = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/models/default`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.model;
|
||||
};
|
||||
|
||||
export const updateDefaultImageGenerationModel = async (token: string = '', model: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/models/default/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.model;
|
||||
};
|
||||
|
||||
export const imageGenerations = async (token: string = '', prompt: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${IMAGES_API_BASE_URL}/generations`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompt: prompt
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
} else {
|
||||
error = 'Server connection failed';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import { WEBUI_API_BASE_URL } from '$lib/constants';
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
|
||||
export const getBackendConfig = async () => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/`, {
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/config`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
|
@ -19,5 +19,180 @@ export const getBackendConfig = async () => {
|
|||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getChangelog = async () => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/changelog`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getVersionUpdates = async () => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/version/updates`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getModelFilterConfig = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/config/model/filter`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const updateModelFilterConfig = async (
|
||||
token: string,
|
||||
enabled: boolean,
|
||||
models: string[]
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/config/model/filter`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
enabled: enabled,
|
||||
models: models
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const getWebhookUrl = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/webhook`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.url;
|
||||
};
|
||||
|
||||
export const updateWebhookUrl = async (token: string, url: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_BASE_URL}/api/webhook`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'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) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res.url;
|
||||
};
|
||||
|
|
|
|||
150
src/lib/apis/litellm/index.ts
Normal file
150
src/lib/apis/litellm/index.ts
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { LITELLM_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
export const getLiteLLMModels = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${LITELLM_API_BASE_URL}/v1/models`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = `LiteLLM: ${err?.error?.message ?? 'Network Problem'}`;
|
||||
return [];
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const models = Array.isArray(res) ? res : res?.data ?? null;
|
||||
|
||||
return models
|
||||
? models
|
||||
.map((model) => ({
|
||||
id: model.id,
|
||||
name: model.name ?? model.id,
|
||||
external: true,
|
||||
source: 'litellm'
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
})
|
||||
: models;
|
||||
};
|
||||
|
||||
export const getLiteLLMModelInfo = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${LITELLM_API_BASE_URL}/model/info`, {
|
||||
method: 'GET',
|
||||
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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = `LiteLLM: ${err?.error?.message ?? 'Network Problem'}`;
|
||||
return [];
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const models = Array.isArray(res) ? res : res?.data ?? null;
|
||||
|
||||
return models;
|
||||
};
|
||||
|
||||
type AddLiteLLMModelForm = {
|
||||
name: string;
|
||||
model: string;
|
||||
api_base: string;
|
||||
api_key: string;
|
||||
rpm: string;
|
||||
max_tokens: string;
|
||||
};
|
||||
|
||||
export const addLiteLLMModel = async (token: string = '', payload: AddLiteLLMModelForm) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${LITELLM_API_BASE_URL}/model/new`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model_name: payload.name,
|
||||
litellm_params: {
|
||||
model: payload.model,
|
||||
...(payload.api_base === '' ? {} : { api_base: payload.api_base }),
|
||||
...(payload.api_key === '' ? {} : { api_key: payload.api_key }),
|
||||
...(isNaN(parseInt(payload.rpm)) ? {} : { rpm: parseInt(payload.rpm) }),
|
||||
...(payload.max_tokens === '' ? {} : { max_tokens: payload.max_tokens })
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = `LiteLLM: ${err?.error?.message ?? 'Network Problem'}`;
|
||||
return [];
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const deleteLiteLLMModel = async (token: string = '', id: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${LITELLM_API_BASE_URL}/model/delete`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: id
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = `LiteLLM: ${err?.error?.message ?? 'Network Problem'}`;
|
||||
return [];
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import { OLLAMA_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
export const getOllamaAPIUrl = async (token: string = '') => {
|
||||
export const getOllamaUrls = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/url`, {
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/urls`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -29,13 +29,13 @@ export const getOllamaAPIUrl = async (token: string = '') => {
|
|||
throw error;
|
||||
}
|
||||
|
||||
return res.OLLAMA_API_BASE_URL;
|
||||
return res.OLLAMA_BASE_URLS;
|
||||
};
|
||||
|
||||
export const updateOllamaAPIUrl = async (token: string = '', url: string) => {
|
||||
export const updateOllamaUrls = async (token: string = '', urls: string[]) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/url/update`, {
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/urls/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -43,7 +43,7 @@ export const updateOllamaAPIUrl = async (token: string = '', url: string) => {
|
|||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
url: url
|
||||
urls: urls
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
|
|
@ -64,13 +64,13 @@ export const updateOllamaAPIUrl = async (token: string = '', url: string) => {
|
|||
throw error;
|
||||
}
|
||||
|
||||
return res.OLLAMA_API_BASE_URL;
|
||||
return res.OLLAMA_BASE_URLS;
|
||||
};
|
||||
|
||||
export const getOllamaVersion = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/version`, {
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/version`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -102,7 +102,7 @@ export const getOllamaVersion = async (token: string = '') => {
|
|||
export const getOllamaModels = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/tags`, {
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/tags`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -128,23 +128,36 @@ export const getOllamaModels = async (token: string = '') => {
|
|||
throw error;
|
||||
}
|
||||
|
||||
return (res?.models ?? []).sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
return (res?.models ?? [])
|
||||
.map((model) => ({ id: model.model, name: model.name ?? model.model, ...model }))
|
||||
.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
};
|
||||
|
||||
export const generateTitle = async (token: string = '', model: string, prompt: string) => {
|
||||
// TODO: migrate to backend
|
||||
export const generateTitle = async (
|
||||
token: string = '',
|
||||
template: string,
|
||||
model: string,
|
||||
prompt: string
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/generate`, {
|
||||
template = template.replace(/{{prompt}}/g, prompt);
|
||||
|
||||
console.log(template);
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/generate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
prompt: `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': ${prompt}`,
|
||||
prompt: template,
|
||||
stream: false
|
||||
})
|
||||
})
|
||||
|
|
@ -174,10 +187,11 @@ export const generatePrompt = async (token: string = '', model: string, conversa
|
|||
conversation = '[no existing conversation]';
|
||||
}
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/generate`, {
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/generate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
|
|
@ -205,15 +219,43 @@ export const generatePrompt = async (token: string = '', model: string, conversa
|
|||
return res;
|
||||
};
|
||||
|
||||
export const generateTextCompletion = async (token: string = '', model: string, text: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/generate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
prompt: text,
|
||||
stream: true
|
||||
})
|
||||
}).catch((err) => {
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const generateChatCompletion = async (token: string = '', body: object) => {
|
||||
let controller = new AbortController();
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/chat`, {
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/chat`, {
|
||||
signal: controller.signal,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
|
|
@ -253,10 +295,11 @@ export const cancelChatCompletion = async (token: string = '', requestId: string
|
|||
export const createModel = async (token: string, tagName: string, content: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/create`, {
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
|
|
@ -275,19 +318,23 @@ export const createModel = async (token: string, tagName: string, content: strin
|
|||
return res;
|
||||
};
|
||||
|
||||
export const deleteModel = async (token: string, tagName: string) => {
|
||||
export const deleteModel = async (token: string, tagName: string, urlIdx: string | null = null) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/delete`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: tagName
|
||||
})
|
||||
})
|
||||
const res = await fetch(
|
||||
`${OLLAMA_API_BASE_URL}/api/delete${urlIdx !== null ? `/${urlIdx}` : ''}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: tagName
|
||||
})
|
||||
}
|
||||
)
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
|
|
@ -298,7 +345,12 @@ export const deleteModel = async (token: string, tagName: string) => {
|
|||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err.error;
|
||||
error = err;
|
||||
|
||||
if ('detail' in err) {
|
||||
error = err.detail;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
|
|
@ -309,13 +361,14 @@ export const deleteModel = async (token: string, tagName: string) => {
|
|||
return res;
|
||||
};
|
||||
|
||||
export const pullModel = async (token: string, tagName: string) => {
|
||||
export const pullModel = async (token: string, tagName: string, urlIdx: string | null = null) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/pull`, {
|
||||
const res = await fetch(`${OLLAMA_API_BASE_URL}/api/pull${urlIdx !== null ? `/${urlIdx}` : ''}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { OPENAI_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
export const getOpenAIUrl = async (token: string = '') => {
|
||||
export const getOpenAIUrls = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/url`, {
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/urls`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -29,13 +29,13 @@ export const getOpenAIUrl = async (token: string = '') => {
|
|||
throw error;
|
||||
}
|
||||
|
||||
return res.OPENAI_API_BASE_URL;
|
||||
return res.OPENAI_API_BASE_URLS;
|
||||
};
|
||||
|
||||
export const updateOpenAIUrl = async (token: string = '', url: string) => {
|
||||
export const updateOpenAIUrls = async (token: string = '', urls: string[]) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/url/update`, {
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/urls/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -43,7 +43,7 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => {
|
|||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
url: url
|
||||
urls: urls
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
|
|
@ -64,13 +64,13 @@ export const updateOpenAIUrl = async (token: string = '', url: string) => {
|
|||
throw error;
|
||||
}
|
||||
|
||||
return res.OPENAI_API_BASE_URL;
|
||||
return res.OPENAI_API_BASE_URLS;
|
||||
};
|
||||
|
||||
export const getOpenAIKey = async (token: string = '') => {
|
||||
export const getOpenAIKeys = async (token: string = '') => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/key`, {
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/keys`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -96,13 +96,13 @@ export const getOpenAIKey = async (token: string = '') => {
|
|||
throw error;
|
||||
}
|
||||
|
||||
return res.OPENAI_API_KEY;
|
||||
return res.OPENAI_API_KEYS;
|
||||
};
|
||||
|
||||
export const updateOpenAIKey = async (token: string = '', key: string) => {
|
||||
export const updateOpenAIKeys = async (token: string = '', keys: string[]) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/key/update`, {
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/keys/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
|
|
@ -110,7 +110,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
|
|||
...(token && { authorization: `Bearer ${token}` })
|
||||
},
|
||||
body: JSON.stringify({
|
||||
key: key
|
||||
keys: keys
|
||||
})
|
||||
})
|
||||
.then(async (res) => {
|
||||
|
|
@ -131,7 +131,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
|
|||
throw error;
|
||||
}
|
||||
|
||||
return res.OPENAI_API_KEY;
|
||||
return res.OPENAI_API_KEYS;
|
||||
};
|
||||
|
||||
export const getOpenAIModels = async (token: string = '') => {
|
||||
|
|
@ -150,7 +150,6 @@ export const getOpenAIModels = async (token: string = '') => {
|
|||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = `OpenAI: ${err?.error?.message ?? 'Network Problem'}`;
|
||||
return [];
|
||||
});
|
||||
|
|
@ -163,7 +162,7 @@ export const getOpenAIModels = async (token: string = '') => {
|
|||
|
||||
return models
|
||||
? models
|
||||
.map((model) => ({ name: model.id, external: true }))
|
||||
.map((model) => ({ id: model.id, name: model.name ?? model.id, external: true }))
|
||||
.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
})
|
||||
|
|
@ -200,17 +199,21 @@ export const getOpenAIModelsDirect = async (
|
|||
const models = Array.isArray(res) ? res : res?.data ?? null;
|
||||
|
||||
return models
|
||||
.map((model) => ({ name: model.id, external: true }))
|
||||
.map((model) => ({ id: model.id, name: model.name ?? model.id, external: true }))
|
||||
.filter((model) => (base_url.includes('openai') ? model.name.includes('gpt') : true))
|
||||
.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
};
|
||||
|
||||
export const generateOpenAIChatCompletion = async (token: string = '', body: object) => {
|
||||
export const generateOpenAIChatCompletion = async (
|
||||
token: string = '',
|
||||
body: object,
|
||||
url: string = OPENAI_API_BASE_URL
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${OPENAI_API_BASE_URL}/chat/completions`, {
|
||||
const res = await fetch(`${url}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,161 @@
|
|||
import { RAG_API_BASE_URL } from '$lib/constants';
|
||||
|
||||
export const getRAGConfig = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${RAG_API_BASE_URL}/config`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.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;
|
||||
};
|
||||
|
||||
type ChunkConfigForm = {
|
||||
chunk_size: number;
|
||||
chunk_overlap: number;
|
||||
};
|
||||
|
||||
type RAGConfigForm = {
|
||||
pdf_extract_images: boolean;
|
||||
chunk: ChunkConfigForm;
|
||||
};
|
||||
|
||||
export const updateRAGConfig = async (token: string, payload: RAGConfigForm) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${RAG_API_BASE_URL}/config/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...payload
|
||||
})
|
||||
})
|
||||
.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 getRAGTemplate = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${RAG_API_BASE_URL}/template`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.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?.template ?? '';
|
||||
};
|
||||
|
||||
export const getQuerySettings = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${RAG_API_BASE_URL}/query/settings`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.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;
|
||||
};
|
||||
|
||||
type QuerySettings = {
|
||||
k: number | null;
|
||||
template: string | null;
|
||||
};
|
||||
|
||||
export const updateQuerySettings = async (token: string, settings: QuerySettings) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${RAG_API_BASE_URL}/query/settings/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...settings
|
||||
})
|
||||
})
|
||||
.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 uploadDocToVectorDB = async (token: string, collection_name: string, file: File) => {
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
|
|
@ -68,7 +224,7 @@ export const queryDoc = async (
|
|||
token: string,
|
||||
collection_name: string,
|
||||
query: string,
|
||||
k: number
|
||||
k: number | null = null
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
|
|
@ -105,7 +261,7 @@ export const queryCollection = async (
|
|||
token: string,
|
||||
collection_names: string,
|
||||
query: string,
|
||||
k: number
|
||||
k: number | null = null
|
||||
) => {
|
||||
let error = null;
|
||||
|
||||
|
|
@ -138,6 +294,32 @@ export const queryCollection = async (
|
|||
return res;
|
||||
};
|
||||
|
||||
export const scanDocs = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${RAG_API_BASE_URL}/scan`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
error = err.detail;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const resetVectorDB = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,3 +21,35 @@ export const getGravatarUrl = async (email: string) => {
|
|||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const downloadDatabase = async (token: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/utils/db/download`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then((blob) => {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'webui.db';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
error = err;
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
<script>
|
||||
import { getContext } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
</script>
|
||||
|
||||
<div class=" text-center text-6xl mb-3">📄</div>
|
||||
<div class="text-center dark:text-white text-2xl font-semibold z-50">Add Files</div>
|
||||
<div class="text-center dark:text-white text-2xl font-semibold z-50">{$i18n.t('Add Files')}</div>
|
||||
|
||||
<slot
|
||||
><div class=" mt-2 text-center text-sm dark:text-gray-200 w-full">
|
||||
Drop any files here to add to the conversation
|
||||
{$i18n.t('Drop any files here to add to the conversation')}
|
||||
</div>
|
||||
</slot>
|
||||
|
|
|
|||
118
src/lib/components/ChangelogModal.svelte
Normal file
118
src/lib/components/ChangelogModal.svelte
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<script lang="ts">
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { Confetti } from 'svelte-confetti';
|
||||
|
||||
import { WEBUI_NAME, config } from '$lib/stores';
|
||||
|
||||
import { WEBUI_VERSION } from '$lib/constants';
|
||||
import { getChangelog } from '$lib/apis';
|
||||
|
||||
import Modal from './common/Modal.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
|
||||
let changelog = null;
|
||||
|
||||
onMount(async () => {
|
||||
const res = await getChangelog();
|
||||
changelog = res;
|
||||
});
|
||||
</script>
|
||||
|
||||
<Modal bind:show>
|
||||
<div class="px-5 py-4 dark:text-gray-300">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="text-xl font-bold">
|
||||
{$i18n.t('What’s New in')}
|
||||
{$WEBUI_NAME}
|
||||
<Confetti x={[-1, -0.25]} y={[0, 0.5]} />
|
||||
</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 items-center mt-1">
|
||||
<div class="text-sm dark:text-gray-200">{$i18n.t('Release Notes')}</div>
|
||||
<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-200 dark:bg-gray-700" />
|
||||
<div class="text-sm dark:text-gray-200">
|
||||
v{WEBUI_VERSION}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-800" />
|
||||
|
||||
<div class=" w-full p-4 px-5">
|
||||
<div class=" overflow-y-scroll max-h-80">
|
||||
<div class="mb-3">
|
||||
{#if changelog}
|
||||
{#each Object.keys(changelog) as version}
|
||||
<div class=" mb-3 pr-2">
|
||||
<div class="font-bold text-xl mb-1 dark:text-white">
|
||||
v{version} - {changelog[version].date}
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-800 my-2" />
|
||||
|
||||
{#each Object.keys(changelog[version]).filter((section) => section !== 'date') as section}
|
||||
<div class="">
|
||||
<div
|
||||
class="font-bold uppercase text-xs {section === 'added'
|
||||
? 'text-white bg-blue-600'
|
||||
: section === 'fixed'
|
||||
? 'text-white bg-green-600'
|
||||
: section === 'changed'
|
||||
? 'text-white bg-yellow-600'
|
||||
: section === 'removed'
|
||||
? 'text-white bg-red-600'
|
||||
: ''} w-fit px-3 rounded-full my-2.5"
|
||||
>
|
||||
{section}
|
||||
</div>
|
||||
|
||||
<div class="my-2.5 px-1.5">
|
||||
{#each Object.keys(changelog[version][section]) as item}
|
||||
<div class="text-sm mb-2">
|
||||
<div class="font-semibold uppercase">
|
||||
{changelog[version][section][item].title}
|
||||
</div>
|
||||
<div class="mb-2 mt-1">{changelog[version][section][item].content}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
localStorage.version = $config.version;
|
||||
show = false;
|
||||
}}
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
>
|
||||
<span class="relative">{$i18n.t("Okay, Let's Go!")}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import dayjs from 'dayjs';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
import { updateUserById } from '$lib/apis/users';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let show = false;
|
||||
|
|
@ -42,7 +43,7 @@
|
|||
<Modal size="sm" bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
|
||||
<div class=" text-lg font-medium self-center">Edit User</div>
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Edit User')}</div>
|
||||
<button
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
|
|
@ -84,7 +85,8 @@
|
|||
<div class=" self-center capitalize font-semibold">{selectedUser.name}</div>
|
||||
|
||||
<div class="text-xs text-gray-500">
|
||||
Created at {dayjs(selectedUser.timestamp * 1000).format('MMMM DD, YYYY')}
|
||||
{$i18n.t('Created at')}
|
||||
{dayjs(selectedUser.timestamp * 1000).format($i18n.t('MMMM DD, YYYY'))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -93,7 +95,7 @@
|
|||
|
||||
<div class=" flex flex-col space-y-1.5">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Email</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Email')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
|
@ -108,7 +110,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Name</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
|
@ -122,7 +124,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">New Password</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('New Password')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
|
@ -140,7 +142,7 @@
|
|||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
65
src/lib/components/admin/Settings/Database.svelte
Normal file
65
src/lib/components/admin/Settings/Database.svelte
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<script lang="ts">
|
||||
import { downloadDatabase } from '$lib/apis/utils';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveHandler: Function;
|
||||
|
||||
onMount(async () => {
|
||||
// permissions = await getUserPermissions(localStorage.token);
|
||||
});
|
||||
</script>
|
||||
|
||||
<form
|
||||
class="flex flex-col h-full justify-between space-y-3 text-sm"
|
||||
on:submit|preventDefault={async () => {
|
||||
saveHandler();
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('Database')}</div>
|
||||
|
||||
<div class=" flex w-full justify-between">
|
||||
<!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
|
||||
|
||||
<button
|
||||
class=" flex rounded-md py-1.5 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
// exportAllUserChats();
|
||||
|
||||
downloadDatabase(localStorage.token);
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Download Database')}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div> -->
|
||||
</form>
|
||||
|
|
@ -1,15 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { getWebhookUrl, updateWebhookUrl } from '$lib/apis';
|
||||
import {
|
||||
getDefaultUserRole,
|
||||
getJWTExpiresDuration,
|
||||
getSignUpEnabledStatus,
|
||||
toggleSignUpEnabledStatus,
|
||||
updateDefaultUserRole
|
||||
updateDefaultUserRole,
|
||||
updateJWTExpiresDuration
|
||||
} from '$lib/apis/auths';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveHandler: Function;
|
||||
let signUpEnabled = true;
|
||||
let defaultUserRole = 'pending';
|
||||
let JWTExpiresIn = '';
|
||||
|
||||
let webhookUrl = '';
|
||||
|
||||
const toggleSignUpEnabled = async () => {
|
||||
signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
|
||||
|
|
@ -19,25 +27,36 @@
|
|||
defaultUserRole = await updateDefaultUserRole(localStorage.token, role);
|
||||
};
|
||||
|
||||
const updateJWTExpiresDurationHandler = async (duration) => {
|
||||
JWTExpiresIn = await updateJWTExpiresDuration(localStorage.token, duration);
|
||||
};
|
||||
|
||||
const updateWebhookUrlHandler = async () => {
|
||||
webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
|
||||
defaultUserRole = await getDefaultUserRole(localStorage.token);
|
||||
JWTExpiresIn = await getJWTExpiresDuration(localStorage.token);
|
||||
webhookUrl = await getWebhookUrl(localStorage.token);
|
||||
});
|
||||
</script>
|
||||
|
||||
<form
|
||||
class="flex flex-col h-full justify-between space-y-3 text-sm"
|
||||
on:submit|preventDefault={() => {
|
||||
// console.log('submit');
|
||||
updateJWTExpiresDurationHandler(JWTExpiresIn);
|
||||
updateWebhookUrlHandler();
|
||||
saveHandler();
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">General Settings</div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
|
||||
|
||||
<div class=" flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Enable New Sign Ups</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Enable New Sign Ups')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -57,7 +76,7 @@
|
|||
d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="ml-2 self-center">Enabled</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Enabled')}</span>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -72,28 +91,69 @@
|
|||
/>
|
||||
</svg>
|
||||
|
||||
<span class="ml-2 self-center">Disabled</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Disabled')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class=" flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Default User Role</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Default User Role')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class="w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
|
||||
class="dark:bg-gray-900 w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={defaultUserRole}
|
||||
placeholder="Select a theme"
|
||||
on:change={(e) => {
|
||||
updateDefaultUserRoleHandler(e.target.value);
|
||||
}}
|
||||
>
|
||||
<option value="pending">Pending</option>
|
||||
<option value="user">User</option>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="pending">{$i18n.t('pending')}</option>
|
||||
<option value="user">{$i18n.t('user')}</option>
|
||||
<option value="admin">{$i18n.t('admin')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-700 my-3" />
|
||||
|
||||
<div class=" w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Webhook URL')}</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-2 space-x-2">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="text"
|
||||
placeholder={`https://example.com/webhook`}
|
||||
bind:value={webhookUrl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-700 my-3" />
|
||||
|
||||
<div class=" w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('JWT Expiration')}</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-2 space-x-2">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="text"
|
||||
placeholder={`e.g.) "30m","1h", "10d". `}
|
||||
bind:value={JWTExpiresIn}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('Valid time units:')}
|
||||
<span class=" text-gray-300 font-medium"
|
||||
>{$i18n.t("'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.")}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -102,7 +162,7 @@
|
|||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { getModelFilterConfig, updateModelFilterConfig } from '$lib/apis';
|
||||
import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
|
||||
import { getUserPermissions, updateUserPermissions } from '$lib/apis/users';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { models } from '$lib/stores';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveHandler: Function;
|
||||
|
||||
let whitelistEnabled = false;
|
||||
let whitelistModels = [''];
|
||||
let permissions = {
|
||||
chat: {
|
||||
deletion: true
|
||||
|
|
@ -13,6 +20,13 @@
|
|||
|
||||
onMount(async () => {
|
||||
permissions = await getUserPermissions(localStorage.token);
|
||||
|
||||
const res = await getModelFilterConfig(localStorage.token);
|
||||
if (res) {
|
||||
whitelistEnabled = res.enabled;
|
||||
|
||||
whitelistModels = res.models.length > 0 ? res.models : [''];
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -21,15 +35,17 @@
|
|||
on:submit|preventDefault={async () => {
|
||||
// console.log('submit');
|
||||
await updateUserPermissions(localStorage.token, permissions);
|
||||
|
||||
await updateModelFilterConfig(localStorage.token, whitelistEnabled, whitelistModels);
|
||||
saveHandler();
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">User Permissions</div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('User Permissions')}</div>
|
||||
|
||||
<div class=" flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Allow Chat Deletion</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -49,7 +65,7 @@
|
|||
d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="ml-2 self-center">Allow</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Allow')}</span>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -64,11 +80,112 @@
|
|||
/>
|
||||
</svg>
|
||||
|
||||
<span class="ml-2 self-center">Don't Allow</span>
|
||||
<span class="ml-2 self-center">{$i18n.t("Don't Allow")}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-700 my-2" />
|
||||
|
||||
<div class="mt-2 space-y-3 pr-1.5">
|
||||
<div>
|
||||
<div class="mb-2">
|
||||
<div class="flex justify-between items-center text-xs">
|
||||
<div class=" text-sm font-medium">{$i18n.t('Manage Models')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" space-y-3">
|
||||
<div>
|
||||
<div class="flex justify-between items-center text-xs">
|
||||
<div class=" text-xs font-medium">{$i18n.t('Model Whitelisting')}</div>
|
||||
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
whitelistEnabled = !whitelistEnabled;
|
||||
}}>{whitelistEnabled ? $i18n.t('On') : $i18n.t('Off')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if whitelistEnabled}
|
||||
<div>
|
||||
<div class=" space-y-1.5">
|
||||
{#each whitelistModels as modelId, modelIdx}
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={modelId}
|
||||
placeholder="Select a model"
|
||||
>
|
||||
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
|
||||
{#each $models.filter((model) => model.id) as model}
|
||||
<option value={model.id} class="bg-gray-100 dark:bg-gray-700"
|
||||
>{model.name}</option
|
||||
>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{#if modelIdx === 0}
|
||||
<button
|
||||
class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-900 dark:text-white rounded-lg transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
if (whitelistModels.at(-1) !== '') {
|
||||
whitelistModels = [...whitelistModels, ''];
|
||||
}
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
{:else}
|
||||
<button
|
||||
class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-900 dark:text-white rounded-lg transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
whitelistModels.splice(modelIdx, 1);
|
||||
whitelistModels = whitelistModels;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end items-center text-xs mt-1.5 text-right">
|
||||
<div class=" text-xs font-medium">
|
||||
{whitelistModels.length}
|
||||
{$i18n.t('Model(s) Whitelisted')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
|
|
@ -76,7 +193,7 @@
|
|||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
<script>
|
||||
import { getContext } from 'svelte';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import Database from './Settings/Database.svelte';
|
||||
|
||||
import General from './Settings/General.svelte';
|
||||
import Users from './Settings/Users.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
|
||||
let selectedTab = 'general';
|
||||
|
|
@ -12,7 +16,7 @@
|
|||
<Modal bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
|
||||
<div class=" text-lg font-medium self-center">Admin Settings</div>
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Admin Settings')}</div>
|
||||
<button
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
|
|
@ -60,7 +64,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">General</div>
|
||||
<div class=" self-center">{$i18n.t('General')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -84,7 +88,35 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">Users</div>
|
||||
<div class=" self-center">{$i18n.t('Users')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||
'db'
|
||||
? 'bg-gray-200 dark:bg-gray-700'
|
||||
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'db';
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path d="M8 7c3.314 0 6-1.343 6-3s-2.686-3-6-3-6 1.343-6 3 2.686 3 6 3Z" />
|
||||
<path
|
||||
d="M8 8.5c1.84 0 3.579-.37 4.914-1.037A6.33 6.33 0 0 0 14 6.78V8c0 1.657-2.686 3-6 3S2 9.657 2 8V6.78c.346.273.72.5 1.087.683C4.42 8.131 6.16 8.5 8 8.5Z"
|
||||
/>
|
||||
<path
|
||||
d="M8 12.5c1.84 0 3.579-.37 4.914-1.037.366-.183.74-.41 1.086-.684V12c0 1.657-2.686 3-6 3s-6-1.343-6-3v-1.22c.346.273.72.5 1.087.683C4.42 12.131 6.16 12.5 8 12.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">{$i18n.t('Database')}</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1 md:min-h-[380px]">
|
||||
|
|
@ -100,6 +132,12 @@
|
|||
show = false;
|
||||
}}
|
||||
/>
|
||||
{:else if selectedTab === 'db'}
|
||||
<Database
|
||||
saveHandler={() => {
|
||||
show = false;
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { onMount, tick, getContext } from 'svelte';
|
||||
import { settings } from '$lib/stores';
|
||||
import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
|
||||
|
||||
|
|
@ -12,13 +12,16 @@
|
|||
import Documents from './MessageInput/Documents.svelte';
|
||||
import Models from './MessageInput/Models.svelte';
|
||||
import { transcribeAudio } from '$lib/apis/audio';
|
||||
import Tooltip from '../common/Tooltip.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let submitPrompt: Function;
|
||||
export let stopResponse: Function;
|
||||
|
||||
export let suggestionPrompts = [];
|
||||
export let autoScroll = true;
|
||||
|
||||
let chatTextAreaElement: HTMLTextAreaElement;
|
||||
let filesInputElement;
|
||||
|
||||
let promptsElement;
|
||||
|
|
@ -42,11 +45,9 @@
|
|||
let speechRecognition;
|
||||
|
||||
$: if (prompt) {
|
||||
const chatInput = document.getElementById('chat-textarea');
|
||||
|
||||
if (chatInput) {
|
||||
chatInput.style.height = '';
|
||||
chatInput.style.height = Math.min(chatInput.scrollHeight, 200) + 'px';
|
||||
if (chatTextAreaElement) {
|
||||
chatTextAreaElement.style.height = '';
|
||||
chatTextAreaElement.style.height = Math.min(chatTextAreaElement.scrollHeight, 200) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,9 +86,7 @@
|
|||
if (res) {
|
||||
prompt = res.text;
|
||||
await tick();
|
||||
|
||||
const inputElement = document.getElementById('chat-textarea');
|
||||
inputElement?.focus();
|
||||
chatTextAreaElement?.focus();
|
||||
|
||||
if (prompt !== '' && $settings?.speechAutoSend === true) {
|
||||
submitPrompt(prompt, user);
|
||||
|
|
@ -190,8 +189,7 @@
|
|||
prompt = `${prompt}${transcript}`;
|
||||
|
||||
await tick();
|
||||
const inputElement = document.getElementById('chat-textarea');
|
||||
inputElement?.focus();
|
||||
chatTextAreaElement?.focus();
|
||||
|
||||
// Restart the inactivity timeout
|
||||
timeoutId = setTimeout(() => {
|
||||
|
|
@ -213,11 +211,11 @@
|
|||
// Event triggered when an error occurs
|
||||
speechRecognition.onerror = function (event) {
|
||||
console.log(event);
|
||||
toast.error(`Speech recognition error: ${event.error}`);
|
||||
toast.error($i18n.t(`Speech recognition error: {{error}}`, { error: event.error }));
|
||||
isRecording = false;
|
||||
};
|
||||
} else {
|
||||
toast.error('SpeechRecognition API is not supported in this browser.');
|
||||
toast.error($i18n.t('SpeechRecognition API is not supported in this browser.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -293,6 +291,8 @@
|
|||
};
|
||||
|
||||
onMount(() => {
|
||||
window.setTimeout(() => chatTextAreaElement?.focus(), 0);
|
||||
|
||||
const dropZone = document.querySelector('body');
|
||||
|
||||
const onDragOver = (e) => {
|
||||
|
|
@ -335,12 +335,15 @@
|
|||
uploadDoc(file);
|
||||
} else {
|
||||
toast.error(
|
||||
`Unknown File Type '${file['type']}', but accepting and treating as plain text`
|
||||
$i18n.t(
|
||||
`Unknown File Type '{{file_type}}', but accepting and treating as plain text`,
|
||||
{ file_type: file['type'] }
|
||||
)
|
||||
);
|
||||
uploadDoc(file);
|
||||
}
|
||||
} else {
|
||||
toast.error(`File not found.`);
|
||||
toast.error($i18n.t(`File not found.`));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -361,12 +364,12 @@
|
|||
|
||||
{#if dragged}
|
||||
<div
|
||||
class="fixed w-full h-full flex z-50 touch-none pointer-events-none"
|
||||
class="fixed lg:w-[calc(100%-260px)] w-full h-full flex z-50 touch-none pointer-events-none"
|
||||
id="dropzone"
|
||||
role="region"
|
||||
aria-label="Drag and Drop Container"
|
||||
>
|
||||
<div class="absolute rounded-xl w-full h-full backdrop-blur bg-gray-800/40 flex justify-center">
|
||||
<div class="absolute w-full h-full backdrop-blur bg-gray-800/40 flex justify-center">
|
||||
<div class="m-auto pt-64 flex flex-col justify-center">
|
||||
<div class="max-w-md">
|
||||
<AddFilesPlaceholder />
|
||||
|
|
@ -479,18 +482,21 @@
|
|||
filesInputElement.value = '';
|
||||
} else {
|
||||
toast.error(
|
||||
`Unknown File Type '${file['type']}', but accepting and treating as plain text`
|
||||
$i18n.t(
|
||||
`Unknown File Type '{{file_type}}', but accepting and treating as plain text`,
|
||||
{ file_type: file['type'] }
|
||||
)
|
||||
);
|
||||
uploadDoc(file);
|
||||
filesInputElement.value = '';
|
||||
}
|
||||
} else {
|
||||
toast.error(`File not found.`);
|
||||
toast.error($i18n.t(`File not found.`));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<form
|
||||
class=" flex flex-col relative w-full rounded-xl border dark:border-gray-700 bg-white dark:bg-gray-900 dark:text-gray-100"
|
||||
class=" flex flex-col relative w-full rounded-3xl px-1.5 border border-gray-100 dark:border-gray-850 bg-white dark:bg-gray-900 dark:text-gray-100"
|
||||
on:submit|preventDefault={() => {
|
||||
submitPrompt(prompt, user);
|
||||
}}
|
||||
|
|
@ -572,7 +578,7 @@
|
|||
{file.name}
|
||||
</div>
|
||||
|
||||
<div class=" text-gray-500 text-sm">Document</div>
|
||||
<div class=" text-gray-500 text-sm">{$i18n.t('Document')}</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if file.type === 'collection'}
|
||||
|
|
@ -600,7 +606,7 @@
|
|||
{file?.title ?? `#${file.name}`}
|
||||
</div>
|
||||
|
||||
<div class=" text-gray-500 text-sm">Collection</div>
|
||||
<div class=" text-gray-500 text-sm">{$i18n.t('Collection')}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -633,40 +639,41 @@
|
|||
|
||||
<div class=" flex">
|
||||
{#if fileUploadEnabled}
|
||||
<div class=" self-end mb-2 ml-1.5">
|
||||
<button
|
||||
class=" text-gray-600 dark:text-gray-200 transition rounded-lg p-1 ml-1"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
filesInputElement.click();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-5 h-5"
|
||||
<div class=" self-end mb-2 ml-1">
|
||||
<Tooltip content={$i18n.t('Upload files')}>
|
||||
<button
|
||||
class="bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
filesInputElement.click();
|
||||
}}
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M15.621 4.379a3 3 0 00-4.242 0l-7 7a3 3 0 004.241 4.243h.001l.497-.5a.75.75 0 011.064 1.057l-.498.501-.002.002a4.5 4.5 0 01-6.364-6.364l7-7a4.5 4.5 0 016.368 6.36l-3.455 3.553A2.625 2.625 0 119.52 9.52l3.45-3.451a.75.75 0 111.061 1.06l-3.45 3.451a1.125 1.125 0 001.587 1.595l3.454-3.553a3 3 0 000-4.242z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-[1.2rem] h-[1.2rem]"
|
||||
>
|
||||
<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>
|
||||
{/if}
|
||||
|
||||
<textarea
|
||||
id="chat-textarea"
|
||||
class=" dark:bg-gray-900 dark:text-gray-100 outline-none w-full py-3 px-2 {fileUploadEnabled
|
||||
bind:this={chatTextAreaElement}
|
||||
class=" dark:bg-gray-900 dark:text-gray-100 outline-none w-full py-3 px-3 {fileUploadEnabled
|
||||
? ''
|
||||
: ' pl-4'} rounded-xl resize-none h-[48px]"
|
||||
placeholder={chatInputPlaceholder !== ''
|
||||
? chatInputPlaceholder
|
||||
: isRecording
|
||||
? 'Listening...'
|
||||
: 'Send a message'}
|
||||
? $i18n.t('Listening...')
|
||||
: $i18n.t('Send a Message')}
|
||||
bind:value={prompt}
|
||||
on:keypress={(e) => {
|
||||
if (e.keyCode == 13 && !e.shiftKey) {
|
||||
|
|
@ -803,92 +810,102 @@
|
|||
}}
|
||||
/>
|
||||
|
||||
<div class="self-end mb-2 flex space-x-0.5 mr-2">
|
||||
<div class="self-end mb-2 flex space-x-1 mr-1">
|
||||
{#if messages.length == 0 || messages.at(-1).done == true}
|
||||
{#if speechRecognitionEnabled}
|
||||
<Tooltip content={$i18n.t('Record voice')}>
|
||||
{#if speechRecognitionEnabled}
|
||||
<button
|
||||
id="voice-input-button"
|
||||
class=" text-gray-600 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-850 transition rounded-full p-1.5 mr-0.5 self-center"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
speechRecognitionHandler();
|
||||
}}
|
||||
>
|
||||
{#if isRecording}
|
||||
<svg
|
||||
class=" w-5 h-5 translate-y-[0.5px]"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_qM83 {
|
||||
animation: spinner_8HQG 1.05s infinite;
|
||||
}
|
||||
.spinner_oXPr {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.spinner_ZTLf {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
@keyframes spinner_8HQG {
|
||||
0%,
|
||||
57.14% {
|
||||
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
||||
transform: translate(0);
|
||||
}
|
||||
28.57% {
|
||||
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(0);
|
||||
}
|
||||
}
|
||||
</style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle
|
||||
class="spinner_qM83 spinner_oXPr"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="2.5"
|
||||
/><circle
|
||||
class="spinner_qM83 spinner_ZTLf"
|
||||
cx="20"
|
||||
cy="12"
|
||||
r="2.5"
|
||||
/></svg
|
||||
>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-5 h-5 translate-y-[0.5px]"
|
||||
>
|
||||
<path d="M7 4a3 3 0 016 0v6a3 3 0 11-6 0V4z" />
|
||||
<path
|
||||
d="M5.5 9.643a.75.75 0 00-1.5 0V10c0 3.06 2.29 5.585 5.25 5.954V17.5h-1.5a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-1.5v-1.546A6.001 6.001 0 0016 10v-.357a.75.75 0 00-1.5 0V10a4.5 4.5 0 01-9 0v-.357z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content={$i18n.t('Send message')}>
|
||||
<button
|
||||
id="voice-input-button"
|
||||
class=" text-gray-600 dark:text-gray-300 transition rounded-lg p-1.5 mr-0.5 self-center"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
speechRecognitionHandler();
|
||||
}}
|
||||
class="{prompt !== ''
|
||||
? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
|
||||
: 'text-white bg-gray-100 dark:text-gray-900 dark:bg-gray-800 disabled'} transition rounded-full p-1.5 self-center"
|
||||
type="submit"
|
||||
disabled={prompt === ''}
|
||||
>
|
||||
{#if isRecording}
|
||||
<svg
|
||||
class=" w-5 h-5 translate-y-[0.5px]"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_qM83 {
|
||||
animation: spinner_8HQG 1.05s infinite;
|
||||
}
|
||||
.spinner_oXPr {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.spinner_ZTLf {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
@keyframes spinner_8HQG {
|
||||
0%,
|
||||
57.14% {
|
||||
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
||||
transform: translate(0);
|
||||
}
|
||||
28.57% {
|
||||
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(0);
|
||||
}
|
||||
}
|
||||
</style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle
|
||||
class="spinner_qM83 spinner_oXPr"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="2.5"
|
||||
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg
|
||||
>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-5 h-5 translate-y-[0.5px]"
|
||||
>
|
||||
<path d="M7 4a3 3 0 016 0v6a3 3 0 11-6 0V4z" />
|
||||
<path
|
||||
d="M5.5 9.643a.75.75 0 00-1.5 0V10c0 3.06 2.29 5.585 5.25 5.954V17.5h-1.5a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-1.5v-1.546A6.001 6.001 0 0016 10v-.357a.75.75 0 00-1.5 0V10a4.5 4.5 0 01-9 0v-.357z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<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>
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
class="{prompt !== ''
|
||||
? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
|
||||
: 'text-white bg-gray-100 dark:text-gray-900 dark:bg-gray-800 disabled'} transition rounded-lg p-1 mr-0.5 w-7 h-7 self-center"
|
||||
type="submit"
|
||||
disabled={prompt === ''}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4.5 h-4.5 mx-auto"
|
||||
>
|
||||
<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>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<button
|
||||
class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-lg p-1.5"
|
||||
class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
|
||||
on:click={stopResponse}
|
||||
>
|
||||
<svg
|
||||
|
|
@ -910,7 +927,7 @@
|
|||
</form>
|
||||
|
||||
<div class="mt-1.5 text-xs text-gray-500 text-center">
|
||||
LLMs can make mistakes. Verify important information.
|
||||
{$i18n.t('LLMs can make mistakes. Verify important information.')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
import { documents } from '$lib/stores';
|
||||
import { removeFirstHashWord, isValidHttpUrl } from '$lib/utils';
|
||||
import { tick } from 'svelte';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { tick, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let prompt = '';
|
||||
|
||||
|
|
@ -89,16 +91,16 @@
|
|||
|
||||
{#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
|
||||
<div class="md:px-2 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
|
||||
<div class="flex w-full rounded-lg border border-gray-100 dark:border-gray-700">
|
||||
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||
<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-lg">
|
||||
<div class=" overflow-y-auto bg-white p-2 rounded-tr-lg space-y-0.5">
|
||||
<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 filteredItems as doc, docIdx}
|
||||
<button
|
||||
class=" px-3 py-1.5 rounded-lg w-full text-left {docIdx === selectedIdx
|
||||
class=" px-3 py-1.5 rounded-xl w-full text-left {docIdx === selectedIdx
|
||||
? ' bg-gray-100 selected-command-option-button'
|
||||
: ''}"
|
||||
type="button"
|
||||
|
|
@ -117,7 +119,7 @@
|
|||
{doc?.title ?? `#${doc.name}`}
|
||||
</div>
|
||||
|
||||
<div class=" text-xs text-gray-600 line-clamp-1">Collection</div>
|
||||
<div class=" text-xs text-gray-600 line-clamp-1">{$i18n.t('Collection')}</div>
|
||||
{:else}
|
||||
<div class=" font-medium text-black line-clamp-1">
|
||||
#{doc.name} ({doc.filename})
|
||||
|
|
@ -132,7 +134,7 @@
|
|||
|
||||
{#if prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
|
||||
<button
|
||||
class="px-3 py-1.5 rounded-lg w-full text-left bg-gray-100 selected-command-option-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);
|
||||
|
|
@ -140,7 +142,9 @@
|
|||
confirmSelectWeb(url);
|
||||
} else {
|
||||
toast.error(
|
||||
'Oops! Looks like the URL is invalid. Please double-check and try again.'
|
||||
$i18n.t(
|
||||
'Oops! Looks like the URL is invalid. Please double-check and try again.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
|
|
@ -149,7 +153,7 @@
|
|||
{prompt.split(' ')?.at(0)?.substring(1)}
|
||||
</div>
|
||||
|
||||
<div class=" text-xs text-gray-600 line-clamp-1">Web</div>
|
||||
<div class=" text-xs text-gray-600 line-clamp-1">{$i18n.t('Web')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
import { generatePrompt } from '$lib/apis/ollama';
|
||||
import { models } from '$lib/stores';
|
||||
import { splitStream } from '$lib/utils';
|
||||
import { tick } from 'svelte';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { tick, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let prompt = '';
|
||||
export let user = null;
|
||||
|
|
@ -41,7 +43,7 @@
|
|||
user = JSON.parse(JSON.stringify(model.name));
|
||||
await tick();
|
||||
|
||||
chatInputPlaceholder = `'${model.name}' is thinking...`;
|
||||
chatInputPlaceholder = $i18n.t('{{modelName}} is thinking...', { modelName: model.name });
|
||||
|
||||
const chatInputElement = document.getElementById('chat-textarea');
|
||||
|
||||
|
|
@ -79,14 +81,18 @@
|
|||
throw data;
|
||||
}
|
||||
|
||||
if (data.done == false) {
|
||||
if (prompt == '' && data.response == '\n') {
|
||||
continue;
|
||||
} else {
|
||||
prompt += data.response;
|
||||
console.log(data.response);
|
||||
chatInputElement.scrollTop = chatInputElement.scrollHeight;
|
||||
await tick();
|
||||
if ('id' in data) {
|
||||
console.log(data);
|
||||
} else {
|
||||
if (data.done == false) {
|
||||
if (prompt == '' && data.response == '\n') {
|
||||
continue;
|
||||
} else {
|
||||
prompt += data.response;
|
||||
console.log(data.response);
|
||||
chatInputElement.scrollTop = chatInputElement.scrollHeight;
|
||||
await tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,7 +115,9 @@
|
|||
toast.error(error.error);
|
||||
}
|
||||
} else {
|
||||
toast.error(`Uh-oh! There was an issue connecting to Ollama.`);
|
||||
toast.error(
|
||||
$i18n.t('Uh-oh! There was an issue connecting to {{provider}}.', { provider: 'llama' })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,16 +129,16 @@
|
|||
|
||||
{#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 rounded-lg border border-gray-100 dark:border-gray-700">
|
||||
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||
<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-lg">
|
||||
<div class=" overflow-y-auto bg-white p-2 rounded-tr-lg space-y-0.5">
|
||||
<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-lg w-full text-left {modelIdx === selectedIdx
|
||||
class=" px-3 py-1.5 rounded-xl w-full text-left {modelIdx === selectedIdx
|
||||
? ' bg-gray-100 selected-command-option-button'
|
||||
: ''}"
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { prompts } from '$lib/stores';
|
||||
import { findWordIndices } from '$lib/utils';
|
||||
import { tick } from 'svelte';
|
||||
import { tick, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let prompt = '';
|
||||
let selectedCommandIdx = 0;
|
||||
|
|
@ -24,7 +27,18 @@
|
|||
};
|
||||
|
||||
const confirmCommand = async (command) => {
|
||||
prompt = command.content;
|
||||
let text = command.content;
|
||||
|
||||
if (command.content.includes('{{CLIPBOARD}}')) {
|
||||
const clipboardText = await navigator.clipboard.readText().catch((err) => {
|
||||
toast.error($i18n.t('Failed to read clipboard contents'));
|
||||
return '{{CLIPBOARD}}';
|
||||
});
|
||||
|
||||
text = command.content.replaceAll('{{CLIPBOARD}}', clipboardText);
|
||||
}
|
||||
|
||||
prompt = text;
|
||||
|
||||
const chatInputElement = document.getElementById('chat-textarea');
|
||||
|
||||
|
|
@ -48,16 +62,16 @@
|
|||
|
||||
{#if filteredPromptCommands.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 rounded-lg border border-gray-100 dark:border-gray-700">
|
||||
<div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-lg text-center">
|
||||
<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-lg">
|
||||
<div class=" overflow-y-auto bg-white p-2 rounded-tr-lg space-y-0.5">
|
||||
<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 filteredPromptCommands as command, commandIdx}
|
||||
<button
|
||||
class=" px-3 py-1.5 rounded-lg w-full text-left {commandIdx === selectedCommandIdx
|
||||
class=" px-3 py-1.5 rounded-xl w-full text-left {commandIdx === selectedCommandIdx
|
||||
? ' bg-gray-100 selected-command-option-button'
|
||||
: ''}"
|
||||
type="button"
|
||||
|
|
@ -81,7 +95,7 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
class=" px-2 pb-1 text-xs text-gray-600 bg-white rounded-br-lg flex items-center space-x-1"
|
||||
class=" px-2 pb-1 text-xs text-gray-600 bg-white rounded-br-xl flex items-center space-x-1"
|
||||
>
|
||||
<div>
|
||||
<svg
|
||||
|
|
@ -101,8 +115,9 @@
|
|||
</div>
|
||||
|
||||
<div class="line-clamp-1">
|
||||
Tip: Update multiple variable slots consecutively by pressing the tab key in the chat
|
||||
input after each replacement.
|
||||
{$i18n.t(
|
||||
'Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.'
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,45 +10,47 @@
|
|||
: suggestionPrompts.sort(() => Math.random() - 0.5).slice(0, 4);
|
||||
</script>
|
||||
|
||||
<div class=" flex flex-wrap-reverse mb-3 md:p-1 text-left w-full">
|
||||
{#each prompts as prompt, promptIdx}
|
||||
<div
|
||||
class="{promptIdx > 1 ? 'hidden sm:inline-flex' : ''} basis-full sm:basis-1/2 p-[5px] px-2"
|
||||
>
|
||||
<button
|
||||
class=" flex-1 flex justify-between w-full h-full px-4 py-2.5 bg-white hover:bg-gray-50 dark:bg-gray-900 dark:hover:bg-gray-700 outline outline-1 outline-gray-200 dark:outline-gray-800 rounded-lg transition group"
|
||||
on:click={() => {
|
||||
submitPrompt(prompt.content);
|
||||
}}
|
||||
<div class=" mb-3 md:p-1 text-left w-full">
|
||||
<div class=" flex flex-wrap-reverse px-2 text-left">
|
||||
{#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="flex flex-col text-left self-center">
|
||||
{#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>
|
||||
{:else}
|
||||
<div class=" self-center text-sm font-medium dark:text-gray-300 line-clamp-2">
|
||||
{prompt.content}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="self-center p-1 rounded-lg text-white group-hover:bg-gray-100 group-hover:text-gray-800 dark:group-hover:bg-gray-800 dark:group-hover:text-gray-100 dark:text-gray-900 transition"
|
||||
<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"
|
||||
on:click={() => {
|
||||
submitPrompt(prompt.content);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
<div class="flex flex-col text-left self-center">
|
||||
{#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>
|
||||
{:else}
|
||||
<div class=" self-center text-sm font-medium dark:text-gray-300 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"
|
||||
>
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
<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="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>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { chats, config, modelfiles, settings, user } from '$lib/stores';
|
||||
import { tick } from 'svelte';
|
||||
import { tick, getContext } from 'svelte';
|
||||
|
||||
import toast from 'svelte-french-toast';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { getChatList, updateChatById } from '$lib/apis/chats';
|
||||
|
||||
import UserMessage from './Messages/UserMessage.svelte';
|
||||
import ResponseMessage from './Messages/ResponseMessage.svelte';
|
||||
import Placeholder from './Messages/Placeholder.svelte';
|
||||
import Spinner from '../common/Spinner.svelte';
|
||||
import { imageGenerations } from '$lib/apis/images';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let chatId = '';
|
||||
export let sendPrompt: Function;
|
||||
|
|
@ -66,7 +69,7 @@
|
|||
navigator.clipboard.writeText(text).then(
|
||||
function () {
|
||||
console.log('Async: Copying to clipboard was successful!');
|
||||
toast.success('Copying to clipboard was successful!');
|
||||
toast.success($i18n.t('Copying to clipboard was successful!'));
|
||||
},
|
||||
function (err) {
|
||||
console.error('Async: Could not copy text: ', err);
|
||||
|
|
@ -221,6 +224,81 @@
|
|||
scrollToBottom();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const messageDeleteHandler = async (messageId) => {
|
||||
const messageToDelete = history.messages[messageId];
|
||||
const messageParentId = messageToDelete.parentId;
|
||||
const messageChildrenIds = messageToDelete.childrenIds ?? [];
|
||||
const hasSibling = messageChildrenIds.some(
|
||||
(childId) => history.messages[childId]?.childrenIds?.length > 0
|
||||
);
|
||||
messageChildrenIds.forEach((childId) => {
|
||||
const child = history.messages[childId];
|
||||
if (child && child.childrenIds) {
|
||||
if (child.childrenIds.length === 0 && !hasSibling) {
|
||||
// if last prompt/response pair
|
||||
history.messages[messageParentId].childrenIds = [];
|
||||
history.currentId = messageParentId;
|
||||
} else {
|
||||
child.childrenIds.forEach((grandChildId) => {
|
||||
if (history.messages[grandChildId]) {
|
||||
history.messages[grandChildId].parentId = messageParentId;
|
||||
history.messages[messageParentId].childrenIds.push(grandChildId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// remove response
|
||||
history.messages[messageParentId].childrenIds = history.messages[
|
||||
messageParentId
|
||||
].childrenIds.filter((id) => id !== childId);
|
||||
});
|
||||
// remove prompt
|
||||
history.messages[messageParentId].childrenIds = history.messages[
|
||||
messageParentId
|
||||
].childrenIds.filter((id) => id !== messageId);
|
||||
await updateChatById(localStorage.token, chatId, {
|
||||
messages: messages,
|
||||
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}
|
||||
|
|
@ -237,8 +315,10 @@
|
|||
>
|
||||
{#if message.role === 'user'}
|
||||
<UserMessage
|
||||
on:delete={() => messageDeleteHandler(message.id)}
|
||||
user={$user}
|
||||
{message}
|
||||
isFirstMessage={messageIdx === 0}
|
||||
siblings={message.parentId !== null
|
||||
? history.messages[message.parentId]?.childrenIds ?? []
|
||||
: Object.values(history.messages)
|
||||
|
|
@ -249,52 +329,6 @@
|
|||
{showNextMessage}
|
||||
{copyToClipboard}
|
||||
/>
|
||||
|
||||
{#if messages.length - 1 === messageIdx && processing !== ''}
|
||||
<div class="flex my-2.5 ml-12 items-center w-fit space-x-2.5">
|
||||
<div class=" dark:text-blue-100">
|
||||
<svg
|
||||
class=" w-4 h-4 translate-y-[0.5px]"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_qM83 {
|
||||
animation: spinner_8HQG 1.05s infinite;
|
||||
}
|
||||
.spinner_oXPr {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.spinner_ZTLf {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
@keyframes spinner_8HQG {
|
||||
0%,
|
||||
57.14% {
|
||||
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
||||
transform: translate(0);
|
||||
}
|
||||
28.57% {
|
||||
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(0);
|
||||
}
|
||||
}
|
||||
</style><circle class="spinner_qM83" cx="4" cy="12" r="2.5" /><circle
|
||||
class="spinner_qM83 spinner_oXPr"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="2.5"
|
||||
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="2.5" /></svg
|
||||
>
|
||||
</div>
|
||||
<div class=" text-sm font-medium">
|
||||
{processing}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<ResponseMessage
|
||||
{message}
|
||||
|
|
@ -308,6 +342,16 @@
|
|||
{copyToClipboard}
|
||||
{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>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import { user } from '$lib/stores';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let models = [];
|
||||
export let modelfiles = [];
|
||||
|
|
@ -27,14 +31,16 @@
|
|||
>
|
||||
{#if model in modelfiles}
|
||||
<img
|
||||
src={modelfiles[model]?.imageUrl ?? './favicon.png'}
|
||||
src={modelfiles[model]?.imageUrl ?? `${WEBUI_BASE_URL}/static/favicon.png`}
|
||||
alt="modelfile"
|
||||
class=" w-14 rounded-full border-[1px] border-gray-200 dark:border-none"
|
||||
draggable="false"
|
||||
/>
|
||||
{:else}
|
||||
<img
|
||||
src={models.length === 1 ? '/favicon.png' : '/favicon.png'}
|
||||
src={models.length === 1
|
||||
? `${WEBUI_BASE_URL}/static/favicon.png`
|
||||
: `${WEBUI_BASE_URL}/static/favicon.png`}
|
||||
class=" w-14 rounded-full border-[1px] border-gray-200 dark:border-none"
|
||||
alt="logo"
|
||||
draggable="false"
|
||||
|
|
@ -44,7 +50,7 @@
|
|||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class=" mt-2 text-2xl text-gray-800 dark:text-gray-100 font-semibold">
|
||||
<div class=" mt-2 mb-5 text-2xl text-gray-800 dark:text-gray-100 font-semibold">
|
||||
{#if modelfile}
|
||||
<span class=" capitalize">
|
||||
{modelfile.title}
|
||||
|
|
@ -60,7 +66,9 @@
|
|||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
How can I help you today?
|
||||
<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>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,31 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import dayjs from 'dayjs';
|
||||
import { marked } from 'marked';
|
||||
import { settings } from '$lib/stores';
|
||||
import tippy from 'tippy.js';
|
||||
import auto_render from 'katex/dist/contrib/auto-render.mjs';
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { onMount, tick, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import { config, settings } from '$lib/stores';
|
||||
import { synthesizeOpenAISpeech } from '$lib/apis/openai';
|
||||
import { imageGenerations } from '$lib/apis/images';
|
||||
import { extractSentences } from '$lib/utils';
|
||||
|
||||
import Name from './Name.svelte';
|
||||
import ProfileImage from './ProfileImage.svelte';
|
||||
import Skeleton from './Skeleton.svelte';
|
||||
import CodeBlock from './CodeBlock.svelte';
|
||||
|
||||
import { synthesizeOpenAISpeech } from '$lib/apis/openai';
|
||||
import { extractSentences } from '$lib/utils';
|
||||
import Image from '$lib/components/common/Image.svelte';
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
|
||||
export let modelfiles = [];
|
||||
export let message;
|
||||
|
|
@ -34,7 +44,7 @@
|
|||
|
||||
let edit = false;
|
||||
let editedContent = '';
|
||||
|
||||
let editTextAreaElement: HTMLTextAreaElement;
|
||||
let tooltipInstance = null;
|
||||
|
||||
let sentencesAudio = {};
|
||||
|
|
@ -42,6 +52,7 @@
|
|||
let speakingIdx = null;
|
||||
|
||||
let loadingSpeech = false;
|
||||
let generatingImage = false;
|
||||
|
||||
$: tokens = marked.lexer(message.content);
|
||||
|
||||
|
|
@ -72,13 +83,20 @@
|
|||
|
||||
if (message.info) {
|
||||
tooltipInstance = tippy(`#info-${message.id}`, {
|
||||
content: `<span class="text-xs" id="tooltip-${message.id}">token/s: ${
|
||||
content: `<span class="text-xs" id="tooltip-${message.id}">response_token/s: ${
|
||||
`${
|
||||
Math.round(
|
||||
((message.info.eval_count ?? 0) / (message.info.eval_duration / 1000000000)) * 100
|
||||
) / 100
|
||||
} tokens` ?? 'N/A'
|
||||
}<br/>
|
||||
prompt_token/s: ${
|
||||
Math.round(
|
||||
((message.info.prompt_eval_count ?? 0) /
|
||||
(message.info.prompt_eval_duration / 1000000000)) *
|
||||
100
|
||||
) / 100 ?? 'N/A'
|
||||
} tokens<br/>
|
||||
total_duration: ${
|
||||
Math.round(((message.info.total_duration ?? 0) / 1000000) * 100) / 100 ??
|
||||
'N/A'
|
||||
|
|
@ -109,10 +127,11 @@
|
|||
// customised options
|
||||
// • auto-render specific keys, e.g.:
|
||||
delimiters: [
|
||||
{ left: '$$', right: '$$', display: true },
|
||||
// { left: '$', right: '$', display: false },
|
||||
{ left: '\\(', right: '\\)', display: true },
|
||||
{ left: '\\[', right: '\\]', display: true }
|
||||
{ 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
|
||||
|
|
@ -232,10 +251,9 @@
|
|||
editedContent = message.content;
|
||||
|
||||
await tick();
|
||||
const editElement = document.getElementById(`message-edit-${message.id}`);
|
||||
|
||||
editElement.style.height = '';
|
||||
editElement.style.height = `${editElement.scrollHeight}px`;
|
||||
editTextAreaElement.style.height = '';
|
||||
editTextAreaElement.style.height = `${editTextAreaElement.scrollHeight}px`;
|
||||
};
|
||||
|
||||
const editMessageConfirmHandler = async () => {
|
||||
|
|
@ -259,6 +277,25 @@
|
|||
renderStyling();
|
||||
};
|
||||
|
||||
const generateImage = async (message) => {
|
||||
generatingImage = true;
|
||||
const res = await imageGenerations(localStorage.token, message.content).catch((error) => {
|
||||
toast.error(error);
|
||||
});
|
||||
console.log(res);
|
||||
|
||||
if (res) {
|
||||
message.files = res.map((image) => ({
|
||||
type: 'image',
|
||||
url: `${image.url}`
|
||||
}));
|
||||
|
||||
dispatch('save', message);
|
||||
}
|
||||
|
||||
generatingImage = false;
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
await tick();
|
||||
renderStyling();
|
||||
|
|
@ -267,7 +304,9 @@
|
|||
|
||||
{#key message.id}
|
||||
<div class=" flex w-full message-{message.id}">
|
||||
<ProfileImage src={modelfiles[message.model]?.imageUrl ?? '/favicon.png'} />
|
||||
<ProfileImage
|
||||
src={modelfiles[message.model]?.imageUrl ?? `${WEBUI_BASE_URL}/static/favicon.png`}
|
||||
/>
|
||||
|
||||
<div class="w-full overflow-hidden">
|
||||
<Name>
|
||||
|
|
@ -279,7 +318,7 @@
|
|||
|
||||
{#if message.timestamp}
|
||||
<span class=" invisible group-hover:visible text-gray-400 text-xs font-medium">
|
||||
{dayjs(message.timestamp * 1000).format('DD/MM/YYYY HH:mm')}
|
||||
{dayjs(message.timestamp * 1000).format($i18n.t('DD/MM/YYYY HH:mm'))}
|
||||
</span>
|
||||
{/if}
|
||||
</Name>
|
||||
|
|
@ -287,17 +326,31 @@
|
|||
{#if message.content === ''}
|
||||
<Skeleton />
|
||||
{:else}
|
||||
{#if message.files}
|
||||
<div class="my-2.5 w-full flex overflow-x-auto gap-2 flex-wrap">
|
||||
{#each message.files as file}
|
||||
<div>
|
||||
{#if file.type === 'image'}
|
||||
<Image src={file.url} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-headings:my-0 prose-p:m-0 prose-p:-mb-6 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-6 prose-ol:-mb-8 prose-li:-mb-4 whitespace-pre-line"
|
||||
class="prose chat-{message.role} w-full max-w-full dark:prose-invert prose-headings:my-0 prose-p:m-0 prose-p:-mb-6 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-img:my-0 prose-ul:-my-4 prose-ol:-my-4 prose-li:-my-3 prose-ul:-mb-6 prose-ol:-mb-8 prose-ol:p-0 prose-li:-mb-4 whitespace-pre-line"
|
||||
>
|
||||
<div>
|
||||
{#if edit === true}
|
||||
<div class=" w-full">
|
||||
<textarea
|
||||
id="message-edit-{message.id}"
|
||||
bind:this={editTextAreaElement}
|
||||
class=" bg-transparent outline-none w-full resize-none"
|
||||
bind:value={editedContent}
|
||||
on:input={(e) => {
|
||||
e.target.style.height = '';
|
||||
e.target.style.height = `${e.target.scrollHeight}px`;
|
||||
}}
|
||||
/>
|
||||
|
|
@ -309,7 +362,7 @@
|
|||
editMessageConfirmHandler();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -318,7 +371,7 @@
|
|||
cancelEditMessage();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
{$i18n.t('Cancel')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -366,10 +419,10 @@
|
|||
|
||||
{#if message.done}
|
||||
<div
|
||||
class=" flex justify-start space-x-1 -mt-1 overflow-x-auto buttons text-gray-700 dark:text-gray-500"
|
||||
class=" flex justify-start space-x-1 overflow-x-auto buttons text-gray-700 dark:text-gray-500"
|
||||
>
|
||||
{#if siblings.length > 1}
|
||||
<div class="flex self-center min-w-fit">
|
||||
<div class="flex self-center min-w-fit -mt-1">
|
||||
<button
|
||||
class="self-center dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
|
|
@ -416,257 +469,346 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
editMessageHandler();
|
||||
}}
|
||||
>
|
||||
<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.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition copy-response-button"
|
||||
on:click={() => {
|
||||
copyToClipboard(message.content);
|
||||
}}
|
||||
>
|
||||
<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="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
|
||||
? 'bg-gray-100 dark:bg-gray-800'
|
||||
: ''} dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
rateMessage(message.id, 1);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="w-4 h-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
|
||||
/></svg
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
|
||||
? 'bg-gray-100 dark:bg-gray-800'
|
||||
: ''} dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
rateMessage(message.id, -1);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="w-4 h-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
|
||||
/></svg
|
||||
>
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="speak-button-{message.id}"
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
if (!loadingSpeech) {
|
||||
toggleSpeakMessage(message);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if loadingSpeech}
|
||||
<svg
|
||||
class=" w-4 h-4"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_S1WN {
|
||||
animation: spinner_MGfb 0.8s linear infinite;
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
.spinner_Km9P {
|
||||
animation-delay: -0.65s;
|
||||
}
|
||||
.spinner_JApP {
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
@keyframes spinner_MGfb {
|
||||
93.75%,
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle
|
||||
class="spinner_S1WN spinner_Km9P"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="3"
|
||||
/><circle class="spinner_S1WN spinner_JApP" cx="20" cy="12" r="3" /></svg
|
||||
>
|
||||
{:else if speaking}
|
||||
<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="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<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="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
{#if message.info}
|
||||
<Tooltip content="Edit" placement="bottom">
|
||||
<button
|
||||
class=" {isLastMessage
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap"
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
console.log(message);
|
||||
editMessageHandler();
|
||||
}}
|
||||
id="info-{message.id}"
|
||||
>
|
||||
<svg
|
||||
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"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
|
||||
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Copy" placement="bottom">
|
||||
<button
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition copy-response-button"
|
||||
on:click={() => {
|
||||
copyToClipboard(message.content);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Good Response" placement="bottom">
|
||||
<button
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
|
||||
? 'bg-gray-100 dark:bg-gray-800'
|
||||
: ''} dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
rateMessage(message.id, 1);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="w-4 h-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
|
||||
/></svg
|
||||
>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Bad Response" placement="bottom">
|
||||
<button
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
|
||||
? 'bg-gray-100 dark:bg-gray-800'
|
||||
: ''} dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
rateMessage(message.id, -1);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="w-4 h-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
|
||||
/></svg
|
||||
>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Read Aloud" placement="bottom">
|
||||
<button
|
||||
id="speak-button-{message.id}"
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
if (!loadingSpeech) {
|
||||
toggleSpeakMessage(message);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if loadingSpeech}
|
||||
<svg
|
||||
class=" w-4 h-4"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_S1WN {
|
||||
animation: spinner_MGfb 0.8s linear infinite;
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
.spinner_Km9P {
|
||||
animation-delay: -0.65s;
|
||||
}
|
||||
.spinner_JApP {
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
@keyframes spinner_MGfb {
|
||||
93.75%,
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle
|
||||
class="spinner_S1WN spinner_Km9P"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="3"
|
||||
/><circle
|
||||
class="spinner_S1WN spinner_JApP"
|
||||
cx="20"
|
||||
cy="12"
|
||||
r="3"
|
||||
/></svg
|
||||
>
|
||||
{:else if speaking}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
{#if $config.images}
|
||||
<Tooltip content="Generate Image" placement="bottom">
|
||||
<button
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
if (!generatingImage) {
|
||||
generateImage(message);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if generatingImage}
|
||||
<svg
|
||||
class=" w-4 h-4"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><style>
|
||||
.spinner_S1WN {
|
||||
animation: spinner_MGfb 0.8s linear infinite;
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
.spinner_Km9P {
|
||||
animation-delay: -0.65s;
|
||||
}
|
||||
.spinner_JApP {
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
@keyframes spinner_MGfb {
|
||||
93.75%,
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
</style><circle class="spinner_S1WN" cx="4" cy="12" r="3" /><circle
|
||||
class="spinner_S1WN spinner_Km9P"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="3"
|
||||
/><circle
|
||||
class="spinner_S1WN spinner_JApP"
|
||||
cx="20"
|
||||
cy="12"
|
||||
r="3"
|
||||
/></svg
|
||||
>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if message.info}
|
||||
<Tooltip content="Generation Info" placement="bottom">
|
||||
<button
|
||||
class=" {isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition whitespace-pre-wrap"
|
||||
on:click={() => {
|
||||
console.log(message);
|
||||
}}
|
||||
id="info-{message.id}"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if isLastMessage}
|
||||
<button
|
||||
type="button"
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
|
||||
on:click={() => {
|
||||
continueGeneration();
|
||||
}}
|
||||
>
|
||||
<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="Continue Response" placement="bottom">
|
||||
<button
|
||||
type="button"
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
|
||||
on:click={() => {
|
||||
continueGeneration();
|
||||
}}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
||||
/>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
||||
/>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
|
||||
on:click={regenerateResponse}
|
||||
>
|
||||
<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="Regenerate" placement="bottom">
|
||||
<button
|
||||
type="button"
|
||||
class="{isLastMessage
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1 rounded dark:hover:text-white hover:text-black transition regenerate-response-button"
|
||||
on:click={regenerateResponse}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
<script lang="ts">
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { tick } from 'svelte';
|
||||
import { tick, createEventDispatcher, getContext } from 'svelte';
|
||||
import Name from './Name.svelte';
|
||||
import ProfileImage from './ProfileImage.svelte';
|
||||
import { modelfiles, settings } from '$lib/stores';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let user;
|
||||
export let message;
|
||||
export let siblings;
|
||||
export let isFirstMessage: boolean;
|
||||
|
||||
export let confirmEditMessage: Function;
|
||||
export let showPreviousMessage: Function;
|
||||
|
|
@ -17,18 +23,17 @@
|
|||
|
||||
let edit = false;
|
||||
let editedContent = '';
|
||||
|
||||
let messageEditTextAreaElement: HTMLTextAreaElement;
|
||||
const editMessageHandler = async () => {
|
||||
edit = true;
|
||||
editedContent = message.content;
|
||||
|
||||
await tick();
|
||||
const editElement = document.getElementById(`message-edit-${message.id}`);
|
||||
|
||||
editElement.style.height = '';
|
||||
editElement.style.height = `${editElement.scrollHeight}px`;
|
||||
messageEditTextAreaElement.style.height = '';
|
||||
messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`;
|
||||
|
||||
editElement?.focus();
|
||||
messageEditTextAreaElement?.focus();
|
||||
};
|
||||
|
||||
const editMessageConfirmHandler = async () => {
|
||||
|
|
@ -42,6 +47,10 @@
|
|||
edit = false;
|
||||
editedContent = '';
|
||||
};
|
||||
|
||||
const deleteMessageHandler = async () => {
|
||||
dispatch('delete', message.id);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class=" flex w-full">
|
||||
|
|
@ -58,17 +67,18 @@
|
|||
{#if $modelfiles.map((modelfile) => modelfile.tagName).includes(message.user)}
|
||||
{$modelfiles.find((modelfile) => modelfile.tagName === message.user)?.title}
|
||||
{:else}
|
||||
You <span class=" text-gray-500 text-sm font-medium">{message?.user ?? ''}</span>
|
||||
{$i18n.t('You')}
|
||||
<span class=" text-gray-500 text-sm font-medium">{message?.user ?? ''}</span>
|
||||
{/if}
|
||||
{:else if $settings.showUsername}
|
||||
{user.name}
|
||||
{:else}
|
||||
You
|
||||
{$i18n.t('You')}
|
||||
{/if}
|
||||
|
||||
{#if message.timestamp}
|
||||
<span class=" invisible group-hover:visible text-gray-400 text-xs font-medium">
|
||||
{dayjs(message.timestamp * 1000).format('DD/MM/YYYY HH:mm')}
|
||||
{dayjs(message.timestamp * 1000).format($i18n.t('DD/MM/YYYY HH:mm'))}
|
||||
</span>
|
||||
{/if}
|
||||
</Name>
|
||||
|
|
@ -116,7 +126,7 @@
|
|||
{file.name}
|
||||
</div>
|
||||
|
||||
<div class=" text-gray-500 text-sm">Document</div>
|
||||
<div class=" text-gray-500 text-sm">{$i18n.t('Document')}</div>
|
||||
</div>
|
||||
</button>
|
||||
{:else if file.type === 'collection'}
|
||||
|
|
@ -145,7 +155,7 @@
|
|||
{file?.title ?? `#${file.name}`}
|
||||
</div>
|
||||
|
||||
<div class=" text-gray-500 text-sm">Collection</div>
|
||||
<div class=" text-gray-500 text-sm">{$i18n.t('Collection')}</div>
|
||||
</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
|
@ -158,9 +168,11 @@
|
|||
<div class=" w-full">
|
||||
<textarea
|
||||
id="message-edit-{message.id}"
|
||||
bind:this={messageEditTextAreaElement}
|
||||
class=" bg-transparent outline-none w-full resize-none"
|
||||
bind:value={editedContent}
|
||||
on:input={(e) => {
|
||||
e.target.style.height = '';
|
||||
e.target.style.height = `${e.target.scrollHeight}px`;
|
||||
}}
|
||||
/>
|
||||
|
|
@ -172,7 +184,7 @@
|
|||
editMessageConfirmHandler();
|
||||
}}
|
||||
>
|
||||
Save & Submit
|
||||
{$i18n.t('Save & Submit')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -181,7 +193,7 @@
|
|||
cancelEditMessage();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
{$i18n.t('Cancel')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -189,11 +201,11 @@
|
|||
<div class="w-full">
|
||||
<pre id="user-message">{message.content}</pre>
|
||||
|
||||
<div class=" flex justify-start space-x-1">
|
||||
<div class=" flex justify-start space-x-1 text-gray-700 dark:text-gray-500">
|
||||
{#if siblings.length > 1}
|
||||
<div class="flex self-center">
|
||||
<div class="flex self-center -mt-1">
|
||||
<button
|
||||
class="self-center"
|
||||
class="self-center dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
showPreviousMessage(message);
|
||||
}}
|
||||
|
|
@ -212,12 +224,12 @@
|
|||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="text-xs font-bold self-center">
|
||||
<div class="text-xs font-bold self-center dark:text-gray-100">
|
||||
{siblings.indexOf(message.id) + 1} / {siblings.length}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="self-center"
|
||||
class="self-center dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
showNextMessage(message);
|
||||
}}
|
||||
|
|
@ -238,49 +250,79 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="invisible group-hover:visible p-1 rounded dark:hover:text-white transition edit-user-message-button"
|
||||
on:click={() => {
|
||||
editMessageHandler();
|
||||
}}
|
||||
>
|
||||
<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="Edit" placement="bottom">
|
||||
<button
|
||||
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition edit-user-message-button"
|
||||
on:click={() => {
|
||||
editMessageHandler();
|
||||
}}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<button
|
||||
class="invisible group-hover:visible p-1 rounded dark:hover:text-white transition"
|
||||
on:click={() => {
|
||||
copyToClipboard(message.content);
|
||||
}}
|
||||
>
|
||||
<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="Copy" placement="bottom">
|
||||
<button
|
||||
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
copyToClipboard(message.content);
|
||||
}}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
{#if !isFirstMessage}
|
||||
<Tooltip content="Delete" placement="bottom">
|
||||
<button
|
||||
class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
deleteMessageHandler();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
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>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { setDefaultModels } from '$lib/apis/configs';
|
||||
import { models, showSettings, settings, user } from '$lib/stores';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { onMount, tick, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let selectedModels = [''];
|
||||
export let disabled = false;
|
||||
|
|
@ -10,7 +12,7 @@
|
|||
const saveDefaultModel = async () => {
|
||||
const hasEmptyModel = selectedModels.filter((it) => it === '');
|
||||
if (hasEmptyModel.length) {
|
||||
toast.error('Choose a model before saving...');
|
||||
toast.error($i18n.t('Choose a model before saving...'));
|
||||
return;
|
||||
}
|
||||
settings.set({ ...$settings, models: selectedModels });
|
||||
|
|
@ -20,12 +22,12 @@
|
|||
console.log('setting default models globally');
|
||||
await setDefaultModels(localStorage.token, selectedModels.join(','));
|
||||
}
|
||||
toast.success('Default model updated');
|
||||
toast.success($i18n.t('Default model updated'));
|
||||
};
|
||||
|
||||
$: if (selectedModels.length > 0 && $models.length > 0) {
|
||||
selectedModels = selectedModels.map((model) =>
|
||||
$models.map((m) => m.name).includes(model) ? model : ''
|
||||
$models.map((m) => m.id).includes(model) ? model : ''
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
|
@ -39,13 +41,15 @@
|
|||
bind:value={selectedModel}
|
||||
{disabled}
|
||||
>
|
||||
<option class=" text-gray-700" value="" selected disabled>Select a model</option>
|
||||
<option class=" text-gray-700" value="" selected disabled
|
||||
>{$i18n.t('Select a model')}</option
|
||||
>
|
||||
|
||||
{#each $models as model}
|
||||
{#if model.name === 'hr'}
|
||||
<hr />
|
||||
{:else}
|
||||
<option value={model.name} class="text-gray-700 text-lg"
|
||||
<option value={model.id} class="text-gray-700 text-lg"
|
||||
>{model.name +
|
||||
`${model.size ? ` (${(model.size / 1024 ** 3).toFixed(1)}GB)` : ''}`}</option
|
||||
>
|
||||
|
|
@ -133,5 +137,5 @@
|
|||
</div>
|
||||
|
||||
<div class="text-left mt-1.5 text-xs text-gray-500">
|
||||
<button on:click={saveDefaultModel}> Set as default</button>
|
||||
<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,38 +1,104 @@
|
|||
<script lang="ts">
|
||||
import { getVersionUpdates } from '$lib/apis';
|
||||
import { getOllamaVersion } from '$lib/apis/ollama';
|
||||
import { WEBUI_NAME, WEB_UI_VERSION } from '$lib/constants';
|
||||
import { config } from '$lib/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { WEBUI_VERSION } from '$lib/constants';
|
||||
import { WEBUI_NAME, config, showChangelog } from '$lib/stores';
|
||||
import { compareVersion } from '$lib/utils';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let ollamaVersion = '';
|
||||
|
||||
let updateAvailable = null;
|
||||
let version = {
|
||||
current: '',
|
||||
latest: ''
|
||||
};
|
||||
|
||||
const checkForVersionUpdates = async () => {
|
||||
updateAvailable = null;
|
||||
version = await getVersionUpdates(localStorage.token).catch((error) => {
|
||||
return {
|
||||
current: WEBUI_VERSION,
|
||||
latest: WEBUI_VERSION
|
||||
};
|
||||
});
|
||||
|
||||
console.log(version);
|
||||
|
||||
updateAvailable = compareVersion(version.latest, version.current);
|
||||
console.log(updateAvailable);
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => {
|
||||
return '';
|
||||
});
|
||||
|
||||
checkForVersionUpdates();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between space-y-3 text-sm mb-6">
|
||||
<div class=" space-y-3">
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{WEBUI_NAME} Version</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 text-xs text-gray-700 dark:text-gray-200">
|
||||
{$config && $config.version ? $config.version : WEB_UI_VERSION}
|
||||
<div class=" mb-2.5 text-sm font-medium flex space-x-2 items-center">
|
||||
<div>
|
||||
{$WEBUI_NAME}
|
||||
{$i18n.t('Version')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full justify-between items-center">
|
||||
<div class="flex flex-col text-xs text-gray-700 dark:text-gray-200">
|
||||
<div>
|
||||
v{WEBUI_VERSION}
|
||||
|
||||
<a
|
||||
href="https://github.com/open-webui/open-webui/releases/tag/v{version.latest}"
|
||||
target="_blank"
|
||||
>
|
||||
{updateAvailable === null
|
||||
? $i18n.t('Checking for updates...')
|
||||
: updateAvailable
|
||||
? `(v${version.latest} ${$i18n.t('available!')})`
|
||||
: $i18n.t('(latest)')}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class=" underline flex items-center space-x-1 text-xs text-gray-500 dark:text-gray-500"
|
||||
on:click={() => {
|
||||
showChangelog.set(true);
|
||||
}}
|
||||
>
|
||||
<div>{$i18n.t("See what's new")}</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class=" text-xs px-3 py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-lg font-medium"
|
||||
on:click={() => {
|
||||
checkForVersionUpdates();
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Check for updates')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
{#if ollamaVersion}
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Ollama Version</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 text-xs text-gray-700 dark:text-gray-200">
|
||||
{ollamaVersion ?? 'N/A'}
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Ollama Version')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 text-xs text-gray-700 dark:text-gray-200">
|
||||
{ollamaVersion ?? 'N/A'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
|
|
@ -44,16 +110,24 @@
|
|||
/>
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/ollama-webui/ollama-webui" target="_blank">
|
||||
<a href="https://twitter.com/OpenWebUI" target="_blank">
|
||||
<img
|
||||
alt="X (formerly Twitter) Follow"
|
||||
src="https://img.shields.io/twitter/follow/OpenWebUI"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/open-webui/open-webui" target="_blank">
|
||||
<img
|
||||
alt="Github Repo"
|
||||
src="https://img.shields.io/github/stars/ollama-webui/ollama-webui?style=social&label=Star us on Github"
|
||||
src="https://img.shields.io/github/stars/open-webui/open-webui?style=social&label=Star us on Github"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Created by <a
|
||||
{$i18n.t('Created by')}
|
||||
<a
|
||||
class=" text-gray-500 dark:text-gray-300 font-medium"
|
||||
href="https://github.com/tjbck"
|
||||
target="_blank">Timothy J. Baek</a
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { onMount } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
import { user } from '$lib/stores';
|
||||
import { updateUserProfile } from '$lib/apis/auths';
|
||||
|
||||
import UpdatePassword from './Account/UpdatePassword.svelte';
|
||||
import { getGravatarUrl } from '$lib/apis/utils';
|
||||
import { copyToClipboard } from '$lib/utils';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveHandler: Function;
|
||||
|
||||
let profileImageUrl = '';
|
||||
let name = '';
|
||||
let showJWTToken = false;
|
||||
let JWTTokenCopied = false;
|
||||
let profileImageInputElement: HTMLInputElement;
|
||||
|
||||
const submitHandler = async () => {
|
||||
const updatedUser = await updateUserProfile(localStorage.token, name, profileImageUrl).catch(
|
||||
|
|
@ -34,14 +40,15 @@
|
|||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
|
||||
<input
|
||||
id="profile-image-input"
|
||||
bind:this={profileImageInputElement}
|
||||
type="file"
|
||||
hidden
|
||||
accept="image/*"
|
||||
on:change={(e) => {
|
||||
const files = e?.target?.files ?? [];
|
||||
const files = profileImageInputElement.files ?? [];
|
||||
let reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
let originalImageUrl = `${event.target.result}`;
|
||||
|
|
@ -83,7 +90,7 @@
|
|||
// Display the compressed image
|
||||
profileImageUrl = compressedSrc;
|
||||
|
||||
e.target.files = null;
|
||||
profileImageInputElement.files = null;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -96,7 +103,7 @@
|
|||
}}
|
||||
/>
|
||||
|
||||
<div class=" mb-2.5 text-sm font-medium">Profile</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Profile')}</div>
|
||||
|
||||
<div class="flex space-x-5">
|
||||
<div class="flex flex-col">
|
||||
|
|
@ -105,7 +112,7 @@
|
|||
class="relative rounded-full dark:bg-gray-700"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
document.getElementById('profile-image-input')?.click();
|
||||
profileImageInputElement.click();
|
||||
}}
|
||||
>
|
||||
<img
|
||||
|
|
@ -138,13 +145,13 @@
|
|||
const url = await getGravatarUrl($user.email);
|
||||
|
||||
profileImageUrl = url;
|
||||
}}>Use Gravatar</button
|
||||
}}>{$i18n.t('Use Gravatar')}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Name</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
|
@ -160,11 +167,113 @@
|
|||
|
||||
<hr class=" dark:border-gray-700 my-4" />
|
||||
<UpdatePassword />
|
||||
|
||||
<hr class=" dark:border-gray-700 my-4" />
|
||||
|
||||
<div class=" w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('JWT Token')}</div>
|
||||
</div>
|
||||
|
||||
<div class="flex mt-2">
|
||||
<div class="flex w-full">
|
||||
<input
|
||||
class="w-full rounded-l-lg py-1.5 pl-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
type={showJWTToken ? 'text' : 'password'}
|
||||
value={localStorage.token}
|
||||
disabled
|
||||
/>
|
||||
|
||||
<button
|
||||
class="dark:bg-gray-800 px-2 transition rounded-r-lg"
|
||||
on:click={() => {
|
||||
showJWTToken = !showJWTToken;
|
||||
}}
|
||||
>
|
||||
{#if showJWTToken}
|
||||
<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="M3.28 2.22a.75.75 0 0 0-1.06 1.06l10.5 10.5a.75.75 0 1 0 1.06-1.06l-1.322-1.323a7.012 7.012 0 0 0 2.16-3.11.87.87 0 0 0 0-.567A7.003 7.003 0 0 0 4.82 3.76l-1.54-1.54Zm3.196 3.195 1.135 1.136A1.502 1.502 0 0 1 9.45 8.389l1.136 1.135a3 3 0 0 0-4.109-4.109Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="m7.812 10.994 1.816 1.816A7.003 7.003 0 0 1 1.38 8.28a.87.87 0 0 1 0-.566 6.985 6.985 0 0 1 1.113-2.039l2.513 2.513a3 3 0 0 0 2.806 2.806Z"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M1.38 8.28a.87.87 0 0 1 0-.566 7.003 7.003 0 0 1 13.238.006.87.87 0 0 1 0 .566A7.003 7.003 0 0 1 1.379 8.28ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="ml-1.5 px-1.5 py-1 hover:bg-gray-800 transition rounded-lg"
|
||||
on:click={() => {
|
||||
copyToClipboard(localStorage.token);
|
||||
JWTTokenCopied = true;
|
||||
setTimeout(() => {
|
||||
JWTTokenCopied = false;
|
||||
}, 2000);
|
||||
}}
|
||||
>
|
||||
{#if JWTTokenCopied}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<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="M11.986 3H12a2 2 0 0 1 2 2v6a2 2 0 0 1-1.5 1.937V7A2.5 2.5 0 0 0 10 4.5H4.063A2 2 0 0 1 6 3h.014A2.25 2.25 0 0 1 8.25 1h1.5a2.25 2.25 0 0 1 2.236 2ZM10.5 4v-.75a.75.75 0 0 0-.75-.75h-1.5a.75.75 0 0 0-.75.75V4h3Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M3 6a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H3Zm1.75 2.5a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5h-3.5ZM4 11.75a.75.75 0 0 1 .75-.75h3.5a.75.75 0 0 1 0 1.5h-3.5a.75.75 0 0 1-.75-.75Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
on:click={async () => {
|
||||
const res = await submitHandler();
|
||||
|
||||
|
|
@ -173,7 +282,7 @@
|
|||
}
|
||||
}}
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { updateUserPassword } from '$lib/apis/auths';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let show = false;
|
||||
let currentPassword = '';
|
||||
let newPassword = '';
|
||||
|
|
@ -17,7 +20,7 @@
|
|||
);
|
||||
|
||||
if (res) {
|
||||
toast.success('Successfully updated.');
|
||||
toast.success($i18n.t('Successfully updated.'));
|
||||
}
|
||||
|
||||
currentPassword = '';
|
||||
|
|
@ -39,21 +42,21 @@
|
|||
updatePasswordHandler();
|
||||
}}
|
||||
>
|
||||
<div class="flex justify-between mb-2.5 items-center text-sm">
|
||||
<div class=" font-medium">Change Password</div>
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<div class=" font-medium">{$i18n.t('Change Password')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
show = !show;
|
||||
}}>{show ? 'Hide' : 'Show'}</button
|
||||
}}>{show ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
{#if show}
|
||||
<div class=" space-y-1.5">
|
||||
<div class=" py-2.5 space-y-1.5">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Current Password</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Current Password')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
|
@ -67,7 +70,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">New Password</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('New Password')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
|
@ -81,7 +84,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Confirm Password</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Confirm Password')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
|
@ -99,7 +102,7 @@
|
|||
<button
|
||||
class=" px-4 py-2 text-xs bg-gray-800 hover:bg-gray-900 dark:bg-gray-700 dark:hover:bg-gray-800 text-gray-100 transition rounded-md font-medium"
|
||||
>
|
||||
Update password
|
||||
{$i18n.t('Update password')}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import AdvancedParams from './Advanced/AdvancedParams.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import AdvancedParams from './Advanced/AdvancedParams.svelte';
|
||||
export let saveSettings: Function;
|
||||
|
||||
// Advanced
|
||||
|
|
@ -55,14 +57,14 @@
|
|||
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div class=" text-sm font-medium">Parameters</div>
|
||||
<div class=" text-sm font-medium">{$i18n.t('Parameters')}</div>
|
||||
|
||||
<AdvancedParams bind:options />
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div class=" py-1 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Keep Alive</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Keep Alive')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -72,9 +74,9 @@
|
|||
}}
|
||||
>
|
||||
{#if keepAlive === null}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -84,7 +86,7 @@
|
|||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="text"
|
||||
placeholder={`e.g.) "30s","10m". Valid time units are "s", "m", "h".`}
|
||||
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
|
||||
bind:value={keepAlive}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -93,7 +95,7 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-1 flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-medium">Request Mode</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Request Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -102,7 +104,7 @@
|
|||
}}
|
||||
>
|
||||
{#if requestFormat === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else if requestFormat === 'json'}
|
||||
<!-- <svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -114,7 +116,7 @@
|
|||
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
|
||||
/>
|
||||
</svg> -->
|
||||
<span class="ml-2 self-center"> JSON </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('JSON')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -147,7 +149,7 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let options = {
|
||||
// Advanced
|
||||
seed: 0,
|
||||
|
|
@ -20,7 +24,7 @@
|
|||
<div class=" space-y-3 text-xs">
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" w-20 text-xs font-medium self-center">Seed</div>
|
||||
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Seed')}</div>
|
||||
<div class=" flex-1 self-center">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
|
|
@ -36,12 +40,12 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" w-20 text-xs font-medium self-center">Stop Sequence</div>
|
||||
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Stop Sequence')}</div>
|
||||
<div class=" flex-1 self-center">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="text"
|
||||
placeholder="Enter Stop Sequence"
|
||||
placeholder={$i18n.t('Enter stop sequence')}
|
||||
bind:value={options.stop}
|
||||
autocomplete="off"
|
||||
/>
|
||||
|
|
@ -51,7 +55,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Temperature</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Temperature')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -61,9 +65,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.temperature === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -97,7 +101,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Mirostat</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -107,9 +111,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.mirostat === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -143,7 +147,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Mirostat Eta</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat Eta')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -153,9 +157,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.mirostat_eta === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -189,7 +193,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Mirostat Tau</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Mirostat Tau')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -199,9 +203,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.mirostat_tau === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -235,7 +239,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Top K</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -245,9 +249,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.top_k === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -281,7 +285,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Top P</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Top P')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -291,9 +295,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.top_p === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -327,7 +331,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Repeat Penalty</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Repeat Penalty')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -337,9 +341,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.repeat_penalty === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -373,7 +377,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Repeat Last N</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Repeat Last N')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -383,9 +387,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.repeat_last_n === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -419,7 +423,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Tfs Z</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Tfs Z')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -429,9 +433,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.tfs_z === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -465,7 +469,7 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Context Length</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Context Length')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -475,9 +479,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.num_ctx === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -510,7 +514,7 @@
|
|||
</div>
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Max Tokens</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -520,9 +524,9 @@
|
|||
}}
|
||||
>
|
||||
{#if options.num_predict === ''}
|
||||
<span class="ml-2 self-center"> Default </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> Custom </span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveSettings: Function;
|
||||
|
||||
// Audio
|
||||
|
|
@ -101,32 +103,36 @@
|
|||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">STT Settings</div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('STT Settings')}</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Speech-to-Text Engine</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Speech-to-Text Engine')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={STTEngine}
|
||||
placeholder="Select a mode"
|
||||
on:change={(e) => {
|
||||
if (e.target.value !== '') {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true }).catch(function (err) {
|
||||
toast.error(`Permission denied when accessing microphone: ${err}`);
|
||||
toast.error(
|
||||
$i18n.t(`Permission denied when accessing microphone: {{error}}`, {
|
||||
error: err
|
||||
})
|
||||
);
|
||||
STTEngine = '';
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option value="">Default (Web API)</option>
|
||||
<option value="whisper-local">Whisper (Local)</option>
|
||||
<option value="">{$i18n.t('Default (Web API)')}</option>
|
||||
<option value="whisper-local">{$i18n.t('Whisper (Local)')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Conversation Mode</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Conversation Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -136,15 +142,17 @@
|
|||
type="button"
|
||||
>
|
||||
{#if conversationMode === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Auto-send input after 3 sec.</div>
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Auto-send input after 3 sec.')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -154,22 +162,22 @@
|
|||
type="button"
|
||||
>
|
||||
{#if speechAutoSend === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">TTS Settings</div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('TTS Settings')}</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Text-to-Speech Engine</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Text-to-Speech Engine')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
class=" dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={TTSEngine}
|
||||
placeholder="Select a mode"
|
||||
on:change={(e) => {
|
||||
|
|
@ -182,14 +190,14 @@
|
|||
}
|
||||
}}
|
||||
>
|
||||
<option value="">Default (Web API)</option>
|
||||
<option value="openai">Open AI</option>
|
||||
<option value="">{$i18n.t('Default (Web API)')}</option>
|
||||
<option value="openai">{$i18n.t('Open AI')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Auto-playback response</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Auto-playback response')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -199,9 +207,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if responseAutoPlayback === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -211,7 +219,7 @@
|
|||
|
||||
{#if TTSEngine === ''}
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Voice</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<select
|
||||
|
|
@ -219,7 +227,7 @@
|
|||
bind:value={speaker}
|
||||
placeholder="Select a voice"
|
||||
>
|
||||
<option value="" selected>Default</option>
|
||||
<option value="" selected>{$i18n.t('Default')}</option>
|
||||
{#each voices.filter((v) => v.localService === true) as voice}
|
||||
<option value={voice.name} class="bg-gray-100 dark:bg-gray-700">{voice.name}</option
|
||||
>
|
||||
|
|
@ -230,7 +238,7 @@
|
|||
</div>
|
||||
{:else if TTSEngine === 'openai'}
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Voice</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<select
|
||||
|
|
@ -251,10 +259,10 @@
|
|||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -13,15 +13,18 @@
|
|||
getChatList
|
||||
} from '$lib/apis/chats';
|
||||
import { getImportOrigin, convertOpenAIChats } from '$lib/utils';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveSettings: Function;
|
||||
// Chats
|
||||
let saveChatHistory = true;
|
||||
let importFiles;
|
||||
let showDeleteConfirm = false;
|
||||
let chatImportInputElement: HTMLInputElement;
|
||||
|
||||
$: if (importFiles) {
|
||||
console.log(importFiles);
|
||||
|
|
@ -75,7 +78,9 @@
|
|||
|
||||
const deleteChats = async () => {
|
||||
await goto('/');
|
||||
await deleteAllChats(localStorage.token);
|
||||
await deleteAllChats(localStorage.token).catch((error) => {
|
||||
toast.error(error);
|
||||
});
|
||||
await chats.set(await getChatList(localStorage.token));
|
||||
};
|
||||
|
||||
|
|
@ -96,13 +101,13 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between space-y-3 text-sm">
|
||||
<div class="flex flex-col h-full justify-between space-y-3 text-sm max-h-[22rem]">
|
||||
<div class=" space-y-2">
|
||||
<div
|
||||
class="flex flex-col justify-between rounded-md items-center py-2 px-3.5 w-full transition"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-medium">Chat History</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Chat History')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -126,7 +131,7 @@
|
|||
/>
|
||||
</svg>
|
||||
|
||||
<span class="ml-2 self-center"> On </span>
|
||||
<span class="ml-2 self-center"> {$i18n.t('On')} </span>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -144,24 +149,31 @@
|
|||
/>
|
||||
</svg>
|
||||
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-left w-full font-medium mt-0.5">
|
||||
This setting does not sync across browsers or devices.
|
||||
{$i18n.t('This setting does not sync across browsers or devices.')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div class="flex flex-col">
|
||||
<input id="chat-import-input" bind:files={importFiles} type="file" accept=".json" hidden />
|
||||
<input
|
||||
id="chat-import-input"
|
||||
bind:this={chatImportInputElement}
|
||||
bind:files={importFiles}
|
||||
type="file"
|
||||
accept=".json"
|
||||
hidden
|
||||
/>
|
||||
<button
|
||||
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
||||
on:click={() => {
|
||||
document.getElementById('chat-import-input').click();
|
||||
chatImportInputElement.click();
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
|
|
@ -178,7 +190,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Import Chats</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Import Chats')}</div>
|
||||
</button>
|
||||
<button
|
||||
class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
|
||||
|
|
@ -200,7 +212,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Export Chats</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Export Chats')}</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -222,7 +234,7 @@
|
|||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span>Are you sure?</span>
|
||||
<span>{$i18n.t('Are you sure?')}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-1.5 items-center">
|
||||
|
|
@ -286,7 +298,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Delete Chats</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Delete Chats')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
|
|
@ -314,7 +326,9 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Export All Chats (All Users)</div>
|
||||
<div class=" self-center text-sm font-medium">
|
||||
{$i18n.t('Export All Chats (All Users)')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
|
@ -328,7 +342,7 @@
|
|||
});
|
||||
|
||||
if (res) {
|
||||
toast.success('Success');
|
||||
toast.success($i18n.t('Success'));
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
@ -346,7 +360,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center text-sm font-medium">Reset Vector Storage</div>
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Reset Vector Storage')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
259
src/lib/components/chat/Settings/Connections.svelte
Normal file
259
src/lib/components/chat/Settings/Connections.svelte
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
<script lang="ts">
|
||||
import { models, user } from '$lib/stores';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import { getOllamaUrls, getOllamaVersion, updateOllamaUrls } from '$lib/apis/ollama';
|
||||
import {
|
||||
getOpenAIKeys,
|
||||
getOpenAIUrls,
|
||||
updateOpenAIKeys,
|
||||
updateOpenAIUrls
|
||||
} from '$lib/apis/openai';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let getModels: Function;
|
||||
|
||||
// External
|
||||
let OLLAMA_BASE_URL = '';
|
||||
let OLLAMA_BASE_URLS = [''];
|
||||
|
||||
let OPENAI_API_KEY = '';
|
||||
let OPENAI_API_BASE_URL = '';
|
||||
|
||||
let OPENAI_API_KEYS = [''];
|
||||
let OPENAI_API_BASE_URLS = [''];
|
||||
|
||||
let showOpenAI = false;
|
||||
|
||||
const updateOpenAIHandler = async () => {
|
||||
OPENAI_API_BASE_URLS = await updateOpenAIUrls(localStorage.token, OPENAI_API_BASE_URLS);
|
||||
OPENAI_API_KEYS = await updateOpenAIKeys(localStorage.token, OPENAI_API_KEYS);
|
||||
|
||||
await models.set(await getModels());
|
||||
};
|
||||
|
||||
const updateOllamaUrlsHandler = async () => {
|
||||
OLLAMA_BASE_URLS = await updateOllamaUrls(localStorage.token, OLLAMA_BASE_URLS);
|
||||
|
||||
const ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (ollamaVersion) {
|
||||
toast.success($i18n.t('Server connection verified'));
|
||||
await models.set(await getModels());
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
if ($user.role === 'admin') {
|
||||
OLLAMA_BASE_URLS = await getOllamaUrls(localStorage.token);
|
||||
OPENAI_API_BASE_URLS = await getOpenAIUrls(localStorage.token);
|
||||
OPENAI_API_KEYS = await getOpenAIKeys(localStorage.token);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<form
|
||||
class="flex flex-col h-full justify-between text-sm"
|
||||
on:submit|preventDefault={() => {
|
||||
updateOpenAIHandler();
|
||||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
<div class=" pr-1.5 overflow-y-scroll max-h-[22rem] space-y-3">
|
||||
<div class=" space-y-3">
|
||||
<div class="mt-2 space-y-2 pr-1.5">
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<div class=" font-medium">{$i18n.t('OpenAI API')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showOpenAI = !showOpenAI;
|
||||
}}>{showOpenAI ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
{#if showOpenAI}
|
||||
<div class="flex flex-col gap-1">
|
||||
{#each OPENAI_API_BASE_URLS as url, idx}
|
||||
<div class="flex w-full gap-2">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
bind:value={url}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</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 outline-none"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
bind:value={OPENAI_API_KEYS[idx]}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div class="self-center flex items-center">
|
||||
{#if idx === 0}
|
||||
<button
|
||||
class="px-1"
|
||||
on:click={() => {
|
||||
OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, ''];
|
||||
OPENAI_API_KEYS = [...OPENAI_API_KEYS, ''];
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<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>
|
||||
{:else}
|
||||
<button
|
||||
class="px-1"
|
||||
on:click={() => {
|
||||
OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
|
||||
(url, urlIdx) => idx !== urlIdx
|
||||
);
|
||||
OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx);
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class=" mb-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('WebUI will make requests to')}
|
||||
<span class=" text-gray-200">'{url}/models'</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Ollama Base URL')}</div>
|
||||
<div class="flex w-full gap-1.5">
|
||||
<div class="flex-1 flex flex-col gap-2">
|
||||
{#each OLLAMA_BASE_URLS as url, idx}
|
||||
<div class="flex gap-1.5">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder="Enter URL (e.g. http://localhost:11434)"
|
||||
bind:value={url}
|
||||
/>
|
||||
|
||||
<div class="self-center flex items-center">
|
||||
{#if idx === 0}
|
||||
<button
|
||||
class="px-1"
|
||||
on:click={() => {
|
||||
OLLAMA_BASE_URLS = [...OLLAMA_BASE_URLS, ''];
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<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>
|
||||
{:else}
|
||||
<button
|
||||
class="px-1"
|
||||
on:click={() => {
|
||||
OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url, urlIdx) => idx !== urlIdx);
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<button
|
||||
class="p-2.5 bg-gray-200 hover:bg-gray-300 dark:bg-gray-850 dark:hover:bg-gray-800 rounded-lg transition"
|
||||
on:click={() => {
|
||||
updateOllamaUrlsHandler();
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('Trouble accessing Ollama?')}
|
||||
<a
|
||||
class=" text-gray-300 font-medium underline"
|
||||
href="https://github.com/open-webui/open-webui#troubleshooting"
|
||||
target="_blank"
|
||||
>
|
||||
{$i18n.t('Click here for help.')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</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"
|
||||
type="submit"
|
||||
>
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { getOpenAIKey, getOpenAIUrl, updateOpenAIKey, updateOpenAIUrl } from '$lib/apis/openai';
|
||||
import { models, user } from '$lib/stores';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let getModels: Function;
|
||||
|
||||
// External
|
||||
let OPENAI_API_KEY = '';
|
||||
let OPENAI_API_BASE_URL = '';
|
||||
|
||||
const updateOpenAIHandler = async () => {
|
||||
OPENAI_API_BASE_URL = await updateOpenAIUrl(localStorage.token, OPENAI_API_BASE_URL);
|
||||
OPENAI_API_KEY = await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);
|
||||
|
||||
await models.set(await getModels());
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
if ($user.role === 'admin') {
|
||||
OPENAI_API_BASE_URL = await getOpenAIUrl(localStorage.token);
|
||||
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<form
|
||||
class="flex flex-col h-full justify-between space-y-3 text-sm"
|
||||
on:submit|preventDefault={() => {
|
||||
updateOpenAIHandler();
|
||||
dispatch('save');
|
||||
|
||||
// saveSettings({
|
||||
// OPENAI_API_KEY: OPENAI_API_KEY !== '' ? OPENAI_API_KEY : undefined,
|
||||
// OPENAI_API_BASE_URL: OPENAI_API_BASE_URL !== '' ? OPENAI_API_BASE_URL : undefined
|
||||
// });
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3">
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">OpenAI API Key</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
placeholder="Enter OpenAI API Key"
|
||||
bind:value={OPENAI_API_KEY}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Adds optional support for online models.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">OpenAI API Base URL</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1">
|
||||
<input
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
placeholder="Enter OpenAI API Base URL"
|
||||
bind:value={OPENAI_API_BASE_URL}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
WebUI will make requests to <span class=" text-gray-200">'{OPENAI_API_BASE_URL}/chat'</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1,33 +1,28 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { getLanguages } from '$lib/i18n';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import { getOllamaAPIUrl, updateOllamaAPIUrl } from '$lib/apis/ollama';
|
||||
import { models, user } from '$lib/stores';
|
||||
import { models, user, theme } from '$lib/stores';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import AdvancedParams from './Advanced/AdvancedParams.svelte';
|
||||
|
||||
export let saveSettings: Function;
|
||||
export let getModels: Function;
|
||||
|
||||
// General
|
||||
let API_BASE_URL = '';
|
||||
let themes = ['dark', 'light', 'rose-pine dark', 'rose-pine-dawn light'];
|
||||
let theme = 'dark';
|
||||
let selectedTheme = 'system';
|
||||
|
||||
let languages = [];
|
||||
let lang = $i18n.language;
|
||||
let notificationEnabled = false;
|
||||
let system = '';
|
||||
|
||||
const toggleTheme = async () => {
|
||||
if (theme === 'dark') {
|
||||
theme = 'light';
|
||||
} else {
|
||||
theme = 'dark';
|
||||
}
|
||||
|
||||
localStorage.theme = theme;
|
||||
|
||||
document.documentElement.classList.remove(theme === 'dark' ? 'light' : 'dark');
|
||||
document.documentElement.classList.add(theme);
|
||||
};
|
||||
let showAdvanced = false;
|
||||
|
||||
const toggleNotification = async () => {
|
||||
const permission = await Notification.requestPermission();
|
||||
|
|
@ -42,183 +37,280 @@
|
|||
}
|
||||
};
|
||||
|
||||
const updateOllamaAPIUrlHandler = async () => {
|
||||
API_BASE_URL = await updateOllamaAPIUrl(localStorage.token, API_BASE_URL);
|
||||
const _models = await getModels('ollama');
|
||||
// Advanced
|
||||
let requestFormat = '';
|
||||
let keepAlive = null;
|
||||
|
||||
if (_models.length > 0) {
|
||||
toast.success('Server connection verified');
|
||||
await models.set(_models);
|
||||
let options = {
|
||||
// Advanced
|
||||
seed: 0,
|
||||
temperature: '',
|
||||
repeat_penalty: '',
|
||||
repeat_last_n: '',
|
||||
mirostat: '',
|
||||
mirostat_eta: '',
|
||||
mirostat_tau: '',
|
||||
top_k: '',
|
||||
top_p: '',
|
||||
stop: '',
|
||||
tfs_z: '',
|
||||
num_ctx: '',
|
||||
num_predict: ''
|
||||
};
|
||||
|
||||
const toggleRequestFormat = async () => {
|
||||
if (requestFormat === '') {
|
||||
requestFormat = 'json';
|
||||
} else {
|
||||
requestFormat = '';
|
||||
}
|
||||
|
||||
saveSettings({ requestFormat: requestFormat !== '' ? requestFormat : undefined });
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
if ($user.role === 'admin') {
|
||||
API_BASE_URL = await getOllamaAPIUrl(localStorage.token);
|
||||
}
|
||||
selectedTheme = localStorage.theme ?? 'system';
|
||||
|
||||
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
|
||||
languages = await getLanguages();
|
||||
|
||||
theme = localStorage.theme ?? 'dark';
|
||||
notificationEnabled = settings.notificationEnabled ?? false;
|
||||
system = settings.system ?? '';
|
||||
|
||||
requestFormat = settings.requestFormat ?? '';
|
||||
keepAlive = settings.keepAlive ?? null;
|
||||
|
||||
options.seed = settings.seed ?? 0;
|
||||
options.temperature = settings.temperature ?? '';
|
||||
options.repeat_penalty = settings.repeat_penalty ?? '';
|
||||
options.top_k = settings.top_k ?? '';
|
||||
options.top_p = settings.top_p ?? '';
|
||||
options.num_ctx = settings.num_ctx ?? '';
|
||||
options = { ...options, ...settings.options };
|
||||
options.stop = (settings?.options?.stop ?? []).join(',');
|
||||
});
|
||||
|
||||
const applyTheme = (_theme: string) => {
|
||||
let themeToApply = _theme;
|
||||
|
||||
if (_theme === 'system') {
|
||||
themeToApply = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
themes
|
||||
.filter((e) => e !== themeToApply)
|
||||
.forEach((e) => {
|
||||
e.split(' ').forEach((e) => {
|
||||
document.documentElement.classList.remove(e);
|
||||
});
|
||||
});
|
||||
|
||||
themeToApply.split(' ').forEach((e) => {
|
||||
document.documentElement.classList.add(e);
|
||||
});
|
||||
|
||||
console.log(_theme);
|
||||
};
|
||||
|
||||
const themeChangeHandler = (_theme: string) => {
|
||||
theme.set(_theme);
|
||||
localStorage.setItem('theme', _theme);
|
||||
|
||||
applyTheme(_theme);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col space-y-3">
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">WebUI Settings</div>
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<div class=" pr-1.5 overflow-y-scroll max-h-[22rem]">
|
||||
<div class="">
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('WebUI Settings')}</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Theme</div>
|
||||
<div class="flex items-center relative">
|
||||
<div class=" absolute right-16">
|
||||
{#if theme === 'dark'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M7.455 2.004a.75.75 0 01.26.77 7 7 0 009.958 7.967.75.75 0 011.067.853A8.5 8.5 0 116.647 1.921a.75.75 0 01.808.083z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{:else if theme === 'light'}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4 self-center"
|
||||
>
|
||||
<path
|
||||
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<select
|
||||
class="w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={theme}
|
||||
placeholder="Select a theme"
|
||||
on:change={(e) => {
|
||||
localStorage.theme = theme;
|
||||
|
||||
themes
|
||||
.filter((e) => e !== theme)
|
||||
.forEach((e) => {
|
||||
e.split(' ').forEach((e) => {
|
||||
document.documentElement.classList.remove(e);
|
||||
});
|
||||
});
|
||||
|
||||
theme.split(' ').forEach((e) => {
|
||||
document.documentElement.classList.add(e);
|
||||
});
|
||||
|
||||
console.log(theme);
|
||||
}}
|
||||
>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="rose-pine dark">Rosé Pine</option>
|
||||
<option value="rose-pine-dawn light">Rosé Pine Dawn</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Notification</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
on:click={() => {
|
||||
toggleNotification();
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
{#if notificationEnabled === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $user.role === 'admin'}
|
||||
<hr class=" dark:border-gray-700" />
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Ollama API URL</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
placeholder="Enter URL (e.g. http://localhost:11434/api)"
|
||||
bind:value={API_BASE_URL}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 rounded transition"
|
||||
on:click={() => {
|
||||
updateOllamaAPIUrlHandler();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Theme')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class=" dark:bg-gray-900 w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={selectedTheme}
|
||||
placeholder="Select a theme"
|
||||
on:change={() => themeChangeHandler(selectedTheme)}
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<option value="system">⚙️ {$i18n.t('System')}</option>
|
||||
<option value="dark">🌑 {$i18n.t('Dark')}</option>
|
||||
<option value="light">☀️ {$i18n.t('Light')}</option>
|
||||
<option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
|
||||
<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Trouble accessing Ollama?
|
||||
<a
|
||||
class=" text-gray-300 font-medium"
|
||||
href="https://github.com/ollama-webui/ollama-webui#troubleshooting"
|
||||
target="_blank"
|
||||
>
|
||||
Click here for help.
|
||||
</a>
|
||||
<div class=" flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Language')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class=" dark:bg-gray-900 w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={lang}
|
||||
placeholder="Select a language"
|
||||
on:change={(e) => {
|
||||
$i18n.changeLanguage(lang);
|
||||
}}
|
||||
>
|
||||
{#each languages as language}
|
||||
<option value={language['code']}>{language['title']}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{#if $i18n.language === 'en-US'}
|
||||
<div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
Couldn't find your language?
|
||||
<a
|
||||
class=" text-gray-300 font-medium underline"
|
||||
href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization"
|
||||
target="_blank"
|
||||
>
|
||||
Help us translate Open WebUI!
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Desktop Notifications')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
on:click={() => {
|
||||
toggleNotification();
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
{#if notificationEnabled === true}
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
<hr class=" dark:border-gray-700 my-3" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">System Prompt</div>
|
||||
<textarea
|
||||
bind:value={system}
|
||||
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
|
||||
rows="4"
|
||||
/>
|
||||
<div>
|
||||
<div class=" my-2.5 text-sm font-medium">{$i18n.t('System Prompt')}</div>
|
||||
<textarea
|
||||
bind:value={system}
|
||||
class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
|
||||
rows="4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 space-y-3 pr-1.5">
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<div class=" font-medium">{$i18n.t('Advanced Parameters')}</div>
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showAdvanced = !showAdvanced;
|
||||
}}>{showAdvanced ? $i18n.t('Hide') : $i18n.t('Show')}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
{#if showAdvanced}
|
||||
<AdvancedParams bind:options />
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div class=" py-1 w-full justify-between">
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Keep Alive')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
keepAlive = keepAlive === null ? '5m' : null;
|
||||
}}
|
||||
>
|
||||
{#if keepAlive === null}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if keepAlive !== null}
|
||||
<div class="flex mt-1 space-x-2">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="text"
|
||||
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
|
||||
bind:value={keepAlive}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" py-1 flex w-full justify-between">
|
||||
<div class=" self-center text-sm font-medium">{$i18n.t('Request Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
on:click={() => {
|
||||
toggleRequestFormat();
|
||||
}}
|
||||
>
|
||||
{#if requestFormat === ''}
|
||||
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
|
||||
{:else if requestFormat === 'json'}
|
||||
<!-- <svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4 self-center"
|
||||
>
|
||||
<path
|
||||
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
|
||||
/>
|
||||
</svg> -->
|
||||
<span class="ml-2 self-center"> {$i18n.t('JSON')} </span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
on:click={() => {
|
||||
saveSettings({
|
||||
system: system !== '' ? system : undefined
|
||||
system: system !== '' ? system : undefined,
|
||||
options: {
|
||||
seed: (options.seed !== 0 ? options.seed : undefined) ?? undefined,
|
||||
stop: options.stop !== '' ? options.stop.split(',').filter((e) => e) : undefined,
|
||||
temperature: options.temperature !== '' ? options.temperature : undefined,
|
||||
repeat_penalty: options.repeat_penalty !== '' ? options.repeat_penalty : undefined,
|
||||
repeat_last_n: options.repeat_last_n !== '' ? options.repeat_last_n : undefined,
|
||||
mirostat: options.mirostat !== '' ? options.mirostat : undefined,
|
||||
mirostat_eta: options.mirostat_eta !== '' ? options.mirostat_eta : undefined,
|
||||
mirostat_tau: options.mirostat_tau !== '' ? options.mirostat_tau : undefined,
|
||||
top_k: options.top_k !== '' ? options.top_k : undefined,
|
||||
top_p: options.top_p !== '' ? options.top_p : undefined,
|
||||
tfs_z: options.tfs_z !== '' ? options.tfs_z : undefined,
|
||||
num_ctx: options.num_ctx !== '' ? options.num_ctx : undefined,
|
||||
num_predict: options.num_predict !== '' ? options.num_predict : undefined
|
||||
},
|
||||
keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined
|
||||
});
|
||||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
343
src/lib/components/chat/Settings/Images.svelte
Normal file
343
src/lib/components/chat/Settings/Images.svelte
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { config, user } from '$lib/stores';
|
||||
import {
|
||||
getAUTOMATIC1111Url,
|
||||
getImageGenerationModels,
|
||||
getDefaultImageGenerationModel,
|
||||
updateDefaultImageGenerationModel,
|
||||
getImageSize,
|
||||
getImageGenerationConfig,
|
||||
updateImageGenerationConfig,
|
||||
updateAUTOMATIC1111Url,
|
||||
updateImageSize,
|
||||
getImageSteps,
|
||||
updateImageSteps,
|
||||
getOpenAIKey,
|
||||
updateOpenAIKey
|
||||
} from '$lib/apis/images';
|
||||
import { getBackendConfig } from '$lib/apis';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveSettings: Function;
|
||||
|
||||
let loading = false;
|
||||
|
||||
let imageGenerationEngine = '';
|
||||
let enableImageGeneration = false;
|
||||
|
||||
let AUTOMATIC1111_BASE_URL = '';
|
||||
let OPENAI_API_KEY = '';
|
||||
|
||||
let selectedModel = '';
|
||||
let models = null;
|
||||
|
||||
let imageSize = '';
|
||||
let steps = 50;
|
||||
|
||||
const getModels = async () => {
|
||||
models = await getImageGenerationModels(localStorage.token).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
selectedModel = await getDefaultImageGenerationModel(localStorage.token).catch((error) => {
|
||||
return '';
|
||||
});
|
||||
};
|
||||
|
||||
const updateAUTOMATIC1111UrlHandler = async () => {
|
||||
const res = await updateAUTOMATIC1111Url(localStorage.token, AUTOMATIC1111_BASE_URL).catch(
|
||||
(error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
if (res) {
|
||||
AUTOMATIC1111_BASE_URL = res;
|
||||
|
||||
await getModels();
|
||||
|
||||
if (models) {
|
||||
toast.success($i18n.t('Server connection verified'));
|
||||
}
|
||||
} else {
|
||||
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
|
||||
}
|
||||
};
|
||||
const updateImageGeneration = async () => {
|
||||
const res = await updateImageGenerationConfig(
|
||||
localStorage.token,
|
||||
imageGenerationEngine,
|
||||
enableImageGeneration
|
||||
).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
imageGenerationEngine = res.engine;
|
||||
enableImageGeneration = res.enabled;
|
||||
}
|
||||
|
||||
if (enableImageGeneration) {
|
||||
config.set(await getBackendConfig(localStorage.token));
|
||||
getModels();
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
if ($user.role === 'admin') {
|
||||
const res = await getImageGenerationConfig(localStorage.token).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
imageGenerationEngine = res.engine;
|
||||
enableImageGeneration = res.enabled;
|
||||
}
|
||||
AUTOMATIC1111_BASE_URL = await getAUTOMATIC1111Url(localStorage.token);
|
||||
OPENAI_API_KEY = await getOpenAIKey(localStorage.token);
|
||||
|
||||
imageSize = await getImageSize(localStorage.token);
|
||||
steps = await getImageSteps(localStorage.token);
|
||||
|
||||
if (enableImageGeneration) {
|
||||
getModels();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<form
|
||||
class="flex flex-col h-full justify-between space-y-3 text-sm"
|
||||
on:submit|preventDefault={async () => {
|
||||
loading = true;
|
||||
|
||||
if (imageGenerationEngine === 'openai') {
|
||||
await updateOpenAIKey(localStorage.token, OPENAI_API_KEY);
|
||||
}
|
||||
|
||||
await updateDefaultImageGenerationModel(localStorage.token, selectedModel);
|
||||
|
||||
await updateImageSize(localStorage.token, imageSize).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
await updateImageSteps(localStorage.token, steps).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
dispatch('save');
|
||||
loading = false;
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[24rem]">
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('Image Settings')}</div>
|
||||
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Image Generation Engine')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<select
|
||||
class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
|
||||
bind:value={imageGenerationEngine}
|
||||
placeholder={$i18n.t('Select a mode')}
|
||||
on:change={async () => {
|
||||
await updateImageGeneration();
|
||||
}}
|
||||
>
|
||||
<option value="">{$i18n.t('Default (Automatic1111)')}</option>
|
||||
<option value="openai">{$i18n.t('Open AI (Dall-E)')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Image Generation (Experimental)')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
on:click={() => {
|
||||
if (imageGenerationEngine === '' && AUTOMATIC1111_BASE_URL === '') {
|
||||
toast.error($i18n.t('AUTOMATIC1111 Base URL is required.'));
|
||||
enableImageGeneration = false;
|
||||
} else if (imageGenerationEngine === 'openai' && OPENAI_API_KEY === '') {
|
||||
toast.error($i18n.t('OpenAI API Key is required.'));
|
||||
enableImageGeneration = false;
|
||||
} else {
|
||||
enableImageGeneration = !enableImageGeneration;
|
||||
}
|
||||
|
||||
updateImageGeneration();
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
{#if enableImageGeneration === true}
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
{#if imageGenerationEngine === ''}
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Base URL')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
|
||||
bind:value={AUTOMATIC1111_BASE_URL}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-600 dark:hover:bg-gray-700 rounded-lg transition"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
// updateOllamaAPIUrlHandler();
|
||||
|
||||
updateAUTOMATIC1111UrlHandler();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t('Include `--api` flag when running stable-diffusion-webui')}
|
||||
<a
|
||||
class=" text-gray-300 font-medium"
|
||||
href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/3734"
|
||||
target="_blank"
|
||||
>
|
||||
{$i18n.t('(e.g. `sh webui.sh --api`)')}
|
||||
</a>
|
||||
</div>
|
||||
{:else if imageGenerationEngine === 'openai'}
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('OpenAI API Key')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('Enter API Key')}
|
||||
bind:value={OPENAI_API_KEY}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if enableImageGeneration}
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Default Model')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={selectedModel}
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
>
|
||||
{#if !selectedModel}
|
||||
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
|
||||
{/if}
|
||||
{#each models ?? [] as model}
|
||||
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">{model.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Image Size')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
|
||||
bind:value={imageSize}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Steps')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<input
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
|
||||
bind:value={steps}
|
||||
/>
|
||||
</div>
|
||||
</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('Save')}
|
||||
|
||||
{#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>
|
||||
|
|
@ -2,10 +2,12 @@
|
|||
import { getBackendConfig } from '$lib/apis';
|
||||
import { setDefaultPromptSuggestions } from '$lib/apis/configs';
|
||||
import { config, models, user } from '$lib/stores';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveSettings: Function;
|
||||
|
||||
// Addons
|
||||
|
|
@ -13,6 +15,7 @@
|
|||
let responseAutoCopy = false;
|
||||
let titleAutoGenerateModel = '';
|
||||
let fullScreenMode = false;
|
||||
let titleGenerationPrompt = '';
|
||||
|
||||
// Interface
|
||||
let promptSuggestions = [];
|
||||
|
|
@ -56,8 +59,15 @@
|
|||
};
|
||||
|
||||
const updateInterfaceHandler = async () => {
|
||||
promptSuggestions = await setDefaultPromptSuggestions(localStorage.token, promptSuggestions);
|
||||
await config.set(await getBackendConfig());
|
||||
if ($user.role === 'admin') {
|
||||
promptSuggestions = await setDefaultPromptSuggestions(localStorage.token, promptSuggestions);
|
||||
await config.set(await getBackendConfig());
|
||||
}
|
||||
|
||||
saveSettings({
|
||||
titleAutoGenerateModel: titleAutoGenerateModel !== '' ? titleAutoGenerateModel : undefined,
|
||||
titleGenerationPrompt: titleGenerationPrompt ? titleGenerationPrompt : undefined
|
||||
});
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
|
|
@ -72,6 +82,11 @@
|
|||
showUsername = settings.showUsername ?? false;
|
||||
fullScreenMode = settings.fullScreenMode ?? false;
|
||||
titleAutoGenerateModel = settings.titleAutoGenerateModel ?? '';
|
||||
titleGenerationPrompt =
|
||||
settings.titleGenerationPrompt ??
|
||||
$i18n.t(
|
||||
"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':"
|
||||
) + ' {{prompt}}';
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -82,13 +97,13 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-80">
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
|
||||
<div>
|
||||
<div class=" mb-1 text-sm font-medium">WebUI Add-ons</div>
|
||||
<div class=" mb-1 text-sm font-medium">{$i18n.t('WebUI Add-ons')}</div>
|
||||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Title Auto-Generation</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Title Auto-Generation')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -98,9 +113,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if titleAutoGenerate === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -108,7 +123,9 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Response AutoCopy to Clipboard</div>
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Response AutoCopy to Clipboard')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -118,9 +135,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if responseAutoCopy === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -128,7 +145,7 @@
|
|||
|
||||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">Full Screen Mode</div>
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Full Screen Mode')}</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -138,9 +155,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if fullScreenMode === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -149,7 +166,7 @@
|
|||
<div>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">
|
||||
Display the username instead of "You" in the Chat
|
||||
{$i18n.t('Display the username instead of You in the Chat')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -160,9 +177,9 @@
|
|||
type="button"
|
||||
>
|
||||
{#if showUsername === true}
|
||||
<span class="ml-2 self-center">On</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('On')}</span>
|
||||
{:else}
|
||||
<span class="ml-2 self-center">Off</span>
|
||||
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -172,45 +189,33 @@
|
|||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">Set Title Auto-Generation Model</div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Title Auto-Generation Model')}</div>
|
||||
<div class="flex w-full">
|
||||
<div class="flex-1 mr-2">
|
||||
<select
|
||||
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
|
||||
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
|
||||
bind:value={titleAutoGenerateModel}
|
||||
placeholder="Select a model"
|
||||
placeholder={$i18n.t('Select a model')}
|
||||
>
|
||||
<option value="" selected>Current Model</option>
|
||||
{#each $models.filter((m) => m.size != null) as model}
|
||||
<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
|
||||
>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
|
||||
>
|
||||
<option value="" selected>{$i18n.t('Current Model')}</option>
|
||||
{#each $models as model}
|
||||
{#if model.size != null}
|
||||
<option value={model.name} class="bg-gray-100 dark:bg-gray-700">
|
||||
{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}
|
||||
</option>
|
||||
{/if}
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
class="px-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-800 dark:text-gray-100 rounded transition"
|
||||
on:click={() => {
|
||||
saveSettings({
|
||||
titleAutoGenerateModel:
|
||||
titleAutoGenerateModel !== '' ? titleAutoGenerateModel : undefined
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-3.5 h-3.5"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.932.75.75 0 0 1-1.3-.75 6 6 0 0 1 9.44-1.242l.842.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44 1.241l-.84-.84v1.371a.75.75 0 0 1-1.5 0V9.591a.75.75 0 0 1 .75-.75H5.35a.75.75 0 0 1 0 1.5H3.98l.841.841a4.5 4.5 0 0 0 7.08-.932.75.75 0 0 1 1.025-.273Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 mr-2">
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Title Generation Prompt')}</div>
|
||||
<textarea
|
||||
bind:value={titleGenerationPrompt}
|
||||
class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
|
||||
rows="3"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -219,7 +224,9 @@
|
|||
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div class="flex w-full justify-between mb-2">
|
||||
<div class=" self-center text-sm font-semibold">Default Prompt Suggestions</div>
|
||||
<div class=" self-center text-sm font-semibold">
|
||||
{$i18n.t('Default Prompt Suggestions')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded transition"
|
||||
|
|
@ -292,19 +299,19 @@
|
|||
|
||||
{#if promptSuggestions.length > 0}
|
||||
<div class="text-xs text-left w-full mt-2">
|
||||
Adjusting these settings will apply changes universally to all users.
|
||||
{$i18n.t('Adjusting these settings will apply changes universally to all users.')}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<div class="flex justify-end text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +1,24 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { models, settings, user } from '$lib/stores';
|
||||
|
||||
import { getOllamaModels } from '$lib/apis/ollama';
|
||||
import { getOpenAIModels } from '$lib/apis/openai';
|
||||
import { getLiteLLMModels } from '$lib/apis/litellm';
|
||||
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import Account from './Settings/Account.svelte';
|
||||
import Advanced from './Settings/Advanced.svelte';
|
||||
import About from './Settings/About.svelte';
|
||||
import Models from './Settings/Models.svelte';
|
||||
import General from './Settings/General.svelte';
|
||||
import External from './Settings/External.svelte';
|
||||
import Interface from './Settings/Interface.svelte';
|
||||
import Audio from './Settings/Audio.svelte';
|
||||
import Chats from './Settings/Chats.svelte';
|
||||
import Connections from './Settings/Connections.svelte';
|
||||
import Images from './Settings/Images.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
|
||||
|
|
@ -27,23 +31,29 @@
|
|||
|
||||
let selectedTab = 'general';
|
||||
|
||||
const getModels = async (type = 'all') => {
|
||||
const models = [];
|
||||
models.push(
|
||||
...(await getOllamaModels(localStorage.token).catch((error) => {
|
||||
toast.error(error);
|
||||
return [];
|
||||
}))
|
||||
);
|
||||
|
||||
if (type === 'all') {
|
||||
const openAIModels = await getOpenAIModels(localStorage.token).catch((error) => {
|
||||
const getModels = async () => {
|
||||
let models = await Promise.all([
|
||||
await getOllamaModels(localStorage.token).catch((error) => {
|
||||
console.log(error);
|
||||
return null;
|
||||
});
|
||||
models.push(...(openAIModels ? [{ name: 'hr' }, ...openAIModels] : []));
|
||||
}
|
||||
}),
|
||||
await getOpenAIModels(localStorage.token).catch((error) => {
|
||||
console.log(error);
|
||||
return null;
|
||||
}),
|
||||
await getLiteLLMModels(localStorage.token).catch((error) => {
|
||||
console.log(error);
|
||||
return null;
|
||||
})
|
||||
]);
|
||||
|
||||
models = models
|
||||
.filter((models) => models)
|
||||
.reduce((a, e, i, arr) => a.concat(e, ...(i < arr.length - 1 ? [{ name: 'hr' }] : [])), []);
|
||||
|
||||
// models.push(...(ollamaModels ? [{ name: 'hr' }, ...ollamaModels] : []));
|
||||
// models.push(...(openAIModels ? [{ name: 'hr' }, ...openAIModels] : []));
|
||||
// models.push(...(liteLLMModels ? [{ name: 'hr' }, ...liteLLMModels] : []));
|
||||
return models;
|
||||
};
|
||||
</script>
|
||||
|
|
@ -51,7 +61,7 @@
|
|||
<Modal bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
|
||||
<div class=" text-lg font-medium self-center">Settings</div>
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Settings')}</div>
|
||||
<button
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
|
|
@ -99,34 +109,34 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">General</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||
'advanced'
|
||||
? 'bg-gray-200 dark:bg-gray-700'
|
||||
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'advanced';
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M17 2.75a.75.75 0 00-1.5 0v5.5a.75.75 0 001.5 0v-5.5zM17 15.75a.75.75 0 00-1.5 0v1.5a.75.75 0 001.5 0v-1.5zM3.75 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5a.75.75 0 01.75-.75zM4.5 2.75a.75.75 0 00-1.5 0v5.5a.75.75 0 001.5 0v-5.5zM10 11a.75.75 0 01.75.75v5.5a.75.75 0 01-1.5 0v-5.5A.75.75 0 0110 11zM10.75 2.75a.75.75 0 00-1.5 0v1.5a.75.75 0 001.5 0v-1.5zM10 6a2 2 0 100 4 2 2 0 000-4zM3.75 10a2 2 0 100 4 2 2 0 000-4zM16.25 10a2 2 0 100 4 2 2 0 000-4z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">Advanced</div>
|
||||
<div class=" self-center">{$i18n.t('General')}</div>
|
||||
</button>
|
||||
|
||||
{#if $user?.role === 'admin'}
|
||||
<button
|
||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||
'connections'
|
||||
? 'bg-gray-200 dark:bg-gray-700'
|
||||
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'connections';
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M1 9.5A3.5 3.5 0 0 0 4.5 13H12a3 3 0 0 0 .917-5.857 2.503 2.503 0 0 0-3.198-3.019 3.5 3.5 0 0 0-6.628 2.171A3.5 3.5 0 0 0 1 9.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">{$i18n.t('Connections')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||
'models'
|
||||
|
|
@ -150,31 +160,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">Models</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||
'external'
|
||||
? 'bg-gray-200 dark:bg-gray-700'
|
||||
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'external';
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
d="M1 9.5A3.5 3.5 0 0 0 4.5 13H12a3 3 0 0 0 .917-5.857 2.503 2.503 0 0 0-3.198-3.019 3.5 3.5 0 0 0-6.628 2.171A3.5 3.5 0 0 0 1 9.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">External</div>
|
||||
<div class=" self-center">{$i18n.t('Models')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
|
|
@ -196,12 +182,12 @@
|
|||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm10.5 5.707a.5.5 0 0 0-.146-.353l-1-1a.5.5 0 0 0-.708 0L9.354 9.646a.5.5 0 0 1-.708 0L6.354 7.354a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0-.146.353V12a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5V9.707ZM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
|
||||
d="M2 4.25A2.25 2.25 0 0 1 4.25 2h7.5A2.25 2.25 0 0 1 14 4.25v5.5A2.25 2.25 0 0 1 11.75 12h-1.312c.1.128.21.248.328.36a.75.75 0 0 1 .234.545v.345a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-.345a.75.75 0 0 1 .234-.545c.118-.111.228-.232.328-.36H4.25A2.25 2.25 0 0 1 2 9.75v-5.5Zm2.25-.75a.75.75 0 0 0-.75.75v4.5c0 .414.336.75.75.75h7.5a.75.75 0 0 0 .75-.75v-4.5a.75.75 0 0 0-.75-.75h-7.5Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">Interface</div>
|
||||
<div class=" self-center">{$i18n.t('Interface')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -228,9 +214,37 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">Audio</div>
|
||||
<div class=" self-center">{$i18n.t('Audio')}</div>
|
||||
</button>
|
||||
|
||||
{#if $user.role === 'admin'}
|
||||
<button
|
||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||
'images'
|
||||
? 'bg-gray-200 dark:bg-gray-700'
|
||||
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'images';
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
<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="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm10.5 5.707a.5.5 0 0 0-.146-.353l-1-1a.5.5 0 0 0-.708 0L9.354 9.646a.5.5 0 0 1-.708 0L6.354 7.354a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0-.146.353V12a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5V9.707ZM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">{$i18n.t('Images')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||
'chats'
|
||||
|
|
@ -254,7 +268,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">Chats</div>
|
||||
<div class=" self-center">{$i18n.t('Chats')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -280,7 +294,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">Account</div>
|
||||
<div class=" self-center">{$i18n.t('Account')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -306,46 +320,46 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center">About</div>
|
||||
<div class=" self-center">{$i18n.t('About')}</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1 md:min-h-[380px]">
|
||||
<div class="flex-1 md:min-h-[25rem]">
|
||||
{#if selectedTab === 'general'}
|
||||
<General
|
||||
{getModels}
|
||||
{saveSettings}
|
||||
on:save={() => {
|
||||
show = false;
|
||||
toast.success($i18n.t('Settings saved successfully!'));
|
||||
}}
|
||||
/>
|
||||
{:else if selectedTab === 'advanced'}
|
||||
<Advanced
|
||||
on:save={() => {
|
||||
show = false;
|
||||
}}
|
||||
{saveSettings}
|
||||
/>
|
||||
{:else if selectedTab === 'models'}
|
||||
<Models {getModels} />
|
||||
{:else if selectedTab === 'external'}
|
||||
<External
|
||||
{:else if selectedTab === 'connections'}
|
||||
<Connections
|
||||
{getModels}
|
||||
on:save={() => {
|
||||
show = false;
|
||||
toast.success($i18n.t('Settings saved successfully!'));
|
||||
}}
|
||||
/>
|
||||
{:else if selectedTab === 'interface'}
|
||||
<Interface
|
||||
{saveSettings}
|
||||
on:save={() => {
|
||||
show = false;
|
||||
toast.success($i18n.t('Settings saved successfully!'));
|
||||
}}
|
||||
/>
|
||||
{:else if selectedTab === 'audio'}
|
||||
<Audio
|
||||
{saveSettings}
|
||||
on:save={() => {
|
||||
show = false;
|
||||
toast.success($i18n.t('Settings saved successfully!'));
|
||||
}}
|
||||
/>
|
||||
{:else if selectedTab === 'images'}
|
||||
<Images
|
||||
{saveSettings}
|
||||
on:save={() => {
|
||||
toast.success($i18n.t('Settings saved successfully!'));
|
||||
}}
|
||||
/>
|
||||
{:else if selectedTab === 'chats'}
|
||||
|
|
@ -353,7 +367,7 @@
|
|||
{:else if selectedTab === 'account'}
|
||||
<Account
|
||||
saveHandler={() => {
|
||||
show = false;
|
||||
toast.success($i18n.t('Settings saved successfully!'));
|
||||
}}
|
||||
/>
|
||||
{:else if selectedTab === 'about'}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let downloadChat: Function;
|
||||
export let shareChat: Function;
|
||||
|
||||
|
|
@ -17,11 +20,11 @@
|
|||
show = false;
|
||||
}}
|
||||
>
|
||||
Share to OpenWebUI Community
|
||||
{$i18n.t('Share to OpenWebUI Community')}
|
||||
</button>
|
||||
|
||||
<div class="flex justify-center space-x-1 mt-1.5">
|
||||
<div class=" self-center text-gray-400 text-xs font-medium">or</div>
|
||||
<div class=" self-center text-gray-400 text-xs font-medium">{$i18n.t('or')}</div>
|
||||
|
||||
<button
|
||||
class=" self-center rounded-full text-xs font-medium text-gray-700 dark:text-gray-500 underline"
|
||||
|
|
@ -31,7 +34,7 @@
|
|||
show = false;
|
||||
}}
|
||||
>
|
||||
Download as a File
|
||||
{$i18n.t('Download as a File')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
</script>
|
||||
|
||||
<Modal bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
|
||||
<div class=" text-lg font-medium self-center">Keyboard shortcuts</div>
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Keyboard shortcuts')}</div>
|
||||
<button
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
|
|
@ -32,7 +35,7 @@
|
|||
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
|
||||
<div class="flex flex-col space-y-3 w-full self-start">
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class=" text-sm">Open new chat</div>
|
||||
<div class=" text-sm">{$i18n.t('Open new chat')}</div>
|
||||
|
||||
<div class="flex space-x-1 text-xs">
|
||||
<div
|
||||
|
|
@ -56,7 +59,7 @@
|
|||
</div>
|
||||
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class=" text-sm">Focus chat input</div>
|
||||
<div class=" text-sm">{$i18n.t('Focus chat input')}</div>
|
||||
|
||||
<div class="flex space-x-1 text-xs">
|
||||
<div
|
||||
|
|
@ -74,7 +77,7 @@
|
|||
</div>
|
||||
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class=" text-sm">Copy last code block</div>
|
||||
<div class=" text-sm">{$i18n.t('Copy last code block')}</div>
|
||||
|
||||
<div class="flex space-x-1 text-xs">
|
||||
<div
|
||||
|
|
@ -98,7 +101,7 @@
|
|||
</div>
|
||||
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class=" text-sm">Copy last response</div>
|
||||
<div class=" text-sm">{$i18n.t('Copy last response')}</div>
|
||||
|
||||
<div class="flex space-x-1 text-xs">
|
||||
<div
|
||||
|
|
@ -124,7 +127,7 @@
|
|||
|
||||
<div class="flex flex-col space-y-3 w-full self-start">
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class=" text-sm">Toggle settings</div>
|
||||
<div class=" text-sm">{$i18n.t('Toggle settings')}</div>
|
||||
|
||||
<div class="flex space-x-1 text-xs">
|
||||
<div
|
||||
|
|
@ -141,7 +144,7 @@
|
|||
</div>
|
||||
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class=" text-sm">Toggle sidebar</div>
|
||||
<div class=" text-sm">{$i18n.t('Toggle sidebar')}</div>
|
||||
|
||||
<div class="flex space-x-1 text-xs">
|
||||
<div
|
||||
|
|
@ -165,7 +168,7 @@
|
|||
</div>
|
||||
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class=" text-sm">Delete chat</div>
|
||||
<div class=" text-sm">{$i18n.t('Delete chat')}</div>
|
||||
|
||||
<div class="flex space-x-1 text-xs">
|
||||
<div
|
||||
|
|
@ -188,7 +191,7 @@
|
|||
</div>
|
||||
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class=" text-sm">Show shortcuts</div>
|
||||
<div class=" text-sm">{$i18n.t('Show shortcuts')}</div>
|
||||
|
||||
<div class="flex space-x-1 text-xs">
|
||||
<div
|
||||
|
|
|
|||
70
src/lib/components/common/Checkbox.svelte
Normal file
70
src/lib/components/common/Checkbox.svelte
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let state = 'unchecked';
|
||||
export let indeterminate = false;
|
||||
|
||||
let _state = 'unchecked';
|
||||
|
||||
$: _state = state;
|
||||
</script>
|
||||
|
||||
<button
|
||||
class=" outline -outline-offset-1 outline-[1.5px] outline-gray-200 dark:outline-gray-600 {state !==
|
||||
'unchecked'
|
||||
? 'bg-black outline-black '
|
||||
: 'hover:outline-gray-500 hover:bg-gray-50 dark:hover:bg-gray-800'} text-white transition-all rounded inline-block w-3.5 h-3.5 relative"
|
||||
on:click={() => {
|
||||
if (_state === 'unchecked') {
|
||||
_state = 'checked';
|
||||
dispatch('change', _state);
|
||||
} else if (_state === 'checked') {
|
||||
_state = 'unchecked';
|
||||
if (!indeterminate) {
|
||||
dispatch('change', _state);
|
||||
}
|
||||
} else if (indeterminate) {
|
||||
_state = 'checked';
|
||||
dispatch('change', _state);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="top-0 left-0 absolute w-full flex justify-center">
|
||||
{#if _state === 'checked'}
|
||||
<svg
|
||||
class="w-3.5 h-3.5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="3"
|
||||
d="m5 12 4.7 4.5 9.3-9"
|
||||
/>
|
||||
</svg>
|
||||
{:else if indeterminate}
|
||||
<svg
|
||||
class="w-3 h-3.5 text-gray-800 dark:text-white"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="3"
|
||||
d="M5 12h14"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- {checked} -->
|
||||
</button>
|
||||
37
src/lib/components/common/Dropdown.svelte
Normal file
37
src/lib/components/common/Dropdown.svelte
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<script lang="ts">
|
||||
import { DropdownMenu } from 'bits-ui';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Root
|
||||
onOpenChange={(state) => {
|
||||
dispatch('change', state);
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.Trigger>
|
||||
<slot />
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<slot name="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full max-w-[130px] rounded-lg px-1 py-1.5 border border-gray-700 z-50 bg-gray-850 text-white"
|
||||
sideOffset={8}
|
||||
side="bottom"
|
||||
align="start"
|
||||
>
|
||||
<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm font-medium">
|
||||
<div class="flex items-center">Profile</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm font-medium">
|
||||
<div class="flex items-center">Profile</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm font-medium">
|
||||
<div class="flex items-center">Profile</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</slot>
|
||||
</DropdownMenu.Root>
|
||||
23
src/lib/components/common/Image.svelte
Normal file
23
src/lib/components/common/Image.svelte
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import ImagePreview from './ImagePreview.svelte';
|
||||
|
||||
export let src = '';
|
||||
export let alt = '';
|
||||
|
||||
let _src = '';
|
||||
|
||||
$: _src = src.startsWith('/') ? `${WEBUI_BASE_URL}${src}` : src;
|
||||
|
||||
let showImagePreview = false;
|
||||
</script>
|
||||
|
||||
<ImagePreview bind:show={showImagePreview} src={_src} {alt} />
|
||||
<button
|
||||
on:click={() => {
|
||||
console.log('image preview');
|
||||
showImagePreview = true;
|
||||
}}
|
||||
>
|
||||
<img src={_src} {alt} class=" max-h-96 rounded-lg" draggable="false" />
|
||||
</button>
|
||||
62
src/lib/components/common/ImagePreview.svelte
Normal file
62
src/lib/components/common/ImagePreview.svelte
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<script lang="ts">
|
||||
export let show = false;
|
||||
export let src = '';
|
||||
export let alt = '';
|
||||
</script>
|
||||
|
||||
{#if show}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="fixed top-0 right-0 left-0 bottom-0 bg-black text-white w-full min-h-screen h-screen flex justify-center z-50 overflow-hidden overscroll-contain"
|
||||
>
|
||||
<div class=" absolute left-0 w-full flex justify-between">
|
||||
<div>
|
||||
<button
|
||||
class=" p-5"
|
||||
on:click={() => {
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
class=" p-5"
|
||||
on:click={() => {
|
||||
const a = document.createElement('a');
|
||||
a.href = src;
|
||||
a.download = 'Image.png';
|
||||
a.click();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-6 h-6"
|
||||
>
|
||||
<path
|
||||
d="M10.75 2.75a.75.75 0 0 0-1.5 0v8.614L6.295 8.235a.75.75 0 1 0-1.09 1.03l4.25 4.5a.75.75 0 0 0 1.09 0l4.25-4.5a.75.75 0 0 0-1.09-1.03l-2.955 3.129V2.75Z"
|
||||
/>
|
||||
<path
|
||||
d="M3.5 12.75a.75.75 0 0 0-1.5 0v2.5A2.75 2.75 0 0 0 4.75 18h10.5A2.75 2.75 0 0 0 18 15.25v-2.5a.75.75 0 0 0-1.5 0v2.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-2.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<img {src} {alt} class=" mx-auto h-full object-scale-down" />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { fade, blur } from 'svelte/transition';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
export let show = true;
|
||||
export let size = 'md';
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
} else if (size === 'sm') {
|
||||
return 'w-[30rem]';
|
||||
} else {
|
||||
return 'w-[42rem]';
|
||||
return 'w-[44rem]';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -34,16 +34,17 @@
|
|||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class="fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full min-h-screen h-screen flex justify-center z-50 overflow-hidden overscroll-contain"
|
||||
class=" fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full min-h-screen h-screen flex justify-center z-50 overflow-hidden overscroll-contain"
|
||||
in:fade={{ duration: 10 }}
|
||||
on:click={() => {
|
||||
show = false;
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="m-auto rounded-xl max-w-full {sizeToWidth(
|
||||
class=" modal-content m-auto rounded-2xl max-w-full {sizeToWidth(
|
||||
size
|
||||
)} mx-2 bg-gray-50 dark:bg-gray-900 shadow-3xl"
|
||||
transition:fade={{ delay: 100, duration: 200 }}
|
||||
in:fade={{ duration: 10 }}
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
|
|
@ -52,3 +53,20 @@
|
|||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.modal-content {
|
||||
animation: scaleUp 0.1s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes scaleUp {
|
||||
from {
|
||||
transform: scale(0.985);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { createEventDispatcher, getContext } from 'svelte';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let showTagInput = false;
|
||||
let tagName = '';
|
||||
</script>
|
||||
|
|
@ -9,12 +11,6 @@
|
|||
<div class="flex space-x-1 pl-1.5">
|
||||
{#if showTagInput}
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
bind:value={tagName}
|
||||
class=" cursor-pointer self-center text-xs h-fit bg-transparent outline-none line-clamp-1 w-[4rem]"
|
||||
placeholder="Add a tag"
|
||||
/>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => {
|
||||
|
|
@ -36,6 +32,11 @@
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<input
|
||||
bind:value={tagName}
|
||||
class=" pl-2 cursor-pointer self-center text-xs h-fit bg-transparent outline-none line-clamp-1 w-[8rem]"
|
||||
placeholder={$i18n.t('Add a tag')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Tag Suggestions -->
|
||||
|
|
|
|||
34
src/lib/components/common/Tooltip.svelte
Normal file
34
src/lib/components/common/Tooltip.svelte
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte';
|
||||
import tippy from 'tippy.js';
|
||||
|
||||
export let placement = 'top';
|
||||
export let content = `I'm a tooltip!`;
|
||||
export let touch = true;
|
||||
|
||||
let tooltipElement;
|
||||
let tooltipInstance;
|
||||
|
||||
$: if (tooltipElement && content) {
|
||||
if (tooltipInstance) {
|
||||
tooltipInstance.setContent(content);
|
||||
} else {
|
||||
tooltipInstance = tippy(tooltipElement, {
|
||||
content: content,
|
||||
placement: placement,
|
||||
allowHTML: true,
|
||||
touch: touch
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (tooltipInstance) {
|
||||
tooltipInstance.destroy();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={tooltipElement} aria-label={content}>
|
||||
<slot />
|
||||
</div>
|
||||
197
src/lib/components/documents/AddDocModal.svelte
Normal file
197
src/lib/components/documents/AddDocModal.svelte
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import dayjs from 'dayjs';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
import { createNewDoc, getDocs, tagDocByName, updateDocByName } from '$lib/apis/documents';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import { documents } from '$lib/stores';
|
||||
import TagInput from '../common/Tags/TagInput.svelte';
|
||||
import Tags from '../common/Tags.svelte';
|
||||
import { addTagById } from '$lib/apis/chats';
|
||||
import { uploadDocToVectorDB } from '$lib/apis/rag';
|
||||
import { transformFileName } from '$lib/utils';
|
||||
import { SUPPORTED_FILE_EXTENSIONS, SUPPORTED_FILE_TYPE } from '$lib/constants';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
export let selectedDoc;
|
||||
let uploadDocInputElement: HTMLInputElement;
|
||||
let inputFiles;
|
||||
let tags = [];
|
||||
|
||||
let doc = {
|
||||
name: '',
|
||||
title: '',
|
||||
content: null
|
||||
};
|
||||
|
||||
const uploadDoc = async (file) => {
|
||||
const res = await uploadDocToVectorDB(localStorage.token, '', file).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
await createNewDoc(
|
||||
localStorage.token,
|
||||
res.collection_name,
|
||||
res.filename,
|
||||
transformFileName(res.filename),
|
||||
res.filename,
|
||||
tags.length > 0
|
||||
? {
|
||||
tags: tags
|
||||
}
|
||||
: null
|
||||
).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
await documents.set(await getDocs(localStorage.token));
|
||||
}
|
||||
};
|
||||
|
||||
const submitHandler = async () => {
|
||||
if (inputFiles && inputFiles.length > 0) {
|
||||
for (const file of inputFiles) {
|
||||
console.log(file, file.name.split('.').at(-1));
|
||||
if (
|
||||
SUPPORTED_FILE_TYPE.includes(file['type']) ||
|
||||
SUPPORTED_FILE_EXTENSIONS.includes(file.name.split('.').at(-1))
|
||||
) {
|
||||
uploadDoc(file);
|
||||
} else {
|
||||
toast.error(
|
||||
`Unknown File Type '${file['type']}', but accepting and treating as plain text`
|
||||
);
|
||||
uploadDoc(file);
|
||||
}
|
||||
}
|
||||
|
||||
inputFiles = null;
|
||||
uploadDocInputElement.value = '';
|
||||
} else {
|
||||
toast.error($i18n.t(`File not found.`));
|
||||
}
|
||||
|
||||
show = false;
|
||||
documents.set(await getDocs(localStorage.token));
|
||||
};
|
||||
|
||||
const addTagHandler = async (tagName) => {
|
||||
if (!tags.find((tag) => tag.name === tagName) && tagName !== '') {
|
||||
tags = [...tags, { name: tagName }];
|
||||
} else {
|
||||
console.log('tag already exists');
|
||||
}
|
||||
};
|
||||
|
||||
const deleteTagHandler = async (tagName) => {
|
||||
tags = tags.filter((tag) => tag.name !== tagName);
|
||||
};
|
||||
|
||||
onMount(() => {});
|
||||
</script>
|
||||
|
||||
<Modal size="sm" bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Add Docs')}</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>
|
||||
<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
|
||||
class="flex flex-col w-full"
|
||||
on:submit|preventDefault={() => {
|
||||
submitHandler();
|
||||
}}
|
||||
>
|
||||
<div class="mb-3 w-full">
|
||||
<input
|
||||
id="upload-doc-input"
|
||||
bind:this={uploadDocInputElement}
|
||||
hidden
|
||||
bind:files={inputFiles}
|
||||
type="file"
|
||||
multiple
|
||||
/>
|
||||
|
||||
<button
|
||||
class="w-full text-sm font-medium py-3 bg-gray-100 hover:bg-gray-200 dark:bg-gray-850 dark:hover:bg-gray-800 text-center rounded-xl"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
uploadDocInputElement.click();
|
||||
}}
|
||||
>
|
||||
{#if inputFiles}
|
||||
{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected.
|
||||
{:else}
|
||||
{$i18n.t('Click here to select documents.')}
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class=" flex flex-col space-y-1.5">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1.5 text-xs text-gray-500">{$i18n.t('Tags')}</div>
|
||||
|
||||
<Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-5 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
type="submit"
|
||||
>
|
||||
{$i18n.t('Save')}
|
||||
</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>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import dayjs from 'dayjs';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
import { getDocs, tagDocByName, updateDocByName } from '$lib/apis/documents';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
|
|
@ -10,6 +10,8 @@
|
|||
import Tags from '../common/Tags.svelte';
|
||||
import { addTagById } from '$lib/apis/chats';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
export let selectedDoc;
|
||||
|
||||
|
|
@ -74,7 +76,7 @@
|
|||
<Modal size="sm" bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
|
||||
<div class=" text-lg font-medium self-center">Edit Doc</div>
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Edit Doc')}</div>
|
||||
<button
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
|
|
@ -105,7 +107,7 @@
|
|||
>
|
||||
<div class=" flex flex-col space-y-1.5">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Name Tag</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name Tag')}</div>
|
||||
|
||||
<div class="flex flex-1">
|
||||
<div
|
||||
|
|
@ -134,7 +136,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1 text-xs text-gray-500">Title</div>
|
||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Title')}</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
|
|
@ -148,7 +150,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class=" mb-1.5 text-xs text-gray-500">Tags</div>
|
||||
<div class=" mb-1.5 text-xs text-gray-500">{$i18n.t('Tags')}</div>
|
||||
|
||||
<Tags {tags} addTag={addTagHandler} deleteTag={deleteTagHandler} />
|
||||
</div>
|
||||
|
|
@ -159,7 +161,7 @@
|
|||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
244
src/lib/components/documents/Settings/General.svelte
Normal file
244
src/lib/components/documents/Settings/General.svelte
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
<script lang="ts">
|
||||
import { getDocs } from '$lib/apis/documents';
|
||||
import {
|
||||
getRAGConfig,
|
||||
updateRAGConfig,
|
||||
getQuerySettings,
|
||||
scanDocs,
|
||||
updateQuerySettings
|
||||
} from '$lib/apis/rag';
|
||||
import { documents } from '$lib/stores';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let saveHandler: Function;
|
||||
|
||||
let loading = false;
|
||||
|
||||
let chunkSize = 0;
|
||||
let chunkOverlap = 0;
|
||||
let pdfExtractImages = true;
|
||||
|
||||
let querySettings = {
|
||||
template: '',
|
||||
k: 4
|
||||
};
|
||||
|
||||
const scanHandler = async () => {
|
||||
loading = true;
|
||||
const res = await scanDocs(localStorage.token);
|
||||
loading = false;
|
||||
|
||||
if (res) {
|
||||
await documents.set(await getDocs(localStorage.token));
|
||||
toast.success($i18n.t('Scan complete!'));
|
||||
}
|
||||
};
|
||||
|
||||
const submitHandler = async () => {
|
||||
const res = await updateRAGConfig(localStorage.token, {
|
||||
pdf_extract_images: pdfExtractImages,
|
||||
chunk: {
|
||||
chunk_overlap: chunkOverlap,
|
||||
chunk_size: chunkSize
|
||||
}
|
||||
});
|
||||
querySettings = await updateQuerySettings(localStorage.token, querySettings);
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
const res = await getRAGConfig(localStorage.token);
|
||||
|
||||
if (res) {
|
||||
pdfExtractImages = res.pdf_extract_images;
|
||||
|
||||
chunkSize = res.chunk.chunk_size;
|
||||
chunkOverlap = res.chunk.chunk_overlap;
|
||||
}
|
||||
|
||||
querySettings = await getQuerySettings(localStorage.token);
|
||||
});
|
||||
</script>
|
||||
|
||||
<form
|
||||
class="flex flex-col h-full justify-between space-y-3 text-sm"
|
||||
on:submit|preventDefault={() => {
|
||||
submitHandler();
|
||||
saveHandler();
|
||||
}}
|
||||
>
|
||||
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
|
||||
<div>
|
||||
<div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
|
||||
|
||||
<div class=" flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Scan for documents from {{path}}', { path: '/data/docs' })}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class=" self-center text-xs p-1 px-3 bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded flex flex-row space-x-1 items-center {loading
|
||||
? ' cursor-not-allowed'
|
||||
: ''}"
|
||||
on:click={() => {
|
||||
scanHandler();
|
||||
console.log('check');
|
||||
}}
|
||||
type="button"
|
||||
disabled={loading}
|
||||
>
|
||||
<div class="self-center font-medium">{$i18n.t('Scan')}</div>
|
||||
|
||||
<!-- <svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="w-3 h-3"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.932.75.75 0 0 1-1.3-.75 6 6 0 0 1 9.44-1.242l.842.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44 1.241l-.84-.84v1.371a.75.75 0 0 1-1.5 0V9.591a.75.75 0 0 1 .75-.75H5.35a.75.75 0 0 1 0 1.5H3.98l.841.841a4.5 4.5 0 0 0 7.08-.932.75.75 0 0 1 1.025-.273Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg> -->
|
||||
|
||||
{#if loading}
|
||||
<div class="ml-3 self-center">
|
||||
<svg
|
||||
class=" w-3 h-3"
|
||||
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>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-700" />
|
||||
|
||||
<div class=" ">
|
||||
<div class=" text-sm font-medium">{$i18n.t('Chunk Params')}</div>
|
||||
|
||||
<div class=" flex">
|
||||
<div class=" flex w-full justify-between">
|
||||
<div class="self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Size')}</div>
|
||||
|
||||
<div class="self-center p-3">
|
||||
<input
|
||||
class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="number"
|
||||
placeholder={$i18n.t('Enter Chunk Size')}
|
||||
bind:value={chunkSize}
|
||||
autocomplete="off"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
<div class=" self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Overlap')}</div>
|
||||
|
||||
<div class="self-center p-3">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="number"
|
||||
placeholder={$i18n.t('Enter Chunk Overlap')}
|
||||
bind:value={chunkOverlap}
|
||||
autocomplete="off"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex justify-between items-center text-xs">
|
||||
<div class=" text-xs font-medium">{$i18n.t('PDF Extract Images (OCR)')}</div>
|
||||
|
||||
<button
|
||||
class=" text-xs font-medium text-gray-500"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
pdfExtractImages = !pdfExtractImages;
|
||||
}}>{pdfExtractImages ? $i18n.t('On') : $i18n.t('Off')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>
|
||||
|
||||
<div class=" flex">
|
||||
<div class=" flex w-full justify-between">
|
||||
<div class="self-center text-xs font-medium flex-1">{$i18n.t('Top K')}</div>
|
||||
|
||||
<div class="self-center p-3">
|
||||
<input
|
||||
class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="number"
|
||||
placeholder={$i18n.t('Enter Top K')}
|
||||
bind:value={querySettings.k}
|
||||
autocomplete="off"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="flex w-full">
|
||||
<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div>
|
||||
|
||||
<div class="self-center p-3">
|
||||
<input
|
||||
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
|
||||
type="number"
|
||||
placeholder="Enter Chunk Overlap"
|
||||
bind:value={chunkOverlap}
|
||||
autocomplete="off"
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
|
||||
<textarea
|
||||
bind:value={querySettings.template}
|
||||
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
|
||||
rows="4"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
|
||||
type="submit"
|
||||
>
|
||||
{$i18n.t('Save')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
89
src/lib/components/documents/SettingsModal.svelte
Normal file
89
src/lib/components/documents/SettingsModal.svelte
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<script>
|
||||
import { getContext } from 'svelte';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import General from './Settings/General.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
|
||||
let selectedTab = 'general';
|
||||
</script>
|
||||
|
||||
<Modal bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Document Settings')}</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>
|
||||
<hr class=" dark:border-gray-800" />
|
||||
|
||||
<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
|
||||
<div
|
||||
class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
|
||||
>
|
||||
<button
|
||||
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
|
||||
'general'
|
||||
? 'bg-gray-200 dark:bg-gray-700'
|
||||
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'general';
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
<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>
|
||||
<div class=" self-center">{$i18n.t('General')}</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1 md:min-h-[380px]">
|
||||
{#if selectedTab === 'general'}
|
||||
<General
|
||||
saveHandler={() => {
|
||||
show = false;
|
||||
}}
|
||||
/>
|
||||
<!-- <General
|
||||
saveHandler={() => {
|
||||
show = false;
|
||||
}}
|
||||
/> -->
|
||||
<!-- {:else if selectedTab === 'users'}
|
||||
<Users
|
||||
saveHandler={() => {
|
||||
show = false;
|
||||
}}
|
||||
/> -->
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
19
src/lib/components/icons/GarbageBin.svelte
Normal file
19
src/lib/components/icons/GarbageBin.svelte
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<script lang="ts">
|
||||
export let className = 'w-4 h-4';
|
||||
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="M14.74 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
19
src/lib/components/icons/Pencil.svelte
Normal file
19
src/lib/components/icons/Pencil.svelte
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<script lang="ts">
|
||||
export let className = 'w-4 h-4';
|
||||
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="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
||||
/>
|
||||
</svg>
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
<script lang="ts">
|
||||
import toast from 'svelte-french-toast';
|
||||
import { getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import fileSaver from 'file-saver';
|
||||
const { saveAs } = fileSaver;
|
||||
|
||||
import { getChatById } from '$lib/apis/chats';
|
||||
import { chatId, modelfiles, settings } from '$lib/stores';
|
||||
import { WEBUI_NAME, chatId, modelfiles, settings } from '$lib/stores';
|
||||
import ShareChatModal from '../chat/ShareChatModal.svelte';
|
||||
import TagInput from '../common/Tags/TagInput.svelte';
|
||||
import Tags from '../common/Tags.svelte';
|
||||
import { WEBUI_NAME } from '$lib/constants';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let initNewChat: Function;
|
||||
export let title: string = WEBUI_NAME;
|
||||
export let title: string = $WEBUI_NAME;
|
||||
export let shareEnabled: boolean = false;
|
||||
|
||||
export let tags = [];
|
||||
|
|
@ -27,7 +29,7 @@
|
|||
const chat = (await getChatById(localStorage.token, $chatId)).chat;
|
||||
console.log('share', chat);
|
||||
|
||||
toast.success('Redirecting you to OpenWebUI Community');
|
||||
toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
|
||||
const url = 'https://openwebui.com';
|
||||
// const url = 'http://localhost:5173';
|
||||
|
||||
|
|
@ -102,7 +104,7 @@
|
|||
</div>
|
||||
<div class=" flex-1 self-center font-medium line-clamp-1">
|
||||
<div>
|
||||
{title != '' ? title : WEBUI_NAME}
|
||||
{title != '' ? title : $WEBUI_NAME}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,15 +7,24 @@
|
|||
import { goto, invalidateAll } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { user, chats, settings, showSettings, chatId, tags } from '$lib/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import {
|
||||
deleteChatById,
|
||||
getChatList,
|
||||
getChatById,
|
||||
getChatListByTagName,
|
||||
updateChatById
|
||||
updateChatById,
|
||||
getAllChatTags
|
||||
} from '$lib/apis/chats';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { WEBUI_BASE_URL } from '$lib/constants';
|
||||
import Tooltip from '../common/Tooltip.svelte';
|
||||
import Dropdown from '../common/Dropdown.svelte';
|
||||
import ChatMenu from './Sidebar/ChatMenu.svelte';
|
||||
|
||||
let show = false;
|
||||
let navElement;
|
||||
|
|
@ -23,14 +32,17 @@
|
|||
let title: string = 'UI';
|
||||
let search = '';
|
||||
|
||||
let selectedChatId = null;
|
||||
|
||||
let chatDeleteId = null;
|
||||
let chatTitleEditId = null;
|
||||
let chatTitle = '';
|
||||
|
||||
let showDropdown = false;
|
||||
let isEditing = false;
|
||||
|
||||
onMount(async () => {
|
||||
if (window.innerWidth > 1280) {
|
||||
if (window.innerWidth > 1024) {
|
||||
show = true;
|
||||
}
|
||||
await chats.set(await getChatList(localStorage.token));
|
||||
|
|
@ -56,12 +68,16 @@
|
|||
};
|
||||
|
||||
const editChatTitle = async (id, _title) => {
|
||||
title = _title;
|
||||
if (_title === '') {
|
||||
toast.error('Title cannot be an empty string.');
|
||||
} else {
|
||||
title = _title;
|
||||
|
||||
await updateChatById(localStorage.token, id, {
|
||||
title: _title
|
||||
});
|
||||
await chats.set(await getChatList(localStorage.token));
|
||||
await updateChatById(localStorage.token, id, {
|
||||
title: _title
|
||||
});
|
||||
await chats.set(await getChatList(localStorage.token));
|
||||
}
|
||||
};
|
||||
|
||||
const deleteChat = async (id) => {
|
||||
|
|
@ -73,7 +89,10 @@
|
|||
});
|
||||
|
||||
if (res) {
|
||||
goto('/');
|
||||
if ($chatId === id) {
|
||||
goto('/');
|
||||
}
|
||||
|
||||
await chats.set(await getChatList(localStorage.token));
|
||||
}
|
||||
};
|
||||
|
|
@ -97,26 +116,31 @@
|
|||
? ''
|
||||
: 'invisible'}"
|
||||
>
|
||||
<div class="px-2.5 flex justify-center space-x-2">
|
||||
<button
|
||||
<div class="px-2 flex justify-center space-x-2">
|
||||
<a
|
||||
id="sidebar-new-chat-button"
|
||||
class="flex-grow flex justify-between rounded-md px-3 py-2 hover:bg-gray-900 transition"
|
||||
class="flex-grow flex justify-between rounded-xl px-3.5 py-2 hover:bg-gray-900 transition"
|
||||
href="/"
|
||||
on:click={async () => {
|
||||
goto('/');
|
||||
selectedChatId = null;
|
||||
|
||||
await goto('/');
|
||||
const newChatButton = document.getElementById('new-chat-button');
|
||||
|
||||
if (newChatButton) {
|
||||
newChatButton.click();
|
||||
}
|
||||
setTimeout(() => {
|
||||
newChatButton?.click();
|
||||
}, 0);
|
||||
}}
|
||||
>
|
||||
<div class="flex self-center">
|
||||
<div class="self-center mr-1.5">
|
||||
<img src="/favicon.png" class=" w-7 -translate-x-1.5 rounded-full" alt="logo" />
|
||||
<img
|
||||
src="{WEBUI_BASE_URL}/static/favicon.png"
|
||||
class=" w-7 -translate-x-1.5 rounded-full"
|
||||
alt="logo"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class=" self-center font-medium text-sm">New Chat</div>
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('New Chat')}</div>
|
||||
</div>
|
||||
|
||||
<div class="self-center">
|
||||
|
|
@ -134,15 +158,17 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{#if $user?.role === 'admin'}
|
||||
<div class="px-2.5 flex justify-center mt-0.5">
|
||||
<button
|
||||
class="flex-grow flex space-x-3 rounded-md px-3 py-2 hover:bg-gray-900 transition"
|
||||
on:click={async () => {
|
||||
goto('/modelfiles');
|
||||
<div class="px-2 flex justify-center mt-0.5">
|
||||
<a
|
||||
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition"
|
||||
href="/modelfiles"
|
||||
on:click={() => {
|
||||
selectedChatId = null;
|
||||
chatId.set('');
|
||||
}}
|
||||
>
|
||||
<div class="self-center">
|
||||
|
|
@ -163,16 +189,18 @@
|
|||
</div>
|
||||
|
||||
<div class="flex self-center">
|
||||
<div class=" self-center font-medium text-sm">Modelfiles</div>
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('Modelfiles')}</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="px-2.5 flex justify-center">
|
||||
<button
|
||||
class="flex-grow flex space-x-3 rounded-md px-3 py-2 hover:bg-gray-900 transition"
|
||||
on:click={async () => {
|
||||
goto('/prompts');
|
||||
<div class="px-2 flex justify-center">
|
||||
<a
|
||||
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition"
|
||||
href="/prompts"
|
||||
on:click={() => {
|
||||
selectedChatId = null;
|
||||
chatId.set('');
|
||||
}}
|
||||
>
|
||||
<div class="self-center">
|
||||
|
|
@ -193,16 +221,18 @@
|
|||
</div>
|
||||
|
||||
<div class="flex self-center">
|
||||
<div class=" self-center font-medium text-sm">Prompts</div>
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('Prompts')}</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="px-2.5 flex justify-center mb-1">
|
||||
<button
|
||||
class="flex-grow flex space-x-3 rounded-md px-3 py-2 hover:bg-gray-900 transition"
|
||||
on:click={async () => {
|
||||
goto('/documents');
|
||||
<div class="px-2 flex justify-center mb-1">
|
||||
<a
|
||||
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-900 transition"
|
||||
href="/documents"
|
||||
on:click={() => {
|
||||
selectedChatId = null;
|
||||
chatId.set('');
|
||||
}}
|
||||
>
|
||||
<div class="self-center">
|
||||
|
|
@ -223,9 +253,9 @@
|
|||
</div>
|
||||
|
||||
<div class="flex self-center">
|
||||
<div class=" self-center font-medium text-sm">Documents</div>
|
||||
<div class=" self-center font-medium text-sm">{$i18n.t('Documents')}</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
|
@ -233,11 +263,13 @@
|
|||
{#if !($settings.saveChatHistory ?? true)}
|
||||
<div class="absolute z-40 w-full h-full bg-black/90 flex justify-center">
|
||||
<div class=" text-left px-5 py-2">
|
||||
<div class=" font-medium">Chat History is off for this browser.</div>
|
||||
<div class=" font-medium">{$i18n.t('Chat History is off for this browser.')}</div>
|
||||
<div class="text-xs mt-2">
|
||||
When history is turned off, new chats on this browser won't appear in your history on
|
||||
any of your devices. <span class=" font-semibold"
|
||||
>This setting does not sync across browsers or devices.</span
|
||||
{$i18n.t(
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices."
|
||||
)}
|
||||
<span class=" font-semibold"
|
||||
>{$i18n.t('This setting does not sync across browsers or devices.')}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
|
|
@ -264,16 +296,16 @@
|
|||
/>
|
||||
</svg>
|
||||
|
||||
<div>Enable Chat History</div>
|
||||
<div>{$i18n.t('Enable Chat History')}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="px-2.5 mt-1 mb-2 flex justify-center space-x-2">
|
||||
<div class="px-2 mt-1 mb-2 flex justify-center space-x-2">
|
||||
<div class="flex w-full" id="chat-search">
|
||||
<div class="self-center pl-3 py-2 rounded-l bg-gray-950">
|
||||
<div class="self-center pl-3 py-2 rounded-l-xl bg-gray-950">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
|
|
@ -289,30 +321,13 @@
|
|||
</div>
|
||||
|
||||
<input
|
||||
class="w-full rounded-r py-1.5 pl-2.5 pr-4 text-sm text-gray-300 bg-gray-950 outline-none"
|
||||
placeholder="Search"
|
||||
class="w-full rounded-r-xl py-1.5 pl-2.5 pr-4 text-sm text-gray-300 bg-gray-950 outline-none"
|
||||
placeholder={$i18n.t('Search')}
|
||||
bind:value={search}
|
||||
on:focus={() => {
|
||||
enrichChatsWithContent($chats);
|
||||
}}
|
||||
/>
|
||||
|
||||
<!-- <div class="self-center pr-3 py-2 bg-gray-900">
|
||||
<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="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z"
|
||||
/>
|
||||
</svg>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -330,7 +345,12 @@
|
|||
<button
|
||||
class="px-2.5 text-xs font-medium bg-gray-900 hover:bg-gray-800 transition rounded-full"
|
||||
on:click={async () => {
|
||||
await chats.set(await getChatListByTagName(localStorage.token, tag.name));
|
||||
let chatIds = await getChatListByTagName(localStorage.token, tag.name);
|
||||
if (chatIds.length === 0) {
|
||||
await tags.set(await getAllChatTags(localStorage.token));
|
||||
chatIds = await getChatList(localStorage.token);
|
||||
}
|
||||
await chats.set(chatIds);
|
||||
}}
|
||||
>
|
||||
{tag.name}
|
||||
|
|
@ -339,7 +359,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="pl-2.5 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">
|
||||
{#each $chats.filter((chat) => {
|
||||
if (search === '') {
|
||||
return true;
|
||||
|
|
@ -359,207 +379,186 @@
|
|||
return title.includes(query) || contentMatches;
|
||||
}
|
||||
}) as chat, i}
|
||||
<div class=" w-full pr-2 relative">
|
||||
<button
|
||||
class=" w-full flex justify-between rounded-md px-3 py-2 hover:bg-gray-900 {chat.id ===
|
||||
$chatId
|
||||
? 'bg-gray-900'
|
||||
: ''} transition whitespace-nowrap text-ellipsis"
|
||||
on:click={() => {
|
||||
// goto(`/c/${chat.id}`);
|
||||
if (chat.id !== chatTitleEditId) {
|
||||
chatTitleEditId = null;
|
||||
chatTitle = '';
|
||||
}
|
||||
|
||||
if (chat.id !== $chatId) {
|
||||
loadChat(chat.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class=" flex self-center flex-1">
|
||||
<div class=" self-center mr-3">
|
||||
<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="M2.25 12.76c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.076-4.076a1.526 1.526 0 011.037-.443 48.282 48.282 0 005.68-.494c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class=" text-left self-center overflow-hidden {chat.id === $chatId
|
||||
? 'w-[120px]'
|
||||
: 'w-[180px]'} "
|
||||
>
|
||||
{#if chatTitleEditId === chat.id}
|
||||
<input bind:value={chatTitle} class=" bg-transparent w-full" />
|
||||
{:else}
|
||||
<div class=" w-full pr-2 relative group">
|
||||
{#if chatTitleEditId === chat.id}
|
||||
<div
|
||||
class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId ||
|
||||
chat.id === chatTitleEditId ||
|
||||
chat.id === chatDeleteId
|
||||
? 'bg-gray-900'
|
||||
: chat.id === selectedChatId
|
||||
? 'bg-gray-950'
|
||||
: 'group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
<input bind:value={chatTitle} class=" bg-transparent w-full outline-none mr-10" />
|
||||
</div>
|
||||
{:else}
|
||||
<a
|
||||
class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId ||
|
||||
chat.id === chatTitleEditId ||
|
||||
chat.id === chatDeleteId
|
||||
? 'bg-gray-900'
|
||||
: chat.id === selectedChatId
|
||||
? 'bg-gray-950'
|
||||
: 'group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
|
||||
href="/c/{chat.id}"
|
||||
on:click={() => {
|
||||
selectedChatId = chat.id;
|
||||
if (window.innerWidth < 1024) {
|
||||
show = false;
|
||||
}
|
||||
}}
|
||||
draggable="false"
|
||||
>
|
||||
<div class=" flex self-center flex-1 w-full">
|
||||
<div class=" text-left self-center overflow-hidden w-full h-[20px]">
|
||||
{chat.title}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{#if chat.id === $chatId}
|
||||
<div class=" absolute right-[22px] top-[10px]">
|
||||
{#if chatTitleEditId === chat.id}
|
||||
<div class="flex self-center space-x-1.5">
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
editChatTitle(chat.id, chatTitle);
|
||||
chatTitleEditId = null;
|
||||
chatTitle = '';
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
chatTitleEditId = null;
|
||||
chatTitle = '';
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<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>
|
||||
{:else if chatDeleteId === chat.id}
|
||||
<div class="flex self-center space-x-1.5">
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
deleteChat(chat.id);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
chatDeleteId = null;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<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>
|
||||
{:else}
|
||||
<div class="flex self-center space-x-1.5">
|
||||
<button
|
||||
id="delete-chat-button"
|
||||
class=" hidden"
|
||||
on:click={() => {
|
||||
deleteChat(chat.id);
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
chatTitle = chat.title;
|
||||
chatTitleEditId = chat.id;
|
||||
// editChatTitle(chat.id, 'a');
|
||||
}}
|
||||
>
|
||||
<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.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
chatDeleteId = chat.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 9l-.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 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="
|
||||
|
||||
{chat.id === $chatId || chat.id === chatTitleEditId || chat.id === chatDeleteId
|
||||
? ' from-gray-900'
|
||||
: chat.id === selectedChatId
|
||||
? 'from-gray-950'
|
||||
: 'invisible group-hover:visible from-gray-950'}
|
||||
absolute right-[10px] top-[10px] pr-2 pl-5 bg-gradient-to-l from-80%
|
||||
|
||||
to-transparent"
|
||||
>
|
||||
{#if chatTitleEditId === chat.id}
|
||||
<div class="flex self-center space-x-1.5 z-10">
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
editChatTitle(chat.id, chatTitle);
|
||||
chatTitleEditId = null;
|
||||
chatTitle = '';
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
chatTitleEditId = null;
|
||||
chatTitle = '';
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<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>
|
||||
{:else if chatDeleteId === chat.id}
|
||||
<div class="flex self-center space-x-1.5 z-10">
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
deleteChat(chat.id);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
chatDeleteId = null;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<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>
|
||||
{:else}
|
||||
<div class="flex self-center space-x-1.5 z-10">
|
||||
<ChatMenu
|
||||
renameHandler={() => {
|
||||
chatTitle = chat.title;
|
||||
chatTitleEditId = chat.id;
|
||||
}}
|
||||
deleteHandler={() => {
|
||||
chatDeleteId = chat.id;
|
||||
}}
|
||||
onClose={() => {
|
||||
selectedChatId = null;
|
||||
}}
|
||||
>
|
||||
<button
|
||||
aria-label="Chat Menu"
|
||||
class=" self-center hover:text-white transition"
|
||||
on:click={() => {
|
||||
selectedChatId = 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>
|
||||
</ChatMenu>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-2.5">
|
||||
<hr class=" border-gray-900 mb-1 w-full" />
|
||||
<!-- <hr class=" border-gray-900 mb-1 w-full" /> -->
|
||||
|
||||
<div class="flex flex-col">
|
||||
{#if $user !== undefined}
|
||||
<button
|
||||
class=" flex rounded-md py-3 px-3.5 w-full hover:bg-gray-900 transition"
|
||||
class=" flex rounded-xl py-3 px-3.5 w-full hover:bg-gray-900 transition"
|
||||
on:click={() => {
|
||||
showDropdown = !showDropdown;
|
||||
}}
|
||||
|
|
@ -577,7 +576,8 @@
|
|||
{#if showDropdown}
|
||||
<div
|
||||
id="dropdownDots"
|
||||
class="absolute z-40 bottom-[70px] 4.5rem rounded-lg shadow w-[240px] bg-gray-900"
|
||||
class="absolute z-40 bottom-[70px] 4.5rem rounded-xl shadow w-[240px] bg-gray-900"
|
||||
in:slide={{ duration: 150 }}
|
||||
>
|
||||
<div class="py-2 w-full">
|
||||
{#if $user.role === 'admin'}
|
||||
|
|
@ -604,7 +604,33 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium">Admin Panel</div>
|
||||
<div class=" self-center font-medium">{$i18n.t('Admin Panel')}</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="flex py-2.5 px-3.5 w-full hover:bg-gray-800 transition"
|
||||
on:click={() => {
|
||||
goto('/playground');
|
||||
showDropdown = false;
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium">{$i18n.t('Playground')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
|
|
@ -636,7 +662,7 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium">Settings</div>
|
||||
<div class=" self-center font-medium">{$i18n.t('Settings')}</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -670,41 +696,11 @@
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium">Sign Out</div>
|
||||
<div class=" self-center font-medium">{$i18n.t('Sign Out')}</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<button
|
||||
class=" flex rounded-md py-3 px-3.5 w-full hover:bg-gray-900 transition"
|
||||
on:click={async () => {
|
||||
await showSettings.set(true);
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-3">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 011.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 01-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.397.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 01-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.107-1.204l-.527-.738a1.125 1.125 0 01.12-1.45l.773-.773a1.125 1.125 0 011.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894z"
|
||||
/>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class=" self-center font-medium">Settings</div>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -713,30 +709,36 @@
|
|||
<div
|
||||
class="fixed left-0 top-[50dvh] z-40 -translate-y-1/2 transition-transform translate-x-[255px] md:translate-x-[260px] rotate-0"
|
||||
>
|
||||
<button
|
||||
id="sidebar-toggle-button"
|
||||
class=" group"
|
||||
on:click={() => {
|
||||
show = !show;
|
||||
}}
|
||||
><span class="" data-state="closed"
|
||||
><div
|
||||
class="flex h-[72px] w-8 items-center justify-center opacity-20 group-hover:opacity-100 transition"
|
||||
>
|
||||
<div class="flex h-6 w-6 flex-col items-center">
|
||||
<div
|
||||
class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[0.15rem] {show
|
||||
? 'group-hover:rotate-[15deg]'
|
||||
: 'group-hover:rotate-[-15deg]'}"
|
||||
/>
|
||||
<div
|
||||
class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[-0.15rem] {show
|
||||
? 'group-hover:rotate-[-15deg]'
|
||||
: 'group-hover:rotate-[15deg]'}"
|
||||
/>
|
||||
<Tooltip
|
||||
placement="right"
|
||||
content={`${show ? $i18n.t('Close') : $i18n.t('Open')} ${$i18n.t('sidebar')}`}
|
||||
touch={false}
|
||||
>
|
||||
<button
|
||||
id="sidebar-toggle-button"
|
||||
class=" group"
|
||||
on:click={() => {
|
||||
show = !show;
|
||||
}}
|
||||
><span class="" data-state="closed"
|
||||
><div
|
||||
class="flex h-[72px] w-8 items-center justify-center opacity-20 group-hover:opacity-100 transition"
|
||||
>
|
||||
<div class="flex h-6 w-6 flex-col items-center">
|
||||
<div
|
||||
class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[0.15rem] {show
|
||||
? 'group-hover:rotate-[15deg]'
|
||||
: 'group-hover:rotate-[-15deg]'}"
|
||||
/>
|
||||
<div
|
||||
class="h-3 w-1 rounded-full bg-[#0f0f0f] dark:bg-white rotate-0 translate-y-[-0.15rem] {show
|
||||
? 'group-hover:rotate-[-15deg]'
|
||||
: 'group-hover:rotate-[15deg]'}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
54
src/lib/components/layout/Sidebar/ChatMenu.svelte
Normal file
54
src/lib/components/layout/Sidebar/ChatMenu.svelte
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<script lang="ts">
|
||||
import { DropdownMenu } from 'bits-ui';
|
||||
|
||||
import Dropdown from '$lib/components/common/Dropdown.svelte';
|
||||
import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
|
||||
import Pencil from '$lib/components/icons/Pencil.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
|
||||
export let renameHandler: Function;
|
||||
export let deleteHandler: Function;
|
||||
|
||||
export let onClose: Function;
|
||||
</script>
|
||||
|
||||
<Dropdown
|
||||
on:change={(e) => {
|
||||
if (e.detail === false) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Tooltip content="More">
|
||||
<slot />
|
||||
</Tooltip>
|
||||
|
||||
<div slot="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full max-w-[130px] rounded-lg px-1 py-1.5 border border-gray-700/50 z-50 bg-gray-850 text-white"
|
||||
sideOffset={-2}
|
||||
side="bottom"
|
||||
align="start"
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
|
||||
on:click={() => {
|
||||
renameHandler();
|
||||
}}
|
||||
>
|
||||
<Pencil strokeWidth="2" />
|
||||
<div class="flex items-center">Rename</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="flex gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer"
|
||||
on:click={() => {
|
||||
deleteHandler();
|
||||
}}
|
||||
>
|
||||
<GarbageBin strokeWidth="2" />
|
||||
<div class="flex items-center">Delete</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</div>
|
||||
</Dropdown>
|
||||
111
src/lib/components/playground/ChatCompletion.svelte
Normal file
111
src/lib/components/playground/ChatCompletion.svelte
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<script lang="ts">
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let messages = [];
|
||||
let textAreaElement: HTMLTextAreaElement;
|
||||
onMount(() => {
|
||||
messages.forEach((message, idx) => {
|
||||
textAreaElement.style.height = '';
|
||||
textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="py-3 space-y-3">
|
||||
{#each messages as message, idx}
|
||||
<div class="flex gap-2 group">
|
||||
<div class="flex items-start pt-1">
|
||||
<button
|
||||
class="px-2 py-1 text-sm font-semibold uppercase min-w-[6rem] text-left dark:group-hover:bg-gray-800 rounded-lg transition"
|
||||
on:click={() => {
|
||||
message.role = message.role === 'user' ? 'assistant' : 'user';
|
||||
}}>{$i18n.t(message.role)}</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<!-- $i18n.t('a user') -->
|
||||
<!-- $i18n.t('an assistant') -->
|
||||
<textarea
|
||||
id="{message.role}-{idx}-textarea"
|
||||
bind:this={textAreaElement}
|
||||
class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden"
|
||||
placeholder={$i18n.t(`Enter {{role}} message here`, {
|
||||
role: message.role === 'user' ? $i18n.t('a user') : $i18n.t('an assistant')
|
||||
})}
|
||||
rows="1"
|
||||
on:input={(e) => {
|
||||
textAreaElement.style.height = '';
|
||||
textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
|
||||
}}
|
||||
on:focus={(e) => {
|
||||
textAreaElement.style.height = '';
|
||||
textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
|
||||
|
||||
// e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
|
||||
}}
|
||||
bind:value={message.content}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class=" pt-1">
|
||||
<button
|
||||
class=" group-hover:text-gray-500 dark:text-gray-900 dark:hover:text-gray-300 transition"
|
||||
on:click={() => {
|
||||
messages = messages.filter((message, messageIdx) => messageIdx !== idx);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class=" dark:border-gray-800" />
|
||||
{/each}
|
||||
|
||||
<button
|
||||
class="flex items-center gap-2 px-2 py-1"
|
||||
on:click={() => {
|
||||
console.log(messages.at(-1));
|
||||
messages.push({
|
||||
role: (messages.at(-1)?.role ?? 'assistant') === 'user' ? 'assistant' : 'user',
|
||||
content: ''
|
||||
});
|
||||
messages = messages;
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class=" text-sm font-medium">{$i18n.t('Add message')}</div>
|
||||
</button>
|
||||
</div>
|
||||
0
src/lib/components/playground/TextCompletion.svelte
Normal file
0
src/lib/components/playground/TextCompletion.svelte
Normal file
|
|
@ -1,16 +1,19 @@
|
|||
import { dev } from '$app/environment';
|
||||
// import { version } from '../../package.json';
|
||||
|
||||
export const WEBUI_NAME = 'Open WebUI';
|
||||
export const APP_NAME = 'Open WebUI';
|
||||
export const WEBUI_BASE_URL = dev ? `http://${location.hostname}:8080` : ``;
|
||||
|
||||
export const WEBUI_API_BASE_URL = `${WEBUI_BASE_URL}/api/v1`;
|
||||
export const OLLAMA_API_BASE_URL = `${WEBUI_BASE_URL}/ollama/api`;
|
||||
|
||||
export const LITELLM_API_BASE_URL = `${WEBUI_BASE_URL}/litellm/api`;
|
||||
export const OLLAMA_API_BASE_URL = `${WEBUI_BASE_URL}/ollama`;
|
||||
export const OPENAI_API_BASE_URL = `${WEBUI_BASE_URL}/openai/api`;
|
||||
export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`;
|
||||
export const AUDIO_API_BASE_URL = `${WEBUI_BASE_URL}/audio/api/v1`;
|
||||
export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`;
|
||||
export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`;
|
||||
|
||||
export const WEB_UI_VERSION = 'v1.0.0-alpha-static';
|
||||
|
||||
export const WEBUI_VERSION = APP_VERSION;
|
||||
export const REQUIRED_OLLAMA_VERSION = '0.1.16';
|
||||
|
||||
export const SUPPORTED_FILE_TYPE = [
|
||||
|
|
@ -87,8 +90,3 @@ export const SUPPORTED_FILE_EXTENSIONS = [
|
|||
// This feature, akin to $env/static/private, exclusively incorporates environment variables
|
||||
// that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
|
||||
// Consequently, these variables can be securely exposed to client-side code.
|
||||
|
||||
// Example of the .env configuration:
|
||||
// OLLAMA_API_BASE_URL="http://localhost:11434/api"
|
||||
// # Public
|
||||
// PUBLIC_API_BASE_URL=$OLLAMA_API_BASE_URL
|
||||
|
|
|
|||
73
src/lib/i18n/index.ts
Normal file
73
src/lib/i18n/index.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import i18next from 'i18next';
|
||||
import resourcesToBackend from 'i18next-resources-to-backend';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import type { i18n as i18nType } from 'i18next';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
const createI18nStore = (i18n: i18nType) => {
|
||||
const i18nWritable = writable(i18n);
|
||||
|
||||
i18n.on('initialized', () => {
|
||||
i18nWritable.set(i18n);
|
||||
});
|
||||
i18n.on('loaded', () => {
|
||||
i18nWritable.set(i18n);
|
||||
});
|
||||
i18n.on('added', () => i18nWritable.set(i18n));
|
||||
i18n.on('languageChanged', () => {
|
||||
i18nWritable.set(i18n);
|
||||
});
|
||||
return i18nWritable;
|
||||
};
|
||||
|
||||
const createIsLoadingStore = (i18n: i18nType) => {
|
||||
const isLoading = writable(false);
|
||||
|
||||
// if loaded resources are empty || {}, set loading to true
|
||||
i18n.on('loaded', (resources) => {
|
||||
// console.log('loaded:', resources);
|
||||
Object.keys(resources).length !== 0 && isLoading.set(false);
|
||||
});
|
||||
|
||||
// if resources failed loading, set loading to true
|
||||
i18n.on('failedLoading', () => {
|
||||
isLoading.set(true);
|
||||
});
|
||||
|
||||
return isLoading;
|
||||
};
|
||||
|
||||
i18next
|
||||
.use(
|
||||
resourcesToBackend(
|
||||
(language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`)
|
||||
)
|
||||
)
|
||||
.use(LanguageDetector)
|
||||
.init({
|
||||
debug: false,
|
||||
detection: {
|
||||
order: ['querystring', 'localStorage', 'navigator'],
|
||||
caches: ['localStorage'],
|
||||
lookupQuerystring: 'lang',
|
||||
lookupLocalStorage: 'locale'
|
||||
},
|
||||
fallbackLng: {
|
||||
default: ['en-US']
|
||||
},
|
||||
ns: 'translation',
|
||||
returnEmptyString: false,
|
||||
interpolation: {
|
||||
escapeValue: false // not needed for svelte as it escapes by default
|
||||
}
|
||||
});
|
||||
|
||||
const i18n = createI18nStore(i18next);
|
||||
const isLoadingStore = createIsLoadingStore(i18next);
|
||||
|
||||
export const getLanguages = async () => {
|
||||
const languages = (await import(`./locales/languages.json`)).default;
|
||||
return languages;
|
||||
};
|
||||
export default i18n;
|
||||
export const isLoading = isLoadingStore;
|
||||
363
src/lib/i18n/locales/ca-ES/translation.json
Normal file
363
src/lib/i18n/locales/ca-ES/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' per no caduca mai.",
|
||||
"(Beta)": "(Beta)",
|
||||
"(e.g. `sh webui.sh --api`)": "(p. ex. `sh webui.sh --api`)",
|
||||
"(latest)": "(últim)",
|
||||
"{{modelName}} is thinking...": "{{modelName}} està pensant...",
|
||||
"{{webUIName}} Backend Required": "Es requereix Backend de {{webUIName}}",
|
||||
"a user": "un usuari",
|
||||
"About": "Sobre",
|
||||
"Account": "Compte",
|
||||
"Action": "Acció",
|
||||
"Add a model": "Afegeix un model",
|
||||
"Add a model tag name": "Afegeix un nom d'etiqueta de model",
|
||||
"Add a short description about what this modelfile does": "Afegeix una descripció curta del que fa aquest arxiu de model",
|
||||
"Add a short title for this prompt": "Afegeix un títol curt per aquest prompt",
|
||||
"Add a tag": "Afegeix una etiqueta",
|
||||
"Add Docs": "Afegeix Documents",
|
||||
"Add Files": "Afegeix Arxius",
|
||||
"Add message": "Afegeix missatge",
|
||||
"add tags": "afegeix etiquetes",
|
||||
"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ó",
|
||||
"Admin Settings": "Configuració d'Administració",
|
||||
"Advanced Parameters": "Paràmetres Avançats",
|
||||
"all": "tots",
|
||||
"All Users": "Tots els Usuaris",
|
||||
"Allow": "Permet",
|
||||
"Allow Chat Deletion": "Permet la Supressió del Xat",
|
||||
"alphanumeric characters and hyphens": "caràcters alfanumèrics i guions",
|
||||
"Already have an account?": "Ja tens un compte?",
|
||||
"an assistant": "un assistent",
|
||||
"and": "i",
|
||||
"API Base URL": "URL Base de l'API",
|
||||
"API Key": "Clau de l'API",
|
||||
"API RPM": "RPM de l'API",
|
||||
"are allowed - Activate this command by typing": "estan permesos - Activa aquesta comanda escrivint",
|
||||
"Are you sure?": "Estàs segur?",
|
||||
"Audio": "Àudio",
|
||||
"Auto-playback response": "Resposta de reproducció automàtica",
|
||||
"Auto-send input after 3 sec.": "Enviar entrada automàticament després de 3 segons",
|
||||
"AUTOMATIC1111 Base URL": "URL Base AUTOMATIC1111",
|
||||
"AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base AUTOMATIC1111.",
|
||||
"available!": "disponible!",
|
||||
"Back": "Enrere",
|
||||
"Builder Mode": "Mode Constructor",
|
||||
"Cancel": "Cancel·la",
|
||||
"Categories": "Categories",
|
||||
"Change Password": "Canvia la Contrasenya",
|
||||
"Chat": "Xat",
|
||||
"Chat History": "Històric del Xat",
|
||||
"Chat History is off for this browser.": "L'historial de xat està desactivat per a aquest navegador.",
|
||||
"Chats": "Xats",
|
||||
"Check Again": "Comprova-ho de Nou",
|
||||
"Check for updates": "Comprova si hi ha actualitzacions",
|
||||
"Checking for updates...": "Comprovant actualitzacions...",
|
||||
"Choose a model before saving...": "Tria un model abans de guardar...",
|
||||
"Chunk Overlap": "Solapament de Blocs",
|
||||
"Chunk Params": "Paràmetres de Blocs",
|
||||
"Chunk Size": "Mida del Bloc",
|
||||
"Click here for help.": "Fes clic aquí per ajuda.",
|
||||
"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 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.",
|
||||
"Close": "Tanca",
|
||||
"Collection": "Col·lecció",
|
||||
"Command": "Comanda",
|
||||
"Confirm Password": "Confirma la Contrasenya",
|
||||
"Connections": "Connexions",
|
||||
"Content": "Contingut",
|
||||
"Context Length": "Longitud del Context",
|
||||
"Conversation Mode": "Mode de Conversa",
|
||||
"Copy last code block": "Copia l'últim bloc de codi",
|
||||
"Copy last response": "Copia l'última resposta",
|
||||
"Copying to clipboard was successful!": "La còpia al porta-retalls ha estat exitosa!",
|
||||
"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':": "Crea una frase concisa de 3-5 paraules com a capçalera per a la següent consulta, seguint estrictament el límit de 3-5 paraules i evitant l'ús de la paraula 'títol':",
|
||||
"Create a modelfile": "Crea un fitxer de model",
|
||||
"Create Account": "Crea un Compte",
|
||||
"Created at": "Creat el",
|
||||
"Created by": "Creat per",
|
||||
"Current Model": "Model Actual",
|
||||
"Current Password": "Contrasenya Actual",
|
||||
"Custom": "Personalitzat",
|
||||
"Customize Ollama models for a specific purpose": "Personalitza els models Ollama per a un propòsit específic",
|
||||
"Dark": "Fosc",
|
||||
"Database": "Base de Dades",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "Per defecte",
|
||||
"Default (Automatic1111)": "Per defecte (Automatic1111)",
|
||||
"Default (Web API)": "Per defecte (Web API)",
|
||||
"Default model updated": "Model per defecte actualitzat",
|
||||
"Default Prompt Suggestions": "Suggeriments de Prompt Per Defecte",
|
||||
"Default User Role": "Rol d'Usuari Per Defecte",
|
||||
"delete": "esborra",
|
||||
"Delete a model": "Esborra un model",
|
||||
"Delete chat": "Esborra xat",
|
||||
"Delete Chats": "Esborra Xats",
|
||||
"Deleted {{deleteModelTag}}": "Esborrat {{deleteModelTag}}",
|
||||
"Deleted {tagName}": "Esborrat {tagName}",
|
||||
"Description": "Descripció",
|
||||
"Desktop Notifications": "Notificacions d'Escriptori",
|
||||
"Disabled": "Desactivat",
|
||||
"Discover a modelfile": "Descobreix un fitxer de model",
|
||||
"Discover a prompt": "Descobreix un prompt",
|
||||
"Discover, download, and explore custom prompts": "Descobreix, descarrega i explora prompts personalitzats",
|
||||
"Discover, download, and explore model presets": "Descobreix, descarrega i explora presets de models",
|
||||
"Display the username instead of You in the Chat": "Mostra el nom d'usuari en lloc de 'Tu' al Xat",
|
||||
"Document": "Document",
|
||||
"Document Settings": "Configuració de Documents",
|
||||
"Documents": "Documents",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "no realitza connexions externes, i les teves dades romanen segures al teu servidor allotjat localment.",
|
||||
"Don't Allow": "No Permetre",
|
||||
"Don't have an account?": "No tens un compte?",
|
||||
"Download as a File": "Descarrega com a Arxiu",
|
||||
"Download Database": "Descarrega Base de Dades",
|
||||
"Drop any files here to add to the conversation": "Deixa qualsevol arxiu aquí per afegir-lo a la conversa",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.",
|
||||
"Edit Doc": "Edita Document",
|
||||
"Edit User": "Edita Usuari",
|
||||
"Email": "Correu electrònic",
|
||||
"Enable Chat History": "Activa Historial de Xat",
|
||||
"Enable New Sign Ups": "Permet Noves Inscripcions",
|
||||
"Enabled": "Activat",
|
||||
"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
|
||||
"Enter API Key": "Introdueix la Clau API",
|
||||
"Enter Chunk Overlap": "Introdueix el Solapament de Blocs",
|
||||
"Enter Chunk Size": "Introdueix la Mida del Bloc",
|
||||
"Enter Image Size (e.g. 512x512)": "Introdueix la Mida de la Imatge (p. ex. 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Introdueix l'URL Base de LiteLLM API (litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "Introdueix la Clau de LiteLLM API (litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Introdueix RPM de LiteLLM API (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Introdueix el Model de LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Introdueix el Màxim de Tokens (litellm_params.max_tokens)",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Introdueix el Nombre de Passos (p. ex. 50)",
|
||||
"Enter stop sequence": "Introdueix la seqüència de parada",
|
||||
"Enter Top K": "Introdueix Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)",
|
||||
"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",
|
||||
"Experimental": "Experimental",
|
||||
"Export All Chats (All Users)": "Exporta Tots els Xats (Tots els Usuaris)",
|
||||
"Export Chats": "Exporta Xats",
|
||||
"Export Documents Mapping": "Exporta el Mapatge de Documents",
|
||||
"Export Modelfiles": "Exporta Fitxers de Model",
|
||||
"Export Prompts": "Exporta Prompts",
|
||||
"Failed to read clipboard contents": "No s'ha pogut llegir el contingut del porta-retalls",
|
||||
"File Mode": "Mode Arxiu",
|
||||
"File not found.": "Arxiu no trobat.",
|
||||
"Focus chat input": "Enfoca l'entrada del xat",
|
||||
"Format your variables using square brackets like this:": "Formata les teves variables utilitzant claudàtors així:",
|
||||
"From (Base Model)": "Des de (Model Base)",
|
||||
"Full Screen Mode": "Mode de Pantalla Completa",
|
||||
"General": "General",
|
||||
"General Settings": "Configuració General",
|
||||
"Hello, {{name}}": "Hola, {{name}}",
|
||||
"Hide": "Amaga",
|
||||
"Hide Additional Params": "Amaga Paràmetres Addicionals",
|
||||
"How can I help you today?": "Com et puc ajudar avui?",
|
||||
"Image Generation (Experimental)": "Generació d'Imatges (Experimental)",
|
||||
"Image Generation Engine": "Motor de Generació d'Imatges",
|
||||
"Image Settings": "Configuració d'Imatges",
|
||||
"Images": "Imatges",
|
||||
"Import Chats": "Importa Xats",
|
||||
"Import Documents Mapping": "Importa el Mapa de Documents",
|
||||
"Import Modelfiles": "Importa Fitxers de Model",
|
||||
"Import Prompts": "Importa Prompts",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Inclou la bandera `--api` quan executis stable-diffusion-webui",
|
||||
"Interface": "Interfície",
|
||||
"join our Discord for help.": "uneix-te al nostre Discord per ajuda.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "Expiració de JWT",
|
||||
"JWT Token": "Token JWT",
|
||||
"Keep Alive": "Mantén Actiu",
|
||||
"Keyboard shortcuts": "Dreceres de Teclat",
|
||||
"Language": "Idioma",
|
||||
"Light": "Clar",
|
||||
"Listening...": "Escoltant...",
|
||||
"LLMs can make mistakes. Verify important information.": "Els LLMs poden cometre errors. Verifica la informació important.",
|
||||
"Made by OpenWebUI Community": "Creat per la Comunitat OpenWebUI",
|
||||
"Make sure to enclose them with": "Assegura't d'envoltar-los amb",
|
||||
"Manage LiteLLM Models": "Gestiona Models LiteLLM",
|
||||
"Manage Models": "Gestiona Models",
|
||||
"Manage Ollama Models": "Gestiona Models Ollama",
|
||||
"Max Tokens": "Màxim de Tokens",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es poden descarregar un màxim de 3 models simultàniament. Si us plau, prova-ho més tard.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Eta de Mirostat",
|
||||
"Mirostat Tau": "Tau de Mirostat",
|
||||
"MMMM DD, YYYY": "DD de MMMM, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "El model '{{modelName}}' s'ha descarregat amb èxit.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
|
||||
"Model {{modelId}} not found": "Model {{modelId}} no trobat",
|
||||
"Model {{modelName}} already exists.": "El model {{modelName}} ja existeix.",
|
||||
"Model Name": "Nom del Model",
|
||||
"Model not selected": "Model no seleccionat",
|
||||
"Model Tag Name": "Nom de l'Etiqueta del Model",
|
||||
"Model Whitelisting": "Llista Blanca de Models",
|
||||
"Model(s) Whitelisted": "Model(s) a la Llista Blanca",
|
||||
"Modelfile": "Fitxer de Model",
|
||||
"Modelfile Advanced Settings": "Configuració Avançada de Fitxers de Model",
|
||||
"Modelfile Content": "Contingut del Fitxer de Model",
|
||||
"Modelfiles": "Fitxers de Model",
|
||||
"Models": "Models",
|
||||
"My Documents": "Els Meus Documents",
|
||||
"My Modelfiles": "Els Meus Fitxers de Model",
|
||||
"My Prompts": "Els Meus Prompts",
|
||||
"Name": "Nom",
|
||||
"Name Tag": "Etiqueta de Nom",
|
||||
"Name your modelfile": "Nomena el teu fitxer de model",
|
||||
"New Chat": "Xat Nou",
|
||||
"New Password": "Nova Contrasenya",
|
||||
"Not sure what to add?": "No estàs segur del que afegir?",
|
||||
"Not sure what to write? Switch to": "No estàs segur del que escriure? Canvia a",
|
||||
"Off": "Desactivat",
|
||||
"Okay, Let's Go!": "D'acord, Anem!",
|
||||
"Ollama Base URL": "URL Base d'Ollama",
|
||||
"Ollama Version": "Versió d'Ollama",
|
||||
"On": "Activat",
|
||||
"Only": "Només",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "Només es permeten caràcters alfanumèrics i guions en la cadena de comandes.",
|
||||
"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.": "Ui! Aguanta! Els teus fitxers encara estan en el forn de processament. Els estem cuinant a la perfecció. Si us plau, tingues paciència i t'avisarem quan estiguin llestos.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ui! Sembla que l'URL és invàlida. Si us plau, revisa-ho i prova de nou.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ui! Estàs utilitzant un mètode no suportat (només frontend). Si us plau, serveix la WebUI des del backend.",
|
||||
"Open": "Obre",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "Obre un nou xat",
|
||||
"OpenAI API": "API d'OpenAI",
|
||||
"OpenAI API Key": "Clau API d'OpenAI",
|
||||
"OpenAI API Key is required.": "Es requereix la Clau API d'OpenAI.",
|
||||
"or": "o",
|
||||
"Parameters": "Paràmetres",
|
||||
"Password": "Contrasenya",
|
||||
"PDF Extract Images (OCR)": "Extreu Imatges de PDF (OCR)",
|
||||
"pending": "pendent",
|
||||
"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
|
||||
"Playground": "Zona de Jocs",
|
||||
"Profile": "Perfil",
|
||||
"Prompt Content": "Contingut del Prompt",
|
||||
"Prompt suggestions": "Suggeriments de Prompt",
|
||||
"Prompts": "Prompts",
|
||||
"Pull a model from Ollama.com": "Treu un model d'Ollama.com",
|
||||
"Pull Progress": "Progrés de Tracció",
|
||||
"Query Params": "Paràmetres de Consulta",
|
||||
"RAG Template": "Plantilla RAG",
|
||||
"Raw Format": "Format Brut",
|
||||
"Record voice": "Enregistra veu",
|
||||
"Redirecting you to OpenWebUI Community": "Redirigint-te a la Comunitat OpenWebUI",
|
||||
"Release Notes": "Notes de la Versió",
|
||||
"Repeat Last N": "Repeteix Últim N",
|
||||
"Repeat Penalty": "Penalització de Repetició",
|
||||
"Request Mode": "Mode de Sol·licitud",
|
||||
"Reset Vector Storage": "Reinicia l'Emmagatzematge de Vectors",
|
||||
"Response AutoCopy to Clipboard": "Resposta AutoCopiar al Portapapers",
|
||||
"Role": "Rol",
|
||||
"Rosé Pine": "Rosé Pine",
|
||||
"Rosé Pine Dawn": "Albada Rosé Pine",
|
||||
"Save": "Guarda",
|
||||
"Save & Create": "Guarda i Crea",
|
||||
"Save & Submit": "Guarda i Envia",
|
||||
"Save & Update": "Guarda i Actualitza",
|
||||
"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": "Guardar registres de xat directament a l'emmagatzematge del teu navegador ja no és suportat. Si us plau, pren un moment per descarregar i eliminar els teus registres de xat fent clic al botó de sota. No et preocupis, pots reimportar fàcilment els teus registres de xat al backend a través de",
|
||||
"Scan": "Escaneja",
|
||||
"Scan complete!": "Escaneig completat!",
|
||||
"Scan for documents from {{path}}": "Escaneja documents des de {{path}}",
|
||||
"Search": "Cerca",
|
||||
"Search Documents": "Cerca Documents",
|
||||
"Search Prompts": "Cerca Prompts",
|
||||
"See readme.md for instructions": "Consulta el readme.md per a instruccions",
|
||||
"See what's new": "Veure novetats",
|
||||
"Seed": "Llavor",
|
||||
"Select a mode": "Selecciona un mode",
|
||||
"Select a model": "Selecciona un model",
|
||||
"Select an Ollama instance": "Selecciona una instància d'Ollama",
|
||||
"Send a Message": "Envia un Missatge",
|
||||
"Send message": "Envia missatge",
|
||||
"Server connection verified": "Connexió al servidor verificada",
|
||||
"Set as default": "Estableix com a predeterminat",
|
||||
"Set Default Model": "Estableix Model Predeterminat",
|
||||
"Set Image Size": "Estableix Mida de la Imatge",
|
||||
"Set Steps": "Estableix Passos",
|
||||
"Set Title Auto-Generation Model": "Estableix Model d'Auto-Generació de Títol",
|
||||
"Set Voice": "Estableix Veu",
|
||||
"Settings": "Configuracions",
|
||||
"Settings saved successfully!": "Configuracions guardades amb èxit!",
|
||||
"Share to OpenWebUI Community": "Comparteix amb la Comunitat OpenWebUI",
|
||||
"short-summary": "resum curt",
|
||||
"Show": "Mostra",
|
||||
"Show Additional Params": "Mostra Paràmetres Addicionals",
|
||||
"Show shortcuts": "Mostra dreceres",
|
||||
"sidebar": "barra lateral",
|
||||
"Sign in": "Inicia sessió",
|
||||
"Sign Out": "Tanca sessió",
|
||||
"Sign up": "Registra't",
|
||||
"Speech recognition error: {{error}}": "Error de reconeixement de veu: {{error}}",
|
||||
"Speech-to-Text Engine": "Motor de Veu a Text",
|
||||
"SpeechRecognition API is not supported in this browser.": "L'API de Reconèixer Veu no és compatible amb aquest navegador.",
|
||||
"Stop Sequence": "Atura Seqüència",
|
||||
"STT Settings": "Configuracions STT",
|
||||
"Submit": "Envia",
|
||||
"Success": "Èxit",
|
||||
"Successfully updated.": "Actualitzat amb èxit.",
|
||||
"Sync All": "Sincronitza Tot",
|
||||
"System": "Sistema",
|
||||
"System Prompt": "Prompt del Sistema",
|
||||
"Tags": "Etiquetes",
|
||||
"Temperature": "Temperatura",
|
||||
"Template": "Plantilla",
|
||||
"Text Completion": "Completació de Text",
|
||||
"Text-to-Speech Engine": "Motor de Text a Veu",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Tema",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Això assegura que les teves converses valuoses queden segurament guardades a la teva base de dades backend. Gràcies!",
|
||||
"This setting does not sync across browsers or devices.": "Aquesta configuració no es sincronitza entre navegadors ni dispositius.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza diversos espais de variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
|
||||
"Title": "Títol",
|
||||
"Title Auto-Generation": "Auto-Generació de Títol",
|
||||
"Title Generation Prompt": "Prompt de Generació de Títol",
|
||||
"to": "a",
|
||||
"To access the available model names for downloading,": "Per accedir als noms dels models disponibles per descarregar,",
|
||||
"To access the GGUF models available for downloading,": "Per accedir als models GGUF disponibles per descarregar,",
|
||||
"to chat input.": "a l'entrada del xat.",
|
||||
"Toggle settings": "Commuta configuracions",
|
||||
"Toggle sidebar": "Commuta barra lateral",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "Problemes accedint a Ollama?",
|
||||
"TTS Settings": "Configuracions TTS",
|
||||
"Type Hugging Face Resolve (Download) URL": "Escriu URL de Resolució (Descàrrega) de Hugging Face",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "Uf! Hi va haver un problema connectant-se a {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tipus d'Arxiu Desconegut '{{file_type}}', però acceptant i tractant com a text pla",
|
||||
"Update password": "Actualitza contrasenya",
|
||||
"Upload a GGUF model": "Puja un model GGUF",
|
||||
"Upload files": "Puja arxius",
|
||||
"Upload Progress": "Progrés de Càrrega",
|
||||
"URL Mode": "Mode URL",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Utilitza '#' a l'entrada del prompt per carregar i seleccionar els teus documents.",
|
||||
"Use Gravatar": "Utilitza Gravatar",
|
||||
"user": "usuari",
|
||||
"User Permissions": "Permisos d'Usuari",
|
||||
"Users": "Usuaris",
|
||||
"Utilize": "Utilitza",
|
||||
"Valid time units:": "Unitats de temps vàlides:",
|
||||
"variable": "variable",
|
||||
"variable to have them replaced with clipboard content.": "variable per tenir-les reemplaçades amb el contingut del porta-retalls.",
|
||||
"Version": "Versió",
|
||||
"Web": "Web",
|
||||
"WebUI Add-ons": "Complements de WebUI",
|
||||
"WebUI Settings": "Configuració de WebUI",
|
||||
"WebUI will make requests to": "WebUI farà peticions a",
|
||||
"What’s New in": "Què hi ha de Nou en",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Quan l'historial està desactivat, els nous xats en aquest navegador no apareixeran en el teu historial en cap dels teus dispositius.",
|
||||
"Whisper (Local)": "Whisper (Local)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Escriu una suggerència de prompt (p. ex. Qui ets tu?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Escriu un resum en 50 paraules que resumeixi [tema o paraula clau].",
|
||||
"You": "Tu",
|
||||
"You're a helpful assistant.": "Ets un assistent útil.",
|
||||
"You're now logged in.": "Ara estàs connectat."
|
||||
}
|
||||
363
src/lib/i18n/locales/de-DE/translation.json
Normal file
363
src/lib/i18n/locales/de-DE/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' oder '-1' für kein Ablaufdatum.",
|
||||
"(Beta)": "(Beta)",
|
||||
"(e.g. `sh webui.sh --api`)": "(z.B. `sh webui.sh --api`)",
|
||||
"(latest)": "(neueste)",
|
||||
"{{modelName}} is thinking...": "{{modelName}} denkt nach...",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}}-Backend erforderlich",
|
||||
"a user": "",
|
||||
"About": "Über",
|
||||
"Account": "Account",
|
||||
"Action": "Aktion",
|
||||
"Add a model": "Füge ein Modell hinzu",
|
||||
"Add a model tag name": "Benenne dein Modell-Tag",
|
||||
"Add a short description about what this modelfile does": "Füge eine kurze Beschreibung hinzu, was dieses Modelfile kann",
|
||||
"Add a short title for this prompt": "Füge einen kurzen Titel für diesen Prompt hinzu",
|
||||
"Add a tag": "Tag hinzugügen",
|
||||
"Add Docs": "Dokumente hinzufügen",
|
||||
"Add Files": "Dateien hinzufügen",
|
||||
"Add message": "Nachricht eingeben",
|
||||
"add tags": "Tags hinzufügen",
|
||||
"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",
|
||||
"Admin Settings": "Admin Einstellungen",
|
||||
"Advanced Parameters": "Erweiterte Parameter",
|
||||
"all": "Alle",
|
||||
"All Users": "Alle Benutzer",
|
||||
"Allow": "Erlauben",
|
||||
"Allow Chat Deletion": "Chat Löschung erlauben",
|
||||
"alphanumeric characters and hyphens": "alphanumerische Zeichen und Bindestriche",
|
||||
"Already have an account?": "Hast du vielleicht schon ein Account?",
|
||||
"an assistant": "",
|
||||
"and": "und",
|
||||
"API Base URL": "API Basis URL",
|
||||
"API Key": "API Key",
|
||||
"API RPM": "API RPM",
|
||||
"are allowed - Activate this command by typing": "sind erlaubt - Aktiviere diesen Befehl, indem du",
|
||||
"Are you sure?": "",
|
||||
"Audio": "Audio",
|
||||
"Auto-playback response": "Automatische Wiedergabe der Antwort",
|
||||
"Auto-send input after 3 sec.": "Automatisches Senden der Eingabe nach 3 Sek",
|
||||
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Basis URL",
|
||||
"AUTOMATIC1111 Base URL is required.": "",
|
||||
"available!": "verfügbar!",
|
||||
"Back": "Zurück",
|
||||
"Builder Mode": "Builder Modus",
|
||||
"Cancel": "Abbrechen",
|
||||
"Categories": "Kategorien",
|
||||
"Change Password": "Passwort ändern",
|
||||
"Chat": "Chat",
|
||||
"Chat History": "Chat Verlauf",
|
||||
"Chat History is off for this browser.": "Chat Verlauf ist für diesen Browser ausgeschaltet.",
|
||||
"Chats": "Chats",
|
||||
"Check Again": "Erneut überprüfen",
|
||||
"Check for updates": "Nach Updates suchen",
|
||||
"Checking for updates...": "Nach Updates suchen...",
|
||||
"Choose a model before saving...": "Wähle bitte zuerst ein Modell, bevor du speicherst...",
|
||||
"Chunk Overlap": "Chunk Overlap",
|
||||
"Chunk Params": "Chunk Parameter",
|
||||
"Chunk Size": "Chunk Size",
|
||||
"Click here for help.": "Klicke hier für Hilfe.",
|
||||
"Click here to check other modelfiles.": "Klicke hier, um andere Modelfiles zu überprüfen.",
|
||||
"Click here to select": "",
|
||||
"Click here to select documents.": "",
|
||||
"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.",
|
||||
"Close": "Schließe",
|
||||
"Collection": "Kollektion",
|
||||
"Command": "Befehl",
|
||||
"Confirm Password": "Passwort bestätigen",
|
||||
"Connections": "Verbindungen",
|
||||
"Content": "Inhalt",
|
||||
"Context Length": "Context Length",
|
||||
"Conversation Mode": "Konversationsmodus",
|
||||
"Copy last code block": "Letzten Codeblock kopieren",
|
||||
"Copy last response": "Letzte Antwort kopieren",
|
||||
"Copying to clipboard was successful!": "Das Kopieren in die Zwischenablage war erfolgreich!",
|
||||
"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':": "Erstelle einen prägnanten Satz mit 3-5 Wörtern als Überschrift für die folgende Abfrage. Halte dich dabei strikt an die 3-5-Wort-Grenze und vermeide die Verwendung des Wortes Titel:",
|
||||
"Create a modelfile": "Modelfiles erstellen",
|
||||
"Create Account": "Konto erstellen",
|
||||
"Created at": "Erstellt am",
|
||||
"Created by": "Erstellt von",
|
||||
"Current Model": "Aktuelles Modell",
|
||||
"Current Password": "Aktuelles Passwort",
|
||||
"Custom": "Benutzerdefiniert",
|
||||
"Customize Ollama models for a specific purpose": "Ollama-Modelle für einen bestimmten Zweck anpassen",
|
||||
"Dark": "Dunkel",
|
||||
"Database": "Datenbank",
|
||||
"DD/MM/YYYY HH:mm": "DD.MM.YYYY HH:mm",
|
||||
"Default": "Standard",
|
||||
"Default (Automatic1111)": "",
|
||||
"Default (Web API)": "Standard (Web-API)",
|
||||
"Default model updated": "Standardmodell aktualisiert",
|
||||
"Default Prompt Suggestions": "Standard-Prompt-Vorschläge",
|
||||
"Default User Role": "Standardbenutzerrolle",
|
||||
"delete": "löschen",
|
||||
"Delete a model": "Ein Modell löschen",
|
||||
"Delete chat": "Chat löschen",
|
||||
"Delete Chats": "Chats löschen",
|
||||
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} gelöscht",
|
||||
"Deleted {tagName}": "{tagName} gelöscht",
|
||||
"Description": "Beschreibung",
|
||||
"Desktop Notifications": "Desktop-Benachrichtigungen",
|
||||
"Disabled": "Deaktiviert",
|
||||
"Discover a modelfile": "Eine Modelfiles entdecken",
|
||||
"Discover a prompt": "Einen Prompt entdecken",
|
||||
"Discover, download, and explore custom prompts": "Benutzerdefinierte Prompts entdecken, herunterladen und erkunden",
|
||||
"Discover, download, and explore model presets": "Modellvorgaben entdecken, herunterladen und erkunden",
|
||||
"Display the username instead of You in the Chat": "Den Benutzernamen anstelle von 'Du' im Chat anzeigen",
|
||||
"Document": "Dokument",
|
||||
"Document Settings": "Dokumenteinstellungen",
|
||||
"Documents": "Dokumente",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "stellt keine externen Verbindungen her, und Deine Daten bleiben sicher auf Deinen lokal gehosteten Server.",
|
||||
"Don't Allow": "Nicht erlauben",
|
||||
"Don't have an account?": "Hast du vielleicht noch kein Account?",
|
||||
"Download as a File": "Als Datei herunterladen",
|
||||
"Download Database": "Datenbank herunterladen",
|
||||
"Drop any files here to add to the conversation": "Lasse Dateien hier fallen, um sie dem Chat anzuhängen",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "z.B. '30s','10m'. Gültige Zeiteinheiten sind 's', 'm', 'h'.",
|
||||
"Edit Doc": "Dokument bearbeiten",
|
||||
"Edit User": "Benutzer bearbeiten",
|
||||
"Email": "E-Mail",
|
||||
"Enable Chat History": "Chat-Verlauf aktivieren",
|
||||
"Enable New Sign Ups": "Neue Anmeldungen aktivieren",
|
||||
"Enabled": "Aktiviert",
|
||||
"Enter {{role}} message here": "",
|
||||
"Enter API Key": "",
|
||||
"Enter Chunk Overlap": "",
|
||||
"Enter Chunk Size": "",
|
||||
"Enter Image Size (e.g. 512x512)": "",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "",
|
||||
"Enter Number of Steps (e.g. 50)": "",
|
||||
"Enter stop sequence": "Stop-Sequenz eingeben",
|
||||
"Enter Top K": "",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
|
||||
"Enter Your Email": "Geben Deine E-Mail-Adresse ein",
|
||||
"Enter Your Full Name": "Gebe Deinen vollständigen Namen ein",
|
||||
"Enter Your Password": "Gebe Dein Passwort ein",
|
||||
"Experimental": "Experimentell",
|
||||
"Export All Chats (All Users)": "Alle Chats exportieren (alle Benutzer)",
|
||||
"Export Chats": "Chats exportieren",
|
||||
"Export Documents Mapping": "Dokumentenmapping exportieren",
|
||||
"Export Modelfiles": "Modelfiles exportieren",
|
||||
"Export Prompts": "Prompts exportieren",
|
||||
"Failed to read clipboard contents": "Fehler beim Lesen des Zwischenablageninhalts",
|
||||
"File Mode": "File Mode",
|
||||
"File not found.": "Datei nicht gefunden.",
|
||||
"Focus chat input": "Chat-Eingabe fokussieren",
|
||||
"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",
|
||||
"General Settings": "Allgemeine Einstellungen",
|
||||
"Hello, {{name}}": "Hallo, {{name}}",
|
||||
"Hide": "Verbergen",
|
||||
"Hide Additional Params": "Hide Additional Params",
|
||||
"How can I help you today?": "Wie kann ich Dir heute helfen?",
|
||||
"Image Generation (Experimental)": "Bildgenerierung (experimentell)",
|
||||
"Image Generation Engine": "",
|
||||
"Image Settings": "Bildeinstellungen",
|
||||
"Images": "Bilder",
|
||||
"Import Chats": "Chats importieren",
|
||||
"Import Documents Mapping": "Dokumentenmapping importieren",
|
||||
"Import Modelfiles": "Modelfiles importieren",
|
||||
"Import Prompts": "Prompts importieren",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Füge das `--api`-Flag hinzu, wenn Du stable-diffusion-webui nutzt",
|
||||
"Interface": "Benutzeroberfläche",
|
||||
"join our Discord for help.": "Trete unserem Discord bei, um Hilfe zu erhalten.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "JWT-Ablauf",
|
||||
"JWT Token": "JWT-Token",
|
||||
"Keep Alive": "Keep Alive",
|
||||
"Keyboard shortcuts": "Tastenkürzel",
|
||||
"Language": "Sprache",
|
||||
"Light": "Hell",
|
||||
"Listening...": "Hören...",
|
||||
"LLMs can make mistakes. Verify important information.": "LLMs können Fehler machen. Überprüfe wichtige Informationen.",
|
||||
"Made by OpenWebUI Community": "Von der OpenWebUI-Community",
|
||||
"Make sure to enclose them with": "Formatiere Deine Variablen mit:",
|
||||
"Manage LiteLLM Models": "LiteLLM-Modelle verwalten",
|
||||
"Manage Models": "Modelle verwalten",
|
||||
"Manage Ollama Models": "Ollama-Modelle verwalten",
|
||||
"Max Tokens": "Maximale Tokens",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es können maximal 3 Modelle gleichzeitig heruntergeladen werden. Bitte versuche es später erneut.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "DD.MM.YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "Modell '{{modelName}}' wurde erfolgreich heruntergeladen.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Modell '{{modelTag}}' befindet sich bereits in der Warteschlange zum Herunterladen.",
|
||||
"Model {{modelId}} not found": "Modell {{modelId}} nicht gefunden",
|
||||
"Model {{modelName}} already exists.": "Modell {{modelName}} existiert bereits.",
|
||||
"Model Name": "Modellname",
|
||||
"Model not selected": "Modell nicht ausgewählt",
|
||||
"Model Tag Name": "Modell-Tag-Name",
|
||||
"Model Whitelisting": "Modell-Whitelisting",
|
||||
"Model(s) Whitelisted": "Modell(e) auf der Whitelist",
|
||||
"Modelfile": "Modelfiles",
|
||||
"Modelfile Advanced Settings": "Erweiterte Modelfileseinstellungen",
|
||||
"Modelfile Content": "Modelfile Content",
|
||||
"Modelfiles": "Modelfiles",
|
||||
"Models": "Modelle",
|
||||
"My Documents": "Meine Dokumente",
|
||||
"My Modelfiles": "Meine Modelfiles",
|
||||
"My Prompts": "Meine Prompts",
|
||||
"Name": "Name",
|
||||
"Name Tag": "Namens-Tag",
|
||||
"Name your modelfile": "Name your modelfile",
|
||||
"New Chat": "Neuer Chat",
|
||||
"New Password": "Neues Passwort",
|
||||
"Not sure what to add?": "Nicht sicher, was hinzugefügt werden soll?",
|
||||
"Not sure what to write? Switch to": "Nicht sicher, was Du schreiben sollst? Wechsel zu",
|
||||
"Off": "Aus",
|
||||
"Okay, Let's Go!": "Okay, los geht's!",
|
||||
"Ollama Base URL": "",
|
||||
"Ollama Version": "Ollama-Version",
|
||||
"On": "Ein",
|
||||
"Only": "Nur",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "Nur alphanumerische Zeichen und Bindestriche sind im Befehlsstring erlaubt.",
|
||||
"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.": "Hoppla! Warte noch einen Moment! Die Dateien sind noch im der Verarbeitung. Bitte habe etwas Geduld und wir informieren Dich, sobald sie bereit sind.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oops! Looks like the URL is invalid. Please double-check and try again.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.",
|
||||
"Open": "Öffne",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "",
|
||||
"Open new chat": "Neuen Chat öffnen",
|
||||
"OpenAI API": "OpenAI-API",
|
||||
"OpenAI API Key": "",
|
||||
"OpenAI API Key is required.": "",
|
||||
"or": "oder",
|
||||
"Parameters": "Parameter",
|
||||
"Password": "Passwort",
|
||||
"PDF Extract Images (OCR)": "Text von Bilder aus PDFs extrahieren (OCR)",
|
||||
"pending": "ausstehend",
|
||||
"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}",
|
||||
"Playground": "Playground",
|
||||
"Profile": "Profil",
|
||||
"Prompt Content": "Prompt-Inhalt",
|
||||
"Prompt suggestions": "Prompt-Vorschläge",
|
||||
"Prompts": "Prompts",
|
||||
"Pull a model from Ollama.com": "Ein Modell von Ollama.com abrufen",
|
||||
"Pull Progress": "Fortschritt abrufen",
|
||||
"Query Params": "Query Parameter",
|
||||
"RAG Template": "RAG-Vorlage",
|
||||
"Raw Format": "Rohformat",
|
||||
"Record voice": "Stimme aufnehmen",
|
||||
"Redirecting you to OpenWebUI Community": "Du wirst zur OpenWebUI-Community weitergeleitet",
|
||||
"Release Notes": "Versionshinweise",
|
||||
"Repeat Last N": "Repeat Last N",
|
||||
"Repeat Penalty": "Repeat Penalty",
|
||||
"Request Mode": "Request-Modus",
|
||||
"Reset Vector Storage": "Vektorspeicher zurücksetzen",
|
||||
"Response AutoCopy to Clipboard": "Antwort automatisch in die Zwischenablage kopieren",
|
||||
"Role": "Rolle",
|
||||
"Rosé Pine": "Rosé Pine",
|
||||
"Rosé Pine Dawn": "Rosé Pine Dawn",
|
||||
"Save": "Speichern",
|
||||
"Save & Create": "Speichern und erstellen",
|
||||
"Save & Submit": "Speichern und senden",
|
||||
"Save & Update": "Speichern und aktualisieren",
|
||||
"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": "Das direkte Speichern von Chat-Protokollen im Browser-Speicher wird nicht mehr unterstützt. Bitte nehme Dir einen Moment Zeit, um Deine Chat-Protokolle herunterzuladen und zu löschen, indem Du auf die Schaltfläche unten klickst. Keine Sorge, Du kannst Deine Chat-Protokolle problemlos über das Backend wieder importieren.",
|
||||
"Scan": "Scannen",
|
||||
"Scan complete!": "Scan abgeschlossen!",
|
||||
"Scan for documents from {{path}}": "Dokumente von {{path}} scannen",
|
||||
"Search": "Suchen",
|
||||
"Search Documents": "Dokumente suchen",
|
||||
"Search Prompts": "Prompts suchen",
|
||||
"See readme.md for instructions": "Anleitung in readme.md anzeigen",
|
||||
"See what's new": "Was gibt's Neues",
|
||||
"Seed": "Seed",
|
||||
"Select a mode": "",
|
||||
"Select a model": "Ein Modell auswählen",
|
||||
"Select an Ollama instance": "",
|
||||
"Send a Message": "Eine Nachricht senden",
|
||||
"Send message": "Nachricht senden",
|
||||
"Server connection verified": "Serververbindung überprüft",
|
||||
"Set as default": "Als Standard festlegen",
|
||||
"Set Default Model": "Standardmodell festlegen",
|
||||
"Set Image Size": "Bildgröße festlegen",
|
||||
"Set Steps": "Schritte festlegen",
|
||||
"Set Title Auto-Generation Model": "Modell für automatische Titelgenerierung festlegen",
|
||||
"Set Voice": "Stimme festlegen",
|
||||
"Settings": "Einstellungen",
|
||||
"Settings saved successfully!": "Einstellungen erfolgreich gespeichert!",
|
||||
"Share to OpenWebUI Community": "Mit OpenWebUI Community teilen",
|
||||
"short-summary": "kurze-zusammenfassung",
|
||||
"Show": "Anzeigen",
|
||||
"Show Additional Params": "Show Additional Params",
|
||||
"Show shortcuts": "Verknüpfungen anzeigen",
|
||||
"sidebar": "Seitenleiste",
|
||||
"Sign in": "Anmelden",
|
||||
"Sign Out": "Abmelden",
|
||||
"Sign up": "Registrieren",
|
||||
"Speech recognition error: {{error}}": "Spracherkennungsfehler: {{error}}",
|
||||
"Speech-to-Text Engine": "Sprache-zu-Text-Engine",
|
||||
"SpeechRecognition API is not supported in this browser.": "Die SpeechRecognition-API wird in diesem Browser nicht unterstützt.",
|
||||
"Stop Sequence": "Stop Sequence",
|
||||
"STT Settings": "STT-Einstellungen",
|
||||
"Submit": "Senden",
|
||||
"Success": "Erfolg",
|
||||
"Successfully updated.": "Erfolgreich aktualisiert.",
|
||||
"Sync All": "Alles synchronisieren",
|
||||
"System": "System",
|
||||
"System Prompt": "System-Prompt",
|
||||
"Tags": "Tags",
|
||||
"Temperature": "Temperatur",
|
||||
"Template": "Vorlage",
|
||||
"Text Completion": "Textvervollständigung",
|
||||
"Text-to-Speech Engine": "Text-zu-Sprache-Engine",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Design",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dadurch werden Deine wertvollen Unterhaltungen sicher in der Backend-Datenbank gespeichert. Vielen Dank!",
|
||||
"This setting does not sync across browsers or devices.": "Diese Einstellung wird nicht zwischen Browsern oder Geräten synchronisiert.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.",
|
||||
"Title": "Titel",
|
||||
"Title Auto-Generation": "Automatische Titelgenerierung",
|
||||
"Title Generation Prompt": "Prompt für Titelgenerierung",
|
||||
"to": "für",
|
||||
"To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zum Herunterladen zuzugreifen,",
|
||||
"To access the GGUF models available for downloading,": "To access the GGUF models available for downloading,",
|
||||
"to chat input.": "to chat input.",
|
||||
"Toggle settings": "Einstellungen umschalten",
|
||||
"Toggle sidebar": "Seitenleiste umschalten",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?",
|
||||
"TTS Settings": "TTS-Einstellungen",
|
||||
"Type Hugging Face Resolve (Download) URL": "",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Unknown File Type '{{file_type}}', but accepting and treating as plain text",
|
||||
"Update password": "Passwort aktualisieren",
|
||||
"Upload a GGUF model": "Upload a GGUF model",
|
||||
"Upload files": "Dateien hochladen",
|
||||
"Upload Progress": "Upload Progress",
|
||||
"URL Mode": "URL Mode",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Verwende '#' in der Prompt-Eingabe, um Deine Dokumente zu laden und auszuwählen.",
|
||||
"Use Gravatar": "",
|
||||
"user": "Benutzer",
|
||||
"User Permissions": "Benutzerberechtigungen",
|
||||
"Users": "Benutzer",
|
||||
"Utilize": "Nutze die",
|
||||
"Valid time units:": "Gültige Zeiteinheiten:",
|
||||
"variable": "Variable",
|
||||
"variable to have them replaced with clipboard content.": "Variable, um den Inhalt der Zwischenablage beim Nutzen des Prompts zu ersetzen.",
|
||||
"Version": "Version",
|
||||
"Web": "Web",
|
||||
"WebUI Add-ons": "WebUI-Add-Ons",
|
||||
"WebUI Settings": "WebUI-Einstellungen",
|
||||
"WebUI will make requests to": "Wenn aktiviert sendet WebUI externe Anfragen an",
|
||||
"What’s New in": "Was gibt's Neues in",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Wenn die Historie ausgeschaltet ist, werden neue Chats nicht in Deiner Historie auf Deine Geräte angezeigt.",
|
||||
"Whisper (Local)": "Whisper (Lokal)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Gebe einen Prompt-Vorschlag ein (z.B. Wer bist du?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.",
|
||||
"You": "Du",
|
||||
"You're a helpful assistant.": "Du bist ein hilfreicher Assistent.",
|
||||
"You're now logged in.": "Du bist nun eingeloggt."
|
||||
}
|
||||
363
src/lib/i18n/locales/en-US/translation.json
Normal file
363
src/lib/i18n/locales/en-US/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.",
|
||||
"(Beta)": "(Beta)",
|
||||
"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
|
||||
"(latest)": "(latest)",
|
||||
"{{modelName}} is thinking...": "{{modelName}} is thinking...",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Backend Required",
|
||||
"a user": "",
|
||||
"About": "About",
|
||||
"Account": "Account",
|
||||
"Action": "Action",
|
||||
"Add a model": "Add a model",
|
||||
"Add a model tag name": "Add a model tag name",
|
||||
"Add a short description about what this modelfile does": "Add a short description about what this modelfile does",
|
||||
"Add a short title for this prompt": "Add a short title for this prompt",
|
||||
"Add a tag": "Add a tag",
|
||||
"Add Docs": "Add Docs",
|
||||
"Add Files": "Add Files",
|
||||
"Add message": "Add message",
|
||||
"add tags": "add tags",
|
||||
"Adjusting these settings will apply changes universally to all users.": "Adjusting these settings will apply changes universally to all users.",
|
||||
"admin": "admin",
|
||||
"Admin Panel": "Admin Panel",
|
||||
"Admin Settings": "Admin Settings",
|
||||
"Advanced Parameters": "Advanced Parameters",
|
||||
"all": "all",
|
||||
"All Users": "All Users",
|
||||
"Allow": "Allow",
|
||||
"Allow Chat Deletion": "Allow Chat Deletion",
|
||||
"alphanumeric characters and hyphens": "alphanumeric characters and hyphens",
|
||||
"Already have an account?": "Already have an account?",
|
||||
"an assistant": "",
|
||||
"and": "and",
|
||||
"API Base URL": "API Base URL",
|
||||
"API Key": "API Key",
|
||||
"API RPM": "API RPM",
|
||||
"are allowed - Activate this command by typing": "are allowed - Activate this command by typing",
|
||||
"Are you sure?": "",
|
||||
"Audio": "Audio",
|
||||
"Auto-playback response": "Auto-playback response",
|
||||
"Auto-send input after 3 sec.": "Auto-send input after 3 sec.",
|
||||
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
|
||||
"AUTOMATIC1111 Base URL is required.": "",
|
||||
"available!": "available!",
|
||||
"Back": "Back",
|
||||
"Builder Mode": "Builder Mode",
|
||||
"Cancel": "Cancel",
|
||||
"Categories": "Categories",
|
||||
"Change Password": "Change Password",
|
||||
"Chat": "Chat",
|
||||
"Chat History": "Chat History",
|
||||
"Chat History is off for this browser.": "Chat History is off for this browser.",
|
||||
"Chats": "Chats",
|
||||
"Check Again": "Check Again",
|
||||
"Check for updates": "Check for updates",
|
||||
"Checking for updates...": "Checking for updates...",
|
||||
"Choose a model before saving...": "Choose a model before saving...",
|
||||
"Chunk Overlap": "Chunk Overlap",
|
||||
"Chunk Params": "Chunk Params",
|
||||
"Chunk Size": "Chunk Size",
|
||||
"Click here for help.": "Click here for help.",
|
||||
"Click here to check other modelfiles.": "Click here to check other modelfiles.",
|
||||
"Click here to select": "",
|
||||
"Click here to select documents.": "",
|
||||
"click here.": "click here.",
|
||||
"Click on the user role button to change a user's role.": "Click on the user role button to change a user's role.",
|
||||
"Close": "Close",
|
||||
"Collection": "Collection",
|
||||
"Command": "Command",
|
||||
"Confirm Password": "Confirm Password",
|
||||
"Connections": "Connections",
|
||||
"Content": "Content",
|
||||
"Context Length": "Context Length",
|
||||
"Conversation Mode": "Conversation Mode",
|
||||
"Copy last code block": "Copy last code block",
|
||||
"Copy last response": "Copy last response",
|
||||
"Copying to clipboard was successful!": "Copying to clipboard was successful!",
|
||||
"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':": "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':",
|
||||
"Create a modelfile": "Create a modelfile",
|
||||
"Create Account": "Create Account",
|
||||
"Created at": "Created at",
|
||||
"Created by": "Created by",
|
||||
"Current Model": "Current Model",
|
||||
"Current Password": "Current Password",
|
||||
"Custom": "Custom",
|
||||
"Customize Ollama models for a specific purpose": "Customize Ollama models for a specific purpose",
|
||||
"Dark": "Dark",
|
||||
"Database": "Database",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "Default",
|
||||
"Default (Automatic1111)": "",
|
||||
"Default (Web API)": "Default (Web API)",
|
||||
"Default model updated": "Default model updated",
|
||||
"Default Prompt Suggestions": "Default Prompt Suggestions",
|
||||
"Default User Role": "Default User Role",
|
||||
"delete": "delete",
|
||||
"Delete a model": "Delete a model",
|
||||
"Delete chat": "Delete chat",
|
||||
"Delete Chats": "Delete Chats",
|
||||
"Deleted {{deleteModelTag}}": "Deleted {{deleteModelTag}}",
|
||||
"Deleted {tagName}": "Deleted {tagName}",
|
||||
"Description": "Description",
|
||||
"Desktop Notifications": "Notification",
|
||||
"Disabled": "Disabled",
|
||||
"Discover a modelfile": "Discover a modelfile",
|
||||
"Discover a prompt": "Discover a prompt",
|
||||
"Discover, download, and explore custom prompts": "Discover, download, and explore custom prompts",
|
||||
"Discover, download, and explore model presets": "Discover, download, and explore model presets",
|
||||
"Display the username instead of You in the Chat": "Display the username instead of 'You' in the Chat",
|
||||
"Document": "Document",
|
||||
"Document Settings": "Document Settings",
|
||||
"Documents": "Documents",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "does not make any external connections, and your data stays securely on your locally hosted server.",
|
||||
"Don't Allow": "Don't Allow",
|
||||
"Don't have an account?": "Don't have an account?",
|
||||
"Download as a File": "Download as a File",
|
||||
"Download Database": "Download Database",
|
||||
"Drop any files here to add to the conversation": "Drop any files here to add to the conversation",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.",
|
||||
"Edit Doc": "Edit Doc",
|
||||
"Edit User": "Edit User",
|
||||
"Email": "Email",
|
||||
"Enable Chat History": "Enable Chat History",
|
||||
"Enable New Sign Ups": "Enable New Sign Ups",
|
||||
"Enabled": "Enabled",
|
||||
"Enter {{role}} message here": "",
|
||||
"Enter API Key": "",
|
||||
"Enter Chunk Overlap": "",
|
||||
"Enter Chunk Size": "",
|
||||
"Enter Image Size (e.g. 512x512)": "",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "",
|
||||
"Enter Number of Steps (e.g. 50)": "",
|
||||
"Enter stop sequence": "Enter stop sequence",
|
||||
"Enter Top K": "",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
|
||||
"Enter Your Email": "Enter Your Email",
|
||||
"Enter Your Full Name": "Enter Your Full Name",
|
||||
"Enter Your Password": "Enter Your Password",
|
||||
"Experimental": "Experimental",
|
||||
"Export All Chats (All Users)": "Export All Chats (All Users)",
|
||||
"Export Chats": "Export Chats",
|
||||
"Export Documents Mapping": "Export Documents Mapping",
|
||||
"Export Modelfiles": "Export Modelfiles",
|
||||
"Export Prompts": "Export Prompts",
|
||||
"Failed to read clipboard contents": "Failed to read clipboard contents",
|
||||
"File Mode": "File Mode",
|
||||
"File not found.": "File not found.",
|
||||
"Focus chat input": "Focus chat input",
|
||||
"Format your variables using square brackets like this:": "Format your variables using square brackets like this:",
|
||||
"From (Base Model)": "From (Base Model)",
|
||||
"Full Screen Mode": "Full Screen Mode",
|
||||
"General": "General",
|
||||
"General Settings": "General Settings",
|
||||
"Hello, {{name}}": "Hello, {{name}}",
|
||||
"Hide": "Hide",
|
||||
"Hide Additional Params": "Hide Additional Params",
|
||||
"How can I help you today?": "How can I help you today?",
|
||||
"Image Generation (Experimental)": "Image Generation (Experimental)",
|
||||
"Image Generation Engine": "",
|
||||
"Image Settings": "Image Settings",
|
||||
"Images": "Images",
|
||||
"Import Chats": "Import Chats",
|
||||
"Import Documents Mapping": "Import Documents Mapping",
|
||||
"Import Modelfiles": "Import Modelfiles",
|
||||
"Import Prompts": "Import Prompts",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Include `--api` flag when running stable-diffusion-webui",
|
||||
"Interface": "Interface",
|
||||
"join our Discord for help.": "join our Discord for help.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "JWT Expiration",
|
||||
"JWT Token": "JWT Token",
|
||||
"Keep Alive": "Keep Alive",
|
||||
"Keyboard shortcuts": "Keyboard shortcuts",
|
||||
"Language": "Language",
|
||||
"Light": "Light",
|
||||
"Listening...": "Listening...",
|
||||
"LLMs can make mistakes. Verify important information.": "LLMs can make mistakes. Verify important information.",
|
||||
"Made by OpenWebUI Community": "Made by OpenWebUI Community",
|
||||
"Make sure to enclose them with": "Make sure to enclose them with",
|
||||
"Manage LiteLLM Models": "Manage LiteLLM Models",
|
||||
"Manage Models": "Manage Models",
|
||||
"Manage Ollama Models": "Manage Ollama Models",
|
||||
"Max Tokens": "Max Tokens",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximum of 3 models can be downloaded simultaneously. Please try again later.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' has been successfully downloaded.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' is already in queue for downloading.",
|
||||
"Model {{modelId}} not found": "Model {{modelId}} not found",
|
||||
"Model {{modelName}} already exists.": "Model {{modelName}} already exists.",
|
||||
"Model Name": "Model Name",
|
||||
"Model not selected": "Model not selected",
|
||||
"Model Tag Name": "Model Tag Name",
|
||||
"Model Whitelisting": "Model Whitelisting",
|
||||
"Model(s) Whitelisted": "Model(s) Whitelisted",
|
||||
"Modelfile": "Modelfile",
|
||||
"Modelfile Advanced Settings": "Modelfile Advanced Settings",
|
||||
"Modelfile Content": "Modelfile Content",
|
||||
"Modelfiles": "Modelfiles",
|
||||
"Models": "Models",
|
||||
"My Documents": "My Documents",
|
||||
"My Modelfiles": "My Modelfiles",
|
||||
"My Prompts": "My Prompts",
|
||||
"Name": "Name",
|
||||
"Name Tag": "Name Tag",
|
||||
"Name your modelfile": "Name your modelfile",
|
||||
"New Chat": "New Chat",
|
||||
"New Password": "New Password",
|
||||
"Not sure what to add?": "Not sure what to add?",
|
||||
"Not sure what to write? Switch to": "Not sure what to write? Switch to",
|
||||
"Off": "Off",
|
||||
"Okay, Let's Go!": "Okay, Let's Go!",
|
||||
"Ollama Base URL": "",
|
||||
"Ollama Version": "Ollama Version",
|
||||
"On": "On",
|
||||
"Only": "Only",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "Only alphanumeric characters and hyphens are allowed in the command string.",
|
||||
"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.": "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.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oops! Looks like the URL is invalid. Please double-check and try again.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.",
|
||||
"Open": "Open",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "",
|
||||
"Open new chat": "Open new chat",
|
||||
"OpenAI API": "OpenAI API",
|
||||
"OpenAI API Key": "",
|
||||
"OpenAI API Key is required.": "",
|
||||
"or": "or",
|
||||
"Parameters": "Parameters",
|
||||
"Password": "Password",
|
||||
"PDF Extract Images (OCR)": "PDF Extract Images (OCR)",
|
||||
"pending": "pending",
|
||||
"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}",
|
||||
"Playground": "Playground",
|
||||
"Profile": "Profile",
|
||||
"Prompt Content": "Prompt Content",
|
||||
"Prompt suggestions": "Prompt suggestions",
|
||||
"Prompts": "Prompts",
|
||||
"Pull a model from Ollama.com": "Pull a model from Ollama.com",
|
||||
"Pull Progress": "Pull Progress",
|
||||
"Query Params": "Query Params",
|
||||
"RAG Template": "RAG Template",
|
||||
"Raw Format": "Raw Format",
|
||||
"Record voice": "Record voice",
|
||||
"Redirecting you to OpenWebUI Community": "Redirecting you to OpenWebUI Community",
|
||||
"Release Notes": "Release Notes",
|
||||
"Repeat Last N": "Repeat Last N",
|
||||
"Repeat Penalty": "Repeat Penalty",
|
||||
"Request Mode": "Request Mode",
|
||||
"Reset Vector Storage": "Reset Vector Storage",
|
||||
"Response AutoCopy to Clipboard": "Response AutoCopy to Clipboard",
|
||||
"Role": "Role",
|
||||
"Rosé Pine": "Rosé Pine",
|
||||
"Rosé Pine Dawn": "Rosé Pine Dawn",
|
||||
"Save": "Save",
|
||||
"Save & Create": "Save & Create",
|
||||
"Save & Submit": "Save & Submit",
|
||||
"Save & Update": "Save & Update",
|
||||
"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": "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",
|
||||
"Scan": "Scan",
|
||||
"Scan complete!": "Scan complete!",
|
||||
"Scan for documents from {{path}}": "Scan for documents from {{path}}",
|
||||
"Search": "Search",
|
||||
"Search Documents": "Search Documents",
|
||||
"Search Prompts": "Search Prompts",
|
||||
"See readme.md for instructions": "See readme.md for instructions",
|
||||
"See what's new": "See what's new",
|
||||
"Seed": "Seed",
|
||||
"Select a mode": "",
|
||||
"Select a model": "Select a model",
|
||||
"Select an Ollama instance": "",
|
||||
"Send a Message": "Send a Message",
|
||||
"Send message": "Send message",
|
||||
"Server connection verified": "Server connection verified",
|
||||
"Set as default": "Set as default",
|
||||
"Set Default Model": "Set Default Model",
|
||||
"Set Image Size": "Set Image Size",
|
||||
"Set Steps": "Set Steps",
|
||||
"Set Title Auto-Generation Model": "Set Title Auto-Generation Model",
|
||||
"Set Voice": "Set Voice",
|
||||
"Settings": "Settings",
|
||||
"Settings saved successfully!": "Settings saved successfully!",
|
||||
"Share to OpenWebUI Community": "Share to OpenWebUI Community",
|
||||
"short-summary": "short-summary",
|
||||
"Show": "Show",
|
||||
"Show Additional Params": "Show Additional Params",
|
||||
"Show shortcuts": "Show shortcuts",
|
||||
"sidebar": "sidebar",
|
||||
"Sign in": "Sign in",
|
||||
"Sign Out": "Sign Out",
|
||||
"Sign up": "Sign up",
|
||||
"Speech recognition error: {{error}}": "Speech recognition error: {{error}}",
|
||||
"Speech-to-Text Engine": "Speech-to-Text Engine",
|
||||
"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API is not supported in this browser.",
|
||||
"Stop Sequence": "Stop Sequence",
|
||||
"STT Settings": "STT Settings",
|
||||
"Submit": "Submit",
|
||||
"Success": "Success",
|
||||
"Successfully updated.": "Successfully updated.",
|
||||
"Sync All": "Sync All",
|
||||
"System": "System",
|
||||
"System Prompt": "System Prompt",
|
||||
"Tags": "Tags",
|
||||
"Temperature": "Temperature",
|
||||
"Template": "Template",
|
||||
"Text Completion": "Text Completion",
|
||||
"Text-to-Speech Engine": "Text-to-Speech Engine",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Theme",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "This ensures that your valuable conversations are securely saved to your backend database. Thank you!",
|
||||
"This setting does not sync across browsers or devices.": "This setting does not sync across browsers or devices.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.",
|
||||
"Title": "Title",
|
||||
"Title Auto-Generation": "Title Auto-Generation",
|
||||
"Title Generation Prompt": "Title Generation Prompt",
|
||||
"to": "to",
|
||||
"To access the available model names for downloading,": "To access the available model names for downloading,",
|
||||
"To access the GGUF models available for downloading,": "To access the GGUF models available for downloading,",
|
||||
"to chat input.": "to chat input.",
|
||||
"Toggle settings": "Toggle settings",
|
||||
"Toggle sidebar": "Toggle sidebar",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "Trouble accessing Ollama?",
|
||||
"TTS Settings": "TTS Settings",
|
||||
"Type Hugging Face Resolve (Download) URL": "",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! There was an issue connecting to {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Unknown File Type '{{file_type}}', but accepting and treating as plain text",
|
||||
"Update password": "Update password",
|
||||
"Upload a GGUF model": "Upload a GGUF model",
|
||||
"Upload files": "Upload files",
|
||||
"Upload Progress": "Upload Progress",
|
||||
"URL Mode": "URL Mode",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Use '#' in the prompt input to load and select your documents.",
|
||||
"Use Gravatar": "",
|
||||
"user": "user",
|
||||
"User Permissions": "User Permissions",
|
||||
"Users": "Users",
|
||||
"Utilize": "Utilize",
|
||||
"Valid time units:": "Valid time units:",
|
||||
"variable": "variable",
|
||||
"variable to have them replaced with clipboard content.": "variable to have them replaced with clipboard content.",
|
||||
"Version": "Version",
|
||||
"Web": "Web",
|
||||
"WebUI Add-ons": "WebUI Add-ons",
|
||||
"WebUI Settings": "WebUI Settings",
|
||||
"WebUI will make requests to": "WebUI will make requests to",
|
||||
"What’s New in": "What’s New in",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "When history is turned off, new chats on this browser won't appear in your history on any of your devices.",
|
||||
"Whisper (Local)": "Whisper (Local)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Write a prompt suggestion (e.g. Who are you?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Write a summary in 50 words that summarizes [topic or keyword].",
|
||||
"You": "You",
|
||||
"You're a helpful assistant.": "You're a helpful assistant.",
|
||||
"You're now logged in.": "You're now logged in."
|
||||
}
|
||||
363
src/lib/i18n/locales/es-ES/translation.json
Normal file
363
src/lib/i18n/locales/es-ES/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' para evitar expiración.",
|
||||
"(Beta)": "(Beta)",
|
||||
"(e.g. `sh webui.sh --api`)": "(p.ej. `sh webui.sh --api`)",
|
||||
"(latest)": "(latest)",
|
||||
"{{modelName}} is thinking...": "{{modelName}} está pensando...",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Servidor Requerido",
|
||||
"a user": "un usuario",
|
||||
"About": "Sobre nosotros",
|
||||
"Account": "Cuenta",
|
||||
"Action": "Acción",
|
||||
"Add a model": "Agregar un modelo",
|
||||
"Add a model tag name": "Agregar un nombre de etiqueta de modelo",
|
||||
"Add a short description about what this modelfile does": "Agregue una descripción corta de lo que este modelfile hace",
|
||||
"Add a short title for this prompt": "Agregue un título corto para este Prompt",
|
||||
"Add a tag": "Agregar una etiqueta",
|
||||
"Add Docs": "Agregar Documentos",
|
||||
"Add Files": "Agregar Archivos",
|
||||
"Add message": "Agregar Prompt",
|
||||
"add tags": "agregar etiquetas",
|
||||
"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 Administrador",
|
||||
"Admin Settings": "Configuración de Administrador",
|
||||
"Advanced Parameters": "Parametros Avanzados",
|
||||
"all": "todo",
|
||||
"All Users": "Todos los Usuarios",
|
||||
"Allow": "Permitir",
|
||||
"Allow Chat Deletion": "Permitir Borrar Chats",
|
||||
"alphanumeric characters and hyphens": "caracteres alfanuméricos y guiones",
|
||||
"Already have an account?": "¿Ya tienes una cuenta?",
|
||||
"an assistant": "un asistente",
|
||||
"and": "y",
|
||||
"API Base URL": "Dirección URL de la API",
|
||||
"API Key": "Clave de la API ",
|
||||
"API RPM": "RPM de la API",
|
||||
"are allowed - Activate this command by typing": "están permitidos - Active este comando escribiendo",
|
||||
"Are you sure?": "Esta usted seguro?",
|
||||
"Audio": "Audio",
|
||||
"Auto-playback response": "Respuesta de reproducción automática",
|
||||
"Auto-send input after 3 sec.": "Envía la información entrada automáticamente luego de 3 segundos.",
|
||||
"AUTOMATIC1111 Base URL": "Dirección URL de AUTOMATIC1111",
|
||||
"AUTOMATIC1111 Base URL is required.": "La dirección URL de AUTOMATIC1111 es requerida.",
|
||||
"available!": "¡disponible!",
|
||||
"Back": "Vuelve atrás",
|
||||
"Builder Mode": "Modo de Constructor",
|
||||
"Cancel": "Cancela",
|
||||
"Categories": "Categorías",
|
||||
"Change Password": "Cambia la Contraseña",
|
||||
"Chat": "Chat",
|
||||
"Chat History": "Historial del Chat",
|
||||
"Chat History is off for this browser.": "El Historial del Chat está apagado para este navegador.",
|
||||
"Chats": "Chats",
|
||||
"Check Again": "Verifica de nuevo",
|
||||
"Check for updates": "Verifica actualizaciones",
|
||||
"Checking for updates...": "Verificando actualizaciones...",
|
||||
"Choose a model before saving...": "Escoge un modelo antes de guardar los cambios...",
|
||||
"Chunk Overlap": "Superposición de fragmentos",
|
||||
"Chunk Params": "Parámetros de fragmentos",
|
||||
"Chunk Size": "Tamaño de fragmentos",
|
||||
"Click here for help.": "Presiona aquí para ayuda.",
|
||||
"Click here to check other modelfiles.": "Presiona aquí para otros modelfiles.",
|
||||
"Click here to select": "Presiona aquí para seleccionar",
|
||||
"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 el rol de un usuario.",
|
||||
"Close": "Cerrar",
|
||||
"Collection": "Colección",
|
||||
"Command": "Comando",
|
||||
"Confirm Password": "Confirmar Contraseña",
|
||||
"Connections": "Conexiones",
|
||||
"Content": "Contenido",
|
||||
"Context Length": "Largura del contexto",
|
||||
"Conversation Mode": "Modo de Conversación",
|
||||
"Copy last code block": "Copia el último bloque de código",
|
||||
"Copy last response": "Copia la última respuesta",
|
||||
"Copying to clipboard was successful!": "¡Copiar al portapapeles fue exitoso!",
|
||||
"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':": "Cree una frase concisa de 3 a 5 palabras como encabezado para la siguiente consulta, respetando estrictamente el límite de 3 a 5 palabras y evitando el uso de la palabra 'título':",
|
||||
"Create a modelfile": "Crea un modelfile",
|
||||
"Create Account": "Crear una cuenta",
|
||||
"Created at": "Creado en",
|
||||
"Created by": "Creado por",
|
||||
"Current Model": "Modelo Actual",
|
||||
"Current Password": "Contraseña Actual",
|
||||
"Custom": "Personalizado",
|
||||
"Customize Ollama models for a specific purpose": "Personaliza modelos de Ollama para un propósito específico",
|
||||
"Dark": "Oscuro",
|
||||
"Database": "Base de datos",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "Por defecto",
|
||||
"Default (Automatic1111)": "Por defecto (Automatic1111)",
|
||||
"Default (Web API)": "Por defecto (Web API)",
|
||||
"Default model updated": "El modelo por defecto ha sido actualizado",
|
||||
"Default Prompt Suggestions": "Sugerencias de mensajes por defecto",
|
||||
"Default User Role": "Rol por defecto para usuarios",
|
||||
"delete": "borrar",
|
||||
"Delete a model": "Borra un modelo",
|
||||
"Delete chat": "Borrar chat",
|
||||
"Delete Chats": "Borrar Chats",
|
||||
"Deleted {{deleteModelTag}}": "Se borró {{deleteModelTag}}",
|
||||
"Deleted {tagName}": "Se borró {tagName}",
|
||||
"Description": "Descripción",
|
||||
"Desktop Notifications": "Notificaciones",
|
||||
"Disabled": "Desactivado",
|
||||
"Discover a modelfile": "Descubre un modelfile",
|
||||
"Discover a prompt": "Descubre un Prompt",
|
||||
"Discover, download, and explore custom prompts": "Descubre, descarga, y explora Prompts personalizados",
|
||||
"Discover, download, and explore model presets": "Descubra, descargue y explore ajustes preestablecidos de modelos",
|
||||
"Display the username instead of You in the Chat": "Mostrar el nombre de usuario en lugar de Usted en el chat",
|
||||
"Document": "Documento",
|
||||
"Document Settings": "Configuración de Documento",
|
||||
"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?",
|
||||
"Download as a File": "Descarga como un Archivo",
|
||||
"Download Database": "Descarga la Base de Datos",
|
||||
"Drop any files here to add to the conversation": "Suelta cualquier archivo aquí para agregarlo a la conversación",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p.ej. '30s','10m'. Unidades válidas de tiempo son 's', 'm', 'h'.",
|
||||
"Edit Doc": "Editar Documento",
|
||||
"Edit User": "Editar Usuario",
|
||||
"Email": "Email",
|
||||
"Enable Chat History": "Activa el Historial de Chat",
|
||||
"Enable New Sign Ups": "Habilitar Nuevos Registros",
|
||||
"Enabled": "Habilitado",
|
||||
"Enter {{role}} message here": "Introduzca el mensaje {{role}} aquí",
|
||||
"Enter API Key": "Ingrese la clave API",
|
||||
"Enter Chunk Overlap": "Ingresar superposición de fragmentos",
|
||||
"Enter Chunk Size": "Introduzca el tamaño del fragmento",
|
||||
"Enter Image Size (e.g. 512x512)": "Ingrese el tamaño de la imagen (p.ej. 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Ingrese la URL base de la API LiteLLM (litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "Ingrese la clave API LiteLLM (litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Ingrese el RPM de la API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Ingrese el modelo LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Ingrese tokens máximos (litellm_params.max_tokens)",
|
||||
"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 stop sequence": "Introduzca la secuencia de parada",
|
||||
"Enter Top K": "Introduzca 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 Your Email": "Introduce tu correo electrónico",
|
||||
"Enter Your Full Name": "Introduce tu nombre completo",
|
||||
"Enter Your Password": "Introduce tu contraseña",
|
||||
"Experimental": "Experimental",
|
||||
"Export All Chats (All Users)": "Exportar todos los chats (Todos los usuarios)",
|
||||
"Export Chats": "Exportar Chats",
|
||||
"Export Documents Mapping": "Exportar el mapeo de documentos",
|
||||
"Export Modelfiles": "Exportal Modelfiles",
|
||||
"Export Prompts": "Exportar Prompts",
|
||||
"Failed to read clipboard contents": "No se pudo leer el contenido del portapapeles",
|
||||
"File Mode": "Modo de archivo",
|
||||
"File not found.": "Archivo no encontrado.",
|
||||
"Focus chat input": "Enfoca la entrada del chat",
|
||||
"Format your variables using square brackets like this:": "Formatee sus variables usando corchetes así:",
|
||||
"From (Base Model)": "Desde (Modelo Base)",
|
||||
"Full Screen Mode": "Modo de Pantalla Completa",
|
||||
"General": "General",
|
||||
"General Settings": "Opciones Generales",
|
||||
"Hello, {{name}}": "Hola, {{name}}",
|
||||
"Hide": "Esconder",
|
||||
"Hide Additional Params": "Esconde los Parámetros Adicionales",
|
||||
"How can I help you today?": "¿Cómo puedo ayudarte hoy?",
|
||||
"Image Generation (Experimental)": "Generación de imágenes (experimental)",
|
||||
"Image Generation Engine": "Motor de generación de imágenes",
|
||||
"Image Settings": "Configuración de Imágen",
|
||||
"Images": "Imágenes",
|
||||
"Import Chats": "Importar chats",
|
||||
"Import Documents Mapping": "Importar Mapeo de Documentos",
|
||||
"Import Modelfiles": "Importar Modelfiles",
|
||||
"Import Prompts": "Importar Prompts",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Incluir el indicador `--api` al ejecutar stable-diffusion-webui",
|
||||
"Interface": "Interface",
|
||||
"join our Discord for help.": "Únase a nuestro Discord para obtener ayuda.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "Expiración del JWT",
|
||||
"JWT Token": "Token JWT",
|
||||
"Keep Alive": "Mantener Vivo",
|
||||
"Keyboard shortcuts": "Atajos de teclado",
|
||||
"Language": "Lenguaje",
|
||||
"Light": "Claro",
|
||||
"Listening...": "Escuchando...",
|
||||
"LLMs can make mistakes. Verify important information.": "Los LLM pueden cometer errores. Verifica la información importante.",
|
||||
"Made by OpenWebUI Community": "Hecho por la comunidad de OpenWebUI",
|
||||
"Make sure to enclose them with": "Make sure to enclose them with",
|
||||
"Manage LiteLLM Models": "Administrar Modelos LiteLLM",
|
||||
"Manage Models": "Administrar Modelos",
|
||||
"Manage Ollama Models": "Administrar Modelos Ollama",
|
||||
"Max Tokens": "Máximo de Tokens",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Se pueden descargar un máximo de 3 modelos simultáneamente. Por favor, inténtelo de nuevo más tarde.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "El modelo '{{modelName}}' se ha descargado correctamente.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "El modelo '{{modelTag}}' ya está en cola para descargar.",
|
||||
"Model {{modelId}} not found": "El modelo {{modelId}} no fue encontrado",
|
||||
"Model {{modelName}} already exists.": "El modelo {{modelName}} ya existe.",
|
||||
"Model Name": "Nombre del modelo",
|
||||
"Model not selected": "Modelo no seleccionado",
|
||||
"Model Tag Name": "Nombre de la etiqueta del modelo",
|
||||
"Model Whitelisting": "Listado de Modelos habilitados",
|
||||
"Model(s) Whitelisted": "Modelo(s) habilitados",
|
||||
"Modelfile": "Modelfile",
|
||||
"Modelfile Advanced Settings": "Opciones avanzadas del Modelfile",
|
||||
"Modelfile Content": "Contenido del Modelfile",
|
||||
"Modelfiles": "Modelfiles",
|
||||
"Models": "Modelos",
|
||||
"My Documents": "Mis Documentos",
|
||||
"My Modelfiles": "Mis Modelfiles",
|
||||
"My Prompts": "Mis Prompts",
|
||||
"Name": "Nombre",
|
||||
"Name Tag": "Nombre de etiqueta",
|
||||
"Name your modelfile": "Nombra tu modelfile",
|
||||
"New Chat": "Nuevo Chat",
|
||||
"New Password": "Nueva Contraseña",
|
||||
"Not sure what to add?": "¿No estás seguro de qué añadir?",
|
||||
"Not sure what to write? Switch to": "¿No estás seguro de qué escribir? Cambia a",
|
||||
"Off": "Apagado",
|
||||
"Okay, Let's Go!": "Okay, Let's Go!",
|
||||
"Ollama Base URL": "URL base de Ollama",
|
||||
"Ollama Version": "Version de Ollama",
|
||||
"On": "Encendido",
|
||||
"Only": "Solamente",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "Sólo se permiten caracteres alfanuméricos y guiones en la cadena de comando.",
|
||||
"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.": "¡Ups! ¡Agárrate fuerte! Tus archivos todavía están en el horno de procesamiento. Los estamos cocinando a la perfección. Tenga paciencia y le avisaremos una vez que estén listos.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "¡Ups! Parece que la URL no es válida. Vuelva a verificar e inténtelo nuevamente.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "¡Ups! Estás utilizando un método no compatible (solo frontend). Sirve la WebUI desde el backend.",
|
||||
"Open": "Abrir",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "",
|
||||
"Open new chat": "Abrir nuevo chat",
|
||||
"OpenAI API": "OpenAI API",
|
||||
"OpenAI API Key": "Clave de OpenAI API",
|
||||
"OpenAI API Key is required.": "La Clave de OpenAI API es requerida.",
|
||||
"or": "o",
|
||||
"Parameters": "Parametros",
|
||||
"Password": "Contraseña",
|
||||
"PDF Extract Images (OCR)": "Extraer imágenes de PDF (OCR)",
|
||||
"pending": "pendiente",
|
||||
"Permission denied when accessing microphone: {{error}}": "Permiso denegado al acceder al micrófono: {{error}}",
|
||||
"Playground": "Playground",
|
||||
"Profile": "Perfil",
|
||||
"Prompt Content": "Contenido del Prompt",
|
||||
"Prompt suggestions": "Sugerencias de Prompts",
|
||||
"Prompts": "Prompts",
|
||||
"Pull a model from Ollama.com": "Extraer un modelo de Ollama.com",
|
||||
"Pull Progress": "Progreso de extracción",
|
||||
"Query Params": "Parámetros de consulta",
|
||||
"RAG Template": "Plantilla de RAG",
|
||||
"Raw Format": "Formato en crudo",
|
||||
"Record voice": "Grabar voz",
|
||||
"Redirecting you to OpenWebUI Community": "Redireccionándote a la comunidad OpenWebUI",
|
||||
"Release Notes": "Notas de la versión",
|
||||
"Repeat Last N": "Repetir las últimas N",
|
||||
"Repeat Penalty": "Penalidad de repetición",
|
||||
"Request Mode": "Modo de petición",
|
||||
"Reset Vector Storage": "Restablecer almacenamiento vectorial",
|
||||
"Response AutoCopy to Clipboard": "Copiar respuesta automáticamente al portapapeles",
|
||||
"Role": "personalizados",
|
||||
"Rosé Pine": "Rosé Pine",
|
||||
"Rosé Pine Dawn": "Rosé Pine Dawn",
|
||||
"Save": "Guardar",
|
||||
"Save & Create": "Guardar y Crear",
|
||||
"Save & Submit": "Guardar y Enviar",
|
||||
"Save & Update": "Guardar y Actualizar",
|
||||
"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": "Ya no se admite guardar registros de chat directamente en el almacenamiento de su navegador. Tómese un momento para descargar y eliminar sus registros de chat haciendo clic en el botón a continuación. No te preocupes, puedes volver a importar fácilmente tus registros de chat al backend a través de",
|
||||
"Scan": "Escanear",
|
||||
"Scan complete!": "Escaneo completado!",
|
||||
"Scan for documents from {{path}}": "Escanear en busca de documentos desde {{path}}",
|
||||
"Search": "Buscar",
|
||||
"Search Documents": "Buscar Documentos",
|
||||
"Search Prompts": "Buscar Prompts",
|
||||
"See readme.md for instructions": "Vea el readme.md para instrucciones",
|
||||
"See what's new": "Ver qué hay de nuevo",
|
||||
"Seed": "Seed",
|
||||
"Select a mode": "Selecciona un modo",
|
||||
"Select a model": "Selecciona un modelo",
|
||||
"Select an Ollama instance": "Seleccione una instancia de Ollama",
|
||||
"Send a Message": "Enviar un Mensaje",
|
||||
"Send message": "Enviar Mensaje",
|
||||
"Server connection verified": "Conexión del servidor verificada",
|
||||
"Set as default": "Establecer por defecto",
|
||||
"Set Default Model": "Establecer modelo predeterminado",
|
||||
"Set Image Size": "Establecer tamaño de imagen",
|
||||
"Set Steps": "Establecer Pasos",
|
||||
"Set Title Auto-Generation Model": "Establecer modelo de generación automática de títulos",
|
||||
"Set Voice": "Establecer la voz",
|
||||
"Settings": "Configuración",
|
||||
"Settings saved successfully!": "Configuración guardada exitosamente!",
|
||||
"Share to OpenWebUI Community": "Compartir con la comunidad OpenWebUI",
|
||||
"short-summary": "resumen-corto",
|
||||
"Show": "Mostrar",
|
||||
"Show Additional Params": "Mostrar parámetros adicionales",
|
||||
"Show shortcuts": "Mostrar atajos",
|
||||
"sidebar": "barra lateral",
|
||||
"Sign in": "Iniciar sesión",
|
||||
"Sign Out": "Desconectar",
|
||||
"Sign up": "Inscribirse",
|
||||
"Speech recognition error: {{error}}": "Error de reconocimiento de voz: {{error}}",
|
||||
"Speech-to-Text Engine": "Motor de voz a texto",
|
||||
"SpeechRecognition API is not supported in this browser.": "La API SpeechRecognition no es compatible con este navegador.",
|
||||
"Stop Sequence": "Detener secuencia",
|
||||
"STT Settings": "Configuraciones de STT",
|
||||
"Submit": "Enviar",
|
||||
"Success": "Éxito",
|
||||
"Successfully updated.": "Actualizado exitosamente.",
|
||||
"Sync All": "Sincronizar todo",
|
||||
"System": "Sistema",
|
||||
"System Prompt": "Prompt del sistema",
|
||||
"Tags": "Etiquetas",
|
||||
"Temperature": "Temperatura",
|
||||
"Template": "Plantilla",
|
||||
"Text Completion": "Finalización de texto",
|
||||
"Text-to-Speech Engine": "Motor de texto a voz",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Tema",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Esto garantiza que sus valiosas conversaciones se guarden de forma segura en su base de datos en el backend. ¡Gracias!",
|
||||
"This setting does not sync across browsers or devices.": "Esta configuración no se sincroniza entre navegadores o dispositivos.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consejo: actualice varias variables consecutivamente presionando la tecla tab en la entrada del chat después de cada reemplazo.",
|
||||
"Title": "Título",
|
||||
"Title Auto-Generation": "Generación automática de títulos",
|
||||
"Title Generation Prompt": "Prompt de generación de título",
|
||||
"to": "para",
|
||||
"To access the available model names for downloading,": "Para acceder a los nombres de modelos disponibles para descargar,",
|
||||
"To access the GGUF models available for downloading,": "Para acceder a los modelos GGUF disponibles para descargar,",
|
||||
"to chat input.": "a la entrada del chat.",
|
||||
"Toggle settings": "Alternar configuración",
|
||||
"Toggle sidebar": "Alternar barra lateral",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "¿Problemas para acceder a Ollama?",
|
||||
"TTS Settings": "Configuración de TTS",
|
||||
"Type Hugging Face Resolve (Download) URL": "Type Hugging Face Resolve (Download) URL",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "¡UH oh! Hubo un problema al conectarse a {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tipo de archivo desconocido '{{file_type}}', pero se acepta y se trata como texto sin formato",
|
||||
"Update password": "Actualiza contraseña",
|
||||
"Upload a GGUF model": "Sube un modelo GGUF",
|
||||
"Upload files": "Subir archivos",
|
||||
"Upload Progress": "Progreso de carga",
|
||||
"URL Mode": "Modo de URL",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Utilice '#' en el prompt para cargar y seleccionar sus documentos.",
|
||||
"Use Gravatar": "Usar Gravatar",
|
||||
"user": "usuario",
|
||||
"User Permissions": "Permisos de usuario",
|
||||
"Users": "Usuarios",
|
||||
"Utilize": "Utilizar",
|
||||
"Valid time units:": "Unidades válidas de tiempo:",
|
||||
"variable": "variable",
|
||||
"variable to have them replaced with clipboard content.": "variable para reemplazarlos con el contenido del portapapeles.",
|
||||
"Version": "Version",
|
||||
"Web": "Web",
|
||||
"WebUI Add-ons": "WebUI Add-ons",
|
||||
"WebUI Settings": "Configuración del WebUI",
|
||||
"WebUI will make requests to": "WebUI realizará solicitudes a",
|
||||
"What’s New in": "Lo qué hay de nuevo en",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Cuando el historial está desactivado, los nuevos chats en este navegador no aparecerán en el historial de ninguno de sus dispositivos..",
|
||||
"Whisper (Local)": "Whisper (Local)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Escribe una sugerencia para un prompt (por ejemplo, ¿quién eres?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Escribe un resumen en 50 palabras que resuma [tema o palabra clave].",
|
||||
"You": "Usted",
|
||||
"You're a helpful assistant.": "Eres un asistente útil.",
|
||||
"You're now logged in.": "Ya has iniciado sesión."
|
||||
}
|
||||
363
src/lib/i18n/locales/fa-IR/translation.json
Normal file
363
src/lib/i18n/locales/fa-IR/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' یا '-1' برای غیر فعال کردن انقضا.",
|
||||
"(Beta)": "(بتا)",
|
||||
"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
|
||||
"(latest)": "(آخرین)",
|
||||
"{{modelName}} is thinking...": "{{modelName}} در حال فکر کردن است...",
|
||||
"{{webUIName}} Backend Required": "بکند {{webUIName}} نیاز است.",
|
||||
"a user": "یک کاربر",
|
||||
"About": "درباره",
|
||||
"Account": "حساب کاربری",
|
||||
"Action": "عمل",
|
||||
"Add a model": "اضافه کردن یک مدل",
|
||||
"Add a model tag name": "اضافه کردن یک نام تگ برای مدل",
|
||||
"Add a short description about what this modelfile does": "توضیح کوتاهی در مورد کاری که این فایل\u200cمدل انجام می دهد اضافه کنید",
|
||||
"Add a short title for this prompt": "یک عنوان کوتاه برای این درخواست اضافه کنید",
|
||||
"Add a tag": "اضافه کردن یک تگ",
|
||||
"Add Docs": "اضافه کردن اسناد",
|
||||
"Add Files": "اضافه کردن فایل\u200cها",
|
||||
"Add message": "اضافه کردن پیغام",
|
||||
"add tags": "اضافه کردن تگ\u200cها",
|
||||
"Adjusting these settings will apply changes universally to all users.": "با تنظیم این تنظیمات، تغییرات به طور کلی برای همه کاربران اعمال می شود.",
|
||||
"admin": "مدیر",
|
||||
"Admin Panel": "پنل مدیریت",
|
||||
"Admin Settings": "تنظیمات مدیریت",
|
||||
"Advanced Parameters": "پارامترهای پیشرفته",
|
||||
"all": "همه",
|
||||
"All Users": "همه کاربران",
|
||||
"Allow": "اجازه دادن",
|
||||
"Allow Chat Deletion": "اجازه حذف گپ",
|
||||
"alphanumeric characters and hyphens": "حروف الفبایی و خط فاصله",
|
||||
"Already have an account?": "از قبل حساب کاربری دارید؟",
|
||||
"an assistant": "یک دستیار",
|
||||
"and": "و",
|
||||
"API Base URL": "API Base URL",
|
||||
"API Key": "API Key",
|
||||
"API RPM": "API RPM",
|
||||
"are allowed - Activate this command by typing": "مجاز هستند - این دستور را با تایپ کردن این فعال کنید:",
|
||||
"Are you sure?": "آیا مطمئن هستید؟",
|
||||
"Audio": "صدا",
|
||||
"Auto-playback response": "پخش خودکار پاسخ ",
|
||||
"Auto-send input after 3 sec.": "به طور خودکار ورودی را پس از 3 ثانیه ارسال کن.",
|
||||
"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ",
|
||||
"AUTOMATIC1111 Base URL is required.": "به URL پایه AUTOMATIC1111 مورد نیاز است.",
|
||||
"available!": "در دسترس!",
|
||||
"Back": "بازگشت",
|
||||
"Builder Mode": "حالت سازنده",
|
||||
"Cancel": "لغو",
|
||||
"Categories": "دسته\u200cبندی\u200cها",
|
||||
"Change Password": "تغییر رمز عبور",
|
||||
"Chat": "گپ",
|
||||
"Chat History": "تاریخچه\u200cی گفتگو",
|
||||
"Chat History is off for this browser.": "سابقه گپ برای این مرورگر خاموش است.",
|
||||
"Chats": "گپ\u200cها",
|
||||
"Check Again": "چک مجدد",
|
||||
"Check for updates": "بررسی به\u200cروزرسانی",
|
||||
"Checking for updates...": "در حال بررسی برای به\u200cروزرسانی..",
|
||||
"Choose a model before saving...": "قبل از ذخیره یک مدل را انتخاب کنید...",
|
||||
"Chunk Overlap": "همپوشانی تکه",
|
||||
"Chunk Params": "پارامترهای تکه",
|
||||
"Chunk Size": "اندازه تکه",
|
||||
"Click here for help.": "برای کمک اینجا را کلیک کنید.",
|
||||
"Click here to check other modelfiles.": "برای بررسی سایر فایل\u200cهای مدل اینجا را کلیک کنید.",
|
||||
"Click here to select": "برای انتخاب اینجا کلیک کنید",
|
||||
"Click here to select documents.": "برای انتخاب اسناد اینجا را کلیک کنید.",
|
||||
"click here.": "اینجا کلیک کنید.",
|
||||
"Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.",
|
||||
"Close": "بسته",
|
||||
"Collection": "مجموعه",
|
||||
"Command": "دستور",
|
||||
"Confirm Password": "تایید رمز عبور",
|
||||
"Connections": "ارتباطات",
|
||||
"Content": "محتوا",
|
||||
"Context Length": "طول زمینه",
|
||||
"Conversation Mode": "حالت مکالمه",
|
||||
"Copy last code block": "کپی آخرین بلوک کد",
|
||||
"Copy last response": "کپی آخرین پاسخ",
|
||||
"Copying to clipboard was successful!": "کپی کردن در کلیپ بورد با موفقیت انجام شد!",
|
||||
"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':": "یک عبارت مختصر و ۳ تا ۵ کلمه ای را به عنوان سرفصل برای پرس و جو زیر ایجاد کنید، به شدت محدودیت ۳-۵ کلمه را رعایت کنید و از استفاده از کلمه 'عنوان' خودداری کنید:",
|
||||
"Create a modelfile": "ایجاد یک فایل مدل",
|
||||
"Create Account": "ساخت حساب کاربری",
|
||||
"Created at": "ایجاد شده در",
|
||||
"Created by": "ایجاد شده توسط",
|
||||
"Current Model": "مدل فعلی",
|
||||
"Current Password": "رمز عبور فعلی",
|
||||
"Custom": "دلخواه",
|
||||
"Customize Ollama models for a specific purpose": "مدل های اولاما را برای یک هدف خاص سفارشی کنید",
|
||||
"Dark": "تیره",
|
||||
"Database": "پایگاه داده",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "پیشفرض",
|
||||
"Default (Automatic1111)": "پیشفرض (Automatic1111)",
|
||||
"Default (Web API)": "پیشفرض (Web API)",
|
||||
"Default model updated": "مدل پیشفرض به\u200cروزرسانی شد",
|
||||
"Default Prompt Suggestions": "پیشنهادات پرامپت پیش فرض",
|
||||
"Default User Role": "نقش کاربر پیش فرض",
|
||||
"delete": "حذف",
|
||||
"Delete a model": "حذف یک مدل",
|
||||
"Delete chat": "حذف گپ",
|
||||
"Delete Chats": "حذف گپ\u200cها",
|
||||
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} پاک شد",
|
||||
"Deleted {tagName}": "{tagName} حذف شد",
|
||||
"Description": "توضیحات",
|
||||
"Desktop Notifications": "اعلان",
|
||||
"Disabled": "غیرفعال",
|
||||
"Discover a modelfile": "فایل مدل را کشف کنید",
|
||||
"Discover a prompt": "یک اعلان را کشف کنید",
|
||||
"Discover, download, and explore custom prompts": "پرامپت\u200cهای سفارشی را کشف، دانلود و کاوش کنید",
|
||||
"Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید",
|
||||
"Display the username instead of You in the Chat": "نمایش نام کاربری به جای «شما» در چت",
|
||||
"Document": "سند",
|
||||
"Document Settings": "تنظیمات سند",
|
||||
"Documents": "اسناد",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "هیچ اتصال خارجی ایجاد نمی کند و داده های شما به طور ایمن در سرور میزبان محلی شما باقی می ماند.",
|
||||
"Don't Allow": "اجازه نده",
|
||||
"Don't have an account?": "حساب کاربری ندارید؟",
|
||||
"Download as a File": "دانلود به صورت فایل",
|
||||
"Download Database": "دانلود پایگاه داده",
|
||||
"Drop any files here to add to the conversation": "هر فایلی را اینجا رها کنید تا به مکالمه اضافه شود",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "به طور مثال '30s','10m'. واحد\u200cهای زمانی معتبر 's', 'm', 'h' هستند.",
|
||||
"Edit Doc": "ویرایش سند",
|
||||
"Edit User": "ویرایش کاربر",
|
||||
"Email": "ایمیل",
|
||||
"Enable Chat History": "تاریخچه چت را فعال کنید",
|
||||
"Enable New Sign Ups": "فعال کردن ثبت نام\u200cهای جدید",
|
||||
"Enabled": "فعال",
|
||||
"Enter {{role}} message here": "پیام {{role}} را اینجا وارد کنید",
|
||||
"Enter API Key": "کلید API را وارد کنید",
|
||||
"Enter Chunk Overlap": "مقدار Chunk Overlap را وارد کنید",
|
||||
"Enter Chunk Size": "مقدار Chunk Size را وارد کنید",
|
||||
"Enter Image Size (e.g. 512x512)": "اندازه تصویر را وارد کنید (مثال: 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "URL پایه مربوط به LiteLLM API را وارد کنید (litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "کلید API مربوط به LiteLLM را وارد کنید (litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "RPM API مربوط به LiteLLM را وارد کنید (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "مدل مربوط به LiteLLM را وارد کنید (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "حداکثر تعداد توکن را وارد کنید (litellm_params.max_tokens)",
|
||||
"Enter model tag (e.g. {{modelTag}})": "تگ مدل را وارد کنید (مثلا {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "تعداد گام ها را وارد کنید (مثال: 50)",
|
||||
"Enter stop sequence": "توالی توقف را وارد کنید",
|
||||
"Enter Top K": "مقدار Top K را وارد کنید",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "مقدار URL را وارد کنید (مثال http://127.0.0.1:7860/)",
|
||||
"Enter Your Email": "ایمیل خود را وارد کنید",
|
||||
"Enter Your Full Name": "نام کامل خود را وارد کنید",
|
||||
"Enter Your Password": "رمز عبور خود را وارد کنید",
|
||||
"Experimental": "آزمایشی",
|
||||
"Export All Chats (All Users)": "اکسپورت از همه گپ\u200cها(همه کاربران)",
|
||||
"Export Chats": "اکسپورت از گپ\u200cها",
|
||||
"Export Documents Mapping": "اکسپورت از نگاشت اسناد",
|
||||
"Export Modelfiles": "اکسپورت از فایل\u200cهای مدل",
|
||||
"Export Prompts": "اکسپورت از پرامپت\u200cها",
|
||||
"Failed to read clipboard contents": "خواندن محتوای کلیپ بورد ناموفق بود",
|
||||
"File Mode": "حالت فایل",
|
||||
"File not found.": "فایل یافت نشد.",
|
||||
"Focus chat input": "فوکوس کردن ورودی گپ",
|
||||
"Format your variables using square brackets like this:": "متغیرهای خود را با استفاده از براکت مربع به شکل زیر قالب بندی کنید:",
|
||||
"From (Base Model)": "از (مدل پایه)",
|
||||
"Full Screen Mode": "حالت تمام صفحه",
|
||||
"General": "عمومی",
|
||||
"General Settings": "تنظیمات عمومی",
|
||||
"Hello, {{name}}": "سلام، {{name}}",
|
||||
"Hide": "پنهان",
|
||||
"Hide Additional Params": "پنهان کردن پارامترهای اضافه",
|
||||
"How can I help you today?": "امروز چطور می توانم کمک تان کنم؟",
|
||||
"Image Generation (Experimental)": "تولید تصویر (آزمایشی)",
|
||||
"Image Generation Engine": "موتور تولید تصویر",
|
||||
"Image Settings": "تنظیمات تصویر",
|
||||
"Images": "تصاویر",
|
||||
"Import Chats": "ایمپورت گپ\u200cها",
|
||||
"Import Documents Mapping": "ایمپورت نگاشت اسناد",
|
||||
"Import Modelfiles": "ایمپورت فایل\u200cهای مدل",
|
||||
"Import Prompts": "ایمپورت پرامپت\u200cها",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "فلگ `--api` را هنکام اجرای stable-diffusion-webui استفاده کنید.",
|
||||
"Interface": "رابط",
|
||||
"join our Discord for help.": "برای کمک به دیسکورد ما بپیوندید.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "JWT انقضای",
|
||||
"JWT Token": "JWT توکن",
|
||||
"Keep Alive": "Keep Alive",
|
||||
"Keyboard shortcuts": "میانبرهای صفحه کلید",
|
||||
"Language": "زبان",
|
||||
"Light": "روشن",
|
||||
"Listening...": "در حال گوش دادن...",
|
||||
"LLMs can make mistakes. Verify important information.": "مدل\u200cهای زبانی بزرگ می\u200cتوانند اشتباه کنند. اطلاعات مهم را راستی\u200cآزمایی کنید.",
|
||||
"Made by OpenWebUI Community": "ساخته شده توسط OpenWebUI Community",
|
||||
"Make sure to enclose them with": "مطمئن شوید که آنها را با این محصور کنید:",
|
||||
"Manage LiteLLM Models": "Manage LiteLLM Models",
|
||||
"Manage Models": "مدیریت مدل\u200cها",
|
||||
"Manage Ollama Models": "مدیریت مدل\u200cهای اولاما",
|
||||
"Max Tokens": "حداکثر توکن",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "حداکثر 3 مدل را می توان به طور همزمان دانلود کرد. لطفاً بعداً دوباره امتحان کنید.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
|
||||
"Model {{modelId}} not found": "مدل {{modelId}} یافت نشد",
|
||||
"Model {{modelName}} already exists.": "مدل {{modelName}} در حال حاضر وجود دارد.",
|
||||
"Model Name": "نام مدل",
|
||||
"Model not selected": "مدل انتخاب نشده",
|
||||
"Model Tag Name": "نام تگ مدل",
|
||||
"Model Whitelisting": "لیست سفید مدل",
|
||||
"Model(s) Whitelisted": "مدل در لیست سفید ثبت شد",
|
||||
"Modelfile": "فایل مدل",
|
||||
"Modelfile Advanced Settings": "تنظیمات پیشرفته فایل\u200cمدل",
|
||||
"Modelfile Content": "محتویات فایل مدل",
|
||||
"Modelfiles": "فایل\u200cهای مدل",
|
||||
"Models": "مدل\u200cها",
|
||||
"My Documents": "اسناد من",
|
||||
"My Modelfiles": "فایل\u200cهای مدل من",
|
||||
"My Prompts": "پرامپت\u200cهای من",
|
||||
"Name": "نام",
|
||||
"Name Tag": "نام تگ",
|
||||
"Name your modelfile": "فایل مدل را نام\u200cگذاری کنید",
|
||||
"New Chat": "گپ جدید",
|
||||
"New Password": "رمز عبور جدید",
|
||||
"Not sure what to add?": "مطمئن نیستید چه چیزی را اضافه کنید؟",
|
||||
"Not sure what to write? Switch to": "مطمئن نیستید چه بنویسید؟ تغییر به",
|
||||
"Off": "خاموش",
|
||||
"Okay, Let's Go!": "باشه، بزن بریم!",
|
||||
"Ollama Base URL": "URL پایه اولاما",
|
||||
"Ollama Version": "نسخه اولاما",
|
||||
"On": "روشن",
|
||||
"Only": "فقط",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "فقط کاراکترهای الفبایی و خط فاصله در رشته فرمان مجاز هستند.",
|
||||
"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.": "اوه! فایل های شما هنوز در فر پردازش هستند. ما آنها را کامل می پزیم. لطفا صبور باشید، به محض آماده شدن به شما اطلاع خواهیم داد.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "اوه! به نظر می رسد URL نامعتبر است. لطفاً دوباره بررسی کنید و دوباره امتحان کنید.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "اوه! شما از یک روش پشتیبانی نشده (فقط frontend) استفاده می کنید. لطفاً WebUI را از بکند اجرا کنید.",
|
||||
"Open": "باز",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "باز کردن گپ جدید",
|
||||
"OpenAI API": "OpenAI API",
|
||||
"OpenAI API Key": "کلید OpenAI API",
|
||||
"OpenAI API Key is required.": "مقدار کلید OpenAI API مورد نیاز است.",
|
||||
"or": "روشن",
|
||||
"Parameters": "پارامترها",
|
||||
"Password": "رمز عبور",
|
||||
"PDF Extract Images (OCR)": "استخراج تصاویر از PDF (OCR)",
|
||||
"pending": "در انتظار",
|
||||
"Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}",
|
||||
"Playground": "زمین بازی",
|
||||
"Profile": "پروفایل",
|
||||
"Prompt Content": "محتویات پرامپت",
|
||||
"Prompt suggestions": "پیشنهادات پرامپت",
|
||||
"Prompts": "پرامپت\u200cها",
|
||||
"Pull a model from Ollama.com": "دریافت یک مدل از Ollama.com",
|
||||
"Pull Progress": "پیشرفت دریافت",
|
||||
"Query Params": "پارامترهای پرس و جو",
|
||||
"RAG Template": "RAG الگوی",
|
||||
"Raw Format": "فرمت خام",
|
||||
"Record voice": "ضبط صدا",
|
||||
"Redirecting you to OpenWebUI Community": "در حال هدایت به OpenWebUI Community",
|
||||
"Release Notes": "یادداشت\u200cهای انتشار",
|
||||
"Repeat Last N": "Repeat Last N",
|
||||
"Repeat Penalty": "Repeat Penalty",
|
||||
"Request Mode": "حالت درخواست",
|
||||
"Reset Vector Storage": "بازنشانی ذخیره سازی برداری",
|
||||
"Response AutoCopy to Clipboard": "کپی خودکار پاسخ به کلیپ بورد",
|
||||
"Role": "نقش",
|
||||
"Rosé Pine": "Rosé Pine",
|
||||
"Rosé Pine Dawn": "Rosé Pine Dawn",
|
||||
"Save": "ذخیره",
|
||||
"Save & Create": "ذخیره و ایجاد",
|
||||
"Save & Submit": "ذخیره و ارسال",
|
||||
"Save & Update": "ذخیره و به\u200cروزرسانی",
|
||||
"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": "ذخیره گزارش\u200cهای چت مستقیماً در حافظه مرورگر شما دیگر پشتیبانی نمی\u200cشود. لطفاً با کلیک بر روی دکمه زیر، چند لحظه برای دانلود و حذف گزارش های چت خود وقت بگذارید. نگران نباشید، شما به راحتی می توانید گزارش های چت خود را از طریق بکند دوباره وارد کنید",
|
||||
"Scan": "اسکن",
|
||||
"Scan complete!": "اسکن کامل شد!",
|
||||
"Scan for documents from {{path}}": "اسکن اسناد از {{path}}",
|
||||
"Search": "جستجو",
|
||||
"Search Documents": "جستجوی اسناد",
|
||||
"Search Prompts": "جستجوی پرامپت\u200cها",
|
||||
"See readme.md for instructions": "برای مشاهده دستورالعمل\u200cها به readme.md مراجعه کنید",
|
||||
"See what's new": "ببینید موارد جدید چه بوده",
|
||||
"Seed": "Seed",
|
||||
"Select a mode": "یک حالت انتخاب کنید",
|
||||
"Select a model": "انتخاب یک مدل",
|
||||
"Select an Ollama instance": "انتخاب یک نمونه از اولاما",
|
||||
"Send a Message": "ارسال یک پیام",
|
||||
"Send message": "ارسال پیام",
|
||||
"Server connection verified": "اتصال سرور تأیید شد",
|
||||
"Set as default": "تنظیم به عنوان پیشفرض",
|
||||
"Set Default Model": "تنظیم مدل پیش فرض",
|
||||
"Set Image Size": "تنظیم اندازه تصویر",
|
||||
"Set Steps": "تنظیم گام\u200cها",
|
||||
"Set Title Auto-Generation Model": "تنظیم مدل تولید خودکار عنوان",
|
||||
"Set Voice": "تنظیم صدا",
|
||||
"Settings": "تنظیمات",
|
||||
"Settings saved successfully!": "تنظیمات با موفقیت ذخیره شد!",
|
||||
"Share to OpenWebUI Community": "اشتراک گذاری با OpenWebUI Community",
|
||||
"short-summary": "خلاصه کوتاه",
|
||||
"Show": "نمایش",
|
||||
"Show Additional Params": "نمایش پارامترهای اضافه",
|
||||
"Show shortcuts": "نمایش میانبرها",
|
||||
"sidebar": "نوار کناری",
|
||||
"Sign in": "ورود",
|
||||
"Sign Out": "خروج",
|
||||
"Sign up": "ثبت نام",
|
||||
"Speech recognition error: {{error}}": "خطای تشخیص گفتار: {{error}}",
|
||||
"Speech-to-Text Engine": "موتور گفتار به متن",
|
||||
"SpeechRecognition API is not supported in this browser.": "API تشخیص گفتار در این مرورگر پشتیبانی نمی شود.",
|
||||
"Stop Sequence": "توالی توقف",
|
||||
"STT Settings": "STT تنظیمات",
|
||||
"Submit": "ارسال",
|
||||
"Success": "موفقیت",
|
||||
"Successfully updated.": "با موفقیت به روز شد",
|
||||
"Sync All": "همگام سازی همه",
|
||||
"System": "سیستم",
|
||||
"System Prompt": "پرامپت سیستم",
|
||||
"Tags": "تگ\u200cها",
|
||||
"Temperature": "دما",
|
||||
"Template": "الگو",
|
||||
"Text Completion": "تکمیل متن",
|
||||
"Text-to-Speech Engine": "موتور تبدیل متن به گفتار",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "قالب",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "این تضمین می کند که مکالمات ارزشمند شما به طور ایمن در پایگاه داده بکند ذخیره می شود. تشکر!",
|
||||
"This setting does not sync across browsers or devices.": "این تنظیم در مرورگرها یا دستگاه\u200cها همگام\u200cسازی نمی\u200cشود.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "با فشردن کلید Tab در ورودی چت پس از هر بار تعویض، چندین متغیر را به صورت متوالی به روزرسانی کنید.",
|
||||
"Title": "عنوان",
|
||||
"Title Auto-Generation": "تولید خودکار عنوان",
|
||||
"Title Generation Prompt": "پرامپت تولید عنوان",
|
||||
"to": "به",
|
||||
"To access the available model names for downloading,": "برای دسترسی به نام مدل های موجود برای دانلود،",
|
||||
"To access the GGUF models available for downloading,": "برای دسترسی به مدل\u200cهای GGUF موجود برای دانلود،",
|
||||
"to chat input.": "در ورودی گپ.",
|
||||
"Toggle settings": "نمایش/عدم نمایش تنظیمات",
|
||||
"Toggle sidebar": "نمایش/عدم نمایش نوار کناری",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "در دسترسی به اولاما مشکل دارید؟",
|
||||
"TTS Settings": "تنظیمات TTS",
|
||||
"Type Hugging Face Resolve (Download) URL": "مقدار URL دانلود (Resolve) Hugging Face را وارد کنید",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "اوه اوه! مشکلی در اتصال به {{provider}} وجود داشت.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "نوع فایل '{{file_type}}' ناشناخته است، به عنوان یک فایل متنی ساده با آن برخورد می شود.",
|
||||
"Update password": "به روزرسانی رمزعبور",
|
||||
"Upload a GGUF model": "آپلود یک مدل GGUF",
|
||||
"Upload files": "بارگذاری فایل\u200cها",
|
||||
"Upload Progress": "پیشرفت آپلود",
|
||||
"URL Mode": "حالت URL",
|
||||
"Use '#' in the prompt input to load and select your documents.": "در پرامپت از '#' برای لود و انتخاب اسناد خود استفاده کنید.",
|
||||
"Use Gravatar": "استفاده از گراواتار",
|
||||
"user": "کاربر",
|
||||
"User Permissions": "مجوزهای کاربر",
|
||||
"Users": "کاربران",
|
||||
"Utilize": "استفاده کنید",
|
||||
"Valid time units:": "واحدهای زمانی معتبر:",
|
||||
"variable": "متغیر",
|
||||
"variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای کلیپ بورد.",
|
||||
"Version": "نسخه",
|
||||
"Web": "وب",
|
||||
"WebUI Add-ons": "WebUI افزونه\u200cهای",
|
||||
"WebUI Settings": "تنظیمات WebUI",
|
||||
"WebUI will make requests to": "WebUI درخواست\u200cها را ارسال خواهد کرد به",
|
||||
"What’s New in": "موارد جدید در",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "وقتی سابقه خاموش است، چت\u200cهای جدید در این مرورگر در سابقه شما در هیچ یک از دستگاه\u200cهایتان ظاهر نمی\u200cشوند.",
|
||||
"Whisper (Local)": "ویسپر (محلی)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "یک پیشنهاد پرامپت بنویسید (مثلاً شما کی هستید؟)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "خلاصه ای در 50 کلمه بنویسید که [موضوع یا کلمه کلیدی] را خلاصه کند.",
|
||||
"You": "شما",
|
||||
"You're a helpful assistant.": "تو یک دستیار سودمند هستی.",
|
||||
"You're now logged in.": "شما اکنون وارد شده\u200cاید."
|
||||
}
|
||||
363
src/lib/i18n/locales/fr-CA/translation.json
Normal file
363
src/lib/i18n/locales/fr-CA/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' ou '-1' pour aucune expiration.",
|
||||
"(Beta)": "(Bêta)",
|
||||
"(e.g. `sh webui.sh --api`)": "(par ex. `sh webui.sh --api`)",
|
||||
"(latest)": "",
|
||||
"{{modelName}} is thinking...": "{{modelName}} réfléchit...",
|
||||
"{{webUIName}} Backend Required": "Backend {{webUIName}} requis",
|
||||
"a user": "un utilisateur",
|
||||
"About": "À propos",
|
||||
"Account": "Compte",
|
||||
"Action": "Action",
|
||||
"Add a model": "Ajouter un modèle",
|
||||
"Add a model tag name": "Ajouter un nom de tag pour le modèle",
|
||||
"Add a short description about what this modelfile does": "Ajouter une courte description de ce que fait ce fichier de modèle",
|
||||
"Add a short title for this prompt": "Ajouter un court titre pour ce prompt",
|
||||
"Add a tag": "Ajouter un tag",
|
||||
"Add Docs": "Ajouter des documents",
|
||||
"Add Files": "Ajouter des fichiers",
|
||||
"Add message": "Ajouter un message",
|
||||
"add tags": "ajouter des tags",
|
||||
"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",
|
||||
"Admin Settings": "Paramètres d'administration",
|
||||
"Advanced Parameters": "Paramètres avancés",
|
||||
"all": "tous",
|
||||
"All Users": "Tous les utilisateurs",
|
||||
"Allow": "Autoriser",
|
||||
"Allow Chat Deletion": "Autoriser la suppression des discussions",
|
||||
"alphanumeric characters and hyphens": "caractères alphanumériques et tirets",
|
||||
"Already have an account?": "Vous avez déjà un compte ?",
|
||||
"an assistant": "un assistant",
|
||||
"and": "et",
|
||||
"API Base URL": "URL de base de l'API",
|
||||
"API Key": "Clé API",
|
||||
"API RPM": "RPM API",
|
||||
"are allowed - Activate this command by typing": "sont autorisés - Activez cette commande en tapant",
|
||||
"Are you sure?": "Êtes-vous sûr ?",
|
||||
"Audio": "Audio",
|
||||
"Auto-playback response": "Réponse en lecture automatique",
|
||||
"Auto-send input after 3 sec.": "Envoyer automatiquement l'entrée après 3 sec.",
|
||||
"AUTOMATIC1111 Base URL": "URL de base AUTOMATIC1111",
|
||||
"AUTOMATIC1111 Base URL is required.": "L'URL de base AUTOMATIC1111 est requise.",
|
||||
"available!": "disponible !",
|
||||
"Back": "Retour",
|
||||
"Builder Mode": "Mode Constructeur",
|
||||
"Cancel": "Annuler",
|
||||
"Categories": "Catégories",
|
||||
"Change Password": "Changer le mot de passe",
|
||||
"Chat": "Discussion",
|
||||
"Chat History": "Historique des discussions",
|
||||
"Chat History is off for this browser.": "L'historique des discussions est désactivé pour ce navigateur.",
|
||||
"Chats": "Discussions",
|
||||
"Check Again": "Vérifier à nouveau",
|
||||
"Check for updates": "Vérifier les mises à jour",
|
||||
"Checking for updates...": "Vérification des mises à jour...",
|
||||
"Choose a model before saving...": "Choisissez un modèle avant d'enregistrer...",
|
||||
"Chunk Overlap": "Chevauchement de bloc",
|
||||
"Chunk Params": "Paramètres de bloc",
|
||||
"Chunk Size": "Taille de bloc",
|
||||
"Click here for help.": "Cliquez ici pour de l'aide.",
|
||||
"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 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.",
|
||||
"Close": "Fermer",
|
||||
"Collection": "Collection",
|
||||
"Command": "Commande",
|
||||
"Confirm Password": "Confirmer le mot de passe",
|
||||
"Connections": "Connexions",
|
||||
"Content": "Contenu",
|
||||
"Context Length": "Longueur du contexte",
|
||||
"Conversation Mode": "Mode de conversation",
|
||||
"Copy last code block": "Copier le dernier bloc de code",
|
||||
"Copy last response": "Copier la dernière réponse",
|
||||
"Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !",
|
||||
"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':": "Créez une phrase concise de 3 à 5 mots comme en-tête pour la requête suivante, en respectant strictement la limite de 3 à 5 mots et en évitant l'utilisation du mot 'titre' :",
|
||||
"Create a modelfile": "Créer un fichier de modèle",
|
||||
"Create Account": "Créer un compte",
|
||||
"Created at": "Créé le",
|
||||
"Created by": "Créé par",
|
||||
"Current Model": "Modèle actuel",
|
||||
"Current Password": "Mot de passe actuel",
|
||||
"Custom": "Personnalisé",
|
||||
"Customize Ollama models for a specific purpose": "Personnaliser les modèles Ollama pour un objectif spécifique",
|
||||
"Dark": "Sombre",
|
||||
"Database": "Base de données",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "Par défaut",
|
||||
"Default (Automatic1111)": "Par défaut (Automatic1111)",
|
||||
"Default (Web API)": "Par défaut (API Web)",
|
||||
"Default model updated": "Modèle par défaut mis à jour",
|
||||
"Default Prompt Suggestions": "Suggestions de prompt par défaut",
|
||||
"Default User Role": "Rôle d'utilisateur par défaut",
|
||||
"delete": "supprimer",
|
||||
"Delete a model": "Supprimer un modèle",
|
||||
"Delete chat": "Supprimer la discussion",
|
||||
"Delete Chats": "Supprimer les discussions",
|
||||
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} supprimé",
|
||||
"Deleted {tagName}": "{tagName} supprimé",
|
||||
"Description": "Description",
|
||||
"Desktop Notifications": "Notifications de bureau",
|
||||
"Disabled": "Désactivé",
|
||||
"Discover a modelfile": "Découvrir un fichier de modèle",
|
||||
"Discover a prompt": "Découvrir un prompt",
|
||||
"Discover, download, and explore custom prompts": "Découvrir, télécharger et explorer des prompts personnalisés",
|
||||
"Discover, download, and explore model presets": "Découvrir, télécharger et explorer des préconfigurations de modèles",
|
||||
"Display the username instead of You in the Chat": "Afficher le nom d'utilisateur au lieu de 'Vous' dans la Discussion",
|
||||
"Document": "Document",
|
||||
"Document Settings": "Paramètres du document",
|
||||
"Documents": "Documents",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "ne fait aucune connexion externe, et vos données restent en sécurité sur votre serveur hébergé localement.",
|
||||
"Don't Allow": "Ne pas autoriser",
|
||||
"Don't have an account?": "Vous n'avez pas de compte ?",
|
||||
"Download as a File": "Télécharger en tant que fichier",
|
||||
"Download Database": "Télécharger la base de données",
|
||||
"Drop any files here to add to the conversation": "Déposez n'importe quel fichier ici pour les ajouter à la conversation",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s', '10m'. Les unités de temps valides sont 's', 'm', 'h'.",
|
||||
"Edit Doc": "Éditer le document",
|
||||
"Edit User": "Éditer l'utilisateur",
|
||||
"Email": "Email",
|
||||
"Enable Chat History": "Activer l'historique des discussions",
|
||||
"Enable New Sign Ups": "Activer les nouvelles inscriptions",
|
||||
"Enabled": "Activé",
|
||||
"Enter {{role}} message here": "Entrez le message {{role}} ici",
|
||||
"Enter API Key": "Entrez la clé API",
|
||||
"Enter Chunk Overlap": "Entrez le chevauchement de bloc",
|
||||
"Enter Chunk Size": "Entrez la taille du bloc",
|
||||
"Enter Image Size (e.g. 512x512)": "Entrez la taille de l'image (p. ex. 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Entrez l'URL de base de l'API LiteLLM (litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "Entrez la clé API LiteLLM (litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Entrez le RPM de l'API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Entrez le modèle LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Entrez le nombre max de tokens (litellm_params.max_tokens)",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Entrez le tag du modèle (p. ex. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (p. ex. 50)",
|
||||
"Enter stop sequence": "Entrez la séquence de fin",
|
||||
"Enter Top K": "Entrez Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Entrez l'URL (p. ex. http://127.0.0.1:7860/)",
|
||||
"Enter Your Email": "Entrez votre adresse email",
|
||||
"Enter Your Full Name": "Entrez votre nom complet",
|
||||
"Enter Your Password": "Entrez votre mot de passe",
|
||||
"Experimental": "Expérimental",
|
||||
"Export All Chats (All Users)": "Exporter toutes les discussions (Tous les utilisateurs)",
|
||||
"Export Chats": "Exporter les discussions",
|
||||
"Export Documents Mapping": "Exporter le mappage des documents",
|
||||
"Export Modelfiles": "Exporter les fichiers de modèle",
|
||||
"Export Prompts": "Exporter les prompts",
|
||||
"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
|
||||
"File Mode": "Mode fichier",
|
||||
"File not found.": "Fichier introuvable.",
|
||||
"Focus chat input": "Se concentrer sur l'entrée de la discussion",
|
||||
"Format your variables using square brackets like this:": "Formatez vos variables en utilisant des crochets comme ceci :",
|
||||
"From (Base Model)": "De (Modèle de base)",
|
||||
"Full Screen Mode": "Mode plein écran",
|
||||
"General": "Général",
|
||||
"General Settings": "Paramètres généraux",
|
||||
"Hello, {{name}}": "Bonjour, {{name}}",
|
||||
"Hide": "Cacher",
|
||||
"Hide Additional Params": "Cacher les paramètres supplémentaires",
|
||||
"How can I help you today?": "Comment puis-je vous aider aujourd'hui ?",
|
||||
"Image Generation (Experimental)": "Génération d'image (Expérimental)",
|
||||
"Image Generation Engine": "Moteur de génération d'image",
|
||||
"Image Settings": "Paramètres de l'image",
|
||||
"Images": "Images",
|
||||
"Import Chats": "Importer les discussions",
|
||||
"Import Documents Mapping": "Importer le mappage des documents",
|
||||
"Import Modelfiles": "Importer les fichiers de modèle",
|
||||
"Import Prompts": "Importer les prompts",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Inclure l'indicateur `--api` lors de l'exécution de stable-diffusion-webui",
|
||||
"Interface": "Interface",
|
||||
"join our Discord for help.": "rejoignez notre Discord pour obtenir de l'aide.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "Expiration du JWT",
|
||||
"JWT Token": "Jeton JWT",
|
||||
"Keep Alive": "Garder actif",
|
||||
"Keyboard shortcuts": "Raccourcis clavier",
|
||||
"Language": "Langue",
|
||||
"Light": "Lumière",
|
||||
"Listening...": "Écoute...",
|
||||
"LLMs can make mistakes. Verify important information.": "Les LLMs peuvent faire des erreurs. Vérifiez les informations importantes.",
|
||||
"Made by OpenWebUI Community": "Réalisé par la communauté OpenWebUI",
|
||||
"Make sure to enclose them with": "Assurez-vous de les entourer avec",
|
||||
"Manage LiteLLM Models": "Gérer les modèles LiteLLM",
|
||||
"Manage Models": "Gérer les modèles",
|
||||
"Manage Ollama Models": "Gérer les modèles Ollama",
|
||||
"Max Tokens": "Tokens maximaux",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Un maximum de 3 modèles peut être téléchargé simultanément. Veuillez réessayer plus tard.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
|
||||
"Model {{modelId}} not found": "Modèle {{modelId}} non trouvé",
|
||||
"Model {{modelName}} already exists.": "Le modèle {{modelName}} existe déjà.",
|
||||
"Model Name": "Nom du modèle",
|
||||
"Model not selected": "Modèle non sélectionné",
|
||||
"Model Tag Name": "Nom de tag du modèle",
|
||||
"Model Whitelisting": "Liste blanche de modèle",
|
||||
"Model(s) Whitelisted": "Modèle(s) sur liste blanche",
|
||||
"Modelfile": "Fichier de modèle",
|
||||
"Modelfile Advanced Settings": "Paramètres avancés du fichier de modèle",
|
||||
"Modelfile Content": "Contenu du fichier de modèle",
|
||||
"Modelfiles": "Fichiers de modèle",
|
||||
"Models": "Modèles",
|
||||
"My Documents": "Mes documents",
|
||||
"My Modelfiles": "Mes fichiers de modèle",
|
||||
"My Prompts": "Mes prompts",
|
||||
"Name": "Nom",
|
||||
"Name Tag": "Tag de nom",
|
||||
"Name your modelfile": "Nommez votre fichier de modèle",
|
||||
"New Chat": "Nouvelle discussion",
|
||||
"New Password": "Nouveau mot de passe",
|
||||
"Not sure what to add?": "Pas sûr de quoi ajouter ?",
|
||||
"Not sure what to write? Switch to": "Pas sûr de quoi écrire ? Changez pour",
|
||||
"Off": "Éteint",
|
||||
"Okay, Let's Go!": "Okay, Allons-y !",
|
||||
"Ollama Base URL": "URL de Base Ollama",
|
||||
"Ollama Version": "Version Ollama",
|
||||
"On": "Activé",
|
||||
"Only": "Seulement",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "Seuls les caractères alphanumériques et les tirets sont autorisés dans la chaîne de commande.",
|
||||
"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.": "Oups ! Tenez bon ! Vos fichiers sont encore dans le four de traitement. Nous les préparons jusqu'à la perfection. Soyez patient et nous vous informerons dès qu'ils seront prêts.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oups ! Il semble que l'URL soit invalide. Merci de vérifier et réessayer.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oups ! Vous utilisez une méthode non prise en charge (frontal uniquement). Veuillez servir WebUI depuis le backend.",
|
||||
"Open": "Ouvrir",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "Ouvrir une nouvelle discussion",
|
||||
"OpenAI API": "API OpenAI",
|
||||
"OpenAI API Key": "Clé API OpenAI",
|
||||
"OpenAI API Key is required.": "La clé API OpenAI est requise.",
|
||||
"or": "ou",
|
||||
"Parameters": "Paramètres",
|
||||
"Password": "Mot de passe",
|
||||
"PDF Extract Images (OCR)": "Extraction d'images PDF (OCR)",
|
||||
"pending": "en attente",
|
||||
"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}",
|
||||
"Playground": "Aire de jeu",
|
||||
"Profile": "Profil",
|
||||
"Prompt Content": "Contenu du prompt",
|
||||
"Prompt suggestions": "Suggestions de prompt",
|
||||
"Prompts": "Prompts",
|
||||
"Pull a model from Ollama.com": "Tirer un modèle de Ollama.com",
|
||||
"Pull Progress": "Progression du téléchargement",
|
||||
"Query Params": "Paramètres de requête",
|
||||
"RAG Template": "Modèle RAG",
|
||||
"Raw Format": "Format brut",
|
||||
"Record voice": "Enregistrer la voix",
|
||||
"Redirecting you to OpenWebUI Community": "Vous redirige vers la communauté OpenWebUI",
|
||||
"Release Notes": "Notes de version",
|
||||
"Repeat Last N": "Répéter les N derniers",
|
||||
"Repeat Penalty": "Pénalité de répétition",
|
||||
"Request Mode": "Mode de requête",
|
||||
"Reset Vector Storage": "Réinitialiser le stockage vectoriel",
|
||||
"Response AutoCopy to Clipboard": "Copie automatique de la réponse vers le presse-papiers",
|
||||
"Role": "Rôle",
|
||||
"Rosé Pine": "Pin Rosé",
|
||||
"Rosé Pine Dawn": "Aube Pin Rosé",
|
||||
"Save": "Enregistrer",
|
||||
"Save & Create": "Enregistrer & Créer",
|
||||
"Save & Submit": "Enregistrer & Soumettre",
|
||||
"Save & Update": "Enregistrer & Mettre à jour",
|
||||
"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": "La sauvegarde des journaux de discussion directement dans le stockage de votre navigateur n'est plus prise en charge. Veuillez prendre un moment pour télécharger et supprimer vos journaux de discussion en cliquant sur le bouton ci-dessous. Ne vous inquiétez pas, vous pouvez facilement réimporter vos journaux de discussion dans le backend via",
|
||||
"Scan": "Scanner",
|
||||
"Scan complete!": "Scan terminé !",
|
||||
"Scan for documents from {{path}}": "Scanner des documents depuis {{path}}",
|
||||
"Search": "Recherche",
|
||||
"Search Documents": "Rechercher des documents",
|
||||
"Search Prompts": "Rechercher des prompts",
|
||||
"See readme.md for instructions": "Voir readme.md pour les instructions",
|
||||
"See what's new": "Voir les nouveautés",
|
||||
"Seed": "Graine",
|
||||
"Select a mode": "Sélectionnez un mode",
|
||||
"Select a model": "Sélectionnez un modèle",
|
||||
"Select an Ollama instance": "Sélectionner une instance Ollama",
|
||||
"Send a Message": "Envoyer un message",
|
||||
"Send message": "Envoyer un message",
|
||||
"Server connection verified": "Connexion au serveur vérifiée",
|
||||
"Set as default": "Définir par défaut",
|
||||
"Set Default Model": "Définir le modèle par défaut",
|
||||
"Set Image Size": "Définir la taille de l'image",
|
||||
"Set Steps": "Définir les étapes",
|
||||
"Set Title Auto-Generation Model": "Définir le modèle de génération automatique de titre",
|
||||
"Set Voice": "Définir la voix",
|
||||
"Settings": "Paramètres",
|
||||
"Settings saved successfully!": "Paramètres enregistrés avec succès !",
|
||||
"Share to OpenWebUI Community": "Partager avec la communauté OpenWebUI",
|
||||
"short-summary": "résumé court",
|
||||
"Show": "Afficher",
|
||||
"Show Additional Params": "Afficher les paramètres supplémentaires",
|
||||
"Show shortcuts": "Afficher les raccourcis",
|
||||
"sidebar": "barre latérale",
|
||||
"Sign in": "Se connecter",
|
||||
"Sign Out": "Se déconnecter",
|
||||
"Sign up": "S'inscrire",
|
||||
"Speech recognition error: {{error}}": "Erreur de reconnaissance vocale : {{error}}",
|
||||
"Speech-to-Text Engine": "Moteur reconnaissance vocale",
|
||||
"SpeechRecognition API is not supported in this browser.": "L'API SpeechRecognition n'est pas prise en charge dans ce navigateur.",
|
||||
"Stop Sequence": "Séquence d'arrêt",
|
||||
"STT Settings": "Paramètres de STT",
|
||||
"Submit": "Soumettre",
|
||||
"Success": "Succès",
|
||||
"Successfully updated.": "Mis à jour avec succès.",
|
||||
"Sync All": "Synchroniser tout",
|
||||
"System": "Système",
|
||||
"System Prompt": "Prompt Système",
|
||||
"Tags": "Tags",
|
||||
"Temperature": "Température",
|
||||
"Template": "Modèle",
|
||||
"Text Completion": "Complétion de texte",
|
||||
"Text-to-Speech Engine": "Moteur de texte à la parole",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Thème",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos précieuses conversations sont enregistrées en toute sécurité dans votre base de données backend. Merci !",
|
||||
"This setting does not sync across browsers or devices.": "Ce réglage ne se synchronise pas entre les navigateurs ou les appareils.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Astuce : Mettez à jour plusieurs emplacements de variables consécutivement en appuyant sur la touche tabulation dans l'entrée de chat après chaque remplacement.",
|
||||
"Title": "Titre",
|
||||
"Title Auto-Generation": "Génération automatique de titre",
|
||||
"Title Generation Prompt": "Prompt de génération de titre",
|
||||
"to": "à",
|
||||
"To access the available model names for downloading,": "Pour accéder aux noms de modèles disponibles pour le téléchargement,",
|
||||
"To access the GGUF models available for downloading,": "Pour accéder aux modèles GGUF disponibles pour le téléchargement,",
|
||||
"to chat input.": "à l'entrée du chat.",
|
||||
"Toggle settings": "Basculer les paramètres",
|
||||
"Toggle sidebar": "Basculer la barre latérale",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "Des problèmes pour accéder à Ollama ?",
|
||||
"TTS Settings": "Paramètres TTS",
|
||||
"Type Hugging Face Resolve (Download) URL": "Entrez l'URL de résolution (téléchargement) Hugging Face",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh ! Il y a eu un problème de connexion à {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Type de fichier inconnu '{{file_type}}', mais accepté et traité comme du texte brut",
|
||||
"Update password": "Mettre à jour le mot de passe",
|
||||
"Upload a GGUF model": "Téléverser un modèle GGUF",
|
||||
"Upload files": "Téléverser des fichiers",
|
||||
"Upload Progress": "Progression du Téléversement",
|
||||
"URL Mode": "Mode URL",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Utilisez '#' dans l'entrée de prompt pour charger et sélectionner vos documents.",
|
||||
"Use Gravatar": "Utiliser Gravatar",
|
||||
"user": "utilisateur",
|
||||
"User Permissions": "Permissions de l'utilisateur",
|
||||
"Users": "Utilisateurs",
|
||||
"Utilize": "Utiliser",
|
||||
"Valid time units:": "Unités de temps valides :",
|
||||
"variable": "variable",
|
||||
"variable to have them replaced with clipboard content.": "variable pour les remplacer par le contenu du presse-papiers.",
|
||||
"Version": "Version",
|
||||
"Web": "Web",
|
||||
"WebUI Add-ons": "Add-ons WebUI",
|
||||
"WebUI Settings": "Paramètres WebUI",
|
||||
"WebUI will make requests to": "WebUI effectuera des demandes à",
|
||||
"What’s New in": "Quoi de neuf dans",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Lorsque l'historique est désactivé, les nouvelles discussions sur ce navigateur n'apparaîtront pas dans votre historique sur aucun de vos appareils.",
|
||||
"Whisper (Local)": "Whisper (Local)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Rédigez une suggestion de prompt (p. ex. Qui êtes-vous ?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Rédigez un résumé en 50 mots qui résume [sujet ou mot-clé].",
|
||||
"You": "You",
|
||||
"You're a helpful assistant.": "Vous êtes un assistant utile",
|
||||
"You're now logged in.": "Vous êtes maintenant connecté."
|
||||
}
|
||||
363
src/lib/i18n/locales/fr-FR/translation.json
Normal file
363
src/lib/i18n/locales/fr-FR/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' ou '-1' pour aucune expiration.",
|
||||
"(Beta)": "(Bêta)",
|
||||
"(e.g. `sh webui.sh --api`)": "(par ex. `sh webui.sh --api`)",
|
||||
"(latest)": "",
|
||||
"{{modelName}} is thinking...": "{{modelName}} réfléchit...",
|
||||
"{{webUIName}} Backend Required": "Backend {{webUIName}} requis",
|
||||
"a user": "un utilisateur",
|
||||
"About": "À propos",
|
||||
"Account": "Compte",
|
||||
"Action": "Action",
|
||||
"Add a model": "Ajouter un modèle",
|
||||
"Add a model tag name": "Ajouter un nom de tag pour le modèle",
|
||||
"Add a short description about what this modelfile does": "Ajouter une courte description de ce que fait ce fichier de modèle",
|
||||
"Add a short title for this prompt": "Ajouter un court titre pour ce prompt",
|
||||
"Add a tag": "Ajouter un tag",
|
||||
"Add Docs": "Ajouter des documents",
|
||||
"Add Files": "Ajouter des fichiers",
|
||||
"Add message": "Ajouter un message",
|
||||
"add tags": "ajouter des tags",
|
||||
"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",
|
||||
"Admin Settings": "Paramètres d'administration",
|
||||
"Advanced Parameters": "Paramètres avancés",
|
||||
"all": "tous",
|
||||
"All Users": "Tous les utilisateurs",
|
||||
"Allow": "Autoriser",
|
||||
"Allow Chat Deletion": "Autoriser la suppression du chat",
|
||||
"alphanumeric characters and hyphens": "caractères alphanumériques et tirets",
|
||||
"Already have an account?": "Vous avez déjà un compte ?",
|
||||
"an assistant": "un assistant",
|
||||
"and": "et",
|
||||
"API Base URL": "URL de base de l'API",
|
||||
"API Key": "Clé API",
|
||||
"API RPM": "RPM API",
|
||||
"are allowed - Activate this command by typing": "sont autorisés - Activez cette commande en tapant",
|
||||
"Are you sure?": "Êtes-vous sûr ?",
|
||||
"Audio": "Audio",
|
||||
"Auto-playback response": "Réponse en lecture automatique",
|
||||
"Auto-send input after 3 sec.": "Envoyer automatiquement l'entrée après 3 sec.",
|
||||
"AUTOMATIC1111 Base URL": "URL de base AUTOMATIC1111",
|
||||
"AUTOMATIC1111 Base URL is required.": "L'URL de base AUTOMATIC1111 est requise.",
|
||||
"available!": "disponible !",
|
||||
"Back": "Retour",
|
||||
"Builder Mode": "Mode Constructeur",
|
||||
"Cancel": "Annuler",
|
||||
"Categories": "Catégories",
|
||||
"Change Password": "Changer le mot de passe",
|
||||
"Chat": "Chat",
|
||||
"Chat History": "Historique du chat",
|
||||
"Chat History is off for this browser.": "L'historique du chat est désactivé pour ce navigateur.",
|
||||
"Chats": "Chats",
|
||||
"Check Again": "Vérifier à nouveau",
|
||||
"Check for updates": "Vérifier les mises à jour",
|
||||
"Checking for updates...": "Vérification des mises à jour...",
|
||||
"Choose a model before saving...": "Choisissez un modèle avant d'enregistrer...",
|
||||
"Chunk Overlap": "Chevauchement de bloc",
|
||||
"Chunk Params": "Paramètres de bloc",
|
||||
"Chunk Size": "Taille de bloc",
|
||||
"Click here for help.": "Cliquez ici pour de l'aide.",
|
||||
"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 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.",
|
||||
"Close": "Fermer",
|
||||
"Collection": "Collection",
|
||||
"Command": "Commande",
|
||||
"Confirm Password": "Confirmer le mot de passe",
|
||||
"Connections": "Connexions",
|
||||
"Content": "Contenu",
|
||||
"Context Length": "Longueur du contexte",
|
||||
"Conversation Mode": "Mode de conversation",
|
||||
"Copy last code block": "Copier le dernier bloc de code",
|
||||
"Copy last response": "Copier la dernière réponse",
|
||||
"Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !",
|
||||
"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':": "Créez une phrase concise de 3-5 mots comme en-tête pour la requête suivante, en respectant strictement la limite de 3-5 mots et en évitant l'utilisation du mot 'titre' :",
|
||||
"Create a modelfile": "Créer un fichier de modèle",
|
||||
"Create Account": "Créer un compte",
|
||||
"Created at": "Créé le",
|
||||
"Created by": "Créé par",
|
||||
"Current Model": "Modèle actuel",
|
||||
"Current Password": "Mot de passe actuel",
|
||||
"Custom": "Personnalisé",
|
||||
"Customize Ollama models for a specific purpose": "Personnaliser les modèles Ollama pour un objectif spécifique",
|
||||
"Dark": "Sombre",
|
||||
"Database": "Base de données",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "Par défaut",
|
||||
"Default (Automatic1111)": "Par défaut (Automatic1111)",
|
||||
"Default (Web API)": "Par défaut (API Web)",
|
||||
"Default model updated": "Modèle par défaut mis à jour",
|
||||
"Default Prompt Suggestions": "Suggestions de prompt par défaut",
|
||||
"Default User Role": "Rôle d'utilisateur par défaut",
|
||||
"delete": "supprimer",
|
||||
"Delete a model": "Supprimer un modèle",
|
||||
"Delete chat": "Supprimer le chat",
|
||||
"Delete Chats": "Supprimer les chats",
|
||||
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} supprimé",
|
||||
"Deleted {tagName}": "{tagName} supprimé",
|
||||
"Description": "Description",
|
||||
"Desktop Notifications": "Notifications de bureau",
|
||||
"Disabled": "Désactivé",
|
||||
"Discover a modelfile": "Découvrir un fichier de modèle",
|
||||
"Discover a prompt": "Découvrir un prompt",
|
||||
"Discover, download, and explore custom prompts": "Découvrir, télécharger et explorer des prompts personnalisés",
|
||||
"Discover, download, and explore model presets": "Découvrir, télécharger et explorer des préconfigurations de modèles",
|
||||
"Display the username instead of You in the Chat": "Afficher le nom d'utilisateur au lieu de 'Vous' dans le Chat",
|
||||
"Document": "Document",
|
||||
"Document Settings": "Paramètres du document",
|
||||
"Documents": "Documents",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "ne fait aucune connexion externe, et vos données restent en sécurité sur votre serveur hébergé localement.",
|
||||
"Don't Allow": "Ne pas autoriser",
|
||||
"Don't have an account?": "Vous n'avez pas de compte ?",
|
||||
"Download as a File": "Télécharger en tant que fichier",
|
||||
"Download Database": "Télécharger la base de données",
|
||||
"Drop any files here to add to the conversation": "Déposez des fichiers ici pour les ajouter à la conversation",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "par ex. '30s', '10m'. Les unités de temps valides sont 's', 'm', 'h'.",
|
||||
"Edit Doc": "Éditer le document",
|
||||
"Edit User": "Éditer l'utilisateur",
|
||||
"Email": "Email",
|
||||
"Enable Chat History": "Activer l'historique du chat",
|
||||
"Enable New Sign Ups": "Activer les nouvelles inscriptions",
|
||||
"Enabled": "Activé",
|
||||
"Enter {{role}} message here": "Entrez le message {{role}} ici",
|
||||
"Enter API Key": "Entrez la clé API",
|
||||
"Enter Chunk Overlap": "Entrez le chevauchement de bloc",
|
||||
"Enter Chunk Size": "Entrez la taille du bloc",
|
||||
"Enter Image Size (e.g. 512x512)": "Entrez la taille de l'image (p. ex. 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Entrez l'URL de base de l'API LiteLLM (litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "Entrez la clé API LiteLLM (litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Entrez le RPM de l'API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Entrez le modèle LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Entrez le nombre max de tokens (litellm_params.max_tokens)",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Entrez le tag du modèle (p. ex. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (p. ex. 50)",
|
||||
"Enter stop sequence": "Entrez la séquence de fin",
|
||||
"Enter Top K": "Entrez Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Entrez l'URL (p. ex. http://127.0.0.1:7860/)",
|
||||
"Enter Your Email": "Entrez votre email",
|
||||
"Enter Your Full Name": "Entrez votre nom complet",
|
||||
"Enter Your Password": "Entrez votre mot de passe",
|
||||
"Experimental": "Expérimental",
|
||||
"Export All Chats (All Users)": "Exporter tous les chats (tous les utilisateurs)",
|
||||
"Export Chats": "Exporter les chats",
|
||||
"Export Documents Mapping": "Exporter la correspondance des documents",
|
||||
"Export Modelfiles": "Exporter les fichiers de modèle",
|
||||
"Export Prompts": "Exporter les prompts",
|
||||
"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
|
||||
"File Mode": "Mode fichier",
|
||||
"File not found.": "Fichier non trouvé.",
|
||||
"Focus chat input": "Concentrer sur l'entrée du chat",
|
||||
"Format your variables using square brackets like this:": "Formatez vos variables en utilisant des crochets comme ceci :",
|
||||
"From (Base Model)": "De (Modèle de base)",
|
||||
"Full Screen Mode": "Mode plein écran",
|
||||
"General": "Général",
|
||||
"General Settings": "Paramètres généraux",
|
||||
"Hello, {{name}}": "Bonjour, {{name}}",
|
||||
"Hide": "Cacher",
|
||||
"Hide Additional Params": "Hide additional params",
|
||||
"How can I help you today?": "Comment puis-je vous aider aujourd'hui ?",
|
||||
"Image Generation (Experimental)": "Génération d'image (Expérimental)",
|
||||
"Image Generation Engine": "Moteur de génération d'image",
|
||||
"Image Settings": "Paramètres d'image",
|
||||
"Images": "Images",
|
||||
"Import Chats": "Importer les chats",
|
||||
"Import Documents Mapping": "Importer la correspondance des documents",
|
||||
"Import Modelfiles": "Importer les fichiers de modèle",
|
||||
"Import Prompts": "Importer les prompts",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Inclure le drapeau `--api` lors de l'exécution de stable-diffusion-webui",
|
||||
"Interface": "Interface",
|
||||
"join our Discord for help.": "rejoignez notre Discord pour obtenir de l'aide.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "Expiration JWT",
|
||||
"JWT Token": "Jeton JWT",
|
||||
"Keep Alive": "Garder en vie",
|
||||
"Keyboard shortcuts": "Raccourcis clavier",
|
||||
"Language": "Langue",
|
||||
"Light": "Clair",
|
||||
"Listening...": "Écoute...",
|
||||
"LLMs can make mistakes. Verify important information.": "Les LLMs peuvent faire des erreurs. Vérifiez les informations importantes.",
|
||||
"Made by OpenWebUI Community": "Réalisé par la communauté OpenWebUI",
|
||||
"Make sure to enclose them with": "Assurez-vous de les entourer avec",
|
||||
"Manage LiteLLM Models": "Gérer les modèles LiteLLM",
|
||||
"Manage Models": "Gérer les modèles",
|
||||
"Manage Ollama Models": "Gérer les modèles Ollama",
|
||||
"Max Tokens": "Tokens maximaux",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Un maximum de 3 modèles peut être téléchargé simultanément. Veuillez réessayer plus tard.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
|
||||
"Model {{modelId}} not found": "Modèle {{modelId}} non trouvé",
|
||||
"Model {{modelName}} already exists.": "Le modèle {{modelName}} existe déjà.",
|
||||
"Model Name": "Nom du modèle",
|
||||
"Model not selected": "Modèle non sélectionné",
|
||||
"Model Tag Name": "Nom de tag du modèle",
|
||||
"Model Whitelisting": "Liste blanche de modèle",
|
||||
"Model(s) Whitelisted": "Modèle(s) sur liste blanche",
|
||||
"Modelfile": "Fichier de modèle",
|
||||
"Modelfile Advanced Settings": "Paramètres avancés du fichier de modèle",
|
||||
"Modelfile Content": "Contenu du fichier de modèle",
|
||||
"Modelfiles": "Fichiers de modèle",
|
||||
"Models": "Modèles",
|
||||
"My Documents": "Mes documents",
|
||||
"My Modelfiles": "Mes fichiers de modèle",
|
||||
"My Prompts": "Mes prompts",
|
||||
"Name": "Nom",
|
||||
"Name Tag": "Tag de nom",
|
||||
"Name your modelfile": "Nommez votre fichier de modèle",
|
||||
"New Chat": "Nouveau chat",
|
||||
"New Password": "Nouveau mot de passe",
|
||||
"Not sure what to add?": "Vous ne savez pas quoi ajouter ?",
|
||||
"Not sure what to write? Switch to": "Vous ne savez pas quoi écrire ? Basculer vers",
|
||||
"Off": "Désactivé",
|
||||
"Okay, Let's Go!": "D'accord, allons-y !",
|
||||
"Ollama Base URL": "URL de Base Ollama",
|
||||
"Ollama Version": "Version Ollama",
|
||||
"On": "Activé",
|
||||
"Only": "Seulement",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "Seuls les caractères alphanumériques et les tirets sont autorisés dans la chaîne de commande.",
|
||||
"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.": "Oups ! Tenez bon ! Vos fichiers sont encore dans le four. Nous les cuisinons à la perfection. Soyez patient et nous vous informerons dès qu'ils seront prêts.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oops! Looks like the URL is invalid. Please double-check and try again.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.",
|
||||
"Open": "Ouvrir",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "Ouvrir un nouveau chat",
|
||||
"OpenAI API": "API OpenAI",
|
||||
"OpenAI API Key": "Clé API OpenAI",
|
||||
"OpenAI API Key is required.": "La clé API OpenAI est requise.",
|
||||
"or": "ou",
|
||||
"Parameters": "Paramètres",
|
||||
"Password": "Mot de passe",
|
||||
"PDF Extract Images (OCR)": "Extraction d'images PDF (OCR)",
|
||||
"pending": "en attente",
|
||||
"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}",
|
||||
"Playground": "Aire de jeu",
|
||||
"Profile": "Profil",
|
||||
"Prompt Content": "Contenu du prompt",
|
||||
"Prompt suggestions": "Suggestions de prompt",
|
||||
"Prompts": "Prompts",
|
||||
"Pull a model from Ollama.com": "Tirer un modèle de Ollama.com",
|
||||
"Pull Progress": "Progression du tirage",
|
||||
"Query Params": "Paramètres de requête",
|
||||
"RAG Template": "Modèle RAG",
|
||||
"Raw Format": "Format brut",
|
||||
"Record voice": "Enregistrer la voix",
|
||||
"Redirecting you to OpenWebUI Community": "Vous redirige vers la communauté OpenWebUI",
|
||||
"Release Notes": "Notes de version",
|
||||
"Repeat Last N": "Répéter les derniers N",
|
||||
"Repeat Penalty": "Pénalité de répétition",
|
||||
"Request Mode": "Mode de demande",
|
||||
"Reset Vector Storage": "Réinitialiser le stockage de vecteur",
|
||||
"Response AutoCopy to Clipboard": "Copie automatique de la réponse dans le presse-papiers",
|
||||
"Role": "Rôle",
|
||||
"Rosé Pine": "Pin Rosé",
|
||||
"Rosé Pine Dawn": "Aube Pin Rosé",
|
||||
"Save": "Enregistrer",
|
||||
"Save & Create": "Enregistrer & Créer",
|
||||
"Save & Submit": "Enregistrer & Soumettre",
|
||||
"Save & Update": "Enregistrer & Mettre à jour",
|
||||
"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": "La sauvegarde des chat directement dans le stockage de votre navigateur n'est plus prise en charge. Veuillez prendre un moment pour télécharger et supprimer vos journaux de chat en cliquant sur le bouton ci-dessous. Ne vous inquiétez pas, vous pouvez facilement importer vos sauvegardes de chat via",
|
||||
"Scan": "Scanner",
|
||||
"Scan complete!": "Scan terminé !",
|
||||
"Scan for documents from {{path}}": "Scanner des documents depuis {{path}}",
|
||||
"Search": "Recherche",
|
||||
"Search Documents": "Rechercher des documents",
|
||||
"Search Prompts": "Rechercher des prompts",
|
||||
"See readme.md for instructions": "Voir readme.md pour les instructions",
|
||||
"See what's new": "Voir les nouveautés",
|
||||
"Seed": "Graine",
|
||||
"Select a mode": "Sélectionnez un mode",
|
||||
"Select a model": "Sélectionner un modèle",
|
||||
"Select an Ollama instance": "Sélectionner une instance Ollama",
|
||||
"Send a Message": "Envoyer un message",
|
||||
"Send message": "Envoyer un message",
|
||||
"Server connection verified": "Connexion au serveur vérifiée",
|
||||
"Set as default": "Définir par défaut",
|
||||
"Set Default Model": "Définir le modèle par défaut",
|
||||
"Set Image Size": "Définir la taille de l'image",
|
||||
"Set Steps": "Définir les étapes",
|
||||
"Set Title Auto-Generation Model": "Définir le modèle de génération automatique de titre",
|
||||
"Set Voice": "Définir la voix",
|
||||
"Settings": "Paramètres",
|
||||
"Settings saved successfully!": "Paramètres enregistrés avec succès !",
|
||||
"Share to OpenWebUI Community": "Partager avec la communauté OpenWebUI",
|
||||
"short-summary": "résumé court",
|
||||
"Show": "Montrer",
|
||||
"Show Additional Params": "Afficher les paramètres supplémentaires",
|
||||
"Show shortcuts": "Afficher les raccourcis",
|
||||
"sidebar": "barre latérale",
|
||||
"Sign in": "Se connecter",
|
||||
"Sign Out": "Se déconnecter",
|
||||
"Sign up": "S'inscrire",
|
||||
"Speech recognition error: {{error}}": "Erreur de reconnaissance vocale : {{error}}",
|
||||
"Speech-to-Text Engine": "Moteur de reconnaissance vocale",
|
||||
"SpeechRecognition API is not supported in this browser.": "L'API SpeechRecognition n'est pas prise en charge dans ce navigateur.",
|
||||
"Stop Sequence": "Séquence d'arrêt",
|
||||
"STT Settings": "Paramètres STT",
|
||||
"Submit": "Soumettre",
|
||||
"Success": "Succès",
|
||||
"Successfully updated.": "Mis à jour avec succès.",
|
||||
"Sync All": "Synchroniser tout",
|
||||
"System": "Système",
|
||||
"System Prompt": "Invite de système",
|
||||
"Tags": "Tags",
|
||||
"Temperature": "Température",
|
||||
"Template": "Modèle",
|
||||
"Text Completion": "Complétion de texte",
|
||||
"Text-to-Speech Engine": "Moteur de synthèse vocale",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Thème",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos précieuses conversations sont en sécurité dans votre base de données. Merci !",
|
||||
"This setting does not sync across browsers or devices.": "Ce paramètre ne se synchronise pas entre les navigateurs ou les appareils.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.",
|
||||
"Title": "Titre",
|
||||
"Title Auto-Generation": "Génération automatique de titre",
|
||||
"Title Generation Prompt": "Prompt de génération de titre",
|
||||
"to": "à",
|
||||
"To access the available model names for downloading,": "Pour accéder aux noms de modèles disponibles pour le téléchargement,",
|
||||
"To access the GGUF models available for downloading,": "Pour accéder aux modèles GGUF disponibles pour le téléchargement,",
|
||||
"to chat input.": "à l'entrée du chat.",
|
||||
"Toggle settings": "Basculer les paramètres",
|
||||
"Toggle sidebar": "Basculer la barre latérale",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "Problèmes d'accès à Ollama ?",
|
||||
"TTS Settings": "Paramètres TTS",
|
||||
"Type Hugging Face Resolve (Download) URL": "Entrez l'URL de résolution (téléchargement) Hugging Face",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh ! Il y a eu un problème de connexion à {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Type de fichier inconnu '{{file_type}}', mais accepté et traité comme du texte brut",
|
||||
"Update password": "Mettre à jour le mot de passe",
|
||||
"Upload a GGUF model": "Téléverser un modèle GGUF",
|
||||
"Upload files": "Téléverser des fichiers",
|
||||
"Upload Progress": "Progression du Téléversement",
|
||||
"URL Mode": "Mode URL",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Utilisez '#' dans l'entrée du prompt pour charger et sélectionner vos documents.",
|
||||
"Use Gravatar": "Utiliser Gravatar",
|
||||
"user": "utilisateur",
|
||||
"User Permissions": "Permissions d'utilisateur",
|
||||
"Users": "Utilisateurs",
|
||||
"Utilize": "Utiliser",
|
||||
"Valid time units:": "Unités de temps valides :",
|
||||
"variable": "variable",
|
||||
"variable to have them replaced with clipboard content.": "variable pour les remplacer par le contenu du presse-papiers.",
|
||||
"Version": "Version",
|
||||
"Web": "Web",
|
||||
"WebUI Add-ons": "Add-ons WebUI",
|
||||
"WebUI Settings": "Paramètres WebUI",
|
||||
"WebUI will make requests to": "WebUI effectuera des demandes à",
|
||||
"What’s New in": "Quoi de neuf dans",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Lorsque l'historique est désactivé, les nouveaux chats sur ce navigateur n'apparaîtront pas dans votre historique sur aucun de vos appareils.",
|
||||
"Whisper (Local)": "Whisper (Local)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Écrivez un prompt (e.x. Qui est-tu ?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Ecrivez un résumé en 50 mots [sujet ou mot-clé]",
|
||||
"You": "You",
|
||||
"You're a helpful assistant.": "Vous êtes un assistant utile",
|
||||
"You're now logged in.": "Vous êtes maintenant connecté."
|
||||
}
|
||||
50
src/lib/i18n/locales/languages.json
Normal file
50
src/lib/i18n/locales/languages.json
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
[
|
||||
{
|
||||
"code": "en-US",
|
||||
"title": "English (US)"
|
||||
},
|
||||
{
|
||||
"code": "ca-ES",
|
||||
"title": "Catalan"
|
||||
},
|
||||
{
|
||||
"code": "de-DE",
|
||||
"title": "Deutsch"
|
||||
},
|
||||
{
|
||||
"code": "es-ES",
|
||||
"title": "Spanish"
|
||||
},
|
||||
{
|
||||
"code": "fa-IR",
|
||||
"title": "فارسی (Farsi)"
|
||||
},
|
||||
{
|
||||
"code": "fr-CA",
|
||||
"title": "French (Canada)"
|
||||
},
|
||||
{
|
||||
"code": "fr-FR",
|
||||
"title": "French (France)"
|
||||
},
|
||||
{
|
||||
"code": "ru-RU",
|
||||
"title": "Russian (Russia)"
|
||||
},
|
||||
{
|
||||
"code": "uk-UA",
|
||||
"title": "Ukrainian"
|
||||
},
|
||||
{
|
||||
"code": "vi-VN",
|
||||
"title": "Tiếng Việt"
|
||||
},
|
||||
{
|
||||
"code": "zh-CN",
|
||||
"title": "Chinese (Simplified)"
|
||||
},
|
||||
{
|
||||
"code": "zh-TW",
|
||||
"title": "Chinese (Traditional)"
|
||||
}
|
||||
]
|
||||
363
src/lib/i18n/locales/ru-RU/translation.json
Normal file
363
src/lib/i18n/locales/ru-RU/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' или '-1' для не истечение.",
|
||||
"(Beta)": "(бета)",
|
||||
"(e.g. `sh webui.sh --api`)": "(например: `sh webui.sh --api`)",
|
||||
"(latest)": "(новый)",
|
||||
"{{modelName}} is thinking...": "{{modelName}} это думает...",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} бэкенд требуемый",
|
||||
"a user": "юзер",
|
||||
"About": "Относительно",
|
||||
"Account": "Аккаунт",
|
||||
"Action": "Действие",
|
||||
"Add a model": "Добавьте модель",
|
||||
"Add a model tag name": "Добавьте тэг модели имя",
|
||||
"Add a short description about what this modelfile does": "Добавьте краткое описание, что делает этот моделифайл",
|
||||
"Add a short title for this prompt": "Добавьте краткое название для этого взаимодействия",
|
||||
"Add a tag": "Добавьте тэг",
|
||||
"Add Docs": "Добавьте документы",
|
||||
"Add Files": "Добавьте файлы",
|
||||
"Add message": "Добавьте message",
|
||||
"add tags": "Добавьте тэгы",
|
||||
"Adjusting these settings will apply changes universally to all users.": "Регулирующий этих настроек приведет к изменениям для все юзеры.",
|
||||
"admin": "админ",
|
||||
"Admin Panel": "Панель админ",
|
||||
"Admin Settings": "Настройки админ",
|
||||
"Advanced Parameters": "Расширенные Параметры",
|
||||
"all": "всё",
|
||||
"All Users": "Всё юзеры",
|
||||
"Allow": "Дозволять",
|
||||
"Allow Chat Deletion": "Дозволять удаление чат",
|
||||
"alphanumeric characters and hyphens": "буквенно цифровые символы и дефисы",
|
||||
"Already have an account?": "у вас есть аккаунт уже?",
|
||||
"an assistant": "ассистент",
|
||||
"and": "и",
|
||||
"API Base URL": "Базовый адрес API",
|
||||
"API Key": "Ключ API",
|
||||
"API RPM": "API RPM",
|
||||
"are allowed - Activate this command by typing": "разрешено - активируйте эту команду набором",
|
||||
"Are you sure?": "Вы уверены?",
|
||||
"Audio": "Аудио",
|
||||
"Auto-playback response": "Автоматическое воспроизведение ответа",
|
||||
"Auto-send input after 3 sec.": "Автоматическая отправка ввода через 3 секунды.",
|
||||
"AUTOMATIC1111 Base URL": "Базовый адрес URL AUTOMATIC1111",
|
||||
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Необходима базовый адрес URL.",
|
||||
"available!": "доступный!",
|
||||
"Back": "Назад",
|
||||
"Builder Mode": "Режим конструктор",
|
||||
"Cancel": "Аннулировать",
|
||||
"Categories": "Категории",
|
||||
"Change Password": "Изменить пароль",
|
||||
"Chat": "Чат",
|
||||
"Chat History": "История чат",
|
||||
"Chat History is off for this browser.": "История чат отключен для этого браузера.",
|
||||
"Chats": "Чаты",
|
||||
"Check Again": "Перепроверять",
|
||||
"Check for updates": "Проверить обновления",
|
||||
"Checking for updates...": "Проверка обновлений...",
|
||||
"Choose a model before saving...": "Выберите модель перед сохранением...",
|
||||
"Chunk Overlap": "Перекрытие фрагментов",
|
||||
"Chunk Params": "Параметры фрагментов",
|
||||
"Chunk Size": "Размер фрагмента",
|
||||
"Click here for help.": "Нажмите здесь для помощь.",
|
||||
"Click here to check other modelfiles.": "Нажмите тут чтобы проверить другие файлы моделей.",
|
||||
"Click here to select": "Нажмите тут чтобы выберите",
|
||||
"Click here to select documents.": "Нажмите здесь чтобы выберите документы.",
|
||||
"click here.": "нажмите здесь.",
|
||||
"Click on the user role button to change a user's role.": "Нажмите кнопку роли пользователя чтобы изменить роль пользователя.",
|
||||
"Close": "Закрывать",
|
||||
"Collection": "Коллекция",
|
||||
"Command": "Команда",
|
||||
"Confirm Password": "Подтвердите пароль",
|
||||
"Connections": "Соединение",
|
||||
"Content": "Содержание",
|
||||
"Context Length": "Длина контексту",
|
||||
"Conversation Mode": "Режим разговора",
|
||||
"Copy last code block": "Копировать последний блок кода",
|
||||
"Copy last response": "Копировать последний ответ",
|
||||
"Copying to clipboard was successful!": "Копирование в буфер обмена прошло успешно!",
|
||||
"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'": "Создайте краткую, 3-5 словную фразу в качестве заголовка для следующего запроса, строго соблюдая ограничение в 3-5 слов и избегая использования слова 'заголовок'",
|
||||
"Create a modelfile": "Создать модельный файл",
|
||||
"Create Account": "Создать аккаунт",
|
||||
"Created at": "Создано в",
|
||||
"Created by": "Создано",
|
||||
"Current Model": "Текущая модель",
|
||||
"Current Password": "Текущий пароль",
|
||||
"Custom": "Пользовательский",
|
||||
"Customize Ollama models for a specific purpose": "Настроить модели Ollama для конкретной цели",
|
||||
"Dark": "Тёмный",
|
||||
"Database": "База данных",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "По умолчанию",
|
||||
"Default (Automatic1111)": "По умолчанию (Автоматический1111)",
|
||||
"Default (Web API)": "По умолчанию (Web API)",
|
||||
"Default model updated": "Модель по умолчанию обновлена",
|
||||
"Default Prompt Suggestions": "Предложения промтов по умолчанию",
|
||||
"Default User Role": "Роль пользователя по умолчанию",
|
||||
"delete": "удалить",
|
||||
"Delete a model": "Удалить модель",
|
||||
"Delete chat": "Удалить чат",
|
||||
"Delete Chats": "Удалить чаты",
|
||||
"Deleted {{deleteModelTag}}": "Удалено {{deleteModelTag}}",
|
||||
"Deleted {tagName}": "Удалено {tagName}",
|
||||
"Description": "Описание",
|
||||
"Desktop Notifications": "Уведомления на рабочем столе",
|
||||
"Disabled": "Отключено",
|
||||
"Discover a modelfile": "Найти файл модели",
|
||||
"Discover a prompt": "Найти промт",
|
||||
"Discover, download, and explore custom prompts": "Находите, загружайте и исследуйте настраиваемые промты",
|
||||
"Discover, download, and explore model presets": "Находите, загружайте и исследуйте предустановки модели",
|
||||
"Display the username instead of You in the Chat": "Отображать имя пользователя вместо 'Вы' в чате",
|
||||
"Document": "Документ",
|
||||
"Document Settings": "Настройки документа",
|
||||
"Documents": "Документы",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "не устанавливает никаких внешних соединений, и ваши данные остаются безопасно на вашем локальном сервере.",
|
||||
"Don't Allow": "Не разрешать",
|
||||
"Don't have an account?": "у вас не есть аккаунт?",
|
||||
"Download as a File": "Загрузить как файл",
|
||||
"Download Database": "Загрузить базу данных",
|
||||
"Drop any files here to add to the conversation": "Перетащите сюда файлы, чтобы добавить их в разговор",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "например, '30с','10м'. Допустимые единицы времени: 'с', 'м', 'ч'.",
|
||||
"Edit Doc": "Редактировать документ",
|
||||
"Edit User": "Редактировать пользователя",
|
||||
"Email": "Электронная почта",
|
||||
"Enable Chat History": "Включить историю чата",
|
||||
"Enable New Sign Ups": "Разрешить новые регистрации",
|
||||
"Enabled": "Включено",
|
||||
"Enter {{role}} message here": "Введите сообщение {{role}} здесь",
|
||||
"Enter API Key": "Введите ключ API",
|
||||
"Enter Chunk Overlap": "Введите перекрытие фрагмента",
|
||||
"Enter Chunk Size": "Введите размер фрагмента",
|
||||
"Enter Image Size (e.g. 512x512)": "Введите размер изображения (например, 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Введите базовый URL API LiteLLM (litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "Введите ключ API LiteLLM (litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Введите RPM API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Введите модель LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Введите максимальное количество токенов (litellm_params.max_tokens)",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Введите тег модели (например, {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Введите количество шагов (например, 50)",
|
||||
"Enter stop sequence": "Введите последовательность остановки",
|
||||
"Enter Top K": "Введите Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Введите URL-адрес (например, http://127.0.0.1:7860/)",
|
||||
"Enter Your Email": "Введите вашу электронную почту",
|
||||
"Enter Your Full Name": "Введите ваше полное имя",
|
||||
"Enter Your Password": "Введите ваш пароль",
|
||||
"Experimental": "Экспериментальное",
|
||||
"Export All Chats (All Users)": "Экспортировать все чаты (все пользователи)",
|
||||
"Export Chats": "Экспортировать чаты",
|
||||
"Export Documents Mapping": "Экспортировать отображение документов",
|
||||
"Export Modelfiles": "Экспортировать файлы модели",
|
||||
"Export Prompts": "Экспортировать промты",
|
||||
"Failed to read clipboard contents": "Не удалось прочитать содержимое буфера обмена",
|
||||
"File Mode": "Режим файла",
|
||||
"File not found.": "Файл не найден.",
|
||||
"Focus chat input": "Фокус ввода чата",
|
||||
"Format your variables using square brackets like this:": "Форматируйте ваши переменные, используя квадратные скобки, как здесь:",
|
||||
"From (Base Model)": "Из (базой модель)",
|
||||
"Full Screen Mode": "Полноэкранный режим",
|
||||
"General": "Общее",
|
||||
"General Settings": "Общие настройки",
|
||||
"Hello, {{name}}": "Привет, {{name}}",
|
||||
"Hide": "Скрыть",
|
||||
"Hide Additional Params": "Скрыть дополнительные параметры",
|
||||
"How can I help you today?": "Чем я могу помочь вам сегодня?",
|
||||
"Image Generation (Experimental)": "Генерация изображений (Экспериментально)",
|
||||
"Image Generation Engine": "Механизм генерации изображений",
|
||||
"Image Settings": "Настройки изображения",
|
||||
"Images": "Изображения",
|
||||
"Import Chats": "Импорт чатов",
|
||||
"Import Documents Mapping": "Импорт сопоставления документов",
|
||||
"Import Modelfiles": "Импорт файлов модели",
|
||||
"Import Prompts": "Импорт подсказок",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Добавьте флаг `--api` при запуске stable-diffusion-webui",
|
||||
"Interface": "Интерфейс",
|
||||
"join our Discord for help.": "присоединяйтесь к нашему Discord для помощи.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "Истечение срока JWT",
|
||||
"JWT Token": "Токен JWT",
|
||||
"Keep Alive": "Поддерживать активность",
|
||||
"Keyboard shortcuts": "Горячие клавиши",
|
||||
"Language": "Язык",
|
||||
"Light": "Светлый",
|
||||
"Listening...": "Слушаю...",
|
||||
"LLMs can make mistakes. Verify important information.": "LLMs могут допускать ошибки. Проверяйте важную информацию.",
|
||||
"Made by OpenWebUI Community": "Сделано сообществом OpenWebUI",
|
||||
"Make sure to enclose them with": "Убедитесь, что они заключены в",
|
||||
"Manage LiteLLM Models": "Управление моделями LiteLLM",
|
||||
"Manage Models": "Управление моделями",
|
||||
"Manage Ollama Models": "Управление моделями Ollama",
|
||||
"Max Tokens": "Максимальное количество токенов",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимальное количество моделей для загрузки одновременно - 3. Пожалуйста, попробуйте позже.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "DD MMMM YYYY г.",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "Модель '{{modelName}}' успешно загружена.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' уже находится в очереди на загрузку.",
|
||||
"Model {{modelId}} not found": "Модель {{modelId}} не найдена",
|
||||
"Model {{modelName}} already exists.": "Модель {{modelName}} уже существует.",
|
||||
"Model Name": "Имя модели",
|
||||
"Model not selected": "Модель не выбрана",
|
||||
"Model Tag Name": "Имя тега модели",
|
||||
"Model Whitelisting": "Включение модели в белый список",
|
||||
"Model(s) Whitelisted": "Модель(и) включены в белый список",
|
||||
"Modelfile": "Файл модели",
|
||||
"Modelfile Advanced Settings": "Дополнительные настройки файла модели",
|
||||
"Modelfile Content": "Содержимое файла модели",
|
||||
"Modelfiles": "Файлы моделей",
|
||||
"Models": "Модели",
|
||||
"My Documents": "Мои документы",
|
||||
"My Modelfiles": "Мои файлы моделей",
|
||||
"My Prompts": "Мои подсказки",
|
||||
"Name": "Имя",
|
||||
"Name Tag": "Имя тега",
|
||||
"Name your modelfile": "Назовите свой файл модели",
|
||||
"New Chat": "Новый чат",
|
||||
"New Password": "Новый пароль",
|
||||
"Not sure what to add?": "Не уверены, что добавить?",
|
||||
"Not sure what to write? Switch to": "Не уверены, что написать? Переключитесь на",
|
||||
"Off": "Выключено.",
|
||||
"Okay, Let's Go!": "Давайте начнём!",
|
||||
"Ollama Base URL": "Базовый адрес URL Ollama",
|
||||
"Ollama Version": "Версия Ollama",
|
||||
"On": "Включено.",
|
||||
"Only": "Только",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "В строке команды разрешено использовать только буквенно-цифровые символы и дефисы.",
|
||||
"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.": "Упс! Зажмите пояса! Ваши файлы все еще в процессе обработки. Мы готовим их до идеального состояния. Пожалуйста, будьте терпеливы, и мы сообщим вам, когда они будут готовы.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Упс! Похоже, что URL-адрес недействителен. Пожалуйста, перепроверьте и попробуйте еще раз.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Упс! Вы используете неподдерживаемый метод (только фронтенд). Пожалуйста, обслуживайте веб-интерфейс из бэкенда.",
|
||||
"Open": "Открыть",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "Открыть новый чат",
|
||||
"OpenAI API": "API OpenAI",
|
||||
"OpenAI API Key": "Ключ API OpenAI",
|
||||
"OpenAI API Key is required.": "Требуется ключ API OpenAI.",
|
||||
"or": "или",
|
||||
"Parameters": "Параметры",
|
||||
"Password": "Пароль",
|
||||
"PDF Extract Images (OCR)": "Извлечение изображений из PDF (OCR)",
|
||||
"pending": "ожидание",
|
||||
"Permission denied when accessing microphone: {{error}}": "Отказано в доступе к микрофону: {{error}}",
|
||||
"Playground": "Площадка",
|
||||
"Profile": "Профиль",
|
||||
"Prompt Content": "Содержание промпта",
|
||||
"Prompt suggestions": "Предложения промптов",
|
||||
"Prompts": "Промпты",
|
||||
"Pull a model from Ollama.com": "Загрузить модель с Ollama.com",
|
||||
"Pull Progress": "Прогресс загрузки",
|
||||
"Query Params": "Параметры запроса",
|
||||
"RAG Template": "Шаблон RAG",
|
||||
"Raw Format": "Сырой формат",
|
||||
"Record voice": "Записать голос",
|
||||
"Redirecting you to OpenWebUI Community": "Перенаправляем вас в сообщество OpenWebUI",
|
||||
"Release Notes": "Примечания к выпуску",
|
||||
"Repeat Last N": "Повторить последние N",
|
||||
"Repeat Penalty": "Штраф за повтор",
|
||||
"Request Mode": "Режим запроса",
|
||||
"Reset Vector Storage": "Сбросить векторное хранилище",
|
||||
"Response AutoCopy to Clipboard": "Автоматическое копирование ответа в буфер обмена",
|
||||
"Role": "Роль",
|
||||
"Rosé Pine": "Розовое сосновое дерево",
|
||||
"Rosé Pine Dawn": "Розовое сосновое дерево рассвет",
|
||||
"Save": "Сохранить",
|
||||
"Save & Create": "Сохранить и создать",
|
||||
"Save & Submit": "Сохранить и отправить",
|
||||
"Save & Update": "Сохранить и обновить",
|
||||
"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": "Прямое сохранение журналов чата в хранилище вашего браузера больше не поддерживается. Пожалуйста, потратьте минуту, чтобы скачать и удалить ваши журналы чата, нажав на кнопку ниже. Не волнуйтесь, вы легко сможете повторно импортировать свои журналы чата в бэкенд через",
|
||||
"Scan": "Сканировать",
|
||||
"Scan complete!": "Сканирование завершено!",
|
||||
"Scan for documents from {{path}}": "Сканирование документов из {{path}}",
|
||||
"Search": "Поиск",
|
||||
"Search Documents": "Поиск документов",
|
||||
"Search Prompts": "Поиск промтов",
|
||||
"See readme.md for instructions": "Смотрите readme.md для инструкций",
|
||||
"See what's new": "Посмотреть, что нового",
|
||||
"Seed": "Сид",
|
||||
"Select a mode": "Выберите режим",
|
||||
"Select a model": "Выберите модель",
|
||||
"Select an Ollama instance": "Выберите экземпляр Ollama",
|
||||
"Send a Message": "Отправить сообщение",
|
||||
"Send message": "Отправить сообщение",
|
||||
"Server connection verified": "Соединение с сервером проверено",
|
||||
"Set as default": "Установить по умолчанию",
|
||||
"Set Default Model": "Установить модель по умолчанию",
|
||||
"Set Image Size": "Установить размер изображения",
|
||||
"Set Steps": "Установить шаги",
|
||||
"Set Title Auto-Generation Model": "Установить модель автогенерации заголовков",
|
||||
"Set Voice": "Установить голос",
|
||||
"Settings": "Настройки",
|
||||
"Settings saved successfully!": "Настройки успешно сохранены!",
|
||||
"Share to OpenWebUI Community": "Поделиться с сообществом OpenWebUI",
|
||||
"short-summary": "краткое описание",
|
||||
"Show": "Показать",
|
||||
"Show Additional Params": "Показать дополнительные параметры",
|
||||
"Show shortcuts": "Показать клавиатурные сокращения",
|
||||
"sidebar": "боковая панель",
|
||||
"Sign in": "Войти",
|
||||
"Sign Out": "Выход",
|
||||
"Sign up": "зарегистрировать",
|
||||
"Speech recognition error: {{error}}": "Ошибка распознавания речи: {{error}}",
|
||||
"Speech-to-Text Engine": "Система распознавания речи",
|
||||
"SpeechRecognition API is not supported in this browser.": "API распознавания речи не поддерживается в этом браузере.",
|
||||
"Stop Sequence": "Последовательность остановки",
|
||||
"STT Settings": "Настройки распознавания речи",
|
||||
"Submit": "Отправить",
|
||||
"Success": "Успех",
|
||||
"Successfully updated.": "Успешно обновлено.",
|
||||
"Sync All": "Синхронизировать все",
|
||||
"System": "Система",
|
||||
"System Prompt": "Системный промпт",
|
||||
"Tags": "Теги",
|
||||
"Temperature": "Температура",
|
||||
"Template": "Шаблон",
|
||||
"Text Completion": "Завершение текста",
|
||||
"Text-to-Speech Engine": "Система синтеза речи",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Тема",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Это обеспечивает сохранение ваших ценных разговоров в безопасной базе данных на вашем сервере. Спасибо!",
|
||||
"This setting does not sync across browsers or devices.": "Эта настройка не синхронизируется между браузерами или устройствами.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Совет: Обновляйте несколько переменных подряд, нажимая клавишу Tab в поле ввода чата после каждой замены.",
|
||||
"Title": "Заголовок",
|
||||
"Title Auto-Generation": "Автогенерация заголовка",
|
||||
"Title Generation Prompt": "Промпт для генерации заголовка",
|
||||
"to": "в",
|
||||
"To access the available model names for downloading,": "Чтобы получить доступ к доступным для загрузки именам моделей,",
|
||||
"To access the GGUF models available for downloading,": "Чтобы получить доступ к моделям GGUF, доступным для загрузки,",
|
||||
"to chat input.": "в чате.",
|
||||
"Toggle settings": "Переключить настройки",
|
||||
"Toggle sidebar": "Переключить боковую панель",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "Проблемы с доступом к Ollama?",
|
||||
"TTS Settings": "Настройки TTS",
|
||||
"Type Hugging Face Resolve (Download) URL": "Введите URL-адрес Hugging Face Resolve (загрузки)",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "Упс! Возникла проблема подключения к {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Неизвестный тип файла '{{file_type}}', но принимается и обрабатывается как обычный текст",
|
||||
"Update password": "Обновить пароль",
|
||||
"Upload a GGUF model": "Загрузить модель GGUF",
|
||||
"Upload files": "Загрузить файлы",
|
||||
"Upload Progress": "Прогресс загрузки",
|
||||
"URL Mode": "Режим URL",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Используйте '#' в поле ввода промпта для загрузки и выбора ваших документов.",
|
||||
"Use Gravatar": "Использовать Gravatar",
|
||||
"user": "пользователь",
|
||||
"User Permissions": "Права пользователя",
|
||||
"Users": "Пользователи",
|
||||
"Utilize": "Использовать",
|
||||
"Valid time units:": "Допустимые единицы времени:",
|
||||
"variable": "переменная",
|
||||
"variable to have them replaced with clipboard content.": "переменная, чтобы их заменить содержимым буфера обмена.",
|
||||
"Version": "Версия",
|
||||
"Web": "Веб",
|
||||
"WebUI Add-ons": "Дополнения для WebUI",
|
||||
"WebUI Settings": "Настройки WebUI",
|
||||
"WebUI will make requests to": "WebUI будет отправлять запросы на",
|
||||
"What’s New in": "Что нового в",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Когда история отключена, новые чаты в этом браузере не будут отображаться в вашей истории на любом из ваших устройств.",
|
||||
"Whisper (Local)": "Шепот (локальный)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Напишите предложение промпта (например, Кто вы?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Напишите резюме в 50 словах, которое кратко описывает [тему или ключевое слово].",
|
||||
"You": "Вы",
|
||||
"You're a helpful assistant.": "Вы полезный ассистент.",
|
||||
"You're now logged in.": "Вы вошли в систему."
|
||||
}
|
||||
363
src/lib/i18n/locales/uk-UA/translation.json
Normal file
363
src/lib/i18n/locales/uk-UA/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' для відсутності терміну дії.",
|
||||
"(Beta)": "(Beta)",
|
||||
"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
|
||||
"(latest)": "(остання)",
|
||||
"{{modelName}} is thinking...": "{{modelName}} думає...",
|
||||
"{{webUIName}} Backend Required": "Необхідний бекенд {{webUIName}}",
|
||||
"a user": "користувача",
|
||||
"About": "Про програму",
|
||||
"Account": "Обліковий запис",
|
||||
"Action": "Дія",
|
||||
"Add a model": "Додати модель",
|
||||
"Add a model tag name": "Додати ім'я тегу моделі",
|
||||
"Add a short description about what this modelfile does": "Додати короткий опис того, що робить цей файл моделі",
|
||||
"Add a short title for this prompt": "Додати коротку назву для цього промту",
|
||||
"Add a tag": "Додайте тег",
|
||||
"Add Docs": "Додати документи",
|
||||
"Add Files": "Додати файли",
|
||||
"Add message": "Додати повідомлення",
|
||||
"add tags": "додати теги",
|
||||
"Adjusting these settings will apply changes universally to all users.": "Зміни в цих налаштуваннях будуть застосовані для всіх користувачів.",
|
||||
"admin": "адмін",
|
||||
"Admin Panel": "Панель адміністратора",
|
||||
"Admin Settings": "Налаштування адміністратора",
|
||||
"Advanced Parameters": "Розширені параметри",
|
||||
"all": "всі",
|
||||
"All Users": "Всі користувачі",
|
||||
"Allow": "Дозволити",
|
||||
"Allow Chat Deletion": "Дозволити видалення чату",
|
||||
"alphanumeric characters and hyphens": "алфавітно-цифрові символи та дефіси",
|
||||
"Already have an account?": "Вже є обліковий запис?",
|
||||
"an assistant": "асистента",
|
||||
"and": "та",
|
||||
"API Base URL": "Базова адреса URL API",
|
||||
"API Key": "Ключ API",
|
||||
"API RPM": "API RPM",
|
||||
"are allowed - Activate this command by typing": "дозволено - активізуйте цю команду набором",
|
||||
"Are you sure?": "Ви впевнені?",
|
||||
"Audio": "Аудіо",
|
||||
"Auto-playback response": "Автоматичне відтворення відповіді",
|
||||
"Auto-send input after 3 sec.": "Автоматична відправка вводу через 3 сек.",
|
||||
"AUTOMATIC1111 Base URL": "Базова адреса URL AUTOMATIC1111",
|
||||
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Необхідна URL-адреса.",
|
||||
"available!": "доступно!",
|
||||
"Back": "Назад",
|
||||
"Builder Mode": "Режим конструктора",
|
||||
"Cancel": "Скасувати",
|
||||
"Categories": "Категорії",
|
||||
"Change Password": "Змінити пароль",
|
||||
"Chat": "Чат",
|
||||
"Chat History": "Історія чату",
|
||||
"Chat History is off for this browser.": "Історія чату вимкнена для цього браузера.",
|
||||
"Chats": "Чати",
|
||||
"Check Again": "Перевірити ще раз",
|
||||
"Check for updates": "Перевірити оновлення",
|
||||
"Checking for updates...": "Перевірка оновлень...",
|
||||
"Choose a model before saving...": "Оберіть модель перед збереженням...",
|
||||
"Chunk Overlap": "Перекриття фрагментів",
|
||||
"Chunk Params": "Параметри фрагментів",
|
||||
"Chunk Size": "Розмір фрагменту",
|
||||
"Click here for help.": "Клацніть тут, щоб отримати допомогу.",
|
||||
"Click here to check other modelfiles.": "Клацніть тут, щоб перевірити інші файли моделей.",
|
||||
"Click here to select": "Натисніть тут, щоб вибрати",
|
||||
"Click here to select documents.": "Натисніть тут, щоб вибрати документи.",
|
||||
"click here.": "клацніть тут.",
|
||||
"Click on the user role button to change a user's role.": "Натисніть кнопку ролі користувача, щоб змінити роль користувача.",
|
||||
"Close": "Закрити",
|
||||
"Collection": "Колекція",
|
||||
"Command": "Команда",
|
||||
"Confirm Password": "Підтвердіть пароль",
|
||||
"Connections": "З'єднання",
|
||||
"Content": "Зміст",
|
||||
"Context Length": "Довжина контексту",
|
||||
"Conversation Mode": "Режим розмови",
|
||||
"Copy last code block": "Копіювати останній блок коду",
|
||||
"Copy last response": "Копіювати останню відповідь",
|
||||
"Copying to clipboard was successful!": "Копіювання в буфер обміну виконано успішно!",
|
||||
"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':": "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':",
|
||||
"Create a modelfile": "Створити файл моделі",
|
||||
"Create Account": "Створити обліковий запис",
|
||||
"Created at": "Створено",
|
||||
"Created by": "Створено",
|
||||
"Current Model": "Поточна модель",
|
||||
"Current Password": "Поточний пароль",
|
||||
"Custom": "Налаштувати",
|
||||
"Customize Ollama models for a specific purpose": "Налаштувати моделі Ollama для конкретної мети",
|
||||
"Dark": "Темна",
|
||||
"Database": "База даних",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "За замовчуванням",
|
||||
"Default (Automatic1111)": "За замовчуванням (Automatic1111)",
|
||||
"Default (Web API)": "За замовчуванням (Web API)",
|
||||
"Default model updated": "Модель за замовчуванням оновлено",
|
||||
"Default Prompt Suggestions": "Пропозиції промтів замовчуванням",
|
||||
"Default User Role": "Роль користувача за замовчуванням",
|
||||
"delete": "видалити",
|
||||
"Delete a model": "Видалити модель",
|
||||
"Delete chat": "Видалити чат",
|
||||
"Delete Chats": "Видалити чати",
|
||||
"Deleted {{deleteModelTag}}": "Видалено {{deleteModelTag}}",
|
||||
"Deleted {tagName}": "Видалено {tagName}",
|
||||
"Description": "Опис",
|
||||
"Desktop Notifications": "Сповіщення",
|
||||
"Disabled": "Вимкнено",
|
||||
"Discover a modelfile": "Знайти файл моделі",
|
||||
"Discover a prompt": "Знайти промт",
|
||||
"Discover, download, and explore custom prompts": "Знайдіть, завантажте та досліджуйте налаштовані промти",
|
||||
"Discover, download, and explore model presets": "Знайдіть, завантажте та досліджуйте налаштовані налаштування моделі",
|
||||
"Display the username instead of You in the Chat": "Показувати ім'я користувача замість 'Ви' в чаті",
|
||||
"Document": "Документ",
|
||||
"Document Settings": "Налаштування документа",
|
||||
"Documents": "Документи",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "не встановлює жодних зовнішніх з'єднань, і ваші дані залишаються в безпеці на вашому локальному сервері.",
|
||||
"Don't Allow": "Не дозволяти",
|
||||
"Don't have an account?": "Немає облікового запису?",
|
||||
"Download as a File": "Завантажити як файл",
|
||||
"Download Database": "Завантажити базу даних",
|
||||
"Drop any files here to add to the conversation": "Перетягніть сюди файли, щоб додати до розмови",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр. '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.",
|
||||
"Edit Doc": "Редагувати документ",
|
||||
"Edit User": "Редагувати користувача",
|
||||
"Email": "Електронна пошта",
|
||||
"Enable Chat History": "Увімкнути історію чату",
|
||||
"Enable New Sign Ups": "Дозволити нові реєстрації",
|
||||
"Enabled": "Увімкнено",
|
||||
"Enter {{role}} message here": "Введіть повідомлення {{role}} тут",
|
||||
"Enter API Key": "Введіть API-ключ",
|
||||
"Enter Chunk Overlap": "Введіть перекриття фрагменту",
|
||||
"Enter Chunk Size": "Введіть розмір фрагменту",
|
||||
"Enter Image Size (e.g. 512x512)": "Введіть розмір зображення (напр. 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Введіть URL-адресу API LiteLLM (litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "Введіть ключ API LiteLLM (litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Введіть RPM API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Введіть модель LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Введіть максимальну кількість токенів (litellm_params.max_tokens)",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Введіть тег моделі (напр. {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Введіть кількість кроків (напр. 50)",
|
||||
"Enter stop sequence": "Введіть символ зупинки",
|
||||
"Enter Top K": "Введіть Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Введіть URL-адресу (напр. http://127.0.0.1:7860/)",
|
||||
"Enter Your Email": "Введіть вашу електронну пошту",
|
||||
"Enter Your Full Name": "Введіть ваше ім'я",
|
||||
"Enter Your Password": "Введіть ваш пароль",
|
||||
"Experimental": "Експериментальне",
|
||||
"Export All Chats (All Users)": "Експортувати всі чати (всі користувачі)",
|
||||
"Export Chats": "Експортувати чати",
|
||||
"Export Documents Mapping": "Експортувати відображення документів",
|
||||
"Export Modelfiles": "Експортувати файл моделі",
|
||||
"Export Prompts": "Експортувати промти",
|
||||
"Failed to read clipboard contents": "Не вдалося прочитати вміст буфера обміну",
|
||||
"File Mode": "Файловий режим",
|
||||
"File not found.": "Файл не знайдено.",
|
||||
"Focus chat input": "Фокус вводу чату",
|
||||
"Format your variables using square brackets like this:": "Форматуйте свої змінні квадратними дужками так:",
|
||||
"From (Base Model)": "Від (базова модель)",
|
||||
"Full Screen Mode": "Режим повного екрану",
|
||||
"General": "Загальні",
|
||||
"General Settings": "Загальні налаштування",
|
||||
"Hello, {{name}}": "Привіт, {{name}}",
|
||||
"Hide": "Приховати",
|
||||
"Hide Additional Params": "Приховати додаткові параметри",
|
||||
"How can I help you today?": "Чим я можу допомогти вам сьогодні?",
|
||||
"Image Generation (Experimental)": "Генерування зображень (експериментально)",
|
||||
"Image Generation Engine": "Механізм генерації зображень",
|
||||
"Image Settings": "Налаштування зображення",
|
||||
"Images": "Зображення",
|
||||
"Import Chats": "Імпортувати чати",
|
||||
"Import Documents Mapping": "Імпортувати відображення документів",
|
||||
"Import Modelfiles": "Імпортувати файл моделі",
|
||||
"Import Prompts": "Імпортувати промти",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Включіть прапор `--api` при запуску stable-diffusion-webui",
|
||||
"Interface": "Інтерфейс",
|
||||
"join our Discord for help.": "приєднуйтеся до нашого Discord для допомоги.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "Термін дії JWT",
|
||||
"JWT Token": "Токен JWT",
|
||||
"Keep Alive": "Зберегти активність",
|
||||
"Keyboard shortcuts": "Клавіатурні скорочення",
|
||||
"Language": "Мова",
|
||||
"Light": "Світла",
|
||||
"Listening...": "Слухаю...",
|
||||
"LLMs can make mistakes. Verify important information.": "LLMs можуть помилятися. Перевірте важливу інформацію.",
|
||||
"Made by OpenWebUI Community": "Зроблено спільнотою OpenWebUI",
|
||||
"Make sure to enclose them with": "Переконайтеся, що вони закриті",
|
||||
"Manage LiteLLM Models": "Керування моделями LiteLLM",
|
||||
"Manage Models": "Керування моделями",
|
||||
"Manage Ollama Models": "Керування моделями Ollama",
|
||||
"Max Tokens": "Максимальна кількість токенів",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимум 3 моделі можна завантажити одночасно. Будь ласка, спробуйте пізніше.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "Модель '{{modelName}}' успішно завантажено.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' вже знаходиться в черзі на завантаження.",
|
||||
"Model {{modelId}} not found": "Модель {{modelId}} не знайдено",
|
||||
"Model {{modelName}} already exists.": "Модель {{modelName}} вже існує.",
|
||||
"Model Name": "Назва моделі",
|
||||
"Model not selected": "Модель не вибрана",
|
||||
"Model Tag Name": "Ім'я тегу моделі",
|
||||
"Model Whitelisting": "Модель білого списку",
|
||||
"Model(s) Whitelisted": "Модель(і) білого списку",
|
||||
"Modelfile": "Файли моделі",
|
||||
"Modelfile Advanced Settings": "Додаткові налаштування файлу моделі",
|
||||
"Modelfile Content": "Вміст файлу моделі",
|
||||
"Modelfiles": "Файли моделей",
|
||||
"Models": "Моделі",
|
||||
"My Documents": "Мої документи",
|
||||
"My Modelfiles": "Мої файли моделей",
|
||||
"My Prompts": "Мої промти",
|
||||
"Name": "Ім'я",
|
||||
"Name Tag": "Назва тегу",
|
||||
"Name your modelfile": "Назвіть свій файл моделі",
|
||||
"New Chat": "Новий чат",
|
||||
"New Password": "Новий пароль",
|
||||
"Not sure what to add?": "Не впевнений, що додати?",
|
||||
"Not sure what to write? Switch to": "Не впевнений, що писати? Переключитися на",
|
||||
"Off": "Вимк",
|
||||
"Okay, Let's Go!": "Гаразд, давайте почнемо!",
|
||||
"Ollama Base URL": "Основна URL-адреса Ollama",
|
||||
"Ollama Version": "Версія Ollama",
|
||||
"On": "Увімк",
|
||||
"Only": "Тільки",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "У рядку команди дозволено використовувати лише алфавітно-цифрові символи та дефіси.",
|
||||
"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.": "Ой! Зачекайте, будь ласка! Ваші файли ще готуються. Ми робимо все, щоб вони були ідеальними. Будь ласка, будьте терплячі, ми повідомимо вам, коли вони будуть готові.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Упс! Схоже, що URL-адреса невірна. Будь ласка, перевірте ще раз та спробуйте ще раз.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Упс! Ви використовуєте непідтримуваний метод (тільки для фронтенду). Будь ласка, обслуговуйте WebUI з бекенду.",
|
||||
"Open": "Відкрити",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "Відкрити новий чат",
|
||||
"OpenAI API": "API OpenAI",
|
||||
"OpenAI API Key": "Ключ API OpenAI",
|
||||
"OpenAI API Key is required.": "Потрібен ключ OpenAI API.",
|
||||
"or": "або",
|
||||
"Parameters": "Параметри",
|
||||
"Password": "Пароль",
|
||||
"PDF Extract Images (OCR)": "Розпізнавання зображень з PDF (OCR)",
|
||||
"pending": "на розгляді",
|
||||
"Permission denied when accessing microphone: {{error}}": "Доступ до мікрофона заборонено: {{error}}",
|
||||
"Playground": "Майданчик",
|
||||
"Profile": "Профіль",
|
||||
"Prompt Content": "Зміст промту",
|
||||
"Prompt suggestions": "Швидкі промти",
|
||||
"Prompts": "Промти",
|
||||
"Pull a model from Ollama.com": "Завантажити модель з Ollama.com",
|
||||
"Pull Progress": "Прогрес завантаження",
|
||||
"Query Params": "Параметри запиту",
|
||||
"RAG Template": "Шаблон RAG",
|
||||
"Raw Format": "Необроблений формат",
|
||||
"Record voice": "Записати голос",
|
||||
"Redirecting you to OpenWebUI Community": "Перенаправляємо вас до спільноти OpenWebUI",
|
||||
"Release Notes": "Нотатки до випуску",
|
||||
"Repeat Last N": "Повторити останні N",
|
||||
"Repeat Penalty": "Штраф за повторення",
|
||||
"Request Mode": "Режим запиту",
|
||||
"Reset Vector Storage": "Скинути векторне сховище",
|
||||
"Response AutoCopy to Clipboard": "Автокопіювання відповіді в буфер обміну",
|
||||
"Role": "Роль",
|
||||
"Rosé Pine": "Rosé Pine",
|
||||
"Rosé Pine Dawn": "Rosé Pine Dawn",
|
||||
"Save": "Зберегти",
|
||||
"Save & Create": "Зберегти та створити",
|
||||
"Save & Submit": "Зберегти та надіслати",
|
||||
"Save & Update": "Зберегти та оновити",
|
||||
"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": "Збереження журналів чату безпосередньо в сховище вашого браузера більше не підтримується. Будь ласка, завантажте та видаліть журнали чату, натиснувши кнопку нижче. Не хвилюйтеся, ви можете легко повторно імпортувати журнали чату до бекенду через",
|
||||
"Scan": "Сканування",
|
||||
"Scan complete!": "Сканування завершено!",
|
||||
"Scan for documents from {{path}}": "Сканування документів з {{path}}",
|
||||
"Search": "Пошук",
|
||||
"Search Documents": "Пошук документів",
|
||||
"Search Prompts": "Пошук промтів",
|
||||
"See readme.md for instructions": "Див. readme.md для інструкцій",
|
||||
"See what's new": "Подивіться, що нового",
|
||||
"Seed": "Сід",
|
||||
"Select a mode": "Оберіть режим",
|
||||
"Select a model": "Виберіть модель",
|
||||
"Select an Ollama instance": "Виберіть екземпляр Ollama",
|
||||
"Send a Message": "Надіслати повідомлення",
|
||||
"Send message": "Надіслати повідомлення",
|
||||
"Server connection verified": "З'єднання з сервером підтверджено",
|
||||
"Set as default": "Встановити за замовчуванням",
|
||||
"Set Default Model": "Встановити модель за замовчуванням",
|
||||
"Set Image Size": "Встановити розмір зображення",
|
||||
"Set Steps": "Встановити кроки",
|
||||
"Set Title Auto-Generation Model": "Встановити модель автогенерації заголовків",
|
||||
"Set Voice": "Встановити голос",
|
||||
"Settings": "Налаштування",
|
||||
"Settings saved successfully!": "Налаштування успішно збережено!",
|
||||
"Share to OpenWebUI Community": "Поділитися зі спільнотою OpenWebUI",
|
||||
"short-summary": "короткий зміст",
|
||||
"Show": "Показати",
|
||||
"Show Additional Params": "Показати додаткові параметри",
|
||||
"Show shortcuts": "Показати клавіатурні скорочення",
|
||||
"sidebar": "бокова панель",
|
||||
"Sign in": "Увійти",
|
||||
"Sign Out": "Вийти",
|
||||
"Sign up": "Зареєструватися",
|
||||
"Speech recognition error: {{error}}": "Помилка розпізнавання мови: {{error}}",
|
||||
"Speech-to-Text Engine": "Система розпізнавання мови",
|
||||
"SpeechRecognition API is not supported in this browser.": "SpeechRecognition API не підтримується в цьому браузері.",
|
||||
"Stop Sequence": "Символ зупинки",
|
||||
"STT Settings": "Налаштування STT",
|
||||
"Submit": "Надіслати",
|
||||
"Success": "Успіх",
|
||||
"Successfully updated.": "Успішно оновлено.",
|
||||
"Sync All": "Синхронізувати все",
|
||||
"System": "Система",
|
||||
"System Prompt": "Системний промт",
|
||||
"Tags": "Теги",
|
||||
"Temperature": "Температура",
|
||||
"Template": "Шаблон",
|
||||
"Text Completion": "Завершення тексту",
|
||||
"Text-to-Speech Engine": "Система синтезу мови",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Тема",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Це забезпечує збереження ваших цінних розмов у безпечному бекенд-сховищі. Дякуємо!",
|
||||
"This setting does not sync across browsers or devices.": "Це налаштування не синхронізується між браузерами або пристроями.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Порада: Оновіть кілька слотів змінних послідовно, натискаючи клавішу табуляції у вікні чату після кожної заміни.",
|
||||
"Title": "Заголовок",
|
||||
"Title Auto-Generation": "Автогенерація заголовків",
|
||||
"Title Generation Prompt": "Промт для генерування заголовків",
|
||||
"to": "в",
|
||||
"To access the available model names for downloading,": "Щоб отримати доступ до назв доступних для завантаження моделей,",
|
||||
"To access the GGUF models available for downloading,": "Щоб отримати доступ до моделей GGUF, які можна завантажити,,",
|
||||
"to chat input.": "в чаті.",
|
||||
"Toggle settings": "Переключити налаштування",
|
||||
"Toggle sidebar": "Переключити бокову панель",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "Проблеми з доступом до Ollama?",
|
||||
"TTS Settings": "Налаштування TTS",
|
||||
"Type Hugging Face Resolve (Download) URL": "Введіть URL ресурсу Hugging Face Resolve (завантаження)",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "Ой! Виникла проблема при підключенні до {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Невідомий тип файлу '{{file_type}}', але приймається та обробляється як звичайний текст",
|
||||
"Update password": "Оновити пароль",
|
||||
"Upload a GGUF model": "Завантажити GGUF модель",
|
||||
"Upload files": "Завантажити файли",
|
||||
"Upload Progress": "Прогрес завантаження",
|
||||
"URL Mode": "Режим URL",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Для введення промтів до веб-сторінок (URL) або вибору документів, будь ласка, використовуйте символ '#'.",
|
||||
"Use Gravatar": "Змінити аватар",
|
||||
"user": "користувач",
|
||||
"User Permissions": "Дозволи користувача",
|
||||
"Users": "Користувачі",
|
||||
"Utilize": "Використовувати",
|
||||
"Valid time units:": "Дійсні одиниці часу:",
|
||||
"variable": "змінна",
|
||||
"variable to have them replaced with clipboard content.": "змінна, щоб замінити їх вмістом буфера обміну.",
|
||||
"Version": "Версія",
|
||||
"Web": "Веб",
|
||||
"WebUI Add-ons": "Додатки WebUI",
|
||||
"WebUI Settings": "Налаштування WebUI",
|
||||
"WebUI will make requests to": "WebUI буде робити запити до",
|
||||
"What’s New in": "Що нового в",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Коли історія вимкнена, нові чати в цьому браузері не будуть відображатися в історії на жодному з ваших пристроїв.",
|
||||
"Whisper (Local)": "Whisper (локально)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Напишіть промт (напр. Хто ти?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Напишіть стислий зміст у 50 слів, який узагальнює [тема або ключове слово].",
|
||||
"You": "Ви",
|
||||
"You're a helpful assistant.": "Ви корисний асистент.",
|
||||
"You're now logged in.": "Ви увійшли в систему."
|
||||
}
|
||||
363
src/lib/i18n/locales/vi-VN/translation.json
Normal file
363
src/lib/i18n/locales/vi-VN/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' hoặc '-1' không hết hạn.",
|
||||
"(Beta)": "(Beta)",
|
||||
"(e.g. `sh webui.sh --api`)": "(vd: `sh webui.sh --api`)",
|
||||
"(latest)": "(mới nhất)",
|
||||
"{{modelName}} is thinking...": "{{modelName}} đang suy nghĩ...",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}} Yêu cầu Backend",
|
||||
"a user": "một người dùng",
|
||||
"About": "Giới thiệu",
|
||||
"Account": "Tài khoản",
|
||||
"Action": "Hành động",
|
||||
"Add a model": "Thêm mô hình",
|
||||
"Add a model tag name": "Thêm tên thẻ mô hình (tag)",
|
||||
"Add a short description about what this modelfile does": "Thêm mô tả ngắn về việc tệp mô tả mô hình (modelfile) này làm gì",
|
||||
"Add a short title for this prompt": "Thêm tiêu đề ngắn cho prompt này",
|
||||
"Add a tag": "Thêm thẻ (tag)",
|
||||
"Add Docs": "Thêm tài liệu",
|
||||
"Add Files": "Thêm tệp",
|
||||
"Add message": "Thêm tin nhắn",
|
||||
"add tags": "thêm thẻ",
|
||||
"Adjusting these settings will apply changes universally to all users.": "Điều chỉnh các cài đặt này sẽ áp dụng thay đổi toàn cầu cho tất cả người dùng.",
|
||||
"admin": "quản trị viên",
|
||||
"Admin Panel": "Trang Quản trị",
|
||||
"Admin Settings": "Cài đặt hệ thống",
|
||||
"Advanced Parameters": "Các tham số Nâng cao",
|
||||
"all": "tất cả",
|
||||
"All Users": "Tất cả Người dùng",
|
||||
"Allow": "Cho phép",
|
||||
"Allow Chat Deletion": "Cho phép Xóa cuộc trò chuyện",
|
||||
"alphanumeric characters and hyphens": "ký tự số và gạch nối",
|
||||
"Already have an account?": "Đã có tài khoản?",
|
||||
"an assistant": "một trợ lý",
|
||||
"and": "và",
|
||||
"API Base URL": "URL Cơ bản API",
|
||||
"API Key": "Khóa API",
|
||||
"API RPM": "RPM API",
|
||||
"are allowed - Activate this command by typing": "được phép - Kích hoạt lệnh này bằng cách gõ",
|
||||
"Are you sure?": "Bạn có chắc chắn không?",
|
||||
"Audio": "Âm thanh",
|
||||
"Auto-playback response": "Tự động phát lại phản hồi",
|
||||
"Auto-send input after 3 sec.": "Tự động gửi đầu vào sau 3 giây.",
|
||||
"AUTOMATIC1111 Base URL": "URL Cơ bản AUTOMATIC1111",
|
||||
"AUTOMATIC1111 Base URL is required.": "Yêu cầu URL Cơ bản AUTOMATIC1111.",
|
||||
"available!": "có sẵn!",
|
||||
"Back": "Quay lại",
|
||||
"Builder Mode": "Chế độ Builder",
|
||||
"Cancel": "Hủy bỏ",
|
||||
"Categories": "Danh mục",
|
||||
"Change Password": "Đổi Mật khẩu",
|
||||
"Chat": "Trò chuyện",
|
||||
"Chat History": "Lịch sử Trò chuyện",
|
||||
"Chat History is off for this browser.": "Lịch sử Trò chuyện đã tắt cho trình duyệt này.",
|
||||
"Chats": "Cuộc trò chuyện",
|
||||
"Check Again": "Kiểm tra Lại",
|
||||
"Check for updates": "Kiểm tra cập nhật",
|
||||
"Checking for updates...": "Đang kiểm tra cập nhật...",
|
||||
"Choose a model before saving...": "Chọn một mô hình trước khi lưu...",
|
||||
"Chunk Overlap": "Chunk chồng lấn (overlap)",
|
||||
"Chunk Params": "Cài đặt tham số cho Chunk",
|
||||
"Chunk Size": "Kích thước Chunk",
|
||||
"Click here for help.": "Bấm vào đây để được trợ giúp.",
|
||||
"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 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 vai trò người dùng để thay đổi vai trò của người dùng.",
|
||||
"Close": "Đóng",
|
||||
"Collection": "Bộ sưu tập",
|
||||
"Command": "Lệnh",
|
||||
"Confirm Password": "Xác nhận Mật khẩu",
|
||||
"Connections": "Kết nối",
|
||||
"Content": "Nội dung",
|
||||
"Context Length": "Độ dài Ngữ cảnh",
|
||||
"Conversation Mode": "Chế độ Hội thoại",
|
||||
"Copy last code block": "Sao chép khối mã cuối cùng",
|
||||
"Copy last response": "Sao chép phản hồi cuối cùng",
|
||||
"Copying to clipboard was successful!": "Sao chép vào clipboard thành công!",
|
||||
"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':": "Tạo một cụm từ súc tích, 3-5 từ làm tiêu đề cho truy vấn sau, tuân thủ nghiêm ngặt giới hạn 3-5 từ và tránh sử dụng từ 'tiêu đề':",
|
||||
"Create a modelfile": "Tạo tệp mô tả mô hình",
|
||||
"Create Account": "Tạo Tài khoản",
|
||||
"Created at": "Được tạo vào lúc",
|
||||
"Created by": "Được tạo bởi",
|
||||
"Current Model": "Mô hình hiện tại",
|
||||
"Current Password": "Mật khẩu hiện tại",
|
||||
"Custom": "Tùy chỉnh",
|
||||
"Customize Ollama models for a specific purpose": "Tùy chỉnh các mô hình dựa trên Ollama cho một mục đích cụ thể",
|
||||
"Dark": "Tối",
|
||||
"Database": "Cơ sở dữ liệu",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "Mặc định",
|
||||
"Default (Automatic1111)": "Mặc định (Automatic1111)",
|
||||
"Default (Web API)": "Mặc định (Web API)",
|
||||
"Default model updated": "Mô hình mặc định đã được cập nhật",
|
||||
"Default Prompt Suggestions": "Đề nghị Nhắc mặc định",
|
||||
"Default User Role": "Vai trò Người dùng Mặc định",
|
||||
"delete": "xóa",
|
||||
"Delete a model": "Xóa mô hình",
|
||||
"Delete chat": "Xóa cuộc trò chuyện",
|
||||
"Delete Chats": "Xóa Cuộc trò chuyện",
|
||||
"Deleted {{deleteModelTag}}": "Đã xóa {{deleteModelTag}}",
|
||||
"Deleted {tagName}": "Đã xóa {tagName}",
|
||||
"Description": "Mô tả",
|
||||
"Desktop Notifications": "Thông báo Máy tính",
|
||||
"Disabled": "Đã vô hiệu hóa",
|
||||
"Discover a modelfile": "Khám phá một tệp mô hình",
|
||||
"Discover a prompt": "Khám phá một prompt",
|
||||
"Discover, download, and explore custom prompts": "Khám phá, tải xuống và khám phá các prompt tùy chỉnh",
|
||||
"Discover, download, and explore model presets": "Khám phá, tải xuống và khám phá các thiết lập mô hình sẵn",
|
||||
"Display the username instead of You in the Chat": "Hiển thị tên người dùng thay vì 'Bạn' trong Cuộc trò chuyện",
|
||||
"Document": "Tài liệu",
|
||||
"Document Settings": "Cài đặt Tài liệu",
|
||||
"Documents": "Tài liệu",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "không thực hiện bất kỳ kết nối ngoài nào, và dữ liệu của bạn vẫn được lưu trữ an toàn trên máy chủ lưu trữ cục bộ của bạn.",
|
||||
"Don't Allow": "Không Cho phép",
|
||||
"Don't have an account?": "Không có tài khoản?",
|
||||
"Download as a File": "Tải xuống dưới dạng tệp",
|
||||
"Download Database": "Tải xuống Cơ sở dữ liệu",
|
||||
"Drop any files here to add to the conversation": "Thả bất kỳ tệp nào ở đây để thêm vào cuộc trò chuyện",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "vd: '30s','10m'. Đơn vị thời gian hợp lệ là 's', 'm', 'h'.",
|
||||
"Edit Doc": "Sửa Tài liệu",
|
||||
"Edit User": "Sửa Người dùng",
|
||||
"Email": "Email",
|
||||
"Enable Chat History": "Bật Lịch sử Trò chuyện",
|
||||
"Enable New Sign Ups": "Cho phép Đăng ký Mới",
|
||||
"Enabled": "Đã bật",
|
||||
"Enter {{role}} message here": "Nhập tin nhắn {{role}} ở đây",
|
||||
"Enter API Key": "Nhập Khóa API",
|
||||
"Enter Chunk Overlap": "Nhập Chunk chồng lấn (overlap)",
|
||||
"Enter Chunk Size": "Nhập Kích thước Chunk",
|
||||
"Enter Image Size (e.g. 512x512)": "Nhập Kích thước ảnh (vd: 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Nhập URL Cơ bản API LiteLLM (litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "Nhập Khóa API LiteLLM (litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "Nhập RPM API LiteLLM (litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "Nhập Mô hình LiteLLM (litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "Nhập Số Token Tối đa (litellm_params.max_tokens)",
|
||||
"Enter model tag (e.g. {{modelTag}})": "Nhập thẻ mô hình (vd: {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "Nhập Số Bước (vd: 50)",
|
||||
"Enter stop sequence": "Nhập trình tự dừng",
|
||||
"Enter Top K": "Nhập Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "Nhập URL (vd: http://127.0.0.1:7860/)",
|
||||
"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",
|
||||
"Experimental": "Thực nghiệm",
|
||||
"Export All Chats (All Users)": "Xuất Tất cả Cuộc trò chuyện (Tất cả Người dùng)",
|
||||
"Export Chats": "Xuất Cuộc trò chuyện",
|
||||
"Export Documents Mapping": "Xuất Ánh xạ Tài liệu",
|
||||
"Export Modelfiles": "Xuất Tệp Mô hình",
|
||||
"Export Prompts": "Xuất prompts",
|
||||
"Failed to read clipboard contents": "Không thể đọc nội dung clipboard",
|
||||
"File Mode": "Chế độ Tệp văn bản",
|
||||
"File not found.": "Không tìm thấy tệp.",
|
||||
"Focus chat input": "Tập trung vào đầu vào cuộc trò chuyện",
|
||||
"Format your variables using square brackets like this:": "Định dạng các biến của bạn bằng cách sử dụng dấu ngoặc vuông như thế này:",
|
||||
"From (Base Model)": "Từ (Mô hình Cơ sở)",
|
||||
"Full Screen Mode": "Chế độ Toàn màn hình",
|
||||
"General": "Tổng quát",
|
||||
"General Settings": "Cài đặt tổng quát",
|
||||
"Hello, {{name}}": "Xin chào, {{name}}",
|
||||
"Hide": "Ẩn",
|
||||
"Hide Additional Params": "Ẩn Các tham số bổ sung",
|
||||
"How can I help you today?": "Tôi có thể giúp gì cho bạn hôm nay?",
|
||||
"Image Generation (Experimental)": "tạo ảnh (Thực nghiệm)",
|
||||
"Image Generation Engine": "Công cụ tạo ảnh",
|
||||
"Image Settings": "Cài đặt ảnh",
|
||||
"Images": "Hình ảnh",
|
||||
"Import Chats": "Nạp cuộc trò chuyện (chats)",
|
||||
"Import Documents Mapping": "Nạp sơ đồ hóa Tài liệu (document mapping)",
|
||||
"Import Modelfiles": "Nạp tệp mô tả mô hình (modelefiles)",
|
||||
"Import Prompts": "Nạp Prompts",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "Bao gồm flag `--api` khi chạy stable-diffusion-webui",
|
||||
"Interface": "Giao diện",
|
||||
"join our Discord for help.": "tham gia Discord của chúng tôi để được trợ giúp.",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "JWT Hết hạn",
|
||||
"JWT Token": "Token JWT",
|
||||
"Keep Alive": "Giữ kết nối",
|
||||
"Keyboard shortcuts": "Phím tắt",
|
||||
"Language": "Ngôn ngữ",
|
||||
"Light": "Sáng",
|
||||
"Listening...": "Đang nghe...",
|
||||
"LLMs can make mistakes. Verify important information.": "Hệ thống có thể tạo ra nội dung không chính xác hoặc sai. Hãy kiểm chứng kỹ lưỡng thông tin trước khi tiếp nhận và sử dụng.",
|
||||
"Made by OpenWebUI Community": "Được tạo bởi Cộng đồng OpenWebUI",
|
||||
"Make sure to enclose them with": "Hãy chắc chắn bao quanh chúng bằng",
|
||||
"Manage LiteLLM Models": "Quản lý mô hình với LiteLLM",
|
||||
"Manage Models": "Quản lý mô hình",
|
||||
"Manage Ollama Models": "Quản lý mô hình với Ollama",
|
||||
"Max Tokens": "Số Token Tối đa",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Tối đa 3 mô hình có thể được tải xuống cùng lúc. Vui lòng thử lại sau.",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "Mô hình '{{modelName}}' đã được tải xuống thành công.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Mô hình '{{modelTag}}' đã có trong hàng đợi để tải xuống.",
|
||||
"Model {{modelId}} not found": "Không tìm thấy Mô hình {{modelId}}",
|
||||
"Model {{modelName}} already exists.": "Mô hình {{modelName}} đã tồn tại.",
|
||||
"Model Name": "Tên Mô hình",
|
||||
"Model not selected": "Chưa chọn Mô hình",
|
||||
"Model Tag Name": "Tên thẻ Mô hình",
|
||||
"Model Whitelisting": "Whitelist mô hình",
|
||||
"Model(s) Whitelisted": "các mô hình được cho vào danh sách Whitelist",
|
||||
"Modelfile": "Tệp Mô hình",
|
||||
"Modelfile Advanced Settings": "Cài đặt Nâng cao Tệp Mô hình",
|
||||
"Modelfile Content": "Nội dung Tệp Mô hình",
|
||||
"Modelfiles": "Tệp Mô hình",
|
||||
"Models": "Mô hình",
|
||||
"My Documents": "Tài liệu của tôi",
|
||||
"My Modelfiles": "Tệp Mô hình của tôi",
|
||||
"My Prompts": "Các prompt của tôi",
|
||||
"Name": "Tên",
|
||||
"Name Tag": "Tên Thẻ",
|
||||
"Name your modelfile": "Đặt tên cho tệp mô hình của bạn",
|
||||
"New Chat": "Cuộc trò chuyện Mới",
|
||||
"New Password": "Mật khẩu Mới",
|
||||
"Not sure what to add?": "Không chắc phải thêm gì?",
|
||||
"Not sure what to write? Switch to": "Không chắc phải viết gì? Chuyển sang",
|
||||
"Off": "Tắt",
|
||||
"Okay, Let's Go!": "Được rồi, Bắt đầu thôi!",
|
||||
"Ollama Base URL": "URL Cơ bản Ollama",
|
||||
"Ollama Version": "Phiên bản Ollama",
|
||||
"On": "Bật",
|
||||
"Only": "Chỉ",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "Chỉ ký tự số và gạch nối được phép trong chuỗi lệnh.",
|
||||
"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.": "Rất tiếc! Hãy giữ nguyên! Các tệp của bạn vẫn đang trong được phân tích và xử lý. Chúng tôi đang cố gắng hoàn thành chúng. Vui lòng kiên nhẫn và chúng tôi sẽ cho bạn biết khi chúng sẵn sàng.",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Rất tiếc! URL dường như không hợp lệ. Vui lòng kiểm tra lại và thử lại.",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Rất tiếc! Bạn đang sử dụng một phương thức không được hỗ trợ (chỉ dành cho frontend). Vui lòng cung cấp phương thức cho WebUI từ phía backend.",
|
||||
"Open": "Mở",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "Mở cuộc trò chuyện mới",
|
||||
"OpenAI API": "API OpenAI",
|
||||
"OpenAI API Key": "Khóa API OpenAI",
|
||||
"OpenAI API Key is required.": "Yêu cầu Khóa API OpenAI.",
|
||||
"or": "hoặc",
|
||||
"Parameters": "Tham số",
|
||||
"Password": "Mật khẩu",
|
||||
"PDF Extract Images (OCR)": "Trích xuất ảnh từ PDF (OCR)",
|
||||
"pending": "đang chờ",
|
||||
"Permission denied when accessing microphone: {{error}}": "Quyền truy cập micrô bị từ chối: {{error}}",
|
||||
"Playground": "Thử nghiệm (Playground)",
|
||||
"Profile": "Hồ sơ",
|
||||
"Prompt Content": "Nội dung prompt",
|
||||
"Prompt suggestions": "Đề nghị prompt",
|
||||
"Prompts": "prompt",
|
||||
"Pull a model from Ollama.com": "Tải một mô hình từ Ollama.com",
|
||||
"Pull Progress": "Tiến trình Tải xuống",
|
||||
"Query Params": "Tham số Truy vấn",
|
||||
"RAG Template": "Mẫu prompt cho RAG",
|
||||
"Raw Format": "Raw Format",
|
||||
"Record voice": "Ghi âm",
|
||||
"Redirecting you to OpenWebUI Community": "Đang chuyển hướng bạn đến Cộng đồng OpenWebUI",
|
||||
"Release Notes": "Ghi chú Phát hành",
|
||||
"Repeat Last N": "Repeat Last N",
|
||||
"Repeat Penalty": "Repeat Penalty",
|
||||
"Request Mode": "Request Mode",
|
||||
"Reset Vector Storage": "Cài đặt lại Vector Storage",
|
||||
"Response AutoCopy to Clipboard": "Tự động Sao chép Phản hồi vào clipboard",
|
||||
"Role": "Vai trò",
|
||||
"Rosé Pine": "Rosé Pine",
|
||||
"Rosé Pine Dawn": "Rosé Pine Dawn",
|
||||
"Save": "Lưu",
|
||||
"Save & Create": "Lưu & Tạo",
|
||||
"Save & Submit": "Lưu & Gửi",
|
||||
"Save & Update": "Lưu & Cập nhật",
|
||||
"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": "Không còn hỗ trợ lưu trữ nhật ký trò chuyện trực tiếp vào bộ nhớ trình duyệt của bạn. Vui lòng dành thời gian để tải xuống và xóa nhật ký trò chuyện của bạn bằng cách nhấp vào nút bên dưới. Đừng lo lắng, bạn có thể dễ dàng nhập lại nhật ký trò chuyện của mình vào backend thông qua",
|
||||
"Scan": "Quét",
|
||||
"Scan complete!": "Quét hoàn tất!",
|
||||
"Scan for documents from {{path}}": "Quét tài liệu từ {{path}}",
|
||||
"Search": "Tìm kiếm",
|
||||
"Search Documents": "Tìm Tài liệu",
|
||||
"Search Prompts": "Tìm prompt",
|
||||
"See readme.md for instructions": "Xem readme.md để biết hướng dẫn",
|
||||
"See what's new": "Xem những gì mới",
|
||||
"Seed": "Hạt giống",
|
||||
"Select a mode": "Chọn một chế độ",
|
||||
"Select a model": "Chọn một mô hình",
|
||||
"Select an Ollama instance": "Chọn một thực thể Ollama",
|
||||
"Send a Message": "Gửi yêu cầu",
|
||||
"Send message": "Gửi yêu cầu",
|
||||
"Server connection verified": "Kết nối máy chủ đã được xác minh",
|
||||
"Set as default": "Đặt làm mặc định",
|
||||
"Set Default Model": "Đặt Mô hình Mặc định",
|
||||
"Set Image Size": "Đặt Kích thước ảnh",
|
||||
"Set Steps": "Đặt Số Bước",
|
||||
"Set Title Auto-Generation Model": "Đặt Mô hình Tự động Tạo Tiêu đề",
|
||||
"Set Voice": "Đặt Giọng nói",
|
||||
"Settings": "Cài đặt",
|
||||
"Settings saved successfully!": "Cài đặt đã được lưu thành công!",
|
||||
"Share to OpenWebUI Community": "Chia sẻ đến Cộng đồng OpenWebUI",
|
||||
"short-summary": "tóm tắt ngắn",
|
||||
"Show": "Hiển thị",
|
||||
"Show Additional Params": "Hiển thị Tham số Bổ sung",
|
||||
"Show shortcuts": "Hiển thị phím tắt",
|
||||
"sidebar": "thanh bên",
|
||||
"Sign in": "Đăng nhập",
|
||||
"Sign Out": "Đăng xuất",
|
||||
"Sign up": "Đăng ký",
|
||||
"Speech recognition error: {{error}}": "Lỗi nhận dạng giọng nói: {{error}}",
|
||||
"Speech-to-Text Engine": "Công cụ Nhận dạng Giọng nói",
|
||||
"SpeechRecognition API is not supported in this browser.": "Trình duyệt này không hỗ trợ API Nhận dạng Giọng nói.",
|
||||
"Stop Sequence": "Trình tự Dừng",
|
||||
"STT Settings": "Cài đặt Nhận dạng Giọng nói",
|
||||
"Submit": "Gửi",
|
||||
"Success": "Thành công",
|
||||
"Successfully updated.": "Đã cập nhật thành công.",
|
||||
"Sync All": "Đồng bộ hóa Tất cả",
|
||||
"System": "Hệ thống",
|
||||
"System Prompt": "prompt Hệ thống",
|
||||
"Tags": "Thẻ",
|
||||
"Temperature": "Nhiệt độ",
|
||||
"Template": "Mẫu",
|
||||
"Text Completion": "Hoàn tất Văn bản",
|
||||
"Text-to-Speech Engine": "Công cụ Chuyển Văn bản thành Giọng nói",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "Chủ đề",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Điều này đảm bảo rằng các cuộc trò chuyện có giá trị của bạn được lưu an toàn vào cơ sở dữ liệu backend của bạn. Cảm ơn bạn!",
|
||||
"This setting does not sync across browsers or devices.": "Cài đặt này không đồng bộ hóa trên các trình duyệt hoặc thiết bị.",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Mẹo: Cập nhật nhiều khe biến liên tiếp bằng cách nhấn phím tab trong đầu vào trò chuyện sau mỗi việc thay thế.",
|
||||
"Title": "Tiêu đề",
|
||||
"Title Auto-Generation": "Tự động Tạo Tiêu đề",
|
||||
"Title Generation Prompt": "prompt Tạo Tiêu đề",
|
||||
"to": "đến",
|
||||
"To access the available model names for downloading,": "Để truy cập các tên mô hình có sẵn để tải xuống,",
|
||||
"To access the GGUF models available for downloading,": "Để truy cập các mô hình GGUF có sẵn để tải xuống,",
|
||||
"to chat input.": "đến đầu vào trò chuyện.",
|
||||
"Toggle settings": "Bật/tắt cài đặt",
|
||||
"Toggle sidebar": "Bật/tắt thanh bên",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "Gặp vấn đề khi truy cập Ollama?",
|
||||
"TTS Settings": "Cài đặt Chuyển văn bản thành Giọng nói",
|
||||
"Type Hugging Face Resolve (Download) URL": "Nhập URL Hugging Face Resolve (Tải xuống)",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "Ồ! Đã xảy ra sự cố khi kết nối với {{provider}}.",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Loại Tệp Không xác định '{{file_type}}', nhưng đang chấp nhận và xử lý như văn bản thô",
|
||||
"Update password": "Cập nhật mật khẩu",
|
||||
"Upload a GGUF model": "Tải lên một mô hình GGUF",
|
||||
"Upload files": "Tải lên tệp",
|
||||
"Upload Progress": "Tiến trình Tải lên",
|
||||
"URL Mode": "Chế độ URL",
|
||||
"Use '#' in the prompt input to load and select your documents.": "Sử dụng '#' trong đầu vào prompt để tải và chọn tài liệu của bạn.",
|
||||
"Use Gravatar": "Sử dụng Gravatar",
|
||||
"user": "người dùng",
|
||||
"User Permissions": "Quyền của Người dùng",
|
||||
"Users": "Người dùng",
|
||||
"Utilize": "Sử dụng",
|
||||
"Valid time units:": "Đơn vị thời gian hợp lệ:",
|
||||
"variable": "biến",
|
||||
"variable to have them replaced with clipboard content.": "biến để có chúng được thay thế bằng nội dung clipboard.",
|
||||
"Version": "Phiên bản",
|
||||
"Web": "Web",
|
||||
"WebUI Add-ons": "Tiện ích WebUI",
|
||||
"WebUI Settings": "Cài đặt WebUI",
|
||||
"WebUI will make requests to": "WebUI sẽ thực hiện các yêu cầu đến",
|
||||
"What's New in": "Có gì mới trong",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Khi chế độ lịch sử trò chuyện đã tắt, các cuộc trò chuyện mới trên trình duyệt này sẽ không xuất hiện trên bất kỳ thiết bị nào của bạn.",
|
||||
"Whisper (Local)": "Whisper (Local)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "Hãy viết một prompt (vd: Bạn là ai?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "Viết một tóm tắt trong vòng 50 từ cho [chủ đề hoặc từ khóa].",
|
||||
"You": "Bạn",
|
||||
"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."
|
||||
}
|
||||
363
src/lib/i18n/locales/zh-CN/translation.json
Normal file
363
src/lib/i18n/locales/zh-CN/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' 或 '-1' 表示无过期时间。",
|
||||
"(Beta)": "(测试版)",
|
||||
"(e.g. `sh webui.sh --api`)": "(例如 `sh webui.sh --api`)",
|
||||
"(latest)": "",
|
||||
"{{modelName}} is thinking...": "{{modelName}} 正在思考...",
|
||||
"{{webUIName}} Backend Required": "需要 {{webUIName}} 后端",
|
||||
"a user": "",
|
||||
"About": "关于",
|
||||
"Account": "账户",
|
||||
"Action": "操作",
|
||||
"Add a model": "添加模型",
|
||||
"Add a model tag name": "添加模型标签名称",
|
||||
"Add a short description about what this modelfile does": "添加关于此模型文件功能的简短描述",
|
||||
"Add a short title for this prompt": "为这个提示添加一个简短的标题",
|
||||
"Add a tag": "",
|
||||
"Add Docs": "添加文档",
|
||||
"Add Files": "添加文件",
|
||||
"Add message": "添加消息",
|
||||
"add tags": "添加标签",
|
||||
"Adjusting these settings will apply changes universally to all users.": "调整这些设置将对所有用户普遍应用更改。",
|
||||
"admin": "管理员",
|
||||
"Admin Panel": "管理员面板",
|
||||
"Admin Settings": "管理员设置",
|
||||
"Advanced Parameters": "高级参数",
|
||||
"all": "所有",
|
||||
"All Users": "所有用户",
|
||||
"Allow": "允许",
|
||||
"Allow Chat Deletion": "允许删除聊天",
|
||||
"alphanumeric characters and hyphens": "字母数字字符和连字符",
|
||||
"Already have an account?": "已经有账户了吗?",
|
||||
"an assistant": "",
|
||||
"and": "和",
|
||||
"API Base URL": "API 基础 URL",
|
||||
"API Key": "API 密钥",
|
||||
"API RPM": "API RPM",
|
||||
"are allowed - Activate this command by typing": "被允许 - 通过输入激活此命令",
|
||||
"Are you sure?": "你确定吗?",
|
||||
"Audio": "音频",
|
||||
"Auto-playback response": "自动播放回应",
|
||||
"Auto-send input after 3 sec.": "3秒后自动发送输入",
|
||||
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 基础 URL",
|
||||
"AUTOMATIC1111 Base URL is required.": "需要 AUTOMATIC1111 基础 URL。",
|
||||
"available!": "可用!",
|
||||
"Back": "返回",
|
||||
"Builder Mode": "构建者模式",
|
||||
"Cancel": "取消",
|
||||
"Categories": "分类",
|
||||
"Change Password": "更改密码",
|
||||
"Chat": "聊天",
|
||||
"Chat History": "聊天历史",
|
||||
"Chat History is off for this browser.": "此浏览器已关闭聊天历史。",
|
||||
"Chats": "聊天",
|
||||
"Check Again": "再次检查",
|
||||
"Check for updates": "检查更新",
|
||||
"Checking for updates...": "正在检查更新...",
|
||||
"Choose a model before saving...": "保存前选择一个模型...",
|
||||
"Chunk Overlap": "块重叠",
|
||||
"Chunk Params": "块参数",
|
||||
"Chunk Size": "块大小",
|
||||
"Click here for help.": "点击这里获取帮助。",
|
||||
"Click here to check other modelfiles.": "点击这里检查其他模型文件。",
|
||||
"Click here to select": "点击这里选择",
|
||||
"Click here to select documents.": "点击这里选择文档。",
|
||||
"click here.": "点击这里。",
|
||||
"Click on the user role button to change a user's role.": "点击用户角色按钮以更改用户的角色。",
|
||||
"Close": "关闭",
|
||||
"Collection": "收藏",
|
||||
"Command": "命令",
|
||||
"Confirm Password": "确认密码",
|
||||
"Connections": "连接",
|
||||
"Content": "内容",
|
||||
"Context Length": "上下文长度",
|
||||
"Conversation Mode": "对话模式",
|
||||
"Copy last code block": "复制最后一个代码块",
|
||||
"Copy last response": "复制最后一次回复",
|
||||
"Copying to clipboard was successful!": "复制到剪贴板成功!",
|
||||
"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':": "为以下查询创建一个简洁的、3-5个词的短语作为标题,严格遵守3-5个词的限制并避免使用“标题”一词:",
|
||||
"Create a modelfile": "创建模型文件",
|
||||
"Create Account": "创建账户",
|
||||
"Created at": "创建于",
|
||||
"Created by": "创建者",
|
||||
"Current Model": "当前模型",
|
||||
"Current Password": "当前密码",
|
||||
"Custom": "自定义",
|
||||
"Customize Ollama models for a specific purpose": "为特定目的定制Ollama模型",
|
||||
"Dark": "暗色",
|
||||
"Database": "数据库",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "默认",
|
||||
"Default (Automatic1111)": "默认(Automatic1111)",
|
||||
"Default (Web API)": "默认(Web API)",
|
||||
"Default model updated": "默认模型已更新",
|
||||
"Default Prompt Suggestions": "默认提示词建议",
|
||||
"Default User Role": "默认用户角色",
|
||||
"delete": "删除",
|
||||
"Delete a model": "删除一个模型",
|
||||
"Delete chat": "删除聊天",
|
||||
"Delete Chats": "删除聊天记录",
|
||||
"Deleted {{deleteModelTag}}": "已删除{{deleteModelTag}}",
|
||||
"Deleted {tagName}": "已删除{tagName}",
|
||||
"Description": "描述",
|
||||
"Desktop Notifications": "桌面通知",
|
||||
"Disabled": "禁用",
|
||||
"Discover a modelfile": "探索模型文件",
|
||||
"Discover a prompt": "探索提示词",
|
||||
"Discover, download, and explore custom prompts": "发现、下载并探索自定义提示词",
|
||||
"Discover, download, and explore model presets": "发现、下载并探索模型预设",
|
||||
"Display the username instead of You in the Chat": "在聊天中显示用户名而不是“您”",
|
||||
"Document": "文档",
|
||||
"Document Settings": "文档设置",
|
||||
"Documents": "文档",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "不进行任何外部连接,您的数据安全地存储在您的本地服务器上。",
|
||||
"Don't Allow": "不允许",
|
||||
"Don't have an account?": "没有账户?",
|
||||
"Download as a File": "下载为文件",
|
||||
"Download Database": "下载数据库",
|
||||
"Drop any files here to add to the conversation": "将任何文件拖到这里以添加到对话中",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s','10m'。有效的时间单位是's', 'm', 'h'。",
|
||||
"Edit Doc": "编辑文档",
|
||||
"Edit User": "编辑用户",
|
||||
"Email": "电子邮件",
|
||||
"Enable Chat History": "启用聊天历史",
|
||||
"Enable New Sign Ups": "启用新注册",
|
||||
"Enabled": "启用",
|
||||
"Enter {{role}} message here": "",
|
||||
"Enter API Key": "",
|
||||
"Enter Chunk Overlap": "",
|
||||
"Enter Chunk Size": "",
|
||||
"Enter Image Size (e.g. 512x512)": "",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "",
|
||||
"Enter model tag (e.g. {{modelTag}})": "",
|
||||
"Enter Number of Steps (e.g. 50)": "",
|
||||
"Enter stop sequence": "输入停止序列",
|
||||
"Enter Top K": "",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
|
||||
"Enter Your Email": "输入您的电子邮件",
|
||||
"Enter Your Full Name": "输入您的全名",
|
||||
"Enter Your Password": "输入您的密码",
|
||||
"Experimental": "实验性",
|
||||
"Export All Chats (All Users)": "导出所有聊天(所有用户)",
|
||||
"Export Chats": "导出聊天",
|
||||
"Export Documents Mapping": "导出文档映射",
|
||||
"Export Modelfiles": "导出模型文件",
|
||||
"Export Prompts": "导出提示词",
|
||||
"Failed to read clipboard contents": "无法读取剪贴板内容",
|
||||
"File Mode": "文件模式",
|
||||
"File not found.": "文件未找到。",
|
||||
"Focus chat input": "聚焦聊天输入",
|
||||
"Format your variables using square brackets like this:": "使用这样的方括号格式化你的变量:",
|
||||
"From (Base Model)": "来自(基础模型)",
|
||||
"Full Screen Mode": "全屏模式",
|
||||
"General": "通用",
|
||||
"General Settings": "通用设置",
|
||||
"Hello, {{name}}": "你好,{{name}}",
|
||||
"Hide": "隐藏",
|
||||
"Hide Additional Params": "隐藏额外参数",
|
||||
"How can I help you today?": "今天我如何能帮到你?",
|
||||
"Image Generation (Experimental)": "图像生成(实验性)",
|
||||
"Image Generation Engine": "图像生成引擎",
|
||||
"Image Settings": "图像设置",
|
||||
"Images": "图像",
|
||||
"Import Chats": "导入聊天",
|
||||
"Import Documents Mapping": "导入文档映射",
|
||||
"Import Modelfiles": "导入模型文件",
|
||||
"Import Prompts": "导入提示",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "运行stable-diffusion-webui时包含`--api`标志",
|
||||
"Interface": "界面",
|
||||
"join our Discord for help.": "加入我们的Discord寻求帮助。",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "JWT过期",
|
||||
"JWT Token": "JWT令牌",
|
||||
"Keep Alive": "保持活动",
|
||||
"Keyboard shortcuts": "键盘快捷键",
|
||||
"Language": "语言",
|
||||
"Light": "浅色",
|
||||
"Listening...": "监听中...",
|
||||
"LLMs can make mistakes. Verify important information.": "大型语言模型可能会犯错。验证重要信息。",
|
||||
"Made by OpenWebUI Community": "由OpenWebUI社区制作",
|
||||
"Make sure to enclose them with": "确保将它们包含在内",
|
||||
"Manage LiteLLM Models": "管理LiteLLM模型",
|
||||
"Manage Models": "",
|
||||
"Manage Ollama Models": "管理Ollama模型",
|
||||
"Max Tokens": "最大令牌数",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可以同时下载3个模型。请稍后再试。",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "模型'{{modelName}}'已成功下载。",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "模型'{{modelTag}}'已在下载队列中。",
|
||||
"Model {{modelId}} not found": "未找到模型{{modelId}}",
|
||||
"Model {{modelName}} already exists.": "模型{{modelName}}已存在。",
|
||||
"Model Name": "模型名称",
|
||||
"Model not selected": "未选择模型",
|
||||
"Model Tag Name": "模型标签名称",
|
||||
"Model Whitelisting": "",
|
||||
"Model(s) Whitelisted": "",
|
||||
"Modelfile": "模型文件",
|
||||
"Modelfile Advanced Settings": "模型文件高级设置",
|
||||
"Modelfile Content": "模型文件内容",
|
||||
"Modelfiles": "模型文件",
|
||||
"Models": "模型",
|
||||
"My Documents": "我的文档",
|
||||
"My Modelfiles": "我的模型文件",
|
||||
"My Prompts": "我的提示",
|
||||
"Name": "名称",
|
||||
"Name Tag": "名称标签",
|
||||
"Name your modelfile": "命名你的模型文件",
|
||||
"New Chat": "新聊天",
|
||||
"New Password": "新密码",
|
||||
"Not sure what to add?": "不确定要添加什么?",
|
||||
"Not sure what to write? Switch to": "不确定写什么?切换到",
|
||||
"Off": "关闭",
|
||||
"Okay, Let's Go!": "好的,我们开始吧!",
|
||||
"Ollama Base URL": "Ollama 基础 URL",
|
||||
"Ollama Version": "Ollama 版本",
|
||||
"On": "开",
|
||||
"Only": "仅",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字符串中只允许使用字母数字字符和连字符。",
|
||||
"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.": "哎呀!请稍等!您的文件仍在处理中。我们正在将它们做得尽善尽美,请耐心等待,一旦准备好我们会通知您。",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "哎呀!看起来 URL 无效。请仔细检查后再试一次。",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "哎呀!您正在使用不支持的方法(仅限前端)。请从后端提供 WebUI。",
|
||||
"Open": "打开",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "打开新聊天",
|
||||
"OpenAI API": "OpenAI API",
|
||||
"OpenAI API Key": "OpenAI API 密钥",
|
||||
"OpenAI API Key is required.": "需要 OpenAI API 密钥。",
|
||||
"or": "或",
|
||||
"Parameters": "参数",
|
||||
"Password": "密码",
|
||||
"PDF Extract Images (OCR)": "",
|
||||
"pending": "待定",
|
||||
"Permission denied when accessing microphone: {{error}}": "访问麦克风时权限被拒绝:{{error}}",
|
||||
"Playground": "游乐场",
|
||||
"Profile": "个人资料",
|
||||
"Prompt Content": "提示词内容",
|
||||
"Prompt suggestions": "提示词建议",
|
||||
"Prompts": "提示词",
|
||||
"Pull a model from Ollama.com": "从 Ollama.com 拉取一个模型",
|
||||
"Pull Progress": "拉取进度",
|
||||
"Query Params": "查询参数",
|
||||
"RAG Template": "RAG 模板",
|
||||
"Raw Format": "原始格式",
|
||||
"Record voice": "录音",
|
||||
"Redirecting you to OpenWebUI Community": "正在将您重定向到 OpenWebUI 社区",
|
||||
"Release Notes": "发布说明",
|
||||
"Repeat Last N": "重复最后 N 次",
|
||||
"Repeat Penalty": "重复惩罚",
|
||||
"Request Mode": "请求模式",
|
||||
"Reset Vector Storage": "重置向量存储",
|
||||
"Response AutoCopy to Clipboard": "响应自动复制到剪贴板",
|
||||
"Role": "角色",
|
||||
"Rosé Pine": "Rosé Pine",
|
||||
"Rosé Pine Dawn": "Rosé Pine Dawn",
|
||||
"Save": "保存",
|
||||
"Save & Create": "保存并创建",
|
||||
"Save & Submit": "保存并提交",
|
||||
"Save & Update": "保存并更新",
|
||||
"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": "直接将聊天记录保存到浏览器存储中不再受支持。请点击下面的按钮下载并删除您的聊天记录。别担心,您可以通过轻松地将聊天记录重新导入到后端",
|
||||
"Scan": "扫描",
|
||||
"Scan complete!": "扫描完成!",
|
||||
"Scan for documents from {{path}}": "从 {{path}} 扫描文档",
|
||||
"Search": "搜索",
|
||||
"Search Documents": "搜索文档",
|
||||
"Search Prompts": "搜索提示词",
|
||||
"See readme.md for instructions": "查看 readme.md 以获取说明",
|
||||
"See what's new": "查看最新内容",
|
||||
"Seed": "种子",
|
||||
"Select a mode": "选择一个模式",
|
||||
"Select a model": "选择一个模型",
|
||||
"Select an Ollama instance": "",
|
||||
"Send a Message": "发送消息",
|
||||
"Send message": "发送消息",
|
||||
"Server connection verified": "服务器连接已验证",
|
||||
"Set as default": "设为默认",
|
||||
"Set Default Model": "设置默认模型",
|
||||
"Set Image Size": "设置图片大小",
|
||||
"Set Steps": "设置步骤",
|
||||
"Set Title Auto-Generation Model": "设置标题自动生成模型",
|
||||
"Set Voice": "设置声音",
|
||||
"Settings": "设置",
|
||||
"Settings saved successfully!": "",
|
||||
"Share to OpenWebUI Community": "分享到OpenWebUI社区",
|
||||
"short-summary": "简短总结",
|
||||
"Show": "显示",
|
||||
"Show Additional Params": "显示额外参数",
|
||||
"Show shortcuts": "显示快捷方式",
|
||||
"sidebar": "侧边栏",
|
||||
"Sign in": "登录",
|
||||
"Sign Out": "登出",
|
||||
"Sign up": "注册",
|
||||
"Speech recognition error: {{error}}": "语音识别错误:{{error}}",
|
||||
"Speech-to-Text Engine": "语音转文字引擎",
|
||||
"SpeechRecognition API is not supported in this browser.": "此浏览器不支持SpeechRecognition API。",
|
||||
"Stop Sequence": "停止序列",
|
||||
"STT Settings": "语音转文字设置",
|
||||
"Submit": "提交",
|
||||
"Success": "成功",
|
||||
"Successfully updated.": "成功更新。",
|
||||
"Sync All": "同步所有",
|
||||
"System": "系统",
|
||||
"System Prompt": "系统提示",
|
||||
"Tags": "标签",
|
||||
"Temperature": "温度",
|
||||
"Template": "模板",
|
||||
"Text Completion": "文本完成",
|
||||
"Text-to-Speech Engine": "文本转语音引擎",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "主题",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "这确保了您宝贵的对话被安全保存到后端数据库中。谢谢!",
|
||||
"This setting does not sync across browsers or devices.": "此设置不会在浏览器或设备之间同步。",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替换后,在聊天输入中按Tab键可以连续更新多个变量槽。",
|
||||
"Title": "标题",
|
||||
"Title Auto-Generation": "标题自动生成",
|
||||
"Title Generation Prompt": "标题生成提示",
|
||||
"to": "到",
|
||||
"To access the available model names for downloading,": "要访问可下载的模型名称,",
|
||||
"To access the GGUF models available for downloading,": "要访问可下载的GGUF模型,",
|
||||
"to chat input.": "到聊天输入。",
|
||||
"Toggle settings": "切换设置",
|
||||
"Toggle sidebar": "切换侧边栏",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "访问Ollama时遇到问题?",
|
||||
"TTS Settings": "文本转语音设置",
|
||||
"Type Hugging Face Resolve (Download) URL": "输入Hugging Face解析(下载)URL",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "哦哦!连接到{{provider}}时出现问题。",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "未知文件类型'{{file_type}}',但接受并视为纯文本",
|
||||
"Update password": "",
|
||||
"Upload a GGUF model": "上传一个GGUF模型",
|
||||
"Upload files": "上传文件",
|
||||
"Upload Progress": "上传进度",
|
||||
"URL Mode": "URL模式",
|
||||
"Use '#' in the prompt input to load and select your documents.": "在提示输入中使用'#'来加载和选择你的文档。",
|
||||
"Use Gravatar": "使用Gravatar",
|
||||
"user": "用户",
|
||||
"User Permissions": "用户权限",
|
||||
"Users": "用户",
|
||||
"Utilize": "利用",
|
||||
"Valid time units:": "有效时间单位:",
|
||||
"variable": "变量",
|
||||
"variable to have them replaced with clipboard content.": "变量将被剪贴板内容替换。",
|
||||
"Version": "",
|
||||
"Web": "网页",
|
||||
"WebUI Add-ons": "WebUI 插件",
|
||||
"WebUI Settings": "WebUI 设置",
|
||||
"WebUI will make requests to": "",
|
||||
"What’s New in": "最新变化",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "当历史记录被关闭时,这个浏览器上的新聊天不会出现在你任何设备的历史记录中。",
|
||||
"Whisper (Local)": "私语(本地)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "写一个提示建议(例如:你是谁?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "用50个字写一个总结[主题或关键词]。",
|
||||
"You": "你",
|
||||
"You're a helpful assistant.": "你是一个有帮助的助手。",
|
||||
"You're now logged in.": "你现在已经登录了。"
|
||||
}
|
||||
363
src/lib/i18n/locales/zh-TW/translation.json
Normal file
363
src/lib/i18n/locales/zh-TW/translation.json
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
{
|
||||
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' 或 '-1' 表示無期限。",
|
||||
"(Beta)": "(測試版)",
|
||||
"(e.g. `sh webui.sh --api`)": "(例如 `sh webui.sh --api`)",
|
||||
"(latest)": "",
|
||||
"{{modelName}} is thinking...": "{{modelName}} 正在思考...",
|
||||
"{{webUIName}} Backend Required": "需要 {{webUIName}} 後台",
|
||||
"a user": "",
|
||||
"About": "關於",
|
||||
"Account": "帳號",
|
||||
"Action": "動作",
|
||||
"Add a model": "新增模型",
|
||||
"Add a model tag name": "新增模型標籤名稱",
|
||||
"Add a short description about what this modelfile does": "新增關於此模型文件功能的簡短描述",
|
||||
"Add a short title for this prompt": "為此提示新增一個簡短的標題",
|
||||
"Add a tag": "新增標籤",
|
||||
"Add Docs": "新增文件",
|
||||
"Add Files": "新增檔案",
|
||||
"Add message": "新增訊息",
|
||||
"add tags": "新增標籤",
|
||||
"Adjusting these settings will apply changes universally to all users.": "調整這些設定將對所有使用者進行更改。",
|
||||
"admin": "管理員",
|
||||
"Admin Panel": "管理員控制台",
|
||||
"Admin Settings": "管理設定",
|
||||
"Advanced Parameters": "進階參數",
|
||||
"all": "所有",
|
||||
"All Users": "所有使用者",
|
||||
"Allow": "允許",
|
||||
"Allow Chat Deletion": "允許刪除聊天",
|
||||
"alphanumeric characters and hyphens": "字母數字字符和連字符",
|
||||
"Already have an account?": "已經有帳號了嗎?",
|
||||
"an assistant": "",
|
||||
"and": "和",
|
||||
"API Base URL": "API 基本 URL",
|
||||
"API Key": "API 金鑰",
|
||||
"API RPM": "API RPM",
|
||||
"are allowed - Activate this command by typing": "是允許的 - 透過輸入來啟動此命令",
|
||||
"Are you sure?": "你確定嗎?",
|
||||
"Audio": "音訊",
|
||||
"Auto-playback response": "自動播放回答",
|
||||
"Auto-send input after 3 sec.": "3秒後自動傳送輸入內容",
|
||||
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 基本 URL",
|
||||
"AUTOMATIC1111 Base URL is required.": "需要 AUTOMATIC1111 基本 URL",
|
||||
"available!": "可以使用!",
|
||||
"Back": "返回",
|
||||
"Builder Mode": "建構模式",
|
||||
"Cancel": "取消",
|
||||
"Categories": "分類",
|
||||
"Change Password": "修改密碼",
|
||||
"Chat": "聊天",
|
||||
"Chat History": "聊天歷史",
|
||||
"Chat History is off for this browser.": "此瀏覽器已關閉聊天歷史。",
|
||||
"Chats": "聊天",
|
||||
"Check Again": "重新檢查",
|
||||
"Check for updates": "檢查更新",
|
||||
"Checking for updates...": "正在檢查更新...",
|
||||
"Choose a model before saving...": "儲存前選擇一個模型...",
|
||||
"Chunk Overlap": "區塊重疊",
|
||||
"Chunk Params": "區塊參數",
|
||||
"Chunk Size": "區塊大小",
|
||||
"Click here for help.": "點擊這裡尋找幫助。",
|
||||
"Click here to check other modelfiles.": "點擊這裡檢查其他模型文件。",
|
||||
"Click here to select": "點擊這裡選擇",
|
||||
"Click here to select documents.": "點擊這裡選擇文件。",
|
||||
"click here.": "點擊這裡。",
|
||||
"Click on the user role button to change a user's role.": "點擊使用者角色按鈕以更改使用者的角色。",
|
||||
"Close": "關閉",
|
||||
"Collection": "收藏",
|
||||
"Command": "命令",
|
||||
"Confirm Password": "確認密碼",
|
||||
"Connections": "連線",
|
||||
"Content": "內容",
|
||||
"Context Length": "上下文長度",
|
||||
"Conversation Mode": "對話模式",
|
||||
"Copy last code block": "複製最後一個程式碼區塊",
|
||||
"Copy last response": "複製最後一個回答",
|
||||
"Copying to clipboard was successful!": "成功複製到剪貼簿!",
|
||||
"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':": "為以下的查詢建立一個簡潔、3-5 個詞的短語作為標題,嚴格遵守 3-5 個詞的限制,避免使用「標題」這個詞:",
|
||||
"Create a modelfile": "建立一個模型文件",
|
||||
"Create Account": "建立帳號",
|
||||
"Created at": "建立於",
|
||||
"Created by": "建立者",
|
||||
"Current Model": "目前模型",
|
||||
"Current Password": "目前密碼",
|
||||
"Custom": "自訂",
|
||||
"Customize Ollama models for a specific purpose": "為特定目的自訂 Ollama 模型",
|
||||
"Dark": "暗色",
|
||||
"Database": "資料庫",
|
||||
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
|
||||
"Default": "預設",
|
||||
"Default (Automatic1111)": "預設(Automatic1111)",
|
||||
"Default (Web API)": "預設(Web API)",
|
||||
"Default model updated": "預設模型已更新",
|
||||
"Default Prompt Suggestions": "預設提示建議",
|
||||
"Default User Role": "預設使用者角色",
|
||||
"delete": "刪除",
|
||||
"Delete a model": "刪除一個模型",
|
||||
"Delete chat": "刪除聊天",
|
||||
"Delete Chats": "刪除聊天",
|
||||
"Deleted {{deleteModelTag}}": "已刪除 {{deleteModelTag}}",
|
||||
"Deleted {tagName}": "已刪除 {tagName}",
|
||||
"Description": "描述",
|
||||
"Desktop Notifications": "桌面通知",
|
||||
"Disabled": "已停用",
|
||||
"Discover a modelfile": "發現一個模型文件",
|
||||
"Discover a prompt": "發現一個提示",
|
||||
"Discover, download, and explore custom prompts": "發現、下載並探索自訂提示",
|
||||
"Discover, download, and explore model presets": "發現、下載並探索模型預設",
|
||||
"Display the username instead of You in the Chat": "在聊天中顯示使用者名稱而不是「你」",
|
||||
"Document": "文件",
|
||||
"Document Settings": "文件設定",
|
||||
"Documents": "文件",
|
||||
"does not make any external connections, and your data stays securely on your locally hosted server.": "不會與外部溝通,你的數據安全地留在你的本機伺服器上。",
|
||||
"Don't Allow": "不允許",
|
||||
"Don't have an account?": "沒有帳號?",
|
||||
"Download as a File": "下載為文件",
|
||||
"Download Database": "下載資料庫",
|
||||
"Drop any files here to add to the conversation": "拖拽文件到此處以新增至對話",
|
||||
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s', '10m'。有效的時間單位為 's', 'm', 'h'。",
|
||||
"Edit Doc": "編輯文件",
|
||||
"Edit User": "編輯使用者",
|
||||
"Email": "電子郵件",
|
||||
"Enable Chat History": "啟用聊天歷史",
|
||||
"Enable New Sign Ups": "允許新使用者註冊",
|
||||
"Enabled": "已啟用",
|
||||
"Enter {{role}} message here": "在這裡輸入 {{role}} 訊息",
|
||||
"Enter API Key": "輸入 API 金鑰",
|
||||
"Enter Chunk Overlap": "輸入區塊重疊",
|
||||
"Enter Chunk Size": "輸入區塊大小",
|
||||
"Enter Image Size (e.g. 512x512)": "輸入圖片大小(例如 512x512)",
|
||||
"Enter LiteLLM API Base URL (litellm_params.api_base)": "輸入 LiteLLM API 基本 URL(litellm_params.api_base)",
|
||||
"Enter LiteLLM API Key (litellm_params.api_key)": "輸入 LiteLLM API 金鑰(litellm_params.api_key)",
|
||||
"Enter LiteLLM API RPM (litellm_params.rpm)": "輸入 LiteLLM API RPM(litellm_params.rpm)",
|
||||
"Enter LiteLLM Model (litellm_params.model)": "輸入 LiteLLM 模型(litellm_params.model)",
|
||||
"Enter Max Tokens (litellm_params.max_tokens)": "輸入最大令牌數(litellm_params.max_tokens)",
|
||||
"Enter model tag (e.g. {{modelTag}})": "輸入模型標籤(例如 {{modelTag}})",
|
||||
"Enter Number of Steps (e.g. 50)": "輸入步數(例如 50)",
|
||||
"Enter stop sequence": "輸入停止序列",
|
||||
"Enter Top K": "輸入 Top K",
|
||||
"Enter URL (e.g. http://127.0.0.1:7860/)": "輸入 URL(例如 http://127.0.0.1:7860/)",
|
||||
"Enter Your Email": "輸入你的電子郵件",
|
||||
"Enter Your Full Name": "輸入你的全名",
|
||||
"Enter Your Password": "輸入你的密碼",
|
||||
"Experimental": "實驗性",
|
||||
"Export All Chats (All Users)": "匯出所有聊天(所有使用者)",
|
||||
"Export Chats": "匯出聊天",
|
||||
"Export Documents Mapping": "匯出文件映射",
|
||||
"Export Modelfiles": "匯出模型文件",
|
||||
"Export Prompts": "匯出提示",
|
||||
"Failed to read clipboard contents": "無法讀取剪貼簿內容",
|
||||
"File Mode": "文件模式",
|
||||
"File not found.": "找不到文件。",
|
||||
"Focus chat input": "聚焦聊天輸入框",
|
||||
"Format your variables using square brackets like this:": "使用這樣的方括號來格式化你的變數:",
|
||||
"From (Base Model)": "來自(基礎模型)",
|
||||
"Full Screen Mode": "全螢幕模式",
|
||||
"General": "常用",
|
||||
"General Settings": "常用設定",
|
||||
"Hello, {{name}}": "你好, {{name}}",
|
||||
"Hide": "隱藏",
|
||||
"Hide Additional Params": "隱藏附加參數",
|
||||
"How can I help you today?": "今天能為你做什麼?",
|
||||
"Image Generation (Experimental)": "圖片產生(實驗性)",
|
||||
"Image Generation Engine": "圖片產生引擎",
|
||||
"Image Settings": "圖片設定",
|
||||
"Images": "圖片",
|
||||
"Import Chats": "匯入聊天",
|
||||
"Import Documents Mapping": "匯入文件映射",
|
||||
"Import Modelfiles": "匯入模型文件",
|
||||
"Import Prompts": "匯入提示",
|
||||
"Include `--api` flag when running stable-diffusion-webui": "執行 stable-diffusion-webui 時包含 `--api` 標誌",
|
||||
"Interface": "介面",
|
||||
"join our Discord for help.": "加入我們的 Discord 尋找幫助。",
|
||||
"JSON": "JSON",
|
||||
"JWT Expiration": "JWT 過期",
|
||||
"JWT Token": "JWT 令牌",
|
||||
"Keep Alive": "保持活躍",
|
||||
"Keyboard shortcuts": "鍵盤快速鍵",
|
||||
"Language": "語言",
|
||||
"Light": "亮色",
|
||||
"Listening...": "正在聽取...",
|
||||
"LLMs can make mistakes. Verify important information.": "LLM 可能會產生錯誤。請驗證重要資訊。",
|
||||
"Made by OpenWebUI Community": "由 OpenWebUI 社區製作",
|
||||
"Make sure to enclose them with": "確保用...圍起來",
|
||||
"Manage LiteLLM Models": "管理 LiteLLM 模型",
|
||||
"Manage Models": "",
|
||||
"Manage Ollama Models": "管理 Ollama 模型",
|
||||
"Max Tokens": "最大令牌數",
|
||||
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可以同時下載 3 個模型。請稍後再試。",
|
||||
"Mirostat": "Mirostat",
|
||||
"Mirostat Eta": "Mirostat Eta",
|
||||
"Mirostat Tau": "Mirostat Tau",
|
||||
"MMMM DD, YYYY": "MMMM DD, YYYY",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' 模型已成功下載。",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' 模型已經在下載佇列中。",
|
||||
"Model {{modelId}} not found": "找不到 {{modelId}} 模型",
|
||||
"Model {{modelName}} already exists.": "模型 {{modelName}} 已存在。",
|
||||
"Model Name": "模型名稱",
|
||||
"Model not selected": "未選擇模型",
|
||||
"Model Tag Name": "模型標籤名稱",
|
||||
"Model Whitelisting": "白名單模型",
|
||||
"Model(s) Whitelisted": "模型已加入白名單",
|
||||
"Modelfile": "模型文件",
|
||||
"Modelfile Advanced Settings": "模型文件進階設定",
|
||||
"Modelfile Content": "模型文件內容",
|
||||
"Modelfiles": "模型文件",
|
||||
"Models": "模型",
|
||||
"My Documents": "我的文件",
|
||||
"My Modelfiles": "我的模型文件",
|
||||
"My Prompts": "我的提示",
|
||||
"Name": "名稱",
|
||||
"Name Tag": "名稱標籤",
|
||||
"Name your modelfile": "命名你的模型文件",
|
||||
"New Chat": "新增聊天",
|
||||
"New Password": "新密碼",
|
||||
"Not sure what to add?": "不確定要新增什麼嗎?",
|
||||
"Not sure what to write? Switch to": "不確定要寫什麼?切換到",
|
||||
"Off": "關閉",
|
||||
"Okay, Let's Go!": "好的,啟動吧!",
|
||||
"Ollama Base URL": "Ollama 基本 URL",
|
||||
"Ollama Version": "Ollama 版本",
|
||||
"On": "開啟",
|
||||
"Only": "僅有",
|
||||
"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字符串中只允許使用字母數字字符和連字符。",
|
||||
"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.": "哎呀!請稍等!你的文件還在處理中。我們正最佳化文件,請耐心等待,一旦準備好,我們會通知你。",
|
||||
"Oops! Looks like the URL is invalid. Please double-check and try again.": "哎呀!看起來 URL 無效。請仔細檢查後再試一次。",
|
||||
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "哎呀!你正在使用不支援的方法(僅有前台)。請從後台提供 WebUI。",
|
||||
"Open": "開啟",
|
||||
"Open AI": "Open AI",
|
||||
"Open AI (Dall-E)": "Open AI (Dall-E)",
|
||||
"Open new chat": "開啟新聊天",
|
||||
"OpenAI API": "OpenAI API",
|
||||
"OpenAI API Key": "OpenAI API 金鑰",
|
||||
"OpenAI API Key is required.": "需要 OpenAI API 金鑰。",
|
||||
"or": "或",
|
||||
"Parameters": "參數",
|
||||
"Password": "密碼",
|
||||
"PDF Extract Images (OCR)": "PDF 圖像輸出(OCR 光學文字辨識)",
|
||||
"pending": "等待中",
|
||||
"Permission denied when accessing microphone: {{error}}": "存取麥克風時被拒絕權限: {{error}}",
|
||||
"Playground": "AI 對話遊樂場",
|
||||
"Profile": "個人資料",
|
||||
"Prompt Content": "提示內容",
|
||||
"Prompt suggestions": "提示建議",
|
||||
"Prompts": "提示",
|
||||
"Pull a model from Ollama.com": "從 Ollama.com 下載模型",
|
||||
"Pull Progress": "下載進度",
|
||||
"Query Params": "查詢參數",
|
||||
"RAG Template": "RAG 範例",
|
||||
"Raw Format": "原始格式",
|
||||
"Record voice": "錄音",
|
||||
"Redirecting you to OpenWebUI Community": "將你重新導向到 OpenWebUI 社群",
|
||||
"Release Notes": "發布說明",
|
||||
"Repeat Last N": "重複最後 N 次",
|
||||
"Repeat Penalty": "重複懲罰",
|
||||
"Request Mode": "請求模式",
|
||||
"Reset Vector Storage": "重置向量儲存空間",
|
||||
"Response AutoCopy to Clipboard": "自動複製回答到剪貼簿",
|
||||
"Role": "角色",
|
||||
"Rosé Pine": "玫瑰松",
|
||||
"Rosé Pine Dawn": "黎明玫瑰松",
|
||||
"Save": "儲存",
|
||||
"Save & Create": "儲存並建立",
|
||||
"Save & Submit": "儲存並送出",
|
||||
"Save & Update": "儲存並更新",
|
||||
"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": "現已不支援將聊天紀錄儲存到瀏覽器儲存空間中。請點擊下面的按鈕下載並刪除你的聊天記錄。別擔心,你可以通過以下方式輕鬆地重新匯入你的聊天記錄到後台",
|
||||
"Scan": "掃描",
|
||||
"Scan complete!": "掃描完成!",
|
||||
"Scan for documents from {{path}}": "從 {{path}} 掃描文件",
|
||||
"Search": "搜尋",
|
||||
"Search Documents": "搜尋文件",
|
||||
"Search Prompts": "搜尋提示",
|
||||
"See readme.md for instructions": "查看 readme.md 獲取指南",
|
||||
"See what's new": "查看最新內容",
|
||||
"Seed": "種子",
|
||||
"Select a mode": "選擇模式",
|
||||
"Select a model": "選擇一個模型",
|
||||
"Select an Ollama instance": "選擇 Ollama 實例",
|
||||
"Send a Message": "傳送訊息",
|
||||
"Send message": "傳送訊息",
|
||||
"Server connection verified": "已驗證伺服器連線",
|
||||
"Set as default": "設為預設",
|
||||
"Set Default Model": "設定預設模型",
|
||||
"Set Image Size": "設定圖片大小",
|
||||
"Set Steps": "設定步數",
|
||||
"Set Title Auto-Generation Model": "設定標題自動產生模型",
|
||||
"Set Voice": "設定語音",
|
||||
"Settings": "設定",
|
||||
"Settings saved successfully!": "成功儲存設定",
|
||||
"Share to OpenWebUI Community": "分享到 OpenWebUI 社群",
|
||||
"short-summary": "簡短摘要",
|
||||
"Show": "顯示",
|
||||
"Show Additional Params": "顯示額外參數",
|
||||
"Show shortcuts": "顯示快速鍵",
|
||||
"sidebar": "側邊欄",
|
||||
"Sign in": "登入",
|
||||
"Sign Out": "登出",
|
||||
"Sign up": "註冊",
|
||||
"Speech recognition error: {{error}}": "語音識別錯誤: {{error}}",
|
||||
"Speech-to-Text Engine": "語音轉文字引擎",
|
||||
"SpeechRecognition API is not supported in this browser.": "此瀏覽器不支持 SpeechRecognition API。",
|
||||
"Stop Sequence": "停止序列",
|
||||
"STT Settings": "語音轉文字設定",
|
||||
"Submit": "提交",
|
||||
"Success": "成功",
|
||||
"Successfully updated.": "更新成功。",
|
||||
"Sync All": "全部同步",
|
||||
"System": "系統",
|
||||
"System Prompt": "系統提示",
|
||||
"Tags": "標籤",
|
||||
"Temperature": "溫度",
|
||||
"Template": "模板",
|
||||
"Text Completion": "文字完成",
|
||||
"Text-to-Speech Engine": "文字轉語音引擎",
|
||||
"Tfs Z": "Tfs Z",
|
||||
"Theme": "主題",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "這確保你寶貴的對話安全地儲存到你的後台資料庫。謝謝!",
|
||||
"This setting does not sync across browsers or devices.": "此設定不會在瀏覽器或裝置間同步。",
|
||||
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:透過在每次替換後在聊天輸入中按 Tab 鍵連續更新多個變量。",
|
||||
"Title": "標題",
|
||||
"Title Auto-Generation": "標題自動產生",
|
||||
"Title Generation Prompt": "標題產生提示",
|
||||
"to": "到",
|
||||
"To access the available model names for downloading,": "要存取可供下載的模型名稱,",
|
||||
"To access the GGUF models available for downloading,": "要存取可供下載的 GGUF 模型,",
|
||||
"to chat input.": "到聊天輸入。",
|
||||
"Toggle settings": "切換設定",
|
||||
"Toggle sidebar": "切換側邊欄",
|
||||
"Top K": "Top K",
|
||||
"Top P": "Top P",
|
||||
"Trouble accessing Ollama?": "存取 Ollama 時遇到問題?",
|
||||
"TTS Settings": "文字轉語音設定",
|
||||
"Type Hugging Face Resolve (Download) URL": "輸入 Hugging Face 解析(下載)URL",
|
||||
"Uh-oh! There was an issue connecting to {{provider}}.": "哎呀!連線到 {{provider}} 時出現問題。",
|
||||
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "未知的文件類型 '{{file_type}}',但接受並視為純文字",
|
||||
"Update password": "更新密碼",
|
||||
"Upload a GGUF model": "上傳一個 GGUF 模型",
|
||||
"Upload files": "上傳文件",
|
||||
"Upload Progress": "上傳進度",
|
||||
"URL Mode": "URL 模式",
|
||||
"Use '#' in the prompt input to load and select your documents.": "使用 '#' 在提示輸入中以載入並選擇你的文件。",
|
||||
"Use Gravatar": "使用 Gravatar",
|
||||
"user": "使用者",
|
||||
"User Permissions": "使用者權限",
|
||||
"Users": "使用者",
|
||||
"Utilize": "使用",
|
||||
"Valid time units:": "有效時間單位:",
|
||||
"variable": "變量",
|
||||
"variable to have them replaced with clipboard content.": "變量將替換為剪貼簿內容。",
|
||||
"Version": "版本",
|
||||
"Web": "網頁",
|
||||
"WebUI Add-ons": "WebUI 擴充套件",
|
||||
"WebUI Settings": "WebUI 設定",
|
||||
"WebUI will make requests to": "WebUI 將會存取",
|
||||
"What’s New in": "全新內容",
|
||||
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "當歷史被關閉時,這個瀏覽器上的新聊天將不會出現在任何裝置的歷史記錄中。",
|
||||
"Whisper (Local)": "Whisper(本機)",
|
||||
"Write a prompt suggestion (e.g. Who are you?)": "寫一個提示建議(例如:你是誰?)",
|
||||
"Write a summary in 50 words that summarizes [topic or keyword].": "寫一個50字的摘要來概括[主題或關鍵詞]。",
|
||||
"You": "你",
|
||||
"You're a helpful assistant.": "你是一個有幫助的助手。",
|
||||
"You're now logged in.": "你已登入。"
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
import { APP_NAME } from '$lib/constants';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
// Backend
|
||||
export const WEBUI_NAME = writable(APP_NAME);
|
||||
export const config = writable(undefined);
|
||||
export const user = writable(undefined);
|
||||
|
||||
// Frontend
|
||||
export const theme = writable('dark');
|
||||
export const theme = writable('system');
|
||||
|
||||
export const chatId = writable('');
|
||||
|
||||
|
|
@ -32,3 +34,4 @@ export const documents = writable([
|
|||
|
||||
export const settings = writable({});
|
||||
export const showSettings = writable(false);
|
||||
export const showChangelog = writable(false);
|
||||
|
|
|
|||
|
|
@ -101,11 +101,10 @@ export const copyToClipboard = (text) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const checkVersion = (required, current) => {
|
||||
// Returns true when current version is below required
|
||||
export const compareVersion = (latest, current) => {
|
||||
return current === '0.0.0'
|
||||
? false
|
||||
: current.localeCompare(required, undefined, {
|
||||
: current.localeCompare(latest, undefined, {
|
||||
numeric: true,
|
||||
sensitivity: 'case',
|
||||
caseFirst: 'upper'
|
||||
|
|
|
|||
|
|
@ -1,17 +1,21 @@
|
|||
export const RAGTemplate = (context: string, query: string) => {
|
||||
let template = `Use the following context as your learned knowledge, inside <context></context> XML tags.
|
||||
<context>
|
||||
[context]
|
||||
</context>
|
||||
|
||||
When answer to user:
|
||||
- If you don't know, just say that you don't know.
|
||||
- If you don't know when you are not sure, ask for clarification.
|
||||
Avoid mentioning that you obtained the information from the context.
|
||||
And answer according to the language of the user's question.
|
||||
|
||||
Given the context information, answer the query.
|
||||
Query: [query]`;
|
||||
import { getRAGTemplate } from '$lib/apis/rag';
|
||||
|
||||
export const RAGTemplate = async (token: string, context: string, query: string) => {
|
||||
let template = await getRAGTemplate(token).catch(() => {
|
||||
return `Use the following context as your learned knowledge, inside <context></context> XML tags.
|
||||
<context>
|
||||
[context]
|
||||
</context>
|
||||
|
||||
When answer to user:
|
||||
- If you don't know, just say that you don't know.
|
||||
- If you don't know when you are not sure, ask for clarification.
|
||||
Avoid mentioning that you obtained the information from the context.
|
||||
And answer according to the language of the user's question.
|
||||
|
||||
Given the context information, answer the query.
|
||||
Query: [query]`;
|
||||
});
|
||||
|
||||
template = template.replace(/\[context\]/g, context);
|
||||
template = template.replace(/\[query\]/g, query);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue