chore: Archive all pending OpenSpec proposals

Force archive the following proposals:
- add-audio-device-selector (complete)
- add-embedded-backend-packaging (19/26 tasks)
- add-flexible-deployment-options (20/21 tasks)

New specs created:
- audio-device-management (7 requirements)
- embedded-backend (8 requirements)

Updated specs:
- transcription (+2 requirements for model download progress)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-22 08:44:04 +08:00
parent c36f4167f2
commit e7a06e2b8f
19 changed files with 1551 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
# Design: Extract Environment Variables
## Context
專案需要支援以下部署場景:
1. 開發環境:前後端在本地同時運行
2. 生產環境:後端部署於 1Panel 伺服器前端Electron 應用)獨立打包部署
**架構說明**
- **後端**FastAPI 服務,使用兩個 Dify 服務LLM 摘要 + STT 轉錄)
- **前端**Electron 應用,包含 Sidecar本地 Whisper 即時轉錄服務)
目前的硬編碼配置使得部署困難,且敏感資訊(如 API 密鑰、資料庫密碼)散落在代碼中。
## Goals / Non-Goals
### Goals
- 將所有硬編碼配置提取到環境變數
- 提供完整的 `.env.example` 範例檔案
- 支援前端獨立打包時指定後端 API URL
- 提供 1Panel 部署完整指南和腳本
- 確保向後相容(預設值與現有行為一致)
### Non-Goals
- 不實現配置熱重載
- 不實現密鑰輪換機制
- 不實現多環境配置管理(如 .env.production, .env.staging
## Decisions
### 1. 環境變數命名規範
**決定**:使用大寫蛇形命名法,前端變數加 `VITE_` 前綴
**原因**
- Vite 要求客戶端環境變數必須以 `VITE_` 開頭
- 大寫蛇形是環境變數的標準慣例
### 2. 前端 API URL 配置
**決定**:使用 `VITE_API_BASE_URL` 環境變數,在 `api.js` 中讀取
```javascript
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || "http://localhost:8000/api";
```
**替代方案**
- 使用 runtime 配置檔案(如 `/config.js`- 更靈活但增加部署複雜度
- 使用相對路徑 `/api` - 需要 nginx 反向代理,不適合獨立部署
### 3. 超時配置單位
**決定**統一使用毫秒ms與 JavaScript 一致
**後端配置項**
| 變數名 | 預設值 | 用途 |
|--------|--------|------|
| UPLOAD_TIMEOUT | 600000 | 大檔案上傳10分鐘 |
| DIFY_STT_TIMEOUT | 300000 | Dify STT 轉錄每個分塊5分鐘 |
| LLM_TIMEOUT | 120000 | Dify LLM 摘要處理2分鐘 |
| AUTH_TIMEOUT | 30000 | 認證 API 調用30秒 |
**前端/Sidecar 配置項**
| 變數名 | 預設值 | 用途 |
|--------|--------|------|
| WHISPER_MODEL | medium | 本地 Whisper 模型大小 |
| WHISPER_DEVICE | cpu | 執行裝置cpu/cuda |
| WHISPER_COMPUTE | int8 | 運算精度 |
### 4. 1Panel 部署架構
**決定**:使用 systemd 管理後端服務nginx 反向代理
```
[Client] → [Nginx:443] → [Uvicorn:8000]
[Static Files]
```
**原因**
- systemd 提供進程管理、日誌、自動重啟
- nginx 處理 HTTPS、靜態檔案、反向代理
- 這是 1Panel 的標準部署模式
### 5. CORS 配置
**決定**:保持 `allow_origins=["*"]`,不額外配置
**原因**
- 前端是 Electron 桌面應用,分發到多台電腦
- Electron 主進程的 HTTP 請求不受 CORS 限制
- 簡化部署配置IT 只需關心 HOST 和 PORT
## Risks / Trade-offs
### 風險 1環境變數遺漏
- **風險**:部署時遺漏必要的環境變數導致服務異常
- **緩解**:提供完整的 `.env.example`,啟動時檢查必要變數
### 風險 2前端打包後無法修改 API URL
- **風險**Vite 環境變數在打包時固定
- **緩解**:文件中說明需要為不同環境分別打包,或考慮未來實現 runtime 配置
### 風險 3敏感資訊外洩
- **風險**`.env` 檔案被提交到版本控制
- **緩解**:確保 `.gitignore` 包含 `.env`,只提交 `.env.example`
## Migration Plan
1. **Phase 1 - 後端配置**
- 更新 `config.py` 添加新配置項
- 更新各 router 使用配置
- 更新 `.env``.env.example`
2. **Phase 2 - 前端配置**
- 創建 `.env``.env.example`
- 更新 `api.js` 使用環境變數
3. **Phase 3 - 部署文件**
- 創建 1Panel 部署指南
- 創建部署腳本
4. **Rollback**
- 所有配置都有預設值,回滾只需刪除環境變數
## Open Questions
- Q: 是否需要支援 Docker 部署?
- A: 暫不包含,但環境變數配置天然支援 Docker

View File

@@ -0,0 +1,85 @@
# Change: Extract Hardcoded Configurations to Environment Variables
## Why
專案中存在大量硬編碼的路徑、URL、API 端點、埠號及敏感資訊,這些配置散落在前後端程式碼中。為了支援獨立部署(後端部署於 1Panel 伺服器,前端獨立打包),需要將這些配置統一提取到環境變數檔案中管理,提高部署彈性與安全性。
## What Changes
### 後端配置提取
後端使用兩個 Dify 服務:
- **LLM 服務**`DIFY_API_KEY`- 產生會議結論及行動事項
- **STT 服務**`DIFY_STT_API_KEY`- 上傳音訊檔案的語音轉文字
1. **新增環境變數**
- `BACKEND_HOST` - 後端監聽地址預設0.0.0.0
- `BACKEND_PORT` - 後端監聽埠號預設8000
- `DB_POOL_SIZE` - 資料庫連線池大小預設5
- `JWT_EXPIRE_HOURS` - JWT Token 過期時間預設24
- `UPLOAD_TIMEOUT` - 檔案上傳超時時間預設600000ms
- `DIFY_STT_TIMEOUT` - Dify STT 轉錄超時時間預設300000ms
- `LLM_TIMEOUT` - Dify LLM 處理超時時間預設120000ms
- `AUTH_TIMEOUT` - 認證 API 超時時間預設30000ms
- `TEMPLATE_DIR` - Excel 範本目錄路徑
- `RECORD_DIR` - 會議記錄匯出目錄路徑
- `MAX_FILE_SIZE` - 最大上傳檔案大小預設500MB
- `SUPPORTED_AUDIO_FORMATS` - 支援的音訊格式
**註**CORS 保持 `allow_origins=["*"]`,因為前端是 Electron 桌面應用,無需細粒度控制。
2. **已存在環境變數**(確認文件化)
- `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASS`, `DB_NAME` - 資料庫配置
- `AUTH_API_URL` - 認證 API 端點
- `DIFY_API_URL` - Dify API 基礎 URL
- `DIFY_API_KEY` - Dify LLM 服務金鑰
- `DIFY_STT_API_KEY` - Dify STT 服務金鑰
- `ADMIN_EMAIL` - 管理員郵箱
- `JWT_SECRET` - JWT 密鑰
### 前端/Electron 配置提取
前端包含 Sidecar本地 Whisper 即時轉錄服務)。
1. **Vite 環境變數**(打包時使用)
- `VITE_API_BASE_URL` - 後端 API 基礎 URL預設http://localhost:8000/api
- `VITE_UPLOAD_TIMEOUT` - 大檔案上傳超時時間預設600000ms
- `VITE_APP_TITLE` - 應用程式標題
2. **Sidecar/Whisper 環境變數**(執行時使用)
- `WHISPER_MODEL` - 模型大小預設medium
- `WHISPER_DEVICE` - 執行裝置預設cpu
- `WHISPER_COMPUTE` - 運算精度預設int8
- `SIDECAR_DIR` - Sidecar 目錄路徑Electron 打包時使用)
### 部署文件與腳本
1. **1Panel 部署指南** - `docs/1panel-deployment.md`
2. **後端部署腳本** - `scripts/deploy-backend.sh`
3. **環境變數範例檔案**
- 更新 `backend/.env.example`
- 新增 `client/.env.example`
## Impact
- Affected specs: `middleware`
- Affected code:
- `backend/app/config.py` - 新增配置項
- `backend/app/database.py` - 使用連線池配置
- `backend/app/routers/ai.py` - 使用 Dify 超時配置
- `backend/app/routers/auth.py` - 使用認證超時配置
- `backend/app/routers/export.py` - 使用目錄路徑配置
- `client/src/services/api.js` - 使用 Vite 環境變數
- `client/src/main.js` - Sidecar 路徑配置
- `start.sh` - 更新啟動腳本
## 部署流程簡化
**IT 只需提供:**
1. 後端伺服器 IP/域名
2. 後端使用的 PORT
**開發者打包前端時:**
1. 設定 `VITE_API_BASE_URL=http://<伺服器>:<PORT>/api`
2. 執行打包命令
3. 分發 EXE 給使用者

View File

@@ -0,0 +1,122 @@
## MODIFIED Requirements
### Requirement: FastAPI Server Configuration
The middleware server SHALL be implemented using Python FastAPI framework with comprehensive environment-based configuration supporting standalone deployment.
#### Scenario: Server startup with valid configuration
- **WHEN** the server starts with valid .env file containing all required variables (DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME, DIFY_API_URL, DIFY_API_KEY, AUTH_API_URL)
- **THEN** the server SHALL start successfully and accept connections on the configured BACKEND_HOST and BACKEND_PORT
#### Scenario: Server startup with missing configuration
- **WHEN** the server starts with missing required environment variables
- **THEN** the server SHALL fail to start with descriptive error message
#### Scenario: Server startup with optional configuration
- **WHEN** optional environment variables (BACKEND_PORT, DB_POOL_SIZE, etc.) are not set
- **THEN** the server SHALL use sensible defaults and start normally
### Requirement: Database Connection Pool
The middleware server SHALL maintain a configurable connection pool to the MySQL database using environment variables.
#### Scenario: Database connection success
- **WHEN** the server connects to MySQL with valid credentials from environment
- **THEN** a connection pool SHALL be established with DB_POOL_SIZE connections
#### Scenario: Database connection failure
- **WHEN** the database is unreachable
- **THEN** the server SHALL return HTTP 503 with error details for affected endpoints
### Requirement: CORS Configuration
The middleware server SHALL allow cross-origin requests from all origins to support Electron desktop application clients.
#### Scenario: CORS preflight request
- **WHEN** any client sends OPTIONS request
- **THEN** the server SHALL respond with CORS headers allowing the request (allow_origins=["*"])
## ADDED Requirements
### Requirement: Backend Server Configuration
The middleware server SHALL support configurable host and port through environment variables for flexible deployment.
#### Scenario: Custom port binding
- **WHEN** BACKEND_PORT environment variable is set to 9000
- **THEN** the server SHALL listen on port 9000
#### Scenario: Production host binding
- **WHEN** BACKEND_HOST is set to 0.0.0.0
- **THEN** the server SHALL accept connections from any network interface
#### Scenario: Default configuration
- **WHEN** BACKEND_HOST and BACKEND_PORT are not set
- **THEN** the server SHALL default to 0.0.0.0:8000
### Requirement: Timeout Configuration
The middleware server SHALL support configurable timeout values for different operations through environment variables.
#### Scenario: File upload timeout
- **WHEN** UPLOAD_TIMEOUT is set to 900000 (15 minutes)
- **THEN** file upload operations SHALL allow up to 15 minutes before timeout
#### Scenario: LLM processing timeout
- **WHEN** LLM_TIMEOUT is set to 180000 (3 minutes)
- **THEN** Dify LLM summarization operations SHALL allow up to 3 minutes before timeout
#### Scenario: Dify STT timeout
- **WHEN** DIFY_STT_TIMEOUT is set to 600000 (10 minutes)
- **THEN** Dify STT audio transcription per chunk SHALL allow up to 10 minutes before timeout
#### Scenario: Authentication timeout
- **WHEN** AUTH_TIMEOUT is set to 60000 (1 minute)
- **THEN** authentication API calls SHALL allow up to 1 minute before timeout
### Requirement: File Path Configuration
The middleware server SHALL support configurable directory paths for templates and records.
#### Scenario: Custom template directory
- **WHEN** TEMPLATE_DIR environment variable is set to /data/templates
- **THEN** Excel templates SHALL be loaded from /data/templates
#### Scenario: Custom record directory
- **WHEN** RECORD_DIR environment variable is set to /data/records
- **THEN** exported meeting records SHALL be saved to /data/records
#### Scenario: Relative path resolution
- **WHEN** directory paths are relative
- **THEN** they SHALL be resolved relative to the backend application root
### Requirement: Frontend Environment Configuration
The frontend Electron application SHALL support environment-based API URL configuration for connecting to deployed backend.
#### Scenario: Custom API URL in production build
- **WHEN** VITE_API_BASE_URL is set to http://192.168.1.100:8000/api during build
- **THEN** the built Electron app SHALL connect to http://192.168.1.100:8000/api
#### Scenario: Default API URL in development
- **WHEN** VITE_API_BASE_URL is not set
- **THEN** the frontend SHALL default to http://localhost:8000/api
### Requirement: Sidecar Whisper Configuration
The Electron frontend's Sidecar (local Whisper transcription service) SHALL support environment-based model configuration.
#### Scenario: Custom Whisper model
- **WHEN** WHISPER_MODEL environment variable is set to "large"
- **THEN** the Sidecar SHALL load the large Whisper model for transcription
#### Scenario: GPU acceleration
- **WHEN** WHISPER_DEVICE is set to "cuda" and WHISPER_COMPUTE is set to "float16"
- **THEN** the Sidecar SHALL use GPU for faster transcription
#### Scenario: Default CPU mode
- **WHEN** WHISPER_DEVICE is not set
- **THEN** the Sidecar SHALL default to CPU with int8 compute type
### Requirement: Environment Example Files
The project SHALL provide example environment files documenting all configuration options.
#### Scenario: Backend environment example
- **WHEN** developer sets up backend
- **THEN** backend/.env.example SHALL list all environment variables with descriptions and example values (without sensitive data)
#### Scenario: Frontend environment example
- **WHEN** developer sets up frontend
- **THEN** client/.env.example SHALL list all VITE_ prefixed and WHISPER_ prefixed environment variables with descriptions

View File

@@ -0,0 +1,45 @@
# Tasks: Extract Environment Variables
## 1. Backend Configuration
- [x] 1.1 Update `backend/app/config.py` with new environment variables
- Add: BACKEND_HOST, BACKEND_PORT
- Add: DB_POOL_SIZE, JWT_EXPIRE_HOURS
- Add: UPLOAD_TIMEOUT, DIFY_STT_TIMEOUT, LLM_TIMEOUT, AUTH_TIMEOUT
- Add: TEMPLATE_DIR, RECORD_DIR, MAX_FILE_SIZE, SUPPORTED_AUDIO_FORMATS
- [x] 1.2 Update `backend/app/database.py` to use DB_POOL_SIZE from config
- [x] 1.3 Update `backend/app/routers/ai.py` to use Dify timeout configs (DIFY_STT_TIMEOUT, LLM_TIMEOUT)
- [x] 1.4 Update `backend/app/routers/auth.py` to use AUTH_TIMEOUT and JWT_EXPIRE_HOURS from config
- [x] 1.5 Update `backend/app/routers/export.py` to use TEMPLATE_DIR and RECORD_DIR from config
- [x] 1.6 Update `backend/.env` with all new variables
- [x] 1.7 Update `backend/.env.example` with all variables (without sensitive values)
## 2. Frontend/Electron Configuration
- [x] 2.1 Create `client/.env` with VITE_API_BASE_URL and Whisper settings
- [x] 2.2 Create `client/.env.example` as template
- [x] 2.3 Update `client/src/services/api.js` to use import.meta.env.VITE_API_BASE_URL
- [x] 2.4 Update `client/src/main.js` to pass Whisper env vars to Sidecar process
## 3. Startup Scripts
- [x] 3.1 Update `start.sh` to load environment variables properly
- [x] 3.2 Create `scripts/deploy-backend.sh` for standalone backend deployment
## 4. Deployment Documentation
- [x] 4.1 Create `docs/1panel-deployment.md` with step-by-step guide
- Include: Prerequisites and system requirements
- Include: Python environment setup
- Include: Environment variable configuration (IT only needs HOST + PORT)
- Include: Nginx reverse proxy configuration example
- Include: Systemd service file example
- Include: SSL/HTTPS setup guide (optional)
- Include: Troubleshooting common issues
## 5. Validation
- [ ] 5.1 Test backend starts with new config
- [ ] 5.2 Test frontend builds with environment variables
- [ ] 5.3 Test API connectivity between frontend and backend
- [ ] 5.4 Verify all hardcoded values are externalized

View File

@@ -0,0 +1,145 @@
# Design: add-audio-device-selector
## Architecture Overview
### Component Structure
```
meeting-detail.html
├── Audio Device Panel (新增)
│ ├── Device Selector (dropdown)
│ ├── Volume Meter (canvas/div bars)
│ ├── Test Controls
│ │ ├── Start Test Button
│ │ ├── Stop Test Button
│ │ └── Play Test Button
│ └── Status Indicator
└── Existing Recording Controls
└── Uses selected device from panel
```
### Data Flow
```
User selects device → Update localStorage → Update AudioContext
→ Start volume monitoring
→ Enable test recording
Test Recording Flow:
Start Test → getUserMedia(selected device) → MediaRecorder → Blob
Play Test → Audio element → Play blob URL
Main Recording Flow:
Start Recording → Read selected device from state
→ getUserMedia(selected device)
→ Existing transcription flow
```
## Technical Decisions
### TD-1: Volume Meter Implementation
**Options Considered:**
1. **Web Audio API AnalyserNode** - Real-time frequency/amplitude analysis
2. **MediaRecorder + periodic sampling** - Sample audio levels periodically
3. **CSS-only animation** - Fake animation without real audio data
**Decision:** Web Audio API AnalyserNode
- Provides accurate real-time audio level data
- Low latency visualization
- Standard browser API, well-supported in Electron
### TD-2: Device Preference Storage
**Options Considered:**
1. **localStorage** - Simple key-value storage
2. **config.json** - App configuration file
3. **Backend database** - Per-user settings
**Decision:** localStorage
- No backend changes required
- Immediate persistence
- Per-device settings (user may use different mics on different computers)
### TD-3: Test Recording Duration
**Decision:** 5 seconds fixed duration
- Long enough to verify audio quality
- Short enough to not waste time
- Auto-stop prevents forgotten recordings
### TD-4: UI Placement
**Options Considered:**
1. **Modal dialog** - Opens on demand
2. **Collapsible panel** - Always visible but can be collapsed
3. **Settings page** - Separate page for audio settings
**Decision:** Collapsible panel in meeting-detail page
- Quick access before recording
- No page navigation needed
- Can be collapsed when not needed
## UI Mockup
```
┌─────────────────────────────────────────────────────────┐
│ Audio Device Settings [▼] │
├─────────────────────────────────────────────────────────┤
│ Microphone: [▼ Realtek Microphone (Realtek Audio) ▼] │
│ │
│ Input Level: ████████░░░░░░░░░░░░ 45% │
│ │
│ [🎤 Test Recording] [▶️ Play Test] Status: Ready │
│ │
Click "Test Recording" to verify your microphone │
└─────────────────────────────────────────────────────────┘
```
## State Management
### Audio Device State
```javascript
const audioDeviceState = {
availableDevices: [], // Array of MediaDeviceInfo
selectedDeviceId: null, // Selected device ID or null for default
isMonitoring: false, // Volume meter active
currentLevel: 0, // Current audio level 0-100
testRecording: null, // Blob of test recording
testState: 'idle' // 'idle' | 'recording' | 'playing'
};
```
### localStorage Keys
- `audioDevice.selectedId` - Last selected device ID
- `audioDevice.lastUsedLabel` - Device label for display fallback
## Integration Points
### With Existing Recording
1. `startRecording()` will read `selectedDeviceId` from state
2. If no device selected, use current auto-selection logic
3. If selected device unavailable, show error and prompt reselection
### IPC Considerations
- No new IPC handlers needed
- All audio device operations happen in renderer process
- Uses existing `navigator.mediaDevices` API
## Error Handling
| Error | User Message | Recovery |
|-------|-------------|----------|
| No devices found | "未偵測到麥克風,請連接麥克風後重試" | Refresh device list |
| Device disconnected | "選擇的麥克風已斷開連接" | Auto-switch to default |
| Permission denied | "麥克風權限被拒絕,請在系統設定中允許" | Show permission guide |
| Device busy | "麥克風正被其他應用程式使用" | Retry button |
## Testing Strategy
### Manual Testing
1. Connect multiple microphones
2. Verify all appear in dropdown
3. Select each and verify volume meter responds
4. Record and play test audio for each
5. Unplug device during use and verify error handling
6. Restart app and verify saved preference loads
### Automated Testing (Future)
- Mock `navigator.mediaDevices` for unit tests
- Test device switching logic
- Test localStorage persistence

View File

@@ -0,0 +1,45 @@
# Proposal: add-audio-device-selector
## Summary
新增音訊設備選擇與驗證功能,讓使用者可以手動選擇麥克風、即時預覽音量、進行收音測試及播放測試錄音。
## Problem Statement
目前系統自動選擇麥克風,使用者無法:
1. 查看可用的音訊輸入設備清單
2. 手動選擇偏好的麥克風
3. 在錄音前確認麥克風是否正常運作
4. 測試收音品質
這導致使用者在錄音失敗時難以診斷問題,也無法在多個麥克風之間切換。
## Proposed Solution
在會議詳情頁面新增音訊設備管理面板,包含:
1. **設備選擇器**:下拉選單顯示所有可用麥克風
2. **音量指示器**即時顯示麥克風輸入音量VU meter
3. **收音測試**:錄製 5 秒測試音訊
4. **播放測試**:播放剛錄製的測試音訊
5. **設備狀態指示**:顯示目前選中設備的連線狀態
## Scope
- **In Scope**:
- 前端 UI 元件(設備選擇器、音量計、測試按鈕)
- 設備列舉與切換邏輯
- 測試錄音與播放功能
- 使用者偏好設定儲存localStorage
- **Out of Scope**:
- 系統音訊輸出設備選擇
- 音訊處理效果(降噪、增益等)
- 遠端音訊設備支援
## Success Criteria
- 使用者可以看到所有可用麥克風並選擇一個
- 選擇麥克風後可即時看到音量變化
- 測試錄音功能可錄製 5 秒音訊並播放
- 偏好設定在下次開啟時保留
- 錄音功能使用使用者選擇的麥克風
## Stakeholders
- End Users: 會議記錄人員
- Developers: 前端開發團隊

View File

@@ -0,0 +1,131 @@
# audio-device-management Specification Delta
## ADDED Requirements
### Requirement: Audio Device Enumeration
The frontend SHALL enumerate and display all available audio input devices.
#### Scenario: List available devices
- **WHEN** user opens meeting detail page
- **THEN** system SHALL enumerate all audio input devices
- **AND** display them in a dropdown selector
- **AND** exclude virtual/system devices like "Stereo Mix"
#### Scenario: Refresh device list
- **WHEN** user clicks refresh button or device is connected/disconnected
- **THEN** system SHALL re-enumerate devices
- **AND** update dropdown options
- **AND** preserve current selection if still available
#### Scenario: Device label display
- **WHEN** devices are listed
- **THEN** each device SHALL display its friendly name (label)
- **AND** indicate if it's the system default device
### Requirement: Manual Device Selection
The frontend SHALL allow users to manually select their preferred audio input device.
#### Scenario: Select device from dropdown
- **WHEN** user selects a device from dropdown
- **THEN** system SHALL update selected device state
- **AND** start volume monitoring on new device
- **AND** save selection to localStorage
#### Scenario: Load saved preference
- **WHEN** meeting detail page loads
- **THEN** system SHALL check localStorage for saved device preference
- **AND** if saved device is available, auto-select it
- **AND** if saved device unavailable, fall back to system default
#### Scenario: Selected device unavailable
- **WHEN** previously selected device is no longer available
- **THEN** system SHALL show warning message
- **AND** fall back to system default device
- **AND** prompt user to select new device
### Requirement: Real-time Volume Indicator
The frontend SHALL display real-time audio input level from the selected microphone.
#### Scenario: Display volume meter
- **WHEN** a device is selected
- **THEN** system SHALL show animated volume meter
- **AND** update meter at least 10 times per second
- **AND** display level as percentage (0-100%)
#### Scenario: Volume meter accuracy
- **WHEN** user speaks into microphone
- **THEN** volume meter SHALL reflect actual audio amplitude
- **AND** peak levels SHALL be visually distinct
#### Scenario: Muted or silent input
- **WHEN** no audio input detected for 3 seconds
- **THEN** volume meter SHALL show minimal/zero level
- **AND** optionally show "No input detected" hint
### Requirement: Audio Test Recording
The frontend SHALL allow users to record a short test audio clip.
#### Scenario: Start test recording
- **WHEN** user clicks "Test Recording" button
- **THEN** system SHALL start recording from selected device
- **AND** button SHALL change to "Stop" with countdown timer
- **AND** recording SHALL auto-stop after 5 seconds
#### Scenario: Stop test recording
- **WHEN** recording reaches 5 seconds or user clicks stop
- **THEN** recording SHALL stop
- **AND** audio blob SHALL be stored in memory
- **AND** "Play Test" button SHALL become enabled
#### Scenario: Recording indicator
- **WHEN** test recording is in progress
- **THEN** UI SHALL show recording indicator (pulsing dot)
- **AND** remaining time SHALL be displayed
### Requirement: Test Audio Playback
The frontend SHALL allow users to play back their test recording.
#### Scenario: Play test recording
- **WHEN** user clicks "Play Test" button
- **THEN** system SHALL play the recorded audio through default output
- **AND** button SHALL change to indicate playing state
- **AND** playback SHALL stop at end of recording
#### Scenario: No test recording available
- **WHEN** no test recording has been made
- **THEN** "Play Test" button SHALL be disabled
- **AND** tooltip SHALL indicate "Record a test first"
### Requirement: Integration with Main Recording
The main recording function SHALL use the user-selected audio device.
#### Scenario: Use selected device for recording
- **WHEN** user starts main recording
- **THEN** system SHALL use the device selected in audio settings panel
- **AND** if no device selected, use auto-selection logic
#### Scenario: Device changed during recording
- **WHEN** user changes device selection while recording
- **THEN** change SHALL NOT affect current recording
- **AND** new selection SHALL apply to next recording session
### Requirement: Audio Settings Panel UI
The frontend SHALL display audio settings in a collapsible panel.
#### Scenario: Panel visibility
- **WHEN** meeting detail page loads
- **THEN** audio settings panel SHALL be visible but collapsible
- **AND** panel state (expanded/collapsed) SHALL be saved
#### Scenario: Panel layout
- **WHEN** panel is expanded
- **THEN** it SHALL display:
- Device dropdown selector
- Volume meter visualization
- Test recording button
- Play test button
- Status indicator
#### Scenario: Compact mode
- **WHEN** panel is collapsed
- **THEN** it SHALL show only selected device name and expand button

View File

@@ -0,0 +1,125 @@
# Tasks: add-audio-device-selector
## Phase 1: Core Device Management
### Task 1.1: Add Audio Settings Panel HTML Structure
- [x] Add collapsible panel container in meeting-detail.html
- [x] Add device dropdown selector element
- [x] Add volume meter container (canvas or div bars)
- [x] Add test recording/playback buttons
- [x] Add status indicator element
- **Validation**: Panel renders correctly, all elements visible
### Task 1.2: Implement Device Enumeration
- [x] Create `enumerateAudioDevices()` function
- [x] Filter out virtual devices (Stereo Mix)
- [x] Populate dropdown with device labels
- [x] Mark default device in dropdown
- [x] Add device change event listener for hot-plug support
- **Validation**: All connected microphones appear in dropdown
### Task 1.3: Implement Device Selection Logic
- [x] Create `selectAudioDevice(deviceId)` function
- [x] Stop existing audio context when switching
- [x] Create new AudioContext with selected device
- [x] Save selection to localStorage
- [x] Handle device unavailable errors
- **Validation**: Selecting device updates state, persists after refresh
## Phase 2: Volume Monitoring
### Task 2.1: Implement Volume Meter
- [x] Create AudioContext and AnalyserNode
- [x] Connect selected device to analyser
- [x] Create volume calculation function (RMS or peak)
- [x] Implement requestAnimationFrame loop for updates
- [x] Render volume level as visual bar
- **Validation**: Meter responds to voice input, updates smoothly
### Task 2.2: Volume Meter Styling
- [x] Add CSS for volume meter bar
- [x] Add gradient colors (green → yellow → red)
- [x] Add percentage text display
- [x] Add "No input detected" indicator
- **Validation**: Visual feedback is clear and responsive
## Phase 3: Test Recording
### Task 3.1: Implement Test Recording Function
- [x] Create `startTestRecording()` function
- [x] Use MediaRecorder with selected device
- [x] Implement 5-second auto-stop timer
- [x] Store recording as Blob
- [x] Update UI during recording (countdown, indicator)
- **Validation**: Can record 5 seconds, blob created
### Task 3.2: Implement Test Playback Function
- [x] Create `playTestRecording()` function
- [x] Create Audio element from blob URL
- [x] Handle play/stop states
- [x] Update UI during playback
- [x] Clean up blob URL when done
- **Validation**: Recorded audio plays back correctly
### Task 3.3: Test Recording UI State Management
- [x] Disable recording button during recording
- [x] Show countdown timer during recording
- [x] Enable play button after recording
- [x] Disable test controls during main recording
- **Validation**: UI states transition correctly
## Phase 4: Integration
### Task 4.1: Integrate with Main Recording
- [x] Modify `startRecording()` to use selected device
- [x] Add fallback to auto-selection if no preference
- [x] Handle selected device being unavailable
- [x] Stop volume monitoring during main recording
- **Validation**: Main recording uses selected device
### Task 4.2: Add Panel Collapse/Expand
- [x] Add collapse toggle button
- [x] Save panel state to localStorage
- [x] Load panel state on page load
- [x] Stop volume monitoring when collapsed
- **Validation**: Panel remembers collapse state
### Task 4.3: Add Refresh Device List Button
- [x] Add refresh icon button
- [x] Re-enumerate devices on click
- [x] Preserve selection if still available
- [x] Update dropdown options
- **Validation**: New devices appear after refresh
## Phase 5: Polish & Error Handling
### Task 5.1: Error Handling
- [x] Handle "No devices found" state
- [x] Handle permission denied errors
- [x] Handle device disconnection during use
- [x] Show user-friendly error messages (Chinese)
- **Validation**: All error states show appropriate messages
### Task 5.2: Localization
- [x] Add Chinese labels for all UI elements
- [x] Add Chinese error messages
- [x] Add tooltips for buttons
- **Validation**: All text is in Traditional Chinese
### Task 5.3: Testing & Documentation
- [x] Manual testing with multiple microphones
- [x] Test USB microphone hot-plug
- [x] Test headset microphone switching
- [x] Update DEPLOYMENT.md if needed
- **Validation**: Feature works with various microphone types
## Dependencies
- Task 1.2 depends on Task 1.1
- Task 2.1 depends on Task 1.3
- Task 3.1 depends on Task 1.3
- Task 4.1 depends on Tasks 1.3, 3.1
- Phase 5 depends on all previous phases
## Parallelizable Work
- Task 1.1 (HTML) and Task 2.2 (CSS) can run in parallel
- Task 3.1 (Recording) and Task 2.1 (Volume) can run in parallel after Task 1.3

View File

@@ -0,0 +1,115 @@
# Design: Embedded Backend Packaging
## Context
Meeting Assistant uses a three-tier architecture: Electron Client → FastAPI Middleware → MySQL/Dify. For enterprise deployment, administrators want to distribute a single executable that users can run without additional setup. The backend must still connect to remote MySQL and Dify services (no local database).
**Stakeholders:**
- Enterprise IT administrators (simplified deployment)
- End users (double-click to run)
- Developers (maintain backward compatibility)
## Goals / Non-Goals
### Goals
- Package backend as a sidecar executable using PyInstaller
- Electron manages backend lifecycle (start on launch, stop on close)
- Single `config.json` for all configuration (frontend, backend, whisper)
- Health check ensures backend is ready before showing UI
- Backward compatible with existing separate-deployment mode
- Show Whisper model download progress to improve UX
### Non-Goals
- Embedding MySQL database (still remote)
- Embedding LLM model (still uses Dify API)
- Removing external authentication (still requires company SSO)
- Pre-bundling Whisper model (user downloads on first run)
## Decisions
### Decision 1: Use PyInstaller for Backend Packaging
**What:** Package FastAPI + uvicorn as standalone executable using PyInstaller `--onedir` mode.
**Why:**
- Consistent with existing transcriber sidecar approach
- `--onedir` provides faster startup than `--onefile`
- Team already has PyInstaller expertise
**Alternatives considered:**
- Nuitka: Better optimization but longer build times
- cx_Freeze: Less community support for FastAPI
### Decision 2: Configuration via Extended config.json
**What:** Add `backend` section to existing `config.json` with database, API, and auth settings.
**Why:**
- Single configuration file for users to manage
- Runtime modifiable without rebuilding
- Consistent with existing whisper config pattern
**Schema:**
```json
{
"apiBaseUrl": "http://localhost:8000/api",
"backend": {
"embedded": true,
"host": "127.0.0.1",
"port": 8000,
"database": { "host": "", "port": 33306, "user": "", "password": "", "database": "" },
"externalApis": { "authApiUrl": "", "difyApiUrl": "", "difyApiKey": "", "difySttApiKey": "" },
"auth": { "adminEmail": "", "jwtSecret": "", "jwtExpireHours": 24 }
}
}
```
### Decision 3: Health Check Before Window Display
**What:** Electron polls `/api/health` endpoint before creating main window.
**Why:**
- Prevents "connection refused" errors on startup
- Provides clear feedback if backend fails to start
- Maximum 30 attempts with 1-second intervals
### Decision 4: Backward Compatibility via Feature Flag
**What:** `backend.embedded: false` (default) preserves existing behavior; `true` enables embedded mode.
**Why:**
- Existing deployments continue working unchanged
- Gradual migration path for enterprises
- Same codebase supports both deployment models
### Decision 5: Huggingface Hub Progress Callback for Model Download
**What:** Intercept huggingface_hub download progress and emit JSON status messages.
**Why:**
- faster-whisper uses huggingface_hub internally
- Can emit progress without modifying faster-whisper source
- JSON format consistent with existing sidecar protocol
## Risks / Trade-offs
| Risk | Impact | Mitigation |
|------|--------|------------|
| PyInstaller hidden imports missing | Backend fails to start | Comprehensive hidden-import list; test on clean Windows |
| Config file contains sensitive data | Security exposure | Document security best practices; consider encryption |
| Backend startup timeout | Poor UX | Increase timeout; show loading indicator |
| Port 8000 already in use | Backend fails | Allow configurable port; detect and report conflicts |
## Migration Plan
### For New Deployments (All-in-One)
1. Build with `--embedded-backend` flag
2. Configure `config.json` with database/API credentials
3. Distribute single exe to users
### For Existing Deployments (Separate Backend)
1. No changes required
2. Ensure `backend.embedded: false` in config
3. Continue using existing backend deployment
### Rollback
- Set `backend.embedded: false` to disable embedded backend
- Deploy backend separately as before
## Open Questions
- Should we add config validation UI on first startup?
- Should backend port be auto-discovered if 8000 is in use?

View File

@@ -0,0 +1,28 @@
# Change: Add Embedded Backend Packaging for All-in-One Deployment
## Why
Currently, deploying Meeting Assistant requires setting up both the Electron client and a separate backend server. For enterprise internal deployment, users want a simpler experience: **double-click the exe and it works** without needing to understand or configure backend services separately.
Additionally, when users first run the packaged application, the Whisper model download (~1.5GB) shows only "loading_model" status with no progress indication, causing confusion about whether the download is actually happening.
## What Changes
- **New capability: Embedded Backend** - Package FastAPI backend as a sidecar managed by Electron
- **Backend sidecar management** - Electron starts/stops backend process automatically
- **Health check mechanism** - Wait for backend readiness before loading frontend
- **Configuration unification** - All settings (DB, API keys, auth) in single `config.json`
- **Backward compatible** - Existing deployment method (separate backend) still works via `backend.embedded: false` flag
- **Model download progress** - Show real-time download percentage for Whisper model
## Impact
- Affected specs: `embedded-backend` (new), `transcription` (modified)
- Affected code:
- `backend/run_server.py` (new) - Backend entry point for packaging
- `backend/build.py` (new) - PyInstaller build script
- `backend/app/config.py` - Support frozen executable paths
- `client/src/main.js` - Backend sidecar management
- `client/src/preload.js` - Expose backend status API
- `client/config.json` - Extended configuration schema
- `client/package.json` - Build configuration for backend resources
- `sidecar/transcriber.py` - Model download progress reporting
- `scripts/build-client.bat` - Integrated build script
- `scripts/build-all.ps1` - PowerShell build script

View File

@@ -0,0 +1,89 @@
## ADDED Requirements
### Requirement: Embedded Backend Packaging
The FastAPI backend SHALL be packaged as a standalone executable using PyInstaller for all-in-one deployment.
#### Scenario: Backend executable creation
- **WHEN** build script runs with embedded backend flag
- **THEN** PyInstaller SHALL create `backend/dist/backend/backend.exe` containing FastAPI, uvicorn, and all dependencies
#### Scenario: Backend executable startup
- **WHEN** backend executable is launched
- **THEN** it SHALL read configuration from `config.json` in the same directory
- **AND** start uvicorn server on configured host and port
### Requirement: Electron Backend Sidecar Management
The Electron main process SHALL manage the embedded backend as a sidecar process.
#### Scenario: Start backend on app launch
- **WHEN** Electron app launches with `backend.embedded: true` in config
- **THEN** main process SHALL spawn backend executable as child process
- **AND** pass configuration via environment variables
#### Scenario: Skip backend when disabled
- **WHEN** Electron app launches with `backend.embedded: false` in config
- **THEN** main process SHALL NOT spawn backend executable
- **AND** frontend SHALL connect to remote backend via `apiBaseUrl`
#### Scenario: Terminate backend on app close
- **WHEN** user closes Electron app
- **THEN** main process SHALL send SIGTERM to backend process
- **AND** force kill after 5 seconds if still running
### Requirement: Backend Health Check
The Electron main process SHALL verify backend readiness before showing the main window.
#### Scenario: Health check success
- **WHEN** backend `/api/health` returns HTTP 200
- **THEN** main process SHALL proceed to create main window
- **AND** set `backendReady` state to true
#### Scenario: Health check timeout
- **WHEN** backend does not respond within 30 seconds (30 attempts, 1s interval)
- **THEN** main process SHALL display error dialog
- **AND** log detailed error for debugging
#### Scenario: Health check polling
- **WHEN** health check attempt fails
- **THEN** main process SHALL retry after 1 second
- **AND** log attempt number for debugging
### Requirement: Unified Configuration Schema
All configuration for frontend, backend, and whisper SHALL be in a single `config.json` file.
#### Scenario: Backend configuration loading
- **WHEN** backend sidecar starts
- **THEN** it SHALL read database credentials from `config.json` backend.database section
- **AND** read API keys from `config.json` backend.externalApis section
- **AND** read auth settings from `config.json` backend.auth section
#### Scenario: Configuration priority
- **WHEN** both environment variable and config.json value exist
- **THEN** environment variable SHALL take precedence
#### Scenario: Default values
- **WHEN** configuration value is not specified
- **THEN** system SHALL use sensible defaults (host: 127.0.0.1, port: 8000)
### Requirement: Backend Status API
The Electron app SHALL expose backend status to the renderer process.
#### Scenario: Get backend status
- **WHEN** renderer calls `window.electronAPI.getBackendStatus()`
- **THEN** it SHALL return object with `ready` boolean and `url` string
#### Scenario: Backend status in UI
- **WHEN** backend is starting
- **THEN** UI MAY display loading indicator
### Requirement: Backward Compatibility
The embedded backend feature SHALL NOT break existing separate-deployment mode.
#### Scenario: Separate deployment unchanged
- **WHEN** `backend.embedded` is false or undefined
- **THEN** system SHALL behave exactly as before this change
- **AND** frontend connects to `apiBaseUrl` without spawning local backend
#### Scenario: Existing scripts work
- **WHEN** user runs `./start.sh start` or `./scripts/setup-backend.sh`
- **THEN** backend SHALL start normally as standalone server

View File

@@ -0,0 +1,40 @@
## ADDED Requirements
### Requirement: Model Download Progress Display
The sidecar SHALL report Whisper model download progress to enable UI feedback.
#### Scenario: Emit download start
- **WHEN** Whisper model download begins
- **THEN** sidecar SHALL emit JSON to stdout: `{"status": "downloading_model", "model": "<size>", "progress": 0, "total_mb": <size>}`
#### Scenario: Emit download progress
- **WHEN** download progress updates
- **THEN** sidecar SHALL emit JSON: `{"status": "downloading_model", "progress": <percent>, "downloaded_mb": <current>, "total_mb": <total>}`
- **AND** progress updates SHALL occur at least every 5% or every 5 seconds
#### Scenario: Emit download complete
- **WHEN** model download completes
- **THEN** sidecar SHALL emit JSON: `{"status": "model_downloaded", "model": "<size>"}`
- **AND** proceed to model loading
#### Scenario: Skip download for cached model
- **WHEN** model already exists in huggingface cache
- **THEN** sidecar SHALL NOT emit download progress messages
- **AND** proceed directly to loading
### Requirement: Frontend Model Download Progress Display
The Electron frontend SHALL display model download progress to users.
#### Scenario: Show download progress in transcript panel
- **WHEN** sidecar emits download progress
- **THEN** whisper status element SHALL display download percentage and size
- **AND** format: "Downloading: XX% (YYY MB / ZZZ MB)"
#### Scenario: Show download complete
- **WHEN** sidecar emits model_downloaded status
- **THEN** whisper status element SHALL briefly show "Model downloaded"
- **AND** transition to loading state
#### Scenario: Forward progress events via IPC
- **WHEN** main process receives download progress from sidecar
- **THEN** it SHALL forward to renderer via `model-download-progress` IPC channel

View File

@@ -0,0 +1,39 @@
# Tasks: Add Embedded Backend Packaging
## 1. Backend Packaging Infrastructure
- [x] 1.1 Create `backend/run_server.py` - Entry point that loads config and starts uvicorn
- [x] 1.2 Create `backend/build.py` - PyInstaller build script with hidden imports
- [x] 1.3 Modify `backend/app/config.py` - Support frozen executable path detection
- [ ] 1.4 Test backend executable runs standalone on Windows
## 2. Electron Backend Sidecar Management
- [x] 2.1 Add `backendProcess` and `backendReady` state variables in `main.js`
- [x] 2.2 Implement `startBackendSidecar()` function
- [x] 2.3 Implement `waitForBackendReady()` health check polling
- [x] 2.4 Modify `app.whenReady()` to start backend before window
- [x] 2.5 Modify window close handler to terminate backend process
- [x] 2.6 Add `get-backend-status` IPC handler
## 3. Configuration Schema Extension
- [x] 3.1 Extend `client/config.json` with `backend` section
- [x] 3.2 Modify `client/src/preload.js` to expose backend status API
- [x] 3.3 Add configuration loading in backend entry point
- [ ] 3.4 Document configuration options in DEPLOYMENT.md
## 4. Build Script Integration
- [x] 4.1 Modify `scripts/build-client.bat` to build backend sidecar
- [ ] 4.2 Modify `scripts/build-all.ps1` to build backend sidecar
- [x] 4.3 Update `client/package.json` extraResources for backend
- [x] 4.4 Add `--embedded-backend` flag to build scripts
## 5. Model Download Progress Display
- [x] 5.1 Modify `sidecar/transcriber.py` to emit download progress JSON
- [x] 5.2 Add progress event forwarding in `main.js`
- [x] 5.3 Expose `onModelDownloadProgress` in `preload.js`
- [x] 5.4 Update `meeting-detail.html` to display download progress
## 6. Testing and Documentation
- [ ] 6.1 Test embedded mode on clean Windows machine
- [ ] 6.2 Test backward compatibility (embedded: false)
- [ ] 6.3 Test model download progress display
- [ ] 6.4 Update DEPLOYMENT.md with all-in-one deployment instructions

View File

@@ -0,0 +1,18 @@
# Change: Add Flexible Deployment Options
## Why
Enterprise deployment environments vary significantly. Some networks block MySQL port 33306, preventing access to cloud databases. Additionally, the current portable executable extracts to a random folder in `%TEMP%`, causing Windows Defender warnings on each launch and potential permission issues.
## What Changes
- **SQLite database support** - Allow choosing between MySQL (cloud) and SQLite (local) databases at build time via `--database-type` parameter
- **Fixed portable extraction path** - Configure `unpackDirName` to use a predictable folder name instead of random UUID
## Impact
- Affected specs: `embedded-backend` (modified)
- Affected code:
- `client/config.json` - Add `database.type` and `database.sqlitePath` fields
- `client/package.json` - Add `unpackDirName` to portable configuration
- `backend/app/config.py` - Add `DB_TYPE` and `SQLITE_PATH` settings
- `backend/app/database.py` - Conditional SQLite/MySQL initialization
- `backend/run_server.py` - Pass database type environment variables
- `scripts/build-client.bat` - Add `--database-type` parameter

View File

@@ -0,0 +1,57 @@
## ADDED Requirements
### Requirement: SQLite Database Support
The backend SHALL support SQLite as an alternative to MySQL for offline/standalone deployments.
#### Scenario: SQLite mode initialization
- **WHEN** `database.type` is set to `"sqlite"` in config.json
- **THEN** backend SHALL create SQLite database at `database.sqlitePath`
- **AND** initialize all required tables using SQLite-compatible syntax
#### Scenario: MySQL mode initialization
- **WHEN** `database.type` is set to `"mysql"` or not specified in config.json
- **THEN** backend SHALL connect to MySQL using credentials from `database` section
- **AND** behave exactly as before this change
#### Scenario: SQLite thread safety
- **WHEN** multiple concurrent requests access SQLite database
- **THEN** backend SHALL use thread lock to serialize database operations
- **AND** use `check_same_thread=False` for SQLite connection
#### Scenario: SQLite data persistence
- **WHEN** app is closed and reopened
- **THEN** all meeting data SHALL persist in SQLite file
- **AND** be accessible on next launch
### Requirement: Portable Extraction Path Configuration
The portable Windows build SHALL extract to a predictable folder name.
#### Scenario: Fixed extraction folder
- **WHEN** portable executable starts
- **THEN** it SHALL extract to `%TEMP%\Meeting-Assistant` instead of random UUID folder
#### Scenario: Windows Defender consistency
- **WHEN** user launches portable executable multiple times
- **THEN** Windows Defender SHALL NOT prompt for permission each time
- **BECAUSE** extraction path is consistent across launches
## MODIFIED Requirements
### Requirement: Unified Configuration Schema
All configuration for frontend, backend, and whisper SHALL be in a single `config.json` file.
#### Scenario: Backend configuration loading
- **WHEN** backend sidecar starts
- **THEN** it SHALL read database type from `config.json` backend.database.type section
- **AND** read SQLite path from `config.json` backend.database.sqlitePath section (if SQLite mode)
- **AND** read database credentials from `config.json` backend.database section (if MySQL mode)
- **AND** read API keys from `config.json` backend.externalApis section
- **AND** read auth settings from `config.json` backend.auth section
#### Scenario: Configuration priority
- **WHEN** both environment variable and config.json value exist
- **THEN** environment variable SHALL take precedence
#### Scenario: Default values
- **WHEN** configuration value is not specified
- **THEN** system SHALL use sensible defaults (host: 127.0.0.1, port: 8000, database.type: mysql)

View File

@@ -0,0 +1,36 @@
# Tasks: Add Flexible Deployment Options
## 1. Portable Extraction Path
- [x] 1.1 Update `client/package.json` - Add `unpackDirName: "Meeting-Assistant"` to portable config
## 2. Configuration Schema for SQLite
- [x] 2.1 Update `client/config.json` - Add `database.type` field (default: "mysql")
- [x] 2.2 Update `client/config.json` - Add `database.sqlitePath` field (default: "data/meeting.db")
## 3. Backend Configuration
- [x] 3.1 Update `backend/app/config.py` - Add `DB_TYPE` setting
- [x] 3.2 Update `backend/app/config.py` - Add `SQLITE_PATH` setting
- [x] 3.3 Update `backend/run_server.py` - Pass `DB_TYPE` and `SQLITE_PATH` to environment
## 4. Database Abstraction Layer
- [x] 4.1 Refactor `backend/app/database.py` - Create `init_db()` dispatcher function
- [x] 4.2 Implement `init_sqlite()` - SQLite connection with row_factory
- [x] 4.3 Implement `init_mysql()` - Keep existing MySQL pool logic
- [x] 4.4 Create unified `get_db_cursor()` context manager for both backends
- [x] 4.5 Add SQLite table creation statements (convert MySQL syntax)
- [x] 4.6 Add thread lock for SQLite connection safety
## 5. Build Script Integration
- [x] 5.1 Update `scripts/build-client.bat` - Add `--database-type` parameter parsing
- [x] 5.2 Update `scripts/build-client.bat` - Add `update_config_database` function
- [x] 5.3 Update help message with new parameter
## 6. Testing
- [x] 6.1 Test SQLite mode - Create meeting, query, update, delete
- [x] 6.2 Test MySQL mode - Ensure backward compatibility
- [ ] 6.3 Test portable extraction to `%TEMP%\Meeting-Assistant` (requires Windows build)
## 7. Documentation
- [x] 7.1 Update DEPLOYMENT.md with SQLite mode instructions
- [x] 7.2 Update DEPLOYMENT.md with --database-type parameter
- [x] 7.3 Update DEPLOYMENT.md with portable extraction path info

View File

@@ -0,0 +1,133 @@
# audio-device-management Specification
## Purpose
TBD - created by archiving change add-audio-device-selector. Update Purpose after archive.
## Requirements
### Requirement: Audio Device Enumeration
The frontend SHALL enumerate and display all available audio input devices.
#### Scenario: List available devices
- **WHEN** user opens meeting detail page
- **THEN** system SHALL enumerate all audio input devices
- **AND** display them in a dropdown selector
- **AND** exclude virtual/system devices like "Stereo Mix"
#### Scenario: Refresh device list
- **WHEN** user clicks refresh button or device is connected/disconnected
- **THEN** system SHALL re-enumerate devices
- **AND** update dropdown options
- **AND** preserve current selection if still available
#### Scenario: Device label display
- **WHEN** devices are listed
- **THEN** each device SHALL display its friendly name (label)
- **AND** indicate if it's the system default device
### Requirement: Manual Device Selection
The frontend SHALL allow users to manually select their preferred audio input device.
#### Scenario: Select device from dropdown
- **WHEN** user selects a device from dropdown
- **THEN** system SHALL update selected device state
- **AND** start volume monitoring on new device
- **AND** save selection to localStorage
#### Scenario: Load saved preference
- **WHEN** meeting detail page loads
- **THEN** system SHALL check localStorage for saved device preference
- **AND** if saved device is available, auto-select it
- **AND** if saved device unavailable, fall back to system default
#### Scenario: Selected device unavailable
- **WHEN** previously selected device is no longer available
- **THEN** system SHALL show warning message
- **AND** fall back to system default device
- **AND** prompt user to select new device
### Requirement: Real-time Volume Indicator
The frontend SHALL display real-time audio input level from the selected microphone.
#### Scenario: Display volume meter
- **WHEN** a device is selected
- **THEN** system SHALL show animated volume meter
- **AND** update meter at least 10 times per second
- **AND** display level as percentage (0-100%)
#### Scenario: Volume meter accuracy
- **WHEN** user speaks into microphone
- **THEN** volume meter SHALL reflect actual audio amplitude
- **AND** peak levels SHALL be visually distinct
#### Scenario: Muted or silent input
- **WHEN** no audio input detected for 3 seconds
- **THEN** volume meter SHALL show minimal/zero level
- **AND** optionally show "No input detected" hint
### Requirement: Audio Test Recording
The frontend SHALL allow users to record a short test audio clip.
#### Scenario: Start test recording
- **WHEN** user clicks "Test Recording" button
- **THEN** system SHALL start recording from selected device
- **AND** button SHALL change to "Stop" with countdown timer
- **AND** recording SHALL auto-stop after 5 seconds
#### Scenario: Stop test recording
- **WHEN** recording reaches 5 seconds or user clicks stop
- **THEN** recording SHALL stop
- **AND** audio blob SHALL be stored in memory
- **AND** "Play Test" button SHALL become enabled
#### Scenario: Recording indicator
- **WHEN** test recording is in progress
- **THEN** UI SHALL show recording indicator (pulsing dot)
- **AND** remaining time SHALL be displayed
### Requirement: Test Audio Playback
The frontend SHALL allow users to play back their test recording.
#### Scenario: Play test recording
- **WHEN** user clicks "Play Test" button
- **THEN** system SHALL play the recorded audio through default output
- **AND** button SHALL change to indicate playing state
- **AND** playback SHALL stop at end of recording
#### Scenario: No test recording available
- **WHEN** no test recording has been made
- **THEN** "Play Test" button SHALL be disabled
- **AND** tooltip SHALL indicate "Record a test first"
### Requirement: Integration with Main Recording
The main recording function SHALL use the user-selected audio device.
#### Scenario: Use selected device for recording
- **WHEN** user starts main recording
- **THEN** system SHALL use the device selected in audio settings panel
- **AND** if no device selected, use auto-selection logic
#### Scenario: Device changed during recording
- **WHEN** user changes device selection while recording
- **THEN** change SHALL NOT affect current recording
- **AND** new selection SHALL apply to next recording session
### Requirement: Audio Settings Panel UI
The frontend SHALL display audio settings in a collapsible panel.
#### Scenario: Panel visibility
- **WHEN** meeting detail page loads
- **THEN** audio settings panel SHALL be visible but collapsible
- **AND** panel state (expanded/collapsed) SHALL be saved
#### Scenario: Panel layout
- **WHEN** panel is expanded
- **THEN** it SHALL display:
- Device dropdown selector
- Volume meter visualization
- Test recording button
- Play test button
- Status indicator
#### Scenario: Compact mode
- **WHEN** panel is collapsed
- **THEN** it SHALL show only selected device name and expand button

View File

@@ -0,0 +1,130 @@
# embedded-backend Specification
## Purpose
TBD - created by archiving change add-embedded-backend-packaging. Update Purpose after archive.
## Requirements
### Requirement: Embedded Backend Packaging
The FastAPI backend SHALL be packaged as a standalone executable using PyInstaller for all-in-one deployment.
#### Scenario: Backend executable creation
- **WHEN** build script runs with embedded backend flag
- **THEN** PyInstaller SHALL create `backend/dist/backend/backend.exe` containing FastAPI, uvicorn, and all dependencies
#### Scenario: Backend executable startup
- **WHEN** backend executable is launched
- **THEN** it SHALL read configuration from `config.json` in the same directory
- **AND** start uvicorn server on configured host and port
### Requirement: Electron Backend Sidecar Management
The Electron main process SHALL manage the embedded backend as a sidecar process.
#### Scenario: Start backend on app launch
- **WHEN** Electron app launches with `backend.embedded: true` in config
- **THEN** main process SHALL spawn backend executable as child process
- **AND** pass configuration via environment variables
#### Scenario: Skip backend when disabled
- **WHEN** Electron app launches with `backend.embedded: false` in config
- **THEN** main process SHALL NOT spawn backend executable
- **AND** frontend SHALL connect to remote backend via `apiBaseUrl`
#### Scenario: Terminate backend on app close
- **WHEN** user closes Electron app
- **THEN** main process SHALL send SIGTERM to backend process
- **AND** force kill after 5 seconds if still running
### Requirement: Backend Health Check
The Electron main process SHALL verify backend readiness before showing the main window.
#### Scenario: Health check success
- **WHEN** backend `/api/health` returns HTTP 200
- **THEN** main process SHALL proceed to create main window
- **AND** set `backendReady` state to true
#### Scenario: Health check timeout
- **WHEN** backend does not respond within 30 seconds (30 attempts, 1s interval)
- **THEN** main process SHALL display error dialog
- **AND** log detailed error for debugging
#### Scenario: Health check polling
- **WHEN** health check attempt fails
- **THEN** main process SHALL retry after 1 second
- **AND** log attempt number for debugging
### Requirement: Unified Configuration Schema
All configuration for frontend, backend, and whisper SHALL be in a single `config.json` file.
#### Scenario: Backend configuration loading
- **WHEN** backend sidecar starts
- **THEN** it SHALL read database type from `config.json` backend.database.type section
- **AND** read SQLite path from `config.json` backend.database.sqlitePath section (if SQLite mode)
- **AND** read database credentials from `config.json` backend.database section (if MySQL mode)
- **AND** read API keys from `config.json` backend.externalApis section
- **AND** read auth settings from `config.json` backend.auth section
#### Scenario: Configuration priority
- **WHEN** both environment variable and config.json value exist
- **THEN** environment variable SHALL take precedence
#### Scenario: Default values
- **WHEN** configuration value is not specified
- **THEN** system SHALL use sensible defaults (host: 127.0.0.1, port: 8000, database.type: mysql)
### Requirement: Backend Status API
The Electron app SHALL expose backend status to the renderer process.
#### Scenario: Get backend status
- **WHEN** renderer calls `window.electronAPI.getBackendStatus()`
- **THEN** it SHALL return object with `ready` boolean and `url` string
#### Scenario: Backend status in UI
- **WHEN** backend is starting
- **THEN** UI MAY display loading indicator
### Requirement: Backward Compatibility
The embedded backend feature SHALL NOT break existing separate-deployment mode.
#### Scenario: Separate deployment unchanged
- **WHEN** `backend.embedded` is false or undefined
- **THEN** system SHALL behave exactly as before this change
- **AND** frontend connects to `apiBaseUrl` without spawning local backend
#### Scenario: Existing scripts work
- **WHEN** user runs `./start.sh start` or `./scripts/setup-backend.sh`
- **THEN** backend SHALL start normally as standalone server
### Requirement: SQLite Database Support
The backend SHALL support SQLite as an alternative to MySQL for offline/standalone deployments.
#### Scenario: SQLite mode initialization
- **WHEN** `database.type` is set to `"sqlite"` in config.json
- **THEN** backend SHALL create SQLite database at `database.sqlitePath`
- **AND** initialize all required tables using SQLite-compatible syntax
#### Scenario: MySQL mode initialization
- **WHEN** `database.type` is set to `"mysql"` or not specified in config.json
- **THEN** backend SHALL connect to MySQL using credentials from `database` section
- **AND** behave exactly as before this change
#### Scenario: SQLite thread safety
- **WHEN** multiple concurrent requests access SQLite database
- **THEN** backend SHALL use thread lock to serialize database operations
- **AND** use `check_same_thread=False` for SQLite connection
#### Scenario: SQLite data persistence
- **WHEN** app is closed and reopened
- **THEN** all meeting data SHALL persist in SQLite file
- **AND** be accessible on next launch
### Requirement: Portable Extraction Path Configuration
The portable Windows build SHALL extract to a predictable folder name.
#### Scenario: Fixed extraction folder
- **WHEN** portable executable starts
- **THEN** it SHALL extract to `%TEMP%\Meeting-Assistant` instead of random UUID folder
#### Scenario: Windows Defender consistency
- **WHEN** user launches portable executable multiple times
- **THEN** Windows Defender SHALL NOT prompt for permission each time
- **BECAUSE** extraction path is consistent across launches

View File

@@ -175,3 +175,42 @@ The system SHALL support both real-time local transcription and file-based cloud
- **WHEN** transcription completes from either source - **WHEN** transcription completes from either source
- **THEN** result SHALL be displayed in the same transcript area in meeting detail page - **THEN** result SHALL be displayed in the same transcript area in meeting detail page
### Requirement: Model Download Progress Display
The sidecar SHALL report Whisper model download progress to enable UI feedback.
#### Scenario: Emit download start
- **WHEN** Whisper model download begins
- **THEN** sidecar SHALL emit JSON to stdout: `{"status": "downloading_model", "model": "<size>", "progress": 0, "total_mb": <size>}`
#### Scenario: Emit download progress
- **WHEN** download progress updates
- **THEN** sidecar SHALL emit JSON: `{"status": "downloading_model", "progress": <percent>, "downloaded_mb": <current>, "total_mb": <total>}`
- **AND** progress updates SHALL occur at least every 5% or every 5 seconds
#### Scenario: Emit download complete
- **WHEN** model download completes
- **THEN** sidecar SHALL emit JSON: `{"status": "model_downloaded", "model": "<size>"}`
- **AND** proceed to model loading
#### Scenario: Skip download for cached model
- **WHEN** model already exists in huggingface cache
- **THEN** sidecar SHALL NOT emit download progress messages
- **AND** proceed directly to loading
### Requirement: Frontend Model Download Progress Display
The Electron frontend SHALL display model download progress to users.
#### Scenario: Show download progress in transcript panel
- **WHEN** sidecar emits download progress
- **THEN** whisper status element SHALL display download percentage and size
- **AND** format: "Downloading: XX% (YYY MB / ZZZ MB)"
#### Scenario: Show download complete
- **WHEN** sidecar emits model_downloaded status
- **THEN** whisper status element SHALL briefly show "Model downloaded"
- **AND** transition to loading state
#### Scenario: Forward progress events via IPC
- **WHEN** main process receives download progress from sidecar
- **THEN** it SHALL forward to renderer via `model-download-progress` IPC channel