Merge branch 'main' of github.com:CherryHQ/cherry-studio into v2

This commit is contained in:
fullex 2025-11-01 09:46:47 +08:00
commit 7c0b03dbdc
37 changed files with 854 additions and 819 deletions

View File

@ -1,4 +1,4 @@
name: 🐛 Bug Report (English)
name: 🐛 Bug Report
description: Create a report to help us improve
title: '[Bug]: '
labels: ['BUG']

View File

@ -1,4 +1,4 @@
name: 💡 Feature Request (English)
name: 💡 Feature Request
description: Suggest an idea for this project
title: '[Feature]: '
labels: ['feature']

View File

@ -1,4 +1,4 @@
name: 🤔 Other Questions (English)
name: 🤔 Other Questions
description: Submit questions that don't fit into bug reports or feature requests
title: '[Other]: '
body:

View File

@ -1,71 +0,0 @@
diff --git a/dist/utils/tiktoken.cjs b/dist/utils/tiktoken.cjs
index 973b0d0e75aeaf8de579419af31b879b32975413..f23c7caa8b9dc8bd404132725346a4786f6b278b 100644
--- a/dist/utils/tiktoken.cjs
+++ b/dist/utils/tiktoken.cjs
@@ -1,25 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodingForModel = exports.getEncoding = void 0;
-const lite_1 = require("js-tiktoken/lite");
const async_caller_js_1 = require("./async_caller.cjs");
const cache = {};
const caller = /* #__PURE__ */ new async_caller_js_1.AsyncCaller({});
async function getEncoding(encoding) {
- if (!(encoding in cache)) {
- cache[encoding] = caller
- .fetch(`https://tiktoken.pages.dev/js/${encoding}.json`)
- .then((res) => res.json())
- .then((data) => new lite_1.Tiktoken(data))
- .catch((e) => {
- delete cache[encoding];
- throw e;
- });
- }
- return await cache[encoding];
+ throw new Error("TikToken Not implemented");
}
exports.getEncoding = getEncoding;
async function encodingForModel(model) {
- return getEncoding((0, lite_1.getEncodingNameForModel)(model));
+ throw new Error("TikToken Not implemented");
}
exports.encodingForModel = encodingForModel;
diff --git a/dist/utils/tiktoken.js b/dist/utils/tiktoken.js
index 8e41ee6f00f2f9c7fa2c59fa2b2f4297634b97aa..aa5f314a6349ad0d1c5aea8631a56aad099176e0 100644
--- a/dist/utils/tiktoken.js
+++ b/dist/utils/tiktoken.js
@@ -1,20 +1,9 @@
-import { Tiktoken, getEncodingNameForModel, } from "js-tiktoken/lite";
import { AsyncCaller } from "./async_caller.js";
const cache = {};
const caller = /* #__PURE__ */ new AsyncCaller({});
export async function getEncoding(encoding) {
- if (!(encoding in cache)) {
- cache[encoding] = caller
- .fetch(`https://tiktoken.pages.dev/js/${encoding}.json`)
- .then((res) => res.json())
- .then((data) => new Tiktoken(data))
- .catch((e) => {
- delete cache[encoding];
- throw e;
- });
- }
- return await cache[encoding];
+ throw new Error("TikToken Not implemented");
}
export async function encodingForModel(model) {
- return getEncoding(getEncodingNameForModel(model));
+ throw new Error("TikToken Not implemented");
}
diff --git a/package.json b/package.json
index 36072aecf700fca1bc49832a19be832eca726103..90b8922fba1c3d1b26f78477c891b07816d6238a 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,6 @@
"ansi-styles": "^5.0.0",
"camelcase": "6",
"decamelize": "1.2.0",
- "js-tiktoken": "^1.0.12",
"langsmith": ">=0.2.8 <0.4.0",
"mustache": "^4.2.0",
"p-queue": "^6.6.2",

View File

@ -0,0 +1,68 @@
diff --git a/dist/utils/tiktoken.cjs b/dist/utils/tiktoken.cjs
index c5b41f121d2e3d24c3a4969e31fa1acffdcad3b9..ec724489dcae79ee6c61acf2d4d84bd19daef036 100644
--- a/dist/utils/tiktoken.cjs
+++ b/dist/utils/tiktoken.cjs
@@ -1,6 +1,5 @@
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
const require_utils_async_caller = require('./async_caller.cjs');
-const js_tiktoken_lite = require_rolldown_runtime.__toESM(require("js-tiktoken/lite"));
//#region src/utils/tiktoken.ts
var tiktoken_exports = {};
@@ -11,14 +10,10 @@ require_rolldown_runtime.__export(tiktoken_exports, {
const cache = {};
const caller = /* @__PURE__ */ new require_utils_async_caller.AsyncCaller({});
async function getEncoding(encoding) {
- if (!(encoding in cache)) cache[encoding] = caller.fetch(`https://tiktoken.pages.dev/js/${encoding}.json`).then((res) => res.json()).then((data) => new js_tiktoken_lite.Tiktoken(data)).catch((e) => {
- delete cache[encoding];
- throw e;
- });
- return await cache[encoding];
+ throw new Error("TikToken Not implemented");
}
async function encodingForModel(model) {
- return getEncoding((0, js_tiktoken_lite.getEncodingNameForModel)(model));
+ throw new Error("TikToken Not implemented");
}
//#endregion
diff --git a/dist/utils/tiktoken.js b/dist/utils/tiktoken.js
index 641acca03cb92f04a6fa5c9c31f1880ce635572e..707389970ad957aa0ff20ef37fa8dd2875be737c 100644
--- a/dist/utils/tiktoken.js
+++ b/dist/utils/tiktoken.js
@@ -1,6 +1,5 @@
import { __export } from "../_virtual/rolldown_runtime.js";
import { AsyncCaller } from "./async_caller.js";
-import { Tiktoken, getEncodingNameForModel } from "js-tiktoken/lite";
//#region src/utils/tiktoken.ts
var tiktoken_exports = {};
@@ -11,14 +10,10 @@ __export(tiktoken_exports, {
const cache = {};
const caller = /* @__PURE__ */ new AsyncCaller({});
async function getEncoding(encoding) {
- if (!(encoding in cache)) cache[encoding] = caller.fetch(`https://tiktoken.pages.dev/js/${encoding}.json`).then((res) => res.json()).then((data) => new Tiktoken(data)).catch((e) => {
- delete cache[encoding];
- throw e;
- });
- return await cache[encoding];
+ throw new Error("TikToken Not implemented");
}
async function encodingForModel(model) {
- return getEncoding(getEncodingNameForModel(model));
+ throw new Error("TikToken Not implemented");
}
//#endregion
diff --git a/package.json b/package.json
index a24f8fc61de58526051999260f2ebee5f136354b..e885359e8966e7730c51772533ce37e01edb3046 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,6 @@
"ansi-styles": "^5.0.0",
"camelcase": "6",
"decamelize": "1.2.0",
- "js-tiktoken": "^1.0.12",
"langsmith": "^0.3.64",
"mustache": "^4.2.0",
"p-queue": "^6.6.2",

View File

@ -1,19 +0,0 @@
diff --git a/dist/embeddings.js b/dist/embeddings.js
index 1f8154be3e9c22442a915eb4b85fa6d2a21b0d0c..dc13ef4a30e6c282824a5357bcee9bd0ae222aab 100644
--- a/dist/embeddings.js
+++ b/dist/embeddings.js
@@ -214,10 +214,12 @@ export class OpenAIEmbeddings extends Embeddings {
* @returns Promise that resolves to an embedding for the document.
*/
async embedQuery(text) {
+ const isBaiduCloud = this.clientConfig.baseURL.includes('baidubce.com')
+ const input = this.stripNewLines ? text.replace(/\n/g, ' ') : text
const params = {
model: this.model,
- input: this.stripNewLines ? text.replace(/\n/g, " ") : text,
- };
+ input: isBaiduCloud ? [input] : input
+ }
if (this.dimensions) {
params.dimensions = this.dimensions;
}

View File

@ -0,0 +1,17 @@
diff --git a/dist/embeddings.js b/dist/embeddings.js
index 6f4b928d3e4717309382e1b5c2e31ab5bc6c5af0..bc79429c88a6d27d4997a2740c4d8ae0707f5991 100644
--- a/dist/embeddings.js
+++ b/dist/embeddings.js
@@ -94,9 +94,11 @@ var OpenAIEmbeddings = class extends Embeddings {
* @returns Promise that resolves to an embedding for the document.
*/
async embedQuery(text) {
+ const isBaiduCloud = this.clientConfig.baseURL.includes('baidubce.com');
+ const input = this.stripNewLines ? text.replace(/\n/g, " ") : text
const params = {
model: this.model,
- input: this.stripNewLines ? text.replace(/\n/g, " ") : text
+ input: isBaiduCloud ? [input] : input
};
if (this.dimensions) params.dimensions = this.dimensions;
if (this.encodingFormat) params.encoding_format = this.encodingFormat;

View File

@ -23,7 +23,7 @@
},
"files": {
"ignoreUnknown": false,
"includes": ["**"],
"includes": ["**", "!**/.claude/**"],
"maxSize": 2097152
},
"formatter": {

View File

@ -151,7 +151,9 @@
"@google/genai": "patch:@google/genai@npm%3A1.0.1#~/.yarn/patches/@google-genai-npm-1.0.1-e26f0f9af7.patch",
"@hello-pangea/dnd": "^18.0.1",
"@heroui/react": "^2.8.3",
"@langchain/community": "^0.3.50",
"@langchain/community": "^1.0.0",
"@langchain/core": "patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch",
"@langchain/openai": "patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch",
"@mistralai/mistralai": "^1.7.5",
"@modelcontextprotocol/sdk": "^1.17.5",
"@mozilla/readability": "^0.6.0",
@ -377,9 +379,7 @@
"@codemirror/language": "6.11.3",
"@codemirror/lint": "6.8.5",
"@codemirror/view": "6.38.1",
"@langchain/core@npm:^0.3.26": "patch:@langchain/core@npm%3A0.3.44#~/.yarn/patches/@langchain-core-npm-0.3.44-41d5c3cb0a.patch",
"@langchain/openai@npm:^0.3.16": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch",
"@langchain/openai@npm:>=0.1.0 <0.4.0": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch",
"@langchain/core@npm:^0.3.26": "patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch",
"app-builder-lib@npm:26.0.13": "patch:app-builder-lib@npm%3A26.0.13#~/.yarn/patches/app-builder-lib-npm-26.0.13-a064c9e1d0.patch",
"app-builder-lib@npm:26.0.15": "patch:app-builder-lib@npm%3A26.0.15#~/.yarn/patches/app-builder-lib-npm-26.0.15-360e5b0476.patch",
"atomically@npm:^1.7.0": "patch:atomically@npm%3A1.7.0#~/.yarn/patches/atomically-npm-1.7.0-e742e5293b.patch",
@ -403,7 +403,10 @@
"@img/sharp-linux-arm64": "0.34.3",
"@img/sharp-linux-x64": "0.34.3",
"@img/sharp-win32-x64": "0.34.3",
"openai@npm:5.12.2": "npm:@cherrystudio/openai@6.5.0"
"openai@npm:5.12.2": "npm:@cherrystudio/openai@6.5.0",
"@langchain/openai@npm:>=0.1.0 <0.6.0": "patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch",
"@langchain/openai@npm:^0.3.16": "patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch",
"@langchain/openai@npm:>=0.2.0 <0.7.0": "patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch"
},
"packageManager": "yarn@4.9.1",
"lint-staged": {

View File

@ -2,6 +2,7 @@ import type { BaseEmbeddings } from '@cherrystudio/embedjs-interfaces'
import { OllamaEmbeddings } from '@cherrystudio/embedjs-ollama'
import { OpenAiEmbeddings } from '@cherrystudio/embedjs-openai'
import type { ApiClient } from '@types'
import { net } from 'electron'
import { VoyageEmbeddings } from './VoyageEmbeddings'
@ -43,7 +44,7 @@ export default class EmbeddingsFactory {
apiKey,
dimensions,
batchSize,
configuration: { baseURL }
configuration: { baseURL, fetch: net.fetch as typeof fetch }
})
}
}

View File

@ -0,0 +1,119 @@
import { cn } from '@heroui/react'
import { TopView } from '@renderer/components/TopView'
import { Modal } from 'antd'
import { Bot, MessageSquare } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
type OptionType = 'assistant' | 'agent'
interface ShowParams {
onSelect: (type: OptionType) => void
}
interface Props extends ShowParams {
resolve: (data: { type?: OptionType }) => void
}
const PopupContainer: React.FC<Props> = ({ onSelect, resolve }) => {
const { t } = useTranslation()
const [open, setOpen] = useState(true)
const [hoveredOption, setHoveredOption] = useState<OptionType | null>(null)
const onCancel = () => {
setOpen(false)
}
const onClose = () => {
resolve({})
}
const handleSelect = (type: OptionType) => {
setOpen(false)
onSelect(type)
resolve({ type })
}
AddAssistantOrAgentPopup.hide = onCancel
return (
<Modal
title={t('chat.add.option.title')}
open={open}
onCancel={onCancel}
afterClose={onClose}
transitionName="animation-move-down"
centered
footer={null}
width={560}>
<div className="grid grid-cols-2 gap-4 py-4">
{/* Assistant Option */}
<button
type="button"
onClick={() => handleSelect('assistant')}
className="group flex flex-col items-center gap-3 rounded-lg bg-[var(--color-background-soft)] p-6 transition-all hover:bg-[var(--color-hover)]"
onMouseEnter={() => setHoveredOption('assistant')}
onMouseLeave={() => setHoveredOption(null)}>
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-list-item)] transition-colors">
<MessageSquare
size={24}
className={cn(
'transition-colors',
hoveredOption === 'assistant' ? 'text-[var(--color-primary)]' : 'text-[var(--color-icon-white)]'
)}
/>
</div>
<div className="text-center">
<h3 className="mb-1 font-semibold text-[var(--color-text-1)] text-base">{t('chat.add.assistant.title')}</h3>
<p className="text-[var(--color-text-2)] text-sm">{t('chat.add.assistant.description')}</p>
</div>
</button>
{/* Agent Option */}
<button
onClick={() => handleSelect('agent')}
type="button"
className="group flex flex-col items-center gap-3 rounded-lg bg-[var(--color-background-soft)] p-6 transition-all hover:bg-[var(--color-hover)]"
onMouseEnter={() => setHoveredOption('agent')}
onMouseLeave={() => setHoveredOption(null)}>
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-list-item)] transition-colors">
<Bot
size={24}
className={cn(
'transition-colors',
hoveredOption === 'agent' ? 'text-[var(--color-primary)]' : 'text-[var(--color-icon-white)]'
)}
/>
</div>
<div className="text-center">
<h3 className="mb-1 font-semibold text-[var(--color-text-1)] text-base">{t('agent.add.title')}</h3>
<p className="text-[var(--color-text-2)] text-sm">{t('agent.add.description')}</p>
</div>
</button>
</div>
</Modal>
)
}
const TopViewKey = 'AddAssistantOrAgentPopup'
export default class AddAssistantOrAgentPopup {
static topviewId = 0
static hide() {
TopView.hide(TopViewKey)
}
static show(props: ShowParams) {
return new Promise<{ type?: OptionType }>((resolve) => {
TopView.show(
<PopupContainer
{...props}
resolve={(v) => {
resolve(v)
TopView.hide(TopViewKey)
}}
/>,
TopViewKey
)
})
}
}

View File

@ -10,6 +10,10 @@ export const useAgent = (id: string | null) => {
const client = useAgentClient()
const key = id ? client.agentPaths.withId(id) : null
const { apiServerConfig, apiServerRunning } = useApiServer()
// Disable SWR fetching when server is not running by setting key to null
const swrKey = apiServerRunning && id ? key : null
const fetcher = useCallback(async () => {
if (!id) {
throw new Error(t('agent.get.error.null_id'))
@ -17,13 +21,10 @@ export const useAgent = (id: string | null) => {
if (!apiServerConfig.enabled) {
throw new Error(t('apiServer.messages.notEnabled'))
}
if (!apiServerRunning) {
throw new Error(t('agent.server.error.not_running'))
}
const result = await client.getAgent(id)
return result
}, [apiServerConfig.enabled, apiServerRunning, client, id, t])
const { data, error, isLoading } = useSWR(key, id ? fetcher : null)
}, [apiServerConfig.enabled, client, id, t])
const { data, error, isLoading } = useSWR(swrKey, fetcher)
return {
agent: data,

View File

@ -1,6 +1,7 @@
{
"agent": {
"add": {
"description": "Handle complex tasks with various tools",
"error": {
"failed": "Failed to add a agent",
"invalid_agent": "Invalid Agent"
@ -547,8 +548,12 @@
"chat": {
"add": {
"assistant": {
"description": "Daily conversations and quick Q&A",
"title": "Add Assistant"
},
"option": {
"title": "Select Type"
},
"topic": {
"title": "New Topic"
}
@ -2923,15 +2928,14 @@
},
"description": "A powerful AI assistant for producer",
"downloading": "Downloading...",
"enterprise": {
"title": "Enterprise"
},
"feedback": {
"button": "Feedback",
"title": "Feedback"
},
"label": "About & Feedback",
"license": {
"button": "License",
"title": "License"
},
"releases": {
"button": "Releases",
"title": "Release Notes"

View File

@ -1,6 +1,7 @@
{
"agent": {
"add": {
"description": "调用各种工具处理复杂任务",
"error": {
"failed": "添加 Agent 失败",
"invalid_agent": "无效的 Agent"
@ -547,8 +548,12 @@
"chat": {
"add": {
"assistant": {
"description": "日常对话和快速问答",
"title": "添加助手"
},
"option": {
"title": "选择添加类型"
},
"topic": {
"title": "新建话题"
}
@ -2923,15 +2928,14 @@
},
"description": "一款为创造者而生的 AI 助手",
"downloading": "正在下载更新...",
"enterprise": {
"title": "企业版"
},
"feedback": {
"button": "反馈",
"title": "意见反馈"
},
"label": "关于我们",
"license": {
"button": "查看",
"title": "许可证"
},
"releases": {
"button": "查看",
"title": "更新日志"

View File

@ -1,6 +1,7 @@
{
"agent": {
"add": {
"description": "調用各種工具處理複雜任務",
"error": {
"failed": "無法新增代理人",
"invalid_agent": "無效的 Agent"
@ -547,8 +548,12 @@
"chat": {
"add": {
"assistant": {
"description": "日常對話和快速問答",
"title": "新增助手"
},
"option": {
"title": "選擇新增類型"
},
"topic": {
"title": "新增話題"
}
@ -1047,7 +1052,7 @@
"clear": "清除",
"close": "關閉",
"collapse": "折疊",
"completed": "[to be translated]:Completed",
"completed": "已完成",
"confirm": "確認",
"copied": "已複製",
"copy": "複製",
@ -2923,15 +2928,14 @@
},
"description": "一款為創作者而生的強大 AI 助手",
"downloading": "正在下載...",
"enterprise": {
"title": "企業版"
},
"feedback": {
"button": "回饋",
"title": "回饋"
},
"label": "關於與回饋",
"license": {
"button": "檢視",
"title": "授權"
},
"releases": {
"button": "檢視",
"title": "更新日誌"
@ -3045,37 +3049,37 @@
},
"content": "匯出部分數據,包括聊天記錄、設定。請注意,備份過程可能需要一些時間,感謝您的耐心等候。",
"lan": {
"auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...",
"confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",
"confirm_close_title": "[to be translated]:Confirm Close",
"connected": "[to be translated]:Connected",
"connection_failed": "[to be translated]:Connection failed",
"auto_close_tip": "將於 {{seconds}} 秒後自動關閉...",
"confirm_close_message": "檔案傳輸正在進行中。關閉將會中斷傳輸。您確定要強制關閉嗎?",
"confirm_close_title": "確認關閉",
"connected": "已連線",
"connection_failed": "連線失敗",
"content": "請確保電腦和手機處於同一網路以使用區域網路傳輸。請打開 Cherry Studio App 掃描此 QR 碼。",
"error": {
"init_failed": "[to be translated]:Initialization failed",
"no_file": "[to be translated]:No file selected",
"no_ip": "[to be translated]:Unable to get IP address",
"send_failed": "[to be translated]:Failed to send file"
"init_failed": "初始化失敗",
"no_file": "未選擇檔案",
"no_ip": "無法取得 IP 位址",
"send_failed": "無法傳送檔案"
},
"force_close": "[to be translated]:Force Close",
"generating_qr": "[to be translated]:Generating QR code...",
"force_close": "強制關閉",
"generating_qr": "正在生成 QR 碼...",
"noZipSelected": "未選取壓縮檔案",
"scan_qr": "[to be translated]:Please scan QR code with your phone",
"scan_qr": "請使用手機掃描QR碼",
"selectZip": "選擇壓縮檔案",
"sendZip": "開始恢復資料",
"status": {
"completed": "[to be translated]:Transfer completed",
"connected": "[to be translated]:Connected",
"connecting": "[to be translated]:Connecting...",
"disconnected": "[to be translated]:Disconnected",
"error": "[to be translated]:Connection error",
"initializing": "[to be translated]:Initializing connection...",
"preparing": "[to be translated]:Preparing transfer...",
"sending": "[to be translated]:Transferring {{progress}}%",
"waiting_qr_scan": "[to be translated]:Please scan QR code to connect"
"completed": "轉帳完成",
"connected": "已連線",
"connecting": "連線中...",
"disconnected": "已斷線",
"error": "連線錯誤",
"initializing": "正在初始化連線...",
"preparing": "正在準備傳輸...",
"sending": "傳輸中 {{progress}}%",
"waiting_qr_scan": "請掃描QR碼以連接"
},
"title": "區域網路傳輸",
"transfer_progress": "[to be translated]:Transfer progress"
"transfer_progress": "傳輸進度"
},
"title": "匯出手機"
},

View File

@ -1047,7 +1047,7 @@
"clear": "Löschen",
"close": "Schließen",
"collapse": "Einklappen",
"completed": "[to be translated]:Completed",
"completed": "Abgeschlossen",
"confirm": "Bestätigen",
"copied": "Kopiert",
"copy": "Kopieren",
@ -3041,43 +3041,43 @@
},
"export_to_phone": {
"confirm": {
"button": "[to be translated]:Select backup file"
"button": "Sicherungsdatei auswählen"
},
"content": "[to be translated]:Export some data, including chat logs and settings. Please note that the backup process may take some time. Thank you for your patience.",
"content": "Exportieren Sie einige Daten, einschließlich Chat-Protokollen und Einstellungen. Bitte beachten Sie, dass der Sicherungsvorgang einige Zeit in Anspruch nehmen kann. Vielen Dank für Ihre Geduld.",
"lan": {
"auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...",
"confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",
"confirm_close_title": "[to be translated]:Confirm Close",
"connected": "[to be translated]:Connected",
"connection_failed": "[to be translated]:Connection failed",
"content": "[to be translated]:Please ensure your computer and phone are on the same network for LAN transfer. Open the Cherry Studio App to scan this QR code.",
"auto_close_tip": "Automatisches Schließen in {{seconds}} Sekunden...",
"confirm_close_message": "Dateiübertragung läuft. Beim Schließen wird die Übertragung unterbrochen. Möchten Sie wirklich das Schließen erzwingen?",
"confirm_close_title": "Schließen bestätigen",
"connected": "Verbunden",
"connection_failed": "Verbindung fehlgeschlagen",
"content": "Bitte stelle sicher, dass sich dein Computer und dein Telefon im selben Netzwerk befinden, um eine LAN-Übertragung durchzuführen. Öffne die Cherry Studio App, um diesen QR-Code zu scannen.",
"error": {
"init_failed": "[to be translated]:Initialization failed",
"no_file": "[to be translated]:No file selected",
"no_ip": "[to be translated]:Unable to get IP address",
"send_failed": "[to be translated]:Failed to send file"
"init_failed": "Initialisierung fehlgeschlagen",
"no_file": "Keine Datei ausgewählt",
"no_ip": "IP-Adresse kann nicht abgerufen werden",
"send_failed": "Fehler beim Senden der Datei"
},
"force_close": "[to be translated]:Force Close",
"generating_qr": "[to be translated]:Generating QR code...",
"noZipSelected": "[to be translated]:No compressed file selected",
"scan_qr": "[to be translated]:Please scan QR code with your phone",
"selectZip": "[to be translated]:Select a compressed file",
"sendZip": "[to be translated]:Begin data recovery",
"force_close": "Erzwungenes Schließen",
"generating_qr": "QR-Code wird generiert...",
"noZipSelected": "Keine komprimierte Datei ausgewählt",
"scan_qr": "Bitte scannen Sie den QR-Code mit Ihrem Telefon.",
"selectZip": "Wählen Sie eine komprimierte Datei",
"sendZip": "Datenwiederherstellung beginnen",
"status": {
"completed": "[to be translated]:Transfer completed",
"connected": "[to be translated]:Connected",
"connecting": "[to be translated]:Connecting...",
"disconnected": "[to be translated]:Disconnected",
"error": "[to be translated]:Connection error",
"initializing": "[to be translated]:Initializing connection...",
"preparing": "[to be translated]:Preparing transfer...",
"sending": "[to be translated]:Transferring {{progress}}%",
"waiting_qr_scan": "[to be translated]:Please scan QR code to connect"
"completed": "Übertragung abgeschlossen",
"connected": "Verbunden",
"connecting": "Verbindung wird hergestellt...",
"disconnected": "Getrennt",
"error": "Verbindungsfehler",
"initializing": "Verbindung wird initialisiert...",
"preparing": "Übertragung wird vorbereitet...",
"sending": "Übertrage {{progress}}%",
"waiting_qr_scan": "Bitte QR-Code scannen, um zu verbinden"
},
"title": "[to be translated]:LAN transmission",
"transfer_progress": "[to be translated]:Transfer progress"
"title": "LAN-Übertragung",
"transfer_progress": "Übertragungsfortschritt"
},
"title": "[to be translated]:Export to phone"
"title": "Auf Telefon exportieren"
},
"hour_interval_one": "{{count}} Stunde",
"hour_interval_other": "{{count}} Stunden",

View File

@ -1047,7 +1047,7 @@
"clear": "Καθαρισμός",
"close": "Κλείσιμο",
"collapse": "Σύμπτυξη",
"completed": "[to be translated]:Completed",
"completed": "Ολοκληρώθηκε",
"confirm": "Επιβεβαίωση",
"copied": "Αντιγράφηκε",
"copy": "Αντιγραφή",
@ -3041,43 +3041,43 @@
},
"export_to_phone": {
"confirm": {
"button": "[to be translated]:选择备份文件"
"button": "Επιλέξτε αρχείο αντιγράφων ασφαλείας"
},
"content": "[to be translated]:导出部分数据,包括聊天记录、设置。请注意,备份过程可能需要一些时间,感谢您的耐心等待。",
"content": "Εξαγωγή μέρους των δεδομένων, συμπεριλαμβανομένων των ιστορικών συνομιλιών και των ρυθμίσεων. Σημειώστε ότι η διαδικασία δημιουργίας αντιγράφων ασφαλείας ενδέχεται να διαρκέσει κάποιο χρονικό διάστημα, ευχαριστούμε για την υπομονή σας.",
"lan": {
"auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...",
"confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",
"confirm_close_title": "[to be translated]:Confirm Close",
"connected": "[to be translated]:Connected",
"connection_failed": "[to be translated]:Connection failed",
"content": "[to be translated]:请确保电脑和手机处于同一网络以使用局域网传输。请打开 Cherry Studio App 扫描此二维码。",
"auto_close_tip": "Αυτόματο κλείσιμο σε {{seconds}} δευτερόλεπτα...",
"confirm_close_message": "Η μεταφορά αρχείων είναι σε εξέλιξη. Το κλείσιμο θα διακόψει τη μεταφορά. Είστε σίγουροι ότι θέλετε να κλείσετε βίαια;",
"confirm_close_title": "Επιβεβαίωση Κλεισίματος",
"connected": "Συνδεδεμένος",
"connection_failed": "Η σύνδεση απέτυχε",
"content": "Βεβαιωθείτε ότι ο υπολογιστής και το κινητό βρίσκονται στο ίδιο δίκτυο για να χρησιμοποιήσετε τη μεταφορά LAN. Ανοίξτε την εφαρμογή Cherry Studio και σαρώστε αυτόν τον κωδικό QR.",
"error": {
"init_failed": "[to be translated]:Initialization failed",
"no_file": "[to be translated]:No file selected",
"no_ip": "[to be translated]:Unable to get IP address",
"send_failed": "[to be translated]:Failed to send file"
"init_failed": "Η αρχικοποίηση απέτυχε",
"no_file": "Κανένα αρχείο δεν επιλέχθηκε",
"no_ip": "Αδυναμία λήψης διεύθυνσης IP",
"send_failed": "Αποτυχία αποστολής αρχείου"
},
"force_close": "[to be translated]:Force Close",
"generating_qr": "[to be translated]:Generating QR code...",
"noZipSelected": "[to be translated]:未选择压缩文件",
"scan_qr": "[to be translated]:Please scan QR code with your phone",
"selectZip": "[to be translated]:选择压缩文件",
"sendZip": "[to be translated]:开始恢复数据",
"force_close": "Κλείσιμο με βία",
"generating_qr": "Δημιουργία κώδικα QR...",
"noZipSelected": "Δεν επιλέχθηκε συμπιεσμένο αρχείο",
"scan_qr": "Παρακαλώ σαρώστε τον κωδικό QR με το τηλέφωνό σας",
"selectZip": "Επιλέξτε συμπιεσμένο αρχείο",
"sendZip": "Έναρξη ανάκτησης δεδομένων",
"status": {
"completed": "[to be translated]:Transfer completed",
"connected": "[to be translated]:Connected",
"connecting": "[to be translated]:Connecting...",
"disconnected": "[to be translated]:Disconnected",
"error": "[to be translated]:Connection error",
"initializing": "[to be translated]:Initializing connection...",
"preparing": "[to be translated]:Preparing transfer...",
"sending": "[to be translated]:Transferring {{progress}}%",
"waiting_qr_scan": "[to be translated]:Please scan QR code to connect"
"completed": "Η μεταφορά ολοκληρώθηκε",
"connected": "Συνδεδεμένος",
"connecting": "Σύνδεση...",
"disconnected": "Αποσυνδέθηκε",
"error": "Σφάλμα σύνδεσης",
"initializing": "Αρχικοποίηση σύνδεσης...",
"preparing": "Προετοιμασία μεταφοράς...",
"sending": "Μεταφορά {{progress}}%",
"waiting_qr_scan": "Παρακαλώ σαρώστε τον κωδικό QR για σύνδεση"
},
"title": "[to be translated]:局域网传输",
"transfer_progress": "[to be translated]:Transfer progress"
"title": "Μεταφορά τοπικού δικτύου",
"transfer_progress": "Πρόοδος μεταφοράς"
},
"title": "[to be translated]:导出至手机"
"title": "Εξαγωγή στο κινητό"
},
"hour_interval_one": "{{count}} ώρα",
"hour_interval_other": "{{count}} ώρες",

View File

@ -1047,7 +1047,7 @@
"clear": "Limpiar",
"close": "Cerrar",
"collapse": "Colapsar",
"completed": "[to be translated]:Completed",
"completed": "Completado",
"confirm": "Confirmar",
"copied": "Copiado",
"copy": "Copiar",
@ -3041,43 +3041,43 @@
},
"export_to_phone": {
"confirm": {
"button": "[to be translated]:选择备份文件"
"button": "Seleccionar archivo de copia de seguridad"
},
"content": "[to be translated]:导出部分数据,包括聊天记录、设置。请注意,备份过程可能需要一些时间,感谢您的耐心等待。",
"content": "Exportar parte de los datos, incluidos los registros de chat y la configuración. Tenga en cuenta que el proceso de copia de seguridad puede tardar un tiempo; gracias por su paciencia.",
"lan": {
"auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...",
"confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",
"confirm_close_title": "[to be translated]:Confirm Close",
"connected": "[to be translated]:Connected",
"connection_failed": "[to be translated]:Connection failed",
"content": "[to be translated]:请确保电脑和手机处于同一网络以使用局域网传输。请打开 Cherry Studio App 扫描此二维码。",
"auto_close_tip": "Cierre automático en {{seconds}} segundos...",
"confirm_close_message": "La transferencia de archivos está en progreso. Cerrar interrumpirá la transferencia. ¿Estás seguro de que quieres forzar el cierre?",
"confirm_close_title": "Confirmar Cierre",
"connected": "Conectado",
"connection_failed": "Conexión fallida",
"content": "Asegúrate de que el ordenador y el móvil estén en la misma red para usar la transferencia por LAN. Abre la aplicación Cherry Studio y escanea este código QR.",
"error": {
"init_failed": "[to be translated]:Initialization failed",
"no_file": "[to be translated]:No file selected",
"no_ip": "[to be translated]:Unable to get IP address",
"send_failed": "[to be translated]:Failed to send file"
"init_failed": "Falló la inicialización",
"no_file": "Ningún archivo seleccionado",
"no_ip": "No se puede obtener la dirección IP",
"send_failed": "Error al enviar el archivo"
},
"force_close": "[to be translated]:Force Close",
"generating_qr": "[to be translated]:Generating QR code...",
"noZipSelected": "[to be translated]:未选择压缩文件",
"scan_qr": "[to be translated]:Please scan QR code with your phone",
"selectZip": "[to be translated]:选择压缩文件",
"sendZip": "[to be translated]:开始恢复数据",
"force_close": "Cerrar forzosamente",
"generating_qr": "Generando código QR...",
"noZipSelected": "No se ha seleccionado ningún archivo comprimido",
"scan_qr": "Por favor, escanea el código QR con tu teléfono",
"selectZip": "Seleccionar archivo comprimido",
"sendZip": "Comenzar la recuperación de datos",
"status": {
"completed": "[to be translated]:Transfer completed",
"connected": "[to be translated]:Connected",
"connecting": "[to be translated]:Connecting...",
"disconnected": "[to be translated]:Disconnected",
"error": "[to be translated]:Connection error",
"initializing": "[to be translated]:Initializing connection...",
"preparing": "[to be translated]:Preparing transfer...",
"sending": "[to be translated]:Transferring {{progress}}%",
"waiting_qr_scan": "[to be translated]:Please scan QR code to connect"
"completed": "Transferencia completada",
"connected": "Conectado",
"connecting": "Conectando...",
"disconnected": "Desconectado",
"error": "Error de conexión",
"initializing": "Inicializando conexión...",
"preparing": "Preparando transferencia...",
"sending": "Transfiriendo {{progress}}%",
"waiting_qr_scan": "Por favor, escanea el código QR para conectarte"
},
"title": "[to be translated]:局域网传输",
"transfer_progress": "[to be translated]:Transfer progress"
"title": "Transferencia de red local",
"transfer_progress": "Progreso de transferencia"
},
"title": "[to be translated]:导出至手机"
"title": "Exportar al teléfono"
},
"hour_interval_one": "{{count}} hora",
"hour_interval_other": "{{count}} horas",

View File

@ -1047,7 +1047,7 @@
"clear": "Effacer",
"close": "Fermer",
"collapse": "Réduire",
"completed": "[to be translated]:Completed",
"completed": "Terminé",
"confirm": "Confirmer",
"copied": "Copié",
"copy": "Copier",
@ -3041,43 +3041,43 @@
},
"export_to_phone": {
"confirm": {
"button": "[to be translated]:选择备份文件"
"button": "Sélectionner le fichier de sauvegarde"
},
"content": "[to be translated]:导出部分数据,包括聊天记录、设置。请注意,备份过程可能需要一些时间,感谢您的耐心等待。",
"content": "Exporter une partie des données, incluant les historiques de discussion et les paramètres. Veuillez noter que le processus de sauvegarde peut prendre un certain temps ; merci pour votre patience.",
"lan": {
"auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...",
"confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",
"confirm_close_title": "[to be translated]:Confirm Close",
"connected": "[to be translated]:Connected",
"connection_failed": "[to be translated]:Connection failed",
"content": "[to be translated]:请确保电脑和手机处于同一网络以使用局域网传输。请打开 Cherry Studio App 扫描此二维码。",
"auto_close_tip": "Fermeture automatique dans {{seconds}} secondes...",
"confirm_close_message": "Le transfert de fichier est en cours. Fermer interrompra le transfert. Êtes-vous sûr de vouloir forcer la fermeture ?",
"confirm_close_title": "Confirmer la fermeture",
"connected": "Connecté",
"connection_failed": "Échec de la connexion",
"content": "Assurez-vous que l'ordinateur et le téléphone sont connectés au même réseau pour utiliser le transfert en réseau local. Ouvrez l'application Cherry Studio et scannez ce code QR.",
"error": {
"init_failed": "[to be translated]:Initialization failed",
"no_file": "[to be translated]:No file selected",
"no_ip": "[to be translated]:Unable to get IP address",
"send_failed": "[to be translated]:Failed to send file"
"init_failed": "Échec de l'initialisation",
"no_file": "Aucun fichier sélectionné",
"no_ip": "Impossible d'obtenir l'adresse IP",
"send_failed": "Échec de l'envoi du fichier"
},
"force_close": "[to be translated]:Force Close",
"generating_qr": "[to be translated]:Generating QR code...",
"noZipSelected": "[to be translated]:未选择压缩文件",
"scan_qr": "[to be translated]:Please scan QR code with your phone",
"selectZip": "[to be translated]:选择压缩文件",
"sendZip": "[to be translated]:开始恢复数据",
"force_close": "Fermer de force",
"generating_qr": "Génération du code QR...",
"noZipSelected": "Aucun fichier compressé sélectionné",
"scan_qr": "Veuillez scanner le code QR avec votre téléphone",
"selectZip": "Sélectionner le fichier compressé",
"sendZip": "Commencer la restauration des données",
"status": {
"completed": "[to be translated]:Transfer completed",
"connected": "[to be translated]:Connected",
"connecting": "[to be translated]:Connecting...",
"disconnected": "[to be translated]:Disconnected",
"error": "[to be translated]:Connection error",
"initializing": "[to be translated]:Initializing connection...",
"preparing": "[to be translated]:Preparing transfer...",
"sending": "[to be translated]:Transferring {{progress}}%",
"waiting_qr_scan": "[to be translated]:Please scan QR code to connect"
"completed": "Transfert terminé",
"connected": "Connecté",
"connecting": "Connexion...",
"disconnected": "Déconnecté",
"error": "Erreur de connexion",
"initializing": "Initialisation de la connexion...",
"preparing": "Préparation du transfert...",
"sending": "Transfert {{progress}} %",
"waiting_qr_scan": "Veuillez scanner le code QR pour vous connecter"
},
"title": "[to be translated]:局域网传输",
"transfer_progress": "[to be translated]:Transfer progress"
"title": "Transmission en réseau local",
"transfer_progress": "Progression du transfert"
},
"title": "[to be translated]:导出至手机"
"title": "Exporter vers le téléphone"
},
"hour_interval_one": "{{count}} heure",
"hour_interval_other": "{{count}} heures",

View File

@ -1047,7 +1047,7 @@
"clear": "クリア",
"close": "閉じる",
"collapse": "折りたたむ",
"completed": "[to be translated]:Completed",
"completed": "完了",
"confirm": "確認",
"copied": "コピーされました",
"copy": "コピー",
@ -3041,43 +3041,43 @@
},
"export_to_phone": {
"confirm": {
"button": "[to be translated]:选择备份文件"
"button": "バックアップファイルを選択"
},
"content": "[to be translated]:导出部分数据,包括聊天记录、设置。请注意,备份过程可能需要一些时间,感谢您的耐心等待。",
"content": "一部のデータ、チャット履歴や設定をエクスポートします。バックアップには時間がかかる場合がありますので、しばらくお待ちください。",
"lan": {
"auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...",
"confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",
"confirm_close_title": "[to be translated]:Confirm Close",
"connected": "[to be translated]:Connected",
"connection_failed": "[to be translated]:Connection failed",
"content": "[to be translated]:请确保电脑和手机处于同一网络以使用局域网传输。请打开 Cherry Studio App 扫描此二维码。",
"auto_close_tip": "{{seconds}}秒後に自動的に閉じます...",
"confirm_close_message": "ファイル転送が進行中です。閉じると転送が中断されます。強制終了してもよろしいですか?",
"confirm_close_title": "閉じることを確認",
"connected": "接続済み",
"connection_failed": "接続に失敗しました",
"content": "コンピューターとスマートフォンが同じネットワークに接続されていることを確認し、ローカルエリアネットワーク転送を使用してください。Cherry Studioアプリを開き、このQRコードをスキャンしてください。",
"error": {
"init_failed": "[to be translated]:Initialization failed",
"no_file": "[to be translated]:No file selected",
"no_ip": "[to be translated]:Unable to get IP address",
"send_failed": "[to be translated]:Failed to send file"
"init_failed": "初期化に失敗しました",
"no_file": "ファイルが選択されていません",
"no_ip": "IPアドレスを取得できません",
"send_failed": "ファイルの送信に失敗しました"
},
"force_close": "[to be translated]:Force Close",
"generating_qr": "[to be translated]:Generating QR code...",
"noZipSelected": "[to be translated]:未选择压缩文件",
"scan_qr": "[to be translated]:Please scan QR code with your phone",
"selectZip": "[to be translated]:选择压缩文件",
"sendZip": "[to be translated]:开始恢复数据",
"force_close": "強制終了",
"generating_qr": "QRコードを生成中...",
"noZipSelected": "圧縮ファイルが選択されていません",
"scan_qr": "携帯電話でQRコードをスキャンしてください",
"selectZip": "圧縮ファイルを選択",
"sendZip": "データの復元を開始します",
"status": {
"completed": "[to be translated]:Transfer completed",
"connected": "[to be translated]:Connected",
"connecting": "[to be translated]:Connecting...",
"disconnected": "[to be translated]:Disconnected",
"error": "[to be translated]:Connection error",
"initializing": "[to be translated]:Initializing connection...",
"preparing": "[to be translated]:Preparing transfer...",
"sending": "[to be translated]:Transferring {{progress}}%",
"waiting_qr_scan": "[to be translated]:Please scan QR code to connect"
"completed": "転送完了",
"connected": "接続済み",
"connecting": "接続中...",
"disconnected": "切断されました",
"error": "接続エラー",
"initializing": "接続を初期化中...",
"preparing": "転送準備中...",
"sending": "転送中 {{progress}}%",
"waiting_qr_scan": "QRコードをスキャンして接続してください"
},
"title": "[to be translated]:局域网传输",
"transfer_progress": "[to be translated]:Transfer progress"
"title": "LAN転送",
"transfer_progress": "転送進行"
},
"title": "[to be translated]:导出至手机"
"title": "スマートフォンにエクスポート"
},
"hour_interval_one": "{{count}} 時間",
"hour_interval_other": "{{count}} 時間",

View File

@ -1047,7 +1047,7 @@
"clear": "Limpar",
"close": "Fechar",
"collapse": "Recolher",
"completed": "[to be translated]:Completed",
"completed": "Concluído",
"confirm": "Confirmar",
"copied": "Copiado",
"copy": "Copiar",
@ -3041,43 +3041,43 @@
},
"export_to_phone": {
"confirm": {
"button": "[to be translated]:选择备份文件"
"button": "Selecionar arquivo de backup"
},
"content": "[to be translated]:导出部分数据,包括聊天记录、设置。请注意,备份过程可能需要一些时间,感谢您的耐心等待。",
"content": "Exportar parte dos dados, incluindo registros de conversas e configurações. Observe que o processo de backup pode demorar um pouco; agradecemos sua paciência.",
"lan": {
"auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...",
"confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",
"confirm_close_title": "[to be translated]:Confirm Close",
"connected": "[to be translated]:Connected",
"connection_failed": "[to be translated]:Connection failed",
"content": "[to be translated]:请确保电脑和手机处于同一网络以使用局域网传输。请打开 Cherry Studio App 扫描此二维码。",
"auto_close_tip": "Fechando automaticamente em {{seconds}} segundos...",
"confirm_close_message": "Transferência de arquivo em andamento. Fechar irá interromper a transferência. Tem certeza de que deseja forçar o fechamento?",
"confirm_close_title": "Confirmar Fechamento",
"connected": "Conectado",
"connection_failed": "Falha na conexão",
"content": "Certifique-se de que o computador e o telefone estejam na mesma rede para usar a transferência via LAN. Abra o aplicativo Cherry Studio e escaneie este código QR.",
"error": {
"init_failed": "[to be translated]:Initialization failed",
"no_file": "[to be translated]:No file selected",
"no_ip": "[to be translated]:Unable to get IP address",
"send_failed": "[to be translated]:Failed to send file"
"init_failed": "Falha na inicialização",
"no_file": "Nenhum arquivo selecionado",
"no_ip": "Incapaz de obter endereço IP",
"send_failed": "Falha ao enviar arquivo"
},
"force_close": "[to be translated]:Force Close",
"generating_qr": "[to be translated]:Generating QR code...",
"noZipSelected": "[to be translated]:未选择压缩文件",
"scan_qr": "[to be translated]:Please scan QR code with your phone",
"selectZip": "[to be translated]:选择压缩文件",
"sendZip": "[to be translated]:开始恢复数据",
"force_close": "Forçar Fechamento",
"generating_qr": "Gerando código QR...",
"noZipSelected": "Nenhum arquivo de compressão selecionado",
"scan_qr": "Por favor, escaneie o código QR com o seu telefone",
"selectZip": "Selecionar arquivo compactado",
"sendZip": "Iniciar recuperação de dados",
"status": {
"completed": "[to be translated]:Transfer completed",
"connected": "[to be translated]:Connected",
"connecting": "[to be translated]:Connecting...",
"disconnected": "[to be translated]:Disconnected",
"error": "[to be translated]:Connection error",
"initializing": "[to be translated]:Initializing connection...",
"preparing": "[to be translated]:Preparing transfer...",
"sending": "[to be translated]:Transferring {{progress}}%",
"waiting_qr_scan": "[to be translated]:Please scan QR code to connect"
"completed": "Transferência concluída",
"connected": "Conectado",
"connecting": "Conectando...",
"disconnected": "Desconectado",
"error": "Erro de conexão",
"initializing": "Inicializando conexão...",
"preparing": "Preparando transferência...",
"sending": "Transferindo {{progress}}%",
"waiting_qr_scan": "Por favor, escaneie o código QR para conectar"
},
"title": "[to be translated]:局域网传输",
"transfer_progress": "[to be translated]:Transfer progress"
"title": "transmissão de rede local",
"transfer_progress": "Progresso da transferência"
},
"title": "[to be translated]:导出至手机"
"title": "Exportar para o telemóvel"
},
"hour_interval_one": "{{count}} hora",
"hour_interval_other": "{{count}} horas",

View File

@ -1047,7 +1047,7 @@
"clear": "Очистить",
"close": "Закрыть",
"collapse": "Свернуть",
"completed": "[to be translated]:Completed",
"completed": "Завершено",
"confirm": "Подтверждение",
"copied": "Скопировано",
"copy": "Копировать",
@ -3041,43 +3041,43 @@
},
"export_to_phone": {
"confirm": {
"button": "[to be translated]:选择备份文件"
"button": "Выберите файл резервной копии"
},
"content": "[to be translated]:导出部分数据,包括聊天记录、设置。请注意,备份过程可能需要一些时间,感谢您的耐心等待。",
"content": "Экспорт части данных, включая чат и настройки. Пожалуйста, обратите внимание, что процесс резервного копирования может занять некоторое время. Благодарим за ваше терпение.",
"lan": {
"auto_close_tip": "[to be translated]:Auto-closing in {{seconds}} seconds...",
"confirm_close_message": "[to be translated]:File transfer is in progress. Closing will interrupt the transfer. Are you sure you want to force close?",
"confirm_close_title": "[to be translated]:Confirm Close",
"connected": "[to be translated]:Connected",
"connection_failed": "[to be translated]:Connection failed",
"content": "[to be translated]:请确保电脑和手机处于同一网络以使用局域网传输。请打开 Cherry Studio App 扫描此二维码。",
"auto_close_tip": "Автоматическое закрытие через {{seconds}} секунд...",
"confirm_close_message": "Передача файла в процессе. Закрытие прервет передачу. Вы уверены, что хотите принудительно закрыть?",
"confirm_close_title": "Подтвердить закрытие",
"connected": "Подключено",
"connection_failed": "Соединение не удалось",
"content": "Убедитесь, что компьютер и телефон подключены к одной сети, чтобы использовать локальную передачу. Откройте приложение Cherry Studio и отсканируйте этот QR-код.",
"error": {
"init_failed": "[to be translated]:Initialization failed",
"no_file": "[to be translated]:No file selected",
"no_ip": "[to be translated]:Unable to get IP address",
"send_failed": "[to be translated]:Failed to send file"
"init_failed": "Инициализация не удалась",
"no_file": "Файл не выбран",
"no_ip": "Не удалось получить IP-адрес",
"send_failed": "Не удалось отправить файл"
},
"force_close": "[to be translated]:Force Close",
"generating_qr": "[to be translated]:Generating QR code...",
"noZipSelected": "[to be translated]:未选择压缩文件",
"scan_qr": "[to be translated]:Please scan QR code with your phone",
"selectZip": "[to be translated]:选择压缩文件",
"sendZip": "[to be translated]:开始恢复数据",
"force_close": "Принудительное закрытие",
"generating_qr": "Генерация QR-кода...",
"noZipSelected": "Архив не выбран",
"scan_qr": "Пожалуйста, отсканируйте QR-код с помощью вашего телефона",
"selectZip": "Выберите архив",
"sendZip": "Начать восстановление данных",
"status": {
"completed": "[to be translated]:Transfer completed",
"connected": "[to be translated]:Connected",
"connecting": "[to be translated]:Connecting...",
"disconnected": "[to be translated]:Disconnected",
"error": "[to be translated]:Connection error",
"initializing": "[to be translated]:Initializing connection...",
"preparing": "[to be translated]:Preparing transfer...",
"sending": "[to be translated]:Transferring {{progress}}%",
"waiting_qr_scan": "[to be translated]:Please scan QR code to connect"
"completed": "Перевод завершён",
"connected": "Подключено",
"connecting": "Подключение...",
"disconnected": "Отключено",
"error": "Ошибка подключения",
"initializing": "Инициализация соединения...",
"preparing": "Подготовка передачи...",
"sending": "Передача {{progress}}%",
"waiting_qr_scan": "Пожалуйста, отсканируйте QR-код для подключения"
},
"title": "[to be translated]:局域网传输",
"transfer_progress": "[to be translated]:Transfer progress"
"title": "Передача по локальной сети",
"transfer_progress": "Прогресс передачи"
},
"title": "[to be translated]:导出至手机"
"title": "Экспорт на телефон"
},
"hour_interval_one": "{{count}} час",
"hour_interval_other": "{{count}} часов",

View File

@ -172,7 +172,7 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
onAssistantSwitch={setActiveAssistant}
onAssistantDelete={onDeleteAssistant}
onAgentDelete={deleteAgent}
onAgentPress={setActiveAgentId}
onAgentPress={handleAgentPress}
addPreset={addAssistantPreset}
copyAssistant={copyAssistant}
onCreateDefaultAssistant={onCreateDefaultAssistant}

View File

@ -85,7 +85,8 @@ export const Container: React.FC<{ isActive?: boolean } & React.HTMLAttributes<H
}) => (
<div
className={cn(
'relative flex h-[37px] w-[calc(var(--assistants-width)-20px)] cursor-pointer flex-row justify-between rounded-[var(--list-item-border-radius)] border border-transparent px-2 hover:bg-[var(--color-list-item-hover)]',
'relative flex h-[37px] w-[calc(var(--assistants-width)-20px)] cursor-pointer flex-row justify-between rounded-[var(--list-item-border-radius)] border border-transparent px-2',
!isActive && 'hover:bg-[var(--color-list-item-hover)]',
isActive && 'bg-[var(--color-list-item)] shadow-[0_1px_2px_0_rgba(0,0,0,0.05)]',
className
)}

View File

@ -404,7 +404,8 @@ const Container = ({
<div
{...props}
className={cn(
'relative flex h-[37px] w-[calc(var(--assistants-width)-20px)] cursor-pointer flex-row justify-between rounded-[var(--list-item-border-radius)] border-[0.5px] border-transparent px-2 hover:bg-[var(--color-list-item-hover)]',
'relative flex h-[37px] w-[calc(var(--assistants-width)-20px)] cursor-pointer flex-row justify-between rounded-[var(--list-item-border-radius)] border-[0.5px] border-transparent px-2',
!isActive && 'hover:bg-[var(--color-list-item-hover)]',
isActive && 'bg-[var(--color-list-item)] shadow-[0_1px_2px_0_rgba(0,0,0,0.05)]',
className
)}>

View File

@ -1,11 +1,11 @@
import { Button, Popover, PopoverContent, PopoverTrigger, useDisclosure } from '@heroui/react'
import { useDisclosure } from '@heroui/react'
import AddAssistantOrAgentPopup from '@renderer/components/Popups/AddAssistantOrAgentPopup'
import { AgentModal } from '@renderer/components/Popups/agent/AgentModal'
import { useAppDispatch } from '@renderer/store'
import { setActiveTopicOrSessionAction } from '@renderer/store/runtime'
import type { AgentEntity, Assistant, Topic } from '@renderer/types'
import { Bot, MessageSquare } from 'lucide-react'
import type { FC } from 'react'
import { useCallback, useState } from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import AddButton from './AddButton'
@ -18,18 +18,19 @@ interface UnifiedAddButtonProps {
const UnifiedAddButton: FC<UnifiedAddButtonProps> = ({ onCreateAssistant, setActiveAssistant, setActiveAgentId }) => {
const { t } = useTranslation()
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
const { isOpen: isAgentModalOpen, onOpen: onAgentModalOpen, onClose: onAgentModalClose } = useDisclosure()
const dispatch = useAppDispatch()
const handleAddAssistant = () => {
setIsPopoverOpen(false)
onCreateAssistant()
}
const handleAddAgent = () => {
setIsPopoverOpen(false)
onAgentModalOpen()
const handleAddButtonClick = () => {
AddAssistantOrAgentPopup.show({
onSelect: (type) => {
if (type === 'assistant') {
onCreateAssistant()
} else if (type === 'agent') {
onAgentModalOpen()
}
}
})
}
const afterCreate = useCallback(
@ -59,32 +60,9 @@ const UnifiedAddButton: FC<UnifiedAddButtonProps> = ({ onCreateAssistant, setAct
return (
<div className="mb-1">
<Popover
isOpen={isPopoverOpen}
onOpenChange={setIsPopoverOpen}
placement="bottom"
classNames={{ content: 'p-0 min-w-[200px]' }}>
<PopoverTrigger>
<AddButton>{t('chat.add.assistant.title')}</AddButton>
</PopoverTrigger>
<PopoverContent>
<div className="flex w-full flex-col gap-1 p-1">
<Button
onClick={handleAddAssistant}
className="w-full justify-start bg-transparent hover:bg-[var(--color-list-item)]"
startContent={<MessageSquare size={16} className="shrink-0" />}>
{t('chat.add.assistant.title')}
</Button>
<Button
onClick={handleAddAgent}
className="w-full justify-start bg-transparent hover:bg-[var(--color-list-item)]"
startContent={<Bot size={16} className="shrink-0" />}>
{t('agent.add.title')}
</Button>
</div>
</PopoverContent>
</Popover>
<AddButton onClick={handleAddButtonClick} className="-mt-[1px] mb-[2px]">
{t('chat.add.assistant.title')}
</AddButton>
<AgentModal isOpen={isAgentModalOpen} onClose={onAgentModalClose} afterSubmit={afterCreate} />
</div>
)

View File

@ -717,10 +717,17 @@ const NotesPage: FC = () => {
const normalizedActivePath = activeFilePath ? normalizePathValue(activeFilePath) : undefined
if (normalizedActivePath) {
if (normalizedActivePath === sourceNode.externalPath) {
// Cancel debounced save to prevent saving to old path
debouncedSaveRef.current?.cancel()
lastFilePathRef.current = destinationPath
dispatch(setActiveFilePath(destinationPath))
} else if (sourceNode.type === 'folder' && normalizedActivePath.startsWith(`${sourceNode.externalPath}/`)) {
const suffix = normalizedActivePath.slice(sourceNode.externalPath.length)
dispatch(setActiveFilePath(`${destinationPath}${suffix}`))
const newActivePath = `${destinationPath}${suffix}`
// Cancel debounced save to prevent saving to old path
debouncedSaveRef.current?.cancel()
lastFilePathRef.current = newActivePath
dispatch(setActiveFilePath(newActivePath))
}
}

View File

@ -18,7 +18,7 @@ import { ThemeMode } from '@shared/data/preference/preferenceTypes'
import { Progress, Row, Tag } from 'antd'
import type { UpdateInfo } from 'builder-util-runtime'
import { debounce } from 'lodash'
import { Bug, FileCheck, Globe, Mail, Rss } from 'lucide-react'
import { Bug, Building2, Github, Globe, Mail, Rss } from 'lucide-react'
import { BadgeQuestionMark } from 'lucide-react'
import type { FC } from 'react'
import { useEffect, useState } from 'react'
@ -90,14 +90,8 @@ const AboutSettings: FC = () => {
await window.api.devTools.toggle()
}
const showLicense = async () => {
const { appPath } = await window.api.getAppInfo()
openSmartMinapp({
id: 'cherrystudio-license',
name: t('settings.about.license.title'),
url: `file://${appPath}/resources/cherry-studio/license.html`,
logo: AppLogo
})
const showEnterprise = async () => {
onOpenWebsite('https://cherry-ai.com/enterprise')
}
const showReleases = async () => {
@ -315,7 +309,7 @@ const AboutSettings: FC = () => {
<SettingDivider />
<SettingRow>
<SettingRowTitle>
<GithubOutlined size={18} />
<Github size={18} />
{t('settings.about.feedback.title')}
</SettingRowTitle>
<Button onClick={() => onOpenWebsite('https://github.com/CherryHQ/cherry-studio/issues/new/choose')}>
@ -325,10 +319,10 @@ const AboutSettings: FC = () => {
<SettingDivider />
<SettingRow>
<SettingRowTitle>
<FileCheck size={18} />
{t('settings.about.license.title')}
<Building2 size={18} />
{t('settings.about.enterprise.title')}
</SettingRowTitle>
<Button onClick={showLicense}>{t('settings.about.license.button')}</Button>
<Button onClick={showEnterprise}>{t('settings.about.website.button')}</Button>
</SettingRow>
<SettingDivider />
<SettingRow>

View File

@ -55,7 +55,7 @@ const PluginSettings: FC<PluginSettingsProps> = ({ agentBase }) => {
)
return (
<SettingsContainer>
<SettingsContainer className="pr-0">
<Tabs
aria-label="Plugin settings tabs"
classNames={{
@ -64,7 +64,7 @@ const PluginSettings: FC<PluginSettingsProps> = ({ agentBase }) => {
panel: 'w-full flex-1 overflow-hidden'
}}>
<Tab key="available" title={t('agent.settings.plugins.available.title')}>
<div className="flex h-full flex-col overflow-y-auto pt-4">
<div className="flex h-full flex-col overflow-y-auto pt-1 pr-2">
{errorAvailable ? (
<Card className="bg-danger-50 dark:bg-danger-900/20">
<CardBody>
@ -89,7 +89,7 @@ const PluginSettings: FC<PluginSettingsProps> = ({ agentBase }) => {
</Tab>
<Tab key="installed" title={t('agent.settings.plugins.installed.title')}>
<div className="flex h-full flex-col overflow-y-auto pt-4">
<div className="flex h-full flex-col overflow-y-auto pt-4 pr-2">
{errorInstalled ? (
<Card className="bg-danger-50 dark:bg-danger-900/20">
<CardBody>

View File

@ -169,6 +169,7 @@ export const ToolingSettings: FC<AgentToolingSettingsProps> = ({ agentBase, upda
</div>
</div>
),
centered: true,
okText: t('common.confirm'),
cancelText: t('common.cancel'),
onOk: applyChange,
@ -275,9 +276,10 @@ export const ToolingSettings: FC<AgentToolingSettingsProps> = ({ agentBase, upda
key={card.mode}
isPressable={!disabled}
isDisabled={disabled || isUpdatingMode}
shadow="none"
onPress={() => handleSelectPermissionMode(card.mode)}
className={`border ${
isSelected ? 'border-primary shadow-lg' : 'border-default-200'
isSelected ? 'border-primary' : 'border-default-200'
} ${disabled ? 'opacity-60' : ''}`}>
<CardHeader className="flex items-start justify-between gap-2">
<div className="flex flex-col">

View File

@ -1,53 +0,0 @@
import { Chip } from '@heroui/react'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
export interface CategoryFilterProps {
categories: string[]
selectedCategories: string[]
onChange: (categories: string[]) => void
}
export const CategoryFilter: FC<CategoryFilterProps> = ({ categories, selectedCategories, onChange }) => {
const { t } = useTranslation()
const isAllSelected = selectedCategories.length === 0
const handleCategoryClick = (category: string) => {
if (selectedCategories.includes(category)) {
onChange(selectedCategories.filter((c) => c !== category))
} else {
onChange([...selectedCategories, category])
}
}
const handleAllClick = () => {
onChange([])
}
return (
<div className="flex max-h-24 flex-wrap gap-2 overflow-y-auto">
<Chip
variant={isAllSelected ? 'solid' : 'bordered'}
color={isAllSelected ? 'primary' : 'default'}
onClick={handleAllClick}
className="cursor-pointer">
{t('plugins.all_categories')}
</Chip>
{categories.map((category) => {
const isSelected = selectedCategories.includes(category)
return (
<Chip
key={category}
variant={isSelected ? 'solid' : 'bordered'}
color={isSelected ? 'primary' : 'default'}
onClick={() => handleCategoryClick(category)}
className="cursor-pointer">
{category}
</Chip>
)
})}
</div>
)
}

View File

@ -56,7 +56,7 @@ export const InstalledPluginsList: FC<InstalledPluginsListProps> = ({ plugins, o
<TableColumn>{t('plugins.name')}</TableColumn>
<TableColumn>{t('plugins.type')}</TableColumn>
<TableColumn>{t('plugins.category')}</TableColumn>
<TableColumn width={100}>{t('plugins.actions')}</TableColumn>
<TableColumn align="end">{t('plugins.actions')}</TableColumn>
</TableHeader>
<TableBody>
{plugins.map((plugin) => (

View File

@ -1,11 +1,10 @@
import { Input, Pagination, Tab, Tabs } from '@heroui/react'
import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Input, Tab, Tabs } from '@heroui/react'
import type { InstalledPlugin, PluginMetadata } from '@renderer/types/plugin'
import { Search } from 'lucide-react'
import { Filter, Search } from 'lucide-react'
import type { FC } from 'react'
import { useMemo, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { CategoryFilter } from './CategoryFilter'
import { PluginCard } from './PluginCard'
import { PluginDetailModal } from './PluginDetailModal'
@ -38,10 +37,11 @@ export const PluginBrowser: FC<PluginBrowserProps> = ({
const [searchQuery, setSearchQuery] = useState('')
const [selectedCategories, setSelectedCategories] = useState<string[]>([])
const [activeType, setActiveType] = useState<PluginType>('all')
const [currentPage, setCurrentPage] = useState(1)
const [displayCount, setDisplayCount] = useState(ITEMS_PER_PAGE)
const [actioningPlugin, setActioningPlugin] = useState<string | null>(null)
const [selectedPlugin, setSelectedPlugin] = useState<PluginMetadata | null>(null)
const [isModalOpen, setIsModalOpen] = useState(false)
const observerTarget = useRef<HTMLDivElement>(null)
// Combine all plugins based on active type
const allPlugins = useMemo(() => {
@ -87,14 +87,35 @@ export const PluginBrowser: FC<PluginBrowserProps> = ({
})
}, [allPlugins, searchQuery, selectedCategories])
// Paginate filtered plugins
const paginatedPlugins = useMemo(() => {
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE
const endIndex = startIndex + ITEMS_PER_PAGE
return filteredPlugins.slice(startIndex, endIndex)
}, [filteredPlugins, currentPage])
// Display plugins based on displayCount
const displayedPlugins = useMemo(() => {
return filteredPlugins.slice(0, displayCount)
}, [filteredPlugins, displayCount])
const totalPages = Math.ceil(filteredPlugins.length / ITEMS_PER_PAGE)
const hasMore = displayCount < filteredPlugins.length
// Reset display count when filters change
useEffect(() => {
setDisplayCount(ITEMS_PER_PAGE)
}, [filteredPlugins])
// Infinite scroll observer
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && hasMore) {
setDisplayCount((prev) => prev + ITEMS_PER_PAGE)
}
},
{ threshold: 0.1 }
)
if (observerTarget.current) {
observer.observe(observerTarget.current)
}
return () => observer.disconnect()
}, [hasMore])
// Check if a plugin is installed
const isPluginInstalled = (plugin: PluginMetadata): boolean => {
@ -117,20 +138,22 @@ export const PluginBrowser: FC<PluginBrowserProps> = ({
setActioningPlugin(null)
}
// Reset to first page when filters change
// Reset display count when filters change
const handleSearchChange = (value: string) => {
setSearchQuery(value)
setCurrentPage(1)
}
const handleCategoryChange = (categories: string[]) => {
setSelectedCategories(categories)
setCurrentPage(1)
const handleCategoryChange = (keys: Set<string>) => {
// Reset if "all" selected, otherwise filter categories
if (keys.has('all') || keys.size === 0) {
setSelectedCategories([])
} else {
setSelectedCategories(Array.from(keys).filter((key) => key !== 'all'))
}
}
const handleTypeChange = (type: string | number) => {
setActiveType(type as PluginType)
setCurrentPage(1)
}
const handlePluginClick = (plugin: PluginMetadata) => {
@ -145,33 +168,76 @@ export const PluginBrowser: FC<PluginBrowserProps> = ({
return (
<div className="flex flex-col gap-4">
{/* Search Input */}
<Input
placeholder={t('plugins.search_placeholder')}
value={searchQuery}
onValueChange={handleSearchChange}
startContent={<Search className="h-4 w-4 text-default-400" />}
isClearable
classNames={{
input: 'text-small',
inputWrapper: 'h-10'
}}
/>
{/* Search and Filter */}
<div className="relative flex gap-0">
<Input
placeholder={t('plugins.search_placeholder')}
value={searchQuery}
onValueChange={handleSearchChange}
startContent={<Search className="h-4 w-4 text-default-400" />}
isClearable
size="md"
className="flex-1"
classNames={{
inputWrapper: 'pr-12'
}}
/>
<Dropdown placement="bottom-end" classNames={{ content: 'max-h-60 overflow-y-auto p-0' }}>
<DropdownTrigger>
<Button
isIconOnly
variant={selectedCategories.length > 0 ? 'flat' : 'light'}
color={selectedCategories.length > 0 ? 'primary' : 'default'}
size="sm"
className="-translate-y-1/2 absolute top-1/2 right-2 z-10">
<Filter className="h-4 w-4" />
</Button>
</DropdownTrigger>
<DropdownMenu
aria-label="Category filter"
closeOnSelect={false}
className="max-h-60 overflow-y-auto"
items={[
{ key: 'all', label: t('plugins.all_categories') },
...allCategories.map((category) => ({ key: category, label: category }))
]}>
{(item) => {
const isSelected =
item.key === 'all' ? selectedCategories.length === 0 : selectedCategories.includes(item.key)
{/* Category Filter */}
<CategoryFilter
categories={allCategories}
selectedCategories={selectedCategories}
onChange={handleCategoryChange}
/>
return (
<DropdownItem
key={item.key}
textValue={item.label}
onPress={() => {
if (item.key === 'all') {
handleCategoryChange(new Set(['all']))
} else {
const newKeys = selectedCategories.includes(item.key)
? new Set(selectedCategories.filter((c) => c !== item.key))
: new Set([...selectedCategories, item.key])
handleCategoryChange(newKeys)
}
}}
className={isSelected ? 'bg-primary-50' : ''}>
{item.label}
{isSelected && <span className="ml-2 text-primary text-sm"></span>}
</DropdownItem>
)
}}
</DropdownMenu>
</Dropdown>
</div>
{/* Type Tabs */}
<Tabs selectedKey={activeType} onSelectionChange={handleTypeChange} variant="underlined">
<Tab key="all" title={t('plugins.all_types')} />
<Tab key="agent" title={t('plugins.agents')} />
<Tab key="command" title={t('plugins.commands')} />
<Tab key="skill" title={t('plugins.skills')} />
</Tabs>
<div className="-mt-3 flex justify-center">
<Tabs selectedKey={activeType} onSelectionChange={handleTypeChange} variant="underlined">
<Tab key="all" title={t('plugins.all_types')} />
<Tab key="agent" title={t('plugins.agents')} />
<Tab key="command" title={t('plugins.commands')} />
<Tab key="skill" title={t('plugins.skills')} />
</Tabs>
</div>
{/* Result Count */}
<div className="flex items-center justify-between">
@ -179,37 +245,35 @@ export const PluginBrowser: FC<PluginBrowserProps> = ({
</div>
{/* Plugin Grid */}
{paginatedPlugins.length === 0 ? (
{displayedPlugins.length === 0 ? (
<div className="flex flex-col items-center justify-center py-12 text-center">
<p className="text-default-400">{t('plugins.no_results')}</p>
<p className="text-default-300 text-small">{t('plugins.try_different_search')}</p>
</div>
) : (
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{paginatedPlugins.map((plugin) => {
const installed = isPluginInstalled(plugin)
const isActioning = actioningPlugin === plugin.sourcePath
<>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
{displayedPlugins.map((plugin) => {
const installed = isPluginInstalled(plugin)
const isActioning = actioningPlugin === plugin.sourcePath
return (
<PluginCard
key={`${plugin.type}-${plugin.sourcePath}`}
plugin={plugin}
installed={installed}
onInstall={() => handleInstall(plugin)}
onUninstall={() => handleUninstall(plugin)}
loading={loading || isActioning}
onClick={() => handlePluginClick(plugin)}
/>
)
})}
</div>
)}
{/* Pagination */}
{totalPages > 1 && (
<div className="flex justify-center">
<Pagination total={totalPages} page={currentPage} onChange={setCurrentPage} showControls />
</div>
return (
<div key={`${plugin.type}-${plugin.sourcePath}`} className="h-full">
<PluginCard
plugin={plugin}
installed={installed}
onInstall={() => handleInstall(plugin)}
onUninstall={() => handleUninstall(plugin)}
loading={loading || isActioning}
onClick={() => handlePluginClick(plugin)}
/>
</div>
)
})}
</div>
{/* Infinite scroll trigger */}
{hasMore && <div ref={observerTarget} className="h-10" />}
</>
)}
{/* Plugin Detail Modal */}

View File

@ -1,5 +1,6 @@
import { Button, Card, CardBody, CardFooter, CardHeader, Chip, Spinner } from '@heroui/react'
import type { PluginMetadata } from '@renderer/types/plugin'
import { upperFirst } from 'lodash'
import { Download, Trash2 } from 'lucide-react'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
@ -17,15 +18,20 @@ export const PluginCard: FC<PluginCardProps> = ({ plugin, installed, onInstall,
const { t } = useTranslation()
return (
<Card className="w-full cursor-pointer transition-shadow hover:shadow-md" isPressable onPress={onClick}>
<Card
className="flex h-full w-full cursor-pointer flex-col border-[0.5px] border-default-200"
isPressable
shadow="none"
onPress={onClick}>
<CardHeader className="flex flex-col items-start gap-2 pb-2">
<div className="flex w-full items-center justify-between">
<h3 className="font-semibold text-medium">{plugin.name}</h3>
<div className="flex w-full items-center justify-between gap-2">
<h3 className="truncate font-medium text-small">{plugin.name}</h3>
<Chip
size="sm"
variant="solid"
color={plugin.type === 'agent' ? 'primary' : plugin.type === 'skill' ? 'success' : 'secondary'}>
{plugin.type}
color={plugin.type === 'agent' ? 'primary' : plugin.type === 'skill' ? 'success' : 'secondary'}
className="h-4 min-w-0 flex-shrink-0 px-0.5 text-xs">
{upperFirst(plugin.type)}
</Chip>
</div>
<Chip size="sm" variant="dot" color="default">
@ -33,7 +39,7 @@ export const PluginCard: FC<PluginCardProps> = ({ plugin, installed, onInstall,
</Chip>
</CardHeader>
<CardBody className="py-2">
<CardBody className="flex-1 py-2">
<p className="line-clamp-3 text-default-500 text-small">{plugin.description || t('plugins.no_description')}</p>
{plugin.tags && plugin.tags.length > 0 && (

View File

@ -1,5 +1,3 @@
export type { CategoryFilterProps } from './CategoryFilter'
export { CategoryFilter } from './CategoryFilter'
export type { InstalledPluginsListProps } from './InstalledPluginsList'
export { InstalledPluginsList } from './InstalledPluginsList'
export type { PluginBrowserProps } from './PluginBrowser'

View File

@ -1,9 +1,10 @@
import { loggerService } from '@logger'
import type { Span } from '@opentelemetry/api'
import AiProvider from '@renderer/aiCore'
import { ModernAiProvider } from '@renderer/aiCore'
import AiProvider from '@renderer/aiCore/legacy'
import { DEFAULT_KNOWLEDGE_DOCUMENT_COUNT, DEFAULT_KNOWLEDGE_THRESHOLD } from '@renderer/config/constant'
import { getEmbeddingMaxContext } from '@renderer/config/embedings'
import { isGeminiProvider } from '@renderer/config/providers'
import { isAzureOpenAIProvider, isGeminiProvider } from '@renderer/config/providers'
import { addSpan, endSpan } from '@renderer/services/SpanManagerService'
import store from '@renderer/store'
import type {
@ -15,6 +16,7 @@ import type {
} from '@renderer/types'
import type { Chunk } from '@renderer/types/chunk'
import { ChunkType } from '@renderer/types/chunk'
import { routeToEndpoint } from '@renderer/utils'
import type { ExtractResults } from '@renderer/utils/extract'
import { isEmpty } from 'lodash'
@ -24,9 +26,8 @@ import FileManager from './FileManager'
const logger = loggerService.withContext('RendererKnowledgeService')
export const getKnowledgeBaseParams = (base: KnowledgeBase): KnowledgeBaseParams => {
const provider = getProviderByModel(base.model)
const rerankProvider = getProviderByModel(base.rerankModel)
const aiProvider = new AiProvider(provider)
const aiProvider = new ModernAiProvider(base.model)
const rerankAiProvider = new AiProvider(rerankProvider)
// get preprocess provider from store instead of base.preprocessProvider
@ -40,12 +41,19 @@ export const getKnowledgeBaseParams = (base: KnowledgeBase): KnowledgeBaseParams
}
: base.preprocessProvider
let host = aiProvider.getBaseURL()
const actualProvider = aiProvider.getActualProvider()
let { baseURL } = routeToEndpoint(actualProvider.apiHost)
const rerankHost = rerankAiProvider.getBaseURL()
if (isGeminiProvider(provider)) {
host = host + '/v1beta/openai/'
if (isGeminiProvider(actualProvider)) {
baseURL = baseURL + '/openai'
} else if (isAzureOpenAIProvider(actualProvider)) {
baseURL = baseURL + '/v1'
}
logger.info(`Knowledge base ${base.name} using baseURL: ${baseURL}`)
let chunkSize = base.chunkSize
const maxChunkSize = getEmbeddingMaxContext(base.model.id)
@ -65,8 +73,7 @@ export const getKnowledgeBaseParams = (base: KnowledgeBase): KnowledgeBaseParams
model: base.model.id,
provider: base.model.provider,
apiKey: aiProvider.getApiKey() || 'secret',
apiVersion: provider.apiVersion,
baseURL: host
baseURL
},
chunkSize,
chunkOverlap: base.chunkOverlap,

429
yarn.lock
View File

@ -2801,7 +2801,7 @@ __metadata:
languageName: unknown
linkType: soft
"@cherrystudio/openai@npm:^6.5.0, openai@npm:@cherrystudio/openai@6.5.0":
"@cherrystudio/openai@npm:^6.5.0":
version: 6.5.0
resolution: "@cherrystudio/openai@npm:6.5.0"
peerDependencies:
@ -4286,39 +4286,6 @@ __metadata:
languageName: node
linkType: hard
"@graphql-typed-document-node/core@npm:^3.2.0":
version: 3.2.0
resolution: "@graphql-typed-document-node/core@npm:3.2.0"
peerDependencies:
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10c0/94e9d75c1f178bbae8d874f5a9361708a3350c8def7eaeb6920f2c820e82403b7d4f55b3735856d68e145e86c85cbfe2adc444fdc25519cd51f108697e99346c
languageName: node
linkType: hard
"@grpc/grpc-js@npm:^1.13.1":
version: 1.13.4
resolution: "@grpc/grpc-js@npm:1.13.4"
dependencies:
"@grpc/proto-loader": "npm:^0.7.13"
"@js-sdsl/ordered-map": "npm:^4.4.2"
checksum: 10c0/ecdb99efbe540d8b261ca53e4be224fb4683fb22c6ab1b575d2f4ca34471fc7f221b58f718001a6d157c54237cc482514766233968f5de50e358f061600a885b
languageName: node
linkType: hard
"@grpc/proto-loader@npm:^0.7.13":
version: 0.7.15
resolution: "@grpc/proto-loader@npm:0.7.15"
dependencies:
lodash.camelcase: "npm:^4.3.0"
long: "npm:^5.0.0"
protobufjs: "npm:^7.2.5"
yargs: "npm:^17.7.2"
bin:
proto-loader-gen-types: build/bin/proto-loader-gen-types.js
checksum: 10c0/514a134a724b56d73d0a202b7e02c84479da21e364547bacb2f4995ebc0d52412a1a21653add9f004ebd146c1e6eb4bcb0b8846fdfe1bfa8a98ed8f3d203da4a
languageName: node
linkType: hard
"@hello-pangea/dnd@npm:^18.0.1":
version: 18.0.1
resolution: "@hello-pangea/dnd@npm:18.0.1"
@ -7497,13 +7464,6 @@ __metadata:
languageName: node
linkType: hard
"@js-sdsl/ordered-map@npm:^4.4.2":
version: 4.4.2
resolution: "@js-sdsl/ordered-map@npm:4.4.2"
checksum: 10c0/cc7e15dc4acf6d9ef663757279600bab70533d847dcc1ab01332e9e680bd30b77cdf9ad885cc774276f51d98b05a013571c940e5b360985af5eb798dc1a2ee2b
languageName: node
linkType: hard
"@jsdevtools/ono@npm:^7.1.3":
version: 7.1.3
resolution: "@jsdevtools/ono@npm:7.1.3"
@ -7511,27 +7471,56 @@ __metadata:
languageName: node
linkType: hard
"@langchain/community@npm:^0.3.50":
version: 0.3.54
resolution: "@langchain/community@npm:0.3.54"
"@langchain/classic@npm:1.0.0":
version: 1.0.0
resolution: "@langchain/classic@npm:1.0.0"
dependencies:
"@langchain/openai": "npm:>=0.2.0 <0.7.0"
"@langchain/weaviate": "npm:^0.2.0"
"@langchain/openai": "npm:1.0.0-alpha.3"
"@langchain/textsplitters": "npm:1.0.0"
handlebars: "npm:^4.7.8"
js-yaml: "npm:^4.1.0"
jsonpointer: "npm:^5.0.1"
langsmith: "npm:^0.3.64"
openapi-types: "npm:^12.1.3"
p-retry: "npm:4"
uuid: "npm:^10.0.0"
yaml: "npm:^2.2.1"
zod: "npm:^3.25.76 || ^4"
peerDependencies:
"@langchain/core": ^1.0.0
cheerio: "*"
peggy: ^3.0.2
typeorm: "*"
dependenciesMeta:
langsmith:
optional: true
peerDependenciesMeta:
cheerio:
optional: true
peggy:
optional: true
typeorm:
optional: true
checksum: 10c0/5d222a0dcd5faa9c0c520a79e8852403f6d4c8ecc34d48a3aaeec6965e68be3b02aae97c91a3ffea0c99114884bd1cbf48551bbb8c5cd71811685778a8ac30d8
languageName: node
linkType: hard
"@langchain/community@npm:^1.0.0":
version: 1.0.0
resolution: "@langchain/community@npm:1.0.0"
dependencies:
"@langchain/classic": "npm:1.0.0"
"@langchain/openai": "npm:1.0.0"
binary-extensions: "npm:^2.2.0"
expr-eval: "npm:^2.0.2"
flat: "npm:^5.0.2"
js-yaml: "npm:^4.1.0"
langchain: "npm:>=0.2.3 <0.3.0 || >=0.3.4 <0.4.0"
langsmith: "npm:^0.3.46"
uuid: "npm:^10.0.0"
zod: "npm:^3.25.32"
zod: "npm:^3.25.76 || ^4"
peerDependencies:
"@arcjet/redact": ^v1.0.0-alpha.23
"@aws-crypto/sha256-js": ^5.0.0
"@aws-sdk/client-bedrock-agent-runtime": ^3.749.0
"@aws-sdk/client-bedrock-runtime": ^3.749.0
"@aws-sdk/client-dynamodb": ^3.749.0
"@aws-sdk/client-kendra": ^3.749.0
"@aws-sdk/client-lambda": ^3.749.0
"@aws-sdk/client-s3": ^3.749.0
"@aws-sdk/client-sagemaker-runtime": ^3.749.0
@ -7542,22 +7531,19 @@ __metadata:
"@browserbasehq/sdk": "*"
"@browserbasehq/stagehand": ^1.0.0
"@clickhouse/client": ^0.2.5
"@cloudflare/ai": "*"
"@datastax/astra-db-ts": ^1.0.0
"@elastic/elasticsearch": ^8.4.0
"@getmetal/metal-sdk": "*"
"@getzep/zep-cloud": ^1.0.6
"@getzep/zep-js": ^0.9.0
"@gomomento/sdk": ^1.51.1
"@gomomento/sdk-core": ^1.51.1
"@google-ai/generativelanguage": "*"
"@google-cloud/storage": ^6.10.1 || ^7.7.0
"@gradientai/nodejs-sdk": ^1.2.0
"@huggingface/inference": ^4.0.5
"@huggingface/transformers": ^3.5.2
"@ibm-cloud/watsonx-ai": "*"
"@lancedb/lancedb": ^0.12.0
"@langchain/core": ">=0.3.58 <0.4.0"
"@lancedb/lancedb": ^0.19.1
"@langchain/core": ^1.0.0
"@layerup/layerup-security": ^1.5.12
"@libsql/client": ^0.14.0
"@mendable/firecrawl-js": ^1.4.3
@ -7566,10 +7552,8 @@ __metadata:
"@neondatabase/serverless": "*"
"@notionhq/client": ^2.2.10
"@opensearch-project/opensearch": "*"
"@pinecone-database/pinecone": "*"
"@planetscale/database": ^1.8.0
"@premai/prem-sdk": ^0.3.25
"@qdrant/js-client-rest": ^1.15.0
"@raycast/api": ^1.55.2
"@rockset/client": ^0.9.1
"@smithy/eventstream-codec": ^2.0.5
@ -7579,7 +7563,6 @@ __metadata:
"@spider-cloud/spider-client": ^0.0.21
"@supabase/supabase-js": ^2.45.0
"@tensorflow-models/universal-sentence-encoder": "*"
"@tensorflow/tfjs-converter": "*"
"@tensorflow/tfjs-core": "*"
"@upstash/ratelimit": ^1.1.3 || ^2.0.3
"@upstash/redis": ^1.20.6
@ -7600,16 +7583,15 @@ __metadata:
closevector-common: 0.1.3
closevector-node: 0.1.6
closevector-web: 0.1.6
cohere-ai: "*"
convex: ^1.3.1
crypto-js: ^4.2.0
d3-dsv: ^2.0.0
discord.js: ^14.14.1
dria: ^0.0.3
duck-duck-scrape: ^2.2.5
epub2: ^3.0.1
faiss-node: "*"
fast-xml-parser: "*"
firebase-admin: ^11.9.0 || ^12.0.0
firebase-admin: ^11.9.0 || ^12.0.0 || ^13.0.0
google-auth-library: "*"
googleapis: "*"
hnswlib-node: ^3.0.0
@ -7621,15 +7603,14 @@ __metadata:
it-all: ^3.0.4
jsdom: "*"
jsonwebtoken: ^9.0.2
llmonitor: ^0.5.9
lodash: ^4.17.21
lunary: ^0.7.10
mammoth: ^1.6.0
mariadb: ^3.4.0
mem0ai: ^2.1.8
mongodb: ^6.17.0
mysql2: ^3.9.8
neo4j-driver: "*"
node-llama-cpp: ">=3.0.0"
notion-to-md: ^3.1.0
officeparser: ^4.0.4
openai: "*"
@ -7641,7 +7622,6 @@ __metadata:
portkey-ai: ^0.1.11
puppeteer: "*"
pyodide: ">=0.24.1 <0.27.0"
redis: "*"
replicate: "*"
sonix-speech-recognition: ^2.1.1
srt-parser-2: ^1.2.3
@ -7649,8 +7629,6 @@ __metadata:
typesense: ^1.5.3
usearch: ^1.1.1
voy-search: 0.6.2
weaviate-client: ^3.5.2
web-auth-library: ^1.0.3
word-extractor: "*"
ws: ^8.14.2
youtubei.js: "*"
@ -7659,14 +7637,8 @@ __metadata:
optional: true
"@aws-crypto/sha256-js":
optional: true
"@aws-sdk/client-bedrock-agent-runtime":
optional: true
"@aws-sdk/client-bedrock-runtime":
optional: true
"@aws-sdk/client-dynamodb":
optional: true
"@aws-sdk/client-kendra":
optional: true
"@aws-sdk/client-lambda":
optional: true
"@aws-sdk/client-s3":
@ -7687,8 +7659,6 @@ __metadata:
optional: true
"@clickhouse/client":
optional: true
"@cloudflare/ai":
optional: true
"@datastax/astra-db-ts":
optional: true
"@elastic/elasticsearch":
@ -7699,12 +7669,8 @@ __metadata:
optional: true
"@getzep/zep-js":
optional: true
"@gomomento/sdk":
optional: true
"@gomomento/sdk-core":
optional: true
"@google-ai/generativelanguage":
optional: true
"@google-cloud/storage":
optional: true
"@gradientai/nodejs-sdk":
@ -7757,8 +7723,6 @@ __metadata:
optional: true
"@tensorflow-models/universal-sentence-encoder":
optional: true
"@tensorflow/tfjs-converter":
optional: true
"@tensorflow/tfjs-core":
optional: true
"@upstash/ratelimit":
@ -7775,6 +7739,8 @@ __metadata:
optional: true
"@xata.io/client":
optional: true
"@xenova/transformers":
optional: true
"@zilliz/milvus2-sdk-node":
optional: true
apify-client:
@ -7809,12 +7775,12 @@ __metadata:
optional: true
discord.js:
optional: true
dria:
optional: true
duck-duck-scrape:
optional: true
epub2:
optional: true
faiss-node:
optional: true
fast-xml-parser:
optional: true
firebase-admin:
@ -7839,8 +7805,6 @@ __metadata:
optional: true
jsonwebtoken:
optional: true
llmonitor:
optional: true
lodash:
optional: true
lunary:
@ -7857,6 +7821,8 @@ __metadata:
optional: true
neo4j-driver:
optional: true
node-llama-cpp:
optional: true
notion-to-md:
optional: true
officeparser:
@ -7895,55 +7861,51 @@ __metadata:
optional: true
weaviate-client:
optional: true
web-auth-library:
optional: true
word-extractor:
optional: true
ws:
optional: true
youtubei.js:
optional: true
checksum: 10c0/17ba91662e644cde0b8ffa9d11d2c2de47e32b3567cee08628d316251f860f0ac874ec3b4f44012e0d93009b1779fbd514c6882fadac8c2368213b6578f59d88
checksum: 10c0/b933e0afeb54267b41579e7b8590e9279a11bb1f7434a0357f437348a79ad74e24d7c0287e1e4db85e049ee72eb97150a3a1e2134c93cf74f82b783e36be4970
languageName: node
linkType: hard
"@langchain/core@npm:0.3.44":
version: 0.3.44
resolution: "@langchain/core@npm:0.3.44"
"@langchain/core@npm:1.0.2":
version: 1.0.2
resolution: "@langchain/core@npm:1.0.2"
dependencies:
"@cfworker/json-schema": "npm:^4.0.2"
ansi-styles: "npm:^5.0.0"
camelcase: "npm:6"
decamelize: "npm:1.2.0"
js-tiktoken: "npm:^1.0.12"
langsmith: "npm:>=0.2.8 <0.4.0"
langsmith: "npm:^0.3.64"
mustache: "npm:^4.2.0"
p-queue: "npm:^6.6.2"
p-retry: "npm:4"
uuid: "npm:^10.0.0"
zod: "npm:^3.22.4"
zod-to-json-schema: "npm:^3.22.3"
checksum: 10c0/fb8d7c5760419cc9d0a3ed4f04473e103c8a27031566ba0c89438879bbd66e3d8869349f943045e86ddb33c4e8db4ae59311a3aad45e832d273b0e7d7db3f939
zod: "npm:^3.25.76 || ^4"
checksum: 10c0/2ffa73615f6e2b98b9204e73b678f10b6d18c100d78481c8dbd4f628f5b934dd3f1b1f4de793d51d2e290aa65c07d7f903d6396ac8dc24d9ed219672157a6a09
languageName: node
linkType: hard
"@langchain/core@patch:@langchain/core@npm%3A0.3.44#~/.yarn/patches/@langchain-core-npm-0.3.44-41d5c3cb0a.patch":
version: 0.3.44
resolution: "@langchain/core@patch:@langchain/core@npm%3A0.3.44#~/.yarn/patches/@langchain-core-npm-0.3.44-41d5c3cb0a.patch::version=0.3.44&hash=41dd7b"
"@langchain/core@patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch":
version: 1.0.2
resolution: "@langchain/core@patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch::version=1.0.2&hash=95b174"
dependencies:
"@cfworker/json-schema": "npm:^4.0.2"
ansi-styles: "npm:^5.0.0"
camelcase: "npm:6"
decamelize: "npm:1.2.0"
js-tiktoken: "npm:^1.0.12"
langsmith: "npm:>=0.2.8 <0.4.0"
langsmith: "npm:^0.3.64"
mustache: "npm:^4.2.0"
p-queue: "npm:^6.6.2"
p-retry: "npm:4"
uuid: "npm:^10.0.0"
zod: "npm:^3.22.4"
zod-to-json-schema: "npm:^3.22.3"
checksum: 10c0/7002581999230bc6fbdb6c2fad0eda2c1ca93a7629e28eadf1501dd0a7f785010d6b3e7841148ced186fc0fb4a84ec5f3b75850985579610e222943cc57e1649
zod: "npm:^3.25.76 || ^4"
checksum: 10c0/c83e49ef2293c49821264ee81cc7bfaac3321289dae1b1553c929bd67ee361df2a0f3c950ac24a361916349c4d66f2db2ee5a6a509c6b8be288acebb06c17a89
languageName: node
linkType: hard
@ -7961,58 +7923,53 @@ __metadata:
languageName: node
linkType: hard
"@langchain/openai@npm:0.3.16":
version: 0.3.16
resolution: "@langchain/openai@npm:0.3.16"
"@langchain/openai@npm:1.0.0":
version: 1.0.0
resolution: "@langchain/openai@npm:1.0.0"
dependencies:
js-tiktoken: "npm:^1.0.12"
openai: "npm:^4.77.0"
zod: "npm:^3.22.4"
zod-to-json-schema: "npm:^3.22.3"
openai: "npm:^6.3.0"
zod: "npm:^3.25.76 || ^4"
peerDependencies:
"@langchain/core": ">=0.2.26 <0.4.0"
checksum: 10c0/5955a02c09227d8d1d7feef26d3487cf151e2c3d36ec7550c4fe111179eb78de76befd1bd2df6a80ae4fc88676f5ebaa35d5d8788faab62972d82989ca18ec87
"@langchain/core": ^1.0.0
checksum: 10c0/788b6c996c19b7ff4a87b4005898f737d54998ce07aeab99225bd251cdfbb49a1b7a2f6340ce55f16ffb7566b3a050bb997866603a2b0acd5b22fff7910af2d0
languageName: node
linkType: hard
"@langchain/openai@npm:>=0.1.0 <0.6.0":
version: 0.5.5
resolution: "@langchain/openai@npm:0.5.5"
"@langchain/openai@npm:1.0.0-alpha.3":
version: 1.0.0-alpha.3
resolution: "@langchain/openai@npm:1.0.0-alpha.3"
dependencies:
js-tiktoken: "npm:^1.0.12"
openai: "npm:^4.87.3"
zod: "npm:^3.22.4"
zod-to-json-schema: "npm:^3.22.3"
openai: "npm:^6.3.0"
zod: "npm:^3.25.76 || ^4"
peerDependencies:
"@langchain/core": ">=0.3.39 <0.4.0"
checksum: 10c0/475c040f473f9c9270e8130c86d480f68834af5723e7e9b761c60152cafe5bc162e87856a4c654b12d6fe8f0cf99b27247b3869e4c0c79797847263523e045d4
"@langchain/core": ^1.0.0-alpha.6
checksum: 10c0/e33abd68b3f22314dcf9256e500eb166d64476dd8f000314c237e94efef47e9889b29934f0dac816ec5834bb9848fe22ea715cd49efccc5363f224a9dc2965c4
languageName: node
linkType: hard
"@langchain/openai@npm:>=0.2.0 <0.7.0":
version: 0.6.11
resolution: "@langchain/openai@npm:0.6.11"
"@langchain/openai@patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch":
version: 1.0.0
resolution: "@langchain/openai@patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch::version=1.0.0&hash=682dd3"
dependencies:
js-tiktoken: "npm:^1.0.12"
openai: "npm:5.12.2"
zod: "npm:^3.25.32"
openai: "npm:^6.3.0"
zod: "npm:^3.25.76 || ^4"
peerDependencies:
"@langchain/core": ">=0.3.68 <0.4.0"
checksum: 10c0/d95bc7f4f4a66c82f031800d1196d1e13a536b3f647480209d8432bb4d2ebfbc53dddb43d86e6d2de0f4d94050ca4b8002e50ab1813552faba24d4045c00feda
"@langchain/core": ^1.0.0
checksum: 10c0/93503a3322af417f2209923ecb01fb8f94946d0806007eacb94fd8b44fa4f2976e2ef84094b4e462b320886bcef8d59f12fa109c8bf654e2dd98dd9c8cd073be
languageName: node
linkType: hard
"@langchain/openai@patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch":
version: 0.3.16
resolution: "@langchain/openai@patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch::version=0.3.16&hash=642f39"
"@langchain/textsplitters@npm:1.0.0":
version: 1.0.0
resolution: "@langchain/textsplitters@npm:1.0.0"
dependencies:
js-tiktoken: "npm:^1.0.12"
openai: "npm:^4.77.0"
zod: "npm:^3.22.4"
zod-to-json-schema: "npm:^3.22.3"
peerDependencies:
"@langchain/core": ">=0.2.26 <0.4.0"
checksum: 10c0/2106626e01e1865ad08b647d155bee86caf83620040a00d3406ec0a7d34650e393a7bbf7cfbbc3b45958ddeaf29055f6e1fe5b61d85da07a87e89ccbc5b6c4b6
"@langchain/core": ^1.0.0
checksum: 10c0/733039261456099f71c282c64a0f36c8283547d3db1c16218f0a67bd80837c73a70bf0c60072c5b496257c6b2d4eff84d88a1a9a4daa8f798f5f573686eb0dbb
languageName: node
linkType: hard
@ -8027,18 +7984,6 @@ __metadata:
languageName: node
linkType: hard
"@langchain/weaviate@npm:^0.2.0":
version: 0.2.2
resolution: "@langchain/weaviate@npm:0.2.2"
dependencies:
uuid: "npm:^10.0.0"
weaviate-client: "npm:^3.5.2"
peerDependencies:
"@langchain/core": ">=0.2.21 <0.4.0"
checksum: 10c0/f1ec809e5b6ba6c14efeea93d6c7a8e015c2f9eaaa732884ba25d32cb6517117e38cddb6ec662358a40246523309f07e5f3114d3067e03eb43af168a43cc6405
languageName: node
linkType: hard
"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.0.2, @lezer/common@npm:^1.0.3, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0, @lezer/common@npm:^1.2.1":
version: 1.2.3
resolution: "@lezer/common@npm:1.2.3"
@ -17235,7 +17180,9 @@ __metadata:
"@google/genai": "patch:@google/genai@npm%3A1.0.1#~/.yarn/patches/@google-genai-npm-1.0.1-e26f0f9af7.patch"
"@hello-pangea/dnd": "npm:^18.0.1"
"@heroui/react": "npm:^2.8.3"
"@langchain/community": "npm:^0.3.50"
"@langchain/community": "npm:^1.0.0"
"@langchain/core": "patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch"
"@langchain/openai": "patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch"
"@libsql/client": "npm:0.14.0"
"@libsql/win32-x64-msvc": "npm:^0.4.7"
"@mistralai/mistralai": "npm:^1.7.5"
@ -17494,13 +17441,6 @@ __metadata:
languageName: node
linkType: hard
"abort-controller-x@npm:^0.4.0, abort-controller-x@npm:^0.4.3":
version: 0.4.3
resolution: "abort-controller-x@npm:0.4.3"
checksum: 10c0/8091b5c9279c304890e4e9cc90601947790846b7b2c149bb322a25e873eb3db060ef3da74a93b6fe40ccea41c3962fc4b175468a0ecdf4c4bb6421023ad9d71e
languageName: node
linkType: hard
"abort-controller@npm:^3.0.0":
version: 3.0.0
resolution: "abort-controller@npm:3.0.0"
@ -23244,25 +23184,6 @@ __metadata:
languageName: node
linkType: hard
"graphql-request@npm:^6.1.0":
version: 6.1.0
resolution: "graphql-request@npm:6.1.0"
dependencies:
"@graphql-typed-document-node/core": "npm:^3.2.0"
cross-fetch: "npm:^3.1.5"
peerDependencies:
graphql: 14 - 16
checksum: 10c0/f8167925a110e8e1de93d56c14245e7e64391dc8dce5002dd01bf24a3059f345d4ca1bb6ce2040e2ec78264211b0704e75da3e63984f0f74d2042f697a4e8cc6
languageName: node
linkType: hard
"graphql@npm:^16.11.0":
version: 16.11.0
resolution: "graphql@npm:16.11.0"
checksum: 10c0/124da7860a2292e9acf2fed0c71fc0f6a9b9ca865d390d112bdd563c1f474357141501c12891f4164fe984315764736ad67f705219c62f7580681d431a85db88
languageName: node
linkType: hard
"gray-matter@npm:^4.0.3":
version: 4.0.3
resolution: "gray-matter@npm:4.0.3"
@ -23292,6 +23213,24 @@ __metadata:
languageName: node
linkType: hard
"handlebars@npm:^4.7.8":
version: 4.7.8
resolution: "handlebars@npm:4.7.8"
dependencies:
minimist: "npm:^1.2.5"
neo-async: "npm:^2.6.2"
source-map: "npm:^0.6.1"
uglify-js: "npm:^3.1.4"
wordwrap: "npm:^1.0.0"
dependenciesMeta:
uglify-js:
optional: true
bin:
handlebars: bin/handlebars
checksum: 10c0/7aff423ea38a14bb379316f3857fe0df3c5d66119270944247f155ba1f08e07a92b340c58edaa00cfe985c21508870ee5183e0634dcb53dd405f35c93ef7f10d
languageName: node
linkType: hard
"has-flag@npm:^4.0.0":
version: 4.0.0
resolution: "has-flag@npm:4.0.0"
@ -24762,7 +24701,7 @@ __metadata:
languageName: node
linkType: hard
"langchain@npm:>=0.2.3 <0.3.0 || >=0.3.4 <0.4.0, langchain@npm:^0.3.8":
"langchain@npm:^0.3.8":
version: 0.3.21
resolution: "langchain@npm:0.3.21"
dependencies:
@ -24869,9 +24808,9 @@ __metadata:
languageName: node
linkType: hard
"langsmith@npm:^0.3.46":
version: 0.3.67
resolution: "langsmith@npm:0.3.67"
"langsmith@npm:^0.3.64":
version: 0.3.76
resolution: "langsmith@npm:0.3.76"
dependencies:
"@types/uuid": "npm:^10.0.0"
chalk: "npm:^4.1.2"
@ -24894,7 +24833,7 @@ __metadata:
optional: true
openai:
optional: true
checksum: 10c0/e22ec270764632a110dc77cb7fd018cd59e905fda529fba4c056e5c1ceda5a2746dc5e07e81b5ef2c949c0a712ffe5dd99721b465ba08d699e9a56e42b041bf4
checksum: 10c0/3e76e69add862e6031a385edd2cf2e963b0db9e606d582a46fd77f05e49d8b9746eacdb5bc6b1fba2be2744f94e9d4e2d055c3ba0f394d6bc6e0f85f9cf4df77
languageName: node
linkType: hard
@ -25295,13 +25234,6 @@ __metadata:
languageName: node
linkType: hard
"lodash.camelcase@npm:^4.3.0":
version: 4.3.0
resolution: "lodash.camelcase@npm:4.3.0"
checksum: 10c0/fcba15d21a458076dd309fce6b1b4bf611d84a0ec252cb92447c948c533ac250b95d2e00955801ebc367e5af5ed288b996d75d37d2035260a937008e14eaf432
languageName: node
linkType: hard
"lodash.curry@npm:^4.0.1":
version: 4.1.1
resolution: "lodash.curry@npm:4.1.1"
@ -25395,7 +25327,7 @@ __metadata:
languageName: node
linkType: hard
"long@npm:^5.0.0, long@npm:^5.3.2":
"long@npm:^5.0.0":
version: 5.3.2
resolution: "long@npm:5.3.2"
checksum: 10c0/7130fe1cbce2dca06734b35b70d380ca3f70271c7f8852c922a7c62c86c4e35f0c39290565eca7133c625908d40e126ac57c02b1b1a4636b9457d77e1e60b981
@ -27178,6 +27110,13 @@ __metadata:
languageName: node
linkType: hard
"neo-async@npm:^2.6.2":
version: 2.6.2
resolution: "neo-async@npm:2.6.2"
checksum: 10c0/c2f5a604a54a8ec5438a342e1f356dff4bc33ccccdb6dc668d94fe8e5eccfc9d2c2eea6064b0967a767ba63b33763f51ccf2cd2441b461a7322656c1f06b3f5d
languageName: node
linkType: hard
"nested-property@npm:^4.0.0":
version: 4.0.0
resolution: "nested-property@npm:4.0.0"
@ -27192,36 +27131,6 @@ __metadata:
languageName: node
linkType: hard
"nice-grpc-client-middleware-retry@npm:^3.1.11":
version: 3.1.11
resolution: "nice-grpc-client-middleware-retry@npm:3.1.11"
dependencies:
abort-controller-x: "npm:^0.4.0"
nice-grpc-common: "npm:^2.0.2"
checksum: 10c0/0d9c704a1a5c399f8243753c75a7db86c9eb414ca5bae1920cd66f60ea235190c5ea667daa1c161345ae4bf86817085f3add4438ca67f9144c5e57b9542ef5c5
languageName: node
linkType: hard
"nice-grpc-common@npm:^2.0.2":
version: 2.0.2
resolution: "nice-grpc-common@npm:2.0.2"
dependencies:
ts-error: "npm:^1.0.6"
checksum: 10c0/9eb8a44e1a5c7051cf0e4a06dc7fda2c7abb6cfbcbb746806418c2c58f3f0075212c61bbce54239a204e6a552065f0fa92dfedcf3402dc16220b2ffaee4ab857
languageName: node
linkType: hard
"nice-grpc@npm:^2.1.12":
version: 2.1.12
resolution: "nice-grpc@npm:2.1.12"
dependencies:
"@grpc/grpc-js": "npm:^1.13.1"
abort-controller-x: "npm:^0.4.0"
nice-grpc-common: "npm:^2.0.2"
checksum: 10c0/7a8e720a42a0297315bfafa0c93297e36d341927eaddae9e5a06c8ea2863b16d701a642dc9610e3e768d19cc9569afe5b99de2dfaeb1648042d32a139a7ba773
languageName: node
linkType: hard
"node-abi@npm:4.12.0":
version: 4.12.0
resolution: "node-abi@npm:4.12.0"
@ -27625,6 +27534,23 @@ __metadata:
languageName: node
linkType: hard
"openai@npm:^6.3.0":
version: 6.7.0
resolution: "openai@npm:6.7.0"
peerDependencies:
ws: ^8.18.0
zod: ^3.25 || ^4.0
peerDependenciesMeta:
ws:
optional: true
zod:
optional: true
bin:
openai: bin/cli
checksum: 10c0/dbee766f28308235fdc370a325f50ab4de2d869a832f78fbb7cb81ede0518b55c6e6ab70cd0fe9cdc7a3fd948ef56a8d8f23d0a86fef21ac7ed97e35ef547c92
languageName: node
linkType: hard
"openapi-types@npm:^12.1.3":
version: 12.1.3
resolution: "openapi-types@npm:12.1.3"
@ -28783,26 +28709,6 @@ __metadata:
languageName: node
linkType: hard
"protobufjs@npm:^7.2.5":
version: 7.5.4
resolution: "protobufjs@npm:7.5.4"
dependencies:
"@protobufjs/aspromise": "npm:^1.1.2"
"@protobufjs/base64": "npm:^1.1.2"
"@protobufjs/codegen": "npm:^2.0.4"
"@protobufjs/eventemitter": "npm:^1.1.0"
"@protobufjs/fetch": "npm:^1.1.0"
"@protobufjs/float": "npm:^1.0.2"
"@protobufjs/inquire": "npm:^1.1.0"
"@protobufjs/path": "npm:^1.1.2"
"@protobufjs/pool": "npm:^1.1.0"
"@protobufjs/utf8": "npm:^1.1.0"
"@types/node": "npm:>=13.7.0"
long: "npm:^5.0.0"
checksum: 10c0/913b676109ffb3c05d3d31e03a684e569be91f3bba8613da4a683d69d9dba948daa2afd7d2e7944d1aa6c417890c35d9d9a8883c1160affafb0f9670d59ef722
languageName: node
linkType: hard
"protobufjs@npm:^7.3.0":
version: 7.5.2
resolution: "protobufjs@npm:7.5.2"
@ -31460,7 +31366,7 @@ __metadata:
languageName: node
linkType: hard
"source-map@npm:^0.6.0, source-map@npm:~0.6.1":
"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.1":
version: 0.6.1
resolution: "source-map@npm:0.6.1"
checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011
@ -32620,13 +32526,6 @@ __metadata:
languageName: node
linkType: hard
"ts-error@npm:^1.0.6":
version: 1.0.6
resolution: "ts-error@npm:1.0.6"
checksum: 10c0/c46994b0b88eae75d676ab18edcdb3e6c309abb39d8169c2d15286d10f4fc7bfc58c537a81f3efe24701e840247b5e79ac8e21a7335327811a07cfc33f69a72f
languageName: node
linkType: hard
"ts-pattern@npm:^5.7.0":
version: 5.7.0
resolution: "ts-pattern@npm:5.7.0"
@ -33006,6 +32905,15 @@ __metadata:
languageName: node
linkType: hard
"uglify-js@npm:^3.1.4":
version: 3.19.3
resolution: "uglify-js@npm:3.19.3"
bin:
uglifyjs: bin/uglifyjs
checksum: 10c0/83b0a90eca35f778e07cad9622b80c448b6aad457c9ff8e568afed978212b42930a95f9e1be943a1ffa4258a3340fbb899f41461131c05bb1d0a9c303aed8479
languageName: node
linkType: hard
"unbzip2-stream@npm:^1.0.9":
version: 1.4.3
resolution: "unbzip2-stream@npm:1.4.3"
@ -33790,22 +33698,6 @@ __metadata:
languageName: node
linkType: hard
"weaviate-client@npm:^3.5.2":
version: 3.8.1
resolution: "weaviate-client@npm:3.8.1"
dependencies:
abort-controller-x: "npm:^0.4.3"
graphql: "npm:^16.11.0"
graphql-request: "npm:^6.1.0"
long: "npm:^5.3.2"
nice-grpc: "npm:^2.1.12"
nice-grpc-client-middleware-retry: "npm:^3.1.11"
nice-grpc-common: "npm:^2.0.2"
uuid: "npm:^9.0.1"
checksum: 10c0/a001a1b0eaf9b1344f1d7963520fe43b83bfc1a34efbe1d1b8748ae7b042f95d502cc6d31f681969865d7df90cf54a902233c3dea379aac6df40cb4ed704f665
languageName: node
linkType: hard
"web-namespaces@npm:^2.0.0":
version: 2.0.1
resolution: "web-namespaces@npm:2.0.1"
@ -34058,6 +33950,13 @@ __metadata:
languageName: node
linkType: hard
"wordwrap@npm:^1.0.0":
version: 1.0.0
resolution: "wordwrap@npm:1.0.0"
checksum: 10c0/7ed2e44f3c33c5c3e3771134d2b0aee4314c9e49c749e37f464bf69f2bcdf0cbf9419ca638098e2717cff4875c47f56a007532f6111c3319f557a2ca91278e92
languageName: node
linkType: hard
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0":
version: 7.0.0
resolution: "wrap-ansi@npm:7.0.0"
@ -34310,7 +34209,7 @@ __metadata:
languageName: node
linkType: hard
"yargs@npm:17.7.2, yargs@npm:^17.0.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2, yargs@npm:^17.7.2":
"yargs@npm:17.7.2, yargs@npm:^17.0.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2":
version: 17.7.2
resolution: "yargs@npm:17.7.2"
dependencies:
@ -34443,10 +34342,10 @@ __metadata:
languageName: node
linkType: hard
"zod@npm:^3.25.32":
version: 3.25.76
resolution: "zod@npm:3.25.76"
checksum: 10c0/5718ec35e3c40b600316c5b4c5e4976f7fee68151bc8f8d90ec18a469be9571f072e1bbaace10f1e85cf8892ea12d90821b200e980ab46916a6166a4260a983c
"zod@npm:^3.25.76 || ^4":
version: 4.1.12
resolution: "zod@npm:4.1.12"
checksum: 10c0/b64c1feb19e99d77075261eaf613e0b2be4dfcd3551eff65ad8b4f2a079b61e379854d066f7d447491fcf193f45babd8095551a9d47973d30b46b6d8e2c46774
languageName: node
linkType: hard