Compare commits

...

253 Commits

Author SHA1 Message Date
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
343 changed files with 25640 additions and 4398 deletions

View File

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

View File

@@ -71,5 +71,6 @@ jobs:
dist/*.rpm dist/*.rpm
dist/*.tar.gz dist/*.tar.gz
dist/latest*.yml dist/latest*.yml
dist/*.blockmap
env: env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}

7
.gitignore vendored
View File

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

View File

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

View File

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

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.

72
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,72 @@
## Cherry Studio目录结构和功能
### 1. `/src`: 主要源代码目录
- ** `/main`**: Electron主进程相关代码
- 负责应用的生命周期管理、窗口创建、IPC通信等
- ** `/renderer`**: Electron渲染进程相关代码
- 包含用户界面的实现使用TypeScript和SCSS
- ** `/preload`**: 预加载脚本
- 用于在渲染进程中安全地暴露主进程功能
- ** `/components`**: React组件
- 可复用的UI组件如对话框、输入框等
- ** `/pages`**: 应用的主要页面
- 如聊天界面、设置页面等
- ** `/store`**: 状态管理
- 可能使用Redux或MobX来管理应用状态
- ** `/utils`**: 工具函数
- 包含各种辅助函数和工具类
- ** `/styles`**: 全局样式文件
- 包含SCSS文件定义全局样式和主题
### 2. `/public`: 静态资源目录
- 包含图标、字体等静态文件
### 3. `/electron`: Electron相关配置
- 包含Electron的构建和打包配置
### 4. `/scripts`: 构建和开发脚本
- 包含npm脚本用于开发、构建和部署
### 5. `/types`: TypeScript类型定义
- 包含自定义的类型定义文件
### 6. `/tests`: 测试文件目录
- 包含单元测试和集成测试
### 7. `/docs`: 文档目录
- 包含项目文档、API文档等
### 8. `/config`: 配置文件目录
- 包含各种配置文件如webpack配置、环境变量等
### 9. `/migrations`: 数据库迁移文件
- 由于使用了Sequelize这里可能包含数据库结构的变更记录
### 10. `/models`: 数据模型
- 定义Sequelize的数据模型对应数据库表结构
## 主要功能实现
### 1. LLM提供商集成
- 可能在`/src/utils``/src/services`中实现与不同LLM API的集成
### 2. 多助手和多主题支持
-`/src/store`中管理助手和主题的状态
-`/src/components`中实现相关的UI组件
### 3. 多模型对话
-`/src/pages`的聊天界面中实现
- 可能使用`/src/store`来管理对话状态
### 4. 拖放排序
-`/src/components`中实现相关的可拖拽组件
### 5. 代码高亮
- 可能使用第三方库如Prism.js集成在`/src/components`
### 6. Mermaid图表支持
-`/src/components`中集成Mermaid库
### 7. 数据持久化
- 使用Sequelize在`/models`中定义数据模型
-`/migrations`中管理数据库结构变更

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.
本协议的解释、效力及争议的解决,均适用中华人民共和国法律。 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.
**7. 联系信息**
- 许可方联系方式:
- 手机号18539907620
- 邮箱kangfenmao@qq.com
**许可方(签字):**
**日期:**
**被许可方(签字):**
**日期:**
--- ---
#### 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** 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.
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:**

View File

@@ -1,17 +1,32 @@
<div 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>
</div>
<div align="center">
English | <a href="./docs/README.zh.md">中文</a> | <a href="./docs/README.ja.md">日本語</a>
</div>
# 🍒 Cherry Studio # 🍒 Cherry Studio
![](https://github.com/user-attachments/assets/7b4f2f78-5cbe-4be8-9aec-f98d8405a505)
Cherry Studio is a desktop client that supports for multiple LLM providers, available on Windows, Mac and Linux. Cherry Studio is a desktop client that supports for multiple LLM providers, available on Windows, Mac and Linux.
👏 Join [Telegram Group](https://t.me/CherryStudioAI)
# 🌠 Screenshot # 🌠 Screenshot
![](https://github.com/user-attachments/assets/e24d1e7d-126a-4647-bd98-f470bfe26fde) ![](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/3f3f0bfa-cb88-4abf-923a-a0859fa3c912) ![](https://github.com/user-attachments/assets/790790d7-b462-48dd-bde1-91c1697a4648)
![](https://github.com/user-attachments/assets/288560c1-d218-437c-87c2-2a5e87b43b93)
# 🌟 Features # 🌟 Features
<div align="center">
<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>
</div>
1. Support for Multiple LLM Providers. 1. Support for Multiple LLM Providers.
2. Allows creation of multiple Assistants. 2. Allows creation of multiple Assistants.
3. Enables creation of multiple topics. 3. Enables creation of multiple topics.
@@ -22,9 +37,9 @@ Cherry Studio is a desktop client that supports for multiple LLM providers, avai
# 🖥️ Develop # 🖥️ Develop
## Recommended IDE Setup ## IDE Setup
- [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) [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)
## Project Setup ## Project Setup
@@ -57,6 +72,20 @@ $ yarn build:linux
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline) [![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)
# 🚀 Contributors
<a href="https://github.com/kangfenmao/cherry-studio/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kangfenmao/cherry-studio" />
</a>
# Community
[Telegram](https://t.me/CherryStudioAI)
# Sponsor
[Buy Me a Coffee](docs/sponsor.md)
# 📃 License # 📃 License
[LICENSE](./LICENSE) [LICENSE](./LICENSE)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 KiB

After

Width:  |  Height:  |  Size: 210 KiB

BIN
build/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

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

@@ -0,0 +1,91 @@
<div 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>
</div>
<div align="center">
<a href="./README.md">English</a> | <a href="./README.zh.md">中文</a> | 日本語
</div>
# 🍒 Cherry Studio
![](https://github.com/user-attachments/assets/7b4f2f78-5cbe-4be8-9aec-f98d8405a505)
Cherry Studioは、複数のLLMプロバイダーをサポートするデスクトップクライアントで、Windows、Mac、Linuxで利用可能です。
👏 [Telegramグループ](https://t.me/CherryStudioAI)に参加しましょう
# 🌠 スクリーンショット
![](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)
# 🌟 特徴
<div align="center">
<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>
</div>
1. 複数のLLMプロバイダーをサポート。
2. 複数のアシスタントを作成可能。
3. 複数のトピックを作成可能。
4. 同じ会話で複数のモデルを使用して質問に回答可能。
5. ドラッグアンドドロップでの並べ替えをサポート。
6. コードハイライト。
7. Mermaidチャート
# 🖥️ 開発
## IDEの設定
[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)
## プロジェクトの設定
### インストール
```bash
$ yarn
```
### 開発
```bash
$ yarn dev
```
### ビルド
```bash
# Windowsの場合
$ yarn build:win
# macOSの場合
$ yarn build:mac
# Linuxの場合
$ yarn build:linux
```
# ⭐️ スター履歴
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)
# 🚀 コントリビューター
<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)
# スポンサー
[Buy Me a Coffee](sponsor.md)
# 📃 ライセンス
[LICENSE](./LICENSE)

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

@@ -0,0 +1,110 @@
<div 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>
</div>
<div align="center">
中文 / <a href="https://github.com/kangfenmao/cherry-studio">English</a> / <a href="./README.ja.md">日本語</a>
</div>
# 🍒 Cherry Studio
![](https://github.com/user-attachments/assets/995910f3-177a-4d1e-97ea-04e3b009ba36)
Cherry Studio 是一款跨平台桌面客户端支持多个大语言模型LLM服务商兼容 Windows、Mac 和 Linux 系统,并拥丰富的个性化选项与领先的功能设计。
👏 欢迎加入 [Telegram 群组](https://t.me/CherryStudioAI)
# 🌠 界面
<img width="1582" alt="Xnip2024-09-23_15-01-53" src="https://github.com/user-attachments/assets/554aa31b-87b6-49fe-877d-af313e1608b0">
<img width="1582" alt="Xnip2024-09-23_15-02-27" src="https://github.com/user-attachments/assets/f43fb4c8-194a-4f46-8575-6db2bd136cb9">
<img width="1582" alt="Xnip2024-09-23_16-12-19" src="https://github.com/user-attachments/assets/82ce3cc1-5a0b-49aa-9fe4-0376d34be1f8">
<img width="1582" alt="Xnip2024-09-23_16-11-44" src="https://github.com/user-attachments/assets/55e420c8-fc0f-40a0-868e-d75bebeb5af3">
<img width="1582" alt="Xnip2024-09-23_16-11-50" src="https://github.com/user-attachments/assets/7413384e-a7c7-4525-96ea-ccd395d7e51a">
<img width="1582" alt="Xnip2024-09-23_16-12-59" src="https://github.com/user-attachments/assets/894b5e97-569f-4471-813c-c48d19455215">
# 🌟 特性
<div align="center">
<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>
</div>
## 😌 轻松上手
🍏WindowsMacLinux跨平台支持
📦开箱即用,无需 Python 与 Docker
🤝简洁、友好的界面与交互设计
## 🛠️多样化的 LLM 服务模式支持
☁️ 全面覆盖 LLM 云服务,支持自定义 api key 与模型管理OpenAIGeminiAnthropic硅基流动...
🔗汇聚流行的 AI Web 服务并计划通过功能增强提升体验ClaudePeplexityPoe腾讯元宝知乎直答...
💻支持 Ollama 运行本地模型
## 📲个性化的功能体验
📄完整的 Markdown 与 Mermaid 渲染支持
🤖使用与创建智能体提升工作效率
🔤持续迭代的翻译功能
🤲生成结果支持 Markdown 与图片分享
📎文件与图片上传RAG 与多模态对话
🎨透明窗口与明暗主题支持
# 🖥️ 开发指南
## 开发环境
[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)
## 项目设置
### 安装依赖
```bash
$ yarn
```
### 启动开发环境
```bash
$ yarn dev
```
### 构建版本
```bash
# For windows
$ yarn build:win
# For macOS
$ yarn build:mac
# For Linux
$ yarn build:linux
```
# ⭐️ Star 记录
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)
# 社区
[Telegram](https://t.me/CherryStudioAI)
# 赞助
[微信赞赏码](sponsor.md)
# 📃 许可证
[LICENSE](./LICENSE)

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

@@ -8,7 +8,8 @@ files:
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}' - '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
- '!src/*' - '!src'
- '!scripts'
- '!local' - '!local'
asarUnpack: asarUnpack:
- resources/** - resources/**
@@ -19,6 +20,8 @@ nsis:
shortcutName: ${productName} shortcutName: ${productName}
uninstallDisplayName: ${productName} uninstallDisplayName: ${productName}
createDesktopShortcut: always createDesktopShortcut: always
allowToChangeInstallationDirectory: true
oneClick: false
mac: mac:
entitlementsInherit: build/entitlements.mac.plist entitlementsInherit: build/entitlements.mac.plist
extendInfo: extendInfo:
@@ -40,13 +43,16 @@ dmg:
artifactName: ${productName}-${version}-${arch}.${ext} artifactName: ${productName}-${version}-${arch}.${ext}
linux: linux:
target: target:
- AppImage - target: AppImage
arch:
- arm64
- x64
# - snap # - snap
# - deb # - deb
maintainer: electronjs.org maintainer: electronjs.org
category: Utility category: Utility
appImage: appImage:
artifactName: ${productName}-${version}.${ext} artifactName: ${productName}-${version}-${arch}.${ext}
npmRebuild: false npmRebuild: false
publish: publish:
provider: github provider: github
@@ -57,14 +63,10 @@ electronDownload:
afterSign: scripts/notarize.js afterSign: scripts/notarize.js
releaseInfo: releaseInfo:
releaseNotes: | releaseNotes: |
本次更新: 增加300个智能体 by @cawabj
支持行内公式 增加快捷键设置界面
支持编辑所有集成的服务商API地址 支持快捷键缩放整体界面大小
新增智能体搜索功能(>10个) 修复 WebDAV 备份文件大小限制
修复正则表达式显示错误 修复跨平台备份回复后文件路径错误问题
修复默认模型参数不生效 请求头增加 X-Api-Key
修复暗黑模式下分界线不明显问题 修复消息滚动跳跃问题
近期更新:
智能助理和消息列表合并
优化输入框样式
提升小程序稳定性

View File

@@ -7,7 +7,8 @@ export default defineConfig({
plugins: [externalizeDepsPlugin()], plugins: [externalizeDepsPlugin()],
resolve: { resolve: {
alias: { alias: {
ollama: resolve('ollama/src') '@types': resolve('src/renderer/src/types'),
'@main': resolve('src/main')
} }
} }
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "CherryStudio", "name": "CherryStudio",
"version": "0.6.13", "version": "0.8.5",
"private": true, "private": true,
"description": "A powerful AI assistant for producer.", "description": "A powerful AI assistant for producer.",
"main": "./out/main/index.js", "main": "./out/main/index.js",
@@ -8,7 +8,11 @@
"homepage": "https://github.com/kangfenmao/cherry-studio", "homepage": "https://github.com/kangfenmao/cherry-studio",
"workspaces": { "workspaces": {
"packages": [ "packages": [
"local" "local",
"packages/*"
],
"nohoist": [
"packages/database"
] ]
}, },
"scripts": { "scripts": {
@@ -26,15 +30,24 @@
"build:mac": "dotenv electron-vite build && electron-builder --mac --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:linux": "dotenv electron-vite build && electron-builder --linux --publish never",
"release": "node scripts/version.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"
}, },
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"archiver": "^7.0.1",
"electron-log": "^5.1.5", "electron-log": "^5.1.5",
"electron-store": "^8.2.0", "electron-store": "^8.2.0",
"electron-updater": "^6.1.7", "electron-updater": "^6.3.9",
"electron-window-state": "^5.0.3" "electron-window-state": "^5.0.3",
"fs-extra": "^11.2.0",
"html2canvas": "^1.4.1",
"officeparser": "^4.1.1",
"unzipper": "^0.12.3",
"webdav": "4.11.4"
}, },
"devDependencies": { "devDependencies": {
"@anthropic-ai/sdk": "^0.24.3", "@anthropic-ai/sdk": "^0.24.3",
@@ -45,18 +58,25 @@
"@hello-pangea/dnd": "^16.6.0", "@hello-pangea/dnd": "^16.6.0",
"@kangfenmao/keyv-storage": "^0.1.0", "@kangfenmao/keyv-storage": "^0.1.0",
"@reduxjs/toolkit": "^2.2.5", "@reduxjs/toolkit": "^2.2.5",
"@types/fs-extra": "^11",
"@types/lodash": "^4.17.5", "@types/lodash": "^4.17.5",
"@types/node": "^18.19.9", "@types/node": "^18.19.9",
"@types/react": "^18.2.48", "@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@types/tinycolor2": "^1",
"@types/unzipper": "^0",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"antd": "^5.18.3", "antd": "^5.18.3",
"axios": "^1.7.3", "axios": "^1.7.3",
"browser-image-compression": "^2.0.2", "browser-image-compression": "^2.0.2",
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7",
"dotenv-cli": "^7.4.2", "dotenv-cli": "^7.4.2",
"electron": "^28.3.3", "electron": "^28.3.3",
"electron-builder": "^24.9.1", "electron-builder": "^24.9.1",
"electron-devtools-installer": "^3.2.0",
"electron-icon-builder": "^2.0.1",
"electron-vite": "^2.0.0", "electron-vite": "^2.0.0",
"emittery": "^1.0.3", "emittery": "^1.0.3",
"emoji-picker-element": "^1.22.1", "emoji-picker-element": "^1.22.1",
@@ -65,10 +85,11 @@
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-unused-imports": "^4.0.0", "eslint-plugin-unused-imports": "^4.0.0",
"gpt-tokens": "^1.3.6", "gpt-tokens": "^1.3.10",
"i18next": "^23.11.5", "i18next": "^23.11.5",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mime": "^4.0.4",
"openai": "^4.52.1", "openai": "^4.52.1",
"prettier": "^3.2.4", "prettier": "^3.2.4",
"react": "^18.2.0", "react": "^18.2.0",
@@ -82,12 +103,15 @@
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
"redux": "^5.0.1", "redux": "^5.0.1",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",
"rehype-katex": "^7.0.0", "rehype-katex": "^7.0.1",
"rehype-mathjax": "^6.0.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
"sass": "^1.77.2", "sass": "^1.77.2",
"styled-components": "^6.1.11", "styled-components": "^6.1.11",
"typescript": "^5.3.3", "tinycolor2": "^1.6.0",
"typescript": "^5.6.2",
"uuid": "^10.0.0", "uuid": "^10.0.0",
"vite": "^5.0.12" "vite": "^5.0.12"
}, },
@@ -96,8 +120,7 @@
"react-dom": "^17.0.0 || ^18.0.0" "react-dom": "^17.0.0 || ^18.0.0"
}, },
"resolutions": { "resolutions": {
"@electron/notarize": "2.3.2",
"@electron/notarize@npm:2.2.1": "patch:@electron/notarize@npm%3A2.3.2#~/.yarn/patches/@electron-notarize-npm-2.3.2-535908a4bd.patch" "@electron/notarize@npm:2.2.1": "patch:@electron/notarize@npm%3A2.3.2#~/.yarn/patches/@electron-notarize-npm-2.3.2-535908a4bd.patch"
}, },
"packageManager": "yarn@4.3.1" "packageManager": "yarn@4.5.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,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

@@ -1,15 +1,33 @@
import fs from 'node:fs'
import { app } from 'electron'
import Store from 'electron-store' import Store from 'electron-store'
import path from 'path'
const isDev = process.env.NODE_ENV === 'development'
isDev && app.setPath('userData', app.getPath('userData') + 'Dev')
const getDataPath = () => {
const dataPath = path.join(app.getPath('userData'), 'Data')
if (!fs.existsSync(dataPath)) {
fs.mkdirSync(dataPath, { recursive: true })
}
return dataPath
}
export const DATA_PATH = getDataPath()
export const appConfig = new Store() export const appConfig = new Store()
export const titleBarOverlayDark = { export const titleBarOverlayDark = {
height: 41, height: 40,
color: '#00000000', color: '#00000000',
symbolColor: '#ffffff' symbolColor: '#ffffff'
} }
export const titleBarOverlayLight = { export const titleBarOverlayLight = {
height: 41, height: 40,
color: '#00000000', color: '#00000000',
symbolColor: '#000000' symbolColor: '#000000'
} }

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

@@ -0,0 +1,91 @@
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 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 代码文件
]

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,7 +1,9 @@
import { electronApp, optimizer } from '@electron-toolkit/utils' import { electronApp, optimizer } from '@electron-toolkit/utils'
import { app, BrowserWindow } from 'electron' import { app, BrowserWindow } from 'electron'
import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer'
import { registerIpc } from './ipc' import { registerIpc } from './ipc'
import { registerZoomShortcut } from './shortcut'
import { updateUserDataPath } from './utils/upgrade' import { updateUserDataPath } from './utils/upgrade'
import { createMainWindow } from './window' import { createMainWindow } from './window'
@@ -12,7 +14,7 @@ app.whenReady().then(async () => {
await updateUserDataPath() await updateUserDataPath()
// Set app user model id for windows // Set app user model id for windows
electronApp.setAppUserModelId('com.kangfenmao.CherryStudio') electronApp.setAppUserModelId(import.meta.env.VITE_MAIN_BUNDLE_ID || 'com.kangfenmao.CherryStudio')
// Default open or close DevTools by F12 in development // Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production. // and ignore CommandOrControl + R in production.
@@ -29,7 +31,15 @@ app.whenReady().then(async () => {
const mainWindow = createMainWindow() const mainWindow = createMainWindow()
registerZoomShortcut(mainWindow)
registerIpc(mainWindow, app) 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))
}
}) })
// Quit when all windows are closed, except on macOS. There, it's common // Quit when all windows are closed, except on macOS. There, it's common

View File

@@ -1,19 +1,26 @@
import path from 'node:path'
import { BrowserWindow, ipcMain, session, shell } from 'electron' import { BrowserWindow, ipcMain, session, shell } from 'electron'
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config' import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
import AppUpdater from './updater' import AppUpdater from './services/AppUpdater'
import { openFile, saveFile } from './utils/file' import BackupManager from './services/BackupManager'
import FileManager from './services/FileManager'
import { compress, decompress } from './utils/zip' import { compress, decompress } from './utils/zip'
import { createMinappWindow } from './window' import { createMinappWindow } from './window'
const fileManager = new FileManager()
const backupManager = new BackupManager()
export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
const { autoUpdater } = new AppUpdater(mainWindow) const { autoUpdater } = new AppUpdater(mainWindow)
// IPC // IPC
ipcMain.handle('get-app-info', () => ({ ipcMain.handle('app:info', () => ({
version: app.getVersion(), version: app.getVersion(),
isPackaged: app.isPackaged, isPackaged: app.isPackaged,
appPath: app.getAppPath() appPath: app.getAppPath(),
filesPath: path.join(app.getPath('userData'), 'Data', 'Files')
})) }))
ipcMain.handle('open-website', (_, url: string) => { ipcMain.handle('open-website', (_, url: string) => {
@@ -24,12 +31,28 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
session.defaultSession.setProxy(proxy ? { proxyRules: proxy } : {}) session.defaultSession.setProxy(proxy ? { proxyRules: proxy } : {})
}) })
ipcMain.handle('save-file', saveFile)
ipcMain.handle('open-file', openFile)
ipcMain.handle('reload', () => mainWindow.reload()) ipcMain.handle('reload', () => mainWindow.reload())
ipcMain.handle('zip:compress', (_, text: string) => compress(text)) ipcMain.handle('zip:compress', (_, text: string) => compress(text))
ipcMain.handle('zip:decompress', (_, text: Buffer) => decompress(text)) ipcMain.handle('zip:decompress', (_, text: Buffer) => decompress(text))
ipcMain.handle('backup:backup', backupManager.backup)
ipcMain.handle('backup:restore', backupManager.restore)
ipcMain.handle('backup:backupToWebdav', backupManager.backupToWebdav)
ipcMain.handle('backup:restoreFromWebdav', backupManager.restoreFromWebdav)
ipcMain.handle('file:open', fileManager.open)
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('minapp', (_, args) => { ipcMain.handle('minapp', (_, args) => {
createMinappWindow({ createMinappWindow({

View File

@@ -20,8 +20,10 @@ export default class AppUpdater {
autoUpdater.on('update-available', (releaseInfo: UpdateInfo) => { autoUpdater.on('update-available', (releaseInfo: UpdateInfo) => {
autoUpdater.logger?.info('检测到新版本,确认是否下载') autoUpdater.logger?.info('检测到新版本,确认是否下载')
mainWindow.webContents.send('update-available', releaseInfo) mainWindow.webContents.send('update-available', releaseInfo)
const releaseNotes = releaseInfo.releaseNotes const releaseNotes = releaseInfo.releaseNotes
let releaseContent = '' let releaseContent = ''
if (releaseNotes) { if (releaseNotes) {
if (typeof releaseNotes === 'string') { if (typeof releaseNotes === 'string') {
releaseContent = <string>releaseNotes releaseContent = <string>releaseNotes

View File

@@ -0,0 +1,116 @@
import { WebDavConfig } from '@types'
import archiver from 'archiver'
import { app } from 'electron'
import Logger from 'electron-log'
import * as fs from 'fs-extra'
import * as path from 'path'
import * as unzipper from 'unzipper'
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)
// 创建 zip 文件
const output = fs.createWriteStream(path.join(destinationPath, fileName))
const archive = archiver('zip', { zlib: { level: 9 } })
archive.pipe(output)
archive.directory(this.tempDir, false)
await archive.finalize()
// 清理临时目录
await fs.remove(this.tempDir)
Logger.log('Backup completed successfully')
const backupedFilePath = path.join(destinationPath, fileName)
return backupedFilePath
} catch (error) {
Logger.error('Backup failed:', error)
throw error
}
}
async restore(_: Electron.IpcMainInvokeEvent, backupPath: string): Promise<string> {
// 创建临时目录
await fs.ensureDir(this.tempDir)
// 解压备份文件到临时目录
await fs
.createReadStream(backupPath)
.pipe(unzipper.Extract({ path: this.tempDir }))
.promise()
// 读取 data.json
const dataPath = path.join(this.tempDir, 'data.json')
const data = await fs.readFile(dataPath, 'utf-8')
// 恢复 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)
// 清理临时目录
await fs.remove(this.tempDir)
Logger.log('Restore completed successfully')
return data
}
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,320 @@
import { documentExts } from '@main/constant'
import { getFileType } from '@main/utils/file'
import { FileType } from '@types'
import * as crypto from 'crypto'
import {
app,
dialog,
OpenDialogOptions,
OpenDialogReturnValue,
SaveDialogOptions,
SaveDialogReturnValue
} 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 FileManager {
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)
}
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)
const destPath = path.join(this.storageDir, uuid + ext)
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 clear = async (): Promise<void> => {
await fs.promises.rmdir(this.storageDir, { recursive: true })
await this.initStorageDir()
}
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 save = async (
_: Electron.IpcMainInvokeEvent,
fileName: string,
content: string,
options?: SaveDialogOptions
): Promise<void> => {
try {
const result: SaveDialogReturnValue = await dialog.showSaveDialog({
title: '保存文件',
defaultPath: fileName,
...options
})
if (!result.canceled && result.filePath) {
await writeFileSync(result.filePath, content, { encoding: 'utf-8' })
}
} catch (err) {
logger.error('[IPC - Error]', 'An error occurred saving the file:', err)
}
}
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
}
}
}
export default FileManager

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
}
}
}

26
src/main/shortcut.ts Normal file
View File

@@ -0,0 +1,26 @@
import { BrowserWindow, globalShortcut } from 'electron'
export function registerZoomShortcut(mainWindow: BrowserWindow) {
// 注册放大快捷键 (Ctrl+Plus 或 Cmd+Plus)
globalShortcut.register('CommandOrControl+=', () => {
if (mainWindow) {
const currentZoom = mainWindow.webContents.getZoomFactor()
mainWindow.webContents.setZoomFactor(currentZoom + 0.1)
}
})
// 注册缩小快捷键 (Ctrl+Minus 或 Cmd+Minus)
globalShortcut.register('CommandOrControl+-', () => {
if (mainWindow) {
const currentZoom = mainWindow.webContents.getZoomFactor()
mainWindow.webContents.setZoomFactor(currentZoom - 0.1)
}
})
// 注册重置缩放快捷键 (Ctrl+0 或 Cmd+0)
globalShortcut.register('CommandOrControl+0', () => {
if (mainWindow) {
mainWindow.webContents.setZoomFactor(1)
}
})
}

View File

@@ -1,55 +1,13 @@
import { dialog, OpenDialogOptions, OpenDialogReturnValue, SaveDialogOptions, SaveDialogReturnValue } from 'electron' import { audioExts, documentExts, imageExts, textExts, videoExts } from '@main/constant'
import logger from 'electron-log'
import { writeFile } from 'fs'
import { readFile } from 'fs/promises'
export async function saveFile( import { FileTypes } from '../../renderer/src/types'
_: Electron.IpcMainInvokeEvent,
fileName: string,
content: string,
options?: SaveDialogOptions
): Promise<void> {
try {
const result: SaveDialogReturnValue = await dialog.showSaveDialog({
title: '保存文件',
defaultPath: fileName,
...options
})
if (!result.canceled && result.filePath) { export function getFileType(ext: string): FileTypes {
writeFile(result.filePath, content, { encoding: 'utf-8' }, (err) => { ext = ext.toLowerCase()
if (err) { if (imageExts.includes(ext)) return FileTypes.IMAGE
logger.error('[IPC - Error]', 'An error occurred saving the file:', err) 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
} catch (err) { return FileTypes.OTHER
logger.error('[IPC - Error]', 'An error occurred saving the file:', err)
}
}
export async function openFile(
_: Electron.IpcMainInvokeEvent,
options: OpenDialogOptions
): Promise<{ fileName: 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, content }
}
return null
} catch (err) {
logger.error('[IPC - Error]', 'An error occurred opening the file:', err)
return null
}
} }

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

@@ -0,0 +1,7 @@
import path from 'node:path'
import { app } from 'electron'
export function getResourcePath() {
return path.join(app.getAppPath(), 'resources')
}

View File

@@ -16,6 +16,8 @@ export function createMainWindow() {
const theme = appConfig.get('theme') || 'light' const theme = appConfig.get('theme') || 'light'
// Create the browser window. // Create the browser window.
const isMac = process.platform === 'darwin'
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
x: mainWindowState.x, x: mainWindowState.x,
y: mainWindowState.y, y: mainWindowState.y,
@@ -25,10 +27,12 @@ export function createMainWindow() {
minHeight: 600, minHeight: 600,
show: true, show: true,
autoHideMenuBar: true, autoHideMenuBar: true,
transparent: process.platform === 'darwin', transparent: isMac,
vibrancy: 'fullscreen-ui', vibrancy: 'fullscreen-ui',
visualEffectState: 'active',
titleBarStyle: 'hidden', titleBarStyle: 'hidden',
titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight, titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight,
backgroundColor: isMac ? undefined : theme === 'dark' ? '#181818' : '#FFFFFF',
trafficLightPosition: { x: 8, y: 12 }, trafficLightPosition: { x: 8, y: 12 },
...(process.platform === 'linux' ? { icon } : {}), ...(process.platform === 'linux' ? { icon } : {}),
webPreferences: { webPreferences: {
@@ -44,11 +48,9 @@ export function createMainWindow() {
mainWindow.webContents.on('context-menu', () => { mainWindow.webContents.on('context-menu', () => {
const menu = new Menu() const menu = new Menu()
menu.append(new MenuItem({ label: '复制', role: 'copy', sublabel: '⌘ + C' })) menu.append(new MenuItem({ label: '复制', role: 'copy' }))
menu.append(new MenuItem({ label: '粘贴', role: 'paste', sublabel: '⌘ + V' })) menu.append(new MenuItem({ label: '粘贴', role: 'paste' }))
menu.append(new MenuItem({ label: '剪切', role: 'cut', sublabel: '⌘ + X' })) menu.append(new MenuItem({ label: '剪切', role: 'cut' }))
menu.append(new MenuItem({ type: 'separator' }))
menu.append(new MenuItem({ label: '全选', role: 'selectAll', sublabel: '⌘ + A' }))
menu.popup() menu.popup()
}) })

View File

@@ -1,5 +1,8 @@
import { ElectronAPI } from '@electron-toolkit/preload' import { ElectronAPI } from '@electron-toolkit/preload'
import { FileType } from '@renderer/types'
import { WebDavConfig } from '@renderer/types'
import type { OpenDialogOptions } from 'electron' import type { OpenDialogOptions } from 'electron'
import { Readable } from 'stream'
declare global { declare global {
interface Window { interface Window {
@@ -9,17 +12,37 @@ declare global {
version: string version: string
isPackaged: boolean isPackaged: boolean
appPath: string appPath: string
filesPath: string
}> }>
checkForUpdate: () => void checkForUpdate: () => void
openWebsite: (url: string) => void openWebsite: (url: string) => void
setProxy: (proxy: string | undefined) => void setProxy: (proxy: string | undefined) => void
saveFile: (path: string, content: string | NodeJS.ArrayBufferView, options?: SaveDialogOptions) => void
openFile: (options?: OpenDialogOptions) => Promise<{ fileName: string; content: Buffer } | null>
setTheme: (theme: 'light' | 'dark') => void setTheme: (theme: 'light' | 'dark') => void
minApp: (options: { url: string; windowOptions?: Electron.BrowserWindowConstructorOptions }) => void minApp: (options: { url: string; windowOptions?: Electron.BrowserWindowConstructorOptions }) => void
reload: () => void reload: () => void
compress: (text: string) => Promise<Buffer> compress: (text: string) => Promise<Buffer>
decompress: (text: Buffer) => Promise<string> decompress: (text: Buffer) => Promise<string>
backup: {
backup: (fileName: string, data: string, destinationPath?: string) => Promise<Readable>
restore: (backupPath: string) => Promise<string>
backupToWebdav: (data: string, webdavConfig: WebDavConfig) => Promise<boolean>
restoreFromWebdav: (webdavConfig: WebDavConfig) => Promise<string>
}
file: {
select: (options?: OpenDialogOptions) => Promise<FileType[] | null>
upload: (file: FileType) => Promise<FileType>
delete: (fileId: string) => Promise<void>
read: (fileId: string) => Promise<string>
clear: () => Promise<void>
get: (filePath: string) => Promise<FileType | null>
selectFolder: () => Promise<string | null>
create: (fileName: string) => Promise<string>
write: (filePath: string, data: Uint8Array | string) => Promise<void>
open: (options?: OpenDialogOptions) => Promise<{ fileName: string; filePath: string; content: Buffer } | null>
save: (path: string, content: string | NodeJS.ArrayBufferView, options?: SaveDialogOptions) => void
saveImage: (name: string, data: string) => void
base64Image: (fileId: string) => Promise<{ mime: string; base64: string; data: string }>
}
} }
} }
} }

View File

@@ -1,21 +1,42 @@
import { electronAPI } from '@electron-toolkit/preload' import { electronAPI } from '@electron-toolkit/preload'
import { contextBridge, ipcRenderer } from 'electron' import { WebDavConfig } from '@types'
import { contextBridge, ipcRenderer, OpenDialogOptions } from 'electron'
// Custom APIs for renderer // Custom APIs for renderer
const api = { const api = {
getAppInfo: () => ipcRenderer.invoke('get-app-info'), getAppInfo: () => ipcRenderer.invoke('app:info'),
checkForUpdate: () => ipcRenderer.invoke('check-for-update'), checkForUpdate: () => ipcRenderer.invoke('check-for-update'),
openWebsite: (url: string) => ipcRenderer.invoke('open-website', url), openWebsite: (url: string) => ipcRenderer.invoke('open-website', url),
setProxy: (proxy: string) => ipcRenderer.invoke('set-proxy', proxy), setProxy: (proxy: string) => ipcRenderer.invoke('set-proxy', proxy),
setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('set-theme', theme), setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('set-theme', theme),
minApp: (url: string) => ipcRenderer.invoke('minapp', url), minApp: (url: string) => ipcRenderer.invoke('minapp', url),
openFile: (options?: { decompress: boolean }) => ipcRenderer.invoke('open-file', options),
reload: () => ipcRenderer.invoke('reload'), reload: () => ipcRenderer.invoke('reload'),
saveFile: (path: string, content: string, options?: { compress: boolean }) => {
ipcRenderer.invoke('save-file', path, content, options)
},
compress: (text: string) => ipcRenderer.invoke('zip:compress', text), compress: (text: string) => ipcRenderer.invoke('zip:compress', text),
decompress: (text: Buffer) => ipcRenderer.invoke('zip:decompress', text) decompress: (text: Buffer) => ipcRenderer.invoke('zip:decompress', text),
backup: {
backup: (fileName: string, data: string, destinationPath?: string) =>
ipcRenderer.invoke('backup:backup', fileName, data, destinationPath),
restore: (backupPath: string) => ipcRenderer.invoke('backup:restore', backupPath),
backupToWebdav: (data: string, webdavConfig: WebDavConfig) =>
ipcRenderer.invoke('backup:backupToWebdav', data, webdavConfig),
restoreFromWebdav: (webdavConfig: WebDavConfig) => ipcRenderer.invoke('backup:restoreFromWebdav', webdavConfig)
},
file: {
select: (options?: OpenDialogOptions) => ipcRenderer.invoke('file:select', options),
upload: (filePath: string) => ipcRenderer.invoke('file:upload', filePath),
delete: (fileId: string) => ipcRenderer.invoke('file:delete', fileId),
read: (fileId: string) => ipcRenderer.invoke('file:read', fileId),
clear: () => ipcRenderer.invoke('file:clear'),
get: (filePath: string) => ipcRenderer.invoke('file:get', filePath),
create: (fileName: string) => ipcRenderer.invoke('file:create', fileName),
write: (filePath: string, data: Uint8Array | string) => ipcRenderer.invoke('file:write', filePath, data),
open: (options?: { decompress: boolean }) => ipcRenderer.invoke('file:open', options),
save: (path: string, content: string, options?: { compress: boolean }) =>
ipcRenderer.invoke('file:save', path, content, options),
selectFolder: () => ipcRenderer.invoke('file:selectFolder'),
saveImage: (name: string, data: string) => ipcRenderer.invoke('file:saveImage', name, data),
base64Image: (fileId: string) => ipcRenderer.invoke('file:base64Image', fileId)
}
} }
// Use `contextBridge` APIs to expose Electron APIs to // Use `contextBridge` APIs to expose Electron APIs to

View File

@@ -1,14 +1,40 @@
<!doctype html> <!doctype html>
<html lang="zh-CN"> <html lang="zh-CN">
<head>
<meta charset="UTF-8" /> <head>
<meta name="viewport" content="initial-scale=1, width=device-width" /> <meta charset="UTF-8" />
<meta <meta name="viewport" content="initial-scale=1, width=device-width" />
http-equiv="Content-Security-Policy" <meta http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src *; script-src 'self' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: *; frame-src * file:" /> content="default-src 'self'; connect-src *; script-src 'self' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: *; frame-src * file:" />
</head> <style>
<body> html,
<div id="root"></div> body {
<script type="module" src="/src/main.tsx"></script> margin: 0;
</body> }
</html>
#spinner {
position: fixed;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
#spinner img {
width: 100px;
border-radius: 50px;
}
</style>
</head>
<body>
<div id="root"></div>
<div id="spinner">
<img src="/src/assets/images/logo.png" />
</div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -1,3 +1,5 @@
import '@renderer/databases'
import store, { persistor } from '@renderer/store' import store, { persistor } from '@renderer/store'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { HashRouter, Route, Routes } from 'react-router-dom' import { HashRouter, Route, Routes } from 'react-router-dom'
@@ -9,6 +11,8 @@ import AntdProvider from './context/AntdProvider'
import { ThemeProvider } from './context/ThemeProvider' import { ThemeProvider } from './context/ThemeProvider'
import AgentsPage from './pages/agents/AgentsPage' import AgentsPage from './pages/agents/AgentsPage'
import AppsPage from './pages/apps/AppsPage' import AppsPage from './pages/apps/AppsPage'
import FilesPage from './pages/files/FilesPage'
import HistoryPage from './pages/history/HistoryPage'
import HomePage from './pages/home/HomePage' import HomePage from './pages/home/HomePage'
import SettingsPage from './pages/settings/SettingsPage' import SettingsPage from './pages/settings/SettingsPage'
import TranslatePage from './pages/translate/TranslatePage' import TranslatePage from './pages/translate/TranslatePage'
@@ -24,9 +28,11 @@ function App(): JSX.Element {
<Sidebar /> <Sidebar />
<Routes> <Routes>
<Route path="/" element={<HomePage />} /> <Route path="/" element={<HomePage />} />
<Route path="/files" element={<FilesPage />} />
<Route path="/agents" element={<AgentsPage />} /> <Route path="/agents" element={<AgentsPage />} />
<Route path="/translate" element={<TranslatePage />} /> <Route path="/translate" element={<TranslatePage />} />
<Route path="/apps" element={<AppsPage />} /> <Route path="/apps" element={<AppsPage />} />
<Route path="/messages/*" element={<HistoryPage />} />
<Route path="/settings/*" element={<SettingsPage />} /> <Route path="/settings/*" element={<SettingsPage />} />
</Routes> </Routes>
</HashRouter> </HashRouter>

View File

@@ -0,0 +1,18 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="4" fill="black"/>
<g filter="url(#filter0_i_2119_154)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.64368 11.7731C7.91976 11.7731 7.20901 11.5147 6.80099 10.9591L6.65707 11.6143L4 13L4.28684 11.6143L6.22186 3H8.59103L7.9066 6.03634C8.45941 5.44199 8.97273 5.22234 9.63083 5.22234C11.0523 5.22234 12 6.1397 12 7.81938C12 9.55074 10.9076 11.7731 8.64368 11.7731ZM9.55186 8.31036C9.55186 9.11144 8.97273 9.71871 8.22249 9.71871C7.8013 9.71871 7.4196 9.56366 7.16952 9.29233L7.53806 7.70309C7.81447 7.43176 8.13036 7.27671 8.49889 7.27671C9.06486 7.27671 9.55186 7.69017 9.55186 8.31036Z" fill="white"/>
</g>
<defs>
<filter id="filter0_i_2119_154" x="4" y="3" width="8" height="10" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.0192413"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.95 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_2119_154"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none">
<path
fill="#FFD21E"
d="M4 15.55C4 9.72 8.72 5 14.55 5h4.11a9.34 9.34 0 1 1 0 18.68H7.58l-2.89 2.8a.41.41 0 0 1-.69-.3V15.55Z"
/>
<path
fill="#32343D"
d="M19.63 12.48c.37.14.52.9.9.7.71-.38.98-1.27.6-1.98a1.46 1.46 0 0 0-1.98-.61 1.47 1.47 0 0 0-.6 1.99c.17.34.74-.21 1.08-.1ZM12.72 12.48c-.37.14-.52.9-.9.7a1.47 1.47 0 0 1-.6-1.98 1.46 1.46 0 0 1 1.98-.61c.71.38.98 1.27.6 1.99-.18.34-.74-.21-1.08-.1ZM16.24 19.55c2.89 0 3.82-2.58 3.82-3.9 0-1.33-1.71.7-3.82.7-2.1 0-3.8-2.03-3.8-.7 0 1.32.92 3.9 3.8 3.9Z"
/>
<path
fill="#FF323D"
d="M18.56 18.8c-.57.44-1.33.75-2.32.75-.92 0-1.65-.27-2.2-.68.3-.63.87-1.11 1.55-1.32.12-.03.24.17.36.38.12.2.24.4.37.4s.26-.2.39-.4.26-.4.38-.36a2.56 2.56 0 0 1 1.47 1.23Z"
/>
</svg>

After

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 195 KiB

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_2" data-name="图层_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.45 66.73">
<defs>
<style>
.cls-1 {
fill: #ea5e5d;
}
.cls-2 {
fill: #23af69;
}
.cls-3 {
fill: #ea5756;
}
</style>
</defs>
<g id="_图层_1-2" data-name="图层_1">
<g>
<g>
<g>
<path class="cls-1" d="M16.72,51.21c-4.45,0-8.64-1.78-11.81-5.01-3.17-3.23-4.91-7.51-4.91-12.04s1.74-8.81,4.91-12.04,7.36-5.01,11.81-5.01,8.71,1.82,11.82,4.99c2.32,2.36,2.32,6.2,0,8.56-2.32,2.36-6.08,2.36-8.4,0-.9-.92-2.15-1.45-3.43-1.45-2.63,0-4.85,2.26-4.85,4.94s2.22,4.94,4.85,4.94c1.28,0,2.52-.53,3.43-1.45,2.32-2.36,6.08-2.36,8.4,0,2.32,2.36,2.32,6.2,0,8.56-3.11,3.17-7.42,4.99-11.82,4.99Z"/>
<path class="cls-1" d="M32.05,66.73c-4.45,0-8.64-1.78-11.81-5.01s-4.91-7.51-4.91-12.04,1.79-8.88,4.9-12.06c2.32-2.36,6.08-2.36,8.4,0,2.32,2.36,2.32,6.2,0,8.56-.9.92-1.42,2.19-1.42,3.49,0,2.68,2.22,4.94,4.85,4.94s4.85-2.26,4.85-4.94c0-.95-.23-2.31-1.32-3.43-3.13-3.19-4.92-7.6-4.92-12.09s1.74-8.81,4.91-12.04,7.36-5.01,11.81-5.01,8.64,1.78,11.81,5.01,4.91,7.51,4.91,12.04-1.79,8.88-4.9,12.06c-2.32,2.36-6.08,2.36-8.4,0-2.32-2.36-2.32-6.2,0-8.56.9-.92,1.42-2.19,1.42-3.49,0-2.68-2.22-4.94-4.85-4.94s-4.85,2.26-4.85,4.94c0,1.31.53,2.6,1.45,3.53,3.1,3.16,4.8,7.42,4.8,11.99s-1.74,8.81-4.91,12.04c-3.17,3.23-7.36,5.01-11.81,5.01Z"/>
</g>
<path class="cls-2" d="M32.05,19.09l-9.72-9.12c-1.5-1.4-1.57-3.75-.17-5.25,1.4-1.49,3.75-1.57,5.25-.17l3.89,3.65,5.53-6.83c1.29-1.59,3.63-1.84,5.22-.55,1.59,1.29,1.84,3.63.55,5.22l-10.56,13.05Z"/>
</g>
<g>
<path class="cls-3" d="M93.93,24.6l.55-.39c.69-.4,1.17-.61,1.46-.61.63,0,1.3.57,2.03,1.7.44.71.67,1.27.67,1.7s-.14.78-.41,1.06c-.27.28-.59.54-.96.76-.36.22-.71.43-1.05.64-.33.2-1.02.47-2.05.79-1.03.32-2.03.49-2.99.49s-1.93-.13-2.91-.38c-.98-.25-1.99-.68-3.03-1.27-1.04-.6-1.98-1.32-2.81-2.18-.83-.86-1.51-1.96-2.05-3.31-.54-1.35-.8-2.81-.8-4.38s.26-3.01.79-4.29c.53-1.28,1.2-2.35,2.02-3.19.82-.84,1.75-1.54,2.81-2.11,1.98-1.09,3.97-1.64,5.98-1.64.95,0,1.92.15,2.9.44.98.29,1.72.59,2.23.9l.73.42c.36.22.65.4.85.55.53.42.79.91.79,1.44s-.21,1.1-.64,1.68c-.79,1.09-1.5,1.64-2.12,1.64-.36,0-.88-.22-1.55-.67-.85-.69-1.98-1.03-3.4-1.03-1.31,0-2.61.46-3.88,1.36-.61.44-1.11,1.07-1.52,1.88-.4.81-.61,1.72-.61,2.75s.2,1.94.61,2.75c.4.81.92,1.45,1.55,1.91,1.23.89,2.52,1.34,3.85,1.34.63,0,1.22-.08,1.77-.24.56-.16.96-.32,1.2-.49Z"/>
<path class="cls-3" d="M114.38,9.07c.16-.3.43-.52.82-.64.38-.12.87-.18,1.46-.18s1.05.05,1.4.15c.34.1.61.22.79.36.18.14.32.34.42.61.1.34.15.87.15,1.58v16.84c0,.47-.02.81-.05,1.05-.03.23-.13.5-.29.8-.28.55-1.07.82-2.37.82-1.42,0-2.25-.37-2.49-1.12-.12-.34-.18-.87-.18-1.58v-6.16h-8.04v6.19c0,.47-.02.81-.05,1.05-.03.23-.13.5-.29.8-.28.55-1.07.82-2.37.82-1.42,0-2.25-.37-2.49-1.12-.12-.34-.18-.87-.18-1.58V10.92c0-.46.02-.81.05-1.05.03-.23.13-.5.29-.8.28-.55,1.07-.82,2.37-.82,1.42,0,2.25.37,2.52,1.12.1.34.15.87.15,1.58v6.19h8.04v-6.22c0-.46.02-.81.05-1.05.03-.23.13-.5.29-.8Z"/>
<path class="cls-3" d="M127.21,25.1h9.34c.47,0,.81.02,1.05.05.23.03.5.13.8.29.55.28.82,1.07.82,2.37,0,1.42-.37,2.25-1.12,2.49-.34.12-.87.18-1.58.18h-12.01c-1.42,0-2.25-.38-2.49-1.15-.12-.32-.18-.84-.18-1.55V10.9c0-1.03.19-1.73.58-2.11.38-.37,1.11-.56,2.18-.56h11.95c.47,0,.81.02,1.05.05.23.03.5.13.8.29.55.28.82,1.07.82,2.37,0,1.42-.37,2.25-1.12,2.49-.34.12-.87.18-1.58.18h-9.31v3.06h6.01c.46,0,.81.02,1.05.05.23.03.5.13.8.29.55.28.82,1.07.82,2.37,0,1.42-.38,2.25-1.15,2.49-.34.12-.87.18-1.58.18h-5.95v3.06Z"/>
<path class="cls-3" d="M196.96,8.79c.99.69,1.49,1.35,1.49,2,0,.38-.23.92-.7,1.61l-6.55,9.8v5.79c0,.47-.02.81-.05,1.05-.03.23-.13.5-.29.8-.16.3-.43.52-.82.64-.38.12-.9.18-1.55.18s-1.16-.06-1.55-.18c-.38-.12-.66-.34-.82-.65-.16-.31-.26-.59-.29-.82-.03-.23-.05-.59-.05-1.08v-5.73l-6.55-9.8c-.47-.69-.7-1.22-.7-1.61,0-.65.44-1.27,1.33-1.87.89-.6,1.53-.9,1.91-.9s.69.08.91.24c.34.22.71.64,1.09,1.24l4.7,7.52,4.7-7.52c.38-.61.72-1.01,1-1.2s.61-.29.99-.29.97.25,1.77.76Z"/>
<g>
<path class="cls-3" d="M81.93,56.63c-.53-.65-.79-1.23-.79-1.74s.43-1.2,1.3-2.05c.51-.49,1.04-.73,1.61-.73s1.36.51,2.37,1.52c.28.34.69.67,1.21.99.53.31,1.01.47,1.46.47,1.88,0,2.82-.77,2.82-2.31,0-.46-.26-.85-.77-1.17-.52-.31-1.16-.54-1.93-.68-.77-.14-1.6-.37-2.49-.68-.89-.31-1.72-.68-2.49-1.11-.77-.42-1.41-1.1-1.93-2.02-.52-.92-.77-2.03-.77-3.32,0-1.78.66-3.33,1.99-4.66s3.13-1.99,5.42-1.99c1.21,0,2.32.16,3.32.47,1,.31,1.69.63,2.08.96l.76.58c.63.59.94,1.08.94,1.49s-.24.96-.73,1.67c-.69,1.01-1.4,1.52-2.12,1.52-.42,0-.95-.2-1.58-.61-.06-.04-.18-.14-.35-.3-.17-.16-.33-.29-.47-.39-.42-.26-.97-.39-1.62-.39s-1.2.16-1.64.47c-.43.31-.65.75-.65,1.3s.26,1.01.77,1.35c.52.34,1.16.58,1.93.7.77.12,1.61.31,2.52.56.91.25,1.75.56,2.52.93.77.36,1.41,1,1.93,1.9.52.9.77,2.01.77,3.32s-.26,2.47-.79,3.47c-.53,1-1.21,1.77-2.06,2.32-1.64,1.07-3.39,1.61-5.25,1.61-.95,0-1.85-.12-2.7-.35-.85-.23-1.54-.52-2.06-.86-1.07-.65-1.82-1.27-2.24-1.88l-.27-.33Z"/>
<path class="cls-3" d="M100.74,37.49h16.87c.65,0,1.12.08,1.43.23.3.15.51.39.61.71.1.32.15.75.15,1.27s-.05.95-.15,1.26c-.1.31-.27.53-.52.65-.36.18-.88.27-1.55.27h-5.79v15.26c0,.47-.02.81-.05,1.03s-.12.48-.27.77c-.15.29-.42.5-.8.62-.38.12-.89.18-1.52.18s-1.13-.06-1.5-.18c-.37-.12-.64-.33-.79-.62-.15-.29-.24-.56-.27-.79-.03-.23-.05-.58-.05-1.05v-15.23h-5.82c-.65,0-1.12-.08-1.43-.23-.3-.15-.51-.39-.61-.71-.1-.32-.15-.75-.15-1.27s.05-.95.15-1.26c.1-.31.27-.53.52-.65.36-.18.88-.27,1.55-.27Z"/>
<path class="cls-3" d="M135.99,38.34c.2-.32.5-.55.88-.67.38-.12.86-.18,1.44-.18s1.04.05,1.38.15c.34.1.61.22.79.36.18.14.31.35.39.64.12.34.18.87.18,1.58v9.16c0,2.67-.83,5.1-2.49,7.28-.81,1.03-1.85,1.87-3.12,2.5s-2.68.96-4.23.96-2.95-.32-4.22-.97c-1.26-.65-2.29-1.5-3.08-2.55-1.64-2.14-2.46-4.57-2.46-7.28v-9.13c0-.49.02-.84.05-1.08.03-.23.13-.5.29-.8.16-.3.43-.52.82-.64.38-.12.9-.18,1.55-.18s1.16.06,1.55.18c.38.12.65.33.79.64.24.47.36,1.1.36,1.91v9.1c0,1.23.3,2.41.91,3.52.3.57.76,1.02,1.37,1.36.61.34,1.32.52,2.15.52,1.48,0,2.58-.55,3.31-1.64.73-1.09,1.09-2.36,1.09-3.79v-9.28c0-.79.1-1.34.3-1.67Z"/>
<path class="cls-3" d="M146.18,37.49l5.61.03c2.93,0,5.51,1.06,7.74,3.17,2.22,2.11,3.34,4.71,3.34,7.8s-1.09,5.73-3.26,7.93c-2.17,2.2-4.81,3.31-7.9,3.31h-5.55c-1.23,0-2-.25-2.31-.76-.24-.42-.36-1.07-.36-1.94v-16.87c0-.49.02-.84.05-1.06s.13-.49.29-.79c.28-.55,1.07-.82,2.37-.82ZM151.79,54.35c1.46,0,2.77-.54,3.94-1.62,1.17-1.08,1.76-2.44,1.76-4.08s-.57-3.01-1.71-4.11c-1.14-1.1-2.48-1.65-4.02-1.65h-2.91v11.47h2.94Z"/>
<path class="cls-3" d="M164.84,40.19c0-.46.02-.81.05-1.05.03-.23.13-.5.29-.8.28-.55,1.07-.82,2.37-.82,1.42,0,2.25.37,2.52,1.12.1.34.15.87.15,1.58v16.87c0,.49-.02.84-.05,1.06s-.13.49-.29.79c-.28.55-1.07.82-2.37.82-1.42,0-2.25-.38-2.49-1.15-.12-.32-.18-.84-.18-1.55v-16.87Z"/>
<path class="cls-3" d="M183.07,37.24c2.99,0,5.59,1.08,7.8,3.25,2.2,2.16,3.31,4.85,3.31,8.05s-1.05,5.94-3.16,8.19c-2.1,2.26-4.69,3.38-7.77,3.38s-5.69-1.11-7.84-3.34c-2.15-2.22-3.23-4.87-3.23-7.95,0-1.68.3-3.25.91-4.72.61-1.47,1.42-2.7,2.43-3.69,1.01-.99,2.17-1.77,3.49-2.34,1.31-.57,2.67-.85,4.07-.85ZM177.55,48.68c0,1.8.58,3.26,1.74,4.38,1.16,1.12,2.46,1.68,3.9,1.68s2.73-.55,3.88-1.64c1.15-1.09,1.73-2.56,1.73-4.4s-.58-3.32-1.74-4.43c-1.16-1.11-2.46-1.67-3.9-1.67s-2.73.56-3.88,1.68c-1.15,1.12-1.73,2.58-1.73,4.38Z"/>
</g>
<g>
<path class="cls-3" d="M176.92,11.06c-.03-.23-.13-.5-.29-.8-.28-.55-1.07-.82-2.37-.82h-6.55c-1.78,0-3.51.65-5.19,1.94-.81.63-1.48,1.48-2,2.55-.53,1.07-.79,2.27-.79,3.58,0,2.29.76,4.17,2.28,5.64-.44,1.07-1.13,2.66-2.06,4.76-.3.73-.45,1.25-.45,1.58,0,.77.63,1.42,1.88,1.94.65.28,1.17.43,1.56.43s.72-.1.97-.29c.25-.19.44-.39.56-.59.2-.38.99-2.21,2.37-5.49l.94.06h3.82v3.43c0,.47.02.81.05,1.05.03.23.13.5.29.8.28.55,1.07.82,2.37.82,1.42,0,2.25-.37,2.49-1.12.12-.34.18-.87.18-1.58V12.11c0-.46-.02-.81-.05-1.05ZM172.81,19.44c-.09.14-.48.77-1.24.91-.2.04-.37.03-.48.02-.02.14-.04.26-.06.38-.16.83-.38,1.05-.57,1.07-.29.05-.51-.35-.93-.9-.23.01-.46.02-.69.02-.51,0-1.01-.03-1.49-.09-.25-.03-.5-.07-.74-.11-1.18-.32-2.03-1.27-2.03-2.4v-1.37c0-1.13.86-2.08,2.03-2.4.24-.04.49-.08.74-.11.48-.06.98-.09,1.49-.09s1.01.03,1.49.09c.25.03.5.07.74.11.6.16,1.12.49,1.49.93.34.41.55.92.55,1.47v1.37c0,.23-.01.66-.29,1.1Z"/>
<circle class="cls-2" cx="167.24" cy="17.67" r=".49"/>
<circle class="cls-2" cx="168.88" cy="17.71" r=".49"/>
<circle class="cls-2" cx="170.59" cy="17.71" r=".49"/>
</g>
<g>
<path class="cls-3" d="M141.01,8.24c.03-.23.13-.5.29-.8.28-.55,1.07-.82,2.37-.82h6.55c1.78,0,3.51.65,5.19,1.94.81.63,1.48,1.48,2,2.55.53,1.07.79,2.27.79,3.58,0,2.29-.76,4.17-2.28,5.64.44,1.07,1.13,2.66,2.06,4.76.3.73.45,1.25.45,1.58,0,.77-.63,1.42-1.88,1.94-.65.28-1.17.43-1.56.43s-.72-.1-.97-.29c-.25-.19-.44-.39-.56-.59-.2-.38-.99-2.21-2.37-5.49l-.94.06h-3.82v3.43c0,.47-.02.81-.05,1.05-.03.23-.13.5-.29.8-.28.55-1.07.82-2.37.82-1.42,0-2.25-.37-2.49-1.12-.12-.34-.18-.87-.18-1.58V9.28c0-.46.02-.81.05-1.05ZM145.12,16.62c.09.14.48.77,1.24.91.2.04.37.03.48.02.02.14.04.26.06.38.16.83.38,1.05.57,1.07.29.05.51-.35.93-.9.23.01.46.02.69.02.51,0,1.01-.03,1.49-.09.25-.03.5-.07.74-.11,1.18-.32,2.03-1.27,2.03-2.4v-1.37c0-1.13-.86-2.08-2.03-2.4-.24-.04-.49-.08-.74-.11-.48-.06-.98-.09-1.49-.09s-1.01.03-1.49.09c-.25.03-.5.07-.74.11-.6.16-1.12.49-1.49.93-.34.41-.55.92-.55,1.47v1.37c0,.23.01.66.29,1.1Z"/>
<circle class="cls-2" cx="150.69" cy="14.84" r=".49"/>
<circle class="cls-2" cx="149.05" cy="14.89" r=".49"/>
<circle class="cls-2" cx="147.35" cy="14.89" r=".49"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_2" data-name="图层_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 84.39 115.44">
<defs>
<style>
.cls-1 {
fill: #ea5e5d;
}
.cls-2 {
fill: #23af69;
}
.cls-3 {
fill: #ea5756;
}
</style>
</defs>
<g id="_图层_1-2" data-name="图层_1">
<g>
<g>
<g>
<path class="cls-1" d="M25.31,51.21c-4.45,0-8.64-1.78-11.81-5.01-3.17-3.23-4.91-7.51-4.91-12.04s1.74-8.81,4.91-12.04,7.36-5.01,11.81-5.01,8.71,1.82,11.82,4.99c2.32,2.36,2.32,6.2,0,8.56-2.32,2.36-6.08,2.36-8.4,0-.9-.92-2.15-1.45-3.43-1.45-2.63,0-4.85,2.26-4.85,4.94s2.22,4.94,4.85,4.94c1.28,0,2.52-.53,3.43-1.45,2.32-2.36,6.08-2.36,8.4,0,2.32,2.36,2.32,6.2,0,8.56-3.11,3.17-7.42,4.99-11.82,4.99Z"/>
<path class="cls-1" d="M40.64,66.73c-4.45,0-8.64-1.78-11.81-5.01s-4.91-7.51-4.91-12.04,1.79-8.88,4.9-12.06c2.32-2.36,6.08-2.36,8.4,0,2.32,2.36,2.32,6.2,0,8.56-.9.92-1.42,2.19-1.42,3.49,0,2.68,2.22,4.94,4.85,4.94s4.85-2.26,4.85-4.94c0-.95-.23-2.31-1.32-3.43-3.13-3.19-4.92-7.6-4.92-12.09s1.74-8.81,4.91-12.04c3.17-3.23,7.36-5.01,11.81-5.01s8.64,1.78,11.81,5.01,4.91,7.51,4.91,12.04-1.79,8.88-4.9,12.06c-2.32,2.36-6.08,2.36-8.4,0-2.32-2.36-2.32-6.2,0-8.56.9-.92,1.42-2.19,1.42-3.49,0-2.68-2.22-4.94-4.85-4.94s-4.85,2.26-4.85,4.94c0,1.31.53,2.6,1.45,3.53,3.1,3.16,4.8,7.42,4.8,11.99s-1.74,8.81-4.91,12.04c-3.17,3.23-7.36,5.01-11.81,5.01Z"/>
</g>
<path class="cls-2" d="M40.64,19.09l-9.72-9.12c-1.5-1.4-1.57-3.75-.17-5.25,1.4-1.49,3.75-1.57,5.25-.17l3.89,3.65,5.53-6.83c1.29-1.59,3.63-1.84,5.22-.55,1.59,1.29,1.84,3.63.55,5.22l-10.56,13.05Z"/>
</g>
<g>
<path class="cls-3" d="M10.19,90.22l.39-.28c.49-.29.83-.43,1.03-.43.45,0,.93.4,1.44,1.21.32.5.47.9.47,1.21s-.1.55-.29.75c-.19.2-.42.38-.68.54-.26.16-.51.31-.74.45-.24.14-.72.33-1.45.56-.73.23-1.44.34-2.12.34s-1.37-.09-2.07-.27c-.7-.18-1.41-.48-2.15-.9-.74-.42-1.4-.94-1.99-1.55-.59-.61-1.07-1.39-1.45-2.35-.38-.96-.57-1.99-.57-3.11s.19-2.14.56-3.05c.37-.91.85-1.67,1.43-2.26.58-.6,1.25-1.09,1.99-1.5,1.41-.78,2.82-1.16,4.24-1.16.67,0,1.36.1,2.06.31.7.21,1.22.42,1.58.64l.52.3c.26.16.46.29.6.39.37.3.56.64.56,1.02s-.15.78-.45,1.2c-.56.78-1.06,1.16-1.51,1.16-.26,0-.62-.16-1.1-.47-.6-.49-1.41-.73-2.41-.73-.93,0-1.85.32-2.76.97-.43.32-.79.76-1.08,1.34-.29.57-.43,1.22-.43,1.95s.14,1.37.43,1.95c.29.57.65,1.03,1.1,1.36.88.63,1.79.95,2.74.95.45,0,.87-.06,1.26-.17.39-.11.68-.23.85-.34Z"/>
<path class="cls-3" d="M24.7,79.2c.11-.22.31-.37.58-.45.27-.09.62-.13,1.03-.13s.75.04.99.11c.24.07.43.16.56.26.13.1.23.24.3.43.07.24.11.62.11,1.12v11.95c0,.33-.01.58-.03.74-.02.17-.09.36-.2.57-.2.39-.76.58-1.68.58-1.01,0-1.59-.27-1.77-.8-.09-.24-.13-.62-.13-1.12v-4.37h-5.71v4.39c0,.33-.01.58-.03.74-.02.17-.09.36-.2.57-.2.39-.76.58-1.68.58-1.01,0-1.59-.27-1.77-.8-.09-.24-.13-.62-.13-1.12v-11.95c0-.33.01-.58.03-.74.02-.17.09-.36.2-.57.2-.39.76-.58,1.68-.58,1.01,0,1.6.27,1.79.8.07.24.11.62.11,1.12v4.39h5.71v-4.42c0-.33.01-.58.03-.74.02-.17.09-.36.2-.57Z"/>
<path class="cls-3" d="M33.82,90.58h6.63c.33,0,.58.01.74.03.17.02.36.09.57.2.39.2.58.76.58,1.68,0,1.01-.27,1.59-.8,1.77-.24.09-.62.13-1.12.13h-8.53c-1.01,0-1.59-.27-1.77-.82-.09-.23-.13-.6-.13-1.1v-11.98c0-.73.14-1.23.41-1.5.27-.27.79-.4,1.55-.4h8.49c.33,0,.58.01.74.03.17.02.36.09.57.2.39.2.58.76.58,1.68,0,1.01-.27,1.59-.8,1.77-.24.09-.62.13-1.12.13h-6.61v2.18h4.26c.33,0,.58.01.74.03.17.02.36.09.57.2.39.2.58.76.58,1.68,0,1.01-.27,1.59-.82,1.77-.24.09-.62.13-1.12.13h-4.22v2.18Z"/>
<path class="cls-3" d="M83.34,79c.7.49,1.06.96,1.06,1.42,0,.27-.17.65-.5,1.14l-4.65,6.96v4.11c0,.33-.01.58-.03.74-.02.17-.09.36-.2.57-.11.22-.31.37-.58.45-.27.09-.64.13-1.1.13s-.83-.04-1.1-.13c-.27-.09-.47-.24-.58-.46-.11-.22-.18-.42-.2-.58-.02-.17-.03-.42-.03-.76v-4.07l-4.65-6.96c-.33-.49-.5-.87-.5-1.14,0-.46.32-.9.95-1.32.63-.42,1.08-.64,1.36-.64s.49.06.65.17c.24.16.5.45.78.88l3.34,5.34,3.34-5.34c.27-.43.51-.71.71-.85s.43-.2.7-.2.69.18,1.26.54Z"/>
<g>
<path class="cls-3" d="M1.66,112.96c-.37-.46-.56-.87-.56-1.24s.31-.85.93-1.45c.36-.34.74-.52,1.14-.52s.96.36,1.68,1.08c.2.24.49.48.86.7.37.22.72.33,1.03.33,1.34,0,2-.55,2-1.64,0-.33-.18-.61-.55-.83-.37-.22-.82-.38-1.37-.48-.55-.1-1.13-.26-1.77-.48-.63-.22-1.22-.48-1.77-.79-.55-.3-1-.78-1.37-1.43-.37-.65-.55-1.44-.55-2.36,0-1.26.47-2.37,1.41-3.31s2.22-1.41,3.84-1.41c.86,0,1.65.11,2.36.33.71.22,1.2.45,1.48.68l.54.41c.45.42.67.77.67,1.06s-.17.68-.52,1.18c-.49.72-.99,1.08-1.51,1.08-.3,0-.67-.14-1.12-.43-.04-.03-.13-.1-.25-.22-.12-.11-.23-.21-.33-.28-.3-.19-.69-.28-1.15-.28s-.85.11-1.16.33c-.31.22-.46.53-.46.93s.18.71.55.96c.37.24.82.41,1.37.5.55.09,1.14.22,1.79.4.65.18,1.24.4,1.79.66.55.26,1,.71,1.37,1.35.37.64.55,1.42.55,2.36s-.19,1.76-.56,2.47c-.37.71-.86,1.26-1.46,1.65-1.16.76-2.4,1.14-3.73,1.14-.68,0-1.31-.08-1.92-.25-.6-.17-1.09-.37-1.46-.61-.76-.46-1.29-.9-1.59-1.34l-.19-.24Z"/>
<path class="cls-3" d="M15.02,99.37h11.98c.46,0,.8.05,1.01.16.22.11.36.28.43.51.07.23.11.53.11.9s-.04.67-.11.89c-.07.22-.19.38-.37.46-.26.13-.62.19-1.1.19h-4.11v10.83c0,.33-.01.57-.03.73s-.09.34-.19.55c-.11.21-.3.36-.57.44-.27.09-.63.13-1.08.13s-.8-.04-1.07-.13c-.27-.09-.45-.23-.56-.44-.11-.21-.17-.4-.19-.56-.02-.17-.03-.41-.03-.74v-10.81h-4.14c-.46,0-.8-.05-1.01-.16-.22-.11-.36-.28-.43-.51-.07-.23-.11-.53-.11-.9s.04-.67.11-.89c.07-.22.19-.38.37-.46.26-.13.62-.19,1.1-.19Z"/>
<path class="cls-3" d="M40.05,99.98c.14-.23.35-.39.62-.47.27-.09.61-.13,1.02-.13s.74.04.98.11c.24.07.43.16.56.26.13.1.22.25.28.45.09.24.13.62.13,1.12v6.5c0,1.9-.59,3.62-1.77,5.17-.57.73-1.31,1.32-2.22,1.78s-1.91.68-3,.68-2.1-.23-2.99-.69c-.9-.46-1.63-1.06-2.19-1.81-1.16-1.52-1.74-3.25-1.74-5.17v-6.48c0-.34.01-.6.03-.76.02-.17.09-.36.2-.57.11-.22.31-.37.58-.45.27-.09.64-.13,1.1-.13s.83.04,1.1.13c.27.09.46.24.56.45.17.33.26.78.26,1.36v6.46c0,.88.22,1.71.65,2.5.22.4.54.72.97.97.43.24.94.37,1.53.37,1.05,0,1.83-.39,2.35-1.16.52-.78.78-1.67.78-2.69v-6.59c0-.56.07-.95.22-1.18Z"/>
<path class="cls-3" d="M47.28,99.37l3.98.02c2.08,0,3.91.75,5.49,2.25,1.58,1.5,2.37,3.35,2.37,5.54s-.77,4.07-2.32,5.63c-1.54,1.57-3.41,2.35-5.61,2.35h-3.94c-.88,0-1.42-.18-1.64-.54-.17-.3-.26-.76-.26-1.38v-11.98c0-.34.01-.6.03-.75s.09-.34.2-.56c.2-.39.76-.58,1.68-.58ZM51.27,111.35c1.03,0,1.97-.38,2.8-1.15.83-.77,1.25-1.73,1.25-2.9s-.41-2.14-1.22-2.92c-.81-.78-1.76-1.17-2.85-1.17h-2.07v8.14h2.09Z"/>
<path class="cls-3" d="M60.53,101.29c0-.33.01-.58.03-.74.02-.17.09-.36.2-.57.2-.39.76-.58,1.68-.58,1.01,0,1.6.27,1.79.8.07.24.11.62.11,1.12v11.98c0,.34-.01.6-.03.75s-.09.34-.2.56c-.2.39-.76.58-1.68.58-1.01,0-1.59-.27-1.77-.82-.09-.23-.13-.6-.13-1.1v-11.98Z"/>
<path class="cls-3" d="M73.47,99.2c2.13,0,3.97.77,5.54,2.3,1.57,1.54,2.35,3.44,2.35,5.72s-.75,4.21-2.24,5.82c-1.49,1.6-3.33,2.4-5.51,2.4s-4.04-.79-5.57-2.37c-1.53-1.58-2.29-3.46-2.29-5.64,0-1.19.22-2.31.65-3.35.43-1.04,1.01-1.91,1.72-2.62.72-.7,1.54-1.26,2.48-1.66.93-.4,1.9-.6,2.89-.6ZM69.55,107.32c0,1.28.41,2.32,1.24,3.11.83.8,1.75,1.2,2.77,1.2s1.94-.39,2.76-1.16c.82-.78,1.23-1.82,1.23-3.12s-.41-2.35-1.24-3.14c-.83-.79-1.75-1.18-2.77-1.18s-1.94.4-2.76,1.2c-.82.8-1.23,1.83-1.23,3.11Z"/>
</g>
<g>
<path class="cls-3" d="M69.11,80.61c-.02-.17-.09-.36-.2-.57-.2-.39-.76-.58-1.68-.58h-4.65c-1.26,0-2.49.46-3.68,1.38-.57.45-1.05,1.05-1.42,1.81-.37.76-.56,1.61-.56,2.54,0,1.62.54,2.96,1.62,4.01-.32.76-.8,1.89-1.46,3.38-.22.52-.32.89-.32,1.12,0,.55.45,1.01,1.34,1.38.46.2.83.3,1.11.3s.51-.07.69-.2c.18-.14.31-.28.4-.42.14-.27.7-1.57,1.68-3.9l.67.04h2.71v2.43c0,.33.01.58.03.74.02.17.09.36.2.57.2.39.76.58,1.68.58,1.01,0,1.59-.27,1.77-.8.09-.24.13-.62.13-1.12v-11.95c0-.33-.01-.58-.03-.74ZM66.19,86.56c-.06.1-.34.54-.88.65-.14.03-.26.02-.34.02-.01.1-.03.19-.04.27-.11.59-.27.74-.4.76-.21.03-.36-.25-.66-.64-.16,0-.32.01-.49.01-.36,0-.72-.02-1.06-.06-.18-.02-.35-.05-.52-.08-.84-.22-1.44-.9-1.44-1.7v-.97c0-.8.61-1.48,1.44-1.7.17-.03.34-.06.52-.08.34-.04.69-.06,1.06-.06s.72.02,1.06.06c.18.02.35.05.52.08.43.12.8.35,1.06.66.24.29.39.65.39,1.05v.97c0,.16,0,.47-.21.78Z"/>
<circle class="cls-2" cx="62.23" cy="85.3" r=".35"/>
<circle class="cls-2" cx="63.4" cy="85.33" r=".35"/>
<circle class="cls-2" cx="64.61" cy="85.33" r=".35"/>
</g>
<g>
<path class="cls-3" d="M43.62,78.61c.02-.17.09-.36.2-.57.2-.39.76-.58,1.68-.58h4.65c1.26,0,2.49.46,3.68,1.38.57.45,1.05,1.05,1.42,1.81.37.76.56,1.61.56,2.54,0,1.62-.54,2.96-1.62,4.01.32.76.8,1.89,1.46,3.38.22.52.32.89.32,1.12,0,.55-.45,1.01-1.34,1.38-.46.2-.83.3-1.11.3s-.51-.07-.69-.2c-.18-.14-.31-.28-.4-.42-.14-.27-.7-1.57-1.68-3.9l-.67.04h-2.71v2.43c0,.33-.01.58-.03.74-.02.17-.09.36-.2.57-.2.39-.76.58-1.68.58-1.01,0-1.59-.27-1.77-.8-.09-.24-.13-.62-.13-1.12v-11.95c0-.33.01-.58.03-.74ZM46.53,84.56c.06.1.34.54.88.65.14.03.26.02.34.02.01.1.03.19.04.27.11.59.27.74.4.76.21.03.36-.25.66-.64.16,0,.32.01.49.01.36,0,.72-.02,1.06-.06.18-.02.35-.05.52-.08.84-.22,1.44-.9,1.44-1.7v-.97c0-.8-.61-1.48-1.44-1.7-.17-.03-.34-.06-.52-.08-.34-.04-.69-.06-1.06-.06s-.72.02-1.06.06c-.18.02-.35.05-.52.08-.43.12-.8.35-1.06.66-.24.29-.39.65-.39,1.05v.97c0,.16,0,.47.21.78Z"/>
<circle class="cls-2" cx="50.49" cy="83.3" r=".35"/>
<circle class="cls-2" cx="49.32" cy="83.33" r=".35"/>
<circle class="cls-2" cx="48.11" cy="83.33" r=".35"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

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