mirror of
https://github.com/FranP-code/ChatGPT.git
synced 2025-10-13 00:13:25 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0d2869dff | ||
|
|
70881f95da | ||
|
|
60f6628ec0 | ||
|
|
60abf6e487 | ||
|
|
216ef3ff1a | ||
|
|
7b12d3ebfe | ||
|
|
396dc0b762 | ||
|
|
cd7d5fab63 | ||
|
|
8363ff234d | ||
|
|
430d6e4af2 | ||
|
|
918d53c9b9 | ||
|
|
a88c3603cc | ||
|
|
785be23528 | ||
|
|
bb44cdd5b7 | ||
|
|
5a835c114e | ||
|
|
3ea8f44452 | ||
|
|
04a579ff19 | ||
|
|
f08cd978cb | ||
|
|
7cf20cb571 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1 +1,3 @@
|
||||
*.js linguist-vendored
|
||||
*.js linguist-vendored
|
||||
*.tsx linguist-vendored
|
||||
*.scss linguist-vendored
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -75,7 +75,7 @@ jobs:
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install app dependencies and build it
|
||||
run: yarn
|
||||
run: yarn && yarn build:fe
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0.3
|
||||
env:
|
||||
|
||||
29
.gitignore
vendored
29
.gitignore
vendored
@@ -1,9 +1,32 @@
|
||||
.DS_Store
|
||||
*.lock
|
||||
|
||||
package-lock.json
|
||||
node_modules/
|
||||
yarn.lock
|
||||
*.lock
|
||||
|
||||
# rust
|
||||
target/
|
||||
Cargo.lock
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
- [ChatGPT Export and Share](https://github.com/liady/ChatGPT-pdf) - A Chrome extension for downloading your ChatGPT history to PNG, PDF or creating a sharable link
|
||||
- [ChatGPT for Google](https://github.com/wong2/chat-gpt-google-extension) - A browser extension to display ChatGPT response alongside Google Search results
|
||||
- [ChatGPT Extension](https://github.com/kazuki-sf/ChatGPT_Extension) - ChatGPT Extension is a really simple Chrome Extension (manifest v3) that you can access OpenAI's ChatGPT from anywhere on the web.
|
||||
- [ChatGPT-Google](https://github.com/ZohaibAhmed/ChatGPT-Google) - Chrome Extension that Integrates ChatGPT (Unofficial) into Google Search
|
||||
|
||||
`VSCode`
|
||||
|
||||
@@ -18,3 +19,8 @@
|
||||
`Bot`
|
||||
|
||||
- [ChatGPT Telegram Bot](https://github.com/altryne/chatGPT-telegram-bot) - This is a very early attempt at having chatGPT work within a telegram bot
|
||||
|
||||
## Tools
|
||||
|
||||
- [commitgpt](https://github.com/RomanHotsiy/commitgpt) - Automatically generate commit messages using ChatGPT
|
||||
- [ShareGPT](https://sharegpt.com/) - ShareGPT: Share your wildest ChatGPT conversations with one click.
|
||||
|
||||
214
LICENSE
214
LICENSE
@@ -1,21 +1,201 @@
|
||||
MIT License
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2022 lencx
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
1. Definitions.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
154
README-ZH.md
Normal file
154
README-ZH.md
Normal file
@@ -0,0 +1,154 @@
|
||||
<p align="center">
|
||||
<img width="180" src="./public/logo.png" alt="ChatGPT">
|
||||
<h1 align="center">ChatGPT</h1>
|
||||
</p>
|
||||
|
||||
> ChatGPT 桌面应用
|
||||
|
||||
[](./README.md)
|
||||
[](./README-ZH.md)
|
||||
[](https://github.com/lencx/ChatGPT/releases)
|
||||
[](https://twitter.com/lencx_)
|
||||
|
||||
[Awesome ChatGPT](./AWESOME.md)
|
||||
|
||||
## 📦 下载
|
||||
|
||||
[📝 更新日志](./UPDATE_LOG.md)
|
||||
|
||||
<!-- download start -->
|
||||
|
||||
**最新版:**
|
||||
|
||||
- `Mac`: [ChatGPT_0.2.1_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/ChatGPT_0.2.1_x64.dmg)
|
||||
- `Linux`: [chat-gpt_0.2.1_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/chat-gpt_0.2.1_amd64.deb)
|
||||
- `Windows`: [ChatGPT_0.2.1_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/ChatGPT_0.2.1_x64_en-US.msi)
|
||||
|
||||
[其他版本...](https://github.com/lencx/ChatGPT/releases)
|
||||
|
||||
<!-- download end -->
|
||||
|
||||
### brew 安装
|
||||
|
||||
Easily install with _[Homebrew](https://brew.sh) ([Cask](https://docs.brew.sh/Cask-Cookbook)):_
|
||||
|
||||
~~~ sh
|
||||
brew tap lencx/chatgpt https://github.com/lencx/ChatGPT.git
|
||||
brew install --cask chatgpt --no-quarantine
|
||||
~~~
|
||||
|
||||
Also, if you keep a _[Brewfile](https://github.com/Homebrew/homebrew-bundle#usage)_, you can add something like this:
|
||||
|
||||
~~~ rb
|
||||
repo = "lencx/chatgpt"
|
||||
tap repo, "https://github.com/#{repo}.git"
|
||||
cask "popcorn-time", args: { "no-quarantine": true }
|
||||
~~~
|
||||
|
||||
## ✨ 功能概览
|
||||
|
||||
- 跨平台: `macOS` `Linux` `Windows`
|
||||
- 导出 ChatGPT 聊天记录 (支持 PNG, PDF 和生成分享链接)
|
||||
- 应用自动升级通知
|
||||
- 丰富的快捷键
|
||||
- 系统托盘悬浮窗
|
||||
- 应用菜单功能强大
|
||||
|
||||
### 菜单项
|
||||
|
||||
- **Preferences (喜好)**
|
||||
- `Theme` - `Light`, `Dark` (仅支持 macOS 和 Windows)
|
||||
- `Always On Top`: 窗口置顶
|
||||
- `Titlebar`: 是否显示 `Titlebar`,仅 macOS 支持
|
||||
- `Inject Script`: 用于修改网站的用户自定义脚本
|
||||
- `Control Center`: ChatGPT 应用的控制中心,它将为应用提供无限的可能
|
||||
- 设置 `Theme`,`Always on Top`,`Titlebar` 等
|
||||
- `User Agent` ([#17](https://github.com/lencx/ChatGPT/issues/17)): 自定义 `user agent` 防止网站安全检测,默认值为空
|
||||
- `Switch Origin` ([#14](https://github.com/lencx/ChatGPT/issues/14)): 切换网站源地址,默认为 `https://chat.openai.com`。需要注意的是镜像网站的 UI 需要和原网站一致,否则可能会导致某些功能不工作
|
||||
- `Go to Config`: 打开 ChatGPT 配置目录 (`path: ~/.chatgpt/*`)
|
||||
- `Clear Config`: 清除 ChatGPT 配置数据 (`path: ~/.chatgpt/*`), 这是危险操作,请提前备份数据
|
||||
- `Restart ChatGPT`: 重启应用。如果注入脚本编辑完成,或者应用可卡死可以通过此菜单重新启动应用
|
||||
- `Awesome ChatGPT`: 一个很棒的 ChatGPT 推荐列表
|
||||
- **Edit** - `Undo`, `Redo`, `Cut`, `Copy`, `SelectAll`, ...
|
||||
- **View** - `Go Back`, `Go Forward`, `Scroll to Top of Screen`, `Scroll to Bottom of Screen`, `Refresh the Screen`, ...
|
||||
- **Help**
|
||||
- `Update Log`: ChatGPT 应用更新日志
|
||||
- `Report Bug`: 报告 BUG 或反馈建议
|
||||
- `Toggle Developer Tools`: 网站调试工具,调试页面或脚本可能需要
|
||||
|
||||
## 👀 预览
|
||||
|
||||
<img width="320" src="./assets/install.png" alt="install"> <img width="320" src="./assets/chat-control-center.png" alt="chat control center">
|
||||
<img width="320" src="./assets/export.png" alt="export"> <img width="320" src="./assets/tray.png" alt="tray">
|
||||
<img width="320" src="./assets/chat-tray-login.png" alt="chat tray login"> <img width="320" src="./assets/auto-update.png" alt="auto update">
|
||||
|
||||
---
|
||||
|
||||
<a href="https://www.buymeacoffee.com/lencx" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
||||
|
||||
## 国内用户
|
||||
|
||||
国内用户如果遇到使用问题或者想交流 ChatGPT 技巧,可以关注公众号“浮之静”,发送 “chat” 进群参与讨论。如果对 tauri 开发应用感兴趣可以关注公众号后回复 “tauri” 进技术开发群(想私聊的也可以关注公众号,来添加微信)。
|
||||
|
||||
<img width="180" src="https://user-images.githubusercontent.com/16164244/207228300-ea5c4688-c916-4c55-a8c3-7f862888f351.png"> <img width="200" src="https://user-images.githubusercontent.com/16164244/207228025-117b5f77-c5d2-48c2-a070-774b7a1596f2.png">
|
||||
|
||||
## ❓常见问题
|
||||
|
||||
### 不能打开 ChatGPT
|
||||
|
||||
如果升级应用后无法打开,请尝试清除配置,它位于此目录 `~/.chatgpt/*`。
|
||||
|
||||
### 主窗口已经登录,但是系统托盘窗口显示未登录
|
||||
|
||||
可通过菜单项里的 `Restart ChatGPT` 重启应用来修复这个问题(`Menu -> Preferences -> Restart ChatGPT`)。
|
||||
|
||||
### 它是否安全?
|
||||
|
||||
它是安全的,仅仅只是对 [OpenAI ChatGPT](https://chat.openai.com) 网站的包装,注入了一些额外功能(均在本地,未发起网络请求),如果存疑,可以检查源代码。
|
||||
|
||||
### Developer cannot be verified?
|
||||
|
||||
Mac 上无法安装,提示开发者未验证,具体可以查看下面给出的解决方案(它是开源的,很安全)。
|
||||
|
||||
- [Open a Mac app from an unidentified developer](https://support.apple.com/en-sg/guide/mac-help/mh40616/mac)
|
||||
|
||||
### 我想自己构建它?
|
||||
|
||||
#### 预安装
|
||||
|
||||
- [Rust](https://www.rust-lang.org/)
|
||||
- [VS Code](https://code.visualstudio.com/)
|
||||
- [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
|
||||
- [tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode)
|
||||
|
||||
#### 开始
|
||||
|
||||
```bash
|
||||
# step1: 克隆仓库
|
||||
git clone https://github.com/lencx/ChatGPT.git
|
||||
|
||||
# step2: 进入目录
|
||||
cd ChatGPT
|
||||
|
||||
# step3: 安装依赖
|
||||
yarn
|
||||
|
||||
# step4: 开发启动
|
||||
yarn dev
|
||||
|
||||
# step5: 构建应用
|
||||
# 构建后的安装包位置: src-tauri/target/release/bundle
|
||||
yarn build
|
||||
```
|
||||
|
||||
## ❤️ 感谢
|
||||
|
||||
- 分享按钮的代码从 [@liady](https://github.com/liady) 的插件获得,并做了一些本地化修改
|
||||
|
||||
---
|
||||
|
||||
[](https://star-history.com/#lencx/chatgpt&Date)
|
||||
|
||||
## License
|
||||
|
||||
Apache License
|
||||
76
README.md
76
README.md
@@ -1,10 +1,12 @@
|
||||
<p align="center">
|
||||
<img width="180" src="./logo.png" alt="ChatGPT">
|
||||
<img width="180" src="./public/logo.png" alt="ChatGPT">
|
||||
<h1 align="center">ChatGPT</h1>
|
||||
</p>
|
||||
|
||||
> ChatGPT Desktop Application
|
||||
|
||||
[](./README.md)
|
||||
[](./README-ZH.md)
|
||||
[](https://github.com/lencx/ChatGPT/releases)
|
||||
[](https://twitter.com/lencx_)
|
||||
|
||||
@@ -18,9 +20,9 @@
|
||||
|
||||
**Latest:**
|
||||
|
||||
- `Mac`: [ChatGPT_0.2.0_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.2.0/ChatGPT_0.2.0_x64.dmg)
|
||||
- `Linux`: [chat-gpt_0.2.0_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.2.0/chat-gpt_0.2.0_amd64.deb)
|
||||
- `Windows`: [ChatGPT_0.2.0_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.2.0/ChatGPT_0.2.0_x64_en-US.msi)
|
||||
- `Mac`: [ChatGPT_0.2.1_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/ChatGPT_0.2.1_x64.dmg)
|
||||
- `Linux`: [chat-gpt_0.2.1_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/chat-gpt_0.2.1_amd64.deb)
|
||||
- `Windows`: [ChatGPT_0.2.1_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.2.1/ChatGPT_0.2.1_x64_en-US.msi)
|
||||
|
||||
[Other version...](https://github.com/lencx/ChatGPT/releases)
|
||||
|
||||
@@ -45,44 +47,46 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- multi-platform: `macOS` `Linux` `Windows`
|
||||
- export ChatGPT history (PNG, PDF and Share Link)
|
||||
- always on top (whether the window should always be on top of other windows)
|
||||
- inject script
|
||||
- auto updater
|
||||
- app menu
|
||||
- tray window
|
||||
- shortcut
|
||||
- Multi-platform: `macOS` `Linux` `Windows`
|
||||
- Export ChatGPT history (PNG, PDF and Share Link)
|
||||
- Automatic application upgrade notification
|
||||
- Common shortcut keys
|
||||
- System tray hover window
|
||||
- Powerful menu items
|
||||
|
||||
### Menu
|
||||
### MenuItem
|
||||
|
||||
- **Preferences**
|
||||
- `Theme` - `Light`, `Dark` (Only macOS and Windows are supported).
|
||||
- `Always On Top`: Window is always on top of other windows.
|
||||
- `Titlebar`: Only supports macOS.
|
||||
- `User Agent` ([#17](https://github.com/lencx/ChatGPT/issues/17)): Customize `user agent` to prevent security detection interception. Default is empty string.
|
||||
- `Inject Script`: User scripts that can modify web pages.
|
||||
- `Switch Origin` ([#14](https://github.com/lencx/ChatGPT/issues/14)): Modify website address, the default is `https://chat.openai.com`. Please ensure that the mirror address is consistent with the UI of the original URL, otherwise the export function will fail.
|
||||
- `Clear Config`: Clear all chatgpt configuration files (`path: ~/.chatgpt/*`), dangerous operation, please backup data.
|
||||
- `Restart ChatGPT`: After editing the injection script file, you can restart the application through this menu item to make the script take effect.
|
||||
- `Awesome ChatGPT`: Related resources recommended.
|
||||
- `Always on Top`: The window is always on top of other windows.
|
||||
- `Titlebar`: Whether to display the titlebar, supported by macOS only.
|
||||
- `Inject Script`: Using scripts to modify pages.
|
||||
- `Control Center`: The control center of ChatGPT application, it will give unlimited imagination to the application.
|
||||
- `Theme`, `Always on Top`, `Titlebar`, ...
|
||||
- `User Agent` ([#17](https://github.com/lencx/ChatGPT/issues/17)): Custom `user agent`, which may be required in some scenarios. The default value is the empty string.
|
||||
- `Switch Origin` ([#14](https://github.com/lencx/ChatGPT/issues/14)): Switch the site source address, the default is `https://chat.openai.com`, please make sure the mirror site UI is the same as the original address. Otherwise, some functions may not be available.
|
||||
- `Go to Config`: Open the configuration file directory (`path: ~/.chatgpt/*`).
|
||||
- `Clear Config`: Clear the configuration file (`path: ~/.chatgpt/*`), dangerous operation, please backup the data in advance.
|
||||
- `Restart ChatGPT`: Restart the application, for example: the program is stuck or the injection script can take effect by restarting the application after editing.
|
||||
- `Awesome ChatGPT`: Recommended Related Resources.
|
||||
- **Edit** - `Undo`, `Redo`, `Cut`, `Copy`, `SelectAll`, ...
|
||||
- **View** - `Go Back`, `Go Forward`, `Scroll to Top of Screen`, `Scroll to Bottom of Screen`, `Refresh the Screen`, ...
|
||||
- **Help**
|
||||
- `Update Log`: ChatGPT app changelog.
|
||||
- `Report Bug`: Defects and Suggestions Feedback.
|
||||
- `Toggle Developer Tools`: Developer tools for debugging web pages.
|
||||
- `Update Log`: ChatGPT changelog.
|
||||
- `Report Bug`: Report a bug or give feedback.
|
||||
- `Toggle Developer Tools`: Developer debugging tools.
|
||||
|
||||
## TODO
|
||||
|
||||
- web access capability ([#20](https://github.com/lencx/ChatGPT/issues/20))
|
||||
- Web access capability ([#20](https://github.com/lencx/ChatGPT/issues/20))
|
||||
- Shortcut command typing chatgpt prompt
|
||||
- ...
|
||||
|
||||
## 👀 Preview
|
||||
|
||||
<img width="320" src="./assets/install.png" alt="install"> <img width="320" src="./assets/chat.png" alt="chat">
|
||||
<img width="320" src="./assets/install.png" alt="install"> <img width="320" src="./assets/chat-control-center.png" alt="chat control center">
|
||||
<img width="320" src="./assets/export.png" alt="export"> <img width="320" src="./assets/tray.png" alt="tray">
|
||||
<img width="320" src="./assets/chat-ua.png" alt="user agent"> <img width="320" src="./assets/auto-update.png" alt="auto update">
|
||||
<img width="320" src="./assets/chat-tray-login.png" alt="chat tray login"> <img width="320" src="./assets/auto-update.png" alt="auto update">
|
||||
|
||||
---
|
||||
|
||||
@@ -92,7 +96,11 @@ cask "popcorn-time", args: { "no-quarantine": true }
|
||||
|
||||
### Can't open ChatGPT
|
||||
|
||||
If the application cannot be opened after the upgrade, please try to clear the configuration file, which is in the `~/.chatgpt/*` directory.
|
||||
If you cannot open the application after the upgrade, please try to clear the configuration file, which is in the `~/.chatgpt/*` directory.
|
||||
|
||||
## Out of sync login status between multiple windows
|
||||
|
||||
If you have already logged in in the main window, but the system tray window shows that you are not logged in, you can fix it by restarting the application (`Menu -> Preferences -> Restart ChatGPT`).
|
||||
|
||||
### Is it safe?
|
||||
|
||||
@@ -133,10 +141,12 @@ yarn build
|
||||
|
||||
## ❤️ Thanks
|
||||
|
||||
- The share buttons code is copied directly from [@liady](https://github.com/liady) extension with minor modifications.
|
||||
- The core implementation of the share button code was copied from the [@liady](https://github.com/liady) extension with some modifications.
|
||||
|
||||
## Related
|
||||
---
|
||||
|
||||
- [Tauri](https://tauri.app) - Build an optimized, secure, and frontend-independent application for multi-platform deployment.
|
||||
- [ChatGPT](https://openai.com/blog/chatgpt) - ChatGPT: Optimizing Language Models for Dialogue.
|
||||
- [ChatGPT Export and Share](https://github.com/liady/ChatGPT-pdf) - A Chrome extension for downloading your ChatGPT history to PNG, PDF or creating a sharable link.
|
||||
[](https://star-history.com/#lencx/chatgpt&Date)
|
||||
|
||||
## License
|
||||
|
||||
Apache License
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# UPDATE LOG
|
||||
|
||||
## v0.3.0
|
||||
|
||||
fix: can't open ChatGPT
|
||||
|
||||
feat: menu enhancement
|
||||
- the control center of ChatGPT application
|
||||
- open the configuration file directory
|
||||
|
||||
## v0.2.2
|
||||
|
||||
feat:
|
||||
- menu: go to config
|
||||
|
||||
## v0.2.1
|
||||
|
||||
feat: menu optimization
|
||||
|
||||
BIN
assets/chat-control-center.png
Normal file
BIN
assets/chat-control-center.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 500 KiB |
BIN
assets/chat-tray-login.png
Normal file
BIN
assets/chat-tray-login.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 989 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 391 KiB |
BIN
assets/chat.png
BIN
assets/chat.png
Binary file not shown.
|
Before Width: | Height: | Size: 653 KiB |
0
dist/.gitkeep
vendored
0
dist/.gitkeep
vendored
13
index.html
Normal file
13
index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ChatGPT</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
23
package.json
23
package.json
@@ -2,6 +2,8 @@
|
||||
"name": "chatgpt",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev:fe": "vite",
|
||||
"build:fe": "tsc && vite build",
|
||||
"dev": "yarn tauri dev",
|
||||
"build": "yarn tauri build",
|
||||
"updater": "tr updater",
|
||||
@@ -27,8 +29,25 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/lencx/ChatGPT"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.2.0",
|
||||
"antd": "^5.0.6",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^1.2.1",
|
||||
"@tauri-release/cli": "^0.2.3"
|
||||
"@tauri-apps/cli": "^1.2.2",
|
||||
"@tauri-release/cli": "^0.2.3",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "^18.7.10",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@vitejs/plugin-react": "^3.0.0",
|
||||
"sass": "^1.56.2",
|
||||
"typescript": "^4.9.4",
|
||||
"vite": "^4.0.0",
|
||||
"vite-tsconfig-paths": "^4.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@@ -35,9 +35,8 @@ pub fn get_chat_conf() -> ChatConfJson {
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn form_confirm(app: AppHandle, data: serde_json::Value) {
|
||||
ChatConfJson::amend(&serde_json::json!(data)).unwrap();
|
||||
tauri::api::process::restart(&app.env());
|
||||
pub fn form_confirm(_app: AppHandle, data: serde_json::Value) {
|
||||
ChatConfJson::amend(&serde_json::json!(data), None).unwrap();
|
||||
}
|
||||
|
||||
#[command]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::{
|
||||
app::window,
|
||||
conf::{self, ChatConfJson},
|
||||
utils,
|
||||
};
|
||||
@@ -26,7 +25,7 @@ pub fn init(context: &Context<EmbeddedAssets>) -> Menu {
|
||||
.add_native_item(MenuItem::Quit),
|
||||
);
|
||||
|
||||
let always_on_top = CustomMenuItem::new("always_on_top".to_string(), "Always On Top")
|
||||
let always_on_top = CustomMenuItem::new("always_on_top".to_string(), "Always on Top")
|
||||
.accelerator("CmdOrCtrl+T");
|
||||
let titlebar =
|
||||
CustomMenuItem::new("titlebar".to_string(), "Titlebar").accelerator("CmdOrCtrl+B");
|
||||
@@ -67,27 +66,25 @@ pub fn init(context: &Context<EmbeddedAssets>) -> Menu {
|
||||
#[cfg(target_os = "macos")]
|
||||
titlebar_menu.into(),
|
||||
MenuItem::Separator.into(),
|
||||
// fix: Checking if the site connection is secure
|
||||
// @link: https://github.com/lencx/ChatGPT/issues/17
|
||||
CustomMenuItem::new("user_agent".to_string(), "User Agent")
|
||||
.accelerator("CmdOrCtrl+U")
|
||||
.into(),
|
||||
CustomMenuItem::new("switch_origin".to_string(), "Switch Origin")
|
||||
.accelerator("CmdOrCtrl+O")
|
||||
.into(),
|
||||
CustomMenuItem::new("inject_script".to_string(), "Inject Script")
|
||||
.accelerator("CmdOrCtrl+J")
|
||||
.into(),
|
||||
CustomMenuItem::new("control_center".to_string(), "Control Center")
|
||||
.accelerator("CmdOrCtrl+Shift+P")
|
||||
.into(),
|
||||
MenuItem::Separator.into(),
|
||||
CustomMenuItem::new("go_conf".to_string(), "Go to Config")
|
||||
.accelerator("CmdOrCtrl+Shift+G")
|
||||
.into(),
|
||||
CustomMenuItem::new("clear_conf".to_string(), "Clear Config")
|
||||
.accelerator("CmdOrCtrl+D")
|
||||
.accelerator("CmdOrCtrl+Shift+D")
|
||||
.into(),
|
||||
CustomMenuItem::new("restart".to_string(), "Restart ChatGPT")
|
||||
.accelerator("CmdOrCtrl+Shift+R")
|
||||
.into(),
|
||||
MenuItem::Separator.into(),
|
||||
CustomMenuItem::new("awesome".to_string(), "Awesome ChatGPT")
|
||||
.accelerator("CmdOrCtrl+Z")
|
||||
.accelerator("CmdOrCtrl+Shift+A")
|
||||
.into(),
|
||||
]),
|
||||
);
|
||||
@@ -162,15 +159,19 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
|
||||
match menu_id {
|
||||
// Preferences
|
||||
"control_center" => app.get_window("main").unwrap().show().unwrap(),
|
||||
"restart" => tauri::api::process::restart(&app.env()),
|
||||
"inject_script" => open(&app, script_path),
|
||||
"go_conf" => utils::open_file(utils::chat_root()),
|
||||
"clear_conf" => utils::clear_conf(&app),
|
||||
"switch_origin" => window::origin_window(&app),
|
||||
"user_agent" => window::ua_window(&app),
|
||||
"awesome" => open(&app, conf::AWESOME_URL.to_string()),
|
||||
"titlebar" => {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
ChatConfJson::amend(&serde_json::json!({ "titlebar": !chat_conf.titlebar })).unwrap();
|
||||
ChatConfJson::amend(
|
||||
&serde_json::json!({ "titlebar": !chat_conf.titlebar }),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
tauri::api::process::restart(&app.env());
|
||||
}
|
||||
"theme_light" | "theme_dark" => {
|
||||
@@ -179,8 +180,7 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
} else {
|
||||
"Light"
|
||||
};
|
||||
ChatConfJson::amend(&serde_json::json!({ "theme": theme })).unwrap();
|
||||
tauri::api::process::restart(&app.env());
|
||||
ChatConfJson::amend(&serde_json::json!({ "theme": theme }), Some(app)).unwrap();
|
||||
}
|
||||
"always_on_top" => {
|
||||
let mut always_on_top = state.always_on_top.lock().unwrap();
|
||||
@@ -190,7 +190,11 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||
.set_selected(*always_on_top)
|
||||
.unwrap();
|
||||
win.set_always_on_top(*always_on_top).unwrap();
|
||||
ChatConfJson::amend(&serde_json::json!({ "always_on_top": *always_on_top })).unwrap();
|
||||
ChatConfJson::amend(
|
||||
&serde_json::json!({ "always_on_top": *always_on_top }),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
// View
|
||||
"reload" => win.eval("window.location.reload()").unwrap(),
|
||||
|
||||
@@ -21,7 +21,7 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
||||
.initialization_script(include_str!("../assets/jspdf.js"))
|
||||
.initialization_script(include_str!("../assets/core.js"))
|
||||
.initialization_script(include_str!("../assets/export.js"))
|
||||
.user_agent(&chat_conf.ua_pc)
|
||||
.user_agent(&chat_conf.ua_window)
|
||||
.build()?;
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
@@ -37,7 +37,7 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
||||
.initialization_script(include_str!("../assets/jspdf.js"))
|
||||
.initialization_script(include_str!("../assets/core.js"))
|
||||
.initialization_script(include_str!("../assets/export.js"))
|
||||
.user_agent(&chat_conf.ua_pc)
|
||||
.user_agent(&chat_conf.ua_window)
|
||||
.build()?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -17,37 +17,9 @@ pub fn mini_window(handle: &tauri::AppHandle) {
|
||||
.initialization_script(include_str!("../assets/jspdf.js"))
|
||||
.initialization_script(include_str!("../assets/core.js"))
|
||||
.initialization_script(include_str!("../assets/export.js"))
|
||||
.user_agent(&chat_conf.ua_phone)
|
||||
.user_agent(&chat_conf.ua_tray)
|
||||
.build()
|
||||
.unwrap()
|
||||
.hide()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn origin_window(handle: &tauri::AppHandle) {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
WindowBuilder::new(handle, "origin", WindowUrl::App(chat_conf.origin.into()))
|
||||
.resizable(false)
|
||||
.fullscreen(false)
|
||||
.inner_size(400.0, 300.0)
|
||||
.always_on_top(true)
|
||||
.decorations(false)
|
||||
.initialization_script(include_str!("../assets/core.js"))
|
||||
.initialization_script(include_str!("../assets/origin.js"))
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn ua_window(handle: &tauri::AppHandle) {
|
||||
let chat_conf = conf::ChatConfJson::get_chat_conf();
|
||||
WindowBuilder::new(handle, "ua", WindowUrl::App(chat_conf.origin.into()))
|
||||
.resizable(false)
|
||||
.fullscreen(false)
|
||||
.inner_size(540.0, 480.0)
|
||||
.always_on_top(true)
|
||||
.decorations(false)
|
||||
.initialization_script(include_str!("../assets/core.js"))
|
||||
.initialization_script(include_str!("../assets/ua.js"))
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
1
src-tauri/src/assets/core.js
vendored
1
src-tauri/src/assets/core.js
vendored
@@ -1,4 +1,5 @@
|
||||
// *** Core Script - IPC ***
|
||||
|
||||
const uid = () => window.crypto.getRandomValues(new Uint32Array(1))[0];
|
||||
function transformCallback(callback = () => {}, once = false) {
|
||||
const identifier = uid();
|
||||
|
||||
20
src-tauri/src/assets/export.js
vendored
20
src-tauri/src/assets/export.js
vendored
@@ -1,5 +1,5 @@
|
||||
// *** Core Script - Export ***
|
||||
// @ref: https://github.com/liady/ChatGPT-pdf/blob/main/src/content_script.js
|
||||
// @ref: https://github.com/liady/ChatGPT-pdf
|
||||
|
||||
async function init() {
|
||||
const chatConf = await invoke('get_chat_conf') || {};
|
||||
@@ -78,22 +78,9 @@ function downloadThread({ as = Format.PNG } = {}) {
|
||||
const pixelRatio = window.devicePixelRatio;
|
||||
const minRatio = as === Format.PDF ? 2 : 2.5;
|
||||
window.devicePixelRatio = Math.max(pixelRatio, minRatio);
|
||||
|
||||
html2canvas(elements.thread, {
|
||||
letterRendering: true,
|
||||
onclone: function (cloneDoc) {
|
||||
//Make small fix of position to all the text containers
|
||||
let listOfTexts = cloneDoc.getElementsByClassName("min-h-[20px]");
|
||||
Array.from(listOfTexts).forEach((text) => {
|
||||
text.style.position = "relative";
|
||||
text.style.top = "-8px";
|
||||
});
|
||||
|
||||
//Delete copy button from code blocks
|
||||
let listOfCopyBtns = cloneDoc.querySelectorAll("button.flex");
|
||||
Array.from(listOfCopyBtns).forEach(
|
||||
(btn) => (btn.style.visibility = "hidden")
|
||||
);
|
||||
},
|
||||
}).then(async function (canvas) {
|
||||
elements.restoreLocation();
|
||||
window.devicePixelRatio = pixelRatio;
|
||||
@@ -166,6 +153,8 @@ class Elements {
|
||||
img.setAttribute("srcset_old", srcset);
|
||||
img.setAttribute("srcset", "");
|
||||
});
|
||||
//Fix to the text shifting down when generating the canvas
|
||||
document.body.style.lineHeight = "0.5";
|
||||
}
|
||||
restoreLocation() {
|
||||
this.hiddens.forEach((el) => {
|
||||
@@ -182,6 +171,7 @@ class Elements {
|
||||
img.setAttribute("srcset", srcset);
|
||||
img.setAttribute("srcset_old", "");
|
||||
});
|
||||
document.body.style.lineHeight = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
77
src-tauri/src/assets/origin.js
vendored
77
src-tauri/src/assets/origin.js
vendored
@@ -1,77 +0,0 @@
|
||||
function init() {
|
||||
document.body.innerHTML = `<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
input {
|
||||
all: unset;
|
||||
width: 280px;
|
||||
height: 30px;
|
||||
margin-bottom: 10px;
|
||||
padding: 0 5px;
|
||||
border: solid 2px #d8d8d8;
|
||||
background-color: #fff;
|
||||
border-radius: 5px !important;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
button {
|
||||
all: unset;
|
||||
height: 30px;
|
||||
font-size: 16px;
|
||||
padding: 0 10px;
|
||||
line-height: 30px;
|
||||
margin: 0 5px;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#cancel {
|
||||
background-color: #999;
|
||||
}
|
||||
#confirm {
|
||||
background-color: #10a37f;
|
||||
}
|
||||
</style>
|
||||
<h3>Switch Origin</h3>
|
||||
<input id="input" type="text" autocapitalize="off" autocomplete="off" spellcheck="false" autofocus placeholder="https://chat.openai.com" />
|
||||
<div class="btns">
|
||||
<button id="cancel">Cancel</button>
|
||||
<button id="confirm">Confirm</button>
|
||||
</div>`;
|
||||
|
||||
const srcipt = document.createElement('script');
|
||||
srcipt.innerHTML = `const input = document.getElementById('input');
|
||||
const cancelBtn = document.getElementById('cancel');
|
||||
const confirmBtn = document.getElementById('confirm');
|
||||
cancelBtn.addEventListener('click', () => {
|
||||
window.invoke('form_cancel', { label: 'origin', title: 'Switch Origin', msg: 'Are you sure you want to cancel editing?' });
|
||||
})
|
||||
confirmBtn.addEventListener('click', () => {
|
||||
if (/^https?:\\/\\//.test(input.value)) {
|
||||
window.invoke('form_confirm', { data: { origin: input.value } });
|
||||
} else {
|
||||
window.invoke('form_msg', { label: 'origin', title: 'Switch Origin', msg: 'Invalid URL!' });
|
||||
}
|
||||
})`;
|
||||
document.head.appendChild(srcipt);
|
||||
}
|
||||
|
||||
// run init
|
||||
if (
|
||||
document.readyState === "complete" ||
|
||||
document.readyState === "interactive"
|
||||
) {
|
||||
init();
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
}
|
||||
89
src-tauri/src/assets/ua.js
vendored
89
src-tauri/src/assets/ua.js
vendored
@@ -1,89 +0,0 @@
|
||||
function init() {
|
||||
document.body.innerHTML = `<style>
|
||||
body {
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
textarea {
|
||||
all: unset;
|
||||
width: 300px;
|
||||
height: 60px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border: solid 2px #d8d8d8;
|
||||
background-color: #fff;
|
||||
border-radius: 5px !important;
|
||||
color: #4a4a4a;
|
||||
}
|
||||
button {
|
||||
all: unset;
|
||||
height: 30px;
|
||||
font-size: 16px;
|
||||
padding: 0 10px;
|
||||
line-height: 30px;
|
||||
margin: 0 5px;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#cancel {
|
||||
background-color: #999;
|
||||
}
|
||||
#confirm {
|
||||
background-color: #10a37f;
|
||||
}
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
label {
|
||||
width: 120px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
<h3>User Agent</h3>
|
||||
<div class="item">
|
||||
<label>Main Window (PC)</label>
|
||||
<textarea id="ua_pc" type="text" autocapitalize="off" autocomplete="off" spellcheck="false" autofocus placeholder="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ..."></textarea>
|
||||
</div>
|
||||
<div class="item">
|
||||
<label>Tray Window (Phone)</label>
|
||||
<textarea id="ua_phone" type="text" autocapitalize="off" autocomplete="off" spellcheck="false" autofocus placeholder="Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS ..."></textarea>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button id="cancel">Cancel</button>
|
||||
<button id="confirm">Confirm</button>
|
||||
</div>`;
|
||||
|
||||
const srcipt = document.createElement('script');
|
||||
srcipt.innerHTML = `const ua_pc = document.getElementById('ua_pc');
|
||||
const ua_phone = document.getElementById('ua_phone');
|
||||
const cancelBtn = document.getElementById('cancel');
|
||||
const confirmBtn = document.getElementById('confirm');
|
||||
cancelBtn.addEventListener('click', () => {
|
||||
window.invoke('form_cancel', { label: 'ua', title: 'User Agent', msg: 'Are you sure you want to cancel editing?' });
|
||||
})
|
||||
confirmBtn.addEventListener('click', () => {
|
||||
window.invoke('form_confirm', { data: { ua_pc: ua_pc.value, ua_phone: ua_phone.value } });
|
||||
})`;
|
||||
document.head.appendChild(srcipt);
|
||||
}
|
||||
|
||||
// run init
|
||||
if (
|
||||
document.readyState === "complete" ||
|
||||
document.readyState === "interactive"
|
||||
) {
|
||||
init();
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use crate::utils::{chat_root, create_file, exists};
|
||||
use anyhow::Result;
|
||||
use serde_json::Value;
|
||||
use std::{collections::BTreeMap, fs, path::PathBuf, sync::Mutex};
|
||||
use tauri::Theme;
|
||||
use tauri::{Manager, Theme};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::TitleBarStyle;
|
||||
@@ -19,8 +19,8 @@ pub const DEFAULT_CHAT_CONF: &str = r#"{
|
||||
"titlebar": true,
|
||||
"default_origin": "https://chat.openai.com",
|
||||
"origin": "https://chat.openai.com",
|
||||
"ua_pc": "",
|
||||
"ua_phone": ""
|
||||
"ua_window": "",
|
||||
"ua_tray": ""
|
||||
}"#;
|
||||
pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{
|
||||
"always_on_top": false,
|
||||
@@ -28,8 +28,8 @@ pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{
|
||||
"titlebar": false,
|
||||
"default_origin": "https://chat.openai.com",
|
||||
"origin": "https://chat.openai.com",
|
||||
"ua_pc": "",
|
||||
"ua_phone": ""
|
||||
"ua_window": "",
|
||||
"ua_tray": ""
|
||||
}"#;
|
||||
|
||||
pub struct ChatState {
|
||||
@@ -51,8 +51,8 @@ pub struct ChatConfJson {
|
||||
pub theme: String,
|
||||
pub default_origin: String,
|
||||
pub origin: String,
|
||||
pub ua_pc: String,
|
||||
pub ua_phone: String,
|
||||
pub ua_window: String,
|
||||
pub ua_tray: String,
|
||||
}
|
||||
|
||||
impl ChatConfJson {
|
||||
@@ -60,15 +60,30 @@ impl ChatConfJson {
|
||||
/// path: ~/.chatgpt/chat.conf.json
|
||||
pub fn init() -> PathBuf {
|
||||
let conf_file = ChatConfJson::conf_path();
|
||||
let content = if cfg!(target_os = "macos") {
|
||||
DEFAULT_CHAT_CONF_MAC
|
||||
} else {
|
||||
DEFAULT_CHAT_CONF
|
||||
};
|
||||
|
||||
if !exists(&conf_file) {
|
||||
create_file(&conf_file).unwrap();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fs::write(&conf_file, DEFAULT_CHAT_CONF_MAC).unwrap();
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fs::write(&conf_file, DEFAULT_CHAT_CONF).unwrap();
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
return conf_file;
|
||||
}
|
||||
|
||||
let conf_file = ChatConfJson::conf_path();
|
||||
let file_content = fs::read_to_string(&conf_file).unwrap();
|
||||
match serde_json::from_str(&file_content) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
if err.to_string() == "invalid type: map, expected unit at line 1 column 0" {
|
||||
return conf_file;
|
||||
}
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
conf_file
|
||||
}
|
||||
|
||||
@@ -77,15 +92,31 @@ impl ChatConfJson {
|
||||
}
|
||||
|
||||
pub fn get_chat_conf() -> Self {
|
||||
let config_file = fs::read_to_string(ChatConfJson::conf_path())
|
||||
.unwrap_or_else(|_| DEFAULT_CHAT_CONF.to_string());
|
||||
let config: Value =
|
||||
serde_json::from_str(&config_file).expect("failed to parse chat.conf.json");
|
||||
serde_json::from_value(config).unwrap_or_else(|_| ChatConfJson::chat_conf_default())
|
||||
let conf_file = ChatConfJson::conf_path();
|
||||
let file_content = fs::read_to_string(&conf_file).unwrap();
|
||||
let content = if cfg!(target_os = "macos") {
|
||||
DEFAULT_CHAT_CONF_MAC
|
||||
} else {
|
||||
DEFAULT_CHAT_CONF
|
||||
};
|
||||
|
||||
match serde_json::from_value(match serde_json::from_str(&file_content) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
serde_json::from_str(content).unwrap()
|
||||
}
|
||||
}) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
fs::write(&conf_file, content).unwrap();
|
||||
serde_json::from_value(serde_json::from_str(content).unwrap()).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://users.rust-lang.org/t/updating-object-fields-given-dynamic-json/39049/3
|
||||
pub fn amend(new_rules: &Value) -> Result<()> {
|
||||
pub fn amend(new_rules: &Value, app: Option<tauri::AppHandle>) -> Result<()> {
|
||||
let config = ChatConfJson::get_chat_conf();
|
||||
let config: Value = serde_json::to_value(&config)?;
|
||||
let mut config: BTreeMap<String, Value> = serde_json::from_value(config)?;
|
||||
@@ -99,6 +130,20 @@ impl ChatConfJson {
|
||||
ChatConfJson::conf_path(),
|
||||
serde_json::to_string_pretty(&config)?,
|
||||
)?;
|
||||
|
||||
if let Some(handle) = app {
|
||||
tauri::api::process::restart(&handle.env());
|
||||
// tauri::api::dialog::ask(
|
||||
// handle.get_window("core").as_ref(),
|
||||
// "ChatGPT Restart",
|
||||
// "Whether to restart immediately?",
|
||||
// move |is_restart| {
|
||||
// if is_restart {
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -120,8 +165,4 @@ impl ChatConfJson {
|
||||
TitleBarStyle::Overlay
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_conf_default() -> Self {
|
||||
serde_json::from_value(serde_json::json!(DEFAULT_CHAT_CONF)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,10 +36,15 @@ fn main() {
|
||||
.on_window_event(|event| {
|
||||
// https://github.com/tauri-apps/tauri/discussions/2684
|
||||
if let tauri::WindowEvent::CloseRequested { api, .. } = event.event() {
|
||||
// TODO: https://github.com/tauri-apps/tauri/issues/3084
|
||||
// event.window().hide().unwrap();
|
||||
// https://github.com/tauri-apps/tao/pull/517
|
||||
event.window().minimize().unwrap();
|
||||
let win = event.window();
|
||||
if win.label() == "main" {
|
||||
win.hide().unwrap();
|
||||
} else {
|
||||
// TODO: https://github.com/tauri-apps/tauri/issues/3084
|
||||
// event.window().hide().unwrap();
|
||||
// https://github.com/tauri-apps/tao/pull/517
|
||||
event.window().minimize().unwrap();
|
||||
}
|
||||
api.prevent_close();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"build": {
|
||||
"beforeDevCommand": "",
|
||||
"beforeBuildCommand": "",
|
||||
"devPath": "../dist",
|
||||
"beforeDevCommand": "npm run dev:fe",
|
||||
"beforeBuildCommand": "npm run build:fe",
|
||||
"devPath": "http://localhost:1420",
|
||||
"distDir": "../dist"
|
||||
},
|
||||
"package": {
|
||||
"productName": "ChatGPT",
|
||||
"version": "0.2.1"
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
@@ -63,6 +63,16 @@
|
||||
"https://lencx.github.io/ChatGPT/install.json"
|
||||
],
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEIxMjY4OUI5MTVFNjBEMDUKUldRRkRlWVZ1WWttc1NGWEE0RFNSb0RqdnhsekRJZTkwK2hVLzhBZTZnaHExSEZ1ZEdzWkpXTHkK"
|
||||
}
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"label": "main",
|
||||
"url": "index.html",
|
||||
"title": "ChatGPT",
|
||||
"visible": false,
|
||||
"width": 800,
|
||||
"height": 600
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
24
src/layout/index.scss
vendored
Normal file
24
src/layout/index.scss
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
.chat-logo {
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
|
||||
img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.ant-menu {
|
||||
.ant-menu-item {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-layout-footer {
|
||||
color: #666 !important;
|
||||
opacity: 0.7;
|
||||
}
|
||||
36
src/layout/index.tsx
vendored
Normal file
36
src/layout/index.tsx
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import { FC, useState } from 'react';
|
||||
import { Layout, Menu } from 'antd';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import Routes, { menuItems } from '@/routes';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const { Content, Footer, Sider } = Layout;
|
||||
|
||||
interface ChatLayoutProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const ChatLayout: FC<ChatLayoutProps> = ({ children }) => {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const go = useNavigate();
|
||||
|
||||
return (
|
||||
<Layout style={{ minHeight: '100vh' }}>
|
||||
<Sider theme="light" collapsible collapsed={collapsed} onCollapse={(value) => setCollapsed(value)}>
|
||||
<div className="chat-logo"><img src="/logo.png" /></div>
|
||||
<Menu defaultSelectedKeys={['/']} mode="vertical" items={menuItems} onClick={(i) => go(i.key)} />
|
||||
</Sider>
|
||||
<Layout className="chat-layout">
|
||||
<Content className="chat-container">
|
||||
<Routes />
|
||||
</Content>
|
||||
<Footer style={{ textAlign: 'center' }}>
|
||||
<a href="https://github.com/lencx/chatgpt" target="_blank">ChatGPT Desktop Application</a> ©2022 Created by lencx</Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatLayout;
|
||||
20
src/main.scss
vendored
Normal file
20
src/main.scss
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color: #2a2a2a;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
16
src/main.tsx
vendored
Normal file
16
src/main.tsx
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import { StrictMode, Suspense } from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import Layout from '@/layout';
|
||||
import './main.scss';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<StrictMode>
|
||||
<Suspense fallback={null}>
|
||||
<BrowserRouter>
|
||||
<Layout/>
|
||||
</BrowserRouter>
|
||||
</Suspense>
|
||||
</StrictMode>
|
||||
);
|
||||
44
src/routes.tsx
vendored
Normal file
44
src/routes.tsx
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useRoutes } from 'react-router-dom';
|
||||
import {
|
||||
DesktopOutlined,
|
||||
BulbOutlined
|
||||
} from '@ant-design/icons';
|
||||
import type { RouteObject } from 'react-router-dom';
|
||||
import type { MenuProps } from 'antd';
|
||||
|
||||
import General from '@view/General';
|
||||
import ChatGPTPrompts from '@view/ChatGPTPrompts';
|
||||
|
||||
export type ChatRouteObject = {
|
||||
label: string;
|
||||
icon?: React.ReactNode,
|
||||
};
|
||||
|
||||
export const routes: Array<RouteObject & { meta: ChatRouteObject }> = [
|
||||
{
|
||||
path: '/',
|
||||
element: <General />,
|
||||
meta: {
|
||||
label: 'General',
|
||||
icon: <DesktopOutlined />,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/chatgpt-prompts',
|
||||
element: <ChatGPTPrompts />,
|
||||
meta: {
|
||||
label: 'ChatGPT Prompts',
|
||||
icon: <BulbOutlined />,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
type MenuItem = Required<MenuProps>['items'][number];
|
||||
export const menuItems: MenuItem[] = routes.map(i => ({
|
||||
...i.meta,
|
||||
key: i.path || '',
|
||||
}));
|
||||
|
||||
export default () => {
|
||||
return useRoutes(routes);
|
||||
};
|
||||
7
src/view/ChatGPTPrompts.tsx
vendored
Normal file
7
src/view/ChatGPTPrompts.tsx
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<div>
|
||||
TODO: ChatGPT Prompts
|
||||
</div>
|
||||
)
|
||||
}
|
||||
102
src/view/General.tsx
vendored
Normal file
102
src/view/General.tsx
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Form, Radio, Switch, Input, Button, Space, message, Tooltip } from 'antd';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import { invoke } from '@tauri-apps/api';
|
||||
import { platform } from '@tauri-apps/api/os';
|
||||
import { ask } from '@tauri-apps/api/dialog';
|
||||
import { relaunch } from '@tauri-apps/api/process';
|
||||
import { clone, omit, isEqual } from 'lodash';
|
||||
|
||||
const OriginLabel = ({ url }: { url: string }) => {
|
||||
return (
|
||||
<span>
|
||||
Switch Origin <Tooltip title={`Default: ${url}`}><QuestionCircleOutlined /></Tooltip>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const disableAuto = {
|
||||
autoCapitalize: 'off',
|
||||
autoComplete: 'off',
|
||||
spellCheck: false
|
||||
}
|
||||
|
||||
export default function General() {
|
||||
const [form] = Form.useForm();
|
||||
const [platformInfo, setPlatform] = useState<string>('');
|
||||
const [chatConf, setChatConf] = useState<any>(null);
|
||||
|
||||
const init = async () => {
|
||||
setPlatform(await platform());
|
||||
const chatData = await invoke('get_chat_conf');
|
||||
setChatConf(chatData);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(clone(chatConf));
|
||||
}, [chatConf])
|
||||
|
||||
const onCancel = () => {
|
||||
form.setFieldsValue(chatConf);
|
||||
};
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
if (!isEqual(omit(chatConf, ['default_origin']), values)) {
|
||||
await invoke('form_confirm', { data: values, label: 'main' });
|
||||
const isOk = await ask(`Configuration saved successfully, whether to restart?`, {
|
||||
title: 'ChatGPT Preferences'
|
||||
});
|
||||
if (isOk) {
|
||||
relaunch();
|
||||
return;
|
||||
}
|
||||
message.success('Configuration saved successfully');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
style={{ maxWidth: 500 }}
|
||||
onFinish={onFinish}
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 15, offset: 1 }}
|
||||
>
|
||||
<Form.Item label="Theme" name="theme">
|
||||
<Radio.Group>
|
||||
<Radio value="Light">Light</Radio>
|
||||
<Radio value="Dark">Dark</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="Always on Top" name="always_on_top" valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
{platformInfo === 'darwin' && (
|
||||
<Form.Item label="Titlebar" name="titlebar" valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label={<OriginLabel url={chatConf?.default_origin} />} name="origin">
|
||||
<Input placeholder="https://chat.openai.com" {...disableAuto} />
|
||||
</Form.Item>
|
||||
<Form.Item label="User Agent (Window)" name="ua_window">
|
||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...disableAuto} placeholder="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" />
|
||||
</Form.Item>
|
||||
<Form.Item label="User Agent (SystemTray)" name="ua_tray">
|
||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...disableAuto} placeholder="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space size={20}>
|
||||
<Button onClick={onCancel}>Cancel</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@view/*": ["src/view/*"],
|
||||
"@comps/*": ["src/components/*"],
|
||||
"@layout/*": ["src/layout/*"],
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
9
tsconfig.node.json
Normal file
9
tsconfig.node.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
28
vite.config.ts
Normal file
28
vite.config.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [tsconfigPaths(), react()],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
// prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
},
|
||||
// to make use of `TAURI_DEBUG` and other env variables
|
||||
// https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
|
||||
envPrefix: ["VITE_", "TAURI_"],
|
||||
build: {
|
||||
// Tauri supports es2021
|
||||
target: ["es2021", "chrome100", "safari13"],
|
||||
// don't minify for debug builds
|
||||
minify: !process.env.TAURI_DEBUG ? "esbuild" : false,
|
||||
// produce sourcemaps for debug builds
|
||||
sourcemap: !!process.env.TAURI_DEBUG,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user