mirror of
https://github.com/lencx/ChatGPT.git
synced 2024-10-01 01:06:13 -04:00
chore: add pretty
This commit is contained in:
parent
1ba356a91f
commit
bc39dcdd72
82
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
82
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,43 +1,43 @@
|
|||||||
name: "🕷️ Bug report"
|
name: '🕷️ Bug report'
|
||||||
description: "report bugs"
|
description: 'report bugs'
|
||||||
title: "[Bug]"
|
title: '[Bug]'
|
||||||
labels:
|
labels:
|
||||||
- "bug"
|
- 'bug'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before filing a new one!"
|
value: 'Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before filing a new one!'
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
## Bug report
|
## Bug report
|
||||||
Please fill in the following information to help us reproduce the bug:
|
Please fill in the following information to help us reproduce the bug:
|
||||||
- type: input
|
- type: input
|
||||||
id: version
|
id: version
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: "Please specify the version of ChatGPT you are using, a newer version may have fixed the bug you encountered.Check the [UPDATE_LOG](https://github.com/lencx/ChatGPT/blob/main/UPDATE_LOG.md) for more information."
|
description: 'Please specify the version of ChatGPT you are using, a newer version may have fixed the bug you encountered.Check the [UPDATE_LOG](https://github.com/lencx/ChatGPT/blob/main/UPDATE_LOG.md) for more information.'
|
||||||
placeholder: "e.g. v0.1.0"
|
placeholder: 'e.g. v0.1.0'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: bug
|
id: bug
|
||||||
attributes:
|
attributes:
|
||||||
label: Bug description
|
label: Bug description
|
||||||
description: |
|
description: |
|
||||||
Please describe the bug here,if possible, please provide a minimal example to reproduce the bug.The content of `~/.chatgpt/chatgpt.log` may be helpful if you encounter a crash.
|
Please describe the bug here,if possible, please provide a minimal example to reproduce the bug.The content of `~/.chatgpt/chatgpt.log` may be helpful if you encounter a crash.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: OS
|
id: OS
|
||||||
attributes:
|
attributes:
|
||||||
label: OS
|
label: OS
|
||||||
description: "Please specify the OS you are using."
|
description: 'Please specify the OS you are using.'
|
||||||
placeholder: "e.g. Ubuntu 22.04"
|
placeholder: 'e.g. Ubuntu 22.04'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: environment
|
id: environment
|
||||||
attributes:
|
attributes:
|
||||||
label: Environment
|
label: Environment
|
||||||
description: "If you think your environment may be related to the problem, please describe it here."
|
description: 'If you think your environment may be related to the problem, please describe it here.'
|
||||||
|
70
.github/ISSUE_TEMPLATE/build_error_report.yml
vendored
70
.github/ISSUE_TEMPLATE/build_error_report.yml
vendored
@ -1,37 +1,37 @@
|
|||||||
name: "❌ Build error report"
|
name: '❌ Build error report'
|
||||||
description: "report errors when building by yourself"
|
description: 'report errors when building by yourself'
|
||||||
title: "[Build Error]"
|
title: '[Build Error]'
|
||||||
labels:
|
labels:
|
||||||
- "build error"
|
- 'build error'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before filing a new one!"
|
value: 'Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before filing a new one!'
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "Please make sure to build from the source code with the latest version of ChatGPT."
|
value: 'Please make sure to build from the source code with the latest version of ChatGPT.'
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
## Build error report
|
## Build error report
|
||||||
Please fill in the following information to help us reproduce the bug:
|
Please fill in the following information to help us reproduce the bug:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: error
|
id: error
|
||||||
attributes:
|
attributes:
|
||||||
label: Error message
|
label: Error message
|
||||||
description: "Please paste the error message here."
|
description: 'Please paste the error message here.'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: OS
|
id: OS
|
||||||
attributes:
|
attributes:
|
||||||
label: OS
|
label: OS
|
||||||
description: "Please specify the OS you are using."
|
description: 'Please specify the OS you are using.'
|
||||||
placeholder: "e.g. Ubuntu 22.04"
|
placeholder: 'e.g. Ubuntu 22.04'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: environment
|
id: environment
|
||||||
attributes:
|
attributes:
|
||||||
label: Environment
|
label: Environment
|
||||||
description: "If you think your environment may be related to the problem, please describe it here."
|
description: 'If you think your environment may be related to the problem, please describe it here.'
|
||||||
|
34
.github/ISSUE_TEMPLATE/docmentation_issue.yml
vendored
34
.github/ISSUE_TEMPLATE/docmentation_issue.yml
vendored
@ -1,19 +1,19 @@
|
|||||||
name: "📚 Documentation Issue"
|
name: '📚 Documentation Issue'
|
||||||
description: "report documentation issues, typos welcome!"
|
description: 'report documentation issues, typos welcome!'
|
||||||
title: "[Doc]"
|
title: '[Doc]'
|
||||||
labels:
|
labels:
|
||||||
- "documentation"
|
- 'documentation'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before creating a new one."
|
value: 'Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before creating a new one.'
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: doc-description
|
id: doc-description
|
||||||
attributes:
|
attributes:
|
||||||
label: "Provide a description of requested docs changes"
|
label: 'Provide a description of requested docs changes'
|
||||||
description: "Briefly describe the requested docs changes."
|
description: 'Briefly describe the requested docs changes.'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: Please limit one request per issue.
|
value: Please limit one request per issue.
|
||||||
|
64
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
64
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,34 +1,34 @@
|
|||||||
name: "⭐ Feature or enhancement request"
|
name: '⭐ Feature or enhancement request'
|
||||||
description: "suggest new features or enhancements"
|
description: 'suggest new features or enhancements'
|
||||||
title: "[Feature]"
|
title: '[Feature]'
|
||||||
labels:
|
labels:
|
||||||
- "enhancement"
|
- 'enhancement'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before creating a new one."
|
value: 'Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before creating a new one.'
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: feature-description
|
id: feature-description
|
||||||
attributes:
|
attributes:
|
||||||
label: "Feature description"
|
label: 'Feature description'
|
||||||
description: "Describe the feature or enhancements you'd like to see."
|
description: "Describe the feature or enhancements you'd like to see."
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: motivation
|
id: motivation
|
||||||
attributes:
|
attributes:
|
||||||
label: "Motivation"
|
label: 'Motivation'
|
||||||
description: "Describe the motivation for this feature or enhancement."
|
description: 'Describe the motivation for this feature or enhancement.'
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: alternatives
|
id: alternatives
|
||||||
attributes:
|
attributes:
|
||||||
label: "Alternatives"
|
label: 'Alternatives'
|
||||||
description: "Describe any alternatives you've considered."
|
description: "Describe any alternatives you've considered."
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: additional-context
|
id: additional-context
|
||||||
attributes:
|
attributes:
|
||||||
label: "Additional context"
|
label: 'Additional context'
|
||||||
description: "Add any other context or screenshots about the feature request here."
|
description: 'Add any other context or screenshots about the feature request here.'
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: Please limit one request per issue.
|
value: Please limit one request per issue.
|
||||||
|
64
.github/ISSUE_TEMPLATE/security.yml
vendored
64
.github/ISSUE_TEMPLATE/security.yml
vendored
@ -1,34 +1,34 @@
|
|||||||
name: "⚠️ Security&Privacy issue"
|
name: '⚠️ Security&Privacy issue'
|
||||||
description: "Report security or privacy issues"
|
description: 'Report security or privacy issues'
|
||||||
title: "[Security]"
|
title: '[Security]'
|
||||||
labels:
|
labels:
|
||||||
- "security"
|
- 'security'
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: "Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before creating a new one."
|
value: 'Please make sure to [search for existing issues](https://github.com/lencx/ChatGPT/issues) before creating a new one.'
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: security-description
|
id: security-description
|
||||||
attributes:
|
attributes:
|
||||||
label: "Description"
|
label: 'Description'
|
||||||
description: "Describe the security or privacy issue."
|
description: 'Describe the security or privacy issue.'
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: motivation
|
id: motivation
|
||||||
attributes:
|
attributes:
|
||||||
label: "Motivation"
|
label: 'Motivation'
|
||||||
description: "Describe the motivation for this security or privacy issue."
|
description: 'Describe the motivation for this security or privacy issue.'
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: alternatives
|
id: alternatives
|
||||||
attributes:
|
attributes:
|
||||||
label: "Alternatives"
|
label: 'Alternatives'
|
||||||
description: "Describe any alternatives you've considered."
|
description: "Describe any alternatives you've considered."
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: additional-context
|
id: additional-context
|
||||||
attributes:
|
attributes:
|
||||||
label: "Additional context"
|
label: 'Additional context'
|
||||||
description: "Add any other context or screenshots about the security or privacy issue here."
|
description: 'Add any other context or screenshots about the security or privacy issue here.'
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: Please limit one request per issue.
|
value: Please limit one request per issue.
|
||||||
|
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -28,4 +28,4 @@ jobs:
|
|||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: fmt
|
command: fmt
|
||||||
args: --all -- --check
|
args: --all -- --check
|
||||||
|
45
.prettierignore
Normal file
45
.prettierignore
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package-lock.json
|
||||||
|
node_modules/
|
||||||
|
yarn.lock
|
||||||
|
*.lock
|
||||||
|
|
||||||
|
casks/
|
||||||
|
|
||||||
|
# rust
|
||||||
|
src-tauri/
|
||||||
|
target/
|
||||||
|
Cargo.lock
|
||||||
|
*.toml
|
||||||
|
|
||||||
|
# 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?
|
||||||
|
|
||||||
|
assets/**
|
||||||
|
public/**
|
||||||
|
|
||||||
|
.gitattributes
|
||||||
|
.gitignore
|
||||||
|
.prettierignore
|
||||||
|
|
||||||
|
LICENSE
|
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "all",
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
[![ChatGPT downloads](https://img.shields.io/github/downloads/lencx/ChatGPT/total.svg?style=flat-square)](https://github.com/lencx/ChatGPT/releases)
|
[![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)
|
[![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_)
|
[![lencx](https://img.shields.io/badge/follow-lencx__-blue?style=flat&logo=Twitter)](https://twitter.com/lencx_)
|
||||||
|
|
||||||
<!-- [![lencx](https://img.shields.io/twitter/follow/lencx_.svg?style=social)](https://twitter.com/lencx_) -->
|
<!-- [![lencx](https://img.shields.io/twitter/follow/lencx_.svg?style=social)](https://twitter.com/lencx_) -->
|
||||||
|
|
||||||
<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: 40px !important;width: 145px !important;" ></a>
|
<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: 40px !important;width: 145px !important;" ></a>
|
||||||
@ -25,6 +26,7 @@
|
|||||||
|
|
||||||
- [ChatGPT_0.9.2_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.9.2/ChatGPT_0.9.2_x64_en-US.msi):
|
- [ChatGPT_0.9.2_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.9.2/ChatGPT_0.9.2_x64_en-US.msi):
|
||||||
- 使用 [winget](https://winstall.app/apps/lencx.ChatGPT):
|
- 使用 [winget](https://winstall.app/apps/lencx.ChatGPT):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# install the latest version
|
# install the latest version
|
||||||
winget install --id=lencx.ChatGPT -e
|
winget install --id=lencx.ChatGPT -e
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
[![ChatGPT downloads](https://img.shields.io/github/downloads/lencx/ChatGPT/total.svg?style=flat-square)](https://github.com/lencx/ChatGPT/releases)
|
[![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)
|
[![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_)
|
[![lencx](https://img.shields.io/badge/follow-lencx__-blue?style=flat&logo=Twitter)](https://twitter.com/lencx_)
|
||||||
|
|
||||||
<!-- [![lencx](https://img.shields.io/twitter/follow/lencx_.svg?style=social)](https://twitter.com/lencx_) -->
|
<!-- [![lencx](https://img.shields.io/twitter/follow/lencx_.svg?style=social)](https://twitter.com/lencx_) -->
|
||||||
|
|
||||||
<!-- [![中文版 badge](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-Traditional%20Chinese-blue)](./README-ZH.md) -->
|
<!-- [![中文版 badge](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-Traditional%20Chinese-blue)](./README-ZH.md) -->
|
||||||
@ -27,6 +28,7 @@
|
|||||||
|
|
||||||
- [ChatGPT_0.9.2_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.9.2/ChatGPT_0.9.2_x64_en-US.msi): Direct download installer
|
- [ChatGPT_0.9.2_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.9.2/ChatGPT_0.9.2_x64_en-US.msi): Direct download installer
|
||||||
- Use [winget](https://winstall.app/apps/lencx.ChatGPT):
|
- Use [winget](https://winstall.app/apps/lencx.ChatGPT):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# install the latest version
|
# install the latest version
|
||||||
winget install --id=lencx.ChatGPT -e
|
winget install --id=lencx.ChatGPT -e
|
||||||
@ -174,6 +176,7 @@ Currently, only json and csv are supported for synchronizing custom files, and t
|
|||||||
## 📌 TODO
|
## 📌 TODO
|
||||||
|
|
||||||
<!-- - Web access capability ([#20](https://github.com/lencx/ChatGPT/issues/20)) -->
|
<!-- - Web access capability ([#20](https://github.com/lencx/ChatGPT/issues/20)) -->
|
||||||
|
|
||||||
- `Control Center` enhancement
|
- `Control Center` enhancement
|
||||||
- `Pop-up Search` enhancement
|
- `Pop-up Search` enhancement
|
||||||
- ...
|
- ...
|
||||||
|
@ -11,15 +11,18 @@ fix: slash command does not work
|
|||||||
## v0.9.0
|
## v0.9.0
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|
||||||
- export button does not work
|
- export button does not work
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- add an export markdown button
|
- add an export markdown button
|
||||||
- `Control Center` adds `Notes` and `Download` menus for managing exported chat files (Markdown, PNG, PDF). `Notes` supports markdown previews.
|
- `Control Center` adds `Notes` and `Download` menus for managing exported chat files (Markdown, PNG, PDF). `Notes` supports markdown previews.
|
||||||
|
|
||||||
## v0.8.1
|
## v0.8.1
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|
||||||
- export button keeps blinking
|
- export button keeps blinking
|
||||||
- export button in the old chat does not work
|
- export button in the old chat does not work
|
||||||
- disable export sharing links because it is a security risk
|
- disable export sharing links because it is a security risk
|
||||||
@ -27,22 +30,26 @@ fix:
|
|||||||
## v0.8.0
|
## v0.8.0
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- theme enhancement (Light, Dark, System)
|
- theme enhancement (Light, Dark, System)
|
||||||
- automatic updates support `silent` settings
|
- automatic updates support `silent` settings
|
||||||
- pop-up search: select the ChatGPT content with the mouse, the `DALL·E 2` button appears, and click to jump (note: because the search content filled by the script cannot trigger the event directly, you need to enter a space in the input box to make the button clickable).
|
- pop-up search: select the ChatGPT content with the mouse, the `DALL·E 2` button appears, and click to jump (note: because the search content filled by the script cannot trigger the event directly, you need to enter a space in the input box to make the button clickable).
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|
||||||
- close the main window and hide it in the tray (windows systems)
|
- close the main window and hide it in the tray (windows systems)
|
||||||
|
|
||||||
## v0.7.4
|
## v0.7.4
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|
||||||
- trying to resolve linux errors: `error while loading shared libraries`
|
- trying to resolve linux errors: `error while loading shared libraries`
|
||||||
- customize global shortcuts (`Menu -> Preferences -> Control Center -> General -> Global Shortcut`)
|
- customize global shortcuts (`Menu -> Preferences -> Control Center -> General -> Global Shortcut`)
|
||||||
|
|
||||||
## v0.7.3
|
## v0.7.3
|
||||||
|
|
||||||
chore:
|
chore:
|
||||||
|
|
||||||
- optimize slash command style
|
- optimize slash command style
|
||||||
- optimize tray menu icon and button icons
|
- optimize tray menu icon and button icons
|
||||||
- global shortcuts to the chatgpt app (mac: `Command + Shift + O`, windows: `Ctrl + Shift + O`)
|
- global shortcuts to the chatgpt app (mac: `Command + Shift + O`, windows: `Ctrl + Shift + O`)
|
||||||
@ -54,6 +61,7 @@ fix: some windows systems cannot start the application
|
|||||||
## v0.7.1
|
## v0.7.1
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|
||||||
- some windows systems cannot start the application
|
- some windows systems cannot start the application
|
||||||
- windows and linux add about menu (show version information)
|
- windows and linux add about menu (show version information)
|
||||||
- the tray icon is indistinguishable from the background in dark mode on window and linux
|
- the tray icon is indistinguishable from the background in dark mode on window and linux
|
||||||
@ -61,10 +69,12 @@ fix:
|
|||||||
## v0.7.0
|
## v0.7.0
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|
||||||
- mac m1 copy/paste does not work on some system versions
|
- mac m1 copy/paste does not work on some system versions
|
||||||
- optimize the save chat log button to a small icon, the tray window no longer provides a save chat log button (the buttons causes the input area to become larger and the content area to become smaller)
|
- optimize the save chat log button to a small icon, the tray window no longer provides a save chat log button (the buttons causes the input area to become larger and the content area to become smaller)
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- use the keyboard `⇧` (arrow up) and `⇩` (arrow down) keys to select the slash command
|
- use the keyboard `⇧` (arrow up) and `⇩` (arrow down) keys to select the slash command
|
||||||
<!-- - global shortcuts to the chatgpt app (mac: command+shift+o, windows: ctrl+shift+o) -->
|
<!-- - global shortcuts to the chatgpt app (mac: command+shift+o, windows: ctrl+shift+o) -->
|
||||||
|
|
||||||
@ -77,6 +87,7 @@ fix: sync failure on windows
|
|||||||
fix: path not allowed on the configured scope
|
fix: path not allowed on the configured scope
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- optimize the generated pdf file size
|
- optimize the generated pdf file size
|
||||||
- menu added `Sync Prompts`
|
- menu added `Sync Prompts`
|
||||||
- `Control Center` added `Sync Custom`
|
- `Control Center` added `Sync Custom`
|
||||||
@ -86,6 +97,7 @@ feat:
|
|||||||
## v0.6.0
|
## v0.6.0
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|
||||||
- windows show Chinese when upgrading
|
- windows show Chinese when upgrading
|
||||||
|
|
||||||
## v0.5.1
|
## v0.5.1
|
||||||
@ -103,11 +115,13 @@ add chatgpt log (path: `~/.chatgpt/chatgpt.log`)
|
|||||||
## v0.4.1
|
## v0.4.1
|
||||||
|
|
||||||
fix:
|
fix:
|
||||||
|
|
||||||
- tray window style optimization
|
- tray window style optimization
|
||||||
|
|
||||||
## v0.4.0
|
## v0.4.0
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- customize the ChatGPT prompts command (https://github.com/lencx/ChatGPT#-announcement)
|
- customize the ChatGPT prompts command (https://github.com/lencx/ChatGPT#-announcement)
|
||||||
- menu enhancement: hide application icons from the Dock (support macOS only)
|
- menu enhancement: hide application icons from the Dock (support macOS only)
|
||||||
|
|
||||||
@ -116,12 +130,14 @@ feat:
|
|||||||
fix: can't open ChatGPT
|
fix: can't open ChatGPT
|
||||||
|
|
||||||
feat: menu enhancement
|
feat: menu enhancement
|
||||||
|
|
||||||
- the control center of ChatGPT application
|
- the control center of ChatGPT application
|
||||||
- open the configuration file directory
|
- open the configuration file directory
|
||||||
|
|
||||||
## v0.2.2
|
## v0.2.2
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- menu: go to config
|
- menu: go to config
|
||||||
|
|
||||||
## v0.2.1
|
## v0.2.1
|
||||||
@ -131,12 +147,14 @@ feat: menu optimization
|
|||||||
## v0.2.0
|
## v0.2.0
|
||||||
|
|
||||||
feat: menu enhancement
|
feat: menu enhancement
|
||||||
|
|
||||||
- customize user-agent to prevent security detection interception
|
- customize user-agent to prevent security detection interception
|
||||||
- clear all chatgpt configuration files
|
- clear all chatgpt configuration files
|
||||||
|
|
||||||
## v0.1.8
|
## v0.1.8
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- menu enhancement: theme, titlebar
|
- menu enhancement: theme, titlebar
|
||||||
- modify website address
|
- modify website address
|
||||||
|
|
||||||
@ -147,6 +165,7 @@ feat: tray window
|
|||||||
## v0.1.6
|
## v0.1.6
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- stay on top
|
- stay on top
|
||||||
- export ChatGPT history
|
- export ChatGPT history
|
||||||
|
|
||||||
@ -157,6 +176,7 @@ fix: mac can't use shortcut keys
|
|||||||
## v0.1.4
|
## v0.1.4
|
||||||
|
|
||||||
feat:
|
feat:
|
||||||
|
|
||||||
- beautify icons
|
- beautify icons
|
||||||
- add system tray menu
|
- add system tray menu
|
||||||
|
|
||||||
|
13
package.json
13
package.json
@ -12,8 +12,11 @@
|
|||||||
"fix:tray": "tr override --json.tauri_systemTray_iconPath=\"icons/tray-icon-light.png\" --json.tauri_systemTray_iconAsTemplate=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",
|
"fix:tray:mac": "tr override --json.tauri_systemTray_iconPath=\"icons/tray-icon.png\" --json.tauri_systemTray_iconAsTemplate=true",
|
||||||
"download": "node ./scripts/download.js",
|
"download": "node ./scripts/download.js",
|
||||||
|
"fmt:rs": "cargo fmt",
|
||||||
"tr": "tr",
|
"tr": "tr",
|
||||||
"tauri": "tauri"
|
"tauri": "tauri",
|
||||||
|
"prettier": "prettier -c --write '**/*'",
|
||||||
|
"pretty-quick": "pretty-quick"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "lencx <cxin1314@gmail.com>",
|
"author": "lencx <cxin1314@gmail.com>",
|
||||||
@ -32,6 +35,11 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/lencx/ChatGPT"
|
"url": "https://github.com/lencx/ChatGPT"
|
||||||
},
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "pretty-quick --staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.8.0",
|
"@ant-design/icons": "^4.8.0",
|
||||||
"@monaco-editor/react": "^4.4.6",
|
"@monaco-editor/react": "^4.4.6",
|
||||||
@ -59,6 +67,9 @@
|
|||||||
"@types/react-syntax-highlighter": "^15.5.6",
|
"@types/react-syntax-highlighter": "^15.5.6",
|
||||||
"@types/uuid": "^9.0.0",
|
"@types/uuid": "^9.0.0",
|
||||||
"@vitejs/plugin-react": "^3.0.0",
|
"@vitejs/plugin-react": "^3.0.0",
|
||||||
|
"husky": "^8.0.3",
|
||||||
|
"prettier": "^2.8.3",
|
||||||
|
"pretty-quick": "^3.1.3",
|
||||||
"sass": "^1.56.2",
|
"sass": "^1.56.2",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4",
|
||||||
"vite": "^4.0.0",
|
"vite": "^4.0.0",
|
||||||
|
2
scripts/download.js
vendored
2
scripts/download.js
vendored
@ -30,4 +30,4 @@ async function init() {
|
|||||||
rewrite('README-ZH_CN.md');
|
rewrite('README-ZH_CN.md');
|
||||||
}
|
}
|
||||||
|
|
||||||
init().catch(console.error);
|
init().catch(console.error);
|
||||||
|
17
src/components/FilePath/index.tsx
vendored
17
src/components/FilePath/index.tsx
vendored
@ -22,15 +22,20 @@ const FilePath: FC<FilePathProps> = ({ className, label = 'PATH', paths = '', ur
|
|||||||
setPath(url);
|
setPath(url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setPath(await path.join(await chatRoot(), ...paths.split('/').filter(i => !!i)));
|
setPath(await path.join(await chatRoot(), ...paths.split('/').filter((i) => !!i)));
|
||||||
})()
|
})();
|
||||||
}, [url, paths])
|
}, [url, paths]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx(className, 'chat-file-path')}>
|
<div className={clsx(className, 'chat-file-path')}>
|
||||||
<div>{label}: <a onClick={() => shell.open(filePath)} title={filePath}>{content ? content : filePath}</a></div>
|
<div>
|
||||||
|
{label}:{' '}
|
||||||
|
<a onClick={() => shell.open(filePath)} title={filePath}>
|
||||||
|
{content ? content : filePath}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default FilePath;
|
export default FilePath;
|
||||||
|
18
src/components/Markdown/Editor.tsx
vendored
18
src/components/Markdown/Editor.tsx
vendored
@ -1,5 +1,5 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import Editor from "@monaco-editor/react";
|
import Editor from '@monaco-editor/react';
|
||||||
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
||||||
|
|
||||||
import Markdown from '@/components/Markdown';
|
import Markdown from '@/components/Markdown';
|
||||||
@ -17,12 +17,12 @@ const MarkdownEditor: FC<MarkdownEditorProps> = ({ value = '', onChange, mode =
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setContent(value);
|
setContent(value);
|
||||||
onChange && onChange(value);
|
onChange && onChange(value);
|
||||||
}, [value])
|
}, [value]);
|
||||||
|
|
||||||
const handleEdit = (e: any) => {
|
const handleEdit = (e: any) => {
|
||||||
setContent(e);
|
setContent(e);
|
||||||
onChange && onChange(e);
|
onChange && onChange(e);
|
||||||
}
|
};
|
||||||
|
|
||||||
const isSplit = mode === 'split';
|
const isSplit = mode === 'split';
|
||||||
|
|
||||||
@ -31,11 +31,7 @@ const MarkdownEditor: FC<MarkdownEditorProps> = ({ value = '', onChange, mode =
|
|||||||
<PanelGroup direction="horizontal">
|
<PanelGroup direction="horizontal">
|
||||||
{['md', 'split'].includes(mode) && (
|
{['md', 'split'].includes(mode) && (
|
||||||
<Panel>
|
<Panel>
|
||||||
<Editor
|
<Editor language="markdown" value={content} onChange={handleEdit} />
|
||||||
language="markdown"
|
|
||||||
value={content}
|
|
||||||
onChange={handleEdit}
|
|
||||||
/>
|
|
||||||
</Panel>
|
</Panel>
|
||||||
)}
|
)}
|
||||||
{isSplit && <PanelResizeHandle className="resize-handle" />}
|
{isSplit && <PanelResizeHandle className="resize-handle" />}
|
||||||
@ -44,9 +40,9 @@ const MarkdownEditor: FC<MarkdownEditorProps> = ({ value = '', onChange, mode =
|
|||||||
<Markdown className="edit-preview">{content}</Markdown>
|
<Markdown className="edit-preview">{content}</Markdown>
|
||||||
</Panel>
|
</Panel>
|
||||||
)}
|
)}
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MarkdownEditor;
|
export default MarkdownEditor;
|
||||||
|
7
src/components/Markdown/index.scss
vendored
7
src/components/Markdown/index.scss
vendored
@ -1,15 +1,16 @@
|
|||||||
.markdown-body {
|
.markdown-body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial,
|
||||||
|
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
|
||||||
|
|
||||||
&.edit-preview {
|
&.edit-preview {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre, code {
|
pre,
|
||||||
|
code {
|
||||||
font-family: monospace, monospace;
|
font-family: monospace, monospace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
src/components/Markdown/index.tsx
vendored
15
src/components/Markdown/index.tsx
vendored
@ -13,7 +13,6 @@ interface MarkdownProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Markdown: FC<MarkdownProps> = ({ children, className }) => {
|
const Markdown: FC<MarkdownProps> = ({ children, className }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx(className, 'markdown-body')}>
|
<div className={clsx(className, 'markdown-body')}>
|
||||||
<div>
|
<div>
|
||||||
@ -22,8 +21,8 @@ const Markdown: FC<MarkdownProps> = ({ children, className }) => {
|
|||||||
linkTarget="_blank"
|
linkTarget="_blank"
|
||||||
remarkPlugins={[remarkGfm]}
|
remarkPlugins={[remarkGfm]}
|
||||||
components={{
|
components={{
|
||||||
code({node, inline, className, children, ...props}) {
|
code({ node, inline, className, children, ...props }) {
|
||||||
const match = /language-(\w+)/.exec(className || '')
|
const match = /language-(\w+)/.exec(className || '');
|
||||||
return !inline && match ? (
|
return !inline && match ? (
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
children={String(children).replace(/\n$/, '')}
|
children={String(children).replace(/\n$/, '')}
|
||||||
@ -38,13 +37,13 @@ const Markdown: FC<MarkdownProps> = ({ children, className }) => {
|
|||||||
<code className={className} {...props}>
|
<code className={className} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</code>
|
</code>
|
||||||
)
|
);
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Markdown;
|
export default Markdown;
|
||||||
|
4
src/components/Tags/index.tsx
vendored
4
src/components/Tags/index.tsx
vendored
@ -20,7 +20,7 @@ const Tags: FC<TagsProps> = ({ max = 99, value = [], onChange, addTxt = 'New Tag
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTags(value);
|
setTags(value);
|
||||||
}, [value])
|
}, [value]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inputVisible) {
|
if (inputVisible) {
|
||||||
@ -97,4 +97,4 @@ const Tags: FC<TagsProps> = ({ max = 99, value = [], onChange, addTxt = 'New Tag
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Tags;
|
export default Tags;
|
||||||
|
14
src/hooks/useChatModel.ts
vendored
14
src/hooks/useChatModel.ts
vendored
@ -15,12 +15,12 @@ export default function useChatModel(key: string, file = CHAT_MODEL_JSON) {
|
|||||||
setModelJson(data);
|
setModelJson(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
const modelSet = async (data: Record<string, any>[]|Record<string, any>) => {
|
const modelSet = async (data: Record<string, any>[] | Record<string, any>) => {
|
||||||
const oData = clone(modelJson);
|
const oData = clone(modelJson);
|
||||||
oData[key] = data;
|
oData[key] = data;
|
||||||
await writeJSON(file, oData);
|
await writeJSON(file, oData);
|
||||||
setModelJson(oData);
|
setModelJson(oData);
|
||||||
}
|
};
|
||||||
|
|
||||||
return { modelJson, modelSet, modelData: modelJson?.[key] || [] };
|
return { modelJson, modelSet, modelData: modelJson?.[key] || [] };
|
||||||
}
|
}
|
||||||
@ -40,15 +40,19 @@ export function useCacheModel(file = '') {
|
|||||||
await writeJSON(newFile ? newFile : file, data, { isRoot: true });
|
await writeJSON(newFile ? newFile : file, data, { isRoot: true });
|
||||||
setModelCacheJson(data);
|
setModelCacheJson(data);
|
||||||
await modelCacheCmd();
|
await modelCacheCmd();
|
||||||
}
|
};
|
||||||
|
|
||||||
const modelCacheCmd = async () => {
|
const modelCacheCmd = async () => {
|
||||||
// Generate the `chat.model.cmd.json` file and refresh the page for the slash command to take effect.
|
// Generate the `chat.model.cmd.json` file and refresh the page for the slash command to take effect.
|
||||||
const list = await invoke('cmd_list');
|
const list = await invoke('cmd_list');
|
||||||
await writeJSON(CHAT_MODEL_CMD_JSON, { name: 'ChatGPT CMD', last_updated: Date.now(), data: list });
|
await writeJSON(CHAT_MODEL_CMD_JSON, {
|
||||||
|
name: 'ChatGPT CMD',
|
||||||
|
last_updated: Date.now(),
|
||||||
|
data: list,
|
||||||
|
});
|
||||||
await invoke('window_reload', { label: 'core' });
|
await invoke('window_reload', { label: 'core' });
|
||||||
await invoke('window_reload', { label: 'tray' });
|
await invoke('window_reload', { label: 'tray' });
|
||||||
};
|
};
|
||||||
|
|
||||||
return { modelCacheJson, modelCacheSet, modelCacheCmd };
|
return { modelCacheJson, modelCacheSet, modelCacheCmd };
|
||||||
}
|
}
|
||||||
|
32
src/hooks/useColumns.tsx
vendored
32
src/hooks/useColumns.tsx
vendored
@ -5,7 +5,7 @@ import { DISABLE_AUTO_COMPLETE } from '@/utils';
|
|||||||
|
|
||||||
export default function useColumns(columns: any[] = []) {
|
export default function useColumns(columns: any[] = []) {
|
||||||
const [opType, setOpType] = useState('');
|
const [opType, setOpType] = useState('');
|
||||||
const [opRecord, setRecord] = useState<Record<string|symbol, any> | null>(null);
|
const [opRecord, setRecord] = useState<Record<string | symbol, any> | null>(null);
|
||||||
const [opTime, setNow] = useState<number | null>(null);
|
const [opTime, setNow] = useState<number | null>(null);
|
||||||
const [opExtra, setExtra] = useState<any>(null);
|
const [opExtra, setExtra] = useState<any>(null);
|
||||||
|
|
||||||
@ -58,26 +58,26 @@ export const EditRow: FC<EditRowProps> = ({ rowKey, row, actions }) => {
|
|||||||
setEdit(true);
|
setEdit(true);
|
||||||
};
|
};
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setVal(e.target.value)
|
setVal(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
setEdit(false);
|
setEdit(false);
|
||||||
row[rowKey] = val?.trim();
|
row[rowKey] = val?.trim();
|
||||||
actions?.setRecord(row, 'rowedit')
|
actions?.setRecord(row, 'rowedit');
|
||||||
};
|
};
|
||||||
|
|
||||||
return isEdit
|
return isEdit ? (
|
||||||
? (
|
<Input
|
||||||
<Input
|
value={val}
|
||||||
value={val}
|
autoFocus
|
||||||
autoFocus
|
onChange={handleChange}
|
||||||
onChange={handleChange}
|
{...DISABLE_AUTO_COMPLETE}
|
||||||
{...DISABLE_AUTO_COMPLETE}
|
onPressEnter={handleSave}
|
||||||
onPressEnter={handleSave}
|
/>
|
||||||
/>
|
) : (
|
||||||
)
|
<div className="rowedit" onClick={handleEdit}>
|
||||||
: (
|
{val}
|
||||||
<div className='rowedit' onClick={handleEdit}>{val}</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
10
src/hooks/useData.ts
vendored
10
src/hooks/useData.ts
vendored
@ -8,7 +8,7 @@ export default function useData(oData: any[]) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
opInit(oData);
|
opInit(oData);
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const opAdd = (val: any) => {
|
const opAdd = (val: any) => {
|
||||||
const v = [val, ...opData];
|
const v = [val, ...opData];
|
||||||
@ -18,19 +18,19 @@ export default function useData(oData: any[]) {
|
|||||||
|
|
||||||
const opInit = (val: any[] = []) => {
|
const opInit = (val: any[] = []) => {
|
||||||
if (!val || !Array.isArray(val)) return;
|
if (!val || !Array.isArray(val)) return;
|
||||||
const nData = val.map(i => ({ [safeKey]: v4(), ...i }));
|
const nData = val.map((i) => ({ [safeKey]: v4(), ...i }));
|
||||||
setData(nData);
|
setData(nData);
|
||||||
};
|
};
|
||||||
|
|
||||||
const opRemove = (id: string) => {
|
const opRemove = (id: string) => {
|
||||||
const nData = opData.filter(i => i[safeKey] !== id);
|
const nData = opData.filter((i) => i[safeKey] !== id);
|
||||||
setData(nData);
|
setData(nData);
|
||||||
return nData;
|
return nData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const opReplace = (id: string, data: any) => {
|
const opReplace = (id: string, data: any) => {
|
||||||
const nData = [...opData];
|
const nData = [...opData];
|
||||||
const idx = opData.findIndex(v => v[safeKey] === id);
|
const idx = opData.findIndex((v) => v[safeKey] === id);
|
||||||
nData[idx] = data;
|
nData[idx] = data;
|
||||||
setData(nData);
|
setData(nData);
|
||||||
return nData;
|
return nData;
|
||||||
@ -52,4 +52,4 @@ export default function useData(oData: any[]) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return { opSafeKey: safeKey, opInit, opReplace, opAdd, opRemove, opData, opReplaceItems };
|
return { opSafeKey: safeKey, opInit, opReplace, opAdd, opRemove, opData, opReplaceItems };
|
||||||
}
|
}
|
||||||
|
4
src/hooks/useInit.ts
vendored
4
src/hooks/useInit.ts
vendored
@ -8,5 +8,5 @@ export default function useInit(callback: () => void) {
|
|||||||
callback();
|
callback();
|
||||||
isInit.current = false;
|
isInit.current = false;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
17
src/hooks/useTable.tsx
vendored
17
src/hooks/useTable.tsx
vendored
@ -7,14 +7,17 @@ import { safeKey } from '@/hooks/useData';
|
|||||||
type rowSelectionOptions = {
|
type rowSelectionOptions = {
|
||||||
key: 'id' | string;
|
key: 'id' | string;
|
||||||
rowType: 'id' | 'row' | 'all';
|
rowType: 'id' | 'row' | 'all';
|
||||||
}
|
};
|
||||||
export function useTableRowSelection(options: Partial<rowSelectionOptions> = {}) {
|
export function useTableRowSelection(options: Partial<rowSelectionOptions> = {}) {
|
||||||
const { key = 'id', rowType = 'id' } = options;
|
const { key = 'id', rowType = 'id' } = options;
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
const [selectedRowIDs, setSelectedRowIDs] = useState<string[]>([]);
|
const [selectedRowIDs, setSelectedRowIDs] = useState<string[]>([]);
|
||||||
const [selectedRows, setSelectedRows] = useState<Record<string|symbol, any>[]>([]);
|
const [selectedRows, setSelectedRows] = useState<Record<string | symbol, any>[]>([]);
|
||||||
|
|
||||||
const onSelectChange = (newSelectedRowKeys: React.Key[], newSelectedRows: Record<string|symbol, any>[]) => {
|
const onSelectChange = (
|
||||||
|
newSelectedRowKeys: React.Key[],
|
||||||
|
newSelectedRows: Record<string | symbol, any>[],
|
||||||
|
) => {
|
||||||
const keys = newSelectedRows.map((i: any) => i[safeKey] || i[key]);
|
const keys = newSelectedRows.map((i: any) => i[safeKey] || i[key]);
|
||||||
setSelectedRowKeys(newSelectedRowKeys);
|
setSelectedRowKeys(newSelectedRowKeys);
|
||||||
if (rowType === 'id') {
|
if (rowType === 'id') {
|
||||||
@ -38,11 +41,7 @@ export function useTableRowSelection(options: Partial<rowSelectionOptions> = {})
|
|||||||
const rowSelection: TableRowSelection<Record<string, any>> = {
|
const rowSelection: TableRowSelection<Record<string, any>> = {
|
||||||
selectedRowKeys,
|
selectedRowKeys,
|
||||||
onChange: onSelectChange,
|
onChange: onSelectChange,
|
||||||
selections: [
|
selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT, Table.SELECTION_NONE],
|
||||||
Table.SELECTION_ALL,
|
|
||||||
Table.SELECTION_INVERT,
|
|
||||||
Table.SELECTION_NONE,
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return { rowSelection, selectedRowIDs, selectedRows, rowReset };
|
return { rowSelection, selectedRowIDs, selectedRows, rowReset };
|
||||||
@ -55,4 +54,4 @@ export const TABLE_PAGINATION = {
|
|||||||
defaultPageSize: 10,
|
defaultPageSize: 10,
|
||||||
pageSizeOptions: [5, 10, 15, 20],
|
pageSizeOptions: [5, 10, 15, 20],
|
||||||
showTotal: (total: number) => <span>Total {total} items</span>,
|
showTotal: (total: number) => <span>Total {total} items</span>,
|
||||||
};
|
};
|
||||||
|
26
src/icons/SplitIcon.tsx
vendored
26
src/icons/SplitIcon.tsx
vendored
@ -1,4 +1,4 @@
|
|||||||
import Icon from "@ant-design/icons";
|
import Icon from '@ant-design/icons';
|
||||||
import type { CustomIconComponentProps } from '@ant-design/icons/lib/components/Icon';
|
import type { CustomIconComponentProps } from '@ant-design/icons/lib/components/Icon';
|
||||||
|
|
||||||
interface IconProps {
|
interface IconProps {
|
||||||
@ -6,12 +6,20 @@ interface IconProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function SplitIcon(props: Partial<CustomIconComponentProps & IconProps>) {
|
export default function SplitIcon(props: Partial<CustomIconComponentProps & IconProps>) {
|
||||||
return <Icon
|
return (
|
||||||
{...props}
|
<Icon
|
||||||
component={() => (
|
{...props}
|
||||||
<svg className="chatico" viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor">
|
component={() => (
|
||||||
<path d="M252.068571 906.496h520.283429c89.581714 0 134.144-44.562286 134.144-132.845714V250.331429c0-88.283429-44.562286-132.845714-134.144-132.845715H252.068571c-89.142857 0-134.582857 44.141714-134.582857 132.845715V773.668571c0 88.704 45.44 132.845714 134.582857 132.845715z m1.28-68.992c-42.843429 0-66.852571-22.710857-66.852571-67.291429V253.805714c0-44.580571 24.009143-67.291429 66.852571-67.291428h222.866286v651.008z m517.723429-651.008c42.422857 0 66.432 22.710857 66.432 67.291429V770.194286c0 44.580571-24.009143 67.291429-66.432 67.291428H548.205714V186.496z" />
|
<svg
|
||||||
</svg>
|
className="chatico"
|
||||||
)}
|
viewBox="0 0 1024 1024"
|
||||||
/>
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M252.068571 906.496h520.283429c89.581714 0 134.144-44.562286 134.144-132.845714V250.331429c0-88.283429-44.562286-132.845714-134.144-132.845715H252.068571c-89.142857 0-134.582857 44.141714-134.582857 132.845715V773.668571c0 88.704 45.44 132.845714 134.582857 132.845715z m1.28-68.992c-42.843429 0-66.852571-22.710857-66.852571-67.291429V253.805714c0-44.580571 24.009143-67.291429 66.852571-67.291428h222.866286v651.008z m517.723429-651.008c42.422857 0 66.432 22.710857 66.432 67.291429V770.194286c0 44.580571-24.009143 67.291429-66.432 67.291428H548.205714V186.496z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
2
src/layout/index.scss
vendored
2
src/layout/index.scss
vendored
@ -36,4 +36,4 @@
|
|||||||
.ant-layout-footer {
|
.ant-layout-footer {
|
||||||
color: #666 !important;
|
color: #666 !important;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
40
src/layout/index.tsx
vendored
40
src/layout/index.tsx
vendored
@ -21,21 +21,21 @@ export default function ChatLayout() {
|
|||||||
setAppInfo({
|
setAppInfo({
|
||||||
appName: await getName(),
|
appName: await getName(),
|
||||||
appVersion: await getVersion(),
|
appVersion: await getVersion(),
|
||||||
appTheme: await invoke("get_theme"),
|
appTheme: await invoke('get_theme'),
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
const checkAppUpdate = async () => {
|
const checkAppUpdate = async () => {
|
||||||
await invoke('run_check_update', { silent: false, hasMsg: true });
|
await invoke('run_check_update', { silent: false, hasMsg: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
const isDark = appInfo.appTheme === "dark";
|
const isDark = appInfo.appTheme === 'dark';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigProvider theme={{ algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm }}>
|
<ConfigProvider theme={{ algorithm: isDark ? theme.darkAlgorithm : theme.defaultAlgorithm }}>
|
||||||
<Layout style={{ minHeight: '100vh' }} hasSider>
|
<Layout style={{ minHeight: '100vh' }} hasSider>
|
||||||
<Sider
|
<Sider
|
||||||
theme={isDark ? "dark" : "light"}
|
theme={isDark ? 'dark' : 'light'}
|
||||||
collapsible
|
collapsible
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
onCollapse={(value) => setCollapsed(value)}
|
onCollapse={(value) => setCollapsed(value)}
|
||||||
@ -49,41 +49,51 @@ export default function ChatLayout() {
|
|||||||
zIndex: 999,
|
zIndex: 999,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="chat-logo"><img src="/logo.png" /></div>
|
<div className="chat-logo">
|
||||||
|
<img src="/logo.png" />
|
||||||
|
</div>
|
||||||
<div className="chat-info">
|
<div className="chat-info">
|
||||||
<Tag>{appInfo.appName}</Tag>
|
<Tag>{appInfo.appName}</Tag>
|
||||||
<Tag>
|
<Tag>
|
||||||
<span style={{ marginRight: 5 }}>{appInfo.appVersion}</span>
|
<span style={{ marginRight: 5 }}>{appInfo.appVersion}</span>
|
||||||
<Tooltip title="click to check update">
|
<Tooltip title="click to check update">
|
||||||
<a onClick={checkAppUpdate}><SyncOutlined /></a>
|
<a onClick={checkAppUpdate}>
|
||||||
</Tooltip>
|
<SyncOutlined />
|
||||||
|
</a>
|
||||||
|
</Tooltip>
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Menu
|
<Menu
|
||||||
defaultSelectedKeys={[location.pathname]}
|
defaultSelectedKeys={[location.pathname]}
|
||||||
mode="inline"
|
mode="inline"
|
||||||
theme={ appInfo.appTheme === "dark" ? "dark" : "light" }
|
theme={appInfo.appTheme === 'dark' ? 'dark' : 'light'}
|
||||||
inlineIndent={12}
|
inlineIndent={12}
|
||||||
items={menuItems}
|
items={menuItems}
|
||||||
// defaultOpenKeys={['/model']}
|
// defaultOpenKeys={['/model']}
|
||||||
onClick={(i) => go(i.key)}
|
onClick={(i) => go(i.key)}
|
||||||
/>
|
/>
|
||||||
</Sider>
|
</Sider>
|
||||||
<Layout className="chat-layout" style={{ marginLeft: collapsed ? 80 : 200, transition: 'margin-left 300ms ease-out' }}>
|
<Layout
|
||||||
|
className="chat-layout"
|
||||||
|
style={{ marginLeft: collapsed ? 80 : 200, transition: 'margin-left 300ms ease-out' }}
|
||||||
|
>
|
||||||
<Content
|
<Content
|
||||||
className="chat-container"
|
className="chat-container"
|
||||||
style={{
|
style={{
|
||||||
overflow: 'inherit'
|
overflow: 'inherit',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Routes />
|
<Routes />
|
||||||
</Content>
|
</Content>
|
||||||
<Footer style={{ textAlign: 'center' }}>
|
<Footer style={{ textAlign: 'center' }}>
|
||||||
<a href="https://github.com/lencx/chatgpt" target="_blank">ChatGPT Desktop Application</a> ©2022 Created by lencx
|
<a href="https://github.com/lencx/chatgpt" target="_blank">
|
||||||
|
ChatGPT Desktop Application
|
||||||
|
</a>{' '}
|
||||||
|
©2022 Created by lencx
|
||||||
</Footer>
|
</Footer>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
5
src/main.scss
vendored
5
src/main.scss
vendored
@ -14,7 +14,8 @@
|
|||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@ -100,4 +101,4 @@ html, body {
|
|||||||
|
|
||||||
.chatico {
|
.chatico {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
4
src/main.tsx
vendored
4
src/main.tsx
vendored
@ -9,8 +9,8 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
|||||||
<StrictMode>
|
<StrictMode>
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Layout/>
|
<Layout />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</StrictMode>
|
</StrictMode>,
|
||||||
);
|
);
|
||||||
|
10
src/routes.tsx
vendored
10
src/routes.tsx
vendored
@ -23,7 +23,7 @@ import Markdown from '@/view/markdown';
|
|||||||
|
|
||||||
export type ChatRouteMetaObject = {
|
export type ChatRouteMetaObject = {
|
||||||
label: string;
|
label: string;
|
||||||
icon?: React.ReactNode,
|
icon?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ChatRouteObject = {
|
type ChatRouteObject = {
|
||||||
@ -32,7 +32,7 @@ type ChatRouteObject = {
|
|||||||
hideMenu?: boolean;
|
hideMenu?: boolean;
|
||||||
meta?: ChatRouteMetaObject;
|
meta?: ChatRouteMetaObject;
|
||||||
children?: ChatRouteObject[];
|
children?: ChatRouteObject[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export const routes: Array<ChatRouteObject> = [
|
export const routes: Array<ChatRouteObject> = [
|
||||||
{
|
{
|
||||||
@ -116,14 +116,14 @@ export const routes: Array<ChatRouteObject> = [
|
|||||||
type MenuItem = Required<MenuProps>['items'][number];
|
type MenuItem = Required<MenuProps>['items'][number];
|
||||||
export const menuItems: MenuItem[] = routes
|
export const menuItems: MenuItem[] = routes
|
||||||
.filter((j) => !j.hideMenu)
|
.filter((j) => !j.hideMenu)
|
||||||
.map(i => ({
|
.map((i) => ({
|
||||||
...i.meta,
|
...i.meta,
|
||||||
key: i.path || '',
|
key: i.path || '',
|
||||||
children: i?.children
|
children: i?.children
|
||||||
?.filter((j) => !j.hideMenu)
|
?.filter((j) => !j.hideMenu)
|
||||||
?.map((j) => ({ ...j.meta, key: `${i.path}/${j.path}` || ''})),
|
?.map((j) => ({ ...j.meta, key: `${i.path}/${j.path}` || '' })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return useRoutes(routes);
|
return useRoutes(routes);
|
||||||
};
|
};
|
||||||
|
60
src/utils.ts
vendored
60
src/utils.ts
vendored
@ -9,27 +9,28 @@ export const CHAT_DOWNLOAD_JSON = 'chat.download.json';
|
|||||||
export const CHAT_AWESOME_JSON = 'chat.awesome.json';
|
export const CHAT_AWESOME_JSON = 'chat.awesome.json';
|
||||||
export const CHAT_NOTES_JSON = 'chat.notes.json';
|
export const CHAT_NOTES_JSON = 'chat.notes.json';
|
||||||
export const CHAT_PROMPTS_CSV = 'chat.prompts.csv';
|
export const CHAT_PROMPTS_CSV = 'chat.prompts.csv';
|
||||||
export const GITHUB_PROMPTS_CSV_URL = 'https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv';
|
export const GITHUB_PROMPTS_CSV_URL =
|
||||||
|
'https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/main/prompts.csv';
|
||||||
|
|
||||||
export const DISABLE_AUTO_COMPLETE = {
|
export const DISABLE_AUTO_COMPLETE = {
|
||||||
autoCapitalize: 'off',
|
autoCapitalize: 'off',
|
||||||
autoComplete: 'off',
|
autoComplete: 'off',
|
||||||
spellCheck: false
|
spellCheck: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const chatRoot = async () => {
|
export const chatRoot = async () => {
|
||||||
return join(await homeDir(), '.chatgpt')
|
return join(await homeDir(), '.chatgpt');
|
||||||
}
|
};
|
||||||
|
|
||||||
export const chatModelPath = async (): Promise<string> => {
|
export const chatModelPath = async (): Promise<string> => {
|
||||||
return join(await chatRoot(), CHAT_MODEL_JSON);
|
return join(await chatRoot(), CHAT_MODEL_JSON);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const chatPromptsPath = async (): Promise<string> => {
|
export const chatPromptsPath = async (): Promise<string> => {
|
||||||
return join(await chatRoot(), CHAT_PROMPTS_CSV);
|
return join(await chatRoot(), CHAT_PROMPTS_CSV);
|
||||||
}
|
};
|
||||||
|
|
||||||
type readJSONOpts = { defaultVal?: Record<string, any>, isRoot?: boolean, isList?: boolean };
|
type readJSONOpts = { defaultVal?: Record<string, any>; isRoot?: boolean; isList?: boolean };
|
||||||
export const readJSON = async (path: string, opts: readJSONOpts = {}) => {
|
export const readJSON = async (path: string, opts: readJSONOpts = {}) => {
|
||||||
const { defaultVal = {}, isRoot = false, isList = false } = opts;
|
const { defaultVal = {}, isRoot = false, isList = false } = opts;
|
||||||
const root = await chatRoot();
|
const root = await chatRoot();
|
||||||
@ -39,26 +40,39 @@ export const readJSON = async (path: string, opts: readJSONOpts = {}) => {
|
|||||||
file = await join(root, path);
|
file = await join(root, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await exists(file)) {
|
if (!(await exists(file))) {
|
||||||
if (await dirname(file) !== root) {
|
if ((await dirname(file)) !== root) {
|
||||||
await createDir(await dirname(file), { recursive: true });
|
await createDir(await dirname(file), { recursive: true });
|
||||||
}
|
}
|
||||||
await writeTextFile(file, isList ? '[]' : JSON.stringify({
|
await writeTextFile(
|
||||||
name: 'ChatGPT',
|
file,
|
||||||
link: 'https://github.com/lencx/ChatGPT',
|
isList
|
||||||
...defaultVal,
|
? '[]'
|
||||||
}, null, 2))
|
: JSON.stringify(
|
||||||
|
{
|
||||||
|
name: 'ChatGPT',
|
||||||
|
link: 'https://github.com/lencx/ChatGPT',
|
||||||
|
...defaultVal,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return JSON.parse(await readTextFile(file));
|
return JSON.parse(await readTextFile(file));
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
type writeJSONOpts = { dir?: string, isRoot?: boolean };
|
type writeJSONOpts = { dir?: string; isRoot?: boolean };
|
||||||
export const writeJSON = async (path: string, data: Record<string, any>, opts: writeJSONOpts = {}) => {
|
export const writeJSON = async (
|
||||||
|
path: string,
|
||||||
|
data: Record<string, any>,
|
||||||
|
opts: writeJSONOpts = {},
|
||||||
|
) => {
|
||||||
const { isRoot = false } = opts;
|
const { isRoot = false } = opts;
|
||||||
const root = await chatRoot();
|
const root = await chatRoot();
|
||||||
let file = path;
|
let file = path;
|
||||||
@ -67,13 +81,17 @@ export const writeJSON = async (path: string, data: Record<string, any>, opts: w
|
|||||||
file = await join(root, path);
|
file = await join(root, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRoot && !await exists(await dirname(file))) {
|
if (isRoot && !(await exists(await dirname(file)))) {
|
||||||
await createDir(await dirname(file), { recursive: true });
|
await createDir(await dirname(file), { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeTextFile(file, JSON.stringify(data, null, 2));
|
await writeTextFile(file, JSON.stringify(data, null, 2));
|
||||||
}
|
};
|
||||||
|
|
||||||
export const fmtDate = (date: any) => dayjs(date).format('YYYY-MM-DD HH:mm:ss');
|
export const fmtDate = (date: any) => dayjs(date).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
|
||||||
export const genCmd = (act: string) => act.replace(/\s+|\/+/g, '_').replace(/[^\d\w]/g, '').toLocaleLowerCase();
|
export const genCmd = (act: string) =>
|
||||||
|
act
|
||||||
|
.replace(/\s+|\/+/g, '_')
|
||||||
|
.replace(/[^\d\w]/g, '')
|
||||||
|
.toLocaleLowerCase();
|
||||||
|
12
src/view/awesome/Form.tsx
vendored
12
src/view/awesome/Form.tsx
vendored
@ -6,7 +6,7 @@ import Tags from '@comps/Tags';
|
|||||||
import { DISABLE_AUTO_COMPLETE } from '@/utils';
|
import { DISABLE_AUTO_COMPLETE } from '@/utils';
|
||||||
|
|
||||||
interface AwesomeFormProps {
|
interface AwesomeFormProps {
|
||||||
record?: Record<string|symbol, any> | null;
|
record?: Record<string | symbol, any> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initFormValue = {
|
const initFormValue = {
|
||||||
@ -28,11 +28,7 @@ const AwesomeForm: ForwardRefRenderFunction<FormProps, AwesomeFormProps> = ({ re
|
|||||||
}, [record]);
|
}, [record]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form form={form} labelCol={{ span: 4 }} initialValues={initFormValue}>
|
||||||
form={form}
|
|
||||||
labelCol={{ span: 4 }}
|
|
||||||
initialValues={initFormValue}
|
|
||||||
>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Title"
|
label="Title"
|
||||||
name="title"
|
name="title"
|
||||||
@ -57,7 +53,7 @@ const AwesomeForm: ForwardRefRenderFunction<FormProps, AwesomeFormProps> = ({ re
|
|||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default forwardRef(AwesomeForm);
|
export default forwardRef(AwesomeForm);
|
||||||
|
14
src/view/awesome/config.tsx
vendored
14
src/view/awesome/config.tsx
vendored
@ -34,7 +34,7 @@ export const awesomeColumns = () => [
|
|||||||
dataIndex: 'category',
|
dataIndex: 'category',
|
||||||
key: 'category',
|
key: 'category',
|
||||||
width: 120,
|
width: 120,
|
||||||
render: (v: string) => <Tag color="geekblue">{v}</Tag>
|
render: (v: string) => <Tag color="geekblue">{v}</Tag>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Tags',
|
title: 'Tags',
|
||||||
@ -42,7 +42,11 @@ export const awesomeColumns = () => [
|
|||||||
key: 'tags',
|
key: 'tags',
|
||||||
width: 150,
|
width: 150,
|
||||||
render: (v: string[]) => (
|
render: (v: string[]) => (
|
||||||
<span className="chat-tags">{v?.map(i => <Tag key={i}>{i}</Tag>)}</span>
|
<span className="chat-tags">
|
||||||
|
{v?.map((i) => (
|
||||||
|
<Tag key={i}>{i}</Tag>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -62,7 +66,7 @@ export const awesomeColumns = () => [
|
|||||||
<a>Delete</a>
|
<a>Delete</a>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
54
src/view/awesome/index.tsx
vendored
54
src/view/awesome/index.tsx
vendored
@ -34,7 +34,8 @@ export default function Awesome() {
|
|||||||
updateJson(data);
|
updateJson(data);
|
||||||
opInfo.resetRecord();
|
opInfo.resetRecord();
|
||||||
}
|
}
|
||||||
}, [opInfo.opType, formRef]);
|
}, [opInfo.opType,
|
||||||
|
formRef]);
|
||||||
|
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
@ -46,45 +47,46 @@ export default function Awesome() {
|
|||||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
||||||
updateJson(data);
|
updateJson(data);
|
||||||
}
|
}
|
||||||
}, [opInfo.opTime])
|
}, [opInfo.opTime]);
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
formRef.current?.form?.validateFields()
|
formRef.current?.form?.validateFields().then(async (vals: Record<string, any>) => {
|
||||||
.then(async (vals: Record<string, any>) => {
|
if (opInfo.opType === 'new') {
|
||||||
if (opInfo.opType === 'new') {
|
const data = opAdd(vals);
|
||||||
const data = opAdd(vals);
|
await updateJson(data);
|
||||||
await updateJson(data);
|
opInit(data);
|
||||||
opInit(data);
|
message.success('Data added successfully');
|
||||||
message.success('Data added successfully');
|
}
|
||||||
}
|
if (opInfo.opType === 'edit') {
|
||||||
if (opInfo.opType === 'edit') {
|
const data = opReplace(opInfo?.opRecord?.[opSafeKey], vals);
|
||||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], vals);
|
await updateJson(data);
|
||||||
await updateJson(data);
|
message.success('Data updated successfully');
|
||||||
message.success('Data updated successfully');
|
}
|
||||||
}
|
hide();
|
||||||
hide();
|
});
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEnable = (isEnable: boolean) => {
|
const handleEnable = (isEnable: boolean) => {
|
||||||
const data = opReplaceItems(selectedRowIDs, { enable: isEnable })
|
const data = opReplaceItems(selectedRowIDs, { enable: isEnable });
|
||||||
updateJson(data);
|
updateJson(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const modalTitle = `${({ new: 'Create', edit: 'Edit' })[opInfo.opType]} URL`;
|
const modalTitle = `${{ new: 'Create', edit: 'Edit' }[opInfo.opType]} URL`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="chat-table-btns">
|
<div className="chat-table-btns">
|
||||||
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>Add URL</Button>
|
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>
|
||||||
|
Add URL
|
||||||
|
</Button>
|
||||||
<div>
|
<div>
|
||||||
{selectedItems.length > 0 && (
|
{selectedItems.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Button type="primary" onClick={() => handleEnable(true)}>Enable</Button>
|
<Button type="primary" onClick={() => handleEnable(true)}>
|
||||||
|
Enable
|
||||||
|
</Button>
|
||||||
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
overlayStyle={{ width: 250 }}
|
overlayStyle={{ width: 250 }}
|
||||||
@ -121,5 +123,5 @@ export default function Awesome() {
|
|||||||
<AwesomeForm ref={formRef} record={opInfo?.opRecord} />
|
<AwesomeForm ref={formRef} record={opInfo?.opRecord} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
18
src/view/download/config.tsx
vendored
18
src/view/download/config.tsx
vendored
@ -10,7 +10,7 @@ import { fmtDate, chatRoot } from '@/utils';
|
|||||||
const colorMap: any = {
|
const colorMap: any = {
|
||||||
pdf: 'blue',
|
pdf: 'blue',
|
||||||
png: 'orange',
|
png: 'orange',
|
||||||
}
|
};
|
||||||
|
|
||||||
export const downloadColumns = () => [
|
export const downloadColumns = () => [
|
||||||
{
|
{
|
||||||
@ -61,20 +61,22 @@ export const downloadColumns = () => [
|
|||||||
<a>Delete</a>
|
<a>Delete</a>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const RenderPath = ({ row }: any) => {
|
const RenderPath = ({ row }: any) => {
|
||||||
const [filePath, setFilePath] = useState('');
|
const [filePath, setFilePath] = useState('');
|
||||||
useInit(async () => {
|
useInit(async () => {
|
||||||
setFilePath(await getPath(row));
|
setFilePath(await getPath(row));
|
||||||
})
|
});
|
||||||
return <a onClick={() => shell.open(filePath)}>{filePath}</a>;
|
return <a onClick={() => shell.open(filePath)}>{filePath}</a>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPath = async (row: any) => {
|
export const getPath = async (row: any) => {
|
||||||
const isImg = ['png'].includes(row?.ext);
|
const isImg = ['png'].includes(row?.ext);
|
||||||
return await path.join(await chatRoot(), 'download', isImg ? 'img' : row.ext, row.id) + `.${row.ext}`;
|
return (
|
||||||
}
|
(await path.join(await chatRoot(), 'download', isImg ? 'img' : row.ext, row.id)) + `.${row.ext}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
24
src/view/download/index.tsx
vendored
24
src/view/download/index.tsx
vendored
@ -37,7 +37,12 @@ export default function Download() {
|
|||||||
(async () => {
|
(async () => {
|
||||||
const record = opInfo?.opRecord;
|
const record = opInfo?.opRecord;
|
||||||
const isImg = ['png'].includes(record?.ext);
|
const isImg = ['png'].includes(record?.ext);
|
||||||
const file = await path.join(await chatRoot(), 'download', isImg ? 'img' : record?.ext, `${record?.id}.${record?.ext}`);
|
const file = await path.join(
|
||||||
|
await chatRoot(),
|
||||||
|
'download',
|
||||||
|
isImg ? 'img' : record?.ext,
|
||||||
|
`${record?.id}.${record?.ext}`,
|
||||||
|
);
|
||||||
if (opInfo.opType === 'preview') {
|
if (opInfo.opType === 'preview') {
|
||||||
const data = await fs.readBinaryFile(file);
|
const data = await fs.readBinaryFile(file);
|
||||||
const sourceData = renderFile(data, record?.ext);
|
const sourceData = renderFile(data, record?.ext);
|
||||||
@ -55,8 +60,8 @@ export default function Download() {
|
|||||||
message.success('Name has been changed!');
|
message.success('Name has been changed!');
|
||||||
}
|
}
|
||||||
opInfo.resetRecord();
|
opInfo.resetRecord();
|
||||||
})()
|
})();
|
||||||
}, [opInfo.opType])
|
}, [opInfo.opType]);
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
if (opData?.length === selectedRows.length) {
|
if (opData?.length === selectedRows.length) {
|
||||||
@ -69,10 +74,15 @@ export default function Download() {
|
|||||||
|
|
||||||
const rows = selectedRows.map(async (i) => {
|
const rows = selectedRows.map(async (i) => {
|
||||||
const isImg = ['png'].includes(i?.ext);
|
const isImg = ['png'].includes(i?.ext);
|
||||||
const file = await path.join(await chatRoot(), 'download', isImg ? 'img' : i?.ext, `${i?.id}.${i?.ext}`);
|
const file = await path.join(
|
||||||
|
await chatRoot(),
|
||||||
|
'download',
|
||||||
|
isImg ? 'img' : i?.ext,
|
||||||
|
`${i?.id}.${i?.ext}`,
|
||||||
|
);
|
||||||
await fs.removeFile(file);
|
await fs.removeFile(file);
|
||||||
return file;
|
return file;
|
||||||
})
|
});
|
||||||
Promise.all(rows).then(async () => {
|
Promise.all(rows).then(async () => {
|
||||||
await handleRefresh();
|
await handleRefresh();
|
||||||
message.success('All files selected are cleared!');
|
message.success('All files selected are cleared!');
|
||||||
@ -131,5 +141,5 @@ export default function Download() {
|
|||||||
<img style={{ maxWidth: '100%' }} src={source} />
|
<img style={{ maxWidth: '100%' }} src={source} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
3
src/view/markdown/index.scss
vendored
3
src/view/markdown/index.scss
vendored
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
.md-task {
|
.md-task {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -14,4 +13,4 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/view/markdown/index.tsx
vendored
12
src/view/markdown/index.tsx
vendored
@ -14,7 +14,7 @@ const modeMap: any = {
|
|||||||
0: 'split',
|
0: 'split',
|
||||||
1: 'md',
|
1: 'md',
|
||||||
2: 'doc',
|
2: 'doc',
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function Markdown() {
|
export default function Markdown() {
|
||||||
const [filePath, setFilePath] = useState('');
|
const [filePath, setFilePath] = useState('');
|
||||||
@ -26,8 +26,8 @@ export default function Markdown() {
|
|||||||
useInit(async () => {
|
useInit(async () => {
|
||||||
const file = await getPath(state);
|
const file = await getPath(state);
|
||||||
setFilePath(file);
|
setFilePath(file);
|
||||||
setSource(await fs.readTextFile(file))
|
setSource(await fs.readTextFile(file));
|
||||||
})
|
});
|
||||||
|
|
||||||
const handleChange = async (v: string) => {
|
const handleChange = async (v: string) => {
|
||||||
await fs.writeTextFile(filePath, v);
|
await fs.writeTextFile(filePath, v);
|
||||||
@ -46,9 +46,7 @@ export default function Markdown() {
|
|||||||
<Breadcrumb.Item onClick={() => history.go(-1)}>
|
<Breadcrumb.Item onClick={() => history.go(-1)}>
|
||||||
<ArrowLeftOutlined />
|
<ArrowLeftOutlined />
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item onClick={() => shell.open(filePath)}>
|
<Breadcrumb.Item onClick={() => shell.open(filePath)}>{filePath}</Breadcrumb.Item>
|
||||||
{filePath}
|
|
||||||
</Breadcrumb.Item>
|
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
<div>
|
<div>
|
||||||
<SplitIcon onClick={handlePreview} style={{ fontSize: 18, color: 'rgba(0,0,0,0.5)' }} />
|
<SplitIcon onClick={handlePreview} style={{ fontSize: 18, color: 'rgba(0,0,0,0.5)' }} />
|
||||||
@ -57,4 +55,4 @@ export default function Markdown() {
|
|||||||
<MarkdownEditor value={source} onChange={handleChange} mode={modeMap[previewMode]} />
|
<MarkdownEditor value={source} onChange={handleChange} mode={modeMap[previewMode]} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
52
src/view/model/SyncCustom/Form.tsx
vendored
52
src/view/model/SyncCustom/Form.tsx
vendored
@ -1,4 +1,10 @@
|
|||||||
import { useEffect, useState, ForwardRefRenderFunction, useImperativeHandle, forwardRef } from 'react';
|
import {
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
ForwardRefRenderFunction,
|
||||||
|
useImperativeHandle,
|
||||||
|
forwardRef,
|
||||||
|
} from 'react';
|
||||||
import { Form, Input, Select, Tooltip } from 'antd';
|
import { Form, Input, Select, Tooltip } from 'antd';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import type { FormProps } from 'antd';
|
import type { FormProps } from 'antd';
|
||||||
@ -7,7 +13,7 @@ import { DISABLE_AUTO_COMPLETE, chatRoot } from '@/utils';
|
|||||||
import useInit from '@/hooks/useInit';
|
import useInit from '@/hooks/useInit';
|
||||||
|
|
||||||
interface SyncFormProps {
|
interface SyncFormProps {
|
||||||
record?: Record<string|symbol, any> | null;
|
record?: Record<string | symbol, any> | null;
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,10 +60,18 @@ const SyncForm: ForwardRefRenderFunction<FormProps, SyncFormProps> = ({ record,
|
|||||||
|
|
||||||
const jsonTip = (
|
const jsonTip = (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={<pre>{JSON.stringify([
|
title={
|
||||||
{ cmd: '', act: '', prompt: '' },
|
<pre>
|
||||||
{ cmd: '', act: '', prompt: '' },
|
{JSON.stringify(
|
||||||
], null, 2)}</pre>}
|
[
|
||||||
|
{ cmd: '', act: '', prompt: '' },
|
||||||
|
{ cmd: '', act: '', prompt: '' },
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<a>JSON</a>
|
<a>JSON</a>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -65,10 +79,12 @@ const SyncForm: ForwardRefRenderFunction<FormProps, SyncFormProps> = ({ record,
|
|||||||
|
|
||||||
const csvTip = (
|
const csvTip = (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={<pre>{`"cmd","act","prompt"
|
title={
|
||||||
|
<pre>{`"cmd","act","prompt"
|
||||||
"cmd","act","prompt"
|
"cmd","act","prompt"
|
||||||
"cmd","act","prompt"
|
"cmd","act","prompt"
|
||||||
"cmd","act","prompt"`}</pre>}
|
"cmd","act","prompt"`}</pre>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<a>CSV</a>
|
<a>CSV</a>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -76,11 +92,7 @@ const SyncForm: ForwardRefRenderFunction<FormProps, SyncFormProps> = ({ record,
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form
|
<Form form={form} labelCol={{ span: 4 }} initialValues={initFormValue}>
|
||||||
form={form}
|
|
||||||
labelCol={{ span: 4 }}
|
|
||||||
initialValues={initFormValue}
|
|
||||||
>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Name"
|
label="Name"
|
||||||
name="name"
|
name="name"
|
||||||
@ -92,7 +104,7 @@ const SyncForm: ForwardRefRenderFunction<FormProps, SyncFormProps> = ({ record,
|
|||||||
label="PATH"
|
label="PATH"
|
||||||
name="path"
|
name="path"
|
||||||
rules={[{ required: true, message: 'Please enter the path!' }]}
|
rules={[{ required: true, message: 'Please enter the path!' }]}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
placeholder="YOUR_PATH"
|
placeholder="YOUR_PATH"
|
||||||
addonBefore={pathOptions}
|
addonBefore={pathOptions}
|
||||||
@ -100,13 +112,17 @@ const SyncForm: ForwardRefRenderFunction<FormProps, SyncFormProps> = ({ record,
|
|||||||
{...DISABLE_AUTO_COMPLETE}
|
{...DISABLE_AUTO_COMPLETE}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item style={{ display: 'none' }} name="id" initialValue={v4().replace(/-/g, '')}><input /></Form.Item>
|
<Form.Item style={{ display: 'none' }} name="id" initialValue={v4().replace(/-/g, '')}>
|
||||||
|
<input />
|
||||||
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
<div className="tip">
|
<div className="tip">
|
||||||
<p>The file supports only {csvTip} and {jsonTip} formats.</p>
|
<p>
|
||||||
|
The file supports only {csvTip} and {jsonTip} formats.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default forwardRef(SyncForm);
|
export default forwardRef(SyncForm);
|
||||||
|
26
src/view/model/SyncCustom/config.tsx
vendored
26
src/view/model/SyncCustom/config.tsx
vendored
@ -26,7 +26,7 @@ export const syncColumns = () => [
|
|||||||
dataIndex: 'path',
|
dataIndex: 'path',
|
||||||
key: 'path',
|
key: 'path',
|
||||||
width: 180,
|
width: 180,
|
||||||
render: (_: string, row: any) => <RenderPath row={row} />
|
render: (_: string, row: any) => <RenderPath row={row} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Last updated',
|
title: 'Last updated',
|
||||||
@ -36,7 +36,7 @@ export const syncColumns = () => [
|
|||||||
render: (v: number) => (
|
render: (v: number) => (
|
||||||
<div>
|
<div>
|
||||||
<HistoryOutlined style={{ marginRight: 5, color: v ? '#52c41a' : '#ff4d4f' }} />
|
<HistoryOutlined style={{ marginRight: 5, color: v ? '#52c41a' : '#ff4d4f' }} />
|
||||||
{ v ? fmtDate(v) : ''}
|
{v ? fmtDate(v) : ''}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -56,7 +56,11 @@ export const syncColumns = () => [
|
|||||||
>
|
>
|
||||||
<a>Sync</a>
|
<a>Sync</a>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
{row.last_updated && <Link to={`${row.id}`} state={row}>View</Link>}
|
{row.last_updated && (
|
||||||
|
<Link to={`${row.id}`} state={row}>
|
||||||
|
View
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
<a onClick={() => actions.setRecord(row, 'edit')}>Edit</a>
|
<a onClick={() => actions.setRecord(row, 'edit')}>Edit</a>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="Are you sure to delete this path?"
|
title="Are you sure to delete this path?"
|
||||||
@ -67,23 +71,23 @@ export const syncColumns = () => [
|
|||||||
<a>Delete</a>
|
<a>Delete</a>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const RenderPath = ({ row }: any) => {
|
const RenderPath = ({ row }: any) => {
|
||||||
const [filePath, setFilePath] = useState('');
|
const [filePath, setFilePath] = useState('');
|
||||||
useInit(async () => {
|
useInit(async () => {
|
||||||
setFilePath(await getPath(row));
|
setFilePath(await getPath(row));
|
||||||
})
|
});
|
||||||
return <a onClick={() => shell.open(filePath)}>{filePath}</a>
|
return <a onClick={() => shell.open(filePath)}>{filePath}</a>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPath = async (row: any) => {
|
export const getPath = async (row: any) => {
|
||||||
if (!/^http/.test(row.protocol)) {
|
if (!/^http/.test(row.protocol)) {
|
||||||
return await path.join(await chatRoot(), row.path) + `.${row.ext}`;
|
return (await path.join(await chatRoot(), row.path)) + `.${row.ext}`;
|
||||||
} else {
|
} else {
|
||||||
return `${row.protocol}://${row.path}.${row.ext}`;
|
return `${row.protocol}://${row.path}.${row.ext}`;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
58
src/view/model/SyncCustom/index.tsx
vendored
58
src/view/model/SyncCustom/index.tsx
vendored
@ -10,7 +10,13 @@ import { CHAT_MODEL_JSON, chatRoot, readJSON, genCmd } from '@/utils';
|
|||||||
import { syncColumns, getPath } from './config';
|
import { syncColumns, getPath } from './config';
|
||||||
import SyncForm from './Form';
|
import SyncForm from './Form';
|
||||||
|
|
||||||
const fmtData = (data: Record<string, any>[] = []) => (Array.isArray(data) ? data : []).map((i) => ({ ...i, cmd: i.cmd ? i.cmd : genCmd(i.act), tags: ['user-sync'], enable: true }));
|
const fmtData = (data: Record<string, any>[] = []) =>
|
||||||
|
(Array.isArray(data) ? data : []).map((i) => ({
|
||||||
|
...i,
|
||||||
|
cmd: i.cmd ? i.cmd : genCmd(i.act),
|
||||||
|
tags: ['user-sync'],
|
||||||
|
enable: true,
|
||||||
|
}));
|
||||||
|
|
||||||
export default function SyncCustom() {
|
export default function SyncCustom() {
|
||||||
const [isVisible, setVisible] = useState(false);
|
const [isVisible, setVisible] = useState(false);
|
||||||
@ -37,7 +43,10 @@ export default function SyncCustom() {
|
|||||||
handleSync(filename).then((isOk: boolean) => {
|
handleSync(filename).then((isOk: boolean) => {
|
||||||
opInfo.resetRecord();
|
opInfo.resetRecord();
|
||||||
if (!isOk) return;
|
if (!isOk) return;
|
||||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], { ...opInfo?.opRecord, last_updated: Date.now() });
|
const data = opReplace(opInfo?.opRecord?.[opSafeKey], {
|
||||||
|
...opInfo?.opRecord,
|
||||||
|
last_updated: Date.now(),
|
||||||
|
});
|
||||||
modelSet(data);
|
modelSet(data);
|
||||||
opInfo.resetRecord();
|
opInfo.resetRecord();
|
||||||
});
|
});
|
||||||
@ -48,9 +57,13 @@ export default function SyncCustom() {
|
|||||||
if (['delete'].includes(opInfo.opType)) {
|
if (['delete'].includes(opInfo.opType)) {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const file = await path.join(await chatRoot(), 'cache_model', `${opInfo?.opRecord?.id}.json`);
|
const file = await path.join(
|
||||||
|
await chatRoot(),
|
||||||
|
'cache_model',
|
||||||
|
`${opInfo?.opRecord?.id}.json`,
|
||||||
|
);
|
||||||
await fs.removeFile(file);
|
await fs.removeFile(file);
|
||||||
} catch(e) {}
|
} catch (e) {}
|
||||||
const data = opRemove(opInfo?.opRecord?.[opSafeKey]);
|
const data = opRemove(opInfo?.opRecord?.[opSafeKey]);
|
||||||
modelSet(data);
|
modelSet(data);
|
||||||
opInfo.resetRecord();
|
opInfo.resetRecord();
|
||||||
@ -94,29 +107,24 @@ export default function SyncCustom() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
formRef.current?.form?.validateFields()
|
formRef.current?.form?.validateFields().then((vals: Record<string, any>) => {
|
||||||
.then((vals: Record<string, any>) => {
|
if (opInfo.opType === 'new') {
|
||||||
if (opInfo.opType === 'new') {
|
const data = opAdd(vals);
|
||||||
const data = opAdd(vals);
|
modelSet(data);
|
||||||
modelSet(data);
|
message.success('Data added successfully');
|
||||||
message.success('Data added successfully');
|
}
|
||||||
}
|
if (opInfo.opType === 'edit') {
|
||||||
if (opInfo.opType === 'edit') {
|
const data = opReplace(opInfo?.opRecord?.[opSafeKey], vals);
|
||||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], vals);
|
modelSet(data);
|
||||||
modelSet(data);
|
message.success('Data updated successfully');
|
||||||
message.success('Data updated successfully');
|
}
|
||||||
}
|
hide();
|
||||||
hide();
|
});
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>
|
||||||
className="chat-add-btn"
|
|
||||||
type="primary"
|
|
||||||
onClick={opInfo.opNew}
|
|
||||||
>
|
|
||||||
Add PATH
|
Add PATH
|
||||||
</Button>
|
</Button>
|
||||||
<Table
|
<Table
|
||||||
@ -138,5 +146,5 @@ export default function SyncCustom() {
|
|||||||
<SyncForm ref={formRef} record={opInfo?.opRecord} type={opInfo.opType} />
|
<SyncForm ref={formRef} record={opInfo?.opRecord} type={opInfo.opType} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
4
src/view/model/SyncPrompts/config.tsx
vendored
4
src/view/model/SyncPrompts/config.tsx
vendored
@ -41,8 +41,6 @@ export const syncColumns = () => [
|
|||||||
dataIndex: 'prompt',
|
dataIndex: 'prompt',
|
||||||
key: 'prompt',
|
key: 'prompt',
|
||||||
// width: 300,
|
// width: 300,
|
||||||
render: (v: string) => (
|
render: (v: string) => <span className="chat-prompts-val">{v}</span>,
|
||||||
<span className="chat-prompts-val">{v}</span>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
3
src/view/model/SyncPrompts/index.scss
vendored
3
src/view/model/SyncPrompts/index.scss
vendored
@ -1,4 +1,5 @@
|
|||||||
.chat-table-tip, .chat-table-btns {
|
.chat-table-tip,
|
||||||
|
.chat-table-btns {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
20
src/view/model/SyncPrompts/index.tsx
vendored
20
src/view/model/SyncPrompts/index.tsx
vendored
@ -52,7 +52,7 @@ export default function SyncPrompts() {
|
|||||||
}, [opInfo.opTime]);
|
}, [opInfo.opTime]);
|
||||||
|
|
||||||
const handleEnable = (isEnable: boolean) => {
|
const handleEnable = (isEnable: boolean) => {
|
||||||
const data = opReplaceItems(selectedRowIDs, { enable: isEnable })
|
const data = opReplaceItems(selectedRowIDs, { enable: isEnable });
|
||||||
modelCacheSet(data);
|
modelCacheSet(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,7 +72,9 @@ export default function SyncPrompts() {
|
|||||||
<div>
|
<div>
|
||||||
{selectedItems.length > 0 && (
|
{selectedItems.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Button type="primary" onClick={() => handleEnable(true)}>Enable</Button>
|
<Button type="primary" onClick={() => handleEnable(true)}>
|
||||||
|
Enable
|
||||||
|
</Button>
|
||||||
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
||||||
<span className="num">Selected {selectedItems.length} items</span>
|
<span className="num">Selected {selectedItems.length} items</span>
|
||||||
</>
|
</>
|
||||||
@ -84,7 +86,11 @@ export default function SyncPrompts() {
|
|||||||
<FilePath url={promptsURL} content="f/awesome-chatgpt-prompts/prompts.csv" />
|
<FilePath url={promptsURL} content="f/awesome-chatgpt-prompts/prompts.csv" />
|
||||||
<FilePath label="CACHE" paths="cache_model/chatgpt_prompts.json" />
|
<FilePath label="CACHE" paths="cache_model/chatgpt_prompts.json" />
|
||||||
</div>
|
</div>
|
||||||
{lastUpdated && <span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>Last updated on {fmtDate(lastUpdated)}</span>}
|
{lastUpdated && (
|
||||||
|
<span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>
|
||||||
|
Last updated on {fmtDate(lastUpdated)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Table
|
<Table
|
||||||
key={lastUpdated}
|
key={lastUpdated}
|
||||||
@ -94,8 +100,10 @@ export default function SyncPrompts() {
|
|||||||
dataSource={opData}
|
dataSource={opData}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
pagination={TABLE_PAGINATION}
|
pagination={TABLE_PAGINATION}
|
||||||
expandable={{expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>}}
|
expandable={{
|
||||||
|
expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
10
src/view/model/SyncRecord/config.tsx
vendored
10
src/view/model/SyncRecord/config.tsx
vendored
@ -25,7 +25,11 @@ export const syncColumns = () => [
|
|||||||
key: 'tags',
|
key: 'tags',
|
||||||
// width: 150,
|
// width: 150,
|
||||||
render: (v: string[]) => (
|
render: (v: string[]) => (
|
||||||
<span className="chat-prompts-tags">{v?.map(i => <Tag key={i}>{i}</Tag>)}</span>
|
<span className="chat-prompts-tags">
|
||||||
|
{v?.map((i) => (
|
||||||
|
<Tag key={i}>{i}</Tag>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -43,8 +47,6 @@ export const syncColumns = () => [
|
|||||||
dataIndex: 'prompt',
|
dataIndex: 'prompt',
|
||||||
key: 'prompt',
|
key: 'prompt',
|
||||||
// width: 300,
|
// width: 300,
|
||||||
render: (v: string) => (
|
render: (v: string) => <span className="chat-prompts-val">{v}</span>,
|
||||||
<span className="chat-prompts-val">{v}</span>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
22
src/view/model/SyncRecord/index.tsx
vendored
22
src/view/model/SyncRecord/index.tsx
vendored
@ -30,7 +30,7 @@ export default function SyncRecord() {
|
|||||||
useInit(async () => {
|
useInit(async () => {
|
||||||
setFilePath(await getPath(state));
|
setFilePath(await getPath(state));
|
||||||
setJsonPath(await path.join(await chatRoot(), 'cache_model', `${state?.id}.json`));
|
setJsonPath(await path.join(await chatRoot(), 'cache_model', `${state?.id}.json`));
|
||||||
})
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (modelCacheJson.length <= 0) return;
|
if (modelCacheJson.length <= 0) return;
|
||||||
@ -45,7 +45,7 @@ export default function SyncRecord() {
|
|||||||
}, [opInfo.opTime]);
|
}, [opInfo.opTime]);
|
||||||
|
|
||||||
const handleEnable = (isEnable: boolean) => {
|
const handleEnable = (isEnable: boolean) => {
|
||||||
const data = opReplaceItems(selectedRowIDs, { enable: isEnable })
|
const data = opReplaceItems(selectedRowIDs, { enable: isEnable });
|
||||||
modelCacheSet(data);
|
modelCacheSet(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,7 +58,9 @@ export default function SyncRecord() {
|
|||||||
<div>
|
<div>
|
||||||
{selectedItems.length > 0 && (
|
{selectedItems.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Button type="primary" onClick={() => handleEnable(true)}>Enable</Button>
|
<Button type="primary" onClick={() => handleEnable(true)}>
|
||||||
|
Enable
|
||||||
|
</Button>
|
||||||
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
||||||
<span className="num">Selected {selectedItems.length} items</span>
|
<span className="num">Selected {selectedItems.length} items</span>
|
||||||
</>
|
</>
|
||||||
@ -70,7 +72,11 @@ export default function SyncRecord() {
|
|||||||
<FilePath url={filePath} />
|
<FilePath url={filePath} />
|
||||||
<FilePath label="CACHE" paths={`cache_model/${state?.id}.json`} />
|
<FilePath label="CACHE" paths={`cache_model/${state?.id}.json`} />
|
||||||
</div>
|
</div>
|
||||||
{state?.last_updated && <span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>Last updated on {fmtDate(state?.last_updated)}</span>}
|
{state?.last_updated && (
|
||||||
|
<span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>
|
||||||
|
Last updated on {fmtDate(state?.last_updated)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Table
|
<Table
|
||||||
key="prompt"
|
key="prompt"
|
||||||
@ -80,8 +86,10 @@ export default function SyncRecord() {
|
|||||||
dataSource={opData}
|
dataSource={opData}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
pagination={TABLE_PAGINATION}
|
pagination={TABLE_PAGINATION}
|
||||||
expandable={{expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>}}
|
expandable={{
|
||||||
|
expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
17
src/view/model/UserCustom/Form.tsx
vendored
17
src/view/model/UserCustom/Form.tsx
vendored
@ -6,7 +6,7 @@ import Tags from '@comps/Tags';
|
|||||||
import { DISABLE_AUTO_COMPLETE } from '@/utils';
|
import { DISABLE_AUTO_COMPLETE } from '@/utils';
|
||||||
|
|
||||||
interface UserCustomFormProps {
|
interface UserCustomFormProps {
|
||||||
record?: Record<string|symbol, any> | null;
|
record?: Record<string | symbol, any> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initFormValue = {
|
const initFormValue = {
|
||||||
@ -16,7 +16,10 @@ const initFormValue = {
|
|||||||
prompt: '',
|
prompt: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const UserCustomForm: ForwardRefRenderFunction<FormProps, UserCustomFormProps> = ({ record }, ref) => {
|
const UserCustomForm: ForwardRefRenderFunction<FormProps, UserCustomFormProps> = (
|
||||||
|
{ record },
|
||||||
|
ref,
|
||||||
|
) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
useImperativeHandle(ref, () => ({ form }));
|
useImperativeHandle(ref, () => ({ form }));
|
||||||
|
|
||||||
@ -27,11 +30,7 @@ const UserCustomForm: ForwardRefRenderFunction<FormProps, UserCustomFormProps> =
|
|||||||
}, [record]);
|
}, [record]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form form={form} labelCol={{ span: 4 }} initialValues={initFormValue}>
|
||||||
form={form}
|
|
||||||
labelCol={{ span: 4 }}
|
|
||||||
initialValues={initFormValue}
|
|
||||||
>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="/{cmd}"
|
label="/{cmd}"
|
||||||
name="cmd"
|
name="cmd"
|
||||||
@ -60,7 +59,7 @@ const UserCustomForm: ForwardRefRenderFunction<FormProps, UserCustomFormProps> =
|
|||||||
<Input.TextArea rows={4} placeholder="Please enter a prompt" {...DISABLE_AUTO_COMPLETE} />
|
<Input.TextArea rows={4} placeholder="Please enter a prompt" {...DISABLE_AUTO_COMPLETE} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default forwardRef(UserCustomForm);
|
export default forwardRef(UserCustomForm);
|
||||||
|
14
src/view/model/UserCustom/config.tsx
vendored
14
src/view/model/UserCustom/config.tsx
vendored
@ -7,7 +7,7 @@ export const modelColumns = () => [
|
|||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
width: 120,
|
width: 120,
|
||||||
key: 'cmd',
|
key: 'cmd',
|
||||||
render: (v: string) => <Tag color="#2a2a2a">/{v}</Tag>
|
render: (v: string) => <Tag color="#2a2a2a">/{v}</Tag>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Act',
|
title: 'Act',
|
||||||
@ -21,7 +21,11 @@ export const modelColumns = () => [
|
|||||||
key: 'tags',
|
key: 'tags',
|
||||||
width: 150,
|
width: 150,
|
||||||
render: (v: string[]) => (
|
render: (v: string[]) => (
|
||||||
<span className="chat-prompts-tags">{v?.map(i => <Tag key={i}>{i}</Tag>)}</span>
|
<span className="chat-prompts-tags">
|
||||||
|
{v?.map((i) => (
|
||||||
|
<Tag key={i}>{i}</Tag>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -39,9 +43,7 @@ export const modelColumns = () => [
|
|||||||
dataIndex: 'prompt',
|
dataIndex: 'prompt',
|
||||||
key: 'prompt',
|
key: 'prompt',
|
||||||
width: 300,
|
width: 300,
|
||||||
render: (v: string) => (
|
render: (v: string) => <span className="chat-prompts-val">{v}</span>,
|
||||||
<span className="chat-prompts-val">{v}</span>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Action',
|
title: 'Action',
|
||||||
@ -61,5 +63,5 @@ export const modelColumns = () => [
|
|||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
77
src/view/model/UserCustom/index.tsx
vendored
77
src/view/model/UserCustom/index.tsx
vendored
@ -50,10 +50,10 @@ export default function LanguageModel() {
|
|||||||
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
const data = opReplace(opInfo?.opRecord?.[opSafeKey], opInfo?.opRecord);
|
||||||
modelCacheSet(data);
|
modelCacheSet(data);
|
||||||
}
|
}
|
||||||
}, [opInfo.opTime])
|
}, [opInfo.opTime]);
|
||||||
|
|
||||||
const handleEnable = (isEnable: boolean) => {
|
const handleEnable = (isEnable: boolean) => {
|
||||||
const data = opReplaceItems(selectedRowIDs, { enable: isEnable })
|
const data = opReplaceItems(selectedRowIDs, { enable: isEnable });
|
||||||
modelCacheSet(data);
|
modelCacheSet(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,38 +63,51 @@ export default function LanguageModel() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
formRef.current?.form?.validateFields()
|
formRef.current?.form?.validateFields().then(async (vals: Record<string, any>) => {
|
||||||
.then(async (vals: Record<string, any>) => {
|
if (
|
||||||
if (modelCacheJson.map((i: any) => i.cmd).includes(vals.cmd) && opInfo?.opRecord?.cmd !== vals.cmd) {
|
modelCacheJson.map((i: any) => i.cmd).includes(vals.cmd) &&
|
||||||
message.warning(`"cmd: /${vals.cmd}" already exists, please change the "${vals.cmd}" name and resubmit.`);
|
opInfo?.opRecord?.cmd !== vals.cmd
|
||||||
return;
|
) {
|
||||||
}
|
message.warning(
|
||||||
let data = [];
|
`"cmd: /${vals.cmd}" already exists, please change the "${vals.cmd}" name and resubmit.`,
|
||||||
switch (opInfo.opType) {
|
);
|
||||||
case 'new': data = opAdd(vals); break;
|
return;
|
||||||
case 'edit': data = opReplace(opInfo?.opRecord?.[opSafeKey], vals); break;
|
}
|
||||||
default: break;
|
let data = [];
|
||||||
}
|
switch (opInfo.opType) {
|
||||||
await modelCacheSet(data);
|
case 'new':
|
||||||
opInit(data);
|
data = opAdd(vals);
|
||||||
modelSet({
|
break;
|
||||||
id: 'user_custom',
|
case 'edit':
|
||||||
last_updated: Date.now(),
|
data = opReplace(opInfo?.opRecord?.[opSafeKey], vals);
|
||||||
});
|
break;
|
||||||
hide();
|
default:
|
||||||
})
|
break;
|
||||||
|
}
|
||||||
|
await modelCacheSet(data);
|
||||||
|
opInit(data);
|
||||||
|
modelSet({
|
||||||
|
id: 'user_custom',
|
||||||
|
last_updated: Date.now(),
|
||||||
|
});
|
||||||
|
hide();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const modalTitle = `${({ new: 'Create', edit: 'Edit' })[opInfo.opType]} Model`;
|
const modalTitle = `${{ new: 'Create', edit: 'Edit' }[opInfo.opType]} Model`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="chat-table-btns">
|
<div className="chat-table-btns">
|
||||||
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>Add Model</Button>
|
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>
|
||||||
|
Add Model
|
||||||
|
</Button>
|
||||||
<div>
|
<div>
|
||||||
{selectedItems.length > 0 && (
|
{selectedItems.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Button type="primary" onClick={() => handleEnable(true)}>Enable</Button>
|
<Button type="primary" onClick={() => handleEnable(true)}>
|
||||||
|
Enable
|
||||||
|
</Button>
|
||||||
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
<Button onClick={() => handleEnable(false)}>Disable</Button>
|
||||||
<span className="num">Selected {selectedItems.length} items</span>
|
<span className="num">Selected {selectedItems.length} items</span>
|
||||||
</>
|
</>
|
||||||
@ -103,7 +116,11 @@ export default function LanguageModel() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="chat-table-tip">
|
<div className="chat-table-tip">
|
||||||
<FilePath label="CACHE" paths="cache_model/user_custom.json" />
|
<FilePath label="CACHE" paths="cache_model/user_custom.json" />
|
||||||
{lastUpdated && <span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>Last updated on {fmtDate(lastUpdated)}</span>}
|
{lastUpdated && (
|
||||||
|
<span style={{ marginLeft: 10, color: '#888', fontSize: 12 }}>
|
||||||
|
Last updated on {fmtDate(lastUpdated)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Table
|
<Table
|
||||||
key={lastUpdated}
|
key={lastUpdated}
|
||||||
@ -113,7 +130,9 @@ export default function LanguageModel() {
|
|||||||
dataSource={opData}
|
dataSource={opData}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
pagination={TABLE_PAGINATION}
|
pagination={TABLE_PAGINATION}
|
||||||
expandable={{expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>}}
|
expandable={{
|
||||||
|
expandedRowRender: (record) => <div style={{ padding: 10 }}>{record.prompt}</div>,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Modal
|
<Modal
|
||||||
open={isVisible}
|
open={isVisible}
|
||||||
@ -126,5 +145,5 @@ export default function LanguageModel() {
|
|||||||
<UserCustomForm record={opInfo?.opRecord} ref={formRef} />
|
<UserCustomForm record={opInfo?.opRecord} ref={formRef} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
18
src/view/notes/config.tsx
vendored
18
src/view/notes/config.tsx
vendored
@ -41,7 +41,9 @@ export const notesColumns = () => [
|
|||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
<a onClick={() => actions.setRecord(row, 'preview')}>Preview</a>
|
<a onClick={() => actions.setRecord(row, 'preview')}>Preview</a>
|
||||||
<Link to={`/md/${row.id}`} state={row}>Edit</Link>
|
<Link to={`/md/${row.id}`} state={row}>
|
||||||
|
Edit
|
||||||
|
</Link>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="Are you sure to delete this file?"
|
title="Are you sure to delete this file?"
|
||||||
onConfirm={() => actions.setRecord(row, 'delete')}
|
onConfirm={() => actions.setRecord(row, 'delete')}
|
||||||
@ -51,20 +53,20 @@ export const notesColumns = () => [
|
|||||||
<a>Delete</a>
|
<a>Delete</a>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const RenderPath = ({ row }: any) => {
|
const RenderPath = ({ row }: any) => {
|
||||||
const [filePath, setFilePath] = useState('');
|
const [filePath, setFilePath] = useState('');
|
||||||
useInit(async () => {
|
useInit(async () => {
|
||||||
setFilePath(await getPath(row));
|
setFilePath(await getPath(row));
|
||||||
})
|
});
|
||||||
return <a onClick={() => shell.open(filePath)}>{filePath}</a>;
|
return <a onClick={() => shell.open(filePath)}>{filePath}</a>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPath = async (row: any) => {
|
export const getPath = async (row: any) => {
|
||||||
const isImg = ['png'].includes(row?.ext);
|
const isImg = ['png'].includes(row?.ext);
|
||||||
return await path.join(await chatRoot(), 'notes', row.id) + `.${row.ext}`;
|
return (await path.join(await chatRoot(), 'notes', row.id)) + `.${row.ext}`;
|
||||||
}
|
};
|
||||||
|
10
src/view/notes/index.tsx
vendored
10
src/view/notes/index.tsx
vendored
@ -46,8 +46,8 @@ export default function Notes() {
|
|||||||
message.success('Name has been changed!');
|
message.success('Name has been changed!');
|
||||||
}
|
}
|
||||||
opInfo.resetRecord();
|
opInfo.resetRecord();
|
||||||
})()
|
})();
|
||||||
}, [opInfo.opType])
|
}, [opInfo.opType]);
|
||||||
|
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
if (opData?.length === selectedRows.length) {
|
if (opData?.length === selectedRows.length) {
|
||||||
@ -62,7 +62,7 @@ export default function Notes() {
|
|||||||
const file = await path.join(await chatRoot(), 'notes', `${i?.id}.${i?.ext}`);
|
const file = await path.join(await chatRoot(), 'notes', `${i?.id}.${i?.ext}`);
|
||||||
await fs.removeFile(file);
|
await fs.removeFile(file);
|
||||||
return file;
|
return file;
|
||||||
})
|
});
|
||||||
Promise.all(rows).then(async () => {
|
Promise.all(rows).then(async () => {
|
||||||
await handleRefresh();
|
await handleRefresh();
|
||||||
message.success('All files selected are cleared!');
|
message.success('All files selected are cleared!');
|
||||||
@ -122,5 +122,5 @@ export default function Notes() {
|
|||||||
<Markdown children={source} />
|
<Markdown children={source} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
62
src/view/settings/General.tsx
vendored
62
src/view/settings/General.tsx
vendored
@ -9,37 +9,47 @@ import { DISABLE_AUTO_COMPLETE } from '@/utils';
|
|||||||
const AutoUpdateLabel = () => {
|
const AutoUpdateLabel = () => {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
Auto Update
|
Auto Update{' '}
|
||||||
{' '}
|
<Tooltip
|
||||||
<Tooltip title={(
|
title={
|
||||||
<div>
|
<div>
|
||||||
<div>Auto Update Policy</div>
|
<div>Auto Update Policy</div>
|
||||||
<div><strong>Prompt</strong>: prompt to install</div>
|
<div>
|
||||||
<div><strong>Silent</strong>: install silently</div>
|
<strong>Prompt</strong>: prompt to install
|
||||||
{/* <div><strong>Disable</strong>: disable auto update</div> */}
|
</div>
|
||||||
</div>
|
<div>
|
||||||
)}><QuestionCircleOutlined style={{ color: '#1677ff' }} /></Tooltip>
|
<strong>Silent</strong>: install silently
|
||||||
|
</div>
|
||||||
|
{/* <div><strong>Disable</strong>: disable auto update</div> */}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<QuestionCircleOutlined style={{ color: '#1677ff' }} />
|
||||||
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const GlobalShortcutLabel = () => {
|
const GlobalShortcutLabel = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
Global Shortcut
|
Global Shortcut{' '}
|
||||||
{' '}
|
<Tooltip
|
||||||
<Tooltip title={(
|
title={
|
||||||
<div>
|
<div>
|
||||||
<div>Shortcut definition, modifiers and key separated by "+" e.g. CmdOrControl+Q</div>
|
<div>Shortcut definition, modifiers and key separated by "+" e.g. CmdOrControl+Q</div>
|
||||||
<div style={{ margin: '10px 0'}}>If empty, the shortcut is disabled.</div>
|
<div style={{ margin: '10px 0' }}>If empty, the shortcut is disabled.</div>
|
||||||
<a href="https://tauri.app/v1/api/js/globalshortcut" target="_blank">https://tauri.app/v1/api/js/globalshortcut</a>
|
<a href="https://tauri.app/v1/api/js/globalshortcut" target="_blank">
|
||||||
</div>
|
https://tauri.app/v1/api/js/globalshortcut
|
||||||
)}>
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
<QuestionCircleOutlined style={{ color: '#1677ff' }} />
|
<QuestionCircleOutlined style={{ color: '#1677ff' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function General() {
|
export default function General() {
|
||||||
const [platformInfo, setPlatform] = useState('');
|
const [platformInfo, setPlatform] = useState('');
|
||||||
@ -62,9 +72,7 @@ export default function General() {
|
|||||||
<Radio.Group>
|
<Radio.Group>
|
||||||
<Radio value="Light">Light</Radio>
|
<Radio value="Light">Light</Radio>
|
||||||
<Radio value="Dark">Dark</Radio>
|
<Radio value="Dark">Dark</Radio>
|
||||||
{["darwin", "windows"].includes(platformInfo) && (
|
{['darwin', 'windows'].includes(platformInfo) && <Radio value="System">System</Radio>}
|
||||||
<Radio value="System">System</Radio>
|
|
||||||
)}
|
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<AutoUpdateLabel />} name="auto_update">
|
<Form.Item label={<AutoUpdateLabel />} name="auto_update">
|
||||||
@ -78,5 +86,5 @@ export default function General() {
|
|||||||
<Input placeholder="CmdOrCtrl+Shift+O" {...DISABLE_AUTO_COMPLETE} />
|
<Input placeholder="CmdOrCtrl+Shift+O" {...DISABLE_AUTO_COMPLETE} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
48
src/view/settings/MainWindow.tsx
vendored
48
src/view/settings/MainWindow.tsx
vendored
@ -9,25 +9,39 @@ import { DISABLE_AUTO_COMPLETE } from '@/utils';
|
|||||||
const OriginLabel = ({ url }: { url: string }) => {
|
const OriginLabel = ({ url }: { url: string }) => {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
Switch Origin <Tooltip title={`Default: ${url}`}><QuestionCircleOutlined style={{ color: '#1677ff' }} /></Tooltip>
|
Switch Origin{' '}
|
||||||
|
<Tooltip title={`Default: ${url}`}>
|
||||||
|
<QuestionCircleOutlined style={{ color: '#1677ff' }} />
|
||||||
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const PopupSearchLabel = () => {
|
const PopupSearchLabel = () => {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
Pop-up Search
|
Pop-up Search{' '}
|
||||||
{' '}
|
<Tooltip
|
||||||
<Tooltip title={(
|
title={
|
||||||
<div>
|
<div>
|
||||||
<div style={{ marginBottom: 10 }}>Generate images according to the content: Select the ChatGPT content with the mouse, no more than 400 characters. the <b>DALL·E 2</b> button appears, and click to jump (Note: because the search content filled by the script cannot trigger the event directly, you need to enter a space in the input box to make the button clickable).</div>
|
<div style={{ marginBottom: 10 }}>
|
||||||
<div>The application is built using Tauri, and due to its security restrictions, some of the action buttons will not work, so we recommend going to your browser.</div>
|
Generate images according to the content: Select the ChatGPT content with the mouse,
|
||||||
</div>
|
no more than 400 characters. the <b>DALL·E 2</b> button appears, and click to jump
|
||||||
)}><QuestionCircleOutlined style={{ color: '#1677ff' }} /></Tooltip>
|
(Note: because the search content filled by the script cannot trigger the event
|
||||||
|
directly, you need to enter a space in the input box to make the button clickable).
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
The application is built using Tauri, and due to its security restrictions, some of
|
||||||
|
the action buttons will not work, so we recommend going to your browser.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<QuestionCircleOutlined style={{ color: '#1677ff' }} />
|
||||||
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function General() {
|
export default function General() {
|
||||||
const [chatConf, setChatConf] = useState<any>(null);
|
const [chatConf, setChatConf] = useState<any>(null);
|
||||||
@ -46,8 +60,12 @@ export default function General() {
|
|||||||
<Input placeholder="https://chat.openai.com" {...DISABLE_AUTO_COMPLETE} />
|
<Input placeholder="https://chat.openai.com" {...DISABLE_AUTO_COMPLETE} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="User Agent (Main)" name="ua_window">
|
<Form.Item label="User Agent (Main)" name="ua_window">
|
||||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...DISABLE_AUTO_COMPLETE} 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" />
|
<Input.TextArea
|
||||||
|
autoSize={{ minRows: 4, maxRows: 4 }}
|
||||||
|
{...DISABLE_AUTO_COMPLETE}
|
||||||
|
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>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
8
src/view/settings/TrayWindow.tsx
vendored
8
src/view/settings/TrayWindow.tsx
vendored
@ -9,8 +9,12 @@ export default function General() {
|
|||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="User Agent (SystemTray)" name="ua_tray">
|
<Form.Item label="User Agent (SystemTray)" name="ua_tray">
|
||||||
<Input.TextArea autoSize={{ minRows: 4, maxRows: 4 }} {...DISABLE_AUTO_COMPLETE} placeholder="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" />
|
<Input.TextArea
|
||||||
|
autoSize={{ minRows: 4, maxRows: 4 }}
|
||||||
|
{...DISABLE_AUTO_COMPLETE}
|
||||||
|
placeholder="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"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
18
src/view/settings/index.tsx
vendored
18
src/view/settings/index.tsx
vendored
@ -21,7 +21,7 @@ export default function Settings() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.setFieldsValue(clone(chatConf));
|
form.setFieldsValue(clone(chatConf));
|
||||||
}, [chatConf])
|
}, [chatConf]);
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
form.setFieldsValue(chatConf);
|
form.setFieldsValue(chatConf);
|
||||||
@ -31,7 +31,7 @@ export default function Settings() {
|
|||||||
const chatData = await invoke('reset_chat_conf');
|
const chatData = await invoke('reset_chat_conf');
|
||||||
setChatConf(chatData);
|
setChatConf(chatData);
|
||||||
const isOk = await dialog.ask(`Configuration reset successfully, whether to restart?`, {
|
const isOk = await dialog.ask(`Configuration reset successfully, whether to restart?`, {
|
||||||
title: 'ChatGPT Preferences'
|
title: 'ChatGPT Preferences',
|
||||||
});
|
});
|
||||||
if (isOk) {
|
if (isOk) {
|
||||||
process.relaunch();
|
process.relaunch();
|
||||||
@ -44,7 +44,7 @@ export default function Settings() {
|
|||||||
if (!isEqual(omit(chatConf, ['default_origin']), values)) {
|
if (!isEqual(omit(chatConf, ['default_origin']), values)) {
|
||||||
await invoke('form_confirm', { data: values, label: 'main' });
|
await invoke('form_confirm', { data: values, label: 'main' });
|
||||||
const isOk = await dialog.ask(`Configuration saved successfully, whether to restart?`, {
|
const isOk = await dialog.ask(`Configuration saved successfully, whether to restart?`, {
|
||||||
title: 'ChatGPT Preferences'
|
title: 'ChatGPT Preferences',
|
||||||
});
|
});
|
||||||
if (isOk) {
|
if (isOk) {
|
||||||
process.relaunch();
|
process.relaunch();
|
||||||
@ -75,11 +75,15 @@ export default function Settings() {
|
|||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Space size={20}>
|
<Space size={20}>
|
||||||
<Button onClick={onCancel}>Cancel</Button>
|
<Button onClick={onCancel}>Cancel</Button>
|
||||||
<Button type="primary" htmlType="submit">Submit</Button>
|
<Button type="primary" htmlType="submit">
|
||||||
<Button type="dashed" onClick={onReset}>Reset to defaults</Button>
|
Submit
|
||||||
|
</Button>
|
||||||
|
<Button type="dashed" onClick={onReset}>
|
||||||
|
Reset to defaults
|
||||||
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"@/*": ["src/*"],
|
"@/*": ["src/*"],
|
||||||
"@view/*": ["src/view/*"],
|
"@view/*": ["src/view/*"],
|
||||||
"@comps/*": ["src/components/*"],
|
"@comps/*": ["src/components/*"],
|
||||||
"@layout/*": ["src/layout/*"],
|
"@layout/*": ["src/layout/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from 'vite';
|
||||||
import react from "@vitejs/plugin-react";
|
import react from '@vitejs/plugin-react';
|
||||||
import tsconfigPaths from "vite-tsconfig-paths";
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@ -16,12 +16,12 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
// to make use of `TAURI_DEBUG` and other env variables
|
// to make use of `TAURI_DEBUG` and other env variables
|
||||||
// https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
|
// https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
|
||||||
envPrefix: ["VITE_", "TAURI_"],
|
envPrefix: ['VITE_', 'TAURI_'],
|
||||||
build: {
|
build: {
|
||||||
// Tauri supports es2021
|
// Tauri supports es2021
|
||||||
target: ["es2021", "chrome100", "safari13"],
|
target: ['es2021', 'chrome100', 'safari13'],
|
||||||
// don't minify for debug builds
|
// don't minify for debug builds
|
||||||
minify: !process.env.TAURI_DEBUG ? "esbuild" : false,
|
minify: !process.env.TAURI_DEBUG ? 'esbuild' : false,
|
||||||
// produce sourcemaps for debug builds
|
// produce sourcemaps for debug builds
|
||||||
sourcemap: !!process.env.TAURI_DEBUG,
|
sourcemap: !!process.env.TAURI_DEBUG,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user