mirror of
https://github.com/yuruotong1/autoMate.git
synced 2026-03-22 13:07:17 +08:00
更新omniparser
This commit is contained in:
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
46
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,46 +0,0 @@
|
||||
---
|
||||
name: '报告Bug 🐛'
|
||||
about: 报告 @yuruotong1/autoMate 的 bug
|
||||
title: '🐛[BUG]'
|
||||
labels: '🐛 BUG'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### 🐛 bug 描述
|
||||
|
||||
<!--
|
||||
详细地描述 bug,让大家都能理解
|
||||
-->
|
||||
|
||||
### 📷 复现步骤
|
||||
|
||||
<!--
|
||||
清晰描述复现步骤,让别人也能看到问题
|
||||
-->
|
||||
|
||||
### 🏞 期望结果
|
||||
|
||||
<!--
|
||||
描述你原本期望看到的结果
|
||||
-->
|
||||
|
||||
### 💻 复现代码
|
||||
|
||||
<!--
|
||||
提供可复现的代码,仓库,或线上示例
|
||||
(可在下方 codesandbox 链接中添加你的最小可复现 demo)
|
||||
-->
|
||||
|
||||
[可复现 demo](https://codesandbox.io/s/html2ksetch-demo-m53be?file=/src/Demo.tsx)
|
||||
|
||||
### © 版本信息
|
||||
|
||||
- @yuruotong1/autoMate 版本: [e.g. 1.0.0]
|
||||
- 浏览器环境
|
||||
- 开发环境 [e.g. mac OS]
|
||||
|
||||
### 🚑 其他信息
|
||||
|
||||
<!--
|
||||
如截图等其他信息可以贴在这里
|
||||
-->
|
||||
25
.github/ISSUE_TEMPLATE/feature_request.md
vendored
25
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: '功能需求 ✨'
|
||||
about: 对 @yuruotong1/autoMate 的需求或建议
|
||||
title: '👑 [需求]'
|
||||
labels: '👑 Feature'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### 🥰 需求描述
|
||||
|
||||
<!--
|
||||
详细地描述问题,让大家都能理解
|
||||
-->
|
||||
|
||||
### 🧐 解决方案
|
||||
|
||||
<!--
|
||||
如果你有解决方案,在这里清晰地阐述
|
||||
-->
|
||||
|
||||
### 🚑 其他信息
|
||||
|
||||
<!--
|
||||
如截图等其他信息可以贴在这里
|
||||
-->
|
||||
25
.github/ISSUE_TEMPLATE/question.md
vendored
25
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: '疑问或需要帮助 ❓'
|
||||
about: 对 @yuruotong1/autoMate 使用的疑问或需要帮助
|
||||
title: '🧐[问题]'
|
||||
labels: '🧐 Question'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### 🧐 问题描述
|
||||
|
||||
<!--
|
||||
详细地描述问题,让大家都能理解
|
||||
-->
|
||||
|
||||
### 💻 示例代码
|
||||
|
||||
<!--
|
||||
如果有必要,展示代码,线上示例,或仓库
|
||||
-->
|
||||
|
||||
### 🚑 其他信息
|
||||
|
||||
<!--
|
||||
如截图等其他信息可以贴在这里
|
||||
-->
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
**/__pycache__**
|
||||
weights**
|
||||
116
README.md
116
README.md
@@ -3,116 +3,30 @@
|
||||
<img src="./resources/logo.png" width="120" height="120" alt="autoMate logo">
|
||||
<h1>autoMate</h1>
|
||||
|
||||
简体中文 | [ENGLISH](./README_EN.md)
|
||||
|
||||
一个开源的Agent+RPA开发平台。
|
||||
一个基于OmniParser的AI+RPA工具。
|
||||
|
||||
[![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url]
|
||||
|
||||
|
||||
📚[文档地址](https://s0soyusc93k.feishu.cn/wiki/JhhIwAUXJiBHG9kmt3YcXisWnec?from=from_copylink)|🎞️[介绍视频](https://www.bilibili.com/video/BV1LW421R7Ai/?share_source=copy_web&vd_source=c28e503b050f016c21660b69e391d391)|🗨[QQ频道](https://pd.qq.com/s/1ygylejjb)
|
||||
|
||||

|
||||
|
||||
[issues-helper-image]: https://img.shields.io/badge/using-actions--cool-blue?style=flat-square
|
||||
[issues-helper-url]: https://github.com/actions-cool
|
||||
[help-wanted-image]: https://flat.badgen.net/github/label-issues/yuruotong1/autoMate/enhancement/open
|
||||
[help-wanted-url]: https://github.com/yuruotong1/autoMate/labels/enhancement
|
||||
|
||||
</div>
|
||||
|
||||
[](https://ant.design)
|
||||
## 安装
|
||||
Clone项目,然后安装环境:
|
||||
|
||||
```bash
|
||||
cd OmniParser
|
||||
conda create -n "omni" python==3.12
|
||||
conda activate omni
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## ✨ 特性
|
||||
## 快速启动
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
- 🌈 聊聊天就能生成自动化代码。
|
||||
- 🔍 快捷键呼出搜索框一键运行自动化代码。
|
||||
- 📦 开箱即用的自动化工具套件。
|
||||
- ⚙️ 自动化开发框架和工具配套。
|
||||
- 🥳 兼容所有在线和本地大模型。
|
||||
## 问题
|
||||
可以通过`pip list`查看pytorch版本,然后从[官网]([官网](https://pytorch.org/get-started/locally/)查看支持的cuda版本。如果cuda不匹配就无法使用GPU,这会导致运行过程非常卡。
|
||||
|
||||
## 🖥 环境支持
|
||||
|
||||
- openai 的 api 格式大模型
|
||||
- 详见以下 litellm 配置
|
||||
|
||||
| 大模型 | [Completion](https://docs.litellm.ai/docs/#basic-usage) | [Streaming](https://docs.litellm.ai/docs/completion/stream#streaming-responses) | [Async Completion](https://docs.litellm.ai/docs/completion/stream#async-completion) | [Async Streaming](https://docs.litellm.ai/docs/completion/stream#async-streaming) | [Async Embedding](https://docs.litellm.ai/docs/embedding/supported_embedding) | [Async Image Generation](https://docs.litellm.ai/docs/image_generation) |
|
||||
|-------------------------------------------------------------------------------------|---------------------------------------------------------|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|-------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||
| [openai](https://docs.litellm.ai/docs/providers/openai) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [azure](https://docs.litellm.ai/docs/providers/azure) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [aws - sagemaker](https://docs.litellm.ai/docs/providers/aws_sagemaker) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [aws - bedrock](https://docs.litellm.ai/docs/providers/bedrock) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [google - vertex_ai](https://docs.litellm.ai/docs/providers/vertex) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [google - palm](https://docs.litellm.ai/docs/providers/palm) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [google AI Studio - gemini](https://docs.litellm.ai/docs/providers/gemini) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [mistral ai api](https://docs.litellm.ai/docs/providers/mistral) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [cloudflare AI Workers](https://docs.litellm.ai/docs/providers/cloudflare_workers) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [cohere](https://docs.litellm.ai/docs/providers/cohere) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [anthropic](https://docs.litellm.ai/docs/providers/anthropic) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [huggingface](https://docs.litellm.ai/docs/providers/huggingface) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [replicate](https://docs.litellm.ai/docs/providers/replicate) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [together_ai](https://docs.litellm.ai/docs/providers/togetherai) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [openrouter](https://docs.litellm.ai/docs/providers/openrouter) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [ai21](https://docs.litellm.ai/docs/providers/ai21) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [baseten](https://docs.litellm.ai/docs/providers/baseten) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [vllm](https://docs.litellm.ai/docs/providers/vllm) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [nlp_cloud](https://docs.litellm.ai/docs/providers/nlp_cloud) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [aleph alpha](https://docs.litellm.ai/docs/providers/aleph_alpha) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [petals](https://docs.litellm.ai/docs/providers/petals) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [ollama](https://docs.litellm.ai/docs/providers/ollama) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [deepinfra](https://docs.litellm.ai/docs/providers/deepinfra) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [perplexity-ai](https://docs.litellm.ai/docs/providers/perplexity) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [Groq AI](https://docs.litellm.ai/docs/providers/groq) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [Deepseek](https://docs.litellm.ai/docs/providers/deepseek) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [anyscale](https://docs.litellm.ai/docs/providers/anyscale) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [IBM - watsonx.ai](https://docs.litellm.ai/docs/providers/watsonx) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [voyage ai](https://docs.litellm.ai/docs/providers/voyage) | | | | | ✅ | |
|
||||
| [xinference [Xorbits Inference]](https://docs.litellm.ai/docs/providers/xinference) | | | | | ✅ | |
|
||||
| [FriendliAI](https://docs.litellm.ai/docs/providers/friendliai) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
|
||||
|
||||
## 🔗 链接
|
||||
|
||||
|
||||
- [基础功能](https://s0soyusc93k.feishu.cn/wiki/JhhIwAUXJiBHG9kmt3YcXisWnec#O9W8dEqfBo13oQxCslycFUWonFd)
|
||||
|
||||
- [项目理念](https://s0soyusc93k.feishu.cn/wiki/SR9ywLMZmin7gakGo21cnyaFnRf?from=from_copylink)
|
||||
|
||||
## 🍬 快速开始
|
||||
|
||||
下载 release 最新版本,双击即可直接运行,无需安装任何依赖。
|
||||
|
||||
## ⌨️ 本地开发
|
||||
|
||||
本项目分为前端和后端两个部分,前端项目在 app 目录下,后端项目在 server 目录下。这意味着,如果要运行 autoMate,你就得同时启动前端和后端。项目启动后会在~ 目录创建 sqlite 数据库 autoMate.db ,如果想查看数据库内容,建议使用开源数据库软件dbeaver。
|
||||
|
||||
### 启动前端
|
||||
|
||||
1. 安装 nodejs,要求nodejs版本为 v18.x;
|
||||
2. 使用命令行进入到 app 目录;
|
||||
3. 输入 npm install 安装依赖;
|
||||
4. 输入 npm run dev 启动前端。
|
||||
|
||||
### 启动后端:
|
||||
|
||||
1. 安装python3,最好 3.9+版本。
|
||||
2. 使用命令行进入 server 目录;
|
||||
3. 创建并激活虚拟环境,输入 python -m venv .venv;
|
||||
4. 输入 pip install -r requirements.txt 安装依赖;
|
||||
5. 输入 flask --app main run 启动后端
|
||||
|
||||
### 打包
|
||||
|
||||
后端打包命令:
|
||||
|
||||
`pyinstaller main.spec`
|
||||
|
||||
前端打包命令:
|
||||
|
||||
`npm run build:win`
|
||||
|
||||
打包完成后,将main.exe放在前端根目录下。
|
||||
|
||||
## 🤝 参与共建
|
||||
|
||||
|
||||
125
README_EN.md
125
README_EN.md
@@ -1,125 +0,0 @@
|
||||
<div align="center"><a name="readme-top"></a>
|
||||
|
||||
<img src="./resources/logo.png" width="120" height="120" alt="autoMate logo">
|
||||
<h1>autoMate</h1>
|
||||
|
||||
[简体中文](./README.md) | ENGLISH
|
||||
|
||||
An Open Source Development Platform for Agent+RPA.
|
||||
|
||||
[![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url]
|
||||
|
||||
|
||||
📚[Documentations](https://s0soyusc93k.feishu.cn/wiki/JhhIwAUXJiBHG9kmt3YcXisWnec?from=from_copylink)|🎞️[Introduction Video](https://www.bilibili.com/video/BV1LW421R7Ai/?share_source=copy_web&vd_source=c28e503b050f016c21660b69e391d391)|🗨[QQ Channel](https://pd.qq.com/s/1ygylejjb)
|
||||
|
||||

|
||||
|
||||
[issues-helper-image]: https://img.shields.io/badge/using-actions--cool-blue?style=flat-square
|
||||
[issues-helper-url]: https://github.com/actions-cool
|
||||
[help-wanted-image]: https://flat.badgen.net/github/label-issues/yuruotong1/autoMate/enhancement/open
|
||||
[help-wanted-url]: https://github.com/yuruotong1/autoMate/labels/enhancement
|
||||
|
||||
</div>
|
||||
|
||||
[](https://ant.design)
|
||||
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🌈 Generate automation code by chatting.
|
||||
- 🔍 Run automation code with one click from quick search.
|
||||
- 📦 Comprehensive automation toolkit
|
||||
- ⚙️ Integrated framework and tools for automation development.
|
||||
- 🥳 Compatible with all online and local LLMs.
|
||||
|
||||
## 🖥 Enviroment
|
||||
|
||||
- LLM APIs formatted by OpenAI.
|
||||
- Refer to the following LiteLLM configuration for detailed information:
|
||||
|
||||
| LLMs | [Completion](https://docs.litellm.ai/docs/#basic-usage) | [Streaming](https://docs.litellm.ai/docs/completion/stream#streaming-responses) | [Async Completion](https://docs.litellm.ai/docs/completion/stream#async-completion) | [Async Streaming](https://docs.litellm.ai/docs/completion/stream#async-streaming) | [Async Embedding](https://docs.litellm.ai/docs/embedding/supported_embedding) | [Async Image Generation](https://docs.litellm.ai/docs/image_generation) |
|
||||
|-------------------------------------------------------------------------------------|---------------------------------------------------------|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|-------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||
| [openai](https://docs.litellm.ai/docs/providers/openai) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [azure](https://docs.litellm.ai/docs/providers/azure) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [aws - sagemaker](https://docs.litellm.ai/docs/providers/aws_sagemaker) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [aws - bedrock](https://docs.litellm.ai/docs/providers/bedrock) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [google - vertex_ai](https://docs.litellm.ai/docs/providers/vertex) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| [google - palm](https://docs.litellm.ai/docs/providers/palm) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [google AI Studio - gemini](https://docs.litellm.ai/docs/providers/gemini) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [mistral ai api](https://docs.litellm.ai/docs/providers/mistral) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [cloudflare AI Workers](https://docs.litellm.ai/docs/providers/cloudflare_workers) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [cohere](https://docs.litellm.ai/docs/providers/cohere) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [anthropic](https://docs.litellm.ai/docs/providers/anthropic) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [huggingface](https://docs.litellm.ai/docs/providers/huggingface) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [replicate](https://docs.litellm.ai/docs/providers/replicate) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [together_ai](https://docs.litellm.ai/docs/providers/togetherai) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [openrouter](https://docs.litellm.ai/docs/providers/openrouter) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [ai21](https://docs.litellm.ai/docs/providers/ai21) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [baseten](https://docs.litellm.ai/docs/providers/baseten) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [vllm](https://docs.litellm.ai/docs/providers/vllm) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [nlp_cloud](https://docs.litellm.ai/docs/providers/nlp_cloud) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [aleph alpha](https://docs.litellm.ai/docs/providers/aleph_alpha) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [petals](https://docs.litellm.ai/docs/providers/petals) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [ollama](https://docs.litellm.ai/docs/providers/ollama) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [deepinfra](https://docs.litellm.ai/docs/providers/deepinfra) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [perplexity-ai](https://docs.litellm.ai/docs/providers/perplexity) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [Groq AI](https://docs.litellm.ai/docs/providers/groq) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [Deepseek](https://docs.litellm.ai/docs/providers/deepseek) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [anyscale](https://docs.litellm.ai/docs/providers/anyscale) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
| [IBM - watsonx.ai](https://docs.litellm.ai/docs/providers/watsonx) | ✅ | ✅ | ✅ | ✅ | ✅ | |
|
||||
| [voyage ai](https://docs.litellm.ai/docs/providers/voyage) | | | | | ✅ | |
|
||||
| [xinference [Xorbits Inference]](https://docs.litellm.ai/docs/providers/xinference) | | | | | ✅ | |
|
||||
| [FriendliAI](https://docs.litellm.ai/docs/providers/friendliai) | ✅ | ✅ | ✅ | ✅ | | |
|
||||
|
||||
|
||||
## 🔗 Related Links
|
||||
|
||||
|
||||
- [Basic Features](https://s0soyusc93k.feishu.cn/wiki/JhhIwAUXJiBHG9kmt3YcXisWnec#O9W8dEqfBo13oQxCslycFUWonFd)
|
||||
|
||||
- [Project Overview](https://s0soyusc93k.feishu.cn/wiki/SR9ywLMZmin7gakGo21cnyaFnRf?from=from_copylink)
|
||||
|
||||
## 🍬 Quick Start
|
||||
|
||||
Download the latest version from the release and double-click to run directly; no dependencies are required.
|
||||
|
||||
## ⌨️ Local LLM Application Development
|
||||
|
||||
This project is divided into two parts: front-end and back-end. The front-end project is in the `app` directory, and the back-end project is in the `server` directory. This means that to run autoMate, you need to start both the front-end and back-end simultaneously. The project will create an SQLite database autoMate.db in the home directory. To view the database contents, we recommend using the open-source database software `DBeaver`.
|
||||
|
||||
### Initiate the Front-End
|
||||
|
||||
1. Install Node.js (version v18.x is required).
|
||||
2. Navigate to the app directory using the command line.
|
||||
3. Run `npm install` to install dependencies.
|
||||
4. Run `npm run dev` to initiate the front-end
|
||||
|
||||
### Initiate the Back-End:
|
||||
|
||||
1. Install Python 3, preferably version 3.9+.
|
||||
2. Navigate to the `server` directory using the command line.
|
||||
3. Create and activate virtual env, and run `python -m venv .venv`.
|
||||
4. Run `pip install -r requirements.txt` to install the required dependencies.
|
||||
5. Run `flask --app main run` to start the back-end.
|
||||
|
||||
### Packaging
|
||||
|
||||
Back-end packaging command:
|
||||
|
||||
`pyinstaller main.spec`
|
||||
|
||||
Front-end packaging command:
|
||||
|
||||
`npm run build:win`
|
||||
|
||||
After packaging, place `main.exe` in the front-end root directory.
|
||||
|
||||
## 🤝 Collaborations
|
||||
|
||||
Please refer to [Contribution Guidance](https://s0soyusc93k.feishu.cn/wiki/ZE7KwtRweicLbNkHSdMcBMTxngg?from=from_copylink).
|
||||
|
||||
> Highly recommended reading [HOW TO ASK QUESTIONS THE SMART WAY](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[HOW TO ASK QUESTIONS TO OPEN SOURCE COMMUNITY](https://github.com/seajs/seajs/issues/545) 和 [HOW TO REPORT BUGS EFFICIENTLY](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[HOW TO SUBMIT A GOOD ISSUE TO OPEN SOURCE PROJECTS](https://zhuanlan.zhihu.com/p/25795393). Better questions are more likely to get help.
|
||||
|
||||
<a href="https://github.com/yuruotong1/autoMate/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=yuruotong1/autoMate" />
|
||||
</a>
|
||||
@@ -1,9 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
out
|
||||
.gitignore
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'@electron-toolkit/eslint-config-ts/recommended',
|
||||
'@electron-toolkit/eslint-config-prettier'
|
||||
]
|
||||
}
|
||||
5
app/.gitignore
vendored
5
app/.gitignore
vendored
@@ -1,5 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
out
|
||||
.DS_Store
|
||||
*.log*
|
||||
@@ -1,2 +0,0 @@
|
||||
electron_mirror=https://npmmirror.com/mirrors/electron/
|
||||
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
|
||||
@@ -1,6 +0,0 @@
|
||||
out
|
||||
dist
|
||||
pnpm-lock.yaml
|
||||
LICENSE.md
|
||||
tsconfig.json
|
||||
tsconfig.*.json
|
||||
@@ -1,4 +0,0 @@
|
||||
singleQuote: true
|
||||
semi: false
|
||||
printWidth: 100
|
||||
trailingComma: none
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "app/globals.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
provider: generic
|
||||
url: https://example.com/auto-updates
|
||||
updaterCacheDirName: automate-updater
|
||||
@@ -1,58 +0,0 @@
|
||||
appId: com.electron.app
|
||||
productName: automate
|
||||
directories:
|
||||
buildResources: build
|
||||
files:
|
||||
- '!**/.vscode/*'
|
||||
- '!src/*'
|
||||
- '!electron.vite.config.{js,ts,mjs,cjs}'
|
||||
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
|
||||
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
|
||||
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
|
||||
extraFiles:
|
||||
- from: '../server/dist/autoMateServer'
|
||||
filter: '**/*'
|
||||
- from: './resources'
|
||||
to: 'resources'
|
||||
filter: '**/*'
|
||||
asarUnpack:
|
||||
- resources/**
|
||||
win:
|
||||
executableName: automate
|
||||
nsis:
|
||||
oneClick: false # 创建一键安装程序还是辅助安装程序(默认是一键安装)
|
||||
allowElevation: true # 是否允许请求提升,如果为false,则用户必须使用提升的权限重新启动安装程序 (仅作用于辅助安装程序)
|
||||
allowToChangeInstallationDirectory: true # 是否允许修改安装目录 (仅作用于辅助安装程序)
|
||||
createStartMenuShortcut: true # 是否创建开始菜单快捷方式
|
||||
artifactName: ${name}-${version}-setup.${ext}
|
||||
shortcutName: ${productName}
|
||||
uninstallDisplayName: ${productName}
|
||||
createDesktopShortcut: always
|
||||
mac:
|
||||
entitlementsInherit: build/entitlements.mac.plist
|
||||
extendInfo:
|
||||
- NSCameraUsageDescription: Application requests access to the device's camera.
|
||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||
notarize: false
|
||||
dmg:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
linux:
|
||||
target:
|
||||
- AppImage
|
||||
- snap
|
||||
- deb
|
||||
maintainer: electronjs.org
|
||||
category: Utility
|
||||
appImage:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
npmRebuild: false
|
||||
publish:
|
||||
provider: github
|
||||
owner: yuruotong1
|
||||
repo: autoMate
|
||||
releaseType: 'release'
|
||||
|
||||
electronDownload:
|
||||
mirror: https://npmmirror.com/mirrors/electron/
|
||||
@@ -1,21 +0,0 @@
|
||||
import { resolve } from 'path'
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
main: {
|
||||
plugins: [externalizeDepsPlugin()]
|
||||
},
|
||||
preload: {
|
||||
plugins: [externalizeDepsPlugin()]
|
||||
},
|
||||
renderer: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@renderer': resolve('src/renderer/src'),
|
||||
'@components': resolve('src/renderer/src/components')
|
||||
}
|
||||
},
|
||||
plugins: [react()]
|
||||
}
|
||||
})
|
||||
16841
app/package-lock.json
generated
16841
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,96 +0,0 @@
|
||||
{
|
||||
"name": "automate",
|
||||
"version": "2.2.3",
|
||||
"description": "An Electron application with React and TypeScript",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "Yu RuoTong <yuruotong1@163.com> (https://github.com/yuruotong1/autoMate)",
|
||||
"homepage": "https://github.com/yuruotong1/autoMate",
|
||||
"repository": "https://github.com/yuruotong1/autoMate/tree/master/app",
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
|
||||
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
|
||||
"typecheck": "npm run typecheck:node && npm run typecheck:web",
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev",
|
||||
"build": "npm run typecheck && electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:unpack": "npm run build && electron-builder --dir",
|
||||
"build:win": "npm run build && electron-builder --win --config",
|
||||
"build:mac": "electron-vite build && electron-builder --mac --config",
|
||||
"build:linux": "electron-vite build && electron-builder --linux --config"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.yuruotong.automate",
|
||||
"mac": {
|
||||
"target": "dmg"
|
||||
},
|
||||
"win": {
|
||||
"target": "exe"
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
"deb",
|
||||
"rpm",
|
||||
"snap"
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^0.0.33",
|
||||
"@ant-design/pro-chat": "^1.15.0",
|
||||
"@ant-design/pro-editor": "^1.2.1",
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
"@electron-toolkit/preload": "^3.0.0",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"@icon-park/react": "^1.4.2",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@types/better-sqlite3": "^7.6.10",
|
||||
"@types/mockjs": "^1.0.10",
|
||||
"@uiw/react-codemirror": "^4.22.2",
|
||||
"ai": "^3.2.0",
|
||||
"antd": "^5.18.3",
|
||||
"antd-style": "^3.6.2",
|
||||
"better-sqlite3": "^11.0.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.11",
|
||||
"electron-updater": "^6.2.1",
|
||||
"localforage": "^1.10.0",
|
||||
"lucide-react": "^0.408.0",
|
||||
"mantine-contextmenu": "^7.10.2",
|
||||
"match-sorter": "^6.3.4",
|
||||
"mockjs": "^1.1.0",
|
||||
"openai": "^4.24.7",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"sort-by": "^0.0.2",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^1.0.1",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@types/node": "^18.19.9",
|
||||
"@types/react": "^18.2.48",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"electron": "^28.2.0",
|
||||
"electron-builder": "^24.9.1",
|
||||
"electron-rebuild": "^3.2.9",
|
||||
"electron-vite": "^2.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sass": "^1.77.4",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.12"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.3 KiB |
@@ -1,56 +0,0 @@
|
||||
|
||||
import { BrowserWindow, BrowserWindowConstructorOptions, shell } from 'electron'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import icon from '../../resources/icon.png?asset'
|
||||
import { join } from 'path'
|
||||
import url from 'node:url'
|
||||
export interface OptionsType extends Partial<BrowserWindowConstructorOptions>{
|
||||
openDevTools?: boolean,
|
||||
hash?: string
|
||||
initShow?: boolean
|
||||
}
|
||||
export function createWindow(options: OptionsType, router_url=""): BrowserWindow { // Create the browser window.
|
||||
const win = new BrowserWindow(Object.assign({
|
||||
width: 500,
|
||||
height: 350,
|
||||
center: true,
|
||||
show: false,
|
||||
frame: false,
|
||||
transparent: true,
|
||||
// alwaysOnTop: true,
|
||||
autoHideMenuBar: true,
|
||||
...(process.platform === 'linux' ? { icon } : {}),
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
sandbox: false,
|
||||
webSecurity: false // 禁用web安全性
|
||||
}
|
||||
}, options))
|
||||
// 如果是在开发环境下并且选项是打开开发者工具
|
||||
if (is.dev && options.openDevTools) win.webContents.openDevTools()
|
||||
win.on('ready-to-show', () => {
|
||||
options.initShow && win.show()
|
||||
})
|
||||
|
||||
win.webContents.setWindowOpenHandler((details) => {
|
||||
shell.openExternal(details.url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
win.loadURL(process.env['ELECTRON_RENDERER_URL'] + options.hash + router_url)
|
||||
} else {
|
||||
win.loadURL(
|
||||
url.format({
|
||||
pathname: join(__dirname, '../renderer/index.html'),
|
||||
protocol: 'file',
|
||||
slashes: true,
|
||||
hash: options.hash?.substring(1) + router_url
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return win
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import Database, * as BetterSqlite3 from 'better-sqlite3';
|
||||
import { app } from 'electron';
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
const db = (): BetterSqlite3.Database => {
|
||||
let dir = resolve(app.getPath('home'), "autoMate.db")
|
||||
|
||||
const db: BetterSqlite3.Database = new Database(dir, {});
|
||||
db.pragma('journal_mode = WAL');
|
||||
return db
|
||||
}
|
||||
export { db };
|
||||
@@ -1,2 +0,0 @@
|
||||
// import './tables'
|
||||
import './ipc'
|
||||
@@ -1,16 +0,0 @@
|
||||
import { IpcMainInvokeEvent, ipcMain } from "electron";
|
||||
import * as query from './query'
|
||||
import { initTable } from "./tables";
|
||||
ipcMain.handle('sql', (_event: IpcMainInvokeEvent, sql: string, type: SqlActionType, params={}) => {
|
||||
return query[type](sql, params)
|
||||
})
|
||||
|
||||
|
||||
|
||||
ipcMain.on('initTable', () => {
|
||||
initTable()
|
||||
})
|
||||
|
||||
ipcMain.handle('getConfig', async () => {
|
||||
return query.findOne('SELECT * FROM config where id = 1')
|
||||
})
|
||||
@@ -1,28 +0,0 @@
|
||||
import {db} from './connect'
|
||||
|
||||
export const findAll = (sql: string, params={}) => {
|
||||
return db().prepare(sql).all(params);
|
||||
}
|
||||
|
||||
export const findOne = (sql: string) => {
|
||||
return db().prepare(sql).get();
|
||||
}
|
||||
|
||||
export const create = (sql: string) => {
|
||||
return db().prepare(sql).run().lastInsertRowid;
|
||||
}
|
||||
|
||||
|
||||
//使用 params 是为了防止 sql 注入
|
||||
export const update = (sql: string, params: Record<string, any>) => {
|
||||
return db().prepare(sql).run(params).changes;
|
||||
}
|
||||
|
||||
export const del = (sql: string, params={}) => {
|
||||
return db().prepare(sql).run(params).changes;
|
||||
}
|
||||
|
||||
export const config = () => {
|
||||
const ret = findOne(`select * from config where id=1`) as {content: string}
|
||||
return JSON.parse(ret.content)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// import { Random } from "mockjs";
|
||||
import { db } from "./connect";
|
||||
import { findOne } from "./query";
|
||||
|
||||
export function initTable() {
|
||||
|
||||
|
||||
db().exec(`
|
||||
CREATE TABLE IF NOT EXISTS categories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT not null,
|
||||
name TEXT not null,
|
||||
created_at text not null
|
||||
);
|
||||
`)
|
||||
|
||||
|
||||
db().exec(`
|
||||
CREATE TABLE IF NOT EXISTS contents (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT not null,
|
||||
title TEXT not null,
|
||||
content TEXT not null,
|
||||
category_id INTEGER,
|
||||
created_at TEXT not null
|
||||
);
|
||||
`)
|
||||
|
||||
|
||||
db().exec(`
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT not null,
|
||||
content TEXT not null
|
||||
);
|
||||
`)
|
||||
|
||||
initData()
|
||||
}
|
||||
|
||||
function initData() {
|
||||
const initData = findOne('select * from config')
|
||||
if (initData) return
|
||||
const llm = {model: "gpt-4-turbo", api_key: "", base_url: "https://api.openai.com/v1"}
|
||||
db().exec(`insert into config (id, content) values(1,'{"shortcut":"Alt+d","llm": ${JSON.stringify(llm)}}')`)
|
||||
|
||||
}
|
||||
|
||||
|
||||
// for (let i = 0; i < 20; i++) {
|
||||
// const name = Random.title(5, 10)
|
||||
// db().exec(`insert into config (content) values('${name}')`)
|
||||
// }
|
||||
|
||||
// for (let i = 0; i < 20; i++) {
|
||||
// const name = Random.title(5, 10)
|
||||
// db().exec(`insert into categories (name, created_at) values('${name}', datetime())`)
|
||||
// }
|
||||
|
||||
// for (let i = 1; i < 20; i++) {
|
||||
// const title = Random.title(5, 10)
|
||||
// const content = Random.paragraph(5, 10)
|
||||
// db().exec(`insert into contents (title, content, category_id, created_at) values('${title}', '${content}', ${i}, datetime())`)
|
||||
// }
|
||||
@@ -1,44 +0,0 @@
|
||||
import { app, ipcMain } from 'electron'
|
||||
import { electronApp, optimizer } from '@electron-toolkit/utils'
|
||||
import "./db"
|
||||
import "./windows"
|
||||
import "./ipc"
|
||||
import "./shortcut"
|
||||
import "./updateRegister"
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(() => {
|
||||
// Set app user model id for windows
|
||||
electronApp.setAppUserModelId('com.electron')
|
||||
|
||||
// Default open or close DevTools by F12 in development
|
||||
// and ignore CommandOrControl + R in production.
|
||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||
app.on('browser-window-created', (_, window) => {
|
||||
optimizer.watchWindowShortcuts(window)
|
||||
})
|
||||
|
||||
// IPC test
|
||||
ipcMain.on('ping', () => console.log('pong'))
|
||||
|
||||
// app.on('activate', function () {
|
||||
// // On macOS it's common to re-create a window in the app when the
|
||||
// // dock icon is clicked and there are no other windows open.
|
||||
// if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
// })
|
||||
|
||||
})
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app"s specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import {app, ipcMain, IpcMainEvent } from "electron"
|
||||
import { getWindowByName, getWindowByEvent} from "./windows"
|
||||
import { autoUpdater } from 'electron-updater'
|
||||
import { shutdownServer } from "./serverUtilts"
|
||||
import updateRegister from "./updateRegister"
|
||||
|
||||
|
||||
ipcMain.on('openWindow', (_event: IpcMainEvent, name: WindowNameType, router_url="") => {
|
||||
const win = getWindowByName(name, router_url)
|
||||
win.show()
|
||||
})
|
||||
|
||||
ipcMain.on('closeWindow', (_event: IpcMainEvent, name: WindowNameType) => {
|
||||
getWindowByName(name).hide()
|
||||
})
|
||||
|
||||
ipcMain.on('setIgnoreMouseEvents',
|
||||
(event: IpcMainEvent, ignore: boolean, options?:{forward: boolean}) => {
|
||||
getWindowByEvent(event).setIgnoreMouseEvents(ignore, options)
|
||||
})
|
||||
|
||||
ipcMain.handle('getVersion', async (_event) => {
|
||||
return app.getVersion()
|
||||
})
|
||||
|
||||
ipcMain.on('restartApp', async () => {
|
||||
await shutdownServer()
|
||||
autoUpdater.quitAndInstall()
|
||||
})
|
||||
|
||||
// 检测更新
|
||||
ipcMain.on('checkUpdate', (event) => {
|
||||
const win = getWindowByEvent(event);
|
||||
updateRegister(win)
|
||||
autoUpdater.checkForUpdates();
|
||||
win.webContents.send('updateInfo', `正在检查更新...`)
|
||||
|
||||
})
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
export async function shutdownServer(){
|
||||
try{
|
||||
await fetch('http://127.0.0.1:5000/shutdown')
|
||||
|
||||
// 退出时会报异常
|
||||
}catch(e){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { IpcMainInvokeEvent, dialog } from "electron"
|
||||
import { ipcMain } from "electron"
|
||||
import { getWindowByName } from "./windows"
|
||||
import { findOne } from "./db/query"
|
||||
const { app, globalShortcut } = require('electron')
|
||||
|
||||
ipcMain.handle("shortcut", (_event: IpcMainInvokeEvent) => {
|
||||
// react 严格模式会执行两次,可能会导致快捷键重复注册,这里在注册前会删除旧快捷键
|
||||
return registerSearchShortcut()
|
||||
|
||||
})
|
||||
|
||||
|
||||
export function registerSearchShortcut(){
|
||||
globalShortcut.unregisterAll()
|
||||
const ret = findOne(`select * from config where id=1`) as {content: string}
|
||||
const shortcut = JSON.parse(ret.content).shortcut as string
|
||||
if (shortcut && globalShortcut.isRegistered(shortcut)){
|
||||
dialog.showErrorBox('提示', '快捷键注册失败,请更换')
|
||||
return false
|
||||
}
|
||||
|
||||
const win = getWindowByName('search')
|
||||
const res = globalShortcut.register(shortcut, () => {
|
||||
win.isVisible() ? win.hide() : win.show()
|
||||
})
|
||||
return res
|
||||
}
|
||||
app.on('will-quit', () => {
|
||||
// Unregister all shortcuts.
|
||||
globalShortcut.unregisterAll()
|
||||
})
|
||||
@@ -1,35 +0,0 @@
|
||||
import { BrowserWindow } from "electron"
|
||||
import { autoUpdater } from "electron-updater"
|
||||
|
||||
export default (win: BrowserWindow) => {
|
||||
//自动下载更新
|
||||
autoUpdater.autoDownload = false
|
||||
//退出时自动安装更新
|
||||
autoUpdater.autoInstallOnAppQuit = true
|
||||
autoUpdater.removeAllListeners()
|
||||
autoUpdater.on('update-available', (_info) => {
|
||||
win.webContents.send('updateInfo', `发现新的版本!}`)
|
||||
autoUpdater.downloadUpdate()
|
||||
})
|
||||
|
||||
//没有新版本时
|
||||
autoUpdater.on('update-not-available', (_info) => {
|
||||
win.webContents.send('updateInfo', '当前为最新版本')
|
||||
})
|
||||
|
||||
autoUpdater.on('update-downloaded', async () => {
|
||||
win.webContents.send('updateInfo', `下载完成,重启软件完成更新!`)
|
||||
|
||||
});
|
||||
|
||||
// 监听下载进度
|
||||
autoUpdater.on('download-progress', (progress) => {
|
||||
win.webContents.send('updateInfo', `发现新的版本,下载进度: ${progress.percent}%`)
|
||||
})
|
||||
|
||||
//更新发生错误
|
||||
autoUpdater.on('error', (_info) => {
|
||||
|
||||
win.webContents.send('updateInfo', `软件更新失败,重试中...`)
|
||||
})
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import { BrowserWindow, IpcMainEvent, IpcMainInvokeEvent, Menu, Tray, app } from "electron"
|
||||
import { OptionsType, createWindow} from "./createWindow"
|
||||
const { exec } = require('child_process');
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import { shutdownServer } from "./serverUtilts";
|
||||
export const config = {
|
||||
search: {
|
||||
id: 0,
|
||||
options: {
|
||||
initShow: true,
|
||||
hash: '',
|
||||
openDevTools: false,
|
||||
}
|
||||
},
|
||||
code: {
|
||||
id: 0,
|
||||
options: {
|
||||
initShow: true,
|
||||
width: 1300,
|
||||
height: 700,
|
||||
openDevTools: false,
|
||||
frame: true,
|
||||
transparent: false,
|
||||
hash: '/#config/category/contentList'
|
||||
}
|
||||
},
|
||||
config: {
|
||||
id: 0,
|
||||
options: {
|
||||
initShow: false,
|
||||
width: 830,
|
||||
height: 670,
|
||||
openDevTools: false,
|
||||
frame: true,
|
||||
transparent: false,
|
||||
hash: '/#config'
|
||||
}
|
||||
},
|
||||
about: {
|
||||
id: 0,
|
||||
options: {
|
||||
initShow: false,
|
||||
width: 500,
|
||||
height: 300,
|
||||
openDevTools: false,
|
||||
frame: true,
|
||||
transparent: false,
|
||||
hash: '/#about'
|
||||
}
|
||||
}
|
||||
|
||||
} as Record<WindowNameType, {id: number, options: OptionsType }>
|
||||
// createWindow({})
|
||||
|
||||
// 根据名称获取窗口
|
||||
export const getWindowByName = (name: WindowNameType, router_url="")=>{
|
||||
|
||||
// 根据id取得窗口
|
||||
let win = BrowserWindow.fromId(config[name].id)
|
||||
// 避免重复点击重复创建窗口
|
||||
if (!win) {
|
||||
win = createWindow(config[name].options,router_url)
|
||||
config[name].id = win.id
|
||||
}
|
||||
// 在页面加载完成后设置窗口标题
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
win.setTitle('autoMate');
|
||||
});
|
||||
|
||||
// 修改窗口图标 (需要提供图标的路径)
|
||||
win.setIcon('resources/icon.png');
|
||||
|
||||
return win
|
||||
}
|
||||
|
||||
|
||||
// 根据触发来源获取窗口
|
||||
export const getWindowByEvent = (event: IpcMainEvent | IpcMainInvokeEvent) => {
|
||||
return BrowserWindow.fromWebContents(event.sender)!
|
||||
}
|
||||
|
||||
function createTray(){
|
||||
const tray = new Tray('resources/icon.png')
|
||||
tray.setToolTip('autoMate智子')
|
||||
tray.setTitle('autoMate')
|
||||
tray.addListener('click', () => {
|
||||
getWindowByName('search').show()
|
||||
})
|
||||
|
||||
const menu = Menu.buildFromTemplate([
|
||||
{ label: '关于', click: () => { getWindowByName('about').show() } },
|
||||
|
||||
{ label: '配置', click: () => { getWindowByName('config').show() } },
|
||||
|
||||
{ label: '退出', click: async () => {
|
||||
await shutdownServer()
|
||||
app.quit()
|
||||
}
|
||||
|
||||
},
|
||||
])
|
||||
tray.setContextMenu(menu)
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createTray()
|
||||
const win = getWindowByName('search')
|
||||
win.on('blur', () => {
|
||||
win.hide()
|
||||
})
|
||||
if(!is.dev){
|
||||
const serverPath = process.platform === 'win32' ? '.\\autoMateServer.exe' : './autoMateServer.exe';
|
||||
exec(serverPath, (error: any, stdout: any, stderr: any) => {
|
||||
if (error) {
|
||||
console.error(`error: ${error}`);
|
||||
return;
|
||||
}
|
||||
console.log(`stdout: ${stdout}`);
|
||||
console.error(`stderr: ${stderr}`);
|
||||
});}
|
||||
|
||||
// getWindowByName('code')
|
||||
// getWindowByName('about')
|
||||
|
||||
})
|
||||
22
app/src/preload/index.d.ts
vendored
22
app/src/preload/index.d.ts
vendored
@@ -1,22 +0,0 @@
|
||||
import { ElectronAPI } from '@electron-toolkit/preload'
|
||||
import { BrowserWindow } from 'electron'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI
|
||||
api: {
|
||||
shortcut: () => Promise<boolean>,
|
||||
setIgnoreMouseEvents: (ignore: boolean, options?: { forward: boolean }) => void,
|
||||
openConfigWindow: () => void,
|
||||
sql: <T>(sql: string, type: SqlActionType, params?: Record<string, any>) => Promise<T>,
|
||||
openWindow: (name: WindowNameType, router_url?: string) => BrowserWindow,
|
||||
closeWindow: (name: WindowNameType) => void,
|
||||
initTable: () => void,
|
||||
getConfig: () => Promise<ConfigType>,
|
||||
getVersion: () => Promise<string>,
|
||||
checkUpdate: () => void,
|
||||
updateInfo: (fn: (value: string) => void) => void,
|
||||
restartApp: () => void,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
import { electronAPI } from '@electron-toolkit/preload'
|
||||
|
||||
// Custom APIs for renderer
|
||||
const api = {
|
||||
shortcut: () => {
|
||||
return ipcRenderer.invoke("shortcut")
|
||||
},
|
||||
setIgnoreMouseEvents: (ignore: boolean, options?: { forward: boolean }) => {
|
||||
ipcRenderer.send("setIgnoreMouseEvents", ignore, options)
|
||||
},
|
||||
openConfigWindow: () => {
|
||||
ipcRenderer.send("openConfigWindow")
|
||||
},
|
||||
sql: (sql: string, type: SqlActionType, params={}) => {
|
||||
return ipcRenderer.invoke("sql", sql, type, params)
|
||||
},
|
||||
openWindow: (name: WindowNameType, router_url?: string) =>{
|
||||
return ipcRenderer.send("openWindow", name, router_url)
|
||||
},
|
||||
closeWindow: (name: WindowNameType) =>{
|
||||
ipcRenderer.send("closeWindow", name)
|
||||
},
|
||||
|
||||
initTable: () => {
|
||||
ipcRenderer.send("initTable")
|
||||
},
|
||||
getConfig: () => {
|
||||
return (ipcRenderer.invoke("getConfig") as Promise<ConfigType>)
|
||||
},
|
||||
getVersion: () => {
|
||||
return ipcRenderer.invoke("getVersion")
|
||||
},
|
||||
checkUpdate: () => {
|
||||
ipcRenderer.send("checkUpdate")
|
||||
},
|
||||
updateInfo: (fn: (value: string) => void)=> {
|
||||
ipcRenderer.on("updateInfo", (_event, value)=> fn(value))
|
||||
},
|
||||
restartApp: () => {
|
||||
ipcRenderer.send("restartApp")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Use `contextBridge` APIs to expose Electron APIs to
|
||||
// renderer only if context isolation is enabled, otherwise
|
||||
// just add to the DOM global.
|
||||
if (process.contextIsolated) {
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI)
|
||||
contextBridge.exposeInMainWorld('api', api)
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore (define in dts)
|
||||
window.electron = electronAPI
|
||||
// @ts-ignore (define in dts)
|
||||
window.api = api
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Electron</title>
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta
|
||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,29 +0,0 @@
|
||||
import Result from "./components/Result"
|
||||
import Search from "./components/Search"
|
||||
import { CodeProvider } from "./context/CodeContext"
|
||||
import Error from "./components/Error"
|
||||
import { MutableRefObject, useEffect, useRef } from "react"
|
||||
import useIgnoreMouseEvents from "./hooks/useIgnoreMouseEvents"
|
||||
|
||||
|
||||
function App(): JSX.Element {
|
||||
const mainRef = useRef<HTMLDivElement>(null)
|
||||
const {setIgnoreMouseEvents} = useIgnoreMouseEvents()
|
||||
useEffect(()=>{
|
||||
setIgnoreMouseEvents(mainRef as MutableRefObject<HTMLDivElement>)
|
||||
}, [])
|
||||
// const shortcut = useShortCut("CommandOrControl+n")
|
||||
// shortcut.register()
|
||||
return (
|
||||
<CodeProvider>
|
||||
<main className="relative" ref={mainRef}>
|
||||
<Error/>
|
||||
<Search />
|
||||
<Result />
|
||||
</main>
|
||||
</CodeProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
:root {
|
||||
--ev-c-white: #ffffff;
|
||||
--ev-c-white-soft: #f8f8f8;
|
||||
--ev-c-white-mute: #f2f2f2;
|
||||
|
||||
--ev-c-black: #1b1b1f;
|
||||
--ev-c-black-soft: #222222;
|
||||
--ev-c-black-mute: #282828;
|
||||
|
||||
--ev-c-gray-1: #515c67;
|
||||
--ev-c-gray-2: #414853;
|
||||
--ev-c-gray-3: #32363f;
|
||||
|
||||
--ev-c-text-1: rgba(255, 255, 245, 0.86);
|
||||
--ev-c-text-2: rgba(235, 235, 245, 0.6);
|
||||
--ev-c-text-3: rgba(235, 235, 245, 0.38);
|
||||
|
||||
--ev-button-alt-border: transparent;
|
||||
--ev-button-alt-text: var(--ev-c-text-1);
|
||||
--ev-button-alt-bg: var(--ev-c-gray-3);
|
||||
--ev-button-alt-hover-border: transparent;
|
||||
--ev-button-alt-hover-text: var(--ev-c-text-1);
|
||||
--ev-button-alt-hover-bg: var(--ev-c-gray-2);
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-background: var(--ev-c-black);
|
||||
--color-background-soft: var(--ev-c-black-soft);
|
||||
--color-background-mute: var(--ev-c-black-mute);
|
||||
|
||||
--color-text: var(--ev-c-text-1);
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<svg viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="64" cy="64" r="64" fill="#2F3242"/>
|
||||
<ellipse cx="63.9835" cy="23.2036" rx="4.48794" ry="4.495" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||
<path d="M51.3954 39.5028C52.3733 39.6812 53.3108 39.033 53.4892 38.055C53.6676 37.0771 53.0194 36.1396 52.0414 35.9612L51.3954 39.5028ZM28.6153 43.5751L30.1748 44.4741L30.1748 44.4741L28.6153 43.5751ZM28.9393 60.9358C29.4332 61.7985 30.5329 62.0976 31.3957 61.6037C32.2585 61.1098 32.5575 60.0101 32.0636 59.1473L28.9393 60.9358ZM37.6935 66.7457C37.025 66.01 35.8866 65.9554 35.1508 66.6239C34.415 67.2924 34.3605 68.4308 35.029 69.1666L37.6935 66.7457ZM53.7489 81.7014L52.8478 83.2597L53.7489 81.7014ZM96.9206 89.515C97.7416 88.9544 97.9526 87.8344 97.3919 87.0135C96.8313 86.1925 95.7113 85.9815 94.8904 86.5422L96.9206 89.515ZM52.0414 35.9612C46.4712 34.9451 41.2848 34.8966 36.9738 35.9376C32.6548 36.9806 29.0841 39.1576 27.0559 42.6762L30.1748 44.4741C31.5693 42.0549 34.1448 40.3243 37.8188 39.4371C41.5009 38.5479 46.1547 38.5468 51.3954 39.5028L52.0414 35.9612ZM27.0559 42.6762C24.043 47.9029 25.2781 54.5399 28.9393 60.9358L32.0636 59.1473C28.6579 53.1977 28.1088 48.0581 30.1748 44.4741L27.0559 42.6762ZM35.029 69.1666C39.6385 74.24 45.7158 79.1355 52.8478 83.2597L54.6499 80.1432C47.8081 76.1868 42.0298 71.5185 37.6935 66.7457L35.029 69.1666ZM52.8478 83.2597C61.344 88.1726 70.0465 91.2445 77.7351 92.3608C85.359 93.4677 92.2744 92.6881 96.9206 89.515L94.8904 86.5422C91.3255 88.9767 85.4902 89.849 78.2524 88.7982C71.0793 87.7567 62.809 84.8612 54.6499 80.1432L52.8478 83.2597ZM105.359 84.9077C105.359 81.4337 102.546 78.6127 99.071 78.6127V82.2127C100.553 82.2127 101.759 83.4166 101.759 84.9077H105.359ZM99.071 78.6127C95.5956 78.6127 92.7831 81.4337 92.7831 84.9077H96.3831C96.3831 83.4166 97.5892 82.2127 99.071 82.2127V78.6127ZM92.7831 84.9077C92.7831 88.3817 95.5956 91.2027 99.071 91.2027V87.6027C97.5892 87.6027 96.3831 86.3988 96.3831 84.9077H92.7831ZM99.071 91.2027C102.546 91.2027 105.359 88.3817 105.359 84.9077H101.759C101.759 86.3988 100.553 87.6027 99.071 87.6027V91.2027Z" fill="#A2ECFB"/>
|
||||
<path d="M91.4873 65.382C90.8456 66.1412 90.9409 67.2769 91.7002 67.9186C92.4594 68.5603 93.5951 68.465 94.2368 67.7058L91.4873 65.382ZM99.3169 43.6354L97.7574 44.5344L99.3169 43.6354ZM84.507 35.2412C83.513 35.2282 82.6967 36.0236 82.6838 37.0176C82.6708 38.0116 83.4661 38.8279 84.4602 38.8409L84.507 35.2412ZM74.9407 39.8801C75.9127 39.6716 76.5315 38.7145 76.323 37.7425C76.1144 36.7706 75.1573 36.1517 74.1854 36.3603L74.9407 39.8801ZM53.7836 46.3728L54.6847 47.931L53.7836 46.3728ZM25.5491 80.9047C25.6932 81.8883 26.6074 82.5688 27.5911 82.4247C28.5747 82.2806 29.2552 81.3664 29.1111 80.3828L25.5491 80.9047ZM94.2368 67.7058C97.8838 63.3907 100.505 58.927 101.752 54.678C103.001 50.4213 102.9 46.2472 100.876 42.7365L97.7574 44.5344C99.1494 46.9491 99.3603 50.0419 98.2974 53.6644C97.2323 57.2945 94.9184 61.3223 91.4873 65.382L94.2368 67.7058ZM100.876 42.7365C97.9119 37.5938 91.7082 35.335 84.507 35.2412L84.4602 38.8409C91.1328 38.9278 95.7262 41.0106 97.7574 44.5344L100.876 42.7365ZM74.1854 36.3603C67.4362 37.8086 60.0878 40.648 52.8826 44.8146L54.6847 47.931C61.5972 43.9338 68.5948 41.2419 74.9407 39.8801L74.1854 36.3603ZM52.8826 44.8146C44.1366 49.872 36.9669 56.0954 32.1491 62.3927C27.3774 68.63 24.7148 75.2115 25.5491 80.9047L29.1111 80.3828C28.4839 76.1026 30.4747 70.5062 35.0084 64.5802C39.496 58.7143 46.2839 52.7889 54.6847 47.931L52.8826 44.8146Z" fill="#A2ECFB"/>
|
||||
<path d="M49.0825 87.2295C48.7478 86.2934 47.7176 85.8059 46.7816 86.1406C45.8455 86.4753 45.358 87.5055 45.6927 88.4416L49.0825 87.2295ZM78.5635 96.4256C79.075 95.5732 78.7988 94.4675 77.9464 93.9559C77.0941 93.4443 75.9884 93.7205 75.4768 94.5729L78.5635 96.4256ZM79.5703 85.1795C79.2738 86.1284 79.8027 87.1379 80.7516 87.4344C81.7004 87.7308 82.71 87.2019 83.0064 86.2531L79.5703 85.1795ZM84.3832 64.0673H82.5832H84.3832ZM69.156 22.5301C68.2477 22.1261 67.1838 22.535 66.7799 23.4433C66.3759 24.3517 66.7848 25.4155 67.6931 25.8194L69.156 22.5301ZM45.6927 88.4416C47.5994 93.7741 50.1496 98.2905 53.2032 101.505C56.2623 104.724 59.9279 106.731 63.9835 106.731V103.131C61.1984 103.131 58.4165 101.765 55.8131 99.0249C53.2042 96.279 50.8768 92.2477 49.0825 87.2295L45.6927 88.4416ZM63.9835 106.731C69.8694 106.731 74.8921 102.542 78.5635 96.4256L75.4768 94.5729C72.0781 100.235 68.0122 103.131 63.9835 103.131V106.731ZM83.0064 86.2531C85.0269 79.7864 86.1832 72.1831 86.1832 64.0673H82.5832C82.5832 71.8536 81.4723 79.0919 79.5703 85.1795L83.0064 86.2531ZM86.1832 64.0673C86.1832 54.1144 84.4439 44.922 81.4961 37.6502C78.5748 30.4436 74.3436 24.8371 69.156 22.5301L67.6931 25.8194C71.6364 27.5731 75.3846 32.1564 78.1598 39.0026C80.9086 45.7836 82.5832 54.507 82.5832 64.0673H86.1832Z" fill="#A2ECFB"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M103.559 84.9077C103.559 82.4252 101.55 80.4127 99.071 80.4127C96.5924 80.4127 94.5831 82.4252 94.5831 84.9077C94.5831 87.3902 96.5924 89.4027 99.071 89.4027C101.55 89.4027 103.559 87.3902 103.559 84.9077V84.9077Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.8143 89.4027C31.2929 89.4027 33.3023 87.3902 33.3023 84.9077C33.3023 82.4252 31.2929 80.4127 28.8143 80.4127C26.3357 80.4127 24.3264 82.4252 24.3264 84.9077C24.3264 87.3902 26.3357 89.4027 28.8143 89.4027V89.4027V89.4027Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M64.8501 68.0857C62.6341 68.5652 60.451 67.1547 59.9713 64.9353C59.4934 62.7159 60.9007 60.5293 63.1167 60.0489C65.3326 59.5693 67.5157 60.9798 67.9954 63.1992C68.4742 65.4186 67.066 67.6052 64.8501 68.0857Z" fill="#A2ECFB"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.7 KiB |
@@ -1,64 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
--radius: 0.5rem;
|
||||
--chart-1: 12 76% 61%;
|
||||
--chart-2: 173 58% 39%;
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
@apply w-screen h-screen;
|
||||
}
|
||||
|
||||
* {
|
||||
opacity: 0.995;
|
||||
}
|
||||
|
||||
|
||||
// 可以拖动
|
||||
.drag {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
// 不可以拖动的组件
|
||||
.no-drag,
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
option {
|
||||
-webkit-app-region: none;
|
||||
}
|
||||
|
||||
.contextMenu {
|
||||
@apply rounded-md;
|
||||
|
||||
button {
|
||||
@apply bg-[#2c3e50] text-white px-5 py-1 hover:bg-[#34495e];
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
@import './base.css';
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background-image: url('./wavy-lines.svg');
|
||||
background-size: cover;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
code {
|
||||
font-weight: 600;
|
||||
padding: 3px 5px;
|
||||
border-radius: 2px;
|
||||
background-color: var(--color-background-mute);
|
||||
font-family:
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
SF Mono,
|
||||
Menlo,
|
||||
Consolas,
|
||||
Liberation Mono,
|
||||
monospace;
|
||||
font-size: 85%;
|
||||
}
|
||||
|
||||
#root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-bottom: 20px;
|
||||
-webkit-user-drag: none;
|
||||
height: 128px;
|
||||
width: 128px;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 1.2em #6988e6aa);
|
||||
}
|
||||
|
||||
.creator {
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
color: var(--ev-c-text-2);
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 28px;
|
||||
color: var(--ev-c-text-1);
|
||||
font-weight: 700;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
margin: 0 10px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: var(--ev-c-text-2);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.react {
|
||||
background: -webkit-linear-gradient(315deg, #087ea4 55%, #7c93ee);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.ts {
|
||||
background: -webkit-linear-gradient(315deg, #3178c6 45%, #f0dc4e);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
padding-top: 32px;
|
||||
margin: -6px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.action {
|
||||
flex-shrink: 0;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.action a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
border: 1px solid transparent;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
border-radius: 20px;
|
||||
padding: 0 20px;
|
||||
line-height: 38px;
|
||||
font-size: 14px;
|
||||
border-color: var(--ev-button-alt-border);
|
||||
color: var(--ev-button-alt-text);
|
||||
background-color: var(--ev-button-alt-bg);
|
||||
}
|
||||
|
||||
.action a:hover {
|
||||
border-color: var(--ev-button-alt-hover-border);
|
||||
color: var(--ev-button-alt-hover-text);
|
||||
background-color: var(--ev-button-alt-hover-bg);
|
||||
}
|
||||
|
||||
.versions {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
margin: 0 auto;
|
||||
padding: 15px 0;
|
||||
font-family: 'Menlo', 'Lucida Console', monospace;
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
border-radius: 22px;
|
||||
background-color: #202127;
|
||||
backdrop-filter: blur(24px);
|
||||
}
|
||||
|
||||
.versions li {
|
||||
display: block;
|
||||
float: left;
|
||||
border-right: 1px solid var(--ev-c-gray-1);
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
opacity: 0.8;
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.text {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
.versions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
.tip,
|
||||
.actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,25 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1422 800" opacity="0.3">
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="oooscillate-grad">
|
||||
<stop stop-color="hsl(206, 75%, 49%)" stop-opacity="1" offset="0%"></stop>
|
||||
<stop stop-color="hsl(331, 90%, 56%)" stop-opacity="1" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g stroke-width="1" stroke="url(#oooscillate-grad)" fill="none" stroke-linecap="round">
|
||||
<path d="M 0 448 Q 355.5 -100 711 400 Q 1066.5 900 1422 448" opacity="0.05"></path>
|
||||
<path d="M 0 420 Q 355.5 -100 711 400 Q 1066.5 900 1422 420" opacity="0.11"></path>
|
||||
<path d="M 0 392 Q 355.5 -100 711 400 Q 1066.5 900 1422 392" opacity="0.18"></path>
|
||||
<path d="M 0 364 Q 355.5 -100 711 400 Q 1066.5 900 1422 364" opacity="0.24"></path>
|
||||
<path d="M 0 336 Q 355.5 -100 711 400 Q 1066.5 900 1422 336" opacity="0.30"></path>
|
||||
<path d="M 0 308 Q 355.5 -100 711 400 Q 1066.5 900 1422 308" opacity="0.37"></path>
|
||||
<path d="M 0 280 Q 355.5 -100 711 400 Q 1066.5 900 1422 280" opacity="0.43"></path>
|
||||
<path d="M 0 252 Q 355.5 -100 711 400 Q 1066.5 900 1422 252" opacity="0.49"></path>
|
||||
<path d="M 0 224 Q 355.5 -100 711 400 Q 1066.5 900 1422 224" opacity="0.56"></path>
|
||||
<path d="M 0 196 Q 355.5 -100 711 400 Q 1066.5 900 1422 196" opacity="0.62"></path>
|
||||
<path d="M 0 168 Q 355.5 -100 711 400 Q 1066.5 900 1422 168" opacity="0.68"></path>
|
||||
<path d="M 0 140 Q 355.5 -100 711 400 Q 1066.5 900 1422 140" opacity="0.75"></path>
|
||||
<path d="M 0 112 Q 355.5 -100 711 400 Q 1066.5 900 1422 112" opacity="0.81"></path>
|
||||
<path d="M 0 84 Q 355.5 -100 711 400 Q 1066.5 900 1422 84" opacity="0.87"></path>
|
||||
<path d="M 0 56 Q 355.5 -100 711 400 Q 1066.5 900 1422 56" opacity="0.94"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,58 +0,0 @@
|
||||
import { FolderClose } from "@icon-park/react"
|
||||
import { NavLink, useFetcher} from "react-router-dom"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useStore } from "@renderer/store/useStore"
|
||||
import useCategory from "@renderer/hooks/useCategory"
|
||||
interface Props {
|
||||
category: CategoryType
|
||||
}
|
||||
|
||||
export const CategoryItem = ({ category }: Props) => {
|
||||
// fetcher 不会刷新路由
|
||||
const fetcher = useFetcher()
|
||||
const setEditCategoryId = useStore(state => state.setEditCategoryId)
|
||||
const editCategoryId = useStore(state => state.editCategoryId)
|
||||
const { contextMenu, dragHandle } = useCategory(category)
|
||||
return (
|
||||
<>
|
||||
{editCategoryId == category.id ? (
|
||||
<div className={styles.input}>
|
||||
<input
|
||||
defaultValue={category.name}
|
||||
name="name"
|
||||
autoFocus
|
||||
onKeyDown={
|
||||
(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
fetcher.submit({ id: category.id, name: e.currentTarget.value }, { method: 'PUT' })
|
||||
setEditCategoryId(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<NavLink
|
||||
onDoubleClick={
|
||||
(_e) => {
|
||||
setEditCategoryId(category.id)
|
||||
}
|
||||
}
|
||||
to={`/config/category/contentList/${category.id}`}
|
||||
key={category.id}
|
||||
className={({ isActive }) => {
|
||||
return isActive ? styles.active : styles.link
|
||||
}}
|
||||
onContextMenu={contextMenu()}
|
||||
{...dragHandle}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
<FolderClose theme="outline" size="12" strokeWidth={3}></FolderClose>
|
||||
<div className="truncate">{category.name}</div>
|
||||
</div>
|
||||
</NavLink>)
|
||||
}
|
||||
</>
|
||||
)
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
$height: 32px;
|
||||
@mixin commonLink{
|
||||
@apply px-2 py-2 truncate cursor-pointer block mx-1 my-1 rounded-md;
|
||||
height: $height;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
// link 编译完是 a 标题
|
||||
.link{
|
||||
@include commonLink();
|
||||
@apply hover:bg-gray-200;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.active{
|
||||
@include commonLink();
|
||||
// mx-1 外边距
|
||||
@apply bg-blue-600 text-white rounded-md;
|
||||
}
|
||||
|
||||
.input{
|
||||
@apply w-full px-2 ;
|
||||
height: $height;
|
||||
input {
|
||||
@apply h-full w-full rounded-md px-2 border outline-none;
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
import { ProChat, ProChatInstance } from '@ant-design/pro-chat';
|
||||
import useChat from '@renderer/hooks/useChat';
|
||||
import useRunCode from '@renderer/hooks/useRunCode';
|
||||
import { useStore } from '@renderer/store/useStore';
|
||||
import { Button } from 'antd';
|
||||
import { useTheme } from 'antd-style';
|
||||
import { useEffect, useRef } from 'react';
|
||||
export default function Chat(props: {id: number, revalidator: () => void, search: string}) {
|
||||
const {search} = props;
|
||||
const {getResponse} = useChat()
|
||||
const theme = useTheme();
|
||||
const chatMessages = useStore(state=>state.chatMessages)
|
||||
const setMessages = useStore(state=>state.setChatMessage)
|
||||
const proChatRef = useRef<ProChatInstance>();
|
||||
// 确保 useeffect 只执行一次
|
||||
const effectRan = useRef(false);
|
||||
const {id, revalidator} = props;
|
||||
const { runCode } = useRunCode()
|
||||
|
||||
useEffect(()=>{
|
||||
if (effectRan.current === false) {
|
||||
if (search) {
|
||||
proChatRef.current?.sendMessage(search)
|
||||
}
|
||||
effectRan.current = true;
|
||||
}
|
||||
}, [])
|
||||
return (
|
||||
<ProChat
|
||||
chats={chatMessages}
|
||||
onChatsChange={(chat)=>{
|
||||
setMessages(chat)
|
||||
}}
|
||||
chatItemRenderConfig={{
|
||||
contentRender: (props, defaultDom) => {
|
||||
if (props.originData!.role === 'coder' && props.originData!.originData) {
|
||||
try {
|
||||
// const resJson = JSON.parse(item?.originData?.content);
|
||||
return (<div className='flex flex-row'>
|
||||
<Button onClick={()=>{
|
||||
const code = props.originData!.originData.code;
|
||||
runCode(code)
|
||||
}}>
|
||||
运行
|
||||
</Button>
|
||||
<Button className='ml-2' onClick={()=>{
|
||||
// 更新代码
|
||||
window.api.sql(
|
||||
`update contents set content=@content where id=@id`,
|
||||
"update",
|
||||
{
|
||||
content: props.originData!.originData.code,
|
||||
id
|
||||
}
|
||||
)
|
||||
// 让代码展示区域重新加载
|
||||
revalidator()
|
||||
}}>
|
||||
应用
|
||||
</Button>
|
||||
</div>)
|
||||
|
||||
} catch (error) {
|
||||
return defaultDom;
|
||||
}
|
||||
}
|
||||
return defaultDom;
|
||||
}
|
||||
}}
|
||||
chatRef={proChatRef}
|
||||
style={{ background: theme.colorBgLayout }}
|
||||
// assistantMeta={{ avatar: '', title: '智子', backgroundColor: '#67dedd' }}
|
||||
helloMessage={
|
||||
<div className='text-black'><b>你好,我叫智子,你的AI自动化代码助手!</b>有什么要求可以随时吩咐!</div>
|
||||
}
|
||||
|
||||
request={async (messages) => {
|
||||
const response = await getResponse(messages)
|
||||
console.log(response)
|
||||
if (response.status === 0 && response.code != "") {
|
||||
setTimeout(() => {
|
||||
proChatRef.current?.pushChat({
|
||||
type: 'text',
|
||||
content: response.content,
|
||||
role: 'coder',
|
||||
originData: response
|
||||
});
|
||||
|
||||
}, 1000); // 延时1秒推送消息
|
||||
}
|
||||
return new Response(response.content)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
.code-mirror {
|
||||
// font-size: 20px ;
|
||||
@apply text-[18px] border-t pt-5 p-3;
|
||||
:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import { python } from '@codemirror/lang-python';
|
||||
import { Drawer, FloatButton } from 'antd';
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons';
|
||||
import Chat from '@renderer/components/Chat';
|
||||
import "./codeEditor.scss"
|
||||
import { useEffect } from 'react';
|
||||
interface CodeEditorProps {
|
||||
id: number;
|
||||
defaultValue: string;
|
||||
revalidator: () => void;
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
search: string;
|
||||
}
|
||||
export default function CodeEditor(props: CodeEditorProps) {
|
||||
const { id, defaultValue, revalidator, open, setOpen, search} = props;
|
||||
useEffect(()=>{
|
||||
if (search) {
|
||||
setOpen(true)
|
||||
}
|
||||
}, [])
|
||||
return (
|
||||
<div>
|
||||
<FloatButton icon={<QuestionCircleOutlined />} type="primary" onClick={() => {
|
||||
setOpen(true);
|
||||
}} />
|
||||
<Drawer
|
||||
title="智子"
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
open={open}
|
||||
styles={{
|
||||
body: {
|
||||
padding: 0,
|
||||
},
|
||||
}}>
|
||||
<Chat id={id} revalidator={revalidator} search={search}/>
|
||||
</Drawer>
|
||||
<CodeMirror
|
||||
maxHeight='550px'
|
||||
maxWidth='850px'
|
||||
className='code-mirror'
|
||||
value={defaultValue}
|
||||
onChange={async (value) => {
|
||||
await window.api.sql(
|
||||
`update contents set content=@content where id=@id`,
|
||||
"update",
|
||||
{
|
||||
id,
|
||||
content: value
|
||||
}
|
||||
)
|
||||
}}
|
||||
extensions={[python()]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
import { Delete } from '@icon-park/react';
|
||||
import dayjs from 'dayjs';
|
||||
import { useContextMenu } from 'mantine-contextmenu';
|
||||
import { NavLink, useSubmit } from 'react-router-dom'
|
||||
import styles from "./styles.module.scss"
|
||||
interface Props {
|
||||
content: ContentType
|
||||
}
|
||||
export const ContentItem = ({content}: Props) => {
|
||||
const submit = useSubmit()
|
||||
const { showContextMenu } = useContextMenu();
|
||||
return (
|
||||
<NavLink
|
||||
key={content.id}
|
||||
to={`/config/category/contentList/${content.category_id}/content/${content.id}`}
|
||||
className={({isActive})=>{
|
||||
return [isActive ? styles.active : '', styles.link].join(' ')
|
||||
}}
|
||||
onDragStart={(e)=>{
|
||||
e.dataTransfer.setData('id', String(content.id))
|
||||
}}
|
||||
onContextMenu={showContextMenu([
|
||||
{
|
||||
key: 'remove',
|
||||
icon: <Delete theme="outline" size="18" strokeWidth={3} />,
|
||||
title: '删除动作',
|
||||
onClick: () => {
|
||||
submit({id: content.id}, {method: 'DELETE'})
|
||||
},
|
||||
}
|
||||
],
|
||||
{className: 'contextMenu'})}
|
||||
>
|
||||
<div className="truncate">{content.title}</div>
|
||||
<div className="text-[10px] opacity-80 ">{dayjs(content.created_at).format("YY/MM/DD")}</div>
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
.link{
|
||||
|
||||
@apply truncate py-1 px-[6px] cursor-pointer flex items-center
|
||||
gap-1 border-b mx-2 my-1 hover:bg-slate-100;
|
||||
|
||||
}
|
||||
|
||||
.active{
|
||||
@apply bg-blue-700 text-white rounded-md border-none hover:bg-blue-700;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Add } from "@icon-park/react"
|
||||
import { Form, useSubmit } from "react-router-dom"
|
||||
|
||||
export const ContentSearch = () => {
|
||||
const submit = useSubmit()
|
||||
return (
|
||||
<Form>
|
||||
<div className="border-b px-3 flex justify-between items-center">
|
||||
<input
|
||||
name="searchWord"
|
||||
type="text"
|
||||
placeholder="搜索..."
|
||||
className="outline-none text-sm font-bold py-2 w-full"
|
||||
onChange={(e) => {
|
||||
submit(e.target.form)
|
||||
}}
|
||||
/>
|
||||
<Add
|
||||
theme="outline"
|
||||
size="18"
|
||||
fill="#000000"
|
||||
strokeWidth={2}
|
||||
onClick={()=>{
|
||||
submit({action: 'add'}, {method: 'post'})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { useStore } from "@renderer/store/useStore"
|
||||
import { Alert } from "antd"
|
||||
import { useEffect } from "react"
|
||||
|
||||
function Error(){
|
||||
const error = useStore(state => state.error)
|
||||
const setError = useStore(state => state.setError)
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => setError(""), 10000)
|
||||
return () => clearTimeout(id)
|
||||
}, [error])
|
||||
if (!error) return <></>
|
||||
return (<main className="absolute top-0 z-10 w-full">
|
||||
<Alert message={error} type="error" showIcon />
|
||||
</main>)
|
||||
|
||||
}
|
||||
|
||||
export default Error
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Add } from '@icon-park/react'
|
||||
import { useSubmit } from 'react-router-dom'
|
||||
|
||||
export const FooterMenu = () => {
|
||||
const submit = useSubmit()
|
||||
return (
|
||||
<>
|
||||
<div className="nav">
|
||||
<Add theme="outline" size="20" fill="#333" onClick={(_e)=>
|
||||
submit(null, {method:'POST'})
|
||||
}/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { AllApplication, FolderFailed } from "@icon-park/react"
|
||||
import { NavLink } from "react-router-dom"
|
||||
import styles from "./styles.module.scss"
|
||||
export const QuickNav = () => {
|
||||
return (
|
||||
<main className="mb-3 border-b">
|
||||
<div className="px-2 mt-2 opacity-90 mb-1 text-[10px]">快捷操作</div>
|
||||
<NavLink to={`/config/category/contentList`} end className={({isActive})=>
|
||||
isActive ? styles.active : styles.link
|
||||
}>
|
||||
<div className="flex items-center gap-1">
|
||||
<AllApplication theme="outline" size="12" strokeWidth={3}/>
|
||||
<div className="truncate">所有内容</div>
|
||||
</div>
|
||||
</NavLink>
|
||||
<NavLink to={`/config/category/contentList/0`} end className={({isActive})=>
|
||||
isActive ? styles.active : styles.link
|
||||
}>
|
||||
<div className="flex items-center gap-1">
|
||||
<FolderFailed theme="outline" size="12" strokeWidth={3}/>
|
||||
<div className="truncate">未分类</div>
|
||||
</div>
|
||||
</NavLink>
|
||||
</main>
|
||||
)
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
@mixin commonLink{
|
||||
@apply px-2 py-2 truncate cursor-pointer block mx-1;
|
||||
|
||||
}
|
||||
// link 编译完是 a 标题
|
||||
.link{
|
||||
@include commonLink;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.active{
|
||||
@include commonLink;
|
||||
// mx-1 外边距
|
||||
@apply bg-orange-400 text-white rounded-md;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
import useSelect from '@renderer/hooks/useSelect'
|
||||
import styles from './styles.module.scss'
|
||||
export default function Result() {
|
||||
const {data, selectId, select} = useSelect()
|
||||
return (
|
||||
<main className = {styles.main}>
|
||||
{data.map((item) => (
|
||||
<div key={item.id}
|
||||
className={item.id == selectId? styles.active : ''}
|
||||
onClick={()=>select(item.id)}
|
||||
>
|
||||
<p>{item.title}</p>
|
||||
</div>
|
||||
))}
|
||||
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
.main{
|
||||
@apply bg-slate-50 px-3 rounded-bl-lg rounded-br-lg -mt-[7px] pb-2;
|
||||
div {
|
||||
@apply text-slate-700 truncate px-2 py-1 rounded-lg;
|
||||
&.active {
|
||||
@apply bg-orange-400 text-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@components/ui/tooltip"
|
||||
import useSearch from "@renderer/hooks/useSearch"
|
||||
import { SettingOne } from "@icon-park/react"
|
||||
import { Button, Input } from "antd"
|
||||
import { useStore } from "@renderer/store/useStore"
|
||||
import { useEffect, useState } from "react"
|
||||
export default function Search(): JSX.Element {
|
||||
const { handleSearch } = useSearch()
|
||||
const search = useStore((state) => state.search)
|
||||
const [version, setVersion] = useState('')
|
||||
const [updateInfo] = useState('')
|
||||
useEffect(() => {
|
||||
window.api.getVersion().then((res) => {
|
||||
setVersion(res)
|
||||
})
|
||||
window.api.updateInfo((value) => {
|
||||
if (value === '软件更新失败,重试中...') {
|
||||
window.api.checkUpdate();
|
||||
}
|
||||
})
|
||||
|
||||
window.api.checkUpdate();
|
||||
}, []);
|
||||
return (
|
||||
<main className="bg-slate-50 p-3 rounded-lg drag" >
|
||||
<div className="bg-slate-200 p-3 rounded-l flex items-center gap-1 no-drag">
|
||||
<TooltipProvider delayDuration={200}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<SettingOne
|
||||
theme="outline"
|
||||
size="22"
|
||||
fill="#34495e"
|
||||
strokeWidth={4}
|
||||
className="cursor-pointer"
|
||||
onClick={() => window.api.openWindow('code')}
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>编辑代码</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<Input
|
||||
value={search}
|
||||
placeholder="请输入常用的action名字以快速调用"
|
||||
onChange={handleSearch}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<section className="text-center text-slate-600 text-xs mt-2 no-drag select-none">
|
||||
<div>
|
||||
autoMate V{version}
|
||||
<span className="text-blue-600 cursor-pointer" onClick={() => window.api.openWindow('config')}>点击配置AI API</span>
|
||||
</div>
|
||||
{updateInfo === '成功' && <Button type="primary" onClick={() => window.api.restartApp()}>重启</Button>}
|
||||
</section>
|
||||
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import * as React from "react"
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||
|
||||
import { cn } from "@renderer/lib/utils"
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root
|
||||
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||
@@ -1 +0,0 @@
|
||||
export const localServerBaseUrl = "http://127.0.0.1:5000"
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Dispatch, SetStateAction, createContext, useState } from "react"
|
||||
import { DataType } from "@renderer/data"
|
||||
interface ContextProps {
|
||||
data: DataType[]
|
||||
setData: Dispatch<SetStateAction<DataType[]>>
|
||||
}
|
||||
export const CodeContext = createContext<ContextProps|undefined>(undefined)
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode
|
||||
}
|
||||
export const CodeProvider = ({children}:Props)=>{
|
||||
const [data, setData] = useState<DataType[]>([])
|
||||
return <CodeContext.Provider value={{data, setData}}>{children}</CodeContext.Provider>
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
export interface DataType {
|
||||
id: number,
|
||||
content: string
|
||||
}
|
||||
export const codes = [
|
||||
{id:1, content: "月报天才猫!"},
|
||||
{id:2, content: "新闻天才猫!"},
|
||||
{id:3, content: "How are you?"},
|
||||
{id:4, content: "I am fine, thank you"},
|
||||
{id:5, content: "How are you?"},
|
||||
{id:6, content: "I am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank youI am fine, thank you"},
|
||||
] as DataType[] //数据断言
|
||||
|
||||
1
app/src/renderer/src/env.d.ts
vendored
1
app/src/renderer/src/env.d.ts
vendored
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -1,45 +0,0 @@
|
||||
import { Delete } from "@icon-park/react"
|
||||
import { useContextMenu } from "mantine-contextmenu"
|
||||
import { useSubmit } from "react-router-dom"
|
||||
import styles from "./styles.module.scss"
|
||||
import useContent from "../useContent"
|
||||
import { DragEvent } from "react"
|
||||
export default (category: CategoryType)=>{
|
||||
const submit = useSubmit()
|
||||
const { showContextMenu } = useContextMenu()
|
||||
const {updateContentCategory} = useContent()
|
||||
|
||||
const contextMenu = () => {
|
||||
return showContextMenu([
|
||||
{
|
||||
key: 'remove',
|
||||
icon: <Delete theme="outline" size="18" strokeWidth={3} />,
|
||||
title: '删除动作',
|
||||
onClick: () => {
|
||||
submit({ id: category.id }, { method: 'DELETE' })
|
||||
},
|
||||
}
|
||||
],
|
||||
{ className: 'contextMenu' })
|
||||
}
|
||||
|
||||
const dragHandle = {
|
||||
onDragOver: (e: DragEvent)=>{
|
||||
e.preventDefault()
|
||||
e!.dataTransfer!.dropEffect= 'move'
|
||||
const el = e.currentTarget as HTMLDivElement
|
||||
el.classList.add(styles.draging)
|
||||
},
|
||||
onDragLeave: (e: DragEvent)=>{
|
||||
const el = e.currentTarget as HTMLDivElement
|
||||
el.classList.remove(styles.draging)
|
||||
},
|
||||
onDrop: (e: DragEvent)=>{
|
||||
const el = e.currentTarget as HTMLDivElement
|
||||
el.classList.remove(styles.draging)
|
||||
const id = e.dataTransfer!.getData("id")
|
||||
updateContentCategory(Number(id), category.id)
|
||||
}
|
||||
}
|
||||
return {contextMenu, dragHandle}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
.draging{
|
||||
@apply bg-slate-400 text-white;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { localServerBaseUrl } from "@renderer/config";
|
||||
|
||||
export default () => {
|
||||
|
||||
const getResponse = async (chat_messages: Array<any>) => {
|
||||
|
||||
const messages = chat_messages
|
||||
.filter((m) => ['assistant', 'user'].includes(m.role)) // 过滤出 role 为 assistant 和 user 的消息
|
||||
.map((m) => {
|
||||
return {
|
||||
role: m.role,
|
||||
content: m.content
|
||||
}
|
||||
})
|
||||
|
||||
const response = await fetch(localServerBaseUrl + "/llm", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({messages, isStream: false })
|
||||
})
|
||||
return response.json()
|
||||
|
||||
|
||||
}
|
||||
return { getResponse }
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { useNavigate } from "react-router-dom"
|
||||
|
||||
export default () => {
|
||||
const navigate = useNavigate()
|
||||
const updateContentCategory = async(id: number, category_id: number) => {
|
||||
await window.api.sql(
|
||||
`UPDATE contents set category_id=@category_id WHERE id=@id`,
|
||||
"update",
|
||||
{category_id, id}
|
||||
)
|
||||
navigate(`/config/category/contentList/${category_id}/content/${id}`)
|
||||
|
||||
}
|
||||
return {updateContentCategory}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { MutableRefObject } from "react"
|
||||
|
||||
export default() => {
|
||||
const setIgnoreMouseEvents = <T extends HTMLElement>(el: MutableRefObject<T>) => {
|
||||
el.current?.addEventListener('mouseover', ()=>{
|
||||
window.api.setIgnoreMouseEvents(false)
|
||||
})
|
||||
|
||||
document.body.addEventListener('mouseover', (e: MouseEvent)=>{
|
||||
if (e.target === document.body) {
|
||||
window.api.setIgnoreMouseEvents(true, {forward: true})
|
||||
}
|
||||
})
|
||||
}
|
||||
return {setIgnoreMouseEvents}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
|
||||
import { localServerBaseUrl } from "@renderer/config"
|
||||
export default async (systemPrompt: string, chatMessages: Array<Record<string, any>>) => {
|
||||
const messages = chatMessages.map((m) => {
|
||||
return {
|
||||
role: m.role,
|
||||
content: m.content
|
||||
}
|
||||
})
|
||||
// 添加 system 消息
|
||||
messages.unshift({
|
||||
role: 'system',
|
||||
content: systemPrompt
|
||||
});
|
||||
|
||||
const response = await fetch(localServerBaseUrl + "/llm", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({messages, isStream: false })
|
||||
})
|
||||
return response.json()
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { localServerBaseUrl } from "@renderer/config";
|
||||
import { useStore } from "@renderer/store/useStore";
|
||||
|
||||
export default()=>{
|
||||
const setChatMessages = useStore(state => state.setChatMessage)
|
||||
const chatMessages = useStore(state => state.chatMessages)
|
||||
const runCode = async function (code: string) {
|
||||
const res = await fetch(localServerBaseUrl + "/execute", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code: code
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
setChatMessages([...chatMessages,
|
||||
{
|
||||
role: "coder",
|
||||
content: "代码运行结果如下:" + JSON.stringify(data),
|
||||
createAt: new Date().getTime(),
|
||||
updateAt: new Date().getTime(),
|
||||
id: new Date().getTime().toString()
|
||||
}])
|
||||
}
|
||||
|
||||
return { runCode }
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
import { ChangeEvent } from "react"
|
||||
import { useStore } from "@renderer/store/useStore"
|
||||
export default()=>{
|
||||
const setData = useStore((state)=>state.setData)
|
||||
const setSearch = useStore((state)=>state.setSearch)
|
||||
const handleSearch = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||
let inputValue = e.target.value ? e.target.value : "#####@@@@@@@@@@!$%^&&"
|
||||
setSearch(e.target.value)
|
||||
const data = await window.api.sql(
|
||||
`select * from contents where title like @content`,
|
||||
'findAll',
|
||||
{content: `%${inputValue}%`})
|
||||
|
||||
setData(data as ContentType[])
|
||||
}
|
||||
return {handleSearch}
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import {useStore} from "@renderer/store/useStore"
|
||||
import { useCallback, useEffect } from "react"
|
||||
export default()=>{
|
||||
const data = useStore((state)=>state.data)
|
||||
const setData = useStore((state)=>state.setData)
|
||||
const search = useStore((state)=>state.search)
|
||||
const setSearch = useStore((state)=>state.setSearch)
|
||||
const selectId = useStore((state)=>state.selectId)
|
||||
const setSelectId = useStore((state)=>state.setSelectId)
|
||||
const handleKeyEvent = useCallback((e: KeyboardEvent) => {
|
||||
|
||||
switch(e.key){
|
||||
case 'ArrowUp': {
|
||||
// 初始没有数据,所以data.length为0 ,防止向上箭头,将数据变为-1
|
||||
if (data.length === 0) return
|
||||
// 找到当前id的索引
|
||||
const index = data.findIndex((item)=>item.id === selectId)
|
||||
setSelectId(data[index - 1]?.id || data[data.length - 1].id)
|
||||
break
|
||||
}
|
||||
case 'ArrowDown':{
|
||||
// 初始没有数据,所以data.length为0 ,防止向上箭头,将数据变为-1
|
||||
if (data.length === 0) return
|
||||
// 找到当前id的索引
|
||||
const index = data.findIndex((item)=>item.id === selectId)
|
||||
setSelectId(data[index + 1]?.id || data[0].id)
|
||||
break
|
||||
}
|
||||
case 'Enter': {
|
||||
select(selectId)
|
||||
break
|
||||
}
|
||||
case 'Escape': {
|
||||
window.api.closeWindow('search')
|
||||
break
|
||||
}
|
||||
}
|
||||
// 将 data 和 currentIndex 变化时,重新定义该函数,currentIndex会更新为最新值
|
||||
}, [data, selectId])
|
||||
|
||||
// 选中代码块
|
||||
async function select(id: Number){
|
||||
if (id === 0) {
|
||||
setData([])
|
||||
window.api.closeWindow('search')
|
||||
const new_id = await window.api.sql(
|
||||
`insert into contents (title, content, category_id, created_at) values ('${search}', '', 0, datetime())`,
|
||||
"create")
|
||||
window.api.openWindow('code', `/0/content/${new_id}/${search}`)
|
||||
setSearch("")
|
||||
return
|
||||
}
|
||||
setData([])
|
||||
setSearch('')
|
||||
// if (content) await navigator.clipboard.writeText(content)
|
||||
window.api.closeWindow('search')
|
||||
const category_id = data.find((item)=>item.id == id)?.category_id
|
||||
window.api.openWindow('code', `/${category_id}/content/${id}`)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', handleKeyEvent)
|
||||
// 如果 data 发生变化,先前的事件监听器会被移除,然后再添加新的监听器,以确保使用最新的 data
|
||||
// 如果不移除,data变化会不停添加监听器
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyEvent)
|
||||
}
|
||||
}, [handleKeyEvent])
|
||||
// 当输入数据变化时,将当前索引重置为0
|
||||
useEffect(() => setSelectId(data[0]?.id || 0), [data])
|
||||
return {data, selectId, select}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// import { useStore } from "@renderer/store/useStore"
|
||||
|
||||
export default() => {
|
||||
// const setError = useStore(state => state.setError)
|
||||
// const register = async ()=>{
|
||||
// const ret = (await window.api.sql('', 'config')) as Record<string, string>
|
||||
// const isBind = await window.api.shortcut(ret.shortcut)
|
||||
// isBind || setError("注册失败")
|
||||
|
||||
// }
|
||||
// return {
|
||||
// register
|
||||
// }
|
||||
return {}
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { Button } from "antd";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
function About() {
|
||||
const [version, setVersion] = useState('');
|
||||
const [updateInfo, setUpdateInfo] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
window.api.updateInfo((value) => {
|
||||
if (value === '软件更新失败,重试中...') {
|
||||
window.api.checkUpdate();
|
||||
}
|
||||
setUpdateInfo(value)
|
||||
})
|
||||
|
||||
window.api.checkUpdate();
|
||||
|
||||
window.api.getVersion().then((res) => {
|
||||
setVersion(res)
|
||||
})
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main className="flex items-center justify-center h-screen">
|
||||
<div className="flex flex-col items-center">
|
||||
<h1 className="text-2xl">
|
||||
autoMate
|
||||
</h1>
|
||||
v{version}
|
||||
<div className="text-sm mt-5 mr-5">
|
||||
{updateInfo}
|
||||
</div>
|
||||
{updateInfo === '下载完成,重启软件完成更新!' && (
|
||||
<Button type="primary" className="flex items-center" onClick={() => {
|
||||
window.api.restartApp()
|
||||
}}>重启</Button>
|
||||
|
||||
)}
|
||||
</div>
|
||||
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export default About;
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Outlet } from "react-router-dom"
|
||||
import { MantineProvider } from '@mantine/core';
|
||||
import { ContextMenuProvider } from 'mantine-contextmenu';
|
||||
// 右键菜单
|
||||
import '@mantine/core/styles.layer.css';
|
||||
import 'mantine-contextmenu/styles.layer.css';
|
||||
export default function Config() {
|
||||
return (
|
||||
<MantineProvider defaultColorScheme="light">
|
||||
<ContextMenuProvider>
|
||||
<main>
|
||||
<Outlet />
|
||||
</main>
|
||||
</ContextMenuProvider>
|
||||
</MantineProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
.container{
|
||||
@apply bg-white w-screen h-screen;
|
||||
display: grid;
|
||||
grid-template:
|
||||
' . . ' 30px
|
||||
'category content' auto
|
||||
// 第三行的高度是50像素、第一列宽度为100像素、第二列占据剩余空间
|
||||
'nav content' 50px /100px 1fr;
|
||||
|
||||
div {
|
||||
&:nth-child(1) {
|
||||
@apply border-r;
|
||||
}
|
||||
&:nth-child(1),
|
||||
&:nth-child(2) {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
&.category {
|
||||
grid-area: category;
|
||||
@apply border-r;
|
||||
}
|
||||
&.content {
|
||||
grid-area: content;
|
||||
@apply bg-yellow-100;
|
||||
}
|
||||
&.nav {
|
||||
grid-area: nav;
|
||||
@apply border-r border-t;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import Result from "@renderer/components/Result"
|
||||
import Search from "@renderer/components/Search"
|
||||
import { CodeProvider } from "@renderer/context/CodeContext"
|
||||
import Error from "@renderer/components/Error"
|
||||
import { MutableRefObject, useEffect, useRef } from "react"
|
||||
import useIgnoreMouseEvents from "@renderer/hooks/useIgnoreMouseEvents"
|
||||
import { useStore } from "@renderer/store/useStore"
|
||||
|
||||
|
||||
function Home(): JSX.Element {
|
||||
const mainRef = useRef<HTMLDivElement>(null)
|
||||
const {setIgnoreMouseEvents} = useIgnoreMouseEvents()
|
||||
window.api.initTable()
|
||||
// 注册快捷键
|
||||
window.api.shortcut()
|
||||
const setError = useStore((state)=>state.setError)
|
||||
useEffect(()=>{
|
||||
setIgnoreMouseEvents(mainRef as MutableRefObject<HTMLDivElement>)
|
||||
// //为开发方便,临时代码
|
||||
// window.api.openConfigWindow()
|
||||
|
||||
}, [])
|
||||
window.api.getConfig().then((res)=>{
|
||||
const config = JSON.parse(res.content) as ConfigDataType
|
||||
if (config.llm.api_key=="") {
|
||||
setError("没有检测到大模型配置信息,请“点击配置”进行配置。如有疑问可查看文档:https://s0soyusc93k.feishu.cn/wiki/JhhIwAUXJiBHG9kmt3YcXisWnec")
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<CodeProvider>
|
||||
<main className="relative" ref={mainRef}>
|
||||
<Error/>
|
||||
<Search />
|
||||
<Result />
|
||||
</main>
|
||||
</CodeProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import '@renderer/assets/tailwind.css'
|
||||
import '@renderer/assets/global.scss'
|
||||
import { RouterProvider } from 'react-router-dom'
|
||||
import router from './router'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
|
||||
<RouterProvider router={router} />
|
||||
|
||||
)
|
||||
@@ -1,39 +0,0 @@
|
||||
import { redirect } from "react-router-dom"
|
||||
|
||||
export default async ({ request }) => {
|
||||
const formData = await request.formData()
|
||||
const data = Object.fromEntries(formData)
|
||||
switch (request.method) {
|
||||
case "POST": {
|
||||
return await window.api.sql(
|
||||
"insert into categories (name, created_at) values('未命名', datetime())",
|
||||
"create",
|
||||
)
|
||||
}
|
||||
case "DELETE": {
|
||||
await window.api.sql(
|
||||
`delete from categories where id = @id`,
|
||||
"del",
|
||||
{
|
||||
id: data.id
|
||||
})
|
||||
|
||||
await window.api.sql(
|
||||
`update contents set category_id = 0 where category_id = @category_id`,
|
||||
"update",
|
||||
{
|
||||
category_id: data.id
|
||||
}
|
||||
)
|
||||
return redirect("/config/category/contentList")
|
||||
}
|
||||
case "PUT": {
|
||||
return await window.api.sql(
|
||||
`update categories set name = @name where id = @id`,
|
||||
"update",
|
||||
data
|
||||
)
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export default () => {
|
||||
return window.api.sql("SELECT * FROM categories order by id desc", "findAll")
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
.category-page{
|
||||
@apply w-screen h-screen;
|
||||
display: grid;
|
||||
grid-template:
|
||||
"categories content" auto
|
||||
"nav content" 30px/170px auto;
|
||||
|
||||
|
||||
.categories{
|
||||
@apply border-r bg-slate-100 text-xs text-slate-700 overflow-y-auto;
|
||||
grid-area: categories;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.nav {
|
||||
@apply border-t border-r bg-slate-100 flex justify-center items-center gap-2;
|
||||
grid-area: nav;
|
||||
}
|
||||
|
||||
.content {
|
||||
grid-area: content;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Outlet, useLoaderData } from "react-router-dom"
|
||||
import "./category.scss"
|
||||
import { CategoryItem } from "@renderer/components/CategoryItem"
|
||||
import { QuickNav } from "@renderer/components/QuickNav"
|
||||
import { FooterMenu } from "@renderer/components/FooterMenu"
|
||||
|
||||
export const Category = () => {
|
||||
const categories = useLoaderData() as CategoryType[]
|
||||
return (
|
||||
<main className="category-page">
|
||||
<div className="categories">
|
||||
<QuickNav/>
|
||||
{categories.map((category) => (
|
||||
<CategoryItem category={category} key={category.id} />
|
||||
))}
|
||||
</div>
|
||||
<FooterMenu/>
|
||||
|
||||
<div className="content">
|
||||
<Outlet></Outlet>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import { Form, useLoaderData, useRevalidator, useSubmit } from "react-router-dom"
|
||||
import "./content.scss"
|
||||
import CodeEditor from "@renderer/components/CodeEditor"
|
||||
import { Button } from "antd"
|
||||
import { useState } from "react"
|
||||
import useRunCode from "@renderer/hooks/useRunCode"
|
||||
|
||||
|
||||
export const Content = () => {
|
||||
const { content, categories, search } = useLoaderData() as {
|
||||
content: ContentType
|
||||
categories: CategoryType[]
|
||||
search: string
|
||||
}
|
||||
const revalidator = useRevalidator();
|
||||
const submit = useSubmit()
|
||||
const [open, setOpen] = useState(false);
|
||||
const { runCode } = useRunCode()
|
||||
return (
|
||||
<Form method="PUT">
|
||||
<main className="content-page" key={content.id}>
|
||||
<input name="id" type="text" defaultValue={content.id} hidden></input>
|
||||
<input autoFocus defaultValue={content.title} name="title" onChange={(e) => {
|
||||
submit(e.target.form)
|
||||
}} />
|
||||
<div className="flex justify-between">
|
||||
<select name="category_id" value={content.category_id} onChange={(e) =>
|
||||
submit(e.target.form)
|
||||
}>
|
||||
<option value="0">未分类</option>
|
||||
{categories.map((category) => (
|
||||
<option key={category.id} value={category.id}>{category.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<Button onClick={async () => {
|
||||
setOpen(true)
|
||||
const code_content = (await window.api.sql(`select * from contents where id = ${content.id}`, "findOne")) as ContentType
|
||||
const code = code_content.content
|
||||
runCode(code)
|
||||
}}>运行</Button>
|
||||
</div>
|
||||
<CodeEditor
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
id={content.id}
|
||||
defaultValue={content.content}
|
||||
revalidator={() => revalidator.revalidate()}
|
||||
search={search}
|
||||
/>
|
||||
</main>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { redirect } from "react-router-dom"
|
||||
|
||||
export default async({request}) => {
|
||||
// params 接收路由中传递过来的数据
|
||||
const formData = await request.formData()
|
||||
const data = Object.fromEntries(formData)
|
||||
await window.api.sql(
|
||||
`update contents set title=@title, category_id=@category_id where id=@id`,
|
||||
"update",
|
||||
data
|
||||
)
|
||||
return redirect(`/config/category/contentList/${data.category_id}/content/${data.id}`)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
export default async({params}) => {
|
||||
const content = await window.api.sql(`select * from contents where id = ${params.id}`, "findOne")
|
||||
const categories = await window.api.sql(`select * from categories order by id desc`, "findAll")
|
||||
return {
|
||||
content,
|
||||
categories,
|
||||
search: params.search
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
.content-page{
|
||||
@apply h-screen;
|
||||
display: grid;
|
||||
grid-template-rows: 50px 30px 1fr;
|
||||
@mixin common {
|
||||
@apply outline-none bg-slate-50;
|
||||
}
|
||||
input{
|
||||
@include common();
|
||||
@apply text-lg p-3;
|
||||
}
|
||||
|
||||
select{
|
||||
@include common();
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
// code-editor{
|
||||
// @include common();
|
||||
// @apply border-t pt-5 text-sm opacity-90 p-3;
|
||||
// }
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { redirect } from "react-router-dom"
|
||||
|
||||
export default async ({request, params}) => {
|
||||
const formData = await request.formData()
|
||||
const data = Object.fromEntries(formData)
|
||||
const cid = params.cid || 0
|
||||
switch(request.method){
|
||||
case "POST": {
|
||||
const id = await window.api.sql(
|
||||
`insert into contents (title, content, category_id, created_at) values ('未命名', '', ${cid}, datetime())`,
|
||||
"create")
|
||||
return redirect(`content/${id}`)
|
||||
}
|
||||
case "DELETE": {
|
||||
return await window.api.sql(`delete from contents where id = ${data.id}`, "del")
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
export default async({params, request}) => {
|
||||
const url = new URL(request.url)
|
||||
const searchWord = url.searchParams.get("searchWord")
|
||||
let sql = "select * from contents"
|
||||
if (searchWord) {
|
||||
sql += ` where title like @searchWord order by id desc`
|
||||
console.log("hello")
|
||||
return window.api.sql(sql, "findAll", {searchWord: `%${searchWord}%`})
|
||||
}
|
||||
if (params.cid != undefined) {
|
||||
sql += ` where category_id=${params.cid}`
|
||||
}
|
||||
sql += " order by id desc"
|
||||
return window.api.sql(sql, "findAll")
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
.contentList-page{
|
||||
@apply h-screen text-xs text-slate-700;
|
||||
display: grid;
|
||||
grid-template: "list content" auto / 220px auto;
|
||||
.list{
|
||||
@apply border-r overflow-y-auto;
|
||||
grid-area: list;
|
||||
}
|
||||
.content{
|
||||
grid-area: content;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import {Outlet, useLoaderData} from "react-router-dom"
|
||||
import "./contentList.scss"
|
||||
import { ContentSearch } from "@renderer/components/ContentSearch";
|
||||
import { ContentItem } from "@renderer/components/ContentItem";
|
||||
|
||||
|
||||
export const ContentList = () => {
|
||||
const contentList = useLoaderData() as ContentType[]
|
||||
|
||||
return (<main className="contentList-page">
|
||||
<div className="list">
|
||||
<ContentSearch/>
|
||||
{contentList.map(content => (
|
||||
<ContentItem key={content.id} content={content}/>
|
||||
))}
|
||||
</div>
|
||||
<div className="content">
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export default async ({ request }) => {
|
||||
const formData = await request.formData()
|
||||
const data = Object.fromEntries(formData)
|
||||
window.api.sql(`update config set content=@content where id = 1`,
|
||||
'update',
|
||||
{
|
||||
content: JSON.stringify(data)
|
||||
}
|
||||
)
|
||||
return {}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export default async ({}) =>{
|
||||
const config = (await window.api.sql('select * from config where id=1', 'findOne', {})) as ConfigType
|
||||
return JSON.parse(config.content) as ConfigDataType
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
import { Button, Form, Input, Select, Space, message } from 'antd';
|
||||
import styles from './styles.module.scss'
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLoaderData } from 'react-router-dom';
|
||||
import { localServerBaseUrl } from '@renderer/config';
|
||||
|
||||
export function Setting(){
|
||||
const [form] = Form.useForm();
|
||||
const config = useLoaderData() as ConfigDataType
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(config);
|
||||
}, [])
|
||||
const { Option } = Select;
|
||||
const tailLayout = {
|
||||
wrapperCol: { offset: 6, span: 16 },
|
||||
};
|
||||
|
||||
const [keys, setKeys] = useState<string[]>([])
|
||||
const onModelChange = (value: string) => {
|
||||
switch (value) {
|
||||
case 'openai':
|
||||
form.resetFields(['llm'])
|
||||
form.setFieldsValue({"llm": {model: 'gpt-4-turbo', base_url:"https://api.openai.com/v1"}});
|
||||
break;
|
||||
case 'ollama':
|
||||
form.resetFields(['llm'])
|
||||
form.setFieldsValue({"llm": {model: 'ollama/codeqwen:latest',api_base: "http://localhost:11434" }});
|
||||
break;
|
||||
default:
|
||||
}
|
||||
};
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
await window.api.sql(`update config set content=@content where id = 1`,
|
||||
'update',
|
||||
{
|
||||
content: JSON.stringify(values)
|
||||
})
|
||||
// 注册快捷键
|
||||
window.api.shortcut()
|
||||
window.close();
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
window.close()
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className={styles.settingPage}>
|
||||
<h1>Setting</h1>
|
||||
<Form
|
||||
form={form}
|
||||
name="control-hooks"
|
||||
onFinish={onFinish}
|
||||
>
|
||||
<section>
|
||||
<h5>快捷键定义</h5>
|
||||
<Form.Item name="shortcut" label="呼出快捷键" rules={[{ required: true }]}>
|
||||
<Input
|
||||
type="text"
|
||||
readOnly
|
||||
onKeyDown={(e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.altKey) {
|
||||
const code = e.code.replace(/Left|Right|Key|Digit/, '')
|
||||
if (keys.includes(code)) return
|
||||
keys.push(code)
|
||||
setKeys(keys)
|
||||
// 如果以数字或字母或者空格结尾
|
||||
if (code.match(/^(\w|\s)$/gi)) {
|
||||
e.currentTarget.value = keys.join('+')
|
||||
form.setFieldsValue({shortcut: e.currentTarget.value})
|
||||
setKeys([])
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</section>
|
||||
<section>
|
||||
|
||||
<div className='flex flex-row justify-between items-center mb-3'>
|
||||
<h5>大模型配置信息</h5>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const hide = message.loading('检测中...', 0);
|
||||
const res = await fetch(`${localServerBaseUrl}/llm`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(
|
||||
{ "messages": [{ "role": "user", "content": "hello" }],
|
||||
"llm_config": JSON.stringify(form.getFieldValue("llm"))}),
|
||||
}
|
||||
)
|
||||
hide();
|
||||
const jsonResponse = await res.json()
|
||||
if (jsonResponse.status === 0) {
|
||||
message.success(`连接成功!`)
|
||||
} else {
|
||||
message.error(
|
||||
<span style={{ whiteSpace: 'pre-line' }}>
|
||||
{`连接失败!\n${jsonResponse.content}`}
|
||||
</span>)
|
||||
}
|
||||
|
||||
}}>检查连接</Button>
|
||||
</div>
|
||||
<Form.Item name="format" label="格式" rules={[{ required: true }]}>
|
||||
<Select
|
||||
placeholder="选择格式,一般OpenAI格式为万能格式"
|
||||
onChange={onModelChange}
|
||||
allowClear
|
||||
>
|
||||
<Option value="openai">OpenAI</Option>
|
||||
<Option value="ollama">Ollama</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name={["llm", "model"]} label="model" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
noStyle
|
||||
shouldUpdate={(prevValues, currentValues) => prevValues.format !== currentValues.format}
|
||||
>
|
||||
{({ getFieldValue }) =>
|
||||
getFieldValue('format') === 'openai' ? (
|
||||
<div>
|
||||
<Form.Item name={["llm", "api_key"]} label="api_key" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name={["llm", "base_url"]} label="base_url" rules={[{ required: false }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
noStyle
|
||||
shouldUpdate={(prevValues, currentValues) => prevValues.format !== currentValues.format}
|
||||
>
|
||||
{({ getFieldValue }) =>
|
||||
getFieldValue('format') === 'ollama' ? (
|
||||
<Form.Item name={["llm", "api_base"]} label="api_base" rules={[{ required: false }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : null
|
||||
}
|
||||
</Form.Item>
|
||||
</section>
|
||||
<div className='mt-10 flex justify-center items-center'>
|
||||
<Form.Item {...tailLayout} >
|
||||
<Space>
|
||||
<Button htmlType="button" onClick={onClose} className='mr-5'>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" htmlType="submit" >
|
||||
保存
|
||||
</Button>
|
||||
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,107 +0,0 @@
|
||||
import { Form, useLoaderData, useSubmit } from 'react-router-dom'
|
||||
import styles from './styles.module.scss'
|
||||
import { useState } from 'react'
|
||||
import { Button, Select, message } from 'antd'
|
||||
import { localServerBaseUrl } from '@renderer/config'
|
||||
|
||||
export const Setting = () => {
|
||||
const [keys, setKeys] = useState<string[]>([])
|
||||
const config = useLoaderData() as ConfigDataType
|
||||
const submit = useSubmit()
|
||||
return (
|
||||
<Form method="POST">
|
||||
<main className={styles.settingPage}>
|
||||
<h1>Setting</h1>
|
||||
<section>
|
||||
<h5>快捷键定义</h5>
|
||||
<input
|
||||
type="text"
|
||||
name="shortcut"
|
||||
defaultValue={config.shortcut}
|
||||
readOnly
|
||||
onKeyDown={(e) => {
|
||||
if (e.metaKey || e.ctrlKey || e.altKey) {
|
||||
const code = e.code.replace(/Left|Right|Key|Digit/, '')
|
||||
if (keys.includes(code)) return
|
||||
keys.push(code)
|
||||
setKeys(keys)
|
||||
// 如果以数字或字母或者空格结尾
|
||||
if (code.match(/^(\w|\s)$/gi)) {
|
||||
e.currentTarget.value = keys.join('+')
|
||||
setKeys([])
|
||||
config.shortcut = e.currentTarget.value
|
||||
// 注册快捷键
|
||||
window.api.shortcut()
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<div className='flex flex-row justify-between items-center mb-3'>
|
||||
<h5>大模型配置信息</h5>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const hide = message.loading('检测中...', 0);
|
||||
try {
|
||||
const res = await fetch(`${localServerBaseUrl}/llm`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ "messages": [{ "role": "user", "content": "hello" }] }),
|
||||
}
|
||||
)
|
||||
hide();
|
||||
if (res.ok) {
|
||||
message.success('连接成功')
|
||||
} else {
|
||||
message.error(`连接失败,请检查配置信息是否正确:\n${res.statusText}`)
|
||||
}
|
||||
} catch (error) {
|
||||
hide();
|
||||
message.error(`连接失败,请检查配置信息是否正确:\n${error}`)
|
||||
}
|
||||
}}>检查连接</Button>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="llm"
|
||||
defaultValue={JSON.stringify(config.llm)}
|
||||
onChange={(e) => {
|
||||
config.llm = JSON.parse(e.currentTarget.value)
|
||||
}}
|
||||
/>
|
||||
{/* <Select
|
||||
value={""}
|
||||
style={{ width: 80, margin: '0 8px' }}
|
||||
onChange={(e) => {
|
||||
config.llm.model = e.Symbol
|
||||
}}
|
||||
>
|
||||
<Option value="rmb">RMB</Option>
|
||||
<Option value="dollar">Dollar</Option>
|
||||
</Select> */}
|
||||
</section>
|
||||
<div className='flex justify-center items-center'>
|
||||
<Button className='mr-8' onClick={() => {
|
||||
window.close();
|
||||
}}>取消</Button>
|
||||
<Button type="primary" onClick={
|
||||
async () => {
|
||||
await window.api.sql(`update config set content=@content where id = 1`,
|
||||
'update',
|
||||
{
|
||||
content: JSON.stringify(config)
|
||||
})
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
>保存</Button>
|
||||
</div>
|
||||
</main>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
.settingPage{
|
||||
@apply bg-slate-100 h-screen p-5;
|
||||
h1 {
|
||||
@apply flex justify-center text-sm;
|
||||
}
|
||||
|
||||
section{
|
||||
h5 {
|
||||
@apply text-sm font-bold mb-8 text-slate-700 opacity-80;
|
||||
}
|
||||
h6 {
|
||||
@apply text-sm mb-2 text-slate-700 opacity-80;
|
||||
}
|
||||
@apply border p-3 rounded-md bg-white my-5;
|
||||
input {
|
||||
@apply border w-full outline-none;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user