feat: CutThenThink v3.0 初始版本

完整实现 Tauri + Vanilla JS 轻量级截图工具

Phase 1 - 项目搭建
- Tauri 2.x 项目初始化
- Vite 前端项目搭建
- 基础 UI 框架(CSS 变量、组件库)
- 构建配置优化

Phase 2 - 核心截图功能
- 全屏/区域/窗口截图
- 截图预览和管理
- 文件命名和缩略图
- 全局快捷键集成

Phase 3 - 上传与存储
- 多图床上传(GitHub/Imgur/自定义)
- 配置管理系统
- SQLite 数据库

Phase 4 - OCR 集成
- 云端 OCR(百度/腾讯云)
- 插件管理系统
- 本地 OCR 插件(Go)
- OCR 结果处理

Phase 5 - AI 分类系统
- Claude/OpenAI API 集成
- Prompt 模板引擎
- 模板管理界面
- 自动分类流程

Phase 6 - 历史记录与管理
- 图库视图(网格/列表)
- 搜索与筛选
- 批量操作
- 导出功能(JSON/CSV/ZIP)

Phase 7 - 打包与发布
- 多平台构建配置
- CI/CD 工作流
- 图标和资源
- 安装包配置

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-02-12 18:58:40 +08:00
commit e2ea309ee6
142 changed files with 38818 additions and 0 deletions

43
src-ocr-plugin/Makefile Normal file
View File

@@ -0,0 +1,43 @@
.PHONY: all clean build-windows build-linux build-mac
# 版本信息
VERSION := 1.0.0
BINARY_NAME := ocr-plugin
# 所有目标
all: build-linux build-windows build-mac
# 构建当前平台
build:
go build -o $(BINARY_NAME) main.go
# 构建 Linux 版本
build-linux:
GOOS=linux GOARCH=amd64 go build -o $(BINARY_NAME)-linux-amd64 main.go
GOOS=linux GOARCH=arm64 go build -o $(BINARY_NAME)-linux-arm64 main.go
# 构建 Windows 版本
build-windows:
GOOS=windows GOARCH=amd64 go build -o $(BINARY_NAME)-windows-amd64.exe main.go
# 构建 macOS 版本
build-mac:
GOOS=darwin GOARCH=amd64 go build -o $(BINARY_NAME)-darwin-amd64 main.go
GOOS=darwin GOARCH=arm64 go build -o $(BINARY_NAME)-darwin-arm64 main.go
# 清理构建文件
clean:
rm -f $(BINARY_NAME)*
# 运行
run:
go run main.go recognize -image ../test-images/sample.png
# 测试
test:
go test -v ./...
# 下载依赖
deps:
go mod download
go mod tidy

109
src-ocr-plugin/README.md Normal file
View File

@@ -0,0 +1,109 @@
# CutThenThink OCR Plugin
本地 OCR 插件,基于 Tesseract OCR 引擎实现。
## 功能特性
- 支持多语言识别(中文、英文、日文、韩文等)
- 返回文本内容和边界框信息
- 提供置信度评分
- 跨平台支持Windows、macOS、Linux
## 系统依赖
### Linux (Ubuntu/Debian)
```bash
sudo apt-get install tesseract-ocr
sudo apt-get install tesseract-ocr-chi-sim # 简体中文
sudo apt-get install tesseract-ocr-chi-tra # 繁体中文
sudo apt-get install libtesseract-dev
```
### macOS
```bash
brew install tesseract
brew install tesseract-lang # 包含中文语言包
```
### Windows
1. 下载安装 Tesseract from [UB Mannheim](https://github.com/UB-Mannheim/tesseract/wiki)
2. 将 Tesseract 安装目录添加到 PATH 环境变量
3. 安装中文语言包
## 编译
```bash
# 下载依赖
go mod download
# 编译当前平台
go build -o ocr-plugin main.go
# 交叉编译
GOOS=windows GOARCH=amd64 go build -o ocr-plugin.exe main.go
GOOS=darwin GOARCH=amd64 go build -o ocr-plugin-mac main.go
GOOS=linux GOARCH=amd64 go build -o ocr-plugin-linux main.go
```
## 使用
### 查看版本
```bash
./ocr-plugin version
```
### 识别文本
```bash
./ocr-plugin recognize -image /path/to/image.png -lang eng+chi_sim
```
### 参数说明
- `-image`: 图片文件路径(必需)
- `-lang`: OCR 语言(默认: eng+chi_sim
#### 支持的语言代码
- `eng` - English
- `chi_sim` - 简体中文
- `chi_tra` - 繁体中文
- `jpn` - Japanese
- `kor` - Korean
## 输出格式
### 成功响应
```json
{
"success": true,
"engine": "tesseract",
"language": "eng+chi_sim",
"blocks": [
{
"text": "识别的文本",
"confidence": 95.5,
"bbox_x": 100,
"bbox_y": 200,
"bbox_width": 150,
"bbox_height": 30,
"block_type": "text"
}
]
}
```
### 错误响应
```json
{
"success": false,
"error": "错误信息"
}
```
## 性能优化
1. **图片预处理**: 在识别前对图片进行降噪、二值化处理可提高准确率
2. **语言选择**: 只加载需要的语言包可以提高速度
3. **图片尺寸**: 过大的图片会降低识别速度,建议缩放到合理尺寸
## 许可证
MIT License

8
src-ocr-plugin/go.mod Normal file
View File

@@ -0,0 +1,8 @@
module github.com/cutthenthink/ocr-plugin
go 1.21
require (
github.com/disintegration/imaging v1.6.2
github.com/otiai10/gosseract v1.4.0
)

185
src-ocr-plugin/main.go Normal file
View File

@@ -0,0 +1,185 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"github.com/otiai10/gosseract/v2"
)
const (
Version = "1.0.0"
)
func main() {
// 命令行参数
command := flag.String("command", "", "Command to execute (recognize, version)")
imagePath := flag.String("image", "", "Path to image file")
language := flag.String("lang", "eng+chi_sim", "OCR language (default: eng+chi_sim)")
flag.Parse()
if *command == "" || len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
switch *command {
case "version":
printVersion()
case "recognize":
if *imagePath == "" {
fmt.Fprintln(os.Stderr, "Error: image path is required for recognize command")
os.Exit(1)
}
recognize(*imagePath, *language)
default:
fmt.Fprintf(os.Stderr, "Unknown command: %s\n", *command)
printUsage()
os.Exit(1)
}
}
func printUsage() {
fmt.Println("CutThenThink OCR Plugin v" + Version)
fmt.Println("\nUsage:")
fmt.Println(" ocr-plugin version - Print version information")
fmt.Println(" ocr-plugin recognize -image <path> - Recognize text from image")
fmt.Println("\nOptions:")
fmt.Println(" -lang <language> - OCR language (default: eng+chi_sim)")
fmt.Println("\nSupported languages:")
fmt.Println(" eng - English")
fmt.Println(" chi_sim - Simplified Chinese")
fmt.Println(" chi_tra - Traditional Chinese")
fmt.Println(" jpn - Japanese")
fmt.Println(" kor - Korean")
fmt.Println(" (combine with + for multiple languages)")
}
func printVersion() {
fmt.Println(Version)
}
// OCRBlock represents a single text block with bounding box
type OCRBlock struct {
Text string `json:"text"`
Confidence float32 `json:"confidence"`
BBoxX uint32 `json:"bbox_x"`
BBoxY uint32 `json:"bbox_y"`
BBoxWidth uint32 `json:"bbox_width"`
BBoxHeight uint32 `json:"bbox_height"`
BlockType string `json:"block_type"`
}
// OCRResponse represents the JSON response from the plugin
type OCRResponse struct {
Success bool `json:"success"`
Error *string `json:"error,omitempty"`
Engine string `json:"engine,omitempty"`
Language string `json:"language,omitempty"`
Blocks []OCRBlock `json:"blocks"`
}
func recognize(imagePath, language string) {
// Check if file exists
if _, err := os.Stat(imagePath); os.IsNotExist(err) {
response := OCRResponse{
Success: false,
Error: stringPtr("Image file not found: " + imagePath),
}
printJSON(response)
os.Exit(1)
}
// Create Tesseract client
client := gosseract.NewClient()
defer client.Close()
// Set language
client.SetLanguage(language)
// Set image
client.SetImage(imagePath)
// Get text
text, err := client.Text()
if err != nil {
response := OCRResponse{
Success: false,
Error: stringPtr("OCR failed: " + err.Error()),
}
printJSON(response)
os.Exit(1)
}
// Get detailed text boxes
boxes, err := client.GetBoundingBoxes(gosseract.RIL_TEXT_LINE)
if err != nil {
log.Printf("Warning: Failed to get bounding boxes: %v", err)
// Continue without bounding boxes
}
// Convert to our format
var blocks []OCRBlock
for i, box := range boxes {
if box.Box != nil {
blocks = append(blocks, OCRBlock{
Text: box.Word,
Confidence: float32(box.Confidence),
BBoxX: uint32(box.Box.Left),
BBoxY: uint32(box.Box.Top),
BBoxWidth: uint32(box.Box.Width),
BBoxHeight: uint32(box.Box.Height),
BlockType: "text",
})
} else {
// Fallback for lines without bounding boxes
blocks = append(blocks, OCRBlock{
Text: box.Word,
Confidence: float32(box.Confidence),
BBoxX: 0,
BBoxY: uint32(i * 20),
BBoxWidth: 100,
BBoxHeight: 20,
BlockType: "text",
})
}
}
// If no boxes found, create a single block with the full text
if len(blocks) == 0 && text != "" {
blocks = append(blocks, OCRBlock{
Text: text,
Confidence: 80.0, // Default confidence
BBoxX: 0,
BBoxY: 0,
BBoxWidth: 100,
BBoxHeight: 100,
BlockType: "text",
})
}
response := OCRResponse{
Success: true,
Engine: "tesseract",
Language: language,
Blocks: blocks,
}
printJSON(response)
}
func printJSON(v interface{}) {
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
if err := encoder.Encode(v); err != nil {
log.Fatalf("Failed to encode JSON: %v", err)
}
}
func stringPtr(s string) *string {
return &s
}