From 3cbe45fc8d835abd8ee33e4e53c2578f14759c01 Mon Sep 17 00:00:00 2001 From: Yanhua Zheng <36413792+rainoffallingstar@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:10:08 +0800 Subject: [PATCH 01/13] docs(README): Add PaperMaterial Theme in README (#3955) * Add PaperMaterial Theme * Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 660395d3f..5335146c9 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,9 @@ https://docs.cherry-ai.com # 🌈 Theme -Theme Gallery: https://cherrycss.com -Aero Theme: https://github.com/hakadao/CherryStudio-Aero +- Theme Gallery: https://cherrycss.com +- Aero Theme: https://github.com/hakadao/CherryStudio-Aero +- PaperMaterial Theme: https://github.com/rainoffallingstar/CherryStudio-PaperMaterial Welcome PR for more themes From 0d320120a454934603228ace84514cfa6a1d7cfc Mon Sep 17 00:00:00 2001 From: purefkh Date: Wed, 26 Mar 2025 16:17:07 +0800 Subject: [PATCH 02/13] fix(UI): exclude rerank models from mention dropdown (#3958) --- src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx index 71eb504c7..7cffbaca3 100644 --- a/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx @@ -1,6 +1,6 @@ import { PushpinOutlined } from '@ant-design/icons' import ModelTags from '@renderer/components/ModelTags' -import { getModelLogo, isEmbeddingModel } from '@renderer/config/models' +import { getModelLogo, isEmbeddingModel, isRerankModel } from '@renderer/config/models' import db from '@renderer/databases' import { useProviders } from '@renderer/hooks/useProvider' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' @@ -67,6 +67,7 @@ const MentionModelsButton: FC = ({ mentionModels, onMentionModel: onSelec .map((p) => { const filteredModels = sortBy(p.models, ['group', 'name']) .filter((m) => !isEmbeddingModel(m)) + .filter((m) => !isRerankModel(m)) // Filter out pinned models from regular groups .filter((m) => !pinnedModels.includes(getModelUniqId(m))) // Filter by search text From cc32c36222b798e09f24dc2af1e37ec68ff51fc0 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Wed, 26 Mar 2025 18:13:58 +0800 Subject: [PATCH 03/13] build: replace @llm-tools/embedjs with @cherrystudio/embedjs --- ...oader-markdown-npm-0.1.28-81647ffac6.patch | 13 - ...-tools-embedjs-npm-0.1.28-8e4393fa2d.patch | 158 ------- electron.vite.config.ts | 20 +- package.json | 22 +- src/main/embeddings/Embeddings.ts | 2 +- src/main/embeddings/EmbeddingsFactory.ts | 6 +- src/main/embeddings/VoyageEmbeddings.ts | 2 +- src/main/loader/draftsExportLoader.ts | 2 +- src/main/loader/epubLoader.ts | 4 +- src/main/loader/index.ts | 6 +- src/main/loader/odLoader.ts | 4 +- src/main/reranker/BaseReranker.ts | 2 +- src/main/reranker/DefaultReranker.ts | 2 +- src/main/reranker/JinaReranker.ts | 2 +- src/main/reranker/Reranker.ts | 2 +- src/main/reranker/SiliconFlowReranker.ts | 2 +- src/main/reranker/VoyageReranker.ts | 2 +- src/main/services/KnowledgeService.ts | 10 +- src/preload/index.d.ts | 2 +- src/preload/index.ts | 2 +- .../components/KnowledgeSearchPopup.tsx | 2 +- src/renderer/src/services/KnowledgeService.ts | 2 +- yarn.lock | 393 ++++++++---------- 23 files changed, 230 insertions(+), 432 deletions(-) delete mode 100644 .yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch delete mode 100644 .yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch diff --git a/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch b/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch deleted file mode 100644 index 106b45a12..000000000 --- a/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/markdown-loader.js b/src/markdown-loader.js -index eaf30b114a273e68abbb92c8b07018495e63f4cb..4b06519bdb51845e4693fe877da9de01c7a81039 100644 ---- a/src/markdown-loader.js -+++ b/src/markdown-loader.js -@@ -21,7 +21,7 @@ export class MarkdownLoader extends BaseLoader { - ? (await getSafe(this.filePathOrUrl, { format: 'buffer' })).body - : await streamToBuffer(fs.createReadStream(this.filePathOrUrl)); - this.debug('MarkdownLoader stream created'); -- const result = micromark(buffer, { extensions: [gfm(), mdxJsx()], htmlExtensions: [gfmHtml()] }); -+ const result = micromark(buffer, { extensions: [gfm()], htmlExtensions: [gfmHtml()] }); - this.debug('Markdown parsed...'); - const webLoader = new WebLoader({ - urlOrContent: result, diff --git a/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch b/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch deleted file mode 100644 index 153681e5e..000000000 --- a/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch +++ /dev/null @@ -1,158 +0,0 @@ -diff --git a/src/loaders/local-path-loader.d.ts b/src/loaders/local-path-loader.d.ts -index 48c20e68c469cd309be2dc8f28e44c1bd04a26e9..1c16d83bcbf9b7140292793d6cbb8c04281949d9 100644 ---- a/src/loaders/local-path-loader.d.ts -+++ b/src/loaders/local-path-loader.d.ts -@@ -4,8 +4,10 @@ export declare class LocalPathLoader extends BaseLoader<{ - }> { - private readonly debug; - private readonly path; -- constructor({ path }: { -+ constructor({ path, chunkSize, chunkOverlap }: { - path: string; -+ chunkSize?: number; -+ chunkOverlap?: number; - }); - getUnfilteredChunks(): AsyncGenerator<{ - metadata: { -diff --git a/src/loaders/local-path-loader.js b/src/loaders/local-path-loader.js -index 4cf8a6bd1d890244c8ec49d4a05ee3bd58861c79..ec8215b01195a21ef20f3c5d56ecc99f186bb596 100644 ---- a/src/loaders/local-path-loader.js -+++ b/src/loaders/local-path-loader.js -@@ -8,8 +8,8 @@ import { BaseLoader } from '@llm-tools/embedjs-interfaces'; - export class LocalPathLoader extends BaseLoader { - debug = createDebugMessages('embedjs:loader:LocalPathLoader'); - path; -- constructor({ path }) { -- super(`LocalPathLoader_${md5(path)}`, { path }); -+ constructor({ path, chunkSize, chunkOverlap }) { -+ super(`LocalPathLoader_${md5(path)}`, { path }, chunkSize ?? 1000, chunkOverlap ?? 0); - this.path = path; - } - async *getUnfilteredChunks() { -@@ -36,10 +36,12 @@ export class LocalPathLoader extends BaseLoader { - const extension = currentPath.split('.').pop().toLowerCase(); - if (extension === 'md' || extension === 'mdx') - mime = 'text/markdown'; -+ if (extension === 'txt') -+ mime = 'text/plain'; - this.debug(`File '${this.path}' mime type updated to 'text/markdown'`); - } - try { -- const loader = await createLoaderFromMimeType(currentPath, mime); -+ const loader = await createLoaderFromMimeType(currentPath, mime, this.chunkSize, this.chunkOverlap); - for await (const result of await loader.getUnfilteredChunks()) { - yield { - pageContent: result.pageContent, -diff --git a/src/util/mime.d.ts b/src/util/mime.d.ts -index 57f56a1b8edc98366af9f84d671676c41c2f01ca..14be3b5727cff6eb1978838045e9a788f8f53bfb 100644 ---- a/src/util/mime.d.ts -+++ b/src/util/mime.d.ts -@@ -1,2 +1,2 @@ - import { BaseLoader } from '@llm-tools/embedjs-interfaces'; --export declare function createLoaderFromMimeType(loaderData: string, mimeType: string): Promise; -+export declare function createLoaderFromMimeType(loaderData: string, mimeType: string, chunkSize?: number, chunkOverlap?: number): Promise; -diff --git a/src/util/mime.js b/src/util/mime.js -index b6426a859968e2bf6206795f70333e90ae27aeb7..16ae2adb863f8d7abfa757f1c5cc39f6bb1c44fa 100644 ---- a/src/util/mime.js -+++ b/src/util/mime.js -@@ -1,7 +1,9 @@ - import mime from 'mime'; - import createDebugMessages from 'debug'; - import { TextLoader } from '../loaders/text-loader.js'; --export async function createLoaderFromMimeType(loaderData, mimeType) { -+import fs from 'node:fs' -+ -+export async function createLoaderFromMimeType(loaderData, mimeType, chunkSize, chunkOverlap) { - createDebugMessages('embedjs:util:createLoaderFromMimeType')(`Incoming mime type '${mimeType}'`); - switch (mimeType) { - case 'application/msword': -@@ -10,7 +12,7 @@ export async function createLoaderFromMimeType(loaderData, mimeType) { - throw new Error('Package `@llm-tools/embedjs-loader-msoffice` needs to be installed to load docx files'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported DocxLoader'); -- return new DocxLoader({ filePathOrUrl: loaderData }); -+ return new DocxLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); - } - case 'application/vnd.ms-excel': - case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { -@@ -18,21 +20,21 @@ export async function createLoaderFromMimeType(loaderData, mimeType) { - throw new Error('Package `@llm-tools/embedjs-loader-msoffice` needs to be installed to load excel files'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported ExcelLoader'); -- return new ExcelLoader({ filePathOrUrl: loaderData }); -+ return new ExcelLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); - } - case 'application/pdf': { - const { PdfLoader } = await import('@llm-tools/embedjs-loader-pdf').catch(() => { - throw new Error('Package `@llm-tools/embedjs-loader-pdf` needs to be installed to load PDF files'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported PdfLoader'); -- return new PdfLoader({ filePathOrUrl: loaderData }); -+ return new PdfLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); - } - case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': { - const { PptLoader } = await import('@llm-tools/embedjs-loader-msoffice').catch(() => { - throw new Error('Package `@llm-tools/embedjs-loader-msoffice` needs to be installed to load pptx files'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported PptLoader'); -- return new PptLoader({ filePathOrUrl: loaderData }); -+ return new PptLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); - } - case 'text/plain': { - const fineType = mime.getType(loaderData); -@@ -42,24 +44,24 @@ export async function createLoaderFromMimeType(loaderData, mimeType) { - throw new Error('Package `@llm-tools/embedjs-loader-csv` needs to be installed to load CSV files'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported CsvLoader'); -- return new CsvLoader({ filePathOrUrl: loaderData }); -+ return new CsvLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); - } -- else -- return new TextLoader({ text: loaderData }); -+ const content = fs.readFileSync(loaderData, 'utf-8'); -+ return new TextLoader({ text: content, chunkSize, chunkOverlap }); - } - case 'application/csv': { - const { CsvLoader } = await import('@llm-tools/embedjs-loader-csv').catch(() => { - throw new Error('Package `@llm-tools/embedjs-loader-csv` needs to be installed to load CSV files'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported CsvLoader'); -- return new CsvLoader({ filePathOrUrl: loaderData }); -+ return new CsvLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); - } - case 'text/html': { - const { WebLoader } = await import('@llm-tools/embedjs-loader-web').catch(() => { - throw new Error('Package `@llm-tools/embedjs-loader-web` needs to be installed to load web documents'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported WebLoader'); -- return new WebLoader({ urlOrContent: loaderData }); -+ return new WebLoader({ urlOrContent: loaderData, chunkSize, chunkOverlap }); - } - case 'text/xml': { - const { SitemapLoader } = await import('@llm-tools/embedjs-loader-sitemap').catch(() => { -@@ -67,14 +69,14 @@ export async function createLoaderFromMimeType(loaderData, mimeType) { - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported SitemapLoader'); - if (await SitemapLoader.test(loaderData)) { -- return new SitemapLoader({ url: loaderData }); -+ return new SitemapLoader({ url: loaderData, chunkSize, chunkOverlap }); - } - //This is not a Sitemap but is still XML - const { XmlLoader } = await import('@llm-tools/embedjs-loader-xml').catch(() => { - throw new Error('Package `@llm-tools/embedjs-loader-xml` needs to be installed to load XML documents'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported XmlLoader'); -- return new XmlLoader({ filePathOrUrl: loaderData }); -+ return new XmlLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); - } - case 'text/x-markdown': - case 'text/markdown': { -@@ -82,7 +84,7 @@ export async function createLoaderFromMimeType(loaderData, mimeType) { - throw new Error('Package `@llm-tools/embedjs-loader-markdown` needs to be installed to load markdown files'); - }); - createDebugMessages('embedjs:util:createLoaderFromMimeType')('Dynamically imported MarkdownLoader'); -- return new MarkdownLoader({ filePathOrUrl: loaderData }); -+ return new MarkdownLoader({ filePathOrUrl: loaderData, chunkSize, chunkOverlap }); - } - case 'image/png': - case 'image/jpeg': { diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 7ba2b7ee9..8c9efbe68 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -12,16 +12,16 @@ export default defineConfig({ plugins: [ externalizeDepsPlugin({ exclude: [ - '@llm-tools/embedjs', - '@llm-tools/embedjs-openai', - '@llm-tools/embedjs-loader-web', - '@llm-tools/embedjs-loader-markdown', - '@llm-tools/embedjs-loader-msoffice', - '@llm-tools/embedjs-loader-xml', - '@llm-tools/embedjs-loader-pdf', - '@llm-tools/embedjs-loader-sitemap', - '@llm-tools/embedjs-libsql', - '@llm-tools/embedjs-loader-image', + '@cherrystudio/embedjs', + '@cherrystudio/embedjs-openai', + '@cherrystudio/embedjs-loader-web', + '@cherrystudio/embedjs-loader-markdown', + '@cherrystudio/embedjs-loader-msoffice', + '@cherrystudio/embedjs-loader-xml', + '@cherrystudio/embedjs-loader-pdf', + '@cherrystudio/embedjs-loader-sitemap', + '@cherrystudio/embedjs-libsql', + '@cherrystudio/embedjs-loader-image', 'p-queue', 'webdav' ] diff --git a/package.json b/package.json index ca7dd4f7d..36ae2c7f6 100644 --- a/package.json +++ b/package.json @@ -50,20 +50,21 @@ "prepare": "husky" }, "dependencies": { + "@cherrystudio/embedjs": "^0.1.28", + "@cherrystudio/embedjs-libsql": "^0.1.28", + "@cherrystudio/embedjs-loader-csv": "^0.1.28", + "@cherrystudio/embedjs-loader-image": "^0.1.28", + "@cherrystudio/embedjs-loader-markdown": "^0.1.28", + "@cherrystudio/embedjs-loader-msoffice": "^0.1.28", + "@cherrystudio/embedjs-loader-pdf": "^0.1.28", + "@cherrystudio/embedjs-loader-sitemap": "^0.1.28", + "@cherrystudio/embedjs-loader-web": "^0.1.28", + "@cherrystudio/embedjs-loader-xml": "^0.1.28", + "@cherrystudio/embedjs-openai": "^0.1.28", "@electron-toolkit/utils": "^3.0.0", "@electron/notarize": "^2.5.0", "@google/generative-ai": "^0.21.0", "@langchain/community": "^0.3.36", - "@llm-tools/embedjs": "patch:@llm-tools/embedjs@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch", - "@llm-tools/embedjs-libsql": "^0.1.28", - "@llm-tools/embedjs-loader-csv": "^0.1.28", - "@llm-tools/embedjs-loader-markdown": "patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch", - "@llm-tools/embedjs-loader-msoffice": "^0.1.28", - "@llm-tools/embedjs-loader-pdf": "^0.1.28", - "@llm-tools/embedjs-loader-sitemap": "^0.1.28", - "@llm-tools/embedjs-loader-web": "^0.1.28", - "@llm-tools/embedjs-loader-xml": "^0.1.28", - "@llm-tools/embedjs-openai": "^0.1.28", "@modelcontextprotocol/sdk": "patch:@modelcontextprotocol/sdk@npm%3A1.6.1#~/.yarn/patches/@modelcontextprotocol-sdk-npm-1.6.1-b46313efe7.patch", "@notionhq/client": "^2.2.15", "@tryfabric/martian": "^1.2.4", @@ -102,7 +103,6 @@ "@google/genai": "^0.4.0", "@hello-pangea/dnd": "^16.6.0", "@kangfenmao/keyv-storage": "^0.1.0", - "@llm-tools/embedjs-loader-image": "^0.1.28", "@notionhq/client": "^2.2.15", "@reduxjs/toolkit": "^2.2.5", "@tavily/core": "patch:@tavily/core@npm%3A0.3.1#~/.yarn/patches/@tavily-core-npm-0.3.1-fe69bf2bea.patch", diff --git a/src/main/embeddings/Embeddings.ts b/src/main/embeddings/Embeddings.ts index 1cbbce9ee..cf354450e 100644 --- a/src/main/embeddings/Embeddings.ts +++ b/src/main/embeddings/Embeddings.ts @@ -1,4 +1,4 @@ -import type { BaseEmbeddings } from '@llm-tools/embedjs-interfaces' +import type { BaseEmbeddings } from '@cherrystudio/embedjs-interfaces' import { KnowledgeBaseParams } from '@types' import EmbeddingsFactory from './EmbeddingsFactory' diff --git a/src/main/embeddings/EmbeddingsFactory.ts b/src/main/embeddings/EmbeddingsFactory.ts index 6524f9b97..5924d00d7 100644 --- a/src/main/embeddings/EmbeddingsFactory.ts +++ b/src/main/embeddings/EmbeddingsFactory.ts @@ -1,6 +1,6 @@ -import type { BaseEmbeddings } from '@llm-tools/embedjs-interfaces' -import { OpenAiEmbeddings } from '@llm-tools/embedjs-openai' -import { AzureOpenAiEmbeddings } from '@llm-tools/embedjs-openai/src/azure-openai-embeddings' +import type { BaseEmbeddings } from '@cherrystudio/embedjs-interfaces' +import { OpenAiEmbeddings } from '@cherrystudio/embedjs-openai' +import { AzureOpenAiEmbeddings } from '@cherrystudio/embedjs-openai/src/azure-openai-embeddings' import { getInstanceName } from '@main/utils' import { KnowledgeBaseParams } from '@types' diff --git a/src/main/embeddings/VoyageEmbeddings.ts b/src/main/embeddings/VoyageEmbeddings.ts index 7635c1146..ce21afe58 100644 --- a/src/main/embeddings/VoyageEmbeddings.ts +++ b/src/main/embeddings/VoyageEmbeddings.ts @@ -1,5 +1,5 @@ +import { BaseEmbeddings } from '@cherrystudio/embedjs-interfaces' import { VoyageEmbeddings as _VoyageEmbeddings } from '@langchain/community/embeddings/voyage' -import { BaseEmbeddings } from '@llm-tools/embedjs-interfaces' export default class VoyageEmbeddings extends BaseEmbeddings { private model: _VoyageEmbeddings diff --git a/src/main/loader/draftsExportLoader.ts b/src/main/loader/draftsExportLoader.ts index 4ad1806ba..5213b4370 100644 --- a/src/main/loader/draftsExportLoader.ts +++ b/src/main/loader/draftsExportLoader.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs' -import { JsonLoader } from '@llm-tools/embedjs' +import { JsonLoader } from '@cherrystudio/embedjs' /** * Drafts 应用导出的笔记文件加载器 diff --git a/src/main/loader/epubLoader.ts b/src/main/loader/epubLoader.ts index ad2e92baa..bb62cba8c 100644 --- a/src/main/loader/epubLoader.ts +++ b/src/main/loader/epubLoader.ts @@ -1,6 +1,6 @@ +import { BaseLoader } from '@cherrystudio/embedjs-interfaces' +import { cleanString } from '@cherrystudio/embedjs-utils' import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters' -import { BaseLoader } from '@llm-tools/embedjs-interfaces' -import { cleanString } from '@llm-tools/embedjs-utils' import { getTempDir } from '@main/utils/file' import Logger from 'electron-log' import EPub from 'epub' diff --git a/src/main/loader/index.ts b/src/main/loader/index.ts index 5c2896328..db837f414 100644 --- a/src/main/loader/index.ts +++ b/src/main/loader/index.ts @@ -1,8 +1,8 @@ import * as fs from 'node:fs' -import { JsonLoader, LocalPathLoader, RAGApplication, TextLoader } from '@llm-tools/embedjs' -import type { AddLoaderReturn } from '@llm-tools/embedjs-interfaces' -import { WebLoader } from '@llm-tools/embedjs-loader-web' +import { JsonLoader, LocalPathLoader, RAGApplication, TextLoader } from '@cherrystudio/embedjs' +import type { AddLoaderReturn } from '@cherrystudio/embedjs-interfaces' +import { WebLoader } from '@cherrystudio/embedjs-loader-web' import { LoaderReturn } from '@shared/config/types' import { FileType, KnowledgeBaseParams } from '@types' import Logger from 'electron-log' diff --git a/src/main/loader/odLoader.ts b/src/main/loader/odLoader.ts index 286fe34ea..7e13420b2 100644 --- a/src/main/loader/odLoader.ts +++ b/src/main/loader/odLoader.ts @@ -1,6 +1,6 @@ +import { BaseLoader } from '@cherrystudio/embedjs-interfaces' +import { cleanString } from '@cherrystudio/embedjs-utils' import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters' -import { BaseLoader } from '@llm-tools/embedjs-interfaces' -import { cleanString } from '@llm-tools/embedjs-utils' import md5 from 'md5' import { OfficeParserConfig, parseOfficeAsync } from 'officeparser' diff --git a/src/main/reranker/BaseReranker.ts b/src/main/reranker/BaseReranker.ts index d31bb40b2..543829089 100644 --- a/src/main/reranker/BaseReranker.ts +++ b/src/main/reranker/BaseReranker.ts @@ -1,4 +1,4 @@ -import type { ExtractChunkData } from '@llm-tools/embedjs-interfaces' +import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { KnowledgeBaseParams } from '@types' export default abstract class BaseReranker { diff --git a/src/main/reranker/DefaultReranker.ts b/src/main/reranker/DefaultReranker.ts index aac0c65e8..70a4d05ac 100644 --- a/src/main/reranker/DefaultReranker.ts +++ b/src/main/reranker/DefaultReranker.ts @@ -1,4 +1,4 @@ -import type { ExtractChunkData } from '@llm-tools/embedjs-interfaces' +import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { KnowledgeBaseParams } from '@types' import BaseReranker from './BaseReranker' diff --git a/src/main/reranker/JinaReranker.ts b/src/main/reranker/JinaReranker.ts index 3dfede195..ed14f4746 100644 --- a/src/main/reranker/JinaReranker.ts +++ b/src/main/reranker/JinaReranker.ts @@ -1,4 +1,4 @@ -import { ExtractChunkData } from '@llm-tools/embedjs-interfaces' +import { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { KnowledgeBaseParams } from '@types' import axios from 'axios' diff --git a/src/main/reranker/Reranker.ts b/src/main/reranker/Reranker.ts index c07b1567e..f9f37cfca 100644 --- a/src/main/reranker/Reranker.ts +++ b/src/main/reranker/Reranker.ts @@ -1,4 +1,4 @@ -import type { ExtractChunkData } from '@llm-tools/embedjs-interfaces' +import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { KnowledgeBaseParams } from '@types' import BaseReranker from './BaseReranker' diff --git a/src/main/reranker/SiliconFlowReranker.ts b/src/main/reranker/SiliconFlowReranker.ts index 1e47cca9c..bba8d5405 100644 --- a/src/main/reranker/SiliconFlowReranker.ts +++ b/src/main/reranker/SiliconFlowReranker.ts @@ -1,4 +1,4 @@ -import type { ExtractChunkData } from '@llm-tools/embedjs-interfaces' +import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { KnowledgeBaseParams } from '@types' import axios from 'axios' diff --git a/src/main/reranker/VoyageReranker.ts b/src/main/reranker/VoyageReranker.ts index 710578829..c48fbb9bc 100644 --- a/src/main/reranker/VoyageReranker.ts +++ b/src/main/reranker/VoyageReranker.ts @@ -1,4 +1,4 @@ -import { ExtractChunkData } from '@llm-tools/embedjs-interfaces' +import { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { KnowledgeBaseParams } from '@types' import axios from 'axios' diff --git a/src/main/services/KnowledgeService.ts b/src/main/services/KnowledgeService.ts index 098f3170c..b224458a1 100644 --- a/src/main/services/KnowledgeService.ts +++ b/src/main/services/KnowledgeService.ts @@ -16,11 +16,11 @@ import * as fs from 'node:fs' import path from 'node:path' -import { RAGApplication, RAGApplicationBuilder, TextLoader } from '@llm-tools/embedjs' -import type { ExtractChunkData } from '@llm-tools/embedjs-interfaces' -import { LibSqlDb } from '@llm-tools/embedjs-libsql' -import { SitemapLoader } from '@llm-tools/embedjs-loader-sitemap' -import { WebLoader } from '@llm-tools/embedjs-loader-web' +import { RAGApplication, RAGApplicationBuilder, TextLoader } from '@cherrystudio/embedjs' +import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' +import { LibSqlDb } from '@cherrystudio/embedjs-libsql' +import { SitemapLoader } from '@cherrystudio/embedjs-loader-sitemap' +import { WebLoader } from '@cherrystudio/embedjs-loader-web' import Embeddings from '@main/embeddings/Embeddings' import { addFileLoader } from '@main/loader' import Reranker from '@main/reranker/Reranker' diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 43d366519..cfc5acad8 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -1,6 +1,6 @@ +import { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { ElectronAPI } from '@electron-toolkit/preload' import type { FileMetadataResponse, ListFilesResponse, UploadFileResponse } from '@google/generative-ai/server' -import { ExtractChunkData } from '@llm-tools/embedjs-interfaces' import type { MCPServer, MCPTool } from '@renderer/types' import { AppInfo, FileType, KnowledgeBaseParams, KnowledgeItem, LanguageVarious, WebDavConfig } from '@renderer/types' import type { LoaderReturn } from '@shared/config/types' diff --git a/src/preload/index.ts b/src/preload/index.ts index fbfbc6d3a..bff488518 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,5 +1,5 @@ +import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { electronAPI } from '@electron-toolkit/preload' -import type { ExtractChunkData } from '@llm-tools/embedjs-interfaces' import { FileType, KnowledgeBaseParams, KnowledgeItem, MCPServer, Shortcut, WebDavConfig } from '@types' import { contextBridge, ipcRenderer, OpenDialogOptions, shell } from 'electron' import { CreateDirectoryOptions } from 'webdav' diff --git a/src/renderer/src/pages/knowledge/components/KnowledgeSearchPopup.tsx b/src/renderer/src/pages/knowledge/components/KnowledgeSearchPopup.tsx index af976419c..e01e6b465 100644 --- a/src/renderer/src/pages/knowledge/components/KnowledgeSearchPopup.tsx +++ b/src/renderer/src/pages/knowledge/components/KnowledgeSearchPopup.tsx @@ -1,4 +1,4 @@ -import type { ExtractChunkData } from '@llm-tools/embedjs-interfaces' +import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { TopView } from '@renderer/components/TopView' import { DEFAULT_KNOWLEDGE_THRESHOLD } from '@renderer/config/constant' import { getFileFromUrl, getKnowledgeBaseParams } from '@renderer/services/KnowledgeService' diff --git a/src/renderer/src/services/KnowledgeService.ts b/src/renderer/src/services/KnowledgeService.ts index 3ebe542bd..0deb8730e 100644 --- a/src/renderer/src/services/KnowledgeService.ts +++ b/src/renderer/src/services/KnowledgeService.ts @@ -1,4 +1,4 @@ -import type { ExtractChunkData } from '@llm-tools/embedjs-interfaces' +import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' import { DEFAULT_KNOWLEDGE_DOCUMENT_COUNT, DEFAULT_KNOWLEDGE_THRESHOLD } from '@renderer/config/constant' import { getEmbeddingMaxContext } from '@renderer/config/embedings' import AiProvider from '@renderer/providers/AiProvider' diff --git a/yarn.lock b/yarn.lock index 1dd9b12b4..44752ba3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -451,6 +451,176 @@ __metadata: languageName: unknown linkType: soft +"@cherrystudio/embedjs-interfaces@npm:0.1.29": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-interfaces@npm:0.1.29" + dependencies: + "@langchain/core": "npm:^0.3.26" + debug: "npm:^4.4.0" + md5: "npm:^2.3.0" + uuid: "npm:^11.0.3" + checksum: 10c0/df7ac19aecae137e5af427bb1605232993ef1510e3d41875cb8dd604e7a692778758e5f640a6ab839aaa4193c21012a50b7dfb0aa42c434e668f99faa0fd04d7 + languageName: node + linkType: hard + +"@cherrystudio/embedjs-libsql@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-libsql@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-utils": "npm:0.1.29" + "@libsql/client": "npm:^0.14.0" + debug: "npm:^4.4.0" + checksum: 10c0/655da13d5e192bb0d46e36cbe1ee444b290f6d98d39ad4ac72004fa0aa1a2f5c6dc7a41dd2a9631cc80014501e3423fbd3a822f9fcb331617ab0a43db9692410 + languageName: node + linkType: hard + +"@cherrystudio/embedjs-loader-csv@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-loader-csv@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-utils": "npm:0.1.29" + csv-parse: "npm:^5.6.0" + debug: "npm:^4.4.0" + md5: "npm:^2.3.0" + checksum: 10c0/7c54791fe836839bf3b6a882a9e4c5656b4f30e54aa4e8967b380bc858fd76e03b2ca39b050bc5c06ffbc9e0c722d91d5dd0acf9edf576ff279805d718dfd437 + languageName: node + linkType: hard + +"@cherrystudio/embedjs-loader-image@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-loader-image@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-utils": "npm:0.1.29" + "@langchain/core": "npm:^0.3.26" + debug: "npm:^4.4.0" + exifremove: "npm:^1.0.1" + md5: "npm:^2.3.0" + mime: "npm:^4.0.6" + stream-mime-type: "npm:^2.0.0" + checksum: 10c0/cbc43bf0be38ccd231a35ee06f160fee4628267a912f222c2c326e6d383a0477e1faad1910c9cd485ef8857b63d4404a9797c7e6a9661773345a4cca1ce956cf + languageName: node + linkType: hard + +"@cherrystudio/embedjs-loader-markdown@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-loader-markdown@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-loader-web": "npm:0.1.29" + debug: "npm:^4.4.0" + md5: "npm:^2.3.0" + micromark: "npm:^4.0.1" + micromark-extension-gfm: "npm:^3.0.0" + micromark-extension-mdx-jsx: "npm:^3.0.1" + checksum: 10c0/068393c00321a03a7b7881bb9b4b3b115440a91496b4fef43ad9b7f352c763aa59a6a33f69c9db39f2ed4c0a727f7e1dcd28448f7a446a5ec6a62edbd035f5a8 + languageName: node + linkType: hard + +"@cherrystudio/embedjs-loader-msoffice@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-loader-msoffice@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-utils": "npm:0.1.29" + "@langchain/textsplitters": "npm:^0.1.0" + md5: "npm:^2.3.0" + office-text-extractor: "npm:^3.0.3" + checksum: 10c0/be97eb2278d29f06b569b0aa2fd2b3640caf43207268f5c5cbe16fb77776fe026e4e0d5a9c6360f61c4af439561022f6f4becbe97c2b903d1d446021ab3bf38f + languageName: node + linkType: hard + +"@cherrystudio/embedjs-loader-pdf@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-loader-pdf@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-utils": "npm:0.1.29" + "@langchain/textsplitters": "npm:^0.1.0" + md5: "npm:^2.3.0" + office-text-extractor: "npm:^3.0.3" + checksum: 10c0/cd45963f9405cd1b817f9539ad876dd32e214d21b651459822fc9f829105dc4934daf1aded9cc7084efd9dc914901b4b72fc52a7c5aa9fb550454b0e465844cf + languageName: node + linkType: hard + +"@cherrystudio/embedjs-loader-sitemap@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-loader-sitemap@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-loader-web": "npm:0.1.29" + debug: "npm:^4.4.0" + md5: "npm:^2.3.0" + sitemapper: "npm:^3.2.20" + checksum: 10c0/2cb5fba68f3e89026b08274f3d286b46c44192f3e8b499d72984e63f36d174bdc7da6c8122c922b8fd5660fa0bc1fbbdbaecc37dae134467d2a501fd1642f0d2 + languageName: node + linkType: hard + +"@cherrystudio/embedjs-loader-web@npm:0.1.29, @cherrystudio/embedjs-loader-web@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-loader-web@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-utils": "npm:0.1.29" + "@langchain/textsplitters": "npm:^0.1.0" + debug: "npm:^4.4.0" + html-to-text: "npm:^9.0.5" + md5: "npm:^2.3.0" + checksum: 10c0/a15529e45e309993644db7ee8546f970c0d94fd54baf5044d650a0af66cddb9729af5f3a3fb115c13cd7b7b2ab38bb3809cc088d4c6346e9ca33e478845820f3 + languageName: node + linkType: hard + +"@cherrystudio/embedjs-loader-xml@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-loader-xml@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + debug: "npm:^4.4.0" + fast-xml-parser: "npm:^4.5.1" + md5: "npm:^2.3.0" + checksum: 10c0/cf24dc1b48d55197f3773a9f7490c521461c6cade86869a333bac6c05dae10529ecdbaa03dbfce0994e07215fe343c9801b81356a6141965aa10d50fe2e6c858 + languageName: node + linkType: hard + +"@cherrystudio/embedjs-openai@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-openai@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@langchain/core": "npm:^0.3.26" + "@langchain/openai": "npm:^0.3.16" + debug: "npm:^4.4.0" + checksum: 10c0/10451eb9e0c8f613ea3829b478120890ee44e2a9c7707a48797c21cbd4f4479ad56f86bd38099762900ddf17d42758dba938325eaaa9fae66f71e033c6b64dd5 + languageName: node + linkType: hard + +"@cherrystudio/embedjs-utils@npm:0.1.29": + version: 0.1.29 + resolution: "@cherrystudio/embedjs-utils@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + checksum: 10c0/1b8d8e38207e7588ee134c316bc566dfb68d56023887be61793480cdeac0fc5a2dcc3c72f7727daae801abcaf16e8518ab9dcb7b0b0f7d3a16473a8830ba9dff + languageName: node + linkType: hard + +"@cherrystudio/embedjs@npm:^0.1.28": + version: 0.1.29 + resolution: "@cherrystudio/embedjs@npm:0.1.29" + dependencies: + "@cherrystudio/embedjs-interfaces": "npm:0.1.29" + "@cherrystudio/embedjs-utils": "npm:0.1.29" + "@langchain/textsplitters": "npm:^0.1.0" + debug: "npm:^4.4.0" + langchain: "npm:^0.3.8" + md5: "npm:^2.3.0" + mime: "npm:^4.0.6" + stream-mime-type: "npm:^2.0.0" + checksum: 10c0/01487ab886e7c5c260fc65dee0d67407988e58fa82a1f0fdc4a332548a570cc63f5d39cc87878a7e3272be547a306fee5ec6caa805614eb4e139ce1259b7f6c9 + languageName: node + linkType: hard + "@develar/schema-utils@npm:~2.6.5": version: 2.6.5 resolution: "@develar/schema-utils@npm:2.6.5" @@ -2167,207 +2337,6 @@ __metadata: languageName: node linkType: hard -"@llm-tools/embedjs-interfaces@npm:0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-interfaces@npm:0.1.28" - dependencies: - "@langchain/core": "npm:^0.3.26" - debug: "npm:^4.4.0" - md5: "npm:^2.3.0" - uuid: "npm:^11.0.3" - checksum: 10c0/51c89f2254da60aa01d911a22e8fe852564609525b95090f71385e653e42ef87b99af8d6450debfd0d5a4baa43ebeef8136109fefb09f19551a969e397d43dc1 - languageName: node - linkType: hard - -"@llm-tools/embedjs-libsql@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-libsql@npm:0.1.28" - dependencies: - "@libsql/client": "npm:^0.14.0" - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-utils": "npm:0.1.28" - debug: "npm:^4.4.0" - checksum: 10c0/ec2c50d89a9d601618d153839fc1545bef38a8a7475f2b6ed21a5d876d7964b958950872df64719a3a851a6d48a3a613233c854a091726ebdf2d992c1fdadf7f - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-csv@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-csv@npm:0.1.28" - dependencies: - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-utils": "npm:0.1.28" - csv-parse: "npm:^5.6.0" - debug: "npm:^4.4.0" - md5: "npm:^2.3.0" - checksum: 10c0/dfa0f6825e0d3da9b703381d7e1c9765aea6b99cea2663fc0134e77b2b8bf331f8d8718f812e4efc31416e21450d67b2bf7fe3138a48146b871ab646aa3fd87e - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-image@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-image@npm:0.1.28" - dependencies: - "@langchain/core": "npm:^0.3.26" - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-utils": "npm:0.1.28" - debug: "npm:^4.4.0" - exifremove: "npm:^1.0.1" - md5: "npm:^2.3.0" - mime: "npm:^4.0.6" - stream-mime-type: "npm:^2.0.0" - checksum: 10c0/e9414dde0d10afe1541bfe464b80f5cb61ec523f28dce62ab931a3f4ffde93c5589fde8e7a7f5751e7dc20d68fe2d28883925ba65e2542ee9d43002591568af1 - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-markdown@npm:0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-markdown@npm:0.1.28" - dependencies: - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-loader-web": "npm:0.1.28" - debug: "npm:^4.4.0" - md5: "npm:^2.3.0" - micromark: "npm:^4.0.1" - micromark-extension-gfm: "npm:^3.0.0" - micromark-extension-mdx-jsx: "npm:^3.0.1" - checksum: 10c0/deb86848c57cdaf1aa89cd3382505aa4cc53c170d68105a97da1f6ebaff508ed6db1f164004ae1e0426266c29e15435a5bc092eb37ca4fb81ee574940daf1c0e - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-markdown@patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-markdown@patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch::version=0.1.28&hash=9c7dea" - dependencies: - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-loader-web": "npm:0.1.28" - debug: "npm:^4.4.0" - md5: "npm:^2.3.0" - micromark: "npm:^4.0.1" - micromark-extension-gfm: "npm:^3.0.0" - micromark-extension-mdx-jsx: "npm:^3.0.1" - checksum: 10c0/4be7354294c9cc1ee5b93e0bf49a218894e5a0ad63f344300a9277751fb7517d7ff9ab51594eb63f548d303cec0c747507a5df29bd92deb506bf9829f1675f67 - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-msoffice@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-msoffice@npm:0.1.28" - dependencies: - "@langchain/textsplitters": "npm:^0.1.0" - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-utils": "npm:0.1.28" - md5: "npm:^2.3.0" - office-text-extractor: "npm:^3.0.3" - checksum: 10c0/dbc511938c16ebfcfe9cfedfe68b64b15e2290474092c3600cf290ccb3da5a116a7bd2d469a7f26ee24882cc6835260ad3129ddd54c0f50cc96ef1fa0b142abe - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-pdf@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-pdf@npm:0.1.28" - dependencies: - "@langchain/textsplitters": "npm:^0.1.0" - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-utils": "npm:0.1.28" - md5: "npm:^2.3.0" - office-text-extractor: "npm:^3.0.3" - checksum: 10c0/3ac930561c137b2cc91dc22782c83857e60441a15710d3913351c69f4960dd50ad31a79a1c5f48c2784daf400fd86a03483b80ae3c375e085b1d42a759c9efbf - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-sitemap@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-sitemap@npm:0.1.28" - dependencies: - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-loader-web": "npm:0.1.28" - debug: "npm:^4.4.0" - md5: "npm:^2.3.0" - sitemapper: "npm:^3.2.20" - checksum: 10c0/007f45d02e1200b7cca0bd2f21514910e4b0243126b553404f55c80196b82e673ce09ef1c77f515ab6a6a9397f06ee58799ac8c5f885635ffa759fcd14076fc2 - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-web@npm:0.1.28, @llm-tools/embedjs-loader-web@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-web@npm:0.1.28" - dependencies: - "@langchain/textsplitters": "npm:^0.1.0" - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-utils": "npm:0.1.28" - debug: "npm:^4.4.0" - html-to-text: "npm:^9.0.5" - md5: "npm:^2.3.0" - checksum: 10c0/2abf61ebfa8e2f145730cc2676cf49f5f84b32b5f1c90b5dc58c820b13ab02c10f31bca9891fdb675a7f66a421bb407a0a192d069ea48184eb2b4b4b995382bd - languageName: node - linkType: hard - -"@llm-tools/embedjs-loader-xml@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-loader-xml@npm:0.1.28" - dependencies: - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - debug: "npm:^4.4.0" - fast-xml-parser: "npm:^4.5.1" - md5: "npm:^2.3.0" - checksum: 10c0/20595caa970a1f715f462a66f949e5f9d931431171cd6a55b70f6aeb9166a84233b397d688594de3764974d9d94c066d104f0e3b0c2b79967404cf452b1f7a93 - languageName: node - linkType: hard - -"@llm-tools/embedjs-openai@npm:^0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-openai@npm:0.1.28" - dependencies: - "@langchain/core": "npm:^0.3.26" - "@langchain/openai": "npm:^0.3.16" - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - debug: "npm:^4.4.0" - checksum: 10c0/cc8a703acf447a568f30b218ccb1935529a956177a8adac08f7a67fc4f6257f2a61ed21218341835250c9610566e3219bc8cf866a04423f3099631a85f29c32c - languageName: node - linkType: hard - -"@llm-tools/embedjs-utils@npm:0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs-utils@npm:0.1.28" - dependencies: - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - checksum: 10c0/7d97b36be831d894102fed34a7e7078eade509b4fbd78d991160f60b2d2d8f3136e6ad93edcf64a3d2b7be346c83cdae9b0ecb63d35aa02eb38dfb4f9cff6117 - languageName: node - linkType: hard - -"@llm-tools/embedjs@npm:0.1.28": - version: 0.1.28 - resolution: "@llm-tools/embedjs@npm:0.1.28" - dependencies: - "@langchain/textsplitters": "npm:^0.1.0" - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-utils": "npm:0.1.28" - debug: "npm:^4.4.0" - langchain: "npm:^0.3.8" - md5: "npm:^2.3.0" - mime: "npm:^4.0.6" - stream-mime-type: "npm:^2.0.0" - checksum: 10c0/0ade20a97b987c5b24175e1e46c7f0917e91b0acde712e9a759fb9fe7f48b04a79fca28c2e4cc8702a25e6cd7fba8a9351bf1f7e5bbf1373f0f60de047703d7a - languageName: node - linkType: hard - -"@llm-tools/embedjs@patch:@llm-tools/embedjs@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch": - version: 0.1.28 - resolution: "@llm-tools/embedjs@patch:@llm-tools/embedjs@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch::version=0.1.28&hash=51ffc6" - dependencies: - "@langchain/textsplitters": "npm:^0.1.0" - "@llm-tools/embedjs-interfaces": "npm:0.1.28" - "@llm-tools/embedjs-utils": "npm:0.1.28" - debug: "npm:^4.4.0" - langchain: "npm:^0.3.8" - md5: "npm:^2.3.0" - mime: "npm:^4.0.6" - stream-mime-type: "npm:^2.0.0" - checksum: 10c0/2af2bcd2f6476eaf02ed9ff5e189ecb03d663146cd5229c3c32b45fef8a37fd84a48193f8d067eaf9339e89a5035502c4ff48d2370dfd655294daa8b4e365841 - languageName: node - linkType: hard - "@malept/cross-spawn-promise@npm:^1.1.0": version: 1.1.1 resolution: "@malept/cross-spawn-promise@npm:1.1.1" @@ -3798,6 +3767,17 @@ __metadata: "@agentic/searxng": "npm:^7.3.3" "@agentic/tavily": "npm:^7.3.3" "@anthropic-ai/sdk": "npm:^0.38.0" + "@cherrystudio/embedjs": "npm:^0.1.28" + "@cherrystudio/embedjs-libsql": "npm:^0.1.28" + "@cherrystudio/embedjs-loader-csv": "npm:^0.1.28" + "@cherrystudio/embedjs-loader-image": "npm:^0.1.28" + "@cherrystudio/embedjs-loader-markdown": "npm:^0.1.28" + "@cherrystudio/embedjs-loader-msoffice": "npm:^0.1.28" + "@cherrystudio/embedjs-loader-pdf": "npm:^0.1.28" + "@cherrystudio/embedjs-loader-sitemap": "npm:^0.1.28" + "@cherrystudio/embedjs-loader-web": "npm:^0.1.28" + "@cherrystudio/embedjs-loader-xml": "npm:^0.1.28" + "@cherrystudio/embedjs-openai": "npm:^0.1.28" "@electron-toolkit/eslint-config-prettier": "npm:^3.0.0" "@electron-toolkit/eslint-config-ts": "npm:^3.0.0" "@electron-toolkit/preload": "npm:^3.0.0" @@ -3812,17 +3792,6 @@ __metadata: "@hello-pangea/dnd": "npm:^16.6.0" "@kangfenmao/keyv-storage": "npm:^0.1.0" "@langchain/community": "npm:^0.3.36" - "@llm-tools/embedjs": "patch:@llm-tools/embedjs@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-npm-0.1.28-8e4393fa2d.patch" - "@llm-tools/embedjs-libsql": "npm:^0.1.28" - "@llm-tools/embedjs-loader-csv": "npm:^0.1.28" - "@llm-tools/embedjs-loader-image": "npm:^0.1.28" - "@llm-tools/embedjs-loader-markdown": "patch:@llm-tools/embedjs-loader-markdown@npm%3A0.1.28#~/.yarn/patches/@llm-tools-embedjs-loader-markdown-npm-0.1.28-81647ffac6.patch" - "@llm-tools/embedjs-loader-msoffice": "npm:^0.1.28" - "@llm-tools/embedjs-loader-pdf": "npm:^0.1.28" - "@llm-tools/embedjs-loader-sitemap": "npm:^0.1.28" - "@llm-tools/embedjs-loader-web": "npm:^0.1.28" - "@llm-tools/embedjs-loader-xml": "npm:^0.1.28" - "@llm-tools/embedjs-openai": "npm:^0.1.28" "@modelcontextprotocol/sdk": "patch:@modelcontextprotocol/sdk@npm%3A1.6.1#~/.yarn/patches/@modelcontextprotocol-sdk-npm-1.6.1-b46313efe7.patch" "@notionhq/client": "npm:^2.2.15" "@reduxjs/toolkit": "npm:^2.2.5" From d64d6969ae90ca9091af68ad316e11af4d29c151 Mon Sep 17 00:00:00 2001 From: Sorades <2019302080119@whu.edu.cn> Date: Wed, 26 Mar 2025 12:25:23 +0800 Subject: [PATCH 04/13] =?UTF-8?q?feat(message):=20=E5=B0=86fold=20display?= =?UTF-8?q?=20mode=E7=9A=84=E7=8A=B6=E6=80=81=E6=8C=81=E4=B9=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/Messages/MessageGroupModelList.tsx | 20 +++++++++++-------- src/renderer/src/store/settings.ts | 6 ++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx b/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx index 315db3aaf..c81d9adc5 100644 --- a/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx +++ b/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx @@ -1,9 +1,12 @@ import { ArrowsAltOutlined, ShrinkOutlined } from '@ant-design/icons' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' import Scrollbar from '@renderer/components/Scrollbar' +import { useSettings } from '@renderer/hooks/useSettings' +import { useAppDispatch } from '@renderer/store' +import { setFoldDisplayMode } from '@renderer/store/settings' import { Message, Model } from '@renderer/types' import { Avatar, Segmented as AntdSegmented, Tooltip } from 'antd' -import { FC, useState } from 'react' +import { FC } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -15,26 +18,27 @@ interface MessageGroupModelListProps { type DisplayMode = 'compact' | 'expanded' const MessageGroupModelList: FC = ({ messages, setSelectedMessage }) => { + const dispatch = useAppDispatch() const { t } = useTranslation() - const [displayMode, setDisplayMode] = useState('expanded') - const isCompact = displayMode === 'compact' + const { foldDisplayMode } = useSettings() + const isCompact = foldDisplayMode === 'compact' return ( - setDisplayMode(isCompact ? 'expanded' : 'compact')}> + dispatch(setFoldDisplayMode(isCompact ? 'expanded' : 'compact'))}> - {displayMode === 'compact' ? : } + {foldDisplayMode === 'compact' ? : } - - {displayMode === 'compact' ? ( + + {foldDisplayMode === 'compact' ? ( /* Compact style display */ {messages.map((message, index) => ( diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index d4cd2c8bc..e27dd8f62 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -53,6 +53,7 @@ export interface SettingsState { mathEngine: 'MathJax' | 'KaTeX' messageStyle: 'plain' | 'bubble' codeStyle: CodeStyleVarious + foldDisplayMode: 'expanded' | 'compact' gridColumns: number gridPopoverTrigger: 'hover' | 'click' messageNavigation: 'none' | 'buttons' | 'anchor' @@ -135,6 +136,7 @@ const initialState: SettingsState = { mathEngine: 'KaTeX', messageStyle: 'plain', codeStyle: 'auto', + foldDisplayMode: 'expanded', gridColumns: 2, gridPopoverTrigger: 'hover', messageNavigation: 'none', @@ -295,6 +297,9 @@ const settingsSlice = createSlice({ setMathEngine: (state, action: PayloadAction<'MathJax' | 'KaTeX'>) => { state.mathEngine = action.payload }, + setFoldDisplayMode: (state, action: PayloadAction<'expanded' | 'compact'>) => { + state.foldDisplayMode = action.payload + }, setGridColumns: (state, action: PayloadAction) => { state.gridColumns = action.payload }, @@ -446,6 +451,7 @@ export const { setCodeCollapsible, setCodeWrappable, setMathEngine, + setFoldDisplayMode, setGridColumns, setGridPopoverTrigger, setMessageStyle, From 487d7a502e710a7e7b14c8f7480ffd638a19218b Mon Sep 17 00:00:00 2001 From: OrzMiku Date: Wed, 26 Mar 2025 20:56:04 +0800 Subject: [PATCH 05/13] refactor(AgentCard): unify dropmenus --- .../src/pages/agents/components/AgentCard.tsx | 23 +++++++++++----- .../src/pages/agents/components/MyAgents.tsx | 26 +++---------------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/renderer/src/pages/agents/components/AgentCard.tsx b/src/renderer/src/pages/agents/components/AgentCard.tsx index 80a515990..2f64e9aa6 100644 --- a/src/renderer/src/pages/agents/components/AgentCard.tsx +++ b/src/renderer/src/pages/agents/components/AgentCard.tsx @@ -1,14 +1,20 @@ import { EllipsisOutlined } from '@ant-design/icons' -import { Agent } from '@renderer/types' +import type { Agent } from '@renderer/types' import { getLeadingEmoji } from '@renderer/utils' import { Dropdown } from 'antd' -import { FC, memo } from 'react' +import { type FC, memo } from 'react' import styled from 'styled-components' interface Props { agent: Agent onClick: () => void - contextMenu?: { label: string; onClick: () => void }[] + contextMenu?: { + key: string + label: string + icon?: React.ReactNode + danger?: boolean + onClick: () => void + }[] menuItems?: { key: string label: string @@ -58,9 +64,14 @@ const AgentCard: FC = ({ agent, onClick, contextMenu, menuItems }) => { ({ - key: item.label, - label: item.label, - onClick: () => item.onClick() + ...item, + onClick: (e) => { + e.domEvent.stopPropagation() + e.domEvent.preventDefault() + setTimeout(() => { + item.onClick() + }, 0) + } })) }} trigger={['contextMenu']}> diff --git a/src/renderer/src/pages/agents/components/MyAgents.tsx b/src/renderer/src/pages/agents/components/MyAgents.tsx index f1749b9bd..ad93a825b 100644 --- a/src/renderer/src/pages/agents/components/MyAgents.tsx +++ b/src/renderer/src/pages/agents/components/MyAgents.tsx @@ -2,7 +2,7 @@ import { DeleteOutlined, EditOutlined, PlusOutlined, SortAscendingOutlined } fro import { useAgents } from '@renderer/hooks/useAgents' import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings' import { createAssistantFromAgent } from '@renderer/services/AssistantService' -import { Agent } from '@renderer/types' +import type { Agent } from '@renderer/types' import { Col, Row } from 'antd' import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' @@ -45,7 +45,7 @@ const MyAgents: React.FC = ({ onClick, search }) => { return ( {filteredAgents.map((agent) => { - const dropdownMenuItems = [ + const menuItems = [ { key: 'edit', label: t('agents.edit.title'), @@ -73,29 +73,9 @@ const MyAgents: React.FC = ({ onClick, search }) => { } ] - const contextMenuItems = [ - { - label: t('agents.edit.title'), - onClick: () => AssistantSettingsPopup.show({ assistant: agent }) - }, - { - label: t('agents.add.button'), - onClick: () => createAssistantFromAgent(agent) - }, - { - label: t('common.delete'), - onClick: () => handleDelete(agent) - } - ] - return ( - onClick?.(agent)} - contextMenu={contextMenuItems} - menuItems={dropdownMenuItems} - /> + onClick?.(agent)} contextMenu={menuItems} menuItems={menuItems} /> ) })} From c1622424338ec8bd8834d7d098c3deec7d2c93fd Mon Sep 17 00:00:00 2001 From: one Date: Wed, 26 Mar 2025 21:44:26 +0800 Subject: [PATCH 06/13] fix(HealthCheck): exclude rerank models from being checked (#3969) * fix(HealthCheck): exclude rerank models from being checked * fix: info in en --- src/renderer/src/i18n/locales/en-us.json | 2 +- src/renderer/src/i18n/locales/ja-jp.json | 2 +- src/renderer/src/i18n/locales/ru-ru.json | 2 +- src/renderer/src/i18n/locales/zh-cn.json | 2 +- src/renderer/src/i18n/locales/zh-tw.json | 2 +- .../ProviderSettings/ProviderSetting.tsx | 17 +++++++++++------ .../SelectProviderModelPopup.tsx | 4 ++-- 7 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 9c1f4253b..af7483085 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1119,7 +1119,7 @@ "docs_more_details": "for more details", "get_api_key": "Get API Key", "is_not_support_array_content": "Enable compatible mode", - "no_models": "Please add models first before checking the API connection", + "no_models_for_check": "No models available for checking (e.g. chat models)", "not_checked": "Not Checked", "remove_duplicate_keys": "Remove Duplicate Keys", "remove_invalid_keys": "Remove Invalid Keys", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 02377ec31..b47b041cf 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1118,7 +1118,7 @@ "docs_more_details": "詳細を確認", "get_api_key": "APIキーを取得", "is_not_support_array_content": "互換モードを有効にする", - "no_models": "API接続をチェックする前に、モデルを追加してください", + "no_models_for_check": "チェックするモデルがありません(例:会話モデル)", "not_checked": "未チェック", "remove_duplicate_keys": "重複キーを削除", "remove_invalid_keys": "無効なキーを削除", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index cf4801110..6a67f05d7 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1118,7 +1118,7 @@ "docs_more_details": "для получения дополнительной информации", "get_api_key": "Получить ключ API", "is_not_support_array_content": "Включить совместимый режим", - "no_models": "Пожалуйста, добавьте модели перед проверкой соединения с API", + "no_models_for_check": "Нет моделей для проверки (например, диалоговые модели)", "not_checked": "Не проверено", "remove_duplicate_keys": "Удалить дубликаты ключей", "remove_invalid_keys": "Удалить недействительные ключи", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 6de80fa53..f51df767c 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1119,7 +1119,7 @@ "docs_more_details": "获取更多详情", "get_api_key": "点击这里获取密钥", "is_not_support_array_content": "开启兼容模式", - "no_models": "请先添加模型再检查 API 连接", + "no_models_for_check": "没有可以被检查的模型(例如对话模型)", "not_checked": "未检查", "remove_duplicate_keys": "移除重复密钥", "remove_invalid_keys": "删除无效密钥", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index ad2cd87eb..5bcb0c3cb 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1118,7 +1118,7 @@ "docs_more_details": "檢視更多細節", "get_api_key": "點選這裡取得金鑰", "is_not_support_array_content": "開啟相容模式", - "no_models": "請先新增模型再檢查 API 連接", + "no_models_for_check": "沒有可以被檢查的模型(例如對話模型)", "not_checked": "未檢查", "remove_duplicate_keys": "移除重複金鑰", "remove_invalid_keys": "刪除無效金鑰", diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx index fdc82d45a..6580ae961 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx @@ -1,6 +1,7 @@ import { CheckOutlined, ExportOutlined, HeartOutlined, LoadingOutlined, SettingOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' import OAuthButton from '@renderer/components/OAuth/OAuthButton' +import { isEmbeddingModel, isRerankModel } from '@renderer/config/models' import { PROVIDER_CONFIG } from '@renderer/config/providers' import { useTheme } from '@renderer/context/ThemeProvider' import { useProvider } from '@renderer/hooks/useProvider' @@ -91,12 +92,14 @@ const ProviderSetting: FC = ({ provider: _provider }) => { const onUpdateApiVersion = () => updateProvider({ ...provider, apiVersion }) const onHealthCheck = async () => { - if (isEmpty(models)) { + const modelsToCheck = models.filter((model) => !isRerankModel(model)) + + if (isEmpty(modelsToCheck)) { window.message.error({ key: 'no-models', style: { marginTop: '3vh' }, duration: 5, - content: t('settings.provider.no_models') + content: t('settings.provider.no_models_for_check') }) return } @@ -124,7 +127,7 @@ const ProviderSetting: FC = ({ provider: _provider }) => { } // Prepare the list of models to be checked - const initialStatuses = models.map((model) => ({ + const initialStatuses = modelsToCheck.map((model) => ({ model, checking: true, status: undefined @@ -135,7 +138,7 @@ const ProviderSetting: FC = ({ provider: _provider }) => { const checkResults = await checkModelsHealth( { provider: { ...provider, apiHost }, - models, + models: modelsToCheck, apiKeys: result.apiKeys, isConcurrent: result.isConcurrent }, @@ -180,12 +183,14 @@ const ProviderSetting: FC = ({ provider: _provider }) => { } const onCheckApi = async () => { - if (isEmpty(models)) { + const modelsToCheck = models.filter((model) => !isEmbeddingModel(model) && !isRerankModel(model)) + + if (isEmpty(modelsToCheck)) { window.message.error({ key: 'no-models', style: { marginTop: '3vh' }, duration: 5, - content: t('settings.provider.no_models') + content: t('settings.provider.no_models_for_check') }) return } diff --git a/src/renderer/src/pages/settings/ProviderSettings/SelectProviderModelPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/SelectProviderModelPopup.tsx index 7b9297c67..0d1d8627d 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/SelectProviderModelPopup.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/SelectProviderModelPopup.tsx @@ -1,5 +1,5 @@ import { TopView } from '@renderer/components/TopView' -import { isEmbeddingModel } from '@renderer/config/models' +import { isEmbeddingModel, isRerankModel } from '@renderer/config/models' import i18n from '@renderer/i18n' import { Provider } from '@renderer/types' import { Modal, Select } from 'antd' @@ -16,7 +16,7 @@ interface Props extends ShowParams { } const PopupContainer: React.FC = ({ provider, resolve, reject }) => { - const models = orderBy(provider.models, 'group').filter((i) => !isEmbeddingModel(i)) + const models = orderBy(provider.models, 'group').filter((i) => !isEmbeddingModel(i) && !isRerankModel(i)) const [open, setOpen] = useState(true) const [model, setModel] = useState(first(models)) From 415f99114326932dbefd45e02b2a59076971c88f Mon Sep 17 00:00:00 2001 From: one Date: Wed, 26 Mar 2025 22:04:05 +0800 Subject: [PATCH 07/13] fix: open 'data' page by default after routing to data settings --- src/renderer/src/pages/settings/DataSettings/DataSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/pages/settings/DataSettings/DataSettings.tsx b/src/renderer/src/pages/settings/DataSettings/DataSettings.tsx index cf0f15a1e..c9ade999b 100644 --- a/src/renderer/src/pages/settings/DataSettings/DataSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/DataSettings.tsx @@ -37,7 +37,7 @@ const DataSettings: FC = () => { const [appInfo, setAppInfo] = useState() const { size, removeAllFiles } = useKnowledgeFiles() const { theme } = useTheme() - const [menu, setMenu] = useState('common') + const [menu, setMenu] = useState('data') //joplin icon needs to be updated into iconfont const JoplinIcon = () => ( From 2af15e417217932a27ecb1d81c41aba624c71aee Mon Sep 17 00:00:00 2001 From: ousugo Date: Thu, 27 Mar 2025 07:16:08 +0800 Subject: [PATCH 08/13] feat(models): add 'qwen2.5-omni' to allowed vision models --- src/renderer/src/config/models.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/config/models.ts b/src/renderer/src/config/models.ts index 5b6de28ab..bd6bb317f 100644 --- a/src/renderer/src/config/models.ts +++ b/src/renderer/src/config/models.ts @@ -149,6 +149,7 @@ const visionAllowedModels = [ 'qwen-vl', 'qwen2-vl', 'qwen2.5-vl', + 'qwen2.5-omni', 'qvq', 'internvl2', 'grok-vision-beta', From 7fb85dc31194030cca7a5bd948d1b5e74919933b Mon Sep 17 00:00:00 2001 From: ousugo Date: Thu, 27 Mar 2025 00:09:33 +0800 Subject: [PATCH 09/13] feat(message): calculate token usage when message content is updated --- src/renderer/src/hooks/useMessageOperations.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/hooks/useMessageOperations.ts b/src/renderer/src/hooks/useMessageOperations.ts index 304142317..fe863d5b4 100644 --- a/src/renderer/src/hooks/useMessageOperations.ts +++ b/src/renderer/src/hooks/useMessageOperations.ts @@ -1,4 +1,5 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' +import { estimateMessageUsage } from '@renderer/services/TokenService' import store, { useAppDispatch, useAppSelector } from '@renderer/store' import { clearStreamMessage, @@ -55,6 +56,16 @@ export function useMessageOperations(topic: Topic) { */ const editMessage = useCallback( async (messageId: string, updates: Partial) => { + // 如果更新包含内容变更,重新计算 token + if ('content' in updates) { + const message = messages.find((m) => m.id === messageId) + if (message) { + const updatedMessage = { ...message, ...updates } + const usage = await estimateMessageUsage(updatedMessage) + updates.usage = usage + } + } + await dispatch( updateMessage({ topicId: topic.id, @@ -63,7 +74,7 @@ export function useMessageOperations(topic: Topic) { }) ) }, - [dispatch, topic.id] + [dispatch, topic.id, messages] ) /** From fb9c23c500bfc0a51d1f81a358153c65cf0524a3 Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat <43230886+MyPrototypeWhat@users.noreply.github.com> Date: Thu, 27 Mar 2025 12:06:47 +0800 Subject: [PATCH 10/13] Perf/optimize rendering (#3923) * optimize useMessageOperations * chore: update dependencies and refactor React imports - Added @ant-design/v5-patch-for-react-19 and rc-virtual-list to package.json. - Updated React and ReactDOM types to version 19 in package.json and yarn.lock. - Refactored ReactDOM usage to createRoot in main.tsx for better compatibility with React 18+. - Changed useContext to use in SyntaxHighlighterProvider and ThemeProvider components. - Adjusted flex-direction in Messages components to column for improved layout. - Removed unused state in CodeBlock component. * refactor(Messages): enhance scrolling behavior and introduce scroll utilities - Added createScrollHandler and scrollToBottom utilities for improved scroll management. - Updated Messages component to utilize new scroll utilities for better user experience. - Refactored scroll handling logic to ensure smooth scrolling when new messages are added. - Changed containerRef type to HTMLElement for better type safety. * refactor(Messages): streamline message handling and introduce useTopicMessages hook - Removed direct message selection from useMessageOperations and created a new useTopicMessages hook for better separation of concerns. - Updated Messages component to utilize the new useTopicMessages hook for fetching messages. - Enhanced message display logic with computeDisplayMessages function for improved message rendering. - Refactored scrolling behavior to maintain a smooth user experience during message updates. * refactor(Message Operations): introduce useTopicLoading hook for improved loading state management - Removed loading state from useMessageOperations and created a new useTopicLoading hook for better separation of concerns. - Updated components to utilize the new useTopicLoading hook for fetching loading states related to topics. - Enhanced code organization and readability by streamlining message operations and loading state handling. * refactor(Messages): replace updateMessage with updateMessageThunk for improved async handling - Updated useMessageOperations and MessageAnchorLine components to utilize updateMessageThunk for message updates. - Enhanced error handling and database synchronization in the new thunk implementation. - Streamlined message update logic to improve code clarity and maintainability. * refactor(SyntaxHighlighterProvider, MessageTools, AddMcpServerPopup): update styles and improve type safety - Changed import statements to use TypeScript's type imports for better clarity and type safety. - Updated MessageTools and AddMcpServerPopup components to replace bodyStyle with styles prop for consistent styling approach. - Enhanced overall code organization and maintainability by adhering to TypeScript best practices. * refactor(Messages): update layout and remove unnecessary prop - Removed the hasChildren prop from the Messages component for cleaner code. - Adjusted flex-direction in the mini chat Messages component to column-reverse for improved layout consistency. * refactor: enhance type safety and component return types - Updated functional components to return React.ReactElement instead of JSX.Element for better type consistency. - Changed import statements to use TypeScript's type imports for improved clarity. - Initialized useRef hooks with null for better type safety in various components. - Adjusted props types to use HTMLAttributes for more accurate type definitions. * chore: update package dependencies - Removed outdated dependencies: @agentic/exa, @agentic/searxng, @agentic/tavily, and rc-virtual-list. - Added back @ant-design/v5-patch-for-react-19 and rc-virtual-list with specified versions for improved compatibility. * fix(useMessageOperations): ensure message retrieval from store when updating content --- package.json | 14 ++- src/renderer/src/App.tsx | 2 +- .../src/components/DragableList/index.tsx | 43 ++++---- src/renderer/src/components/app/Navbar.tsx | 5 +- .../src/context/StyleSheetManager.tsx | 4 +- .../src/context/SyntaxHighlighterProvider.tsx | 68 ++++++------ src/renderer/src/context/ThemeProvider.tsx | 4 +- .../src/hooks/useMessageOperations.ts | 45 ++++---- src/renderer/src/main.tsx | 9 +- .../src/pages/home/Inputbar/Inputbar.tsx | 9 +- .../src/pages/home/Markdown/CodeBlock.tsx | 15 ++- .../pages/home/Messages/ChatFlowHistory.tsx | 2 +- .../src/pages/home/Messages/Message.tsx | 2 +- .../pages/home/Messages/MessageAnchorLine.tsx | 18 ++-- .../pages/home/Messages/MessageMenubar.tsx | 20 ++-- .../src/pages/home/Messages/MessageTools.tsx | 12 ++- .../src/pages/home/Messages/Messages.tsx | 70 +++++++++--- .../src/pages/home/Tabs/TopicsTab.tsx | 2 +- .../src/pages/paintings/PaintingsPage.tsx | 7 +- .../MCPSettings/AddMcpServerPopup.tsx | 7 +- .../src/pages/settings/ShortcutSettings.tsx | 6 +- src/renderer/src/store/messages.ts | 65 +++++++---- src/renderer/src/utils/index.ts | 9 +- src/renderer/src/windows/mini/App.tsx | 2 +- yarn.lock | 101 +++++++----------- 25 files changed, 293 insertions(+), 248 deletions(-) diff --git a/package.json b/package.json index 36ae2c7f6..4571179dd 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "@agentic/exa": "^7.3.3", "@agentic/searxng": "^7.3.3", "@agentic/tavily": "^7.3.3", + "@ant-design/v5-patch-for-react-19": "^1.0.3", "@anthropic-ai/sdk": "^0.38.0", "@electron-toolkit/eslint-config-prettier": "^3.0.0", "@electron-toolkit/eslint-config-ts": "^3.0.0", @@ -114,8 +115,8 @@ "@types/md5": "^2.3.5", "@types/node": "^18.19.9", "@types/pako": "^1.0.2", - "@types/react": "^18.2.48", - "@types/react-dom": "^18.2.18", + "@types/react": "^19.0.12", + "@types/react-dom": "^19.0.4", "@types/react-infinite-scroll-component": "^5.0.0", "@types/tinycolor2": "^1", "@vitejs/plugin-react": "^4.2.1", @@ -149,8 +150,9 @@ "openai": "patch:openai@npm%3A4.77.3#~/.yarn/patches/openai-npm-4.77.3-59c6d42e7a.patch", "p-queue": "^8.1.0", "prettier": "^3.5.3", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "rc-virtual-list": "^3.18.5", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-hotkeys-hook": "^4.6.1", "react-i18next": "^14.1.2", "react-infinite-scroll-component": "^6.1.0", @@ -177,10 +179,6 @@ "uuid": "^10.0.0", "vite": "^5.0.12" }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, "resolutions": { "pdf-parse@npm:1.1.1": "patch:pdf-parse@npm%3A1.1.1#~/.yarn/patches/pdf-parse-npm-1.1.1-04a6109b2a.patch", "@langchain/openai@npm:^0.3.16": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch", diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index e820866b4..c5e70a920 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -21,7 +21,7 @@ import PaintingsPage from './pages/paintings/PaintingsPage' import SettingsPage from './pages/settings/SettingsPage' import TranslatePage from './pages/translate/TranslatePage' -function App(): JSX.Element { +function App(): React.ReactElement { return ( diff --git a/src/renderer/src/components/DragableList/index.tsx b/src/renderer/src/components/DragableList/index.tsx index 05ed09c42..a5c2db91a 100644 --- a/src/renderer/src/components/DragableList/index.tsx +++ b/src/renderer/src/components/DragableList/index.tsx @@ -8,6 +8,7 @@ import { OnDragStartResponder, ResponderProvided } from '@hello-pangea/dnd' +import VirtualList from 'rc-virtual-list' import { droppableReorder } from '@renderer/utils' import { FC } from 'react' @@ -47,26 +48,28 @@ const DragableList: FC> = ({ {(provided) => (
- {list.map((item, index) => { - const id = item.id || item - return ( - - {(provided) => ( -
- {children(item, index)} -
- )} -
- ) - })} + + {(item, index) => { + const id = item.id || item + return ( + + {(provided) => ( +
+ {children(item, index)} +
+ )} +
+ ) + }} +
{provided.placeholder}
)} diff --git a/src/renderer/src/components/app/Navbar.tsx b/src/renderer/src/components/app/Navbar.tsx index 47c1b3f3d..db6ed9fa7 100644 --- a/src/renderer/src/components/app/Navbar.tsx +++ b/src/renderer/src/components/app/Navbar.tsx @@ -1,9 +1,10 @@ import { isMac } from '@renderer/config/constant' import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor' -import { FC, PropsWithChildren } from 'react' +import type { FC, PropsWithChildren } from 'react' +import type { HTMLAttributes } from 'react' import styled from 'styled-components' -type Props = PropsWithChildren & JSX.IntrinsicElements['div'] +type Props = PropsWithChildren & HTMLAttributes export const Navbar: FC = ({ children, ...props }) => { const backgroundColor = useNavBackgroundColor() diff --git a/src/renderer/src/context/StyleSheetManager.tsx b/src/renderer/src/context/StyleSheetManager.tsx index 04083c799..64c506bf2 100644 --- a/src/renderer/src/context/StyleSheetManager.tsx +++ b/src/renderer/src/context/StyleSheetManager.tsx @@ -1,12 +1,12 @@ import isPropValid from '@emotion/is-prop-valid' -import { ReactNode } from 'react' +import type { ReactNode } from 'react' import { StyleSheetManager as StyledComponentsStyleSheetManager } from 'styled-components' interface StyleSheetManagerProps { children: ReactNode } -const StyleSheetManager = ({ children }: StyleSheetManagerProps): JSX.Element => { +const StyleSheetManager = ({ children }: StyleSheetManagerProps): React.ReactElement => { return ( { diff --git a/src/renderer/src/context/SyntaxHighlighterProvider.tsx b/src/renderer/src/context/SyntaxHighlighterProvider.tsx index cbdee21df..bbb4624d2 100644 --- a/src/renderer/src/context/SyntaxHighlighterProvider.tsx +++ b/src/renderer/src/context/SyntaxHighlighterProvider.tsx @@ -1,16 +1,11 @@ import { useTheme } from '@renderer/context/ThemeProvider' import { useMermaid } from '@renderer/hooks/useMermaid' import { useSettings } from '@renderer/hooks/useSettings' -import { CodeStyleVarious, ThemeMode } from '@renderer/types' -import React, { createContext, PropsWithChildren, useContext, useEffect, useMemo, useState } from 'react' -import { - BundledLanguage, - bundledLanguages, - BundledTheme, - bundledThemes, - createHighlighter, - HighlighterGeneric -} from 'shiki' +import { type CodeStyleVarious, ThemeMode } from '@renderer/types' +import type React from 'react' +import { createContext, type PropsWithChildren, use, useCallback, useEffect, useMemo, useState } from 'react' +import type { BundledLanguage, BundledTheme, HighlighterGeneric } from 'shiki' +import { bundledLanguages, bundledThemes, createHighlighter } from 'shiki' interface SyntaxHighlighterContextType { codeToHtml: (code: string, language: string) => Promise @@ -51,42 +46,47 @@ export const SyntaxHighlighterProvider: React.FC = ({ childre initHighlighter() }, [highlighterTheme]) - const codeToHtml = async (code: string, language: string) => { - if (!highlighter) return '' + const codeToHtml = useCallback( + async (_code: string, language: string) => { + { + if (!highlighter) return '' - const languageMap: Record = { - vab: 'vb' - } + const languageMap: Record = { + vab: 'vb' + } - const mappedLanguage = languageMap[language] || language + const mappedLanguage = languageMap[language] || language - code = code?.trimEnd() ?? '' - const escapedCode = code?.replace(/[<>]/g, (char) => ({ '<': '<', '>': '>' })[char]!) + const code = _code?.trimEnd() ?? '' + const escapedCode = code?.replace(/[<>]/g, (char) => ({ '<': '<', '>': '>' })[char]!) - try { - if (!highlighter.getLoadedLanguages().includes(mappedLanguage as BundledLanguage)) { - if (mappedLanguage in bundledLanguages || mappedLanguage === 'text') { - await highlighter.loadLanguage(mappedLanguage as BundledLanguage) - } else { + try { + if (!highlighter.getLoadedLanguages().includes(mappedLanguage as BundledLanguage)) { + if (mappedLanguage in bundledLanguages || mappedLanguage === 'text') { + await highlighter.loadLanguage(mappedLanguage as BundledLanguage) + } else { + return `
${escapedCode}
` + } + } + + return highlighter.codeToHtml(code, { + lang: mappedLanguage, + theme: highlighterTheme + }) + } catch (error) { + console.warn(`Error highlighting code for language '${mappedLanguage}':`, error) return `
${escapedCode}
` } } - - return highlighter.codeToHtml(code, { - lang: mappedLanguage, - theme: highlighterTheme - }) - } catch (error) { - console.warn(`Error highlighting code for language '${mappedLanguage}':`, error) - return `
${escapedCode}
` - } - } + }, + [highlighter, highlighterTheme] + ) return {children} } export const useSyntaxHighlighter = () => { - const context = useContext(SyntaxHighlighterContext) + const context = use(SyntaxHighlighterContext) if (!context) { throw new Error('useSyntaxHighlighter must be used within a SyntaxHighlighterProvider') } diff --git a/src/renderer/src/context/ThemeProvider.tsx b/src/renderer/src/context/ThemeProvider.tsx index 5a3fd812e..113898f4f 100644 --- a/src/renderer/src/context/ThemeProvider.tsx +++ b/src/renderer/src/context/ThemeProvider.tsx @@ -1,7 +1,7 @@ import { isMac } from '@renderer/config/constant' import { useSettings } from '@renderer/hooks/useSettings' import { ThemeMode } from '@renderer/types' -import React, { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react' +import React, { createContext, PropsWithChildren, use, useEffect, useState } from 'react' interface ThemeContextType { theme: ThemeMode @@ -64,4 +64,4 @@ export const ThemeProvider: React.FC = ({ children, defaultT ) } -export const useTheme = () => useContext(ThemeContext) +export const useTheme = () => use(ThemeContext) diff --git a/src/renderer/src/hooks/useMessageOperations.ts b/src/renderer/src/hooks/useMessageOperations.ts index fe863d5b4..05d072c7e 100644 --- a/src/renderer/src/hooks/useMessageOperations.ts +++ b/src/renderer/src/hooks/useMessageOperations.ts @@ -5,14 +5,15 @@ import { clearStreamMessage, clearTopicMessages, commitStreamMessage, + deleteMessageAction, resendMessage, selectDisplayCount, selectTopicLoading, selectTopicMessages, setStreamMessage, setTopicLoading, - updateMessage, - updateMessages + updateMessages, + updateMessageThunk } from '@renderer/store/messages' import type { Assistant, Message, Topic } from '@renderer/types' import { abortCompletion } from '@renderer/utils/abortController' @@ -27,17 +28,15 @@ import { TopicManager } from './useTopic' */ export function useMessageOperations(topic: Topic) { const dispatch = useAppDispatch() - const messages = useAppSelector((state) => selectTopicMessages(state, topic.id)) /** * 删除单个消息 */ const deleteMessage = useCallback( - async (message: Message) => { - const newMessages = messages.filter((m) => m.id !== message.id) - await dispatch(updateMessages(topic, newMessages)) + async (id: string) => { + await dispatch(deleteMessageAction(topic, id)) }, - [dispatch, topic, messages] + [dispatch, topic] ) /** @@ -45,10 +44,9 @@ export function useMessageOperations(topic: Topic) { */ const deleteGroupMessages = useCallback( async (askId: string) => { - const newMessages = messages.filter((m) => m.askId !== askId) - await dispatch(updateMessages(topic, newMessages)) + await dispatch(deleteMessageAction(topic, askId, 'askId')) }, - [dispatch, topic, messages] + [dispatch, topic] ) /** @@ -58,23 +56,17 @@ export function useMessageOperations(topic: Topic) { async (messageId: string, updates: Partial) => { // 如果更新包含内容变更,重新计算 token if ('content' in updates) { - const message = messages.find((m) => m.id === messageId) + const messages = store.getState().messages.messagesByTopic[topic.id] + const message = messages?.find((m) => m.id === messageId) if (message) { const updatedMessage = { ...message, ...updates } const usage = await estimateMessageUsage(updatedMessage) updates.usage = usage } } - - await dispatch( - updateMessage({ - topicId: topic.id, - messageId, - updates - }) - ) + await dispatch(updateMessageThunk(topic.id, messageId, updates)) }, - [dispatch, topic.id, messages] + [dispatch, topic.id] ) /** @@ -159,7 +151,6 @@ export function useMessageOperations(topic: Topic) { EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT) }, []) - const loading = useAppSelector((state) => selectTopicLoading(state, topic.id)) const displayCount = useAppSelector(selectDisplayCount) // /** // * 获取当前消息列表 @@ -211,8 +202,6 @@ export function useMessageOperations(topic: Topic) { ) return { - messages, - loading, displayCount, updateMessages: updateMessagesAction, deleteMessage, @@ -230,3 +219,13 @@ export function useMessageOperations(topic: Topic) { resumeMessage } } + +export const useTopicMessages = (topic: Topic) => { + const messages = useAppSelector((state) => selectTopicMessages(state, topic.id)) + return messages +} + +export const useTopicLoading = (topic: Topic) => { + const loading = useAppSelector((state) => selectTopicLoading(state, topic.id)) + return loading +} diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 4885d60bf..e9b964514 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -1,13 +1,16 @@ import './assets/styles/index.scss' +import '@ant-design/v5-patch-for-react-19' -import ReactDOM from 'react-dom/client' +import { createRoot } from 'react-dom/client' import App from './App' import MiniApp from './windows/mini/App' if (location.hash === '#/mini') { document.getElementById('spinner')?.remove() - ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render() + const root = createRoot(document.getElementById('root') as HTMLElement) + root.render() } else { - ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render() + const root = createRoot(document.getElementById('root') as HTMLElement) + root.render() } diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 1dadb0545..5fe831bff 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -13,7 +13,7 @@ import TranslateButton from '@renderer/components/TranslateButton' import { isFunctionCallingModel, isGenerateImageModel, isVisionModel, isWebSearchModel } from '@renderer/config/models' import db from '@renderer/databases' import { useAssistant } from '@renderer/hooks/useAssistant' -import { useMessageOperations } from '@renderer/hooks/useMessageOperations' +import { useMessageOperations, useTopicLoading } from '@renderer/hooks/useMessageOperations' import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime' import { useMessageStyle, useSettings } from '@renderer/hooks/useSettings' import { useShortcut, useShortcutDisplay } from '@renderer/hooks/useShortcuts' @@ -83,10 +83,11 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = const containerRef = useRef(null) const { searching } = useRuntime() const { isBubbleStyle } = useMessageStyle() - const { loading, pauseMessages } = useMessageOperations(topic) + const { pauseMessages } = useMessageOperations(topic) + const loading = useTopicLoading(topic) const dispatch = useAppDispatch() const [spaceClickCount, setSpaceClickCount] = useState(0) - const spaceClickTimer = useRef() + const spaceClickTimer = useRef(null) const [isTranslating, setIsTranslating] = useState(false) const [selectedKnowledgeBases, setSelectedKnowledgeBases] = useState([]) const [mentionModels, setMentionModels] = useState([]) @@ -96,7 +97,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = const [textareaHeight, setTextareaHeight] = useState() const startDragY = useRef(0) const startHeight = useRef(0) - const currentMessageId = useRef() + const currentMessageId = useRef('') const isVision = useMemo(() => isVisionModel(model), [model]) const supportExts = useMemo(() => [...textExts, ...documentExts, ...(isVision ? imageExts : [])], [isVision]) const navigate = useNavigate() diff --git a/src/renderer/src/pages/home/Markdown/CodeBlock.tsx b/src/renderer/src/pages/home/Markdown/CodeBlock.tsx index 764a3eee4..2975f35df 100644 --- a/src/renderer/src/pages/home/Markdown/CodeBlock.tsx +++ b/src/renderer/src/pages/home/Markdown/CodeBlock.tsx @@ -26,7 +26,7 @@ const CodeBlock: React.FC = ({ children, className }) => { const match = /language-(\w+)/.exec(className || '') || children?.includes('\n') const { codeShowLineNumbers, fontSize, codeCollapsible, codeWrappable } = useSettings() const language = match?.[1] ?? 'text' - const [html, setHtml] = useState('') + // const [html, setHtml] = useState('') const { codeToHtml } = useSyntaxHighlighter() const [isExpanded, setIsExpanded] = useState(!codeCollapsible) const [isUnwrapped, setIsUnwrapped] = useState(!codeWrappable) @@ -40,17 +40,14 @@ const CodeBlock: React.FC = ({ children, className }) => { useEffect(() => { const loadHighlightedCode = async () => { const highlightedHtml = await codeToHtml(children, language) - setHtml(highlightedHtml) + if (codeContentRef.current) { + codeContentRef.current.innerHTML = highlightedHtml + setShouldShowExpandButton(codeContentRef.current.scrollHeight > 350) + } } loadHighlightedCode() }, [children, language, codeToHtml]) - useEffect(() => { - if (codeContentRef.current) { - setShouldShowExpandButton(codeContentRef.current.scrollHeight > 350) - } - }, [html]) - useEffect(() => { if (!codeCollapsible) { setIsExpanded(true) @@ -112,7 +109,7 @@ const CodeBlock: React.FC = ({ children, className }) => { isShowLineNumbers={codeShowLineNumbers} isUnwrapped={isUnwrapped} isCodeWrappable={codeWrappable} - dangerouslySetInnerHTML={{ __html: html }} + // dangerouslySetInnerHTML={{ __html: html }} style={{ border: '0.5px solid var(--color-code-background)', borderTopLeftRadius: 0, diff --git a/src/renderer/src/pages/home/Messages/ChatFlowHistory.tsx b/src/renderer/src/pages/home/Messages/ChatFlowHistory.tsx index a9bf4502b..8157ed1d3 100644 --- a/src/renderer/src/pages/home/Messages/ChatFlowHistory.tsx +++ b/src/renderer/src/pages/home/Messages/ChatFlowHistory.tsx @@ -50,7 +50,7 @@ const CustomNode: FC<{ data: any }> = ({ data }) => { let title = '' let backgroundColor = 'var(--bg-color)' let gradientColor = 'rgba(0, 0, 0, 0.03)' - let avatar: JSX.Element | null = null + let avatar: React.ReactNode | null = null // 根据消息类型设置不同的样式和图标 if (nodeType === 'user') { diff --git a/src/renderer/src/pages/home/Messages/Message.tsx b/src/renderer/src/pages/home/Messages/Message.tsx index 39350721d..818511cb4 100644 --- a/src/renderer/src/pages/home/Messages/Message.tsx +++ b/src/renderer/src/pages/home/Messages/Message.tsx @@ -163,7 +163,7 @@ const MessageItem: FC = ({ isLastMessage={isLastMessage} isAssistantMessage={isAssistantMessage} isGrouped={isGrouped} - messageContainerRef={messageContainerRef} + messageContainerRef={messageContainerRef as React.RefObject} setModel={setModel} /> diff --git a/src/renderer/src/pages/home/Messages/MessageAnchorLine.tsx b/src/renderer/src/pages/home/Messages/MessageAnchorLine.tsx index be3c64faa..be70d8f4d 100644 --- a/src/renderer/src/pages/home/Messages/MessageAnchorLine.tsx +++ b/src/renderer/src/pages/home/Messages/MessageAnchorLine.tsx @@ -6,11 +6,11 @@ import { useSettings } from '@renderer/hooks/useSettings' import { getMessageModelId } from '@renderer/services/MessagesService' import { getModelName } from '@renderer/services/ModelService' import { useAppDispatch } from '@renderer/store' -import { updateMessage } from '@renderer/store/messages' -import { Message } from '@renderer/types' +import { updateMessageThunk } from '@renderer/store/messages' +import type { Message } from '@renderer/types' import { isEmoji, removeLeadingEmoji } from '@renderer/utils' import { Avatar } from 'antd' -import { FC, useCallback, useEffect, useRef, useState } from 'react' +import { type FC, useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' interface MessageLineProps { @@ -100,15 +100,9 @@ const MessageAnchorLine: FC = ({ messages }) => { (message: Message) => { const groupMessages = messages.filter((m) => m.askId === message.askId) if (groupMessages.length > 1) { - groupMessages.forEach((m) => { - dispatch( - updateMessage({ - topicId: m.topicId, - messageId: m.id, - updates: { foldSelected: m.id === message.id } - }) - ) - }) + for (const m of groupMessages) { + dispatch(updateMessageThunk(m.topicId, m.id, { foldSelected: m.id === message.id })) + } setTimeout(() => { const messageElement = document.getElementById(`message-${message.id}`) diff --git a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx index 5b5185245..220a5becb 100644 --- a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx +++ b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx @@ -17,12 +17,12 @@ import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup' import TextEditPopup from '@renderer/components/Popups/TextEditPopup' import { isReasoningModel } from '@renderer/config/models' import { TranslateLanguageOptions } from '@renderer/config/translate' -import { useMessageOperations } from '@renderer/hooks/useMessageOperations' +import { useMessageOperations, useTopicLoading } from '@renderer/hooks/useMessageOperations' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { getMessageTitle, resetAssistantMessage } from '@renderer/services/MessagesService' import { translateText } from '@renderer/services/TranslateService' -import { Message, Model } from '@renderer/types' -import { Assistant, Topic } from '@renderer/types' +import type { Message, Model } from '@renderer/types' +import type { Assistant, Topic } from '@renderer/types' import { captureScrollableDivAsBlob, captureScrollableDivAsDataURL, removeTrailingDoubleSpaces } from '@renderer/utils' import { exportMarkdownToJoplin, @@ -62,15 +62,9 @@ const MessageMenubar: FC = (props) => { const [showRegenerateTooltip, setShowRegenerateTooltip] = useState(false) const [showDeleteTooltip, setShowDeleteTooltip] = useState(false) const assistantModel = assistant?.model - const { - loading, - editMessage, - setStreamMessage, - deleteMessage, - resendMessage, - commitStreamMessage, - clearStreamMessage - } = useMessageOperations(topic) + const { editMessage, setStreamMessage, deleteMessage, resendMessage, commitStreamMessage, clearStreamMessage } = + useMessageOperations(topic) + const loading = useTopicLoading(topic) const isUserMessage = message.role === 'user' @@ -382,7 +376,7 @@ const MessageMenubar: FC = (props) => { okButtonProps={{ danger: true }} icon={} onOpenChange={(open) => open && setShowDeleteTooltip(false)} - onConfirm={() => deleteMessage(message)}> + onConfirm={() => deleteMessage(message.id)}> e.stopPropagation()}> = ({ message }) => { // Format tool responses for collapse items const getCollapseItems = () => { - const items: { key: string; label: JSX.Element; children: React.ReactNode }[] = [] + const items: { key: string; label: React.ReactNode; children: React.ReactNode }[] = [] // Add tool responses - toolResponses.forEach((toolResponse: MCPToolResponse) => { + for (const toolResponse of toolResponses) { const { id, tool, status, response } = toolResponse const isInvoking = status === 'invoking' const isDone = status === 'done' @@ -105,7 +105,7 @@ const MessageTools: FC = ({ message }) => { ) }) - }) + } return items } @@ -129,7 +129,9 @@ const MessageTools: FC = ({ message }) => { onCancel={() => setExpandedResponse(null)} footer={null} width="80%" - bodyStyle={{ maxHeight: '80vh', overflow: 'auto' }}> + styles={{ + body: { maxHeight: '80vh', overflow: 'auto' } + }}> {expandedResponse && ( void } +const computeDisplayMessages = (messages: Message[], startIndex: number, displayCount: number) => { + const reversedMessages = [...messages].reverse() + + // 如果剩余消息数量小于 displayCount,直接返回所有剩余消息 + if (reversedMessages.length - startIndex <= displayCount) { + return reversedMessages.slice(startIndex) + } + + const userIdSet = new Set() // 用户消息 id 集合 + const assistantIdSet = new Set() // 助手消息 askId 集合 + const displayMessages: Message[] = [] + + // 处理单条消息的函数 + const processMessage = (message: Message) => { + if (!message) return + + const idSet = message.role === 'user' ? userIdSet : assistantIdSet + const messageId = message.role === 'user' ? message.id : message.askId + + if (!idSet.has(messageId)) { + idSet.add(messageId) + displayMessages.push(message) + return + } + // 如果是相同 askId 的助手消息,也要显示 + displayMessages.push(message) + } + + // 遍历消息直到满足显示数量要求 + for (let i = startIndex; i < reversedMessages.length && userIdSet.size + assistantIdSet.size < displayCount; i++) { + processMessage(reversedMessages[i]) + } + + return displayMessages +} + const Messages: React.FC = ({ assistant, topic, setActiveTopic }) => { const { t } = useTranslation() const { showTopics, topicPosition, showAssistants, messageNavigation } = useSettings() @@ -48,9 +84,9 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) const [hasMore, setHasMore] = useState(false) const [isLoadingMore, setIsLoadingMore] = useState(false) const [isProcessingContext, setIsProcessingContext] = useState(false) - const { messages, displayCount, loading, updateMessages, clearTopicMessages, deleteMessage } = - useMessageOperations(topic) - + const messages = useTopicMessages(topic) + const { displayCount, updateMessages, clearTopicMessages, deleteMessage } = useMessageOperations(topic) + const loading = useTopicLoading(topic) const messagesRef = useRef(messages) useEffect(() => { @@ -58,9 +94,7 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) }, [messages]) useEffect(() => { - const reversedMessages = [...messages].reverse() - const newDisplayMessages = reversedMessages.slice(0, displayCount) - + const newDisplayMessages = computeDisplayMessages(messages, 0, displayCount) setDisplayMessages(newDisplayMessages) setHasMore(messages.length > displayCount) }, [messages, displayCount]) @@ -73,7 +107,15 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) }, [showAssistants, showTopics, topicPosition]) const scrollToBottom = useCallback(() => { - setTimeout(() => containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'auto' }), 50) + if (containerRef.current) { + requestAnimationFrame(() => { + if (containerRef.current) { + containerRef.current.scrollTo({ + top: containerRef.current.scrollHeight + }) + } + }) + } }, []) useEffect(() => { @@ -122,7 +164,7 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) const lastMessage = last(messages) if (lastMessage?.type === 'clear') { - await deleteMessage(lastMessage) + await deleteMessage(lastMessage.id) scrollToBottom() return } @@ -183,10 +225,9 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) setIsLoadingMore(true) setTimeout(() => { const currentLength = displayMessages.length - const reversedMessages = [...messages].reverse() - const moreMessages = reversedMessages.slice(currentLength, currentLength + LOAD_MORE_COUNT) + const newMessages = computeDisplayMessages(messages, currentLength, LOAD_MORE_COUNT) - setDisplayMessages((prev) => [...prev, ...moreMessages]) + setDisplayMessages((prev) => [...prev, ...newMessages]) setHasMore(currentLength + LOAD_MORE_COUNT < messages.length) setIsLoadingMore(false) }, 300) @@ -199,7 +240,6 @@ const Messages: React.FC = ({ assistant, topic, setActiveTopic }) window.message.success(t('message.copy.success')) } }) - return ( = ({ assistant, topic, setActiveTopic }) next={loadMoreMessages} hasMore={hasMore} loader={null} - inverse={true} - scrollableTarget="messages"> + scrollableTarget="messages" + inverse> diff --git a/src/renderer/src/pages/home/Tabs/TopicsTab.tsx b/src/renderer/src/pages/home/Tabs/TopicsTab.tsx index 920a291c6..514160f33 100644 --- a/src/renderer/src/pages/home/Tabs/TopicsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/TopicsTab.tsx @@ -56,7 +56,7 @@ const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic const borderRadius = showTopicTime ? 12 : 'var(--list-item-border-radius)' const [deletingTopicId, setDeletingTopicId] = useState(null) - const deleteTimerRef = useRef() + const deleteTimerRef = useRef(null) const pendingTopics = useMemo(() => { return new Set() diff --git a/src/renderer/src/pages/paintings/PaintingsPage.tsx b/src/renderer/src/pages/paintings/PaintingsPage.tsx index 21d5169a0..638d10056 100644 --- a/src/renderer/src/pages/paintings/PaintingsPage.tsx +++ b/src/renderer/src/pages/paintings/PaintingsPage.tsx @@ -23,11 +23,12 @@ import { translateText } from '@renderer/services/TranslateService' import { useAppDispatch } from '@renderer/store' import { DEFAULT_PAINTING } from '@renderer/store/paintings' import { setGenerating } from '@renderer/store/runtime' -import { FileType, Painting } from '@renderer/types' +import type { FileType, Painting } from '@renderer/types' import { getErrorMessage } from '@renderer/utils' import { Button, Input, InputNumber, Radio, Select, Slider, Switch, Tooltip } from 'antd' import TextArea from 'antd/es/input/TextArea' -import { FC, useEffect, useRef, useState } from 'react' +import type { FC } from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -243,7 +244,7 @@ const PaintingsPage: FC = () => { const { autoTranslateWithSpace } = useSettings() const [spaceClickCount, setSpaceClickCount] = useState(0) const [isTranslating, setIsTranslating] = useState(false) - const spaceClickTimer = useRef() + const spaceClickTimer = useRef(null) const translate = async () => { if (isTranslating) { diff --git a/src/renderer/src/pages/settings/MCPSettings/AddMcpServerPopup.tsx b/src/renderer/src/pages/settings/MCPSettings/AddMcpServerPopup.tsx index 6e8c912fa..5a729237a 100644 --- a/src/renderer/src/pages/settings/MCPSettings/AddMcpServerPopup.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/AddMcpServerPopup.tsx @@ -152,7 +152,12 @@ const PopupContainer: React.FC = ({ server, create, resolve }) => { width={600} transitionName="ant-move-down" centered - bodyStyle={{ maxHeight: '70vh', overflowY: 'auto' }}> + styles={{ + body: { + maxHeight: '70vh', + overflowY: 'auto' + } + }}>
{ {isEditing ? ( el && (inputRefs.current[record.key] = el)} + ref={(el) => { + if (el) { + inputRefs.current[record.key] = el + } + }} value={formatShortcut(shortcut)} placeholder={t('settings.shortcuts.press_shortcut')} onKeyDown={(e) => handleKeyDown(e, record)} diff --git a/src/renderer/src/store/messages.ts b/src/renderer/src/store/messages.ts index 5ee2173dd..6e244b892 100644 --- a/src/renderer/src/store/messages.ts +++ b/src/renderer/src/store/messages.ts @@ -6,9 +6,9 @@ import { fetchChatCompletion } from '@renderer/services/ApiService' import { getAssistantMessage, resetAssistantMessage } from '@renderer/services/MessagesService' import type { AppDispatch, RootState } from '@renderer/store' import type { Assistant, Message, Topic } from '@renderer/types' -import { Model } from '@renderer/types' +import type { Model } from '@renderer/types' import { clearTopicQueue, getTopicQueue, waitForTopicQueue } from '@renderer/utils/queue' -import { cloneDeep, isEmpty, throttle } from 'lodash' +import { isEmpty, throttle } from 'lodash' export interface MessagesState { messagesByTopic: Record @@ -113,14 +113,10 @@ const messagesSlice = createSlice({ ) => { const { topicId, messageId, updates } = action.payload const topicMessages = state.messagesByTopic[topicId] - if (topicMessages) { const message = topicMessages.find((msg) => msg.id === messageId) if (message) { Object.assign(message, updates) - db.topics.update(topicId, { - messages: topicMessages.map((m) => (m.id === message.id ? cloneDeep(message) : cloneDeep(m))) - }) } } }, @@ -255,7 +251,7 @@ export const sendMessage = const isGroupedMessage = messageToReset.length > 1 const resetMessage = resetAssistantMessage(m, isGroupedMessage ? m.model : assistant.model) // 更新状态 - dispatch(updateMessage({ topicId: topic.id, messageId: m.id, updates: resetMessage })) + dispatch(updateMessageThunk(topic.id, m.id, resetMessage)) // 使用重置后的消息 return resetMessage }) @@ -263,7 +259,7 @@ export const sendMessage = const { model, id } = messageToReset const resetMessage = resetAssistantMessage(messageToReset, model) // 更新状态 - dispatch(updateMessage({ topicId: topic.id, messageId: id, updates: resetMessage })) + dispatch(updateMessageThunk(topic.id, id, resetMessage)) // 使用重置后的消息 assistantMessages.push(resetMessage) } @@ -396,10 +392,9 @@ export const sendMessage = } catch (error: any) { console.error('Error in chat completion:', error) dispatch( - updateMessage({ - topicId: topic.id, - messageId: assistantMessage.id, - updates: { status: 'error', error: { message: error.message } } + updateMessageThunk(topic.id, assistantMessage.id, { + status: 'error', + error: { message: error.message } }) ) dispatch(clearStreamMessage({ topicId: topic.id, messageId: assistantMessage.id })) @@ -448,10 +443,9 @@ export const resendMessage = const userMessage = topicMessages.find((m) => m.id === message.askId && m.role === 'user') if (!userMessage) { dispatch( - updateMessage({ - topicId: topic.id, - messageId: message.id, - updates: { status: 'error', error: { message: i18n.t('error.user_message_not_found') } } + updateMessageThunk(topic.id, message.id, { + status: 'error', + error: { message: i18n.t('error.user_message_not_found') } }) ) console.error(i18n.t('error.user_message_not_found')) @@ -521,6 +515,14 @@ export const clearTopicMessagesThunk = (topic: Topic) => async (dispatch: AppDis } } +export const deleteMessageAction = + (topic: Topic, id: string, idType: 'id' | 'askId' = 'id') => + async (dispatch: AppDispatch, getState: () => RootState) => { + const messages = getState().messages.messagesByTopic[topic.id] || [] + const newMessages = messages.filter((m) => m[idType] !== id) + await dispatch(updateMessages(topic, newMessages)) + } + // 修改的 updateMessages thunk,同时更新缓存 export const updateMessages = (topic: Topic, messages: Message[]) => async (dispatch: AppDispatch) => { try { @@ -534,6 +536,28 @@ export const updateMessages = (topic: Topic, messages: Message[]) => async (disp } } +// 新增一个 thunk 来处理消息更新 +export const updateMessageThunk = + (topicId: string, messageId: string, updates: Partial) => + async (dispatch: AppDispatch, getState: () => RootState) => { + try { + // 先更新 Redux 状态 + dispatch(updateMessage({ topicId, messageId, updates })) + + // 然后同步到数据库 + const state = getState() + const topicMessages = state.messages.messagesByTopic[topicId] + if (topicMessages) { + await db.topics.update(topicId, { + messages: topicMessages + }) + } + } catch (error) { + console.error('Failed to update message:', error) + dispatch(setError(error instanceof Error ? error.message : 'Failed to update message')) + } + } + // Selectors export const selectCurrentTopicId = (state: RootState): string | null => { const messagesState = state.messages @@ -546,11 +570,10 @@ export const selectTopicMessages = createSelector( ) // 获取特定话题的loading状态 -export const selectTopicLoading = (state: RootState, topicId?: string): boolean => { - const messagesState = state.messages as MessagesState - const currentTopicId = topicId || messagesState.currentTopic?.id || '' - return currentTopicId ? (messagesState.loadingByTopic[currentTopicId] ?? false) : false -} +export const selectTopicLoading = createSelector( + [(state: RootState) => state.messages.loadingByTopic, (_, topicId?: string) => topicId], + (loadingByTopic, topicId) => (topicId ? (loadingByTopic[topicId] ?? false) : false) +) export const selectDisplayCount = (state: RootState): number => { const messagesState = state.messages as MessagesState diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts index 7684c78b1..10679d929 100644 --- a/src/renderer/src/utils/index.ts +++ b/src/renderer/src/utils/index.ts @@ -306,7 +306,7 @@ export async function captureDiv(divRef: React.RefObject) { return Promise.resolve(undefined) } -export const captureScrollableDiv = async (divRef: React.RefObject) => { +export const captureScrollableDiv = async (divRef: React.RefObject) => { if (divRef.current) { try { const div = divRef.current @@ -392,7 +392,7 @@ export const captureScrollableDiv = async (divRef: React.RefObject) => { +export const captureScrollableDivAsDataURL = async (divRef: React.RefObject) => { return captureScrollableDiv(divRef).then((canvas) => { if (canvas) { return canvas.toDataURL('image/png') @@ -401,7 +401,10 @@ export const captureScrollableDivAsDataURL = async (divRef: React.RefObject, func: BlobCallback) => { +export const captureScrollableDivAsBlob = async ( + divRef: React.RefObject, + func: BlobCallback +) => { await captureScrollableDiv(divRef).then((canvas) => { canvas?.toBlob(func, 'image/png') }) diff --git a/src/renderer/src/windows/mini/App.tsx b/src/renderer/src/windows/mini/App.tsx index 56075c4f2..17367a0c8 100644 --- a/src/renderer/src/windows/mini/App.tsx +++ b/src/renderer/src/windows/mini/App.tsx @@ -9,7 +9,7 @@ import { SyntaxHighlighterProvider } from '../../context/SyntaxHighlighterProvid import { ThemeProvider } from '../../context/ThemeProvider' import HomeWindow from './home/HomeWindow' -function MiniWindow(): JSX.Element { +function MiniWindow(): React.ReactElement { return ( diff --git a/yarn.lock b/yarn.lock index 44752ba3e..0b7d4fa44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -165,6 +165,17 @@ __metadata: languageName: node linkType: hard +"@ant-design/v5-patch-for-react-19@npm:^1.0.3": + version: 1.0.3 + resolution: "@ant-design/v5-patch-for-react-19@npm:1.0.3" + peerDependencies: + antd: ">=5.22.6" + react: ">=19.0.0" + react-dom: ">=19.0.0" + checksum: 10c0/e3848c929b01a0a29a41e2886f489932e54d9665dd990c60c4b505e21740da2ef0db0d08f4dc7591651fa1f21b15227e7bb40f40280742225b4da74fd18cc899 + languageName: node + linkType: hard + "@anthropic-ai/sdk@npm:^0.38.0": version: 0.38.0 resolution: "@anthropic-ai/sdk@npm:0.38.0" @@ -3455,19 +3466,12 @@ __metadata: languageName: node linkType: hard -"@types/prop-types@npm:*": - version: 15.7.14 - resolution: "@types/prop-types@npm:15.7.14" - checksum: 10c0/1ec775160bfab90b67a782d735952158c7e702ca4502968aa82565bd8e452c2de8601c8dfe349733073c31179116cf7340710160d3836aa8a1ef76d1532893b1 - languageName: node - linkType: hard - -"@types/react-dom@npm:^18.2.18": - version: 18.3.5 - resolution: "@types/react-dom@npm:18.3.5" +"@types/react-dom@npm:^19.0.4": + version: 19.0.4 + resolution: "@types/react-dom@npm:19.0.4" peerDependencies: - "@types/react": ^18.0.0 - checksum: 10c0/b163d35a6b32a79f5782574a7aeb12a31a647e248792bf437e6d596e2676961c394c5e3c6e91d1ce44ae90441dbaf93158efb4f051c0d61e2612f1cb04ce4faa + "@types/react": ^19.0.0 + checksum: 10c0/4e71853919b94df9e746a4bd73f8180e9ae13016333ce9c543dcba9f4f4c8fe6e28b038ca6ee61c24e291af8e03ca3bc5ded17c46dee938fcb32d71186fda7a3 languageName: node linkType: hard @@ -3480,7 +3484,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*": +"@types/react@npm:*, @types/react@npm:^19.0.12": version: 19.0.12 resolution: "@types/react@npm:19.0.12" dependencies: @@ -3489,16 +3493,6 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^18.2.48": - version: 18.3.19 - resolution: "@types/react@npm:18.3.19" - dependencies: - "@types/prop-types": "npm:*" - csstype: "npm:^3.0.2" - checksum: 10c0/236bfe0c4748ada1a640f13573eca3e0fc7c9d847b442947adb352b0718d6d285357fd84c33336c8ffb8cbfabc0d58a43a647c7fd79857fecd61fb58ab6f7918 - languageName: node - linkType: hard - "@types/responselike@npm:^1.0.0": version: 1.0.3 resolution: "@types/responselike@npm:1.0.3" @@ -3766,6 +3760,7 @@ __metadata: "@agentic/exa": "npm:^7.3.3" "@agentic/searxng": "npm:^7.3.3" "@agentic/tavily": "npm:^7.3.3" + "@ant-design/v5-patch-for-react-19": "npm:^1.0.3" "@anthropic-ai/sdk": "npm:^0.38.0" "@cherrystudio/embedjs": "npm:^0.1.28" "@cherrystudio/embedjs-libsql": "npm:^0.1.28" @@ -3804,8 +3799,8 @@ __metadata: "@types/md5": "npm:^2.3.5" "@types/node": "npm:^18.19.9" "@types/pako": "npm:^1.0.2" - "@types/react": "npm:^18.2.48" - "@types/react-dom": "npm:^18.2.18" + "@types/react": "npm:^19.0.12" + "@types/react-dom": "npm:^19.0.4" "@types/react-infinite-scroll-component": "npm:^5.0.0" "@types/tinycolor2": "npm:^1" "@vitejs/plugin-react": "npm:^4.2.1" @@ -3853,8 +3848,9 @@ __metadata: p-queue: "npm:^8.1.0" prettier: "npm:^3.5.3" proxy-agent: "npm:^6.5.0" - react: "npm:^18.2.0" - react-dom: "npm:^18.2.0" + rc-virtual-list: "npm:^3.18.5" + react: "npm:^19.0.0" + react-dom: "npm:^19.0.0" react-hotkeys-hook: "npm:^4.6.1" react-i18next: "npm:^14.1.2" react-infinite-scroll-component: "npm:^6.1.0" @@ -3884,9 +3880,6 @@ __metadata: vite: "npm:^5.0.12" webdav: "npm:^5.8.0" zipread: "npm:^1.3.3" - peerDependencies: - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 languageName: unknown linkType: soft @@ -9190,7 +9183,7 @@ __metadata: languageName: node linkType: hard -"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": +"js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed @@ -9798,17 +9791,6 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.1.0": - version: 1.4.0 - resolution: "loose-envify@npm:1.4.0" - dependencies: - js-tokens: "npm:^3.0.0 || ^4.0.0" - bin: - loose-envify: cli.js - checksum: 10c0/655d110220983c1a4b9c0c679a2e8016d4b67f6e9c7b5435ff5979ecdb20d0813f4dec0a08674fcbdd4846a3f07edbb50a36811fd37930b94aaa0d9daceb017e - languageName: node - linkType: hard - "lop@npm:^0.4.1": version: 0.4.2 resolution: "lop@npm:0.4.2" @@ -13272,7 +13254,7 @@ __metadata: languageName: node linkType: hard -"rc-virtual-list@npm:^3.14.2, rc-virtual-list@npm:^3.5.1, rc-virtual-list@npm:^3.5.2": +"rc-virtual-list@npm:^3.14.2, rc-virtual-list@npm:^3.18.5, rc-virtual-list@npm:^3.5.1, rc-virtual-list@npm:^3.5.2": version: 3.18.5 resolution: "rc-virtual-list@npm:3.18.5" dependencies: @@ -13301,15 +13283,14 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.2.0": - version: 18.3.1 - resolution: "react-dom@npm:18.3.1" +"react-dom@npm:^19.0.0": + version: 19.0.0 + resolution: "react-dom@npm:19.0.0" dependencies: - loose-envify: "npm:^1.1.0" - scheduler: "npm:^0.23.2" + scheduler: "npm:^0.25.0" peerDependencies: - react: ^18.3.1 - checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85 + react: ^19.0.0 + checksum: 10c0/a36ce7ab507b237ae2759c984cdaad4af4096d8199fb65b3815c16825e5cfeb7293da790a3fc2184b52bfba7ba3ff31c058c01947aff6fd1a3701632aabaa6a9 languageName: node linkType: hard @@ -13480,12 +13461,10 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.2.0": - version: 18.3.1 - resolution: "react@npm:18.3.1" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10c0/283e8c5efcf37802c9d1ce767f302dd569dd97a70d9bb8c7be79a789b9902451e0d16334b05d73299b20f048cbc3c7d288bbbde10b701fa194e2089c237dbea3 +"react@npm:^19.0.0": + version: 19.0.0 + resolution: "react@npm:19.0.0" + checksum: 10c0/9cad8f103e8e3a16d15cb18a0d8115d8bd9f9e1ce3420310aea381eb42aa0a4f812cf047bb5441349257a05fba8a291515691e3cb51267279b2d2c3253f38471 languageName: node linkType: hard @@ -14183,12 +14162,10 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.23.2": - version: 0.23.2 - resolution: "scheduler@npm:0.23.2" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78 +"scheduler@npm:^0.25.0": + version: 0.25.0 + resolution: "scheduler@npm:0.25.0" + checksum: 10c0/a4bb1da406b613ce72c1299db43759526058fdcc413999c3c3e0db8956df7633acf395cb20eb2303b6a65d658d66b6585d344460abaee8080b4aa931f10eaafe languageName: node linkType: hard From 1a85b8bd5dae3aa1b9005812c82d96026e47af6f Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 27 Mar 2025 13:19:04 +0800 Subject: [PATCH 11/13] feat(i18n): update assistant settings titles and add new translations for multiple languages --- src/renderer/src/i18n/locales/en-us.json | 4 +++- src/renderer/src/i18n/locales/ja-jp.json | 4 +++- src/renderer/src/i18n/locales/ru-ru.json | 4 +++- src/renderer/src/i18n/locales/zh-cn.json | 4 +++- src/renderer/src/i18n/locales/zh-tw.json | 6 +++-- .../src/pages/home/Tabs/SettingsTab.tsx | 23 +++++++++++++------ .../settings/AssistantSettings/index.tsx | 11 +++++---- 7 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index af7483085..7604210c1 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -32,7 +32,9 @@ "title": "Agents" }, "assistants": { + "title": "Assistants", "abbr": "Assistant", + "settings.title": "Assistant Settings", "clear.content": "Clearing the topic will delete all topics and files in the assistant. Are you sure you want to continue?", "clear.title": "Clear topics", "copy.title": "Copy Assistant", @@ -53,7 +55,7 @@ "settings.reasoning_effort.medium": "medium", "settings.reasoning_effort.off": "off", "settings.reasoning_effort.tip": "Only supports OpenAI o-series and Anthropic reasoning models", - "title": "Assistants" + "settings.more": "Assistant Settings" }, "auth": { "error": "API key automatically obtained failed, please get it manually", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index b47b041cf..b4501f8ab 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -32,7 +32,9 @@ "title": "エージェント" }, "assistants": { + "title": "アシスタント", "abbr": "アシスタント", + "settings.title": "アシスタント設定", "clear.content": "トピックをクリアすると、アシスタント内のすべてのトピックとファイルが削除されます。続行しますか?", "clear.title": "トピックをクリア", "copy.title": "アシスタントをコピー", @@ -53,7 +55,7 @@ "settings.reasoning_effort.medium": "中程度", "settings.reasoning_effort.off": "オフ", "settings.reasoning_effort.tip": "OpenAIのoシリーズとAnthropicの推論モデルのみサポートしています", - "title": "アシスタント" + "settings.more": "アシスタント設定" }, "auth": { "error": "APIキーの自動取得に失敗しました。手動で取得してください", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 6a67f05d7..a7c94c270 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -32,7 +32,9 @@ "title": "Агенты" }, "assistants": { + "title": "Ассистенты", "abbr": "Ассистент", + "settings.title": "Настройки ассистента", "clear.content": "Очистка топика удалит все топики и файлы в ассистенте. Вы уверены, что хотите продолжить?", "clear.title": "Очистить топики", "copy.title": "Копировать ассистента", @@ -53,7 +55,7 @@ "settings.reasoning_effort.medium": "Средняя", "settings.reasoning_effort.off": "Выключено", "settings.reasoning_effort.tip": "Поддерживается только моделями с рассуждением OpenAI o-series и Anthropic", - "title": "Ассистенты" + "settings.more": "Настройки ассистента" }, "auth": { "error": "Автоматический получение ключа API не удалось, пожалуйста, получите ключ вручную", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index f51df767c..73e62e73f 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -32,7 +32,9 @@ "title": "智能体" }, "assistants": { + "title": "助手", "abbr": "助手", + "settings.title": "助手设置", "clear.content": "清空话题会删除助手下所有话题和文件,确定要继续吗?", "clear.title": "清空话题", "copy.title": "复制助手", @@ -53,7 +55,7 @@ "settings.reasoning_effort.medium": "中", "settings.reasoning_effort.off": "关", "settings.reasoning_effort.tip": "仅支持 OpenAI o-series 和 Anthropic 推理模型", - "title": "助手" + "settings.more": "助手设置" }, "auth": { "error": "自动获取密钥失败,请手动获取", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 5bcb0c3cb..4f778ace7 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -32,7 +32,9 @@ "title": "智慧代理人" }, "assistants": { + "title": "助手", "abbr": "助手", + "settings.title": "助手設定", "clear.content": "清空話題會刪除助手下所有主題和檔案,確定要繼續嗎?", "clear.title": "清空話題", "copy.title": "複製助手", @@ -52,8 +54,8 @@ "settings.reasoning_effort.low": "短", "settings.reasoning_effort.medium": "中", "settings.reasoning_effort.off": "關", - "settings.reasoning_effort.tip": "僅支援 OpenAI o 系列和 Anthropic 推理模型", - "title": "助手" + "settings.reasoning_effort.tip": "僅支援 OpenAI o-series 和 Anthropic 推理模型", + "settings.more": "助手設定" }, "auth": { "error": "自動取得金鑰失敗,請手動取得", diff --git a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx index 70281fd4c..9a45d4959 100644 --- a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx @@ -1,4 +1,4 @@ -import { CheckOutlined, QuestionCircleOutlined, ReloadOutlined } from '@ant-design/icons' +import { CheckOutlined, QuestionCircleOutlined, ReloadOutlined, SettingOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' import Scrollbar from '@renderer/components/Scrollbar' import { @@ -13,6 +13,7 @@ import { codeThemes } from '@renderer/context/SyntaxHighlighterProvider' import { useAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' import { SettingDivider, SettingRow, SettingRowTitle, SettingSubtitle } from '@renderer/pages/settings' +import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings' import { getDefaultModel } from '@renderer/services/AssistantService' import { useAppDispatch } from '@renderer/store' import { @@ -37,7 +38,7 @@ import { } from '@renderer/store/settings' import { Assistant, AssistantSettings, CodeStyleVarious, ThemeMode, TranslateLanguageVarious } from '@renderer/types' import { modalConfirm } from '@renderer/utils' -import { Col, InputNumber, Row, Segmented, Select, Slider, Switch, Tooltip } from 'antd' +import { Button, Col, InputNumber, Row, Segmented, Select, Slider, Switch, Tooltip } from 'antd' import { FC, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -145,11 +146,19 @@ const SettingsTab: FC = (props) => { return ( - - {t('settings.messages.model.title')}{' '} - - - + + + {t('assistants.settings.title')}{' '} + + + + +