Compare commits

...

1206 Commits

Author SHA1 Message Date
kangfenmao
ce70c7239c chore(version): 1.0.0 2025-02-23 15:10:24 +08:00
kangfenmao
c354537f30 feat: Add Tavily dark mode logo and improve web search settings UI 2025-02-23 15:10:01 +08:00
kangfenmao
fb6b0b0c97 refactor: Rename AssistantItemComponent to AssistantItem and update imports 2025-02-23 14:30:17 +08:00
wnzzer
fc59144b1d fix:清空话题总是修复当前话题 (#2167) 2025-02-23 14:26:31 +08:00
kangfenmao
cacd0a1387 feat: Improve web search UI and localization 2025-02-23 14:22:09 +08:00
kangfenmao
af9763d142 chore: Update Tavily core package and remove js-tiktoken dependency 2025-02-23 11:01:30 +08:00
kangfenmao
b9402a8370 feat: add web search 2025-02-23 11:01:29 +08:00
kangfenmao
97a08f00a3 chore(version): 0.9.30 2025-02-23 07:02:00 +08:00
kangfenmao
4e20bd1ef8 feat: Enhance assistant emoji and popup UI interactions 2025-02-23 06:45:35 +08:00
George·Dong
5c2d936688 feat: 添加Notion导出自动分页功能 (#2098)
* fix: 长对话Notion导出失败(分页导出)

* feat: 添加Notion导出自动分页设置
2025-02-23 06:45:26 +08:00
ousugo
6b34aac263 feat: Auto-select newly added knowledge base 2025-02-23 00:17:46 +08:00
kangfenmao
f59f6ade69 chore(version): 0.9.29 2025-02-22 22:58:19 +08:00
ousugo
50478c600f fix: Regenerat messages don't use @ specified models 2025-02-22 22:35:02 +08:00
ousugo
bba5cac246 fix: Restore textarea focus after selecting mention model via mouse 2025-02-22 22:34:16 +08:00
shijian
b9b31aed52 fix: 复制聊天信息按钮显示不全 2025-02-22 22:32:30 +08:00
kangfenmao
40203fb721 fix: Remove LM Studio provider from initial state 2025-02-22 22:31:37 +08:00
kangfenmao
b83343a8b9 fix: Remove duplicate empty LM Studio providers 2025-02-22 13:35:14 +08:00
aber0724
7677850547 feat: Add Monica minapp 2025-02-22 11:27:30 +08:00
sijie-chan
d0e233f1b3 fix: 添加目录到知识库任意一个失败界面上会展示失败 2025-02-22 11:26:31 +08:00
kangfenmao
903d0043ba chore(version): 0.9.28 2025-02-21 18:25:30 +08:00
kangfenmao
811815d69d feat: update miniapp logo 2025-02-21 18:25:23 +08:00
kangfenmao
d5b9c35f0a feat: Enhance topic message clearing functionality
关于“清空话题”的Bug反馈 #2107

close #2107
2025-02-21 16:48:04 +08:00
suyao
83c8f06b81 fix: add first message handling in mini home window 2025-02-21 16:32:56 +08:00
kangfenmao
acb2ea30fb feat: add export function to message 2025-02-21 16:27:07 +08:00
kangfenmao
55317b5608 refactor: Remove chat settings toolbar button from input bar 2025-02-21 14:31:05 +08:00
kangfenmao
b42a5c5e63 chore: Upgrade Yarn and TypeScript patch versions 2025-02-21 14:18:16 +08:00
kangfenmao
cc76fe19f9 feat: Synchronize and clean up localization files
This commit involves several improvements to localization files across different languages:
- Reordered and cleaned up translation keys
- Removed redundant entries
- Ensured consistent ordering of keys
- Added missing translations for various features
- Normalized whitespace and formatting
2025-02-21 14:15:36 +08:00
落子
cf2d7ba8b4 feat: add "Copy as" options to topics right click menu (#2095)
* feat: Add copy topic as image and Markdown functionality

* add translation
2025-02-21 13:59:34 +08:00
Avan
1c163c55b8 feat: baidu ai search 2025-02-21 13:58:19 +08:00
kangfenmao
69bb661b5a feat: Add API host formatting utility function 2025-02-21 13:50:24 +08:00
lizhixuan
f062c56de4 refactor: Move abort controller to utils and update imports 2025-02-21 12:50:18 +08:00
lizhixuan
4c9bd02f8e feat: Add message completion abort functionality 2025-02-21 12:50:18 +08:00
kangfenmao
241cb0c0d8 refactor: Simplify translation history clear button configuration 2025-02-21 11:17:41 +08:00
ousugo
6a57973864 feat: Add newline tooltip for translation input 2025-02-21 11:16:02 +08:00
kangfenmao
369f629206 refactor: Simplify embedding model regex pattern 2025-02-21 09:46:26 +08:00
kangfenmao
09a8f83650 refactor: Clean up MinAppsPopover component formatting 2025-02-20 15:35:51 +08:00
kangfenmao
40912eaaf4 refactor: Simplify topic deletion interaction 2025-02-20 15:33:24 +08:00
ousugo
0d236a94ab feat: Add direct topic deletion shortcut 2025-02-20 15:19:32 +08:00
ousugo
3a936e0f26 feat: Add confirmation dialog for topic deletion 2025-02-20 15:19:32 +08:00
Yrom
81a35d129d feat: add developer tools button in MinApp for development mode 2025-02-20 15:19:10 +08:00
ousugo
e2d0c3bbce feat: Add emoji selection for assistant name 2025-02-20 15:17:04 +08:00
kangfenmao
12c9d810a2 feat: Add C and C++ file extensions to supported text files
close #2066
2025-02-20 14:45:11 +08:00
kangfenmao
b18b161094 fix: Update migration config to set default thought auto-collapse 2025-02-20 12:24:24 +08:00
ousugo
32da853f27 feat: Add thought content auto-collapse setting 2025-02-20 12:22:45 +08:00
ousugo
bf51a0b5c6 feat: Add copy functionality to message reasoning content 2025-02-20 12:22:45 +08:00
kangfenmao
ae71a7be9e refactor: Simplify translation page layout and styling 2025-02-20 12:16:38 +08:00
Chen Tao
0fb6795833 feat: add shortcuts to open settings page (#1964)
* feat: add shortcuts to open settings page
* refactor: simplify code
* fix: change to useHotKeys
2025-02-20 12:10:16 +08:00
jtsang4
dd6d228760 feat: Add backspace handling to remove last mentioned model 2025-02-20 12:08:25 +08:00
eeee0717
8c5999dc82 fix: knowledge base status problem 2025-02-20 12:07:05 +08:00
kangfenmao
31b0fbf775 fix: Improve null handling in code block and syntax highlighting 2025-02-20 12:04:29 +08:00
kangfenmao
39fe583030 refactor: Remove default grid settings from migration config 2025-02-20 11:54:21 +08:00
littel_penguin66
5c19695e21 feat: add dify miniapp (#1999)
* add Dify miniapp

* add wpslingxi,lechat,abacus miniapps

* resize pic

* change png to webp

* update 71
2025-02-20 11:52:36 +08:00
FischLu
6e4610e337 fix: fix translation 2025-02-20 11:50:49 +08:00
onlyfeng
eb2439b90c fix(KnowledgeBase): handle JSON parse failure by falling back to text file processing to avoid error display affecting normal progress indication 2025-02-20 11:44:36 +08:00
kangfenmao
a4a0980cd3 fix: Ignore hidden files when traversing directories 2025-02-20 11:43:01 +08:00
FischLu
7d99765589 fix: fix the issue where the menu scrolls back to the top after clicking an option 2025-02-19 21:40:32 +08:00
kangfenmao
5f6cf1bd66 docs: Update README files with comprehensive TODO list and minor formatting improvements 2025-02-19 20:07:10 +08:00
Teo
02930a2793 feat: 添加翻译历史功能 2025-02-19 19:38:59 +08:00
Yrom
b31b1c7908 feat: Support dark theme for PlantUML 2025-02-19 18:37:05 +08:00
Yrom
d0ee764732 feat: Add PlantUML diagram support (via PlantUML official online server) 2025-02-19 18:37:05 +08:00
ousugo
01cd10b364 refactor: Simplify model type detection with utility functions 2025-02-19 18:33:51 +08:00
ousugo
29ba156b9a feat: Add Doubao provider support for model type detection
(cherry picked from commit 008b84e128484c98eb59c75d8f44cbc320509049)
2025-02-19 18:33:51 +08:00
ousugo
e541c7b429 feat: Add model generation check before narrow mode toggle 2025-02-19 18:33:16 +08:00
ousugo
5f4142f0c4 chore: Enhance issue templates with improved guidance and visual context 2025-02-19 17:19:15 +08:00
jiangjiwei
95bbc70c93 feat: 为 Notion 导出添加可配置的页面名称 key 2025-02-19 17:18:45 +08:00
ousugo
4add56ae6a fix: Ensure model is updated when setting default model 2025-02-19 17:13:52 +08:00
Teo
16f87537a2 refactor: 翻译页UI重构 2025-02-19 15:53:52 +08:00
kangfenmao
7f05626a8f chore(version): 0.9.27 2025-02-19 10:21:12 +08:00
kangfenmao
2094e2201a feat: Add web search support for OpenRouter provider 2025-02-19 10:11:49 +08:00
icinggslits
e0fcdf43c5 feature: Adaptive height of textarea on translation page 2025-02-19 09:57:26 +08:00
kangfenmao
affc866c17 feat: Add default API host for DMX provider in migration 2025-02-19 09:45:41 +08:00
kangfenmao
799267049f fix: Safely update topic with existing topic data 2025-02-19 09:35:10 +08:00
kangfenmao
cb8d47a17b fix: Improve knowledge base processing and deletion handling 2025-02-19 09:28:36 +08:00
kangfenmao
c494288f7b refactor: Simplify DragableList styling and remove unnecessary margins 2025-02-19 09:21:10 +08:00
suyao
2c3f89dbde fix: update model identification with provider-specific uniqueness 2025-02-19 09:14:45 +08:00
ousugo
4721a660fa feat: Add German language support to translation options 2025-02-19 08:21:00 +08:00
George·Dong
6aaa3def0d feat: 添加Notion文档按钮Tooltip 2025-02-19 08:20:21 +08:00
George·Dong
045708d9b3 fix: 修改导出到Notion的相关提示 2025-02-19 08:20:21 +08:00
icinggslits
9ffe92d378 fix: Update language options promptly 2025-02-19 08:18:41 +08:00
首都爱护动物协会
7159481217 Updated provider information 2025-02-19 07:10:45 +08:00
kangfenmao
d07e136037 fix: Add top margin to 'Add Assistant' button in AssistantsTab 2025-02-18 21:17:13 +08:00
Yrom
b38a9c954a feat: Enable search capability for Qwen commercial version model 2025-02-18 21:14:43 +08:00
kangfenmao
7139d5093a chore(version): 0.9.26 2025-02-18 20:55:04 +08:00
kangfenmao
9e283d6930 fix: Update agent knowledge base field name and handling 2025-02-18 20:55:04 +08:00
Chen Tao
c9a4e12765 feat: artifacts add open external (#1812)
* feat: artifacts add open external

* fix: remove modal
2025-02-18 19:56:39 +08:00
Teo
7bd644451b fix: 解决生成过程中出现错误内容被清空覆盖问题 2025-02-18 19:46:50 +08:00
美兰十三
5a00bdcbc6 fix: 修复mac下快捷键注册control被替换成command的问题 2025-02-18 19:46:28 +08:00
eeee0717
3c958c3d11 feat: 目录进度可视化 2025-02-18 19:45:47 +08:00
kangfenmao
1d5ace0fb2 feat: Add 'off' option for reasoning effort in assistant settings 2025-02-18 18:16:14 +08:00
ousugo
f8fce871da fix: Recalculate token consumption after modifying the message, resolve #1829 2025-02-18 16:34:52 +08:00
ousugo
de76d3fedc fix: Improve DragableList component styling and placeholder handling 2025-02-18 16:26:34 +08:00
lucifer9
b2c6662192 adjust Notion database ID input width in DataSettings 2025-02-18 16:22:53 +08:00
lucifer9
bf8a7c01b0 Refactor WebDAV i18n and UI for improved flexibility and localization
- i18n Updates:
   - Refactored WebDAV-related translations into nested JSON structures for better organization.
   - Added support for pluralization in time intervals (minutes and hours) across all locales (en-us, ja-jp, ru-ru, zh-cn, zh-tw).

 - UI Enhancements:
   - Updated `DataSettings` and `WebDavSettings` components to use the new i18n keys for time intervals.
   - Improved the `Select` dropdown for sync intervals with dynamic pluralization based on locale.
   - Adjusted input field widths for better alignment and consistency.

 - Code Cleanup:
   - Removed redundant comments and unused code in `WebDavSettings.tsx`.
   - Simplified button and input styling for a cleaner layout.
2025-02-18 16:22:53 +08:00
ousugo
fb8ed35b59 fix: Clicking the taskbar icon while enable the Quick Assistant can't open the main window 2025-02-18 16:01:10 +08:00
ousugo
7c4d81c108 feat: Add kimi-latest model support in vision and model logos 2025-02-18 15:50:01 +08:00
kangfenmao
7199f73e06 style: Adjust horizontal message layout display property 2025-02-18 15:48:13 +08:00
Teo
869e56b53c style: 优化聊天窗口UI (#1881) 2025-02-18 11:43:42 +08:00
MyPrototypeWhat
f99851fb6b feat: Conditionally hide thinking loader for paused messages (#1875)
Co-authored-by: lizhixuan <zhixuan.li@banosuperapp.com>
2025-02-18 11:10:48 +08:00
kangfenmao
c94450db44 chore(version): 0.9.25 2025-02-18 10:41:44 +08:00
kangfenmao
195ef92acc feat: Add size prop to MessageThought Collapse component 2025-02-18 10:12:30 +08:00
kangfenmao
a67370426b fix: Handle undefined provider in model name generation 2025-02-18 09:56:26 +08:00
kangfenmao
9d35205681 feat: Improve system prompt styling with theme-aware background 2025-02-18 09:42:52 +08:00
icinggslits
98087e50db feat: Backspace deletes clipboard text in MiniApp 2025-02-18 08:13:56 +08:00
icinggslits
bedac4f59d fix: init zoom 2025-02-18 08:11:38 +08:00
ousugo
aba3874797 refactor: Improve PromptPopup text area focus and cursor placement 2025-02-18 08:10:58 +08:00
ousugo
3383280726 feat: Improve text edit popup focus and cursor placement 2025-02-18 08:10:58 +08:00
kangfenmao
0c13e708b9 refactor: Extract message group menu bar into a separate component 2025-02-17 23:21:24 +08:00
kangfenmao
bc77c423b3 fix: Adjust paragraph margin when followed by list 2025-02-17 23:08:17 +08:00
kangfenmao
4821756301 fix: Conditionally render message group border based on popover state 2025-02-17 23:02:49 +08:00
Chen Tao
78290ca70e feat: add knowledge base filter (#1822)
* feat: add search filter

* chore
2025-02-17 22:18:10 +08:00
kangfenmao
7feeb07624 refactor: Extract message group settings into a separate component 2025-02-17 22:14:47 +08:00
luwux
93e28ed916 improvement(shortcut): Supports Option + Space on Mac
Supports the Option (⌥) + Space shortcut, as it's the default shortcut for ChatGPT Desktop app to show popup.
2025-02-17 19:04:08 +08:00
ousugo
b4aaf052fe feat: Add page title for Cherry Studio, resolve #1222 2025-02-17 19:02:56 +08:00
rebecca554owen
b37e0389fc fix 2025-02-17 18:38:22 +08:00
kangfenmao
e1ebe069a5 feat: Add grid mode settings for message display 2025-02-17 18:35:36 +08:00
kangfenmao
d73912ee3b feat: Enhance Notion settings with placeholders and help icon 2025-02-17 17:19:24 +08:00
kangfenmao
f81c7c7a6c feat: update knowledge base file upload hint text 2025-02-17 16:50:34 +08:00
FischLu
5a7bcd5997 feat: improve model mention autocomplete behavior under IME 2025-02-17 16:38:44 +08:00
duanyongcheng
09a347cae4 feat: show provider in mesage 2025-02-17 16:38:00 +08:00
Chen Tao
266f909045 feat: allow knowledge base multiple search #1346 (#1773)
* feat: agent can select multiple knowledge bases

* feat: basic search multiple knowledge base

* fix bug: knowledge base is delete, assistants and agents sync delete

* fix bug: assistant and knowledge base button sync

* feat: allow to search multiple knowledge base

* chore: finish rebase to upstream/main
2025-02-17 16:36:25 +08:00
cl1107
bad2f15c1f feat: Add a new grid mode for message display. (#1626)
* chore(version): 0.9.23

* feat(renderer): 新增网格模式的消息展示方式

* feat(message): 新增消息网格展示相关设置

* 根据 gridPopoverTrigger 属性动态设置消息分组的样式

* 在 MessageMenubar 组件中,各个按钮 click 事件阻止事件冒泡,避免打开 popover

* 多模型回答样式添加网格模式并优化消息样式

---------

Co-authored-by: kangfenmao <kangfenmao@qq.com>
2025-02-17 16:36:01 +08:00
shniubobo
e3115d00bf fix: Rendering error with MathJax for Chinese text 2025-02-17 16:24:38 +08:00
首都爱护动物协会
0c0ccf3d11 update provider info 2025-02-17 16:21:09 +08:00
ousugo
2076e6f998 fix: open current webview URL when launching external link 2025-02-17 16:20:12 +08:00
ousugo
b49d80b78d fix: Clicking the help button always opens a new webview
(cherry picked from commit 4939afafabcbfb294f00d21053939cad8238731e)
2025-02-17 16:19:31 +08:00
kangfenmao
ab5e830ed1 docs: remove Chinese issue templates for bug reports, feature requests, and questions 2025-02-17 12:02:07 +08:00
ousugo
e0eca97053 fix: update Baidu API key URL in provider configuration, resolve #1794 2025-02-17 11:55:53 +08:00
George·Dong
d175212d9a fix: 修复切换助手时无法正确切换到助手默认模型的问题 (#1776) 2025-02-17 11:28:17 +08:00
Shelly
642ce160a1 fix: 修复同名模型选择问题 (#1772)
1. 同名模型显示的供应商名称问题
2. 同名模型不同供应商不能被同时选择

Co-authored-by: duanyongcheng <duanyongcheng77@gmail.com>
2025-02-17 09:47:01 +08:00
Wenwei Lin
574d02a8c9 feat: support json and draftsExport file in knowledge base (#1717) 2025-02-17 08:25:07 +08:00
ousugo
7764507d74 feat: Expand reasoning model regex to include 'thinking' keyword 2025-02-17 08:16:47 +08:00
ousugo
fa8bf61532 fix: sidebar navigation and active state handling
- 当固定在侧边栏的小程序被打开时,对应图标显示为被选中
- 修复点击两次主题切换按钮会导致当前 Webview 被错误关闭的问题
- 修复当 Webview 处于打开状态,点击侧边栏按钮无法立即跳转到对应界面的问题
- 修复打开帮助文档,其按钮没有显示为被选中的问题
- 修复在设置界面时打开帮助文档,设置按钮继续显示为被选中的问题
2025-02-17 08:15:03 +08:00
ousugo
30e8cef9cc fix: correction of the capitalization of Perplexity names 2025-02-17 08:13:56 +08:00
ousugo
1a2861e81a fix: Fix the miniapp sorting problem, resolve #1725
- 修复小程序拖动排序不生效的问题
- 修复小程序拖动排序时列表滚动排序不生效的问题
2025-02-17 08:13:56 +08:00
Neal_Tan
653e5d82ed docs: optimize issues (#1790) 2025-02-17 08:12:40 +08:00
kangfenmao
5be0e0ae72 style: Enhance scrollbar appearance in mention models dropdown 2025-02-16 13:56:10 +08:00
FischLu
b92b46f2b0 refine code 2025-02-16 13:54:32 +08:00
FischLu
23686d4926 feat: implement select mode menu autoscroll for long mode lists 2025-02-16 13:54:32 +08:00
kangfenmao
b340b40bcf Revert "fix: Improve the @ model list experience"
This reverts commit c53d63f7af.
2025-02-16 13:54:09 +08:00
bfdyanshe
253fc6f4e1 fix: Separate EPUB files into dedicated book file extension category 2025-02-16 13:46:52 +08:00
bC2y5tal
99aa0d3255 feat: Add EPUB file support to document loader 2025-02-16 13:46:52 +08:00
icinggslits
23a2a6b57c improvement(shortcut): Support more keyboard shortcuts 2025-02-16 13:45:03 +08:00
icinggslits
a869857fc1 add usableEndKeys 2025-02-16 13:45:03 +08:00
kangfenmao
4ecedcb267 feat: Enhance topic handling and message prompt generation 2025-02-16 13:41:31 +08:00
kangfenmao
cbd6a30e14 feat: Improve knowledge base threshold tooltip and input 2025-02-16 12:20:08 +08:00
kangfenmao
5f2cddee09 chore: Update store migration for Coze minapp 2025-02-16 12:14:20 +08:00
Chen Tao
c0e0e924f7 feat: 添加知识库匹配度阈值 (#1634)
* feat: 添加知识库匹配度阈值

* fix: 增加问答时知识库阈值

* feat: 当知识库未检索到数据时使用通用对话逻辑

* fix: add toast
2025-02-16 11:38:00 +08:00
Avan
b6ad7eeb9a style: add bot.n.cn logo 2025-02-16 11:36:58 +08:00
Avan
9cf74317a6 feat: add bot.n.cn 2025-02-16 11:36:58 +08:00
George·Dong
82fcc2292e feat: Add Coze minapp 2025-02-16 10:38:28 +08:00
yangtb2024
4eb0c25682 fix: 窗口较小时,工具显示适配问题 2025-02-16 10:35:45 +08:00
kassadin
9e128d2524 fix: unregister global shortcuts 2025-02-16 10:34:24 +08:00
jyeric
1473cb3123 Fix: Font size and Latex problem, resolve CherryHQ#1034 CherryHQ#1596 (#1723) 2025-02-15 22:55:43 +08:00
Wenwei Lin
2c5fe01fbf fix: add ellipsis in knowledge base item (#1718) 2025-02-15 22:51:07 +08:00
Wenwei Lin
d574a09529 fix: support html file in knowledge base (#1703) 2025-02-15 22:50:05 +08:00
美兰十三
f20bccfd7d feature: add topic prompt (#1696)
* feat: 新增话题补充提示词

* feat: 新增话题补充提示词

* feat: 新增话题补充提示词

* feat: 新增话题补充提示词

* feat: 新增话题补充提示词
2025-02-15 08:21:59 +08:00
icinggslits
5dcc892f31 调整show_app快捷键功能的交互逻辑 2025-02-15 08:17:18 +08:00
kangfenmao
26e3871688 Revert "fix: 网页链接附带中文标点解析错误"
This reverts commit 16feb49e9e.
2025-02-15 01:30:13 +08:00
kangfenmao
9a6aad35b0 fix: Improve handling of 'undefined' values in JSON parsing 2025-02-15 01:25:59 +08:00
eeee0717
16feb49e9e fix: 网页链接附带中文标点解析错误 2025-02-15 01:06:32 +08:00
kangfenmao
30959e2380 feat: Add LM Studio and ModelScope as system LLM providers
- Update llm.ts to include LM Studio and ModelScope in initial system providers
- Modify migrate.ts to add migration logic for adding these new providers
- Ensure providers are added only if they don't already exist in the configuration
2025-02-15 01:03:09 +08:00
kangfenmao
2c17f75f4f fix: Correct migration version configuration 2025-02-15 00:55:07 +08:00
Yihong Wang
2d1a930bfe feat: Add NotebookLM to MinApps solve #1679 2025-02-15 00:52:47 +08:00
eeee0717
320d27059f fix: 分组和非分组逻辑修改 2025-02-15 00:33:39 +08:00
eeee0717
31014aa8a6 fix: Switching model does not work 2025-02-15 00:33:39 +08:00
ousugo
b468ecfce7 feat: Improve textarea cursor positioning on focus 2025-02-15 00:31:36 +08:00
ousugo
c53d63f7af fix: Improve the @ model list experience
- 修复使用方向键上下移动时,列表不随之滚动的问题
- 添加滚动条
2025-02-15 00:29:32 +08:00
ousugo
dabff0a847 feat: Add platform and version fields to all issue templates 2025-02-15 00:26:31 +08:00
Konjac-XZ
26a5ae0086 fix: Translation error when passing empty user messages to certain models.(Refined) 2025-02-15 00:24:17 +08:00
kangfenmao
88e0d293a2 chore(version): 0.9.24 2025-02-14 15:04:59 +08:00
kangfenmao
0c97b52c53 refactor: Improve provider removal logic in LLM store 2025-02-14 14:49:34 +08:00
ousugo
2449a22c69 perf: Add new Infini AI models to system models list 2025-02-14 14:37:57 +08:00
ousugo
028f9d88d9 feat: Add reasoning model filter in EditModelsPopup 2025-02-14 14:30:48 +08:00
kangfenmao
a07c6cdffb refactor: Improve provider settings and menu handling 2025-02-14 13:35:58 +08:00
kangfenmao
5a647b0d61 style: Adjust group menu bar styling 2025-02-14 13:18:16 +08:00
kangfenmao
007e6419ba feat: Add ModelScope provider to LLM providers list 2025-02-14 13:13:32 +08:00
Col0ring
caa473639c feat: add modelscope provider (#1563)
Co-authored-by: 亢奋猫 <kangfenmao@qq.com>
2025-02-14 13:12:46 +08:00
kangfenmao
b6825a6ea2 feat(notion): Add divider to Notion settings page 2025-02-14 13:08:47 +08:00
Trey Dong
710180997f feat(notion): 添加 Notion连接检查功能 (#1620)
- 在 Notion 配置页面添加"检查"按钮
- 实现 Notion 连接检查逻辑
- 添加相关国际化文本
2025-02-14 10:52:16 +08:00
hehua2008
fd4334f331 feat: Add LM Studio support (#1572)
Co-authored-by: hehua2008 <hegan2010@gmail.com>
Co-authored-by: 亢奋猫 <kangfenmao@qq.com>
2025-02-14 10:49:57 +08:00
FischLu
80dedc149a feat: Implement circular selection in model selector 2025-02-14 10:40:03 +08:00
duanyongcheng77
8eacaa281a chore: add ignore for .cursoerules 2025-02-14 10:38:51 +08:00
duanyongcheng77
6e75140939 chore: 🤖 add aider gitignore 2025-02-14 10:38:51 +08:00
Asurada
5a3a97135f feat: Add XiaoYi miniapp, resolve #1591 (#1595)
Co-authored-by: 亢奋猫 <kangfenmao@qq.com>
2025-02-14 10:37:42 +08:00
ousugo
44d42d64ef fix: Solve the problem that eslint always reports line break errors on Windows 2025-02-14 10:36:07 +08:00
ousugo
fad3f67678 feat: Improve model search by adding name-based filtering, resolve #1520
搜索模型时,同时搜索模型的名字和 ID
2025-02-14 10:32:34 +08:00
kangfenmao
65b30b3b0d chore: Update Vite config and remove deprecated migration code
- Exclude additional chunk in Electron Vite configuration
- Remove outdated migration logic for providers and MinApps
2025-02-14 10:31:24 +08:00
首都爱护动物协会
0278228a84 add providers
新增服务商:
1.无问芯穹
2Perplexity
3.DMXAPI

补充部分embedding模型信息
2025-02-14 10:28:52 +08:00
shniubobo
bb0cb1cecc fix: Regression on reasoning time
PR #1253 fixed reasoning time calculation for APIs that return reasoning
content in `delta.content`, but introduced a regression for those
returning it in `delta.reasoning_content`. This commit fixes the
regression.

Fixes #1593
2025-02-14 10:26:54 +08:00
shniubobo
f5cd6ecb50 fix: Remove trailing newline in codeblocks 2025-02-14 10:10:30 +08:00
Xin Rui
76c0ad9985 fix: translation error when passing empty user messages to certain models.. (#1612) 2025-02-14 10:09:47 +08:00
kangfenmao
4c771c6913 chore(version): 0.9.23 2025-02-13 17:49:06 +08:00
kangfenmao
15d50761e7 feat: Enhance file processing and loader configuration
- Remove image loader from dependencies
- Update file loading to skip image, video, and audio files
- Add logging for knowledge base file processing
- Modify common file extensions list
- Add type ignore for LocalPathLoader to resolve TypeScript issues
2025-02-13 17:49:06 +08:00
kangfenmao
1c33c90884 feat: Add file type not supported error message in multiple languages 2025-02-13 16:41:48 +08:00
kangfenmao
53e5a3bf76 fix: Reset MinApp onClose handler when closing the app 2025-02-13 16:10:46 +08:00
kangfenmao
adfea253d7 chore(version): 0.9.22 2025-02-13 14:02:45 +08:00
kangfenmao
6c17fee69e refactor: Remove unused onMaxTokensChange function 2025-02-13 14:02:36 +08:00
kangfenmao
40958ffb2c feat: Add useSidebarIconShow hook to conditionally render sidebar icons 2025-02-13 13:38:46 +08:00
kangfenmao
22d2121dcc feat: Add documentation link to sidebar with multilingual support 2025-02-13 13:27:25 +08:00
kangfenmao
4632f1a92a feat: Improve token limit settings and knowledge base URL handling
- Update OpenAI package to version 4.77.3
- Add confirmation modal for max tokens setting
- Modify max tokens input to use InputNumber instead of Slider
- Update localization strings for max tokens and URL input
- Enhance knowledge base URL input to support multiple URLs
- Improve error handling and input validation
2025-02-13 13:12:51 +08:00
kangfenmao
b9affe3eb8 style: code format 2025-02-13 11:40:55 +08:00
Chen Tao
c6f136caa2 feat: knowledge base support more file types (#1300)
* feat: knowledge base support more file types

* chore: add common document types

* feat: finish basic odloader

* feat: finish open document loader

* feat: support more type from dictionary

* fix: 删除目录时仍能检索到信息

* chore
2025-02-13 11:36:33 +08:00
Wenwei Lin
827959e580 fix: Label text was obscured in create agent page 2025-02-13 11:36:07 +08:00
Chen Tao
8739c49634 feat: add i18n sync script (#1538)
* feat: add i18n sync script

* chore
2025-02-13 11:34:23 +08:00
Asurada
e99f253d48 feat: add google AI Studio minapp, resolve #832 (#1518)
* feat: add google AI Studio minapp, resolve #832

* fix: correct variable name for aistudio in migrateConfig
2025-02-13 11:28:51 +08:00
牡丹凤凰
0cb82bd3bd Update README.zh.md (#1516) 2025-02-12 23:10:50 +08:00
牡丹凤凰
cf89981b64 Update README.ja.md (#1515) 2025-02-12 23:10:10 +08:00
牡丹凤凰
e4133b0054 Update README.zh.md (#1514) 2025-02-12 23:08:59 +08:00
牡丹凤凰
9249ee9094 Update README.md (#1513) 2025-02-12 23:07:46 +08:00
kangfenmao
0a4b360745 feat: update AIHubMix OAuth and release workflow configuration 2025-02-12 18:24:57 +08:00
kangfenmao
59c4d60d6a fix(i18n): use translation for API key error message 2025-02-12 18:24:39 +08:00
kangfenmao
dcdb00eee7 feat: remove auto reset model feature #1435
close #1435
2025-02-12 16:58:33 +08:00
kangfenmao
946129ceb3 feat: expand embedding model regex to include more model identifiers #1481
close  #1481
2025-02-12 16:39:35 +08:00
kangfenmao
ada45b229d feat: switch default math rendering engine to KaTeX #1485 2025-02-12 16:34:24 +08:00
kangfenmao
7934ce473d fix(i18n): model exist error tip 2025-02-12 16:11:01 +08:00
kangfenmao
f749bef2fd fix: openai o1 models not support max_token params
close #1378
close #1378
close #1340
close #1197
close #491
2025-02-12 15:59:42 +08:00
kangfenmao
6b2452422e fix: doubao not support files type 2025-02-12 15:41:24 +08:00
kangfenmao
50438dd612 feat: WebDAV data backup and restore secondary confirmation #1326
close #1326
2025-02-12 15:19:21 +08:00
kangfenmao
38f665e484 fix: update Ollama API endpoint URL 2025-02-12 14:56:59 +08:00
kangfenmao
635e125ef4 chore: update store migration and LLM providers 2025-02-12 14:52:45 +08:00
kangfenmao
1b3ae92854 docs: change knowledge base documents chunk max limit 2025-02-12 14:36:56 +08:00
cnJasonZ
ce66f2e2ea feat: add new model provider PPIO (#1359) 2025-02-12 14:14:56 +08:00
方程
726efe3558 feat: add gitee ai provider (#1473)
* feat: add gitee ai provider

* feat: add gitee ai provider

---------

Co-authored-by: 方程 <fangcheng@oschina.cn>
2025-02-12 14:11:13 +08:00
ousugo
24deb56d00 feat(ISSUE_TEMPLATE): 添加Issue提交检查清单 2025-02-12 14:09:36 +08:00
亢奋猫
5e8d7682f5 feat: 🎸 使用@呼出模型选择列表 #1317 #1324 (#1458)
* feat: 🎸 使用@呼出模型选择列表

输入第一个字符为@符号的时候可以呼出选择模型的列表

* feat: 🎸 Only one can be chosen at a time

一次只能选择一个模型,选择后自动关闭。选择过的模型不在出现在列表,避免删除模型的时候显示异常。

* fix: 🐛 When choosing the model, Enter will send a message

* feat: 🎸 选中的模型显示供应商

* feat: 🎸 pinned module show privoder

* feat: 🎸 only selected modle show provider

* feat: 🎸 删除@符号以后自动关闭

* feat: 🎸 增加模糊搜索

---------

Co-authored-by: duanyongcheng77 <duanyongcheng77@gmail.com>
2025-02-12 14:08:18 +08:00
牡丹凤凰
ceb97e80ff Update embedings.ts 2025-02-12 08:31:00 +08:00
牡丹凤凰
bf1fa5b767 Update zh-tw.json
更正繁体中文当中关于Temperature的描述
2025-02-12 08:30:34 +08:00
airwish
e99f34893e feat: expanded web search models 2025-02-12 08:30:00 +08:00
牡丹凤凰
a49dd6101e Update zh-tw.json (#1433)
助手菜单名称显示不全
2025-02-12 08:25:14 +08:00
kangfenmao
d03b852671 feat: Add image loader to embedjs configuration 2025-02-11 22:55:49 +08:00
kangfenmao
13e48411c1 chore: Update embedjs dependencies to version 0.1.28 2025-02-11 22:36:34 +08:00
linauror
de1976d984 fix:针对思考类模型的返回,总结标题仅截取</think>之后的内容 (#1415) 2025-02-11 18:05:09 +08:00
Trey Dong
220046cc95 feat: add pin topic feature (#1408)
* feat: 新增导出至Notion的选项

* fix:添加多语言支持

* fix:添加提示语的多语言支持,以及防止重复导入的状态

* fix:修复多语言错误及调整UI样式统一

* feat:添加话题固定功能
2025-02-11 16:51:58 +08:00
kangfenmao
bae76f921b fix: upload button tooltip 2025-02-11 16:34:26 +08:00
lucifer9
cb88a48d8b feat: add support for clickable citations in message content (#1381)
* Add support for clickable citations in message content

* update format
2025-02-11 16:21:20 +08:00
kshern
4d13a8d9c2 fix: retain selected model when regenerating message (#1382)
* fix: retain selected model when regenerating message instead of reverting to default model

* fix: use props model

---------

Co-authored-by: Shern <shenkeji@corp.netease.com>
2025-02-11 14:12:56 +08:00
Trey Dong
50cc1c6b5a feat: 增加导出话题至Notion的功能 (#1331)
* feat: 新增导出至Notion的选项

* fix:添加多语言支持

* fix:添加提示语的多语言支持,以及防止重复导入的状态

* fix:修复多语言错误及调整UI样式统一
2025-02-11 11:27:01 +08:00
kangfenmao
1d82552491 Revert "feat: enable local network access on macOS #1178"
This reverts commit 6fb79c17d2.
2025-02-11 11:18:33 +08:00
Chen Tao
5c2129c0c8 fix: 数据库文档个数变为默认6个#1366 #1370 2025-02-11 10:05:34 +08:00
美兰十三
0eead315d8 fix: 快捷键无法绑定空格 2025-02-11 09:25:42 +08:00
Peter Chen
04cfe5019e Update MessageThought.tsx 2025-02-10 16:48:47 +08:00
kangfenmao
a0cfe7df4a fix: store migrate version 2025-02-10 14:40:30 +08:00
Chen Tao
3d8748a61a feat: add translate selection (#1010)
* feat: add translate selection

* chore: add default translate value

* feat: optimize trigger translation shotcut and add TanslateLanguageVarious

* fix

* fix: add database migrate version
2025-02-10 13:19:46 +08:00
郑恩
f3940159b3 feat: 优化思考过程显示的换行效果 2025-02-10 13:17:32 +08:00
Fadouse
81d4accacf fix: correct thinking time calculation for deepseek r1 2025-02-09 21:29:21 +08:00
Wenwei Lin
c900a186b7 feat: 知识库文件支持 html 类型 2025-02-09 21:27:15 +08:00
kangfenmao
72d61ef853 chore(version): 0.9.21 2025-02-09 10:20:03 +08:00
kangfenmao
8c32f51892 fix: Remove local network access switch and add insecure content loading 2025-02-09 10:14:35 +08:00
kangfenmao
0a3ad04f12 fix: max chunk size 2025-02-09 09:32:40 +08:00
牡丹凤凰
555a991a30 Update embedings.ts
embedding models information
2025-02-09 09:31:01 +08:00
kangfenmao
6ba6357d21 chore(version): 0.9.20 2025-02-09 00:05:15 +08:00
kangfenmao
cb3db57d2f feat: Add support for SiliconFlow provider charge and knowledge base improvements 2025-02-08 23:53:28 +08:00
kangfenmao
e1a04030b5 feat: Add HTTP error handling and localized error messages 2025-02-08 23:01:27 +08:00
kangfenmao
220d11a414 feat: Make knowledge base reference document count configurable 2025-02-08 20:56:59 +08:00
kangfenmao
3d2e209550 feat: add knowledge base settings 2025-02-08 19:19:59 +08:00
kangfenmao
ee46d2055a feat: Add support for AIHubMix topup and charge functionality 2025-02-08 16:26:10 +08:00
magicdmer
3049023266 fix: 翻译的temperature改为0.7
improve: 模型特性默认支持的时候,打勾
2025-02-08 14:46:55 +08:00
kangfenmao
6fb79c17d2 feat: enable local network access on macOS #1178 2025-02-08 10:58:12 +08:00
teaim
9efc196ec5 fix: o3-mini markdown formatting #997 2025-02-08 07:33:27 +08:00
kangfenmao
186a1612e8 feat: remove knowledge base references when deleting a base 2025-02-07 23:58:44 +08:00
kangfenmao
22920204d1 fix: update knowledge base button tooltip and input bar dependency 2025-02-07 23:38:25 +08:00
Chen Tao
d4efbbb1bf feat: add knowledge base settings to assistant (#1112)
* feat: #1047

* fix: allow clear
2025-02-07 23:20:22 +08:00
kangfenmao
3f7e84e17c fix: sanitize error details by removing sensitive headers and stack trace 2025-02-07 23:05:27 +08:00
kangfenmao
6df0b02e49 fix: improve reasoning effort handling for different providers 2025-02-07 22:37:18 +08:00
kangfenmao
280ec3377b feat: add aihubmix oauth 2025-02-07 22:27:28 +08:00
kangfenmao
6a30eec5b4 chore: update @langchain/openai package dependency 2025-02-07 21:20:14 +08:00
Chris Wan
a6497b8c98 fix: temperature tip English translation 2025-02-07 18:34:27 +08:00
Yrom Wang
acda36ae3f fix: KnowledgeSearchPopup keyword highlighting issue 2025-02-07 18:24:03 +08:00
kangfenmao
496b4684ea feat: expand supported Gemini models for web search #1096 2025-02-07 17:18:11 +08:00
kangfenmao
2898215a00 feat: add baidu cloud provider 2025-02-07 16:47:29 +08:00
kangfenmao
762c3d4950 feat: Reposition ocoolAI provider in LLM store configuration 2025-02-07 13:29:07 +08:00
kangfenmao
cec5eb3989 fix: remove QwenLM provider #1122
close #1122
close #886
2025-02-07 12:38:12 +08:00
牡丹凤凰
61ebe97a5a Update README.ja.md 2025-02-07 06:50:07 +08:00
牡丹凤凰
609a80bf2d Update README.zh.md 2025-02-07 06:49:17 +08:00
牡丹凤凰
468423040f Update README.md
readme增加相关项目one-api
2025-02-07 06:47:55 +08:00
gyuannn
85efc6e96b feat: Improve handling of think tags 2025-02-06 13:43:06 +08:00
gyuannn
9f19493b41 feat: optimize MessageThought to enhance user experience 2025-02-06 13:43:06 +08:00
牡丹凤凰
3e43887be8 Update README.ja.md 2025-02-06 01:50:54 +08:00
牡丹凤凰
bffc9d23aa Update README.zh.md 2025-02-06 01:50:25 +08:00
牡丹凤凰
402a3e5ef9 Update README.md 2025-02-06 01:49:54 +08:00
kangfenmao
ce2c8c7495 chore(version): 0.9.19 2025-02-05 21:34:55 +08:00
kangfenmao
dc6d79366e feat: Add DeepSeek V3 model to Silicon provider 2025-02-05 21:34:55 +08:00
kangfenmao
777455f167 feat: Enhance web search parameter handling for assistants #575 2025-02-05 20:35:25 +08:00
kangfenmao
65cc51ea94 feat: Update VisionIcon from PictureOutlined to EyeOutlined 2025-02-05 20:21:26 +08:00
kangfenmao
95936dca2a feat: Add DeepSeek VL to vision allowed models #1015 2025-02-05 20:09:29 +08:00
kangfenmao
9aa829e6fc feat: Enhance model tags and icons with new reasoning and visual styles 2025-02-05 20:00:18 +08:00
shellming
a27150c154 feat: Add 3MinTop to default mini apps 2025-02-05 16:16:37 +08:00
Gutsy Yuan
9acae0a728 feat: Add new model type for reasoning models & reasoning_effort setting (#992) 2025-02-05 16:15:31 +08:00
kangfenmao
9024d48938 feat: add support for o1 models and update provider configurations 2025-02-05 16:14:35 +08:00
kangfenmao
02080954bc chore(version): 0.9.18 2025-02-04 22:37:31 +08:00
kangfenmao
abb922a2b1 feat: improve error message display for embedding dimensions retrieval 2025-02-04 17:25:24 +08:00
kangfenmao
a1b88758cc feat: improve error handling and formatting across providers 2025-02-04 16:45:09 +08:00
kangfenmao
3d16c735d9 fix: oauth error 2025-02-04 16:45:03 +08:00
Lei Huang
e74391562b fix: prompt-injection-like behavior in translation #956 (#974)
* fix: prompt-injection-like behavior in translation #956

* fix: edit translation prompt to embded text to system prompt
2025-02-04 15:49:17 +08:00
亢奋猫
53f46218d3 feat: add oauth for siliconflow (#976)
* wip: silicon oauth

* feat: Add custom protocol handler for SiliconFlow OAuth login

* feat: Improve SiliconFlow OAuth flow with dynamic key update

* feat: Enhance OAuth and Provider Settings UI

* feat: Refactor SiliconFlow OAuth and update localization strings

* chore: Update provider localization and system provider configuration

* feat: Add OAuth support for AIHubMix provider
2025-02-04 15:41:40 +08:00
kangfenmao
333547df3d feat: support for vba code mode #949
close #949
2025-02-04 13:22:41 +08:00
kangfenmao
4c877fb0a3 feat: add mention model title to locales 2025-02-04 13:00:31 +08:00
kangfenmao
bfa61ae3ee feat: reduce embedding batch size to 5 2025-02-04 12:35:42 +08:00
kangfenmao
2208ab7277 feat: update text-to-image model regex to include Janus 2025-02-04 12:28:17 +08:00
kangfenmao
507efda688 feat: update text-to-image models with new FLUX and Stable Diffusion variants 2025-02-04 12:21:58 +08:00
kangfenmao
0914df8908 feat: add Janus model to vision allowed models 2025-02-04 12:11:14 +08:00
kangfenmao
205aa70825 feat: adjust translation assistant temperature 2025-02-04 12:06:06 +08:00
eeee0717
6c51e1d756 feat: add Janus-Pro-7B for silicon 2025-02-04 00:13:41 +08:00
Kcalb35
8e58dab337 fix: translation issue #900 2025-02-03 17:26:31 +08:00
rebecca554owen
e171715490 GitHub Actions 工作流优化
升级 actions/checkout 从 v3 到 v4。
新增 Get release tag 步骤,支持手动触发和自动触发时动态获取发布标签:
手动触发时,使用 workflow_dispatch 输入的 tag 参数。
自动触发时,从 GITHUB_REF 中提取标签(如 v1.0.0)。
统一使用 secrets.GITHUB_TOKEN 替代 secrets.GH_TOKEN,遵循 GitHub 官方推荐的最佳实践。
2025-02-03 15:07:16 +08:00
Shenghang Tsai
bda00e0a90 default use r1 in silicon 2025-02-03 15:06:50 +08:00
kangfenmao
b56d00a7e2 fix: reset knowledge base selection when switching assistants 2025-02-03 13:45:21 +08:00
kangfenmao
e520db6949 feat: add azure provider type #931 2025-02-03 13:37:09 +08:00
kangfenmao
de141c8127 feat: change DEFAULT_TEMPERATURE to 1.0 2025-02-03 11:57:26 +08:00
kangfenmao
3f3259784b feat: openrouter reasoning field and include_reasoning key #863 2025-02-03 11:48:46 +08:00
kangfenmao
66c2c530c5 feat: add regenerate message button 2025-02-03 11:18:55 +08:00
kangfenmao
d12fc29515 fix: add model input max length 2025-02-03 10:52:21 +08:00
kangfenmao
44991edfbd feat: add knowledge base settings popup 2025-02-03 09:22:59 +08:00
Wenwei Lin
d33714ad68 fix: OpenAI o3-mini 无法使用 2025-02-02 00:17:00 +08:00
牡丹凤凰
e50223d219 Update models.ts
Match the avatar of the o3-mini
2025-02-01 04:38:25 +08:00
牡丹凤凰
4128b075e1 Update README.ja.md 2025-01-31 22:33:52 +08:00
牡丹凤凰
a1f0c039b1 Update README.zh.md 2025-01-31 22:33:28 +08:00
牡丹凤凰
76abe08d1b Update README.md 2025-01-31 22:32:27 +08:00
牡丹凤凰
1355dfe744 Update README.ja.md 2025-01-29 17:29:19 +08:00
牡丹凤凰
791d29cc41 Update README.zh.md 2025-01-29 17:28:47 +08:00
牡丹凤凰
88e76da466 Update README.md 2025-01-29 17:27:30 +08:00
gyuannn
250aa7154a fix: 修复 mini-app 中无法使用 context-menu 的问题 2025-01-29 10:33:44 +08:00
Nanami
790caae2ab feat: Support configurable chunk size and overlap for knowledge base 2025-01-27 12:30:22 +08:00
Nanami
7f7300e6dc feat: Support configurable chunk size and overlap for knowledge base 2025-01-27 12:30:22 +08:00
kangfenmao
4464992873 docs: update Japanese and Chinese README files to include QQ group link 2025-01-24 18:16:51 +08:00
kangfenmao
37d1c250d2 docs: update README files to include Discord link for community support
- Added a Discord link to the English, Japanese, and Chinese README files, encouraging users to join discussions and seek help alongside the existing Telegram group invitation.
- This change enhances community engagement options for Cherry Studio users.
2025-01-24 18:07:29 +08:00
kangfenmao
e9c51579a2 chore(version): 0.9.17 2025-01-24 13:54:04 +08:00
kangfenmao
aec2952780 feat: add delete group message confirm modal 2025-01-24 13:13:00 +08:00
kangfenmao
95a1bdac72 fix: resend message logic 2025-01-24 13:02:57 +08:00
kangfenmao
306cb04ef0 fix: siliconflow image url with query params #844
close #844
2025-01-24 09:31:31 +08:00
kangfenmao
dc9444a9d4 feat(constants): add C# file extension to textExts array #835
- Updated the textExts array in constant.ts to include '.cs' for C# files, enhancing the file type recognition capabilities.
2025-01-23 13:22:17 +08:00
kangfenmao
ad9fefe902 chore(migration): update version and adjust provider type for QwenLM #833
- Incremented version from 60 to 61 in the persisted reducer configuration.
- Updated migration logic to change the provider type for 'qwenlm' from 'openai' to 'qwenlm', ensuring correct identification in the state management.
2025-01-23 13:20:15 +08:00
kangfenmao
e07d4838a9 docs: update README files to encourage community support
- Added a call-to-action in English, Japanese, and Chinese README files inviting users to star the project or sponsor its development.
- Enhanced visibility of community engagement options to foster support for Cherry Studio.
2025-01-23 11:59:15 +08:00
hxp0618
30d070040c fix: apikey and ApiHost incorrectly set to empty 2025-01-23 08:30:07 +08:00
hobee
f335699958 feat: add new minimax model configuration 2025-01-23 08:29:48 +08:00
kangfenmao
b1bc576e3f chore(version): 0.9.16 2025-01-22 16:32:57 +08:00
kangfenmao
a6f086e3be fix: group message bugs 2025-01-22 16:29:05 +08:00
kangfenmao
084da9ebab feat: enhance message model handling and user display
- Updated Message component to fallback to message.model if model retrieval fails, improving robustness.
- Refactored MessageHeader to utilize getModelName for better user name display based on message role, enhancing clarity.
- Introduced getModelName function in ModelService to streamline model name retrieval, improving code modularity and readability.
2025-01-22 15:08:44 +08:00
kangfenmao
57aef23741 feat: enhance agent management and UI in AddAssistantPopup and AgentsPage
- Updated AddAssistantPopup to improve layout and styling, ensuring better overflow handling and text display.
- Refactored AgentsPage to utilize a new utility function for grouping agents, enhancing data management and organization.
- Exported getAgentsFromSystemAgents function for better modularity and reusability across components.
2025-01-22 14:47:35 +08:00
kangfenmao
900b11bdf7 feat: enhance translation functionality in MessageMenubar
- Updated translateText function to accept an optional callback for handling translated text directly within the function.
- Refactored MessageMenubar to utilize the new callback mechanism, improving the flow of translated content handling.
- Enhanced error handling during translation to ensure better user feedback in case of failures.
2025-01-22 14:37:15 +08:00
kangfenmao
8aec8a60b3 feat: add file reading functionality and integrate system agents
- Introduced FileService to handle file reading operations via IPC.
- Implemented a new IPC handler for reading files, enhancing the application's ability to access and manage data.
- Integrated system agents from a JSON file, allowing dynamic loading of agent data into the application.
- Updated the AgentsPage and AddAssistantPopup components to utilize the new system agents, improving user experience and functionality.
- Enhanced application state management by adding resourcesPath to the runtime state, ensuring proper resource handling across components.
2025-01-22 14:35:38 +08:00
kangfenmao
a566b0e91a refactor: unify message model handling across components
- Replaced direct usage of modelId with model object in Message, MessageHeader, MessageMenubar, and TranslatePage components for consistency.
- Introduced getMessageModelId utility function to streamline model retrieval from messages.
- Updated event handling in Messages component to align with new model structure.
- Enhanced code readability and maintainability by reducing redundancy in model handling.
2025-01-22 13:29:21 +08:00
kangfenmao
4d201059ad feat: conditionally render resend button in MessageMenubar
- Updated MessageMenubar to display the resend button only for user messages, enhancing user experience and preventing unnecessary actions for other roles.
- Refactored the children prop of TextEditPopup to include conditional rendering logic based on message role.
2025-01-22 12:26:40 +08:00
kangfenmao
00d91ecf01 feat: enhance message grouping and styling
- Added new styles for message thought containers and group message wrappers to improve UI layout.
- Updated MessageGroup component to dynamically set the selected message index based on message length.
- Introduced a new event for appending messages, enhancing message handling capabilities.
- Refactored MessageMenubar to support the new append message functionality.
- Adjusted multi-model message style setting to 'fold' for better user experience.
- Improved responsiveness of message grid layout for smaller screens.
2025-01-22 12:04:21 +08:00
kangfenmao
462ac39897 feat: streamline language translation options in MessageMenubar
- Replaced hardcoded language translation options with a dynamic mapping from TranslateLanguageOptions.
- Improved maintainability and scalability of the translation feature by utilizing a centralized configuration for language options.
2025-01-22 10:18:19 +08:00
kangfenmao
3fa1e8c842 feat: add FlagOpen logo to model configuration
- Introduced a new image asset for the FlagOpen model in the assets directory.
- Updated the models configuration to include the FlagOpen logo, allowing for its use in the model logo mapping.
2025-01-22 10:05:50 +08:00
kangfenmao
d32a76c087 refactor: improve message rendering and add reasoning content extraction
- Refactored `getMessageBackground` function for better readability.
- Updated `MessageContent` component to use a new `withMessageThought` utility for extracting reasoning content from messages.
- Changed fragment usage to `Fragment` for consistency in JSX.
- Enhanced message handling by separating reasoning content from the main message content.
2025-01-22 09:50:29 +08:00
duanyongcheng77
9e9fd37bda fix: 🐛 fixed bug #779
助手的预设消息保存逻辑的修改
2025-01-21 22:06:52 +08:00
kangfenmao
dd464db594 feat: add group message action bar 2025-01-21 17:58:34 +08:00
Teo
ccac5358f4 chore(version): update version to 60 and add migration for multiModelMessageStyle setting 2025-01-21 15:16:18 +08:00
Teo
e72e324155 refact: 多模型回答优化 2025-01-21 15:16:18 +08:00
kangfenmao
28c18b6651 fix: regenerate message not rewrite reasoning_content 2025-01-21 15:15:55 +08:00
kangfenmao
3d432d810f chore(version): 0.9.15 2025-01-21 14:28:01 +08:00
kangfenmao
21ad28ee62 feat: add deepseek-reasoner model support 2025-01-21 14:28:01 +08:00
kangfenmao
f7db1289e4 feat(miniwindow): add up and down key switch menu #792 2025-01-21 10:11:42 +08:00
Cololi
f5c547cdb2 feat: add deepseek-reasoner & delete deepseek-coder 2025-01-21 10:05:21 +08:00
ousugo
9160cee919 feat: add WebDAV backup hour options and optimize english hour translations 2025-01-21 08:38:08 +08:00
kangfenmao
298bb8be29 feat: update minapp url to 'https://grok.com' #791
close #791
2025-01-20 16:53:33 +08:00
kangfenmao
b800c64fed docs: update readme.md 2025-01-20 15:32:01 +08:00
kangfenmao
504d7b88d4 chore(version): 0.9.14 2025-01-20 13:56:52 +08:00
kangfenmao
713d6dba8f fix: added warning for manual download on failed auto updates, simplified window lifecycle 2025-01-20 13:56:25 +08:00
kangfenmao
a6833d5994 chore(version): 0.9.13 2025-01-20 13:11:26 +08:00
kangfenmao
d850fd315a feat: add onclick event to login icon in footer component 2025-01-20 12:57:26 +08:00
kangfenmao
c04fd62bec feat: extended safety threshold check to include 'thinking-exp' model ids 2025-01-20 12:55:24 +08:00
kangfenmao
f86a274cd3 feat: update contact email address 2025-01-20 12:20:46 +08:00
kangfenmao
798a6e8c3e chore(version): 0.9.12 2025-01-20 11:52:26 +08:00
kangfenmao
749353f460 feat: added copy last message feature and translations 2025-01-20 11:09:57 +08:00
kangfenmao
c510f5dcce feat: added utility function, sorting, and new shortcut 2025-01-20 10:29:44 +08:00
kangfenmao
46b314303c feat: enable pinned functionality for minapps and update 'flowith' configuration 2025-01-20 09:58:47 +08:00
kangfenmao
b01aca9066 fix: prevent unnecessary route changes and trim input field on change 2025-01-20 09:52:58 +08:00
ousugo
725f81c165 fix: conditionally render pin button based on app ID 2025-01-20 09:32:13 +08:00
ousugo
c0e25879e5 feat: add Flowith minapp, resolve #780 2025-01-20 09:31:34 +08:00
MrChen
4c22c404ca feat: add the shortcuts for 'clear' and 'new context' and fix (#786)
* Fix: ESC key to exit the expanded editor

* Add the shortcuts for 'clear' and 'new context' to the input bar
Clear Messages: Ctrl+L
New Context: Ctrl+R
https://github.com/CherryHQ/cherry-studio/issues/740
https://github.com/CherryHQ/cherry-studio/issues/766

* Fix: the paste issue when copying from an email (content was pasted as an image; ensure it is pasted as text). Prioritize the text in the clipboard during pasting.
2025-01-20 09:31:09 +08:00
kangfenmao
63673ec39f chore(version): 0.9.11 2025-01-19 20:50:33 +08:00
kangfenmao
88cc783a95 fix: quick assistant bugs 2025-01-19 20:03:45 +08:00
kangfenmao
9c55b4516c feat: add a startup switch for quick assistant 2025-01-19 19:22:25 +08:00
kangfenmao
aecc5fefcf feat: translate support stream output 2025-01-19 16:56:35 +08:00
kangfenmao
afc2e2f595 feat: auto-scroll to selected menu item on model open 2025-01-19 15:47:19 +08:00
kangfenmao
67b63ee07a refactor: add qwenlm provider 2025-01-19 15:39:48 +08:00
kangfenmao
fd7132cd3a fix: store minapp url use base64 data image 2025-01-19 15:35:17 +08:00
kangfenmao
a7d9700f06 feat: add mini window 2025-01-19 13:59:32 +08:00
ousugo
d9bb552f3f feat: add pinning functionality for MinApp component 2025-01-19 13:59:06 +08:00
ousugo
ad2713c0be fix: fix wrong NVIDIA official website link, fix #771 2025-01-19 13:59:06 +08:00
牡丹凤凰
1e756614f9 Delete .github/workflows/update-lmarena.yml 2025-01-19 13:59:06 +08:00
牡丹凤凰
d457dfa3d3 Update update-lmarena.yml 2025-01-19 13:59:06 +08:00
牡丹凤凰
b24d88dfe3 Create update-lmarena.yml 2025-01-19 13:59:06 +08:00
kangfenmao
b6d598c52e fix: remove default message for webdav backup initiation 2025-01-19 13:59:06 +08:00
kangfenmao
67e1dd56e9 style: increased padding at the bottom of the sidebar component 2025-01-19 13:59:06 +08:00
kangfenmao
8b5dd427d0 fix: WebDAV not automatic backup on app reopened #752 2025-01-19 13:59:06 +08:00
kangfenmao
4f44afeec4 feat: auto focs input textarea #759
close #759
2025-01-19 13:59:06 +08:00
kangfenmao
c46219cd6c feat: improved 'my agents' list rendering 2025-01-19 13:59:06 +08:00
magicdmer
999bd802c4 perf: 优化智能体页面性能和体验 (#756)
* feat: improved model validation and error handling

* refactor: 优化智能体页面下拉流畅度和分类切换效果,让其更加顺畅自然

---------

Co-authored-by: kangfenmao <kangfenmao@qq.com>
Co-authored-by: magicdmer <magicdmer@163.com>
2025-01-19 13:59:06 +08:00
kangfenmao
2300cca070 refactor: improved code organization and reusability 2025-01-19 13:59:06 +08:00
kangfenmao
b4de6292c3 feat: improved model safety settings for geminiprovider class 2025-01-19 13:59:06 +08:00
magicdmer
42908e8834 refactor: (GeminiProvider) optimize safety settings handling
- Extract safety threshold logic into getModelSafetySetting method
- gemini-exp-* models not support 'OFF', must use 'BLOCK_NONE'
2025-01-19 13:59:06 +08:00
kangfenmao
57718dda6f feat: update harmblockthreshold for harm_category_civic_integrity 2025-01-19 13:59:06 +08:00
kangfenmao
c87e88a53a feat: add civic integrity category to harm block settings in GeminiProvider 2025-01-19 13:59:06 +08:00
kangfenmao
5b00c21f15 feat: update safety settings for specific categories #696
Gemini安全设置是否没有完全关闭
2025-01-19 13:59:06 +08:00
kangfenmao
6276890e5b feat: replaced visionicon with modeltags 2025-01-19 13:59:06 +08:00
kangfenmao
a7337ed4b0 feat: add 思维链(CoT) agent 2025-01-19 13:59:06 +08:00
kangfenmao
fe0f6318c9 fix: improved openai provider model id validation logic 2025-01-19 13:59:06 +08:00
magicdmer
75742323ea fix: 修正o1模型无法使用的问题 2025-01-19 13:59:06 +08:00
kangfenmao
f7f8c6f0c6 fix: remove specific unicode characters from removespecialcharacters function 2025-01-19 13:59:06 +08:00
Linjun
e4f4c6cd86 fix issue#762: upon clicking to resend, the conversation content is cleared.
If there is no subsequent message or if the next message is from the user, this message should be resent. delete the old message after processing is complete.
2025-01-19 12:26:55 +08:00
kangfenmao
8eac836e05 feat: improved model validation and error handling 2025-01-16 10:14:32 +08:00
Nanami
a6795289da fix: qwenlm context error 2025-01-15 09:09:01 +08:00
kangfenmao
eff639ddf9 chore(version): 0.9.10 2025-01-15 08:57:05 +08:00
kangfenmao
a046cf32ba fix: artifacts cannot preview 2025-01-14 23:27:54 +08:00
kangfenmao
66bc9cb3f9 refactor: improved type safety and consistency for file handling 2025-01-14 21:02:55 +08:00
kangfenmao
247d1a1846 chore(version): 0.9.9 2025-01-14 20:57:16 +08:00
kangfenmao
0e7fb2b19c refactor: update model group names and sync interval 2025-01-14 20:53:52 +08:00
kangfenmao
8a94bb05ea fix: fix model type logic based on provider properties 2025-01-14 20:32:04 +08:00
Nanami
bc454d4dec feat: add support for qwenlm and image upload (#726)
* feat: add support for qwenlm and image upload

* fix: qwenlm return

* feat: add provider config
2025-01-14 18:59:19 +08:00
Teo
d388aeecfb feat: 添加模型提及功能,支持多个模型一起回答 2025-01-14 17:46:55 +08:00
kangfenmao
3e33ee6cc5 feat: add release workflow behavior control option 2025-01-14 14:55:32 +08:00
kangfenmao
1991df18d2 chore(version): 0.9.8 2025-01-14 14:36:05 +08:00
kangfenmao
de3206b052 chore: update store version and migration 2025-01-14 14:34:36 +08:00
kangfenmao
cb3ed42846 style: update markdown link text color 2025-01-14 13:54:10 +08:00
kangfenmao
edbc8560cc chore(version): 0.9.7 2025-01-14 13:24:54 +08:00
kangfenmao
56761d6f69 fix: improved input validation and debouncing for assistant settings updates 2025-01-14 13:18:34 +08:00
kangfenmao
2b4cfe7cb1 feat: add grounding source info to gemini message 2025-01-14 12:32:50 +08:00
kangfenmao
6a5faa6610 feat: auto focus search input box #705
close #705
2025-01-13 18:09:59 +08:00
kangfenmao
84979a975c feat: add native app regions support 2025-01-13 18:06:22 +08:00
kangfenmao
74740d7fcc style: update pinned apps style and refactor config model 2025-01-13 17:56:16 +08:00
kangfenmao
dff04187be feat: add refresh icon to knowledge base items #567
close #567
2025-01-13 17:42:59 +08:00
kangfenmao
a0a13a4015 feat: added openai model configuration and search parameter logic 2025-01-13 16:42:53 +08:00
kangfenmao
2ad6a1f24c feat: check api use selected model 2025-01-13 16:11:09 +08:00
kangfenmao
cf7c0fc1fc fix: enforce max tokens above 0 in assistantservice #530 2025-01-13 15:03:37 +08:00
kangfenmao
4ecbf3edab feat: csv download #710
close #710
2025-01-13 14:44:30 +08:00
kangfenmao
83cc4ccec7 refactor: update terminology to 'backup' throughout the application 2025-01-13 14:00:35 +08:00
kangfenmao
3998ad08de feat: add qwenlm minapp 2025-01-13 13:52:45 +08:00
kangfenmao
49a5bc7900 refactor: sidebar minapps 2025-01-13 13:04:01 +08:00
hxp0618
7633d70435 feat: MinApp added to the sidebar does not support direct hiding. 2025-01-13 10:13:47 +08:00
hxp0618
ad9fb9aa6d feat: Adjust the order of settings 2025-01-13 10:13:34 +08:00
hxp0618
fc3d15fae8 feat: minApp supports show/hide, add to the sidebar 2025-01-13 10:13:34 +08:00
王瑞
c45fc2bbad feat: add Grok app logo and configuration 2025-01-12 22:33:46 +08:00
kangfenmao
270216f461 chore(version): 0.9.6 2025-01-09 16:23:29 +08:00
kangfenmao
112e90c15c fix: create agent popup error 2025-01-09 09:15:16 +08:00
kangfenmao
c579eff86e chore(version): 0.9.5 2025-01-08 16:52:03 +08:00
kangfenmao
f9f5befc59 fix: window navbar layout 2025-01-08 14:35:48 +08:00
kangfenmao
7271a86677 style: update container component styling and navbar responsiveness 2025-01-08 13:25:34 +08:00
kangfenmao
42ede42f62 feat: narrow layout 2025-01-08 12:44:01 +08:00
kangfenmao
ea7a42f736 style: adjusted padding and container gap styles 2025-01-08 11:06:51 +08:00
kangfenmao
d2836826e7 fix: removed unnecessary conditional logic for attachment button #667 2025-01-08 10:56:22 +08:00
kangfenmao
7d61af7170 Revert "fix:修复单行CodeBlock中显示sub"
This reverts commit 09e6756efe.
2025-01-08 10:46:35 +08:00
kangfenmao
3f4fa9b0ec refactor: refactor upload component layout and styling for responsiveness #674
fix: 当插入文件过多的时候,无法看到输入框了。 close #674
2025-01-08 10:21:17 +08:00
kangfenmao
1bdf6c7955 fix: update model filtering logic to exclude empty ids #493
close #493
2025-01-08 10:00:23 +08:00
kangfenmao
5d005cf5a7 chore: standardize artifact names across platforms 2025-01-08 09:42:38 +08:00
kangfenmao
1fbd727a7b fix: @google/generative-ai local compilation issue #682
close #682
2025-01-07 23:18:18 +08:00
亢奋猫
c9813bb1e2 feature: customizable sidebar module #644 (#680)
* feat:对话的时候支持侧边栏拖拽调整宽度

* feat:对话的时候支持侧边栏拖拽调整宽度

* feat: 隐藏app sidebar 用户体验度提升,不支持隐藏对话

* fix:对话勾选知识库 国际化错误

* refactor: split the SidebarIconsManager module out of DisplaySettings

* style: update SidebarIconsManager style

* ci: fix typecheck

* Revert "feat:对话的时候支持侧边栏拖拽调整宽度"

This reverts commit 58072128f0.

* refactor: merge migrate versions

* refactor: simplify sidebarIcons data structure

* chore: move react-beautiful-dnd to dev dependencies

* chore: use @hello-pangea/dnd replace react-beautiful-dnd

* docs: update translation and formatting of input messages

---------

Co-authored-by: hxp0618 <1169924772@qq.com>
Co-authored-by: huang <hxp0618@gmail.com>
2025-01-07 19:11:12 +08:00
kangfenmao
edac2004a0 feat: add gemini files support 2025-01-07 16:49:11 +08:00
kangfenmao
a051f9fa44 feat: add optional free model tag display 2025-01-07 11:23:32 +08:00
kangfenmao
a70e69caf9 feat: enable web search for zhipu ai provider #657 2025-01-07 10:53:34 +08:00
kangfenmao
4896db93fd fix: improved error message formatting in api service 2025-01-07 10:19:21 +08:00
kangfenmao
2e7ecbc753 feat: add ModelTags component 2025-01-07 09:54:22 +08:00
kangfenmao
f68bd4d8d8 feat: add support for 'aihubmix' models and aihubmix llm provider 2025-01-07 09:46:05 +08:00
kangfenmao
d0948e6f8a feature: customizable sidebar module #644
close #644
2025-01-06 16:59:10 +08:00
kangfenmao
ac9017c031 feat: add search message shortcut #366 2025-01-06 16:29:39 +08:00
kangfenmao
de1d79abb8 fix: the minimum width limit of the window is too large #544
close #544
2025-01-06 16:25:00 +08:00
kangfenmao
ad577818dd fix: generating topic name after exporting prompt file name is invalid #641
close #641
2025-01-06 15:50:57 +08:00
kangfenmao
bb50447a98 fix: Ollama is unable to create a knowledge base using a local embedding model #630 2025-01-06 15:43:20 +08:00
kangfenmao
158f9bf1ad fix: turn off spell check #648
The next version will be released. close #648
2025-01-06 15:10:03 +08:00
kangfenmao
6a9bc103d7 feat: added optional chaining for code variable 2025-01-06 14:54:04 +08:00
xx-moos
529ec3612e fix: 修复 message 显示时间过长的问题 2025-01-06 14:43:31 +08:00
kangfenmao
d241c38c61 style: border radius use var 2025-01-04 22:50:44 +08:00
kangfenmao
ee5ed8c565 style: logo v3
# Conflicts:
#	src/renderer/src/assets/images/logo.png
2025-01-04 21:52:05 +08:00
huang
dc73661678 feat: 支持 mermaid 点击按钮放大缩小以及鼠标滑轮放大缩小 2025-01-04 19:17:39 +08:00
huang
ce973ce3a0 feat: 支持 mermaid 点击按钮放大缩小以及鼠标滑轮放大缩小 2025-01-04 19:17:39 +08:00
huang
a0413158c8 fix: 修复在macOS m1 中点击全屏幕后,点击关闭后黑屏的问题 2025-01-04 19:17:39 +08:00
kangfenmao
6cb3b16451 fix: Qwen2.5和Qwen的划分不合理 #633 2025-01-03 18:05:01 +08:00
huang
08b0990cf9 fix: 中文国际化错误 2025-01-03 17:35:17 +08:00
kangfenmao
10b9940edd chore(version): 0.9.4 2025-01-02 21:34:30 +08:00
kangfenmao
4cbdd563e8 feat: add translations and file management features 2025-01-02 18:29:36 +08:00
kangfenmao
dba1f76db7 feat: update assistantmodelsettings to persist custom parameters 2025-01-02 17:21:33 +08:00
kangfenmao
15fb605eb4 feat: improved form validation and model addition functionality 2025-01-02 16:58:58 +08:00
kangfenmao
1bf147fa6a refactor: improve model generation and handling functionality 2025-01-02 16:39:30 +08:00
kangfenmao
a782b2b4aa fix: 腾讯混元的联网开关 #575 2025-01-02 16:26:24 +08:00
kangfenmao
7f92cb59a6 feat: add more classname 2025-01-02 16:25:50 +08:00
kangfenmao
6009ae84fb fix: 重新发送按钮无反应 #587 2025-01-02 15:42:47 +08:00
kangfenmao
038aa2d5cc feat: paintings add prompt enhancement params 2025-01-02 14:51:52 +08:00
kangfenmao
6384525e20 feat: added error handling and knowledge base provider support 2025-01-02 14:16:37 +08:00
kangfenmao
3fc7911c97 feat: add new branch option to message menubar 2025-01-02 13:41:51 +08:00
kangfenmao
5f55d8c22c style: adjusted padding and border styles in settingsgroup component 2025-01-02 13:37:04 +08:00
kangfenmao
d9f7bcfc21 feat: custom parameters add json type 2025-01-02 13:34:21 +08:00
kangfenmao
aa72794967 feat: improved translation features and settings 2025-01-02 12:21:22 +08:00
zhouxl
09e6756efe fix:修复单行CodeBlock中显示sub 2025-01-02 11:47:34 +08:00
kangfenmao
dde0400f0d chore: update hika app assets and styles 2025-01-02 11:18:15 +08:00
kangfenmao
1d3a01dd49 feat: add sync status show 2025-01-02 11:07:20 +08:00
YongHao Hu
63cdc15bc2 feat: add hika minapp 2025-01-02 11:06:57 +08:00
kangfenmao
b2818f8619 fix: reduce batch size for knowledge service and openai embeddings 2024-12-31 14:41:08 +08:00
kangfenmao
8ef9fb0216 feat: add github auto assignment workflow 2024-12-31 00:43:16 +08:00
kangfenmao
63488e6fab chore(version): 0.9.3 2024-12-31 00:38:20 +08:00
kangfenmao
6d9013f0a1 fix: 知识库无法向量化 MD 文件 #569 2024-12-31 00:11:51 +08:00
kangfenmao
1a68587684 fix: Microsoft Visual C++ Redistributable #577 2024-12-30 15:07:31 +08:00
kangfenmao
47c455b125 feat: 增加保持并发送的功能 #527 2024-12-30 14:09:59 +08:00
kangfenmao
96124cf58e feat: 增加genspark小程序 #578 2024-12-30 13:10:27 +08:00
juzeon
ef975add01 fix: 修复zh-tw语言文件中的乱码 (#579) 2024-12-30 11:49:40 +08:00
n2yt584v2t4nh7y
ed49066bab feat: 添加自定义API参数功能 (#564)
* add custom api parameters

* allow more data types for custom api parameters

* pass parameter to api payload

* add custom parameter settings to sidebar

* remove unnecessary object and array types

* extract API custom parameter method to BaseProvider

* add i18n for custom parameter settings

---------

Co-authored-by: 亢奋猫 <kangfenmao@qq.com>
2024-12-29 20:19:07 +08:00
kangfenmao
e7545c5a94 feat: 用户自定义话题总结Prompt #562
close #562
2024-12-29 10:20:45 +08:00
kangfenmao
fc35df65b8 feat: add release notes pages 2024-12-29 09:49:22 +08:00
littel_penguin66
56ca81d245 Fix incorrect synchronization behavior of webdav auto sync (#568) 2024-12-29 08:44:21 +08:00
kangfenmao
6bc1f4b640 chore(version): 0.9.2 2024-12-27 23:03:17 +08:00
kangfenmao
ccb216e76a fix: 模型名后面标注一下服务商 #557 2024-12-27 18:09:22 +08:00
kangfenmao
60931b85ff fix: model settings params step size 2024-12-27 16:47:44 +08:00
kangfenmao
dc1dbc7bb6 feat: add jina provider 2024-12-27 16:29:17 +08:00
kangfenmao
5d2efbd62b fix: 需要只发送图片功能 #538 2024-12-27 14:40:44 +08:00
sommermorgentraum
5337017648 feat: Add capabilities for user to load custom CSS #548 2024-12-27 14:11:12 +08:00
kangfenmao
c409256ae9 fix: azure openai embedding 2024-12-27 14:02:53 +08:00
kangfenmao
4ac608052c chore: update dependencies and improve project structure 2024-12-27 12:42:17 +08:00
kangfenmao
5e6aaabb23 fix: 小程序中增加 github copilot #547 2024-12-27 12:10:41 +08:00
kangfenmao
8812daeeee fix: 某些输出包含 sub 无法正常显示 #545 2024-12-27 11:54:11 +08:00
kangfenmao
13e3a8478c feat: added topic message update and search state management 2024-12-27 11:48:12 +08:00
kangfenmao
8687985ccb feat: add windows platform support for node file detection and npm package download 2024-12-26 12:38:51 +08:00
kangfenmao
7d54f9b4fa chore(version): 0.9.1 2024-12-26 12:25:58 +08:00
kangfenmao
6b7ba35183 fix: build native module script 2024-12-26 12:25:58 +08:00
kangfenmao
5b42a6d054 feat: add embeding tag to settings 2024-12-26 12:25:58 +08:00
kangfenmao
153e7a9299 refactor: knowledge base engine change to libsql 2024-12-26 10:00:37 +08:00
littel_penguin66
77e0c5172e Add Japanese localization for i18n (#533) 2024-12-25 22:04:29 +08:00
kangfenmao
c50ac440c8 fix: knowledge base bugs 2024-12-25 21:54:46 +08:00
kangfenmao
34ebab0af8 refactor: knowledge base database engine 2024-12-25 17:42:03 +08:00
Tan Xiang
b85765915e fix: shortcut tips (#525) 2024-12-24 23:09:54 +08:00
kangfenmao
960f50e4e4 fix: gemini web serach modal 400 request error 2024-12-24 18:00:25 +08:00
kangfenmao
65e19d187c revert: cloudflare-worker.js 2024-12-24 17:38:30 +08:00
kangfenmao
aa4f94f8a4 build: download npm node native modules 2024-12-24 17:24:38 +08:00
kangfenmao
aa3812eddc fix: linux window title style 2024-12-24 14:43:32 +08:00
kangfenmao
6b9e58171b feat: update models inside 2024-12-24 13:27:40 +08:00
kangfenmao
2f64653b1e fix: knowledge base bugs 2024-12-24 12:41:58 +08:00
kangfenmao
03dd3038e0 patch: @llm-tools 2024-12-24 12:11:07 +08:00
kangfenmao
f1f7e8e11b feat: added webdav auto-sync settings synchronization and custom show message option 2024-12-24 10:25:19 +08:00
kangfenmao
fbd189c5e1 Merge branch 'knowledge' 2024-12-24 09:38:38 +08:00
little_penguin66
87c3716f75 add autoSync in WebDav 2024-12-24 09:34:16 +08:00
kangfenmao
37477587b6 fix: check provider connection use the last model 2024-12-24 09:33:43 +08:00
kangfenmao
d558572d97 chore(version): 0.9.0 2024-12-23 17:07:26 +08:00
kangfenmao
7506d04c55 build: reduce package size 2024-12-23 14:22:37 +08:00
kangfenmao
35fd5aef22 fix: knowledge bugs 2024-12-23 10:48:40 +08:00
kangfenmao
8f11d2b1c9 chore: remove release and build workflow, update release workflow for macos-latest 2024-12-19 17:24:39 +08:00
kangfenmao
9aa2a4727d build: add matrix 2024-12-19 17:20:52 +08:00
kangfenmao
ca6027dd83 feat: remove knowledge queue 2024-12-19 13:45:11 +08:00
kangfenmao
c2462fd51c feat: knowledge base 2024-12-19 09:24:20 +08:00
tanxiang
0739758469 feat(i18n): add "Switch Model" message to multiple locales and update tooltip in MessageMenubar 2024-12-18 13:35:39 +08:00
adfnekc
b2554333a9 feat: message 增加 metrics 字段 用以统计token生成速度和首字时延 (#479) 2024-12-16 17:10:36 +08:00
kangfenmao
6ced973b35 chore(version): 0.8.27 2024-12-16 15:47:07 +08:00
kangfenmao
ccbeefc546 feat: added long text paste control and threshold settings 2024-12-16 15:08:40 +08:00
kangfenmao
7fdc2db522 fix: o1模型支持流式输出 #439 2024-12-16 14:48:51 +08:00
kangfenmao
978f1342e4 feat: disable select menu text 2024-12-16 14:39:54 +08:00
kangfenmao
ff935a656e feat: add copy last message shortcuts 2024-12-16 14:13:59 +08:00
kangfenmao
15539a5609 feat: add thinkany minapp 2024-12-16 13:42:08 +08:00
kangfenmao
88cd4f2144 fix: mermaid图表代码一键复制功能 #460 2024-12-16 13:20:24 +08:00
kangfenmao
daf2e035b2 fix: 输出不显示 markdown 的小圆点 #446 2024-12-16 12:58:31 +08:00
kangfenmao
7ceb4920ec feat: added hotkey functionality and improved appstorepopover layout 2024-12-16 12:55:14 +08:00
kangfenmao
0074d5c8b4 feat: add svg preview 2024-12-16 12:35:39 +08:00
kangfenmao
96737ed695 feat: add display settings 2024-12-16 12:04:12 +08:00
kangfenmao
356da1ea67 feat: add miniapp icon to navbar right 2024-12-16 11:32:50 +08:00
kangfenmao
debf996146 feat: add n.cn to minapp list 2024-12-16 10:48:55 +08:00
kangfenmao
8d73d1e844 fix: input bar default rows #431 2024-12-16 10:26:04 +08:00
duanyongcheng77
b0d777293b feat: 🎸 可以多次点击上传文件按钮上传文件 2024-12-16 09:54:12 +08:00
kangfenmao
1a9fbbc0a2 fix: KaTeX引擎公式渲染错位 #473 2024-12-16 09:42:10 +08:00
kangfenmao
ab99a7b96d chore(version): 0.8.26 2024-12-15 18:03:36 +08:00
kangfenmao
7d561dbfb7 feat: added setshowassistants function to useshowassistants hook and updated error handling logic 2024-12-13 16:37:48 +08:00
kangfenmao
6af07c278d fix: handle unknown models in iswebsearchmodel function 2024-12-13 10:28:09 +08:00
kangfenmao
9c18b851cc build: update electron version 2024-12-13 09:52:18 +08:00
Shelly
b1ebe13b5f feat: 🎸 allowMarkdownLongTextToAutomaticallyWrap (#454)
Co-authored-by: duanyongcheng77 <duanyongcheng77@gmail.com>
2024-12-13 09:51:42 +08:00
kangfenmao
9b258734c4 chore: update dependencies and remove unused code 2024-12-13 09:35:40 +08:00
kangfenmao
25eb97902b chore(version): 0.8.25 2024-12-12 18:24:06 +08:00
kangfenmao
2fae6e4a3e feat: add web search for google gemini modal gemini-2.0-flash-exp 2024-12-12 14:26:52 +08:00
Shelly
f312c5fc40 feat: 🎸 add shortcut for command + enter (#443)
* feat: 🎸 add shortcut for command

* feat: 🎸 only command

---------

Co-authored-by: duanyongcheng77 <duanyongcheng77@gmail.com>
2024-12-12 14:22:41 +08:00
kangfenmao
afa96549a3 styles: use mac style 2024-12-11 20:02:15 +08:00
kangfenmao
6beee78ce8 fix: can not delete last message 2024-12-11 20:01:52 +08:00
kangfenmao
a230ee2c69 chore(version): 0.8.24 2024-12-11 11:41:16 +08:00
kangfenmao
28a27447a5 feat: add new social media translations and links 2024-12-10 20:36:37 +08:00
kangfenmao
408976e5dc feat: add shortcut for assistant and topic show 2024-12-10 20:28:05 +08:00
kangfenmao
7153996c35 fix: reduced message counts for messages component 2024-12-10 19:53:14 +08:00
kangfenmao
73f6a743cd feat: add enter key trigger for translate model prompt 2024-12-10 19:41:50 +08:00
kangfenmao
3b250d7d78 style: improved layout and styling 2024-12-10 19:39:00 +08:00
kangfenmao
272efaf76e feat: add top-p settings #224 2024-12-10 19:24:30 +08:00
kangfenmao
44c64a571a feat: sidebar shadow 2024-12-10 18:07:37 +08:00
kangfenmao
f817d9136b fix: 清除上下文按钮容易误点 #426 2024-12-10 17:23:00 +08:00
kangfenmao
c0f192c6f2 fix: support "ctrl+enter" as send shortcuts #244 2024-12-10 17:09:57 +08:00
kangfenmao
b5a109401c feat: add update info ui 2024-12-10 17:06:29 +08:00
牡丹凤凰
aeff59946c Update models.ts 2024-12-10 05:23:13 +08:00
kangfenmao
21ad4cfecc refactor: improve llmodel group assignment logic and sorting 2024-12-09 11:26:02 +08:00
kangfenmao
4df39179bb fix: escaped special characters in code snippets #419 2024-12-09 09:50:15 +08:00
牡丹凤凰
423fdb6992 Update cloudflare-worker.js 2024-12-07 15:28:57 +08:00
亢奋猫
f66adcd217 Merge pull request #418 from 1355873789/develop
历史消息懒加载
2024-12-07 14:41:48 +08:00
Amatsuka
465bf4006c elfix: Add the grok vision model and fix the incorrect marking of the grok beta model as a visual model. 2024-12-07 14:39:48 +08:00
首都爱护动物协会
14c9cb6001 历史消息懒加载
性能优化
2024-12-07 12:27:16 +08:00
牡丹凤凰
e35d928bcd Merge branch 'kangfenmao:main' into develop 2024-12-07 12:21:15 +08:00
duanyongcheng77
1981f2e648 style: 💄 change chinese-traditional icon to hk 2024-12-06 17:03:28 +08:00
kangfenmao
e5c1791135 chore(version): 0.8.23 2024-12-06 00:12:07 +08:00
kangfenmao
ae1960f5c6 fix: plain text render 2024-12-05 22:33:21 +08:00
kangfenmao
51ca9cb289 fix: check for update ui 2024-12-05 21:38:11 +08:00
kangfenmao
7d2df1a8c5 fix: register shortcut key error 2024-12-05 21:05:26 +08:00
kangfenmao
2757535cf0 feat: added translation support for non-english languages 2024-12-05 09:50:15 +08:00
kangfenmao
243065221d fix: add error handling and logging for shortcut registration failures, remove windows shortcut support 2024-12-04 19:37:37 +08:00
kangfenmao
2a674c169e chore(version): 0.8.22 2024-12-04 14:08:09 +08:00
kangfenmao
100dbc8101 feat: added new issue templates for documentation and question issues 2024-12-04 09:32:52 +08:00
kangfenmao
67d7ccbf10 chore(version): 0.8.21 2024-12-03 20:45:30 +08:00
kangfenmao
a71782abb6 fix: improved error handling with formatted json error messages 2024-12-03 20:42:13 +08:00
kangfenmao
73973ecb7f feat: change re-generage message logic 2024-12-03 20:32:18 +08:00
kangfenmao
368de84440 docs: add chineses issue template 2024-12-03 19:02:41 +08:00
kangfenmao
a170dbd6f0 feat: improved search functionality for agent page 2024-12-03 18:46:57 +08:00
kangfenmao
9b84176a42 wip 2024-12-03 17:45:39 +08:00
kangfenmao
0f36610e23 chore: cleaned up dependencies and database schema 2024-12-03 17:21:51 +08:00
kangfenmao
1e273834b8 fix: translate prompt and translateText funciton 2024-12-03 17:18:18 +08:00
kangfenmao
3b569131a5 chore: add github issue template 2024-12-03 16:06:17 +08:00
kangfenmao
115f111071 chore(version): 0.8.20 2024-12-03 12:52:08 +08:00
kangfenmao
a4d1bcffd9 style: update api key input field formatting and validation 2024-12-03 12:24:25 +08:00
kangfenmao
f5d37a4e53 feat: implement automatic text translation functionality 2024-12-03 11:55:52 +08:00
kangfenmao
a9d4a0885c feat: add MessageErrorBoundary component 2024-12-03 11:15:35 +08:00
kangfenmao
6596497c97 feat: added topic auto-renaming to messages page 2024-12-03 10:57:38 +08:00
kangfenmao
12d8f57dab feat: add enable topic naming settings #399 2024-12-03 10:47:43 +08:00
kangfenmao
7f2f3ad88a refactor: shortcuts feature 2024-12-03 10:23:53 +08:00
kangfenmao
cd3c053f81 feat: add translations and reset functionality for new features 2024-12-02 22:29:18 +08:00
kangfenmao
7dacd58821 feat: add shortcut feature 2024-12-02 18:03:38 +08:00
kangfenmao
744a6ac7cb feat: add translated message content 2024-12-02 13:41:28 +08:00
kangfenmao
2e9041c891 feat: auto translate input text 2024-11-30 22:37:33 +08:00
kangfenmao
3717ff25bf feat: generate random seed for image generation in usepaintings hook 2024-11-30 20:22:07 +08:00
kangfenmao
494d52ac85 fix: update ipc handle to retrieve update information 2024-11-30 20:22:00 +08:00
kangfenmao
22d2ff1518 feat: added command shortcuts for new topic input 2024-11-30 14:18:38 +08:00
kangfenmao
06ae4328ea style: update code block padding and topic position 2024-11-30 12:01:00 +08:00
kangfenmao
8de1197557 feat: add configurable request options to gemini provider 2024-11-26 13:15:25 +08:00
kangfenmao
09e86b35a5 fix: proxy 2024-11-25 15:57:16 +08:00
kangfenmao
76ea170a01 chore(version): 0.8.19 2024-11-25 14:16:33 +08:00
kangfenmao
4cd962b42f fix: 链接失败警告不消失 #382
close #382
2024-11-25 14:14:03 +08:00
kangfenmao
1cae86f93d fix: 模型回答时可以新建对话 #369
close #369
2024-11-25 14:10:09 +08:00
kangfenmao
1171100417 fix: code block copy button missing 2024-11-25 13:56:41 +08:00
kangfenmao
dcf57651fe feat: update input component and add translation features 2024-11-25 13:43:03 +08:00
kangfenmao
603b867a5f fix: auto update assistant model 2024-11-25 12:49:47 +08:00
kangfenmao
e765bf9828 feat: create assistant with search text 2024-11-25 12:30:46 +08:00
kangfenmao
33d5da7325 feat: clear app cache 2024-11-25 11:16:24 +08:00
kangfenmao
383e8255a0 feat: add localization text and model type support #378 2024-11-25 10:31:57 +08:00
kangfenmao
4cb5c128bb feat: add border to list item 2024-11-24 01:35:41 +08:00
kangfenmao
949a13b021 fix: 设置中有两个“代码风格”项 #373 2024-11-23 23:08:27 +08:00
kangfenmao
e6c9cb60dc chore(version): 0.8.18 2024-11-23 14:23:22 +08:00
kangfenmao
ad625b23a7 feat: improve proxy configuration handling 2024-11-23 14:21:08 +08:00
kangfenmao
2e34b79f26 build: add scripts/cloudflare-worker.js 2024-11-22 10:01:54 +08:00
kangfenmao
9288e7b292 feat: update electron configuration and add locale-based language selection #370 2024-11-21 18:14:27 +08:00
kangfenmao
ec703852f8 style: adjust window vibrancy style 2024-11-20 20:18:51 +08:00
kangfenmao
fca9fb0c84 chore(version): 0.8.17 2024-11-20 19:15:33 +08:00
kangfenmao
64c8831530 style: removed dark theme styles, updated search and ui 2024-11-20 19:14:09 +08:00
kangfenmao
75e396ecf0 chore(version): 0.8.16 2024-11-20 18:50:27 +08:00
kangfenmao
697c3b1838 feat: implement new ui features and styles 2024-11-20 17:49:39 +08:00
kangfenmao
efa0f4cbdb Revert "feat: remove model settings from settings tab"
This reverts commit a9aa5a8da0.

# Conflicts:
#	src/renderer/src/pages/home/Tabs/Settings.tsx
2024-11-20 17:38:10 +08:00
kangfenmao
c3414e9b6d feat: add new model visionallowedmodels 2024-11-20 16:40:00 +08:00
kangfenmao
6ba6108f43 style: update styles for improved visual consistency and layout 2024-11-20 16:37:52 +08:00
kangfenmao
c3d007b52c feat: added size attribute to preview and download buttons 2024-11-20 11:57:57 +08:00
kangfenmao
e1494d408f feat: enable auto-update and auto-install of app updates 2024-11-20 11:04:42 +08:00
kangfenmao
cd625430b2 style: adjusted layout and styling for searchpopup component 2024-11-20 11:01:02 +08:00
kangfenmao
aefb08965d chore(version): 0.8.15 2024-11-19 23:46:22 +08:00
kangfenmao
d2dd70000b docs: visual updates to shortcutsettings page 2024-11-19 20:53:12 +08:00
kangfenmao
f0a96bb34c fix: 无法恢复备份文件 #341 2024-11-19 20:30:47 +08:00
kangfenmao
0ec61e1c47 feat: new settings ui 2024-11-19 19:54:18 +08:00
kangfenmao
335ce4963b feat: added data folder 2024-11-19 18:48:57 +08:00
kangfenmao
63ef0d2df1 fix: 编辑消息无效 #313 2024-11-19 15:34:03 +08:00
kangfenmao
c0c0e8ae33 style: update antdesign segmented styles 2024-11-19 15:17:54 +08:00
kangfenmao
771b078df9 feat: adjusted searchpopup dimensions and added assistant generation checks 2024-11-19 15:08:04 +08:00
kangfenmao
64e70ea918 style: update color scheme to darker shades of black 2024-11-19 14:45:22 +08:00
kangfenmao
2d46a4494e feat: add search popup 2024-11-19 14:17:38 +08:00
kangfenmao
9b7e2282fe feat: improved agents page filtering and layout adjustments, added russian translations support 2024-11-19 12:04:01 +08:00
kangfenmao
535b7d0a92 style: update code block ui styling and logic 2024-11-19 11:49:35 +08:00
kangfenmao
223496192d feat: validate html elements in markdown, refactor cursor type, disable markdown input rendering 2024-11-19 11:45:00 +08:00
kangfenmao
db779446f0 chore(version): 0.8.14 2024-11-19 11:13:38 +08:00
kangfenmao
8ef3ef2a8f fix: has unsafe elements 2024-11-19 11:13:16 +08:00
Emmanuel Ferdman
30da183578 docs: update license reference
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
2024-11-19 09:19:02 +08:00
首都爱护动物协会
49c09f381c Update models.ts
douhbao matching rules update
2024-11-19 04:08:26 +08:00
kangfenmao
c8c58ddcfb feat: auto switch to topics 2024-11-18 17:51:17 +08:00
kangfenmao
5bffb86d4f feat: code collapsible settings 2024-11-18 17:44:33 +08:00
kangfenmao
84fa5b065b wip 2024-11-18 17:16:55 +08:00
kangfenmao
7ecb35dfa7 fix: 缩放快捷键不支持小键盘 #354 2024-11-18 17:05:50 +08:00
kangfenmao
2d7d403b15 fix: 重启之后无法保存界面的缩放 #354 2024-11-18 17:00:26 +08:00
kangfenmao
7342a0afef feat: added proxy settings and handling functionality 2024-11-18 14:05:17 +08:00
kangfenmao
1b8a3885f7 feat: add provider type 2024-11-18 13:04:46 +08:00
kangfenmao
c33c0b20f2 feat: enabled provider after add model 2024-11-18 11:41:17 +08:00
kangfenmao
4f75f29361 feat: added gemini experimental to vision allowed models 2024-11-18 11:26:02 +08:00
injurka
fe00eed7b9 feat: russia locale 2024-11-13 08:21:13 +08:00
kangfenmao
30fa9277ff chore: update release workflow with tag filtering 2024-11-12 19:59:44 +08:00
kangfenmao
11a446e106 chore(version): 0.8.13 2024-11-12 19:27:46 +08:00
kangfenmao
d2ca6f1041 feat: add new button to inputbar component and remove unnecessary toolbar button 2024-11-12 19:27:46 +08:00
kangfenmao
0f3dc87d08 fix: improved popup item filtering logic 2024-11-12 19:27:46 +08:00
kangfenmao
7d3cae1f5b feat: add adaptive tray icon handling 2024-11-12 17:52:00 +08:00
kangfenmao
ceae1fa3d0 feat: remove tray behavior on app close, add platform detection and file extensions 2024-11-12 16:36:47 +08:00
kangfenmao
12a2c8c86d feat: added api connection validation and provider configuration 2024-11-12 13:56:03 +08:00
kangfenmao
29d6c4be18 feat: update system_models to use gpt-4o-latest model 2024-11-12 13:28:40 +08:00
kangfenmao
738e51c078 style: adjusted styling and layout for mac os and file list view 2024-11-12 12:09:34 +08:00
kangfenmao
db050c002a feat: improved pin model functionality and translations 2024-11-12 11:54:11 +08:00
kangfenmao
398f995cd1 feat: handle sorted and filtered agent groups in agentspage 2024-11-12 11:26:09 +08:00
kangfenmao
348fc365fa feat: update system tray icon functionality and ui 2024-11-12 11:16:55 +08:00
kangfenmao
7b6d38e349 feat: add provider deletion functionality and related translations 2024-11-12 10:14:01 +08:00
kangfenmao
6a35c0e3d8 fix: fixed tiling window manager and password input handling issues 2024-11-12 10:00:58 +08:00
kangfenmao
9a63169a73 fix: 视觉模型识别错误 #329 2024-11-12 09:23:24 +08:00
kangfenmao
a9aa5a8da0 feat: remove model settings from settings tab 2024-11-12 00:50:13 +08:00
kangfenmao
a2d568175b feat: update translations and display settings 2024-11-12 00:37:23 +08:00
kangfenmao
0b9717780d feat: improved tray functionality and ui theme settings 2024-11-12 00:25:57 +08:00
kangfenmao
b371fed814 style: improved mermaid popup styling and positioning 2024-11-11 23:49:44 +08:00
injurka
3311f8cdef feat: added settings for minimize to tray instead of closing 2024-11-11 23:49:18 +08:00
kangfenmao
422baa848b fix: fix select box issue with mermaid diagrams 2024-11-11 23:33:57 +08:00
kangfenmao
739aa21475 style: improved chat window styling and accessibility 2024-11-11 23:26:45 +08:00
kangfenmao
23ef4ab952 fix: adjusted tray icon size to match linux platform requirements 2024-11-11 18:03:03 +08:00
injurka
b77f845cb0 fix: the context menu is not displayed on linux 2024-11-11 17:58:20 +08:00
kangfenmao
0573b274ed feat: add loading indicator and disable check button while checking api keys 2024-11-11 17:57:48 +08:00
kangfenmao
60433bb1ab style: update bubble ui component styles 2024-11-11 17:41:53 +08:00
kangfenmao
1caf53fbda fix: message color 2024-11-11 13:40:25 +08:00
kangfenmao
446e011c6a chore(version): 0.8.12 2024-11-11 11:18:43 +08:00
kangfenmao
c319b54a26 feat: rename images to paintings for consistency 2024-11-11 10:45:42 +08:00
kangfenmao
e9ca1d54a0 feat: add check all keys popup 2024-11-11 10:41:43 +08:00
kangfenmao
1ff8fe0c2e fix: removed macos window minimize event handler 2024-11-11 09:25:22 +08:00
kangfenmao
902341bc1d chore(version): 0.8.11 2024-11-10 17:20:52 +08:00
kangfenmao
17fff46024 style: update code formatting for markdown styles 2024-11-10 17:16:10 +08:00
kangfenmao
d258c1cfe2 feat: add mermaid preview and download feature 2024-11-10 17:09:18 +08:00
kangfenmao
79cabadfb8 fix: 加入账号轮询功能 #293 2024-11-09 23:18:56 +08:00
kangfenmao
61ceca2363 feat: update sidebar menu with interactive tooltips and painting terminology 2024-11-09 22:26:35 +08:00
kangfenmao
8a8deda002 fix: removed theme dependency in syntaxhighlighterprovider, added mermaid delay 2024-11-09 15:02:38 +08:00
kangfenmao
6536ec227a fix: 气泡默认界面显示异常 #308 2024-11-09 14:54:22 +08:00
kangfenmao
af1fd90118 feat: add mermaid library and theme initialization 2024-11-09 14:51:39 +08:00
kangfenmao
68fa2bad15 fix: mermaid图渲染有问题 #310 2024-11-09 14:16:42 +08:00
kangfenmao
bac3bad8db style: improved syntax highlighting functionality and code loading 2024-11-09 10:05:34 +08:00
kangfenmao
e11633310c feat: add the ability to display the application in tray #297 2024-11-09 08:42:09 +08:00
kangfenmao
612b39a878 fix: code style init value 2024-11-08 11:59:32 +08:00
injurka
8491141edc fix: expand code syntax highlighting options (#307)
* added locale for context-menu

* fix: expand code syntax highlighting options

* fix: type for theme

---------

Co-authored-by: injurka <ikornilov.ext@prosebya.ru~>
2024-11-08 11:59:05 +08:00
kangfenmao
088628f89f chore(version): 0.8.10 2024-11-07 23:06:42 +08:00
kangfenmao
a6b4e48640 fix: 添加自定义的模型的分组总是成为大写字母 #257 2024-11-07 22:51:37 +08:00
kangfenmao
ba0e2c5848 fix: input token i18n 2024-11-07 22:51:37 +08:00
injurka
d986087857 fix: when changing the values in the inputs in the tab Default Assisting, they do not change (#305)
* added locale for context-menu

* fix: when changing the values in the inputs in the tab Default Assisting, they do not change

---------

Co-authored-by: injurka <ikornilov.ext@prosebya.ru~>
2024-11-07 22:13:47 +08:00
kangfenmao
73c93c5581 feat: 添加DuckDuckGo AI Chat #271 2024-11-07 22:08:11 +08:00
kangfenmao
ceec4a9f97 fix: 删除最后一组预设消息组后无法保存 #300 2024-11-07 18:11:45 +08:00
kangfenmao
cf08467552 fix: 气泡模式,代码框配色看不清楚 #302 2024-11-07 17:49:31 +08:00
kangfenmao
34c85e8f0c fix: github models 不支持图片 #291 2024-11-07 17:32:37 +08:00
injurka
1db3faa2a8 fix type import 2024-11-06 22:09:28 +08:00
injurka
35efada37e added locale for context-menu 2024-11-06 22:09:28 +08:00
kangfenmao
ceca3408ff refactor: refactored capturescrollablediv function and updated chat and messages components 2024-11-06 15:56:48 +08:00
kangfenmao
f2def559d4 chore: remove unused llm store model and migration config 2024-11-06 13:17:43 +08:00
首都爱护动物协会
cd97be0f10 Match the avatar for the Pixtral model. 2024-11-05 20:50:43 +08:00
首都爱护动物协会
b87394ed88 add pixtral avatar 2024-11-05 20:50:43 +08:00
亢奋猫
a4d8e71916 Merge branch 'main' into develop 2024-11-05 20:50:25 +08:00
首都爱护动物协会
39cf227e42 Match the avatar for the Pixtral model. 2024-11-05 17:26:04 +08:00
kangfenmao
5d01d12d2a fix: fix shortcut registration and unregistration 2024-11-05 17:25:38 +08:00
首都爱护动物协会
d2cad31db4 add pixtral avatar 2024-11-05 17:22:24 +08:00
Hypered1
d7f4e4584a Fix typo in zh-cn.json 2024-11-05 16:48:56 +08:00
首都爱护动物协会
eda870f181 Add new model and provider avatars. 2024-11-05 16:48:07 +08:00
首都爱护动物协会
3f093a91be Add Providers
1. Fix the naming error of the Grok model
2. Add new providers: Grok, Mistral, Jina, Hyperbolic
2024-11-05 16:48:07 +08:00
首都爱护动物协会
aa864f3876 Add new model and provider avatars. 2024-11-05 16:04:30 +08:00
首都爱护动物协会
77a8b23d76 Add Providers
1. Fix the naming error of the Grok model
2. Add new providers: Grok, Mistral, Jina, Hyperbolic
2024-11-05 15:56:36 +08:00
kangfenmao
45dd76e281 chore(version): 0.8.9 2024-11-03 01:26:23 +08:00
kangfenmao
568d4814e3 feat: remove image compress 2024-11-03 01:26:00 +08:00
kangfenmao
9468f3b511 refactor: main code 2024-11-02 23:32:59 +08:00
kangfenmao
04af940144 feat: export to word 2024-11-02 21:45:23 +08:00
kangfenmao
e33d9ac0ae fix: 在对话中插入图片时,应该自动压缩一下 #132 2024-11-02 15:30:41 +08:00
kangfenmao
cd835b7c36 fix: Ctrl + - 缩放时 缩到最小 再缩的话会报错 #266 2024-11-02 15:13:43 +08:00
kangfenmao
dd4239da87 fix: 默认模型在模型服务中禁用后显示错误 #266 2024-11-02 15:11:36 +08:00
kangfenmao
41c3895da4 feat: add message card style switch 2024-11-01 21:50:40 +08:00
kangfenmao
2e9c7d0830 fix: chat text color 2024-11-01 18:14:23 +08:00
kangfenmao
8ea73e14c9 style(message): user message use black color 2024-11-01 17:43:21 +08:00
kangfenmao
3791556b13 chore(version): 0.8.8 2024-11-01 12:39:11 +08:00
kangfenmao
e0dab5cf5b feat: set topic position to left 2024-11-01 12:28:13 +08:00
kangfenmao
1785e7df0a feat: dynamic handling for tab groups 2024-11-01 12:18:48 +08:00
kangfenmao
6cb1846b23 fix: ui and layout 2024-11-01 12:06:30 +08:00
kangfenmao
21243579b3 feat: added sorting functionality and updated translations 2024-11-01 11:46:11 +08:00
kangfenmao
0d2ad2e4c3 feat: improved ui layout and added reusable add agent card component 2024-11-01 11:00:17 +08:00
kangfenmao
071a3950cd feat: topic position set default to left 2024-10-31 17:11:32 +08:00
Teo
dc6066b74c refactor(navbar): 移除未使用的代码 2024-10-31 16:51:38 +08:00
Teo
ce55d8d0e7 refactor(navbar): 移除未使用的代码 2024-10-31 16:51:38 +08:00
Teo
d4ae321cd2 style(toggleTheme): 将主题切换按钮移到左下角菜单栏中 2024-10-31 16:51:38 +08:00
Teo
89dd35c98d style(chat): 对话界面改为左右布局 2024-10-31 16:51:38 +08:00
Teo
b8c70a3061 style(markdown): 小代码块不换行 2024-10-31 16:51:38 +08:00
kangfenmao
968a749aaa chore(version): 0.8.7 2024-10-31 15:01:37 +08:00
kangfenmao
e2fc593624 style: update responsive container styling for paintingslist component 2024-10-31 14:39:27 +08:00
kangfenmao
0e1674ce6c feat: added new painting functionality with mac device restriction 2024-10-31 14:35:05 +08:00
kangfenmao
18566989be chore(version): 0.8.6 2024-10-31 13:34:06 +08:00
kangfenmao
31fa10f185 feat: improved real-time painting generation support 2024-10-31 13:28:07 +08:00
kangfenmao
f6aa0dc55a feat: added translations and ui improvements 2024-10-31 13:18:35 +08:00
kangfenmao
ca2a9ed84a feat: added translation options for opening all files 2024-10-31 12:17:27 +08:00
kangfenmao
79f6d598ab feat: added translation functionality and chinese support 2024-10-31 12:11:30 +08:00
kangfenmao
fb564733e4 style: prevent drag on image preview switch 2024-10-31 11:38:37 +08:00
kangfenmao
63e5972dd2 feat: improvedscrollbar component functionality and added internationalization support to agentspage component 2024-10-31 11:31:38 +08:00
首都爱护动物协会
b80270709f add new providers logo 2024-10-31 09:37:17 +08:00
首都爱护动物协会
d7b459dcee Agents Page Upgrade
1. Simplified the layout of the agents page for improved user experience.

2. Enhanced the design of agent cards for a more visually appealing look.
2024-10-31 09:37:17 +08:00
kangfenmao
76b9e1a65e fix: painting no provider 2024-10-31 09:23:58 +08:00
kangfenmao
b148c5adf5 feat: files ui improvements 2024-10-30 20:45:48 +08:00
kangfenmao
2313f66ad9 refactor: services 2024-10-30 17:23:52 +08:00
kangfenmao
02edd983d1 chore: remove useless files 2024-10-30 00:32:27 +08:00
kangfenmao
3e049baaa4 feat: added file download functionality and improved api 2024-10-30 00:32:27 +08:00
kangfenmao
7401d85825 feat: add paintaing page 2024-10-30 00:32:27 +08:00
AHpx-Lap
241dcddfed feat: use auto theme as default 2024-10-30 00:31:56 +08:00
kangfenmao
cd0ea8154d chore: update dependencies and electron version 2024-10-30 00:31:03 +08:00
kangfenmao
6d6788eeb2 feat: update feature list and documentation for improved user understanding 2024-10-29 16:12:16 +08:00
kangfenmao
9ac35ae3d8 docs: update documentation to reflect project changes 2024-10-29 15:29:44 +08:00
kangfenmao
72e847258d feat: add instance lock and second instance handling 2024-10-29 14:48:48 +08:00
kangfenmao
0cc460a4a3 chore(version): 0.8.5 2024-10-29 02:46:45 +08:00
kangfenmao
98307d5d85 feat: add chinese translations and improve ui 2024-10-29 02:26:10 +08:00
kangfenmao
f73749ac63 feat: add keyborad shortcut settings 2024-10-29 01:55:11 +08:00
kangfenmao
c5deba270f docs: update sponsorship links and qr code references 2024-10-29 00:45:26 +08:00
kangfenmao
bf5617393b feat: enhanced search functionality with translation support 2024-10-29 00:40:44 +08:00
kangfenmao
057efbf98c fix: agents sort 2024-10-29 00:27:35 +08:00
kangfenmao
2143a6614e fix: add X-Api-Key headers #246 2024-10-28 23:33:20 +08:00
kangfenmao
6f9eb2ae75 fix: add claude-3-5-sonnet-latest support #247 2024-10-28 16:40:37 +08:00
kangfenmao
73c2945961 fix: agents tabs not shown 2024-10-28 16:23:55 +08:00
首都爱护动物协会
18beffcc29 Update agents.json 2024-10-28 08:50:59 +08:00
kangfenmao
2b17319855 feat: enhanced text wrapping and ant-input styling 2024-10-27 22:50:45 +08:00
kangfenmao
d77c1ce2b4 feat: update agents.json 2024-10-27 19:53:20 +08:00
kangfenmao
b43f5c9ead fix: fix stale state issue in chat component 2024-10-27 19:30:18 +08:00
kangfenmao
a8651ec558 feat: enhanced ui with translation and layout improvements 2024-10-27 19:13:54 +08:00
kangfenmao
d76a173706 feat: use real file path 2024-10-27 18:58:23 +08:00
kangfenmao
7ec3cb05f2 feat: scroll to bottom on messages page load 2024-10-27 18:33:01 +08:00
kangfenmao
a83d514169 fix: removed filter condition and messages from fetchchatcompletion() payload 2024-10-27 00:11:30 +08:00
kangfenmao
1f8551135f style: optimized performance and refined styles 2024-10-26 23:48:14 +08:00
kangfenmao
1444739cc6 style: align tab content horizontally and ignore agents.json with prettier 2024-10-26 23:36:06 +08:00
kangfenmao
c7cbecad68 feat: update ui components with improved design and functionality 2024-10-26 23:14:33 +08:00
kangfenmao
ab1c597e1c refactor: improved code readability for filtering agents 2024-10-26 22:38:31 +08:00
kangfenmao
ac21c90b6f feat: add agents tabs and search 2024-10-26 22:33:47 +08:00
kangfenmao
9ec0836d26 feat: added icons to buttons for preview and download 2024-10-26 17:29:35 +08:00
kangfenmao
ee966010e1 refactor: messages completion 2024-10-26 17:12:06 +08:00
kangfenmao
7c99621558 fix: WebDAV 备份失败 maxBodyLength 限制 #243 2024-10-25 13:26:46 +08:00
kangfenmao
cfb3eb7d90 docs: update documentation for a more inclusive environment and added japanese and chinese documentation 2024-10-25 00:09:01 +08:00
kangfenmao
64ad2fc9f4 chore(version): 0.8.4 2024-10-24 23:14:14 +08:00
kangfenmao
7f0909c796 fix: 新的滚动条组件 2024-10-24 23:08:11 +08:00
kangfenmao
27631d9cff chore(version): 0.8.3 2024-10-24 18:47:20 +08:00
kangfenmao
596cf8e3f2 docs: update translation and api url tip 2024-10-24 16:19:47 +08:00
kangfenmao
6e2ab66b81 fix: 添加默认助手会添加两个 #238 2024-10-24 15:46:08 +08:00
kangfenmao
2cbb4c8831 fix: 公式显示问题 #239 2024-10-24 15:33:04 +08:00
kangfenmao
5347f63aa8 fix: 添加默认助手会添加两个 #238 2024-10-24 15:05:13 +08:00
kangfenmao
077a66c675 feat: scrollbar 2024-10-24 14:58:13 +08:00
kangfenmao
6e7b6d8387 build: update yarn.lock 2024-10-24 11:45:41 +08:00
Ikko Eltociear Ashimine
bdf6df1936 docs: add Japanese README file
I created Japanese translated README.
2024-10-23 20:50:58 +08:00
kangfenmao
a2dd440f77 fix: 公式又不居中了 #231 2024-10-23 20:49:22 +08:00
kangfenmao
b47d6c95e7 fix: 修复数据库和 store 数据不一致问题 2024-10-23 14:08:48 +08:00
kangfenmao
6265d27ebc feat: add cherry-stuido-db project 2024-10-22 22:01:56 +08:00
kangfenmao
0dd60cb129 chore(version): 0.8.2 2024-10-22 20:06:11 +08:00
kangfenmao
04dae10d89 fix: 汉语新解卡片 css 2024-10-22 19:53:59 +08:00
kangfenmao
71ef0f319f feat: 话题分享功能 #103 2024-10-22 19:01:46 +08:00
kangfenmao
58817ae82f fix: 文件保存相关问题 #208 2024-10-22 17:37:22 +08:00
kangfenmao
7e477cb9c7 docs: CONTRIBUTING.md.md to CONTRIBUTING.md 2024-10-22 16:04:31 +08:00
kangfenmao
1063610c01 fix: scrollbar width 2024-10-22 16:03:13 +08:00
kangfenmao
927670d3a3 build: add pulish:artifacts command 2024-10-22 15:43:28 +08:00
XuQing Chai
2fea7659b1 Modify the path of README.zh.md in README.md correctly 2024-10-21 23:27:18 +08:00
kangfenmao
43b9298329 feat: 智能体改进:名称、上下文支持、模型参数支持 #59 2024-10-21 23:21:46 +08:00
kangfenmao
fe2e3bfc36 feat: add qwen2-vl modal vision support 2024-10-18 13:21:11 +08:00
牡丹凤凰
bae80fda8d Merge pull request #205 from cawabj/develop
misc
2024-10-18 03:31:22 +08:00
首都爱护动物协会
ab709b9c61 misc 2024-10-18 03:30:05 +08:00
kangfenmao
2c28e3bb76 style: improved layout and functionality for the prompt editing field. 2024-10-17 16:52:18 +08:00
kangfenmao
d98020e12c docs: update readme documentation with telegram link and welcome message 2024-10-17 16:29:47 +08:00
kangfenmao
25addc390f docs: update community information and telegram 2024-10-17 16:22:19 +08:00
kangfenmao
88d04a1a6e docs: add product hunt 2024-10-17 15:59:41 +08:00
kangfenmao
1f582c672d fix: remove some file extensions 2024-10-17 15:13:18 +08:00
kangfenmao
c913b2a6d0 fix: java 格式文件上传支持 #201
close #201
2024-10-17 14:26:08 +08:00
kangfenmao
267c60f24d docs: update LICENSE 2024-10-17 14:09:30 +08:00
kangfenmao
a8ccaf6847 feat: Agents 页面改版 #198 2024-10-17 13:44:52 +08:00
kangfenmao
a3a005b946 docs: update commercial use license terms 2024-10-17 13:36:20 +08:00
kangfenmao
2220a6016e feat: added human-readable file size formatting and unit support 2024-10-17 13:35:51 +08:00
kangfenmao
3197390f1a docs: update references to main branch 2024-10-17 10:06:21 +08:00
kangfenmao
5f04d1adb1 fix: local package.json 2024-10-16 17:43:19 +08:00
kangfenmao
76b6593545 fix: 检查更新按钮不生效 #184
close #184
2024-10-16 13:14:15 +08:00
kangfenmao
04ce641bf7 fix: removed unnecessary newline replacement
- Removed unnecessary newline replacement from input message content.
2024-10-16 11:23:58 +08:00
kangfenmao
31e912aac3 fix: 点击清除上下文直接跳转到最下面 #192
close #192
2024-10-16 09:53:56 +08:00
kangfenmao
832ec99d92 chore: removed resources from excluded files
- Removed resources from excluded files.
2024-10-16 09:44:29 +08:00
kangfenmao
ef9fda6d0c chore(version): 0.8.1 2024-10-15 21:19:54 +08:00
kangfenmao
624230411a feat: added new translations and api url handling features
- Added new translation strings for API URL actions and hints.
- Updated Chinese translations and added new provider API URL descriptions.
- Added new translations for API URL preview and reset tip.
- Added support for Open AI API settings preview and hint.
- Added a new isOpenAIProvider function to handle specific provider type checks.
- Added a new function to validate if a given URL has a valid non-root path.
2024-10-15 21:14:19 +08:00
kangfenmao
14808649f8 feat: added conditional rendering to messagetokens component
- Added conditional rendering to MessageTokens component.
- Added parameter 'isLastMessage' to MessageTokens component to determine conditional rendering based on message position.
2024-10-15 20:22:01 +08:00
kangfenmao
3cc8cfb43b fix: code font size 2024-10-15 19:21:18 +08:00
kangfenmao
4055111ade feat: add show line number in code 2024-10-15 19:18:12 +08:00
kangfenmao
dc98b27e3e feat: add license.html 2024-10-15 19:02:53 +08:00
kangfenmao
90fec317e5 feat: add data settings 2024-10-15 18:56:09 +08:00
kangfenmao
303a0e20a0 fix: webdav备份恢复的逻辑似乎有点问题 #178 2024-10-15 17:48:48 +08:00
kangfenmao
d69252a7da feat: add success message on new branch creation
- Added new functionality to emit success message upon creating a new branch.
2024-10-15 17:13:41 +08:00
kangfenmao
99f05383cb feat: update translations and add new topic functionality 2024-10-15 16:33:15 +08:00
kangfenmao
5ba6c9f882 feat: add default timestamps for topic updates
- Added default values for createdAt and updatedAt timestamps when updating topics.
2024-10-15 16:19:38 +08:00
kangfenmao
27f64409d6 feat: added drag and drop file upload feature #190
- Added drag and drop file uploading functionality to input bar.

close #190
2024-10-15 16:15:59 +08:00
kangfenmao
7237729ff6 feat: enhanced model search in popup
- Improved search functionality for selecting models in the popup by modifying the filter criteria to include both model and provider names.
2024-10-15 15:55:15 +08:00
kangfenmao
d29cd3c657 feat: improved data display and scrolling experience
- Increased file list pagination size to improve data display.
- Disable inline styles for Markdown content.
- Removed overflow functionality for a smoother scrolling experience.
2024-10-15 15:15:58 +08:00
kangfenmao
8c87f59822 docs: update readme 2024-10-15 15:15:45 +08:00
1355873789
5780141df4 新增:腾讯混元服务商 2024-10-15 01:58:17 +08:00
kangfenmao
f5799ef47b fix: 一次上传多个文件 #183
close #183
2024-10-14 22:52:35 +08:00
kangfenmao
6f502049f4 chore(version): 0.8.0 2024-10-14 14:57:19 +08:00
kangfenmao
c68ad4febb feat: add artifacts preview 2024-10-14 14:37:04 +08:00
kangfenmao
2ebcec9f59 feat: add event listeners and topic handling improvements #181
- Added event listeners for estimated token count and add new topic events.
- Updated default topic handling when clearing messages.
- Removed feature to add new topics directly in Navbar and replaced it with emitting an event to create a new topic.
- Added the functionality to add new topics, clear messages, and handle topic switching with improved conditional logic.
- The event constants configuration has been updated to include two new event names.
2024-10-14 10:39:14 +08:00
kangfenmao
7bc74a5b86 feat: add clear message menu to topic context menu 2024-10-14 10:19:48 +08:00
kangfenmao
75152421d9 fix: DashScope upgrade 2024-10-14 09:57:56 +08:00
1355873789
3326074076 chore: 更新 provider 名称, Dashscope 更新为 Bailian 2024-10-14 09:17:01 +08:00
kangfenmao
362d82bdcc fix: text input token caused stuttering 2024-10-13 00:50:28 +08:00
kangfenmao
fcce241c82 style: improved visual separation and aesthetic
- Added a border radius to scrollbar thumb styles for improved aesthetic.
- Updated the Divider component to include a border for better visual separation.
- Added border to the divider in SelectModelPopup for improved visibility.
2024-10-13 00:38:13 +08:00
kangfenmao
693b06c126 docs: remove uppercase filename docs 2024-10-12 23:24:00 +08:00
kangfenmao
c310c71576 fix: 长文本输入时生成文件后文本依旧保留 #179 2024-10-12 23:22:32 +08:00
kangfenmao
bea95fc52f fix: 使用滚动条显示不全 #176 2024-10-12 17:42:16 +08:00
kangfenmao
969cf8ea21 fix: 移除 input 等输入标签的渲染 2024-10-12 17:37:56 +08:00
kangfenmao
5b357f14e5 chore(version): 0.7.16 2024-10-12 15:31:21 +08:00
kangfenmao
de5db4f805 fix: 修复无法正常选择文本文档的问题 2024-10-12 14:56:17 +08:00
kangfenmao
1ccb5edda7 chore(version): 0.7.15 2024-10-12 14:14:46 +08:00
kangfenmao
97b8749dd1 fix: 一键返回到消息顶部 #166
close #166
2024-10-12 14:03:06 +08:00
kangfenmao
a6d7ecae81 fix: 自定义界面字体 #158 2024-10-12 13:57:45 +08:00
kangfenmao
938efb5aef refactor: renamed model display names and fixed logic
- Renamed the display of model names to show the exact model name instead of capitalized first letter.
- Fixed logic to handle model name retrieval for assistant messages.
- Renaming of model display name to use the model's original name instead of a capitalized version.
- Removed unnecessary import and corrected label formatting in the options array.
2024-10-12 13:52:17 +08:00
kangfenmao
9baf0f772e fix: 黑暗模式的启动页是白色的 #118
close #118
2024-10-12 13:40:34 +08:00
kangfenmao
ff5de3625e fix: o1模型设置使用优化 #172 2024-10-12 13:28:42 +08:00
kangfenmao
f1cfdb29f8 feat: add document files support 2024-10-12 13:18:53 +08:00
kangfenmao
26e48f07fd fix: old version of the backup file cannot be restored. 2024-10-12 10:09:52 +08:00
kangfenmao
8bb5fb9811 feat: add event handling to blur current target element after showing popup
- Added event handling to the onSelectModel function to blur the current target element after showing the SelectModelPopup.
2024-10-12 10:00:03 +08:00
kangfenmao
d41667b599 feat: update release notes and add image preview component
- Updated release notes to reflect changes including image preview and download.
- Added interactive image preview component with toolbar for rotation, zooming, and downloading.
- Added support for image previews in Markdown rendering.
- Added functionality to download files from a URL with automatic filename detection and handling.
2024-10-12 09:53:20 +08:00
kangfenmao
85152cbcd7 chore(version): 0.7.14 2024-10-11 23:22:51 +08:00
kangfenmao
b80863111f feat: update release notes and fix issues
- This commit updates release notes to include new features and fix existing issues.
- Removed non-essential keyboard shortcuts from context menu items.
2024-10-11 18:04:08 +08:00
kangfenmao
6cd88fa51d feat: add bolt minapp 2024-10-11 14:15:37 +08:00
kangfenmao
3619e8f47b style: updated ui styles and translations
- Adjusted padding styles in AssistantModelSettings component.
- The addition of a close button to the Assistant Prompt Settings component to enhance its functionality.
- Added OK callback event to AssistantPromptSettings component.
- Added translations for new UI elements.
- Updated translation data for Chinese language.
- Added new translations and updated existing values in the language file to incorporate additional features.
2024-10-11 14:05:50 +08:00
kangfenmao
5b41dd24d4 refactor: regenerate model on selection
- Updated the logic in the `onSelectModel` function to regenerate the model when a selection is made.
2024-10-11 13:49:06 +08:00
kangfenmao
91dd2f233a fix: azure openai model provider wrong 2024-10-11 13:42:36 +08:00
kangfenmao
7e651f9abc feat: quickly select model 2024-10-11 13:31:14 +08:00
kangfenmao
e44f666c5c refactor: latex解析不支持矩阵环境 #169 2024-10-11 10:15:46 +08:00
kangfenmao
c254b52b51 chore(version): 0.7.13 2024-10-10 09:18:44 +08:00
kangfenmao
37c3a4438f fix: markdown表格目前还没有支持 br 换行 #160
close #160
2024-10-08 21:01:48 +08:00
kangfenmao
302d7511dc feat: add azure openai provider 2024-10-08 20:14:50 +08:00
kangfenmao
68d57ba238 chore(version): 0.7.12 2024-10-06 10:12:46 +08:00
kangfenmao
cf98675223 wip 2024-10-05 19:02:56 +08:00
kangfenmao
4cc140e4f2 feat: add topics history 2024-10-05 17:52:18 +08:00
亢奋猫
2da3a3f010 Update README.md 2024-09-30 22:42:06 +08:00
kangfenmao
fa6f7ecab0 chore(version): 0.7.11 2024-09-30 22:33:58 +08:00
kangfenmao
31ab444300 fix: together ai models 2024-09-30 20:45:51 +08:00
kangfenmao
85453f5a3a fix: webdav backup path 2024-09-30 18:15:15 +08:00
kangfenmao
6d92539524 fix: merge migration versions 2024-09-30 13:37:55 +08:00
牡丹凤凰
a605ae6043 新增:模型服务商together (#148)
* 新增:模型服务商together

新增:模型服务商together
修复:providers为null或undefined时会抛出错误。

* 新增服务商:fireworks、360智脑、英伟达

* 新增:若干模型头像

* 谷歌其他系列模型匹配头像

* 1

* version+
2024-09-30 13:30:09 +08:00
kangfenmao
6aaa6bf042 chore(version): 0.7.10 2024-09-29 23:29:28 +08:00
kangfenmao
aa578194c7 fix: add markdown rendering input msg switcher #143 #142 2024-09-29 23:21:31 +08:00
kangfenmao
220600070c fix: paste long text issue 2024-09-29 22:37:33 +08:00
kangfenmao
32cdfbbfb0 feat: add assistant setting popup 2024-09-29 22:31:07 +08:00
kangfenmao
33b83bf242 feat: add webdav settings component and backup user data files #69 2024-09-29 16:44:18 +08:00
dray
2e1b433365 feat: 添加 WebDAV 配置项
为应用程序添加了 WebDAV 配置项,包括主机、用户、密码和路径。这样用户就可以将备份文件定时上传到 WebDAV 服务器,并从 WebDAV 服务器恢复备份文件。

- 添加了新的依赖项 "webdav": "^5.7.1"
- 修改了 package.json 文件
- 修改了 zh-tw.json、zh-cn.json 和 en-us.json 文件
- 修改了 settings.ts 文件
- 修改了 GeneralSettings.tsx 文件

https://github.com/kangfenmao/cherry-studio/issues/129
2024-09-29 09:27:42 +08:00
kangfenmao
2771a842fe fix: context count 2024-09-28 22:01:09 +08:00
牡丹凤凰
4af3d16e61 Merge pull request #137 from 1355873789/develop
add new app(Felo)
2024-09-28 17:58:00 +08:00
1355873789
eb47fb051b add new app(Felo) 2024-09-28 17:58:30 +08:00
牡丹凤凰
0f9655611b Update models.ts
fix: Confusion between Minimax and Hailuo logos
2024-09-28 11:21:01 +08:00
kangfenmao
0c72ccac12 chore(version): 0.7.9 2024-09-28 00:45:05 +08:00
kangfenmao
09f7fcd2b4 fix: about page minapp logo 2024-09-27 22:44:45 +08:00
kangfenmao
b9250df347 feat: backup all files
1. remove window.api.compress window.api.decompress
2024-09-27 22:35:22 +08:00
kangfenmao
ca897db0d2 fix: minimax hailuo logo 2024-09-27 14:14:10 +08:00
牡丹凤凰
af75d4139c fix: correct display for non-vision GPT-4 models (#135)
* Update models.ts

feat: add matching rules for EMBEDDING_REGEX
fix: correct display for non-vision gpt-4 models

* Update models.ts

feat:add matching rules for gpt-4

* Update models.ts

feat:add matching rules for gpt-4

* Update models.ts

feat:add matching rules for gpt-4
2024-09-27 11:47:03 +08:00
kangfenmao
d2e35a888d refactor: MessageContent component 2024-09-27 00:25:45 +08:00
kangfenmao
fb56c3744b docs: add dev docs 2024-09-27 00:13:24 +08:00
drfyup
26942cfd1f doc: add dev docs (#133)
* Create PR_FAQ.md

* Create Code_DSC.md
2024-09-27 00:04:46 +08:00
kangfenmao
1601fc6d81 fix: 在提问时携带图片会卡住软件 #108 2024-09-27 00:01:35 +08:00
kangfenmao
f543a9ff80 fix: Assistant 的 Prompt 过长时会超出组件 #95
close #95
2024-09-26 23:29:04 +08:00
kangfenmao
5299a2a687 feat: check update settings #131
close #131
2024-09-26 23:17:21 +08:00
kangfenmao
fcc627db6f feat: edit message 2024-09-26 22:45:59 +08:00
kangfenmao
1035019fc2 feat: translate settings persist 2024-09-26 19:15:26 +08:00
kangfenmao
9d311a7261 fix: filter unsupported models 2024-09-26 15:12:47 +08:00
kangfenmao
a973c5fb89 fix: remove filter messages 2024-09-26 14:55:09 +08:00
亢奋猫
be081ccf7a docs: new screenshot 2024-09-26 14:10:14 +08:00
kangfenmao
c25db02acf docs: add sponsor 2024-09-26 13:55:30 +08:00
1355873789
01f98235c6 更新智谱清言APP logo 2024-09-26 08:40:07 +08:00
1355873789
00f3b87215 繁体中文支持 2024-09-26 08:40:07 +08:00
1355873789
849958eeec 模型头像相关
修正部分模型头像错误
新增部分模型头像
2024-09-26 08:40:07 +08:00
牡丹凤凰
9655153e01 Update providers.ts 2024-09-26 03:10:44 +08:00
kangfenmao
74d5355e02 chore(version): 0.7.8 2024-09-25 12:52:42 +08:00
kangfenmao
bb137cc799 feat: auto-scroll to bottom on new messages, return null for empty suggestions
- Added functionality to automatically scroll to the bottom of the messages container upon receiving new messages.
- Return null when suggestions list is empty instead of displaying an empty container.
2024-09-25 12:33:03 +08:00
kangfenmao
6aee3d8088 feat: add hugging chat minapp 2024-09-25 12:09:51 +08:00
kangfenmao
51cedcb644 fix: auto scroll to bottom #120
close #120
2024-09-25 11:36:44 +08:00
kangfenmao
270c754c00 refactor: ant design styles separation 2024-09-25 11:29:38 +08:00
kangfenmao
8f68aca24c feat: add streaming output options #93 2024-09-25 11:23:45 +08:00
kangfenmao
93710c1e78 fix: gemini safety settings #110 2024-09-25 09:49:19 +08:00
kangfenmao
ac2a3fd38e docs(README.md): update readme 2024-09-25 00:26:14 +08:00
kangfenmao
750f1cd63d feat: new providers logos 2024-09-25 00:21:20 +08:00
kangfenmao
4413528d0e feat: add ocoolai provider 2024-09-25 00:21:20 +08:00
kangfenmao
e8b992c289 fix: transparent window 2024-09-25 00:21:20 +08:00
kangfenmao
938ff38aeb fix: windows ico icon 2024-09-25 00:20:10 +08:00
亢奋猫
77cb534e16 Update README.md 2024-09-25 00:20:10 +08:00
亢奋猫
58513b63a3 docs(README.md): add contributors 2024-09-25 00:20:10 +08:00
牡丹凤凰
712e7ff104 docs(README.md): add banners
Delete docs/images/1.png

update
2024-09-25 00:19:52 +08:00
kangfenmao
c68d283766 feat: improved layout and accessibility
- Added hidden overflow to HomePage content container for improved layout.
- Changed the upload list type to text to improve accessibility.
2024-09-25 00:00:34 +08:00
kangfenmao
4d198ff5f1 chore(version): 0.7.7 2024-09-25 00:00:34 +08:00
kangfenmao
10598d430a style: removed background color from agentcard component
- Removed background color from AgentCard component.
2024-09-25 00:00:34 +08:00
kangfenmao
3fbcce3b04 feat: new vi logo 2024-09-25 00:00:34 +08:00
kangfenmao
994dac3af4 fix: agent i18n tranlate 2024-09-25 00:00:34 +08:00
王叔叔
d76e7229fc Update README_zh.md 2024-09-25 00:00:20 +08:00
kangfenmao
26f072fac7 fix: appImage artifactName #112
close #112
2024-09-24 23:59:38 +08:00
kangfenmao
64f96f561b docs: add docs folder 2024-09-24 23:59:38 +08:00
kangfenmao
23c61b8099 fix: content container overflow 2024-09-24 23:59:38 +08:00
kangfenmao
3b06b9474c feat: new model logo 2024-09-24 23:59:38 +08:00
kangfenmao
3d3410b4fd feat: use rounded corner design 2024-09-24 23:59:38 +08:00
kangfenmao
d7f8eec59e feat: fix siliconflow reset api url 2024-09-22 09:24:55 +08:00
kangfenmao
f98879a1e5 chore(version): 0.7.6 2024-09-22 00:44:47 +08:00
kangfenmao
ef40e9db5f style: centered positioning and alignment added to modal and userpopup components
- Added centered positioning to Modal component in PromptPopup.
- Added centered alignment to UserPopup component.
2024-09-22 00:19:45 +08:00
kangfenmao
eb799879ff feat: export topic message as image #103 2024-09-22 00:16:36 +08:00
kangfenmao
13fddc8e7f feat: add loading spinner #86
close #86
2024-09-21 21:07:50 +08:00
kangfenmao
fa3d7f7f4a refactor: message component 2024-09-21 13:20:16 +08:00
kangfenmao
6845ee1664 feat: improved formula rendering with new escaping functions
- Improved formula rendering by removing unnecessary escaping of dollar numbers.
- Implemented two new string escaping functions to prevent incorrect LaTeX formula rendering.

#101
2024-09-21 10:27:32 +08:00
kangfenmao
c8b98681ef fix: After stopping content generation, messages cannot be cleared #66
close #66
2024-09-21 00:25:17 +08:00
kangfenmao
ae4542ce68 feat: improved input bar functionality and added text insertion feature
- Improved functionality for handling text input and file uploads in the input bar.
- Added functionality to insert text at cursor position in a text area.
2024-09-21 00:04:53 +08:00
kangfenmao
0140ff5f6e fix: anthropic api url #97 2024-09-20 23:10:25 +08:00
kangfenmao
a22a47c16a chore(version): 0.7.5 2024-09-20 17:01:52 +08:00
kangfenmao
6bb7b2ca5d feat: improved ui effects and rendering for components
- Added smooth all property transition effect to Icon component.
- Added hover effect and conditional rendering for Switch Topic Sidebar button on current assistant.
- Updated the existing conditional options array to consistently include both topic and settings options.
- Improved hover effects on topic list items.
2024-09-20 16:48:24 +08:00
kangfenmao
1ec7df9a7e feat: add new add topic button 2024-09-20 15:11:50 +08:00
kangfenmao
83925832be fix: attachment open handler 2024-09-20 11:38:30 +08:00
kangfenmao
4dadf98909 fix: improved api call validation
- Improved API call validation to account for additional usage properties.
2024-09-20 11:12:15 +08:00
kangfenmao
375c07e442 style: removed unnecessary import and optimized sidebar styling
- Removed unnecessary import and optimized sidebar styling for improved performance.
2024-09-20 10:49:50 +08:00
kangfenmao
9374541993 chore(version): 0.7.4 2024-09-20 00:15:24 +08:00
kangfenmao
372224469d feat: added minapp event handling and sidebar menu interactions #50
- Added functionality for handling MinApp window closure and provided a default close event handler.
- Added event listeners to Sidebar menus to interact with MinApp.
2024-09-19 23:28:06 +08:00
kangfenmao
60e87e8a22 fix: Disable topic switching and movement during rendering.
- Added functionality to disable topic switching and movement when rendering is in progress.
2024-09-19 23:01:21 +08:00
kangfenmao
353e497642 feat: Improved layout and added file content filtering.
- Added a margin bottom to the Upload component in the MessageAttachments page for improved layout.
- Added support for not displaying file contents for specific providers.
2024-09-19 22:58:12 +08:00
kangfenmao
0ee72a9ef8 chore(version): 0.7.3 2024-09-19 18:20:52 +08:00
kangfenmao
d9873b4261 fix: attachment select extension for windows 2024-09-19 17:40:45 +08:00
kangfenmao
934ab1a374 chore(version): 0.7.2 2024-09-19 16:56:58 +08:00
kangfenmao
33ac0937df feat: Added translations, new column, and UI improvements.
- Added translations for a new field.
- Added new column for file count in the FilesPage view.
- Improved handling of message tokens in the UI.
- Added functionality to display message tokens for messages with specific roles.
- Added window style selection and styling adjustments to the General Settings page.
- Added support for vision models in OpenAIProvider.
2024-09-19 16:56:44 +08:00
kangfenmao
f1c8922752 fix: openai sdk request error 2024-09-19 15:21:24 +08:00
kangfenmao
03bdbdb412 fix: support \(...\) and \[...\] style math formula #78 2024-09-19 15:21:06 +08:00
kangfenmao
cf9d4c5370 feat: add click assistant switch to topics settings 2024-09-19 13:55:44 +08:00
kangfenmao
bfa6bfa196 feat: enhanced user experience with layout adjustments.
- This commit addresses key feature enhancements and minor optimizations for improved user experience and functionality.
- Adjusted margin top for upload container to a positive value.
- Adjusted the max-height of the container to improve rendering on smaller screens.
2024-09-19 12:04:06 +08:00
kangfenmao
af8144d45e feat: Improved file management and added new features.
- Updated file manager to use FileManager class instead of File class.
- Improved file management functionality with features for finding duplicate files, file uploading, and storage management.
- Added styles to wrap and truncate text in a no-drag area.
- Added explicit file extensions to imageExts constant.
- Added the 'paste long text as file' input setting.
- Added image file display and UI improvements for file names and overflow.
- Improved file paste and long text handling functionality.
- awaited onSendMessage function call and added message to chat completion.
- Implemented new option to paste long text as file in the Settings page.
- Updated content display logic to include file origin name along with the file content for text files.
- Improved functionality for handling image and text file contents in the Gemini chat provider.
- Updated file content formatting logic for text files with origin name and content prefix.
- Added a new setting "pasteLongTextAsFile" and its corresponding action to the application settings.
2024-09-19 10:51:30 +08:00
kangfenmao
29605fbcdb feat: copy and paste files or images 2024-09-18 21:18:42 +08:00
kangfenmao
6e7e5cb1f1 feat: add file attachment 2024-09-18 18:00:49 +08:00
kangfenmao
6f5dccd595 feat: estimate completion usage calculation added to chat.
- Estimated usage calculation has been added to chat completion fetching to track message usage.
- Added functionality to estimate completion usage tokens based on input and prompt data.
2024-09-17 14:56:10 +08:00
kangfenmao
0af35b9f10 feat: Added functionality to move topics between assistants.
- Added functionality to move topics between assistants.
- Updated i18n translations to improve user interface clarity and accessibility.
- Improved code organization and functionality to support moving topics between assistants.
2024-09-17 14:37:42 +08:00
kangfenmao
8350ac037e fix: dexie data upgrade 2024-09-16 18:04:46 +08:00
kangfenmao
74b80b474e chore(version): 0.7.1 2024-09-16 16:56:38 +08:00
kangfenmao
be4bf5b510 fix: clear database and restore specific data from backup
- Updated restore function now clears database and restores specific data from backup.
- Removed unused imports and refactored logic for item transformation in the '24' migration step.
2024-09-16 16:44:41 +08:00
kangfenmao
fdb610736d fix: backup and restore 2024-09-16 14:59:42 +08:00
kangfenmao
82e9baf211 fix: Improved user experience by adding timeout to text area resize.
- Added timeout before resizing text area to improve user experience.
- Removed import of the unused `useProviderByAssistant` hook.
2024-09-16 13:03:29 +08:00
kangfenmao
e34d4be6f2 feat: new message branch 2024-09-16 12:56:00 +08:00
kangfenmao
e7f7f8509e feat: add copy button on message footer 2024-09-16 11:51:20 +08:00
kangfenmao
fa1f00f4f5 refactor: add topics and settings table
dexie
2024-09-16 10:19:06 +08:00
kangfenmao
cee373bb6f chore: Update package manager to yarn 4.5.0 and re-add notarize dependency.
- Updated the package manager to yarn version 4.5.0.
- Removed and re-added "electron/notarize" dependency with a specific patch version.
2024-09-15 14:20:58 +08:00
kangfenmao
01acdeb777 feat: added vite_main_bundle_id config and improved code cleanliness 2024-09-15 10:35:02 +08:00
kangfenmao
a654ccc25e chore(version): 0.7.0 2024-09-14 21:28:39 +08:00
kangfenmao
71a35ccd44 fix: removed dev tools, updated sidebar links, fixed file deletion.
- Removed ability to open developer tools in main window.
- Added and removed a link to the "/files" route in the Sidebar component.
- Fixed file deletion logic to correctly delete files from both the database and the file system.
2024-09-14 21:28:39 +08:00
kangfenmao
29826ff091 fix: removed 'trigger' attribute from popover component 2024-09-14 17:22:03 +08:00
kangfenmao
8566476d91 feat: add id to miniapp 2024-09-14 17:02:47 +08:00
kangfenmao
a173a87f29 style: improved formatting in add agent popup.
- Improved formatting of prompt and fetched generated text in Add Agent Popup.
2024-09-14 16:53:22 +08:00
Aimer
cb068d71ca Modified the prompt part Modified the minapp data part 2024-09-14 16:23:58 +08:00
kangfenmao
66210d1d2e fix: remove trailing double spaces from markdown strings 2024-09-14 16:17:35 +08:00
kangfenmao
aa427c9911 refactor: update file management to use filetype instead of filemetadata 2024-09-14 16:08:43 +08:00
kangfenmao
9ae9fdf392 refactor: remove sqlite3 use dexie 2024-09-14 15:25:56 +08:00
kangfenmao
0ddef31ed8 chore: update build process and database configuration.
- Updated configuration to exclude additional directories from electron-builder's build process.
- Dropped the creation of the "files" table in the database schema.
- Improved code organization and extracted the data path into a reusable function.
- Updated database migration configuration to use a new migration manager.
- Added database migration to create a table for file management.
- A migration to remove the "files" table has been applied.
2024-09-13 17:03:26 +08:00
kangfenmao
617af8b12a feat: implemented vision model support and ui enhancements.
- Updated color palette settings have been implemented.
- Added VisionIcon component utilizing Ant Design icons and styled components for visual customization.
- Updated vision model regex to include additional models.
- Added support for multiple file columns in i18n resources.
- Added translations to column titles.
- Added support for vision models in the Select Model Button component.
- Added functionality to display a vision model icon next to the model name on dropdown items.
- Implemented changes to add vision model support to the Edit Models Popup.
- Added icon to display vision models in provider settings.
2024-09-13 15:46:48 +08:00
kangfenmao
71876e6a70 feat: added attachment preview and upload/removal capabilities.
- Added functionality to display attachment preview with upload and removal capabilities.
- Added support for file attachments to the input bar.
2024-09-13 14:47:05 +08:00
kangfenmao
4f250cdcb1 refactor: use sequelize replace better-sqlite3 2024-09-13 13:26:22 +08:00
kangfenmao
9268ab845e fix: Corrected image mime type in IPC message.
- Corrected image mime type in IPC message.
2024-09-13 13:26:22 +08:00
kangfenmao
0337c6649b feat: Added tracking column to files table and updated FileMetadata interface.
- Added a "count" column with default value 1 to the "files" table for tracking purposes.
- Improved file duplication and deletion handling.
- Updated regular expression for vision models to include additional providers.
- Improved removal of topics for assistants from local storage.
- Added support for human-readable date formats in file metadata.
- Improved handling of messages with image attachments to include base64 encoded images in the response.
- Added new 'count' property to the FileMetadata interface.
2024-09-13 13:26:22 +08:00
kangfenmao
8781388760 feat: Improved IPC image handling and added vision model support.
- Improved IPC image handling to return mime type and base64 encoded data alongside the image data.
- Updated type definition for `base64` method in image object to return an object with mime, base64, and data properties.
- Added support for vision models using new function and regex.
- Table cell size has been reduced on the FilesPage component.
- Added support for vision model attachments.
- Added model dependency to AttachmentButton component.
- Implemented new functionality to handle image messages in the GeminiProvider class.
- Update image base64 encoding to directly use API response data.
2024-09-13 13:26:22 +08:00
kangfenmao
2016ba7062 feat: add attachment files 2024-09-13 13:26:22 +08:00
kangfenmao
a03d619e2f feat: add sqlite database manager 2024-09-13 13:26:22 +08:00
kangfenmao
76d1f0bb1e feat: added file management functionality and API operations
- Improved functionality for file management has been added.
- Added file system management functionality through IPC.
- Added functionality to interact with files including selection, upload, deletion, and batch operations.
- Added new file operations to the custom API, including file select, upload, delete, batch upload, and batch delete functions.
- Implemented feature to select and upload files via API.
2024-09-13 13:26:22 +08:00
kangfenmao
2bad5a1184 feat: add file class 2024-09-13 13:26:22 +08:00
kangfenmao
94ba3aee05 feat: add files sidebar menu 2024-09-13 13:26:22 +08:00
kangfenmao
563758f69f refactor: renamed generate method to generateText for clarity and consistency 2024-09-13 10:03:30 +08:00
kangfenmao
56af85cc3e feat: add generate to ai provider api 2024-09-13 09:57:27 +08:00
kangfenmao
6a1a861ecc chore(version): 0.6.14 2024-09-11 20:58:46 +08:00
kangfenmao
ceab574a22 feat: Add new image file and Poe app support.
- Added a new image file.
- Added Poe app to the list of supported apps.
- Removed unused provider configuration.
2024-09-11 20:58:29 +08:00
kangfenmao
98704fdb28 docs: Update translations and UI for better readability.
- Updated English translations in internationalization resources to simplify search assistant placeholder.
- Removed unused import, improved text search UI and adjusted font sizes for better readability.
2024-09-11 19:39:27 +08:00
kangfenmao
fd5cba5219 chore(version): 0.6.13 2024-09-11 19:22:34 +08:00
kangfenmao
be5aaa2b66 feat: Add Cohere model support and binary asset.
- Added new binary asset 'cohere.webp'.
- Added Cohere model support to the application.
2024-09-11 19:19:09 +08:00
kangfenmao
7e8687decd feat: Added GitHub provider support and models.
- Added a new SVG logo for the GitHub provider.
- Added a new social media platform provider to the SYSTEM_MODELS configuration.
- Added support for Github provider in the application configuration.
- Added two new translation keys: 'github' with 'GitHub Models' and updated the existing key 'graphrag-kylin-mountain'.
- Added width parameter to EditModelsPopup configuration.
- Added GitHub-specific model handling to OpenAIProvider class.
- Incremented the application version to 25.
- Added support for a new LLM model type.
- Added a new migration step to configure and enable a GitHub LLM provider.
2024-09-11 19:08:40 +08:00
kangfenmao
4c96324ef7 docs: Update release notes for Electron application.
- Updated release notes for Electron application now include additional features and fixes.
2024-09-11 17:36:37 +08:00
kangfenmao
dd3c81ec5f feat: Enhanced search functionality with user interaction and command shortcuts.
- Improved functionality to search Assistants with enhanced user interaction and command shortcuts.
- Implemented search functionality with runtime state management.
- Added functionality to return default assistant settings and updated conversion of agents to assistants to include default settings.
- Added a new 'searching' boolean field and corresponding state update action to the runtime store.
2024-09-11 17:29:46 +08:00
kangfenmao
42f0b5f8fc feat: Update temperature slider maximum value to 2 #62
- Increased the maximum temperature value in the settings slider.
- Increased the temperature slider maximum value from 1.2 to 2.
2024-09-11 16:24:07 +08:00
kangfenmao
11b2cd88b7 feat: Added configurable Droppable component props to DragableList, updated translations and implemented search functionality.
- Added support for configurable Droppable component props to the DragableList component.
- Updated translations for multiple components and languages.
- Implemented search functionality in the Assistants page.
2024-09-11 16:14:06 +08:00
kangfenmao
6bf98f6db3 fix: Corrected deletions and added API host reset for editable providers.
- Corrected deletions of the 'editable' property for multiple providers.
- Added ability to reset API host for editable providers when not empty.
2024-09-11 15:25:44 +08:00
kangfenmao
10b4e3c634 feat: enable Math support in Markdown rendering.
- Enabled Math support in Markdown rendering without single dollar text math.
2024-09-10 15:31:32 +08:00
kangfenmao
a3f5223b4c fix: disable math formula conversion in Markdown.
- Disabled math formula conversion in Markdown rendering.
2024-09-10 15:25:18 +08:00
kangfenmao
2855575b36 style: Refine UI styles and layout.
- Adjusted various font and layout styles to refine the user interface.
- Updated the minimum width of the NavbarRightContainer to match the var(--topic-list-width) setting.
- Added logic to synchronize local _activeTopic with activeTopic state.
- Improve logic for dynamically updating tab state in RightSidebar component based on position and topic settings.
- Removed unneeded console statement from font size slider's onChangeComplete event.
- Adjusted the width of the SettingMenus component to utilize the --settings-width variable.
2024-09-10 15:20:59 +08:00
kangfenmao
1f0ba20523 feat: Added platform-specific functionality to GeneralSettings page.
- Added platform-specific functionality to GeneralSettings page.
2024-09-10 13:52:50 +08:00
kangfenmao
2f53416e09 docs: Update agent-related translations to use 'assistant' term.
- "All agent-related translations have been updated to use the term 'assistant' instead of 'agent'."
2024-09-10 13:51:47 +08:00
kangfenmao
ddbf266a3f style: Updated component styles and layouts.
- Added new styles for the business smart assistant icon.
- Adjusted the sizes and positions of the ArrowRightButton components.
- Removed conditional style for NavbarLeft component.
- Implemented logic to resolve tab initialization based on component position.
2024-09-10 13:50:20 +08:00
kangfenmao
d815415f36 style: Adjusted layout and styling of right sidebar.
- Modified color border variable to a lighter grayish white.
- Adjusted the layout and styling of the right sidebar.
2024-09-10 13:28:34 +08:00
kangfenmao
cdacc56fd7 chore(version): 0.6.12 2024-09-09 17:34:30 +08:00
kangfenmao
455d909c74 style: Centered buttons and modals.
- Added the centered property to the OK button on the AgentsPage.
- Added centered option to modal confirmation dialog.
- Centred the delete button in the ProvidersList component.
- Added centered confirmation to reset modal.
2024-09-09 17:16:14 +08:00
kangfenmao
52d84afed6 feat: Update release notes with new features and bug fixes. 2024-09-09 17:01:02 +08:00
kangfenmao
f06d1d4d9a style: Centered layout updates across components.
- Centered the 'Add Assistant' popup in the chat modal.
- Added centered alignment to the AssistantSettingPopup component.
- The text area prompt input field now has a larger height.
- Updated the positioning of the Manage Agents popup to be centered.
- Added a centered attribute to the AddModelPopup modal footer.
- Added centered positioning to ProviderSettings AddProviderPopup.
- Centered layout has been added to the SearchContainer.
2024-09-09 16:57:20 +08:00
kangfenmao
805a65bbaa Revert "refactor: Migrate DeepSeek models to v2 naming convention"
This reverts commit 9ff65441ef.
2024-09-09 16:33:29 +08:00
kangfenmao
f217950b13 style: Adjusted dropdown menu maxHeight to 55vh. #52
- Adjusted the maxHeight property of the dropdown menu to 55vh from 80vh.
2024-09-09 13:03:51 +08:00
kangfenmao
9ff65441ef refactor: Migrate DeepSeek models to v2 naming convention
- Updated DeepSeek models to use version 2 naming convention.
2024-09-09 11:58:18 +08:00
kangfenmao
2b20282a41 feat: Add Zhihu app support and image asset.
- A new image file 'zhihu.png' has been added.
- Added support for Zhihu app in the minapp configuration.
2024-09-09 11:20:02 +08:00
kangfenmao
96ad2de896 chore(version): 0.6.11 2024-09-08 22:59:12 +08:00
kangfenmao
e1ea875c21 feat: Add list styling and optimize DragableList component
- Added list styling functionality to the DragableList component.
- Removed unused imports and updated container height to accommodate additional content.
2024-09-08 22:55:58 +08:00
kangfenmao
500e91977c feat: Show all topics on drag start
- Enforce the drag and drop functionality to show all topics on drag start.
2024-09-08 22:35:34 +08:00
kangfenmao
bd194ff955 refactor: Simplify import and topic deletion logic
- Updated import statement to remove unused type reference.
- Improved handling of deleting a topic.
2024-09-08 22:25:56 +08:00
kangfenmao
828bd71f22 feat: Remove activeAssistant dependency, add assistant dependency
- Updated the `onEditAssistant` function to remove dependency on `activeAssistant` variable and add `assistant` as a dependency.
2024-09-08 20:57:49 +08:00
kangfenmao
5991f692b2 feat: Edit assistant settings with real-time sync.
- Added support for editing an assistant's settings with real-time synchronization to the agent.
2024-09-08 16:09:17 +08:00
kangfenmao
200d78a140 feat: Enhanced UI/UX with design updates, i18n, and feature enhancements.
- Updated design styles for segmented tabs and size adjustments for assistive elements.
- Added internationalization translations for English and Chinese.
- Removed unused import and functionality for switching topics sidebar.
- Added functionality to hide or show the right sidebar in the Chat page.
- Renamed Assistants component to RightSidebar.
- Improved functionality for showing and toggling topics and settings in the input bar.
- Removed unused imports and refactored Navbar component layout.
- Updated existing right sidebar functionality to allow for custom position and show topic settings.
- Removed inline styles for width from Settings component Container styles.
- Added new features for managing topics in the home page, including drag and drop functionality, a "show all" button for viewing more topics, and improved handling of large topic lists.
2024-09-08 15:56:16 +08:00
kangfenmao
9a502b5e47 refactor: Improve code reusability and model service logic
- Improved code reusability in ModelSettings component by utilizing the hasModel function and Memoization.
- Refactored model service to include logic for checking if a model exists and retrieving its unique ID.
2024-09-08 10:13:15 +08:00
kangfenmao
97ef3772ea chore(version): 0.6.10 2024-09-07 18:21:30 +08:00
kangfenmao
eb18be200e feat: Improved UI components and added new features
- Replaced 'CopyOutlined' icon with custom 'CopyIcon'.
- Replaced Topics component with RightSidebar component to match topicPosition settings.
- Removed unused imports and updated UI components in the Inputbar.
- Implemented a new Token Count component for displaying context and estimated token information in the input bar.
- Adjusted the height of code block header.
- Added functionality to toggle theme opacity.
- Added functionality to dynamically change the sidebar border style based on stored settings.
- Updated CSS styles for dynamic topic list width and padding adjustments.
- Removed unused import and styles to improve code efficiency and reduce clutter.
2024-09-07 18:11:27 +08:00
kangfenmao
467e97ff4b feat: Improved model selection and unique id generation
- Improved dropdown menu selection logic for models.
- Changes improve ModelSettings component to use getModelUniqId function for model identifiers.
- Added modeling service functionality to generate unique model identifiers.
2024-09-07 18:11:13 +08:00
kangfenmao
27b802d3c2 chore(version): 0.6.9 2024-09-06 18:04:11 +08:00
kangfenmao
37b0a175f7 feat: Add theme switching to Navbar
- Added a new theme switching functionality to the Navbar.
2024-09-06 18:03:06 +08:00
kangfenmao
b2b79f12a2 feat: Enhanced code block styling in Markdown editor
- Added styles for code blocks in markdown to match the application's design.
- Improved the rendering of code blocks in the Markdown editor by adding a border and changing the default display in dark mode.
2024-09-06 17:58:15 +08:00
kangfenmao
885c578582 chore(version): 0.6.8 2024-09-06 15:54:44 +08:00
kangfenmao
e61e4b109a refactor: Remove unused CSS classes and optimize conditional styling
- Removed unused CSS classes and optimized code for conditional styling.
2024-09-06 15:53:58 +08:00
kangfenmao
f3bafbeb52 feat: Update UI components and styling for consistency and readability.
- Updated icon font asset reference URL to reflect a new timestamp.
- Updated icon-fonts file asset.
- Updated markdown styling to adjust margins and padding of pre-formatted text elements.
- Added Windows-specific styling to the Inputbar component.
- Improved the rendering of code blocks with a focus on readability and theming consistency.
- Added new 'plain' attribute to Divider component for 'clear' message type.
- Minor adjustments made to the navigation bar styles and layout.
2024-09-06 15:41:46 +08:00
kangfenmao
e55c0cdcef feat: Update context count logic
- Updated logic for determining context count based on clear messages.
2024-09-06 14:17:22 +08:00
kangfenmao
e73bbf4d6a style: Update toolbar button hover and active states
- Updated styles and icons for hover and active states of toolbar buttons.
2024-09-06 14:12:01 +08:00
kangfenmao
3859289218 style: Update styling and input bar characters.
- Updated styling and characters added to input bar.
2024-09-06 14:07:45 +08:00
kangfenmao
591bb45a4e feat: Improved chat UI with context handling and filtering #43
- Updated default context count from 5 to 6.
- Updated string translations for multiple languages.
- Added functionality to handle new context and update context count in Inputbar component.
- Added support for displaying new chat context divider for 'clear' type messages.
- Added functionality to emit estimated token count with context count when the estimated token count event is triggered.
- Improved filtering and processing of user messages for the AnthropicProvider class.
- Updated message filtering logic with context consideration.
- Improved filtering of user messages to include only context-relevant messages.
- Updated logic to pass messages directly to AI.completions and AI.suggestions API requests instead of filtered messages.
- Added new event names for handling topic sidebar and context switching.
- Improved handling of message filtering and context counting.
- Added new valid value 'clear' to type option in Message type.
2024-09-06 13:54:48 +08:00
kangfenmao
b31f518fca fix: Handle Enter key press event in input field
- Updated handling for Enter key press event in input field to match shortcut settings.
2024-09-06 11:34:55 +08:00
kangfenmao
dfbdb989db feat: Update icon font and navigation buttons
- Updated icon font references and added new icon font glyphs.
- Updated icon font file for improved rendering.
- Updated icon font sizes and hover animations for navigation buttons.
- Removed border styles from styled Container component.
- Removed unused import and updated icon for '/settings/model' menu item.
2024-09-06 10:00:18 +08:00
kangfenmao
f194ebbc20 chore(version): 0.6.7 2024-09-05 23:53:47 +08:00
kangfenmao
ab0e7e1e07 feat: change topics position 2024-09-05 23:53:47 +08:00
kangfenmao
d809f50c0e feat: Update Content-Security-Policy to allow file: frame-src #38
- Updated Content-Security-Policy directive to allow frame-src from file: in the HTML document.
2024-09-05 17:19:17 +08:00
kangfenmao
a48d24de26 refactor: renamed and refactored topic properties and added date-time tracking
- Renamed localforage topic item property from topic object to id.
- Added date-time tracking for assistant topics.
- Incremented the store version to 24.
- Refactored migrate function to add support for local storage and update topics timestamps.
- Added createdAt and updatedAt properties to Topic type.
2024-09-05 16:15:48 +08:00
kangfenmao
0dacc20e74 docs(DragableList): improve types and props documentation for DragDropContext responders 2024-09-05 15:30:26 +08:00
kangfenmao
08df6cb4f8 feat: highlight acitve topic icon 2024-09-05 14:36:19 +08:00
kangfenmao
0676ac8942 feat: quickly edit the asistant on edit title #42 2024-09-05 13:41:47 +08:00
kangfenmao
c257e8f0fe fix: anthropic first message must use the user role #39
{"type":"error","error":{"type":"invalid_request_error","message":"messages: first message must use the "user" role"}}
2024-09-05 13:35:16 +08:00
kangfenmao
521670f683 fix: assistant and topic list style 2024-09-05 00:04:35 +08:00
kangfenmao
87216b5d91 chore(version): 0.6.6 2024-09-04 22:33:15 +08:00
kangfenmao
e6122a3d36 fix: left sidebar icon 2024-09-04 22:31:39 +08:00
kangfenmao
e6e1502308 feat: remove hashtag title 2024-09-04 21:57:23 +08:00
kangfenmao
7f5be3a688 chore(version): 0.6.5 2024-09-04 21:29:56 +08:00
kangfenmao
4dde49a9f0 feat: new chat style 2024-09-04 21:29:16 +08:00
kangfenmao
ce830b692b revert: fold topics 2024-09-04 15:37:39 +08:00
kangfenmao
563472f3a9 wip 2024-09-04 13:26:51 +08:00
kangfenmao
14acd45927 feat: transparent window settings 2024-09-04 11:23:45 +08:00
kangfenmao
9e2c7a08df feat: change assistant sidebar width 2024-09-03 23:37:40 +08:00
kangfenmao
f10c8dc379 chore(version): 0.6.4 2024-09-03 22:14:12 +08:00
kangfenmao
fdd815879a feat: double click to change assistat view 2024-09-03 22:13:25 +08:00
kangfenmao
635f238576 chore(version): 0.6.3 2024-09-03 20:50:46 +08:00
kangfenmao
615e337e3f fix: assistant nav style 2024-09-03 20:50:37 +08:00
kangfenmao
acd5d4b192 feat: change default avatar 2024-09-03 20:39:27 +08:00
kangfenmao
9a41b697c6 fix: inputbar height 2024-09-03 20:11:25 +08:00
kangfenmao
5cb67e00a6 feat: change default provider 2024-09-03 20:11:20 +08:00
kangfenmao
350f13e97c fix: backup and restore i18n 2024-09-03 19:30:21 +08:00
kangfenmao
4d6cbf5073 refactor: provider sdk 2024-09-03 19:00:24 +08:00
kangfenmao
8d7b10d21e refactor: remove modal enabled key 2024-09-03 13:17:55 +08:00
kangfenmao
6753a93c0d fix: use webview replace iframe 2024-09-03 13:17:38 +08:00
kangfenmao
9ee763337d refactor: remove models config enabled 2024-09-03 11:40:46 +08:00
kangfenmao
ace0cb7823 feat: merge assistant and topics 2024-09-03 11:36:57 +08:00
kangfenmao
44e518ef03 refactor: assistant drap and drop 2024-09-02 20:48:31 +08:00
kangfenmao
e28b96b45e feat: expand inputbar height 2024-09-02 15:38:48 +08:00
kangfenmao
11427a980c feat: auto change inputbar height 2024-09-02 14:09:03 +08:00
kangfenmao
cb95562e58 feat: add attachment button 2024-09-01 23:22:21 +08:00
kangfenmao
89bdab58f7 feat: hide entry for local ai 2024-08-28 18:11:35 +08:00
kangfenmao
d42ee59335 fix: https://github.com/electron/notarize/issues/193 2024-08-27 19:42:39 +08:00
kangfenmao
88e7ab211d fix: electron-builder files path 2024-08-27 19:42:32 +08:00
kangfenmao
5347bdfa83 refactor: change env file path 2024-08-27 11:58:19 +08:00
kangfenmao
c8711c5804 feat: add local module 2024-08-27 11:31:05 +08:00
kangfenmao
24cf3bb043 chore(version): 0.6.2 2024-08-26 18:30:05 +08:00
kangfenmao
0531ecf3cf fix: electron builder ignore files 2024-08-26 18:19:01 +08:00
kangfenmao
0cbfd26883 build: remove sentry 2024-08-26 18:06:07 +08:00
kangfenmao
ee398489de build: remove electron-devtools-installer 2024-08-26 18:02:20 +08:00
kangfenmao
71d7c2c738 fix: workspace config 2024-08-26 17:49:19 +08:00
kangfenmao
b98f7298a2 build: add yarn workspace config 2024-08-25 22:12:31 +08:00
kangfenmao
de4f2599be refactor: remove unnecessary logs 2024-08-25 21:37:13 +08:00
kangfenmao
93b32e8e21 feat: update user data path 2024-08-25 18:39:53 +08:00
kangfenmao
e353d0f8ee fix: default assistant name 2024-08-23 21:41:16 +08:00
kangfenmao
dfd42fe9a6 feat: add devv referral code 2024-08-23 20:57:54 +08:00
kangfenmao
a2dc325896 chore(version): 0.6.1 2024-08-22 19:17:35 +08:00
kangfenmao
b131d320ea feat: more ai minapp 2024-08-22 18:45:06 +08:00
kangfenmao
b88f4a869e wip 2024-08-22 16:36:04 +08:00
kangfenmao
461458e5ec refactor: remove minapp.html 2024-08-22 13:04:24 +08:00
kangfenmao
4c2014f1d6 chore(version): 0.6.0 2024-08-21 10:28:31 +08:00
kangfenmao
647dd3e751 feat: add minapps 2024-08-21 10:14:04 +08:00
kangfenmao
4225312d4a chore(version): 0.5.9 2024-08-20 13:42:50 +08:00
kangfenmao
c2a4613e32 fix: windows minapp control button 2024-08-18 23:37:09 +08:00
kangfenmao
5d5c1eee74 feat: change sidebar width 2024-08-18 22:20:09 +08:00
kangfenmao
c1b5e6b183 feat: new input status bar style 2024-08-18 20:44:55 +08:00
kangfenmao
fd37ba18dc chore(version): 0.5.8 2024-08-18 18:06:56 +08:00
kangfenmao
4a26f7ce78 feat: add minimax provider 2024-08-18 18:06:21 +08:00
kangfenmao
8b38ebcac4 fix: hmr recycle 2024-08-18 17:10:59 +08:00
kangfenmao
e8dac28787 fix: graph rag model id 2024-08-17 21:54:34 +08:00
kangfenmao
3ccebb503f fix: input text 2024-08-17 21:30:28 +08:00
kangfenmao
42327836de fix: graphrag node url 2024-08-17 21:30:04 +08:00
kangfenmao
4d7a3bb8c3 feat: add minapp window 2024-08-17 17:11:48 +08:00
kangfenmao
1996e163c9 feat: add minapp window 2024-08-17 13:30:54 +08:00
kangfenmao
e43f7f87ab feat: window.app add app path 2024-08-16 22:44:00 +08:00
kangfenmao
47a83fa67f fix: minapp title null 2024-08-16 22:43:18 +08:00
kangfenmao
5e954566c9 chore(version): 0.5.7 2024-08-16 17:41:30 +08:00
kangfenmao
b8960ef02c fix: windows frame background color 2024-08-16 17:41:14 +08:00
kangfenmao
1866b00265 feat: add user edit modal & add prompt block 2024-08-16 17:19:18 +08:00
kangfenmao
be0799a4c6 chore(version): 0.5.6 2024-08-14 21:32:14 +08:00
kangfenmao
d0f5547419 feat: new windows and linux sidebar style 2024-08-14 21:28:44 +08:00
kangfenmao
076011b02b fix: anthropic message generating error 2024-08-14 20:35:57 +08:00
kangfenmao
ba5c70c45a feat: add minapp popup 2024-08-14 19:47:58 +08:00
kangfenmao
ebe74ffd05 chore(version): 0.5.5 2024-08-13 21:10:04 +08:00
kangfenmao
d0bea0491f fix(settings): provider list scroll 2024-08-13 21:04:04 +08:00
kangfenmao
514e1a4796 chore: remove ahooks 2024-08-13 20:50:54 +08:00
kangfenmao
2ffedadee4 Revert "feat(translate): use full screen input"
This reverts commit b0c479190c.
2024-08-13 20:48:51 +08:00
kangfenmao
7b72783ae7 feat: add graphrag provider 2024-08-13 20:48:38 +08:00
kangfenmao
4485a00395 feat: add doubao provider 2024-08-13 19:41:01 +08:00
kangfenmao
77c0952635 feat: add stepfun provider 2024-08-13 18:02:00 +08:00
kangfenmao
e1c7a25b87 feat: add gemini provider 2024-08-13 16:51:52 +08:00
kangfenmao
b0c479190c feat(translate): use full screen input 2024-08-13 14:57:46 +08:00
kangfenmao
c7c3d28893 chore(version): 0.5.4 2024-08-12 22:37:09 +08:00
kangfenmao
994ee8d7df feat: change sidebar opacity 2024-08-12 22:35:35 +08:00
kangfenmao
57f9550891 feat: add font size options to assistant settings pannel 2024-08-12 22:21:47 +08:00
kangfenmao
0c0d1560db feat: about page add icons 2024-08-12 22:03:16 +08:00
kangfenmao
145d7ee748 refactor: slider onChange event 2024-08-12 21:48:59 +08:00
kangfenmao
52af23b931 feat: enable anthropic api host edit 2024-08-12 21:31:32 +08:00
kangfenmao
f7151bd066 feat: add change message font size feature #22
支持消息字体大小调节
2024-08-12 21:28:18 +08:00
kangfenmao
744a1fedba style(Inputbar): add width: auto to Textarea 2024-08-11 16:18:06 +08:00
kangfenmao
978432d910 fix: clear topic white generating 2024-08-11 16:11:31 +08:00
kangfenmao
b6cb1e4d84 refactor: code format 2024-08-11 15:49:08 +08:00
kangfenmao
0096783f26 chore(version): 0.5.3 2024-08-09 18:58:58 +08:00
kangfenmao
4fc53d7c19 feat: new inputbar style 2024-08-09 18:56:45 +08:00
亢奋猫
34d99b711c Update README.md 2024-08-09 11:30:56 +08:00
kangfenmao
5dd74a1018 chore(version): 0.5.2 2024-08-08 23:53:18 +08:00
kangfenmao
e028d0600f fix: windows style 2024-08-08 23:30:55 +08:00
kangfenmao
64ee3f2108 chore(version): 0.5.1 2024-08-08 18:13:47 +08:00
kangfenmao
30a082b979 fix: filter empty user messages 2024-08-08 18:13:15 +08:00
kangfenmao
5a0927393d feat(message): add error tips 2024-08-08 17:57:57 +08:00
kangfenmao
16c68dcdcb fix: inputbar height 2024-08-08 17:21:00 +08:00
kangfenmao
b6500977b0 fix: model settings crash 2024-08-08 17:16:45 +08:00
kangfenmao
78cf33e8bc feat(AssistantSettings.tsx): fix reset functionality 2024-08-08 16:49:18 +08:00
kangfenmao
2f62f04adf feat(ModelSettings.tsx): sorting model names and capitalizing first letter 2024-08-08 16:36:36 +08:00
kangfenmao
84915b1ede feat(AboutSettings.tsx): add GithubOutlined icon linking to project repository for better user navigation and project visibility 2024-08-08 16:12:42 +08:00
亢奋猫
248c7ea20e Update README.md 2024-08-08 15:43:01 +08:00
kangfenmao
1031d40ddb docs(README.md): add star history chart for project visibility 2024-08-08 15:38:31 +08:00
kangfenmao
3d44fc2208 fix: navbar style on linux 2024-08-08 14:50:36 +08:00
kangfenmao
22e3c0e270 build: add linux target 2024-08-08 11:31:15 +08:00
kangfenmao
5d81874166 fix(i18n): update default assistant emoji from 😀 to 🔆 2024-08-08 09:18:33 +08:00
kangfenmao
f7ef895ce6 chore(version): 0.5.0 2024-08-07 21:55:51 +08:00
kangfenmao
beb40f5baf feat: fix add assistant search keywords format 2024-08-07 20:57:31 +08:00
kangfenmao
07613e65f5 feat: add max token limit #18 2024-08-07 20:49:21 +08:00
kangfenmao
6185068353 feat: use ubuntu font as default 2024-08-07 14:28:29 +08:00
kangfenmao
61934cd65c feat add agent popup #14 2024-08-07 13:23:29 +08:00
625 changed files with 61280 additions and 9451 deletions

View File

@@ -6,4 +6,4 @@ indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
trim_trailing_whitespace = true

View File

@@ -2,4 +2,4 @@ node_modules
dist
out
.gitignore
scripts/cloudflare-worker.js

View File

@@ -15,6 +15,8 @@ module.exports = {
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'react/prop-types': 'off',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error'
'simple-import-sort/exports': 'error',
'react/no-is-mounted': 'off',
'prettier/prettier': ['error', { endOfLine: 'auto' }]
}
}

90
.github/ISSUE_TEMPLATE/0_bug_report.yml vendored Normal file
View File

@@ -0,0 +1,90 @@
name: 🐛 Bug Report
description: Create a report to help us improve
title: '[Bug]: '
labels: ['bug']
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out this bug report!
Before submitting this issue, please make sure that you have understood the [FAQ](https://docs.cherry-ai.com/question-contact/questions) and [Knowledge Science](https://docs.cherry-ai.com/question-contact/knowledge)
- type: checkboxes
id: checklist
attributes:
label: Issue Checklist
description: |
Before submitting an issue, please make sure you have completed the following steps
options:
- label: I understand that issues are for feedback and problem solving, not for complaining in the comment section, and will provide as much information as possible to help solve the problem.
required: true
- label: I've looked at pinned issues and searched for existing [Open Issues](https://github.com/CherryHQ/cherry-studio/issues), [Closed Issues](https://github.com/CherryHQ/cherry-studio/issues?q=is%3Aissue%20state%3Aclosed), and [Discussions](https://github.com/CherryHQ/cherry-studio/discussions), no similar issue or discussion was found.
required: true
- label: I've filled in short, clear headings so that developers can quickly identify a rough idea of what to expect when flipping through the list of issues. And not "a suggestion", "stuck", etc.
required: true
- type: dropdown
id: platform
attributes:
label: Platform
description: What platform are you using?
options:
- Windows
- macOS
- Linux
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: What version of Cherry Studio are you running?
placeholder: e.g. v1.0.0
validations:
required: true
- type: textarea
id: description
attributes:
label: Bug Description
description: Please be as detailed as possible when describing the problem. Please provide screenshots or screen recordings whenever possible to help us better understand the issue.
placeholder: Tell us what happened... (Remember to attach screenshots/recordings if applicable)
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps To Reproduce
description: Provide detailed steps to reproduce the issue so that our developers can reproduce the issue accurately. Please include screenshots or screen recordings for each step when possible.
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
Remember to attach screenshots/recordings for each step when possible!
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant Log Output
description: Please copy and paste any relevant log output
render: shell
- type: textarea
id: additional
attributes:
label: Additional Context
description: Anything that gives us a better understanding of the problem you're experiencing

View File

@@ -0,0 +1,76 @@
name: 💡 Feature Request
description: Suggest an idea for this project
title: '[Feature]: '
labels: ['enhancement']
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to submit a feature request!
Before submitting this issue, please make sure you have reviewed the [Project Roadmap](https://docs.cherry-ai.com/cherrystudio/planning) and the [Feature Overview](https://docs.cherry-ai.com/cherrystudio/preview).
- type: checkboxes
id: checklist
attributes:
label: Issue Checklist
description: |
Before submitting an issue, please make sure you have completed the following steps
options:
- label: I understand that issues are for reporting problems and requesting features, not for off-topic comments, and I will provide as much detail as possible to help resolve the issue.
required: true
- label: I have checked the pinned issues and searched through the existing [open issues](https://github.com/CherryHQ/cherry-studio/issues), [closed issues](https://github.com/CherryHQ/cherry-studio/issues?q=is%3Aissue%20state%3Aclosed), and [discussions](https://github.com/CherryHQ/cherry-studio/discussions) and did not find a similar suggestion.
required: true
- label: I have provided a short and descriptive title so that developers can quickly understand the issue when browsing the issue list, rather than vague titles like "A suggestion" or "Stuck."
required: true
- label: The latest version of Cherry Studio does not include the feature I am suggesting.
required: true
- type: dropdown
id: platform
attributes:
label: Platform
description: What platform are you using?
options:
- Windows
- macOS
- Linux
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: What version of Cherry Studio are you running?
placeholder: e.g. v1.0.0
validations:
required: true
- type: textarea
id: problem
attributes:
label: Is your feature request related to an existing issue?
description: Please briefly describe the problem you are experiencing. If possible, include screenshots or recordings to help illustrate the current situation or pain points.
placeholder: I often feel frustrated because... (Remember to attach screenshots/recordings if applicable)
validations:
required: true
- type: textarea
id: solution
attributes:
label: Desired Solution
description: Please briefly describe what you would like to happen. You can include mockups, screenshots, or screen recordings to better illustrate your proposed solution.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternative Solutions
description: Please briefly describe any alternative solutions or features you have considered. Feel free to include screenshots or mockups of alternative approaches.
- type: textarea
id: additional
attributes:
label: Additional Information
description: Add any other context, screenshots, mockups or recordings that can help us better understand your feature request.

79
.github/ISSUE_TEMPLATE/2_question.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: ❓ Discussion & Questions
description: Seeking help, discussing issues, asking questions, etc...
title: '[Discussion]: '
labels: ['question']
body:
- type: markdown
attributes:
value: |
Thank you for your question! Please describe your issue in as much detail as possible so that we can better assist you.
- type: checkboxes
id: checklist
attributes:
label: Issue Checklist
description: |
Before submitting an issue, please make sure you have completed the following steps
options:
- label: I understand that issues are meant for feedback and problem-solving, not for venting, and I will provide as much detail as possible to help resolve the issue.
required: true
- label: I have checked the pinned issues and searched through the existing [open issues](https://github.com/CherryHQ/cherry-studio/issues), [closed issues](https://github.com/CherryHQ/cherry-studio/issues?q=is%3Aissue%20state%3Aclosed), and [discussions](https://github.com/CherryHQ/cherry-studio/discussions) and did not find a similar suggestion.
required: true
- label: I confirm that I am here to ask questions and discuss issues, not to report bugs or request features.
required: true
- type: dropdown
id: platform
attributes:
label: Platform
description: What platform are you using?
options:
- Windows
- macOS
- Linux
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: What version of Cherry Studio are you running?
placeholder: e.g. v1.0.0
validations:
required: true
- type: textarea
id: question
attributes:
label: Your Question
description: Please describe your issue in detail. Include screenshots or screen recordings whenever possible to help us better understand your question.
placeholder: Please explain your issue as clearly as possible...(Remember to attach screenshots/recordings if applicable)
validations:
required: true
- type: textarea
id: context
attributes:
label: Context
description: Please provide some background information to help us better understand your question. Screenshots or recordings of your current setup or situation can be very helpful.
placeholder: "For example: use case, solutions you've tried, etc. Don't forget to include relevant screenshots/recordings!"
- type: textarea
id: additional
attributes:
label: Additional Information
description: Any other relevant information, screenshots, recordings, or code examples that can help us better assist you
render: shell
- type: dropdown
id: priority
attributes:
label: Priority
description: How urgent is this issue for you?
options:
- Low (Review when available)
- Medium (Would like a response soon)
- High (Blocking progress)
validations:
required: true

88
.github/issues/#0_bug_report.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: 🐛 错误报告 (中文)
description: 创建一个报告以帮助我们改进
title: '[错误]: '
labels: ['bug']
body:
- type: markdown
attributes:
value: |
感谢您花时间填写此错误报告!
在提交此问题之前,请确保您已经了解了[常见问题](https://docs.cherry-ai.com/question-contact/questions)和[知识科普](https://docs.cherry-ai.com/question-contact/knowledge)
- type: checkboxes
id: checklist
attributes:
label: 提交前检查
description: |
在提交 Issue 前请确保您已经完成了以下所有步骤
options:
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决。
required: true
- label: 我已经查看了置顶 Issue 并搜索了现有的 [开放Issue](https://github.com/CherryHQ/cherry-studio/issues)和[已关闭Issue](https://github.com/CherryHQ/cherry-studio/issues?q=is%3Aissue%20state%3Aclosed%20),没有找到类似的问题。
required: true
- label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等。
required: true
- type: dropdown
id: platform
attributes:
label: 平台
description: 您正在使用哪个平台?
options:
- Windows
- macOS
- Linux
validations:
required: true
- type: input
id: version
attributes:
label: 版本
description: 您正在运行的 Cherry Studio 版本是什么?
placeholder: 例如 v1.0.0
validations:
required: true
- type: textarea
id: description
attributes:
label: 错误描述
description: 描述问题时请尽可能详细
placeholder: 告诉我们发生了什么...
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: 重现步骤
description: 提供详细的重现步骤,以便于我们可以准确地重现问题
placeholder: |
1. 转到 '...'
2. 点击 '....'
3. 向下滚动到 '....'
4. 看到错误
validations:
required: true
- type: textarea
id: expected
attributes:
label: 预期行为
description: 清晰简洁地描述您期望发生的事情
validations:
required: true
- type: textarea
id: logs
attributes:
label: 相关日志输出
description: 请复制并粘贴任何相关的日志输出
render: shell
- type: textarea
id: additional
attributes:
label: 附加信息
description: 任何能让我们对你所遇到的问题有更多了解的东西

76
.github/issues/#1_feature_request.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: 💡 功能建议 (中文)
description: 为项目提出新的想法
title: '[功能]: '
labels: ['enhancement']
body:
- type: markdown
attributes:
value: |
感谢您花时间提出新的功能建议!
在提交此问题之前,请确保您已经了解了[项目规划](https://docs.cherry-ai.com/cherrystudio/planning)和[功能介绍](https://docs.cherry-ai.com/cherrystudio/preview)
- type: checkboxes
id: checklist
attributes:
label: 提交前检查
description: |
在提交 Issue 前请确保您已经完成了以下所有步骤
options:
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决。
required: true
- label: 我已经查看了置顶 Issue 并搜索了现有的 [开放Issue](https://github.com/CherryHQ/cherry-studio/issues)和[已关闭Issue](https://github.com/CherryHQ/cherry-studio/issues?q=is%3Aissue%20state%3Aclosed%20),没有找到类似的建议。
required: true
- label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等。
required: true
- label: 最新的 Cherry Studio 版本没有实现我所提出的功能。
required: true
- type: dropdown
id: platform
attributes:
label: 平台
description: 您正在使用哪个平台?
options:
- Windows
- macOS
- Linux
validations:
required: true
- type: input
id: version
attributes:
label: 版本
description: 您正在运行的 Cherry Studio 版本是什么?
placeholder: 例如 v1.0.0
validations:
required: true
- type: textarea
id: problem
attributes:
label: 您的功能建议是否与某个问题/issue相关?
description: 请简明扼要地描述您遇到的问题
placeholder: 我总是感到沮丧,因为...
validations:
required: true
- type: textarea
id: solution
attributes:
label: 请描述您希望实现的解决方案
description: 请简明扼要地描述您希望发生的情况
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: 请描述您考虑过的其他方案
description: 请简明扼要地描述您考虑过的任何其他解决方案或功能
- type: textarea
id: additional
attributes:
label: 其他补充信息
description: 在此添加任何其他与功能建议相关的上下文或截图

77
.github/issues/#2_question.yml vendored Normal file
View File

@@ -0,0 +1,77 @@
name: ❓ 讨论 & 提问 (中文)
description: 寻求帮助、讨论问题、提出疑问等...
title: '[讨论]: '
labels: ['question']
body:
- type: markdown
attributes:
value: |
感谢您的提问!请尽可能详细地描述您的问题,这样我们才能更好地帮助您。
- type: checkboxes
id: checklist
attributes:
label: Issue 检查清单
description: |
在提交 Issue 前请确保您已经完成了以下所有步骤
options:
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决。
required: true
- label: 我确认自己需要的是提出问题并且讨论问题,而不是 Bug 反馈或需求建议。
required: true
- type: dropdown
id: platform
attributes:
label: 平台
description: 您正在使用哪个平台?
options:
- Windows
- macOS
- Linux
validations:
required: true
- type: input
id: version
attributes:
label: 版本
description: 您正在运行的 Cherry Studio 版本是什么?
placeholder: 例如 v1.0.0
validations:
required: true
- type: textarea
id: question
attributes:
label: 您的问题
description: 请详细描述您的问题
placeholder: 请尽可能清楚地说明您的问题...
validations:
required: true
- type: textarea
id: context
attributes:
label: 相关背景
description: 请提供一些背景信息,帮助我们更好地理解您的问题
placeholder: 例如:使用场景、已尝试的解决方案等
- type: textarea
id: additional
attributes:
label: 补充信息
description: 任何其他相关的信息、截图或代码示例
render: shell
- type: dropdown
id: priority
attributes:
label: 优先级
description: 这个问题对您来说有多紧急?
options:
- 低 (有空再看)
- 中 (希望尽快得到答复)
- 高 (阻碍工作进行)
validations:
required: true

View File

@@ -1,6 +1,12 @@
name: Release
on:
workflow_dispatch:
inputs:
tag:
description: 'Release tag (e.g. v1.0.0)'
required: true
default: 'v0.9.18'
push:
tags:
- v*.*.*
@@ -14,11 +20,22 @@ jobs:
strategy:
matrix:
os: [macos-latest, windows-latest]
os: [macos-latest, windows-latest, ubuntu-latest]
fail-fast: false
steps:
- name: Check out Git repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Get release tag
id: get-tag
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
else
echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
fi
- name: Install Node.js
uses: actions/setup-node@v3
@@ -28,50 +45,63 @@ jobs:
- name: Install corepack
run: corepack enable && corepack prepare yarn@4.3.1 --activate
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- name: Cache yarn dependencies
uses: actions/cache@v3
with:
path: |
${{ steps.yarn-cache-dir-path.outputs.dir }}
node_modules
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install Dependencies
run: yarn install
- name: Build Linux
if: matrix.os == 'ubuntu-latest'
run: yarn build:linux
run: |
yarn build:npm linux
yarn build:linux
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }}
- name: Build Mac
if: matrix.os == 'macos-latest'
run: yarn build:mac
run: |
yarn build:npm mac
yarn build:mac
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ vars.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ vars.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build Windows
if: matrix.os == 'windows-latest'
run: yarn build:win
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }}
- name: Replace spaces in filenames
run: node scripts/replaceSpaces.js
run: node scripts/replace-spaces.js
- name: Release
uses: softprops/action-gh-release@v2
uses: ncipollo/release-action@v1
with:
draft: true
files: |
dist/*.exe
dist/*.zip
dist/*.dmg
dist/*.AppImage
dist/*.snap
dist/*.deb
dist/*.rpm
dist/*.tar.gz
dist/latest*.yml
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
allowUpdates: true
makeLatest: false
tag: ${{ steps.get-tag.outputs.tag }}
artifacts: 'dist/*.exe,dist/*.zip,dist/*.dmg,dist/*.AppImage,dist/*.snap,dist/*.deb,dist/*.rpm,dist/*.tar.gz,dist/latest*.yml,dist/*.blockmap'
token: ${{ secrets.GITHUB_TOKEN }}

13
.gitignore vendored
View File

@@ -19,12 +19,6 @@ lerna-debug.log*
*.sln
*.sw?
# NPM
npm/*/*
!npm/*/dist
!npm/*/package.json
!npm/*/*.js
# Yarn
.pnp.*
.yarn/*
@@ -41,7 +35,14 @@ Thumbs.db
node_modules
dist
out
build/icons
stats.html
# ENV
.env
.env.*
# Local
local
.aider*
.cursorrules

View File

@@ -5,3 +5,4 @@ LICENSE.md
tsconfig.json
tsconfig.*.json
CHANGELOG*.md
agents.json

View File

@@ -29,5 +29,6 @@
},
"[markdown]": {
"files.trimTrailingWhitespace": false
}
},
"i18n-ally.localesPaths": ["src/renderer/src/i18n"]
}

View File

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

View File

@@ -0,0 +1,13 @@
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,

View File

@@ -0,0 +1,158 @@
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<BaseLoader>;
+export declare function createLoaderFromMimeType(loaderData: string, mimeType: string, chunkSize?: number, chunkOverlap?: number): Promise<BaseLoader>;
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': {

View File

@@ -0,0 +1,57 @@
diff --git a/dist/index.js b/dist/index.js
index 88c405a000d21b3631eaa378690907c5527b8eaf..e03e66440c7c93aee38adf57df3096c6fefcd96d 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -82,7 +82,6 @@ module.exports = __toCommonJS(index_exports);
// src/utils.ts
var import_axios = __toESM(require("axios"));
-var import_js_tiktoken = require("js-tiktoken");
var BASE_URL = "https://api.tavily.com";
var DEFAULT_MODEL_ENCODING = "gpt-3.5-turbo";
var DEFAULT_MAX_TOKENS = 4e3;
@@ -97,8 +96,7 @@ function post(endpoint, body, apiKey) {
});
}
function getTotalTokensFromString(str, encodingName = DEFAULT_MODEL_ENCODING) {
- const encoding = (0, import_js_tiktoken.encodingForModel)(encodingName);
- return encoding.encode(str).length;
+ return 0;
}
function getMaxTokensFromList(data, maxTokens = DEFAULT_MAX_TOKENS) {
var result = [];
diff --git a/dist/index.mjs b/dist/index.mjs
index 0a9ea6a0add8d709e6721e806571f373d9fe0487..b81f1ea48a2b2a30ee98d53980a1b04ea3fdc5d4 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -49,7 +49,6 @@ var __async = (__this, __arguments, generator) => {
// src/utils.ts
import axios from "axios";
-import { encodingForModel } from "js-tiktoken";
var BASE_URL = "https://api.tavily.com";
var DEFAULT_MODEL_ENCODING = "gpt-3.5-turbo";
var DEFAULT_MAX_TOKENS = 4e3;
@@ -64,8 +63,7 @@ function post(endpoint, body, apiKey) {
});
}
function getTotalTokensFromString(str, encodingName = DEFAULT_MODEL_ENCODING) {
- const encoding = encodingForModel(encodingName);
- return encoding.encode(str).length;
+ return 0;
}
function getMaxTokensFromList(data, maxTokens = DEFAULT_MAX_TOKENS) {
var result = [];
diff --git a/package.json b/package.json
index 36d4a613166a7906c1dc5377a89dc0a65f746f73..dc6e0e9363046755cad123e627cc270a2e3580d1 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,6 @@
"typescript": "^5.6.2"
},
"dependencies": {
- "axios": "^1.7.7",
- "js-tiktoken": "^1.0.14"
+ "axios": "^1.7.7"
}
}

View File

@@ -0,0 +1,39 @@
diff --git a/core.js b/core.js
index e75a18281ce8f051990c5a50bc1076afdddf91a3..e62f796791a155f23d054e74a429516c14d6e11b 100644
--- a/core.js
+++ b/core.js
@@ -156,7 +156,7 @@ class APIClient {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': this.getUserAgent(),
- ...getPlatformHeaders(),
+ // ...getPlatformHeaders(),
...this.authHeaders(opts),
};
}
diff --git a/core.mjs b/core.mjs
index fcef58eb502664c41a77483a00db8adaf29b2817..18c5d6ed4be86b3640931277bdc27700006764d7 100644
--- a/core.mjs
+++ b/core.mjs
@@ -149,7 +149,7 @@ export class APIClient {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': this.getUserAgent(),
- ...getPlatformHeaders(),
+ // ...getPlatformHeaders(),
...this.authHeaders(opts),
};
}
diff --git a/error.mjs b/error.mjs
index 7d19f5578040afa004bc887aab1725e8703d2bac..59ec725b6142299a62798ac4bdedb63ba7d9932c 100644
--- a/error.mjs
+++ b/error.mjs
@@ -36,7 +36,7 @@ export class APIError extends OpenAIError {
if (!status || !headers) {
return new APIConnectionError({ message, cause: castToError(errorResponse) });
}
- const error = errorResponse?.['error'];
+ const error = errorResponse?.['error'] || errorResponse;
if (status === 400) {
return new BadRequestError(status, error, message, headers);
}

View File

@@ -0,0 +1,29 @@
diff --git a/lib/pdf-parse.js b/lib/pdf-parse.js
index 96bfbc705dcb4fb73cb077a75f02c115371b3477..6d02d2bb426063c3a31cb740c3d86841de162a22 100644
--- a/lib/pdf-parse.js
+++ b/lib/pdf-parse.js
@@ -21,12 +21,12 @@ function render_page(pageData) {
for (let item of textContent.items) {
if (lastY == item.transform[5] || !lastY){
text += item.str;
- }
+ }
else{
text += '\n' + item.str;
- }
+ }
lastY = item.transform[5];
- }
+ }
//let strings = textContent.items.map(item => item.str);
//let text = strings.join("\n");
//text = text.replace(/[ ]+/ig," ");
@@ -60,7 +60,7 @@ async function PDF(dataBuffer, options) {
if (typeof options.version != 'string') options.version = DEFAULT_OPTIONS.version;
if (options.version == 'default') options.version = DEFAULT_OPTIONS.version;
- PDFJS = PDFJS ? PDFJS : require(`./pdf.js/${options.version}/build/pdf.js`);
+ PDFJS = PDFJS ? PDFJS : require(`./pdf.js/v1.10.100/build/pdf.js`);
ret.version = PDFJS.version;

View File

@@ -1,2 +1,5 @@
nodeLinker: node-modules
enableImmutableInstalls: false
httpTimeout: 300000
nodeLinker: node-modules

29
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,29 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

45
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,45 @@
# Cherry Studio 贡献者指南
欢迎来到 Cherry Studio 的贡献者社区!我们致力于将 Cherry Studio 打造成一个长期提供价值的项目,并希望邀请更多的开发者加入我们的行列。无论您是经验丰富的开发者还是刚刚起步的初学者,您的贡献都将帮助我们更好地服务用户,提升软件质量。
## 如何贡献
以下是您可以参与的几种方式:
1. **贡献代码**:帮助我们开发新功能或优化现有代码。请确保您的代码符合我们的编码标准,并通过所有测试。
2. **修复 BUG**:如果您发现了 BUG欢迎提交修复方案。请在提交前确认问题已被解决并附上相关测试。
3. **维护 Issue**:协助我们管理 GitHub 上的 issue帮助标记、分类和解决问题。
4. **产品设计**:参与产品设计讨论,帮助我们改进用户体验和界面设计。
5. **编写文档**帮助我们完善用户手册、API 文档和开发者指南。
6. **社区维护**:参与社区讨论,帮助解答用户问题,促进社区活跃。
7. **推广使用**:通过博客、社交媒体等渠道推广 Cherry Studio吸引更多用户和开发者。
## 开始贡献
1. **Fork 仓库**:在 GitHub 上 fork 我们的仓库,并将其克隆到本地。
2. **创建分支**:为您要进行的更改创建一个新的分支。
3. **提交更改**:在本地进行更改并提交。请确保您的提交信息清晰明了。
4. **发起 Pull Request**:将您的更改推送到 GitHub并发起 Pull Request。请描述您的更改内容和原因。
### 其他建议
- **联系开发者**:在提交 PR 之前,您可以先和开发者进行联系,共同探讨或者获取帮助。
- **成为核心开发者**:如果您能够稳定为项目贡献,恭喜您可以成为项目核心开发者,获取到项目成员身份。
## 联系我们
如果您有任何问题或建议,欢迎通过以下方式联系我们:
- 微信kangfenmao
- [GitHub Issues](https://github.com/kangfenmao/cherry-studio/issues)
感谢您的支持和贡献!我们期待与您一起将 Cherry Studio 打造成更好的产品。

136
LICENSE
View File

@@ -1,101 +1,79 @@
### Cherry Studio 商业许可协议
## Cherry Studio 用户协议
欢迎使用 Cherry Studio 桌面 AI 客户端工具。请仔细阅读以下协议条款,继续使用本软件即表示您同意本协议内容。
**许可协议**
本软件采用 Apache License 2.0 许可。除 Apache License 2.0 规定的条款外,您在使用 Cherry Studio 时还应遵守以下附加条款:
**一. 商用许可**
1. **免费商用**:用户在不修改代码的情况下,可以免费用于商业目的。
2. **商业授权**:如果您满足以下任意条件之一,需取得商业授权:
1. 对本软件进行二次修改、开发包括但不限于修改应用名称、logo、代码以及功能
2. 为企业客户提供多租户服务,且该服务支持 10 人或以上的使用。
3. 预装或集成到硬件设备或产品中进行捆绑销售。
4. 政府或教育机构的大规模采购项目,特别是涉及安全、数据隐私等敏感需求时。
**二. 贡献者协议**
作为 Cherry Studio 的贡献者,您应当同意以下条款:
1. **许可调整**:生产者有权根据需要对开源协议进行调整,使其更加严格或宽松。
2. **商业用途**:您贡献的代码可能会被用于商业用途,包括但不限于云业务运营。
**三. 其他条款**
1. 本协议条款的解释权归 Cherry Studio 开发者所有。
2. 本协议可能根据实际情况进行更新,更新时将通过本软件通知用户。
如有任何问题或需申请商业授权,请联系 Cherry Studio 开发团队。
除上述特定条件外,其他所有权利和限制均遵循 Apache License 2.0。有关 Apache License 2.0 的详细信息,请访问 http://www.apache.org/licenses/LICENSE-2.0。
---
#### 中文版
根据 Apache 许可证 2.0 版(“许可证”)进行许可;除非符合许可证,否则您不得使用此文件。您可以在以下网址获取许可证副本:
**Cherry Studio 商业许可协议**
http://www.apache.org/licenses/LICENSE-2.0
本协议(以下简称“协议”)由以下双方签订:
除非适用法律要求或书面同意,软件根据许可证分发的内容以“原样”分发,不附带任何明示或暗示的保证或条件。请参阅特定语言管理权限的许可证和许可证下的限制。
- 许可方王谦kangfenmao@qq.com
- 被许可方:[被许可方名称]
## Cherry Studio User Agreement
**1. 定义**
Welcome to Cherry Studio, a desktop AI client tool. Please read the following agreement carefully. By continuing to use this software, you agree to the terms outlined below.
- “软件”指 Cherry Studio 软件,网址为 https://cherry-ai.com。
- “商业用途”指任何以盈利为目的的使用。
**License Agreement**
**2. 许可**
This software is licensed under the **Apache License 2.0**. In addition to the terms of the Apache License 2.0, the following additional terms apply to the use of Cherry Studio:
- 未经许可方明确书面许可,被许可方不得将软件用于商业用途。
- 未经许可方事先书面同意,被许可方不得将软件全部或部分用于商业用途分发。
- 未经许可方明确授权,被许可方不得再许可、租赁、销售、出租或以其他方式将软件转让给任何第三方用于商业用途。
**I. Commercial Use License**
**3. 责任限制**
1. **Free Commercial Use**: Users can use the software for commercial purposes without modifying the code.
2. **Commercial License Required**: A commercial license is required if any of the following conditions are met:
1. You modify, develop, or alter the software, including but not limited to changes to the application name, logo, code, or functionality.
2. You provide multi-tenant services to enterprise customers with 10 or more users.
3. You pre-install or integrate the software into hardware devices or products and bundle it for sale.
4. You are engaging in large-scale procurement for government or educational institutions, especially involving security, data privacy, or other sensitive requirements.
开发者不对因使用本软件而产生的任何直接或间接损失承担责任。用户应自行承担使用本软件的风险。
**II. Contributor Agreement**
**4. 许可协议生效日期**
As a contributor to Cherry Studio, you agree to the following:
本许可协议自用户首次下载或使用本软件之日起生效。
1. **License Adjustment**: The producer reserves the right to adjust the open-source license as needed, making it stricter or more lenient.
2. **Commercial Use**: Any code you contribute may be used for commercial purposes, including but not limited to cloud business operations.
**5. 许可终止**
**III. Other Terms**
如发现用户违反上述条款,开发者有权随时终止本许可,并要求用户停止使用本软件及删除所有相关副本。
1. The interpretation of these terms is subject to the discretion of Cherry Studio developers.
2. These terms may be updated, and users will be notified through the software when changes occur.
**6. 其他**
For any questions or to request a commercial license, please contact the Cherry Studio development team.
本协议的解释、效力及争议的解决,均适用中华人民共和国法律。
**7. 联系信息**
- 许可方联系方式:
- 手机号18539907620
- 邮箱kangfenmao@qq.com
**许可方(签字):**
**日期:**
**被许可方(签字):**
**日期:**
Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache License 2.0. Detailed information about the Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
---
#### English Version
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
**Cherry Studio Commercial License Agreement**
This Agreement ("Agreement") is entered into by and between:
- Licensor: Wang Qian (kangfenmao)
- Licensee: [Licensee Name]
**1. Definitions**
- "Software" refers to the Cherry Studio software, available at https://cherry-ai.com.
- "Commercial Use" refers to any use for profit.
**2. License**
- The Licensee may not use the Software for Commercial Use without the Licensor's explicit written permission.
- The Licensee may not distribute the Software in whole or in part for Commercial Use without the Licensor's prior written consent.
- The Licensee may not sublicense, lease, sell, rent, or otherwise transfer the Software to any third party for Commercial Use without the Licensor's explicit authorization.
**3. Termination of License**
The developer reserves the right to terminate this license at any time if the terms are violated, and may require the user to cease using the software and delete all related copies.
**4. Effective Date of License Agreement**
This license agreement becomes effective from the date the user first downloads or uses the software.
**5. Termination of License**
The developer reserves the right to terminate this license at any time if the terms are violated, and may require the user to cease using the software and delete all related copies.
**6. Miscellaneous**
This Agreement shall be governed by and construed in accordance with the laws of the People's Republic of China.
**7. Contact Information**
- Licensor's Contact Details:
- Phone: 18539907620
- Email: kangfenmao@qq.com
**Licensor (Signature):**
**Date:**
**Licensee (Signature):**
**Date:**
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

143
README.md
View File

@@ -1,29 +1,84 @@
# Cherry Studio
<h1 align="center">
<a href="https://github.com/kangfenmao/cherry-studio/releases">
<img src="https://github.com/kangfenmao/cherry-studio/blob/main/build/icon.png?raw=true" width="150" height="150" alt="banner" /><br>
</a>
</h1>
<p align="center">English | <a href="./docs/README.zh.md">中文</a> | <a href="./docs/README.ja.md">日本語</a><br></p>
<div align="center">
<a href="https://trendshift.io/repositories/11772" target="_blank"><img src="https://trendshift.io/api/badge/repositories/11772" alt="kangfenmao%2Fcherry-studio | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</div>
🍒 Cherry Studio is a desktop client that supports multiple Large Language Model (LLM) providers, available on Windows, Mac and Linux.
# 🍒 Cherry Studio
# Screenshot
Cherry Studio is a desktop client that supports for multiple LLM providers, available on Windows, Mac and Linux.
![](https://github.com/user-attachments/assets/e32b244f-3a84-473a-89ef-0b12ef4127b2)
👏 Join [Telegram Group](https://t.me/CherryStudioAI)[Discord](https://discord.gg/wez8HtpxqQ) | [QQ Group(1022779719)](https://qm.qq.com/q/Qtw8As0cwe)
![](https://github.com/user-attachments/assets/18c10eed-4711-4975-bf9c-b274c61924f3)
❤️ Like Cherry Studio? Give it a star 🌟 or [Sponsor](docs/sponsor.md) to support the development!
![](https://github.com/user-attachments/assets/7395ebf2-64f8-46fa-aa48-63293516c320)
# 🌠 Screenshot
# Feature
![](https://github.com/user-attachments/assets/28585d83-4bf0-4714-b561-8c7bf57cc600)
![](https://github.com/user-attachments/assets/8576863a-f632-4776-bc12-657eeced9da3)
![](https://github.com/user-attachments/assets/790790d7-b462-48dd-bde1-91c1697a4648)
1. Supports multiple large language model service providers.
2. Allows creation of multiple Assistants.
3. Enables creation of multiple topics.
4. Allows using multiple models to answer questions in the same conversation.
5. Supports drag-and-drop sorting.
6. Code highlighting.
7. Mermaid chart
# 🌟 Key Features
# Develop
## Recommended IDE Setup
![](https://github.com/user-attachments/assets/7b4f2f78-5cbe-4be8-9aec-f98d8405a505)
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
1. **Diverse LLM Provider Support**:
- ☁️ Major LLM Cloud Services: OpenAI, Gemini, Anthropic, and more
- 🔗 AI Web Service Integration: Claude, Peplexity, Poe, and others
- 💻 Local Model Support with Ollama, LM Studio
2. **AI Assistants & Conversations**:
- 📚 300+ Pre-configured AI Assistants
- 🤖 Custom Assistant Creation
- 💬 Multi-model Simultaneous Conversations
3. **Document & Data Processing**:
- 📄 Support for Text, Images, Office, PDF, and more
- ☁️ WebDAV File Management and Backup
- 📊 Mermaid Chart Visualization
- 💻 Code Syntax Highlighting
4. **Practical Tools Integration**:
- 🔍 Global Search Functionality
- 📝 Topic Management System
- 🔤 AI-powered Translation
- 🎯 Drag-and-drop Sorting
- 🔌 Mini Program Support
5. **Enhanced User Experience**:
- 🖥️ Cross-platform Support for Windows, Mac, and Linux
- 📦 Ready to Use, No Environment Setup Required
- 🎨 Light/Dark Themes and Transparent Window
- 📝 Complete Markdown Rendering
- 🤲 Easy Content Sharing
# 📝 TODO
- [x] Quick popup (read clipboard, quick question, explain, translate, summarize)
- [x] Comparison of multi-model answers
- [x] Support login using SSO provided by service providers
- [ ] All models support networking (in development...)
- [ ] Launch of the first official version
- [ ] Plugin functionality (JavaScript)
- [ ] Browser extension (highlight text to translate, summarize, add to knowledge base)
- [ ] iOS & Android client
- [ ] AI notes
- [ ] Voice input and output (AI call)
- [ ] Data backup supports custom backup content
# 🖥️ Develop
## IDE Setup
[Cursor](https://www.cursor.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
## Project Setup
@@ -51,3 +106,57 @@ $ yarn build:mac
# For Linux
$ yarn build:linux
```
# 🤝 Contributing
We welcome contributions to Cherry Studio! Here are some ways you can contribute:
1. **Contribute Code**: Develop new features or optimize existing code.
2. **Fix Bugs**: Submit fixes for any bugs you find.
3. **Maintain Issues**: Help manage GitHub issues.
4. **Product Design**: Participate in design discussions.
5. **Write Documentation**: Improve user manuals and guides.
6. **Community Engagement**: Join discussions and help users.
7. **Promote Usage**: Spread the word about Cherry Studio.
## Getting Started
1. **Fork the Repository**: Fork and clone it to your local machine.
2. **Create a Branch**: For your changes.
3. **Submit Changes**: Commit and push your changes.
4. **Open a Pull Request**: Describe your changes and reasons.
For more detailed guidelines, please refer to our [Contributing Guide](./CONTRIBUTING.md).
Thank you for your support and contributions!
## Related Projects
- [one-api](https://github.com/songquanpeng/one-api):LLM API management and distribution system, supporting mainstream models like OpenAI, Azure, and Anthropic. Features unified API interface, suitable for key management and secondary distribution.
# 🚀 Contributors
<a href="https://github.com/kangfenmao/cherry-studio/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kangfenmao/cherry-studio" />
</a>
<br /><br />
# 🌐 Community
[Telegram](https://t.me/CherryStudioAI) | [Email](mailto:kangfenmao@gmail.com) | [Twitter](https://x.com/kangfenmao)
# 📣 Product Hunt
<a href="https://www.producthunt.com/posts/cherry-studio?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-cherry&#0045;studio" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=496640&theme=light" alt="Cherry&#0032;Studio - AI&#0032;Chatbots&#0044;&#0032;AI&#0032;Desktop&#0032;Client | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
# ☕ Sponsor
[Buy Me a Coffee](docs/sponsor.md)
# 📃 License
[LICENSE](./LICENSE)
# ⭐️ Star History
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 KiB

After

Width:  |  Height:  |  Size: 137 KiB

BIN
build/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

47
build/nsis-installer.nsh Normal file
View File

@@ -0,0 +1,47 @@
;Inspired by:
; https://gist.github.com/bogdibota/062919938e1ed388b3db5ea31f52955c
; https://stackoverflow.com/questions/34177547/detect-if-visual-c-redistributable-for-visual-studio-2013-is-installed
; https://stackoverflow.com/a/54391388
; https://github.com/GitCommons/cpp-redist-nsis/blob/main/installer.nsh
;Find latests downloads here:
; https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist
!include LogicLib.nsh
; https://github.com/electron-userland/electron-builder/issues/1122
!ifndef BUILD_UNINSTALLER
Function checkVCRedist
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed"
FunctionEnd
!endif
!macro customInit
Push $0
Call checkVCRedist
${If} $0 != "1"
MessageBox MB_YESNO "\
NOTE: ${PRODUCT_NAME} requires $\r$\n\
'Microsoft Visual C++ Redistributable'$\r$\n\
to function properly.$\r$\n$\r$\n\
Download and install now?" /SD IDYES IDYES InstallVCRedist IDNO DontInstall
InstallVCRedist:
inetc::get /CAPTION " " /BANNER "Downloading Microsoft Visual C++ Redistributable..." "https://aka.ms/vs/17/release/vc_redist.x64.exe" "$TEMP\vc_redist.x64.exe"
ExecWait "$TEMP\vc_redist.x64.exe /install /norestart"
;IfErrors InstallError ContinueInstall ; vc_redist exit code is unreliable :(
Call checkVCRedist
${If} $0 == "1"
Goto ContinueInstall
${EndIf}
;InstallError:
MessageBox MB_ICONSTOP "\
There was an unexpected error installing$\r$\n\
Microsoft Visual C++ Redistributable.$\r$\n\
The installation of ${PRODUCT_NAME} cannot continue."
DontInstall:
Abort
${EndIf}
ContinueInstall:
Pop $0
!macroend

BIN
build/tray_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
build/tray_icon_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
build/tray_icon_light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,6 +1,8 @@
# provider: generic
# url: http://127.0.0.1:8080
# updaterCacheDirName: cherry-studio-updater
provider: github
repo: cherry-studio
owner: kangfenmao
# provider: github
# repo: cherry-studio
# owner: kangfenmao
provider: generic
url: https://cherrystudio.ocool.online

162
docs/README.ja.md Normal file
View File

@@ -0,0 +1,162 @@
<h1 align="center">
<a href="https://github.com/kangfenmao/cherry-studio/releases">
<img src="https://github.com/kangfenmao/cherry-studio/blob/main/build/icon.png?raw=true" width="150" height="150" alt="banner" />
</a>
</h1>
<div align="center">
<a href="./README.md">English</a> | <a href="./README.zh.md">中文</a> | 日本語
</div>
<div align="center">
<a href="https://trendshift.io/repositories/11772" target="_blank"><img src="https://trendshift.io/api/badge/repositories/11772" alt="kangfenmao%2Fcherry-studio | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</div>
# 🍒 Cherry Studio
Cherry Studioは、複数のLLMプロバイダーをサポートするデスクトップクライアントで、Windows、Mac、Linuxで利用可能です。
👏 [Telegram](https://t.me/CherryStudioAI)[Discord](https://discord.gg/wez8HtpxqQ) | [QQグループ(1022779719)](https://qm.qq.com/q/Qtw8As0cwe)
❤️ Cherry Studioをお気に入りにしましたか小さな星をつけてください 🌟 または [スポンサー](sponsor.md) をして開発をサポートしてください!❤️
# 🌠 スクリーンショット
![](https://github.com/user-attachments/assets/28585d83-4bf0-4714-b561-8c7bf57cc600)
![](https://github.com/user-attachments/assets/8576863a-f632-4776-bc12-657eeced9da3)
![](https://github.com/user-attachments/assets/790790d7-b462-48dd-bde1-91c1697a4648)
# 🌟 主な機能
![](https://github.com/user-attachments/assets/7b4f2f78-5cbe-4be8-9aec-f98d8405a505)
1. **多様な LLM サービス対応**
- ☁️ 主要な LLM クラウドサービス対応OpenAI、Gemini、Anthropic など
- 🔗 AI Web サービス統合Claude、Peplexity、Poe など
- 💻 Ollama、LM Studio によるローカルモデル実行対応
2. **AI アシスタントと対話**
- 📚 300+ の事前設定済み AI アシスタント
- 🤖 カスタム AI アシスタントの作成
- 💬 複数モデルでの同時対話機能
3. **文書とデータ処理**
- 📄 テキスト、画像、Office、PDF など多様な形式対応
- ☁️ WebDAV によるファイル管理とバックアップ
- 📊 Mermaid による図表作成
- 💻 コードハイライト機能
4. **実用的なツール統合**
- 🔍 グローバル検索機能
- 📝 トピック管理システム
- 🔤 AI による翻訳機能
- 🎯 ドラッグ&ドロップによる整理
- 🔌 ミニプログラム対応
5. **優れたユーザー体験**
- 🖥️ Windows、Mac、Linux のクロスプラットフォーム対応
- 📦 環境構築不要ですぐに使用可能
- 🎨 ライト/ダークテーマと透明ウィンドウ対応
- 📝 完全な Markdown レンダリング
- 🤲 簡単な共有機能
# 📝 TODO
- [x] クイックポップアップ(クリップボードの読み取り、簡単な質問、説明、翻訳、要約)
- [x] 複数モデルの回答の比較
- [x] サービスプロバイダーが提供するSSOを使用したログインをサポート
- [ ] すべてのモデルがネットワークをサポート(開発中...
- [ ] 最初の公式バージョンのリリース
- [ ] プラグイン機能JavaScript
- [ ] ブラウザ拡張機能(テキストをハイライトして翻訳、要約、ナレッジベースに追加)
- [ ] iOS & Android クライアント
- [ ] AIート
- [ ] 音声入出力AIコール
- [ ] データバックアップはカスタムバックアップコンテンツをサポート
# 🖥️ 開発
## IDEの設定
[Cursor](https://www.cursor.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
## プロジェクトの設定
### インストール
```bash
$ yarn
```
### 開発
```bash
$ yarn dev
```
### ビルド
```bash
# Windowsの場合
$ yarn build:win
# macOSの場合
$ yarn build:mac
# Linuxの場合
$ yarn build:linux
```
# 🤝 貢献
Cherry Studioへの貢献を歓迎します以下の方法で貢献できます
1. **コードの貢献**:新機能を開発するか、既存のコードを最適化します。
2. **バグの修正**:見つけたバグを修正します。
3. **問題の管理**GitHubの問題を管理するのを手伝います。
4. **製品デザイン**:デザインの議論に参加します。
5. **ドキュメントの作成**:ユーザーマニュアルやガイドを改善します。
6. **コミュニティの参加**:ディスカッションに参加し、ユーザーを支援します。
7. **使用の促進**Cherry Studioを広めます。
## 始め方
1. **リポジトリをフォーク**:フォークしてローカルマシンにクローンします。
2. **ブランチを作成**:変更のためのブランチを作成します。
3. **変更を提出**:変更をコミットしてプッシュします。
4. **プルリクエストを開く**:変更内容と理由を説明します。
詳細なガイドラインについては、[貢献ガイド](./CONTRIBUTING.md)をご覧ください。
ご支援と貢献に感謝します!
## 関連頁版
- [one-api](https://github.com/songquanpeng/one-api):LLM APIの管理・配信システム。OpenAI、Azure、Anthropicなどの主要モデルに対応し、統一APIインターフェースを提供。APIキー管理と再配布に利用可能。
# 🚀 コントリビューター
<a href="https://github.com/kangfenmao/cherry-studio/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kangfenmao/cherry-studio" />
</a>
# コミュニティ
[Telegram](https://t.me/CherryStudioAI) | [Email](mailto:kangfenmao@gmail.com) | [Twitter](https://x.com/kangfenmao)
# 📣 プロダクトハント
<a href="https://www.producthunt.com/posts/cherry-studio?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-cherry&#0045;studio" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=496640&theme=light" alt="Cherry&#0032;Studio - AI&#0032;Chatbots&#0044;&#0032;AI&#0032;Desktop&#0032;Client | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
# スポンサー
[Buy Me a Coffee](sponsor.md)
# 📃 ライセンス
[LICENSE](../LICENSE)
# ⭐️ スター履歴
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)

163
docs/README.zh.md Normal file
View File

@@ -0,0 +1,163 @@
<h1 align="center">
<a href="https://github.com/kangfenmao/cherry-studio/releases">
<img src="https://github.com/kangfenmao/cherry-studio/blob/main/build/icon.png?raw=true" width="150" height="150" alt="banner" />
</a>
</h1>
<div align="center">
中文 / <a href="https://github.com/kangfenmao/cherry-studio">English</a> / <a href="./README.ja.md">日本語</a>
</div>
<div align="center">
<a href="https://trendshift.io/repositories/11772" target="_blank"><img src="https://trendshift.io/api/badge/repositories/11772" alt="kangfenmao%2Fcherry-studio | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</div>
# 🍒 Cherry Studio
Cherry Studio 是一款支持多个大语言模型LLM服务商的桌面客户端兼容 Windows、Mac 和 Linux 系统。
👏 欢迎加入 [Telegram 群组](https://t.me/CherryStudioAI)[Discord](https://discord.gg/wez8HtpxqQ) | [QQ群(1022779719)](https://qm.qq.com/q/Qtw8As0cwe)
❤️ 喜欢 Cherry Studio? 点亮小星星 🌟 或 [赞助开发者](sponsor.md)! ❤️
# 🌠 界面
![](https://github.com/user-attachments/assets/28585d83-4bf0-4714-b561-8c7bf57cc600)
![](https://github.com/user-attachments/assets/8576863a-f632-4776-bc12-657eeced9da3)
![](https://github.com/user-attachments/assets/790790d7-b462-48dd-bde1-91c1697a4648)
# 🌟 主要特性
![](https://github.com/user-attachments/assets/995910f3-177a-4d1e-97ea-04e3b009ba36)
1. **多样化 LLM 服务支持**
- ☁️ 支持主流 LLM 云服务OpenAI、Gemini、Anthropic、硅基流动等
- 🔗 集成流行 AI Web 服务Claude、Peplexity、Poe、腾讯元宝、知乎直答等
- 💻 支持 Ollama、LM Studio 本地模型部署
2. **智能助手与对话**
- 📚 内置 300+ 预配置 AI 助手
- 🤖 支持自定义创建专属助手
- 💬 多模型同时对话,获得多样化观点
3. **文档与数据处理**
- 📄 支持文本、图片、Office、PDF 等多种格式
- ☁️ WebDAV 文件管理与数据备份
- 📊 Mermaid 图表可视化
- 💻 代码高亮显示
4. **实用工具集成**
- 🔍 全局搜索功能
- 📝 话题管理系统
- 🔤 AI 驱动的翻译功能
- 🎯 拖拽排序
- 🔌 小程序支持
5. **优质使用体验**
- 🖥️ Windows、Mac、Linux 跨平台支持
- 📦 开箱即用,无需配置环境
- 🎨 支持明暗主题与透明窗口
- 📝 完整的 Markdown 渲染
- 🤲 便捷的内容分享功能
# 📝 待辦事項
- [x] 快捷彈窗 (讀取剪貼簿、快速提問、解釋、翻譯、總結)
- [x] 多模型回答對比
- [x] 支援使用服務供應商提供的 SSO 進行登入
- [ ] 全部模型支援連網(開發中...
- [ ] 推出第一個正式版
- [ ] 插件功能JavaScript
- [ ] 瀏覽器插件(劃詞翻譯、總結、新增至知識庫)
- [ ] iOS & Android 客戶端
- [ ] AI 筆記
- [ ] 語音輸入輸出AI 通話)
- [ ] 資料備份支援自訂備份內容
# 🖥️ 开发
## IDE 设置
[Cursor](https://www.cursor.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
## 项目设置
### 安装
```bash
$ yarn
```
### 开发
```bash
$ yarn dev
```
### 构建
```bash
# Windows
$ yarn build:win
# macOS
$ yarn build:mac
# Linux
$ yarn build:linux
```
# 🤝 贡献
我们欢迎对 Cherry Studio 的贡献!您可以通过以下方式贡献:
1. **贡献代码**:开发新功能或优化现有代码。
2. **修复错误**:提交您发现的错误修复。
3. **维护问题**:帮助管理 GitHub 问题。
4. **产品设计**:参与设计讨论。
5. **撰写文档**:改进用户手册和指南。
6. **社区参与**:加入讨论并帮助用户。
7. **推广使用**:宣传 Cherry Studio。
## 入门
1. **Fork 仓库**Fork 并克隆到您的本地机器。
2. **创建分支**:为您的更改创建分支。
3. **提交更改**:提交并推送您的更改。
4. **打开 Pull Request**:描述您的更改和原因。
有关更详细的指南,请参阅我们的 [贡献指南](./CONTRIBUTING.md)。
感谢您的支持和贡献!
## 相关项目
- [one-api](https://github.com/songquanpeng/one-api):LLM API管理及分发系统支持OpenAI、Azure、Anthropic等主流模型统一API接口可用于密钥管理与二次分发。
# 🚀 贡献者
<a href="https://github.com/kangfenmao/cherry-studio/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kangfenmao/cherry-studio" />
</a>
<br /><br />
# 🌐 社区
[Telegram](https://t.me/CherryStudioAI) | [Email](mailto:kangfenmao@gmail.com) | [Twitter](https://x.com/kangfenmao)
# 📣 产品猎人
<a href="https://www.producthunt.com/posts/cherry-studio?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-cherry&#0045;studio" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=496640&theme=light" alt="Cherry&#0032;Studio - AI&#0032;Chatbots&#0044;&#0032;AI&#0032;Desktop&#0032;Client | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
# ☕ 赞助
[微信赞赏码](sponsor.md)
# 📃 许可证
[LICENSE](../LICENSE)
# ⭐️ Star 记录
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)

5
docs/sponsor.md Normal file
View File

@@ -0,0 +1,5 @@
# Sponsor
<div align="center">
<img src="https://github.com/user-attachments/assets/4665f07f-5ecc-4bd8-8727-ae00f35d6d98" alt="Buy Me a Coffee" width="280"/>
</div>

View File

@@ -3,29 +3,57 @@ productName: Cherry Studio
directories:
buildResources: build
files:
- '!**/.vscode/*'
- '!src/*'
- '!{.vscode,.yarn,.github}'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
- '!src'
- '!scripts'
- '!local'
- '!docs'
- '!packages'
- '!stats.html'
- '!*.md'
- '!**/*.{map,ts,tsx,jsx,less,scss,sass,css.d.ts,d.cts,d.mts,md,markdown,yaml,yml}'
- '!**/{test,tests,__tests__,coverage}/**'
- '!**/*.{spec,test}.{js,jsx,ts,tsx}'
- '!**/*.min.*.map'
- '!**/*.d.ts'
- '!**/{.DS_Store,Thumbs.db}'
- '!**/{LICENSE,LICENSE.txt,LICENSE-MIT.txt,*.LICENSE.txt,NOTICE.txt,README.md,CHANGELOG.md}'
- '!node_modules/rollup-plugin-visualizer'
- '!node_modules/js-tiktoken'
- '!node_modules/@tavily/core/node_modules/js-tiktoken'
- '!node_modules/pdf-parse/lib/pdf.js/{v1.9.426,v1.10.88,v2.0.550}'
- '!node_modules/mammoth/{mammoth.browser.js,mammoth.browser.min.js}'
- '!node_modules/html2canvas/dist/{html2canvas.min.js,html2canvas.esm.js}'
asarUnpack:
- resources/**
- '**/*.{node,dll,metal,exp,lib}'
win:
executableName: Cherry Studio
artifactName: ${productName}-${version}-portable.${ext}
target:
- target: nsis
- target: portable
nsis:
artifactName: ${productName}-${version}-setup.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
allowToChangeInstallationDirectory: true
oneClick: false
include: build/nsis-installer.nsh
mac:
entitlementsInherit: build/entitlements.mac.plist
notarize: false
artifactName: ${productName}-${version}-${arch}.${ext}
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: false
target:
- target: dmg
arch:
@@ -35,26 +63,22 @@ mac:
arch:
- arm64
- x64
dmg:
artifactName: ${productName}-${version}-${arch}.${ext}
linux:
artifactName: ${productName}-${version}-${arch}.${ext}
target:
- AppImage
- snap
- deb
- target: AppImage
arch:
- arm64
- x64
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${productName}-${version}.${ext}
npmRebuild: false
publish:
provider: github
repo: cherry-studio
owner: kangfenmao
provider: generic
url: https://cherrystudio.ocool.online
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/
afterPack: scripts/after-pack.js
afterSign: scripts/notarize.js
releaseInfo:
releaseNotes: |
支持保存自定义智能体
修复话题重命名的问题
全部模型支持 Web 搜索

View File

@@ -1,21 +1,57 @@
import react from '@vitejs/plugin-react'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import { resolve } from 'path'
import { visualizer } from 'rollup-plugin-visualizer'
const visualizerPlugin = (type: 'renderer' | 'main') => {
return process.env[`VISUALIZER_${type.toUpperCase()}`] ? [visualizer({ open: true })] : []
}
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()]
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'
]
}),
...visualizerPlugin('main')
],
resolve: {
alias: {
'@main': resolve('src/main'),
'@types': resolve('src/renderer/src/types'),
'@shared': resolve('packages/shared')
}
},
build: {
rollupOptions: {
external: ['@libsql/client']
}
}
},
preload: {
plugins: [externalizeDepsPlugin()]
},
renderer: {
plugins: [react(), ...visualizerPlugin('renderer')],
resolve: {
alias: {
'@renderer': resolve('src/renderer/src')
'@renderer': resolve('src/renderer/src'),
'@shared': resolve('packages/shared')
}
},
plugins: [react()],
assetsInclude: ['**/*.md']
optimizeDeps: {
exclude: ['chunk-PZ64DZKH.js', 'chunk-JMKENWIY.js', 'chunk-UXYB6GHG.js']
}
}
})

View File

@@ -1,10 +1,22 @@
{
"name": "cherry-studio",
"version": "0.4.9",
"name": "CherryStudio",
"version": "1.0.0",
"private": true,
"description": "A powerful AI assistant for producer.",
"main": "./out/main/index.js",
"author": "kangfenmao@qq.com",
"homepage": "https://github.com/kangfenmao/cherry-studio",
"workspaces": {
"packages": [
"local",
"packages/*"
],
"installConfig": {
"hoistingLimits": [
"packages/database"
]
}
},
"scripts": {
"format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
@@ -13,24 +25,59 @@
"typecheck": "npm run typecheck:node && npm run typecheck:web",
"start": "electron-vite preview",
"dev": "electron-vite dev",
"build:check": "yarn typecheck",
"build": "npm run typecheck && electron-vite build",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "dotenv npm run build && electron-builder --dir",
"build:win": "dotenv npm run build && electron-builder --win --publish never",
"build:mac": "dotenv electron-vite build && electron-builder --mac --publish never",
"build:linux": "dotenv electron-vite build && electron-builder --linux --publish never",
"build:win": "dotenv npm run build && electron-builder --win",
"build:win:x64": "dotenv npm run build && electron-builder --win --x64",
"build:mac": "dotenv electron-vite build && electron-builder --mac",
"build:mac:arm64": "dotenv electron-vite build && electron-builder --mac --arm64",
"build:mac:x64": "dotenv electron-vite build && electron-builder --mac --x64",
"build:linux": "dotenv electron-vite build && electron-builder --linux",
"build:linux:arm64": "dotenv electron-vite build && electron-builder --linux --arm64",
"build:linux:x64": "dotenv electron-vite build && electron-builder --linux --x64",
"build:npm": "node scripts/build-npm.js",
"release": "node scripts/version.js",
"publish": "yarn release patch push"
"publish": "yarn release patch push",
"pulish:artifacts": "cd packages/artifacts && npm publish && cd -",
"generate:agents": "yarn workspace @cherry-studio/database agents",
"generate:icons": "electron-icon-builder --input=./build/logo.png --output=build",
"analyze:renderer": "VISUALIZER_RENDERER=true yarn build",
"analyze:main": "VISUALIZER_MAIN=true yarn build",
"check": "node scripts/check-i18n.js"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"@sentry/electron": "^5.2.0",
"@electron/notarize": "^2.5.0",
"@google/generative-ai": "^0.21.0",
"@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",
"@notionhq/client": "^2.2.15",
"@types/react-infinite-scroll-component": "^5.0.0",
"adm-zip": "^0.5.16",
"apache-arrow": "^18.1.0",
"docx": "^9.0.2",
"electron-log": "^5.1.5",
"electron-store": "^8.2.0",
"electron-updater": "^6.1.7",
"electron-updater": "^6.3.9",
"electron-window-state": "^5.0.3",
"eslint-plugin-simple-import-sort": "^12.1.1"
"epub": "^1.3.0",
"fs-extra": "^11.2.0",
"html2canvas": "^1.4.1",
"markdown-it": "^14.1.0",
"officeparser": "^4.1.1",
"tokenx": "^0.4.1",
"webdav": "4.11.4"
},
"devDependencies": {
"@anthropic-ai/sdk": "^0.24.3",
@@ -39,49 +86,70 @@
"@electron-toolkit/tsconfig": "^1.0.1",
"@hello-pangea/dnd": "^16.6.0",
"@kangfenmao/keyv-storage": "^0.1.0",
"@llm-tools/embedjs-loader-image": "^0.1.28",
"@reduxjs/toolkit": "^2.2.5",
"@tavily/core": "patch:@tavily/core@npm%3A0.3.1#~/.yarn/patches/@tavily-core-npm-0.3.1-fe69bf2bea.patch",
"@types/adm-zip": "^0",
"@types/fs-extra": "^11",
"@types/lodash": "^4.17.5",
"@types/markdown-it": "^14",
"@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-infinite-scroll-component": "^5.0.0",
"@types/tinycolor2": "^1",
"@vitejs/plugin-react": "^4.2.1",
"ahooks": "^3.8.0",
"antd": "^5.18.3",
"antd": "^5.22.5",
"applescript": "^1.0.0",
"axios": "^1.7.3",
"browser-image-compression": "^2.0.2",
"dayjs": "^1.11.11",
"dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7",
"dotenv-cli": "^7.4.2",
"electron": "^28.2.0",
"electron-builder": "^24.9.1",
"electron": "31.7.6",
"electron-builder": "^24.13.3",
"electron-devtools-installer": "^3.2.0",
"electron-vite": "^2.0.0",
"electron-icon-builder": "^2.0.1",
"electron-vite": "^2.3.0",
"emittery": "^1.0.3",
"emoji-picker-element": "^1.22.1",
"eslint": "^8.56.0",
"eslint-plugin-react": "^7.34.3",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-unused-imports": "^4.0.0",
"gpt-tokens": "^1.3.6",
"i18next": "^23.11.5",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"openai": "^4.52.1",
"mime": "^4.0.4",
"openai": "patch:openai@npm%3A4.77.3#~/.yarn/patches/openai-npm-4.77.3-59c6d42e7a.patch",
"prettier": "^3.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hotkeys-hook": "^4.6.1",
"react-i18next": "^14.1.2",
"react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^9.0.1",
"react-redux": "^9.1.2",
"react-router": "6",
"react-router-dom": "6",
"react-spinners": "^0.14.1",
"react-syntax-highlighter": "^15.5.0",
"redux": "^5.0.1",
"redux-persist": "^6.0.0",
"rehype-katex": "^7.0.0",
"rehype-katex": "^7.0.1",
"rehype-mathjax": "^7.0.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.77.2",
"shiki": "^1.22.2",
"string-width": "^7.2.0",
"styled-components": "^6.1.11",
"typescript": "^5.3.3",
"tinycolor2": "^1.6.0",
"typescript": "^5.6.2",
"uuid": "^10.0.0",
"vite": "^5.0.12"
},
@@ -90,7 +158,10 @@
"react-dom": "^17.0.0 || ^18.0.0"
},
"resolutions": {
"@electron/notarize": "2.3.2"
"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",
"@langchain/openai@npm:>=0.1.0 <0.4.0": "patch:@langchain/openai@npm%3A0.3.16#~/.yarn/patches/@langchain-openai-npm-0.3.16-e525b59526.patch",
"openai@npm:^4.77.0": "patch:openai@npm%3A4.77.3#~/.yarn/patches/openai-npm-4.77.3-59c6d42e7a.patch"
},
"packageManager": "yarn@4.3.1"
"packageManager": "yarn@4.6.0"
}

View File

@@ -0,0 +1 @@
# Cherry Studio Artifacts

View File

@@ -0,0 +1,19 @@
{
"name": "@cherry-studio/artifacts",
"version": "0.1.0",
"description": "Cherry Studio Artifacts",
"main": "index.js",
"homepage": "https://github.com/kangfenmao/cherry-studio/blob/main/npm/artifacts",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"artifacts"
],
"author": "kangfenmao",
"license": "ISC"
}

View File

@@ -0,0 +1,108 @@
:root {
/* 莫兰迪色系:使用柔和、低饱和度的颜色 */
--primary-color: #b6b5a7; /* 莫兰迪灰褐色,用于背景文字 */
--secondary-color: #9a8f8f; /* 莫兰迪灰棕色,用于标题背景 */
--accent-color: #c5b4a0; /* 莫兰迪淡棕色,用于强调元素 */
--background-color: #e8e3de; /* 莫兰迪米色,用于页面背景 */
--text-color: #5b5b5b; /* 莫兰迪深灰色,用于主要文字 */
--light-text-color: #8c8c8c; /* 莫兰迪中灰色,用于次要文字 */
--divider-color: #d1cbc3; /* 莫兰迪浅灰色,用于分隔线 */
}
body,
html {
margin: 0;
padding: 0;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--background-color); /* 使用莫兰迪米色作为页面背景 */
font-family: 'Noto Sans SC', sans-serif;
color: var(--text-color); /* 使用莫兰迪深灰色作为主要文字颜色 */
}
.card {
width: 300px;
height: 500px;
background-color: #f2ede9; /* 莫兰迪浅米色,用于卡片背景 */
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
}
.header {
background-color: var(--secondary-color); /* 使用莫兰迪灰棕色作为标题背景 */
color: #f2ede9; /* 浅色文字与深色背景形成对比 */
padding: 20px;
text-align: left;
position: relative;
z-index: 1;
}
h1 {
font-family: 'Noto Serif SC', serif;
font-size: 20px;
margin: 0;
font-weight: 700;
}
.content {
padding: 30px 20px;
display: flex;
flex-direction: column;
flex-grow: 1;
}
.word {
text-align: left;
margin-bottom: 20px;
}
.word-main {
font-family: 'Noto Serif SC', serif;
font-size: 36px;
color: var(--text-color); /* 使用莫兰迪深灰色作为主要词汇颜色 */
margin-bottom: 10px;
position: relative;
}
.word-main::after {
content: '';
position: absolute;
left: 0;
bottom: -5px;
width: 50px;
height: 3px;
background-color: var(--accent-color); /* 使用莫兰迪淡棕色作为下划线 */
}
.word-sub {
font-size: 14px;
color: var(--light-text-color); /* 使用莫兰迪中灰色作为次要文字颜色 */
margin: 5px 0;
}
.divider {
width: 100%;
height: 1px;
background-color: var(--divider-color); /* 使用莫兰迪浅灰色作为分隔线 */
margin: 20px 0;
}
.explanation {
font-size: 18px;
line-height: 1.6;
text-align: left;
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.quote {
position: relative;
padding-left: 20px;
border-left: 3px solid var(--accent-color); /* 使用莫兰迪淡棕色作为引用边框 */
}
.background-text {
position: absolute;
font-size: 150px;
color: rgba(182, 181, 167, 0.15); /* 使用莫兰迪灰褐色的透明版本作为背景文字 */
z-index: 0;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-weight: bold;
}

3
packages/database/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
data/*
!data/.gitkeep

Binary file not shown.

View File

@@ -0,0 +1,3 @@
# Cherry Studio Database
Cherry Studio 依赖的数据文件由这个数据库来生成,数据库文件请联系开发者获取

View File

View File

@@ -0,0 +1,13 @@
{
"name": "@cherry-studio/database",
"packageManager": "yarn@4.3.1",
"dependencies": {
"csv-parser": "^3.0.0",
"sqlite3": "^5.1.7"
},
"scripts": {
"agents": "node src/agents.js",
"email": "yarn csv && node src/email.js",
"csv": "node src/csv.js"
}
}

View File

@@ -0,0 +1,47 @@
const sqlite3 = require('sqlite3').verbose()
const fs = require('fs')
// 连接到数据库
const db = new sqlite3.Database('./data/CherryStudio.sqlite3', (err) => {
if (err) {
console.error('Error connecting to the database:', err.message)
return
}
console.log('Connected to the database.')
})
// 查询数据并转换为JSON
db.all('SELECT * FROM agents', [], (err, rows) => {
if (err) {
console.error('Error querying the database:', err.message)
return
}
// 将 ID 类型转换为字符串
for (const row of rows) {
row.id = row.id.toString()
row.group = row.group.toString().split(',')
row.group = row.group.map((item) => item.trim().replace('\r\n', ''))
}
// 将查询结果转换为JSON字符串
const jsonData = JSON.stringify(rows, null, 2)
// 将JSON数据写入文件
fs.writeFile('../../src/renderer/src/config/agents.json', jsonData, (err) => {
if (err) {
console.error('Error writing to file:', err.message)
return
}
console.log('Data has been written to agents.json')
})
// 关闭数据库连接
db.close((err) => {
if (err) {
console.error('Error closing the database:', err.message)
return
}
console.log('Database connection closed.')
})
})

View File

@@ -0,0 +1,77 @@
const fs = require('fs')
const csv = require('csv-parser')
const sqlite3 = require('sqlite3').verbose()
// 连接到 SQLite 数据库
const db = new sqlite3.Database('./data/CherryStudio.sqlite3', (err) => {
if (err) {
console.error('Error opening database', err)
return
}
console.log('Connected to the SQLite database.')
})
// 创建一个数组来存储 CSV 数据
const results = []
// 读取 CSV 文件
fs.createReadStream('./data/data.csv')
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', () => {
// 准备 SQL 插入语句,使用 INSERT OR IGNORE
const stmt = db.prepare('INSERT OR IGNORE INTO emails (email, github, sent) VALUES (?, ?, ?)')
// 插入每一行数据
let inserted = 0
let skipped = 0
let emptyEmail = 0
db.serialize(() => {
// 开始一个事务以提高性能
db.run('BEGIN TRANSACTION')
results.forEach((row) => {
// 检查 email 是否为空
if (!row.email || row.email.trim() === '') {
emptyEmail++
return // 跳过这一行
}
stmt.run(row.email, row['user-href'], 0, function (err) {
if (err) {
console.error('Error inserting row', err)
} else {
if (this.changes === 1) {
inserted++
} else {
skipped++
}
}
})
})
// 提交事务
db.run('COMMIT', (err) => {
if (err) {
console.error('Error committing transaction', err)
} else {
console.log(
`Insertion complete. Inserted: ${inserted}, Skipped (duplicate): ${skipped}, Skipped (empty email): ${emptyEmail}`
)
}
// 完成插入
stmt.finalize()
// 关闭数据库连接
db.close((err) => {
if (err) {
console.error('Error closing database', err)
} else {
console.log('Database connection closed.')
}
})
})
})
})

View File

@@ -0,0 +1,36 @@
const sqlite3 = require('sqlite3').verbose()
// 连接到数据库
const db = new sqlite3.Database('./data/CherryStudio.sqlite3', (err) => {
if (err) {
console.error('Error connecting to the database:', err.message)
return
}
})
// 查询数据并转换为JSON
db.all('SELECT * FROM emails WHERE sent = 0', [], (err, rows) => {
if (err) {
console.error('Error querying the database:', err.message)
return
}
for (const row of rows) {
console.log(row.email)
// Update row set sent = 1
db.run('UPDATE emails SET sent = 1 WHERE id = ?', [row.id], (err) => {
if (err) {
console.error('Error updating the database:', err.message)
return
}
})
}
// 关闭数据库连接
db.close((err) => {
if (err) {
console.error('Error closing the database:', err.message)
return
}
})
})

1643
packages/database/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
export const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
export const videoExts = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv']
export const audioExts = ['.mp3', '.wav', '.ogg', '.flac', '.aac']
export const documentExts = ['.pdf', '.docx', '.pptx', '.xlsx', '.odt', '.odp', '.ods']
export const thirdPartyApplicationExts = ['.draftsExport']
export const bookExts = ['.epub']
export const textExts = [
'.txt', // 普通文本文件
'.md', // Markdown 文件
'.mdx', // Markdown 文件
'.html', // HTML 文件
'.htm', // HTML 文件的另一种扩展名
'.xml', // XML 文件
'.json', // JSON 文件
'.yaml', // YAML 文件
'.yml', // YAML 文件的另一种扩展名
'.csv', // 逗号分隔值文件
'.tsv', // 制表符分隔值文件
'.ini', // 配置文件
'.log', // 日志文件
'.rtf', // 富文本格式文件
'.tex', // LaTeX 文件
'.srt', // 字幕文件
'.xhtml', // XHTML 文件
'.nfo', // 信息文件(主要用于场景发布)
'.conf', // 配置文件
'.config', // 配置文件
'.env', // 环境变量文件
'.rst', // reStructuredText 文件
'.php', // PHP 脚本文件,包含嵌入的 HTML
'.js', // JavaScript 文件(部分是文本,部分可能包含代码)
'.ts', // TypeScript 文件
'.jsp', // JavaServer Pages 文件
'.aspx', // ASP.NET 文件
'.bat', // Windows 批处理文件
'.sh', // Unix/Linux Shell 脚本文件
'.py', // Python 脚本文件
'.rb', // Ruby 脚本文件
'.pl', // Perl 脚本文件
'.sql', // SQL 脚本文件
'.css', // Cascading Style Sheets 文件
'.less', // Less CSS 预处理器文件
'.scss', // Sass CSS 预处理器文件
'.sass', // Sass 文件
'.styl', // Stylus CSS 预处理器文件
'.coffee', // CoffeeScript 文件
'.ino', // Arduino 代码文件
'.asm', // Assembly 语言文件
'.go', // Go 语言文件
'.scala', // Scala 语言文件
'.swift', // Swift 语言文件
'.kt', // Kotlin 语言文件
'.rs', // Rust 语言文件
'.lua', // Lua 语言文件
'.groovy', // Groovy 语言文件
'.dart', // Dart 语言文件
'.hs', // Haskell 语言文件
'.clj', // Clojure 语言文件
'.cljs', // ClojureScript 语言文件
'.elm', // Elm 语言文件
'.erl', // Erlang 语言文件
'.ex', // Elixir 语言文件
'.exs', // Elixir 脚本文件
'.pug', // Pug (formerly Jade) 模板文件
'.haml', // Haml 模板文件
'.slim', // Slim 模板文件
'.tpl', // 模板文件(通用)
'.ejs', // Embedded JavaScript 模板文件
'.hbs', // Handlebars 模板文件
'.mustache', // Mustache 模板文件
'.jade', // Jade 模板文件 (已重命名为 Pug)
'.twig', // Twig 模板文件
'.blade', // Blade 模板文件 (Laravel)
'.vue', // Vue.js 单文件组件
'.jsx', // React JSX 文件
'.tsx', // React TSX 文件
'.graphql', // GraphQL 查询语言文件
'.gql', // GraphQL 查询语言文件
'.proto', // Protocol Buffers 文件
'.thrift', // Thrift 文件
'.toml', // TOML 配置文件
'.edn', // Clojure 数据表示文件
'.cake', // CakePHP 配置文件
'.ctp', // CakePHP 视图文件
'.cfm', // ColdFusion 标记语言文件
'.cfc', // ColdFusion 组件文件
'.m', // Objective-C 源文件
'.mm', // Objective-C++ 源文件
'.gradle', // Gradle 构建文件
'.groovy', // Gradle 构建文件
'.kts', // Kotlin Script 文件
'.java', // Java 代码文件
'.cs', // C# 代码文件
'.cpp', // C++ 代码文件
'.c', // C++ 代码文件
'.h' // C++ 头文件
]
export const ZOOM_SHORTCUTS = [
{
key: 'zoom_in',
shortcut: ['CommandOrControl', '='],
editable: false,
enabled: true,
system: true
},
{
key: 'zoom_out',
shortcut: ['CommandOrControl', '-'],
editable: false,
enabled: true,
system: true
},
{
key: 'zoom_reset',
shortcut: ['CommandOrControl', '0'],
editable: false,
enabled: true,
system: true
}
]

View File

@@ -0,0 +1,6 @@
export type LoaderReturn = {
entriesAdded: number
uniqueId: string
uniqueIds: string[]
loaderType: string
}

View File

@@ -0,0 +1,118 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CherryStudio 许可协议-ZH/EN</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet" />
</head>
<body class="bg-gray-100 p-8">
<div class="container mx-auto bg-white p-6 rounded shadow-lg">
<h1 class="text-3xl font-bold mb-6 text-center">Cherry Studio 许可协议</h1>
<div class="mb-8">
<h2 class="text-2xl font-semibold mb-4">许可协议</h2>
<p class="mb-4">
本软件采用 <strong>Apache License 2.0</strong> 许可。除 Apache License 2.0 规定的条款外,您在使用 Cherry
Studio 时还应遵守以下附加条款:
</p>
<h3 class="text-xl font-semibold mb-2">一. 商用许可</h3>
<ol class="list-decimal list-inside mb-4">
<li><strong>免费商用</strong>:用户在不修改代码的情况下,可以免费用于商业目的。</li>
<li>
<strong>商业授权</strong>:如果您满足以下任意条件之一,需取得商业授权:
<ol class="list-decimal list-inside ml-4">
<li>对本软件进行二次修改、开发包括但不限于修改应用名称、logo、代码以及功能</li>
<li>为企业客户提供多租户服务,且该服务支持 10 人或以上的使用。</li>
<li>预装或集成到硬件设备或产品中进行捆绑销售。</li>
<li>政府或教育机构的大规模采购项目,特别是涉及安全、数据隐私等敏感需求时。</li>
</ol>
</li>
</ol>
<h3 class="text-xl font-semibold mb-2">二. 贡献者协议</h3>
<ol class="list-decimal list-inside mb-4">
<li><strong>许可调整</strong>:生产者有权根据需要对开源协议进行调整,使其更加严格或宽松。</li>
<li><strong>商业用途</strong>:您贡献的代码可能会被用于商业用途,包括但不限于云业务运营。</li>
</ol>
<h3 class="text-xl font-semibold mb-2">三. 其他条款</h3>
<ol class="list-decimal list-inside mb-4">
<li>本协议条款的解释权归 Cherry Studio 开发者所有。</li>
<li>本协议可能根据实际情况进行更新,更新时将通过本软件通知用户。</li>
</ol>
<p class="mb-4">如有任何问题或需申请商业授权,请联系 Cherry Studio 开发团队。</p>
<p>
除上述特定条件外,其他所有权利和限制均遵循 Apache License 2.0。有关 Apache License 2.0 的详细信息,请访问
<a href="http://www.apache.org/licenses/LICENSE-2.0"
class="text-blue-500 underline">http://www.apache.org/licenses/LICENSE-2.0</a>
</p>
</div>
<h1 class="text-3xl font-bold mb-6 text-center">Cherry Studio License</h1>
<div class="mb-8">
<h2 class="text-2xl font-semibold mb-4">License Agreement</h2>
<p class="mb-4">
This software is licensed under the <strong>Apache License 2.0</strong>. In addition to the terms of the
Apache License 2.0, the following additional terms apply to the use of Cherry Studio:
</p>
<h3 class="text-xl font-semibold mb-2">I. Commercial Use License</h3>
<ol class="list-decimal list-inside mb-4">
<li>
<strong>Free Commercial Use</strong>: Users can use the software for commercial purposes without
modifying
the code.
</li>
<li>
<strong>Commercial License Required</strong>: A commercial license is required if any of the
following
conditions are met:
<ol class="list-decimal list-inside ml-4">
<li>
You modify, develop, or alter the software, including but not limited to changes to the
application
name, logo, code, or functionality.
</li>
<li>You provide multi-tenant services to enterprise customers with 10 or more users.</li>
<li>
You pre-install or integrate the software into hardware devices or products and bundle it
for sale.
</li>
<li>
You are engaging in large-scale procurement for government or educational institutions,
especially
involving security, data privacy, or other sensitive requirements.
</li>
</ol>
</li>
</ol>
<h3 class="text-xl font-semibold mb-2">II. Contributor Agreement</h3>
<ol class="list-decimal list-inside mb-4">
<li>
<strong>License Adjustment</strong>: The producer reserves the right to adjust the open-source
license as
needed, making it stricter or more lenient.
</li>
<li>
<strong>Commercial Use</strong>: Any code you contribute may be used for commercial purposes,
including but
not limited to cloud business operations.
</li>
</ol>
<h3 class="text-xl font-semibold mb-2">III. Other Terms</h3>
<ol class="list-decimal list-inside mb-4">
<li>The interpretation of these terms is subject to the discretion of Cherry Studio developers.</li>
<li>These terms may be updated, and users will be notified through the software when changes occur.</li>
</ol>
<p class="mb-4">
For any questions or to request a commercial license, please contact the Cherry Studio development team.
</p>
<p>
Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache
License 2.0. Detailed information about the Apache License 2.0 can be found at
<a href="http://www.apache.org/licenses/LICENSE-2.0"
class="text-blue-500 underline">http://www.apache.org/licenses/LICENSE-2.0</a>
</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,202 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Github Releases Timeline</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" />
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/typography@0.5.10/dist/typography.min.css"></script>
</head>
<body id="app">
<div :class="isDark ? 'dark-bg' : 'bg'" class="min-h-screen">
<div class="max-w-3xl mx-auto py-12 px-4">
<h1 class="text-3xl font-bold mb-8" :class="isDark ? 'text-white' : 'text-gray-900'">Release Timeline</h1>
<!-- Loading状态 -->
<div v-if="loading" class="text-center py-8">
<div class="inline-block animate-spin rounded-full h-8 w-8 border-4"
:class="isDark ? 'border-gray-700 border-t-blue-500' : 'border-gray-300 border-t-blue-500'"></div>
</div>
<!-- Error 状态 -->
<div v-else-if="error" class="text-red-500 text-center py-8">{{ error }}</div>
<!-- Release 列表 -->
<div v-else class="space-y-8">
<div v-for="release in releases" :key="release.id" class="relative pl-8"
:class="isDark ? 'border-l-2 border-gray-700' : 'border-l-2 border-gray-200'">
<div class="absolute -left-2 top-0 w-4 h-4 rounded-full bg-green-500"></div>
<div class="rounded-lg shadow-sm p-6 transition-shadow"
:class="isDark ? 'bg-black hover:shadow-md hover:shadow-black' : 'bg-white hover:shadow-md'">
<div class="flex items-start justify-between mb-4">
<div>
<h2 class="text-xl font-semibold" :class="isDark ? 'text-white' : 'text-gray-900'">
{{ release.name || release.tag_name }}
</h2>
<p class="text-sm mt-1" :class="isDark ? 'text-gray-400' : 'text-gray-500'">
{{ formatDate(release.published_at) }}
</p>
</div>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium"
:class="isDark ? 'bg-green-900 text-green-200' : 'bg-green-100 text-green-800'">
{{ release.tag_name }}
</span>
</div>
<div class="prose" :class="isDark ? 'text-gray-300 dark-prose' : 'text-gray-600'"
v-html="renderMarkdown(release.body)"></div>
</div>
</div>
</div>
</div>
</div>
<script>
const md = window.markdownit({
breaks: true,
linkify: true
})
const { createApp } = Vue
createApp({
data() {
return {
releases: [],
loading: true,
error: null,
isDark: false
}
},
methods: {
async fetchReleases() {
try {
this.loading = true
this.error = null
const response = await fetch('https://api.github.com/repos/kangfenmao/cherry-studio/releases')
if (!response.ok) {
throw new Error('Failed to fetch releases')
}
this.releases = await response.json()
} catch (err) {
this.error = 'Error loading releases: ' + err.message
} finally {
this.loading = false
}
},
formatDate(dateString) {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
},
renderMarkdown(content) {
if (!content) return ''
return md.render(content)
},
initTheme() {
// 从 URL 参数获取主题设置
const url = new URL(window.location.href)
const theme = url.searchParams.get('theme')
this.isDark = theme === 'dark'
}
},
mounted() {
this.initTheme()
this.fetchReleases()
}
}).mount('#app')
</script>
<style>
/* 基础的 Markdown 样式 */
.prose {
line-height: 1.6;
}
.prose h1 {
font-size: 1.5em;
margin: 1em 0;
}
.prose h2 {
font-size: 1.3em;
margin: 0.8em 0;
}
.prose h3 {
font-size: 1.1em;
margin: 0.6em 0;
}
.prose ul {
list-style-type: disc;
margin-left: 1.5em;
margin-bottom: 1em;
}
.prose ol {
list-style-type: decimal;
margin-left: 1.5em;
margin-bottom: 1em;
}
.prose code {
padding: 0.2em 0.4em;
border-radius: 0.2em;
font-size: 0.9em;
}
.dark .prose code {
background-color: #1f2937;
}
.prose code {
background-color: #f3f4f6;
}
.prose pre code {
display: block;
padding: 1em;
overflow-x: auto;
}
.prose a {
color: #3b82f6;
text-decoration: underline;
}
.dark .prose a {
color: #60a5fa;
}
.prose blockquote {
border-left: 4px solid #e5e7eb;
padding-left: 1em;
margin: 1em 0;
}
.dark .prose blockquote {
border-left-color: #374151;
color: #9ca3af;
}
.dark .prose {
color: #e5e7eb;
}
.dark-bg {
background-color: #151515;
}
.bg {
background-color: #f2f2f2;
}
</style>
</body>
</html>

9087
resources/data/agents.json Normal file

File diff suppressed because one or more lines are too long

68
resources/graphrag.html Normal file
View File

@@ -0,0 +1,68 @@
<head>
<style>
body {
margin: 0;
}
</style>
<script src="https://unpkg.com/3d-force-graph"></script>
</head>
<body>
<div id="3d-graph"></div>
<script src="./js/bridge.js"></script>
<script type="module">
import { getQueryParam } from './js/utils.js'
const apiUrl = getQueryParam('apiUrl')
const modelId = getQueryParam('modelId')
const jsonUrl = `${apiUrl}/v1/global_graph/${modelId}`
const infoCard = document.createElement('div')
infoCard.style.position = 'fixed'
infoCard.style.backgroundColor = 'rgba(255, 255, 255, 0.9)'
infoCard.style.padding = '8px'
infoCard.style.borderRadius = '4px'
infoCard.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)'
infoCard.style.fontSize = '12px'
infoCard.style.maxWidth = '200px'
infoCard.style.display = 'none'
infoCard.style.zIndex = '1000'
document.body.appendChild(infoCard)
document.addEventListener('mousemove', (event) => {
infoCard.style.left = `${event.clientX + 10}px`
infoCard.style.top = `${event.clientY + 10}px`
})
const elem = document.getElementById('3d-graph')
const Graph = ForceGraph3D()(elem)
.jsonUrl(jsonUrl)
.nodeAutoColorBy((node) => node.properties.type || 'default')
.nodeVal((node) => node.properties.degree)
.linkWidth((link) => link.properties.weight)
.onNodeHover((node) => {
if (node) {
infoCard.innerHTML = `
<div style="font-weight: bold; margin-bottom: 4px; color: #333;">
${node.properties.title}
</div>
<div style="color: #666;">
${node.properties.description}
</div>`
infoCard.style.display = 'block'
} else {
infoCard.style.display = 'none'
}
})
.onNodeClick((node) => {
const url = `${apiUrl}/v1/references/${modelId}/entities/${node.properties.human_readable_id}`
window.api.minApp({
url,
windowOptions: {
title: node.properties.title,
width: 500,
height: 800
}
})
})
</script>
</body>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

36
resources/js/bridge.js Normal file
View File

@@ -0,0 +1,36 @@
;(() => {
let messageId = 0
const pendingCalls = new Map()
function api(method, ...args) {
const id = messageId++
return new Promise((resolve, reject) => {
pendingCalls.set(id, { resolve, reject })
window.parent.postMessage({ id, type: 'api-call', method, args }, '*')
})
}
window.addEventListener('message', (event) => {
if (event.data.type === 'api-response') {
const { id, result, error } = event.data
const pendingCall = pendingCalls.get(id)
if (pendingCall) {
if (error) {
pendingCall.reject(new Error(error))
} else {
pendingCall.resolve(result)
}
pendingCalls.delete(id)
}
}
})
window.api = new Proxy(
{},
{
get: (target, prop) => {
return (...args) => api(prop, ...args)
}
}
)
})()

5
resources/js/utils.js Normal file
View File

@@ -0,0 +1,5 @@
export function getQueryParam(paramName) {
const url = new URL(window.location.href)
const params = new URLSearchParams(url.search)
return params.get(paramName)
}

117
resources/textMonitor.swift Normal file
View File

@@ -0,0 +1,117 @@
import Cocoa
import Foundation
class TextSelectionObserver: NSObject {
let workspace = NSWorkspace.shared
var lastSelectedText: String?
override init() {
super.init()
//
let observer = NSWorkspace.shared.notificationCenter
observer.addObserver(
self,
selector: #selector(handleSelectionChange),
name: NSWorkspace.didActivateApplicationNotification,
object: nil
)
//
var axObserver: AXObserver?
let error = AXObserverCreate(getpid(), { observer, element, notification, userData in
let selfPointer = userData!.load(as: TextSelectionObserver.self)
selfPointer.checkSelectedText()
}, &axObserver)
if error == .success, let axObserver = axObserver {
CFRunLoopAddSource(
RunLoop.main.getCFRunLoop(),
AXObserverGetRunLoopSource(axObserver),
.defaultMode
)
//
updateActiveAppObserver(axObserver)
}
}
@objc func handleSelectionChange(_ notification: Notification) {
//
var axObserver: AXObserver?
let error = AXObserverCreate(getpid(), { _, _, _, _ in }, &axObserver)
if error == .success, let axObserver = axObserver {
updateActiveAppObserver(axObserver)
}
}
func updateActiveAppObserver(_ axObserver: AXObserver) {
guard let app = workspace.frontmostApplication else { return }
let pid = app.processIdentifier
let element = AXUIElementCreateApplication(pid)
//
AXObserverAddNotification(
axObserver,
element,
kAXSelectedTextChangedNotification as CFString,
UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
)
}
func checkSelectedText() {
if let text = getSelectedText() {
if text.count > 0 && text != lastSelectedText {
print(text)
fflush(stdout)
lastSelectedText = text
}
}
}
func getSelectedText() -> String? {
guard let app = NSWorkspace.shared.frontmostApplication else { return nil }
let pid = app.processIdentifier
let axApp = AXUIElementCreateApplication(pid)
var focusedElement: AnyObject?
// Get focused element
let result = AXUIElementCopyAttributeValue(axApp, kAXFocusedUIElementAttribute as CFString, &focusedElement)
guard result == .success else { return nil }
// Try different approaches to get selected text
var selectedText: AnyObject?
// First try: Direct selected text
var textResult = AXUIElementCopyAttributeValue(focusedElement as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedText)
// Second try: Selected text in text area
if textResult != .success {
var selectedTextRange: AnyObject?
textResult = AXUIElementCopyAttributeValue(focusedElement as! AXUIElement, kAXSelectedTextRangeAttribute as CFString, &selectedTextRange)
if textResult == .success {
textResult = AXUIElementCopyAttributeValue(focusedElement as! AXUIElement, kAXValueAttribute as CFString, &selectedText)
}
}
// Third try: Get selected text from parent element
if textResult != .success {
var parent: AnyObject?
if AXUIElementCopyAttributeValue(focusedElement as! AXUIElement, kAXParentAttribute as CFString, &parent) == .success {
textResult = AXUIElementCopyAttributeValue(parent as! AXUIElement, kAXSelectedTextAttribute as CFString, &selectedText)
}
}
guard textResult == .success, let text = selectedText as? String else { return nil }
return text
}
}
let observer = TextSelectionObserver()
signal(SIGINT) { _ in
exit(0)
}
RunLoop.main.run()

45
scripts/after-pack.js Normal file
View File

@@ -0,0 +1,45 @@
const { Arch } = require('electron-builder')
const { default: removeLocales } = require('./remove-locales')
const fs = require('fs')
const path = require('path')
exports.default = async function (context) {
await removeLocales(context)
const platform = context.packager.platform.name
const arch = context.arch
if (platform === 'mac') {
const node_modules_path = path.join(
context.appOutDir,
'Cherry Studio.app',
'Contents',
'Resources',
'app.asar.unpacked',
'node_modules'
)
removeDifferentArchNodeFiles(node_modules_path, '@libsql', arch === Arch.arm64 ? ['darwin-arm64'] : ['darwin-x64'])
}
if (platform === 'linux') {
const node_modules_path = path.join(context.appOutDir, 'resources', 'app.asar.unpacked', 'node_modules')
const _arch = arch === Arch.arm64 ? ['linux-arm64-gnu', 'linux-arm64-musl'] : ['linux-x64-gnu', 'linux-x64-musl']
removeDifferentArchNodeFiles(node_modules_path, '@libsql', _arch)
}
if (platform === 'windows') {
const node_modules_path = path.join(context.appOutDir, 'resources', 'app.asar.unpacked', 'node_modules')
removeDifferentArchNodeFiles(node_modules_path, '@libsql', ['win32-x64-msvc'])
}
}
function removeDifferentArchNodeFiles(nodeModulesPath, packageName, arch) {
const modulePath = path.join(nodeModulesPath, packageName)
const dirs = fs.readdirSync(modulePath)
dirs
.filter((dir) => !arch.includes(dir))
.forEach((dir) => {
fs.rmSync(path.join(modulePath, dir), { recursive: true, force: true })
console.log(`Removed dir: ${dir}`, arch)
})
}

40
scripts/build-npm.js Normal file
View File

@@ -0,0 +1,40 @@
const { downloadNpmPackage } = require('./utils')
async function downloadNpm(platform) {
if (!platform || platform === 'mac') {
downloadNpmPackage(
'@libsql/darwin-arm64',
'https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.4.7.tgz'
)
downloadNpmPackage('@libsql/darwin-x64', 'https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.4.7.tgz')
}
if (!platform || platform === 'linux') {
downloadNpmPackage(
'@libsql/linux-arm64-gnu',
'https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.4.7.tgz'
)
downloadNpmPackage(
'@libsql/linux-arm64-musl',
'https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.4.7.tgz'
)
downloadNpmPackage(
'@libsql/linux-x64-gnu',
'https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.4.7.tgz'
)
downloadNpmPackage(
'@libsql/linux-x64-musl',
'https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.4.7.tgz'
)
}
if (!platform || platform === 'windows') {
downloadNpmPackage(
'@libsql/win32-x64-msvc',
'https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.4.7.tgz'
)
}
}
const platformArg = process.argv[2]
downloadNpm(platformArg)

104
scripts/check-i18n.js Normal file
View File

@@ -0,0 +1,104 @@
'use strict'
Object.defineProperty(exports, '__esModule', { value: true })
var fs = require('fs')
var path = require('path')
var translationsDir = path.join(__dirname, '../src/renderer/src/i18n/locales')
var baseLocale = 'zh-CN'
var baseFileName = ''.concat(baseLocale, '.json')
var baseFilePath = path.join(translationsDir, baseFileName)
/**
* 递归同步 target 对象,使其与 template 对象保持一致
* 1. 如果 template 中存在 target 中缺少的 key则添加'[to be translated]'
* 2. 如果 target 中存在 template 中不存在的 key则删除
* 3. 对于子对象,递归同步
*
* @param target 目标对象(需要更新的语言对象)
* @param template 主模板对象(中文)
* @returns 返回是否对 target 进行了更新
*/
function syncRecursively(target, template) {
var isUpdated = false
// 添加 template 中存在但 target 中缺少的 key
for (var key in template) {
if (!(key in target)) {
target[key] =
typeof template[key] === 'object' && template[key] !== null ? {} : '[to be translated]:'.concat(template[key])
console.log('\u6DFB\u52A0\u65B0\u5C5E\u6027\uFF1A'.concat(key))
isUpdated = true
}
if (typeof template[key] === 'object' && template[key] !== null) {
if (typeof target[key] !== 'object' || target[key] === null) {
target[key] = {}
isUpdated = true
}
// 递归同步子对象
var childUpdated = syncRecursively(target[key], template[key])
if (childUpdated) {
isUpdated = true
}
}
}
// 删除 target 中存在但 template 中没有的 key
for (var targetKey in target) {
if (!(targetKey in template)) {
console.log('\u79FB\u9664\u591A\u4F59\u5C5E\u6027\uFF1A'.concat(targetKey))
delete target[targetKey]
isUpdated = true
}
}
return isUpdated
}
function syncTranslations() {
if (!fs.existsSync(baseFilePath)) {
console.error(
'\u4E3B\u6A21\u677F\u6587\u4EF6 '.concat(
baseFileName,
' \u4E0D\u5B58\u5728\uFF0C\u8BF7\u68C0\u67E5\u8DEF\u5F84\u6216\u6587\u4EF6\u540D\u3002'
)
)
return
}
var baseContent = fs.readFileSync(baseFilePath, 'utf-8')
var baseJson = {}
try {
baseJson = JSON.parse(baseContent)
} catch (error) {
console.error('\u89E3\u6790 '.concat(baseFileName, ' \u51FA\u9519:'), error)
return
}
var files = fs.readdirSync(translationsDir).filter(function (file) {
return file.endsWith('.json') && file !== baseFileName
})
for (var _i = 0, files_1 = files; _i < files_1.length; _i++) {
var file = files_1[_i]
var filePath = path.join(translationsDir, file)
var targetJson = {}
try {
var fileContent = fs.readFileSync(filePath, 'utf-8')
targetJson = JSON.parse(fileContent)
} catch (error) {
console.error(
'\u89E3\u6790 '.concat(
file,
' \u51FA\u9519\uFF0C\u8DF3\u8FC7\u6B64\u6587\u4EF6\u3002\u9519\u8BEF\u4FE1\u606F:'
),
error
)
continue
}
var isUpdated = syncRecursively(targetJson, baseJson)
if (isUpdated) {
try {
fs.writeFileSync(filePath, JSON.stringify(targetJson, null, 2), 'utf-8')
console.log(
'\u6587\u4EF6 '.concat(file, ' \u5DF2\u66F4\u65B0\u540C\u6B65\u4E3B\u6A21\u677F\u7684\u5185\u5BB9\u3002')
)
} catch (error) {
console.error('\u5199\u5165 '.concat(file, ' \u51FA\u9519:'), error)
}
} else {
console.log('\u6587\u4EF6 '.concat(file, ' \u65E0\u9700\u66F4\u65B0\u3002'))
}
}
}
syncTranslations()

98
scripts/check-i18n.ts Normal file
View File

@@ -0,0 +1,98 @@
import * as fs from 'fs'
import * as path from 'path'
const translationsDir = path.join(__dirname, '../src/renderer/src/i18n/locales')
const baseLocale = 'zh-CN'
const baseFileName = `${baseLocale}.json`
const baseFilePath = path.join(translationsDir, baseFileName)
/**
* 递归同步 target 对象,使其与 template 对象保持一致
* 1. 如果 template 中存在 target 中缺少的 key则添加'[to be translated]'
* 2. 如果 target 中存在 template 中不存在的 key则删除
* 3. 对于子对象,递归同步
*
* @param target 目标对象(需要更新的语言对象)
* @param template 主模板对象(中文)
* @returns 返回是否对 target 进行了更新
*/
function syncRecursively(target: any, template: any): boolean {
let isUpdated = false
// 添加 template 中存在但 target 中缺少的 key
for (const key in template) {
if (!(key in target)) {
target[key] =
typeof template[key] === 'object' && template[key] !== null ? {} : `[to be translated]:${template[key]}`
console.log(`添加新属性:${key}`)
isUpdated = true
}
if (typeof template[key] === 'object' && template[key] !== null) {
if (typeof target[key] !== 'object' || target[key] === null) {
target[key] = {}
isUpdated = true
}
// 递归同步子对象
const childUpdated = syncRecursively(target[key], template[key])
if (childUpdated) {
isUpdated = true
}
}
}
// 删除 target 中存在但 template 中没有的 key
for (const targetKey in target) {
if (!(targetKey in template)) {
console.log(`移除多余属性:${targetKey}`)
delete target[targetKey]
isUpdated = true
}
}
return isUpdated
}
function syncTranslations() {
if (!fs.existsSync(baseFilePath)) {
console.error(`主模板文件 ${baseFileName} 不存在,请检查路径或文件名`)
return
}
const baseContent = fs.readFileSync(baseFilePath, 'utf-8')
let baseJson: Record<string, any> = {}
try {
baseJson = JSON.parse(baseContent)
} catch (error) {
console.error(`解析 ${baseFileName} 出错:`, error)
return
}
const files = fs.readdirSync(translationsDir).filter((file) => file.endsWith('.json') && file !== baseFileName)
for (const file of files) {
const filePath = path.join(translationsDir, file)
let targetJson: Record<string, any> = {}
try {
const fileContent = fs.readFileSync(filePath, 'utf-8')
targetJson = JSON.parse(fileContent)
} catch (error) {
console.error(`解析 ${file} 出错,跳过此文件。错误信息:`, error)
continue
}
const isUpdated = syncRecursively(targetJson, baseJson)
if (isUpdated) {
try {
fs.writeFileSync(filePath, JSON.stringify(targetJson, null, 2) + '\n', 'utf-8')
console.log(`文件 ${file} 已更新同步主模板的内容`)
} catch (error) {
console.error(`写入 ${file} 出错:`, error)
}
} else {
console.log(`文件 ${file} 无需更新`)
}
}
}
syncTranslations()

View File

@@ -0,0 +1,529 @@
// 配置信息
const config = {
R2_CUSTOM_DOMAIN: 'cherrystudio.ocool.online',
R2_BUCKET_NAME: 'cherrystudio',
// 缓存键名
CACHE_KEY: 'cherry-studio-latest-release',
VERSION_DB: 'versions.json',
LOG_FILE: 'logs.json',
MAX_LOGS: 1000 // 最多保存多少条日志
}
// Worker 入口函数
const worker = {
// 定时器触发配置
scheduled: {
cron: '*/1 * * * *' // 每分钟执行一次
},
// 定时器执行函数 - 只负责检查和更新
async scheduled(event, env, ctx) {
try {
await initDataFiles(env)
console.log('开始定时检查新版本...')
// 使用新的 checkNewRelease 函数
await checkNewRelease(env)
} catch (error) {
console.error('定时任务执行失败:', error)
}
},
// HTTP 请求处理函数 - 只负责返回数据
async fetch(request, env, ctx) {
if (!env || !env.R2_BUCKET) {
return new Response(
JSON.stringify({
error: 'R2 存储桶未正确配置'
}),
{
status: 500,
headers: { 'Content-Type': 'application/json' }
}
)
}
const url = new URL(request.url)
const filename = url.pathname.slice(1)
try {
// 处理文件下载请求
if (filename) {
return await handleDownload(env, filename)
}
// 只返回缓存的版本信息
return await getCachedRelease(env)
} catch (error) {
return new Response(
JSON.stringify({
error: error.message,
stack: error.stack
}),
{
status: 500,
headers: { 'Content-Type': 'application/json' }
}
)
}
}
}
export default worker
/**
* 添加日志记录函数
*/
async function addLog(env, type, event, details = null) {
try {
const logFile = await env.R2_BUCKET.get(config.LOG_FILE)
let logs = { logs: [] }
if (logFile) {
logs = JSON.parse(await logFile.text())
}
logs.logs.unshift({
timestamp: new Date().toISOString(),
type,
event,
details
})
// 保持日志数量在限制内
if (logs.logs.length > config.MAX_LOGS) {
logs.logs = logs.logs.slice(0, config.MAX_LOGS)
}
await env.R2_BUCKET.put(config.LOG_FILE, JSON.stringify(logs, null, 2))
} catch (error) {
console.error('写入日志失败:', error)
}
}
/**
* 获取最新版本信息
*/
async function getLatestRelease(env) {
try {
const cached = await env.R2_BUCKET.get(config.CACHE_KEY)
if (!cached) {
// 如果缓存不存在,先检查版本数据库
const versionDB = await env.R2_BUCKET.get(config.VERSION_DB)
if (versionDB) {
const versions = JSON.parse(await versionDB.text())
if (versions.latestVersion) {
// 从版本数据库重建缓存
const latestVersion = versions.versions[versions.latestVersion]
const cacheData = {
version: latestVersion.version,
publishedAt: latestVersion.publishedAt,
changelog: latestVersion.changelog,
downloads: latestVersion.files
.filter((file) => file.uploaded)
.map((file) => ({
name: file.name,
url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`,
size: formatFileSize(file.size)
}))
}
// 更新缓存
await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData))
return new Response(JSON.stringify(cacheData), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
}
}
// 如果版本数据库也没有数据,才执行检查更新
const data = await checkNewRelease(env)
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
}
const data = await cached.text()
return new Response(data, {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
} catch (error) {
await addLog(env, 'ERROR', '获取版本信息失败', error.message)
return new Response(
JSON.stringify({
error: '获取版本信息失败: ' + error.message,
detail: '请稍<E8AFB7><E7A88D><EFBFBD>再试'
}),
{
status: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
}
)
}
}
// 修改下载处理函数,直接接收 env
async function handleDownload(env, filename) {
try {
const object = await env.R2_BUCKET.get(filename)
if (!object) {
return new Response('文件未找到', { status: 404 })
}
// 设置响应头
const headers = new Headers()
object.writeHttpMetadata(headers)
headers.set('etag', object.httpEtag)
headers.set('Content-Disposition', `attachment; filename="${filename}"`)
return new Response(object.body, {
headers
})
} catch (error) {
console.error('下载文件时发生错误:', error)
return new Response('获取文件失败', { status: 500 })
}
}
/**
* 根据文件扩展名获取对应的 Content-Type
*/
function getContentType(filename) {
const ext = filename.split('.').pop().toLowerCase()
const types = {
exe: 'application/x-msdownload', // Windows 可执行文件
dmg: 'application/x-apple-diskimage', // macOS 安装包
zip: 'application/zip', // 压缩包
AppImage: 'application/x-executable', // Linux 可执行文件
blockmap: 'application/octet-stream' // 更新文件
}
return types[ext] || 'application/octet-stream'
}
/**
* 格式化文件大小
* 将字节转换为人类可读的格式B, KB, MB, GB
*/
function formatFileSize(bytes) {
const units = ['B', 'KB', 'MB', 'GB']
let size = bytes
let unitIndex = 0
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024
unitIndex++
}
return `${size.toFixed(2)} ${units[unitIndex]}`
}
/**
* 版本号比较函数
* 用于对版本号进行排序
*/
function compareVersions(a, b) {
const partsA = a.replace('v', '').split('.')
const partsB = b.replace('v', '').split('.')
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
const numA = parseInt(partsA[i] || 0)
const numB = parseInt(partsB[i] || 0)
if (numA !== numB) {
return numA - numB
}
}
return 0
}
/**
* 初始化数据文件
*/
async function initDataFiles(env) {
try {
// 检查并初始化版本数据库
const versionDB = await env.R2_BUCKET.get(config.VERSION_DB)
if (!versionDB) {
const initialVersions = {
versions: {},
latestVersion: null,
lastChecked: new Date().toISOString()
}
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(initialVersions, null, 2))
await addLog(env, 'INFO', 'versions.json 初始化成功')
}
// 检查并初始化日志文件
const logFile = await env.R2_BUCKET.get(config.LOG_FILE)
if (!logFile) {
const initialLogs = {
logs: [
{
timestamp: new Date().toISOString(),
type: 'INFO',
event: '系统初始化'
}
]
}
await env.R2_BUCKET.put(config.LOG_FILE, JSON.stringify(initialLogs, null, 2))
console.log('logs.json 初始化成功')
}
} catch (error) {
console.error('初始化数据文件失败:', error)
}
}
// 新增:只获取缓存的版本信息
async function getCachedRelease(env) {
try {
const cached = await env.R2_BUCKET.get(config.CACHE_KEY)
if (!cached) {
// 如果缓存不存在,从版本数据库获取
const versionDB = await env.R2_BUCKET.get(config.VERSION_DB)
if (versionDB) {
const versions = JSON.parse(await versionDB.text())
if (versions.latestVersion) {
const latestVersion = versions.versions[versions.latestVersion]
const cacheData = {
version: latestVersion.version,
publishedAt: latestVersion.publishedAt,
changelog: latestVersion.changelog,
downloads: latestVersion.files
.filter((file) => file.uploaded)
.map((file) => ({
name: file.name,
url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`,
size: formatFileSize(file.size)
}))
}
// 重建缓存
await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData))
return new Response(JSON.stringify(cacheData), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
}
}
// 如果没有任何数据,返回错误
return new Response(
JSON.stringify({
error: '没有可用的版本信息'
}),
{
status: 404,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
}
)
}
// 返回缓存数据
return new Response(await cached.text(), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
} catch (error) {
await addLog(env, 'ERROR', '获取缓存版本信息失败', error.message)
throw error
}
}
// 新增:只检查新版本并更新
async function checkNewRelease(env) {
try {
// 获取 GitHub 最新版本
const githubResponse = await fetch('https://api.github.com/repos/kangfenmao/cherry-studio/releases/latest', {
headers: { 'User-Agent': 'CloudflareWorker' }
})
if (!githubResponse.ok) {
throw new Error('GitHub API 请求失败')
}
const releaseData = await githubResponse.json()
const version = releaseData.tag_name
// 获取版本数据库
const versionDB = await env.R2_BUCKET.get(config.VERSION_DB)
let versions = { versions: {}, latestVersion: null, lastChecked: new Date().toISOString() }
if (versionDB) {
versions = JSON.parse(await versionDB.text())
}
// 移除版本检查,改为记录是否有文件更新的标志
let hasUpdates = false
if (versions.latestVersion !== version) {
await addLog(env, 'INFO', `发现新版本: ${version}`)
hasUpdates = true
} else {
await addLog(env, 'INFO', `版本 ${version} 文件完整性检查开始`)
}
// 准备新版本记录
const versionRecord = {
version,
publishedAt: releaseData.published_at,
uploadedAt: null,
files: releaseData.assets.map((asset) => ({
name: asset.name,
size: asset.size,
uploaded: false
})),
changelog: releaseData.body
}
// 检查并上传文件
for (const asset of releaseData.assets) {
try {
const existingFile = await env.R2_BUCKET.get(asset.name)
// 检查文件是否存在且大小是否一致
if (!existingFile || existingFile.size !== asset.size) {
hasUpdates = true
const response = await fetch(asset.browser_download_url)
if (!response.ok) {
throw new Error(`下载失败: HTTP ${response.status}`)
}
const file = await response.arrayBuffer()
await env.R2_BUCKET.put(asset.name, file, {
httpMetadata: { contentType: getContentType(asset.name) }
})
// 更新文件状态
const fileIndex = versionRecord.files.findIndex((f) => f.name === asset.name)
if (fileIndex !== -1) {
versionRecord.files[fileIndex].uploaded = true
}
await addLog(env, 'INFO', `文件${existingFile ? '更新' : '上传'}成功: ${asset.name}`)
} else {
// 文件存在且大小相同,标记为已上传
const fileIndex = versionRecord.files.findIndex((f) => f.name === asset.name)
if (fileIndex !== -1) {
versionRecord.files[fileIndex].uploaded = true
}
await addLog(env, 'INFO', `文件完整性验证通过: ${asset.name}`)
}
} catch (error) {
await addLog(env, 'ERROR', `文件处理失败: ${asset.name}`, error.message)
}
}
// 只有在有更新或是新版本时才更新数据库和缓存
if (hasUpdates) {
// 更新版本记录
versionRecord.uploadedAt = new Date().toISOString()
versions.versions[version] = versionRecord
versions.latestVersion = version
// 保存版本数据库
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(versions, null, 2))
// 更新缓存
const cacheData = {
version,
publishedAt: releaseData.published_at,
changelog: releaseData.body,
downloads: versionRecord.files
.filter((file) => file.uploaded)
.map((file) => ({
name: file.name,
url: `https://${config.R2_CUSTOM_DOMAIN}/${file.name}`,
size: formatFileSize(file.size)
}))
}
await env.R2_BUCKET.put(config.CACHE_KEY, JSON.stringify(cacheData))
await addLog(env, 'INFO', hasUpdates ? '更新完成' : '文件完整性检查完成')
// 清理旧版本
const versionList = Object.keys(versions.versions).sort((a, b) => compareVersions(b, a))
if (versionList.length > 2) {
// 获取需要保留的两个最新版本
const keepVersions = versionList.slice(0, 2)
// 获取所有需要删除的版本
const oldVersions = versionList.slice(2)
// 先获取 R2 桶中的所有文件列表
const allFiles = await listAllFiles(env)
// 获取需要保留的文件名列表
const keepFiles = new Set()
for (const keepVersion of keepVersions) {
const versionFiles = versions.versions[keepVersion].files
versionFiles.forEach((file) => keepFiles.add(file.name))
}
// 删除所有旧版本文件
for (const oldVersion of oldVersions) {
const oldFiles = versions.versions[oldVersion].files
for (const file of oldFiles) {
try {
if (file.uploaded) {
await env.R2_BUCKET.delete(file.name)
await addLog(env, 'INFO', `删除旧文件: ${file.name}`)
}
} catch (error) {
await addLog(env, 'ERROR', `删除旧文件失败: ${file.name}`, error.message)
}
}
delete versions.versions[oldVersion]
}
// 清理可能遗留的旧文件
for (const file of allFiles) {
if (!keepFiles.has(file.name)) {
try {
await env.R2_BUCKET.delete(file.name)
await addLog(env, 'INFO', `删除遗留文件: ${file.name}`)
} catch (error) {
await addLog(env, 'ERROR', `删除遗留文件失败: ${file.name}`, error.message)
}
}
}
// 保存更新后的版本数据库
await env.R2_BUCKET.put(config.VERSION_DB, JSON.stringify(versions, null, 2))
}
} else {
await addLog(env, 'INFO', '所有文件完整性检查通过,无需更新')
}
return hasUpdates ? cacheData : null
} catch (error) {
await addLog(env, 'ERROR', '检查新版本失败', error.message)
throw error
}
}
// 新增:获取 R2 桶中的所有文件列表
async function listAllFiles(env) {
const files = []
let cursor
do {
const listed = await env.R2_BUCKET.list({ cursor, include: ['customMetadata'] })
files.push(...listed.objects)
cursor = listed.cursor
} while (cursor)
return files
}

58
scripts/remove-locales.js Normal file
View File

@@ -0,0 +1,58 @@
const fs = require('fs')
const path = require('path')
exports.default = async function (context) {
const platform = context.packager.platform.name
// 根据平台确定 locales 目录位置
let resourceDirs = []
if (platform === 'mac') {
// macOS 的语言文件位置
resourceDirs = [
path.join(context.appOutDir, 'Cherry Studio.app', 'Contents', 'Resources'),
path.join(
context.appOutDir,
'Cherry Studio.app',
'Contents',
'Frameworks',
'Electron Framework.framework',
'Resources'
)
]
} else {
// Windows 和 Linux 的语言文件位置
resourceDirs = [path.join(context.appOutDir, 'locales')]
}
// 处理每个资源目录
for (const resourceDir of resourceDirs) {
if (!fs.existsSync(resourceDir)) {
console.log(`Resource directory not found: ${resourceDir}, skipping...`)
continue
}
// 读取所有文件和目录
const items = fs.readdirSync(resourceDir)
// 遍历并删除不需要的语言文件
for (const item of items) {
if (platform === 'mac') {
// 在 macOS 上检查 .lproj 目录
if (item.endsWith('.lproj') && !item.match(/^(en|zh|ru)/)) {
const dirPath = path.join(resourceDir, item)
fs.rmSync(dirPath, { recursive: true, force: true })
console.log(`Removed locale directory: ${item} from ${resourceDir}`)
}
} else {
// 其他平台处理 .pak 文件
if (!item.match(/^(en|zh|ru)/)) {
const filePath = path.join(resourceDir, item)
fs.unlinkSync(filePath)
console.log(`Removed locale file: ${item} from ${resourceDir}`)
}
}
}
}
console.log('Locale cleanup completed!')
}

58
scripts/replace-spaces.js Normal file
View File

@@ -0,0 +1,58 @@
// replaceSpaces.js
const fs = require('fs')
const path = require('path')
const directory = 'dist'
// 处理文件名中的空格
function replaceFileNames() {
fs.readdir(directory, (err, files) => {
if (err) throw err
files.forEach((file) => {
const oldPath = path.join(directory, file)
const newPath = path.join(directory, file.replace(/ /g, '-'))
fs.stat(oldPath, (err, stats) => {
if (err) throw err
if (stats.isFile() && oldPath !== newPath) {
fs.rename(oldPath, newPath, (err) => {
if (err) throw err
console.log(`Renamed: ${oldPath} -> ${newPath}`)
})
}
})
})
})
}
function replaceYmlContent() {
fs.readdir(directory, (err, files) => {
if (err) throw err
files.forEach((file) => {
if (path.extname(file).toLowerCase() === '.yml') {
const filePath = path.join(directory, file)
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) throw err
// 替换内容
const newContent = data.replace(/Cherry Studio-/g, 'Cherry-Studio-')
// 写回文件
fs.writeFile(filePath, newContent, 'utf8', (err) => {
if (err) throw err
console.log(`Updated content in: ${filePath}`)
})
})
}
})
})
}
// 执行两个操作
replaceFileNames()
replaceYmlContent()

View File

@@ -1,26 +0,0 @@
// replaceSpaces.js
const fs = require('fs')
const path = require('path')
const directory = 'dist'
fs.readdir(directory, (err, files) => {
if (err) throw err
files.forEach((file) => {
const oldPath = path.join(directory, file)
const newPath = path.join(directory, file.replace(/ /g, '-'))
fs.stat(oldPath, (err, stats) => {
if (err) throw err
if (stats.isFile() && oldPath !== newPath) {
fs.rename(oldPath, newPath, (err) => {
if (err) throw err
console.log(`Renamed: ${oldPath} -> ${newPath}`)
})
}
})
})
})

39
scripts/utils.js Normal file
View File

@@ -0,0 +1,39 @@
const fs = require('fs')
const path = require('path')
const os = require('os')
function downloadNpmPackage(packageName, url) {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'npm-download-'))
const targetDir = path.join('./node_modules/', packageName)
const filename = packageName.replace('/', '-') + '.tgz'
// Skip if directory already exists
if (fs.existsSync(targetDir)) {
console.log(`${targetDir} already exists, skipping download...`)
return
}
try {
console.log(`Downloading ${packageName}...`, url)
const { execSync } = require('child_process')
execSync(`curl --fail -o ${filename} ${url}`)
console.log(`Extracting ${filename}...`)
execSync(`tar -xvf ${filename}`)
execSync(`rm -rf ${filename}`)
execSync(`mv package ${targetDir}`)
} catch (error) {
console.error(`Error processing ${packageName}: ${error.message}`)
if (fs.existsSync(filename)) {
fs.unlinkSync(filename)
}
throw error
}
fs.rmSync(tempDir, { recursive: true, force: true })
}
module.exports = {
downloadNpmPackage
}

View File

@@ -1,15 +1,23 @@
import Store from 'electron-store'
import { app } from 'electron'
export const appConfig = new Store()
import { getDataPath } from './utils'
const isDev = process.env.NODE_ENV === 'development'
if (isDev) {
app.setPath('userData', app.getPath('userData') + 'Dev')
}
export const DATA_PATH = getDataPath()
export const titleBarOverlayDark = {
height: 41,
height: 40,
color: '#00000000',
symbolColor: '#ffffff'
}
export const titleBarOverlayLight = {
height: 41,
height: 40,
color: '#00000000',
symbolColor: '#000000'
}

3
src/main/constant.ts Normal file
View File

@@ -0,0 +1,3 @@
export const isMac = process.platform === 'darwin'
export const isWin = process.platform === 'win32'
export const isLinux = process.platform === 'linux'

9
src/main/electron.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
declare global {
namespace Electron {
interface App {
isQuitting: boolean
}
}
}
export {}

9
src/main/env.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
VITE_MAIN_BUNDLE_ID: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

View File

@@ -1,24 +0,0 @@
import { dialog, SaveDialogOptions, SaveDialogReturnValue } from 'electron'
import logger from 'electron-log'
import { writeFile } from 'fs'
export async function saveFile(_: Electron.IpcMainInvokeEvent, fileName: string, content: string): Promise<void> {
try {
const options: SaveDialogOptions = {
title: '保存文件',
defaultPath: fileName
}
const result: SaveDialogReturnValue = await dialog.showSaveDialog(options)
if (!result.canceled && result.filePath) {
writeFile(result.filePath, content, { encoding: 'utf-8' }, (err) => {
if (err) {
logger.error('[IPC - Error]', 'An error occurred saving the file:', err)
}
})
}
} catch (err) {
logger.error('[IPC - Error]', 'An error occurred saving the file:', err)
}
}

View File

@@ -1,154 +1,82 @@
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
import * as Sentry from '@sentry/electron/main'
import { app, BrowserWindow, ipcMain, Menu, MenuItem, session, shell } from 'electron'
import { electronApp, optimizer } from '@electron-toolkit/utils'
import { app } from 'electron'
import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer'
import windowStateKeeper from 'electron-window-state'
import { join } from 'path'
import icon from '../../resources/icon.png?asset'
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
import { saveFile } from './event'
import AppUpdater from './updater'
import { registerIpc } from './ipc'
import { registerShortcuts } from './services/ShortcutService'
import { TrayService } from './services/TrayService'
import { windowService } from './services/WindowService'
import { updateUserDataPath } from './utils/upgrade'
function createWindow() {
// Load the previous state with fallback to defaults
const mainWindowState = windowStateKeeper({
defaultWidth: 1080,
defaultHeight: 670
})
// Check for single instance lock
if (!app.requestSingleInstanceLock()) {
app.quit()
process.exit(0)
} else {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
const theme = appConfig.get('theme') || 'light'
app.whenReady().then(async () => {
await updateUserDataPath()
// Create the browser window.
const mainWindow = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth: 1080,
minHeight: 500,
show: true,
autoHideMenuBar: true,
transparent: process.platform === 'darwin',
vibrancy: 'fullscreen-ui',
titleBarStyle: 'hidden',
titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight,
trafficLightPosition: { x: 8, y: 12 },
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
webSecurity: false
// devTools: !app.isPackaged,
// Register custom protocol
if (!app.isDefaultProtocolClient('cherrystudio')) {
app.setAsDefaultProtocolClient('cherrystudio')
}
// Handle protocol open
app.on('open-url', (event, url) => {
event.preventDefault()
const parsedUrl = new URL(url)
if (parsedUrl.pathname === 'siliconflow.oauth.login') {
const code = parsedUrl.searchParams.get('code')
if (code) {
// Handle the OAuth code here
console.log('OAuth code received:', code)
// You can send this code to your renderer process via IPC if needed
}
}
})
// Set app user model id for windows
electronApp.setAppUserModelId(import.meta.env.VITE_MAIN_BUNDLE_ID || 'com.kangfenmao.CherryStudio')
const mainWindow = windowService.createMainWindow()
new TrayService()
app.on('activate', function () {
const mainWindow = windowService.getMainWindow()
if (!mainWindow || mainWindow.isDestroyed()) {
windowService.createMainWindow()
} else {
windowService.showMainWindow()
}
})
registerShortcuts(mainWindow)
registerIpc(mainWindow, app)
if (process.env.NODE_ENV === 'development') {
installExtension(REDUX_DEVTOOLS)
.then((name) => console.log(`Added Extension: ${name}`))
.catch((err) => console.log('An error occurred: ', err))
}
})
mainWindowState.manage(mainWindow)
mainWindow.webContents.on('context-menu', () => {
const menu = new Menu()
menu.append(new MenuItem({ label: '复制', role: 'copy', sublabel: '⌘ + C' }))
menu.append(new MenuItem({ label: '粘贴', role: 'paste', sublabel: '⌘ + V' }))
menu.append(new MenuItem({ label: '剪切', role: 'cut', sublabel: '⌘ + X' }))
menu.append(new MenuItem({ type: 'separator' }))
menu.append(new MenuItem({ label: '全选', role: 'selectAll', sublabel: '⌘ + A' }))
menu.popup()
// Listen for second instance
app.on('second-instance', () => {
windowService.showMainWindow()
})
mainWindow.webContents.on('will-navigate', (event, url) => {
event.preventDefault()
shell.openExternal(url)
})
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
return mainWindow
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
// Set app user model id for windows
electronApp.setAppUserModelId('com.kangfenmao.CherryStudio')
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
app.on('before-quit', () => {
app.isQuitting = true
})
const mainWindow = createWindow()
const { autoUpdater } = new AppUpdater(mainWindow)
// IPC
ipcMain.handle('get-app-info', () => ({
version: app.getVersion(),
isPackaged: app.isPackaged
}))
ipcMain.handle('open-website', (_, url: string) => {
shell.openExternal(url)
})
ipcMain.handle('set-proxy', (_, proxy: string) => {
session.defaultSession.setProxy(proxy ? { proxyRules: proxy } : {})
})
ipcMain.handle('save-file', saveFile)
ipcMain.handle('set-theme', (_, theme: 'light' | 'dark') => {
appConfig.set('theme', theme)
mainWindow?.setTitleBarOverlay &&
mainWindow.setTitleBarOverlay(theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight)
})
// 触发检查更新(此方法用于被渲染线程调用,例如页面点击检查更新按钮来调用此方法)
ipcMain.handle('check-for-update', async () => {
autoUpdater.logger?.info('触发检查更新')
return {
currentVersion: autoUpdater.currentVersion,
update: await autoUpdater.checkForUpdates()
}
})
installExtension(REDUX_DEVTOOLS)
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// In this file you can include the rest of your app"s specific main process
// code. You can also put them in separate files and require them here.
Sentry.init({
dsn: 'https://f0e972deff79c2df3e887e232d8a46a3@o4507610668007424.ingest.us.sentry.io/4507610670563328'
})
// In this file you can include the rest of your app"s specific main process
// code. You can also put them in separate files and require them here.
}

210
src/main/ipc.ts Normal file
View File

@@ -0,0 +1,210 @@
import fs from 'node:fs'
import path from 'node:path'
import { Shortcut, ThemeMode } from '@types'
import { BrowserWindow, ipcMain, ProxyConfig, session, shell } from 'electron'
import log from 'electron-log'
import { titleBarOverlayDark, titleBarOverlayLight } from './config'
import AppUpdater from './services/AppUpdater'
import BackupManager from './services/BackupManager'
import { configManager } from './services/ConfigManager'
import { ExportService } from './services/ExportService'
import FileService from './services/FileService'
import FileStorage from './services/FileStorage'
import { GeminiService } from './services/GeminiService'
import KnowledgeService from './services/KnowledgeService'
import { registerShortcuts, unregisterAllShortcuts } from './services/ShortcutService'
import { TrayService } from './services/TrayService'
import { windowService } from './services/WindowService'
import { getResourcePath } from './utils'
import { decrypt } from './utils/aes'
import { encrypt } from './utils/aes'
import { compress, decompress } from './utils/zip'
const fileManager = new FileStorage()
const backupManager = new BackupManager()
const exportService = new ExportService(fileManager)
export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
const { autoUpdater } = new AppUpdater(mainWindow)
ipcMain.handle('app:info', () => ({
version: app.getVersion(),
isPackaged: app.isPackaged,
appPath: app.getAppPath(),
filesPath: path.join(app.getPath('userData'), 'Data', 'Files'),
appDataPath: app.getPath('userData'),
resourcesPath: getResourcePath(),
logsPath: log.transports.file.getFile().path
}))
ipcMain.handle('app:proxy', async (_, proxy: string) => {
const sessions = [session.defaultSession, session.fromPartition('persist:webview')]
const proxyConfig: ProxyConfig = proxy === 'system' ? { mode: 'system' } : proxy ? { proxyRules: proxy } : {}
await Promise.all(sessions.map((session) => session.setProxy(proxyConfig)))
})
ipcMain.handle('app:reload', () => mainWindow.reload())
ipcMain.handle('open:website', (_, url: string) => shell.openExternal(url))
// language
ipcMain.handle('app:set-language', (_, language) => {
configManager.setLanguage(language)
})
// tray
ipcMain.handle('app:set-tray', (_, isActive: boolean) => {
configManager.setTray(isActive)
})
ipcMain.handle('app:restart-tray', () => TrayService.getInstance().restartTray())
ipcMain.handle('config:set', (_, key: string, value: any) => {
configManager.set(key, value)
})
ipcMain.handle('config:get', (_, key: string) => {
return configManager.get(key)
})
// theme
ipcMain.handle('app:set-theme', (_, theme: ThemeMode) => {
configManager.setTheme(theme)
mainWindow?.setTitleBarOverlay &&
mainWindow.setTitleBarOverlay(theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight)
})
// clear cache
ipcMain.handle('app:clear-cache', async () => {
const sessions = [session.defaultSession, session.fromPartition('persist:webview')]
try {
await Promise.all(
sessions.map(async (session) => {
await session.clearCache()
await session.clearStorageData({
storages: ['cookies', 'filesystem', 'shadercache', 'websql', 'serviceworkers', 'cachestorage']
})
})
)
await fileManager.clearTemp()
await fs.writeFileSync(log.transports.file.getFile().path, '')
return { success: true }
} catch (error: any) {
log.error('Failed to clear cache:', error)
return { success: false, error: error.message }
}
})
// check for update
ipcMain.handle('app:check-for-update', async () => {
const update = await autoUpdater.checkForUpdates()
return {
currentVersion: autoUpdater.currentVersion,
updateInfo: update?.updateInfo
}
})
// zip
ipcMain.handle('zip:compress', (_, text: string) => compress(text))
ipcMain.handle('zip:decompress', (_, text: Buffer) => decompress(text))
// backup
ipcMain.handle('backup:backup', backupManager.backup)
ipcMain.handle('backup:restore', backupManager.restore)
ipcMain.handle('backup:backupToWebdav', backupManager.backupToWebdav)
ipcMain.handle('backup:restoreFromWebdav', backupManager.restoreFromWebdav)
// file
ipcMain.handle('file:open', fileManager.open)
ipcMain.handle('file:openPath', fileManager.openPath)
ipcMain.handle('file:save', fileManager.save)
ipcMain.handle('file:select', fileManager.selectFile)
ipcMain.handle('file:upload', fileManager.uploadFile)
ipcMain.handle('file:clear', fileManager.clear)
ipcMain.handle('file:read', fileManager.readFile)
ipcMain.handle('file:delete', fileManager.deleteFile)
ipcMain.handle('file:get', fileManager.getFile)
ipcMain.handle('file:selectFolder', fileManager.selectFolder)
ipcMain.handle('file:create', fileManager.createTempFile)
ipcMain.handle('file:write', fileManager.writeFile)
ipcMain.handle('file:saveImage', fileManager.saveImage)
ipcMain.handle('file:base64Image', fileManager.base64Image)
ipcMain.handle('file:download', fileManager.downloadFile)
ipcMain.handle('file:copy', fileManager.copyFile)
ipcMain.handle('file:binaryFile', fileManager.binaryFile)
// fs
ipcMain.handle('fs:read', FileService.readFile)
// minapp
ipcMain.handle('minapp', (_, args) => {
windowService.createMinappWindow({
url: args.url,
parent: mainWindow,
windowOptions: {
...mainWindow.getBounds(),
...args.windowOptions
}
})
})
// export
ipcMain.handle('export:word', exportService.exportToWord)
// open path
ipcMain.handle('open:path', async (_, path: string) => {
await shell.openPath(path)
})
// shortcuts
ipcMain.handle('shortcuts:update', (_, shortcuts: Shortcut[]) => {
configManager.setShortcuts(shortcuts)
// Refresh shortcuts registration
if (mainWindow) {
unregisterAllShortcuts()
registerShortcuts(mainWindow)
}
})
// knowledge base
ipcMain.handle('knowledge-base:create', KnowledgeService.create)
ipcMain.handle('knowledge-base:reset', KnowledgeService.reset)
ipcMain.handle('knowledge-base:delete', KnowledgeService.delete)
ipcMain.handle('knowledge-base:add', KnowledgeService.add)
ipcMain.handle('knowledge-base:remove', KnowledgeService.remove)
ipcMain.handle('knowledge-base:search', KnowledgeService.search)
// window
ipcMain.handle('window:set-minimum-size', (_, width: number, height: number) => {
mainWindow?.setMinimumSize(width, height)
})
ipcMain.handle('window:reset-minimum-size', () => {
mainWindow?.setMinimumSize(1080, 600)
const [width, height] = mainWindow?.getSize() ?? [1080, 600]
if (width < 1080) {
mainWindow?.setSize(1080, height)
}
})
// gemini
ipcMain.handle('gemini:upload-file', GeminiService.uploadFile)
ipcMain.handle('gemini:base64-file', GeminiService.base64File)
ipcMain.handle('gemini:retrieve-file', GeminiService.retrieveFile)
ipcMain.handle('gemini:list-files', GeminiService.listFiles)
ipcMain.handle('gemini:delete-file', GeminiService.deleteFile)
// mini window
ipcMain.handle('miniwindow:show', () => windowService.showMiniWindow())
ipcMain.handle('miniwindow:hide', () => windowService.hideMiniWindow())
ipcMain.handle('miniwindow:close', () => windowService.closeMiniWindow())
ipcMain.handle('miniwindow:toggle', () => windowService.toggleMiniWindow())
// aes
ipcMain.handle('aes:encrypt', (_, text: string, secretKey: string, iv: string) => encrypt(text, secretKey, iv))
ipcMain.handle('aes:decrypt', (_, encryptedData: string, iv: string, secretKey: string) =>
decrypt(encryptedData, iv, secretKey)
)
}

View File

@@ -0,0 +1,22 @@
import * as fs from 'node:fs'
import { JsonLoader } from '@llm-tools/embedjs'
/**
* Drafts 应用导出的笔记文件加载器
* 原始文件是一个 JSON 数组。每条笔记只保留 content、tags、modified_at 三个字段
*/
export class DraftsExportLoader extends JsonLoader {
constructor(filePath: string) {
const fileContent = fs.readFileSync(filePath, 'utf-8')
const rawJson = JSON.parse(fileContent) as any[]
const json = rawJson.map((item) => {
return {
content: item.content?.replace(/\n/g, '<br>'),
tags: item.tags,
modified_at: item.created_at
}
})
super({ object: json })
}
}

View File

@@ -0,0 +1,228 @@
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters'
import { BaseLoader } from '@llm-tools/embedjs-interfaces'
import { cleanString } from '@llm-tools/embedjs-utils'
import Logger from 'electron-log'
import EPub from 'epub'
import * as fs from 'fs'
/**
* epub 加载器的配置选项
*/
interface EpubLoaderOptions {
/** epub 文件路径 */
filePath: string
/** 文本分块大小 */
chunkSize: number
/** 分块重叠大小 */
chunkOverlap: number
}
/**
* epub 文件的元数据信息
*/
interface EpubMetadata {
/** 作者显示名称(例如:"Lewis Carroll" */
creator?: string
/** 作者规范化名称,用于排序和索引(例如:"Carroll, Lewis" */
creatorFileAs?: string
/** 书籍标题(例如:"Alice's Adventures in Wonderland" */
title?: string
/** 语言代码(例如:"en" 或 "zh-CN" */
language?: string
/** 主题或分类(例如:"Fantasy"、"Fiction" */
subject?: string
/** 创建日期(例如:"2024-02-14" */
date?: string
/** 书籍描述或简介 */
description?: string
}
/**
* epub 章节信息
*/
interface EpubChapter {
/** 章节 ID */
id: string
/** 章节标题 */
title?: string
/** 章节顺序 */
order?: number
}
/**
* epub 文件加载器
* 用于解析 epub 电子书文件,提取文本内容和元数据
*/
export class EpubLoader extends BaseLoader<Record<string, string | number | boolean>, Record<string, unknown>> {
protected filePath: string
protected chunkSize: number
protected chunkOverlap: number
private extractedText: string
private metadata: EpubMetadata | null
/**
* 创建 epub 加载器实例
* @param options 加载器配置选项
*/
constructor(options: EpubLoaderOptions) {
super(options.filePath, {
chunkSize: options.chunkSize,
chunkOverlap: options.chunkOverlap
})
this.filePath = options.filePath
this.chunkSize = options.chunkSize
this.chunkOverlap = options.chunkOverlap
this.extractedText = ''
this.metadata = null
}
/**
* 等待 epub 文件初始化完成
* epub 库使用事件机制,需要等待 'end' 事件触发后才能访问文件内容
* @param epub epub 实例
* @returns 元数据和章节信息
*/
private waitForEpubInit(epub: any): Promise<{ metadata: EpubMetadata; chapters: EpubChapter[] }> {
return new Promise((resolve, reject) => {
epub.on('end', () => {
// 提取元数据
const metadata: EpubMetadata = {
creator: epub.metadata.creator,
creatorFileAs: epub.metadata.creatorFileAs,
title: epub.metadata.title,
language: epub.metadata.language,
subject: epub.metadata.subject,
date: epub.metadata.date,
description: epub.metadata.description
}
// 提取章节信息
const chapters: EpubChapter[] = epub.flow.map((chapter: any, index: number) => ({
id: chapter.id,
title: chapter.title || `Chapter ${index + 1}`,
order: index + 1
}))
resolve({ metadata, chapters })
})
epub.on('error', (error: Error) => {
reject(error)
})
epub.parse()
})
}
/**
* 获取章节内容
* @param epub epub 实例
* @param chapterId 章节 ID
* @returns 章节文本内容
*/
private getChapter(epub: any, chapterId: string): Promise<string> {
return new Promise((resolve, reject) => {
epub.getChapter(chapterId, (error: Error | null, text: string) => {
if (error) {
reject(error)
} else {
resolve(text)
}
})
})
}
/**
* 从 epub 文件中提取文本内容
* 1. 检查文件是否存在
* 2. 初始化 epub 并获取元数据
* 3. 遍历所有章节并提取文本
* 4. 清理 HTML 标签
* 5. 合并所有章节文本
*/
private async extractTextFromEpub() {
try {
// 检查文件是否存在
if (!fs.existsSync(this.filePath)) {
throw new Error(`File not found: ${this.filePath}`)
}
const epub = new EPub(this.filePath)
// 等待 epub 初始化完成并获取元数据
const { metadata, chapters } = await this.waitForEpubInit(epub)
this.metadata = metadata
if (!epub.flow || epub.flow.length === 0) {
throw new Error('No content found in epub file')
}
const chapterTexts: string[] = []
// 遍历所有章节
for (const chapter of chapters) {
try {
const content = await this.getChapter(epub, chapter.id)
if (!content) {
continue
}
// 移除 HTML 标签并清理文本
const text = content
.replace(/<[^>]*>/g, ' ') // 移除所有 HTML 标签
.replace(/\s+/g, ' ') // 将多个空白字符替换为单个空格
.trim() // 移除首尾空白
if (text) {
chapterTexts.push(text)
}
} catch (error) {
Logger.error(`[EpubLoader] Error processing chapter ${chapter.id}:`, error)
}
}
// 使用双换行符连接所有章节文本
this.extractedText = chapterTexts.join('\n\n')
} catch (error) {
Logger.error('[EpubLoader] Error in extractTextFromEpub:', error)
throw error
}
}
/**
* 生成文本块
* 重写 BaseLoader 的方法,将提取的文本分割成适当大小的块
* 每个块都包含源文件和元数据信息
*/
override async *getUnfilteredChunks() {
// 如果还没有提取文本,先提取
if (!this.extractedText) {
await this.extractTextFromEpub()
}
Logger.info('[EpubLoader] 书名:', this.metadata?.title || '未知书名', ' 文本大小:', this.extractedText.length)
// 创建文本分块器
const chunker = new RecursiveCharacterTextSplitter({
chunkSize: this.chunkSize,
chunkOverlap: this.chunkOverlap
})
// 清理并分割文本
const chunks = await chunker.splitText(cleanString(this.extractedText))
// 为每个文本块添加元数据
for (const chunk of chunks) {
yield {
pageContent: chunk,
metadata: {
source: this.filePath,
title: this.metadata?.title || '',
creator: this.metadata?.creator || '',
language: this.metadata?.language || ''
}
}
}
}
}

159
src/main/loader/index.ts Normal file
View File

@@ -0,0 +1,159 @@
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 { LoaderReturn } from '@shared/config/types'
import { FileType, KnowledgeBaseParams } from '@types'
import Logger from 'electron-log'
import { DraftsExportLoader } from './draftsExportLoader'
import { EpubLoader } from './epubLoader'
import { OdLoader, OdType } from './odLoader'
// embedjs内置loader类型
const commonExts = ['.pdf', '.csv', '.docx', '.pptx', '.xlsx', '.md']
export async function addOdLoader(
ragApplication: RAGApplication,
file: FileType,
base: KnowledgeBaseParams,
forceReload: boolean
): Promise<AddLoaderReturn> {
const loaderMap: Record<string, OdType> = {
'.odt': OdType.OdtLoader,
'.ods': OdType.OdsLoader,
'.odp': OdType.OdpLoader
}
const odType = loaderMap[file.ext]
if (!odType) {
throw new Error('Unknown odType')
}
return ragApplication.addLoader(
new OdLoader({
odType,
filePath: file.path,
chunkSize: base.chunkSize,
chunkOverlap: base.chunkOverlap
}) as any,
forceReload
)
}
export async function addFileLoader(
ragApplication: RAGApplication,
file: FileType,
base: KnowledgeBaseParams,
forceReload: boolean
): Promise<LoaderReturn> {
// 内置类型
if (commonExts.includes(file.ext)) {
const loaderReturn = await ragApplication.addLoader(
// @ts-ignore LocalPathLoader
new LocalPathLoader({ path: file.path, chunkSize: base.chunkSize, chunkOverlap: base.chunkOverlap }) as any,
forceReload
)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
} as LoaderReturn
}
// 自定义类型
if (['.odt', '.ods', '.odp'].includes(file.ext)) {
const loaderReturn = await addOdLoader(ragApplication, file, base, forceReload)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
} as LoaderReturn
}
// epub 文件处理
if (file.ext === '.epub') {
const loaderReturn = await ragApplication.addLoader(
new EpubLoader({
filePath: file.path,
chunkSize: base.chunkSize ?? 1000,
chunkOverlap: base.chunkOverlap ?? 200
}) as any,
forceReload
)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
} as LoaderReturn
}
// DraftsExport类型 (file.ext会自动转换成小写)
if (['.draftsexport'].includes(file.ext)) {
const loaderReturn = await ragApplication.addLoader(new DraftsExportLoader(file.path) as any, forceReload)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
}
}
const fileContent = fs.readFileSync(file.path, 'utf-8')
// HTML类型
if (['.html', '.htm'].includes(file.ext)) {
const loaderReturn = await ragApplication.addLoader(
new WebLoader({
urlOrContent: fileContent,
chunkSize: base.chunkSize,
chunkOverlap: base.chunkOverlap
}) as any,
forceReload
)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
}
}
// JSON类型
if (['.json'].includes(file.ext)) {
let jsonObject = {}
let jsonParsed = true
try {
jsonObject = JSON.parse(fileContent)
} catch (error) {
jsonParsed = false
Logger.warn('[KnowledgeBase] failed parsing json file, failling back to text processing:', file.path, error)
}
if (jsonParsed) {
const loaderReturn = await ragApplication.addLoader(new JsonLoader({ object: jsonObject }))
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
}
}
}
// 文本类型
const loaderReturn = await ragApplication.addLoader(
new TextLoader({ text: fileContent, chunkSize: base.chunkSize, chunkOverlap: base.chunkOverlap }) as any,
forceReload
)
Logger.info('[KnowledgeBase] processing file', file.path)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
} as LoaderReturn
}

View File

@@ -0,0 +1,71 @@
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'
export enum OdType {
OdtLoader = 'OdtLoader',
OdsLoader = 'OdsLoader',
OdpLoader = 'OdpLoader',
undefined = 'undefined'
}
export class OdLoader<OdType> extends BaseLoader<{ type: string }> {
private readonly odType: OdType
private readonly filePath: string
private extractedText: string
private config: OfficeParserConfig
constructor({
odType,
filePath,
chunkSize,
chunkOverlap
}: {
odType: OdType
filePath: string
chunkSize?: number
chunkOverlap?: number
}) {
super(`${odType}_${md5(filePath)}`, { filePath }, chunkSize ?? 1000, chunkOverlap ?? 0)
this.odType = odType
this.filePath = filePath
this.extractedText = ''
this.config = {
newlineDelimiter: ' ',
ignoreNotes: false
}
}
private async extractTextFromOdt() {
try {
this.extractedText = await parseOfficeAsync(this.filePath, this.config)
} catch (err) {
console.error('odLoader error', err)
throw err
}
}
override async *getUnfilteredChunks() {
if (!this.extractedText) {
await this.extractTextFromOdt()
}
const chunker = new RecursiveCharacterTextSplitter({
chunkSize: this.chunkSize,
chunkOverlap: this.chunkOverlap
})
const chunks = await chunker.splitText(cleanString(this.extractedText))
for (const chunk of chunks) {
yield {
pageContent: chunk,
metadata: {
type: this.odType as string,
source: this.filePath
}
}
}
}
}

BIN
src/main/resources/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

View File

@@ -0,0 +1,84 @@
import { app, BrowserWindow, dialog } from 'electron'
import logger from 'electron-log'
import { AppUpdater as _AppUpdater, autoUpdater, UpdateInfo } from 'electron-updater'
import icon from '../../../build/icon.png?asset'
export default class AppUpdater {
autoUpdater: _AppUpdater = autoUpdater
constructor(mainWindow: BrowserWindow) {
logger.transports.file.level = 'info'
autoUpdater.logger = logger
autoUpdater.forceDevUpdateConfig = !app.isPackaged
autoUpdater.autoDownload = true
// 检测下载错误
autoUpdater.on('error', (error) => {
logger.error('更新异常', error)
mainWindow.webContents.send('update-error', error)
})
autoUpdater.on('update-available', (releaseInfo: UpdateInfo) => {
logger.info('检测到新版本', releaseInfo)
mainWindow.webContents.send('update-available', releaseInfo)
})
// 检测到不需要更新时
autoUpdater.on('update-not-available', () => {
mainWindow.webContents.send('update-not-available')
})
// 更新下载进度
autoUpdater.on('download-progress', (progress) => {
mainWindow.webContents.send('download-progress', progress)
})
// 当需要更新的内容下载完成后
autoUpdater.on('update-downloaded', (releaseInfo: UpdateInfo) => {
mainWindow.webContents.send('update-downloaded')
logger.info('下载完成,询问用户是否更新', releaseInfo)
dialog
.showMessageBox({
type: 'info',
title: '安装更新',
icon,
message: `新版本 ${releaseInfo.version} 已准备就绪`,
detail: this.formatReleaseNotes(releaseInfo.releaseNotes),
buttons: ['稍后安装', '立即安装'],
defaultId: 1,
cancelId: 0
})
.then(({ response }) => {
if (response === 1) {
app.isQuitting = true
setImmediate(() => autoUpdater.quitAndInstall())
} else {
mainWindow.webContents.send('update-downloaded-cancelled')
}
})
})
this.autoUpdater = autoUpdater
}
private formatReleaseNotes(releaseNotes: string | ReleaseNoteInfo[] | null | undefined): string {
if (!releaseNotes) {
return '暂无更新说明'
}
if (typeof releaseNotes === 'string') {
return releaseNotes
}
return releaseNotes.map((note) => note.note).join('\n')
}
}
interface ReleaseNoteInfo {
readonly version: string
readonly note: string | null
}

View File

@@ -0,0 +1,121 @@
import { WebDavConfig } from '@types'
import AdmZip from 'adm-zip'
import { app } from 'electron'
import Logger from 'electron-log'
import * as fs from 'fs-extra'
import * as path from 'path'
import WebDav from './WebDav'
class BackupManager {
private tempDir = path.join(app.getPath('temp'), 'cherry-studio', 'backup', 'temp')
private backupDir = path.join(app.getPath('temp'), 'cherry-studio', 'backup')
constructor() {
this.backup = this.backup.bind(this)
this.restore = this.restore.bind(this)
this.backupToWebdav = this.backupToWebdav.bind(this)
this.restoreFromWebdav = this.restoreFromWebdav.bind(this)
}
async backup(
_: Electron.IpcMainInvokeEvent,
fileName: string,
data: string,
destinationPath: string = this.backupDir
): Promise<string> {
try {
await fs.ensureDir(this.tempDir)
// 将 data 写入临时文件
const tempDataPath = path.join(this.tempDir, 'data.json')
await fs.writeFile(tempDataPath, data)
// 复制 Data 目录到临时目录
const sourcePath = path.join(app.getPath('userData'), 'Data')
const tempDataDir = path.join(this.tempDir, 'Data')
await fs.copy(sourcePath, tempDataDir)
// 使用 adm-zip 创建压缩文件
const zip = new AdmZip()
zip.addLocalFolder(this.tempDir)
const backupedFilePath = path.join(destinationPath, fileName)
zip.writeZip(backupedFilePath)
// 清理临时目录
await fs.remove(this.tempDir)
Logger.log('Backup completed successfully')
return backupedFilePath
} catch (error) {
Logger.error('Backup failed:', error)
throw error
}
}
async restore(_: Electron.IpcMainInvokeEvent, backupPath: string): Promise<string> {
try {
// 创建临时目录
await fs.ensureDir(this.tempDir)
Logger.log('[backup] step 1: unzip backup file', this.tempDir)
// 使用 adm-zip 解压
const zip = new AdmZip(backupPath)
zip.extractAllTo(this.tempDir, true) // true 表示覆盖已存在的文件
Logger.log('[backup] step 2: read data.json')
// 读取 data.json
const dataPath = path.join(this.tempDir, 'data.json')
const data = await fs.readFile(dataPath, 'utf-8')
Logger.log('[backup] step 3: restore Data directory')
// 恢复 Data 目录
const sourcePath = path.join(this.tempDir, 'Data')
const destPath = path.join(app.getPath('userData'), 'Data')
await fs.remove(destPath)
await fs.copy(sourcePath, destPath)
Logger.log('[backup] step 4: clean up temp directory')
// 清理临时目录
await fs.remove(this.tempDir)
Logger.log('[backup] step 5: Restore completed successfully')
return data
} catch (error) {
Logger.error('[backup] Restore failed:', error)
await fs.remove(this.tempDir).catch(() => {})
throw error
}
}
async backupToWebdav(_: Electron.IpcMainInvokeEvent, data: string, webdavConfig: WebDavConfig) {
const filename = 'cherry-studio.backup.zip'
const backupedFilePath = await this.backup(_, filename, data)
const webdavClient = new WebDav(webdavConfig)
return await webdavClient.putFileContents(filename, fs.createReadStream(backupedFilePath), {
overwrite: true
})
}
async restoreFromWebdav(_: Electron.IpcMainInvokeEvent, webdavConfig: WebDavConfig) {
const filename = 'cherry-studio.backup.zip'
const webdavClient = new WebDav(webdavConfig)
const retrievedFile = await webdavClient.getFileContents(filename)
const backupedFilePath = path.join(this.backupDir, filename)
if (!fs.existsSync(this.backupDir)) {
fs.mkdirSync(this.backupDir, { recursive: true })
}
await fs.writeFileSync(backupedFilePath, retrievedFile as Buffer)
return await this.restore(_, backupedFilePath)
}
}
export default BackupManager

View File

@@ -0,0 +1,74 @@
interface CacheItem<T> {
data: T
timestamp: number
duration: number
}
export class CacheService {
private static cache: Map<string, CacheItem<any>> = new Map()
/**
* Set cache
* @param key Cache key
* @param data Cache data
* @param duration Cache duration (in milliseconds)
*/
static set<T>(key: string, data: T, duration: number): void {
this.cache.set(key, {
data,
timestamp: Date.now(),
duration
})
}
/**
* Get cache
* @param key Cache key
* @returns Returns data if cache exists and not expired, otherwise returns null
*/
static get<T>(key: string): T | null {
const item = this.cache.get(key)
if (!item) return null
const now = Date.now()
if (now - item.timestamp > item.duration) {
this.remove(key)
return null
}
return item.data
}
/**
* Remove specific cache
* @param key Cache key
*/
static remove(key: string): void {
this.cache.delete(key)
}
/**
* Clear all cache
*/
static clear(): void {
this.cache.clear()
}
/**
* Check if cache exists and is valid
* @param key Cache key
* @returns boolean
*/
static has(key: string): boolean {
const item = this.cache.get(key)
if (!item) return false
const now = Date.now()
if (now - item.timestamp > item.duration) {
this.remove(key)
return false
}
return true
}
}

View File

@@ -0,0 +1,118 @@
import { debounce, getResourcePath } from '@main/utils'
import { exec } from 'child_process'
import { screen } from 'electron'
import path from 'path'
import { windowService } from './WindowService'
export default class ClipboardMonitor {
private platform: string
private lastText: string
private user32: any
private observer: any
public onTextSelected: (text: string) => void
constructor() {
this.platform = process.platform
this.lastText = ''
this.onTextSelected = debounce((text: string) => this.handleTextSelected(text), 550)
if (this.platform === 'win32') {
this.setupWindows()
} else if (this.platform === 'darwin') {
this.setupMacOS()
}
}
setupMacOS() {
// 使用 Swift 脚本来监听文本选择
const scriptPath = path.join(getResourcePath(), 'textMonitor.swift')
// 启动 Swift 进程来监听文本选择
const process = exec(`swift ${scriptPath}`)
process?.stdout?.on('data', (data: string) => {
console.log('[ClipboardMonitor] MacOS data:', data)
const text = data.toString().trim()
if (text && text !== this.lastText) {
this.lastText = text
this.onTextSelected(text)
}
})
process.on('error', (error) => {
console.error('[ClipboardMonitor] MacOS error:', error)
})
}
setupWindows() {
// 使用 Windows API 监听文本选择事件
const ffi = require('ffi-napi')
const ref = require('ref-napi')
this.user32 = new ffi.Library('user32', {
SetWinEventHook: ['pointer', ['uint32', 'uint32', 'pointer', 'pointer', 'uint32', 'uint32', 'uint32']],
UnhookWinEvent: ['bool', ['pointer']]
})
// 定义事件常量
const EVENT_OBJECT_SELECTION = 0x8006
const WINEVENT_OUTOFCONTEXT = 0x0000
const WINEVENT_SKIPOWNTHREAD = 0x0001
const WINEVENT_SKIPOWNPROCESS = 0x0002
// 创建回调函数
const callback = ffi.Callback('void', ['pointer', 'uint32', 'pointer', 'long', 'long', 'uint32', 'uint32'], () => {
this.getSelectedText()
})
// 设置事件钩子
this.observer = this.user32.SetWinEventHook(
EVENT_OBJECT_SELECTION,
EVENT_OBJECT_SELECTION,
ref.NULL,
callback,
0,
0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD | WINEVENT_SKIPOWNPROCESS
)
}
getSelectedText() {
// Get selected text
if (this.platform === 'win32') {
const ref = require('ref-napi')
if (this.user32.OpenClipboard(ref.NULL)) {
// Get clipboard content
const text = this.user32.GetClipboardData(1) // CF_TEXT = 1
this.user32.CloseClipboard()
if (text && text !== this.lastText) {
this.lastText = text
this.onTextSelected(text)
}
}
}
}
private handleTextSelected(text: string) {
if (!text) return
console.debug('[ClipboardMonitor] handleTextSelected', text)
windowService.setLastSelectedText(text)
const mousePosition = screen.getCursorScreenPoint()
windowService.showSelectionMenu({
x: mousePosition.x,
y: mousePosition.y + 10
})
}
dispose() {
if (this.platform === 'win32' && this.observer) {
this.user32.UnhookWinEvent(this.observer)
}
}
}

View File

@@ -0,0 +1,112 @@
import { ZOOM_SHORTCUTS } from '@shared/config/constant'
import { LanguageVarious, Shortcut, ThemeMode } from '@types'
import { app } from 'electron'
import Store from 'electron-store'
import { locales } from '../utils/locales'
export class ConfigManager {
private store: Store
private subscribers: Map<string, Array<(newValue: any) => void>> = new Map()
constructor() {
this.store = new Store()
}
getLanguage(): LanguageVarious {
const locale = Object.keys(locales).includes(app.getLocale()) ? app.getLocale() : 'en-US'
return this.store.get('language', locale) as LanguageVarious
}
setLanguage(theme: LanguageVarious) {
this.store.set('language', theme)
}
getTheme(): ThemeMode {
return this.store.get('theme', ThemeMode.light) as ThemeMode
}
setTheme(theme: ThemeMode) {
this.store.set('theme', theme)
}
getTray(): boolean {
return !!this.store.get('tray', true)
}
setTray(value: boolean) {
this.store.set('tray', value)
this.notifySubscribers('tray', value)
}
getZoomFactor(): number {
return this.store.get('zoomFactor', 1) as number
}
setZoomFactor(factor: number) {
this.store.set('zoomFactor', factor)
this.notifySubscribers('zoomFactor', factor)
}
subscribe<T>(key: string, callback: (newValue: T) => void) {
if (!this.subscribers.has(key)) {
this.subscribers.set(key, [])
}
this.subscribers.get(key)!.push(callback)
}
unsubscribe<T>(key: string, callback: (newValue: T) => void) {
const subscribers = this.subscribers.get(key)
if (subscribers) {
this.subscribers.set(
key,
subscribers.filter((subscriber) => subscriber !== callback)
)
}
}
private notifySubscribers<T>(key: string, newValue: T) {
const subscribers = this.subscribers.get(key)
if (subscribers) {
subscribers.forEach((subscriber) => subscriber(newValue))
}
}
getShortcuts() {
return this.store.get('shortcuts', ZOOM_SHORTCUTS) as Shortcut[] | []
}
setShortcuts(shortcuts: Shortcut[]) {
this.store.set(
'shortcuts',
shortcuts.filter((shortcut) => shortcut.system)
)
this.notifySubscribers('shortcuts', shortcuts)
}
getClickTrayToShowQuickAssistant(): boolean {
return this.store.get('clickTrayToShowQuickAssistant', false) as boolean
}
setClickTrayToShowQuickAssistant(value: boolean) {
this.store.set('clickTrayToShowQuickAssistant', value)
}
getEnableQuickAssistant(): boolean {
return this.store.get('enableQuickAssistant', false) as boolean
}
setEnableQuickAssistant(value: boolean) {
this.store.set('enableQuickAssistant', value)
}
set(key: string, value: any) {
this.store.set(key, value)
}
get(key: string) {
return this.store.get(key)
}
}
export const configManager = new ConfigManager()

View File

@@ -0,0 +1,222 @@
/* eslint-disable no-case-declarations */
// ExportService
import { AlignmentType, BorderStyle, Document, HeadingLevel, Packer, Paragraph, ShadingType, TextRun } from 'docx'
import { dialog } from 'electron'
import Logger from 'electron-log'
import MarkdownIt from 'markdown-it'
import FileStorage from './FileStorage'
export class ExportService {
private fileManager: FileStorage
private md: MarkdownIt
constructor(fileManager: FileStorage) {
this.fileManager = fileManager
this.md = new MarkdownIt()
}
private convertMarkdownToDocxElements(markdown: string) {
const tokens = this.md.parse(markdown, {})
const elements: any[] = []
let listLevel = 0
const processInlineTokens = (tokens: any[]): TextRun[] => {
const runs: TextRun[] = []
for (const token of tokens) {
switch (token.type) {
case 'text':
runs.push(new TextRun(token.content))
break
case 'strong':
runs.push(new TextRun({ text: token.content, bold: true }))
break
case 'em':
runs.push(new TextRun({ text: token.content, italics: true }))
break
case 'code_inline':
runs.push(new TextRun({ text: token.content, font: 'Consolas', size: 20 }))
break
}
}
return runs
}
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
switch (token.type) {
case 'heading_open':
// 获取标题级别 (h1 -> h6)
const level = parseInt(token.tag.slice(1)) as 1 | 2 | 3 | 4 | 5 | 6
const headingText = tokens[i + 1].content
elements.push(
new Paragraph({
text: headingText,
heading: HeadingLevel[`HEADING_${level}`],
spacing: {
before: 240,
after: 120
}
})
)
i += 2 // 跳过内容标记和闭合标记
break
case 'paragraph_open':
const inlineTokens = tokens[i + 1].children || []
elements.push(
new Paragraph({
children: processInlineTokens(inlineTokens),
spacing: {
before: 120,
after: 120
}
})
)
i += 2
break
case 'bullet_list_open':
listLevel++
break
case 'bullet_list_close':
listLevel--
break
case 'list_item_open':
const itemInlineTokens = tokens[i + 2].children || []
elements.push(
new Paragraph({
children: [
new TextRun({ text: '•', bold: true }),
new TextRun({ text: '\t' }),
...processInlineTokens(itemInlineTokens)
],
indent: {
left: listLevel * 720
}
})
)
i += 3
break
case 'fence': // 代码块
const codeLines = token.content.split('\n')
elements.push(
new Paragraph({
children: codeLines.map(
(line) =>
new TextRun({
text: line + '\n',
font: 'Consolas',
size: 20,
break: 1
})
),
shading: {
type: ShadingType.SOLID,
color: 'F5F5F5'
},
spacing: {
before: 120,
after: 120
},
border: {
top: { style: BorderStyle.SINGLE, size: 1, color: 'DDDDDD' },
bottom: { style: BorderStyle.SINGLE, size: 1, color: 'DDDDDD' },
left: { style: BorderStyle.SINGLE, size: 1, color: 'DDDDDD' },
right: { style: BorderStyle.SINGLE, size: 1, color: 'DDDDDD' }
}
})
)
break
case 'hr':
elements.push(
new Paragraph({
children: [new TextRun({ text: '─'.repeat(50), color: '999999' })],
alignment: AlignmentType.CENTER
})
)
break
case 'blockquote_open':
const quoteText = tokens[i + 2].content
elements.push(
new Paragraph({
children: [
new TextRun({
text: quoteText,
italics: true
})
],
indent: {
left: 720
},
border: {
left: {
style: BorderStyle.SINGLE,
size: 3,
color: 'CCCCCC'
}
},
spacing: {
before: 120,
after: 120
}
})
)
i += 3
break
}
}
return elements
}
public exportToWord = async (_: Electron.IpcMainInvokeEvent, markdown: string, fileName: string): Promise<void> => {
try {
const elements = this.convertMarkdownToDocxElements(markdown)
const doc = new Document({
styles: {
paragraphStyles: [
{
id: 'Normal',
name: 'Normal',
run: {
size: 24,
font: 'Arial'
}
}
]
},
sections: [
{
properties: {},
children: elements
}
]
})
const buffer = await Packer.toBuffer(doc)
const filePath = dialog.showSaveDialogSync({
title: '保存文件',
filters: [{ name: 'Word Document', extensions: ['docx'] }],
defaultPath: fileName
})
if (filePath) {
await this.fileManager.writeFile(_, filePath, buffer)
Logger.info('[ExportService] Document exported successfully')
}
} catch (error) {
Logger.error('[ExportService] Export to Word failed:', error)
throw error
}
}
}

View File

@@ -0,0 +1,7 @@
import fs from 'node:fs'
export default class FileService {
public static async readFile(_: Electron.IpcMainInvokeEvent, path: string) {
return fs.readFileSync(path, 'utf8')
}
}

View File

@@ -0,0 +1,472 @@
import { getFileType } from '@main/utils/file'
import { documentExts, imageExts } from '@shared/config/constant'
import { FileType } from '@types'
import * as crypto from 'crypto'
import {
app,
dialog,
OpenDialogOptions,
OpenDialogReturnValue,
SaveDialogOptions,
SaveDialogReturnValue,
shell
} from 'electron'
import logger from 'electron-log'
import * as fs from 'fs'
import { writeFileSync } from 'fs'
import { readFile } from 'fs/promises'
import officeParser from 'officeparser'
import * as path from 'path'
import { chdir } from 'process'
import { v4 as uuidv4 } from 'uuid'
class FileStorage {
private storageDir = path.join(app.getPath('userData'), 'Data', 'Files')
private tempDir = path.join(app.getPath('temp'), 'CherryStudio')
constructor() {
this.initStorageDir()
}
private initStorageDir = (): void => {
if (!fs.existsSync(this.storageDir)) {
fs.mkdirSync(this.storageDir, { recursive: true })
}
if (!fs.existsSync(this.tempDir)) {
fs.mkdirSync(this.tempDir, { recursive: true })
}
}
private getFileHash = async (filePath: string): Promise<string> => {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('md5')
const stream = fs.createReadStream(filePath)
stream.on('data', (data) => hash.update(data))
stream.on('end', () => resolve(hash.digest('hex')))
stream.on('error', reject)
})
}
findDuplicateFile = async (filePath: string): Promise<FileType | null> => {
const stats = fs.statSync(filePath)
const fileSize = stats.size
const files = await fs.promises.readdir(this.storageDir)
for (const file of files) {
const storedFilePath = path.join(this.storageDir, file)
const storedStats = fs.statSync(storedFilePath)
if (storedStats.size === fileSize) {
const [originalHash, storedHash] = await Promise.all([
this.getFileHash(filePath),
this.getFileHash(storedFilePath)
])
if (originalHash === storedHash) {
const ext = path.extname(file)
const id = path.basename(file, ext)
return {
id,
origin_name: file,
name: file + ext,
path: storedFilePath,
created_at: storedStats.birthtime,
size: storedStats.size,
ext,
type: getFileType(ext),
count: 2
}
}
}
}
return null
}
public selectFile = async (
_: Electron.IpcMainInvokeEvent,
options?: OpenDialogOptions
): Promise<FileType[] | null> => {
const defaultOptions: OpenDialogOptions = {
properties: ['openFile']
}
const dialogOptions = { ...defaultOptions, ...options }
const result = await dialog.showOpenDialog(dialogOptions)
if (result.canceled || result.filePaths.length === 0) {
return null
}
const fileMetadataPromises = result.filePaths.map(async (filePath) => {
const stats = fs.statSync(filePath)
const ext = path.extname(filePath)
const fileType = getFileType(ext)
return {
id: uuidv4(),
origin_name: path.basename(filePath),
name: path.basename(filePath),
path: filePath,
created_at: stats.birthtime,
size: stats.size,
ext: ext,
type: fileType,
count: 1
}
})
return Promise.all(fileMetadataPromises)
}
private async compressImage(sourcePath: string, destPath: string): Promise<void> {
try {
const stats = fs.statSync(sourcePath)
const fileSizeInMB = stats.size / (1024 * 1024)
// 如果图片大于1MB才进行压缩
if (fileSizeInMB > 1) {
try {
await fs.promises.copyFile(sourcePath, destPath)
logger.info('[FileStorage] Image compressed successfully:', sourcePath)
} catch (jimpError) {
logger.error('[FileStorage] Image compression failed:', jimpError)
await fs.promises.copyFile(sourcePath, destPath)
}
} else {
// 小图片直接复制
await fs.promises.copyFile(sourcePath, destPath)
}
} catch (error) {
logger.error('[FileStorage] Image handling failed:', error)
// 错误情况下直接复制原文件
await fs.promises.copyFile(sourcePath, destPath)
}
}
public uploadFile = async (_: Electron.IpcMainInvokeEvent, file: FileType): Promise<FileType> => {
const duplicateFile = await this.findDuplicateFile(file.path)
if (duplicateFile) {
return duplicateFile
}
const uuid = uuidv4()
const origin_name = path.basename(file.path)
const ext = path.extname(origin_name).toLowerCase()
const destPath = path.join(this.storageDir, uuid + ext)
logger.info('[FileStorage] Uploading file:', file.path)
// 根据文件类型选择处理方式
if (imageExts.includes(ext)) {
await this.compressImage(file.path, destPath)
} else {
await fs.promises.copyFile(file.path, destPath)
}
const stats = await fs.promises.stat(destPath)
const fileType = getFileType(ext)
const fileMetadata: FileType = {
id: uuid,
origin_name,
name: uuid + ext,
path: destPath,
created_at: stats.birthtime,
size: stats.size,
ext: ext,
type: fileType,
count: 1
}
return fileMetadata
}
public getFile = async (_: Electron.IpcMainInvokeEvent, filePath: string): Promise<FileType | null> => {
if (!fs.existsSync(filePath)) {
return null
}
const stats = fs.statSync(filePath)
const ext = path.extname(filePath)
const fileType = getFileType(ext)
const fileInfo: FileType = {
id: uuidv4(),
origin_name: path.basename(filePath),
name: path.basename(filePath),
path: filePath,
created_at: stats.birthtime,
size: stats.size,
ext: ext,
type: fileType,
count: 1
}
return fileInfo
}
public deleteFile = async (_: Electron.IpcMainInvokeEvent, id: string): Promise<void> => {
await fs.promises.unlink(path.join(this.storageDir, id))
}
public readFile = async (_: Electron.IpcMainInvokeEvent, id: string): Promise<string> => {
const filePath = path.join(this.storageDir, id)
if (documentExts.includes(path.extname(filePath))) {
const originalCwd = process.cwd()
try {
chdir(this.tempDir)
const data = await officeParser.parseOfficeAsync(filePath)
chdir(originalCwd)
return data
} catch (error) {
chdir(originalCwd)
logger.error(error)
throw error
}
}
return fs.readFileSync(filePath, 'utf8')
}
public createTempFile = async (_: Electron.IpcMainInvokeEvent, fileName: string): Promise<string> => {
if (!fs.existsSync(this.tempDir)) {
fs.mkdirSync(this.tempDir, { recursive: true })
}
const tempFilePath = path.join(this.tempDir, `temp_file_${uuidv4()}_${fileName}`)
return tempFilePath
}
public writeFile = async (
_: Electron.IpcMainInvokeEvent,
filePath: string,
data: Uint8Array | string
): Promise<void> => {
await fs.promises.writeFile(filePath, data)
}
public base64Image = async (
_: Electron.IpcMainInvokeEvent,
id: string
): Promise<{ mime: string; base64: string; data: string }> => {
const filePath = path.join(this.storageDir, id)
const data = await fs.promises.readFile(filePath)
const base64 = data.toString('base64')
const mime = `image/${path.extname(filePath).slice(1)}`
return {
mime,
base64,
data: `data:${mime};base64,${base64}`
}
}
public binaryFile = async (_: Electron.IpcMainInvokeEvent, id: string): Promise<{ data: Buffer; mime: string }> => {
const filePath = path.join(this.storageDir, id)
const data = await fs.promises.readFile(filePath)
const mime = `image/${path.extname(filePath).slice(1)}`
return { data, mime }
}
public clear = async (): Promise<void> => {
await fs.promises.rmdir(this.storageDir, { recursive: true })
await this.initStorageDir()
}
public clearTemp = async (): Promise<void> => {
await fs.promises.rmdir(this.tempDir, { recursive: true })
await fs.promises.mkdir(this.tempDir, { recursive: true })
}
public open = async (
_: Electron.IpcMainInvokeEvent,
options: OpenDialogOptions
): Promise<{ fileName: string; filePath: string; content: Buffer } | null> => {
try {
const result: OpenDialogReturnValue = await dialog.showOpenDialog({
title: '打开文件',
properties: ['openFile'],
filters: [{ name: '所有文件', extensions: ['*'] }],
...options
})
if (!result.canceled && result.filePaths.length > 0) {
const filePath = result.filePaths[0]
const fileName = filePath.split('/').pop() || ''
const content = await readFile(filePath)
return { fileName, filePath, content }
}
return null
} catch (err) {
logger.error('[IPC - Error]', 'An error occurred opening the file:', err)
return null
}
}
public openPath = async (_: Electron.IpcMainInvokeEvent, path: string): Promise<void> => {
shell.openPath(path).catch((err) => logger.error('[IPC - Error] Failed to open file:', err))
}
public save = async (
_: Electron.IpcMainInvokeEvent,
fileName: string,
content: string,
options?: SaveDialogOptions
): Promise<string | null> => {
try {
const result: SaveDialogReturnValue = await dialog.showSaveDialog({
title: '保存文件',
defaultPath: fileName,
...options
})
if (!result.canceled && result.filePath) {
await writeFileSync(result.filePath, content, { encoding: 'utf-8' })
}
return result.filePath
} catch (err) {
logger.error('[IPC - Error]', 'An error occurred saving the file:', err)
return null
}
}
public saveImage = async (_: Electron.IpcMainInvokeEvent, name: string, data: string): Promise<void> => {
try {
const filePath = dialog.showSaveDialogSync({
defaultPath: `${name}.png`,
filters: [{ name: 'PNG Image', extensions: ['png'] }]
})
if (filePath) {
const base64Data = data.replace(/^data:image\/png;base64,/, '')
fs.writeFileSync(filePath, base64Data, 'base64')
}
} catch (error) {
logger.error('[IPC - Error]', 'An error occurred saving the image:', error)
}
}
public selectFolder = async (_: Electron.IpcMainInvokeEvent, options: OpenDialogOptions): Promise<string | null> => {
try {
const result: OpenDialogReturnValue = await dialog.showOpenDialog({
title: '选择文件夹',
properties: ['openDirectory'],
...options
})
if (!result.canceled && result.filePaths.length > 0) {
return result.filePaths[0]
}
return null
} catch (err) {
logger.error('[IPC - Error]', 'An error occurred selecting the folder:', err)
return null
}
}
public downloadFile = async (_: Electron.IpcMainInvokeEvent, url: string): Promise<FileType> => {
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
// 尝试从Content-Disposition获取文件名
const contentDisposition = response.headers.get('Content-Disposition')
let filename = 'download'
if (contentDisposition) {
const filenameMatch = contentDisposition.match(/filename="?(.+)"?/i)
if (filenameMatch) {
filename = filenameMatch[1]
}
}
// 如果URL中有文件名使用URL中的文件名
const urlFilename = url.split('/').pop()?.split('?')[0]
if (urlFilename && urlFilename.includes('.')) {
filename = urlFilename
}
// 如果文件名没有后缀根据Content-Type添加后缀
if (!filename.includes('.')) {
const contentType = response.headers.get('Content-Type')
const ext = this.getExtensionFromMimeType(contentType)
filename += ext
}
const uuid = uuidv4()
const ext = path.extname(filename)
const destPath = path.join(this.storageDir, uuid + ext)
// 将响应内容写入文件
const buffer = Buffer.from(await response.arrayBuffer())
await fs.promises.writeFile(destPath, buffer)
const stats = await fs.promises.stat(destPath)
const fileType = getFileType(ext)
const fileMetadata: FileType = {
id: uuid,
origin_name: filename,
name: uuid + ext,
path: destPath,
created_at: stats.birthtime,
size: stats.size,
ext: ext,
type: fileType,
count: 1
}
return fileMetadata
} catch (error) {
logger.error('[FileStorage] Download file error:', error)
throw error
}
}
private getExtensionFromMimeType(mimeType: string | null): string {
if (!mimeType) return '.bin'
const mimeToExtension: { [key: string]: string } = {
'image/jpeg': '.jpg',
'image/png': '.png',
'image/gif': '.gif',
'application/pdf': '.pdf',
'text/plain': '.txt',
'application/msword': '.doc',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx',
'application/zip': '.zip',
'application/x-zip-compressed': '.zip',
'application/octet-stream': '.bin'
}
return mimeToExtension[mimeType] || '.bin'
}
public copyFile = async (_: Electron.IpcMainInvokeEvent, id: string, destPath: string): Promise<void> => {
try {
const sourcePath = path.join(this.storageDir, id)
// 确保目标目录存在
const destDir = path.dirname(destPath)
if (!fs.existsSync(destDir)) {
await fs.promises.mkdir(destDir, { recursive: true })
}
// 复制文件
await fs.promises.copyFile(sourcePath, destPath)
logger.info('[FileStorage] File copied successfully:', { from: sourcePath, to: destPath })
} catch (error) {
logger.error('[FileStorage] Copy file failed:', error)
throw error
}
}
}
export default FileStorage

View File

@@ -0,0 +1,63 @@
import { FileMetadataResponse, FileState, GoogleAIFileManager } from '@google/generative-ai/server'
import { FileType } from '@types'
import fs from 'fs'
import { CacheService } from './CacheService'
export class GeminiService {
private static readonly FILE_LIST_CACHE_KEY = 'gemini_file_list'
private static readonly CACHE_DURATION = 3000
static async uploadFile(_: Electron.IpcMainInvokeEvent, file: FileType, apiKey: string) {
const fileManager = new GoogleAIFileManager(apiKey)
const uploadResult = await fileManager.uploadFile(file.path, {
mimeType: 'application/pdf',
displayName: file.origin_name
})
return uploadResult
}
static async base64File(_: Electron.IpcMainInvokeEvent, file: FileType) {
return {
data: Buffer.from(fs.readFileSync(file.path)).toString('base64'),
mimeType: 'application/pdf'
}
}
static async retrieveFile(
_: Electron.IpcMainInvokeEvent,
file: FileType,
apiKey: string
): Promise<FileMetadataResponse | undefined> {
const fileManager = new GoogleAIFileManager(apiKey)
const cachedResponse = CacheService.get<any>(GeminiService.FILE_LIST_CACHE_KEY)
if (cachedResponse) {
return GeminiService.processResponse(cachedResponse, file)
}
const response = await fileManager.listFiles()
CacheService.set(GeminiService.FILE_LIST_CACHE_KEY, response, GeminiService.CACHE_DURATION)
return GeminiService.processResponse(response, file)
}
private static processResponse(response: any, file: FileType) {
if (response.files) {
return response.files
.filter((file) => file.state === FileState.ACTIVE)
.find((i) => i.displayName === file.origin_name && Number(i.sizeBytes) === file.size)
}
return undefined
}
static async listFiles(_: Electron.IpcMainInvokeEvent, apiKey: string) {
const fileManager = new GoogleAIFileManager(apiKey)
return await fileManager.listFiles()
}
static async deleteFile(_: Electron.IpcMainInvokeEvent, apiKey: string, fileId: string) {
const fileManager = new GoogleAIFileManager(apiKey)
await fileManager.deleteFile(fileId)
}
}

View File

@@ -0,0 +1,193 @@
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 { AzureOpenAiEmbeddings, OpenAiEmbeddings } from '@llm-tools/embedjs-openai'
import { addFileLoader } from '@main/loader'
import { getInstanceName } from '@main/utils'
import { getAllFiles } from '@main/utils/file'
import type { LoaderReturn } from '@shared/config/types'
import { FileType, KnowledgeBaseParams, KnowledgeItem } from '@types'
import { app } from 'electron'
import { v4 as uuidv4 } from 'uuid'
import { windowService } from './WindowService'
class KnowledgeService {
private storageDir = path.join(app.getPath('userData'), 'Data', 'KnowledgeBase')
constructor() {
this.initStorageDir()
}
private initStorageDir = (): void => {
if (!fs.existsSync(this.storageDir)) {
fs.mkdirSync(this.storageDir, { recursive: true })
}
}
private getRagApplication = async ({
id,
model,
apiKey,
apiVersion,
baseURL,
dimensions
}: KnowledgeBaseParams): Promise<RAGApplication> => {
const batchSize = 10
return new RAGApplicationBuilder()
.setModel('NO_MODEL')
.setEmbeddingModel(
apiVersion
? new AzureOpenAiEmbeddings({
azureOpenAIApiKey: apiKey,
azureOpenAIApiVersion: apiVersion,
azureOpenAIApiDeploymentName: model,
azureOpenAIApiInstanceName: getInstanceName(baseURL),
dimensions,
batchSize
})
: new OpenAiEmbeddings({
model,
apiKey,
configuration: { baseURL },
dimensions,
batchSize
})
)
.setVectorDatabase(new LibSqlDb({ path: path.join(this.storageDir, id) }))
.build()
}
public create = async (_: Electron.IpcMainInvokeEvent, base: KnowledgeBaseParams): Promise<void> => {
this.getRagApplication(base)
}
public reset = async (_: Electron.IpcMainInvokeEvent, { base }: { base: KnowledgeBaseParams }): Promise<void> => {
const ragApplication = await this.getRagApplication(base)
await ragApplication.reset()
}
public delete = async (_: Electron.IpcMainInvokeEvent, id: string): Promise<void> => {
const dbPath = path.join(this.storageDir, id)
if (fs.existsSync(dbPath)) {
fs.rmSync(dbPath, { recursive: true })
}
}
public add = async (
_: Electron.IpcMainInvokeEvent,
{ base, item, forceReload = false }: { base: KnowledgeBaseParams; item: KnowledgeItem; forceReload: boolean }
): Promise<LoaderReturn> => {
const ragApplication = await this.getRagApplication(base)
const sendDirectoryProcessingPercent = (totalFiles: number, processedFiles: number) => {
const mainWindow = windowService.getMainWindow()
mainWindow?.webContents.send(base.id, (processedFiles / totalFiles) * 100)
}
if (item.type === 'directory') {
const directory = item.content as string
const files = getAllFiles(directory)
const totalFiles = files.length
let processedFiles = 0
const loaderPromises = files.map(async (file) => {
const result = await addFileLoader(ragApplication, file, base, forceReload)
processedFiles++
sendDirectoryProcessingPercent(totalFiles, processedFiles)
return result
})
const loaderResults = await Promise.allSettled(loaderPromises)
// @ts-ignore uniqueId
const uniqueIds = loaderResults.filter((result) => result.status === 'fulfilled').map((result) => result.uniqueId)
return {
entriesAdded: loaderResults.length,
uniqueId: `DirectoryLoader_${uuidv4()}`,
uniqueIds,
loaderType: 'DirectoryLoader'
} as LoaderReturn
}
if (item.type === 'url') {
const content = item.content as string
if (content.startsWith('http')) {
const loaderReturn = await ragApplication.addLoader(
new WebLoader({ urlOrContent: content, chunkSize: base.chunkSize, chunkOverlap: base.chunkOverlap }) as any,
forceReload
)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
} as LoaderReturn
}
}
if (item.type === 'sitemap') {
const content = item.content as string
// @ts-ignore loader type
const loaderReturn = await ragApplication.addLoader(
new SitemapLoader({ url: content, chunkSize: base.chunkSize, chunkOverlap: base.chunkOverlap }) as any,
forceReload
)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
} as LoaderReturn
}
if (item.type === 'note') {
const content = item.content as string
console.debug('chunkSize', base.chunkSize)
const loaderReturn = await ragApplication.addLoader(
new TextLoader({ text: content, chunkSize: base.chunkSize, chunkOverlap: base.chunkOverlap }),
forceReload
)
return {
entriesAdded: loaderReturn.entriesAdded,
uniqueId: loaderReturn.uniqueId,
uniqueIds: [loaderReturn.uniqueId],
loaderType: loaderReturn.loaderType
} as LoaderReturn
}
if (item.type === 'file') {
const file = item.content as FileType
return await addFileLoader(ragApplication, file, base, forceReload)
}
return { entriesAdded: 0, uniqueId: '', uniqueIds: [''], loaderType: '' }
}
public remove = async (
_: Electron.IpcMainInvokeEvent,
{ uniqueId, uniqueIds, base }: { uniqueId: string; uniqueIds: string[]; base: KnowledgeBaseParams }
): Promise<void> => {
const ragApplication = await this.getRagApplication(base)
console.debug(`[ KnowledgeService Remove Item UniqueId: ${uniqueId}]`)
for (const id of uniqueIds) {
await ragApplication.deleteLoader(id)
}
}
public search = async (
_: Electron.IpcMainInvokeEvent,
{ search, base }: { search: string; base: KnowledgeBaseParams }
): Promise<ExtractChunkData[]> => {
const ragApplication = await this.getRagApplication(base)
return await ragApplication.search(search)
}
}
export default new KnowledgeService()

View File

@@ -0,0 +1,215 @@
import { Shortcut } from '@types'
import { BrowserWindow, globalShortcut } from 'electron'
import Logger from 'electron-log'
import { configManager } from './ConfigManager'
import { windowService } from './WindowService'
let showAppAccelerator: string | null = null
let showMiniWindowAccelerator: string | null = null
function getShortcutHandler(shortcut: Shortcut) {
switch (shortcut.key) {
case 'zoom_in':
return (window: BrowserWindow) => handleZoom(0.1)(window)
case 'zoom_out':
return (window: BrowserWindow) => handleZoom(-0.1)(window)
case 'zoom_reset':
return (window: BrowserWindow) => {
window.webContents.setZoomFactor(1)
configManager.setZoomFactor(1)
}
case 'show_app':
return (window: BrowserWindow) => {
if (window.isVisible()) {
if (window.isFocused()) {
window.hide()
} else {
window.focus()
}
} else {
window.show()
window.focus()
}
}
case 'mini_window':
return () => {
windowService.toggleMiniWindow()
}
default:
return null
}
}
function formatShortcutKey(shortcut: string[]): string {
return shortcut.join('+')
}
function handleZoom(delta: number) {
return (window: BrowserWindow) => {
const currentZoom = configManager.getZoomFactor()
const newZoom = Number((currentZoom + delta).toFixed(1))
if (newZoom >= 0.1 && newZoom <= 5.0) {
window.webContents.setZoomFactor(newZoom)
configManager.setZoomFactor(newZoom)
}
}
}
const convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat = (
shortcut: string | string[]
): string => {
const accelerator = (() => {
if (Array.isArray(shortcut)) {
return shortcut
} else {
return shortcut.split('+').map((key) => key.trim())
}
})()
return accelerator
.map((key) => {
switch (key) {
case 'Command':
return 'CommandOrControl'
case 'Control':
return 'Control'
case 'Ctrl':
return 'Control'
case 'ArrowUp':
return 'Up'
case 'ArrowDown':
return 'Down'
case 'ArrowLeft':
return 'Left'
case 'ArrowRight':
return 'Right'
case 'AltGraph':
return 'Alt'
case 'Slash':
return '/'
case 'Semicolon':
return ';'
case 'BracketLeft':
return '['
case 'BracketRight':
return ']'
case 'Backslash':
return '\\'
case 'Quote':
return "'"
case 'Comma':
return ','
case 'Minus':
return '-'
case 'Equal':
return '='
default:
return key
}
})
.join('+')
}
export function registerShortcuts(window: BrowserWindow) {
window.once('ready-to-show', () => {
window.webContents.setZoomFactor(configManager.getZoomFactor())
})
const register = () => {
if (window.isDestroyed()) return
const shortcuts = configManager.getShortcuts()
if (!shortcuts) return
shortcuts.forEach((shortcut) => {
try {
if (shortcut.shortcut.length === 0) {
return
}
const handler = getShortcutHandler(shortcut)
if (!handler) {
return
}
const accelerator = formatShortcutKey(shortcut.shortcut)
if (shortcut.key === 'show_app' && shortcut.enabled) {
showAppAccelerator = accelerator
}
if (shortcut.key === 'mini_window' && shortcut.enabled) {
showMiniWindowAccelerator = accelerator
}
if (shortcut.key.includes('zoom')) {
switch (shortcut.key) {
case 'zoom_in':
globalShortcut.register('CommandOrControl+=', () => shortcut.enabled && handler(window))
globalShortcut.register('CommandOrControl+numadd', () => shortcut.enabled && handler(window))
return
case 'zoom_out':
globalShortcut.register('CommandOrControl+-', () => shortcut.enabled && handler(window))
globalShortcut.register('CommandOrControl+numsub', () => shortcut.enabled && handler(window))
return
case 'zoom_reset':
globalShortcut.register('CommandOrControl+0', () => shortcut.enabled && handler(window))
return
}
}
if (shortcut.enabled) {
const accelerator = convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat(
shortcut.shortcut
)
globalShortcut.register(accelerator, () => handler(window))
}
} catch (error) {
Logger.error(`[ShortcutService] Failed to register shortcut ${shortcut.key}`)
}
})
}
const unregister = () => {
if (window.isDestroyed()) return
try {
globalShortcut.unregisterAll()
if (showAppAccelerator) {
const handler = getShortcutHandler({ key: 'show_app' } as Shortcut)
const accelerator =
convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat(showAppAccelerator)
handler && globalShortcut.register(accelerator, () => handler(window))
}
if (showMiniWindowAccelerator) {
const handler = getShortcutHandler({ key: 'mini_window' } as Shortcut)
const accelerator =
convertShortcutRecordedByKeyboardEventKeyValueToElectronGlobalShortcutFormat(showMiniWindowAccelerator)
handler && globalShortcut.register(accelerator, () => handler(window))
}
} catch (error) {
Logger.error('[ShortcutService] Failed to unregister shortcuts')
}
}
window.on('focus', () => register())
window.on('blur', () => unregister())
if (!window.isDestroyed() && window.isFocused()) {
register()
}
}
export function unregisterAllShortcuts() {
try {
showAppAccelerator = null
showMiniWindowAccelerator = null
globalShortcut.unregisterAll()
} catch (error) {
Logger.error('[ShortcutService] Failed to unregister all shortcuts')
}
}

View File

@@ -0,0 +1,118 @@
import { isMac } from '@main/constant'
import { locales } from '@main/utils/locales'
import { app, Menu, MenuItemConstructorOptions, nativeImage, nativeTheme, Tray } from 'electron'
import icon from '../../../build/tray_icon.png?asset'
import iconDark from '../../../build/tray_icon_dark.png?asset'
import iconLight from '../../../build/tray_icon_light.png?asset'
import { configManager } from './ConfigManager'
import { windowService } from './WindowService'
export class TrayService {
private static instance: TrayService
private tray: Tray | null = null
constructor() {
this.updateTray()
this.watchTrayChanges()
TrayService.instance = this
}
public static getInstance() {
return TrayService.instance
}
private createTray() {
this.destroyTray()
const iconPath = isMac ? (nativeTheme.shouldUseDarkColors ? iconLight : iconDark) : icon
const tray = new Tray(iconPath)
if (process.platform === 'win32') {
tray.setImage(iconPath)
} else if (process.platform === 'darwin') {
const image = nativeImage.createFromPath(iconPath)
const resizedImage = image.resize({ width: 16, height: 16 })
resizedImage.setTemplateImage(true)
tray.setImage(resizedImage)
} else if (process.platform === 'linux') {
const image = nativeImage.createFromPath(iconPath)
const resizedImage = image.resize({ width: 16, height: 16 })
tray.setImage(resizedImage)
}
this.tray = tray
const locale = locales[configManager.getLanguage()]
const { tray: trayLocale } = locale.translation
const enableQuickAssistant = configManager.getEnableQuickAssistant()
const template = [
{
label: trayLocale.show_window,
click: () => windowService.showMainWindow()
},
enableQuickAssistant && {
label: trayLocale.show_mini_window,
click: () => windowService.showMiniWindow()
},
{ type: 'separator' },
{
label: trayLocale.quit,
click: () => this.quit()
}
].filter(Boolean) as MenuItemConstructorOptions[]
const contextMenu = Menu.buildFromTemplate(template)
if (process.platform === 'linux') {
this.tray.setContextMenu(contextMenu)
}
this.tray.setToolTip('Cherry Studio')
this.tray.on('right-click', () => {
this.tray?.popUpContextMenu(contextMenu)
})
this.tray.on('click', () => {
if (enableQuickAssistant && configManager.getClickTrayToShowQuickAssistant()) {
windowService.showMiniWindow()
} else {
windowService.showMainWindow()
}
})
}
private updateTray() {
const showTray = configManager.getTray()
if (showTray) {
this.createTray()
} else {
this.destroyTray()
}
}
public restartTray() {
if (configManager.getTray()) {
this.destroyTray()
this.createTray()
}
}
private destroyTray() {
if (this.tray) {
this.tray.destroy()
this.tray = null
}
}
private watchTrayChanges() {
configManager.subscribe<boolean>('tray', () => this.updateTray())
}
private quit() {
app.quit()
}
}

View File

@@ -0,0 +1,68 @@
import { WebDavConfig } from '@types'
import Logger from 'electron-log'
import Stream from 'stream'
import { BufferLike, createClient, GetFileContentsOptions, PutFileContentsOptions, WebDAVClient } from 'webdav'
export default class WebDav {
public instance: WebDAVClient | undefined
private webdavPath: string
constructor(params: WebDavConfig) {
this.webdavPath = params.webdavPath
this.instance = createClient(params.webdavHost, {
username: params.webdavUser,
password: params.webdavPass,
maxBodyLength: Infinity,
maxContentLength: Infinity
})
this.putFileContents = this.putFileContents.bind(this)
this.getFileContents = this.getFileContents.bind(this)
}
public putFileContents = async (
filename: string,
data: string | BufferLike | Stream.Readable,
options?: PutFileContentsOptions
) => {
if (!this.instance) {
return new Error('WebDAV client not initialized')
}
try {
if (!(await this.instance.exists(this.webdavPath))) {
await this.instance.createDirectory(this.webdavPath, {
recursive: true
})
}
} catch (error) {
Logger.error('[WebDAV] Error creating directory on WebDAV:', error)
throw error
}
const remoteFilePath = `${this.webdavPath}/${filename}`
try {
return await this.instance.putFileContents(remoteFilePath, data, options)
} catch (error) {
Logger.error('[WebDAV] Error putting file contents on WebDAV:', error)
throw error
}
}
public getFileContents = async (filename: string, options?: GetFileContentsOptions) => {
if (!this.instance) {
throw new Error('WebDAV client not initialized')
}
const remoteFilePath = `${this.webdavPath}/${filename}`
try {
return await this.instance.getFileContents(remoteFilePath, options)
} catch (error) {
Logger.error('[WebDAV] Error getting file contents on WebDAV:', error)
throw error
}
}
}

View File

@@ -0,0 +1,443 @@
import { is } from '@electron-toolkit/utils'
import { isLinux, isWin } from '@main/constant'
import { app, BrowserWindow, ipcMain, Menu, MenuItem, shell } from 'electron'
import Logger from 'electron-log'
import windowStateKeeper from 'electron-window-state'
import path, { join } from 'path'
import icon from '../../../build/icon.png?asset'
import { titleBarOverlayDark, titleBarOverlayLight } from '../config'
import { locales } from '../utils/locales'
import { configManager } from './ConfigManager'
export class WindowService {
private static instance: WindowService | null = null
private mainWindow: BrowserWindow | null = null
private miniWindow: BrowserWindow | null = null
private wasFullScreen: boolean = false
private selectionMenuWindow: BrowserWindow | null = null
private lastSelectedText: string = ''
private contextMenu: Menu | null = null
public static getInstance(): WindowService {
if (!WindowService.instance) {
WindowService.instance = new WindowService()
}
return WindowService.instance
}
public createMainWindow(): BrowserWindow {
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
this.mainWindow.show()
return this.mainWindow
}
const mainWindowState = windowStateKeeper({
defaultWidth: 1080,
defaultHeight: 670
})
const theme = configManager.getTheme()
const isMac = process.platform === 'darwin'
const isLinux = process.platform === 'linux'
this.mainWindow = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth: 1080,
minHeight: 600,
show: false, // 初始不显示
autoHideMenuBar: true,
transparent: isMac,
vibrancy: 'under-window',
visualEffectState: 'active',
titleBarStyle: isLinux ? 'default' : 'hidden',
titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight,
backgroundColor: isMac ? undefined : theme === 'dark' ? '#181818' : '#FFFFFF',
trafficLightPosition: { x: 8, y: 12 },
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
webSecurity: false,
webviewTag: true,
allowRunningInsecureContent: true
}
})
this.setupMainWindow(this.mainWindow, mainWindowState)
return this.mainWindow
}
public createMinappWindow({
url,
parent,
windowOptions
}: {
url: string
parent?: BrowserWindow
windowOptions?: Electron.BrowserWindowConstructorOptions
}): BrowserWindow {
const width = windowOptions?.width || 1000
const height = windowOptions?.height || 680
const minappWindow = new BrowserWindow({
width,
height,
autoHideMenuBar: true,
title: 'Cherry Studio',
...windowOptions,
parent,
webPreferences: {
preload: join(__dirname, '../preload/minapp.js'),
sandbox: false,
contextIsolation: false
}
})
minappWindow.loadURL(url)
return minappWindow
}
private setupMainWindow(mainWindow: BrowserWindow, mainWindowState: any) {
mainWindowState.manage(mainWindow)
this.setupContextMenu(mainWindow)
this.setupWindowEvents(mainWindow)
this.setupWebContentsHandlers(mainWindow)
this.setupWindowLifecycleEvents(mainWindow)
this.loadMainWindowContent(mainWindow)
}
private setupContextMenu(mainWindow: BrowserWindow) {
if (!this.contextMenu) {
const locale = locales[configManager.getLanguage()]
const { common } = locale.translation
this.contextMenu = new Menu()
this.contextMenu.append(new MenuItem({ label: common.copy, role: 'copy' }))
this.contextMenu.append(new MenuItem({ label: common.paste, role: 'paste' }))
this.contextMenu.append(new MenuItem({ label: common.cut, role: 'cut' }))
}
mainWindow.webContents.on('context-menu', () => {
this.contextMenu?.popup()
})
// Handle webview context menu
mainWindow.webContents.on('did-attach-webview', (_, webContents) => {
webContents.on('context-menu', () => {
this.contextMenu?.popup()
})
})
}
private setupWindowEvents(mainWindow: BrowserWindow) {
mainWindow.once('ready-to-show', () => {
mainWindow.show()
})
// 处理全屏相关事件
mainWindow.on('enter-full-screen', () => {
this.wasFullScreen = true
mainWindow.webContents.send('fullscreen-status-changed', true)
})
mainWindow.on('leave-full-screen', () => {
this.wasFullScreen = false
mainWindow.webContents.send('fullscreen-status-changed', false)
})
}
private setupWebContentsHandlers(mainWindow: BrowserWindow) {
mainWindow.webContents.on('will-navigate', (event, url) => {
if (url.includes('localhost:5173')) {
return
}
event.preventDefault()
shell.openExternal(url)
})
mainWindow.webContents.setWindowOpenHandler((details) => {
const { url } = details
const oauthProviderUrls = [
'https://account.siliconflow.cn/oauth',
'https://cloud.siliconflow.cn/expensebill',
'https://aihubmix.com/token',
'https://aihubmix.com/topup'
]
if (oauthProviderUrls.some((link) => url.startsWith(link))) {
return {
action: 'allow',
overrideBrowserWindowOptions: {
webPreferences: {
partition: 'persist:webview'
}
}
}
}
if (url.includes('http://file/')) {
const fileName = url.replace('http://file/', '')
const storageDir = path.join(app.getPath('userData'), 'Data', 'Files')
const filePath = storageDir + '/' + fileName
shell.openPath(filePath).catch((err) => Logger.error('Failed to open file:', err))
} else {
shell.openExternal(details.url)
}
return { action: 'deny' }
})
this.setupWebRequestHeaders(mainWindow)
}
private setupWebRequestHeaders(mainWindow: BrowserWindow) {
mainWindow.webContents.session.webRequest.onHeadersReceived({ urls: ['*://*/*'] }, (details, callback) => {
if (details.responseHeaders?.['X-Frame-Options']) {
delete details.responseHeaders['X-Frame-Options']
}
if (details.responseHeaders?.['x-frame-options']) {
delete details.responseHeaders['x-frame-options']
}
if (details.responseHeaders?.['Content-Security-Policy']) {
delete details.responseHeaders['Content-Security-Policy']
}
if (details.responseHeaders?.['content-security-policy']) {
delete details.responseHeaders['content-security-policy']
}
callback({ cancel: false, responseHeaders: details.responseHeaders })
})
}
private loadMainWindowContent(mainWindow: BrowserWindow) {
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
}
public getMainWindow(): BrowserWindow | null {
return this.mainWindow
}
private setupWindowLifecycleEvents(mainWindow: BrowserWindow) {
mainWindow.on('close', (event) => {
// 如果已经触发退出,直接退出
if (app.isQuitting) {
return app.quit()
}
// 没有开启托盘且是Windows或Linux系统直接退出
const notInTray = !configManager.getTray()
if ((isWin || isLinux) && notInTray) {
return app.quit()
}
// 如果是全屏状态,直接退出
if (this.wasFullScreen) {
return app.quit()
}
event.preventDefault()
mainWindow.hide()
})
mainWindow.on('closed', () => {
this.mainWindow = null
})
mainWindow.on('show', () => {
if (this.miniWindow && !this.miniWindow.isDestroyed()) {
this.miniWindow.hide()
}
})
}
public showMainWindow() {
if (this.miniWindow && !this.miniWindow.isDestroyed()) {
this.miniWindow.hide()
}
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
if (this.mainWindow.isMinimized()) {
this.mainWindow.restore()
}
this.mainWindow.show()
this.mainWindow.focus()
} else {
this.mainWindow = this.createMainWindow()
this.mainWindow.focus()
}
}
public showMiniWindow() {
const enableQuickAssistant = configManager.getEnableQuickAssistant()
if (!enableQuickAssistant) {
return
}
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
this.mainWindow.hide()
}
if (this.selectionMenuWindow && !this.selectionMenuWindow.isDestroyed()) {
this.selectionMenuWindow.hide()
}
if (this.miniWindow && !this.miniWindow.isDestroyed()) {
if (this.miniWindow.isMinimized()) {
this.miniWindow.restore()
}
this.miniWindow.show()
this.miniWindow.center()
this.miniWindow.focus()
return
}
const isMac = process.platform === 'darwin'
this.miniWindow = new BrowserWindow({
width: 500,
height: 520,
show: true,
autoHideMenuBar: true,
transparent: isMac,
vibrancy: 'under-window',
visualEffectState: 'followWindow',
center: true,
frame: false,
alwaysOnTop: true,
resizable: false,
useContentSize: true,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
webSecurity: false,
webviewTag: true
}
})
this.miniWindow.on('blur', () => {
this.miniWindow?.hide()
})
this.miniWindow.on('closed', () => {
this.miniWindow = null
})
this.miniWindow.on('hide', () => {
this.miniWindow?.webContents.send('hide-mini-window')
})
this.miniWindow.on('show', () => {
this.miniWindow?.webContents.send('show-mini-window')
})
ipcMain.on('miniwindow-reload', () => {
this.miniWindow?.reload()
})
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
this.miniWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '#/mini')
} else {
this.miniWindow.loadFile(join(__dirname, '../renderer/index.html'), {
hash: '#/mini'
})
}
}
public hideMiniWindow() {
this.miniWindow?.hide()
}
public closeMiniWindow() {
this.miniWindow?.close()
}
public toggleMiniWindow() {
if (this.miniWindow) {
this.miniWindow.isVisible() ? this.miniWindow.hide() : this.miniWindow.show()
} else {
this.showMiniWindow()
}
}
public showSelectionMenu(bounds: { x: number; y: number }) {
if (this.selectionMenuWindow && !this.selectionMenuWindow.isDestroyed()) {
this.selectionMenuWindow.setPosition(bounds.x, bounds.y)
this.selectionMenuWindow.show()
return
}
const theme = configManager.getTheme()
const isMac = process.platform === 'darwin'
this.selectionMenuWindow = new BrowserWindow({
width: 280,
height: 40,
x: bounds.x,
y: bounds.y,
show: true,
autoHideMenuBar: true,
transparent: true,
frame: false,
alwaysOnTop: false,
skipTaskbar: true,
backgroundColor: isMac ? undefined : theme === 'dark' ? '#181818' : '#FFFFFF',
resizable: false,
vibrancy: 'popover',
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
webSecurity: false
}
})
// 点击其他地方时隐藏窗口
this.selectionMenuWindow.on('blur', () => {
this.selectionMenuWindow?.hide()
this.miniWindow?.webContents.send('selection-action', {
action: 'home',
selectedText: this.lastSelectedText
})
})
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
this.selectionMenuWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/src/windows/menu/menu.html')
} else {
this.selectionMenuWindow.loadFile(join(__dirname, '../renderer/src/windows/menu/menu.html'))
}
this.setupSelectionMenuEvents()
}
private setupSelectionMenuEvents() {
if (!this.selectionMenuWindow) return
ipcMain.removeHandler('selection-menu:action')
ipcMain.handle('selection-menu:action', (_, action) => {
this.selectionMenuWindow?.hide()
this.showMiniWindow()
setTimeout(() => {
this.miniWindow?.webContents.send('selection-action', {
action,
selectedText: this.lastSelectedText
})
}, 100)
})
}
public setLastSelectedText(text: string) {
this.lastSelectedText = text
}
}
export const windowService = WindowService.getInstance()

View File

@@ -1,87 +0,0 @@
import { BrowserWindow, dialog } from 'electron'
import logger from 'electron-log'
import { AppUpdater as _AppUpdater, autoUpdater, UpdateInfo } from 'electron-updater'
export default class AppUpdater {
autoUpdater: _AppUpdater = autoUpdater
constructor(mainWindow: BrowserWindow) {
logger.transports.file.level = 'debug'
autoUpdater.logger = logger
autoUpdater.forceDevUpdateConfig = true
autoUpdater.autoDownload = false
// 检测下载错误
autoUpdater.on('error', (error) => {
logger.error('更新异常', error)
mainWindow.webContents.send('update-error', error)
})
// 检测是否需要更新
autoUpdater.on('checking-for-update', () => {
logger.info('正在检查更新……')
})
autoUpdater.on('update-available', (releaseInfo: UpdateInfo) => {
autoUpdater.logger?.info('检测到新版本,确认是否下载')
mainWindow.webContents.send('update-available', releaseInfo)
const releaseNotes = releaseInfo.releaseNotes
let releaseContent = ''
if (releaseNotes) {
if (typeof releaseNotes === 'string') {
releaseContent = <string>releaseNotes
} else if (releaseNotes instanceof Array) {
releaseNotes.forEach((releaseNote) => {
releaseContent += `${releaseNote}\n`
})
}
} else {
releaseContent = '暂无更新说明'
}
// 弹框确认是否下载更新releaseContent是更新日志
dialog
.showMessageBox({
type: 'info',
title: '应用有新的更新',
detail: releaseContent,
message: '发现新版本,是否现在更新?',
buttons: ['下次再说', '更新']
})
.then(({ response }) => {
if (response === 1) {
logger.info('用户选择更新,准备下载更新')
mainWindow.webContents.send('download-update')
autoUpdater.downloadUpdate()
}
})
})
// 检测到不需要更新时
autoUpdater.on('update-not-available', () => {
logger.info('现在使用的就是最新版本,不用更新')
mainWindow.webContents.send('update-not-available')
})
// 更新下载进度
autoUpdater.on('download-progress', (progress) => {
logger.info('下载进度', progress)
mainWindow.webContents.send('download-progress', progress)
})
// 当需要更新的内容下载完成后
autoUpdater.on('update-downloaded', () => {
logger.info('下载完成,准备更新')
dialog
.showMessageBox({
title: '安装更新',
message: '更新下载完毕,应用将重启并进行安装'
})
.then(() => {
setImmediate(() => autoUpdater.quitAndInstall())
})
})
this.autoUpdater = autoUpdater
}
}

21
src/main/utils/aes.ts Normal file
View File

@@ -0,0 +1,21 @@
import * as crypto from 'crypto'
// 加密函数
export function encrypt(text: string, secretKey: string, iv: string): { iv: string; encryptedData: string } {
const _iv = Buffer.from(iv, 'hex')
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(secretKey), _iv)
let encrypted = cipher.update(text, 'utf8', 'hex')
encrypted += cipher.final('hex')
return {
iv: _iv.toString('hex'),
encryptedData: encrypted
}
}
// 解密函数
export function decrypt(encryptedData: string, iv: string, secretKey: string): string {
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(secretKey), Buffer.from(iv, 'hex'))
let decrypted = decipher.update(encryptedData, 'hex', 'utf8')
decrypted += decipher.final('utf8')
return decrypted
}

56
src/main/utils/file.ts Normal file
View File

@@ -0,0 +1,56 @@
import * as fs from 'node:fs'
import path from 'node:path'
import { audioExts, documentExts, imageExts, textExts, videoExts } from '@shared/config/constant'
import { FileType, FileTypes } from '@types'
import { v4 as uuidv4 } from 'uuid'
export function getFileType(ext: string): FileTypes {
ext = ext.toLowerCase()
if (imageExts.includes(ext)) return FileTypes.IMAGE
if (videoExts.includes(ext)) return FileTypes.VIDEO
if (audioExts.includes(ext)) return FileTypes.AUDIO
if (textExts.includes(ext)) return FileTypes.TEXT
if (documentExts.includes(ext)) return FileTypes.DOCUMENT
return FileTypes.OTHER
}
export function getAllFiles(dirPath: string, arrayOfFiles: FileType[] = []): FileType[] {
const files = fs.readdirSync(dirPath)
files.forEach((file) => {
if (file.startsWith('.')) {
return
}
const fullPath = path.join(dirPath, file)
if (fs.statSync(fullPath).isDirectory()) {
arrayOfFiles = getAllFiles(fullPath, arrayOfFiles)
} else {
const ext = path.extname(file)
const fileType = getFileType(ext)
if ([FileTypes.OTHER, FileTypes.IMAGE, FileTypes.VIDEO, FileTypes.AUDIO].includes(fileType)) {
return
}
const name = path.basename(file)
const size = fs.statSync(fullPath).size
const fileItem: FileType = {
id: uuidv4(),
name,
path: fullPath,
size,
ext,
count: 1,
origin_name: name,
type: fileType,
created_at: new Date()
}
arrayOfFiles.push(fileItem)
}
})
return arrayOfFiles
}

44
src/main/utils/index.ts Normal file
View File

@@ -0,0 +1,44 @@
import fs from 'node:fs'
import path from 'node:path'
import { app } from 'electron'
export function getResourcePath() {
return path.join(app.getAppPath(), 'resources')
}
export function getDataPath() {
const dataPath = path.join(app.getPath('userData'), 'Data')
if (!fs.existsSync(dataPath)) {
fs.mkdirSync(dataPath, { recursive: true })
}
return dataPath
}
export function getInstanceName(baseURL: string) {
try {
return new URL(baseURL).host.split('.')[0]
} catch (error) {
return ''
}
}
export function debounce(func: (...args: any[]) => void, wait: number, immediate: boolean = false) {
let timeout: NodeJS.Timeout | null = null
return function (...args: any[]) {
if (timeout) clearTimeout(timeout)
if (immediate) {
func(...args)
} else {
timeout = setTimeout(() => func(...args), wait)
}
}
}
export function dumpPersistState() {
const persistState = JSON.parse(localStorage.getItem('persist:cherry-studio') || '{}')
for (const key in persistState) {
persistState[key] = JSON.parse(persistState[key])
}
return JSON.stringify(persistState)
}

Some files were not shown because too many files have changed in this diff Show More