From f3d75ebce86de39099d1fd3dd2adf7e02878814b Mon Sep 17 00:00:00 2001 From: Momentry Studio Date: Thu, 1 Oct 2026 00:42:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Library=20=E6=89=B9=E9=87=8F=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=20+=20PeopleView=20=E4=BF=AE=E5=A4=8D=20+=20=E5=9B=BD?= =?UTF-8?q?=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Library: - 添加 checkbox 和鼠标框选功能 - Unregistered 卡片添加快速注册按钮 - 状态标签移至左下方,人脸图标移至左侧中间 - Deep Scan 改为 unregister -> register -> process - 完整国际化(日语、繁体中文、英语) PeopleView: - 修复卡片不显示问题(使用 get_file_traces API) - 移除对 identity_id 字段的依赖 其他: - 添加日语语言包 - 添加 useDragSelect composable - 新增文档:Face Bbox Null Bug Report --- AGENTS.md | 48 +- src-tauri/Cargo.lock | 46 +- src-tauri/Cargo.toml | 3 + src-tauri/src/bin/proxy.rs | 34 + src-tauri/src/db.rs | 80 + src-tauri/src/main.rs | 117 +- src-tauri/src/proxy.rs | 492 +++++- src-tauri/src/validate.rs | 447 ++++++ src/App.vue | 123 +- src/api/index.ts | 189 ++- src/api/types.ts | 33 + src/assets/momentry-base.css | 12 +- src/assets/momentry-full.css | 9 +- src/assets/wordpress-exact.css | 13 +- src/components/BuildVersion.vue | 54 +- src/components/FaceFilterPanel.vue | 164 +- src/components/VideoPlayer.vue | 640 ++++++-- src/composables/useDragSelect.ts | 149 ++ src/composables/useFaceFilter.ts | 115 +- src/composables/useUndoRedo.ts | 6 +- src/locales/en.json | 193 ++- src/locales/index.ts | 4 +- src/locales/ja.json | 448 ++++++ src/locales/zh-TW.json | 205 ++- src/store.ts | 242 ++- src/stores/autoScan.ts | 28 + src/stores/themeStore.ts | 32 + src/utils/asrStatus.ts | 8 +- src/utils/faceLandmarkScoring.ts | 122 ++ src/views/AdminView.vue | 966 +++++++++++- src/views/ClientView.vue | 72 +- src/views/LibraryView.vue | 827 ++++++++-- src/views/PeopleView.vue | 2353 ++++++++++++++++++++++++---- src/views/PersonDetailView.vue | 34 +- src/views/SearchView.vue | 707 ++++++++- 35 files changed, 8054 insertions(+), 961 deletions(-) create mode 100644 src-tauri/src/validate.rs create mode 100644 src/composables/useDragSelect.ts create mode 100644 src/locales/ja.json create mode 100644 src/stores/autoScan.ts create mode 100644 src/stores/themeStore.ts create mode 100644 src/utils/faceLandmarkScoring.ts diff --git a/AGENTS.md b/AGENTS.md index 053cde5..50c9f9e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,11 @@ Examples: - ✅ `/Users/accusys/momentry_studio/src-tauri/src/db.rs` - ❌ `src-tauri/src/db.rs` +## Workflow + +**Before starting any task, always create a `todowrite` with all steps listed.** +Mark items `in_progress` while working and `completed` when done. + ## Stack - **Frontend**: Vue 3 + TypeScript + Vite (port 5173) - **Desktop**: Tauri 2 (Rust backend in `src-tauri/`) @@ -25,17 +30,31 @@ npm run tauri # Alias for `cargo tauri` cargo tauri dev # Full Tauri desktop dev (builds frontend + opens native window) cargo tauri build # Full native app build (outputs to src-tauri/target/release/bundle/) ``` -- **Browser dev flow**: `npm run dev` → kills old proxy on 8888 → starts `momentry-proxy` → sleeps 2s → starts Vite. Vite proxy forwards `/api/v1/*` to Rust proxy at `localhost:8888`. Rust proxy handles face-crop, cluster-results, identity-matches, search-history, bookmarks, profile images locally; proxies rest to Core API at `localhost:3002`. +- **Browser dev flow**: `npm run dev` → kills old proxy on 8888 → starts `momentry-proxy` → sleeps 2s → starts Vite. Vite proxy forwards `/api/v1/*` to Rust proxy at `localhost:8888` and `/api/v2/*` directly to MarkBase at `localhost:11438`. Rust proxy handles face-crop, cluster-results, identity-matches, search-history, bookmarks, profile images locally; proxies rest to Core API at `localhost:3002`. + +## Two-API Architecture (不可混淆) +**Two independent backend APIs. `/api/v1/*` → Momentry Core API ONLY; `/api/v2/*` → MarkBase Core API ONLY. Never cross.** + +| API | Port | Namespace | Owner | Auth | +|---|---|---|---|---| +| **Momentry Core API** | `:3002` | `/api/v1/*` only (files, search, identities, faces, traces, agents, video-marks…) | `/Users/accusys/momentry_core` | `api_key` injected by Studio proxy | +| **MarkBase Core API** | `:11438` | `/api/v2/*` only (admin, myfiles, service, auth, config, tree, products…) | `/Users/accusys/markbase/markbase-core` | Bearer token (`/api/v2/admin/login`) | + +**Routing rules** (`/Users/accusys/momentry_studio/src-tauri/src/proxy.rs`): +- Studio proxy `:8888` is a v1/v2 gateway: `fallback_handler` routes `/api/v2/*` → `proxy_to(MARKBASE_API, inject_api_key=false)` and any other `/api/*` → `proxy_to(CORE_API, inject_api_key=true)`. MarkBase does NOT get `api_key` injected. +- Vite dev proxy (`/Users/accusys/momentry_studio/vite.config.ts`): `/api/v1` → `localhost:8888`, `/api/v2` → `localhost:11438`. +- **Admin data (`get_system_stats`/`list_admin_users`/`list_shares`) always comes from MarkBase `/api/v2/admin/*` in ALL modes** — these are forced to `httpCall()` in `src/api/index.ts` (no Tauri commands exist for them). +- `ClientView` (`/Users/accusys/momentry_studio/src/views/ClientView.vue`) talks to MarkBase directly via `MARKBASE_URL + '/api/v2'` (bypasses Studio proxy). `start_service`/`stop_service` use `detectBaseUrl()` → MarkBase directly. ## Architecture - **Dual-mode API**: Frontend uses `apiCall()` from `src/api/index.ts` which detects `window.__TAURI__` and dispatches to either Tauri IPC (`invoke`) or HTTP to proxy at `http://0.0.0.0:8888`. -- **Rust Tauri commands**: `src-tauri/src/main.rs` — 22 Tauri commands: +- **Rust Tauri commands**: `src-tauri/src/main.rs` — 19 Tauri commands: - Core API: `search_llm_smart`, `search_agents`, `get_files`, `get_people`, `get_faces`, `get_traces`, `get_file_info` - Media: `get_thumbnail`, `get_face_thumbnail`, `get_identity_profile` - Identity: `update_identity_name`, `update_identity_status`, `update_identity_starred`, `delete_identity`, `search_identities`, `get_face_candidates`, `merge_identities`, `bind_face`, `unbind_face` - File ops: `register_file`, `process_file`, `unregister_file` - Local SQLite: `get_search_history`, `save_search_history`, `rename_search_history`, `pin_search_history`, `delete_search_history`, `get_bookmarks`, `save_bookmark`, `delete_bookmark` -- **Rust HTTP proxy** (`src-tauri/src/proxy.rs`): Axum on port 8888. Forwards `/api/v1/*` to Core API (localhost:3002) with API key injection. Handles `/api/v1/search-history` and `/api/v1/bookmarks` locally via `db::` functions. Handles `/api/v1/identity/{uuid}/profile` (reads local filesystem) and `/api/v1/face-thumbnail` (downloads frame + crops bbox) locally — these mirror the Tauri IPC commands that can't be proxied to Core API. +- **Rust HTTP proxy** (`src-tauri/src/proxy.rs`): Axum on port 8888. Routes `/api/v2/*` to MarkBase API (localhost:11438) and `/api/v1/*` to Core API (localhost:3002) with API key injection. Handles `/api/v1/search-history` and `/api/v1/bookmarks` locally via `db::` functions. Handles `/api/v1/identity/{uuid}/profile` (reads local filesystem) and `/api/v1/face-thumbnail` (downloads frame + crops bbox) locally — these mirror the Tauri IPC commands that can't be proxied to Core API. - **Per-user SQLite**: `src-tauri/src/db.rs` — Project-root `data/users/demo.sqlite` (outside `src-tauri/` to avoid dev watcher rebuilds). WAL mode enabled. Tables: `search_history`, `bookmarks`, `app_users`. `init_db()` called on app setup. - **Frontend routes**: `/search`, `/library`, `/people` (redirect `/` → `/search`) - **Frontend entry**: `src/main.ts` → `App.vue` → `router/index.ts` @@ -50,7 +69,7 @@ cargo tauri build # Full native app build (outputs to src-tauri/target/releas - **Full native build order**: `npm install` → `npm run build` → `cargo tauri build` - **Rust deps**: sqlx 0.8, rusqlite 0.32 (bundled), reqwest, tokio, tauri-plugin-shell, base64, chrono, uuid - **Core API**: `http://localhost:3002`, API key hardcoded in Rust only. -- **Proxy**: `http://0.0.0.0:8888` — forwards `/api/v1/*` to Core API with auto-injected key; handles `/api/v1/search-history` and `/api/v1/bookmarks` locally. +- **Proxy**: `http://0.0.0.0:8888` — v1/v2 gateway: `/api/v2/*` → MarkBase (11438, no key), `/api/v1/*` → Core API with auto-injected key; handles `/api/v1/search-history` and `/api/v1/bookmarks` locally. ## Patterns - **Search thumbnails**: Use `start_frame` for frame-accurate thumbnails. Cache key format: `${file_uuid}:${start_frame}`. Not a single `file_uuid` key because same file can appear in multiple search results at different time segments. @@ -60,6 +79,8 @@ cargo tauri build # Full native app build (outputs to src-tauri/target/releas - **v-observe directive**: Global directive (`v-observe`) registered in `main.ts`. Defined in `src/directives/vObserve.ts`. Uses `IntersectionObserver` with 200px rootMargin. Includes `requestAnimationFrame` fallback for elements already visible on mount. - **SearchView empty state**: When `messages.length === 0`, shows `.ms-hero` with "Momentry Studio" + "Turn Every Moment Into Intelligence" slogan. Chat area gets `.chat--empty` class (flex-column, justify-end) to position hero near the search bar. Once messages exist, hero disappears and `.ibar` stays fixed at bottom. - **No `console.log` in *.vue files**: All debug console.log statements have been removed. Use `console.error` only for actual errors. +- **No dev residue allowed (`以後不再有 dev 殘留`)**: Never commit or leave scratch/dev files in the repo — this includes tracked backup snapshots (`*.bak`, `*.bak3`, `*.base`, `*.clean`, `*.work`, `*.backup_*`), standalone dev-only binaries/scripts (e.g. `gen_password.rs`), and unused dead code. Keep backups out of git (personal temp or gitignored); delete them when done. New dev features must not introduce `console.log` (even temporary — use `console.error` or remove before commit). +- **Face settings owned by Core**: `src/api/index.ts` `register_file`/`process_file` and the Rust commands in `/Users/accusys/momentry_studio/src-tauri/src/main.rs` must NOT send or read `face_settings`/`FACE_MIN_*` env vars. Core API owns the face-detection defaults server-side (see Core guide `docs_v1.0/M4_workspace/2026-08-08_face_settings_studio_guide.md`). - **Search history & bookmarks**: Stored in per-user SQLite (`data/users/demo.sqlite`), not localStorage. History items have `id`, `query`, `title`, `chat_state` (JSON-serialized Vue messages), `mode`, `pinned`, timestamps. Max 30 items; pinned sorted first. Composable `useSearchHistory()` provides `loadHistory`, `saveToHistory`, `renameHistory`, `pinHistory`, `deleteHistory`, `restoreFromHistory`. Composable `useBookmarks()` provides `loadBookmarks`, `saveBookmark`, `deleteBookmark`. - **SearchView history panel**: Dropdown with ⋯ menu per item (Pin/Unpin, Rename, Delete). +New button clears chat. Click item → restores `chat_state` JSON or runs new search. Renames via modal dialog. - **SearchView bookmark panel**: Dropdown with items from SQLite. Click → runs search. Hover shows x button to delete. @@ -67,4 +88,21 @@ cargo tauri build # Full native app build (outputs to src-tauri/target/releas - **HistoryItem ID format**: `h_{timestamp}_{random}` e.g. `h_1718000000_abc123`. - **SQLite WAL mode + path outside src-tauri**: Database at project-root `data/users/demo.sqlite` (not inside `src-tauri/`). WAL journal mode prevents `-journal` file creation that triggers `cargo tauri dev` watcher rebuilds. Default path `../data/users/demo.sqlite` resolved from `src-tauri/` cwd. - **Video streaming**: `get_video_stream` is NOT a Tauri command. Both Tauri and browser modes use the proxy URL directly (`http://localhost:8888/api/v1/file/{uuid}/video?...`). The `