mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-19 12:40:09 +01:00
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into render
This commit is contained in:
commit
86a31d7551
37
.github/workflows/check-translations.yaml
vendored
Normal file
37
.github/workflows/check-translations.yaml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# This Github action is for validation of all languages which translations are offered for
|
||||
# in the locales folder in `frontend/src`. All languages are compared to the EN translation
|
||||
# schema since that is the fallback language setting. This workflow will run on all PRs that
|
||||
# modify any files in the translation directory
|
||||
name: Verify translations files
|
||||
|
||||
concurrency:
|
||||
group: build-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "frontend/src/locales/**.js"
|
||||
|
||||
jobs:
|
||||
run-script:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Run verifyTranslations.mjs script
|
||||
run: |
|
||||
cd frontend/src/locales
|
||||
node verifyTranslations.mjs
|
||||
|
||||
- name: Fail job on error
|
||||
if: failure()
|
||||
run: exit 1
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -3,14 +3,17 @@
|
||||
"adoc",
|
||||
"aibitat",
|
||||
"AIbitat",
|
||||
"allm",
|
||||
"anythingllm",
|
||||
"Astra",
|
||||
"Chartable",
|
||||
"cleancss",
|
||||
"comkey",
|
||||
"cooldown",
|
||||
"cooldowns",
|
||||
"Deduplicator",
|
||||
"Dockerized",
|
||||
"docpath",
|
||||
"elevenlabs",
|
||||
"Embeddable",
|
||||
"epub",
|
||||
@ -32,7 +35,9 @@
|
||||
"opendocument",
|
||||
"openrouter",
|
||||
"Qdrant",
|
||||
"searxng",
|
||||
"Serper",
|
||||
"Serply",
|
||||
"textgenwebui",
|
||||
"togetherai",
|
||||
"vectordbs",
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<p align="center">
|
||||
<b>AnythingLLM:</b> The all-in-one AI app you were looking for.<br />
|
||||
Chat with your docs, use AI Agents, hyper-configurable, multi-user, & no fustrating set up required.
|
||||
Chat with your docs, use AI Agents, hyper-configurable, multi-user, & no frustrating set up required.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
|
@ -1,18 +1,41 @@
|
||||
const { setDataSigner } = require("../middleware/setDataSigner");
|
||||
const { verifyPayloadIntegrity } = require("../middleware/verifyIntegrity");
|
||||
const { reqBody } = require("../utils/http");
|
||||
const { validURL } = require("../utils/url");
|
||||
const RESYNC_METHODS = require("./resync");
|
||||
|
||||
function extensions(app) {
|
||||
if (!app) return;
|
||||
|
||||
app.post(
|
||||
"/ext/github-repo",
|
||||
[verifyPayloadIntegrity],
|
||||
"/ext/resync-source-document",
|
||||
[verifyPayloadIntegrity, setDataSigner],
|
||||
async function (request, response) {
|
||||
try {
|
||||
const loadGithubRepo = require("../utils/extensions/GithubRepo");
|
||||
const { type, options } = reqBody(request);
|
||||
if (!RESYNC_METHODS.hasOwnProperty(type)) throw new Error(`Type "${type}" is not a valid type to sync.`);
|
||||
return await RESYNC_METHODS[type](options, response);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
response.status(200).json({
|
||||
success: false,
|
||||
content: null,
|
||||
reason: e.message || "A processing error occurred.",
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
)
|
||||
|
||||
app.post(
|
||||
"/ext/github-repo",
|
||||
[verifyPayloadIntegrity, setDataSigner],
|
||||
async function (request, response) {
|
||||
try {
|
||||
const { loadGithubRepo } = require("../utils/extensions/GithubRepo");
|
||||
const { success, reason, data } = await loadGithubRepo(
|
||||
reqBody(request)
|
||||
reqBody(request),
|
||||
response,
|
||||
);
|
||||
response.status(200).json({
|
||||
success,
|
||||
@ -67,7 +90,7 @@ function extensions(app) {
|
||||
[verifyPayloadIntegrity],
|
||||
async function (request, response) {
|
||||
try {
|
||||
const loadYouTubeTranscript = require("../utils/extensions/YoutubeTranscript");
|
||||
const { loadYouTubeTranscript } = require("../utils/extensions/YoutubeTranscript");
|
||||
const { success, reason, data } = await loadYouTubeTranscript(
|
||||
reqBody(request)
|
||||
);
|
||||
@ -108,12 +131,13 @@ function extensions(app) {
|
||||
|
||||
app.post(
|
||||
"/ext/confluence",
|
||||
[verifyPayloadIntegrity],
|
||||
[verifyPayloadIntegrity, setDataSigner],
|
||||
async function (request, response) {
|
||||
try {
|
||||
const loadConfluence = require("../utils/extensions/Confluence");
|
||||
const { loadConfluence } = require("../utils/extensions/Confluence");
|
||||
const { success, reason, data } = await loadConfluence(
|
||||
reqBody(request)
|
||||
reqBody(request),
|
||||
response
|
||||
);
|
||||
response.status(200).json({ success, reason, data });
|
||||
} catch (e) {
|
||||
|
113
collector/extensions/resync/index.js
Normal file
113
collector/extensions/resync/index.js
Normal file
@ -0,0 +1,113 @@
|
||||
const { getLinkText } = require("../../processLink");
|
||||
|
||||
/**
|
||||
* Fetches the content of a raw link. Returns the content as a text string of the link in question.
|
||||
* @param {object} data - metadata from document (eg: link)
|
||||
* @param {import("../../middleware/setDataSigner").ResponseWithSigner} response
|
||||
*/
|
||||
async function resyncLink({ link }, response) {
|
||||
if (!link) throw new Error('Invalid link provided');
|
||||
try {
|
||||
const { success, content = null } = await getLinkText(link);
|
||||
if (!success) throw new Error(`Failed to sync link content. ${reason}`);
|
||||
response.status(200).json({ success, content });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
response.status(200).json({
|
||||
success: false,
|
||||
content: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the content of a YouTube link. Returns the content as a text string of the video in question.
|
||||
* We offer this as there may be some videos where a transcription could be manually edited after initial scraping
|
||||
* but in general - transcriptions often never change.
|
||||
* @param {object} data - metadata from document (eg: link)
|
||||
* @param {import("../../middleware/setDataSigner").ResponseWithSigner} response
|
||||
*/
|
||||
async function resyncYouTube({ link }, response) {
|
||||
if (!link) throw new Error('Invalid link provided');
|
||||
try {
|
||||
const { fetchVideoTranscriptContent } = require("../../utils/extensions/YoutubeTranscript");
|
||||
const { success, reason, content } = await fetchVideoTranscriptContent({ url: link });
|
||||
if (!success) throw new Error(`Failed to sync YouTube video transcript. ${reason}`);
|
||||
response.status(200).json({ success, content });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
response.status(200).json({
|
||||
success: false,
|
||||
content: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the content of a specific confluence page via its chunkSource.
|
||||
* Returns the content as a text string of the page in question and only that page.
|
||||
* @param {object} data - metadata from document (eg: chunkSource)
|
||||
* @param {import("../../middleware/setDataSigner").ResponseWithSigner} response
|
||||
*/
|
||||
async function resyncConfluence({ chunkSource }, response) {
|
||||
if (!chunkSource) throw new Error('Invalid source property provided');
|
||||
try {
|
||||
// Confluence data is `payload` encrypted. So we need to expand its
|
||||
// encrypted payload back into query params so we can reFetch the page with same access token/params.
|
||||
const source = response.locals.encryptionWorker.expandPayload(chunkSource);
|
||||
const { fetchConfluencePage } = require("../../utils/extensions/Confluence");
|
||||
const { success, reason, content } = await fetchConfluencePage({
|
||||
pageUrl: `https:${source.pathname}`, // need to add back the real protocol
|
||||
baseUrl: source.searchParams.get('baseUrl'),
|
||||
accessToken: source.searchParams.get('token'),
|
||||
username: source.searchParams.get('username'),
|
||||
});
|
||||
|
||||
if (!success) throw new Error(`Failed to sync Confluence page content. ${reason}`);
|
||||
response.status(200).json({ success, content });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
response.status(200).json({
|
||||
success: false,
|
||||
content: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the content of a specific confluence page via its chunkSource.
|
||||
* Returns the content as a text string of the page in question and only that page.
|
||||
* @param {object} data - metadata from document (eg: chunkSource)
|
||||
* @param {import("../../middleware/setDataSigner").ResponseWithSigner} response
|
||||
*/
|
||||
async function resyncGithub({ chunkSource }, response) {
|
||||
if (!chunkSource) throw new Error('Invalid source property provided');
|
||||
try {
|
||||
// Github file data is `payload` encrypted (might contain PAT). So we need to expand its
|
||||
// encrypted payload back into query params so we can reFetch the page with same access token/params.
|
||||
const source = response.locals.encryptionWorker.expandPayload(chunkSource);
|
||||
const { fetchGithubFile } = require("../../utils/extensions/GithubRepo");
|
||||
const { success, reason, content } = await fetchGithubFile({
|
||||
repoUrl: `https:${source.pathname}`, // need to add back the real protocol
|
||||
branch: source.searchParams.get('branch'),
|
||||
accessToken: source.searchParams.get('pat'),
|
||||
sourceFilePath: source.searchParams.get('path'),
|
||||
});
|
||||
|
||||
if (!success) throw new Error(`Failed to sync Github file content. ${reason}`);
|
||||
response.status(200).json({ success, content });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
response.status(200).json({
|
||||
success: false,
|
||||
content: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
link: resyncLink,
|
||||
youtube: resyncYouTube,
|
||||
confluence: resyncConfluence,
|
||||
github: resyncGithub,
|
||||
}
|
41
collector/middleware/setDataSigner.js
Normal file
41
collector/middleware/setDataSigner.js
Normal file
@ -0,0 +1,41 @@
|
||||
const { EncryptionWorker } = require("../utils/EncryptionWorker");
|
||||
const { CommunicationKey } = require("../utils/comKey");
|
||||
|
||||
/**
|
||||
* Express Response Object interface with defined encryptionWorker attached to locals property.
|
||||
* @typedef {import("express").Response & import("express").Response['locals'] & {encryptionWorker: EncryptionWorker} } ResponseWithSigner
|
||||
*/
|
||||
|
||||
// You can use this middleware to assign the EncryptionWorker to the response locals
|
||||
// property so that if can be used to encrypt/decrypt arbitrary data via response object.
|
||||
// eg: Encrypting API keys in chunk sources.
|
||||
|
||||
// The way this functions is that the rolling RSA Communication Key is used server-side to private-key encrypt the raw
|
||||
// key of the persistent EncryptionManager credentials. Since EncryptionManager credentials do _not_ roll, we should not send them
|
||||
// even between server<>collector in plaintext because if the user configured the server/collector to be public they could technically
|
||||
// be exposing the key in transit via the X-Payload-Signer header. Even if this risk is minimal we should not do this.
|
||||
|
||||
// This middleware uses the CommunicationKey public key to first decrypt the base64 representation of the EncryptionManager credentials
|
||||
// and then loads that in to the EncryptionWorker as a buffer so we can use the same credentials across the system. Should we ever break the
|
||||
// collector out into its own service this would still work without SSL/TLS.
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import("express").Request} request
|
||||
* @param {import("express").Response} response
|
||||
* @param {import("express").NextFunction} next
|
||||
*/
|
||||
function setDataSigner(request, response, next) {
|
||||
const comKey = new CommunicationKey();
|
||||
const encryptedPayloadSigner = request.header("X-Payload-Signer");
|
||||
if (!encryptedPayloadSigner) console.log('Failed to find signed-payload to set encryption worker! Encryption calls will fail.');
|
||||
|
||||
const decryptedPayloadSignerKey = comKey.decrypt(encryptedPayloadSigner);
|
||||
const encryptionWorker = new EncryptionWorker(decryptedPayloadSignerKey);
|
||||
response.locals.encryptionWorker = encryptionWorker;
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setDataSigner
|
||||
}
|
77
collector/utils/EncryptionWorker/index.js
Normal file
77
collector/utils/EncryptionWorker/index.js
Normal file
@ -0,0 +1,77 @@
|
||||
const crypto = require("crypto");
|
||||
|
||||
// Differs from EncryptionManager in that is does not set or define the keys that will be used
|
||||
// to encrypt or read data and it must be told the key (as base64 string) explicitly that will be used and is provided to
|
||||
// the class on creation. This key should be the same `key` that is used by the EncryptionManager class.
|
||||
class EncryptionWorker {
|
||||
constructor(presetKeyBase64 = "") {
|
||||
this.key = Buffer.from(presetKeyBase64, "base64");
|
||||
this.algorithm = "aes-256-cbc";
|
||||
this.separator = ":";
|
||||
}
|
||||
|
||||
log(text, ...args) {
|
||||
console.log(`\x1b[36m[EncryptionManager]\x1b[0m ${text}`, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Give a chunk source, parse its payload query param and expand that object back into the URL
|
||||
* as additional query params
|
||||
* @param {string} chunkSource
|
||||
* @returns {URL} Javascript URL object with query params decrypted from payload query param.
|
||||
*/
|
||||
expandPayload(chunkSource = "") {
|
||||
try {
|
||||
const url = new URL(chunkSource);
|
||||
if (!url.searchParams.has("payload")) return url;
|
||||
|
||||
const decryptedPayload = this.decrypt(url.searchParams.get("payload"));
|
||||
const encodedParams = JSON.parse(decryptedPayload);
|
||||
url.searchParams.delete("payload"); // remove payload prop
|
||||
|
||||
// Add all query params needed to replay as query params
|
||||
Object.entries(encodedParams).forEach(([key, value]) =>
|
||||
url.searchParams.append(key, value)
|
||||
);
|
||||
return url;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return new URL(chunkSource);
|
||||
}
|
||||
|
||||
encrypt(plainTextString = null) {
|
||||
try {
|
||||
if (!plainTextString)
|
||||
throw new Error("Empty string is not valid for this method.");
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
|
||||
const encrypted = cipher.update(plainTextString, "utf8", "hex");
|
||||
return [
|
||||
encrypted + cipher.final("hex"),
|
||||
Buffer.from(iv).toString("hex"),
|
||||
].join(this.separator);
|
||||
} catch (e) {
|
||||
this.log(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
decrypt(encryptedString) {
|
||||
try {
|
||||
const [encrypted, iv] = encryptedString.split(this.separator);
|
||||
if (!iv) throw new Error("IV not found");
|
||||
const decipher = crypto.createDecipheriv(
|
||||
this.algorithm,
|
||||
this.key,
|
||||
Buffer.from(iv, "hex")
|
||||
);
|
||||
return decipher.update(encrypted, "hex", "utf8") + decipher.final("utf8");
|
||||
} catch (e) {
|
||||
this.log(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { EncryptionWorker };
|
@ -40,6 +40,15 @@ class CommunicationKey {
|
||||
} catch {}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use the rolling public-key to decrypt arbitrary data that was encrypted via the private key on the server side CommunicationKey class
|
||||
// that we know was done with the same key-pair and the given input is in base64 format already.
|
||||
// Returns plaintext string of the data that was encrypted.
|
||||
decrypt(base64String = "") {
|
||||
return crypto
|
||||
.publicDecrypt(this.#readPublicKey(), Buffer.from(base64String, "base64"))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { CommunicationKey };
|
||||
|
@ -3,44 +3,19 @@ const path = require("path");
|
||||
const { default: slugify } = require("slugify");
|
||||
const { v4 } = require("uuid");
|
||||
const UrlPattern = require("url-pattern");
|
||||
const { writeToServerDocuments } = require("../../files");
|
||||
const { writeToServerDocuments, sanitizeFileName } = require("../../files");
|
||||
const { tokenizeString } = require("../../tokenizer");
|
||||
const {
|
||||
ConfluencePagesLoader,
|
||||
} = require("langchain/document_loaders/web/confluence");
|
||||
|
||||
function validSpaceUrl(spaceUrl = "") {
|
||||
// Atlassian default URL match
|
||||
const atlassianPattern = new UrlPattern(
|
||||
"https\\://(:subdomain).atlassian.net/wiki/spaces/(:spaceKey)*"
|
||||
);
|
||||
const atlassianMatch = atlassianPattern.match(spaceUrl);
|
||||
if (atlassianMatch) {
|
||||
return { valid: true, result: atlassianMatch };
|
||||
}
|
||||
|
||||
let customMatch = null;
|
||||
[
|
||||
"https\\://(:subdomain.):domain.:tld/wiki/spaces/(:spaceKey)*", // Custom Confluence space
|
||||
"https\\://(:subdomain.):domain.:tld/display/(:spaceKey)*", // Custom Confluence space + Human-readable space tag.
|
||||
].forEach((matchPattern) => {
|
||||
if (!!customMatch) return;
|
||||
const pattern = new UrlPattern(matchPattern);
|
||||
customMatch = pattern.match(spaceUrl);
|
||||
});
|
||||
|
||||
if (customMatch) {
|
||||
customMatch.customDomain =
|
||||
(customMatch.subdomain ? `${customMatch.subdomain}.` : "") + //
|
||||
`${customMatch.domain}.${customMatch.tld}`;
|
||||
return { valid: true, result: customMatch, custom: true };
|
||||
}
|
||||
|
||||
// No match
|
||||
return { valid: false, result: null };
|
||||
}
|
||||
|
||||
async function loadConfluence({ pageUrl, username, accessToken }) {
|
||||
/**
|
||||
* Load Confluence documents from a spaceID and Confluence credentials
|
||||
* @param {object} args - forwarded request body params
|
||||
* @param {import("../../../middleware/setDataSigner").ResponseWithSigner} response - Express response object with encryptionWorker
|
||||
* @returns
|
||||
*/
|
||||
async function loadConfluence({ pageUrl, username, accessToken }, response) {
|
||||
if (!pageUrl || !username || !accessToken) {
|
||||
return {
|
||||
success: false,
|
||||
@ -49,21 +24,16 @@ async function loadConfluence({ pageUrl, username, accessToken }) {
|
||||
};
|
||||
}
|
||||
|
||||
const validSpace = validSpaceUrl(pageUrl);
|
||||
if (!validSpace.result) {
|
||||
const { valid, result } = validSpaceUrl(pageUrl);
|
||||
if (!valid) {
|
||||
return {
|
||||
success: false,
|
||||
reason:
|
||||
"Confluence space URL is not in the expected format of https://domain.atlassian.net/wiki/space/~SPACEID/* or https://customDomain/wiki/space/~SPACEID/*",
|
||||
"Confluence space URL is not in the expected format of one of https://domain.atlassian.net/wiki/space/~SPACEID/* or https://customDomain/wiki/space/~SPACEID/* or https://customDomain/display/~SPACEID/*",
|
||||
};
|
||||
}
|
||||
|
||||
const { subdomain, customDomain, spaceKey } = validSpace.result;
|
||||
let baseUrl = `https://${subdomain}.atlassian.net/wiki`;
|
||||
if (customDomain) {
|
||||
baseUrl = `https://${customDomain}/wiki`;
|
||||
}
|
||||
|
||||
const { apiBase: baseUrl, spaceKey, subdomain } = result;
|
||||
console.log(`-- Working Confluence ${baseUrl} --`);
|
||||
const loader = new ConfluencePagesLoader({
|
||||
baseUrl,
|
||||
@ -106,7 +76,10 @@ async function loadConfluence({ pageUrl, username, accessToken }) {
|
||||
docAuthor: subdomain,
|
||||
description: doc.metadata.title,
|
||||
docSource: `${subdomain} Confluence`,
|
||||
chunkSource: `confluence://${doc.metadata.url}`,
|
||||
chunkSource: generateChunkSource(
|
||||
{ doc, baseUrl, accessToken, username },
|
||||
response.locals.encryptionWorker
|
||||
),
|
||||
published: new Date().toLocaleString(),
|
||||
wordCount: doc.pageContent.split(" ").length,
|
||||
pageContent: doc.pageContent,
|
||||
@ -116,11 +89,11 @@ async function loadConfluence({ pageUrl, username, accessToken }) {
|
||||
console.log(
|
||||
`[Confluence Loader]: Saving ${doc.metadata.title} to ${outFolder}`
|
||||
);
|
||||
writeToServerDocuments(
|
||||
data,
|
||||
`${slugify(doc.metadata.title)}-${data.id}`,
|
||||
outFolderPath
|
||||
|
||||
const fileName = sanitizeFileName(
|
||||
`${slugify(doc.metadata.title)}-${data.id}`
|
||||
);
|
||||
writeToServerDocuments(data, fileName, outFolderPath);
|
||||
});
|
||||
|
||||
return {
|
||||
@ -133,4 +106,194 @@ async function loadConfluence({ pageUrl, username, accessToken }) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = loadConfluence;
|
||||
/**
|
||||
* Gets the page content from a specific Confluence page, not all pages in a workspace.
|
||||
* @returns
|
||||
*/
|
||||
async function fetchConfluencePage({
|
||||
pageUrl,
|
||||
baseUrl,
|
||||
username,
|
||||
accessToken,
|
||||
}) {
|
||||
if (!pageUrl || !baseUrl || !username || !accessToken) {
|
||||
return {
|
||||
success: false,
|
||||
content: null,
|
||||
reason:
|
||||
"You need either a username and access token, or a personal access token (PAT), to use the Confluence connector.",
|
||||
};
|
||||
}
|
||||
|
||||
const { valid, result } = validSpaceUrl(pageUrl);
|
||||
if (!valid) {
|
||||
return {
|
||||
success: false,
|
||||
content: null,
|
||||
reason:
|
||||
"Confluence space URL is not in the expected format of https://domain.atlassian.net/wiki/space/~SPACEID/* or https://customDomain/wiki/space/~SPACEID/*",
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`-- Working Confluence Page ${pageUrl} --`);
|
||||
const { spaceKey } = result;
|
||||
const loader = new ConfluencePagesLoader({
|
||||
baseUrl,
|
||||
spaceKey,
|
||||
username,
|
||||
accessToken,
|
||||
});
|
||||
|
||||
const { docs, error } = await loader
|
||||
.load()
|
||||
.then((docs) => {
|
||||
return { docs, error: null };
|
||||
})
|
||||
.catch((e) => {
|
||||
return {
|
||||
docs: [],
|
||||
error: e.message?.split("Error:")?.[1] || e.message,
|
||||
};
|
||||
});
|
||||
|
||||
if (!docs.length || !!error) {
|
||||
return {
|
||||
success: false,
|
||||
reason: error ?? "No pages found for that Confluence space.",
|
||||
content: null,
|
||||
};
|
||||
}
|
||||
|
||||
const targetDocument = docs.find(
|
||||
(doc) => doc.pageContent && doc.metadata.url === pageUrl
|
||||
);
|
||||
if (!targetDocument) {
|
||||
return {
|
||||
success: false,
|
||||
reason: "Target page could not be found in Confluence space.",
|
||||
content: null,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
reason: null,
|
||||
content: targetDocument.pageContent,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A match result for a url-pattern of a Confluence URL
|
||||
* @typedef {Object} ConfluenceMatchResult
|
||||
* @property {string} subdomain - the subdomain of an organization's Confluence space
|
||||
* @property {string} spaceKey - the spaceKey of an organization that determines the documents to collect.
|
||||
* @property {string} apiBase - the correct REST API url to use for loader.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generates the correct API base URL for interfacing with the Confluence REST API
|
||||
* depending on the URL pattern being used since there are various ways to host/access a
|
||||
* Confluence space.
|
||||
* @param {ConfluenceMatchResult} matchResult - result from `url-pattern`.match
|
||||
* @param {boolean} isCustomDomain - determines if we need to coerce the subpath of the provided URL
|
||||
* @returns {string} - the resulting REST API URL
|
||||
*/
|
||||
function generateAPIBaseUrl(matchResult = {}, isCustomDomain = false) {
|
||||
const { subdomain } = matchResult;
|
||||
let subpath = isCustomDomain ? `` : `/wiki`;
|
||||
if (isCustomDomain) return `https://${customDomain}${subpath}`;
|
||||
return `https://${subdomain}.atlassian.net${subpath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and parses the correct information from a given Confluence URL
|
||||
* @param {string} spaceUrl - The organization's Confluence URL to parse
|
||||
* @returns {{
|
||||
* valid: boolean,
|
||||
* result: (ConfluenceMatchResult|null),
|
||||
* }}
|
||||
*/
|
||||
function validSpaceUrl(spaceUrl = "") {
|
||||
let matchResult;
|
||||
const patterns = {
|
||||
default: new UrlPattern(
|
||||
"https\\://(:subdomain).atlassian.net/wiki/spaces/(:spaceKey)*"
|
||||
),
|
||||
subdomain: new UrlPattern(
|
||||
"https\\://(:subdomain.):domain.:tld/wiki/spaces/(:spaceKey)*"
|
||||
),
|
||||
custom: new UrlPattern(
|
||||
"https\\://(:subdomain.):domain.:tld/display/(:spaceKey)*"
|
||||
),
|
||||
};
|
||||
|
||||
// If using the default Atlassian Confluence URL pattern.
|
||||
// We can proceed because the Library/API can use this base url scheme.
|
||||
matchResult = patterns.default.match(spaceUrl);
|
||||
if (matchResult)
|
||||
return {
|
||||
valid: matchResult.hasOwnProperty("spaceKey"),
|
||||
result: {
|
||||
...matchResult,
|
||||
apiBase: generateAPIBaseUrl(matchResult),
|
||||
},
|
||||
};
|
||||
|
||||
// If using a custom subdomain Confluence URL pattern.
|
||||
// We need to attach the customDomain as a property to the match result
|
||||
// so we can form the correct REST API base from the subdomain.
|
||||
matchResult = patterns.subdomain.match(spaceUrl);
|
||||
if (matchResult) {
|
||||
return {
|
||||
valid: matchResult.hasOwnProperty("spaceKey"),
|
||||
result: {
|
||||
...matchResult,
|
||||
apiBase: generateAPIBaseUrl(matchResult),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// If using a base FQDN Confluence URL pattern.
|
||||
// We need to attach the customDomain as a property to the match result
|
||||
// so we can form the correct REST API base from the root domain since /display/ is basically a URL mask.
|
||||
matchResult = patterns.custom.match(spaceUrl);
|
||||
if (matchResult) {
|
||||
return {
|
||||
valid: matchResult.hasOwnProperty("spaceKey"),
|
||||
result: {
|
||||
...matchResult,
|
||||
apiBase: generateAPIBaseUrl(matchResult, true),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// No match
|
||||
return { valid: false, result: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the full chunkSource for a specific Confluence page so that we can resync it later.
|
||||
* This data is encrypted into a single `payload` query param so we can replay credentials later
|
||||
* since this was encrypted with the systems persistent password and salt.
|
||||
* @param {object} chunkSourceInformation
|
||||
* @param {import("../../EncryptionWorker").EncryptionWorker} encryptionWorker
|
||||
* @returns {string}
|
||||
*/
|
||||
function generateChunkSource(
|
||||
{ doc, baseUrl, accessToken, username },
|
||||
encryptionWorker
|
||||
) {
|
||||
const payload = {
|
||||
baseUrl,
|
||||
token: accessToken,
|
||||
username,
|
||||
};
|
||||
return `confluence://${doc.metadata.url}?payload=${encryptionWorker.encrypt(
|
||||
JSON.stringify(payload)
|
||||
)}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
loadConfluence,
|
||||
fetchConfluencePage,
|
||||
};
|
||||
|
@ -150,6 +150,36 @@ class RepoLoader {
|
||||
this.branches = [...new Set(branches.flat())];
|
||||
return this.#branchPrefSort(this.branches);
|
||||
}
|
||||
|
||||
async fetchSingleFile(sourceFilePath) {
|
||||
try {
|
||||
return fetch(
|
||||
`https://api.github.com/repos/${this.author}/${this.project}/contents/${sourceFilePath}?ref=${this.branch}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
...(!!this.accessToken
|
||||
? { Authorization: `Bearer ${this.accessToken}` }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => {
|
||||
if (res.ok) return res.json();
|
||||
throw new Error(`Failed to fetch from Github API: ${res.statusText}`);
|
||||
})
|
||||
.then((json) => {
|
||||
if (json.hasOwnProperty("status") || !json.hasOwnProperty("content"))
|
||||
throw new Error(json?.message || "missing content");
|
||||
return atob(json.content);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`RepoLoader.fetchSingleFile`, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RepoLoader;
|
||||
|
@ -6,7 +6,13 @@ const { v4 } = require("uuid");
|
||||
const { writeToServerDocuments, documentsFolder } = require("../../files");
|
||||
const { tokenizeString } = require("../../tokenizer");
|
||||
|
||||
async function loadGithubRepo(args) {
|
||||
/**
|
||||
* Load in a Github Repo recursively or just the top level if no PAT is provided
|
||||
* @param {object} args - forwarded request body params
|
||||
* @param {import("../../../middleware/setDataSigner").ResponseWithSigner} response - Express response object with encryptionWorker
|
||||
* @returns
|
||||
*/
|
||||
async function loadGithubRepo(args, response) {
|
||||
const repo = new RepoLoader(args);
|
||||
await repo.init();
|
||||
|
||||
@ -43,7 +49,11 @@ async function loadGithubRepo(args) {
|
||||
docAuthor: repo.author,
|
||||
description: "No description found.",
|
||||
docSource: doc.metadata.source,
|
||||
chunkSource: `link://${doc.metadata.repository}/blob/${doc.metadata.branch}/${doc.metadata.source}`,
|
||||
chunkSource: generateChunkSource(
|
||||
repo,
|
||||
doc,
|
||||
response.locals.encryptionWorker
|
||||
),
|
||||
published: new Date().toLocaleString(),
|
||||
wordCount: doc.pageContent.split(" ").length,
|
||||
pageContent: doc.pageContent,
|
||||
@ -72,4 +82,69 @@ async function loadGithubRepo(args) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = loadGithubRepo;
|
||||
/**
|
||||
* Gets the page content from a specific source file in a give Github Repo, not all items in a repo.
|
||||
* @returns
|
||||
*/
|
||||
async function fetchGithubFile({
|
||||
repoUrl,
|
||||
branch,
|
||||
accessToken = null,
|
||||
sourceFilePath,
|
||||
}) {
|
||||
const repo = new RepoLoader({
|
||||
repo: repoUrl,
|
||||
branch,
|
||||
accessToken,
|
||||
});
|
||||
await repo.init();
|
||||
|
||||
if (!repo.ready)
|
||||
return {
|
||||
success: false,
|
||||
content: null,
|
||||
reason: "Could not prepare Github repo for loading! Check URL or PAT.",
|
||||
};
|
||||
|
||||
console.log(
|
||||
`-- Working Github ${repo.author}/${repo.project}:${repo.branch} file:${sourceFilePath} --`
|
||||
);
|
||||
const fileContent = await repo.fetchSingleFile(sourceFilePath);
|
||||
if (!fileContent) {
|
||||
return {
|
||||
success: false,
|
||||
reason: "Target file returned a null content response.",
|
||||
content: null,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
reason: null,
|
||||
content: fileContent,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the full chunkSource for a specific file so that we can resync it later.
|
||||
* This data is encrypted into a single `payload` query param so we can replay credentials later
|
||||
* since this was encrypted with the systems persistent password and salt.
|
||||
* @param {RepoLoader} repo
|
||||
* @param {import("@langchain/core/documents").Document} doc
|
||||
* @param {import("../../EncryptionWorker").EncryptionWorker} encryptionWorker
|
||||
* @returns {string}
|
||||
*/
|
||||
function generateChunkSource(repo, doc, encryptionWorker) {
|
||||
const payload = {
|
||||
owner: repo.author,
|
||||
project: repo.project,
|
||||
branch: repo.branch,
|
||||
path: doc.metadata.source,
|
||||
pat: !!repo.accessToken ? repo.accessToken : null,
|
||||
};
|
||||
return `github://${repo.repo}?payload=${encryptionWorker.encrypt(
|
||||
JSON.stringify(payload)
|
||||
)}`;
|
||||
}
|
||||
|
||||
module.exports = { loadGithubRepo, fetchGithubFile };
|
||||
|
@ -10,7 +10,7 @@ const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
async function discoverLinks(startUrl, depth = 1, maxLinks = 20) {
|
||||
const baseUrl = new URL(startUrl).origin;
|
||||
const baseUrl = new URL(startUrl);
|
||||
const discoveredLinks = new Set();
|
||||
const pendingLinks = [startUrl];
|
||||
let currentLevel = 0;
|
||||
@ -66,8 +66,12 @@ function extractLinks(html, baseUrl) {
|
||||
for (const link of links) {
|
||||
const href = link.getAttribute("href");
|
||||
if (href) {
|
||||
const absoluteUrl = new URL(href, baseUrl).href;
|
||||
if (absoluteUrl.startsWith(baseUrl)) {
|
||||
const absoluteUrl = new URL(href, baseUrl.href).href;
|
||||
if (
|
||||
absoluteUrl.startsWith(
|
||||
baseUrl.origin + baseUrl.pathname.split("/").slice(0, -1).join("/")
|
||||
)
|
||||
) {
|
||||
extractedLinks.add(absoluteUrl);
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,13 @@ function validYoutubeVideoUrl(link) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async function loadYouTubeTranscript({ url }) {
|
||||
async function fetchVideoTranscriptContent({ url }) {
|
||||
if (!validYoutubeVideoUrl(url)) {
|
||||
return {
|
||||
success: false,
|
||||
reason: "Invalid URL. Should be youtu.be or youtube.com/watch.",
|
||||
content: null,
|
||||
metadata: {},
|
||||
};
|
||||
}
|
||||
|
||||
@ -51,6 +53,8 @@ async function loadYouTubeTranscript({ url }) {
|
||||
return {
|
||||
success: false,
|
||||
reason: error ?? "No transcript found for that YouTube video.",
|
||||
content: null,
|
||||
metadata: {},
|
||||
};
|
||||
}
|
||||
|
||||
@ -60,9 +64,30 @@ async function loadYouTubeTranscript({ url }) {
|
||||
return {
|
||||
success: false,
|
||||
reason: "No transcript could be parsed for that YouTube video.",
|
||||
content: null,
|
||||
metadata: {},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
reason: null,
|
||||
content,
|
||||
metadata,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadYouTubeTranscript({ url }) {
|
||||
const transcriptResults = await fetchVideoTranscriptContent({ url });
|
||||
if (!transcriptResults.success) {
|
||||
return {
|
||||
success: false,
|
||||
reason:
|
||||
transcriptResults.reason ||
|
||||
"An unknown error occurred during transcription retrieval",
|
||||
};
|
||||
}
|
||||
const { content, metadata } = transcriptResults;
|
||||
const outFolder = slugify(
|
||||
`${metadata.author} YouTube transcripts`
|
||||
).toLowerCase();
|
||||
@ -76,7 +101,7 @@ async function loadYouTubeTranscript({ url }) {
|
||||
docAuthor: metadata.author,
|
||||
description: metadata.description,
|
||||
docSource: url,
|
||||
chunkSource: `link://${url}`,
|
||||
chunkSource: `youtube://${url}`,
|
||||
published: new Date().toLocaleString(),
|
||||
wordCount: content.split(" ").length,
|
||||
pageContent: content,
|
||||
@ -101,4 +126,7 @@ async function loadYouTubeTranscript({ url }) {
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = loadYouTubeTranscript;
|
||||
module.exports = {
|
||||
loadYouTubeTranscript,
|
||||
fetchVideoTranscriptContent,
|
||||
};
|
||||
|
@ -131,6 +131,11 @@ function normalizePath(filepath = "") {
|
||||
return result;
|
||||
}
|
||||
|
||||
function sanitizeFileName(fileName) {
|
||||
if (!fileName) return fileName;
|
||||
return fileName.replace(/[<>:"\/\\|?*]/g, "");
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
documentsFolder,
|
||||
trashFile,
|
||||
@ -140,4 +145,5 @@ module.exports = {
|
||||
wipeCollectorStorage,
|
||||
normalizePath,
|
||||
isWithin,
|
||||
sanitizeFileName,
|
||||
};
|
||||
|
@ -2,6 +2,8 @@ SERVER_PORT=3001
|
||||
STORAGE_DIR="/app/server/storage"
|
||||
UID='1000'
|
||||
GID='1000'
|
||||
# SIG_KEY='passphrase' # Please generate random string at least 32 chars long.
|
||||
# SIG_SALT='salt' # Please generate random string at least 32 chars long.
|
||||
# JWT_SECRET="my-random-string-for-seeding" # Only needed if AUTH_TOKEN is set. Please generate random string at least 12 chars long.
|
||||
|
||||
###########################################
|
||||
@ -134,6 +136,12 @@ GID='1000'
|
||||
# LITE_LLM_BASE_PATH='http://127.0.0.1:4000'
|
||||
# LITE_LLM_API_KEY='sk-123abc'
|
||||
|
||||
# EMBEDDING_ENGINE='generic-openai'
|
||||
# EMBEDDING_MODEL_PREF='text-embedding-ada-002'
|
||||
# EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
|
||||
# EMBEDDING_BASE_PATH='http://127.0.0.1:4000'
|
||||
# GENERIC_OPEN_AI_EMBEDDING_API_KEY='sk-123abc'
|
||||
|
||||
###########################################
|
||||
######## Vector Database Selection ########
|
||||
###########################################
|
||||
@ -245,3 +253,6 @@ GID='1000'
|
||||
|
||||
#------ Serply.io ----------- https://serply.io/
|
||||
# AGENT_SERPLY_API_KEY=
|
||||
|
||||
#------ SearXNG ----------- https://github.com/searxng/searxng
|
||||
# AGENT_SEARXNG_API_URL=
|
@ -87,46 +87,51 @@ mintplexlabs/anythingllm;
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> Docker Compose</td>
|
||||
<td>
|
||||
version: '3.8'
|
||||
services:
|
||||
anythingllm:
|
||||
image: mintplexlabs/anythingllm
|
||||
container_name: anythingllm
|
||||
ports:
|
||||
- "3001:3001"
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
environment:
|
||||
# Adjust for your environemnt
|
||||
- STORAGE_DIR=/app/server/storage
|
||||
- JWT_SECRET="make this a large list of random numbers and letters 20+"
|
||||
- LLM_PROVIDER=ollama
|
||||
- OLLAMA_BASE_PATH=http://127.0.0.1:11434
|
||||
- OLLAMA_MODEL_PREF=llama2
|
||||
- OLLAMA_MODEL_TOKEN_LIMIT=4096
|
||||
- EMBEDDING_ENGINE=ollama
|
||||
- EMBEDDING_BASE_PATH=http://127.0.0.1:11434
|
||||
- EMBEDDING_MODEL_PREF=nomic-embed-text:latest
|
||||
- EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
|
||||
- VECTOR_DB=lancedb
|
||||
- WHISPER_PROVIDER=local
|
||||
- TTS_PROVIDER=native
|
||||
- PASSWORDMINCHAR=8
|
||||
- AGENT_SERPER_DEV_KEY="SERPER DEV API KEY"
|
||||
- AGENT_SERPLY_API_KEY="Serply.io API KEY"
|
||||
volumes:
|
||||
- anythingllm_storage:/app/server/storage
|
||||
restart: always
|
||||
<td> Docker Compose</td>
|
||||
<td>
|
||||
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
anythingllm:
|
||||
image: mintplexlabs/anythingllm
|
||||
container_name: anythingllm
|
||||
ports:
|
||||
- "3001:3001"
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
environment:
|
||||
# Adjust for your environment
|
||||
- STORAGE_DIR=/app/server/storage
|
||||
- JWT_SECRET="make this a large list of random numbers and letters 20+"
|
||||
- LLM_PROVIDER=ollama
|
||||
- OLLAMA_BASE_PATH=http://127.0.0.1:11434
|
||||
- OLLAMA_MODEL_PREF=llama2
|
||||
- OLLAMA_MODEL_TOKEN_LIMIT=4096
|
||||
- EMBEDDING_ENGINE=ollama
|
||||
- EMBEDDING_BASE_PATH=http://127.0.0.1:11434
|
||||
- EMBEDDING_MODEL_PREF=nomic-embed-text:latest
|
||||
- EMBEDDING_MODEL_MAX_CHUNK_LENGTH=8192
|
||||
- VECTOR_DB=lancedb
|
||||
- WHISPER_PROVIDER=local
|
||||
- TTS_PROVIDER=native
|
||||
- PASSWORDMINCHAR=8
|
||||
- AGENT_SERPER_DEV_KEY="SERPER DEV API KEY"
|
||||
- AGENT_SERPLY_API_KEY="Serply.io API KEY"
|
||||
volumes:
|
||||
- anythingllm_storage:/app/server/storage
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
anythingllm_storage:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /path/on/local/disk
|
||||
```
|
||||
|
||||
volumes:
|
||||
anythingllm_storage:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /path/on/local/disk
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -6,9 +6,12 @@
|
||||
"scripts": {
|
||||
"dev": "nodemon -e js,jsx,css --watch src --exec \"yarn run dev:preview\"",
|
||||
"dev:preview": "yarn run dev:build && yarn serve . -p 3080 --no-clipboard",
|
||||
"dev:build": "vite build && cat src/static/tailwind@3.4.1.js >> dist/anythingllm-chat-widget.js",
|
||||
"build": "vite build && cat src/static/tailwind@3.4.1.js >> dist/anythingllm-chat-widget.js && npx terser --compress -o dist/anythingllm-chat-widget.min.js -- dist/anythingllm-chat-widget.js",
|
||||
"build:publish": "yarn build && mkdir -p ../frontend/public/embed && cp -r dist/anythingllm-chat-widget.min.js ../frontend/public/embed/anythingllm-chat-widget.min.js",
|
||||
"dev:build": "vite build && yarn styles",
|
||||
"styles": "npx cleancss -o dist/anythingllm-chat-widget.min.css dist/style.css",
|
||||
"build": "vite build && yarn styles && npx terser --compress -o dist/anythingllm-chat-widget.min.js -- dist/anythingllm-chat-widget.js",
|
||||
"build:publish": "yarn build:publish:js && yarn build:publish:css",
|
||||
"build:publish:js": "yarn build && mkdir -p ../frontend/public/embed && cp -r dist/anythingllm-chat-widget.min.js ../frontend/public/embed/anythingllm-chat-widget.min.js",
|
||||
"build:publish:css": "cp -r dist/anythingllm-chat-widget.min.css ../frontend/public/embed/anythingllm-chat-widget.min.css",
|
||||
"lint": "yarn prettier --ignore-path ../.prettierignore --write ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -29,16 +32,20 @@
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"clean-css": "^5.3.3",
|
||||
"clean-css-cli": "^5.6.3",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"globals": "^13.21.0",
|
||||
"nodemon": "^2.0.22",
|
||||
"postcss": "^8.4.23",
|
||||
"prettier": "^3.0.3",
|
||||
"serve": "^14.2.1",
|
||||
"tailwindcss": "3.4.1",
|
||||
"terser": "^5.27.0",
|
||||
"vite": "^5.0.0",
|
||||
"vite-plugin-singlefile": "^0.13.5"
|
||||
}
|
||||
}
|
||||
}
|
10
embed/postcss.config.js
Normal file
10
embed/postcss.config.js
Normal file
@ -0,0 +1,10 @@
|
||||
import tailwind from 'tailwindcss'
|
||||
import autoprefixer from 'autoprefixer'
|
||||
import tailwindConfig from './tailwind.config.js'
|
||||
|
||||
export default {
|
||||
plugins: [
|
||||
tailwind(tailwindConfig),
|
||||
autoprefixer,
|
||||
],
|
||||
}
|
@ -20,29 +20,29 @@ export default function App() {
|
||||
if (!embedSettings.loaded) return null;
|
||||
|
||||
const positionClasses = {
|
||||
"bottom-left": "bottom-0 left-0 ml-4",
|
||||
"bottom-right": "bottom-0 right-0 mr-4",
|
||||
"top-left": "top-0 left-0 ml-4 mt-4",
|
||||
"top-right": "top-0 right-0 mr-4 mt-4",
|
||||
"bottom-left": "allm-bottom-0 allm-left-0 allm-ml-4",
|
||||
"bottom-right": "allm-bottom-0 allm-right-0 allm-mr-4",
|
||||
"top-left": "allm-top-0 allm-left-0 allm-ml-4 allm-mt-4",
|
||||
"top-right": "allm-top-0 allm-right-0 allm-mr-4 allm-mt-4",
|
||||
};
|
||||
|
||||
const position = embedSettings.position || "bottom-right";
|
||||
const windowWidth = embedSettings.windowWidth
|
||||
? `max-w-[${embedSettings.windowWidth}]`
|
||||
: "max-w-[400px]";
|
||||
const windowHeight = embedSettings.windowHeight
|
||||
? `max-h-[${embedSettings.windowHeight}]`
|
||||
: "max-h-[700px]";
|
||||
const windowWidth = embedSettings.windowWidth ?? "400px";
|
||||
const windowHeight = embedSettings.windowHeight ?? "700px";
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head />
|
||||
<div
|
||||
id="anything-llm-embed-chat-container"
|
||||
className={`fixed inset-0 z-50 ${isChatOpen ? "block" : "hidden"}`}
|
||||
className={`allm-fixed allm-inset-0 allm-z-50 ${isChatOpen ? "allm-block" : "allm-hidden"}`}
|
||||
>
|
||||
<div
|
||||
className={`${windowHeight} ${windowWidth} h-full w-full bg-white fixed bottom-0 right-0 mb-4 md:mr-4 rounded-2xl border border-gray-300 shadow-[0_4px_14px_rgba(0,0,0,0.25)] ${positionClasses[position]}`}
|
||||
style={{
|
||||
maxWidth: windowWidth,
|
||||
maxHeight: windowHeight,
|
||||
}}
|
||||
className={`allm-h-full allm-w-full allm-bg-white allm-fixed allm-bottom-0 allm-right-0 allm-mb-4 allm-md:mr-4 allm-rounded-2xl allm-border allm-border-gray-300 allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)] ${positionClasses[position]}`}
|
||||
id="anything-llm-chat"
|
||||
>
|
||||
{isChatOpen && (
|
||||
@ -57,7 +57,7 @@ export default function App() {
|
||||
{!isChatOpen && (
|
||||
<div
|
||||
id="anything-llm-embed-chat-button-container"
|
||||
className={`fixed bottom-0 ${positionClasses[position]} mb-4 z-50`}
|
||||
className={`allm-fixed allm-bottom-0 ${positionClasses[position]} allm-mb-4 allm-z-50`}
|
||||
>
|
||||
<OpenButton
|
||||
settings={embedSettings}
|
||||
|
@ -5,7 +5,7 @@ import { Tooltip } from "react-tooltip";
|
||||
|
||||
const Actions = ({ message }) => {
|
||||
return (
|
||||
<div className="flex justify-start items-center gap-x-4">
|
||||
<div className="allm-flex allm-justify-start allm-items-center allm-gap-x-4">
|
||||
<CopyMessage message={message} />
|
||||
{/* Other actions to go here later. */}
|
||||
</div>
|
||||
@ -16,17 +16,17 @@ function CopyMessage({ message }) {
|
||||
const { copied, copyText } = useCopyText();
|
||||
return (
|
||||
<>
|
||||
<div className="mt-3 relative">
|
||||
<div className="allm-mt-3 allm-relative">
|
||||
<button
|
||||
data-tooltip-id="copy-assistant-text"
|
||||
data-tooltip-content="Copy"
|
||||
className="text-zinc-300"
|
||||
className="allm-border-none allm-text-zinc-300"
|
||||
onClick={() => copyText(message)}
|
||||
>
|
||||
{copied ? (
|
||||
<Check size={18} className="mb-1" />
|
||||
<Check size={18} className="allm-mb-1" />
|
||||
) : (
|
||||
<ClipboardText size={18} className="mb-1" />
|
||||
<ClipboardText size={18} className="allm-mb-1" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
@ -34,7 +34,7 @@ function CopyMessage({ message }) {
|
||||
id="copy-assistant-text"
|
||||
place="bottom"
|
||||
delayShow={300}
|
||||
className="tooltip !text-xs"
|
||||
className="allm-tooltip !allm-text-xs"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -14,14 +14,14 @@ const HistoricalMessage = forwardRef(
|
||||
ref
|
||||
) => {
|
||||
const textSize = !!embedderSettings.settings.textSize
|
||||
? `text-[${embedderSettings.settings.textSize}px]`
|
||||
: "text-sm";
|
||||
? `allm-text-[${embedderSettings.settings.textSize}px]`
|
||||
: "allm-text-sm";
|
||||
|
||||
return (
|
||||
<div className="py-[5px]">
|
||||
{role === "assistant" && (
|
||||
<div
|
||||
className={`text-[10px] font-medium text-gray-400 ml-[54px] mr-6 mb-2 text-left`}
|
||||
className={`allm-text-[10px] allm-text-gray-400 allm-ml-[54px] allm-mr-6 allm-mb-2 allm-text-left allm-font-sans`}
|
||||
>
|
||||
{embedderSettings.settings.assistantName ||
|
||||
"Anything LLM Chat Assistant"}
|
||||
@ -30,42 +30,48 @@ const HistoricalMessage = forwardRef(
|
||||
<div
|
||||
key={uuid}
|
||||
ref={ref}
|
||||
className={`flex items-start w-full h-fit ${
|
||||
role === "user" ? "justify-end" : "justify-start"
|
||||
className={`allm-flex allm-items-start allm-w-full allm-h-fit ${
|
||||
role === "user" ? "allm-justify-end" : "allm-justify-start"
|
||||
}`}
|
||||
>
|
||||
{role === "assistant" && (
|
||||
<img
|
||||
src={embedderSettings.settings.assistantIcon || AnythingLLMIcon}
|
||||
alt="Anything LLM Icon"
|
||||
className="w-9 h-9 flex-shrink-0 ml-2 mt-2"
|
||||
className="allm-w-9 allm-h-9 allm-flex-shrink-0 allm-ml-2 allm-mt-2"
|
||||
id="anything-llm-icon"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
style={{ wordBreak: "break-word" }}
|
||||
className={`py-[11px] px-4 flex flex-col ${
|
||||
style={{
|
||||
wordBreak: "break-word",
|
||||
backgroundColor:
|
||||
role === "user"
|
||||
? embedderSettings.USER_STYLES.msgBg
|
||||
: embedderSettings.ASSISTANT_STYLES.msgBg,
|
||||
}}
|
||||
className={`allm-py-[11px] allm-px-4 allm-flex allm-flex-col allm-font-sans ${
|
||||
error
|
||||
? "bg-red-200 rounded-lg mr-[37px] ml-[9px]"
|
||||
? "allm-bg-red-200 allm-rounded-lg allm-mr-[37px] allm-ml-[9px]"
|
||||
: role === "user"
|
||||
? `${embedderSettings.USER_STYLES} anything-llm-user-message`
|
||||
: `${embedderSettings.ASSISTANT_STYLES} anything-llm-assistant-message`
|
||||
} shadow-[0_4px_14px_rgba(0,0,0,0.25)]`}
|
||||
? `${embedderSettings.USER_STYLES.base} allm-anything-llm-user-message`
|
||||
: `${embedderSettings.ASSISTANT_STYLES.base} allm-anything-llm-assistant-message`
|
||||
} allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)]`}
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="allm-flex">
|
||||
{error ? (
|
||||
<div className="p-2 rounded-lg bg-red-50 text-red-500">
|
||||
<span className={`inline-block `}>
|
||||
<Warning className="h-4 w-4 mb-1 inline-block" /> Could not
|
||||
respond to message.
|
||||
<div className="allm-p-2 allm-rounded-lg allm-bg-red-50 allm-text-red-500">
|
||||
<span className={`allm-inline-block `}>
|
||||
<Warning className="allm-h-4 allm-w-4 allm-mb-1 allm-inline-block" />{" "}
|
||||
Could not respond to message.
|
||||
</span>
|
||||
<p className="text-xs font-mono mt-2 border-l-2 border-red-500 pl-2 bg-red-300 p-2 rounded-sm">
|
||||
<p className="allm-text-xs allm-font-mono allm-mt-2 allm-border-l-2 allm-border-red-500 allm-pl-2 allm-bg-red-300 allm-p-2 allm-rounded-sm">
|
||||
{error}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<span
|
||||
className={`whitespace-pre-line font-medium flex flex-col gap-y-1 ${textSize} leading-[20px]`}
|
||||
className={`allm-whitespace-pre-line allm-flex allm-flex-col allm-gap-y-1 ${textSize} allm-leading-[20px]`}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(renderMarkdown(message)),
|
||||
}}
|
||||
@ -77,7 +83,7 @@ const HistoricalMessage = forwardRef(
|
||||
|
||||
{sentAt && (
|
||||
<div
|
||||
className={`text-[10px] font-medium text-gray-400 ml-[54px] mr-6 mt-2 ${role === "user" ? "text-right" : "text-left"}`}
|
||||
className={`allm-font-sans allm-text-[10px] allm-text-gray-400 allm-ml-[54px] allm-mr-6 allm-mt-2 ${role === "user" ? "allm-text-right" : "allm-text-left"}`}
|
||||
>
|
||||
{formatDate(sentAt)}
|
||||
</div>
|
||||
|
@ -11,18 +11,23 @@ const PromptReply = forwardRef(
|
||||
|
||||
if (pending) {
|
||||
return (
|
||||
<div className={`flex items-start w-full h-fit justify-start`}>
|
||||
<div
|
||||
className={`allm-flex allm-items-start allm-w-full allm-h-fit allm-justify-start`}
|
||||
>
|
||||
<img
|
||||
src={embedderSettings.settings.assistantIcon || AnythingLLMIcon}
|
||||
alt="Anything LLM Icon"
|
||||
className="w-9 h-9 flex-shrink-0 ml-2"
|
||||
className="allm-w-9 allm-h-9 allm-flex-shrink-0 allm-ml-2"
|
||||
/>
|
||||
<div
|
||||
style={{ wordBreak: "break-word" }}
|
||||
className={`py-[11px] px-4 flex flex-col ${embedderSettings.ASSISTANT_STYLES} shadow-[0_4px_14px_rgba(0,0,0,0.25)]`}
|
||||
style={{
|
||||
wordBreak: "break-word",
|
||||
backgroundColor: embedderSettings.ASSISTANT_STYLES.msgBg,
|
||||
}}
|
||||
className={`allm-py-[11px] allm-px-4 allm-flex allm-flex-col ${embedderSettings.ASSISTANT_STYLES.base} allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)]`}
|
||||
>
|
||||
<div className="flex gap-x-5">
|
||||
<div className="mx-4 my-1 dot-falling"></div>
|
||||
<div className="allm-flex allm-gap-x-5">
|
||||
<div className="allm-mx-4 allm-my-1 allm-dot-falling"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,23 +36,27 @@ const PromptReply = forwardRef(
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className={`flex items-end w-full h-fit justify-start`}>
|
||||
<div
|
||||
className={`allm-flex allm-items-end allm-w-full allm-h-fit allm-justify-start`}
|
||||
>
|
||||
<img
|
||||
src={embedderSettings.settings.assistantIcon || AnythingLLMIcon}
|
||||
alt="Anything LLM Icon"
|
||||
className="w-9 h-9 flex-shrink-0 ml-2"
|
||||
className="allm-w-9 allm-h-9 allm-flex-shrink-0 allm-ml-2"
|
||||
/>
|
||||
<div
|
||||
style={{ wordBreak: "break-word" }}
|
||||
className={`py-[11px] px-4 rounded-lg flex flex-col bg-red-200 shadow-[0_4px_14px_rgba(0,0,0,0.25)] mr-[37px] ml-[9px]`}
|
||||
className={`allm-py-[11px] allm-px-4 allm-rounded-lg allm-flex allm-flex-col allm-bg-red-200 allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)] allm-mr-[37px] allm-ml-[9px]`}
|
||||
>
|
||||
<div className="flex gap-x-5">
|
||||
<div className="allm-flex allm-gap-x-5">
|
||||
<span
|
||||
className={`inline-block p-2 rounded-lg bg-red-50 text-red-500`}
|
||||
className={`allm-inline-block allm-p-2 allm-rounded-lg allm-bg-red-50 allm-text-red-500`}
|
||||
>
|
||||
<Warning className="h-4 w-4 mb-1 inline-block" /> Could not
|
||||
respond to message.
|
||||
<span className="text-xs">Reason: {error || "unknown"}</span>
|
||||
<Warning className="allm-h-4 allm-w-4 allm-mb-1 allm-inline-block" />{" "}
|
||||
Could not respond to message.
|
||||
<span className="allm-text-xs">
|
||||
Reason: {error || "unknown"}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -56,9 +65,9 @@ const PromptReply = forwardRef(
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="py-[5px]">
|
||||
<div className="allm-py-[5px]">
|
||||
<div
|
||||
className={`text-[10px] font-medium text-gray-400 ml-[54px] mr-6 mb-2 text-left`}
|
||||
className={`allm-text-[10px] allm-text-gray-400 allm-ml-[54px] allm-mr-6 allm-mb-2 allm-text-left allm-font-sans`}
|
||||
>
|
||||
{embedderSettings.settings.assistantName ||
|
||||
"Anything LLM Chat Assistant"}
|
||||
@ -66,29 +75,32 @@ const PromptReply = forwardRef(
|
||||
<div
|
||||
key={uuid}
|
||||
ref={ref}
|
||||
className={`flex items-start w-full h-fit justify-start`}
|
||||
className={`allm-flex allm-items-start allm-w-full allm-h-fit allm-justify-start`}
|
||||
>
|
||||
<img
|
||||
src={embedderSettings.settings.assistantIcon || AnythingLLMIcon}
|
||||
alt="Anything LLM Icon"
|
||||
className="w-9 h-9 flex-shrink-0 ml-2"
|
||||
className="allm-w-9 allm-h-9 allm-flex-shrink-0 allm-ml-2"
|
||||
/>
|
||||
<div
|
||||
style={{ wordBreak: "break-word" }}
|
||||
className={`py-[11px] px-4 flex flex-col ${
|
||||
error ? "bg-red-200" : embedderSettings.ASSISTANT_STYLES
|
||||
} shadow-[0_4px_14px_rgba(0,0,0,0.25)]`}
|
||||
style={{
|
||||
wordBreak: "break-word",
|
||||
backgroundColor: embedderSettings.ASSISTANT_STYLES.msgBg,
|
||||
}}
|
||||
className={`allm-py-[11px] allm-px-4 allm-flex allm-flex-col ${
|
||||
error ? "allm-bg-red-200" : embedderSettings.ASSISTANT_STYLES.base
|
||||
} allm-shadow-[0_4px_14px_rgba(0,0,0,0.25)]`}
|
||||
>
|
||||
<div className="flex gap-x-5">
|
||||
<div className="allm-flex allm-gap-x-5">
|
||||
<span
|
||||
className={`reply whitespace-pre-line font-normal text-sm md:text-sm flex flex-col gap-y-1`}
|
||||
className={`allm-font-sans allm-reply allm-whitespace-pre-line allm-font-normal allm-text-sm allm-md:text-sm allm-flex allm-flex-col allm-gap-y-1`}
|
||||
dangerouslySetInnerHTML={{ __html: renderMarkdown(reply) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`text-[10px] font-medium text-gray-400 ml-[54px] mr-6 mt-2 text-left`}
|
||||
className={`allm-text-[10px] allm-text-gray-400 allm-ml-[54px] allm-mr-6 allm-mt-2 allm-text-left allm-font-sans`}
|
||||
>
|
||||
{formatDate(Date.now() / 1000)}
|
||||
</div>
|
||||
|
@ -46,9 +46,9 @@ export default function ChatHistory({ settings = {}, history = [] }) {
|
||||
|
||||
if (history.length === 0) {
|
||||
return (
|
||||
<div className="pb-[100px] pt-[5px] rounded-lg px-2 h-full mt-2 gap-y-2 overflow-y-scroll flex flex-col justify-start no-scroll">
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<p className="text-slate-400 text-sm font-base py-4 text-center">
|
||||
<div className="allm-pb-[100px] allm-pt-[5px] allm-rounded-lg allm-px-2 allm-h-full allm-mt-2 allm-gap-y-2 allm-overflow-y-scroll allm-flex allm-flex-col allm-justify-start allm-no-scroll">
|
||||
<div className="allm-flex allm-h-full allm-flex-col allm-items-center allm-justify-center">
|
||||
<p className="allm-text-slate-400 allm-text-sm allm-font-sans allm-py-4 allm-text-center">
|
||||
{settings?.greeting ?? "Send a chat to get started."}
|
||||
</p>
|
||||
</div>
|
||||
@ -58,7 +58,7 @@ export default function ChatHistory({ settings = {}, history = [] }) {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="pb-[30px] pt-[5px] rounded-lg px-2 h-full gap-y-2 overflow-y-scroll flex flex-col justify-start no-scroll md:max-h-[500px]"
|
||||
className="allm-pb-[30px] allm-pt-[5px] allm-rounded-lg allm-px-2 allm-h-full allm-gap-y-2 allm-overflow-y-scroll allm-flex allm-flex-col allm-justify-start allm-no-scroll allm-md:max-h-[500px]"
|
||||
id="chat-history"
|
||||
ref={chatHistoryRef}
|
||||
>
|
||||
@ -97,12 +97,12 @@ export default function ChatHistory({ settings = {}, history = [] }) {
|
||||
);
|
||||
})}
|
||||
{!isAtBottom && (
|
||||
<div className="fixed bottom-[10rem] right-[50px] z-50 cursor-pointer animate-pulse">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="p-1 rounded-full border border-white/10 bg-black/20 hover:bg-black/50">
|
||||
<div className="allm-fixed allm-bottom-[10rem] allm-right-[50px] allm-z-50 allm-cursor-pointer allm-animate-pulse">
|
||||
<div className="allm-flex allm-flex-col allm-items-center">
|
||||
<div className="allm-p-1 allm-rounded-full allm-border allm-border-white/10 allm-bg-black/20 hover:allm-bg-black/50">
|
||||
<ArrowDown
|
||||
weight="bold"
|
||||
className="text-white/50 w-5 h-5"
|
||||
className="allm-text-white/50 allm-w-5 allm-h-5"
|
||||
onClick={scrollToBottom}
|
||||
id="scroll-to-bottom-button"
|
||||
aria-label="Scroll to bottom"
|
||||
@ -117,10 +117,13 @@ export default function ChatHistory({ settings = {}, history = [] }) {
|
||||
|
||||
export function ChatHistoryLoading() {
|
||||
return (
|
||||
<div className="h-full w-full relative">
|
||||
<div className="h-full max-h-[82vh] pb-[100px] pt-[5px] bg-gray-100 rounded-lg px-2 h-full mt-2 gap-y-2 overflow-y-scroll flex flex-col justify-start no-scroll">
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<CircleNotch size={14} className="text-slate-400 animate-spin" />
|
||||
<div className="allm-h-full allm-w-full allm-relative">
|
||||
<div className="allm-h-full allm-max-h-[82vh] allm-pb-[100px] allm-pt-[5px] allm-bg-gray-100 allm-rounded-lg allm-px-2 allm-h-full allm-mt-2 allm-gap-y-2 allm-overflow-y-scroll allm-flex allm-flex-col allm-justify-start allm-no-scroll">
|
||||
<div className="allm-flex allm-h-full allm-flex-col allm-items-center allm-justify-center">
|
||||
<CircleNotch
|
||||
size={14}
|
||||
className="allm-text-slate-400 allm-animate-spin"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -46,14 +46,17 @@ export default function PromptInput({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full sticky bottom-0 z-10 flex justify-center items-center px-5 bg-white">
|
||||
<div className="allm-w-full allm-sticky allm-bottom-0 allm-z-10 allm-flex allm-justify-center allm-items-center allm-bg-white">
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="flex flex-col gap-y-1 rounded-t-lg w-full items-center justify-center"
|
||||
className="allm-flex allm-flex-col allm-gap-y-1 allm-rounded-t-lg allm-w-full allm-items-center allm-justify-center"
|
||||
>
|
||||
<div className="flex items-center w-full">
|
||||
<div className="bg-white border-[1.5px] border-[#22262833]/20 rounded-2xl flex flex-col px-4 overflow-hidden w-full">
|
||||
<div className="flex items-center w-full">
|
||||
<div className="allm-flex allm-items-center allm-w-full">
|
||||
<div className="allm-bg-white allm-flex allm-flex-col allm-px-4 allm-overflow-hidden allm-w-full">
|
||||
<div
|
||||
style={{ border: "1.5px solid #22262833" }}
|
||||
className="allm-flex allm-items-center allm-w-full allm-rounded-2xl"
|
||||
>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
onKeyUp={adjustTextArea}
|
||||
@ -67,7 +70,7 @@ export default function PromptInput({
|
||||
adjustTextArea(e);
|
||||
}}
|
||||
value={message}
|
||||
className="cursor-text max-h-[100px] text-[14px] mx-2 py-2 w-full text-black bg-transparent placeholder:text-slate-800/60 resize-none active:outline-none focus:outline-none flex-grow"
|
||||
className="allm-font-sans allm-border-none allm-cursor-text allm-max-h-[100px] allm-text-[14px] allm-mx-2 allm-py-2 allm-w-full allm-text-black allm-bg-transparent placeholder:allm-text-slate-800/60 allm-resize-none active:allm-outline-none focus:allm-outline-none allm-flex-grow"
|
||||
placeholder={"Send a message"}
|
||||
id="message-input"
|
||||
/>
|
||||
@ -75,20 +78,20 @@ export default function PromptInput({
|
||||
ref={formRef}
|
||||
type="submit"
|
||||
disabled={buttonDisabled}
|
||||
className="inline-flex justify-center rounded-2xl cursor-pointer text-black group ml-4"
|
||||
className="allm-bg-transparent allm-border-none allm-inline-flex allm-justify-center allm-rounded-2xl allm-cursor-pointer allm-text-black group"
|
||||
id="send-message-button"
|
||||
aria-label="Send message"
|
||||
>
|
||||
{buttonDisabled ? (
|
||||
<CircleNotch className="w-4 h-4 animate-spin" />
|
||||
<CircleNotch className="allm-w-4 allm-h-4 allm-animate-spin" />
|
||||
) : (
|
||||
<PaperPlaneRight
|
||||
size={24}
|
||||
className="my-3 text-[#22262899]/60 group-hover:text-[#22262899]/90"
|
||||
className="allm-my-3 allm-text-[#22262899]/60 group-hover:allm-text-[#22262899]/90"
|
||||
weight="fill"
|
||||
/>
|
||||
)}
|
||||
<span className="sr-only">Send message</span>
|
||||
<span className="allm-sr-only">Send message</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -77,8 +77,8 @@ export default function ChatContainer({
|
||||
}, [loadingResponse, chatHistory]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col">
|
||||
<div className="flex-grow overflow-y-auto">
|
||||
<div className="allm-h-full allm-w-full allm-flex allm-flex-col">
|
||||
<div className="allm-flex-grow allm-overflow-y-auto">
|
||||
<ChatHistory settings={settings} history={chatHistory} />
|
||||
</div>
|
||||
<PromptInput
|
||||
|
@ -45,23 +45,24 @@ export default function ChatWindowHeader({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center relative rounded-t-2xl bg-black/10"
|
||||
style={{ borderBottom: "1px solid #E9E9E9" }}
|
||||
className="allm-flex allm-items-center allm-relative allm-rounded-t-2xl"
|
||||
id="anything-llm-header"
|
||||
>
|
||||
<div className="flex justify-center items-center w-full h-[76px]">
|
||||
<div className="allm-flex allm-justify-center allm-items-center allm-w-full allm-h-[76px]">
|
||||
<img
|
||||
style={{ maxWidth: 48, maxHeight: 48 }}
|
||||
src={iconUrl ?? AnythingLLMIcon}
|
||||
alt={iconUrl ? "Brand" : "AnythingLLM Logo"}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute right-0 flex gap-x-1 items-center px-[22px]">
|
||||
<div className="allm-absolute allm-right-0 allm-flex allm-gap-x-1 allm-items-center allm-px-[22px]">
|
||||
{settings.loaded && (
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
onClick={() => setShowOptions(!showingOptions)}
|
||||
className="hover:bg-gray-100 rounded-sm text-slate-800"
|
||||
className="allm-bg-transparent hover:allm-cursor-pointer allm-border-none hover:allm-bg-gray-100 allm-rounded-sm allm-text-slate-800/60"
|
||||
aria-label="Options"
|
||||
>
|
||||
<DotsThreeOutlineVertical size={20} weight="fill" />
|
||||
@ -70,7 +71,7 @@ export default function ChatWindowHeader({
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeChat}
|
||||
className="hover:bg-gray-100 rounded-sm text-slate-800"
|
||||
className="allm-bg-transparent hover:allm-cursor-pointer allm-border-none hover:allm-bg-gray-100 allm-rounded-sm allm-text-slate-800/60"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X size={20} weight="bold" />
|
||||
@ -92,14 +93,14 @@ function OptionsMenu({ settings, showing, resetChat, sessionId, menuRef }) {
|
||||
return (
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="absolute z-10 bg-white flex flex-col gap-y-1 rounded-xl shadow-lg border border-gray-300 top-[64px] right-[46px]"
|
||||
className="allm-bg-white allm-absolute allm-z-10 allm-flex allm-flex-col allm-gap-y-1 allm-rounded-xl allm-shadow-lg allm-top-[64px] allm-right-[46px]"
|
||||
>
|
||||
<button
|
||||
onClick={resetChat}
|
||||
className="flex items-center gap-x-2 hover:bg-gray-100 text-sm text-gray-700 py-2.5 px-4 rounded-xl"
|
||||
className="hover:allm-cursor-pointer allm-bg-white allm-gap-x-[12px] hover:allm-bg-gray-100 allm-rounded-lg allm-border-none allm-flex allm-items-center allm-text-base allm-text-[#7A7D7E] allm-font-bold allm-px-4"
|
||||
>
|
||||
<ArrowCounterClockwise size={24} />
|
||||
<p className="text-sm text-[#7A7D7E] font-bold">Reset Chat</p>
|
||||
<p className="allm-text-[14px]">Reset Chat</p>
|
||||
</button>
|
||||
<ContactSupport email={settings.supportEmail} />
|
||||
<SessionID sessionId={sessionId} />
|
||||
@ -120,9 +121,9 @@ function SessionID({ sessionId }) {
|
||||
|
||||
if (sessionIdCopied) {
|
||||
return (
|
||||
<div className="flex items-center gap-x-2 hover:bg-gray-100 text-sm text-gray-700 py-2.5 px-4 rounded-xl">
|
||||
<div className="hover:allm-cursor-pointer allm-bg-white allm-gap-x-[12px] hover:allm-bg-gray-100 allm-rounded-lg allm-border-none allm-flex allm-items-center allm-text-base allm-text-[#7A7D7E] allm-font-bold allm-px-4">
|
||||
<Check size={24} />
|
||||
<p className="text-sm text-[#7A7D7E] font-bold">Copied!</p>
|
||||
<p className="allm-text-[14px] allm-font-sans">Copied!</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -130,10 +131,10 @@ function SessionID({ sessionId }) {
|
||||
return (
|
||||
<button
|
||||
onClick={copySessionId}
|
||||
className="flex items-center gap-x-2 hover:bg-gray-100 text-sm text-gray-700 py-2.5 px-4 rounded-xl"
|
||||
className="hover:allm-cursor-pointer allm-bg-white allm-gap-x-[12px] hover:allm-bg-gray-100 allm-rounded-lg allm-border-none allm-flex allm-items-center allm-text-base allm-text-[#7A7D7E] allm-font-bold allm-px-4"
|
||||
>
|
||||
<Copy size={24} />
|
||||
<p className="text-sm text-[#7A7D7E] font-bold">Session ID</p>
|
||||
<p className="allm-text-[14px]">Session ID</p>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@ -145,10 +146,10 @@ function ContactSupport({ email = null }) {
|
||||
return (
|
||||
<a
|
||||
href={`mailto:${email}?Subject=${encodeURIComponent(subject)}`}
|
||||
className="flex items-center gap-x-2 hover:bg-gray-100 text-sm text-gray-700 py-2.5 px-4 rounded-xl"
|
||||
className="allm-no-underline hover:allm-underline hover:allm-cursor-pointer allm-bg-white allm-gap-x-[12px] hover:allm-bg-gray-100 allm-rounded-lg allm-border-none allm-flex allm-items-center allm-text-base allm-text-[#7A7D7E] allm-font-bold allm-px-4"
|
||||
>
|
||||
<Envelope size={24} />
|
||||
<p className="text-sm text-[#7A7D7E] font-bold">Email Support</p>
|
||||
<p className="allm-text-[14px] allm-font-sans">Email Support</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export default function ChatWindow({ closeChat, settings, sessionId }) {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="allm-flex allm-flex-col allm-h-full">
|
||||
<ChatWindowHeader
|
||||
sessionId={sessionId}
|
||||
settings={settings}
|
||||
@ -23,7 +23,7 @@ export default function ChatWindow({ closeChat, settings, sessionId }) {
|
||||
setChatHistory={setChatHistory}
|
||||
/>
|
||||
<ChatHistoryLoading />
|
||||
<div className="pt-4 pb-2 h-fit gap-y-1">
|
||||
<div className="allm-pt-4 allm-pb-2 allm-h-fit allm-gap-y-1">
|
||||
<SessionId />
|
||||
<Sponsor settings={settings} />
|
||||
</div>
|
||||
@ -34,7 +34,7 @@ export default function ChatWindow({ closeChat, settings, sessionId }) {
|
||||
setEventDelegatorForCodeSnippets();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="allm-flex allm-flex-col allm-h-full">
|
||||
<ChatWindowHeader
|
||||
sessionId={sessionId}
|
||||
settings={settings}
|
||||
@ -42,14 +42,14 @@ export default function ChatWindow({ closeChat, settings, sessionId }) {
|
||||
closeChat={closeChat}
|
||||
setChatHistory={setChatHistory}
|
||||
/>
|
||||
<div className="flex-grow overflow-y-auto">
|
||||
<div className="allm-flex-grow allm-overflow-y-auto">
|
||||
<ChatContainer
|
||||
sessionId={sessionId}
|
||||
settings={settings}
|
||||
knownHistory={chatHistory}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 pb-4 h-fit gap-y-2 z-10">
|
||||
<div className="allm-mt-4 allm-pb-4 allm-h-fit allm-gap-y-2 allm-z-10">
|
||||
<Sponsor settings={settings} />
|
||||
<ResetChat
|
||||
setChatHistory={setChatHistory}
|
||||
@ -76,13 +76,13 @@ function copyCodeSnippet(uuid) {
|
||||
|
||||
window.navigator.clipboard.writeText(markdown);
|
||||
|
||||
target.classList.add("text-green-500");
|
||||
target.classList.add("allm-text-green-500");
|
||||
const originalText = target.innerHTML;
|
||||
target.innerText = "Copied!";
|
||||
target.setAttribute("disabled", true);
|
||||
|
||||
setTimeout(() => {
|
||||
target.classList.remove("text-green-500");
|
||||
target.classList.remove("allm-text-green-500");
|
||||
target.innerHTML = originalText;
|
||||
target.removeAttribute("disabled");
|
||||
}, 2500);
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { embedderSettings } from "@/main";
|
||||
|
||||
const hljsCss = `
|
||||
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
||||
Theme: GitHub Dark Dimmed
|
||||
@ -16,7 +18,7 @@ const customCss = `
|
||||
* Dot Falling
|
||||
* ==============================================
|
||||
*/
|
||||
.dot-falling {
|
||||
.allm-dot-falling {
|
||||
position: relative;
|
||||
left: -9999px;
|
||||
width: 10px;
|
||||
@ -29,15 +31,15 @@ const customCss = `
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.dot-falling::before,
|
||||
.dot-falling::after {
|
||||
.allm-dot-falling::before,
|
||||
.allm-dot-falling::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.dot-falling::before {
|
||||
.allm-dot-falling::before {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
@ -47,7 +49,7 @@ const customCss = `
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.dot-falling::after {
|
||||
.allm-dot-falling::after {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
@ -101,50 +103,20 @@ const customCss = `
|
||||
|
||||
#chat-history::-webkit-scrollbar,
|
||||
#chat-container::-webkit-scrollbar,
|
||||
.no-scroll::-webkit-scrollbar {
|
||||
.allm-no-scroll::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
#chat-history,
|
||||
#chat-container,
|
||||
.no-scroll {
|
||||
.allm-no-scroll {
|
||||
-ms-overflow-style: none !important; /* IE and Edge */
|
||||
scrollbar-width: none !important; /* Firefox */
|
||||
}
|
||||
|
||||
.animate-slow-pulse {
|
||||
transform: scale(1);
|
||||
animation: subtlePulse 20s infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@keyframes subtlePulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes subtleShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-black-900 {
|
||||
background: #141414;
|
||||
span.allm-whitespace-pre-line>p {
|
||||
margin: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
@ -153,6 +125,7 @@ export default function Head() {
|
||||
<head>
|
||||
<style>{hljsCss}</style>
|
||||
<style>{customCss}</style>
|
||||
<link rel="stylesheet" href={embedderSettings.stylesSrc} />
|
||||
</head>
|
||||
);
|
||||
}
|
||||
|
@ -23,9 +23,10 @@ export default function OpenButton({ settings, isOpen, toggleOpen }) {
|
||||
: CHAT_ICONS.plus;
|
||||
return (
|
||||
<button
|
||||
style={{ backgroundColor: settings.buttonColor }}
|
||||
id="anything-llm-embed-chat-button"
|
||||
onClick={toggleOpen}
|
||||
className={`flex items-center justify-center p-4 rounded-full bg-[${settings.buttonColor}] text-white text-2xl`}
|
||||
className={`hover:allm-cursor-pointer allm-border-none allm-flex allm-items-center allm-justify-center allm-p-4 allm-rounded-full allm-text-white allm-text-2xl hover:allm-opacity-95`}
|
||||
aria-label="Toggle Menu"
|
||||
>
|
||||
<ChatIcon className="text-white" />
|
||||
|
@ -7,9 +7,10 @@ export default function ResetChat({ setChatHistory, settings, sessionId }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex justify-center">
|
||||
<div className="allm-w-full allm-flex allm-justify-center">
|
||||
<button
|
||||
className="text-sm text-[#7A7D7E] hover:text-[#7A7D7E]/80 hover:underline"
|
||||
style={{ color: "#7A7D7E" }}
|
||||
className="hover:allm-cursor-pointer allm-border-none allm-text-sm allm-bg-transparent hover:allm-opacity-80 hover:allm-underline"
|
||||
onClick={() => handleChatReset()}
|
||||
>
|
||||
Reset Chat
|
||||
|
@ -5,6 +5,8 @@ export default function SessionId() {
|
||||
if (!sessionId) return null;
|
||||
|
||||
return (
|
||||
<div className="text-xs text-gray-300 w-full text-center">{sessionId}</div>
|
||||
<div className="allm-text-xs allm-text-gray-300 allm-w-full allm-text-center">
|
||||
{sessionId}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -2,12 +2,13 @@ export default function Sponsor({ settings }) {
|
||||
if (!!settings.noSponsor) return null;
|
||||
|
||||
return (
|
||||
<div className="flex w-full items-center justify-center">
|
||||
<div className="allm-flex allm-w-full allm-items-center allm-justify-center">
|
||||
<a
|
||||
style={{ color: "#0119D9" }}
|
||||
href={settings.sponsorLink ?? "#"}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-xs text-[#0119D9] hover:text-[#0119D9]/80 hover:underline"
|
||||
className="allm-text-xs allm-font-sans hover:allm-opacity-80 hover:allm-underline"
|
||||
>
|
||||
{settings.sponsorText}
|
||||
</a>
|
||||
|
3
embed/src/index.css
Normal file
3
embed/src/index.css
Normal file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
@ -1,6 +1,8 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App.jsx";
|
||||
import "./index.css";
|
||||
import { parseStylesSrc } from "./utils/constants.js";
|
||||
const appElement = document.createElement("div");
|
||||
|
||||
document.body.appendChild(appElement);
|
||||
@ -17,6 +19,13 @@ const scriptSettings = Object.assign(
|
||||
);
|
||||
export const embedderSettings = {
|
||||
settings: scriptSettings,
|
||||
USER_STYLES: `bg-[${scriptSettings?.userBgColor ?? "#3DBEF5"}] text-white rounded-t-[18px] rounded-bl-[18px] rounded-br-[4px] mx-[20px]`,
|
||||
ASSISTANT_STYLES: `bg-[${scriptSettings?.assistantBgColor ?? "#FFFFFF"}] text-[#222628] rounded-t-[18px] rounded-br-[18px] rounded-bl-[4px] mr-[37px] ml-[9px]`,
|
||||
stylesSrc: parseStylesSrc(document?.currentScript?.src),
|
||||
USER_STYLES: {
|
||||
msgBg: scriptSettings?.userBgColor ?? "#3DBEF5",
|
||||
base: `allm-text-white allm-rounded-t-[18px] allm-rounded-bl-[18px] allm-rounded-br-[4px] allm-mx-[20px]`,
|
||||
},
|
||||
ASSISTANT_STYLES: {
|
||||
msgBg: scriptSettings?.userBgColor ?? "#FFFFFF",
|
||||
base: `allm-text-[#222628] allm-rounded-t-[18px] allm-rounded-br-[18px] allm-rounded-bl-[4px] allm-mr-[37px] allm-ml-[9px]`,
|
||||
},
|
||||
};
|
||||
|
File diff suppressed because one or more lines are too long
@ -1 +1,15 @@
|
||||
export const CHAT_UI_REOPEN = "___anythingllm-chat-widget-open___";
|
||||
export function parseStylesSrc(scriptSrc = null) {
|
||||
try {
|
||||
const _url = new URL(scriptSrc);
|
||||
_url.pathname = _url.pathname
|
||||
.replace("anythingllm-chat-widget.js", "anythingllm-chat-widget.min.css")
|
||||
.replace(
|
||||
"anythingllm-chat-widget.min.js",
|
||||
"anythingllm-chat-widget.min.css"
|
||||
);
|
||||
return _url.toString();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
103
embed/tailwind.config.js
Normal file
103
embed/tailwind.config.js
Normal file
@ -0,0 +1,103 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: 'false',
|
||||
prefix: 'allm-',
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
content: {
|
||||
relative: true,
|
||||
files: [
|
||||
"./src/components/**/*.{js,jsx}",
|
||||
"./src/hooks/**/*.js",
|
||||
"./src/models/**/*.js",
|
||||
"./src/pages/**/*.{js,jsx}",
|
||||
"./src/utils/**/*.js",
|
||||
"./src/*.jsx",
|
||||
"./index.html",
|
||||
]
|
||||
},
|
||||
theme: {
|
||||
extend: {
|
||||
rotate: {
|
||||
"270": "270deg",
|
||||
"360": "360deg"
|
||||
},
|
||||
colors: {
|
||||
"black-900": "#141414",
|
||||
accent: "#3D4147",
|
||||
"sidebar-button": "#31353A",
|
||||
sidebar: "#25272C",
|
||||
"historical-msg-system": "rgba(255, 255, 255, 0.05);",
|
||||
"historical-msg-user": "#2C2F35",
|
||||
outline: "#4E5153",
|
||||
"primary-button": "#46C8FF",
|
||||
secondary: "#2C2F36",
|
||||
"dark-input": "#18181B",
|
||||
"mobile-onboarding": "#2C2F35",
|
||||
"dark-highlight": "#1C1E21",
|
||||
"dark-text": "#222628",
|
||||
description: "#D2D5DB",
|
||||
"x-button": "#9CA3AF"
|
||||
},
|
||||
backgroundImage: {
|
||||
"preference-gradient":
|
||||
"linear-gradient(180deg, #5A5C63 0%, rgba(90, 92, 99, 0.28) 100%);",
|
||||
"chat-msg-user-gradient":
|
||||
"linear-gradient(180deg, #3D4147 0%, #2C2F35 100%);",
|
||||
"selected-preference-gradient":
|
||||
"linear-gradient(180deg, #313236 0%, rgba(63.40, 64.90, 70.13, 0) 100%);",
|
||||
"main-gradient": "linear-gradient(180deg, #3D4147 0%, #2C2F35 100%)",
|
||||
"modal-gradient": "linear-gradient(180deg, #3D4147 0%, #2C2F35 100%)",
|
||||
"sidebar-gradient": "linear-gradient(90deg, #5B616A 0%, #3F434B 100%)",
|
||||
"login-gradient": "linear-gradient(180deg, #3D4147 0%, #2C2F35 100%)",
|
||||
"menu-item-gradient":
|
||||
"linear-gradient(90deg, #3D4147 0%, #2C2F35 100%)",
|
||||
"menu-item-selected-gradient":
|
||||
"linear-gradient(90deg, #5B616A 0%, #3F434B 100%)",
|
||||
"workspace-item-gradient":
|
||||
"linear-gradient(90deg, #3D4147 0%, #2C2F35 100%)",
|
||||
"workspace-item-selected-gradient":
|
||||
"linear-gradient(90deg, #5B616A 0%, #3F434B 100%)",
|
||||
"switch-selected": "linear-gradient(146deg, #5B616A 0%, #3F434B 100%)"
|
||||
},
|
||||
fontFamily: {
|
||||
sans: [
|
||||
"plus-jakarta-sans",
|
||||
"ui-sans-serif",
|
||||
"system-ui",
|
||||
"-apple-system",
|
||||
"BlinkMacSystemFont",
|
||||
'"Segoe UI"',
|
||||
"Roboto",
|
||||
'"Helvetica Neue"',
|
||||
"Arial",
|
||||
'"Noto Sans"',
|
||||
"sans-serif",
|
||||
'"Apple Color Emoji"',
|
||||
'"Segoe UI Emoji"',
|
||||
'"Segoe UI Symbol"',
|
||||
'"Noto Color Emoji"'
|
||||
]
|
||||
},
|
||||
animation: {
|
||||
sweep: "sweep 0.5s ease-in-out"
|
||||
},
|
||||
keyframes: {
|
||||
sweep: {
|
||||
"0%": { transform: "scaleX(0)", transformOrigin: "bottom left" },
|
||||
"100%": { transform: "scaleX(1)", transformOrigin: "bottom left" }
|
||||
},
|
||||
fadeIn: {
|
||||
"0%": { opacity: 0 },
|
||||
"100%": { opacity: 1 }
|
||||
},
|
||||
fadeOut: {
|
||||
"0%": { opacity: 1 },
|
||||
"100%": { opacity: 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: []
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
// vite.config.js
|
||||
import { defineConfig } from "vite"
|
||||
import { fileURLToPath, URL } from "url"
|
||||
import postcss from "./postcss.config.js"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import image from "@rollup/plugin-image"
|
||||
|
||||
@ -10,6 +11,9 @@ export default defineConfig({
|
||||
// In dev, we need to disable this, but in prod, we need to enable it
|
||||
"process.env.NODE_ENV": JSON.stringify("production")
|
||||
},
|
||||
css: {
|
||||
postcss
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
|
429
embed/yarn.lock
429
embed/yarn.lock
@ -7,6 +7,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
|
||||
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
|
||||
|
||||
"@alloc/quick-lru@^5.2.0":
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
|
||||
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
|
||||
@ -379,6 +384,18 @@
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
|
||||
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
|
||||
|
||||
"@isaacs/cliui@^8.0.2":
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
|
||||
integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
|
||||
dependencies:
|
||||
string-width "^5.1.2"
|
||||
string-width-cjs "npm:string-width@^4.2.0"
|
||||
strip-ansi "^7.0.1"
|
||||
strip-ansi-cjs "npm:strip-ansi@^6.0.1"
|
||||
wrap-ansi "^8.1.0"
|
||||
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2":
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
|
||||
@ -432,12 +449,12 @@
|
||||
"@nodelib/fs.stat" "2.0.5"
|
||||
run-parallel "^1.1.9"
|
||||
|
||||
"@nodelib/fs.stat@2.0.5":
|
||||
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
|
||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||
|
||||
"@nodelib/fs.walk@^1.2.8":
|
||||
"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
|
||||
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
|
||||
@ -450,6 +467,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@phosphor-icons/react/-/react-2.0.15.tgz#4d8e28484d45649f53a6cd75db161cf8b8379e1d"
|
||||
integrity sha512-PQKNcRrfERlC8gJGNz0su0i9xVmeubXSNxucPcbCLDd9u0cwJVTEyYK87muul/svf0UXFdL2Vl6bbeOhT1Mwow==
|
||||
|
||||
"@pkgjs/parseargs@^0.11.0":
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@rollup/plugin-image@^3.0.3":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-image/-/plugin-image-3.0.3.tgz#025b557180bae20f2349ff5130ef2114169feaac"
|
||||
@ -684,7 +706,7 @@ ansi-styles@^3.2.1:
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-styles@^4.1.0:
|
||||
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
|
||||
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
|
||||
@ -696,6 +718,11 @@ ansi-styles@^6.1.0:
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
|
||||
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
|
||||
|
||||
any-promise@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||
integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
|
||||
@ -709,7 +736,7 @@ arch@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
|
||||
integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
|
||||
|
||||
arg@5.0.2:
|
||||
arg@5.0.2, arg@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
|
||||
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
|
||||
@ -838,6 +865,13 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
brace-expansion@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
|
||||
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
braces@^3.0.2, braces@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
@ -845,6 +879,13 @@ braces@^3.0.2, braces@~3.0.2:
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
braces@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
|
||||
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
||||
dependencies:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
browserslist@^4.22.2:
|
||||
version "4.22.3"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6"
|
||||
@ -879,6 +920,11 @@ callsites@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
camelcase-css@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
|
||||
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
|
||||
|
||||
camelcase@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048"
|
||||
@ -938,6 +984,38 @@ chokidar@^3.5.2:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
chokidar@^3.5.3:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
|
||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
||||
dependencies:
|
||||
anymatch "~3.1.2"
|
||||
braces "~3.0.2"
|
||||
glob-parent "~5.1.2"
|
||||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.6.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
clean-css-cli@^5.6.3:
|
||||
version "5.6.3"
|
||||
resolved "https://registry.yarnpkg.com/clean-css-cli/-/clean-css-cli-5.6.3.tgz#be2d65c9b17bba01a598d7eeb5ce68f4119f07b5"
|
||||
integrity sha512-MUAta8pEqA/d2DKQwtZU5nm0Og8TCyAglOx3GlWwjhGdKBwY4kVF6E5M6LU/jmmuswv+HbYqG/dKKkq5p1dD0A==
|
||||
dependencies:
|
||||
chokidar "^3.5.2"
|
||||
clean-css "^5.3.3"
|
||||
commander "7.x"
|
||||
glob "^7.1.6"
|
||||
|
||||
clean-css@^5.3.3:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd"
|
||||
integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==
|
||||
dependencies:
|
||||
source-map "~0.6.0"
|
||||
|
||||
cli-boxes@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145"
|
||||
@ -976,11 +1054,21 @@ color-name@~1.1.4:
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
commander@7.x:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||
|
||||
commander@^2.20.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
commander@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
||||
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
|
||||
|
||||
compressible@~2.0.16:
|
||||
version "2.0.18"
|
||||
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
|
||||
@ -1016,7 +1104,7 @@ convert-source-map@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||
|
||||
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
||||
@ -1025,6 +1113,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
cssesc@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
csstype@^3.0.2:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||
@ -1079,6 +1172,16 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
|
||||
has-property-descriptors "^1.0.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
didyoumean@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
|
||||
integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
|
||||
|
||||
dlv@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
|
||||
integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
|
||||
|
||||
doctrine@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
|
||||
@ -1404,6 +1507,17 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-glob@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
|
||||
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "^2.0.2"
|
||||
"@nodelib/fs.walk" "^1.2.3"
|
||||
glob-parent "^5.1.2"
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
@ -1442,6 +1556,13 @@ fill-range@^7.0.1:
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
fill-range@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
find-up@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
|
||||
@ -1471,6 +1592,14 @@ for-each@^0.3.3:
|
||||
dependencies:
|
||||
is-callable "^1.1.3"
|
||||
|
||||
foreground-child@^3.1.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7"
|
||||
integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.0"
|
||||
signal-exit "^4.0.1"
|
||||
|
||||
fraction.js@^4.3.7:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
|
||||
@ -1534,6 +1663,13 @@ get-symbol-description@^1.0.0:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.1.1"
|
||||
|
||||
glob-parent@^5.1.2, glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob-parent@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
|
||||
@ -1541,14 +1677,19 @@ glob-parent@^6.0.2:
|
||||
dependencies:
|
||||
is-glob "^4.0.3"
|
||||
|
||||
glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
glob@^10.3.10:
|
||||
version "10.4.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5"
|
||||
integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
foreground-child "^3.1.0"
|
||||
jackspeak "^3.1.2"
|
||||
minimatch "^9.0.4"
|
||||
minipass "^7.1.2"
|
||||
package-json-from-dist "^1.0.0"
|
||||
path-scurry "^1.11.1"
|
||||
|
||||
glob@^7.1.3:
|
||||
glob@^7.1.3, glob@^7.1.6:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
@ -1921,6 +2062,20 @@ iterator.prototype@^1.1.2:
|
||||
reflect.getprototypeof "^1.0.4"
|
||||
set-function-name "^2.0.1"
|
||||
|
||||
jackspeak@^3.1.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a"
|
||||
integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==
|
||||
dependencies:
|
||||
"@isaacs/cliui" "^8.0.2"
|
||||
optionalDependencies:
|
||||
"@pkgjs/parseargs" "^0.11.0"
|
||||
|
||||
jiti@^1.19.1:
|
||||
version "1.21.6"
|
||||
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268"
|
||||
integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@ -1988,6 +2143,21 @@ levn@^0.4.1:
|
||||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lilconfig@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
|
||||
integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==
|
||||
|
||||
lilconfig@^3.0.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb"
|
||||
integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==
|
||||
|
||||
lines-and-columns@^1.1.6:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
|
||||
|
||||
linkify-it@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec"
|
||||
@ -2019,6 +2189,11 @@ loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
lru-cache@^10.2.0:
|
||||
version "10.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b"
|
||||
integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
@ -2047,6 +2222,19 @@ merge-stream@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||
|
||||
merge2@^1.3.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
|
||||
micromatch@^4.0.4:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5"
|
||||
integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==
|
||||
dependencies:
|
||||
braces "^3.0.3"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
micromatch@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
|
||||
@ -2096,11 +2284,23 @@ minimatch@3.1.2, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^9.0.4:
|
||||
version "9.0.5"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
|
||||
integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimist@^1.2.0:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
|
||||
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
@ -2116,6 +2316,15 @@ ms@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
mz@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
|
||||
integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
nanoid@^3.3.7:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
||||
@ -2176,11 +2385,16 @@ npm-run-path@^4.0.1:
|
||||
dependencies:
|
||||
path-key "^3.0.0"
|
||||
|
||||
object-assign@^4.1.1:
|
||||
object-assign@^4.0.1, object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
||||
|
||||
object-hash@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
|
||||
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
|
||||
|
||||
object-inspect@^1.13.1, object-inspect@^1.9.0:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
|
||||
@ -2281,6 +2495,11 @@ p-locate@^5.0.0:
|
||||
dependencies:
|
||||
p-limit "^3.0.2"
|
||||
|
||||
package-json-from-dist@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00"
|
||||
integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==
|
||||
|
||||
parent-module@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
||||
@ -2313,6 +2532,14 @@ path-parse@^1.0.7:
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
path-scurry@^1.11.1:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
|
||||
integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
|
||||
dependencies:
|
||||
lru-cache "^10.2.0"
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
|
||||
path-to-regexp@2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45"
|
||||
@ -2323,16 +2550,79 @@ picocolors@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||
|
||||
picocolors@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
|
||||
integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
postcss-value-parser@^4.2.0:
|
||||
pify@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||
integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
|
||||
|
||||
pirates@^4.0.1:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
|
||||
integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
|
||||
|
||||
postcss-import@^15.1.0:
|
||||
version "15.1.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70"
|
||||
integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==
|
||||
dependencies:
|
||||
postcss-value-parser "^4.0.0"
|
||||
read-cache "^1.0.0"
|
||||
resolve "^1.1.7"
|
||||
|
||||
postcss-js@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2"
|
||||
integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==
|
||||
dependencies:
|
||||
camelcase-css "^2.0.1"
|
||||
|
||||
postcss-load-config@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3"
|
||||
integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==
|
||||
dependencies:
|
||||
lilconfig "^3.0.0"
|
||||
yaml "^2.3.4"
|
||||
|
||||
postcss-nested@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c"
|
||||
integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.0.11"
|
||||
|
||||
postcss-selector-parser@^6.0.11:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz#49694cb4e7c649299fea510a29fa6577104bcf53"
|
||||
integrity sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss@^8.4.23:
|
||||
version "8.4.39"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.39.tgz#aa3c94998b61d3a9c259efa51db4b392e1bde0e3"
|
||||
integrity sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==
|
||||
dependencies:
|
||||
nanoid "^3.3.7"
|
||||
picocolors "^1.0.1"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
postcss@^8.4.32:
|
||||
version "8.4.33"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742"
|
||||
@ -2421,6 +2711,13 @@ react@^18.2.0:
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
read-cache@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
|
||||
integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==
|
||||
dependencies:
|
||||
pify "^2.3.0"
|
||||
|
||||
readdirp@~3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||
@ -2474,6 +2771,15 @@ resolve-from@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
|
||||
|
||||
resolve@^1.1.7, resolve@^1.22.2:
|
||||
version "1.22.8"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
|
||||
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
||||
dependencies:
|
||||
is-core-module "^2.13.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
resolve@^2.0.0-next.4:
|
||||
version "2.0.0-next.5"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c"
|
||||
@ -2652,6 +2958,11 @@ signal-exit@^3.0.3:
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
|
||||
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
|
||||
|
||||
signal-exit@^4.0.1:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
|
||||
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
|
||||
|
||||
simple-update-notifier@^1.0.7:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82"
|
||||
@ -2664,6 +2975,11 @@ source-map-js@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
|
||||
source-map-js@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||
|
||||
source-map-support@~0.5.20:
|
||||
version "0.5.21"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
||||
@ -2672,12 +2988,12 @@ source-map-support@~0.5.20:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map@^0.6.0:
|
||||
source-map@^0.6.0, source-map@~0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
string-width@^4.1.0:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -2737,7 +3053,7 @@ string.prototype.trimstart@^1.0.7:
|
||||
define-properties "^1.2.0"
|
||||
es-abstract "^1.22.1"
|
||||
|
||||
strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@ -2766,6 +3082,19 @@ strip-json-comments@~2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
|
||||
|
||||
sucrase@^3.32.0:
|
||||
version "3.35.0"
|
||||
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263"
|
||||
integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.2"
|
||||
commander "^4.0.0"
|
||||
glob "^10.3.10"
|
||||
lines-and-columns "^1.1.6"
|
||||
mz "^2.7.0"
|
||||
pirates "^4.0.1"
|
||||
ts-interface-checker "^0.1.9"
|
||||
|
||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
@ -2785,6 +3114,34 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
tailwindcss@3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.1.tgz#f512ca5d1dd4c9503c7d3d28a968f1ad8f5c839d"
|
||||
integrity sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==
|
||||
dependencies:
|
||||
"@alloc/quick-lru" "^5.2.0"
|
||||
arg "^5.0.2"
|
||||
chokidar "^3.5.3"
|
||||
didyoumean "^1.2.2"
|
||||
dlv "^1.1.3"
|
||||
fast-glob "^3.3.0"
|
||||
glob-parent "^6.0.2"
|
||||
is-glob "^4.0.3"
|
||||
jiti "^1.19.1"
|
||||
lilconfig "^2.1.0"
|
||||
micromatch "^4.0.5"
|
||||
normalize-path "^3.0.0"
|
||||
object-hash "^3.0.0"
|
||||
picocolors "^1.0.0"
|
||||
postcss "^8.4.23"
|
||||
postcss-import "^15.1.0"
|
||||
postcss-js "^4.0.1"
|
||||
postcss-load-config "^4.0.1"
|
||||
postcss-nested "^6.0.1"
|
||||
postcss-selector-parser "^6.0.11"
|
||||
resolve "^1.22.2"
|
||||
sucrase "^3.32.0"
|
||||
|
||||
terser@^5.27.0:
|
||||
version "5.27.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c"
|
||||
@ -2800,6 +3157,20 @@ text-table@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
||||
|
||||
thenify-all@^1.0.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
|
||||
integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==
|
||||
dependencies:
|
||||
thenify ">= 3.1.0 < 4"
|
||||
|
||||
"thenify@>= 3.1.0 < 4":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
|
||||
integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
|
||||
to-fast-properties@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
||||
@ -2819,6 +3190,11 @@ touch@^3.1.0:
|
||||
dependencies:
|
||||
nopt "~1.0.10"
|
||||
|
||||
ts-interface-checker@^0.1.9:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
|
||||
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
|
||||
|
||||
type-check@^0.4.0, type-check@~0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
|
||||
@ -2918,6 +3294,11 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
util-deprecate@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
uuid@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
|
||||
@ -3010,7 +3391,16 @@ widest-line@^4.0.1:
|
||||
dependencies:
|
||||
string-width "^5.0.1"
|
||||
|
||||
wrap-ansi@^8.0.1:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||
integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
|
||||
@ -3029,6 +3419,11 @@ yallist@^3.0.2:
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||
|
||||
yaml@^2.3.4:
|
||||
version "2.4.5"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e"
|
||||
integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
|
@ -4,7 +4,9 @@
|
||||
"target": "esnext",
|
||||
"jsx": "react",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,14 +19,18 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"he": "^1.2.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
"i18next": "^23.11.3",
|
||||
"i18next-browser-languagedetector": "^7.2.1",
|
||||
"js-levenshtein": "^1.1.6",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"markdown-it": "^13.0.1",
|
||||
"moment": "^2.30.1",
|
||||
"pluralize": "^8.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-device-detect": "^2.2.2",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-i18next": "^14.1.1",
|
||||
"react-loading-skeleton": "^3.1.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-speech-recognition": "^3.10.0",
|
||||
@ -64,4 +68,4 @@
|
||||
"tailwindcss": "^3.3.1",
|
||||
"vite": "^4.3.0"
|
||||
}
|
||||
}
|
||||
}
|
1
frontend/public/embed/anythingllm-chat-widget.min.css
vendored
Normal file
1
frontend/public/embed/anythingllm-chat-widget.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,5 +1,6 @@
|
||||
import React, { lazy, Suspense } from "react";
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import { ContextWrapper } from "@/AuthContext";
|
||||
import PrivateRoute, {
|
||||
AdminRoute,
|
||||
@ -9,9 +10,11 @@ import { ToastContainer } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import Login from "@/pages/Login";
|
||||
import OnboardingFlow from "@/pages/OnboardingFlow";
|
||||
import i18n from "./i18n";
|
||||
|
||||
import { PfpProvider } from "./PfpContext";
|
||||
import { LogoProvider } from "./LogoContext";
|
||||
import { FullScreenLoader } from "./components/Preloader";
|
||||
|
||||
const Main = lazy(() => import("@/pages/Main"));
|
||||
const InvitePage = lazy(() => import("@/pages/Invite"));
|
||||
@ -21,6 +24,7 @@ const AdminInvites = lazy(() => import("@/pages/Admin/Invitations"));
|
||||
const AdminWorkspaces = lazy(() => import("@/pages/Admin/Workspaces"));
|
||||
const AdminSystem = lazy(() => import("@/pages/Admin/System"));
|
||||
const AdminLogs = lazy(() => import("@/pages/Admin/Logging"));
|
||||
const AdminAgents = lazy(() => import("@/pages/Admin/Agents"));
|
||||
const GeneralChats = lazy(() => import("@/pages/GeneralSettings/Chats"));
|
||||
const GeneralAppearance = lazy(
|
||||
() => import("@/pages/GeneralSettings/Appearance")
|
||||
@ -53,113 +57,138 @@ const EmbedChats = lazy(() => import("@/pages/GeneralSettings/EmbedChats"));
|
||||
const PrivacyAndData = lazy(
|
||||
() => import("@/pages/GeneralSettings/PrivacyAndData")
|
||||
);
|
||||
const ExperimentalFeatures = lazy(
|
||||
() => import("@/pages/Admin/ExperimentalFeatures")
|
||||
);
|
||||
const LiveDocumentSyncManage = lazy(
|
||||
() => import("@/pages/Admin/ExperimentalFeatures/Features/LiveSync/manage")
|
||||
);
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Suspense fallback={<div />}>
|
||||
<Suspense fallback={<FullScreenLoader />}>
|
||||
<ContextWrapper>
|
||||
<LogoProvider>
|
||||
<PfpProvider>
|
||||
<Routes>
|
||||
<Route path="/" element={<PrivateRoute Component={Main} />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route
|
||||
path="/workspace/:slug/settings/:tab"
|
||||
element={<ManagerRoute Component={WorkspaceSettings} />}
|
||||
/>
|
||||
<Route
|
||||
path="/workspace/:slug"
|
||||
element={<PrivateRoute Component={WorkspaceChat} />}
|
||||
/>
|
||||
<Route
|
||||
path="/workspace/:slug/t/:threadSlug"
|
||||
element={<PrivateRoute Component={WorkspaceChat} />}
|
||||
/>
|
||||
<Route path="/accept-invite/:code" element={<InvitePage />} />
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Routes>
|
||||
<Route path="/" element={<PrivateRoute Component={Main} />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route
|
||||
path="/workspace/:slug/settings/:tab"
|
||||
element={<ManagerRoute Component={WorkspaceSettings} />}
|
||||
/>
|
||||
<Route
|
||||
path="/workspace/:slug"
|
||||
element={<PrivateRoute Component={WorkspaceChat} />}
|
||||
/>
|
||||
<Route
|
||||
path="/workspace/:slug/t/:threadSlug"
|
||||
element={<PrivateRoute Component={WorkspaceChat} />}
|
||||
/>
|
||||
<Route path="/accept-invite/:code" element={<InvitePage />} />
|
||||
|
||||
{/* Admin */}
|
||||
<Route
|
||||
path="/settings/llm-preference"
|
||||
element={<AdminRoute Component={GeneralLLMPreference} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/transcription-preference"
|
||||
element={
|
||||
<AdminRoute Component={GeneralTranscriptionPreference} />
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/audio-preference"
|
||||
element={<AdminRoute Component={GeneralAudioPreference} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/embedding-preference"
|
||||
element={<AdminRoute Component={GeneralEmbeddingPreference} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/text-splitter-preference"
|
||||
element={
|
||||
<AdminRoute Component={EmbeddingTextSplitterPreference} />
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/vector-database"
|
||||
element={<AdminRoute Component={GeneralVectorDatabase} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/event-logs"
|
||||
element={<AdminRoute Component={AdminLogs} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/embed-config"
|
||||
element={<AdminRoute Component={EmbedConfigSetup} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/embed-chats"
|
||||
element={<AdminRoute Component={EmbedChats} />}
|
||||
/>
|
||||
{/* Manager */}
|
||||
<Route
|
||||
path="/settings/security"
|
||||
element={<ManagerRoute Component={GeneralSecurity} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/privacy"
|
||||
element={<AdminRoute Component={PrivacyAndData} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/appearance"
|
||||
element={<ManagerRoute Component={GeneralAppearance} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/api-keys"
|
||||
element={<AdminRoute Component={GeneralApiKeys} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/workspace-chats"
|
||||
element={<ManagerRoute Component={GeneralChats} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/system-preferences"
|
||||
element={<ManagerRoute Component={AdminSystem} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/invites"
|
||||
element={<ManagerRoute Component={AdminInvites} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/users"
|
||||
element={<ManagerRoute Component={AdminUsers} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/workspaces"
|
||||
element={<ManagerRoute Component={AdminWorkspaces} />}
|
||||
/>
|
||||
{/* Onboarding Flow */}
|
||||
<Route path="/onboarding" element={<OnboardingFlow />} />
|
||||
<Route path="/onboarding/:step" element={<OnboardingFlow />} />
|
||||
</Routes>
|
||||
<ToastContainer />
|
||||
{/* Admin */}
|
||||
<Route
|
||||
path="/settings/llm-preference"
|
||||
element={<AdminRoute Component={GeneralLLMPreference} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/transcription-preference"
|
||||
element={
|
||||
<AdminRoute Component={GeneralTranscriptionPreference} />
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/audio-preference"
|
||||
element={<AdminRoute Component={GeneralAudioPreference} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/embedding-preference"
|
||||
element={
|
||||
<AdminRoute Component={GeneralEmbeddingPreference} />
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/text-splitter-preference"
|
||||
element={
|
||||
<AdminRoute Component={EmbeddingTextSplitterPreference} />
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/vector-database"
|
||||
element={<AdminRoute Component={GeneralVectorDatabase} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/agents"
|
||||
element={<AdminRoute Component={AdminAgents} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/event-logs"
|
||||
element={<AdminRoute Component={AdminLogs} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/embed-config"
|
||||
element={<AdminRoute Component={EmbedConfigSetup} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/embed-chats"
|
||||
element={<AdminRoute Component={EmbedChats} />}
|
||||
/>
|
||||
{/* Manager */}
|
||||
<Route
|
||||
path="/settings/security"
|
||||
element={<ManagerRoute Component={GeneralSecurity} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/privacy"
|
||||
element={<AdminRoute Component={PrivacyAndData} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/appearance"
|
||||
element={<ManagerRoute Component={GeneralAppearance} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/beta-features"
|
||||
element={<AdminRoute Component={ExperimentalFeatures} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/api-keys"
|
||||
element={<AdminRoute Component={GeneralApiKeys} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/workspace-chats"
|
||||
element={<ManagerRoute Component={GeneralChats} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/system-preferences"
|
||||
element={<ManagerRoute Component={AdminSystem} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/invites"
|
||||
element={<ManagerRoute Component={AdminInvites} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/users"
|
||||
element={<ManagerRoute Component={AdminUsers} />}
|
||||
/>
|
||||
<Route
|
||||
path="/settings/workspaces"
|
||||
element={<ManagerRoute Component={AdminWorkspaces} />}
|
||||
/>
|
||||
{/* Onboarding Flow */}
|
||||
<Route path="/onboarding" element={<OnboardingFlow />} />
|
||||
<Route path="/onboarding/:step" element={<OnboardingFlow />} />
|
||||
|
||||
{/* Experimental feature pages */}
|
||||
{/* Live Document Sync feature */}
|
||||
<Route
|
||||
path="/settings/beta-features/live-document-sync/manage"
|
||||
element={<AdminRoute Component={LiveDocumentSyncManage} />}
|
||||
/>
|
||||
</Routes>
|
||||
<ToastContainer />
|
||||
</I18nextProvider>
|
||||
</PfpProvider>
|
||||
</LogoProvider>
|
||||
</ContextWrapper>
|
||||
|
32
frontend/src/components/ContextualSaveBar/index.jsx
Normal file
32
frontend/src/components/ContextualSaveBar/index.jsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { Warning } from "@phosphor-icons/react";
|
||||
|
||||
export default function ContextualSaveBar({
|
||||
showing = false,
|
||||
onSave,
|
||||
onCancel,
|
||||
}) {
|
||||
if (!showing) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed top-0 left-0 right-0 h-14 bg-dark-input flex items-center justify-end px-4 z-[999]">
|
||||
<div className="absolute ml-4 left-0 md:left-1/2 transform md:-translate-x-1/2 flex items-center gap-x-2">
|
||||
<Warning size={18} className="text-white" />
|
||||
<p className="text-white font-medium text-xs">Unsaved Changes</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<button
|
||||
className="border-none text-white font-medium text-sm px-[10px] py-[6px] rounded-md bg-white/5 hover:bg-white/10"
|
||||
onClick={onCancel}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="border-none text-dark-text font-medium text-sm px-[10px] py-[6px] rounded-md bg-primary-button hover:bg-[#3DB5E8]"
|
||||
onClick={onSave}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { X } from "@phosphor-icons/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function EditingChatBubble({
|
||||
message,
|
||||
@ -11,11 +12,12 @@ export default function EditingChatBubble({
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [tempMessage, setTempMessage] = useState(message[type]);
|
||||
const isUser = type === "user";
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p className={`text-xs text-[#D3D4D4] ${isUser ? "text-right" : ""}`}>
|
||||
{isUser ? "User" : "AnythingLLM Chat Assistant"}
|
||||
{isUser ? t("common.user") : t("appearance.message.assistant")}
|
||||
</p>
|
||||
<div
|
||||
className={`relative flex w-full mt-2 items-start ${
|
||||
|
@ -29,7 +29,7 @@ export default function EmbedderItem({
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-semibold text-white">{name}</div>
|
||||
<div className="mt-1 text-xs text-[#D2D5DB]">{description}</div>
|
||||
<div className="mt-1 text-xs text-description">{description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,74 @@
|
||||
export default function GenericOpenAiEmbeddingOptions({ settings }) {
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-y-4">
|
||||
<div className="w-full flex items-center gap-4 flex-wrap">
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Base URL
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
name="EmbeddingBasePath"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="https://api.openai.com/v1"
|
||||
defaultValue={settings?.EmbeddingBasePath}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Embedding Model
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="EmbeddingModelPref"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="text-embedding-ada-002"
|
||||
defaultValue={settings?.EmbeddingModelPref}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Max embedding chunk length
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="EmbeddingModelMaxChunkLength"
|
||||
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="8192"
|
||||
min={1}
|
||||
onScroll={(e) => e.target.blur()}
|
||||
defaultValue={settings?.EmbeddingModelMaxChunkLength}
|
||||
required={false}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex items-center gap-4">
|
||||
<div className="flex flex-col w-60">
|
||||
<div className="flex flex-col gap-y-1 mb-4">
|
||||
<label className="text-white text-sm font-semibold flex items-center gap-x-2">
|
||||
API Key <p className="!text-xs !italic !font-thin">optional</p>
|
||||
</label>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
name="GenericOpenAiEmbeddingApiKey"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="sk-mysecretkey"
|
||||
defaultValue={
|
||||
settings?.GenericOpenAiEmbeddingApiKey ? "*".repeat(20) : ""
|
||||
}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,48 +1,112 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import System from "@/models/system";
|
||||
import PreLoader from "@/components/Preloader";
|
||||
import { LMSTUDIO_COMMON_URLS } from "@/utils/constants";
|
||||
import { CaretDown, CaretUp } from "@phosphor-icons/react";
|
||||
import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery";
|
||||
|
||||
export default function LMStudioEmbeddingOptions({ settings }) {
|
||||
const [basePathValue, setBasePathValue] = useState(
|
||||
settings?.EmbeddingBasePath
|
||||
const {
|
||||
autoDetecting: loading,
|
||||
basePath,
|
||||
basePathValue,
|
||||
showAdvancedControls,
|
||||
setShowAdvancedControls,
|
||||
handleAutoDetectClick,
|
||||
} = useProviderEndpointAutoDiscovery({
|
||||
provider: "lmstudio",
|
||||
initialBasePath: settings?.EmbeddingBasePath,
|
||||
ENDPOINTS: LMSTUDIO_COMMON_URLS,
|
||||
});
|
||||
|
||||
const [maxChunkLength, setMaxChunkLength] = useState(
|
||||
settings?.EmbeddingModelMaxChunkLength || 8192
|
||||
);
|
||||
const [basePath, setBasePath] = useState(settings?.EmbeddingBasePath);
|
||||
|
||||
const handleMaxChunkLengthChange = (e) => {
|
||||
setMaxChunkLength(Number(e.target.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-y-4">
|
||||
<div className="w-full flex items-center gap-4">
|
||||
<div className="w-full flex items-start gap-4">
|
||||
<LMStudioModelSelection settings={settings} basePath={basePath.value} />
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
LMStudio Base URL
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
name="EmbeddingBasePath"
|
||||
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="http://localhost:1234/v1"
|
||||
defaultValue={settings?.EmbeddingBasePath}
|
||||
onChange={(e) => setBasePathValue(e.target.value)}
|
||||
onBlur={() => setBasePath(basePathValue)}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
<LMStudioModelSelection settings={settings} basePath={basePath} />
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Max embedding chunk length
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
Max Embedding Chunk Length
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="EmbeddingModelMaxChunkLength"
|
||||
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="8192"
|
||||
min={1}
|
||||
value={maxChunkLength}
|
||||
onChange={handleMaxChunkLengthChange}
|
||||
onScroll={(e) => e.target.blur()}
|
||||
defaultValue={settings?.EmbeddingModelMaxChunkLength}
|
||||
required={false}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Maximum length of text chunks for embedding.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-start mt-4">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setShowAdvancedControls(!showAdvancedControls);
|
||||
}}
|
||||
className="text-white hover:text-white/70 flex items-center text-sm"
|
||||
>
|
||||
{showAdvancedControls ? "Hide" : "Show"} Manual Endpoint Input
|
||||
{showAdvancedControls ? (
|
||||
<CaretUp size={14} className="ml-1" />
|
||||
) : (
|
||||
<CaretDown size={14} className="ml-1" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div hidden={!showAdvancedControls}>
|
||||
<div className="w-full flex items-start gap-4">
|
||||
<div className="flex flex-col w-60">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<label className="text-white text-sm font-semibold">
|
||||
LM Studio Base URL
|
||||
</label>
|
||||
{loading ? (
|
||||
<PreLoader size="6" />
|
||||
) : (
|
||||
<>
|
||||
{!basePathValue.value && (
|
||||
<button
|
||||
onClick={handleAutoDetectClick}
|
||||
className="bg-primary-button text-xs font-medium px-2 py-1 rounded-lg hover:bg-secondary hover:text-white shadow-[0_4px_14px_rgba(0,0,0,0.25)]"
|
||||
>
|
||||
Auto-Detect
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
type="url"
|
||||
name="EmbeddingBasePath"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="http://localhost:1234/v1"
|
||||
value={basePathValue.value}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
onChange={basePath.onChange}
|
||||
onBlur={basePath.onBlur}
|
||||
/>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Enter the URL where LM Studio is running.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,14 +119,23 @@ function LMStudioModelSelection({ settings, basePath = null }) {
|
||||
|
||||
useEffect(() => {
|
||||
async function findCustomModels() {
|
||||
if (!basePath || !basePath.includes("/v1")) {
|
||||
if (!basePath) {
|
||||
setCustomModels([]);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const { models } = await System.customModels("lmstudio", null, basePath);
|
||||
setCustomModels(models || []);
|
||||
try {
|
||||
const { models } = await System.customModels(
|
||||
"lmstudio",
|
||||
null,
|
||||
basePath
|
||||
);
|
||||
setCustomModels(models || []);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch custom models:", error);
|
||||
setCustomModels([]);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
findCustomModels();
|
||||
@ -71,8 +144,8 @@ function LMStudioModelSelection({ settings, basePath = null }) {
|
||||
if (loading || customModels.length == 0) {
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Chat Model Selection
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
LM Studio Embedding Model
|
||||
</label>
|
||||
<select
|
||||
name="EmbeddingModelPref"
|
||||
@ -80,19 +153,23 @@ function LMStudioModelSelection({ settings, basePath = null }) {
|
||||
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||
>
|
||||
<option disabled={true} selected={true}>
|
||||
{basePath?.includes("/v1")
|
||||
? "-- loading available models --"
|
||||
: "-- waiting for URL --"}
|
||||
{!!basePath
|
||||
? "--loading available models--"
|
||||
: "Enter LM Studio URL first"}
|
||||
</option>
|
||||
</select>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Select the LM Studio model for embeddings. Models will load after
|
||||
entering a valid LM Studio URL.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Chat Model Selection
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
LM Studio Embedding Model
|
||||
</label>
|
||||
<select
|
||||
name="EmbeddingModelPref"
|
||||
@ -115,6 +192,9 @@ function LMStudioModelSelection({ settings, basePath = null }) {
|
||||
</optgroup>
|
||||
)}
|
||||
</select>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Choose the LM Studio model you want to use for generating embeddings.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function NativeEmbeddingOptions() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="w-full h-10 items-center flex">
|
||||
<p className="text-sm font-base text-white text-opacity-60">
|
||||
There is no set up required when using AnythingLLM's native embedding
|
||||
engine.
|
||||
{t("embedding.provider.description")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,55 +1,122 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import System from "@/models/system";
|
||||
import PreLoader from "@/components/Preloader";
|
||||
import { OLLAMA_COMMON_URLS } from "@/utils/constants";
|
||||
import { CaretDown, CaretUp } from "@phosphor-icons/react";
|
||||
import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery";
|
||||
|
||||
export default function OllamaEmbeddingOptions({ settings }) {
|
||||
const [basePathValue, setBasePathValue] = useState(
|
||||
settings?.EmbeddingBasePath
|
||||
const {
|
||||
autoDetecting: loading,
|
||||
basePath,
|
||||
basePathValue,
|
||||
showAdvancedControls,
|
||||
setShowAdvancedControls,
|
||||
handleAutoDetectClick,
|
||||
} = useProviderEndpointAutoDiscovery({
|
||||
provider: "ollama",
|
||||
initialBasePath: settings?.EmbeddingBasePath,
|
||||
ENDPOINTS: OLLAMA_COMMON_URLS,
|
||||
});
|
||||
|
||||
const [maxChunkLength, setMaxChunkLength] = useState(
|
||||
settings?.EmbeddingModelMaxChunkLength || 8192
|
||||
);
|
||||
const [basePath, setBasePath] = useState(settings?.EmbeddingBasePath);
|
||||
|
||||
const handleMaxChunkLengthChange = (e) => {
|
||||
setMaxChunkLength(Number(e.target.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-y-4">
|
||||
<div className="w-full flex items-center gap-4">
|
||||
<div className="w-full flex items-start gap-4">
|
||||
<OllamaEmbeddingModelSelection
|
||||
settings={settings}
|
||||
basePath={basePath.value}
|
||||
/>
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Ollama Base URL
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
name="EmbeddingBasePath"
|
||||
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="http://127.0.0.1:11434"
|
||||
defaultValue={settings?.EmbeddingBasePath}
|
||||
onChange={(e) => setBasePathValue(e.target.value)}
|
||||
onBlur={() => setBasePath(basePathValue)}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
<OllamaLLMModelSelection settings={settings} basePath={basePath} />
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Max embedding chunk length
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
Max Embedding Chunk Length
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="EmbeddingModelMaxChunkLength"
|
||||
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="8192"
|
||||
min={1}
|
||||
value={maxChunkLength}
|
||||
onChange={handleMaxChunkLengthChange}
|
||||
onScroll={(e) => e.target.blur()}
|
||||
defaultValue={settings?.EmbeddingModelMaxChunkLength}
|
||||
required={false}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Maximum length of text chunks for embedding.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-start mt-4">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setShowAdvancedControls(!showAdvancedControls);
|
||||
}}
|
||||
className="text-white hover:text-white/70 flex items-center text-sm"
|
||||
>
|
||||
{showAdvancedControls ? "Hide" : "Show"} Manual Endpoint Input
|
||||
{showAdvancedControls ? (
|
||||
<CaretUp size={14} className="ml-1" />
|
||||
) : (
|
||||
<CaretDown size={14} className="ml-1" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div hidden={!showAdvancedControls}>
|
||||
<div className="w-full flex items-start gap-4">
|
||||
<div className="flex flex-col w-60">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<label className="text-white text-sm font-semibold">
|
||||
Ollama Base URL
|
||||
</label>
|
||||
{loading ? (
|
||||
<PreLoader size="6" />
|
||||
) : (
|
||||
<>
|
||||
{!basePathValue.value && (
|
||||
<button
|
||||
onClick={handleAutoDetectClick}
|
||||
className="bg-primary-button text-xs font-medium px-2 py-1 rounded-lg hover:bg-secondary hover:text-white shadow-[0_4px_14px_rgba(0,0,0,0.25)]"
|
||||
>
|
||||
Auto-Detect
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
type="url"
|
||||
name="EmbeddingBasePath"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="http://127.0.0.1:11434"
|
||||
value={basePathValue.value}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
onChange={basePath.onChange}
|
||||
onBlur={basePath.onBlur}
|
||||
/>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Enter the URL where Ollama is running.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OllamaLLMModelSelection({ settings, basePath = null }) {
|
||||
function OllamaEmbeddingModelSelection({ settings, basePath = null }) {
|
||||
const [customModels, setCustomModels] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
@ -61,8 +128,13 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const { models } = await System.customModels("ollama", null, basePath);
|
||||
setCustomModels(models || []);
|
||||
try {
|
||||
const { models } = await System.customModels("ollama", null, basePath);
|
||||
setCustomModels(models || []);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch custom models:", error);
|
||||
setCustomModels([]);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
findCustomModels();
|
||||
@ -71,33 +143,37 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
|
||||
if (loading || customModels.length == 0) {
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Embedding Model Selection
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
Ollama Embedding Model
|
||||
</label>
|
||||
<select
|
||||
name="EmbeddingModelPref"
|
||||
disabled={true}
|
||||
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||
>
|
||||
<option disabled={true} selected={true}>
|
||||
{!!basePath
|
||||
? "-- loading available models --"
|
||||
: "-- waiting for URL --"}
|
||||
? "--loading available models--"
|
||||
: "Enter Ollama URL first"}
|
||||
</option>
|
||||
</select>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Select the Ollama model for embeddings. Models will load after
|
||||
entering a valid Ollama URL.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Embedding Model Selection
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
Ollama Embedding Model
|
||||
</label>
|
||||
<select
|
||||
name="EmbeddingModelPref"
|
||||
required={true}
|
||||
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||
>
|
||||
{customModels.length > 0 && (
|
||||
<optgroup label="Your loaded models">
|
||||
@ -115,6 +191,9 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
|
||||
</optgroup>
|
||||
)}
|
||||
</select>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Choose the Ollama model you want to use for generating embeddings.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ export default function AnthropicAiOptions({ settings }) {
|
||||
"claude-3-haiku-20240307",
|
||||
"claude-3-opus-20240229",
|
||||
"claude-3-sonnet-20240229",
|
||||
"claude-3-5-sonnet-20240620",
|
||||
].map((model) => {
|
||||
return (
|
||||
<option key={model} value={model}>
|
||||
|
@ -29,7 +29,7 @@ export default function LLMItem({
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-semibold text-white">{name}</div>
|
||||
<div className="mt-1 text-xs text-[#D2D5DB]">{description}</div>
|
||||
<div className="mt-1 text-xs text-description">{description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,13 +1,32 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Info } from "@phosphor-icons/react";
|
||||
import { Info, CaretDown, CaretUp } from "@phosphor-icons/react";
|
||||
import paths from "@/utils/paths";
|
||||
import System from "@/models/system";
|
||||
import PreLoader from "@/components/Preloader";
|
||||
import { LMSTUDIO_COMMON_URLS } from "@/utils/constants";
|
||||
import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery";
|
||||
|
||||
export default function LMStudioOptions({ settings, showAlert = false }) {
|
||||
const [basePathValue, setBasePathValue] = useState(
|
||||
settings?.LMStudioBasePath
|
||||
const {
|
||||
autoDetecting: loading,
|
||||
basePath,
|
||||
basePathValue,
|
||||
showAdvancedControls,
|
||||
setShowAdvancedControls,
|
||||
handleAutoDetectClick,
|
||||
} = useProviderEndpointAutoDiscovery({
|
||||
provider: "lmstudio",
|
||||
initialBasePath: settings?.LMStudioBasePath,
|
||||
ENDPOINTS: LMSTUDIO_COMMON_URLS,
|
||||
});
|
||||
|
||||
const [maxTokens, setMaxTokens] = useState(
|
||||
settings?.LMStudioTokenLimit || 4096
|
||||
);
|
||||
const [basePath, setBasePath] = useState(settings?.LMStudioBasePath);
|
||||
|
||||
const handleMaxTokensChange = (e) => {
|
||||
setMaxTokens(Number(e.target.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col">
|
||||
@ -28,45 +47,86 @@ export default function LMStudioOptions({ settings, showAlert = false }) {
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full flex items-center gap-4">
|
||||
<div className="w-full flex items-start gap-4">
|
||||
<LMStudioModelSelection settings={settings} basePath={basePath.value} />
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
LMStudio Base URL
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
Max Tokens
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
name="LMStudioBasePath"
|
||||
type="number"
|
||||
name="LMStudioTokenLimit"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="http://localhost:1234/v1"
|
||||
defaultValue={settings?.LMStudioBasePath}
|
||||
placeholder="4096"
|
||||
defaultChecked="4096"
|
||||
min={1}
|
||||
value={maxTokens}
|
||||
onChange={handleMaxTokensChange}
|
||||
onScroll={(e) => e.target.blur()}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
onChange={(e) => setBasePathValue(e.target.value)}
|
||||
onBlur={() => setBasePath(basePathValue)}
|
||||
/>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Maximum number of tokens for context and response.
|
||||
</p>
|
||||
</div>
|
||||
{!settings?.credentialsOnly && (
|
||||
<>
|
||||
<LMStudioModelSelection settings={settings} basePath={basePath} />
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Token context window
|
||||
</div>
|
||||
<div className="flex justify-start mt-4">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setShowAdvancedControls(!showAdvancedControls);
|
||||
}}
|
||||
className="text-white hover:text-white/70 flex items-center text-sm"
|
||||
>
|
||||
{showAdvancedControls ? "Hide" : "Show"} Manual Endpoint Input
|
||||
{showAdvancedControls ? (
|
||||
<CaretUp size={14} className="ml-1" />
|
||||
) : (
|
||||
<CaretDown size={14} className="ml-1" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div hidden={!showAdvancedControls}>
|
||||
<div className="w-full flex items-start gap-4 mt-4">
|
||||
<div className="flex flex-col w-60">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<label className="text-white text-sm font-semibold">
|
||||
LM Studio Base URL
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="LMStudioTokenLimit"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="4096"
|
||||
min={1}
|
||||
onScroll={(e) => e.target.blur()}
|
||||
defaultValue={settings?.LMStudioTokenLimit}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
/>
|
||||
{loading ? (
|
||||
<PreLoader size="6" />
|
||||
) : (
|
||||
<>
|
||||
{!basePathValue.value && (
|
||||
<button
|
||||
onClick={handleAutoDetectClick}
|
||||
className="bg-primary-button text-xs font-medium px-2 py-1 rounded-lg hover:bg-secondary hover:text-white shadow-[0_4px_14px_rgba(0,0,0,0.25)]"
|
||||
>
|
||||
Auto-Detect
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<input
|
||||
type="url"
|
||||
name="LMStudioBasePath"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="http://localhost:1234/v1"
|
||||
value={basePathValue.value}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
onChange={basePath.onChange}
|
||||
onBlur={basePath.onBlur}
|
||||
/>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Enter the URL where LM Studio is running.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -78,14 +138,23 @@ function LMStudioModelSelection({ settings, basePath = null }) {
|
||||
|
||||
useEffect(() => {
|
||||
async function findCustomModels() {
|
||||
if (!basePath || !basePath.includes("/v1")) {
|
||||
if (!basePath) {
|
||||
setCustomModels([]);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const { models } = await System.customModels("lmstudio", null, basePath);
|
||||
setCustomModels(models || []);
|
||||
try {
|
||||
const { models } = await System.customModels(
|
||||
"lmstudio",
|
||||
null,
|
||||
basePath
|
||||
);
|
||||
setCustomModels(models || []);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch custom models:", error);
|
||||
setCustomModels([]);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
findCustomModels();
|
||||
@ -94,8 +163,8 @@ function LMStudioModelSelection({ settings, basePath = null }) {
|
||||
if (loading || customModels.length == 0) {
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Chat Model Selection
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
LM Studio Model
|
||||
</label>
|
||||
<select
|
||||
name="LMStudioModelPref"
|
||||
@ -103,19 +172,23 @@ function LMStudioModelSelection({ settings, basePath = null }) {
|
||||
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
|
||||
>
|
||||
<option disabled={true} selected={true}>
|
||||
{basePath?.includes("/v1")
|
||||
? "-- loading available models --"
|
||||
: "-- waiting for URL --"}
|
||||
{!!basePath
|
||||
? "--loading available models--"
|
||||
: "Enter LM Studio URL first"}
|
||||
</option>
|
||||
</select>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Select the LM Studio model you want to use. Models will load after
|
||||
entering a valid LM Studio URL.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Chat Model Selection
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
LM Studio Model
|
||||
</label>
|
||||
<select
|
||||
name="LMStudioModelPref"
|
||||
@ -138,6 +211,9 @@ function LMStudioModelSelection({ settings, basePath = null }) {
|
||||
</optgroup>
|
||||
)}
|
||||
</select>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Choose the LM Studio model you want to use for your conversations.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,53 +1,117 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import System from "@/models/system";
|
||||
import PreLoader from "@/components/Preloader";
|
||||
import { OLLAMA_COMMON_URLS } from "@/utils/constants";
|
||||
import { CaretDown, CaretUp } from "@phosphor-icons/react";
|
||||
import useProviderEndpointAutoDiscovery from "@/hooks/useProviderEndpointAutoDiscovery";
|
||||
|
||||
export default function OllamaLLMOptions({ settings }) {
|
||||
const [basePathValue, setBasePathValue] = useState(
|
||||
settings?.OllamaLLMBasePath
|
||||
const {
|
||||
autoDetecting: loading,
|
||||
basePath,
|
||||
basePathValue,
|
||||
showAdvancedControls,
|
||||
setShowAdvancedControls,
|
||||
handleAutoDetectClick,
|
||||
} = useProviderEndpointAutoDiscovery({
|
||||
provider: "ollama",
|
||||
initialBasePath: settings?.OllamaLLMBasePath,
|
||||
ENDPOINTS: OLLAMA_COMMON_URLS,
|
||||
});
|
||||
|
||||
const [maxTokens, setMaxTokens] = useState(
|
||||
settings?.OllamaLLMTokenLimit || 4096
|
||||
);
|
||||
const [basePath, setBasePath] = useState(settings?.OllamaLLMBasePath);
|
||||
|
||||
const handleMaxTokensChange = (e) => {
|
||||
setMaxTokens(Number(e.target.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col gap-y-4">
|
||||
<div className="w-full flex items-center gap-4">
|
||||
<div className="w-full flex items-start gap-4">
|
||||
<OllamaLLMModelSelection
|
||||
settings={settings}
|
||||
basePath={basePath.value}
|
||||
/>
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Ollama Base URL
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
Max Tokens
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
name="OllamaLLMBasePath"
|
||||
type="number"
|
||||
name="OllamaLLMTokenLimit"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="http://127.0.0.1:11434"
|
||||
defaultValue={settings?.OllamaLLMBasePath}
|
||||
placeholder="4096"
|
||||
defaultChecked="4096"
|
||||
min={1}
|
||||
value={maxTokens}
|
||||
onChange={handleMaxTokensChange}
|
||||
onScroll={(e) => e.target.blur()}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
onChange={(e) => setBasePathValue(e.target.value)}
|
||||
onBlur={() => setBasePath(basePathValue)}
|
||||
/>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Maximum number of tokens for context and response.
|
||||
</p>
|
||||
</div>
|
||||
{!settings?.credentialsOnly && (
|
||||
<>
|
||||
<OllamaLLMModelSelection settings={settings} basePath={basePath} />
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Token context window
|
||||
</div>
|
||||
<div className="flex justify-start mt-4">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setShowAdvancedControls(!showAdvancedControls);
|
||||
}}
|
||||
className="text-white hover:text-white/70 flex items-center text-sm"
|
||||
>
|
||||
{showAdvancedControls ? "Hide" : "Show"} Manual Endpoint Input
|
||||
{showAdvancedControls ? (
|
||||
<CaretUp size={14} className="ml-1" />
|
||||
) : (
|
||||
<CaretDown size={14} className="ml-1" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div hidden={!showAdvancedControls}>
|
||||
<div className="w-full flex items-start gap-4">
|
||||
<div className="flex flex-col w-60">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<label className="text-white text-sm font-semibold">
|
||||
Ollama Base URL
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="OllamaLLMTokenLimit"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="4096"
|
||||
min={1}
|
||||
onScroll={(e) => e.target.blur()}
|
||||
defaultValue={settings?.OllamaLLMTokenLimit}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
/>
|
||||
{loading ? (
|
||||
<PreLoader size="6" />
|
||||
) : (
|
||||
<>
|
||||
{!basePathValue.value && (
|
||||
<button
|
||||
onClick={handleAutoDetectClick}
|
||||
className="bg-primary-button text-xs font-medium px-2 py-1 rounded-lg hover:bg-secondary hover:text-white shadow-[0_4px_14px_rgba(0,0,0,0.25)]"
|
||||
>
|
||||
Auto-Detect
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<input
|
||||
type="url"
|
||||
name="OllamaLLMBasePath"
|
||||
className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5"
|
||||
placeholder="http://127.0.0.1:11434"
|
||||
value={basePathValue.value}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
onChange={basePath.onChange}
|
||||
onBlur={basePath.onBlur}
|
||||
/>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Enter the URL where Ollama is running.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -65,8 +129,13 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const { models } = await System.customModels("ollama", null, basePath);
|
||||
setCustomModels(models || []);
|
||||
try {
|
||||
const { models } = await System.customModels("ollama", null, basePath);
|
||||
setCustomModels(models || []);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch custom models:", error);
|
||||
setCustomModels([]);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
findCustomModels();
|
||||
@ -75,8 +144,8 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
|
||||
if (loading || customModels.length == 0) {
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Chat Model Selection
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
Ollama Model
|
||||
</label>
|
||||
<select
|
||||
name="OllamaLLMModelPref"
|
||||
@ -85,18 +154,22 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
|
||||
>
|
||||
<option disabled={true} selected={true}>
|
||||
{!!basePath
|
||||
? "-- loading available models --"
|
||||
: "-- waiting for URL --"}
|
||||
? "--loading available models--"
|
||||
: "Enter Ollama URL first"}
|
||||
</option>
|
||||
</select>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Select the Ollama model you want to use. Models will load after
|
||||
entering a valid Ollama URL.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Chat Model Selection
|
||||
<label className="text-white text-sm font-semibold block mb-2">
|
||||
Ollama Model
|
||||
</label>
|
||||
<select
|
||||
name="OllamaLLMModelPref"
|
||||
@ -119,6 +192,9 @@ function OllamaLLMModelSelection({ settings, basePath = null }) {
|
||||
</optgroup>
|
||||
)}
|
||||
</select>
|
||||
<p className="text-xs leading-[18px] font-base text-white text-opacity-60 mt-2">
|
||||
Choose the Ollama model you want to use for your conversations.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,28 @@
|
||||
import { createPortal } from "react-dom";
|
||||
/**
|
||||
* @typedef {Object} ModalWrapperProps
|
||||
* @property {import("react").ReactComponentElement} children - The DOM/JSX to render
|
||||
* @property {boolean} isOpen - Option that renders the modal
|
||||
* @property {boolean} noPortal - (default: false) Used for creating sub-DOM modals that need to be rendered as a child element instead of a modal placed at the root
|
||||
* Note: This can impact the bg-overlay presentation due to conflicting DOM positions so if using this property you should
|
||||
double check it renders as desired.
|
||||
*/
|
||||
|
||||
export default function ModalWrapper({ children, isOpen }) {
|
||||
/**
|
||||
* @param {ModalWrapperProps} props - ModalWrapperProps to pass
|
||||
* @returns {import("react").ReactNode}
|
||||
*/
|
||||
export default function ModalWrapper({ children, isOpen, noPortal = false }) {
|
||||
if (!isOpen) return null;
|
||||
|
||||
if (noPortal) {
|
||||
return (
|
||||
<div className="bg-black/60 backdrop-blur-sm fixed top-0 left-0 outline-none w-screen h-screen flex items-center justify-center z-30">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return createPortal(
|
||||
<div className="bg-black/60 backdrop-blur-sm fixed top-0 left-0 outline-none w-screen h-screen flex items-center justify-center z-30">
|
||||
{children}
|
||||
|
@ -32,7 +32,7 @@ export default function RecoveryCodeModal({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="inline-block bg-[#2C2F36] rounded-lg text-left overflow-hidden shadow-xl transform transition-all border-2 border-[#BCC9DB]/10 w-[600px] mx-4">
|
||||
<div className="inline-block bg-secondary rounded-lg text-left overflow-hidden shadow-xl transform transition-all border-2 border-[#BCC9DB]/10 w-[600px] mx-4">
|
||||
<div className="md:py-[35px] md:px-[50px] py-[28px] px-[20px]">
|
||||
<div className="flex gap-x-2">
|
||||
<Key size={24} className="text-white" weight="bold" />
|
||||
@ -51,7 +51,7 @@ export default function RecoveryCodeModal({
|
||||
<b className="mt-4">These recovery codes are only shown once!</b>
|
||||
</p>
|
||||
<div
|
||||
className="bg-[#1C1E21] text-white hover:text-[#46C8FF]
|
||||
className="bg-dark-highlight text-white hover:text-primary-button
|
||||
flex items-center justify-center rounded-md mt-6 cursor-pointer"
|
||||
onClick={handleCopyToClipboard}
|
||||
>
|
||||
@ -68,7 +68,7 @@ export default function RecoveryCodeModal({
|
||||
<div className="flex w-full justify-center items-center p-3 space-x-2 rounded-b border-gray-500/50 -mt-4 mb-4">
|
||||
<button
|
||||
type="button"
|
||||
className="transition-all duration-300 text-xs md:w-[500px] md:h-[34px] h-[48px] w-full m-2 font-semibold rounded-lg bg-[#46C8FF] hover:bg-[#2C2F36] border-2 border-transparent hover:border-[#46C8FF] hover:text-white whitespace-nowrap shadow-[0_4px_14px_rgba(0,0,0,0.25)] flex justify-center items-center gap-x-2"
|
||||
className="transition-all duration-300 text-xs md:w-[500px] md:h-[34px] h-[48px] w-full m-2 font-semibold rounded-lg bg-primary-button hover:bg-secondary border-2 border-transparent hover:border-[#46C8FF] hover:text-white whitespace-nowrap shadow-[0_4px_14px_rgba(0,0,0,0.25)] flex justify-center items-center gap-x-2"
|
||||
onClick={downloadClicked ? handleClose : downloadRecoveryCodes}
|
||||
>
|
||||
{downloadClicked ? (
|
||||
|
@ -146,7 +146,7 @@ export default function ConfluenceOptions() {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="mt-2 w-full justify-center border border-slate-200 px-4 py-2 rounded-lg text-[#222628] text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed"
|
||||
className="mt-2 w-full justify-center border border-slate-200 px-4 py-2 rounded-lg text-dark-text text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed"
|
||||
>
|
||||
{loading ? "Collecting pages..." : "Submit"}
|
||||
</button>
|
||||
|
@ -146,7 +146,7 @@ export default function GithubOptions() {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="mt-2 w-full justify-center border border-slate-200 px-4 py-2 rounded-lg text-[#222628] text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed"
|
||||
className="mt-2 w-full justify-center border border-slate-200 px-4 py-2 rounded-lg text-dark-text text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed"
|
||||
>
|
||||
{loading ? "Collecting files..." : "Submit"}
|
||||
</button>
|
||||
|
@ -116,7 +116,7 @@ export default function WebsiteDepthOptions() {
|
||||
disabled={loading}
|
||||
className={`mt-2 w-full ${
|
||||
loading ? "cursor-not-allowed animate-pulse" : ""
|
||||
} justify-center border border-slate-200 px-4 py-2 rounded-lg text-[#222628] text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed`}
|
||||
} justify-center border border-slate-200 px-4 py-2 rounded-lg text-dark-text text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed`}
|
||||
>
|
||||
{loading ? "Scraping website..." : "Submit"}
|
||||
</button>
|
||||
|
@ -73,7 +73,7 @@ export default function YoutubeOptions() {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="mt-2 w-full justify-center border border-slate-200 px-4 py-2 rounded-lg text-[#222628] text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed"
|
||||
className="mt-2 w-full justify-center border border-slate-200 px-4 py-2 rounded-lg text-dark-text text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed"
|
||||
>
|
||||
{loading ? "Collecting transcript..." : "Collect transcript"}
|
||||
</button>
|
||||
|
@ -1,26 +1,13 @@
|
||||
import { useState } from "react";
|
||||
import React from "react";
|
||||
import {
|
||||
formatDate,
|
||||
getFileExtension,
|
||||
middleTruncate,
|
||||
} from "@/utils/directories";
|
||||
import { File } from "@phosphor-icons/react";
|
||||
import debounce from "lodash.debounce";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
export default function FileRow({ item, selected, toggleSelection }) {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
const handleShowTooltip = () => {
|
||||
setShowTooltip(true);
|
||||
};
|
||||
|
||||
const handleHideTooltip = () => {
|
||||
setShowTooltip(false);
|
||||
};
|
||||
|
||||
const handleMouseEnter = debounce(handleShowTooltip, 500);
|
||||
const handleMouseLeave = debounce(handleHideTooltip, 500);
|
||||
|
||||
return (
|
||||
<tr
|
||||
onClick={() => toggleSelection(item)}
|
||||
@ -28,7 +15,10 @@ export default function FileRow({ item, selected, toggleSelection }) {
|
||||
selected ? "selected" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="pl-2 col-span-6 flex gap-x-[4px] items-center">
|
||||
<div
|
||||
data-tooltip-id={`directory-item-${item.url}`}
|
||||
className="col-span-10 w-fit flex gap-x-[4px] items-center relative"
|
||||
>
|
||||
<div
|
||||
className="shrink-0 w-3 h-3 rounded border-[1px] border-white flex justify-center items-center cursor-pointer"
|
||||
role="checkbox"
|
||||
@ -41,34 +31,35 @@ export default function FileRow({ item, selected, toggleSelection }) {
|
||||
className="shrink-0 text-base font-bold w-4 h-4 mr-[3px]"
|
||||
weight="fill"
|
||||
/>
|
||||
<div
|
||||
className="relative"
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<p className="whitespace-nowrap overflow-hidden max-w-[165px] text-ellipsis">
|
||||
{middleTruncate(item.title, 17)}
|
||||
</p>
|
||||
{showTooltip && (
|
||||
<div className="absolute left-0 bg-white text-black p-1.5 rounded shadow-lg whitespace-nowrap">
|
||||
{item.title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{middleTruncate(item.title, 55)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="col-span-3 pl-3.5 whitespace-nowrap">
|
||||
{formatDate(item?.published)}
|
||||
</p>
|
||||
<p className="col-span-2 pl-2 uppercase overflow-x-hidden">
|
||||
{getFileExtension(item.url)}
|
||||
</p>
|
||||
<div className="-col-span-2 flex justify-end items-center">
|
||||
<div className="col-span-2 flex justify-end items-center">
|
||||
{item?.cached && (
|
||||
<div className="bg-white/10 rounded-3xl">
|
||||
<p className="text-xs px-2 py-0.5">Cached</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Tooltip
|
||||
id={`directory-item-${item.url}`}
|
||||
place="bottom"
|
||||
delayShow={800}
|
||||
className="tooltip invert z-99"
|
||||
>
|
||||
<div className="text-xs ">
|
||||
<p className="text-white">{item.title}</p>
|
||||
<div className="flex mt-1 gap-x-2">
|
||||
<p className="">
|
||||
Date: <b>{formatDate(item?.published)}</b>
|
||||
</p>
|
||||
<p className="">
|
||||
Type: <b>{getFileExtension(item.url).toUpperCase()}</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ export default function FolderRow({
|
||||
<>
|
||||
<tr
|
||||
onClick={onRowClick}
|
||||
className={`text-white/80 text-xs grid grid-cols-12 py-2 pl-3.5 pr-8 bg-[#1C1E21] hover:bg-sky-500/20 cursor-pointer w-full file-row ${
|
||||
className={`text-white/80 text-xs grid grid-cols-12 py-2 pl-3.5 pr-8 bg-dark-highlight hover:bg-sky-500/20 cursor-pointer w-full file-row ${
|
||||
selected ? "selected" : ""
|
||||
}`}
|
||||
>
|
||||
|
@ -203,8 +203,6 @@ function Directory({
|
||||
<div className="relative w-[560px] h-[310px] bg-zinc-900 rounded-2xl overflow-hidden">
|
||||
<div className="absolute top-0 left-0 right-0 z-10 rounded-t-2xl text-white/80 text-xs grid grid-cols-12 py-2 px-8 border-b border-white/20 shadow-lg bg-zinc-900">
|
||||
<p className="col-span-6">Name</p>
|
||||
<p className="col-span-3">Date</p>
|
||||
<p className="col-span-2">Kind</p>
|
||||
</div>
|
||||
|
||||
<div className="overflow-y-auto h-full pt-8">
|
||||
@ -258,9 +256,9 @@ function Directory({
|
||||
onClick={() =>
|
||||
setShowFolderSelection(!showFolderSelection)
|
||||
}
|
||||
className="border-none text-sm font-semibold bg-white h-[32px] w-[32px] rounded-lg text-[#222628] hover:bg-neutral-800/80 flex justify-center items-center group"
|
||||
className="border-none text-sm font-semibold bg-white h-[32px] w-[32px] rounded-lg text-dark-text hover:bg-neutral-800/80 flex justify-center items-center group"
|
||||
>
|
||||
<MoveToFolderIcon className="text-[#222628] group-hover:text-white" />
|
||||
<MoveToFolderIcon className="text-dark-text group-hover:text-white" />
|
||||
</button>
|
||||
{showFolderSelection && (
|
||||
<FolderSelectionPopup
|
||||
@ -274,7 +272,7 @@ function Directory({
|
||||
</div>
|
||||
<button
|
||||
onClick={deleteFiles}
|
||||
className="border-none text-sm font-semibold bg-white h-[32px] w-[32px] rounded-lg text-[#222628] hover:text-white hover:bg-neutral-800/80 flex justify-center items-center"
|
||||
className="border-none text-sm font-semibold bg-white h-[32px] w-[32px] rounded-lg text-dark-text hover:text-white hover:bg-neutral-800/80 flex justify-center items-center"
|
||||
>
|
||||
<Trash size={18} weight="bold" />
|
||||
</button>
|
||||
|
@ -8,7 +8,7 @@ const uuidPattern =
|
||||
const jsonPattern = /\.json$/;
|
||||
|
||||
// Function to strip UUID v4 and JSON from file names as that will impact search results.
|
||||
const stripUuidAndJsonFromString = (input = "") => {
|
||||
export const stripUuidAndJsonFromString = (input = "") => {
|
||||
return input
|
||||
?.replace(uuidPattern, "") // remove v4 uuid
|
||||
?.replace(jsonPattern, "") // remove trailing .json
|
||||
|
@ -4,11 +4,11 @@ import {
|
||||
getFileExtension,
|
||||
middleTruncate,
|
||||
} from "@/utils/directories";
|
||||
import { ArrowUUpLeft, File, PushPin } from "@phosphor-icons/react";
|
||||
import { ArrowUUpLeft, Eye, File, PushPin } from "@phosphor-icons/react";
|
||||
import Workspace from "@/models/workspace";
|
||||
import debounce from "lodash.debounce";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import showToast from "@/utils/toast";
|
||||
import System from "@/models/system";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
export default function WorkspaceFileRow({
|
||||
item,
|
||||
@ -20,8 +20,6 @@ export default function WorkspaceFileRow({
|
||||
hasChanges,
|
||||
movedItems,
|
||||
}) {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
const onRemoveClick = async () => {
|
||||
setLoading(true);
|
||||
|
||||
@ -40,62 +38,62 @@ export default function WorkspaceFileRow({
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleShowTooltip = () => {
|
||||
setShowTooltip(true);
|
||||
};
|
||||
|
||||
const handleHideTooltip = () => {
|
||||
setShowTooltip(false);
|
||||
};
|
||||
|
||||
const isMovedItem = movedItems?.some((movedItem) => movedItem.id === item.id);
|
||||
const handleMouseEnter = debounce(handleShowTooltip, 500);
|
||||
const handleMouseLeave = debounce(handleHideTooltip, 500);
|
||||
return (
|
||||
<div
|
||||
className={`items-center text-white/80 text-xs grid grid-cols-12 py-2 pl-3.5 pr-8 hover:bg-sky-500/20 cursor-pointer
|
||||
${isMovedItem ? "bg-green-800/40" : "file-row"}`}
|
||||
className={`items-center text-white/80 text-xs grid grid-cols-12 py-2 pl-3.5 pr-8 hover:bg-sky-500/20 cursor-pointer ${
|
||||
isMovedItem ? "bg-green-800/40" : "file-row"
|
||||
}`}
|
||||
>
|
||||
<div className="col-span-5 flex gap-x-[4px] items-center">
|
||||
<div
|
||||
data-tooltip-id={`directory-item-${item.url}`}
|
||||
className="col-span-10 w-fit flex gap-x-[4px] items-center relative"
|
||||
>
|
||||
<File
|
||||
className="text-base font-bold w-4 h-4 ml-3 mr-[3px]"
|
||||
className="shrink-0 text-base font-bold w-4 h-4 mr-[3px] ml-3"
|
||||
weight="fill"
|
||||
/>
|
||||
<div
|
||||
className="relative"
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<p className="whitespace-nowrap overflow-hidden max-w-[165px] text-ellipsis">
|
||||
{middleTruncate(item.title, 17)}
|
||||
</p>
|
||||
{showTooltip && (
|
||||
<div className="absolute left-0 bg-white text-black p-1.5 rounded shadow-lg whitespace-nowrap">
|
||||
{item.title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{middleTruncate(item.title, 50)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="col-span-3 pl-3.5 whitespace-nowrap">
|
||||
{formatDate(item?.published)}
|
||||
</p>
|
||||
<p className="col-span-2 pl-2 uppercase overflow-x-hidden">
|
||||
{getFileExtension(item.url)}
|
||||
</p>
|
||||
<div className="col-span-2 flex justify-center items-center">
|
||||
<div className="col-span-2 flex justify-end items-center">
|
||||
{hasChanges ? (
|
||||
<div className="w-4 h-4 ml-2 flex-shrink-0" />
|
||||
) : (
|
||||
<div className="flex gap-x-2 items-center">
|
||||
<WatchForChanges
|
||||
workspace={workspace}
|
||||
docPath={`${folderName}/${item.name}`}
|
||||
item={item}
|
||||
/>
|
||||
<PinItemToWorkspace
|
||||
workspace={workspace}
|
||||
docPath={`${folderName}/${item.name}`} // how to find documents during pin/unpin
|
||||
docPath={`${folderName}/${item.name}`}
|
||||
item={item}
|
||||
/>
|
||||
<RemoveItemFromWorkspace item={item} onClick={onRemoveClick} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Tooltip
|
||||
id={`directory-item-${item.url}`}
|
||||
place="bottom"
|
||||
delayShow={800}
|
||||
className="tooltip invert z-99"
|
||||
>
|
||||
<div className="text-xs ">
|
||||
<p className="text-white">{item.title}</p>
|
||||
<div className="flex mt-1 gap-x-2">
|
||||
<p className="">
|
||||
Date: <b>{formatDate(item?.published)}</b>
|
||||
</p>
|
||||
<p className="">
|
||||
Type: <b>{getFileExtension(item.url).toUpperCase()}</b>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -137,7 +135,7 @@ const PinItemToWorkspace = memo(({ workspace, docPath, item }) => {
|
||||
}
|
||||
};
|
||||
|
||||
if (!item) return <div />;
|
||||
if (!item) return <div className="w-[16px] p-[2px] ml-2" />;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -165,6 +163,78 @@ const PinItemToWorkspace = memo(({ workspace, docPath, item }) => {
|
||||
);
|
||||
});
|
||||
|
||||
const WatchForChanges = memo(({ workspace, docPath, item }) => {
|
||||
const [watched, setWatched] = useState(item?.watched || false);
|
||||
const [hover, setHover] = useState(false);
|
||||
const watchEvent = new CustomEvent("watch_document_for_changes");
|
||||
|
||||
const updateWatchStatus = async () => {
|
||||
try {
|
||||
if (!watched) window.dispatchEvent(watchEvent);
|
||||
const success =
|
||||
await System.experimentalFeatures.liveSync.setWatchStatusForDocument(
|
||||
workspace.slug,
|
||||
docPath,
|
||||
!watched
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
showToast(
|
||||
`Failed to ${!watched ? "watch" : "unwatch"} document.`,
|
||||
"error",
|
||||
{
|
||||
clear: true,
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
showToast(
|
||||
`Document ${
|
||||
!watched
|
||||
? "will be watched for changes"
|
||||
: "will no longer be watched for changes"
|
||||
}.`,
|
||||
"success",
|
||||
{ clear: true }
|
||||
);
|
||||
setWatched(!watched);
|
||||
} catch (error) {
|
||||
showToast(`Failed to watch document. ${error.message}`, "error", {
|
||||
clear: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if (!item || !item.canWatch) return <div className="w-[16px] p-[2px] ml-2" />;
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
className="flex gap-x-2 items-center hover:bg-main-gradient p-[2px] rounded ml-2"
|
||||
>
|
||||
<Eye
|
||||
data-tooltip-id={`watch-changes-${item.id}`}
|
||||
data-tooltip-content={
|
||||
watched ? "Stop watching for changes" : "Watch document for changes"
|
||||
}
|
||||
size={16}
|
||||
onClick={updateWatchStatus}
|
||||
weight={hover || watched ? "fill" : "regular"}
|
||||
className="outline-none text-base font-bold flex-shrink-0 cursor-pointer"
|
||||
/>
|
||||
<Tooltip
|
||||
id={`watch-changes-${item.id}`}
|
||||
place="bottom"
|
||||
delayShow={300}
|
||||
className="tooltip invert !text-xs"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const RemoveItemFromWorkspace = ({ item, onClick }) => {
|
||||
return (
|
||||
<div>
|
||||
|
@ -3,8 +3,10 @@ import { dollarFormat } from "@/utils/numbers";
|
||||
import WorkspaceFileRow from "./WorkspaceFileRow";
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import ModalWrapper from "@/components/ModalWrapper";
|
||||
import { PushPin } from "@phosphor-icons/react";
|
||||
import { SEEN_DOC_PIN_ALERT } from "@/utils/constants";
|
||||
import { Eye, PushPin } from "@phosphor-icons/react";
|
||||
import { SEEN_DOC_PIN_ALERT, SEEN_WATCH_ALERT } from "@/utils/constants";
|
||||
import paths from "@/utils/paths";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
function WorkspaceDirectory({
|
||||
workspace,
|
||||
@ -31,8 +33,6 @@ function WorkspaceDirectory({
|
||||
<div className="relative w-[560px] h-[445px] bg-zinc-900 rounded-2xl mt-5">
|
||||
<div className="text-white/80 text-xs grid grid-cols-12 py-2 px-8">
|
||||
<p className="col-span-5">Name</p>
|
||||
<p className="col-span-3">Date</p>
|
||||
<p className="col-span-2">Kind</p>
|
||||
<p className="col-span-2" />
|
||||
</div>
|
||||
<div className="w-full h-full flex items-center justify-center flex-col gap-y-5">
|
||||
@ -61,8 +61,6 @@ function WorkspaceDirectory({
|
||||
>
|
||||
<div className="text-white/80 text-xs grid grid-cols-12 py-2 px-8 border-b border-white/20 bg-zinc-900 sticky top-0 z-10">
|
||||
<p className="col-span-5">Name</p>
|
||||
<p className="col-span-3">Date</p>
|
||||
<p className="col-span-2">Kind</p>
|
||||
<p className="col-span-2" />
|
||||
</div>
|
||||
<div className="w-full h-full flex flex-col z-0">
|
||||
@ -122,6 +120,7 @@ function WorkspaceDirectory({
|
||||
)}
|
||||
</div>
|
||||
<PinAlert />
|
||||
<DocumentWatchAlert />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -145,7 +144,7 @@ const PinAlert = memo(() => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ModalWrapper isOpen={showAlert}>
|
||||
<ModalWrapper isOpen={showAlert} noPortal={true}>
|
||||
<div className="relative w-full max-w-2xl max-h-full">
|
||||
<div className="relative bg-main-gradient rounded-lg shadow">
|
||||
<div className="flex items-start justify-between p-4 rounded-t border-gray-500/50">
|
||||
@ -188,4 +187,75 @@ const PinAlert = memo(() => {
|
||||
);
|
||||
});
|
||||
|
||||
const DocumentWatchAlert = memo(() => {
|
||||
const [showAlert, setShowAlert] = useState(false);
|
||||
function dismissAlert() {
|
||||
setShowAlert(false);
|
||||
window.localStorage.setItem(SEEN_WATCH_ALERT, "1");
|
||||
window.removeEventListener(handlePinEvent);
|
||||
}
|
||||
|
||||
function handlePinEvent() {
|
||||
if (!!window?.localStorage?.getItem(SEEN_WATCH_ALERT)) return;
|
||||
setShowAlert(true);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!window || !!window?.localStorage?.getItem(SEEN_WATCH_ALERT)) return;
|
||||
window?.addEventListener("watch_document_for_changes", handlePinEvent);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ModalWrapper isOpen={showAlert} noPortal={true}>
|
||||
<div className="relative w-full max-w-2xl max-h-full">
|
||||
<div className="relative bg-main-gradient rounded-lg shadow">
|
||||
<div className="flex items-start justify-between p-4 rounded-t border-gray-500/50">
|
||||
<div className="flex items-center gap-2">
|
||||
<Eye
|
||||
className="text-yellow-600 text-lg w-6 h-6"
|
||||
weight="regular"
|
||||
/>
|
||||
<h3 className="text-xl font-semibold text-white">
|
||||
What does watching a document do?
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full p-6 text-white text-md flex flex-col gap-y-2">
|
||||
<p>
|
||||
When you <b>watch</b> a document in AnythingLLM we will{" "}
|
||||
<i>automatically</i> sync your document content from it's original
|
||||
source on regular intervals. This will automatically update the
|
||||
content in every workspace where this file is managed.
|
||||
</p>
|
||||
<p>
|
||||
This feature currently supports online-based content and will not
|
||||
be available for manually uploaded documents.
|
||||
</p>
|
||||
<p>
|
||||
You can manage what documents are watched from the{" "}
|
||||
<Link
|
||||
to={paths.experimental.liveDocumentSync.manage()}
|
||||
className="text-blue-600 underline"
|
||||
>
|
||||
File manager
|
||||
</Link>{" "}
|
||||
admin view.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full justify-between items-center p-6 space-x-2 border-t rounded-b border-gray-500/50">
|
||||
<button disabled={true} className="invisible" />
|
||||
<button
|
||||
onClick={dismissAlert}
|
||||
className="border border-slate-200 px-4 py-2 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
|
||||
>
|
||||
Okay, got it
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
);
|
||||
});
|
||||
|
||||
export default memo(WorkspaceDirectory);
|
||||
|
@ -6,6 +6,7 @@ import showToast from "@/utils/toast";
|
||||
import ModalWrapper from "@/components/ModalWrapper";
|
||||
import { useModal } from "@/hooks/useModal";
|
||||
import RecoveryCodeModal from "@/components/Modals/DisplayRecoveryCodeModal";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const RecoveryForm = ({ onSubmit, setShowRecoveryForm }) => {
|
||||
const [username, setUsername] = useState("");
|
||||
@ -81,13 +82,13 @@ const RecoveryForm = ({ onSubmit, setShowRecoveryForm }) => {
|
||||
<div className="flex items-center md:p-12 md:px-0 px-6 mt-12 md:mt-0 space-x-2 border-gray-600 w-full flex-col gap-y-8">
|
||||
<button
|
||||
type="submit"
|
||||
className="md:text-[#46C8FF] md:bg-transparent md:w-[300px] text-[#222628] text-sm font-bold focus:ring-4 focus:outline-none rounded-md border-[1.5px] border-[#46C8FF] md:h-[34px] h-[48px] md:hover:text-white md:hover:bg-[#46C8FF] bg-[#46C8FF] focus:z-10 w-full"
|
||||
className="md:text-primary-button md:bg-transparent md:w-[300px] text-dark-text text-sm font-bold focus:ring-4 focus:outline-none rounded-md border-[1.5px] border-[#46C8FF] md:h-[34px] h-[48px] md:hover:text-white md:hover:bg-primary-button bg-primary-button focus:z-10 w-full"
|
||||
>
|
||||
Reset Password
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="text-white text-sm flex gap-x-1 hover:text-[#46C8FF] hover:underline -mb-8"
|
||||
className="text-white text-sm flex gap-x-1 hover:text-primary-button hover:underline -mb-8"
|
||||
onClick={() => setShowRecoveryForm(false)}
|
||||
>
|
||||
Back to Login
|
||||
@ -150,7 +151,7 @@ const ResetPasswordForm = ({ onSubmit }) => {
|
||||
<div className="flex items-center md:p-12 md:px-0 px-6 mt-12 md:mt-0 space-x-2 border-gray-600 w-full flex-col gap-y-8">
|
||||
<button
|
||||
type="submit"
|
||||
className="md:text-[#46C8FF] md:bg-transparent md:w-[300px] text-[#222628] text-sm font-bold focus:ring-4 focus:outline-none rounded-md border-[1.5px] border-[#46C8FF] md:h-[34px] h-[48px] md:hover:text-white md:hover:bg-[#46C8FF] bg-[#46C8FF] focus:z-10 w-full"
|
||||
className="md:text-primary-button md:bg-transparent md:w-[300px] text-dark-text text-sm font-bold focus:ring-4 focus:outline-none rounded-md border-[1.5px] border-[#46C8FF] md:h-[34px] h-[48px] md:hover:text-white md:hover:bg-primary-button bg-primary-button focus:z-10 w-full"
|
||||
>
|
||||
Reset Password
|
||||
</button>
|
||||
@ -160,6 +161,7 @@ const ResetPasswordForm = ({ onSubmit }) => {
|
||||
};
|
||||
|
||||
export default function MultiUserAuth() {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [recoveryCodes, setRecoveryCodes] = useState([]);
|
||||
@ -279,14 +281,15 @@ export default function MultiUserAuth() {
|
||||
<div className="flex items-center flex-col gap-y-4">
|
||||
<div className="flex gap-x-1">
|
||||
<h3 className="text-md md:text-2xl font-bold text-white text-center white-space-nowrap hidden md:block">
|
||||
Welcome to
|
||||
{t("login.multi-user.welcome")}
|
||||
</h3>
|
||||
<p className="text-4xl md:text-2xl font-bold bg-gradient-to-r from-[#75D6FF] via-[#FFFFFF] to-[#FFFFFF] bg-clip-text text-transparent">
|
||||
{customAppName || "AnythingLLM"}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm text-white/90 text-center">
|
||||
Sign in to your {customAppName || "AnythingLLM"} account.
|
||||
{t("login.sign-in.start")} {customAppName || "AnythingLLM"}{" "}
|
||||
{t("login.sign-in.end")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -296,7 +299,7 @@ export default function MultiUserAuth() {
|
||||
<input
|
||||
name="username"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
placeholder={t("login.multi-user.placeholder-username")}
|
||||
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-md p-2.5 w-full h-[48px] md:w-[300px] md:h-[34px]"
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
@ -306,7 +309,7 @@ export default function MultiUserAuth() {
|
||||
<input
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
placeholder={t("login.multi-user.placeholder-password")}
|
||||
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-md p-2.5 w-full h-[48px] md:w-[300px] md:h-[34px]"
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
@ -319,22 +322,25 @@ export default function MultiUserAuth() {
|
||||
<button
|
||||
disabled={loading}
|
||||
type="submit"
|
||||
className="md:text-[#46C8FF] md:bg-transparent text-[#222628] text-sm font-bold focus:ring-4 focus:outline-none rounded-md border-[1.5px] border-[#46C8FF] md:h-[34px] h-[48px] md:hover:text-white md:hover:bg-[#46C8FF] bg-[#46C8FF] focus:z-10 w-full"
|
||||
className="md:text-primary-button md:bg-transparent text-dark-text text-sm font-bold focus:ring-4 focus:outline-none rounded-md border-[1.5px] border-[#46C8FF] md:h-[34px] h-[48px] md:hover:text-white md:hover:bg-primary-button bg-primary-button focus:z-10 w-full"
|
||||
>
|
||||
{loading ? "Validating..." : "Login"}
|
||||
{loading
|
||||
? t("login.multi-user.validating")
|
||||
: t("login.multi-user.login")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="text-white text-sm flex gap-x-1 hover:text-[#46C8FF] hover:underline"
|
||||
className="text-white text-sm flex gap-x-1 hover:text-primary-button hover:underline"
|
||||
onClick={handleResetPassword}
|
||||
>
|
||||
Forgot password?<b>Reset</b>
|
||||
{t("login.multi-user.forgot-pass")}?
|
||||
<b>{t("login.multi-user.reset")}</b>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<ModalWrapper isOpen={isRecoveryCodeModalOpen}>
|
||||
<ModalWrapper isOpen={isRecoveryCodeModalOpen} noPortal={true}>
|
||||
<RecoveryCodeModal
|
||||
recoveryCodes={recoveryCodes}
|
||||
onDownloadComplete={handleDownloadComplete}
|
||||
|
@ -5,8 +5,10 @@ import paths from "../../../utils/paths";
|
||||
import ModalWrapper from "@/components/ModalWrapper";
|
||||
import { useModal } from "@/hooks/useModal";
|
||||
import RecoveryCodeModal from "@/components/Modals/DisplayRecoveryCodeModal";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function SingleUserAuth() {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [recoveryCodes, setRecoveryCodes] = useState([]);
|
||||
@ -73,14 +75,15 @@ export default function SingleUserAuth() {
|
||||
<div className="flex items-center flex-col gap-y-4">
|
||||
<div className="flex gap-x-1">
|
||||
<h3 className="text-md md:text-2xl font-bold text-white text-center white-space-nowrap hidden md:block">
|
||||
Welcome to
|
||||
{t("login.multi-user.welcome")}
|
||||
</h3>
|
||||
<p className="text-4xl md:text-2xl font-bold bg-gradient-to-r from-[#75D6FF] via-[#FFFFFF] to-[#FFFFFF] bg-clip-text text-transparent">
|
||||
{customAppName || "AnythingLLM"}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm text-white/90 text-center">
|
||||
Sign in to your {customAppName || "AnythingLLM"} instance.
|
||||
{t("login.sign-in.start")} {customAppName || "AnythingLLM"}{" "}
|
||||
{t("login.sign-in.end")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -104,7 +107,7 @@ export default function SingleUserAuth() {
|
||||
<button
|
||||
disabled={loading}
|
||||
type="submit"
|
||||
className="md:text-[#46C8FF] md:bg-transparent text-[#222628] text-sm font-bold focus:ring-4 focus:outline-none rounded-md border-[1.5px] border-[#46C8FF] md:h-[34px] h-[48px] md:hover:text-white md:hover:bg-[#46C8FF] bg-[#46C8FF] focus:z-10 w-full"
|
||||
className="md:text-primary-button md:bg-transparent text-dark-text text-sm font-bold focus:ring-4 focus:outline-none rounded-md border-[1.5px] border-[#46C8FF] md:h-[34px] h-[48px] md:hover:text-white md:hover:bg-primary-button bg-primary-button focus:z-10 w-full"
|
||||
>
|
||||
{loading ? "Validating..." : "Login"}
|
||||
</button>
|
||||
|
@ -22,14 +22,19 @@ import {
|
||||
EyeSlash,
|
||||
SplitVertical,
|
||||
Microphone,
|
||||
Robot,
|
||||
Flask,
|
||||
} from "@phosphor-icons/react";
|
||||
import useUser from "@/hooks/useUser";
|
||||
import { USER_BACKGROUND_COLOR } from "@/utils/constants";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import Footer from "../Footer";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import showToast from "@/utils/toast";
|
||||
|
||||
export default function SettingsSidebar() {
|
||||
const { t } = useTranslation();
|
||||
const { logo } = useLogo();
|
||||
const { user } = useUser();
|
||||
const sidebarRef = useRef(null);
|
||||
@ -112,7 +117,7 @@ export default function SettingsSidebar() {
|
||||
<div className="h-full flex flex-col w-full justify-between pt-4 overflow-y-scroll no-scroll">
|
||||
<div className="h-auto md:sidebar-items md:dark:sidebar-items">
|
||||
<div className="flex flex-col gap-y-4 pb-[60px] overflow-y-scroll no-scroll">
|
||||
<SidebarOptions user={user} />
|
||||
<SidebarOptions user={user} t={t} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -145,12 +150,12 @@ export default function SettingsSidebar() {
|
||||
>
|
||||
<div className="w-full h-full flex flex-col overflow-x-hidden items-between min-w-[235px]">
|
||||
<div className="text-white text-opacity-60 text-sm font-medium uppercase mt-[4px] mb-0 ml-2">
|
||||
Instance Settings
|
||||
{t("settings.title")}
|
||||
</div>
|
||||
<div className="relative h-[calc(100%-60px)] flex flex-col w-full justify-between pt-[10px] overflow-y-scroll no-scroll">
|
||||
<div className="h-auto sidebar-items">
|
||||
<div className="flex flex-col gap-y-2 pb-[60px] overflow-y-scroll no-scroll">
|
||||
<SidebarOptions user={user} />
|
||||
<SidebarOptions user={user} t={t} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -220,47 +225,56 @@ const Option = ({
|
||||
);
|
||||
};
|
||||
|
||||
const SidebarOptions = ({ user = null }) => (
|
||||
const SidebarOptions = ({ user = null, t }) => (
|
||||
<>
|
||||
<Option
|
||||
href={paths.settings.system()}
|
||||
btnText="System Preferences"
|
||||
btnText={t("settings.system")}
|
||||
icon={<SquaresFour className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.invites()}
|
||||
btnText="Invitation"
|
||||
btnText={t("settings.invites")}
|
||||
icon={<EnvelopeSimple className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.users()}
|
||||
btnText="Users"
|
||||
btnText={t("settings.users")}
|
||||
icon={<Users className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.workspaces()}
|
||||
btnText="Workspaces"
|
||||
btnText={t("settings.workspaces")}
|
||||
icon={<BookOpen className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.chats()}
|
||||
btnText="Workspace Chat"
|
||||
btnText={t("settings.workspace-chats")}
|
||||
icon={<ChatCenteredText className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
|
||||
<Option
|
||||
href={paths.settings.agentSkills()}
|
||||
btnText="Agent Skills"
|
||||
icon={<Robot className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.appearance()}
|
||||
btnText="Appearance"
|
||||
btnText={t("settings.appearance")}
|
||||
icon={<Eye className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -268,7 +282,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.apiKeys()}
|
||||
btnText="API Keys"
|
||||
btnText={t("settings.api-keys")}
|
||||
icon={<Key className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -276,7 +290,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.llmPreference()}
|
||||
btnText="LLM Preference"
|
||||
btnText={t("settings.llm")}
|
||||
icon={<ChatText className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -292,7 +306,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.transcriptionPreference()}
|
||||
btnText="Transcription Model"
|
||||
btnText={t("settings.transcription")}
|
||||
icon={<ClosedCaptioning className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -301,7 +315,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
<Option
|
||||
href={paths.settings.embedder.modelPreference()}
|
||||
childLinks={[paths.settings.embedder.chunkingPreference()]}
|
||||
btnText="Embedder Preferences"
|
||||
btnText={t("settings.embedder")}
|
||||
icon={<FileCode className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -310,7 +324,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
<>
|
||||
<Option
|
||||
href={paths.settings.embedder.chunkingPreference()}
|
||||
btnText="Text Splitter & Chunking"
|
||||
btnText={t("settings.text-splitting")}
|
||||
icon={<SplitVertical className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -321,7 +335,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.vectorDatabase()}
|
||||
btnText="Vector Database"
|
||||
btnText={t("settings.vector-database")}
|
||||
icon={<Database className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -330,7 +344,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
<Option
|
||||
href={paths.settings.embedSetup()}
|
||||
childLinks={[paths.settings.embedChats()]}
|
||||
btnText="Chat Embed Widgets"
|
||||
btnText={t("settings.embeds")}
|
||||
icon={<CodeBlock className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -339,7 +353,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
<>
|
||||
<Option
|
||||
href={paths.settings.embedChats()}
|
||||
btnText="Chat Embed History"
|
||||
btnText={t("settings.embed-chats")}
|
||||
icon={<Barcode className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -350,7 +364,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.security()}
|
||||
btnText="Security"
|
||||
btnText={t("settings.security")}
|
||||
icon={<Lock className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -359,7 +373,7 @@ const SidebarOptions = ({ user = null }) => (
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.logs()}
|
||||
btnText="Event Logs"
|
||||
btnText={t("settings.event-logs")}
|
||||
icon={<Notepad className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
@ -367,11 +381,70 @@ const SidebarOptions = ({ user = null }) => (
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.privacy()}
|
||||
btnText="Privacy & Data"
|
||||
btnText={t("settings.privacy")}
|
||||
icon={<EyeSlash className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
<HoldToReveal key="exp_features">
|
||||
<Option
|
||||
href={paths.settings.experimental()}
|
||||
btnText="Experimental Features"
|
||||
icon={<Flask className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
</HoldToReveal>
|
||||
</>
|
||||
);
|
||||
|
||||
function HoldToReveal({ children, holdForMs = 3_000 }) {
|
||||
let timeout = null;
|
||||
const [showing, setShowing] = useState(
|
||||
window.localStorage.getItem(
|
||||
"anythingllm_experimental_feature_preview_unlocked"
|
||||
)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const onPress = (e) => {
|
||||
if (!["Control", "Meta"].includes(e.key) || timeout !== null) return;
|
||||
timeout = setTimeout(() => {
|
||||
setShowing(true);
|
||||
// Setting toastId prevents hook spam from holding control too many times or the event not detaching
|
||||
showToast("Experimental feature previews unlocked!");
|
||||
window.localStorage.setItem(
|
||||
"anythingllm_experimental_feature_preview_unlocked",
|
||||
"enabled"
|
||||
);
|
||||
window.removeEventListener("keypress", onPress);
|
||||
window.removeEventListener("keyup", onRelease);
|
||||
clearTimeout(timeout);
|
||||
}, holdForMs);
|
||||
};
|
||||
const onRelease = (e) => {
|
||||
if (!["Control", "Meta"].includes(e.key)) return;
|
||||
if (showing) {
|
||||
window.removeEventListener("keypress", onPress);
|
||||
window.removeEventListener("keyup", onRelease);
|
||||
clearTimeout(timeout);
|
||||
return;
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
|
||||
if (!showing) {
|
||||
window.addEventListener("keydown", onPress);
|
||||
window.addEventListener("keyup", onRelease);
|
||||
}
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onPress);
|
||||
window.removeEventListener("keyup", onRelease);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!showing) return null;
|
||||
return children;
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Gauge } from "@phosphor-icons/react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Gauge } from "@phosphor-icons/react";
|
||||
|
||||
export default function NativeTranscriptionOptions({ settings }) {
|
||||
const { t } = useTranslation();
|
||||
const [model, setModel] = useState(settings?.WhisperModelPref);
|
||||
|
||||
return (
|
||||
@ -10,7 +12,7 @@ export default function NativeTranscriptionOptions({ settings }) {
|
||||
<div className="w-full flex items-center gap-4">
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
Model Selection
|
||||
{t("common.selection")}
|
||||
</label>
|
||||
<select
|
||||
name="WhisperModelPref"
|
||||
@ -46,20 +48,19 @@ function LocalWarning({ model }) {
|
||||
}
|
||||
|
||||
function WhisperSmall() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row md:items-center gap-x-2 text-white mb-4 bg-blue-800/30 w-fit rounded-lg px-4 py-2">
|
||||
<div className="gap-x-2 flex items-center">
|
||||
<Gauge size={25} />
|
||||
<p className="text-sm">
|
||||
Running the <b>whisper-small</b> model on a machine with limited RAM
|
||||
or CPU can stall AnythingLLM when processing media files.
|
||||
{t("transcription.warn-start")}
|
||||
<br />
|
||||
We recommend at least 2GB of RAM and upload files <10Mb.
|
||||
{t("transcription.warn-recommend")}
|
||||
<br />
|
||||
<br />
|
||||
<i>
|
||||
This model will automatically download on the first use. (250mb)
|
||||
</i>
|
||||
<i>{t("transcription.warn-end")} (250mb)</i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,21 +68,19 @@ function WhisperSmall() {
|
||||
}
|
||||
|
||||
function WhisperLarge() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row md:items-center gap-x-2 text-white mb-4 bg-blue-800/30 w-fit rounded-lg px-4 py-2">
|
||||
<div className="gap-x-2 flex items-center">
|
||||
<Gauge size={25} />
|
||||
<p className="text-sm">
|
||||
Using the <b>whisper-large</b> model on machines with limited RAM or
|
||||
CPU can stall AnythingLLM when processing media files. This model is
|
||||
substantially larger than the whisper-small.
|
||||
{t("transcription.warn-start")}
|
||||
<br />
|
||||
We recommend at least 8GB of RAM and upload files <10Mb.
|
||||
{t("transcription.warn-recommend")}
|
||||
<br />
|
||||
<br />
|
||||
<i>
|
||||
This model will automatically download on the first use. (1.56GB)
|
||||
</i>
|
||||
<i>{t("transcription.warn-end")} (1.56GB)</i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useLanguageOptions } from "@/hooks/useLanguageOptions";
|
||||
import usePfp from "@/hooks/usePfp";
|
||||
import System from "@/models/system";
|
||||
import { AUTH_USER } from "@/utils/constants";
|
||||
@ -147,6 +148,7 @@ export default function AccountModal({ user, hideModal }) {
|
||||
placeholder={`${user.username}'s new password`}
|
||||
/>
|
||||
</div>
|
||||
<LanguagePreference />
|
||||
</div>
|
||||
<div className="flex justify-between items-center border-t border-gray-500/50 pt-4 p-6">
|
||||
<button
|
||||
@ -168,3 +170,37 @@ export default function AccountModal({ user, hideModal }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LanguagePreference() {
|
||||
const {
|
||||
currentLanguage,
|
||||
supportedLanguages,
|
||||
getLanguageName,
|
||||
changeLanguage,
|
||||
} = useLanguageOptions();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="userLang"
|
||||
className="block mb-2 text-sm font-medium text-white"
|
||||
>
|
||||
Preferred language
|
||||
</label>
|
||||
<select
|
||||
name="userLang"
|
||||
className="bg-zinc-900 w-fit mt-2 px-4 border-gray-500 text-white text-sm rounded-lg block py-2"
|
||||
defaultValue={currentLanguage || "en"}
|
||||
onChange={(e) => changeLanguage(e.target.value)}
|
||||
>
|
||||
{supportedLanguages.map((lang) => {
|
||||
return (
|
||||
<option key={lang} value={lang}>
|
||||
{getLanguageName(lang)}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
export default function LanceDBOptions() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="w-full h-10 items-center flex">
|
||||
<p className="text-sm font-base text-white text-opacity-60">
|
||||
There is no configuration needed for LanceDB.
|
||||
{t("vector.provider.description")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
@ -29,7 +29,7 @@ export default function VectorDBItem({
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-semibold text-white">{name}</div>
|
||||
<div className="mt-1 text-xs text-[#D2D5DB]">{description}</div>
|
||||
<div className="mt-1 text-xs text-description">{description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -66,7 +66,7 @@ export function Chartable({ props, workspace }) {
|
||||
const chartType = content?.type?.toLowerCase();
|
||||
const data =
|
||||
typeof content.dataset === "string"
|
||||
? safeJsonParse(content.dataset, null)
|
||||
? safeJsonParse(content.dataset, [])
|
||||
: content.dataset;
|
||||
const value = data.length > 0 ? Object.keys(data[0])[1] : "value";
|
||||
const title = content?.title;
|
||||
|
@ -222,14 +222,16 @@ function parseChunkSource({ title = "", chunks = [] }) {
|
||||
|
||||
if (
|
||||
!chunks.length ||
|
||||
(!chunks[0].chunkSource.startsWith("link://") &&
|
||||
!chunks[0].chunkSource.startsWith("confluence://"))
|
||||
(!chunks[0].chunkSource?.startsWith("link://") &&
|
||||
!chunks[0].chunkSource?.startsWith("confluence://") &&
|
||||
!chunks[0].chunkSource?.startsWith("github://"))
|
||||
)
|
||||
return nullResponse;
|
||||
try {
|
||||
const url = new URL(
|
||||
chunks[0].chunkSource.split("link://")[1] ||
|
||||
chunks[0].chunkSource.split("confluence://")[1]
|
||||
chunks[0].chunkSource.split("confluence://")[1] ||
|
||||
chunks[0].chunkSource.split("github://")[1]
|
||||
);
|
||||
let text = url.host + url.pathname;
|
||||
let icon = "link";
|
||||
|
@ -39,7 +39,7 @@ const HistoricalMessage = ({
|
||||
role === "user" ? USER_BACKGROUND_COLOR : AI_BACKGROUND_COLOR
|
||||
}`}
|
||||
>
|
||||
<div className="py-8 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
|
||||
<div className="py-8 px-4 w-full flex gap-x-5 md:max-w-[80%] flex-col">
|
||||
<div className="flex gap-x-5">
|
||||
<ProfileImage role={role} workspace={workspace} />
|
||||
<div className="p-2 rounded-lg bg-red-50 text-red-500">
|
||||
|
@ -8,7 +8,7 @@ export default function CTAButton({
|
||||
<button
|
||||
disabled={disabled}
|
||||
onClick={() => onClick?.()}
|
||||
className={`text-xs px-4 py-1 font-semibold rounded-lg bg-[#46C8FF] hover:bg-[#2C2F36] hover:text-white h-[34px] -mr-8 whitespace-nowrap shadow-[0_4px_14px_rgba(0,0,0,0.25)] w-fit ${className}`}
|
||||
className={`text-xs px-4 py-1 font-semibold rounded-lg bg-primary-button hover:bg-secondary hover:text-white h-[34px] -mr-8 whitespace-nowrap shadow-[0_4px_14px_rgba(0,0,0,0.25)] w-fit ${className}`}
|
||||
>
|
||||
<div className="flex items-center justify-center gap-2">{children}</div>
|
||||
</button>
|
||||
|
@ -23,6 +23,7 @@ const PROVIDER_DEFAULT_MODELS = {
|
||||
"claude-3-opus-20240229",
|
||||
"claude-3-sonnet-20240229",
|
||||
"claude-3-haiku-20240307",
|
||||
"claude-3-5-sonnet-20240620",
|
||||
],
|
||||
azure: [],
|
||||
lmstudio: [],
|
||||
|
20
frontend/src/hooks/useLanguageOptions.js
Normal file
20
frontend/src/hooks/useLanguageOptions.js
Normal file
@ -0,0 +1,20 @@
|
||||
import i18n from "@/i18n";
|
||||
import { resources as languages } from "@/locales/resources";
|
||||
|
||||
export function useLanguageOptions() {
|
||||
const supportedLanguages = Object.keys(languages);
|
||||
const languageNames = new Intl.DisplayNames(supportedLanguages, {
|
||||
type: "language",
|
||||
});
|
||||
const changeLanguage = (newLang = "en") => {
|
||||
if (!Object.keys(languages).includes(newLang)) return false;
|
||||
i18n.changeLanguage(newLang);
|
||||
};
|
||||
|
||||
return {
|
||||
currentLanguage: i18n.language || "en",
|
||||
supportedLanguages,
|
||||
getLanguageName: (lang = "en") => languageNames.of(lang),
|
||||
changeLanguage,
|
||||
};
|
||||
}
|
99
frontend/src/hooks/useProviderEndpointAutoDiscovery.js
Normal file
99
frontend/src/hooks/useProviderEndpointAutoDiscovery.js
Normal file
@ -0,0 +1,99 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import System from "@/models/system";
|
||||
import showToast from "@/utils/toast";
|
||||
|
||||
export default function useProviderEndpointAutoDiscovery({
|
||||
provider = null,
|
||||
initialBasePath = "",
|
||||
ENDPOINTS = [],
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [basePath, setBasePath] = useState(initialBasePath);
|
||||
const [basePathValue, setBasePathValue] = useState(initialBasePath);
|
||||
const [autoDetectAttempted, setAutoDetectAttempted] = useState(false);
|
||||
const [showAdvancedControls, setShowAdvancedControls] = useState(true);
|
||||
|
||||
async function autoDetect(isInitialAttempt = false) {
|
||||
setLoading(true);
|
||||
setAutoDetectAttempted(true);
|
||||
const possibleEndpoints = [];
|
||||
ENDPOINTS.forEach((endpoint) => {
|
||||
possibleEndpoints.push(
|
||||
new Promise((resolve, reject) => {
|
||||
System.customModels(provider, null, endpoint, 2_000)
|
||||
.then((results) => {
|
||||
if (!results?.models || results.models.length === 0)
|
||||
throw new Error("No models");
|
||||
resolve({ endpoint, models: results.models });
|
||||
})
|
||||
.catch(() => {
|
||||
reject(`${provider} @ ${endpoint} did not resolve.`);
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const { endpoint, models } = await Promise.any(possibleEndpoints)
|
||||
.then((resolved) => resolved)
|
||||
.catch(() => {
|
||||
console.error("All endpoints failed to resolve.");
|
||||
return { endpoint: null, models: null };
|
||||
});
|
||||
|
||||
if (models !== null) {
|
||||
setBasePath(endpoint);
|
||||
setBasePathValue(endpoint);
|
||||
setLoading(false);
|
||||
showToast("Provider endpoint discovered automatically.", "success", {
|
||||
clear: true,
|
||||
});
|
||||
setShowAdvancedControls(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
setShowAdvancedControls(true);
|
||||
showToast(
|
||||
"Couldn't automatically discover the provider endpoint. Please enter it manually.",
|
||||
"info",
|
||||
{ clear: true }
|
||||
);
|
||||
}
|
||||
|
||||
function handleAutoDetectClick(e) {
|
||||
e.preventDefault();
|
||||
autoDetect();
|
||||
}
|
||||
|
||||
function handleBasePathChange(e) {
|
||||
const value = e.target.value;
|
||||
setBasePathValue(value);
|
||||
}
|
||||
|
||||
function handleBasePathBlur() {
|
||||
setBasePath(basePathValue);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!initialBasePath && !autoDetectAttempted) autoDetect(true);
|
||||
}, [initialBasePath, autoDetectAttempted]);
|
||||
|
||||
return {
|
||||
autoDetecting: loading,
|
||||
autoDetectAttempted,
|
||||
showAdvancedControls,
|
||||
setShowAdvancedControls,
|
||||
basePath: {
|
||||
value: basePath,
|
||||
set: setBasePathValue,
|
||||
onChange: handleBasePathChange,
|
||||
onBlur: handleBasePathBlur,
|
||||
},
|
||||
basePathValue: {
|
||||
value: basePathValue,
|
||||
set: setBasePathValue,
|
||||
},
|
||||
handleAutoDetectClick,
|
||||
runAutoDetect: autoDetect,
|
||||
};
|
||||
}
|
21
frontend/src/i18n.js
Normal file
21
frontend/src/i18n.js
Normal file
@ -0,0 +1,21 @@
|
||||
import i18next from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import { defaultNS, resources } from "./locales/resources";
|
||||
|
||||
i18next
|
||||
// https://github.com/i18next/i18next-browser-languageDetector/blob/9efebe6ca0271c3797bc09b84babf1ba2d9b4dbb/src/index.js#L11
|
||||
.use(initReactI18next) // Initialize i18n for React
|
||||
.use(LanguageDetector)
|
||||
.init({
|
||||
fallbackLng: "en",
|
||||
debug: import.meta.env.DEV,
|
||||
defaultNS,
|
||||
resources,
|
||||
lowerCaseLng: true,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18next;
|
@ -616,7 +616,7 @@ dialog::backdrop {
|
||||
}
|
||||
|
||||
.file-row:nth-child(even) {
|
||||
@apply bg-[#1C1E21];
|
||||
@apply bg-dark-highlight;
|
||||
}
|
||||
|
||||
.file-row:nth-child(odd) {
|
||||
|
451
frontend/src/locales/en/common.js
Normal file
451
frontend/src/locales/en/common.js
Normal file
@ -0,0 +1,451 @@
|
||||
const TRANSLATIONS = {
|
||||
common: {
|
||||
"workspaces-name": "Workspaces Name",
|
||||
error: "error",
|
||||
success: "success",
|
||||
user: "User",
|
||||
selection: "Model Selection",
|
||||
saving: "Saving...",
|
||||
save: "Save changes",
|
||||
previous: "Previous Page",
|
||||
next: "Next Page",
|
||||
},
|
||||
|
||||
// Setting Sidebar menu items.
|
||||
settings: {
|
||||
title: "Instance Settings",
|
||||
system: "System Preferences",
|
||||
invites: "Invitation",
|
||||
users: "Users",
|
||||
workspaces: "Workspaces",
|
||||
"workspace-chats": "Workspace Chat",
|
||||
appearance: "Appearance",
|
||||
"api-keys": "API Keys",
|
||||
llm: "LLM Preference",
|
||||
transcription: "Transcription Model",
|
||||
embedder: "Embedding Preferences",
|
||||
"text-splitting": "Text Splitter & Chunking",
|
||||
"vector-database": "Vector Database",
|
||||
embeds: "Chat Embed Widgets",
|
||||
"embed-chats": "Chat Embed History",
|
||||
security: "Security",
|
||||
"event-logs": "Event Logs",
|
||||
privacy: "Privacy & Data",
|
||||
},
|
||||
|
||||
// Page Definitions
|
||||
login: {
|
||||
"multi-user": {
|
||||
welcome: "Welcome to",
|
||||
"placeholder-username": "Username",
|
||||
"placeholder-password": "Password",
|
||||
login: "Login",
|
||||
validating: "Validating...",
|
||||
"forgot-pass": "Forgot password",
|
||||
reset: "Reset",
|
||||
},
|
||||
"sign-in": {
|
||||
start: "Sign in to your",
|
||||
end: "account.",
|
||||
},
|
||||
},
|
||||
|
||||
// Workspace Settings menu items
|
||||
"workspaces—settings": {
|
||||
general: "General Settings",
|
||||
chat: "Chat Settings",
|
||||
vector: "Vector Database",
|
||||
members: "Members",
|
||||
agent: "Agent Configuration",
|
||||
},
|
||||
|
||||
// General Appearance
|
||||
general: {
|
||||
vector: {
|
||||
title: "Vector Count",
|
||||
description: "Total number of vectors in your vector database.",
|
||||
},
|
||||
names: {
|
||||
description: "This will only change the display name of your workspace.",
|
||||
},
|
||||
message: {
|
||||
title: "Suggested Chat Messages",
|
||||
description:
|
||||
"Customize the messages that will be suggested to your workspace users.",
|
||||
add: "Add new message",
|
||||
save: "Save Messages",
|
||||
heading: "Explain to me",
|
||||
body: "the benefits of AnythingLLM",
|
||||
},
|
||||
pfp: {
|
||||
title: "Assistant Profile Image",
|
||||
description:
|
||||
"Customize the profile image of the assistant for this workspace.",
|
||||
image: "Workspace Image",
|
||||
remove: "Remove Workspace Image",
|
||||
},
|
||||
delete: {
|
||||
title: "Delete Workspace",
|
||||
description:
|
||||
"Delete this workspace and all of its data. This will delete the workspace for all users.",
|
||||
delete: "Delete Workspace",
|
||||
deleting: "Deleting Workspace...",
|
||||
"confirm-start": "You are about to delete your entire",
|
||||
"confirm-end":
|
||||
"workspace. This will remove all vector embeddings in your vector database.\n\nThe original source files will remain untouched. This action is irreversible.",
|
||||
},
|
||||
},
|
||||
|
||||
// Chat Settings
|
||||
chat: {
|
||||
llm: {
|
||||
title: "Workspace LLM Provider",
|
||||
description:
|
||||
"The specific LLM provider & model that will be used for this workspace. By default, it uses the system LLM provider and settings.",
|
||||
search: "Search all LLM providers",
|
||||
},
|
||||
model: {
|
||||
title: "Workspace Chat model",
|
||||
description:
|
||||
"The specific chat model that will be used for this workspace. If empty, will use the system LLM preference.",
|
||||
wait: "-- waiting for models --",
|
||||
},
|
||||
mode: {
|
||||
title: "Chat mode",
|
||||
chat: {
|
||||
title: "Chat",
|
||||
"desc-start": "will provide answers with the LLM's general knowledge",
|
||||
and: "and",
|
||||
"desc-end": "document context that is found.",
|
||||
},
|
||||
query: {
|
||||
title: "Query",
|
||||
"desc-start": "will provide answers",
|
||||
only: "only",
|
||||
"desc-end": "if document context is found.",
|
||||
},
|
||||
},
|
||||
history: {
|
||||
title: "Chat History",
|
||||
"desc-start":
|
||||
"The number of previous chats that will be included in the response's short-term memory.",
|
||||
recommend: "Recommend 20. ",
|
||||
"desc-end":
|
||||
"AAnything more than 45 is likely to lead to continuous chat failures depending on message size.",
|
||||
},
|
||||
prompt: {
|
||||
title: "Prompt",
|
||||
description:
|
||||
"The prompt that will be used on this workspace. Define the context and instructions for the AI to generate a response. You should to provide a carefully crafted prompt so the AI can generate a relevant and accurate response.",
|
||||
},
|
||||
refusal: {
|
||||
title: "Query mode refusal response",
|
||||
"desc-start": "When in",
|
||||
query: "query",
|
||||
"desc-end":
|
||||
"mode, you may want to return a custom refusal response when no context is found.",
|
||||
},
|
||||
temperature: {
|
||||
title: "LLM Temperature",
|
||||
"desc-start":
|
||||
'This setting controls how "creative" your LLM responses will be.',
|
||||
"desc-end":
|
||||
"The higher the number the more creative. For some models this can lead to incoherent responses when set too high.",
|
||||
hint: "Most LLMs have various acceptable ranges of valid values. Consult your LLM provider for that information.",
|
||||
},
|
||||
},
|
||||
|
||||
// Vector Database
|
||||
"vector-workspace": {
|
||||
identifier: "Vector database identifier",
|
||||
snippets: {
|
||||
title: "Max Context Snippets",
|
||||
description:
|
||||
"This setting controls the maximum amount of context snippets the will be sent to the LLM for per chat or query.",
|
||||
recommend: "Recommended: 4",
|
||||
},
|
||||
doc: {
|
||||
title: "Document similarity threshold",
|
||||
description:
|
||||
"The minimum similarity score required for a source to be considered related to the chat. The higher the number, the more similar the source must be to the chat.",
|
||||
zero: "No restriction",
|
||||
low: "Low (similarity score ≥ .25)",
|
||||
medium: "Medium (similarity score ≥ .50)",
|
||||
high: "High (similarity score ≥ .75)",
|
||||
},
|
||||
reset: {
|
||||
reset: "Reset Vector Database",
|
||||
resetting: "Clearing vectors...",
|
||||
confirm:
|
||||
"You are about to reset this workspace's vector database. This will remove all vector embeddings currently embedded.\n\nThe original source files will remain untouched. This action is irreversible.",
|
||||
error: "Workspace vector database could not be reset!",
|
||||
success: "Workspace vector database was reset!",
|
||||
},
|
||||
},
|
||||
|
||||
// Agent Configuration
|
||||
agent: {
|
||||
"performance-warning":
|
||||
"Performance of LLMs that do not explicitly support tool-calling is highly dependent on the model's capabilities and accuracy. Some abilities may be limited or non-functional.",
|
||||
provider: {
|
||||
title: "Workspace Agent LLM Provider",
|
||||
description:
|
||||
"The specific LLM provider & model that will be used for this workspace's @agent agent.",
|
||||
},
|
||||
mode: {
|
||||
chat: {
|
||||
title: "Workspace Agent Chat model",
|
||||
description:
|
||||
"The specific chat model that will be used for this workspace's @agent agent.",
|
||||
},
|
||||
title: "Workspace Agent model",
|
||||
description:
|
||||
"The specific LLM model that will be used for this workspace's @agent agent.",
|
||||
wait: "-- waiting for models --",
|
||||
},
|
||||
|
||||
skill: {
|
||||
title: "Default agent skills",
|
||||
description:
|
||||
"Improve the natural abilities of the default agent with these pre-built skills. This set up applies to all workspaces.",
|
||||
rag: {
|
||||
title: "RAG & long-term memory",
|
||||
description:
|
||||
'Allow the agent to leverage your local documents to answer a query or ask the agent to "remember" pieces of content for long-term memory retrieval.',
|
||||
},
|
||||
view: {
|
||||
title: "View & summarize documents",
|
||||
description:
|
||||
"Allow the agent to list and summarize the content of workspace files currently embedded.",
|
||||
},
|
||||
scrape: {
|
||||
title: "Scrape websites",
|
||||
description:
|
||||
"Allow the agent to visit and scrape the content of websites.",
|
||||
},
|
||||
generate: {
|
||||
title: "Generate charts",
|
||||
description:
|
||||
"Enable the default agent to generate various types of charts from data provided or given in chat.",
|
||||
},
|
||||
save: {
|
||||
title: "Generate & save files to browser",
|
||||
description:
|
||||
"Enable the default agent to generate and write to files that save and can be downloaded in your browser.",
|
||||
},
|
||||
web: {
|
||||
title: "Live web search and browsing",
|
||||
"desc-start":
|
||||
"Enable your agent to search the web to answer your questions by connecting to a web-search (SERP) provider.",
|
||||
"desc-end":
|
||||
"Web search during agent sessions will not work until this is set up.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Workspace Chats
|
||||
recorded: {
|
||||
title: "Workspace Chats",
|
||||
description:
|
||||
"These are all the recorded chats and messages that have been sent by users ordered by their creation date.",
|
||||
export: "Export",
|
||||
table: {
|
||||
id: "Id",
|
||||
by: "Sent By",
|
||||
workspace: "Workspace",
|
||||
prompt: "Prompt",
|
||||
response: "Response",
|
||||
at: "Sent At",
|
||||
},
|
||||
},
|
||||
|
||||
// Appearance
|
||||
appearance: {
|
||||
title: "Appearance",
|
||||
description: "Customize the appearance settings of your platform.",
|
||||
logo: {
|
||||
title: "Customize Logo",
|
||||
description: "Upload your custom logo to make your chatbot yours.",
|
||||
add: "Add a custom logo",
|
||||
recommended: "Recommended size: 800 x 200",
|
||||
remove: "Remove",
|
||||
replace: "Replace",
|
||||
},
|
||||
message: {
|
||||
title: "Customize Messages",
|
||||
description: "Customize the automatic messages displayed to your users.",
|
||||
new: "New",
|
||||
system: "system",
|
||||
user: "user",
|
||||
message: "message",
|
||||
assistant: "AnythingLLM Chat Assistant",
|
||||
"double-click": "Double click to edit...",
|
||||
save: "Save Messages",
|
||||
},
|
||||
icons: {
|
||||
title: "Custom Footer Icons",
|
||||
description:
|
||||
"Customize the footer icons displayed on the bottom of the sidebar.",
|
||||
icon: "Icon",
|
||||
link: "Link",
|
||||
},
|
||||
},
|
||||
|
||||
// API Keys
|
||||
api: {
|
||||
title: "API Keys",
|
||||
description:
|
||||
"API keys allow the holder to programmatically access and manage this AnythingLLM instance.",
|
||||
link: "Read the API documentation",
|
||||
generate: "Generate New API Key",
|
||||
table: {
|
||||
key: "API Key",
|
||||
by: "Created By",
|
||||
created: "Created",
|
||||
},
|
||||
},
|
||||
|
||||
llm: {
|
||||
title: "LLM Preference",
|
||||
description:
|
||||
"These are the credentials and settings for your preferred LLM chat & embedding provider. Its important these keys are current and correct or else AnythingLLM will not function properly.",
|
||||
provider: "LLM Provider",
|
||||
},
|
||||
|
||||
transcription: {
|
||||
title: "Transcription Model Preference",
|
||||
description:
|
||||
"These are the credentials and settings for your preferred transcription model provider. Its important these keys are current and correct or else media files and audio will not transcribe.",
|
||||
provider: "Transcription Provider",
|
||||
"warn-start":
|
||||
"Using the local whisper model on machines with limited RAM or CPU can stall AnythingLLM when processing media files.",
|
||||
"warn-recommend":
|
||||
"We recommend at least 2GB of RAM and upload files <10Mb.",
|
||||
"warn-end":
|
||||
"The built-in model will automatically download on the first use.",
|
||||
},
|
||||
|
||||
embedding: {
|
||||
title: "Embedding Preference",
|
||||
"desc-start":
|
||||
"When using an LLM that does not natively support an embedding engine - you may need to additionally specify credentials to for embedding text.",
|
||||
"desc-end":
|
||||
"Embedding is the process of turning text into vectors. These credentials are required to turn your files and prompts into a format which AnythingLLM can use to process.",
|
||||
provider: {
|
||||
title: "Embedding Provider",
|
||||
description:
|
||||
"There is no set up required when using AnythingLLM's native embedding engine.",
|
||||
},
|
||||
},
|
||||
|
||||
text: {
|
||||
title: "Text splitting & Chunking Preferences",
|
||||
"desc-start":
|
||||
"Sometimes, you may want to change the default way that new documents are split and chunked before being inserted into your vector database.",
|
||||
"desc-end":
|
||||
"You should only modify this setting if you understand how text splitting works and it's side effects.",
|
||||
"warn-start": "Changes here will only apply to",
|
||||
"warn-center": "newly embedded documents",
|
||||
"warn-end": ", not existing documents.",
|
||||
size: {
|
||||
title: "Text Chunk Size",
|
||||
description:
|
||||
"This is the maximum length of characters that can be present in a single vector.",
|
||||
recommend: "Embed model maximum length is",
|
||||
},
|
||||
|
||||
overlap: {
|
||||
title: "Text Chunk Overlap",
|
||||
description:
|
||||
"This is the maximum overlap of characters that occurs during chunking between two adjacent text chunks.",
|
||||
},
|
||||
},
|
||||
|
||||
// Vector Database
|
||||
vector: {
|
||||
title: "Vector Database",
|
||||
description:
|
||||
"These are the credentials and settings for how your AnythingLLM instance will function. It's important these keys are current and correct.",
|
||||
provider: {
|
||||
title: "Vector Database Provider",
|
||||
description: "There is no configuration needed for LanceDB.",
|
||||
},
|
||||
},
|
||||
|
||||
// Embeddable Chat Widgets
|
||||
embeddable: {
|
||||
title: "Embeddable Chat Widgets",
|
||||
description:
|
||||
"Embeddable chat widgets are public facing chat interfaces that are tied to a single workspace. These allow you to build workspaces that then you can publish to the world.",
|
||||
create: "Create embed",
|
||||
table: {
|
||||
workspace: "Workspace",
|
||||
chats: "Sent Chats",
|
||||
Active: "Active Domains",
|
||||
},
|
||||
},
|
||||
|
||||
"embed-chats": {
|
||||
title: "Embed Chats",
|
||||
description:
|
||||
"These are all the recorded chats and messages from any embed that you have published.",
|
||||
table: {
|
||||
embed: "Embed",
|
||||
sender: "Sender",
|
||||
message: "Message",
|
||||
response: "Response",
|
||||
at: "Sent At",
|
||||
},
|
||||
},
|
||||
|
||||
multi: {
|
||||
title: "Multi-User Mode",
|
||||
description:
|
||||
"Set up your instance to support your team by activating Multi-User Mode.",
|
||||
enable: {
|
||||
"is-enable": "Multi-User Mode is Enabled",
|
||||
enable: "Enable Multi-User Mode",
|
||||
description:
|
||||
"By default, you will be the only admin. As an admin you will need to create accounts for all new users or admins. Do not lose your password as only an Admin user can reset passwords.",
|
||||
username: "Admin account username",
|
||||
password: "Admin account password",
|
||||
},
|
||||
password: {
|
||||
title: "Password Protection",
|
||||
description:
|
||||
"Protect your AnythingLLM instance with a password. If you forget this there is no recovery method so ensure you save this password.",
|
||||
},
|
||||
instance: {
|
||||
title: "Password Protect Instance",
|
||||
description:
|
||||
"By default, you will be the only admin. As an admin you will need to create accounts for all new users or admins. Do not lose your password as only an Admin user can reset passwords.",
|
||||
password: "Instance password",
|
||||
},
|
||||
},
|
||||
|
||||
// Event Logs
|
||||
event: {
|
||||
title: "Event Logs",
|
||||
description:
|
||||
"View all actions and events happening on this instance for monitoring.",
|
||||
clear: "Clear Event Logs",
|
||||
table: {
|
||||
type: "Event Type",
|
||||
user: "User",
|
||||
occurred: "Occurred At",
|
||||
},
|
||||
},
|
||||
|
||||
// Privacy & Data-Handling
|
||||
privacy: {
|
||||
title: "Privacy & Data-Handling",
|
||||
description:
|
||||
"This is your configuration for how connected third party providers and AnythingLLM handle your data.",
|
||||
llm: "LLM Selection",
|
||||
embedding: "Embedding Preference",
|
||||
vector: "Vector Database",
|
||||
anonymous: "Anonymous Telemetry Enabled",
|
||||
},
|
||||
};
|
||||
|
||||
export default TRANSLATIONS;
|
443
frontend/src/locales/es/common.js
Normal file
443
frontend/src/locales/es/common.js
Normal file
@ -0,0 +1,443 @@
|
||||
const TRANSLATIONS = {
|
||||
common: {
|
||||
"workspaces-name": "Nombre de espacios de trabajo",
|
||||
error: "error",
|
||||
success: "éxito",
|
||||
user: "Usuario",
|
||||
selection: "Selección de modelo",
|
||||
saving: "Guardando...",
|
||||
save: "Guardar cambios",
|
||||
previous: "Página anterior",
|
||||
next: "Página siguiente",
|
||||
},
|
||||
|
||||
settings: {
|
||||
title: "Configuración de instancia",
|
||||
system: "Preferencias del sistema",
|
||||
invites: "Invitación",
|
||||
users: "Usuarios",
|
||||
workspaces: "Espacios de trabajo",
|
||||
"workspace-chats": "Chat del espacio de trabajo",
|
||||
appearance: "Apariencia",
|
||||
"api-keys": "Claves API",
|
||||
llm: "Preferencia de LLM",
|
||||
transcription: "Modelo de transcripción",
|
||||
embedder: "Preferencias de incrustación",
|
||||
"text-splitting": "Divisor y fragmentación de texto",
|
||||
"vector-database": "Base de datos de vectores",
|
||||
embeds: "Widgets de chat incrustados",
|
||||
"embed-chats": "Historial de chats incrustados",
|
||||
security: "Seguridad",
|
||||
"event-logs": "Registros de eventos",
|
||||
privacy: "Privacidad y datos",
|
||||
},
|
||||
|
||||
login: {
|
||||
"multi-user": {
|
||||
welcome: "Bienvenido a",
|
||||
"placeholder-username": "Nombre de usuario",
|
||||
"placeholder-password": "Contraseña",
|
||||
login: "Iniciar sesión",
|
||||
validating: "Validando...",
|
||||
"forgot-pass": "Olvidé mi contraseña",
|
||||
reset: "Restablecer",
|
||||
},
|
||||
"sign-in": {
|
||||
start: "Iniciar sesión en tu",
|
||||
end: "cuenta.",
|
||||
},
|
||||
},
|
||||
|
||||
"workspaces—settings": {
|
||||
general: "Configuración general",
|
||||
chat: "Configuración de chat",
|
||||
vector: "Base de datos de vectores",
|
||||
members: "Miembros",
|
||||
agent: "Configuración del agente",
|
||||
},
|
||||
|
||||
general: {
|
||||
vector: {
|
||||
title: "Conteo de vectores",
|
||||
description: "Número total de vectores en tu base de datos de vectores.",
|
||||
},
|
||||
names: {
|
||||
description:
|
||||
"Esto solo cambiará el nombre de visualización de tu espacio de trabajo.",
|
||||
},
|
||||
message: {
|
||||
title: "Mensajes de chat sugeridos",
|
||||
description:
|
||||
"Personaliza los mensajes que se sugerirán a los usuarios de tu espacio de trabajo.",
|
||||
add: "Agregar nuevo mensaje",
|
||||
save: "Guardar mensajes",
|
||||
heading: "Explícame",
|
||||
body: "los beneficios de AnythingLLM",
|
||||
},
|
||||
pfp: {
|
||||
title: "Imagen de perfil del asistente",
|
||||
description:
|
||||
"Personaliza la imagen de perfil del asistente para este espacio de trabajo.",
|
||||
image: "Imagen del espacio de trabajo",
|
||||
remove: "Eliminar imagen del espacio de trabajo",
|
||||
},
|
||||
delete: {
|
||||
title: "Eliminar Espacio de Trabajo",
|
||||
description:
|
||||
"Eliminar este espacio de trabajo y todos sus datos. Esto eliminará el espacio de trabajo para todos los usuarios.",
|
||||
delete: "Eliminar espacio de trabajo",
|
||||
deleting: "Eliminando espacio de trabajo...",
|
||||
"confirm-start": "Estás a punto de eliminar tu",
|
||||
"confirm-end":
|
||||
"espacio de trabajo. Esto eliminará todas las incrustaciones de vectores en tu base de datos de vectores.\n\nLos archivos de origen originales permanecerán intactos. Esta acción es irreversible.",
|
||||
},
|
||||
},
|
||||
|
||||
chat: {
|
||||
llm: {
|
||||
title: "Proveedor LLM del espacio de trabajo",
|
||||
description:
|
||||
"El proveedor y modelo LLM específico que se utilizará para este espacio de trabajo. Por defecto, utiliza el proveedor y configuración del sistema LLM.",
|
||||
search: "Buscar todos los proveedores LLM",
|
||||
},
|
||||
model: {
|
||||
title: "Modelo de chat del espacio de trabajo",
|
||||
description:
|
||||
"El modelo de chat específico que se utilizará para este espacio de trabajo. Si está vacío, se utilizará la preferencia LLM del sistema.",
|
||||
wait: "-- esperando modelos --",
|
||||
},
|
||||
mode: {
|
||||
title: "Modo de chat",
|
||||
chat: {
|
||||
title: "Chat",
|
||||
"desc-start":
|
||||
"proporcionará respuestas con el conocimiento general del LLM",
|
||||
and: "y",
|
||||
"desc-end": "el contexto del documento que se encuentre.",
|
||||
},
|
||||
query: {
|
||||
title: "Consulta",
|
||||
"desc-start": "proporcionará respuestas",
|
||||
only: "solo",
|
||||
"desc-end": "si se encuentra el contexto del documento.",
|
||||
},
|
||||
},
|
||||
history: {
|
||||
title: "Historial de chat",
|
||||
"desc-start":
|
||||
"El número de chats anteriores que se incluirán en la memoria a corto plazo de la respuesta.",
|
||||
recommend: "Recomendar 20. ",
|
||||
"desc-end":
|
||||
"Cualquier cosa más de 45 probablemente conducirá a fallos continuos en el chat dependiendo del tamaño del mensaje.",
|
||||
},
|
||||
prompt: {
|
||||
title: "Prompt",
|
||||
description:
|
||||
"El prompt que se utilizará en este espacio de trabajo. Define el contexto y las instrucciones para que la IA genere una respuesta. Debes proporcionar un prompt cuidadosamente elaborado para que la IA pueda generar una respuesta relevante y precisa.",
|
||||
},
|
||||
refusal: {
|
||||
title: "Respuesta de rechazo en modo consulta",
|
||||
"desc-start": "Cuando esté en",
|
||||
query: "consulta",
|
||||
"desc-end":
|
||||
"modo, es posible que desees devolver una respuesta de rechazo personalizada cuando no se encuentre contexto.",
|
||||
},
|
||||
temperature: {
|
||||
title: "Temperatura de LLM",
|
||||
"desc-start":
|
||||
'Esta configuración controla cuán "creativas" serán las respuestas de tu LLM.',
|
||||
"desc-end":
|
||||
"Cuanto mayor sea el número, más creativas serán las respuestas. Para algunos modelos, esto puede llevar a respuestas incoherentes cuando se establece demasiado alto.",
|
||||
hint: "La mayoría de los LLM tienen varios rangos aceptables de valores válidos. Consulta a tu proveedor de LLM para obtener esa información.",
|
||||
},
|
||||
},
|
||||
|
||||
"vector-workspace": {
|
||||
identifier: "Identificador de la base de datos de vectores",
|
||||
snippets: {
|
||||
title: "Máximo de fragmentos de contexto",
|
||||
description:
|
||||
"Esta configuración controla la cantidad máxima de fragmentos de contexto que se enviarán al LLM por chat o consulta.",
|
||||
recommend: "Recomendado: 4",
|
||||
},
|
||||
doc: {
|
||||
title: "Umbral de similitud de documentos",
|
||||
description:
|
||||
"La puntuación mínima de similitud requerida para que una fuente se considere relacionada con el chat. Cuanto mayor sea el número, más similar debe ser la fuente al chat.",
|
||||
zero: "Sin restricción",
|
||||
low: "Bajo (puntuación de similitud ≥ .25)",
|
||||
medium: "Medio (puntuación de similitud ≥ .50)",
|
||||
high: "Alto (puntuación de similitud ≥ .75)",
|
||||
},
|
||||
reset: {
|
||||
reset: "Restablecer la base de datos de vectores",
|
||||
resetting: "Borrando vectores...",
|
||||
confirm:
|
||||
"Estás a punto de restablecer la base de datos de vectores de este espacio de trabajo. Esto eliminará todas las incrustaciones de vectores actualmente incrustadas.\n\nLos archivos de origen originales permanecerán intactos. Esta acción es irreversible.",
|
||||
error:
|
||||
"¡No se pudo restablecer la base de datos de vectores del espacio de trabajo!",
|
||||
success:
|
||||
"¡La base de datos de vectores del espacio de trabajo fue restablecida!",
|
||||
},
|
||||
},
|
||||
|
||||
agent: {
|
||||
"performance-warning":
|
||||
"El rendimiento de los LLM que no admiten explícitamente la llamada de herramientas depende en gran medida de las capacidades y la precisión del modelo. Algunas habilidades pueden estar limitadas o no funcionar.",
|
||||
provider: {
|
||||
title: "Proveedor de LLM del agente del espacio de trabajo",
|
||||
description:
|
||||
"El proveedor y modelo LLM específico que se utilizará para el agente @agent de este espacio de trabajo.",
|
||||
},
|
||||
mode: {
|
||||
chat: {
|
||||
title: "Modelo de chat del agente del espacio de trabajo",
|
||||
description:
|
||||
"El modelo de chat específico que se utilizará para el agente @agent de este espacio de trabajo.",
|
||||
},
|
||||
title: "Modelo del agente del espacio de trabajo",
|
||||
description:
|
||||
"El modelo LLM específico que se utilizará para el agente @agent de este espacio de trabajo.",
|
||||
wait: "-- esperando modelos --",
|
||||
},
|
||||
|
||||
skill: {
|
||||
title: "Habilidades predeterminadas del agente",
|
||||
description:
|
||||
"Mejora las habilidades naturales del agente predeterminado con estas habilidades preconstruidas. Esta configuración se aplica a todos los espacios de trabajo.",
|
||||
rag: {
|
||||
title: "RAG y memoria a largo plazo",
|
||||
description:
|
||||
'Permitir que el agente aproveche tus documentos locales para responder a una consulta o pedirle al agente que "recuerde" piezas de contenido para la recuperación de memoria a largo plazo.',
|
||||
},
|
||||
view: {
|
||||
title: "Ver y resumir documentos",
|
||||
description:
|
||||
"Permitir que el agente enumere y resuma el contenido de los archivos del espacio de trabajo actualmente incrustados.",
|
||||
},
|
||||
scrape: {
|
||||
title: "Rastrear sitios web",
|
||||
description:
|
||||
"Permitir que el agente visite y rastree el contenido de sitios web.",
|
||||
},
|
||||
generate: {
|
||||
title: "Generar gráficos",
|
||||
description:
|
||||
"Habilitar al agente predeterminado para generar varios tipos de gráficos a partir de datos proporcionados o dados en el chat.",
|
||||
},
|
||||
save: {
|
||||
title: "Generar y guardar archivos en el navegador",
|
||||
description:
|
||||
"Habilitar al agente predeterminado para generar y escribir archivos que se guarden y puedan descargarse en tu navegador.",
|
||||
},
|
||||
web: {
|
||||
title: "Búsqueda en vivo en la web y navegación",
|
||||
"desc-start":
|
||||
"Permitir que tu agente busque en la web para responder tus preguntas conectándose a un proveedor de búsqueda en la web (SERP).",
|
||||
"desc-end":
|
||||
"La búsqueda en la web durante las sesiones del agente no funcionará hasta que esto esté configurado.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
recorded: {
|
||||
title: "Chats del espacio de trabajo",
|
||||
description:
|
||||
"Estos son todos los chats y mensajes grabados que han sido enviados por los usuarios ordenados por su fecha de creación.",
|
||||
export: "Exportar",
|
||||
table: {
|
||||
id: "Id",
|
||||
by: "Enviado por",
|
||||
workspace: "Espacio de trabajo",
|
||||
prompt: "Prompt",
|
||||
response: "Respuesta",
|
||||
at: "Enviado a",
|
||||
},
|
||||
},
|
||||
|
||||
appearance: {
|
||||
title: "Apariencia",
|
||||
description: "Personaliza la configuración de apariencia de tu plataforma.",
|
||||
logo: {
|
||||
title: "Personalizar logotipo",
|
||||
description:
|
||||
"Sube tu logotipo personalizado para hacer que tu chatbot sea tuyo.",
|
||||
add: "Agregar un logotipo personalizado",
|
||||
recommended: "Tamaño recomendado: 800 x 200",
|
||||
remove: "Eliminar",
|
||||
replace: "Reemplazar",
|
||||
},
|
||||
message: {
|
||||
title: "Personalizar mensajes",
|
||||
description:
|
||||
"Personaliza los mensajes automáticos que se muestran a tus usuarios.",
|
||||
new: "Nuevo",
|
||||
system: "sistema",
|
||||
user: "usuario",
|
||||
message: "mensaje",
|
||||
assistant: "Asistente de chat AnythingLLM",
|
||||
"double-click": "Haz doble clic para editar...",
|
||||
save: "Guardar mensajes",
|
||||
},
|
||||
icons: {
|
||||
title: "Iconos de pie de página personalizados",
|
||||
description:
|
||||
"Personaliza los iconos de pie de página que se muestran en la parte inferior de la barra lateral.",
|
||||
icon: "Icono",
|
||||
link: "Enlace",
|
||||
},
|
||||
},
|
||||
|
||||
api: {
|
||||
title: "Claves API",
|
||||
description:
|
||||
"Las claves API permiten al titular acceder y gestionar programáticamente esta instancia de AnythingLLM.",
|
||||
link: "Leer la documentación de la API",
|
||||
generate: "Generar nueva clave API",
|
||||
table: {
|
||||
key: "Clave API",
|
||||
by: "Creado por",
|
||||
created: "Creado",
|
||||
},
|
||||
},
|
||||
|
||||
llm: {
|
||||
title: "Preferencia de LLM",
|
||||
description:
|
||||
"Estas son las credenciales y configuraciones para tu proveedor preferido de chat y incrustación de LLM. Es importante que estas claves estén actualizadas y correctas, de lo contrario AnythingLLM no funcionará correctamente.",
|
||||
provider: "Proveedor de LLM",
|
||||
},
|
||||
|
||||
transcription: {
|
||||
title: "Preferencia de modelo de transcripción",
|
||||
description:
|
||||
"Estas son las credenciales y configuraciones para tu proveedor preferido de modelo de transcripción. Es importante que estas claves estén actualizadas y correctas, de lo contrario los archivos multimedia y de audio no se transcribirán.",
|
||||
provider: "Proveedor de transcripción",
|
||||
"warn-start":
|
||||
"El uso del modelo local Whisper en máquinas con RAM o CPU limitadas puede bloquear AnythingLLM al procesar archivos multimedia.",
|
||||
"warn-recommend":
|
||||
"Recomendamos al menos 2GB de RAM y subir archivos <10Mb.",
|
||||
"warn-end":
|
||||
"El modelo incorporado se descargará automáticamente en el primer uso.",
|
||||
},
|
||||
|
||||
embedding: {
|
||||
title: "Preferencia de incrustación",
|
||||
"desc-start":
|
||||
"Cuando uses un LLM que no admita de forma nativa un motor de incrustación, es posible que necesites especificar credenciales adicionales para incrustar texto.",
|
||||
"desc-end":
|
||||
"La incrustación es el proceso de convertir texto en vectores. Estas credenciales son necesarias para convertir tus archivos y prompts en un formato que AnythingLLM pueda usar para procesar.",
|
||||
provider: {
|
||||
title: "Proveedor de incrustación",
|
||||
description:
|
||||
"No se requiere configuración cuando se utiliza el motor de incrustación nativo de AnythingLLM.",
|
||||
},
|
||||
},
|
||||
|
||||
text: {
|
||||
title: "Preferencias de división y fragmentación de texto",
|
||||
"desc-start":
|
||||
"A veces, es posible que desees cambiar la forma predeterminada en que los nuevos documentos se dividen y fragmentan antes de ser insertados en tu base de datos de vectores.",
|
||||
"desc-end":
|
||||
"Solo debes modificar esta configuración si entiendes cómo funciona la división de texto y sus efectos secundarios.",
|
||||
"warn-start": "Los cambios aquí solo se aplicarán a",
|
||||
"warn-center": "documentos recién incrustados",
|
||||
"warn-end": ", no a los documentos existentes.",
|
||||
size: {
|
||||
title: "Tamaño del fragmento de texto",
|
||||
description:
|
||||
"Esta es la longitud máxima de caracteres que puede estar presente en un solo vector.",
|
||||
recommend: "La longitud máxima del modelo de incrustación es",
|
||||
},
|
||||
|
||||
overlap: {
|
||||
title: "Superposición de fragmentos de texto",
|
||||
description:
|
||||
"Esta es la superposición máxima de caracteres que ocurre durante la fragmentación entre dos fragmentos de texto adyacentes.",
|
||||
},
|
||||
},
|
||||
|
||||
vector: {
|
||||
title: "Base de datos de vectores",
|
||||
description:
|
||||
"Estas son las credenciales y configuraciones para cómo funcionará tu instancia de AnythingLLM. Es importante que estas claves estén actualizadas y correctas.",
|
||||
provider: {
|
||||
title: "Proveedor de base de datos de vectores",
|
||||
description: "No se necesita configuración para LanceDB.",
|
||||
},
|
||||
},
|
||||
|
||||
embeddable: {
|
||||
title: "Widgets de chat incrustables",
|
||||
description:
|
||||
"Los widgets de chat incrustables son interfaces de chat de cara al público que están vinculadas a un solo espacio de trabajo. Esto te permite crear espacios de trabajo que luego puedes publicar al mundo.",
|
||||
create: "Crear incrustación",
|
||||
table: {
|
||||
workspace: "Espacio de trabajo",
|
||||
chats: "Chats enviados",
|
||||
Active: "Dominios activos",
|
||||
},
|
||||
},
|
||||
|
||||
"embed-chats": {
|
||||
title: "Incrustar chats",
|
||||
description:
|
||||
"Estos son todos los chats y mensajes grabados de cualquier incrustación que hayas publicado.",
|
||||
table: {
|
||||
embed: "Incrustar",
|
||||
sender: "Remitente",
|
||||
message: "Mensaje",
|
||||
response: "Respuesta",
|
||||
at: "Enviado a",
|
||||
},
|
||||
},
|
||||
|
||||
multi: {
|
||||
title: "Modo multiusuario",
|
||||
description:
|
||||
"Configura tu instancia para admitir a tu equipo activando el modo multiusuario.",
|
||||
enable: {
|
||||
"is-enable": "El modo multiusuario está habilitado",
|
||||
enable: "Habilitar modo multiusuario",
|
||||
description:
|
||||
"Por defecto, serás el único administrador. Como administrador, necesitarás crear cuentas para todos los nuevos usuarios o administradores. No pierdas tu contraseña ya que solo un usuario administrador puede restablecer las contraseñas.",
|
||||
username: "Nombre de usuario de la cuenta de administrador",
|
||||
password: "Contraseña de la cuenta de administrador",
|
||||
},
|
||||
password: {
|
||||
title: "Protección con contraseña",
|
||||
description:
|
||||
"Protege tu instancia de AnythingLLM con una contraseña. Si olvidas esta contraseña, no hay método de recuperación, así que asegúrate de guardar esta contraseña.",
|
||||
},
|
||||
instance: {
|
||||
title: "Proteger instancia con contraseña",
|
||||
description:
|
||||
"Por defecto, serás el único administrador. Como administrador, necesitarás crear cuentas para todos los nuevos usuarios o administradores. No pierdas tu contraseña ya que solo un usuario administrador puede restablecer las contraseñas.",
|
||||
password: "Contraseña de la instancia",
|
||||
},
|
||||
},
|
||||
|
||||
event: {
|
||||
title: "Registros de eventos",
|
||||
description:
|
||||
"Ver todas las acciones y eventos que ocurren en esta instancia para monitoreo.",
|
||||
clear: "Borrar registros de eventos",
|
||||
table: {
|
||||
type: "Tipo de evento",
|
||||
user: "Usuario",
|
||||
occurred: "Ocurrido a",
|
||||
},
|
||||
},
|
||||
|
||||
privacy: {
|
||||
title: "Privacidad y manejo de datos",
|
||||
description:
|
||||
"Esta es tu configuración para cómo los proveedores de terceros conectados y AnythingLLM manejan tus datos.",
|
||||
llm: "Selección de LLM",
|
||||
embedding: "Preferencia de incrustación",
|
||||
vector: "Base de datos de vectores",
|
||||
anonymous: "Telemetría anónima habilitada",
|
||||
},
|
||||
};
|
||||
|
||||
export default TRANSLATIONS;
|
459
frontend/src/locales/fr/common.js
Normal file
459
frontend/src/locales/fr/common.js
Normal file
@ -0,0 +1,459 @@
|
||||
const TRANSLATIONS = {
|
||||
common: {
|
||||
"workspaces-name": "Nom des espaces de travail",
|
||||
error: "erreur",
|
||||
success: "succès",
|
||||
user: "Utilisateur",
|
||||
selection: "Sélection du modèle",
|
||||
saving: "Enregistrement...",
|
||||
save: "Enregistrer les modifications",
|
||||
previous: "Page précédente",
|
||||
next: "Page suivante",
|
||||
},
|
||||
|
||||
// Setting Sidebar menu items.
|
||||
settings: {
|
||||
title: "Paramètres de l'instance",
|
||||
system: "Préférences système",
|
||||
invites: "Invitation",
|
||||
users: "Utilisateurs",
|
||||
workspaces: "Espaces de travail",
|
||||
"workspace-chats": "Chat de l'espace de travail",
|
||||
appearance: "Apparence",
|
||||
"api-keys": "Clés API",
|
||||
llm: "Préférence LLM",
|
||||
transcription: "Modèle de transcription",
|
||||
embedder: "Préférences d'intégration",
|
||||
"text-splitting": "Diviseur de texte et découpage",
|
||||
"vector-database": "Base de données vectorielle",
|
||||
embeds: "Widgets de chat intégrés",
|
||||
"embed-chats": "Historique des chats intégrés",
|
||||
security: "Sécurité",
|
||||
"event-logs": "Journaux d'événements",
|
||||
privacy: "Confidentialité et données",
|
||||
},
|
||||
|
||||
// Page Definitions
|
||||
login: {
|
||||
"multi-user": {
|
||||
welcome: "Bienvenue à",
|
||||
"placeholder-username": "Nom d'utilisateur",
|
||||
"placeholder-password": "Mot de passe",
|
||||
login: "Connexion",
|
||||
validating: "Validation...",
|
||||
"forgot-pass": "Mot de passe oublié",
|
||||
reset: "Réinitialiser",
|
||||
},
|
||||
"sign-in": {
|
||||
start: "Connectez-vous à votre",
|
||||
end: "compte.",
|
||||
},
|
||||
},
|
||||
|
||||
// Workspace Settings menu items
|
||||
"workspaces—settings": {
|
||||
general: "Paramètres généraux",
|
||||
chat: "Paramètres de chat",
|
||||
vector: "Base de données vectorielle",
|
||||
members: "Membres",
|
||||
agent: "Configuration de l'agent",
|
||||
},
|
||||
|
||||
// General Appearance
|
||||
general: {
|
||||
vector: {
|
||||
title: "Nombre de vecteurs",
|
||||
description:
|
||||
"Nombre total de vecteurs dans votre base de données vectorielle.",
|
||||
},
|
||||
names: {
|
||||
description:
|
||||
"Cela ne changera que le nom d'affichage de votre espace de travail.",
|
||||
},
|
||||
message: {
|
||||
title: "Messages de chat suggérés",
|
||||
description:
|
||||
"Personnalisez les messages qui seront suggérés aux utilisateurs de votre espace de travail.",
|
||||
add: "Ajouter un nouveau message",
|
||||
save: "Enregistrer les messages",
|
||||
heading: "Expliquez-moi",
|
||||
body: "les avantages de AnythingLLM",
|
||||
},
|
||||
pfp: {
|
||||
title: "Image de profil de l'assistant",
|
||||
description:
|
||||
"Personnalisez l'image de profil de l'assistant pour cet espace de travail.",
|
||||
image: "Image de l'espace de travail",
|
||||
remove: "Supprimer l'image de l'espace de travail",
|
||||
},
|
||||
delete: {
|
||||
title: "Supprimer l'Espace de Travail",
|
||||
description:
|
||||
"Supprimer cet espace de travail et toutes ses données. Cela supprimera l'espace de travail pour tous les utilisateurs.",
|
||||
delete: "Supprimer l'espace de travail",
|
||||
deleting: "Suppression de l'espace de travail...",
|
||||
"confirm-start": "Vous êtes sur le point de supprimer votre",
|
||||
"confirm-end":
|
||||
"espace de travail. Cela supprimera toutes les intégrations vectorielles dans votre base de données vectorielle.\n\nLes fichiers source originaux resteront intacts. Cette action est irréversible.",
|
||||
},
|
||||
},
|
||||
|
||||
// Chat Settings
|
||||
chat: {
|
||||
llm: {
|
||||
title: "Fournisseur LLM de l'espace de travail",
|
||||
description:
|
||||
"Le fournisseur et le modèle LLM spécifiques qui seront utilisés pour cet espace de travail. Par défaut, il utilise le fournisseur et les paramètres LLM du système.",
|
||||
search: "Rechercher tous les fournisseurs LLM",
|
||||
},
|
||||
model: {
|
||||
title: "Modèle de chat de l'espace de travail",
|
||||
description:
|
||||
"Le modèle de chat spécifique qui sera utilisé pour cet espace de travail. Si vide, utilisera la préférence LLM du système.",
|
||||
wait: "-- en attente des modèles --",
|
||||
},
|
||||
mode: {
|
||||
title: "Mode de chat",
|
||||
chat: {
|
||||
title: "Chat",
|
||||
"desc-start":
|
||||
"fournira des réponses avec les connaissances générales du LLM",
|
||||
and: "et",
|
||||
"desc-end": "le contexte du document trouvé.",
|
||||
},
|
||||
query: {
|
||||
title: "Requête",
|
||||
"desc-start": "fournira des réponses",
|
||||
only: "uniquement",
|
||||
"desc-end": "si un contexte de document est trouvé.",
|
||||
},
|
||||
},
|
||||
history: {
|
||||
title: "Historique des chats",
|
||||
"desc-start":
|
||||
"Le nombre de chats précédents qui seront inclus dans la mémoire à court terme de la réponse.",
|
||||
recommend: "Recommandé: 20.",
|
||||
"desc-end":
|
||||
"Tout nombre supérieur à 45 risque de provoquer des échecs de chat continus en fonction de la taille du message.",
|
||||
},
|
||||
prompt: {
|
||||
title: "Invite",
|
||||
description:
|
||||
"L'invite qui sera utilisée sur cet espace de travail. Définissez le contexte et les instructions pour que l'IA génère une réponse. Vous devez fournir une invite soigneusement conçue pour que l'IA puisse générer une réponse pertinente et précise.",
|
||||
},
|
||||
refusal: {
|
||||
title: "Réponse de refus en mode requête",
|
||||
"desc-start": "En mode",
|
||||
query: "requête",
|
||||
"desc-end":
|
||||
", vous pouvez souhaiter retourner une réponse de refus personnalisée lorsque aucun contexte n'est trouvé.",
|
||||
},
|
||||
temperature: {
|
||||
title: "Température LLM",
|
||||
"desc-start":
|
||||
"Ce paramètre contrôle le niveau de créativité des réponses de votre LLM.",
|
||||
"desc-end":
|
||||
"Plus le nombre est élevé, plus la réponse sera créative. Pour certains modèles, cela peut entraîner des réponses incohérentes si la valeur est trop élevée.",
|
||||
hint: "La plupart des LLM ont diverses plages acceptables de valeurs valides. Consultez votre fournisseur LLM pour cette information.",
|
||||
},
|
||||
},
|
||||
|
||||
// Vector Database
|
||||
"vector-workspace": {
|
||||
identifier: "Identifiant de la base de données vectorielle",
|
||||
snippets: {
|
||||
title: "Nombre maximum de contextes",
|
||||
description:
|
||||
"Ce paramètre contrôle le nombre maximum de contextes qui seront envoyés au LLM par chat ou requête.",
|
||||
recommend: "Recommandé: 4",
|
||||
},
|
||||
doc: {
|
||||
title: "Seuil de similarité des documents",
|
||||
description:
|
||||
"Le score de similarité minimum requis pour qu'une source soit considérée comme liée au chat. Plus le nombre est élevé, plus la source doit être similaire au chat.",
|
||||
zero: "Aucune restriction",
|
||||
low: "Bas (score de similarité ≥ .25)",
|
||||
medium: "Moyen (score de similarité ≥ .50)",
|
||||
high: "Élevé (score de similarité ≥ .75)",
|
||||
},
|
||||
reset: {
|
||||
reset: "Réinitialiser la base de données vectorielle",
|
||||
resetting: "Effacement des vecteurs...",
|
||||
confirm:
|
||||
"Vous êtes sur le point de réinitialiser la base de données vectorielle de cet espace de travail. Cela supprimera toutes les intégrations vectorielles actuellement intégrées.\n\nLes fichiers source originaux resteront intacts. Cette action est irréversible.",
|
||||
error:
|
||||
"La base de données vectorielle de l'espace de travail n'a pas pu être réinitialisée !",
|
||||
success:
|
||||
"La base de données vectorielle de l'espace de travail a été réinitialisée !",
|
||||
},
|
||||
},
|
||||
|
||||
// Agent Configuration
|
||||
agent: {
|
||||
"performance-warning":
|
||||
"La performance des LLM qui ne supportent pas explicitement l'appel d'outils dépend fortement des capacités et de la précision du modèle. Certaines capacités peuvent être limitées ou non fonctionnelles.",
|
||||
provider: {
|
||||
title: "Fournisseur LLM de l'agent de l'espace de travail",
|
||||
description:
|
||||
"Le fournisseur et le modèle LLM spécifiques qui seront utilisés pour l'agent @agent de cet espace de travail.",
|
||||
},
|
||||
mode: {
|
||||
chat: {
|
||||
title: "Modèle de chat de l'agent de l'espace de travail",
|
||||
description:
|
||||
"Le modèle de chat spécifique qui sera utilisé pour l'agent @agent de cet espace de travail.",
|
||||
},
|
||||
title: "Modèle de l'agent de l'espace de travail",
|
||||
description:
|
||||
"Le modèle LLM spécifique qui sera utilisé pour l'agent @agent de cet espace de travail.",
|
||||
wait: "-- en attente des modèles --",
|
||||
},
|
||||
|
||||
skill: {
|
||||
title: "Compétences par défaut de l'agent",
|
||||
description:
|
||||
"Améliorez les capacités naturelles de l'agent par défaut avec ces compétences préconstruites. Cette configuration s'applique à tous les espaces de travail.",
|
||||
rag: {
|
||||
title: "RAG et mémoire à long terme",
|
||||
description:
|
||||
"Permettez à l'agent de s'appuyer sur vos documents locaux pour répondre à une requête ou demandez à l'agent de se souvenir de morceaux de contenu pour la récupération de mémoire à long terme.",
|
||||
},
|
||||
view: {
|
||||
title: "Voir et résumer des documents",
|
||||
description:
|
||||
"Permettez à l'agent de lister et de résumer le contenu des fichiers de l'espace de travail actuellement intégrés.",
|
||||
},
|
||||
scrape: {
|
||||
title: "Récupérer des sites web",
|
||||
description:
|
||||
"Permettez à l'agent de visiter et de récupérer le contenu des sites web.",
|
||||
},
|
||||
generate: {
|
||||
title: "Générer des graphiques",
|
||||
description:
|
||||
"Activez l'agent par défaut pour générer différents types de graphiques à partir des données fournies ou données dans le chat.",
|
||||
},
|
||||
save: {
|
||||
title: "Générer et sauvegarder des fichiers dans le navigateur",
|
||||
description:
|
||||
"Activez l'agent par défaut pour générer et écrire des fichiers qui peuvent être sauvegardés et téléchargés dans votre navigateur.",
|
||||
},
|
||||
web: {
|
||||
title: "Recherche web en direct et navigation",
|
||||
"desc-start":
|
||||
"Permettez à votre agent de rechercher sur le web pour répondre à vos questions en se connectant à un fournisseur de recherche web (SERP).",
|
||||
"desc-end":
|
||||
"La recherche web pendant les sessions d'agent ne fonctionnera pas tant que cela ne sera pas configuré.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Workspace Chats
|
||||
recorded: {
|
||||
title: "Chats de l'espace de travail",
|
||||
description:
|
||||
"Voici tous les chats et messages enregistrés qui ont été envoyés par les utilisateurs, classés par date de création.",
|
||||
export: "Exporter",
|
||||
table: {
|
||||
id: "Id",
|
||||
by: "Envoyé par",
|
||||
workspace: "Espace de travail",
|
||||
prompt: "Invite",
|
||||
response: "Réponse",
|
||||
at: "Envoyé à",
|
||||
},
|
||||
},
|
||||
|
||||
// Appearance
|
||||
appearance: {
|
||||
title: "Apparence",
|
||||
description:
|
||||
"Personnalisez les paramètres d'apparence de votre plateforme.",
|
||||
logo: {
|
||||
title: "Personnaliser le logo",
|
||||
description:
|
||||
"Téléchargez votre logo personnalisé pour rendre votre chatbot unique.",
|
||||
add: "Ajouter un logo personnalisé",
|
||||
recommended: "Taille recommandée: 800 x 200",
|
||||
remove: "Supprimer",
|
||||
replace: "Remplacer",
|
||||
},
|
||||
message: {
|
||||
title: "Personnaliser les messages",
|
||||
description:
|
||||
"Personnalisez les messages automatiques affichés à vos utilisateurs.",
|
||||
new: "Nouveau",
|
||||
system: "système",
|
||||
user: "utilisateur",
|
||||
message: "message",
|
||||
assistant: "Assistant de chat AnythingLLM",
|
||||
"double-click": "Double-cliquez pour modifier...",
|
||||
save: "Enregistrer les messages",
|
||||
},
|
||||
icons: {
|
||||
title: "Icônes de pied de page personnalisées",
|
||||
description:
|
||||
"Personnalisez les icônes de pied de page affichées en bas de la barre latérale.",
|
||||
icon: "Icône",
|
||||
link: "Lien",
|
||||
},
|
||||
},
|
||||
|
||||
// API Keys
|
||||
api: {
|
||||
title: "Clés API",
|
||||
description:
|
||||
"Les clés API permettent au titulaire d'accéder et de gérer de manière programmatique cette instance AnythingLLM.",
|
||||
link: "Lisez la documentation de l'API",
|
||||
generate: "Générer une nouvelle clé API",
|
||||
table: {
|
||||
key: "Clé API",
|
||||
by: "Créé par",
|
||||
created: "Créé",
|
||||
},
|
||||
},
|
||||
|
||||
llm: {
|
||||
title: "Préférence LLM",
|
||||
description:
|
||||
"Voici les identifiants et les paramètres de votre fournisseur LLM de chat et d'intégration préféré. Il est important que ces clés soient actuelles et correctes, sinon AnythingLLM ne fonctionnera pas correctement.",
|
||||
provider: "Fournisseur LLM",
|
||||
},
|
||||
|
||||
transcription: {
|
||||
title: "Préférence du modèle de transcription",
|
||||
description:
|
||||
"Voici les identifiants et les paramètres de votre fournisseur de modèle de transcription préféré. Il est important que ces clés soient actuelles et correctes, sinon les fichiers multimédias et audio ne seront pas transcrits.",
|
||||
provider: "Fournisseur de transcription",
|
||||
"warn-start":
|
||||
"L'utilisation du modèle local whisper sur des machines avec une RAM ou un CPU limités peut bloquer AnythingLLM lors du traitement des fichiers multimédias.",
|
||||
"warn-recommend":
|
||||
"Nous recommandons au moins 2 Go de RAM et des fichiers téléchargés <10 Mo.",
|
||||
"warn-end":
|
||||
"Le modèle intégré se téléchargera automatiquement lors de la première utilisation.",
|
||||
},
|
||||
|
||||
embedding: {
|
||||
title: "Préférence d'intégration",
|
||||
"desc-start":
|
||||
"Lorsque vous utilisez un LLM qui ne supporte pas nativement un moteur d'intégration - vous devrez peut-être spécifier en plus des identifiants pour intégrer le texte.",
|
||||
"desc-end":
|
||||
"L'intégration est le processus de transformation du texte en vecteurs. Ces identifiants sont nécessaires pour transformer vos fichiers et invites en un format que AnythingLLM peut utiliser pour traiter.",
|
||||
provider: {
|
||||
title: "Fournisseur d'intégration",
|
||||
description:
|
||||
"Aucune configuration n'est nécessaire lors de l'utilisation du moteur d'intégration natif de AnythingLLM.",
|
||||
},
|
||||
},
|
||||
|
||||
text: {
|
||||
title: "Préférences de division et de découpage du texte",
|
||||
"desc-start":
|
||||
"Parfois, vous voudrez peut-être changer la façon dont les nouveaux documents sont divisés et découpés avant d'être insérés dans votre base de données vectorielle.",
|
||||
"desc-end":
|
||||
"Vous ne devez modifier ce paramètre que si vous comprenez comment fonctionne la division du texte et ses effets secondaires.",
|
||||
"warn-start": "Les changements ici s'appliqueront uniquement aux",
|
||||
"warn-center": "nouveaux documents intégrés",
|
||||
"warn-end": ", pas aux documents existants.",
|
||||
size: {
|
||||
title: "Taille des segments de texte",
|
||||
description:
|
||||
"C'est la longueur maximale de caractères pouvant être présents dans un seul vecteur.",
|
||||
recommend: "Longueur maximale du modèle d'intégration est",
|
||||
},
|
||||
|
||||
overlap: {
|
||||
title: "Chevauchement des segments de texte",
|
||||
description:
|
||||
"C'est le chevauchement maximal de caractères qui se produit pendant le découpage entre deux segments de texte adjacents.",
|
||||
},
|
||||
},
|
||||
|
||||
// Vector Database
|
||||
vector: {
|
||||
title: "Base de données vectorielle",
|
||||
description:
|
||||
"Voici les identifiants et les paramètres de fonctionnement de votre instance AnythingLLM. Il est important que ces clés soient actuelles et correctes.",
|
||||
provider: {
|
||||
title: "Fournisseur de base de données vectorielle",
|
||||
description: "Aucune configuration n'est nécessaire pour LanceDB.",
|
||||
},
|
||||
},
|
||||
|
||||
// Embeddable Chat Widgets
|
||||
embeddable: {
|
||||
title: "Widgets de chat intégrables",
|
||||
description:
|
||||
"Les widgets de chat intégrables sont des interfaces de chat publiques associées à un espace de travail unique. Ils vous permettent de créer des espaces de travail que vous pouvez ensuite publier dans le monde entier.",
|
||||
create: "Créer un widget intégré",
|
||||
table: {
|
||||
workspace: "Espace de travail",
|
||||
chats: "Chats envoyés",
|
||||
Active: "Domaines actifs",
|
||||
},
|
||||
},
|
||||
|
||||
"embed-chats": {
|
||||
title: "Chats intégrés",
|
||||
description:
|
||||
"Voici tous les chats et messages enregistrés de tout widget intégré que vous avez publié.",
|
||||
table: {
|
||||
embed: "Intégration",
|
||||
sender: "Expéditeur",
|
||||
message: "Message",
|
||||
response: "Réponse",
|
||||
at: "Envoyé à",
|
||||
},
|
||||
},
|
||||
|
||||
multi: {
|
||||
title: "Mode multi-utilisateurs",
|
||||
description:
|
||||
"Configurez votre instance pour prendre en charge votre équipe en activant le mode multi-utilisateurs.",
|
||||
enable: {
|
||||
"is-enable": "Le mode multi-utilisateurs est activé",
|
||||
enable: "Activer le mode multi-utilisateurs",
|
||||
description:
|
||||
"Par défaut, vous serez le seul administrateur. En tant qu'administrateur, vous devrez créer des comptes pour tous les nouveaux utilisateurs ou administrateurs. Ne perdez pas votre mot de passe car seul un utilisateur administrateur peut réinitialiser les mots de passe.",
|
||||
username: "Nom d'utilisateur du compte administrateur",
|
||||
password: "Mot de passe du compte administrateur",
|
||||
},
|
||||
password: {
|
||||
title: "Protection par mot de passe",
|
||||
description:
|
||||
"Protégez votre instance AnythingLLM avec un mot de passe. Si vous oubliez ce mot de passe, il n'y a pas de méthode de récupération, donc assurez-vous de le sauvegarder.",
|
||||
},
|
||||
instance: {
|
||||
title: "Protéger l'instance par mot de passe",
|
||||
description:
|
||||
"Par défaut, vous serez le seul administrateur. En tant qu'administrateur, vous devrez créer des comptes pour tous les nouveaux utilisateurs ou administrateurs. Ne perdez pas votre mot de passe car seul un utilisateur administrateur peut réinitialiser les mots de passe.",
|
||||
password: "Mot de passe de l'instance",
|
||||
},
|
||||
},
|
||||
|
||||
// Event Logs
|
||||
event: {
|
||||
title: "Journaux d'événements",
|
||||
description:
|
||||
"Consultez toutes les actions et événements se produisant sur cette instance pour la surveillance.",
|
||||
clear: "Effacer les journaux d'événements",
|
||||
table: {
|
||||
type: "Type d'événement",
|
||||
user: "Utilisateur",
|
||||
occurred: "Survenu à",
|
||||
},
|
||||
},
|
||||
|
||||
// Privacy & Data-Handling
|
||||
privacy: {
|
||||
title: "Confidentialité et gestion des données",
|
||||
description:
|
||||
"Voici votre configuration pour la gestion des données et des fournisseurs tiers connectés avec AnythingLLM.",
|
||||
llm: "Sélection LLM",
|
||||
embedding: "Préférence d'intégration",
|
||||
vector: "Base de données vectorielle",
|
||||
anonymous: "Télémétrie anonyme activée",
|
||||
},
|
||||
};
|
||||
|
||||
export default TRANSLATIONS;
|
40
frontend/src/locales/resources.js
Normal file
40
frontend/src/locales/resources.js
Normal file
@ -0,0 +1,40 @@
|
||||
// Looking for a language to translate AnythingLLM to?
|
||||
// Create a `common.js` file in the language's ISO code https://www.w3.org/International/O-charset-lang.html
|
||||
// eg: Spanish => es/common.js
|
||||
// eg: French => fr/common.js
|
||||
// You should copy the en/common.js file as your template and just translate every string in there.
|
||||
// By default, we try to see what the browsers native language is set to and use that. If a string
|
||||
// is not defined or is null in the translation file, it will fallback to the value in the en/common.js file
|
||||
// RULES:
|
||||
// The EN translation file is the ground-truth for what keys and options are available. DO NOT add a special key
|
||||
// to a specific language file as this will break the other languages. Any new keys should be added to english
|
||||
// and the language file you are working on.
|
||||
|
||||
// Contributor Notice: If you are adding a translation you MUST locally run `yarn verify:translations` from the root prior to PR.
|
||||
// please do not submit PR's without first verifying this test passes as it will tell you about missing keys or values
|
||||
// from the primary dictionary.
|
||||
|
||||
import English from "./en/common.js";
|
||||
import Spanish from "./es/common.js";
|
||||
import French from "./fr/common.js";
|
||||
import Mandarin from "./zh/common.js";
|
||||
import Russian from "./ru/common.js";
|
||||
|
||||
export const defaultNS = "common";
|
||||
export const resources = {
|
||||
en: {
|
||||
common: English,
|
||||
},
|
||||
zh: {
|
||||
common: Mandarin,
|
||||
},
|
||||
es: {
|
||||
common: Spanish,
|
||||
},
|
||||
fr: {
|
||||
common: French,
|
||||
},
|
||||
ru: {
|
||||
common: Russian,
|
||||
},
|
||||
};
|
418
frontend/src/locales/ru/common.js
Normal file
418
frontend/src/locales/ru/common.js
Normal file
@ -0,0 +1,418 @@
|
||||
const TRANSLATIONS = {
|
||||
common: {
|
||||
"workspaces-name": "Имя рабочих пространств",
|
||||
error: "ошибка",
|
||||
success: "успех",
|
||||
user: "Пользователь",
|
||||
selection: "Выбор модели",
|
||||
saving: "Сохранение...",
|
||||
save: "Сохранить изменения",
|
||||
previous: "Предыдущая страница",
|
||||
next: "Следующая страница",
|
||||
},
|
||||
settings: {
|
||||
title: "Настройки экземпляра",
|
||||
system: "Системные настройки",
|
||||
invites: "Приглашение",
|
||||
users: "Пользователи",
|
||||
workspaces: "Рабочие пространства",
|
||||
"workspace-chats": "Чат рабочего пространства",
|
||||
appearance: "Внешний вид",
|
||||
"api-keys": "API ключи",
|
||||
llm: "Предпочтение LLM",
|
||||
transcription: "Модель транскрипции",
|
||||
embedder: "Настройки встраивания",
|
||||
"text-splitting": "Разделение и сегментация текста",
|
||||
"vector-database": "Векторная база данных",
|
||||
embeds: "Виджеты встраивания чата",
|
||||
"embed-chats": "История встраивания чатов",
|
||||
security: "Безопасность",
|
||||
"event-logs": "Журналы событий",
|
||||
privacy: "Конфиденциальность и данные",
|
||||
},
|
||||
login: {
|
||||
"multi-user": {
|
||||
welcome: "Добро пожаловать в",
|
||||
"placeholder-username": "Имя пользователя",
|
||||
"placeholder-password": "Пароль",
|
||||
login: "Войти",
|
||||
validating: "Проверка...",
|
||||
"forgot-pass": "Забыли пароль",
|
||||
reset: "Сбросить",
|
||||
},
|
||||
"sign-in": {
|
||||
start: "Войти в ваш",
|
||||
end: "аккаунт.",
|
||||
},
|
||||
},
|
||||
"workspaces—settings": {
|
||||
general: "Общие настройки",
|
||||
chat: "Настройки чата",
|
||||
vector: "Векторная база данных",
|
||||
members: "Участники",
|
||||
agent: "Конфигурация агента",
|
||||
},
|
||||
general: {
|
||||
vector: {
|
||||
title: "Количество векторов",
|
||||
description: "Общее количество векторов в вашей векторной базе данных.",
|
||||
},
|
||||
names: {
|
||||
description:
|
||||
"Это изменит только отображаемое имя вашего рабочего пространства.",
|
||||
},
|
||||
message: {
|
||||
title: "Предлагаемые сообщения чата",
|
||||
description:
|
||||
"Настройте сообщения, которые будут предложены пользователям вашего рабочего пространства.",
|
||||
add: "Добавить новое сообщение",
|
||||
save: "Сохранить сообщения",
|
||||
heading: "Объясните мне",
|
||||
body: "преимущества AnythingLLM",
|
||||
},
|
||||
pfp: {
|
||||
title: "Изображение профиля помощника",
|
||||
description:
|
||||
"Настройте изображение профиля помощника для этого рабочего пространства.",
|
||||
image: "Изображение рабочего пространства",
|
||||
remove: "Удалить изображение рабочего пространства",
|
||||
},
|
||||
delete: {
|
||||
title: "Удалить Рабочее Пространство",
|
||||
description:
|
||||
"Удалите это рабочее пространство и все его данные. Это удалит рабочее пространство для всех пользователей.",
|
||||
delete: "Удалить рабочее пространство",
|
||||
deleting: "Удаление рабочего пространства...",
|
||||
"confirm-start": "Вы собираетесь удалить весь ваш",
|
||||
"confirm-end":
|
||||
"рабочее пространство. Это удалит все векторные встраивания в вашей векторной базе данных.\n\nОригинальные исходные файлы останутся нетронутыми. Это действие необратимо.",
|
||||
},
|
||||
},
|
||||
chat: {
|
||||
llm: {
|
||||
title: "Поставщик LLM рабочего пространства",
|
||||
description:
|
||||
"Конкретный поставщик и модель LLM, которые будут использоваться для этого рабочего пространства. По умолчанию используется системный поставщик и настройки LLM.",
|
||||
search: "Искать всех поставщиков LLM",
|
||||
},
|
||||
model: {
|
||||
title: "Модель чата рабочего пространства",
|
||||
description:
|
||||
"Конкретная модель чата, которая будет использоваться для этого рабочего пространства. Если пусто, будет использоваться системное предпочтение LLM.",
|
||||
wait: "-- ожидание моделей --",
|
||||
},
|
||||
mode: {
|
||||
title: "Режим чата",
|
||||
chat: {
|
||||
title: "Чат",
|
||||
"desc-start": "будет предоставлять ответы с общей информацией LLM",
|
||||
and: "и",
|
||||
"desc-end": "найденный контекст документов.",
|
||||
},
|
||||
query: {
|
||||
title: "Запрос",
|
||||
"desc-start": "будет предоставлять ответы",
|
||||
only: "только",
|
||||
"desc-end": "если найден контекст документов.",
|
||||
},
|
||||
},
|
||||
history: {
|
||||
title: "История чата",
|
||||
"desc-start":
|
||||
"Количество предыдущих чатов, которые будут включены в краткосрочную память ответа.",
|
||||
recommend: "Рекомендуем 20.",
|
||||
"desc-end":
|
||||
"Любое количество более 45 может привести к непрерывным сбоям чата в зависимости от размера сообщений.",
|
||||
},
|
||||
prompt: {
|
||||
title: "Подсказка",
|
||||
description:
|
||||
"Подсказка, которая будет использоваться в этом рабочем пространстве. Определите контекст и инструкции для AI для создания ответа. Вы должны предоставить тщательно разработанную подсказку, чтобы AI мог генерировать релевантный и точный ответ.",
|
||||
},
|
||||
refusal: {
|
||||
title: "Ответ об отказе в режиме запроса",
|
||||
"desc-start": "В режиме",
|
||||
query: "запроса",
|
||||
"desc-end":
|
||||
"вы можете вернуть пользовательский ответ об отказе, если контекст не найден.",
|
||||
},
|
||||
temperature: {
|
||||
title: "Температура LLM",
|
||||
"desc-start":
|
||||
"Этот параметр контролирует, насколько 'креативными' будут ответы вашего LLM.",
|
||||
"desc-end":
|
||||
"Чем выше число, тем более креативные ответы. Для некоторых моделей это может привести к несвязным ответам при слишком высоких настройках.",
|
||||
hint: "Большинство LLM имеют различные допустимые диапазоны значений. Проконсультируйтесь с вашим поставщиком LLM для получения этой информации.",
|
||||
},
|
||||
},
|
||||
"vector-workspace": {
|
||||
identifier: "Идентификатор векторной базы данных",
|
||||
snippets: {
|
||||
title: "Максимальное количество контекстных фрагментов",
|
||||
description:
|
||||
"Этот параметр контролирует максимальное количество контекстных фрагментов, которые будут отправлены LLM для каждого чата или запроса.",
|
||||
recommend: "Рекомендуемое количество: 4",
|
||||
},
|
||||
doc: {
|
||||
title: "Порог сходства документов",
|
||||
description:
|
||||
"Минимальная оценка сходства, необходимая для того, чтобы источник считался связанным с чатом. Чем выше число, тем более схожим должен быть источник с чатом.",
|
||||
zero: "Без ограничений",
|
||||
low: "Низкий (оценка сходства ≥ .25)",
|
||||
medium: "Средний (оценка сходства ≥ .50)",
|
||||
high: "Высокий (оценка сходства ≥ .75)",
|
||||
},
|
||||
reset: {
|
||||
reset: "Сброс векторной базы данных",
|
||||
resetting: "Очистка векторов...",
|
||||
confirm:
|
||||
"Вы собираетесь сбросить векторную базу данных этого рабочего пространства. Это удалит все текущие векторные встраивания.\n\nОригинальные исходные файлы останутся нетронутыми. Это действие необратимо.",
|
||||
error: "Не удалось сбросить векторную базу данных рабочего пространства!",
|
||||
success: "Векторная база данных рабочего пространства была сброшена!",
|
||||
},
|
||||
},
|
||||
agent: {
|
||||
"performance-warning":
|
||||
"Производительность LLM, не поддерживающих вызовы инструментов, сильно зависит от возможностей и точности модели. Некоторые способности могут быть ограничены или не функционировать.",
|
||||
provider: {
|
||||
title: "Поставщик LLM агента рабочего пространства",
|
||||
description:
|
||||
"Конкретный поставщик и модель LLM, которые будут использоваться для агента @agent этого рабочего пространства.",
|
||||
},
|
||||
mode: {
|
||||
chat: {
|
||||
title: "Модель чата агента рабочего пространства",
|
||||
description:
|
||||
"Конкретная модель чата, которая будет использоваться для агента @agent этого рабочего пространства.",
|
||||
},
|
||||
title: "Модель агента рабочего пространства",
|
||||
description:
|
||||
"Конкретная модель LLM, которая будет использоваться для агента @agent этого рабочего пространства.",
|
||||
wait: "-- ожидание моделей --",
|
||||
},
|
||||
skill: {
|
||||
title: "Навыки агента по умолчанию",
|
||||
description:
|
||||
"Улучшите естественные способности агента по умолчанию с помощью этих предустановленных навыков. Эта настройка применяется ко всем рабочим пространствам.",
|
||||
rag: {
|
||||
title: "RAG и долговременная память",
|
||||
description:
|
||||
"Позвольте агенту использовать ваши локальные документы для ответа на запрос или попросите агента 'запомнить' части контента для долгосрочного извлечения из памяти.",
|
||||
},
|
||||
view: {
|
||||
title: "Просмотр и резюмирование документов",
|
||||
description:
|
||||
"Позвольте агенту перечислять и резюмировать содержание файлов рабочего пространства, которые в данный момент встроены.",
|
||||
},
|
||||
scrape: {
|
||||
title: "Сбор данных с веб-сайтов",
|
||||
description:
|
||||
"Позвольте агенту посещать и собирать содержимое веб-сайтов.",
|
||||
},
|
||||
generate: {
|
||||
title: "Создание диаграмм",
|
||||
description:
|
||||
"Включите возможность создания различных типов диаграмм из предоставленных данных или данных, указанных в чате.",
|
||||
},
|
||||
save: {
|
||||
title: "Создание и сохранение файлов в браузер",
|
||||
description:
|
||||
"Включите возможность создания и записи файлов, которые можно сохранить и загрузить в вашем браузере.",
|
||||
},
|
||||
web: {
|
||||
title: "Поиск в Интернете и просмотр в реальном времени",
|
||||
"desc-start":
|
||||
"Позвольте вашему агенту искать в Интернете для ответа на ваши вопросы, подключаясь к поставщику поиска (SERP).",
|
||||
"desc-end":
|
||||
"Поиск в Интернете во время сессий агента не будет работать, пока это не настроено.",
|
||||
},
|
||||
},
|
||||
},
|
||||
recorded: {
|
||||
title: "Чаты рабочего пространства",
|
||||
description:
|
||||
"Это все записанные чаты и сообщения, отправленные пользователями, упорядоченные по дате создания.",
|
||||
export: "Экспорт",
|
||||
table: {
|
||||
id: "Идентификатор",
|
||||
by: "Отправлено",
|
||||
workspace: "Рабочее пространство",
|
||||
prompt: "Подсказка",
|
||||
response: "Ответ",
|
||||
at: "Отправлено в",
|
||||
},
|
||||
},
|
||||
appearance: {
|
||||
title: "Внешний вид",
|
||||
description: "Настройте параметры внешнего вида вашей платформы.",
|
||||
logo: {
|
||||
title: "Настроить логотип",
|
||||
description:
|
||||
"Загрузите свой логотип, чтобы персонализировать ваш чат-бот.",
|
||||
add: "Добавить пользовательский логотип",
|
||||
recommended: "Рекомендуемый размер: 800 x 200",
|
||||
remove: "Удалить",
|
||||
replace: "Заменить",
|
||||
},
|
||||
message: {
|
||||
title: "Настроить сообщения",
|
||||
description:
|
||||
"Настройте автоматические сообщения, отображаемые вашим пользователям.",
|
||||
new: "Новое",
|
||||
system: "система",
|
||||
user: "пользователь",
|
||||
message: "сообщение",
|
||||
assistant: "Чат-ассистент AnythingLLM",
|
||||
"double-click": "Дважды щелкните, чтобы редактировать...",
|
||||
save: "Сохранить сообщения",
|
||||
},
|
||||
icons: {
|
||||
title: "Пользовательские иконки в подвале",
|
||||
description:
|
||||
"Настройте иконки в подвале, отображаемые внизу боковой панели.",
|
||||
icon: "Иконка",
|
||||
link: "Ссылка",
|
||||
},
|
||||
},
|
||||
api: {
|
||||
title: "API ключи",
|
||||
description:
|
||||
"API ключи позволяют владельцу программно получать доступ к этому экземпляру AnythingLLM и управлять им.",
|
||||
link: "Прочитать документацию по API",
|
||||
generate: "Создать новый API ключ",
|
||||
table: {
|
||||
key: "API ключ",
|
||||
by: "Создано",
|
||||
created: "Создано",
|
||||
},
|
||||
},
|
||||
llm: {
|
||||
title: "Предпочтение LLM",
|
||||
description:
|
||||
"Это учетные данные и настройки для вашего предпочтительного поставщика чата и встраивания LLM. Важно, чтобы эти ключи были актуальными и правильными, иначе AnythingLLM не будет работать должным образом.",
|
||||
provider: "Поставщик LLM",
|
||||
},
|
||||
transcription: {
|
||||
title: "Предпочтение модели транскрипции",
|
||||
description:
|
||||
"Это учетные данные и настройки для вашего предпочтительного поставщика моделей транскрипции. Важно, чтобы эти ключи были актуальными и правильными, иначе медиафайлы и аудио не будут транскрибироваться.",
|
||||
provider: "Поставщик транскрипции",
|
||||
"warn-start":
|
||||
"Использование локальной модели whisper на машинах с ограниченной оперативной памятью или процессором может привести к зависанию AnythingLLM при обработке медиафайлов.",
|
||||
"warn-recommend":
|
||||
"Мы рекомендуем минимум 2ГБ оперативной памяти и загружать файлы <10МБ.",
|
||||
"warn-end":
|
||||
"Встроенная модель будет автоматически загружена при первом использовании.",
|
||||
},
|
||||
embedding: {
|
||||
title: "Настройки встраивания",
|
||||
"desc-start":
|
||||
"При использовании LLM, который не поддерживает встроенный механизм встраивания - возможно, потребуется дополнительно указать учетные данные для встраивания текста.",
|
||||
"desc-end":
|
||||
"Встраивание - это процесс превращения текста в векторы. Эти учетные данные необходимы для превращения ваших файлов и подсказок в формат, который AnythingLLM может использовать для обработки.",
|
||||
provider: {
|
||||
title: "Поставщик встраивания",
|
||||
description:
|
||||
"Нет необходимости в настройке при использовании встроенного механизма встраивания AnythingLLM.",
|
||||
},
|
||||
},
|
||||
text: {
|
||||
title: "Настройки разделения и сегментации текста",
|
||||
"desc-start":
|
||||
"Иногда может понадобиться изменить стандартный способ разделения и сегментации новых документов перед их вставкой в векторную базу данных.",
|
||||
"desc-end":
|
||||
"Следует изменять этот параметр только при полном понимании работы разделения текста и его побочных эффектов.",
|
||||
"warn-start": "Изменения здесь будут применяться только к",
|
||||
"warn-center": "новым встроенным документам",
|
||||
"warn-end": ", а не к существующим документам.",
|
||||
size: {
|
||||
title: "Размер сегмента текста",
|
||||
description:
|
||||
"Это максимальная длина символов, которые могут присутствовать в одном векторе.",
|
||||
recommend: "Максимальная длина модели встраивания составляет",
|
||||
},
|
||||
overlap: {
|
||||
title: "Перекрытие сегментов текста",
|
||||
description:
|
||||
"Это максимальное перекрытие символов, которое происходит при сегментации между двумя смежными сегментами текста.",
|
||||
},
|
||||
},
|
||||
vector: {
|
||||
title: "Векторная база данных",
|
||||
description:
|
||||
"Это учетные данные и настройки для того, как будет функционировать ваш экземпляр AnythingLLM. Важно, чтобы эти ключи были актуальными и правильными.",
|
||||
provider: {
|
||||
title: "Поставщик векторной базы данных",
|
||||
description: "Настройка для LanceDB не требуется.",
|
||||
},
|
||||
},
|
||||
embeddable: {
|
||||
title: "Встраиваемые виджеты чата",
|
||||
description:
|
||||
"Встраиваемые виджеты чата - это интерфейсы чата, ориентированные на публичное использование и привязанные к одному рабочему пространству. Они позволяют создавать рабочие пространства, которые затем можно публиковать в Интернете.",
|
||||
create: "Создать встраивание",
|
||||
table: {
|
||||
workspace: "Рабочее пространство",
|
||||
chats: "Отправленные чаты",
|
||||
Active: "Активные домены",
|
||||
},
|
||||
},
|
||||
"embed-chats": {
|
||||
title: "Встраивание чатов",
|
||||
description:
|
||||
"Это все записанные чаты и сообщения от любого встраивания, которое вы опубликовали.",
|
||||
table: {
|
||||
embed: "Встраивание",
|
||||
sender: "Отправитель",
|
||||
message: "Сообщение",
|
||||
response: "Ответ",
|
||||
at: "Отправлено в",
|
||||
},
|
||||
},
|
||||
multi: {
|
||||
title: "Многопользовательский режим",
|
||||
description:
|
||||
"Настройте ваш экземпляр для поддержки вашей команды, активировав многопользовательский режим.",
|
||||
enable: {
|
||||
"is-enable": "Многопользовательский режим включен",
|
||||
enable: "Включить многопользовательский режим",
|
||||
description:
|
||||
"По умолчанию, вы будете единственным администратором. Как администратор, вы должны будете создавать учетные записи для всех новых пользователей или администраторов. Не теряйте ваш пароль, так как только администратор может сбросить пароли.",
|
||||
username: "Имя пользователя учетной записи администратора",
|
||||
password: "Пароль учетной записи администратора",
|
||||
},
|
||||
password: {
|
||||
title: "Защита паролем",
|
||||
description:
|
||||
"Защитите ваш экземпляр AnythingLLM паролем. Если вы забудете его, метода восстановления не существует, поэтому убедитесь, что вы сохранили этот пароль.",
|
||||
},
|
||||
instance: {
|
||||
title: "Защитить экземпляр паролем",
|
||||
description:
|
||||
"По умолчанию, вы будете единственным администратором. Как администратор, вы должны будете создавать учетные записи для всех новых пользователей или администраторов. Не теряйте ваш пароль, так как только администратор может сбросить пароли.",
|
||||
password: "Пароль экземпляра",
|
||||
},
|
||||
},
|
||||
event: {
|
||||
title: "Журналы событий",
|
||||
description:
|
||||
"Просматривайте все действия и события, происходящие в этом экземпляре для мониторинга.",
|
||||
clear: "Очистить журналы событий",
|
||||
table: {
|
||||
type: "Тип события",
|
||||
user: "Пользователь",
|
||||
occurred: "Произошло в",
|
||||
},
|
||||
},
|
||||
privacy: {
|
||||
title: "Конфиденциальность и обработка данных",
|
||||
description:
|
||||
"Это ваша конфигурация для того, как подключенные сторонние поставщики и AnythingLLM обрабатывают ваши данные.",
|
||||
llm: "Выбор LLM",
|
||||
embedding: "Предпочтение встраивания",
|
||||
vector: "Векторная база данных",
|
||||
anonymous: "Анонимная телеметрия включена",
|
||||
},
|
||||
};
|
||||
|
||||
export default TRANSLATIONS;
|
100
frontend/src/locales/verifyTranslations.mjs
Normal file
100
frontend/src/locales/verifyTranslations.mjs
Normal file
@ -0,0 +1,100 @@
|
||||
import { resources } from "./resources.js";
|
||||
const languageNames = new Intl.DisplayNames(Object.keys(resources), {
|
||||
type: "language",
|
||||
});
|
||||
|
||||
function langDisplayName(lang) {
|
||||
return languageNames.of(lang);
|
||||
}
|
||||
|
||||
function compareStructures(lang, a, b, subdir = null) {
|
||||
//if a and b aren't the same type, they can't be equal
|
||||
if (typeof a !== typeof b && a !== null && b !== null) {
|
||||
console.log("Invalid type comparison", [
|
||||
{
|
||||
lang,
|
||||
a: typeof a,
|
||||
b: typeof b,
|
||||
values: {
|
||||
a,
|
||||
b,
|
||||
},
|
||||
...(!!subdir ? { subdir } : {}),
|
||||
},
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Need the truthy guard because
|
||||
// typeof null === 'object'
|
||||
if (a && typeof a === "object") {
|
||||
var keysA = Object.keys(a).sort(),
|
||||
keysB = Object.keys(b).sort();
|
||||
|
||||
//if a and b are objects with different no of keys, unequal
|
||||
if (keysA.length !== keysB.length) {
|
||||
console.log("Keys are missing!", {
|
||||
[lang]: keysA,
|
||||
en: keysB,
|
||||
...(!!subdir ? { subdir } : {}),
|
||||
diff: {
|
||||
added: keysB.filter((key) => !keysA.includes(key)),
|
||||
removed: keysA.filter((key) => !keysB.includes(key)),
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
//if keys aren't all the same, unequal
|
||||
if (
|
||||
!keysA.every(function (k, i) {
|
||||
return k === keysB[i];
|
||||
})
|
||||
) {
|
||||
console.log("Keys are not equal!", {
|
||||
[lang]: keysA,
|
||||
en: keysB,
|
||||
...(!!subdir ? { subdir } : {}),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
//recurse on the values for each key
|
||||
return keysA.every(function (key) {
|
||||
//if we made it here, they have identical keys
|
||||
return compareStructures(lang, a[key], b[key], key);
|
||||
});
|
||||
|
||||
//for primitives just ignore since we don't check values.
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const failed = [];
|
||||
const TRANSLATIONS = {};
|
||||
for (const [lang, { common }] of Object.entries(resources))
|
||||
TRANSLATIONS[lang] = common;
|
||||
const PRIMARY = { ...TRANSLATIONS["en"] };
|
||||
delete TRANSLATIONS["en"];
|
||||
|
||||
console.log(
|
||||
`The following translation files will be verified: [${Object.keys(
|
||||
TRANSLATIONS
|
||||
).join(",")}]`
|
||||
);
|
||||
for (const [lang, translations] of Object.entries(TRANSLATIONS)) {
|
||||
const passed = compareStructures(lang, translations, PRIMARY);
|
||||
console.log(`${langDisplayName(lang)} (${lang}): ${passed ? "✅" : "❌"}`);
|
||||
!passed && failed.push(lang);
|
||||
}
|
||||
|
||||
if (failed.length !== 0)
|
||||
throw new Error(
|
||||
`The following translations files are INVALID and need fixing. Please see logs`,
|
||||
failed
|
||||
);
|
||||
console.log(
|
||||
`👍 All translation files located match the schema defined by the English file!`
|
||||
);
|
||||
process.exit(0);
|
426
frontend/src/locales/zh/common.js
Normal file
426
frontend/src/locales/zh/common.js
Normal file
@ -0,0 +1,426 @@
|
||||
// Anything with "null" requires a translation. Contribute to translation via a PR!
|
||||
const TRANSLATIONS = {
|
||||
common: {
|
||||
"workspaces-name": "工作区名称",
|
||||
error: "错误",
|
||||
success: "成功",
|
||||
user: "用户",
|
||||
selection: "模型选择",
|
||||
save: "保存更改",
|
||||
saving: "保存中...",
|
||||
previous: "上一页",
|
||||
next: "下一页",
|
||||
},
|
||||
|
||||
// Setting Sidebar menu items.
|
||||
settings: {
|
||||
title: "设置",
|
||||
system: "系统",
|
||||
invites: "邀请",
|
||||
users: "用户",
|
||||
workspaces: "工作区",
|
||||
"workspace-chats": "对话历史记录", // "workspace-chats" should be "对话历史记录", means "chat history",or "chat history records"
|
||||
appearance: "外观",
|
||||
"api-keys": "API 密钥",
|
||||
llm: "LLM 首选项",
|
||||
transcription: "Transcription 模型",
|
||||
embedder: "Embedder 首选项",
|
||||
"text-splitting": "文本分割",
|
||||
"vector-database": "向量数据库",
|
||||
embeds: "嵌入式对话",
|
||||
"embed-chats": "嵌入式对话历史",
|
||||
security: "用户与安全",
|
||||
"event-logs": "事件日志",
|
||||
privacy: "隐私与数据",
|
||||
},
|
||||
|
||||
// Page Definitions
|
||||
login: {
|
||||
"multi-user": {
|
||||
welcome: "欢迎!",
|
||||
"placeholder-username": "请输入用户名",
|
||||
"placeholder-password": "请输入密码",
|
||||
login: "登录",
|
||||
validating: "登录",
|
||||
"forgot-pass": "忘记密码",
|
||||
reset: "重置",
|
||||
},
|
||||
"sign-in": {
|
||||
start: "登录你的",
|
||||
end: "账户",
|
||||
},
|
||||
},
|
||||
|
||||
// Workspace Settings menu items
|
||||
"workspaces—settings": {
|
||||
general: "通用设置",
|
||||
chat: "聊天设置",
|
||||
vector: "向量数据库",
|
||||
members: "成员",
|
||||
agent: "代理配置",
|
||||
},
|
||||
|
||||
// General Appearance
|
||||
general: {
|
||||
vector: {
|
||||
title: "向量数量",
|
||||
description: "向量数据库中的总向量数。",
|
||||
},
|
||||
names: {
|
||||
description: "这只会更改工作区的显示名称。",
|
||||
},
|
||||
message: {
|
||||
title: "建议的聊天消息",
|
||||
description: "自定义将向您的工作区用户建议的消息。",
|
||||
add: "添加新消息",
|
||||
save: "保存消息",
|
||||
heading: "向我解释",
|
||||
body: "AnythingLLM的好处",
|
||||
},
|
||||
pfp: {
|
||||
title: "助理头像",
|
||||
description: "为此工作区自定义助手的个人资料图像。",
|
||||
image: "工作区图像",
|
||||
remove: "移除工作区图像",
|
||||
},
|
||||
delete: {
|
||||
title: "删除工作区",
|
||||
description: "删除此工作区及其所有数据。这将删除所有用户的工作区。",
|
||||
delete: "删除工作区",
|
||||
deleting: "正在删除工作区...",
|
||||
"confirm-start": "您即将删除整个",
|
||||
"confirm-end":
|
||||
"工作区。这将删除矢量数据库中的所有矢量嵌入。\n\n原始源文件将保持不变。此操作是不可逆转的。",
|
||||
},
|
||||
},
|
||||
|
||||
// Chat Settings
|
||||
chat: {
|
||||
llm: {
|
||||
title: "工作区LLM提供者",
|
||||
description:
|
||||
"将用于此工作区的特定 LLM 提供商和模型。默认情况下,它使用系统 LLM 提供程序和设置。",
|
||||
search: "搜索所有 LLM 提供商",
|
||||
},
|
||||
model: {
|
||||
title: "工作区聊天模型",
|
||||
description:
|
||||
"将用于此工作区的特定聊天模型。如果为空,将使用系统LLM首选项。",
|
||||
wait: "-- 等待模型 --",
|
||||
},
|
||||
mode: {
|
||||
title: "聊天模式",
|
||||
chat: {
|
||||
title: "聊天",
|
||||
"desc-start": "将提供法学硕士的一般知识",
|
||||
and: "和",
|
||||
"desc-end": "找到的文档上下文的答案。",
|
||||
},
|
||||
query: {
|
||||
title: "查询",
|
||||
"desc-start": "将",
|
||||
only: "仅",
|
||||
"desc-end": "提供找到的文档上下文的答案。",
|
||||
},
|
||||
},
|
||||
history: {
|
||||
title: "聊天历史记录",
|
||||
"desc-start": "将包含在响应的短期记忆中的先前聊天的数量。",
|
||||
recommend: "推荐 20。",
|
||||
"desc-end":
|
||||
"任何超过 45 的值都可能导致连续聊天失败,具体取决于消息大小。",
|
||||
},
|
||||
prompt: {
|
||||
title: "聊天提示",
|
||||
description:
|
||||
"将在此工作区上使用的提示。定义 AI 生成响应的上下文和指令。您应该提供精心设计的提示,以便人工智能可以生成相关且准确的响应。",
|
||||
},
|
||||
refusal: {
|
||||
title: "查询模式拒绝响应",
|
||||
"desc-start": "当处于",
|
||||
query: "查询",
|
||||
"desc-end": "模式时,当未找到上下文时,您可能希望返回自定义拒绝响应。",
|
||||
},
|
||||
temperature: {
|
||||
title: "LLM Temperature",
|
||||
"desc-start": "此设置控制您的 LLM 回答的“创意”程度",
|
||||
"desc-end":
|
||||
"数字越高越有创意。对于某些模型,如果设置得太高,可能会导致响应不一致。",
|
||||
hint: "大多数法学硕士都有各种可接受的有效值范围。请咨询您的法学硕士提供商以获取该信息。",
|
||||
},
|
||||
},
|
||||
|
||||
// Vector Database Settings
|
||||
"vector-workspace": {
|
||||
identifier: "向量数据库标识符",
|
||||
snippets: {
|
||||
title: "最大上下文片段",
|
||||
description:
|
||||
"此设置控制每次聊天或查询将发送到 LLM 的上下文片段的最大数量。",
|
||||
recommend: "推荐: 4",
|
||||
},
|
||||
doc: {
|
||||
title: "文档相似性阈值",
|
||||
description:
|
||||
"源被视为与聊天相关所需的最低相似度分数。数字越高,来源与聊天就越相似。",
|
||||
zero: "无限制",
|
||||
low: "低(相似度分数 ≥ .25)",
|
||||
medium: "中(相似度分数 ≥ .50)",
|
||||
high: "高(相似度分数 ≥ .75)",
|
||||
},
|
||||
reset: {
|
||||
reset: "重置向量数据库",
|
||||
resetting: "清除向量...",
|
||||
confirm:
|
||||
"您将重置此工作区的矢量数据库。这将删除当前嵌入的所有矢量嵌入。\n\n原始源文件将保持不变。此操作是不可逆转的。",
|
||||
success: "向量数据库已重置。",
|
||||
error: "无法重置工作区向量数据库!",
|
||||
},
|
||||
},
|
||||
|
||||
// Agent Configuration
|
||||
agent: {
|
||||
"performance-warning":
|
||||
"不明确支持工具调用的 LLMs 的性能高度依赖于模型的功能和准确性。有些能力可能受到限制或不起作用。",
|
||||
provider: {
|
||||
title: "工作区代理 LLM 提供商",
|
||||
description: "将用于此工作区的 @agent 代理的特定 LLM 提供商和模型。",
|
||||
},
|
||||
mode: {
|
||||
chat: {
|
||||
title: "工作区代理聊天模型",
|
||||
description: "将用于此工作区的 @agent 代理的特定聊天模型。",
|
||||
},
|
||||
title: "工作区代理模型",
|
||||
description: "将用于此工作区的 @agent 代理的特定 LLM 模型。",
|
||||
wait: "-- 等待模型 --",
|
||||
},
|
||||
skill: {
|
||||
title: "默认代理技能",
|
||||
description:
|
||||
"使用这些预构建的技能提高默认代理的自然能力。此设置适用于所有工作区。",
|
||||
rag: {
|
||||
title: "RAG和长期记忆",
|
||||
description:
|
||||
'允许代理利用您的本地文档来回答查询,或要求代理"记住"长期记忆检索的内容片段。',
|
||||
},
|
||||
view: {
|
||||
title: "查看和总结文档",
|
||||
description: "允许代理列出和总结当前嵌入的工作区文件的内容。",
|
||||
},
|
||||
scrape: {
|
||||
title: "抓取网站",
|
||||
description: "允许代理访问和抓取网站的内容。",
|
||||
},
|
||||
generate: {
|
||||
title: "生成图表",
|
||||
description: "使默认代理能够从提供的数据或聊天中生成各种类型的图表。",
|
||||
},
|
||||
save: {
|
||||
title: "生成并保存文件到浏览器",
|
||||
description:
|
||||
"使默认代理能够生成并写入文件,这些文件可以保存并在您的浏览器中下载。",
|
||||
},
|
||||
web: {
|
||||
title: "实时网络搜索和浏览",
|
||||
"desc-start":
|
||||
"通过连接到网络搜索(SERP)提供者,使您的代理能够搜索网络以回答您的问题。",
|
||||
"desc-end": "在代理会话期间,网络搜索将不起作用,直到此设置完成。",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Workspace Chat
|
||||
recorded: {
|
||||
title: "工作区聊天历史记录",
|
||||
description: "这些是用户发送的所有聊天记录和消息,按创建日期排序。",
|
||||
export: "导出",
|
||||
table: {
|
||||
id: "Id",
|
||||
by: "Sent By",
|
||||
workspace: "Workspace",
|
||||
prompt: "Prompt",
|
||||
response: "Response",
|
||||
at: "Sent At",
|
||||
},
|
||||
},
|
||||
|
||||
appearance: {
|
||||
title: "外观",
|
||||
description: "自定义平台的外观设置。",
|
||||
logo: {
|
||||
title: "自定义图标",
|
||||
description: "上传您的自定义图标,让您的聊天机器人成为您的。",
|
||||
add: "添加自定义图标",
|
||||
recommended: "建议尺寸:800 x 200",
|
||||
remove: "移除",
|
||||
replace: "替换",
|
||||
},
|
||||
message: {
|
||||
title: "自定义消息",
|
||||
description: "自定义向用户显示的自动消息。",
|
||||
new: "新建",
|
||||
system: "系统",
|
||||
user: "用户",
|
||||
message: "消息",
|
||||
assistant: "AnythingLLM 聊天助手",
|
||||
"double-click": "双击以编辑...",
|
||||
save: "保存消息",
|
||||
},
|
||||
icons: {
|
||||
title: "自定义页脚图标",
|
||||
description: "自定义侧边栏底部显示的页脚图标。",
|
||||
icon: "图标",
|
||||
link: "链接",
|
||||
},
|
||||
},
|
||||
|
||||
// API Keys
|
||||
api: {
|
||||
title: "API 密钥",
|
||||
description: "API 密钥允许持有者以编程方式访问和管理此 AnythingLLM 实例。",
|
||||
link: "阅读 API 文档",
|
||||
generate: "生成新的 API 密钥",
|
||||
table: {
|
||||
key: "API 密钥",
|
||||
by: "创建者",
|
||||
created: "创建",
|
||||
},
|
||||
},
|
||||
|
||||
// LLM Preferences
|
||||
llm: {
|
||||
title: "LLM 偏好",
|
||||
description:
|
||||
"这些是您首选的 LLM 聊天和嵌入提供商的凭据和设置。重要的是,这些密钥是最新的和正确的,否则 AnythingLLM 将无法正常运行。",
|
||||
provider: "LLM 提供商",
|
||||
},
|
||||
|
||||
transcription: {
|
||||
title: "转录模型偏好",
|
||||
description:
|
||||
"这些是您的首选转录模型提供商的凭据和设置。重要的是这些密钥是最新且正确的,否则媒体文件和音频将无法转录。",
|
||||
provider: "转录提供商",
|
||||
"warn-start":
|
||||
"在 RAM 或 CPU 有限的计算机上使用本地耳语模型可能会在处理媒体文件时停止 AnythingLLM。",
|
||||
"warn-recommend": "我们建议至少 2GB RAM 并上传 <10Mb 的文件。",
|
||||
"warn-end": "内置模型将在首次使用时自动下载。",
|
||||
},
|
||||
|
||||
embedding: {
|
||||
title: "嵌入首选项",
|
||||
"desc-start":
|
||||
"当使用本身不支持嵌入引擎的 LLM 时,您可能需要额外指定用于嵌入文本的凭据。",
|
||||
"desc-end":
|
||||
"嵌入是将文本转换为矢量的过程。需要这些凭据才能将您的文件和提示转换为 AnythingLLM 可以用来处理的格式。",
|
||||
provider: {
|
||||
title: "嵌入引擎提供商",
|
||||
description: "使用 AnythingLLM 的本机嵌入引擎时不需要设置。",
|
||||
},
|
||||
},
|
||||
|
||||
text: {
|
||||
title: "文本拆分和分块首选项",
|
||||
"desc-start":
|
||||
"有时,您可能希望更改新文档在插入到矢量数据库之前拆分和分块的默认方式。",
|
||||
"desc-end": "只有在了解文本拆分的工作原理及其副作用时,才应修改此设置。",
|
||||
"warn-start": "此处的更改仅适用于",
|
||||
"warn-center": "新嵌入的文档",
|
||||
"warn-end": ",而不是现有文档。",
|
||||
size: {
|
||||
title: "文本块大小",
|
||||
description: "这是单个向量中可以存在的字符的最大长度。",
|
||||
recommend: "嵌入模型的最大长度为",
|
||||
},
|
||||
overlap: {
|
||||
title: "文本块重叠",
|
||||
description: "这是在两个相邻文本块之间分块期间发生的最大字符重叠。",
|
||||
},
|
||||
},
|
||||
|
||||
// Vector Database
|
||||
vector: {
|
||||
title: "向量数据库",
|
||||
description:
|
||||
"这些是 AnythingLLM 实例如何运行的凭据和设置。重要的是,这些密钥是最新的和正确的。",
|
||||
provider: {
|
||||
title: "向量数据库提供商",
|
||||
description: "LanceDB 不需要任何配置。",
|
||||
},
|
||||
},
|
||||
|
||||
// Embeddable Chats
|
||||
embeddable: {
|
||||
title: "可嵌入的聊天小部件",
|
||||
description:
|
||||
"可嵌入的聊天小部件是与单个工作区绑定的面向公众的聊天界面。这些允许您构建工作区,然后您可以将其发布到全世界。",
|
||||
create: "创建嵌入式对话",
|
||||
table: {
|
||||
workspace: "工作区",
|
||||
chats: "已发送聊天",
|
||||
Active: "活动域",
|
||||
},
|
||||
},
|
||||
|
||||
// Embeddable Chat History
|
||||
"embed-chats": {
|
||||
title: "嵌入聊天",
|
||||
description: "这些是您发布的任何嵌入的所有记录的聊天和消息。",
|
||||
table: {
|
||||
embed: "嵌入",
|
||||
sender: "发送者",
|
||||
message: "消息",
|
||||
response: "响应",
|
||||
at: "发送于",
|
||||
},
|
||||
},
|
||||
|
||||
multi: {
|
||||
title: "多用户模式",
|
||||
description: "通过激活多用户模式来设置您的实例以支持您的团队。",
|
||||
enable: {
|
||||
"is-enable": "多用户模式已启用",
|
||||
enable: "启用多用户模式",
|
||||
description:
|
||||
"默认情况下,您将是唯一的管理员。作为管理员,您需要为所有新用户或管理员创建账户。不要丢失您的密码,因为只有管理员用户可以重置密码。",
|
||||
username: "管理员账户用户名",
|
||||
password: "管理员账户密码",
|
||||
},
|
||||
password: {
|
||||
title: "密码保护",
|
||||
description:
|
||||
"用密码保护您的AnythingLLM实例。如果您忘记了密码,那么没有恢复方法,所以请确保保存这个密码。",
|
||||
},
|
||||
instance: {
|
||||
title: "实例密码保护",
|
||||
description:
|
||||
"默认情况下,您将是唯一的管理员。作为管理员,您需要为所有新用户或管理员创建账户。不要丢失您的密码,因为只有管理员用户可以重置密码。",
|
||||
password: "实例密码",
|
||||
},
|
||||
},
|
||||
|
||||
// Event Logs
|
||||
event: {
|
||||
title: "事件日志",
|
||||
description: "查看此实例上发生的所有操作和事件以进行监控。",
|
||||
clear: "清除事件日志",
|
||||
table: {
|
||||
type: "事件类型",
|
||||
user: "用户",
|
||||
occurred: "发生时间",
|
||||
},
|
||||
},
|
||||
|
||||
// Privacy & Data-Handling
|
||||
privacy: {
|
||||
title: "隐私和数据处理",
|
||||
description:
|
||||
"这是您对如何处理连接的第三方提供商和AnythingLLM的数据的配置。",
|
||||
llm: "LLM选择",
|
||||
embedding: "嵌入偏好",
|
||||
vector: "向量数据库",
|
||||
anonymous: "启用匿名遥测",
|
||||
},
|
||||
};
|
||||
|
||||
export default TRANSLATIONS;
|
BIN
frontend/src/media/agents/generate-charts.png
Normal file
BIN
frontend/src/media/agents/generate-charts.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 169 KiB |
BIN
frontend/src/media/agents/generate-save-files.png
Normal file
BIN
frontend/src/media/agents/generate-save-files.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 172 KiB |
BIN
frontend/src/media/agents/rag-memory.png
Normal file
BIN
frontend/src/media/agents/rag-memory.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 171 KiB |
BIN
frontend/src/media/agents/scrape-websites.png
Normal file
BIN
frontend/src/media/agents/scrape-websites.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 171 KiB |
BIN
frontend/src/media/agents/sql-agent.png
Normal file
BIN
frontend/src/media/agents/sql-agent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 171 KiB |
BIN
frontend/src/media/agents/view-summarize.png
Normal file
BIN
frontend/src/media/agents/view-summarize.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 170 KiB |
59
frontend/src/models/experimental/liveSync.js
Normal file
59
frontend/src/models/experimental/liveSync.js
Normal file
@ -0,0 +1,59 @@
|
||||
import { API_BASE } from "@/utils/constants";
|
||||
import { baseHeaders } from "@/utils/request";
|
||||
|
||||
const LiveDocumentSync = {
|
||||
featureFlag: "experimental_live_file_sync",
|
||||
toggleFeature: async function (updatedStatus = false) {
|
||||
return await fetch(`${API_BASE}/experimental/toggle-live-sync`, {
|
||||
method: "POST",
|
||||
headers: baseHeaders(),
|
||||
body: JSON.stringify({ updatedStatus }),
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error("Could not update status.");
|
||||
return true;
|
||||
})
|
||||
.then((res) => res)
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
queues: async function () {
|
||||
return await fetch(`${API_BASE}/experimental/live-sync/queues`, {
|
||||
headers: baseHeaders(),
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error("Could not update status.");
|
||||
return res.json();
|
||||
})
|
||||
.then((res) => res?.queues || [])
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
return [];
|
||||
});
|
||||
},
|
||||
|
||||
// Should be in Workspaces but is here for now while in preview
|
||||
setWatchStatusForDocument: async function (slug, docPath, watchStatus) {
|
||||
return fetch(`${API_BASE}/workspace/${slug}/update-watch-status`, {
|
||||
method: "POST",
|
||||
headers: baseHeaders(),
|
||||
body: JSON.stringify({ docPath, watchStatus }),
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
res.statusText || "Error setting watch status for document."
|
||||
);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default LiveDocumentSync;
|
@ -1,6 +1,7 @@
|
||||
import { API_BASE, AUTH_TIMESTAMP, fullApiUrl } from "@/utils/constants";
|
||||
import { baseHeaders, safeJsonParse } from "@/utils/request";
|
||||
import DataConnector from "./dataConnector";
|
||||
import LiveDocumentSync from "./experimental/liveSync";
|
||||
|
||||
const System = {
|
||||
cacheKeys: {
|
||||
@ -419,22 +420,6 @@ const System = {
|
||||
return { success: false, error: e.message };
|
||||
});
|
||||
},
|
||||
getCanDeleteWorkspaces: async function () {
|
||||
return await fetch(`${API_BASE}/system/can-delete-workspaces`, {
|
||||
method: "GET",
|
||||
cache: "no-cache",
|
||||
headers: baseHeaders(),
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error("Could not fetch can delete workspaces.");
|
||||
return res.json();
|
||||
})
|
||||
.then((res) => res?.canDelete)
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
getWelcomeMessages: async function () {
|
||||
return await fetch(`${API_BASE}/system/welcome-messages`, {
|
||||
method: "GET",
|
||||
@ -511,10 +496,23 @@ const System = {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
customModels: async function (provider, apiKey = null, basePath = null) {
|
||||
customModels: async function (
|
||||
provider,
|
||||
apiKey = null,
|
||||
basePath = null,
|
||||
timeout = null
|
||||
) {
|
||||
const controller = new AbortController();
|
||||
if (!!timeout) {
|
||||
setTimeout(() => {
|
||||
controller.abort("Request timed out.");
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
return fetch(`${API_BASE}/system/custom-models`, {
|
||||
method: "POST",
|
||||
headers: baseHeaders(),
|
||||
signal: controller.signal,
|
||||
body: JSON.stringify({
|
||||
provider,
|
||||
apiKey,
|
||||
@ -675,6 +673,9 @@ const System = {
|
||||
return false;
|
||||
});
|
||||
},
|
||||
experimentalFeatures: {
|
||||
liveSync: LiveDocumentSync,
|
||||
},
|
||||
};
|
||||
|
||||
export default System;
|
||||
|
25
frontend/src/pages/Admin/Agents/Badges/default.jsx
Normal file
25
frontend/src/pages/Admin/Agents/Badges/default.jsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
export function DefaultBadge({ title }) {
|
||||
return (
|
||||
<>
|
||||
<span
|
||||
className="w-fit"
|
||||
data-tooltip-id={`default-skill-${title}`}
|
||||
data-tooltip-content="This skill is enabled by default and cannot be turned off."
|
||||
>
|
||||
<div className="flex items-center gap-x-1 w-fit rounded-full bg-[#F4FFD0]/10 px-2.5 py-0.5 text-sm font-medium text-sky-400 shadow-sm cursor-pointer">
|
||||
<div className="text-[#F4FFD0] text-[12px] leading-[15px]">
|
||||
Default
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<Tooltip
|
||||
id={`default-skill-${title}`}
|
||||
place="bottom"
|
||||
delayShow={300}
|
||||
className="tooltip !text-xs"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
29
frontend/src/pages/Admin/Agents/DefaultSkillPanel/index.jsx
Normal file
29
frontend/src/pages/Admin/Agents/DefaultSkillPanel/index.jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
import { DefaultBadge } from "../Badges/default";
|
||||
|
||||
export default function DefaultSkillPanel({ title, description, image, icon }) {
|
||||
return (
|
||||
<div className="p-2">
|
||||
<div className="flex flex-col gap-y-[18px] max-w-[500px]">
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
{icon &&
|
||||
React.createElement(icon, {
|
||||
size: 24,
|
||||
color: "white",
|
||||
weight: "bold",
|
||||
})}
|
||||
<label htmlFor="name" className="text-white text-md font-bold">
|
||||
{title}
|
||||
</label>
|
||||
<DefaultBadge title={title} />
|
||||
</div>
|
||||
</div>
|
||||
<img src={image} alt={title} className="w-full rounded-md" />
|
||||
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user