Compare commits

...

55 Commits

Author SHA1 Message Date
lencx
cd36360f0e v0.11.0 2023-02-16 23:54:37 +08:00
lencx
2398026795 readme 2023-02-16 23:53:56 +08:00
lencx
5426a711e9 feat: set window size (#405) 2023-02-16 22:02:12 +08:00
lencx
5ec9bb5656 chore: action 2023-02-15 23:55:08 +08:00
lencx
9cd30c158c Merge branch 'main' into dev 2023-02-15 21:37:20 +08:00
lencx
8c7e6b0818 Merge pull request #397 from targed/main 2023-02-15 14:12:39 +08:00
lencx
48a2a37fa2 Merge pull request #389 from cyhhao/main 2023-02-15 12:27:52 +08:00
targed
3a7da4b610 Update README.md 2023-02-14 17:34:37 -06:00
cyhhao
fde75aa81e format 2023-02-14 16:18:52 +08:00
cyhhao
95f8c16be0 Fix Chinese input method Enter on Safari 2023-02-14 16:10:44 +08:00
lencx
668d666377 Merge branch 'main' into dev 2023-02-13 16:05:11 +08:00
lencx
0871824093 Merge pull request #377 from lencx/doc 2023-02-13 13:49:32 +08:00
lencx
ce66cdbffb readme 2023-02-13 13:47:19 +08:00
lencx
56dae0017e Merge pull request #376 from lencx/doc 2023-02-13 13:19:34 +08:00
lencx
2e46cf4e63 readme 2023-02-13 13:18:39 +08:00
lencx
7c4913b499 readme 2023-02-13 13:12:35 +08:00
lencx
7730d626da readme 2023-02-13 13:11:52 +08:00
lencx
64706adec6 readme 2023-02-13 13:10:29 +08:00
lencx
7fe4a73b5e readme 2023-02-13 13:08:49 +08:00
lencx
7479ee905f fix: close (#349) 2023-02-12 23:33:23 +08:00
lencx
522f7b86b8 fix: save window state (#313) 2023-02-12 00:50:21 +08:00
lencx
4cba634d4a chore: casks 2023-02-12 00:33:51 +08:00
lencx
4a93573f4c Merge branch 'main' into dev 2023-02-12 00:21:13 +08:00
lencx
a67bd0468a chore: action 2023-02-12 00:20:42 +08:00
lencx
2d0cac6766 chore: download 2023-02-11 23:30:06 +08:00
lencx
7836c1263b Merge pull request #330 from cnyy7/patch-1 2023-02-07 22:56:20 +08:00
chennan
b403dea11b Update chatgpt.rb to version 0.10.3
checked locally:

brew reinstall --cask chatgpt --no-quarantine
==> Downloading https://github.com/lencx/ChatGPT/releases/download/v0.10.3/ChatGPT_0.10.3_x64.dmg
==> Downloading from https://objects.githubusercontent.com/github-production-release-asset-2e65be/575340621/b
######################################################################## 100.0%
Warning: Cannot verify integrity of 'a77703907b8af9fcb951d9e896b73c3424f60799b39b49a6b9a2d2e3fca259e3--ChatGPT_0.10.3_x64.dmg'.
No checksum was provided for this cask.
For your reference, the checksum is:
  sha256 "f44838a80844999191a303684fd7ae1811dd2fae6709aebe8bff23c70f9b8a10"
==> Uninstalling Cask chatgpt
==> Backing App 'ChatGPT.app' up to '/usr/local/Caskroom/chatgpt/0.6.10/ChatGPT.app'
==> Removing App '/Applications/ChatGPT.app'
==> Purging files for version 0.6.10 of Cask chatgpt
==> Installing Cask chatgpt
Warning: macOS's Gatekeeper has been disabled for this Cask
==> Moving App 'ChatGPT.app' to '/Applications/ChatGPT.app'
🍺  chatgpt was successfully installed!
2023-02-07 17:03:26 +08:00
lencx
62cf15d952 chore: action 2023-02-06 15:05:30 +08:00
lencx
d786e3a96c chore: action 2023-02-06 14:37:49 +08:00
lencx
28db6a72e5 chore: action 2023-02-06 14:22:58 +08:00
lencx
bc35164538 chore: action 2023-02-06 14:18:31 +08:00
lencx
c2482224a7 chore: action 2023-02-06 14:16:51 +08:00
lencx
8a0b85905d Merge pull request #310 from slithery0/patch-1 2023-02-05 13:04:37 +08:00
rukh
baf055388d Aur repo added, for git version of ChatGPT 2023-02-05 08:36:15 +05:45
lencx
c293f07b3c Merge pull request #301 from lencx/dev 2023-02-03 14:39:50 +08:00
lencx
c6efaec586 v0.10.3 2023-02-03 13:34:42 +08:00
lencx
01c6e11e8c release 2023-02-03 13:33:26 +08:00
lencx
70aac07f8e feat: markdown export (#233) 2023-02-03 12:22:15 +08:00
lencx
4847bb6fac chore: copy 2023-02-03 01:30:03 +08:00
lencx
041ffb7462 fix: conf error (#295) 2023-02-03 01:11:56 +08:00
lencx
93bb98e9c0 Merge pull request #292 from lencx/dev 2023-02-02 01:12:06 +08:00
lencx
11877a960d v0.10.2 2023-02-02 00:46:33 +08:00
lencx
a107e2b21e release 2023-02-02 00:46:23 +08:00
lencx
62924975a6 fix: export buttons (#286) 2023-02-02 00:39:30 +08:00
lencx
8aeca251e6 fix: shortcuts (#254) 2023-02-02 00:04:38 +08:00
lencx
272ef1cd37 feat: copy to clipboard (#191) 2023-02-01 23:48:35 +08:00
lencx
8eade8e9b9 chore: optim 2023-02-01 00:26:41 +08:00
lencx
171ac94f77 chore: optim 2023-01-30 23:55:43 +08:00
lencx
ffef57e934 fix: export buttons do not work (#274) 2023-01-29 23:25:23 +08:00
lencx
7e499b6b1e Merge pull request #266 from lencx/dev 2023-01-27 23:10:46 +08:00
lencx
a38804139a chore: log 2023-01-27 17:44:01 +08:00
lencx
a939b3442e add visitor 2023-01-27 11:52:10 +08:00
lencx
074afb58c8 Merge pull request #256 from lencx/dev 2023-01-27 00:26:38 +08:00
lencx
f9f173407e refactor: app conf 2023-01-27 00:23:05 +08:00
lencx
99877209a1 Merge pull request #250 from lencx/dev 2023-01-25 20:30:06 +08:00
36 changed files with 10284 additions and 660 deletions

View File

@@ -6,97 +6,192 @@ on:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
jobs:
create-release:
runs-on: ubuntu-20.04
outputs:
RELEASE_UPLOAD_ID: ${{ steps.create_release.outputs.id }}
steps:
- uses: actions/checkout@v2
- name: Query version number
id: get_version
shell: bash
run: |
echo "using version tag ${GITHUB_REF:10}"
echo "version=${GITHUB_REF:10}" >> $GITHUB_ENV
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: '${{ env.version }}'
release_name: 'ChatGPT ${{ env.version }}'
body: 'See the assets to download this version and install.'
build-tauri:
needs: create-release
build-chatgpt:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
platform: [macos-latest, ubuntu-20.04, windows-latest]
include:
- build: linux
os: ubuntu-latest
arch: x86_64
target: x86_64-unknown-linux-gnu
- build: macos
os: macos-latest
arch: x86_64
target: x86_64-apple-darwin
- buid: macos
os: macos-latest
arch: aarch64
target: aarch64-apple-darwin
- build: windows
os: windows-latest
arch: x86_64
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: setup node
- uses: pnpm/action-setup@v2
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: install Rust stable
- name: 'Setup Rust'
uses: dtolnay/rust-toolchain@stable
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-20.04'
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libayatana-appindicator3-dev librsvg2-dev patchelf
- name: Install app dependencies and build it
run: yarn && yarn build:fe
- name: Build FE
run: pnpm build:fe
# - name: Rewrite tauri.conf.json
# run: yarn fix:conf
- name: Install rust target
run: rustup target add ${{ matrix.target }}
- name: fix tray icon
if: matrix.platform != 'macos-latest'
if: matrix.os != 'macos-latest'
run: |
yarn fix:tray
pnpm fix:tray
- uses: tauri-apps/tauri-action@v0.3
- run: pnpm build --target ${{ matrix.target }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 📝: https://tauri.app/v1/guides/distribution/updater#signing-updates
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- uses: actions/upload-artifact@v3
if: matrix.os == 'ubuntu-latest'
with:
releaseId: ${{ needs.create-release.outputs.RELEASE_UPLOAD_ID }}
name: artifacts-${{ matrix.arch }}
path: |
./target/${{ matrix.target }}/release/bundle/appimage/**.AppImage.*
./target/${{ matrix.target }}/release/bundle/deb/**.deb
- uses: actions/upload-artifact@v3
if: matrix.os == 'macos-latest'
with:
name: artifacts-${{ matrix.arch }}
path: |
./target/${{ matrix.target }}/release/bundle/dmg/**.dmg
./target/${{ matrix.target }}/release/bundle/macos/**.app.*
- uses: actions/upload-artifact@v3
if: matrix.os == 'windows-latest'
with:
name: artifacts-${{ matrix.arch }}
path: |
./target/${{ matrix.target }}/release/bundle/msi/**
release-chatgpt:
needs: build-chatgpt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Query version number
run: echo "version=${GITHUB_REF:11}" >> $GITHUB_ENV
- name: Download x86_64 artifacts
uses: actions/download-artifact@v3
with:
name: artifacts-x86_64
path: artifacts/x86_64
- name: Download aarch64 artifacts
uses: actions/download-artifact@v3
with:
name: artifacts-aarch64
path: artifacts/aarch64
- name: Rename artifacts
run: |
mv "artifacts/aarch64/dmg/ChatGPT_${{ env.version }}_aarch64.dmg" "artifacts/ChatGPT_${{ env.version }}_macos_aarch64.dmg"
mv "artifacts/aarch64/macos/ChatGPT.app.tar.gz" "artifacts/ChatGPT_${{ env.version }}_macos_aarch64.app.tar.gz"
mv "artifacts/aarch64/macos/ChatGPT.app.tar.gz.sig" "artifacts/ChatGPT_${{ env.version }}_macos_aarch64.app.tar.gz.sig"
mv "artifacts/x86_64/dmg/ChatGPT_${{ env.version }}_x64.dmg" "artifacts/ChatGPT_${{ env.version }}_macos_x86_64.dmg"
mv "artifacts/x86_64/macos/ChatGPT.app.tar.gz" "artifacts/ChatGPT_${{ env.version }}_macos_x86_64.app.tar.gz"
mv "artifacts/x86_64/macos/ChatGPT.app.tar.gz.sig" "artifacts/ChatGPT_${{ env.version }}_macos_x86_64.app.tar.gz.sig"
mv "artifacts/x86_64/deb/chat-gpt_${{ env.version }}_amd64.deb" "artifacts/x86_64/deb/ChatGPT_${{ env.version }}_linux_x86_64.deb"
mv "artifacts/x86_64/appimage/chat-gpt_${{ env.version }}_amd64.AppImage.tar.gz" "artifacts/ChatGPT_${{ env.version }}_linux_x86_64.AppImage.tar.gz"
mv "artifacts/x86_64/appimage/chat-gpt_${{ env.version }}_amd64.AppImage.tar.gz.sig" "artifacts/ChatGPT_${{ env.version }}_linux_x86_64.AppImage.tar.gz.sig"
mv "artifacts/x86_64/ChatGPT_${{ env.version }}_x64_en-US.msi" "artifacts/ChatGPT_${{ env.version }}_windows_x86_64.msi"
mv "artifacts/x86_64/ChatGPT_${{ env.version }}_x64_en-US.msi.zip" "artifacts/ChatGPT_${{ env.version }}_windows_x86_64.msi.zip"
mv "artifacts/x86_64/ChatGPT_${{ env.version }}_x64_en-US.msi.zip.sig" "artifacts/ChatGPT_${{ env.version }}_windows_x86_64.msi.zip.sig"
- name: Create Release
uses: softprops/action-gh-release@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ env.version }}
name: ChatGPT v${{ env.version }}
body: See the assets to download this version and install.
prerelease: false
generate_release_notes: false
files: ./artifacts/**/*
updater:
runs-on: ubuntu-20.04
needs: [create-release, build-tauri]
runs-on: ubuntu-latest
needs: release-chatgpt
steps:
- uses: actions/checkout@v2
- run: yarn
- run: yarn updater --token=${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Updater JSON
run: pnpm updater --token=${{ secrets.GITHUB_TOKEN }}
- name: Deploy install.json
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# 📝: Edit the deployment directory
publish_dir: ./updater
force_orphan: true
# force_orphan: true
# publish-winget:
# # Action can only be run on windows
# runs-on: windows-latest
# needs: [create-release, build-tauri]
# steps:
# - uses: vedantmgoyal2009/winget-releaser@v1
# with:
# identifier: lencx.ChatGPT
# token: ${{ secrets.WINGET_TOKEN }}
# version: ${{ env.version }}
- name: Query version number
run: echo "version=${GITHUB_REF:11}" >> $GITHUB_ENV
publish-winget:
# Action can only be run on windows
runs-on: windows-latest
needs: [build-chatgpt, release-chatgpt]
steps:
- uses: vedantmgoyal2009/winget-releaser@v1
with:
identifier: lencx.ChatGPT
token: ${{ secrets.WINGET_TOKEN }}
version: ${{ github.event.release.tag_name }}

6
.gitignore vendored
View File

@@ -1,11 +1,11 @@
package-lock.json
node_modules/
yarn.lock
*.lock
.yarn/*
.pnp.*
# rust
target/
Cargo.lock
# Logs
logs

View File

@@ -1,26 +0,0 @@
# Awesome ChatGPT
- [Awesome ChatGPT Prompts](https://github.com/f/awesome-chatgpt-prompts) - This repo includes ChatGPT prompt curation to use ChatGPT better.
- [Awesome ChatGPT](https://github.com/humanloop/awesome-chatgpt) - Curated list of awesome tools, demos, docs for ChatGPT and GPT-3
## Extension
`Browser`
- [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`
- [ChatGPT Extension for VSCode](https://github.com/mpociot/chatgpt-vscode) - A VSCode extension that allows you to use ChatGPT
`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.

4589
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -20,11 +20,11 @@
- [📝 更新日志](./UPDATE_LOG.md)
- [🕒 历史版本...](https://github.com/lencx/ChatGPT/releases)
<!-- download start -->
<!-- tr-download-start -->
### Windows
- [ChatGPT_0.10.1_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/ChatGPT_0.10.1_x64_en-US.msi):
- [ChatGPT_0.11.0_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/ChatGPT_0.11.0_x64_en-US.msi):
- 使用 [winget](https://winstall.app/apps/lencx.ChatGPT):
```bash
@@ -35,12 +35,12 @@
winget install --id=lencx.ChatGPT -e --version 0.10.0
```
**注意:如果安装路径和应用名称相同,会导致冲突 ([#142](https://github.com/lencx/ChatGPT/issues/142#issuecomment-0.10.1))**
**注意:如果安装路径和应用名称相同,会导致冲突 ([#142](https://github.com/lencx/ChatGPT/issues/142#issuecomment-0.11.0))**
### Mac
- [ChatGPT_0.10.1_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/ChatGPT_0.10.1_x64.dmg)
- [ChatGPT.app.tar.gz](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/ChatGPT.app.tar.gz)
- [ChatGPT_0.11.0_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/ChatGPT_0.11.0_x64.dmg)
- [ChatGPT.app.tar.gz](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/ChatGPT.app.tar.gz)
- Homebrew \
_[Homebrew 快捷安装](https://brew.sh) ([Cask](https://docs.brew.sh/Cask-Cookbook)):_
```sh
@@ -56,14 +56,14 @@
### Linux
- [chat-gpt_0.10.1_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/chat-gpt_0.10.1_amd64.deb)
- [chat-gpt_0.10.1_amd64.AppImage](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/chat-gpt_0.10.1_amd64.AppImage): **工作可靠,`.deb` 运行失败时可以尝试它**
- [chat-gpt_0.11.0_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/chat-gpt_0.11.0_amd64.deb)
- [chat-gpt_0.11.0_amd64.AppImage](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/chat-gpt_0.11.0_amd64.AppImage): **工作可靠,`.deb` 运行失败时可以尝试它**
- 使用 [AUR](https://aur.archlinux.org/packages/chatgpt-desktop-bin):
```bash
yay -S chatgpt-desktop-bin
```
<!-- download end -->
<!-- tr-download-end -->
## 📢 公告
@@ -248,6 +248,8 @@ yarn build
<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">
<a href="https://t.zsxq.com/0bQikmcVw"><img width="360" src="./assets/zsxq.png"></a>
## License
Apache License

View File

@@ -7,6 +7,7 @@
[![English badge](https://img.shields.io/badge/%E8%8B%B1%E6%96%87-English-blue)](./README.md)
[![简体中文 badge](https://img.shields.io/badge/%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87-Simplified%20Chinese-blue)](./README-ZH_CN.md)\
![License](https://img.shields.io/badge/License-Apache%202-green.svg)
![visitor](https://visitor-badge.glitch.me/badge?page_id=lencx.chatgpt)
[![ChatGPT downloads](https://img.shields.io/github/downloads/lencx/ChatGPT/total.svg?style=flat-square)](https://github.com/lencx/ChatGPT/releases)
[![chat](https://img.shields.io/badge/chat-discord-blue?style=flat&logo=discord)](https://discord.gg/aPhCRf4zZr)
[![lencx](https://img.shields.io/badge/follow-lencx__-blue?style=flat&logo=Twitter)](https://twitter.com/lencx_)
@@ -22,11 +23,11 @@
- [📝 Update Log](./UPDATE_LOG.md)
- [🕒 History versions...](https://github.com/lencx/ChatGPT/releases)
<!-- download start -->
<!-- tr-download-start -->
### Windows
- [ChatGPT_0.10.1_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/ChatGPT_0.10.1_x64_en-US.msi): Direct download installer
- [ChatGPT_0.11.0_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/ChatGPT_0.11.0_x64_en-US.msi): Direct download installer
- Use [winget](https://winstall.app/apps/lencx.ChatGPT):
```bash
@@ -37,12 +38,19 @@
winget install --id=lencx.ChatGPT -e --version 0.10.0
```
**Note: If the installation path and application name are the same, it will lead to conflict ([#142](https://github.com/lencx/ChatGPT/issues/142#issuecomment-0.10.1))**
- Use [Chocolatey](https://community.chocolatey.org/packages/chatgpt/0.11.0#versionhistory):
```bash
# install the package
choco install chatgpt -y
```
**Note: If the installation path and application name are the same, it will lead to conflict ([#142](https://github.com/lencx/ChatGPT/issues/142#issuecomment-0.11.0))**
### Mac
- [ChatGPT_0.10.1_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/ChatGPT_0.10.1_x64.dmg): Direct download installer
- [ChatGPT.app.tar.gz](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/ChatGPT.app.tar.gz): Download the `.app` installer
- [ChatGPT_0.11.0_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/ChatGPT_0.11.0_x64.dmg): Direct download installer
- [ChatGPT.app.tar.gz](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/ChatGPT.app.tar.gz): Download the `.app` installer
- Homebrew \
Or you can install with _[Homebrew](https://brew.sh) ([Cask](https://docs.brew.sh/Cask-Cookbook)):_
```sh
@@ -58,11 +66,13 @@
### Linux
- [chat-gpt_0.10.1_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/chat-gpt_0.10.1_amd64.deb): Download `.deb` installer, advantage small size, disadvantage poor compatibility
- [chat-gpt_0.10.1_amd64.AppImage](https://github.com/lencx/ChatGPT/releases/download/v0.10.1/chat-gpt_0.10.1_amd64.AppImage): Works reliably, you can try it if `.deb` fails to run
- [chat-gpt_0.11.0_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/chat-gpt_0.11.0_amd64.deb): Download `.deb` installer, advantage small size, disadvantage poor compatibility
- [chat-gpt_0.11.0_amd64.AppImage](https://github.com/lencx/ChatGPT/releases/download/v0.11.0/chat-gpt_0.11.0_amd64.AppImage): Works reliably, you can try it if `.deb` fails to run
- Available on [AUR](https://aur.archlinux.org/packages/chatgpt-desktop-bin) with the package name `chatgpt-desktop-bin`, and you can use your favourite AUR package manager to install it.
- Also, [Aur](https://aur.archlinux.org/packages/chatgpt-desktop-git) available with the package name `chatgpt-desktop-git`.
<!-- chatgpt-desktop-git always builds the latest commits of main branch -->
<!-- download end -->
<!-- tr-download-end -->
## 📢 Announcement
@@ -257,6 +267,8 @@ yarn build
<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">
<a href="https://t.zsxq.com/0bQikmcVw"><img width="360" src="./assets/zsxq.png"></a>
## License
Apache License

View File

@@ -1,5 +1,45 @@
# UPDATE LOG
## v0.11.0
Fix:
- User-defined close button behavior (exit or minimize) (`Control Center -> Settings -> Main Window -> Close Exit`). (https://github.com/lencx/ChatGPT/issues/359)
- Markdown content layout (https://github.com/lencx/ChatGPT/issues/378)
Feat:
- Set the main window and tray window size (https://github.com/lencx/ChatGPT/issues/405)
- Save window positions and sizes and restore them when the app is reopened (`Control Center -> Settings -> General -> Save Window State`)
- macOS support for aarch64 installer (https://github.com/lencx/ChatGPT/issues/380)
## v0.10.3
> Note: As of now the ChatGPT desktop app has added a lot of exciting features and it continues to improve, as the app grows it has gone far beyond what ChatGPT was intended for. I want to make it the ultimate goal that any website can be easily wrapped to the desktop through user customization. So it needed an international user guide to guide users to use it more professionally. And https://app.nofwl.com is the manual for the app, which will be built into the app (`Menu -> Window -> ChatGPT User's Guide`) so you can access it anytime. It's just starting at the moment, so stay tuned.
Fix:
- Incompatible configuration data causes program crashes (https://github.com/lencx/ChatGPT/issues/295)
Feat:
- Silent copy text
- Markdown export support distinguishes between users and bots (https://github.com/lencx/ChatGPT/issues/233)
## v0.10.2
Fix:
- PNG and PDF buttons do not work (https://github.com/lencx/ChatGPT/issues/274)
- Change the window size and the Send button is obscured by the Export button (https://github.com/lencx/ChatGPT/issues/286)
- Change forward and backward shortcuts (https://github.com/lencx/ChatGPT/issues/254)
- MacOS: `Cmd [`, `Cmd ]`
- Windows and Linux: `Ctrl [`, `Ctrl ]`
Feat:
- Copy a single record to the clipboard (https://github.com/lencx/ChatGPT/issues/191)
## v0.10.1
Fix:

BIN
assets/zsxq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,8 +1,8 @@
cask "chatgpt" do
version "0.6.10"
sha256 "e85062565f826d32219c53b184d6df9c89441d4231cdfff775c2de8c50ac9906"
version "0.10.3"
sha256 "f44838a80844999191a303684fd7ae1811dd2fae6709aebe8bff23c70f9b8a10"
url "https://github.com/lencx/ChatGPT/releases/download/v#{version}/ChatGPT_#{version}_x64.dmg"
url "https://github.com/lencx/ChatGPT/releases/download/v#{version}/ChatGPT_#{version}_macos_x86_64.dmg"
name "ChatGPT"
desc "Desktop wrapper for OpenAI ChatGPT"
homepage "https://github.com/lencx/ChatGPT#readme"

View File

@@ -1,3 +0,0 @@
# ChatGPT Model
- [Awesome ChatGPT Prompts](https://github.com/f/awesome-chatgpt-prompts)

View File

@@ -4,14 +4,14 @@
"scripts": {
"dev:fe": "vite",
"build:fe": "tsc && vite build",
"dev": "yarn tauri dev",
"build": "yarn tauri build",
"dev": "tauri dev",
"build": "tauri build",
"updater": "tr updater",
"release": "tr release --git",
"fix:conf": "tr override --json.tauri_updater_active=false",
"fix:tray": "tr override --json.tauri_systemTray_iconPath=\"icons/tray-icon-light.png\" --json.tauri_systemTray_iconAsTemplate=false",
"fix:tray:mac": "tr override --json.tauri_systemTray_iconPath=\"icons/tray-icon.png\" --json.tauri_systemTray_iconAsTemplate=true",
"download": "node ./scripts/download.js",
"download": "tr download --mdfile=README.md,README-ZH_CN.md --f1=38 --f2=35",
"fmt:rs": "cargo fmt",
"tr": "tr",
"tauri": "tauri",
@@ -45,6 +45,7 @@
"dayjs": "^1.11.7",
"github-markdown-css": "^5.1.0",
"lodash": "^4.17.21",
"monaco-editor": "^0.34.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^8.0.4",
@@ -58,7 +59,7 @@
},
"devDependencies": {
"@tauri-apps/cli": "^1.2.2",
"@tauri-release/cli": "^0.2.3",
"@tauri-release/cli": "^0.2.5",
"@types/lodash": "^4.14.191",
"@types/node": "^18.7.10",
"@types/react": "^18.0.15",

4764
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

33
scripts/download.js vendored
View File

@@ -1,33 +0,0 @@
const fs = require('fs');
const argv = process.argv.slice(2);
async function rewrite(filename) {
const content = fs.readFileSync(filename, 'utf8').split('\n');
const startRe = /<!-- download start -->/;
const endRe = /<!-- download end -->/;
let flag = false;
for (let i = 0; i < content.length; i++) {
if (startRe.test(content[i])) {
flag = true;
}
if (flag) {
if (!/winget install --id=lencx.ChatGPT -e --version/.test(content[i])) {
content[i] = content[i].replace(/(\d+).(\d+).(\d+)/g, argv[0]);
}
}
if (endRe.test(content[i])) {
break;
}
}
fs.writeFileSync(filename, content.join('\n'), 'utf8');
}
async function init() {
rewrite('README.md');
rewrite('README-ZH_CN.md');
}
init().catch(console.error);

View File

@@ -30,6 +30,7 @@ tauri = { version = "1.2.4", features = ["api-all", "devtools", "global-shortcut
tauri-plugin-positioner = { version = "1.0.4", features = ["system-tray"] }
tauri-plugin-log = { git = "https://github.com/lencx/tauri-plugins-workspace", branch = "dev", features = ["colored"] }
tauri-plugin-autostart = { git = "https://github.com/lencx/tauri-plugins-workspace", branch = "dev" }
tauri-plugin-window-state = { git = "https://github.com/lencx/tauri-plugins-workspace", branch = "dev" }
# sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite"] }

View File

@@ -1,10 +1,7 @@
use crate::{
conf::ChatConfJson,
utils::{self, chat_root, create_file},
};
use log::info;
use crate::utils;
use log::error;
use std::{fs, path::PathBuf};
use tauri::{api, command, AppHandle, Manager, Theme};
use tauri::{api, command, AppHandle, Manager};
#[command]
pub fn drag_window(app: AppHandle) {
@@ -22,19 +19,29 @@ pub fn fullscreen(app: AppHandle) {
}
#[command]
pub fn download(_app: AppHandle, name: String, blob: Vec<u8>) {
let path = chat_root().join(PathBuf::from(name));
create_file(&path).unwrap();
pub fn download(app: AppHandle, name: String, blob: Vec<u8>) {
let win = app.app_handle().get_window("core");
let path = utils::app_root().join(PathBuf::from(name));
utils::create_file(&path).unwrap();
fs::write(&path, blob).unwrap();
utils::open_file(path);
tauri::api::dialog::message(
win.as_ref(),
"Save File",
format!("PATH: {}", path.display()),
);
}
#[command]
pub fn save_file(_app: AppHandle, name: String, content: String) {
let path = chat_root().join(PathBuf::from(name));
create_file(&path).unwrap();
pub fn save_file(app: AppHandle, name: String, content: String) {
let win = app.app_handle().get_window("core");
let path = utils::app_root().join(PathBuf::from(name));
utils::create_file(&path).unwrap();
fs::write(&path, content).unwrap();
utils::open_file(path);
tauri::api::dialog::message(
win.as_ref(),
"Save File",
format!("PATH: {}", path.display()),
);
}
#[command]
@@ -42,52 +49,11 @@ pub fn open_link(app: AppHandle, url: String) {
api::shell::open(&app.shell_scope(), url, None).unwrap();
}
#[command]
pub fn get_chat_conf() -> ChatConfJson {
ChatConfJson::get_chat_conf()
}
#[command]
pub fn reset_chat_conf() -> ChatConfJson {
ChatConfJson::reset_chat_conf()
}
#[command]
pub fn get_theme() -> String {
ChatConfJson::theme().unwrap_or(Theme::Light).to_string()
}
#[command]
pub fn run_check_update(app: AppHandle, silent: bool, has_msg: Option<bool>) {
utils::run_check_update(app, silent, has_msg);
}
#[command]
pub fn form_confirm(_app: AppHandle, data: serde_json::Value) {
ChatConfJson::amend(&serde_json::json!(data), None).unwrap();
}
#[command]
pub fn form_cancel(app: AppHandle, label: &str, title: &str, msg: &str) {
let win = app.app_handle().get_window(label).unwrap();
tauri::api::dialog::ask(
app.app_handle().get_window(label).as_ref(),
title,
msg,
move |is_cancel| {
if is_cancel {
win.close().unwrap();
}
},
);
}
#[command]
pub fn form_msg(app: AppHandle, label: &str, title: &str, msg: &str) {
let win = app.app_handle().get_window(label);
tauri::api::dialog::message(win.as_ref(), title, msg);
}
#[command]
pub fn open_file(path: PathBuf) {
utils::open_file(path);
@@ -102,7 +68,7 @@ pub async fn get_data(app: AppHandle, url: String, is_msg: Option<bool>) -> Opti
utils::get_data(&url, None).await
};
res.unwrap_or_else(|err| {
info!("chatgpt_client_http_error: {}", err);
error!("chatgpt_client_http: {}", err);
None
})
}

View File

@@ -1,9 +1,9 @@
use crate::{
app::{fs_extra, window},
conf::GITHUB_PROMPTS_CSV_URL,
utils::{self, chat_root},
utils,
};
use log::info;
use log::{error, info};
use regex::Regex;
use std::{collections::HashMap, fs, path::PathBuf, vec};
use tauri::{api, command, AppHandle, Manager};
@@ -11,7 +11,7 @@ use walkdir::WalkDir;
#[command]
pub fn get_chat_model_cmd() -> serde_json::Value {
let path = utils::chat_root().join("chat.model.cmd.json");
let path = utils::app_root().join("chat.model.cmd.json");
let content = fs::read_to_string(path).unwrap_or_else(|_| r#"{"data":[]}"#.to_string());
serde_json::from_str(&content).unwrap()
}
@@ -29,7 +29,7 @@ pub fn parse_prompt(data: String) -> Vec<PromptRecord> {
let mut list = vec![];
for result in rdr.deserialize() {
let record: PromptRecord = result.unwrap_or_else(|err| {
info!("parse_prompt_error: {}", err);
error!("parse_prompt: {}", err);
PromptRecord {
cmd: None,
act: "".to_string(),
@@ -55,7 +55,7 @@ pub struct ModelRecord {
#[command]
pub fn cmd_list() -> Vec<ModelRecord> {
let mut list = vec![];
for entry in WalkDir::new(utils::chat_root().join("cache_model"))
for entry in WalkDir::new(utils::app_root().join("cache_model"))
.into_iter()
.filter_map(|e| e.ok())
{
@@ -82,14 +82,14 @@ pub struct FileMetadata {
#[tauri::command]
pub fn get_download_list(pathname: &str) -> (Vec<serde_json::Value>, PathBuf) {
info!("get_download_list: {}", pathname);
let download_path = chat_root().join(PathBuf::from(pathname));
let download_path = utils::app_root().join(PathBuf::from(pathname));
let content = fs::read_to_string(&download_path).unwrap_or_else(|err| {
info!("download_list_error: {}", err);
error!("download_list: {}", err);
fs::write(&download_path, "[]").unwrap();
"[]".to_string()
});
let list = serde_json::from_str::<Vec<serde_json::Value>>(&content).unwrap_or_else(|err| {
info!("download_list_parse_error: {}", err);
error!("download_list_parse: {}", err);
vec![]
});
@@ -104,7 +104,7 @@ pub fn download_list(pathname: &str, dir: &str, filename: Option<String>, id: Op
let mut idmap = HashMap::new();
utils::vec_to_hashmap(data.0.into_iter(), "id", &mut idmap);
for entry in WalkDir::new(utils::chat_root().join(dir))
for entry in WalkDir::new(utils::app_root().join(dir))
.into_iter()
.filter_entry(|e| !utils::is_hidden(e))
.filter_map(|e| e.ok())
@@ -182,9 +182,9 @@ pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>>
let data2 = data.clone();
let model = utils::chat_root().join("chat.model.json");
let model_cmd = utils::chat_root().join("chat.model.cmd.json");
let chatgpt_prompts = utils::chat_root()
let model = utils::app_root().join("chat.model.json");
let model_cmd = utils::app_root().join("chat.model.cmd.json");
let chatgpt_prompts = utils::app_root()
.join("cache_model")
.join("chatgpt_prompts.json");
@@ -238,8 +238,8 @@ pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>>
"Sync Prompts",
"ChatGPT Prompts data has been synchronized!",
);
window::window_reload(app.clone(), "core");
window::window_reload(app, "tray");
window::cmd::window_reload(app.clone(), "core");
window::cmd::window_reload(app, "tray");
return Some(data2);
}
@@ -249,13 +249,12 @@ pub async fn sync_prompts(app: AppHandle, time: u64) -> Option<Vec<ModelRecord>>
#[command]
pub async fn sync_user_prompts(url: String, data_type: String) -> Option<Vec<ModelRecord>> {
info!("sync_user_prompts: url => {}", url);
let res = utils::get_data(&url, None).await.unwrap_or_else(|err| {
info!("chatgpt_http_error: {}", err);
error!("chatgpt_http: {}", err);
None
});
info!("chatgpt_http_url: {}", url);
if let Some(v) = res {
let data;
if data_type == "csv" {
@@ -264,11 +263,11 @@ pub async fn sync_user_prompts(url: String, data_type: String) -> Option<Vec<Mod
} else if data_type == "json" {
info!("chatgpt_http_json_parse");
data = serde_json::from_str(&v).unwrap_or_else(|err| {
info!("chatgpt_http_json_parse_error: {}", err);
error!("chatgpt_http_json_parse: {}", err);
vec![]
});
} else {
info!("chatgpt_http_unknown_type");
error!("chatgpt_http_unknown_type");
data = vec![];
}

View File

@@ -1,6 +1,6 @@
use crate::{
app::window,
conf::{self, ChatConfJson},
conf::{self, AppConf},
utils,
};
use tauri::{
@@ -14,7 +14,7 @@ use tauri::AboutMetadata;
// --- Menu
pub fn init() -> Menu {
let chat_conf = ChatConfJson::get_chat_conf();
let app_conf = AppConf::read();
let name = "ChatGPT";
let app_menu = Submenu::new(
name,
@@ -35,7 +35,7 @@ pub fn init() -> Menu {
let stay_on_top =
CustomMenuItem::new("stay_on_top".to_string(), "Stay On Top").accelerator("CmdOrCtrl+T");
let stay_on_top_menu = if chat_conf.stay_on_top {
let stay_on_top_menu = if app_conf.stay_on_top {
stay_on_top.selected()
} else {
stay_on_top
@@ -44,15 +44,15 @@ pub fn init() -> Menu {
let theme_light = CustomMenuItem::new("theme_light".to_string(), "Light");
let theme_dark = CustomMenuItem::new("theme_dark".to_string(), "Dark");
let theme_system = CustomMenuItem::new("theme_system".to_string(), "System");
let is_dark = chat_conf.theme == "Dark";
let is_system = chat_conf.theme == "System";
let is_dark = app_conf.clone().theme_check("dark");
let is_system = app_conf.clone().theme_check("system");
let update_prompt = CustomMenuItem::new("update_prompt".to_string(), "Prompt");
let update_silent = CustomMenuItem::new("update_silent".to_string(), "Silent");
let _update_disable = CustomMenuItem::new("update_disable".to_string(), "Disable");
// let _update_disable = CustomMenuItem::new("update_disable".to_string(), "Disable");
let popup_search = CustomMenuItem::new("popup_search".to_string(), "Pop-up Search");
let popup_search_menu = if chat_conf.popup_search {
let popup_search_menu = if app_conf.popup_search {
popup_search.selected()
} else {
popup_search
@@ -61,19 +61,20 @@ pub fn init() -> Menu {
#[cfg(target_os = "macos")]
let titlebar = CustomMenuItem::new("titlebar".to_string(), "Titlebar").accelerator("CmdOrCtrl+B");
#[cfg(target_os = "macos")]
let titlebar_menu = if chat_conf.titlebar {
let titlebar_menu = if app_conf.titlebar {
titlebar.selected()
} else {
titlebar
};
let system_tray = CustomMenuItem::new("system_tray".to_string(), "System Tray");
let system_tray_menu = if chat_conf.tray {
let system_tray_menu = if app_conf.tray {
system_tray.selected()
} else {
system_tray
};
let auto_update = app_conf.get_auto_update();
let preferences_menu = Submenu::new(
"Preferences",
Menu::with_items([
@@ -114,16 +115,16 @@ pub fn init() -> Menu {
Submenu::new(
"Auto Update",
Menu::new()
.add_item(if chat_conf.auto_update == "Prompt" {
.add_item(if auto_update == "prompt" {
update_prompt.selected()
} else {
update_prompt
})
.add_item(if chat_conf.auto_update == "Silent" {
.add_item(if auto_update == "silent" {
update_silent.selected()
} else {
update_silent
}), // .add_item(if chat_conf.auto_update == "Disable" {
}), // .add_item(if auto_update == "disable" {
// update_disable.selected()
// } else {
// update_disable
@@ -142,7 +143,6 @@ pub fn init() -> Menu {
.into(),
CustomMenuItem::new("clear_conf".to_string(), "Clear Config").into(),
MenuItem::Separator.into(),
CustomMenuItem::new("awesome".to_string(), "Awesome ChatGPT").into(),
CustomMenuItem::new("buy_coffee".to_string(), "Buy lencx a coffee").into(),
]),
);
@@ -162,9 +162,9 @@ pub fn init() -> Menu {
let view_menu = Submenu::new(
"View",
Menu::new()
.add_item(CustomMenuItem::new("go_back".to_string(), "Go Back").accelerator("CmdOrCtrl+Left"))
.add_item(CustomMenuItem::new("go_back".to_string(), "Go Back").accelerator("CmdOrCtrl+["))
.add_item(
CustomMenuItem::new("go_forward".to_string(), "Go Forward").accelerator("CmdOrCtrl+Right"),
CustomMenuItem::new("go_forward".to_string(), "Go Forward").accelerator("CmdOrCtrl+]"),
)
.add_item(
CustomMenuItem::new("scroll_top".to_string(), "Scroll to Top of Screen")
@@ -189,6 +189,10 @@ pub fn init() -> Menu {
let window_menu = Submenu::new(
"Window",
Menu::new()
.add_item(CustomMenuItem::new(
"app_website".to_string(),
"ChatGPT User's Guide",
))
.add_item(CustomMenuItem::new("dalle2".to_string(), "DALL·E 2"))
.add_native_item(MenuItem::Separator)
.add_native_item(MenuItem::Minimize)
@@ -241,23 +245,31 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
utils::run_check_update(app, false, None);
}
// Preferences
"control_center" => window::control_window(app),
"control_center" => window::cmd::control_window(app),
"restart" => tauri::api::process::restart(&app.env()),
"inject_script" => open(&app, script_path),
"go_conf" => utils::open_file(utils::chat_root()),
"go_conf" => utils::open_file(utils::app_root()),
"clear_conf" => utils::clear_conf(&app),
"awesome" => open(&app, conf::AWESOME_URL.to_string()),
"app_website" => window::cmd::wa_window(
app,
"app_website".into(),
"ChatGPT User's Guide".into(),
conf::APP_WEBSITE.into(),
None,
),
"buy_coffee" => open(&app, conf::BUY_COFFEE.to_string()),
"popup_search" => {
let chat_conf = conf::ChatConfJson::get_chat_conf();
let popup_search = !chat_conf.popup_search;
let app_conf = AppConf::read();
let popup_search = !app_conf.popup_search;
menu_handle
.get_item(menu_id)
.set_selected(popup_search)
.unwrap();
ChatConfJson::amend(&serde_json::json!({ "popup_search": popup_search }), None).unwrap();
window::window_reload(app.clone(), "core");
window::window_reload(app, "tray");
app_conf
.amend(serde_json::json!({ "popup_search": popup_search }))
.write();
window::cmd::window_reload(app.clone(), "core");
window::cmd::window_reload(app, "tray");
}
"sync_prompts" => {
tauri::api::dialog::ask(
@@ -276,29 +288,37 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
);
}
"hide_dock_icon" => {
ChatConfJson::amend(&serde_json::json!({ "hide_dock_icon": true }), Some(app)).unwrap()
AppConf::read()
.amend(serde_json::json!({ "hide_dock_icon": true }))
.write()
.restart(app);
}
"titlebar" => {
let chat_conf = conf::ChatConfJson::get_chat_conf();
ChatConfJson::amend(
&serde_json::json!({ "titlebar": !chat_conf.titlebar }),
None,
)
.unwrap();
tauri::api::process::restart(&app.env());
let app_conf = AppConf::read();
app_conf
.clone()
.amend(serde_json::json!({ "titlebar": !app_conf.titlebar }))
.write()
.restart(app);
}
"system_tray" => {
let chat_conf = conf::ChatConfJson::get_chat_conf();
ChatConfJson::amend(&serde_json::json!({ "tray": !chat_conf.tray }), None).unwrap();
tauri::api::process::restart(&app.env());
let app_conf = AppConf::read();
app_conf
.clone()
.amend(serde_json::json!({ "tray": !app_conf.tray }))
.write()
.restart(app);
}
"theme_light" | "theme_dark" | "theme_system" => {
let theme = match menu_id {
"theme_dark" => "Dark",
"theme_system" => "System",
_ => "Light",
"theme_dark" => "dark",
"theme_system" => "system",
_ => "light",
};
ChatConfJson::amend(&serde_json::json!({ "theme": theme }), Some(app)).unwrap();
AppConf::read()
.amend(serde_json::json!({ "theme": theme }))
.write()
.restart(app);
}
"update_prompt" | "update_silent" | "update_disable" => {
// for id in ["update_prompt", "update_silent", "update_disable"] {
@@ -311,34 +331,38 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
.get_item("update_silent")
.set_selected(true)
.unwrap();
"Silent"
"silent"
}
"update_disable" => {
menu_handle
.get_item("update_disable")
.set_selected(true)
.unwrap();
"Disable"
"disable"
}
_ => {
menu_handle
.get_item("update_prompt")
.set_selected(true)
.unwrap();
"Prompt"
"prompt"
}
};
ChatConfJson::amend(&serde_json::json!({ "auto_update": auto_update }), None).unwrap();
AppConf::read()
.amend(serde_json::json!({ "auto_update": auto_update }))
.write();
}
"stay_on_top" => {
let chat_conf = conf::ChatConfJson::get_chat_conf();
let stay_on_top = !chat_conf.stay_on_top;
let app_conf = AppConf::read();
let stay_on_top = !app_conf.stay_on_top;
menu_handle
.get_item(menu_id)
.set_selected(stay_on_top)
.unwrap();
win.set_always_on_top(stay_on_top).unwrap();
ChatConfJson::amend(&serde_json::json!({ "stay_on_top": stay_on_top }), None).unwrap();
app_conf
.amend(serde_json::json!({ "stay_on_top": stay_on_top }))
.write();
}
// Window
"dalle2" => window::dalle2_window(&app, None, None, Some(false)),
@@ -367,7 +391,7 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
)
.unwrap(),
// Help
"chatgpt_log" => utils::open_file(utils::chat_root().join("chatgpt.log")),
"chatgpt_log" => utils::open_file(utils::app_root().join("chatgpt.log")),
"update_log" => open(&app, conf::UPDATE_LOG_URL.to_string()),
"report_bug" => open(&app, conf::ISSUES_URL.to_string()),
"dev_tools" => {
@@ -381,22 +405,29 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
// --- SystemTray Menu
pub fn tray_menu() -> SystemTray {
if cfg!(target_os = "macos") {
SystemTray::new().with_menu(
SystemTrayMenu::new()
.add_item(CustomMenuItem::new(
"control_center".to_string(),
"Control Center",
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new(
"show_dock_icon".to_string(),
"Show Dock Icon",
))
let mut tray_menu = SystemTrayMenu::new()
.add_item(CustomMenuItem::new(
"control_center".to_string(),
"Control Center",
))
.add_native_item(SystemTrayMenuItem::Separator);
if AppConf::read().hide_dock_icon {
tray_menu = tray_menu.add_item(CustomMenuItem::new(
"show_dock_icon".to_string(),
"Show Dock Icon",
));
} else {
tray_menu = tray_menu
.add_item(CustomMenuItem::new(
"hide_dock_icon".to_string(),
"Hide Dock Icon",
))
.add_item(CustomMenuItem::new("show_core".to_string(), "Show ChatGPT"))
.add_item(CustomMenuItem::new("show_core".to_string(), "Show ChatGPT"));
}
SystemTray::new().with_menu(
tray_menu
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("quit".to_string(), "Quit ChatGPT")),
)
@@ -422,42 +453,51 @@ pub fn tray_handler(handle: &AppHandle, event: SystemTrayEvent) {
match event {
SystemTrayEvent::LeftClick { .. } => {
let chat_conf = conf::ChatConfJson::get_chat_conf();
let app_conf = AppConf::read();
if !chat_conf.hide_dock_icon {
let core_win = handle.get_window("core").unwrap();
core_win.minimize().unwrap();
if !app_conf.hide_dock_icon {
if let Some(core_win) = handle.get_window("core") {
core_win.minimize().unwrap();
}
}
let tray_win = handle.get_window("tray").unwrap();
tray_win.move_window(Position::TrayCenter).unwrap();
if let Some(tray_win) = handle.get_window("tray") {
tray_win.move_window(Position::TrayCenter).unwrap();
if tray_win.is_visible().unwrap() {
tray_win.hide().unwrap();
} else {
tray_win.show().unwrap();
if tray_win.is_visible().unwrap() {
tray_win.hide().unwrap();
} else {
tray_win.show().unwrap();
}
}
}
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
"control_center" => window::control_window(app),
"control_center" => window::cmd::control_window(app),
"restart" => tauri::api::process::restart(&handle.env()),
"show_dock_icon" => {
ChatConfJson::amend(&serde_json::json!({ "hide_dock_icon": false }), Some(app)).unwrap();
AppConf::read()
.amend(serde_json::json!({ "hide_dock_icon": false }))
.write()
.restart(app);
}
"hide_dock_icon" => {
let chat_conf = conf::ChatConfJson::get_chat_conf();
if !chat_conf.hide_dock_icon {
ChatConfJson::amend(&serde_json::json!({ "hide_dock_icon": true }), Some(app)).unwrap();
let app_conf = AppConf::read();
if !app_conf.hide_dock_icon {
app_conf
.amend(serde_json::json!({ "hide_dock_icon": true }))
.write()
.restart(app);
}
}
"show_core" => {
let core_win = app.get_window("core").unwrap();
let tray_win = app.get_window("tray").unwrap();
if !core_win.is_visible().unwrap() {
core_win.show().unwrap();
core_win.set_focus().unwrap();
tray_win.hide().unwrap();
}
if let Some(core_win) = app.get_window("core") {
let tray_win = app.get_window("tray").unwrap();
if !core_win.is_visible().unwrap() {
core_win.show().unwrap();
core_win.set_focus().unwrap();
tray_win.hide().unwrap();
}
};
}
"quit" => std::process::exit(0),
_ => (),

View File

@@ -1,20 +1,21 @@
use crate::{app::window, conf::ChatConfJson, utils};
use log::info;
use crate::{app::window, conf::AppConf, utils};
use log::{error, info};
use tauri::{utils::config::WindowUrl, window::WindowBuilder, App, GlobalShortcutManager, Manager};
use wry::application::accelerator::Accelerator;
pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>> {
info!("stepup");
let chat_conf = ChatConfJson::get_chat_conf();
let url = chat_conf.main_origin.to_string();
let theme = ChatConfJson::theme();
let app_conf = AppConf::read();
let url = app_conf.main_origin.to_string();
let theme = AppConf::theme_mode();
let handle = app.app_handle();
tauri::async_runtime::spawn(async move {
info!("stepup_tray");
window::tray_window(&handle);
});
if let Some(v) = chat_conf.global_shortcut {
if let Some(v) = app_conf.clone().global_shortcut {
info!("global_shortcut: `{}`", v);
match v.parse::<Accelerator>() {
Ok(_) => {
@@ -33,47 +34,49 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
}
})
.unwrap_or_else(|err| {
info!("global_shortcut_register_error: {}", err);
error!("global_shortcut_register_error: {}", err);
});
}
Err(err) => {
info!("global_shortcut_parse_error: {}", err);
error!("global_shortcut_parse_error: {}", err);
}
}
} else {
info!("global_shortcut_unregister");
};
if chat_conf.hide_dock_icon {
let app_conf2 = app_conf.clone();
if app_conf.hide_dock_icon {
#[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Accessory);
} else {
let app = app.handle();
tauri::async_runtime::spawn(async move {
let link = if chat_conf.main_dashboard {
let link = if app_conf2.main_dashboard {
"index.html"
} else {
&url
};
info!("main_window: {}", link);
let mut main_win = WindowBuilder::new(&app, "core", WindowUrl::App(link.into()))
.title("ChatGPT")
.resizable(true)
.fullscreen(false)
.inner_size(800.0, 600.0)
.theme(theme)
.always_on_top(chat_conf.stay_on_top)
.inner_size(app_conf2.main_width, app_conf2.main_height)
.theme(Some(theme))
.always_on_top(app_conf2.stay_on_top)
.initialization_script(&utils::user_script())
.initialization_script(include_str!("../scripts/core.js"))
.user_agent(&chat_conf.ua_window);
.user_agent(&app_conf2.ua_window);
#[cfg(target_os = "macos")]
{
main_win = main_win
.title_bar_style(ChatConfJson::titlebar())
.title_bar_style(app_conf2.clone().titlebar())
.hidden_title(true);
}
if url == "https://chat.openai.com" && !chat_conf.main_dashboard {
if url == "https://chat.openai.com" && !app_conf2.main_dashboard {
main_win = main_win
.initialization_script(include_str!("../vendors/floating-ui-core.js"))
.initialization_script(include_str!("../vendors/floating-ui-dom.js"))
@@ -92,10 +95,11 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
}
// auto_update
if chat_conf.auto_update != "Disable" {
info!("stepup::run_check_update");
let auto_update = app_conf.get_auto_update();
if auto_update != "disable" {
info!("run_check_update");
let app = app.handle();
utils::run_check_update(app, chat_conf.auto_update == "Silent", None);
utils::run_check_update(app, auto_update == "silent", None);
}
Ok(())

View File

@@ -1,32 +1,32 @@
use crate::{conf, utils};
use crate::{conf::AppConf, utils};
use log::info;
use std::time::SystemTime;
use tauri::{utils::config::WindowUrl, window::WindowBuilder, Manager};
pub fn tray_window(handle: &tauri::AppHandle) {
let chat_conf = conf::ChatConfJson::get_chat_conf();
let theme = conf::ChatConfJson::theme();
let app_conf = AppConf::read();
let theme = AppConf::theme_mode();
let app = handle.clone();
tauri::async_runtime::spawn(async move {
let link = if chat_conf.tray_dashboard {
let link = if app_conf.tray_dashboard {
"index.html"
} else {
&chat_conf.tray_origin
&app_conf.tray_origin
};
let mut tray_win = WindowBuilder::new(&app, "tray", WindowUrl::App(link.into()))
.title("ChatGPT")
.resizable(false)
.fullscreen(false)
.inner_size(360.0, 540.0)
.inner_size(app_conf.tray_width, app_conf.tray_height)
.decorations(false)
.always_on_top(true)
.theme(theme)
.theme(Some(theme))
.initialization_script(&utils::user_script())
.initialization_script(include_str!("../scripts/core.js"))
.user_agent(&chat_conf.ua_tray);
.user_agent(&app_conf.ua_tray);
if chat_conf.tray_origin == "https://chat.openai.com" && !chat_conf.tray_dashboard {
if app_conf.tray_origin == "https://chat.openai.com" && !app_conf.tray_dashboard {
tray_win = tray_win
.initialization_script(include_str!("../vendors/floating-ui-core.js"))
.initialization_script(include_str!("../vendors/floating-ui-dom.js"))
@@ -45,7 +45,7 @@ pub fn dalle2_window(
is_new: Option<bool>,
) {
info!("dalle2_query: {:?}", query);
let theme = conf::ChatConfJson::theme();
let theme = AppConf::theme_mode();
let app = handle.clone();
let query = if query.is_some() {
@@ -76,7 +76,7 @@ pub fn dalle2_window(
.fullscreen(false)
.inner_size(800.0, 600.0)
.always_on_top(false)
.theme(theme)
.theme(Some(theme))
.initialization_script(include_str!("../scripts/core.js"))
.initialization_script(&query)
.initialization_script(include_str!("../scripts/dalle2.js"))
@@ -90,78 +90,82 @@ pub fn dalle2_window(
}
}
#[tauri::command]
pub fn dalle2_search_window(app: tauri::AppHandle, query: String) {
dalle2_window(
&app.app_handle(),
Some(query),
Some("ChatGPT & DALL·E 2".to_string()),
None,
);
}
pub mod cmd {
use super::*;
use log::info;
use tauri::{command, utils::config::WindowUrl, window::WindowBuilder, Manager};
#[tauri::command]
pub fn control_window(handle: tauri::AppHandle) {
tauri::async_runtime::spawn(async move {
if handle.get_window("main").is_none() {
WindowBuilder::new(
&handle,
"main",
WindowUrl::App("index.html?type=control".into()),
)
.title("Control Center")
.resizable(true)
.fullscreen(false)
.inner_size(1200.0, 700.0)
.min_inner_size(1000.0, 600.0)
.build()
.unwrap();
} else {
let main_win = handle.get_window("main").unwrap();
main_win.show().unwrap();
main_win.set_focus().unwrap();
}
});
}
#[tauri::command]
pub fn dalle2_search_window(app: tauri::AppHandle, query: String) {
dalle2_window(
&app.app_handle(),
Some(query),
Some("ChatGPT & DALL·E 2".to_string()),
None,
);
}
#[tauri::command]
pub async fn wa_window(
app: tauri::AppHandle,
label: String,
title: String,
url: String,
script: Option<String>,
) {
info!("wa_window: {} :=> {}", title, url);
let win = app.get_window(&label);
if win.is_none() {
#[tauri::command]
pub fn control_window(handle: tauri::AppHandle) {
tauri::async_runtime::spawn(async move {
tauri::WindowBuilder::new(&app, label, tauri::WindowUrl::App(url.parse().unwrap()))
.initialization_script(&script.unwrap_or_default())
.initialization_script(include_str!("../scripts/core.js"))
.title(title)
if handle.get_window("main").is_none() {
WindowBuilder::new(
&handle,
"main",
WindowUrl::App("index.html?type=control".into()),
)
.title("Control Center")
.resizable(true)
.fullscreen(false)
.inner_size(1200.0, 700.0)
.min_inner_size(1000.0, 600.0)
.build()
.unwrap();
} else {
let main_win = handle.get_window("main").unwrap();
main_win.show().unwrap();
main_win.set_focus().unwrap();
}
});
} else {
if !win.clone().unwrap().is_visible().unwrap() {
win.clone().unwrap().show().unwrap();
}
#[command]
pub fn wa_window(
app: tauri::AppHandle,
label: String,
title: String,
url: String,
script: Option<String>,
) {
info!("wa_window: {} :=> {}", title, url);
let win = app.get_window(&label);
if win.is_none() {
tauri::async_runtime::spawn(async move {
tauri::WindowBuilder::new(&app, label, tauri::WindowUrl::App(url.parse().unwrap()))
.initialization_script(&script.unwrap_or_default())
.initialization_script(include_str!("../scripts/core.js"))
.title(title)
.inner_size(960.0, 700.0)
.resizable(true)
.build()
.unwrap();
});
} else if let Some(v) = win {
if !v.is_visible().unwrap() {
v.show().unwrap();
}
v.eval("window.location.reload()").unwrap();
v.set_focus().unwrap();
}
win
.clone()
}
#[command]
pub fn window_reload(app: tauri::AppHandle, label: &str) {
app
.app_handle()
.get_window(label)
.unwrap()
.eval("window.location.reload()")
.unwrap();
win.unwrap().set_focus().unwrap();
}
}
#[tauri::command]
pub fn window_reload(app: tauri::AppHandle, label: &str) {
app
.app_handle()
.get_window(label)
.unwrap()
.eval("window.location.reload()")
.unwrap();
}

View File

@@ -1,179 +1,167 @@
use crate::utils::{chat_root, create_file, exists};
use anyhow::Result;
use log::info;
use log::{error, info};
use serde_json::Value;
use std::{collections::BTreeMap, fs, path::PathBuf};
use std::{collections::BTreeMap, path::PathBuf};
use tauri::{Manager, Theme};
#[cfg(target_os = "macos")]
use tauri::TitleBarStyle;
// pub const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15";
// pub const PHONE_USER_AGENT: &str = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1";
use crate::utils::{app_root, create_file, exists};
pub const APP_WEBSITE: &str = "https://lencx.github.io/app/";
pub const ISSUES_URL: &str = "https://github.com/lencx/ChatGPT/issues";
pub const UPDATE_LOG_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/UPDATE_LOG.md";
pub const AWESOME_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/AWESOME.md";
pub const BUY_COFFEE: &str = "https://www.buymeacoffee.com/lencx";
pub const GITHUB_PROMPTS_CSV_URL: &str =
"https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv";
pub const DEFAULT_CHAT_CONF: &str = r#"{
"stay_on_top": false,
"auto_update": "Prompt",
"theme": "Light",
"tray": true,
"titlebar": true,
"popup_search": false,
"global_shortcut": "",
"hide_dock_icon": false,
"main_dashboard": false,
"tray_dashboard": false,
"main_origin": "https://chat.openai.com",
"tray_origin": "https://chat.openai.com",
"default_origin": "https://chat.openai.com",
"ua_window": "",
"ua_tray": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"
}"#;
pub const DEFAULT_CHAT_CONF_MAC: &str = r#"{
"stay_on_top": false,
"auto_update": "Prompt",
"theme": "Light",
"tray": true,
"titlebar": false,
"popup_search": false,
"global_shortcut": "",
"hide_dock_icon": false,
"main_dashboard": false,
"tray_dashboard": false,
"main_origin": "https://chat.openai.com",
"tray_origin": "https://chat.openai.com",
"default_origin": "https://chat.openai.com",
"ua_window": "",
"ua_tray": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"
}"#;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct ChatConfJson {
// support macOS only
pub titlebar: bool,
pub hide_dock_icon: bool,
pub const APP_CONF_PATH: &str = "chat.conf.json";
pub const CHATGPT_URL: &str = "https://chat.openai.com";
pub const UA_MOBILE: &str = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1";
// macOS and Windows, Light/Dark/System
pub theme: String,
// auto update policy, Prompt/Silent/Disable
pub auto_update: String,
pub tray: bool,
pub popup_search: bool,
pub stay_on_top: bool,
pub main_dashboard: bool,
pub tray_dashboard: bool,
pub main_origin: String,
pub tray_origin: String,
pub default_origin: String,
pub ua_window: String,
pub ua_tray: String,
pub global_shortcut: Option<String>,
macro_rules! pub_struct {
($name:ident {$($field:ident: $t:ty,)*}) => {
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct $name {
$(pub $field: $t),*
}
}
}
impl ChatConfJson {
/// init chat.conf.json
/// path: ~/.chatgpt/chat.conf.json
pub fn init() -> PathBuf {
info!("chat_conf_init");
let conf_file = ChatConfJson::conf_path();
let content = if cfg!(target_os = "macos") {
DEFAULT_CHAT_CONF_MAC
} else {
DEFAULT_CHAT_CONF
};
pub_struct!(AppConf {
titlebar: bool,
hide_dock_icon: bool,
// macOS and Windows: light / dark / system
theme: String,
// auto update policy: prompt / silent / disable
auto_update: String,
stay_on_top: bool,
save_window_state: bool,
global_shortcut: Option<String>,
default_origin: String,
if !exists(&conf_file) {
create_file(&conf_file).unwrap();
fs::write(&conf_file, content).unwrap();
return conf_file;
// Main Window
isinit: bool,
popup_search: bool,
main_close: bool,
main_dashboard: bool,
main_origin: String,
ua_window: String,
main_width: f64,
main_height: f64,
// Tray Window
tray_width: f64,
tray_height: f64,
tray: bool,
tray_dashboard: bool,
tray_origin: String,
ua_tray: String,
});
impl AppConf {
pub fn new() -> Self {
info!("conf_init");
Self {
titlebar: !cfg!(target_os = "macos"),
hide_dock_icon: false,
save_window_state: false,
theme: "light".into(),
auto_update: "prompt".into(),
tray: true,
popup_search: false,
isinit: true,
main_close: false,
stay_on_top: false,
main_dashboard: false,
tray_dashboard: false,
main_width: 800.0,
main_height: 600.0,
tray_width: 360.0,
tray_height: 540.0,
main_origin: CHATGPT_URL.into(),
tray_origin: CHATGPT_URL.into(),
default_origin: CHATGPT_URL.into(),
ua_tray: UA_MOBILE.into(),
ua_window: "".into(),
global_shortcut: None,
}
}
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;
pub fn file_path() -> PathBuf {
app_root().join(APP_CONF_PATH)
}
pub fn read() -> Self {
match std::fs::read_to_string(Self::file_path()) {
Ok(v) => {
if let Ok(v2) = serde_json::from_str::<AppConf>(&v) {
v2
} else {
error!("conf_read_parse_error");
Self::default()
}
fs::write(&conf_file, content).unwrap();
}
};
conf_file
}
pub fn conf_path() -> PathBuf {
chat_root().join("chat.conf.json")
}
pub fn get_chat_conf() -> Self {
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()
Err(err) => {
error!("conf_read_error: {}", err);
Self::default()
}
}
}
pub fn reset_chat_conf() -> Self {
let conf_file = ChatConfJson::conf_path();
let content = if cfg!(target_os = "macos") {
DEFAULT_CHAT_CONF_MAC
pub fn write(self) -> Self {
let path = &Self::file_path();
if !exists(path) {
create_file(path).unwrap();
info!("conf_create");
}
if let Ok(v) = serde_json::to_string_pretty(&self) {
std::fs::write(path, v).unwrap_or_else(|err| {
error!("conf_write: {}", err);
Self::default().write();
});
} else {
DEFAULT_CHAT_CONF
};
fs::write(&conf_file, content).unwrap();
serde_json::from_str(content).unwrap()
error!("conf_ser");
}
self
}
// https://users.rust-lang.org/t/updating-object-fields-given-dynamic-json/39049/3
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)?;
let new_rules: BTreeMap<String, Value> = serde_json::from_value(new_rules.clone())?;
pub fn amend(self, json: Value) -> Self {
let val = serde_json::to_value(&self).unwrap();
let mut config: BTreeMap<String, Value> = serde_json::from_value(val).unwrap();
let new_json: BTreeMap<String, Value> = serde_json::from_value(json).unwrap();
for (k, v) in new_rules {
for (k, v) in new_json {
config.insert(k, v);
}
fs::write(
ChatConfJson::conf_path(),
serde_json::to_string_pretty(&config)?,
)?;
if let Some(handle) = app {
tauri::api::process::restart(&handle.env());
match serde_json::to_string_pretty(&config) {
Ok(v) => match serde_json::from_str::<AppConf>(&v) {
Ok(v) => v,
Err(err) => {
error!("conf_amend_parse: {}", err);
self
}
},
Err(err) => {
error!("conf_amend_str: {}", err);
self
}
}
Ok(())
}
pub fn theme() -> Option<Theme> {
let conf = ChatConfJson::get_chat_conf();
let theme = match conf.theme.as_str() {
"System" => match dark_light::detect() {
#[cfg(target_os = "macos")]
pub fn titlebar(self) -> TitleBarStyle {
if self.titlebar {
TitleBarStyle::Transparent
} else {
TitleBarStyle::Overlay
}
}
pub fn theme_mode() -> Theme {
match Self::get_theme().as_str() {
"system" => match dark_light::detect() {
// Dark mode
dark_light::Mode::Dark => Theme::Dark,
// Light mode
@@ -181,20 +169,76 @@ impl ChatConfJson {
// Unspecified
dark_light::Mode::Default => Theme::Light,
},
"Dark" => Theme::Dark,
"dark" => Theme::Dark,
_ => Theme::Light,
};
Some(theme)
}
#[cfg(target_os = "macos")]
pub fn titlebar() -> TitleBarStyle {
let conf = ChatConfJson::get_chat_conf();
if conf.titlebar {
TitleBarStyle::Transparent
} else {
TitleBarStyle::Overlay
}
}
pub fn get_theme() -> String {
Self::read().theme.to_lowercase()
}
pub fn get_auto_update(self) -> String {
self.auto_update.to_lowercase()
}
pub fn theme_check(self, mode: &str) -> bool {
self.theme.to_lowercase() == mode
}
pub fn restart(self, app: tauri::AppHandle) {
tauri::api::process::restart(&app.env());
}
}
impl Default for AppConf {
fn default() -> Self {
Self::new()
}
}
pub mod cmd {
use super::AppConf;
use tauri::{command, AppHandle, Manager};
#[command]
pub fn get_app_conf() -> AppConf {
AppConf::read()
}
#[command]
pub fn reset_app_conf() -> AppConf {
AppConf::default().write()
}
#[command]
pub fn get_theme() -> String {
AppConf::get_theme()
}
#[command]
pub fn form_confirm(_app: AppHandle, data: serde_json::Value) {
AppConf::read().amend(serde_json::json!(data)).write();
}
#[command]
pub fn form_cancel(app: AppHandle, label: &str, title: &str, msg: &str) {
let win = app.app_handle().get_window(label).unwrap();
tauri::api::dialog::ask(
app.app_handle().get_window(label).as_ref(),
title,
msg,
move |is_cancel| {
if is_cancel {
win.close().unwrap();
}
},
);
}
#[command]
pub fn form_msg(app: AppHandle, label: &str, title: &str, msg: &str) {
let win = app.app_handle().get_window(label);
tauri::api::dialog::message(win.as_ref(), title, msg);
}
}

View File

@@ -8,8 +8,7 @@ mod conf;
mod utils;
use app::{cmd, fs_extra, gpt, menu, setup, window};
use conf::ChatConfJson;
use tauri::api::path;
use conf::AppConf;
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_log::{
fern::colors::{Color, ColoredLevelConfig},
@@ -18,38 +17,36 @@ use tauri_plugin_log::{
#[tokio::main]
async fn main() {
ChatConfJson::init();
let app_conf = AppConf::read().write();
// If the file does not exist, creating the file will block menu synchronization
utils::create_chatgpt_prompts();
let context = tauri::generate_context!();
let colors = ColoredLevelConfig {
error: Color::Red,
warn: Color::Yellow,
debug: Color::Blue,
info: Color::BrightGreen,
trace: Color::Cyan,
};
gpt::download_list("chat.download.json", "download", None, None);
gpt::download_list("chat.notes.json", "notes", None, None);
let chat_conf = ChatConfJson::get_chat_conf();
let mut log = tauri_plugin_log::Builder::default()
.targets([
// LogTarget::LogDir,
// LOG PATH: ~/.chatgpt/ChatGPT.log
LogTarget::Folder(utils::app_root()),
LogTarget::Stdout,
LogTarget::Webview,
])
.level(log::LevelFilter::Debug);
if cfg!(debug_assertions) {
log = log.with_colors(ColoredLevelConfig {
error: Color::Red,
warn: Color::Yellow,
debug: Color::Blue,
info: Color::BrightGreen,
trace: Color::Cyan,
});
}
let mut builder = tauri::Builder::default()
// https://github.com/tauri-apps/tauri/pull/2736
.plugin(
tauri_plugin_log::Builder::default()
.targets([
// LogTarget::LogDir,
// LOG PATH: ~/.chatgpt/ChatGPT.log
LogTarget::Folder(path::home_dir().unwrap().join(".chatgpt")),
LogTarget::Stdout,
LogTarget::Webview,
])
.level(log::LevelFilter::Debug)
.with_colors(colors)
.build(),
)
.plugin(log.build())
.plugin(tauri_plugin_positioner::init())
.plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
@@ -61,13 +58,7 @@ async fn main() {
cmd::download,
cmd::save_file,
cmd::open_link,
cmd::get_chat_conf,
cmd::get_theme,
cmd::reset_chat_conf,
cmd::run_check_update,
cmd::form_cancel,
cmd::form_confirm,
cmd::form_msg,
cmd::open_file,
cmd::get_data,
gpt::get_chat_model_cmd,
@@ -77,38 +68,60 @@ async fn main() {
gpt::cmd_list,
gpt::download_list,
gpt::get_download_list,
window::wa_window,
window::control_window,
window::window_reload,
window::dalle2_search_window,
fs_extra::metadata,
conf::cmd::get_app_conf,
conf::cmd::reset_app_conf,
conf::cmd::get_theme,
conf::cmd::form_confirm,
conf::cmd::form_cancel,
conf::cmd::form_msg,
window::cmd::wa_window,
window::cmd::control_window,
window::cmd::window_reload,
window::cmd::dalle2_search_window,
])
.setup(setup::init)
.menu(menu::init());
if chat_conf.tray {
if app_conf.tray {
builder = builder.system_tray(menu::tray_menu());
}
if app_conf.save_window_state {
builder = builder.plugin(tauri_plugin_window_state::Builder::default().build());
}
builder
.on_menu_event(menu::menu_handler)
.on_system_tray_event(menu::tray_handler)
.on_window_event(|event| {
// https://github.com/tauri-apps/tauri/discussions/2684
.on_window_event(move |event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event.event() {
let win = event.window();
let win = event.window().clone();
let app_conf = AppConf::read();
if win.label() == "core" {
// TODO: https://github.com/tauri-apps/tauri/issues/3084
// event.window().hide().unwrap();
// https://github.com/tauri-apps/tao/pull/517
#[cfg(target_os = "macos")]
event.window().minimize().unwrap();
// fix: https://github.com/lencx/ChatGPT/issues/93
#[cfg(not(target_os = "macos"))]
event.window().hide().unwrap();
if app_conf.isinit {
tauri::api::dialog::ask(
Some(event.window()),
"",
"Do you want to exit the application when you click the [x] button?",
move |is_ok| {
app_conf
.amend(serde_json::json!({ "isinit" : false, "main_close": is_ok }))
.write();
if is_ok {
std::process::exit(0);
} else {
win.minimize().unwrap();
}
},
);
} else if app_conf.main_close {
std::process::exit(0);
} else {
win.minimize().unwrap();
}
} else {
win.close().unwrap();
event.window().close().unwrap();
}
api.prevent_close();
}

View File

@@ -75,6 +75,14 @@ function init() {
width: 22px;
height: 22px;
}
.chatappico.copy {
width: 16px;
height: 16px;
}
.chatappico.cpok {
width: 16px;
height: 16px;
}
@media screen and (max-width: 767px) {
#download-png-button, #download-pdf-button, #download-html-button {
display: none;

View File

@@ -36,8 +36,22 @@ async function invoke(cmd, args) {
});
}
async function message(message) {
invoke('messageDialog', {
__tauriModule: 'Dialog',
message: {
cmd: 'messageDialog',
message: message.toString(),
title: null,
type: null,
buttonLabel: null
}
});
}
window.uid = uid;
window.invoke = invoke;
window.message = message;
window.transformCallback = transformCallback;
async function init() {
@@ -54,7 +68,7 @@ async function init() {
if (__TAURI_METADATA__.__currentWindow.label !== 'tray') {
const _platform = await platform();
const chatConf = await invoke('get_chat_conf') || {};
const chatConf = await invoke('get_app_conf') || {};
if (/darwin/.test(_platform) && !chatConf.titlebar) {
const topStyleDom = document.createElement("style");
topStyleDom.innerHTML = `#chatgpt-app-window-top{position:fixed;top:0;z-index:999999999;width:100%;height:24px;background:transparent;cursor:grab;cursor:-webkit-grab;user-select:none;-webkit-user-select:none;}#chatgpt-app-window-top:active {cursor:grabbing;cursor:-webkit-grabbing;}`;
@@ -87,16 +101,10 @@ async function init() {
}
});
document.addEventListener('wheel', function(event) {
const deltaX = event.wheelDeltaX;
if (Math.abs(deltaX) >= 50) {
if (deltaX > 0) {
window.history.go(-1);
} else {
window.history.go(1);
}
}
});
// Fix Chinese input method "Enter" on Safari
document.addEventListener("keydown", (e) => {
if(e.keyCode == 229) e.stopPropagation();
}, true)
if (window.location.host === 'chat.openai.com') {
window.__sync_prompts = async function() {

View File

@@ -2,11 +2,13 @@
async function init() {
const buttonOuterHTMLFallback = `<button class="btn flex justify-center gap-2 btn-neutral" id="download-png-button">Try Again</button>`;
if (window.innerWidth < 767) return;
const chatConf = await invoke('get_chat_conf') || {};
removeButtons();
if (window.buttonsInterval) {
clearInterval(window.buttonsInterval);
}
if (window.innerWidth < 767) return;
const chatConf = await invoke('get_app_conf') || {};
window.buttonsInterval = setInterval(() => {
const actionsArea = document.querySelector("form>div>div");
if (!actionsArea) {
@@ -20,12 +22,15 @@ async function init() {
TryAgainButton = parentNode.querySelector("button");
}
addActionsButtons(actionsArea, TryAgainButton, chatConf);
copyBtns();
} else if (shouldRemoveButtons()) {
removeButtons();
}
}, 1000);
}
window.addEventListener('resize', init);
const Format = {
PNG: "png",
PDF: "pdf",
@@ -132,7 +137,14 @@ function addActionsButtons(actionsArea, TryAgainButton) {
}
async function exportMarkdown() {
const data = ExportMD.turndown(document.querySelector("main div>div>div").innerHTML);
const content = Array.from(document.querySelectorAll('main .items-center>div')).map(i => {
let j = i.cloneNode(true);
if (/dark\:bg-gray-800/.test(i.getAttribute('class'))) {
j.innerHTML = `<blockquote>${i.innerHTML}</blockquote>`;
}
return j.innerHTML;
}).join('<hr />');
const data = ExportMD.turndown(content);
const { id, filename } = getName();
await invoke('save_file', { name: `notes/${id}.md`, content: data });
await invoke('download_list', { pathname: 'chat.notes.json', filename, id, dir: 'notes' });
@@ -200,7 +212,7 @@ class Elements {
}
init() {
// this.threadWrapper = document.querySelector(".cdfdFe");
this.spacer = document.querySelector(".w-full.h-48.flex-shrink-0");
this.spacer = document.querySelector("[class*='h-48'].w-full.flex-shrink-0");
this.thread = document.querySelector(
"[class*='react-scroll-to-bottom']>[class*='react-scroll-to-bottom']>div"
);
@@ -268,10 +280,49 @@ function setIcon(type) {
// link: `<svg class="chatappico" viewBox="0 0 1024 1024"><path d="M1007.382 379.672L655.374 75.702C624.562 49.092 576 70.694 576 112.03v160.106C254.742 275.814 0 340.2 0 644.652c0 122.882 79.162 244.618 166.666 308.264 27.306 19.862 66.222-5.066 56.154-37.262C132.132 625.628 265.834 548.632 576 544.17V720c0 41.4 48.6 62.906 79.374 36.328l352.008-304c22.142-19.124 22.172-53.506 0-72.656z" p-id="8506" fill="currentColor"></path></svg>`,
png: `<svg class="chatappico" viewBox="0 0 1070 1024"><path d="M981.783273 0H85.224727C38.353455 0 0 35.374545 0 83.083636v844.893091c0 47.616 38.353455 86.574545 85.178182 86.574546h903.633454c46.917818 0 81.733818-38.958545 81.733819-86.574546V83.083636C1070.592 35.374545 1028.701091 0 981.783273 0zM335.825455 135.912727c74.193455 0 134.330182 60.974545 134.330181 136.285091 0 75.170909-60.136727 136.192-134.330181 136.192-74.286545 0-134.516364-61.021091-134.516364-136.192 0-75.264 60.229818-136.285091 134.516364-136.285091z m-161.512728 745.937455a41.890909 41.890909 0 0 1-27.648-10.379637 43.752727 43.752727 0 0 1-4.654545-61.067636l198.097454-255.162182a42.123636 42.123636 0 0 1 57.716364-6.702545l116.549818 128.139636 286.906182-352.814545c14.615273-18.711273 90.251636-106.775273 135.866182-6.935273 0.093091-0.093091 0.093091 112.965818 0.232727 247.761455 0.093091 140.8 0.093091 317.067636 0.093091 317.067636-1.024-0.093091-762.740364 0.093091-763.112727 0.093091z" fill="currentColor"></path></svg>`,
pdf: `<svg class="chatappico pdf" viewBox="0 0 1024 1024"><path d="M821.457602 118.382249H205.725895c-48.378584 0-87.959995 39.583368-87.959996 87.963909v615.731707c0 48.378584 39.581411 87.959995 87.959996 87.959996h615.733664c48.380541 0 87.961952-39.581411 87.961952-87.959996V206.346158c-0.001957-48.378584-39.583368-87.963909-87.963909-87.963909zM493.962468 457.544987c-10.112054 32.545237-21.72487 82.872662-38.806571 124.248336-8.806957 22.378397-8.380404 18.480717-15.001764 32.609808l5.71738-1.851007c58.760658-16.443827 99.901532-20.519564 138.162194-27.561607-7.67796-6.06371-14.350194-10.751884-19.631237-15.586807-26.287817-29.101504-35.464584-34.570387-70.440002-111.862636v0.003913z m288.36767 186.413594c-7.476424 8.356924-20.670227 13.191847-40.019704 13.191847-33.427694 0-63.808858-9.229597-107.79277-31.660824-75.648648 8.356924-156.097 17.214754-201.399704 31.729308-2.199293 0.876587-4.832967 1.759043-7.916674 3.077836-54.536215 93.237125-95.031389 132.767663-130.621199 131.19646-11.286054-0.49895-27.694661-7.044-32.973748-10.11988l-6.52157-6.196764-2.29517-4.353583c-3.07588-7.91863-3.954423-15.395054-2.197337-23.751977 4.838837-23.309771 29.907651-60.251638 82.686779-93.237126 8.356924-6.159587 27.430511-15.897917 45.020944-24.25484 13.311204-21.177004 19.45905-34.744531 36.341171-72.259702 19.102937-45.324228 36.505531-99.492589 47.500041-138.191543v-0.44025c-16.267727-53.219378-25.945401-89.310095-9.67376-147.80856 3.958337-16.71189 18.46702-33.864031 34.748444-33.864031h10.552304c10.115967 0 19.791684 3.520043 26.829814 10.552304 29.029107 29.031064 15.39114 103.824649 0.8805 162.323113-0.8805 2.63563-1.322707 4.832967-1.761 6.153717 17.59239 49.697378 45.400538 98.774492 73.108895 121.647926 11.436717 8.791304 22.638634 18.899444 36.71098 26.814161 19.791684-2.20125 37.517128-4.11487 55.547812-4.11487 54.540128 0 87.525615 9.67963 100.279169 30.351814 4.400543 7.034217 6.595923 15.389184 5.281043 24.1844-0.44025 10.996467-4.39663 21.112434-12.31526 29.031064z m-27.796407-36.748157c-4.394673-4.398587-17.024957-16.936907-78.601259-16.936907-3.073923 0-10.622744-0.784623-14.57521 3.612007 32.104987 14.072347 62.830525 24.757704 83.058545 24.757703 3.083707 0 5.72325-0.442207 8.356923-0.876586h1.759044c2.20125-0.8805 3.520043-1.324663 3.960293-5.71738-0.87463-1.324663-1.757087-3.083707-3.958336-4.838837z m-387.124553 63.041845c-9.237424 5.27713-16.71189 10.112054-21.112433 13.634053-31.226444 28.586901-51.018128 57.616008-53.217422 74.331812 19.789727-6.59788 45.737084-35.626987 74.329855-87.961952v-0.003913z m125.574957-297.822284l2.197336-1.761c3.079793-14.072347 5.232127-29.189554 7.87167-38.869184l1.318794-7.036174c4.39663-25.070771 2.71781-39.720334-4.76057-50.272637l-6.59788-2.20125a57.381208 57.381208 0 0 0-3.079794 5.27713c-7.474467 18.47289-7.063567 55.283661 3.0524 94.865072l-0.001956-0.001957z" fill="currentColor"></path></svg>`,
md: `<svg class="chatappico md" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1380" width="200" height="200"><path d="M128 128h768a42.666667 42.666667 0 0 1 42.666667 42.666667v682.666666a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667V170.666667a42.666667 42.666667 0 0 1 42.666667-42.666667z m170.666667 533.333333v-170.666666l85.333333 85.333333 85.333333-85.333333v170.666666h85.333334v-298.666666h-85.333334l-85.333333 85.333333-85.333333-85.333333H213.333333v298.666666h85.333334z m469.333333-128v-170.666666h-85.333333v170.666666h-85.333334l128 128 128-128h-85.333333z" p-id="1381" fill="currentColor"></path></svg>`
md: `<svg class="chatappico md" viewBox="0 0 1024 1024" width="200" height="200"><path d="M128 128h768a42.666667 42.666667 0 0 1 42.666667 42.666667v682.666666a42.666667 42.666667 0 0 1-42.666667 42.666667H128a42.666667 42.666667 0 0 1-42.666667-42.666667V170.666667a42.666667 42.666667 0 0 1 42.666667-42.666667z m170.666667 533.333333v-170.666666l85.333333 85.333333 85.333333-85.333333v170.666666h85.333334v-298.666666h-85.333334l-85.333333 85.333333-85.333333-85.333333H213.333333v298.666666h85.333334z m469.333333-128v-170.666666h-85.333333v170.666666h-85.333334l128 128 128-128h-85.333333z" p-id="1381" fill="currentColor"></path></svg>`,
copy: `<svg class="chatappico copy" stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>`,
cpok: `<svg class="chatappico cpok" viewBox="0 0 24 24"><g fill="none" stroke="#10a37f" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2M16 4h2a2 2 0 0 1 2 2v4m1 4H11"/><path d="m15 10l-4 4l4 4"/></g></svg>`
}[type];
}
function copyBtns() {
Array.from(document.querySelectorAll("main >div>div>div>div>div"))
.forEach(i => {
if (i.querySelector('.chat-item-copy')) return;
if (!i.querySelector('button.rounded-md')) return;
const btn = i.querySelector('button.rounded-md').cloneNode(true);
btn.classList.add('chat-item-copy');
btn.title = 'Copy to clipboard';
btn.innerHTML = setIcon('copy');
i.querySelector('.self-end').appendChild(btn);
btn.onclick = () => {
copyToClipboard(i?.innerText?.trim() || '', btn);
}
})
}
function copyToClipboard(text, btn) {
window.clearTimeout(window.__cpTimeout);
btn.innerHTML = setIcon('cpok');
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
} else {
var textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.style.position = 'fixed';
textarea.style.clip = 'rect(0 0 0 0)';
textarea.style.top = '10px';
textarea.value = text;
textarea.select();
document.execCommand('copy', true);
document.body.removeChild(textarea);
}
window.__cpTimeout = setTimeout(() => {
btn.innerHTML = setIcon('copy');
}, 1000);
}
if (
document.readyState === "complete" ||
document.readyState === "interactive"

View File

@@ -2,7 +2,9 @@ var ExportMD = (function () {
if (!TurndownService || !turndownPluginGfm) return;
const hljsREG = /^.*(hljs).*(language-[a-z0-9]+).*$/i;
const gfm = turndownPluginGfm.gfm
const turndownService = new TurndownService()
const turndownService = new TurndownService({
hr: '---'
})
.use(gfm)
.addRule('code', {
filter: (node) => {

View File

@@ -1,7 +1,7 @@
// *** Core Script - DALL·E 2 Core ***
async function init() {
const chatConf = await invoke('get_chat_conf') || {};
const chatConf = await invoke('get_app_conf') || {};
if (!chatConf.popup_search) return;
if (!window.FloatingUIDOM) return;

View File

@@ -1,5 +1,5 @@
use anyhow::Result;
use log::info;
use log::{error, info};
use regex::Regex;
use serde_json::Value;
use std::{
@@ -11,7 +11,7 @@ use std::{
use tauri::updater::UpdateResponse;
use tauri::{utils::config::Config, AppHandle, Manager, Wry};
pub fn chat_root() -> PathBuf {
pub fn app_root() -> PathBuf {
tauri::api::path::home_dir().unwrap().join(".chatgpt")
}
@@ -33,7 +33,7 @@ pub fn create_file(path: &Path) -> Result<File> {
}
pub fn create_chatgpt_prompts() {
let sync_file = chat_root().join("cache_model").join("chatgpt_prompts.json");
let sync_file = app_root().join("cache_model").join("chatgpt_prompts.json");
if !exists(&sync_file) {
create_file(&sync_file).unwrap();
fs::write(&sync_file, "[]").unwrap();
@@ -41,7 +41,7 @@ pub fn create_chatgpt_prompts() {
}
pub fn script_path() -> PathBuf {
let script_file = chat_root().join("main.js");
let script_file = app_root().join("main.js");
if !exists(&script_file) {
create_file(&script_file).unwrap();
fs::write(
@@ -96,7 +96,7 @@ pub fn convert_path(path_str: &str) -> String {
}
pub fn clear_conf(app: &tauri::AppHandle) {
let root = chat_root();
let root = app_root();
let msg = format!(
"Path: {}\n
Are you sure you want to clear all ChatGPT configurations? Performing this operation data can not be restored, please back up in advance.\n
@@ -145,7 +145,7 @@ pub async fn get_data(
if is_ok {
Ok(Some(body))
} else {
info!("chatgpt_http_error: {}", body);
error!("chatgpt_http: {}", body);
if let Some(v) = app {
tauri::api::dialog::message(v.get_window("core").as_ref(), "ChatGPT HTTP", body);
}
@@ -156,25 +156,25 @@ pub async fn get_data(
pub fn run_check_update(app: AppHandle<Wry>, silent: bool, has_msg: Option<bool>) {
info!("run_check_update: silent={} has_msg={:?}", silent, has_msg);
tauri::async_runtime::spawn(async move {
let result = app.updater().check().await;
let update_resp = result.unwrap();
if update_resp.is_update_available() {
if silent {
tauri::async_runtime::spawn(async move {
silent_install(app, update_resp).await.unwrap();
});
} else {
tauri::async_runtime::spawn(async move {
prompt_for_install(app, update_resp).await.unwrap();
});
}
} else if let Some(v) = has_msg {
if v {
tauri::api::dialog::message(
app.app_handle().get_window("core").as_ref(),
"ChatGPT",
"Your ChatGPT is up to date",
);
if let Ok(update_resp) = app.updater().check().await {
if update_resp.is_update_available() {
if silent {
tauri::async_runtime::spawn(async move {
silent_install(app, update_resp).await.unwrap();
});
} else {
tauri::async_runtime::spawn(async move {
prompt_for_install(app, update_resp).await.unwrap();
});
}
} else if let Some(v) = has_msg {
if v {
tauri::api::dialog::message(
app.app_handle().get_window("core").as_ref(),
"ChatGPT",
"Your ChatGPT is up to date",
);
}
}
}
});

View File

@@ -7,7 +7,7 @@
},
"package": {
"productName": "ChatGPT",
"version": "0.10.1"
"version": "0.11.0"
},
"tauri": {
"allowlist": {

View File

@@ -8,8 +8,8 @@ type rowSelectionOptions = {
key: 'id' | string;
rowType: 'id' | 'row' | 'all';
};
export function useTableRowSelection(options: Partial<rowSelectionOptions> = {}) {
const { key = 'id', rowType = 'id' } = options;
export function useTableRowSelection(options?: Partial<rowSelectionOptions>) {
const { key = 'id', rowType = 'id' } = options || {};
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [selectedRowIDs, setSelectedRowIDs] = useState<string[]>([]);
const [selectedRows, setSelectedRows] = useState<Record<string | symbol, any>[]>([]);

2
src/utils.ts vendored
View File

@@ -2,7 +2,7 @@ import { readTextFile, writeTextFile, exists, createDir } from '@tauri-apps/api/
import { homeDir, join, dirname } from '@tauri-apps/api/path';
import dayjs from 'dayjs';
export const CHAT_CONF_JSON = 'chat.conf.json';
export const APP_CONF_JSON = 'chat.conf.json';
export const CHAT_MODEL_JSON = 'chat.model.json';
export const CHAT_MODEL_CMD_JSON = 'chat.model.cmd.json';
export const CHAT_DOWNLOAD_JSON = 'chat.download.json';

View File

@@ -7,7 +7,7 @@ import { os, invoke } from '@tauri-apps/api';
import useInit from '@/hooks/useInit';
import useJson from '@/hooks/useJson';
import { CHAT_AWESOME_JSON, CHAT_CONF_JSON, readJSON } from '@/utils';
import { CHAT_AWESOME_JSON, APP_CONF_JSON, readJSON } from '@/utils';
import './index.scss';
export default function Dashboard() {
@@ -19,7 +19,7 @@ export default function Dashboard() {
useInit(async () => {
const getOS = await os.platform();
const conf = await readJSON(CHAT_CONF_JSON);
const conf = await readJSON(APP_CONF_JSON);
const appTheme = await invoke('get_theme');
setTheme(appTheme as string);
setClass(!conf?.titlebar && getOS === 'darwin');

View File

@@ -18,6 +18,9 @@ export default function General() {
<Form.Item label="Stay On Top" name="stay_on_top" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label="Save Window State" name="save_window_state" valuePropName="checked">
<Switch />
</Form.Item>
{platformInfo === 'darwin' && (
<Form.Item label="Titlebar" name="titlebar" valuePropName="checked">
<Switch />
@@ -30,16 +33,16 @@ export default function General() {
)}
<Form.Item label="Theme" name="theme">
<Radio.Group>
<Radio value="Light">Light</Radio>
<Radio value="Dark">Dark</Radio>
<Radio value="light">Light</Radio>
<Radio value="dark">Dark</Radio>
{['darwin', 'windows'].includes(platformInfo) && <Radio value="System">System</Radio>}
</Radio.Group>
</Form.Item>
<Form.Item label={<AutoUpdateLabel />} name="auto_update">
<Radio.Group>
<Radio value="Prompt">Prompt</Radio>
<Radio value="Silent">Silent</Radio>
{/*<Radio value="Disable">Disable</Radio>*/}
<Radio value="prompt">Prompt</Radio>
<Radio value="silent">Silent</Radio>
{/*<Radio value="disable">Disable</Radio>*/}
</Radio.Group>
</Form.Item>
<Form.Item label={<GlobalShortcutLabel />} name="global_shortcut">

View File

@@ -1,4 +1,4 @@
import { Form, Switch, Input, Tooltip } from 'antd';
import { Form, Switch, Input, InputNumber, Tooltip } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import SwitchOrigin from '@/components/SwitchOrigin';
@@ -30,12 +30,32 @@ const PopupSearchLabel = () => {
);
};
const MainCloseLabel = () => {
return (
<span>
Close Exit{' '}
<Tooltip title="Click the close button whether to exit directly, the default minimized.">
<QuestionCircleOutlined style={{ color: '#1677ff' }} />
</Tooltip>
</span>
);
};
export default function MainWindow() {
return (
<>
<Form.Item label={<PopupSearchLabel />} name="popup_search" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label={<MainCloseLabel />} name="main_close" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label="Default Width" name="main_width">
<InputNumber />
</Form.Item>
<Form.Item label="Default Height" name="main_height">
<InputNumber />
</Form.Item>
<SwitchOrigin name="main" />
<Form.Item label="User Agent (Main)" name="ua_window">
<Input.TextArea

View File

@@ -1,4 +1,4 @@
import { Form, Switch, Input, Tooltip } from 'antd';
import { Form, Switch, Input, InputNumber, Tooltip } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { DISABLE_AUTO_COMPLETE } from '@/utils';
@@ -23,6 +23,12 @@ export default function TrayWindow() {
<Form.Item label="Enable SystemTray" name="tray" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label="Default Width" name="tray_width">
<InputNumber />
</Form.Item>
<Form.Item label="Default Height" name="tray_height">
<InputNumber />
</Form.Item>
<SwitchOrigin name="tray" />
<Form.Item label={<UALabel />} name="ua_tray">
<Input.TextArea

View File

@@ -6,7 +6,7 @@ import { clone, omit, isEqual } from 'lodash';
import useInit from '@/hooks/useInit';
import FilePath from '@/components/FilePath';
import { chatRoot, CHAT_CONF_JSON } from '@/utils';
import { chatRoot, APP_CONF_JSON } from '@/utils';
import General from './General';
import MainWindow from './MainWindow';
import TrayWindow from './TrayWindow';
@@ -24,8 +24,8 @@ export default function Settings() {
}, [key]);
useInit(async () => {
setChatConf(await invoke('get_chat_conf'));
setPath(await path.join(await chatRoot(), CHAT_CONF_JSON));
setChatConf(await invoke('get_app_conf'));
setPath(await path.join(await chatRoot(), APP_CONF_JSON));
});
useEffect(() => {
@@ -37,7 +37,7 @@ export default function Settings() {
};
const onReset = async () => {
const chatData = await invoke('reset_chat_conf');
const chatData = await invoke('reset_app_conf');
setChatConf(chatData);
const isOk = await dialog.ask(`Configuration reset successfully, whether to restart?`, {
title: 'ChatGPT Preferences',
@@ -69,7 +69,7 @@ export default function Settings() {
return (
<div>
<FilePath paths={CHAT_CONF_JSON} />
<FilePath paths={APP_CONF_JSON} />
<Form
form={form}
style={{ maxWidth: 500 }}

View File

@@ -24,5 +24,19 @@ export default defineConfig({
minify: !process.env.TAURI_DEBUG ? 'esbuild' : false,
// produce sourcemaps for debug builds
sourcemap: !!process.env.TAURI_DEBUG,
rollupOptions: {
output: {
manualChunks: {
ant: ['antd'],
antico: ['@ant-design/icons'],
editor: ['@monaco-editor/react'],
utils: ['lodash', 'uuid', 'dayjs', 'clsx'],
rrr: ['react', 'react-dom', 'react-router-dom'],
rm: ['react-markdown'],
rsh: ['react-syntax-highlighter'],
md: ['github-markdown-css', 'rehype-raw', 'remark-comment-config', 'remark-gfm'],
},
},
},
},
});