跨平台剪贴板同步、历史记录管理工具 / Cross-platform cipboard syncing, history management tool
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
SyncClipboard is a cross-platform clipboard synchronization tool (Windows/macOS/Linux) with clipboard history management. It supports a standalone ASP.NET Core server, client-built-in server, WebDAV, or S3-compatible storage as the sync backend. Desktop clients use either Avalonia (cross-platform) or WinUI3 (Windows-native) for UI.
## Build & Test Commands
This is a multi-platform .NET solution. The sln cannot be built as a whole (it contains platform-specific projects that fail on incompatible OS/arch). All projects use [central package management](https://learn.microsoft.com/en-us/nuget/consume-packages/Central-Package-Management) via `src/Directory.Packages.props`. Commands below are run from the repo root unless noted.
### Windows (WinUI3)
WinUI3 is the primary Windows client (`net10.0-windows10.0.19041.0`). Requires the Windows App SDK — only builds on Windows. The CI uses msbuild, not dotnet CLI.
```bash
# Restore
dotnet restore src/SyncClipboard.WinUI3
# Build (Release, x64 — CI command)
msbuild src\SyncClipboard.WinUI3\SyncClipboard.WinUI3.csproj \
/p:Platform=x64 /p:RuntimeIdentifier=win-x64 /p:Configuration=Release \
/p:WindowsAppSDKSelfContained=true /p:SelfContained=true /v:m -restore
```
For `arm64`, swap `/p:Platform=arm64 /p:RuntimeIdentifier=win-arm64`. Set `/p:SelfContained=false` to skip bundling the .NET runtime; set `/p:WindowsAppSDKSelfContained=false` to skip bundling the Windows App SDK.
### Linux (Avalonia)
Linux uses `SyncClipboard.Desktop.Default` (Avalonia, `net10.0`).
```bash
dotnet build src/SyncClipboard.Desktop.Default/SyncClipboard.Desktop.Default.csproj -f net10.0
```
### macOS (Avalonia)
macOS uses `SyncClipboard.Desktop.MacOS` (Avalonia, `net10.0-macos`). Requires the `macos` workload and only builds on macOS. Run the commands below from the repository root.
```bash
# Restore
dotnet restore src/SyncClipboard.Desktop.MacOS
# Publish (same project and configuration as CI)
dotnet publish src/SyncClipboard.Desktop.MacOS/SyncClipboard.Desktop.MacOS.csproj \
-r osx-x64 -c Release
```
Replace `osx-x64` with `osx-arm64` for Apple Silicon.
### Server
The standalone server is cross-platform `net10.0`.
```bash
# Restore
dotnet restore src/SyncClipboard.Server
# Build (Debug)
dotnet build src/SyncClipboard.Server
# Publish (CI command)
dotnet publish src/SyncClipboard.Server/SyncClipboard.Server.csproj \
--configuration Release --no-restore
```
### Shared / Core Libraries
These are platform-agnostic and build anywhere:
```bash
dotnet build src/SyncClipboard.Shared
dotnet build src/SyncClipboard.Core
dotnet build src/SyncClipboard.Server.Core
dotnet build src/SyncClipboard.Desktop
```
### Tests
```bash
# Run all tests
dotnet test src/SyncClipboard.Test
dotnet test src/SyncClipboard.Test.Desktop
dotnet test src/SyncClipboard.Test.WinUI3 # Windows only
# Run a single test
dotnet test src/SyncClipboard.Test --filter "FullyQualifiedName~TestClassName"
```
Tests use MSTest with Moq. `ServiceProviderDataSource` attributes drive DI validation tests that ensure all registered services can be resolved.
#### Test organization
- Mark tests that require real OS behavior with `[TestCategory("PlatformWindows")]`, `[TestCategory("PlatformMacOS")]`, or `[TestCategory("PlatformLinux")]`. Use multiple categories when appropriate; leave platform-independent tests unmarked. Platform-specific tests must also check the current OS and call `Assert.Inconclusive` on unsupported platforms before performing platform-specific setup or operations; categories alone do not skip tests when running the full suite locally.
### Code Style
```bash
# Check formatting (from src/ directory)
dotnet format --verify-no-changes --severity info --no-restore
```
Rules are defined in `src/.editorconfig`.
#### XAML / AXAML
Format changed `.xaml` and `.axaml` files with **XamlStyler.Console**, using `src/Settings.XamlStyler`. Restrict formatting to files changed by the current task or PR; do not reformat unrelated files. `dotnet format` does not replace this check.
Use the globally installed `xstyler`. If it is not available, install it with the command below; no specific version is required. Run the formatting command from the repository root, replacing the example file list with the actual changed files:
```bash
dotnet tool install --global XamlStyler.Console
xstyler -f "path/to/ChangedView.xaml,path/to/ChangedView.axaml" -c src/Settings.XamlStyler -i
```
`-f` accepts a comma-separated file list; `-i` allows `.axaml` files. Repeat the formatting command with `-p` to verify without writing files. Review the diff to ensure only formatting changed.
## Architecture
### Project Dependency Graph
```
SyncClipboard.Shared — Profile models, IProfileEnv, utilities (no heavy deps)
↑
SyncClipboard.Server.Core — ASP.NET Core library: controllers, SignalR hub, EF Core SQLite history, basic auth
↑ ↑
SyncClipboard.Core ───────────┘ — Core business logic: services, remote server adapters, ViewModels, DI setup (AppCore)
↑
SyncClipboard.Desktop — Shared Avalonia desktop UI: clipboard factory, views, tray icon, hotkeys
↑
├── SyncClipboard.Desktop.Default — Windows/Linux desktop executable (Avalonia, net10.0)
├── SyncClipboard.Desktop.MacOS — macOS desktop executable (Avalonia, net10.0-macos)
└── SyncClipboard.WinUI3 — WinUI3 native Windows executable (net10.0-windows10.0.19041.0)
SyncClipboard.Server — Standalone server executable (wraps SyncClipboard.Server.Core)
```
### Core Concepts
**Profile Model (`SyncClipboard.Shared`):** Abstract `Profile` is the unified clipboard content representation. Subtypes: `TextProfile`, `ImageProfile`, `FileProfile`, `GroupProfile`. Every profile has a lazily-computed hash used for deduplication. Profiles can be serialized to `ProfileDto` for wire transfer and `ProfilePersistentInfo`/`ProfileLocalInfo` for storage.
**Service Pattern (`SyncClipboard.Core`):** Every runnable feature implements `IService` (abstract base: `Service`). `ServiceManager` orchestrates all services through their lifecycle: `Start()` → `RegistEventHandler()` → `Load()` (on config change) → `Stop()`. Register new services in `AppCore.ConfigurateUserService()`.
**Clipboard Pipeline:** `UploadService` and `DownloadService` coordinate via named messenger events (`PUSH_START`, `PUSH_STOP`, `PULL_START`, `PULL_STOP`). They use a `remoteProfilemutex` semaphore and `LocalClipboard.Semaphore` to prevent races. `UploadService` extends `ClipboardHander` (which extends `Service`), adding clipboard monitoring and content-control validation.
**Server Adapters (`SyncClipboard.Core/RemoteServer/`):** `RemoteClipboardServerFactory` creates the appropriate `IServerAdapter` based on the active account config. Three adapters exist: `WebDavAdapter` (HTTP/WebDAV), `OfficialAdapter` (SignalR-based real-time sync), and `S3Adapter` (S3-compatible object storage). Register adapters with the `AddServerAdapter<TConfig, TAdapter>()` extension.
**Server Side (`SyncClipboard.Server.Core`):** `SyncClipboardController` handles the WebDAV-compatible API (`GET/PUT SyncClipboard.json` and `file/{name}`) plus dedicated endpoints. `SyncClipboardHub` (SignalR) pushes real-time profile changes to connected clients. History is stored in SQLite via EF Core (`HistoryDbContext`), managed by `HistoryService`. Authentication uses a custom `BasicAuthenticationHandler` with either `FileCredentialChecker` (reads from appsettings.json) or `StaticCredentialChecker` (env vars).
### DI Wiring
`AppCore.ConfigCommonService()` in `SyncClipboard.Core` registers the full service graph: logging, configuration, clipboards, adapters, managers, and infrastructure. Platform-specific registrations happen in `AppServices.ConfigDesktopCommonService()` in `SyncClipboard.Desktop`, which calls `ConfigCommonService()` then adds platform-specific clipboard readers, hotkey registries, and window implementations. On Linux, additional clipboard readers (`XClipReader`, `WlClipboardReader`) and position providers are registered.
### Platform Abstraction
Compile-time constants (`WINDOWS`, `MACOS`, `LINUX`) are defined via `RuntimeIdentifier` checks in `SyncClipboard.Core.csproj`. Platform-specific code uses `#if` guards or `OperatingSystem.Is*()` runtime checks. Desktop clipboard implementations live in `SyncClipboard.Desktop/ClipboardAva/`, with Linux-specific readers in `ClipboardAva/ClipboardReader/`.
### Configuration
`ConfigManager` handles JSON-based configuration files. User-level config types (e.g., `SyncConfig`, `ServerConfig`, `HistoryConfig`) are records in `Models/UserConfigs/`. `AccountManager` manages the list of server connections, each with a type-specific config (`WebDavConfig`, `OfficialConfig`, `S3Config`).
### i18n
Strings are in `SyncClipboard.Core/I18n/Strings.resx` (auto-generated `Strings.Designer.cs`). Supports Chinese and English. Language is set via `I18nHelper.SetProgramLanguage()` from the `ProgramConfig.Language` setting.
## Git Workflow
- Never push to remote `master`, including normal and force pushes.
- Treat “提交到 master” (submit to master) as merging commits into the local `master` branch only. It does not authorize a remote push.
## Documentation
- AI-generated or AI-maintained design documents must be stored in `docs/ai_design/`.
- Keep `AGENTS.md` and `CLAUDE.md` synchronized. Any applicable instruction change in either file must be reflected in the other.
## Code Conventions
- Target framework: all product and test projects use `net10.0`, with `net10.0-macos` for the macOS project and `net10.0-windows10.0.19041.0` for WinUI3.
- Nullable reference types enabled project-wide (`<Nullable>enable</Nullable>`).
- Central package management: add/update versions only in `Directory.Packages.props`.
- The project uses MSTest with Moq for mocking. Test data source attributes (`PlatformServiceProviderDataSource`, `SystemServiceProviderDataSource`) drive DI validation tests.
- Commit messages follow conventional format with Chinese description prefixes (e.g., `fix:`, `feat:`).
- `src/.editorconfig` defines C# code style rules.
- Keep embedded control-statement bodies on their own line (IDE2001), including `if`, `for`, `foreach`, and `while`. Use `dotnet format style --diagnostics IDE2001` to migrate violations.
- Use roughly 120 columns as a readability guideline for C#, not a hard limit. Prefer keeping short method calls and conditions on one line; wrap noticeably long or complex expressions at logical boundaries. Do not split cohesive expressions solely to meet 120 columns or mechanically put each argument on its own line. Reassess layout after edits and collapse unnecessary line breaks when expressions become shorter. Avoid unrelated formatting-only changes.
- Before adding a file-level `using`, check the owning project's `global using` declarations (such as `GlobalUsings.cs` and `Usings.cs`) and SDK-generated implicit usings. Do not duplicate namespaces already imported globally; remove redundant usings in files touched by the task without expanding into unrelated cleanup. Global usings apply only within their project, do not propagate through project references, and do not implicitly import child namespaces.
## 测试原则
- 新增测试应验证明确的功能要求、重要失败路径或已确认缺陷,不应仅证明本次修改生效。
- 用户要求删除或简化机制时,同步清理相关测试,不自动增加“旧机制不存在”的测试或把原断言取反。
- “不保证”不等于“保证相反”:例如不保留旧权限,不代表新旧权限必须不同。
- 优先验证实际行为,避免只检查内部标志、旧参数名称等实现细节。
- 测试混有有效验证和废弃断言时,仅删除无效部分,保留必要的功能测试。