Compare commits

...

424 Commits

Author SHA1 Message Date
kangfenmao
11a446e106 chore(version): 0.8.13 2024-11-12 19:27:46 +08:00
kangfenmao
d2ca6f1041 feat: add new button to inputbar component and remove unnecessary toolbar button 2024-11-12 19:27:46 +08:00
kangfenmao
0f3dc87d08 fix: improved popup item filtering logic 2024-11-12 19:27:46 +08:00
kangfenmao
7d3cae1f5b feat: add adaptive tray icon handling 2024-11-12 17:52:00 +08:00
kangfenmao
ceae1fa3d0 feat: remove tray behavior on app close, add platform detection and file extensions 2024-11-12 16:36:47 +08:00
kangfenmao
12a2c8c86d feat: added api connection validation and provider configuration 2024-11-12 13:56:03 +08:00
kangfenmao
29d6c4be18 feat: update system_models to use gpt-4o-latest model 2024-11-12 13:28:40 +08:00
kangfenmao
738e51c078 style: adjusted styling and layout for mac os and file list view 2024-11-12 12:09:34 +08:00
kangfenmao
db050c002a feat: improved pin model functionality and translations 2024-11-12 11:54:11 +08:00
kangfenmao
398f995cd1 feat: handle sorted and filtered agent groups in agentspage 2024-11-12 11:26:09 +08:00
kangfenmao
348fc365fa feat: update system tray icon functionality and ui 2024-11-12 11:16:55 +08:00
kangfenmao
7b6d38e349 feat: add provider deletion functionality and related translations 2024-11-12 10:14:01 +08:00
kangfenmao
6a35c0e3d8 fix: fixed tiling window manager and password input handling issues 2024-11-12 10:00:58 +08:00
kangfenmao
9a63169a73 fix: 视觉模型识别错误 #329 2024-11-12 09:23:24 +08:00
kangfenmao
a9aa5a8da0 feat: remove model settings from settings tab 2024-11-12 00:50:13 +08:00
kangfenmao
a2d568175b feat: update translations and display settings 2024-11-12 00:37:23 +08:00
kangfenmao
0b9717780d feat: improved tray functionality and ui theme settings 2024-11-12 00:25:57 +08:00
kangfenmao
b371fed814 style: improved mermaid popup styling and positioning 2024-11-11 23:49:44 +08:00
injurka
3311f8cdef feat: added settings for minimize to tray instead of closing 2024-11-11 23:49:18 +08:00
kangfenmao
422baa848b fix: fix select box issue with mermaid diagrams 2024-11-11 23:33:57 +08:00
kangfenmao
739aa21475 style: improved chat window styling and accessibility 2024-11-11 23:26:45 +08:00
kangfenmao
23ef4ab952 fix: adjusted tray icon size to match linux platform requirements 2024-11-11 18:03:03 +08:00
injurka
b77f845cb0 fix: the context menu is not displayed on linux 2024-11-11 17:58:20 +08:00
kangfenmao
0573b274ed feat: add loading indicator and disable check button while checking api keys 2024-11-11 17:57:48 +08:00
kangfenmao
60433bb1ab style: update bubble ui component styles 2024-11-11 17:41:53 +08:00
kangfenmao
1caf53fbda fix: message color 2024-11-11 13:40:25 +08:00
kangfenmao
446e011c6a chore(version): 0.8.12 2024-11-11 11:18:43 +08:00
kangfenmao
c319b54a26 feat: rename images to paintings for consistency 2024-11-11 10:45:42 +08:00
kangfenmao
e9ca1d54a0 feat: add check all keys popup 2024-11-11 10:41:43 +08:00
kangfenmao
1ff8fe0c2e fix: removed macos window minimize event handler 2024-11-11 09:25:22 +08:00
kangfenmao
902341bc1d chore(version): 0.8.11 2024-11-10 17:20:52 +08:00
kangfenmao
17fff46024 style: update code formatting for markdown styles 2024-11-10 17:16:10 +08:00
kangfenmao
d258c1cfe2 feat: add mermaid preview and download feature 2024-11-10 17:09:18 +08:00
kangfenmao
79cabadfb8 fix: 加入账号轮询功能 #293 2024-11-09 23:18:56 +08:00
kangfenmao
61ceca2363 feat: update sidebar menu with interactive tooltips and painting terminology 2024-11-09 22:26:35 +08:00
kangfenmao
8a8deda002 fix: removed theme dependency in syntaxhighlighterprovider, added mermaid delay 2024-11-09 15:02:38 +08:00
kangfenmao
6536ec227a fix: 气泡默认界面显示异常 #308 2024-11-09 14:54:22 +08:00
kangfenmao
af1fd90118 feat: add mermaid library and theme initialization 2024-11-09 14:51:39 +08:00
kangfenmao
68fa2bad15 fix: mermaid图渲染有问题 #310 2024-11-09 14:16:42 +08:00
kangfenmao
bac3bad8db style: improved syntax highlighting functionality and code loading 2024-11-09 10:05:34 +08:00
kangfenmao
e11633310c feat: add the ability to display the application in tray #297 2024-11-09 08:42:09 +08:00
kangfenmao
612b39a878 fix: code style init value 2024-11-08 11:59:32 +08:00
injurka
8491141edc fix: expand code syntax highlighting options (#307)
* added locale for context-menu

* fix: expand code syntax highlighting options

* fix: type for theme

---------

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

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

---------

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

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

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

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

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

* 新增:若干模型头像

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

* 1

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

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

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

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

* Update models.ts

feat:add matching rules for gpt-4

* Update models.ts

feat:add matching rules for gpt-4

* Update models.ts

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

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

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

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

View File

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

View File

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

7
.gitignore vendored
View File

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

View File

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

View File

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

View File

@@ -0,0 +1,26 @@
diff --git a/core.js b/core.js
index 00b67a48b7b5cf0029413fc84abd0c01630c3d14..5550b58495b468060f775ca86e4d849d82573ea5 100644
--- a/core.js
+++ b/core.js
@@ -156,7 +156,7 @@ class APIClient {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': this.getUserAgent(),
- ...getPlatformHeaders(),
+ // ...getPlatformHeaders(),
...this.authHeaders(opts),
};
}
diff --git a/core.mjs b/core.mjs
index 8bc7a0ee10d61560d7113cf3f703355bb19f7ddd..5e4c8586ea6b13fe887a22af2de05eaa4700b5ec 100644
--- a/core.mjs
+++ b/core.mjs
@@ -149,7 +149,7 @@ export class APIClient {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': this.getUserAgent(),
- ...getPlatformHeaders(),
+ // ...getPlatformHeaders(),
...this.authHeaders(opts),
};
}

29
CODE_OF_CONDUCT.md Normal file
View File

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

45
CONTRIBUTING.md Normal file
View File

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

136
LICENSE
View File

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

109
README.md
View File

@@ -1,30 +1,67 @@
<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
![](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.
👏 Join [Telegram Group](https://t.me/CherryStudioAI)
# 🌠 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/790790d7-b462-48dd-bde1-91c1697a4648)
![](https://github.com/user-attachments/assets/3f3f0bfa-cb88-4abf-923a-a0859fa3c912)
# 🌟 Key Features
![](https://github.com/user-attachments/assets/288560c1-d218-437c-87c2-2a5e87b43b93)
1. **Diverse LLM Provider Support**:
# 🌟 Features
- ☁️ Major LLM Cloud Services: OpenAI, Gemini, Anthropic, and more
- 🔗 AI Web Service Integration: Claude, Peplexity, Poe, and others
- 💻 Local Model Support with Ollama
1. Support for Multiple LLM Providers.
2. Allows creation of multiple Assistants.
3. Enables creation of multiple topics.
4. Allows using multiple models to answer questions in the same conversation.
5. Supports drag-and-drop sorting.
6. Code highlighting.
7. Mermaid chart
2. **AI Assistants & Conversations**:
- 📚 300+ Pre-configured AI Assistants
- 🤖 Custom Assistant Creation
- 💬 Multi-model Simultaneous Conversations
3. **Document & Data Processing**:
- 📄 Support for Text, Images, Office, PDF, and more
- ☁️ WebDAV File Management and Backup
- 📊 Mermaid Chart Visualization
- 💻 Code Syntax Highlighting
4. **Practical Tools Integration**:
- 🔍 Global Search Functionality
- 📝 Topic Management System
- 🔤 AI-powered Translation
- 🎯 Drag-and-drop Sorting
- 🔌 Mini Program Support
5. **Enhanced User Experience**:
- 🖥️ Cross-platform Support for Windows, Mac, and Linux
- 📦 Ready to Use, No Environment Setup Required
- 🎨 Light/Dark Themes and Transparent Window
- 📝 Complete Markdown Rendering
- 🤲 Easy Content Sharing
# 🖥️ 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)
[Cursor](https://www.cursor.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
## Project Setup
@@ -53,10 +90,52 @@ $ yarn build:mac
$ yarn build:linux
```
# ⭐️ Star History
# 🤝 Contributing
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)
We welcome contributions to Cherry Studio! Here are some ways you can contribute:
1. **Contribute Code**: Develop new features or optimize existing code.
2. **Fix Bugs**: Submit fixes for any bugs you find.
3. **Maintain Issues**: Help manage GitHub issues.
4. **Product Design**: Participate in design discussions.
5. **Write Documentation**: Improve user manuals and guides.
6. **Community Engagement**: Join discussions and help users.
7. **Promote Usage**: Spread the word about Cherry Studio.
## Getting Started
1. **Fork the Repository**: Fork and clone it to your local machine.
2. **Create a Branch**: For your changes.
3. **Submit Changes**: Commit and push your changes.
4. **Open a Pull Request**: Describe your changes and reasons.
For more detailed guidelines, please refer to our [Contributing Guide](./CONTRIBUTING.md).
Thank you for your support and contributions!
# 🚀 Contributors
<a href="https://github.com/kangfenmao/cherry-studio/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kangfenmao/cherry-studio" />
</a>
<br /><br />
# 🌐 Community
[Telegram](https://t.me/CherryStudioAI) | [Email](mailto:kangfenmao@gmail.com) | [Twitter](https://x.com/kangfenmao)
# 📣 Product Hunt
<a href="https://www.producthunt.com/posts/cherry-studio?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-cherry&#0045;studio" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=496640&theme=light" alt="Cherry&#0032;Studio - AI&#0032;Chatbots&#0044;&#0032;AI&#0032;Desktop&#0032;Client | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
# ☕ Sponsor
[Buy Me a Coffee](docs/sponsor.md)
# 📃 License
[LICENSE](./LICENSE)
# ⭐️ Star History
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 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

BIN
build/tray_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
build/tray_icon_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
build/tray_icon_light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

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

@@ -0,0 +1,140 @@
<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)
# 🌟 主な機能
1. **多様な LLM サービス対応**
- ☁️ 主要な LLM クラウドサービス対応OpenAI、Gemini、Anthropic など
- 🔗 AI Web サービス統合Claude、Peplexity、Poe など
- 💻 Ollama によるローカルモデル実行対応
2. **AI アシスタントと対話**
- 📚 300+ の事前設定済み AI アシスタント
- 🤖 カスタム AI アシスタントの作成
- 💬 複数モデルでの同時対話機能
3. **文書とデータ処理**
- 📄 テキスト、画像、Office、PDF など多様な形式対応
- ☁️ WebDAV によるファイル管理とバックアップ
- 📊 Mermaid による図表作成
- 💻 コードハイライト機能
4. **実用的なツール統合**
- 🔍 グローバル検索機能
- 📝 トピック管理システム
- 🔤 AI による翻訳機能
- 🎯 ドラッグ&ドロップによる整理
- 🔌 ミニプログラム対応
5. **優れたユーザー体験**
- 🖥️ Windows、Mac、Linux のクロスプラットフォーム対応
- 📦 環境構築不要ですぐに使用可能
- 🎨 ライト/ダークテーマと透明ウィンドウ対応
- 📝 完全な Markdown レンダリング
- 🤲 簡単な共有機能
# 🖥️ 開発
## IDEの設定
[Cursor](https://www.cursor.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
## プロジェクトの設定
### インストール
```bash
$ yarn
```
### 開発
```bash
$ yarn dev
```
### ビルド
```bash
# Windowsの場合
$ yarn build:win
# macOSの場合
$ yarn build:mac
# Linuxの場合
$ yarn build:linux
```
# 🤝 貢献
Cherry Studioへの貢献を歓迎します以下の方法で貢献できます
1. **コードの貢献**:新機能を開発するか、既存のコードを最適化します。
2. **バグの修正**:見つけたバグを修正します。
3. **問題の管理**GitHubの問題を管理するのを手伝います。
4. **製品デザイン**:デザインの議論に参加します。
5. **ドキュメントの作成**:ユーザーマニュアルやガイドを改善します。
6. **コミュニティの参加**:ディスカッションに参加し、ユーザーを支援します。
7. **使用の促進**Cherry Studioを広めます。
## 始め方
1. **リポジトリをフォーク**:フォークしてローカルマシンにクローンします。
2. **ブランチを作成**:変更のためのブランチを作成します。
3. **変更を提出**:変更をコミットしてプッシュします。
4. **プルリクエストを開く**:変更内容と理由を説明します。
詳細なガイドラインについては、[貢献ガイド](./CONTRIBUTING.md)をご覧ください。
ご支援と貢献に感謝します!
# 🚀 コントリビューター
<a href="https://github.com/kangfenmao/cherry-studio/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kangfenmao/cherry-studio" />
</a>
# コミュニティ
[Telegram](https://t.me/CherryStudioAI) | [Email](mailto:kangfenmao@gmail.com) | [Twitter](https://x.com/kangfenmao)
# 📣 プロダクトハント
<a href="https://www.producthunt.com/posts/cherry-studio?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-cherry&#0045;studio" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=496640&theme=light" alt="Cherry&#0032;Studio - AI&#0032;Chatbots&#0044;&#0032;AI&#0032;Desktop&#0032;Client | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
# スポンサー
[Buy Me a Coffee](sponsor.md)
# 📃 ライセンス
[LICENSE](./LICENSE)
# ⭐️ スター履歴
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)

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

@@ -0,0 +1,141 @@
<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)
# 🌠 界面
![](https://github.com/user-attachments/assets/28585d83-4bf0-4714-b561-8c7bf57cc600)
![](https://github.com/user-attachments/assets/8576863a-f632-4776-bc12-657eeced9da3)
![](https://github.com/user-attachments/assets/790790d7-b462-48dd-bde1-91c1697a4648)
# 🌟 主要特性
1. **多样化 LLM 服务支持**
- ☁️ 支持主流 LLM 云服务OpenAI、Gemini、Anthropic、硅基流动等
- 🔗 集成流行 AI Web 服务Claude、Peplexity、Poe、腾讯元宝、知乎直答等
- 💻 支持 Ollama 本地模型部署
2. **智能助手与对话**
- 📚 内置 300+ 预配置 AI 助手
- 🤖 支持自定义创建专属助手
- 💬 多模型同时对话,获得多样化观点
3. **文档与数据处理**
- 📄 支持文本、图片、Office、PDF 等多种格式
- ☁️ WebDAV 文件管理与数据备份
- 📊 Mermaid 图表可视化
- 💻 代码高亮显示
4. **实用工具集成**
- 🔍 全局搜索功能
- 📝 话题管理系统
- 🔤 AI 驱动的翻译功能
- 🎯 拖拽排序
- 🔌 小程序支持
5. **优质使用体验**
- 🖥️ Windows、Mac、Linux 跨平台支持
- 📦 开箱即用,无需配置环境
- 🎨 支持明暗主题与透明窗口
- 📝 完整的 Markdown 渲染
- 🤲 便捷的内容分享功能
# 🖥️ 开发
## IDE 设置
[Cursor](https://www.cursor.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
## 项目设置
### 安装
```bash
$ yarn
```
### 开发
```bash
$ yarn dev
```
### 构建
```bash
# Windows
$ yarn build:win
# macOS
$ yarn build:mac
# Linux
$ yarn build:linux
```
# 🤝 贡献
我们欢迎对 Cherry Studio 的贡献!您可以通过以下方式贡献:
1. **贡献代码**:开发新功能或优化现有代码。
2. **修复错误**:提交您发现的错误修复。
3. **维护问题**:帮助管理 GitHub 问题。
4. **产品设计**:参与设计讨论。
5. **撰写文档**:改进用户手册和指南。
6. **社区参与**:加入讨论并帮助用户。
7. **推广使用**:宣传 Cherry Studio。
## 入门
1. **Fork 仓库**Fork 并克隆到您的本地机器。
2. **创建分支**:为您的更改创建分支。
3. **提交更改**:提交并推送您的更改。
4. **打开 Pull Request**:描述您的更改和原因。
有关更详细的指南,请参阅我们的 [贡献指南](./CONTRIBUTING.md)。
感谢您的支持和贡献!
# 🚀 贡献者
<a href="https://github.com/kangfenmao/cherry-studio/graphs/contributors">
<img src="https://contrib.rocks/image?repo=kangfenmao/cherry-studio" />
</a>
<br /><br />
# 🌐 社区
[Telegram](https://t.me/CherryStudioAI) | [Email](mailto:kangfenmao@gmail.com) | [Twitter](https://x.com/kangfenmao)
# 📣 产品猎人
<a href="https://www.producthunt.com/posts/cherry-studio?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-cherry&#0045;studio" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=496640&theme=light" alt="Cherry&#0032;Studio - AI&#0032;Chatbots&#0044;&#0032;AI&#0032;Desktop&#0032;Client | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
# ☕ 赞助
[微信赞赏码](sponsor.md)
# 📃 许可证
[LICENSE](./LICENSE)
# ⭐️ Star 记录
[![Star History Chart](https://api.star-history.com/svg?repos=kangfenmao/cherry-studio&type=Timeline)](https://star-history.com/#kangfenmao/cherry-studio&Timeline)

5
docs/sponsor.md Normal file
View File

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

View File

@@ -8,7 +8,8 @@ files:
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
- '!src/*'
- '!src'
- '!scripts'
- '!local'
asarUnpack:
- resources/**
@@ -19,6 +20,8 @@ nsis:
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
allowToChangeInstallationDirectory: true
oneClick: false
mac:
entitlementsInherit: build/entitlements.mac.plist
extendInfo:
@@ -40,13 +43,16 @@ dmg:
artifactName: ${productName}-${version}-${arch}.${ext}
linux:
target:
- AppImage
- target: AppImage
arch:
- arm64
- x64
# - snap
# - deb
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${productName}-${version}.${ext}
artifactName: ${productName}-${version}-${arch}.${ext}
npmRebuild: false
publish:
provider: github
@@ -57,6 +63,10 @@ electronDownload:
afterSign: scripts/notarize.js
releaseInfo:
releaseNotes: |
智能助理和消息列表合并
优化输入框样式
提升小程序稳定性
增加最小化到托盘功能 @injurka
托盘图标使用彩色图标
修复 Mermaid png 格式图表无法下载问题
选择模型弹窗支持固定一些常用模型
默认助手合并到模型设置里
设置面板移除模型相关设置项
修复一些视觉模型不能识别问题

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "CherryStudio",
"version": "0.6.4",
"version": "0.8.13",
"private": true,
"description": "A powerful AI assistant for producer.",
"main": "./out/main/index.js",
@@ -8,7 +8,11 @@
"homepage": "https://github.com/kangfenmao/cherry-studio",
"workspaces": {
"packages": [
"local"
"local",
"packages/*"
],
"nohoist": [
"packages/database"
]
},
"scripts": {
@@ -26,15 +30,26 @@
"build:mac": "dotenv electron-vite build && electron-builder --mac --publish never",
"build:linux": "dotenv electron-vite build && electron-builder --linux --publish never",
"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": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"archiver": "^7.0.1",
"docx": "^9.0.2",
"electron-log": "^5.1.5",
"electron-store": "^8.2.0",
"electron-updater": "^6.1.7",
"electron-window-state": "^5.0.3"
"electron-updater": "^6.3.9",
"electron-window-state": "^5.0.3",
"fs-extra": "^11.2.0",
"html2canvas": "^1.4.1",
"markdown-it": "^14.1.0",
"officeparser": "^4.1.1",
"unzipper": "^0.12.3",
"webdav": "4.11.4"
},
"devDependencies": {
"@anthropic-ai/sdk": "^0.24.3",
@@ -45,18 +60,26 @@
"@hello-pangea/dnd": "^16.6.0",
"@kangfenmao/keyv-storage": "^0.1.0",
"@reduxjs/toolkit": "^2.2.5",
"@types/fs-extra": "^11",
"@types/lodash": "^4.17.5",
"@types/markdown-it": "^14",
"@types/node": "^18.19.9",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@types/tinycolor2": "^1",
"@types/unzipper": "^0",
"@vitejs/plugin-react": "^4.2.1",
"antd": "^5.18.3",
"axios": "^1.7.3",
"browser-image-compression": "^2.0.2",
"dayjs": "^1.11.11",
"dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7",
"dotenv-cli": "^7.4.2",
"electron": "^28.3.3",
"electron-builder": "^24.9.1",
"electron-devtools-installer": "^3.2.0",
"electron-icon-builder": "^2.0.1",
"electron-vite": "^2.0.0",
"emittery": "^1.0.3",
"emoji-picker-element": "^1.22.1",
@@ -65,11 +88,12 @@
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-unused-imports": "^4.0.0",
"gpt-tokens": "^1.3.6",
"gpt-tokens": "^1.3.10",
"i18next": "^23.11.5",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"openai": "^4.52.1",
"mime": "^4.0.4",
"openai": "patch:openai@npm%3A4.71.1#~/.yarn/patches/openai-npm-4.71.1-b5940d6401.patch",
"prettier": "^3.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -79,15 +103,18 @@
"react-router": "6",
"react-router-dom": "6",
"react-spinners": "^0.14.1",
"react-syntax-highlighter": "^15.5.0",
"redux": "^5.0.1",
"redux-persist": "^6.0.0",
"rehype-katex": "^7.0.0",
"rehype-katex": "^7.0.1",
"rehype-mathjax": "^6.0.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"sass": "^1.77.2",
"shiki": "^1.22.2",
"styled-components": "^6.1.11",
"typescript": "^5.3.3",
"tinycolor2": "^1.6.0",
"typescript": "^5.6.2",
"uuid": "^10.0.0",
"vite": "^5.0.12"
},
@@ -96,8 +123,7 @@
"react-dom": "^17.0.0 || ^18.0.0"
},
"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"
},
"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,23 @@
import Store from 'electron-store'
import { app } from 'electron'
export const appConfig = new Store()
import { getDataPath } from './utils'
const isDev = process.env.NODE_ENV === 'development'
if (isDev) {
app.setPath('userData', app.getPath('userData') + 'Dev')
}
export const DATA_PATH = getDataPath()
export const titleBarOverlayDark = {
height: 41,
height: 40,
color: '#00000000',
symbolColor: '#ffffff'
}
export const titleBarOverlayLight = {
height: 41,
height: 40,
color: '#00000000',
symbolColor: '#000000'
}

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

@@ -0,0 +1,95 @@
export const isMac = process.platform === 'darwin'
export const isWin = process.platform === 'win32'
export const isLinux = process.platform === 'linux'
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/electron.d.ts vendored Normal file
View File

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

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

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

View File

@@ -1,45 +1,69 @@
import { electronApp, optimizer } from '@electron-toolkit/utils'
import { app, BrowserWindow } from 'electron'
import installExtension, { REDUX_DEVTOOLS } from 'electron-devtools-installer'
import { registerIpc } from './ipc'
import { registerZoomShortcut } from './services/ShortcutService'
import { TrayService } from './services/TrayService'
import { windowService } from './services/WindowService'
import { updateUserDataPath } from './utils/upgrade'
import { createMainWindow } from './window'
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
await updateUserDataPath()
// Check for single instance lock
if (!app.requestSingleInstanceLock()) {
app.quit()
process.exit(0)
} else {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
await updateUserDataPath()
// Set app user model id for windows
electronApp.setAppUserModelId('com.kangfenmao.CherryStudio')
// Set app user model id for windows
electronApp.setAppUserModelId(import.meta.env.VITE_MAIN_BUNDLE_ID || 'com.kangfenmao.CherryStudio')
const mainWindow = windowService.createMainWindow()
new TrayService()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
windowService.createMainWindow()
} else {
windowService.showMainWindow()
}
})
registerZoomShortcut(mainWindow)
registerIpc(mainWindow, app)
if (process.env.NODE_ENV === 'development') {
installExtension(REDUX_DEVTOOLS)
.then((name) => console.log(`Added Extension: ${name}`))
.catch((err) => console.log('An error occurred: ', err))
}
})
// Listen for second instance
app.on('second-instance', () => {
const mainWindow = BrowserWindow.getAllWindows()[0]
if (mainWindow) {
mainWindow.isMinimized() && mainWindow.restore()
mainWindow.show()
mainWindow.focus()
}
})
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createMainWindow()
app.on('before-quit', () => {
app.isQuitting = true
})
const mainWindow = createMainWindow()
registerIpc(mainWindow, app)
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
// In this file you can include the rest of your app"s specific main process
// code. You can also put them in separate files and require them here.
// In this file you can include the rest of your app"s specific main process
// code. You can also put them in separate files and require them here.
}

View File

@@ -1,38 +1,90 @@
import path from 'node:path'
import { ThemeMode } from '@types'
import { BrowserWindow, ipcMain, session, shell } from 'electron'
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
import AppUpdater from './updater'
import { openFile, saveFile } from './utils/file'
import { titleBarOverlayDark, titleBarOverlayLight } from './config'
import AppUpdater from './services/AppUpdater'
import BackupManager from './services/BackupManager'
import { configManager } from './services/ConfigManager'
import { ExportService } from './services/ExportService'
import FileStorage from './services/FileStorage'
import { windowService } from './services/WindowService'
import { compress, decompress } from './utils/zip'
import { createMinappWindow } from './window'
const fileManager = new FileStorage()
const backupManager = new BackupManager()
const exportService = new ExportService(fileManager)
export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
const { autoUpdater } = new AppUpdater(mainWindow)
// IPC
ipcMain.handle('get-app-info', () => ({
ipcMain.handle('app:info', () => ({
version: app.getVersion(),
isPackaged: app.isPackaged,
appPath: app.getAppPath()
appPath: app.getAppPath(),
filesPath: path.join(app.getPath('userData'), 'Data', 'Files')
}))
ipcMain.handle('open-website', (_, url: string) => {
shell.openExternal(url)
ipcMain.handle('app:proxy', (_, proxy: string) => session.defaultSession.setProxy(proxy ? { proxyRules: proxy } : {}))
ipcMain.handle('app:reload', () => mainWindow.reload())
ipcMain.handle('open:website', (_, url: string) => shell.openExternal(url))
// language
ipcMain.handle('app:set-language', (_, language) => {
configManager.setLanguage(language)
})
ipcMain.handle('set-proxy', (_, proxy: string) => {
session.defaultSession.setProxy(proxy ? { proxyRules: proxy } : {})
// tray
ipcMain.handle('app:set-tray', (_, isActive: boolean) => {
configManager.setTray(isActive)
})
ipcMain.handle('save-file', saveFile)
ipcMain.handle('open-file', openFile)
ipcMain.handle('reload', () => mainWindow.reload())
// theme
ipcMain.handle('app:set-theme', (_, theme: ThemeMode) => {
configManager.setTheme(theme)
mainWindow?.setTitleBarOverlay &&
mainWindow.setTitleBarOverlay(theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight)
})
// check for update
ipcMain.handle('app:check-for-update', async () => {
return {
currentVersion: autoUpdater.currentVersion,
update: await autoUpdater.checkForUpdates()
}
})
// zip
ipcMain.handle('zip:compress', (_, text: string) => compress(text))
ipcMain.handle('zip:decompress', (_, text: Buffer) => decompress(text))
// backup
ipcMain.handle('backup:backup', backupManager.backup)
ipcMain.handle('backup:restore', backupManager.restore)
ipcMain.handle('backup:backupToWebdav', backupManager.backupToWebdav)
ipcMain.handle('backup:restoreFromWebdav', backupManager.restoreFromWebdav)
// file
ipcMain.handle('file:open', fileManager.open)
ipcMain.handle('file:save', fileManager.save)
ipcMain.handle('file:select', fileManager.selectFile)
ipcMain.handle('file:upload', fileManager.uploadFile)
ipcMain.handle('file:clear', fileManager.clear)
ipcMain.handle('file:read', fileManager.readFile)
ipcMain.handle('file:delete', fileManager.deleteFile)
ipcMain.handle('file:get', fileManager.getFile)
ipcMain.handle('file:selectFolder', fileManager.selectFolder)
ipcMain.handle('file:create', fileManager.createTempFile)
ipcMain.handle('file:write', fileManager.writeFile)
ipcMain.handle('file:saveImage', fileManager.saveImage)
ipcMain.handle('file:base64Image', fileManager.base64Image)
ipcMain.handle('file:download', fileManager.downloadFile)
ipcMain.handle('file:copy', fileManager.copyFile)
// minapp
ipcMain.handle('minapp', (_, args) => {
createMinappWindow({
windowService.createMinappWindow({
url: args.url,
parent: mainWindow,
windowOptions: {
@@ -42,17 +94,6 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
})
})
ipcMain.handle('set-theme', (_, theme: 'light' | 'dark') => {
appConfig.set('theme', theme)
mainWindow?.setTitleBarOverlay &&
mainWindow.setTitleBarOverlay(theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight)
})
// 触发检查更新(此方法用于被渲染线程调用,例如页面点击检查更新按钮来调用此方法)
ipcMain.handle('check-for-update', async () => {
return {
currentVersion: autoUpdater.currentVersion,
update: await autoUpdater.checkForUpdates()
}
})
// export
ipcMain.handle('export:word', exportService.exportToWord)
}

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

View File

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

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,63 @@
import { LanguageVarious, ThemeMode } from '@types'
import { app } from 'electron'
import Store from 'electron-store'
export class ConfigManager {
private store: Store
private subscribers: Map<string, Array<(newValue: any) => void>> = new Map()
constructor() {
this.store = new Store()
}
getLanguage(): LanguageVarious {
return this.store.get('language', app.getLocale()) as LanguageVarious
}
setLanguage(theme: LanguageVarious) {
this.store.set('language', theme)
}
getTheme(): ThemeMode {
return this.store.get('theme', ThemeMode.light) as ThemeMode
}
setTheme(theme: ThemeMode) {
this.store.set('theme', theme)
}
isTray(): boolean {
return !!this.store.get('tray', true)
}
setTray(value: boolean) {
this.store.set('tray', value)
this.notifySubscribers('tray', value)
}
subscribe<T>(key: string, callback: (newValue: T) => void) {
if (!this.subscribers.has(key)) {
this.subscribers.set(key, [])
}
this.subscribers.get(key)!.push(callback)
}
unsubscribe<T>(key: string, callback: (newValue: T) => void) {
const subscribers = this.subscribers.get(key)
if (subscribers) {
this.subscribers.set(
key,
subscribers.filter((subscriber) => subscriber !== callback)
)
}
}
private notifySubscribers<T>(key: string, newValue: T) {
const subscribers = this.subscribers.get(key)
if (subscribers) {
subscribers.forEach((subscriber) => subscriber(newValue))
}
}
}
export const configManager = new ConfigManager()

View File

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

View File

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

View File

@@ -0,0 +1,66 @@
import { BrowserWindow, globalShortcut } from 'electron'
export function registerZoomShortcut(mainWindow: BrowserWindow) {
const registerShortcuts = () => {
// 注册放大快捷键 (Ctrl+Plus 或 Cmd+Plus)
globalShortcut.register('CommandOrControl+=', () => {
if (mainWindow) {
const currentZoom = mainWindow.webContents.getZoomFactor()
const newZoom = currentZoom + 0.1
// Prevent zoom factor from exceeding reasonable limits
if (newZoom <= 5.0) {
mainWindow.webContents.setZoomFactor(newZoom)
}
}
})
// 注册缩小快捷键 (Ctrl+Minus 或 Cmd+Minus)
globalShortcut.register('CommandOrControl+-', () => {
if (mainWindow) {
const currentZoom = mainWindow.webContents.getZoomFactor()
const newZoom = currentZoom - 0.1
// Prevent zoom factor from going below 0.1
if (newZoom >= 0.1) {
mainWindow.webContents.setZoomFactor(newZoom)
}
}
})
// 注册重置缩放快捷键 (Ctrl+0 或 Cmd+0)
globalShortcut.register('CommandOrControl+0', () => {
if (mainWindow) {
mainWindow.webContents.setZoomFactor(1)
}
})
}
const unregisterShortcuts = () => {
globalShortcut.unregister('CommandOrControl+=')
globalShortcut.unregister('CommandOrControl+-')
globalShortcut.unregister('CommandOrControl+0')
}
// Add check for window destruction
if (mainWindow.isDestroyed()) {
return
}
// When window gains focus, register shortcuts
mainWindow.on('focus', () => {
if (!mainWindow.isDestroyed()) {
registerShortcuts()
}
})
// When window loses focus, unregister shortcuts
mainWindow.on('blur', () => {
if (!mainWindow.isDestroyed()) {
unregisterShortcuts()
}
})
// Initial registration (if window is already focused)
if (!mainWindow.isDestroyed() && mainWindow.isFocused()) {
registerShortcuts()
}
}

View File

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

View File

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

View File

@@ -0,0 +1,198 @@
import { is } from '@electron-toolkit/utils'
import { isLinux, isWin } from '@main/constant'
import { app, BrowserWindow, Menu, MenuItem, shell } from 'electron'
import windowStateKeeper from 'electron-window-state'
import { join } from 'path'
import icon from '../../../build/icon.png?asset'
import { titleBarOverlayDark, titleBarOverlayLight } from '../config'
import { locales } from '../utils/locales'
import { configManager } from './ConfigManager'
export class WindowService {
private static instance: WindowService | null = null
private mainWindow: BrowserWindow | null = null
public static getInstance(): WindowService {
if (!WindowService.instance) {
WindowService.instance = new WindowService()
}
return WindowService.instance
}
public createMainWindow(): BrowserWindow {
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
return this.mainWindow
}
const mainWindowState = windowStateKeeper({
defaultWidth: 1080,
defaultHeight: 670
})
const theme = configManager.getTheme()
const isMac = process.platform === 'darwin'
this.mainWindow = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth: 1080,
minHeight: 600,
show: true,
autoHideMenuBar: true,
transparent: isMac,
vibrancy: 'fullscreen-ui',
visualEffectState: 'active',
titleBarStyle: 'hidden',
titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight,
backgroundColor: isMac ? undefined : theme === 'dark' ? '#181818' : '#FFFFFF',
trafficLightPosition: { x: 8, y: 12 },
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
webSecurity: false,
webviewTag: true
}
})
this.setupMainWindow(this.mainWindow, mainWindowState)
return this.mainWindow
}
public createMinappWindow({
url,
parent,
windowOptions
}: {
url: string
parent?: BrowserWindow
windowOptions?: Electron.BrowserWindowConstructorOptions
}): BrowserWindow {
const width = windowOptions?.width || 1000
const height = windowOptions?.height || 680
const minappWindow = new BrowserWindow({
width,
height,
autoHideMenuBar: true,
title: 'Cherry Studio',
...windowOptions,
parent,
webPreferences: {
preload: join(__dirname, '../preload/minapp.js'),
sandbox: false,
contextIsolation: false
}
})
minappWindow.loadURL(url)
return minappWindow
}
private setupMainWindow(mainWindow: BrowserWindow, mainWindowState: any) {
mainWindowState.manage(mainWindow)
this.setupContextMenu(mainWindow)
this.setupWindowEvents(mainWindow)
this.setupWebContentsHandlers(mainWindow)
this.setupWindowLifecycleEvents(mainWindow)
this.loadMainWindowContent(mainWindow)
}
private setupContextMenu(mainWindow: BrowserWindow) {
mainWindow.webContents.on('context-menu', () => {
const locale = locales[configManager.getLanguage()]
const { common } = locale.translation
const menu = new Menu()
menu.append(new MenuItem({ label: common.copy, role: 'copy' }))
menu.append(new MenuItem({ label: common.paste, role: 'paste' }))
menu.append(new MenuItem({ label: common.cut, role: 'cut' }))
menu.popup()
})
}
private setupWindowEvents(mainWindow: BrowserWindow) {
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})
}
private setupWebContentsHandlers(mainWindow: BrowserWindow) {
mainWindow.webContents.on('will-navigate', (event, url) => {
event.preventDefault()
shell.openExternal(url)
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
this.setupWebRequestHeaders(mainWindow)
}
private setupWebRequestHeaders(mainWindow: BrowserWindow) {
mainWindow.webContents.session.webRequest.onHeadersReceived({ urls: ['*://*/*'] }, (details, callback) => {
if (details.responseHeaders?.['X-Frame-Options']) {
delete details.responseHeaders['X-Frame-Options']
}
if (details.responseHeaders?.['x-frame-options']) {
delete details.responseHeaders['x-frame-options']
}
if (details.responseHeaders?.['Content-Security-Policy']) {
delete details.responseHeaders['Content-Security-Policy']
}
if (details.responseHeaders?.['content-security-policy']) {
delete details.responseHeaders['content-security-policy']
}
callback({ cancel: false, responseHeaders: details.responseHeaders })
})
}
private loadMainWindowContent(mainWindow: BrowserWindow) {
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
}
public getMainWindow(): BrowserWindow | null {
return this.mainWindow
}
private setupWindowLifecycleEvents(mainWindow: BrowserWindow) {
mainWindow.on('close', (event) => {
const notInTray = !configManager.isTray()
// Windows and Linux
if ((isWin || isLinux) && notInTray) {
return app.quit()
}
// Mac
if (!app.isQuitting) {
event.preventDefault()
mainWindow.hide()
}
})
}
public showMainWindow() {
if (this.mainWindow) {
if (this.mainWindow.isMinimized()) {
return this.mainWindow.restore()
}
this.mainWindow.show()
this.mainWindow.focus()
} else {
this.createMainWindow()
}
}
}
export const windowService = WindowService.getInstance()

View File

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

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

@@ -0,0 +1,16 @@
import fs from 'node:fs'
import path from 'node:path'
import { app } from 'electron'
export function getResourcePath() {
return path.join(app.getAppPath(), 'resources')
}
export function getDataPath() {
const dataPath = path.join(app.getPath('userData'), 'Data')
if (!fs.existsSync(dataPath)) {
fs.mkdirSync(dataPath, { recursive: true })
}
return dataPath
}

11
src/main/utils/locales.ts Normal file
View File

@@ -0,0 +1,11 @@
import EnUs from '../../renderer/src/i18n/locales/en-us.json'
import ZhCn from '../../renderer/src/i18n/locales/zh-cn.json'
import ZhTw from '../../renderer/src/i18n/locales/zh-tw.json'
const locales = {
'en-US': EnUs,
'zh-CN': ZhCn,
'zh-TW': ZhTw
}
export { locales }

View File

@@ -0,0 +1,16 @@
function isTilingWindowManager() {
if (process.platform === 'darwin') {
return false
}
if (process.platform !== 'linux') {
return true
}
const desktopEnv = process.env.XDG_CURRENT_DESKTOP?.toLowerCase()
const tilingSystems = ['hyprland', 'i3', 'sway', 'bspwm', 'dwm', 'awesome', 'qtile', 'herbstluftwm', 'xmonad']
return tilingSystems.some((system) => desktopEnv?.includes(system))
}
export { isTilingWindowManager }

View File

@@ -1,125 +0,0 @@
import { is } from '@electron-toolkit/utils'
import { BrowserWindow, Menu, MenuItem, shell } from 'electron'
import windowStateKeeper from 'electron-window-state'
import { join } from 'path'
import icon from '../../build/icon.png?asset'
import { appConfig, titleBarOverlayDark, titleBarOverlayLight } from './config'
export function createMainWindow() {
// Load the previous state with fallback to defaults
const mainWindowState = windowStateKeeper({
defaultWidth: 1080,
defaultHeight: 670
})
const theme = appConfig.get('theme') || 'light'
// Create the browser window.
const mainWindow = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth: 1080,
minHeight: 600,
show: true,
autoHideMenuBar: true,
transparent: process.platform === 'darwin',
vibrancy: 'fullscreen-ui',
titleBarStyle: 'hidden',
titleBarOverlay: theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight,
trafficLightPosition: { x: 8, y: 12 },
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
webSecurity: false,
webviewTag: true
// devTools: !app.isPackaged,
}
})
mainWindowState.manage(mainWindow)
mainWindow.webContents.on('context-menu', () => {
const menu = new Menu()
menu.append(new MenuItem({ label: '复制', role: 'copy', sublabel: '⌘ + C' }))
menu.append(new MenuItem({ label: '粘贴', role: 'paste', sublabel: '⌘ + V' }))
menu.append(new MenuItem({ label: '剪切', role: 'cut', sublabel: '⌘ + X' }))
menu.append(new MenuItem({ type: 'separator' }))
menu.append(new MenuItem({ label: '全选', role: 'selectAll', sublabel: '⌘ + A' }))
menu.popup()
})
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})
mainWindow.webContents.on('will-navigate', (event, url) => {
event.preventDefault()
shell.openExternal(url)
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
mainWindow.webContents.session.webRequest.onHeadersReceived({ urls: ['*://*/*'] }, (details, callback) => {
if (details.responseHeaders?.['X-Frame-Options']) {
delete details.responseHeaders['X-Frame-Options']
}
if (details.responseHeaders?.['x-frame-options']) {
delete details.responseHeaders['x-frame-options']
}
if (details.responseHeaders?.['Content-Security-Policy']) {
delete details.responseHeaders['Content-Security-Policy']
}
if (details.responseHeaders?.['content-security-policy']) {
delete details.responseHeaders['content-security-policy']
}
callback({ cancel: false, responseHeaders: details.responseHeaders })
})
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
return mainWindow
}
export function createMinappWindow({
url,
parent,
windowOptions
}: {
url: string
parent?: BrowserWindow
windowOptions?: Electron.BrowserWindowConstructorOptions
}) {
const width = windowOptions?.width || 1000
const height = windowOptions?.height || 680
const minappWindow = new BrowserWindow({
width,
height,
autoHideMenuBar: true,
title: 'Cherry Studio',
...windowOptions,
parent,
webPreferences: {
preload: join(__dirname, '../preload/minapp.js'),
sandbox: false,
contextIsolation: false
}
})
minappWindow.loadURL(url)
return minappWindow
}

View File

@@ -1,5 +1,9 @@
import { ElectronAPI } from '@electron-toolkit/preload'
import { FileType } from '@renderer/types'
import { WebDavConfig } from '@renderer/types'
import { LanguageVarious } from '@renderer/types'
import type { OpenDialogOptions } from 'electron'
import { Readable } from 'stream'
declare global {
interface Window {
@@ -9,17 +13,50 @@ declare global {
version: string
isPackaged: boolean
appPath: string
filesPath: string
}>
checkForUpdate: () => void
openWebsite: (url: string) => 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>
setLanguage: (theme: LanguageVarious) => void
setTray: (isActive: boolean) => void
setTheme: (theme: 'light' | 'dark') => void
minApp: (options: { url: string; windowOptions?: Electron.BrowserWindowConstructorOptions }) => void
reload: () => void
compress: (text: string) => Promise<Buffer>
decompress: (text: Buffer) => Promise<string>
zip: {
compress: (text: string) => Promise<Buffer>
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
) => Promise<string | null>
saveImage: (name: string, data: string) => void
base64Image: (fileId: string) => Promise<{ mime: string; base64: string; data: string }>
download: (url: string) => Promise<FileType | null>
copy: (fileId: string, destPath: string) => Promise<void>
}
export: {
toWord: (markdown: string, fileName: string) => Promise<void>
}
}
}
}

View File

@@ -1,21 +1,51 @@
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
const api = {
getAppInfo: () => ipcRenderer.invoke('get-app-info'),
checkForUpdate: () => ipcRenderer.invoke('check-for-update'),
openWebsite: (url: string) => ipcRenderer.invoke('open-website', url),
setProxy: (proxy: string) => ipcRenderer.invoke('set-proxy', proxy),
setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('set-theme', theme),
getAppInfo: () => ipcRenderer.invoke('app:info'),
reload: () => ipcRenderer.invoke('app:reload'),
setProxy: (proxy: string) => ipcRenderer.invoke('app:proxy', proxy),
checkForUpdate: () => ipcRenderer.invoke('app:check-for-update'),
setLanguage: (lang: string) => ipcRenderer.invoke('app:set-language', lang),
setTray: (isActive: boolean) => ipcRenderer.invoke('app:set-tray', isActive),
setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('app:set-theme', theme),
openWebsite: (url: string) => ipcRenderer.invoke('open:website', url),
minApp: (url: string) => ipcRenderer.invoke('minapp', url),
openFile: (options?: { decompress: boolean }) => ipcRenderer.invoke('open-file', options),
reload: () => ipcRenderer.invoke('reload'),
saveFile: (path: string, content: string, options?: { compress: boolean }) => {
ipcRenderer.invoke('save-file', path, content, options)
zip: {
compress: (text: string) => ipcRenderer.invoke('zip:compress', text),
decompress: (text: Buffer) => ipcRenderer.invoke('zip:decompress', text)
},
compress: (text: string) => ipcRenderer.invoke('zip:compress', 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),
download: (url: string) => ipcRenderer.invoke('file:download', url),
copy: (fileId: string, destPath: string) => ipcRenderer.invoke('file:copy', fileId, destPath)
},
export: {
toWord: (markdown: string, fileName: string) => ipcRenderer.invoke('export:word', markdown, fileName)
}
}
// Use `contextBridge` APIs to expose Electron APIs to

View File

@@ -1,14 +1,41 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="initial-scale=1, width=device-width" />
<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:" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="initial-scale=1, width=device-width" />
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src blob: *; script-src 'self' 'unsafe-eval' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: * blob:; frame-src * file:" />
<style>
html,
body {
margin: 0;
}
#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 { Provider } from 'react-redux'
import { HashRouter, Route, Routes } from 'react-router-dom'
@@ -6,10 +8,14 @@ import { PersistGate } from 'redux-persist/integration/react'
import Sidebar from './components/app/Sidebar'
import TopViewContainer from './components/TopView'
import AntdProvider from './context/AntdProvider'
import { SyntaxHighlighterProvider } from './context/SyntaxHighlighterProvider'
import { ThemeProvider } from './context/ThemeProvider'
import AgentsPage from './pages/agents/AgentsPage'
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 PaintingsPage from './pages/paintings/PaintingsPage'
import SettingsPage from './pages/settings/SettingsPage'
import TranslatePage from './pages/translate/TranslatePage'
@@ -18,20 +24,25 @@ function App(): JSX.Element {
<Provider store={store}>
<ThemeProvider>
<AntdProvider>
<PersistGate loading={null} persistor={persistor}>
<TopViewContainer>
<HashRouter>
<Sidebar />
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/agents" element={<AgentsPage />} />
<Route path="/translate" element={<TranslatePage />} />
<Route path="/apps" element={<AppsPage />} />
<Route path="/settings/*" element={<SettingsPage />} />
</Routes>
</HashRouter>
</TopViewContainer>
</PersistGate>
<SyntaxHighlighterProvider>
<PersistGate loading={null} persistor={persistor}>
<TopViewContainer>
<HashRouter>
<Sidebar />
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/files" element={<FilesPage />} />
<Route path="/agents" element={<AgentsPage />} />
<Route path="/paintings" element={<PaintingsPage />} />
<Route path="/translate" element={<TranslatePage />} />
<Route path="/apps" element={<AppsPage />} />
<Route path="/messages/*" element={<HistoryPage />} />
<Route path="/settings/*" element={<SettingsPage />} />
</Routes>
</HashRouter>
</TopViewContainer>
</PersistGate>
</SyntaxHighlighterProvider>
</AntdProvider>
</ThemeProvider>
</Provider>

View File

@@ -1,6 +1,6 @@
@font-face {
font-family: 'iconfont'; /* Project id 4563475 */
src: url('iconfont.woff2?t=1724204739157') format('woff2');
src: url('iconfont.woff2?t=1725606177995') format('woff2');
}
.iconfont {
@@ -11,6 +11,30 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-a-darkmode:before {
content: '\e6cd';
}
.icon-ai-model:before {
content: '\e827';
}
.icon-ai-model1:before {
content: '\ec09';
}
.icon-gridlines:before {
content: '\e942';
}
.icon-grid-row-2copy:before {
content: '\e681';
}
.icon-inbox:before {
content: '\e869';
}
.icon-business-smart-assistant:before {
content: '\e601';
}
@@ -39,11 +63,11 @@
content: '\e758';
}
.icon-hidesidebarhoriz:before {
.icon-hide-sidebar:before {
content: '\e8eb';
}
.icon-showsidebarhoriz:before {
.icon-show-sidebar:before {
content: '\e944';
}

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: 4.8 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.

After

Width:  |  Height:  |  Size: 14 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: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

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