add package and docs
This commit is contained in:
parent
c5850d67fd
commit
c0b593958d
@ -1,8 +1,69 @@
|
||||
# 说明
|
||||
Prompt
|
||||
我的目标是:写一个Python程序,可以通过选择任何windows应用的一段富文本,点击鼠标右键后弹出一个选项,点击这个选项可以把富文本保存在配置路径的.markdown文件里,这个markdown会保持选中富文本的图文等相对样式不变可以正常显示。请给出完整的实现步骤和代码,应用.
|
||||
|
||||
以管理员权限打开命令提示符,进入脚本所在目录,运行:
|
||||
python app.py --install
|
||||
# 应用步骤
|
||||
1、激活venv的python环境.venv\Scripts\activate
|
||||
2、配置config.ini文件,配置保存路径
|
||||
3、启动程序,python main.py
|
||||
4、选择需要保存的富文本,右键双击
|
||||
|
||||
如果要卸载右键菜单,运行:
|
||||
python app.py --uninstall
|
||||
# 本地可执行文件打包
|
||||
|
||||
# 创建虚拟环境
|
||||
python -m venv .venv
|
||||
|
||||
# 激活虚拟环境
|
||||
# 对于 Windows:
|
||||
source .venv/Scripts/activate
|
||||
# 对于 Unix 或 MacOS:
|
||||
source .venv/bin/activate
|
||||
|
||||
1. 项目目录结构:
|
||||
llmclipboard/
|
||||
├── llmclipboard/
|
||||
│ ├── __init__.py
|
||||
│ ├── app.py
|
||||
├── config.ini
|
||||
├── README.md
|
||||
├── pyproject.toml
|
||||
|
||||
2. pyproject.toml 文件:
|
||||
[project]
|
||||
name = "llmclipboard"
|
||||
version = "0.1.0"
|
||||
description = "A text capture tool for saving formatted text from clipboard to markdown files."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"pynput",
|
||||
"pywin32",
|
||||
"html2text",
|
||||
"keyboard",
|
||||
"configparser"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
llmclipboard = "llmclipboard.app:main"
|
||||
|
||||
3. app.py 文件(确保有 main 函数)
|
||||
|
||||
4. 安装项目:
|
||||
# 在编辑模式下安装项目
|
||||
uv pip install -e .
|
||||
|
||||
5. 测试入口点:
|
||||
llmclipboard
|
||||
|
||||
# 分发构建
|
||||
1. 构建分发包
|
||||
uv pip install build
|
||||
python -m build
|
||||
这将在项目根目录下生成一个 dist 目录,里面包含 .tar.gz 和 .whl 文件,这些就是你的分发包。
|
||||
|
||||
2. 其他python环境安装
|
||||
pip install dist\llmclipboard-0.1.0.tar.gz
|
||||
pip install dist\llmclipboard-0.1.0-py3-none-any.whl
|
||||
|
||||
3. 程序启动
|
||||
llmclipboard
|
||||
87
project/llmclipboard/docs/captured_text_20241213_145218.md
Normal file
87
project/llmclipboard/docs/captured_text_20241213_145218.md
Normal file
@ -0,0 +1,87 @@
|
||||
为了实现应用程序的快速启动,可以考虑以下几种方案:
|
||||
|
||||
|
||||
|
||||
### 1. 使用 PyInstaller 创建独立的可执行文件 (.exe)
|
||||
|
||||
- **描述**:PyInstaller 可以将你的应用程序打包成一个独立的可执行文件。这个方法可以让应用程序在没有预先安装 Python 的机器上运行。
|
||||
|
||||
- **优点**:对终端用户来说非常方便,无需额外的设置和依赖安装。
|
||||
|
||||
- **缺点**:生成的可执行文件可能比较大。
|
||||
|
||||
|
||||
|
||||
### 2. 使用 `setuptools` 进行打包
|
||||
|
||||
- **描述**:使用 `setuptools` 定义你的包,并在 `setup.py` 或 `pyproject.toml` 文件中创建控制台脚本入口点。这个方法允许用户通过 `pip` 安装应用程序,并使其可用作命令行工具。
|
||||
|
||||
- **优点**:用户可以通过 `pip` 安装和升级此包。
|
||||
|
||||
- **缺点**:需要用户的机器上预先安装 Python 和 `pip`。
|
||||
|
||||
|
||||
|
||||
### 3. 使用 Docker 将应用程序容器化
|
||||
|
||||
- **描述**:将应用程序打包到一个 Docker 容器中,这个容器包含所有的依赖和运行时环境。
|
||||
|
||||
- **优点**:在不同环境和机器间保证一致性。
|
||||
|
||||
- **缺点**:需要用户的机器上安装 Docker。
|
||||
|
||||
|
||||
|
||||
### 4. 创建一个 Zipapp
|
||||
|
||||
- **描述**:Python 的 `zipapp` 模块允许你将整个应用程序打包成一个 zip 文件,且可以通过 Python 执行。
|
||||
|
||||
- **优点**:分发简单,zip 文件包含所有依赖。
|
||||
|
||||
- **缺点**:需要用户安装正确版本的 Python。
|
||||
|
||||
|
||||
|
||||
### 5. 使用 `shiv` 创建 PEX 文件
|
||||
|
||||
- **描述**:`shiv` 是一个用于构建完全自包含的 Python zipapps(PEP 441)的工具,实际应用中是单文件 Python 应用程序。
|
||||
|
||||
- **优点**:单文件,包含所有依赖,可以直接用 Python 执行。
|
||||
|
||||
- **缺点**:需要用户安装正确版本的 Python。
|
||||
|
||||
|
||||
|
||||
### 6. 平台特定安装程序
|
||||
|
||||
- **描述**:使用例如 Windows 的 WiX Toolset 等工具创建平台特定的安装程序(例如 Windows MSI,macOS DMG)。
|
||||
|
||||
- **优点**:用户友好的安装体验。
|
||||
|
||||
- **缺点**:需要为每个平台创建和维护不同的安装程序。
|
||||
|
||||
|
||||
|
||||
### 7. 使用 `pkg_resources` 进行运行时依赖管理
|
||||
|
||||
- **描述**:如果你更倾向于在运行时安装依赖,可以使用 `pkg_resources` 或类似工具来管理应用程序内部的依赖。然而由于复杂性和潜在的运行时错误,这种方法一般不推荐。
|
||||
|
||||
- **优点**:对用户来说设置最小化。
|
||||
|
||||
- **缺点**:高复杂性和潜在的运行时问题。
|
||||
|
||||
|
||||
|
||||
### 总结:
|
||||
|
||||
- **为了便于分发和用户友好性**,使用 PyInstaller 创建独立的可执行文件是一个常见选择。
|
||||
|
||||
- **对于更 Pythonic 的方法**,利用 `setuptools` 创建一个控制台脚本入口点,并通过 `pip` 安装会更好。
|
||||
|
||||
- **为了环境一致性和高级部署能力**,使用 Docker 是非常有益的。
|
||||
|
||||
- **在分发简便性和依赖管理之间取得平衡**,使用 `shiv` 创建的 zipapp 提供了一个好的折中方案。
|
||||
|
||||
|
||||
|
||||
每种方法都有其使用场景和优点,具体选择取决于你的应用程序和用户的需求和限制。
|
||||
50
project/llmclipboard/docs/captured_text_20241213_145554.md
Normal file
50
project/llmclipboard/docs/captured_text_20241213_145554.md
Normal file
@ -0,0 +1,50 @@
|
||||
使用方案2,即使用 `setuptools` 对应用进行打包,并创建一个控制台脚本入口点,具体步骤如下:
|
||||
|
||||
### 具体步骤:
|
||||
|
||||
1. **准备项目目录结构**:
|
||||
- 确保项目目录结构规范,包含必要的文件,如 `app.py`、`__init__.py`、`setup.py` 或 `pyproject.toml`、`README.md` 等。
|
||||
- 添加文件 `__init__.py` 以使目录被识别为 Python 包。
|
||||
|
||||
2. **创建 `setup.py` 或更新 `pyproject.toml`**:
|
||||
- 如果使用 `setup.py`,需要定义项目的元数据和依赖项,同时指定入口点。
|
||||
- 如果使用 `pyproject.toml`,同样需要定义元数据和依赖项,并在 `[project.scripts]` 中声明入口点。
|
||||
|
||||
3. **配置入口点**:
|
||||
- 在 `setup.py` 或 `pyproject.toml` 中设置控制台脚本入口点,使用户可以通过命令行启动应用程序。
|
||||
|
||||
4. **更新应用主模块**:
|
||||
- 确保在应用的主模块(如 `app.py`)中有一个 `main` 函数,作为入口点函数。
|
||||
|
||||
5. **创建并激活虚拟环境**:
|
||||
- 使用 `venv` 模块创建并激活一个虚拟环境以进行隔离和管理依赖。
|
||||
|
||||
6. **安装项目**:
|
||||
- 通过 `pip install -e .` 命令在编辑模式下安装项目。这会根据 `setup.py` 或 `pyproject.toml` 中的配置,建立项目到虚拟环境的符号链接,并自动添加控制台脚本。
|
||||
|
||||
7. **测试入口点**:
|
||||
- 安装完成后,确保在命令行中可以使用项目名称启动应用,并验证功能是否正常。
|
||||
|
||||
8. **编写 `README.md` 文件**:
|
||||
- 添加使用说明和安装步骤,以便用户能够快速上手使用该应用。
|
||||
|
||||
以上步骤中所用到的文件和目录可能包括:
|
||||
|
||||
```
|
||||
llmclipboard/
|
||||
├── llmclipboard/
|
||||
│ ├── __init__.py
|
||||
│ ├── app.py
|
||||
├── config.ini
|
||||
├── README.md
|
||||
├── pyproject.toml (或 setup.py)
|
||||
```
|
||||
|
||||
### 详细步骤实现:
|
||||
- 编写 `setup.py` 或 `pyproject.toml` 文件包含项目元数据。
|
||||
- 在 `pyproject.toml` 中使用 `[project.scripts]` 声明控制台入口点,或者在 `setup.py` 中使用 `entry_points` 参数进行配置。
|
||||
-确保 `app.py` 中具有 `main` 函数,该函数作为入口点函数。
|
||||
- 创建虚拟环境并激活,安装项目。
|
||||
- 测试入口点调用以确保其正常工作。
|
||||
|
||||
通过这些步骤,你的应用程序可以打包成一个可通过命令行快速启动的工具,用户只需安装一次,即可通过配置的命令启动应用程序。
|
||||
200
project/llmclipboard/docs/captured_text_20241213_150437.md
Normal file
200
project/llmclipboard/docs/captured_text_20241213_150437.md
Normal file
@ -0,0 +1,200 @@
|
||||
在打包后的命令中,配置文件 `config.ini` 可以有几种处理方式。我们处理配置文件的方法包括:
|
||||
|
||||
1. **将 config.ini 文件作为项目的一部分进行分发**:
|
||||
确保配置文件在项目结构中的正确位置。
|
||||
|
||||
2. **在代码中指定配置文件的路径**:
|
||||
通过代码确保应用程序能够找到并加载配置文件。
|
||||
|
||||
3. **提供默认配置文件**:
|
||||
在缺失时创建配置文件,确保应用程序有初始配置。
|
||||
|
||||
### 详细步骤:
|
||||
|
||||
1. **项目目录结构**:
|
||||
确保 `config.ini` 文件和应用程序的入口文件在同级目录中,如下所示:
|
||||
```
|
||||
llmclipboard/
|
||||
├── llmclipboard/
|
||||
│ ├── __init__.py
|
||||
│ ├── app.py
|
||||
├── config.ini
|
||||
├── README.md
|
||||
├── pyproject.toml
|
||||
```
|
||||
|
||||
2. **在 `app.py` 中加载配置文件**:
|
||||
修改 `load_config` 方法,确保程序能找到 `config.ini` 文件,通常可以使用相对路径或环境变量。
|
||||
|
||||
### 示例代码实现:
|
||||
|
||||
**项目目录结构**:
|
||||
```
|
||||
llmclipboard/
|
||||
├── llmclipboard/
|
||||
│ ├── __init__.py
|
||||
│ ├── app.py
|
||||
├── config.ini
|
||||
├── README.md
|
||||
├── pyproject.toml
|
||||
```
|
||||
|
||||
**在 `app.py` 中加载配置文件**:
|
||||
```python project/llmclipboard/app.py
|
||||
import os
|
||||
import time
|
||||
import configparser
|
||||
from pynput import mouse
|
||||
import win32clipboard
|
||||
import win32con
|
||||
from html2text import html2text
|
||||
import keyboard
|
||||
import threading
|
||||
import sys
|
||||
import logging
|
||||
|
||||
class TextCaptureApp:
|
||||
def __init__(self):
|
||||
self.load_config()
|
||||
self.running = True
|
||||
self.last_right_click_time = 0
|
||||
self.setup_logging()
|
||||
|
||||
def load_config(self):
|
||||
"""Loads the configuration file."""
|
||||
config = configparser.ConfigParser()
|
||||
config_path = os.path.join(os.path.dirname(__file__), '..', 'config.ini')
|
||||
|
||||
if not os.path.isfile(config_path):
|
||||
self.logger.error(f"配置文件未找到: {config_path}")
|
||||
sys.exit(1)
|
||||
|
||||
config.read(config_path)
|
||||
self.double_click_threshold = config.getfloat('Settings', 'double_click_threshold', fallback=0.3)
|
||||
self.save_location = config.get('Settings', 'save_location', fallback=os.path.join(os.path.expanduser('~'), 'Desktop'))
|
||||
self.copy_delay = config.getfloat('Settings', 'copy_delay', fallback=0.1)
|
||||
|
||||
def setup_logging(self):
|
||||
logging.basicConfig(
|
||||
filename='text_capture.log',
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def save_to_markdown(self, content):
|
||||
try:
|
||||
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
||||
file_path = os.path.join(self.save_location, f'captured_text_{timestamp}.md')
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
self.logger.info(f"Content saved to: {file_path}")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error saving file: {e}")
|
||||
|
||||
def get_clipboard_content(self):
|
||||
try:
|
||||
win32clipboard.OpenClipboard()
|
||||
|
||||
try:
|
||||
content = win32clipboard.GetClipboardData(win32con.CF_HTML)
|
||||
self.logger.info("Clipboard HTML content captured")
|
||||
return html2text(content)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
content = win32clipboard.GetClipboardData(win32con.CF_TEXT)
|
||||
self.logger.info("Clipboard TEXT content captured")
|
||||
return content.decode('gbk')
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
content = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
|
||||
self.logger.info("Clipboard UNICODE TEXT content captured")
|
||||
return content
|
||||
except:
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error getting clipboard content: {e}")
|
||||
return None
|
||||
finally:
|
||||
try:
|
||||
win32clipboard.CloseClipboard()
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error closing clipboard: {e}")
|
||||
|
||||
def simulate_copy(self):
|
||||
keyboard.press('ctrl')
|
||||
keyboard.press('c')
|
||||
keyboard.release('c')
|
||||
keyboard.release('ctrl')
|
||||
time.sleep(self.copy_delay) # 等待复制操作完成
|
||||
|
||||
def on_click(self, x, y, button, pressed):
|
||||
if not pressed:
|
||||
return
|
||||
|
||||
if button == mouse.Button.right:
|
||||
current_time = time.time()
|
||||
if (current_time - self.last_right_click_time) < self.double_click_threshold:
|
||||
self.simulate_copy()
|
||||
content = self.get_clipboard_content()
|
||||
if content:
|
||||
self.save_to_markdown(content)
|
||||
else:
|
||||
self.logger.warning("No content captured from clipboard")
|
||||
self.last_right_click_time = current_time
|
||||
|
||||
def on_keyboard_event(self, event):
|
||||
# 监听Esc键退出程序
|
||||
if event.name == 'esc':
|
||||
self.running = False
|
||||
return False
|
||||
|
||||
def run(self):
|
||||
self.logger.info("Text Capture App started")
|
||||
print("文本捕获程序已启动...")
|
||||
print("使用说明:")
|
||||
print("1. 选中要保存的文本")
|
||||
print("2. 快速双击鼠标右键来保存选中的文本")
|
||||
print("3. 按ESC键退出程序")
|
||||
|
||||
mouse_listener = mouse.Listener(on_click=self.on_click)
|
||||
mouse_listener.start()
|
||||
|
||||
# 启动键盘监听
|
||||
keyboard.on_press(self.on_keyboard_event)
|
||||
|
||||
# Wait until the running flag becomes False
|
||||
while self.running:
|
||||
time.sleep(0.1)
|
||||
|
||||
print("\n程序已退出")
|
||||
mouse_listener.stop()
|
||||
mouse_listener.join()
|
||||
keyboard_listener.stop()
|
||||
keyboard_listener.join()
|
||||
self.logger.info("Text Capture App stopped")
|
||||
|
||||
def main():
|
||||
try:
|
||||
app = TextCaptureApp()
|
||||
app.run()
|
||||
except Exception as e:
|
||||
logging.error(f"程序发生错误: {e}")
|
||||
input("按Enter键退出...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
### 其他注意事项:
|
||||
- **配置文件路径**:在 `load_config` 方法中使用 `os.path` 来获取配置文件的路径,并确保路径的正确性。
|
||||
- **默认配置**:如果配置文件丢失,可以选择在初始化时生成一个默认的配置文件,确保应用程序能够正常启动。
|
||||
- **文档说明**:在 `README.md` 文件中添加关于如何找到和编辑 `config.ini` 文件的说明,以帮助用户自定义他们的配置。
|
||||
|
||||
通过以上步骤, `config.ini` 文件将在包安装过程中被正确识别,并能在应用程序运行时被读取和加载。
|
||||
119
project/llmclipboard/docs/captured_text_20241213_151754.md
Normal file
119
project/llmclipboard/docs/captured_text_20241213_151754.md
Normal file
@ -0,0 +1,119 @@
|
||||
要将打包后的应用程序在其他 Python 环境中安装,可以执行以下步骤:
|
||||
|
||||
### 1. 创建安装包并上传到 PyPI(可选)
|
||||
|
||||
如果你不想上传到 PyPI,可以直接跳过此步骤,直接创建安装包并手动分发。
|
||||
|
||||
### 2. 构建分发包
|
||||
|
||||
你可以使用 `build` 工具来创建一个源码分发包(`sdist`)和一个 Wheel 分发包(`bdist_wheel`)。
|
||||
|
||||
首先,确保你已经安装了 `build` 工具:
|
||||
```sh
|
||||
pip install build
|
||||
```
|
||||
|
||||
然后,在项目根目录下运行以下命令来构建分发包:
|
||||
```sh
|
||||
python -m build
|
||||
```
|
||||
|
||||
这将在项目根目录下生成一个 `dist` 目录,里面包含 `.tar.gz` 和 `.whl` 文件,这些就是你的分发包。
|
||||
|
||||
### 3. 在目标环境中安装分发包
|
||||
|
||||
你可以将分发包(`.tar.gz` 或 `.whl` 文件)复制到目标环境机器上,然后使用 `pip` 安装:
|
||||
|
||||
```sh
|
||||
pip install path/to/your-package.whl
|
||||
```
|
||||
|
||||
或者:
|
||||
```sh
|
||||
pip install path/to/your-package.tar.gz
|
||||
```
|
||||
|
||||
### 详细步骤示例
|
||||
|
||||
假设你的项目目录结构如下:
|
||||
```
|
||||
llmclipboard/
|
||||
├── llmclipboard/
|
||||
│ ├── __init__.py
|
||||
│ ├── app.py
|
||||
├── config.ini
|
||||
├── README.md
|
||||
├── pyproject.toml
|
||||
```
|
||||
|
||||
### 项目打包
|
||||
|
||||
确保 `pyproject.toml` 文件如下配置:
|
||||
```toml project/llmclipboard/pyproject.toml
|
||||
[project]
|
||||
name = "llmclipboard"
|
||||
version = "0.1.0"
|
||||
description = "A text capture tool for saving formatted text from clipboard to markdown files."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"pynput",
|
||||
"pywin32",
|
||||
"html2text",
|
||||
"keyboard",
|
||||
"configparser",
|
||||
"logging"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
llmclipboard = "llmclipboard.app:main"
|
||||
```
|
||||
|
||||
### 构建分发包
|
||||
|
||||
在项目根目录下运行以下命令:
|
||||
```sh
|
||||
python -m build
|
||||
```
|
||||
|
||||
这将在项目根目录下生成一个 `dist` 目录,里面包含构建好的分发包, 如:
|
||||
|
||||
```
|
||||
dist/
|
||||
│── llmclipboard-0.1.0.tar.gz
|
||||
│── llmclipboard-0.1.0-py3-none-any.whl
|
||||
```
|
||||
|
||||
### 在目标环境中安装
|
||||
|
||||
你可以将这些分发包文件复制到目标环境的机器上,然后运行以下命令进行安装:
|
||||
|
||||
```sh
|
||||
pip install llmclipboard-0.1.0-py3-none-any.whl
|
||||
```
|
||||
|
||||
或者安装源码分发包:
|
||||
|
||||
```sh
|
||||
pip install llmclipboard-0.1.0.tar.gz
|
||||
```
|
||||
|
||||
### 使用打包后的应用
|
||||
|
||||
安装完成后,在目标环境的命令行中运行以下命令启动应用程序:
|
||||
|
||||
```sh
|
||||
llmclipboard
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **配置文件路径**:在目标环境中,配置文件 `config.ini` 应该与应用程序一起分发,可以通过如下方式确保应用知道配置文件的位置:
|
||||
- 可以在程序启动时通过环境变量或命令行参数指定配置文件路径。
|
||||
- 修改代码,让应用在启动时在相对路径或用户目录下寻找配置文件。
|
||||
|
||||
2. **依赖安装**:确保目标环境已经安装所有依赖包。部署时可以为目标环境创建一个新的虚拟环境,然后在其中安装应用分发包。
|
||||
|
||||
3. **适配不同平台**:考虑到 Python 环境可能跨多个操作系统,确保在不同平台上进行足够的测试,尤其是涉及到特定平台的库(如 `pywin32`)。
|
||||
|
||||
通过以上步骤,你可以在其他 Python 环境中安装和运行打包后的应用程序。
|
||||
0
project/llmclipboard/llmclipboard/__init__.py
Normal file
0
project/llmclipboard/llmclipboard/__init__.py
Normal file
@ -129,11 +129,14 @@ class TextCaptureApp:
|
||||
keyboard_listener.stop()
|
||||
keyboard_listener.join()
|
||||
self.logger.info("Text Capture App stopped")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
def main():
|
||||
try:
|
||||
app = TextCaptureApp()
|
||||
app.run()
|
||||
except Exception as e:
|
||||
logging.error(f"程序发生错误: {e}")
|
||||
input("按Enter键退出...")
|
||||
input("按Enter键退出...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -1,7 +1,16 @@
|
||||
[project]
|
||||
name = "llmclipboard"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
description = "A text capture tool for saving formatted text from clipboard to markdown files."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = []
|
||||
dependencies = [
|
||||
"pynput",
|
||||
"pywin32",
|
||||
"html2text",
|
||||
"keyboard",
|
||||
"configparser"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
llmclipboard = "llmclipboard.app:main"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user