Add comprehensive AGENTS.md with build/test commands and code style guidelines
This commit is contained in:
373
AGENTS.md
Normal file
373
AGENTS.md
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
# MoMentry Playground - AGENTS.md
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
MoMentry Playground 是一個整合式桌面應用程式,提供:
|
||||||
|
- 多格式媒體播放(視頻、音頻、圖片)
|
||||||
|
- Markdown/PDF/HTML 檢視
|
||||||
|
- Frame-精確的視頻控制
|
||||||
|
- 與 momentry_core 處理模組的進度監控
|
||||||
|
- Database 查詢功能
|
||||||
|
|
||||||
|
## 技術棧
|
||||||
|
|
||||||
|
| 元件 | 技術 |
|
||||||
|
|------|------|
|
||||||
|
| 語言 | Rust 2021 |
|
||||||
|
| 桌面框架 | tao + wry |
|
||||||
|
| 視頻播放 | SDL2 + FFmpeg-sidecar |
|
||||||
|
| 前端 | HTML/CSS/JS (WebView) |
|
||||||
|
| 後端接口 | HTTP API (momentry_core port 3002) |
|
||||||
|
| 數據庫 | PostgreSQL, MongoDB, Qdrant (via API) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 構建/測試/代碼質量命令
|
||||||
|
|
||||||
|
### 構建
|
||||||
|
```bash
|
||||||
|
# Debug 構建
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
# Release 構建
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
# 運行
|
||||||
|
cargo run
|
||||||
|
|
||||||
|
# 指定功能運行
|
||||||
|
cargo run --features "youtube,cloud"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 測試
|
||||||
|
```bash
|
||||||
|
# 運行所有測試
|
||||||
|
cargo test
|
||||||
|
|
||||||
|
# 運行單個測試
|
||||||
|
cargo test test_name
|
||||||
|
|
||||||
|
# 運行並顯示輸出
|
||||||
|
cargo test -- --nocapture
|
||||||
|
|
||||||
|
# 運行 doc tests
|
||||||
|
cargo test --doc
|
||||||
|
```
|
||||||
|
|
||||||
|
### 代碼質量
|
||||||
|
```bash
|
||||||
|
# 格式化代碼
|
||||||
|
cargo fmt
|
||||||
|
|
||||||
|
# 檢查格式化
|
||||||
|
cargo fmt -- --check
|
||||||
|
|
||||||
|
# Lint + 自動修復
|
||||||
|
cargo clippy --fix --allow-dirty --allow-staged
|
||||||
|
|
||||||
|
# Lint 檢查
|
||||||
|
cargo clippy
|
||||||
|
|
||||||
|
# 類型檢查
|
||||||
|
cargo check
|
||||||
|
|
||||||
|
# 完整檢查(相當於所有以上)
|
||||||
|
cargo build --all-targets
|
||||||
|
```
|
||||||
|
|
||||||
|
### WASM 目標(如使用 Yew)
|
||||||
|
```bash
|
||||||
|
# 添加 WASM 目標
|
||||||
|
rustup target add wasm32-unknown-unknown
|
||||||
|
|
||||||
|
# 使用 Trunk 開發
|
||||||
|
trunk serve
|
||||||
|
|
||||||
|
# Production 構建
|
||||||
|
trunk build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 代碼風格指南
|
||||||
|
|
||||||
|
### 格式規範
|
||||||
|
- **最大行長**: 100 字符
|
||||||
|
- **縮進**: 4 空格(使用 `rustfmt` 自動處理)
|
||||||
|
- **換行**: Unix 風格 (`\n`)
|
||||||
|
|
||||||
|
### 命名慣例
|
||||||
|
```rust
|
||||||
|
// 類型/結構體/枚舉: PascalCase
|
||||||
|
struct VideoPlayer;
|
||||||
|
enum PlaybackState;
|
||||||
|
trait MediaViewer;
|
||||||
|
|
||||||
|
// 函數/方法/變量: snake_case
|
||||||
|
fn play_video();
|
||||||
|
let current_frame = 0;
|
||||||
|
let is_playing = true;
|
||||||
|
|
||||||
|
// 常量: SCREAMING_SNAKE_CASE
|
||||||
|
const MAX_FRAME_BUFFER: usize = 10;
|
||||||
|
const SEEK_DELAY_MS: u64 = 500;
|
||||||
|
|
||||||
|
// 模塊: snake_case
|
||||||
|
mod video_player;
|
||||||
|
pub mod ffmpeg;
|
||||||
|
|
||||||
|
// 公開 API: 完整單詞
|
||||||
|
fn get_playback_position() -> Duration;
|
||||||
|
fn set_volume() -> Result<(), PlayerError>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 錯誤處理
|
||||||
|
```rust
|
||||||
|
// 使用 anyhow 進行應用級錯誤處理
|
||||||
|
use anyhow::{Context, Result, anyhow};
|
||||||
|
|
||||||
|
fn load_video(path: &str) -> Result<VideoFile> {
|
||||||
|
let file = File::open(path)
|
||||||
|
.with_context(|| format!("Failed to open video: {}", path))?;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定義專用錯誤枚舉
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum PlayerError {
|
||||||
|
#[error("Codec not supported: {0}")]
|
||||||
|
UnsupportedCodec(String),
|
||||||
|
|
||||||
|
#[error("Seek failed: {0}")]
|
||||||
|
SeekFailed(String),
|
||||||
|
|
||||||
|
#[error("FFmpeg error: {stderr}")]
|
||||||
|
FFmpegError { stderr: String },
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模塊結構
|
||||||
|
```rust
|
||||||
|
// src/lib.rs - 庫入口
|
||||||
|
pub mod player;
|
||||||
|
pub mod ui;
|
||||||
|
pub mod viewer;
|
||||||
|
pub mod api;
|
||||||
|
|
||||||
|
// 每個模塊的組織
|
||||||
|
mod player {
|
||||||
|
pub mod video; // 公開子模塊
|
||||||
|
mod controls; // 私有實現
|
||||||
|
|
||||||
|
pub use video::{VideoPlayer, VideoEvent};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 並發安全
|
||||||
|
```rust
|
||||||
|
// 使用 Arc<Mutex<T>> 共享狀態
|
||||||
|
let player = Arc::new(Mutex::new(VideoPlayer::new()));
|
||||||
|
|
||||||
|
// 在多線程間共享 Frame buffer
|
||||||
|
type FrameBuffer = Arc<Mutex<VecDeque<Frame>>>;
|
||||||
|
|
||||||
|
// Spawn 線程時克隆 Arc
|
||||||
|
let player_clone = Arc::clone(&player);
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文檔注釋
|
||||||
|
```rust
|
||||||
|
/// 視頻播放器核心結構
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// let player = VideoPlayer::new();
|
||||||
|
/// player.open("video.mp4")?;
|
||||||
|
/// player.play()?;
|
||||||
|
/// ```
|
||||||
|
pub struct VideoPlayer {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 播放事件
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum PlayEvent {
|
||||||
|
/// 播放開始
|
||||||
|
Play,
|
||||||
|
/// 播放暫停
|
||||||
|
Pause,
|
||||||
|
/// 進度更新 (當前幀, 總幀數)
|
||||||
|
Progress(u64, u64),
|
||||||
|
/// 播放結束
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 依賴管理
|
||||||
|
```toml
|
||||||
|
# 明確版本範圍
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
|
# 避免使用 * 導入
|
||||||
|
# 錯誤: use std::collections::*;
|
||||||
|
# 正確: use std::collections::{HashMap, HashSet};
|
||||||
|
```
|
||||||
|
|
||||||
|
### API 設計
|
||||||
|
```rust
|
||||||
|
// 使用 Result 明確錯誤
|
||||||
|
fn command(&self, cmd: &str) -> Result<String>;
|
||||||
|
|
||||||
|
// 公開方法要有文檔
|
||||||
|
/// 發送播放命令
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `cmd` - 命令字串 ("play", "pause", "seek <time>")
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// 返回無效命令或播放錯誤
|
||||||
|
pub fn command(&self, cmd: &str) -> Result<String> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WebView 前端規範
|
||||||
|
|
||||||
|
### HTML 結構
|
||||||
|
```html
|
||||||
|
<div id="app">
|
||||||
|
<div id="toolbar"><!-- 控制工具欄 --></div>
|
||||||
|
<div id="input-area"><!-- 命令輸入 --></div>
|
||||||
|
<div id="media-viewport"><!-- 媒體顯示 --></div>
|
||||||
|
<div id="controls"><!-- 播放控制 --></div>
|
||||||
|
<div id="status-bar"><!-- 狀態信息 --></div>
|
||||||
|
<div id="output-log"><!-- 日誌輸出 --></div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### JS 命令接口
|
||||||
|
```javascript
|
||||||
|
// 發送命令到 Rust 後端
|
||||||
|
function sendCommand(cmd) {
|
||||||
|
window.momentry_command(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接收後端事件
|
||||||
|
window.momentry_on_event = function(event) {
|
||||||
|
console.log('Event:', event);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSS 樣式
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--bg-primary: #1e1e1e;
|
||||||
|
--bg-secondary: #2d2d2d;
|
||||||
|
--text-primary: #ffffff;
|
||||||
|
--accent: #007acc;
|
||||||
|
--error: #f44747;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body { font-family: system-ui, sans-serif; }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 外部依賴
|
||||||
|
|
||||||
|
### FFmpeg (必需)
|
||||||
|
```bash
|
||||||
|
# macOS
|
||||||
|
brew install ffmpeg
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
sudo apt install ffmpeg
|
||||||
|
|
||||||
|
# 驗證
|
||||||
|
ffmpeg -version
|
||||||
|
ffprobe -version
|
||||||
|
```
|
||||||
|
|
||||||
|
### yt-dlp (YouTube 支援)
|
||||||
|
```bash
|
||||||
|
brew install yt-dlp # macOS
|
||||||
|
pip install yt-dlp # pip
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 環境變量
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# API 服務地址
|
||||||
|
export MOMENTRY_API_URL=http://localhost:3002
|
||||||
|
|
||||||
|
# 數據庫連接
|
||||||
|
export DATABASE_URL=postgresql://user:pass@localhost:5432/momentry
|
||||||
|
export QDRANT_URL=http://localhost:6333
|
||||||
|
|
||||||
|
# 輸出目錄
|
||||||
|
export MOMENTRY_OUTPUT_DIR=./output
|
||||||
|
|
||||||
|
# 日誌級別
|
||||||
|
export RUST_LOG=debug
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 開發工作流
|
||||||
|
|
||||||
|
### 1. 創建功能分支
|
||||||
|
```bash
|
||||||
|
git checkout -b feature/video-controls
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 開發與測試
|
||||||
|
```bash
|
||||||
|
cargo test feature_name
|
||||||
|
cargo clippy --fix
|
||||||
|
cargo fmt
|
||||||
|
cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 提交(使用Conventional Commits)
|
||||||
|
```bash
|
||||||
|
git commit -m "feat(player): add frame-precise seeking"
|
||||||
|
git commit -m "fix(controls): correct volume sync"
|
||||||
|
git commit -m "docs: update AGENTS.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 推送與 PR
|
||||||
|
```bash
|
||||||
|
git push -u origin feature/video-controls
|
||||||
|
# 在 Gitea 創建 PR → main
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常見問題
|
||||||
|
|
||||||
|
### Q: FFmpeg 路徑找不到
|
||||||
|
```rust
|
||||||
|
// 在代碼中指定路徑
|
||||||
|
let ffmpeg_path = std::path::Path::new("/opt/homebrew/bin/ffmpeg");
|
||||||
|
VideoDecoder::new(ffmpeg_path)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: WebView 不顯示
|
||||||
|
確保 `index.html` 在正確的資源路徑,或使用 `include_bytes!` 嵌入。
|
||||||
|
|
||||||
|
### Q: 編譯緩慢
|
||||||
|
```bash
|
||||||
|
# 使用 sccache
|
||||||
|
cargo install sccache
|
||||||
|
export RUSTC_WRAPPER=sccache
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user