# AGENTS.md

## 项目概述

SnapGo 是一个常驻菜单栏的轻量截图工具：全局快捷键唤起选区截图，确认后自动上传到 S3 兼容对象存储（或通过 SSH/SCP 保存到远端），并把可分享链接复制到剪贴板。目前主要打磨 macOS 体验。

技术栈：**Wails v2**（Go 后端 + WebView 前端）+ **Vue 3 / TypeScript / Vite** 前端。Go 后端遵循分层架构（domain → application → infrastructure），macOS 上截图选区使用原生 AppKit overlay，其它平台回退到 Wails 透明窗口 overlay。

### 分层架构（`internal/`）

- `domain/`：核心业务类型与接口，**不依赖任何第三方 SDK 或 OS API**（仅标准库）。如 `OSSProvider` 接口、`AppConfig`/`S3Config`/`SSHConfig`、`Screenshot`/`UploadResult`。
- `application/`：用例编排（capture → upload/save → clipboard → notify）。**只依赖 domain 接口**，具体适配器从 `main.go`/`app.go` 注入，便于单测。如 `CaptureAndUploadService`、`CaptureActionsService`、`CaptureAndSSHService`、`ApplyAnnotations`。
- `infrastructure/`：对接外部世界的适配器实现 —— `oss/`(S3)、`ssh/`(内置 Go client + Kerberos 委托系统 ssh)、`clipboard/`、`config/`(JSON 文件存储)、`screencapture/`、`display/`、`cursor/`、`hotkey/`、`tray/`(菜单栏)、`logging/`。

### 顶层文件（`main` 包，根目录）

- `main.go`：Wails 启动入口，embed `frontend/dist`，装配 tray 与 `App`。
- `app.go`：`App` 结构体，通过 Wails `Bind` 暴露给前端的方法（RPC），编排截图/上传/保存流程与 overlay 窗口生命周期。
- `*_darwin.go` / `*_other.go`：平台分离的 overlay、dock 图标、激活策略、帧刷新等实现。`native_overlay_darwin.go` 使用 cgo + AppKit。

### 关键约定

- 前端通过 Wails 自动生成的 `frontend/wailsjs/go/main/App.js` 调用后端 `App` 的导出方法；后端通过 `wruntime.EventsEmit` 向前端推事件（如 `upload:success`、`upload:failure`、`hotkey:ready`、`capture:overlay`）。
- 新增 OSS provider 只需在 `infrastructure/oss/` 加适配器并实现 `domain.OSSProvider`，无需改 application 层。
- S3 与 SSH 走独立 pipeline（`runUploadPipeline` vs `runSaveRemotePipeline`），避免 provider 分支泄漏进彼此。

## 构建与命令

依赖前置：Go 1.23+、Node/npm、`wails` CLI（`go install github.com/wailsapp/wails/v2/cmd/wails@latest`）。

- `go mod tidy`：同步 Go 依赖。
- `wails dev`：开发模式（热重载前端 + 后端），日志可直接在终端看到。
- `wails build -platform darwin/arm64 -clean`：构建 `.app`（产物在 `build/bin/`）。
- `./scripts/create-dev-cert.sh`：首次创建本地自签开发证书（保证 macOS TCC 权限不被反复重置）。
- `./scripts/dev-build.sh`：本地迭代用 —— 构建 + 稳定证书重签名 + 同步到 `/Applications` + 打开。优先用它而非裸 `wails build`。
- `./scripts/release.sh`：一键 build → sign → DMG → notarize（notarize 需 `NOTARIZE=1` 与凭证，默认跳过）。`ARCH=universal` 可出 fat binary。
- 前端单独命令（`frontend/`）：`npm run dev` / `npm run build`（`vue-tsc --noEmit && vite build`）/ `npm run preview`。

## 代码风格

- **Go**：标准 `gofmt`（tab 缩进）。包级 doc 注释说明“设计动机/rationale”是本仓库的强约定，注释解释“为什么”而非“是什么”。导出标识符必须有注释。错误用 `fmt.Errorf("...: %w", err)` 包装。日志统一用 `log/slog`（结构化 kv，如 `slog.Info("...", "host", h)`）。
- **平台分离**：用 `_darwin.go` / `_other.go` 文件名后缀或 `//go:build` 标签隔离平台代码，保持跨平台编译通过。
- **TypeScript/Vue**：Vue 3 `<script setup>` + Composition API；2 空格缩进。视图在 `frontend/src/views/`，组件在 `components/`。
- 注释中可使用中文（部分 infra 包如 logging 已用中文 rationale），与现有文件保持一致即可。
- 不要手改 `frontend/wailsjs/` 下文件，它们由 Wails 生成。

## 测试

- 框架：Go 标准 `testing` 包（无第三方断言库）。运行：`go test ./...`。
- 现有测试集中在 `internal/application/`（如 `capture_actions_test.go`、`annotation_test.go`）。
- 约定：application 层依赖 domain 接口，测试用手写 fake 实现（如 `fakeClipboard`、`fakeNotifier`）注入，不触碰真实 OS/网络。文件 IO 测试使用 `t.TempDir()`。
- infrastructure 适配器（S3/SSH/screencapture 等）依赖真实环境，通常不写单测；SSH 连通性可借助 `cmd/sshdiag/` 诊断工具与设置页的 “Test connection” 按钮验证。

## 安全

- **凭证落盘**：配置（含 S3 access key、SSH 密码）以 JSON 存于 `os.UserConfigDir()/SnapGo/config.json`，文件权限 `0600`、目录 `0700`。尚未接入 OS keychain（后续计划），改动配置存储时务必保持严格权限并避免把密钥写入日志。
- **日志**：写入 `~/Library/Logs/SnapGo/snapgo.log`（大小滚动，5MiB×2 备份）。记录连接信息时只记 host/user/port 等非敏感字段，**切勿记录密码/密钥**（参考 `app.go` 中的 `slog` 用法）。
- **SSH**：`StrictHostKey=false` 时使用 `InsecureIgnoreHostKey()`（牺牲安全换首启可用性，仅限个人 LAN）；Kerberos 模式委托系统 `/usr/bin/ssh` 复用 `kinit` 凭证缓存。`PathPrefix` 相对远端 `$HOME`，会剥离前导 `/` 和 `~` 防止逃逸。
- **上传兜底**：上传失败时把 PNG 落地到 `~/Pictures/SnapGo` 并复制本地路径，保证不丢截图。
- macOS 需 Screen Recording / Accessibility（TCC）权限；稳定的代码签名身份与安装路径是权限保持的关键（见 `dev-build.sh` 注释）。

## 配置

- Wails 项目配置：`wails.json`（产物名、frontend install/build/dev 命令、应用元信息）。
- 运行时配置：首启写入默认值（`domain.DefaultAppConfig()`：快捷键 `cmd+shift+a`、S3 `pathPrefix=snapgo/`、SSH port 22 / timeout 10s）。原子写入（temp + rename）。
- 环境变量：`SNAPGO_LOG_LEVEL=debug|info|warn|error`（默认 debug）。Release 相关：`ARCH`、`NOTARIZE`、`SIGN_MODE`、`DEVELOPER_ID_APPLICATION`、`KEYCHAIN_PROFILE`、`APPLE_ID`/`APPLE_TEAM_ID`/`APPLE_APP_SPECIFIC_PASSWORD`。
- S3 兼容性：`UsePathStyle` 默认 true（适配 MinIO/R2）；`PublicURLBase` 可选用于 CDN/自定义域名，否则回退 `{Endpoint}/{Bucket}/{Key}`。
